Files
stackpilot/backend/models/backup_schedule.py
T
menzeljandClaude Opus 4.8 5cd55382ed Phase 8: back up & restore remote (agent) stacks (0.8.0)
- Agent: GET /agent/stacks/{id}/backup + POST /agent/stacks/restore (reuse
  backup_service). backup_service gains backup_basename/backup_filename helpers.
- Main proxy streams agent <-> main <-> destination (creds stay central):
  agent_service download_to_file/upload_file; routers/agents.py backup download,
  backup/push, restore upload, restore-from.
- Schedules: BackupSchedule.agent_id; schedule_service downloads from the agent
  when set; per-host filename prefix isolates retention across hosts.
- Frontend: agents api backup/restore; BackupButton/RestoreButton agent-aware
  (Backup on remote stack detail, Restore per host section); schedule form host
  selector (local or an online agent) + host shown on schedule rows.

Rough-verified (per request): py_compile, frontend tsc build, image imports
(main 99 / agent 16 routes). Full live agent round-trip to be tested post-deploy.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 22:26:20 +00:00

85 lines
2.2 KiB
Python

from __future__ import annotations
from datetime import datetime, timezone
from typing import Optional
from sqlmodel import Field, SQLModel
def _now() -> datetime:
return datetime.now(timezone.utc)
FREQUENCIES = ["hourly", "daily", "weekly"]
class BackupSchedule(SQLModel, table=True):
"""An automatic, recurring backup of a local stack to a destination."""
id: Optional[int] = Field(default=None, primary_key=True)
stack_id: str
destination_id: int
agent_id: Optional[int] = None # None = local host; otherwise a remote agent
frequency: str = "daily" # one of FREQUENCIES
hour: int = 3 # UTC, used for daily/weekly
minute: int = 0
weekday: int = 0 # 0=Mon .. 6=Sun, used for weekly
include_volumes: bool = True
stop_first: bool = True
keep: int = 7 # retention: keep newest N for this stack on the dest (0 = all)
enabled: bool = True
last_run: Optional[datetime] = None
last_status: Optional[str] = None # "ok" | "error: ..."
next_run: Optional[datetime] = None
created_at: datetime = Field(default_factory=_now)
# --- API schemas ---
class ScheduleCreate(SQLModel):
stack_id: str
destination_id: int
agent_id: Optional[int] = None
frequency: str = "daily"
hour: int = 3
minute: int = 0
weekday: int = 0
include_volumes: bool = True
stop_first: bool = True
keep: int = 7
enabled: bool = True
class ScheduleUpdate(SQLModel):
destination_id: Optional[int] = None
frequency: Optional[str] = None
hour: Optional[int] = None
minute: Optional[int] = None
weekday: Optional[int] = None
include_volumes: Optional[bool] = None
stop_first: Optional[bool] = None
keep: Optional[int] = None
enabled: Optional[bool] = None
class ScheduleRead(SQLModel):
id: int
stack_id: str
destination_id: int
destination_name: Optional[str]
agent_id: Optional[int]
agent_name: Optional[str]
frequency: str
hour: int
minute: int
weekday: int
include_volumes: bool
stop_first: bool
keep: int
enabled: bool
last_run: Optional[datetime]
last_status: Optional[str]
next_run: Optional[datetime]
created_at: datetime