refactor: migrate to common eslint+prettier configs (#4168)

* refactor: migrate to common eslint+prettier configs

* fix: prettier frontend

* feat: config changes

* fix: lint issues

* fix: lint

* fix: type imports

* fix: cyclical import issue

* fix: lockfile

* fix: missing dep

* fix: switch to tabs

* fix: continue switch to tabs

* fix: rustfmt parity

* fix: moderation lint issue

* fix: lint issues

* fix: ui intl

* fix: lint issues

* Revert "fix: rustfmt parity"

This reverts commit cb99d2376c321d813d4b7fc7e2a213bb30a54711.

* feat: revert last rs
This commit is contained in:
Cal H.
2025-08-14 21:48:38 +01:00
committed by GitHub
parent 82697278dc
commit 2aabcf36ee
702 changed files with 101360 additions and 102020 deletions

View File

@@ -1,79 +1,80 @@
import type { Backup, AutoBackupSettings } from "@modrinth/utils";
import { useServersFetch } from "../servers-fetch.ts";
import { ServerModule } from "./base.ts";
import type { AutoBackupSettings, Backup } from '@modrinth/utils'
import { useServersFetch } from '../servers-fetch.ts'
import { ServerModule } from './base.ts'
export class BackupsModule extends ServerModule {
data: Backup[] = [];
data: Backup[] = []
async fetch(): Promise<void> {
this.data = await useServersFetch<Backup[]>(`servers/${this.serverId}/backups`, {}, "backups");
}
async fetch(): Promise<void> {
this.data = await useServersFetch<Backup[]>(`servers/${this.serverId}/backups`, {}, 'backups')
}
async create(backupName: string): Promise<string> {
const response = await useServersFetch<{ id: string }>(`servers/${this.serverId}/backups`, {
method: "POST",
body: { name: backupName },
});
await this.fetch(); // Refresh this module
return response.id;
}
async create(backupName: string): Promise<string> {
const response = await useServersFetch<{ id: string }>(`servers/${this.serverId}/backups`, {
method: 'POST',
body: { name: backupName },
})
await this.fetch() // Refresh this module
return response.id
}
async rename(backupId: string, newName: string): Promise<void> {
await useServersFetch(`servers/${this.serverId}/backups/${backupId}/rename`, {
method: "POST",
body: { name: newName },
});
await this.fetch(); // Refresh this module
}
async rename(backupId: string, newName: string): Promise<void> {
await useServersFetch(`servers/${this.serverId}/backups/${backupId}/rename`, {
method: 'POST',
body: { name: newName },
})
await this.fetch() // Refresh this module
}
async delete(backupId: string): Promise<void> {
await useServersFetch(`servers/${this.serverId}/backups/${backupId}`, {
method: "DELETE",
});
await this.fetch(); // Refresh this module
}
async delete(backupId: string): Promise<void> {
await useServersFetch(`servers/${this.serverId}/backups/${backupId}`, {
method: 'DELETE',
})
await this.fetch() // Refresh this module
}
async restore(backupId: string): Promise<void> {
await useServersFetch(`servers/${this.serverId}/backups/${backupId}/restore`, {
method: "POST",
});
await this.fetch(); // Refresh this module
}
async restore(backupId: string): Promise<void> {
await useServersFetch(`servers/${this.serverId}/backups/${backupId}/restore`, {
method: 'POST',
})
await this.fetch() // Refresh this module
}
async prepare(backupId: string): Promise<void> {
await useServersFetch(`servers/${this.serverId}/backups/${backupId}/prepare-download`, {
method: "POST",
});
}
async prepare(backupId: string): Promise<void> {
await useServersFetch(`servers/${this.serverId}/backups/${backupId}/prepare-download`, {
method: 'POST',
})
}
async lock(backupId: string): Promise<void> {
await useServersFetch(`servers/${this.serverId}/backups/${backupId}/lock`, {
method: "POST",
});
await this.fetch(); // Refresh this module
}
async lock(backupId: string): Promise<void> {
await useServersFetch(`servers/${this.serverId}/backups/${backupId}/lock`, {
method: 'POST',
})
await this.fetch() // Refresh this module
}
async unlock(backupId: string): Promise<void> {
await useServersFetch(`servers/${this.serverId}/backups/${backupId}/unlock`, {
method: "POST",
});
await this.fetch(); // Refresh this module
}
async unlock(backupId: string): Promise<void> {
await useServersFetch(`servers/${this.serverId}/backups/${backupId}/unlock`, {
method: 'POST',
})
await this.fetch() // Refresh this module
}
async retry(backupId: string): Promise<void> {
await useServersFetch(`servers/${this.serverId}/backups/${backupId}/retry`, {
method: "POST",
});
}
async retry(backupId: string): Promise<void> {
await useServersFetch(`servers/${this.serverId}/backups/${backupId}/retry`, {
method: 'POST',
})
}
async updateAutoBackup(autoBackup: "enable" | "disable", interval: number): Promise<void> {
await useServersFetch(`servers/${this.serverId}/autobackup`, {
method: "POST",
body: { set: autoBackup, interval },
});
}
async updateAutoBackup(autoBackup: 'enable' | 'disable', interval: number): Promise<void> {
await useServersFetch(`servers/${this.serverId}/autobackup`, {
method: 'POST',
body: { set: autoBackup, interval },
})
}
async getAutoBackup(): Promise<AutoBackupSettings> {
return await useServersFetch(`servers/${this.serverId}/autobackup`);
}
async getAutoBackup(): Promise<AutoBackupSettings> {
return await useServersFetch(`servers/${this.serverId}/autobackup`)
}
}

View File

@@ -1,15 +1,15 @@
import type { ModrinthServer } from "../modrinth-servers.ts";
import type { ModrinthServer } from '../modrinth-servers.ts'
export abstract class ServerModule {
protected server: ModrinthServer;
protected server: ModrinthServer
constructor(server: ModrinthServer) {
this.server = server;
}
constructor(server: ModrinthServer) {
this.server = server
}
protected get serverId(): string {
return this.server.serverId;
}
protected get serverId(): string {
return this.server.serverId
}
abstract fetch(): Promise<void>;
abstract fetch(): Promise<void>
}

View File

@@ -1,36 +1,37 @@
import type { Mod, ContentType } from "@modrinth/utils";
import { useServersFetch } from "../servers-fetch.ts";
import { ServerModule } from "./base.ts";
import type { ContentType, Mod } from '@modrinth/utils'
import { useServersFetch } from '../servers-fetch.ts'
import { ServerModule } from './base.ts'
export class ContentModule extends ServerModule {
data: Mod[] = [];
data: Mod[] = []
async fetch(): Promise<void> {
const mods = await useServersFetch<Mod[]>(`servers/${this.serverId}/mods`, {}, "content");
this.data = mods.sort((a, b) => (a?.name ?? "").localeCompare(b?.name ?? ""));
}
async fetch(): Promise<void> {
const mods = await useServersFetch<Mod[]>(`servers/${this.serverId}/mods`, {}, 'content')
this.data = mods.sort((a, b) => (a?.name ?? '').localeCompare(b?.name ?? ''))
}
async install(contentType: ContentType, projectId: string, versionId: string): Promise<void> {
await useServersFetch(`servers/${this.serverId}/mods`, {
method: "POST",
body: {
rinth_ids: { project_id: projectId, version_id: versionId },
install_as: contentType,
},
});
}
async install(contentType: ContentType, projectId: string, versionId: string): Promise<void> {
await useServersFetch(`servers/${this.serverId}/mods`, {
method: 'POST',
body: {
rinth_ids: { project_id: projectId, version_id: versionId },
install_as: contentType,
},
})
}
async remove(path: string): Promise<void> {
await useServersFetch(`servers/${this.serverId}/deleteMod`, {
method: "POST",
body: { path },
});
}
async remove(path: string): Promise<void> {
await useServersFetch(`servers/${this.serverId}/deleteMod`, {
method: 'POST',
body: { path },
})
}
async reinstall(replace: string, projectId: string, versionId: string): Promise<void> {
await useServersFetch(`servers/${this.serverId}/mods/update`, {
method: "POST",
body: { replace, project_id: projectId, version_id: versionId },
});
}
async reinstall(replace: string, projectId: string, versionId: string): Promise<void> {
await useServersFetch(`servers/${this.serverId}/mods/update`, {
method: 'POST',
body: { replace, project_id: projectId, version_id: versionId },
})
}
}

View File

@@ -1,247 +1,248 @@
import type {
FileUploadQuery,
JWTAuth,
DirectoryResponse,
FilesystemOp,
FSQueuedOp,
} from "@modrinth/utils";
import { ModrinthServerError } from "@modrinth/utils";
import { useServersFetch } from "../servers-fetch.ts";
import { ServerModule } from "./base.ts";
DirectoryResponse,
FilesystemOp,
FileUploadQuery,
FSQueuedOp,
JWTAuth,
} from '@modrinth/utils'
import { ModrinthServerError } from '@modrinth/utils'
import { useServersFetch } from '../servers-fetch.ts'
import { ServerModule } from './base.ts'
export class FSModule extends ServerModule {
auth!: JWTAuth;
ops: FilesystemOp[] = [];
queuedOps: FSQueuedOp[] = [];
opsQueuedForModification: string[] = [];
auth!: JWTAuth
ops: FilesystemOp[] = []
queuedOps: FSQueuedOp[] = []
opsQueuedForModification: string[] = []
async fetch(): Promise<void> {
this.auth = await useServersFetch<JWTAuth>(`servers/${this.serverId}/fs`, {}, "fs");
this.ops = [];
this.queuedOps = [];
this.opsQueuedForModification = [];
}
async fetch(): Promise<void> {
this.auth = await useServersFetch<JWTAuth>(`servers/${this.serverId}/fs`, {}, 'fs')
this.ops = []
this.queuedOps = []
this.opsQueuedForModification = []
}
private async retryWithAuth<T>(
requestFn: () => Promise<T>,
ignoreFailure: boolean = false,
): Promise<T> {
try {
return await requestFn();
} catch (error) {
if (error instanceof ModrinthServerError && error.statusCode === 401) {
console.debug("Auth failed, refreshing JWT and retrying");
await this.fetch(); // Refresh auth
return await requestFn();
}
private async retryWithAuth<T>(
requestFn: () => Promise<T>,
ignoreFailure: boolean = false,
): Promise<T> {
try {
return await requestFn()
} catch (error) {
if (error instanceof ModrinthServerError && error.statusCode === 401) {
console.debug('Auth failed, refreshing JWT and retrying')
await this.fetch() // Refresh auth
return await requestFn()
}
const available = await this.server.testNodeReachability();
if (!available && !ignoreFailure) {
this.server.moduleErrors.general = {
error: new ModrinthServerError(
"Unable to reach node. FS operation failed and subsequent ping test failed.",
500,
error as Error,
"fs",
),
timestamp: Date.now(),
};
}
const available = await this.server.testNodeReachability()
if (!available && !ignoreFailure) {
this.server.moduleErrors.general = {
error: new ModrinthServerError(
'Unable to reach node. FS operation failed and subsequent ping test failed.',
500,
error as Error,
'fs',
),
timestamp: Date.now(),
}
}
throw error;
}
}
throw error
}
}
listDirContents(
path: string,
page: number,
pageSize: number,
ignoreFailure: boolean = false,
): Promise<DirectoryResponse> {
return this.retryWithAuth(async () => {
const encodedPath = encodeURIComponent(path);
return await useServersFetch(`/list?path=${encodedPath}&page=${page}&page_size=${pageSize}`, {
override: this.auth,
retry: false,
});
}, ignoreFailure);
}
listDirContents(
path: string,
page: number,
pageSize: number,
ignoreFailure: boolean = false,
): Promise<DirectoryResponse> {
return this.retryWithAuth(async () => {
const encodedPath = encodeURIComponent(path)
return await useServersFetch(`/list?path=${encodedPath}&page=${page}&page_size=${pageSize}`, {
override: this.auth,
retry: false,
})
}, ignoreFailure)
}
createFileOrFolder(path: string, type: "file" | "directory"): Promise<void> {
return this.retryWithAuth(async () => {
const encodedPath = encodeURIComponent(path);
await useServersFetch(`/create?path=${encodedPath}&type=${type}`, {
method: "POST",
contentType: "application/octet-stream",
override: this.auth,
});
});
}
createFileOrFolder(path: string, type: 'file' | 'directory'): Promise<void> {
return this.retryWithAuth(async () => {
const encodedPath = encodeURIComponent(path)
await useServersFetch(`/create?path=${encodedPath}&type=${type}`, {
method: 'POST',
contentType: 'application/octet-stream',
override: this.auth,
})
})
}
uploadFile(path: string, file: File): FileUploadQuery {
const encodedPath = encodeURIComponent(path);
const progressSubject = new EventTarget();
const abortController = new AbortController();
uploadFile(path: string, file: File): FileUploadQuery {
const encodedPath = encodeURIComponent(path)
const progressSubject = new EventTarget()
const abortController = new AbortController()
const uploadPromise = new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
const uploadPromise = new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest()
xhr.upload.addEventListener("progress", (e) => {
if (e.lengthComputable) {
const progress = (e.loaded / e.total) * 100;
progressSubject.dispatchEvent(
new CustomEvent("progress", {
detail: { loaded: e.loaded, total: e.total, progress },
}),
);
}
});
xhr.upload.addEventListener('progress', (e) => {
if (e.lengthComputable) {
const progress = (e.loaded / e.total) * 100
progressSubject.dispatchEvent(
new CustomEvent('progress', {
detail: { loaded: e.loaded, total: e.total, progress },
}),
)
}
})
xhr.onload = () => {
if (xhr.status >= 200 && xhr.status < 300) {
resolve(xhr.response);
} else {
reject(new Error(`Upload failed with status ${xhr.status}`));
}
};
xhr.onload = () => {
if (xhr.status >= 200 && xhr.status < 300) {
resolve(xhr.response)
} else {
reject(new Error(`Upload failed with status ${xhr.status}`))
}
}
xhr.onerror = () => reject(new Error("Upload failed"));
xhr.onabort = () => reject(new Error("Upload cancelled"));
xhr.onerror = () => reject(new Error('Upload failed'))
xhr.onabort = () => reject(new Error('Upload cancelled'))
xhr.open("POST", `https://${this.auth.url}/create?path=${encodedPath}&type=file`);
xhr.setRequestHeader("Authorization", `Bearer ${this.auth.token}`);
xhr.setRequestHeader("Content-Type", "application/octet-stream");
xhr.send(file);
xhr.open('POST', `https://${this.auth.url}/create?path=${encodedPath}&type=file`)
xhr.setRequestHeader('Authorization', `Bearer ${this.auth.token}`)
xhr.setRequestHeader('Content-Type', 'application/octet-stream')
xhr.send(file)
abortController.signal.addEventListener("abort", () => xhr.abort());
});
abortController.signal.addEventListener('abort', () => xhr.abort())
})
return {
promise: uploadPromise,
onProgress: (
callback: (progress: { loaded: number; total: number; progress: number }) => void,
) => {
progressSubject.addEventListener("progress", ((e: CustomEvent) => {
callback(e.detail);
}) as EventListener);
},
cancel: () => abortController.abort(),
} as FileUploadQuery;
}
return {
promise: uploadPromise,
onProgress: (
callback: (progress: { loaded: number; total: number; progress: number }) => void,
) => {
progressSubject.addEventListener('progress', ((e: CustomEvent) => {
callback(e.detail)
}) as EventListener)
},
cancel: () => abortController.abort(),
} as FileUploadQuery
}
renameFileOrFolder(path: string, name: string): Promise<void> {
const pathName = path.split("/").slice(0, -1).join("/") + "/" + name;
return this.retryWithAuth(async () => {
await useServersFetch(`/move`, {
method: "POST",
override: this.auth,
body: { source: path, destination: pathName },
});
});
}
renameFileOrFolder(path: string, name: string): Promise<void> {
const pathName = path.split('/').slice(0, -1).join('/') + '/' + name
return this.retryWithAuth(async () => {
await useServersFetch(`/move`, {
method: 'POST',
override: this.auth,
body: { source: path, destination: pathName },
})
})
}
updateFile(path: string, content: string): Promise<void> {
const octetStream = new Blob([content], { type: "application/octet-stream" });
return this.retryWithAuth(async () => {
await useServersFetch(`/update?path=${path}`, {
method: "PUT",
contentType: "application/octet-stream",
body: octetStream,
override: this.auth,
});
});
}
updateFile(path: string, content: string): Promise<void> {
const octetStream = new Blob([content], { type: 'application/octet-stream' })
return this.retryWithAuth(async () => {
await useServersFetch(`/update?path=${path}`, {
method: 'PUT',
contentType: 'application/octet-stream',
body: octetStream,
override: this.auth,
})
})
}
moveFileOrFolder(path: string, newPath: string): Promise<void> {
return this.retryWithAuth(async () => {
await this.server.createMissingFolders(newPath.substring(0, newPath.lastIndexOf("/")));
await useServersFetch(`/move`, {
method: "POST",
override: this.auth,
body: { source: path, destination: newPath },
});
});
}
moveFileOrFolder(path: string, newPath: string): Promise<void> {
return this.retryWithAuth(async () => {
await this.server.createMissingFolders(newPath.substring(0, newPath.lastIndexOf('/')))
await useServersFetch(`/move`, {
method: 'POST',
override: this.auth,
body: { source: path, destination: newPath },
})
})
}
deleteFileOrFolder(path: string, recursive: boolean): Promise<void> {
const encodedPath = encodeURIComponent(path);
return this.retryWithAuth(async () => {
await useServersFetch(`/delete?path=${encodedPath}&recursive=${recursive}`, {
method: "DELETE",
override: this.auth,
});
});
}
deleteFileOrFolder(path: string, recursive: boolean): Promise<void> {
const encodedPath = encodeURIComponent(path)
return this.retryWithAuth(async () => {
await useServersFetch(`/delete?path=${encodedPath}&recursive=${recursive}`, {
method: 'DELETE',
override: this.auth,
})
})
}
downloadFile(path: string, raw: boolean = false, ignoreFailure: boolean = false): Promise<any> {
return this.retryWithAuth(async () => {
const encodedPath = encodeURIComponent(path);
const fileData = await useServersFetch(`/download?path=${encodedPath}`, {
override: this.auth,
});
downloadFile(path: string, raw: boolean = false, ignoreFailure: boolean = false): Promise<any> {
return this.retryWithAuth(async () => {
const encodedPath = encodeURIComponent(path)
const fileData = await useServersFetch(`/download?path=${encodedPath}`, {
override: this.auth,
})
if (fileData instanceof Blob) {
return raw ? fileData : await fileData.text();
}
return fileData;
}, ignoreFailure);
}
if (fileData instanceof Blob) {
return raw ? fileData : await fileData.text()
}
return fileData
}, ignoreFailure)
}
extractFile(
path: string,
override = true,
dry = false,
silentQueue = false,
): Promise<{ modpack_name: string | null; conflicting_files: string[] }> {
return this.retryWithAuth(async () => {
const encodedPath = encodeURIComponent(path);
extractFile(
path: string,
override = true,
dry = false,
silentQueue = false,
): Promise<{ modpack_name: string | null; conflicting_files: string[] }> {
return this.retryWithAuth(async () => {
const encodedPath = encodeURIComponent(path)
if (!silentQueue) {
this.queuedOps.push({ op: "unarchive", src: path });
setTimeout(() => this.removeQueuedOp("unarchive", path), 4000);
}
if (!silentQueue) {
this.queuedOps.push({ op: 'unarchive', src: path })
setTimeout(() => this.removeQueuedOp('unarchive', path), 4000)
}
try {
return await useServersFetch(
`/unarchive?src=${encodedPath}&trg=/&override=${override}&dry=${dry}`,
{
method: "POST",
override: this.auth,
version: 1,
},
undefined,
"Error extracting file",
);
} catch (err) {
this.removeQueuedOp("unarchive", path);
throw err;
}
});
}
try {
return await useServersFetch(
`/unarchive?src=${encodedPath}&trg=/&override=${override}&dry=${dry}`,
{
method: 'POST',
override: this.auth,
version: 1,
},
undefined,
'Error extracting file',
)
} catch (err) {
this.removeQueuedOp('unarchive', path)
throw err
}
})
}
modifyOp(id: string, action: "dismiss" | "cancel"): Promise<void> {
return this.retryWithAuth(async () => {
await useServersFetch(
`/ops/${action}?id=${id}`,
{
method: "POST",
override: this.auth,
version: 1,
},
undefined,
`Error ${action === "dismiss" ? "dismissing" : "cancelling"} filesystem operation`,
);
modifyOp(id: string, action: 'dismiss' | 'cancel'): Promise<void> {
return this.retryWithAuth(async () => {
await useServersFetch(
`/ops/${action}?id=${id}`,
{
method: 'POST',
override: this.auth,
version: 1,
},
undefined,
`Error ${action === 'dismiss' ? 'dismissing' : 'cancelling'} filesystem operation`,
)
this.opsQueuedForModification = this.opsQueuedForModification.filter((x: string) => x !== id);
this.ops = this.ops.filter((x: FilesystemOp) => x.id !== id);
});
}
this.opsQueuedForModification = this.opsQueuedForModification.filter((x: string) => x !== id)
this.ops = this.ops.filter((x: FilesystemOp) => x.id !== id)
})
}
removeQueuedOp(op: FSQueuedOp["op"], src: string): void {
this.queuedOps = this.queuedOps.filter((x: FSQueuedOp) => x.op !== op || x.src !== src);
}
removeQueuedOp(op: FSQueuedOp['op'], src: string): void {
this.queuedOps = this.queuedOps.filter((x: FSQueuedOp) => x.op !== op || x.src !== src)
}
clearQueuedOps(): void {
this.queuedOps = [];
}
clearQueuedOps(): void {
this.queuedOps = []
}
}

