docs: describe React dashboard migration state
This commit is contained in:
1 parent
720e5e3e34
commit
12a853fa42
6 files changed
+194
-95
No files matched your search
+11
-11
@@ -27,22 +27,21 @@ The RedditBot web dashboard exposes a REST API for monitoring and controlling th
|
|||||||
|
|
||||||
**Content-Type**: `application/json` (except SSE)
|
**Content-Type**: `application/json` (except SSE)
|
||||||
**Response Format**: JSON
|
**Response Format**: JSON
|
||||||
**Authentication**: Dashboard session cookie or bearer token
|
**Authentication**: Required for cutover; not yet enforced by the checked-in Express host
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Authentication
|
## Authentication
|
||||||
|
|
||||||
The dashboard, API, SSE streams, and private media require the token configured
|
The React cutover requires a single Express authentication boundary covering
|
||||||
through `WEB_DASHBOARD_TOKEN` (preferred) or `web.authToken`. Interactive users
|
the dashboard assets, `/api`, both SSE streams, debug routes, and private media.
|
||||||
exchange the token for an HttpOnly, SameSite=Strict session cookie at
|
That boundary and its token/session configuration are not present in the
|
||||||
`POST /auth/session`. API clients may instead send
|
checked-in server yet. Until they land and their security tests pass, bind the
|
||||||
`Authorization: Bearer <token>`.
|
dashboard to a trusted network and do not expose port 3000 publicly.
|
||||||
|
|
||||||
`GET /auth/login` and `POST /auth/session` are the only unauthenticated
|
The intended contract is an HttpOnly, SameSite=Strict dashboard session for
|
||||||
dashboard routes. A rejected API request returns HTTP 401 with
|
interactive users and bearer-token access for API clients. Treat those details
|
||||||
`{"error":"Dashboard authentication required"}`. Debug endpoints are absent
|
as a migration requirement, not as proof of current protection.
|
||||||
unless `web.debugEnabled` is explicitly enabled.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -1001,7 +1000,8 @@ the complete validated event envelope:
|
|||||||
|
|
||||||
Clients may reconnect with `Last-Event-ID`; the server replays retained events
|
Clients may reconnect with `Last-Event-ID`; the server replays retained events
|
||||||
after that ID in order. The stream sends heartbeats, bounds buffered events and
|
after that ID in order. The stream sends heartbeats, bounds buffered events and
|
||||||
slow-client queues, and requires the same dashboard authentication as the API.
|
slow-client queues. Applying the same authentication boundary as the API is a
|
||||||
|
pending cutover gate.
|
||||||
|
|
||||||
Current event families include bot status, message sent or failed,
|
Current event families include bot status, message sent or failed,
|
||||||
conversation updated, reply received, sync lifecycle and progress, rate-limit
|
conversation updated, reply received, sync lifecycle and progress, rate-limit
|
||||||
|
|||||||
+66
-23
@@ -41,7 +41,7 @@
|
|||||||
| Response Monitoring | Background service checks inbox for user replies |
|
| Response Monitoring | Background service checks inbox for user replies |
|
||||||
| Chat Automation | Reddit chat scraping via shadow DOM traversal, auto-reply support |
|
| Chat Automation | Reddit chat scraping via shadow DOM traversal, auto-reply support |
|
||||||
| Media Management | Download, cache, deduplicate, and sync media from conversations |
|
| Media Management | Download, cache, deduplicate, and sync media from conversations |
|
||||||
| Web Dashboard | Real-time monitoring with SSE log streaming, analytics, and controls |
|
| Web Dashboard | React operator console with typed SSE events, analytics, and guarded controls |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -101,9 +101,9 @@
|
|||||||
│
|
│
|
||||||
▼
|
▼
|
||||||
┌─────────────────────────────────────────────────────────┐
|
┌─────────────────────────────────────────────────────────┐
|
||||||
│ Web Dashboard (Express) │
|
│ React Dashboard + Express API/SSE host │
|
||||||
│ │
|
│ │
|
||||||
│ 25+ API endpoints │ SSE log streaming │ Analytics │
|
│ Typed contracts │ SSE events │ Analytics │
|
||||||
│ Runtime config │ Bot controls │ Media gallery │
|
│ Runtime config │ Bot controls │ Media gallery │
|
||||||
└─────────────────────────────────────────────────────────┘
|
└─────────────────────────────────────────────────────────┘
|
||||||
```
|
```
|
||||||
@@ -161,8 +161,14 @@ RedditBot/
|
|||||||
├── Dockerfile # Container build definition
|
├── Dockerfile # Container build definition
|
||||||
├── package.json # Node.js dependencies & scripts
|
├── package.json # Node.js dependencies & scripts
|
||||||
├── tsconfig.json # TypeScript configuration
|
├── tsconfig.json # TypeScript configuration
|
||||||
|
├── apps/
|
||||||
|
│ └── dashboard/ # React, TypeScript, React Router, and Vite
|
||||||
|
│ ├── src/ # Shell, routes, feature slices, API clients
|
||||||
|
│ └── dist/ # Generated production assets (not committed)
|
||||||
|
├── packages/
|
||||||
|
│ └── api-contracts/ # Shared Zod HTTP and realtime schemas
|
||||||
├── public/
|
├── public/
|
||||||
│ └── index.html # Single-file web dashboard (HTML+CSS+JS)
|
│ └── index.html # Temporary legacy dashboard fallback
|
||||||
├── scripts/
|
├── scripts/
|
||||||
│ ├── inspect-chat-dom.ts # Debug: inspect chat DOM structure
|
│ ├── inspect-chat-dom.ts # Debug: inspect chat DOM structure
|
||||||
│ └── login.ts # Manual browser login helper
|
│ └── login.ts # Manual browser login helper
|
||||||
@@ -196,7 +202,9 @@ RedditBot/
|
|||||||
│ ├── retry.ts # Exponential backoff wrapper (28 lines)
|
│ ├── retry.ts # Exponential backoff wrapper (28 lines)
|
||||||
│ └── image.ts # Image download + base64 + MIME validation (34 lines)
|
│ └── image.ts # Image download + base64 + MIME validation (34 lines)
|
||||||
└── web/
|
└── web/
|
||||||
└── server.ts # Express dashboard + SSE streaming (794 lines)
|
├── server.ts # Express API, SSE, and dashboard host
|
||||||
|
├── dashboard-assets.ts # React-build selection with legacy fallback
|
||||||
|
└── events/ # Typed event hub and Express SSE adapter
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -206,6 +214,7 @@ RedditBot/
|
|||||||
### Core Services
|
### Core Services
|
||||||
|
|
||||||
#### MonitorService (`src/services/monitor.ts`)
|
#### MonitorService (`src/services/monitor.ts`)
|
||||||
|
|
||||||
Discovers new image posts across configured subreddits.
|
Discovers new image posts across configured subreddits.
|
||||||
|
|
||||||
- Shuffles subreddit order each cycle
|
- Shuffles subreddit order each cycle
|
||||||
@@ -214,6 +223,7 @@ Discovers new image posts across configured subreddits.
|
|||||||
- Returns the single newest valid post per cycle
|
- Returns the single newest valid post per cycle
|
||||||
|
|
||||||
#### ProcessorService (`src/services/processor.ts`)
|
#### ProcessorService (`src/services/processor.ts`)
|
||||||
|
|
||||||
Core processing pipeline for each discovered post.
|
Core processing pipeline for each discovered post.
|
||||||
|
|
||||||
1. Downloads image via `ImageService`
|
1. Downloads image via `ImageService`
|
||||||
@@ -224,6 +234,7 @@ Core processing pipeline for each discovered post.
|
|||||||
6. Handles AI refusals gracefully (marks post, tries next)
|
6. Handles AI refusals gracefully (marks post, tries next)
|
||||||
|
|
||||||
#### StrategySelector (`src/services/strategy-selector.ts`)
|
#### StrategySelector (`src/services/strategy-selector.ts`)
|
||||||
|
|
||||||
Implements explore/exploit A/B testing for message variants.
|
Implements explore/exploit A/B testing for message variants.
|
||||||
|
|
||||||
- Tracks response rates per strategy variant
|
- Tracks response rates per strategy variant
|
||||||
@@ -232,6 +243,7 @@ Implements explore/exploit A/B testing for message variants.
|
|||||||
- Selects best variant based on weighted response rates
|
- Selects best variant based on weighted response rates
|
||||||
|
|
||||||
#### ResponseChecker (`src/services/response-checker.ts`)
|
#### ResponseChecker (`src/services/response-checker.ts`)
|
||||||
|
|
||||||
Background service that monitors for user replies.
|
Background service that monitors for user replies.
|
||||||
|
|
||||||
- Navigates Reddit inbox via Playwright
|
- Navigates Reddit inbox via Playwright
|
||||||
@@ -240,6 +252,7 @@ Background service that monitors for user replies.
|
|||||||
- Stores successful message/response pairs for few-shot learning
|
- Stores successful message/response pairs for few-shot learning
|
||||||
|
|
||||||
#### ChatService (`src/services/chat.ts`)
|
#### ChatService (`src/services/chat.ts`)
|
||||||
|
|
||||||
Most complex service - manages Reddit chat interactions.
|
Most complex service - manages Reddit chat interactions.
|
||||||
|
|
||||||
- Scrapes `chat.reddit.com` using JavaScript injection (shadow DOM traversal)
|
- Scrapes `chat.reddit.com` using JavaScript injection (shadow DOM traversal)
|
||||||
@@ -251,6 +264,7 @@ Most complex service - manages Reddit chat interactions.
|
|||||||
- Parameter: `myUsername` is hardcoded (should be from config)
|
- Parameter: `myUsername` is hardcoded (should be from config)
|
||||||
|
|
||||||
#### MediaService (`src/services/media.ts`)
|
#### MediaService (`src/services/media.ts`)
|
||||||
|
|
||||||
Media download, caching, and indexing.
|
Media download, caching, and indexing.
|
||||||
|
|
||||||
- Downloads images from chat conversations
|
- Downloads images from chat conversations
|
||||||
@@ -260,6 +274,7 @@ Media download, caching, and indexing.
|
|||||||
- Tracks download status per conversation
|
- Tracks download status per conversation
|
||||||
|
|
||||||
#### MediaSyncService (`src/services/media-sync.ts`)
|
#### MediaSyncService (`src/services/media-sync.ts`)
|
||||||
|
|
||||||
Periodic background media synchronization.
|
Periodic background media synchronization.
|
||||||
|
|
||||||
- Runs incremental syncs (top 10 chats) or full syncs (all conversations)
|
- Runs incremental syncs (top 10 chats) or full syncs (all conversations)
|
||||||
@@ -268,6 +283,7 @@ Periodic background media synchronization.
|
|||||||
- Exposed via web dashboard endpoints
|
- Exposed via web dashboard endpoints
|
||||||
|
|
||||||
#### AutoReplyService (`src/services/auto-reply.ts`)
|
#### AutoReplyService (`src/services/auto-reply.ts`)
|
||||||
|
|
||||||
Automated reply to Reddit chat conversations.
|
Automated reply to Reddit chat conversations.
|
||||||
|
|
||||||
- Checks for unread messages in cached conversations
|
- Checks for unread messages in cached conversations
|
||||||
@@ -279,6 +295,7 @@ Automated reply to Reddit chat conversations.
|
|||||||
### External Integrations
|
### External Integrations
|
||||||
|
|
||||||
#### RedditApi (`src/reddit/api.ts`)
|
#### RedditApi (`src/reddit/api.ts`)
|
||||||
|
|
||||||
Axios-based Reddit API client.
|
Axios-based Reddit API client.
|
||||||
|
|
||||||
- Fetches posts from subreddits (`/r/{name}/new.json`)
|
- Fetches posts from subreddits (`/r/{name}/new.json`)
|
||||||
@@ -288,6 +305,7 @@ Axios-based Reddit API client.
|
|||||||
- Uses Playwright browser cookies for authenticated requests
|
- Uses Playwright browser cookies for authenticated requests
|
||||||
|
|
||||||
#### OpenRouterService (`src/ai/openrouter.ts`)
|
#### OpenRouterService (`src/ai/openrouter.ts`)
|
||||||
|
|
||||||
OpenRouter API client using OpenAI-compatible SDK.
|
OpenRouter API client using OpenAI-compatible SDK.
|
||||||
|
|
||||||
- `analyzeImage()`: Multi-model image analysis with fallback chain
|
- `analyzeImage()`: Multi-model image analysis with fallback chain
|
||||||
@@ -298,6 +316,7 @@ OpenRouter API client using OpenAI-compatible SDK.
|
|||||||
- Refusal detection via heuristic patterns
|
- Refusal detection via heuristic patterns
|
||||||
|
|
||||||
#### Browser Client (`src/browser/client.ts`)
|
#### Browser Client (`src/browser/client.ts`)
|
||||||
|
|
||||||
Playwright persistent context manager.
|
Playwright persistent context manager.
|
||||||
|
|
||||||
- Browser session persistence (cookies/storage)
|
- Browser session persistence (cookies/storage)
|
||||||
@@ -306,6 +325,7 @@ Playwright persistent context manager.
|
|||||||
- Debug screenshot capture on login failure
|
- Debug screenshot capture on login failure
|
||||||
|
|
||||||
#### MessagePage (`src/browser/message-page.ts`)
|
#### MessagePage (`src/browser/message-page.ts`)
|
||||||
|
|
||||||
Reddit DM compose page automation.
|
Reddit DM compose page automation.
|
||||||
|
|
||||||
- Fills recipient/subject/body fields
|
- Fills recipient/subject/body fields
|
||||||
@@ -316,6 +336,7 @@ Reddit DM compose page automation.
|
|||||||
### Data Layer
|
### Data Layer
|
||||||
|
|
||||||
#### Database Client (`src/database/client.ts`)
|
#### Database Client (`src/database/client.ts`)
|
||||||
|
|
||||||
Better-SQLite3 database initialization.
|
Better-SQLite3 database initialization.
|
||||||
|
|
||||||
- Creates database at configured path
|
- Creates database at configured path
|
||||||
@@ -324,6 +345,7 @@ Better-SQLite3 database initialization.
|
|||||||
- Safe column-addition for schema evolution
|
- Safe column-addition for schema evolution
|
||||||
|
|
||||||
#### Repositories (`src/database/repositories.ts`)
|
#### Repositories (`src/database/repositories.ts`)
|
||||||
|
|
||||||
Three repository classes abstracting SQLite operations.
|
Three repository classes abstracting SQLite operations.
|
||||||
|
|
||||||
- **PostsRepository**: Processed post CRUD, mark sent, update analysis
|
- **PostsRepository**: Processed post CRUD, mark sent, update analysis
|
||||||
@@ -333,6 +355,7 @@ Three repository classes abstracting SQLite operations.
|
|||||||
### Utilities
|
### Utilities
|
||||||
|
|
||||||
#### Logger (`src/utils/logger.ts`)
|
#### Logger (`src/utils/logger.ts`)
|
||||||
|
|
||||||
Winston structured logging.
|
Winston structured logging.
|
||||||
|
|
||||||
- JSON format for log files
|
- JSON format for log files
|
||||||
@@ -340,6 +363,7 @@ Winston structured logging.
|
|||||||
- File logging: `error.log` and combined log
|
- File logging: `error.log` and combined log
|
||||||
|
|
||||||
#### RateLimiter (`src/utils/rate-limiter.ts`)
|
#### RateLimiter (`src/utils/rate-limiter.ts`)
|
||||||
|
|
||||||
Token-bucket rate limiting.
|
Token-bucket rate limiting.
|
||||||
|
|
||||||
- p-queue with concurrency=1
|
- p-queue with concurrency=1
|
||||||
@@ -348,6 +372,7 @@ Token-bucket rate limiting.
|
|||||||
- Timer status exposed to dashboard
|
- Timer status exposed to dashboard
|
||||||
|
|
||||||
#### Retry (`src/utils/retry.ts`)
|
#### Retry (`src/utils/retry.ts`)
|
||||||
|
|
||||||
Exponential backoff wrapper.
|
Exponential backoff wrapper.
|
||||||
|
|
||||||
- p-retry with configurable retries (default 3)
|
- p-retry with configurable retries (default 3)
|
||||||
@@ -355,6 +380,7 @@ Exponential backoff wrapper.
|
|||||||
- Logging on retries and failures
|
- Logging on retries and failures
|
||||||
|
|
||||||
#### Image (`src/utils/image.ts`)
|
#### Image (`src/utils/image.ts`)
|
||||||
|
|
||||||
Image processing utilities.
|
Image processing utilities.
|
||||||
|
|
||||||
- Download via axios
|
- Download via axios
|
||||||
@@ -364,15 +390,33 @@ Image processing utilities.
|
|||||||
### Web Dashboard
|
### Web Dashboard
|
||||||
|
|
||||||
#### WebServer (`src/web/server.ts`)
|
#### WebServer (`src/web/server.ts`)
|
||||||
Express server with 25+ API endpoints.
|
|
||||||
|
Express server with API route modules and SSE transport.
|
||||||
|
|
||||||
- Stats, activity, messages, chats, conversations, media endpoints
|
- Stats, activity, messages, chats, conversations, media endpoints
|
||||||
- Bot controls (start/stop)
|
- Bot controls (start/stop)
|
||||||
- SSE realtime log streaming
|
- Typed resumable events at `/api/events/stream`
|
||||||
|
- Temporary legacy log stream at `/api/logs/stream`
|
||||||
- Analytics: strategy, subreddit, daily trends
|
- Analytics: strategy, subreddit, daily trends
|
||||||
- Runtime config management (GET/PUT)
|
- Runtime config management (GET/PUT)
|
||||||
- Debug endpoints
|
- Debug endpoints
|
||||||
|
|
||||||
|
#### React dashboard (`apps/dashboard`)
|
||||||
|
|
||||||
|
- React Router routes Overview, Conversations, Media, Performance, Operations,
|
||||||
|
and Settings.
|
||||||
|
- TanStack Query owns authoritative HTTP snapshots and mutation state.
|
||||||
|
- Feature-local clients validate responses with schemas from
|
||||||
|
`packages/api-contracts`.
|
||||||
|
- Operations consumes typed dashboard events and invalidates affected query
|
||||||
|
keys; cached content remains visible if the stream reconnects.
|
||||||
|
- Vite writes production assets to `apps/dashboard/dist`.
|
||||||
|
|
||||||
|
The asset resolver prefers the React build and can fall back to
|
||||||
|
`public/index.html`. Wiring that resolver into authenticated Express static and
|
||||||
|
private-media hosting is still a migration gate; the legacy file has not been
|
||||||
|
removed.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Data Flow
|
## Data Flow
|
||||||
@@ -597,7 +641,8 @@ All endpoints are served by the Express server at `http://localhost:{port}`.
|
|||||||
|
|
||||||
| Method | Endpoint | Description |
|
| Method | Endpoint | Description |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| GET | `/api/logs/stream` | SSE realtime log stream |
|
| GET | `/api/events/stream` | Typed resumable SSE event stream |
|
||||||
|
| GET | `/api/logs/stream` | Temporary legacy SSE log stream |
|
||||||
| GET | `/api/logs` | Recent logs (HTTP) |
|
| GET | `/api/logs` | Recent logs (HTTP) |
|
||||||
| GET | `/api/timers` | Rate limiter timer status |
|
| GET | `/api/timers` | Rate limiter timer status |
|
||||||
| POST | `/api/debug/inspect-dom` | Debug: inspect chat DOM structure |
|
| POST | `/api/debug/inspect-dom` | Debug: inspect chat DOM structure |
|
||||||
@@ -744,16 +789,14 @@ Configuration and database creation use factory functions:
|
|||||||
|
|
||||||
### Observer Pattern
|
### Observer Pattern
|
||||||
|
|
||||||
SSE (Server-Sent Events) for realtime log streaming:
|
SSE (Server-Sent Events) carries validated dashboard events:
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// Logger writes to a shared buffer
|
// The transport-agnostic hub assigns IDs and retains a bounded replay window.
|
||||||
logger.info("Message sent to user123");
|
eventHub.publish({ type: "message.sent", payload: { /* validated fields */ } });
|
||||||
|
|
||||||
// WebServer subscribes clients via SSE
|
// Express adapts the hub to resumable SSE.
|
||||||
app.get("/api/logs/stream", (req, res) => {
|
router.get("/events/stream", broadcaster.events);
|
||||||
// Push new log entries to connected clients
|
|
||||||
});
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Circuit Breaker / Retry
|
### Circuit Breaker / Retry
|
||||||
@@ -827,7 +870,7 @@ class RateLimiter {
|
|||||||
|
|
||||||
The project includes full Docker support:
|
The project includes full Docker support:
|
||||||
|
|
||||||
- **`Dockerfile`** - Multi-stage build with Node.js
|
- **`Dockerfile`** - Playwright image with browser, VNC, and frontend/server build tooling
|
||||||
- **`docker-compose.yml`** - Service orchestration
|
- **`docker-compose.yml`** - Service orchestration
|
||||||
- **`.dockerignore`** - Excludes unnecessary files
|
- **`.dockerignore`** - Excludes unnecessary files
|
||||||
|
|
||||||
@@ -849,7 +892,7 @@ Deployment documentation is provided in `DOKPLOY_DEPLOY.md`.
|
|||||||
1. Set up `config.yaml` with target subreddits and settings
|
1. Set up `config.yaml` with target subreddits and settings
|
||||||
2. Set environment variables (or use `.env` file)
|
2. Set environment variables (or use `.env` file)
|
||||||
3. Run `npm install` to install dependencies
|
3. Run `npm install` to install dependencies
|
||||||
4. Run `npm run build` to compile TypeScript
|
4. Run `npm run build` to compile the server and Vite dashboard
|
||||||
5. Run `scripts/login.ts` once to establish browser session
|
5. Run `scripts/login.ts` once to establish browser session
|
||||||
6. Start the bot (via `npm start` or Docker)
|
6. Start the bot (via `npm start` or Docker)
|
||||||
|
|
||||||
@@ -859,10 +902,8 @@ Deployment documentation is provided in `DOKPLOY_DEPLOY.md`.
|
|||||||
|
|
||||||
### Critical
|
### Critical
|
||||||
|
|
||||||
1. **No Tests** - Zero test coverage. Add unit tests for services, especially:
|
1. **Dashboard security cutover** - Wire authenticated React asset, API, SSE,
|
||||||
- `StrategySelector` (complex algorithm)
|
debug, and private-media hosting before exposing the dashboard externally.
|
||||||
- `ChatService` (shadow DOM logic)
|
|
||||||
- `OpenRouterService` (model fallback logic)
|
|
||||||
|
|
||||||
2. **No README.md** - No project documentation for new developers.
|
2. **No README.md** - No project documentation for new developers.
|
||||||
|
|
||||||
@@ -881,9 +922,11 @@ Deployment documentation is provided in `DOKPLOY_DEPLOY.md`.
|
|||||||
|
|
||||||
### Medium Priority
|
### Medium Priority
|
||||||
|
|
||||||
7. **Single-Page Dashboard** - `public/index.html` is ~2000+ lines with embedded CSS/JS. Consider a proper frontend framework.
|
7. **Legacy dashboard fallback** - `public/index.html` remains during migration.
|
||||||
|
Remove it only after authenticated React hosting and feature-parity gates pass.
|
||||||
|
|
||||||
8. **WebServer Size** - 794 lines. Consider splitting routes into separate files.
|
8. **Settings route** - The React route is still a placeholder; runtime and
|
||||||
|
access settings have not yet moved into validated route-backed forms.
|
||||||
|
|
||||||
9. **Config Mutability** - Runtime config updates via `PUT /api/config` modify the in-memory config object. Consider a proper config service with validation on updates.
|
9. **Config Mutability** - Runtime config updates via `PUT /api/config` modify the in-memory config object. Consider a proper config service with validation on updates.
|
||||||
|
|
||||||
|
|||||||
+28
-3
@@ -670,7 +670,32 @@ How the bot automatically replies to chat messages.
|
|||||||
|
|
||||||
## Dashboard Data Flow
|
## Dashboard Data Flow
|
||||||
|
|
||||||
How data flows from services to the web dashboard.
|
How data flows from services to the React operator console.
|
||||||
|
|
||||||
|
### Current React model
|
||||||
|
|
||||||
|
1. Feature-local API clients request authoritative snapshots from `/api/*`.
|
||||||
|
2. Shared Zod schemas in `packages/api-contracts` validate HTTP responses and
|
||||||
|
realtime envelopes at runtime.
|
||||||
|
3. TanStack Query caches snapshots, preserves them during background refresh,
|
||||||
|
and owns mutation pending/error/success state.
|
||||||
|
4. Operations opens `/api/events/stream`. Each typed event invalidates the
|
||||||
|
affected query keys, such as stats, timers, conversations, media, or
|
||||||
|
operations activity.
|
||||||
|
5. Browser `EventSource` reconnects automatically. The client marks cached
|
||||||
|
state stale while reconnecting instead of blanking the UI.
|
||||||
|
|
||||||
|
```text
|
||||||
|
services/repositories -> Express /api snapshots -> Zod -> TanStack Query -> React routes
|
||||||
|
| ^
|
||||||
|
+-> realtime event hub -> /api/events/stream --------+
|
||||||
|
```
|
||||||
|
|
||||||
|
### Legacy fallback model
|
||||||
|
|
||||||
|
The diagram below describes `public/index.html`, which remains only as a
|
||||||
|
compatibility fallback until Express is switched to authenticated React static
|
||||||
|
hosting and private-media protection is verified.
|
||||||
|
|
||||||
```
|
```
|
||||||
┌─────────────────────────────────────────────────────────────────┐
|
┌─────────────────────────────────────────────────────────────────┐
|
||||||
@@ -698,7 +723,7 @@ How data flows from services to the web dashboard.
|
|||||||
│ ├─► Update in-memory config │
|
│ ├─► Update in-memory config │
|
||||||
│ └─► Return updated config │
|
│ └─► Return updated config │
|
||||||
│ │
|
│ │
|
||||||
│ ──── SSE LOG STREAMING ────────────────────────────────────── │
|
│ ──── LEGACY SSE LOG STREAMING ─────────────────────────────── │
|
||||||
│ │
|
│ │
|
||||||
│ GET /api/logs/stream │
|
│ GET /api/logs/stream │
|
||||||
│ ├─► Client connects │
|
│ ├─► Client connects │
|
||||||
@@ -709,7 +734,7 @@ How data flows from services to the web dashboard.
|
|||||||
│ │ └─► Data: { level, message, timestamp } │
|
│ │ └─► Data: { level, message, timestamp } │
|
||||||
│ └─► On disconnect: unsubscribe │
|
│ └─► On disconnect: unsubscribe │
|
||||||
│ │
|
│ │
|
||||||
│ ──── FRONTEND (public/index.html) ─────────────────────────── │
|
│ ──── LEGACY FALLBACK (public/index.html) ──────────────────── │
|
||||||
│ │
|
│ │
|
||||||
│ JavaScript polls API endpoints: │
|
│ JavaScript polls API endpoints: │
|
||||||
│ ├─► /api/stats every 5 seconds │
|
│ ├─► /api/stats every 5 seconds │
|
||||||
|
|||||||
+25
-27
@@ -23,8 +23,8 @@ Complete guide for deploying RedditBot in various environments.
|
|||||||
|
|
||||||
| Requirement | Version | Notes |
|
| Requirement | Version | Notes |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| Node.js | 18+ | LTS recommended |
|
| Node.js | 20.19+ or 22.12+ | Required by the locked Vite toolchain |
|
||||||
| npm | 9+ | Or yarn/pnpm |
|
| npm | Bundled with Node | Use `npm ci` for lockfile-exact installs |
|
||||||
| OpenRouter API Key | - | Sign up at [openrouter.ai](https://openrouter.ai) |
|
| OpenRouter API Key | - | Sign up at [openrouter.ai](https://openrouter.ai) |
|
||||||
| Reddit Account | - | With DM permissions enabled |
|
| Reddit Account | - | With DM permissions enabled |
|
||||||
|
|
||||||
@@ -110,16 +110,21 @@ This will:
|
|||||||
### 4. Start the Bot
|
### 4. Start the Bot
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Development (with TypeScript execution)
|
# Terminal 1: backend with TypeScript execution
|
||||||
npm run dev
|
npm run dev
|
||||||
|
|
||||||
# Production (compiled JavaScript)
|
# Terminal 2: Vite dashboard; proxies /api to port 3000
|
||||||
|
npm run dashboard:dev
|
||||||
|
|
||||||
|
# Production: compile server and dashboard, then start
|
||||||
|
npm run build
|
||||||
npm start
|
npm start
|
||||||
```
|
```
|
||||||
|
|
||||||
### 5. Access Dashboard
|
### 5. Access Dashboard
|
||||||
|
|
||||||
Open http://localhost:3000 in your browser.
|
Open the Vite URL (normally http://localhost:5173) during frontend development.
|
||||||
|
Production and legacy-fallback checks use http://localhost:3000.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -145,28 +150,18 @@ docker run -d \
|
|||||||
reddit-bot:latest
|
reddit-bot:latest
|
||||||
```
|
```
|
||||||
|
|
||||||
### Dockerfile Structure
|
### Dockerfile behavior
|
||||||
|
|
||||||
```dockerfile
|
The image is based on the Playwright Jammy image rather than Alpine because the
|
||||||
# Build stage
|
runtime needs Chromium and its system libraries. It installs Xvfb, x11vnc,
|
||||||
FROM node:18-alpine AS builder
|
noVNC, Fluxbox, and Websockify for interactive Reddit login; installs the locked
|
||||||
WORKDIR /app
|
Node dependencies; copies the repository; and runs `npm run build`.
|
||||||
COPY package*.json ./
|
|
||||||
RUN npm ci
|
|
||||||
COPY . .
|
|
||||||
RUN npm run build
|
|
||||||
|
|
||||||
# Production stage
|
That build produces both `dist/` for the Express server and
|
||||||
FROM node:18-alpine
|
`apps/dashboard/dist/` for Vite assets. Ports 3000 (dashboard/API) and 6080
|
||||||
WORKDIR /app
|
(noVNC) are exposed, and `docker/entrypoint.sh` starts the display services and
|
||||||
COPY --from=builder /app/dist ./dist
|
application. This is a single-stage image and retains development dependencies
|
||||||
COPY --from=builder /app/node_modules ./node_modules
|
needed by the current build/runtime layout.
|
||||||
COPY --from=builder /app/config.yaml ./config.yaml
|
|
||||||
COPY --from=builder /app/package.json ./package.json
|
|
||||||
|
|
||||||
EXPOSE 3000
|
|
||||||
CMD ["node", "dist/index.js"]
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -271,9 +266,12 @@ app:
|
|||||||
|
|
||||||
### Security
|
### Security
|
||||||
|
|
||||||
1. **Network Isolation**: Run behind a reverse proxy with authentication
|
1. **Network Isolation**: Keep the dashboard on a trusted network during the
|
||||||
|
migration cutover
|
||||||
2. **Environment Variables**: Never commit credentials; use secrets management
|
2. **Environment Variables**: Never commit credentials; use secrets management
|
||||||
3. **Dashboard Access**: Restrict to internal network or add auth
|
3. **Dashboard Access**: Authenticated Express hosting for React assets, API,
|
||||||
|
SSE, debug routes, and private media is a required cutover gate, not a
|
||||||
|
currently proven guarantee
|
||||||
4. **Browser Data**: Protect `data/browser/` directory
|
4. **Browser Data**: Protect `data/browser/` directory
|
||||||
|
|
||||||
### Performance
|
### Performance
|
||||||
|
|||||||
+37
-21
@@ -26,8 +26,12 @@ RedditBot/
|
|||||||
├── tsconfig.json # TypeScript config
|
├── tsconfig.json # TypeScript config
|
||||||
├── Dockerfile # Container build
|
├── Dockerfile # Container build
|
||||||
├── docker-compose.yml # Docker Compose
|
├── docker-compose.yml # Docker Compose
|
||||||
|
├── apps/
|
||||||
|
│ └── dashboard/ # React + Vite operator console
|
||||||
|
├── packages/
|
||||||
|
│ └── api-contracts/ # Shared Zod transport contracts
|
||||||
├── public/
|
├── public/
|
||||||
│ └── index.html # Web dashboard (single file)
|
│ └── index.html # Temporary legacy dashboard fallback
|
||||||
├── scripts/
|
├── scripts/
|
||||||
│ ├── login.ts # Manual login helper
|
│ ├── login.ts # Manual login helper
|
||||||
│ └── inspect-chat-dom.ts # DOM inspection debug script
|
│ └── inspect-chat-dom.ts # DOM inspection debug script
|
||||||
@@ -60,8 +64,7 @@ RedditBot/
|
|||||||
│ ├── rate-limiter.ts # Rate limiting
|
│ ├── rate-limiter.ts # Rate limiting
|
||||||
│ ├── retry.ts # Retry wrapper
|
│ ├── retry.ts # Retry wrapper
|
||||||
│ └── image.ts # Image utilities
|
│ └── image.ts # Image utilities
|
||||||
└── web/
|
└── web/ # Express API, events, and dashboard hosting
|
||||||
└── server.ts # Express dashboard server
|
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -101,16 +104,29 @@ npm run login
|
|||||||
### 5. Start Development Server
|
### 5. Start Development Server
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Development mode (hot reload with tsx)
|
# Terminal 1: backend reload with tsx
|
||||||
npm run dev
|
npm run dev
|
||||||
|
|
||||||
# Production mode (compiled JS)
|
# Terminal 2: Vite frontend with /api proxied to port 3000
|
||||||
|
npm run dashboard:dev
|
||||||
|
|
||||||
|
# Production build: server plus apps/dashboard/dist
|
||||||
npm run build && npm start
|
npm run build && npm start
|
||||||
```
|
```
|
||||||
|
|
||||||
### 6. Access Dashboard
|
### 6. Access Dashboard
|
||||||
|
|
||||||
Open http://localhost:3000
|
During frontend development, open the URL printed by Vite (normally
|
||||||
|
http://localhost:5173). The Express dashboard remains on http://localhost:3000
|
||||||
|
for production/fallback verification.
|
||||||
|
|
||||||
|
Useful frontend checks:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run dashboard:typecheck
|
||||||
|
npm run dashboard:build
|
||||||
|
npx vitest run tests/dashboard
|
||||||
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -277,12 +293,14 @@ setInterval(() => {
|
|||||||
}, intervalMs);
|
}, intervalMs);
|
||||||
```
|
```
|
||||||
|
|
||||||
### 4. Add to Web Dashboard (Optional)
|
### 4. Add to the Web API and Dashboard (Optional)
|
||||||
|
|
||||||
If the service needs API endpoints, add them in `src/web/server.ts`:
|
If the service needs an API, add a focused router under `src/web/routes/` and
|
||||||
|
mount it from `src/web/server.ts`. Define its transport schema in
|
||||||
|
`packages/api-contracts`; do not expose repository rows directly.
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
server.app.get('/api/new-service', async (req, res) => {
|
router.get('/new-service', async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const result = await newService.execute(req.query.input as string);
|
const result = await newService.execute(req.query.input as string);
|
||||||
res.json(result);
|
res.json(result);
|
||||||
@@ -292,6 +310,9 @@ server.app.get('/api/new-service', async (req, res) => {
|
|||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Add frontend behavior under `apps/dashboard/src/features/<feature>/` and drive
|
||||||
|
it with Testing Library and MSW tests under `tests/dashboard/`.
|
||||||
|
|
||||||
### 5. Update Config (If Needed)
|
### 5. Update Config (If Needed)
|
||||||
|
|
||||||
Add new configuration options to:
|
Add new configuration options to:
|
||||||
@@ -498,17 +519,12 @@ Reddit frequently updates their `faceplate-*` components. Shadow DOM traversal c
|
|||||||
|
|
||||||
**Mitigation**: Use `scripts/inspect-chat-dom.ts` to debug when selectors change.
|
**Mitigation**: Use `scripts/inspect-chat-dom.ts` to debug when selectors change.
|
||||||
|
|
||||||
### No Test Coverage
|
### Dashboard Migration Boundary
|
||||||
|
|
||||||
The project has no automated tests. Any changes should be manually tested.
|
The React feature slices live in `apps/dashboard`, but `public/index.html`
|
||||||
|
remains as a compatibility fallback. Do not delete it until authenticated
|
||||||
|
Express hosting, private-media protection, and browser parity checks are proven.
|
||||||
|
|
||||||
**Recommendation**: Add tests for critical paths:
|
The repository has automated service, contract, component, and transport tests.
|
||||||
- StrategySelector algorithm
|
Manual browser verification is still required for Reddit DOM changes and the
|
||||||
- ChatService shadow DOM parsing
|
final dashboard cutover.
|
||||||
- OpenRouterService model fallback
|
|
||||||
|
|
||||||
### Single-File Dashboard
|
|
||||||
|
|
||||||
The entire dashboard is in `public/index.html` (~2000+ lines). Making changes requires editing a single large file.
|
|
||||||
|
|
||||||
**Recommendation**: Consider migrating to a proper frontend framework for maintainability.
|
|
||||||
+27
-10
@@ -3,8 +3,24 @@
|
|||||||
## Outcome
|
## Outcome
|
||||||
|
|
||||||
Replace the inline dashboard in `public/index.html` with a typed, accessible React
|
Replace the inline dashboard in `public/index.html` with a typed, accessible React
|
||||||
application without disrupting the bot worker or its Express API. Express remains
|
application without disrupting the bot worker or its Express API. At cutover,
|
||||||
the authenticated API, SSE, private-media, and static-asset host.
|
Express will remain the authenticated API, SSE, private-media, and static-asset
|
||||||
|
host.
|
||||||
|
|
||||||
|
## Current status
|
||||||
|
|
||||||
|
The React application and production build are present. The current routes are
|
||||||
|
Overview, Conversations (including route-addressable detail), Media,
|
||||||
|
Performance, Operations, and a Settings placeholder. Shared HTTP and realtime
|
||||||
|
schemas live in `packages/api-contracts`, and `/api/events/stream` supplies the
|
||||||
|
typed SSE transport.
|
||||||
|
|
||||||
|
`npm run build` compiles the server and writes the Vite application to
|
||||||
|
`apps/dashboard/dist`. `src/web/dashboard-assets.ts` can select that build and
|
||||||
|
fall back to `public/index.html`, but the checked-in Express server has not yet
|
||||||
|
completed that authenticated static-hosting cutover. Until it does, the legacy
|
||||||
|
page remains the served fallback and the dashboard must not be exposed to an
|
||||||
|
untrusted network.
|
||||||
|
|
||||||
The migration is complete only when the legacy dashboard can be removed without
|
The migration is complete only when the legacy dashboard can be removed without
|
||||||
losing a documented capability and the full test, type-check, production-build,
|
losing a documented capability and the full test, type-check, production-build,
|
||||||
@@ -34,19 +50,20 @@ The application shell provides a responsive sidebar or drawer with these routes:
|
|||||||
| `/media` | Private blurred-by-default gallery and sync controls |
|
| `/media` | Private blurred-by-default gallery and sync controls |
|
||||||
| `/performance` | Trends, strategies, subreddits, and successful examples |
|
| `/performance` | Trends, strategies, subreddits, and successful examples |
|
||||||
| `/operations` | Domain activity, background jobs, and diagnostics |
|
| `/operations` | Domain activity, background jobs, and diagnostics |
|
||||||
| `/operations/logs` | Filterable bounded realtime technical console |
|
| `/settings` | Placeholder for runtime and access settings |
|
||||||
| `/settings/*` | Monitoring, limits, behaviour, AI, and access settings |
|
|
||||||
|
|
||||||
The shell always surfaces bot state, dry-run state, Reddit session state,
|
Overview surfaces bot state, dry-run state, cooldowns, timers, and the guarded
|
||||||
realtime connection state, cooldowns, active sync progress, notifications, and
|
start/stop control. Operations owns connection health, sync state, activity,
|
||||||
the guarded start/stop control.
|
and the bounded technical console.
|
||||||
|
|
||||||
## Realtime model
|
## Realtime model
|
||||||
|
|
||||||
HTTP snapshots are authoritative. A typed SSE event stream updates or invalidates
|
HTTP snapshots are authoritative. A typed SSE event stream updates or invalidates
|
||||||
the relevant query cache. Every event has an ID, type, occurrence timestamp, and
|
the relevant query cache. Every event has an ID, type, occurrence timestamp, and
|
||||||
validated payload. The stream supports bounded replay and `Last-Event-ID` resume.
|
validated payload. The stream supports bounded replay and `Last-Event-ID` resume.
|
||||||
After reconnection the client refetches authoritative snapshots to repair gaps.
|
Relevant events invalidate TanStack Query keys so the next HTTP read remains
|
||||||
|
authoritative. Connection loss marks cached state stale while `EventSource`
|
||||||
|
reconnects.
|
||||||
|
|
||||||
Required event families are bot status, message sent or failed, conversation
|
Required event families are bot status, message sent or failed, conversation
|
||||||
updated, reply received, sync lifecycle and progress, rate-limit state, Reddit
|
updated, reply received, sync lifecycle and progress, rate-limit state, Reddit
|
||||||
@@ -91,8 +108,8 @@ The legacy dashboard remains available until replacement behavior is proven.
|
|||||||
- No production frontend or backend code uses `any` or `console.log`.
|
- No production frontend or backend code uses `any` or `console.log`.
|
||||||
- Mutations display the confirmed server outcome and do not rely on optimistic
|
- Mutations display the confirmed server outcome and do not rely on optimistic
|
||||||
success for operational controls.
|
success for operational controls.
|
||||||
- Production builds are deterministic and the Docker image serves only generated
|
- Production builds are deterministic. Serving only generated frontend assets
|
||||||
frontend assets plus authenticated private content.
|
plus authenticated private content remains a cutover gate.
|
||||||
|
|
||||||
## Cutover evidence
|
## Cutover evidence
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user