diff --git a/.sisyphus/run-continuation/ses_1d89ad89affe987QEXPgCGcx4S.json b/.sisyphus/run-continuation/ses_1d89ad89affe987QEXPgCGcx4S.json index 3c66818..8c68e9b 100644 --- a/.sisyphus/run-continuation/ses_1d89ad89affe987QEXPgCGcx4S.json +++ b/.sisyphus/run-continuation/ses_1d89ad89affe987QEXPgCGcx4S.json @@ -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" } } } \ No newline at end of file diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..e36296d --- /dev/null +++ b/AGENTS.md @@ -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` diff --git a/src/components/dm/AGENTS.md b/src/components/dm/AGENTS.md new file mode 100644 index 0000000..8e71e56 --- /dev/null +++ b/src/components/dm/AGENTS.md @@ -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 diff --git a/src/components/generators/AGENTS.md b/src/components/generators/AGENTS.md new file mode 100644 index 0000000..c8c50f1 --- /dev/null +++ b/src/components/generators/AGENTS.md @@ -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 `