Skip to content

mchen04/castor

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

117 Commits
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Castor - GEO Chatbot Platform

A premium Generative Engine Optimization (GEO) consultant chatbot platform with subscription-based access ($59.99/month). Castor helps businesses optimize their content for AI search engines like ChatGPT, Perplexity, and Google AI Overviews.

🌐 Live: trycastor.dev


πŸ—οΈ Architecture

Tech Stack:

  • Frontend: Next.js 15 (App Router), React 18, TypeScript, Tailwind CSS, shadcn/ui
  • State Management: TanStack React Query (server state + caching)
  • Authentication: Clerk (Google OAuth, GitHub OAuth, email/password)
  • Database: Supabase (PostgreSQL)
  • Payments: Stripe (Subscriptions)
  • AI Chatbot: Dify AI (API proxy with streaming SSE)
  • Hosting: Vercel
  • Styling: Tailwind CSS + shadcn/ui components

Key Features:

  • βœ… Multi-provider authentication via Clerk (Google, GitHub, email/password)
  • βœ… Subscription-based paywall ($59.99/month)
  • βœ… Stripe Checkout integration with webhook handling
  • βœ… AI chatbot with real-time streaming and conversation persistence
  • βœ… Rate limiting (30 requests/minute per user)
  • βœ… Payment grace period for failed recurring payments
  • βœ… Stripe Customer Portal for self-service billing
  • βœ… Fully responsive landing page with SEO optimization
  • βœ… Server-side payment verification

πŸ“‚ Project Structure

