import os from datetime import timedelta from flask import Flask from flask_cors import CORS from flask_jwt_extended import JWTManager from flask_socketio import SocketIO from app.database import init_db from app.services.auth_service import AuthService from app.services.scheduler_service import scheduler_service # Global service instances auth_service = AuthService() socketio = SocketIO() def create_app(): """Create and configure the Flask application.""" app = Flask(__name__) # Configure Flask secret key (required for sessions used by OAuth) app.config["SECRET_KEY"] = os.environ.get("SECRET_KEY", "dev-secret-key") # Configure SQLAlchemy database database_url = os.environ.get("DATABASE_URL", "sqlite:///soundboard.db") app.config["SQLALCHEMY_DATABASE_URI"] = database_url app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False # Configure Flask-JWT-Extended app.config["JWT_SECRET_KEY"] = os.environ.get( "JWT_SECRET_KEY", "jwt-secret-key", ) app.config["JWT_ACCESS_TOKEN_EXPIRES"] = timedelta(minutes=15) app.config["JWT_REFRESH_TOKEN_EXPIRES"] = timedelta(days=7) app.config["JWT_TOKEN_LOCATION"] = ["cookies"] app.config["JWT_COOKIE_SECURE"] = False # Set to True in production app.config["JWT_COOKIE_CSRF_PROTECT"] = False app.config["JWT_ACCESS_COOKIE_PATH"] = ( "/" # Allow access to all paths including SocketIO ) app.config["JWT_REFRESH_COOKIE_PATH"] = "/api/auth/refresh" # Initialize CORS CORS( app, origins=["http://localhost:3000"], # Frontend URL supports_credentials=True, # Allow cookies allow_headers=["Content-Type", "Authorization"], methods=["GET", "POST", "PATCH", "PUT", "DELETE", "OPTIONS"], ) # Initialize SocketIO socketio.init_app( app, cors_allowed_origins="http://localhost:3000", cors_credentials=True, ) # Initialize JWT manager jwt = JWTManager(app) # Initialize database init_db(app) # Initialize database tables and seed data with app.app_context(): from app.database_init import init_database init_database() # Initialize authentication service with app auth_service.init_app(app) # Initialize SocketIO service (import after socketio is initialized) from app.services.socketio_service import socketio_service # noqa: F401 # Initialize scheduler service with app scheduler_service.app = app # Start scheduler for background tasks scheduler_service.start() # Initialize stream processing service from app.services.stream_processing_service import StreamProcessingService StreamProcessingService.initialize(app) # Initialize music player service from app.services.music_player_service import music_player_service music_player_service.app = app # Store app instance for Flask context music_player_service.start_vlc_instance() # Register blueprints from app.routes import ( admin, admin_sounds, auth, main, player, soundboard, stream, ) app.register_blueprint(main.bp, url_prefix="/api") app.register_blueprint(auth.bp, url_prefix="/api/auth") app.register_blueprint(admin.bp, url_prefix="/api/admin") app.register_blueprint(admin_sounds.bp, url_prefix="/api/admin/sounds") app.register_blueprint(soundboard.bp, url_prefix="/api/soundboard") app.register_blueprint(stream.bp, url_prefix="/api/stream") app.register_blueprint(player.bp, url_prefix="/api/player") # Shutdown services when app is torn down @app.teardown_appcontext def shutdown_services(exception): """Stop services when app context is torn down.""" if exception: scheduler_service.stop() music_player_service.stop_vlc_instance() return app