188 lines
6.9 KiB
Markdown
188 lines
6.9 KiB
Markdown
# CLAUDE.md
|
|
|
|
This file provides guidance to Claude Code (claude.ai/code) when working with code in this 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
|
|
|
|
# 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
|
|
|
|
# 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)
|
|
|
|
# Admin Panel
|
|
bun run panel:dev # Start Vite dev server for dashboard
|
|
bun run panel:build # Build React dashboard for production
|
|
```
|
|
|
|
## 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.
|
|
|
|
```
|
|
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)
|
|
|
|
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.)
|
|
|
|
api/ # REST API (Bun HTTP server)
|
|
└── src/routes/ # Route handlers for each domain
|
|
|
|
panel/ # React admin dashboard (Vite + Tailwind + Radix UI)
|
|
```
|
|
|
|
**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
|
|
|
|
Use path aliases (defined in `tsconfig.json`). Order: external packages → aliases → relative.
|
|
|
|
```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
|
|
```
|
|
|
|
**Aliases:**
|
|
- `@/*` → `bot/`
|
|
- `@shared/*` → `shared/`
|
|
- `@db/*` → `shared/db/`
|
|
- `@lib/*` → `bot/lib/`
|
|
- `@modules/*` → `bot/modules/`
|
|
- `@commands/*` → `bot/commands/`
|
|
|
|
## Code Patterns
|
|
|
|
### Module File Suffixes
|
|
|
|
- `*.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)
|
|
|
|
### Command Definition
|
|
|
|
```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 });
|
|
},
|
|
});
|
|
```
|
|
|
|
`withCommandErrorHandling` (from `@lib/commandUtils`) handles `deferReply`, `UserError` display, and unexpected error logging automatically.
|
|
|
|
### Service Pattern
|
|
|
|
```typescript
|
|
export const serviceName = {
|
|
methodName: async (params: ParamType): Promise<ReturnType> => {
|
|
return await withTransaction(async (tx) => {
|
|
// database operations
|
|
});
|
|
},
|
|
};
|
|
```
|
|
|
|
### Error Handling
|
|
|
|
```typescript
|
|
import { UserError, SystemError } from "@shared/lib/errors";
|
|
|
|
throw new UserError("You don't have enough coins!"); // shown to user
|
|
throw new SystemError("DB connection failed"); // logged, generic message shown
|
|
```
|
|
|
|
### 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` |
|