Reset repository to README title only
Approach is changing entirely; clearing prior implementation to start over from a clean slate. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -1,247 +0,0 @@
|
||||
import fs from 'node:fs'
|
||||
import fsp from 'node:fs/promises'
|
||||
import path from 'node:path'
|
||||
import { accountPath, fileDir, manifestDir, manifestRootPath } from './paths'
|
||||
import { AccountEntry, DashboardPackEntry, PackDefinition, PackListEntry, RootManifest } from './types'
|
||||
|
||||
const defaultRootManifest: RootManifest = {
|
||||
packs: [
|
||||
{
|
||||
name: 'Sample Pack',
|
||||
file: 'sample-pack'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
const defaultAccount: AccountEntry[] = [
|
||||
{
|
||||
id: 'admin',
|
||||
password: 'change-me'
|
||||
}
|
||||
]
|
||||
|
||||
const defaultPackDefinition: PackDefinition = {
|
||||
mcVersion: '1.20.1',
|
||||
recommendedJdkVersion: 17,
|
||||
loaderType: 'vanilla',
|
||||
loaderVersion: '',
|
||||
loaderInstallerPath: '',
|
||||
serverMinRam: 2048,
|
||||
serverMaxRam: 4096,
|
||||
clientMinRam: 4096,
|
||||
clientRecommendedRam: 8192,
|
||||
packPath: 'sample-pack.zip',
|
||||
description: '새 서버팩',
|
||||
configEditableFiles: ['server.properties', 'bukkit.yml'],
|
||||
resourcePackFiles: [],
|
||||
shaderPackFiles: []
|
||||
}
|
||||
|
||||
async function ensureDir(targetPath: string): Promise<void> {
|
||||
await fsp.mkdir(targetPath, { recursive: true })
|
||||
}
|
||||
|
||||
async function ensureJsonFile<T>(targetPath: string, defaultValue: T): Promise<void> {
|
||||
if (!fs.existsSync(targetPath)) {
|
||||
await fsp.writeFile(targetPath, `${JSON.stringify(defaultValue, null, 2)}\n`, 'utf8')
|
||||
}
|
||||
}
|
||||
|
||||
export async function ensureProjectFiles(): Promise<void> {
|
||||
await ensureDir(manifestDir)
|
||||
await ensureDir(fileDir)
|
||||
await ensureJsonFile(manifestRootPath, defaultRootManifest)
|
||||
await ensureJsonFile(accountPath, defaultAccount)
|
||||
|
||||
const samplePackPath = path.join(manifestDir, 'sample-pack.json')
|
||||
await ensureJsonFile(samplePackPath, defaultPackDefinition)
|
||||
}
|
||||
|
||||
async function readJsonFile<T>(targetPath: string, fallback: T): Promise<T> {
|
||||
try {
|
||||
const raw = await fsp.readFile(targetPath, 'utf8')
|
||||
return JSON.parse(raw) as T
|
||||
} catch {
|
||||
return fallback
|
||||
}
|
||||
}
|
||||
|
||||
async function writeJsonFile(targetPath: string, payload: unknown): Promise<void> {
|
||||
await fsp.writeFile(targetPath, `${JSON.stringify(payload, null, 2)}\n`, 'utf8')
|
||||
}
|
||||
|
||||
function sanitizePackKey(name: string): string {
|
||||
const trimmed = name.trim().replace(/\.json$/i, '')
|
||||
const normalized = trimmed.replace(/[^a-zA-Z0-9_-]/g, '-').replace(/-+/g, '-')
|
||||
return normalized.length > 0 ? normalized : 'new'
|
||||
}
|
||||
|
||||
export async function loadRootManifest(): Promise<RootManifest> {
|
||||
await ensureProjectFiles()
|
||||
return readJsonFile<RootManifest>(manifestRootPath, defaultRootManifest)
|
||||
}
|
||||
|
||||
export async function saveRootManifest(manifest: RootManifest): Promise<void> {
|
||||
await writeJsonFile(manifestRootPath, manifest)
|
||||
}
|
||||
|
||||
export async function loadAccounts(): Promise<AccountEntry[]> {
|
||||
await ensureProjectFiles()
|
||||
return readJsonFile<AccountEntry[]>(accountPath, defaultAccount)
|
||||
}
|
||||
|
||||
export async function listManifestFiles(): Promise<string[]> {
|
||||
await ensureProjectFiles()
|
||||
const entries = await fsp.readdir(manifestDir, { withFileTypes: true })
|
||||
return entries
|
||||
.filter((entry) => entry.isFile() && entry.name.endsWith('.json'))
|
||||
.map((entry) => entry.name.replace(/\.json$/i, ''))
|
||||
.sort((left, right) => left.localeCompare(right))
|
||||
}
|
||||
|
||||
export async function listDashboardPacks(): Promise<DashboardPackEntry[]> {
|
||||
const [manifestFiles, rootManifest] = await Promise.all([
|
||||
listManifestFiles(),
|
||||
loadRootManifest()
|
||||
])
|
||||
|
||||
return manifestFiles.map((file) => {
|
||||
const registeredPack = rootManifest.packs.find((entry) => entry.file === file)
|
||||
return {
|
||||
file,
|
||||
name: registeredPack?.name ?? file,
|
||||
registered: registeredPack != null
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export async function loadPackDefinition(packKey: string): Promise<PackDefinition | null> {
|
||||
await ensureProjectFiles()
|
||||
const safeKey = sanitizePackKey(packKey)
|
||||
const filePath = path.join(manifestDir, `${safeKey}.json`)
|
||||
if (!fs.existsSync(filePath)) {
|
||||
return null
|
||||
}
|
||||
|
||||
return readJsonFile<PackDefinition>(filePath, defaultPackDefinition)
|
||||
}
|
||||
|
||||
export async function savePackDefinition(packKey: string, payload: PackDefinition): Promise<void> {
|
||||
const safeKey = sanitizePackKey(packKey)
|
||||
await writeJsonFile(path.join(manifestDir, `${safeKey}.json`), payload)
|
||||
}
|
||||
|
||||
function nextAvailableNewKey(existing: string[]): string {
|
||||
if (!existing.includes('new')) {
|
||||
return 'new'
|
||||
}
|
||||
|
||||
let index = 2
|
||||
while (existing.includes(`new${index}`)) {
|
||||
index += 1
|
||||
}
|
||||
return `new${index}`
|
||||
}
|
||||
|
||||
export async function createNewPack(): Promise<string> {
|
||||
const existing = await listManifestFiles()
|
||||
const packKey = nextAvailableNewKey(existing)
|
||||
await savePackDefinition(packKey, {
|
||||
...defaultPackDefinition,
|
||||
description: `새 서버팩 (${packKey})`
|
||||
})
|
||||
|
||||
const manifest = await loadRootManifest()
|
||||
manifest.packs.push({
|
||||
name: `새 서버팩 (${packKey})`,
|
||||
file: packKey
|
||||
})
|
||||
await saveRootManifest(manifest)
|
||||
return packKey
|
||||
}
|
||||
|
||||
export async function deletePacks(packKeys: string[]): Promise<void> {
|
||||
const targetKeys = new Set(packKeys.map((entry) => sanitizePackKey(entry)))
|
||||
const manifest = await loadRootManifest()
|
||||
manifest.packs = manifest.packs.filter((entry) => !targetKeys.has(entry.file))
|
||||
await saveRootManifest(manifest)
|
||||
|
||||
await Promise.all(
|
||||
[...targetKeys].map(async (packKey) => {
|
||||
const filePath = path.join(manifestDir, `${packKey}.json`)
|
||||
if (fs.existsSync(filePath)) {
|
||||
await fsp.unlink(filePath)
|
||||
}
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
export async function updatePack(
|
||||
currentKey: string,
|
||||
nextName: string,
|
||||
nextKey: string,
|
||||
definition: PackDefinition
|
||||
): Promise<string> {
|
||||
const safeCurrentKey = sanitizePackKey(currentKey)
|
||||
const safeNextKey = sanitizePackKey(nextKey)
|
||||
const currentPath = path.join(manifestDir, `${safeCurrentKey}.json`)
|
||||
const nextPath = path.join(manifestDir, `${safeNextKey}.json`)
|
||||
|
||||
if (safeCurrentKey !== safeNextKey && fs.existsSync(nextPath)) {
|
||||
throw new Error('같은 이름의 JSON 파일이 이미 존재합니다.')
|
||||
}
|
||||
|
||||
await savePackDefinition(safeNextKey, definition)
|
||||
|
||||
if (safeCurrentKey !== safeNextKey && fs.existsSync(currentPath)) {
|
||||
await fsp.unlink(currentPath)
|
||||
}
|
||||
|
||||
const manifest = await loadRootManifest()
|
||||
const targetIndex = manifest.packs.findIndex((entry) => entry.file === safeCurrentKey)
|
||||
const nextEntry: PackListEntry = {
|
||||
name: nextName.trim(),
|
||||
file: safeNextKey
|
||||
}
|
||||
|
||||
if (targetIndex >= 0) {
|
||||
manifest.packs[targetIndex] = nextEntry
|
||||
} else {
|
||||
manifest.packs.push(nextEntry)
|
||||
}
|
||||
|
||||
await saveRootManifest(manifest)
|
||||
return safeNextKey
|
||||
}
|
||||
|
||||
export function normalizePackDefinition(input: Partial<PackDefinition>): PackDefinition {
|
||||
return {
|
||||
mcVersion: String(input.mcVersion ?? '1.20.1').trim() || '1.20.1',
|
||||
recommendedJdkVersion: Number.isFinite(Number(input.recommendedJdkVersion))
|
||||
? Number(input.recommendedJdkVersion)
|
||||
: 17,
|
||||
loaderType: ['vanilla', 'forge', 'fabric', 'neoforge'].includes(String(input.loaderType ?? 'vanilla'))
|
||||
? String(input.loaderType ?? 'vanilla') as PackDefinition['loaderType']
|
||||
: 'vanilla',
|
||||
loaderVersion: String(input.loaderVersion ?? '').trim(),
|
||||
loaderInstallerPath: String(input.loaderInstallerPath ?? '').trim(),
|
||||
serverMinRam: Number(input.serverMinRam ?? 2048),
|
||||
serverMaxRam: Number(input.serverMaxRam ?? 4096),
|
||||
clientMinRam: Number(input.clientMinRam ?? 4096),
|
||||
clientRecommendedRam: Number(input.clientRecommendedRam ?? 8192),
|
||||
packPath: String(input.packPath ?? '').trim(),
|
||||
description: String(input.description ?? '').trim(),
|
||||
files: Array.isArray(input.files)
|
||||
? input.files.map((entry) => String(entry).trim()).filter((entry) => entry.length > 0)
|
||||
: undefined,
|
||||
configEditableFiles: Array.isArray(input.configEditableFiles)
|
||||
? input.configEditableFiles.map((entry) => String(entry).trim()).filter((entry) => entry.length > 0)
|
||||
: ['server.properties', 'bukkit.yml'],
|
||||
resourcePackFiles: Array.isArray(input.resourcePackFiles)
|
||||
? input.resourcePackFiles.map((entry) => String(entry).trim()).filter((entry) => entry.length > 0)
|
||||
: [],
|
||||
shaderPackFiles: Array.isArray(input.shaderPackFiles)
|
||||
? input.shaderPackFiles.map((entry) => String(entry).trim()).filter((entry) => entry.length > 0)
|
||||
: []
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user