5.8 KiB
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.currentLocaledrives locale. EN pages at root, RU under/ru/ - Generator pages: Each generator has its own
.astropage file (never dynamic[slug].astrofor generators — Astro bundles all imported scripts) - Color system:
var(--accent)in BaseLayout =#534AB7(purple). DM Dashboard overrides to#E87722(orange) via.dm-themeCSS class - Auth cookie name:
auth_token(unified across OAuth callback, middleware, logout) - DB connection:
process.env.DATABASE_URL— must match docker-composepostgresservice hostname inside containers
Anti-Patterns
- Dynamic
[slug].astrofor generators: Forbidden. Each generator must have its own page file to avoid script bundling issues - Using
url.originin OAuth callbacks: Broken behind nginx proxy. Always useprocess.env.PUBLIC_APP_URL || url.origin - Joining multiple
Set-Cookiewith comma: Browsers reject this. UseHeaders.append()for each cookie SameSite=Stricton session cookie: Breaks cross-site OAuth redirects. UseSameSite=Lax+Secure- Static
prerenderfor DM pages: Middleware won't run,Astro.locals.userstaysnull. DM pages MUST haveexport const prerender = false Math.random()for cryptographic purposes: Generators should usecrypto.getRandomValues()viasrc/lib/client/random.ts- Importing
import.meta.envin server code: Useprocess.envfor server-side secrets (OAuth, JWT, DB) - Unknown fields in generator JSON:
src/lib/generator-schema.tsuses Zod.strict(). Any unknown field breaks the build - Unescaped
innerHTMLwith user input: Generators useinnerHTMLextensively. Always escape user input viaescapeHtml()(currently duplicated in 3 files — consolidate tosrc/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!insrc/lib/auth/jwt.tscrashes 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 viaSELECT 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.jshas@typescript-eslint/no-unused-varsdisabled for service worker globals - Zero TODO/FIXME/HACK markers in source code — codebase is clean
- Explicit prerender required: Hybrid output mode means every
.astropage MUST declareexport const prerender = true/false