Files
QZMusic-Web/server.cjs

120 lines
4.1 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.
/**
* QZMusic Web 静态文件服务器(生产用)
* 监听 0.0.0.0:1219支持公网访问
*/
const http = require('http');
const fs = require('fs');
const path = require('path');
const url = require('url');
const PORT = parseInt(process.env.QZMUSIC_PORT || '1219', 10);
const HOST = process.env.QZMUSIC_HOST || '0.0.0.0';
const ROOT = path.join(__dirname, 'dist');
const mimeTypes = {
'.html': 'text/html; charset=utf-8',
'.htm': 'text/html; charset=utf-8',
'.js': 'application/javascript; charset=utf-8',
'.mjs': 'application/javascript; charset=utf-8',
'.css': 'text/css; charset=utf-8',
'.json': 'application/json; charset=utf-8',
'.png': 'image/png',
'.jpg': 'image/jpeg',
'.jpeg': 'image/jpeg',
'.gif': 'image/gif',
'.webp': 'image/webp',
'.svg': 'image/svg+xml',
'.ico': 'image/x-icon',
'.ttf': 'font/ttf',
'.woff': 'font/woff',
'.woff2':'font/woff2',
'.map': 'application/json; charset=utf-8',
'.txt': 'text/plain; charset=utf-8'
};
function log(...args) {
const ts = new Date().toISOString().replace('T', ' ').substring(0, 19);
console.log(`[${ts}]`, ...args);
}
const server = http.createServer((req, res) => {
const parsed = url.parse(req.url);
let pathname = decodeURIComponent(parsed.pathname || '/');
if (pathname === '/') pathname = '/index.html';
let filePath = path.join(ROOT, pathname);
// 防止路径穿越
if (!filePath.startsWith(ROOT)) {
res.writeHead(403);
res.end('Forbidden');
return;
}
const extname = String(path.extname(filePath)).toLowerCase();
const contentType = mimeTypes[extname] || 'application/octet-stream';
// SPA 路由:如果不是带扩展名的文件且文件不存在,则回退到 index.html
const tryStatic = (cb) => {
fs.stat(filePath, (err, stat) => {
if (!err && stat.isDirectory()) {
filePath = path.join(filePath, 'index.html');
return fs.stat(filePath, (e2, s2) => {
if (!e2 && s2.isFile()) return cb(true);
return cb(false);
});
}
if (!err && stat.isFile()) return cb(true);
return cb(false);
});
};
tryStatic((found) => {
if (!found) {
filePath = path.join(ROOT, 'index.html');
}
fs.readFile(filePath, (error, content) => {
if (error) {
log(`${req.method} ${req.url} -> 500 (${error.code})`);
res.writeHead(500, { 'Content-Type': 'text/plain; charset=utf-8' });
res.end('Server Error: ' + error.code);
} else {
const finalType = found ? contentType : 'text/html; charset=utf-8';
res.writeHead(200, {
'Content-Type': finalType,
'Cache-Control': extname === '.html' || extname === '.htm'
? 'no-cache, no-store, must-revalidate'
: 'public, max-age=3600'
});
res.end(content);
log(`${req.method} ${req.url} -> 200 (${pathname})`);
}
});
});
});
server.listen(PORT, HOST, () => {
const banner = `
╔══════════════════════════════════════════════════════════════╗
║ ║
║ QZMusic Web Server 启动成功! ║
║ ║
║ 监听地址: http://${HOST}:${PORT}
║ 本地访问: http://localhost:${PORT}
║ 公网访问: http://[你的公网IP]:${PORT}
║ ║
╚══════════════════════════════════════════════════════════════╝
`;
console.log(banner);
});
process.on('SIGINT', () => {
log('收到 SIGINT正在关闭服务器...');
server.close(() => process.exit(0));
});
process.on('SIGTERM', () => {
log('收到 SIGTERM正在关闭服务器...');
server.close(() => process.exit(0));
});