fix(server): 彻底放弃 :path(*) 等参数化通配路由,改用 app.use + req.path 手动截取,解决 path-to-regexp v8.3 启动崩溃
服务器环境 path-to-regexp 实际版本是 8.3.0(Express 5.2.1 内置),
该版本既不接受裸 '*'、也不接受 ':name(.*)' 自定义正则、也不接受
':name(*)' 单段通配修饰符,参数路由只要带修饰符就会在 new Layer
阶段直接抛 PathError。
解决方案(这次本地用真实 node_modules 实装 require 整份 server.js
模拟启动,已确认不崩溃,http.createServer 正常被调用 3 次):
- 7 条 /media/* 路由全部删除 app.get('/prefix/:path(*)', ...)
- 改为 app.use('/prefix', middleware) + 在中间件里用 req.path
手动去掉前置 '/' 得到相对路径,再 decodeURIComponent();只处理
GET 请求,其他方法 next()
- static 命中优先(fallthrough),未命中时继续调用原
sendBinaryFile / sendBinaryFileFromCandidates 兜底,保持
Content-Type / Range / Accept-Ranges / Content-Length 等头
与旧行为完全一致
- 涉及:general / beep / music / voice / line / station / temp
客户端 bump v1.0.103 -> v1.0.104
This commit is contained in:
+49
-35
@@ -149,11 +149,16 @@ function safeRelativeToBase(baseDir, relativePath) {
|
||||
}
|
||||
|
||||
app.use('/media/audio/general', (req, res, next) => {
|
||||
if (req.method && String(req.method).toUpperCase() !== 'GET') return next();
|
||||
const dirs = getBeepAudioDirs();
|
||||
if (!dirs || dirs.length === 0) return next();
|
||||
const rel = decodeURIComponent(String(req.path || '/').replace(/^\/+/, ''));
|
||||
let i = 0;
|
||||
const tryNext = () => {
|
||||
if (i >= dirs.length) return next();
|
||||
if (i >= dirs.length) {
|
||||
const candidates = (dirs || []).map(dir => safeRelativeToBase(dir, rel)).filter(Boolean);
|
||||
if (candidates && candidates.length) return sendBinaryFileFromCandidates(res, candidates);
|
||||
return next();
|
||||
}
|
||||
const dir = String(dirs[i++] || '');
|
||||
if (!dir) return tryNext();
|
||||
express.static(dir, { fallthrough: true })(req, res, tryNext);
|
||||
@@ -161,63 +166,72 @@ app.use('/media/audio/general', (req, res, next) => {
|
||||
tryNext();
|
||||
});
|
||||
app.use('/media/audio/beep', (req, res, next) => {
|
||||
if (req.method && String(req.method).toUpperCase() !== 'GET') return next();
|
||||
const dirs = getBeepAudioDirs();
|
||||
if (!dirs || dirs.length === 0) return next();
|
||||
const rel = decodeURIComponent(String(req.path || '/').replace(/^\/+/, ''));
|
||||
let i = 0;
|
||||
const tryNext = () => {
|
||||
if (i >= dirs.length) return next();
|
||||
if (i >= dirs.length) {
|
||||
const candidates = (dirs || []).map(dir => safeRelativeToBase(dir, rel)).filter(Boolean);
|
||||
if (candidates && candidates.length) return sendBinaryFileFromCandidates(res, candidates);
|
||||
return next();
|
||||
}
|
||||
const dir = String(dirs[i++] || '');
|
||||
if (!dir) return tryNext();
|
||||
express.static(dir, { fallthrough: true })(req, res, tryNext);
|
||||
};
|
||||
tryNext();
|
||||
});
|
||||
app.use('/media/audio/music', express.static(String(audioMusicDir || ''), { fallthrough: true }));
|
||||
app.use('/media/audio/voice', express.static(String(audioVoiceDir || ''), { fallthrough: true }));
|
||||
app.use('/media/audio/line', express.static(String(audioLineDir || ''), { fallthrough: true }));
|
||||
app.use('/media/audio/station', express.static(String(audioStationDir || ''), { fallthrough: true }));
|
||||
app.use('/media/temp', express.static(String(tempBroadcastDir || ''), { fallthrough: true }));
|
||||
|
||||
app.get('/media/audio/general/:path(*)', (req, res) => {
|
||||
const rel = decodeURIComponent(req.params.path || '');
|
||||
const candidates = getBeepAudioDirs().map(dir => safeRelativeToBase(dir, rel)).filter(Boolean);
|
||||
sendBinaryFileFromCandidates(res, candidates);
|
||||
});
|
||||
app.get('/media/audio/beep/:path(*)', (req, res) => {
|
||||
const rel = decodeURIComponent(req.params.path || '');
|
||||
const candidates = getBeepAudioDirs().map(dir => safeRelativeToBase(dir, rel)).filter(Boolean);
|
||||
sendBinaryFileFromCandidates(res, candidates);
|
||||
});
|
||||
app.get('/media/audio/music/:path(*)', (req, res) => {
|
||||
const rel = decodeURIComponent(req.params.path || '');
|
||||
const full = safeRelativeToBase(audioMusicDir, rel);
|
||||
app.use('/media/audio/music', (req, res, next) => {
|
||||
if (req.method && String(req.method).toUpperCase() !== 'GET') return next();
|
||||
const rel = decodeURIComponent(String(req.path || '/').replace(/^\/+/, ''));
|
||||
const base = String(audioMusicDir || '');
|
||||
express.static(base, { fallthrough: true })(req, res, () => {
|
||||
const full = base ? safeRelativeToBase(base, rel) : null;
|
||||
if (!full) return res.status(404).end();
|
||||
sendBinaryFile(res, full);
|
||||
});
|
||||
app.get('/media/audio/voice/:path(*)', (req, res) => {
|
||||
const rel = decodeURIComponent(req.params.path || '');
|
||||
const full = safeRelativeToBase(audioVoiceDir, rel);
|
||||
});
|
||||
app.use('/media/audio/voice', (req, res, next) => {
|
||||
if (req.method && String(req.method).toUpperCase() !== 'GET') return next();
|
||||
const rel = decodeURIComponent(String(req.path || '/').replace(/^\/+/, ''));
|
||||
const base = String(audioVoiceDir || '');
|
||||
express.static(base, { fallthrough: true })(req, res, () => {
|
||||
const full = base ? safeRelativeToBase(base, rel) : null;
|
||||
if (!full) return res.status(404).end();
|
||||
sendBinaryFile(res, full);
|
||||
});
|
||||
app.get('/media/audio/line/:path(*)', (req, res) => {
|
||||
const rel = decodeURIComponent(req.params.path || '');
|
||||
const full = safeRelativeToBase(audioLineDir, rel);
|
||||
});
|
||||
app.use('/media/audio/line', (req, res, next) => {
|
||||
if (req.method && String(req.method).toUpperCase() !== 'GET') return next();
|
||||
const rel = decodeURIComponent(String(req.path || '/').replace(/^\/+/, ''));
|
||||
const base = String(audioLineDir || '');
|
||||
express.static(base, { fallthrough: true })(req, res, () => {
|
||||
const full = base ? safeRelativeToBase(base, rel) : null;
|
||||
if (!full) return res.status(404).end();
|
||||
sendBinaryFile(res, full);
|
||||
});
|
||||
app.get('/media/audio/station/:path(*)', (req, res) => {
|
||||
const rel = decodeURIComponent(req.params.path || '');
|
||||
const full = safeRelativeToBase(audioStationDir, rel);
|
||||
});
|
||||
app.use('/media/audio/station', (req, res, next) => {
|
||||
if (req.method && String(req.method).toUpperCase() !== 'GET') return next();
|
||||
const rel = decodeURIComponent(String(req.path || '/').replace(/^\/+/, ''));
|
||||
const base = String(audioStationDir || '');
|
||||
express.static(base, { fallthrough: true })(req, res, () => {
|
||||
const full = base ? safeRelativeToBase(base, rel) : null;
|
||||
if (!full) return res.status(404).end();
|
||||
sendBinaryFile(res, full);
|
||||
});
|
||||
app.get('/media/temp/:path(*)', (req, res) => {
|
||||
const rel = decodeURIComponent(req.params.path || '');
|
||||
const full = safeRelativeToBase(tempBroadcastDir, rel);
|
||||
});
|
||||
app.use('/media/temp', (req, res, next) => {
|
||||
if (req.method && String(req.method).toUpperCase() !== 'GET') return next();
|
||||
const rel = decodeURIComponent(String(req.path || '/').replace(/^\/+/, ''));
|
||||
const base = String(tempBroadcastDir || '');
|
||||
express.static(base, { fallthrough: true })(req, res, () => {
|
||||
const full = base ? safeRelativeToBase(base, rel) : null;
|
||||
if (!full) return res.status(404).end();
|
||||
sendBinaryFile(res, full);
|
||||
});
|
||||
});
|
||||
|
||||
app.get('/media', (req, res) => {
|
||||
res.json({
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
|
||||
-- 1. Configuration & Constants
|
||||
local APP_VERSION_TEXT = "v1.0.103"
|
||||
local APP_VERSION_TEXT = "v1.0.104"
|
||||
|
||||
local CONFIG = {
|
||||
CLOUD_URL = "ws://stabadmin.fse-media.group",
|
||||
|
||||
Reference in New Issue
Block a user