Skip to content

Implement Netlify deployment integration and payment templates#217

Open
Jackson57279 wants to merge 13 commits intomasterfrom
feat/pr-215-netlify-payment-integration
Open

Implement Netlify deployment integration and payment templates#217
Jackson57279 wants to merge 13 commits intomasterfrom
feat/pr-215-netlify-payment-integration

Conversation

@Jackson57279
Copy link
Owner

@Jackson57279 Jackson57279 commented Jan 27, 2026

Overview

This PR adds comprehensive Netlify deployment capabilities and payment integration templates to the ZapDev platform.

Note: This PR contains all changes from PR #215 (feat/roadmap-completeation) which remains open as requested.


Key Features

1. Netlify Deployment Integration

  • ✅ OAuth authentication with Netlify
  • ✅ Full deployment workflow (deploy, status tracking, rollback)
  • ✅ Custom domain management
  • ✅ Environment variable management
  • ✅ Deployment logs and history
  • ✅ Preview deployments

2. GitHub Export Functionality

  • ✅ Export projects to GitHub repositories
  • ✅ Create new repositories or push to existing ones
  • ✅ Branch and commit management
  • ✅ OAuth integration with GitHub

3. Payment Integration Templates

  • ✅ Framework-specific templates for Angular, React, Next.js, Svelte, and Vue
  • ✅ Integration with Stripe via Autumn
  • ✅ Checkout flows, billing portal, feature gates, and usage tracking

4. Additional Features

  • ✅ Anthropic OAuth for Claude Code mode
  • ✅ Encrypted OAuth token storage
  • ✅ Color theme picker
  • ✅ Inngest integration for real-time agent streaming
  • ✅ Database provider templates (Drizzle + Neon, Convex)

Files Changed

111 files changed (+12,293 additions, -1,257 deletions)

