Implement a centralized logging system with typed events (webhook, PR, comment, review, Discord relay, errors, etc.) that feeds a pub/sub pattern. Events are logged to console (with ANSI colors: green=normal, yellow=skipped, red=errors, cyan=PR#) and persisted to JSONL files organized by git commit SHA. Restructure server code into src/server/ directory with separate route handlers. Add a new /logs endpoint with an HTML viewer that displays events in a searchable table with type/repo filtering and browser timezone conversion. Remove the old /errors endpoint. Improvements: - All webhook handlers, Discord events, and errors now use log() instead of console.* calls - Error objects are properly serialized to JSONL (message + stack extracted) - Path traversal vulnerability fixed in readLogFile() - Failed API calls and caught errors are now always logged - Graceful error handling with fallbacks where appropriate (e.g., missing Discord users) Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2.3 KiB
2.3 KiB
Spike
Discord-Gitea bridge bot. Syncs PRs, comments, and code reviews between a Gitea server (git.nose.space) and Discord.
Architecture
src/
├── server/ — HTTP server, routes, and web pages (logs viewer, auth)
├── config.ts — Dev/prod environment config (DB paths, channel IDs, username mappings)
├── log.ts — Typed event logging (pub/sub, console + JSONL file listeners)
├── discord/ — Discord bot client, event listeners, slash commands
├── gitea/ — Gitea API calls, types, username conversion
└── bridge/ — Wiring between Gitea and Discord (webhook handler, DB, Discord helpers)
Dependencies flow one way: bridge/ → gitea/, bridge/ → discord/. Neither gitea/ nor discord/ imports from the other.
Libs
Each directory with an index.ts barrel is a standalone lib. Read the lib's README for its public API — you don't need to read internals to use it.
- server/ — HTTP server, webhook route, log viewer page, auth page.
- gitea/ — Pure Gitea API client and types. No side effects, no Discord, no DB.
- discord/ — Discord bot client, events, slash commands. Hands off to bridge for Gitea integration.
- bridge/ — The glue. Webhook handler, Discord helpers, SQLite DB for ID mappings.
Import rules:
- External code imports from the barrel only:
import { handleGiteaWebhook } from "./bridge" - Never reach into internal files:
import { handleGiteaWebhook } from "./bridge/webhook-handler" - Internal files within a lib can import from each other freely
This keeps AI context small. When working on code that uses a lib, only read the lib's README — not every internal file.
Running
bun run subdomain:dev— Start with hot reloadbun run subdomain:start— Start for productionbun test— Run integration tests (requires Tailscale funnel + test env vars)
Environment Variables
DISCORD_TOKEN— Discord bot tokenDISCORD_CLIENT_ID— Discord application client IDGITEA_API_TOKEN— Gitea API tokenDATA_DIR— Data directory (prod only)NODE_ENV—productionor omit for devPORT— Server port (default 3000)