antigravity-claude-proxy 1.2.16 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +165 -62
- package/package.json +16 -2
- package/public/app.js +198 -0
- package/public/css/src/input.css +491 -0
- package/public/css/style.css +1 -0
- package/public/favicon.svg +10 -0
- package/public/index.html +333 -0
- package/public/js/app-init.js +137 -0
- package/public/js/components/account-manager.js +211 -0
- package/public/js/components/claude-config.js +145 -0
- package/public/js/components/dashboard/charts.js +536 -0
- package/public/js/components/dashboard/filters.js +362 -0
- package/public/js/components/dashboard/stats.js +82 -0
- package/public/js/components/dashboard.js +220 -0
- package/public/js/components/logs-viewer.js +100 -0
- package/public/js/components/model-manager.js +47 -0
- package/public/js/components/models.js +36 -0
- package/public/js/components/server-config.js +252 -0
- package/public/js/config/constants.js +82 -0
- package/public/js/data-store.js +238 -0
- package/public/js/settings-store.js +58 -0
- package/public/js/store.js +549 -0
- package/public/js/utils/account-actions.js +199 -0
- package/public/js/utils/error-handler.js +145 -0
- package/public/js/utils/model-config.js +42 -0
- package/public/js/utils/validators.js +168 -0
- package/public/js/utils.js +69 -0
- package/public/views/accounts.html +269 -0
- package/public/views/dashboard.html +457 -0
- package/public/views/logs.html +97 -0
- package/public/views/models.html +271 -0
- package/public/views/settings.html +1023 -0
- package/src/account-manager/index.js +12 -0
- package/src/account-manager/rate-limits.js +3 -0
- package/src/account-manager/selection.js +3 -0
- package/src/account-manager/storage.js +10 -2
- package/src/auth/oauth.js +3 -2
- package/src/cloudcode/index.js +4 -3
- package/src/cloudcode/message-handler.js +10 -0
- package/src/cloudcode/model-api.js +72 -0
- package/src/cloudcode/sse-streamer.js +4 -4
- package/src/cloudcode/streaming-handler.js +11 -0
- package/src/config.js +86 -0
- package/src/constants.js +17 -9
- package/src/modules/usage-stats.js +205 -0
- package/src/server.js +109 -31
- package/src/utils/claude-config.js +111 -0
- package/src/utils/logger.js +43 -12
- package/src/utils/retry.js +161 -0
- package/src/webui/index.js +598 -0
package/README.md
CHANGED
|
@@ -59,75 +59,54 @@ npm start
|
|
|
59
59
|
|
|
60
60
|
## Quick Start
|
|
61
61
|
|
|
62
|
-
### 1.
|
|
63
|
-
|
|
64
|
-
You have two options:
|
|
65
|
-
|
|
66
|
-
**Option A: Use Antigravity (Single Account)**
|
|
67
|
-
|
|
68
|
-
If you have Antigravity installed and logged in, the proxy will automatically extract your token. No additional setup needed.
|
|
69
|
-
|
|
70
|
-
**Option B: Add Google Accounts via OAuth (Recommended for Multi-Account)**
|
|
71
|
-
|
|
72
|
-
Add one or more Google accounts for load balancing.
|
|
73
|
-
|
|
74
|
-
#### Desktop/Laptop (with browser)
|
|
62
|
+
### 1. Start the Proxy Server
|
|
75
63
|
|
|
76
64
|
```bash
|
|
77
65
|
# If installed via npm
|
|
78
|
-
antigravity-claude-proxy
|
|
66
|
+
antigravity-claude-proxy start
|
|
79
67
|
|
|
80
68
|
# If using npx
|
|
81
|
-
npx antigravity-claude-proxy@latest
|
|
69
|
+
npx antigravity-claude-proxy@latest start
|
|
82
70
|
|
|
83
71
|
# If cloned locally
|
|
84
|
-
npm
|
|
72
|
+
npm start
|
|
85
73
|
```
|
|
86
74
|
|
|
87
|
-
|
|
75
|
+
The server runs on `http://localhost:8080` by default.
|
|
88
76
|
|
|
89
|
-
|
|
77
|
+
### 2. Link Account(s)
|
|
90
78
|
|
|
91
|
-
|
|
92
|
-
# If installed via npm
|
|
93
|
-
antigravity-claude-proxy accounts add --no-browser
|
|
79
|
+
Choose one of the following methods to authorize the proxy:
|
|
94
80
|
|
|
95
|
-
|
|
96
|
-
npx antigravity-claude-proxy@latest accounts add -- --no-browser
|
|
81
|
+
#### **Method A: Web Dashboard (Recommended)**
|
|
97
82
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
83
|
+
1. With the proxy running, open `http://localhost:8080` in your browser.
|
|
84
|
+
2. Navigate to the **Accounts** tab and click **Add Account**.
|
|
85
|
+
3. Complete the Google OAuth authorization in the popup window.
|
|
101
86
|
|
|
102
|
-
|
|
87
|
+
#### **Method B: CLI (Desktop or Headless)**
|
|
103
88
|
|
|
104
|
-
|
|
89
|
+
If you prefer the terminal or are on a remote server:
|
|
105
90
|
|
|
106
91
|
```bash
|
|
107
|
-
#
|
|
108
|
-
antigravity-claude-proxy accounts
|
|
109
|
-
|
|
110
|
-
# Verify accounts are working
|
|
111
|
-
antigravity-claude-proxy accounts verify
|
|
92
|
+
# Desktop (opens browser)
|
|
93
|
+
antigravity-claude-proxy accounts add
|
|
112
94
|
|
|
113
|
-
#
|
|
114
|
-
antigravity-claude-proxy accounts
|
|
95
|
+
# Headless (Docker/SSH)
|
|
96
|
+
antigravity-claude-proxy accounts add --no-browser
|
|
115
97
|
```
|
|
116
98
|
|
|
117
|
-
|
|
99
|
+
> For full CLI account management options, run `antigravity-claude-proxy accounts --help`.
|
|
118
100
|
|
|
119
|
-
|
|
120
|
-
# If installed via npm
|
|
121
|
-
antigravity-claude-proxy start
|
|
101
|
+
#### **Method C: Automatic (Antigravity Users)**
|
|
122
102
|
|
|
123
|
-
|
|
124
|
-
npx antigravity-claude-proxy@latest start
|
|
103
|
+
If you have the **Antigravity** app installed and logged in, the proxy will automatically detect your local session. No additional setup is required.
|
|
125
104
|
|
|
126
|
-
|
|
127
|
-
npm start
|
|
128
|
-
```
|
|
105
|
+
To use a custom port:
|
|
129
106
|
|
|
130
|
-
|
|
107
|
+
```bash
|
|
108
|
+
PORT=3001 antigravity-claude-proxy start
|
|
109
|
+
```
|
|
131
110
|
|
|
132
111
|
### 3. Verify It's Working
|
|
133
112
|
|
|
@@ -145,6 +124,18 @@ curl "http://localhost:8080/account-limits?format=table"
|
|
|
145
124
|
|
|
146
125
|
### Configure Claude Code
|
|
147
126
|
|
|
127
|
+
You can configure these settings in two ways:
|
|
128
|
+
|
|
129
|
+
#### **Via Web Console (Recommended)**
|
|
130
|
+
|
|
131
|
+
1. Open the WebUI at `http://localhost:8080`.
|
|
132
|
+
2. Go to **Settings** → **Claude CLI**.
|
|
133
|
+
3. Select your preferred models and click **Apply to Claude CLI**.
|
|
134
|
+
|
|
135
|
+
> [!TIP] > **Configuration Precedence**: System environment variables (set in shell profile like `.zshrc`) take precedence over the `settings.json` file. If you use the Web Console to manage settings, ensure you haven't manually exported conflicting variables in your terminal.
|
|
136
|
+
|
|
137
|
+
#### **Manual Configuration**
|
|
138
|
+
|
|
148
139
|
Create or edit the Claude Code settings file:
|
|
149
140
|
|
|
150
141
|
**macOS:** `~/.claude/settings.json`
|
|
@@ -261,18 +252,18 @@ Then run `claude` for official API or `claude-antigravity` for this proxy.
|
|
|
261
252
|
|
|
262
253
|
### Claude Models
|
|
263
254
|
|
|
264
|
-
| Model ID
|
|
265
|
-
|
|
255
|
+
| Model ID | Description |
|
|
256
|
+
| ---------------------------- | ---------------------------------------- |
|
|
266
257
|
| `claude-sonnet-4-5-thinking` | Claude Sonnet 4.5 with extended thinking |
|
|
267
|
-
| `claude-opus-4-5-thinking`
|
|
268
|
-
| `claude-sonnet-4-5`
|
|
258
|
+
| `claude-opus-4-5-thinking` | Claude Opus 4.5 with extended thinking |
|
|
259
|
+
| `claude-sonnet-4-5` | Claude Sonnet 4.5 without thinking |
|
|
269
260
|
|
|
270
261
|
### Gemini Models
|
|
271
262
|
|
|
272
|
-
| Model ID
|
|
273
|
-
|
|
274
|
-
| `gemini-3-flash`
|
|
275
|
-
| `gemini-3-pro-low`
|
|
263
|
+
| Model ID | Description |
|
|
264
|
+
| ------------------- | ------------------------------- |
|
|
265
|
+
| `gemini-3-flash` | Gemini 3 Flash with thinking |
|
|
266
|
+
| `gemini-3-pro-low` | Gemini 3 Pro Low with thinking |
|
|
276
267
|
| `gemini-3-pro-high` | Gemini 3 Pro High with thinking |
|
|
277
268
|
|
|
278
269
|
Gemini models include full thinking support with `thoughtSignature` handling for multi-turn conversations.
|
|
@@ -289,23 +280,74 @@ When you add multiple accounts, the proxy automatically:
|
|
|
289
280
|
- **Invalid account detection**: Accounts needing re-authentication are marked and skipped
|
|
290
281
|
- **Prompt caching support**: Stable session IDs enable cache hits across conversation turns
|
|
291
282
|
|
|
292
|
-
Check account status anytime:
|
|
283
|
+
Check account status, subscription tiers, and quota anytime:
|
|
293
284
|
|
|
294
285
|
```bash
|
|
286
|
+
# Web UI: http://localhost:8080/ (Accounts tab - shows tier badges and quota progress)
|
|
287
|
+
# CLI Table:
|
|
295
288
|
curl "http://localhost:8080/account-limits?format=table"
|
|
296
289
|
```
|
|
297
290
|
|
|
291
|
+
#### CLI Management Reference
|
|
292
|
+
|
|
293
|
+
If you prefer using the terminal for management:
|
|
294
|
+
|
|
295
|
+
```bash
|
|
296
|
+
# List all accounts
|
|
297
|
+
antigravity-claude-proxy accounts list
|
|
298
|
+
|
|
299
|
+
# Verify account health
|
|
300
|
+
antigravity-claude-proxy accounts verify
|
|
301
|
+
|
|
302
|
+
# Interactive CLI menu
|
|
303
|
+
antigravity-claude-proxy accounts
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
---
|
|
307
|
+
|
|
308
|
+
## Web Management Console
|
|
309
|
+
|
|
310
|
+
The proxy includes a built-in, modern web interface for real-time monitoring and configuration. Access the console at: `http://localhost:8080` (default port).
|
|
311
|
+
|
|
312
|
+

