Import the existing local project snapshot. This commit organizes existing files; it does not represent newly developed functionality.
177 lines
13 KiB
Markdown
177 lines
13 KiB
Markdown
# 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): `<p class="lead">` opening, numbered `<h2>` sections, `<strong>` 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 `<a href="/glossary/:slug">` 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
|