from fastapi import APIRouter, Depends, HTTPException, Request from fastapi.responses import RedirectResponse from fastapi.templating import Jinja2Templates from app.db import get_db from app.security import crypto from app.security.deps import require_auth from app.services import credential_service router = APIRouter(prefix="/settings/credentials", tags=["credentials"]) templates = Jinja2Templates(directory="app/templates") @router.get("") async def credentials_index(request: Request, user: dict = Depends(require_auth), db=Depends(get_db)): # Write-only by design: never send a previously-saved secret value back # to the browser. Each scope just reports which fields are already set. configured = { scope: set(credential_service.get_scope(db, scope).keys()) for scope in credential_service.SCOPE_FIELDS } enabled = { scope: credential_service.is_scope_enabled(db, scope) for scope in credential_service.SCOPE_FIELDS } return templates.TemplateResponse( request, "credentials/index.html", { "scope_fields": credential_service.SCOPE_FIELDS, "scope_descriptions": credential_service.SCOPE_DESCRIPTIONS, "core_scopes": credential_service.CORE_SCOPES, "configured": configured, "enabled": enabled, "key_present": crypto.key_present(), }, ) @router.post("/{scope}") async def save_credentials( scope: str, request: Request, user: dict = Depends(require_auth), db=Depends(get_db) ): if scope not in credential_service.SCOPE_FIELDS: raise HTTPException(404, "unknown credential scope") form = await request.form() values = { field: value for field, value in form.items() if field in credential_service.SCOPE_FIELDS[scope] and value != "" } try: credential_service.set_credentials(db, scope, values) except RuntimeError: # Almost always the encryption key is missing, so the value can't be # encrypted. Redirect back; the page shows a key-missing banner with # exactly what to do. return RedirectResponse(url="/settings/credentials", status_code=303) return RedirectResponse(url="/settings/credentials", status_code=303) @router.post("/{scope}/toggle") async def toggle_scope(scope: str, user: dict = Depends(require_auth), db=Depends(get_db)): if scope not in credential_service.OPTIONAL_SCOPES: raise HTTPException(404, "unknown or non-toggleable credential scope") currently_enabled = credential_service.is_scope_enabled(db, scope) credential_service.set_scope_enabled(db, scope, not currently_enabled) return RedirectResponse(url="/settings/credentials", status_code=303)