Governed substrate for autonomous agents: scoped identity (passports), audited actions, MCP workspace. Infra IPs and secrets redacted for public release.
194 lines
5.3 KiB
JavaScript
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);
|
|
});
|