Phase 7: scheduled (recurring) backups (0.7.0)

- BackupSchedule model + schedule_service: next-run calc (hourly/daily/weekly,
  UTC), background scheduler loop (lifespan), run-one with retention pruning
  (keep newest N per stack on the destination), backup_failed notify event.
- routers/schedules.py: schedules CRUD + run-now; registered in main.py.
- Frontend: api/schedules.ts + Settings → Scheduled backups (list with next/last
  run + status, enable/disable, run-now, delete; add form with stack/destination/
  frequency/time/weekday/retention/volumes).

Rough-verified only (per request): py_compile, frontend tsc build, app import
(95 routes), next-run math sanity. Full live run to be tested after deploy.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
menzelj
2026-06-07 22:02:52 +00:00
co-authored by Claude Opus 4.8
parent 7bd449101d
commit 84ef3df59e
10 changed files with 697 additions and 5 deletions
+6 -2
View File
@@ -22,6 +22,7 @@ from routers import (
editor,
images,
ports,
schedules,
settings as settings_router,
stacks,
system,
@@ -29,7 +30,7 @@ from routers import (
volumes,
ws,
)
from services import update_service
from services import schedule_service, update_service
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger("stackpilot")
@@ -45,12 +46,14 @@ async def lifespan(app: FastAPI):
except Exception as exc: # noqa: BLE001
logger.warning("Stack discovery failed: %s", exc)
update_task = asyncio.create_task(update_service.background_loop())
schedule_task = asyncio.create_task(schedule_service.scheduler_loop())
logger.info("StackPilot backend ready on port %s", settings.PORT)
yield
update_task.cancel()
schedule_task.cancel()
app = FastAPI(title="StackPilot", version="0.6.0", lifespan=lifespan)
app = FastAPI(title="StackPilot", version="0.7.0", lifespan=lifespan)
app.add_middleware(
CORSMiddleware,
@@ -81,6 +84,7 @@ app.include_router(audit.router)
app.include_router(settings_router.router)
app.include_router(backups.router)
app.include_router(destinations.router)
app.include_router(schedules.router)
app.include_router(agents.router)
app.include_router(ws.router)