169 lines
6.0 KiB
Python
169 lines
6.0 KiB
Python
"""淇濆崟鏅鸿兘瀹㈡湇绯荤粺 鈥?鐙珛鍚庣鍏ュ彛
|
|
|
|
鐙珛閮ㄧ讲鏃朵娇鐢ㄧ殑 Flask 搴旂敤锛屼笉渚濊禆 Dify 鎵╁睍銆?"""
|
|
import os
|
|
import logging
|
|
from logging.handlers import RotatingFileHandler
|
|
from flask import Flask
|
|
from flask_cors import CORS
|
|
from dotenv import load_dotenv
|
|
|
|
# 鍔犺浇鐜鍙橀噺
|
|
load_dotenv()
|
|
|
|
|
|
def setup_logging(app: Flask) -> None:
|
|
"""閰嶇疆缁熶竴鐨勬棩蹇楃郴缁熴€?""
|
|
log_level = app.config.get('LOG_LEVEL', 'INFO')
|
|
log_file = app.config.get('LOG_FILE', 'logs/insurance.log')
|
|
|
|
# 鍒涘缓鏃ュ織鐩綍
|
|
log_dir = os.path.dirname(log_file)
|
|
if log_dir and not os.path.exists(log_dir):
|
|
os.makedirs(log_dir, exist_ok=True)
|
|
|
|
# 閰嶇疆鏍规棩蹇楄褰曞櫒
|
|
root_logger = logging.getLogger()
|
|
root_logger.setLevel(getattr(logging, log_level.upper(), logging.INFO))
|
|
|
|
# 鎺у埗鍙拌緭鍑? console_handler = logging.StreamHandler()
|
|
console_handler.setLevel(logging.INFO)
|
|
console_format = logging.Formatter(
|
|
'%(asctime)s [%(levelname)s] %(name)s: %(message)s',
|
|
datefmt='%Y-%m-%d %H:%M:%S'
|
|
)
|
|
console_handler.setFormatter(console_format)
|
|
root_logger.addHandler(console_handler)
|
|
|
|
# 鏂囦欢杈撳嚭锛堝甫杞浆锛? try:
|
|
file_handler = RotatingFileHandler(
|
|
log_file,
|
|
maxBytes=10 * 1024 * 1024, # 10MB
|
|
backupCount=5,
|
|
encoding='utf-8'
|
|
)
|
|
file_handler.setLevel(logging.DEBUG)
|
|
file_format = logging.Formatter(
|
|
'%(asctime)s [%(levelname)s] %(name)s [%(filename)s:%(lineno)d]: %(message)s',
|
|
datefmt='%Y-%m-%d %H:%M:%S'
|
|
)
|
|
file_handler.setFormatter(file_format)
|
|
root_logger.addHandler(file_handler)
|
|
except Exception as e:
|
|
print(f"[logging] 鏂囦欢鏃ュ織閰嶇疆澶辫触: {e}")
|
|
|
|
# 灏?print 鏇挎崲涓?logging
|
|
import builtins
|
|
original_print = builtins.print
|
|
def logging_print(*args, **kwargs):
|
|
msg = ' '.join(str(arg) for arg in args)
|
|
logging.info(msg)
|
|
builtins.print = logging_print
|
|
|
|
|
|
def create_app() -> Flask:
|
|
"""鍒涘缓 Flask 搴旂敤銆?""
|
|
app = Flask(__name__)
|
|
|
|
# 閰嶇疆
|
|
app.config['SECRET_KEY'] = os.getenv('JWT_SECRET', 'baodan-jwt-secret-2026')
|
|
app.config['JWT_SECRET'] = os.getenv('JWT_SECRET', 'baodan-jwt-secret-2026')
|
|
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
|
|
app.config['REDIS_URL'] = os.getenv('REDIS_URL')
|
|
|
|
# 閰嶇疆鏃ュ織
|
|
setup_logging(app)
|
|
|
|
# 鏁版嵁搴擄細浼樺厛 DATABASE_URL锛屽叾娆?DB_* 鍙橀噺鏋勫缓 PostgreSQL URL锛屾渶鍚庡洖閫€鍒?SQLite
|
|
db_url = os.getenv('DATABASE_URL', '')
|
|
if not db_url:
|
|
# 浠?DB_* 鐜鍙橀噺鏋勫缓 PostgreSQL URL
|
|
db_host = os.getenv('DB_HOST', '')
|
|
db_port = os.getenv('DB_PORT', '5432')
|
|
db_user = os.getenv('DB_USERNAME', '')
|
|
db_pass = os.getenv('DB_PASSWORD', '')
|
|
db_name = os.getenv('DB_DATABASE', '')
|
|
if db_host and db_user and db_name:
|
|
db_url = f"postgresql://{db_user}:{db_pass}@{db_host}:{db_port}/{db_name}"
|
|
if db_url and 'postgresql' in db_url:
|
|
# 妫€鏌?psycopg2 鏄惁鍙敤
|
|
try:
|
|
import psycopg2
|
|
app.config['SQLALCHEMY_DATABASE_URI'] = db_url
|
|
except ImportError:
|
|
print("[warning] psycopg2 鏈畨瑁咃紝鍥為€€鍒?SQLite")
|
|
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///baodan.db'
|
|
else:
|
|
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///baodan.db'
|
|
app.config['MIGRATION_ENABLED'] = True
|
|
|
|
# Dify 閰嶇疆
|
|
app.config['BAODAN_API_URL'] = os.getenv('DIFY_BASE_URL', 'http://localhost:5001')
|
|
app.config['BAODAN_CHAT_API_KEY'] = os.getenv('DIFY_CHAT_APP_API_KEY', '')
|
|
app.config['BAODAN_WORKFLOW_API_KEY'] = os.getenv('DIFY_WORKFLOW_APP_API_KEY', '')
|
|
|
|
# 浼佷笟寰俊閰嶇疆
|
|
app.config['WECOM_CORP_ID'] = os.getenv('WECOM_CORP_ID', '')
|
|
app.config['WECOM_SECRET'] = os.getenv('WECOM_SECRET', '')
|
|
app.config['WECOM_TOKEN'] = os.getenv('WECOM_TOKEN', '')
|
|
app.config['WECOM_AES_KEY'] = os.getenv('WECOM_AES_KEY', '')
|
|
app.config['WECOM_WEBHOOK_URL'] = os.getenv('WECOM_WEBHOOK_URL', '')
|
|
|
|
# 璁垮妯″紡
|
|
app.config['GUEST_MODE'] = os.getenv('GUEST_MODE', 'true').lower() == 'true'
|
|
|
|
# 鍩熷悕锛堝垎浜摼鎺ョ敤锛? app.config['DOMAIN'] = os.getenv('DOMAIN', 'localhost')
|
|
|
|
# CORS
|
|
CORS(app, resources={r"/*": {"origins": "*"}})
|
|
|
|
# 鍒濆鍖栨暟鎹簱
|
|
from insurance.db.database import init_db
|
|
init_db(app)
|
|
|
|
# 鍒濆鍖?Redis
|
|
from insurance.db.redis import init_redis
|
|
init_redis(app)
|
|
|
|
# 娉ㄥ唽璺敱
|
|
from insurance.routes import register_insurance_routes
|
|
register_insurance_routes(app)
|
|
|
|
# 娉ㄥ唽閿欒澶勭悊鍣? from insurance.utils.error_handler import register_error_handlers
|
|
register_error_handlers(app)
|
|
|
|
# 鍋ュ悍妫€鏌ワ紙妫€鏌ュ疄闄呬緷璧栵級
|
|
@app.route('/health')
|
|
def health():
|
|
checks = {'status': 'ok', 'service': 'baodanagent-api'}
|
|
try:
|
|
from insurance.db.compat import db
|
|
db.session.execute(db.text('SELECT 1'))
|
|
checks['database'] = 'ok'
|
|
except Exception as e:
|
|
checks['database'] = f'error: {str(e)[:100]}'
|
|
checks['status'] = 'degraded'
|
|
try:
|
|
from insurance.db.compat import redis_client
|
|
if redis_client:
|
|
redis_client.ping()
|
|
checks['redis'] = 'ok'
|
|
else:
|
|
checks['redis'] = 'not configured'
|
|
except Exception as e:
|
|
checks['redis'] = f'error: {str(e)[:100]}'
|
|
checks['status'] = 'degraded'
|
|
return checks
|
|
|
|
return app
|
|
|
|
|
|
# 鍒涘缓搴旂敤瀹炰緥
|
|
app = create_app()
|
|
|
|
if __name__ == '__main__':
|
|
port = int(os.getenv('PORT', 5001))
|
|
debug = os.getenv('FLASK_DEBUG', 'false').lower() == 'true'
|
|
app.run(host='0.0.0.0', port=port, debug=debug)
|
|
|