feat: /logs debug endpoint with circular buffer
This commit is contained in:
1 parent
76b471edba
commit
769ba2b4bb
1 file changed
+32
-3
+32
-3
@@ -50,6 +50,25 @@ if (!MCP_TOKEN) {
|
|||||||
const PORT = Number(process.env.PORT || 8935);
|
const PORT = Number(process.env.PORT || 8935);
|
||||||
const OUTPUT_DIR = process.env.IMAGE_MCP_OUTPUT_DIR || '/app/generated-images';
|
const OUTPUT_DIR = process.env.IMAGE_MCP_OUTPUT_DIR || '/app/generated-images';
|
||||||
|
|
||||||
|
// ---------- In-memory circular log buffer (for /logs debug endpoint) ----------
|
||||||
|
const LOG_MAX = 500;
|
||||||
|
const logBuffer = [];
|
||||||
|
function writeLog(msg) {
|
||||||
|
logBuffer.push(msg);
|
||||||
|
if (logBuffer.length > LOG_MAX) logBuffer.shift();
|
||||||
|
process.stderr.write(msg);
|
||||||
|
}
|
||||||
|
function readLogBuffer(n) {
|
||||||
|
return logBuffer.slice(-n).join('');
|
||||||
|
}
|
||||||
|
// Override stderr.write to also capture to our buffer
|
||||||
|
const origStdErrWrite = process.stderr.write.bind(process.stderr);
|
||||||
|
process.stderr.write = (chunk, ...args) => {
|
||||||
|
logBuffer.push(String(chunk));
|
||||||
|
if (logBuffer.length > LOG_MAX) logBuffer.shift();
|
||||||
|
return origStdErrWrite(chunk, ...args);
|
||||||
|
};
|
||||||
|
|
||||||
// ---------- Models that support transparent background ----------
|
// ---------- Models that support transparent background ----------
|
||||||
// Recraft (v3 confirmed via OpenRouter /api/v1/images endpoint, accepts background param)
|
// Recraft (v3 confirmed via OpenRouter /api/v1/images endpoint, accepts background param)
|
||||||
// GPT-Image (1/2/5-image) via /chat/completions with image_config.background=transparent
|
// GPT-Image (1/2/5-image) via /chat/completions with image_config.background=transparent
|
||||||
@@ -602,8 +621,8 @@ await mcpServer.connect(mcpTransport);
|
|||||||
const httpServer = http.createServer(async (req, res) => {
|
const httpServer = http.createServer(async (req, res) => {
|
||||||
const url = new URL(req.url, `http://${req.headers.host || 'localhost'}`);
|
const url = new URL(req.url, `http://${req.headers.host || 'localhost'}`);
|
||||||
|
|
||||||
// Auth gate — skip /health and /images/
|
// Auth gate — skip /health, /images/ and /logs
|
||||||
if (!url.pathname.startsWith('/health') && !url.pathname.startsWith('/images/')) {
|
if (!url.pathname.startsWith('/health') && !url.pathname.startsWith('/images/') && !url.pathname.startsWith('/logs')) {
|
||||||
const auth = req.headers.authorization || '';
|
const auth = req.headers.authorization || '';
|
||||||
const expected = `Bearer ${MCP_TOKEN}`;
|
const expected = `Bearer ${MCP_TOKEN}`;
|
||||||
if (auth !== expected) {
|
if (auth !== expected) {
|
||||||
@@ -616,7 +635,7 @@ const httpServer = http.createServer(async (req, res) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Lightweight /health for Coolify / Traefik probes
|
// Lightweight /health for Coolify / Traefik probes
|
||||||
if (url.pathname === '/health') {
|
if (url.pathname === '/health') {
|
||||||
res.statusCode = 200;
|
res.statusCode = 200;
|
||||||
res.setHeader('content-type', 'application/json');
|
res.setHeader('content-type', 'application/json');
|
||||||
@@ -624,6 +643,16 @@ const httpServer = http.createServer(async (req, res) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Recent logs viewer (no auth, for debugging Figma connections)
|
||||||
|
if (url.pathname === '/logs') {
|
||||||
|
const maxLines = parseInt(url.searchParams.get('n') || '50', 10);
|
||||||
|
const logLines = readLogBuffer(maxLines);
|
||||||
|
res.statusCode = 200;
|
||||||
|
res.setHeader('content-type', 'text/plain; charset=utf-8');
|
||||||
|
res.end(logLines);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
process.stderr.write(`[HTTP] ${req.method} ${url.pathname} -> incoming\n`);
|
process.stderr.write(`[HTTP] ${req.method} ${url.pathname} -> incoming\n`);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|||||||
Reference in new issue
Block a user