Files
Codex f5997b47d9
CI / Frontend build (push) Successful in 13s
CI / Script syntax (push) Successful in 3s
CI / Backend tests (push) Failing after 17s
Add Gitea CI workflow
2026-07-15 01:10:59 +02:00

6.4 KiB

Agent notes for PVE Cloud Backup

This project is a directly installed Proxmox backup web app. It runs from:

/opt/pve-cloud-backup/
  backend/    FastAPI app, worker, SQLite migrations, tests
  frontend/   Vue 3 + TypeScript + Tailwind source
  static/     generated frontend build; ignored by git
  data/       runtime SQLite database; ignored by git
  logs/       runtime logs; ignored by git
  scripts/    install/update/uninstall/manual integration scripts
  systemd/    source copies of service units

The live services are:

  • pve-cloud-backup-web.service
  • pve-cloud-backup-worker.service

The web app listens on 0.0.0.0:8080.

Core rules

  • Keep runtime configuration in SQLite only. Do not add .env, YAML, JSON, TOML, or other runtime config files.
  • Do not commit /opt/pve-cloud-backup/data/, /logs/, /static/, .venv, node_modules, caches, or generated artifacts.
  • Do not use Docker or add distributed-worker infrastructure.
  • Do not call vzdump directly. Use pvesh for backup operations.
  • Do not require a crypt remote. Plain OneDrive remotes and crypt remotes are both allowed.
  • Do not use rclone sync.
  • Use exact-object rclone operations:
    • upload with copyto;
    • verify with lsjson;
    • delete files with deletefile;
    • remove empty per-backup folders with rmdir.
  • Never delete a local archive unless upload completed and the remote object was verified.
  • Mock pvesh and rclone in automated tests. Manual integration testing is limited to scripts/integration-test.sh and must only run when the user explicitly asks for a real backup test.

Runtime data

SQLite database:

/opt/pve-cloud-backup/data/app.db

Important runtime settings live in SQLite, not files:

  • Proxmox node
  • Proxmox backup storage
  • local dump directory
  • rclone executable path
  • rclone remote name
  • remote path inside that rclone remote
  • timezone
  • retention defaults
  • Discord webhook URL
  • CORS origins

Treat all node names, storage names, guest names, VMIDs, rclone remotes, and filesystem paths as operator-specific runtime data. Do not hardcode deployment-specific values in application code or publish-facing docs.

Backend map

  • backend/app/main.py
    • FastAPI app, API routes, setup/settings validation, static frontend serving.
  • backend/app/backup_service.py
    • Backup state machine, upload/delete/retention/recovery logic.
  • backend/app/commands.py
    • Shell wrappers for pvesh and rclone.
  • backend/app/jobs.py
    • Cron validation, timezone-aware next-run calculation, job CRUD helpers.
  • backend/app/migrations.py
    • SQLite schema and default settings.
  • backend/app/settings_store.py
    • SQLite settings serialization/parsing.
  • backend/app/worker.py
    • Scheduler/worker entrypoint.
  • backend/tests/
    • Unit tests. Keep external command usage mocked.

Frontend map

  • frontend/src/App.vue
    • Single-file UI for setup, dashboard, guests, jobs, history, backup details, and settings.
  • frontend/src/main.ts
    • Vue mount entrypoint.
  • frontend/src/style.css
    • Tailwind/global styling.

The built frontend goes to /opt/pve-cloud-backup/static/ via npm run build; do not commit that directory.

Backup workflow

Expected states:

  • queued
  • pve_running
  • local_ready
  • uploading
  • remote_ready
  • local_deleting
  • completed
  • failed
  • deleting
  • deleted

Important behavior:

  • The worker atomically claims queued backups before starting Proxmox work.
  • pve_running must have a real Proxmox UPID before polling task status.
  • Local archive discovery must ignore .log and .notes; only real archive suffixes count.
  • Remote path shape is:
<rclone_remote>:<rclone_remote_path>/<backup_id>/<archive_name>
  • Deleting a backup removes the exact remote file and then attempts to remove the empty <backup_id> folder.
  • Deleting a backup already in deleted state purges the SQLite metadata row.
  • Deleting a job deletes all known non-active backups for that job first, then removes the job.
  • Retention runs after backup completion and hourly from the worker.

Applying updates after pulling changes

After a git pull, apply the new code with:

cd /opt/pve-cloud-backup
./scripts/apply-update.sh

The script:

  1. backs up data/app.db to data/update-backups/;
  2. stops web/worker services;
  3. creates/updates backend/.venv;
  4. installs backend requirements;
  5. optionally runs backend tests if RUN_TESTS=1;
  6. runs npm ci or npm install;
  7. builds frontend assets into static/;
  8. runs SQLite migrations;
  9. installs systemd units from systemd/;
  10. reloads systemd;
  11. restarts and health-checks services.

Use this stricter variant when practical:

cd /opt/pve-cloud-backup
git pull --ff-only
RUN_TESTS=1 ./scripts/apply-update.sh

If the health check fails, inspect:

journalctl -u pve-cloud-backup-web.service -u pve-cloud-backup-worker.service --no-pager -n 100

Validation commands

CI workflow:

.gitea/workflows/ci.yml

The workflow is for Gitea Actions and runs backend tests, frontend build, and shell script syntax checks. It uses setup-action package caching for pip and npm, keyed from backend/requirements.txt and frontend/package-lock.json.

Backend tests:

cd /opt/pve-cloud-backup/backend
./.venv/bin/pytest

Frontend typecheck/build:

cd /opt/pve-cloud-backup/frontend
npm run build

Service status:

systemctl is-active pve-cloud-backup-web.service pve-cloud-backup-worker.service

API smoke checks:

curl -fsS http://127.0.0.1:8080/api/setup/status
curl -fsS http://127.0.0.1:8080/api/jobs | python3 -m json.tool
curl -fsS http://127.0.0.1:8080/api/backups | python3 -m json.tool

Manual integration test:

INTEGRATION_VMID=<vmid> API_URL=http://127.0.0.1:8080 /opt/pve-cloud-backup/scripts/integration-test.sh

Only run the manual integration test when the user explicitly wants a real Proxmox/rclone backup test.

Git workflow

Default branch:

main

Do not document private deployment remotes, hostnames, or organization names in committed files. Local git remotes belong in .git/config, not project documentation.

Before committing, check:

git status --short --ignored

Confirm ignored runtime files stay ignored:

git check-ignore -v data/app.db logs/worker.log static/index.html frontend/node_modules/.package-lock.json backend/.venv/pyvenv.cfg