Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 05dc9d7166 |
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "minecraft-music-quiz-installer",
|
||||
"version": "0.3.2",
|
||||
"version": "0.3.3",
|
||||
"description": "마인크래프트 음악퀴즈 간편설치기 + 관리 사이트",
|
||||
"main": "dist/installer/main.js",
|
||||
"scripts": {
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
manifestRootPath, manifestDirPath, manifestTermsDirPath,
|
||||
fileDirPath, viewsDirPath, publicDirPath
|
||||
} from '../shared/paths.js'
|
||||
import { isPublicTermsFile } from '../shared/store.js'
|
||||
import { ensurePackTermsDir, isPublicTermsFile, loadPackDefinition } from '../shared/store.js'
|
||||
import { loadEnv } from '../shared/env.js'
|
||||
import { t, localeDict } from './i18n.js'
|
||||
import { indexRouter } from './routes/index.js'
|
||||
@@ -66,17 +66,32 @@ app.get('/manifest.json', (_req, res) => {
|
||||
// 설치기 + 사이트가 약관(markdown) 을 가져갈 수 있도록 .md 만 허용한다.
|
||||
// 음악퀴즈(pack) 별로 manifest/terms/<packKey>/<file>.md 에서 노출한다.
|
||||
// _meta.json 같은 시스템 파일이나 경로 탈출은 isPublicTermsFile 에서 차단.
|
||||
app.get('/manifest/terms/:packKey/:fileName', (req, res) => {
|
||||
//
|
||||
// fresh 배포에서 관리자가 약관 페이지를 한 번도 열지 않은 상태로 설치기가 약관을
|
||||
// 요청하는 경우에도 작동하도록, 실제 pack 이면 ensurePackTermsDir 로 v0.3.1
|
||||
// 전역 .md 들을 시드 복사한 뒤 sendFile 한다. 임의 packKey 로 빈 폴더가
|
||||
// 생성되는 것은 loadPackDefinition 으로 차단.
|
||||
app.get('/manifest/terms/:packKey/:fileName', async (req, res, next) => {
|
||||
try {
|
||||
const { packKey, fileName } = req.params
|
||||
if (!isPublicTermsFile(packKey, fileName)) {
|
||||
res.status(404).send('Not Found')
|
||||
return
|
||||
}
|
||||
const pack = await loadPackDefinition(packKey)
|
||||
if (!pack) {
|
||||
res.status(404).send('Not Found')
|
||||
return
|
||||
}
|
||||
await ensurePackTermsDir(packKey)
|
||||
res.type('text/markdown; charset=utf-8')
|
||||
res.sendFile(path.join(manifestTermsDirPath, packKey, fileName), (err) => {
|
||||
if (!err || res.headersSent) return
|
||||
res.status(404).send('Not Found')
|
||||
})
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
})
|
||||
|
||||
// 설치기에서 개별 음악퀴즈 JSON을 가져갈 수 있도록 파일 단위로만 허용.
|
||||
|
||||
@@ -178,6 +178,14 @@ export async function deletePackKeys(keys: string[]): Promise<void> {
|
||||
} catch (error) {
|
||||
if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error
|
||||
}
|
||||
// pack 이 삭제되면 약관 폴더도 함께 정리한다. 동일 packKey 로 재생성될 때
|
||||
// 옛 약관이 부활하는 것을 막기 위함.
|
||||
const termsDir = path.join(manifestTermsDirPath, key)
|
||||
try {
|
||||
await fsp.rm(termsDir, { recursive: true, force: true })
|
||||
} catch (error) {
|
||||
if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error
|
||||
}
|
||||
await syncManifestWith(key, '', 'remove')
|
||||
}
|
||||
}
|
||||
@@ -198,6 +206,19 @@ export async function renamePack(oldKey: string, newKey: string, pack: PackDefin
|
||||
} catch (error) {
|
||||
if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error
|
||||
}
|
||||
// 약관 폴더도 함께 이름을 바꾼다 (있는 경우만). pack 이름이 바뀌었는데 약관이
|
||||
// 옛 폴더에 남아 있으면 인스톨러가 새 packKey 로 약관을 받지 못한다.
|
||||
const oldTermsDir = path.join(manifestTermsDirPath, oldKey)
|
||||
const newTermsDir = path.join(manifestTermsDirPath, safeNew)
|
||||
try {
|
||||
await fsp.rename(oldTermsDir, newTermsDir)
|
||||
} catch (error) {
|
||||
const code = (error as NodeJS.ErrnoException).code
|
||||
// 옛 약관 폴더가 없으면 그대로 둔다. 새 폴더가 이미 있어 충돌하면 그것도 그냥 둔다
|
||||
// (renamePack 단계에서 사용자에게 보낼 마땅한 UX 가 없고, 다음 약관 접근 때
|
||||
// 새 폴더 내용이 정상적으로 사용된다).
|
||||
if (code !== 'ENOENT' && code !== 'ENOTEMPTY' && code !== 'EEXIST') throw error
|
||||
}
|
||||
await syncManifestWith(oldKey, '', 'remove')
|
||||
}
|
||||
await syncManifestWith(safeNew, pack.name, 'upsert')
|
||||
@@ -347,8 +368,12 @@ function isValidPackKey(packKey: string): boolean {
|
||||
* 해당 pack 폴더가 없으면 만든다. 이전 버전(v0.3.1) 의 전역 `manifest/terms/*.md`
|
||||
* 파일이 남아 있는 경우 첫 접근 시 그 내용을 그대로 새 폴더에 복사해 시드한다.
|
||||
* 시드는 한 번만 발생: 폴더가 이미 있으면 아무것도 안 한다.
|
||||
*
|
||||
* 공개 라우트(`/manifest/terms/<packKey>/<file>`) 에서도 호출되므로 export 한다.
|
||||
* 라우트 측은 packKey 가 실제 존재하는 pack 인지 확인한 다음에 호출해야 한다
|
||||
* (그렇지 않으면 임의 키로 빈 폴더가 생성될 수 있다).
|
||||
*/
|
||||
async function ensurePackTermsDir(packKey: string): Promise<string> {
|
||||
export async function ensurePackTermsDir(packKey: string): Promise<string> {
|
||||
const dir = termsDirForPack(packKey)
|
||||
try {
|
||||
await fsp.access(dir)
|
||||
|
||||
Reference in New Issue
Block a user