Initial pve cloud backup app
This commit is contained in:
@@ -0,0 +1,149 @@
|
||||
from pathlib import Path
|
||||
|
||||
from .db import db, utc_now
|
||||
|
||||
|
||||
def _system_timezone() -> str:
|
||||
timezone_file = Path("/etc/timezone")
|
||||
if timezone_file.exists():
|
||||
value = timezone_file.read_text().strip()
|
||||
if value:
|
||||
return value
|
||||
return "UTC"
|
||||
|
||||
|
||||
DEFAULT_SETTINGS = {
|
||||
"setup_complete": "false",
|
||||
"proxmox_node": "",
|
||||
"proxmox_storage": "",
|
||||
"local_backup_dir": "/var/lib/vz/dump",
|
||||
"rclone_path": "/usr/bin/rclone",
|
||||
"rclone_remote": "",
|
||||
"rclone_remote_path": "pve-cloud-backup",
|
||||
"discord_webhook_url": "",
|
||||
"allowed_cors_origins": "http://localhost:5173,http://127.0.0.1:5173",
|
||||
"default_compression": "zstd",
|
||||
"default_backup_mode": "snapshot",
|
||||
"default_retention_type": "days",
|
||||
"default_retention_value": "30",
|
||||
"max_concurrent_backups": "1",
|
||||
"timezone": _system_timezone(),
|
||||
}
|
||||
|
||||
|
||||
def run_migrations() -> None:
|
||||
with db() as conn:
|
||||
conn.execute(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS schema_migrations (
|
||||
version INTEGER PRIMARY KEY,
|
||||
applied_at TEXT NOT NULL
|
||||
)
|
||||
"""
|
||||
)
|
||||
applied = {
|
||||
row["version"]
|
||||
for row in conn.execute("SELECT version FROM schema_migrations").fetchall()
|
||||
}
|
||||
if 1 not in applied:
|
||||
conn.executescript(
|
||||
"""
|
||||
CREATE TABLE settings (
|
||||
key TEXT PRIMARY KEY,
|
||||
value TEXT NOT NULL,
|
||||
updated_at TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE backup_jobs (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
guest_vmid INTEGER NOT NULL,
|
||||
guest_name TEXT NOT NULL,
|
||||
guest_type TEXT NOT NULL CHECK (guest_type IN ('vm','lxc')),
|
||||
node TEXT NOT NULL,
|
||||
enabled INTEGER NOT NULL DEFAULT 1,
|
||||
cron_schedule TEXT NOT NULL,
|
||||
proxmox_storage TEXT NOT NULL,
|
||||
backup_mode TEXT NOT NULL,
|
||||
compression TEXT NOT NULL,
|
||||
retention_type TEXT NOT NULL CHECK (retention_type IN ('days','latest')),
|
||||
retention_value INTEGER NOT NULL,
|
||||
next_run_at TEXT,
|
||||
created_at TEXT NOT NULL,
|
||||
updated_at TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE backups (
|
||||
id TEXT PRIMARY KEY,
|
||||
job_id INTEGER,
|
||||
guest_vmid INTEGER NOT NULL,
|
||||
guest_name TEXT NOT NULL,
|
||||
guest_type TEXT NOT NULL,
|
||||
node TEXT NOT NULL,
|
||||
state TEXT NOT NULL,
|
||||
upid TEXT,
|
||||
local_path TEXT,
|
||||
remote_path TEXT,
|
||||
size_bytes INTEGER,
|
||||
error_message TEXT,
|
||||
started_at TEXT NOT NULL,
|
||||
pve_completed_at TEXT,
|
||||
upload_started_at TEXT,
|
||||
completed_at TEXT,
|
||||
deleted_at TEXT,
|
||||
updated_at TEXT NOT NULL,
|
||||
FOREIGN KEY (job_id) REFERENCES backup_jobs(id) ON DELETE SET NULL
|
||||
);
|
||||
|
||||
CREATE TABLE notification_logs (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
event_type TEXT NOT NULL,
|
||||
backup_id TEXT,
|
||||
payload TEXT NOT NULL,
|
||||
success INTEGER NOT NULL,
|
||||
error_message TEXT,
|
||||
created_at TEXT NOT NULL,
|
||||
FOREIGN KEY (backup_id) REFERENCES backups(id) ON DELETE SET NULL
|
||||
);
|
||||
|
||||
CREATE INDEX idx_backup_jobs_enabled_next_run ON backup_jobs(enabled, next_run_at);
|
||||
CREATE INDEX idx_backups_guest_state ON backups(guest_vmid, state);
|
||||
CREATE INDEX idx_backups_state ON backups(state);
|
||||
"""
|
||||
)
|
||||
now = utc_now()
|
||||
conn.executemany(
|
||||
"INSERT INTO settings(key, value, updated_at) VALUES (?, ?, ?)",
|
||||
[(key, value, now) for key, value in DEFAULT_SETTINGS.items()],
|
||||
)
|
||||
conn.execute(
|
||||
"INSERT INTO schema_migrations(version, applied_at) VALUES (?, ?)",
|
||||
(1, now),
|
||||
)
|
||||
if 2 not in applied:
|
||||
now = utc_now()
|
||||
conn.execute(
|
||||
"""
|
||||
INSERT INTO settings(key, value, updated_at)
|
||||
VALUES ('rclone_remote_path', 'pve-cloud-backup', ?)
|
||||
ON CONFLICT(key) DO NOTHING
|
||||
""",
|
||||
(now,),
|
||||
)
|
||||
conn.execute(
|
||||
"INSERT INTO schema_migrations(version, applied_at) VALUES (?, ?)",
|
||||
(2, now),
|
||||
)
|
||||
if 3 not in applied:
|
||||
now = utc_now()
|
||||
conn.execute(
|
||||
"""
|
||||
INSERT INTO settings(key, value, updated_at)
|
||||
VALUES ('timezone', ?, ?)
|
||||
ON CONFLICT(key) DO NOTHING
|
||||
""",
|
||||
(DEFAULT_SETTINGS["timezone"], now),
|
||||
)
|
||||
conn.execute(
|
||||
"INSERT INTO schema_migrations(version, applied_at) VALUES (?, ?)",
|
||||
(3, now),
|
||||
)
|
||||
Reference in New Issue
Block a user