feat: continued post qa for servers in app (#5818)

* fix: intercom in app

* feat: Logs.vue dynamic console resizing with window + padding problem

* fix: search highlight with decorator + change to be better

* fix: qa

* fix: allow paper+purpur into app csp

* fix: lint
This commit is contained in:
Calum H.
2026-04-15 21:16:05 +02:00
committed by GitHub
parent 37b0f7ff98
commit 3d5f29a7a2
15 changed files with 379 additions and 86 deletions

View File

@@ -60,6 +60,7 @@ import { useQuery } from '@tanstack/vue-query'
import { getVersion } from '@tauri-apps/api/app'
import { invoke } from '@tauri-apps/api/core'
import { getCurrentWindow } from '@tauri-apps/api/window'
import { fetch as tauriFetch } from '@tauri-apps/plugin-http'
import { openUrl } from '@tauri-apps/plugin-opener'
import { type } from '@tauri-apps/plugin-os'
import { saveWindowState, StateFlags } from '@tauri-apps/plugin-window-state'
@@ -446,7 +447,7 @@ router.afterEach((to, from, failure) => {
failed: failure,
})
setTimeout(() => {
if (!suspensePending) {
if (!suspensePending && stateInitialized.value) {
loading.stopLoading()
}
}, 100)
@@ -504,9 +505,27 @@ setupAuthProvider(credentials, async (_redirectPath) => {
await signIn()
})
async function validateSession(sessionToken) {
try {
const response = await tauriFetch(`${config.labrinthBaseUrl}/v2/user`, {
method: 'GET',
headers: { Authorization: sessionToken },
})
if (response.status === 401) return false
return true
} catch {
return true
}
}
async function fetchCredentials() {
const creds = await getCreds().catch(handleError)
if (creds && creds.user_id) {
if (creds.session && !(await validateSession(creds.session))) {
await logout().catch(handleError)
credentials.value = null
return
}
creds.user = await get_user(creds.user_id, 'bypass').catch(handleError)
}
credentials.value = creds ?? null

View File

@@ -100,24 +100,38 @@ const loadingProgress = ref(0)
const hidden = ref(false)
const message = ref()
// const MIN_DISPLAY_MS = 1000
// const mountedAt = Date.now()
const loading = useLoading()
watch(loading, (newValue) => {
if (!newValue.barEnabled) {
if (loading.loading) {
loadingProgress.value = 0
fakeLoadingIncrease()
} else {
loadingProgress.value = 100
doneLoading.value = true
watch(
loading,
(newValue) => {
if (!newValue.barEnabled) {
if (loading.loading) {
loadingProgress.value = 0
fakeLoadingIncrease()
} else {
// const elapsed = Date.now() - mountedAt
// const delay = Math.max(0, MIN_DISPLAY_MS - elapsed)
setTimeout(() => {
hidden.value = true
loading.setEnabled(true)
}, 50)
// setTimeout(() => {
// if (loading.loading) return
loadingProgress.value = 100
doneLoading.value = true
setTimeout(() => {
hidden.value = true
loading.setEnabled(true)
}, 50)
// }, delay)
}
}
}
})
},
{ immediate: true },
)
function fakeLoadingIncrease() {
if (loadingProgress.value < 95) {

View File

@@ -1,10 +1,12 @@
<template>
<div class="h-full w-full py-6">
<div class="h-full w-full pt-6">
<ServersManageRootLayout
:server-id="serverId"
:reload-page="() => router.go(0)"
:resolve-viewer="resolveViewer"
:show-copy-id-action="themeStore.devMode"
:auth-user="authUser"
:fetch-intercom-token="fetchIntercomToken"
:navigate-to-billing="() => openUrl('https://modrinth.com/settings/billing')"
:navigate-to-servers="() => router.push('/hosting/manage')"
:browse-modpacks="
@@ -48,10 +50,12 @@
import type { Archon, Labrinth } from '@modrinth/api-client'
import { injectAuth, LoadingIndicator, ServersManageRootLayout } from '@modrinth/ui'
import { useQuery } from '@tanstack/vue-query'
import { fetch as tauriFetch } from '@tauri-apps/plugin-http'
import { openUrl } from '@tauri-apps/plugin-opener'
import { computed, watch } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { config } from '@/config'
import { get_user } from '@/helpers/cache'
import { get as getCreds } from '@/helpers/mr_auth'
import { useBreadcrumbs } from '@/store/breadcrumbs'
@@ -97,6 +101,37 @@ watch(
},
)
const authUser = computed(() => {
const user = auth.user.value
if (!user?.id) return undefined
return {
id: user.id,
username: user.username,
email: user.email ?? '',
created: user.created,
}
})
async function fetchIntercomToken(): Promise<{ token: string }> {
const credentials = await getCreds()
if (!credentials?.session) {
throw new Error('Not authenticated')
}
const response = await tauriFetch(
`${config.siteUrl}/api/intercom/messenger-jwt?server_id=${encodeURIComponent(serverId.value)}`,
{
method: 'GET',
headers: {
Authorization: `Bearer ${credentials.session}`,
},
},
)
if (!response.ok) {
throw new Error(`Failed to fetch Intercom token: ${response.status}`)
}
return (await response.json()) as { token: string }
}
async function resolveViewer(): Promise<{ userId: string | null; userRole: string | null }> {
const credentials = await getCreds().catch(() => null)
if (!credentials?.user_id) {

View File

@@ -1,6 +1,9 @@
<template>
<div v-if="instance">
<div class="p-6 pr-2 pb-4" @contextmenu.prevent.stop="(event) => handleRightClick(event)">
<div v-if="instance" class="flex h-full flex-col">
<div
class="shrink-0 p-6 pr-2 pb-4"
@contextmenu.prevent.stop="(event) => handleRightClick(event)"
>
<ExportModal ref="exportModal" :instance="instance" />
<InstanceSettingsModal
:key="instance.path"
@@ -205,10 +208,10 @@
</template>
</ContentPageHeader>
</div>
<div class="px-6">
<div class="shrink-0 px-6">
<NavTabs :links="tabs" />
</div>
<div v-if="!!instance" class="p-6 pt-4">
<div v-if="!!instance" class="min-h-0 flex-1 overflow-y-auto p-6 pt-4">
<RouterView
v-if="route.path.startsWith('/instance')"
v-slot="{ Component }"

View File

@@ -87,7 +87,7 @@
"capabilities": ["ads", "core", "plugins"],
"csp": {
"default-src": "'self' customprotocol: asset:",
"connect-src": "ipc: http://ipc.localhost https://modrinth.com https://*.modrinth.com https://*.nodes.modrinth.com https://*.posthog.com https://posthog.modrinth.com https://*.sentry.io https://api.mclo.gs http://textures.minecraft.net https://textures.minecraft.net https://js.stripe.com https://*.stripe.com wss://*.stripe.com wss://*.nodes.modrinth.com wss://*.ts.net 'self' data: blob:",
"connect-src": "ipc: http://ipc.localhost https://modrinth.com https://*.modrinth.com https://*.nodes.modrinth.com https://*.posthog.com https://posthog.modrinth.com https://*.sentry.io https://api.mclo.gs http://textures.minecraft.net https://textures.minecraft.net https://js.stripe.com https://*.stripe.com wss://*.stripe.com wss://*.nodes.modrinth.com wss://*.ts.net https://fill.papermc.io https://api.purpurmc.org 'self' data: blob:",
"font-src": ["https://cdn-raw.modrinth.com/fonts/"],
"img-src": "https: 'unsafe-inline' 'self' asset: http://asset.localhost http://textures.minecraft.net blob: data:",
"style-src": "'unsafe-inline' 'self'",

View File

@@ -57,7 +57,11 @@ export default defineEventHandler(async (event): Promise<IntercomTokenResponse>
})
}
const authToken = getCookie(event, 'auth-token')
const authHeader = getRequestHeader(event, 'authorization')
const bearerToken = authHeader?.toLowerCase().startsWith('bearer ')
? authHeader.slice(7).trim()
: undefined
const authToken = bearerToken || getCookie(event, 'auth-token')
if (!authToken) {
throw createError({
statusCode: 401,