fix(ws): 发送 WebSocket 协议级 Ping 控制帧防止 60s 后连接断开

writePump 只发 ActionCable 文本 JSON ping,从不发 websocket.PingMessage,
导致 readPump 的 PongHandler 永远不触发,ReadDeadline(60s) 到期后连接
被强制关闭。前端每 60 秒弹出"离线""重连"通知。

修复:在 ticker 中先发 PingMessage 控制帧(浏览器自动回 Pong 重置
ReadDeadline),再发 ActionCable 文本 ping(保活 ConnectionMonitor)。

同时包含此前会话中的其他变更:
- 移除安全设置(SAML)页面及路由
- 新增超级管理员控制台前端页面
- platform agent_bots 路由使用 Platform* handler
- 超级管理员入口改为 SPA 内路由跳转
This commit is contained in:
Rogee
2026-07-30 16:55:44 +08:00
parent 1fb95e4151
commit cd82d079e3
26 changed files with 2421 additions and 199 deletions
@@ -59,6 +59,7 @@ import copilotMessages from './captain/copilotMessages';
import captainScenarios from './captain/scenarios';
import captainTools from './captain/tools';
import captainCustomTools from './captain/customTools';
import platform from './modules/platform';
const plugins = [];
@@ -123,6 +124,7 @@ export default createStore({
captainScenarios,
captainTools,
captainCustomTools,
platform,
},
plugins,
});
@@ -0,0 +1,394 @@
import types from '../mutation-types';
import PlatformAPI from '../../api/platform';
import { throwErrorMessage } from '../utils/api';
export const state = {
accounts: [],
users: [],
agentBots: [],
installationConfigs: [],
uiFlags: {
isFetchingAccounts: false,
isFetchingUsers: false,
isFetchingAgentBots: false,
isFetchingConfigs: false,
isCreating: false,
isUpdating: false,
isDeleting: false,
},
};
export const getters = {
getAccounts: $state => $state.accounts,
getUsers: $state => $state.users,
getPlatformAgentBots: $state => $state.agentBots,
getInstallationConfigs: $state => $state.installationConfigs,
getUIFlags: $state => $state.uiFlags,
};
export const actions = {
// ── Accounts ──────────────────────────────────────────
fetchAccounts: async ({ commit }, params) => {
commit(types.SET_PLATFORM_UI_FLAG, { isFetchingAccounts: true });
try {
const response = await PlatformAPI.getAccounts(params);
const payload = response.data?.data || response.data || [];
commit(types.SET_PLATFORM_ACCOUNTS, payload);
return payload;
} catch (error) {
throwErrorMessage(error);
} finally {
commit(types.SET_PLATFORM_UI_FLAG, { isFetchingAccounts: false });
}
return null;
},
createAccount: async ({ commit }, accountData) => {
commit(types.SET_PLATFORM_UI_FLAG, { isCreating: true });
try {
const response = await PlatformAPI.createAccount(accountData);
const payload = response.data?.data || response.data;
commit(types.ADD_PLATFORM_ACCOUNT, payload);
return payload;
} catch (error) {
throwErrorMessage(error);
} finally {
commit(types.SET_PLATFORM_UI_FLAG, { isCreating: false });
}
return null;
},
updateAccount: async ({ commit }, { id, data }) => {
commit(types.SET_PLATFORM_UI_FLAG, { isUpdating: true });
try {
const response = await PlatformAPI.updateAccount(id, data);
commit(types.EDIT_PLATFORM_ACCOUNT, response.data);
return response.data;
} catch (error) {
throwErrorMessage(error);
} finally {
commit(types.SET_PLATFORM_UI_FLAG, { isUpdating: false });
}
return null;
},
deleteAccount: async ({ commit }, id) => {
commit(types.SET_PLATFORM_UI_FLAG, { isDeleting: true });
try {
await PlatformAPI.deleteAccount(id);
commit(types.DELETE_PLATFORM_ACCOUNT, id);
} catch (error) {
throwErrorMessage(error);
} finally {
commit(types.SET_PLATFORM_UI_FLAG, { isDeleting: false });
}
},
// ── Users ─────────────────────────────────────────────
fetchUsers: async ({ commit }, params) => {
commit(types.SET_PLATFORM_UI_FLAG, { isFetchingUsers: true });
try {
const response = await PlatformAPI.getUsers(params);
const payload = response.data?.data || response.data || [];
commit(types.SET_PLATFORM_USERS, payload);
return payload;
} catch (error) {
throwErrorMessage(error);
} finally {
commit(types.SET_PLATFORM_UI_FLAG, { isFetchingUsers: false });
}
return null;
},
createUser: async ({ commit }, userData) => {
commit(types.SET_PLATFORM_UI_FLAG, { isCreating: true });
try {
const response = await PlatformAPI.createUser(userData);
const payload = response.data?.data || response.data;
commit(types.ADD_PLATFORM_USER, payload);
return payload;
} catch (error) {
throwErrorMessage(error);
} finally {
commit(types.SET_PLATFORM_UI_FLAG, { isCreating: false });
}
return null;
},
updateUser: async ({ commit }, { id, data }) => {
commit(types.SET_PLATFORM_UI_FLAG, { isUpdating: true });
try {
const response = await PlatformAPI.updateUser(id, data);
commit(types.EDIT_PLATFORM_USER, response.data);
return response.data;
} catch (error) {
throwErrorMessage(error);
} finally {
commit(types.SET_PLATFORM_UI_FLAG, { isUpdating: false });
}
return null;
},
deleteUser: async ({ commit }, id) => {
commit(types.SET_PLATFORM_UI_FLAG, { isDeleting: true });
try {
await PlatformAPI.deleteUser(id);
commit(types.DELETE_PLATFORM_USER, id);
} catch (error) {
throwErrorMessage(error);
} finally {
commit(types.SET_PLATFORM_UI_FLAG, { isDeleting: false });
}
},
// ── Agent Bots (platform-level) ───────────────────────
fetchAgentBots: async ({ commit }, params) => {
commit(types.SET_PLATFORM_UI_FLAG, { isFetchingAgentBots: true });
try {
const response = await PlatformAPI.getAgentBots(params);
const payload = response.data?.data || response.data || [];
commit(types.SET_PLATFORM_AGENT_BOTS, payload);
return payload;
} catch (error) {
throwErrorMessage(error);
} finally {
commit(types.SET_PLATFORM_UI_FLAG, { isFetchingAgentBots: false });
}
return null;
},
createAgentBot: async ({ commit }, botData) => {
commit(types.SET_PLATFORM_UI_FLAG, { isCreating: true });
try {
const response = await PlatformAPI.createAgentBot(botData);
const payload = response.data?.data || response.data;
commit(types.ADD_PLATFORM_AGENT_BOT, payload);
return payload;
} catch (error) {
throwErrorMessage(error);
} finally {
commit(types.SET_PLATFORM_UI_FLAG, { isCreating: false });
}
return null;
},
updateAgentBot: async ({ commit }, { id, data }) => {
commit(types.SET_PLATFORM_UI_FLAG, { isUpdating: true });
try {
const response = await PlatformAPI.updateAgentBot(id, data);
commit(types.EDIT_PLATFORM_AGENT_BOT, response.data);
return response.data;
} catch (error) {
throwErrorMessage(error);
} finally {
commit(types.SET_PLATFORM_UI_FLAG, { isUpdating: false });
}
return null;
},
deleteAgentBot: async ({ commit }, id) => {
commit(types.SET_PLATFORM_UI_FLAG, { isDeleting: true });
try {
await PlatformAPI.deleteAgentBot(id);
commit(types.DELETE_PLATFORM_AGENT_BOT, id);
} catch (error) {
throwErrorMessage(error);
} finally {
commit(types.SET_PLATFORM_UI_FLAG, { isDeleting: false });
}
},
// ── Installation Config ───────────────────────────────
fetchInstallationConfigs: async ({ commit }, params) => {
commit(types.SET_PLATFORM_UI_FLAG, { isFetchingConfigs: true });
try {
const response = await PlatformAPI.getInstallationConfigs(params);
const payload = response.data?.data || response.data || [];
commit(types.SET_INSTALLATION_CONFIGS, payload);
return payload;
} catch (error) {
throwErrorMessage(error);
} finally {
commit(types.SET_PLATFORM_UI_FLAG, { isFetchingConfigs: false });
}
return null;
},
createInstallationConfig: async ({ commit }, configData) => {
commit(types.SET_PLATFORM_UI_FLAG, { isCreating: true });
try {
const response = await PlatformAPI.createInstallationConfig(configData);
const payload = response.data?.data || response.data;
commit(types.ADD_INSTALLATION_CONFIG, payload);
return payload;
} catch (error) {
throwErrorMessage(error);
} finally {
commit(types.SET_PLATFORM_UI_FLAG, { isCreating: false });
}
return null;
},
updateInstallationConfig: async ({ commit }, { id, data }) => {
commit(types.SET_PLATFORM_UI_FLAG, { isUpdating: true });
try {
const response = await PlatformAPI.updateInstallationConfig(id, data);
commit(types.EDIT_INSTALLATION_CONFIG, response.data);
return response.data;
} catch (error) {
throwErrorMessage(error);
} finally {
commit(types.SET_PLATFORM_UI_FLAG, { isUpdating: false });
}
return null;
},
deleteInstallationConfig: async ({ commit }, id) => {
commit(types.SET_PLATFORM_UI_FLAG, { isDeleting: true });
try {
await PlatformAPI.deleteInstallationConfig(id);
commit(types.DELETE_INSTALLATION_CONFIG, id);
} catch (error) {
throwErrorMessage(error);
} finally {
commit(types.SET_PLATFORM_UI_FLAG, { isDeleting: false });
}
},
// ── Banners ───────────────────────────────────────────
fetchBanners: async ({ commit }, params) => {
commit(types.SET_PLATFORM_UI_FLAG, { isFetchingBanners: true });
try {
const response = await PlatformAPI.getBanners(params);
const payload = response.data?.data || response.data || [];
commit(types.SET_PLATFORM_BANNERS, payload);
return payload;
} catch (error) {
throwErrorMessage(error);
} finally {
commit(types.SET_PLATFORM_UI_FLAG, { isFetchingBanners: false });
}
return null;
},
createBanner: async ({ commit }, bannerData) => {
commit(types.SET_PLATFORM_UI_FLAG, { isCreating: true });
try {
const response = await PlatformAPI.createBanner(bannerData);
const payload = response.data?.data || response.data;
commit(types.ADD_PLATFORM_BANNER, payload);
return payload;
} catch (error) {
throwErrorMessage(error);
} finally {
commit(types.SET_PLATFORM_UI_FLAG, { isCreating: false });
}
return null;
},
updateBanner: async ({ commit }, { id, data }) => {
commit(types.SET_PLATFORM_UI_FLAG, { isUpdating: true });
try {
const response = await PlatformAPI.updateBanner(id, data);
commit(types.EDIT_PLATFORM_BANNER, response.data);
return response.data;
} catch (error) {
throwErrorMessage(error);
} finally {
commit(types.SET_PLATFORM_UI_FLAG, { isUpdating: false });
}
return null;
},
deleteBanner: async ({ commit }, id) => {
commit(types.SET_PLATFORM_UI_FLAG, { isDeleting: true });
try {
await PlatformAPI.deleteBanner(id);
commit(types.DELETE_PLATFORM_BANNER, id);
} catch (error) {
throwErrorMessage(error);
} finally {
commit(types.SET_PLATFORM_UI_FLAG, { isDeleting: false });
}
},
};
export const mutations = {
[types.SET_PLATFORM_UI_FLAG]($state, data) {
$state.uiFlags = { ...$state.uiFlags, ...data };
},
// Accounts
[types.SET_PLATFORM_ACCOUNTS]($state, data) {
$state.accounts = Array.isArray(data) ? data : data?.payload || [];
},
[types.ADD_PLATFORM_ACCOUNT]($state, account) {
$state.accounts.unshift(account);
},
[types.EDIT_PLATFORM_ACCOUNT]($state, account) {
const idx = $state.accounts.findIndex(a => a.id === account.id);
if (idx !== -1) $state.accounts[idx] = account;
},
[types.DELETE_PLATFORM_ACCOUNT]($state, id) {
$state.accounts = $state.accounts.filter(a => a.id !== id);
},
// Users
[types.SET_PLATFORM_USERS]($state, data) {
$state.users = Array.isArray(data) ? data : data?.payload || [];
},
[types.ADD_PLATFORM_USER]($state, user) {
$state.users.unshift(user);
},
[types.EDIT_PLATFORM_USER]($state, user) {
const idx = $state.users.findIndex(u => u.id === user.id);
if (idx !== -1) $state.users[idx] = user;
},
[types.DELETE_PLATFORM_USER]($state, id) {
$state.users = $state.users.filter(u => u.id !== id);
},
// Agent Bots
[types.SET_PLATFORM_AGENT_BOTS]($state, data) {
$state.agentBots = Array.isArray(data) ? data : data?.payload || [];
},
[types.ADD_PLATFORM_AGENT_BOT]($state, bot) {
$state.agentBots.unshift(bot);
},
[types.EDIT_PLATFORM_AGENT_BOT]($state, bot) {
const idx = $state.agentBots.findIndex(b => b.id === bot.id);
if (idx !== -1) $state.agentBots[idx] = bot;
},
[types.DELETE_PLATFORM_AGENT_BOT]($state, id) {
$state.agentBots = $state.agentBots.filter(b => b.id !== id);
},
// Installation Configs
[types.SET_INSTALLATION_CONFIGS]($state, data) {
$state.installationConfigs = Array.isArray(data)
? data
: data?.payload || [];
},
[types.ADD_INSTALLATION_CONFIG]($state, config) {
$state.installationConfigs.unshift(config);
},
[types.EDIT_INSTALLATION_CONFIG]($state, config) {
const idx = $state.installationConfigs.findIndex(
c => c.id === config.id
);
if (idx !== -1) $state.installationConfigs[idx] = config;
},
[types.DELETE_INSTALLATION_CONFIG]($state, id) {
$state.installationConfigs = $state.installationConfigs.filter(
c => c.id !== id
);
},
};
export default {
namespaced: true,
actions,
state,
getters,
mutations,
};
@@ -392,4 +392,23 @@ export default {
EDIT_AGENT_CAPACITY_POLICIES_INBOXES: 'EDIT_AGENT_CAPACITY_POLICIES_INBOXES',
DELETE_AGENT_CAPACITY_POLICIES_INBOXES:
'DELETE_AGENT_CAPACITY_POLICIES_INBOXES',
// Platform (Super Admin)
SET_PLATFORM_UI_FLAG: 'SET_PLATFORM_UI_FLAG',
SET_PLATFORM_ACCOUNTS: 'SET_PLATFORM_ACCOUNTS',
ADD_PLATFORM_ACCOUNT: 'ADD_PLATFORM_ACCOUNT',
EDIT_PLATFORM_ACCOUNT: 'EDIT_PLATFORM_ACCOUNT',
DELETE_PLATFORM_ACCOUNT: 'DELETE_PLATFORM_ACCOUNT',
SET_PLATFORM_USERS: 'SET_PLATFORM_USERS',
ADD_PLATFORM_USER: 'ADD_PLATFORM_USER',
EDIT_PLATFORM_USER: 'EDIT_PLATFORM_USER',
DELETE_PLATFORM_USER: 'DELETE_PLATFORM_USER',
SET_PLATFORM_AGENT_BOTS: 'SET_PLATFORM_AGENT_BOTS',
ADD_PLATFORM_AGENT_BOT: 'ADD_PLATFORM_AGENT_BOT',
EDIT_PLATFORM_AGENT_BOT: 'EDIT_PLATFORM_AGENT_BOT',
DELETE_PLATFORM_AGENT_BOT: 'DELETE_PLATFORM_AGENT_BOT',
SET_INSTALLATION_CONFIGS: 'SET_INSTALLATION_CONFIGS',
ADD_INSTALLATION_CONFIG: 'ADD_INSTALLATION_CONFIG',
EDIT_INSTALLATION_CONFIG: 'EDIT_INSTALLATION_CONFIG',
DELETE_INSTALLATION_CONFIG: 'DELETE_INSTALLATION_CONFIG',
};