New API Endpoints

  • /api/deploy/netlify/* - Complete Netlify deployment API
  • /api/github/repositories - GitHub repository management
  • /api/projects/[projectId]/export/github - GitHub export
  • /api/auth/anthropic/* - Anthropic OAuth

New Libraries & Components

  • src/lib/netlify-client.ts - Netlify API client
  • src/lib/github-api.ts - GitHub API client
  • src/lib/payment-provider.ts - Payment provider abstraction
  • src/lib/payment-templates/* - Framework-specific payment templates
  • src/lib/database-templates/* - Database integration templates
  • src/modules/projects/ui/components/* - Deployment UI components

Convex Schema Updates

  • New deployments table for tracking Netlify deployments
  • New githubExports table for GitHub export history
  • Enhanced oauth table for multiple providers

Environment Variables Required

# Netlify
NETLIFY_CLIENT_ID=""
NETLIFY_CLIENT_SECRET=""
NETLIFY_OAUTH_STATE_SECRET=""

# OAuth Encryption
OAUTH_ENCRYPTION_KEY=""

# Payment Integration (Autumn/Stripe)
AUTUMN_API_KEY=""
STRIPE_PUBLISHABLE_KEY=""
STRIPE_SECRET_KEY=""
STRIPE_WEBHOOK_SECRET=""

# Anthropic (Claude Code)
ANTHROPIC_CLIENT_ID=""
ANTHROPIC_CLIENT_SECRET=""
CLAUDE_CODE_ENABLED=""

Testing Checklist

  • Netlify OAuth flow
  • Deploy project to Netlify
  • Custom domain management
  • Environment variable management
  • GitHub export (new repository)
  • GitHub export (existing repository)
  • Payment template generation for all frameworks
  • Database template selection
  • Deployment status tracking
  • Rollback functionality

Related


Summary by cubic

Adds one-click Netlify deployments and framework-specific payment templates, plus GitHub export and Inngest-powered real-time agent streaming. Also adds Anthropic OAuth, encrypts OAuth tokens, and updates the project UI for deploys, exports, and env management.

  • New Features

    • Netlify deploys: OAuth connect, deploy/preview/rollback, status/logs, custom domains, env vars; Convex models and API routes; deployment dashboard UI.
    • GitHub export: create or push to repos with branches/commits; tracked in Convex; export modal and repo picker.
    • Payments: templates for Next.js, React, Vue, Angular, and Svelte using Autumn + Stripe (checkout, billing portal, feature gates, usage metering).
    • Agents and auth: Inngest orchestration with realtime streaming; Anthropic OAuth for Claude Code mode; encrypted OAuth token storage; sturdier rate-limit/error handling.
    • Database and theming: Drizzle+Neon and Convex templates with env examples and provider selection; color theme picker.
  • Migration

    • Add env vars:
      • Netlify: NETLIFY_CLIENT_ID, NETLIFY_CLIENT_SECRET, NETLIFY_OAUTH_STATE_SECRET
      • OAuth encryption: OAUTH_ENCRYPTION_KEY
      • Anthropic (Claude Code): ANTHROPIC_CLIENT_ID, ANTHROPIC_CLIENT_SECRET, CLAUDE_CODE_ENABLED, NEXT_PUBLIC_APP_URL
      • Payments (Autumn/Stripe): AUTUMN_API_KEY, STRIPE_PUBLISHABLE_KEY, STRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRET
    • Deploy Convex schema to create deployments/githubExports tables and updated oauth schema.

Written for commit ae78f07. Summary will update on new commits.

Summary by CodeRabbit

Release Notes

  • New Features

    • Claude Code integration for advanced AI-powered development
    • Netlify deployment with site management, custom domains, and environment variables
    • GitHub export functionality to push generated code to repositories
    • Database provider selection (Drizzle + Neon, Convex, or none)
    • Payment system integration via Autumn/Stripe for generated apps
    • Color theme customization options
    • Blog and AI tool comparison pages
    • Anthropic OAuth authentication
  • Documentation

    • Updated multi-framework support documentation
    • Added comparison guides for AI code generation tools

✏️ Tip: You can customize this high-level summary in your review settings.

- Added support for deploying projects to Netlify, including authentication and deployment status tracking.
- Introduced new API routes for managing Netlify domains, environment variables, and deployment logs.
- Created payment integration templates for various frameworks (Angular, React, Next.js, Svelte, Vue) using Autumn and Stripe.
- Updated environment example and README to include new Netlify and payment integration configurations.
- Enhanced project UI with deployment dashboard and export options to GitHub.

This commit significantly expands the deployment capabilities and payment processing features of the application.
- Refactored deployment creation to utilize a project deployment counter for better tracking of deployment numbers.
- Updated OAuth token handling to include encryption for access and refresh tokens, improving security.
- Modified GitHub export functionality to check for connection existence without requiring an access token.
- Improved error handling and validation in various API routes, ensuring more robust interactions with external services.
- Enhanced UI components for deployment history and environment variable management, adding loading states and better user feedback.

These changes significantly improve the deployment process and security of OAuth tokens, while also enhancing user experience in the application.
Add provider selection/templates and persist choices so generated projects get the right integration rules, plus add color theme selection and refresh the roadmap.
- Added creation timestamps to project deployment counters for better tracking.
- Improved GitHub export functionality by integrating a new action to retrieve the current user's GitHub access token.
- Updated OAuth handling to ensure required environment variables are set, enhancing security and error management.
- Enhanced error handling in various API routes and improved user feedback in UI components.

These changes streamline deployment processes and strengthen OAuth security, contributing to a more robust application.
- Revised README to reflect expanded multi-framework support in E2B sandboxes, including Next.js, React, Vue, Angular, and Svelte.
- Updated tech stack details, replacing Inngest with Convex for real-time project management and persistence.
- Enhanced SEO metadata generation to include structured data for Organization and WebSite types, improving search visibility.
- Added new routes to the sitemap and robots.txt for better indexing.
- Improved content clarity across various pages, emphasizing AI development solutions and framework comparisons.

These changes improve documentation accuracy and enhance the application's SEO performance.
- Updated robots.txt to streamline disallowed paths and improve bot directives.
- Enhanced client configuration in agents to include custom headers for better tracking.
- Improved error handling in the runCodeAgent function, adding retry logic and detailed error collection for rate limit scenarios.

These changes enhance the application's bot management, client tracking, and error resilience, contributing to a more robust and user-friendly experience.
…latform updates

- Deleted the Clerk Billing migration summary, progress, quick reference, and setup checklist documents as they are no longer relevant.
- Removed the changelog for November & December 2025, which detailed significant platform improvements and changes that have since been superseded.

These deletions help streamline the documentation and ensure that only current and relevant information is maintained in the repository.
- Added `react-markdown` and `remark-gfm` to dependencies for improved markdown support.
- Updated `robots.txt` to include directives for various AI crawlers, enhancing bot management.
- Added a new blog URL to the sitemap and RSS feed for better indexing and visibility.

These changes improve the application's dependency management, SEO performance, and bot handling capabilities.
- Added support for comparison pages in the sitemap, including a new `/compare` route and dynamic comparison URLs based on available comparisons.
- Updated the homepage and pricing page metadata to better reflect Zapdev's offerings and improve search engine optimization.
- Enhanced blog content with updated statistics and comparisons, emphasizing the advantages of using Zapdev over competitors.
- Revised framework and solutions pages to provide clearer guidance on framework selection and available AI development solutions.

These changes improve the application's SEO performance and enhance user navigation through better content organization.
…rove error handling

- Added `react-markdown` and `remark-gfm` to package dependencies for improved markdown rendering.
- Refactored `SANDBOX_ROOT` initialization to support environment variable configuration with validation for absolute paths.
- Enhanced file path validation in the `createClaudeCodeTools` function to handle invalid paths more gracefully and provide clearer error messages.
- Updated the RSS feed to use a fixed publication date for consistency.

These changes improve dependency management, enhance path handling, and provide better user feedback in the application.
- Introduced `@inngest/realtime` and `inngest` dependencies to facilitate real-time event subscription and handling.
- Updated the agent's `POST` route to generate a unique run ID and send requests to Inngest for processing.
- Implemented subscription to Inngest events, allowing for real-time streaming of execution results and error handling.
- Enhanced error handling to ensure proper cancellation of subscriptions and graceful error reporting.

These changes improve the agent's responsiveness and provide a more interactive user experience during code execution.
@codecapyai
Copy link

codecapyai bot commented Jan 27, 2026

🔍 Analyzing PR changes and preparing to run tests...

@vercel
Copy link

vercel bot commented Jan 27, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Review Updated (UTC)
zapdev Error Error Jan 27, 2026 8:11am

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 27, 2026

📝 Walkthrough

Walkthrough

Comprehensive platform expansion introducing agent orchestration via Inngest, Netlify deployment infrastructure with custom domains and environment variables, GitHub export capability, multi-database templates (Drizzle Neon/Convex), Autumn/Stripe payment integration templates for all frameworks, Claude Code support, color theming, SEO-optimized comparison pages, and blog functionality while removing deprecated billing migration documentation.

Changes

Cohort / File(s) Summary
Inngest Agent Orchestration
src/inngest/client.ts, src/inngest/functions/code-agent.ts, src/inngest/types.ts, src/app/api/inngest/route.ts
New Inngest client with realtime middleware; code-agent function wrapping runCodeAgent with event streaming via agentChannel topics (status, text, tool-call, progress, complete, error); event type definitions for requested/progress/complete/error states.
Claude Code Integration
src/agents/client.ts, src/agents/code-agent.ts, src/agents/claude-code-tools.ts, src/agents/types.ts, src/agents/index.ts
New Anthropic client creation with token support; Claude Code model routing and feature flag; comprehensive sandbox tooling (execute_command, read/write/delete files, search, list_directory); extended model configs with claude-code variants and isClaudeCode flags.
Database Provider Support
convex/schema.ts, convex/projects.ts, src/agents/types.ts, src/lib/database-templates/*
Schema additions: databaseProviderEnum, deployments/githubExports/projectDeploymentCounters tables; project databaseProvider field; Drizzle Neon and Convex template bundles with auth, schema, and framework-specific implementations for Next.js; environment examples.
Netlify Deployment API
src/app/api/deploy/netlify/{auth,callback,deploy,domains,env-vars,logs,preview,rollback,sites,status}/route.ts
OAuth flow initiation and callback handling; site and deployment management; environment variable CRUD; custom domain management; deployment logs retrieval; preview deployment deletion; rollback capability; status polling.
Deployment UI Components
src/modules/projects/ui/components/{deployment-dashboard,deployment-status,deployment-history,deploy-button,env-vars-dialog,custom-domain-dialog,preview-deployments,netlify-connect-dialog}.tsx
Dashboard integrating status, history, and environment management; status polling with Netlify API; deployment history with log viewing and rollback actions; environment variable editor; custom domain dialog; Netlify connection dialog.
GitHub Export Integration
src/app/api/github/repositories/route.ts, src/app/api/projects/[projectId]/export/github/route.ts, src/modules/projects/ui/components/{github-export-button,github-export-modal}.tsx, convex/githubExports.ts, src/lib/github-api.ts
GitHub OAuth token retrieval and repository listing; export to new/existing repositories with commit, branch, and file filtering; UI modal for export workflow with repository selection; Convex backend for export status tracking; comprehensive GitHub API client with tree/commit operations and readme/gitignore generation.
Payment Templates (All Frameworks)
src/lib/payment-templates/{index,types,angular,nextjs,react,vue,svelte}.ts, src/lib/payment-templates/{autumn-config,env-example}.ts
PaymentTemplateBundle for each framework with Autumn API client, checkout/portal/usage/feature routes, webhook verification, client-side components (CheckoutButton, FeatureGate), and usage tracking; centralized config and environment examples.
Payment Integration Prompts
src/prompts/{payment-integration,database-integration,database-selector}.ts, src/prompts/{nextjs,react,vue,angular,svelte}.ts
New PAYMENT_INTEGRATION_RULES injected into all framework prompts; database integration rules for Drizzle Neon and Convex; database selector prompt with validation helper; DATABASE_SELECTOR_PROMPT export.
OAuth & Authentication
src/app/api/auth/anthropic/{route,callback}/route.ts, convex/oauth.ts
New Anthropic OAuth routes (initiate and callback); token encryption/decryption (AES-256-GCM); GitHub and Anthropic token retrieval for users; Anthropic connection existence check.
Convex Deployments Backend
convex/deployments.ts
Mutations and queries for deployment lifecycle: create, update, get single, list by project; metadata tracking (platform, site, branch, status, build logs); per-project deployment counter.
SEO & Comparisons
src/lib/comparisons.ts, src/app/compare/{page,\[slug\]/page}.tsx, src/components/seo/internal-links.tsx, src/lib/seo.ts
Comprehensive comparison data model with multiple tool comparisons (Zapdev vs Lovable/Bolt/GitHub Copilot, best tools); dynamic comparison pages with structured data, FAQs, pros/cons, recommendations; internal links support for comparisons; SEO metadata consolidation with organization constants.
Color Theming System
src/lib/themes.ts, src/components/color-theme-provider.tsx, src/components/color-theme-picker.tsx
ColorTheme type with light/dark preview colors and theme variables; COLOR_THEMES array with 10+ predefined themes; provider component managing theme persistence and CSS variable injection; picker UI with visual previews and tooltips.
Blog & Content
src/app/blog/{page,markdown-content,content}.tsx, public/{llms.txt,robots.txt}
New blog page rendering markdown content; MarkdownContent component with GFM support; blog article content on AI code generation alternatives; llms.txt descriptor; enhanced robots.txt with per-bot rules and project/monitoring disallows.
Project Header & View Updates
src/modules/projects/ui/components/project-header.tsx, src/modules/projects/ui/views/project-view.tsx
Color theme picker integration in header; new Deploy control group with DeploymentStatus, DeployButton, GitHubExportButton; new Deploy tab in project view with DeploymentDashboard.
Home & Pricing Page Updates
src/app/(home)/page.tsx, src/app/(home)/pricing/page.tsx
New FAQ items and statistics on build speed/frameworks; updated SEO metadata; pricing page keyword expansion.
Framework & Solution Pages
src/app/frameworks/{page,\[slug\]/page}.tsx, src/app/solutions/{page,\[slug\]/page.tsx, src/app/showcase/page.tsx
Updated copy and headings for multi-framework framing; expanded intro/footer messaging; reworded section titles for user-focus.
Sitemap & RSS Updates
src/app/sitemap.ts, src/app/rss.xml/route.ts, src/app/robots.ts
Added comparison page entries to sitemap; blog entry to RSS with fixed date; consolidated bot rules in robots.ts via array spread pattern.
Layout & Theme Provider
src/app/layout.tsx
Wrapped ThemeProvider children with new ColorThemeProvider.
Documentation Removal
CHANGELOG_NOVEMBER_DECEMBER_2025.md, CLERK_BILLING_MIGRATION*.md (4 files)
Deleted changelog and billing migration guides (477 total lines removed).
README & ROADMAP Updates
README.md, ROADMAP.md
Expanded tech stack narrative from Inngest to Convex; multi-framework support; Polar.sh payments; updated dev workflow to bun; new deployment/GitHub export/payments sections; reorganized roadmap with status labels and new platform/database/deployment sections.
Configuration & Utilities
package.json, env.example, src/agents/rate-limit.ts, src/agents/tools.ts, src/lib/netlify-client.ts, src/lib/netlify-config.ts
Added Anthropic/Inngest/react-markdown dependencies; Anthropic/Netlify env vars; improved rate-limit error detection with error chain traversal; new payment/database template tools; Netlify API client with site/deployment/domain management; Netlify TOML config generator for frameworks.
Frameworks & Prompt System
src/lib/frameworks.ts, src/prompt.ts, src/prompts/shared.ts
Type inference update for frameworks satisfies clause; new exports for database/payment selectors and rules; updated shared guidelines emphasizing databaseTemplates approach, modularization, and production quality.
Geo Implementation Prompt
geo-implementation-prompt.md
New comprehensive SEO/GEO optimization strategy document (612 lines) covering content audit, schema markup, enhancement, distribution, and measurement.

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Extensive, heterogeneous changes spanning new orchestration layer (Inngest), multiple integration systems (Netlify, GitHub, Anthropic, Autumn), backend schema and Convex operations, payment/database template libraries, agent tooling, SEO/comparison infrastructure, theming system, and numerous API routes. High logic density in GitHub export, deployment management, and token encryption workflows; diverse file types and patterns (backend, frontend, API, utilities, documentation); significant surface area requiring holistic understanding of interconnected systems.

Possibly related PRs

  • PRs modifying agent execution/streaming – Related through changes to runCodeAgent invocation surface; prior PR may have altered StreamEvent types or agent tooling that interfaces with the new Inngest wrapper.
  • PRs extending agent model providers – Related through modifications to src/agents/client.ts and src/agents/types.ts for model routing; prior work on model support or provider abstraction may need alignment.

Poem

🐰 A rabbit's code grows wings of cloud,
With Inngest steps and GitHub proud,
Netlify dreams and themes so bright,
Claude's tools dance left and right,
Payments flowing, databases deep—
What wonders shall the platform reap!

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 4.41% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main changes: introducing Netlify deployment integration and payment templates, which aligns with the primary objectives of the PR.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecapyai
Copy link

codecapyai bot commented Jan 27, 2026

🚀 Launching Scrapybara desktop...

@codecapyai
Copy link

codecapyai bot commented Jan 27, 2026

❌ Something went wrong:

status_code: 500, body: {'detail': 'Error creating instance: HTTPSConnectionPool(host=\'dd71ce9e4c14175cfb2d4b4d613159f4.sk1.us-west-1.eks.amazonaws.com\', port=443): Max retries exceeded with url: /api/v1/namespaces/scrapybara-instances/services (Caused by NameResolutionError("<urllib3.connection.HTTPSConnection object at 0x7fd7889ad4d0>: Failed to resolve \'dd71ce9e4c14175cfb2d4b4d613159f4.sk1.us-west-1.eks.amazonaws.com\' ([Errno -2] Name or service not known)"))'}

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ae78f07b3d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +86 to +95
const getNetlifyAccessToken = async (token?: string): Promise<string> => {
const connection = await fetchQuery(api.oauth.getConnection, {
provider: "netlify",
}, { token }) as NetlifyConnection | null;

if (!connection?.accessToken) {
throw new Error("Netlify connection not found. Please connect your Netlify account.");
}

return connection.accessToken;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Decrypt stored Netlify tokens before API calls

All Netlify API routes pull connection.accessToken from api.oauth.getConnection, but storeConnection encrypts access tokens before persisting them in convex/oauth.ts. That means getNetlifyAccessToken() returns an encrypted blob, so createNetlifyClient() will send an invalid bearer token and Netlify will reject every deploy/status/env/domain request with 401 after a successful OAuth connect. This needs a decrypting query/action (like the GitHub path uses) before passing the token to Netlify.

Useful? React with 👍 / 👎.

Comment on lines +46 to +49
try {
const decodedState = JSON.parse(Buffer.from(state, "base64").toString());
if (decodedState.userId !== userId) {
throw new Error("State token mismatch");

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Validate Anthropic OAuth state with a signature/nonce

The callback only base64-decodes state and checks decodedState.userId, with no signature, nonce, or timestamp validation. If an attacker can learn/guess a user’s ID, they can forge state and supply their own OAuth code to bind the victim’s account to the attacker’s Anthropic token (CSRF/replay). The state needs to be unguessable and verified (e.g., HMAC + TTL like the Netlify flow).

Useful? React with 👍 / 👎.

Copy link

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

31 issues found across 111 files

Note: This PR contains a large number of files. cubic only reviews up to 75 files per PR, so some files may not have been reviewed.

Prompt for AI agents (all issues)

Check if these issues are valid — if so, understand the root cause of each and fix them.


<file name="src/app/api/deploy/netlify/callback/route.ts">

<violation number="1" location="src/app/api/deploy/netlify/callback/route.ts:108">
P2: `crypto.timingSafeEqual()` throws an error when buffers have different lengths. Since `decodedState.signature` comes from user input, an attacker could trigger an exception by providing a signature of unexpected length. Add a length check before comparison, similar to other usages in this codebase.</violation>
</file>

<file name="src/app/api/auth/anthropic/callback/route.ts">

<violation number="1" location="src/app/api/auth/anthropic/callback/route.ts:47">
P1: **Security: OAuth state token lacks cryptographic signature (CSRF vulnerability)**

The state parameter is only base64-encoded JSON with no cryptographic signature, making it trivially forgeable. An attacker can craft a valid-looking state for any user by simply base64-encoding `{"userId": "targetId", "timestamp": ...}`.

This defeats the purpose of OAuth state validation (CSRF protection). The Netlify OAuth implementation in this codebase correctly uses HMAC-SHA256 signing with `NETLIFY_OAUTH_STATE_SECRET`.

Recommendation: Add HMAC-SHA256 signature verification similar to the Netlify implementation, or use a cryptographically random token stored server-side.</violation>

<violation number="2" location="src/app/api/auth/anthropic/callback/route.ts:48">
P2: **Security: OAuth state timestamp is not validated**

The state includes a `timestamp` field but it's never checked. This allows state tokens to be valid indefinitely. Consider adding TTL validation similar to the Netlify implementation (`STATE_TTL_MS = 10 * 60 * 1000`).</violation>
</file>

<file name="src/components/color-theme-provider.tsx">

<violation number="1" location="src/components/color-theme-provider.tsx:81">
P2: Context value object should be memoized with `useMemo` to prevent unnecessary re-renders of all consuming components. Without memoization, a new object reference is created on every render, causing all `useColorTheme()` consumers to re-render even when theme values haven't changed.</violation>
</file>

<file name="src/lib/github-api.ts">

<violation number="1" location="src/lib/github-api.ts:236">
P1: Path traversal vulnerability: `sanitizePath` does not block `..` sequences, null bytes, or control characters. An attacker could use paths like `../../../.git/hooks/pre-commit` to write files outside the intended directory. Consider reusing `isValidFilePath` from `sandbox-utils.ts` or adding validation for path traversal patterns.</violation>
</file>

<file name="src/app/api/deploy/netlify/auth/route.ts">

<violation number="1" location="src/app/api/deploy/netlify/auth/route.ts:6">
P1: Hardcoded fallback secret undermines OAuth CSRF protection. If `NETLIFY_OAUTH_STATE_SECRET` is not set, attackers can forge valid state tokens using this public fallback value. Handle the missing secret like `NETLIFY_CLIENT_ID` by returning an error when not configured.</violation>
</file>

<file name="src/app/compare/[slug]/page.tsx">

<violation number="1" location="src/app/compare/[slug]/page.tsx:260">
P1: CTA button is non-functional - missing `Link` wrapper or `onClick` handler. This "Get Started Free" button won't navigate anywhere when clicked. Based on `src/app/compare/page.tsx`, this should be wrapped in a `<Link href="/">` component.</violation>
</file>

<file name="src/app/api/deploy/netlify/sites/route.ts">

<violation number="1" location="src/app/api/deploy/netlify/sites/route.ts:36">
P2: Missing Netlify OAuth connection returns 500 instead of 401/403. When `getNetlifyAccessToken()` throws "Netlify connection not found", it's caught by the generic error handler and returned as a 500 server error. This is semantically incorrect—a missing OAuth connection is an authorization issue, not a server error. Consider checking for this specific error and returning an appropriate status code.</violation>
</file>

<file name="convex/deployments.ts">

<violation number="1" location="convex/deployments.ts:189">
P2: Query returns unbounded data. Consider adding a `limit` argument with `.take(limit)` to prevent performance issues as deployments accumulate. See `convex/webhooks.ts` `getRecentWebhookEvents` for the recommended pattern.</violation>
</file>

<file name="src/app/api/github/repositories/route.ts">

<violation number="1" location="src/app/api/github/repositories/route.ts:15">
P1: This calls an `internalQuery` which is not accessible from the public API. `getGithubAccessToken` is an internal Convex query that requires a `userId` argument. Use `getGithubAccessTokenForCurrentUser` action instead, which handles authentication internally.</violation>
</file>

<file name="src/app/api/deploy/netlify/preview/route.ts">

<violation number="1" location="src/app/api/deploy/netlify/preview/route.ts:32">
P1: Missing authorization check before deleting deployment. The `deployId` is taken directly from user input without verifying it belongs to a project the user has access to. Query the `deployments` table to validate ownership before calling the Netlify API.</violation>
</file>

<file name="src/app/api/deploy/netlify/logs/route.ts">

<violation number="1" location="src/app/api/deploy/netlify/logs/route.ts:43">
P2: Authorization errors returned as 500 status. When `getNetlifyAccessToken()` throws "Netlify connection not found" (user hasn't connected Netlify), it's caught here and returned as 500 instead of 401/403. This prevents the frontend from properly prompting users to connect their Netlify account.</violation>
</file>

<file name="src/app/api/deploy/netlify/rollback/route.ts">

<violation number="1" location="src/app/api/deploy/netlify/rollback/route.ts:46">
P2: The catch block returns HTTP 500 for all errors, but "Netlify connection not found" is an authorization error and should return 401 or 403. Consider checking for specific error messages and returning appropriate status codes.</violation>
</file>

<file name="src/lib/payment-provider.ts">

<violation number="1" location="src/lib/payment-provider.ts:227">
P2: Unsafe type assertion `null as T` violates project conventions and could cause runtime errors. When the API returns 204, this returns `null` but callers expect non-nullable types like `CheckoutSession` or `SubscriptionSummary`.

Consider either:
1. Making the return type explicitly nullable for methods that could receive 204
2. Throwing an error for unexpected 204 responses
3. Using a type guard or conditional type</violation>
</file>

<file name="src/lib/database-templates/drizzle-neon/nextjs.ts">

<violation number="1" location="src/lib/database-templates/drizzle-neon/nextjs.ts:70">
P2: The `pathname` should be URL-encoded when used as a query parameter to prevent malformed URLs or potential security issues with special characters.</violation>
</file>

<file name="src/lib/payment-templates/angular.ts">

<violation number="1" location="src/lib/payment-templates/angular.ts:304">
P2: Missing `response.ok` check before parsing JSON. If the server returns an error (4xx/5xx), this will silently return `false` instead of handling the error, potentially denying feature access during server issues.</violation>

<violation number="2" location="src/lib/payment-templates/angular.ts:309">
P2: Usage tracking fails silently without any error handling. If the API request fails, the caller has no way to know that usage wasn't recorded.</violation>
</file>

<file name="src/lib/netlify-client.ts">

<violation number="1" location="src/lib/netlify-client.ts:46">
P2: Returning `{} as T` for empty responses is type-unsafe and can cause runtime errors. Callers expecting typed objects (e.g., `NetlifySite` with required `id`, `name`) will get an empty object, leading to `undefined` property access. Consider returning `null` with `Promise<T | null>` signature, or throwing an error for unexpected empty responses.</violation>
</file>

<file name="src/app/compare/page.tsx">

<violation number="1" location="src/app/compare/page.tsx:131">
P2: Avoid nesting a <button> inside <Link>; it produces invalid HTML and accessibility issues. Style the Link as a button instead.</violation>
</file>

<file name="src/app/api/deploy/netlify/domains/route.ts">

<violation number="1" location="src/app/api/deploy/netlify/domains/route.ts:64">
P2: Missing null check on parsed JSON body. If the request body is `null` (valid JSON), accessing `body.siteId` throws a TypeError, returning a 500 error instead of a 400 validation error. Add a null check before accessing properties.</violation>
</file>

<file name="src/lib/database-templates/convex/nextjs.ts">

<violation number="1" location="src/lib/database-templates/convex/nextjs.ts:259">
P2: Dashboard template shows blank page briefly before redirect. When `!session`, it returns `null` while the `useEffect` redirect is in progress. Keep showing the loading indicator until navigation completes to avoid the blank flash.</violation>
</file>

<file name="src/inngest/functions/code-agent.ts">

<violation number="1" location="src/inngest/functions/code-agent.ts:9">
P1: Setting `retries: 3` with `publish()` inside `step.run()` will cause duplicate real-time events on retry. Inngest's realtime documentation recommends `retries: 0` for functions using publish to prevent subscribers from receiving duplicate streaming events when steps retry.</violation>
</file>

<file name="src/app/api/deploy/netlify/status/route.ts">

<violation number="1" location="src/app/api/deploy/netlify/status/route.ts:42">
P2: All errors return 500, but 'Netlify connection not found' from `getNetlifyAccessToken()` is an authorization issue, not a server error. Consider checking for this specific error and returning 401 to help clients differentiate between missing OAuth connections and actual server failures.</violation>
</file>

<file name="src/app/robots.ts">

<violation number="1" location="src/app/robots.ts:10">
P2: The AI crawler rules are less restrictive than the default robots rule, so those bots can crawl sensitive paths like `/admin/` and `/_next/` that are otherwise disallowed. Align the disallow list with the default rule to avoid unintentionally exposing these paths to those crawlers.</violation>
</file>

<file name="src/app/api/deploy/netlify/deploy/route.ts">

<violation number="1" location="src/app/api/deploy/netlify/deploy/route.ts:165">
P2: All errors return HTTP 500, but client errors like 'No AI-generated files' (400) and 'Netlify connection not found' (401/403) should use appropriate 4xx status codes. Consider using custom error classes or checking error messages to return correct status codes.</violation>
</file>

<file name="src/agents/claude-code-tools.ts">

<violation number="1" location="src/agents/claude-code-tools.ts:202">
P1: Missing path validation in `read_files` allows path traversal. Unlike `write_files` which validates paths with `isValidFilePath`, this tool passes paths directly to `readFileFast` which doesn't validate. Add validation before reading each file to prevent reading files outside the sandbox.</violation>
</file>

<file name="src/app/api/deploy/netlify/env-vars/route.ts">

<violation number="1" location="src/app/api/deploy/netlify/env-vars/route.ts:67">
P2: Request body validation doesn't verify that `value` is a string type. The check `body.value == null` only ensures presence, not type correctness. A request with a non-string `value` (e.g., number or object) would pass validation but could cause unexpected behavior when passed to the Netlify API.</violation>
</file>

<file name="README.md">

<violation number="1" location="README.md:55">
P2: The README points to `src/agents/code-agent.ts` for updating the E2B template name, but the actual template mapping is in `src/agents/sandbox-utils.ts` (`getE2BTemplate`). This will mislead contributors and prevent them from updating the template used by sandbox creation.</violation>
</file>

<file name="convex/oauth.ts">

<violation number="1" location="convex/oauth.ts:6">
P0: Missing `"use node"` directive required for Node.js `crypto` module. Convex runs functions in a V8 runtime by default, and using Node.js built-in modules like `crypto` requires adding `"use node"` at the top of the file. Without this, the code will fail at runtime.</violation>

<violation number="2" location="convex/oauth.ts:9">
P1: AES-256-GCM requires a 32-byte key, but the validation only checks for non-empty. If `OAUTH_ENCRYPTION_KEY` is shorter than 32 characters, `slice(0, 32)` will produce an invalid key length causing runtime encryption failures.</violation>
</file>

<file name="src/lib/database-templates/convex/shared.ts">

<violation number="1" location="src/lib/database-templates/convex/shared.ts:118">
P2: Avoid falling back to a placeholder Convex URL. This masks misconfiguration and makes the client connect to an invalid endpoint instead of failing fast with a clear error. Prefer an explicit env check like the server handler uses.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 19

Note

Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
src/app/showcase/page.tsx (1)

128-131: CTA buttons lack navigation or click handlers.

The following buttons don't have any functionality:

  • "Submit Your Project" (line 128-131)
  • "Start Building" (lines 206-208, 249-251)
  • "Join Community" (lines 253-256)

Consider adding:

  • href props to convert to Link components for navigation
  • onClick handlers to trigger modals or actions
  • Or wrap with <Link> if these should navigate to specific routes
Example implementation
-          <Button size="lg" className="gap-2">
+          <Link href="/submit">
+            <Button size="lg" className="gap-2">
             <Star className="h-5 w-5" />
             Submit Your Project
+            </Button>
+          </Link>
-          </Button>

Also applies to: 206-208, 249-256

ROADMAP.md (1)

1-179: Move ROADMAP.md under /explanations.
Docs placement rules require markdown documentation to live in /explanations (except README/CLAUDE). Consider relocating this file or adding an explicit exception. As per coding guidelines, ...

convex/oauth.ts (1)

88-102: getConnection returns encrypted tokens to callers.

The getConnection query returns the raw database record containing encrypted tokens. Callers using this query (like the domains route) may expect decrypted tokens.

This could cause silent failures where encrypted tokens are used as-is against APIs. Consider either:

  1. Decrypting in getConnection (but this exposes tokens in query results)
  2. Documenting clearly that tokens are encrypted
  3. Creating separate queries for token retrieval vs. connection metadata
#!/bin/bash
# Description: Find all usages of api.oauth.getConnection to verify they handle encrypted tokens

rg -n "getConnection" --type ts -B 2 -A 5 | rg -A 7 "oauth"
🤖 Fix all issues with AI agents
In `@convex/oauth.ts`:
- Around line 14-21: encryptToken currently derives the AES key by slicing a
UTF-8 string which can yield wrong byte lengths and low entropy; instead,
require ENCRYPTION_KEY to be a 64-character hex string (32 bytes) or derive a
32-byte key via a proper KDF and validate its length. Replace the current key
construction in encryptToken (where Buffer.from(ENCRYPTION_KEY.slice(0, 32),
"utf8") is used) with a validated Buffer.from(ENCRYPTION_KEY, "hex") and throw
an explicit error if keyBuffer.length !== 32; ensure the same validated key
handling is used wherever ALGORITHM/ENCRYPTION_KEY are consumed so encryption
and decryption remain compatible.
- Around line 8-11: Module-level throw for ENCRYPTION_KEY prevents Convex from
initializing; change to defer validation to runtime by replacing the top-level
ENCRYPTION_KEY constant with a function (e.g., getEncryptionKey()) that reads
process.env.OAUTH_ENCRYPTION_KEY, trims/validates it, and throws only when the
value is actually required, or return undefined and let callers handle the
missing key; update all places using ENCRYPTION_KEY to call getEncryptionKey()
(or handle undefined) so the module no longer throws during load.

In `@env.example`:
- Around line 30-38: convex/oauth.ts requires an OAUTH_ENCRYPTION_KEY env var
and currently throws if it is missing, so add a new entry
OAUTH_ENCRYPTION_KEY="" to env.example with a short comment instructing
developers to generate a strong 32+ character key (e.g., use a secure random
generator or openssl/uuid) and note its purpose for encrypting OAuth tokens;
ensure the variable name matches exactly what convex/oauth.ts reads and include
guidance to keep it secret and consistent across deployments.

In `@geo-implementation-prompt.md`:
- Line 1: Move the markdown file geo-implementation-prompt.md into the
explanations/ directory to comply with project documentation rules; rename/move
the file path to explanations/geo-implementation-prompt.md and update any
references or links in the repo (imports, README, docs index) that point to the
old location so they reference explanations/geo-implementation-prompt.md
instead.

In `@public/robots.txt`:
- Line 20: The directive "Disallow: *.json" is invalid for robots.txt; replace
it by either enumerating the specific JSON-containing paths to disallow (e.g.,
list each /path/to/file.json or directory prefixes) or, if you only care about
Googlebot-style pattern matching, use the supported Google-specific pattern
"Disallow: /*.json$"; remove the invalid "Disallow: *.json" line if you cannot
reliably list paths or rely on non-standard patterns.
- Around line 22-78: Per-agent User-agent blocks (e.g., "User-agent: GPTBot",
"User-agent: ClaudeBot", etc.) only disallow "/api/" and "/projects/" and
therefore override the global User-agent: * rules, unintentionally permitting
access to sensitive paths; fix by either removing these per-agent blocks so the
global rules apply, or update each per-agent block to include all global
disallowed paths (add "/_next/", "/admin/", "/monitoring", "/.well-known/"
alongside the existing "/api/" and "/projects/") and apply this change to every
User-agent block in the file.

In `@src/agents/claude-code-tools.ts`:
- Around line 115-213: The tools write_files and read_files accept unvalidated
paths and can escape the sandbox; update both to call validateAndSanitizePath
for each input path (instead of relying only on isValidFilePath) before any IO:
in write_files (the execute handler for write_files) validate and sanitize each
file.path with validateAndSanitizePath(sandbox, path) and skip/collect invalid
ones before building filesToWrite and calling writeFilesBatch; in read_files
(the execute handler for read_files) validate each path with
validateAndSanitizePath(sandbox, path) and only call readFileFast on sanitized
paths, returning errors for invalid inputs. Keep existing usage of getSandbox,
readFileFast, writeFilesBatch, updateFiles, and onFileCreated; ensure error
responses include invalidPaths and preserve current JSON response shapes.

In `@src/agents/client.ts`:
- Around line 24-43: The current OAuth flow is incorrect:
createClaudeCodeClientWithToken and createAnthropicProviderWithToken set apiKey
(which maps to x-api-key) instead of sending an Authorization: Bearer header
through your centralized OpenRouter gateway; also some Claude model IDs in
CLAUDE_CODE_MODEL_MAP are retired. Fix by routing Claude Code requests through
your OpenRouter client (use the existing OpenRouter client factory instead of
instantiating Anthropic directly), ensure the gateway request uses headers: {
Authorization: `Bearer ${accessToken}` } (not apiKey), remove passing
accessToken into apiKey, and replace retired model IDs (e.g.,
claude-3-5-sonnet-20241022 and claude-3-opus-20240229) with their *-latest
aliases (e.g., claude-3-5-sonnet-latest) or current snapshot IDs; update
createClaudeCodeClientWithToken/createAnthropicProviderWithToken to return or
configure the OpenRouter-based client and keep isClaudeCodeFeatureEnabled
unchanged.

In `@src/agents/code-agent.ts`:
- Around line 446-463: The code is calling
internal.oauth.getAnthropicAccessToken via convex.query from the agent (using a
browser client) which can't access internal Convex functions; replace that call
with the public action getAnthropicAccessTokenForCurrentUser (from
convex/oauth.ts) or otherwise obtain the token from the Inngest function context
and pass it into the agent. Specifically, in the isClaudeCodeModel branch where
you currently call convex.query(internal.oauth.getAnthropicAccessToken, {
userId: project.userId }), switch to invoking
getAnthropicAccessTokenForCurrentUser (or use the passed-in token) to retrieve
userAnthropicToken so the agent uses an authenticated, public action instead of
internal.oauth.getAnthropicAccessToken.

In `@src/app/api/deploy/netlify/auth/route.ts`:
- Around line 5-7: Remove the hard-coded fallback for NETLIFY_OAUTH_STATE_SECRET
and make the code fail fast if the environment variable is not set: replace the
current declaration that defaults to "fallback-secret-change-me" with logic that
throws or returns an error when NETLIFY_OAUTH_STATE_SECRET is undefined
(consistent with the existing callback handler behavior). Update any
initialization or export that uses NETLIFY_OAUTH_STATE_SECRET (referencing the
NETLIFY_OAUTH_STATE_SECRET symbol) so callers cannot proceed without a real
secret, and ensure the process logs an explanatory error and exits or returns a
500 response when the variable is missing.

In `@src/app/compare/`[slug]/page.tsx:
- Around line 253-262: The CTA Button is inert and the ArrowRight icon uses the
wrong sizing props; wrap the Button with a next/link Link (import Link from
'next/link') and pass asChild to the Button component so it becomes a clickable
link to the intended route (set Link's href to the desired destination), and
replace the ArrowRight class-based sizing (className="h-5 w-5") with the
lucide-react size prop (e.g., size={16}) to match the size-4 guideline; update
JSX for the Button component usage (Button, ArrowRight, Link references)
accordingly.

In `@src/app/compare/page.tsx`:
- Around line 6-7: The JSX currently nests a native <button> inside a <Link> (an
<a>), which is invalid — replace the nested <button> with the Shadcn Button
component used as a child of Link (i.e., import Button from your shadcn/ui and
use <Button asChild><Link ...>...</Link></Button> or vice‑versa depending on
your Link wrapper) so the rendered element is an anchor without a nested button;
update both occurrences (the block around
Card/CardContent/CardHeader/CardTitle/CardDescription with ArrowRight and the
similar block at lines referenced 130-133) to use the Button asChild pattern and
ensure accessibility and styling remain intact while keeping ArrowRight and Card
components unchanged.

In `@src/lib/netlify-config.ts`:
- Around line 24-27: The ANGULAR block currently sets publishDir to "dist",
which breaks Angular 17+ outputs; update the ANGULAR.publishDir to point to the
new default output "dist/<projectName>/browser" (or make publishDir configurable
from project settings) and ensure buildCommand remains "bun run build";
alternatively add documentation in the ANGULAR block comment explaining that
users must set their angular.json outputPath if they wish a different
publishDir.

In `@src/lib/payment-provider.ts`:
- Around line 204-231: The request<T> helper has two problems: it casts null as
T for 204 responses and lacks a request timeout; replace this with an
AbortController-based timeout (use a configurable default like 10s, attach
controller.signal to fetch, clear the timer, and throw a clear timeout error on
abort) and stop returning `null as T` — either change request<T> to return
Promise<T | undefined> for endpoints that may be 204 or add a dedicated method
(e.g., requestNoContent or requestVoid) for 204 responses; update callers like
createCheckoutSession and updateSubscription to handle undefined or use the new
no-content method accordingly.

In `@src/lib/payment-templates/angular.ts`:
- Around line 7-265: The template literal for "server/routes/billing.ts"
contains unescaped `${encodeURIComponent(...)}'` occurrences which are being
interpreted by the outer template in payment-templates/angular.ts; fix by
escaping the inner template interpolations and any backticks inside that inner
file string: replace `${` with `\${` for the two uses of encodeURIComponent
(used in the router.patch and router.delete request paths) and escape any
backticks within the billing file content so the outer template string remains
valid (look for encodeURIComponent(...) and autumn.request(...) usages in the
billing template).

In `@src/lib/payment-templates/nextjs.ts`:
- Around line 368-372: The code currently calls response.json() twice (once
inside the !response.ok branch and again afterwards), consuming the body stream
and causing an error; fix by awaiting response.json() once into a local variable
(e.g., const data = await response.json()), then check response.ok and throw
using data.error || "Checkout failed" when not ok, otherwise reuse that same
data object for the success path (replace the duplicate response.json() calls in
the function where response and data are used).

In `@src/lib/payment-templates/react.ts`:
- Around line 117-146: The template backticks inside string literals are
breaking TS parsing; locate the autumn.request calls (e.g., the PATCH call using
`/v1/subscriptions/${encodeURIComponent(subscriptionId)}` and the POST cancel
call `/v1/subscriptions/${encodeURIComponent(subscriptionId)}/cancel`) and
replace those inner template literals with safe string construction (either
escape the backticks or switch to single-quoted concatenation like
'/v1/subscriptions/' + encodeURIComponent(subscriptionId) + '/cancel' or
'/v1/subscriptions/' + encodeURIComponent(subscriptionId)); update all
occurrences in this file so no unescaped backticks remain inside outer
backtick-delimited strings.

In `@src/lib/payment-templates/vue.ts`:
- Around line 107-143: The PATCH and DELETE /subscription route handlers
(router.patch("/subscription", ...) and router.delete("/subscription", ...))
lack try/catch around the autumn.request calls so thrown errors are unhandled;
wrap each handler's async body in a try/catch, call autumn.request inside the
try, and in the catch send a consistent JSON error response (e.g.,
res.status(500).json({ error: String(err) }) or include err.message) and
optionally log the error before returning so failures from autumn.request are
handled safely.
🟡 Minor comments (25)
public/llms.txt-1-4 (1)

1-4: Align brand capitalization with existing UI copy (ZapDev).

The file uses “Zapdev” while the design system copy calls it “ZapDev.” Consider standardizing capitalization for consistency across public-facing content.

src/lib/database-templates/convex/shared.ts-41-41 (1)

41-41: Inconsistent environment variable naming between templates.

convexAuth (line 41) uses SITE_URL while convexAuthClient (line 79) uses NEXT_PUBLIC_SITE_URL. Both serve the same purpose but the naming inconsistency could cause configuration issues. Consider aligning on NEXT_PUBLIC_SITE_URL since it needs to be available on the client.

Also applies to: 79-79

src/lib/netlify-client.ts-43-49 (1)

43-49: Unsafe type assertion when response body is empty.

Returning {} as T when the response is empty can cause runtime errors if the caller expects specific properties on T. Consider returning null and updating the return type, or throw an error for unexpected empty responses.

Proposed fix
-const parseJson = async <T>(response: Response): Promise<T> => {
+const parseJson = async <T>(response: Response): Promise<T | null> => {
   const text = await response.text();
   if (!text) {
-    return {} as T;
+    return null;
   }
   return JSON.parse(text) as T;
 };

Then update callers to handle the null case appropriately, or use a different approach for methods that expect no response body (like DELETE operations).

src/app/api/projects/[projectId]/export/github/route.ts-115-125 (1)

115-125: projectId is fetched but not used in GET handler.

The projectId is extracted from params but never used for authorization or filtering. Consider either:

  1. Validating that the export belongs to the specified project for security
  2. Removing the unused variable if the exportId alone is sufficient
Proposed fix: Add project validation
     const { projectId } = await params;
     const { searchParams } = new URL(request.url);
     const exportId = searchParams.get("exportId");

     if (!exportId) {
       return NextResponse.json({ error: "Missing exportId" }, { status: 400 });
     }

     const record = await fetchQuery(api.githubExports.get, {
       exportId: exportId as Id<"githubExports">,
     });

-    if (!record) {
+    if (!record || record.projectId !== projectId) {
       return NextResponse.json({ error: "Export not found" }, { status: 404 });
     }
src/lib/database-templates/convex/shared.ts-111-120 (1)

111-120: Placeholder URL fallback may cause silent failures.

The ConvexReactClient uses "https://placeholder.convex.cloud" as a fallback, which will silently fail at runtime if the environment variable is missing. This is inconsistent with convexAuthHandler (lines 91-96) which throws an explicit error.

Proposed fix for consistency
 export const convexProvider = \`"use client";

 import { ConvexReactClient } from "convex/react";
 import { ConvexBetterAuthProvider } from "@convex-dev/better-auth/react";
 import { authClient } from "@/lib/auth-client";
 import type { ReactNode } from "react";

+const convexUrl = process.env.NEXT_PUBLIC_CONVEX_URL;
+if (!convexUrl) {
+  throw new Error("NEXT_PUBLIC_CONVEX_URL environment variable is required.");
+}
+
 const convex = new ConvexReactClient(
-  process.env.NEXT_PUBLIC_CONVEX_URL || "https://placeholder.convex.cloud"
+  convexUrl
 );
src/app/rss.xml/route.ts-32-42 (1)

32-42: Avoid a hard-coded blog pubDate that will go stale.

A fixed 2025 date will look outdated in 2026 and can suppress updates in feed readers. Consider sourcing the publish/updated date from blog metadata (or at least a constant aligned with the actual post) and, if possible, linking to a specific post URL instead of the blog index.

src/modules/projects/ui/views/project-view.tsx-46-46 (1)

46-46: Remove the as type assertion on line 123 and use a type guard instead.

The as assertion in onValueChange={(value) => setTabState(value as "preview" | "code" | "deploy")} bypasses type safety. Line 46's type annotation is already correct; extract the union type and validate with a type guard:

Suggested fix
+  type TabState = "preview" | "code" | "deploy";
+  const isTabState = (value: string): value is TabState =>
+    value === "preview" || value === "code" || value === "deploy";
+
-            onValueChange={(value) => setTabState(value as "preview" | "code" | "deploy")}
+            onValueChange={(value) => {
+              if (isTabState(value)) setTabState(value);
+            }}
src/modules/projects/ui/components/deployment-dashboard.tsx-1-15 (1)

1-15: Missing "use client" directive for client component.

This component uses useQuery from Convex React, which requires client-side rendering. In Next.js App Router, components using React hooks must be marked as client components.

🔧 Proposed fix
+"use client";
+
 import { useQuery } from "convex/react";
 import { api } from "@/convex/_generated/api";
src/modules/projects/ui/components/deploy-button.tsx-40-42 (1)

40-42: Handle loading state separately from missing connection.

The useQuery hook returns undefined while loading. The current check if (!connection) treats both loading and "no connection" states the same, causing the NetlifyConnectDialog to flash briefly before the actual button appears once the query resolves.

🐛 Proposed fix
+  // Show nothing while loading to prevent flash
+  if (connection === undefined) {
+    return null;
+  }
+
   if (!connection) {
     return <NetlifyConnectDialog />;
   }

Alternatively, show a loading skeleton:

+  if (connection === undefined) {
+    return <Button size="sm" disabled className="animate-pulse">Loading...</Button>;
+  }
+
   if (!connection) {
     return <NetlifyConnectDialog />;
   }
src/app/api/deploy/netlify/sites/route.ts-35-38 (1)

35-38: Differentiate error status codes based on error type.

When getNetlifyAccessToken throws "Netlify connection not found", the response should be 401 (unauthorized) rather than 500. Currently all errors return 500, which misrepresents authentication failures as server errors.

Proposed fix
   } catch (error) {
     const message = error instanceof Error ? error.message : "Failed to fetch sites";
+    const status = message.includes("connection not found") ? 401 : 500;
-    return NextResponse.json({ error: message }, { status: 500 });
+    return NextResponse.json({ error: message }, { status });
   }
src/modules/projects/ui/components/deployment-history.tsx-33-34 (1)

33-34: URL-encode the deployId parameter.

The deployId is interpolated directly into the URL without encoding. If the ID contains special characters, this could cause issues.

Proposed fix
-      const response = await fetch(`/api/deploy/netlify/logs?deployId=${deployId}`);
+      const response = await fetch(`/api/deploy/netlify/logs?deployId=${encodeURIComponent(deployId)}`);
src/lib/payment-templates/nextjs.ts-41-43 (1)

41-43: Unsafe type assertion for 204 responses.

Returning undefined as T is unsafe when the generic type T doesn't include undefined. This could cause runtime issues if callers expect a defined response.

Suggested fix with explicit undefined handling
   if (response.status === 204) {
-    return undefined as T;
+    return undefined as unknown as T;
   }

Alternatively, consider changing the return type to Promise<T | undefined> and updating callers to handle the undefined case explicitly.

src/inngest/functions/code-agent.ts-5-12 (1)

5-12: Reduce Inngest retries to 0; agent has internal retry loops for transient failures.

The agent's internal retry mechanisms (MAX_STREAM_RETRIES=3 for LLM errors, AUTO_FIX_MAX_ATTEMPTS=1 for build/lint failures) already handle recoverable failures. Error events are yielded only when permanent failures occur (no files generated, no summary available after all retries), and Inngest's retries: 3 will not resolve these failures. Additionally, each Inngest retry creates a new sandbox and duplicate database records without providing any recovery benefit.

Set retries: 0 and rely on the agent's internal retry logic, since transient errors are already handled and permanent errors cannot be fixed by re-executing the entire function.

src/modules/projects/ui/components/custom-domain-dialog.tsx-36-88 (1)

36-88: Encode query params and trim the domain input.

siteId/domainId are interpolated into URLs without encoding, and whitespace-only domains can slip through. Encoding + trimming avoids malformed requests.

✅ Suggested fix
-      const response = await fetch(`/api/deploy/netlify/domains?siteId=${siteId}`);
+      const response = await fetch(
+        `/api/deploy/netlify/domains?siteId=${encodeURIComponent(siteId)}`
+      );
-    if (!domainInput || isSubmitting) {
-      if (!domainInput) {
+    const domain = domainInput.trim();
+    if (!domain || isSubmitting) {
+      if (!domain) {
         toast.error("Enter a domain");
       }
       return;
     }
-      const response = await fetch("/api/deploy/netlify/domains", {
+      const response = await fetch("/api/deploy/netlify/domains", {
         method: "POST",
         headers: { "Content-Type": "application/json" },
-        body: JSON.stringify({ siteId, domain: domainInput }),
+        body: JSON.stringify({ siteId, domain }),
       });
-      const response = await fetch(
-        `/api/deploy/netlify/domains?siteId=${siteId}&domainId=${domainId}`,
+      const response = await fetch(
+        `/api/deploy/netlify/domains?siteId=${encodeURIComponent(
+          siteId
+        )}&domainId=${encodeURIComponent(domainId)}`,
         { method: "DELETE" }
       );
src/app/api/deploy/netlify/deploy/route.ts-107-112 (1)

107-112: Change parseResult.error.errors to parseResult.error.issues for Zod v4 compatibility.

Zod v4 uses the .issues property on ZodError; the .errors property was removed in v4. The current code will result in undefined details in the error response.

🔧 Suggested change
-        { error: "Invalid request body", details: parseResult.error.errors },
+        { error: "Invalid request body", details: parseResult.error.issues },
convex/oauth.ts-23-32 (1)

23-32: Decryption lacks input validation.

If encryptedToken doesn't contain exactly two colons, the destructuring will fail silently or produce incorrect values. Add validation.

🔧 Suggested fix
 function decryptToken(encryptedToken: string): string {
   const [ivHex, authTagHex, encrypted] = encryptedToken.split(":");
+  if (!ivHex || !authTagHex || !encrypted) {
+    throw new Error("Invalid encrypted token format");
+  }
   const iv = Buffer.from(ivHex, "hex");
   const authTag = Buffer.from(authTagHex, "hex");
src/lib/payment-templates/svelte.ts-253-274 (1)

253-274: Indentation inconsistency and duplicate JSON parsing in CheckoutButton.svelte.

The startCheckout function has inconsistent indentation (extra spaces at Line 253). Also, when the response is not OK, the code parses JSON to get the error, but then parses it again on Line 265 even though the response was already consumed on Line 262.

🔧 Proposed fix
-    const startCheckout = async () => {
+  const startCheckout = async () => {
     loading = true;
     try {
       const response = await fetch("/api/billing/checkout", {
         method: "POST",
         headers: { "Content-Type": "application/json" },
         body: JSON.stringify({ productId, customerId, successUrl, cancelUrl }),
       });
+      const data = (await response.json()) as { url?: string; error?: string };
       if (!response.ok) {
-        const data = await response.json();
         throw new Error(data.error || "Checkout failed");
       }
-      const data = (await response.json()) as { url?: string };
       if (data.url) {
         window.location.href = data.url;
       }
src/lib/database-templates/drizzle-neon/nextjs.ts-77-81 (1)

77-81: Fix excessive escaping in middleware matcher regex.

The matcher pattern uses \\\\. (quadruple backslash) but should use \\. (double backslash) to correctly match file extensions. The double-backslash escaping is necessary in JavaScript strings to represent a regex literal dot \..

Change line 79 from:

"/((?!_next/static|_next/image|favicon.ico|.*\\\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)",

to:

"/((?!_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)",

This matches the correct escaping used in src/proxy.ts and aligns with Next.js documentation patterns.

.cursor/plans/add_inngest_as_agent_orchestration_layer_c89435a8.plan.md-4-22 (1)

4-22: Fix remark-lint front-matter list indentation and add trailing newline.
CI flags the indented todos list and missing final newline. Update the YAML list indentation (no extra two spaces) and ensure the file ends with a newline.

🔧 Example fix for the front-matter list
 todos:
-  - id: install-inngest
+ - id: install-inngest
    content: "Install Inngest packages: inngest and `@inngest/realtime`"
    status: completed

Also applies to: 203-208

.cursor/plans/add_inngest_as_agent_orchestration_layer_c89435a8.plan.md-1-3 (1)

1-3: Move plan markdown under /explanations (or document an exemption).
This file is a documentation-style Markdown file outside /explanations. If .cursor/plans is intentionally exempt, please note that explicitly; otherwise relocate it. As per coding guidelines, please keep Markdown docs under /explanations.

README.md-20-28 (1)

20-28: Update Inngest statement—it's still in dependencies, and migration is incomplete. Lines 20–27: Next.js 16 and React 19 are correct, but line 27 claims "Custom agent orchestration (replaces Inngest)," while package.json includes "inngest": "^3.49.3". According to the codebase learnings, the migration from Inngest to custom agents is in progress, not complete. Update line 27 to reflect the actual state: either reference Inngest explicitly or clarify that custom orchestration is being implemented alongside it.

geo-implementation-prompt.md-17-19 (1)

17-19: Tighten wording per lint feedback.

Use hyphenated compound adjectives and proper noun capitalization.

🔧 Suggested change
-### 1. Content Enhancement (High Impact Methods)
+### 1. Content Enhancement (High-impact Methods)

-**Deliverable:** Spreadsheet or markdown document listing:
+**Deliverable:** Spreadsheet or Markdown document listing:

-## What to Avoid (Low Impact Methods)
+## What to Avoid (Low-impact Methods)

Also applies to: 148-148, 304-306

geo-implementation-prompt.md-569-570 (1)

569-570: Avoid bare URLs in Markdown.

MD034 flags bare URLs; wrap them in angle brackets or use a proper link.

🔧 Suggested change
-- Schema.org documentation: https://schema.org/
+- Schema.org documentation: <https://schema.org/>
geo-implementation-prompt.md-427-446 (1)

427-446: Add a language to the fenced block.

markdownlint MD040 expects a language tag on fenced code blocks.

🔧 Suggested change
-```
+```text
geo-implementation-prompt.md-32-58 (1)

32-58: Escape placeholder brackets to avoid undefined reference links.

The [topic]-style placeholders are parsed as reference links; remark-lint flags them as undefined. Use backticks or escape brackets to keep them as placeholders.

🔧 Example fix (apply similarly throughout)
-- "What is [topic]?"
-- "How does [system] work?"
-- "Best [tool] for [use case]"
-+ "What is `topic`?"
-+ "How does `system` work?"
-+ "Best `tool` for `use case`"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant