Files
outlookEmailPlus/outlook_web/app.py
T
胡绍康andClaude Opus 4.6 5af073caa8 fix: 修复 CSRF token 导入错误并豁免临时邮箱 API
- 在 csrf.py 添加模块级别 generate_csrf() 函数,解决 /api/csrf-token 端点导入错误
- 为临时邮箱 API 路由添加 CSRF 豁免,避免 Session 认证下的 CSRF token 缺失错误
- 重构 temp_emails.py 路由注册逻辑,支持统一应用 csrf_exempt 装饰器

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 17:56:45 +08:00

130 lines
4.6 KiB
Python

from __future__ import annotations
from typing import Optional
_APP_INSTANCE = None
def create_app(*, autostart_scheduler: Optional[bool] = None):
"""
应用工厂(迁移期实现):
- 统一装配入口,便于测试与后续 Blueprint/分层拆分
- 控制 import-time 副作用:初始化/调度器启动放到 create_app 中受控执行
- routes 采用 Blueprint 模块化注册(URL 不变)
"""
global _APP_INSTANCE
if _APP_INSTANCE is None:
from pathlib import Path
from flask import Flask
from werkzeug.middleware.proxy_fix import ProxyFix
from werkzeug.exceptions import HTTPException
from outlook_web import config
from outlook_web.db import register_db, init_db
from outlook_web.security.csrf import init_csrf
from outlook_web.middleware import (
ensure_trace_id,
attach_trace_id_and_normalize_errors,
handle_http_exception,
handle_exception,
)
from outlook_web.routes import (
accounts,
audit,
emails,
groups,
oauth,
pages,
scheduler,
settings,
system,
tags,
temp_emails,
)
# 初始化(DB/目录等)
repo_root = Path(__file__).resolve().parents[1]
templates_dir = repo_root / "templates"
static_dir = repo_root / "static"
# 确保目录存在
templates_dir.mkdir(parents=True, exist_ok=True)
static_dir.mkdir(parents=True, exist_ok=True)
# 确保数据目录存在
data_dir = config.get_database_path()
if data_dir:
import os
os.makedirs(os.path.dirname(data_dir) if os.path.dirname(data_dir) else ".", exist_ok=True)
# 初始化数据库
init_db()
app = Flask(
__name__,
template_folder=str(templates_dir),
static_folder=str(static_dir),
static_url_path="/static",
)
app.secret_key = config.require_secret_key()
app.config["PERMANENT_SESSION_LIFETIME"] = 60 * 60 * 24 * 7 # 7 天
app.config["SESSION_COOKIE_HTTPONLY"] = True
app.config["SESSION_COOKIE_SAMESITE"] = "Lax"
# ProxyFix 中间件(仅在配置启用时应用)
# 注意:启用前必须配置 TRUSTED_PROXIES 环境变量
if config.get_proxy_fix_enabled():
app.wsgi_app = ProxyFix(app.wsgi_app, x_for=1, x_proto=1, x_host=1, x_prefix=1)
# DB teardown(请求结束释放连接)
register_db(app)
# CSRF(可选)
_csrf, csrf_exempt, _generate_csrf = init_csrf(app)
# trace_id + error 结构标准化(迁移到 middleware 模块)
app.before_request(ensure_trace_id)
app.after_request(attach_trace_id_and_normalize_errors)
app.register_error_handler(HTTPException, handle_http_exception)
app.register_error_handler(Exception, handle_exception)
# Blueprint 路由注册(URL 不变)
app.register_blueprint(pages.create_blueprint(csrf_exempt=csrf_exempt))
app.register_blueprint(groups.create_blueprint())
app.register_blueprint(tags.create_blueprint())
app.register_blueprint(accounts.create_blueprint())
app.register_blueprint(emails.create_blueprint())
app.register_blueprint(temp_emails.create_blueprint(csrf_exempt=csrf_exempt))
app.register_blueprint(oauth.create_blueprint())
app.register_blueprint(settings.create_blueprint())
app.register_blueprint(scheduler.create_blueprint())
app.register_blueprint(system.create_blueprint())
app.register_blueprint(audit.create_blueprint())
# 打印初始化信息
print("=" * 60)
print("Outlook 邮件 Web 应用已初始化")
print(f"数据库文件: {config.get_database_path()}")
print(f"GPTMail API: {config.get_gptmail_base_url()}")
print("=" * 60)
_APP_INSTANCE = app
# 调度器启动控制
if autostart_scheduler is None:
from outlook_web.services import scheduler as scheduler_service
from outlook_web.services import graph as graph_service
if scheduler_service.should_autostart_scheduler():
scheduler_service.init_scheduler(_APP_INSTANCE, graph_service.test_refresh_token)
elif autostart_scheduler:
from outlook_web.services import scheduler as scheduler_service
from outlook_web.services import graph as graph_service
scheduler_service.init_scheduler(_APP_INSTANCE, graph_service.test_refresh_token)
return _APP_INSTANCE