coze_api_01 / app.py
CatPtain's picture
Update app.py
f379b5c verified
from flask import Flask, request, jsonify
from flask_cors import CORS
import jwt
import time
import uuid
import requests
import os
import base64
from functools import wraps
import logging
app = Flask(__name__)
CORS(app, origins=os.getenv('ALLOWED_ORIGINS', 'https://cybercity.top').split(','))
# 环境变量配置
CLIENT_ID = os.getenv('COZE_CLIENT_ID', '1243934778935')
KID = os.getenv('COZE_KID', 'tlrohMMZyKMrrpP3GtxF_3_cerDhVIMINs0LOW91m7w')
PRIVATE_KEY = os.getenv('COZE_PRIVATE_KEY').replace('\\n', '\n') # 从环境变量获取并格式化
CLIENT_SECRET = os.getenv('COZE_CLIENT_SECRET', 'your_client_secret')
# 日志配置
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
# JWT缓存机制(简易内存缓存)
jwt_cache = {'token': None, 'exp': 0}
def validate_basic_auth(auth_header):
"""实现RFC6749标准的Basic认证验证[10](@ref)"""
if not auth_header or not auth_header.startswith('Basic '):
return False
try:
credentials = base64.b64decode(auth_header[6:]).decode('utf-8')
client_id, client_secret = credentials.split(':', 1)
return client_id == CLIENT_ID and client_secret == CLIENT_SECRET
except Exception as e:
logger.error(f"Basic auth validation failed: {str(e)}")
return False
def generate_jwt():
"""生成符合RFC7519标准的JWT[1,3](@ref)"""
current_time = int(time.time())
payload = {
"iss": CLIENT_ID,
"sub": CLIENT_ID, # 必须包含sub字段[6](@ref)
"aud": "https://api.coze.cn", # 精确的URI格式
"iat": current_time,
"exp": current_time + 3600,
"jti": uuid.uuid4().hex,
"connector_id": CLIENT_ID, # 统一使用client_id
"user_id": CLIENT_ID
}
header = {
"alg": "RS256",
"typ": "JWT",
"kid": KID
}
try:
return jwt.encode(payload, PRIVATE_KEY, algorithm="RS256", headers=header)
except jwt.PyJWTError as e:
logger.error(f"JWT generation failed: {str(e)}")
raise
def get_access_token(jwt_token):
"""获取访问令牌(带重试机制)[3](@ref)"""
url = "https://api.coze.cn/api/permission/oauth2/token"
data = {
"grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer",
"duration_seconds": 86399
}
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {jwt_token}"
}
try:
response = requests.post(url, json=data, headers=headers, timeout=10)
response.raise_for_status()
return response.json()
except requests.exceptions.RequestException as e:
logger.error(f"Access token request failed: {str(e)}")
return {"error": "coze_api_error"}
# 健康检查端点
@app.route('/health', methods=['GET'])
def health_check():
return jsonify({"status": "healthy", "timestamp": int(time.time())}), 200
# 令牌获取端点
@app.route('/api/token', methods=['POST'])
def get_coze_token():
# Basic认证验证
if not validate_basic_auth(request.headers.get('Authorization')):
return jsonify({"error": "invalid_client"}), 401
# 检查缓存中的有效JWT
current_time = time.time()
if jwt_cache['exp'] > current_time + 300: # 有效期剩余超过5分钟时复用
cached_token = jwt_cache['token']
else:
try:
cached_token = generate_jwt()
jwt_cache.update({
'token': cached_token,
'exp': current_time + 3600
})
except Exception as e:
return jsonify({"error": "jwt_generation_failed"}), 500
# 获取访问令牌
token_response = get_access_token(cached_token)
if 'error' in token_response:
return jsonify({
"error": "coze_oauth_error",
"details": token_response.get('error_description')
}), 502
return jsonify({
"access_token": token_response['access_token'],
"expires_in": token_response['expires_in'],
"token_type": "Bearer"
})
# 错误处理
@app.errorhandler(404)
def not_found(error):
return jsonify({"error": "endpoint_not_found"}), 404
@app.errorhandler(500)
def internal_error(error):
return jsonify({"error": "internal_server_error"}), 500
if __name__ == '__main__':
port = int(os.getenv('PORT', 7860))
app.run(host='0.0.0.0', port=port, debug=os.getenv('DEBUG', 'false').lower() == 'true')