"""Production Flask app for meal-order-manager. Local: PYTHONPATH=src:src/shared python3 -m server.app Prod: gunicorn server.wsgi:app """ from __future__ import annotations import os from pathlib import Path import json from flask import Flask, Response, jsonify, request, send_file from server import http_api CORS_ORIGINS = [ "https://orders.seahaven.com", "https://internal.seahaven.com", "https://internal.dev.seahaven.com", "http://localhost:5173", "http://localhost:4173", "http://127.0.0.1:5173", "http://127.0.0.1:5050", ] PROJECT_ROOT = Path(__file__).resolve().parents[2] OUTPUT_DIR = PROJECT_ROOT / "output" def create_app() -> Flask: app = Flask(__name__) extra = os.environ.get("CORS_ORIGINS", "") origins = list(CORS_ORIGINS) if extra: origins.extend(o.strip() for o in extra.split(",") if o.strip()) form_url = os.environ.get("FORM_URL", "").rstrip("/") if form_url and form_url not in origins: origins.append(form_url) @app.after_request def add_cors(resp: Response) -> Response: origin = request.headers.get("Origin", "") if origin in origins: resp.headers["Access-Control-Allow-Origin"] = origin resp.headers["Vary"] = "Origin" resp.headers["Access-Control-Allow-Headers"] = ( "Authorization, Content-Type, X-Meals-Publish-Key" ) resp.headers["Access-Control-Allow-Methods"] = ( "GET, POST, PUT, DELETE, OPTIONS" ) resp.headers["Access-Control-Max-Age"] = "3600" return resp @app.route("/api/health") def health(): return jsonify( { "stage": os.environ.get("STAGE", "local"), "sha": os.environ.get("GIT_SHA", "dev"), } ) @app.route("/", methods=["GET"]) def root(): if os.environ.get("STAGE", "local") == "local": from datetime import datetime form_file = OUTPUT_DIR / ( f"order-form-{datetime.now().strftime('%Y-W%U')}.html" ) if form_file.exists(): return send_file(form_file) return "ok", 200 def _dispatch(path: str): if request.method == "OPTIONS": return "", 204 qs = request.args.to_dict(flat=True) path_params = {} parts = path.strip("/").split("/") if ( len(parts) >= 3 and parts[0] == "api" and parts[1] in { "menu", "orders", "form-status", } ): path_params["week"] = parts[2] event = { "requestContext": {"http": {"method": request.method, "path": path}}, "rawPath": path, "headers": {k: v for k, v in request.headers.items()}, "body": request.get_data(as_text=True) or "{}", "pathParameters": path_params, "queryStringParameters": qs or None, } result = http_api.lambda_handler(event, None) try: payload = json.loads(result["body"]) except (TypeError, KeyError, json.JSONDecodeError): resp = jsonify({"error": "Internal error"}) resp.status_code = 500 return resp if not isinstance(payload, dict): resp = jsonify({"error": "Internal error"}) resp.status_code = 500 return resp resp = jsonify(payload) resp.status_code = int(result.get("statusCode") or 500) return resp @app.route("/api/", methods=["GET", "POST", "PUT", "DELETE", "OPTIONS"]) def api(rest: str): return _dispatch("/api/" + rest) return app app = create_app() def main(): app.run(host="0.0.0.0", port=int(os.environ.get("PORT", "5050"))) if __name__ == "__main__": main()