Skip to content

Latest commit

 

History

History
794 lines (580 loc) · 16.9 KB

File metadata and controls

794 lines (580 loc) · 16.9 KB

Deploy OpenCodeHub to Vercel

Complete guide to deploying OpenCodeHub on Vercel with Turso, Google Drive, and Redis

This guide walks you through deploying OpenCodeHub to Vercel using:

  • Turso - Serverless SQLite database (edge-compatible)
  • Google Drive API - File storage for repositories
  • Upstash Redis - Serverless Redis for sessions and caching

Estimated time: 30-45 minutes
Cost: Free tier available for all services


📋 Table of Contents

  1. Prerequisites
  2. Setup Turso Database
  3. Setup Google Drive Storage
  4. Setup Upstash Redis
  5. Configure Project
  6. Deploy to Vercel
  7. Post-Deployment
  8. Troubleshooting

Prerequisites

Before starting, ensure you have:


1. Setup Turso Database

Turso provides edge-compatible, serverless SQLite databases perfect for Vercel.

1.1. Install Turso CLI

# macOS/Linux
curl -sSfL https://get.tur.so/install.sh | bash

# Windows (PowerShell)
irm get.tur.so/install.ps1 | iex

# Verify installation
turso --version

1.2. Login to Turso

turso auth login

This opens your browser to authenticate.

1.3. Create Database

# Create database
turso db create opencodehub

# Create auth token
turso db tokens create opencodehub

# Get database URL
turso db show opencodehub --url

Save these values:

TURSO_DATABASE_URL=libsql://opencodehub-[your-org].turso.io
TURSO_AUTH_TOKEN=eyJhbGciOiJ...

1.4. Initialize Database Schema

# Download schema
curl -O https://raw.githubusercontent.com/swadhinbiswas/OpencodeHub/main/drizzle/schema.sql

# Apply schema
turso db shell opencodehub < schema.sql

# Verify tables
turso db shell opencodehub "SELECT name FROM sqlite_master WHERE type='table';"

Expected output:

users
repositories
pull_requests
pr_stacks
merge_queue_items
ai_reviews
...

2. Setup Google Drive Storage

Google Drive API will store Git repositories and LFS files.

2.1. Create Google Cloud Project

  1. Go to Google Cloud Console
  2. Click Select a projectNew Project
  3. Name: opencodehub-storage
  4. Click Create

2.2. Enable Google Drive API

  1. In the Cloud Console, go to APIs & ServicesEnable APIs
  2. Search for "Google Drive API"
  3. Click Enable

2.3. Create Service Account

  1. Go to APIs & ServicesCredentials
  2. Click Create CredentialsService Account
  3. Name: opencodehub-storage-sa
  4. Click Create and Continue
  5. Grant role: Editor
  6. Click Done

2.4. Generate Service Account Key

  1. Click on the service account you just created
  2. Go to Keys tab
  3. Click Add KeyCreate new key
  4. Choose JSON
  5. Click Create (downloads opencodehub-storage-xxxxx.json)

2.5. Create Google Drive Folder

  1. Go to Google Drive
  2. Create a new folder: OpenCodeHub-Repositories
  3. Right-click → Share
  4. Add the service account email: opencodehub-storage-sa@opencodehub-storage.iam.gserviceaccount.com
  5. Grant Editor access
  6. Copy the Folder ID from the URL:
    https://drive.google.com/drive/folders/1a2B3c4D5e6F7g8H9i0J
                                           ^^^^^^^^^^^^^^^^^^^^
                                           This is your folder ID
    

2.6. Prepare Service Account Credentials

Open the downloaded JSON file and extract these values:

{
  "type": "service_account",
  "project_id": "opencodehub-storage",
  "private_key_id": "abc123...",
  "private_key": "-----BEGIN PRIVATE KEY-----\n...",
  "client_email": "opencodehub-storage-sa@...",
  "client_id": "123456789...",
  ...
}

Save for later:

GOOGLE_DRIVE_FOLDER_ID=1a2B3c4D5e6F7g8H9i0J
GOOGLE_SERVICE_ACCOUNT_EMAIL=opencodehub-storage-sa@opencodehub-storage.iam.gserviceaccount.com
GOOGLE_PRIVATE_KEY=-----BEGIN PRIVATE KEY-----\n...
GOOGLE_CLIENT_ID=123456789...

3. Setup Upstash Redis

