refactor(web): 统一前端轮询组合式函数并发布 v1.5.4
This commit is contained in:
@@ -353,7 +353,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, h, onMounted, onUnmounted, ref } from 'vue'
|
||||
import { computed, h, onMounted, ref } from 'vue'
|
||||
import { NButton, NDataTable, NIcon, NSpin, NTag, useMessage, type DataTableColumns } from 'naive-ui'
|
||||
import {
|
||||
AlertCircleOutline,
|
||||
@@ -375,6 +375,7 @@ import {
|
||||
} from '@vicons/ionicons5'
|
||||
import bannerImg from '../assets/brand/banner.png'
|
||||
import { api, asList } from '../api/client'
|
||||
import { useVisibilityPolling } from '../composables/useVisibilityPolling'
|
||||
import type {
|
||||
AccessLogEntry,
|
||||
CertificateRecord,
|
||||
@@ -440,8 +441,6 @@ const trafficByRule = ref<Record<number, ProxyTraffic>>({})
|
||||
type RatePoint = { at: number; upload: number; download: number }
|
||||
const rateHistory = ref<RatePoint[]>([])
|
||||
const RATE_HISTORY_MS = 5 * 60 * 1000
|
||||
let trafficTimer: ReturnType<typeof setInterval> | null = null
|
||||
let statusTimer: ReturnType<typeof setInterval> | null = null
|
||||
|
||||
// 实时流量的数据源:本机(Fonu 管理的本机 Nginx 访问日志)或公网(frps 侧的逐规则计数)
|
||||
type TrafficSource = 'local' | 'public'
|
||||
@@ -898,18 +897,6 @@ async function ensureFrpProxyNames() {
|
||||
}
|
||||
}
|
||||
|
||||
function startTrafficPoll() {
|
||||
stopTrafficPoll()
|
||||
trafficTimer = setInterval(refreshTraffic, 2000)
|
||||
}
|
||||
|
||||
function stopTrafficPoll() {
|
||||
if (trafficTimer) {
|
||||
clearInterval(trafficTimer)
|
||||
trafficTimer = null
|
||||
}
|
||||
}
|
||||
|
||||
// CPU 占用率需要两次采样才有意义,因此 /api/status 每 15 秒刷新一次
|
||||
async function refreshStatus() {
|
||||
try {
|
||||
@@ -919,29 +906,6 @@ async function refreshStatus() {
|
||||
}
|
||||
}
|
||||
|
||||
function startStatusPoll() {
|
||||
stopStatusPoll()
|
||||
statusTimer = setInterval(refreshStatus, 15000)
|
||||
}
|
||||
|
||||
function stopStatusPoll() {
|
||||
if (statusTimer) {
|
||||
clearInterval(statusTimer)
|
||||
statusTimer = null
|
||||
}
|
||||
}
|
||||
|
||||
// 页面隐藏时暂停轮询,避免后台标签页持续请求
|
||||
function handleVisibilityChange() {
|
||||
if (document.hidden) {
|
||||
stopTrafficPoll()
|
||||
stopStatusPoll()
|
||||
return
|
||||
}
|
||||
startTrafficPoll()
|
||||
startStatusPoll()
|
||||
}
|
||||
|
||||
// 系统资源:/api/status 返回的宿主机 CPU / 内存 / 磁盘(容器内为宿主机视角)
|
||||
const resourcesAvailable = computed(
|
||||
() => (status.value?.mem_total_bytes ?? 0) > 0 || (status.value?.disk_total_bytes ?? 0) > 0,
|
||||
@@ -1033,27 +997,20 @@ async function loadChinaCIDR() {
|
||||
}
|
||||
|
||||
// 中国 IP 段更新是后端后台任务:刷新后按 2 秒轮询直到就绪或结束
|
||||
let cidrPollTimer: ReturnType<typeof setInterval> | null = null
|
||||
|
||||
function stopCIDRPoll() {
|
||||
if (cidrPollTimer) {
|
||||
clearInterval(cidrPollTimer)
|
||||
cidrPollTimer = null
|
||||
}
|
||||
}
|
||||
const cidrPolling = ref(false)
|
||||
|
||||
async function pollCIDR() {
|
||||
await loadChinaCIDR()
|
||||
const data = chinaCIDR.value
|
||||
if (!data || data.ready || !data.updating) {
|
||||
stopCIDRPoll()
|
||||
cidrPolling.value = false
|
||||
if (data?.ready) message.success('中国 IP 段已更新')
|
||||
}
|
||||
}
|
||||
|
||||
async function refreshCIDR() {
|
||||
refreshingCIDR.value = true
|
||||
stopCIDRPoll()
|
||||
cidrPolling.value = false
|
||||
try {
|
||||
chinaCIDR.value = await api.refreshChinaCIDR()
|
||||
if (chinaCIDR.value.ready) {
|
||||
@@ -1061,7 +1018,7 @@ async function refreshCIDR() {
|
||||
`中国 IP 段已更新:IPv4 ${chinaCIDR.value.entry_count_v4} 条,IPv6 ${chinaCIDR.value.entry_count_v6} 条`,
|
||||
)
|
||||
} else if (chinaCIDR.value.updating) {
|
||||
cidrPollTimer = setInterval(() => void pollCIDR(), 2000)
|
||||
cidrPolling.value = true
|
||||
} else {
|
||||
message.warning('更新完成,但数据尚未就绪,请查看错误信息')
|
||||
}
|
||||
@@ -1188,18 +1145,11 @@ async function load() {
|
||||
|
||||
onMounted(() => {
|
||||
load()
|
||||
refreshTraffic()
|
||||
startTrafficPoll()
|
||||
startStatusPoll()
|
||||
document.addEventListener('visibilitychange', handleVisibilityChange)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
stopTrafficPoll()
|
||||
stopStatusPoll()
|
||||
stopCIDRPoll()
|
||||
document.removeEventListener('visibilitychange', handleVisibilityChange)
|
||||
})
|
||||
useVisibilityPolling(refreshTraffic, 2000, { immediate: true })
|
||||
useVisibilityPolling(refreshStatus, 15000, { immediate: true })
|
||||
useVisibilityPolling(pollCIDR, 2000, { enabled: cidrPolling })
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
</template>
|
||||
</PageHeader>
|
||||
|
||||
<n-alert type="success" :bordered="false" class="example-alert" title="v1.5.3 示例页面验证标识已生效">
|
||||
<n-alert type="success" :bordered="false" class="example-alert" title="v1.5.4 示例页面验证标识已生效">
|
||||
当前页面由 v{{ version }} 提供,验证标识为 <span class="mono">{{ verificationMarker }}</span>。
|
||||
如果你是在升级后首次看到这行内容,说明前端构建、镜像重建和服务重启均已成功。
|
||||
</n-alert>
|
||||
@@ -19,7 +19,7 @@
|
||||
<strong class="example-proof__value">{{ verificationRound }}</strong>
|
||||
</div>
|
||||
<div class="example-proof__hint">
|
||||
看到这个新区块,说明前端资源已经从旧版本切换到 v1.5.3。
|
||||
看到这个新区块,说明前端资源已经从旧版本切换到 v1.5.4。
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -33,16 +33,16 @@
|
||||
<StatCard label="本次验证标识" :value="verificationMarker" sub="用于区分升级前后的前端资源" tone="blue" value-small>
|
||||
<template #icon><n-icon :component="FlashOutline" /></template>
|
||||
</StatCard>
|
||||
<StatCard label="页面状态" value="已更新" sub="v1.5.3 前端资源已加载" tone="teal">
|
||||
<StatCard label="页面状态" value="已更新" sub="v1.5.4 前端资源已加载" tone="teal">
|
||||
<template #icon><n-icon :component="CheckmarkCircleOutline" /></template>
|
||||
</StatCard>
|
||||
</div>
|
||||
|
||||
<FonuCard title="本次示例页变化" subtitle="用于确认升级后的页面内容确实发生变化">
|
||||
<ul class="example-changes">
|
||||
<li>页面版本提示更新为 v1.5.3。</li>
|
||||
<li>「本次升级验证」区块和页面状态同步更新为 v1.5.3。</li>
|
||||
<li>验证标识更新为 <span class="mono">v1.5.3-example-page</span>。</li>
|
||||
<li>页面版本提示更新为 v1.5.4。</li>
|
||||
<li>「本次升级验证」区块和页面状态同步更新为 v1.5.4。</li>
|
||||
<li>验证标识更新为 <span class="mono">v1.5.4-example-page</span>。</li>
|
||||
</ul>
|
||||
</FonuCard>
|
||||
|
||||
@@ -87,8 +87,8 @@ const message = useMessage()
|
||||
const version = ref(__APP_VERSION__)
|
||||
const checkedAt = ref('')
|
||||
const refreshing = ref(false)
|
||||
const verificationRound = 'v1.5.3'
|
||||
const verificationMarker = 'v1.5.3-example-page'
|
||||
const verificationRound = 'v1.5.4'
|
||||
const verificationMarker = 'v1.5.4-example-page'
|
||||
|
||||
async function refreshVersion() {
|
||||
refreshing.value = true
|
||||
|
||||
+11
-24
@@ -642,7 +642,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, h, onMounted, onUnmounted, reactive, ref, type VNode } from 'vue'
|
||||
import { computed, h, onMounted, reactive, ref, type VNode } from 'vue'
|
||||
import type { DataTableColumns } from 'naive-ui'
|
||||
import type { FrpProxyTraffic, FrpProxiesTraffic } from '../api/types'
|
||||
import { NButton, NIcon, NSpace, NSwitch, NTag, useDialog, useMessage } from 'naive-ui'
|
||||
@@ -652,12 +652,12 @@ import {
|
||||
} from '@vicons/ionicons5'
|
||||
import { RouterLink } from 'vue-router'
|
||||
import { api, asList } from '../api/client'
|
||||
import { useVisibilityPolling } from '../composables/useVisibilityPolling'
|
||||
import { formatBytes, formatDate, formatRate } from '../utils/format'
|
||||
|
||||
import type { FrpBinary, FrpDownloadTask, FrpProxy, FrpProxyOptions, FrpProxyPayload, FrpRelease, FrpRuntimeInfo, FrpServer, FrpStatus } from '../api/types'
|
||||
import FonuCard from '../components/FonuCard.vue'
|
||||
import EmptyState from '../components/EmptyState.vue'
|
||||
import { useVisibilityPolling } from '../composables/useVisibilityPolling'
|
||||
import LoadError from '../components/LoadError.vue'
|
||||
import PageHeader from '../components/PageHeader.vue'
|
||||
import StatusBadge from '../components/StatusBadge.vue'
|
||||
@@ -699,7 +699,7 @@ const downloadProxyOptions = [
|
||||
{ label: 'cdn.gh-proxy.org', value: 'https://cdn.gh-proxy.org/' },
|
||||
]
|
||||
const editingProxy = ref<FrpProxy | null>(null)
|
||||
let downloadTimer: number | undefined
|
||||
const downloadPolling = ref(false)
|
||||
|
||||
function defaultProxyOptions(): FrpProxyOptions & { allow_ips_text?: string } {
|
||||
return { transport: {}, health_check: {}, load_balancer: {}, nat_traversal: { disable_assisted_addrs: false }, allow_ips_text: '' }
|
||||
@@ -865,7 +865,7 @@ const proxyLogsLoading = ref(false)
|
||||
|
||||
// 详情弹窗的实时速率采样,复用列表页 5 秒轮询更新的 proxyTraffic 快照
|
||||
const proxyRateHistory = ref<Array<{ at: number; upload: number; download: number }>>([])
|
||||
let proxyRateTimer: ReturnType<typeof setInterval> | null = null
|
||||
const proxyRatePolling = computed(() => showProxyDetail.value)
|
||||
|
||||
const proxyRateChart = computed(() => {
|
||||
const now = Date.now()
|
||||
@@ -909,14 +909,10 @@ function openProxyDetail(proxy: FrpProxy, tab: 'overview' | 'logs' = 'overview')
|
||||
proxyRateHistory.value = []
|
||||
recordProxyRateSample()
|
||||
if (tab === 'logs') void loadProxyLogs(proxy.id)
|
||||
// 弹窗打开期间每 5 秒补一个速率采样点,支撑最近 5 分钟实时流量图
|
||||
if (proxyRateTimer) window.clearInterval(proxyRateTimer)
|
||||
proxyRateTimer = window.setInterval(recordProxyRateSample, 5_000)
|
||||
}
|
||||
|
||||
function closeProxyDetail() {
|
||||
showProxyDetail.value = false
|
||||
if (proxyRateTimer) { window.clearInterval(proxyRateTimer); proxyRateTimer = null }
|
||||
}
|
||||
|
||||
async function loadProxyLogs(proxyID: number) {
|
||||
@@ -1045,7 +1041,7 @@ async function loadRuntimeConfig() {
|
||||
binaries.value = asList(nextBinaries)
|
||||
releases.value = asList(nextReleases)
|
||||
downloadTask.value = nextDownloadTask.status === 'completed' ? emptyDownloadTask() : nextDownloadTask
|
||||
ensureDownloadPolling()
|
||||
downloadPolling.value = downloadInProgress.value
|
||||
} catch (error) { message.error(error instanceof Error ? error.message : '读取 FRP 应用配置失败') }
|
||||
finally { runtimeLoading.value = false }
|
||||
}
|
||||
@@ -1078,21 +1074,16 @@ async function downloadRelease(version: string) {
|
||||
downloadingVersion.value = version
|
||||
try {
|
||||
downloadTask.value = await api.downloadFrpRelease(version)
|
||||
ensureDownloadPolling()
|
||||
downloadPolling.value = downloadInProgress.value
|
||||
} catch (error) { message.error(error instanceof Error ? error.message : '下载 FRP 客户端失败') }
|
||||
finally { downloadingVersion.value = null }
|
||||
}
|
||||
|
||||
function ensureDownloadPolling() {
|
||||
if (!downloadInProgress.value || downloadTimer) return
|
||||
downloadTimer = window.setInterval(refreshDownloadTask, 500)
|
||||
}
|
||||
|
||||
async function refreshDownloadTask() {
|
||||
try {
|
||||
downloadTask.value = await api.getFrpDownloadStatus()
|
||||
if (downloadInProgress.value) return
|
||||
stopDownloadPolling()
|
||||
downloadPolling.value = false
|
||||
if (downloadTask.value.status === 'completed') {
|
||||
message.success(`${downloadTask.value.version} 已下载并完成校验`)
|
||||
await loadRuntimeConfig()
|
||||
@@ -1101,17 +1092,11 @@ async function refreshDownloadTask() {
|
||||
}
|
||||
if (downloadTask.value.status === 'failed') message.error(downloadTask.value.error || '下载 FRP 客户端失败')
|
||||
} catch (error) {
|
||||
stopDownloadPolling()
|
||||
downloadPolling.value = false
|
||||
message.error(error instanceof Error ? error.message : '读取下载进度失败')
|
||||
}
|
||||
}
|
||||
|
||||
function stopDownloadPolling() {
|
||||
if (!downloadTimer) return
|
||||
window.clearInterval(downloadTimer)
|
||||
downloadTimer = undefined
|
||||
}
|
||||
|
||||
function emptyDownloadTask(): FrpDownloadTask {
|
||||
return { status: 'idle', message: '', downloaded: 0, total: 0 }
|
||||
}
|
||||
@@ -1211,7 +1196,9 @@ function proxyTransportTags(proxy: FrpProxy) {
|
||||
useVisibilityPolling(() => load(), 5_000)
|
||||
|
||||
onMounted(async () => { await load() })
|
||||
onUnmounted(() => { if (proxyRateTimer) { window.clearInterval(proxyRateTimer); proxyRateTimer = null } stopDownloadPolling() })
|
||||
|
||||
useVisibilityPolling(recordProxyRateSample, 5000, { enabled: proxyRatePolling })
|
||||
useVisibilityPolling(refreshDownloadTask, 500, { enabled: downloadPolling })
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@@ -162,7 +162,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, defineComponent, h, onMounted, onUnmounted, ref, watch } from 'vue'
|
||||
import { computed, defineComponent, h, onMounted, ref, watch } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import {
|
||||
NButton,
|
||||
@@ -177,6 +177,7 @@ import {
|
||||
type DataTableColumns,
|
||||
} from 'naive-ui'
|
||||
import { api, asList } from '../api/client'
|
||||
import { useVisibilityPolling } from '../composables/useVisibilityPolling'
|
||||
import type { AccessLogEntry, ProxyRule, SystemLogEntry } from '../api/types'
|
||||
import EmptyState from '../components/EmptyState.vue'
|
||||
import FonuCard from '../components/FonuCard.vue'
|
||||
@@ -296,7 +297,6 @@ function resolveTab(queryTab: unknown): LogTab {
|
||||
const tab = ref(resolveTab(route.query.tab))
|
||||
const pageError = ref('')
|
||||
const autoRefresh = ref(false)
|
||||
let refreshTimer: ReturnType<typeof setInterval> | null = null
|
||||
|
||||
const accessLogs = ref<AccessLogEntry[]>([])
|
||||
const proxyRules = ref<ProxyRule[]>([])
|
||||
@@ -802,10 +802,7 @@ watch([frpKeyword, frpLevel], () => {
|
||||
frpPage.value = 1
|
||||
})
|
||||
|
||||
watch(autoRefresh, (on) => {
|
||||
if (refreshTimer) clearInterval(refreshTimer)
|
||||
if (on) refreshTimer = setInterval(() => loadAccess(), 10000)
|
||||
})
|
||||
useVisibilityPolling(() => loadAccess(), 10000, { enabled: autoRefresh })
|
||||
|
||||
watch(
|
||||
() => route.query.tab,
|
||||
@@ -824,9 +821,6 @@ onMounted(() => {
|
||||
tab.value = resolveTab(route.query.tab)
|
||||
loadAll()
|
||||
})
|
||||
onUnmounted(() => {
|
||||
if (refreshTimer) clearInterval(refreshTimer)
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@@ -860,6 +860,7 @@ import {
|
||||
} from '@vicons/ionicons5'
|
||||
import { api, asList } from '../api/client'
|
||||
import type { ProxyClientConn, ProxyRule, ProxySavePayload, ProxyTraffic } from '../api/types'
|
||||
import { useVisibilityPolling } from '../composables/useVisibilityPolling'
|
||||
import EmptyState from '../components/EmptyState.vue'
|
||||
import FonuCard from '../components/FonuCard.vue'
|
||||
import ProxyAccessLogBox from '../components/ProxyAccessLogBox.vue'
|
||||
@@ -912,8 +913,6 @@ const clientsLoading = ref(false)
|
||||
type RatePoint = { at: number; upload: number; download: number }
|
||||
const rateHistory = ref<RatePoint[]>([])
|
||||
const RATE_HISTORY_MS = 5 * 60 * 1000
|
||||
let trafficTimer: ReturnType<typeof setInterval> | null = null
|
||||
let clientsTimer: ReturnType<typeof setInterval> | null = null
|
||||
|
||||
const nginxHttpPort = ref(80)
|
||||
const nginxHttpsPort = ref(443)
|
||||
@@ -1794,18 +1793,6 @@ async function refreshAll() {
|
||||
await Promise.all([load(), refreshTraffic()])
|
||||
}
|
||||
|
||||
function startTrafficPoll() {
|
||||
stopTrafficPoll()
|
||||
trafficTimer = setInterval(refreshTraffic, 2000)
|
||||
}
|
||||
|
||||
function stopTrafficPoll() {
|
||||
if (trafficTimer) {
|
||||
clearInterval(trafficTimer)
|
||||
trafficTimer = null
|
||||
}
|
||||
}
|
||||
|
||||
async function loadClients() {
|
||||
const rule = selectedRule.value
|
||||
if (!rule) return
|
||||
@@ -1819,25 +1806,8 @@ async function loadClients() {
|
||||
}
|
||||
}
|
||||
|
||||
function startClientsPoll() {
|
||||
stopClientsPoll()
|
||||
void loadClients()
|
||||
clientsTimer = setInterval(loadClients, 5000)
|
||||
}
|
||||
|
||||
function stopClientsPoll() {
|
||||
if (clientsTimer) {
|
||||
clearInterval(clientsTimer)
|
||||
clientsTimer = null
|
||||
}
|
||||
}
|
||||
|
||||
watch([showDetailPanel, selectedRuleId, detailTab], ([visible, id, tab]) => {
|
||||
if (!visible || !id || tab !== 'overview') {
|
||||
stopClientsPoll()
|
||||
return
|
||||
}
|
||||
startClientsPoll()
|
||||
if (visible && id && tab === 'overview') void loadClients()
|
||||
})
|
||||
|
||||
async function loadDefaults() {
|
||||
@@ -2191,17 +2161,17 @@ watch(
|
||||
onMounted(async () => {
|
||||
await loadDefaults()
|
||||
await load()
|
||||
await refreshTraffic()
|
||||
startTrafficPoll()
|
||||
})
|
||||
onUnmounted(() => {
|
||||
destroyRowSortable()
|
||||
stopLogStream()
|
||||
stopTrafficPoll()
|
||||
stopClientsPoll()
|
||||
document.removeEventListener('keydown', onLogFullscreenKeydown)
|
||||
document.body.style.overflow = ''
|
||||
})
|
||||
|
||||
const clientsPolling = computed(() => showDetailPanel.value && !!selectedRuleId.value && detailTab.value === 'overview')
|
||||
useVisibilityPolling(refreshTraffic, 2000, { immediate: true })
|
||||
useVisibilityPolling(loadClients, 5000, { enabled: clientsPolling })
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@@ -799,7 +799,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, onBeforeUnmount, onMounted, reactive, ref } from 'vue'
|
||||
import { computed, onMounted, reactive, ref } from 'vue'
|
||||
import {
|
||||
NButton,
|
||||
NIcon,
|
||||
@@ -825,6 +825,7 @@ import {
|
||||
SunnyOutline,
|
||||
} from '@vicons/ionicons5'
|
||||
import { api } from '../api/client'
|
||||
import { useVisibilityPolling } from '../composables/useVisibilityPolling'
|
||||
import type {
|
||||
ApiToken,
|
||||
BackupArchive,
|
||||
@@ -1055,7 +1056,7 @@ const globalIPWhitelistText = ref('')
|
||||
const chinaCIDRHours = ref(24)
|
||||
const refreshingCIDR = ref(false)
|
||||
const chinaCIDR = ref<ChinaCIDRStatus>({ entry_count_v4: 0, entry_count_v6: 0 })
|
||||
let chinaCIDRPollTimer: ReturnType<typeof setInterval> | undefined
|
||||
const chinaCIDRPolling = ref(false)
|
||||
|
||||
const globalNginxFramework = ref('')
|
||||
const globalNginxGeneratedSnippet = ref('')
|
||||
@@ -1335,31 +1336,15 @@ async function resetGlobalNginxAuto() {
|
||||
async function loadChinaCIDRStatus() {
|
||||
try {
|
||||
chinaCIDR.value = await api.getChinaCIDRStatus()
|
||||
if (chinaCIDR.value.updating) {
|
||||
startChinaCIDRPoll()
|
||||
} else {
|
||||
stopChinaCIDRPoll()
|
||||
}
|
||||
chinaCIDRPolling.value = Boolean(chinaCIDR.value.updating)
|
||||
} catch {
|
||||
// optional on load
|
||||
}
|
||||
}
|
||||
|
||||
function stopChinaCIDRPoll() {
|
||||
if (chinaCIDRPollTimer) {
|
||||
clearInterval(chinaCIDRPollTimer)
|
||||
chinaCIDRPollTimer = undefined
|
||||
}
|
||||
}
|
||||
|
||||
function startChinaCIDRPoll() {
|
||||
if (chinaCIDRPollTimer) return
|
||||
chinaCIDRPollTimer = setInterval(() => void loadChinaCIDRStatus(), 2000)
|
||||
}
|
||||
|
||||
async function refreshChinaCIDR() {
|
||||
refreshingCIDR.value = true
|
||||
stopChinaCIDRPoll()
|
||||
chinaCIDRPolling.value = false
|
||||
try {
|
||||
chinaCIDR.value = await api.refreshChinaCIDR()
|
||||
if (chinaCIDR.value.ready) {
|
||||
@@ -1377,10 +1362,6 @@ async function refreshChinaCIDR() {
|
||||
}
|
||||
}
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
stopChinaCIDRPoll()
|
||||
})
|
||||
|
||||
function applyNotifySettings(settings: Record<string, string>) {
|
||||
notifyType.value = (settings.notify_type as NotifyType) || ''
|
||||
notifySMTPPassword.value = ''
|
||||
@@ -1770,6 +1751,8 @@ onMounted(() => {
|
||||
void loadApiTokens()
|
||||
void loadBackupArchives()
|
||||
})
|
||||
|
||||
useVisibilityPolling(loadChinaCIDRStatus, 2000, { enabled: chinaCIDRPolling })
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
Reference in New Issue
Block a user