""" Shinkan Jinkendo - Main Application Entry Point Trainer- und Vereinsplattform für Kampfsport-Trainingsplanung """ from pathlib import Path from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import JSONResponse from fastapi.staticfiles import StaticFiles import os from slowapi import _rate_limit_exceeded_handler from slowapi.errors import RateLimitExceeded from version import APP_VERSION, BUILD_DATE, DB_SCHEMA_VERSION, MODULE_VERSIONS # Run database migrations on startup try: import run_migrations run_migrations.main() print("✓ Database migrations completed") except Exception as e: print(f"⚠ Warning: Migration error: {e}") print(" Continuing startup - migrations may need manual intervention") from routers.auth import limiter as auth_rate_limiter # Initialize FastAPI app app = FastAPI( title="Shinkan Jinkendo API", description="Trainer- und Vereinsplattform für Kampfsport-Trainingsplanung", version=APP_VERSION ) # SlowAPI: Rate Limits auf /api/auth/* (Decorator in routers/auth.py) app.state.limiter = auth_rate_limiter app.add_exception_handler(RateLimitExceeded, _rate_limit_exceeded_handler) # CORS — kommaseparierte Liste (z. B. https://dev.shinkan… und http://192.168.x.x:3098) _cors_raw = os.getenv("ALLOWED_ORIGINS", "http://localhost:3098") ALLOWED_ORIGINS = [o.strip() for o in _cors_raw.split(",") if o.strip()] app.add_middleware( CORSMiddleware, allow_origins=ALLOWED_ORIGINS, allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) # TODO: Initialize Database with migrations # Version Endpoint (public, no auth) @app.get("/api/version") def get_version(): """Get application version and build info""" return { "app_version": APP_VERSION, "build_date": BUILD_DATE, "backend_version": APP_VERSION, "modules": MODULE_VERSIONS, "db_schema_version": DB_SCHEMA_VERSION, "environment": os.getenv("ENVIRONMENT", "development") } # Health Check @app.get("/health") def health_check(): """Health check endpoint""" return {"status": "healthy", "version": APP_VERSION} # Root Endpoint @app.get("/") def read_root(): """Root endpoint - API info""" return { "app": "Shinkan Jinkendo API", "version": APP_VERSION, "docs": "/docs", "health": "/health" } # Register routers from routers import auth, profiles, exercises, clubs, skills, training_planning, catalogs, maturity_models, matrix_stack_bundle, import_wiki, import_wiki_admin app.include_router(auth.router) app.include_router(profiles.router) app.include_router(exercises.router) app.include_router(clubs.router) app.include_router(skills.router) app.include_router(training_planning.router) app.include_router(catalogs.router) app.include_router(maturity_models.router) app.include_router(matrix_stack_bundle.router) app.include_router(import_wiki.router) app.include_router(import_wiki_admin.router) # Lokale Medien (Übungen-Uploads) unter MEDIA_ROOT, ausliefern unter /media/... _media_dir = os.getenv("MEDIA_ROOT", str(Path(__file__).resolve().parent / "media")) Path(_media_dir).mkdir(parents=True, exist_ok=True) app.mount("/media", StaticFiles(directory=_media_dir), name="media") if __name__ == "__main__": import uvicorn uvicorn.run( "main:app", host="0.0.0.0", port=8000, reload=True )