Refresh repository documentation
Some checks failed
Deploy to Production / test (push) Failing after 33s

- Rewrite AGENTS and README files to match the current app layout
- Document API routes, trivia UI, and the active panel design language
This commit is contained in:
syntaxbullet
2026-04-09 21:10:10 +02:00
parent 8369d10bab
commit 6abbd4652a
17 changed files with 893 additions and 639 deletions

262
AGENTS.md
View File

@@ -1,211 +1,135 @@
# CLAUDE.md
# AGENTS.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
This file documents the current implementation shape of the Aurora repository.
## Commands
```bash
# Development
bun --watch bot/index.ts # Run bot + API with hot reload
docker compose up # Start all services (bot, API, database)
docker compose up app # Start just the app (bot + API)
docker compose up db # Start just the database
# App
bun run dev # bot + API in one Bun process with watch mode
docker compose up # app + db
docker compose up app # app only
docker compose up db # database only
# Testing
bun test # Run all tests
bun test path/to/file.test.ts # Run a single test file
bun test shared/modules/economy # Run tests in a directory
bun test --watch # Watch mode
bun test # Bun's native runner
bun run test # repo test wrapper script
bun run test:ci # include CI/integration path
# Database
bun run db:push:local # Push schema changes (local)
bun run db:studio # Open Drizzle Studio (localhost:4983)
bun run generate # Generate Drizzle migrations (Docker)
bun run migrate # Apply migrations (Docker)
bun run db:push # drizzle-kit push via Docker
bun run db:push:local # drizzle-kit push locally
bun run db:generate # drizzle-kit generate via Docker
bun run db:migrate # drizzle-kit migrate via Docker
bun run db:studio # local Drizzle Studio on :4983
# Admin Panel
bun run panel:dev # Start Vite dev server for dashboard
bun run panel:build # Build React dashboard for production
# Panel
bun run panel:dev # Vite dev server on :5173
bun run panel:build # build panel/dist
```
## Architecture
Aurora is a Discord RPG bot + REST API running as a **single Bun process**. The bot and API share the same database client and services.
Aurora is a single-process Bun application:
```
bot/ # Discord bot
├── commands/ # Slash commands by category (admin, economy, inventory, etc.)
├── events/ # Discord event handlers
├── lib/ # BotClient, handlers, loaders, embed helpers, commandUtils
├── modules/ # Feature modules (views, interactions per domain)
└── graphics/ # Canvas-based image generation (@napi-rs/canvas)
- `bot/index.ts` boots shared config, registers domain listeners, starts the API server, then logs into Discord.
- `api/src/server.ts` hosts REST routes, WebSocket traffic, and built panel assets.
- `shared/modules/*` contains the business logic used by both the bot and the API.
- `shared/games/*` contains reusable game plugins; `api/src/games/*` runs rooms and WebSocket orchestration.
shared/ # Shared between bot and API
├── db/ # Drizzle ORM client + schema (users, economy, inventory, quests, etc.)
├── lib/ # env, config, errors, logger, types, utils
└── modules/ # Domain services (economy, user, inventory, quest, moderation, etc.)
Current high-level layout:
api/ # REST API (Bun HTTP server)
└── src/routes/ # Route handlers for each domain
panel/ # React admin dashboard (Vite + Tailwind + Radix UI)
```text
bot/ Discord commands, events, views, interactions
api/ Bun HTTP + WebSocket server
panel/ React dashboard
shared/db/ Drizzle client and schema
shared/lib/ config, env, errors, logger, events, constants
shared/modules/ domain services
shared/games/ game plugins shared by API and panel
```
**Key architectural details:**
- Bot and API both import from `shared/` — do not duplicate logic.
- Services in `shared/modules/` are singleton objects, not classes.
- The database uses PostgreSQL 16+ via Drizzle ORM with `bigint` mode for Discord IDs and currency.
- Feature modules follow a strict file suffix convention (see below).
## Import conventions
## Import Conventions
Use path aliases from the repo `tsconfig.json`:
Use path aliases (defined in `tsconfig.json`). Order: external packages → aliases → relative.
- `@/*` -> `bot/*`
- `@commands/*` -> `bot/commands/*`
- `@db/*` -> `shared/db/*`
- `@lib/*` -> `bot/lib/*`
- `@modules/*` -> `bot/modules/*`
- `@shared/*` -> `shared/*`
```typescript
import { SlashCommandBuilder } from "discord.js"; // external
import { economyService } from "@shared/modules/economy/economy.service"; // alias
import { users } from "@db/schema"; // alias
import { createErrorEmbed } from "@lib/embeds"; // alias
import { localHelper } from "./helper"; // relative
```
Import order in the repo is generally:
**Aliases:**
- `@/*``bot/`
- `@shared/*``shared/`
- `@db/*``shared/db/`
- `@lib/*``bot/lib/`
- `@modules/*``bot/modules/`
- `@commands/*``bot/commands/`
1. external packages
2. aliases
3. relative imports
## Code Patterns
## File patterns
### Module File Suffixes
- `*.service.ts`: domain/business logic, usually in `shared/modules/*`
- `*.view.ts`: Discord message/view construction
- `*.interaction.ts`: component interaction handlers
- `*.types.ts`: local types and custom ID helpers
- `*.handler.ts`: bot-side orchestration around services/views
- `*.test.ts`: colocated tests
- `*.view.ts` — Creates Discord embeds/components
- `*.interaction.ts` — Handles button/select/modal interactions
- `*.service.ts` — Business logic (lives in `shared/modules/`)
- `*.types.ts` — Module-specific TypeScript types
- `*.test.ts` — Tests (co-located with source)
## Runtime config
### Interaction Routing
- Global game settings live in `game_settings` and are loaded into `shared/lib/config.ts`.
- Guild-specific settings live in `guild_settings`; `getGuildConfig()` adds a 60-second cache on top of DB reads.
- Most numeric DB values exposed through runtime config are converted to `bigint` in `shared/lib/config.ts`.
Component interactions (buttons, select menus, modals) flow through a centralized routing system:
## Interaction routing
```
Discord event → interactionCreate → ComponentInteractionHandler → interaction.routes.ts → *.interaction.ts
```
Global component routing is defined in `bot/lib/interaction.routes.ts` and consumed by `ComponentInteractionHandler`.
`ComponentInteractionHandler` (`bot/lib/handlers/ComponentInteractionHandler.ts`) iterates over the route table in `bot/lib/interaction.routes.ts`. Each route has a `predicate` that matches on `customId`, a lazy `handler` import, and a `method` name to call. The handler also provides centralized `UserError` / system error handling.
Current route table:
**Route table (custom ID prefix → handler):**
- `trade_` and `amount` -> `bot/modules/trade/trade.interaction.ts`
- `shop_buy_` -> `bot/modules/economy/shop.interaction.ts`
- `lootdrop_` -> `bot/modules/economy/lootdrop.interaction.ts`
- `trivia_` -> `bot/modules/trivia/trivia.interaction.ts`
- `createitem_` -> `bot/modules/admin/item_wizard.ts`
- `enrollment` -> `bot/modules/user/enrollment.interaction.ts`
- `feedback_` -> `bot/modules/feedback/feedback.interaction.ts`
| Custom ID prefix | Handler file | Method |
| ------------------ | ----------------------------------------------- | ------------------------------ |
| `trade_`, `amount` | `bot/modules/trade/trade.interaction.ts` | `handleTradeInteraction` |
| `shop_buy_` | `bot/modules/economy/shop.interaction.ts` | `handleShopInteraction` |
| `lootdrop_` | `bot/modules/economy/lootdrop.interaction.ts` | `handleLootdropInteraction` |
| `trivia_` | `bot/modules/trivia/trivia.interaction.ts` | `handleTriviaInteraction` |
| `createitem_` | `bot/modules/admin/item_wizard.ts` | `handleItemWizardInteraction` |
| `enrollment` | `bot/modules/user/enrollment.interaction.ts` | `handleEnrollmentInteraction` |
| `feedback_` | `bot/modules/feedback/feedback.interaction.ts` | `handleFeedbackInteraction` |
Some features still use local collectors instead of the global route table, notably inventory.
Routes are evaluated in order — the first matching predicate wins. Some modules (e.g., inventory with `inv_` prefix) handle interactions locally via message component collectors instead of the global route table.
## Commands and access control
### Command Definition
- Slash command execution is centralized in `bot/lib/handlers/CommandHandler.ts`.
- `withCommandErrorHandling()` is the normal command wrapper for defer/reply/error behavior.
- Beta commands rely on `featureFlagsService.hasAccess()`.
- `ADMIN_USER_IDS` controls admin panel access, not Discord permissions inside command code.
```typescript
export const commandName = createCommand({
data: new SlashCommandBuilder().setName("name").setDescription("desc"),
execute: async (interaction) => {
await withCommandErrorHandling(interaction, async () => {
const result = await service.method();
await interaction.editReply({ embeds: [createSuccessEmbed(result)] });
}, { ephemeral: true });
},
});
```
## API and panel
`withCommandErrorHandling` (from `@lib/commandUtils`) handles `deferReply`, `UserError` display, and unexpected error logging automatically.
- API routes are prefix-matched in `api/src/routes/index.ts`.
- `/auth/*` and `/api/health` are public.
- Players may access `/api/stats`, `/api/health`, `/api/me`, and `/api/me/inventory`.
- Remaining `/api/*` routes are admin-only.
- The panel dev server proxies back to the Bun server; the integrated server serves `panel/dist` when built.
### Service Pattern
## Database notes
```typescript
export const serviceName = {
methodName: async (params: ParamType): Promise<ReturnType> => {
return await withTransaction(async (tx) => {
// database operations
});
},
};
```
- Docker Compose uses PostgreSQL 17.
- Discord IDs and currency/xp values are stored as `bigint`.
- `withTransaction()` lives in `bot/lib/db.ts` and is the normal way shared services compose DB work.
### Error Handling
## Testing
```typescript
import { UserError, SystemError } from "@shared/lib/errors";
- Tests use `bun:test`.
- Mock modules before importing the unit under test.
- Most service tests stub `DrizzleClient` or `withTransaction()` rather than hitting the real database.
throw new UserError("You don't have enough coins!"); // shown to user
throw new SystemError("DB connection failed"); // logged, generic message shown
```
## Key entrypoints
### Database Transactions
```typescript
import { withTransaction } from "@/lib/db";
return await withTransaction(async (tx) => {
const user = await tx.query.users.findFirst({ where: eq(users.id, id) });
await tx.update(users).set({ coins: newBalance }).where(eq(users.id, id));
return user;
}, existingTx); // pass existing tx for nested transactions
```
### Testing
Mock modules **before** imports. Use `bun:test`.
```typescript
import { describe, it, expect, mock, beforeEach } from "bun:test";
mock.module("@shared/db/DrizzleClient", () => ({
DrizzleClient: { query: mockQuery },
}));
describe("serviceName", () => {
beforeEach(() => mockFn.mockClear());
it("should handle expected case", async () => {
mockFn.mockResolvedValue(testData);
const result = await service.method(input);
expect(result).toEqual(expected);
});
});
```
## Naming Conventions
| Element | Convention | Example |
| ---------------- | ---------------------- | -------------------------------- |
| Files | camelCase or kebab-case | `BotClient.ts`, `economy.service.ts` |
| Classes | PascalCase | `CommandHandler`, `UserError` |
| Functions | camelCase | `createCommand`, `handleShopInteraction` |
| Constants | UPPER_SNAKE_CASE | `EVENTS`, `BRANDING` |
| Enums | PascalCase | `TimerType`, `TransactionType` |
| Services | camelCase singleton | `economyService`, `userService` |
| Types/Interfaces | PascalCase | `Command`, `Event`, `GameConfigType` |
| DB tables | snake_case | `users`, `moderation_cases` |
| Custom IDs | snake_case with prefix | `shop_buy_`, `trade_accept_` |
| API routes | kebab-case | `/api/guild-settings` |
## Key Files
| Purpose | File |
| ----------------- | -------------------------- |
| Bot entry point | `bot/index.ts` |
| Discord client | `bot/lib/BotClient.ts` |
| DB schema index | `shared/db/schema.ts` |
| Error classes | `shared/lib/errors.ts` |
| Environment vars | `shared/lib/env.ts` |
| Config loader | `shared/lib/config.ts` |
| Embed helpers | `bot/lib/embeds.ts` |
| Command utils | `bot/lib/commandUtils.ts` |
| API server | `api/src/server.ts` |
- `bot/index.ts`
- `bot/lib/BotClient.ts`
- `api/src/server.ts`
- `api/src/routes/index.ts`
- `shared/lib/config.ts`
- `shared/db/DrizzleClient.ts`
- `shared/db/schema/index.ts`