diff --git a/chat2api.py b/chat2api.py index 848fcc6..8b584d5 100644 --- a/chat2api.py +++ b/chat2api.py @@ -14,7 +14,7 @@ from starlette.background import BackgroundTask from chatgpt.ChatService import ChatService from chatgpt.reverseProxy import chatgpt_reverse_proxy from utils.Logger import logger -from utils.authorization import token_list, refresh_all_tokens +from utils.authorization import token_list, error_token_list, refresh_all_tokens from utils.config import api_prefix, scheduled_refresh from utils.retry import async_retry @@ -88,7 +88,7 @@ async def send_conversation(request: Request, req_token: str = Depends(oauth2_sc @app.get(f"/{api_prefix}/tokens" if api_prefix else "/tokens", response_class=HTMLResponse) async def upload_html(request: Request): - tokens_count = len(token_list) + tokens_count = len(set(token_list) - set(error_token_list)) return templates.TemplateResponse("tokens.html", {"request": request, "api_prefix": api_prefix, "tokens_count": tokens_count}) @@ -102,20 +102,27 @@ async def upload_post(text: str = Form(...)): with open("data/token.txt", "a", encoding="utf-8") as f: f.write(line.strip() + "\n") logger.info(f"Token list count: {len(token_list)}") - tokens_count = len(token_list) + tokens_count = len(set(token_list) - set(error_token_list)) return {"status": "success", "tokens_count": tokens_count} @app.post(f"/{api_prefix}/tokens/clear" if api_prefix else "/tokens/clear") async def upload_post(): token_list.clear() + error_token_list.clear() with open("data/token.txt", "w", encoding="utf-8") as f: pass logger.info(f"Token list count: {len(token_list)}") - tokens_count = len(token_list) + tokens_count = len(set(token_list) - set(error_token_list)) return {"status": "success", "tokens_count": tokens_count} +@app.post(f"/{api_prefix}/tokens/error" if api_prefix else "/tokens/error") +async def error_tokens(): + error_tokens_list = list(set(error_token_list)) + return {"status": "success", "error_tokens": error_tokens_list} + + @app.api_route("/{path:path}", methods=["GET", "POST", "PUT", "DELETE", "OPTIONS", "HEAD", "PATCH", "TRACE"]) async def reverse_proxy(request: Request, path: str): return await chatgpt_reverse_proxy(request, path) diff --git a/chatgpt/refreshToken.py b/chatgpt/refreshToken.py index c7e048d..63eb4f2 100644 --- a/chatgpt/refreshToken.py +++ b/chatgpt/refreshToken.py @@ -33,11 +33,14 @@ async def rt2ac(refresh_token, force_refresh=False): logger.info(f"refresh_token -> access_token from cache") return access_token else: - access_token = await chat_refresh(refresh_token) - refresh_map[refresh_token] = {"token": access_token, "timestamp": int(time.time())} - save_refresh_map(refresh_map) - logger.info(f"refresh_token -> access_token with openai: {access_token}") - return access_token + try: + access_token = await chat_refresh(refresh_token) + refresh_map[refresh_token] = {"token": access_token, "timestamp": int(time.time())} + save_refresh_map(refresh_map) + logger.info(f"refresh_token -> access_token with openai: {access_token}") + return access_token + except HTTPException as e: + raise HTTPException(status_code=e.status_code, detail=e.detail) async def chat_refresh(refresh_token): @@ -56,7 +59,7 @@ async def chat_refresh(refresh_token): else: raise Exception(r.text[:100]) except Exception as e: - logger.error(f"Failed to refresh access_token: {str(e)}") + logger.error(f"Failed to refresh access_token `{refresh_token}`: {str(e)}") raise HTTPException(status_code=500, detail=f"Failed to refresh access_token.") finally: await client.close() diff --git a/docs/tokens.png b/docs/tokens.png index 5bc9d09..f50a424 100644 Binary files a/docs/tokens.png and b/docs/tokens.png differ diff --git a/templates/tokens.html b/templates/tokens.html index 3e04416..4514d45 100644 --- a/templates/tokens.html +++ b/templates/tokens.html @@ -10,35 +10,73 @@ const apiPrefix = "{{ api_prefix }}"; const uploadForm = document.getElementById('uploadForm'); const clearForm = document.getElementById('clearForm'); + const errorButton = document.getElementById('errorButton'); if (apiPrefix === "None") { uploadForm.action = "/tokens/upload"; clearForm.action = "/tokens/clear"; + errorButton.dataset.api = "/tokens/error"; } else { uploadForm.action = `/${apiPrefix}/tokens/upload`; clearForm.action = `/${apiPrefix}/tokens/clear`; + errorButton.dataset.api = `/${apiPrefix}/tokens/error`; } + + errorButton.addEventListener('click', async () => { + const response = await fetch(errorButton.dataset.api, { + method: 'POST', + }); + const result = await response.json(); + const errorTokens = result.error_tokens; + + const errorModal = document.getElementById('errorModal'); + const errorModalContent = document.getElementById('errorModalContent'); + + errorModalContent.innerHTML = errorTokens.map(token => `
${token}
`).join(''); + errorModal.classList.remove('hidden'); + }); + + document.getElementById('errorModalClose').addEventListener('click', () => { + document.getElementById('errorModal').classList.add('hidden'); + }); + + document.getElementById('errorModalCopy').addEventListener('click', () => { + const errorModalContent = document.getElementById('errorModalContent'); + const textToCopy = errorModalContent.innerText.replace(/\n\n/g, '\n'); + navigator.clipboard.writeText(textToCopy).then(() => { + alert('错误 Tokens 已复制到剪贴板'); + }).catch(err => { + alert('复制失败,请手动复制'); + }); + }); }); -当前可用 Tokens 数量:{{ tokens_count }}
- - -点击清空,将会清空所有已保存的 Tokens
-当前可用 Tokens 数量:{{ tokens_count }}
+ + +点击清空,将会清空上传和错误的 Tokens
+ +