mirror of
https://github.com/modrinth/code.git
synced 2026-08-31 03:55:59 +00:00
fix: ditch files v0
This commit is contained in:
@@ -407,6 +407,7 @@ export type PendingChange = {
|
||||
|
||||
type ServerListingProps = {
|
||||
server_id: string
|
||||
worldId?: string | null
|
||||
name: string
|
||||
status: Archon.Servers.v0.Status
|
||||
suspension_reason?: Archon.Servers.v0.SuspensionReason | null
|
||||
@@ -546,16 +547,28 @@ async function dataURLToBlob(dataURL: string): Promise<Blob> {
|
||||
return res.blob()
|
||||
}
|
||||
|
||||
async function getActiveWorldId(serverId: string): Promise<string | null> {
|
||||
const server = await archon.servers_v1.get(serverId)
|
||||
const activeWorld = server.worlds.find((world) => world.is_active)
|
||||
return activeWorld?.id ?? server.worlds[0]?.id ?? null
|
||||
}
|
||||
|
||||
async function uploadWorldFile(worldId: string, path: string, file: File | Blob) {
|
||||
await kyros.files_v1.ensureFile(worldId, path)
|
||||
await kyros.files_v1.uploadFile(worldId, path, file).promise
|
||||
}
|
||||
|
||||
const { data: image } = useQuery({
|
||||
queryKey: ['server-icon', props.server_id] as const,
|
||||
queryKey: computed(() => ['server-icon', props.server_id, props.worldId ?? null] as const),
|
||||
queryFn: async (): Promise<string | null> => {
|
||||
if (!props.server_id || props.status !== 'available') return null
|
||||
|
||||
try {
|
||||
const fsAuth = await archon.servers_v0.getFilesystemAuth(props.server_id)
|
||||
const worldId = props.worldId ?? (await getActiveWorldId(props.server_id))
|
||||
if (!worldId) return null
|
||||
|
||||
try {
|
||||
const blob = await kyros.files_v0.downloadFileWithAuth(fsAuth, '/server-icon.png')
|
||||
const blob = await kyros.files_v1.downloadRawFileContents(worldId, '/server-icon.png')
|
||||
return await processImageBlob(blob, 64)
|
||||
} catch (error) {
|
||||
const statusCode = (error as { statusCode?: number })?.statusCode
|
||||
@@ -564,8 +577,8 @@ const { data: image } = useQuery({
|
||||
}
|
||||
|
||||
try {
|
||||
const originalBlob = await kyros.files_v0.downloadFileWithAuth(
|
||||
fsAuth,
|
||||
const originalBlob = await kyros.files_v1.downloadRawFileContents(
|
||||
worldId,
|
||||
'/server-icon-original.png',
|
||||
)
|
||||
return await processImageBlob(originalBlob, 64)
|
||||
@@ -585,13 +598,12 @@ const { data: image } = useQuery({
|
||||
const scaledBlob = await dataURLToBlob(scaledDataUrl)
|
||||
const scaledFile = new File([scaledBlob], 'server-icon.png', { type: 'image/png' })
|
||||
|
||||
await kyros.files_v0.uploadFileWithAuth(fsAuth, '/server-icon.png', scaledFile).promise
|
||||
await uploadWorldFile(worldId, '/server-icon.png', scaledFile)
|
||||
|
||||
const originalFile = new File([blob], 'server-icon-original.png', {
|
||||
type: 'image/png',
|
||||
})
|
||||
await kyros.files_v0.uploadFileWithAuth(fsAuth, '/server-icon-original.png', originalFile)
|
||||
.promise
|
||||
await uploadWorldFile(worldId, '/server-icon-original.png', originalFile)
|
||||
|
||||
return scaledDataUrl
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ const props = withDefaults(
|
||||
const { addNotification } = injectNotificationManager()
|
||||
const { formatMessage } = useVIntl()
|
||||
const client = injectModrinthClient()
|
||||
const { serverId, server } = injectModrinthServerContext()
|
||||
const { serverId, server, worldId } = injectModrinthServerContext()
|
||||
const queryClient = useQueryClient()
|
||||
const isUploadingIcon = ref(false)
|
||||
const isSyncingIcon = ref(false)
|
||||
@@ -95,6 +95,7 @@ const {
|
||||
computed(() => server.value?.upstream ?? null),
|
||||
{
|
||||
includeProjectFallback: false,
|
||||
worldId,
|
||||
},
|
||||
)
|
||||
|
||||
@@ -107,6 +108,19 @@ function isNotFound(error: unknown): boolean {
|
||||
return getStatusCode(error) === 404
|
||||
}
|
||||
|
||||
function getWorldId() {
|
||||
if (!worldId.value) {
|
||||
throw new Error('World ID is not available.')
|
||||
}
|
||||
return worldId.value
|
||||
}
|
||||
|
||||
async function uploadWorldFile(path: string, file: File | Blob) {
|
||||
const id = getWorldId()
|
||||
await client.kyros.files_v1.ensureFile(id, path)
|
||||
await client.kyros.files_v1.uploadFile(id, path, file).promise
|
||||
}
|
||||
|
||||
const uploadFile = async (e: Event) => {
|
||||
if (isIconActionDisabled.value) return
|
||||
|
||||
@@ -144,39 +158,11 @@ const uploadFile = async (e: Event) => {
|
||||
img.src = URL.createObjectURL(file)
|
||||
})
|
||||
|
||||
const fsAuth = await client.archon.servers_v0.getFilesystemAuth(serverId)
|
||||
|
||||
try {
|
||||
await client.kyros.files_v0.uploadFileWithAuth(fsAuth, '/server-icon.png', scaledFile).promise
|
||||
} catch (scaledUploadError) {
|
||||
// Node FS may reject create when file already exists. Delete and retry once.
|
||||
try {
|
||||
await client.kyros.files_v0.deleteFileOrFolderWithAuth(fsAuth, '/server-icon.png', false)
|
||||
} catch (deleteError) {
|
||||
if (!isNotFound(deleteError)) {
|
||||
throw scaledUploadError
|
||||
}
|
||||
}
|
||||
|
||||
await client.kyros.files_v0.uploadFileWithAuth(fsAuth, '/server-icon.png', scaledFile).promise
|
||||
}
|
||||
await uploadWorldFile('/server-icon.png', scaledFile)
|
||||
|
||||
// Keep original file in sync when possible, but don't block icon updates on failures here.
|
||||
try {
|
||||
await client.kyros.files_v0.deleteFileOrFolderWithAuth(
|
||||
fsAuth,
|
||||
'/server-icon-original.png',
|
||||
false,
|
||||
)
|
||||
} catch (deleteOriginalError) {
|
||||
if (!isNotFound(deleteOriginalError)) {
|
||||
// best effort
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
await client.kyros.files_v0.uploadFileWithAuth(fsAuth, '/server-icon-original.png', file)
|
||||
.promise
|
||||
await uploadWorldFile('/server-icon-original.png', file)
|
||||
} catch (originalUploadError) {
|
||||
if (!isNotFound(originalUploadError)) {
|
||||
// best effort
|
||||
@@ -222,10 +208,10 @@ const resetIcon = async () => {
|
||||
isSyncingIcon.value = true
|
||||
|
||||
try {
|
||||
const fsAuth = await client.archon.servers_v0.getFilesystemAuth(serverId)
|
||||
const id = getWorldId()
|
||||
const deleteResults = await Promise.allSettled([
|
||||
client.kyros.files_v0.deleteFileOrFolderWithAuth(fsAuth, '/server-icon.png', false),
|
||||
client.kyros.files_v0.deleteFileOrFolderWithAuth(fsAuth, '/server-icon-original.png', false),
|
||||
client.kyros.files_v1.deleteFile(id, '/server-icon.png'),
|
||||
client.kyros.files_v1.deleteFile(id, '/server-icon-original.png'),
|
||||
])
|
||||
|
||||
for (const result of deleteResults) {
|
||||
|
||||
@@ -370,7 +370,7 @@ export function useServerManageCoreRuntime(options: UseServerManageCoreRuntimeOp
|
||||
dismissedOpIds.value = new Set([...dismissedOpIds.value, opId])
|
||||
}
|
||||
try {
|
||||
await client.kyros.files_v0.modifyOperation(opId, action)
|
||||
await client.kyros.files_v1.modifyOperation(opId, action)
|
||||
} catch (error) {
|
||||
if (action === 'dismiss') return
|
||||
console.error(`Failed to ${action} operation:`, error)
|
||||
|
||||
@@ -6,11 +6,13 @@ import { injectModrinthClient } from '#ui/providers'
|
||||
|
||||
type UpstreamRef = ComputedRef<Archon.Servers.v0.Server['upstream'] | null | undefined>
|
||||
type ServerIdSource = string | { readonly value: string }
|
||||
type WorldIdSource = string | null | undefined | { readonly value: string | null | undefined }
|
||||
|
||||
type UseServerImageOptions = {
|
||||
enabled?: ComputedRef<boolean> | boolean
|
||||
size?: number
|
||||
includeProjectFallback?: boolean
|
||||
worldId?: WorldIdSource
|
||||
}
|
||||
|
||||
export async function processImageBlob(blob: Blob, size: number): Promise<string> {
|
||||
@@ -49,6 +51,7 @@ export function useServerImage(
|
||||
const iconSize = options.size ?? 512
|
||||
const includeProjectFallback = options.includeProjectFallback ?? false
|
||||
const resolvedServerId = computed(() => resolveServerId(serverId))
|
||||
const resolvedWorldId = computed(() => resolveWorldId(options.worldId))
|
||||
|
||||
const queryKey = computed(
|
||||
() =>
|
||||
@@ -57,6 +60,7 @@ export function useServerImage(
|
||||
'detail',
|
||||
resolvedServerId.value,
|
||||
'icon',
|
||||
resolvedWorldId.value ?? 'active',
|
||||
upstream.value?.project_id ?? null,
|
||||
] as const,
|
||||
)
|
||||
@@ -74,18 +78,22 @@ export function useServerImage(
|
||||
if (!id) return null
|
||||
|
||||
try {
|
||||
const fsAuth = await client.archon.servers_v0.getFilesystemAuth(id)
|
||||
const targetWorldId = resolvedWorldId.value ?? (await getActiveWorldId(id))
|
||||
if (!targetWorldId) return null
|
||||
|
||||
try {
|
||||
const blob = await client.kyros.files_v0.downloadFileWithAuth(fsAuth, '/server-icon.png')
|
||||
const blob = await client.kyros.files_v1.downloadRawFileContents(
|
||||
targetWorldId,
|
||||
'/server-icon.png',
|
||||
)
|
||||
return await processImageBlob(blob, iconSize)
|
||||
} catch (error) {
|
||||
if (!isNotFound(error)) throw error
|
||||
}
|
||||
|
||||
try {
|
||||
const blob = await client.kyros.files_v0.downloadFileWithAuth(
|
||||
fsAuth,
|
||||
const blob = await client.kyros.files_v1.downloadRawFileContents(
|
||||
targetWorldId,
|
||||
'/server-icon-original.png',
|
||||
)
|
||||
return await processImageBlob(blob, iconSize)
|
||||
@@ -94,7 +102,6 @@ export function useServerImage(
|
||||
}
|
||||
} catch (error) {
|
||||
console.debug('Server image fetch failed:', error)
|
||||
return null
|
||||
}
|
||||
|
||||
if (!includeProjectFallback || !upstream.value?.project_id) return null
|
||||
@@ -133,6 +140,12 @@ export function useServerImage(
|
||||
localImage.value = undefined
|
||||
}
|
||||
|
||||
async function getActiveWorldId(id: string): Promise<string | null> {
|
||||
const server = await client.archon.servers_v1.get(id)
|
||||
const activeWorld = server.worlds.find((world) => world.is_active)
|
||||
return activeWorld?.id ?? server.worlds[0]?.id ?? null
|
||||
}
|
||||
|
||||
return {
|
||||
image,
|
||||
queryKey,
|
||||
@@ -146,3 +159,9 @@ export function useServerImage(
|
||||
function resolveServerId(serverId: ServerIdSource): string {
|
||||
return typeof serverId === 'string' ? serverId : serverId.value
|
||||
}
|
||||
|
||||
function resolveWorldId(worldId: WorldIdSource): string | null {
|
||||
if (worldId == null) return null
|
||||
if (typeof worldId === 'string') return worldId
|
||||
return worldId.value ?? null
|
||||
}
|
||||
|
||||
+10
-20
@@ -121,6 +121,7 @@ const { formatMessage } = useVIntl()
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
worldId: string
|
||||
disabled?: boolean
|
||||
disabledTooltip?: string
|
||||
}>(),
|
||||
@@ -187,14 +188,6 @@ const messages = defineMessages({
|
||||
id: 'files.zip-url-modal.error-url-invalid',
|
||||
defaultMessage: 'URL must be valid.',
|
||||
},
|
||||
cfNotFoundTitle: {
|
||||
id: 'files.zip-url-modal.cf-not-found-title',
|
||||
defaultMessage: 'CurseForge modpack not found',
|
||||
},
|
||||
cfNotFoundText: {
|
||||
id: 'files.zip-url-modal.cf-not-found-text',
|
||||
defaultMessage: 'Could not find CurseForge modpack at that URL.',
|
||||
},
|
||||
installFailedTitle: {
|
||||
id: 'files.zip-url-modal.install-failed-title',
|
||||
defaultMessage: 'Installation failed',
|
||||
@@ -265,19 +258,16 @@ const handleSubmit = async () => {
|
||||
|
||||
submitted.value = true
|
||||
try {
|
||||
const dry = await client.kyros.files_v0.extractFile(trimmedUrl.value, true, true)
|
||||
|
||||
if (!cf.value || dry.modpack_name) {
|
||||
await client.kyros.files_v0.extractFile(trimmedUrl.value, true, false)
|
||||
hide()
|
||||
} else {
|
||||
submitted.value = false
|
||||
addNotification({
|
||||
title: formatMessage(messages.cfNotFoundTitle),
|
||||
text: formatMessage(messages.cfNotFoundText),
|
||||
type: 'error',
|
||||
})
|
||||
const stream = await client.kyros.files_v1.unzipFile(props.worldId, {
|
||||
source: { type: 'zip_url', url: trimmedUrl.value },
|
||||
target: '/',
|
||||
})
|
||||
const reader = stream.getReader()
|
||||
while (true) {
|
||||
const { done } = await reader.read()
|
||||
if (done) break
|
||||
}
|
||||
hide()
|
||||
} catch (err) {
|
||||
submitted.value = false
|
||||
console.error('Error installing:', err)
|
||||
|
||||
@@ -181,12 +181,13 @@ interface UploadItem {
|
||||
| 'cancelled'
|
||||
| 'incorrect-type'
|
||||
size: string
|
||||
uploader?: ReturnType<typeof client.kyros.files_v0.uploadFile>
|
||||
uploader?: ReturnType<typeof client.kyros.files_v1.uploadFile>
|
||||
error?: Error
|
||||
}
|
||||
|
||||
interface Props {
|
||||
currentPath: string
|
||||
worldId: string
|
||||
fileType?: string
|
||||
marginBottom?: number
|
||||
acceptedTypes?: Array<string>
|
||||
@@ -281,11 +282,12 @@ const uploadFile = async (file: File) => {
|
||||
uploadItem.status = 'uploading'
|
||||
const filePath = `${props.currentPath}/${file.name}`.replace('//', '/')
|
||||
|
||||
const uploader = client.kyros.files_v0.uploadFile(filePath, file, {
|
||||
await client.kyros.files_v1.ensureFile(props.worldId, filePath)
|
||||
const uploader = client.kyros.files_v1.uploadFile(props.worldId, filePath, file, {
|
||||
onProgress: ({ progress }) => {
|
||||
const index = uploadQueue.value.findIndex((item) => item.file.name === file.name)
|
||||
if (index !== -1) {
|
||||
uploadQueue.value[index].progress = Math.round(progress)
|
||||
uploadQueue.value[index].progress = Math.round(progress * 100)
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
@@ -4,8 +4,9 @@
|
||||
<FileCreateItemModal ref="createItemModal" :type="newItemType" @create="handleCreateNewItem" />
|
||||
<FileUploadConflictModal ref="uploadConflictModal" @proceed="handleExtractConfirm" />
|
||||
<FileUploadZipUrlModal
|
||||
v-if="ctx.showInstallFromUrl"
|
||||
v-if="showInstallFromUrl"
|
||||
ref="uploadZipUrlModal"
|
||||
:world-id="installFromUrlWorldId"
|
||||
:disabled="isBusy"
|
||||
:disabled-tooltip="busyTooltip"
|
||||
/>
|
||||
@@ -48,7 +49,7 @@
|
||||
:is-editor-find-open="fileEditorRef?.isFindOpen"
|
||||
:search-query="searchQuery"
|
||||
:show-refresh-button="showRefreshButton"
|
||||
:show-install-from-url="ctx.showInstallFromUrl"
|
||||
:show-install-from-url="showInstallFromUrl"
|
||||
:base-id="baseId"
|
||||
:disabled="isBusy"
|
||||
:disabled-tooltip="busyTooltip"
|
||||
@@ -378,6 +379,8 @@ const selectedItem = ref<FileItem | null>(null)
|
||||
const unsavedChangesModal = ref<InstanceType<typeof FileUnsavedChangesModal>>()
|
||||
|
||||
const hasUnsavedChanges = computed(() => fileEditorRef.value?.hasUnsavedChanges ?? false)
|
||||
const installFromUrlWorldId = computed(() => ctx.worldId?.value ?? '')
|
||||
const showInstallFromUrl = computed(() => !!ctx.showInstallFromUrl && !!installFromUrlWorldId.value)
|
||||
|
||||
async function confirmDiscardChanges(): Promise<boolean> {
|
||||
if (!hasUnsavedChanges.value) return true
|
||||
|
||||
@@ -35,6 +35,7 @@ export interface FileManagerContext {
|
||||
uploadFiles: (files: File[]) => void
|
||||
cancelUpload?: () => void
|
||||
uploadState?: Ref<UploadState> | ComputedRef<UploadState>
|
||||
worldId?: Ref<string | null> | ComputedRef<string | null>
|
||||
|
||||
refresh: () => void
|
||||
|
||||
|
||||
@@ -818,6 +818,7 @@ const { disconnect: disconnectPanelSync } = useServerPanelSync({
|
||||
const { image: serverImage } = useServerImage(
|
||||
props.serverId,
|
||||
computed(() => serverData.value?.upstream ?? null),
|
||||
{ worldId },
|
||||
)
|
||||
const { data: serverProject } = useServerProject(computed(() => serverData.value?.upstream ?? null))
|
||||
|
||||
@@ -1228,7 +1229,7 @@ const handleFilesystemOps = (data: Archon.Websocket.v0.WSFilesystemOpsEvent) =>
|
||||
const cancelled = allOps.filter((x) => x.state === 'cancelled')
|
||||
Promise.all(
|
||||
cancelled.map((x) =>
|
||||
client.kyros.files_v0.modifyOperation(x.id, 'dismiss').catch((error) => {
|
||||
client.kyros.files_v1.modifyOperation(x.id, 'dismiss').catch((error) => {
|
||||
console.error('Failed to dismiss cancelled operation:', error)
|
||||
}),
|
||||
),
|
||||
@@ -1260,20 +1261,25 @@ const handleInstallationResult = async (data: Archon.Websocket.v0.WSInstallation
|
||||
installError.value = new Error(errorMessage.value)
|
||||
|
||||
try {
|
||||
let files = await client.kyros.files_v0.listDirectory('/', 1, 100)
|
||||
if (files && files.total > 1) {
|
||||
for (let i = 2; i <= files.total; i++) {
|
||||
const nextFiles = await client.kyros.files_v0.listDirectory('/', i, 100)
|
||||
if (nextFiles?.items?.length === 0) break
|
||||
if (nextFiles) files = nextFiles
|
||||
if (!worldId.value) break
|
||||
let files = await client.kyros.files_v1.listDescendants(worldId.value, '/', 1, 100)
|
||||
for (let i = 2; i <= files.page_total; i++) {
|
||||
const nextFiles = await client.kyros.files_v1.listDescendants(worldId.value, '/', i, 100)
|
||||
if (nextFiles.items.length === 0) break
|
||||
files = {
|
||||
...nextFiles,
|
||||
items: [...files.items, ...nextFiles.items],
|
||||
}
|
||||
}
|
||||
const fileName = files?.items?.find((file) =>
|
||||
const file = files.items.find((file) =>
|
||||
file.name.startsWith('modrinth-installation'),
|
||||
)?.name
|
||||
errorLogFile.value = fileName ?? ''
|
||||
if (fileName) {
|
||||
const content = await client.kyros.files_v0.downloadFile(fileName)
|
||||
)
|
||||
errorLogFile.value = file?.full_path ?? ''
|
||||
if (file) {
|
||||
const content = await client.kyros.files_v1.downloadRawFileContents(
|
||||
worldId.value,
|
||||
file.full_path,
|
||||
)
|
||||
errorLog.value = await content.text()
|
||||
}
|
||||
} catch (err) {
|
||||
|
||||
+16
-10
@@ -326,9 +326,11 @@ const moveMutation = useMutation({
|
||||
})
|
||||
|
||||
const createMutation = useMutation({
|
||||
mutationFn: (_vars: { path: string; type: 'file' | 'directory' }) => {
|
||||
// await client.kyros.files_v0.createFileOrFolder(path, type)
|
||||
throw new Error('Creating files or folders is not supported by the v1 world-scoped files API.')
|
||||
mutationFn: ({ path, type }: { path: string; type: 'file' | 'directory' }) => {
|
||||
const id = getWorldId()
|
||||
return type === 'directory'
|
||||
? client.kyros.files_v1.mkdirFile(id, path)
|
||||
: client.kyros.files_v1.touchFile(id, path)
|
||||
},
|
||||
onError: (err: Error) => {
|
||||
addNotification({
|
||||
@@ -337,6 +339,9 @@ const createMutation = useMutation({
|
||||
type: 'error',
|
||||
})
|
||||
},
|
||||
onSettled: () => {
|
||||
refreshList()
|
||||
},
|
||||
})
|
||||
|
||||
// File I/O
|
||||
@@ -344,7 +349,7 @@ async function readFile(path: string): Promise<string> {
|
||||
const normalizedPath = path.startsWith('/') ? path : `/${path}`
|
||||
const id = getWorldId()
|
||||
const cachedContent = queryClient.getQueryData<string>(['file-content', id, normalizedPath])
|
||||
if (cachedContent) return cachedContent
|
||||
if (cachedContent != null) return cachedContent
|
||||
const blob = await client.kyros.files_v1.downloadRawFileContents(id, normalizedPath)
|
||||
return await blob.text()
|
||||
}
|
||||
@@ -356,10 +361,11 @@ async function readFileAsBlob(path: string): Promise<Blob> {
|
||||
|
||||
async function writeFile(path: string, content: string): Promise<void> {
|
||||
if (fileWriteDisabled.value) return
|
||||
void path
|
||||
void content
|
||||
// await client.kyros.files_v0.updateFile(path, content)
|
||||
throw new Error('Updating file contents is not supported by the v1 world-scoped files API.')
|
||||
const normalizedPath = path.startsWith('/') ? path : `/${path}`
|
||||
const id = getWorldId()
|
||||
await client.kyros.files_v1.editFile(id, normalizedPath, content)
|
||||
queryClient.setQueryData(['file-content', id, normalizedPath], content)
|
||||
refreshList()
|
||||
}
|
||||
|
||||
async function downloadFile(path: string, fileName: string): Promise<void> {
|
||||
@@ -462,14 +468,14 @@ provideFileManager({
|
||||
uploadFiles,
|
||||
cancelUpload,
|
||||
uploadState,
|
||||
worldId,
|
||||
refresh: refreshList,
|
||||
isBusy: fileWriteDisabled,
|
||||
busyTooltip: fileWriteDisabledTooltip,
|
||||
busyWarning,
|
||||
// extractFile: async (path, override, dry) => client.kyros.files_v0.extractFile(path, override, dry),
|
||||
prefetchDirectory,
|
||||
prefetchFile,
|
||||
showInstallFromUrl: false,
|
||||
showInstallFromUrl: true,
|
||||
canRestart: canUsePowerActions.value,
|
||||
restartServer,
|
||||
canShareToMclogs: true,
|
||||
|
||||
@@ -58,6 +58,7 @@ const client = injectModrinthClient()
|
||||
const {
|
||||
server: _serverData,
|
||||
serverId,
|
||||
worldId,
|
||||
isConnected,
|
||||
isWsAuthIncorrect,
|
||||
stats,
|
||||
@@ -83,9 +84,13 @@ const isDismissed = () => Date.now() < dismissedUntil.value
|
||||
|
||||
const inspectError = async () => {
|
||||
if (isDismissed()) return
|
||||
if (!worldId.value) return
|
||||
|
||||
try {
|
||||
const blob = await client.kyros.files_v0.downloadFile('/logs/latest.log')
|
||||
const blob = await client.kyros.files_v1.downloadRawFileContents(
|
||||
worldId.value,
|
||||
'/logs/latest.log',
|
||||
)
|
||||
const log = await blob.text()
|
||||
if (!log) return
|
||||
|
||||
|
||||
@@ -180,6 +180,7 @@
|
||||
v-for="server in ownedFilteredData.filter((s) => !s.is_medal)"
|
||||
:key="`owned-${server.server_id}`"
|
||||
v-bind="server"
|
||||
:world-id="getServerWorldId(server.server_id)"
|
||||
:cancellation-date="serverBillingMap.get(server.server_id)?.cancellationDate"
|
||||
:is-provisioning="serverBillingMap.get(server.server_id)?.isProvisioning"
|
||||
:on-resubscribe="serverBillingMap.get(server.server_id)?.onResubscribe"
|
||||
@@ -211,6 +212,7 @@
|
||||
v-for="server in sharedFilteredData.filter((s) => !s.is_medal)"
|
||||
:key="`shared-${server.server_id}`"
|
||||
v-bind="server"
|
||||
:world-id="getServerWorldId(server.server_id)"
|
||||
/>
|
||||
</TransitionGroup>
|
||||
<div v-else class="text-secondary">
|
||||
@@ -762,6 +764,12 @@ const { data: serverFullList } = useQuery({
|
||||
enabled: loggedIn,
|
||||
})
|
||||
|
||||
function getServerWorldId(serverId: string): string | null {
|
||||
const server = serverFullList.value?.find((server) => server.id === serverId)
|
||||
const activeWorld = server?.worlds.find((world) => world.is_active)
|
||||
return activeWorld?.id ?? server?.worlds[0]?.id ?? null
|
||||
}
|
||||
|
||||
type ServerBillingInfo = {
|
||||
cancellationDate?: string | null
|
||||
isProvisioning?: boolean
|
||||
|
||||
Reference in New Issue
Block a user