View File

@@ -1,224 +1,229 @@
import { $fetch } from "ofetch";
import type { ServerGeneral, Project, PowerAction, JWTAuth } from "@modrinth/utils";
import { useServersFetch } from "../servers-fetch.ts";
import { ServerModule } from "./base.ts";
import type { JWTAuth, PowerAction, Project, ServerGeneral } from '@modrinth/utils'
import { $fetch } from 'ofetch'
import { useServersFetch } from '../servers-fetch.ts'
import { ServerModule } from './base.ts'
export class GeneralModule extends ServerModule implements ServerGeneral {
server_id!: string;
name!: string;
owner_id!: string;
net!: { ip: string; port: number; domain: string };
game!: string;
backup_quota!: number;
used_backup_quota!: number;
status!: string;
suspension_reason!: string;
loader!: string;
loader_version!: string;
mc_version!: string;
upstream!: {
kind: "modpack" | "mod" | "resourcepack";
version_id: string;
project_id: string;
} | null;
server_id!: string
name!: string
owner_id!: string
net!: { ip: string; port: number; domain: string }
game!: string
backup_quota!: number
used_backup_quota!: number
status!: string
suspension_reason!: string
loader!: string
loader_version!: string
mc_version!: string
upstream!: {
kind: 'modpack' | 'mod' | 'resourcepack'
version_id: string
project_id: string
} | null
motd?: string;
image?: string;
project?: Project;
sftp_username!: string;
sftp_password!: string;
sftp_host!: string;
datacenter?: string;
notices?: any[];
node!: { token: string; instance: string };
flows?: { intro?: boolean };
motd?: string
image?: string
project?: Project
sftp_username!: string
sftp_password!: string
sftp_host!: string
datacenter?: string
notices?: any[]
node!: { token: string; instance: string }
flows?: { intro?: boolean }
async fetch(): Promise<void> {
const data = await useServersFetch<ServerGeneral>(`servers/${this.serverId}`, {}, "general");
async fetch(): Promise<void> {
const data = await useServersFetch<ServerGeneral>(`servers/${this.serverId}`, {}, 'general')
if (data.upstream?.project_id) {
const project = await $fetch(
`https://api.modrinth.com/v2/project/${data.upstream.project_id}`,
);
data.project = project as Project;
}
if (data.upstream?.project_id) {
const project = await $fetch(
`https://api.modrinth.com/v2/project/${data.upstream.project_id}`,
)
data.project = project as Project
}
if (import.meta.client) {
data.image = (await this.server.processImage(data.project?.icon_url)) ?? undefined;
}
if (import.meta.client) {
data.image = (await this.server.processImage(data.project?.icon_url)) ?? undefined
}
try {
const motd = await this.getMotd();
if (motd === "A Minecraft Server") {
await this.setMotd(
`§b${data.project?.title || data.loader + " " + data.mc_version} §f♦ §aModrinth Servers`,
);
}
data.motd = motd;
} catch {
console.error("[Modrinth Servers] [General] Failed to fetch MOTD.");
data.motd = undefined;
}
try {
const motd = await this.getMotd()
if (motd === 'A Minecraft Server') {
await this.setMotd(
`§b${data.project?.title || data.loader + ' ' + data.mc_version} §f♦ §aModrinth Servers`,
)
}
data.motd = motd
} catch {
console.error('[Modrinth Servers] [General] Failed to fetch MOTD.')
data.motd = undefined
}
// Copy data to this module
Object.assign(this, data);
}
// Copy data to this module
Object.assign(this, data)
}
async updateName(newName: string): Promise<void> {
await useServersFetch(`servers/${this.serverId}/name`, {
method: "POST",
body: { name: newName },
});
}
async updateName(newName: string): Promise<void> {
await useServersFetch(`servers/${this.serverId}/name`, {
method: 'POST',
body: { name: newName },
})
}
async power(action: PowerAction): Promise<void> {
await useServersFetch(`servers/${this.serverId}/power`, {
method: "POST",
body: { action },
});
await new Promise((resolve) => setTimeout(resolve, 1000));
await this.fetch(); // Refresh this module
}
async power(action: PowerAction): Promise<void> {
await useServersFetch(`servers/${this.serverId}/power`, {
method: 'POST',
body: { action },
})
await new Promise((resolve) => setTimeout(resolve, 1000))
await this.fetch() // Refresh this module
}
async reinstall(
loader: boolean,
projectId: string,
versionId?: string,
loaderVersionId?: string,
hardReset: boolean = false,
): Promise<void> {
const hardResetParam = hardReset ? "true" : "false";
if (loader) {
if (projectId.toLowerCase() === "neoforge") {
projectId = "NeoForge";
}
await useServersFetch(`servers/${this.serverId}/reinstall?hard=${hardResetParam}`, {
method: "POST",
body: { loader: projectId, loader_version: loaderVersionId, game_version: versionId },
});
} else {
await useServersFetch(`servers/${this.serverId}/reinstall?hard=${hardResetParam}`, {
method: "POST",
body: { project_id: projectId, version_id: versionId },
});
}
}
async reinstall(
loader: boolean,
projectId: string,
versionId?: string,
loaderVersionId?: string,
hardReset: boolean = false,
): Promise<void> {
const hardResetParam = hardReset ? 'true' : 'false'
if (loader) {
if (projectId.toLowerCase() === 'neoforge') {
projectId = 'NeoForge'
}
await useServersFetch(`servers/${this.serverId}/reinstall?hard=${hardResetParam}`, {
method: 'POST',
body: {
loader: projectId,
loader_version: loaderVersionId,
game_version: versionId,
},
})
} else {
await useServersFetch(`servers/${this.serverId}/reinstall?hard=${hardResetParam}`, {
method: 'POST',
body: { project_id: projectId, version_id: versionId },
})
}
}
reinstallFromMrpack(
mrpack: File,
hardReset: boolean = false,
): {
promise: Promise<void>;
onProgress: (cb: (p: { loaded: number; total: number; progress: number }) => void) => void;
} {
const hardResetParam = hardReset ? "true" : "false";
reinstallFromMrpack(
mrpack: File,
hardReset: boolean = false,
): {
promise: Promise<void>
onProgress: (cb: (p: { loaded: number; total: number; progress: number }) => void) => void
} {
const hardResetParam = hardReset ? 'true' : 'false'
const progressSubject = new EventTarget();
const progressSubject = new EventTarget()
const uploadPromise = (async () => {
try {
const auth = await useServersFetch<JWTAuth>(`servers/${this.serverId}/reinstallFromMrpack`);
const uploadPromise = (async () => {
try {
const auth = await useServersFetch<JWTAuth>(`servers/${this.serverId}/reinstallFromMrpack`)
await new Promise<void>((resolve, reject) => {
const xhr = new XMLHttpRequest();
await new Promise<void>((resolve, reject) => {
const xhr = new XMLHttpRequest()
xhr.upload.addEventListener("progress", (e) => {
if (e.lengthComputable) {
progressSubject.dispatchEvent(
new CustomEvent("progress", {
detail: {
loaded: e.loaded,
total: e.total,
progress: (e.loaded / e.total) * 100,
},
}),
);
}
});
xhr.upload.addEventListener('progress', (e) => {
if (e.lengthComputable) {
progressSubject.dispatchEvent(
new CustomEvent('progress', {
detail: {
loaded: e.loaded,
total: e.total,
progress: (e.loaded / e.total) * 100,
},
}),
)
}
})
xhr.onload = () =>
xhr.status >= 200 && xhr.status < 300
? resolve()
: reject(new Error(`[pyroservers] XHR error status: ${xhr.status}`));
xhr.onload = () =>
xhr.status >= 200 && xhr.status < 300
? resolve()
: reject(new Error(`[pyroservers] XHR error status: ${xhr.status}`))
xhr.onerror = () => reject(new Error("[pyroservers] .mrpack upload failed"));
xhr.onabort = () => reject(new Error("[pyroservers] .mrpack upload cancelled"));
xhr.ontimeout = () => reject(new Error("[pyroservers] .mrpack upload timed out"));
xhr.timeout = 30 * 60 * 1000;
xhr.onerror = () => reject(new Error('[pyroservers] .mrpack upload failed'))
xhr.onabort = () => reject(new Error('[pyroservers] .mrpack upload cancelled'))
xhr.ontimeout = () => reject(new Error('[pyroservers] .mrpack upload timed out'))
xhr.timeout = 30 * 60 * 1000
xhr.open("POST", `https://${auth.url}/reinstallMrpackMultiparted?hard=${hardResetParam}`);
xhr.setRequestHeader("Authorization", `Bearer ${auth.token}`);
xhr.open('POST', `https://${auth.url}/reinstallMrpackMultiparted?hard=${hardResetParam}`)
xhr.setRequestHeader('Authorization', `Bearer ${auth.token}`)
const formData = new FormData();
formData.append("file", mrpack);
xhr.send(formData);
});
} catch (err) {
console.error("Error reinstalling from mrpack:", err);
throw err;
}
})();
const formData = new FormData()
formData.append('file', mrpack)
xhr.send(formData)
})
} catch (err) {
console.error('Error reinstalling from mrpack:', err)
throw err
}
})()
return {
promise: uploadPromise,
onProgress: (cb: (p: { loaded: number; total: number; progress: number }) => void) =>
progressSubject.addEventListener("progress", ((e: CustomEvent) =>
cb(e.detail)) as EventListener),
};
}
return {
promise: uploadPromise,
onProgress: (cb: (p: { loaded: number; total: number; progress: number }) => void) =>
progressSubject.addEventListener('progress', ((e: CustomEvent) =>
cb(e.detail)) as EventListener),
}
}
async suspend(status: boolean): Promise<void> {
await useServersFetch(`servers/${this.serverId}/suspend`, {
method: "POST",
body: { suspended: status },
});
}
async suspend(status: boolean): Promise<void> {
await useServersFetch(`servers/${this.serverId}/suspend`, {
method: 'POST',
body: { suspended: status },
})
}
async endIntro(): Promise<void> {
await useServersFetch(`servers/${this.serverId}/flows/intro`, {
method: "DELETE",
version: 1,
});
await this.fetch(); // Refresh this module
}
async endIntro(): Promise<void> {
await useServersFetch(`servers/${this.serverId}/flows/intro`, {
method: 'DELETE',
version: 1,
})
await this.fetch() // Refresh this module
}
async getMotd(): Promise<string | undefined> {
try {
const props = await this.server.fs.downloadFile("/server.properties", false, true);
if (props) {
const lines = props.split("\n");
for (const line of lines) {
if (line.startsWith("motd=")) {
return line.slice(5);
}
}
}
} catch {
return undefined;
}
return undefined;
}
async getMotd(): Promise<string | undefined> {
try {
const props = await this.server.fs.downloadFile('/server.properties', false, true)
if (props) {
const lines = props.split('\n')
for (const line of lines) {
if (line.startsWith('motd=')) {
return line.slice(5)
}
}
}
} catch {
return undefined
}
return undefined
}
async setMotd(motd: string): Promise<void> {
try {
const props = (await this.server.fetchConfigFile("ServerProperties")) as any;
if (props) {
props.motd = motd;
const newProps = this.server.constructServerProperties(props);
const octetStream = new Blob([newProps], { type: "application/octet-stream" });
const auth = await useServersFetch<JWTAuth>(`servers/${this.serverId}/fs`);
async setMotd(motd: string): Promise<void> {
try {
const props = (await this.server.fetchConfigFile('ServerProperties')) as any
if (props) {
props.motd = motd
const newProps = this.server.constructServerProperties(props)
const octetStream = new Blob([newProps], { type: 'application/octet-stream' })
const auth = await useServersFetch<JWTAuth>(`servers/${this.serverId}/fs`)
await useServersFetch(`/update?path=/server.properties`, {
method: "PUT",
contentType: "application/octet-stream",
body: octetStream,
override: auth,
});
}
} catch {
console.error(
"[Modrinth Servers] [General] Failed to set MOTD due to lack of server properties file.",
);
}
}
await useServersFetch(`/update?path=/server.properties`, {
method: 'PUT',
contentType: 'application/octet-stream',
body: octetStream,
override: auth,
})
}
} catch {
console.error(
'[Modrinth Servers] [General] Failed to set MOTD due to lack of server properties file.',
)
}
}
}

