feat(instanceDetails): replace API logs with WebSocket for real-time updates
Implement WebSocket connection for instance logs to enable real-time updates instead of periodic API polling. Remove deprecated API endpoints and update log display styling with level-specific backgrounds. Add cleanup on component unmount to prevent memory leaks.
This commit is contained in:
@@ -86,8 +86,6 @@ export const instanceApi = {
|
|||||||
api.get(`/frpcAct/instanceMgr/getInfo?instanceID=${instanceID}`),
|
api.get(`/frpcAct/instanceMgr/getInfo?instanceID=${instanceID}`),
|
||||||
getInstanceStatus: (instanceID) =>
|
getInstanceStatus: (instanceID) =>
|
||||||
api.get(`/frpcAct/instanceMgr/status?instanceID=${instanceID}`),
|
api.get(`/frpcAct/instanceMgr/status?instanceID=${instanceID}`),
|
||||||
getInstanceLogs: (instanceID) =>
|
|
||||||
api.post('/frpcAct/instanceMgr/logs', { instanceID }),
|
|
||||||
getInstanceProxies: (instanceID) =>
|
getInstanceProxies: (instanceID) =>
|
||||||
api.get(`/frpcAct/proxyMgr/list?instanceID=${instanceID}`),
|
api.get(`/frpcAct/proxyMgr/list?instanceID=${instanceID}`),
|
||||||
createProxy: (instanceID, proxyInfo) =>
|
createProxy: (instanceID, proxyInfo) =>
|
||||||
@@ -109,9 +107,6 @@ export const systemInfoApi = {
|
|||||||
export default api;
|
export default api;
|
||||||
|
|
||||||
export const TODO_API_NOTES = {
|
export const TODO_API_NOTES = {
|
||||||
instanceLogs: 'TODO: /frpcAct/instanceMgr/logs - 获取实例日志 API 未在 api-backend.md 中定义',
|
|
||||||
instanceProxies: 'TODO: /frpcAct/instanceMgr/proxies - 获取实例代理列表 API 未在 api-backend.md 中定义',
|
|
||||||
logList: 'TODO: /logMgr/list - 获取系统日志 API 未在 api-backend.md 中定义',
|
|
||||||
monitorStats: 'TODO: /monitor/stats - 获取系统监控数据 API 未在 api-backend.md 中定义',
|
monitorStats: 'TODO: /monitor/stats - 获取系统监控数据 API 未在 api-backend.md 中定义',
|
||||||
systemInfo: 'TODO: /system/info - 获取系统信息 API 未在 api-backend.md 中定义'
|
systemInfo: 'TODO: /system/info - 获取系统信息 API 未在 api-backend.md 中定义'
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -272,7 +272,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { ref, onMounted } from 'vue';
|
import { ref, onMounted, onUnmounted } from 'vue';
|
||||||
import { useRoute, useRouter } from 'vue-router';
|
import { useRoute, useRouter } from 'vue-router';
|
||||||
import { instanceApi } from '../api/index.js';
|
import { instanceApi } from '../api/index.js';
|
||||||
import { showNotification, formatDate, getCookie } from '../utils/functions.js';
|
import { showNotification, formatDate, getCookie } from '../utils/functions.js';
|
||||||
@@ -395,17 +395,64 @@ export default {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let logSocket = null;
|
||||||
|
|
||||||
|
const connectLogWebSocket = () => {
|
||||||
|
const token = getCookie('token');
|
||||||
|
const wsProtocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
|
||||||
|
const target = __APP_TARGET__;
|
||||||
|
const wsUrl = `${wsProtocol}//${target}/frpcAct/instanceMgr/logs?instanceID=${instanceID.value}&token=${token}`;
|
||||||
|
|
||||||
|
logSocket = new WebSocket(wsUrl);
|
||||||
|
|
||||||
|
logSocket.onopen = () => {
|
||||||
|
console.log(`Connected to instance ${instanceID.value} log server`);
|
||||||
|
};
|
||||||
|
|
||||||
|
logSocket.onmessage = (event) => {
|
||||||
|
try {
|
||||||
|
const log = JSON.parse(event.data);
|
||||||
|
logs.value.unshift({
|
||||||
|
time: log.timestamp,
|
||||||
|
level: log.level,
|
||||||
|
message: log.content
|
||||||
|
});
|
||||||
|
if (logs.value.length > 100) {
|
||||||
|
logs.value.pop();
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error parsing log message:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
logSocket.onclose = () => {
|
||||||
|
console.log(`Disconnected from instance ${instanceID.value} log server`);
|
||||||
|
};
|
||||||
|
|
||||||
|
logSocket.onerror = (error) => {
|
||||||
|
console.error('WebSocket error:', error);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const disconnectLogWebSocket = () => {
|
||||||
|
if (logSocket) {
|
||||||
|
logSocket.close();
|
||||||
|
logSocket = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const loadLogs = async () => {
|
const loadLogs = async () => {
|
||||||
try {
|
try {
|
||||||
const result = await instanceApi.getInstanceLogs(instanceID.value);
|
const result = await instanceApi.getInstanceInfo(instanceID.value);
|
||||||
logs.value = result.data.map(log => ({
|
logs.value = result.data.logs ? result.data.logs.map(log => ({
|
||||||
time: formatDate(log.timestamp),
|
time: formatDate(log.timestamp),
|
||||||
level: log.level,
|
level: log.level,
|
||||||
message: log.message
|
message: log.content
|
||||||
}));
|
})) : [];
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
showNotification('Load logs failed', 'error');
|
showNotification('Load logs failed', 'error');
|
||||||
}
|
}
|
||||||
|
connectLogWebSocket();
|
||||||
};
|
};
|
||||||
|
|
||||||
const goBack = () => {
|
const goBack = () => {
|
||||||
@@ -524,6 +571,10 @@ export default {
|
|||||||
await loadLogs();
|
await loadLogs();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
disconnectLogWebSocket();
|
||||||
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
instanceID,
|
instanceID,
|
||||||
instanceName,
|
instanceName,
|
||||||
@@ -647,18 +698,33 @@ export default {
|
|||||||
.log-level {
|
.log-level {
|
||||||
min-width: 60px;
|
min-width: 60px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
|
padding: 2px 8px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.log-level.DEBUG {
|
||||||
|
color: #52c41a;
|
||||||
|
background: #f6ffed;
|
||||||
}
|
}
|
||||||
|
|
||||||
.log-level.INFO {
|
.log-level.INFO {
|
||||||
color: #1890ff;
|
color: #1890ff;
|
||||||
|
background: #e6f7ff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.log-level.WARN {
|
.log-level.WARN {
|
||||||
color: #faad14;
|
color: #faad14;
|
||||||
|
background: #fffbe6;
|
||||||
}
|
}
|
||||||
|
|
||||||
.log-level.ERROR {
|
.log-level.ERROR {
|
||||||
color: #ff4d4f;
|
color: #ff4d4f;
|
||||||
|
background: #fff1f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.log-level.FATAL {
|
||||||
|
color: #cf1322;
|
||||||
|
background: #fff1f0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.empty-state {
|
.empty-state {
|
||||||
|
|||||||
Reference in New Issue
Block a user