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:
2026-05-09 20:41:19 +09:00
parent 9d55819e30
commit cd79378f3c
33 changed files with 0 additions and 8451 deletions

View File

@@ -1,58 +0,0 @@
import express from 'express'
import session from 'express-session'
import path from 'node:path'
import { fileDir, manifestDir, publicDir, viewsDir } from '../shared/paths'
import { ensureProjectFiles } from '../shared/store'
import { indexRouter } from './routes/index'
import { opRouter } from './routes/op'
export async function createApp() {
await ensureProjectFiles()
const app = express()
app.set('view engine', 'ejs')
app.set('views', viewsDir)
app.use(express.urlencoded({ extended: true }))
app.use(express.json())
app.use(
session({
secret: 'mc-custom-suite-session-secret',
resave: false,
saveUninitialized: false,
cookie: {
httpOnly: true,
sameSite: 'lax'
}
})
)
app.use('/static', express.static(publicDir))
app.use('/manifest', express.static(manifestDir))
app.use('/file', express.static(fileDir))
app.use(indexRouter)
app.use(opRouter)
app.use((error: unknown, _req: express.Request, res: express.Response, _next: express.NextFunction) => {
console.error(error)
res.status(500).send('서버 내부 오류가 발생했습니다.')
})
return app
}
async function bootstrap() {
const app = await createApp()
const port = Number(process.env.PORT ?? 3000)
app.listen(port, '127.0.0.1', () => {
console.log(`Management site listening on http://127.0.0.1:${port}`)
})
}
if (require.main === module) {
bootstrap().catch((error) => {
console.error(error)
process.exit(1)
})
}

View File

@@ -1,15 +0,0 @@
import { NextFunction, Request, Response } from 'express'
declare module 'express-session' {
interface SessionData {
userId?: string
}
}
export function requireAuth(req: Request, res: Response, next: NextFunction): void {
if (req.session.userId == null) {
res.redirect('/op')
return
}
next()
}

View File

@@ -1,31 +0,0 @@
import { Router } from 'express'
import path from 'node:path'
import { manifestRootPath } from '../../shared/paths'
import { fetchReleaseVersions } from '../../shared/mojang'
import { loadRootManifest } from '../../shared/store'
export const indexRouter = Router()
indexRouter.get('/', async (_req, res, next) => {
try {
const manifest = await loadRootManifest()
res.render('index', {
packs: manifest.packs
})
} catch (error) {
next(error)
}
})
indexRouter.get('/manifest.json', (_req, res) => {
res.sendFile(path.resolve(manifestRootPath))
})
indexRouter.get('/api/releases', async (_req, res, next) => {
try {
const releases = await fetchReleaseVersions()
res.json(releases)
} catch (error) {
next(error)
}
})

View File

@@ -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)
}
})