castor-1/
β”œβ”€β”€ frontend/                       # Next.js application
β”‚   β”œβ”€β”€ app/                        # Next.js App Router
β”‚   β”‚   β”œβ”€β”€ page.tsx               # Landing page
β”‚   β”‚   β”œβ”€β”€ layout.tsx             # Root layout with Clerk provider
β”‚   β”‚   β”œβ”€β”€ providers.tsx          # TanStack Query provider
β”‚   β”‚   β”œβ”€β”€ loading.tsx            # Global loading state
β”‚   β”‚   β”œβ”€β”€ sign-in/               # Clerk sign-in page
β”‚   β”‚   β”œβ”€β”€ sign-up/               # Clerk sign-up page
β”‚   β”‚   β”œβ”€β”€ dashboard/             # Protected dashboard (paywall + chatbot)
β”‚   β”‚   └── api/                   # API routes
β”‚   β”‚       β”œβ”€β”€ checkout/          # Stripe Checkout session creation
β”‚   β”‚       β”œβ”€β”€ customer-portal/   # Stripe Customer Portal session
β”‚   β”‚       β”œβ”€β”€ verify-checkout/   # Verify checkout completion
β”‚   β”‚       β”œβ”€β”€ dify/              # Dify AI proxy endpoints
β”‚   β”‚       β”‚   β”œβ”€β”€ chat/          # Stream chat messages
β”‚   β”‚       β”‚   β”œβ”€β”€ conversations/ # List conversations
β”‚   β”‚       β”‚   β”œβ”€β”€ messages/      # Get conversation messages
β”‚   β”‚       β”‚   β”œβ”€β”€ delete/        # Delete conversation
β”‚   β”‚       β”‚   β”œβ”€β”€ rename/        # Rename conversation
β”‚   β”‚       β”‚   β”œβ”€β”€ stop/          # Stop generation
β”‚   β”‚       β”‚   └── generate-title/ # Generate conversation title
β”‚   β”‚       β”œβ”€β”€ generate-title/    # OpenRouter title generation
β”‚   β”‚       └── webhook/stripe/    # Stripe webhook handler
β”‚   β”œβ”€β”€ components/                # React components
β”‚   β”‚   β”œβ”€β”€ ui/                    # shadcn/ui components
β”‚   β”‚   β”œβ”€β”€ landing/               # Landing page sections
β”‚   β”‚   β”‚   β”œβ”€β”€ hero-section.tsx
β”‚   β”‚   β”‚   β”œβ”€β”€ value-proposition-section.tsx
β”‚   β”‚   β”‚   β”œβ”€β”€ services-section.tsx
β”‚   β”‚   β”‚   β”œβ”€β”€ testimonials-section.tsx
β”‚   β”‚   β”‚   β”œβ”€β”€ final-cta-section.tsx
β”‚   β”‚   β”‚   └── chat-preview.tsx
β”‚   β”‚   β”œβ”€β”€ assistant-ui/          # Chat interface components
β”‚   β”‚   β”‚   β”œβ”€β”€ thread.tsx         # Conversation view
β”‚   β”‚   β”‚   β”œβ”€β”€ threadlist-sidebar.tsx # Conversation list
β”‚   β”‚   β”‚   └── sidebar/           # Sidebar subcomponents
β”‚   β”‚   β”œβ”€β”€ assistant-ui-dashboard.tsx # Main chat dashboard
β”‚   β”‚   β”œβ”€β”€ header.tsx             # Site header with navigation
β”‚   β”‚   β”œβ”€β”€ footer.tsx             # Site footer
β”‚   β”‚   β”œβ”€β”€ paywall-card.tsx       # Subscription paywall
β”‚   β”‚   β”œβ”€β”€ grace-period-warning.tsx # Payment failure warning
β”‚   β”‚   β”œβ”€β”€ checkout-verifier.tsx  # Verify checkout completion
β”‚   β”‚   └── user-menu.tsx          # User menu with billing link
β”‚   β”œβ”€β”€ lib/                       # Utilities and helpers
β”‚   β”‚   β”œβ”€β”€ auth/
β”‚   β”‚   β”‚   └── verify-subscription.ts # Subscription verification
β”‚   β”‚   β”œβ”€β”€ dify/
β”‚   β”‚   β”‚   β”œβ”€β”€ client.ts          # Centralized Dify API client
β”‚   β”‚   β”‚   └── errors.ts          # Error handling utilities
β”‚   β”‚   β”œβ”€β”€ hooks/
β”‚   β”‚   β”‚   β”œβ”€β”€ use-dify-chat.ts   # Chat state & streaming
β”‚   β”‚   β”‚   └── use-conversations.ts # Conversation management
β”‚   β”‚   β”œβ”€β”€ middleware/
β”‚   β”‚   β”‚   └── rate-limit.ts      # In-memory rate limiting
β”‚   β”‚   β”œβ”€β”€ openrouter/
β”‚   β”‚   β”‚   └── title-generator.ts # AI title generation
β”‚   β”‚   β”œβ”€β”€ supabase/
β”‚   β”‚   β”‚   β”œβ”€β”€ server.ts          # Server-side Supabase client
β”‚   β”‚   β”‚   └── admin.ts           # Admin Supabase client
β”‚   β”‚   β”œβ”€β”€ content/
β”‚   β”‚   β”‚   └── landing-page-content.ts  # Landing page content
β”‚   β”‚   β”œβ”€β”€ types.ts               # TypeScript type definitions
β”‚   β”‚   └── utils.ts               # Utility functions (cn, etc.)
β”‚   β”œβ”€β”€ middleware.ts              # Clerk authentication middleware
β”‚   β”œβ”€β”€ .env.local                 # Local environment variables
β”‚   └── .env.production            # Production env reference
└── CLAUDE.md                      # AI agent instructions

πŸš€ Getting Started

Prerequisites

  • Node.js 22.x
  • npm or pnpm
  • Clerk account
  • Supabase project
  • Stripe account
  • Dify AI chatbot instance

Installation

  1. Clone the repository:

    git clone https://github.com/mchen04/castor.git
    cd castor-1/frontend
  2. Install dependencies:

    npm install
  3. Set up environment variables:

    Copy .env.example to .env.local and fill in your credentials:

    cp .env.example .env.local

    Required variables:

    # Supabase
    NEXT_PUBLIC_SUPABASE_URL=your-supabase-url
    NEXT_PUBLIC_SUPABASE_ANON_KEY=your-supabase-anon-key
    SUPABASE_SERVICE_ROLE_KEY=your-service-role-key
    
    # Stripe
    NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=your-stripe-publishable-key
    STRIPE_SECRET_KEY=your-stripe-secret-key
    STRIPE_WEBHOOK_SECRET=your-stripe-webhook-secret
    STRIPE_PRICE_ID=your-stripe-price-id
    
    # Clerk
    NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=your-clerk-publishable-key
    CLERK_SECRET_KEY=your-clerk-secret-key
    NEXT_PUBLIC_CLERK_SIGN_IN_URL=/sign-in
    NEXT_PUBLIC_CLERK_SIGN_UP_URL=/sign-up
    NEXT_PUBLIC_CLERK_SIGN_IN_FALLBACK_REDIRECT_URL=/dashboard
    NEXT_PUBLIC_CLERK_SIGN_UP_FALLBACK_REDIRECT_URL=/dashboard
    
    # Dify AI
    DIFY_API_KEY=your-dify-api-key
    
    # OpenRouter (for title generation)
    OPENROUTER_API_KEY=your-openrouter-api-key
    
    # App
    NEXT_PUBLIC_SITE_URL=http://localhost:3000
  4. Run development server:

    npm run dev

    Visit http://localhost:3000


