mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2025-06-05 21:59:27 +02:00
feat(middleware): add separate access log middleware with config option
This commit is contained in:
@@ -85,6 +85,11 @@ cookieSecret: ''
|
|||||||
disableCsrfProtection: false
|
disableCsrfProtection: false
|
||||||
# Disable startup security checks - NOT RECOMMENDED
|
# Disable startup security checks - NOT RECOMMENDED
|
||||||
securityOverride: false
|
securityOverride: false
|
||||||
|
# -- LOGGING CONFIGURATION --
|
||||||
|
logging:
|
||||||
|
# Enable access logging to access.log file
|
||||||
|
# Records new connections with timestamp, IP address and user agent
|
||||||
|
enableAccessLog: true
|
||||||
# -- RATE LIMITING CONFIGURATION --
|
# -- RATE LIMITING CONFIGURATION --
|
||||||
rateLimiting:
|
rateLimiting:
|
||||||
# Use X-Real-IP header instead of socket IP for rate limiting
|
# Use X-Real-IP header instead of socket IP for rate limiting
|
||||||
|
@@ -57,7 +57,8 @@ import {
|
|||||||
|
|
||||||
import getWebpackServeMiddleware from './src/middleware/webpack-serve.js';
|
import getWebpackServeMiddleware from './src/middleware/webpack-serve.js';
|
||||||
import basicAuthMiddleware from './src/middleware/basicAuth.js';
|
import basicAuthMiddleware from './src/middleware/basicAuth.js';
|
||||||
import whitelistMiddleware, { getAccessLogPath, migrateAccessLog } from './src/middleware/whitelist.js';
|
import whitelistMiddleware from './src/middleware/whitelist.js';
|
||||||
|
import accessLoggerMiddleware, { getAccessLogPath, migrateAccessLog } from './src/middleware/accessLogger.js';
|
||||||
import multerMonkeyPatch from './src/middleware/multerMonkeyPatch.js';
|
import multerMonkeyPatch from './src/middleware/multerMonkeyPatch.js';
|
||||||
import initRequestProxy from './src/request-proxy.js';
|
import initRequestProxy from './src/request-proxy.js';
|
||||||
import getCacheBusterMiddleware from './src/middleware/cacheBuster.js';
|
import getCacheBusterMiddleware from './src/middleware/cacheBuster.js';
|
||||||
@@ -342,7 +343,8 @@ app.use(CORS);
|
|||||||
|
|
||||||
if (listen && basicAuthMode) app.use(basicAuthMiddleware);
|
if (listen && basicAuthMode) app.use(basicAuthMiddleware);
|
||||||
|
|
||||||
app.use(whitelistMiddleware(enableWhitelist, listen));
|
app.use(whitelistMiddleware(enableWhitelist));
|
||||||
|
app.use(accessLoggerMiddleware());
|
||||||
|
|
||||||
if (enableCorsProxy) {
|
if (enableCorsProxy) {
|
||||||
app.use(bodyParser.json({
|
app.use(bodyParser.json({
|
||||||
|
59
src/middleware/accessLogger.js
Normal file
59
src/middleware/accessLogger.js
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
import path from 'node:path';
|
||||||
|
import fs from 'node:fs';
|
||||||
|
import { getRealIpFromHeader } from '../express-common.js';
|
||||||
|
import { color, getConfigValue } from '../util.js';
|
||||||
|
|
||||||
|
const enableAccessLog = getConfigValue('logging.enableAccessLog', true);
|
||||||
|
|
||||||
|
const knownIPs = new Set();
|
||||||
|
|
||||||
|
export const getAccessLogPath = () => path.join(globalThis.DATA_ROOT, 'access.log');
|
||||||
|
|
||||||
|
export function migrateAccessLog() {
|
||||||
|
try {
|
||||||
|
if (!fs.existsSync('access.log')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const logPath = getAccessLogPath();
|
||||||
|
if (fs.existsSync(logPath)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fs.renameSync('access.log', logPath);
|
||||||
|
console.log(color.yellow('Migrated access.log to new location:'), logPath);
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Failed to migrate access log:', e);
|
||||||
|
console.info('Please move access.log to the data directory manually.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates middleware for logging access and new connections
|
||||||
|
* @returns {import('express').RequestHandler}
|
||||||
|
*/
|
||||||
|
export default function accessLoggerMiddleware() {
|
||||||
|
return function (req, res, next) {
|
||||||
|
const clientIp = getRealIpFromHeader(req);
|
||||||
|
const userAgent = req.headers['user-agent'];
|
||||||
|
|
||||||
|
if (!knownIPs.has(clientIp)) {
|
||||||
|
// Log new connection
|
||||||
|
console.info(color.yellow(`New connection from ${clientIp}; User Agent: ${userAgent}\n`));
|
||||||
|
knownIPs.add(clientIp);
|
||||||
|
|
||||||
|
// Write to access log if enabled
|
||||||
|
if (enableAccessLog) {
|
||||||
|
const logPath = getAccessLogPath();
|
||||||
|
const timestamp = new Date().toISOString();
|
||||||
|
const log = `${timestamp} ${clientIp} ${userAgent}\n`;
|
||||||
|
|
||||||
|
fs.appendFile(logPath, log, (err) => {
|
||||||
|
if (err) {
|
||||||
|
console.error('Failed to write access log:', err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
next();
|
||||||
|
};
|
||||||
|
}
|
@@ -10,9 +10,6 @@ import { color, getConfigValue, safeReadFileSync } from '../util.js';
|
|||||||
const whitelistPath = path.join(process.cwd(), './whitelist.txt');
|
const whitelistPath = path.join(process.cwd(), './whitelist.txt');
|
||||||
const enableForwardedWhitelist = getConfigValue('enableForwardedWhitelist', false);
|
const enableForwardedWhitelist = getConfigValue('enableForwardedWhitelist', false);
|
||||||
let whitelist = getConfigValue('whitelist', []);
|
let whitelist = getConfigValue('whitelist', []);
|
||||||
let knownIPs = new Set();
|
|
||||||
|
|
||||||
export const getAccessLogPath = () => path.join(globalThis.DATA_ROOT, 'access.log');
|
|
||||||
|
|
||||||
if (fs.existsSync(whitelistPath)) {
|
if (fs.existsSync(whitelistPath)) {
|
||||||
try {
|
try {
|
||||||
@@ -48,30 +45,12 @@ function getForwardedIp(req) {
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function migrateAccessLog() {
|
|
||||||
try {
|
|
||||||
if (!fs.existsSync('access.log')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const logPath = getAccessLogPath();
|
|
||||||
if (fs.existsSync(logPath)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
fs.renameSync('access.log', logPath);
|
|
||||||
console.log(color.yellow('Migrated access.log to new location:'), logPath);
|
|
||||||
} catch (e) {
|
|
||||||
console.error('Failed to migrate access log:', e);
|
|
||||||
console.info('Please move access.log to the data directory manually.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a middleware function that checks if the client IP is in the whitelist.
|
* Returns a middleware function that checks if the client IP is in the whitelist.
|
||||||
* @param {boolean} whitelistMode If whitelist mode is enabled via config or command line
|
* @param {boolean} whitelistMode If whitelist mode is enabled via config or command line
|
||||||
* @param {boolean} listen If listen mode is enabled via config or command line
|
|
||||||
* @returns {import('express').RequestHandler} The middleware function
|
* @returns {import('express').RequestHandler} The middleware function
|
||||||
*/
|
*/
|
||||||
export default function whitelistMiddleware(whitelistMode, listen) {
|
export default function whitelistMiddleware(whitelistMode) {
|
||||||
const forbiddenWebpage = Handlebars.compile(
|
const forbiddenWebpage = Handlebars.compile(
|
||||||
safeReadFileSync('./public/error/forbidden-by-whitelist.html') ?? '',
|
safeReadFileSync('./public/error/forbidden-by-whitelist.html') ?? '',
|
||||||
);
|
);
|
||||||
@@ -81,21 +60,6 @@ export default function whitelistMiddleware(whitelistMode, listen) {
|
|||||||
const forwardedIp = getForwardedIp(req);
|
const forwardedIp = getForwardedIp(req);
|
||||||
const userAgent = req.headers['user-agent'];
|
const userAgent = req.headers['user-agent'];
|
||||||
|
|
||||||
if (listen && !knownIPs.has(clientIp)) {
|
|
||||||
console.info(color.yellow(`New connection from ${clientIp}; User Agent: ${userAgent}\n`));
|
|
||||||
knownIPs.add(clientIp);
|
|
||||||
|
|
||||||
// Write access log
|
|
||||||
const logPath = getAccessLogPath();
|
|
||||||
const timestamp = new Date().toISOString();
|
|
||||||
const log = `${timestamp} ${clientIp} ${userAgent}\n`;
|
|
||||||
fs.appendFile(logPath, log, (err) => {
|
|
||||||
if (err) {
|
|
||||||
console.error('Failed to write access log:', err);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
//clientIp = req.connection.remoteAddress.split(':').pop();
|
//clientIp = req.connection.remoteAddress.split(':').pop();
|
||||||
if (whitelistMode === true && !whitelist.some(x => ipMatching.matches(clientIp, ipMatching.getMatch(x)))
|
if (whitelistMode === true && !whitelist.some(x => ipMatching.matches(clientIp, ipMatching.getMatch(x)))
|
||||||
|| forwardedIp && whitelistMode === true && !whitelist.some(x => ipMatching.matches(forwardedIp, ipMatching.getMatch(x)))
|
|| forwardedIp && whitelistMode === true && !whitelist.some(x => ipMatching.matches(forwardedIp, ipMatching.getMatch(x)))
|
||||||
|
Reference in New Issue
Block a user