Files
MoviePilot-Frontend/public/service.js
2025-06-29 03:06:31 +08:00

112 lines
3.3 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.
const path = require('node:path')
const express = require('express')
const proxy = require('express-http-proxy')
const app = express()
const port = process.env.NGINX_PORT || 3000
// 后端 API 地址
const proxyConfig = {
URL: '127.0.0.1',
PORT: process.env.PORT || 3001
}
// 静态文件服务目录
app.use(express.static(__dirname))
// 配置代理中间件将请求转发给后端API
app.use(
'/api',
proxy(`${proxyConfig.URL}:${proxyConfig.PORT}`, {
// 路径加上 /api 前缀
proxyReqPathResolver: (req) => {
return `/api${req.url}`
},
proxyReqOptDecorator: (proxyReqOpts, srcReq) => {
proxyReqOpts.headers = proxyReqOpts.headers || {};
// 检测是否为SSE请求
const isSSE = srcReq.headers.accept && srcReq.headers.accept.includes('text/event-stream');
// 动态设置超时时间SSE无超时普通请求600秒超时
proxyReqOpts.timeout = isSSE ? 0 : 600000;
if (isSSE) {
// SSE请求的特殊头部设置
proxyReqOpts.headers['Cache-Control'] = 'no-cache';
proxyReqOpts.headers['Connection'] = 'keep-alive';
proxyReqOpts.headers['Accept'] = 'text/event-stream';
}
return proxyReqOpts;
},
userResDecorator: (proxyRes, proxyResData, userReq, userRes) => {
// 检测响应是否为SSE类型
const isSSEResponse = proxyRes.headers['content-type'] &&
proxyRes.headers['content-type'].includes('text/event-stream');
if (isSSEResponse) {
// SSE响应设置流式传输头部并禁用缓冲
userRes.writeHead(proxyRes.statusCode, {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': 'Cache-Control'
});
return false; // 禁用默认响应处理,让数据直接流向客户端
} else {
// 普通响应:正常处理
return proxyResData;
}
},
// 统一错误处理(添加超时错误处理)
proxyErrorHandler: (err, res, next) => {
// 客户端断开连接的正常情况常见于SSE
if (err.code === 'ECONNRESET' || err.code === 'EPIPE') {
console.log('Client disconnected:', err.code);
res.end(); // 优雅结束响应
return;
}
// 添加超时错误处理
if (err.code === 'ETIMEDOUT') {
console.log('Proxy request timed out:', err.code);
if (!res.headersSent) {
res.status(504).send('Gateway Timeout');
}
return;
}
// 其他错误正常处理
console.error('Proxy error:', err);
next(err);
}
})
);
// 配置代理中间件将CookieCloud请求转发给后端API
app.use(
'/cookiecloud',
proxy(`${proxyConfig.URL}:${proxyConfig.PORT}`, {
// 路径加上 /cookiecloud 前缀
proxyReqPathResolver: (req) => {
return `/cookiecloud${req.url}`
}
})
);
// 处理根路径的请求
app.get('/', (req, res) => {
res.sendFile(path.join(__dirname, 'index.html'))
})
// 处理所有其他请求,重定向到前端入口文件
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname, 'index.html'))
})
app.listen(port, () => {
console.log(`Server is running on port ${port}`)
})