Make recording guidelines editable from admin panel; v2.0.18
Publish Container / docker (push) Successful in 14m23s
Publish Container / docker (push) Successful in 14m23s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
ef1d9fd8fc
commit
86de45dbc3
5 files changed
+157
-8
No files matched your search
@@ -16,6 +16,7 @@ import {
|
||||
NUGGETS_UPLOAD_HOSTS_FILE,
|
||||
NUGGETS_UPLOAD_HISTORY_FILE,
|
||||
NUGGETS_PORTAL_ANNOUNCEMENT_FILE,
|
||||
NUGGETS_PORTAL_GUIDELINES_FILE,
|
||||
NUGGETS_NOTIFY_EMAIL,
|
||||
SEAFILE_URL,
|
||||
SEAFILE_TOKEN,
|
||||
@@ -139,6 +140,30 @@ async function saveAnnouncement(text) {
|
||||
return data
|
||||
}
|
||||
|
||||
// ── Guidelines helpers ────────────────────────────────────────────────────
|
||||
const DEFAULT_GUIDELINES = [
|
||||
'Export as MP3 or WAV at 44.1 kHz, 128 kbps minimum.',
|
||||
'File name: YYYY-MM-DD-EpisodeName.mp3 (e.g. 2026-09-09-Romans-5.mp3).',
|
||||
'Recommended length: 8–15 minutes. Keep a 0.5s lead-in and 1s fade-out.',
|
||||
'Normalize to -1 dBFS before exporting.',
|
||||
'Match the episode name here to the file name exactly so search stays clean.',
|
||||
]
|
||||
|
||||
async function loadGuidelines() {
|
||||
try {
|
||||
const raw = await readFile(NUGGETS_PORTAL_GUIDELINES_FILE, 'utf8')
|
||||
return JSON.parse(raw)
|
||||
} catch {
|
||||
return { lines: DEFAULT_GUIDELINES, updatedAt: null }
|
||||
}
|
||||
}
|
||||
|
||||
async function saveGuidelines(lines) {
|
||||
const data = { lines, updatedAt: new Date().toISOString() }
|
||||
await writeFile(NUGGETS_PORTAL_GUIDELINES_FILE, JSON.stringify(data, null, 2), 'utf8')
|
||||
return data
|
||||
}
|
||||
|
||||
// ── Multer (disk storage, temp dir) ──────────────────────────────────────
|
||||
const UPLOAD_TEMP_DIR = path.join(DATA_DIR, 'upload-tmp')
|
||||
|
||||
@@ -475,6 +500,13 @@ export function register(app) {
|
||||
res.json(data)
|
||||
})
|
||||
|
||||
// ── Public: recording guidelines ─────────────────────────────────────
|
||||
|
||||
app.get('/api/nuggets-upload/guidelines', async (_req, res) => {
|
||||
const data = await loadGuidelines()
|
||||
res.json(data)
|
||||
})
|
||||
|
||||
// ── Admin: manage upload host accounts ───────────────────────────────
|
||||
|
||||
app.get('/api/admin/nuggets-upload/hosts', requireAdminAuth, async (_req, res) => {
|
||||
@@ -597,6 +629,24 @@ export function register(app) {
|
||||
res.json({ ok: true, ...data })
|
||||
})
|
||||
|
||||
// ── Admin: recording guidelines ───────────────────────────────────────
|
||||
|
||||
app.post('/api/admin/nuggets-upload/guidelines', requireAdminAuth, async (req, res) => {
|
||||
const raw = typeof req.body?.lines === 'string' ? req.body.lines : ''
|
||||
const lines = raw.split('\n').map(l => l.trim()).filter(Boolean)
|
||||
if (lines.length > 20) {
|
||||
res.status(400).json({ message: 'Maximum 20 guideline lines.' })
|
||||
return
|
||||
}
|
||||
const data = await saveGuidelines(lines)
|
||||
res.json({ ok: true, ...data })
|
||||
})
|
||||
|
||||
app.delete('/api/admin/nuggets-upload/guidelines', requireAdminAuth, async (_req, res) => {
|
||||
const data = await saveGuidelines(DEFAULT_GUIDELINES)
|
||||
res.json({ ok: true, ...data })
|
||||
})
|
||||
|
||||
// ── Admin: upload history ─────────────────────────────────────────────
|
||||
|
||||
app.get('/api/admin/nuggets-upload/history', requireAdminAuth, async (_req, res) => {
|
||||
|
||||
Reference in new issue
Block a user