Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion css/serverinfo-main.css
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
/* extracted by css-entry-points-plugin */
@import './main-DtGZuPjJ.chunk.css';
@import './main-CNs9NLij.chunk.css';
24 changes: 12 additions & 12 deletions js/serverinfo-main.mjs

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion js/serverinfo-main.mjs.map

Large diffs are not rendered by default.

28 changes: 25 additions & 3 deletions src/composables/useLiveData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ import { onUnmounted, ref } from 'vue'
export function useLiveData<T>(url: string, intervalMs = 2000) {
const data = ref<T | null>(null) as Ref<T | null>
const tick = ref(0)
/** Consecutive failed requests; 0 means the values on screen are current. */
const failures = ref(0)
const lastUpdated = ref<Date | null>(null)

const MAX_BACKOFF_MS = 60_000

let timeoutId: ReturnType<typeof setTimeout> | null = null
let stopped = false
Expand All @@ -31,8 +36,12 @@ export function useLiveData<T>(url: string, intervalMs = 2000) {
const response = await axios.get(generateUrl(url))
data.value = response.data as T
tick.value++
failures.value = 0
lastUpdated.value = new Date()
} catch {
// Keep previous values on error
// The previous values stay on screen deliberately, but silently: it is
// `failures` that lets the page admit they have stopped being current.
failures.value++
} finally {
inFlight = false
schedule()
Expand All @@ -47,7 +56,20 @@ export function useLiveData<T>(url: string, intervalMs = 2000) {
return
}

timeoutId = setTimeout(poll, intervalMs)
timeoutId = setTimeout(poll, delay())
}

/**
* Back off while the server is unreachable. Polling a server that is down
* every two seconds gains nothing and costs it a full request cycle each
* time; the interval returns to normal on the first success.
*/
function delay(): number {
if (failures.value === 0) {
return intervalMs
}

return Math.min(intervalMs * 2 ** failures.value, MAX_BACKOFF_MS)
}

/**
Expand Down Expand Up @@ -80,5 +102,5 @@ export function useLiveData<T>(url: string, intervalMs = 2000) {
}
})

return { data, tick }
return { data, tick, failures, lastUpdated }
}
5 changes: 4 additions & 1 deletion src/composables/useStaticData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ export function useStaticData() {
const error = ref(false)

async function load() {
loading.value = true
error.value = false

try {
const response = await axios.get(generateUrl('/apps/serverinfo/data'))
data.value = response.data
Expand All @@ -74,5 +77,5 @@ export function useStaticData() {

load()

return { data, loading, error }
return { data, loading, error, reload: load }
}
42 changes: 38 additions & 4 deletions src/views/SettingsAdmin.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,33 @@
-->
<template>
<div class="server-info-wrapper">
<NcNoteCard v-if="staticError" type="error" class="server-info-notice">
<!-- TRANSLATORS: Shown when the server information could not be fetched at all -->
<p>{{ t('serverinfo', 'The server information could not be loaded.') }}</p>
<NcButton @click="reloadStatic">
<!-- TRANSLATORS: Button that fetches the server information again after it failed -->
{{ t('serverinfo', 'Try again') }}
</NcButton>
</NcNoteCard>
<NcNoteCard v-else-if="isStale" type="warning" class="server-info-notice">
<template v-if="liveUpdatedAt">
<!-- TRANSLATORS: Warning that the numbers on screen have stopped refreshing. {time} is a clock time. -->
{{ t('serverinfo', 'These values stopped updating at {time}: the server is not responding.', { time: liveUpdatedAt.toLocaleTimeString() }) }}
</template>
<template v-else>
<!-- TRANSLATORS: Warning shown when the live values never arrived at all -->
{{ t('serverinfo', 'The live values could not be loaded: the server is not responding.') }}
</template>
</NcNoteCard>

<SettingsNavigation v-model="view" />

<!-- v-show rather than v-if: switching pages must not throw away the
chart history useLiveData has been accumulating since page load. -->
<StatusView
v-show="view === 'status'"
:staticData="staticData"
:staticError="staticError"
:liveData="liveData"
:tick="tick" />
<BackgroundJobsView
Expand All @@ -22,7 +42,10 @@
<script setup lang="ts">
import type { LiveData, PeriodicData, SettingsView } from '../types.ts'

import { ref } from 'vue'
import { t } from '@nextcloud/l10n'
import { computed, ref } from 'vue'
import NcButton from '@nextcloud/vue/components/NcButton'
import NcNoteCard from '@nextcloud/vue/components/NcNoteCard'
import SettingsNavigation from '../components/SettingsNavigation.vue'
import BackgroundJobsView from './BackgroundJobsView.vue'
import StatusView from './StatusView.vue'
Expand All @@ -33,7 +56,18 @@ defineOptions({ name: 'ServerInfo' })

const view = ref<SettingsView>('status')

const { data: staticData } = useStaticData()
const { data: liveData, tick } = useLiveData<LiveData>('/apps/serverinfo/update')
const { data: periodicData } = useLiveData<PeriodicData>('/apps/serverinfo/periodic', 60000)
const { data: staticData, error: staticError, reload: reloadStatic } = useStaticData()
const { data: liveData, tick, failures: liveFailures, lastUpdated: liveUpdatedAt } = useLiveData<LiveData>('/apps/serverinfo/update')
const { data: periodicData, failures: periodicFailures } = useLiveData<PeriodicData>('/apps/serverinfo/periodic', 60000)

// One failed request is usually a blip worth riding out; two in a row means the
// figures on screen have quietly stopped being current, and saying nothing
// would leave a monitoring page looking healthy while the server is not.
const isStale = computed(() => liveFailures.value >= 2 || periodicFailures.value >= 2)
</script>

<style scoped>
.server-info-notice {
margin: 12px 0;
}
</style>
3 changes: 2 additions & 1 deletion src/views/StatusView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@
<MonitoringSection :ocs="staticData.ocs" />
</template>

<template v-else>
<template v-else-if="!staticError">
<div class="section server-infos-two">
<div class="row">
<div>
Expand Down Expand Up @@ -167,6 +167,7 @@ import ThermalSection from '../components/ThermalSection.vue'

const props = defineProps<{
staticData: StaticData | null
staticError: boolean
liveData: LiveData | null
tick: number
}>()
Expand Down
Loading