diff --git a/app/loader.py b/app/loader.py index 74d2d87..f5b707b 100644 --- a/app/loader.py +++ b/app/loader.py @@ -2,6 +2,7 @@ import os import yaml SERVICES_DIR = "services" +CONFIG_DIR = "configs" def load_services(): services = [] @@ -20,3 +21,23 @@ def load_services(): services.append(cfg) return services + + +def load_app_config(): + config = {} + + if not os.path.isdir(CONFIG_DIR): + return config + + for filename in os.listdir(CONFIG_DIR): + if not filename.endswith((".yml", ".yaml")): + continue + + path = os.path.join(CONFIG_DIR, filename) + with open(path, "r", encoding="utf-8") as f: + data = yaml.safe_load(f) or {} + + # merge config + config.update(data) + + return config diff --git a/app/main.py b/app/main.py index 54e3135..75adc35 100644 --- a/app/main.py +++ b/app/main.py @@ -6,12 +6,15 @@ from app.loader import load_services from app.store import all, get, history, init_db from app.scheduler import run_service from app.utils.schema import load_schema, render_response +from app.middleware.auth import auth_middleware + services = load_services() TASKS = [] + @asynccontextmanager async def lifespan(app: FastAPI): # ===== STARTUP ===== @@ -32,6 +35,8 @@ app = FastAPI( lifespan=lifespan ) +app.middleware("http")(auth_middleware) + @app.get("/health") def health(): health_schema = load_schema("health_response") @@ -49,9 +54,6 @@ def health(): "failed_checks": failed, "last_updated": data.get("last_updated"), }) - - - print(services) return render_response(health_schema, services) diff --git a/app/middleware/auth.py b/app/middleware/auth.py new file mode 100644 index 0000000..2312bdb --- /dev/null +++ b/app/middleware/auth.py @@ -0,0 +1,38 @@ +from fastapi import Request +from fastapi.responses import JSONResponse +from app.loader import load_app_config + +CONFIG = load_app_config() + +AUTH = CONFIG.get("auth", {}) +AUTH_ENABLED = AUTH.get("enabled", False) +API_TOKEN = AUTH.get("token") +PUBLIC_PATHS = set(AUTH.get("public_paths", [])) + + +async def auth_middleware(request: Request, call_next): + if not AUTH_ENABLED: + return await call_next(request) + + path = request.url.path + + if path in PUBLIC_PATHS: + return await call_next(request) + + auth_header = request.headers.get("Authorization") + + if not auth_header or not auth_header.startswith("Bearer "): + return JSONResponse( + status_code=401, + content={"message": "Missing or invalid Authorization header"}, + ) + + token = auth_header.replace("Bearer ", "").strip() + + if token != API_TOKEN: + return JSONResponse( + status_code=403, + content={"message": "Invalid token"}, + ) + + return await call_next(request) diff --git a/configs/app.yml b/configs/app.yml new file mode 100644 index 0000000..c9a9325 --- /dev/null +++ b/configs/app.yml @@ -0,0 +1,5 @@ +auth: + enabled: true + token: Work1234. + public_paths: + - /health