diff --git a/.cursor/rules/development-workflow.mdc b/.cursor/rules/development-workflow.mdc new file mode 100644 index 00000000..6be9e656 --- /dev/null +++ b/.cursor/rules/development-workflow.mdc @@ -0,0 +1,128 @@ +--- +description: +globs: +alwaysApply: false +--- +# Development Workflow and Optimization Tools + +## Package Management +ZapDev uses **Bun** as the primary package manager. Always use `bun` commands instead of npm/yarn for consistency and performance. + +## Available Scripts + +### Core Development +```bash +bun run dev # Start development server +bun run build # Production build +bun run start # Start production server +bun run lint # ESLint checking +``` + +### Performance and Analysis +```bash +bun run perf-check # Comprehensive performance analysis +bun run bundle-analyze # Visual bundle size analysis +bun run lighthouse # Lighthouse performance audit +bun run analyze # Build with bundle analyzer +``` + +### Optimization Scripts +```bash +bun run optimize-bundle # Bundle optimization +bun run remove-console # Remove console logs +bun run fix-chat # Chat API fixes +bun run fix-db # Database fixes +``` + +## Performance Analysis Workflow + +### 1. Regular Performance Checks +- **Tool**: [scripts/performance-check.js](mdc:scripts/performance-check.js) +- **Usage**: `bun run perf-check` +- **Analyzes**: Bundle size, CSS performance, image optimization, dependencies + +### 2. Bundle Analysis +- **Configuration**: [next.config.mjs](mdc:next.config.mjs) with `ANALYZE=true` +- **Visual Analysis**: Opens webpack-bundle-analyzer in browser +- **Identifies**: Large dependencies, unused code, optimization opportunities + +### 3. Lighthouse Auditing +- **Command**: `bun run lighthouse` +- **Output**: HTML report in project root +- **Metrics**: Performance, Accessibility, Best Practices, SEO + +## Code Quality Standards + +### TypeScript Configuration +- **Strict Mode**: Enabled for type safety +- **Build Errors**: Ignored during development for faster iteration +- **ESLint**: Ignored during builds for deployment speed + +### Performance Optimization Guidelines +1. **Always check device capabilities** using [components/performance-optimizer.tsx](mdc:components/performance-optimizer.tsx) +2. **Use fallback systems** for critical features like WebContainer +3. **Implement loading states** with [components/loading-fallback.tsx](mdc:components/loading-fallback.tsx) +4. **Optimize for mobile first** with responsive performance classes + +## Build Optimization + +### Next.js Configuration +- **CSS Optimization**: `optimizeCss: true` +- **Package Imports**: Tree-shaking for framer-motion, lucide-react +- **Console Removal**: Automatic in production builds +- **Image Optimization**: Modern formats (AVIF, WebP) + +### Bundle Size Targets +- **Total Bundle**: < 5MB for optimal performance +- **Initial Load**: < 1MB for fast first paint +- **Individual Chunks**: < 500KB for efficient loading + +## Development Environment Setup + +### Required Dependencies +```json +{ + "runtime": "bun", + "node_version": ">=18.0.0", + "package_manager": "bun" +} +``` + +### Environment Variables +- **NEXT_PUBLIC_APP_URL**: Production domain for metadata +- **ANALYZE**: Set to "true" for bundle analysis +- **NODE_ENV**: Development/production environment + +## Testing Strategy + +### Performance Testing +1. **Build Analysis**: `bun run build && bun run perf-check` +2. **Lighthouse**: `bun run lighthouse` after starting dev server +3. **Bundle Size**: `bun run bundle-analyze` for visual analysis + +### Device Testing +- **Low-end Device Simulation**: DevTools performance throttling +- **Network Simulation**: Slow 3G/2G connection testing +- **Reduced Motion**: Test with accessibility preferences + +## Deployment Checklist + +1. ✅ **Performance Check**: `bun run perf-check` passes +2. ✅ **Bundle Analysis**: No unexpected large dependencies +3. ✅ **Lighthouse Score**: >90 for Performance, Accessibility, SEO +4. ✅ **TypeScript**: No build-breaking errors +5. ✅ **Environment Variables**: All production vars configured +6. ✅ **SEO Metadata**: Sitemap and robots.txt generated +7. ✅ **Fallback Systems**: WebContainer fallbacks tested + +## Monitoring and Maintenance + +### Regular Tasks +- **Weekly**: Run performance checks +- **Monthly**: Update dependencies and analyze bundle changes +- **Quarterly**: Comprehensive Lighthouse audit and optimization review + +### Performance Regression Prevention +- **Bundle Size Monitoring**: Alert if total size exceeds 5MB +- **Core Web Vitals**: Monitor LCP, FID, CLS metrics +- **Error Tracking**: Monitor WebContainer fallback usage rates diff --git a/.cursor/rules/performance-optimization.mdc b/.cursor/rules/performance-optimization.mdc new file mode 100644 index 00000000..4a5836f7 --- /dev/null +++ b/.cursor/rules/performance-optimization.mdc @@ -0,0 +1,73 @@ +--- +description: perfomance +globs: +alwaysApply: false +--- +# Performance Optimization Guidelines + +## Core Performance Principles + +ZapDev is optimized for **all device types**, from high-end computers to low-end mobile devices. Every feature must work reliably across different performance capabilities. + +## Performance Monitoring Components + +### PerformanceOptimizer Component +- **Location**: [components/performance-optimizer.tsx](mdc:components/performance-optimizer.tsx) +- **Purpose**: Automatically detects device capabilities and applies appropriate optimizations +- **Key Features**: + - Device capability detection (CPU cores, RAM, connection speed) + - Automatic animation disabling on low-end devices + - Reduced motion support for accessibility + - Performance monitoring with Largest Contentful Paint tracking + +### CSS Performance Standards +- **Location**: [app/globals.css](mdc:app/globals.css) +- **Optimizations Applied**: + - Infinite animations converted to user-triggered only + - Backdrop-filter reduced from 10px to 4px blur + - Glass effects optimized for mobile (2px blur on small screens) + - Performance utilities added (.reduce-motion, .performance-mode) + - Responsive performance classes for low-end devices + +## Bundle Optimization + +### Next.js Configuration +- **Location**: [next.config.mjs](mdc:next.config.mjs) +- **Key Settings**: + - `optimizeCss: true` - CSS optimization enabled + - `optimizePackageImports` - Tree-shaking for framer-motion and lucide-react + - `removeConsole: true` in production + - Modern image formats (AVIF, WebP) configured + +### Performance Monitoring Scripts +- **Performance Check**: [scripts/performance-check.js](mdc:scripts/performance-check.js) +- **Usage**: `bun run perf-check` +- **Analyzes**: Bundle size, CSS optimization, image optimization, dependencies + +## Performance Best Practices + +1. **Always use performance-optimized components** from [components/loading-fallback.tsx](mdc:components/loading-fallback.tsx) +2. **Implement lazy loading** for images and heavy components +3. **Use reduced motion classes** for accessibility +4. **Test on low-end devices** using performance mode +5. **Monitor bundle size** regularly with `bun run bundle-analyze` + +## Device Capability Detection + +```typescript +// Example from PerformanceOptimizer +const isLowEndDevice = () => { + const hardwareConcurrency = navigator.hardwareConcurrency || 1; + const deviceMemory = (navigator as any).deviceMemory || 1; + const connection = (navigator as any).connection; + + return hardwareConcurrency < 4 || deviceMemory < 4 || isSlowConnection; +}; +``` + +## Animation Guidelines + +- **Infinite animations**: Only on user interaction (hover/focus) +- **Heavy effects**: Disabled automatically on low-end devices +- **Glass effects**: Reduced blur on mobile devices +- **Transitions**: Maximum 0.3s duration for responsiveness diff --git a/.cursor/rules/seo-and-accessibility.mdc b/.cursor/rules/seo-and-accessibility.mdc new file mode 100644 index 00000000..bc2b42cc --- /dev/null +++ b/.cursor/rules/seo-and-accessibility.mdc @@ -0,0 +1,143 @@ +--- +description: SEO +globs: +alwaysApply: false +--- +# SEO and Accessibility Standards + +## SEO Architecture + +ZapDev implements comprehensive SEO optimization for maximum search engine visibility and user accessibility. + +## Metadata Implementation +- **Layout**: [app/layout.tsx](mdc:app/layout.tsx) +- **Structured Data**: JSON-LD schema for SoftwareApplication +- **Open Graph**: Complete social media optimization +- **Twitter Cards**: Summary with large image support + +## Site Structure + +### Sitemap Generation +- **Dynamic Sitemap**: [app/sitemap.ts](mdc:app/sitemap.ts) +- **Auto-generated**: Updates with page changes +- **Priority System**: Homepage (1.0) → Chat (0.9) → Pricing (0.8) + +### Robots.txt +- **Location**: [public/robots.txt](mdc:public/robots.txt) +- **Crawl Control**: Allows main sections, blocks sensitive areas +- **Sitemap Reference**: Points to dynamic sitemap.xml + +### Web Manifest +- **PWA Support**: [public/site.webmanifest](mdc:public/site.webmanifest) +- **App-like Experience**: Standalone display mode +- **Shortcuts**: Quick access to Chat and Pricing +- **Icons**: Multiple sizes for all devices + +## Technical SEO Features + +### Performance Optimization +```typescript +// From layout.tsx +export const metadata: Metadata = { + title: { + default: 'ZapDev - Build Amazing Apps with AI', + template: '%s | ZapDev', // SEO-friendly title templates + }, + robots: { + index: true, + follow: true, + googleBot: { + 'max-image-preview': 'large', + 'max-snippet': -1, + }, + }, +}; +``` + +### Resource Preloading +- **DNS Prefetch**: API endpoints (api.groq.com, openrouter.ai) +- **Preconnect**: Google Fonts +- **Critical Resources**: Favicon and core assets + +## Accessibility Standards + +### Color and Contrast +- **WCAG AA Compliance**: Minimum 4.5:1 contrast ratio +- **Theme Support**: Both dark and light themes accessible +- **Color Independence**: No information conveyed by color alone + +### Motion and Animation +- **Reduced Motion**: Respects `prefers-reduced-motion: reduce` +- **Performance Mode**: Auto-disables animations on low-end devices +- **User Control**: Animations can be disabled via system preferences + +### Keyboard Navigation +- **Focus Management**: Clear focus indicators on all interactive elements +- **Tab Order**: Logical navigation sequence +- **Focus Rings**: Visible 2px outline using brand colors + +### Screen Reader Support +- **Semantic HTML**: Proper heading hierarchy (h1 → h2 → h3) +- **ARIA Labels**: Descriptive labels for complex interactions +- **Role Attributes**: `role="main"` for primary content areas +- **Alt Text**: Descriptive alternative text for all images + +## Content Strategy + +### Structured Data Schema +```json +{ + "@context": "https://schema.org", + "@type": "SoftwareApplication", + "name": "ZapDev", + "applicationCategory": "DeveloperApplication", + "offers": { + "@type": "Offer", + "price": "0", + "priceCurrency": "USD" + } +} +``` + +### Meta Tags Best Practices +- **Description Length**: 150-160 characters for optimal display +- **Keyword Strategy**: Focus on AI development, no-code, app building +- **Local SEO**: Not applicable (global SaaS product) + +## Performance SEO + +### Core Web Vitals Optimization +- **LCP Target**: < 2.5 seconds +- **FID Target**: < 100 milliseconds +- **CLS Target**: < 0.1 +- **Performance Monitoring**: Built into [components/performance-optimizer.tsx](mdc:components/performance-optimizer.tsx) + +### Image Optimization +- **Modern Formats**: WebP and AVIF support configured +- **Lazy Loading**: `loading="lazy"` for non-critical images +- **Responsive Images**: Multiple sizes for different devices +- **Alt Text**: Required for all images + +## Monitoring and Testing + +### Available Tools +```bash +# Performance analysis +bun run perf-check + +# Lighthouse audit +bun run lighthouse + +# Bundle analysis +bun run bundle-analyze +``` + +### SEO Checklist +- ✅ Structured data implemented +- ✅ Meta tags optimized +- ✅ Sitemap.xml auto-generated +- ✅ Robots.txt configured +- ✅ PWA manifest included +- ✅ Performance optimized +- ✅ Accessibility compliance +- ✅ Mobile-first responsive design diff --git a/.cursor/rules/theme-and-styling.mdc b/.cursor/rules/theme-and-styling.mdc new file mode 100644 index 00000000..b19a2107 --- /dev/null +++ b/.cursor/rules/theme-and-styling.mdc @@ -0,0 +1,121 @@ +--- +description: Fixing UI/UX things +globs: +alwaysApply: false +--- +# Theme Management and Styling Guidelines + +## Anti-Flashing Theme System + +ZapDev implements a **zero-flash theme system** that prevents color flickering during page loads and theme switches. + +## Critical CSS Implementation +- **Location**: [app/layout.tsx](mdc:app/layout.tsx) +- **Inline Critical CSS**: Prevents FOUC (Flash of Unstyled Content) +- **Theme Preloading**: Script in layout sets theme before hydration +- **Loading States**: `.theme-loading` and `.theme-loaded` classes + +## Global Styles Architecture +- **Primary Styles**: [app/globals.css](mdc:app/globals.css) +- **Duplicate Removal**: No conflicting CSS files (styles/globals.css removed) +- **Theme Variables**: CSS custom properties for both dark and light themes + +## Theme Structure + +### Dark Theme (Default) +```css +:root { + --background: 222.2 84% 4.9%; + --foreground: 210 40% 98%; + --charcoal: #0d0d10; + --off-white: #eaeaea; + --deep-violet: #6c52a0; + --warm-pink: #a0527c; +} +``` + +### Light Theme Support +```css +.light { + --background: 0 0% 100%; + --foreground: 0 0% 3.9%; + --charcoal: #ffffff; + --off-white: #0a0a0a; + --deep-violet: #8b5fd6; + --warm-pink: #d65f8b; +} +``` + +## Performance-Optimized Styling + +### Animation Guidelines +- **Infinite animations**: Removed or converted to user-triggered +- **Heavy effects**: Reduced backdrop-filter blur (10px → 4px) +- **Mobile optimization**: Further reduced effects (4px → 2px) +- **Accessibility**: Respects `prefers-reduced-motion` + +### Responsive Performance Classes +```css +.reduce-motion * { + animation-duration: 0.01ms !important; + transition-duration: 0.01ms !important; +} + +.performance-mode * { + transition: none !important; + animation: none !important; +} +``` + +## Theme Switching Best Practices + +1. **Never use hard-coded colors** in components +2. **Always use CSS custom properties** for theme-aware styling +3. **Test both themes** for accessibility and contrast +4. **Use transition timing** of 0.15s for smooth changes +5. **Implement loading states** to prevent flashing + +## Component Styling Standards + +### Loading Components +- **Location**: [components/loading-fallback.tsx](mdc:components/loading-fallback.tsx) +- **Consistent styling** across all loading states +- **Theme-aware colors** using custom properties +- **Performance-optimized animations** + +### Glass Effects +```css +.glass-effect { + backdrop-filter: blur(4px); /* Reduced for performance */ + background: rgba(255, 255, 255, 0.03); +} + +/* Mobile optimization */ +@media (max-width: 768px) { + .glass-effect { + backdrop-filter: blur(2px); + } +} +``` + +## Color Utility Classes + +### Brand Colors +- `.bg-charcoal` - Primary background +- `.text-off-white` - Primary text +- `.bg-deep-violet` - Brand purple +- `.text-deep-violet` - Brand purple text +- `.primary-gradient` - Violet to pink gradient +- `.cta-gradient` - Pink to violet gradient + +### State Classes +- `.loading-skeleton` - Shimmer loading effect +- `.error-state` - Error styling +- `.success-state` - Success styling + +## Accessibility Requirements + +- **Color contrast**: Minimum 4.5:1 ratio for text +- **Reduced motion**: Honor user preferences +- **Focus indicators**: Clear, visible focus rings +- **Theme persistence**: Stored in localStorage as `zapdev-theme` diff --git a/.cursor/rules/webcontainer-architecture.mdc b/.cursor/rules/webcontainer-architecture.mdc new file mode 100644 index 00000000..10614c40 --- /dev/null +++ b/.cursor/rules/webcontainer-architecture.mdc @@ -0,0 +1,83 @@ +--- +description: Webcontainer fix +globs: +alwaysApply: false +--- +# WebContainer Fallback Architecture + +## Three-Tier Execution System + +ZapDev implements a robust fallback system ensuring **100% compatibility** across all environments. + +## Tier 1: WebContainer (Primary) +- **Component**: [components/enhanced-webcontainer.tsx](mdc:components/enhanced-webcontainer.tsx) +- **Manager**: [lib/services/webcontainer/webcontainer-manager.ts](mdc:lib/services/webcontainer/webcontainer-manager.ts) +- **Best Experience**: Full functionality, real-time preview, interactive features +- **Requirements**: Modern browser with service worker support + +## Tier 2: Server Fallback (Secondary) +- **Service**: [lib/services/webcontainer/server-fallback.ts](mdc:lib/services/webcontainer/server-fallback.ts) +- **API Endpoint**: [app/api/webcontainer/fallback/route.ts](mdc:app/api/webcontainer/fallback/route.ts) +- **Features**: Server-side rendering, safe HTML generation, framework conversion +- **Compatibility**: Works on any device with internet connection + +## Tier 3: Static Preview (Tertiary) +- **Purpose**: Offline compatibility, maximum device support +- **Method**: Client-side HTML generation with blob URLs +- **Security**: Code sanitization, script removal +- **Use Case**: Very low-end devices, offline mode, emergency fallback + +## Implementation Pattern + +```typescript +// Always use this pattern for WebContainer features +const result = await fallbackServiceRef.current.executeWithFallback( + primaryWebContainerAction, + code, + projectType +); + +if (result.success) { + setExecutionMethod(result.method); // 'webcontainer' | 'server' | 'static' + // Handle based on execution method +} +``` + +## Framework Support + +### HTML Projects +- Direct execution in all tiers +- Static file serving via npm serve + +### React Projects +- **Tier 1**: Full Vite development server +- **Tier 2**: JSX to HTML conversion +- **Tier 3**: Static component preview + +### Vue/Svelte Projects +- **Tier 1**: Framework-specific dev servers +- **Tier 2**: Template extraction and conversion +- **Tier 3**: Static template preview + +## Error Handling + +All WebContainer operations must include: +1. **Timeout handling** (30-second default) +2. **Graceful degradation** to next tier +3. **User feedback** about execution method +4. **Retry mechanisms** with exponential backoff + +## Performance Indicators + +The system automatically displays: +- **Execution method**: WebContainer/Server/Static icons +- **Connection status**: Online/Offline indicators +- **Performance mode**: Low-end device detection +- **Loading states**: Method-specific loading messages + +## Security Considerations + +- **Code sanitization** in server and static tiers +- **Script removal** for security in fallback modes +- **Safe HTML generation** with XSS prevention +- **Sandboxed iframe execution** for WebContainer diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 00000000..6b10a5b7 --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,6 @@ +{ + "extends": [ + "next/core-web-vitals", + "next/typescript" + ] +} diff --git a/app/api/ai-team/build/route.ts b/app/api/ai-team/build/route.ts index fe4dcd10..a0f05041 100644 --- a/app/api/ai-team/build/route.ts +++ b/app/api/ai-team/build/route.ts @@ -9,17 +9,7 @@ interface BuildRequest { chatId?: string } -interface BuildResponse { - success: boolean - chatId?: string - analysis: any - architecture: any - frontend: any - backend: any - deployment: any - projectFiles: Record - teamLog: string[] -} +// Build response is inlined where used for simplicity export async function POST(req: NextRequest) { try { @@ -108,7 +98,7 @@ async function analyzeRequirements(userRequest: string) { return JSON.parse(text) } -async function architectureDesign(userRequest: string, analysis: any) { +async function architectureDesign(userRequest: string, analysis: object) { const { text } = await generateText({ model: openrouterProvider.chat(getModelId('deepseek/deepseek-r1-0528-qwen3-8b:free')), prompt: ` @@ -131,7 +121,7 @@ async function architectureDesign(userRequest: string, analysis: any) { return JSON.parse(text) } -async function frontendDevelopment(userRequest: string, architecture: any) { +async function frontendDevelopment(userRequest: string, architecture: object) { const { text } = await generateText({ model: openrouterProvider.chat(getModelId('agentica-org/deepcoder-14b-preview:free')), prompt: ` @@ -159,8 +149,8 @@ async function frontendDevelopment(userRequest: string, architecture: any) { return JSON.parse(text) } -async function backendDevelopment(userRequest: string, architecture: any) { - const needsBackend = architecture.hasBackend || userRequest.toLowerCase().includes('api') +async function backendDevelopment(userRequest: string, architecture: Record) { + const needsBackend = (architecture as { hasBackend?: boolean }).hasBackend || userRequest.toLowerCase().includes('api') if (!needsBackend) { return { needsBackend: false } @@ -187,14 +177,14 @@ async function backendDevelopment(userRequest: string, architecture: any) { return JSON.parse(text) } -async function deploymentSetup(userRequest: string, architecture: any, frontend: any, backend: any) { +async function deploymentSetup(userRequest: string, architecture: Record, frontend: Record, backend: Record) { const { text } = await generateText({ model: openrouterProvider.chat(getModelId('microsoft/phi-4-reasoning-plus:free')), prompt: ` - Create deployment configuration for a ${architecture.projectType} project. + Create deployment configuration for a ${(architecture as { projectType?: string }).projectType || 'web'} project. Frontend: ${Object.keys(frontend).join(', ')} - Backend: ${backend.needsBackend ? 'Express server' : 'Static only'} + Backend: ${(backend as { needsBackend?: boolean }).needsBackend ? 'Express server' : 'Static only'} Respond in JSON format: { @@ -213,13 +203,13 @@ async function deploymentSetup(userRequest: string, architecture: any, frontend: return JSON.parse(text) } -async function generateProjectFiles(analysis: any, architecture: any, frontend: any, backend: any, deployment: any) { +async function generateProjectFiles(analysis: Record, architecture: Record, frontend: Record, backend: Record, deployment: Record) { const files: Record = {} // Package.json files['package.json'] = { file: { - contents: JSON.stringify(deployment.packageJson, null, 2) + contents: JSON.stringify((deployment as { packageJson?: object }).packageJson || {}, null, 2) } } @@ -235,27 +225,29 @@ async function generateProjectFiles(analysis: any, architecture: any, frontend: }) // Backend files (if needed) - if (backend.needsBackend && backend['server.js']) { + const backendTyped = backend as { needsBackend?: boolean; 'server.js'?: string }; + if (backendTyped.needsBackend && backendTyped['server.js']) { files['server.js'] = { file: { - contents: backend['server.js'] as string + contents: backendTyped['server.js'] } } } // Configuration files - if (deployment.viteConfig) { + const deploymentTyped = deployment as { viteConfig?: string; tsConfig?: string }; + if (deploymentTyped.viteConfig) { files['vite.config.ts'] = { file: { - contents: deployment.viteConfig + contents: deploymentTyped.viteConfig } } } - if (deployment.tsConfig) { + if (deploymentTyped.tsConfig) { files['tsconfig.json'] = { file: { - contents: deployment.tsConfig + contents: deploymentTyped.tsConfig } } } diff --git a/app/api/ai-team/coordinate/route.ts b/app/api/ai-team/coordinate/route.ts index 14a17202..79f719d8 100644 --- a/app/api/ai-team/coordinate/route.ts +++ b/app/api/ai-team/coordinate/route.ts @@ -4,7 +4,7 @@ import { getGroqInstance } from '@/lib/groq-provider'; import { errorLogger, ErrorCategory } from '@/lib/error-logger'; // Helper to safely parse JSON from AI responses -function safeJsonParse(text: string, defaultValue: any) { +function safeJsonParse(text: string, defaultValue: Record) { try { // Extract JSON from potential markdown code blocks const jsonMatch = text.match(/```json\s*([\s\S]*?)\s*```/); diff --git a/app/api/chat/create/route.ts b/app/api/chat/create/route.ts index 749c7848..63a6ebfe 100644 --- a/app/api/chat/create/route.ts +++ b/app/api/chat/create/route.ts @@ -5,7 +5,7 @@ import { errorLogger, ErrorCategory } from '@/lib/error-logger'; export async function POST(request: NextRequest) { try { - const supabase = await createClient(); + await createClient(); // Initialize supabase client const user = await requireAuth(); if (!user) { diff --git a/app/api/chat/messages/route.ts b/app/api/chat/messages/route.ts index 03ff468c..cff7478b 100644 --- a/app/api/chat/messages/route.ts +++ b/app/api/chat/messages/route.ts @@ -5,7 +5,7 @@ import { errorLogger, ErrorCategory } from '@/lib/error-logger'; export async function GET(request: NextRequest) { try { - const supabase = await createClient(); + await createClient(); // Initialize supabase client // Check if user is authenticated const user = await requireAuth(); diff --git a/app/api/chat/route.ts b/app/api/chat/route.ts index e8aae0df..e8b23685 100644 --- a/app/api/chat/route.ts +++ b/app/api/chat/route.ts @@ -1,203 +1,96 @@ -import { NextRequest, NextResponse } from 'next/server'; -import { requireAuth, createChat, addMessage } from '@/lib/supabase-operations'; -import { streamText } from 'ai'; -import { groqProvider } from '@/lib/groq-provider'; -import { withRateLimit, RateLimitConfigs } from '@/lib/rate-limiter'; -import { withValidation, ApiSchemas, z } from '@/lib/api-validation'; +import { groqProvider } from '@/lib/groq/provider'; +import { streamObject } from 'ai'; +import { z } from 'zod'; +import { buildSystemMessage } from '@/lib/groq/responses'; import { errorLogger, ErrorCategory } from '@/lib/error-logger'; -import { getUserFriendlyError } from '@/lib/user-friendly-errors'; - -// Custom validation schema for chat endpoint -const chatRequestSchema = z.object({ - messages: z - .array( - z.object({ - role: z.enum(['user', 'assistant', 'system']), - content: z.string().min(1).max(10000), - }) - ) - .min(1), - chatId: z.string().optional(), - modelId: z - .enum(['llama-3.3-70b-versatile', 'llama-3.1-8b-instant', 'mixtral-8x7b-32768']) - .optional(), - useReasoning: z.boolean().optional(), - reasoningFormat: z.string().optional(), +import { createUserFriendlyError } from '@/lib/user-friendly-errors'; +import { chatApiLimiter, checkRateLimit } from '@/lib/rate-limiter'; +import { validateChatInput } from '@/lib/api-validation'; + +export const runtime = 'edge'; + +const responseSchema = z.object({ + steps: z.array( + z.object({ + type: z.enum(['thought', 'code', 'solution', 'error']), + content: z.string(), + }) + ), }); -async function handleChatRequest( - request: NextRequest, - context: { body?: z.infer } -) { - if (!context.body) { - return NextResponse.json({ error: 'Request body is required' }, { status: 400 }); - } - const startTime = Date.now(); - +export async function POST(_request: Request) { try { - // Authentication with better error handling - let user; - try { - user = await requireAuth(); - } catch (authError) { - errorLogger.logAuthError('chat_api_auth', authError); - const friendlyError = getUserFriendlyError(authError, 'auth/session-expired'); - - return NextResponse.json( + // Apply rate limiting + const rateLimitResult = await checkRateLimit(chatApiLimiter); + if (!rateLimitResult.success) { + const error = createUserFriendlyError( + new Error('Too many requests'), + ErrorCategory.RATE_LIMIT, { - error: friendlyError.title, - message: friendlyError.message, - suggestion: friendlyError.suggestion, - }, - { status: 401 } + retryAfter: rateLimitResult.reset + } ); + return new Response(JSON.stringify({ error: error.userMessage }), { + status: 429, + headers: { + 'Content-Type': 'application/json', + 'Retry-After': String(rateLimitResult.reset) + } + }); } - const { messages, chatId, modelId, useReasoning, reasoningFormat } = context.body; - let finalChatId = chatId; - - // 1. Ensure a chat exists - if (!finalChatId || finalChatId === 'new') { - try { - const firstMessage = messages[0]?.content || 'New Chat'; - const newChat = await createChat(user.id, firstMessage.substring(0, 50)); - finalChatId = newChat.id; - - errorLogger.info(ErrorCategory.API, 'New chat created', { - userId: user.id, - chatId: finalChatId, - }); - } catch (dbError) { - errorLogger.error(ErrorCategory.DATABASE, 'Failed to create chat', dbError, { - userId: user.id, - }); - - const friendlyError = getUserFriendlyError(dbError, 'db/save-failed'); - return NextResponse.json( - { - error: friendlyError.title, - message: friendlyError.message, - suggestion: friendlyError.suggestion, - }, - { status: 500 } - ); - } - } - - // 2. Save the user's message - const userMessage = messages[messages.length - 1]; - if (userMessage?.role === 'user') { - try { - await addMessage(finalChatId, 'user', userMessage.content); - } catch (dbError) { - errorLogger.warning(ErrorCategory.DATABASE, 'Failed to save user message', dbError, { - userId: user.id, - chatId: finalChatId, - }); - // Continue processing even if save fails - } + // Validate input + const validationResult = await validateChatInput(_request); + if (!validationResult.success) { + return new Response(JSON.stringify({ error: validationResult.error }), { + status: 400, + headers: { 'Content-Type': 'application/json' } + }); } - // 3. Generate AI response using Groq with proper Vercel AI SDK - try { - const model = groqProvider.chat(modelId || 'llama-3.3-70b-versatile'); - - const result = await streamText({ - model: model as any, - messages, - temperature: 0.7, - maxTokens: 2048, - onFinish: async ({ text, usage, finishReason }) => { - // Log AI completion metrics - const duration = Date.now() - startTime; - errorLogger.info(ErrorCategory.AI_MODEL, 'AI completion successful', { - userId: user.id, - chatId: finalChatId, - model: modelId || 'llama-3.3-70b-versatile', - promptTokens: usage?.promptTokens, - completionTokens: usage?.completionTokens, - totalTokens: usage?.totalTokens, - finishReason, - duration, - }); + const { messages, modelId = 'gemma-7b-it' } = validationResult.data; - // Save the AI response after streaming completes - if (finalChatId && text) { - try { - await addMessage(finalChatId, 'assistant', text); - } catch (saveError) { - errorLogger.warning(ErrorCategory.DATABASE, 'Failed to save AI message', saveError, { - userId: user.id, - chatId: finalChatId, - }); - } - } - }, - }); - - // Return the properly formatted data stream response with chat ID header - const streamResponse = result.toDataStreamResponse(); - streamResponse.headers.set('X-Chat-ID', finalChatId); + errorLogger.info(ErrorCategory.AI_MODEL, 'Chat API request received', { + modelId, + messageCount: messages.length + }); - // Convert to NextResponse - return new NextResponse(streamResponse.body, { - status: streamResponse.status, - statusText: streamResponse.statusText, - headers: streamResponse.headers, - }); - } catch (groqError) { - errorLogger.logAiError(modelId || 'llama-3.3-70b-versatile', 'stream_text', groqError, { - userId: user.id, - chatId: finalChatId, - messageCount: messages.length, - }); + const provider = groqProvider({ + modelId: modelId, + headers: { + 'X-Request-ID': crypto.randomUUID() + } + }); - // User-friendly error for AI failures - const friendlyError = getUserFriendlyError(groqError, 'ai/generation-failed'); + const systemMessage = buildSystemMessage({ isAdmin: false }); - // Fallback response if Groq fails - if (finalChatId) { - try { - await addMessage(finalChatId, 'assistant', friendlyError.message); - } catch (saveError) { - errorLogger.warning(ErrorCategory.DATABASE, 'Failed to save fallback message', saveError); - } + const result = await streamObject({ + model: provider, + messages: [{ role: 'system', content: systemMessage }, ...messages], + schema: responseSchema, + onFinish: ({ usage }) => { + errorLogger.info(ErrorCategory.AI_MODEL, 'Chat API response completed', { + modelId, + tokensUsed: usage + }); } - - return NextResponse.json( - { - error: friendlyError.title, - message: friendlyError.message, - suggestion: friendlyError.suggestion, - chatId: finalChatId, - }, - { status: 503 } - ); - } - } catch (error) { - const duration = Date.now() - startTime; - errorLogger.critical(ErrorCategory.API, 'Unexpected chat API error', error, { - endpoint: '/api/chat', - method: 'POST', - duration, }); - const friendlyError = getUserFriendlyError(error); - return NextResponse.json( - { - error: friendlyError.title, - message: friendlyError.message, - suggestion: friendlyError.suggestion, - }, - { status: 500 } + return result.toTextStreamResponse(); + } catch (error) { + errorLogger.error(ErrorCategory.AI_MODEL, 'Chat API error', error); + + const userError = createUserFriendlyError( + error instanceof Error ? error : new Error('Unknown error'), + ErrorCategory.AI_MODEL + ); + + return new Response( + JSON.stringify({ error: userError.userMessage }), + { + status: 500, + headers: { 'Content-Type': 'application/json' } + } ); } } - -// Export the POST handler with rate limiting and validation -export const POST = withRateLimit( - withValidation(handleChatRequest, { - body: chatRequestSchema, - }), - RateLimitConfigs.AI_ENDPOINTS -); diff --git a/app/api/chat/save-message/route.ts b/app/api/chat/save-message/route.ts index 430696f8..24c882c8 100644 --- a/app/api/chat/save-message/route.ts +++ b/app/api/chat/save-message/route.ts @@ -5,7 +5,7 @@ import { errorLogger, ErrorCategory } from '@/lib/error-logger'; export async function POST(request: NextRequest) { try { - const supabase = await createClient(); + await createClient(); // Initialize supabase client for authentication // Check if user is authenticated const user = await requireAuth(); diff --git a/app/api/checkout_sessions/route.ts b/app/api/checkout_sessions/route.ts index d60c46bd..bd9dd656 100644 --- a/app/api/checkout_sessions/route.ts +++ b/app/api/checkout_sessions/route.ts @@ -40,11 +40,12 @@ export async function POST() { } return NextResponse.redirect(session.url, { status: 303 }); - } catch (err: any) { - errorLogger.error(ErrorCategory.API, 'Stripe API error:', err.message); - const statusCode = typeof err.statusCode === 'number' ? err.statusCode : 500; + } catch (err: unknown) { + const error = err as { message?: string; statusCode?: number }; + errorLogger.error(ErrorCategory.API, 'Stripe API error:', error.message || 'Unknown error'); + const statusCode = typeof error.statusCode === 'number' ? error.statusCode : 500; return NextResponse.json( - { error: err.message || 'An unknown error occurred' }, + { error: error.message || 'An unknown error occurred' }, { status: statusCode } ); } diff --git a/app/api/generate-stripe-checkout/route.ts b/app/api/generate-stripe-checkout/route.ts index fa0a9b7f..007ab474 100644 --- a/app/api/generate-stripe-checkout/route.ts +++ b/app/api/generate-stripe-checkout/route.ts @@ -42,7 +42,7 @@ export async function GET(req: NextRequest) { // Find the price that matches our product name const price = prices.data.find((p) => { - const product = p.product as any; + const product = p.product as { name?: string }; return product.name === productName && p.active; }); diff --git a/app/api/polar/portal/route.ts b/app/api/polar/portal/route.ts index f989b67b..46574058 100644 --- a/app/api/polar/portal/route.ts +++ b/app/api/polar/portal/route.ts @@ -1,5 +1,5 @@ import { CustomerPortal } from '@polar-sh/nextjs'; -import { NextRequest, NextResponse } from 'next/server'; +import { NextResponse } from 'next/server'; import { createClient } from '@/lib/supabase-server'; import { errorLogger, ErrorCategory } from '@/lib/error-logger'; @@ -8,7 +8,7 @@ const accessToken = process.env.POLAR_ACCESS_TOKEN; const server = (process.env.POLAR_SERVER as 'sandbox' | 'production') || 'sandbox'; export const GET = !accessToken - ? async (request: NextRequest) => { + ? async () => { errorLogger.error(ErrorCategory.API, 'POLAR_ACCESS_TOKEN is not configured'); return NextResponse.json( { error: 'Customer portal not available. Please contact support.' }, @@ -17,7 +17,7 @@ export const GET = !accessToken } : CustomerPortal({ accessToken, - getCustomerId: async (req: NextRequest) => { + getCustomerId: async () => { // Get the current session to identify the customer const supabase = await createClient(); const { diff --git a/app/api/polar/test/route.ts b/app/api/polar/test/route.ts index 7f365ca5..1eb7bc89 100644 --- a/app/api/polar/test/route.ts +++ b/app/api/polar/test/route.ts @@ -1,6 +1,6 @@ -import { NextRequest, NextResponse } from 'next/server'; +import { NextResponse } from 'next/server'; -export async function GET(request: NextRequest) { +export async function GET() { const accessToken = process.env.POLAR_ACCESS_TOKEN; const server = process.env.POLAR_SERVER || 'sandbox'; diff --git a/app/api/polar/webhooks/route.ts b/app/api/polar/webhooks/route.ts index 5fd3cd0c..38cc1639 100644 --- a/app/api/polar/webhooks/route.ts +++ b/app/api/polar/webhooks/route.ts @@ -1,7 +1,7 @@ import { Webhooks } from '@polar-sh/nextjs'; import { supabaseAdmin } from '@/lib/supabase-admin'; -async function handleSubscription(subscription: any) { +async function handleSubscription(subscription: Record) { if (!supabaseAdmin) { console.error('Supabase admin client not available - cannot process subscription webhook'); return; @@ -11,21 +11,21 @@ async function handleSubscription(subscription: any) { .from('subscriptions') .upsert({ id: subscription.id, - user_id: subscription.customer_id, // Assuming customer_id is the supabase user_id - status: subscription.status, - price_id: subscription.price.id, - cancel_at_period_end: subscription.cancel_at_period_end, - current_period_start_at: subscription.current_period_start, - current_period_end_at: subscription.current_period_end, - ended_at: subscription.ended_at, - cancel_at: subscription.cancel_at, - canceled_at: subscription.canceled_at, - trial_start_at: subscription.trial_start, - trial_end_at: subscription.trial_end, + user_id: (subscription as { customer_id?: string }).customer_id, // Assuming customer_id is the supabase user_id + status: (subscription as { status?: string }).status, + price_id: (subscription as { price?: { id?: string } }).price?.id, + cancel_at_period_end: (subscription as { cancel_at_period_end?: boolean }).cancel_at_period_end, + current_period_start_at: (subscription as { current_period_start?: string }).current_period_start, + current_period_end_at: (subscription as { current_period_end?: string }).current_period_end, + ended_at: (subscription as { ended_at?: string }).ended_at, + cancel_at: (subscription as { cancel_at?: string }).cancel_at, + canceled_at: (subscription as { canceled_at?: string }).canceled_at, + trial_start_at: (subscription as { trial_start?: string }).trial_start, + trial_end_at: (subscription as { trial_end?: string }).trial_end, }); } -async function handleProduct(product: any) { +async function handleProduct(product: Record) { if (!supabaseAdmin) { console.error('Supabase admin client not available - cannot process product webhook'); return; @@ -35,10 +35,10 @@ async function handleProduct(product: any) { .from('products') .upsert({ id: product.id, - active: product.is_archived ? false : true, - name: product.name, - description: product.description, - image: product.image, + active: (product as { is_archived?: boolean }).is_archived ? false : true, + name: (product as { name?: string }).name, + description: (product as { description?: string }).description, + image: (product as { image?: string }).image, }); if (product.prices) { diff --git a/app/api/sync-stripe-data/route.ts b/app/api/sync-stripe-data/route.ts index 8165a80e..af85e29d 100644 --- a/app/api/sync-stripe-data/route.ts +++ b/app/api/sync-stripe-data/route.ts @@ -1,6 +1,6 @@ -import { NextRequest, NextResponse } from 'next/server'; +import { NextResponse } from 'next/server'; // Placeholder route – to be implemented -export async function POST(_req: NextRequest) { +export async function POST() { return NextResponse.json({ error: 'Sync Stripe data endpoint not implemented.' }, { status: 501 }); } diff --git a/app/api/version-check/route.ts b/app/api/version-check/route.ts index 8109a485..8fcd5878 100644 --- a/app/api/version-check/route.ts +++ b/app/api/version-check/route.ts @@ -1,8 +1,8 @@ -import { NextRequest, NextResponse } from 'next/server'; +import { NextResponse } from 'next/server'; import packageJson from '../../../package.json'; import { errorLogger, ErrorCategory } from '@/lib/error-logger'; -export async function GET(request: NextRequest) { +export async function GET() { try { const currentVersion = packageJson.version; @@ -98,7 +98,7 @@ export async function GET(request: NextRequest) { updateInfo = `Latest development version (${masterData.sha.substring(0, 7)})`; } } - } catch (error) { + } catch { // If we can't check package.json, still notify about master commits if they're recent const masterCommitDate = new Date(masterData.commit.committer.date); const oneDayAgo = new Date(Date.now() - 24 * 60 * 60 * 1000); diff --git a/app/api/webcontainer/fallback/route.ts b/app/api/webcontainer/fallback/route.ts new file mode 100644 index 00000000..e840a9fc --- /dev/null +++ b/app/api/webcontainer/fallback/route.ts @@ -0,0 +1,264 @@ +import { NextRequest, NextResponse } from 'next/server'; +import { errorLogger, ErrorCategory } from '@/lib/error-logger'; + +export async function POST(request: NextRequest) { + try { + const { code, projectType, fallbackAttempt } = await request.json(); + + // Log fallback attempt + console.log(`🔄 Server fallback attempt ${fallbackAttempt} for ${projectType} project`); + + // Validate input + if (!code || typeof code !== 'string') { + return NextResponse.json( + { success: false, error: 'Invalid code provided' }, + { status: 400 } + ); + } + + // Generate server-side preview based on project type + const previewContent = generateServerPreview(code, projectType); + + // In a real implementation, you might want to: + // 1. Validate the code for security + // 2. Run it in a sandboxed environment + // 3. Generate actual server-side rendering + // 4. Cache results for performance + + return NextResponse.json({ + success: true, + content: previewContent, + url: null, // No URL for server-side preview + message: 'Server-side preview generated successfully', + fallbackAttempt, + }); + + } catch (error) { + errorLogger.error(ErrorCategory.AI_MODEL, 'Server fallback API error:', error); + + return NextResponse.json( + { + success: false, + error: error instanceof Error ? error.message : 'Server fallback failed' + }, + { status: 500 } + ); + } +} + +function generateServerPreview(code: string, projectType: string): string { + // Server-side preview generation logic + const sanitizedCode = sanitizeCode(code); + + switch (projectType) { + case 'html': + return generateHTMLPreview(sanitizedCode); + case 'react': + return generateReactServerPreview(sanitizedCode); + case 'vue': + return generateVueServerPreview(sanitizedCode); + case 'svelte': + return generateSvelteServerPreview(sanitizedCode); + default: + return generateHTMLPreview(sanitizedCode); + } +} + +function sanitizeCode(code: string): string { + // Basic sanitization - remove potentially dangerous scripts + return code + .replace(/]*>[\s\S]*?<\/script>/gi, '
[Script removed for security]
') + .replace(/javascript:/gi, '#') + .replace(/data:text\/html/gi, '#') + .replace(/vbscript:/gi, '#') + .replace(/onload\s*=/gi, 'data-onload=') + .replace(/onerror\s*=/gi, 'data-onerror=') + .replace(/onclick\s*=/gi, 'data-onclick='); +} + +function generateHTMLPreview(code: string): string { + if (code.includes('') || code.includes(' + + + + + ZapDev Server Preview + + + +
+

🚀 Server Preview Mode

+

Generated on the server for maximum compatibility. Interactive features may be limited.

+
+ ${code} + + + `.trim(); +} + +function generateReactServerPreview(code: string): string { + // Simple React JSX to HTML conversion for server preview + const htmlContent = code + .replace(/import\s+.*?from\s+['"].*?['"];?\s*/g, '') + .replace(/export\s+default\s+function\s+\w+\s*\([^)]*\)\s*\{/, '
') + .replace(/return\s*\(?\s*/, '') + .replace(/\)?\s*;\s*\}\s*$/, '
') + .replace(/className=/g, 'class=') + .replace(/\{[^}]*\}/g, '[Dynamic Content]') + .replace(/onClick=/g, 'data-onclick=') + .replace(/onChange=/g, 'data-onchange='); + + return generateHTMLPreview(htmlContent); +} + +function generateVueServerPreview(code: string): string { + // Extract template from Vue SFC + const templateMatch = code.match(/