Upstash provides serverless Redis, perfect for Vercel edge functions.

3.1. Create Redis Database

  1. Go to Upstash Console
  2. Click Create Database
  3. Name: opencodehub-sessions
  4. Region: Choose closest to your users (e.g., us-east-1)
  5. Type: Regional (free tier)
  6. Click Create

3.2. Get Connection Details

After creation, copy these values:

REST API:

UPSTASH_REDIS_REST_URL=https://us1-prepared-moose-12345.upstash.io
UPSTASH_REDIS_REST_TOKEN=AaBbCc...==

Redis URL (optional):

REDIS_URL=redis://default:password@region.upstash.io:port

3.3. Configure Redis for Sessions

Upstash Redis will handle:

  • User sessions
  • Rate limiting
  • Temporary locks
  • Cache

No additional setup needed!


4. Configure Project

4.1. Update Database Configuration

Create/update src/db/turso.ts:

import { drizzle } from 'drizzle-orm/libsql';
import { createClient } from '@libsql/client';

const turso = createClient({
  url: process.env.TURSO_DATABASE_URL!,
  authToken: process.env.TURSO_AUTH_TOKEN!,
});

export const db = drizzle(turso);

4.2. Update Storage Configuration

Create/update src/lib/storage/google-drive.ts:

import { google } from 'googleapis';

const auth = new google.auth.GoogleAuth({
  credentials: {
    client_email: process.env.GOOGLE_SERVICE_ACCOUNT_EMAIL,
    private_key: process.env.GOOGLE_PRIVATE_KEY?.replace(/\\n/g, '\n'),
  },
  scopes: ['https://www.googleapis.com/auth/drive.file'],
});

const drive = google.drive({ version: 'v3', auth });
const folderId = process.env.GOOGLE_DRIVE_FOLDER_ID;

export async function uploadFile(fileName: string, content: Buffer) {
  const response = await drive.files.create({
    requestBody: {
      name: fileName,
      parents: [folderId!],
    },
    media: {
      body: content,
    },
  });
  
  return response.data.id;
}

export async function downloadFile(fileId: string) {
  const response = await drive.files.get(
    { fileId, alt: 'media' },
    { responseType: 'arraybuffer' }
  );
  
  return Buffer.from(response.data as ArrayBuffer);
}

export async function deleteFile(fileId: string) {
  await drive.files.delete({ fileId });
}

4.3. Update Redis Configuration

Create/update src/lib/redis.ts:

import { Redis } from '@upstash/redis';

export const redis = new Redis({
  url: process.env.UPSTASH_REDIS_REST_URL!,
  token: process.env.UPSTASH_REDIS_REST_TOKEN!,
});

// Session management
export async function setSession(sessionId: string, data: any, ttl = 7 * 24 * 60 * 60) {
  await redis.setex(sessionId, ttl, JSON.stringify(data));
}

export async function getSession(sessionId: string) {
  const data = await redis.get(sessionId);
  return data ? JSON.parse(data as string) : null;
}

export async function deleteSession(sessionId: string) {
  await redis.del(sessionId);
}

4.4. Install Dependencies

# Add Turso client
bun add @libsql/client drizzle-orm

# Add Google Drive API
bun add googleapis

# Add Upstash Redis
bun add @upstash/redis

4.5. Update Environment Variables Template

Create .env.example:

# === Turso Database ===
TURSO_DATABASE_URL=libsql://your-db.turso.io
TURSO_AUTH_TOKEN=your-token

# === Google Drive Storage ===
GOOGLE_DRIVE_FOLDER_ID=your-folder-id
GOOGLE_SERVICE_ACCOUNT_EMAIL=your-sa@project.iam.gserviceaccount.com
GOOGLE_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n"
GOOGLE_CLIENT_ID=your-client-id

# === Upstash Redis ===
UPSTASH_REDIS_REST_URL=https://your-redis.upstash.io
UPSTASH_REDIS_REST_TOKEN=your-token

# === Application ===
SITE_URL=https://your-app.vercel.app
JWT_SECRET=generate-a-strong-secret
SESSION_SECRET=generate-another-strong-secret
INTERNAL_HOOK_SECRET=generate-hook-secret

# === Optional: AI Review ===
AI_PROVIDER=openai
OPENAI_API_KEY=sk-...

4.6. Generate Secrets

# Generate secrets
openssl rand -base64 32  # JWT_SECRET
openssl rand -base64 32  # SESSION_SECRET
openssl rand -base64 32  # INTERNAL_HOOK_SECRET

