Skip to content
Open
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
20 changes: 20 additions & 0 deletions packages/api-client/src/modules/archon/backups-queue/v1.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,14 @@ export class ArchonBackupsQueueV1Module extends AbstractModule {
)
}

/** POST /v1/servers/:server_id/worlds/:world_id/backups-queue/history/create/:operation_id/cancel */
public async cancelCreate(serverId: string, worldId: string, operationId: number): Promise<void> {
await this.client.request<void>(
`/servers/${serverId}/worlds/${worldId}/backups-queue/history/create/${operationId}/cancel`,
{ api: 'archon', version: 1, method: 'POST' },
)
}

/** POST /v1/servers/:server_id/worlds/:world_id/backups-queue/history/restore/:operation_id/ack */
public async ackRestore(serverId: string, worldId: string, operationId: number): Promise<void> {
await this.client.request<void>(
Expand All @@ -45,6 +53,18 @@ export class ArchonBackupsQueueV1Module extends AbstractModule {
)
}

/** POST /v1/servers/:server_id/worlds/:world_id/backups-queue/history/restore/:operation_id/cancel */
public async cancelRestore(
serverId: string,
worldId: string,
operationId: number,
): Promise<void> {
await this.client.request<void>(
`/servers/${serverId}/worlds/${worldId}/backups-queue/history/restore/${operationId}/cancel`,
{ api: 'archon', version: 1, method: 'POST' },
)
}

/** DELETE /v1/servers/:server_id/worlds/:world_id/backups-queue/:backup_id */
public async delete(serverId: string, worldId: string, backupId: string): Promise<void> {
await this.client.request<void>(
Expand Down
3 changes: 2 additions & 1 deletion packages/api-client/src/modules/archon/backups/v1.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,8 @@ export class ArchonBackupsV1Module extends AbstractModule {
}

/**
* @deprecated Use `client.archon.backups_queue_v1.delete` instead.
* @deprecated Use `client.archon.backups_queue_v1.delete` for backup deletion, or
* `client.archon.backups_queue_v1.cancelCreate` / `cancelRestore` for active operations.
*/
/** DELETE /v1/servers/:server_id/worlds/:world_id/backups/:backup_id */
public async delete(serverId: string, worldId: string, backupId: string): Promise<void> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -314,7 +314,21 @@ async function onBackupCancel(item: BackupAdmonitionEntry) {
if (cancellingIds.has(item.key)) return
cancellingIds.add(item.key)
try {
await client.archon.backups_v1.delete(ctx.serverId, ctx.worldId.value!, item.backupId)
if (item.operationId == null) {
await client.archon.backups_v1.delete(ctx.serverId, ctx.worldId.value!, item.backupId)
} else if (item.type === 'create') {
await client.archon.backups_queue_v1.cancelCreate(
ctx.serverId,
ctx.worldId.value!,
item.operationId,
)
} else {
await client.archon.backups_queue_v1.cancelRestore(
ctx.serverId,
ctx.worldId.value!,
item.operationId,
)
}
await invalidate()
} catch (err) {
cancellingIds.delete(item.key)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ export function useInlineBackup(backupName: string | (() => string)) {
)

const createdBackupId = ref<string | null>(null)
const createdOperationId = ref<number | null>(null)
const pendingCreate = ref(false)
const backupFailed = ref(false)
const backupComplete = ref(false)
Expand Down Expand Up @@ -102,6 +103,16 @@ export function useInlineBackup(backupName: string | (() => string)) {
{ immediate: true },
)

watch(
myActiveOp,
(op) => {
if (op?.operation_id != null) {
createdOperationId.value = op.operation_id
}
},
{ immediate: true },
)

async function startBackup() {
if (!worldId.value) return

Expand All @@ -112,6 +123,7 @@ export function useInlineBackup(backupName: string | (() => string)) {
backupCancelled.value = false
isCancelling.value = false
createdBackupId.value = null
createdOperationId.value = null
pendingCreate.value = true

try {
Expand All @@ -137,7 +149,18 @@ export function useInlineBackup(backupName: string | (() => string)) {

isCancelling.value = true
try {
await client.archon.backups_v1.delete(serverId, worldId.value, createdBackupId.value)
let operationId = createdOperationId.value ?? myActiveOp.value?.operation_id ?? null
if (operationId == null) {
const queue = await client.archon.backups_queue_v1.list(serverId, worldId.value)
const operation = queue.active_operations.find(
(op) => op.backup_id === createdBackupId.value && op.operation_type === 'create',
)
operationId = operation?.operation_id ?? null
}
if (operationId == null) {
throw new Error('Could not find the backup creation operation to cancel.')
}
await client.archon.backups_queue_v1.cancelCreate(serverId, worldId.value, operationId)
backupCancelled.value = true
isCancelling.value = false
await invalidate()
Expand Down
65 changes: 44 additions & 21 deletions packages/ui/src/layouts/wrapped/hosting/manage/backups.vue
Original file line number Diff line number Diff line change
Expand Up @@ -347,10 +347,11 @@ const serverId = route.params.id as string

defineEmits(['onDownload'])

const { backups, invalidate, hasActiveCreate, hasActiveRestore, query } = useServerBackupsQueue(
computed(() => serverId),
worldId,
)
const { backups, invalidate, activeOperationByBackupId, hasActiveCreate, hasActiveRestore, query } =
useServerBackupsQueue(
computed(() => serverId),
worldId,
)

const error = computed(() => {
const err = query.error.value
Expand Down Expand Up @@ -386,17 +387,37 @@ const deleteQueueMutation = useMutation({
},
})

/** In-progress / incomplete backups: legacy cancel + delete path. */
const deleteLegacyMutation = useMutation({
mutationFn: (backupId: string) =>
client.archon.backups_v1.delete(serverId, worldId.value!, backupId),
const cancelOperationMutation = useMutation({
mutationFn: async (backup: Archon.BackupsQueue.v1.BackupQueueBackup) => {
const activeOperation = activeOperationByBackupId.value.get(backup.id)
const historyOperation = backup.history.find(
(op) => (op.state === 'pending' || op.state === 'ongoing') && op.operation_id != null,
)
const operation = activeOperation?.operation_id != null ? activeOperation : historyOperation

if (operation?.operation_id == null) {
await client.archon.backups_v1.delete(serverId, worldId.value!, backup.id)
} else if (operation.operation_type === 'create') {
await client.archon.backups_queue_v1.cancelCreate(
serverId,
worldId.value!,
operation.operation_id,
)
} else {
await client.archon.backups_queue_v1.cancelRestore(
serverId,
worldId.value!,
operation.operation_id,
)
}
},
onSuccess: async () => {
await invalidate()
await queryClient.invalidateQueries({ queryKey: ['servers', 'detail', serverId] })
},
})

/** Bulk delete via queue API — handles both completed and in-progress backups (cancels the latter). */
/** Bulk delete selected backups via queue API. */
const deleteManyMutation = useMutation({
mutationFn: (backupIds: string[]) =>
client.archon.backups_queue_v1.deleteMany(serverId, worldId.value!, backupIds),
Expand Down Expand Up @@ -557,18 +578,20 @@ function deleteBackup(backup?: Archon.BackupsQueue.v1.BackupQueueBackup) {
return
}

const mutation = useQueueDeleteFor(backup) ? deleteQueueMutation : deleteLegacyMutation

mutation.mutate(backup.id, {
onError: (err) => {
const message = err instanceof Error ? err.message : String(err)
addNotification({
type: 'error',
title: 'Error deleting backup',
text: message,
})
},
})
const onError = (err: unknown) => {
const message = err instanceof Error ? err.message : String(err)
addNotification({
type: 'error',
title: 'Error deleting backup',
text: message,
})
}

if (useQueueDeleteFor(backup)) {
deleteQueueMutation.mutate(backup.id, { onError })
} else {
cancelOperationMutation.mutate(backup, { onError })
}
}
</script>

Expand Down
Loading