### Install Project Dependencies using PNPM Source: https://github.com/haydenbleasel/tersa/blob/main/README.md This command installs all the necessary project dependencies using the PNPM package manager. Make sure PNPM is installed globally. ```shell pnpm install ``` -------------------------------- ### Run Tersa Development Server Source: https://github.com/haydenbleasel/tersa/blob/main/README.md This command starts the development server for Tersa, allowing you to preview and test the application locally. Access the application via http://localhost:3000. ```shell pnpm dev ``` -------------------------------- ### Clone Tersa Repository using Git Source: https://github.com/haydenbleasel/tersa/blob/main/README.md This command clones the Tersa repository from GitHub to your local machine. Ensure you have Git installed and configured. ```shell git clone https://github.com/haydenbleasel/tersa.git cd tersa ``` -------------------------------- ### Track AI Resource Credit Usage (TypeScript) Source: https://context7.com/haydenbleasel/tersa/llms.txt Stripe-integrated system for metering AI resource consumption. The `trackCreditUsage` function logs the cost of AI operations, automatically converting dollar amounts to credits. It creates billing meter events linked to the customer's profile and is used across all AI generation endpoints. Examples include tracking image generation costs and chat-based interactions with token-based cost calculations. ```typescript // lib/stripe.ts - Track credit usage import { trackCreditUsage } from '@/lib/stripe'; // Track credits for any AI operation await trackCreditUsage({ action: 'generate_image', cost: 0.025 // Dollar amount }); // Credit value: $0.005 per credit // Cost is automatically converted to credits (rounded up) // Creates Stripe billing meter event // Links to customer via profile.customerId // Used in all AI generation endpoints // Example: Image generation with cost calculation const inputCost = 0.00002; // per token const outputCost = 0.00006; // per token const totalCost = (inputTokens * inputCost) + (outputTokens * outputCost); await trackCreditUsage({ action: 'chat', cost: totalCost }); ``` -------------------------------- ### Create AI Workflow Project (TypeScript) Source: https://context7.com/haydenbleasel/tersa/llms.txt Server action for creating new AI workflow projects. It takes a project name and an optional boolean flag for a welcome project. On success, it returns the project ID, which can be used for navigation. This action requires an authenticated user, sets default transcription and vision models, and stores project data in PostgreSQL using Drizzle ORM. ```typescript // Server action: createProjectAction // Create a new AI workflow project import { createProjectAction } from '@/app/actions/project/create'; const result = await createProjectAction( 'My AI Content Pipeline', false // welcomeProject flag ); if ('error' in result) { console.error(result.error); } else { const projectId = result.id; // Navigate to /projects/{projectId} } // Requires authenticated user // Sets default transcription and vision models // Stores in PostgreSQL via Drizzle ORM // Returns project ID on success // Content structure includes nodes, edges, viewport ``` -------------------------------- ### Checkout API - Stripe Subscription Management Source: https://context7.com/haydenbleasel/tersa/llms.txt This API creates Stripe checkout sessions for subscription plans, including usage-based billing for hobby and pro tiers. It requires an authenticated user and handles Stripe customer creation. ```APIDOC ## GET /api/checkout ### Description Creates Stripe checkout sessions for hobby and pro subscription plans with usage-based billing. ### Method GET ### Endpoint /api/checkout ### Parameters #### Query Parameters - **product** (string) - Required - The subscription product ('hobby' or 'pro'). - **frequency** (string) - Required - The subscription frequency ('month' or 'year'). ### Request Example ``` GET /api/checkout?product=pro&frequency=month ``` ### Response #### Success Response (200) - Redirects to Stripe hosted checkout. #### Error Response (401) - Returns 401 if the user is not logged in. #### Error Response (400) - Returns 400 for invalid product or frequency. ``` -------------------------------- ### Image Generation Action Source: https://context7.com/haydenbleasel/tersa/llms.txt A server action that generates images from text prompts, uploads them to Supabase storage, and generates image descriptions. It supports multiple AI models and tracks credit usage. ```APIDOC ## Server Action: generateImageAction ### Description Server action that generates images from text prompts using various AI models, automatically uploads to Supabase storage, and generates image descriptions. ### Method Server Action (Internal) ### Endpoint N/A (Called directly within Next.js Server Actions) ### Parameters #### Request Body - **modelId** (string) - Required - The ID of the image generation model (e.g., 'gpt-image-1'). - **prompt** (string) - Required - The text prompt for image generation. - **instructions** (string) - Optional - Additional instructions for image generation. - **size** (string) - Optional - The desired size of the image (e.g., '1024x1024'). - **nodeId** (string) - Required - The ID of the node in the workflow. - **projectId** (string) - Required - The ID of the project. ### Request Example ```typescript import { generateImageAction } from '@/app/actions/image/create'; const result = await generateImageAction({ modelId: 'gpt-image-1', prompt: 'A futuristic cityscape at sunset with flying cars', instructions: 'Make it vibrant with warm colors', size: '1024x1024', nodeId: 'node_123', projectId: 'project_456' }); ``` ### Response #### Success Response - **generated.url** (string) - Public URL to the generated image. - **generated.type** (string) - MIME type of the image. - **description** (string) - AI-generated description of the image. - **updatedAt** (string) - Timestamp of generation. #### Error Response - **error** (string) - An error message if image generation fails. ``` -------------------------------- ### Generate Video with Text and Image Prompts (TypeScript) Source: https://context7.com/haydenbleasel/tersa/llms.txt Server action for generating videos from text prompts, with optional image frames as input. It supports specifying model ID, prompt, and image details. The output includes a public URL to the generated MP4 video, its type, and the update timestamp. It defaults to a 5-second duration and 16:9 aspect ratio, uploads to Supabase storage, and tracks credit usage. ```typescript // Server action: generateVideoAction // Create videos with optional image prompts import { generateVideoAction } from '@/app/actions/video/create'; const result = await generateVideoAction({ modelId: 'luma-dream-machine', prompt: 'A serene mountain landscape with clouds moving', images: [ { url: 'https://storage.example.com/first-frame.jpg', type: 'image/jpeg' } ], nodeId: 'video_node_789', projectId: 'project_456' }); if ('error' in result) { console.error(result.error); } else { // result.nodeData contains: // - generated.url: Public URL to MP4 video // - generated.type: 'video/mp4' // - updatedAt: Timestamp of generation } // Default duration: 5 seconds // Default aspect ratio: 16:9 // Supports image-to-video with first frame // Uploads to Supabase storage as MP4 // Tracks credit usage based on duration ``` -------------------------------- ### Checkout API - Stripe Subscription Management (TypeScript) Source: https://context7.com/haydenbleasel/tersa/llms.txt API endpoint to create Stripe checkout sessions for subscription plans. Supports both hobby (free tier with usage billing) and pro (monthly/annual) plans. Requires an authenticated user and automatically handles Stripe customer creation or usage. Returns 401 for unauthenticated users and 400 for invalid parameters. ```typescript // GET /api/checkout?product=pro&frequency=month // Redirect to Stripe checkout for subscription // For hobby plan (free tier with usage billing) const hobbyUrl = '/api/checkout?product=hobby&frequency=month'; // For pro plan (monthly or annual) const proMonthlyUrl = '/api/checkout?product=pro&frequency=month'; const proAnnualUrl = '/api/checkout?product=pro&frequency=year'; // Requires authenticated user // Automatically creates or uses existing Stripe customer // Includes usage-based pricing for both tiers // Redirects to Stripe hosted checkout // Returns 401 if not logged in // Returns 400 for invalid product or frequency ``` -------------------------------- ### Generate Speech from Text (TypeScript) Source: https://context7.com/haydenbleasel/tersa/llms.txt Server action to convert text into speech audio using AI voice models. It allows specifying the model, text content, desired voice, and specific instructions for tone. The output provides a public URL to the generated MP3 audio, its MIME type, and the generation timestamp. The system supports multiple voices and charges based on text length, uploading the audio to Supabase storage. ```typescript // Server action: generateSpeechAction // Convert text to natural speech import { generateSpeechAction } from '@/app/actions/speech/create'; const result = await generateSpeechAction({ modelId: 'openai-tts', text: 'Welcome to Tersa, where you can build amazing AI workflows.', voice: 'alloy', instructions: 'Speak in an enthusiastic and friendly tone', nodeId: 'speech_node_321', projectId: 'project_456' }); if ('error' in result) { console.error(result.error); } else { // result.nodeData contains: // - generated.url: Public URL to MP3 audio // - generated.type: Audio MIME type // - updatedAt: Timestamp of generation } // Output format: MP3 // Supports multiple voices (alloy, echo, fable, onyx, nova, shimmer) // Credit cost based on text length // Uploads to Supabase storage // Updates project node data ``` -------------------------------- ### TypeScript: PostgreSQL Schema with Drizzle ORM for Projects and Profiles Source: https://context7.com/haydenbleasel/tersa/llms.txt Defines PostgreSQL database schemas for 'projects' and 'profile' using Drizzle ORM. The 'projects' schema includes fields for project details, content, ownership, and members. The 'profile' schema stores user-specific subscription and Stripe information. It demonstrates querying user projects and updating project content. ```typescript // schema.ts - Database models import { projects, profile } from '@/schema'; import { database } from '@/lib/database'; import { eq } from 'drizzle-orm'; // Projects table structure // - id: UUID primary key // - name: Project name // - transcriptionModel: Default model for audio transcription // - visionModel: Default model for image analysis // - content: JSON storing { nodes, edges, viewport } // - userId: Owner reference // - image: Project thumbnail URL // - members: Array of user IDs // - welcomeProject: Boolean for demo projects // - createdAt, updatedAt: Timestamps // Query projects const userProjects = await database .select() .from(projects) .where(eq(projects.userId, userId)); // Update project content await database .update(projects) .set({ content: { nodes: updatedNodes, edges, viewport }, updatedAt: new Date() }) .where(eq(projects.id, projectId)); // Profile table structure // - id: User ID from Supabase Auth // - customerId: Stripe customer ID // - subscriptionId: Stripe subscription ID // - productId: Stripe product ID (hobby/pro) // - onboardedAt: First login timestamp ``` -------------------------------- ### Image Generation Action - AI Image Creation (TypeScript) Source: https://context7.com/haydenbleasel/tersa/llms.txt A server action that generates images from text prompts using various AI models. It automatically uploads the generated images to Supabase storage, creates an AI-generated description, and updates project node data. Credit usage is tracked based on image size. ```typescript // Server action: generateImageAction // Generate images with automatic storage and description import { generateImageAction } from '@/app/actions/image/create'; const result = await generateImageAction({ modelId: 'gpt-image-1', prompt: 'A futuristic cityscape at sunset with flying cars', instructions: 'Make it vibrant with warm colors', size: '1024x1024', nodeId: 'node_123', projectId: 'project_456' }); if ('error' in result) { console.error(result.error); } else { // result.nodeData contains: // - generated.url: Public URL to the image // - generated.type: Image MIME type // - description: AI-generated description of the image // - updatedAt: Timestamp of generation } // Supports multiple image models (gpt-image-1, black-forest-labs models) // Automatically uploads to Supabase storage // Generates description using vision model // Tracks credit usage based on image size // Updates project node with generated image data ``` -------------------------------- ### TypeScript: Upstash Redis Rate Limiter with Sliding Window Source: https://context7.com/haydenbleasel/tersa/llms.txt Implements API rate limiting using Upstash Redis and the sliding window algorithm. It configures limits per minute and applies them to API routes, with different limits for chat and code endpoints. The limiter bypasses in development mode and includes headers for limit, remaining, and reset times in responses. ```typescript // Rate limiting configuration import { createRateLimiter, slidingWindow } from '@/lib/rate-limit'; const rateLimiter = createRateLimiter({ limiter: slidingWindow(10, '1 m'), // 10 requests per minute prefix: 'api-chat' }); // Apply in API route (production only) if (process.env.NODE_ENV === 'production') { const ip = req.headers.get('x-forwarded-for') || 'anonymous'; const { success, limit, reset, remaining } = await rateLimiter.limit(ip); if (!success) { return new Response('Too many requests', { status: 429, headers: { 'X-RateLimit-Limit': limit.toString(), 'X-RateLimit-Remaining': remaining.toString(), 'X-RateLimit-Reset': reset.toString(), }, }); } } // Rate limits per endpoint: // - /api/chat: 10 requests/minute // - /api/code: 10 requests/minute // All limits bypass in development mode ``` -------------------------------- ### Chat API - AI Text Generation Source: https://context7.com/haydenbleasel/tersa/llms.txt This endpoint provides streaming text generation for AI language models, supporting reasoning extraction and credit tracking. It requires an authenticated and subscribed user. ```APIDOC ## POST /api/chat ### Description Streaming text generation endpoint that supports multiple AI language models with built-in reasoning extraction and credit tracking. ### Method POST ### Endpoint /api/chat ### Parameters #### Request Body - **modelId** (string) - Required - The ID of the AI model to use (e.g., 'gpt-4o'). - **messages** (array) - Required - An array of message objects, where each object has a `role` (string, e.g., 'user') and `content` (string). ### Request Example ```json { "modelId": "gpt-4o", "messages": [ { "role": "user", "content": "Analyze the sentiment of customer reviews and provide insights." } ] } ``` ### Response #### Success Response (200) - The response is a streaming UI message response. #### Response Example (Streaming UI message response with reasoning and sources) ``` -------------------------------- ### Manage User Authentication and Subscriptions (TypeScript) Source: https://context7.com/haydenbleasel/tersa/llms.txt Core authentication utilities for accessing current user data and enforcing subscription requirements. Functions like `currentUser`, `currentUserProfile`, and `getSubscribedUser` handle user retrieval, profile creation/updates, and subscription status checks. These are crucial for protected API routes and actions, providing specific error messages for unauthorized access or insufficient credits. ```typescript // lib/auth.ts - Authentication utilities import { currentUser, currentUserProfile, getSubscribedUser } from '@/lib/auth'; // Get current authenticated user const user = await currentUser(); // Returns: Supabase User object or null // Get or create user profile const profile = await currentUserProfile(); // Returns: { id, customerId, subscriptionId, productId, onboardedAt } // Automatically creates profile if doesn't exist // Verify user has active subscription with credits try { const user = await getSubscribedUser(); // User has active subscription and credits // Use in protected API routes and actions } catch (error) { // Error messages: // - "Create an account to use AI features." // - "Claim your free AI credits to use this feature." // - "Sorry, you have no credits remaining! Please upgrade for more credits." } // Usage in API route: export const POST = async (req: Request) => { try { await getSubscribedUser(); // Proceed with API logic } catch (error) { return new Response(parseError(error), { status: 401 }); } }; ``` -------------------------------- ### TypeScript: Next.js Middleware for Supabase Session Management Source: https://context7.com/haydenbleasel/tersa/llms.txt This Next.js middleware is designed to automatically update Supabase authentication sessions on every request. It uses a `matcher` pattern to define which routes it should apply to, excluding static files, media, favicons, and webhook endpoints. Session refresh logic is handled by `@/lib/supabase/middleware`. ```typescript // middleware.ts - Authentication middleware // Automatically updates Supabase session on every request // Runs on all routes except: // - Static files (_next/static, _next/image) // - Media files (svg, png, jpg, jpeg, gif, webp, mp3, mp4) // - Favicon // - Webhook endpoints (/api/webhooks/) // Implemented via matcher pattern export const config = { matcher: [ '/((?!_next/static|_next/image|favicon.ico|api/webhooks/|.*\.(?:svg|png|jpg|jpeg|gif|webp|mp3|mp4)$).*)', ], }; // Session refresh handled by @/lib/supabase/middleware // Maintains authentication state across page navigation // Supports server-side auth checks in API routes ``` -------------------------------- ### Chat API - AI Text Generation with Reasoning (TypeScript) Source: https://context7.com/haydenbleasel/tersa/llms.txt A streaming text generation endpoint that interacts with AI language models. It supports multiple models, extracts reasoning, and tracks credit usage. Requires an authenticated and subscribed user and has a rate limit of 10 requests per minute in production. ```typescript // POST /api/chat // Send messages to AI models with reasoning support const response = await fetch('/api/chat', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ modelId: 'gpt-4o', messages: [ { role: 'user', content: 'Analyze the sentiment of customer reviews and provide insights.' } ] }) }); // The response is a streaming UI message response // Rate limit: 10 requests per minute in production // Requires authenticated and subscribed user // Tracks credit usage based on input/output tokens // Response format: UIMessageStream with reasoning and sources ``` -------------------------------- ### Code Generation API - AI Code Writing (TypeScript) Source: https://context7.com/haydenbleasel/tersa/llms.txt A specialized API endpoint for generating code in specific programming languages with streaming responses. It enforces language specification via a system prompt and tracks credits based on token usage. Rate limited to 10 requests per minute in production. ```typescript // POST /api/code // Generate code with specified language const response = await fetch('/api/code', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ modelId: 'gpt-4o', language: 'python', messages: [ { role: 'user', content: 'Create a function that calculates fibonacci numbers with memoization' } ] }) }); // Returns raw code without markdown formatting // Rate limit: 10 requests per minute in production // System prompt enforces language specification // Credits tracked per token usage ``` -------------------------------- ### Code Generation API - AI Code Writing Source: https://context7.com/haydenbleasel/tersa/llms.txt A specialized endpoint for generating code in specific programming languages with streaming responses. It enforces language specification via system prompt and tracks credit usage. ```APIDOC ## POST /api/code ### Description Specialized endpoint for generating code in specific programming languages with streaming responses. ### Method POST ### Endpoint /api/code ### Parameters #### Request Body - **modelId** (string) - Required - The ID of the AI model to use (e.g., 'gpt-4o'). - **language** (string) - Required - The programming language for code generation (e.g., 'python'). - **messages** (array) - Required - An array of message objects, where each object has a `role` (string, e.g., 'user') and `content` (string). ### Request Example ```json { "modelId": "gpt-4o", "language": "python", "messages": [ { "role": "user", "content": "Create a function that calculates fibonacci numbers with memoization" } ] } ``` ### Response #### Success Response (200) - Returns raw code without markdown formatting. #### Response Example ```python def fibonacci(n, memo={}): if n in memo: return memo[n] if n <= 1: return n memo[n] = fibonacci(n-1, memo) + fibonacci(n-2, memo) return memo[n] ``` ``` -------------------------------- ### TypeScript: Unified Error Parsing Utility Source: https://context7.com/haydenbleasel/tersa/llms.txt Provides a utility function `parseError` for standardized error handling across the application. It can parse various error types, including standard `Error` objects, string errors, and unknown types, returning a consistent error message format. This function is intended for use within `try-catch` blocks, including in server actions. ```typescript // lib/error/parse.ts - Error parsing utility import { parseError } from '@/lib/error/parse'; // Use in try-catch blocks try { await riskyOperation(); } catch (error) { const message = parseError(error); return { error: message }; } // Handles various error types: // - Error objects with message property // - String errors // - Unknown error types (returns generic message) // Consistent error responses across actions and API routes // Example in server action export const myAction = async (): Promise< { data: object } | { error: string } > => { try { // Action logic return { data: result }; } catch (error) { const message = parseError(error); return { error: message }; } }; ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.