# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## Directives - **Zero CDN dependencies.** No external JS, CSS, fonts, images, or `cdn.*` URLs. `npm run build` fails on any CDN reference via `scripts/check-no-cdn.mjs`. - **Persian/RTL first.** All UI copy is Persian. Layouts assume `direction: rtl`; use logical CSS properties, not left/right. - **Frontend uses APIs only.** Never call the database from frontend code. All persistence goes through `/api/v1/*` at the backend. Frontend API clients may pass unversioned `/api/*` paths because `src/lib/api.js` rewrites them to `/api/v1/*`. `teknav-data.js` and `teknav-store.js` are seed/reference data — never use them as admin-panel fallbacks. - **No mock admin data.** Admin/writer/reviewer/dashboard/media views must use real backend APIs and show empty/error states when data is missing. - **Production is native Linux/systemd + nginx.** No Docker unless explicitly asked. See `DEPLOY.md`. - **Protect credentials.** `ACCOUNTS.md` is gitignored and nginx-blocked. Never move it into `public/` or `dist/`. Seed credentials are in `backend/prisma/credentials.json`. - **Port and registry are fixed.** Vite dev/preview → port `3009`; npm registry → `https://npm.devneeds.ir/`. - **Reuse shared styling.** Use `src/styles/global.css` keyframes/utilities — do not redefine animation primitives. - **Phase 15 is current.** Keep `ARCH.md`, `AGENTS.md`, `DEPLOY.md`, and `SECURITY.md` aligned when schema, SEO, trust, retention, or monetization flows change. Phase 14 Linux validation (pgvector + LMS) is still pending before full production go-live. - **Production hardening.** Run `npm run preflight:prod` before deploy; run `TEST_REQUIRE_SERVER=1 npm test` in `backend/` against a live backend to enforce endpoint contracts. ## Commands **Frontend** (repo root): ```bash npm run dev # Vite dev server → port 3009, proxies /api → localhost:3010 npm run build # Full pipeline (see Build Pipeline Order) npm run preview # Preview dist/ on port 4173 npm run preflight:prod # Static pre-deploy audit npm run seo:sitemap # Regenerate sitemap.xml, robots.txt, feed.xml npm run og:images # Generate OG images into public/images/og/ (Linux only) npm run check:seo # Audit Persian articles for indexability gaps npm run smoke:prod # Smoke-test all public endpoints ``` **Backend** (`cd backend` first): ```bash npm run dev # tsx watch → port 3010 npm run build # TypeScript compile npm run prisma:generate # Regenerate Prisma client after schema changes npm run prisma:apply # Apply schema (migrations if present, else db push) npm run seed # Seed PostgreSQL + Persian FTS npm run publish:due # Publish scheduled articles + fan out notifications npm test # Skips endpoint tests when no backend running TEST_REQUIRE_SERVER=1 npm test # Strict contract tests against a live backend ``` **Startup order:** PostgreSQL + Redis → `cd backend && npm run dev` → `npm run dev` **Schema change workflow:** edit schema → `npm run prisma:generate --prefix backend` → `npm run prisma:apply --prefix backend` → backend build. **Article data change workflow:** edit `teknav-data.js` → `npm run check:seo` → `npm run seo:sitemap` → `npm run build`. ## Build Pipeline Order ``` seo:sitemap → og:images → vite build → check-no-cdn → check-responsive → generate-article-pages → generate-web-stories → generate-comparison-pages → generate-company-pages → generate-en-page → generate-author-pages → generate-semantic-pages → compress-dist ``` All `generate-*` scripts produce static HTML that nginx serves directly — never manually edit files under `dist/`. `generate-article-pages` must run after `vite build` (needs `dist/index.html` as template) and before `compress-dist` (so `.gz`/`.br` pairs are created). Phase 15 scripts output: `dist/stories/{slug}/` (web stories), `dist/compare/{slug}/` (comparisons), `dist/directory/{slug}/` (company directory). ## SEO Invariants - **No `hreflang="en"`.** `?lang=en` is a UI preference only; canonical always points to the clean URL. - **No placeholder verification codes.** Never commit `INSERT_GOOGLE_VERIFICATION_CODE` or similar strings. - **Static article pages** under `dist/article/{slug}/index.html` are build-time generated — do not edit manually. - **Sitemap lastmod must not be future-dated.** `capDate()` in `generate-seo.mjs` enforces this. - **Organization schema:** `['Organization', 'NewsMediaOrganization']` — required for Google News. - **Article schema:** `['NewsArticle', 'TechArticle']` — required for Google Article rich results. - **Run `npm run check:seo`** after any article metadata or content change. - **Proven content formula** (article `ai-data-centric-2026` ranked #1 on Google, above Wikipedia): `

