Skip to content

feat(utilities): Backup & Restore — commands per engine, with saved defaults - #477

Merged
huyplb merged 2 commits into
mainfrom
feat/backup-restore
Oct 4, 2026
Merged

huyplb merged 2 commits into
mainfrom
feat/backup-restore

Conversation

@huyplb

@huyplb huyplb commented Oct 4, 2026 •

Copy link
Copy Markdown
Contributor

A new Backup & Restore tool in the Utilities workspace. It writes the backup and restore commands for the chosen connection, on every engine Fox Schema connects to. Like Database Access, Fox Schema writes the commands and never runs them.

What a user sees

  • Where it runs, first. That decides what "folder" means:

    • "Runs on your machine": pg_dump, mysqldump, SqlPackage, sqlite3, mongodump, redis-cli;
    • "Runs on the database server": SQL Server, Oracle Data Pump (a DIRECTORY object), Db2, ClickHouse (a backup disk), CockroachDB (a collection URI), DuckDB;
    • "Kept by the cloud provider": Redshift snapshots.

    A folder typed on the wrong side of that line is how a backup "succeeds" into nowhere.

  • Options, where the engine has them:

    • folder and file name;
    • format: pg_dump custom, plain, directory or tar; SQLite file copy or SQL dump; DuckDB Parquet or CSV;
    • what to back up: Everything / Structure only (DDL) / Rows only (data);
    • compression;
    • only the connection's schema;
    • only some tables.
  • Backup and Restore blocks with Copy. SQL-language engines also get "Open in SQL Editor" on this connection.

  • Notes:

    • how the tool gets the password (no command ever contains one);
    • restores that overwrite live data, which say so;
    • CockroachDB, ClickHouse and Azure SQL restore beside the live database under a new name.
  • "Save as my default for " keeps folder, format, scope, compression and schema limit per user and engine, server-side.

Screenshots: apps/e2e/screenshots/backup-restore-postgres_*.png and backup-restore-sqlserver_*.png (local run).

Design

  • @foxschema/sql, pure generation:
    • modules/utilities/backup.ts (facade: settle settings, name the file, build);
    • backup.registry.ts;
    • backup-helpers.ts (shell, SQL and path quoting);
    • one providers/<engine>/<engine>.backup.ts per engine, following docs/CONVENTIONS.md.
  • Server:
    • metadata migration 26 backup_settings (user_id, dialect, JSON settings, PK both). Append-only.
    • GET /api/backup-settings and PUT /api/backup-settings/:dialect, signed-in user only. The contract test is updated (113 → 115).
    • Settings are re-validated through normalizeBackupSettings on every read and write, so a stale or hand-edited row comes back as something the engine can honour.
  • Web: BackupRestorePanel.tsx, shared/api/backupApi.ts, wired into UtilitiesView.

Verification

  • Unit, 29 tests (backup.test.ts):
    • every engine's commands, exactly;
    • no password in any command;
    • quoting of spaces, ' and ];
    • Windows paths;
    • settings normalization from arbitrary JSON;
    • file naming;
    • every dialect in DIALECTS covered.
  • Server, 6 tests (backup-settings.service.test.ts): save, settle, replace, per-user isolation, unknown engine, a damaged row.
  • Panel, 7 tests: command updates as options change; a saved default loads and saves; the table filter survives a save; the SQL Server editor hand-off; no connection; an unknown engine; failed loading. Also a race where an edit made before saved defaults arrive was lost; it was found while testing, fixed, and A/B-checked.
  • Live, against the dev containers:
    • Postgres: the generated pg_dump (custom) and pg_restore, and plain + gzip with psql, run inside the container into scratch databases. 45 of 45 tables came back. Cleaned up.
    • SQL Server: the generated BACKUP DATABASE, then RESTORE VERIFYONLY on the file, through the product's SQL runner (backup-commands-dialects.test.ts).
    • DuckDB: EXPORT DATABASE ran (schema.sql and load.sql written).
    • ClickHouse: the statement parsed. The server refused only because it has no backups.allowed_disk, which the suite reports as a skip.
    • CockroachDB: container not running here, so skipped. Not verified live.
    • mysqldump, mariadb-dump, expdp/impdp, db2 backup/restore, db2look: every flag used appears in the tool's own help in its container. Running them needs the containers' passwords, which I did not read.
  • Browser e2e (backup-restore-ui.test.ts), 3 of 3: says where pg_dump runs; the command has the connection's user and no password; a saved folder survives a reload; SQL Server's backup opens in the SQL editor.
  • npm run typecheck (web + e2e), the unit project (4,974) and web-ui (335): pass. ESLint: clean. Both new suites are registered in run-all.mjs.

