fix(ops): run backups as an admin login, and stop recording failed dumps as good
The Operaciones panel (backup, restore, sync, re-import) shelled out to mysqldump as the application user, parsed straight out of DATABASE_URL. `--single-transaction` issues FLUSH TABLES, which needs the global RELOAD privilege, and the app user is granted only ALL ON jorgecuadros.* plus USAGE ON *.*. BACKUP failed outright; SYNC and REIMPORT failed with it, since both take a safety backup first. An admin credential is now supplied out of band via OPS_DB_ADMIN_USER / OPS_DB_ADMIN_PASSWORD, mirroring what deploy/scripts/pre-migrate-backup.mjs already does, rather than permanently elevating the user the API serves requests as. Host, port and database still come from DATABASE_URL, so the override can only change who logs in, never which server. Unset, it falls back to the DATABASE_URL credentials and warns — local development is unaffected. Two defects in the dumps themselves, both shared with the deploy backup before it was rewritten: - No --set-gtid-purged=OFF. The production server is the replication source with GTID on, so every dump embedded SET @@GLOBAL.GTID_PURGED and was unrestorable onto the server it came from — the one thing the restore screen is for. - The pipeline's exit status was gzip's, and gzip succeeded. A mysqldump that died on its first statement left a small, perfectly valid archive that the job recorded as SUCCESS and the restore screen listed as an ordinary restore point. Dumps now run under `set -o pipefail`, assert a CREATE TABLE count, and delete their own output on failure. Verified with a stubbed mysqldump: a failing dump exits 1, surfaces the real error, removes the partial file, and — critically — stops SYNC/REIMPORT before the ETL touches anything. Restores gained pipefail too: a corrupt archive made gunzip fail while mysql, fed a truncated stream, could still exit 0. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -61,6 +61,14 @@ services:
|
||||
INGEST_DIR: /data/ingest
|
||||
BACKUP_DIR: /data/backups
|
||||
MIGRATION_ENV: prod
|
||||
# Credentials the "Operaciones" screen runs mysqldump/mysql as. NOT the
|
||||
# application user: --single-transaction needs the global RELOAD privilege
|
||||
# and the app user has only ALL ON jorgecuadros.*, so every backup, sync
|
||||
# and re-import fails without this. Host/port/database still come from
|
||||
# DATABASE_URL — this only changes who logs in. See opsConn() in
|
||||
# apps/api/src/ops/ops.service.ts.
|
||||
OPS_DB_ADMIN_USER: ${OPS_DB_ADMIN_USER:-root}
|
||||
OPS_DB_ADMIN_PASSWORD: ${OPS_DB_ADMIN_PASSWORD:?OPS_DB_ADMIN_PASSWORD must be set}
|
||||
S3_ENDPOINT: ${S3_ENDPOINT:?S3_ENDPOINT must be set}
|
||||
S3_BUCKET: ${S3_BUCKET:-jorgecuadros-documents}
|
||||
MINIO_ROOT_USER: ${MINIO_ROOT_USER:?MINIO_ROOT_USER must be set}
|
||||
|
||||
Reference in New Issue
Block a user