| """ |
| 管理后台 API 接口 |
| 用于 htmx 调用的 HTML 片段返回 |
| """ |
| from fastapi import APIRouter, Request |
| from fastapi.templating import Jinja2Templates |
| from fastapi.responses import HTMLResponse, JSONResponse, Response |
| from datetime import datetime |
| from app.utils.logger import logger |
| import os |
|
|
| router = APIRouter(prefix="/admin/api", tags=["admin-api"]) |
| templates = Jinja2Templates(directory="app/templates") |
|
|
|
|
| |
|
|
| @router.post("/login") |
| async def login(request: Request): |
| """管理后台登录""" |
| from app.admin.auth import create_session |
|
|
| try: |
| data = await request.json() |
| password = data.get("password", "") |
|
|
| |
| session_token = create_session(password) |
|
|
| if session_token: |
| |
| response = JSONResponse({ |
| "success": True, |
| "message": "登录成功" |
| }) |
| response.set_cookie( |
| key="admin_session", |
| value=session_token, |
| httponly=True, |
| max_age=86400, |
| samesite="lax" |
| ) |
| logger.info("✅ 管理后台登录成功") |
| return response |
| else: |
| |
| logger.warning("❌ 管理后台登录失败:密码错误") |
| return JSONResponse({ |
| "success": False, |
| "message": "密码错误" |
| }, status_code=401) |
|
|
| except Exception as e: |
| logger.error(f"❌ 登录异常: {e}") |
| return JSONResponse({ |
| "success": False, |
| "message": "登录失败" |
| }, status_code=500) |
|
|
|
|
| @router.post("/logout") |
| async def logout(request: Request): |
| """管理后台登出""" |
| from app.admin.auth import delete_session, get_session_token_from_request |
|
|
| session_token = get_session_token_from_request(request) |
| delete_session(session_token) |
|
|
| |
| response = JSONResponse({ |
| "success": True, |
| "message": "已登出" |
| }) |
| response.delete_cookie("admin_session") |
| logger.info("✅ 管理后台已登出") |
| return response |
|
|
|
|
| async def reload_settings(): |
| """热重载配置(重新加载环境变量并更新 settings 对象)""" |
| from app.core.config import settings |
| from app.utils.logger import setup_logger |
| from dotenv import load_dotenv |
|
|
| |
| load_dotenv(override=True) |
|
|
| |
| new_settings = type(settings)() |
|
|
| |
| for field_name in new_settings.model_fields.keys(): |
| setattr(settings, field_name, getattr(new_settings, field_name)) |
|
|
| |
| setup_logger(log_dir="logs", debug_mode=settings.DEBUG_LOGGING) |
|
|
| logger.info(f"🔄 配置已热重载 (DEBUG_LOGGING={settings.DEBUG_LOGGING})") |
|
|
|
|
| @router.get("/token-pool", response_class=HTMLResponse) |
| async def get_token_pool_status(request: Request): |
| """获取 Token 池状态(HTML 片段)""" |
| from app.utils.token_pool import get_token_pool |
|
|
| token_pool = get_token_pool() |
|
|
| if not token_pool: |
| |
| context = { |
| "request": request, |
| "tokens": [], |
| } |
| return templates.TemplateResponse("components/token_pool.html", context) |
|
|
| |
| pool_status = token_pool.get_pool_status() |
| tokens_info = [] |
|
|
| for idx, token_info in enumerate(pool_status.get("tokens", []), 1): |
| is_available = token_info.get("is_available", False) |
| is_healthy = token_info.get("is_healthy", False) |
|
|
| |
| if is_healthy: |
| status = "健康" |
| status_color = "bg-green-100 text-green-800" |
| elif is_available: |
| status = "可用" |
| status_color = "bg-yellow-100 text-yellow-800" |
| else: |
| status = "失败" |
| status_color = "bg-red-100 text-red-800" |
|
|
| |
| last_success = token_info.get("last_success_time", 0) |
| if last_success > 0: |
| from datetime import datetime |
| last_used = datetime.fromtimestamp(last_success).strftime("%Y-%m-%d %H:%M:%S") |
| else: |
| last_used = "从未使用" |
|
|
| tokens_info.append({ |
| "index": idx, |
| "key": token_info.get("token", "")[:20] + "...", |
| "status": status, |
| "status_color": status_color, |
| "last_used": last_used, |
| "failure_count": token_info.get("failure_count", 0), |
| "success_rate": token_info.get("success_rate", "0%"), |
| "token_type": token_info.get("token_type", "unknown"), |
| }) |
|
|
| context = { |
| "request": request, |
| "tokens": tokens_info, |
| } |
|
|
| return templates.TemplateResponse("components/token_pool.html", context) |
|
|
|
|
| @router.get("/recent-logs", response_class=HTMLResponse) |
| async def get_recent_logs(request: Request): |
| """获取最近的请求日志(HTML 片段)""" |
| |
| logs = [ |
| { |
| "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"), |
| "endpoint": "/hf/v1/chat/completions", |
| "model": "gpt-4o", |
| "status": 200, |
| "duration": "1.23s", |
| "provider": "zai", |
| } |
| ] |
|
|
| context = { |
| "request": request, |
| "logs": logs, |
| } |
|
|
| return templates.TemplateResponse("components/recent_logs.html", context) |
|
|
|
|
| @router.post("/config/save") |
| async def save_config(request: Request): |
| """保存配置到 .env 文件并热重载""" |
| try: |
| form_data = await request.form() |
|
|
| |
| env_lines = [ |
| "# Z.AI2API 配置文件", |
| "", |
| "# ========== 服务器配置 ==========", |
| f"SERVICE_NAME={form_data.get('service_name', 'Z.AI2API')}", |
| f"LISTEN_PORT={form_data.get('listen_port', '7860')}", |
| f"DEBUG_LOGGING={'true' if 'debug_logging' in form_data else 'false'}", |
| "", |
| "# ========== 认证配置 ==========", |
| f"AUTH_TOKEN={form_data.get('auth_token', 'sk-your-api-key')}", |
| f"SKIP_AUTH_TOKEN={'true' if 'skip_auth_token' in form_data else 'false'}", |
| f"ANONYMOUS_MODE={'true' if 'anonymous_mode' in form_data else 'false'}", |
| "", |
| "# ========== 功能配置 ==========", |
| f"TOOL_SUPPORT={'true' if 'tool_support' in form_data else 'false'}", |
| f"SCAN_LIMIT={form_data.get('scan_limit', '200000')}", |
| "", |
| "# ========== Token 池配置 ==========", |
| f"TOKEN_FAILURE_THRESHOLD={form_data.get('token_failure_threshold', '3')}", |
| f"TOKEN_RECOVERY_TIMEOUT={form_data.get('token_recovery_timeout', '1800')}", |
| "", |
| "# ========== 提供商配置 ==========", |
| f"DEFAULT_PROVIDER={form_data.get('default_provider', 'zai')}", |
| ] |
|
|
| |
| longcat_token = form_data.get('longcat_token', '').strip() |
| if longcat_token: |
| env_lines.append(f"LONGCAT_TOKEN={longcat_token}") |
|
|
| |
| with open(".env", "w", encoding="utf-8") as f: |
| f.write("\n".join(env_lines)) |
|
|
| logger.info("✅ 配置文件已保存") |
|
|
| |
| await reload_settings() |
|
|
| return HTMLResponse(""" |
| <div class="bg-green-100 border border-green-400 text-green-700 px-4 py-3 rounded relative" role="alert"> |
| <strong class="font-bold">成功!</strong> |
| <span class="block sm:inline">配置已保存并重载成功</span> |
| </div> |
| """) |
|
|
| except Exception as e: |
| logger.error(f"❌ 配置保存失败: {str(e)}") |
| return HTMLResponse(f""" |
| <div class="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded relative" role="alert"> |
| <strong class="font-bold">错误!</strong> |
| <span class="block sm:inline">保存失败: {str(e)}</span> |
| </div> |
| """) |
|
|
|
|
| @router.get("/env-preview") |
| async def get_env_preview(): |
| """获取 .env 文件预览""" |
| try: |
| with open(".env", "r", encoding="utf-8") as f: |
| content = f.read() |
| return HTMLResponse(f"<pre>{content}</pre>") |
| except FileNotFoundError: |
| return HTMLResponse("<pre># .env 文件不存在</pre>") |
| except Exception as e: |
| return HTMLResponse(f"<pre># 读取失败: {str(e)}</pre>") |
|
|
|
|
| @router.get("/provider-status", response_class=HTMLResponse) |
| async def get_provider_status(request: Request): |
| """获取提供商状态详情(HTML 片段)""" |
| from app.services.token_dao import get_token_dao |
|
|
| dao = get_token_dao() |
|
|
| |
| providers = ["zai", "k2think", "longcat"] |
| provider_stats_list = [] |
|
|
| for provider in providers: |
| stats = await dao.get_provider_stats(provider) |
| tokens = await dao.get_tokens_by_provider(provider, enabled_only=False) |
|
|
| |
| total_requests = stats.get("total_requests", 0) or 0 |
| successful_requests = stats.get("successful_requests", 0) or 0 |
| failed_requests = stats.get("failed_requests", 0) or 0 |
|
|
| if total_requests > 0: |
| success_rate = f"{(successful_requests / total_requests * 100):.1f}%" |
| else: |
| success_rate = "N/A" |
|
|
| |
| user_tokens = sum(1 for t in tokens if t.get("token_type") == "user") |
| guest_tokens = sum(1 for t in tokens if t.get("token_type") == "guest") |
| unknown_tokens = sum(1 for t in tokens if t.get("token_type") == "unknown") |
|
|
| provider_stats_list.append({ |
| "name": provider, |
| "name_upper": provider.upper(), |
| "display_name": { |
| "zai": "Z.AI", |
| "k2think": "K2Think", |
| "longcat": "LongCat" |
| }.get(provider, provider.upper()), |
| "total_tokens": stats.get("total_tokens", 0) or 0, |
| "enabled_tokens": stats.get("enabled_tokens", 0) or 0, |
| "user_tokens": user_tokens, |
| "guest_tokens": guest_tokens, |
| "unknown_tokens": unknown_tokens, |
| "total_requests": total_requests, |
| "successful_requests": successful_requests, |
| "failed_requests": failed_requests, |
| "success_rate": success_rate, |
| }) |
|
|
| context = { |
| "request": request, |
| "providers": provider_stats_list, |
| } |
|
|
| return templates.TemplateResponse("components/provider_status.html", context) |
|
|
|
|
| @router.get("/live-logs", response_class=HTMLResponse) |
| async def get_live_logs(): |
| """获取实时日志(最新 50 行)""" |
| import os |
| from datetime import datetime |
|
|
| logs = [] |
|
|
| |
| log_dir = "logs" |
| if os.path.exists(log_dir): |
| log_files = sorted([f for f in os.listdir(log_dir) if f.endswith('.log')], reverse=True) |
| if log_files: |
| log_file = os.path.join(log_dir, log_files[0]) |
| try: |
| with open(log_file, 'r', encoding='utf-8') as f: |
| |
| lines = f.readlines()[-50:] |
| logs = lines |
| except Exception as e: |
| logs = [f"# [{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] 读取日志失败: {str(e)}"] |
|
|
| if not logs: |
| logs = [f"# [{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] 暂无日志数据"] |
|
|
| html = "" |
| for log in logs: |
| log_line = log.strip() |
| if not log_line: |
| continue |
|
|
| |
| if "ERROR" in log_line or "CRITICAL" in log_line: |
| color_class = "text-red-400 font-semibold" |
| icon = "❌" |
| elif "WARNING" in log_line or "WARN" in log_line: |
| color_class = "text-yellow-400" |
| icon = "⚠️" |
| elif "SUCCESS" in log_line or "✅" in log_line: |
| color_class = "text-green-400" |
| icon = "✅" |
| elif "INFO" in log_line: |
| color_class = "text-blue-400" |
| icon = "ℹ️" |
| elif "DEBUG" in log_line: |
| color_class = "text-gray-400 text-xs" |
| icon = "🔍" |
| else: |
| color_class = "text-gray-300" |
| icon = "•" |
|
|
| |
| log_escaped = log_line.replace('<', '<').replace('>', '>') |
|
|
| html += f'<div class="{color_class} py-0.5 hover:bg-gray-800 px-2 rounded transition-colors">{icon} {log_escaped}</div>' |
|
|
| return HTMLResponse(html) |
|
|
|
|
| |
|
|
| @router.get("/tokens/list", response_class=HTMLResponse) |
| async def get_tokens_list(request: Request, provider: str = "zai"): |
| """获取 Token 列表(HTML 片段)""" |
| from app.services.token_dao import get_token_dao |
|
|
| dao = get_token_dao() |
| tokens = await dao.get_tokens_by_provider(provider, enabled_only=False) |
|
|
| context = { |
| "request": request, |
| "tokens": tokens, |
| "provider": provider |
| } |
|
|
| return templates.TemplateResponse("components/token_list.html", context) |
|
|
|
|
| @router.post("/tokens/add") |
| async def add_tokens(request: Request): |
| """添加 Token""" |
| from app.services.token_dao import get_token_dao |
| from app.utils.token_pool import get_token_pool |
|
|
| form_data = await request.form() |
| provider = form_data.get("provider", "zai") |
| single_token = form_data.get("single_token", "").strip() |
| bulk_tokens = form_data.get("bulk_tokens", "").strip() |
|
|
| dao = get_token_dao() |
| added_count = 0 |
| failed_count = 0 |
|
|
| |
| if single_token: |
| token_id = await dao.add_token(provider, single_token, validate=True) |
| if token_id: |
| added_count += 1 |
| else: |
| failed_count += 1 |
|
|
| |
| if bulk_tokens: |
| |
| tokens = [] |
| for line in bulk_tokens.split('\n'): |
| line = line.strip() |
| if ',' in line: |
| tokens.extend([t.strip() for t in line.split(',') if t.strip()]) |
| elif line: |
| tokens.append(line) |
|
|
| success, failed = await dao.bulk_add_tokens(provider, tokens, validate=True) |
| added_count += success |
| failed_count += failed |
|
|
| |
| if added_count > 0: |
| pool = get_token_pool() |
| if pool: |
| await pool.sync_from_database(provider) |
| logger.info(f"✅ Token 池已同步,新增 {added_count} 个 Token ({provider})") |
|
|
| |
| if added_count > 0 and failed_count == 0: |
| return HTMLResponse(f""" |
| <div class="bg-green-100 border border-green-400 text-green-700 px-4 py-3 rounded relative" role="alert"> |
| <strong class="font-bold">成功!</strong> |
| <span class="block sm:inline">已添加 {added_count} 个有效 Token</span> |
| </div> |
| """) |
| elif added_count > 0 and failed_count > 0: |
| return HTMLResponse(f""" |
| <div class="bg-yellow-100 border border-yellow-400 text-yellow-700 px-4 py-3 rounded relative" role="alert"> |
| <strong class="font-bold">部分成功!</strong> |
| <span class="block sm:inline">已添加 {added_count} 个 Token,{failed_count} 个失败(可能是重复、无效或匿名 Token)</span> |
| </div> |
| """) |
| else: |
| return HTMLResponse(""" |
| <div class="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded relative" role="alert"> |
| <strong class="font-bold">失败!</strong> |
| <span class="block sm:inline">所有 Token 添加失败(可能是重复、无效或匿名 Token)</span> |
| </div> |
| """) |
|
|
|
|
| @router.post("/tokens/toggle/{token_id}") |
| async def toggle_token(token_id: int, enabled: bool): |
| """切换 Token 启用状态""" |
| from app.services.token_dao import get_token_dao |
| from app.utils.token_pool import get_token_pool |
|
|
| dao = get_token_dao() |
| await dao.update_token_status(token_id, enabled) |
|
|
| |
| pool = get_token_pool() |
| if pool: |
| |
| async with dao.get_connection() as conn: |
| cursor = await conn.execute("SELECT provider FROM tokens WHERE id = ?", (token_id,)) |
| row = await cursor.fetchone() |
| if row: |
| provider = row[0] |
| await pool.sync_from_database(provider) |
| logger.info(f"✅ Token 池已同步 ({provider})") |
|
|
| |
| if enabled: |
| button_class = "bg-green-100 text-green-800 hover:bg-green-200" |
| indicator_class = "bg-green-500" |
| label = "已启用" |
| next_state = "false" |
| else: |
| button_class = "bg-red-100 text-red-800 hover:bg-red-200" |
| indicator_class = "bg-red-500" |
| label = "已禁用" |
| next_state = "true" |
|
|
| return HTMLResponse(f""" |
| <button hx-post="/admin/api/tokens/toggle/{token_id}?enabled={next_state}" |
| hx-swap="outerHTML" |
| class="inline-flex items-center px-2.5 py-0.5 text-xs font-semibold rounded-full transition-colors {button_class}"> |
| <span class="h-2 w-2 rounded-full mr-1.5 {indicator_class}"></span> |
| {label} |
| </button> |
| """) |
|
|
|
|
| @router.delete("/tokens/delete/{token_id}") |
| async def delete_token(token_id: int): |
| """删除 Token""" |
| from app.services.token_dao import get_token_dao |
| from app.utils.token_pool import get_token_pool |
|
|
| dao = get_token_dao() |
|
|
| |
| async with dao.get_connection() as conn: |
| cursor = await conn.execute("SELECT provider FROM tokens WHERE id = ?", (token_id,)) |
| row = await cursor.fetchone() |
| provider = row[0] if row else "zai" |
|
|
| await dao.delete_token(token_id) |
|
|
| |
| pool = get_token_pool() |
| if pool: |
| await pool.sync_from_database(provider) |
| logger.info(f"✅ Token 池已同步 ({provider})") |
|
|
| return HTMLResponse("") |
|
|
|
|
| @router.get("/tokens/stats", response_class=HTMLResponse) |
| async def get_tokens_stats(request: Request, provider: str = "zai"): |
| """获取 Token 统计信息(HTML 片段)""" |
| from app.services.token_dao import get_token_dao |
|
|
| dao = get_token_dao() |
|
|
| |
| stats = await dao.get_provider_stats(provider) |
|
|
| |
| tokens = await dao.get_tokens_by_provider(provider, enabled_only=False) |
|
|
| user_tokens = sum(1 for t in tokens if t.get("token_type") == "user") |
| guest_tokens = sum(1 for t in tokens if t.get("token_type") == "guest") |
| unknown_tokens = sum(1 for t in tokens if t.get("token_type") == "unknown") |
|
|
| stats_data = { |
| "total_tokens": stats.get("total_tokens", 0) or 0, |
| "enabled_tokens": stats.get("enabled_tokens", 0) or 0, |
| "user_tokens": user_tokens, |
| "guest_tokens": guest_tokens, |
| "unknown_tokens": unknown_tokens, |
| "total_requests": stats.get("total_requests", 0) or 0, |
| "successful_requests": stats.get("successful_requests", 0) or 0, |
| "failed_requests": stats.get("failed_requests", 0) or 0, |
| } |
|
|
| context = { |
| "request": request, |
| "stats": stats_data, |
| "provider": provider |
| } |
|
|
| return templates.TemplateResponse("components/token_stats.html", context) |
|
|
|
|
| @router.post("/tokens/validate") |
| async def validate_tokens(request: Request): |
| """批量验证 Token""" |
| from app.services.token_dao import get_token_dao |
|
|
| form_data = await request.form() |
| provider = form_data.get("provider", "zai") |
|
|
| dao = get_token_dao() |
|
|
| |
| stats = await dao.validate_all_tokens(provider) |
|
|
| valid_count = stats.get("valid", 0) |
| guest_count = stats.get("guest", 0) |
| invalid_count = stats.get("invalid", 0) |
|
|
| |
| if guest_count > 0: |
| message_class = "bg-yellow-100 border-yellow-400 text-yellow-700" |
| message = f"验证完成:有效 {valid_count} 个,匿名 {guest_count} 个,无效 {invalid_count} 个。匿名 Token 已标记。" |
| elif invalid_count > 0: |
| message_class = "bg-blue-100 border-blue-400 text-blue-700" |
| message = f"验证完成:有效 {valid_count} 个,无效 {invalid_count} 个。" |
| else: |
| message_class = "bg-green-100 border-green-400 text-green-700" |
| message = f"验证完成:所有 {valid_count} 个 Token 均有效!" |
|
|
| return HTMLResponse(f""" |
| <div class="{message_class} border px-4 py-3 rounded relative" role="alert"> |
| <strong class="font-bold">批量验证完成!</strong> |
| <span class="block sm:inline">{message}</span> |
| </div> |
| """) |
|
|
|
|
| @router.post("/tokens/validate-single/{token_id}") |
| async def validate_single_token(request: Request, token_id: int): |
| """验证单个 Token 并返回更新后的行""" |
| from app.services.token_dao import get_token_dao |
|
|
| dao = get_token_dao() |
|
|
| |
| is_valid = await dao.validate_and_update_token(token_id) |
|
|
| |
| async with dao.get_connection() as conn: |
| cursor = await conn.execute(""" |
| SELECT t.*, ts.total_requests, ts.successful_requests, ts.failed_requests, |
| ts.last_success_time, ts.last_failure_time |
| FROM tokens t |
| LEFT JOIN token_stats ts ON t.id = ts.token_id |
| WHERE t.id = ? |
| """, (token_id,)) |
| row = await cursor.fetchone() |
|
|
| if row: |
| |
| token = dict(row) |
| context = { |
| "request": request, |
| "token": token, |
| } |
| |
| return templates.TemplateResponse("components/token_row.html", context) |
| else: |
| return HTMLResponse("") |
|
|
|
|
| @router.post("/tokens/health-check") |
| async def health_check_tokens(request: Request): |
| """执行 Token 池健康检查""" |
| from app.utils.token_pool import get_token_pool |
|
|
| form_data = await request.form() |
| provider = form_data.get("provider", "zai") |
|
|
| pool = get_token_pool() |
|
|
| if not pool: |
| return HTMLResponse(""" |
| <div class="bg-yellow-100 border border-yellow-400 text-yellow-700 px-4 py-3 rounded relative" role="alert"> |
| <strong class="font-bold">提示!</strong> |
| <span class="block sm:inline">Token 池未初始化,请重启服务。</span> |
| </div> |
| """) |
|
|
| |
| await pool.health_check_all() |
|
|
| |
| status = pool.get_pool_status() |
| healthy_count = status.get("healthy_tokens", 0) |
| total_count = status.get("total_tokens", 0) |
|
|
| if healthy_count == total_count: |
| message_class = "bg-green-100 border-green-400 text-green-700" |
| message = f"所有 {total_count} 个 Token 均健康!" |
| elif healthy_count > 0: |
| message_class = "bg-blue-100 border-blue-400 text-blue-700" |
| message = f"健康检查完成:{healthy_count}/{total_count} 个 Token 健康。" |
| else: |
| message_class = "bg-red-100 border-red-400 text-red-700" |
| message = f"警告:0/{total_count} 个 Token 健康,请检查配置。" |
|
|
| return HTMLResponse(f""" |
| <div class="{message_class} border px-4 py-3 rounded relative" role="alert"> |
| <strong class="font-bold">健康检查完成!</strong> |
| <span class="block sm:inline">{message}</span> |
| </div> |
| """) |
|
|
|
|
| @router.post("/tokens/sync-pool") |
| async def sync_token_pool(request: Request): |
| """手动同步 Token 池(从数据库重新加载)""" |
| from app.utils.token_pool import get_token_pool |
|
|
| form_data = await request.form() |
| provider = form_data.get("provider", "zai") |
|
|
| pool = get_token_pool() |
|
|
| if not pool: |
| return HTMLResponse(""" |
| <div class="bg-yellow-100 border border-yellow-400 text-yellow-700 px-4 py-3 rounded relative" role="alert"> |
| <strong class="font-bold">提示!</strong> |
| <span class="block sm:inline">Token 池未初始化,请重启服务。</span> |
| </div> |
| """) |
|
|
| |
| await pool.sync_from_database(provider) |
|
|
| |
| status = pool.get_pool_status() |
| total_count = status.get("total_tokens", 0) |
| available_count = status.get("available_tokens", 0) |
| user_count = status.get("user_tokens", 0) |
|
|
| logger.info(f"✅ Token 池手动同步完成: {provider}, 总计 {total_count} 个 Token, 可用 {available_count} 个, 认证用户 {user_count} 个") |
|
|
| if total_count == 0: |
| message_class = "bg-yellow-100 border-yellow-400 text-yellow-700" |
| message = f"同步完成:当前没有可用的 {provider.upper()} Token,请在数据库中启用 Token。" |
| elif available_count == 0: |
| message_class = "bg-orange-100 border-orange-400 text-orange-700" |
| message = f"同步完成:共 {total_count} 个 Token,但无可用 Token(可能都已禁用)。" |
| else: |
| message_class = "bg-green-100 border-green-400 text-green-700" |
| message = f"同步完成:共 {total_count} 个 Token,{available_count} 个可用,{user_count} 个认证用户。" |
|
|
| return HTMLResponse(f""" |
| <div class="{message_class} border px-4 py-3 rounded relative" role="alert"> |
| <strong class="font-bold">Token 池同步完成!</strong> |
| <span class="block sm:inline">{message}</span> |
| </div> |
| """) |
|
|