#!/bin/sh set -e echo "=== Syncing NextAuth schema ===" # NOTE: Do NOT use --accept-data-loss — it drops tables not in the Prisma schema, # which destroys fs_users, fs_projects etc. Use --skip-generate only. npx prisma db push --skip-generate || echo "Prisma push failed (non-fatal — tables may already be correct)" echo "=== Ensuring app tables exist ===" node -e " const { Pool } = require('pg'); const pool = new Pool({ connectionString: process.env.DATABASE_URL }); pool.query(\` CREATE EXTENSION IF NOT EXISTS pgcrypto; CREATE TABLE IF NOT EXISTS fs_users ( id TEXT PRIMARY KEY DEFAULT gen_random_uuid()::text, user_id TEXT, data JSONB NOT NULL DEFAULT '{}'::jsonb, created_at TIMESTAMP DEFAULT NOW(), updated_at TIMESTAMP DEFAULT NOW() ); CREATE UNIQUE INDEX IF NOT EXISTS idx_fs_users_email_unique ON fs_users ((data->>'email')); CREATE INDEX IF NOT EXISTS idx_fs_users_user_id ON fs_users (user_id); CREATE TABLE IF NOT EXISTS fs_projects ( id TEXT PRIMARY KEY DEFAULT gen_random_uuid()::text, user_id TEXT REFERENCES fs_users(id) ON DELETE CASCADE, workspace TEXT, slug TEXT, data JSONB NOT NULL DEFAULT '{}'::jsonb, created_at TIMESTAMP DEFAULT NOW(), updated_at TIMESTAMP DEFAULT NOW() ); CREATE INDEX IF NOT EXISTS idx_fs_projects_user_id ON fs_projects (user_id); CREATE INDEX IF NOT EXISTS idx_fs_projects_slug ON fs_projects (slug); CREATE TABLE IF NOT EXISTS fs_sessions ( id TEXT PRIMARY KEY DEFAULT gen_random_uuid()::text, user_id TEXT REFERENCES fs_users(id) ON DELETE CASCADE, data JSONB NOT NULL DEFAULT '{}'::jsonb, created_at TIMESTAMP DEFAULT NOW(), updated_at TIMESTAMP DEFAULT NOW() ); CREATE INDEX IF NOT EXISTS idx_fs_sessions_user_id ON fs_sessions (user_id); CREATE INDEX IF NOT EXISTS idx_fs_sessions_project_id ON fs_sessions ((data->>'projectId')); CREATE TABLE IF NOT EXISTS fs_knowledge_items ( id TEXT PRIMARY KEY DEFAULT gen_random_uuid()::text, project_id TEXT NOT NULL, data JSONB NOT NULL DEFAULT '{}'::jsonb, created_at TIMESTAMP DEFAULT NOW(), updated_at TIMESTAMP DEFAULT NOW() ); CREATE INDEX IF NOT EXISTS idx_fs_knowledge_project_id ON fs_knowledge_items (project_id); CREATE TABLE IF NOT EXISTS chat_conversations ( project_id TEXT PRIMARY KEY, messages JSONB NOT NULL DEFAULT '[]', updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE TABLE IF NOT EXISTS agent_sessions ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), project_id TEXT NOT NULL, app_name TEXT NOT NULL, app_path TEXT NOT NULL, task TEXT NOT NULL, plan JSONB, status TEXT NOT NULL DEFAULT 'pending', output JSONB NOT NULL DEFAULT '[]'::jsonb, changed_files JSONB NOT NULL DEFAULT '[]'::jsonb, error TEXT, started_at TIMESTAMPTZ, completed_at TIMESTAMPTZ, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ NOT NULL DEFAULT now() ); CREATE INDEX IF NOT EXISTS agent_sessions_project_idx ON agent_sessions (project_id, created_at DESC); CREATE INDEX IF NOT EXISTS agent_sessions_status_idx ON agent_sessions (status); CREATE TABLE IF NOT EXISTS agent_session_events ( id BIGSERIAL PRIMARY KEY, session_id UUID NOT NULL REFERENCES agent_sessions(id) ON DELETE CASCADE, project_id TEXT NOT NULL, seq INT NOT NULL, ts TIMESTAMPTZ NOT NULL, type TEXT NOT NULL, payload JSONB NOT NULL DEFAULT '{}'::jsonb, client_event_id UUID UNIQUE, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), UNIQUE(session_id, seq) ); CREATE INDEX IF NOT EXISTS agent_session_events_session_seq_idx ON agent_session_events (session_id, seq); \`).then(() => { console.log('App tables ready'); pool.end(); }).catch(e => { console.error('Table init error:', e.message); pool.end(); }); " echo "=== Starting Next.js server ===" exec node server.js