In-app store, torrential backend, locales (#332)

* feat: add store nav and fixes

* fix: reduce password requirement & new task error ui

* fix: client webtoken fix

* fix: delta versions and dockerfile

* fix: use setup platforms for filter & display

* fix: setup not accounted when returning valid options

* feat: tighter delta version support

* feat: dl/disk size

* feat: offload manifest generation to torrential

* fix: bump torrential

* feat: remove droplet

* feat: bump torrential

* feat: convert locales
This commit is contained in:
DecDuck
2026-02-06 00:12:24 +11:00
committed by GitHub
parent 6b614acfd8
commit 13c97cfcfc
82 changed files with 1737 additions and 967 deletions
+4 -18
View File
@@ -173,7 +173,7 @@
:title="t('home.admin.biggestGamesToDownload')"
:subtitle="t('home.admin.latestVersionOnly')"
>
<RankingList :items="biggestGamesLatest.map(gameToRankItem)" />
<!-- <RankingList :items="biggestGamesLatest.map(gameToRankItem)" />-->
</TileWithLink>
</div>
<div class="col-span-6 lg:col-span-2">
@@ -181,7 +181,7 @@
:title="t('home.admin.biggestGamesOnServer')"
:subtitle="t('home.admin.allVersionsCombined')"
>
<RankingList :items="biggestGamesCombined.map(gameToRankItem)" />
<!-- <RankingList :items="biggestGamesCombined.map(gameToRankItem)" />-->
</TileWithLink>
</div>
</div>
@@ -196,8 +196,6 @@ import DropLogo from "~/components/DropLogo.vue";
import { ServerStackIcon, UserGroupIcon } from "@heroicons/vue/24/outline";
import { getPercentage } from "~/utils/utils";
import { getBarColor } from "~/utils/colors";
import type { GameSize } from "~/server/internal/gamesize";
import type { RankItem } from "~/components/RankingList.vue";
definePageMeta({
layout: "admin",
@@ -211,20 +209,8 @@ const { t } = useI18n();
const systemData = useSystemData();
const {
version,
gameCount,
sources,
userStats,
biggestGamesLatest,
biggestGamesCombined,
} = await $dropFetch("/api/v1/admin/home");
const gameToRankItem = (game: GameSize, rank: number): RankItem => ({
rank: rank + 1,
name: game.gameName,
value: formatBytes(game.size),
});
const { version, gameCount, sources, userStats } =
await $dropFetch("/api/v1/admin/home");
const pieChartData = [
{
+3 -3
View File
@@ -191,9 +191,9 @@
<span v-if="launch.name" class="text-sm font-semibold">{{
launch.name
}}</span>
<span v-else class="text-sm text-zinc-500 italic"
>No name provided.</span
>
<span v-else class="text-sm text-zinc-500 italic">{{
$t("library.admin.import.version.noNameProvided")
}}</span>
<span class="ml-auto flex h-7 items-center">
<PlusIcon v-if="!open" class="size-6" aria-hidden="true" />
<MinusIcon v-else class="size-6" aria-hidden="true" />
+3 -2
View File
@@ -115,13 +115,14 @@
<div v-if="currentlySelectedGame !== -1" class="flex flex-col gap-y-4">
<fieldset>
<legend class="text-sm/6 font-semibold text-white">Import as</legend>
<legend class="text-sm/6 font-semibold text-white">
{{ $t("library.admin.import.importAs") }}
</legend>
<div class="mt-6 grid grid-cols-1 gap-y-6 sm:grid-cols-3 sm:gap-x-4">
<label
v-for="[type, meta] in Object.entries(importModes)"
:key="type"
:aria-label="meta.title"
:aria-description="`Import as ${meta.title}`"
class="cursor-pointer group relative flex rounded-lg border border-white/10 bg-gray-800/50 p-4 has-checked:bg-blue-500/10 has-checked:outline-2 has-checked:-outline-offset-2 has-checked:outline-blue-500 has-focus-visible:outline-3 has-focus-visible:-outline-offset-1 has-disabled:bg-gray-800 has-disabled:opacity-25"
>
<input
+1 -1
View File
@@ -161,7 +161,7 @@
class="w-fit rounded-md bg-red-600 px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm transition-all duration-200 hover:bg-red-500 hover:scale-105 hover:shadow-lg hover:shadow-red-500/25 active:scale-95 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-red-600"
@click="() => deleteGame(game.id)"
>
{{ $t("delete") }}
{{ $t("common.delete") }}
</button>
</div>
</div>
@@ -78,7 +78,7 @@
class="w-fit rounded-md bg-red-600 px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm transition-all duration-200 hover:bg-red-500 hover:scale-105 hover:shadow-lg hover:shadow-red-500/25 active:scale-95 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-red-600"
@click="() => deleteCompany(company.id)"
>
{{ $t("delete") }}
{{ $t("common.delete") }}
</button>
</div>
</div>
+24 -28
View File
@@ -11,39 +11,35 @@
</i18n-t>
</NuxtLink>
<div
v-if="task && task.error"
class="grow w-full flex items-center justify-center"
>
<div class="flex flex-col items-center">
<ExclamationCircleIcon
class="h-12 w-12 text-red-600"
aria-hidden="true"
/>
<div class="mt-3 text-center sm:mt-5">
<h1
class="text-3xl font-semibold font-display leading-6 text-zinc-100"
>
{{ task.error.title }}
</h1>
<div class="mt-4">
<p class="text-sm text-zinc-400 max-w-md">
{{ task.error.description }}
</p>
</div>
</div>
</div>
</div>
<div v-else-if="task" class="flex flex-col w-full gap-y-4">
<div v-if="task" class="flex flex-col w-full gap-y-4">
<h1
class="inline-flex items-center gap-x-3 text-3xl text-zinc-100 font-bold font-display"
>
<div>
<CheckCircleIcon v-if="task.success" class="size-5 text-green-600" />
<CheckCircleIcon v-if="task.success" class="size-8 text-green-600" />
<XMarkIcon v-else-if="task.error" class="size-8 text-red-600" />
<div v-else class="size-4 bg-blue-600 rounded-full animate-pulse" />
</div>
{{ task.name }}
</h1>
<div
v-if="task.error"
class="rounded-md bg-red-500/15 p-4 outline outline-red-500/25"
>
<div class="flex">
<div class="shrink-0">
<XCircleIcon class="size-5 text-red-400" aria-hidden="true" />
</div>
<div class="ml-3">
<h3 class="text-sm font-medium text-red-200">
{{ task.error.title }}
</h3>
<div class="mt-2 text-sm text-red-200/80">
{{ task.error.description }}
</div>
</div>
</div>
</div>
<ul class="flex flex-row items-center h-12 gap-x-3">
<li
v-for="[name, link] in task.actions.map((v) => v.split(':'))"
@@ -57,7 +53,7 @@
v-if="task.actions.length == 0"
class="text-md uppercase font-display font-bold text-zinc-700"
>
No actions
{{ $t("tasks.admin.noActions") }}
</li>
</ul>
@@ -95,8 +91,8 @@
</template>
<script setup lang="ts">
import { CheckCircleIcon } from "@heroicons/vue/16/solid";
import { ExclamationCircleIcon } from "@heroicons/vue/24/solid";
import { CheckCircleIcon } from "@heroicons/vue/24/solid";
import { XMarkIcon, XCircleIcon } from "@heroicons/vue/24/outline";
const route = useRoute();
const taskId = route.params.id.toString();