|
|
313
|
+
|
|
314
|
+
### Key Features
|
|
315
|
+
|
|
316
|
+
- **Real-time Dashboard**: Monitor request volume, active accounts, model health, and subscription tier distribution.
|
|
317
|
+
- **Visual Model Quota**: Track per-model usage and next reset times with color-coded progress indicators.
|
|
318
|
+
- **Account Management**: Add/remove Google accounts via OAuth, view subscription tiers (Free/Pro/Ultra) and quota status at a glance.
|
|
319
|
+
- **Claude CLI Configuration**: Edit your `~/.claude/settings.json` directly from the browser.
|
|
320
|
+
- **Live Logs**: Stream server logs with level-based filtering and search.
|
|
321
|
+
- **Advanced Tuning**: Configure retries, timeouts, and debug mode on the fly.
|
|
322
|
+
- **Bilingual Interface**: Full support for English and Chinese (switch via Settings).
|
|
323
|
+
|
|
324
|
+
---
|
|
325
|
+
|
|
326
|
+
## Advanced Configuration
|
|
327
|
+
|
|
328
|
+
While most users can use the default settings, you can tune the proxy behavior via the **Settings → Server** tab in the WebUI or by creating a `config.json` file.
|
|
329
|
+
|
|
330
|
+
### Configurable Options
|
|
331
|
+
|
|
332
|
+
- **WebUI Password**: Secure your dashboard with `WEBUI_PASSWORD` env var or in config.
|
|
333
|
+
- **Custom Port**: Change the default `8080` port.
|
|
334
|
+
- **Retry Logic**: Configure `maxRetries`, `retryBaseMs`, and `retryMaxMs`.
|
|
335
|
+
- **Load Balancing**: Adjust `defaultCooldownMs` and `maxWaitBeforeErrorMs`.
|
|
336
|
+
- **Persistence**: Enable `persistTokenCache` to save OAuth sessions across restarts.
|
|
337
|
+
|
|
338
|
+
Refer to `config.example.json` for a complete list of fields and documentation.
|
|
339
|
+
|
|
298
340
|
---
|
|
299
341
|
|
|
300
342
|
## API Endpoints
|
|
301
343
|
|
|
302
|
-
| Endpoint
|
|
303
|
-
|
|
304
|
-
| `/health`
|
|
305
|
-
| `/account-limits` | GET
|
|
306
|
-
| `/v1/messages`
|
|
307
|
-
| `/v1/models`
|
|
308
|
-
| `/refresh-token`
|
|
344
|
+
| Endpoint | Method | Description |
|
|
345
|
+
| ----------------- | ------ | --------------------------------------------------------------------- |
|
|
346
|
+
| `/health` | GET | Health check |
|
|
347
|
+
| `/account-limits` | GET | Account status and quota limits (add `?format=table` for ASCII table) |
|
|
348
|
+
| `/v1/messages` | POST | Anthropic Messages API |
|
|
349
|
+
| `/v1/models` | GET | List available models |
|
|
350
|
+
| `/refresh-token` | POST | Force token refresh |
|
|
309
351
|
|
|
310
352
|
---
|
|
311
353
|
|
|
@@ -339,6 +381,7 @@ npm run test:caching # Prompt caching
|
|
|
339
381
|
### "Could not extract token from Antigravity"
|
|
340
382
|
|
|
341
383
|
If using single-account mode with Antigravity:
|
|
384
|
+
|
|
342
385
|
1. Make sure Antigravity app is installed and running
|
|
343
386
|
2. Ensure you're logged in to Antigravity
|
|
344
387
|
|
|
@@ -347,11 +390,13 @@ Or add accounts via OAuth instead: `antigravity-claude-proxy accounts add`
|
|
|
347
390
|
### 401 Authentication Errors
|
|
348
391
|
|
|
349
392
|
The token might have expired. Try:
|
|
393
|
+
|
|
350
394
|
```bash
|
|
351
395
|
curl -X POST http://localhost:8080/refresh-token
|
|
352
396
|
```
|
|
353
397
|
|
|
354
398
|
Or re-authenticate the account:
|
|
399
|
+
|
|
355
400
|
```bash
|
|
356
401
|
antigravity-claude-proxy accounts
|
|
357
402
|
```
|
|
@@ -363,6 +408,7 @@ With multiple accounts, the proxy automatically switches to the next available a
|
|
|
363
408
|
### Account Shows as "Invalid"
|
|
364
409
|
|
|
365
410
|
Re-authenticate the account:
|
|
411
|
+
|
|
366
412
|
```bash
|
|
367
413
|
antigravity-claude-proxy accounts
|
|
368
414
|
# Choose "Re-authenticate" for the invalid account
|
|
@@ -412,6 +458,63 @@ By using this software, you acknowledge and accept the following:
|
|
|
412
458
|
|
|
413
459
|
---
|
|
414
460
|
|
|
461
|
+
## Development
|
|
462
|
+
|
|
463
|
+
### For Developers & Contributors
|
|
464
|
+
|
|
465
|
+
This project uses a local Tailwind CSS build system. CSS is pre-compiled and included in the repository, so you can run the project immediately after cloning.
|
|
466
|
+
|
|
467
|
+
#### Quick Start
|
|
468
|
+
|
|
469
|
+
```bash
|
|
470
|
+
git clone https://github.com/badri-s2001/antigravity-claude-proxy.git
|
|
471
|
+
cd antigravity-claude-proxy
|
|
472
|
+
npm install # Automatically builds CSS via prepare hook
|
|
473
|
+
npm start # Start server (no rebuild needed)
|
|
474
|
+
```
|
|
475
|
+
|
|
476
|
+
#### Frontend Development
|
|
477
|
+
|
|
478
|
+
If you need to modify styles in `public/css/src/input.css`:
|
|
479
|
+
|
|
480
|
+
```bash
|
|
481
|
+
# Option 1: Build once
|
|
482
|
+
npm run build:css
|
|
483
|
+
|
|
484
|
+
# Option 2: Watch for changes (auto-rebuild)
|
|
485
|
+
npm run watch:css
|
|
486
|
+
|
|
487
|
+
# Option 3: Watch both CSS and server (recommended)
|
|
488
|
+
npm run dev:full
|
|
489
|
+
```
|
|
490
|
+
|
|
491
|
+
**File Structure:**
|
|
492
|
+
- `public/css/src/input.css` - Source CSS with Tailwind `@apply` directives (edit this)
|
|
493
|
+
- `public/css/style.css` - Compiled & minified CSS (auto-generated, don't edit)
|
|
494
|
+
- `tailwind.config.js` - Tailwind configuration
|
|
495
|
+
- `postcss.config.js` - PostCSS configuration
|
|
496
|
+
|
|
497
|
+
#### Backend-Only Development
|
|
498
|
+
|
|
499
|
+
If you're only working on backend code and don't need frontend dev tools:
|
|
500
|
+
|
|
501
|
+
```bash
|
|
502
|
+
npm install --production # Skip devDependencies (saves ~20MB)
|
|
503
|
+
npm start
|
|
504
|
+
```
|
|
505
|
+
|
|
506
|
+
**Note:** Pre-compiled CSS is committed to the repository, so you don't need to rebuild unless modifying styles.
|
|
507
|
+
|
|
508
|
+
#### Project Structure
|
|
509
|
+
|
|
510
|
+
See [CLAUDE.md](./CLAUDE.md) for detailed architecture documentation, including:
|
|
511
|
+
- Request flow and module organization
|
|
512
|
+
- Frontend architecture (Alpine.js + Tailwind)
|
|
513
|
+
- Service layer patterns (`ErrorHandler.withLoading`, `AccountActions`)
|
|
514
|
+
- Dashboard module documentation
|
|
515
|
+
|
|
516
|
+
---
|
|
517
|
+
|
|
415
518
|
## Credits
|
|
416
519
|
|
|
417
520
|
This project is based on insights and code from:
|
|
@@ -429,4 +532,4 @@ MIT
|
|
|
429
532
|
|
|
430
533
|
## Star History
|
|
431
534
|
|
|
432
|
-
[](https://www.star-history.com/#badrisnarayanan/antigravity-claude-proxy&type=date&legend=top-left)
|
|
535
|
+
[](https://www.star-history.com/#badrisnarayanan/antigravity-claude-proxy&type=date&legend=top-left)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "antigravity-claude-proxy",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.1",
|
|
4
4
|
"description": "Proxy server to use Antigravity's Claude models with Claude Code CLI",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -9,11 +9,16 @@
|
|
|
9
9
|
},
|
|
10
10
|
"files": [
|
|
11
11
|
"src",
|
|
12
|
-
"bin"
|
|
12
|
+
"bin",
|
|
13
|
+
"public"
|
|
13
14
|
],
|
|
14
15
|
"scripts": {
|
|
16
|
+
"build:css": "tailwindcss -i ./public/css/src/input.css -o ./public/css/style.css --minify",
|
|
17
|
+
"watch:css": "tailwindcss -i ./public/css/src/input.css -o ./public/css/style.css --watch",
|
|
18
|
+
"prepare": "npm run build:css",
|
|
15
19
|
"start": "node src/index.js",
|
|
16
20
|
"dev": "node --watch src/index.js",
|
|
21
|
+
"dev:full": "concurrently \"npm run watch:css\" \"npm run dev\"",
|
|
17
22
|
"accounts": "node src/cli/accounts.js",
|
|
18
23
|
"accounts:add": "node src/cli/accounts.js add",
|
|
19
24
|
"accounts:list": "node src/cli/accounts.js list",
|
|
@@ -52,8 +57,17 @@
|
|
|
52
57
|
"node": ">=18.0.0"
|
|
53
58
|
},
|
|
54
59
|
"dependencies": {
|
|
60
|
+
"async-mutex": "^0.5.0",
|
|
55
61
|
"better-sqlite3": "^12.5.0",
|
|
56
62
|
"cors": "^2.8.5",
|
|
57
63
|
"express": "^4.18.2"
|
|
64
|
+
},
|
|
65
|
+
"devDependencies": {
|
|
66
|
+
"@tailwindcss/forms": "^0.5.7",
|
|
67
|
+
"autoprefixer": "^10.4.16",
|
|
68
|
+
"concurrently": "^8.2.2",
|
|
69
|
+
"daisyui": "^4.12.14",
|
|
70
|
+
"postcss": "^8.4.32",
|
|
71
|
+
"tailwindcss": "^3.4.0"
|
|
58
72
|
}
|
|
59
73
|
}
|
package/public/app.js
ADDED
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Antigravity Console - Main Entry
|
|
3
|
+
*
|
|
4
|
+
* This file orchestrates Alpine.js initialization.
|
|
5
|
+
* Components are loaded via separate script files that register themselves
|
|
6
|
+
* to window.Components before this script runs.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
document.addEventListener('alpine:init', () => {
|
|
10
|
+
// Register Components (loaded from separate files via window.Components)
|
|
11
|
+
Alpine.data('dashboard', window.Components.dashboard);
|
|
12
|
+
Alpine.data('models', window.Components.models);
|
|
13
|
+
Alpine.data('accountManager', window.Components.accountManager);
|
|
14
|
+
Alpine.data('claudeConfig', window.Components.claudeConfig);
|
|
15
|
+
Alpine.data('logsViewer', window.Components.logsViewer);
|
|
16
|
+
|
|
17
|
+
// View Loader Directive
|
|
18
|
+
Alpine.directive('load-view', (el, { expression }, { evaluate }) => {
|
|
19
|
+
if (!window.viewCache) window.viewCache = new Map();
|
|
20
|
+
|
|
21
|
+
// Evaluate the expression to get the actual view name (removes quotes)
|
|
22
|
+
const viewName = evaluate(expression);
|
|
23
|
+
|
|
24
|
+
if (window.viewCache.has(viewName)) {
|
|
25
|
+
el.innerHTML = window.viewCache.get(viewName);
|
|
26
|
+
Alpine.initTree(el);
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
fetch(`views/${viewName}.html?t=${Date.now()}`)
|
|
31
|
+
.then(response => {
|
|
32
|
+
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
33
|
+
return response.text();
|
|
34
|
+
})
|
|
35
|
+
.then(html => {
|
|
36
|
+
// Update cache (optional, or remove if we want always-fresh)
|
|
37
|
+
// keeping cache for session performance, but initial load will now bypass browser cache
|
|
38
|
+
window.viewCache.set(viewName, html);
|
|
39
|
+
el.innerHTML = html;
|
|
40
|
+
Alpine.initTree(el);
|
|
41
|
+
})
|
|
42
|
+
.catch(err => {
|
|
43
|
+
console.error('Failed to load view:', viewName, err);
|
|
44
|
+
el.innerHTML = `<div class="p-4 border border-red-500/50 bg-red-500/10 rounded-lg text-red-400 font-mono text-sm">
|
|
45
|
+
Error loading view: ${viewName}<br>
|
|
46
|
+
<span class="text-xs opacity-75">${err.message}</span>
|
|
47
|
+
</div>`;
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
// Main App Controller
|
|
52
|
+
Alpine.data('app', () => ({
|
|
53
|
+
get connectionStatus() {
|
|
54
|
+
return Alpine.store('data')?.connectionStatus || 'connecting';
|
|
55
|
+
},
|
|
56
|
+
get loading() {
|
|
57
|
+
return Alpine.store('data')?.loading || false;
|
|
58
|
+
},
|
|
59
|
+
|
|
60
|
+
init() {
|
|
61
|
+
console.log('App controller initialized');
|
|
62
|
+
|
|
63
|
+
// Theme setup
|
|
64
|
+
document.documentElement.setAttribute('data-theme', 'black');
|
|
65
|
+
document.documentElement.classList.add('dark');
|
|
66
|
+
|
|
67
|
+
// Chart Defaults
|
|
68
|
+
if (typeof Chart !== 'undefined') {
|
|
69
|
+
Chart.defaults.color = window.utils.getThemeColor('--color-text-dim');
|
|
70
|
+
Chart.defaults.borderColor = window.utils.getThemeColor('--color-space-border');
|
|
71
|
+
Chart.defaults.font.family = '"JetBrains Mono", monospace';
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Start Data Polling
|
|
75
|
+
this.startAutoRefresh();
|
|
76
|
+
document.addEventListener('refresh-interval-changed', () => this.startAutoRefresh());
|
|
77
|
+
|
|
78
|
+
// Initial Fetch
|
|
79
|
+
Alpine.store('data').fetchData();
|
|
80
|
+
},
|
|
81
|
+
|
|
82
|
+
refreshTimer: null,
|
|
83
|
+
|
|
84
|
+
fetchData() {
|
|
85
|
+
Alpine.store('data').fetchData();
|
|
86
|
+
},
|
|
87
|
+
|
|
88
|
+
startAutoRefresh() {
|
|
89
|
+
if (this.refreshTimer) clearInterval(this.refreshTimer);
|
|
90
|
+
const interval = parseInt(Alpine.store('settings')?.refreshInterval || 60);
|
|
91
|
+
if (interval > 0) {
|
|
92
|
+
this.refreshTimer = setInterval(() => Alpine.store('data').fetchData(), interval * 1000);
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
|
|
96
|
+
t(key) {
|
|
97
|
+
return Alpine.store('global')?.t(key) || key;
|
|
98
|
+
},
|
|
99
|
+
|
|
100
|
+
async addAccountWeb(reAuthEmail = null) {
|
|
101
|
+
const password = Alpine.store('global').webuiPassword;
|
|
102
|
+
try {
|
|
103
|
+
const urlPath = reAuthEmail
|
|
104
|
+
? `/api/auth/url?email=${encodeURIComponent(reAuthEmail)}`
|
|
105
|
+
: '/api/auth/url';
|
|
106
|
+
|
|
107
|
+
const { response, newPassword } = await window.utils.request(urlPath, {}, password);
|
|
108
|
+
if (newPassword) Alpine.store('global').webuiPassword = newPassword;
|
|
109
|
+
|
|
110
|
+
const data = await response.json();
|
|
111
|
+
|
|
112
|
+
if (data.status === 'ok') {
|
|
113
|
+
// Show info toast that OAuth is in progress
|
|
114
|
+
Alpine.store('global').showToast(Alpine.store('global').t('oauthInProgress'), 'info');
|
|
115
|
+
|
|
116
|
+
// Open OAuth window
|
|
117
|
+
const oauthWindow = window.open(data.url, 'google_oauth', 'width=600,height=700,scrollbars=yes');
|
|
118
|
+
|
|
119
|
+
// Poll for account changes instead of relying on postMessage
|
|
120
|
+
// (since OAuth callback is now on port 51121, not this server)
|
|
121
|
+
const initialAccountCount = Alpine.store('data').accounts.length;
|
|
122
|
+
let pollCount = 0;
|
|
123
|
+
const maxPolls = 60; // 2 minutes (2 second intervals)
|
|
124
|
+
let cancelled = false;
|
|
125
|
+
|
|
126
|
+
// Show progress modal
|
|
127
|
+
Alpine.store('global').oauthProgress = {
|
|
128
|
+
active: true,
|
|
129
|
+
current: 0,
|
|
130
|
+
max: maxPolls,
|
|
131
|
+
cancel: () => {
|
|
132
|
+
cancelled = true;
|
|
133
|
+
clearInterval(pollInterval);
|
|
134
|
+
Alpine.store('global').oauthProgress.active = false;
|
|
135
|
+
Alpine.store('global').showToast(Alpine.store('global').t('oauthCancelled'), 'info');
|
|
136
|
+
if (oauthWindow && !oauthWindow.closed) {
|
|
137
|
+
oauthWindow.close();
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
const pollInterval = setInterval(async () => {
|
|
143
|
+
if (cancelled) {
|
|
144
|
+
clearInterval(pollInterval);
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
pollCount++;
|
|
149
|
+
Alpine.store('global').oauthProgress.current = pollCount;
|
|
150
|
+
|
|
151
|
+
// Check if OAuth window was closed manually
|
|
152
|
+
if (oauthWindow && oauthWindow.closed && !cancelled) {
|
|
153
|
+
clearInterval(pollInterval);
|
|
154
|
+
Alpine.store('global').oauthProgress.active = false;
|
|
155
|
+
Alpine.store('global').showToast(Alpine.store('global').t('oauthWindowClosed'), 'warning');
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Refresh account list
|
|
160
|
+
await Alpine.store('data').fetchData();
|
|
161
|
+
|
|
162
|
+
// Check if new account was added
|
|
163
|
+
const currentAccountCount = Alpine.store('data').accounts.length;
|
|
164
|
+
if (currentAccountCount > initialAccountCount) {
|
|
165
|
+
clearInterval(pollInterval);
|
|
166
|
+
Alpine.store('global').oauthProgress.active = false;
|
|
167
|
+
|
|
168
|
+
const actionKey = reAuthEmail ? 'accountReauthSuccess' : 'accountAddedSuccess';
|
|
169
|
+
Alpine.store('global').showToast(
|
|
170
|
+
Alpine.store('global').t(actionKey),
|
|
171
|
+
'success'
|
|
172
|
+
);
|
|
173
|
+
document.getElementById('add_account_modal')?.close();
|
|
174
|
+
|
|
175
|
+
if (oauthWindow && !oauthWindow.closed) {
|
|
176
|
+
oauthWindow.close();
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Stop polling after max attempts
|
|
181
|
+
if (pollCount >= maxPolls) {
|
|
182
|
+
clearInterval(pollInterval);
|
|
183
|
+
Alpine.store('global').oauthProgress.active = false;
|
|
184
|
+
Alpine.store('global').showToast(
|
|
185
|
+
Alpine.store('global').t('oauthTimeout'),
|
|
186
|
+
'warning'
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
}, 2000); // Poll every 2 seconds
|
|
190
|
+
} else {
|
|
191
|
+
Alpine.store('global').showToast(data.error || Alpine.store('global').t('failedToGetAuthUrl'), 'error');
|
|
192
|
+
}
|
|
193
|
+
} catch (e) {
|
|
194
|
+
Alpine.store('global').showToast(Alpine.store('global').t('failedToStartOAuth') + ': ' + e.message, 'error');
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}));
|
|
198
|
+
});
|