修复安全与备份问题
This commit is contained in:
@@ -14,6 +14,8 @@ const ioRouter = require('./routes/io');
|
||||
|
||||
const app = express();
|
||||
|
||||
app.set('trust proxy', config.trustProxy);
|
||||
|
||||
// 请求日志中间件
|
||||
app.use(logger.requestLogger);
|
||||
|
||||
|
||||
+2
-1
@@ -11,7 +11,8 @@ if (isProduction && !jwtSecret) {
|
||||
module.exports = {
|
||||
// 服务器配置
|
||||
port: process.env.PORT || 3000,
|
||||
host: process.env.HOST || '0.0.0.0',
|
||||
host: process.env.HOST || (isProduction ? '127.0.0.1' : '0.0.0.0'),
|
||||
trustProxy: process.env.TRUST_PROXY || (isProduction ? 'loopback' : false),
|
||||
|
||||
// JWT 配置
|
||||
jwtSecret: jwtSecret || 'dev-only-gamer-order-secret',
|
||||
|
||||
+18
-9
@@ -21,6 +21,23 @@ function nullableDbValue(value) {
|
||||
return value === undefined || value === null || value === '' ? null : value;
|
||||
}
|
||||
|
||||
const csvTextFields = new Set(['type', 'category', 'boss', 'partner', 'source', 'destination', 'note', 'created_at']);
|
||||
|
||||
function escapeCsvFormula(value) {
|
||||
return /^[\t\r=+\-@]/.test(value) || /^[ \t]+[=+\-@]/.test(value)
|
||||
? `'${value}`
|
||||
: value;
|
||||
}
|
||||
|
||||
function encodeCsvCell(value, col) {
|
||||
if (value === null || value === undefined) return '';
|
||||
const str = csvTextFields.has(col) ? escapeCsvFormula(String(value)) : String(value);
|
||||
if (str.includes(',') || str.includes('"') || str.includes('\n') || str.includes('\r')) {
|
||||
return '"' + str.replace(/"/g, '""') + '"';
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
// 导出数据
|
||||
router.get('/export', (req, res, next) => {
|
||||
try {
|
||||
@@ -54,15 +71,7 @@ router.get('/export', (req, res, next) => {
|
||||
const BOM = '\uFEFF';
|
||||
const cols = ['type', 'category', 'amount', 'quantity', 'unit_price', 'rebate', 'boss', 'partner', 'source', 'destination', 'note', 'created_at'];
|
||||
const csvRows = records.map(r => {
|
||||
return cols.map(col => {
|
||||
const val = r[col];
|
||||
if (val === null || val === undefined) return '';
|
||||
const str = String(val);
|
||||
if (str.includes(',') || str.includes('"') || str.includes('\n')) {
|
||||
return '"' + str.replace(/"/g, '""') + '"';
|
||||
}
|
||||
return str;
|
||||
}).join(',');
|
||||
return cols.map(col => encodeCsvCell(r[col], col)).join(',');
|
||||
});
|
||||
logger.info(`用户 ${req.user.username} 导出 CSV 数据: ${records.length} 条`);
|
||||
res.setHeader('Content-Type', 'text/csv; charset=utf-8');
|
||||
|
||||
@@ -70,10 +70,10 @@ const logger = {
|
||||
const duration = Date.now() - start;
|
||||
const status = res.statusCode;
|
||||
const level = status >= 400 ? 'warn' : 'info';
|
||||
logger[level](`${req.method} ${req.path} ${status} ${duration}ms`);
|
||||
logger[level](`${req.method} ${req.originalUrl || req.url} ${status} ${duration}ms`);
|
||||
});
|
||||
next();
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = logger;
|
||||
module.exports = logger;
|
||||
|
||||
Reference in New Issue
Block a user