docs: add AGENTS.md knowledge base (root + generators + dm)
This commit is contained in:
@@ -1,10 +1,10 @@
|
||||
{
|
||||
"sessionID": "ses_1d89ad89affe987QEXPgCGcx4S",
|
||||
"updatedAt": "2026-05-14T23:12:10.460Z",
|
||||
"updatedAt": "2026-05-15T01:27:46.830Z",
|
||||
"sources": {
|
||||
"background-task": {
|
||||
"state": "idle",
|
||||
"updatedAt": "2026-05-14T23:12:10.460Z"
|
||||
"updatedAt": "2026-05-15T01:27:46.830Z"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
# Randify.pro — Knowledge Base
|
||||
|
||||
**Stack:** Astro 4 + Tailwind CSS 4 + TypeScript + PostgreSQL (Drizzle ORM)
|
||||
**Deploy:** Docker Compose (app + postgres) on VPS, nginx reverse proxy, Let's Encrypt SSL
|
||||
|
||||
## Overview
|
||||
|
||||
Bilingual (EN/RU) static/hybrid site hosting random-value generators (10 live). Recently expanded with **DM Dashboard** — a server-rendered OAuth-authenticated subdomain (`/dm/`) with dice roller, initiative tracker, Open5e reference, and notes.
|
||||
|
||||
## Structure
|
||||
|
||||
```
|
||||
/
|
||||
├── src/
|
||||
│ ├── pages/ # Astro pages (EN + RU mirrors)
|
||||
│ │ ├── api/auth/ # OAuth 2.1 + PKCE (VK ID + Yandex)
|
||||
│ │ ├── generators/ # Static generator pages
|
||||
│ │ ├── dm/ # DM Dashboard (server-rendered, prerender=false)
|
||||
│ │ └── ru/ # Russian mirror pages
|
||||
│ ├── components/
|
||||
│ │ ├── generators/ # Interactive generator components (see sub-AGENTS)
|
||||
│ │ └── dm/ # DM Dashboard UI (see sub-AGENTS)
|
||||
│ ├── layouts/ # BaseLayout, GeneratorLayout, DmLayout
|
||||
│ ├── lib/
|
||||
│ │ ├── auth/ # JWT, OAuth configs, session utils
|
||||
│ │ ├── client/ # Browser utilities (dice, animations, random)
|
||||
│ │ └── open5e/ # Open5e API client + cache
|
||||
│ ├── db/ # Drizzle schema, migrations, client
|
||||
│ ├── i18n/ # Translations (en/ru) + DM translations
|
||||
│ ├── content/ # Astro Content Collections (generators, blog)
|
||||
│ └── data/ # Static data (generator list, config)
|
||||
├── tests/ # Vitest + Playwright tests
|
||||
├── drizzle/ # SQL migrations
|
||||
├── docker-compose.yml # App + PostgreSQL production setup
|
||||
└── .github/workflows/ # CI/CD deploy via rsync
|
||||
```
|
||||
|
||||
## Where to Look
|
||||
|
||||
| Task | Location | Notes |
|
||||
|------|----------|-------|
|
||||
| Add new generator | `src/content/generators/`, `src/components/generators/`, `src/pages/generators/` | Must create EN + RU simultaneously |
|
||||
| Fix OAuth | `src/pages/api/auth/`, `src/lib/auth/`, `src/middleware/index.ts` | VK ID uses `id.vk.ru`, Yandex uses `oauth.yandex.com` |
|
||||
| DB schema change | `src/db/schema.ts` → `drizzle/` | Run `npm run db:generate` then migrate |
|
||||
| DM Dashboard UI | `src/components/dm/`, `src/pages/dm/` | Orange theme `#E87722`, RU-only |
|
||||
| i18n strings | `src/i18n/translations.ts`, `src/i18n/dm-translations.ts` | `useT(lang)` returns typed object |
|
||||
| Production deploy | `.github/workflows/deploy.yml`, `docker-compose.yml` | GitHub Actions → rsync → Docker Compose restart |
|
||||
|
||||
## Conventions
|
||||
|
||||
- **Path aliases:** `@/*` → `src/*` used everywhere
|
||||
- **i18n routing:** Astro built-in. `Astro.currentLocale` drives locale. EN pages at root, RU under `/ru/`
|
||||
- **Generator pages:** Each generator has its own `.astro` page file (never dynamic `[slug].astro` for generators — Astro bundles all imported scripts)
|
||||
- **Color system:** `var(--accent)` in BaseLayout = `#534AB7` (purple). DM Dashboard overrides to `#E87722` (orange) via `.dm-theme` CSS class
|
||||
- **Auth cookie name:** `auth_token` (unified across OAuth callback, middleware, logout)
|
||||
- **DB connection:** `process.env.DATABASE_URL` — must match docker-compose `postgres` service hostname inside containers
|
||||
|
||||
## Anti-Patterns
|
||||
|
||||
- **Dynamic `[slug].astro` for generators:** Forbidden. Each generator must have its own page file to avoid script bundling issues
|
||||
- **Using `url.origin` in OAuth callbacks:** Broken behind nginx proxy. Always use `process.env.PUBLIC_APP_URL || url.origin`
|
||||
- **Joining multiple `Set-Cookie` with comma:** Browsers reject this. Use `Headers.append()` for each cookie
|
||||
- **`SameSite=Strict` on session cookie:** Breaks cross-site OAuth redirects. Use `SameSite=Lax` + `Secure`
|
||||
- **Static `prerender` for DM pages:** Middleware won't run, `Astro.locals.user` stays `null`. DM pages MUST have `export const prerender = false`
|
||||
- **`Math.random()` for cryptographic purposes:** Generators should use `crypto.getRandomValues()` via `src/lib/client/random.ts`
|
||||
- **Importing `import.meta.env` in server code:** Use `process.env` for server-side secrets (OAuth, JWT, DB)
|
||||
- **Unknown fields in generator JSON:** `src/lib/generator-schema.ts` uses Zod `.strict()`. Any unknown field breaks the build
|
||||
- **Unescaped `innerHTML` with user input:** Generators use `innerHTML` extensively. Always escape user input via `escapeHtml()` (currently duplicated in 3 files — consolidate to `src/lib/client/`)
|
||||
- **OAuth empty-string fallbacks:** `process.env.VK_CLIENT_ID || ''` silently produces invalid requests. Validate env vars explicitly or fail fast
|
||||
- **JWT secret non-null assertion:** `process.env.JWT_SECRET!` in `src/lib/auth/jwt.ts` crashes at runtime if env var is missing
|
||||
|
||||
## Commands
|
||||
|
||||
```bash
|
||||
npm run dev # Dev server (localhost:4321)
|
||||
npm run build # Static + server build → dist/
|
||||
npm run preview # Preview built dist/
|
||||
npm run test # Vitest unit tests
|
||||
npm run test:e2e # Playwright E2E tests
|
||||
npm run db:generate # Generate Drizzle migration
|
||||
npm run db:migrate # Run migrations
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
- **No test runner or linter** configured in CI (per CLAUDE.md)
|
||||
- **Health endpoint** (`/api/health`) checks DB connectivity via `SELECT 1`
|
||||
- **Analytics:** Yandex Metrika + Top.Mail.Ru counters, IDs in `src/data/config.ts`
|
||||
- **Service Worker** registered in BaseLayout for PWA support
|
||||
- **Blog** uses Astro Content Collections with MDX/Markdown
|
||||
- **Dockerfile** is single-stage Node 20 Alpine (not multi-stage as CLAUDE.md claims)
|
||||
- **ESLint override:** `public/sw.js` has `@typescript-eslint/no-unused-vars` disabled for service worker globals
|
||||
- **Zero TODO/FIXME/HACK markers** in source code — codebase is clean
|
||||
- **Explicit prerender required:** Hybrid output mode means every `.astro` page MUST declare `export const prerender = true/false`
|
||||
@@ -0,0 +1,40 @@
|
||||
# DM Dashboard — Knowledge Base
|
||||
|
||||
**Location:** `src/components/dm/`, `src/pages/dm/`, `src/pages/ru/dm/`
|
||||
|
||||
## Overview
|
||||
|
||||
Dungeon Master Dashboard — standalone RPG tool module within Randify. Server-rendered (hybrid Astro), RU-only, orange theme (`#E87722`). OAuth-authenticated via VK ID and Yandex.
|
||||
|
||||
## Components
|
||||
|
||||
| Component | Purpose |
|
||||
|-----------|---------|
|
||||
| `DiceRoller.astro` | Dice notation parser + roller with history |
|
||||
| `InitiativeTracker.astro` | Combat initiative list with sorting |
|
||||
| `Open5eReference.astro` | Open5e monster search + detail view |
|
||||
| `NotesPanel.astro` | `localStorage`-backed notes editor |
|
||||
| `AuthPanel.astro` | Login/logout UI, reads `Astro.locals.user` |
|
||||
| `DmHeader.astro` | Header with back link to Randify |
|
||||
| `DmCard.astro` | Styled container card |
|
||||
| `DmButton.astro` | Reusable button with variants |
|
||||
| `DmInput.astro` | Styled form input |
|
||||
|
||||
## Key Files
|
||||
|
||||
- **`src/layouts/DmLayout.astro`** — Wraps DM pages, imports `dm-theme.css`, hides `LanguageSwitcher`
|
||||
- **`src/styles/dm-theme.css`** — CSS vars scoped to `.dm-theme` class (overrides BaseLayout purple)
|
||||
- **`src/i18n/dm-translations.ts`** — RU-only strings for DM Dashboard
|
||||
|
||||
## Conventions
|
||||
|
||||
- **Theme override:** `.dm-theme` class on wrapper div sets `--accent: #E87722`
|
||||
- **Server-rendered:** All DM pages have `export const prerender = false` (middleware sets `Astro.locals.user`)
|
||||
- **Auth state:** Check `Astro.locals.user` in components; middleware validates `auth_token` cookie + JWT + DB session
|
||||
- **History:** Dice roller uses `sessionStorage`, notes use `localStorage`
|
||||
|
||||
## Anti-Patterns
|
||||
|
||||
- **Using `:root` for DM CSS vars:** BaseLayout sets `--accent: #534AB7` on `:root`. DM theme MUST use `.dm-theme` selector for higher specificity
|
||||
- **Static DM pages:** Will break auth. Always `prerender = false`
|
||||
- **Forgetting `hideLanguageSwitcher` on `DmLayout`:** DM should feel like standalone product
|
||||
@@ -0,0 +1,54 @@
|
||||
# Generators — Knowledge Base
|
||||
|
||||
**Location:** `src/components/generators/`
|
||||
|
||||
## Overview
|
||||
|
||||
10 interactive random-value generator components. Each is an Astro `.astro` file with client-side `<script>` for interactivity. Shared patterns for i18n, history persistence, and copy-to-clipboard.
|
||||
|
||||
## Structure
|
||||
|
||||
```
|
||||
src/components/generators/
|
||||
├── DiceGenerator.astro # Dice notation parser + roller
|
||||
├── MealGenerator.astro # Random meal picker
|
||||
├── EmojiGenerator.astro # Random emoji with categories
|
||||
├── HashGenerator.astro # Hash generators (MD5, SHA, etc.)
|
||||
├── FontPairGenerator.astro # Google Font pairings
|
||||
├── Magic8BallGenerator.astro # Magic 8-ball
|
||||
├── TimeGenerator.astro # Random time generator
|
||||
├── WheelSpinner.astro # Weighted wheel spinner
|
||||
├── WeightedGenerator.astro # Weighted random picker
|
||||
├── PaletteGenerator.astro # Color palette generator
|
||||
└── (shared utilities in src/lib/client/)
|
||||
```
|
||||
|
||||
## Conventions
|
||||
|
||||
- **Language detection (frontmatter):**
|
||||
```astro
|
||||
const isRu = Astro.url.pathname.startsWith('/ru');
|
||||
const T = useT(isRu ? 'ru' : 'en');
|
||||
```
|
||||
- **Language detection (client script):**
|
||||
```js
|
||||
const isRu = document.documentElement.lang === 'ru';
|
||||
```
|
||||
- **History persistence:** Use `sessionStorage` (not `localStorage`) with prefixed keys
|
||||
- **Copy feedback:** Show "Copied!" toast using `T.copyFeedback` / `T.copied`
|
||||
- **Secure random:** Import from `@/lib/client/random` (uses `crypto.getRandomValues`)
|
||||
|
||||
## Anti-Patterns
|
||||
|
||||
- **Using `Math.random()` directly:** Always use `@/lib/client/random` for cryptographic quality
|
||||
- **Hardcoding strings:** All user-visible text must go through `T.*` keys
|
||||
- **Skipping RU version:** Every generator MUST have both EN and RU pages
|
||||
|
||||
## Where to Look
|
||||
|
||||
| Task | File | Notes |
|
||||
|------|------|-------|
|
||||
| Add new generator | Copy `DiceGenerator.astro` as template | Follow i18n + history + copy patterns |
|
||||
| Fix dice logic | `src/lib/dice-engine.ts` | Parser + roller, shared across generators |
|
||||
| Fix randomness | `src/lib/client/random.ts` | `crypto.getRandomValues` wrapper |
|
||||
| Generator metadata | `src/content/generators/<slug>.json` | Zod-validated, `.strict()` — unknown fields fail build |
|
||||
Reference in New Issue
Block a user