import { createClient } from '@supabase/supabase-js' import { NextResponse } from 'next/server' import type { CreateBookRequest } from '@/lib/types/database' import { templateService } from '@/lib/services/template-service' // Create server-side Supabase client with service role for admin operations function createServerSupabaseClient() { const serviceRoleKey = process.env.SUPABASE_SERVICE_ROLE_KEY if (!serviceRoleKey) { throw new Error('SUPABASE_SERVICE_ROLE_KEY is not set') } return createClient( process.env.NEXT_PUBLIC_SUPABASE_URL!, serviceRoleKey, { auth: { autoRefreshToken: false, persistSession: false } } ) } // GET /api/books - Get all books for the authenticated user export async function GET(request: Request) { try { const url = new URL(request.url) const userId = url.searchParams.get('userId') if (!userId) { return NextResponse.json({ error: 'User ID is required' }, { status: 400 }) } const supabase = createServerSupabaseClient() // Get user's books const { data: books, error } = await supabase .from('books') .select(` id, title, description, status, word_count, created_at, updated_at, author, genre, target_word_count, cover_image_url `) .eq('user_id', userId) .order('updated_at', { ascending: false }) if (error) { console.error('Error fetching books:', error) return NextResponse.json({ error: 'Failed to fetch books' }, { status: 500 }) } // Transform dates to be compatible with frontend const transformedBooks = books?.map((book: any) => ({ ...book, lastModified: book.updated_at.split('T')[0], // Convert to YYYY-MM-DD format for compatibility })) || [] return NextResponse.json({ books: transformedBooks }) } catch (error) { console.error('Unexpected error:', error) return NextResponse.json({ error: 'Internal server error' }, { status: 500 }) } } // POST /api/books - Create a new book export async function POST(request: Request) { try { const supabase = createServerSupabaseClient() // Parse request body const body: CreateBookRequest & { userId: string } = await request.json() // Validate required fields if (!body.title || body.title.trim() === '') { return NextResponse.json({ error: 'Title is required' }, { status: 400 }) } if (!body.userId) { return NextResponse.json({ error: 'User ID is required' }, { status: 400 }) } // **CRITICAL**: Check book creation limits BEFORE creating the book try { // Get user's current usage const { data: usageRecords, error: usageError } = await supabase .from('user_usage') .select('*') .eq('user_id', body.userId) .order('updated_at', { ascending: false }) .limit(1) if (usageError) { console.error('Error checking book creation limits:', usageError) // Continue with creation if we can't check limits (fail open) } else { const currentUsage = usageRecords?.[0] || null const booksCreated = currentUsage?.books_created || 0 // Get user's subscription to determine book limits const { data: subscriptionData, error: subscriptionError } = await supabase .from('subscriptions') .select('price_id, status') .eq('user_id', body.userId) .eq('status', 'active') .order('created_at', { ascending: false }) .limit(1) if (subscriptionError) { console.error('Error getting subscription for book limits:', subscriptionError) } // Get the first subscription or null if none exists const subscription = subscriptionData && subscriptionData.length > 0 ? subscriptionData[0] : null // Default to free tier limits const { PRICING_TIERS } = await import('@/lib/stripe') let bookLimit = PRICING_TIERS.FREE.maxBooks // Set limits based on subscription if (subscription?.price_id) { const { getPlanByPriceId } = await import('@/lib/stripe') const plan = getPlanByPriceId(subscription.price_id) if (plan) { bookLimit = plan.maxBooks } } // Check if user has reached their book limit if (bookLimit !== undefined && bookLimit !== null && booksCreated >= bookLimit) { return NextResponse.json({ error: `You've reached your book creation limit (${booksCreated}/${bookLimit}). Upgrade your plan to create more books.`, limitExceeded: true, usageInfo: { currentUsage: booksCreated, limit: bookLimit, feature: 'books' } }, { status: 429 }) // 429 Too Many Requests } } } catch (limitCheckError) { console.error('Error checking book creation limits:', limitCheckError) // Continue with book creation if limit check fails (fail open for better UX) } // Handle genres - convert array to comma-separated string or use first genre for backward compatibility const genreValue = Array.isArray(body.genres) && body.genres.length > 0 ? body.genres.join(', ') : body.genre?.trim() || null // Create the book const { data: book, error } = await supabase .from('books') .insert({ title: body.title.trim(), description: body.description?.trim() || null, author: body.author?.trim() || null, genre: genreValue, target_word_count: body.target_word_count || null, template_id: body.template_id || null, user_id: body.userId, status: 'Planning', word_count: 0 }) .select() .single() if (error) { console.error('Error creating book:', error) return NextResponse.json({ error: 'Failed to create book' }, { status: 500 }) } // Create initial folder structure for the new book using template await templateService.createBookFromTemplate(supabase, book.id, body.template_id) // **IMPORTANT**: Increment book creation counter after successful creation try { const { error: incrementError } = await supabase .rpc('increment_usage', { user_uuid: body.userId, feature_type_param: 'books', amount_param: 1, metadata_param: { book_id: book.id, book_title: book.title, template_id: body.template_id }, skip_limit_check: true // Skip limit check since we already validated before creation }) if (incrementError) { console.error('Failed to increment book creation counter:', incrementError) // Don't fail the whole operation - book creation is more important } } catch (incrementError) { console.error('Error incrementing book creation counter:', incrementError) // Don't fail the whole operation } // Transform the response const transformedBook = { ...book, lastModified: book.updated_at.split('T')[0], } return NextResponse.json({ book: transformedBook }, { status: 201 }) } catch (error) { console.error('Unexpected error:', error) return NextResponse.json({ error: 'Internal server error' }, { status: 500 }) } } // Legacy function - now replaced by template system // Kept for reference only async function createInitialBookStructure(supabase: any, bookId: string) { try { // Create comprehensive initial structure for a new book const items = [ // Root level welcome files { book_id: bookId, name: '📖 Welcome to Your New Book', type: 'file', content: `# Welcome to Your New Book! 🎉 Congratulations on starting your writing journey with Bookwiz! This structure is designed to help you make the most of our AI-powered writing assistant. ## Quick Start Checklist ✅ - [ ] Read the Getting Started Guide - [ ] Explore the AI Writing Assistant Guide - [ ] Customize your Book Overview in the planning folder - [ ] Try the example prompts with the AI chat - [ ] Start writing your first chapter! ## What's Included This book comes pre-loaded with: - **Guides** - Learn how to write with AI assistance - **Templates** - Ready-to-use outlines and character sheets - **Examples** - See AI collaboration in action - **Planning** - Structure your story - **Your Content** - Chapters, characters, research ## Need Help? Open the AI chat panel and try asking: - "Help me plan my story structure" - "Act as my main character and answer questions" - "Improve this scene I'm working on" Happy writing! 📝✨`, file_extension: 'md', mime_type: 'text/markdown', sort_order: 1 }, { book_id: bookId, name: '🚀 Getting Started Guide', type: 'file', content: `# Getting Started with Bookwiz 🚀 ## How This Works Bookwiz combines traditional writing with AI assistance. You write, the AI helps enhance, brainstorm, and develop your ideas. ## Basic Workflow 1. **Plan** - Use templates in planning/ to outline your story 2. **Create** - Start writing in the chapters/ folder 3. **Enhance** - Use AI to improve, expand, or brainstorm 4. **Organize** - Keep research and character notes updated ## Navigation Tips - **Left Panel**: File explorer (organize your content) - **Center**: Writing area (your main workspace) - **Right Panel**: AI chat (your writing assistant) ## File Organization - **guides/** - Educational content about writing with AI - **templates/** - Blank forms you can copy and customize - **examples/** - See how AI collaboration works - **planning/** - Your story structure and outlines - **characters/** - Character development and profiles - **research/** - Notes, worldbuilding, references - **chapters/** - Your actual book content ## Customizing This Structure Feel free to: - Rename folders to match your book - Delete files you don't need - Add new folders for specific needs - Move files around as needed Start with the AI Writing Assistant Guide to learn how to collaborate effectively with AI!`, file_extension: 'md', mime_type: 'text/markdown', sort_order: 2 }, { book_id: bookId, name: '🤖 AI Writing Assistant Guide', type: 'file', content: `# AI Writing Assistant Guide 🤖 ## What the AI Can Do Your AI writing assistant can help with: - **Brainstorming** ideas and plot developments - **Character development** and dialogue - **Scene enhancement** and descriptions - **Plot problem solving** when you're stuck - **Research** and fact-checking - **Style improvements** and editing suggestions - **Role-playing** as your characters ## How to Get Better Results ### Give Context Instead of: "Write a scene" Try: "I'm writing a fantasy novel about a young mage. Write a scene where she discovers her powers for the first time. She's shy and has always felt ordinary." ### Be Specific Instead of: "Make this better" Try: "Make this dialogue more natural and add more tension between these two characters who used to be friends" ### Iterate - Start with a basic request - Review the AI's response - Ask for specific improvements - Build on what works ## Example Conversation Starters - "Help me brainstorm what my villain's motivation could be" - "I'm stuck on this plot point: [describe situation]. What are some ways this could resolve?" - "Act as [Character Name] and answer some questions about their backstory" - "Here's a scene I wrote: [paste scene]. How can I make it more engaging?" - "What are some good plot twists for a [genre] story?" ## Advanced Techniques - **Character Interviews**: Have AI roleplay as your characters - **Plot Hole Detection**: Ask AI to find inconsistencies - **Style Matching**: Ask AI to write in your established style - **Research Assistant**: Get help with world-building details Check out the examples/ folder to see these techniques in action!`, file_extension: 'md', mime_type: 'text/markdown', sort_order: 3 }, // Guides folder { book_id: bookId, name: 'guides', type: 'folder', expanded: false, sort_order: 4 }, // Templates folder { book_id: bookId, name: 'templates', type: 'folder', expanded: false, sort_order: 5 }, // Examples folder { book_id: bookId, name: 'examples', type: 'folder', expanded: false, sort_order: 6 }, // Planning folder { book_id: bookId, name: 'planning', type: 'folder', expanded: true, sort_order: 7 }, // Characters folder { book_id: bookId, name: 'characters', type: 'folder', expanded: false, sort_order: 8 }, // Research folder { book_id: bookId, name: 'research', type: 'folder', expanded: false, sort_order: 9 }, // Chapters folder { book_id: bookId, name: 'chapters', type: 'folder', expanded: true, sort_order: 10 } ] // Insert base structure first const { data: baseItems, error: baseError } = await supabase .from('file_system_items') .insert(items) .select() if (baseError) { console.error('Error creating base book structure:', baseError) return } // Get folder IDs for nested items const guidesFolder = baseItems.find((item: any) => item.name === 'guides') const templatesFolder = baseItems.find((item: any) => item.name === 'templates') const examplesFolder = baseItems.find((item: any) => item.name === 'examples') const planningFolder = baseItems.find((item: any) => item.name === 'planning') const charactersFolder = baseItems.find((item: any) => item.name === 'characters') const researchFolder = baseItems.find((item: any) => item.name === 'research') const chaptersFolder = baseItems.find((item: any) => item.name === 'chapters') // Create nested items const nestedItems = [ // Guide files { book_id: bookId, parent_id: guidesFolder?.id, name: 'how-to-write-with-ai', type: 'file', content: `# How to Write with AI 🤖✍️ ## Core Principles ### 1. AI as a Collaborator, Not a Replacement - Use AI to enhance your ideas, not replace your creativity - Your voice and vision should always guide the story - AI helps overcome blocks, not create your entire book ### 2. Iterative Development - Start with rough ideas - Use AI to explore and expand - Refine through multiple iterations - Always maintain creative control ## Effective AI Writing Techniques ### Character Development \`\`\` Prompt: "I have a character who is [brief description]. Help me develop their backstory, particularly focusing on [specific aspect]. Ask me questions to help flesh this out." \`\`\` ### Plot Development \`\`\` Prompt: "My story is about [premise]. I'm at the point where [current situation]. What are 5 different ways this could develop, considering my character's personality and the story's themes?" \`\`\` ### Scene Writing \`\`\` Prompt: "Help me write a scene where [situation]. The mood should be [emotion/tone]. Focus on [specific elements like dialogue, action, description]." \`\`\` ### Dialogue Improvement \`\`\` Prompt: "Here's some dialogue I wrote: [paste dialogue]. Make it sound more natural while keeping each character's distinct voice. [Character A] is [personality], [Character B] is [personality]." \`\`\` ## Best Practices - **Always provide context** about your story, characters, and goals - **Be specific** about what kind of help you need - **Review and edit** AI suggestions to match your style - **Ask follow-up questions** to refine ideas - **Combine multiple AI responses** for richer content`, file_extension: 'md', mime_type: 'text/markdown', sort_order: 1 }, // Template files { book_id: bookId, parent_id: templatesFolder?.id, name: 'character-sheet-template', type: 'file', content: `# Character Sheet Template ## Basic Information - **Name**: - **Age**: - **Occupation**: - **Role in Story**: (Protagonist/Antagonist/Supporting) ## Physical Description - **Appearance**: - **Distinguishing Features**: - **Style/Clothing**: ## Personality - **Core Traits**: - **Strengths**: - **Flaws**: - **Fears**: - **Desires**: - **Motivations**: ## Background - **Family**: - **Education**: - **Past Experiences**: - **Formative Events**: ## Story Arc - **Starting Point**: - **Character Growth**: - **Ending Point**: - **Key Relationships**: ## Voice & Dialogue - **Speech Patterns**: - **Vocabulary Level**: - **Accent/Dialect**: - **Favorite Phrases**: ## AI Collaboration Notes *Use this section to note how you want AI to portray this character* - **Key personality aspects to emphasize**: - **How they would react to conflict**: - **Their decision-making style**: --- **AI Prompt to develop this character:** "Act as [Character Name]. You are [brief personality description]. Answer questions and respond to situations as this character would, considering their background and motivations."`, file_extension: 'md', mime_type: 'text/markdown', sort_order: 1 }, // Example files { book_id: bookId, parent_id: examplesFolder?.id, name: 'ai-prompt-examples', type: 'file', content: `# AI Prompt Examples 💡 ## Plot Development Prompts ### When You're Stuck \`\`\` "I'm writing a [genre] story about [brief premise]. My protagonist just [recent event], but I'm not sure what should happen next. Given that the main conflict is [describe conflict] and my character's goal is [goal], what are some compelling directions the story could take?" \`\`\` ### Creating Tension \`\`\` "I need to add more tension to this scene: [describe scene]. The characters are [character descriptions]. How can I increase the stakes and make readers more invested in the outcome?" \`\`\` ## Character Development Prompts ### Character Interviews \`\`\` "Please roleplay as my character [Name]. Here's their background: [background]. I'm going to ask you questions about their past, motivations, and how they'd react in different situations. Stay in character and answer as they would." \`\`\` ### Relationship Dynamics \`\`\` "I have two characters: [Character A description] and [Character B description]. They need to [situation/conflict]. How would their different personalities create tension or cooperation in this scenario?" \`\`\` ## Scene Enhancement Prompts ### Improving Descriptions \`\`\` "Here's a scene I wrote: [paste scene]. Help me make the setting more vivid and atmospheric. The mood should be [mood] and I want readers to feel [emotion]." \`\`\` ### Dialogue Polish \`\`\` "Improve this dialogue to sound more natural: [paste dialogue]. Character A is [personality] and Character B is [personality]. Make sure each character has a distinct voice." \`\`\` ## Research & Worldbuilding Prompts ### Historical Research \`\`\` "I'm writing a story set in [time period/location]. What are some interesting historical details about [specific aspect] that could add authenticity to my story?" \`\`\` ### Fantasy/Sci-Fi Worldbuilding \`\`\` "Help me develop the rules for [magic system/technology/society] in my world. It should be [characteristics] and create interesting limitations that drive conflict." \`\`\` ## Problem-Solving Prompts ### Plot Holes \`\`\` "I think I have a plot hole: [describe issue]. Given what I've established about [relevant story elements], how can I resolve this logically while staying true to my story's rules?" \`\`\` ### Pacing Issues \`\`\` "This part of my story feels too [slow/fast]: [describe section]. How can I adjust the pacing while maintaining [story elements you want to keep]?" \`\`\``, file_extension: 'md', mime_type: 'text/markdown', sort_order: 1 }, // Planning files { book_id: bookId, parent_id: planningFolder?.id, name: 'book-overview', type: 'file', content: `# Book Overview ## Basic Information - **Title**: - **Genre**: - **Target Audience**: - **Estimated Length**: ## Core Concept **Logline** (1-2 sentences): **Premise** (1 paragraph): ## Main Elements **Protagonist**: **Central Conflict**: **Stakes**: **Theme**: ## Setting **Time Period**: **Location(s)**: **World Details**: ## Target Goals - **Daily Word Count**: - **Total Target**: - **Completion Date**: ## AI Collaboration Strategy *How do you plan to use AI for this project?* - **Research areas where AI can help**: - **Character development assistance needed**: - **Plot elements to brainstorm**: --- **AI Prompt for this book:** "I'm writing a [genre] novel about [brief premise]. The main character is [protagonist description] and the central conflict involves [conflict]. Help me develop [specific areas where you need help]."`, file_extension: 'md', mime_type: 'text/markdown', sort_order: 1 }, // Sample chapter { book_id: bookId, parent_id: chaptersFolder?.id, name: 'chapter-01-sample', type: 'file', content: `# Chapter 1: The Beginning *This is a sample chapter showing how you might collaborate with AI. Delete this and start your own story!* ## AI Collaboration Notes *I asked the AI: "Help me write an opening scene for a mystery novel. The protagonist is a detective who just moved to a small town. Create intrigue but don't reveal too much."* --- Detective Sarah Chen had been in Millbrook exactly three days when she found the first note. It was tucked under her windshield wiper, barely visible in the pre-dawn darkness. The paper was thick, expensive—the kind that came from the town's only stationery shop. Someone had written in careful script: "Welcome to town. Some secrets are better left buried." Sarah glanced around the empty parking lot outside Miller's Diner. Main Street was deserted except for a single streetlight casting long shadows between the antique shops and cafes. Somewhere in the distance, a dog barked twice and fell silent. *AI Enhancement Note: I asked the AI to "make this opening more atmospheric and add a sense of unease." The AI suggested adding sensory details and the ominous note to create immediate intrigue.* She folded the note and slipped it into her jacket pocket. In Chicago, anonymous threats were part of the job. But this felt different—personal, deliberate. Someone knew she was here, knew she was a cop, and wanted her gone before she'd even unpacked her boxes. *Next AI prompt: "What should Sarah's reaction be? She's experienced but this is her first day in a small town where everyone knows everyone. Show her investigative instincts kicking in."* ## Writing Process Notes - Started with basic scene idea - Used AI to enhance atmosphere - Asked for character reaction suggestions - Will develop mystery elements in next scene ## Questions for AI: - Who might have left the note? - What secrets could a small town be hiding? - How should Sarah investigate without seeming paranoid?`, file_extension: 'md', mime_type: 'text/markdown', sort_order: 1 } ] // Insert nested items await supabase .from('file_system_items') .insert(nestedItems) } catch (error) { console.error('Unexpected error creating book structure:', error) } }