Scaffold app/ package, pinned requirements.txt, multi-stage Dockerfile,
docker-compose.yml, and .env.example. Add typed pydantic-settings loader
with full env contract and a production validator that refuses the
dev-sentinel SECRET_KEY. Wire structlog with an APP_ENV-driven renderer
(console in dev, JSON in prod). Ship a minimal unauthenticated /healthz
returning {status, version, commit_sha} with commit SHA fed through a
GIT_COMMIT_SHA build arg.
Also mark Phase 0 complete in docs/ROADMAP.md.
70 lines
2.1 KiB
Python
70 lines
2.1 KiB
Python
"""Liveness / version endpoint.
|
|
|
|
The ``/healthz`` endpoint is intentionally minimal: it must be safe to
|
|
expose unauthenticated (Caddy + uptime checks will hit it), so it leaks
|
|
only the app version and the build's git commit SHA — no hostnames,
|
|
paths, config values, or environment details.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Literal
|
|
|
|
from fastapi import APIRouter, Depends
|
|
from pydantic import BaseModel, Field
|
|
|
|
from app import __version__
|
|
from app.config import Settings, get_settings
|
|
|
|
|
|
class HealthResponse(BaseModel):
|
|
"""Response schema for ``GET /healthz``.
|
|
|
|
Kept as an explicit :class:`pydantic.BaseModel` so FastAPI publishes
|
|
the shape in the OpenAPI schema and so any drift is caught at typing
|
|
time rather than via a loose ``dict``.
|
|
"""
|
|
|
|
status: Literal["ok"] = Field(
|
|
default="ok",
|
|
description="Liveness indicator; this endpoint only returns 'ok'.",
|
|
)
|
|
version: str = Field(description="Application semantic version.")
|
|
commit_sha: str = Field(
|
|
description="Git commit SHA of the running build ('unknown' in dev).",
|
|
)
|
|
|
|
|
|
# Module-level router; mounted by `app.main.create_app`. No prefix and no
|
|
# auth dependencies — /healthz is intentionally public.
|
|
router: APIRouter = APIRouter(tags=["health"])
|
|
|
|
|
|
@router.get(
|
|
"/healthz",
|
|
response_model=HealthResponse,
|
|
summary="Liveness + version probe",
|
|
)
|
|
def healthz(settings: Settings = Depends(get_settings)) -> HealthResponse:
|
|
"""Return a minimal liveness envelope.
|
|
|
|
Parameters
|
|
----------
|
|
settings:
|
|
Injected via FastAPI's dependency system so tests can override
|
|
configuration cleanly.
|
|
|
|
Returns
|
|
-------
|
|
HealthResponse
|
|
``status`` is always ``"ok"``; ``version`` and ``commit_sha``
|
|
identify the running build.
|
|
"""
|
|
# Intentionally does not touch the DB, filesystem, or any external
|
|
# service. This is liveness, not readiness — a readiness probe with
|
|
# deeper checks can be added in a later phase behind a different path.
|
|
return HealthResponse(
|
|
version=__version__,
|
|
commit_sha=settings.git_commit_sha,
|
|
)
|