All files / server/src/middleware logApiCall.ts

89.28% Statements 25/28
65% Branches 13/20
100% Functions 3/3
89.28% Lines 25/28

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53      42x 6778x 438x   6778x 6778x     6778x 6164x   6541x 176x   6537x           42x         9066x 2206x 2206x         9066x 73x 73x     9053x 6787x 6778x 6778x 6778x 6778x 6778x       6787x    
import { NextFunction, Request, Response } from "express";
import { logger } from "server/utils/logger";
 
const formatSize = (header: string | undefined): string => {
  Iif (header === undefined) {
    return "-";
  }
  const bytes = Number(header);
  Iif (!Number.isFinite(bytes)) {
    return header;
  }
  if (bytes < 1024) {
    return `${bytes} B`;
  }
  Eif (bytes < 1024 * 1024) {
    return `${(bytes / 1024).toFixed(1)} KB`;
  }
  if (bytes < 1024 * 1024 * 1024) {
    return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
  }
  return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)} GB`;
};
 
export const logApiCall = (
  req: Request,
  res: Response,
  next: NextFunction,
): void => {
  Iif (req.method === "OPTIONS") {
    next();
    return;
  }
 
  // The router carrying this is mounted at the app root, so it sees every
  // request - including the ones that fall through to static serving below it
  if (!req.path.startsWith("/api") && !req.path.startsWith("/auth")) {
    next();
    return;
  }
 
  const start = Date.now();
  res.on("finish", () => {
    const ms = Date.now() - start;
    const user = req.auth?.username ?? "anon";
    const ip = req.ip?.replace(/^::ffff:/, "") ?? "-";
    const size = formatSize(res.get("Content-Length"));
    logger.info(
      `API call: ${req.method} ${req.path} ${res.statusCode} ${ms}ms user=${user} ip=${ip} size=${size}`,
    );
  });
  next();
};