🤖 Generated with Claude Code


Note

Medium Risk
New persisted user settings and API surface behind auth; generated restore scripts can be destructive if run blindly, though the product still does not execute backups.

Overview
Adds Backup & Restore to Database utilities: a new pane that generates backup/restore commands for the workspace connection, shows where they run (client vs server vs cloud), and exposes folder, format, scope, compression, schema/table filters, copy, and Open in SQL Editor for SQL engines.

@foxschema/sql gains a backup module (buildBackupCommands, settings normalization, registry) with per-dialect builders for every supported engine; commands never embed passwords.

Server: migration 26 (backup_settings per user/dialect), authenticated GET /api/backup-settings and PUT /api/backup-settings/:dialect, with settings normalized on read/write.

Tests: unit coverage for SQL generation, panel behavior, and settings service; browser e2e for the panel and saved defaults; e2e that runs generated SQL on SQL Server, CockroachDB, ClickHouse, and DuckDB when configured. test:backup and run-all.mjs register the new suites; test IDs/docs updated.

Reviewed by Cursor Bugbot for commit 91dc223. Bugbot is set up for automated code reviews on this repo. Configure here.

…efaults

A new Utilities tool that writes the backup and restore commands for the
chosen connection. Fox Schema writes them and does not run them.

- Where the command runs comes first, because it decides what "folder"
  means: pg_dump writes where it runs, SQL Server's BACKUP DATABASE on the
  database server, Oracle Data Pump into a DIRECTORY object, Redshift keeps
  a snapshot in AWS.
- Every engine Fox Schema connects to (16): pg_dump/pg_restore,
  ysql_dump, CockroachDB BACKUP/RESTORE, Redshift snapshots via the AWS CLI,
  mysqldump / mariadb-dump (TiDB without routines), SQL Server BACKUP
  DATABASE (COPY_ONLY), Azure SQL SqlPackage, Oracle expdp/impdp, Db2
  BACKUP/RESTORE and db2look, ClickHouse BACKUP to a disk, DuckDB EXPORT,
  sqlite3 .backup/.dump, mongodump, redis-cli --rdb.
- Options per engine: folder, format, what to back up (everything,
  structure only (DDL), rows only), compression, only the connection's
  schema, only some tables. Restores that would overwrite live data say so;
  CockroachDB, ClickHouse and Azure SQL restore beside it under a new name.
- No command contains a password; each says how its tool asks for one.
- Folder, format and scope save as the user's default per engine:
  metadata migration 26 (backup_settings), GET/PUT /api/backup-settings,
  re-validated on every read.

Pure command generation in @foxschema/sql (one provider file per engine,
registry, facade); the server stores defaults; the panel is
features/utilities/components/BackupRestorePanel.tsx.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@cursor

cursor Bot commented Oct 4, 2026

Copy link
Copy Markdown
Contributor

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: e38af7e4-9a34-4dae-96dc-a9f6ef3b4a1f)

@github-actions

github-actions Bot commented Oct 4, 2026

Copy link
Copy Markdown
Contributor

Test IDs

Added (27)

  • backup-command
  • backup-command-copy
  • backup-command-open-sql
  • backup-command-text
  • backup-compress
  • backup-error
  • backup-file-name
  • backup-folder
  • backup-format
  • backup-format-{…}
  • backup-limit-schema
  • backup-no-connection
  • backup-notes
  • backup-panel
  • backup-reset
  • backup-runs-on
  • backup-save-default
  • backup-save-status
  • backup-scope
  • backup-scope-{…}
  • backup-tables
  • backup-unsupported
  • restore-command
  • restore-command-copy
  • restore-command-open-sql
  • restore-command-text
  • utilities-backup

{…} is a part filled in at run time. The full tree: docs/testing/TEST_IDS.md.

The file name is the reader's to edit, and expdp/impdp took DIRECTORY=,
DUMPFILE= and LOGFILE= unquoted, as did the Db2 commands the database name:
a space or `;` there split the copied command. Each is now one shell word,
bare when it is plainly safe, so ordinary commands read as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@cursor

cursor Bot commented Oct 4, 2026

Copy link
Copy Markdown
Contributor

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: a3dcd837-3c82-4c21-aec1-971a7f635576)

@huyplb
huyplb merged commit 546deac into main Oct 4, 2026
13 checks passed
@huyplb
huyplb deleted the feat/backup-restore branch October 4, 2026 23:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant