real-debrid-downloader/src/main/rename-log.ts
Sucukdeluxe 3ed3877ac9 chore: remove all source code comments and internal artifacts
Strip every comment from the source (parsed with the TypeScript compiler so
strings, template literals, regex literals and JSX are never touched), and drop
internal/working artifacts that do not belong in the public repository
(design mockups, internal analysis docs, a stray backup file and an old log).
No functional change: build is green, the full test suite passes.
2026-06-06 04:53:54 +02:00

120 lines
3.2 KiB
TypeScript

import fs from "node:fs";
import { logTimestamp } from "./log-timestamp";
import path from "node:path";
type RenameLogLevel = "INFO" | "WARN" | "ERROR";
const RENAME_LOG_MAX_FILE_BYTES = Number(process.env.RD_RENAME_LOG_MAX_BYTES || 10 * 1024 * 1024);
const RENAME_LOG_RETENTION_DAYS = Number(process.env.RD_RENAME_LOG_RETENTION_DAYS || 30);
let renameLogPath: string | null = null;
function sanitizeFieldValue(value: unknown): string {
if (value === undefined || value === null) {
return "";
}
if (typeof value === "string") {
return value.replace(/\r?\n/g, "\\n");
}
if (typeof value === "number" || typeof value === "boolean") {
return String(value);
}
try {
return JSON.stringify(value).replace(/\r?\n/g, "\\n");
} catch {
return String(value);
}
}
function formatFields(fields?: Record<string, unknown>): string {
if (!fields) {
return "";
}
const parts = Object.entries(fields)
.filter(([, value]) => value !== undefined && value !== null && sanitizeFieldValue(value) !== "")
.map(([key, value]) => `${key}=${sanitizeFieldValue(value)}`);
return parts.length > 0 ? ` | ${parts.join(" | ")}` : "";
}
function rotateIfNeeded(filePath: string): void {
try {
const stat = fs.statSync(filePath);
if (stat.size < RENAME_LOG_MAX_FILE_BYTES) {
return;
}
const backup = `${filePath}.old`;
try {
fs.rmSync(backup, { force: true });
} catch {
}
fs.renameSync(filePath, backup);
} catch {
}
}
function cleanupOldBackup(filePath: string): void {
const backup = `${filePath}.old`;
try {
const stat = fs.statSync(backup);
const cutoff = Date.now() - RENAME_LOG_RETENTION_DAYS * 24 * 60 * 60 * 1000;
if (stat.mtimeMs < cutoff) {
fs.rmSync(backup, { force: true });
}
} catch {
}
}
export function initRenameLog(baseDir: string): void {
renameLogPath = path.join(baseDir, "rename.log");
try {
fs.mkdirSync(path.dirname(renameLogPath), { recursive: true });
cleanupOldBackup(renameLogPath);
if (!fs.existsSync(renameLogPath)) {
fs.writeFileSync(renameLogPath, "", "utf8");
}
rotateIfNeeded(renameLogPath);
if (!fs.existsSync(renameLogPath)) {
fs.writeFileSync(renameLogPath, "", "utf8");
}
fs.appendFileSync(renameLogPath, `=== Rename-Log Start: ${logTimestamp()} ===\n`, "utf8");
} catch {
renameLogPath = null;
}
}
export function logRenameEvent(level: RenameLogLevel, message: string, fields?: Record<string, unknown>): void {
if (!renameLogPath) {
return;
}
try {
rotateIfNeeded(renameLogPath);
if (!fs.existsSync(renameLogPath)) {
fs.writeFileSync(renameLogPath, "", "utf8");
}
fs.appendFileSync(
renameLogPath,
`${logTimestamp()} [${level}] ${message}${formatFields(fields)}\n`,
"utf8"
);
} catch {
}
}
export function getRenameLogPath(): string | null {
if (!renameLogPath) {
return null;
}
return fs.existsSync(renameLogPath) ? renameLogPath : null;
}
export function shutdownRenameLog(): void {
if (!renameLogPath) {
return;
}
try {
fs.appendFileSync(renameLogPath, `=== Rename-Log Ende: ${logTimestamp()} ===\n`, "utf8");
} catch {
}
renameLogPath = null;
}