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,293 +0,0 @@
|
||||
import { Router } from 'express'
|
||||
import multer from 'multer'
|
||||
import path from 'node:path'
|
||||
import fs from 'node:fs'
|
||||
import fsp from 'node:fs/promises'
|
||||
import { fetchReleaseVersions } from '../../shared/mojang'
|
||||
import { fileDir } from '../../shared/paths'
|
||||
import {
|
||||
createNewPack,
|
||||
deletePacks,
|
||||
listDashboardPacks,
|
||||
loadAccounts,
|
||||
loadPackDefinition,
|
||||
loadRootManifest,
|
||||
normalizePackDefinition,
|
||||
savePackDefinition,
|
||||
updatePack
|
||||
} from '../../shared/store'
|
||||
import { PackDefinition } from '../../shared/types'
|
||||
import { requireAuth } from '../middleware/auth'
|
||||
|
||||
export const opRouter = Router()
|
||||
const upload = multer({ storage: multer.memoryStorage() })
|
||||
|
||||
function pickFirstValue(value: unknown): string {
|
||||
if (Array.isArray(value)) {
|
||||
return typeof value[0] === 'string' ? value[0] : ''
|
||||
}
|
||||
return typeof value === 'string' ? value : ''
|
||||
}
|
||||
|
||||
function sanitizeUploadFileName(name: string): string {
|
||||
return name.replace(/[^a-zA-Z0-9._-]/g, '-').replace(/-+/g, '-')
|
||||
}
|
||||
|
||||
function normalizeAssetPathForWeb(filePath: string): string {
|
||||
return filePath.replace(/\\/g, '/')
|
||||
}
|
||||
|
||||
async function saveUploadedPackAsset(packKey: string, bucket: 'loaders' | 'resourcepacks' | 'shaderpacks', file: Express.Multer.File): Promise<string> {
|
||||
const safeName = sanitizeUploadFileName(file.originalname)
|
||||
const relativePath = path.join('uploads', packKey, bucket, `${Date.now()}-${safeName}`)
|
||||
const absolutePath = path.join(fileDir, relativePath)
|
||||
await fsp.mkdir(path.dirname(absolutePath), { recursive: true })
|
||||
await fsp.writeFile(absolutePath, file.buffer)
|
||||
return normalizeAssetPathForWeb(relativePath)
|
||||
}
|
||||
|
||||
async function mutatePackDefinition(packKey: string, mutate: (pack: PackDefinition) => void): Promise<void> {
|
||||
const current = await loadPackDefinition(packKey)
|
||||
if (current == null) {
|
||||
throw new Error('서버팩 JSON을 찾을 수 없습니다.')
|
||||
}
|
||||
|
||||
const next = normalizePackDefinition(current)
|
||||
mutate(next)
|
||||
await savePackDefinition(packKey, next)
|
||||
}
|
||||
|
||||
async function removeUploadedAsset(relativePath: string): Promise<void> {
|
||||
const absolutePath = path.join(fileDir, relativePath)
|
||||
if (fs.existsSync(absolutePath)) {
|
||||
await fsp.unlink(absolutePath)
|
||||
}
|
||||
}
|
||||
|
||||
opRouter.get('/op', (req, res) => {
|
||||
if (req.session.userId != null) {
|
||||
res.redirect('/op/dashboard')
|
||||
return
|
||||
}
|
||||
|
||||
res.render('op/login', {
|
||||
errorMessage: null
|
||||
})
|
||||
})
|
||||
|
||||
opRouter.post('/op/login', async (req, res, next) => {
|
||||
try {
|
||||
const { password } = req.body as { password?: string }
|
||||
const accounts = await loadAccounts()
|
||||
const matched = accounts.find((entry) => entry.password === password)
|
||||
|
||||
if (matched == null) {
|
||||
res.status(401).render('op/login', {
|
||||
errorMessage: '비밀번호가 올바르지 않습니다.'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
req.session.userId = matched.id
|
||||
res.redirect('/op/dashboard')
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
})
|
||||
|
||||
opRouter.post('/op/logout', requireAuth, (req, res) => {
|
||||
req.session.destroy(() => {
|
||||
res.redirect('/op')
|
||||
})
|
||||
})
|
||||
|
||||
opRouter.get('/op/dashboard', requireAuth, async (_req, res, next) => {
|
||||
try {
|
||||
const packs = await listDashboardPacks()
|
||||
res.render('op/dashboard', {
|
||||
userId: _req.session.userId,
|
||||
packs
|
||||
})
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
})
|
||||
|
||||
opRouter.post('/op/dashboard/packs', requireAuth, async (_req, res, next) => {
|
||||
try {
|
||||
const packKey = await createNewPack()
|
||||
res.redirect(`/op/dashboard/${packKey}`)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
})
|
||||
|
||||
opRouter.post('/op/dashboard/packs/delete', requireAuth, async (req, res, next) => {
|
||||
try {
|
||||
const rawSelection = req.body.packKeys
|
||||
const packKeys = Array.isArray(rawSelection)
|
||||
? rawSelection.map(String)
|
||||
: typeof rawSelection === 'string'
|
||||
? [rawSelection]
|
||||
: []
|
||||
|
||||
await deletePacks(packKeys)
|
||||
res.redirect('/op/dashboard')
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
})
|
||||
|
||||
opRouter.get('/op/dashboard/:packName', requireAuth, async (req, res, next) => {
|
||||
try {
|
||||
const packName = pickFirstValue(req.params.packName)
|
||||
const definition = await loadPackDefinition(packName)
|
||||
if (definition == null) {
|
||||
res.status(404).send('서버팩 JSON을 찾을 수 없습니다.')
|
||||
return
|
||||
}
|
||||
|
||||
const rootManifest = await loadRootManifest()
|
||||
const packEntry = rootManifest.packs.find((entry) => entry.file === packName)
|
||||
const releases = await fetchReleaseVersions()
|
||||
|
||||
res.render('op/editor', {
|
||||
userId: req.session.userId,
|
||||
packKey: packName,
|
||||
packEntry,
|
||||
pack: definition,
|
||||
releases
|
||||
})
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
})
|
||||
|
||||
opRouter.post('/op/dashboard/:packName', requireAuth, async (req, res, next) => {
|
||||
try {
|
||||
const packKey = pickFirstValue(req.params.packName)
|
||||
const nextPackName = pickFirstValue(req.body.displayName).trim() || packKey
|
||||
const nextJsonKey = pickFirstValue(req.body.fileName).trim() || packKey
|
||||
const currentDefinition = await loadPackDefinition(packKey)
|
||||
if (currentDefinition == null) {
|
||||
throw new Error('서버팩 JSON을 찾을 수 없습니다.')
|
||||
}
|
||||
|
||||
const normalized = normalizePackDefinition({
|
||||
...currentDefinition,
|
||||
mcVersion: pickFirstValue(req.body.mcVersion),
|
||||
recommendedJdkVersion: Number(pickFirstValue(req.body.recommendedJdkVersion)),
|
||||
loaderType: pickFirstValue(req.body.loaderType) as PackDefinition['loaderType'],
|
||||
loaderVersion: pickFirstValue(req.body.loaderVersion),
|
||||
serverMinRam: Number(pickFirstValue(req.body.serverMinRam)),
|
||||
serverMaxRam: Number(pickFirstValue(req.body.serverMaxRam)),
|
||||
clientMinRam: Number(pickFirstValue(req.body.clientMinRam)),
|
||||
clientRecommendedRam: Number(pickFirstValue(req.body.clientRecommendedRam)),
|
||||
packPath: pickFirstValue(req.body.packPath),
|
||||
description: pickFirstValue(req.body.description)
|
||||
})
|
||||
|
||||
const changedKey = await updatePack(packKey, nextPackName, nextJsonKey, normalized)
|
||||
res.redirect(`/op/dashboard/${changedKey}`)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
})
|
||||
|
||||
opRouter.post('/op/dashboard/:packName/assets/loader', requireAuth, upload.single('asset'), async (req, res, next) => {
|
||||
try {
|
||||
const packKey = pickFirstValue(req.params.packName)
|
||||
if (req.file == null) {
|
||||
throw new Error('업로드된 로더 파일이 없습니다.')
|
||||
}
|
||||
|
||||
const relativePath = await saveUploadedPackAsset(packKey, 'loaders', req.file)
|
||||
await mutatePackDefinition(packKey, (pack) => {
|
||||
pack.loaderInstallerPath = relativePath
|
||||
})
|
||||
|
||||
res.redirect(`/op/dashboard/${packKey}`)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
})
|
||||
|
||||
opRouter.post('/op/dashboard/:packName/assets/resource-pack', requireAuth, upload.single('asset'), async (req, res, next) => {
|
||||
try {
|
||||
const packKey = pickFirstValue(req.params.packName)
|
||||
if (req.file == null) {
|
||||
throw new Error('업로드된 리소스팩 파일이 없습니다.')
|
||||
}
|
||||
|
||||
const relativePath = await saveUploadedPackAsset(packKey, 'resourcepacks', req.file)
|
||||
await mutatePackDefinition(packKey, (pack) => {
|
||||
pack.resourcePackFiles = [...(pack.resourcePackFiles ?? []), relativePath]
|
||||
})
|
||||
|
||||
res.redirect(`/op/dashboard/${packKey}`)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
})
|
||||
|
||||
opRouter.post('/op/dashboard/:packName/assets/shader-pack', requireAuth, upload.single('asset'), async (req, res, next) => {
|
||||
try {
|
||||
const packKey = pickFirstValue(req.params.packName)
|
||||
if (req.file == null) {
|
||||
throw new Error('업로드된 쉐이더 파일이 없습니다.')
|
||||
}
|
||||
|
||||
const relativePath = await saveUploadedPackAsset(packKey, 'shaderpacks', req.file)
|
||||
await mutatePackDefinition(packKey, (pack) => {
|
||||
pack.shaderPackFiles = [...(pack.shaderPackFiles ?? []), relativePath]
|
||||
})
|
||||
|
||||
res.redirect(`/op/dashboard/${packKey}`)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
})
|
||||
|
||||
opRouter.post('/op/dashboard/:packName/assets/loader/remove', requireAuth, async (req, res, next) => {
|
||||
try {
|
||||
const packKey = pickFirstValue(req.params.packName)
|
||||
const targetPath = pickFirstValue(req.body.assetPath)
|
||||
await removeUploadedAsset(targetPath)
|
||||
await mutatePackDefinition(packKey, (pack) => {
|
||||
if (pack.loaderInstallerPath === targetPath) {
|
||||
pack.loaderInstallerPath = ''
|
||||
}
|
||||
})
|
||||
res.redirect(`/op/dashboard/${packKey}`)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
})
|
||||
|
||||
opRouter.post('/op/dashboard/:packName/assets/resource-pack/remove', requireAuth, async (req, res, next) => {
|
||||
try {
|
||||
const packKey = pickFirstValue(req.params.packName)
|
||||
const targetPath = pickFirstValue(req.body.assetPath)
|
||||
await removeUploadedAsset(targetPath)
|
||||
await mutatePackDefinition(packKey, (pack) => {
|
||||
pack.resourcePackFiles = (pack.resourcePackFiles ?? []).filter((entry) => entry !== targetPath)
|
||||
})
|
||||
res.redirect(`/op/dashboard/${packKey}`)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
})
|
||||
|
||||
opRouter.post('/op/dashboard/:packName/assets/shader-pack/remove', requireAuth, async (req, res, next) => {
|
||||
try {
|
||||
const packKey = pickFirstValue(req.params.packName)
|
||||
const targetPath = pickFirstValue(req.body.assetPath)
|
||||
await removeUploadedAsset(targetPath)
|
||||
await mutatePackDefinition(packKey, (pack) => {
|
||||
pack.shaderPackFiles = (pack.shaderPackFiles ?? []).filter((entry) => entry !== targetPath)
|
||||
})
|
||||
res.redirect(`/op/dashboard/${packKey}`)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
})
|
||||
Reference in New Issue
Block a user