Import the existing local project snapshot. This commit organizes existing files; it does not represent newly developed functionality.
26 KiB
PHASES.md
Completion log. Active work and future roadmap live in ARCH.md.
Current Production Baseline
Teknav is a Persian RTL Vite/React SPA backed by Fastify, Prisma, PostgreSQL, Redis, Argon2id sessions, CSRF, Persian search, SEO artifacts, admin CMS APIs, media uploads, scheduled publishing, SMTP email flows, and Kavenegar OTP. Production deployment is native Linux/systemd with nginx on https://www.teknav.ir; Docker is not the current deployment target.
Phase 1 - Frontend: Zero CDN + Vite
Shipped.
- Vite 5 + React 18.3.1 with ES modules.
- Removed unpkg, Google Fonts, and CDN scripts.
- Local font loading from
public/fonts/. scripts/check-no-cdn.mjsruns onnpm run build.- Dev/preview port is locked to
3009.
Phase 2 - Database + Seed
Shipped.
- Backend workspace in
backend/. - Prisma schema for users, authors, categories, tags, articles, reactions, saved articles, newsletter subscribers, activity logs, and media assets.
- Persian FTS function/indexes are applied idempotently from
backend/prisma/seed.ts. - Seed imports
teknav-data.js, hashes credentials with Argon2id, and creates the three real Teknav authors. - Staff seed credentials live in
backend/prisma/credentials.json; operator-facing credentials live in ignoredACCOUNTS.md.
Phase 3 - Auth
Shipped.
- Redis-backed sessions with
tek_sidandtek_csrf. - Login/logout/me APIs in
backend/src/routes/auth.ts. - CSRF middleware for mutating routes.
- Login/signup captcha and per-IP rate limiting.
- Frontend
AuthProvideruses/api/v1/auth/*on the wire; no localStorage auth source.
Phase 4 - Public API + Caching
Shipped.
- Public articles, categories, tags, authors, search, newsletter, save, and reaction APIs.
- Redis response caching, search caching, newsletter rate limiting, and buffered article view counters.
- Public pages use
src/lib/content-api.js.
Phase 5 - Admin/CMS API
Shipped.
- Role-gated article CRUD, publish, schedule, category/tag/user/author management, uploads, activity logs, and dashboard metrics.
- Admin panels use
src/lib/admin-api.jsand show real API data only. - No mock/example data should appear in admin, writer, reviewer, media, activity, or dashboard views.
- Media uploads are tracked in
MediaAsset; admin/editor media library is backed by/api/v1/admin/media.
Phase 6 - Native Deployment
Shipped.
- Production runbook:
DEPLOY.md. - nginx templates:
nginx/nginx.conf.exampleandnginx/nginx.default.conf. - systemd assets:
deploy/systemd/teknav-backend.service,teknav-migrate.service,teknav-publish-due.service/.timer, andteknav-backup.service/.timer. - Log rotation:
deploy/logrotate/teknav. - Backups:
scripts/systemd-backup.sh. - Schema application:
backend/scripts/apply-schema.mjsvianpm run prisma:apply.
Phase 7 - Production CMS Readiness
Shipped.
- Public profiles and profile editing.
- Avatar uploads and WebP conversion support.
- HTML article editor with image upload.
- Email verification and password reset via SMTP.
- Phone OTP via Kavenegar.
- Article scheduling and due-publish timer.
- Activity CSV export.
- Persian SEO metadata, canonical
www.teknav.ir, sitemap, robots, and RSS feed. - Three writer-admin seed accounts added for آرسام صباغ, رادمان قلیچی, and سیداحمدرضا محجوب.
Phase 8 - Engagement + Real-Time Admin
Implemented; Linux validation pending.
- Prisma schema adds comments, comment flags, reading history, notifications, and writer follows.
- Article pages include real API-backed comments/replies, flagging, history tracking, and comment count.
- Header notifications use authenticated SSE at
/api/v1/auth/notifications/stream. - Writer profiles expose follower count and follow/unfollow; own profile shows reading history and followed writers.
- Admin/editor dashboard uses SSE at
/api/v1/admin/streamfor active visitors, activity, and live view counter updates. - Admin/editor comments panel supports flagged filtering, delete, and clear-flag actions.
- nginx templates disable buffering/compression for SSE stream paths.
Phase 9 - Content Modernization + Publication Features
Implemented. Linux production validation pending.
- Prisma schema adds article SEO fields, daily article view logs, comment likes, newsletter campaigns, article series, editorial review workflow, and reading lists.
- Public API adds related articles, series pages, newsletter unsubscribe, comment likes, and reading-list endpoints.
- Admin API adds analytics, review queue, newsletter campaign send/list, and series CRUD endpoints.
- Frontend article pages consume richer SEO metadata, expose comment likes, keep related articles API-backed, and route public series pages at
/series/:slug. - Admin panel adds API-backed Phase 9 sections for analytics, review workflow, newsletter campaigns, and article series.
teknav-data.jsfinalizes Phase 9 seed/reference metadata: 23 published articles, refreshed dates, canonical paths, Open Graph metadata, and generated content for former placeholders.scripts/generate-seo.mjsnow usesdateModified, canonical paths, meta descriptions, and RSScontent:encoded.scripts/generate-og-images.mjsprovides local OG image generation for article cards; install dependencies on Linux before use.- Series prev/next navigation banner added to article pages (
SeriesNavBanner), pulling live series membership from the public article API.
Phase 10 - SEO Growth, Trust, and Writer Studio
Implemented. Linux production validation pending.
SEO and trust foundations
- Public topic authority hubs at
/topics/ai,/topics/security,/topics/software,/topics/hardware,/topics/startups, and/topics/datawithCollectionPage,FAQPage,ItemList, andBreadcrumbListJSON-LD. scripts/generate-seo.mjscovers topic hubs, public author profiles, glossary terms, newsletter archive, and/ACCOUNTS.mdrobots blocking.- Prisma schema adds editorial trust fields (
factCheckedAt,reviewedById,sourceNotes,contentFreshnessStatus),TopicFollow, newsletter campaign slugs, and the first-partyAnalyticsEventtable. - First-party analytics events for article views, newsletter subscriptions, saves, writer/topic follows, and share clicks.
- Topic follows at
/api/v1/topics/:slug/followwith counts on hub pages. - Public newsletter archive at
/newsletterand/newsletter/:slug. - Role-aware panel metrics at
GET /api/v1/admin/panel-metrics. - Article approval-to-publish quality gates: review workflow, Phase 10 metadata completion, cache refresh, and writer/topic follower notifications.
- Public tag pages and
GET /api/v1/articles?tag=.... - Admin API adds
GET /api/v1/admin/seo/audit,GET /api/v1/admin/analytics/content,GET /api/v1/admin/analytics/newsletter,GET /api/v1/admin/analytics/engagement. npm run smoke:prodproduction health checks.
Community and retention
- PWA with
manifest.json, service worker, offline caching, and install prompt. - Personalized home feed (
sort=personalized) using followed topics, followed writers, and read history. - Comment upvoting with best-comment sorting; expanded heart/fire/thinking reactions.
- 5-day reading streaks and "Continue Reading" progress tracking.
- Threaded article Q&A at
/api/v1/articles/:slug/qa. - Glossary/dictionary term pages at
/glossary/:slug. - "X vs Y" comparison templates with responsive comparison-table styling.
Auth and trust
- Google/GitHub OAuth at
/api/v1/auth/oauth/:provider. - SMS 2FA via Kavenegar OTP;
tek_2faticket-based login completion. - Verified expert badges on author records, profiles, lists, and article bylines.
- Published article corrections/errata via
ArticleCorrection. - Google Discover readiness: 1200×630 local hero images,
fetchPriority="high",max-image-preview:large.
Monetization foundations
/jobswithJobListingand company submission API./courseswith course API and public page./membershippremium membership page.User.membershipTier,Article.premiumOnly, content locking for non-premium readers.Article.sponsoredand public "Promoted Content" labels.
Operations
- Global Fastify rate limiting.
- Bulk comment moderation.
- First-party client error capture at
/api/v1/errors. backend/tests/phase10-smoke.test.mjsfor auth/article/comment smoke coverage.- Weekly newsletter digest job:
npm run newsletter:digestandteknav-weekly-digest.timer.
Writer Studio V5 (close-out polish)
- HtmlEditor toolbar: grouped buttons (Formatting | Headings | Inline+Block | Lists | Media) with hover states; Bold/Italic labels styled; keyboard shortcuts Ctrl+B, Ctrl+I in editor and Ctrl+S to save draft globally.
- Live word count and character count footer on the editor textarea.
- WritingTipCard: manual prev/next navigation with position counter; auto-advance timer resets on manual nav.
- ArtifactStudio diagram library: gradient placeholder cards replaced with live scaled
DiagramRenderer compactmini-previews. - ArtifactStudio code mode: relabelled "code preview" with empty-state hint; was falsely claiming "live preview".
- WeeklyChart data pipeline fixed: was reading non-existent
weeklyViews; now correctly usesviewsByDay.slice(-7).map(d => d.views). - Analytics sparklines upgraded: full-width filled-area SVG with gradient, dot on latest point, "۳۰ روز پیش → امروز" axis labels.
- Analytics table: sparklines prefetched for all articles on mount; inline 80×20 mini-sparkline per row; totals footer row; column header corrected to "روند اخیر".
- Overview tab: amber
needsRevisionbanner when reviewer has flagged articles; dynamic time-of-day greeting (صبح/ظهر/عصر/شب بخیر) with matching tagline. - Article editor meta-panel: fields grouped into "اطلاعات پایه", "زمانبندی", and collapsible "سئو پیشرفته" sections; slug field added to SEO section.
- Article editor preview mode: renders selected diagram (
DiagramRenderer) and subtitle above content. - Article editor: dirty-state indicator (● ذخیره نشده · Ctrl+S) shown when form has unsaved changes.
- Article editor: amber "بازخورد ویراستار" banner when opening a
نیازمند اصلاحarticle that has a reviewer note. - Articles list: hard cap of 15 replaced with show-more/less pagination button; reviewer note shown inline for revision-flagged rows.
- Overview recent-articles: reviewer note shown inline for
نیازمند اصلاحarticles. - Comments tab: article title chip next to commenter name; "8-recent" cap note with redirect to article; button relabelled to direct to full comment thread.
- Backend:
articleIncludejoinsreview { note, status };publicArticleexposesreviewNote; this flows through writer dashboard, articles list, and editor.
Phase 11 — Platform Completeness
Implemented. Linux production validation pending.
Track 1 — Admin completeness
- Sponsored / premiumOnly / featured toggle buttons in
ArticleEditormeta-panel (flags group). - Content freshness filter dropdown (
current/needs_update/scheduled_refresh/archived) in articles management list. - Dedicated SEO audit tab in AdminPanel — full paginated table with severity badges (خطا/هشدار/پیشنهاد) and per-article links; removed hard-capped inline audit from analytics panel.
- Dedicated engagement analytics sub-tab inside analytics panel — 7-day bar chart, event-type breakdown table, top-5 articles.
- Bulk comment moderation: checkbox per row, floating action bar (حذف/تأیید/علامتگذاری/لغو انتخاب), wired to existing
POST /api/v1/admin/comments/bulk.
Track 2 — Reader UX
- Article card reading list picker: bookmark icon (admin only for logged-in users) opens
ReadingListModalfrom any card on the home feed. - Continue-reading progress overlay: progress bar + "ادامه مطالعه" chip on home feed cards when user has 5–95% progress via
engagementApi.getStreaks(). - Scheduled-publish topic notifications:
backend/scripts/publish-due.mjsnow also queriesTopicFollowfor the article's category and notifies topic followers (deduped against writer-follow notifications). - Google Search Console and Bing Webmaster placeholder verification files in
public/(google-token-placeholder.html,BingSiteAuth.xml).
Track 3 — Monetization
- Premium gate card redesign: blurred/faded content with
mask-imagegradient,PremiumGateCardcomponent with crown icon, 3-bullet benefit list, "عضویت ویژه" CTA, and "ورود به حساب" secondary for unauthenticated users. - Zarinpal membership subscription flow:
POST /api/v1/membership/subscribe→ Zarinpal payment request → gateway redirect;GET /api/v1/membership/callback→ verify → upgradeUser.membershipTier='premium'+membershipExpiresAt+Notification;GET /api/v1/membership/status;MembershipPagewired with live status and subscribe button;MembershipSuccessPageat/membership/success. - Job listing admin panel:
GET /api/v1/admin/jobs(with status filter) andPATCH /api/v1/admin/jobs/:id(approve/reject/featuredUntil) inmonetization.ts;JobsPanelcomponent in admin sidebar;JobListing.featuredUntilfield added to schema. - Premium enforcement confirmed:
publicArticle()inpublic.tsalready strips body and setspremiumLocked: truefor non-premium users onpremiumOnlyarticles.
Track 4 — Reliability
ClientErrorPrisma model: persists client errors fromPOST /api/v1/errors(was logging-only);GET /api/v1/admin/errorsreturns top-10 messages by count, daily counts (30 days), and 20 most recent raw errors;ClientErrorsPaneladmin tab with daily sparkline, top-errors table, recent errors list.StorageAdapterinterface inbackend/src/lib/storage.ts:LocalStorageAdapter(default, active onSTORAGE_BACKEND=localor unset — all uploads go toconfig.UPLOAD_DIR),S3StorageAdapterstub (activate withSTORAGE_BACKEND=s3; not yet wired to a real S3 client). Upload handler inadmin.tsswitched to use the adapter.- Schema additions:
ClientError,MembershipPayment,JobListing.featuredUntil,NotificationType.system.
Data completeness
teknav-data.jsexpanded to 32 articles (art1–art32) with full content viaarticle-contents-*.jsfiles; allcontentFreshnessStatusvalues corrected fromfresh/stabletocurrent.
Phase 12 — Web Push + Published Article Editing
Implemented. Linux production validation pending (VAPID keys + web-push install + PushSubscription schema apply).
Web push notifications
PushSubscriptionPrisma model:id,userId,endpoint(unique),auth,p256dh,userAgent,createdAt;@@index([userId]); cascade-delete on user removal.backend/src/lib/push.ts: VAPID init guarded by env var presence (no-op if unconfigured);buildPushPayload()maps all 8NotificationTypevalues to Persian{title, body, url, icon};sendPushToUser()queries subscriptions, sends viaweb-push, auto-deletes expired (410) endpoints.- Three new authenticated routes in
notifications.ts:GET /api/v1/auth/push/vapid-key(returns public key),POST /api/v1/auth/push/subscribe(upsert by endpoint),DELETE /api/v1/auth/push/unsubscribe. createNotification()inrealtime.tsextended: fire-and-forgetsendPushToUser()call after SSE publish; in-app notification path unchanged.- Service worker:
pushevent handler shows RTL/Persian notification with badge;notificationclickhandler focuses existing window or opens new one atdata.url. pushApiclient inengagement-api.js:getVapidKey(),isSubscribed(),subscribe(),unsubscribe()with CSRF token from cookie.PushNotificationPromptdismissible banner inteknav-ui.jsx: shown to authenticated users whenNotification.permission === 'default'and not previously dismissed; bottom-right dark overlay, RTL.- Push toggle in
ProfileSettingsModal(teknav-profile.jsx): handles enabled / blocked / unsupported states; callspushApi.subscribe()/unsubscribe().
Published article editing
ArticleEditoracceptsroleprop (default'writer'); derivesisPublishedandcanPublishDirectly(true for admin/editor).- Info banners: blue for writers (changes go to draft/review), green for admin/editor (direct publish with Ctrl+S note).
- Button set branches: admin/editor on published article → "بروزرسانی و انتشار" / "تبدیل به پیشنویس" / "زمانبندی مجدد"; writer on published article → "ذخیره پیشنویس" / "ارسال برای بررسی مجدد".
- Backend PATCH bug fixed in
admin.ts: removedPUBLISHED → PENDINGconversion in bulk data update;requestedStatus === PUBLISHEDis now excluded from the initial DB write and handled cleanly by the existingpublishArticleWithPhase10Checks()block (preserves originalpublishedAt, runs quality gates). quality_check_failed(409) error surfaced inArticleEditorsave handler — shows specific issue list via toast.
Bug fixes
scripts/generate-seo.mjs: localxmlvariable insideurl()function renamed tolines; was shadowing the outerxml()HTML-escape function, causing image titles to not be escaped.
Phase 13 — Community Depth + Writer Tools
Implemented. Linux production validation pending (requires prisma:apply for new schema additions).
Schema additions
PushSubscription.userIdmade nullable (supports guest subscriptions);topics String[]array added.NotificationPreferencemodel: per-user, per-eventType, per-channel (push/email/sms) opt-out toggle; defaults to enabled.UserBadgemodel: earned reading badges (AI enthusiast, streak-7, etc.) with@@unique([userId, badgeType]).ArticleRevisionmodel: content snapshot before each article PATCH, capped at 20 per article.
Web Push for guests
POST /api/v1/push/guest-subscribe(unauthenticated, IP-rate-limited 5/min): storesPushSubscriptionwithuserId=nullandtopicsarray.DELETE /api/v1/push/guest-unsubscribe: removes guest subscription by endpoint.GET /api/v1/push/vapid-key: unauthenticated VAPID public key endpoint.backend/src/lib/push.ts:sendGuestPushByTopic(topic, payload)fans out touserId IS NULL AND topics @> [topic].backend/scripts/publish-due.mjs: after notifying registered topic followers, also callssendGuestPushfor each published article's topic.- Topic hub pages (
teknav-pages.jsx): "🔔 دریافت اعلان مقالات" button next to follow button; works without login; persists in browser push subscription.
Fine-Grained Notification Center
GET /api/v1/auth/notifications/preferences: returns full 8×3 matrix (eventType × channel) with enabled state (default true for unset).PUT /api/v1/auth/notifications/preferences: batch upsert via@@unique([userId, eventType, channel]).sendPushToUser()inpush.tsnow checksNotificationPreferenceforchannel='push'before delivering — honors user opt-out.NotificationPreferencesPanelcollapsible section inProfileSettingsModal: toggle grid with inline optimistic updates.
Verified Reader Badges
- Badge rules:
ai_enthusiast,security_expert,data_scientist,hardware_geek,software_craftsman,startup_explorer(10 articles read in topic), plusstreak_7andstreak_30(streak count). - Evaluation runs in
publish-due.mjscron — NOT on the article-view hot path. GET /api/v1/auth/badgesandGET /api/v1/admin/users/:id/badgesendpoints.engagementApi.listBadges()client method.
Article Version History
ArticleRevisionsnapshot created before every PATCH update inadmin.ts(captures title, content, summary, status, savedById).- Pruned to 20 most recent per article at write time.
GET /api/v1/admin/articles/:id/revisions(paginated list, no content),GET /api/v1/admin/articles/:id/revisions/:revisionId(with full content),POST /api/v1/admin/articles/:id/revisions/:revisionId/restore(admin/editor only; snapshots current state before restoring).RevisionDrawerside-panel inArticleEditor: lists revisions with date/author/status; click to preview content; "بازیابی این نسخه" restores for admin/editor.revisionsApiclient inengagement-api.js.
Collaborative Editing Presence
- Heartbeat:
POST /api/v1/admin/articles/:id/heartbeatsetsRedis SET presence:article:{id}:user:{uid}with 45s TTL. GET /api/v1/admin/articles/:id/presence: SCAN + MGET, enriches with user name/avatar from DB.ArticleEditorfires heartbeat on mount and every 30s; polls presence every 30s.- Shows avatar-chip row in editor header when 2+ editors are online on the same article.
presenceApiclient inengagement-api.js.
Production Follow-Ups
- On Linux: run
npm run preflight:prod, thencd backend && npm run prisma:generate && npm run prisma:apply && npm run seed, then frontend SEO/OG generation, frontend/backend builds, strict backend endpoint tests, andnpm run smoke:prod. - Verify all flows after schema apply: auth, comments/likes/flags, history, notifications/SSE, reading lists, topic/writer follows, tag pages, series pages, topic hubs, newsletter archive/campaigns, review workflow, analytics endpoints, panel metrics, first-party events.
- Rotate all seed/admin passwords after first production login.
- Remove
resetPasswordOnSeedfrom writer-admin credentials after rotation. - Confirm SMTP and Kavenegar credentials on the server before sending emails/OTPs.
Post-Phase 13 Production Hardening
Implemented.
- Added root
.env.prod.examplefor/etc/teknav/backend.env, covering database, Redis, sessions, CORS, uploads, SMTP, SMS, VAPID, Zarinpal, and local storage defaults. - Added
npm run preflight:prodviascripts/production-readiness.mjsto catch missing templates, nginx SSE/credential-protection regressions, generated Prisma engine artifacts, and missing test coverage before deployment. - Added
backend/tests/public-contract.test.mjsfor public/anonymous API contract checks, and made smoke tests strict withTEST_REQUIRE_SERVER=1when a running backend must be validated. - Added minimal public
/api/v1/health, added/api/v1/readydependency readiness, and added admin-only/api/v1/admin/healthdiagnostics with non-secret feature flags.
Phase 14 — Farsi Knowledge & Tech Ecosystem
Implemented. Linux production validation pending (CREATE EXTENSION vector, prisma:apply).
- Wiki & Glossary Proposals:
WikiProposalandGlossaryProposalPrisma models;POST /api/v1/wiki/proposalsandPATCH /api/v1/admin/wiki/proposals/:id;DiffViewerReact component. - Automated Internal Linking Engine:
link-engine.tswithcheerio; injects<a href="/glossary/:slug">anchors into published articles. - Semantic Search & RAG:
ArticleEmbeddingmodel; pgvector;POST /api/v1/search/semantic;GET /api/v1/rag/:slug. - LMS:
LearningPath,LearningModule,Quiz,UserCertificationmodels; auto-certification at ≥80% pass score. - Tech Directory (backend):
CompanyProfile,CompanyClaimPrisma models;DirectoryPaneladmin UI. - Production note: requires
CREATE EXTENSION vectoras PostgreSQL superuser beforeprisma:apply.
Phase 15 — Viewership & Top-of-Funnel Scaling
Implemented. Build verified: 0 CDN refs, 0 preflight failures.
15A — Web Stories:
- CSS scroll-snap story pages for all published articles at
dist/stories/{slug}/index.html. - OG + Twitter Card meta tags on each story page; keyboard navigation (arrow keys).
sitemap-stories.xmladded to sitemap index;<link rel="alternate">on every article page.scripts/generate-web-stories.mjs— build script; runs aftergenerate-article-pages.
15B1 — Quote Card Sharer:
src/components/QuoteSharer.jsx— Selection API tooltip, Canvas 1080×1080 branded card.- RTL text wrapping, dark gradient, gold separator, Teknav branding.
- Actions: Web Share API → PNG download fallback, copy-to-clipboard (text + URL), Escape-to-close.
- Analytics:
contentApi.trackEvent({ type: 'share_clicked', metadata: { channel: 'quote_card' } }). - Mounted in
ArticleDetailfor non-premium, non-TekRAG articles.
15B2 — Embeddable Widget System:
embed.html— second Vite rollup entry;<meta name="robots" content="noindex,nofollow">.src/embed.jsx— bootstrapper routing by URL path type.src/widgets/GlossaryWidget.jsx/ArticleCardWidget.jsx/CompanyWidget.jsx— dark mode, postMessage resize on height change only.public/embed-loader.js— third-party<script>snippet injecting sandboxed<iframe>.- nginx:
/embed/location withframe-ancestors *CSP.
15C1 — Comparison Pages:
- 10 comparisons in
TeknavData.comparisons;dist/compare/{slug}/index.htmlwith ItemList + FAQPage + BreadcrumbList JSON-LD. ComparisonPage— two-column pros/cons, use cases, verdict, localStorage vote widget, share button,factCheckedAt.ComparisonsListPageat/comparewith search/filter.- Related comparisons block auto-injected on article pages.
scripts/generate-comparison-pages.mjs.
15C2 — Tech Directory:
- 10 companies in
TeknavData.companies;dist/directory/{slug}/index.htmlwith Organization + BreadcrumbList JSON-LD. DirectoryListPage+CompanyProfilePagewith claim button.GET /api/v1/directory/companiesandGET /api/v1/directory/companies/:sluginbackend/src/routes/directory.ts.scripts/generate-company-pages.mjs.
Additional:
StoriesListPageat/stories— 9:16 thumbnail grid with title + category overlay, opens story in new tab.- Footer "ابزارها" nav section: compare, stories, directory, glossary, tools.
- Profile badge display — 8 badge types from
engagementApi.getBadges(). - art37
vibe-coding-mcp-2026— 14-section Persian article on Vibe Coding + MCP (9 h2 sections, 14KB).