mirror of
https://github.com/modrinth/code.git
synced 2026-09-03 05:25:58 +00:00
feat: impl delete world + move onboarding reset to server scope
This commit is contained in:
@@ -79,6 +79,18 @@ export class ArchonServersV1Module extends AbstractModule {
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a world
|
||||
* DELETE /v1/servers/:id/worlds/:wid
|
||||
*/
|
||||
public async deleteWorld(serverId: string, worldId: string): Promise<void> {
|
||||
await this.client.request(`/servers/${serverId}/worlds/${worldId}`, {
|
||||
api: 'archon',
|
||||
version: 1,
|
||||
method: 'DELETE',
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* End the intro flow for a server
|
||||
* DELETE /v1/servers/:id/flows/intro
|
||||
@@ -109,14 +121,19 @@ export class ArchonServersV1Module extends AbstractModule {
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset a world to onboarding
|
||||
* POST /v1/servers/:id/worlds/:wid/onboard
|
||||
* Reset a server to onboarding
|
||||
* POST /v1/servers/:id/onboard
|
||||
*/
|
||||
public async resetToOnboarding(serverId: string, worldId: string): Promise<void> {
|
||||
await this.client.request(`/servers/${serverId}/worlds/${worldId}/onboard`, {
|
||||
api: 'archon',
|
||||
version: 1,
|
||||
method: 'POST',
|
||||
})
|
||||
public async resetToOnboarding(
|
||||
serverId: string,
|
||||
): Promise<Archon.Servers.v1.ServerOnboardResponse> {
|
||||
return this.client.request<Archon.Servers.v1.ServerOnboardResponse>(
|
||||
`/servers/${serverId}/onboard`,
|
||||
{
|
||||
api: 'archon',
|
||||
version: 1,
|
||||
method: 'POST',
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -732,6 +732,10 @@ export namespace Archon {
|
||||
worlds: WorldFull[]
|
||||
}
|
||||
|
||||
export type ServerOnboardResponse = {
|
||||
archived_worlds: string[]
|
||||
}
|
||||
|
||||
export type ServerResources = {
|
||||
cpu: number
|
||||
memory_mb: number
|
||||
|
||||
@@ -18,7 +18,7 @@ import {
|
||||
TextCursorInputIcon,
|
||||
TrashIcon,
|
||||
} from '@modrinth/assets'
|
||||
import { computed, ref, watch } from 'vue'
|
||||
import { computed, nextTick, ref, watch } from 'vue'
|
||||
|
||||
import ButtonStyled from '#ui/components/base/ButtonStyled.vue'
|
||||
import EmptyState from '#ui/components/base/EmptyState.vue'
|
||||
@@ -389,10 +389,11 @@ async function promptDeleteItems(items: ContentItem[], event?: MouseEvent) {
|
||||
showDeletionConfirmation(event)
|
||||
}
|
||||
|
||||
function showDeletionConfirmation(event?: MouseEvent) {
|
||||
async function showDeletionConfirmation(event?: MouseEvent) {
|
||||
if ((event?.shiftKey || skipNonEssentialWarnings.value) && !ctx.isBusy.value) {
|
||||
confirmDelete()
|
||||
} else {
|
||||
await nextTick()
|
||||
confirmDeletionModal.value?.show()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,17 @@
|
||||
<template>
|
||||
<div class="relative h-full w-full">
|
||||
<Teleport to="body">
|
||||
<div class="relative z-[100]">
|
||||
<ConfirmModal
|
||||
ref="resetToOnboardingModal"
|
||||
:title="formatMessage(messages.resetToOnboardingModalTitle)"
|
||||
:description="formatMessage(messages.resetToOnboardingModalDescription)"
|
||||
:proceed-label="formatMessage(messages.resetToOnboardingButton)"
|
||||
@proceed="confirmResetToOnboarding"
|
||||
/>
|
||||
</div>
|
||||
</Teleport>
|
||||
|
||||
<div v-if="data" class="flex h-full w-full flex-col">
|
||||
<div class="flex flex-col gap-6">
|
||||
<div class="flex justify-start gap-16">
|
||||
@@ -131,6 +143,30 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="isSiteAdmin" class="flex flex-col gap-2.5">
|
||||
<span class="font-semibold text-contrast">
|
||||
{{ formatMessage(messages.supportZoneLabel) }}
|
||||
</span>
|
||||
<div class="flex flex-col gap-4 rounded-2xl border border-solid border-surface-5 p-4">
|
||||
<div class="flex flex-col items-start gap-2.5">
|
||||
<ButtonStyled color="blue">
|
||||
<button
|
||||
v-tooltip="supportResetToOnboardingTooltip"
|
||||
class="!shadow-none"
|
||||
:disabled="supportResetToOnboardingDisabled"
|
||||
@click="showResetToOnboardingModal"
|
||||
>
|
||||
<RotateCounterClockwiseIcon class="size-5" />
|
||||
{{ formatMessage(messages.resetToOnboardingButton) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<span class="text-primary">
|
||||
{{ formatMessage(messages.resetToOnboardingDescription) }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else />
|
||||
@@ -146,14 +182,18 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { Labrinth } from '@modrinth/api-client'
|
||||
import { RotateCounterClockwiseIcon } from '@modrinth/assets'
|
||||
import { useQuery, useQueryClient } from '@tanstack/vue-query'
|
||||
import { useStorage } from '@vueuse/core'
|
||||
import { computed, ref, watch } from 'vue'
|
||||
|
||||
import { CopyCode, StyledInput, Toggle } from '#ui/components'
|
||||
import { ButtonStyled, ConfirmModal, CopyCode, StyledInput, Toggle } from '#ui/components'
|
||||
import EditServerIcon from '#ui/components/servers/edit-server-icon/EditServerIcon.vue'
|
||||
import SaveBanner from '#ui/components/servers/SaveBanner.vue'
|
||||
import { useModrinthServersConsole } from '#ui/composables'
|
||||
import { defineMessages, useVIntl } from '#ui/composables/i18n'
|
||||
import { useServerPermissions } from '#ui/composables/server-permissions'
|
||||
import { injectServerSettings } from '#ui/layouts/shared/server-settings'
|
||||
import {
|
||||
injectModrinthClient,
|
||||
injectModrinthServerContext,
|
||||
@@ -161,18 +201,60 @@ import {
|
||||
injectPageContext,
|
||||
} from '#ui/providers'
|
||||
|
||||
const { formatMessage } = useVIntl()
|
||||
const { addNotification } = injectNotificationManager()
|
||||
const client = injectModrinthClient()
|
||||
const { server: data, serverId, busyReasons } = injectModrinthServerContext()
|
||||
const { featureFlags } = injectPageContext()
|
||||
const queryClient = useQueryClient()
|
||||
const { canUseAdvancedSettings, canWriteFiles, permissionDeniedMessage } = useServerPermissions()
|
||||
const serverSettings = injectServerSettings()
|
||||
const modrinthServersConsole = useModrinthServersConsole()
|
||||
const { canResetServer, canUseAdvancedSettings, canWriteFiles, permissionDeniedMessage } =
|
||||
useServerPermissions()
|
||||
const advancedActionTooltip = computed(() =>
|
||||
canUseAdvancedSettings.value ? undefined : permissionDeniedMessage.value,
|
||||
)
|
||||
|
||||
const serverName = ref(data.value?.name)
|
||||
const serverSubdomain = ref(data.value?.net?.domain ?? '')
|
||||
const resetToOnboardingModal = ref<InstanceType<typeof ConfirmModal>>()
|
||||
const isResettingToOnboarding = ref(false)
|
||||
|
||||
const messages = defineMessages({
|
||||
supportZoneLabel: {
|
||||
id: 'server.settings.general.support-zone.label',
|
||||
defaultMessage: 'Support zone',
|
||||
},
|
||||
resetToOnboardingButton: {
|
||||
id: 'hosting.loader.reset-to-onboarding-button',
|
||||
defaultMessage: 'Reset to onboarding',
|
||||
},
|
||||
resetToOnboardingDescription: {
|
||||
id: 'server.settings.general.reset-to-onboarding.description',
|
||||
defaultMessage: 'Send this server back into onboarding so setup can be completed again.',
|
||||
},
|
||||
resetToOnboardingModalTitle: {
|
||||
id: 'hosting.loader.reset-to-onboarding-modal-title',
|
||||
defaultMessage: 'Reset to onboarding',
|
||||
},
|
||||
resetToOnboardingModalDescription: {
|
||||
id: 'server.settings.general.reset-to-onboarding.modal.description',
|
||||
defaultMessage:
|
||||
'This will send the server back into onboarding so setup can be completed again. Are you sure you want to continue?',
|
||||
},
|
||||
resetToOnboardingSuccessTitle: {
|
||||
id: 'server.settings.general.reset-to-onboarding.success.title',
|
||||
defaultMessage: 'Server reset to onboarding',
|
||||
},
|
||||
resetToOnboardingSuccessDescription: {
|
||||
id: 'server.settings.general.reset-to-onboarding.success.description',
|
||||
defaultMessage: 'The server has been returned to the onboarding flow.',
|
||||
},
|
||||
failedToResetToOnboarding: {
|
||||
id: 'server.settings.general.reset-to-onboarding.error',
|
||||
defaultMessage: 'Failed to reset server to onboarding',
|
||||
},
|
||||
})
|
||||
|
||||
watch(data, (newData) => {
|
||||
if (newData) {
|
||||
@@ -188,6 +270,13 @@ const isValidSubdomain = computed(() => isValidLengthSubdomain.value && isValidC
|
||||
|
||||
const isUpdating = ref(false)
|
||||
const isValidServerName = computed(() => (serverName.value?.length ?? 0) > 0)
|
||||
const isSiteAdmin = computed(() => serverSettings.currentUserRole.value === 'admin')
|
||||
const supportResetToOnboardingDisabled = computed(
|
||||
() => isResettingToOnboarding.value || !canResetServer.value,
|
||||
)
|
||||
const supportResetToOnboardingTooltip = computed(() =>
|
||||
!canResetServer.value ? permissionDeniedMessage.value : undefined,
|
||||
)
|
||||
|
||||
// Preferences
|
||||
const preferences = {
|
||||
@@ -418,4 +507,53 @@ const resetGeneral = () => {
|
||||
serverSubdomain.value = data.value?.net?.domain ?? ''
|
||||
newUserPreferences.value = { ...userPreferences.value }
|
||||
}
|
||||
|
||||
function showResetToOnboardingModal() {
|
||||
if (supportResetToOnboardingDisabled.value) return
|
||||
resetToOnboardingModal.value?.show()
|
||||
}
|
||||
|
||||
async function confirmResetToOnboarding() {
|
||||
if (supportResetToOnboardingDisabled.value) return
|
||||
|
||||
try {
|
||||
isResettingToOnboarding.value = true
|
||||
await client.archon.servers_v1.resetToOnboarding(serverId)
|
||||
modrinthServersConsole.clear()
|
||||
try {
|
||||
await client.kyros.logs_v1.clear()
|
||||
} catch (error) {
|
||||
console.error('Failed to clear server logs:', error)
|
||||
}
|
||||
data.value.flows = { intro: true }
|
||||
await invalidateServerState()
|
||||
addNotification({
|
||||
type: 'success',
|
||||
title: formatMessage(messages.resetToOnboardingSuccessTitle),
|
||||
text: formatMessage(messages.resetToOnboardingSuccessDescription),
|
||||
})
|
||||
serverSettings.closeModal?.()
|
||||
} catch (err) {
|
||||
addNotification({
|
||||
type: 'error',
|
||||
text: err instanceof Error ? err.message : formatMessage(messages.failedToResetToOnboarding),
|
||||
})
|
||||
} finally {
|
||||
isResettingToOnboarding.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function invalidateServerState() {
|
||||
return Promise.all([
|
||||
queryClient.invalidateQueries({ queryKey: ['servers', 'detail', serverId] }),
|
||||
queryClient.invalidateQueries({ queryKey: ['servers', 'v1', 'detail', serverId] }),
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: ['servers', 'worlds', 'summary', 'v1', serverId],
|
||||
}),
|
||||
queryClient.invalidateQueries({ queryKey: ['files', serverId] }),
|
||||
queryClient.invalidateQueries({ queryKey: ['content', 'list', 'v1', serverId] }),
|
||||
queryClient.invalidateQueries({ queryKey: ['servers', 'properties', 'v1', serverId] }),
|
||||
queryClient.invalidateQueries({ queryKey: ['servers', 'startup', 'v1', serverId] }),
|
||||
])
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -10,11 +10,11 @@
|
||||
@proceed="resetWorldFiles"
|
||||
/>
|
||||
<ConfirmModal
|
||||
ref="resetToOnboardingModal"
|
||||
:title="formatMessage(messages.resetToOnboardingModalTitle)"
|
||||
:description="formatMessage(messages.resetToOnboardingModalDescription)"
|
||||
:proceed-label="formatMessage(messages.resetToOnboardingButton)"
|
||||
@proceed="confirmResetToOnboarding"
|
||||
ref="deleteInstanceModal"
|
||||
:title="formatMessage(messages.deleteInstanceModalTitle)"
|
||||
:description="formatMessage(messages.deleteInstanceModalDescription)"
|
||||
:proceed-label="formatMessage(messages.deleteInstanceButton)"
|
||||
@proceed="deleteInstance"
|
||||
/>
|
||||
<ServerSetupModal
|
||||
ref="setupModal"
|
||||
@@ -71,28 +71,21 @@
|
||||
{{ formatMessage(messages.resetEverythingDescription) }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="isSiteAdmin" class="flex flex-col gap-2.5">
|
||||
<span class="font-semibold text-contrast">
|
||||
{{ formatMessage(messages.supportZoneLabel) }}
|
||||
</span>
|
||||
<div class="flex flex-col gap-4 rounded-2xl border border-solid border-surface-5 p-4">
|
||||
<div class="flex flex-col items-start gap-2.5">
|
||||
<ButtonStyled color="blue">
|
||||
<ButtonStyled color="red">
|
||||
<button
|
||||
v-tooltip="supportResetToOnboardingTooltip"
|
||||
v-tooltip="deleteInstanceDisabledTooltip"
|
||||
class="!shadow-none"
|
||||
:disabled="supportResetToOnboardingDisabled"
|
||||
@click="showResetToOnboardingModal"
|
||||
:disabled="isDeleteInstanceDisabled"
|
||||
@click="showDeleteInstanceModal"
|
||||
>
|
||||
<RotateCounterClockwiseIcon class="size-5" />
|
||||
{{ formatMessage(messages.resetToOnboardingButton) }}
|
||||
<TrashIcon class="size-5" />
|
||||
{{ formatMessage(messages.deleteInstanceButton) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<span class="text-primary">
|
||||
{{ formatMessage(messages.resetToOnboardingDescription) }}
|
||||
{{ formatMessage(messages.deleteInstanceDescription) }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -114,6 +107,7 @@
|
||||
import { RotateCounterClockwiseIcon, TrashIcon } from '@modrinth/assets'
|
||||
import { useMutation, useQuery, useQueryClient } from '@tanstack/vue-query'
|
||||
import { computed, ref, watch } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
|
||||
import { ButtonStyled, ConfirmModal, StyledInput } from '#ui/components'
|
||||
import SaveBanner from '#ui/components/servers/SaveBanner.vue'
|
||||
@@ -136,10 +130,12 @@ const serverSettings = injectServerSettings()
|
||||
const queryClient = useQueryClient()
|
||||
const modrinthServersConsole = useModrinthServersConsole()
|
||||
const { canUseAdvancedSettings, canResetServer, permissionDeniedMessage } = useServerPermissions()
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
|
||||
const setupModal = ref<InstanceType<typeof ServerSetupModal>>()
|
||||
const resetWorldFilesModal = ref<InstanceType<typeof ConfirmModal>>()
|
||||
const resetToOnboardingModal = ref<InstanceType<typeof ConfirmModal>>()
|
||||
const deleteInstanceModal = ref<InstanceType<typeof ConfirmModal>>()
|
||||
const editingInstanceName = ref('')
|
||||
|
||||
const messages = defineMessages({
|
||||
@@ -171,10 +167,6 @@ const messages = defineMessages({
|
||||
id: 'server.instance-settings.general.danger-zone.label',
|
||||
defaultMessage: 'Danger zone',
|
||||
},
|
||||
supportZoneLabel: {
|
||||
id: 'server.instance-settings.general.support-zone.label',
|
||||
defaultMessage: 'Support zone',
|
||||
},
|
||||
resetWorldFilesButton: {
|
||||
id: 'server.instance-settings.general.reset-world-files.button',
|
||||
defaultMessage: 'Reset world data',
|
||||
@@ -205,35 +197,6 @@ const messages = defineMessages({
|
||||
id: 'server.instance-settings.general.reset-world-files.error',
|
||||
defaultMessage: 'Failed to reset world data',
|
||||
},
|
||||
resetToOnboardingButton: {
|
||||
id: 'hosting.loader.reset-to-onboarding-button',
|
||||
defaultMessage: 'Reset to onboarding',
|
||||
},
|
||||
resetToOnboardingDescription: {
|
||||
id: 'hosting.loader.reset-to-onboarding-description',
|
||||
defaultMessage: 'Send this instance back into onboarding so setup can be completed again.',
|
||||
},
|
||||
resetToOnboardingModalTitle: {
|
||||
id: 'hosting.loader.reset-to-onboarding-modal-title',
|
||||
defaultMessage: 'Reset to onboarding',
|
||||
},
|
||||
resetToOnboardingModalDescription: {
|
||||
id: 'hosting.loader.reset-to-onboarding-modal-description',
|
||||
defaultMessage:
|
||||
'This will send the instance back into onboarding so setup can be completed again. Are you sure you want to continue?',
|
||||
},
|
||||
resetToOnboardingSuccessTitle: {
|
||||
id: 'hosting.loader.reset-to-onboarding-success-title',
|
||||
defaultMessage: 'Instance reset to onboarding',
|
||||
},
|
||||
resetToOnboardingSuccessDescription: {
|
||||
id: 'hosting.loader.reset-to-onboarding-success-description',
|
||||
defaultMessage: 'The instance has been returned to the onboarding flow.',
|
||||
},
|
||||
failedToResetToOnboarding: {
|
||||
id: 'hosting.loader.failed-to-reset-to-onboarding',
|
||||
defaultMessage: 'Failed to reset instance to onboarding',
|
||||
},
|
||||
resetEverythingButton: {
|
||||
id: 'server.instance-settings.general.reset-everything.button',
|
||||
defaultMessage: 'Reset everything',
|
||||
@@ -243,8 +206,47 @@ const messages = defineMessages({
|
||||
defaultMessage:
|
||||
'Reset your instance completely. This removes world data, content, and any configuration. A backup of the previous instance will remain available.',
|
||||
},
|
||||
deleteInstanceButton: {
|
||||
id: 'server.instance-settings.general.delete-instance.button',
|
||||
defaultMessage: 'Delete instance',
|
||||
},
|
||||
deleteInstanceDescription: {
|
||||
id: 'server.instance-settings.general.delete-instance.description',
|
||||
defaultMessage:
|
||||
'Permanently delete this instance, including its content, files, settings, and backups.',
|
||||
},
|
||||
deleteInstanceModalTitle: {
|
||||
id: 'server.instance-settings.general.delete-instance.modal.title',
|
||||
defaultMessage: 'Delete instance',
|
||||
},
|
||||
deleteInstanceModalDescription: {
|
||||
id: 'server.instance-settings.general.delete-instance.modal.description',
|
||||
defaultMessage:
|
||||
'This deletes the instance, including its content, files, settings, and backups. This cannot be undone.',
|
||||
},
|
||||
deleteInstanceSuccessTitle: {
|
||||
id: 'server.instance-settings.general.delete-instance.success.title',
|
||||
defaultMessage: 'Instance deleted',
|
||||
},
|
||||
deleteInstanceSuccessDescription: {
|
||||
id: 'server.instance-settings.general.delete-instance.success.description',
|
||||
defaultMessage: 'The instance has been deleted.',
|
||||
},
|
||||
deleteInstanceError: {
|
||||
id: 'server.instance-settings.general.delete-instance.error',
|
||||
defaultMessage: 'Failed to delete instance',
|
||||
},
|
||||
})
|
||||
|
||||
type WorldSummaryCacheItem = {
|
||||
id: string
|
||||
}
|
||||
|
||||
type DeleteInstanceOptimisticState = {
|
||||
serverFull: Archon.Servers.v1.ServerFull | undefined
|
||||
worldSummary: WorldSummaryCacheItem[] | undefined
|
||||
}
|
||||
|
||||
const serverFullQuery = useQuery({
|
||||
queryKey: ['servers', 'v1', 'detail', serverId],
|
||||
queryFn: () => client.archon.servers_v1.get(serverId),
|
||||
@@ -306,9 +308,87 @@ const resetWorldMutation = useMutation({
|
||||
},
|
||||
})
|
||||
|
||||
const deleteWorldMutation = useMutation({
|
||||
mutationFn: (deletedWorldId: string) =>
|
||||
client.archon.servers_v1.deleteWorld(serverId, deletedWorldId),
|
||||
onMutate: async (deletedWorldId): Promise<DeleteInstanceOptimisticState> => {
|
||||
const serverFullKey = ['servers', 'v1', 'detail', serverId] as const
|
||||
const worldSummaryKey = ['servers', 'worlds', 'summary', 'v1', serverId] as const
|
||||
|
||||
await Promise.all([
|
||||
queryClient.cancelQueries({ queryKey: serverFullKey }),
|
||||
queryClient.cancelQueries({ queryKey: worldSummaryKey }),
|
||||
])
|
||||
|
||||
const previousServerFull = queryClient.getQueryData<Archon.Servers.v1.ServerFull>(
|
||||
serverFullKey,
|
||||
)
|
||||
const previousWorldSummary = queryClient.getQueryData<WorldSummaryCacheItem[]>(
|
||||
worldSummaryKey,
|
||||
)
|
||||
|
||||
queryClient.setQueryData<Archon.Servers.v1.ServerFull | undefined>(
|
||||
serverFullKey,
|
||||
(previous) =>
|
||||
previous
|
||||
? {
|
||||
...previous,
|
||||
worlds: previous.worlds.filter((world) => world.id !== deletedWorldId),
|
||||
}
|
||||
: previous,
|
||||
)
|
||||
queryClient.setQueryData<WorldSummaryCacheItem[] | undefined>(
|
||||
worldSummaryKey,
|
||||
(previous) => previous?.filter((world) => world.id !== deletedWorldId),
|
||||
)
|
||||
queryClient.removeQueries({ queryKey: ['content', 'list', 'v1', serverId, deletedWorldId] })
|
||||
queryClient.removeQueries({
|
||||
queryKey: ['servers', 'properties', 'v1', serverId, deletedWorldId],
|
||||
})
|
||||
queryClient.removeQueries({
|
||||
queryKey: ['servers', 'startup', 'v1', serverId, deletedWorldId],
|
||||
})
|
||||
|
||||
serverSettings.closeModal?.()
|
||||
if (route.path !== instancesPath.value) {
|
||||
void router.push(instancesPath.value)
|
||||
}
|
||||
|
||||
return {
|
||||
serverFull: previousServerFull,
|
||||
worldSummary: previousWorldSummary,
|
||||
}
|
||||
},
|
||||
onSuccess: () => {
|
||||
addNotification({
|
||||
type: 'success',
|
||||
title: formatMessage(messages.deleteInstanceSuccessTitle),
|
||||
text: formatMessage(messages.deleteInstanceSuccessDescription),
|
||||
})
|
||||
},
|
||||
onError: (error, _deletedWorldId, context) => {
|
||||
if (context?.serverFull) {
|
||||
queryClient.setQueryData(['servers', 'v1', 'detail', serverId], context.serverFull)
|
||||
}
|
||||
|
||||
if (context?.worldSummary) {
|
||||
queryClient.setQueryData(
|
||||
['servers', 'worlds', 'summary', 'v1', serverId],
|
||||
context.worldSummary,
|
||||
)
|
||||
}
|
||||
|
||||
addNotification({
|
||||
type: 'error',
|
||||
text: error instanceof Error ? error.message : formatMessage(messages.deleteInstanceError),
|
||||
})
|
||||
},
|
||||
onSettled: (_data, _error, deletedWorldId) => {
|
||||
void invalidateServerState(deletedWorldId)
|
||||
},
|
||||
})
|
||||
|
||||
const isUpdatingName = computed(() => patchWorldMutation.isPending.value)
|
||||
const isSiteAdmin = computed(() => serverSettings.currentUserRole.value === 'admin')
|
||||
const isResettingToOnboarding = ref(false)
|
||||
const isNameSaveDisabled = computed(
|
||||
() => !worldId.value || isUpdatingName.value || !canUseAdvancedSettings.value,
|
||||
)
|
||||
@@ -323,12 +403,13 @@ const isResetWorldFilesDisabled = computed(
|
||||
const resetWorldFilesDisabledTooltip = computed(() =>
|
||||
canResetServer.value ? undefined : permissionDeniedMessage.value,
|
||||
)
|
||||
const supportResetToOnboardingDisabled = computed(
|
||||
() => !worldId.value || isResettingToOnboarding.value || !canResetServer.value,
|
||||
const isDeleteInstanceDisabled = computed(
|
||||
() => isResetDisabled.value || deleteWorldMutation.isPending.value || !canResetServer.value,
|
||||
)
|
||||
const supportResetToOnboardingTooltip = computed(() =>
|
||||
!canResetServer.value ? permissionDeniedMessage.value : undefined,
|
||||
const deleteInstanceDisabledTooltip = computed(() =>
|
||||
canResetServer.value ? undefined : permissionDeniedMessage.value,
|
||||
)
|
||||
const instancesPath = computed(() => `/hosting/manage/${encodeURIComponent(serverId)}/instances`)
|
||||
|
||||
watch(
|
||||
instanceName,
|
||||
@@ -359,42 +440,19 @@ function resetWorldFiles() {
|
||||
resetWorldMutation.mutate()
|
||||
}
|
||||
|
||||
function showResetToOnboardingModal() {
|
||||
if (supportResetToOnboardingDisabled.value) return
|
||||
resetToOnboardingModal.value?.show()
|
||||
function showDeleteInstanceModal() {
|
||||
if (isDeleteInstanceDisabled.value) return
|
||||
deleteInstanceModal.value?.show()
|
||||
}
|
||||
|
||||
async function confirmResetToOnboarding() {
|
||||
if (supportResetToOnboardingDisabled.value || !worldId.value) return
|
||||
function deleteInstance() {
|
||||
const deletedWorldId = worldId.value
|
||||
if (isDeleteInstanceDisabled.value || !deletedWorldId) return
|
||||
|
||||
try {
|
||||
isResettingToOnboarding.value = true
|
||||
await client.archon.servers_v1.resetToOnboarding(serverId, worldId.value)
|
||||
modrinthServersConsole.clear()
|
||||
try {
|
||||
await client.kyros.logs_v1.clear()
|
||||
} catch (error) {
|
||||
console.error('Failed to clear server logs:', error)
|
||||
}
|
||||
server.value.flows = { intro: true }
|
||||
await invalidateServerState()
|
||||
addNotification({
|
||||
type: 'success',
|
||||
title: formatMessage(messages.resetToOnboardingSuccessTitle),
|
||||
text: formatMessage(messages.resetToOnboardingSuccessDescription),
|
||||
})
|
||||
serverSettings.closeModal?.()
|
||||
} catch (err) {
|
||||
addNotification({
|
||||
type: 'error',
|
||||
text: err instanceof Error ? err.message : formatMessage(messages.failedToResetToOnboarding),
|
||||
})
|
||||
} finally {
|
||||
isResettingToOnboarding.value = false
|
||||
}
|
||||
deleteWorldMutation.mutate(deletedWorldId)
|
||||
}
|
||||
|
||||
function invalidateServerState() {
|
||||
function invalidateServerState(targetWorldId = worldId.value) {
|
||||
return Promise.all([
|
||||
queryClient.invalidateQueries({ queryKey: ['servers', 'detail', serverId] }),
|
||||
queryClient.invalidateQueries({ queryKey: ['servers', 'v1', 'detail', serverId] }),
|
||||
@@ -402,9 +460,19 @@ function invalidateServerState() {
|
||||
queryKey: ['servers', 'worlds', 'summary', 'v1', serverId],
|
||||
}),
|
||||
queryClient.invalidateQueries({ queryKey: ['files', serverId] }),
|
||||
worldId.value
|
||||
targetWorldId
|
||||
? queryClient.invalidateQueries({
|
||||
queryKey: ['content', 'list', 'v1', serverId, worldId.value],
|
||||
queryKey: ['content', 'list', 'v1', serverId, targetWorldId],
|
||||
})
|
||||
: Promise.resolve(),
|
||||
targetWorldId
|
||||
? queryClient.invalidateQueries({
|
||||
queryKey: ['servers', 'properties', 'v1', serverId, targetWorldId],
|
||||
})
|
||||
: Promise.resolve(),
|
||||
targetWorldId
|
||||
? queryClient.invalidateQueries({
|
||||
queryKey: ['servers', 'startup', 'v1', serverId, targetWorldId],
|
||||
})
|
||||
: Promise.resolve(),
|
||||
])
|
||||
|
||||
@@ -3641,6 +3641,24 @@
|
||||
"server.instance-settings.general.support-zone.label": {
|
||||
"defaultMessage": "Support zone"
|
||||
},
|
||||
"server.settings.general.reset-to-onboarding.description": {
|
||||
"defaultMessage": "Send this server back into onboarding so setup can be completed again."
|
||||
},
|
||||
"server.settings.general.reset-to-onboarding.error": {
|
||||
"defaultMessage": "Failed to reset server to onboarding"
|
||||
},
|
||||
"server.settings.general.reset-to-onboarding.modal.description": {
|
||||
"defaultMessage": "This will send the server back into onboarding so setup can be completed again. Are you sure you want to continue?"
|
||||
},
|
||||
"server.settings.general.reset-to-onboarding.success.description": {
|
||||
"defaultMessage": "The server has been returned to the onboarding flow."
|
||||
},
|
||||
"server.settings.general.reset-to-onboarding.success.title": {
|
||||
"defaultMessage": "Server reset to onboarding"
|
||||
},
|
||||
"server.settings.general.support-zone.label": {
|
||||
"defaultMessage": "Support zone"
|
||||
},
|
||||
"servers.access-page.activity-log-filter.action-group.backups": {
|
||||
"defaultMessage": "Backups"
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user