5. Deploy to Vercel

5.1. Install Vercel CLI (Optional)

bun install -g vercel
vercel login

5.2. Connect Repository to Vercel

Option A: Via Vercel Dashboard

  1. Go to Vercel Dashboard
  2. Click Add NewProject
  3. Import your GitHub repository
  4. Select OpencodeHub

Option B: Via CLI

cd opencodehub
vercel

5.3. Configure Environment Variables

In Vercel Dashboard:

  1. Go to Project SettingsEnvironment Variables
  2. Add all variables from step 4.5:

Turso:

  • TURSO_DATABASE_URL = libsql://opencodehub-xxxx.turso.io
  • TURSO_AUTH_TOKEN = eyJhbG...

Google Drive:

  • GOOGLE_DRIVE_FOLDER_ID = 1a2B3c4D5e6F7g8H9i0J
  • GOOGLE_SERVICE_ACCOUNT_EMAIL = opencodehub-storage-sa@...
  • GOOGLE_PRIVATE_KEY = Paste entire private key (including \n)
  • GOOGLE_CLIENT_ID = 123456789...

Upstash Redis:

  • UPSTASH_REDIS_REST_URL = https://...upstash.io
  • UPSTASH_REDIS_REST_TOKEN = AaBbCc...

Application:

  • SITE_URL = https://your-project.vercel.app (update later)
  • JWT_SECRET = Generated secret
  • SESSION_SECRET = Generated secret
  • INTERNAL_HOOK_SECRET = Generated secret

AI (Optional):

  • AI_PROVIDER = openai
  • OPENAI_API_KEY = sk-...

5.4. Configure Build Settings

In Project SettingsBuild & Development Settings:

  • Framework Preset: Astro
  • Build Command: bun run build
  • Output Directory: dist
  • Install Command: bun install

5.5. Deploy!

Click Deploy or run:

vercel --prod

Deployment will:

  1. Install dependencies
  2. Build the project
  3. Deploy to Vercel Edge Network
  4. Provide deployment URL

6. Post-Deployment

6.1. Update SITE_URL

  1. Copy your Vercel deployment URL: https://opencodehub-abc123.vercel.app
  2. Update environment variable:
    • Go to SettingsEnvironment Variables
    • Edit SITE_URL = your actual URL
  3. Redeploy (Vercel Dashboard → Deployments → Redeploy)

6.2. Create Admin User

Option A: Via Turso CLI

turso db shell opencodehub

-- In the Turso shell:
INSERT INTO users (id, email, password_hash, username, role, created_at)
VALUES (
  'admin-user-id',
  'admin@yourcompany.com',
  '$2b$10$...',  -- bcrypt hash of your password
  'admin',
  'admin',
  current_timestamp
);

Generate bcrypt hash:

// Run in Node.js
const bcrypt = require('bcrypt');
const hash = await bcrypt.hash('your-password', 10);
console.log(hash);

Option B: Via Deployment Script

Create scripts/create-admin-vercel.ts:

import { db } from '../src/db/turso';
import { users } from '../src/db/schema';
import bcrypt from 'bcrypt';

const email = process.env.ADMIN_EMAIL || 'admin@example.com';
const password = process.env.ADMIN_PASSWORD || 'changeme';

const hash = await bcrypt.hash(password, 10);

await db.insert(users).values({
  id: crypto.randomUUID(),
  email,
  username: 'admin',
  password_hash: hash,
  role: 'admin',
  createdAt: new Date(),
});

console.log(`✅ Admin user created: ${email}`);

Run once:

ADMIN_EMAIL=your@email.com ADMIN_PASSWORD=strong123 bun scripts/create-admin-vercel.ts

6.3. Configure Custom Domain (Optional)

  1. Go to SettingsDomains
  2. Add your custom domain: git.yourcompany.com
  3. Follow DNS configuration instructions
  4. Update SITE_URL to your custom domain

6.4. Setup Monitoring

Vercel Analytics:

  1. Go to Analytics tab
  2. Enable Web Analytics
  3. Enable Speed Insights

Sentry (Optional):

bun add @sentry/astro

Add to .env:

SENTRY_DSN=https://...@sentry.io/...

7. Verification

7.1. Test Database Connection

Visit: https://your-app.vercel.app/api/health

Should return:

{
  "status": "ok",
  "database": "connected",
  "redis": "connected",
  "storage": "configured"
}

