Files
gatehouse-api/app/__init__.py
T
2026-01-08 01:00:26 +10:30

176 lines
4.4 KiB
Python

"""Application factory."""
import os
import logging
from flask import Flask
from config import get_config
from app.extensions import db, migrate, bcrypt, cors, ma, limiter, session
from app.middleware import RequestIDMiddleware, SecurityHeadersMiddleware, setup_cors
from app.exceptions.base import BaseAPIException
from app.utils.response import api_response
import redis
def create_app(config_name=None):
"""
Create and configure the Flask application.
Args:
config_name: Configuration name (development, testing, production)
Returns:
Flask application instance
"""
app = Flask(__name__)
# Load configuration
config = get_config(config_name)
app.config.from_object(config)
# Initialize extensions
initialize_extensions(app)
# Setup middleware
setup_middleware(app)
# Register blueprints
register_blueprints(app)
# Register error handlers
register_error_handlers(app)
# Setup logging
setup_logging(app)
return app
def initialize_extensions(app):
"""Initialize Flask extensions."""
# Database
db.init_app(app)
migrate.init_app(app, db)
# Security
bcrypt.init_app(app)
# CORS
cors.init_app(
app,
origins=app.config.get("CORS_ORIGINS", []),
supports_credentials=app.config.get("CORS_SUPPORTS_CREDENTIALS", True),
)
# Marshmallow
ma.init_app(app)
# Rate limiting
if app.config.get("RATELIMIT_ENABLED"):
limiter.init_app(app)
# Redis for sessions
try:
redis_url = app.config.get("REDIS_URL")
if redis_url:
redis_client = redis.from_url(redis_url)
app.config["SESSION_REDIS"] = redis_client
except Exception as e:
app.logger.warning(f"Redis connection failed: {e}")
# Flask-Session
session.init_app(app)
def setup_middleware(app):
"""Setup application middleware."""
RequestIDMiddleware(app)
SecurityHeadersMiddleware(app)
setup_cors(app, cors)
def register_blueprints(app):
"""Register application blueprints."""
from app.api import register_api_blueprints
register_api_blueprints(app)
def register_error_handlers(app):
"""Register error handlers."""
@app.errorhandler(BaseAPIException)
def handle_api_exception(error):
"""Handle custom API exceptions."""
return api_response(
success=False,
message=error.message,
status=error.status_code,
error_type=error.error_type,
error_details=error.error_details,
)
@app.errorhandler(404)
def handle_not_found(error):
"""Handle 404 errors."""
return api_response(
success=False,
message="Resource not found",
status=404,
error_type="NOT_FOUND",
)
@app.errorhandler(405)
def handle_method_not_allowed(error):
"""Handle 405 errors."""
return api_response(
success=False,
message="Method not allowed",
status=405,
error_type="METHOD_NOT_ALLOWED",
)
@app.errorhandler(500)
def handle_internal_error(error):
"""Handle 500 errors."""
app.logger.error(f"Internal server error: {error}")
return api_response(
success=False,
message="Internal server error",
status=500,
error_type="INTERNAL_ERROR",
)
@app.errorhandler(Exception)
def handle_unexpected_error(error):
"""Handle unexpected errors."""
app.logger.error(f"Unexpected error: {error}", exc_info=True)
return api_response(
success=False,
message="An unexpected error occurred",
status=500,
error_type="INTERNAL_ERROR",
)
def setup_logging(app):
"""Setup application logging."""
log_level = getattr(logging, app.config.get("LOG_LEVEL", "INFO"))
# Create formatter
formatter = logging.Formatter(
"[%(asctime)s] %(levelname)s in %(module)s: %(message)s"
)
# Configure root logger
if app.config.get("LOG_TO_STDOUT"):
stream_handler = logging.StreamHandler()
stream_handler.setFormatter(formatter)
stream_handler.setLevel(log_level)
app.logger.addHandler(stream_handler)
app.logger.setLevel(log_level)
# Reduce SQLAlchemy logging noise
logging.getLogger('sqlalchemy').setLevel(logging.WARNING)
app.logger.info("Application startup")