godcrm/backend/scripts/clone-doc-tables.js
GOD CRM Release f89e074dd1
Some checks failed
CI / Lint / Typecheck / Test / Build (push) Has been cancelled
CI / PostgreSQL Integration Tests (push) Has been cancelled
GOD CRM — public scrubbed snapshot
Governed substrate for autonomous agents: scoped identity (passports),
audited actions, MCP workspace. Infra IPs and secrets redacted for public release.
2026-08-10 04:01:45 +03:00

194 lines
5.3 KiB
JavaScript

/**
* Clone all doc_* tables from project 146 (Development/Knowledge Base)
* to project 183 (Holetron/Knowledge Base)
*
* Usage: node backend/scripts/clone-doc-tables.js
*/
import pg from 'pg';
const { Pool } = pg;
const pool = new Pool({
host: 'localhost',
port: 5432,
database: 'godcrm_prod',
user: 'godcrm',
password: 'godcrm_dev_2026'
});
const SOURCE_PROJECT_ID = 146;
const TARGET_PROJECT_ID = 183;
const CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
function generateBaseId() {
let result = '';
for (let i = 0; i < 8; i++) {
result += CHARS[Math.floor(Math.random() * CHARS.length)];
}
return result;
}
async function cloneTable(client, sourceTableId) {
// Get source table
const { rows: [source] } = await client.query(
'SELECT * FROM universal_tables WHERE id = $1', [sourceTableId]
);
if (!source) throw new Error(`Table ${sourceTableId} not found`);
// Get columns
const { rows: columns } = await client.query(
'SELECT * FROM table_columns WHERE table_id = $1 ORDER BY order_index', [sourceTableId]
);
// Get rows
const { rows: dataRows } = await client.query(
'SELECT * FROM table_rows WHERE table_id = $1', [sourceTableId]
);
// Create new table
const { rows: [newTable] } = await client.query(`
INSERT INTO universal_tables (
project_id, name, display_name, description, icon, is_system,
show_in_nav, order_index, config, table_type, color, folder_path
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)
RETURNING id
`, [
TARGET_PROJECT_ID,
source.name,
source.display_name,
source.description,
source.icon,
source.is_system || 0,
source.show_in_nav,
source.order_index || 0,
source.config || '{}',
source.table_type,
source.color,
source.folder_path
]);
const newTableId = newTable.id;
// Copy columns
for (const col of columns) {
await client.query(`
INSERT INTO table_columns (
table_id, column_name, display_name, type, config,
order_index, is_visible, is_required, is_system,
is_from_source, is_primary_key, is_locked, formula,
options, required, unique_constraint, default_value,
is_readonly, width, min_width, max_width, mapping
) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$20,$21,$22)
`, [
newTableId,
col.column_name,
col.display_name,
col.type,
col.config,
col.order_index,
col.is_visible,
col.is_required,
col.is_system,
col.is_from_source,
col.is_primary_key,
col.is_locked,
col.formula,
col.options,
col.required,
col.unique_constraint,
col.default_value,
col.is_readonly,
col.width,
col.min_width,
col.max_width,
col.mapping
]);
}
// Copy data rows
for (const row of dataRows) {
const baseId = generateBaseId();
await client.query(`
INSERT INTO table_rows (table_id, base_id, data, created_by)
VALUES ($1, $2, $3, $4)
`, [newTableId, baseId, row.data, row.created_by]);
}
return {
sourceId: sourceTableId,
newId: newTableId,
name: source.name,
displayName: source.display_name,
columnsCount: columns.length,
rowsCount: dataRows.length
};
}
async function main() {
const client = await pool.connect();
try {
// Get all doc_* tables from source project
const { rows: docTables } = await client.query(
`SELECT id, name, display_name FROM universal_tables
WHERE project_id = $1 AND name LIKE 'doc_%'
ORDER BY id`,
[SOURCE_PROJECT_ID]
);
console.log(`Found ${docTables.length} doc_* tables in project ${SOURCE_PROJECT_ID}`);
console.log('='.repeat(80));
const mapping = [];
const errors = [];
let successCount = 0;
for (let i = 0; i < docTables.length; i++) {
const table = docTables[i];
try {
// Use a transaction per table so failures don't cascade
await client.query('BEGIN');
const result = await cloneTable(client, table.id);
await client.query('COMMIT');
mapping.push(result);
successCount++;
console.log(`[${i + 1}/${docTables.length}] OK: ${result.sourceId} -> ${result.newId} | ${result.name} (${result.columnsCount} cols, ${result.rowsCount} rows)`);
} catch (err) {
await client.query('ROLLBACK');
errors.push({ tableId: table.id, name: table.name, error: err.message });
console.error(`[${i + 1}/${docTables.length}] FAIL: ${table.id} | ${table.name} | ${err.message}`);
}
}
console.log('\n' + '='.repeat(80));
console.log(`\nSUMMARY:`);
console.log(` Total doc tables found: ${docTables.length}`);
console.log(` Successfully cloned: ${successCount}`);
console.log(` Failed: ${errors.length}`);
if (errors.length > 0) {
console.log(`\nFAILED TABLES:`);
for (const err of errors) {
console.log(` ${err.tableId} | ${err.name} | ${err.error}`);
}
}
console.log(`\nCOMPLETE MAPPING (SOURCE_ID -> NEW_ID):`);
console.log(JSON.stringify(mapping.map(m => ({
sourceId: m.sourceId,
newId: m.newId,
name: m.name,
displayName: m.displayName
})), null, 2));
} finally {
client.release();
await pool.end();
}
}
main().catch(err => {
console.error('Fatal error:', err);
process.exit(1);
});