feat(frontend): lookup server by subdomain (#7210)

feat(frontend): lookup server by subdomain admin
This commit is contained in:
François-Xavier Talbot
2026-08-19 17:49:27 -04:00
committed by GitHub
parent 10b1d7002a
commit 79fcf41316
6 changed files with 154 additions and 0 deletions
+9
View File
@@ -427,6 +427,14 @@
tone: 'brand',
shown: isAdmin(auth.user),
},
{
id: 'servers-lookup',
label: 'Server lookup',
type: 'link',
to: '/admin/servers/lookup',
tone: 'brand',
shown: isAdmin(auth.user),
},
{
id: 'servers-notices',
label: formatMessage(messages.manageServerNotices),
@@ -480,6 +488,7 @@
<template #file-lookup>
<FileIcon aria-hidden="true" /> {{ formatMessage(messages.fileLookup) }}
</template>
<template #servers-lookup> <ServerIcon aria-hidden="true" /> Server lookup </template>
<template #servers-notices>
<IssuesIcon aria-hidden="true" /> {{ formatMessage(messages.manageServerNotices) }}
</template>
@@ -0,0 +1,113 @@
<template>
<div class="normal-page no-sidebar">
<h1>Server lookup</h1>
<div class="normal-page__content">
<form class="card flex flex-col gap-3" @submit.prevent="lookupServer">
<div class="flex flex-col gap-2">
<label for="server-subdomain">
<span class="text-lg font-semibold text-contrast">
Server subdomain
<span class="text-brand-red">*</span>
</span>
</label>
<StyledInput
id="server-subdomain"
v-model="subdomainInput"
:icon="ServerIcon"
:error="submitted && !!validationError"
placeholder="example or example.modrinth.gg"
autocomplete="off"
autocapitalize="none"
:spellcheck="false"
/>
<span v-if="submitted && validationError" class="text-sm font-medium text-red">
{{ validationError }}
</span>
</div>
<div>
<Button type="colored" color="brand" native-type="submit" :loading="isLoading">
<SearchIcon aria-hidden="true" />
Look up server
</Button>
</div>
<Admonition v-if="lookupError" type="critical" header="Lookup failed">
{{ lookupError }}
</Admonition>
</form>
</div>
</div>
</template>
<script setup lang="ts">
import { ModrinthApiError } from '@modrinth/api-client'
import { SearchIcon, ServerIcon } from '@modrinth/assets'
import { Admonition, Button, injectModrinthClient, StyledInput } from '@modrinth/ui'
const client = injectModrinthClient()
const subdomainInput = ref('')
const submitted = ref(false)
const isLoading = ref(false)
const lookupError = ref('')
const normalizedSubdomain = computed(() =>
subdomainInput.value
.trim()
.replace(/\.modrinth\.gg$/i, '')
.toLowerCase(),
)
const validationError = computed(() => {
if (!normalizedSubdomain.value) {
return 'Enter a server subdomain.'
}
if (normalizedSubdomain.value.length > 63) {
return 'Subdomain must be 63 characters or fewer.'
}
if (!/^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/.test(normalizedSubdomain.value)) {
return 'Use only letters, numbers, and hyphens, without a leading or trailing hyphen.'
}
return undefined
})
watch(subdomainInput, (value) => {
const withoutDomain = value.replace(/\.modrinth\.gg$/i, '')
if (withoutDomain !== value) {
subdomainInput.value = withoutDomain
}
lookupError.value = ''
})
async function lookupServer() {
if (isLoading.value) {
return
}
submitted.value = true
lookupError.value = ''
if (validationError.value) {
return
}
subdomainInput.value = normalizedSubdomain.value
isLoading.value = true
try {
const server = await client.archon.servers_internal.getBySubdomain(normalizedSubdomain.value)
await navigateTo(`/hosting/manage/${server.id}`)
} catch (error) {
if (error instanceof ModrinthApiError && error.statusCode === 404) {
lookupError.value = `No server was found for ${normalizedSubdomain.value}.modrinth.gg.`
} else {
lookupError.value = 'The server could not be looked up. Try again.'
}
} finally {
isLoading.value = false
}
}
</script>