Skip to content

Commit 1ce1075

Browse files
committed
fix: update initial authentication index handling to use -1 for no active account
1 parent fd47fd0 commit 1ce1075

File tree

11 files changed

+78
-182
lines changed

11 files changed

+78
-182
lines changed

.env.example

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ RATE_LIMIT_MAX_ATTEMPTS=5
4040
RATE_LIMIT_WINDOW_MINUTES=15
4141

4242
# Initial authentication index to use on startup
43-
INITIAL_AUTH_INDEX=1
43+
INITIAL_AUTH_INDEX=0
4444

4545
# ===================================
4646
# UI Configuration

README.md

Lines changed: 22 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -48,47 +48,9 @@ The API server will be available at `http://localhost:7860`
4848

4949
### ☁ Cloud Deployment (Linux VPS)
5050

51-
For production deployment on a server (Linux VPS), you need to extract authentication credentials from a Windows machine first.
51+
For production deployment on a server (Linux VPS), you can now deploy directly using Docker without pre-extracting authentication credentials.
5252

53-
#### 📝 Step 1: Extract Authentication Credentials (on Windows)
54-
55-
1. Clone the repository on a Windows machine:
56-
57-
```powershell
58-
git clone https://github.com/iBenzene/AIStudioToAPI.git
59-
cd AIStudioToAPI
60-
```
61-
62-
2. Run the setup script:
63-
64-
```powershell
65-
npm run setup-auth
66-
```
67-
68-
This will:
69-
70-
- Download Camoufox browser automatically
71-
- Launch the browser and navigate to AI Studio automatically
72-
- Log in with your Google account
73-
- Save authentication credentials to `configs/auth/auth-N.json` (where N is an auto-incremented index starting from 0)
74-
75-
**How it works**: The script uses browser automation to capture your AI Studio session cookies and tokens, storing them securely in a JSON file. The authentication file is named with an auto-incremented index (auth-0.json, auth-1.json, etc.) to support multiple accounts. This allows the API to make authenticated requests to AI Studio without requiring interactive login on the server.
76-
77-
3. Locate the authentication file:
78-
79-
```powershell
80-
ls configs/auth/auth-*.json
81-
```
82-
83-
4. Copy the auth file to your server:
84-
85-
```powershell
86-
scp configs/auth/auth-*.json user@your-server:/path/to/deployment/configs/auth/
87-
```
88-
89-
5. You can now delete the cloned repository from your Windows machine.
90-
91-
#### 🚢 Step 2: Deploy on Server
53+
#### 🚢 Step 1: Deploy on Server
9254

9355
##### 🐋 Option 1: Docker Command
9456

@@ -149,7 +111,25 @@ Stop the service:
149111
sudo docker compose down
150112
```
151113

152-
##### 🌐 Step 3 (Optional): Nginx Reverse Proxy
114+
#### 🔑 Step 2: Account Management
115+
116+
After deployment, you need to add Google accounts using one of these methods:
117+
118+
**Method 1: VNC-Based Login (Recommended)**
119+
120+
- Visit the homepage and click the "Add User" button
121+
- You'll be redirected to a VNC page with a browser instance
122+
- Log in to your Google account
123+
- The account will be automatically saved as `auth-N.json` (N starts from 0)
124+
125+
**Method 2: Upload Auth Files (Legacy)**
126+
127+
- Run `npm run setup-auth` on a Windows machine to generate auth files
128+
- Upload `auth-N.json` files (N starts from 0) to the mounted `/path/to/auth` directory
129+
130+
> **Note**: Environment variable-based auth injection is no longer supported.
131+
132+
#### 🌐 Step 3 (Optional): Nginx Reverse Proxy
153133

154134
If you need to access via a domain name or want unified management at the reverse proxy layer (e.g., configure HTTPS, load balancing, etc.), you can use Nginx.
155135

@@ -194,7 +174,7 @@ This endpoint is forwarded to the official Gemini API format endpoint.
194174

195175
| Variable | Description | Default |
196176
| :------------------------------ | :--------------------------------------------------------------------------------------------------- | :-------- |
197-
| `INITIAL_AUTH_INDEX` | Initial authentication index to use on startup. | `1` |
177+
| `INITIAL_AUTH_INDEX` | Initial authentication index to use on startup. | `0` |
198178
| `MAX_RETRIES` | Maximum number of retries for failed requests (only effective for fake streaming and non-streaming). | `3` |
199179
| `RETRY_DELAY` | Delay between retries in milliseconds. | `2000` |
200180
| `SWITCH_ON_USES` | Number of requests before automatically switching accounts (0 to disable). | `40` |

README_CN.md

Lines changed: 22 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -48,47 +48,9 @@ API 服务将在 `http://localhost:7860` 上运行。
4848

