Graceful shutdown ensures in-flight operations complete before process exits: - Lifespan shutdown handler drains pending tasks with 25s timeout - Scheduler stops accepting new jobs immediately - HTTP session, external logging, scheduler lock, DB conn closed cleanly - 25s Python timeout leaves 5s margin before Docker's 30s SIGKILL Files changed: - backend/app/main.py: enhanced _lifespan shutdown with task drain - Docker/Dockerfile.backend: documented signal handling in header - Docker/docker-compose.yml: added stop_grace_period: 30s - Docker/compose.prod.yml: added stop_grace_period: 30s - Docs/Deployment.md: new Graceful Shutdown section with sequence table - Docs/TROUBLESHOOTING.md: new Graceful Shutdown Issues section Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
52 lines
1.7 KiB
Python
52 lines
1.7 KiB
Python
"""Authentication Pydantic models.
|
|
|
|
Request, response, and domain models used by the auth router and service.
|
|
"""
|
|
|
|
from pydantic import Field
|
|
|
|
from app.models.response import BanGuiBaseModel
|
|
|
|
|
|
class LoginRequest(BanGuiBaseModel):
|
|
"""Payload for ``POST /api/auth/login``."""
|
|
|
|
password: str = Field(
|
|
...,
|
|
max_length=72,
|
|
description="Master password to authenticate with (max 72 bytes due to bcrypt truncation).",
|
|
)
|
|
|
|
class LoginResponse(BanGuiBaseModel):
|
|
"""Successful login response.
|
|
|
|
The session token is set as an ``HttpOnly`` ``SameSite=Lax`` cookie by the
|
|
router, protecting it from JavaScript access. The JSON body contains only
|
|
the expiry timestamp, allowing the frontend to know when to prompt for
|
|
re-authentication.
|
|
|
|
For programmatic API clients that require a token in the response body,
|
|
use ``POST /api/auth/token`` instead, which does not set a cookie.
|
|
"""
|
|
|
|
expires_at: str = Field(..., description="ISO 8601 UTC expiry timestamp.")
|
|
|
|
class LogoutResponse(BanGuiBaseModel):
|
|
"""Response body for ``POST /api/auth/logout``."""
|
|
|
|
message: str = Field(default="Logged out successfully.")
|
|
|
|
class SessionValidResponse(BanGuiBaseModel):
|
|
"""Response for ``GET /api/auth/session`` confirming session validity."""
|
|
|
|
valid: bool = Field(default=True, description="Whether the session is valid and active.")
|
|
|
|
|
|
class Session(BanGuiBaseModel):
|
|
"""Internal domain model representing a persisted session record."""
|
|
|
|
id: int = Field(..., description="Auto-incremented row ID.")
|
|
token: str = Field(..., description="Opaque session token.")
|
|
created_at: str = Field(..., description="ISO 8601 UTC creation timestamp.")
|
|
expires_at: str = Field(..., description="ISO 8601 UTC expiry timestamp.")
|