KiroProxy User
chore: repo cleanup and maintenance
0edbd7b
from fastapi import APIRouter, Request, HTTPException, Depends, Response
from fastapi.responses import JSONResponse
from ..core import get_history_config, get_rate_limiter, update_history_config
from ..core.auth_middleware import require_admin_auth, optional_admin_auth
from ..core.admin_auth import get_admin_auth, authenticate_admin, create_admin_session, revoke_admin_session, is_auth_required
from ..handlers import admin as admin_handler
from ..resources import get_resource_path
router = APIRouter(prefix="/api")
# ==================== 认证相关端点 ====================
@router.post("/auth/login")
async def api_admin_login(request: Request, response: Response):
"""管理员登录"""
try:
data = await request.json()
password = data.get("password")
if not password:
raise HTTPException(status_code=400, detail="密码不能为空")
if not authenticate_admin(password):
raise HTTPException(status_code=401, detail="密码错误")
# 创建会话
session_id = create_admin_session()
# 设置Cookie(可选,主要用Bearer Token)
response.set_cookie(
key="admin_session",
value=session_id,
max_age=24 * 60 * 60, # 24小时
httponly=True,
secure=False, # 在生产环境中应该设为True
samesite="lax"
)
return {
"success": True,
"session_id": session_id,
"message": "登录成功"
}
except HTTPException:
raise
except Exception as e:
raise HTTPException(status_code=500, detail=f"登录失败: {str(e)}")
@router.post("/auth/logout")
async def api_admin_logout(
response: Response,
session_id: str = Depends(require_admin_auth)
):
"""管理员登出"""
try:
# 撤销会话
revoke_admin_session(session_id)
# 清除Cookie
response.delete_cookie(key="admin_session")
return {
"success": True,
"message": "登出成功"
}
except Exception as e:
raise HTTPException(status_code=500, detail=f"登出失败: {str(e)}")
@router.get("/auth/session")
async def api_admin_session_info(session_id: str = Depends(require_admin_auth)):
"""获取当前会话信息"""
try:
auth = get_admin_auth()
session_info = auth.get_session_info(session_id)
if not session_info:
raise HTTPException(status_code=401, detail="会话无效")
return {
"success": True,
"session": session_info
}
except HTTPException:
raise
except Exception as e:
raise HTTPException(status_code=500, detail=f"获取会话信息失败: {str(e)}")
@router.get("/auth/sessions")
async def api_admin_sessions(session_id: str = Depends(require_admin_auth)):
"""获取所有活跃会话"""
try:
auth = get_admin_auth()
sessions = auth.get_active_sessions()
return {
"success": True,
"sessions": sessions
}
except Exception as e:
raise HTTPException(status_code=500, detail=f"获取会话列表失败: {str(e)}")
@router.post("/auth/check")
async def api_admin_auth_check(session_id: str = Depends(optional_admin_auth)):
"""检查认证状态"""
auth_required = is_auth_required()
return {
"auth_required": auth_required,
"authenticated": not auth_required or session_id is not None,
"session_id": session_id
}
# ==================== 公开API(无需认证) ====================
@router.get("/status")
async def api_status():
return await admin_handler.get_status()
@router.post("/event_logging/batch")
async def api_event_logging_batch(request: Request):
return await admin_handler.event_logging_batch(request)
@router.get("/stats")
async def api_stats():
return await admin_handler.get_stats()
@router.get("/logs")
async def api_logs(limit: int = 100):
return await admin_handler.get_logs(limit)
# ==================== 需要认证的管理员API ====================
@router.get("/accounts/export")
async def api_export_accounts(session_id: str = Depends(require_admin_auth)):
return await admin_handler.export_accounts()
@router.post("/accounts/import")
async def api_import_accounts(request: Request, session_id: str = Depends(require_admin_auth)):
return await admin_handler.import_accounts(request)
@router.post("/accounts/manual")
async def api_add_manual_token(request: Request, session_id: str = Depends(require_admin_auth)):
return await admin_handler.add_manual_token(request)
@router.post("/accounts/batch")
async def api_batch_import_accounts(request: Request, session_id: str = Depends(require_admin_auth)):
return await admin_handler.batch_import_accounts(request)
@router.post("/accounts/refresh-all")
async def api_refresh_all(session_id: str = Depends(require_admin_auth)):
return await admin_handler.refresh_all_tokens()
@router.get("/accounts/status")
async def api_accounts_status_enhanced(session_id: str = Depends(require_admin_auth)):
return await admin_handler.get_accounts_status_enhanced()
@router.get("/accounts/summary")
async def api_accounts_summary(session_id: str = Depends(require_admin_auth)):
return await admin_handler.get_accounts_summary()
@router.post("/accounts/refresh-all-quotas")
async def api_refresh_all_quotas(session_id: str = Depends(require_admin_auth)):
return await admin_handler.refresh_all_quotas()
@router.get("/refresh/progress")
async def api_refresh_progress(session_id: str = Depends(require_admin_auth)):
return await admin_handler.get_refresh_progress()
@router.post("/refresh/all")
async def api_refresh_all_with_progress(session_id: str = Depends(require_admin_auth)):
return await admin_handler.refresh_all_with_progress()
@router.get("/refresh/config")
async def api_get_refresh_config(session_id: str = Depends(require_admin_auth)):
return await admin_handler.get_refresh_config()
@router.put("/refresh/config")
async def api_update_refresh_config(request: Request, session_id: str = Depends(require_admin_auth)):
return await admin_handler.update_refresh_config(request)
@router.get("/refresh/status")
async def api_refresh_status(session_id: str = Depends(require_admin_auth)):
return await admin_handler.get_refresh_manager_status()
@router.get("/accounts")
async def api_accounts(session_id: str = Depends(require_admin_auth)):
return await admin_handler.get_accounts()
@router.post("/accounts")
async def api_add_account(request: Request, session_id: str = Depends(require_admin_auth)):
return await admin_handler.add_account(request)
@router.delete("/accounts/{account_id}")
async def api_delete_account(account_id: str, session_id: str = Depends(require_admin_auth)):
return await admin_handler.delete_account(account_id)
@router.put("/accounts/{account_id}")
async def api_update_account(account_id: str, request: Request, session_id: str = Depends(require_admin_auth)):
return await admin_handler.update_account(account_id, request)
@router.post("/accounts/{account_id}/toggle")
async def api_toggle_account(account_id: str, session_id: str = Depends(require_admin_auth)):
return await admin_handler.toggle_account(account_id)
@router.post("/speedtest")
async def api_speedtest(session_id: str = Depends(require_admin_auth)):
return await admin_handler.speedtest()
@router.get("/accounts/{account_id}/test")
async def api_test_account_token(account_id: str, session_id: str = Depends(require_admin_auth)):
return await admin_handler.test_account_token(account_id)
@router.get("/token/scan")
async def api_scan_tokens(session_id: str = Depends(require_admin_auth)):
return await admin_handler.scan_tokens()
@router.post("/token/add-from-scan")
async def api_add_from_scan(request: Request, session_id: str = Depends(require_admin_auth)):
return await admin_handler.add_from_scan(request)
@router.get("/config/export")
async def api_export_config(session_id: str = Depends(require_admin_auth)):
return await admin_handler.export_config()
@router.post("/config/import")
async def api_import_config(request: Request, session_id: str = Depends(require_admin_auth)):
return await admin_handler.import_config(request)
@router.post("/token/refresh-check")
async def api_refresh_check(session_id: str = Depends(require_admin_auth)):
return await admin_handler.refresh_token_check()
@router.post("/accounts/{account_id}/refresh")
async def api_refresh_account(account_id: str, session_id: str = Depends(require_admin_auth)):
return await admin_handler.refresh_account_token_with_manager(account_id)
@router.post("/accounts/{account_id}/restore")
async def api_restore_account(account_id: str, session_id: str = Depends(require_admin_auth)):
return await admin_handler.restore_account(account_id)
@router.get("/accounts/{account_id}/usage")
async def api_account_usage(account_id: str, session_id: str = Depends(require_admin_auth)):
return await admin_handler.get_account_usage_info(account_id)
@router.get("/accounts/{account_id}")
async def api_account_detail(account_id: str, session_id: str = Depends(require_admin_auth)):
return await admin_handler.get_account_detail(account_id)
@router.post("/accounts/{account_id}/refresh-quota")
async def api_refresh_account_quota(account_id: str, session_id: str = Depends(require_admin_auth)):
return await admin_handler.refresh_account_quota_with_token(account_id)
@router.get("/priority")
async def api_get_priority_accounts(session_id: str = Depends(require_admin_auth)):
return await admin_handler.get_priority_accounts()
@router.post("/priority/{account_id}")
async def api_set_priority_account(account_id: str, request: Request, session_id: str = Depends(require_admin_auth)):
return await admin_handler.set_priority_account(account_id, request)
@router.delete("/priority/{account_id}")
async def api_remove_priority_account(account_id: str, session_id: str = Depends(require_admin_auth)):
return await admin_handler.remove_priority_account(account_id)
@router.put("/priority/reorder")
async def api_reorder_priority_accounts(request: Request, session_id: str = Depends(require_admin_auth)):
return await admin_handler.reorder_priority_accounts(request)
@router.get("/quota")
async def api_quota_status(session_id: str = Depends(require_admin_auth)):
return await admin_handler.get_quota_status()
@router.get("/kiro/login-url")
async def api_login_url(session_id: str = Depends(require_admin_auth)):
return await admin_handler.get_kiro_login_url()
@router.get("/stats/detailed")
async def api_detailed_stats(session_id: str = Depends(require_admin_auth)):
return await admin_handler.get_detailed_stats()
@router.post("/health-check")
async def api_health_check(session_id: str = Depends(require_admin_auth)):
return await admin_handler.run_health_check()
@router.get("/browsers")
async def api_browsers(session_id: str = Depends(require_admin_auth)):
return await admin_handler.get_browsers()
@router.post("/kiro/login/start")
async def api_kiro_login_start(request: Request, session_id: str = Depends(require_admin_auth)):
return await admin_handler.start_kiro_login(request)
@router.get("/kiro/login/poll")
async def api_kiro_login_poll(session_id: str = Depends(require_admin_auth)):
return await admin_handler.poll_kiro_login()
@router.post("/kiro/login/cancel")
async def api_kiro_login_cancel(session_id: str = Depends(require_admin_auth)):
return await admin_handler.cancel_kiro_login()
@router.get("/kiro/login/status")
async def api_kiro_login_status(session_id: str = Depends(require_admin_auth)):
return await admin_handler.get_kiro_login_status()
@router.post("/kiro/social/start")
async def api_social_login_start(request: Request, session_id: str = Depends(require_admin_auth)):
return await admin_handler.start_social_login(request)
@router.post("/kiro/social/exchange")
async def api_social_token_exchange(request: Request, session_id: str = Depends(require_admin_auth)):
return await admin_handler.exchange_social_token(request)
@router.post("/kiro/social/cancel")
async def api_social_login_cancel(session_id: str = Depends(require_admin_auth)):
return await admin_handler.cancel_social_login()
@router.get("/kiro/social/status")
async def api_social_login_status(session_id: str = Depends(require_admin_auth)):
return await admin_handler.get_social_login_status()
@router.post("/protocol/register")
async def api_register_protocol(session_id: str = Depends(require_admin_auth)):
return await admin_handler.register_kiro_protocol()
@router.post("/protocol/unregister")
async def api_unregister_protocol(session_id: str = Depends(require_admin_auth)):
return await admin_handler.unregister_kiro_protocol()
@router.get("/protocol/status")
async def api_protocol_status(session_id: str = Depends(require_admin_auth)):
return await admin_handler.get_protocol_status()
@router.get("/protocol/callback")
async def api_protocol_callback(session_id: str = Depends(require_admin_auth)):
return await admin_handler.get_callback_result()
@router.get("/flows")
async def api_flows(
protocol: str = None,
model: str = None,
account_id: str = None,
state: str = None,
has_error: bool = None,
bookmarked: bool = None,
search: str = None,
limit: int = 50,
offset: int = 0,
session_id: str = Depends(require_admin_auth),
):
return await admin_handler.get_flows(
protocol=protocol,
model=model,
account_id=account_id,
state_filter=state,
has_error=has_error,
bookmarked=bookmarked,
search=search,
limit=limit,
offset=offset,
)
@router.get("/flows/stats")
async def api_flow_stats(session_id: str = Depends(require_admin_auth)):
return await admin_handler.get_flow_stats()
@router.get("/flows/{flow_id}")
async def api_flow_detail(flow_id: str, session_id: str = Depends(require_admin_auth)):
return await admin_handler.get_flow_detail(flow_id)
@router.post("/flows/{flow_id}/bookmark")
async def api_bookmark_flow(flow_id: str, request: Request, session_id: str = Depends(require_admin_auth)):
return await admin_handler.bookmark_flow(flow_id, request)
@router.post("/flows/{flow_id}/note")
async def api_add_flow_note(flow_id: str, request: Request, session_id: str = Depends(require_admin_auth)):
return await admin_handler.add_flow_note(flow_id, request)
@router.post("/flows/{flow_id}/tag")
async def api_add_flow_tag(flow_id: str, request: Request, session_id: str = Depends(require_admin_auth)):
return await admin_handler.add_flow_tag(flow_id, request)
@router.post("/flows/export")
async def api_export_flows(request: Request, session_id: str = Depends(require_admin_auth)):
return await admin_handler.export_flows(request)
@router.get("/settings/history")
async def api_get_history_config(session_id: str = Depends(require_admin_auth)):
config = get_history_config()
return config.to_dict()
@router.post("/settings/history")
async def api_update_history_config(request: Request, session_id: str = Depends(require_admin_auth)):
data = await request.json()
update_history_config(data)
return {"ok": True, "config": get_history_config().to_dict()}
@router.get("/settings/rate-limit")
async def api_get_rate_limit_config(session_id: str = Depends(require_admin_auth)):
limiter = get_rate_limiter()
return {
"enabled": limiter.config.enabled,
"min_request_interval": limiter.config.min_request_interval,
"max_requests_per_minute": limiter.config.max_requests_per_minute,
"global_max_requests_per_minute": limiter.config.global_max_requests_per_minute,
"stats": limiter.get_stats(),
}
@router.post("/settings/rate-limit")
async def api_update_rate_limit_config(request: Request, session_id: str = Depends(require_admin_auth)):
data = await request.json()
limiter = get_rate_limiter()
limiter.update_config(**data)
return {
"ok": True,
"config": {
"enabled": limiter.config.enabled,
"min_request_interval": limiter.config.min_request_interval,
"max_requests_per_minute": limiter.config.max_requests_per_minute,
"global_max_requests_per_minute": limiter.config.global_max_requests_per_minute,
},
}
DOC_TITLES = {
"01-quickstart": "快速开始",
"02-features": "功能特性",
"03-faq": "常见问题",
"04-api": "API 参考",
"05-server-deploy": "服务器部署",
}
@router.get("/docs")
async def api_docs_list():
docs_dir = get_resource_path("kiro_proxy/docs")
docs = []
if docs_dir.exists():
for doc_file in sorted(docs_dir.glob("*.md")):
doc_id = doc_file.stem
title = DOC_TITLES.get(doc_id, doc_id)
docs.append({"id": doc_id, "title": title})
return {"docs": docs}
@router.get("/docs/{doc_id}")
async def api_docs_content(doc_id: str):
docs_dir = get_resource_path("kiro_proxy/docs")
doc_file = docs_dir / f"{doc_id}.md"
if not doc_file.exists():
raise HTTPException(status_code=404, detail="文档不存在")
content = doc_file.read_text(encoding="utf-8")
title = DOC_TITLES.get(doc_id, doc_id)
return {"id": doc_id, "title": title, "content": content}
# ==================== 数据同步相关端点 ====================
@router.get("/sync/status")
async def api_sync_status(session_id: str = Depends(require_admin_auth)):
"""获取同步状态"""
from ..core.database import get_sync_manager
sync_manager = await get_sync_manager()
if not sync_manager:
return {
"enabled": False,
"message": "未配置 DATABASE_URL,同步未启用"
}
return await sync_manager.get_sync_status()
@router.post("/sync/force")
async def api_force_sync(request: Request, session_id: str = Depends(require_admin_auth)):
"""强制同步"""
from ..core.database import get_sync_manager
sync_manager = await get_sync_manager()
if not sync_manager:
raise HTTPException(status_code=400, detail="同步未启用")
data = await request.json()
source = data.get("source", "merge")
if source not in ["local", "remote", "merge"]:
raise HTTPException(status_code=400, detail="无效的同步源,可选: local, remote, merge")
success = await sync_manager.force_sync(source)
if success:
return {"ok": True, "message": f"同步完成 ({source})"}
else:
raise HTTPException(status_code=500, detail="同步失败")