Skip to content

Commit a308630

Browse files
committed
feat: add admin logout, 72h session expiry, /v1/stats endpoint, and UI fixes
1 parent 99ce5c9 commit a308630

File tree

8 files changed

+70
-12
lines changed

8 files changed

+70
-12
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ curl http://localhost:8080/v1/chat/completions \
151151
| `claude-sonnet-4.5` | claude-sonnet-4.5 |
152152
| `claude-haiku-4.5` | claude-haiku-4.5 |
153153
| `claude-opus-4.5` | claude-opus-4.5 |
154+
| `claude-opus-4.6` | claude-opus-4.6 |
154155
| `gpt-4o`, `gpt-4` | claude-sonnet-4-20250514 |
155156
| `gpt-3.5-turbo` | claude-sonnet-4-20250514 |
156157

@@ -209,6 +210,7 @@ Configure thinking mode in the Admin Panel under **Settings > Thinking Mode Sett
209210
|----------|-------------|
210211
| `GET /health` | Health check |
211212
| `GET /v1/models` | List models |
213+
| `GET /v1/stats` | Statistics |
212214
| `POST /v1/messages` | Claude Messages API |
213215
| `POST /v1/messages/count_tokens` | Token counting |
214216
| `POST /v1/chat/completions` | OpenAI Chat API |

README_CN.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ curl http://localhost:8080/v1/chat/completions \
151151
| `claude-sonnet-4.5` | claude-sonnet-4.5 |
152152
| `claude-haiku-4.5` | claude-haiku-4.5 |
153153
| `claude-opus-4.5` | claude-opus-4.5 |
154+
| `claude-opus-4.6` | claude-opus-4.6 |
154155
| `gpt-4o`, `gpt-4` | claude-sonnet-4-20250514 |
155156
| `gpt-3.5-turbo` | claude-sonnet-4-20250514 |
156157

@@ -209,6 +210,7 @@ curl http://localhost:8080/v1/messages \
209210
|-----|------|
210211
| `GET /health` | 健康检查 |
211212
| `GET /v1/models` | 模型列表 |
213+
| `GET /v1/stats` | 统计数据 |
212214
| `POST /v1/messages` | Claude Messages API |
213215
| `POST /v1/messages/count_tokens` | Token 计数 |
214216
| `POST /v1/chat/completions` | OpenAI Chat API |

