diff --git a/app/api/add-page/route.ts b/app/api/add-page/route.ts
index 1a5f2fc..eaf72e5 100644
--- a/app/api/add-page/route.ts
+++ b/app/api/add-page/route.ts
@@ -140,7 +140,7 @@ export async function POST(request: NextRequest) {
});
const client = new Together({
- apiKey: process.env.TOGETHER_API_KEY_DEFAULT,
+ apiKey: process.env.TOGETHER_API_KEY,
});
let response;
diff --git a/app/api/check-credits/route.ts b/app/api/check-credits/route.ts
new file mode 100644
index 0000000..7c65b93
--- /dev/null
+++ b/app/api/check-credits/route.ts
@@ -0,0 +1,71 @@
+import { type NextRequest, NextResponse } from "next/server";
+import { auth } from "@clerk/nextjs/server";
+import { freeTierRateLimit } from "@/lib/rate-limit";
+import { db } from "@/lib/db";
+import { stories } from "@/lib/schema";
+import { gte } from "drizzle-orm";
+
+export async function POST(request: NextRequest) {
+ try {
+ const { userId } = await auth();
+
+ if (!userId) {
+ return NextResponse.json(
+ { error: "Authentication required" },
+ { status: 401 }
+ );
+ }
+
+ const { hasApiKey } = await request.json();
+
+ // Check if user has API key (unlimited)
+ if (hasApiKey) {
+ return NextResponse.json({
+ hasApiKey: true,
+ creditsRemaining: "unlimited",
+ resetTime: null,
+ });
+ }
+
+ // For free tier, check rate limit status
+ try {
+ // Try to check remaining without consuming
+ const limitResult = await freeTierRateLimit.getRemaining(userId);
+
+ return NextResponse.json({
+ hasApiKey: false,
+ creditsRemaining: limitResult.remaining,
+ resetTime: limitResult.reset,
+ });
+ } catch (error) {
+ console.error("Rate limit check error:", error);
+ // Fallback to database check
+ const sevenDaysAgo = new Date();
+ sevenDaysAgo.setDate(sevenDaysAgo.getDate() - 7);
+
+ const recentStories = await db
+ .select()
+ .from(stories)
+ .where(gte(stories.createdAt, sevenDaysAgo))
+ .limit(1);
+
+ const hasUsedCredit = recentStories.length > 0;
+
+ return NextResponse.json({
+ hasApiKey: false,
+ creditsRemaining: hasUsedCredit ? 0 : 1,
+ resetTime: hasUsedCredit ? sevenDaysAgo.getTime() + 7 * 24 * 60 * 60 * 1000 : null,
+ });
+ }
+ } catch (error) {
+ console.error("Error in check-credits API:", error);
+ return NextResponse.json(
+ {
+ error: `Internal server error: ${
+ error instanceof Error ? error.message : "Unknown error"
+ }`,
+ },
+ { status: 500 }
+ );
+ }
+}
\ No newline at end of file
diff --git a/app/api/generate-comic/route.ts b/app/api/generate-comic/route.ts
index 3270c62..512f14c 100644
--- a/app/api/generate-comic/route.ts
+++ b/app/api/generate-comic/route.ts
@@ -65,7 +65,7 @@ export async function POST(request: NextRequest) {
if (isUsingFreeTier) {
// Use default API key for free tier
- finalApiKey = process.env.TOGETHER_API_KEY_DEFAULT;
+ finalApiKey = process.env.TOGETHER_API_KEY
if (!finalApiKey) {
return NextResponse.json(
{
diff --git a/app/story/[storySlug]/story-editor-client.tsx b/app/story/[storySlug]/story-editor-client.tsx
index acaa741..ca8fac5 100644
--- a/app/story/[storySlug]/story-editor-client.tsx
+++ b/app/story/[storySlug]/story-editor-client.tsx
@@ -167,16 +167,55 @@ export function StoryEditorClient() {
}, [pages.length, apiKey]);
- const handleAddPage = () => {
+ const handleAddPage = async () => {
if (!isLoaded || !isSignedIn) {
return;
}
-
- if (!apiKey && pages.length >= 1) {
- setShowApiModal(true);
- return;
+
+ // Check credits
+ try {
+ const hasApiKey = !!apiKey;
+ const response = await fetch('/api/check-credits', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify({ hasApiKey }),
+ });
+ const data = await response.json();
+
+ if (!response.ok) {
+ toast({
+ title: "Error",
+ description: "Failed to check credits",
+ variant: "destructive",
+ });
+ return;
+ }
+
+ if (hasApiKey || data.creditsRemaining === "unlimited") {
+ // Has API key, unlimited
+ setShowGenerateModal(true);
+ } else if (data.creditsRemaining > 0) {
+ // Has credits
+ setShowGenerateModal(true);
+ } else {
+ // No credits left, show API modal
+ setShowApiModal(true);
+ toast({
+ title: "No credits remaining",
+ description: "You get 1 credit weekly. Add an API key for unlimited generation.",
+ variant: "destructive",
+ });
+ }
+ } catch (error) {
+ console.error("Error checking credits:", error);
+ toast({
+ title: "Error",
+ description: "Failed to check credits",
+ variant: "destructive",
+ });
}
- setShowGenerateModal(true);
};
const handleRedrawPage = () => {
diff --git a/components/api-key-modal.tsx b/components/api-key-modal.tsx
index 955fcbe..51c8117 100644
--- a/components/api-key-modal.tsx
+++ b/components/api-key-modal.tsx
@@ -70,11 +70,11 @@ export function ApiKeyModal({ isOpen, onClose, onSubmit }: ApiKeyModalProps) {
: "Add your API key to continue"}
-