import os from fastapi import APIRouter, Depends, HTTPException from sqlalchemy.orm import Session from ... import models, schemas, auth from ...database import get_db, BASE_DIR from ...scheduler import sync_scheduler_config from ...config_manager import ConfigManager router = APIRouter( prefix="/admin/db", tags=["Admin Configuration"] ) PROJECT_ROOT = os.path.dirname(BASE_DIR) PROMPT_FILE_PATH = os.path.join(PROJECT_ROOT, "config", "ai_prompt.md") @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.""" 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() sync_scheduler_config() return settings @router.get("/settings/prompt") def get_ai_prompt( db: Session = Depends(get_db), current_admin: auth.TokenData = Depends(auth.get_current_admin) ): """Get the current AI extraction prompt.""" if os.path.exists(PROMPT_FILE_PATH): try: with open(PROMPT_FILE_PATH, 'r', encoding='utf-8') as f: return {"value": f.read().strip(), "source": "file"} except Exception: pass setting = db.query(models.SystemSetting).filter(models.SystemSetting.key == "ai_extraction_prompt").first() if not setting: return {"value": "", "source": "none"} return {"value": setting.value, "source": "database"} @router.post("/settings/prompt") def update_ai_prompt( payload: dict, db: Session = Depends(get_db), current_admin: auth.TokenData = Depends(auth.get_current_admin) ): """Update the AI extraction prompt.""" value = payload.get("value") if value is None: raise HTTPException(status_code=400, detail="Value required") try: os.makedirs(os.path.dirname(PROMPT_FILE_PATH), exist_ok=True) with open(PROMPT_FILE_PATH, 'w', encoding='utf-8') as f: f.write(value) except Exception: pass existing = db.query(models.SystemSetting).filter(models.SystemSetting.key == "ai_extraction_prompt").first() if existing: existing.value = value else: db.add(models.SystemSetting(key="ai_extraction_prompt", value=value)) db.commit() return {"status": "success", "file_updated": os.path.exists(PROMPT_FILE_PATH)} @router.get("/settings/ai") def get_ai_config( db: Session = Depends(get_db), current_admin: auth.TokenData = Depends(auth.get_current_admin) ): """Check AI provider status and active provider.""" gemini_key = os.environ.get("GEMINI_API_KEY") claude_key = os.environ.get("CLAUDE_API_KEY") provider_setting = db.query(models.SystemSetting).filter(models.SystemSetting.key == "ai_provider").first() active_provider = provider_setting.value if provider_setting else "gemini" return { "active_provider": active_provider, "providers": [ { "id": "gemini", "name": "Google Gemini 2.0", "configured": bool(gemini_key), "active": active_provider == "gemini", "masked_key": ConfigManager.get_masked_key("GEMINI_API_KEY") }, { "id": "claude", "name": "Anthropic Claude 3.5", "configured": bool(claude_key), "active": active_provider == "claude", "masked_key": ConfigManager.get_masked_key("CLAUDE_API_KEY") } ] } @router.post("/settings/ai-keys") def update_ai_keys( payload: dict, current_admin: auth.TokenData = Depends(auth.get_current_admin) ): """Update AI API keys.""" gemini_key = payload.get("gemini_api_key") claude_key = payload.get("claude_api_key") updates = {} if gemini_key: updates["GEMINI_API_KEY"] = gemini_key if claude_key: updates["CLAUDE_API_KEY"] = claude_key if updates: ConfigManager.update_keys(updates) return { "status": "success", "gemini_configured": bool(os.environ.get("GEMINI_API_KEY")), "claude_configured": bool(os.environ.get("CLAUDE_API_KEY")) } @router.post("/settings/test-ai-key") def test_ai_key( payload: dict, current_admin: auth.TokenData = Depends(auth.get_current_admin) ): """Test AI API key connectivity.""" provider = payload.get("provider") key = payload.get("key") if not provider or provider not in ["gemini", "claude"]: raise HTTPException(status_code=400, detail="Invalid provider") if not key or "****" in key: key = os.environ.get("GEMINI_API_KEY" if provider == "gemini" else "CLAUDE_API_KEY") if not key: raise HTTPException(status_code=400, detail="No API key provided or configured") try: if provider == "gemini": from google import genai client = genai.Client(api_key=key, http_options={'api_version': 'v1beta'}) client.models.list() return {"status": "success", "message": "Google Gemini API connection verified!"} elif provider == "claude": import anthropic client = anthropic.Anthropic(api_key=key) client.models.list(limit=1) return {"status": "success", "message": "Anthropic Claude API connection verified!"} except Exception as e: raise HTTPException(status_code=400, detail=f"{provider.capitalize()} Test Failed: {str(e)}") @router.post("/settings/ai") def update_ai_provider( payload: dict, db: Session = Depends(get_db), current_admin: auth.TokenData = Depends(auth.get_current_admin) ): """Update the active AI provider.""" provider = payload.get("provider") if provider not in ["gemini", "claude"]: raise HTTPException(status_code=400, detail="Invalid provider") existing = db.query(models.SystemSetting).filter(models.SystemSetting.key == "ai_provider").first() if existing: existing.value = provider else: db.add(models.SystemSetting(key="ai_provider", value=provider)) db.commit() return {"status": "success", "active_provider": provider}