diff --git a/app.py b/app.py index 9d66504..6c59256 100644 --- a/app.py +++ b/app.py @@ -2,10 +2,12 @@ Live proposal site with real Gemini-powered demos. PostgreSQL-backed data dashboard.""" -import os, json, time, traceback, logging +import os, json, time, traceback, logging, secrets from pathlib import Path +from functools import wraps from flask import (Flask, render_template, jsonify, request, - send_from_directory, redirect) + send_from_directory, redirect, session, url_for, + make_response) from scraper import scrape_product, scrape_competitor from ai_engine import (generate_asset_pack, competitor_xray, pdp_surgeon, @@ -18,6 +20,9 @@ app = Flask(__name__, static_folder="static", template_folder="templates") +app.secret_key = os.environ.get("SECRET_KEY", secrets.token_hex(32)) +SITE_PASSWORD = os.environ.get("SITE_PASSWORD", "jv2026") + GEN_DIR = Path(__file__).parent / "generated" GEN_DIR.mkdir(exist_ok=True) @@ -28,6 +33,87 @@ with app.app_context(): except Exception as e: logging.error(f"DB init failed: {e}") + +# ── Auth ───────────────────────────────────────────────────── + +PUBLIC_PATHS = {"/login", "/api/health"} + +@app.before_request +def require_auth(): + """Gate every request behind a simple password session.""" + path = request.path + # Allow login page, health check, and static login assets + if path in PUBLIC_PATHS or path.startswith("/static/"): + return + if not session.get("authed"): + if request.is_json: + return jsonify({"error": "Authentication required"}), 401 + return redirect(url_for("login", next=request.path)) + + +LOGIN_HTML = """ +
+ +Confidential proposal — enter password to continue
+ {{ERR}} + + +