7.2. Test Storage

  1. Login as admin
  2. Create a repository
  3. Push a commit
  4. Verify file appears in Google Drive folder

7.3. Test Redis

  1. Login
  2. Check session persists
  3. Logout
  4. Session cleared

7.4. Load Test

# Install hey
go install github.com/rakyll/hey@latest

# Test
hey -n 1000 -c 10 https://your-app.vercel.app

Expected: >90% success rate


8. Troubleshooting

Database Connection Fails

Error: Failed to connect to Turso

Solution:

# Verify token is valid
turso db tokens validate opencodehub YOUR_TOKEN

# Regenerate if needed
turso db tokens create opencodehub

Update environment variable and redeploy.

Google Drive Upload Fails

Error: Insufficient permissions

Solution:

  1. Verify service account has Editor access to folder
  2. Check private key doesn't have extra spaces/newlines
  3. Test locally:
    bun test src/lib/storage/google-drive.test.ts

Redis Connection Timeout

Error: ETIMEDOUT connecting to Redis

Solution:

  1. Verify Upstash Redis region matches Vercel deployment region
  2. Check REST URL and token are correct
  3. Test connection:
    curl https://YOUR_REDIS_URL/get/test \
      -H "Authorization: Bearer YOUR_TOKEN"

Build Fails on Vercel

Error: Command "build" exited with 1

Solution:

# Test build locally
bun run build

# Check for TypeScript errors
bun run typecheck

# Check environment variables are set
vercel env ls

Out of Memory

Error: JavaScript heap out of memory

** Solution:** Upgrade Vercel plan or optimize build:

// package.json
{
  "scripts": {
    "build": "NODE_OPTIONS=--max_old_space_size=4096 astro build"
  }
}

Cost Estimation

Free Tier Limits

Turso:

  • Database: Free up to 8 GB
  • Rows: 500M
  • Locations: 1 primary + 2 replicas

Upstash Redis:

  • Commands: 10,000/day
  • Storage: 256 MB
  • Bandwidth: 256 MB/month

Google Drive:

  • Storage: 15 GB free
  • API calls: Unlimited

Vercel:

  • Bandwidth: 100 GB/month
  • Deployments: Unlimited
  • Edge Functions: 100 GB-hrs

Estimated Monthly Cost

Low traffic (< 1,000 users):

  • All free tier ✅

Medium traffic (1,000-10,000 users):

  • Turso: $0 (within limits)
  • Upstash: $10/month (higher tier)
  • Google Drive: $2/month (Google One 100GB)
  • Vercel: $20/month (Pro plan) Total: ~$32/month

High traffic (>10,000 users):

  • Turso: $29/month (Pro)
  • Upstash: $50/month (Pro)
  • Google Drive: $10/month (Google Workspace)
  • Vercel: $20/month (Pro) Total: ~$109/month

Performance Optimization

Edge Caching

Add to vercel.json:

{
  "headers": [
    {
      "source": "/assets/(.*)",
      "headers": [
        {
          "key": "Cache-Control",
          "value": "public, max-age=31536000, immutable"
        }
      ]
    }
  ]
}

Database Connection Pooling

Turso handles this automatically with libSQL.

Redis Caching Strategy

// Cache expensive queries
async function getRepository(id: string) {
  const cached = await redis.get(`repo:${id}`);
  if (cached) return JSON.parse(cached as string);
  
  const repo = await db.query.repositories.findFirst({ where: eq(repositories.id, id) });
  await redis.setex(`repo:${id}`, 300, JSON.stringify(repo)); // Cache 5 min
  
  return repo;
}

Security Checklist

  • All secrets in environment variables (not committed to Git)
  • HTTPS enabled (automatic on Vercel)
  • Database access restricted to Vercel IPs
  • Google Drive folder shared only with service account
  • Redis requires authentication
  • Rate limiting enabled
  • CORS configured properly
  • CSP headers set

Next Steps

  1. Setup CI/CD: Automatic deployments on push to main
  2. Add Monitoring: Set up Sentry for error tracking
  3. Configure Backups: Turso auto-backups + manual exports
  4. Scale Redis: Upgrade to Global for multi-region
  5. Add CDN: Use Vercel Edge Network or Cloudflare

Additional Resources


Support

Need help?


Congratulations! 🎉 Your OpenCodeHub instance is now running on Vercel with world-class serverless infrastructure!