@deinossrl/dgp-agent 1.4.59 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +19 -0
- package/index.mjs +163 -5
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
# Changelog - DGP Agent
|
|
2
2
|
|
|
3
|
+
## [1.5.0] - 2026-01-12
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- **Auto-creación de schemas en restore**: Detecta automáticamente qué schemas necesita el backup y los crea antes de restaurar
|
|
7
|
+
- Agregado `psql.exe` a los binarios instalados (pg_dump, pg_restore, psql)
|
|
8
|
+
- Detección robusta de schemas con 3 métodos:
|
|
9
|
+
1. `pg_restore --list` con múltiples patrones de búsqueda
|
|
10
|
+
2. Análisis directo del contenido del backup si --list falla
|
|
11
|
+
3. Fallback a parsing manual del archivo
|
|
12
|
+
- Manejo inteligente de errores al crear schemas (ya existe, sin permisos, etc.)
|
|
13
|
+
|
|
14
|
+
### Changed
|
|
15
|
+
- Logs más detallados durante detección y creación de schemas
|
|
16
|
+
- Mejor manejo de errores críticos vs warnings
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
- Restauraciones personalizadas ahora funcionan correctamente sin necesidad de crear schemas manualmente
|
|
20
|
+
- Resuelve el problema de "schema does not exist" en backups selectivos
|
|
21
|
+
|
|
3
22
|
## [1.4.59] - 2026-01-12
|
|
4
23
|
|
|
5
24
|
### Added
|
package/index.mjs
CHANGED
|
@@ -1532,16 +1532,21 @@ async function findOrInstallPgDump() {
|
|
|
1532
1532
|
// Copiar pg_dump.exe, pg_restore.exe y TODAS las DLLs al BIN_DIR raíz
|
|
1533
1533
|
const binSrcDir = join(BIN_DIR, 'pgsql', 'bin');
|
|
1534
1534
|
|
|
1535
|
-
// Copiar pg_dump.exe
|
|
1535
|
+
// Copiar pg_dump.exe, pg_restore.exe y psql.exe
|
|
1536
1536
|
execSync(`copy "${extractedPgDump}" "${localPgDump}"`, { shell: 'cmd.exe' });
|
|
1537
1537
|
|
|
1538
|
-
// Copiar pg_restore.exe
|
|
1539
1538
|
const extractedPgRestore = join(BIN_DIR, 'pgsql', 'bin', 'pg_restore.exe');
|
|
1540
1539
|
const localPgRestore = join(BIN_DIR, 'pg_restore.exe');
|
|
1541
1540
|
if (existsSync(extractedPgRestore)) {
|
|
1542
1541
|
execSync(`copy "${extractedPgRestore}" "${localPgRestore}"`, { shell: 'cmd.exe' });
|
|
1543
1542
|
}
|
|
1544
1543
|
|
|
1544
|
+
const extractedPsql = join(BIN_DIR, 'pgsql', 'bin', 'psql.exe');
|
|
1545
|
+
const localPsql = join(BIN_DIR, 'psql.exe');
|
|
1546
|
+
if (existsSync(extractedPsql)) {
|
|
1547
|
+
execSync(`copy "${extractedPsql}" "${localPsql}"`, { shell: 'cmd.exe' });
|
|
1548
|
+
}
|
|
1549
|
+
|
|
1545
1550
|
// Copiar TODAS las DLLs (no solo algunas específicas)
|
|
1546
1551
|
execSync(`copy "${binSrcDir}\\*.dll" "${BIN_DIR}\\"`, { shell: 'cmd.exe' });
|
|
1547
1552
|
|
|
@@ -1582,16 +1587,21 @@ async function findOrInstallPgDump() {
|
|
|
1582
1587
|
if (existsSync(extractedPgDump)) {
|
|
1583
1588
|
const binSrcDir = join(BIN_DIR, 'pgsql', 'bin');
|
|
1584
1589
|
|
|
1585
|
-
// Copiar pg_dump.exe
|
|
1590
|
+
// Copiar pg_dump.exe, pg_restore.exe y psql.exe
|
|
1586
1591
|
execSync(`copy "${extractedPgDump}" "${localPgDump}"`, { shell: 'cmd.exe' });
|
|
1587
1592
|
|
|
1588
|
-
// Copiar pg_restore.exe
|
|
1589
1593
|
const extractedPgRestore = join(BIN_DIR, 'pgsql', 'bin', 'pg_restore.exe');
|
|
1590
1594
|
const localPgRestore = join(BIN_DIR, 'pg_restore.exe');
|
|
1591
1595
|
if (existsSync(extractedPgRestore)) {
|
|
1592
1596
|
execSync(`copy "${extractedPgRestore}" "${localPgRestore}"`, { shell: 'cmd.exe' });
|
|
1593
1597
|
}
|
|
1594
1598
|
|
|
1599
|
+
const extractedPsql = join(BIN_DIR, 'pgsql', 'bin', 'psql.exe');
|
|
1600
|
+
const localPsql = join(BIN_DIR, 'psql.exe');
|
|
1601
|
+
if (existsSync(extractedPsql)) {
|
|
1602
|
+
execSync(`copy "${extractedPsql}" "${localPsql}"`, { shell: 'cmd.exe' });
|
|
1603
|
+
}
|
|
1604
|
+
|
|
1595
1605
|
// Copiar TODAS las DLLs
|
|
1596
1606
|
execSync(`copy "${binSrcDir}\\*.dll" "${BIN_DIR}\\"`, { shell: 'cmd.exe' });
|
|
1597
1607
|
|
|
@@ -1958,7 +1968,155 @@ async function executePgRestore(command) {
|
|
|
1958
1968
|
.update({ status: 'running', started_at: new Date().toISOString() })
|
|
1959
1969
|
.eq('id', restore_id);
|
|
1960
1970
|
|
|
1961
|
-
//
|
|
1971
|
+
// PASO 1: Detectar y crear schemas necesarios antes de restaurar
|
|
1972
|
+
logInfo('Analizando backup para detectar schemas...');
|
|
1973
|
+
await addCommandLog('info', 'Detectando schemas del backup...');
|
|
1974
|
+
|
|
1975
|
+
const schemas = new Set();
|
|
1976
|
+
|
|
1977
|
+
try {
|
|
1978
|
+
// Usar pg_restore --list para ver el contenido del backup
|
|
1979
|
+
const listResult = spawnSync(pgRestorePath, ['--list', localFilePath], {
|
|
1980
|
+
env: process.env,
|
|
1981
|
+
encoding: 'utf-8',
|
|
1982
|
+
stdio: 'pipe',
|
|
1983
|
+
});
|
|
1984
|
+
|
|
1985
|
+
if (listResult.status === 0 && listResult.stdout) {
|
|
1986
|
+
const listOutput = listResult.stdout;
|
|
1987
|
+
|
|
1988
|
+
// Método 1: Buscar líneas con "SCHEMA - nombre"
|
|
1989
|
+
const schemaLines = listOutput.match(/^\s*\d+;\s*\d+\s+\d+\s+SCHEMA\s+-\s+(\w+)/gm) || [];
|
|
1990
|
+
schemaLines.forEach(line => {
|
|
1991
|
+
const match = line.match(/SCHEMA\s+-\s+(\w+)/);
|
|
1992
|
+
if (match && match[1] && match[1] !== 'public' && match[1] !== '-') {
|
|
1993
|
+
schemas.add(match[1]);
|
|
1994
|
+
}
|
|
1995
|
+
});
|
|
1996
|
+
|
|
1997
|
+
// Método 2: Buscar referencias a schemas en tablas (ej: "TABLE - crm.quotes")
|
|
1998
|
+
const tableLines = listOutput.match(/TABLE\s+-\s+(\w+)\./g) || [];
|
|
1999
|
+
tableLines.forEach(line => {
|
|
2000
|
+
const match = line.match(/TABLE\s+-\s+(\w+)\./);
|
|
2001
|
+
if (match && match[1] && match[1] !== 'public') {
|
|
2002
|
+
schemas.add(match[1]);
|
|
2003
|
+
}
|
|
2004
|
+
});
|
|
2005
|
+
|
|
2006
|
+
// Método 3: Buscar "SCHEMA COMMENT" o "ACL - SCHEMA"
|
|
2007
|
+
const schemaRefs = listOutput.match(/(?:SCHEMA COMMENT|ACL - SCHEMA)\s+-\s+(\w+)/g) || [];
|
|
2008
|
+
schemaRefs.forEach(line => {
|
|
2009
|
+
const match = line.match(/(?:SCHEMA COMMENT|ACL - SCHEMA)\s+-\s+(\w+)/);
|
|
2010
|
+
if (match && match[1] && match[1] !== 'public') {
|
|
2011
|
+
schemas.add(match[1]);
|
|
2012
|
+
}
|
|
2013
|
+
});
|
|
2014
|
+
|
|
2015
|
+
logInfo(`pg_restore --list ejecutado correctamente`);
|
|
2016
|
+
} else {
|
|
2017
|
+
logInfo(`pg_restore --list no devolvió output, buscando en contenido del backup...`);
|
|
2018
|
+
}
|
|
2019
|
+
|
|
2020
|
+
// Si no encontramos schemas con --list, parsear el archivo directamente
|
|
2021
|
+
if (schemas.size === 0) {
|
|
2022
|
+
try {
|
|
2023
|
+
const backupContent = readFileSync(localFilePath, 'utf-8').substring(0, 500000); // Primeros 500KB
|
|
2024
|
+
|
|
2025
|
+
// Buscar patrones "schema.table" o "CREATE TABLE schema."
|
|
2026
|
+
const schemaPatterns = [
|
|
2027
|
+
/CREATE TABLE\s+(\w+)\./gi,
|
|
2028
|
+
/INSERT INTO\s+(\w+)\./gi,
|
|
2029
|
+
/ALTER TABLE\s+(\w+)\./gi,
|
|
2030
|
+
/DROP.*ON\s+(\w+)\./gi,
|
|
2031
|
+
];
|
|
2032
|
+
|
|
2033
|
+
schemaPatterns.forEach(pattern => {
|
|
2034
|
+
const matches = backupContent.matchAll(pattern);
|
|
2035
|
+
for (const match of matches) {
|
|
2036
|
+
if (match[1] && match[1] !== 'public' && match[1] !== 'pg_catalog') {
|
|
2037
|
+
schemas.add(match[1]);
|
|
2038
|
+
}
|
|
2039
|
+
}
|
|
2040
|
+
});
|
|
2041
|
+
|
|
2042
|
+
logInfo('Schemas detectados por análisis directo del backup');
|
|
2043
|
+
} catch (parseError) {
|
|
2044
|
+
logInfo(`No se pudo parsear directamente: ${parseError.message}`);
|
|
2045
|
+
}
|
|
2046
|
+
}
|
|
2047
|
+
|
|
2048
|
+
} catch (listError) {
|
|
2049
|
+
logInfo(`Error en detección de schemas: ${listError.message}`);
|
|
2050
|
+
}
|
|
2051
|
+
|
|
2052
|
+
// Crear los schemas detectados
|
|
2053
|
+
if (schemas.size > 0) {
|
|
2054
|
+
logInfo(`Schemas detectados: ${Array.from(schemas).join(', ')}`);
|
|
2055
|
+
await addCommandLog('info', `Creando schemas: ${Array.from(schemas).join(', ')}`);
|
|
2056
|
+
|
|
2057
|
+
const psqlPath = pgRestorePath.replace('pg_restore', 'psql');
|
|
2058
|
+
const psqlExe = psqlPath.replace('.exe', '') + '.exe';
|
|
2059
|
+
|
|
2060
|
+
// Verificar que psql existe
|
|
2061
|
+
if (!existsSync(psqlExe)) {
|
|
2062
|
+
logInfo('psql no disponible, schemas deben existir previamente');
|
|
2063
|
+
} else {
|
|
2064
|
+
// Crear cada schema
|
|
2065
|
+
for (const schemaName of schemas) {
|
|
2066
|
+
try {
|
|
2067
|
+
const createSchemaSQL = `CREATE SCHEMA IF NOT EXISTS "${schemaName}";`;
|
|
2068
|
+
const psqlArgs = [];
|
|
2069
|
+
|
|
2070
|
+
// Conexión (misma que pg_restore)
|
|
2071
|
+
if (target_environment.connection_type === 'supabase') {
|
|
2072
|
+
const projectRef = target_environment.supabase_project_ref;
|
|
2073
|
+
psqlArgs.push(`--host=db.${projectRef}.supabase.co`);
|
|
2074
|
+
psqlArgs.push(`--port=5432`);
|
|
2075
|
+
psqlArgs.push(`--username=postgres`);
|
|
2076
|
+
psqlArgs.push(`--dbname=postgres`);
|
|
2077
|
+
} else if (target_environment.connection_type === 'postgresql') {
|
|
2078
|
+
psqlArgs.push(`--host=${target_environment.db_host}`);
|
|
2079
|
+
psqlArgs.push(`--port=${target_environment.db_port || 5432}`);
|
|
2080
|
+
psqlArgs.push(`--username=${target_environment.db_user}`);
|
|
2081
|
+
psqlArgs.push(`--dbname=${target_environment.db_name}`);
|
|
2082
|
+
}
|
|
2083
|
+
|
|
2084
|
+
psqlArgs.push('--command', createSchemaSQL);
|
|
2085
|
+
psqlArgs.push('--no-psqlrc'); // No cargar archivos de configuración
|
|
2086
|
+
psqlArgs.push('--quiet'); // Silencioso
|
|
2087
|
+
|
|
2088
|
+
logInfo(`Creando schema: ${schemaName}`);
|
|
2089
|
+
|
|
2090
|
+
const createResult = spawnSync(psqlExe, psqlArgs, {
|
|
2091
|
+
env: process.env,
|
|
2092
|
+
encoding: 'utf-8',
|
|
2093
|
+
stdio: 'pipe',
|
|
2094
|
+
});
|
|
2095
|
+
|
|
2096
|
+
if (createResult.status === 0) {
|
|
2097
|
+
logSuccess(`✓ Schema "${schemaName}" creado`);
|
|
2098
|
+
} else {
|
|
2099
|
+
const stderr = createResult.stderr || '';
|
|
2100
|
+
if (stderr.includes('already exists')) {
|
|
2101
|
+
logInfo(`✓ Schema "${schemaName}" ya existe`);
|
|
2102
|
+
} else if (stderr.includes('permission denied')) {
|
|
2103
|
+
logError(`✗ Sin permisos para crear schema "${schemaName}"`);
|
|
2104
|
+
throw new Error(`No hay permisos para crear el schema "${schemaName}"`);
|
|
2105
|
+
} else {
|
|
2106
|
+
logInfo(`⚠ Schema "${schemaName}": ${stderr.substring(0, 200)}`);
|
|
2107
|
+
}
|
|
2108
|
+
}
|
|
2109
|
+
} catch (schemaError) {
|
|
2110
|
+
logError(`Error creando schema "${schemaName}": ${schemaError.message}`);
|
|
2111
|
+
throw schemaError;
|
|
2112
|
+
}
|
|
2113
|
+
}
|
|
2114
|
+
}
|
|
2115
|
+
} else {
|
|
2116
|
+
logInfo('No se detectaron schemas personalizados (solo public)');
|
|
2117
|
+
}
|
|
2118
|
+
|
|
2119
|
+
// PASO 2: Construir argumentos de pg_restore
|
|
1962
2120
|
const pgRestoreArgs = [];
|
|
1963
2121
|
|
|
1964
2122
|
// Conexión
|