feat: 新增平台

This commit is contained in:
2025-11-30 19:49:25 +08:00
parent c3e56a954d
commit fbd2c491b2
41 changed files with 4293 additions and 76 deletions

104
utils/api_auth.py Normal file
View File

@@ -0,0 +1,104 @@
from functools import wraps
from flask import request, jsonify
from models import db, UserApiKey, ApiKeyDailyStat
from datetime import datetime, date
def get_api_key_from_request():
"""从请求中获取 API Key"""
# 优先从 Header 获取
api_key = request.headers.get('X-API-Key')
if api_key:
return api_key
# 其次从 Authorization Bearer 获取
auth_header = request.headers.get('Authorization')
if auth_header and auth_header.startswith('Bearer '):
return auth_header[7:]
# 最后从查询参数获取
return request.args.get('api_key')
def validate_api_key(api_key):
"""
验证 API Key
返回: (is_valid, key_obj_or_error_msg)
"""
if not api_key:
return False, 'API Key 不能为空'
key_obj = UserApiKey.query.filter_by(api_key=api_key).first()
if not key_obj:
return False, 'API Key 无效'
if not key_obj.is_active:
return False, 'API Key 已被禁用'
if not key_obj.user.is_active:
return False, '用户账号已被禁用'
# 检查每日限额
today_stat = ApiKeyDailyStat.query.filter_by(
api_key_id=key_obj.id,
date=date.today()
).first()
if today_stat and today_stat.call_count >= key_obj.daily_limit:
return False, f'已达到每日调用限额({key_obj.daily_limit}次)'
return True, key_obj
def record_api_call(key_obj, ip_address, success=True):
"""记录 API 调用"""
# 更新 Key 统计
key_obj.total_calls += 1
key_obj.last_used_at = datetime.utcnow()
key_obj.last_used_ip = ip_address
# 更新每日统计
today_stat = ApiKeyDailyStat.query.filter_by(
api_key_id=key_obj.id,
date=date.today()
).first()
if not today_stat:
today_stat = ApiKeyDailyStat(
api_key_id=key_obj.id,
date=date.today()
)
db.session.add(today_stat)
today_stat.call_count += 1
if success:
today_stat.success_count += 1
else:
today_stat.fail_count += 1
db.session.commit()
def api_key_required(f):
"""API Key 鉴权装饰器"""
@wraps(f)
def decorated_function(*args, **kwargs):
api_key = get_api_key_from_request()
is_valid, result = validate_api_key(api_key)
if not is_valid:
return jsonify({
'success': False,
'error': {
'code': 'UNAUTHORIZED',
'message': result
}
}), 401
# 将 key 对象传递给视图函数
request.api_key_obj = result
return f(*args, **kwargs)
return decorated_function