πŸ—„οΈ Database Schema

Supabase Tables:

users

- id: uuid (primary key, generated)
- clerk_id: text (unique, NOT NULL - Clerk user identifier)
- email: text
- has_paid: boolean (default: false)
- stripe_customer_id: text (unique)
- stripe_subscription_id: text (unique)
- stripe_subscription_status: text (active, trialing, past_due, canceled, unpaid, etc.)
- stripe_subscription_current_period_end: timestamptz
- stripe_subscription_cancel_at_period_end: boolean
- in_grace_period: boolean (default: false)
- created_at: timestamptz
- updated_at: timestamptz (auto-updated)

processed_events (webhook idempotency)

- id: uuid (primary key)
- event_id: text (unique, Stripe event ID)
- created_at: timestamp

πŸ’³ Payment Flow

Subscription Flow

  1. User signs in with Clerk (Google/GitHub OAuth or email/password)
  2. Clerk redirects to Dashboard
  3. Dashboard checks payment status from Supabase users table (by clerk_id)
  4. If not subscribed β†’ PaywallCard component shows Stripe Checkout button
  5. User clicks "Subscribe" β†’ /api/checkout creates Stripe Checkout session
  6. User completes payment on Stripe
  7. Stripe sends checkout.session.completed webhook β†’ /api/webhook/stripe
  8. Webhook handler:
    • Verifies signature
    • Checks idempotency (processed_events table)
    • Updates users table with subscription metadata
  9. User returns to dashboard β†’ sees chatbot

Recurring Payments

  • Success: invoice.payment_succeeded webhook clears grace period
  • Failure: invoice.payment_failed webhook sets grace period flag
  • Grace Period: User sees warning banner but maintains access during Stripe's retry window

Cancellation

  • User clicks "Manage Billing" β†’ Stripe Customer Portal
  • Cancels subscription β†’ customer.subscription.deleted webhook revokes access

πŸ€– Dify AI Integration

The Dify AI chatbot is integrated via API proxy pattern using Vercel Serverless Functions:

