forked from didirus/AstralRinth
620894aecb
* feat: card alignment + fix modals * feat: change admon title in restore alert modal * fix: lint * feat: backups queue api into api-client * feat: impl backup queue api endpoints into frontend * feat: ack fix * feat: bulk actions * feat: bulk delete impl * fix: lint * fix: align error states * fix: transition group * feat: ready for qa * fix: lint * feat: qa * feat: stacked admonitions component * fix: issues with stacking * feat: hook up admonition stacking + fix app csp for staging kyros nodes * fix: logs.vue * qa: close stack on admonitions click * fix: all problems with stacked admonitions * qa: admonition cleanup and copy overhaul draft * fix: qa issues padding * fix: padding bug * feat: qa * fix: intercom in app csp bug * fix: positioning intercom * feat: loading overlay on top of console + admon consistency changes * feat: scroll indicator fade in backup delete modal + admon timestamp fix * feat: move action bar behind modal * fix: lint + i18n * fix: server ping spam on filter (cache but clear on unmount) * fix: 1 admon fade in flicker issue * chore: temp staging undo * qa: changes * fix: lint * chore: revert staging to use staging * fix: scoping
134 lines
3.8 KiB
TypeScript
134 lines
3.8 KiB
TypeScript
import type { Archon } from '@modrinth/api-client'
|
|
import { useQuery, useQueryClient } from '@tanstack/vue-query'
|
|
import { computed, reactive, type Ref } from 'vue'
|
|
|
|
import { type BusyReason, injectModrinthClient } from '#ui/providers'
|
|
|
|
import { defineMessage } from './i18n'
|
|
|
|
type ProgressKey = `${string}:${'create' | 'restore'}`
|
|
|
|
export function useServerBackupsQueue(serverId: Ref<string>, worldId: Ref<string | null>) {
|
|
const client = injectModrinthClient()
|
|
const queryClient = useQueryClient()
|
|
|
|
const queryKey = computed(() => ['backups', 'queue', serverId.value] as const)
|
|
|
|
const progressOverlay = reactive(new Map<ProgressKey, number>())
|
|
const lastSeenState = new Map<ProgressKey, Archon.Websocket.v0.BackupState>()
|
|
|
|
const query = useQuery({
|
|
queryKey,
|
|
queryFn: () => client.archon.backups_queue_v1.list(serverId.value, worldId.value!),
|
|
enabled: computed(() => !!worldId.value),
|
|
refetchInterval: (q) => {
|
|
const data = q.state.data as Archon.BackupsQueue.v1.BackupsQueueResponse | undefined
|
|
return data?.active_operations?.length ? 3000 : false
|
|
},
|
|
})
|
|
|
|
const data = computed(() => query.data.value)
|
|
const activeOperations = computed(() => data.value?.active_operations ?? [])
|
|
const backups = computed(() =>
|
|
[...(data.value?.backups ?? [])].sort(
|
|
(a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime(),
|
|
),
|
|
)
|
|
|
|
const activeOperationByBackupId = computed(() => {
|
|
const map = new Map<string, Archon.BackupsQueue.v1.ActiveOperation>()
|
|
for (const op of activeOperations.value) map.set(op.backup_id, op)
|
|
return map
|
|
})
|
|
const backupById = computed(() => {
|
|
const map = new Map<string, Archon.BackupsQueue.v1.BackupQueueBackup>()
|
|
for (const backup of backups.value) map.set(backup.id, backup)
|
|
return map
|
|
})
|
|
|
|
const hasActiveCreate = computed(() =>
|
|
activeOperations.value.some((o) => o.operation_type === 'create' && !o.has_parent),
|
|
)
|
|
const hasActiveRestore = computed(() =>
|
|
activeOperations.value.some((o) => o.operation_type === 'restore'),
|
|
)
|
|
const hasRunningCreate = computed(() =>
|
|
activeOperations.value.some(
|
|
(o) =>
|
|
o.operation_type === 'create' &&
|
|
!o.has_parent &&
|
|
backupById.value.get(o.backup_id)?.status === 'in_progress',
|
|
),
|
|
)
|
|
const hasRunningRestore = computed(() =>
|
|
activeOperations.value.some(
|
|
(o) =>
|
|
o.operation_type === 'restore' &&
|
|
backupById.value.get(o.backup_id)?.status === 'in_progress',
|
|
),
|
|
)
|
|
|
|
function handleWsBackupProgress(evt: Archon.Websocket.v0.WSBackupProgressEvent) {
|
|
if (evt.task === 'file') return
|
|
const key = `${evt.id}:${evt.task}` as ProgressKey
|
|
|
|
if (evt.state === 'ongoing') {
|
|
progressOverlay.set(key, evt.progress)
|
|
} else {
|
|
progressOverlay.delete(key)
|
|
}
|
|
|
|
const prev = lastSeenState.get(key)
|
|
if (prev !== evt.state) {
|
|
lastSeenState.set(key, evt.state)
|
|
queryClient.invalidateQueries({ queryKey: queryKey.value })
|
|
}
|
|
}
|
|
|
|
function progressFor(backupId: string, kind: 'create' | 'restore'): number | undefined {
|
|
return progressOverlay.get(`${backupId}:${kind}`)
|
|
}
|
|
|
|
const busyReasons = computed<BusyReason[]>(() => {
|
|
const reasons: BusyReason[] = []
|
|
if (hasRunningCreate.value) {
|
|
reasons.push({
|
|
reason: defineMessage({
|
|
id: 'servers.busy.backup-creating',
|
|
defaultMessage: 'Backup creation in progress',
|
|
}),
|
|
})
|
|
}
|
|
if (hasRunningRestore.value) {
|
|
reasons.push({
|
|
reason: defineMessage({
|
|
id: 'servers.busy.backup-restoring',
|
|
defaultMessage: 'Backup restore in progress',
|
|
}),
|
|
})
|
|
}
|
|
return reasons
|
|
})
|
|
|
|
async function invalidate() {
|
|
await queryClient.invalidateQueries({ queryKey: queryKey.value })
|
|
}
|
|
|
|
return {
|
|
query,
|
|
queryKey,
|
|
data,
|
|
activeOperations,
|
|
activeOperationByBackupId,
|
|
backups,
|
|
hasActiveCreate,
|
|
hasActiveRestore,
|
|
hasRunningCreate,
|
|
hasRunningRestore,
|
|
progressFor,
|
|
handleWsBackupProgress,
|
|
busyReasons,
|
|
invalidate,
|
|
}
|
|
}
|