diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..640a5c7 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,12 @@ +FROM python:3.11-slim + +WORKDIR /app + +COPY requirements.txt . +RUN pip install -r requirements.txt + +COPY . . + +EXPOSE 8000 + +CMD ["bash", "/app/start.sh"] \ No newline at end of file diff --git a/backup/app/main.py b/backup/app/main.py index fd9964a..f7d5268 100644 --- a/backup/app/main.py +++ b/backup/app/main.py @@ -2,6 +2,7 @@ import shutil import logging from contextlib import asynccontextmanager +from urllib.parse import urlsplit from dotenv import load_dotenv from fastapi import FastAPI @@ -22,21 +23,47 @@ logger = logging.getLogger(__name__) +def _normalize_origin(origin: str) -> str | None: + """Normalize CORS entries to scheme://host[:port] and drop invalid values.""" + raw = origin.strip() + if not raw: + return None + + parsed = urlsplit(raw) + if not parsed.scheme or not parsed.netloc: + logger.warning("Ignoring invalid CORS origin '%s'", origin) + return None + + normalized = f"{parsed.scheme}://{parsed.netloc}" + if normalized != raw.rstrip("/"): + logger.warning("Normalized CORS origin '%s' to '%s'", origin, normalized) + return normalized + + +def _normalized_unique(origins: list[str]) -> list[str]: + result: list[str] = [] + for origin in origins: + normalized = _normalize_origin(origin) + if normalized and normalized not in result: + result.append(normalized) + return result + + def _build_allowed_origins() -> list[str]: """Build a strict CORS allow-list from environment variables.""" allowed_origins_env = os.getenv("FRONTEND_ALLOWED_ORIGINS", "") - parsed = [origin.strip() for origin in allowed_origins_env.split(",") if origin.strip()] + parsed = _normalized_unique(allowed_origins_env.split(",")) if parsed: return parsed # Backward-compatible fallback when comma-separated variable is not set. - legacy_origins = [o for o in [FRONTEND_DEV_URL, FRONTEND_PROD_URL] if o] + legacy_origins = _normalized_unique([FRONTEND_DEV_URL, FRONTEND_PROD_URL]) if legacy_origins: return legacy_origins # Safe local fallback for development/test only. - return ["http://localhost:3000", "http://127.0.0.1:3000", "*"] + return ["http://localhost:3000", "http://127.0.0.1:3000"] ALLOWED_ORIGINS = _build_allowed_origins() diff --git a/pages/scoreboard.html b/pages/scoreboard.html index ea753ba..be90096 100644 --- a/pages/scoreboard.html +++ b/pages/scoreboard.html @@ -63,6 +63,7 @@

Laboratory of Research in Computer Science and Applications (LRSIA), 2026

+ diff --git a/pages/static/js/auth.js b/pages/static/js/auth.js index e06cce7..1c808f4 100644 --- a/pages/static/js/auth.js +++ b/pages/static/js/auth.js @@ -1,4 +1,4 @@ -const API_URL = "http://localhost:8000"; +const API_URL = window.APP_CONFIG?.API_URL || "https://mpvrppythonapi.pinite37.me"; let authMode = 'login'; document.getElementById('mobile-menu').addEventListener('click', () => { diff --git a/pages/static/js/config.js b/pages/static/js/config.js new file mode 100644 index 0000000..13536b7 --- /dev/null +++ b/pages/static/js/config.js @@ -0,0 +1,3 @@ +window.APP_CONFIG = Object.freeze({ + API_URL: "https://mpvrppythonapi.pinite37.me" +}); diff --git a/pages/static/js/scoreboard.js b/pages/static/js/scoreboard.js index af4c18c..00b9165 100644 --- a/pages/static/js/scoreboard.js +++ b/pages/static/js/scoreboard.js @@ -1,5 +1,4 @@ -// load API_URL from .env file -const API_URL = 'http://localhost:8000'; +const API_URL = window.APP_CONFIG?.API_URL || "https://mpvrppythonapi.pinite37.me"; // ── Nav mobile ─────────────────────────────────────────── document.getElementById('mobile-menu').addEventListener('click', () => { diff --git a/pages/submission.html b/pages/submission.html index f13a46c..9c636b3 100644 --- a/pages/submission.html +++ b/pages/submission.html @@ -22,7 +22,7 @@