File size: 4,247 Bytes
7644eac
 
 
 
 
 
 
 
7362a57
7644eac
 
 
 
 
 
 
 
7362a57
7644eac
 
 
 
 
 
7362a57
 
7644eac
 
 
7362a57
 
a5cfef0
e7ac0a2
 
 
 
7362a57
e7ac0a2
7644eac
 
 
 
 
 
 
 
9b44947
 
7644eac
 
 
 
 
 
9b44947
7644eac
9b44947
 
 
 
 
7644eac
 
e7ac0a2
 
7644eac
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
import os
import redis
from rq import Queue
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager
from flask_migrate import Migrate
from flask_cors import CORS
from flask_wtf.csrf import CSRFProtect
from config import Config
from werkzeug.middleware.proxy_fix import ProxyFix

db = SQLAlchemy()
login_manager = LoginManager()
login_manager.login_view = 'auth.login'  # Route for @login_required
login_manager.login_message_category = 'info'
migrate = Migrate()
csrf = CSRFProtect()


def create_app(config_class=Config):
    app = Flask(__name__)
    app.config.from_object(config_class)

    # If the app is running behind a proxy (like on Render or HF Spaces), fix the WSGI environment
    if os.environ.get('RENDER') or os.environ.get('SPACE_ID'):
        app.wsgi_app = ProxyFix(app.wsgi_app, x_for=1,
                                x_proto=1, x_host=1, x_prefix=1)

    # Initialize CSRF protection
    csrf.init_app(app)

    # Exempt API endpoints from CSRF (they use token auth)
    @csrf.exempt
    def csrf_exempt_api():
        pass

    # Enable CORS for API routes only (not for auth pages)
    # This allows requests from Codespace frontend and mobile app
    allowed_origins = [
        "http://localhost:3000",   # React frontend
        "http://localhost:8081",   # Expo mobile app (web)
        "http://127.0.0.1:3000",
        "http://127.0.0.1:8081",
        "http://localhost:19006",  # Expo web
        "http://127.0.0.1:19006",
        "http://localhost:8082",   # Expo web alternate
        "http://127.0.0.1:8082",
    ]
    # Add any additional origins from environment
    extra_origin = os.environ.get('FRONTEND_ORIGIN')
    if extra_origin and extra_origin not in allowed_origins:
        allowed_origins.append(extra_origin)

    # Apply CORS to /api/* and /auth/api/* routes for mobile app support
    CORS(app,
         resources={
             r"/api/*": {"origins": "*"},
             r"/auth/api/*": {"origins": "*"},
             r"/health": {"origins": "*"}
         },
         methods=["GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH"],
         allow_headers=["Content-Type", "Authorization", "X-Requested-With"],
         supports_credentials=False,
         max_age=3600)

    # Set DEV_MODE from environment
    app.config['DEV_MODE'] = os.environ.get(
        'DEV_MODE', 'False').lower() == 'true'
    if app.config['DEV_MODE']:
        print("\033[93m⚠️  Running in DEV_MODE - API calls will be stubbed!\033[0m")

    db.init_app(app)
    login_manager.init_app(app)
    migrate.init_app(app, db)

    # Initialize Redis connection for RQ
    try:
        redis_url = os.environ.get('REDIS_URL')
        if not redis_url:
            raise ValueError(
                "REDIS_URL not set, worker queue will not be available.")
        # ssl_cert_reqs=None is important for managed services like Upstash/Render Redis
        app.redis = redis.from_url(redis_url, ssl_cert_reqs=None)
        app.logger.info("Redis connection for RQ initialized successfully.")
    except Exception as e:
        app.logger.error(f"Failed to initialize Redis connection: {e}")
        app.redis = None

    # Import and register blueprints
    from web_app.main_routes import bp as main_bp
    app.register_blueprint(main_bp)

    from web_app.auth_routes import bp as auth_bp
    app.register_blueprint(auth_bp, url_prefix='/auth')

    from web_app.api_endpoints import api_bp
    app.register_blueprint(api_bp)

    # Assessment API blueprint
    from web_app.assessment_routes import assessment_bp
    app.register_blueprint(assessment_bp)

    # Import models here to ensure they are registered with SQLAlchemy
    from web_app import models

    # Google OAuth blueprint (Flask-Dance)
    from web_app.google_oauth import google_bp, bp as google_auth_bp
    # Register Flask-Dance blueprint at /login/google
    app.register_blueprint(google_bp, url_prefix="/login")
    # Register our auth blueprint for callbacks and helper routes under /auth
    app.register_blueprint(google_auth_bp, url_prefix="/auth")

    # Flask-Dance will use session storage by default
    # This works better for our use case since we create the user in our callback

    return app