Spaces:
Sleeping
Sleeping
File size: 5,612 Bytes
b1bcc51 75836b4 6a5b28d a095012 6a5b28d a095012 b4872ed b1bcc51 4ed7447 559f768 c8e2a82 b1bcc51 6a5b28d b1bcc51 6a5b28d 1fa736f 6a5b28d 559f768 6a5b28d 4ed7447 6a5b28d 1fa736f 6a5b28d b1bcc51 75836b4 b1bcc51 a095012 fa6601b 559f768 b4872ed 0341228 bfb2d8e b4872ed 559f768 b1bcc51 c8e2a82 b4872ed b1bcc51 b4872ed 559f768 b4872ed 559f768 b4872ed 559f768 3375e89 559f768 b4872ed 559f768 b4872ed c8e2a82 b4872ed |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 |
# uvicorn app:app --host 0.0.0.0 --port 7860 --reload
from fastapi import FastAPI, Request, HTTPException, Depends, status
from fastapi.responses import Response # 导入Response
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials # 导入HTTPBearer和HTTPAuthorizationCredentials
import httpx # 使用httpx替代requests,因为requests是同步的,而FastAPI是异步的
import os, json, pprint
from dotenv import load_dotenv
from enum import Enum
from supabase import create_client, Client
from datetime import datetime, timezone, timedelta, time # 导入 datetime 和 timezone
# 加载环境变量
load_dotenv()
# 从环境变量获取代理自身的API密钥
proxy_api_key = os.getenv("PROXY_API_KEY")
# Supabase 配置
SUPABASE_URL = os.getenv("SUPABASE_URL")
SUPABASE_KEY = os.getenv("SUPABASE_KEY")
# 初始化 Supabase 客户端
supabase: Client = create_client(SUPABASE_URL, SUPABASE_KEY)
# 定义HTTPBearer安全方案
security = HTTPBearer()
# 枚举校验协议类型
class ProtocolType(str, Enum):
HTTP = "http"
HTTPS = "https"
# 依赖函数:验证代理的API密钥
def verify_proxy_api_key(credentials: HTTPAuthorizationCredentials = Depends(security)):
if not proxy_api_key or credentials.credentials != proxy_api_key:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid or missing proxy API key",
headers={"WWW-Authenticate": "Bearer"},
)
return credentials.credentials
app = FastAPI(title="Gemini Proxy API")
@app.get("/")
def greet_json():
return {"Hello": "World!"}
# 3. 健康检查接口(可选,用于验证服务是否正常)
@app.get("/health")
async def health_check():
return {"status": "ok", "message": "Proxy service is running."}
@app.api_route("/v1/{protocol}/{host}/{path:path}", methods=["GET", "POST", "PUT", "DELETE"])
# @app.api_route("/v1/{protocol}/{host}/{path:path}", methods=["POST"])
async def proxy(request: Request, protocol: ProtocolType, host:str, path: str, proxy_api_key: str = Depends(verify_proxy_api_key)): # 添加代理认证依赖
send_req = await make_send_req(request, protocol, host, path)
print(send_req)
print(send_req.get('headers'))
# 使用httpx异步客户端发起请求
async with httpx.AsyncClient() as client:
response = await client.request(
method=send_req.get('method'),
url=send_req.get('url'),
headers=send_req.get('headers'),
content=send_req.get('content'), # httpx使用content参数传递请求体
timeout=30 # 超时时间,可调整
)
try:
# 创建北京时间时区对象(UTC+8)
beijing_tz = timezone(timedelta(hours=8))
now_beijing = datetime.now(beijing_tz) # 带时区的datetime对象
current_local_time = now_beijing.isoformat()
# current_local_time = int(now_beijing.timestamp()) # 正确的时间戳
# current_local_time = datetime.now().isoformat()
supabase.table("airs_api_keys").update({"ran_at": current_local_time}).eq("id", send_req.get('api_key_info').get('api_key_id')).execute()
print('更新成功')
except Exception as e:
raise HTTPException(status_code=500, detail="更新API密钥运行时间失败!")
return Response(
content=response.content,
status_code=response.status_code,
headers=dict(response.headers)
)
async def get_api_key_info(model: str = None):
try:
# 根据用户提供的SQL语句修改,从 'airs_model_api_keys_view' 视图中获取 'api_key'
if model:
response = supabase.from_('airs_model_api_keys_view').select('api_key', 'api_key_id').eq('model_name', model).order('api_key_ran_at', desc=False).limit(1).execute()
else:
# 如果没有提供model,则获取所有模型的api_key
raise HTTPException(status_code=400, detail="请提供模型名称!")
if response.data:
api_key_info = response.data[0]
return api_key_info
else:
return None
except Exception as e:
print(f"从Supabase获取API密钥失败: {e}")
return None
async def make_send_req(request: Request, protocol: ProtocolType, host:str, path: str):
_method = request.method
_url = f"{protocol.value}://{host}/{path}"
_headers = dict(request.headers)
# 移除FastAPI自带的Host头,避免Gemini校验报错
_headers.pop("host", None)
_headers.pop("authorization", None)
_headers.pop("user-agent", None)
_headers.pop("content-length", None)
# 将User-Agent改为curl/8.7.1,以模拟curl请求
_headers["User-Agent"] = "curl/8.7.1"
try:
# 读取客户端请求体
_content = await request.body()
content_json = json.loads(_content)
try:
_model = content_json.get("model")
except Exception as e:
print(f"获取模型名称失败: {e}")
return None
except Exception as e:
print(f"读取客户端请求体失败: {e}")
return None
_api_key_info = await get_api_key_info(_model)
if not _api_key_info:
raise HTTPException(status_code=400, detail="未找到匹配的API密钥!")
_headers["Authorization"] = f"Bearer {_api_key_info['api_key']}"
send_req={
"method": _method,
"url": _url,
"headers": _headers,
"content": _content,
"model": _model,
"api_key_info": _api_key_info
}
return send_req
|