mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2025-06-05 21:59:27 +02:00
Merge branch 'staging' into immutable-config
This commit is contained in:
@@ -230,7 +230,7 @@ router.post('/version', jsonParser, async (request, response) => {
|
||||
} catch (error) {
|
||||
// it is not a git repo, or has no commits yet, or is a bare repo
|
||||
// not possible to update it, most likely can't get the branch name either
|
||||
return response.send({ currentBranchName: null, currentCommitHash, isUpToDate: true, remoteUrl: null });
|
||||
return response.send({ currentBranchName: '', currentCommitHash: '', isUpToDate: true, remoteUrl: '' });
|
||||
}
|
||||
|
||||
const currentBranch = await git.cwd(extensionPath).branch();
|
||||
|
@@ -125,8 +125,14 @@ router.get('/get', jsonParser, function (request, response) {
|
||||
.map((file) => {
|
||||
const pathToSprite = path.join(spritesPath, file);
|
||||
const mtime = fs.statSync(pathToSprite).mtime?.toISOString().replace(/[^0-9]/g, '').slice(0, 14);
|
||||
|
||||
const fileName = path.parse(pathToSprite).name.toLowerCase();
|
||||
// Extract the label from the filename via regex, which can be suffixed with a sub-name, either connected with a dash or a dot.
|
||||
// Examples: joy.png, joy-1.png, joy.expressive.png
|
||||
const label = fileName.match(/^(.+?)(?:[-\\.].*?)?$/)?.[1] ?? fileName;
|
||||
|
||||
return {
|
||||
label: path.parse(pathToSprite).name.toLowerCase(),
|
||||
label: label,
|
||||
path: `/characters/${name}/${file}` + (mtime ? `?t=${mtime}` : ''),
|
||||
};
|
||||
});
|
||||
@@ -141,8 +147,9 @@ router.get('/get', jsonParser, function (request, response) {
|
||||
router.post('/delete', jsonParser, async (request, response) => {
|
||||
const label = request.body.label;
|
||||
const name = request.body.name;
|
||||
const spriteName = request.body.spriteName || label;
|
||||
|
||||
if (!label || !name) {
|
||||
if (!spriteName || !name) {
|
||||
return response.sendStatus(400);
|
||||
}
|
||||
|
||||
@@ -158,7 +165,7 @@ router.post('/delete', jsonParser, async (request, response) => {
|
||||
|
||||
// Remove existing sprite with the same label
|
||||
for (const file of files) {
|
||||
if (path.parse(file).name === label) {
|
||||
if (path.parse(file).name === spriteName) {
|
||||
fs.rmSync(path.join(spritesPath, file));
|
||||
}
|
||||
}
|
||||
@@ -221,6 +228,7 @@ router.post('/upload', urlencodedParser, async (request, response) => {
|
||||
const file = request.file;
|
||||
const label = request.body.label;
|
||||
const name = request.body.name;
|
||||
const spriteName = request.body.spriteName || label;
|
||||
|
||||
if (!file || !label || !name) {
|
||||
return response.sendStatus(400);
|
||||
@@ -243,12 +251,12 @@ router.post('/upload', urlencodedParser, async (request, response) => {
|
||||
|
||||
// Remove existing sprite with the same label
|
||||
for (const file of files) {
|
||||
if (path.parse(file).name === label) {
|
||||
if (path.parse(file).name === spriteName) {
|
||||
fs.rmSync(path.join(spritesPath, file));
|
||||
}
|
||||
}
|
||||
|
||||
const filename = label + path.parse(file.originalname).ext;
|
||||
const filename = spriteName + path.parse(file.originalname).ext;
|
||||
const spritePath = path.join(file.destination, file.filename);
|
||||
const pathToFile = path.join(spritesPath, filename);
|
||||
// Copy uploaded file to sprites folder
|
||||
|
59
src/middleware/accessLogWriter.js
Normal file
59
src/middleware/accessLogWriter.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 enableForwardedWhitelist = getConfigValue('enableForwardedWhitelist', false, 'boolean');
|
||||
let whitelist = getConfigValue('whitelist', []);
|
||||
let knownIPs = new Set();
|
||||
|
||||
export const getAccessLogPath = () => path.join(globalThis.DATA_ROOT, 'access.log');
|
||||
|
||||
if (fs.existsSync(whitelistPath)) {
|
||||
try {
|
||||
@@ -48,67 +45,41 @@ function getForwardedIp(req) {
|
||||
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.
|
||||
* @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
|
||||
*/
|
||||
export default function whitelistMiddleware(whitelistMode, listen) {
|
||||
export default function whitelistMiddleware() {
|
||||
const forbiddenWebpage = Handlebars.compile(
|
||||
safeReadFileSync('./public/error/forbidden-by-whitelist.html') ?? '',
|
||||
);
|
||||
|
||||
const noLogPaths = [
|
||||
'/favicon.ico',
|
||||
];
|
||||
|
||||
return function (req, res, next) {
|
||||
const clientIp = getIpFromRequest(req);
|
||||
const forwardedIp = getForwardedIp(req);
|
||||
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();
|
||||
if (whitelistMode === true && !whitelist.some(x => ipMatching.matches(clientIp, ipMatching.getMatch(x)))
|
||||
|| forwardedIp && whitelistMode === true && !whitelist.some(x => ipMatching.matches(forwardedIp, ipMatching.getMatch(x)))
|
||||
if (!whitelist.some(x => ipMatching.matches(clientIp, ipMatching.getMatch(x)))
|
||||
|| forwardedIp && !whitelist.some(x => ipMatching.matches(forwardedIp, ipMatching.getMatch(x)))
|
||||
) {
|
||||
// Log the connection attempt with real IP address
|
||||
const ipDetails = forwardedIp
|
||||
? `${clientIp} (forwarded from ${forwardedIp})`
|
||||
: clientIp;
|
||||
console.warn(
|
||||
color.red(
|
||||
`Blocked connection from ${clientIp}; User Agent: ${userAgent}\n\tTo allow this connection, add its IP address to the whitelist or disable whitelist mode by editing config.yaml in the root directory of your SillyTavern installation.\n`,
|
||||
),
|
||||
);
|
||||
|
||||
if (!noLogPaths.includes(req.path)) {
|
||||
console.warn(
|
||||
color.red(
|
||||
`Blocked connection from ${clientIp}; User Agent: ${userAgent}\n\tTo allow this connection, add its IP address to the whitelist or disable whitelist mode by editing config.yaml in the root directory of your SillyTavern installation.\n`,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return res.status(403).send(forbiddenWebpage({ ipDetails }));
|
||||
}
|
||||
next();
|
||||
|
@@ -3,7 +3,7 @@ import path from 'node:path';
|
||||
import url from 'node:url';
|
||||
|
||||
import express from 'express';
|
||||
import { default as git } from 'simple-git';
|
||||
import { default as git, CheckRepoActions } from 'simple-git';
|
||||
import { sync as commandExistsSync } from 'command-exists';
|
||||
import { getConfigValue, color } from './util.js';
|
||||
|
||||
@@ -256,7 +256,7 @@ async function updatePlugins(pluginsPath) {
|
||||
const pluginPath = path.join(pluginsPath, directory);
|
||||
const pluginRepo = git(pluginPath);
|
||||
|
||||
const isRepo = await pluginRepo.checkIsRepo();
|
||||
const isRepo = await pluginRepo.checkIsRepo(CheckRepoActions.IS_REPO_ROOT);
|
||||
if (!isRepo) {
|
||||
continue;
|
||||
}
|
||||
|
@@ -805,7 +805,7 @@ export function stringToBool(str) {
|
||||
* Setup the minimum log level
|
||||
*/
|
||||
export function setupLogLevel() {
|
||||
const logLevel = getConfigValue('minLogLevel', LOG_LEVELS.DEBUG, 'number');
|
||||
const logLevel = getConfigValue('logging.minLogLevel', LOG_LEVELS.DEBUG, 'number');
|
||||
|
||||
globalThis.console.debug = logLevel <= LOG_LEVELS.DEBUG ? console.debug : () => {};
|
||||
globalThis.console.info = logLevel <= LOG_LEVELS.INFO ? console.info : () => {};
|
||||
|
Reference in New Issue
Block a user