YD_TrainingCompetition_Api_Web/scripts/generate-static-manifest.mjs
2026-06-10 09:42:57 +08:00

124 lines
3.5 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 扫描 public/static 下各赛事文件夹,生成 manifest.json
* 约定子目录:背景、口令、赛事LOGO、秩序册
*/
import fs from 'fs'
import path from 'path'
import { fileURLToPath } from 'url'
const __dirname = path.dirname(fileURLToPath(import.meta.url))
const ROOT = path.join(__dirname, '..', 'public', 'static')
/** 优先使用新目录名,兼容旧名称 */
const SUBDIR_CANDIDATES = {
backgrounds: ['背景', '背景图'],
passwords: ['口令'],
logos: ['赛事LOGO', '赛事logo'],
rosters: ['秩序册'],
}
function resolveSubdir(folderPath, candidates) {
for (const name of candidates) {
if (fs.existsSync(path.join(folderPath, name))) return name
}
return candidates[0]
}
const IMAGE_EXT = new Set(['.jpg', '.jpeg', '.png', '.webp', '.gif', '.bmp', '.svg'])
const ROSTER_EXT = new Set(['.txt', '.csv', '.xlsx', '.xls'])
const PASSWORD_EXT = new Set(['.mp3'])
const LOGO_EXT = IMAGE_EXT
function listFiles(dir, allowedExt) {
if (!fs.existsSync(dir)) return []
return fs
.readdirSync(dir, { withFileTypes: true })
.filter(
(e) =>
e.isFile() &&
!e.name.startsWith('~$') &&
allowedExt.has(path.extname(e.name).toLowerCase()),
)
.map((e) => e.name)
.sort((a, b) => a.localeCompare(b, 'zh-CN'))
}
/** 使用原始路径;由前端 resolveStaticUrl 统一编码 */
function toPublicPath(folder, sub, file) {
return `/static/${folder}/${sub}/${file}`
}
function readEventMeta(folderPath) {
const metaPath = path.join(folderPath, 'event.json')
if (!fs.existsSync(metaPath)) return {}
try {
return JSON.parse(fs.readFileSync(metaPath, 'utf-8'))
} catch {
return {}
}
}
function scan() {
if (!fs.existsSync(ROOT)) {
fs.mkdirSync(ROOT, { recursive: true })
return { generatedAt: new Date().toISOString(), events: [] }
}
const events = []
for (const entry of fs.readdirSync(ROOT, { withFileTypes: true })) {
if (!entry.isDirectory()) continue
if (entry.name.startsWith('.')) continue
const folder = entry.name
const folderPath = path.join(ROOT, folder)
const meta = readEventMeta(folderPath)
const bgDir = resolveSubdir(folderPath, SUBDIR_CANDIDATES.backgrounds)
const pwdDir = resolveSubdir(folderPath, SUBDIR_CANDIDATES.passwords)
const logoDir = resolveSubdir(folderPath, SUBDIR_CANDIDATES.logos)
const rosterDir = resolveSubdir(folderPath, SUBDIR_CANDIDATES.rosters)
const backgrounds = listFiles(path.join(folderPath, bgDir), IMAGE_EXT).map((file) => ({
file,
path: toPublicPath(folder, bgDir, file),
}))
const passwords = listFiles(path.join(folderPath, pwdDir), PASSWORD_EXT).map((file) => ({
file,
path: toPublicPath(folder, pwdDir, file),
}))
const logos = listFiles(path.join(folderPath, logoDir), LOGO_EXT).map((file) => ({
file,
path: toPublicPath(folder, logoDir, file),
}))
const rosters = listFiles(path.join(folderPath, rosterDir), ROSTER_EXT).map((file) => ({
file,
path: toPublicPath(folder, rosterDir, file),
}))
events.push({
id: folder,
name: meta.displayName || folder,
folder,
backgrounds,
passwords,
logos,
rosters,
})
}
events.sort((a, b) => a.name.localeCompare(b.name, 'zh-CN'))
return {
generatedAt: new Date().toISOString(),
events,
}
}
const manifest = scan()
const outPath = path.join(ROOT, 'manifest.json')
fs.writeFileSync(outPath, JSON.stringify(manifest, null, 2), 'utf-8')
console.log(`[static-manifest] ${manifest.events.length} 个赛事 -> ${outPath}`)