Spaces:
Sleeping
Sleeping
update
Browse files- .clinerules +1 -0
- api_key_sb.py +9 -8
- app.py +8 -0
- memory-bank/activeContext.md +33 -0
- memory-bank/productContext.md +18 -0
- memory-bank/progress.md +22 -0
- memory-bank/projectBrief.md +14 -0
- memory-bank/systemPatterns.md +23 -0
- memory-bank/techContext.md +28 -0
- proxy.py +13 -15
.clinerules
CHANGED
|
@@ -6,6 +6,7 @@ CUSTOM_INSTRUCTIONS = """
|
|
| 6 |
if [ -z "$CONDA_DEFAULT_ENV" ] || [ "$CONDA_DEFAULT_ENV" != "airs" ]; then
|
| 7 |
conda activate airs
|
| 8 |
fi
|
|
|
|
| 9 |
"""
|
| 10 |
|
| 11 |
|
|
|
|
| 6 |
if [ -z "$CONDA_DEFAULT_ENV" ] || [ "$CONDA_DEFAULT_ENV" != "airs" ]; then
|
| 7 |
conda activate airs
|
| 8 |
fi
|
| 9 |
+
# 使用中文进行交流
|
| 10 |
"""
|
| 11 |
|
| 12 |
|
api_key_sb.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
| 1 |
from supabase import create_client, Client
|
| 2 |
import os
|
|
|
|
| 3 |
from fastapi import FastAPI, Request, HTTPException, Depends, status
|
| 4 |
from dotenv import load_dotenv
|
| 5 |
from datetime import timezone, timedelta, datetime
|
|
@@ -10,6 +11,9 @@ load_dotenv()
|
|
| 10 |
SUPABASE_URL = os.getenv("SUPABASE_URL")
|
| 11 |
SUPABASE_KEY = os.getenv("SUPABASE_KEY")
|
| 12 |
|
|
|
|
|
|
|
|
|
|
| 13 |
def get_supabase_client() -> Client:
|
| 14 |
"""Initializes and returns a Supabase client instance."""
|
| 15 |
return create_client(SUPABASE_URL, SUPABASE_KEY)
|
|
@@ -27,14 +31,11 @@ async def get_api_key_info(model: str = None):
|
|
| 27 |
|
| 28 |
if response.data:
|
| 29 |
api_key_info = response.data[0]
|
| 30 |
-
api_key_id = api_key_info.get('api_key_id')
|
| 31 |
-
if api_key_id:
|
| 32 |
-
await update_api_key_ran_at(api_key_id)
|
| 33 |
return api_key_info
|
| 34 |
else:
|
| 35 |
return None
|
| 36 |
except Exception as e:
|
| 37 |
-
|
| 38 |
return None
|
| 39 |
|
| 40 |
async def update_api_key_ran_at(api_key_id: str, ran_at_time: datetime = None):
|
|
@@ -51,15 +52,15 @@ async def update_api_key_ran_at(api_key_id: str, ran_at_time: datetime = None):
|
|
| 51 |
else:
|
| 52 |
current_local_time = ran_at_time.isoformat()
|
| 53 |
|
| 54 |
-
|
| 55 |
response = supabase.table("airs_api_keys").update({"ran_at": current_local_time}).eq("id", api_key_id).execute()
|
| 56 |
-
|
| 57 |
|
| 58 |
# 验证更新是否成功
|
| 59 |
verification_response = supabase.from_('airs_api_keys').select('ran_at').eq('id', api_key_id).single().execute()
|
| 60 |
verified_ran_at = verification_response.data.get('ran_at')
|
| 61 |
-
|
| 62 |
|
| 63 |
except Exception as e:
|
| 64 |
-
|
| 65 |
raise HTTPException(status_code=500, detail=f"更新API密钥运行时间失败!错误信息:{str(e)}")
|
|
|
|
| 1 |
from supabase import create_client, Client
|
| 2 |
import os
|
| 3 |
+
import logging # 导入logging模块
|
| 4 |
from fastapi import FastAPI, Request, HTTPException, Depends, status
|
| 5 |
from dotenv import load_dotenv
|
| 6 |
from datetime import timezone, timedelta, datetime
|
|
|
|
| 11 |
SUPABASE_URL = os.getenv("SUPABASE_URL")
|
| 12 |
SUPABASE_KEY = os.getenv("SUPABASE_KEY")
|
| 13 |
|
| 14 |
+
# 配置日志
|
| 15 |
+
logger = logging.getLogger(__name__)
|
| 16 |
+
|
| 17 |
def get_supabase_client() -> Client:
|
| 18 |
"""Initializes and returns a Supabase client instance."""
|
| 19 |
return create_client(SUPABASE_URL, SUPABASE_KEY)
|
|
|
|
| 31 |
|
| 32 |
if response.data:
|
| 33 |
api_key_info = response.data[0]
|
|
|
|
|
|
|
|
|
|
| 34 |
return api_key_info
|
| 35 |
else:
|
| 36 |
return None
|
| 37 |
except Exception as e:
|
| 38 |
+
logger.error(f"从Supabase获取API密钥失败: {e}")
|
| 39 |
return None
|
| 40 |
|
| 41 |
async def update_api_key_ran_at(api_key_id: str, ran_at_time: datetime = None):
|
|
|
|
| 52 |
else:
|
| 53 |
current_local_time = ran_at_time.isoformat()
|
| 54 |
|
| 55 |
+
logger.info(f"尝试更新 API 密钥 {api_key_id} 的 ran_at 为 {current_local_time}")
|
| 56 |
response = supabase.table("airs_api_keys").update({"ran_at": current_local_time}).eq("id", api_key_id).execute()
|
| 57 |
+
logger.info(f'API 密钥 {api_key_id} 的 ran_at 更新成功为 {current_local_time}. ')
|
| 58 |
|
| 59 |
# 验证更新是否成功
|
| 60 |
verification_response = supabase.from_('airs_api_keys').select('ran_at').eq('id', api_key_id).single().execute()
|
| 61 |
verified_ran_at = verification_response.data.get('ran_at')
|
| 62 |
+
logger.info(f"验证:API 密钥 {api_key_id} 的实际 ran_at 值为 {verified_ran_at}")
|
| 63 |
|
| 64 |
except Exception as e:
|
| 65 |
+
logger.error(f"更新API密钥运行时间失败!错误信息:{str(e)}")
|
| 66 |
raise HTTPException(status_code=500, detail=f"更新API密钥运行时间失败!错误信息:{str(e)}")
|
app.py
CHANGED
|
@@ -5,11 +5,16 @@ from fastapi.responses import Response # 导入Response
|
|
| 5 |
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials # 导入HTTPBearer和HTTPAuthorizationCredentials
|
| 6 |
import httpx # 使用httpx替代requests,因为requests是同步的,而FastAPI是异步的
|
| 7 |
import os, json
|
|
|
|
| 8 |
from dotenv import load_dotenv
|
| 9 |
from enum import Enum
|
| 10 |
from proxy import do_proxy
|
| 11 |
# from api_key_sb import get_api_key
|
| 12 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 13 |
# 加载环境变量
|
| 14 |
load_dotenv()
|
| 15 |
|
|
@@ -61,5 +66,8 @@ async def proxy_gemini(request: Request, protocol: ProtocolType, host:str, path:
|
|
| 61 |
# 将User-Agent改为curl/8.7.1,以模拟curl请求
|
| 62 |
client_headers["User-Agent"] = "curl/8.7.1"
|
| 63 |
|
|
|
|
|
|
|
|
|
|
| 64 |
# 调用 do_proxy 并返回其结果,此处传的 headers 是不带 api_key 的数据
|
| 65 |
return await do_proxy(real_url, request.method, client_headers, client_body)
|
|
|
|
| 5 |
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials # 导入HTTPBearer和HTTPAuthorizationCredentials
|
| 6 |
import httpx # 使用httpx替代requests,因为requests是同步的,而FastAPI是异步的
|
| 7 |
import os, json
|
| 8 |
+
import logging # 导入logging模块
|
| 9 |
from dotenv import load_dotenv
|
| 10 |
from enum import Enum
|
| 11 |
from proxy import do_proxy
|
| 12 |
# from api_key_sb import get_api_key
|
| 13 |
|
| 14 |
+
# 配置日志
|
| 15 |
+
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
|
| 16 |
+
logger = logging.getLogger(__name__)
|
| 17 |
+
|
| 18 |
# 加载环境变量
|
| 19 |
load_dotenv()
|
| 20 |
|
|
|
|
| 66 |
# 将User-Agent改为curl/8.7.1,以模拟curl请求
|
| 67 |
client_headers["User-Agent"] = "curl/8.7.1"
|
| 68 |
|
| 69 |
+
logger.info(f"Proxying request to: {real_url}")
|
| 70 |
+
logger.debug(f"Request method: {request.method}, Headers: {client_headers}")
|
| 71 |
+
|
| 72 |
# 调用 do_proxy 并返回其结果,此处传的 headers 是不带 api_key 的数据
|
| 73 |
return await do_proxy(real_url, request.method, client_headers, client_body)
|
memory-bank/activeContext.md
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Active Context
|
| 2 |
+
|
| 3 |
+
## Current work focus
|
| 4 |
+
The current focus is on refining the existing codebase and improving its robustness and maintainability.
|
| 5 |
+
|
| 6 |
+
## Recent changes
|
| 7 |
+
- Created the `memory-bank` directory and all core documentation files.
|
| 8 |
+
- Reviewed `app.py`, `proxy.py`, `api_key_sb.py`, `requirements.txt`, and `.env`.
|
| 9 |
+
- Removed the unnecessary call to `update_api_key_ran_at` from `api_key_sb.py`'s `get_api_key_info` function.
|
| 10 |
+
- Removed the redundant `finally` block in `proxy.py` for `httpx.AsyncClient()`.
|
| 11 |
+
|
| 12 |
+
## Next steps
|
| 13 |
+
- All `print` statements have been replaced with Python's `logging` module.
|
| 14 |
+
- Consider a unified and structured error response format.
|
| 15 |
+
|
| 16 |
+
## Active decisions and considerations
|
| 17 |
+
- Ensure the Memory Bank accurately reflects the current state and future direction of the project.
|
| 18 |
+
- Prioritize understanding the existing codebase before proposing new changes.
|
| 19 |
+
|
| 20 |
+
## Learnings and project insights
|
| 21 |
+
- The project is a FastAPI application acting as a reverse proxy.
|
| 22 |
+
- It uses `httpx` for asynchronous HTTP requests.
|
| 23 |
+
- API key management is handled via Supabase, with `api_key_sb.py` providing functions to fetch and update API key information.
|
| 24 |
+
- The proxy implements retry logic with exponential backoff for `429 Too Many Requests` errors and updates the `ran_at` timestamp for API keys.
|
| 25 |
+
- Environment variables (`PROXY_API_KEY`, `SUPABASE_URL`, `SUPABASE_KEY`) are loaded from `.env` using `python-dotenv`.
|
| 26 |
+
- Dependencies are managed via `requirements.txt`.
|
| 27 |
+
|
| 28 |
+
## Important patterns and preferences
|
| 29 |
+
- Adherence to the specified Memory Bank structure and content guidelines.
|
| 30 |
+
|
| 31 |
+
## Learnings and project insights
|
| 32 |
+
- The project appears to be a Python-based FastAPI application, likely serving as a reverse proxy.
|
| 33 |
+
- There's an existing `conda` environment setup.
|
memory-bank/productContext.md
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Product Context
|
| 2 |
+
|
| 3 |
+
## Why this project exists
|
| 4 |
+
The "superproxy-1" project aims to address the need for a centralized and controlled access point to various backend APIs. This helps in managing API keys, enforcing rate limits, and providing a consistent interface for client applications.
|
| 5 |
+
|
| 6 |
+
## Problems it solves
|
| 7 |
+
- **API Key Management:** Centralizes API key storage and usage, reducing the risk of exposure in client-side code.
|
| 8 |
+
- **Rate Limiting:** Allows for global or per-client rate limiting to prevent abuse and ensure fair usage of backend services.
|
| 9 |
+
- **Security:** Adds an additional layer of security by obscuring direct access to backend services.
|
| 10 |
+
- **Simplified Integration:** Provides a single endpoint for multiple APIs, simplifying client-side integration.
|
| 11 |
+
|
| 12 |
+
## How it should work
|
| 13 |
+
The proxy should receive incoming requests, validate them (e.g., check for valid API keys, apply rate limits), forward them to the appropriate backend API, and return the response to the client.
|
| 14 |
+
|
| 15 |
+
## User experience goals
|
| 16 |
+
- **Reliability:** Ensure high availability and minimal latency for API requests.
|
| 17 |
+
- **Ease of Use:** Provide clear documentation and a straightforward integration process for developers.
|
| 18 |
+
- **Security:** Maintain a secure environment for API interactions.
|
memory-bank/progress.md
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Progress
|
| 2 |
+
|
| 3 |
+
## What works
|
| 4 |
+
- The foundational Memory Bank structure has been established with core documentation files.
|
| 5 |
+
- Removed redundant `finally` block in `proxy.py` for `httpx.AsyncClient()`.
|
| 6 |
+
|
| 7 |
+
## What's left to build
|
| 8 |
+
- All `print` statements have been replaced with Python's `logging` module.
|
| 9 |
+
- Consider a unified and structured error response format.
|
| 10 |
+
- Deployment strategy (Docker setup).
|
| 11 |
+
|
| 12 |
+
## Current status
|
| 13 |
+
- Initial setup of project documentation is complete.
|
| 14 |
+
- The project's core purpose and technical stack are outlined.
|
| 15 |
+
- Redundant `finally` block in `proxy.py` has been successfully removed.
|
| 16 |
+
|
| 17 |
+
## Known issues
|
| 18 |
+
- No specific issues identified yet, as core development has not begun.
|
| 19 |
+
|
| 20 |
+
## Evolution of project decisions
|
| 21 |
+
- The decision to use FastAPI and `httpx` for asynchronous proxying is based on performance and ease of development.
|
| 22 |
+
- The Memory Bank structure is adopted to ensure consistent and comprehensive project documentation.
|
memory-bank/projectBrief.md
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Project Brief
|
| 2 |
+
|
| 3 |
+
This project, "superproxy-1", is a proxy service designed to handle API requests. The primary goal is to provide a flexible and robust proxy layer for various applications.
|
| 4 |
+
|
| 5 |
+
## Core Requirements
|
| 6 |
+
- Act as an intermediary for API requests.
|
| 7 |
+
- Potentially handle authentication and authorization.
|
| 8 |
+
- Provide logging and monitoring capabilities.
|
| 9 |
+
- Be easily deployable and scalable.
|
| 10 |
+
|
| 11 |
+
## Goals
|
| 12 |
+
- Enhance security by abstracting direct API access.
|
| 13 |
+
- Improve performance through caching or load balancing (future consideration).
|
| 14 |
+
- Simplify API integration for client applications.
|
memory-bank/systemPatterns.md
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# System Patterns
|
| 2 |
+
|
| 3 |
+
## System Architecture
|
| 4 |
+
The "superproxy-1" is designed as a lightweight, API-gateway-like service. It will primarily function as a reverse proxy, forwarding requests to various upstream services based on defined rules.
|
| 5 |
+
|
| 6 |
+
## Key Technical Decisions
|
| 7 |
+
- **Asynchronous Processing:** Utilize an asynchronous framework (e.g., FastAPI with Uvicorn) to handle concurrent requests efficiently.
|
| 8 |
+
- **Configuration-driven Routing:** Implement a flexible routing mechanism that can be configured to direct requests to different backend services.
|
| 9 |
+
- **Modular Design:** Keep the core proxy logic separate from authentication, logging, and other cross-cutting concerns to promote maintainability and extensibility.
|
| 10 |
+
|
| 11 |
+
## Design Patterns in Use
|
| 12 |
+
- **Proxy Pattern:** The core of the service, acting as a surrogate for other objects (backend APIs).
|
| 13 |
+
- **Middleware Pattern:** For handling concerns like authentication, logging, and rate limiting before or after the main request processing.
|
| 14 |
+
|
| 15 |
+
## Component Relationships
|
| 16 |
+
- **Client Applications:** Interact with the proxy service.
|
| 17 |
+
- **Proxy Service:** Receives requests, processes them, and forwards them to Upstream Services.
|
| 18 |
+
- **Upstream Services (Backend APIs):** The actual APIs that the proxy communicates with.
|
| 19 |
+
|
| 20 |
+
## Critical Implementation Paths
|
| 21 |
+
- **Request Routing:** Efficiently determine the correct upstream service for an incoming request.
|
| 22 |
+
- **Error Handling:** Gracefully handle errors from upstream services and communicate them effectively to clients.
|
| 23 |
+
- **Security Measures:** Implement robust authentication and authorization mechanisms.
|
memory-bank/techContext.md
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Tech Context
|
| 2 |
+
|
| 3 |
+
## Technologies Used
|
| 4 |
+
- **Python:** Primary programming language.
|
| 5 |
+
- **FastAPI:** Web framework for building the API.
|
| 6 |
+
- **Uvicorn:** ASGI server for running FastAPI applications.
|
| 7 |
+
- **`httpx`:** Asynchronous HTTP client for forwarding requests to upstream services.
|
| 8 |
+
- **`python-dotenv`:** For managing environment variables.
|
| 9 |
+
|
| 10 |
+
## Development Setup
|
| 11 |
+
- **Conda Environment:** The project uses a `conda` environment named `airs` for dependency management.
|
| 12 |
+
- **`requirements.txt`:** Specifies Python dependencies.
|
| 13 |
+
- **Docker:** For containerization and deployment.
|
| 14 |
+
|
| 15 |
+
## Technical Constraints
|
| 16 |
+
- **Performance:** The proxy should be able to handle a high volume of concurrent requests with low latency.
|
| 17 |
+
- **Security:** All sensitive information (e.g., API keys) must be handled securely, preferably through environment variables.
|
| 18 |
+
- **Scalability:** The architecture should support horizontal scaling to accommodate increased load.
|
| 19 |
+
|
| 20 |
+
## Dependencies
|
| 21 |
+
- `fastapi`
|
| 22 |
+
- `uvicorn`
|
| 23 |
+
- `httpx`
|
| 24 |
+
- `python-dotenv`
|
| 25 |
+
|
| 26 |
+
## Tool Usage Patterns
|
| 27 |
+
- **`uvicorn app:app --host 0.0.0.0 --port 7860 --reload`:** Command for local development server.
|
| 28 |
+
- **`conda activate airs`:** To activate the development environment.
|
proxy.py
CHANGED
|
@@ -1,20 +1,21 @@
|
|
| 1 |
import httpx
|
| 2 |
import asyncio
|
| 3 |
from starlette.responses import Response
|
| 4 |
-
from api_key_sb import get_api_key_info,
|
| 5 |
-
from supabase import Client
|
| 6 |
from dotenv import load_dotenv
|
| 7 |
import os
|
| 8 |
from datetime import timezone, timedelta, datetime
|
| 9 |
from fastapi import FastAPI, Request, HTTPException, Depends, status
|
|
|
|
| 10 |
|
| 11 |
load_dotenv()
|
| 12 |
|
| 13 |
-
#
|
| 14 |
-
|
| 15 |
|
| 16 |
async def do_proxy(url: str, method: str, headers: dict, content: str, max_retries: int = 3):
|
| 17 |
-
|
| 18 |
client = None
|
| 19 |
try:
|
| 20 |
async with httpx.AsyncClient() as client:
|
|
@@ -25,7 +26,7 @@ async def do_proxy(url: str, method: str, headers: dict, content: str, max_retri
|
|
| 25 |
api_key = api_key_info.get('api_key')
|
| 26 |
api_key_id = api_key_info.get('api_key_id')
|
| 27 |
api_key_show = api_key[:5]+'*****'+api_key[-5:]
|
| 28 |
-
|
| 29 |
headers["Authorization"] = f"Bearer {api_key}"
|
| 30 |
response = await client.request(
|
| 31 |
method=method,
|
|
@@ -39,7 +40,7 @@ async def do_proxy(url: str, method: str, headers: dict, content: str, max_retri
|
|
| 39 |
if response.status_code == 429:
|
| 40 |
retry_after = response.headers.get("Retry-After", "5") # 默认5秒
|
| 41 |
wait_time = int(retry_after) + attempt * 2 # 指数退避基础值
|
| 42 |
-
|
| 43 |
await asyncio.sleep(wait_time)
|
| 44 |
|
| 45 |
try:
|
|
@@ -49,10 +50,11 @@ async def do_proxy(url: str, method: str, headers: dict, content: str, max_retri
|
|
| 49 |
future_time = now_beijing + timedelta(days=1)
|
| 50 |
# future_timestamp = future_time.timestamp()
|
| 51 |
future_timestamp = future_time.isoformat()
|
| 52 |
-
|
| 53 |
await update_api_key_ran_at(api_key_id, future_time)
|
| 54 |
-
|
| 55 |
except Exception as e:
|
|
|
|
| 56 |
raise HTTPException(status_code=500, detail=f"更新API密钥运行时间失败!错误信息:{str(e)}")
|
| 57 |
continue
|
| 58 |
|
|
@@ -64,13 +66,9 @@ async def do_proxy(url: str, method: str, headers: dict, content: str, max_retri
|
|
| 64 |
)
|
| 65 |
|
| 66 |
except httpx.HTTPStatusError as e:
|
| 67 |
-
|
| 68 |
return Response(content=str(e), status_code=e.response.status_code)
|
| 69 |
|
| 70 |
except Exception as e:
|
| 71 |
-
|
| 72 |
return Response(content="Internal Server Error", status_code=500)
|
| 73 |
-
|
| 74 |
-
finally:
|
| 75 |
-
if client:
|
| 76 |
-
await client.aclose() # 确保连接关闭
|
|
|
|
| 1 |
import httpx
|
| 2 |
import asyncio
|
| 3 |
from starlette.responses import Response
|
| 4 |
+
from api_key_sb import get_api_key_info, update_api_key_ran_at, supabase as sb_client # 移除 get_supabase_client
|
| 5 |
+
# from supabase import Client
|
| 6 |
from dotenv import load_dotenv
|
| 7 |
import os
|
| 8 |
from datetime import timezone, timedelta, datetime
|
| 9 |
from fastapi import FastAPI, Request, HTTPException, Depends, status
|
| 10 |
+
import logging # 导入logging模块
|
| 11 |
|
| 12 |
load_dotenv()
|
| 13 |
|
| 14 |
+
# 配置日志
|
| 15 |
+
logger = logging.getLogger(__name__)
|
| 16 |
|
| 17 |
async def do_proxy(url: str, method: str, headers: dict, content: str, max_retries: int = 3):
|
| 18 |
+
logger.info(f"Proxy service started for URL: {url}")
|
| 19 |
client = None
|
| 20 |
try:
|
| 21 |
async with httpx.AsyncClient() as client:
|
|
|
|
| 26 |
api_key = api_key_info.get('api_key')
|
| 27 |
api_key_id = api_key_info.get('api_key_id')
|
| 28 |
api_key_show = api_key[:5]+'*****'+api_key[-5:]
|
| 29 |
+
logger.info(f"使用API密钥:{api_key_show}")
|
| 30 |
headers["Authorization"] = f"Bearer {api_key}"
|
| 31 |
response = await client.request(
|
| 32 |
method=method,
|
|
|
|
| 40 |
if response.status_code == 429:
|
| 41 |
retry_after = response.headers.get("Retry-After", "5") # 默认5秒
|
| 42 |
wait_time = int(retry_after) + attempt * 2 # 指数退避基础值
|
| 43 |
+
logger.warning(f"⚠️ 429错误!{wait_time}秒后重试 (尝试:{attempt+1})")
|
| 44 |
await asyncio.sleep(wait_time)
|
| 45 |
|
| 46 |
try:
|
|
|
|
| 50 |
future_time = now_beijing + timedelta(days=1)
|
| 51 |
# future_timestamp = future_time.timestamp()
|
| 52 |
future_timestamp = future_time.isoformat()
|
| 53 |
+
logger.info(f'future_timestamp: {future_timestamp}')
|
| 54 |
await update_api_key_ran_at(api_key_id, future_time)
|
| 55 |
+
logger.info('API密钥运行时间更新成功')
|
| 56 |
except Exception as e:
|
| 57 |
+
logger.error(f"更新API密钥运行时间失败!错误信息:{str(e)}")
|
| 58 |
raise HTTPException(status_code=500, detail=f"更新API密钥运行时间失败!错误信息:{str(e)}")
|
| 59 |
continue
|
| 60 |
|
|
|
|
| 66 |
)
|
| 67 |
|
| 68 |
except httpx.HTTPStatusError as e:
|
| 69 |
+
logger.error(f"🚨 服务器错误 {e.response.status_code}: {e.request.url}")
|
| 70 |
return Response(content=str(e), status_code=e.response.status_code)
|
| 71 |
|
| 72 |
except Exception as e:
|
| 73 |
+
logger.error(f"🔥 致命错误: {type(e).__name__}: {str(e)}")
|
| 74 |
return Response(content="Internal Server Error", status_code=500)
|
|
|
|
|
|
|
|
|
|
|
|