feat: Containerize services with Docker Compose (PostgreSQL, Strapi CMS, Express Backend, Nginx Frontend) on custom ports >9000
This commit is contained in:
@@ -0,0 +1,161 @@
|
||||
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();
|
||||
Reference in New Issue
Block a user