Depot API & v4 (#298)
* feat: nginx + torrential basics & services system * fix: lint + i18n * fix: update torrential to remove openssl * feat: add torrential to Docker build * feat: move to self hosted runner * fix: move off self-hosted runner * fix: update nginx.conf * feat: torrential cache invalidation * fix: update torrential for cache invalidation * feat: integrity check task * fix: lint * feat: move to version ids * fix: client fixes and client-side checks * feat: new depot apis and version id fixes * feat: update torrential * feat: droplet bump and remove unsafe update functions * fix: lint * feat: v4 featureset: emulators, multi-launch commands * fix: lint * fix: mobile ui for game editor * feat: launch options * fix: lint * fix: remove axios, use $fetch * feat: metadata and task api improvements * feat: task actions * fix: slight styling issue * feat: fix style and lints * feat: totp backend routes * feat: oidc groups * fix: update drop-base * feat: creation of passkeys & totp * feat: totp signin * feat: webauthn mfa/signin * feat: launch selecting ui * fix: manually running tasks * feat: update add company game modal to use new SelectorGame * feat: executor selector * fix(docker): update rust to rust nightly for torrential build (#305) * feat: new version ui * feat: move package lookup to build time to allow for deno dev * fix: lint * feat: localisation cleanup * feat: apply localisation cleanup * feat: potential i18n refactor logic * feat: remove args from commands * fix: lint * fix: lockfile --------- Co-authored-by: Aden Lindsay <140392385+AdenMGB@users.noreply.github.com>
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="flex flex-col gap-y-4 max-w-lg">
|
||||
<div class="flex flex-col gap-y-4 max-w-[35vw]">
|
||||
<Listbox
|
||||
as="div"
|
||||
:model-value="currentlySelectedVersion"
|
||||
@@ -73,139 +73,59 @@
|
||||
</div>
|
||||
</Listbox>
|
||||
|
||||
<div v-if="versionGuesses" class="flex flex-col gap-8">
|
||||
<div v-if="versionGuesses" class="flex flex-col gap-4">
|
||||
<!-- setup executable -->
|
||||
<div>
|
||||
<label
|
||||
for="startup"
|
||||
class="block text-sm font-medium leading-6 text-zinc-100"
|
||||
>{{ $t("library.admin.import.version.setupCmd") }}</label
|
||||
>
|
||||
<p class="text-zinc-400 text-xs">
|
||||
{{ $t("library.admin.import.version.setupDesc") }}
|
||||
</p>
|
||||
<div class="mt-2">
|
||||
<div
|
||||
class="flex w-fit rounded-md shadow-sm bg-zinc-950 ring-1 ring-inset ring-zinc-800 focus-within:ring-2 focus-within:ring-inset focus-within:ring-blue-600"
|
||||
>
|
||||
<span
|
||||
class="flex select-none items-center pl-3 text-zinc-500 sm:text-sm"
|
||||
>
|
||||
{{ $t("library.admin.import.version.installDir") }}
|
||||
</span>
|
||||
<Combobox
|
||||
as="div"
|
||||
:value="versionSettings.setup"
|
||||
nullable
|
||||
@update:model-value="(v) => updateSetupCommand(v)"
|
||||
>
|
||||
<div class="relative">
|
||||
<ComboboxInput
|
||||
class="block flex-1 border-0 py-1.5 pl-1 bg-transparent text-zinc-100 placeholder:text-zinc-400 focus:ring-0 sm:text-sm sm:leading-6"
|
||||
:placeholder="
|
||||
$t('library.admin.import.version.setupPlaceholder')
|
||||
"
|
||||
@change="setupProcessQuery = $event.target.value"
|
||||
@blur="setupProcessQuery = ''"
|
||||
/>
|
||||
<ComboboxButton
|
||||
v-if="setupFilteredVersionGuesses?.length ?? 0 > 0"
|
||||
class="absolute inset-y-0 right-0 flex items-center rounded-r-md px-2 focus:outline-none"
|
||||
>
|
||||
<ChevronUpDownIcon
|
||||
class="size-5 text-gray-400"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</ComboboxButton>
|
||||
|
||||
<ComboboxOptions
|
||||
class="absolute z-10 mt-1 max-h-60 w-full overflow-auto rounded-md bg-zinc-900 py-1 text-base shadow-lg ring-1 ring-white/5 focus:outline-none sm:text-sm"
|
||||
>
|
||||
<ComboboxOption
|
||||
v-for="guess in setupFilteredVersionGuesses"
|
||||
:key="guess.filename"
|
||||
v-slot="{ active, selected }"
|
||||
:value="guess.filename"
|
||||
as="template"
|
||||
>
|
||||
<li
|
||||
:class="[
|
||||
'relative cursor-default select-none py-2 pl-3 pr-9',
|
||||
active
|
||||
? 'bg-blue-600 text-white outline-none'
|
||||
: 'text-zinc-100',
|
||||
]"
|
||||
>
|
||||
<span
|
||||
:class="[
|
||||
'inline-flex items-center gap-x-2 block truncate',
|
||||
selected && 'font-semibold',
|
||||
]"
|
||||
>
|
||||
{{ guess.filename }}
|
||||
<component
|
||||
:is="PLATFORM_ICONS[guess.platform as PlatformClient]"
|
||||
class="size-5"
|
||||
/>
|
||||
</span>
|
||||
|
||||
<span
|
||||
v-if="selected"
|
||||
:class="[
|
||||
'absolute inset-y-0 right-0 flex items-center pr-4',
|
||||
active ? 'text-white' : 'text-blue-600',
|
||||
]"
|
||||
>
|
||||
<CheckIcon class="size-5" aria-hidden="true" />
|
||||
</span>
|
||||
</li>
|
||||
</ComboboxOption>
|
||||
<ComboboxOption
|
||||
v-if="setupProcessQuery"
|
||||
v-slot="{ active, selected }"
|
||||
:value="setupProcessQuery"
|
||||
>
|
||||
<li
|
||||
:class="[
|
||||
'relative cursor-default select-none py-2 pl-3 pr-9',
|
||||
active
|
||||
? 'bg-blue-600 text-white outline-none'
|
||||
: 'text-zinc-100',
|
||||
]"
|
||||
>
|
||||
<span
|
||||
:class="['block truncate', selected && 'font-semibold']"
|
||||
>
|
||||
{{ $t("chars.quoted", { text: setupProcessQuery }) }}
|
||||
</span>
|
||||
|
||||
<span
|
||||
v-if="selected"
|
||||
:class="[
|
||||
'absolute inset-y-0 right-0 flex items-center pr-4',
|
||||
active ? 'text-white' : 'text-blue-600',
|
||||
]"
|
||||
>
|
||||
<CheckIcon class="size-5" aria-hidden="true" />
|
||||
</span>
|
||||
</li>
|
||||
</ComboboxOption>
|
||||
</ComboboxOptions>
|
||||
</div>
|
||||
</Combobox>
|
||||
<input
|
||||
id="startup"
|
||||
v-model="versionSettings.setupArgs"
|
||||
type="text"
|
||||
name="startup"
|
||||
class="border-l border-zinc-700 block flex-1 border-0 py-1.5 pl-2 bg-transparent text-zinc-100 placeholder:text-zinc-400 focus:ring-0 sm:text-sm sm:leading-6"
|
||||
placeholder="--setup"
|
||||
/>
|
||||
</div>
|
||||
<div class="bg-zinc-800 p-4 rounded-xl relative flex flex-col gap-y-2">
|
||||
<div>
|
||||
<label class="block text-sm font-medium leading-6 text-zinc-100">{{
|
||||
$t("library.admin.import.version.setupCmd")
|
||||
}}</label>
|
||||
<p class="text-zinc-400 text-xs">
|
||||
{{ $t("library.admin.import.version.setupDesc") }}
|
||||
</p>
|
||||
</div>
|
||||
<ol
|
||||
v-if="versionSettings.setups.length > 0"
|
||||
class="divide-y-1 divide-zinc-700"
|
||||
>
|
||||
<li
|
||||
v-for="(launch, launchIdx) in versionSettings.setups"
|
||||
:key="launchIdx"
|
||||
class="py-2 inline-flex items-start gap-x-1"
|
||||
>
|
||||
<ImportVersionLaunchRow
|
||||
v-model="versionSettings.setups[launchIdx]"
|
||||
:version-guesses="versionGuesses"
|
||||
:needs-name="false"
|
||||
/>
|
||||
<button
|
||||
class="transition rounded p-1 bg-zinc-900/30 group hover:bg-red-600/30"
|
||||
@click="() => versionSettings.setups.splice(launchIdx, 1)"
|
||||
>
|
||||
<TrashIcon
|
||||
class="transition size-5 text-zinc-700 group-hover:text-red-700"
|
||||
/>
|
||||
</button>
|
||||
</li>
|
||||
</ol>
|
||||
<span
|
||||
v-else
|
||||
class="text-sm text-zinc-700 uppercase font-display font-bold"
|
||||
>{{ $t("library.admin.import.version.noSetups") }}</span
|
||||
>
|
||||
<LoadingButton
|
||||
:loading="false"
|
||||
class="w-fit"
|
||||
@click="() => versionSettings.setups.push({} as any)"
|
||||
>{{ $t("common.add") }}</LoadingButton
|
||||
>
|
||||
</div>
|
||||
<!-- setup mode -->
|
||||
<SwitchGroup as="div" class="flex items-center justify-between">
|
||||
<SwitchGroup
|
||||
as="div"
|
||||
class="bg-zinc-800 p-4 rounded-xl flex items-center justify-between gap-4"
|
||||
>
|
||||
<span class="flex flex-grow flex-col">
|
||||
<SwitchLabel
|
||||
as="span"
|
||||
@@ -220,7 +140,7 @@
|
||||
<Switch
|
||||
v-model="versionSettings.onlySetup"
|
||||
:class="[
|
||||
versionSettings.onlySetup ? 'bg-blue-600' : 'bg-zinc-800',
|
||||
versionSettings.onlySetup ? 'bg-blue-600' : 'bg-zinc-900',
|
||||
'relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-blue-600 focus:ring-offset-2',
|
||||
]"
|
||||
>
|
||||
@@ -233,143 +153,62 @@
|
||||
/>
|
||||
</Switch>
|
||||
</SwitchGroup>
|
||||
<div class="relative">
|
||||
<label
|
||||
for="startup"
|
||||
class="block text-sm font-medium leading-6 text-zinc-100"
|
||||
>{{ $t("library.admin.import.version.launchCmd") }}</label
|
||||
>
|
||||
<p class="text-zinc-400 text-xs">
|
||||
{{ $t("library.admin.import.version.launchDesc") }}
|
||||
</p>
|
||||
<div class="mt-2">
|
||||
<div
|
||||
class="flex w-fit rounded-md shadow-sm bg-zinc-950 ring-1 ring-inset ring-zinc-800 focus-within:ring-2 focus-within:ring-inset focus-within:ring-blue-600"
|
||||
>
|
||||
<span
|
||||
class="flex select-none items-center pl-3 text-zinc-500 sm:text-sm"
|
||||
>{{ $t("library.admin.import.version.installDir") }}</span
|
||||
>
|
||||
<Combobox
|
||||
as="div"
|
||||
:value="versionSettings.launch"
|
||||
nullable
|
||||
@update:model-value="(v) => updateLaunchCommand(v)"
|
||||
>
|
||||
<div class="relative">
|
||||
<ComboboxInput
|
||||
class="block flex-1 border-0 py-1.5 pl-1 bg-transparent text-zinc-100 placeholder:text-zinc-400 focus:ring-0 sm:text-sm sm:leading-6"
|
||||
:placeholder="
|
||||
$t('library.admin.import.version.launchPlaceholder')
|
||||
"
|
||||
@change="launchProcessQuery = $event.target.value"
|
||||
@blur="launchProcessQuery = ''"
|
||||
/>
|
||||
<ComboboxButton
|
||||
v-if="launchFilteredVersionGuesses?.length ?? 0 > 0"
|
||||
class="absolute inset-y-0 right-0 flex items-center rounded-r-md px-2 focus:outline-none"
|
||||
>
|
||||
<ChevronUpDownIcon
|
||||
class="size-5 text-gray-400"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</ComboboxButton>
|
||||
|
||||
<ComboboxOptions
|
||||
class="absolute z-10 mt-1 max-h-60 w-full overflow-auto rounded-md bg-zinc-900 py-1 text-base shadow-lg ring-1 ring-white/5 focus:outline-none sm:text-sm"
|
||||
>
|
||||
<ComboboxOption
|
||||
v-for="guess in launchFilteredVersionGuesses"
|
||||
:key="guess.filename"
|
||||
v-slot="{ active, selected }"
|
||||
:value="guess.filename"
|
||||
as="template"
|
||||
>
|
||||
<li
|
||||
:class="[
|
||||
'relative cursor-default select-none py-2 pl-3 pr-9',
|
||||
active
|
||||
? 'bg-blue-600 text-white outline-none'
|
||||
: 'text-zinc-100',
|
||||
]"
|
||||
>
|
||||
<span
|
||||
:class="[
|
||||
'inline-flex items-center gap-x-2 block truncate',
|
||||
selected && 'font-semibold',
|
||||
]"
|
||||
>
|
||||
{{ guess.filename }}
|
||||
<component
|
||||
:is="PLATFORM_ICONS[guess.platform as PlatformClient]"
|
||||
class="size-5"
|
||||
/>
|
||||
</span>
|
||||
|
||||
<span
|
||||
v-if="selected"
|
||||
:class="[
|
||||
'absolute inset-y-0 right-0 flex items-center pr-4',
|
||||
active ? 'text-white' : 'text-blue-600',
|
||||
]"
|
||||
>
|
||||
<CheckIcon class="size-5" aria-hidden="true" />
|
||||
</span>
|
||||
</li>
|
||||
</ComboboxOption>
|
||||
<ComboboxOption
|
||||
v-if="launchProcessQuery"
|
||||
v-slot="{ active, selected }"
|
||||
:value="launchProcessQuery"
|
||||
>
|
||||
<li
|
||||
:class="[
|
||||
'relative cursor-default select-none py-2 pl-3 pr-9',
|
||||
active
|
||||
? 'bg-blue-600 text-white outline-none'
|
||||
: 'text-zinc-100',
|
||||
]"
|
||||
>
|
||||
<span
|
||||
:class="['block truncate', selected && 'font-semibold']"
|
||||
>
|
||||
{{ $t("chars.quoted", { text: launchProcessQuery }) }}
|
||||
</span>
|
||||
|
||||
<span
|
||||
v-if="selected"
|
||||
:class="[
|
||||
'absolute inset-y-0 right-0 flex items-center pr-4',
|
||||
active ? 'text-white' : 'text-blue-600',
|
||||
]"
|
||||
>
|
||||
<CheckIcon class="size-5" aria-hidden="true" />
|
||||
</span>
|
||||
</li>
|
||||
</ComboboxOption>
|
||||
</ComboboxOptions>
|
||||
</div>
|
||||
</Combobox>
|
||||
<input
|
||||
id="startup"
|
||||
v-model="versionSettings.launchArgs"
|
||||
type="text"
|
||||
name="startup"
|
||||
class="border-l border-zinc-700 block flex-1 border-0 py-1.5 pl-2 bg-transparent text-zinc-100 placeholder:text-zinc-400 focus:ring-0 sm:text-sm sm:leading-6"
|
||||
placeholder="--launch"
|
||||
/>
|
||||
</div>
|
||||
<!-- launch executables -->
|
||||
<div class="relative flex flex-col gap-y-2 bg-zinc-800 p-4 rounded-xl">
|
||||
<div>
|
||||
<label class="block text-sm font-medium leading-6 text-zinc-100">{{
|
||||
$t("library.admin.import.version.launchCmd")
|
||||
}}</label>
|
||||
<p class="text-zinc-400 text-xs">
|
||||
{{ $t("library.admin.import.version.launchDesc") }}
|
||||
</p>
|
||||
</div>
|
||||
<ol
|
||||
v-if="versionSettings.launches.length > 0"
|
||||
class="divide-y-1 divide-zinc-700"
|
||||
>
|
||||
<li
|
||||
v-for="(launch, launchIdx) in versionSettings.launches"
|
||||
:key="launchIdx"
|
||||
class="py-2 inline-flex items-start gap-x-1 w-full"
|
||||
>
|
||||
<ImportVersionLaunchRow
|
||||
v-model="versionSettings.launches[launchIdx]"
|
||||
:version-guesses="versionGuesses"
|
||||
:needs-name="true"
|
||||
/>
|
||||
<button
|
||||
class="transition rounded p-1 bg-zinc-900/30 group hover:bg-red-600/30"
|
||||
@click="() => versionSettings.launches.splice(launchIdx, 1)"
|
||||
>
|
||||
<TrashIcon
|
||||
class="transition size-5 text-zinc-700 group-hover:text-red-700"
|
||||
/>
|
||||
</button>
|
||||
</li>
|
||||
</ol>
|
||||
<span
|
||||
v-else
|
||||
class="text-sm text-zinc-700 uppercase font-display font-bold"
|
||||
>{{ $t("library.admin.import.version.noLaunches") }}</span
|
||||
>
|
||||
<LoadingButton
|
||||
:loading="false"
|
||||
class="w-fit"
|
||||
@click="() => versionSettings.launches.push({} as any)"
|
||||
>{{ $t("common.add") }}</LoadingButton
|
||||
>
|
||||
|
||||
<div
|
||||
v-if="versionSettings.onlySetup"
|
||||
class="absolute inset-0 bg-zinc-900/50"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<PlatformSelector v-model="versionSettings.platform">
|
||||
{{ $t("library.admin.import.version.platform") }}
|
||||
</PlatformSelector>
|
||||
<SwitchGroup as="div" class="flex items-center justify-between">
|
||||
<SwitchGroup
|
||||
as="div"
|
||||
class="bg-zinc-800 p-4 rounded-xl flex items-center gap-4 justify-between"
|
||||
>
|
||||
<span class="flex flex-grow flex-col">
|
||||
<SwitchLabel
|
||||
as="span"
|
||||
@@ -385,7 +224,7 @@
|
||||
<Switch
|
||||
v-model="versionSettings.delta"
|
||||
:class="[
|
||||
versionSettings.delta ? 'bg-blue-600' : 'bg-zinc-800',
|
||||
versionSettings.delta ? 'bg-blue-600' : 'bg-zinc-900',
|
||||
'relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-blue-600 focus:ring-offset-2',
|
||||
]"
|
||||
>
|
||||
@@ -398,89 +237,9 @@
|
||||
/>
|
||||
</Switch>
|
||||
</SwitchGroup>
|
||||
<Disclosure v-slot="{ open }" as="div" class="py-2">
|
||||
<dt>
|
||||
<DisclosureButton
|
||||
class="border-b border-zinc-600 pb-2 flex w-full items-start justify-between text-left text-zinc-100"
|
||||
>
|
||||
<span class="text-base/7 font-semibold">
|
||||
{{ $t("library.admin.import.version.advancedOptions") }}
|
||||
</span>
|
||||
<span class="ml-6 flex h-7 items-center">
|
||||
<ChevronUpIcon v-if="!open" class="size-6" aria-hidden="true" />
|
||||
<ChevronDownIcon v-else class="size-6" aria-hidden="true" />
|
||||
</span>
|
||||
</DisclosureButton>
|
||||
</dt>
|
||||
<DisclosurePanel
|
||||
as="dd"
|
||||
class="bg-zinc-950/30 p-3 rounded-b-lg mt-2 flex flex-col gap-y-4"
|
||||
>
|
||||
<!-- UMU launcher configuration -->
|
||||
<div
|
||||
v-if="versionSettings.platform == PlatformClient.Windows"
|
||||
class="flex flex-col gap-y-4"
|
||||
>
|
||||
<SwitchGroup as="div" class="flex items-center justify-between">
|
||||
<span class="flex flex-grow flex-col">
|
||||
<SwitchLabel
|
||||
as="span"
|
||||
class="text-sm font-medium leading-6 text-zinc-100"
|
||||
passive
|
||||
>
|
||||
{{ $t("library.admin.import.version.umuOverride") }}
|
||||
</SwitchLabel>
|
||||
<SwitchDescription as="span" class="text-sm text-zinc-400">
|
||||
{{ $t("library.admin.import.version.umuOverrideDesc") }}
|
||||
</SwitchDescription>
|
||||
</span>
|
||||
<Switch
|
||||
v-model="umuIdEnabled"
|
||||
:class="[
|
||||
umuIdEnabled ? 'bg-blue-600' : 'bg-zinc-800',
|
||||
'relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-blue-600 focus:ring-offset-2',
|
||||
]"
|
||||
>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
:class="[
|
||||
umuIdEnabled ? 'translate-x-5' : 'translate-x-0',
|
||||
'pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out',
|
||||
]"
|
||||
/>
|
||||
</Switch>
|
||||
</SwitchGroup>
|
||||
<div>
|
||||
<label
|
||||
for="umu-id"
|
||||
class="block text-sm font-medium leading-6 text-zinc-100"
|
||||
>
|
||||
{{ $t("library.admin.import.version.umuLauncherId") }}
|
||||
</label>
|
||||
<div class="mt-2">
|
||||
<input
|
||||
id="umu-id"
|
||||
v-model="umuId"
|
||||
name="umu-id"
|
||||
type="text"
|
||||
autocomplete="umu-id"
|
||||
required
|
||||
:disabled="!umuIdEnabled"
|
||||
placeholder="umu-starcitizen"
|
||||
class="block w-full rounded-md border-0 py-1.5 px-3 bg-zinc-950 disabled:bg-zinc-900/80 text-zinc-100 disabled:text-zinc-400 shadow-sm ring-1 ring-inset ring-zinc-700 disabled:ring-zinc-800 placeholder:text-zinc-400 focus:ring-2 focus:ring-inset focus:ring-blue-600 sm:text-sm sm:leading-6"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-else class="text-zinc-400">
|
||||
{{ $t("library.admin.import.version.noAdv") }}
|
||||
</div>
|
||||
</DisclosurePanel>
|
||||
</Disclosure>
|
||||
|
||||
<LoadingButton
|
||||
class="w-fit"
|
||||
class="w-fit ml-auto"
|
||||
:loading="importLoading"
|
||||
@click="startImport_wrapper"
|
||||
>
|
||||
@@ -536,18 +295,15 @@ import {
|
||||
SwitchDescription,
|
||||
SwitchGroup,
|
||||
SwitchLabel,
|
||||
Disclosure,
|
||||
DisclosureButton,
|
||||
DisclosurePanel,
|
||||
Combobox,
|
||||
ComboboxButton,
|
||||
ComboboxInput,
|
||||
ComboboxOption,
|
||||
ComboboxOptions,
|
||||
} from "@headlessui/vue";
|
||||
import { XCircleIcon } from "@heroicons/vue/16/solid";
|
||||
import { CheckIcon, ChevronUpDownIcon } from "@heroicons/vue/20/solid";
|
||||
import { ChevronDownIcon, ChevronUpIcon } from "@heroicons/vue/24/solid";
|
||||
import {
|
||||
CheckIcon,
|
||||
ChevronUpDownIcon,
|
||||
TrashIcon,
|
||||
} from "@heroicons/vue/20/solid";
|
||||
import type { Platform } from "~/prisma/client/enums";
|
||||
import type { ImportVersion } from "~/server/api/v1/admin/import/version/index.post";
|
||||
|
||||
definePageMeta({
|
||||
layout: "admin",
|
||||
@@ -561,76 +317,16 @@ const versions = await $dropFetch(
|
||||
`/api/v1/admin/import/version?id=${encodeURIComponent(gameId)}`,
|
||||
);
|
||||
const currentlySelectedVersion = ref(-1);
|
||||
const versionSettings = ref<{
|
||||
platform: PlatformClient | undefined;
|
||||
|
||||
onlySetup: boolean;
|
||||
launch: string;
|
||||
launchArgs: string;
|
||||
setup: string;
|
||||
setupArgs: string;
|
||||
|
||||
delta: boolean;
|
||||
umuId: string;
|
||||
}>({
|
||||
platform: undefined,
|
||||
launch: "",
|
||||
launchArgs: "",
|
||||
setup: "",
|
||||
setupArgs: "",
|
||||
const versionSettings = ref<typeof ImportVersion.infer>({
|
||||
id: gameId,
|
||||
version: "",
|
||||
delta: false,
|
||||
onlySetup: false,
|
||||
umuId: "",
|
||||
launches: [],
|
||||
setups: [],
|
||||
});
|
||||
|
||||
const versionGuesses =
|
||||
ref<Array<{ platform: PlatformClient; filename: string }>>();
|
||||
const launchProcessQuery = ref("");
|
||||
const setupProcessQuery = ref("");
|
||||
|
||||
const launchFilteredVersionGuesses = computed(() =>
|
||||
versionGuesses.value?.filter((e) =>
|
||||
e.filename.toLowerCase().includes(launchProcessQuery.value.toLowerCase()),
|
||||
),
|
||||
);
|
||||
const setupFilteredVersionGuesses = computed(() =>
|
||||
versionGuesses.value?.filter((e) =>
|
||||
e.filename.toLowerCase().includes(setupProcessQuery.value.toLowerCase()),
|
||||
),
|
||||
);
|
||||
|
||||
function updateLaunchCommand(value: string) {
|
||||
versionSettings.value.launch = value;
|
||||
autosetPlatform(value);
|
||||
}
|
||||
|
||||
function updateSetupCommand(value: string) {
|
||||
versionSettings.value.setup = value;
|
||||
autosetPlatform(value);
|
||||
}
|
||||
|
||||
function autosetPlatform(value: string) {
|
||||
if (!versionGuesses.value) return;
|
||||
if (versionSettings.value.platform) return;
|
||||
const guessIndex = versionGuesses.value.findIndex(
|
||||
(e) => e.filename === value,
|
||||
);
|
||||
if (guessIndex == -1) return;
|
||||
versionSettings.value.platform = versionGuesses.value[guessIndex].platform;
|
||||
}
|
||||
|
||||
const umuIdEnabled = ref(false);
|
||||
const umuId = computed({
|
||||
get() {
|
||||
if (umuIdEnabled.value) return versionSettings.value.umuId;
|
||||
return undefined;
|
||||
},
|
||||
set(v) {
|
||||
if (umuIdEnabled.value && v) {
|
||||
versionSettings.value.umuId = v;
|
||||
}
|
||||
},
|
||||
});
|
||||
const versionGuesses = ref<Array<{ platform: Platform; filename: string }>>();
|
||||
|
||||
const importLoading = ref(false);
|
||||
const importError = ref<string | undefined>();
|
||||
@@ -639,15 +335,19 @@ async function updateCurrentlySelectedVersion(value: number) {
|
||||
if (currentlySelectedVersion.value == value) return;
|
||||
currentlySelectedVersion.value = value;
|
||||
const version = versions[currentlySelectedVersion.value];
|
||||
const results = await $dropFetch(
|
||||
`/api/v1/admin/import/version/preload?id=${encodeURIComponent(
|
||||
gameId,
|
||||
)}&version=${encodeURIComponent(version)}`,
|
||||
);
|
||||
versionGuesses.value = results.map((e) => ({
|
||||
...e,
|
||||
platform: e.platform as PlatformClient,
|
||||
}));
|
||||
try {
|
||||
const results = await $dropFetch(
|
||||
`/api/v1/admin/import/version/preload?id=${encodeURIComponent(
|
||||
gameId,
|
||||
)}&version=${encodeURIComponent(version)}`,
|
||||
{
|
||||
failTitle: "Failed to fetch version information",
|
||||
},
|
||||
);
|
||||
versionGuesses.value = results as typeof versionGuesses.value;
|
||||
} catch {
|
||||
currentlySelectedVersion.value = -1;
|
||||
}
|
||||
}
|
||||
|
||||
async function startImport() {
|
||||
@@ -655,9 +355,9 @@ async function startImport() {
|
||||
const taskId = await $dropFetch("/api/v1/admin/import/version", {
|
||||
method: "POST",
|
||||
body: {
|
||||
...versionSettings.value,
|
||||
id: gameId,
|
||||
version: versions[currentlySelectedVersion.value],
|
||||
...versionSettings.value,
|
||||
},
|
||||
});
|
||||
router.push(`/admin/task/${taskId.taskId}`);
|
||||
|
||||
@@ -3,11 +3,11 @@
|
||||
class="pt-8 lg:pt-0 lg:pl-20 fixed inset-0 flex flex-col overflow-auto bg-zinc-900"
|
||||
>
|
||||
<div
|
||||
class="bg-zinc-950 w-full flex flex-col sm:flex-row items-center gap-2 justify-between pr-2"
|
||||
class="bg-zinc-950 w-full flex flex-row items-center gap-2 justify-between px-2 pt-6 lg:pt-0"
|
||||
>
|
||||
<!--start-->
|
||||
<div>
|
||||
<Listbox v-if="false" v-model="currentMode" as="div">
|
||||
<Listbox v-model="currentMode" as="div" class="sm:hidden mb-2">
|
||||
<div class="relative mt-2">
|
||||
<ListboxButton
|
||||
class="min-w-[10vw] w-full cursor-default inline-flex items-center gap-x-2 rounded-md bg-zinc-900 py-1.5 pr-2 pl-3 text-left text-zinc-200 outline-1 -outline-offset-1 outline-zinc-700 focus:outline-2 focus:-outline-offset-2 focus:outline-blue-600 sm:text-sm/6"
|
||||
@@ -68,7 +68,7 @@
|
||||
</div>
|
||||
</Listbox>
|
||||
|
||||
<div class="pt-4 inline-flex gap-x-2">
|
||||
<div class="hidden sm:inline-flex pt-4 gap-x-2">
|
||||
<div
|
||||
v-for="[value, { icon }] in Object.entries(components)"
|
||||
:key="value"
|
||||
@@ -93,7 +93,7 @@
|
||||
<NuxtLink
|
||||
:href="`/store/${game.id}`"
|
||||
type="button"
|
||||
class="inline-flex w-fit items-center gap-x-2 rounded-md bg-zinc-800 px-3 py-1 text-sm font-semibold font-display text-white shadow-sm hover:bg-zinc-700 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-600"
|
||||
class="whitespace-nowrap inline-flex w-fit items-center gap-x-2 rounded-md bg-zinc-800 px-3 py-1 text-sm font-semibold font-display text-white shadow-sm hover:bg-zinc-700 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-600"
|
||||
>
|
||||
{{ $t("library.admin.openStore") }}
|
||||
<ArrowTopRightOnSquareIcon
|
||||
|
||||
@@ -42,6 +42,7 @@
|
||||
import {
|
||||
BuildingStorefrontIcon,
|
||||
CodeBracketIcon,
|
||||
ServerIcon,
|
||||
} from "@heroicons/vue/24/outline";
|
||||
|
||||
const navigation: Array<NavigationItem & { icon: Component }> = [
|
||||
@@ -57,6 +58,12 @@ const navigation: Array<NavigationItem & { icon: Component }> = [
|
||||
prefix: "/admin/settings/tokens",
|
||||
icon: CodeBracketIcon,
|
||||
},
|
||||
{
|
||||
label: "Services",
|
||||
route: "/admin/settings/services",
|
||||
prefix: "/admin/settings/services",
|
||||
icon: ServerIcon,
|
||||
},
|
||||
];
|
||||
|
||||
// const notifications = useNotifications();
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
<template>
|
||||
<div class="max-w-xl">
|
||||
<div
|
||||
class="divide-y divide-white/10 overflow-hidden rounded-lg bg-zinc-900 outline -outline-offset-1 outline-white/20 sm:grid sm:grid-cols-2 sm:divide-y-0"
|
||||
>
|
||||
<div
|
||||
v-for="(service, serviceIdx) in services"
|
||||
:key="service.name"
|
||||
:class="[
|
||||
serviceIdx === 0
|
||||
? 'rounded-tl-lg rounded-tr-lg sm:rounded-tr-none'
|
||||
: '',
|
||||
serviceIdx === 1 ? 'sm:rounded-tr-lg' : '',
|
||||
serviceIdx === services.length - 2 ? 'sm:rounded-bl-lg' : '',
|
||||
serviceIdx === services.length - 1
|
||||
? 'rounded-br-lg rounded-bl-lg sm:rounded-bl-none'
|
||||
: '',
|
||||
'group relative border-white/10 bg-zinc-800/50 p-6 focus-within:outline-2 focus-within:-outline-offset-2 focus-within:outline-indigo-500 sm:odd:not-nth-last-2:border-b sm:even:border-l sm:even:not-last:border-b',
|
||||
]"
|
||||
>
|
||||
<div>
|
||||
<span
|
||||
:class="[
|
||||
serviceMetadata[service.name].iconBackground,
|
||||
serviceMetadata[service.name].iconForeground,
|
||||
'inline-flex rounded-lg p-3',
|
||||
]"
|
||||
>
|
||||
<component
|
||||
:is="serviceMetadata[service.name].icon"
|
||||
class="size-6"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
<div class="mt-8">
|
||||
<h3 class="text-base font-semibold text-white">
|
||||
<a :href="service.href" class="focus:outline-hidden">
|
||||
<!-- Extend touch target to entire panel -->
|
||||
<span class="absolute inset-0" aria-hidden="true"></span>
|
||||
{{ serviceMetadata[service.name].title }}
|
||||
</a>
|
||||
</h3>
|
||||
<p class="mt-2 text-sm text-zinc-400">
|
||||
{{ serviceMetadata[service.name].description }}
|
||||
</p>
|
||||
</div>
|
||||
<span
|
||||
class="pointer-events-none absolute top-6 right-6"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<CheckIcon
|
||||
:class="[
|
||||
'size-6',
|
||||
service.healthy ? 'text-green-600' : 'text-zinc-500',
|
||||
]"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ArrowDownTrayIcon, CheckIcon } from "@heroicons/vue/24/outline";
|
||||
|
||||
definePageMeta({
|
||||
layout: "admin",
|
||||
});
|
||||
|
||||
const services = await $dropFetch("/api/v1/admin/services");
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const serviceMetadata = computed(() => ({
|
||||
torrential: {
|
||||
title: t("services.torrential.title"),
|
||||
description: t("services.torrential.description"),
|
||||
iconForeground: "text-blue-400",
|
||||
iconBackground: "bg-blue-500/10",
|
||||
icon: ArrowDownTrayIcon,
|
||||
},
|
||||
nginx: {
|
||||
title: t("services.nginx.title"),
|
||||
description: t("services.nginx.description"),
|
||||
iconForeground: "text-green-400",
|
||||
iconBackground: "bg-green-500/10",
|
||||
icon: ArrowDownTrayIcon,
|
||||
},
|
||||
}));
|
||||
</script>
|
||||
@@ -206,7 +206,6 @@ async function createToken(
|
||||
},
|
||||
failTitle: "Failed to create API token.",
|
||||
});
|
||||
console.log(result);
|
||||
newToken.value = result.token;
|
||||
tokens.value.push(result);
|
||||
} catch {
|
||||
|
||||
@@ -44,8 +44,25 @@
|
||||
</div>
|
||||
{{ task.name }}
|
||||
</h1>
|
||||
<ul class="flex flex-row items-center h-12 gap-x-3">
|
||||
<li
|
||||
v-for="[name, link] in task.actions.map((v) => v.split(':'))"
|
||||
:key="link"
|
||||
>
|
||||
<NuxtLink :href="link">
|
||||
<LoadingButton :loading="false"> {{ name }} </LoadingButton>
|
||||
</NuxtLink>
|
||||
</li>
|
||||
<li
|
||||
v-if="task.actions.length == 0"
|
||||
class="text-md uppercase font-display font-bold text-zinc-700"
|
||||
>
|
||||
No actions
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div
|
||||
class="bg-zinc-950 p-2 rounded-md h-[80vh] flex flex-col flex-col-reverse overflow-y-scroll gap-y-1"
|
||||
class="bg-zinc-950 p-2 rounded-md h-[70vh] flex flex-col flex-col-reverse overflow-y-scroll gap-y-1"
|
||||
>
|
||||
<LogLine
|
||||
v-for="(_, idx) in task.log"
|
||||
|
||||
@@ -164,8 +164,8 @@ const scheduledTasks: {
|
||||
description: "",
|
||||
},
|
||||
debug: {
|
||||
name: "Debug Task",
|
||||
description: "Does debugging things.",
|
||||
name: "",
|
||||
description: "",
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user