4949
### ☁ 云端部署(Linux VPS)
5050

51-
在生产环境中部署到服务器(Linux VPS)时,需要先从 Windows 机器中提取身份验证凭据
51+
在生产环境中部署到服务器(Linux VPS)时,现在可以直接使用 Docker 部署,无需预先提取身份验证凭据
5252

53-
#### 📝 步骤 1:提取身份验证凭据(在 Windows 上)
54-
55-
1. 在 Windows 机器上克隆仓库:
56-
57-
```powershell
58-
git clone https://github.com/iBenzene/AIStudioToAPI.git
59-
cd AIStudioToAPI
60-
```
61-
62-
2. 运行设置脚本:
63-
64-
```powershell
65-
npm run setup-auth
66-
```
67-
68-
这将:
69-
70-
- 自动下载 Camoufox 浏览器
71-
- 启动浏览器并自动导航到 AI Studio
72-
- 手动登录你的 Google 账号
73-
- 将身份验证凭据保存到 `configs/auth/auth-N.json`(其中 N 是从 0 开始自动递增的索引)
74-
75-
**工作原理**:脚本使用浏览器自动化技术捕获您的 AI Studio 会话 Cookie 和令牌,并将它们安全地存储在 JSON 文件中。认证文件使用自动递增的索引命名(auth-0.json、auth-1.json 等)以支持多个账号。这样 API 就可以在服务器上进行经过身份验证的请求,而无需交互式登录。
76-
77-
3. 找到身份验证文件:
78-
79-
```powershell
80-
ls configs/auth/auth-*.json
81-
```
82-
83-
4. 将认证文件复制到服务器:
84-
85-
```powershell
86-
scp configs/auth/auth-*.json user@your-server:/path/to/deployment/configs/auth/
87-
```
88-
89-
5. 现在可以从 Windows 机器中删除克隆的仓库了。
90-
91-
#### 🚢 步骤 2:部署到服务器
53+
#### 🚢 步骤 1:部署到服务器
9254

9355
##### 🐋 方式 1:Docker 命令
9456

