lightspeed commited on
Commit
aea8fce
·
verified ·
1 Parent(s): a8c0fef

Upload 8 files

Browse files
.gitattributes CHANGED
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ docs/币安.jpg filter=lfs diff=lfs merge=lfs -text
docs/DONATE.md ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 💰 支持项目发展
2
+
3
+ 感谢您使用 gcli2api!如果这个项目对您有帮助,欢迎通过以下方式支持项目的持续发展:
4
+
5
+ ## 💝 捐赠方式
6
+
7
+ ### 币安捐赠
8
+
9
+ ![币安捐赠二维码](币安.jpg)
10
+
11
+ *扫描上方二维码,使用币安进行捐赠*
docs/HUGGINGFACE_DEPLOY.md ADDED
@@ -0,0 +1,130 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Hugging Face Spaces 部署指南
2
+
3
+ 本文档介绍如何将 GCLI2API 部署到 Hugging Face Spaces。
4
+
5
+ ## 1. 部署方式选择
6
+
7
+ 推荐使用 **Gradio (Python) Space** 进行部署。
8
+
9
+ ## 2. 必要文件说明
10
+
11
+ 已添加以下适配文件:
12
+
13
+ | 文件 | 作用 |
14
+ | ---- | ---- |
15
+ | `app.py` | Hugging Face Space 启动入口,启动 FastAPI 服务并提供 Gradio 信息页 |
16
+ | `README_HF.md` | Hugging Face Space 展示 README,用于空间首页说明 |
17
+ | `requirements.txt` | 依赖列表,已加入 gradio 与 requests |
18
+ | `.env.example` | 环境变量示例文件 |
19
+ | `README.md` | 已添加 front matter 以支持 HF Metadata |
20
+
21
+ ## 3. 创建 Hugging Face Space
22
+
23
+ 1. 打开 https://huggingface.co/spaces
24
+ 2. 点击 "Create new Space"
25
+ 3. 选择技术栈:`Gradio`
26
+ 4. 填写名称,例如:`your-username/gcli2api`
27
+ 5. 选择可见性:`Public` 或 `Private`
28
+ 6. 创建后,将当前仓库代码推送到新建 Space 或在 Space 内手动上传文件
29
+
30
+ ## 4. 设置环境变量 (Settings → Variables)
31
+
32
+ 最少需要配置:
33
+
34
+ | 名称 | 说明 | 是否必需 |
35
+ | ---- | ---- | -------- |
36
+ | `API_PASSWORD` | API 请求认证密码 | 是(或用 PASSWORD) |
37
+ | `PANEL_PASSWORD` | 控制面板登录密码 | 是(或用 PASSWORD) |
38
+ | `PASSWORD` | 通用密码(设置后覆盖上面两个) | 否 |
39
+ | `AUTO_LOAD_ENV_CREDS` | 是否自动加载环境变量凭证 | 否 |
40
+
41
+ 凭证(任选其一方式):
42
+
43
+ 1. 在 Web 控制面板上传 credential JSON 文件
44
+ 2. 设置 `AUTO_LOAD_ENV_CREDS=true` 并添加:
45
+ ```bash
46
+ GCLI_CREDS_1={"client_id":"...","client_secret":"...","refresh_token":"...","token_uri":"https://oauth2.googleapis.com/token","project_id":"your-project"}
47
+ GCLI_CREDS_2={"client_id":"...","project_id":"..."}
48
+ ```
49
+
50
+ 可选优化参数:
51
+ ```bash
52
+ CALLS_PER_ROTATION=10
53
+ RETRY_429_ENABLED=true
54
+ RETRY_429_MAX_RETRIES=5
55
+ COMPATIBILITY_MODE=true
56
+ ANTI_TRUNCATION_MAX_ATTEMPTS=3
57
+ ```
58
+
59
+ ## 5. Space 启动后访问
60
+
61
+ | 功能 | URL 示例 |
62
+ | ---- | -------- |
63
+ | 控制面板 | `https://<space-id>.hf.space/auth` |
64
+ | OpenAI 模型列表 | `https://<space-id>.hf.space/v1/models` |
65
+ | 健康检测 | `https://<space-id>.hf.space/keepalive` |
66
+
67
+ ## 6. 示例请求
68
+
69
+ ```bash
70
+ curl -X POST "https://<space-id>.hf.space/v1/chat/completions" \
71
+ -H "Authorization: Bearer ${API_PASSWORD}" \
72
+ -H "Content-Type: application/json" \
73
+ -d '{
74
+ "model": "gemini-2.5-pro",
75
+ "messages": [
76
+ {"role": "user", "content": "你好"}
77
+ ]
78
+ }'
79
+ ```
80
+
81
+ ## 7. 常见问题与解决
82
+
83
+ | 问题 | 可能原因 | 解决方案 |
84
+ | ---- | -------- | -------- |
85
+ | 401 Unauthorized | 密码错误 | 检查 `API_PASSWORD` 或 `PASSWORD` |
86
+ | 503 Service Unavailable | 无有效凭证 | 上传或配置 GCLI_CREDS_* |
87
+ | 模型列表为空 | 未正确认证 | 确认凭证刷新成功 |
88
+ | 流式不输出 | 客户端不支持 SSE | 使用假流式模型 `假流式/` 前缀 |
89
+
90
+ ## 8. 模型命名速查
91
+
92
+ | 模式 | 示例 |
93
+ | ---- | ---- |
94
+ | 基础 | `gemini-2.5-pro` |
95
+ | 假流式 | `假流式/gemini-2.5-pro` |
96
+ | 抗截断流式 | `流式抗截断/gemini-2.5-pro` |
97
+ | 最大思考 | `gemini-2.5-pro-maxthinking` |
98
+ | 无思考 | `gemini-2.5-pro-nothinking` |
99
+ | 搜索增强 | `gemini-2.5-pro-search` |
100
+
101
+ 可组合:`流式抗截断/gemini-2.5-pro-maxthinking`
102
+
103
+ ## 9. 安全建议
104
+
105
+ - 使用随机长密码(>=24字符)
106
+ - Private Space 存放敏感凭证
107
+ - 不要在公共 README 中泄露密码
108
+ - 定期轮换凭证与刷新 token
109
+
110
+ ## 10. 维护操作
111
+
112
+ | 操作 | 方法 |
113
+ | ---- | ---- |
114
+ | 更新代码 | 推送到 Space 触发重建 |
115
+ | 查看日志 | Space Logs 面板 + Web 面板实时日志 |
116
+ | 清空日志 | 面板中提供清空按钮 |
117
+ | 添加凭证 | 面板上传或新增环境变量 |
118
+
119
+ ## 11. 自定义扩展 (可选)
120
+
121
+ 如果需要自定义依赖或系统级库,可切换到 **Docker Space**,并使用项目中现有 `Dockerfile`(如需精简可自行裁剪)。
122
+
123
+ ## 12. 版本升级策略
124
+
125
+ - 关注上游仓库 release
126
+ - 使用 Git Submodule 或定期同步
127
+ - 修改前备份 `creds/` 与持久化数据库
128
+
129
+ ---
130
+ 如需进一步自动化(CI 自动推送 Space),可添加 GitHub Actions,构建并同步至 Hugging Face。
docs/HUGGINGFACE_DOCKER_SPACE.md ADDED
@@ -0,0 +1,154 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Hugging Face Docker Space 部署指南
2
+
3
+ 本文档说明如何使用 Hugging Face 的 Docker Space 技术栈部署 GCLI2API。
4
+
5
+ ## 1. 选择 Docker 方式的适用场景
6
+
7
+ 选择 **Docker** 而不是 Gradio 技术栈的场景:
8
+ - 需要完全自定义运行环境
9
+ - 需要更快的冷启动或更可控的依赖
10
+ - 不需要额外 UI,仅提供 API 服务
11
+ - 需要使用系统级扩展、构建工具或自定义脚本
12
+
13
+ ## 2. 准备代码仓库
14
+
15
+ 仓库已包含优化后的 `Dockerfile`,特点:
16
+ - 多阶段构建(减小最终镜像体积)
17
+ - 使用 `tini` 作为 init 进程,优雅处理信号
18
+ - 非 root 用户运行(安全)
19
+ - 支持 Hugging Face 自动注入的 `PORT` 环境变量
20
+ - 支持健康检查(`/keepalive`)
21
+
22
+ ## 3. 创建 Docker Space
23
+
24
+ 1. 打开 https://huggingface.co/spaces
25
+ 2. Create New Space → 选择 `Docker` 技术栈
26
+ 3. 填写 Space 名称与可见性
27
+ 4. 创建后把本项目代码推送到该 Space(或者直接在 Space 网页上传文件)
28
+
29
+ ## 4. Dockerfile 说明
30
+
31
+ 当前项目内的 `Dockerfile` 默认启动命令:
32
+ ```dockerfile
33
+ CMD ["python","app.py"]
34
+ ```
35
+ app.py 会:
36
+ - 启动 FastAPI 主应用 (web.py)
37
+ - 提供可选的 Gradio 信息界面(若需要)
38
+
39
+ 如仅需 API,可改为:
40
+ ```dockerfile
41
+ CMD ["python","web.py"]
42
+ ```
43
+
44
+ ## 5. 配置环境变量
45
+
46
+ 在 Space 设置(Settings → Variables)中添加:
47
+
48
+ | 变量 | 说明 | 是否必需 |
49
+ | ---- | ---- | -------- |
50
+ | API_PASSWORD | API 访问密码 | 是 |
51
+ | PANEL_PASSWORD | 控制面板密码 | 是(或使用 PASSWORD) |
52
+ | AUTO_LOAD_ENV_CREDS | 启用环境变量凭证载入 | 否 |
53
+ | GCLI_CREDS_* | 提供 Google OAuth 凭证 | 依使用方式决定 |
54
+
55
+ 可选性能优化:
56
+ ```bash
57
+ CALLS_PER_ROTATION=10
58
+ RETRY_429_ENABLED=true
59
+ RETRY_429_MAX_RETRIES=5
60
+ ANTI_TRUNCATION_MAX_ATTEMPTS=3
61
+ COMPATIBILITY_MODE=true
62
+ ```
63
+
64
+ ## 6. 初次部署流程
65
+
66
+ 1. 推送代码后 Space 会自动构建镜像
67
+ 2. 构建完成后自动运行容器
68
+ 3. 查看 Logs 确认输出中包含:`GCLI2API Lightspeed - Hugging Face Spaces Edition`
69
+ 4. 访问:`https://<space-id>.hf.space/auth` 登录控制台
70
+ 5. 上传或配置凭证
71
+ 6. 通过 `/v1/models` 验证 API 可用
72
+
73
+ ## 7. API 访问示例
74
+
75
+ ```bash
76
+ curl -X POST "https://<space-id>.hf.space/v1/chat/completions" \
77
+ -H "Authorization: Bearer ${API_PASSWORD}" \
78
+ -H "Content-Type: application/json" \
79
+ -d '{
80
+ "model": "gemini-2.5-pro",
81
+ "messages": [
82
+ {"role": "user", "content": "介绍一下你自己"}
83
+ ]
84
+ }'
85
+ ```
86
+
87
+ 健康检查:
88
+ ```bash
89
+ curl -I "https://<space-id>.hf.space/keepalive"
90
+ ```
91
+
92
+ ## 8. 自定义与扩展
93
+
94
+ ### 更换 Python 版本
95
+ 修改 `FROM python:3.13-slim` 为其他受支持版本(建议 >=3.11)。
96
+
97
+ ### 添加系统依赖
98
+ 在 `Base Stage` 中:
99
+ ```dockerfile
100
+ RUN apt-get update && apt-get install -y --no-install-recommends \
101
+ ffmpeg libmagic1 && rm -rf /var/lib/apt/lists/*
102
+ ```
103
+
104
+ ### 修改启动参数
105
+ 使用环境变量:
106
+ ```bash
107
+ PORT=7860 HOST=0.0.0.0 LOG_LEVEL=INFO
108
+ ```
109
+
110
+ 或修改 Dockerfile:
111
+ ```dockerfile
112
+ CMD ["python","-m","web"]
113
+ ```
114
+
115
+ ## 9. 故障排查
116
+
117
+ | 症状 | 可能原因 | 解决方案 |
118
+ | ---- | -------- | -------- |
119
+ | Space 构建失败 | 依赖缺失/语法错误 | 查看构建日志,确认 requirements 正常安装 |
120
+ | 401 Unauthorized | 密码错误 | 确认 API_PASSWORD 与请求头一致 |
121
+ | 503 Service Unavailable | 无有效凭证 | 上传 Google OAuth 凭证或配置 GCLI_CREDS_* |
122
+ | 模型名称报错 | 模型名带前缀/后缀拼写错误 | 参考 README_HF.md 中模型命名规则 |
123
+ | 流式响应无输出 | 客户端不支持 SSE | 使用 假流式/ 前缀模型 |
124
+ | 截断 | 输出过长 | 使用 流式抗截断/ 前缀模型 |
125
+
126
+ ## 10. 升级策略
127
+
128
+ 更新代码并推送 -> Space 自动重建 -> 验证功能。
129
+ 如需保留持久凭证,可改用外部数据库(Redis/MongoDB/Postgres)。
130
+
131
+ ## 11. 使用外部 Redis (示例)
132
+
133
+ 在 Variables 中配置:
134
+ ```bash
135
+ REDIS_URI=rediss://default:token@xxx.upstash.io:6379
136
+ REDIS_DATABASE=0
137
+ ```
138
+
139
+ ## 12. 使用 MongoDB Atlas (示例)
140
+ ```bash
141
+ MONGODB_URI=mongodb+srv://user:pass@cluster.mongodb.net
142
+ MONGODB_DATABASE=gcli2api
143
+ ```
144
+
145
+ ## 13. 切换到 web.py 纯 API 模式
146
+
147
+ 编辑 Dockerfile:
148
+ ```dockerfile
149
+ CMD ["python","web.py"]
150
+ ```
151
+ 并删除 `app.py` 中的 Gradio 依赖(如不需要 UI)。
152
+
153
+ ---
154
+ 如需 CI 自动同步到 Space,可添加 GitHub Action 使用 `huggingface-cli` 推送更新。
docs/README_EN.md ADDED
@@ -0,0 +1,822 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # GeminiCLI to API
2
+
3
+ **Convert GeminiCLI to OpenAI and GEMINI API interfaces**
4
+
5
+ [中文](../README.md) | English
6
+
7
+ ## 🚀 Quick Deploy
8
+
9
+ [![Deploy on Zeabur](https://zeabur.com/button.svg)](https://zeabur.com/templates/2QLQC2?referralCode=su-kaka)
10
+ ---
11
+
12
+ ## ⚠️ License Declaration
13
+
14
+ **This project is licensed under the Cooperative Non-Commercial License (CNC-1.0)**
15
+
16
+ This is a strict anti-commercial open source license. Please refer to the [LICENSE](../LICENSE) file for details.
17
+
18
+ ### ✅ Permitted Uses:
19
+ - Personal learning, research, and educational purposes
20
+ - Non-profit organization use
21
+ - Open source project integration (must comply with the same license)
22
+ - Academic research and publication
23
+
24
+ ### ❌ Prohibited Uses:
25
+ - Any form of commercial use
26
+ - Enterprise use with annual revenue exceeding $1 million
27
+ - Venture capital-backed or publicly traded companies
28
+ - Providing paid services or products
29
+ - Commercial competitive use
30
+
31
+ ---
32
+
33
+ ## Core Features
34
+
35
+ ### 🔄 API Endpoints and Format Support
36
+
37
+ **Multi-endpoint Dual Format Support**
38
+ - **OpenAI Compatible Endpoints**: `/v1/chat/completions` and `/v1/models`
39
+ - Supports standard OpenAI format (messages structure)
40
+ - Supports Gemini native format (contents structure)
41
+ - Automatic format detection and conversion, no manual switching required
42
+ - Supports multimodal input (text + images)
43
+ - **Gemini Native Endpoints**: `/v1/models/{model}:generateContent` and `streamGenerateContent`
44
+ - Supports complete Gemini native API specifications
45
+ - Multiple authentication methods: Bearer Token, x-goog-api-key header, URL parameter key
46
+
47
+ ### 🔐 Authentication and Security Management
48
+
49
+ **Flexible Password Management**
50
+ - **Separate Password Support**: API password (chat endpoints) and control panel password can be set independently
51
+ - **Multiple Authentication Methods**: Supports Authorization Bearer, x-goog-api-key header, URL parameters, etc.
52
+ - **JWT Token Authentication**: Control panel supports JWT token authentication
53
+ - **User Email Retrieval**: Automatically retrieves and displays Google account email addresses
54
+
55
+ ### 📊 Intelligent Credential Management System
56
+
57
+ **Advanced Credential Management**
58
+ - Multiple Google OAuth credential automatic rotation
59
+ - Enhanced stability through redundant authentication
60
+ - Load balancing and concurrent request support
61
+ - Automatic failure detection and credential disabling
62
+ - Credential usage statistics and quota management
63
+ - Support for manual enable/disable credential files
64
+ - Batch credential file operations (enable, disable, delete)
65
+
66
+ **Credential Status Monitoring**
67
+ - Real-time credential health checks
68
+ - Error code tracking (429, 403, 500, etc.)
69
+ - Automatic banning mechanism (configurable)
70
+ - Credential rotation strategy (based on call count)
71
+ - Usage statistics and quota monitoring
72
+
73
+ ### 🌊 Streaming and Response Processing
74
+
75
+ **Multiple Streaming Support**
76
+ - True real-time streaming responses
77
+ - Fake streaming mode (for compatibility)
78
+ - Streaming anti-truncation feature (prevents answer truncation)
79
+ - Asynchronous task management and timeout handling
80
+
81
+ **Response Optimization**
82
+ - Thinking chain content separation
83
+ - Reasoning process (reasoning_content) handling
84
+ - Multi-turn conversation context management
85
+ - Compatibility mode (converts system messages to user messages)
86
+
87
+ ### 🎛️ Web Management Console
88
+
89
+ **Full-featured Web Interface**
90
+ - OAuth authentication flow management
91
+ - Credential file upload, download, and management
92
+ - Real-time log viewing (WebSocket)
93
+ - System configuration management
94
+ - Usage statistics and monitoring dashboard
95
+ - Mobile-friendly interface
96
+
97
+ **Batch Operation Support**
98
+ - ZIP file batch credential upload
99
+ - Batch enable/disable/delete credentials
100
+ - Batch user email retrieval
101
+ - Batch configuration management
102
+
103
+ ### 📈 Usage Statistics and Monitoring
104
+
105
+ **Detailed Usage Statistics**
106
+ - Call count statistics by credential file
107
+ - Gemini 2.5 Pro model specific statistics
108
+ - Daily quota management (UTC+7 reset)
109
+ - Aggregated statistics and analysis
110
+ - Custom daily limit configuration
111
+
112
+ **Real-time Monitoring**
113
+ - WebSocket real-time log streams
114
+ - System status monitoring
115
+ - Credential health status
116
+ - API call success rate statistics
117
+
118
+ ### 🔧 Advanced Configuration and Customization
119
+
120
+ **Network and Proxy Configuration**
121
+ - HTTP/HTTPS proxy support
122
+ - Proxy endpoint configuration (OAuth, Google APIs, metadata service)
123
+ - Timeout and retry configuration
124
+ - Network error handling and recovery
125
+
126
+ **Performance and Stability Configuration**
127
+ - 429 error automatic retry (configurable interval and attempts)
128
+ - Anti-truncation maximum retry attempts
129
+ - Credential rotation strategy
130
+ - Concurrent request management
131
+
132
+ **Logging and Debugging**
133
+ - Multi-level logging system (DEBUG, INFO, WARNING, ERROR)
134
+ - Log file management
135
+ - Real-time log streams
136
+ - Log download and clearing
137
+
138
+ ### 🔄 Environment Variables and Configuration Management
139
+
140
+ **Flexible Configuration Methods**
141
+ - TOML configuration file support
142
+ - Environment variable configuration
143
+ - Hot configuration updates (partial configuration items)
144
+ - Configuration locking (environment variable priority)
145
+
146
+ **Environment Variable Credential Support**
147
+ - `GCLI_CREDS_*` format environment variable import
148
+ - Automatic loading of environment variable credentials
149
+ - Base64 encoded credential support
150
+ - Docker container friendly
151
+
152
+ ## Supported Models
153
+
154
+ All models have 1M context window capacity. Each credential file provides 1000 request quota.
155
+
156
+ ### 🤖 Base Models
157
+ - `gemini-2.5-pro`
158
+ - `gemini-2.5-pro-preview-06-05`
159
+ - `gemini-2.5-pro-preview-05-06`
160
+
161
+ ### 🧠 Thinking Models
162
+ - `gemini-2.5-pro-maxthinking`: Maximum thinking budget mode
163
+ - `gemini-2.5-pro-nothinking`: No thinking mode
164
+ - Supports custom thinking budget configuration
165
+ - Automatic separation of thinking content and final answers
166
+
167
+ ### 🔍 Search-Enhanced Models
168
+ - `gemini-2.5-pro-search`: Model with integrated search functionality
169
+
170
+ ### 🌊 Special Feature Variants
171
+ - **Fake Streaming Mode**: Add `-假流式` suffix to any model name
172
+ - Example: `gemini-2.5-pro-假流式`
173
+ - For scenarios requiring streaming responses but server doesn't support true streaming
174
+ - **Streaming Anti-truncation Mode**: Add `流式抗截断/` prefix to model name
175
+ - Example: `流式抗截断/gemini-2.5-pro`
176
+ - Automatically detects response truncation and retries to ensure complete answers
177
+
178
+ ### 🔧 Automatic Model Feature Detection
179
+ - System automatically recognizes feature identifiers in model names
180
+ - Transparently handles feature mode transitions
181
+ - Supports feature combination usage
182
+
183
+ ---
184
+
185
+ ## Installation Guide
186
+
187
+ ### Termux Environment
188
+
189
+ **Initial Installation**
190
+ ```bash
191
+ curl -o termux-install.sh "https://raw.githubusercontent.com/su-kaka/gcli2api/refs/heads/master/termux-install.sh" && chmod +x termux-install.sh && ./termux-install.sh
192
+ ```
193
+
194
+ **Restart Service**
195
+ ```bash
196
+ cd gcli2api
197
+ bash termux-start.sh
198
+ ```
199
+
200
+ ### Windows Environment
201
+
202
+ **Initial Installation**
203
+ ```powershell
204
+ iex (iwr "https://raw.githubusercontent.com/su-kaka/gcli2api/refs/heads/master/install.ps1" -UseBasicParsing).Content
205
+ ```
206
+
207
+ **Restart Service**
208
+ Double-click to execute `start.bat`
209
+
210
+ ### Linux Environment
211
+
212
+ **Initial Installation**
213
+ ```bash
214
+ curl -o install.sh "https://raw.githubusercontent.com/su-kaka/gcli2api/refs/heads/master/install.sh" && chmod +x install.sh && ./install.sh
215
+ ```
216
+
217
+ **Restart Service**
218
+ ```bash
219
+ cd gcli2api
220
+ bash start.sh
221
+ ```
222
+
223
+ ### Docker Environment
224
+
225
+ **Docker Run Command**
226
+ ```bash
227
+ # Using universal password
228
+ docker run -d --name gcli2api --network host -e PASSWORD=pwd -e PORT=7861 -v $(pwd)/data/creds:/app/creds ghcr.io/su-kaka/gcli2api:latest
229
+
230
+ # Using separate passwords
231
+ docker run -d --name gcli2api --network host -e API_PASSWORD=api_pwd -e PANEL_PASSWORD=panel_pwd -e PORT=7861 -v $(pwd)/data/creds:/app/creds ghcr.io/su-kaka/gcli2api:latest
232
+ ```
233
+
234
+ **Docker Compose Run Command**
235
+ 1. Save the following content as `docker-compose.yml` file:
236
+ ```yaml
237
+ version: '3.8'
238
+
239
+ services:
240
+ gcli2api:
241
+ image: ghcr.io/su-kaka/gcli2api:latest
242
+ container_name: gcli2api
243
+ restart: unless-stopped
244
+ network_mode: host
245
+ environment:
246
+ # Using universal password (recommended for simple deployment)
247
+ - PASSWORD=pwd
248
+ - PORT=7861
249
+ # Or use separate passwords (recommended for production)
250
+ # - API_PASSWORD=your_api_password
251
+ # - PANEL_PASSWORD=your_panel_password
252
+ volumes:
253
+ - ./data/creds:/app/creds
254
+ healthcheck:
255
+ test: ["CMD-SHELL", "python -c \"import sys, urllib.request, os; port = os.environ.get('PORT', '7861'); req = urllib.request.Request(f'http://localhost:{port}/v1/models', headers={'Authorization': 'Bearer ' + os.environ.get('PASSWORD', 'pwd')}); sys.exit(0 if urllib.request.urlopen(req, timeout=5).getcode() == 200 else 1)\""]
256
+ interval: 30s
257
+ timeout: 10s
258
+ retries: 3
259
+ start_period: 40s
260
+ ```
261
+ 2. Start the service:
262
+ ```bash
263
+ docker-compose up -d
264
+ ```
265
+
266
+ ---
267
+
268
+ ## ⚠️ Important Notes
269
+
270
+ - The current OAuth authentication process **only supports localhost access**, meaning authentication must be completed through `http://127.0.0.1:7861/auth` (default port 7861, modifiable via PORT environment variable).
271
+ - **For deployment on cloud servers or other remote environments, please first run the service locally and complete OAuth authentication to obtain the generated json credential files (located in the `./geminicli/creds` directory), then upload these files via the auth panel.**
272
+ - **Please strictly comply with usage restrictions, only for personal learning and non-commercial purposes**
273
+
274
+ ---
275
+
276
+ ## Configuration Instructions
277
+
278
+ 1. Visit `http://127.0.0.1:7861/auth` (default port, modifiable via PORT environment variable)
279
+ 2. Complete OAuth authentication flow (default password: `pwd`, modifiable via environment variables)
280
+ 3. Configure client:
281
+
282
+ **OpenAI Compatible Client:**
283
+ - **Endpoint Address**: `http://127.0.0.1:7861/v1`
284
+ - **API Key**: `pwd` (default value, modifiable via API_PASSWORD or PASSWORD environment variables)
285
+
286
+ **Gemini Native Client:**
287
+ - **Endpoint Address**: `http://127.0.0.1:7861`
288
+ - **Authentication Methods**:
289
+ - `Authorization: Bearer your_api_password`
290
+ - `x-goog-api-key: your_api_password`
291
+ - URL parameter: `?key=your_api_password`
292
+
293
+ ## 💾 Distributed Storage Mode
294
+
295
+ ### 🌟 Storage Backend Priority
296
+
297
+ gcli2api supports multiple storage backends, automatically selecting by priority: **Redis > Postgres > MongoDB > Local Files**
298
+
299
+ ### ⚡ Redis Distributed Storage Mode
300
+
301
+ ### ⚙️ Enable Redis Mode
302
+
303
+ **Step 1: Configure Redis Connection**
304
+ ```bash
305
+ # Local Redis
306
+ export REDIS_URI="redis://localhost:6379"
307
+
308
+ # Redis with password
309
+ export REDIS_URI="redis://:password@localhost:6379"
310
+
311
+ # SSL connection (recommended for production)
312
+ export REDIS_URI="rediss://default:password@host:6380"
313
+
314
+ # Upstash Redis (free cloud service)
315
+ export REDIS_URI="rediss://default:token@your-host.upstash.io:6379"
316
+
317
+ # Optional: Custom database index (default: 0)
318
+ export REDIS_DATABASE="1"
319
+ ```
320
+
321
+ **Step 2: Start Application**
322
+ ```bash
323
+ # Application will automatically detect Redis configuration and prioritize Redis storage
324
+ python web.py
325
+ ```
326
+
327
+ ### 🐘 Postgres Distributed Storage Mode
328
+
329
+ If Redis is not configured, or you prefer a relational database, gcli2api also supports Postgres (it is checked after Redis and before MongoDB).
330
+
331
+ ⚙️ Enable Postgres Mode
332
+
333
+ Step 1: Configure Postgres DSN
334
+ ```bash
335
+ # Example DSN:
336
+ export POSTGRES_DSN="postgresql://user:password@localhost:5432/gcli2api"
337
+ ```
338
+
339
+ Step 2: Start Application
340
+ ```bash
341
+ # Application will detect POSTGRES_DSN and use Postgres when Redis is not available
342
+ python web.py
343
+ ```
344
+
345
+ ### 🍃 MongoDB Distributed Storage Mode
346
+
347
+ ### 🌟 Alternative Storage Solution
348
+
349
+ If Redis is not configured, gcli2api will attempt to use **MongoDB storage mode**.
350
+
351
+ ### ⚙️ Enable MongoDB Mode
352
+
353
+ **Step 1: Configure MongoDB Connection**
354
+ ```bash
355
+ # Local MongoDB
356
+ export MONGODB_URI="mongodb://localhost:27017"
357
+
358
+ # MongoDB Atlas cloud service
359
+ export MONGODB_URI="mongodb+srv://username:password@cluster.mongodb.net"
360
+
361
+ # MongoDB with authentication
362
+ export MONGODB_URI="mongodb://admin:password@localhost:27017/admin"
363
+
364
+ # Optional: Custom database name (default: gcli2api)
365
+ export MONGODB_DATABASE="my_gcli_db"
366
+ ```
367
+
368
+ **Step 2: Start Application**
369
+ ```bash
370
+ # Application will automatically detect MongoDB configuration and use MongoDB storage
371
+ python web.py
372
+ ```
373
+
374
+ **Docker Environment using MongoDB**
375
+ ```bash
376
+ # Single MongoDB deployment
377
+ docker run -d --name gcli2api \
378
+ -e MONGODB_URI="mongodb://mongodb:27017" \
379
+ -e API_PASSWORD=your_password \
380
+ --network your_network \
381
+ ghcr.io/su-kaka/gcli2api:latest
382
+
383
+ # Using MongoDB Atlas
384
+ docker run -d --name gcli2api \
385
+ -e MONGODB_URI="mongodb+srv://user:pass@cluster.mongodb.net/gcli2api" \
386
+ -e API_PASSWORD=your_password \
387
+ -p 7861:7861 \
388
+ ghcr.io/su-kaka/gcli2api:latest
389
+ ```
390
+
391
+ **Docker Compose Example**
392
+ ```yaml
393
+ version: '3.8'
394
+
395
+ services:
396
+ mongodb:
397
+ image: mongo:7
398
+ container_name: gcli2api-mongodb
399
+ restart: unless-stopped
400
+ environment:
401
+ MONGO_INITDB_ROOT_USERNAME: admin
402
+ MONGO_INITDB_ROOT_PASSWORD: password123
403
+ volumes:
404
+ - mongodb_data:/data/db
405
+ ports:
406
+ - "27017:27017"
407
+
408
+ gcli2api:
409
+ image: ghcr.io/su-kaka/gcli2api:latest
410
+ container_name: gcli2api
411
+ restart: unless-stopped
412
+ depends_on:
413
+ - mongodb
414
+ environment:
415
+ - MONGODB_URI=mongodb://admin:password123@mongodb:27017/admin
416
+ - MONGODB_DATABASE=gcli2api
417
+ - API_PASSWORD=your_api_password
418
+ - PORT=7861
419
+ ports:
420
+ - "7861:7861"
421
+
422
+ volumes:
423
+ mongodb_data:
424
+ ```
425
+
426
+ ### 🛠️ Troubleshooting
427
+
428
+ **Common Issue Solutions**
429
+
430
+ ```bash
431
+ # Check MongoDB connection
432
+ python mongodb_setup.py check
433
+
434
+ # View detailed status information
435
+ python mongodb_setup.py status
436
+
437
+ # Verify data migration results
438
+ python -c "
439
+ import asyncio
440
+ from src.storage_adapter import get_storage_adapter
441
+
442
+ async def test():
443
+ storage = await get_storage_adapter()
444
+ info = await storage.get_backend_info()
445
+ print(f'Current mode: {info[\"backend_type\"]}')
446
+ if info['backend_type'] == 'mongodb':
447
+ print(f'Database: {info.get(\"database_name\", \"Unknown\")}')
448
+
449
+ asyncio.run(test())
450
+ "
451
+ ```
452
+
453
+ **Migration Failure Handling**
454
+ ```bash
455
+ # If migration is interrupted, re-run
456
+ python mongodb_setup.py migrate
457
+
458
+ # To rollback to file mode, remove MONGODB_URI environment variable
459
+ unset MONGODB_URI
460
+ # Then export data from MongoDB
461
+ python mongodb_setup.py export
462
+ ```
463
+
464
+ ### 🔧 Advanced Configuration
465
+
466
+ **MongoDB Connection Optimization**
467
+ ```bash
468
+ # Connection pool and timeout configuration
469
+ export MONGODB_URI="mongodb://localhost:27017?maxPoolSize=10&serverSelectionTimeoutMS=5000"
470
+
471
+ # Replica set configuration
472
+ export MONGODB_URI="mongodb://host1:27017,host2:27017,host3:27017/gcli2api?replicaSet=myReplicaSet"
473
+
474
+ # Read-write separation configuration
475
+ export MONGODB_URI="mongodb://localhost:27017/gcli2api?readPreference=secondaryPreferred"
476
+ ```
477
+
478
+ ## 🏗️ Technical Architecture
479
+
480
+ ### Core Module Description
481
+
482
+ **Authentication and Credential Management** (`src/auth.py`, `src/credential_manager.py`)
483
+ - OAuth 2.0 authentication flow management
484
+ - Multi-credential file status management and rotation
485
+ - Automatic failure detection and recovery
486
+ - JWT token generation and validation
487
+
488
+ **API Routing and Conversion** (`src/openai_router.py`, `src/gemini_router.py`, `src/openai_transfer.py`)
489
+ - OpenAI and Gemini format bidirectional conversion
490
+ - Multimodal input processing (text+images)
491
+ - Thinking chain content separation and processing
492
+ - Streaming response management
493
+
494
+ **Network and Proxy** (`src/httpx_client.py`, `src/google_chat_api.py`)
495
+ - Unified HTTP client management
496
+ - Proxy configuration and hot update support
497
+ - Timeout and retry strategies
498
+ - Asynchronous request pool management
499
+
500
+ **State Management** (`src/state_manager.py`, `src/usage_stats.py`)
501
+ - Atomic state operations
502
+ - Usage statistics and quota management
503
+ - File locking and concurrency safety
504
+ - Data persistence (TOML format)
505
+
506
+ **Task Management** (`src/task_manager.py`)
507
+ - Global asynchronous task lifecycle management
508
+ - Resource cleanup and memory management
509
+ - Graceful shutdown and exception handling
510
+
511
+ **Web Console** (`src/web_routes.py`)
512
+ - RESTful API endpoints
513
+ - WebSocket real-time communication
514
+ - Mobile device adaptation detection
515
+ - Batch operation support
516
+
517
+ ### Advanced Feature Implementation
518
+
519
+ **Streaming Anti-truncation Mechanism** (`src/anti_truncation.py`)
520
+ - Response truncation pattern detection
521
+ - Automatic retry and state recovery
522
+ - Context connection management
523
+
524
+ **Format Detection and Conversion** (`src/format_detector.py`)
525
+ - Automatic request format detection (OpenAI vs Gemini)
526
+ - Seamless format conversion
527
+ - Parameter mapping and validation
528
+
529
+ **User Agent Simulation** (`src/utils.py`)
530
+ - GeminiCLI format user agent generation
531
+ - Platform detection and client metadata
532
+ - API compatibility guarantee
533
+
534
+ ### Environment Variable Configuration
535
+
536
+ **Basic Configuration**
537
+ - `PORT`: Service port (default: 7861)
538
+ - `HOST`: Server listen address (default: 0.0.0.0)
539
+
540
+ **Password Configuration**
541
+ - `API_PASSWORD`: Chat API access password (default: inherits PASSWORD or pwd)
542
+ - `PANEL_PASSWORD`: Control panel access password (default: inherits PASSWORD or pwd)
543
+ - `PASSWORD`: Universal password, overrides the above two when set (default: pwd)
544
+
545
+ **Performance and Stability Configuration**
546
+ - `CALLS_PER_ROTATION`: Number of calls before each credential rotation (default: 10)
547
+ - `RETRY_429_ENABLED`: Enable 429 error automatic retry (default: true)
548
+ - `RETRY_429_MAX_RETRIES`: Maximum retry attempts for 429 errors (default: 3)
549
+ - `RETRY_429_INTERVAL`: Retry interval for 429 errors, in seconds (default: 1.0)
550
+ - `ANTI_TRUNCATION_MAX_ATTEMPTS`: Maximum retry attempts for anti-truncation (default: 3)
551
+
552
+ **Network and Proxy Configuration**
553
+ - `PROXY`: HTTP/HTTPS proxy address (format: `http://host:port`)
554
+ - `OAUTH_PROXY_URL`: OAuth authentication proxy endpoint
555
+ - `GOOGLEAPIS_PROXY_URL`: Google APIs proxy endpoint
556
+ - `METADATA_SERVICE_URL`: Metadata service proxy endpoint
557
+
558
+ **Automation Configuration**
559
+ - `AUTO_BAN`: Enable automatic credential banning (default: true)
560
+ - `AUTO_LOAD_ENV_CREDS`: Automatically load environment variable credentials at startup (default: false)
561
+
562
+ **Compatibility Configuration**
563
+ - `COMPATIBILITY_MODE`: Enable compatibility mode, converts system messages to user messages (default: false)
564
+
565
+ **Logging Configuration**
566
+ - `LOG_LEVEL`: Log level (DEBUG/INFO/WARNING/ERROR, default: INFO)
567
+ - `LOG_FILE`: Log file path (default: gcli2api.log)
568
+
569
+ **Storage Configuration (by priority)**
570
+
571
+ **Redis Configuration (Highest Priority)**
572
+ - `REDIS_URI`: Redis connection string (enables Redis mode when set)
573
+ - Local: `redis://localhost:6379`
574
+ - With password: `redis://:password@host:6379`
575
+ - SSL: `rediss://default:password@host:6380`
576
+ - `REDIS_DATABASE`: Redis database index (0-15, default: 0)
577
+
578
+ **MongoDB Configuration (Second Priority)**
579
+ - `MONGODB_URI`: MongoDB connection string (enables MongoDB mode when set)
580
+ - `MONGODB_DATABASE`: MongoDB database name (default: gcli2api)
581
+
582
+ **Credential Configuration**
583
+
584
+ Support importing multiple credentials using `GCLI_CREDS_*` environment variables:
585
+
586
+ #### Credential Environment Variable Usage Examples
587
+
588
+ **Method 1: Numbered Format**
589
+ ```bash
590
+ export GCLI_CREDS_1='{"client_id":"your-client-id","client_secret":"your-secret","refresh_token":"your-token","token_uri":"https://oauth2.googleapis.com/token","project_id":"your-project"}'
591
+ export GCLI_CREDS_2='{"client_id":"...","project_id":"..."}'
592
+ ```
593
+
594
+ **Method 2: Project Name Format**
595
+ ```bash
596
+ export GCLI_CREDS_myproject='{"client_id":"...","project_id":"myproject",...}'
597
+ export GCLI_CREDS_project2='{"client_id":"...","project_id":"project2",...}'
598
+ ```
599
+
600
+ **Enable Automatic Loading**
601
+ ```bash
602
+ export AUTO_LOAD_ENV_CREDS=true # Automatically import environment variable credentials at program startup
603
+ ```
604
+
605
+ **Docker Usage Example**
606
+ ```bash
607
+ # Using universal password
608
+ docker run -d --name gcli2api \
609
+ -e PASSWORD=mypassword \
610
+ -e PORT=8080 \
611
+ -e GOOGLE_CREDENTIALS="$(cat credential.json | base64 -w 0)" \
612
+ ghcr.io/su-kaka/gcli2api:latest
613
+
614
+ # Using separate passwords
615
+ docker run -d --name gcli2api \
616
+ -e API_PASSWORD=my_api_password \
617
+ -e PANEL_PASSWORD=my_panel_password \
618
+ -e PORT=8080 \
619
+ -e GOOGLE_CREDENTIALS="$(cat credential.json | base64 -w 0)" \
620
+ ghcr.io/su-kaka/gcli2api:latest
621
+ ```
622
+
623
+ Note: When credential environment variables are set, the system will prioritize using credentials from environment variables and ignore files in the `creds` directory.
624
+
625
+ ### API Usage Methods
626
+
627
+ This service supports two complete sets of API endpoints:
628
+
629
+ #### 1. OpenAI Compatible Endpoints
630
+
631
+ **Endpoint:** `/v1/chat/completions`
632
+ **Authentication:** `Authorization: Bearer your_api_password`
633
+
634
+ Supports two request formats with automatic detection and processing:
635
+
636
+ **OpenAI Format:**
637
+ ```json
638
+ {
639
+ "model": "gemini-2.5-pro",
640
+ "messages": [
641
+ {"role": "system", "content": "You are a helpful assistant"},
642
+ {"role": "user", "content": "Hello"}
643
+ ],
644
+ "temperature": 0.7,
645
+ "stream": true
646
+ }
647
+ ```
648
+
649
+ **Gemini Native Format:**
650
+ ```json
651
+ {
652
+ "model": "gemini-2.5-pro",
653
+ "contents": [
654
+ {"role": "user", "parts": [{"text": "Hello"}]}
655
+ ],
656
+ "systemInstruction": {"parts": [{"text": "You are a helpful assistant"}]},
657
+ "generationConfig": {
658
+ "temperature": 0.7
659
+ }
660
+ }
661
+ ```
662
+
663
+ #### 2. Gemini Native Endpoints
664
+
665
+ **Non-streaming Endpoint:** `/v1/models/{model}:generateContent`
666
+ **Streaming Endpoint:** `/v1/models/{model}:streamGenerateContent`
667
+ **Model List:** `/v1/models`
668
+
669
+ **Authentication Methods (choose one):**
670
+ - `Authorization: Bearer your_api_password`
671
+ - `x-goog-api-key: your_api_password`
672
+ - URL parameter: `?key=your_api_password`
673
+
674
+ **Request Examples:**
675
+ ```bash
676
+ # Using x-goog-api-key header
677
+ curl -X POST "http://127.0.0.1:7861/v1/models/gemini-2.5-pro:generateContent" \
678
+ -H "x-goog-api-key: your_api_password" \
679
+ -H "Content-Type: application/json" \
680
+ -d '{
681
+ "contents": [
682
+ {"role": "user", "parts": [{"text": "Hello"}]}
683
+ ]
684
+ }'
685
+
686
+ # Using URL parameter
687
+ curl -X POST "http://127.0.0.1:7861/v1/models/gemini-2.5-pro:streamGenerateContent?key=your_api_password" \
688
+ -H "Content-Type: application/json" \
689
+ -d '{
690
+ "contents": [
691
+ {"role": "user", "parts": [{"text": "Hello"}]}
692
+ ]
693
+ }'
694
+ ```
695
+
696
+ **Notes:**
697
+ - OpenAI endpoints return OpenAI-compatible format
698
+ - Gemini endpoints return Gemini native format
699
+ - Both endpoints use the same API password
700
+
701
+ ## 📋 Complete API Reference
702
+
703
+ ### Web Console API
704
+
705
+ **Authentication Endpoints**
706
+ - `POST /auth/login` - User login
707
+ - `POST /auth/start` - Start OAuth authentication
708
+ - `POST /auth/callback` - Handle OAuth callback
709
+ - `GET /auth/status/{project_id}` - Check authentication status
710
+
711
+ **Credential Management Endpoints**
712
+ - `GET /creds/status` - Get all credential statuses
713
+ - `POST /creds/action` - Single credential operation (enable/disable/delete)
714
+ - `POST /creds/batch-action` - Batch credential operations
715
+ - `POST /auth/upload` - Batch upload credential files (supports ZIP)
716
+ - `GET /creds/download/{filename}` - Download credential file
717
+ - `GET /creds/download-all` - Package download all credentials
718
+ - `POST /creds/fetch-email/{filename}` - Get user email
719
+ - `POST /creds/refresh-all-emails` - Batch refresh user emails
720
+
721
+ **Configuration Management Endpoints**
722
+ - `GET /config/get` - Get current configuration
723
+ - `POST /config/save` - Save configuration
724
+
725
+ **Environment Variable Credential Endpoints**
726
+ - `POST /auth/load-env-creds` - Load environment variable credentials
727
+ - `DELETE /auth/env-creds` - Clear environment variable credentials
728
+ - `GET /auth/env-creds-status` - Get environment variable credential status
729
+
730
+ **Log Management Endpoints**
731
+ - `POST /auth/logs/clear` - Clear logs
732
+ - `GET /auth/logs/download` - Download log file
733
+ - `WebSocket /auth/logs/stream` - Real-time log stream
734
+
735
+ **Usage Statistics Endpoints**
736
+ - `GET /usage/stats` - Get usage statistics
737
+ - `GET /usage/aggregated` - Get aggregated statistics
738
+ - `POST /usage/update-limits` - Update usage limits
739
+ - `POST /usage/reset` - Reset usage statistics
740
+
741
+ ### Chat API Features
742
+
743
+ **Multimodal Support**
744
+ ```json
745
+ {
746
+ "model": "gemini-2.5-pro",
747
+ "messages": [
748
+ {
749
+ "role": "user",
750
+ "content": [
751
+ {"type": "text", "text": "Describe this image"},
752
+ {
753
+ "type": "image_url",
754
+ "image_url": {
755
+ "url": "data:image/jpeg;base64,/9j/4AAQSkZJRgABA..."
756
+ }
757
+ }
758
+ ]
759
+ }
760
+ ]
761
+ }
762
+ ```
763
+
764
+ **Thinking Mode Support**
765
+ ```json
766
+ {
767
+ "model": "gemini-2.5-pro-maxthinking",
768
+ "messages": [
769
+ {"role": "user", "content": "Complex math problem"}
770
+ ]
771
+ }
772
+ ```
773
+
774
+ Response will include separated thinking content:
775
+ ```json
776
+ {
777
+ "choices": [{
778
+ "message": {
779
+ "role": "assistant",
780
+ "content": "Final answer",
781
+ "reasoning_content": "Detailed thought process..."
782
+ }
783
+ }]
784
+ }
785
+ ```
786
+
787
+ **Streaming Anti-truncation Usage**
788
+ ```json
789
+ {
790
+ "model": "流式抗截断/gemini-2.5-pro",
791
+ "messages": [
792
+ {"role": "user", "content": "Write a long article"}
793
+ ],
794
+ "stream": true
795
+ }
796
+ ```
797
+
798
+ **Compatibility Mode**
799
+ ```bash
800
+ # Enable compatibility mode
801
+ export COMPATIBILITY_MODE=true
802
+ ```
803
+ In this mode, all `system` messages are converted to `user` messages, improving compatibility with certain clients.
804
+
805
+ ---
806
+
807
+ ## Support the Project
808
+
809
+ If this project has been helpful to you, we welcome your support for the project's continued development!
810
+
811
+ For detailed donation information, please see: [📖 Donation Documentation](DONATE.md)
812
+
813
+ ---
814
+
815
+ ## License and Disclaimer
816
+
817
+ This project is for learning and research purposes only. Using this project indicates that you agree to:
818
+ - Not use this project for any commercial purposes
819
+ - Bear all risks and responsibilities of using this project
820
+ - Comply with relevant terms of service and legal regulations
821
+
822
+ The project authors are not responsible for any direct or indirect losses arising from the use of this project.
docs/币安.jpg ADDED

Git LFS Details

  • SHA256: dda43355858a13738dcb9023374aefc303ec846dd21f7c4b60c37bea5165852b
  • Pointer size: 131 Bytes
  • Size of remote file: 106 kB
front/control_panel.html ADDED
The diff for this file is too large to render. See raw diff
 
front/control_panel_mobile.html ADDED
The diff for this file is too large to render. See raw diff
 
front/multi_user_auth_web.html ADDED
@@ -0,0 +1,762 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="zh-CN">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Google OAuth 认证</title>
7
+ <style>
8
+ body {
9
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Arial, sans-serif;
10
+ max-width: 700px;
11
+ margin: 0 auto;
12
+ padding: 20px;
13
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
14
+ min-height: 100vh;
15
+ display: flex;
16
+ align-items: center;
17
+ justify-content: center;
18
+ }
19
+ .container {
20
+ background: white;
21
+ padding: 40px;
22
+ border-radius: 15px;
23
+ box-shadow: 0 20px 40px rgba(0,0,0,0.1);
24
+ width: 100%;
25
+ max-width: 600px;
26
+ }
27
+ h1 {
28
+ color: #333;
29
+ text-align: center;
30
+ margin-bottom: 30px;
31
+ font-size: 28px;
32
+ font-weight: 300;
33
+ }
34
+ .logo {
35
+ text-align: center;
36
+ margin-bottom: 30px;
37
+ }
38
+ .logo svg {
39
+ width: 60px;
40
+ height: 60px;
41
+ }
42
+ .form-group {
43
+ margin-bottom: 25px;
44
+ }
45
+ label {
46
+ display: block;
47
+ margin-bottom: 8px;
48
+ font-weight: 600;
49
+ color: #555;
50
+ font-size: 14px;
51
+ }
52
+ input[type="text"], input[type="password"] {
53
+ width: 100%;
54
+ padding: 12px 16px;
55
+ border: 2px solid #e1e5e9;
56
+ border-radius: 8px;
57
+ font-size: 16px;
58
+ box-sizing: border-box;
59
+ transition: all 0.3s ease;
60
+ }
61
+ input[type="text"]:focus, input[type="password"]:focus {
62
+ border-color: #4285f4;
63
+ outline: none;
64
+ box-shadow: 0 0 0 3px rgba(66, 133, 244, 0.1);
65
+ }
66
+ .btn {
67
+ background: linear-gradient(135deg, #4285f4, #34a853);
68
+ color: white;
69
+ padding: 14px 30px;
70
+ border: none;
71
+ border-radius: 8px;
72
+ font-size: 16px;
73
+ cursor: pointer;
74
+ width: 100%;
75
+ margin-bottom: 15px;
76
+ font-weight: 600;
77
+ transition: all 0.3s ease;
78
+ }
79
+ .btn:hover {
80
+ transform: translateY(-2px);
81
+ box-shadow: 0 10px 20px rgba(66, 133, 244, 0.3);
82
+ }
83
+ .btn:active {
84
+ transform: translateY(0);
85
+ }
86
+ .btn:disabled {
87
+ background: #ccc;
88
+ cursor: not-allowed;
89
+ transform: none;
90
+ box-shadow: none;
91
+ }
92
+ .btn.secondary {
93
+ background: linear-gradient(135deg, #34a853, #fbbc04);
94
+ }
95
+ .btn.download {
96
+ background: linear-gradient(135deg, #ea4335, #ff6900);
97
+ }
98
+ .auth-url {
99
+ background: #f8f9fa;
100
+ border: 2px solid #e1e4e8;
101
+ border-radius: 8px;
102
+ padding: 20px;
103
+ margin: 25px 0;
104
+ word-break: break-all;
105
+ border-left: 4px solid #4285f4;
106
+ }
107
+ .auth-url a {
108
+ color: #4285f4;
109
+ text-decoration: none;
110
+ font-weight: 600;
111
+ }
112
+ .auth-url a:hover {
113
+ text-decoration: underline;
114
+ }
115
+ .credentials {
116
+ background: #f0f8ff;
117
+ border: 2px solid #b0d4ff;
118
+ border-radius: 8px;
119
+ padding: 20px;
120
+ margin: 25px 0;
121
+ font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
122
+ font-size: 12px;
123
+ white-space: pre-wrap;
124
+ word-break: break-all;
125
+ max-height: 400px;
126
+ overflow-y: auto;
127
+ border-left: 4px solid #34a853;
128
+ }
129
+ .status {
130
+ padding: 15px 20px;
131
+ border-radius: 8px;
132
+ margin: 20px 0;
133
+ font-weight: 500;
134
+ }
135
+ .status.success {
136
+ background: #d4edda;
137
+ border: 2px solid #c3e6cb;
138
+ color: #155724;
139
+ border-left: 4px solid #28a745;
140
+ }
141
+ .status.error {
142
+ background: #f8d7da;
143
+ border: 2px solid #f5c6cb;
144
+ color: #721c24;
145
+ border-left: 4px solid #dc3545;
146
+ }
147
+ .status.info {
148
+ background: #d1ecf1;
149
+ border: 2px solid #bee5eb;
150
+ color: #0c5460;
151
+ border-left: 4px solid #17a2b8;
152
+ }
153
+ .status.warning {
154
+ background: #fff3cd;
155
+ border: 2px solid #ffeaa7;
156
+ color: #856404;
157
+ border-left: 4px solid #ffc107;
158
+ }
159
+ .hidden {
160
+ display: none;
161
+ }
162
+ .loading {
163
+ text-align: center;
164
+ color: #666;
165
+ }
166
+ .login-form {
167
+ text-align: center;
168
+ padding: 20px 0;
169
+ }
170
+ .steps {
171
+ background: #f8f9fa;
172
+ border-radius: 8px;
173
+ padding: 20px;
174
+ margin: 25px 0;
175
+ border-left: 4px solid #ffc107;
176
+ }
177
+ .steps h4 {
178
+ margin-top: 0;
179
+ color: #333;
180
+ font-size: 16px;
181
+ }
182
+ .steps ol {
183
+ margin: 15px 0;
184
+ padding-left: 20px;
185
+ }
186
+ .steps li {
187
+ margin-bottom: 8px;
188
+ color: #555;
189
+ line-height: 1.5;
190
+ }
191
+ .api-warning {
192
+ background: #fff3cd;
193
+ border: 2px solid #ffeaa7;
194
+ border-radius: 8px;
195
+ padding: 20px;
196
+ margin: 25px 0;
197
+ border-left: 4px solid #ffc107;
198
+ }
199
+ .api-warning h4 {
200
+ color: #856404;
201
+ margin-top: 0;
202
+ display: flex;
203
+ align-items: center;
204
+ gap: 8px;
205
+ }
206
+ .api-warning a {
207
+ color: #4285f4;
208
+ text-decoration: none;
209
+ font-weight: 600;
210
+ }
211
+ .api-warning a:hover {
212
+ text-decoration: underline;
213
+ }
214
+ .progress-bar {
215
+ background: #e9ecef;
216
+ border-radius: 10px;
217
+ height: 8px;
218
+ margin: 15px 0;
219
+ overflow: hidden;
220
+ }
221
+ .progress-fill {
222
+ background: linear-gradient(135deg, #28a745, #20c997);
223
+ height: 100%;
224
+ width: 0%;
225
+ transition: width 0.3s ease;
226
+ }
227
+ .footer {
228
+ text-align: center;
229
+ margin-top: 30px;
230
+ padding-top: 20px;
231
+ border-top: 1px solid #e1e5e9;
232
+ color: #666;
233
+ font-size: 14px;
234
+ }
235
+ @media (max-width: 768px) {
236
+ body {
237
+ padding: 10px;
238
+ }
239
+ .container {
240
+ padding: 20px;
241
+ }
242
+ }
243
+
244
+ /* 下载按钮动画 */
245
+ .download-animation {
246
+ animation: downloadPulse 2s infinite;
247
+ }
248
+
249
+ @keyframes downloadPulse {
250
+ 0% { transform: scale(1); }
251
+ 50% { transform: scale(1.05); }
252
+ 100% { transform: scale(1); }
253
+ }
254
+ </style>
255
+ </head>
256
+ <body>
257
+ <div class="container">
258
+
259
+ <!-- 登录界面 -->
260
+ <div id="loginSection">
261
+ <div class="logo">
262
+ <svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
263
+ <path d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z" fill="#4285F4"/>
264
+ <path d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z" fill="#34A853"/>
265
+ <path d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z" fill="#FBBC05"/>
266
+ <path d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z" fill="#EA4335"/>
267
+ </svg>
268
+ </div>
269
+ <h1>Google OAuth 认证</h1>
270
+ <div class="login-form">
271
+ <div class="form-group">
272
+ <input type="password" id="password" placeholder="请输入访问密码" onkeypress="handlePasswordEnter(event)" />
273
+ </div>
274
+ <button class="btn" onclick="login()">登录</button>
275
+ </div>
276
+ </div>
277
+
278
+ <!-- 主界面 -->
279
+ <div id="mainSection" class="hidden">
280
+ <h1>Google OAuth 认证</h1>
281
+
282
+ <!-- API 自动启用说明 -->
283
+ <div class="status info">
284
+ <h4>✨ 自动化优化</h4>
285
+ <p style="margin: 15px 0;">系统现在会在认证成功后自动为您的项目启用以下必需的API服务:</p>
286
+ <ul style="margin: 15px 0; padding-left: 20px;">
287
+ <li><strong>Gemini Cloud Assist API</strong></li>
288
+ <li><strong>Gemini for Google Cloud API</strong></li>
289
+ </ul>
290
+ <p style="margin: 15px 0; color: #0c5460;"><strong>说明:</strong>无需手动启用API,系统会自动处理这些配置步骤。</p>
291
+ </div>
292
+
293
+ <!-- 折叠式 Project ID 输入框 -->
294
+ <div class="form-group">
295
+ <div style="cursor: pointer; user-select: none; padding: 12px; border: 2px solid #e1e5e9; border-radius: 8px; background: #f8f9fa; display: flex; justify-content: space-between; align-items: center;" onclick="toggleProjectIdSection()">
296
+ <span style="font-weight: 600; color: #555;">📁 高级选项:Google Cloud Project ID (不用管,直接点击获取链接即可)</span>
297
+ <span id="projectIdToggleIcon" style="font-size: 14px; color: #666; transition: transform 0.3s ease;">▶</span>
298
+ </div>
299
+ <div id="projectIdSection" style="display: none; margin-top: 15px; padding: 15px; border: 2px solid #e1e5e9; border-top: none; border-radius: 0 0 8px 8px; background: #ffffff;">
300
+ <label for="projectId" style="display: block; margin-bottom: 8px; font-weight: 600; color: #555; font-size: 14px;">Google Cloud Project ID (可选):</label>
301
+ <input type="text" id="projectId" style="width: 100%; padding: 12px 16px; border: 2px solid #e1e5e9; border-radius: 8px; font-size: 16px; box-sizing: border-box; transition: all 0.3s ease;" placeholder="留空将尝试自动检测,或手动输入项目ID" />
302
+ <small style="color: #666; font-size: 12px; margin-top: 5px; display: block;">
303
+ 💡 提示:如果你不懂这是什么,可以留空此字段让系统自动检测项目ID
304
+ </small>
305
+ </div>
306
+ </div>
307
+
308
+ <button class="btn" id="getAuthBtn" onclick="startAuth()">获取认证链接</button>
309
+
310
+ <div id="authUrlSection" class="hidden">
311
+ <h3>步骤一:认证链接</h3>
312
+ <div class="auth-url">
313
+ <a id="authUrl" href="#" target="_blank">点击此链接进行 OAuth 认证</a>
314
+ </div>
315
+
316
+ <div class="steps">
317
+ <h4>认证步骤:</h4>
318
+ <ol>
319
+ <li>点击上方认证链接,在新窗口中完成 Google 登录</li>
320
+ <li>授权应用访问您的 Google Cloud 项目</li>
321
+ <li>看到 "OAuth authentication successful!" 页面后,关闭该窗口</li>
322
+ <li>返回本页面,点击下方"获取认证文件"按钮</li>
323
+ </ol>
324
+
325
+ <div class="status warning" style="margin-top: 20px;">
326
+ <h4>⚠️ 注意</h4>
327
+ <p>当回源时,网页访问会显示报错。这时请手动将浏览器地址栏的 <code>localhost</code> 修改为 <code>gcli-auth.sukaka.top</code>,然后重新访问即可。</p>
328
+ </div>
329
+ </div>
330
+
331
+ <!-- 快捷回调URL输入选项 -->
332
+ <div style="margin: 20px 0; padding: 15px; border: 2px solid #e8f4fd; border-radius: 8px; background: #f8fcff;">
333
+ <div style="cursor: pointer; user-select: none; display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px;" onclick="toggleCallbackUrlSection()">
334
+ <span style="font-weight: 600; color: #0066cc; font-size: 16px;">🚀 无法回源?试试快捷方式</span>
335
+ <span id="callbackUrlToggleIcon" style="font-size: 14px; color: #666; transition: transform 0.3s ease;">▼</span>
336
+ </div>
337
+ <div id="callbackUrlSection" style="display: none;">
338
+ <div style="background: #fff3cd; border: 1px solid #ffeaa7; border-radius: 6px; padding: 12px; margin-bottom: 12px;">
339
+ <div style="color: #856404; font-size: 14px; font-weight: bold; margin-bottom: 6px;">📚 适用场景:</div>
340
+ <ul style="color: #856404; font-size: 13px; margin: 0; padding-left: 18px; line-height: 1.5;">
341
+ <li>云服务器、VPS等非本地环境</li>
342
+ <li>防火墙阻止了回调端口访问</li>
343
+ <li>网络环境无法正常回源</li>
344
+ <li>Docker容器端口映射问题</li>
345
+ </ul>
346
+ </div>
347
+ <div style="color: #666; font-size: 13px; margin-bottom: 12px; line-height: 1.6;">
348
+ <strong style="color: #0066cc;">🔍 什么是回调URL?</strong><br>
349
+ 完成Google OAuth授权后,浏览器地址栏显示的完整URL,通常看起来像这样:<br>
350
+ <code style="background: #f1f3f4; padding: 2px 6px; border-radius: 3px; font-size: 12px; word-break: break-all; display: block; margin-top: 4px;">
351
+ http://localhost:8080/?state=abc123...&code=4/0AVMBsJ...&scope=email%20profile...
352
+ </code>
353
+ </div>
354
+ <div style="background: #e7f3ff; border: 1px solid #b3d9ff; border-radius: 6px; padding: 10px; margin-bottom: 12px;">
355
+ <div style="color: #0066cc; font-size: 13px; font-weight: bold; margin-bottom: 4px;">📋 使用步骤:</div>
356
+ <ol style="color: #0066cc; font-size: 12px; margin: 0; padding-left: 18px; line-height: 1.4;">
357
+ <li>点击上方认证链接,完成Google授权</li>
358
+ <li>授权成功后,复制浏览器地址栏的<strong>完整URL</strong></li>
359
+ <li>粘贴到下方输入框,点击获取凭证即可</li>
360
+ </ol>
361
+ </div>
362
+ <div style="margin-bottom: 12px;">
363
+ <input type="url" id="callbackUrlInput" placeholder="粘贴完整的回调URL,例如:http://localhost:8080/?state=xxx&code=xxx&scope=xxx..."
364
+ style="width: 100%; padding: 10px; border: 2px solid #ddd; border-radius: 4px; font-size: 13px; box-sizing: border-box;">
365
+ </div>
366
+ <button class="btn" style="background: #28a745; border-color: #28a745;" onclick="processCallbackUrl()">
367
+ 从回调URL获取凭证
368
+ </button>
369
+ </div>
370
+ </div>
371
+
372
+ <button class="btn secondary" id="getCredsBtn" onclick="getCredentials()">获取认证文件</button>
373
+ </div>
374
+
375
+ <div id="credentialsSection" class="hidden">
376
+ <h3>步骤二:认证成功!</h3>
377
+ <div class="status success">
378
+ <strong>✅ OAuth 认证成功完成!</strong><br>
379
+ 认证文件已生成,您可以下载保存或复制使用。
380
+ </div>
381
+
382
+ <div class="credentials" id="credentialsContent"></div>
383
+
384
+ <button class="btn download download-animation" id="downloadBtn" onclick="downloadCredentials()">
385
+ 📥 下载认证文件
386
+ </button>
387
+
388
+ <button class="btn" onclick="resetForm()">认证其他项目</button>
389
+ </div>
390
+
391
+ <div id="statusSection"></div>
392
+
393
+ <div class="footer">
394
+ <p>🔒 您的认证信息将安全保存,仅用于访问指定的 Google Cloud 项目</p>
395
+
396
+ <!-- 项目信息 -->
397
+ <div style="background-color: #f8f9fa; border: 1px solid #dee2e6; border-radius: 8px; padding: 15px; margin-top: 20px; text-align: center; border-left: 4px solid #17a2b8;">
398
+ <p style="margin: 6px 0; font-size: 15px; color: #495057;">GitHub: <a href="https://github.com/su-kaka/gcli2api" target="_blank" style="color: #17a2b8; text-decoration: none; font-weight: 500;">https://github.com/su-kaka/gcli2api</a></p>
399
+ <p style="margin: 6px 0; font-size: 15px; color: #dc3545; font-weight: 500;">⚠️ 禁止商业用途和倒卖 - 仅供学习使用 ⚠️</p>
400
+ </div>
401
+ </div>
402
+ </div>
403
+ </div>
404
+
405
+ <script>
406
+ let currentProjectId = '';
407
+ let authInProgress = false;
408
+ let authToken = '';
409
+ let currentCredentials = null;
410
+
411
+ function showStatus(message, type = 'info') {
412
+ const statusSection = document.getElementById('statusSection');
413
+ if (statusSection) {
414
+ statusSection.innerHTML = `<div class="status ${type}">${message}</div>`;
415
+
416
+ // 自动滚动到状态消息
417
+ statusSection.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
418
+ }
419
+ }
420
+
421
+ // 登录相关函数
422
+ async function login() {
423
+ const password = document.getElementById('password').value;
424
+
425
+ if (!password) {
426
+ showStatus('请输入密码', 'error');
427
+ return;
428
+ }
429
+
430
+ try {
431
+ const response = await fetch('/auth/login', {
432
+ method: 'POST',
433
+ headers: {
434
+ 'Content-Type': 'application/json'
435
+ },
436
+ body: JSON.stringify({ password: password })
437
+ });
438
+
439
+ const data = await response.json();
440
+
441
+ if (response.ok) {
442
+ authToken = data.token;
443
+ document.getElementById('loginSection').classList.add('hidden');
444
+ document.getElementById('mainSection').classList.remove('hidden');
445
+ showStatus('登录成功', 'success');
446
+ } else {
447
+ showStatus(`登录失败: ${data.detail || data.error || '密码错误'}`, 'error');
448
+ }
449
+ } catch (error) {
450
+ showStatus(`网络错误: ${error.message}`, 'error');
451
+ }
452
+ }
453
+
454
+ function handlePasswordEnter(event) {
455
+ if (event.key === 'Enter') {
456
+ login();
457
+ }
458
+ }
459
+
460
+ // 获取认证头
461
+ function getAuthHeaders() {
462
+ return {
463
+ 'Content-Type': 'application/json',
464
+ 'Authorization': `Bearer ${authToken}`
465
+ };
466
+ }
467
+
468
+ async function startAuth() {
469
+ const projectId = document.getElementById('projectId').value.trim();
470
+ // 项目ID现在是可选的
471
+ currentProjectId = projectId || null;
472
+
473
+ const btn = document.getElementById('getAuthBtn');
474
+ btn.disabled = true;
475
+ btn.textContent = '正在生成认证链接...';
476
+
477
+ try {
478
+ const requestBody = {};
479
+ if (projectId) {
480
+ requestBody.project_id = projectId;
481
+ showStatus('使用指定的项目ID生成认证链接...', 'info');
482
+ } else {
483
+ showStatus('将尝试自动检测项目ID,正在生成认证链接...', 'info');
484
+ }
485
+
486
+ const response = await fetch('/auth/start', {
487
+ method: 'POST',
488
+ headers: getAuthHeaders(),
489
+ body: JSON.stringify(requestBody)
490
+ });
491
+
492
+ const data = await response.json();
493
+
494
+ if (response.ok) {
495
+ document.getElementById('authUrl').href = data.auth_url;
496
+ document.getElementById('authUrl').textContent = data.auth_url;
497
+ document.getElementById('authUrlSection').classList.remove('hidden');
498
+
499
+ if (data.auto_project_detection) {
500
+ showStatus('认证链接已生成(将在认证完成后自动检测项目ID),请点击链接完成 OAuth 授权', 'info');
501
+ } else {
502
+ showStatus(`认证链接已生成(项目ID: ${data.detected_project_id}),请点击链接完成 OAuth 授权`, 'info');
503
+ }
504
+ authInProgress = true;
505
+ } else {
506
+ showStatus(`生成认证链接失败: ${data.error || '未知错误'}`, 'error');
507
+ }
508
+ } catch (error) {
509
+ showStatus(`网络错误: ${error.message}`, 'error');
510
+ } finally {
511
+ btn.disabled = false;
512
+ btn.textContent = '获取认证链接';
513
+ }
514
+ }
515
+
516
+ async function getCredentials() {
517
+ if (!authInProgress) {
518
+ showStatus('请先获取认证链接并完成 OAuth 授权', 'error');
519
+ return;
520
+ }
521
+
522
+ const btn = document.getElementById('getCredsBtn');
523
+ btn.disabled = true;
524
+ btn.textContent = '等待 OAuth 回调...';
525
+
526
+ try {
527
+ showStatus('正在等待 OAuth 回调,请确保已完成浏览器中的授权...', 'info');
528
+
529
+ const requestBody = {};
530
+ if (currentProjectId) {
531
+ requestBody.project_id = currentProjectId;
532
+ }
533
+
534
+ const response = await fetch('/auth/callback', {
535
+ method: 'POST',
536
+ headers: getAuthHeaders(),
537
+ body: JSON.stringify(requestBody)
538
+ });
539
+
540
+ const data = await response.json();
541
+
542
+ if (response.ok) {
543
+ currentCredentials = data.credentials;
544
+ document.getElementById('credentialsContent').textContent = JSON.stringify(data.credentials, null, 2);
545
+ document.getElementById('credentialsSection').classList.remove('hidden');
546
+
547
+ if (data.auto_detected_project) {
548
+ showStatus(`认证成功!项目ID已自动检测为: ${data.credentials.project_id},文件已保存到服务器: ${data.file_path}`, 'success');
549
+ } else {
550
+ showStatus(`认证成功!文件已保存到服务器: ${data.file_path}`, 'success');
551
+ }
552
+
553
+ authInProgress = false;
554
+
555
+ // 隐藏前面的步骤,突出显示成功结果
556
+ document.getElementById('authUrlSection').style.opacity = '0.6';
557
+ } else {
558
+ // 检查是否需要项目选择
559
+ if (data.requires_project_selection && data.available_projects) {
560
+ let projectOptions = "请选择一个项目:\n\n";
561
+ data.available_projects.forEach((project, index) => {
562
+ projectOptions += `${index + 1}. ${project.name} (${project.projectId})\n`;
563
+ });
564
+ projectOptions += `\n请输入序号 (1-${data.available_projects.length}):`;
565
+
566
+ const selection = prompt(projectOptions);
567
+ const projectIndex = parseInt(selection) - 1;
568
+
569
+ if (projectIndex >= 0 && projectIndex < data.available_projects.length) {
570
+ const selectedProject = data.available_projects[projectIndex];
571
+ currentProjectId = selectedProject.projectId;
572
+ btn.textContent = '重新尝试获取认证文件';
573
+ showStatus(`使用选择的项目 ${selectedProject.name} (${selectedProject.projectId}) 重新尝试...`, 'info');
574
+ setTimeout(() => getCredentials(), 1000);
575
+ return;
576
+ } else {
577
+ showStatus('无效的选择,请重新开始认证', 'error');
578
+ }
579
+ }
580
+ // 检查是否需要手动输入项目ID
581
+ else if (data.requires_manual_project_id) {
582
+ const userProjectId = prompt('无法自动检测项目ID,请手动输入您的Google Cloud项目ID:');
583
+ if (userProjectId && userProjectId.trim()) {
584
+ // 重新尝试,使用用户输入的项目ID
585
+ currentProjectId = userProjectId.trim();
586
+ btn.textContent = '重新尝试获取认证文件';
587
+ showStatus('使用手动输入的项目ID重新尝试...', 'info');
588
+ setTimeout(() => getCredentials(), 1000);
589
+ return;
590
+ } else {
591
+ showStatus('需要项目ID才能完成认证,请重新开始并输入正确的项目ID', 'error');
592
+ }
593
+ } else {
594
+ showStatus(`认证失败: ${data.error || '获取认证文件失败'}`, 'error');
595
+ if (data.error && data.error.includes('未接收到授权回调')) {
596
+ showStatus('提示:请确保已完成浏览器中的 OAuth 认证,并看到了"OAuth authentication successful"页面', 'warning');
597
+ }
598
+ }
599
+ }
600
+ } catch (error) {
601
+ showStatus(`网络错误: ${error.message}`, 'error');
602
+ } finally {
603
+ btn.disabled = false;
604
+ btn.textContent = '获取认证文件';
605
+ }
606
+ }
607
+
608
+ function downloadCredentials() {
609
+ if (!currentCredentials) {
610
+ showStatus('没有可下载的认证文件', 'error');
611
+ return;
612
+ }
613
+
614
+ const filename = `${currentProjectId}-credentials.json`;
615
+ const content = JSON.stringify(currentCredentials, null, 2);
616
+
617
+ const blob = new Blob([content], { type: 'application/json' });
618
+ const url = window.URL.createObjectURL(blob);
619
+ const a = document.createElement('a');
620
+ a.style.display = 'none';
621
+ a.href = url;
622
+ a.download = filename;
623
+ document.body.appendChild(a);
624
+ a.click();
625
+ window.URL.revokeObjectURL(url);
626
+ document.body.removeChild(a);
627
+
628
+ showStatus(`认证文件已下载: ${filename}`, 'success');
629
+ }
630
+
631
+ function resetForm() {
632
+ // 重置表单
633
+ document.getElementById('projectId').value = '';
634
+ document.getElementById('authUrlSection').classList.add('hidden');
635
+ document.getElementById('credentialsSection').classList.add('hidden');
636
+ document.getElementById('authUrlSection').style.opacity = '1';
637
+
638
+ // 重置状态
639
+ currentProjectId = '';
640
+ authInProgress = false;
641
+ currentCredentials = null;
642
+
643
+ showStatus('请输入新的 Project ID 开始认证', 'info');
644
+ }
645
+
646
+ // Project ID 折叠切换函数
647
+ function toggleProjectIdSection() {
648
+ const section = document.getElementById('projectIdSection');
649
+ const icon = document.getElementById('projectIdToggleIcon');
650
+
651
+ if (section.style.display === 'none') {
652
+ section.style.display = 'block';
653
+ icon.style.transform = 'rotate(90deg)';
654
+ icon.textContent = '▼';
655
+ } else {
656
+ section.style.display = 'none';
657
+ icon.style.transform = 'rotate(0deg)';
658
+ icon.textContent = '▶';
659
+ }
660
+ }
661
+
662
+ // 回调URL输入区域折叠切换函数
663
+ function toggleCallbackUrlSection() {
664
+ const section = document.getElementById('callbackUrlSection');
665
+ const icon = document.getElementById('callbackUrlToggleIcon');
666
+
667
+ if (section.style.display === 'none') {
668
+ section.style.display = 'block';
669
+ icon.style.transform = 'rotate(180deg)';
670
+ icon.textContent = '▲';
671
+ } else {
672
+ section.style.display = 'none';
673
+ icon.style.transform = 'rotate(0deg)';
674
+ icon.textContent = '▼';
675
+ }
676
+ }
677
+
678
+ // 处理回调URL的函数
679
+ async function processCallbackUrl() {
680
+ const callbackUrlInput = document.getElementById('callbackUrlInput');
681
+ const callbackUrl = callbackUrlInput.value.trim();
682
+
683
+ if (!callbackUrl) {
684
+ showStatus('请输入回调URL', 'error');
685
+ return;
686
+ }
687
+
688
+ // 简单验证URL格式
689
+ if (!callbackUrl.startsWith('http://') && !callbackUrl.startsWith('https://')) {
690
+ showStatus('请输入有效的URL(以http://或https://开头)', 'error');
691
+ return;
692
+ }
693
+
694
+ // 检查是否包含必要参数
695
+ if (!callbackUrl.includes('code=') || !callbackUrl.includes('state=')) {
696
+ showStatus('❌ 这不是有效的回调URL!请确保:\n1. 已完成Google OAuth授权\n2. 复制的是浏览器地址栏的完整URL\n3. URL包含code和state参数', 'error');
697
+ return;
698
+ }
699
+
700
+ showStatus('正在从回调URL获取凭证...', 'info');
701
+
702
+ try {
703
+ // 获取当前项目ID设置(如果有的话)
704
+ const projectIdInput = document.getElementById('projectId');
705
+ const projectId = projectIdInput ? projectIdInput.value.trim() : null;
706
+
707
+ const response = await fetch('/auth/callback-url', {
708
+ method: 'POST',
709
+ headers: getAuthHeaders(),
710
+ body: JSON.stringify({
711
+ callback_url: callbackUrl,
712
+ project_id: projectId || null
713
+ })
714
+ });
715
+
716
+ const result = await response.json();
717
+
718
+ if (result.credentials) {
719
+ // 显示成功信息
720
+ showStatus(result.message || '从回调URL获取凭证成功!', 'success');
721
+
722
+ // 隐藏认证URL区域,显示凭证内容
723
+ document.getElementById('authUrlSection').classList.add('hidden');
724
+ document.getElementById('credentialsSection').classList.remove('hidden');
725
+
726
+ // 显示凭证内容
727
+ document.getElementById('credentialsContent').innerHTML =
728
+ '<pre>' + JSON.stringify(result.credentials, null, 2) + '</pre>';
729
+
730
+ // 设置全局变量供下载使用
731
+ window.currentCredentials = result.credentials;
732
+
733
+ // 清空输入框
734
+ callbackUrlInput.value = '';
735
+
736
+ } else if (result.requires_manual_project_id) {
737
+ showStatus('需要手动指定项目ID,请在高级选项中填入Google Cloud项目ID后重试', 'error');
738
+ } else if (result.requires_project_selection) {
739
+ let projectOptions = '\n\n可用项目:\n';
740
+ result.available_projects.forEach(project => {
741
+ projectOptions += `• ${project.name} (ID: ${project.projectId})\n`;
742
+ });
743
+ showStatus('检测到多个项目,请在高级选项中指定项目ID:' + projectOptions, 'error');
744
+ } else {
745
+ showStatus(result.error || '从回调URL获取凭证失败', 'error');
746
+ }
747
+ } catch (error) {
748
+ console.error('从回调URL获取凭证时出错:', error);
749
+ showStatus(`从回调URL获取凭证失败: ${error.message}`, 'error');
750
+ }
751
+ }
752
+
753
+ // 页面加载时的初始化
754
+ window.onload = function() {
755
+ showStatus('请输入密码登录以开始 OAuth 认证', 'info');
756
+
757
+ // 自动聚焦到密码输入框
758
+ document.getElementById('password').focus();
759
+ };
760
+ </script>
761
+ </body>
762
+ </html>