Files
antigravity-claude-proxy/public/js/utils/account-actions.js
Wha1eChai a56bc06cc1 feat(webui): add Tailwind build system and refactor frontend architecture
- Replace Tailwind CDN with local build (PostCSS + autoprefixer + daisyui)

- Add CSS build scripts with automatic prepare hook on npm install

- Create account-actions.js service layer with unified response format

- Extend ErrorHandler.withLoading() for automatic loading state management

- Add skeleton screens for initial load, silent refresh for subsequent updates

- Implement loading animations for async operations (buttons, modals)

- Improve empty states and add ARIA labels for accessibility

- Abstract component styles using @apply (buttons, badges, inputs)

- Add JSDoc documentation for Dashboard modules

- Update README and CLAUDE.md with development guidelines
2026-01-11 02:11:35 +08:00

200 lines
5.8 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Account Actions Service
* 纯业务逻辑层 - 处理账号操作的 HTTP 请求、乐观更新和数据刷新
* 不包含 UI 关注点Toast、Loading、模态框由组件层处理
*/
window.AccountActions = window.AccountActions || {};
/**
* 刷新账号 token 和配额信息
* @param {string} email - 账号邮箱
* @returns {Promise<{success: boolean, data?: object, error?: string}>}
*/
window.AccountActions.refreshAccount = async function(email) {
const store = Alpine.store('global');
try {
const { response, newPassword } = await window.utils.request(
`/api/accounts/${encodeURIComponent(email)}/refresh`,
{ method: 'POST' },
store.webuiPassword
);
if (newPassword) {
store.webuiPassword = newPassword;
}
const data = await response.json();
if (data.status !== 'ok') {
return { success: false, error: data.error || 'Refresh failed' };
}
// 触发数据刷新
await Alpine.store('data').fetchData();
return { success: true, data };
} catch (error) {
return { success: false, error: error.message };
}
};
/**
* 切换账号启用/禁用状态(包含乐观更新和错误回滚)
* @param {string} email - 账号邮箱
* @param {boolean} enabled - 目标状态true=启用, false=禁用)
* @returns {Promise<{success: boolean, rolledBack?: boolean, data?: object, error?: string}>}
*/
window.AccountActions.toggleAccount = async function(email, enabled) {
const store = Alpine.store('global');
const dataStore = Alpine.store('data');
// 乐观更新:立即修改 UI
const account = dataStore.accounts.find(a => a.email === email);
const previousState = account ? account.enabled : !enabled;
if (account) {
account.enabled = enabled;
}
try {
const { response, newPassword } = await window.utils.request(
`/api/accounts/${encodeURIComponent(email)}/toggle`,
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ enabled })
},
store.webuiPassword
);
if (newPassword) {
store.webuiPassword = newPassword;
}
const data = await response.json();
if (data.status !== 'ok') {
throw new Error(data.error || 'Toggle failed');
}
// 确认服务器状态
await dataStore.fetchData();
return { success: true, data };
} catch (error) {
// 错误回滚:恢复原状态
if (account) {
account.enabled = previousState;
}
await dataStore.fetchData();
return { success: false, error: error.message, rolledBack: true };
}
};
/**
* 删除账号
* @param {string} email - 账号邮箱
* @returns {Promise<{success: boolean, data?: object, error?: string}>}
*/
window.AccountActions.deleteAccount = async function(email) {
const store = Alpine.store('global');
try {
const { response, newPassword } = await window.utils.request(
`/api/accounts/${encodeURIComponent(email)}`,
{ method: 'DELETE' },
store.webuiPassword
);
if (newPassword) {
store.webuiPassword = newPassword;
}
const data = await response.json();
if (data.status !== 'ok') {
return { success: false, error: data.error || 'Delete failed' };
}
// 触发数据刷新
await Alpine.store('data').fetchData();
return { success: true, data };
} catch (error) {
return { success: false, error: error.message };
}
};
/**
* 获取账号重新认证的 OAuth URL
* 注意:此方法仅返回 URL不打开窗口由组件层决定如何处理
* @param {string} email - 账号邮箱
* @returns {Promise<{success: boolean, url?: string, error?: string}>}
*/
window.AccountActions.getFixAccountUrl = async function(email) {
const store = Alpine.store('global');
try {
const urlPath = `/api/auth/url?email=${encodeURIComponent(email)}`;
const { response, newPassword } = await window.utils.request(
urlPath,
{},
store.webuiPassword
);
if (newPassword) {
store.webuiPassword = newPassword;
}
const data = await response.json();
if (data.status !== 'ok') {
return { success: false, error: data.error || 'Failed to get auth URL' };
}
return { success: true, url: data.url };
} catch (error) {
return { success: false, error: error.message };
}
};
/**
* 从磁盘重新加载所有账号配置
* @returns {Promise<{success: boolean, data?: object, error?: string}>}
*/
window.AccountActions.reloadAccounts = async function() {
const store = Alpine.store('global');
try {
const { response, newPassword } = await window.utils.request(
'/api/accounts/reload',
{ method: 'POST' },
store.webuiPassword
);
if (newPassword) {
store.webuiPassword = newPassword;
}
const data = await response.json();
if (data.status !== 'ok') {
return { success: false, error: data.error || 'Reload failed' };
}
// 触发数据刷新
await Alpine.store('data').fetchData();
return { success: true, data };
} catch (error) {
return { success: false, error: error.message };
}
};
/**
* 检查账号是否可以删除
* 来自 Antigravity 数据库的账号source='database')不可删除
* @param {object} account - 账号对象
* @returns {boolean} true 表示可删除
*/
window.AccountActions.canDelete = function(account) {
return account && account.source !== 'database';
};