From 23cb8c890f5591efc9215fecfdbde3b57348b4f3 Mon Sep 17 00:00:00 2001
From: ValueOn AG
Date: Wed, 18 Feb 2026 22:00:33 +0100
Subject: [PATCH] feat: add screenshot list and file serving endpoints for
debug viewer
Co-authored-by: Cursor
---
src/server/httpServer.ts | 58 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 58 insertions(+)
diff --git a/src/server/httpServer.ts b/src/server/httpServer.ts
index 3296728..5ff9ed3 100644
--- a/src/server/httpServer.ts
+++ b/src/server/httpServer.ts
@@ -1,5 +1,7 @@
import express, { Express, Request, Response } from 'express';
import { Server } from 'http';
+import path from 'path';
+import fs from 'fs';
import { logger } from '../utils/logger';
import { config } from '../config';
import { runAuthTests, runSingleVariant, getVariantIds } from '../bot/authTestProcedure';
@@ -184,5 +186,61 @@ export class HttpServer {
res.status(500).json({ error: (error as Error).message });
}
});
+
+ // List screenshots for a session
+ // Filename format: {sessionId}-{step}-{timestamp}.png
+ this._app.get('/api/bot/screenshots/:sessionId', (req: Request, res: Response) => {
+ try {
+ const { sessionId } = req.params;
+ const screenshotDir = config.screenshotDir;
+
+ if (!fs.existsSync(screenshotDir)) {
+ res.json({ screenshots: [] });
+ return;
+ }
+
+ const files = fs.readdirSync(screenshotDir)
+ .filter(f => f.startsWith(`${sessionId}-`) && f.endsWith('.png'))
+ .sort();
+
+ const screenshots = files.map(name => {
+ const stats = fs.statSync(path.join(screenshotDir, name));
+ const withoutExt = name.replace('.png', '');
+ const parts = withoutExt.split('-');
+ // sessionId is a UUID (5 parts joined by -), step follows, timestamp is last
+ const timestamp = parseInt(parts[parts.length - 1], 10) || 0;
+ const step = parts.slice(5, -1).join('-');
+ return { name, step, timestamp, sizeBytes: stats.size };
+ });
+
+ res.json({ screenshots });
+ } catch (error) {
+ logger.error('Error listing screenshots:', error);
+ res.status(500).json({ error: (error as Error).message });
+ }
+ });
+
+ // Serve a single screenshot file
+ this._app.get('/api/bot/screenshots/file/:filename', (req: Request, res: Response) => {
+ try {
+ const { filename } = req.params;
+
+ if (!filename.endsWith('.png') || filename.includes('..') || filename.includes('/') || filename.includes('\\')) {
+ res.status(400).json({ error: 'Invalid filename' });
+ return;
+ }
+
+ const filepath = path.join(config.screenshotDir, filename);
+ if (!fs.existsSync(filepath)) {
+ res.status(404).json({ error: 'Screenshot not found' });
+ return;
+ }
+
+ res.type('image/png').sendFile(path.resolve(filepath));
+ } catch (error) {
+ logger.error('Error serving screenshot:', error);
+ res.status(500).json({ error: (error as Error).message });
+ }
+ });
}
}