/** * Production Database Query Tool * Allows CRUD operations on the production database * * Usage: * DATABASE_URL="..." pnpm tsx scripts/db-query.ts * * Commands (interactive): * tables - List all tables * users - List all users * threads - List chat threads * sql - Run raw SQL * insert-user - Create a user * delete-user - Delete a user * help - Show commands * exit - Exit */ import "dotenv/config" import postgres from "postgres" import * as readline from "readline" const CONNECTION_STRING = process.env.DATABASE_URL if (!CONNECTION_STRING) { console.error("āŒ DATABASE_URL is required") process.exit(1) } const sql = postgres(CONNECTION_STRING, { ssl: "require", max: 1, }) const rl = readline.createInterface({ input: process.stdin, output: process.stdout, }) function prompt(question: string): Promise { return new Promise((resolve) => { rl.question(question, resolve) }) } async function listTables() { const tables = await sql` SELECT table_name FROM information_schema.tables WHERE table_schema = 'public' ORDER BY table_name ` console.log("\nšŸ“‹ Tables:") if (tables.length === 0) { console.log(" (no tables found)") } else { for (const t of tables) { const count = await sql` SELECT COUNT(*) as count FROM ${sql(t.table_name)} ` console.log(` - ${t.table_name} (${count[0].count} rows)`) } } console.log() } async function listUsers() { try { const users = await sql`SELECT id, name, email, "createdAt" FROM users ORDER BY "createdAt" DESC LIMIT 20` console.log("\nšŸ‘„ Users:") if (users.length === 0) { console.log(" (no users)") } else { for (const u of users) { console.log(` - ${u.id}: ${u.name} <${u.email}> (${u.createdAt})`) } } console.log() } catch (e) { console.log(" āŒ users table not found or error:", (e as Error).message) } } async function listThreads() { try { const threads = await sql` SELECT id, title, user_id, created_at FROM chat_threads ORDER BY created_at DESC LIMIT 20 ` console.log("\nšŸ’¬ Chat Threads:") if (threads.length === 0) { console.log(" (no threads)") } else { for (const t of threads) { console.log(` - #${t.id}: "${t.title}" (user: ${t.user_id})`) } } console.log() } catch (e) { console.log(" āŒ chat_threads table not found or error:", (e as Error).message) } } async function runSQL(query: string) { try { const result = await sql.unsafe(query) console.log("\nāœ… Result:") console.log(result) console.log() } catch (e) { console.log("āŒ Error:", (e as Error).message) } } async function insertUser(email: string, name: string) { try { const id = `user_${Date.now()}` await sql` INSERT INTO users (id, name, email, "emailVerified", "createdAt", "updatedAt") VALUES (${id}, ${name}, ${email}, false, NOW(), NOW()) ` console.log(`āœ… Created user: ${id}`) } catch (e) { console.log("āŒ Error:", (e as Error).message) } } async function deleteUser(id: string) { try { const result = await sql`DELETE FROM users WHERE id = ${id}` console.log(`āœ… Deleted ${result.count} user(s)`) } catch (e) { console.log("āŒ Error:", (e as Error).message) } } function showHelp() { console.log(` šŸ“– Commands: tables - List all tables with row counts users - List users (max 20) threads - List chat threads (max 20) sql - Run raw SQL query insert-user - Create a new user delete-user - Delete a user by ID drop-all - Drop all tables (dangerous!) help - Show this help exit - Exit the tool `) } async function dropAll() { const confirm = await prompt("āš ļø This will DROP ALL TABLES. Type 'DROP' to confirm: ") if (confirm !== "DROP") { console.log("Aborted.") return } const tables = [ "thread_context_items", "context_items", "canvas_images", "canvas", "chat_messages", "chat_threads", "verifications", "accounts", "sessions", "users", ] for (const table of tables) { try { await sql`DROP TABLE IF EXISTS ${sql(table)} CASCADE` console.log(` āœ“ Dropped ${table}`) } catch (e) { console.log(` āœ— ${table}: ${(e as Error).message}`) } } console.log("\nāœ“ All tables dropped") } async function main() { console.log("šŸ”Œ Connected to production database") console.log('Type "help" for commands, "exit" to quit.\n') // Check initial connection try { const [result] = await sql`SELECT current_database() as db` console.log(`Database: ${result.db}\n`) } catch (e) { console.error("āŒ Connection failed:", (e as Error).message) process.exit(1) } while (true) { const input = await prompt("db> ") const [cmd, ...args] = input.trim().split(/\s+/) switch (cmd.toLowerCase()) { case "tables": await listTables() break case "users": await listUsers() break case "threads": await listThreads() break case "sql": await runSQL(args.join(" ")) break case "insert-user": if (args.length < 2) { console.log("Usage: insert-user ") } else { await insertUser(args[0], args.slice(1).join(" ")) } break case "delete-user": if (args.length < 1) { console.log("Usage: delete-user ") } else { await deleteUser(args[0]) } break case "drop-all": await dropAll() break case "help": showHelp() break case "exit": case "quit": case "q": console.log("Bye!") await sql.end() rl.close() process.exit(0) case "": break default: console.log(`Unknown command: ${cmd}. Type "help" for commands.`) } } } main().catch(console.error)