import { createClient } from "@libsql/client"; import { execSync } from "child_process"; import { readFileSync, writeFileSync, unlinkSync, existsSync } from "fs"; import { env } from "../src/env.js"; async function migrateToTurso() { console.log("๐Ÿš€ Pushing local SQLite data to live Turso database...\n"); try { // Check if local database exists console.log("๐Ÿ“ Checking local database..."); const dbExists = existsSync("./db.sqlite"); if (!dbExists) { console.error("โŒ Local database db.sqlite not found!"); process.exit(1); } console.log("โœ… Found local database"); // Create SQL dump of local database console.log("๐Ÿ“ฆ Creating SQL dump from local database..."); const dumpPath = "./temp_dump.sql"; try { execSync(`sqlite3 db.sqlite ".dump" > ${dumpPath}`, { stdio: "inherit" }); console.log("โœ… SQL dump created"); } catch (error) { console.error( "โŒ Failed to create SQL dump. Make sure sqlite3 is installed.", ); process.exit(1); } // Read and filter the dump file console.log("๐Ÿ” Processing SQL dump..."); const dumpContent = readFileSync(dumpPath, "utf8"); // Split into lines and filter for beenvoice tables const lines = dumpContent.split("\n"); const filteredLines = []; let inBeenvoiceTable = false; for (const line of lines) { // Skip PRAGMA and TRANSACTION statements if ( line.startsWith("PRAGMA") || line.startsWith("BEGIN TRANSACTION") || line.startsWith("COMMIT") ) { continue; } // Check if we're starting a beenvoice table if ( line.startsWith("CREATE TABLE `beenvoice_") || line.startsWith("CREATE TABLE beenvoice_") ) { inBeenvoiceTable = true; filteredLines.push(line); continue; } // Check if we're inserting into a beenvoice table if ( line.startsWith("INSERT INTO beenvoice_") || line.startsWith("INSERT INTO `beenvoice_") ) { filteredLines.push(line); continue; } // If we were in a beenvoice table and hit another CREATE TABLE, we're done with that table if ( inBeenvoiceTable && line.startsWith("CREATE TABLE") && !line.includes("beenvoice_") ) { inBeenvoiceTable = false; } // If we're in a beenvoice table, include the line if (inBeenvoiceTable) { filteredLines.push(line); } } console.log(`โœ… Filtered ${filteredLines.length} SQL statements`); // Connect to Turso console.log("๐Ÿ”— Connecting to live Turso database..."); if (!env.DATABASE_URL || !env.DATABASE_AUTH_TOKEN) { console.error("โŒ Missing DATABASE_URL or DATABASE_AUTH_TOKEN"); console.log("๐Ÿ’ก Make sure your .env file has the Turso credentials"); process.exit(1); } const tursoClient = createClient({ url: env.DATABASE_URL, authToken: env.DATABASE_AUTH_TOKEN, }); console.log("โœ… Connected to Turso"); // Clear existing data from beenvoice tables console.log("๐Ÿ—‘๏ธ Clearing existing data..."); const tablesToClear = [ "beenvoice_invoice_item", "beenvoice_invoice", "beenvoice_client", "beenvoice_business", "beenvoice_session", "beenvoice_account", "beenvoice_user", ]; for (const table of tablesToClear) { try { await tursoClient.execute(`DELETE FROM ${table}`); console.log(` โœ… Cleared ${table}`); } catch (error) { // Table might not exist, that's okay console.log(` โญ๏ธ Skipped ${table} (doesn't exist)`); } } // Execute the filtered SQL statements console.log("๐Ÿ“ค Pushing data to Turso..."); let successCount = 0; let errorCount = 0; for (const line of filteredLines) { const trimmed = line.trim(); if (!trimmed || trimmed === "") continue; try { await tursoClient.execute(trimmed); successCount++; // Show progress for inserts if (trimmed.startsWith("INSERT")) { const match = trimmed.match(/INSERT INTO (\w+)/); if (match && successCount % 10 === 0) { console.log(` ๐Ÿ“ Inserted ${successCount} records...`); } } } catch (error) { errorCount++; if (trimmed.startsWith("CREATE TABLE")) { console.log( ` โš ๏ธ Table already exists: ${trimmed.substring(0, 50)}...`, ); } else { console.error( ` โŒ Error executing: ${trimmed.substring(0, 50)}...`, ); console.error( ` Error: ${error instanceof Error ? error.message : String(error)}`, ); } } } // Verify the migration console.log("\n๐Ÿ” Verifying migration..."); const tables = [ "beenvoice_user", "beenvoice_client", "beenvoice_invoice", "beenvoice_invoice_item", ]; for (const table of tables) { try { const result = await tursoClient.execute( `SELECT COUNT(*) as count FROM ${table}`, ); const count = result.rows[0]?.count || 0; console.log(` ๐Ÿ“Š ${table}: ${count} records`); } catch (error) { console.log(` โญ๏ธ ${table}: table doesn't exist`); } } console.log(`\n๐ŸŽ‰ Migration completed!`); console.log(` โœ… ${successCount} statements executed successfully`); if (errorCount > 0) { console.log( ` โš ๏ธ ${errorCount} statements had errors (likely table creation conflicts)`, ); } console.log(`\n๐Ÿ’ก Your local data is now live on Turso!`); console.log(`๐Ÿ’ก Your Vercel deployment will use this data.`); } catch (error) { console.error( "\nโŒ Migration failed:", error instanceof Error ? error.message : String(error), ); process.exit(1); } finally { // Cleanup try { unlinkSync("./temp_dump.sql"); console.log("๐Ÿงน Cleaned up temporary files"); } catch (e) { // File might not exist, that's okay } console.log("๐Ÿ”Œ Done!"); } } migrateToTurso().catch(console.error);