@@ -149,7 +111,25 @@ sudo docker compose logs -f
149111
sudo docker compose down
150112
```
151113

152-
##### 🌐 步骤 3(可选):使用 Nginx 反向代理
114+
#### 🔑 步骤 2:账户管理
115+
116+
部署后,您需要使用以下方式之一添加 Google 账户:
117+
118+
**方法 1:VNC 登录(推荐)**
119+
120+
- 访问主页并点击"添加账户"按钮
121+
- 将跳转到 VNC 页面,显示浏览器实例
122+
- 登录您的 Google 账户
123+
- 账户将自动保存为 `auth-N.json`(N 从 0 开始)
124+
125+
**方法 2:上传认证文件(传统方式)**
126+
127+
- 在 Windows 机器上运行 `npm run setup-auth` 生成认证文件
128+
-`auth-N.json` 文件(N 从 0 开始)上传到挂载的 `/path/to/auth` 目录
129+
130+
> **注意**:不再支持通过环境变量注入认证信息。
131+
132+
#### 🌐 步骤 3(可选):使用 Nginx 反向代理
153133

154134
如果需要通过域名访问或希望在反向代理层统一管理(例如配置 HTTPS、负载均衡等),可以使用 Nginx。
155135

@@ -194,7 +174,7 @@ sudo docker compose down
194174

195175
| 变量名 | 描述 | 默认值 |
196176
| :------------------------------ | :--------------------------------------------------- | :-------- |
197-
| `INITIAL_AUTH_INDEX` | 启动时使用的初始身份验证索引。 | `1` |
177+
| `INITIAL_AUTH_INDEX` | 启动时使用的初始身份验证索引。 | `0` |
198178
| `MAX_RETRIES` | 请求失败后的最大重试次数(仅对假流式和非流式生效)。 | `3` |
199179
| `RETRY_DELAY` | 两次重试之间的间隔(毫秒)。 | `2000` |
200180
| `SWITCH_ON_USES` | 自动切换帐户前允许的请求次数(设为 0 禁用)。 | `40` |

main.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ const ProxyServerSystem = require("./src/core/ProxyServerSystem");
1717
* Initialize and start the server
1818
*/
1919
const initializeServer = async () => {
20-
const initialAuthIndex = parseInt(process.env.INITIAL_AUTH_INDEX, 10) || 1;
20+
const initialAuthIndex = parseInt(process.env.INITIAL_AUTH_INDEX, 10) || 0;
2121

2222
try {
2323
const serverSystem = new ProxyServerSystem();

scripts/auth/saveAuth.js

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ const ensureDirectoryExists = dirPath => {
2828

2929
/**
3030
* Gets the next available authentication file index from the 'configs/auth' directory.
31+
* Uses the same logic as CreateAuth.js: finds the first available index starting from 0.
3132
* @returns {number} - The next available index value.
3233
*/
3334
const getNextAuthIndex = () => {
@@ -38,20 +39,12 @@ const getNextAuthIndex = () => {
3839
return 0;
3940
}
4041

41-
const files = fs.readdirSync(directory);
42-
const authRegex = /^auth-(\d+)\.json$/;
43-
44-
let maxIndex = -1;
45-
files.forEach(file => {
46-
const match = file.match(authRegex);
47-
if (match) {
48-
const currentIndex = parseInt(match[1], 10);
49-
if (currentIndex > maxIndex) {
50-
maxIndex = currentIndex;
51-
}
52-
}
53-
});
54-
return maxIndex + 1;
42+
// Find the first non-existent index starting from 0
43+
let nextIndex = 0;
44+
while (fs.existsSync(path.join(directory, `auth-${nextIndex}.json`))) {
45+
nextIndex++;
46+
}
47+
return nextIndex;
5548
};
5649

5750
(async () => {

src/auth/AuthSwitcher.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ class AuthSwitcher {
103103
this.logger.info("==================================================");
104104

105105
const failedAccounts = [];
106-
// If no current account (currentAuthIndex=0), start from i=0 to try all accounts
106+
// If no current account (currentAuthIndex=-1), start from i=0 to try all accounts
107107
// If has current account, start from i=1 to skip current and try others
108108
const startOffset = hasCurrentAccount ? 1 : 0;
109109
const tryCount = hasCurrentAccount ? available.length - 1 : available.length;
@@ -166,7 +166,7 @@ class AuthSwitcher {
166166
failedAccounts.push(originalStartAccount);
167167

168168
// Throw fallback failure error with detailed information
169-
this.currentAuthIndex = 0;
169+
this.currentAuthIndex = -1;
170170
throw new Error(
171171
`Fallback failed reason: All accounts failed including fallback to #${originalStartAccount}. Failed accounts: [${failedAccounts.join(", ")}]`
172172
);
@@ -177,7 +177,7 @@ class AuthSwitcher {
177177
this.logger.error(
178178
`FATAL: All ${available.length} accounts failed! Failed accounts: [${failedAccounts.join(", ")}]`
179179
);
180-
this.currentAuthIndex = 0;
180+
this.currentAuthIndex = -1;
181181
throw new Error(
182182
`Switching to account failed: All ${available.length} available accounts failed to initialize. Failed accounts: [${failedAccounts.join(", ")}]`
183183
);

src/auth/CreateAuth.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,7 @@ class CreateAuth {
307307
fs.mkdirSync(configDir, { recursive: true });
308308
}
309309

310-
let nextAuthIndex = 1;
310+
let nextAuthIndex = 0;
311311
while (fs.existsSync(path.join(configDir, `auth-${nextAuthIndex}.json`))) {
312312
nextAuthIndex++;
313313
}

src/core/BrowserManager.js

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ class BrowserManager {
2424
this.context = null;
2525
this.page = null;
2626
// currentAuthIndex is the single source of truth for current account, accessed via getter/setter
27-
// 0 means no account is currently active
28-
this._currentAuthIndex = 0;
27+
// -1 means no account is currently active (invalid/error state)
28+
this._currentAuthIndex = -1;
2929
this.scriptFileName = "build.js";
3030

3131
// Added for background wakeup logic from new core
@@ -530,15 +530,15 @@ class BrowserManager {
530530
}
531531

532532
async launchOrSwitchContext(authIndex) {
533-
if (typeof authIndex !== "number" || authIndex <= 0) {
534-
this.logger.error(`[Browser] Invalid authIndex: ${authIndex}. authIndex must be > 0.`);
535-
this._currentAuthIndex = 0;
536-
throw new Error(`Invalid authIndex: ${authIndex}. Must be > 0.`);
533+
if (typeof authIndex !== "number" || authIndex < 0) {
534+
this.logger.error(`[Browser] Invalid authIndex: ${authIndex}. authIndex must be >= 0.`);
535+
this._currentAuthIndex = -1;
536+
throw new Error(`Invalid authIndex: ${authIndex}. Must be >= 0.`);
537537
}
538538
if (!this.browser) {
539539
this.logger.info("🚀 [Browser] Main browser instance not running, performing first-time launch...");
540540
if (!fs.existsSync(this.browserExecutablePath)) {
541-
this._currentAuthIndex = 0;
541+
this._currentAuthIndex = -1;
542542
throw new Error(`Browser executable not found at path: ${this.browserExecutablePath}`);
543543
}
544544
this.browser = await firefox.launch({
@@ -551,8 +551,8 @@ class BrowserManager {
551551
this.browser = null;
552552
this.context = null;
553553
this.page = null;
554-
this._currentAuthIndex = 0;
555-
this.logger.warn("[Browser] Reset currentAuthIndex to 0 due to unexpected disconnect.");
554+
this._currentAuthIndex = -1;
555+
this.logger.warn("[Browser] Reset currentAuthIndex to -1 due to unexpected disconnect.");
556556
});
557557
this.logger.info("✅ [Browser] Main browser instance successfully launched.");
558558
}
@@ -851,7 +851,7 @@ class BrowserManager {
851851
} catch (error) {
852852
this.logger.error(`❌ [Browser] Account ${authIndex} context initialization failed: ${error.message}`);
853853
await this.closeBrowser();
854-
this._currentAuthIndex = 0;
854+
this._currentAuthIndex = -1;
855855
throw error;
856856
}
857857
}
@@ -878,8 +878,8 @@ class BrowserManager {
878878
this.browser = null;
879879
this.context = null;
880880
this.page = null;
881-
this._currentAuthIndex = 0;
882-
this.logger.info("[Browser] Main browser instance closed, currentAuthIndex reset to 0.");
881+
this._currentAuthIndex = -1;
882+
this.logger.info("[Browser] Main browser instance closed, currentAuthIndex reset to -1.");
883883
}
884884
}
885885

src/core/RequestHandler.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -80,8 +80,8 @@ class RequestHandler {
8080
* Handle browser recovery when connection is lost
8181
*
8282
* Important: isSystemBusy flag management strategy:
83-
* - Direct recovery (recoveryAuthIndex > 0): We manually set and reset isSystemBusy
84-
* - Switch to next account (recoveryAuthIndex = 0): Let switchToNextAuth() manage isSystemBusy internally
83+
* - Direct recovery (recoveryAuthIndex >= 0): We manually set and reset isSystemBusy
84+
* - Switch to next account (recoveryAuthIndex = -1): Let switchToNextAuth() manage isSystemBusy internally
8585
* - This prevents the bug where isSystemBusy is set here, then switchToNextAuth() checks it and returns "already in progress"
8686
*
8787
* @returns {boolean} true if recovery successful, false otherwise
@@ -103,12 +103,12 @@ class RequestHandler {
103103
"❌ [System] Browser WebSocket connection disconnected! Possible process crash. Attempting recovery..."
104104
);
105105

106-
const recoveryAuthIndex = this.currentAuthIndex || 0;
106+
const recoveryAuthIndex = this.currentAuthIndex;
107107
let wasDirectRecovery = false;
108108
let recoverySuccess = false;
109109

110110
try {
111-
if (recoveryAuthIndex > 0) {
111+
if (recoveryAuthIndex >= 0) {
112112
// Direct recovery: we manage isSystemBusy ourselves
113113
wasDirectRecovery = true;
114114
this.authSwitcher.isSystemBusy = true;

0 commit comments

Comments
 (0)