feat(deploy): healthcheck api и worker, отдельный migrate-сервис, лимит загрузки отчёта
Миграции вынесены из команды api в one-shot сервис migrate, api и worker стартуют после него. /health отвечает 503 при недоступной БД. Отчёт больше MAX_UPLOAD_BYTES (25 МиБ) получает 413, Caddy режет на 30 МБ раньше. Том uploads убран, секреты env_file передаются сервисам явно. В CI добавлена сборка образа бэкенда без push, test_migrations сверяет модели с историей Alembic.
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from fastapi import APIRouter
|
||||
from fastapi import APIRouter, Response, status
|
||||
from pydantic import BaseModel
|
||||
from sqlalchemy import text
|
||||
|
||||
@@ -17,10 +17,12 @@ class Health(BaseModel):
|
||||
|
||||
|
||||
@router.get("/health", name="check")
|
||||
async def check(session: SessionDep) -> Health:
|
||||
async def check(session: SessionDep, response: Response) -> Health:
|
||||
"""503 when the database cannot be reached, so a container healthcheck can trust the code."""
|
||||
try:
|
||||
await session.execute(text("SELECT 1"))
|
||||
db = "ok"
|
||||
except Exception as exc:
|
||||
db = f"error: {type(exc).__name__}"
|
||||
response.status_code = status.HTTP_503_SERVICE_UNAVAILABLE
|
||||
return Health(status="ok" if db == "ok" else "degraded", version=__version__, database=db)
|
||||
|
||||
@@ -20,7 +20,7 @@ from typing import Annotated
|
||||
from fastapi import APIRouter, File, Form, Query, UploadFile
|
||||
from sqlalchemy import select
|
||||
|
||||
from fintracker.api.deps import CurrentUser, SessionDep
|
||||
from fintracker.api.deps import CurrentUser, SessionDep, SettingsDep
|
||||
from fintracker.api.errors import Problem
|
||||
from fintracker.api.schemas.imports import (
|
||||
AccountSuggestion,
|
||||
@@ -64,13 +64,19 @@ def _problem(exc: ImportProblem) -> Problem:
|
||||
@router.post("", name="create")
|
||||
async def create_import(
|
||||
session: SessionDep,
|
||||
settings: SettingsDep,
|
||||
_: CurrentUser,
|
||||
file: Annotated[UploadFile, File(description="the report itself")],
|
||||
account_id: Annotated[int | None, Form(description="target account, if known")] = None,
|
||||
parser: Annotated[str | None, Form(description="force a parser from registry.names()")] = None,
|
||||
) -> ImportPreview:
|
||||
"""Upload a report, parse it and show what committing it would do. Writes no event."""
|
||||
data = await file.read()
|
||||
limit = settings.max_upload_bytes
|
||||
data = await file.read(limit + 1)
|
||||
if len(data) > limit:
|
||||
raise Problem(
|
||||
413, "Payload Too Large", f"Report is larger than {limit // (1024 * 1024)} MiB"
|
||||
)
|
||||
try:
|
||||
outcome = await report_import.upload(
|
||||
session,
|
||||
|
||||
Reference in New Issue
Block a user