refactor: Huge pyro servers composable cleanup (#3745)

* refactor: start refactor of pyro servers module-based class

* refactor: finish modules

* refactor: start on type checking + matching api

* refactor: finish pyro servers composable refactor

* refactor: pyro -> modrinth

* fix: import not refactored

* fix: broken power action enums

* fix: remove pyro mentions

* fix: lint

* refactor: fix option pages

* fix: error renames

* remove empty pyro-servers.ts file

---------

Signed-off-by: IMB11 <hendersoncal117@gmail.com>
Co-authored-by: Prospector <prospectordev@gmail.com>
This commit is contained in:
IMB11
2025-06-11 23:32:39 +01:00
committed by GitHub
parent 6955731def
commit 1b1d41605b
77 changed files with 1791 additions and 2513 deletions

View File

@@ -45,9 +45,11 @@
import { ref, nextTick, computed } from "vue";
import { ButtonStyled, NewModal } from "@modrinth/ui";
import { IssuesIcon, PlusIcon, XIcon } from "@modrinth/assets";
import { ModrinthServersFetchError, type ServerBackup } from "@modrinth/utils";
import { ModrinthServer } from "~/composables/servers/modrinth-servers.ts";
const props = defineProps<{
server: Server<["general", "content", "backups", "network", "startup", "ws", "fs"]>;
server: ModrinthServer;
}>();
const modal = ref<InstanceType<typeof NewModal>>();
@@ -64,7 +66,7 @@ const trimmedName = computed(() => backupName.value.trim());
const nameExists = computed(() => {
if (!props.server.backups?.data) return false;
return props.server.backups.data.some(
(backup) => backup.name.trim().toLowerCase() === trimmedName.value.toLowerCase(),
(backup: ServerBackup) => backup.name.trim().toLowerCase() === trimmedName.value.toLowerCase(),
);
});
@@ -98,7 +100,7 @@ const createBackup = async () => {
hideModal();
await props.server.refresh();
} catch (error) {
if (error instanceof PyroFetchError && error.statusCode === 429) {
if (error instanceof ModrinthServersFetchError && error?.statusCode === 429) {
isRateLimited.value = true;
addNotification({
type: "error",

View File

@@ -20,13 +20,9 @@
<script setup lang="ts">
import { ref } from "vue";
import { ConfirmModal } from "@modrinth/ui";
import type { Server } from "~/composables/pyroServers";
import type { Backup } from "@modrinth/utils";
import BackupItem from "~/components/ui/servers/BackupItem.vue";
defineProps<{
server: Server<["general", "content", "backups", "network", "startup", "ws", "fs"]>;
}>();
const emit = defineEmits<{
(e: "delete", backup: Backup | undefined): void;
}>();

View File

@@ -17,7 +17,7 @@ import {
import { ButtonStyled, commonMessages, OverflowMenu, ProgressBar } from "@modrinth/ui";
import { defineMessages, useVIntl } from "@vintl/vintl";
import { ref } from "vue";
import type { Backup } from "~/composables/pyroServers.ts";
import type { Backup } from "@modrinth/utils";
const flags = useFeatureFlags();
const { formatMessage } = useVIntl();

View File

@@ -48,10 +48,11 @@
import { ref, nextTick, computed } from "vue";
import { ButtonStyled, NewModal } from "@modrinth/ui";
import { SpinnerIcon, SaveIcon, XIcon, IssuesIcon } from "@modrinth/assets";
import type { Server } from "~/composables/pyroServers";
import type { Backup } from "@modrinth/utils";
import { ModrinthServer } from "~/composables/servers/modrinth-servers.ts";
const props = defineProps<{
server: Server<["general", "content", "backups", "network", "startup", "ws", "fs"]>;
server: ModrinthServer;
}>();
const modal = ref<InstanceType<typeof NewModal>>();
@@ -70,7 +71,7 @@ const nameExists = computed(() => {
}
return props.server.backups.data.some(
(backup) => backup.name.trim().toLowerCase() === trimmedName.value.toLowerCase(),
(backup: Backup) => backup.name.trim().toLowerCase() === trimmedName.value.toLowerCase(),
);
});

View File

@@ -19,11 +19,12 @@
<script setup lang="ts">
import { ref } from "vue";
import { ConfirmModal, NewModal } from "@modrinth/ui";
import type { Server } from "~/composables/pyroServers";
import type { Backup } from "@modrinth/utils";
import BackupItem from "~/components/ui/servers/BackupItem.vue";
import { ModrinthServer } from "~/composables/servers/modrinth-servers.ts";
const props = defineProps<{
server: Server<["general", "content", "backups", "network", "startup", "ws", "fs"]>;
server: ModrinthServer;
}>();
const modal = ref<InstanceType<typeof NewModal>>();

View File

@@ -59,10 +59,10 @@
import { ButtonStyled, NewModal } from "@modrinth/ui";
import { XIcon, SaveIcon } from "@modrinth/assets";
import { ref, computed } from "vue";
import type { Server } from "~/composables/pyroServers";
import { ModrinthServer } from "~/composables/servers/modrinth-servers.ts";
const props = defineProps<{
server: Server<["backups"]>;
server: ModrinthServer;
}>();
const modal = ref<InstanceType<typeof NewModal>>();

View File

@@ -239,7 +239,7 @@ import {
import { Admonition, ButtonStyled, NewModal } from "@modrinth/ui";
import TagItem from "@modrinth/ui/src/components/base/TagItem.vue";
import { ref, computed } from "vue";
import { formatCategory, formatVersionsForDisplay, type Version } from "@modrinth/utils";
import { formatCategory, formatVersionsForDisplay, type Mod, type Version } from "@modrinth/utils";
import Accordion from "~/components/ui/Accordion.vue";
import Checkbox from "~/components/ui/Checkbox.vue";
import ContentVersionFilter, {

View File

@@ -99,7 +99,7 @@
import { FolderOpenIcon, CheckCircleIcon, XCircleIcon } from "@modrinth/assets";
import { ButtonStyled } from "@modrinth/ui";
import { ref, computed, watch, nextTick } from "vue";
import type { FSModule } from "~/composables/pyroServers.ts";
import { FSModule } from "~/composables/servers/modules/fs.ts";
interface UploadItem {
file: File;

View File

@@ -75,13 +75,14 @@
<script setup lang="ts">
import { ExternalIcon, SpinnerIcon, DownloadIcon, XIcon } from "@modrinth/assets";
import { BackupWarning, ButtonStyled, NewModal } from "@modrinth/ui";
import { ModrinthServersFetchError } from "@modrinth/utils";
import { ref, computed, nextTick } from "vue";
import { handleError, type Server } from "~/composables/pyroServers.ts";
import { handleError, ModrinthServer } from "~/composables/servers/modrinth-servers.ts";
const cf = ref(false);
const props = defineProps<{
server: Server<["general", "content", "backups", "network", "startup", "ws", "fs"]>;
server: ModrinthServer;
}>();
const modal = ref<typeof NewModal>();
@@ -110,24 +111,18 @@ const handleSubmit = async () => {
if (!error.value) {
// hide();
try {
const dry = await props.server.fs?.extractFile(trimmedUrl.value, true, true);
const dry = await props.server.fs.extractFile(trimmedUrl.value, true, true);
if (!cf.value || dry.modpack_name) {
await props.server.fs?.extractFile(trimmedUrl.value, true, false, true);
await props.server.fs.extractFile(trimmedUrl.value, true, false, true);
hide();
} else {
submitted.value = false;
handleError(
new ServersError(
new ModrinthServersFetchError(
"Could not find CurseForge modpack at that URL.",
undefined,
undefined,
undefined,
{
context: "Error installing modpack",
error: `url: ${url.value}`,
description: "Could not find CurseForge modpack at that URL.",
},
404,
new Error(`No modpack found at ${url.value}`),
),
);
}

View File

@@ -59,7 +59,7 @@
<template v-else>
<ButtonStyled v-if="showStopButton" type="transparent">
<button :disabled="!canTakeAction" @click="initiateAction('stop')">
<button :disabled="!canTakeAction" @click="initiateAction('Stop')">
<div class="flex gap-1">
<StopCircleIcon class="h-5 w-5" />
<span>{{ isStoppingState ? "Stopping..." : "Stop" }}</span>
@@ -120,14 +120,12 @@ import {
import { ButtonStyled, NewModal } from "@modrinth/ui";
import { useRouter } from "vue-router";
import { useStorage } from "@vueuse/core";
type ServerAction = "start" | "stop" | "restart" | "kill";
type ServerState = "stopped" | "starting" | "running" | "stopping" | "restarting";
import type { PowerAction as ServerPowerAction, ServerState } from "@modrinth/utils";
const flags = useFeatureFlags();
interface PowerAction {
action: ServerAction;
action: ServerPowerAction;
nextState: ServerState;
}
@@ -142,7 +140,7 @@ const props = defineProps<{
}>();
const emit = defineEmits<{
(e: "action", action: ServerAction): void;
(e: "action", action: ServerPowerAction): void;
}>();
const router = useRouter();
@@ -170,7 +168,7 @@ const isStoppingState = computed(() => serverState.value === "stopping");
const showStopButton = computed(() => isRunning.value || isStoppingState.value);
const primaryActionText = computed(() => {
const states: Record<ServerState, string> = {
const states: Partial<Record<ServerState, string>> = {
starting: "Starting...",
restarting: "Restarting...",
running: "Restart",
@@ -193,7 +191,7 @@ const menuOptions = computed(() => [
id: "kill",
label: "Kill server",
icon: SlashIcon,
action: () => initiateAction("kill"),
action: () => initiateAction("Kill"),
},
]),
{
@@ -221,17 +219,17 @@ async function copyId() {
await navigator.clipboard.writeText(serverId as string);
}
function initiateAction(action: ServerAction) {
function initiateAction(action: ServerPowerAction) {
if (!canTakeAction.value) return;
const stateMap: Record<ServerAction, ServerState> = {
start: "starting",
stop: "stopping",
restart: "restarting",
kill: "stopping",
const stateMap: Record<ServerPowerAction, ServerState> = {
Start: "starting",
Stop: "stopping",
Restart: "restarting",
Kill: "stopping",
};
if (action === "start") {
if (action === "Start") {
emit("action", action);
serverState.value = stateMap[action];
startingDelay.value = true;
@@ -249,7 +247,7 @@ function initiateAction(action: ServerAction) {
}
function handlePrimaryAction() {
initiateAction(isRunning.value ? "restart" : "start");
initiateAction(isRunning.value ? "Restart" : "Start");
}
function executePowerAction() {
@@ -263,7 +261,7 @@ function executePowerAction() {
userPreferences.value.powerDontAskAgain = true;
}
if (action === "start") {
if (action === "Start") {
startingDelay.value = true;
setTimeout(() => (startingDelay.value = false), 5000);
}

View File

@@ -40,7 +40,7 @@
<script setup lang="ts">
import { ref } from "vue";
import type { ServerState } from "~/types/servers";
import type { ServerState } from "@modrinth/utils";
const STATUS_CLASSES = {
running: { main: "bg-brand", bg: "bg-bg-green" },
@@ -49,7 +49,7 @@ const STATUS_CLASSES = {
unknown: { main: "", bg: "" },
} as const;
const STATUS_TEXTS = {
const STATUS_TEXTS: Partial<Record<ServerState, string>> = {
running: "Running",
stopped: "",
crashed: "Crashed",
@@ -63,7 +63,10 @@ defineProps<{
const isExpanded = ref(false);
function getStatusClass(state: ServerState) {
return STATUS_CLASSES[state] ?? STATUS_CLASSES.unknown;
if (state in STATUS_CLASSES) {
return STATUS_CLASSES[state as keyof typeof STATUS_CLASSES];
}
return STATUS_CLASSES.unknown;
}
function getStatusText(state: ServerState) {

View File

@@ -301,7 +301,7 @@ import { useDebounceFn } from "@vueuse/core";
import { NewModal } from "@modrinth/ui";
import ButtonStyled from "@modrinth/ui/src/components/base/ButtonStyled.vue";
import DOMPurify from "dompurify";
import { usePyroConsole } from "~/store/console.ts";
import { useModrinthServersConsole } from "~/store/console.ts";
const { $cosmetics } = useNuxtApp();
const cosmetics = $cosmetics;
@@ -318,7 +318,7 @@ const SEPARATOR_HEIGHT = 32;
const SCROLL_END_DELAY = 150;
const progressiveBlurIterations = ref(8);
const pyroConsole = usePyroConsole();
const pyroConsole = useModrinthServersConsole();
const consoleOutput = computed(() => (props.loading ? [] : pyroConsole.output.value));
const scrollContainer = ref<HTMLElement | null>(null);

View File

@@ -69,10 +69,11 @@
<script setup lang="ts">
import { ButtonStyled, NewModal } from "@modrinth/ui";
import { DownloadIcon, XIcon } from "@modrinth/assets";
import type { Server } from "~/composables/pyroServers";
import { ModrinthServersFetchError } from "@modrinth/utils";
import { ModrinthServer } from "~/composables/servers/modrinth-servers.ts";
const props = defineProps<{
server: Server<["general", "content", "backups", "network", "startup", "ws", "fs"]>;
server: ModrinthServer;
project: any;
versions: any[];
currentVersion?: any;
@@ -98,8 +99,7 @@ const handleReinstall = async () => {
try {
const versionId = props.versions.find((v) => v.version_number === selectedVersion.value)?.id;
await props.server.general?.reinstall(
props.server.serverId,
await props.server.general.reinstall(
false,
props.project.id,
versionId,
@@ -110,7 +110,7 @@ const handleReinstall = async () => {
emit("reinstall");
hide();
} catch (error) {
if (error instanceof PyroFetchError && error.statusCode === 429) {
if (error instanceof ModrinthServersFetchError && error.statusCode === 429) {
addNotification({
group: "server",
title: "Cannot reinstall server",

View File

@@ -116,11 +116,12 @@
<script setup lang="ts">
import { BackupWarning, ButtonStyled, NewModal } from "@modrinth/ui";
import { UploadIcon, RightArrowIcon, XIcon, ServerIcon } from "@modrinth/assets";
import type { Server } from "~/composables/pyroServers";
import { ModrinthServersFetchError } from "@modrinth/utils";
import type { BackupInProgressReason } from "~/pages/servers/manage/[id].vue";
import { ModrinthServer } from "~/composables/servers/modrinth-servers.ts";
const props = defineProps<{
server: Server<["general", "content", "backups", "network", "startup", "ws", "fs"]>;
server: ModrinthServer;
backupInProgress?: BackupInProgressReason;
}>();
@@ -175,7 +176,7 @@ const handleReinstall = async () => {
window.scrollTo(0, 0);
hide();
} catch (error) {
if (error instanceof PyroFetchError && error.statusCode === 429) {
if (error instanceof ModrinthServersFetchError && error?.statusCode === 429) {
addNotification({
group: "server",
title: "Cannot reinstall server",

View File

@@ -200,9 +200,9 @@
import { BackupWarning, ButtonStyled, NewModal, Toggle } from "@modrinth/ui";
import { DropdownIcon, RightArrowIcon, ServerIcon, XIcon } from "@modrinth/assets";
import { $fetch } from "ofetch";
import type { Server } from "~/composables/pyroServers";
import type { Loaders } from "~/types/servers";
import { type Loaders, ModrinthServersFetchError } from "@modrinth/utils";
import type { BackupInProgressReason } from "~/pages/servers/manage/[id].vue";
import { ModrinthServer } from "~/composables/servers/modrinth-servers.ts";
const { formatMessage } = useVIntl();
@@ -220,7 +220,7 @@ type VersionMap = Record<string, LoaderVersion[]>;
type VersionCache = Record<string, any>;
const props = defineProps<{
server: Server<["general", "content", "backups", "network", "startup", "ws", "fs"]>;
server: ModrinthServer;
currentLoader: Loaders | undefined;
backupInProgress?: BackupInProgressReason;
initialSetup?: boolean;
@@ -458,7 +458,6 @@ const handleReinstall = async () => {
try {
await props.server.general?.reinstall(
props.server.serverId,
true,
selectedLoader.value,
selectedMCVersion.value,
@@ -474,7 +473,7 @@ const handleReinstall = async () => {
hide();
} catch (error) {
if (error instanceof PyroFetchError && error.statusCode === 429) {
if (error instanceof ModrinthServersFetchError && (error as any)?.statusCode === 429) {
addNotification({
group: "server",
title: "Cannot reinstall server",

View File

@@ -31,7 +31,7 @@
<script setup lang="ts">
import { ButtonStyled } from "@modrinth/ui";
import type { Server } from "~/composables/pyroServers";
import { ModrinthServer } from "~/composables/servers/modrinth-servers.ts";
const props = defineProps<{
isUpdating: boolean;
@@ -39,7 +39,7 @@ const props = defineProps<{
save: () => void;
reset: () => void;
isVisible: boolean;
server: Server<["general", "content", "backups", "network", "startup", "ws", "fs"]>;
server: ModrinthServer;
}>();
const saveAndRestart = async () => {

View File

@@ -160,14 +160,14 @@
<script setup lang="ts">
import { ButtonStyled, NewProjectCard } from "@modrinth/ui";
import { TransferIcon, UploadIcon, InfoIcon, CompassIcon, SettingsIcon } from "@modrinth/assets";
import type { Server } from "~/composables/pyroServers";
import type { Loaders } from "~/types/servers";
import type { Loaders } from "@modrinth/utils";
import type { BackupInProgressReason } from "~/pages/servers/manage/[id].vue";
import { ModrinthServer } from "~/composables/servers/modrinth-servers.ts";
const { formatMessage } = useVIntl();
const props = defineProps<{
server: Server<["general", "content", "backups", "network", "startup", "ws", "fs"]>;
server: ModrinthServer;
ignoreCurrentInstallation?: boolean;
backupInProgress?: BackupInProgressReason;
}>();

View File

@@ -81,12 +81,13 @@
<script setup lang="ts">
import { ChevronRightIcon, LockIcon, SparklesIcon } from "@modrinth/assets";
import type { Project, Server } from "~/types/servers";
import type { Project, Server } from "@modrinth/utils";
import { useModrinthServers } from "~/composables/servers/modrinth-servers.ts";
const props = defineProps<Partial<Server>>();
if (props.server_id) {
await usePyroServer(props.server_id, ["general"]);
await useModrinthServers(props.server_id, ["general"]);
}
const showGameLabel = computed(() => !!props.game);
@@ -109,7 +110,7 @@ if (props.upstream) {
const image = useState<string | undefined>(`server-icon-${props.server_id}`, () => undefined);
if (import.meta.server && projectData.value?.icon_url) {
await usePyroServer(props.server_id!, ["general"]);
await useModrinthServers(props.server_id!, ["general"]);
}
const iconUrl = computed(() => projectData.value?.icon_url || undefined);

View File

@@ -34,15 +34,15 @@
<script setup lang="ts">
import { RightArrowIcon } from "@modrinth/assets";
import type { RouteLocationNormalized } from "vue-router";
import type { Server } from "~/composables/pyroServers";
import type { BackupInProgressReason } from "~/pages/servers/manage/[id].vue";
import { ModrinthServer } from "~/composables/servers/modrinth-servers.ts";
const emit = defineEmits(["reinstall"]);
defineProps<{
navLinks: { label: string; href: string; icon: Component; external?: boolean }[];
route: RouteLocationNormalized;
server: Server<["general", "content", "backups", "network", "startup", "ws", "fs"]>;
server: ModrinthServer;
backupInProgress?: BackupInProgressReason;
}>();

View File

@@ -72,7 +72,7 @@
import { ref, computed, shallowRef } from "vue";
import { FolderOpenIcon, CPUIcon, DatabaseIcon, IssuesIcon } from "@modrinth/assets";
import { useStorage } from "@vueuse/core";
import type { Stats } from "~/types/servers";
import type { Stats } from "@modrinth/utils";
const route = useNativeRoute();
const serverId = route.params.id;

View File

@@ -224,7 +224,7 @@
<script setup lang="ts">
import { LoaderIcon } from "@modrinth/assets";
import type { Loaders } from "~/types/servers";
import type { Loaders } from "@modrinth/utils";
defineProps<{
loader: Loaders;

View File

@@ -1,7 +1,7 @@
<script setup lang="ts">
import { ButtonStyled, ServersSpecs } from "@modrinth/ui";
import type { MessageDescriptor } from "@vintl/vintl";
import { formatPrice } from "../../../../../../../packages/utils";
import { formatPrice } from "@modrinth/utils";
const { formatMessage, locale } = useVIntl();

View File

@@ -3,7 +3,7 @@ import { Accordion, ButtonStyled, NewModal, ServerNotice, TagItem } from "@modri
import { PlusIcon, XIcon } from "@modrinth/assets";
import type { ServerNotice as ServerNoticeType } from "@modrinth/utils";
import { ref } from "vue";
import { usePyroFetch } from "~/composables/pyroFetch.ts";
import { useServersFetch } from "~/composables/servers/servers-fetch.ts";
const app = useNuxtApp() as unknown as { $notify: any };
@@ -23,7 +23,7 @@ const assignedNodes = computed(() => assigned.value.filter((n) => n.kind === "no
const inputField = ref("");
async function refresh() {
await usePyroFetch("notices").then((res) => {
await useServersFetch("notices").then((res) => {
const notices = res as ServerNoticeType[];
assigned.value = notices.find((n) => n.id === notice.value?.id)?.assigned ?? [];
});
@@ -33,9 +33,12 @@ async function assign(server: boolean = true) {
const input = inputField.value.trim();
if (input !== "" && notice.value) {
await usePyroFetch(`notices/${notice.value.id}/assign?${server ? "server" : "node"}=${input}`, {
method: "PUT",
}).catch((err) => {
await useServersFetch(
`notices/${notice.value.id}/assign?${server ? "server" : "node"}=${input}`,
{
method: "PUT",
},
).catch((err) => {
app.$notify({
group: "main",
title: "Error assigning notice",
@@ -75,9 +78,12 @@ async function unassignDetect() {
async function unassign(id: string, server: boolean = true) {
if (notice.value) {
await usePyroFetch(`notices/${notice.value.id}/unassign?${server ? "server" : "node"}=${id}`, {
method: "PUT",
}).catch((err) => {
await useServersFetch(
`notices/${notice.value.id}/unassign?${server ? "server" : "node"}=${id}`,
{
method: "PUT",
},
).catch((err) => {
app.$notify({
group: "main",
title: "Error unassigning notice",

View File

@@ -2,13 +2,8 @@
import dayjs from "dayjs";
import { ButtonStyled, commonMessages, CopyCode, ServerNotice, TagItem } from "@modrinth/ui";
import { EditIcon, SettingsIcon, TrashIcon } from "@modrinth/assets";
import { ServerNotice as ServerNoticeType } from "@modrinth/utils";
import { useRelativeTime } from "@modrinth/ui";
import {
DISMISSABLE,
getDismissableMetadata,
NOTICE_LEVELS,
} from "@modrinth/ui/src/utils/notices.ts";
import type { ServerNotice as ServerNoticeType } from "@modrinth/utils";
import { useRelativeTime, getDismissableMetadata, NOTICE_LEVELS } from "@modrinth/ui";
import { useVIntl } from "@vintl/vintl";
const { formatMessage } = useVIntl();