auth/builderid.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ func StartBuilderIdLogin(region string) (*BuilderIdSession, error) {
4646

4747
// Step 1: 注册 OIDC 客户端
4848
regPayload := map[string]interface{}{
49-
"clientName": "Kiro API Proxy",
49+
"clientName": "Kiro",
5050
"clientType": "public",
5151
"scopes": scopes,
5252
"grantTypes": []string{"urn:ietf:params:oauth:grant-type:device_code", "refresh_token"},

config/config.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ type AccountInfo struct {
127127
}
128128

129129
// Version 当前版本号
130-
const Version = "1.0.0"
130+
const Version = "1.0.1"
131131

132132
var (
133133
cfg *Config

main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ func main() {
5454

5555
// 启动服务器
5656
addr := fmt.Sprintf("%s:%d", config.GetHost(), config.GetPort())
57-
log.Printf("Kiro API Proxy starting on http://%s", addr)
57+
log.Printf("Kiro-Go starting on http://%s", addr)
5858
log.Printf("Admin panel: http://%s/admin", addr)
5959
log.Printf("Claude API: http://%s/v1/messages", addr)
6060
log.Printf("OpenAI API: http://%s/v1/chat/completions", addr)

proxy/handler.go

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,16 +194,37 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
194194
case path == "/health" || path == "/":
195195
h.handleHealth(w, r)
196196

197+
// 统计端点(需要 API Key 鉴权)
198+
case path == "/v1/stats":
199+
if !h.validateApiKey(r) {
200+
w.Header().Set("Content-Type", "application/json; charset=utf-8")
201+
w.WriteHeader(401)
202+
json.NewEncoder(w).Encode(map[string]string{"error": "Invalid or missing API key"})
203+
return
204+
}
205+
h.handleStats(w, r)
206+
197207
default:
198208
http.Error(w, "Not Found", 404)
199209
}
200210
}
201211

202-
// handleHealth 健康检查
212+
// handleHealth 健康检查(不暴露统计数据)
203213
func (h *Handler) handleHealth(w http.ResponseWriter, r *http.Request) {
214+
w.Header().Set("Content-Type", "application/json; charset=utf-8")
215+
json.NewEncoder(w).Encode(map[string]interface{}{
216+
"status": "ok",
217+
"version": config.Version,
218+
"uptime": time.Now().Unix() - h.startTime,
219+
})
220+
}
221+
222+
// handleStats 统计数据(需要 API Key 鉴权)
223+
func (h *Handler) handleStats(w http.ResponseWriter, r *http.Request) {
204224
w.Header().Set("Content-Type", "application/json; charset=utf-8")
205225
json.NewEncoder(w).Encode(map[string]interface{}{
206226
"status": "ok",
227+
"version": config.Version,
207228
"accounts": h.pool.Count(),
208229
"available": h.pool.AvailableCount(),
209230
"totalRequests": atomic.LoadInt64(&h.totalRequests),

version.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"version": "1.0.0",
3-
"changelog": "🎉 v1.0.0 首个正式版",
2+
"version": "1.0.1",
3+
"changelog": "✨ 新增隐私模式支持\n🆕 管理面板增加退出登录功能\n📊 统计数据迁移至独立接口\n🎨 优化管理面板按钮样式与交互",
44
"download": "https://github.com/Quorinex/Kiro-Go"
5-
}
5+
}

web/index.html

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<head>
55
<meta charset="UTF-8">
66
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
7-
<title>Kiro API Proxy</title>
7+
<title>Kiro-Go</title>
88
<style>
99
* {
1010
box-sizing: border-box;
@@ -306,6 +306,13 @@
306306
flex-shrink: 0;
307307
}
308308

309+
.switch input {
310+
opacity: 0;
311+
width: 0;
312+
height: 0;
313+
position: absolute;
314+
}
315+
309316
.slider {
310317
position: absolute;
311318
cursor: pointer;
@@ -831,7 +838,7 @@
831838
</div>
832839
<h1 class="logo"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
833840
<path d="M13 2L3 14h9l-1 8 10-12h-9l1-8z" />
834-
</svg>Kiro API Proxy</h1>
841+
</svg>Kiro-Go</h1>
835842
<p data-i18n="login.subtitle"></p>
836843
<div class="form-group">
837844
<label data-i18n="login.password"></label>
@@ -847,14 +854,15 @@ <h1 class="logo"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stro
847854
<div class="header">
848855
<h1 class="logo"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
849856
<path d="M13 2L3 14h9l-1 8 10-12h-9l1-8z" />
850-
</svg>Kiro API Proxy</h1>
857+
</svg>Kiro-Go</h1>
851858
<div class="header-right">
852859
<div class="lang-switch">
853860
<button class="lang-btn" data-lang="zh" onclick="setLang('zh')">中文</button>
854861
<button class="lang-btn" data-lang="en" onclick="setLang('en')">EN</button>
855862
</div>
856863
<span class="badge badge-success" id="statusBadge" data-i18n="status.running"></span>
857864
<span class="badge badge-info" id="versionBadge" style="cursor:pointer" onclick="checkUpdate(true)"></span>
865+
<button class="btn btn-sm btn-danger" onclick="logout()" data-i18n="common.logout" style="padding:4px 12px;font-size:12px"></button>
858866
</div>
859867
</div>
860868
<div class="stats-grid">
@@ -893,13 +901,13 @@ <h1 class="logo"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stro
893901
<div class="card-header">
894902
<span class="card-title" data-i18n="accounts.title"></span>
895903
<div class="card-actions">
896-
<label class="privacy-toggle" style="display:flex;align-items:center;gap:8px;cursor:pointer;user-select:none">
904+
<div class="privacy-toggle" style="display:flex;align-items:center;gap:8px;cursor:pointer;user-select:none">
897905
<span style="font-size:13px;color:#374151;font-weight:500" data-i18n="privacy.label"></span>
898906
<label class="switch" style="margin:0">
899907
<input type="checkbox" id="privacyModeToggle" checked onchange="togglePrivacyMode()">
900908
<span class="slider"></span>
901909
</label>
902-
</label>
910+
</div>
903911
<button class="btn btn-secondary btn-sm" onclick="showExportModal()" data-i18n="accounts.export"></button>
904912
<button class="btn btn-primary btn-sm" onclick="showModal('add')" data-i18n="accounts.add"></button>
905913
</div>
@@ -986,6 +994,10 @@ <h1 class="logo"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stro
986994
<p style="margin:14px 0 10px;font-weight:500;font-size:14px" data-i18n="api.modelList"></p>
987995
<div class="endpoint"><span id="modelsEndpoint"></span><button class="btn btn-sm btn-secondary"
988996
onclick="copy('modelsEndpoint')" data-i18n="common.copy"></button></div>
997+
<p style="margin:14px 0 10px;font-weight:500;font-size:14px" data-i18n="api.stats"></p>
998+
<div class="endpoint"><span id="statsEndpoint"></span><button class="btn btn-sm btn-secondary"
999+
onclick="copy('statsEndpoint')" data-i18n="common.copy"></button></div>
1000+
<p style="font-size:12px;color:#64748b;margin-top:4px" data-i18n="api.statsHint"></p>
9891001
</div>
9901002
</div>
9911003
</div>
@@ -1091,6 +1103,8 @@ <h1 class="logo"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stro
10911103
'settings.confirmReset': '确定重置统计?',
10921104
'api.endpoints': 'API 端点',
10931105
'api.modelList': '模型列表',
1106+
'api.stats': '统计数据',
1107+
'api.statsHint': '需要在请求头中携带 API Key 鉴权(Authorization: Bearer sk-xxx),未启用 API Key 验证时无需鉴权',
10941108
'detail.title': '账号详情',
10951109
'detail.basicInfo': '基本信息',
10961110
'detail.email': '邮箱',
@@ -1184,6 +1198,7 @@ <h1 class="logo"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stro
11841198
'common.add': '添加',
11851199
'common.failed': '失败',
11861200
'common.saveFailed': '保存失败',
1201+
'common.logout': '退出登录',
11871202
'accounts.export': '导出',
11881203
'export.title': '导出账号',
11891204
'export.selectAll': '全选',
@@ -1271,6 +1286,8 @@ <h1 class="logo"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stro
12711286
'settings.confirmReset': 'Confirm reset statistics?',
12721287
'api.endpoints': 'API Endpoints',
12731288
'api.modelList': 'Model List',
1289+
'api.stats': 'Statistics',
1290+
'api.statsHint': 'Requires API Key in header (Authorization: Bearer sk-xxx). No auth needed if API Key verification is disabled.',
12741291
'detail.title': 'Account Details',
12751292
'detail.basicInfo': 'Basic Info',
12761293
'detail.email': 'Email',
@@ -1360,6 +1377,7 @@ <h1 class="logo"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stro
13601377
'common.add': 'Add',
13611378
'common.failed': 'Failed',
13621379
'common.saveFailed': 'Save failed',
1380+
'common.logout': 'Logout',
13631381
'accounts.export': 'Export',
13641382
'export.title': 'Export Accounts',
13651383
'export.selectAll': 'Select All',
@@ -1489,6 +1507,14 @@ <h1 class="logo"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stro
14891507
document.querySelectorAll('.tab').forEach(tab => { tab.onclick = () => switchTab(tab.dataset.tab); });
14901508
});
14911509
async function tryAutoLogin() {
1510+
// 72h 过期检查
1511+
const loginTime = parseInt(localStorage.getItem('admin_login_time') || '0');
1512+
if (loginTime && Date.now() - loginTime > 72 * 3600 * 1000) {
1513+
localStorage.removeItem('admin_password');
1514+
localStorage.removeItem('admin_login_time');
1515+
password = '';
1516+
return;
1517+
}
14921518
try {
14931519
const res = await fetch('/admin/api/status', { headers: { 'X-Admin-Password': password } });
14941520
if (res.ok) { showMain(); loadData(); }
@@ -1500,6 +1526,7 @@ <h1 class="logo"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stro
15001526
const res = await fetch('/admin/api/status', { headers: { 'X-Admin-Password': password } });
15011527
if (res.ok) {
15021528
localStorage.setItem('admin_password', password);
1529+
localStorage.setItem('admin_login_time', Date.now().toString());
15031530
showMain(); loadData();
15041531
} else {
15051532
document.getElementById('loginError').textContent = t('login.error');
@@ -1510,6 +1537,11 @@ <h1 class="logo"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stro
15101537
document.getElementById('loginError').classList.remove('hidden');
15111538
}
15121539
}
1540+
function logout() {
1541+
localStorage.removeItem('admin_password');
1542+
localStorage.removeItem('admin_login_time');
1543+
location.reload();
1544+
}
15131545
function showMain() {
15141546
document.getElementById('loginPage').classList.add('hidden');
15151547
document.getElementById('mainPage').classList.remove('hidden');
@@ -1519,6 +1551,7 @@ <h1 class="logo"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stro
15191551
document.getElementById('claudeEndpoint').textContent = baseUrl + '/v1/messages';
15201552
document.getElementById('openaiEndpoint').textContent = baseUrl + '/v1/chat/completions';
15211553
document.getElementById('modelsEndpoint').textContent = baseUrl + '/v1/models';
1554+
document.getElementById('statsEndpoint').textContent = baseUrl + '/v1/stats';
15221555
// 自动检查更新
15231556
setTimeout(() => checkUpdate(false), 2000);
15241557
}

0 commit comments

Comments
 (0)