This commit is contained in:
seb
2026-10-08 03:43:28 +02:00
parent c543c76a21
commit 717f4754d4
9 changed files with 787 additions and 24 deletions

View File

@@ -14,8 +14,11 @@ import { closeAll as closeAllSse } from './sse.js';
import { closeAllManagers } from '../services/conversationManager.js';
import { shutdownImageCropWorkers } from '../services/imageCrop.js';
import { getOrCreateCert } from '../util/cert.js';
import { createLogger } from '../util/logger.js';
import v1Router from '../v1/index.js';
const apiLog = createLogger('api');
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
@@ -155,7 +158,7 @@ async function createApi() {
if (err instanceof SyntaxError && err.status === 400 && 'body' in err) {
return res.status(400).json({ error: 'Invalid JSON body' });
}
console.error('Unhandled API error:', err);
apiLog.error('Unhandled API error:', err);
const status = typeof err.status === 'number' && err.status >= 400 && err.status < 600 ? err.status : 500;
res.status(status).json({ error: err.message || 'Internal server error' });
});
@@ -178,9 +181,9 @@ function listenServer(instance, port, protocol) {
} catch {
// lsof unavailable or no match
}
console.error(`Port ${port} (${protocol}) is already in use${detail}. Check with: lsof -i :${port}`);
apiLog.error(`Port ${port} (${protocol}) is already in use${detail}. Check with: lsof -i :${port}`);
} else {
console.error(`${protocol} server error:`, err.message);
apiLog.error(`${protocol} server error:`, err.message);
}
reject(err);
});
@@ -206,7 +209,7 @@ export async function startApi() {
const httpInstance = http.createServer(app);
await listenServer(httpInstance, env.port, 'HTTP');
server = httpInstance;
console.log(`API listening on port ${env.port} (HTTP)`);
apiLog.info(`API listening on port ${env.port} (HTTP)`);
if (env.httpsPort) {
try {
@@ -214,9 +217,9 @@ export async function startApi() {
const httpsInstance = https.createServer(tls, app);
await listenServer(httpsInstance, env.httpsPort, 'HTTPS');
httpsServer = httpsInstance;
console.log(`API listening on port ${env.httpsPort} (HTTPS self-signed)`);
apiLog.info(`API listening on port ${env.httpsPort} (HTTPS self-signed)`);
} catch (err) {
console.error('Failed to start HTTPS server:', err.message);
apiLog.error('Failed to start HTTPS server:', err.message);
}
}

View File

@@ -1,17 +1,18 @@
import chalk from 'chalk';
import { isEmpty, prettyJson } from '../../util/prettyJson.js';
import { writeErrorLog } from '../../util/errorLog.js';
import { createLogger, chalk } from '../../util/logger.js';
const httpLog = createLogger('http');
function logField(label, value, { skipEmpty = false } = {}) {
if (skipEmpty && isEmpty(value)) return;
const rendered = prettyJson(value);
const prefix = chalk.gray(` ${label}: `);
if (!rendered.includes('\n')) {
console.log(prefix + rendered);
httpLog.debug(prefix + rendered);
return;
}
console.log(prefix);
console.log(rendered.replace(/^/gm, ' '));
httpLog.debug(prefix + '\n' + rendered.replace(/^/gm, ' '));
}
function captureResponseBody(res) {
@@ -53,14 +54,22 @@ export const loggerMiddleware = (req, res, next) => {
else if (status >= 300) status = chalk.cyan(status);
else status = chalk.green(status);
console.log(`${method} ${url} ${status} ${chalk.gray(`(${duration}ms)`)}`);
const summary = `${method} ${url} ${status} ${chalk.gray(`(${duration}ms)`)}`;
if (res.statusCode >= 500) {
httpLog.error(summary);
} else if (res.statusCode >= 400) {
httpLog.warn(summary);
} else {
httpLog.info(summary);
}
logField('params', req.params ?? {}, { skipEmpty: true });
logField('query', req.query ?? {}, { skipEmpty: true });
if (req.headers['content-type']) {
console.log(chalk.gray(' content-type:'), chalk.cyan(req.headers['content-type']));
httpLog.debug(chalk.gray(' content-type: ') + chalk.cyan(req.headers['content-type']));
}
if (req.headers.authorization) {
console.log(chalk.gray(' authorization:'), chalk.cyan(req.headers.authorization));
httpLog.debug(chalk.gray(' authorization: ') + chalk.cyan(req.headers.authorization));
}
logField('body', req.body ?? {}, { skipEmpty: true });
logField('response', getBody());