This commit is contained in:
jjbrunton committed 2026-01-04 19:16:31 +00:00
commit c552956516
23 files changed
+5193

No files matched your search

+140
View File
@@ -0,0 +1,140 @@
import express from 'express';
import path from 'path';
import type { Server } from 'http';
import type { Logger } from '../utils/logger.js';
import type { PostsRepository, MessagesRepository, SuccessfulMessagesRepository } from '../database/repositories.js';
import type { Config } from '../config/index.js';
export interface BotStats {
isRunning: boolean;
dryRunMode: boolean;
totalProcessed: number;
totalMessagesSent: number;
totalResponses: number;
responseRate: number;
messagesThisHour: number;
maxMessagesPerHour: number;
subreddits: string[];
targetGenders: string[];
recentActivity: ActivityItem[];
}
export interface ActivityItem {
timestamp: number;
type: 'sent' | 'skipped' | 'response' | 'error';
message: string;
recipient?: string;
}
export class WebServer {
private app: express.Application;
private server: Server | null = null;
private activityLog: ActivityItem[] = [];
private botStats = {
isRunning: true,
messagesThisHour: 0,
};
constructor(
private port: number,
private config: Config,
private postsRepo: PostsRepository,
private messagesRepo: MessagesRepository,
private successfulMessagesRepo: SuccessfulMessagesRepository,
private logger: Logger
) {
this.app = express();
this.setupRoutes();
}
private setupRoutes(): void {
// Serve static files
this.app.use(express.static(path.join(process.cwd(), 'public')));
this.app.use(express.json());
// API: Get stats
this.app.get('/api/stats', (req, res) => {
const stats = this.getStats();
res.json(stats);
});
// API: Get recent activity
this.app.get('/api/activity', (req, res) => {
res.json(this.activityLog.slice(-50).reverse());
});
// API: Get recent messages
this.app.get('/api/messages', (req, res) => {
const messages = this.messagesRepo.getRecent(20);
res.json(messages);
});
// API: Get successful messages (ones that got responses)
this.app.get('/api/successful', (req, res) => {
const successful = this.successfulMessagesRepo.getRecent(10);
res.json(successful);
});
// Serve dashboard for all other routes
this.app.get('*', (req, res) => {
res.sendFile(path.join(process.cwd(), 'public', 'index.html'));
});
}
private getStats(): BotStats {
const messages = this.messagesRepo.getRecent(1000);
const totalSent = messages.length;
const totalResponses = messages.filter((m) => m.gotResponse).length;
return {
isRunning: this.botStats.isRunning,
dryRunMode: this.config.browser.dryRun,
totalProcessed: this.getTotalProcessed(),
totalMessagesSent: totalSent,
totalResponses,
responseRate: totalSent > 0 ? Math.round((totalResponses / totalSent) * 100) : 0,
messagesThisHour: this.botStats.messagesThisHour,
maxMessagesPerHour: this.config.rateLimit.maxMessagesPerHour,
subreddits: this.config.reddit.subreddits,
targetGenders: this.config.reddit.targetGenders,
recentActivity: this.activityLog.slice(-10).reverse(),
};
}
private getTotalProcessed(): number {
// This is a simple count - in production you'd query the DB
return this.activityLog.filter((a) => a.type === 'sent' || a.type === 'skipped').length;
}
logActivity(type: ActivityItem['type'], message: string, recipient?: string): void {
const item: ActivityItem = {
timestamp: Date.now(),
type,
message,
recipient,
};
this.activityLog.push(item);
// Keep only last 200 items
if (this.activityLog.length > 200) {
this.activityLog = this.activityLog.slice(-200);
}
}
updateStats(messagesThisHour: number): void {
this.botStats.messagesThisHour = messagesThisHour;
}
start(): void {
this.server = this.app.listen(this.port, () => {
this.logger.info(`Web dashboard running at http://localhost:${this.port}`);
});
}
stop(): void {
if (this.server) {
this.server.close();
this.server = null;
}
}
}