Build & Deploy / build-and-push (discord-gateway) (push) Failing after 2m22s
Build & Deploy / build-and-push (backend) (push) Failing after 3m22s
Build & Deploy / build-and-push (proxy) (push) Successful in 1m36s
Build & Deploy / deploy (push) Skipped
- Consolidate all DB schema definitions into packages/shared as single source of truth - Migrate backend from raw SQL to Drizzle ORM across all modules - Extract frontend inline UI into separate component files - Refactor discord-gateway circuitBreaker into conversationState + moderationState - Convert messageStore to Proxy singleton pattern - Add validateBody/validateQuery middleware + Zod schemas for API endpoints - Modernize Docker builds with multi-stage + pnpm deploy - Migrate CI/CD from deployment to image-based pipeline - Remove 60+ unused/dead files (~15K lines) - Update color scheme from sky-blue to teal-cyan - Move DB connection management to @bete/shared/database Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
135 lines
4.5 KiB
Markdown
135 lines
4.5 KiB
Markdown
# Refactoring Backend & Discord-Gateway Services
|
|
|
|
**Date:** 2026-07-27
|
|
**Status:** Draft
|
|
|
|
## Overview
|
|
|
|
Comprehensive refactoring of `services/backend` (4.2k lines) and `services/discord-gateway` (17.9k lines) targeting code consistency, file-size reduction, deduplication, and pattern uniformity.
|
|
|
|
## Scope
|
|
|
|
### Phase 1 — Backend Controller Consistency
|
|
|
|
**Problem:** Two competing controller patterns.
|
|
|
|
- `messages.controller.ts`, `mascot-chat.controller.ts` use convoluted `asyncHandler` inside function body (Gaya A)
|
|
- `voice.controller.ts`, `health.controller.ts` use clean `asyncHandler` decorator (Gaya B)
|
|
|
|
**Fix:** Convert all controllers to **Gaya B** (decorator pattern).
|
|
|
|
Before (Gaya A):
|
|
```ts
|
|
export function handleListMessages(req, res, next) {
|
|
return asyncHandler(async (req, res) => {
|
|
// ...
|
|
})(req, res, next);
|
|
}
|
|
```
|
|
|
|
After (Gaya B):
|
|
```ts
|
|
export const handleListMessages = asyncHandler(async (req, res) => {
|
|
// ...
|
|
});
|
|
```
|
|
|
|
**Files affected:**
|
|
- `modules/messages/messages.controller.ts`
|
|
- `modules/mascot-chat/mascot-chat.controller.ts`
|
|
|
|
### Phase 2 — Backend `response.ts` Cleanup
|
|
|
|
**Problem:** `success()`/`error()` helpers exist but are unused (except health controller).
|
|
|
|
**Fix:** Apply `success()` consistently to all API responses that are successful data returns. Remove `error()` if unused after audit.
|
|
|
|
**Files affected:** All route/service files that `res.json()` data.
|
|
|
|
### Phase 3 — Backend `ws/` Barrel
|
|
|
|
**Problem:** `ws/broadcast.ts`, `ws/redis-bridge.ts`, `ws/server.ts` — no barrel.
|
|
|
|
**Fix:** Add `ws/index.ts` barrel.
|
|
|
|
### Phase 4 — Gateway: Split `moderationPrompt.ts` (1015 lines)
|
|
|
|
**Problem:** Monolithic prompt file mixing all prompt types.
|
|
|
|
**Fix:** Split into:
|
|
- `prompts/text-analysis.ts` — Text moderation prompts
|
|
- `prompts/media-analysis.ts` — Image/video analysis prompts
|
|
- `prompts/stickers.ts` — Sticker analysis prompts
|
|
- `prompts/emojis.ts` — Custom emoji prompts
|
|
- `prompts/system.ts` — System prompt builder and shared helpers
|
|
|
|
### Phase 5 — Gateway: Split `moderationOrchestrator.ts` (955 lines)
|
|
|
|
**Problem:** Entry point that also contains inline text-only batch, media batch, and simple fallback.
|
|
|
|
**Fix:** Extract into:
|
|
- `textBatchProcessor.ts` — All text-only batching logic
|
|
- `mediaBatchProcessor.ts` — All media batching logic
|
|
- `simpleFallback.ts` — The `runSimpleTextFallback` function
|
|
|
|
### Phase 6 — Gateway: Split `mediaAnalysisClient.ts` (826 lines)
|
|
|
|
**Problem:** Cache logic (LRU + phash + DB), download logic (image/video + ffmpeg), and vision LLM in one file.
|
|
|
|
**Fix:** Extract into:
|
|
- `mediaCache.ts` — All caching layers (LRU, phash dedup, DB)
|
|
- `mediaDownloader.ts` — Image/video download, ffmpeg frame extraction
|
|
- `visionAnalyzer.ts` — Vision LLM orchestration
|
|
|
|
### Phase 7 — Gateway: Consolidate `bootstrap.ts`
|
|
|
|
**Problem:** 304-line bootstrap that embeds retention cleanup inline.
|
|
|
|
**Fix:** Extract `startRetentionCleanup` into `app/retention.ts`. Leave event registrations in bootstrap as they're inherently app-wide wiring.
|
|
|
|
### Phase 8 — Gateway: Simplify EventBroadcaster
|
|
|
|
**Problem:** `RedisEventPublisher` wrapping is thin — only adds a `publish` wrapper.
|
|
|
|
**Fix:** Merge `RedisEventPublisher` into `EventBroadcaster` as a private inner detail.
|
|
|
|
### Phase 9 — Cross-cutting: Database initialization dedup
|
|
|
|
**Problem:** Backend (`shared/database/index.ts`) and gateway (`shared/database/drizzle.ts`) have near-identical pool creation and lifecycle code.
|
|
|
|
**Fix:** Extract common pool/drizzle lifecycle into `@bete/shared`:
|
|
```ts
|
|
// packages/shared/src/database/index.ts
|
|
export function createDatabasePool(url: string, opts?: PoolOpts): Pool
|
|
export function createDrizzleClient(pool: Pool): DrizzleClient
|
|
export function closePool(pool: Pool): Promise<void>
|
|
```
|
|
Both services keep their own getDatabase/close wrappers but delegate pool creation to shared.
|
|
|
|
### Phase 10 — Gateway: Consolidate `moderationState.ts` / `conversationState.ts`
|
|
|
|
**Problem:** Two state files with overlapping concerns.
|
|
|
|
**Fix:** Audit both for overlap, merge if significant duplication found.
|
|
|
|
### Phase 11 — Gateway: Redis connection usage audit
|
|
|
|
**Problem:** Multiple independent Redis connections for EventBroadcaster and CommandHandler.
|
|
|
|
**Fix:** Both already need separate connections (Redis pub/sub limits). Document the pattern. No structural change.
|
|
|
|
## Files Changed
|
|
|
|
| Phase | Files | Type |
|
|
|-------|-------|------|
|
|
| 1 | 3 | edit |
|
|
| 2 | ~15 | edit |
|
|
| 3 | 1 | create |
|
|
| 4 | ~6 | split |
|
|
| 5 | ~4 | split |
|
|
| 6 | ~4 | split |
|
|
| 7 | 2 | split |
|
|
| 8 | 2 | refactor |
|
|
| 9 | 2 | refactor |
|
|
| 10 | 1-2 | audit+merge |
|