diff --git a/src/actions/check-web-content-analysis-credits.ts b/src/actions/check-web-content-analysis-credits.ts
deleted file mode 100644
index 3ca2c7f..0000000
--- a/src/actions/check-web-content-analysis-credits.ts
+++ /dev/null
@@ -1,37 +0,0 @@
-'use server';
-
-import { getWebContentAnalysisCost } from '@/ai/text/utils/web-content-analyzer-config';
-import { getUserCredits, hasEnoughCredits } from '@/credits/credits';
-import type { User } from '@/lib/auth-types';
-import { userActionClient } from '@/lib/safe-action';
-
-/**
- * Check if user has enough credits for web content analysis
- */
-export const checkWebContentAnalysisCreditsAction = userActionClient.action(
- async ({ ctx }) => {
- const currentUser = (ctx as { user: User }).user;
-
- try {
- const requiredCredits = getWebContentAnalysisCost();
- const currentCredits = await getUserCredits(currentUser.id);
- const hasCredits = await hasEnoughCredits({
- userId: currentUser.id,
- requiredCredits,
- });
-
- return {
- success: true,
- hasEnoughCredits: hasCredits,
- currentCredits,
- requiredCredits,
- };
- } catch (error) {
- console.error('check web content analysis credits error:', error);
- return {
- success: false,
- error: error instanceof Error ? error.message : 'Something went wrong',
- };
- }
- }
-);
diff --git a/src/ai/text/components/error-display.tsx b/src/ai/text/components/error-display.tsx
index 489ddd6..43a9435 100644
--- a/src/ai/text/components/error-display.tsx
+++ b/src/ai/text/components/error-display.tsx
@@ -34,7 +34,6 @@ interface ErrorDisplayProps {
const errorIcons = {
[ErrorType.VALIDATION]: AlertCircleIcon,
[ErrorType.NETWORK]: WifiOffIcon,
- [ErrorType.CREDITS]: CreditCardIcon,
[ErrorType.SCRAPING]: ServerIcon,
[ErrorType.ANALYSIS]: HelpCircleIcon,
[ErrorType.TIMEOUT]: ClockIcon,
@@ -84,7 +83,6 @@ const severityColors = {
const errorTitles = {
[ErrorType.VALIDATION]: 'Invalid Input',
[ErrorType.NETWORK]: 'Connection Error',
- [ErrorType.CREDITS]: 'Insufficient Credits',
[ErrorType.SCRAPING]: 'Unable to Access Website',
[ErrorType.ANALYSIS]: 'Analysis Failed',
[ErrorType.TIMEOUT]: 'Request Timed Out',
diff --git a/src/ai/text/components/url-input-form.tsx b/src/ai/text/components/url-input-form.tsx
index 504d7de..8528b2e 100644
--- a/src/ai/text/components/url-input-form.tsx
+++ b/src/ai/text/components/url-input-form.tsx
@@ -1,9 +1,7 @@
'use client';
-import { checkWebContentAnalysisCreditsAction } from '@/actions/check-web-content-analysis-credits';
import type { UrlInputFormProps } from '@/ai/text/utils/web-content-analyzer';
import { webContentAnalyzerConfig } from '@/ai/text/utils/web-content-analyzer-config';
-import { LoginWrapper } from '@/components/auth/login-wrapper';
import { Button } from '@/components/ui/button';
import {
Form,
@@ -20,21 +18,10 @@ import {
SelectTrigger,
SelectValue,
} from '@/components/ui/select';
-import { useLocalePathname } from '@/i18n/navigation';
-import { authClient } from '@/lib/auth-client';
import { zodResolver } from '@hookform/resolvers/zod';
-import {
- AlertCircleIcon,
- CoinsIcon,
- LinkIcon,
- Loader2Icon,
- LogInIcon,
- SparklesIcon,
-} from 'lucide-react';
-import { useAction } from 'next-safe-action/hooks';
+import { LinkIcon, Loader2Icon, SparklesIcon } from 'lucide-react';
import { useEffect, useState } from 'react';
import { useForm } from 'react-hook-form';
-import { toast } from 'sonner';
import { z } from 'zod';
import { useDebounce } from '../utils/performance';
@@ -52,19 +39,9 @@ export function UrlInputForm({
modelProvider,
setModelProvider,
}: UrlInputFormProps) {
- const [creditInfo, setCreditInfo] = useState<{
- hasEnoughCredits: boolean;
- currentCredits: number;
- requiredCredits: number;
- } | null>(null);
const [mounted, setMounted] = useState(false);
- // Get authentication status and current path for callback
- const { data: session, isPending: isAuthLoading } = authClient.useSession();
- const isAuthenticated = !!session?.user;
- const currentPath = useLocalePathname();
-
- // Prevent hydration mismatch by only rendering auth-dependent content after mount
+ // Prevent hydration mismatch by only rendering content after mount
useEffect(() => {
setMounted(true);
}, []);
@@ -84,42 +61,6 @@ export function UrlInputForm({
webContentAnalyzerConfig.performance.urlInputDebounceMs
);
- const { execute: checkCredits, isExecuting: isCheckingCredits } = useAction(
- checkWebContentAnalysisCreditsAction,
- {
- onSuccess: (result) => {
- if (result.data?.success) {
- setCreditInfo({
- hasEnoughCredits: result.data.hasEnoughCredits ?? false,
- currentCredits: result.data.currentCredits ?? 0,
- requiredCredits: result.data.requiredCredits ?? 0,
- });
- } else {
- // Only show error toast if it's not an auth error
- if (result.data?.error !== 'Unauthorized') {
- setTimeout(() => {
- toast.error(result.data?.error || 'Failed to check credits');
- }, 0);
- }
- }
- },
- onError: (error) => {
- console.error('Credit check error:', error);
- // Only show error toast for non-auth errors
- setTimeout(() => {
- toast.error('Failed to check credits');
- }, 0);
- },
- }
- );
-
- // Check credits only when user is authenticated
- useEffect(() => {
- if (isAuthenticated && !isAuthLoading) {
- checkCredits();
- }
- }, [isAuthenticated, isAuthLoading, checkCredits]);
-
// Debounced URL validation effect
useEffect(() => {
if (debouncedUrl && debouncedUrl !== urlValue) {
@@ -129,23 +70,12 @@ export function UrlInputForm({
}, [debouncedUrl, urlValue, form]);
const handleSubmit = (data: UrlFormData) => {
- // For authenticated users, check credits before submitting
- if (creditInfo && !creditInfo.hasEnoughCredits) {
- // Defer toast to avoid flushSync during render
- setTimeout(() => {
- toast.error(
- `Insufficient credits. You need ${creditInfo.requiredCredits} credits but only have ${creditInfo.currentCredits}.`
- );
- }, 0);
- return;
- }
onSubmit(data.url ?? '', modelProvider);
};
const handleFormSubmit = form.handleSubmit(handleSubmit);
- const isInsufficientCredits = creditInfo && !creditInfo.hasEnoughCredits;
- const isFormDisabled = isLoading || disabled || !!isInsufficientCredits;
+ const isFormDisabled = isLoading || disabled;
return (
<>
@@ -194,67 +124,20 @@ export function UrlInputForm({
)}
/>
- {/* Credit Information - Only show for authenticated users */}
- {isAuthenticated && creditInfo && (
-
-
-
-
- Cost: {creditInfo.requiredCredits} credits
-
-
-
-
- Balance: {creditInfo.currentCredits}
-
- {!creditInfo.hasEnoughCredits && (
-
- )}
-
-
- )}
-
- {/* Insufficient Credits Warning */}
- {isAuthenticated && isInsufficientCredits && (
-
-
-
- Insufficient credits. You need {creditInfo.requiredCredits}{' '}
- credits but only have {creditInfo.currentCredits}.
-
-
- )}
-
{!mounted ? (
// Show loading state during hydration to prevent mismatch
- ) : isAuthenticated ? (
+ ) : (
- ) : (
-
-
-
)}
diff --git a/src/ai/text/components/web-content-analyzer.tsx b/src/ai/text/components/web-content-analyzer.tsx
index e2b23d2..64efc6f 100644
--- a/src/ai/text/components/web-content-analyzer.tsx
+++ b/src/ai/text/components/web-content-analyzer.tsx
@@ -232,16 +232,6 @@ export function WebContentAnalyzer({ className }: WebContentAnalyzerProps) {
errorType = ErrorType.VALIDATION;
retryable = false;
break;
- case 401:
- errorType = ErrorType.AUTHENTICATION;
- severity = ErrorSeverity.HIGH;
- retryable = false;
- break;
- case 402:
- errorType = ErrorType.CREDITS;
- severity = ErrorSeverity.HIGH;
- retryable = false;
- break;
case 408:
errorType = ErrorType.TIMEOUT;
break;
diff --git a/src/ai/text/utils/error-handling.ts b/src/ai/text/utils/error-handling.ts
index 8896d5e..ea648a6 100644
--- a/src/ai/text/utils/error-handling.ts
+++ b/src/ai/text/utils/error-handling.ts
@@ -9,7 +9,6 @@ import { webContentAnalyzerConfig } from '@/ai/text/utils/web-content-analyzer-c
export enum ErrorType {
VALIDATION = 'validation',
NETWORK = 'network',
- CREDITS = 'credits',
SCRAPING = 'scraping',
ANALYSIS = 'analysis',
TIMEOUT = 'timeout',
@@ -96,22 +95,6 @@ export function classifyError(error: unknown): WebContentAnalyzerError {
);
}
- // Credit errors
- if (
- message.includes('credit') ||
- message.includes('insufficient') ||
- message.includes('balance')
- ) {
- return new WebContentAnalyzerError(
- ErrorType.CREDITS,
- error.message,
- 'Insufficient credits to perform analysis. Please purchase more credits.',
- ErrorSeverity.HIGH,
- false,
- error
- );
- }
-
// Scraping errors
if (
message.includes('scrape') ||
@@ -278,16 +261,6 @@ export function getRecoveryActions(error: WebContentAnalyzerError): Array<{
{ label: 'Try Simpler URL', action: 'simplify_url' },
];
- case ErrorType.CREDITS:
- return [
- {
- label: 'Purchase Credits',
- action: 'purchase_credits',
- primary: true,
- },
- { label: 'Check Balance', action: 'check_balance' },
- ];
-
case ErrorType.SCRAPING:
return [
{ label: 'Try Again', action: 'retry', primary: true },
diff --git a/src/ai/text/utils/web-content-analyzer-config.ts b/src/ai/text/utils/web-content-analyzer-config.ts
index 83b183c..078cb14 100644
--- a/src/ai/text/utils/web-content-analyzer-config.ts
+++ b/src/ai/text/utils/web-content-analyzer-config.ts
@@ -6,11 +6,6 @@
*/
export const webContentAnalyzerConfig = {
- /**
- * Credit cost for performing a web content analysis
- */
- creditsCost: 100,
-
/**
* Maximum content length for AI analysis (in characters)
* Optimized to prevent token limit issues while maintaining quality
@@ -118,21 +113,14 @@ export const webContentAnalyzerConfig = {
maxTokens: 2000,
},
openrouter: {
- model: 'openrouter/horizon-beta',
+ // model: 'openrouter/horizon-beta',
// model: 'x-ai/grok-3-beta',
- // model: 'openai/gpt-4o-mini',
+ model: 'openai/gpt-4o-mini',
temperature: 0.1,
maxTokens: 2000,
},
} as const;
-/**
- * Get the credit cost for web content analysis
- */
-export function getWebContentAnalysisCost(): number {
- return webContentAnalyzerConfig.creditsCost;
-}
-
/**
* Validates if the Firecrawl API key is configured
*/
@@ -151,8 +139,6 @@ export function validateFirecrawlConfig(): boolean {
*/
export function validateWebContentAnalyzerConfig(): boolean {
return (
- typeof webContentAnalyzerConfig.creditsCost === 'number' &&
- webContentAnalyzerConfig.creditsCost > 0 &&
typeof webContentAnalyzerConfig.maxContentLength === 'number' &&
webContentAnalyzerConfig.maxContentLength > 0 &&
typeof webContentAnalyzerConfig.timeoutMillis === 'number' &&
diff --git a/src/app/api/analyze-content/route.ts b/src/app/api/analyze-content/route.ts
index 91db24e..ef01183 100644
--- a/src/app/api/analyze-content/route.ts
+++ b/src/app/api/analyze-content/route.ts
@@ -13,12 +13,9 @@ import {
validateUrl,
} from '@/ai/text/utils/web-content-analyzer';
import {
- getWebContentAnalysisCost,
validateFirecrawlConfig,
webContentAnalyzerConfig,
} from '@/ai/text/utils/web-content-analyzer-config';
-import { consumeCredits, hasEnoughCredits } from '@/credits/credits';
-import { getSession } from '@/lib/server';
import { createDeepSeek } from '@ai-sdk/deepseek';
import { createGoogleGenerativeAI } from '@ai-sdk/google';
import { createOpenAI } from '@ai-sdk/openai';
@@ -30,7 +27,6 @@ import { z } from 'zod';
// Constants from configuration
const TIMEOUT_MILLIS = webContentAnalyzerConfig.timeoutMillis;
-const CREDITS_COST = getWebContentAnalysisCost();
const MAX_CONTENT_LENGTH = webContentAnalyzerConfig.maxContentLength;
// Initialize Firecrawl client
@@ -361,28 +357,6 @@ export async function POST(req: NextRequest) {
);
}
- // Check authentication
- const session = await getSession();
- if (!session) {
- const authError = new WebContentAnalyzerError(
- ErrorType.AUTHENTICATION,
- 'Authentication required',
- 'Please sign in to analyze web content.',
- ErrorSeverity.HIGH,
- false
- );
-
- logError(authError, { requestId });
-
- return NextResponse.json(
- {
- success: false,
- error: authError.userMessage,
- } satisfies AnalyzeContentResponse,
- { status: 401 }
- );
- }
-
// Check if Firecrawl is configured
if (!validateFirecrawlConfig()) {
const configError = new WebContentAnalyzerError(
@@ -404,39 +378,7 @@ export async function POST(req: NextRequest) {
);
}
- // Check if user has sufficient credits before starting analysis
- const hasCredits = await hasEnoughCredits({
- userId: session.user.id,
- requiredCredits: CREDITS_COST,
- });
-
- if (!hasCredits) {
- const creditError = new WebContentAnalyzerError(
- ErrorType.CREDITS,
- 'Insufficient credits to perform analysis',
- "You don't have enough credits to analyze this webpage. Please purchase more credits.",
- ErrorSeverity.HIGH,
- false
- );
-
- logError(creditError, {
- requestId,
- userId: session.user.id,
- requiredCredits: CREDITS_COST,
- });
-
- return NextResponse.json(
- {
- success: false,
- error: creditError.userMessage,
- } satisfies AnalyzeContentResponse,
- { status: 402 }
- );
- }
-
- console.log(
- `Starting analysis [requestId=${requestId}, url=${url}, userId=${session.user.id}]`
- );
+ console.log(`Starting analysis [requestId=${requestId}, url=${url}]`);
// Perform analysis with timeout and enhanced error handling
const analysisPromise = (async () => {
@@ -447,13 +389,6 @@ export async function POST(req: NextRequest) {
// Step 2: Analyze content with AI (pass provider)
const analysis = await analyzeContent(content, url, modelProvider);
- // Step 3: Consume credits (only on successful analysis)
- await consumeCredits({
- userId: session.user.id,
- amount: CREDITS_COST,
- description: `Web content analysis: ${url}`,
- });
-
return { analysis, screenshot };
} catch (error) {
// If it's already a WebContentAnalyzerError, just re-throw
@@ -477,7 +412,6 @@ export async function POST(req: NextRequest) {
return NextResponse.json({
success: true,
data: result,
- creditsConsumed: CREDITS_COST,
} satisfies AnalyzeContentResponse);
} catch (error) {
const elapsed = ((performance.now() - startTime) / 1000).toFixed(1);
@@ -499,12 +433,6 @@ export async function POST(req: NextRequest) {
case ErrorType.VALIDATION:
statusCode = 400;
break;
- case ErrorType.AUTHENTICATION:
- statusCode = 401;
- break;
- case ErrorType.CREDITS:
- statusCode = 402;
- break;
case ErrorType.TIMEOUT:
statusCode = 408;
break;