* readme for auth tests * wip prm test * add example helper for local oauth w/o browser * working through dangling timeout issue * add better test function * pretty printing * add mcp method * some more logging tweaks * refactor logger into 1 class * use common request logger * fix base scenario * some renaming * refactor into helpers * refactor to support second test * rm readme * add comment about www-authenticate
115 lines
3.1 KiB
TypeScript
115 lines
3.1 KiB
TypeScript
import { Request, Response, NextFunction } from 'express';
|
|
import { ConformanceCheck } from '../types.js';
|
|
|
|
export interface LoggerOptions {
|
|
incomingId: string;
|
|
outgoingId: string;
|
|
mcpRoute?: string;
|
|
}
|
|
|
|
export function createRequestLogger(
|
|
checks: ConformanceCheck[],
|
|
options: LoggerOptions
|
|
) {
|
|
return (req: Request, res: Response, next: NextFunction) => {
|
|
// Log incoming request
|
|
let requestDescription = `Received ${req.method} request for ${req.path}`;
|
|
const requestDetails: any = {
|
|
method: req.method,
|
|
path: req.path
|
|
};
|
|
|
|
// Add query parameters to details if they exist
|
|
if (Object.keys(req.query).length > 0) {
|
|
requestDetails.query = req.query;
|
|
}
|
|
|
|
// Extract MCP method if this is the MCP route
|
|
if (
|
|
options.mcpRoute &&
|
|
req.path === options.mcpRoute &&
|
|
req.get('content-type')?.includes('application/json') &&
|
|
req.body &&
|
|
typeof req.body === 'object' &&
|
|
req.body.method
|
|
) {
|
|
const mcpMethod = req.body.method;
|
|
requestDescription += ` (method: ${mcpMethod})`;
|
|
requestDetails.mcpMethod = mcpMethod;
|
|
}
|
|
|
|
checks.push({
|
|
id: options.incomingId,
|
|
name:
|
|
options.incomingId.charAt(0).toUpperCase() +
|
|
options.incomingId.slice(1),
|
|
description: requestDescription,
|
|
status: 'INFO',
|
|
timestamp: new Date().toISOString(),
|
|
details: requestDetails
|
|
});
|
|
|
|
// Capture response body
|
|
const oldWrite = res.write.bind(res);
|
|
const oldEnd = res.end.bind(res);
|
|
const chunks: (Buffer | string)[] = [];
|
|
|
|
(res.write as any) = function (chunk: any, ...args: any[]) {
|
|
chunks.push(chunk);
|
|
return oldWrite(chunk, ...args);
|
|
};
|
|
|
|
(res.end as any) = function (chunk?: any, ...args: any[]) {
|
|
if (chunk) {
|
|
chunks.push(chunk);
|
|
}
|
|
|
|
const buffers = chunks.map((c) =>
|
|
typeof c === 'string' ? Buffer.from(c) : c
|
|
);
|
|
const body = Buffer.concat(buffers).toString('utf8');
|
|
let responseDescription = `Sent ${res.statusCode} response for ${req.method} ${req.path}`;
|
|
const responseDetails: any = {
|
|
method: req.method,
|
|
path: req.path,
|
|
statusCode: res.statusCode
|
|
};
|
|
|
|
// Include MCP method in response log if present
|
|
if (requestDetails.mcpMethod) {
|
|
responseDescription += ` (method: ${requestDetails.mcpMethod})`;
|
|
responseDetails.mcpMethod = requestDetails.mcpMethod;
|
|
}
|
|
|
|
// Add response headers
|
|
const headers = res.getHeaders();
|
|
if (Object.keys(headers).length > 0) {
|
|
responseDetails.headers = headers;
|
|
}
|
|
|
|
// Add response body if available
|
|
if (body) {
|
|
try {
|
|
responseDetails.body = JSON.parse(body);
|
|
} catch {
|
|
responseDetails.body = body;
|
|
}
|
|
}
|
|
|
|
checks.push({
|
|
id: options.outgoingId,
|
|
name:
|
|
options.outgoingId.charAt(0).toUpperCase() +
|
|
options.outgoingId.slice(1),
|
|
description: responseDescription,
|
|
status: 'INFO',
|
|
timestamp: new Date().toISOString(),
|
|
details: responseDetails
|
|
});
|
|
|
|
return oldEnd(chunk, ...args);
|
|
};
|
|
|
|
next();
|
|
};
|
|
}
|