import os import click from datetime import timedelta from flask import Flask from dotenv import load_dotenv from flask_migrate import Migrate from flask_jwt_extended import JWTManager from flask_cors import CORS from models import db, bcrypt, User from celery_app import celery # Import celery instance def create_app(): """Application Factory Pattern""" load_dotenv() app = Flask(__name__) # --- Configuration --- app.config.from_mapping( SQLALCHEMY_DATABASE_URI=os.environ.get('DATABASE_URL'), SQLALCHEMY_ENGINE_OPTIONS={ 'pool_recycle': 3600, 'pool_size': 20, 'max_overflow': 30, 'pool_pre_ping': True, 'pool_timeout': 30 }, JWT_SECRET_KEY=os.environ.get('JWT_SECRET_KEY'), SQLALCHEMY_TRACK_MODIFICATIONS=False, JWT_ACCESS_TOKEN_EXPIRES=timedelta(days=2), CELERY_BROKER_URL=os.environ.get('CELERY_BROKER_URL', 'redis://localhost:6379/0'), CELERY_RESULT_BACKEND=os.environ.get('CELERY_RESULT_BACKEND', 'redis://localhost:6379/0'), DIFY_API_BASE_URL=os.environ.get("DIFY_API_BASE_URL"), DIFY_STT_API_KEY=os.environ.get("DIFY_STT_API_KEY"), DIFY_TRANSLATOR_API_KEY=os.environ.get("DIFY_TRANSLATOR_API_KEY"), DIFY_SUMMARIZER_API_KEY=os.environ.get("DIFY_SUMMARIZER_API_KEY"), DIFY_ACTION_EXTRACTOR_API_KEY=os.environ.get("DIFY_ACTION_EXTRACTOR_API_KEY"), # LDAP Configuration LDAP_SERVER=os.environ.get('LDAP_SERVER', 'panjit.com.tw'), LDAP_PORT=int(os.environ.get('LDAP_PORT', 389)), LDAP_USE_SSL=os.environ.get('LDAP_USE_SSL', 'False').lower() == 'true', LDAP_BIND_USER_DN=os.environ.get('LDAP_BIND_USER_DN', ''), LDAP_BIND_USER_PASSWORD=os.environ.get('LDAP_BIND_USER_PASSWORD', ''), LDAP_SEARCH_BASE=os.environ.get('LDAP_SEARCH_BASE', 'DC=panjit,DC=com,DC=tw'), LDAP_USER_LOGIN_ATTR=os.environ.get('LDAP_USER_LOGIN_ATTR', 'userPrincipalName') ) project_root = os.path.dirname(os.path.abspath(__file__)) UPLOAD_FOLDER = os.path.join(project_root, 'uploads') if not os.path.exists(UPLOAD_FOLDER): os.makedirs(UPLOAD_FOLDER) app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER app.config['MAX_CONTENT_LENGTH'] = 1024 * 1024 * 1024 # 1GB upload limit # --- Initialize Extensions --- db.init_app(app) bcrypt.init_app(app) Migrate(app, db) JWTManager(app) CORS(app) # --- Configure Celery --- celery.conf.update(app.config) # This custom Task class ensures tasks run with the Flask app context class ContextTask(celery.Task): def __call__(self, *args, **kwargs): with app.app_context(): return self.run(*args, **kwargs) celery.Task = ContextTask # --- Import and Register Blueprints --- from auth_routes import auth_bp from api_routes import api_bp from ai_routes import ai_bp from action_item_routes import action_bp app.register_blueprint(auth_bp) app.register_blueprint(api_bp) app.register_blueprint(ai_bp) app.register_blueprint(action_bp) # --- Static File Serving (for Single Container) --- from flask import send_from_directory, send_file # Serve React build files @app.route('/') def serve_frontend(): try: return send_file(os.path.join(app.root_path, 'frontend/dist/index.html')) except: return "AI Meeting Assistant is running. Frontend build not found." @app.route('/') def serve_static(path): # Try to serve static files first try: return send_from_directory(os.path.join(app.root_path, 'frontend/dist'), path) except: # If not found, serve index.html for SPA routing try: return send_file(os.path.join(app.root_path, 'frontend/dist/index.html')) except: return "File not found", 404 # --- CLI Commands --- @app.cli.command("create_admin") @click.argument("username") @click.argument("password") def create_admin(username, password): """Creates a new admin user.""" with app.app_context(): try: if User.query.filter_by(username=username).first(): print(f"Error: User '{username}' already exists.") return admin_user = User(username=username, role='admin') admin_user.set_password(password) db.session.add(admin_user) db.session.commit() print(f"Admin user '{username}' created successfully.") except Exception as e: db.session.rollback() print(f"An error occurred: {e}") return app app = create_app() if __name__ == '__main__': port = int(os.environ.get("FLASK_RUN_PORT", 5000)) app.run(host='0.0.0.0', port=port, debug=True)