Files
Document_Translator/app/__init__.py
2025-09-04 16:37:33 +08:00

218 lines
6.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Flask 應用程式工廠
Author: PANJIT IT Team
Created: 2024-01-28
Modified: 2024-01-28
"""
import os
import redis
from flask import Flask, request, make_response
from flask_sqlalchemy import SQLAlchemy
from flask_cors import CORS
from flask_jwt_extended import JWTManager
from celery import Celery
from app.config import config
from app.utils.logger import init_logging
# 初始化擴展
db = SQLAlchemy()
cors = CORS()
jwt = JWTManager()
def make_celery(app):
"""創建 Celery 實例"""
celery = Celery(
app.import_name,
backend=app.config['CELERY_RESULT_BACKEND'],
broker=app.config['CELERY_BROKER_URL']
)
celery.conf.update(app.config)
class ContextTask(celery.Task):
"""在 Flask 應用上下文中執行任務"""
def __call__(self, *args, **kwargs):
with app.app_context():
return self.run(*args, **kwargs)
celery.Task = ContextTask
return celery
def create_app(config_name=None):
"""應用程式工廠"""
app = Flask(__name__)
# 載入配置
config_name = config_name or os.getenv('FLASK_ENV', 'default')
# 先載入 Dify API 配置
config[config_name].load_dify_config()
# 然後載入配置到 Flask app
app.config.from_object(config[config_name])
# 初始化必要目錄
config[config_name].init_directories()
# 初始化擴展
db.init_app(app)
# 不使用 Flask-CORS 避免衝突使用手動CORS處理
# 初始化 JWT
jwt.init_app(app)
app.logger.info(f"🔑 [JWT Config] JWT_SECRET_KEY: {app.config.get('JWT_SECRET_KEY')[:10]}...{app.config.get('JWT_SECRET_KEY')[-10:] if app.config.get('JWT_SECRET_KEY') else 'None'}")
app.logger.info(f"🔑 [JWT Config] JWT_ACCESS_TOKEN_EXPIRES: {app.config.get('JWT_ACCESS_TOKEN_EXPIRES')}")
app.logger.info(f"🔑 [JWT Config] JWT_REFRESH_TOKEN_EXPIRES: {app.config.get('JWT_REFRESH_TOKEN_EXPIRES')}")
app.logger.info("🔑 [JWT] Using JWT authentication")
# 設定 Redis用於Celery
try:
redis_client = redis.from_url(app.config['REDIS_URL'])
app.redis_client = redis_client
except Exception as e:
app.logger.warning(f"Redis initialization failed: {str(e)}")
app.redis_client = None
# 初始化日誌
init_logging(app)
# 註冊 API 路由
from app.api import api_v1
app.register_blueprint(api_v1)
# 註冊錯誤處理器
register_error_handlers(app)
# 添加 CORS 響應headers
@app.after_request
def after_request(response):
origin = request.headers.get('Origin')
allowed_origins = ['http://localhost:3000', 'http://127.0.0.1:3000', 'http://localhost:3001', 'http://127.0.0.1:3001', 'http://localhost:12010', 'http://127.0.0.1:12010']
if origin and origin in allowed_origins:
response.headers['Access-Control-Allow-Origin'] = origin
response.headers['Access-Control-Allow-Headers'] = 'Content-Type, Authorization, X-Requested-With'
response.headers['Access-Control-Allow-Methods'] = 'GET, POST, PUT, DELETE, OPTIONS, PATCH'
response.headers['Access-Control-Allow-Credentials'] = 'true'
response.headers['Access-Control-Max-Age'] = '86400'
return response
# 處理 OPTIONS 預檢請求
@app.before_request
def before_request():
if request.method == 'OPTIONS':
response = make_response()
origin = request.headers.get('Origin')
allowed_origins = ['http://localhost:3000', 'http://127.0.0.1:3000', 'http://localhost:3001', 'http://127.0.0.1:3001', 'http://localhost:12010', 'http://127.0.0.1:12010']
if origin and origin in allowed_origins:
response.headers['Access-Control-Allow-Origin'] = origin
response.headers['Access-Control-Allow-Headers'] = 'Content-Type, Authorization, X-Requested-With'
response.headers['Access-Control-Allow-Methods'] = 'GET, POST, PUT, DELETE, OPTIONS, PATCH'
response.headers['Access-Control-Allow-Credentials'] = 'true'
response.headers['Access-Control-Max-Age'] = '86400'
return response
# 建立資料表
with app.app_context():
# 導入模型
from app.models import User, TranslationJob, JobFile, TranslationCache, APIUsageStats, SystemLog, Notification
db.create_all()
# 創建默認管理員用戶(如果不存在)
create_default_admin()
# 創建 Celery 實例
app.celery = make_celery(app)
# 初始化 WebSocket
from app.websocket import init_websocket
app.socketio = init_websocket(app)
app.logger.info("Flask application created successfully")
return app
def register_error_handlers(app):
"""註冊錯誤處理器"""
@app.errorhandler(404)
def not_found(error):
return {
'success': False,
'error': 'NOT_FOUND',
'message': '請求的資源不存在'
}, 404
@app.errorhandler(403)
def forbidden(error):
return {
'success': False,
'error': 'FORBIDDEN',
'message': '權限不足'
}, 403
@app.errorhandler(401)
def unauthorized(error):
return {
'success': False,
'error': 'UNAUTHORIZED',
'message': '需要認證'
}, 401
@app.errorhandler(500)
def internal_server_error(error):
return {
'success': False,
'error': 'INTERNAL_SERVER_ERROR',
'message': '系統內部錯誤'
}, 500
@app.errorhandler(413)
def request_entity_too_large(error):
return {
'success': False,
'error': 'FILE_TOO_LARGE',
'message': '檔案大小超過限制'
}, 413
def create_default_admin():
"""創建默認管理員用戶"""
try:
from app.models import User
admin_email = os.environ.get('ADMIN_EMAIL', 'ymirliu@panjit.com.tw')
# 檢查是否已存在管理員
admin_user = User.query.filter_by(email=admin_email).first()
if not admin_user:
# 創建管理員用戶(待 LDAP 登入時完善資訊)
admin_user = User(
username=admin_email.split('@')[0],
display_name='系統管理員',
email=admin_email,
department='IT',
is_admin=True
)
db.session.add(admin_user)
db.session.commit()
print(f"Created default admin user: {admin_email}")
except Exception as e:
print(f"Failed to create default admin: {str(e)}")
# 導入模型在需要時才進行,避免循環導入