Files
Randify.pro/AGENTS.md
T

5.8 KiB

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

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