import os from flask import Flask, send_from_directory from flask_sqlalchemy import SQLAlchemy from flask_migrate import Migrate from flask_cors import CORS from config.settings import config db = SQLAlchemy() migrate = Migrate() def create_app(config_name=None): """Application factory pattern""" if config_name is None: config_name = os.environ.get('FLASK_ENV', 'development') # Check if static folder exists (for Docker deployment) static_folder = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'static') if os.path.exists(static_folder): app = Flask(__name__, static_folder=static_folder, static_url_path='') else: app = Flask(__name__) app.config.from_object(config[config_name]) # Initialize extensions db.init_app(app) migrate.init_app(app, db) CORS(app) # Create upload directories os.makedirs(app.config.get('UPLOAD_FOLDER', 'uploads'), exist_ok=True) os.makedirs(app.config.get('REPORTS_FOLDER', 'reports'), exist_ok=True) # Register blueprints from app.api import api_bp app.register_blueprint(api_bp, url_prefix='/api') # Register error handlers from app.errors import register_error_handlers register_error_handlers(app) # Serve frontend static files (for Docker deployment) # This must be registered AFTER error handlers to properly catch SPA routes if app.static_folder and os.path.exists(app.static_folder): @app.route('/') def serve_index(): return send_from_directory(app.static_folder, 'index.html') # Catch-all route for SPA - must be the last route registered @app.errorhandler(404) def serve_spa(error): # Check if this is an API request from flask import request if request.path.startswith('/api/'): # Return JSON error for API requests return {'error': {'code': 'RESOURCE_NOT_FOUND', 'message': 'Resource not found'}}, 404 # For non-API requests, serve index.html for SPA routing return send_from_directory(app.static_folder, 'index.html') return app