first commit
This commit is contained in:
112
backend/app.py
Normal file
112
backend/app.py
Normal file
@ -0,0 +1,112 @@
|
||||
import logging
|
||||
from typing import List
|
||||
|
||||
from fastapi import Body, Depends, FastAPI, HTTPException, Request, status
|
||||
from fastapi.responses import HTMLResponse
|
||||
from fastapi.templating import Jinja2Templates
|
||||
|
||||
from backend import actions
|
||||
from backend.actions import ActionError
|
||||
from backend.auth import authenticate_admin_user, get_current_admin, issue_token, list_manageable_users
|
||||
from backend.models import ActionRequest, ActionResponse, LoginRequest, LoginResponse, UserStatus
|
||||
from backend.settings import Settings, get_settings
|
||||
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format="%(asctime)s %(levelname)s %(name)s - %(message)s",
|
||||
)
|
||||
logger = logging.getLogger("skd")
|
||||
|
||||
app = FastAPI(title="Safe Kiddo Daemon", version="1.0.0")
|
||||
templates = Jinja2Templates(directory="backend/templates")
|
||||
|
||||
|
||||
def validate_user(username: str, settings: Settings = Depends(get_settings)) -> str:
|
||||
allowed = set(list_manageable_users(settings))
|
||||
if username not in allowed:
|
||||
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="User not allowed")
|
||||
return username
|
||||
|
||||
|
||||
@app.get("/health")
|
||||
def health(settings: Settings = Depends(get_settings)) -> dict:
|
||||
return {"status": "ok", "dry_run": settings.dry_run}
|
||||
|
||||
|
||||
@app.post("/login", response_model=LoginResponse)
|
||||
def login(payload: LoginRequest, settings: Settings = Depends(get_settings)) -> LoginResponse:
|
||||
authenticate_admin_user(payload.username, payload.password, settings)
|
||||
token = issue_token(payload.username, settings)
|
||||
return LoginResponse(token=token, expires_in=settings.token_ttl_seconds)
|
||||
|
||||
|
||||
@app.get("/users", response_model=List[UserStatus], dependencies=[Depends(get_current_admin)])
|
||||
def users(settings: Settings = Depends(get_settings)) -> List[UserStatus]:
|
||||
logged_in = set(actions.list_logged_in_users())
|
||||
targets = list_manageable_users(settings)
|
||||
return [UserStatus(user=user, logged_in=user in logged_in) for user in targets]
|
||||
|
||||
|
||||
@app.post(
|
||||
"/users/{username}/disable",
|
||||
response_model=ActionResponse,
|
||||
dependencies=[Depends(get_current_admin)],
|
||||
)
|
||||
def disable_user(
|
||||
username: str = Depends(validate_user),
|
||||
payload: ActionRequest | None = Body(default=None),
|
||||
settings: Settings = Depends(get_settings),
|
||||
) -> ActionResponse:
|
||||
try:
|
||||
steps = actions.disable_user(
|
||||
username,
|
||||
countdown=payload.countdown if payload else None,
|
||||
sound=payload.sound if payload else None,
|
||||
message=payload.message if payload else None,
|
||||
)
|
||||
except ActionError as exc:
|
||||
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(exc)) from exc
|
||||
except Exception as exc: # pragma: no cover - safeguard
|
||||
logger.exception("Failed to disable %s", username)
|
||||
raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="failed") from exc
|
||||
|
||||
logged_in = username in actions.list_logged_in_users()
|
||||
return ActionResponse(
|
||||
user=username,
|
||||
action="disable",
|
||||
dry_run=settings.dry_run,
|
||||
steps=steps,
|
||||
logged_in=logged_in,
|
||||
)
|
||||
|
||||
|
||||
@app.post(
|
||||
"/users/{username}/enable",
|
||||
response_model=ActionResponse,
|
||||
dependencies=[Depends(get_current_admin)],
|
||||
)
|
||||
def enable_user(
|
||||
username: str = Depends(validate_user),
|
||||
settings: Settings = Depends(get_settings),
|
||||
) -> ActionResponse:
|
||||
try:
|
||||
steps = actions.enable_user(username)
|
||||
except ActionError as exc:
|
||||
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(exc)) from exc
|
||||
except Exception as exc: # pragma: no cover - safeguard
|
||||
logger.exception("Failed to enable %s", username)
|
||||
raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="failed") from exc
|
||||
|
||||
logged_in = username in actions.list_logged_in_users()
|
||||
return ActionResponse(
|
||||
user=username,
|
||||
action="enable",
|
||||
dry_run=settings.dry_run,
|
||||
steps=steps,
|
||||
logged_in=logged_in,
|
||||
)
|
||||
|
||||
|
||||
@app.get("/", response_class=HTMLResponse)
|
||||
def index(request: Request) -> HTMLResponse:
|
||||
return templates.TemplateResponse("index.html", {"request": request})
|
||||
Reference in New Issue
Block a user