You've already forked AstralRinth
forked from didirus/AstralRinth
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:
@@ -46,7 +46,7 @@ export const NOTICE_LEVELS: Record<
|
||||
},
|
||||
}
|
||||
|
||||
const DISMISSABLE = {
|
||||
export const DISMISSABLE = {
|
||||
name: defineMessage({
|
||||
id: 'servers.notice.dismissable',
|
||||
defaultMessage: 'Dismissable',
|
||||
@@ -57,7 +57,7 @@ const DISMISSABLE = {
|
||||
},
|
||||
}
|
||||
|
||||
const UNDISMISSABLE = {
|
||||
export const UNDISMISSABLE = {
|
||||
name: defineMessage({
|
||||
id: 'servers.notice.undismissable',
|
||||
defaultMessage: 'Undismissable',
|
||||
|
||||
@@ -7,3 +7,4 @@ export * from './projects'
|
||||
export * from './types'
|
||||
export * from './users'
|
||||
export * from './utils'
|
||||
export * from './servers'
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
"dayjs": "^1.11.10",
|
||||
"highlight.js": "^11.9.0",
|
||||
"markdown-it": "^14.1.0",
|
||||
"ofetch": "^1.3.4",
|
||||
"xss": "^1.0.14"
|
||||
}
|
||||
}
|
||||
|
||||
3
packages/utils/servers/errors/index.ts
Normal file
3
packages/utils/servers/errors/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export * from './modrinth-servers-multi-error'
|
||||
export * from './modrinth-servers-fetch-error'
|
||||
export * from './modrinth-server-error'
|
||||
59
packages/utils/servers/errors/modrinth-server-error.ts
Normal file
59
packages/utils/servers/errors/modrinth-server-error.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
import { FetchError } from 'ofetch'
|
||||
import { V1ErrorInfo } from '../types'
|
||||
|
||||
export class ModrinthServerError extends Error {
|
||||
constructor(
|
||||
message: string,
|
||||
public readonly statusCode?: number,
|
||||
public readonly originalError?: Error,
|
||||
public readonly module?: string,
|
||||
public readonly v1Error?: V1ErrorInfo,
|
||||
) {
|
||||
let errorMessage = message
|
||||
let method = 'GET'
|
||||
let path = ''
|
||||
|
||||
if (originalError instanceof FetchError) {
|
||||
const matches = message.match(/\[([A-Z]+)\]\s+"([^"]+)":/)
|
||||
if (matches) {
|
||||
method = matches[1]
|
||||
path = matches[2].replace(/https?:\/\/[^/]+\/[^/]+\/v\d+\//, '')
|
||||
}
|
||||
|
||||
const statusMessage = (() => {
|
||||
if (!statusCode) return 'Unknown Error'
|
||||
switch (statusCode) {
|
||||
case 400:
|
||||
return 'Bad Request'
|
||||
case 401:
|
||||
return 'Unauthorized'
|
||||
case 403:
|
||||
return 'Forbidden'
|
||||
case 404:
|
||||
return 'Not Found'
|
||||
case 408:
|
||||
return 'Request Timeout'
|
||||
case 429:
|
||||
return 'Too Many Requests'
|
||||
case 500:
|
||||
return 'Internal Server Error'
|
||||
case 502:
|
||||
return 'Bad Gateway'
|
||||
case 503:
|
||||
return 'Service Unavailable'
|
||||
case 504:
|
||||
return 'Gateway Timeout'
|
||||
default:
|
||||
return `HTTP ${statusCode}`
|
||||
}
|
||||
})()
|
||||
|
||||
errorMessage = `[${method}] ${statusMessage} (${statusCode}) while fetching ${path}${module ? ` in ${module}` : ''}`
|
||||
} else {
|
||||
errorMessage = `${message}${statusCode ? ` (${statusCode})` : ''}${module ? ` in ${module}` : ''}`
|
||||
}
|
||||
|
||||
super(errorMessage)
|
||||
this.name = 'PyroServersFetchError'
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
export class ModrinthServersFetchError extends Error {
|
||||
constructor(
|
||||
message: string,
|
||||
public statusCode?: number,
|
||||
public originalError?: Error,
|
||||
) {
|
||||
super(message)
|
||||
this.name = 'PyroFetchError'
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
export class ModrinthServersMultiError extends Error {
|
||||
public readonly errors: Map<string, Error> = new Map()
|
||||
public readonly timestamp: number = Date.now()
|
||||
|
||||
constructor(message?: string) {
|
||||
super(message || 'Multiple errors occurred')
|
||||
this.name = 'MultipleErrors'
|
||||
}
|
||||
|
||||
addError(module: string, error: Error) {
|
||||
this.errors.set(module, error)
|
||||
this.message = this.buildErrorMessage()
|
||||
}
|
||||
|
||||
hasErrors() {
|
||||
return this.errors.size > 0
|
||||
}
|
||||
|
||||
private buildErrorMessage(): string {
|
||||
return (
|
||||
Array.from(this.errors.entries())
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
.map(([_module, error]) => error.message)
|
||||
.join('\n')
|
||||
)
|
||||
}
|
||||
}
|
||||
2
packages/utils/servers/index.ts
Normal file
2
packages/utils/servers/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export * from './errors'
|
||||
export * from './types'
|
||||
19
packages/utils/servers/types/api.ts
Normal file
19
packages/utils/servers/types/api.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { ModrinthServerError } from '../errors'
|
||||
|
||||
export interface V1ErrorInfo {
|
||||
context?: string
|
||||
error: string
|
||||
description: string
|
||||
}
|
||||
|
||||
export interface JWTAuth {
|
||||
url: string
|
||||
token: string
|
||||
}
|
||||
|
||||
export interface ModuleError {
|
||||
error: ModrinthServerError
|
||||
timestamp: number
|
||||
}
|
||||
|
||||
export type ModuleName = 'general' | 'content' | 'backups' | 'network' | 'startup' | 'ws' | 'fs'
|
||||
28
packages/utils/servers/types/backup.ts
Normal file
28
packages/utils/servers/types/backup.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import type { WSBackupTask, WSBackupState } from './websocket'
|
||||
|
||||
export interface Backup {
|
||||
id: string
|
||||
name: string
|
||||
created_at: string
|
||||
locked: boolean
|
||||
automated: boolean
|
||||
interrupted: boolean
|
||||
ongoing: boolean
|
||||
task: {
|
||||
[K in WSBackupTask]?: {
|
||||
progress: number
|
||||
state: WSBackupState
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export interface AutoBackupSettings {
|
||||
enabled: boolean
|
||||
interval: number
|
||||
}
|
||||
|
||||
export interface ServerBackup {
|
||||
id: string
|
||||
name: string
|
||||
created_at: string
|
||||
}
|
||||
59
packages/utils/servers/types/common.ts
Normal file
59
packages/utils/servers/types/common.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
import type { Project } from '../../types'
|
||||
import { Allocation } from './server'
|
||||
import { ServerBackup } from './backup'
|
||||
import { Mod } from './content'
|
||||
|
||||
export type ServerNotice = {
|
||||
id: number
|
||||
message: string
|
||||
title?: string
|
||||
level: 'info' | 'warn' | 'critical' | 'survey'
|
||||
dismissable: boolean
|
||||
announce_at: string
|
||||
expires: string
|
||||
assigned: {
|
||||
kind: 'server' | 'node'
|
||||
id: string
|
||||
name: string
|
||||
}[]
|
||||
dismissed_by: {
|
||||
server: string
|
||||
dismissed_on: string
|
||||
}[]
|
||||
}
|
||||
|
||||
export interface Server {
|
||||
server_id: string
|
||||
name: string
|
||||
status: string
|
||||
net: {
|
||||
ip: string
|
||||
port: number
|
||||
domain: string
|
||||
allocations: Allocation[]
|
||||
}
|
||||
game: string
|
||||
loader: string | null
|
||||
loader_version: string | null
|
||||
mc_version: string | null
|
||||
backup_quota: number
|
||||
used_backup_quota: number
|
||||
backups: ServerBackup[]
|
||||
mods: Mod[]
|
||||
project: Project | null
|
||||
suspension_reason: string | null
|
||||
image: string | null
|
||||
upstream?: {
|
||||
kind: 'modpack'
|
||||
project_id: string
|
||||
version_id: string
|
||||
}
|
||||
motd: string
|
||||
flows: {
|
||||
intro?: boolean
|
||||
}
|
||||
}
|
||||
|
||||
export interface Servers {
|
||||
servers: Server[]
|
||||
}
|
||||
13
packages/utils/servers/types/content.ts
Normal file
13
packages/utils/servers/types/content.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
export interface Mod {
|
||||
filename: string
|
||||
project_id: string | undefined
|
||||
version_id: string | undefined
|
||||
name: string | undefined
|
||||
version_number: string | undefined
|
||||
icon_url: string | undefined
|
||||
owner: string | undefined
|
||||
disabled: boolean
|
||||
installing: boolean
|
||||
}
|
||||
|
||||
export type ContentType = 'mod' | 'plugin'
|
||||
33
packages/utils/servers/types/filesystem.ts
Normal file
33
packages/utils/servers/types/filesystem.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import type { FSQueuedOp, FilesystemOp } from './websocket'
|
||||
import { JWTAuth } from './api'
|
||||
|
||||
export interface DirectoryItem {
|
||||
name: string
|
||||
type: 'directory' | 'file'
|
||||
count?: number
|
||||
modified: number
|
||||
created: number
|
||||
path: string
|
||||
}
|
||||
|
||||
export interface FileUploadQuery {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
promise: Promise<any>
|
||||
onProgress: (
|
||||
callback: (progress: { loaded: number; total: number; progress: number }) => void,
|
||||
) => void
|
||||
cancel: () => void
|
||||
}
|
||||
|
||||
export interface DirectoryResponse {
|
||||
items: DirectoryItem[]
|
||||
total: number
|
||||
current?: number
|
||||
}
|
||||
|
||||
export interface FSModule {
|
||||
auth: JWTAuth
|
||||
ops: FilesystemOp[]
|
||||
queuedOps: FSQueuedOp[]
|
||||
opsQueuedForModification: string[]
|
||||
}
|
||||
8
packages/utils/servers/types/index.ts
Normal file
8
packages/utils/servers/types/index.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
export * from './api'
|
||||
export * from './content'
|
||||
export * from './server'
|
||||
export * from './backup'
|
||||
export * from './filesystem'
|
||||
export * from './websocket'
|
||||
export * from './stats'
|
||||
export * from './common'
|
||||
76
packages/utils/servers/types/server.ts
Normal file
76
packages/utils/servers/types/server.ts
Normal file
@@ -0,0 +1,76 @@
|
||||
import type { Project } from '../../types'
|
||||
import type { ServerNotice } from './common'
|
||||
|
||||
export interface ServerGeneral {
|
||||
server_id: string
|
||||
name: string
|
||||
net: {
|
||||
ip: string
|
||||
port: number
|
||||
domain: string
|
||||
}
|
||||
game: string
|
||||
backup_quota: number
|
||||
used_backup_quota: number
|
||||
status: string
|
||||
suspension_reason: 'moderated' | 'paymentfailed' | 'cancelled' | 'upgrading' | 'other' | 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?: ServerNotice[]
|
||||
node: {
|
||||
token: string
|
||||
instance: string
|
||||
}
|
||||
flows?: {
|
||||
intro?: boolean
|
||||
}
|
||||
}
|
||||
|
||||
export interface Allocation {
|
||||
port: number
|
||||
name: string
|
||||
}
|
||||
|
||||
export interface Startup {
|
||||
invocation: string
|
||||
original_invocation: string
|
||||
jdk_version: 'lts8' | 'lts11' | 'lts17' | 'lts21'
|
||||
jdk_build: 'corretto' | 'temurin' | 'graal'
|
||||
}
|
||||
|
||||
export type PowerAction = 'Start' | 'Stop' | 'Restart' | 'Kill'
|
||||
export type JDKVersion = 'lts8' | 'lts11' | 'lts17' | 'lts21'
|
||||
export type JDKBuild = 'corretto' | 'temurin' | 'graal'
|
||||
|
||||
export type Loaders =
|
||||
| 'Fabric'
|
||||
| 'Quilt'
|
||||
| 'Forge'
|
||||
| 'NeoForge'
|
||||
| 'Paper'
|
||||
| 'Spigot'
|
||||
| 'Bukkit'
|
||||
| 'Vanilla'
|
||||
| 'Purpur'
|
||||
|
||||
export type ServerState =
|
||||
| 'starting'
|
||||
| 'running'
|
||||
| 'restarting'
|
||||
| 'stopping'
|
||||
| 'stopped'
|
||||
| 'crashed'
|
||||
| 'unknown'
|
||||
20
packages/utils/servers/types/stats.ts
Normal file
20
packages/utils/servers/types/stats.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
export interface Stats {
|
||||
current: {
|
||||
cpu_percent: number
|
||||
ram_usage_bytes: number
|
||||
ram_total_bytes: number
|
||||
storage_usage_bytes: number
|
||||
storage_total_bytes: number
|
||||
}
|
||||
past: {
|
||||
cpu_percent: number
|
||||
ram_usage_bytes: number
|
||||
ram_total_bytes: number
|
||||
storage_usage_bytes: number
|
||||
storage_total_bytes: number
|
||||
}
|
||||
graph: {
|
||||
cpu: number[]
|
||||
ram: number[]
|
||||
}
|
||||
}
|
||||
124
packages/utils/servers/types/websocket.ts
Normal file
124
packages/utils/servers/types/websocket.ts
Normal file
@@ -0,0 +1,124 @@
|
||||
import type { Stats } from './stats'
|
||||
import type { ServerState } from './server'
|
||||
|
||||
export interface WSAuth {
|
||||
url: string
|
||||
token: string
|
||||
}
|
||||
|
||||
export interface WSLogEvent {
|
||||
event: 'log'
|
||||
message: string
|
||||
}
|
||||
|
||||
type CurrentStats = Stats['current']
|
||||
|
||||
export interface WSStatsEvent extends CurrentStats {
|
||||
event: 'stats'
|
||||
}
|
||||
|
||||
export interface WSAuthExpiringEvent {
|
||||
event: 'auth-expiring'
|
||||
}
|
||||
|
||||
export interface WSPowerStateEvent {
|
||||
event: 'power-state'
|
||||
state: ServerState
|
||||
// if state "crashed"
|
||||
oom_killed?: boolean
|
||||
exit_code?: number
|
||||
}
|
||||
|
||||
export interface WSAuthIncorrectEvent {
|
||||
event: 'auth-incorrect'
|
||||
}
|
||||
|
||||
export interface WSInstallationResultOkEvent {
|
||||
event: 'installation-result'
|
||||
result: 'ok'
|
||||
}
|
||||
|
||||
export interface WSInstallationResultErrEvent {
|
||||
event: 'installation-result'
|
||||
result: 'err'
|
||||
reason: string
|
||||
}
|
||||
|
||||
export type WSInstallationResultEvent = WSInstallationResultOkEvent | WSInstallationResultErrEvent
|
||||
|
||||
export interface WSAuthOkEvent {
|
||||
event: 'auth-ok'
|
||||
}
|
||||
|
||||
export interface WSUptimeEvent {
|
||||
event: 'uptime'
|
||||
uptime: number // seconds
|
||||
}
|
||||
|
||||
export interface WSNewModEvent {
|
||||
event: 'new-mod'
|
||||
}
|
||||
|
||||
export type WSBackupTask = 'file' | 'create' | 'restore'
|
||||
export type WSBackupState = 'ongoing' | 'done' | 'failed' | 'cancelled' | 'unchanged'
|
||||
|
||||
export interface WSBackupProgressEvent {
|
||||
event: 'backup-progress'
|
||||
task: WSBackupTask
|
||||
id: string
|
||||
progress: number // percentage
|
||||
state: WSBackupState
|
||||
ready: boolean
|
||||
}
|
||||
|
||||
export type FSQueuedOpUnarchive = {
|
||||
op: 'unarchive'
|
||||
src: string
|
||||
}
|
||||
|
||||
export type FSQueuedOp = FSQueuedOpUnarchive
|
||||
|
||||
export type FSOpUnarchive = {
|
||||
op: 'unarchive'
|
||||
progress: number // Note: 1 does not mean it's done
|
||||
id: string // UUID
|
||||
|
||||
mime: string
|
||||
src: string
|
||||
state:
|
||||
| 'queued'
|
||||
| 'ongoing'
|
||||
| 'cancelled'
|
||||
| 'done'
|
||||
| 'failed-corrupted'
|
||||
| 'failed-invalid-path'
|
||||
| 'failed-cf-no-serverpack'
|
||||
| 'failed-cf-not-available'
|
||||
| 'failed-not-reachable'
|
||||
|
||||
current_file: string | null
|
||||
failed_path?: string
|
||||
bytes_processed: number
|
||||
files_processed: number
|
||||
started: string
|
||||
}
|
||||
|
||||
export type FilesystemOp = FSOpUnarchive
|
||||
|
||||
export interface WSFilesystemOpsEvent {
|
||||
event: 'filesystem-ops'
|
||||
all: FilesystemOp[]
|
||||
}
|
||||
|
||||
export type WSEvent =
|
||||
| WSLogEvent
|
||||
| WSStatsEvent
|
||||
| WSPowerStateEvent
|
||||
| WSAuthExpiringEvent
|
||||
| WSAuthIncorrectEvent
|
||||
| WSInstallationResultEvent
|
||||
| WSAuthOkEvent
|
||||
| WSUptimeEvent
|
||||
| WSNewModEvent
|
||||
| WSBackupProgressEvent
|
||||
| WSFilesystemOpsEvent
|
||||
@@ -294,22 +294,3 @@ export type Report = {
|
||||
created: string
|
||||
body: string
|
||||
}
|
||||
|
||||
export type ServerNotice = {
|
||||
id: number
|
||||
message: string
|
||||
title?: string
|
||||
level: 'info' | 'warn' | 'critical' | 'survey'
|
||||
dismissable: boolean
|
||||
announce_at: string
|
||||
expires: string
|
||||
assigned: {
|
||||
kind: 'server' | 'node'
|
||||
id: string
|
||||
name: string
|
||||
}[]
|
||||
dismissed_by: {
|
||||
server: string
|
||||
dismissed_on: string
|
||||
}[]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user