Key Features:

  • API Proxy: All Dify API calls routed through /api/dify/* endpoints
  • Authentication: Clerk user verification + Supabase subscription check
  • User Identification: Clerk user ID passed to Dify API for conversation persistence
  • Rate Limiting: 30 requests/minute per user
  • Streaming Chat: Real-time responses via Server-Sent Events (SSE)
  • Error Handling: User-friendly error messages with retry logic
  • Title Generation: AI-powered conversation titles via OpenRouter

Architecture:

Client β†’ /api/dify/chat β†’ Clerk Auth β†’ Subscription Check β†’ Dify API β†’ SSE Stream β†’ Client

API Endpoints:

  • POST /api/dify/chat - Send chat message (streaming)
  • GET /api/dify/conversations - List conversations
  • GET /api/dify/messages - Get conversation messages
  • POST /api/dify/delete - Delete conversation
  • POST /api/dify/rename - Rename conversation
  • POST /api/dify/stop - Stop generation
  • POST /api/generate-title - Generate conversation title

πŸ” Authentication & Authorization

Authentication: Clerk handles all auth (Google OAuth, GitHub OAuth, email/password) Authorization: Middleware + server-side payment checks

Middleware (middleware.ts)

  • Protects routes except: /, /sign-in, /sign-up, /api/webhook
  • Uses Clerk's clerkMiddleware for session management
  • Automatic session refresh and CSRF protection

Payment Verification

  • Client-side: Dashboard reads user data from Supabase
  • Server-side: API routes verify payment status before granting access
  • Webhook: Stripe webhooks update payment status in real-time

πŸ“‘ API Routes

Payment Routes

POST /api/checkout Creates Stripe Checkout session for new subscriptions.

POST /api/customer-portal Creates Stripe Customer Portal session for billing management.

GET /api/verify-checkout Verifies checkout session completion.

POST /api/webhook/stripe Handles Stripe webhook events with signature verification and idempotency.

Dify AI Proxy Routes

POST /api/dify/chat Send chat message with streaming SSE response. Requires active subscription. Rate limited to 30 req/min.

GET /api/dify/conversations List user's conversations with pagination.

GET /api/dify/messages Get messages from specific conversation.

POST /api/dify/delete Delete a conversation.

POST /api/dify/rename Rename a conversation.

POST /api/dify/stop Stop generating a streaming response.

POST /api/dify/generate-title Auto-generate conversation title.

Title Generation

POST /api/generate-title Generate conversation title using OpenRouter AI.


🎨 UI Components

Built with shadcn/ui and custom components:

Landing Page Components

  • HeroSection - Hero with CTA
  • ValuePropositionSection - Value proposition with feature highlights
  • ServicesSection - Feature cards
  • TestimonialsSection - Customer testimonials
  • FinalCTASection - Bottom CTA
  • ChatPreview - Interactive chat preview demo

Dashboard Components

  • PaywallCard - Subscription paywall
  • AssistantUIDashboard - Main chat dashboard orchestrator
  • Thread - Conversation message view
  • ThreadlistSidebar - Conversation list with sidebar navigation
  • GracePeriodWarning - Payment failure alert
  • UserMenu - User dropdown with billing link
  • CheckoutVerifier - Verify checkout completion

UI Primitives (shadcn/ui)

  • Button, Card, Dialog, Dropdown, Input, Select, Tabs, Toast, etc.

πŸ§ͺ Testing & Quality Checks

Type Checking:

npm run type-check

Linting:

npm run lint

Unused Code Detection:

npm run check:unused    # Detect unused code and dependencies
npm run fix:unused      # Auto-fix detected unused code

Run All Checks:

npm run check:all       # Type + Lint + Unused detection

Build:

npm run build

🚒 Deployment

Platform: Vercel

Environment Variables (Vercel Dashboard)

Add all variables from .env.production to Vercel:

  • Settings β†’ Environment Variables β†’ Add each variable

Stripe Webhooks

  1. Add webhook endpoint in Stripe Dashboard:

    https://trycastor.dev/api/webhook/stripe
    
  2. Subscribe to events:

    • checkout.session.completed
    • customer.subscription.updated
    • customer.subscription.deleted
    • invoice.payment_succeeded
    • invoice.payment_failed
  3. Copy webhook signing secret to STRIPE_WEBHOOK_SECRET in Vercel

Deploy

git push origin main

Vercel auto-deploys on push to main branch.


πŸ“ Development Guidelines

Code Style

  • TypeScript: Strict mode, explicit types
  • Components: PascalCase files, kebab-case for multi-word
  • Functions: camelCase
  • Constants: UPPER_SNAKE_CASE
  • Files: Keep under 350 lines, split if needed

Security

  • Never commit secrets (use .env.local)
  • Validate all webhook signatures
  • Use Row Level Security (RLS) in Supabase
  • Server-side payment verification only

Git Workflow

  • main branch for production
  • Feature branches for development
  • Vercel previews for pull requests

πŸ› Troubleshooting

Common Issues

Issue: Clerk redirects to /login (404)

  • Fix: Ensure NEXT_PUBLIC_CLERK_SIGN_IN_FALLBACK_REDIRECT_URL=/dashboard is set in Vercel

Issue: Stripe webhook not working

  • Fix: Check webhook signature secret, verify endpoint URL in Stripe Dashboard

Issue: Chatbot not loading

  • Fix: Verify user has active subscription, check Dify URL and user_id encoding

Issue: Payment status not updating

  • Fix: Check Supabase webhook logs, verify processed_events table for duplicates

πŸ“„ License

Proprietary - All rights reserved


πŸ‘€ Author

Michael Chen


πŸ™ Acknowledgments

About

Subscription-based GEO chatbot platform helping businesses optimize content for AI search engines like ChatGPT and Perplexity.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors