diff --git a/docs/04_solution_strategy.md b/docs/04_solution_strategy.md index f2d1dec..7226222 100644 --- a/docs/04_solution_strategy.md +++ b/docs/04_solution_strategy.md @@ -2,32 +2,34 @@ ## Technology Decisions -| Decision | Choice | Rationale | -| ---------------- | -------------------- | --------------------------------------------------------------------------------------- | -| Bot framework | Discord.js (Node.js) | Mature library, strong type support, rich event model | -| Database | PostgreSQL | Reliable, supports JSONB for flexible user state, well-suited for mileage/role tracking | -| Frontend (Admin) | React | Team familiarity, rich ecosystem for dashboards | -| Hosting | Coolify | Easy deployment for Node.js with Nixpacks. | -| Auth | Discord OAuth2 | No custom auth needed, seamless web/bot integration | +| Decision | Choice | Rationale | +| -------------- | ----------------------------------------- | -------------------------------------------------------------------------------------- | +| Bot runtime | Node.js + TypeScript + discord.js | Strong ecosystem, typed Discord interactions, shared language across backend/dashboard | +| Persistence | PostgreSQL (`pg`) | Reliable relational storage for mileage, schedules, and settings | +| Admin API | Express | Lightweight routing and middleware model for operational endpoints | +| Dashboard | React + Vite + TypeScript | Fast iteration and typed API integration | +| Authentication | Bearer token (admin API) + Discord OAuth2 | Secure machine-to-machine admin calls plus user identity bridge | +| Deployment | Coolify + Gitea Actions | Simple CI/CD path with webhook-triggered production rollout | -## Top-Level Decomposition +## Architectural Strategy -The system follows a **modular monolith** pattern with clear separation: +System uses modular monolith strategy in single backend process: -- **Core Bot** — command handlers, event listeners, Discord Gateway logic -- **Content Syndicator** — polling adapters for YouTube/IG/TikTok, webhook dispatch -- **Event Manager** — FIFO queue for stage/speaker management -- **Mileage Engine** — scoring logic, persistence, state sync with web app -- **Admin API** — RESTful endpoints for bot config, content scheduling, analytics +- Bot core orchestrates Discord gateway and command/event handling. +- Domain services encapsulate call-sheet onboarding, tour queue, mileage, and dailies. +- Operational interfaces exposed through Admin API and OAuth bridge. +- Shared configuration layer centralizes environment parsing and validation. +- Separate dashboard SPA consumes API without direct DB access. -## Key Quality Goals +## Quality Strategies -- **Availability** — Stateless bot instances scale horizontally behind load balancer -- **Consistency** — Write-ahead logging for mileage transactions; periodic state reconciliation with web app -- **Extensibility** — Adapter pattern for content sources: implement interface, register, deploy +- Availability: graceful startup/shutdown, service initialization guards, process-level health endpoint. +- Consistency: transactional mileage writes, strict DB row guards, idempotent schedule updates. +- Security: token-based admin auth, OAuth code exchange on server side, no token logging. +- Extensibility: adapter interface for new dailies sources and isolated service modules. +- Operability: CI checks (lint/build/test) for bot and dashboard before deploy trigger. -## Organizational Decisions +## Trade-offs -- One-person or small-team development per project scope -- CI/CD via Gitea Actions: lint → test → deploy -- Feature toggles for admin dashboard rollout +- In-memory OAuth sessions reduce complexity but require re-authentication after process restart. +- Modular monolith minimizes operational overhead now, while preserving service boundaries for future extraction. diff --git a/docs/05_building_block_view.md b/docs/05_building_block_view.md index b7ed962..9be47a6 100644 --- a/docs/05_building_block_view.md +++ b/docs/05_building_block_view.md @@ -3,39 +3,53 @@ ## Whitebox Overall System (Level 1) ```txt -┌──────────────────────────────────────────────────────┐ -│ omo-bot │ -├──────────────────────────────────────────────────────┤ -│ ┌──────────────────┐ ┌─────────────────────────┐ │ -│ │ Command Layer │ │ Event Listener Layer │ │ -│ │ /sign-up, /map │ │ member_join, reaction │ │ -│ └────────┬─────────┘ └───────────┬─────────────┘ │ -│ │ │ │ -│ ▼ ▼ │ -│ ┌──────────────────────────────────────────────┐ │ -│ │ Service Layer │ │ -│ │ ┌──────────┐ ┌──────────┐ ┌──────────────┐ │ │ -│ │ │ Event │ │ Mileage │ │ Content │ │ │ -│ │ │ Manager │ │ Engine │ │ Syndicator │ │ │ -│ │ └──────────┘ └──────────┘ └──────────────┘ │ │ -│ └───────────────────┬──────────────────────────┘ │ -│ │ │ -│ ▼ │ -│ ┌──────────────────────────────────────────────┐ │ -│ │ Data / Persistence Layer │ │ -│ │ PostgreSQL DB + Discord API Wrapper │ │ -│ └──────────────────────────────────────────────┘ │ -└──────────────────────────────────────────────────────┘ +┌────────────────────────────────────────────────────────────┐ +│ omo-bot │ +├────────────────────────────────────────────────────────────┤ +│ Discord Gateway Runtime │ +│ ├─ Command handlers (/ping, /call-sheet, /sign-up, /dailies) │ +│ ├─ Reaction/member event handlers │ +│ └─ Service orchestration (`startBot`) │ +│ │ +│ Core Services │ +│ ├─ Call Sheet Service │ +│ ├─ Tour Schedule Engine │ +│ ├─ Mileage Engine │ +│ ├─ Dailies Service (adapters + dispatcher) │ +│ ├─ OAuth Bridge Service │ +│ └─ Configuration Database Service │ +│ │ +│ Interfaces │ +│ ├─ Admin API (Express) │ +│ └─ Health endpoint │ +└──────────────────────────────┬─────────────────────────────┘ + │ + ▼ + ┌────────────────────────────┐ + │ PostgreSQL + Discord APIs │ + └────────────────────────────┘ ``` -### Black Box Descriptions +## Building Blocks and Responsibilities -| Building Block | Responsibility | -| ------------------ | ------------------------------------------------------------------------------------------ | -| Command Layer | Register and handle slash commands (`/sign-up`, `/map`, `/help`); validate permissions | -| Event Listener | Subscribe to Discord Gateway events (member join, reaction add, voice state change) | -| Event Manager | Manage FIFO queue for voice/stage speakers; schedule "Tour Stop" events via Discord API | -| Mileage Engine | Score user interactions, persist to DB, trigger role upgrades, sync with web app | -| Content Syndicator | Poll YouTube/IG/TikTok APIs; format rich embeds; dispatch to Discord channels via webhooks | -| Admin API | Expose REST endpoints for dashboard (configuration, content schedule, engagement stats) | -| DB Layer | PostgreSQL connection pool, migrations, query builders | +| Building Block | Responsibility | +| ------------------------------- | ---------------------------------------------------------------------------------- | +| `src/bot.ts` | Runtime bootstrap, command registration, service lifecycle, graceful shutdown | +| `src/call-sheet.ts` | Reaction-role onboarding, grouped role exclusivity, welcome flow hooks | +| `src/tour-schedule.ts` | Queue state, join/leave/advance operations, stage speaker management | +| `src/mileage.ts` | Event scoring, persistence, mileage aggregation, role tier synchronization | +| `src/dailies/service.ts` | Polling scheduler, adapter orchestration, webhook dispatch loop | +| `src/oauth-bridge.ts` | Discord OAuth code exchange, user fetch, temporary session cache, optional sync | +| `src/configuration-database.ts` | Settings/schedules CRUD and engagement snapshot persistence | +| `src/admin-api.ts` | Authenticated operational endpoints for config, queue, stats, OAuth, and config DB | +| `admin-dashboard/src/*` | Browser UI for OAuth connect flow, API calls, and analytics views | + +## Level 2 (Admin API) + +Admin API route groups: + +- Health and runtime config (`/health`, `/admin/config`) +- Tour schedule operations (`/admin/schedule`, `/admin/schedule/clear`) +- Engagement statistics (`/admin/stats`, `/admin/db/engagement/latest`) +- Configuration DB CRUD (`/admin/db/settings`, `/admin/db/schedules`) +- OAuth bridge session flow (`/admin/oauth/discord/exchange`, `/admin/oauth/session/:sessionId`) diff --git a/docs/06_runtime_view.md b/docs/06_runtime_view.md index dde9863..73060fa 100644 --- a/docs/06_runtime_view.md +++ b/docs/06_runtime_view.md @@ -1,28 +1,43 @@ # Runtime View -## Scenario: User Signs Up for Open Mic +## Scenario: `/sign-up join` to Stage Queue -1. User runs `/sign-up` in voice channel -2. Command Layer validates user has `@ComedyFan` role -3. Event Manager adds user to FIFO queue -4. When previous speaker leaves stage (voice state change), Event Manager pops queue -5. Bot grants temporary speaker permission via voice channel API -6. Announcement sent to `#stage-door` channel -7. Mileage Engine awards +50 miles for participation +1. User invokes `/sign-up join`. +2. Command handler validates guild/context and required permissions. +3. Tour schedule engine appends user to guild queue if not already present. +4. Engine returns queue position and current queue snapshot. +5. Bot replies ephemerally with confirmation and position. +6. If configured, queue announcement message is sent to target channel. -## Scenario: Content Auto-Post +## Scenario: Advance Next Performer -1. CRON job triggers Content Syndicator every 15 min -2. Syndicator polls YouTube Data API for new uploads on channel -3. New video detected → fetches metadata + thumbnail -4. Syndicator builds rich embed (title, description, thumbnail, link) -5. Embed posted to `#screenings` channel via Discord webhook -6. Mileage Engine awards +10 miles to channel subscribers +1. Moderator invokes `/sign-up next`. +2. Tour schedule engine pops head of FIFO queue. +3. If stage channel integration is enabled, bot resolves member and promotes stage speaker. +4. Confirmation response includes promoted user and remaining queue length. +5. Optional announcement is sent to configured channel. -## Scenario: Admin Updates Bot Restart +## Scenario: Mileage Award on Interaction -1. Bot receives SIGTERM from hosting platform -2. Graceful shutdown initiated: close Gateway connection, flush pending DB writes -3. Bot process restarts -4. On startup: reconnect to Discord Gateway, rehydrate in-memory state from DB -5. Mileage state is consistent — no data loss +1. Discord interaction event reaches runtime handler. +2. Bot resolves guild/member context and chooses mileage event type. +3. Mileage engine calculates points from configured event map. +4. Transaction persists event row and upserts total user mileage. +5. Role-tier thresholds are evaluated and missing roles are granted. +6. Runtime stores an engagement snapshot in configuration DB for dashboard consumption. + +## Scenario: Dashboard OAuth Connect + +1. Browser redirects user to Discord OAuth authorize URL. +2. Dashboard callback receives authorization code. +3. Dashboard posts code to `POST /admin/oauth/discord/exchange`. +4. OAuth bridge exchanges code for Discord token, fetches user, creates local session. +5. API returns `sessionId`; dashboard fetches session details from `/admin/oauth/session/:sessionId`. +6. Optional backend sync call propagates session to openmicodyssey.com. + +## Scenario: Graceful Shutdown + +1. Process receives SIGINT/SIGTERM. +2. Runtime stops HTTP server and Discord client. +3. Service tear-down closes DB pools and cancels dailies intervals. +4. Process exits with clean status after shutdown promises resolve. diff --git a/docs/07_deployment_view.md b/docs/07_deployment_view.md index f32f3bd..bdc9c86 100644 --- a/docs/07_deployment_view.md +++ b/docs/07_deployment_view.md @@ -3,44 +3,47 @@ ## Infrastructure Overview ```txt -┌─────────────┐ ┌──────────────────────────────────────┐ -│ Discord │◄─────►│ Coolify │ -│ Gateway │ WSS │ │ -│ + REST API │ │ ┌──────────────────────────────┐ │ -└─────────────┘ │ │ omo-bot (Node.js process) │ │ - │ │ - Command handlers │ │ - │ │ - Event listeners │ │ - │ │ - Mileage engine │ │ - │ └──────────┬───────────────────┘ │ - │ │ │ - │ ▼ │ - │ ┌──────────────────────────────┐ │ - │ │ PostgreSQL (local) │ │ - │ │ - User profiles │ │ - │ │ - Mileage scores │ │ - │ │ - Event schedules │ │ - │ │ - Content cache │ │ - │ └──────────────────────────────┘ │ - │ │ - │ ┌──────────────────────────────┐ │ - │ │ Admin API / Dashboard │ │ - │ │ (React SPA) │ │ - │ │ admin.openmicodyssey.com │ │ - │ └──────────────────────────────┘ │ - └──────────────────────────────────────┘ - │ - OAuth2 │ - ▼ - ┌─────────────────────┐ - │ openmicodyssey.com │ - │ (Web Application) │ - └─────────────────────┘ +┌──────────────┐ HTTPS/WSS ┌───────────────────────────────────┐ +│ Discord APIs │◄───────────────────►│ Coolify deployment │ +└──────────────┘ │ ┌───────────────────────────────┐ │ + │ │ Backend container │ │ + │ │ - Discord gateway runtime │ │ + │ │ - Admin API (Express) │ │ + │ │ - Dailies pollers │ │ + │ └──────────────┬────────────────┘ │ + └─────────────────│──────────────────┘ + │ + ▼ + ┌───────────────────┐ + │ PostgreSQL │ + │ mileage + config │ + └───────────────────┘ + +┌──────────────────────────┐ HTTPS ┌─────────────────────────────┐ +│ Admin Dashboard (SPA) │◄───────────►│ Admin API + OAuth bridge │ +│ React/Vite static deploy │ │ │ +└──────────────────────────┘ └─────────────────────────────┘ ``` -## Environments +## CI/CD Pipeline -| Environment | Host | Bot Instance | DB | Purpose | -| ----------- | ---------------- | ------------ | ---------------- | ---------------------------- | ---------------------- | -| Development | Local machine | 1 process | Local Postgres | Feature development, testing | -| Staging | Coolify | 1 instance | Staging Postgres | Integration | Pre-release validation | -| Production | Coolify (scaled) | 2+ instances | Production (HA) | Live community server | +Source file: `.gitea/workflows/ci-cd.yml` + +1. Bot job: install dependencies, run lint, build, and test. +2. Dashboard job: install dependencies in `admin-dashboard`, run lint/build. +3. Deploy job: on `main`, trigger Coolify webhook (optionally authenticated with token). + +## Environment Matrix + +| Environment | Runtime | Database | Notes | +| ----------------- | --------------------------------- | -------------------------------------- | ------------------------------------------- | +| Local development | Node.js process via `npm run dev` | Local/Postgres dev instance | Fast iteration, manual command registration | +| CI | Ephemeral runner | Test/dev DB or mocked integration path | Quality gates before deployment | +| Production | Coolify-managed container(s) | Managed PostgreSQL | Webhook-triggered deployment from Gitea | + +## Deployment Risks and Mitigations + +- Risk: bot restarts clear in-memory OAuth sessions. + Mitigation: dashboard re-auth flow and short-lived session design. +- Risk: DB connectivity interruptions. + Mitigation: startup validation + retry behavior on mileage writes. diff --git a/docs/08_concepts.md b/docs/08_concepts.md index 3938df6..f66980c 100644 --- a/docs/08_concepts.md +++ b/docs/08_concepts.md @@ -1,32 +1,39 @@ # Cross-cutting Concepts -## Logging & Monitoring +## Configuration and Validation -- Structured logging (JSON) to stdout — visible in Coolify logs and via `docker logs` -- Log levels: DEBUG (dev), INFO (prod), ERROR (alert-worthy) -- Key metrics exposed via Prometheus endpoint: commands/sec, webhook latency, active users +- Central config parsing in `src/config.ts` with typed getters and validation. +- Required environment variables fail fast at startup. +- Feature toggles gate optional services (admin API, dailies, OAuth sync). -## Error Handling +## Persistence Strategy -- Command handlers: try/catch → ephemeral error reply to user + logged -- Content pollers: exponential backoff on API failures, alert after 3 consecutive failures -- Mileage writes: retry (3x, 50ms backoff) before logging as failed +- PostgreSQL used for both high-volume mileage events and operational configuration. +- Schema initialization runs inside service startup (`init` methods). +- Strict null and row count checks protect runtime behavior under `exactOptionalPropertyTypes`. -## Configuration +## Security Model -- Environment variables via `.env` file (dev) or Coolify secrets (prod), keep example in `.env.example` -- Config schema validated on startup — bot exits on missing required vars +- Admin API uses bearer token authentication middleware. +- Health endpoint can be optionally public for infrastructure probes. +- OAuth bridge performs code exchange server-side and stores ephemeral sessions in memory. +- Secrets are environment-based and excluded from logs. -## Security +## Error Handling and Resilience -- Bot token stored in env variable, never logged -- OAuth2 tokens short-lived; refresh flow managed by web app -- Admin dashboard routes guarded by Discord OAuth2 role check (`@Producer` or `@Director`) -- All external API calls over HTTPS +- Command handlers and API handlers return explicit user-safe errors. +- Mileage persistence applies bounded retries for transient DB failures. +- Dailies polling loop logs and continues on adapter-specific errors. +- Shutdown hooks close HTTP/Discord/DB resources to avoid orphaned connections. -## Conventions +## Integration Concepts -- Slash command names: kebab-case -- Event handler naming: `on` pattern -- Database table names: snake_case, plural -- PRs require passing lint + tests before merge +- Discord integration points: slash commands, reactions, stage channels, role management. +- Dashboard integration points: REST API JSON contracts and OAuth callback/session flow. +- External content integration: adapter contract + webhook dispatcher for dailies. + +## Testing and Quality Gates + +- Jest + ts-jest for command and integration-like flows. +- ESLint flat config for src and tests. +- CI requires lint, build, and tests for backend plus lint/build for dashboard.