- Extract services into services/{frontend,backend,discord-gateway}
- Create packages/shared/ for shared logger, errors, utils, types
- Setup Modular MVC pattern in backend (controller→service→repository)
- Setup event-driven architecture in discord-gateway with Redis pub/sub
- Move Docker files to infra/docker/ with per-service Dockerfiles
- Update docker-compose.yml to use Traefik-only routing (no port exposes)
- Update GitHub Actions deploy workflow for multi-service matrix build
- Fix all import paths and resolve type errors across all services
- All 3 services pass tsc --noEmit clean
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
8.2 KiB
8.2 KiB
Discord Moderation Watcher Bot - Microservices Architecture
Quick Start
Prerequisites
- Docker & Docker Compose
- Node.js 20+
- pnpm 11+
- Discord bot token
- OpenAI API key
Environment Setup
Create .env.local in the root directory:
# Discord Configuration
DISCORD_TOKEN=your_discord_token_here
MONITOR_GUILD_ID=your_guild_id_here
# AI Configuration
AI_LLM_API_KEY=your_openai_api_key_here
# Optional: Database URL (defaults to PostgreSQL in Docker)
DATABASE_URL=postgresql://postgres:postgres@localhost:5432/bete
# Optional: Redis URL (defaults to Redis in Docker)
REDIS_URL=redis://localhost:6379
Local Development with Docker Compose
# Start all services
docker-compose up -d
# View logs
docker-compose logs -f
# Stop all services
docker-compose down
# Rebuild services
docker-compose up -d --build
Services will be available at:
- Frontend: http://localhost:5173
- Backend API: http://localhost:3001
- Backend WebSocket: ws://localhost:3001
- PostgreSQL: localhost:5432
- Redis: localhost:6379
Local Development without Docker
# Install dependencies
pnpm install
# Run database migrations
pnpm run db:migrate
# Start all services in separate terminals
# Terminal 1: Backend
cd services/backend
pnpm run dev
# Terminal 2: Discord Gateway
cd services/discord-gateway
pnpm run dev
# Terminal 3: Frontend
cd services/frontend
pnpm run dev:web
Architecture Overview
3 Independent Microservices
1. Frontend Service (services/frontend/)
- Tech: React 19, Vite, TanStack Query, WebSocket
- Port: 5173 (dev) / served by Backend (prod)
- Responsibilities:
- Dashboard UI (analytics, messages, voice, media)
- Real-time WebSocket connection to Backend
- API calls to Backend REST endpoints
- State management (React Query)
2. Backend Service (services/backend/)
- Tech: Express, Drizzle ORM, PostgreSQL, Redis
- Port: 3001
- Responsibilities:
- REST API endpoints (
/api/*) - WebSocket server for real-time updates
- Database operations (PostgreSQL)
- Event orchestration from Discord Gateway
- Static file serving (built Frontend)
- Admin authentication
- REST API endpoints (
Modular MVC Structure:
services/backend/src/
├── shared/
│ ├── database/ → Drizzle ORM setup
│ ├── config/ → Environment config
│ ├── errors/ → Custom error classes
│ ├── middlewares/ → Express middlewares
│ ├── logger/ → Logging utilities
│ └── utils/ → Shared utilities
├── modules/
│ ├── messages/ → Message CRUD
│ ├── analytics/ → Analytics queries
│ ├── media/ → Media management
│ ├── voice/ → Voice recordings
│ └── health/ → Health checks
└── index.ts
3. Discord Gateway Service (services/discord-gateway/)
- Tech: discord.js-selfbot-v13, @discordjs/voice, OpenAI API
- Port: None (internal service, no HTTP)
- Responsibilities:
- Discord client connection
- Message capture (create/edit/delete)
- Voice channel recording
- AI moderation analysis
- Attachment upload
- Event publishing to Backend (Redis pub/sub)
Modular MVC Structure:
services/discord-gateway/src/
├── shared/
│ ├── database/ → Drizzle ORM setup
│ ├── config/ → Environment config
│ ├── errors/ → Custom error classes
│ ├── logger/ → Logging utilities
│ └── utils/ → Shared utilities
├── modules/
│ ├── message-capture/ → Message listeners
│ ├── voice-recording/ → Voice recording
│ ├── ai-moderation/ → AI analysis
│ ├── attachment-upload/ → File uploads
│ └── event-broadcaster/ → Redis pub/sub
└── index.ts
Shared Package (packages/shared/)
- Types: Common interfaces and data models
- Errors: Custom error classes
- Logger: Pino logger setup
- Utils: Pagination, validation, helpers
Communication Patterns
Frontend ↔ Backend:
- REST API:
GET/POST /api/*(HTTP) - WebSocket: Real-time updates (JSON messages)
- Auth: Admin password header
Backend ↔ Discord Gateway:
- Redis pub/sub (low-latency, decoupled)
- Events:
discord:message:created,discord:voice:started, etc. - Backend subscribes and broadcasts to Frontend via WebSocket
Shared Resources:
- PostgreSQL: Both Backend and Discord Gateway
- Redis: Pub/sub and caching
Development Workflow
Adding a New API Endpoint
-
Create module structure (if new feature):
mkdir -p services/backend/src/modules/feature/{routes,controllers,services,repositories,schemas} -
Define schema (
feature.schema.ts):import { z } from 'zod'; export const createFeatureSchema = z.object({ name: z.string().min(1), description: z.string().optional(), }); -
Create repository (
feature.repository.ts):export async function createFeature(data: CreateFeatureInput) { return db.insert(features).values(data).returning(); } -
Create service (
feature.service.ts):export async function createFeatureService(data: CreateFeatureInput) { // Business logic, validation, orchestration return createFeature(data); } -
Create controller (
feature.controller.ts):export async function createFeatureController(req: Request, res: Response) { const data = createFeatureSchema.parse(req.body); const result = await createFeatureService(data); res.json(result); } -
Create route (
feature.route.ts):router.post('/features', createFeatureController);
Adding a New Discord Event
-
Create module in
services/discord-gateway/src/modules/event-name/ -
Register listener in
index.ts:client.on('eventName', async (data) => { await handleEvent(data); publishEvent('discord:event:name', data); }); -
Publish to Redis:
import { redis } from '../shared/redis'; redis.publish('discord:event:name', JSON.stringify(data)); -
Subscribe in Backend (
services/backend/src/ws/server.ts):redis.subscribe('discord:event:name', (message) => { broadcastToClients({ type: 'event_name', data: JSON.parse(message) }); });
Testing
Run All Tests
pnpm run test
Run Tests for Specific Service
cd services/backend
pnpm run test
cd services/discord-gateway
pnpm run test
Type Checking
pnpm run typecheck
Linting
pnpm run lint
Deployment
Build Docker Images
docker-compose build
Push to Container Registry
docker tag bete-backend ghcr.io/username/bete-backend:latest
docker push ghcr.io/username/bete-backend:latest
Deploy to Production
See .github/workflows/deploy.yml for GitHub Actions CI/CD pipeline.
Troubleshooting
Backend can't connect to PostgreSQL
# Check PostgreSQL is running
docker-compose ps postgres
# Check connection string
echo $DATABASE_URL
# Verify credentials
psql -h localhost -U postgres -d bete
Discord Gateway not receiving events
# Check Redis connection
redis-cli ping
# Check Discord token
echo $DISCORD_TOKEN
# View logs
docker-compose logs discord-gateway
Frontend can't connect to Backend
# Check Backend is running
curl http://localhost:3001/health
# Check WebSocket connection
# Open browser DevTools → Network → WS
API Documentation
Health Check
GET /health
Messages
GET /api/messages?channel=<id>&type=text|image
POST /api/messages (admin only)
Analytics
GET /api/analytics
Voice Recordings
GET /api/recordings
WebSocket Events
message_created
message_updated
message_deleted
attachment_uploaded
user_state
Contributing
- Create a feature branch
- Make changes following Modular MVC pattern
- Run tests and linting
- Submit PR with description
License
MIT