diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..6fad477 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,10 @@ +node_modules +dist +.git +.env +data/browser-session +data/reddit-bot.db +data/reddit-bot.db-journal +test-results +playwright-report +blob-report diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..9bf3609 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,30 @@ +# Use the official Playwright image which includes all browser dependencies +FROM mcr.microsoft.com/playwright:v1.57.0-jammy + +# Set working directory +WORKDIR /app + +# Copy package files +COPY package*.json ./ + +# Install dependencies (including devDependencies for building) +RUN npm ci + +# Copy source code +COPY . . + +# Build the TypeScript project +RUN npm run build + +# Prune dev dependencies to reduce image size (optional, but good practice) +# Note: we need to keep some deps if they are runtime requirements +# npm prune --production + +# Create data directory +RUN mkdir -p data + +# Expose the port defined in config (default 3000) +EXPOSE 3000 + +# Start the application using the compiled code +CMD ["node", "dist/index.js"] diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..5cd5fe9 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,17 @@ +version: '3.8' + +services: + reddit-bot: + build: . + container_name: reddit-bot + restart: unless-stopped + # Mount the data directory to persist the database and browser session + volumes: + - ./data:/app/data + # Environment variables + environment: + - OPENROUTER_API_KEY=${OPENROUTER_API_KEY} + # You can add other overrides if supported by the app or if you modify config loading + # - HEADLESS=true + ports: + - "3000:3000" diff --git a/src/config/index.ts b/src/config/index.ts index e70ea6a..2d74556 100644 --- a/src/config/index.ts +++ b/src/config/index.ts @@ -56,6 +56,12 @@ export function loadConfig(): Config { } rawConfig.openrouter.apiKey = process.env.OPENROUTER_API_KEY || rawConfig.openrouter.apiKey; + // Override browser settings from environment + if (process.env.BROWSER_HEADLESS !== undefined) { + if (!rawConfig.browser) rawConfig.browser = {}; + rawConfig.browser.headless = process.env.BROWSER_HEADLESS === 'true'; + } + if (!rawConfig.openrouter.apiKey) { throw new Error('OPENROUTER_API_KEY environment variable is required'); } diff --git a/src/web/server.ts b/src/web/server.ts index ce73d58..41a1458 100644 --- a/src/web/server.ts +++ b/src/web/server.ts @@ -30,10 +30,8 @@ export class WebServer { private app: express.Application; private server: Server | null = null; private activityLog: ActivityItem[] = []; - private botStats = { - isRunning: true, - messagesThisHour: 0, - }; + private botRunning = false; // Start stopped + private messagesThisHour = 0; constructor( private port: number, @@ -75,25 +73,45 @@ export class WebServer { res.json(successful); }); + // API: Start bot + this.app.post('/api/start', (req, res) => { + this.botRunning = true; + this.logActivity('sent', 'Bot started by user'); + this.logger.info('Bot started via web UI'); + res.json({ success: true, running: true }); + }); + + // API: Stop bot + this.app.post('/api/stop', (req, res) => { + this.botRunning = false; + this.logActivity('skipped', 'Bot stopped by user'); + this.logger.info('Bot stopped via web UI'); + res.json({ success: true, running: false }); + }); + // Serve dashboard for all other routes this.app.get('*', (req, res) => { res.sendFile(path.join(process.cwd(), 'public', 'index.html')); }); } + isRunning(): boolean { + return this.botRunning; + } + 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, + isRunning: this.botRunning, dryRunMode: this.config.browser.dryRun, totalProcessed: this.getTotalProcessed(), totalMessagesSent: totalSent, totalResponses, responseRate: totalSent > 0 ? Math.round((totalResponses / totalSent) * 100) : 0, - messagesThisHour: this.botStats.messagesThisHour, + messagesThisHour: this.messagesThisHour, maxMessagesPerHour: this.config.rateLimit.maxMessagesPerHour, subreddits: this.config.reddit.subreddits, targetGenders: this.config.reddit.targetGenders, @@ -122,7 +140,7 @@ export class WebServer { } updateStats(messagesThisHour: number): void { - this.botStats.messagesThisHour = messagesThisHour; + this.messagesThisHour = messagesThisHour; } start(): void {