import os import logging from flask import Flask from dotenv import load_dotenv load_dotenv() logging.basicConfig(level=logging.INFO) # Sentry — guarded on SENTRY_DSN being set, so earlier phases and local # dev never silently depend on it (CLAUDE.md §22 Observability). Must # be initialised before the Flask app is constructed so FlaskIntegration # can instrument it. SENTRY_DSN = os.getenv('SENTRY_DSN') if SENTRY_DSN: import sentry_sdk from sentry_sdk.integrations.flask import FlaskIntegration from sentry_sdk.integrations.rq import RqIntegration def _scrub_sensitive_data(event, hint): """Removes headers (which may carry X-API-Key/session tokens) from Sentry events. PII must never reach Sentry.""" if 'request' in event: event['request'].pop('headers', None) return event sentry_sdk.init( dsn=SENTRY_DSN, integrations=[FlaskIntegration(), RqIntegration()], traces_sample_rate=0.1, environment=os.getenv('FLASK_ENV', 'production'), send_default_pii=False, before_send=_scrub_sensitive_data, ) from extensions import limiter, cors # noqa: E402 from api import api_bp # noqa: E402 # Entry point only — imports and wires everything together. Business # logic lives in services/, data access in repositories/, routes in # api.py. Nothing else belongs in this file per CLAUDE.md §5/§18. app = Flask(__name__) limiter.init_app(app) cors.init_app(app) app.register_blueprint(api_bp) if __name__ == '__main__': app.run(host='0.0.0.0', port=5000, debug=False)