21 KiB
راهنمای جامع پروژه اندیشکده فولاد آینده (Andishkade Foolad)
این سند، راهنمای فنی و معماریِ پروژه اندیشکده فولاد آینده است — یک پلتفرم تحلیلیِ صنعت فولاد، شاملِ یک وبسایتِ محتوایی و یک پنلِ مدیریت/بکاند. در این سند ساختار پوشهها، پشتهی فناوری، پایگاه داده، احراز هویت، قابلیتها و نحوهی استقرار توضیح داده شده است.
فهرست مطالب
- ۱. ساختار کلی پروژه
- ۲. فرانتاند (
frontend/) - ۳. پنل مدیریت و بکاند (
panel/) - ۴. پایگاه داده
- ۵. احراز هویت و اعضا
- ۶. اتصال فرانتاند به پنل (Wiring)
- ۷. فهرست APIها
- ۸. استقرار (Deployment)
۱. ساختار کلی پروژه
پروژه یک مونوریپو با دو اپلیکیشنِ مستقل و کنارهم (sibling) است که هیچ build یا وابستگیِ مشترکی ندارند؛ فرانتاند فقط در زمانِ اجرا (runtime) و از طریق متغیر VITE_PANEL_API با پنل حرف میزند.
andishkade-foolad/
├── frontend/ # وبسایت عمومی — Vite + React 19 + TypeScript
│ ├── src/
│ │ ├── app/ # مسیریابی (router) و قالب اصلی (RootLayout)
│ │ ├── components/ # کامپوننتهای اشتراکی و UI
│ │ ├── context/ # کانتکستها: زبان (LangContext) و احراز هویت (AuthContext)
│ │ ├── content/ data/ # دادههای اولیه/ماک + ماژولهای bootstrap که از پنل واکشی میکنند
│ │ └── pages/ # صفحات سایت
│ ├── public/ # داراییهای استاتیک، فونتها، manifest و service worker (PWA)
│ ├── index.html
│ ├── vite.config.ts tsconfig*.json eslint.config.js
│ ├── Dockerfile nginx.conf liara.json # استقرار بهصورت سرور استاتیک Nginx روی پورت ۷۸۶۰
│ └── package.json
├── panel/ # بکاند (Express API) + پنلِ مدیریت (CMS) — یک اپ واحد
│ ├── server.js # سرور و همهی Routeهای API
│ ├── db.js # اتصال PostgreSQL + facade شبیه better-sqlite3 + مپ ردیفها
│ ├── public/ # رابط کاربری پنل (SPA با Vanilla JS، بدون build)
│ ├── uploads/ # آپلودِ محلی (در صورت نبودِ S3)
│ ├── liara.json
│ └── package.json
├── docker-compose.yml # اجرای لوکالِ فرانت (context: ./frontend)
├── CLAUDE.md README.md README_FA.md SECURITY.md design-system.md
نکته: پنل هم بکاندِ API است و هم CMS (رابطِ مدیریت را از
panel/public/سرو میکند). این دو بههم گره خوردهاند و یک اپلیکیشنِ واحدند.
نمودار معماری
┌──────────────────────────┐
│ مرورگرِ کاربر │
└───────────┬──────────────┘
صفحاتِ سایت │ │ پنلِ مدیریت (ادمین)
▼ ▼
┌───────────────────────────┐ ┌───────────────────────────────┐
│ FRONTEND (اپِ Liara: │ │ PANEL (اپِ Liara: │
│ steelforesight) │ │ cms-steelforesight) │
│ Nginx → فایلهای استاتیکِ │ │ Express (server.js) │
│ dist/ (پورت ۷۸۶۰) │ │ + رابطِ CMS از panel/public/ │
│ React 19 / Vite │ │ │
└───────────┬───────────────┘ └──────┬───────────┬────────┬───┘
│ fetch (runtime) │ │ │
│ VITE_PANEL_API ─────────┘ │ │
▼ ▼ ▼
┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ REST /api │ │ PostgreSQL │ │ S3 Storage │ │ Kavenegar SMS │
│ (JSON) │ │ (Liara) │ │ (Liara) │ │ OpenAI/Claude │
└──────────────┘ └──────────────┘ └──────────────┘ └──────────────┘
- دو اپِ کاملاً جدا: هرکدام Dockerfile/استقرارِ خودش را دارد و جداگانه روی Liara مستقر میشود. هیچ کدِ مشترکی بین
frontend/وpanel/import نمیشود. - زمانِ build (فرانت): مقدارِ
VITE_PANEL_APIدر زمانِvite buildداخلِ باندل تزریق میشود (نه در زمانِ اجرا). یعنی آدرسِ پنل در فایلهای استاتیکِ خروجی ثابت میشود. - زمانِ اجرا: مرورگر فایلهای استاتیک را از فرانت میگیرد، سپس مستقیماً با
fetchبهhttps://cms-steelforesight.liara.run/api/...وصل میشود (cross-origin، باcredentials: 'include'برای کوکیِ نشست). CORS در پنل باALLOWED_ORIGINSکنترل میشود. - پنل ↔ سرویسها: پنل تنها مؤلفهای است که به PostgreSQL، object storageِ S3، پیامکِ کاوهنگار و APIهای هوش مصنوعی وصل میشود؛ فرانت هیچوقت مستقیماً با اینها حرف نمیزند.
لایهبندیِ فرانتاند (jریانِ یک درخواست)
main.tsx → bootstrap*() → fetch(VITE_PANEL_API + /api/...) → پر کردنِ ماژولهای content/data
│ │
│ (در صورت خطا/آفلاین: fallback به دادهٔ ایستا)
▼
RouterProvider → RootLayout (Header / Footer / Lenis / بومیسازِ ارقام / ردیابِ فعالیت)
▼
صفحه (src/pages/*) → useLang() برای fa/en · useAuth() برای نشستِ عضو
لایهبندیِ پنل (jریانِ یک درخواست)
درخواست → helmet → CORS(ALLOWED_ORIGINS) → cookieParser → rate-limit
→ میدلورِ احراز هویت (authRequired برای ادمین / memberRequired برای عضو)
→ هندلرِ Route → db.prepare(sql).get/all/run() ↔ PostgreSQL
→ (آپلود: multer → sharp/WebP → S3 یا دیسک)
→ پاسخِ JSON
۲. فرانتاند (frontend/)
پورتالِ محتواییِ فارسیمحور (Persian-first)، دوزبانه (fa/en) و RTL با انیمیشنهای غنی.
پشتهی فناوری
- هسته: React 19 + TypeScript.
- بیلد: Vite (HMR در توسعه، خروجی بهینه در پروداکشن). اعتبارسنجیِ سریع:
npx tsc -b --noEmit. - استایل: عمدتاً
styleاینلاین + کلاسهای Tailwind CSS v4 برای واکنشگرایی (max-md:…) + متغیرهای CSS درsrc/index.css. رنگِ سازمانی: طلایی#CD9E53و سورمهای#032340(در بسیاری فایلها بهصورتconst GOLD/const NAVYهاردکد شدهاند). - مسیریابی: React Router DOM v7 (
createBrowserRouter).RootLayoutهمهی صفحات را با Header/Footer و موتورِ اسکرول میپیچد. - انیمیشن و اسکرول:
- GSAP + ScrollTrigger: انیمیشنهای مبتنی بر اسکرول.
- Lenis: اسکرولِ نرم هماهنگ با تیکرِ GSAP — فقط دسکتاپ (روی لمسی به مومنتومِ بومیِ سیستمعامل واگذار میشود تا لگ نشود).
- Framer Motion: میکروانیمیشنها، پاپآپها و ترنزیشنها.
- سهبعدی و نقشه: Three.js + React Three Fiber،
three-globeوreact-globe.glبرای کرهی زمینِ تعاملی. - نمودار: ApexCharts (صفحهی Market).
- مدیریت وضعیت: صرفاً React Context (زبان و احراز هویت). بدون کتابخانهی state خارجی.
قابلیتهای کلیدی
- دوزبانه/RTL: هوک
useLang()(src/context/LangContext) مقدار{ lang, toggle }میدهد؛ کامپوننتها دیکشنریِ درونخطیِ{ fa, en }دارند و جهتِ صفحه باdirتنظیم میشود. - اعدادِ فارسی: یک «بومیسازِ ارقام» در
RootLayoutهمهی ارقامِ لاتینِ نمایشدادهشده را در حالتِ فارسی به فارسی تبدیل میکند (با MutationObserver برای محتوای دیرلودشده). - تاریخ شمسی: بدون کتابخانهی تاریخ — مستقیماً با
Intl.DateTimeFormat('fa-IR-u-ca-persian', …). - PWA و موبایل: manifest + service worker (نصبپذیری + کشِ آفلاین)؛ صفحه روی موبایل غیرقابلِ زوم است (viewport + بلوکِ pinch/double-tap برای iOS).
- جستجوی سراسری: آیکونِ سرچِ هدر → صفحهی
/searchکه بهصورت کلاینتساید روی همهی محتوا (گزارشها، مطالب، «در یک نگاه»، رادار آینده، ویدیوکست/پادکست، رویدادها) میگردد و به صفحهی جزئیاتِ هر مورد لینک میدهد.
دستورات (در پوشهی frontend/)
cd frontend
npm install
npm run dev # سرورِ توسعهی Vite (HMR)
npx tsc -b --noEmit # فقط type-check (سریعترین حلقهی اعتبارسنجی)
npm run lint # ESLint
npm run build # tsc -b && vite build → خروجی در dist/
npm run preview # پیشنمایشِ بیلدِ پروداکشن
برای دیدنِ دادهٔ واقعیِ پنل در حالتِ توسعه:
VITE_PANEL_API=https://cms-steelforesight.liara.run npm run dev
هیچ فریمورکِ تستی پیکربندی نشده؛ «تأیید» یعنی
tscتمیز + بازدیدِ سرورِ در حالِ اجرا.
۳. پنل مدیریت و بکاند (panel/)
سرورِ Express که هم API میدهد و هم رابطِ مدیریت (CMS) را سرو میکند.
پشتهی فناوری
- سرور: Express 4 روی Node.js.
- پایگاه داده: PostgreSQL (لیارا) با ماژول
pg(Pool رویDATABASE_URL). درdb.jsیک facadeِ شبیهِbetter-sqlite3ساخته شده:db.prepare(sql).get/all/run(...)— همه async و باید await شوند. جداول و ایندکسها در زمانِ استارتآپ بهصورت idempotent ساخته/مهاجرت میشوند. - امنیت/احراز هویت:
bcryptjs(هشِ گذرواژه)،jsonwebtoken(کوکیِ HttpOnly باSecure/SameSiteپشتِ TLS)،helmet(هدر/CSP)،express-rate-limit(محدودسازیِ ورود و فرمِ تماس). CORS باALLOWED_ORIGINSکنترل میشود (و در توسعه هرlocalhost). - آپلود و تصویر:
multer(بافرِ حافظه) +sharp(چرخشِ EXIF، ریسایز تا ۲۰۰۰px و خروجیِ WebP).pdf-parseبرای استخراجِ متنِ PDF. - ذخیرهسازی: S3-compatible (object storage لیارا) در صورتِ تنظیمِ متغیرها؛ وگرنه fallback به دیسکِ محلی
panel/uploads/. - هوش مصنوعی: SDKهای OpenAI و Anthropic (Claude) برای خلاصهسازی و استخراجِ دادهٔ کلیدی از اسناد.
- رابطِ مدیریت: SPA با Vanilla JS در
panel/public/(بدون build). تبها در دو گروه: محتوا (در یک نگاه، رادار آینده، ویدیوکست/پادکست، رویدادها، بنرها) و کاربران (کاربران سایت، خبرنامه).
دستورات (در پوشهی panel/)
cd panel
npm install
npm run seed # ساختِ ادمینِ اولیه از روی .env (ADMIN_USERNAME/PASSWORD)
npm start # node server.js → http://localhost:3001
npm run dev # node --watch server.js
۴. پایگاه داده
جداولِ اصلی (PostgreSQL):
| جدول | کاربرد |
|---|---|
users |
ادمینهای پنل با نقشِ admin یا owner |
articles |
مقالات/گزارشها (مدلِ منعطف؛ با category/type تفکیک میشود) |
radar_items |
آیتمهای «در یک نگاه» در ۵ دسته: market/tech/commodity/geo/energy |
radar_pages |
صفحاتِ «رادار آینده» (مقالهی بلوکیِ هر زیرمنو) |
risk_signals |
سیگنالهای ریسک با سطوحِ critical…opportunity |
events |
رویدادهای تقویم |
team_members |
اعضای تیم/خبرگان |
market_prices, market_chart_points |
قیمتها و نمودارهای صفحهی Market (دستی، ادیتشونده در پنل) |
banners, integrations, institute_stats, vision_items, advisory_board, plans, factory_reports |
بخشهای مختلفِ محتواییِ سایت |
members |
اعضای ثبتنامیِ سایت (موبایل/OTP) + last_login, is_active |
purchases |
خریدِ گزارشهای غیررایگانِ اعضا |
member_activity |
لاگِ فعالیتِ اعضا (بازدیدِ صفحه + کلیک) |
subscribers |
ایمیلهای عضویت در خبرنامه (فرمِ فوتر) |
جدولِ
prices(اسکرپرِ قدیمیِ tgju) دیگر استفاده نمیشود؛ اسکرپرِ زنده حذف شده و صفحهی Market ازmarket_pricesمیخواند.
۵. احراز هویت و اعضا
دو نشستِ مجزا:
- ادمینِ پنل: کوکیِ
session(جدولِusers). نقشِownerمیتواند ادمینها و اعضا را حذف کند. - عضوِ سایت: کوکیِ
member_session(جدولِmembers).
ورود/ثبتنامِ اعضا با موبایل + کدِ یکبارمصرف (OTP): ارسالِ پیامک با کاوهنگار (KAVENEGAR_API_KEY). در نبودِ کلید (محیطِ لوکال) کد در کنسولِ سرور چاپ میشود تا تست ممکن باشد. بازنشانیِ رمز هم از طریقِ SMS ممکن است.
مدیریتِ کاربران در پنل (تب «کاربران سایت»): فهرستِ اعضا با شماره و آخرین ورود، صفحهی جزئیات (خریدها + تایملاینِ فعالیت: بازدید/کلیک)، مسدودسازی/رفعِ مسدودی (is_active) و حذف. عضوِ مسدودشده در سمتِ سایت هم اثر میگیرد: AuthContext نشست را هنگامِ لود و بازگشتِ فوکوسِ تب با /api/members/me اعتبارسنجی میکند و در صورتِ 403 کاربر را خارج میکند.
۶. اتصال فرانتاند به پنل (Wiring)
فرانتاند هنگامِ استارتآپ، محتوای چند بخش را از پنل واکشی میکند (در frontend/src/main.tsx توابعِ bootstrap* فراخوانی میشوند) و در صورتِ در دسترس نبودنِ پنل به دادهٔ ایستای داخلِ src/content و src/data بهعنوان fallback برمیگردد. نمونهها: /api/articles (گزارشها)، /api/radar (در یک نگاه)، /api/events، /api/market-prices و غیره. احراز هویتِ اعضا از طریقِ AuthContext و مسیرهای /api/members/* انجام میشود.
آدرسِ پنل از متغیرِ VITE_PANEL_API خوانده میشود (در پروداکشن هنگامِ build در Dockerfile مقدارِ https://cms-steelforesight.liara.run به باندل تزریق میشود).
۷. فهرست APIها
خواندنیها عمومیاند؛ نوشتنیها (POST/PUT/PATCH/DELETE) نیازمندِ کوکیِ نشست یا هدرِ Authorization: Bearer <token> هستند.
| متد | مسیر | احراز هویت | توضیح |
|---|---|---|---|
| POST | /api/auth/login /logout |
— / — | ورود/خروجِ ادمین |
| GET | /api/auth/me |
ادمین | مشخصاتِ ادمینِ جاری |
| GET/POST/PUT/DELETE | /api/articles[...] |
خواندن آزاد / نوشتن ادمین | CRUD مقالات |
| GET/POST/PUT/DELETE | /api/radar, /api/radar-pages, /api/events, /api/banners, /api/risks, /api/team, /api/market-prices … |
خواندن آزاد / نوشتن ادمین | CRUD بخشهای محتوایی |
| POST | /api/uploads, /api/uploads/raw |
ادمین | آپلودِ تصویر (→WebP) / ویدیو و PDF (→S3) |
| POST | /api/admin/import |
ادمین | ایمپورتِ دادهٔ تحلیلیِ هوش مصنوعی |
| POST | /api/members/otp/send /otp/verify |
— | ورود/ثبتنام با OTP |
| POST | /api/members/register-verify |
— | تکمیلِ ثبتنام پس از تأییدِ کد |
| GET/PUT | /api/members/me |
عضو | پروفایلِ عضو |
| GET | /api/members/me/purchases[...] |
عضو | خریدها و دانلودِ گزارش |
| POST | /api/members/activity |
عضو | ثبتِ فعالیت (بازدید/کلیک) |
| GET | /api/members /api/members/:id /:id/activity |
ادمین | فهرست/جزئیات/فعالیتِ اعضا |
| PATCH/DELETE | /api/members/:id |
ادمین/owner | مسدودسازی/حذفِ عضو |
| POST/GET/DELETE | /api/newsletter[...] |
نوشتن آزاد / خواندن ادمین | عضویت در خبرنامه و مدیریتِ آن |
| POST | /api/contact |
— | فرمِ تماس با ما |
| GET | /api/health |
— | سلامتِ سرور |
۸. استقرار (Deployment)
هر دو اپ روی لیارا (Liara) مستقر میشوند (دو اپِ جدا).
فرانتاند — اپِ steelforesight
خروجیِ استاتیک، با Dockerfileِ دومرحلهای: مرحلهی Node برای vite build و سپس Nginxِ سبک برای سروِ dist/ روی پورتِ ۷۸۶۰ (سازگار با Hugging Face Spaces).
cd frontend
liara deploy --app steelforesight --platform docker --port 7860 --build-location iran
VITE_PANEL_API هنگامِ build از طریقِ ARG در Dockerfile تزریق میشود.
پنل — اپِ cms-steelforesight
cd panel
liara deploy --app cms-steelforesight --platform node --build-location iran
متغیرهای محیطیِ لازم (در کنترلپنلِ هاست):
DATABASE_URL— اتصالِ PostgreSQL.JWT_SECRET— رشتهی تصادفیِ بلند (≥۳۲ کاراکتر).ADMIN_USERNAME/ADMIN_PASSWORD— برایnpm run seed.ALLOWED_ORIGINS— دامنهی فرانت برای عبور از CORS.KAVENEGAR_API_KEY— ارسالِ پیامکِ OTP.- اختیاری: متغیرهای ایمیل و object storage (
LIARA_ENDPOINT,LIARA_BUCKET,LIARA_ACCESS_KEY,LIARA_SECRET_KEY).