Compare commits
No commits in common. "b03806fae6cf833f9c91736138db725342939e68" and "ea2fb9ca6114399683e9e2eab0f2115fb07c6388" have entirely different histories.
b03806fae6
...
ea2fb9ca61
|
|
@ -338,7 +338,7 @@ app.get('/api/config', (_req, res) => {
|
|||
})
|
||||
|
||||
/**
|
||||
* Get recent logs from memory (dev/debug only)
|
||||
* Get recent logs (dev/debug only)
|
||||
* GET /api/logs?limit=100&level=debug|info|warn|error&q=keyword
|
||||
*/
|
||||
app.get('/api/logs', (req, res) => {
|
||||
|
|
@ -360,70 +360,6 @@ app.get('/api/logs', (req, res) => {
|
|||
res.json({ count: sliced.length, items: sliced })
|
||||
})
|
||||
|
||||
/**
|
||||
* List all log files in logs directory
|
||||
* GET /api/logs/files
|
||||
*/
|
||||
app.get('/api/logs/files', (req, res) => {
|
||||
if (!LOG_EXPOSE_API) {
|
||||
return res.status(403).json({ error: 'FORBIDDEN', message: 'Log API disabled. Set LOG_EXPOSE_API=true to enable.' })
|
||||
}
|
||||
try {
|
||||
const files = fs.readdirSync(LOG_DIR)
|
||||
.filter(f => f.startsWith('LOGS_') && f.endsWith('.log'))
|
||||
.map(f => {
|
||||
const stats = fs.statSync(path.join(LOG_DIR, f))
|
||||
return {
|
||||
filename: f,
|
||||
size: stats.size,
|
||||
modified: stats.mtime,
|
||||
path: `/api/logs/files/${f}`
|
||||
}
|
||||
})
|
||||
.sort((a, b) => b.modified - a.modified)
|
||||
res.json({ count: files.length, files })
|
||||
} catch (e) {
|
||||
logError('logs.files.error', { message: e?.message })
|
||||
res.status(500).json({ error: 'READ_ERROR', message: e?.message || 'Failed to read log files' })
|
||||
}
|
||||
})
|
||||
|
||||
/**
|
||||
* Read specific log file
|
||||
* GET /api/logs/files/:filename
|
||||
*/
|
||||
app.get('/api/logs/files/:filename', (req, res) => {
|
||||
if (!LOG_EXPOSE_API) {
|
||||
return res.status(403).json({ error: 'FORBIDDEN', message: 'Log API disabled. Set LOG_EXPOSE_API=true to enable.' })
|
||||
}
|
||||
try {
|
||||
const { filename } = req.params
|
||||
|
||||
// SECURITY: VALIDATE FILENAME TO PREVENT DIRECTORY TRAVERSAL
|
||||
if (!filename.match(/^LOGS_\d{8}\.log$/)) {
|
||||
return res.status(400).json({ error: 'INVALID_FILENAME', message: 'Invalid log filename format' })
|
||||
}
|
||||
|
||||
const filePath = path.join(LOG_DIR, filename)
|
||||
|
||||
if (!fs.existsSync(filePath)) {
|
||||
return res.status(404).json({ error: 'NOT_FOUND', message: 'Log file not found' })
|
||||
}
|
||||
|
||||
const content = fs.readFileSync(filePath, 'utf8')
|
||||
const lines = content.split('\n').filter(Boolean)
|
||||
|
||||
res.json({
|
||||
filename,
|
||||
lines: lines.length,
|
||||
content: lines
|
||||
})
|
||||
} catch (e) {
|
||||
logError('logs.file.read.error', { message: e?.message, filename: req.params.filename })
|
||||
res.status(500).json({ error: 'READ_ERROR', message: e?.message || 'Failed to read log file' })
|
||||
}
|
||||
})
|
||||
|
||||
/**
|
||||
* Update payment toggles at runtime (dev only)
|
||||
* POST /api/config
|
||||
|
|
|
|||
Loading…
Reference in New Issue