fixed API and stopping delay
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { Router } from 'express';
|
||||
import express from 'express';
|
||||
import { requireAdmin } from '../auth.js';
|
||||
import { requireAdmin, createApiKey, listApiKeys, revokeApiKey } from '../auth.js';
|
||||
import { runHealthCheck } from '../streams/checker.js';
|
||||
import { probeStream } from '../streams/probe.js';
|
||||
import { applySeedIfEmpty } from '../sources/seed.js';
|
||||
@@ -19,6 +19,25 @@ import { broadcastGlobal } from '../ws.js';
|
||||
export const router = Router();
|
||||
router.use(requireAdmin);
|
||||
|
||||
// --- API key management ---
|
||||
|
||||
router.get('/api-keys', (_req, res) => {
|
||||
res.json(listApiKeys());
|
||||
});
|
||||
|
||||
router.post('/api-keys', (req, res) => {
|
||||
const label = String(req.body?.label || '').trim();
|
||||
const userId = Number(req.body?.userId) || req.user.id;
|
||||
const key = createApiKey(userId, label);
|
||||
res.status(201).json({ key }); // plaintext key shown exactly once
|
||||
});
|
||||
|
||||
router.delete('/api-keys/:id', (req, res) => {
|
||||
revokeApiKey(Number(req.params.id));
|
||||
res.json({ ok: true });
|
||||
});
|
||||
|
||||
|
||||
// Raw body parser used only by the image upload route. The global JSON
|
||||
// parser is mounted before us so we have to opt-out for `image/*`.
|
||||
const rawImageBody = express.raw({ type: ['image/*', 'application/octet-stream'], limit: '5mb' });
|
||||
|
||||
@@ -117,7 +117,7 @@ router.get('/:id/proxy', requireUser, async (req, res) => {
|
||||
if (resolved.format === 'hls') return res.status(415).json({ error: 'hls not proxied' });
|
||||
|
||||
const controller = new AbortController();
|
||||
req.on('close', () => controller.abort());
|
||||
req.on('close', () => { try { controller.abort(); } catch { } });
|
||||
|
||||
let upstream;
|
||||
try {
|
||||
@@ -127,7 +127,9 @@ router.get('/:id/proxy', requireUser, async (req, res) => {
|
||||
headers: { 'User-Agent': 'oradio-kiosk/1.0', 'Icy-MetaData': '0' }
|
||||
});
|
||||
} catch (err) {
|
||||
return res.status(502).json({ error: `upstream: ${err.message || err}` });
|
||||
if (err.name === 'AbortError') { res.end(); return; }
|
||||
if (!res.headersSent) res.status(502).json({ error: `upstream: ${err.message || err}` });
|
||||
return;
|
||||
}
|
||||
if (!upstream.ok || !upstream.body) {
|
||||
return res.status(502).json({ error: `upstream HTTP ${upstream.status}` });
|
||||
@@ -141,7 +143,11 @@ router.get('/:id/proxy', requireUser, async (req, res) => {
|
||||
res.set('Access-Control-Expose-Headers', 'Content-Type');
|
||||
|
||||
// Pipe the WHATWG ReadableStream into the Express response.
|
||||
// We cancel the reader directly on client-close — equivalent to aborting
|
||||
// the fetch but without the AbortController rejection that escapes the
|
||||
// async route in older Node/Electron versions.
|
||||
const reader = upstream.body.getReader();
|
||||
req.on('close', () => { reader.cancel().catch(() => {}); });
|
||||
const pump = async () => {
|
||||
try {
|
||||
while (true) {
|
||||
@@ -151,13 +157,13 @@ router.get('/:id/proxy', requireUser, async (req, res) => {
|
||||
await new Promise((r) => res.once('drain', r));
|
||||
}
|
||||
}
|
||||
} catch { /* client disconnect or upstream abort */ }
|
||||
} catch { /* client disconnect or upstream error */ }
|
||||
finally {
|
||||
try { reader.cancel(); } catch { }
|
||||
res.end();
|
||||
try { res.end(); } catch { }
|
||||
}
|
||||
};
|
||||
pump();
|
||||
pump().catch(() => {});
|
||||
});
|
||||
|
||||
function guessContentType(format) {
|
||||
|
||||
Reference in New Issue
Block a user