from fastapi import APIRouter, Depends, HTTPException, status from sqlalchemy.orm import Session from typing import List from .. import models, schemas, auth from ..database import get_db from ..db_manager import DbManager from ..scheduler import sync_scheduler_config router = APIRouter( prefix="/admin/db", tags=["Admin Database"] ) @router.get("/backups", response_model=List[schemas.BackupInfo]) def get_backups( db: Session = Depends(get_db), current_admin: auth.TokenData = Depends(auth.get_current_admin) ): """List available database backups.""" return DbManager.get_backup_list() @router.get("/stats", response_model=schemas.DatabaseStats) def get_db_stats( db: Session = Depends(get_db), current_admin: auth.TokenData = Depends(auth.get_current_admin) ): """Get database backup storage statistics.""" return DbManager.get_stats() @router.post("/backup", response_model=schemas.BackupInfo) def trigger_manual_backup( db: Session = Depends(get_db), current_admin: auth.TokenData = Depends(auth.get_current_admin) ): """Trigger a manual database backup.""" filename = DbManager.create_backup(db, label="manual", user_id=current_admin.sub) # Re-fetch the newly created file info backups = DbManager.get_backup_list() for b in backups: if b.filename == filename: return b raise HTTPException(status_code=500, detail="Backup created but info not found") @router.post("/restore") def restore_database( payload: dict, db: Session = Depends(get_db), current_admin: auth.TokenData = Depends(auth.get_current_admin) ): """Restore database from a specific file. DANGEROUS.""" filename = payload.get("filename") confirm = payload.get("confirm", False) if not filename: raise HTTPException(status_code=400, detail="Filename required") if not confirm: raise HTTPException(status_code=400, detail="Confirmation required") try: success = DbManager.restore_backup(filename, db, user_id=current_admin.sub) return {"status": "success", "message": f"Database restored from {filename}"} except Exception as e: raise HTTPException(status_code=500, detail=str(e)) @router.get("/settings", response_model=schemas.DbSettingsUpdate) def get_db_settings( db: Session = Depends(get_db), current_admin: auth.TokenData = Depends(auth.get_current_admin) ): """Get database retention and scheduling settings.""" # Ensure default settings exist retention = db.query(models.SystemSetting).filter(models.SystemSetting.key == "backup_retention_count").first() hour = db.query(models.SystemSetting).filter(models.SystemSetting.key == "backup_schedule_hour").first() freq = db.query(models.SystemSetting).filter(models.SystemSetting.key == "backup_schedule_freq_days").first() return { "retention_count": int(retention.value) if retention else 10, "schedule_hour": int(hour.value) if hour else 3, "schedule_freq_days": int(freq.value) if freq else 1 } @router.patch("/settings", response_model=schemas.DbSettingsUpdate) def update_db_settings( settings: schemas.DbSettingsUpdate, db: Session = Depends(get_db), current_admin: auth.TokenData = Depends(auth.get_current_admin) ): """Update database settings and re-trigger scheduler sync.""" pairs = { "backup_retention_count": str(settings.retention_count), "backup_schedule_hour": str(settings.schedule_hour), "backup_schedule_freq_days": str(settings.schedule_freq_days) } for key, val in pairs.items(): existing = db.query(models.SystemSetting).filter(models.SystemSetting.key == key).first() if existing: existing.value = val else: db.add(models.SystemSetting(key=key, value=val)) db.commit() # Re-trigger scheduler sync sync_scheduler_config() return settings