new ping
Some checks are pending
Test / build (lts/*, ubuntu-latest) (push) Waiting to run
Test / build (lts/*, windows-latest) (push) Waiting to run

This commit is contained in:
Dustella 2025-02-20 15:44:09 +08:00
parent 4fc70895fa
commit 1d4892a076
Signed by: Dustella
GPG Key ID: 35AA0AA3DC402D5C
4 changed files with 202 additions and 52 deletions

View File

@ -1,7 +1,15 @@
<script setup lang="ts">
import { useBackendOnline } from './composables'
// import { useBackendOnline } from './composables'
import { useAPIOnline } from './composables/online'
const online = useBackendOnline()
const online = useAPIOnline()
onMounted(() => {
online.connect()
})
onUnmounted(() => {
online.disconnect()
})
</script>
<template>

View File

@ -34,7 +34,7 @@ import {
SidebarRail,
} from '~/components/ui/sidebar'
import { authCode, useBackendOnline } from '../composables'
import { authCode } from '../composables'
const router = useRouter()
@ -99,8 +99,6 @@ onMounted(() => {
router.push('/auth')
}
})
const online = useBackendOnline()
</script>
<template>
@ -113,7 +111,7 @@ const online = useBackendOnline()
<Sidebar collapsible="icon">
<SidebarHeader>
<Alert>
<AlertTitle>服务状态正常 {{ online.isOnline }}</AlertTitle>
<AlertTitle>服务状态正常 </AlertTitle>
</Alert>
</SidebarHeader>
<SidebarContent>

View File

@ -25,50 +25,50 @@ export const baseFetch = createFetch({
})
export async function doCheckOnline() {
let resp = null
try {
resp = await baseFetch('/ping', {
timeout: 2000,
})
const { error } = resp
if (error.value)
throw new Error(error.value)
const status = resp.response.value?.status
if (!status) {
return false
}
if (status === 200) {
return true
}
else if (status >= 400 && status < 500) {
return true
}
return false
}
catch (err) {
if (resp?.response.value?.status === 401) {
return true
}
console.error(err)
return false
}
}
// export async function doCheckOnline() {
// let resp = null
// try {
// resp = await baseFetch('/ping', {
// timeout: 2000,
// })
// const { error } = resp
// if (error.value)
// throw new Error(error.value)
// const status = resp.response.value?.status
// if (!status) {
// return false
// }
// if (status === 200) {
// return true
// }
// else if (status >= 400 && status < 500) {
// return true
// }
// return false
// }
// catch (err) {
// if (resp?.response.value?.status === 401) {
// return true
// }
// console.error(err)
// return false
// }
// }
export const useBackendOnline = createSharedComposable(() => {
const isOnline = ref(true)
// export const useBackendOnline = createSharedComposable(() => {
// const isOnline = ref(true)
const useCheckInterval = useIntervalFn(() => {
doCheckOnline().then((online) => {
isOnline.value = online
})
}, 5000, {
immediate: true,
immediateCallback: true,
})
useCheckInterval.resume()
// const useCheckInterval = useIntervalFn(() => {
// doCheckOnline().then((online) => {
// isOnline.value = online
// })
// }, 5000, {
// immediate: true,
// immediateCallback: true,
// })
// useCheckInterval.resume()
return {
isOnline,
}
})
// return {
// isOnline,
// }
// })

View File

@ -1,3 +1,147 @@
export const hasConnection = ref(false)
import { useIntervalFn } from '@vueuse/core'
import { API_BASE_URL } from '~/CONSTANT'
// get `https://`
interface UseAPIOnlineOptions {
/**
* WebSocket endpoint URL
*/
url?: string
/**
* (ms)
* @default 30000
*/
heartbeatInterval?: number
/**
* (ms)
* @default 5000
*/
reconnectDelay?: number
/**
*
*/
onConnected?: () => void
/**
*
*/
onDisconnected?: () => void
/**
*
*/
onError?: (error: any) => void
}
export function useAPIOnline(options: UseAPIOnlineOptions = {}) {
const {
url = `ws://${API_BASE_URL.replace('https://', '').replace('http://', '')}/ping/ws`,
heartbeatInterval = 30000,
reconnectDelay = 5000,
onConnected,
onDisconnected,
onError,
} = options
const ws = ref<WebSocket | null>(null)
const isOnline = ref(false)
const isConnecting = ref(false)
// 最后一次收到pong的时间戳
const lastPongTime = ref(0)
// 检查是否需要重连的定时器
const { pause: pauseHealthCheck, resume: resumeHealthCheck } = useIntervalFn(() => {
const now = Date.now()
// 如果45秒没有收到pong认为连接断开
if (now - lastPongTime.value > 45000) {
isOnline.value = false
reconnect()
}
}, 15000)
// 心跳定时器
const { pause: pauseHeartbeat, resume: resumeHeartbeat } = useIntervalFn(() => {
if (ws.value?.readyState === WebSocket.OPEN) {
ws.value.send(JSON.stringify({ type: 'ping' }))
}
}, heartbeatInterval)
const connect = () => {
if (isConnecting.value || (ws.value?.readyState === WebSocket.CONNECTING)) {
return
}
isConnecting.value = true
// 清理旧的连接
if (ws.value) {
ws.value.close()
ws.value = null
}
const socket = new WebSocket(url)
socket.onopen = () => {
isOnline.value = true
isConnecting.value = false
lastPongTime.value = Date.now()
resumeHeartbeat()
resumeHealthCheck()
onConnected?.()
}
socket.onclose = () => {
isOnline.value = false
isConnecting.value = false
pauseHeartbeat()
pauseHealthCheck()
onDisconnected?.()
setTimeout(reconnect, reconnectDelay)
}
socket.onerror = (error) => {
isOnline.value = false
isConnecting.value = false
pauseHeartbeat()
pauseHealthCheck()
onError?.(error)
}
socket.onmessage = (event) => {
try {
const data = JSON.parse(event.data)
if (data.type === 'pong') {
lastPongTime.value = Date.now()
}
}
catch (e) {
console.error('Failed to parse websocket message:', e)
}
}
ws.value = socket
}
function reconnect() {
if (!isOnline.value && !isConnecting.value) {
connect()
}
}
const disconnect = () => {
if (ws.value) {
ws.value.close()
ws.value = null
}
isOnline.value = false
isConnecting.value = false
pauseHeartbeat()
pauseHealthCheck()
}
return {
isOnline,
isConnecting,
connect,
disconnect,
reconnect,
}
}