Refresh repository documentation
Some checks failed
Deploy to Production / test (push) Failing after 33s
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:
@@ -1,40 +1,79 @@
|
||||
# Database Layer
|
||||
# Database layer
|
||||
|
||||
## Column Types
|
||||
- **Bigint** (`mode: 'bigint'`): All Discord snowflake IDs (`userId`, `guildId`, `roleId`, `channelId`), currency amounts (`balance`, `xp`, transaction `amount`, item `price`, inventory `quantity`). Use `0n` BigInt literals, never `Number()`.
|
||||
- **Integer**: Small counts and internal IDs — `level`, `daily_streak`, `progress`, `reward_amount` in lootdrops, auto-increment `serial('id')` for items.
|
||||
Aurora uses Drizzle ORM with PostgreSQL. Docker Compose currently runs PostgreSQL 17.
|
||||
|
||||
## Composite Primary Keys
|
||||
- `inventory(userId, itemId)` — one stack per user per item
|
||||
- `userQuests(userId, questId)` — one assignment per user per quest
|
||||
- `userTimers(userId, type, key)` — one timer per user per type/key combo
|
||||
## Schema modules
|
||||
|
||||
## Constraints
|
||||
- `inventory.quantity > 0` check constraint — never store zero-quantity rows
|
||||
- `classes.name` and `items.name` are unique
|
||||
- Cascade deletes on user FK; set null on `relatedUserId` (preserves transaction history)
|
||||
- `users.ts`
|
||||
- `inventory.ts`
|
||||
- `economy.ts`
|
||||
- `quests.ts`
|
||||
- `moderation.ts`
|
||||
- `feature-flags.ts`
|
||||
- `guild-settings.ts`
|
||||
- `game-settings.ts`
|
||||
|
||||
## JSON Columns (JSONB)
|
||||
- `quests.requirements`: `{ target: number }`
|
||||
- `quests.rewards`: `{ xp?: number, balance?: number }`
|
||||
- `gameSettings` fields: Typed via `.$type<T>()` — `LevelingConfig`, `EconomyConfig`, etc.
|
||||
- `guildSettings.featureOverrides`: `Record<string, boolean>` (sparse)
|
||||
- `guildSettings.colorRoleIds`: `string[]`
|
||||
- `users.settings`, `userTimers.metadata`, `items.usageData`: Untyped JSONB, default `{}`
|
||||
`shared/db/schema/index.ts` re-exports the full schema surface.
|
||||
|
||||
## Enums
|
||||
Defined as TypeScript enums in `shared/lib/constants.ts`, **not** as database enums. Schema columns use `varchar` with length constraints. Key enum types:
|
||||
- `TimerType`: COOLDOWN, EFFECT, ACCESS, EXAM_SYSTEM, TRIVIA_COOLDOWN
|
||||
- `TransactionType`: TRANSFER_IN, DAILY_REWARD, etc. (8 values)
|
||||
- `ModerationCaseType`: warn, timeout, kick, ban, note, prune
|
||||
- `ItemType`: MATERIAL, CONSUMABLE, EQUIPMENT, QUEST
|
||||
## Numeric conventions
|
||||
|
||||
## Notable Indexes
|
||||
- `user_timers_lookup_idx`: Composite on (userId, type, key) — fast timer checks
|
||||
- `user_timers_expires_at_idx`: Expiry-based cleanup queries
|
||||
- `users_balance_idx` / `users_level_xp_idx`: Leaderboard queries
|
||||
- Discord IDs, balances, XP, quantities, and transaction amounts are stored as `bigint`
|
||||
- many API responses serialize those `bigint` values to strings
|
||||
- JSON config blobs inside `game_settings` use strings for values that become `bigint` at runtime, for example:
|
||||
- `economy.daily.amount`
|
||||
- `economy.daily.streakBonus`
|
||||
- `economy.daily.weeklyBonus`
|
||||
- `economy.transfers.minAmount`
|
||||
- `inventory.maxStackSize`
|
||||
- `trivia.entryFee`
|
||||
|
||||
## Client Setup
|
||||
- `DrizzleClient` is a singleton in `shared/db/DrizzleClient.ts` (postgres-js driver, no prefetch).
|
||||
- `withTransaction` utility in `bot/lib/db.ts` wraps Drizzle transactions and tracks count for graceful shutdown. Accepts optional existing `tx` for nested calls.
|
||||
- No soft deletes anywhere. `moderationCases` uses `active: boolean` + `resolvedAt`/`resolvedBy` for lifecycle, but rows are never deleted.
|
||||
## Important tables
|
||||
|
||||
- `users`
|
||||
- `classes`
|
||||
- `transactions`
|
||||
- `item_transactions`
|
||||
- `items`
|
||||
- `inventory`
|
||||
- `quests`
|
||||
- `user_quests`
|
||||
- `user_timers`
|
||||
- `moderation_cases`
|
||||
- `lootdrops`
|
||||
- `feature_flags`
|
||||
- `feature_flag_access`
|
||||
- `guild_settings`
|
||||
- `game_settings`
|
||||
|
||||
## Composite keys and constraints
|
||||
|
||||
- `inventory(userId, itemId)`
|
||||
- `userQuests(userId, questId)`
|
||||
- `userTimers(userId, type, key)`
|
||||
- `inventory.quantity > 0`
|
||||
- `items.name` is unique
|
||||
- `feature_flags.name` is unique
|
||||
|
||||
## JSON columns
|
||||
|
||||
- `items.usageData`
|
||||
- `users.settings`
|
||||
- `userTimers.metadata`
|
||||
- `quests.requirements`
|
||||
- `quests.rewards`
|
||||
- `guildSettings.colorRoleIds`
|
||||
- `guildSettings.featureOverrides`
|
||||
- every main section in `gameSettings`
|
||||
|
||||
## Relations and deletion behavior
|
||||
|
||||
- inventory rows cascade on user/item deletion
|
||||
- `transactions.relatedUserId` and `item_transactions.relatedUserId` use `set null`
|
||||
- feature flag access rows cascade when a feature flag is deleted
|
||||
- moderation cases are not soft-deleted; lifecycle is represented by `active`, `resolvedAt`, and `resolvedBy`
|
||||
|
||||
## Client setup
|
||||
|
||||
- `shared/db/DrizzleClient.ts` exports the singleton DB client
|
||||
- `bot/lib/db.ts` exports `withTransaction()`
|
||||
- shared services normally accept an optional existing transaction so nested operations stay atomic
|
||||
|
||||
Reference in New Issue
Block a user