690 lines
25 KiB
JavaScript
690 lines
25 KiB
JavaScript
// 如果设置了VITE_API_URL环境变量,使用它;否则在开发环境使用相对路径(通过vite代理),生产环境使用默认值
|
||
const API_BASE_URL = import.meta.env.VITE_API_URL || (import.meta.env.DEV ? '' : 'http://localhost:8000');
|
||
|
||
// 登录鉴权:JWT token(Authorization: Bearer xxx)
|
||
const AUTH_TOKEN_STORAGE_KEY = 'ats_auth_token';
|
||
export const getAuthToken = () => {
|
||
try {
|
||
return localStorage.getItem(AUTH_TOKEN_STORAGE_KEY) || '';
|
||
} catch (e) {
|
||
return '';
|
||
}
|
||
};
|
||
|
||
export const setAuthToken = (token) => {
|
||
try {
|
||
const t = String(token || '').trim();
|
||
if (!t) {
|
||
localStorage.removeItem(AUTH_TOKEN_STORAGE_KEY);
|
||
return;
|
||
}
|
||
localStorage.setItem(AUTH_TOKEN_STORAGE_KEY, t);
|
||
} catch (e) {
|
||
// ignore
|
||
}
|
||
};
|
||
|
||
export const clearAuthToken = () => setAuthToken('');
|
||
|
||
// 多账号:前端通过 Header 选择账号(默认 1)
|
||
const ACCOUNT_ID_STORAGE_KEY = 'ats_account_id';
|
||
export const getCurrentAccountId = () => {
|
||
try {
|
||
const v = localStorage.getItem(ACCOUNT_ID_STORAGE_KEY);
|
||
const n = parseInt(v || '1', 10);
|
||
return Number.isFinite(n) && n > 0 ? n : 1;
|
||
} catch (e) {
|
||
return 1;
|
||
}
|
||
};
|
||
|
||
export const setCurrentAccountId = (accountId) => {
|
||
try {
|
||
const n = parseInt(String(accountId || '1'), 10);
|
||
localStorage.setItem(ACCOUNT_ID_STORAGE_KEY, String(Number.isFinite(n) && n > 0 ? n : 1));
|
||
} catch (e) {
|
||
// ignore
|
||
}
|
||
};
|
||
|
||
export const clearCurrentAccountId = () => {
|
||
try {
|
||
localStorage.removeItem(ACCOUNT_ID_STORAGE_KEY);
|
||
} catch (e) {
|
||
// ignore
|
||
}
|
||
};
|
||
|
||
const withAuthHeaders = (headers = {}) => {
|
||
const token = getAuthToken();
|
||
if (!token) return { ...headers };
|
||
return { ...headers, Authorization: `Bearer ${token}` };
|
||
};
|
||
|
||
const withAccountHeaders = (headers = {}) => {
|
||
const aid = getCurrentAccountId();
|
||
return withAuthHeaders({ ...headers, 'X-Account-Id': String(aid) });
|
||
};
|
||
|
||
// 构建API URL的辅助函数,避免双斜杠和格式问题
|
||
const buildUrl = (path) => {
|
||
const baseUrl = API_BASE_URL.endsWith('/') ? API_BASE_URL.slice(0, -1) : API_BASE_URL;
|
||
const cleanPath = path.startsWith('/') ? path : `/${path}`;
|
||
return baseUrl ? `${baseUrl}${cleanPath}` : cleanPath;
|
||
};
|
||
|
||
export const api = {
|
||
// 登录
|
||
login: async (username, password) => {
|
||
const response = await fetch(buildUrl('/api/auth/login'), {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({ username, password }),
|
||
});
|
||
if (!response.ok) {
|
||
const error = await response.json().catch(() => ({ detail: '登录失败' }));
|
||
throw new Error(error.detail || '登录失败');
|
||
}
|
||
const data = await response.json();
|
||
if (data?.access_token) setAuthToken(data.access_token);
|
||
return data;
|
||
},
|
||
me: async () => {
|
||
const response = await fetch(buildUrl('/api/auth/me'), { headers: withAuthHeaders() });
|
||
if (!response.ok) {
|
||
const error = await response.json().catch(() => ({ detail: '获取登录信息失败' }));
|
||
throw new Error(error.detail || '获取登录信息失败');
|
||
}
|
||
return response.json();
|
||
},
|
||
|
||
// 公共状态(非管理员也可用)
|
||
getPublicStatus: async () => {
|
||
const response = await fetch(buildUrl('/api/public/status'))
|
||
if (!response.ok) {
|
||
const error = await response.json().catch(() => ({ detail: '获取服务状态失败' }))
|
||
throw new Error(error.detail || '获取服务状态失败')
|
||
}
|
||
return response.json()
|
||
},
|
||
|
||
// 账号管理
|
||
getAccounts: async () => {
|
||
const response = await fetch(buildUrl('/api/accounts'), { headers: withAccountHeaders() });
|
||
if (!response.ok) {
|
||
const error = await response.json().catch(() => ({ detail: '获取账号列表失败' }));
|
||
throw new Error(error.detail || '获取账号列表失败');
|
||
}
|
||
return response.json();
|
||
},
|
||
createAccount: async (data) => {
|
||
const response = await fetch(buildUrl('/api/accounts'), {
|
||
method: 'POST',
|
||
headers: withAccountHeaders({ 'Content-Type': 'application/json' }),
|
||
body: JSON.stringify(data || {}),
|
||
});
|
||
if (!response.ok) {
|
||
const error = await response.json().catch(() => ({ detail: '创建账号失败' }));
|
||
throw new Error(error.detail || '创建账号失败');
|
||
}
|
||
return response.json();
|
||
},
|
||
updateAccount: async (accountId, data) => {
|
||
const response = await fetch(buildUrl(`/api/accounts/${accountId}`), {
|
||
method: 'PUT',
|
||
headers: withAccountHeaders({ 'Content-Type': 'application/json' }),
|
||
body: JSON.stringify(data || {}),
|
||
});
|
||
if (!response.ok) {
|
||
const error = await response.json().catch(() => ({ detail: '更新账号失败' }));
|
||
throw new Error(error.detail || '更新账号失败');
|
||
}
|
||
return response.json();
|
||
},
|
||
updateAccountCredentials: async (accountId, data) => {
|
||
const response = await fetch(buildUrl(`/api/accounts/${accountId}/credentials`), {
|
||
method: 'PUT',
|
||
headers: withAccountHeaders({ 'Content-Type': 'application/json' }),
|
||
body: JSON.stringify(data || {}),
|
||
});
|
||
if (!response.ok) {
|
||
const error = await response.json().catch(() => ({ detail: '更新账号密钥失败' }));
|
||
throw new Error(error.detail || '更新账号密钥失败');
|
||
}
|
||
return response.json();
|
||
},
|
||
|
||
// 交易进程(按账号;需要 owner 或 admin)
|
||
getAccountTradingStatus: async (accountId) => {
|
||
const response = await fetch(buildUrl(`/api/accounts/${accountId}/trading/status`), { headers: withAccountHeaders() })
|
||
if (!response.ok) {
|
||
const error = await response.json().catch(() => ({ detail: '获取交易进程状态失败' }))
|
||
throw new Error(error.detail || '获取交易进程状态失败')
|
||
}
|
||
return response.json()
|
||
},
|
||
startAccountTrading: async (accountId) => {
|
||
const response = await fetch(buildUrl(`/api/accounts/${accountId}/trading/start`), {
|
||
method: 'POST',
|
||
headers: withAccountHeaders({ 'Content-Type': 'application/json' }),
|
||
})
|
||
if (!response.ok) {
|
||
const error = await response.json().catch(() => ({ detail: '启动交易进程失败' }))
|
||
throw new Error(error.detail || '启动交易进程失败')
|
||
}
|
||
return response.json()
|
||
},
|
||
stopAccountTrading: async (accountId) => {
|
||
const response = await fetch(buildUrl(`/api/accounts/${accountId}/trading/stop`), {
|
||
method: 'POST',
|
||
headers: withAccountHeaders({ 'Content-Type': 'application/json' }),
|
||
})
|
||
if (!response.ok) {
|
||
const error = await response.json().catch(() => ({ detail: '停止交易进程失败' }))
|
||
throw new Error(error.detail || '停止交易进程失败')
|
||
}
|
||
return response.json()
|
||
},
|
||
restartAccountTrading: async (accountId) => {
|
||
const response = await fetch(buildUrl(`/api/accounts/${accountId}/trading/restart`), {
|
||
method: 'POST',
|
||
headers: withAccountHeaders({ 'Content-Type': 'application/json' }),
|
||
})
|
||
if (!response.ok) {
|
||
const error = await response.json().catch(() => ({ detail: '重启交易进程失败' }))
|
||
throw new Error(error.detail || '重启交易进程失败')
|
||
}
|
||
return response.json()
|
||
},
|
||
ensureAccountTradingProgram: async (accountId) => {
|
||
const response = await fetch(buildUrl(`/api/accounts/${accountId}/trading/ensure-program`), {
|
||
method: 'POST',
|
||
headers: withAccountHeaders({ 'Content-Type': 'application/json' }),
|
||
})
|
||
if (!response.ok) {
|
||
const error = await response.json().catch(() => ({ detail: '生成/刷新 supervisor 配置失败' }))
|
||
throw new Error(error.detail || '生成/刷新 supervisor 配置失败')
|
||
}
|
||
return response.json()
|
||
},
|
||
|
||
// 配置管理
|
||
getConfigMeta: async () => {
|
||
const response = await fetch(buildUrl('/api/config/meta'), { headers: withAuthHeaders() })
|
||
if (!response.ok) {
|
||
const error = await response.json().catch(() => ({ detail: '获取配置元信息失败' }))
|
||
throw new Error(error.detail || '获取配置元信息失败')
|
||
}
|
||
return response.json()
|
||
},
|
||
getConfigs: async () => {
|
||
const response = await fetch(buildUrl('/api/config'), { headers: withAccountHeaders() });
|
||
if (!response.ok) {
|
||
const error = await response.json().catch(() => ({ detail: '获取配置失败' }));
|
||
throw new Error(error.detail || '获取配置失败');
|
||
}
|
||
return response.json();
|
||
},
|
||
|
||
getConfig: async (key) => {
|
||
const response = await fetch(buildUrl(`/api/config/${key}`), { headers: withAccountHeaders() });
|
||
if (!response.ok) {
|
||
const error = await response.json().catch(() => ({ detail: '获取配置失败' }));
|
||
throw new Error(error.detail || '获取配置失败');
|
||
}
|
||
return response.json();
|
||
},
|
||
|
||
updateConfig: async (key, data) => {
|
||
const response = await fetch(buildUrl(`/api/config/${key}`), {
|
||
method: 'PUT',
|
||
headers: withAccountHeaders({'Content-Type': 'application/json'}),
|
||
body: JSON.stringify(data)
|
||
});
|
||
if (!response.ok) {
|
||
const error = await response.json();
|
||
throw new Error(error.detail || '更新配置失败');
|
||
}
|
||
return response.json();
|
||
},
|
||
|
||
updateConfigsBatch: async (configs) => {
|
||
const response = await fetch(buildUrl('/api/config/batch'), {
|
||
method: 'POST',
|
||
headers: withAccountHeaders({'Content-Type': 'application/json'}),
|
||
body: JSON.stringify(configs)
|
||
});
|
||
if (!response.ok) {
|
||
const error = await response.json().catch(() => ({ detail: '批量更新配置失败' }));
|
||
throw new Error(error.detail || '批量更新配置失败');
|
||
}
|
||
return response.json();
|
||
},
|
||
|
||
// 检查配置可行性
|
||
checkConfigFeasibility: async () => {
|
||
const response = await fetch(buildUrl('/api/config/feasibility-check'), { headers: withAccountHeaders() });
|
||
if (!response.ok) {
|
||
const error = await response.json().catch(() => ({ detail: '检查配置可行性失败' }));
|
||
throw new Error(error.detail || '检查配置可行性失败');
|
||
}
|
||
return response.json();
|
||
},
|
||
|
||
// 交易记录
|
||
getTrades: async (params = {}) => {
|
||
const query = new URLSearchParams(params).toString();
|
||
const url = query ? `${buildUrl('/api/trades')}?${query}` : buildUrl('/api/trades');
|
||
const response = await fetch(url, {
|
||
method: 'GET',
|
||
headers: {
|
||
...withAccountHeaders({ 'Content-Type': 'application/json' }),
|
||
},
|
||
});
|
||
if (!response.ok) {
|
||
const error = await response.json().catch(() => ({ detail: '获取交易记录失败' }));
|
||
throw new Error(error.detail || '获取交易记录失败');
|
||
}
|
||
return response.json();
|
||
},
|
||
|
||
getTradeStats: async (params = {}) => {
|
||
const query = new URLSearchParams(params).toString();
|
||
const url = query ? `${buildUrl('/api/trades/stats')}?${query}` : buildUrl('/api/trades/stats');
|
||
const response = await fetch(url, {
|
||
method: 'GET',
|
||
headers: {
|
||
...withAccountHeaders({ 'Content-Type': 'application/json' }),
|
||
},
|
||
});
|
||
if (!response.ok) {
|
||
const error = await response.json().catch(() => ({ detail: '获取交易统计失败' }));
|
||
throw new Error(error.detail || '获取交易统计失败');
|
||
}
|
||
return response.json();
|
||
},
|
||
|
||
// 统计
|
||
getPerformance: async (days = 7) => {
|
||
const response = await fetch(buildUrl(`/api/stats/performance?days=${days}`), { headers: withAccountHeaders() });
|
||
if (!response.ok) {
|
||
const error = await response.json().catch(() => ({ detail: '获取性能统计失败' }));
|
||
throw new Error(error.detail || '获取性能统计失败');
|
||
}
|
||
return response.json();
|
||
},
|
||
|
||
getDashboard: async () => {
|
||
const response = await fetch(buildUrl('/api/dashboard'), { headers: withAccountHeaders() });
|
||
if (!response.ok) {
|
||
const error = await response.json().catch(() => ({ detail: '获取仪表板数据失败' }));
|
||
throw new Error(error.detail || '获取仪表板数据失败');
|
||
}
|
||
return response.json();
|
||
},
|
||
|
||
// 平仓操作
|
||
closePosition: async (symbol) => {
|
||
const response = await fetch(buildUrl(`/api/account/positions/${symbol}/close`), {
|
||
method: 'POST',
|
||
headers: {
|
||
...withAccountHeaders({ 'Content-Type': 'application/json' }),
|
||
},
|
||
});
|
||
if (!response.ok) {
|
||
const error = await response.json().catch(() => ({ detail: '平仓失败' }));
|
||
throw new Error(error.detail || '平仓失败');
|
||
}
|
||
return response.json();
|
||
},
|
||
|
||
// 补挂止盈止损(交易所保护单)
|
||
ensurePositionSLTP: async (symbol) => {
|
||
const response = await fetch(buildUrl(`/api/account/positions/${symbol}/sltp/ensure`), {
|
||
method: 'POST',
|
||
headers: withAccountHeaders({ 'Content-Type': 'application/json' }),
|
||
});
|
||
if (!response.ok) {
|
||
const error = await response.json().catch(() => ({ detail: '补挂止盈止损失败' }));
|
||
throw new Error(error.detail || '补挂止盈止损失败');
|
||
}
|
||
return response.json();
|
||
},
|
||
|
||
ensureAllPositionsSLTP: async (limit = 50) => {
|
||
const response = await fetch(buildUrl(`/api/account/positions/sltp/ensure-all?limit=${encodeURIComponent(limit)}`), {
|
||
method: 'POST',
|
||
headers: withAccountHeaders({ 'Content-Type': 'application/json' }),
|
||
});
|
||
if (!response.ok) {
|
||
const error = await response.json().catch(() => ({ detail: '批量补挂止盈止损失败' }));
|
||
throw new Error(error.detail || '批量补挂止盈止损失败');
|
||
}
|
||
return response.json();
|
||
},
|
||
|
||
// 同步持仓状态
|
||
syncPositions: async () => {
|
||
const response = await fetch(buildUrl('/api/account/positions/sync'), {
|
||
method: 'POST',
|
||
headers: {
|
||
...withAccountHeaders({ 'Content-Type': 'application/json' }),
|
||
},
|
||
});
|
||
if (!response.ok) {
|
||
const error = await response.json().catch(() => ({ detail: '同步失败' }));
|
||
throw new Error(error.detail || '同步失败');
|
||
}
|
||
return response.json();
|
||
},
|
||
|
||
// 交易推荐
|
||
getRecommendations: async (params = {}) => {
|
||
// 默认使用实时推荐
|
||
if (!params.type) {
|
||
params.type = 'realtime'
|
||
}
|
||
const query = new URLSearchParams(params).toString();
|
||
const url = query ? `${buildUrl('/api/recommendations')}?${query}` : buildUrl('/api/recommendations');
|
||
const response = await fetch(url, { headers: withAccountHeaders() });
|
||
if (!response.ok) {
|
||
const error = await response.json().catch(() => ({ detail: '获取推荐失败' }));
|
||
throw new Error(error.detail || '获取推荐失败');
|
||
}
|
||
return response.json();
|
||
},
|
||
|
||
bookmarkRecommendation: async (recommendationData) => {
|
||
const response = await fetch(buildUrl('/api/recommendations/bookmark'), {
|
||
method: 'POST',
|
||
headers: withAccountHeaders({ 'Content-Type': 'application/json' }),
|
||
body: JSON.stringify(recommendationData)
|
||
});
|
||
if (!response.ok) {
|
||
const error = await response.json().catch(() => ({ detail: '标记推荐失败' }));
|
||
throw new Error(error.detail || '标记推荐失败');
|
||
}
|
||
return response.json();
|
||
},
|
||
|
||
getActiveRecommendations: async () => {
|
||
const response = await fetch(buildUrl('/api/recommendations/active'), { headers: withAccountHeaders() });
|
||
if (!response.ok) {
|
||
const error = await response.json().catch(() => ({ detail: '获取有效推荐失败' }));
|
||
throw new Error(error.detail || '获取有效推荐失败');
|
||
}
|
||
return response.json();
|
||
},
|
||
|
||
getRecommendation: async (id) => {
|
||
const response = await fetch(buildUrl(`/api/recommendations/${id}`), { headers: withAccountHeaders() });
|
||
if (!response.ok) {
|
||
const error = await response.json().catch(() => ({ detail: '获取推荐详情失败' }));
|
||
throw new Error(error.detail || '获取推荐详情失败');
|
||
}
|
||
return response.json();
|
||
},
|
||
|
||
generateRecommendations: async (minSignalStrength = 5, maxRecommendations = 20) => {
|
||
const response = await fetch(
|
||
buildUrl(`/api/recommendations/generate?min_signal_strength=${minSignalStrength}&max_recommendations=${maxRecommendations}`),
|
||
{
|
||
method: 'POST',
|
||
headers: withAccountHeaders({ 'Content-Type': 'application/json' }),
|
||
}
|
||
);
|
||
if (!response.ok) {
|
||
const error = await response.json().catch(() => ({ detail: '生成推荐失败' }));
|
||
throw new Error(error.detail || '生成推荐失败');
|
||
}
|
||
return response.json();
|
||
},
|
||
|
||
markRecommendationExecuted: async (id, tradeId = null) => {
|
||
const response = await fetch(buildUrl(`/api/recommendations/${id}/execute`), {
|
||
method: 'POST',
|
||
headers: withAccountHeaders({ 'Content-Type': 'application/json' }),
|
||
body: JSON.stringify({ trade_id: tradeId }),
|
||
});
|
||
if (!response.ok) {
|
||
const error = await response.json().catch(() => ({ detail: '标记执行失败' }));
|
||
throw new Error(error.detail || '标记执行失败');
|
||
}
|
||
return response.json();
|
||
},
|
||
|
||
cancelRecommendation: async (id, notes = null) => {
|
||
const response = await fetch(buildUrl(`/api/recommendations/${id}/cancel`), {
|
||
method: 'POST',
|
||
headers: withAccountHeaders({ 'Content-Type': 'application/json' }),
|
||
body: JSON.stringify({ notes }),
|
||
});
|
||
if (!response.ok) {
|
||
const error = await response.json().catch(() => ({ detail: '取消推荐失败' }));
|
||
throw new Error(error.detail || '取消推荐失败');
|
||
}
|
||
return response.json();
|
||
},
|
||
|
||
// 系统控制(supervisor)
|
||
clearSystemCache: async () => {
|
||
const response = await fetch(buildUrl('/api/system/clear-cache'), {
|
||
method: 'POST',
|
||
headers: withAccountHeaders({ 'Content-Type': 'application/json' }),
|
||
});
|
||
if (!response.ok) {
|
||
const error = await response.json().catch(() => ({ detail: '清理缓存失败' }));
|
||
throw new Error(error.detail || '清理缓存失败');
|
||
}
|
||
return response.json();
|
||
},
|
||
|
||
getTradingSystemStatus: async () => {
|
||
const response = await fetch(buildUrl('/api/system/trading/status'), { headers: withAccountHeaders() });
|
||
if (!response.ok) {
|
||
const error = await response.json().catch(() => ({ detail: '获取交易系统状态失败' }));
|
||
throw new Error(error.detail || '获取交易系统状态失败');
|
||
}
|
||
return response.json();
|
||
},
|
||
|
||
startTradingSystem: async () => {
|
||
const response = await fetch(buildUrl('/api/system/trading/start'), {
|
||
method: 'POST',
|
||
headers: withAccountHeaders({ 'Content-Type': 'application/json' }),
|
||
});
|
||
if (!response.ok) {
|
||
const error = await response.json().catch(() => ({ detail: '启动交易系统失败' }));
|
||
throw new Error(error.detail || '启动交易系统失败');
|
||
}
|
||
return response.json();
|
||
},
|
||
|
||
stopTradingSystem: async () => {
|
||
const response = await fetch(buildUrl('/api/system/trading/stop'), {
|
||
method: 'POST',
|
||
headers: withAccountHeaders({ 'Content-Type': 'application/json' }),
|
||
});
|
||
if (!response.ok) {
|
||
const error = await response.json().catch(() => ({ detail: '停止交易系统失败' }));
|
||
throw new Error(error.detail || '停止交易系统失败');
|
||
}
|
||
return response.json();
|
||
},
|
||
|
||
restartTradingSystem: async () => {
|
||
const response = await fetch(buildUrl('/api/system/trading/restart'), {
|
||
method: 'POST',
|
||
headers: withAccountHeaders({ 'Content-Type': 'application/json' }),
|
||
});
|
||
if (!response.ok) {
|
||
const error = await response.json().catch(() => ({ detail: '重启交易系统失败' }));
|
||
throw new Error(error.detail || '重启交易系统失败');
|
||
}
|
||
return response.json();
|
||
},
|
||
|
||
restartAllTradingSystems: async (opts = {}) => {
|
||
const params = new URLSearchParams()
|
||
if (opts?.prefix) params.set('prefix', String(opts.prefix))
|
||
if (opts?.include_default) params.set('include_default', 'true')
|
||
if (opts?.do_update === false) params.set('do_update', 'false')
|
||
const url = params.toString()
|
||
? `${buildUrl('/api/system/trading/restart-all')}?${params.toString()}`
|
||
: buildUrl('/api/system/trading/restart-all')
|
||
const response = await fetch(url, {
|
||
method: 'POST',
|
||
headers: withAccountHeaders({ 'Content-Type': 'application/json' }),
|
||
})
|
||
if (!response.ok) {
|
||
const error = await response.json().catch(() => ({ detail: '批量重启失败' }))
|
||
throw new Error(error.detail || '批量重启失败')
|
||
}
|
||
return response.json()
|
||
},
|
||
|
||
// 后端控制(uvicorn)
|
||
getBackendStatus: async () => {
|
||
const response = await fetch(buildUrl('/api/system/backend/status'), { headers: withAccountHeaders() });
|
||
if (!response.ok) {
|
||
const error = await response.json().catch(() => ({ detail: '获取后端状态失败' }));
|
||
throw new Error(error.detail || '获取后端状态失败');
|
||
}
|
||
return response.json();
|
||
},
|
||
|
||
restartBackend: async () => {
|
||
const response = await fetch(buildUrl('/api/system/backend/restart'), {
|
||
method: 'POST',
|
||
headers: withAccountHeaders({ 'Content-Type': 'application/json' }),
|
||
});
|
||
if (!response.ok) {
|
||
const error = await response.json().catch(() => ({ detail: '重启后端失败' }));
|
||
throw new Error(error.detail || '重启后端失败');
|
||
}
|
||
return response.json();
|
||
},
|
||
|
||
// 日志监控(Redis List)
|
||
getSystemLogs: async (params = {}) => {
|
||
const query = new URLSearchParams(params).toString();
|
||
const url = query ? `${buildUrl('/api/system/logs')}?${query}` : buildUrl('/api/system/logs');
|
||
const response = await fetch(url, { headers: withAccountHeaders() });
|
||
if (!response.ok) {
|
||
const error = await response.json().catch(() => ({ detail: '获取日志失败' }));
|
||
throw new Error(error.detail || '获取日志失败');
|
||
}
|
||
return response.json();
|
||
},
|
||
|
||
getLogsOverview: async () => {
|
||
const response = await fetch(buildUrl('/api/system/logs/overview'), { headers: withAccountHeaders() });
|
||
if (!response.ok) {
|
||
const error = await response.json().catch(() => ({ detail: '获取日志概览失败' }));
|
||
throw new Error(error.detail || '获取日志概览失败');
|
||
}
|
||
return response.json();
|
||
},
|
||
|
||
updateLogsConfig: async (data) => {
|
||
const response = await fetch(buildUrl('/api/system/logs/config'), {
|
||
method: 'PUT',
|
||
headers: withAccountHeaders({ 'Content-Type': 'application/json' }),
|
||
body: JSON.stringify(data || {}),
|
||
});
|
||
if (!response.ok) {
|
||
const error = await response.json().catch(() => ({ detail: '更新日志配置失败' }));
|
||
throw new Error(error.detail || '更新日志配置失败');
|
||
}
|
||
return response.json();
|
||
},
|
||
|
||
writeLogsTest: async () => {
|
||
const response = await fetch(buildUrl('/api/system/logs/test-write'), {
|
||
method: 'POST',
|
||
headers: withAccountHeaders({ 'Content-Type': 'application/json' }),
|
||
});
|
||
if (!response.ok) {
|
||
const error = await response.json().catch(() => ({ detail: '写入测试日志失败' }));
|
||
throw new Error(error.detail || '写入测试日志失败');
|
||
}
|
||
return response.json();
|
||
},
|
||
|
||
// 管理员:获取用户列表
|
||
getUsers: async () => {
|
||
const response = await fetch(buildUrl('/api/admin/users'), { headers: withAuthHeaders() });
|
||
if (!response.ok) {
|
||
const error = await response.json().catch(() => ({ detail: '获取用户列表失败' }));
|
||
throw new Error(error.detail || '获取用户列表失败');
|
||
}
|
||
return response.json();
|
||
},
|
||
|
||
// 管理员:获取用户关联的账号列表
|
||
getUserAccounts: async (userId) => {
|
||
const response = await fetch(buildUrl(`/api/admin/users/${userId}/accounts`), { headers: withAuthHeaders() });
|
||
if (!response.ok) {
|
||
const error = await response.json().catch(() => ({ detail: '获取用户账号列表失败' }));
|
||
throw new Error(error.detail || '获取用户账号列表失败');
|
||
}
|
||
return response.json();
|
||
},
|
||
|
||
// 管理员:创建用户
|
||
createUser: async (data) => {
|
||
const response = await fetch(buildUrl('/api/admin/users'), {
|
||
method: 'POST',
|
||
headers: withAuthHeaders({ 'Content-Type': 'application/json' }),
|
||
body: JSON.stringify(data),
|
||
});
|
||
if (!response.ok) {
|
||
const error = await response.json().catch(() => ({ detail: '创建用户失败' }));
|
||
throw new Error(error.detail || '创建用户失败');
|
||
}
|
||
return response.json();
|
||
},
|
||
|
||
// 管理员:更新用户密码
|
||
updateUserPassword: async (userId, password) => {
|
||
const response = await fetch(buildUrl(`/api/admin/users/${userId}/password`), {
|
||
method: 'PUT',
|
||
headers: withAuthHeaders({ 'Content-Type': 'application/json' }),
|
||
body: JSON.stringify({ password }),
|
||
});
|
||
if (!response.ok) {
|
||
const error = await response.json().catch(() => ({ detail: '更新密码失败' }));
|
||
throw new Error(error.detail || '更新密码失败');
|
||
}
|
||
return response.json();
|
||
},
|
||
|
||
// 管理员:更新用户角色
|
||
updateUserRole: async (userId, role) => {
|
||
const response = await fetch(buildUrl(`/api/admin/users/${userId}/role`), {
|
||
method: 'PUT',
|
||
headers: withAuthHeaders({ 'Content-Type': 'application/json' }),
|
||
body: JSON.stringify({ role }),
|
||
});
|
||
if (!response.ok) {
|
||
const error = await response.json().catch(() => ({ detail: '更新角色失败' }));
|
||
throw new Error(error.detail || '更新角色失败');
|
||
}
|
||
return response.json();
|
||
},
|
||
|
||
// 管理员:更新用户状态
|
||
updateUserStatus: async (userId, status) => {
|
||
const response = await fetch(buildUrl(`/api/admin/users/${userId}/status`), {
|
||
method: 'PUT',
|
||
headers: withAuthHeaders({ 'Content-Type': 'application/json' }),
|
||
body: JSON.stringify({ status }),
|
||
});
|
||
if (!response.ok) {
|
||
const error = await response.json().catch(() => ({ detail: '更新状态失败' }));
|
||
throw new Error(error.detail || '更新状态失败');
|
||
}
|
||
return response.json();
|
||
},
|
||
};
|