import os from flask import Flask, redirect, url_for, request, g, session, flash # import blueprints from app.blueprints.auth import auth_bp from app.blueprints.main import main_bp from app.blueprints.char import char_bp from app.blueprints.public import public_bp from app.blueprints.ajax import ajax_bp # load_dotenv() from .utils.settings import get_settings from app.utils.logging import get_logger from .utils.session_user import SessionUser logger = get_logger() settings = get_settings() def create_app(): app = Flask(__name__, template_folder="templates") app.config.update( SECRET_KEY=settings.flask_secret_key, APPWRITE_ENDPOINT=settings.appwrite_endpoint, APPWRITE_PROJECT_ID=settings.appwrite_project_id, APPWRITE_API_KEY=settings.appwrite_api_key, ) if not app.config["APPWRITE_ENDPOINT"] or not app.config["APPWRITE_PROJECT_ID"]: raise RuntimeError("Missing APPWRITE_ENDPOINT or APPWRITE_PROJECT_ID") # Blueprints app.register_blueprint(auth_bp) app.register_blueprint(main_bp) app.register_blueprint(char_bp) app.register_blueprint(public_bp) app.register_blueprint(ajax_bp) @app.before_request def require_login(): """Gate all routes behind a session 'user' except auth + static.""" # Always allow static files if request.endpoint == "static": return # Endpoints that should be accessible without being logged in public_endpoints = [ "auth.login", "auth.register", "auth.verify", "auth.callback", "auth.send_verification", # add any health checks or webhooks here "public.home", ] # Make session user easy to access in views/templates g.user = session.get("user") endpoint = (request.endpoint or "") # Let any route under the auth blueprint through (login/verify/etc.) if endpoint.startswith("public.") or endpoint.startswith("auth."): return if endpoint in public_endpoints: return # Block everything else unless logged in if g.user is None: # preserve destination for GETs next_url = request.url if request.method == "GET" else url_for("auth.login") flash("Please log in to continue.", "warning") return redirect(url_for("auth.login", next=next_url)) @app.before_request def load_user(): user_data = session.get("user") if user_data: g.current_user = SessionUser( id=user_data.get("$id",""), registered_on=user_data.get("registration",""), email=user_data.get("email",""), email_verified=user_data.get("emailVerification", False), phone=user_data.get("phone",""), phone_verified=user_data.get("phoneVerification",False), mfa=user_data.get("mfa","") ) else: # Anonymous user object with same interface class AnonymousUser: is_authenticated = False email_verification = False g.current_user = AnonymousUser() @app.context_processor def inject_globals(): """Add variables available to all Jinja templates.""" return dict( app_name=settings.app_name, app_version=settings.app_version, current_user=getattr(g, "current_user", None), ) return app