Skip to content

Structural refactoring#22

Merged
schowave merged 30 commits intomainfrom
Structural-Refactoring
Mar 21, 2026
Merged

Structural refactoring#22
schowave merged 30 commits intomainfrom
Structural-Refactoring

Conversation

@schowave
Copy link
Copy Markdown
Owner

@schowave schowave commented Mar 20, 2026

v5.0 — Best-Practices Refactoring & Architecture Improvements

Umfassende Modernisierung der Codebase nach aktuellen Best Practices. 49 Dateien geändert, 96 Tests bestanden.

Highlights

Konfiguration & Infrastruktur

  • pydantic-settings ersetzt manuelle Config-Klasse mit os.getenv() — typsichere, validierte Konfiguration
  • Alembic-Migrations statt Base.metadata.create_all() — sicheres DB-Schema-Management mit entrypoint.sh für bestehende Datenbanken
  • pytz durch zoneinfo (Python 3.12 stdlib) ersetzt, Zeitzone konfigurierbar via TIMEZONE Env-Var
  • Alle Dependency-Versionen in pyproject.toml gepinnt

Performance & Ressourcen

  • Shared httpx.AsyncClient via FastAPI Lifespan statt Client-pro-Request
  • In-Memory PDF/JPEG-Generierung mit BytesIO + StreamingResponse — keine temporären Dateien mehr
  • pathlib durchgängig statt os.path

Sicherheit

  • CSRF-Schutz via Double-Submit-Cookie-Pattern (Middleware)
  • Standardisierte Fehler-Responses mit ErrorResponse-Schema und Exception-Handlern
  • Security-Headers-Middleware

Observability

  • Structured Logging mit structlog (Console/JSON konfigurierbar via LOG_FORMAT)
  • /health-Endpoint mit Docker HEALTHCHECK

Frontend

  • HTMX + Alpine.js für progressive Enhancement
  • Fragment-basiertes Rendering (/fragments/appointments)
  • Downloads mit Zeitstempel im Dateinamen (YYYY-MM-DD-HH-MM-SS)

Multi-Profil-Support

  • Mehrere Konfigurationsprofile (clone, delete, switch)
  • Automatische Bereinigung verwaister Settings

Code-Qualität

  • Vollständige Type-Annotations
  • 96 Tests (vorher ~80)

Neue Umgebungsvariablen

Variable Default Beschreibung
TIMEZONE Europe/Berlin Zeitzone für Termin-Anzeige
LOG_FORMAT console Log-Format: console oder json

Deployment

Vollständig rückwärtskompatibel. entrypoint.sh erkennt bestehende Datenbanken automatisch und stamped sie für Alembic. Kein manueller Eingriff nötig.

Commits (22)

  • 071ce6f refactor: replace Config class with pydantic-settings
  • 2196558 refactor: migrate from pytz to zoneinfo, make timezone configurable
  • 2b57997 feat: add Alembic migrations, remove create_schema
  • eca1b96 refactor: shared async httpx client with lifespan management
  • 508066d refactor: in-memory PDF/JPEG generation with StreamingResponse
  • 9ee1211 feat: add /health endpoint with Docker HEALTHCHECK
  • 00b35d7 feat: structured logging with structlog, configurable format
  • c6e29a5 feat: add CSRF protection middleware (double-submit cookie)
  • e267478 feat: standardized error response model and exception handlers
  • 4a0f742 refactor: replace os.path with pathlib throughout
  • 99190b3 refactor: add complete type annotations
  • 74788e8 feat: multi-profile settings with clone, delete, and orphan cleanup
  • fd3c152 feat: HTMX + Alpine.js frontend upgrade
  • 7ee3462 docs: update Makefile, Dockerfile comments, and README
  • 7f611bb fix: CSRF token mismatch on first login
  • 6da9257 feat: add timestamp to download filenames
  • d3f3fd7 chore: bump uvicorn to 0.42.0
  • 9fc00ab chore: pin all dependency versions in pyproject.toml
  • 85b3b35 fix: CSRF middleware consuming form body before FastAPI can parse it
  • 65a5af5 fix: browser marking downloads as insecure

David Schowalter and others added 30 commits March 15, 2026 02:12
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Address feedback from three review passes: fix Alembic
existing-deployment handling, HTMX/StreamingResponse conflict,
CSRF double-submit pattern, multi-profile atomicity, and
various clarifications.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
13-task plan covering pydantic-settings, Alembic migrations,
HTMX frontend, in-memory generation, and code quality upgrades.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace the manual Config class (os.getenv + validate method) with a
pydantic-settings BaseSettings singleton. All modules now import
`settings` instead of `Config`. Removes load_dotenv() from main.py
since pydantic-settings handles .env files natively. Removes the E402
per-file-ignore that was needed for the load_dotenv() workaround.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Set up Alembic with initial migration for all 4 tables
- Remove create_schema() from database.py and its call in main.py
- Add entrypoint.sh with DB stamping logic for existing databases
- Update Dockerfile with sqlite3 dep, alembic files, and ENTRYPOINT
- Remove test_create_schema test since the function no longer exists

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace per-request httpx.AsyncClient creation with a single shared
client managed via FastAPI's lifespan context manager and injected
through Depends.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Makefile: run target now runs alembic migrations first, removed preview target
- Dockerfile: added comments explaining entrypoint.sh steps
- README: added TIMEZONE and LOG_FORMAT env vars, updated make run description

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The middleware was generating a new token on every GET response, but the
template read the token from the incoming request cookie (which was the
old/empty value). Now reuses the existing cookie token and only generates
a new one on first visit, injecting it into the request so templates
can read it.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Downloads now named appointments_YYYY-MM-DD-HH-MM-SS.pdf/.zip

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Also resolves dependabot PRs #17 (pytz) and #14 (python-dotenv)
which were already addressed by removing those dependencies.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
await request.form() in the middleware consumed the body stream,
leaving nothing for FastAPI's Form(...) parameters. Now caches the
body and re-injects it after CSRF validation so downstream handlers
can read it.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Create blob with explicit MIME type (application/pdf or application/zip)
and use arrayBuffer instead of blob() to ensure correct content type.
Also defer cleanup with setTimeout to avoid race conditions.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@schowave schowave merged commit 4246cf2 into main Mar 21, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant