|
|
||
|---|---|---|
| .claude | ||
| admin | ||
| backend | ||
| deploy | ||
| frontend | ||
| tools | ||
| .env.example | ||
| .gitignore | ||
| ADVERSARIAL-ARCHITECTURE-REVIEW.md | ||
| API-DOCUMENTATION-FA.md | ||
| API-SHARE.md | ||
| DEPLOY-ARVAN.md | ||
| DEPLOY.md | ||
| LIVE-DATA-CATALOG.json | ||
| LIVE-DATA-CATALOG.md | ||
| PRODUCT-CATALOG.csv | ||
| PRODUCT-CATALOG.json | ||
| PRODUCT-CATALOG.md | ||
| PRODUCT.md | ||
| README.md | ||
| SECURITY-CHECKLIST.md | ||
| SECURITY.md | ||
| docker-compose.yml | ||
| liara-deploy.md | ||
README.md
سامانهٔ جامع آمار و اطلاعات «دیدوان»
داشبورد زندهٔ بازار فلزات، فولاد، ارز، طلا/سکه، کامودیتی و کریپتو — بههمراه آرشیو گزارشهای منابع معتبر جهانی. این سند برای توسعهدهندهٔ بعدی نوشته شده؛ کل معماری، نحوهٔ کار، راهاندازی، امنیت و دامها (gotchas) را پوشش میدهد. کامل بخوان.
۱) این سیستم چیست؟
یک اپلیکیشن سهبخشی:
| بخش | تکنولوژی | پورت | نقش |
|---|---|---|---|
| فرانتاند | TanStack Start + React 19 + Vite 7 + Tailwind v4 | 5174 |
لندینگ، ورود/ثبتنام، داشبورد |
| بکاند (API) | FastAPI + SQLite + Playwright | 8000 |
اسکرپرها، API دادهٔ بازار، احراز هویت |
| پنل ادمین | FastAPI (HTML درونخطی) | 8001 |
بازبینی/تأیید کاربران، مدیریت توکن، سلامت سرویسها، گزارشها |
دیتای بازار با اسکرپرهای پسزمینه جمع میشود و در SQLite ذخیره میشود (فقط مقادیرِ تغییرکرده — dedup). فرانتاند هر چند ثانیه از API میخواند و «زنده» نشان میدهد.
۲) ساختار پوشهها
asianmetal-dashboard/
├─ src/ # فرانتاند
│ ├─ routes/ # مسیرهای فایلمحورِ TanStack
│ │ ├─ index.tsx # لندینگ (LaserFlow، پنل زنده، گالریها، …)
│ │ ├─ login.tsx # ورود (کاراکترهای کارتونی + احراز واقعی)
│ │ ├─ register.tsx # ثبتنام (→ pending)
│ │ ├─ dashboard.tsx # داشبورد + همهٔ تبها/کارتها/چارتها
│ │ └─ __root.tsx # لایهٔ ریشه (RTL، فونت Vazirmatn، لود styles)
│ ├─ components/ # LaserFlow, CircularGallery, CardSwap, TubelightNavbar, …
│ ├─ components/ui/ # shadcn/ui
│ ├─ lib/ # labels.ts، metals-data.ts، utils.ts
│ ├─ styles.css # توکنهای تم (oklch)، .tile/.glass/.panel، انیمیشنها
│ └─ routeTree.gen.ts # خودکار توسط TanStack (دستی ویرایش نکن)
│
├─ backend/ # API + اسکرپرها
│ ├─ main.py # اپ FastAPI، CORS/هدرها/ریتلیمیت، همهٔ اندپوینتها، lifespan
│ ├─ db.py # اتصال SQLite + init_db (ساخت/مهاجرت جداول)
│ ├─ auth.py # هشِ رمز (PBKDF2) + نشستهای سمتسرور
│ ├─ currency_stream.py # اسکرپِ ارز/طلا/سکهٔ داخلی (Playwright، هر ۶۰ث)
│ ├─ crypto_market.py # کریپتو از API رایگانِ CoinGecko (هر ۶۰ث)
│ ├─ world_economy.py # شاخصهای کلانِ اقتصادهای بزرگ (هفتگی)
│ ├─ steel_stocks.py # سهام شرکتهای فولادی (companiesmarketcap، هر ۵ دقیقه)
│ ├─ live_stream.py # کامودیتی لحظهای (Yahoo Finance) روی WebSocket
│ ├─ reports.py # اسکنِ آرشیو PDFِ گزارشها (فایلسیستم)
│ └─ sync.py # اسکریپت دستیِ سینکِ قیمت فلزات از API منبعِ بالادست
│
├─ admin/admin_app.py # پنل ادمین (مستقل)
├─ vite.config.ts # پورت ۵۱۷۴ + proxy (/api,/ws → :8000) + allowedHosts
├─ .gitignore # شاملِ .env، __pycache__، *.db
└─ README.md # همین فایل
۳) راهاندازی محلی
پیشنیازها: Node 20+ (یا Bun)، Python 3.11+، و برای اسکرپرها playwright install chromium.
# نصب وابستگیهای فرانت
npm install
# نصب وابستگیهای پایتون (نمونه)
pip install fastapi uvicorn playwright requests yfinance pandas websockets
python -m playwright install chromium
اجرای هر سه سرویس (هرکدام در ترمینال جدا)
# ۱) بکاند (پورت 8000)
$env:PYTHONUTF8="1"; python -m uvicorn main:app --host 127.0.0.1 --port 8000 --app-dir backend
# ۲) فرانتاند (پورت 5174)
npm run dev
# ۳) پنل ادمین (پورت 8001) — ADMIN_PASSWORD الزامی است
$env:ADMIN_USER="admin"; $env:ADMIN_PASSWORD="یکرمزِقوی"; python -m uvicorn admin_app:app --host 127.0.0.1 --port 8001 --app-dir admin
سپس: فرانت روی http://localhost:5174 ، پنل ادمین روی http://localhost:8001.
۴) متغیرهای محیطی
| متغیر | کجا | پیشفرض | توضیح |
|---|---|---|---|
DB_PATH |
backend, admin | مسیر محلی | محل فایل SQLite (هر دو سرویس باید یکی باشند) |
ASIANMETAL_TOKEN |
sync.py | — | توکنِ API بالادست (یا از پنل ادمین در DB) — هیچوقت در کد هاردکد نکن |
ADMIN_USER / ADMIN_PASSWORD |
admin | admin / الزامی |
احراز Basic پنل ادمین؛ بدون ADMIN_PASSWORD بالا نمیآید (fail-closed) |
ALLOWED_ORIGINS |
backend | localhost:5174,127.0.0.1:5174 |
originهای مجاز CORS (با کاما) |
COOKIE_SECURE |
backend | 0 |
در پروداکشن (HTTPS) روی 1 بگذار تا کوکیِ نشست Secure شود |
REPORTS_DIR |
reports.py | پوشهٔ آرشیو | محل PDFهای گزارش (ساختار <منبع> - <دسته>/<file>.pdf) |
۵) دیتابیس (SQLite)
init_db() در db.py جداول را میسازد و مهاجرتِ امن انجام میدهد (ستونها را اگر نبودند ALTER میکند):
prices— تاریخچهٔ روزانهٔ قیمت فلزات:grp, category, title, date, low, mid, high(یکتا رویtitle,date). منبعِ تب «فلزات و فولاد».users— کاربران:id, name, email, phone, organization, password_hash, status('pending'|'approved'|'rejected'), created_at.sessions— نشستهای سمتسرور:token, user_id, created_at, expires_at.app_config— جفتهایkey/value(مثلاًasianmetal_token، هارتبیت اسکرپرها).- جداول اسکرپرها:
currency_prices, gold_prices, coin_prices,te_prices,world_economy,steel_stocks— هرکدام آخرین/تاریخچهٔ مقادیر را نگه میدارند (dedup روی تغییر).
نکته: قیمتهای لحظهای (ارز/کریپتو/کامودیتی) عمدتاً در حافظه کش میشوند (
_latest) و فقط تغییرها در DB ذخیره میشوند تا حجم کم بماند.
۶) اسکرپرها (در lifespan بکاند خودکار استارت میشوند)
| ماژول | منبع | بازه | جدول/خروجی |
|---|---|---|---|
currency_stream |
نرخ داخلیِ ارز/طلا/سکه (URL در همان فایل) | ۶۰ ثانیه | currency_prices/gold_prices/coin_prices |
crypto_market |
CoinGecko (API رایگان، بدون کلید) | ۶۰ ثانیه | حافظه (get_latest) |
world_economy |
شاخصهای کلانِ اقتصادهای بزرگ | هفتگی | world_economy |
steel_stocks |
companiesmarketcap.com | ۵ دقیقه | steel_stocks |
live_stream |
Yahoo Finance | لحظهای | WebSocket /ws/live |
reports |
فایلسیستمِ REPORTS_DIR |
کشِ ۱۰ دقیقه | اسکنِ PDF |
دامِ مهم: سایتهای منبع قیمتها را با JS لِیزیلود میکنند. اسکرپرهای Playwright بعد از goto یک wait_for_timeout(5000–9000) دارند؛ حذفش نکن وگرنه مقادیرِ کهنه/خالی خوانده میشود. هر اسکرپر هم try/except دارد تا یک خطای گذرا کلِ تسک پسزمینه را نکُشد.
sync.py یک اسکریپتِ دستی است (در lifespan نیست) که قیمت فلزات را از API بالادست میگیرد؛ توکن را از app_config یا env ASIANMETAL_TOKEN میخواند.
۷) اندپوینتهای API (بکاند :8000)
عمومی (دادهٔ بازار): GET /api/tree, /api/history?title=&days=, /api/search?q=, /api/latest, /api/live, /api/currency, /api/gold, /api/coin, /api/world-economy, /api/steel-stocks, /api/crypto, /api/reports, /api/reports/files?folder=, /api/reports/file?folder=&name= (استریمِ inline PDF با گاردِ traversal)، WebSocket /ws/live.
احراز هویت: POST /api/register، POST /api/login، GET /api/me، POST /api/logout.
پنل ادمین :8001 (پشت HTTP Basic): /api/users، POST /api/users/{id}/approve|reject، GET|POST /api/token، /api/health، /api/test-scrape، /api/reports، /api/export?dataset=.
۸) احراز هویت و گردشِ تأیید (مهم)
اصل: هیچ اعتمادی به کلاینت نیست. ورود کاملاً سمتسرور تأیید میشود؛ نشست در کوکیِ httpOnly است، نه
localStorage.
ثبتنام (نام، نامخانوادگی، سازمان، ایمیل، موبایل، رمز)
│ POST /api/register → رمز با PBKDF2 هش میشود، رکورد با status='pending'
▼
کاربر در وضعیت «در انتظار تأیید» ──► ادمین در پنل (تبِ کاربران) تأیید/رد میکند
│ (POST /api/users/{id}/approve | reject)
▼
ورود POST /api/login → اگر approved بود: کوکیِ نشست (httpOnly, SameSite=Lax) ست میشود
│ pending → 403 «در انتظار تأیید» ، rejected → 403 ، رمز غلط → 401
▼
داشبورد → گاردِ سمتسرور: fetch('/api/me')؛ اگر 401 شد → ریدایرکت به /login
خروج POST /api/logout → نشست از DB حذف + کوکی پاک میشود
نکات پیادهسازی:
- هشِ رمز:
auth.hash_passwordباhashlib.pbkdf2_hmac('sha256', ..., 200000)— بدونِ وابستگیِ خارجی. مقایسه باhmac.compare_digest. - توکنِ نشست:
secrets.token_urlsafe(32)، انقضای ۷ روزه، در جدولsessions. - ریتلیمیتِ per-IP (در حافظه) روی
register/login(۵ در دقیقه). - اعتبارسنجیِ Pydantic: فرمت/طولِ ایمیل و موبایل، حداقل ۸ کاراکترِ رمز.
- رد کردنِ کاربر، نشستهای فعالش را هم پاک میکند (دسترسی فوراً قطع).
۹) پنل ادمین (:8001)
- ورود با HTTP Basic؛
ADMIN_PASSWORDالزامی است (بدونش بالا نمیآید). - تبها: کاربران (لیست + تأیید/رد + سازمان/وضعیت)، توکن منبع داده (ذخیره/تستِ توکن بالادست)، سلامت سرویسها (هارتبیتِ هر اسکرپر + تست اسکرپ)، گزارشات (دیتاستها + خروجی CSV).
- خروجیِ کاربران در UI با
esc()فرار داده میشود (جلوگیری از XSSِ ذخیرهشده).
۱۰) فرانتاند
- مسیرها:
/(لندینگ)،/login،/register،/dashboard. - داشبورد، تبها (
VIEW_NAV): بازار داخلی، کامودیتی، فلزات و فولاد (کاتالوگِ کارت + چارت)، اقتصاد جهانی (جدول حرارتی)، سهام فولادی، کریپتو، گزارشها. - زبان بصری: تم لایتِ کرم + لهجهٔ کهربایی (فولاد مذاب)؛ توکنها در
styles.css(oklch). قیمتکارتها سبکِ Spark-canvas (نمودار پسزمینه + رقمهای غلتانِOdometer+ پیلِ درصد). - API نسبی است (
const API = "") تا از طریق پراکسیِ Vite هممبدأ کار کند → برای share فقط یک تونل لازم است. - RTL سراسری، فونت Vazirmatn. اعداد همه با همین فونت (نه مونو).
۱۱) اشتراکگذاری (ngrok)
چون vite.config.ts مسیرهای /api و /ws را به :8000 پراکسی میکند و فرانت از URLهای نسبی استفاده میکند، فقط یک تونل روی ۵۱۷۴ کافی است:
ngrok http 5174
# یا مستقیم باینری: & "$env:APPDATA\npm\node_modules\ngrok\bin\ngrok.exe" http 5174
نکته: شیمِ ngrok.ps1ی npm گاهی پورت را اشتباه پاس میدهد؛ اگر تونل به پورت غلط رفت، مستقیم ngrok.exe را صدا بزن. URL را از http://127.0.0.1:4040/api/tunnels هم میتوانی بخوانی. (پلنِ رایگان: هر بار URL عوض میشود و یک صفحهٔ هشدارِ اولیه دارد.)
۱۲) امنیت — وضعیت و کارهای واجب
سختسازیهای انجامشده: CORS محدود، هدرهای امنیتی (nosniff, X-Frame-Options, Referrer-Policy)، اعتبارسنجی + ریتلیمیتِ ورودیها، محدودیتِ کوئریِ history/search، گاردِ path-traversal روی استریمِ PDF، کوئریهای پارامتری، احراز هویتِ سمتسرور با کوکیِ httpOnly، و حذفِ توکنِ هاردکد از کد.
واجب قبل از پروداکشن:
- توکنِ بالادست (SteelStatista) که قبلاً در git history لو رفته را باطل/تمدید کن؛ مقدارِ جدید فقط در پنل ادمین/
env. ADMIN_PASSWORDقوی بگذار (پیشفرضِ ضعیف حذف شده).- در HTTPS واقعی:
COOKIE_SECURE=1وALLOWED_ORIGINSرا به دامنهٔ واقعی محدود کن. - پنل ادمین (
:8001) را عمومی نکن (در تونل نگذار؛ فقط لوکال/VPN/SSH).
۱۳) دامها و بدهیِ فنی (برای دفعهٔ بعد)
DB_PATH/مسیرها درdb.pyوadmin_app.pyپیشفرضِ ویندوزیِ مطلق دارند (شاملِ نام کاربری) — بهتره به مسیر نسبی/envمنتقل شود.- وابستگیهای
package.jsonبا^پین نشدهاند؛ برای بیلدِ تکرارپذیرnpm ci+ پین. - فایلهای
__pycache__/*.pycقبلاً کامیت شدهاند؛ untrack کن:git rm -r --cached backend/__pycache__. routeTree.gen.tsخودکار است — دستی ویرایش نکن (با اضافهشدن فایل route، خودِ Vite بازتولید میکند).- اسکرپرها به ساختار HTMLِ منابع وابستهاند؛ اگر منبع قالبش را عوض کند، سلکتورها/
wait_for_timeoutباید تنظیم شوند.