const betterSqlite3 = require('better-sqlite3'); const { Pool } = require('pg'); const path = require('path'); const sqlitePath = path.join(__dirname, '.tmp/data.db'); const sqliteDb = new betterSqlite3(sqlitePath); const pgPool = new Pool({ host: process.env.DATABASE_HOST || '127.0.0.1', port: parseInt(process.env.DATABASE_PORT || '9543', 10), user: process.env.DATABASE_USERNAME || 'strapi', password: process.env.DATABASE_PASSWORD || 'strapi_password_9000', database: process.env.DATABASE_NAME || 'strapi', }); async function migrate() { console.log('Connecting to PostgreSQL...'); const client = await pgPool.connect(); try { console.log('Disabling constraints temporarily for migration...'); await client.query("SET session_replication_role = 'replica';"); // 1. Get all public tables in Postgres and TRUNCATE them const allTablesRes = await client.query(` SELECT table_name FROM information_schema.tables WHERE table_schema = 'public' AND table_type = 'BASE TABLE'; `); const pgTables = allTablesRes.rows.map(r => r.table_name); for (const t of pgTables) { await client.query(`TRUNCATE TABLE "${t}" CASCADE;`); } console.log(`Truncated all ${pgTables.length} PostgreSQL tables.`); // 2. Migrate tables from SQLite const tablesToMigrate = [ 'strapi_database_schema', 'strapi_migrations_internal', 'strapi_core_store_settings', 'i18n_locale', 'files', 'files_related_mph', 'job_roles', 'blogs', 'blogs_author_lnk', 'blogs_editor_lnk', 'admin_roles', 'admin_users', 'admin_users_roles_lnk', 'admin_permissions', 'admin_permissions_role_lnk', 'up_roles', 'up_permissions', 'up_permissions_role_lnk', 'strapi_api_tokens', 'strapi_sessions', ]; for (const table of tablesToMigrate) { if (!pgTables.includes(table)) { console.log(`Table ${table} does not exist in Postgres, skipping.`); continue; } const colInfoRes = await client.query( `SELECT column_name, data_type FROM information_schema.columns WHERE table_name = $1;`, [table] ); const colDataTypes = {}; colInfoRes.rows.forEach(r => { colDataTypes[r.column_name] = r.data_type; }); const rows = sqliteDb.prepare(`SELECT * FROM "${table}"`).all(); console.log(`Migrating table: ${table} (${rows.length} rows)`); if (rows.length === 0) continue; const columns = Object.keys(rows[0]); const colNames = columns.map((c) => `"${c}"`).join(', '); for (let index = 0; index < rows.length; index++) { const row = rows[index]; const values = columns.map((col) => { let val = row[col]; const dt = colDataTypes[col]; if ((dt === 'timestamp with time zone' || dt === 'timestamp without time zone') && val !== null) { if (typeof val === 'number' || (typeof val === 'string' && /^\d+$/.test(val))) { val = new Date(Number(val)).toISOString(); } } if (dt === 'json' || dt === 'jsonb') { if (val === '' || val === undefined) { val = null; } else if (typeof val === 'object') { val = JSON.stringify(val); } } if (dt === 'boolean' && val !== null) { if (val === 1 || val === '1' || val === 'true') val = true; if (val === 0 || val === '0' || val === 'false') val = false; } return val; }); const placeholders = values.map((_, i) => `$${i + 1}`).join(', '); const query = `INSERT INTO "${table}" (${colNames}) VALUES (${placeholders});`; await client.query(query, values); } if (columns.includes('id')) { const seqRes = await client.query(` SELECT pg_get_serial_sequence('"${table}"', 'id') AS seq; `); const seqName = seqRes.rows[0]?.seq; if (seqName) { await client.query(` SELECT setval('${seqName}', COALESCE((SELECT MAX(id) FROM "${table}"), 1)); `); } } } // 3. Fix ordinals for all link tables console.log('Fixing ordinals for link tables...'); await client.query(` UPDATE public.admin_users_roles_lnk a SET user_ord = b.inv_order FROM ( SELECT id, ROW_NUMBER() OVER (PARTITION BY role_id ORDER BY id) as inv_order FROM public.admin_users_roles_lnk ) b WHERE a.id = b.id; `); await client.query(` UPDATE public.admin_permissions_role_lnk a SET permission_ord = b.inv_order FROM ( SELECT id, ROW_NUMBER() OVER (PARTITION BY role_id ORDER BY id) as inv_order FROM public.admin_permissions_role_lnk ) b WHERE a.id = b.id; `); await client.query(` UPDATE public.up_permissions_role_lnk a SET permission_ord = b.inv_order FROM ( SELECT id, ROW_NUMBER() OVER (PARTITION BY role_id ORDER BY id) as inv_order FROM public.up_permissions_role_lnk ) b WHERE a.id = b.id; `); console.log('Re-enabling constraints...'); await client.query("SET session_replication_role = 'origin';"); console.log('Migration completed successfully!'); } catch (err) { console.error('Migration failed:', err); } finally { client.release(); await pgPool.end(); sqliteDb.close(); } } migrate();