feat(webui): add hot-reload account management with OAuth support
This commit is contained in:
@@ -28,6 +28,14 @@ window.Components.accountManager = () => ({
|
||||
async toggleAccount(email, enabled) {
|
||||
const store = Alpine.store('global');
|
||||
const password = store.webuiPassword;
|
||||
|
||||
// Optimistic update: immediately update UI
|
||||
const dataStore = Alpine.store('data');
|
||||
const account = dataStore.accounts.find(a => a.email === email);
|
||||
if (account) {
|
||||
account.enabled = enabled;
|
||||
}
|
||||
|
||||
try {
|
||||
const { response, newPassword } = await window.utils.request(`/api/accounts/${encodeURIComponent(email)}/toggle`, {
|
||||
method: 'POST',
|
||||
@@ -40,12 +48,23 @@ window.Components.accountManager = () => ({
|
||||
if (data.status === 'ok') {
|
||||
const status = enabled ? store.t('enabledStatus') : store.t('disabledStatus');
|
||||
store.showToast(store.t('accountToggled', { email, status }), 'success');
|
||||
Alpine.store('data').fetchData();
|
||||
// Refresh to confirm server state
|
||||
await dataStore.fetchData();
|
||||
} else {
|
||||
store.showToast(data.error || store.t('toggleFailed'), 'error');
|
||||
// Rollback optimistic update on error
|
||||
if (account) {
|
||||
account.enabled = !enabled;
|
||||
}
|
||||
await dataStore.fetchData();
|
||||
}
|
||||
} catch (e) {
|
||||
store.showToast(store.t('toggleFailed') + ': ' + e.message, 'error');
|
||||
// Rollback optimistic update on error
|
||||
if (account) {
|
||||
account.enabled = !enabled;
|
||||
}
|
||||
await dataStore.fetchData();
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@@ -495,7 +495,10 @@ window.Components.dashboard = () => ({
|
||||
|
||||
const isCore = (id) => /sonnet|opus|pro|flash/i.test(id);
|
||||
|
||||
accounts.forEach(acc => {
|
||||
// Only count enabled accounts in statistics
|
||||
const enabledAccounts = accounts.filter(acc => acc.enabled !== false);
|
||||
|
||||
enabledAccounts.forEach(acc => {
|
||||
if (acc.status === 'ok') {
|
||||
const limits = Object.entries(acc.limits || {});
|
||||
let hasActiveCore = limits.some(([id, l]) => l && l.remainingFraction > 0.05 && isCore(id));
|
||||
@@ -512,7 +515,10 @@ window.Components.dashboard = () => ({
|
||||
limited++;
|
||||
}
|
||||
});
|
||||
this.stats.total = accounts.length;
|
||||
|
||||
// TOTAL shows only enabled accounts
|
||||
// Disabled accounts are excluded from all statistics
|
||||
this.stats.total = enabledAccounts.length;
|
||||
this.stats.active = active;
|
||||
this.stats.limited = limited;
|
||||
},
|
||||
|
||||
@@ -50,17 +50,20 @@ document.addEventListener('alpine:init', () => {
|
||||
enabled: "ENABLED",
|
||||
health: "HEALTH",
|
||||
identity: "IDENTITY (EMAIL)",
|
||||
source: "SOURCE",
|
||||
projectId: "PROJECT ID",
|
||||
sessionState: "SESSION STATE",
|
||||
operations: "OPERATIONS",
|
||||
delete: "Delete",
|
||||
confirmDelete: "Are you sure you want to remove this account?",
|
||||
cannotDeleteDatabase: "Cannot delete: This account is from Antigravity database (read-only)",
|
||||
connectGoogle: "Connect Google Account",
|
||||
reauthenticated: "re-authenticated",
|
||||
added: "added",
|
||||
successfully: "successfully",
|
||||
failedToGetAuthUrl: "Failed to get auth URL",
|
||||
failedToStartOAuth: "Failed to start OAuth flow",
|
||||
oauthInProgress: "OAuth in progress. Please complete authentication in the popup window...",
|
||||
family: "Family",
|
||||
model: "Model",
|
||||
activeSuffix: "Active",
|
||||
@@ -211,17 +214,20 @@ document.addEventListener('alpine:init', () => {
|
||||
enabled: "启用",
|
||||
health: "健康度",
|
||||
identity: "身份 (邮箱)",
|
||||
source: "来源",
|
||||
projectId: "项目 ID",
|
||||
sessionState: "会话状态",
|
||||
operations: "操作",
|
||||
delete: "删除",
|
||||
confirmDelete: "确定要移除此账号吗?",
|
||||
cannotDeleteDatabase: "无法删除:此账号来自 Antigravity 数据库(只读)",
|
||||
connectGoogle: "连接 Google 账号",
|
||||
reauthenticated: "已重新认证",
|
||||
added: "已添加",
|
||||
successfully: "成功",
|
||||
failedToGetAuthUrl: "获取认证链接失败",
|
||||
failedToStartOAuth: "启动 OAuth 流程失败",
|
||||
oauthInProgress: "OAuth 授权进行中,请在弹出窗口中完成认证...",
|
||||
family: "系列",
|
||||
model: "模型",
|
||||
activeSuffix: "活跃",
|
||||
|
||||
Reference in New Issue
Block a user