Initial release v1.0.0

This commit is contained in:
Kevin O'Neill
2025-12-25 18:58:06 -06:00
commit 021aec7a63
439 changed files with 116588 additions and 0 deletions
+226
View File
@@ -0,0 +1,226 @@
import * as fs from "fs";
import * as path from "path";
/**
* Discovery Logger - Creates detailed log files for each discovery playlist generation
*/
class DiscoveryLogger {
private logDir: string;
private currentLogFile: string | null = null;
private currentStream: fs.WriteStream | null = null;
constructor() {
// Store logs in /app/logs/discovery (matches Dockerfile directory)
this.logDir = process.env.NODE_ENV === "production"
? "/app/logs/discovery"
: path.join(process.cwd(), "data", "logs", "discovery");
}
/**
* Start a new log file for a discovery generation
*/
start(userId: string, jobId?: number): string {
// Ensure log directory exists
if (!fs.existsSync(this.logDir)) {
fs.mkdirSync(this.logDir, { recursive: true });
}
// Create filename with timestamp
const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
const filename = `discovery-${timestamp}-job${jobId || "manual"}.log`;
this.currentLogFile = path.join(this.logDir, filename);
// Open write stream
this.currentStream = fs.createWriteStream(this.currentLogFile, { flags: "a" });
// Write header
this.write("═".repeat(60));
this.write(`DISCOVERY WEEKLY GENERATION LOG`);
this.write(`Started: ${new Date().toISOString()}`);
this.write(`User ID: ${userId}`);
this.write(`Job ID: ${jobId || "manual"}`);
this.write("═".repeat(60));
this.write("");
return this.currentLogFile;
}
/**
* Write a line to the current log
*/
write(message: string, indent: number = 0): void {
const prefix = " ".repeat(indent);
const timestamp = new Date().toISOString().split("T")[1].split(".")[0];
const line = `[${timestamp}] ${prefix}${message}`;
// Write to file
if (this.currentStream) {
this.currentStream.write(line + "\n");
}
// Also write to console for real-time visibility
console.log(message);
}
/**
* Write a section header
*/
section(title: string): void {
this.write("");
this.write("─".repeat(50));
this.write(`> ${title}`);
this.write("─".repeat(50));
}
/**
* Write a success message
*/
success(message: string, indent: number = 0): void {
this.write(`${message}`, indent);
}
/**
* Write an error message
*/
error(message: string, indent: number = 0): void {
this.write(`${message}`, indent);
}
/**
* Write a warning message
*/
warn(message: string, indent: number = 0): void {
this.write(`[WARN] ${message}`, indent);
}
/**
* Write info message
*/
info(message: string, indent: number = 0): void {
this.write(` ${message}`, indent);
}
/**
* Write a table of key-value pairs
*/
table(data: Record<string, any>, indent: number = 1): void {
for (const [key, value] of Object.entries(data)) {
this.write(`${key}: ${value}`, indent);
}
}
/**
* Write a list of items
*/
list(items: string[], indent: number = 1): void {
for (const item of items) {
this.write(`${item}`, indent);
}
}
/**
* End the current log and close the stream
*/
end(success: boolean, summary?: string): void {
this.write("");
this.write("═".repeat(60));
this.write(`GENERATION ${success ? "COMPLETED" : "FAILED"}`);
if (summary) {
this.write(summary);
}
this.write(`Ended: ${new Date().toISOString()}`);
this.write("═".repeat(60));
if (this.currentStream) {
this.currentStream.end();
this.currentStream = null;
}
}
/**
* Get the path to the current log file
*/
getCurrentLogPath(): string | null {
return this.currentLogFile;
}
/**
* Get the most recent log file
*/
getLatestLog(): { path: string; content: string } | null {
if (!fs.existsSync(this.logDir)) {
return null;
}
const files = fs.readdirSync(this.logDir)
.filter(f => f.startsWith("discovery-") && f.endsWith(".log"))
.sort()
.reverse();
if (files.length === 0) {
return null;
}
const latestPath = path.join(this.logDir, files[0]);
const content = fs.readFileSync(latestPath, "utf-8");
return { path: latestPath, content };
}
/**
* Get all log files (most recent first)
*/
getAllLogs(): { filename: string; date: Date; size: number }[] {
if (!fs.existsSync(this.logDir)) {
return [];
}
return fs.readdirSync(this.logDir)
.filter(f => f.startsWith("discovery-") && f.endsWith(".log"))
.map(filename => {
const filePath = path.join(this.logDir, filename);
const stats = fs.statSync(filePath);
return {
filename,
date: stats.mtime,
size: stats.size
};
})
.sort((a, b) => b.date.getTime() - a.date.getTime());
}
/**
* Get a specific log file content
*/
getLogContent(filename: string): string | null {
const filePath = path.join(this.logDir, filename);
if (!fs.existsSync(filePath)) {
return null;
}
return fs.readFileSync(filePath, "utf-8");
}
/**
* Clean up old logs (keep last N)
*/
cleanup(keepCount: number = 20): number {
const logs = this.getAllLogs();
let deleted = 0;
for (let i = keepCount; i < logs.length; i++) {
const filePath = path.join(this.logDir, logs[i].filename);
fs.unlinkSync(filePath);
deleted++;
}
return deleted;
}
}
export const discoveryLogger = new DiscoveryLogger();