` opening, numbered `

` sections, `` entity highlighting, bilingual terminology (Persian + English inline), technical formulas/data inline, `factCheckedAt` timestamp, 8+ sections / ~3000 words. ## Architecture **Frontend** — Vite 5 + React 18 ES modules, automatic JSX runtime (no `import React` needed). Route dispatch in `teknav-app.jsx`; pages in `teknav-home.jsx`, `teknav-articles.jsx`, `teknav-pages.jsx`, `teknav-admin.jsx`, `teknav-auth.jsx`, `teknav-profile.jsx`. Providers, header, footer, shared UI in `teknav-ui.jsx`. Styles/fonts/keyframes in `src/styles/global.css`. **Backend** — Fastify 4 + Prisma 5 + PostgreSQL 16 (+ `pgvector` for semantic search) + Redis 7. Route modules in `backend/src/routes/`. Global error handler registered early in `backend/src/server.ts` — maps `FST_ERR_VALIDATION` to `'invalid_input'` uniformly. Config validated by Zod in `backend/src/config.ts`. **Data layer** — `teknav-data.js` holds all articles (`art1`–`art35`), authors, categories, topic hub metadata, glossary reference data. Article content beyond art10 lives in `article-contents-{N}.js` files. `src/lib/content-api.js` merges API results over this local fallback (API wins). `teknav-store.js` handles client-side localStorage caching. **Auth** — Argon2id passwords (m=19MiB, t=2, p=1), Redis sessions (`tek_sid`, 14-day sliding TTL), CSRF double-submit cookie (`tek_csrf`). Login by email or `@username`. Google/GitHub OAuth (authorization-code, state in Redis). SMS 2FA via Kavenegar OTP (`tek_2fa` ticket). **Role-based access:** | Role | Access | |---|---| | `reader` | View, save, react, manage own profile | | `reviewer` | + Article review queue | | `writer` | + Create/edit own articles | | `editor` | + Edit any article, manage categories/tags | | `admin` | + Delete articles, manage users | **Real-time** — SSE at `/api/v1/auth/notifications/stream` (reader) and `/api/v1/admin/stream` (admin live signals). Redis pub/sub is the bus (`backend/src/lib/realtime.ts`). **Web push** — RFC 8030/VAPID via `web-push`. `PushSubscription` table stores endpoint per user; `userId` nullable for guest topic subscriptions. `sendPushToUser()` checks `NotificationPreference` before delivering. VAPID keys in `/etc/teknav/backend.env` — missing keys make push a silent no-op, all other features unaffected. **Storage** — `LocalStorageAdapter` active by default (writes to `UPLOAD_DIR`). S3 migration: set `STORAGE_BACKEND=s3` and wire the stub in `backend/src/lib/storage.ts`. ## Phase 14 — Farsi Knowledge & Ecosystem (current, Linux validation pending) Requires `CREATE EXTENSION vector;` (PostgreSQL superuser) and `prisma:apply` before going live: | Subsystem | Models | Key endpoints | |---|---|---| | **Semantic Search / RAG** | `ArticleEmbedding` | `POST /api/v1/search/semantic`, `GET /api/v1/rag/:slug` | | **LMS** | `LearningPath`, `LearningModule`, `Quiz`, `UserCertification` | `POST /api/v1/lms/quiz/:quizId/submit`, `GET /api/v1/lms/me` — auto-cert at ≥80 | | **Tech Directory** | `CompanyProfile`, `CompanyClaim` | Authenticated claim endpoint | | **Wiki/Glossary Proposals** | `WikiProposal`, `GlossaryProposal` | `POST /api/v1/wiki/proposals`, `PATCH /api/v1/admin/wiki/proposals/:id` | `link-engine.ts` uses `cheerio` to auto-inject `` anchors into newly published articles. ## Key Files | Path | Purpose | |---|---| | `teknav-app.jsx` | Root route dispatch | | `teknav-ui.jsx` | Providers, header, footer, `PushNotificationPrompt` | | `teknav-home.jsx` | Home page — hero, featured, latest, personalized feed, streaks | | `teknav-articles.jsx` | Article detail — comments, TOC, reactions, Q&A, series nav | | `teknav-pages.jsx` | Topic hubs, glossary, tag pages, author pages, series, newsletter archive, jobs, courses, directory | | `teknav-admin.jsx` | CMS, `ArticleEditor` (role-aware, `RevisionDrawer`, presence chips), LMS panel, directory panel | | `teknav-auth.jsx` | Login, signup, OAuth, 2FA, forgot-password | | `teknav-profile.jsx` | `/profile/@username`, push toggle, notification preferences, history | | `src/lib/api.js` | Fetch wrapper with credentials + CSRF | | `src/lib/content-api.js` | Public content client — merges API over local fallback | | `src/lib/admin-api.js` | Admin/CMS API client | | `src/lib/engagement-api.js` | Comments, history, notifications, follows, streaks, badges, revisions, presence | | `src/lib/seo.jsx` | Page-level meta tags and JSON-LD for all page types | | `backend/src/server.ts` | Fastify bootstrap — error handler registered before all plugins | | `backend/src/routes/admin.ts` | Role-gated article CRUD, publish quality gates, analytics, review, newsletter, series | | `backend/src/routes/auth.ts` | Local auth, OAuth, 2FA, session, `/api/v1/auth/me` | | `backend/src/routes/comments.ts` | Comments, replies, likes, upvotes, flags, bulk mod. Returns `{items:[],total:0}` (not 404) for unknown slugs. | | `backend/src/routes/notifications.ts` | Notification list, SSE stream, VAPID key, push subscribe/unsubscribe | | `backend/src/routes/revisions.ts` | Article revision list/get/restore (admin/editor only) | | `backend/src/routes/monetization.ts` | Jobs, courses, Zarinpal subscribe/callback/status | | `backend/src/routes/errors.ts` | `POST /api/v1/errors` → `ClientError` table; `GET /api/v1/admin/errors` | | `backend/src/lib/push.ts` | VAPID, `sendPushToUser()` (checks prefs), `sendGuestPushByTopic()`, 410 cleanup | | `backend/src/lib/storage.ts` | `StorageAdapter` — `LocalStorageAdapter` default, `S3StorageAdapter` stub | | `backend/src/lib/realtime.ts` | Redis pub/sub, SSE helpers, visitor counter | | `backend/prisma/schema.prisma` | Single source of truth for all models | | `backend/prisma/seed.ts` | Seed data, staff users, Persian FTS setup | | `scripts/production-readiness.mjs` | `npm run preflight:prod` static pre-deploy checklist | | `.env.prod.example` | Production env template → copy to `/etc/teknav/backend.env` | | `ACCOUNTS.md` | Operator credential sheet — gitignored, nginx-blocked | | `deploy/systemd/` | Production systemd units and timers | | `nginx/nginx.conf.example` | Production nginx template | ## Production Notes - nginx must serve pre-rendered semantic folders (`/category/`, `/tag/`, `/glossary/`, `/topics/`, `/series/`, `/author/`, `/profile/`) with `gzip_static on; brotli_static on;` — avoids redirect loops and sub_filter cost. - Never copy Windows-generated Prisma engine binaries (`query_engine*.node`) or local `node_modules` to the Linux server. - Zarinpal requires `ZARINPAL_MERCHANT_ID` in env; until set, subscribe returns `payment_unavailable`. Use `ZARINPAL_SANDBOX=1` for testing. - Phase 12 requires `cd backend && npm install web-push` and VAPID key generation once on Linux. - Phase 14 requires `CREATE EXTENSION vector;` as PostgreSQL superuser before `prisma:apply`. - After first production login, rotate writer-admin passwords via admin UI, then remove `resetPasswordOnSeed` from `backend/prisma/credentials.json`. ## Large Codebase Analysis (Gemini CLI) When multiple large files exceed context, use `gemini -p` with `@` path syntax: ```bash gemini -p "@src/ @backend/src/ List all API endpoints and their auth requirements" gemini -p "@teknav-admin.jsx @backend/src/routes/admin.ts Trace the article publish flow end-to-end" gemini --all_files -p "List all Prisma models and their relations" ``` Paths are relative to where you run `gemini`. Use for cross-file architecture questions or verifying feature coverage across many files. ## Memory See `MEMORY.md` for full stack context and key paths. See `ARCH.md` for the active roadmap and Phase 14 Linux validation checklist. See `DEPLOY.md` for the full production runbook. See `SECURITY.md` for the pre-deploy security checklist and nginx/Fastify hardening details. See `PHASES.md` for completed phase history. See `PHASE16.md` for the planned English `/en/` subdirectory (blocked on LTR layout pass — not started). Quick reference: - **Stack:** Vite 5 + React 18 / Fastify 4 + Prisma 5 / PostgreSQL 16 + pgvector + Redis 7 - **Current phase:** 15 (implemented; Phase 14 Linux validation pending) - **Writers:** آرسام صباغ (`arsam-sabbagh`), سیداحمدرضا محجوب (`ahmadreza-mahjoub`), رادمان قلیچی (`radman-ghelichi`) - **Constraints:** Zero CDN · Persian-first RTL · native Linux/systemd · no mock admin data