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>
- New reports backend (registry, service, controller, outputs, types)
with catalog endpoint + slug/CSV/XLSX/PDF/print outputs.
- /reportes catalog + /reportes/[slug] runner; ReportRunner + ContextReports
components wire pre-filtered links from domain pages.
- Fix: /reportes/[slug] now reads searchParams and forwards initialParams to
ReportRunner so /reportes/edo-cuenta-datos?customerId=... auto-runs
instead of dropping the id and forcing a manual customer search.
- /inicio landing page; root + login redirect to /inicio.
- Company header env vars + logo asset for PDF/print rendering.
- exceljs + pdfkit deps.
Next.js + NestJS + Prisma (MySQL) monorepo replacing the legacy PHP
internal app. Includes a session-based auth module with Argon2 password
hashing and global input validation (replacing the old app's SQL
injection and plaintext password comparison), the full target Prisma
schema for customers/insurance/utilities/shared ledger/bank register,
Docker Compose + Dockerfiles, and an Access-to-staging migration
pipeline (migration/) already run against the real source databases.
See PLAN.md and RESUME.md for the full architecture and session history.