Import the existing local project snapshot. This commit organizes existing files; it does not represent newly developed functionality.
13 KiB
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 buildfails on any CDN reference viascripts/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 becausesrc/lib/api.jsrewrites them to/api/v1/*.teknav-data.jsandteknav-store.jsare 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.mdis gitignored and nginx-blocked. Never move it intopublic/ordist/. Seed credentials are inbackend/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.csskeyframes/utilities — do not redefine animation primitives. - Phase 15 is current. Keep
ARCH.md,AGENTS.md,DEPLOY.md, andSECURITY.mdaligned 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:prodbefore deploy; runTEST_REQUIRE_SERVER=1 npm testinbackend/against a live backend to enforce endpoint contracts.
Commands
Frontend (repo root):
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):
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=enis a UI preference only; canonical always points to the clean URL. - No placeholder verification codes. Never commit
INSERT_GOOGLE_VERIFICATION_CODEor similar strings. - Static article pages under
dist/article/{slug}/index.htmlare build-time generated — do not edit manually. - Sitemap lastmod must not be future-dated.
capDate()ingenerate-seo.mjsenforces this. - Organization schema:
['Organization', 'NewsMediaOrganization']— required for Google News. - Article schema:
['NewsArticle', 'TechArticle']— required for Google Article rich results. - Run
npm run check:seoafter any article metadata or content change. - Proven content formula (article
ai-data-centric-2026ranked #1 on Google, above Wikipedia):<p class="lead">opening, numbered<h2>sections,<strong>entity highlighting, bilingual terminology (Persian + English inline), technical formulas/data inline,factCheckedAttimestamp, 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/) withgzip_static on; brotli_static on;— avoids redirect loops and sub_filter cost. - Never copy Windows-generated Prisma engine binaries (
query_engine*.node) or localnode_modulesto the Linux server. - Zarinpal requires
ZARINPAL_MERCHANT_IDin env; until set, subscribe returnspayment_unavailable. UseZARINPAL_SANDBOX=1for testing. - Phase 12 requires
cd backend && npm install web-pushand VAPID key generation once on Linux. - Phase 14 requires
CREATE EXTENSION vector;as PostgreSQL superuser beforeprisma:apply. - After first production login, rotate writer-admin passwords via admin UI, then remove
resetPasswordOnSeedfrombackend/prisma/credentials.json.
Large Codebase Analysis (Gemini CLI)
When multiple large files exceed context, use gemini -p with @ path syntax:
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