steelforesight/panel/transfer.js

116 lines
3.9 KiB
JavaScript

import pg from 'pg';
const { Pool } = pg;
const liaraPool = new Pool({
connectionString: 'postgresql://root:hePGFWh8f5TAEWCBK0V8XtQh@el-capitan.liara.cloud:32727/postgres',
ssl: false,
});
const vpsPool = new Pool({
connectionString: 'postgresql://user:d1ph12poyhd1o2pDASd2jp2dnbcba@94.101.186.198:5433/steelforesight',
ssl: false,
});
async function migrateFast() {
console.log('Starting fast batch migration...');
const tablesRes = await liaraPool.query(`
SELECT table_name
FROM information_schema.tables
WHERE table_schema = 'public' AND table_type = 'BASE TABLE'
ORDER BY table_name;
`);
const tables = tablesRes.rows.map(r => r.table_name);
for (const table of tables) {
console.log(`\nTable: ${table}`);
// Check if table exists on VPS
const checkTable = await vpsPool.query(`
SELECT table_name FROM information_schema.tables WHERE table_schema = 'public' AND table_name = $1
`, [table]);
if (checkTable.rows.length === 0) {
const colsRes = await liaraPool.query(`
SELECT column_name, data_type, udt_name, is_nullable, column_default
FROM information_schema.columns
WHERE table_schema = 'public' AND table_name = $1
ORDER BY ordinal_position;
`, [table]);
const colDefs = colsRes.rows.map(col => {
let type = col.data_type;
if (col.data_type === 'USER-DEFINED') type = col.udt_name;
if (col.column_name === 'id' && (col.data_type === 'integer' || col.data_type === 'bigint') && col.column_default?.includes('nextval')) {
return `"${col.column_name}" SERIAL PRIMARY KEY`;
}
let def = `"${col.column_name}" ${type}`;
if (col.is_nullable === 'NO') def += ' NOT NULL';
if (col.column_name === 'id' && col.data_type === 'text') def += ' PRIMARY KEY';
return def;
}).join(',\n ');
const createSql = `CREATE TABLE IF NOT EXISTS "${table}" (\n ${colDefs}\n);`;
await vpsPool.query(createSql);
console.log(`Created table ${table}`);
}
const rowsRes = await liaraPool.query(`SELECT * FROM "${table}"`);
console.log(`Copying ${rowsRes.rows.length} rows...`);
if (rowsRes.rows.length > 0) {
await vpsPool.query(`TRUNCATE TABLE "${table}" CASCADE;`);
// Batch insert in chunks of 100 rows
const chunkSize = 100;
for (let i = 0; i < rowsRes.rows.length; i += chunkSize) {
const chunk = rowsRes.rows.slice(i, i + chunkSize);
const keys = Object.keys(chunk[0]);
const colNames = keys.map(k => `"${k}"`).join(', ');
const values = [];
const valuePlaceholders = [];
let paramIdx = 1;
for (const row of chunk) {
const rowPlaceholders = [];
for (const key of keys) {
values.push(row[key]);
rowPlaceholders.push(`$${paramIdx++}`);
}
valuePlaceholders.push(`(${rowPlaceholders.join(', ')})`);
}
const batchSql = `INSERT INTO "${table}" (${colNames}) VALUES ${valuePlaceholders.join(', ')} ON CONFLICT DO NOTHING;`;
await vpsPool.query(batchSql, values);
}
console.log(`✓ Completed table ${table}`);
}
}
// Update sequences
const seqRes = await vpsPool.query(`
SELECT sequence_name FROM information_schema.sequences WHERE sequence_schema = 'public';
`);
for (const seq of seqRes.rows) {
const seqName = seq.sequence_name;
const tabName = seqName.replace(/_id_seq$/, '');
try {
await vpsPool.query(`SELECT setval('${seqName}', COALESCE((SELECT MAX(id) FROM "${tabName}"), 1), true);`);
console.log(`Updated sequence: ${seqName}`);
} catch (e) {
// ignore
}
}
console.log('\nAll PostgreSQL tables & data migrated successfully!');
await liaraPool.end();
await vpsPool.end();
}
migrateFast().catch(err => {
console.error('Migration error:', err);
process.exit(1);
});