diff --git a/app/src/lib/api/auth.ts b/app/src/lib/api/auth.ts index b8559d5..78fe59e 100644 --- a/app/src/lib/api/auth.ts +++ b/app/src/lib/api/auth.ts @@ -11,7 +11,7 @@ export const auth = betterAuth({ socialProviders: { microsoft: { clientId: process.env.MICROSOFT_CLIENT_ID as string, - clientSecret: process.env.MICROSOFT_CLIENT_SECRET as string, + clientSecret: process.env.MICROSOFT_CLIENT_SECRET as string } } }); diff --git a/app/src/lib/api/utils/projectmanager.ts b/app/src/lib/api/utils/docker/projectmanager.ts similarity index 82% rename from app/src/lib/api/utils/projectmanager.ts rename to app/src/lib/api/utils/docker/projectmanager.ts index 5f4abaf..0c20c62 100644 --- a/app/src/lib/api/utils/projectmanager.ts +++ b/app/src/lib/api/utils/docker/projectmanager.ts @@ -1,5 +1,6 @@ import { spawn, $, write, file } from 'bun'; -import { join } from 'path'; +import { join } from 'node:path'; +import { stringifyYAML } from '../stringify-yaml'; declare namespace Bun { export const YAML: { @@ -10,7 +11,7 @@ declare namespace Bun { interface MinecraftEnvironment { EULA: string; VERSION?: string; - TYPE?: 'VANILLA' | 'FORGE' | 'FABRIC' | 'PAPER' | 'SPIGOT' | 'BUKKIT' | 'PURPUR' | 'MODDED'; + TYPE?: ; DIFFICULTY?: 'PEACEFUL' | 'EASY' | 'NORMAL' | 'HARD'; MODE?: 'SURVIVAL' | 'CREATIVE' | 'ADVENTURE' | 'SPECTATOR'; MAX_PLAYERS?: string; @@ -41,50 +42,6 @@ interface MinecraftService { restart?: 'unless-stopped' | 'no'; networks?: string[]; } - -// simple YAML stringifier for basic objects -function stringifyYAML(obj: any, indent = 0): string { - const spaces = ' '.repeat(indent); - - if (obj === null || obj === undefined) { - return 'null'; - } - - if (typeof obj === 'string') { - // handle strings that need quoting - if (obj.includes(':') || obj.includes('\n') || obj.includes('#')) { - return `"${obj.replace(/"/g, '\\"')}"`; - } - return obj; - } - - if (typeof obj === 'number' || typeof obj === 'boolean') { - return String(obj); - } - - if (Array.isArray(obj)) { - if (obj.length === 0) return '[]'; - return obj.map((item) => `${spaces}- ${stringifyYAML(item, indent + 2)}`).join('\n'); - } - - if (typeof obj === 'object') { - const entries = Object.entries(obj); - if (entries.length === 0) return '{}'; - - return entries - .map(([key, value]) => { - if (typeof value === 'object' && value !== null && !Array.isArray(value)) { - return `${spaces}${key}:\n${stringifyYAML(value, indent + 2)}`; - } else { - return `${spaces}${key}: ${stringifyYAML(value, 0)}`; - } - }) - .join('\n'); - } - - return String(obj); -} - // minimal Minecraft docker-compose template const baseTemplate: { services: { mc: MinecraftService } } = { services: { @@ -96,7 +53,7 @@ const baseTemplate: { services: { mc: MinecraftService } } = { ports: ['25565:25565'], environment: { EULA: 'TRUE', - TYPE: 'PAPER', + TYPE: 'LEAF', ENABLE_WHITELIST: 'true', WHITELIST: 'f396e2b9-cbb1-46a0-bb72-96898a1ca44d', DIFFICULTY: 'NORMAL', @@ -176,13 +133,13 @@ class ProjectManager { await $`rm -rf ${cwd}`; } - async runCompose(name: string, args: string[]) { + runCompose(name: string, args: string[]) { const cwd = this.projectPath(name); - const result = await spawn(['docker', 'compose', ...args], { cwd }); + const result = spawn(['docker', 'compose', ...args], { cwd }); return result.exited; } - async attach(name: string) { + attach(name: string) { const cwd = this.projectPath(name); const process = spawn(['docker', 'exec', '-i', 'minecraft', 'rcon-cli'], { @@ -215,11 +172,11 @@ class ProjectManager { const ansiRegex = /\x1b\[([0-9;]+)m/g; const chunks: any[] = []; let lastIndex = 0; - let match; + let match: RegExpExecArray | null = ansiRegex.exec(text); let currentColor: string | undefined; let currentBold = false; - while ((match = ansiRegex.exec(text)) !== null) { + while (match !== null) { if (match.index > lastIndex) { const textChunk = text.substring(lastIndex, match.index); chunks.push({ @@ -243,6 +200,7 @@ class ProjectManager { } lastIndex = ansiRegex.lastIndex; + match = ansiRegex.exec(text); } if (lastIndex < text.length) { @@ -325,7 +283,7 @@ class ProjectManager { const writer = { async write(text: string) { if (process.stdin) { - await process.stdin.write(text); + process.stdin.write(text); } }, async close() { diff --git a/app/src/lib/api/utils/stringify-yaml.ts b/app/src/lib/api/utils/stringify-yaml.ts new file mode 100644 index 0000000..3029726 --- /dev/null +++ b/app/src/lib/api/utils/stringify-yaml.ts @@ -0,0 +1,41 @@ +export function stringifyYAML(obj: any, indent = 0): string { + const spaces = ' '.repeat(indent); + + if (obj === null || obj === undefined) { + return 'null'; + } + + if (typeof obj === 'string') { + // handle strings that need quoting + if (obj.includes(':') || obj.includes('\n') || obj.includes('#')) { + return `"${obj.replace(/"/g, '\\"')}"`; + } + return obj; + } + + if (typeof obj === 'number' || typeof obj === 'boolean') { + return String(obj); + } + + if (Array.isArray(obj)) { + if (obj.length === 0) return '[]'; + return obj.map((item) => `${spaces}- ${stringifyYAML(item, indent + 2)}`).join('\n'); + } + + if (typeof obj === 'object') { + const entries = Object.entries(obj); + if (entries.length === 0) return '{}'; + + return entries + .map(([key, value]) => { + if (typeof value === 'object' && value !== null && !Array.isArray(value)) { + return `${spaces}${key}:\n${stringifyYAML(value, indent + 2)}`; + } else { + return `${spaces}${key}: ${stringifyYAML(value, 0)}`; + } + }) + .join('\n'); + } + + return String(obj); +} diff --git a/app/src/lib/types/mcservetype.type.ts b/app/src/lib/types/mcservetype.type.ts new file mode 100644 index 0000000..9f78699 --- /dev/null +++ b/app/src/lib/types/mcservetype.type.ts @@ -0,0 +1,17 @@ +type McServerType = + | 'VANILLA' + | 'NEOFORGE' + | 'FORGE' + | 'FABRIC' + | 'PAPER' + | 'LEAF' + | 'SPIGOT' + | 'CANYON' + | 'PURPUR' + | 'MAGMA' + | 'MAGMA_MAINTAINED' + | 'KETTING' + | 'MOHIST' + | 'YOUER' + | 'BANNER' + | 'CATSERVER';