View File

@@ -1,8 +1,8 @@
export * from "./base.ts";
export * from "./backups.ts";
export * from "./content.ts";
export * from "./fs.ts";
export * from "./general.ts";
export * from "./network.ts";
export * from "./startup.ts";
export * from "./ws.ts";
export * from './backups.ts'
export * from './base.ts'
export * from './content.ts'
export * from './fs.ts'
export * from './general.ts'
export * from './network.ts'
export * from './startup.ts'
export * from './ws.ts'

View File

@@ -1,47 +1,48 @@
import type { Allocation } from "@modrinth/utils";
import { useServersFetch } from "../servers-fetch.ts";
import { ServerModule } from "./base.ts";
import type { Allocation } from '@modrinth/utils'
import { useServersFetch } from '../servers-fetch.ts'
import { ServerModule } from './base.ts'
export class NetworkModule extends ServerModule {
allocations: Allocation[] = [];
allocations: Allocation[] = []
async fetch(): Promise<void> {
this.allocations = await useServersFetch<Allocation[]>(
`servers/${this.serverId}/allocations`,
{},
"network",
);
}
async fetch(): Promise<void> {
this.allocations = await useServersFetch<Allocation[]>(
`servers/${this.serverId}/allocations`,
{},
'network',
)
}
async reserveAllocation(name: string): Promise<Allocation> {
return await useServersFetch<Allocation>(`servers/${this.serverId}/allocations?name=${name}`, {
method: "POST",
});
}
async reserveAllocation(name: string): Promise<Allocation> {
return await useServersFetch<Allocation>(`servers/${this.serverId}/allocations?name=${name}`, {
method: 'POST',
})
}
async updateAllocation(port: number, name: string): Promise<void> {
await useServersFetch(`servers/${this.serverId}/allocations/${port}?name=${name}`, {
method: "PUT",
});
}
async updateAllocation(port: number, name: string): Promise<void> {
await useServersFetch(`servers/${this.serverId}/allocations/${port}?name=${name}`, {
method: 'PUT',
})
}
async deleteAllocation(port: number): Promise<void> {
await useServersFetch(`servers/${this.serverId}/allocations/${port}`, {
method: "DELETE",
});
}
async deleteAllocation(port: number): Promise<void> {
await useServersFetch(`servers/${this.serverId}/allocations/${port}`, {
method: 'DELETE',
})
}
async checkSubdomainAvailability(subdomain: string): Promise<boolean> {
const result = (await useServersFetch(`subdomains/${subdomain}/isavailable`)) as {
available: boolean;
};
return result.available;
}
async checkSubdomainAvailability(subdomain: string): Promise<boolean> {
const result = (await useServersFetch(`subdomains/${subdomain}/isavailable`)) as {
available: boolean
}
return result.available
}
async changeSubdomain(subdomain: string): Promise<void> {
await useServersFetch(`servers/${this.serverId}/subdomain`, {
method: "POST",
body: { subdomain },
});
}
async changeSubdomain(subdomain: string): Promise<void> {
await useServersFetch(`servers/${this.serverId}/subdomain`, {
method: 'POST',
body: { subdomain },
})
}
}

