Compare commits
6
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1d661e2e48 | ||
|
|
2ee956665f | ||
|
|
0d5db42c77 | ||
|
|
21127b8cff | ||
|
|
50a9dc5183 | ||
|
|
a69df1d2d6 |
+1
-1
@@ -9,7 +9,7 @@
|
||||
"start:dev": "nest start --watch",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"test": "tsc -p tsconfig.test.json --noEmit && node --import tsx --test test/**/*.test.ts",
|
||||
"migration:run": "node dist/database/migration-cli.js run && node dist/cli/fresh-start-reset.js --preview",
|
||||
"migration:run": "node dist/database/migration-cli.js run && node dist/cli/fresh-start-reset.js --apply DHV2-FRESH-START-20260905",
|
||||
"migration:show": "node dist/database/migration-cli.js show",
|
||||
"migration:revert": "node dist/database/migration-cli.js revert",
|
||||
"bootstrap:admin": "node dist/cli/bootstrap-admin.js",
|
||||
|
||||
@@ -18,8 +18,10 @@ const PRESERVED_TABLES = new Set([
|
||||
'asset_attribute_definitions',
|
||||
'finding_categories',
|
||||
'finding_catalog_items',
|
||||
'finding_catalog_item_versions',
|
||||
'finding_catalog_item_asset_types',
|
||||
'finding_catalog_asset_type_profiles',
|
||||
'institutional_delivery_settings',
|
||||
]);
|
||||
|
||||
type AdminRow = {
|
||||
@@ -126,6 +128,7 @@ async function main(): Promise<void> {
|
||||
for (const table of tablesToPreserve) {
|
||||
process.stdout.write(`KEEP ${table.padEnd(46)} ${await rowCount(table)}\n`);
|
||||
}
|
||||
process.stdout.write('RESET institutional_delivery_settings emails/updated_by\n');
|
||||
|
||||
process.stdout.write('\n========== DATOS A ELIMINAR ==========\n');
|
||||
let rowsToDelete = 0;
|
||||
@@ -147,12 +150,28 @@ async function main(): Promise<void> {
|
||||
}
|
||||
|
||||
await migrationDataSource.transaction(async (manager) => {
|
||||
// Se vacía el grafo operacional completo sin depender del orden físico
|
||||
// de las FK. La estructura maestra está excluida por PRESERVED_TABLES.
|
||||
await manager.query(`SET LOCAL session_replication_role = replica`);
|
||||
|
||||
for (const table of tablesToClear) {
|
||||
await manager.query(`DELETE FROM public.${quoteIdentifier(table)}`);
|
||||
}
|
||||
|
||||
// Antes de tocar cualquier tabla preservada reactivamos las FK reales.
|
||||
// Si apareciera una dependencia RESTRICT inesperada, PostgreSQL aborta
|
||||
// la transacción completa en lugar de dejar referencias huérfanas.
|
||||
await manager.query(`SET LOCAL session_replication_role = origin`);
|
||||
|
||||
await manager.query(`
|
||||
UPDATE institutional_delivery_settings
|
||||
SET office_email = NULL,
|
||||
director_email = NULL,
|
||||
updated_by = NULL,
|
||||
updated_at = CURRENT_TIMESTAMP
|
||||
WHERE id = 1
|
||||
`);
|
||||
|
||||
await manager.query(
|
||||
`DELETE FROM user_roles WHERE user_id <> $1::uuid`,
|
||||
[admin.id],
|
||||
@@ -224,7 +243,8 @@ async function main(): Promise<void> {
|
||||
process.stdout.write('\n========== VERIFICACIÓN FRESH START ==========\n');
|
||||
process.stdout.write(`Usuarios: 1 (${admin.username})\n`);
|
||||
process.stdout.write('Datos operativos/importados: 0\n');
|
||||
process.stdout.write('Roles/permisos/tipos/catálogos estructurales: conservados\n');
|
||||
process.stdout.write('Roles/permisos/tipos/catálogo base: conservados\n');
|
||||
process.stdout.write('Configuración institucional editable: reiniciada\n');
|
||||
process.stdout.write('FRESH_START_OK\n');
|
||||
} finally {
|
||||
if (migrationDataSource.isInitialized) {
|
||||
|
||||
@@ -114,7 +114,7 @@ rollback() {
|
||||
echo
|
||||
if [ -d "$BACKUP" ]; then
|
||||
echo "Backup PRE disponible en: $BACKUP"
|
||||
echo "Las migraciones son forward-only; database-before.dump queda disponible para restauración manual si hiciera falta."
|
||||
echo "database-before.dump y asset-media-before.tar.gz permiten una restauración manual integral si hiciera falta."
|
||||
else
|
||||
echo "No fue necesario crear backup PRE: el fallo ocurrió durante el preflight del candidato, antes de tocar producción."
|
||||
fi
|
||||
@@ -207,9 +207,15 @@ install -m 600 .env "$BACKUP/.env"
|
||||
git rev-parse HEAD > "$BACKUP/previous.sha"
|
||||
printf '%s\n' "$TARGET_SHA" > "$BACKUP/target.sha"
|
||||
docker compose ps -a > "$BACKUP/docker-before.txt"
|
||||
|
||||
echo "Respaldando volumen de fotos/importaciones..."
|
||||
docker compose run --rm --no-deps --user root \
|
||||
-v "$BACKUP:/backup" \
|
||||
api sh -lc 'tar -czf /backup/asset-media-before.tar.gz -C /app/storage/asset-media .'
|
||||
|
||||
(
|
||||
cd "$BACKUP"
|
||||
sha256sum database-before.dump source-before.tar.gz .env previous.sha target.sha docker-before.txt > SHA256SUMS.txt
|
||||
sha256sum database-before.dump source-before.tar.gz asset-media-before.tar.gz .env previous.sha target.sha docker-before.txt > SHA256SUMS.txt
|
||||
sha256sum -c SHA256SUMS.txt
|
||||
)
|
||||
chmod 600 "$BACKUP"/* "$BACKUP/.env" 2>/dev/null || true
|
||||
@@ -228,11 +234,31 @@ docker compose build api migrate web </dev/null
|
||||
|
||||
PHASE="migrations"
|
||||
echo
|
||||
echo "========== MIGRACIONES =========="
|
||||
echo "========== MIGRACIONES + FRESH START =========="
|
||||
docker compose --profile tools run --rm migrate </dev/null
|
||||
docker compose --profile tools run --rm migrate npm run migration:show </dev/null | tee "$BACKUP/migrations.txt"
|
||||
grep -Fq 'Pending migrations: no' "$BACKUP/migrations.txt"
|
||||
|
||||
PHASE="media-clean"
|
||||
echo
|
||||
echo "========== LIMPIEZA DE ARCHIVOS CARGADOS =========="
|
||||
docker compose run --rm --no-deps --user root api sh -lc '
|
||||
set -eu
|
||||
ROOT=/app/storage/asset-media
|
||||
before="$(find "$ROOT" -type f 2>/dev/null | wc -l | tr -d " ")"
|
||||
echo "files_before=$before"
|
||||
for path in "$ROOT"/* "$ROOT"/.[!.]* "$ROOT"/..?*; do
|
||||
[ -e "$path" ] || continue
|
||||
rm -rf -- "$path"
|
||||
done
|
||||
mkdir -p "$ROOT/imports"
|
||||
chown -R node:node "$ROOT"
|
||||
after="$(find "$ROOT" -type f 2>/dev/null | wc -l | tr -d " ")"
|
||||
echo "files_after=$after"
|
||||
[ "$after" = "0" ]
|
||||
' | tee "$BACKUP/media-clean.txt"
|
||||
grep -Fq 'files_after=0' "$BACKUP/media-clean.txt"
|
||||
|
||||
PHASE="recreate"
|
||||
echo
|
||||
echo "========== RECREATE API + WEB =========="
|
||||
@@ -284,7 +310,7 @@ git rev-parse HEAD > "$BACKUP/deployed.sha"
|
||||
printf 'API=%s\nWEB=%s\n' "$EXPECTED_API_VERSION" "$EXPECTED_WEB_VERSION" > "$BACKUP/deployed-versions.txt"
|
||||
(
|
||||
cd "$BACKUP"
|
||||
sha256sum database-after.dump deployed.sha deployed-versions.txt health.json migrations.txt docker-after.txt >> SHA256SUMS.txt
|
||||
sha256sum database-after.dump deployed.sha deployed-versions.txt health.json migrations.txt media-clean.txt docker-after.txt >> SHA256SUMS.txt
|
||||
sha256sum -c SHA256SUMS.txt
|
||||
)
|
||||
chmod 600 "$BACKUP"/* "$BACKUP/.env" 2>/dev/null || true
|
||||
|
||||
Reference in New Issue
Block a user