7 Commits
Author SHA1 Message Date
DumiFlex e3caa710f9 fix(backups): restore a backup at once, without restarting ComfyUI (#247)
Restore copied the backup file over the library at the next start because swapping a SQLite file under open connections corrupts it. It now copies the backup into the open library through SQLite's online backup API, after checking it and saving the library as a pre-restore backup, then migrates it. The Backups panel refreshes the library and offers Undo.
2026-11-03 19:27:50 +01:00
DumiFlex 670bae4388 feat(backups): pin and label backups, and Backups to keep covers manual ones (#194)
Manual backups were never pruned, so the list filled up. Pruning now keeps the newest unpinned backups of every reason, and Back up now prunes too. A pin (a .pin marker file beside the backup) exempts a backup from pruning and from the count; the backup staged for restore is never pruned either. Existing manual backups are pinned once so none vanish on update.

Back up now takes an optional short note, kept in a .note file beside the backup and shown in the list instead of Manual.
2026-10-29 13:50:03 +01:00
Claude f86b554616 feat(engine): server-side settings, runtime ref depth limit and database backups
The Settings page's ref recursion limit was stored in localStorage and read
by nothing: every run stopped at 8. Server-side preferences now live in
user/wildcard-pipeline/settings.json (engine/prefs.py, /wp/api/settings) and
PipelineEngine.run seeds __wp_max_ref_depth__ from them, so canvas runs,
previews and the Test Runner honour it.

Database backups (engine/db/backups.py): a copy before any migration that
changes the schema, a daily copy while ComfyUI runs, pruning of automatic
copies beyond the configured count (manual ones are kept), and a restore
that is staged in the DB sidecar and applied on the next start before any
connection opens, after backing up the current database.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BMHPZ4k2kXrePkcgeYJJSF
2026-09-30 09:26:44 +00:00
DumiFlex 05334d3e05 fix(api): confine the deferred DB move to known database locations
`PUT /wp/api/database/config` accepted `pending_move.from` and `.to` as
any absolute paths. `execute_pending_move` then runs `shutil.copy2` or
`shutil.move` on them at plugin load, as whoever runs ComfyUI. Every
ComfyUI route is unauthenticated by default, so anyone able to reach the
port could schedule:

  * an arbitrary file read — copy a target into `web/`, which this plugin
    serves at `/wp/<path>`, turning our own static server into the
    exfiltration channel;
  * an arbitrary overwrite or deletion — move any file anywhere.

Fixed with an allowlist rather than a sanitiser. The database can
legitimately live in exactly three server-computed locations under one
fixed filename, so there is no reason to accept a client-supplied path.
The live path is also allowed, since WP_DB_PATH / COMFYUI_USER_DIR can
place the file elsewhere and those users must still be able to migrate
off it.

Both paths are `resolve()`d before the membership test, so neither `..`
nor a symlink planted at an allowed location can smuggle a different
target past it, and the resolved values are what get stored — what
executes is what was validated.

`spa.py` already did this correctly (`resolve()` + `relative_to()` + 403);
this route simply missed the pattern.

Adds 7 containment tests covering arbitrary source, arbitrary
destination, the exfiltration-into-`web/` chain, traversal, and a no-op
move — plus one asserting a legitimate relocation still works, since a
guard that breaks the feature it protects is its own bug.
2026-08-04 21:36:59 +03:00
DumiFlex 04d3795103 feat(api): expose db config get/put endpoints with pending-move 2026-06-03 02:16:51 +03:00
DumiFlex 0909817018 feat(api): add POST /wp/api/database/maintenance 2026-06-03 00:19:59 +03:00
DumiFlex dc839bfb99 feat(api): add GET /wp/api/database/info 2026-06-03 00:15:04 +03:00