View File

@@ -1,26 +1,27 @@
import type { Startup, JDKVersion, JDKBuild } from "@modrinth/utils";
import { useServersFetch } from "../servers-fetch.ts";
import { ServerModule } from "./base.ts";
import type { JDKBuild, JDKVersion, Startup } from '@modrinth/utils'
import { useServersFetch } from '../servers-fetch.ts'
import { ServerModule } from './base.ts'
export class StartupModule extends ServerModule implements Startup {
invocation!: string;
original_invocation!: string;
jdk_version!: JDKVersion;
jdk_build!: JDKBuild;
invocation!: string
original_invocation!: string
jdk_version!: JDKVersion
jdk_build!: JDKBuild
async fetch(): Promise<void> {
const data = await useServersFetch<Startup>(`servers/${this.serverId}/startup`, {}, "startup");
Object.assign(this, data);
}
async fetch(): Promise<void> {
const data = await useServersFetch<Startup>(`servers/${this.serverId}/startup`, {}, 'startup')
Object.assign(this, data)
}
async update(invocation: string, jdkVersion: JDKVersion, jdkBuild: JDKBuild): Promise<void> {
await useServersFetch(`servers/${this.serverId}/startup`, {
method: "POST",
body: {
invocation: invocation || null,
jdk_version: jdkVersion || null,
jdk_build: jdkBuild || null,
},
});
}
async update(invocation: string, jdkVersion: JDKVersion, jdkBuild: JDKBuild): Promise<void> {
await useServersFetch(`servers/${this.serverId}/startup`, {
method: 'POST',
body: {
invocation: invocation || null,
jdk_version: jdkVersion || null,
jdk_build: jdkBuild || null,
},
})
}
}

View File

@@ -1,13 +1,14 @@
import type { JWTAuth } from "@modrinth/utils";
import { useServersFetch } from "../servers-fetch.ts";
import { ServerModule } from "./base.ts";
import type { JWTAuth } from '@modrinth/utils'
import { useServersFetch } from '../servers-fetch.ts'
import { ServerModule } from './base.ts'
export class WSModule extends ServerModule implements JWTAuth {
url!: string;
token!: string;
url!: string
token!: string
async fetch(): Promise<void> {
const data = await useServersFetch<JWTAuth>(`servers/${this.serverId}/ws`, {}, "ws");
Object.assign(this, data);
}
async fetch(): Promise<void> {
const data = await useServersFetch<JWTAuth>(`servers/${this.serverId}/ws`, {}, 'ws')
Object.assign(this, data)
}
}