# راهنمای جامع پروژه اندیشکده فولاد آینده (Andishkade Foolad) این سند، راهنمای فنی و معماریِ پروژه **اندیشکده فولاد آینده** است — یک پلتفرم تحلیلیِ صنعت فولاد، شاملِ یک وب‌سایتِ محتوایی و یک پنلِ مدیریت/بک‌اند. در این سند ساختار پوشه‌ها، پشته‌ی فناوری، پایگاه داده، احراز هویت، قابلیت‌ها و نحوه‌ی استقرار توضیح داده شده است. --- ## فهرست مطالب - [۱. ساختار کلی پروژه](#۱-ساختار-کلی-پروژه) - [۲. فرانت‌اند (`frontend/`)](#۲-فرانت‌اند-frontend) - [۳. پنل مدیریت و بک‌اند (`panel/`)](#۳-پنل-مدیریت-و-بک‌اند-panel) - [۴. پایگاه داده](#۴-پایگاه-داده) - [۵. احراز هویت و اعضا](#۵-احراز-هویت-و-اعضا) - [۶. اتصال فرانت‌اند به پنل (Wiring)](#۶-اتصال-فرانت‌اند-به-پنل-wiring) - [۷. فهرست APIها](#۷-فهرست-apiها) - [۸. استقرار (Deployment)](#۸-استقرار-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 خارجی. ### قابلیت‌های کلیدی 1. **دوزبانه/RTL:** هوک `useLang()` (`src/context/LangContext`) مقدار `{ lang, toggle }` می‌دهد؛ کامپوننت‌ها دیکشنریِ درون‌خطیِ `{ fa, en }` دارند و جهتِ صفحه با `dir` تنظیم می‌شود. 2. **اعدادِ فارسی:** یک «بومی‌سازِ ارقام» در `RootLayout` همه‌ی ارقامِ لاتینِ نمایش‌داده‌شده را در حالتِ فارسی به فارسی تبدیل می‌کند (با MutationObserver برای محتوای دیرلودشده). 3. **تاریخ شمسی:** بدون کتابخانه‌ی تاریخ — مستقیماً با `Intl.DateTimeFormat('fa-IR-u-ca-persian', …)`. 4. **PWA و موبایل:** manifest + service worker (نصب‌پذیری + کشِ آفلاین)؛ صفحه روی موبایل **غیرقابلِ زوم** است (viewport + بلوکِ pinch/double-tap برای iOS). 5. **جستجوی سراسری:** آیکونِ سرچِ هدر → صفحه‌ی `/search` که به‌صورت کلاینت‌ساید روی همه‌ی محتوا (گزارش‌ها، مطالب، «در یک نگاه»، رادار آینده، ویدیوکست/پادکست، رویدادها) می‌گردد و به صفحه‌ی جزئیاتِ هر مورد لینک می‌دهد. ### دستورات (در پوشه‌ی `frontend/`) ```bash 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 # پیش‌نمایشِ بیلدِ پروداکشن ``` برای دیدنِ دادهٔ واقعیِ پنل در حالتِ توسعه: ```bash 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/`) ```bash 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 ` هستند. | متد | مسیر | احراز هویت | توضیح | | :--- | :--- | :--- | :--- | | 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). ```bash cd frontend liara deploy --app steelforesight --platform docker --port 7860 --build-location iran ``` `VITE_PANEL_API` هنگامِ build از طریقِ `ARG` در Dockerfile تزریق می‌شود. ### پنل — اپِ `cms-steelforesight` ```bash 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`).