feat(federation): add room sync and deletion notification endpoints for federated instances fix(federation): handle room deletion and update settings during sync process feat(federation): enhance FederatedRoomCard and FederatedRoomDetail components to display deleted rooms i18n: add translations for room deletion messages in English and German
158 lines
6.1 KiB
JavaScript
158 lines
6.1 KiB
JavaScript
/**
|
|
* Centralized logger for Redlight server.
|
|
*
|
|
* Produces clean, colorized, tagged log lines inspired by Greenlight/Rails lograge style.
|
|
*
|
|
* Format: TIMESTAMP LEVEL [TAG] message
|
|
* Example: 2026-03-01 12:00:00 INFO [BBB] GET getMeetings → 200 SUCCESS (45ms)
|
|
*/
|
|
|
|
// ── ANSI colors ─────────────────────────────────────────────────────────────
|
|
const C = {
|
|
reset: '\x1b[0m',
|
|
bold: '\x1b[1m',
|
|
dim: '\x1b[2m',
|
|
red: '\x1b[31m',
|
|
green: '\x1b[32m',
|
|
yellow: '\x1b[33m',
|
|
blue: '\x1b[34m',
|
|
magenta: '\x1b[35m',
|
|
cyan: '\x1b[36m',
|
|
white: '\x1b[37m',
|
|
gray: '\x1b[90m',
|
|
};
|
|
|
|
const USE_COLOR = process.env.NO_COLOR ? false : true;
|
|
const c = (color, text) => USE_COLOR ? `${color}${text}${C.reset}` : text;
|
|
|
|
// ── Timestamp ───────────────────────────────────────────────────────────────
|
|
function ts() {
|
|
const d = new Date();
|
|
const pad = n => String(n).padStart(2, '0');
|
|
const ms = String(d.getMilliseconds()).padStart(3, '0');
|
|
return `${d.getUTCFullYear()}-${pad(d.getUTCMonth() + 1)}-${pad(d.getUTCDate())} ${pad(d.getUTCHours())}:${pad(d.getUTCMinutes())}:${pad(d.getUTCSeconds())}.${ms}`;
|
|
}
|
|
|
|
// ── Level formatting ────────────────────────────────────────────────────────
|
|
const LEVEL_STYLE = {
|
|
DEBUG: { color: C.gray, label: 'DEBUG' },
|
|
INFO: { color: C.green, label: ' INFO' },
|
|
WARN: { color: C.yellow, label: ' WARN' },
|
|
ERROR: { color: C.red, label: 'ERROR' },
|
|
};
|
|
|
|
// ── Tag colors ──────────────────────────────────────────────────────────────
|
|
const TAG_COLORS = {
|
|
BBB: C.magenta,
|
|
HTTP: C.cyan,
|
|
Federation: C.blue,
|
|
FedSync: C.blue,
|
|
DB: C.yellow,
|
|
Auth: C.green,
|
|
Server: C.white,
|
|
Mailer: C.cyan,
|
|
Redis: C.magenta,
|
|
Admin: C.yellow,
|
|
Rooms: C.green,
|
|
Recordings: C.cyan,
|
|
Branding: C.white,
|
|
};
|
|
|
|
function formatLine(level, tag, message) {
|
|
const lvl = LEVEL_STYLE[level] || LEVEL_STYLE.INFO;
|
|
const tagColor = TAG_COLORS[tag] || C.white;
|
|
const timestamp = c(C.gray, ts());
|
|
const levelStr = c(lvl.color, lvl.label);
|
|
const tagStr = c(tagColor, `[${tag}]`);
|
|
return `${timestamp} ${levelStr} ${tagStr} ${message}`;
|
|
}
|
|
|
|
// ── Public API ──────────────────────────────────────────────────────────────
|
|
|
|
/**
|
|
* Create a tagged logger.
|
|
* @param {string} tag - e.g. 'BBB', 'HTTP', 'Federation'
|
|
*/
|
|
export function createLogger(tag) {
|
|
return {
|
|
debug: (msg, ...args) => console.debug(formatLine('DEBUG', tag, msg), ...args),
|
|
info: (msg, ...args) => console.info(formatLine('INFO', tag, msg), ...args),
|
|
warn: (msg, ...args) => console.warn(formatLine('WARN', tag, msg), ...args),
|
|
error: (msg, ...args) => console.error(formatLine('ERROR', tag, msg), ...args),
|
|
};
|
|
}
|
|
|
|
// ── Pre-built loggers for common subsystems ─────────────────────────────────
|
|
export const log = {
|
|
bbb: createLogger('BBB'),
|
|
http: createLogger('HTTP'),
|
|
federation: createLogger('Federation'),
|
|
fedSync: createLogger('FedSync'),
|
|
db: createLogger('DB'),
|
|
auth: createLogger('Auth'),
|
|
server: createLogger('Server'),
|
|
mailer: createLogger('Mailer'),
|
|
redis: createLogger('Redis'),
|
|
admin: createLogger('Admin'),
|
|
rooms: createLogger('Rooms'),
|
|
recordings: createLogger('Recordings'),
|
|
branding: createLogger('Branding'),
|
|
};
|
|
|
|
// ── Helpers ─────────────────────────────────────────────────────────────────
|
|
|
|
/** Format duration with unit and color. */
|
|
export function fmtDuration(ms) {
|
|
const num = Number(ms);
|
|
if (num < 100) return c(C.green, `${num.toFixed(0)}ms`);
|
|
if (num < 1000) return c(C.yellow, `${num.toFixed(0)}ms`);
|
|
return c(C.red, `${(num / 1000).toFixed(2)}s`);
|
|
}
|
|
|
|
/** Format HTTP status with color. */
|
|
export function fmtStatus(status) {
|
|
const s = Number(status);
|
|
if (s < 300) return c(C.green, String(s));
|
|
if (s < 400) return c(C.cyan, String(s));
|
|
if (s < 500) return c(C.yellow, String(s));
|
|
return c(C.red, String(s));
|
|
}
|
|
|
|
/** Format HTTP method with color. */
|
|
export function fmtMethod(method) {
|
|
const m = String(method).toUpperCase();
|
|
const colors = { GET: C.green, POST: C.cyan, PUT: C.yellow, PATCH: C.yellow, DELETE: C.red };
|
|
return c(colors[m] || C.white, m.padEnd(6));
|
|
}
|
|
|
|
/** Format BBB returncode with color. */
|
|
export function fmtReturncode(code) {
|
|
if (code === 'SUCCESS') return c(C.green, code);
|
|
if (code === 'FAILED') return c(C.red, code);
|
|
return c(C.yellow, code || '-');
|
|
}
|
|
|
|
// ── Sensitive value filtering ───────────────────────────────────────────────
|
|
const SENSITIVE_KEYS = [/pass/i, /pwd/i, /password/i, /token/i, /jwt/i, /secret/i, /authorization/i, /api[_-]?key/i];
|
|
export function isSensitiveKey(key) {
|
|
return SENSITIVE_KEYS.some(rx => rx.test(key));
|
|
}
|
|
|
|
/**
|
|
* Sanitize BBB params for logging: filter sensitive values, truncate long strings, omit checksum.
|
|
*/
|
|
export function sanitizeBBBParams(params) {
|
|
const parts = [];
|
|
for (const k of Object.keys(params || {})) {
|
|
if (k.toLowerCase() === 'checksum') continue;
|
|
if (isSensitiveKey(k)) {
|
|
parts.push(`${k}=${c(C.dim, '[FILTERED]')}`);
|
|
} else {
|
|
let v = params[k];
|
|
if (typeof v === 'string' && v.length > 80) v = v.slice(0, 80) + '…';
|
|
parts.push(`${c(C.gray, k)}=${v}`);
|
|
}
|
|
}
|
|
return parts.join(' ') || '-';
|
|
}
|