1515 :show-uptime =" false"
1616 >
1717 <template #actions >
18- <div class = " flex gap-2 " v-if =" isConnected && !server.flows?.intro" >
18+ <div v-if =" isConnected && !server.flows?.intro" class = " flex gap-2 " >
1919 <PanelServerActionButton />
2020 <ButtonStyled circular size =" large" >
2121 <button v-tooltip =" 'Server settings'" @click =" openServerSettingsModal" >
3535 <Suspense >
3636 <ServerSettingsModal ref =" serverSettingsModal" />
3737 </Suspense >
38- <RouterView v-slot =" { Component }" >
38+ <RouterView v-slot =" { Component, route: childRoute }" >
3939 <template v-if =" Component " >
4040 <Suspense >
41- <component :is =" Component" />
41+ <component
42+ :is =" Component"
43+ v-bind =" childRoute.name === 'ServerManageOverview' ? overviewChildProps : undefined"
44+ />
4245 <template #fallback >
4346 <LoadingIndicator />
4447 </template >
5154</template >
5255
5356<script setup lang="ts">
54- import { type Archon , clearNodeAuthState , setNodeAuthState } from ' @modrinth/api-client'
55- import { BoxesIcon , DatabaseBackupIcon , FolderOpenIcon , SettingsIcon } from ' @modrinth/assets'
57+ import type { Archon } from ' @modrinth/api-client'
58+ import {
59+ BoxesIcon ,
60+ DatabaseBackupIcon ,
61+ FolderOpenIcon ,
62+ LayoutTemplateIcon ,
63+ SettingsIcon ,
64+ } from ' @modrinth/assets'
5665import {
57- type BusyReason ,
5866 ButtonStyled ,
59- defineMessage ,
6067 injectModrinthClient ,
6168 LoadingIndicator ,
6269 NavTabs ,
6370 PanelServerActionButton ,
6471 PanelServerOverflowMenu ,
65- provideModrinthServerContext ,
6672 ServerManageHeader ,
73+ useServerManageCoreRuntime ,
6774} from ' @modrinth/ui'
68- import { useQuery , useQueryClient } from ' @tanstack/vue-query'
69- import { computed , onUnmounted , reactive , ref , watch } from ' vue'
75+ import { useQuery } from ' @tanstack/vue-query'
76+ import { computed , onUnmounted , ref , watch } from ' vue'
7077import { useRoute } from ' vue-router'
7178
7279import ServerSettingsModal from ' @/components/ui/modal/ServerSettingsModal.vue'
@@ -75,7 +82,6 @@ import { useTheming } from '@/store/theme'
7582const route = useRoute ()
7683const themeStore = useTheming ()
7784const client = injectModrinthClient ()
78- const queryClient = useQueryClient ()
7985
8086const serverId = computed (() => {
8187 const rawId = route .params .id
@@ -137,39 +143,28 @@ const serverProjectLink = computed(() => {
137143 return ` /project/${serverProject .value .slug ?? serverProject .value .id } `
138144})
139145
140- const isConnected = ref (false )
141- const powerState = ref <Archon .Websocket .v0 .PowerState >(' stopped' )
142- const isServerRunning = computed (() => powerState .value === ' running' )
143- const backupsState = reactive (new Map ())
144146const isSyncingContent = ref (false )
145- const socketUnsubscribers = ref <(() => void )[]>([])
146- const connectedSocketServerId = ref <string | null >(null )
147-
148- const busyReasons = computed <BusyReason []>(() => {
149- const reasons: BusyReason [] = []
150- if (server .value ?.status === ' installing' ) {
151- reasons .push ({
152- reason: defineMessage ({
153- id: ' servers.busy.installing' ,
154- defaultMessage: ' Server is installing' ,
155- }),
156- })
157- }
158- if (isSyncingContent .value ) {
159- reasons .push ({
160- reason: defineMessage ({
161- id: ' servers.busy.syncing-content' ,
162- defaultMessage: ' Content sync in progress' ,
163- }),
164- })
165- }
166- return reasons
147+ const {
148+ cleanupCoreRuntime,
149+ connectSocket,
150+ connectedSocketServerId,
151+ disconnectSocket,
152+ fsAuth,
153+ isConnected,
154+ isServerRunning,
155+ isWsAuthIncorrect,
156+ powerStateDetails,
157+ refreshFsAuth,
158+ serverPowerState,
159+ stats,
160+ } = useServerManageCoreRuntime ({
161+ serverId ,
162+ worldId ,
163+ server ,
164+ isSyncingContent ,
165+ setDisconnectedOnAuthIncorrect: true ,
167166})
168167
169- const fsAuth = ref <{ url: string ; token: string } | null >(null )
170- const fsOps = ref <Archon .Websocket .v0 .FilesystemOperation []>([])
171- const fsQueuedOps = ref <Archon .Websocket .v0 .QueuedFilesystemOp []>([])
172-
173168async function processImageBlob(blob : Blob , size : number ): Promise <string > {
174169 return new Promise ((resolve ) => {
175170 const canvas = document .createElement (' canvas' )
@@ -205,85 +200,11 @@ const { data: serverImage } = useQuery({
205200 enabled: computed (() => !! serverId .value && server .value .status === ' available' ),
206201})
207202
208- async function refreshFsAuth() {
209- if (! serverId .value ) {
210- fsAuth .value = null
211- return
212- }
213- fsAuth .value = await queryClient .fetchQuery ({
214- queryKey: [' servers' , ' filesystem-auth' , serverId .value ],
215- queryFn : () => client .archon .servers_v0 .getFilesystemAuth (serverId .value ),
216- })
217- }
218-
219- function markBackupCancelled(backupId : string ) {
220- backupsState .delete (backupId )
221- }
222-
223203function openServerSettingsModal() {
224204 if (! serverId .value ) return
225205 serverSettingsModal .value ?.show ({ serverId: serverId .value })
226206}
227207
228- function setPowerState(state : Archon .Websocket .v0 .PowerState ) {
229- powerState .value = state
230- }
231-
232- function handlePowerState(data : Archon .Websocket .v0 .WSPowerStateEvent ) {
233- setPowerState (data .state )
234- }
235-
236- function handleState(data : Archon .Websocket .v0 .WSStateEvent ) {
237- const powerMap: Record <Archon .Websocket .v0 .FlattenedPowerState , Archon .Websocket .v0 .PowerState > =
238- {
239- not_ready: ' stopped' ,
240- starting: ' starting' ,
241- running: ' running' ,
242- stopping: ' stopping' ,
243- idle:
244- data .was_oom || (data .exit_code != null && data .exit_code !== 0 ) ? ' crashed' : ' stopped' ,
245- }
246- setPowerState (powerMap [data .power_variant ])
247- }
248-
249- function disconnectSocket(targetServerId ? : string ) {
250- for (const unsub of socketUnsubscribers .value ) unsub ()
251- socketUnsubscribers .value = []
252-
253- if (targetServerId ) {
254- client .archon .sockets .disconnect (targetServerId )
255- }
256- connectedSocketServerId .value = null
257- isConnected .value = false
258- setPowerState (' stopped' )
259- }
260-
261- async function connectSocket(targetServerId : string ) {
262- if (connectedSocketServerId .value === targetServerId && isConnected .value ) {
263- return
264- }
265- disconnectSocket (targetServerId )
266-
267- try {
268- await client .archon .sockets .safeConnect (targetServerId , { force: true })
269- connectedSocketServerId .value = targetServerId
270- isConnected .value = true
271- socketUnsubscribers .value = [
272- client .archon .sockets .on (targetServerId , ' state' , handleState ),
273- client .archon .sockets .on (targetServerId , ' power-state' , handlePowerState ),
274- client .archon .sockets .on (targetServerId , ' auth-incorrect' , () => {
275- isConnected .value = false
276- }),
277- client .archon .sockets .on (targetServerId , ' auth-ok' , () => {
278- isConnected .value = true
279- }),
280- ]
281- } catch (error ) {
282- console .error (' [hosting/manage] Failed to connect server socket:' , error )
283- isConnected .value = false
284- }
285- }
286-
287208watch (
288209 () => serverId .value ,
289210 (newServerId , oldServerId ) => {
@@ -304,41 +225,36 @@ watch(
304225 disconnectSocket (currentServerId )
305226 return
306227 }
307- if (connectedSocketServerId .value === currentServerId && isConnected .value ) {
228+ if (
229+ connectedSocketServerId .value === currentServerId &&
230+ (isConnected .value || isWsAuthIncorrect .value )
231+ ) {
308232 return
309233 }
310- void connectSocket (currentServerId )
234+ void connectSocket (currentServerId , { force: true } )
311235 },
312236 { immediate: true },
313237)
314238
315- provideModrinthServerContext ({
316- get serverId() {
317- return serverId .value
318- },
319- worldId ,
320- server ,
321- isConnected ,
322- powerState ,
323- isServerRunning ,
324- backupsState ,
325- markBackupCancelled ,
326- isSyncingContent ,
327- busyReasons ,
328- fsAuth ,
329- fsOps ,
330- fsQueuedOps ,
331- refreshFsAuth ,
332- })
333-
334- setNodeAuthState (() => fsAuth .value , refreshFsAuth )
335-
336239onUnmounted (() => {
337- disconnectSocket (serverId .value || undefined )
338- clearNodeAuthState ()
240+ cleanupCoreRuntime (serverId .value || undefined )
339241})
340242
243+ const overviewChildProps = computed (() => ({
244+ isConnected: isConnected .value ,
245+ isWsAuthIncorrect: isWsAuthIncorrect .value ,
246+ isServerRunning: isServerRunning .value ,
247+ stats: stats .value ,
248+ serverPowerState: serverPowerState .value ,
249+ powerStateDetails: powerStateDetails .value ,
250+ }))
251+
341252const tabs = computed (() => [
253+ {
254+ label: ' Overview' ,
255+ href: basePath .value ,
256+ icon: LayoutTemplateIcon ,
257+ },
342258 {
343259 label: ' Content' ,
344260 href: ` ${basePath .value }/content ` ,
0 commit comments