Initial commit

This commit is contained in:
Yaser
2026-08-13 20:26:04 +03:30
commit 056fa76514
1923 changed files with 113766 additions and 0 deletions

View File

@@ -0,0 +1,366 @@
<div dir="rtl">
# Rango WebSocket — جایگزینی Polling در Shahoo
**تاریخ:** 2026-07-19
**آخرین به‌روزرسانی پیاده‌سازی:** 2026-07-20
**وابستگی:** [01-dynamic-markets-remediation.md](./01-dynamic-markets-remediation.md) (فاز ۳+ توصیه می‌شود)
**مرجع Gereh:** `Gereh/src/modules/public/ranger/`
**Infra:** Rango 2.6.1 — `quay.io/openware/rango:2.6.1`
> **وضعیت کد (۲۰۲۶-۰۷-۲۰):** WS-0 → WS-4 پیاده‌سازی شده؛ follow-up: thrashing/refcount، sequence resync، private reconnect، compose shahoo-ranger، setupProxy.
> باقی ops: WS0.3/0.4 staging + Cloudflare، WS5.1/5.3 latency/load.
> Public: `RangerProvider` + `global.tickers` / `ob-inc` / `trades` / `kline-*`.
> Private: BFF `GET /api/1/ranger/credentials` + WS proxy `/api/1/ranger/ws` → Rango private با cookie session.
> Fallback: `REACT_APP_RANGER_ENABLED=false` یا قطع WS → polling قبلی.
---
## ۱. خلاصه اجرایی
Shahoo امروز **HTTP polling** دارد؛ Gereh از **Rango (Ranger) WebSocket** استفاده می‌کند. هدف: latency کمتر در order book، tickers، trades و (در فاز بعد) balances/orders.
| معیار | Polling (فعلی) | Rango WS (هدف) |
|-------|----------------|----------------|
| Order book | هر **۵s** | push فوری (`ob-inc` / `update`) |
| Tickers (stats) | هر **۵s** | `global.tickers` |
| Recent trades | هر **۱۰s** | `{market}.trades` |
| Chart realtime | stub (CryptoCompare comment) | `{market}.kline-{period}` |
| بار سرور | N × clients × freq | یک WS per client |
| Auth private | — | `order`, `trade`, `balances` |
**توصیه:** بعد از **MarketsContext داینامیک** (doc 01 فاز ۳) شروع شود — subscribe به streamها به `marketId` Peatio (مثلاً `btcirt`) وابسته است.
---
## ۲. وضعیت فعلی Shahoo (Polling Map)
| فایل | interval | API | جایگزین Rango |
|------|----------|-----|----------------|
| `src/features/User/context/index.js` | 5s | `GET /api/1/ohlcvs/stats` | `global.tickers` |
| `OrderBook/RecentTransactionsList/index.js` | 5s | `GET /api/1/orders/orderBook/...` | `{id}.ob-inc` یا `{id}.update` |
| `hook/useGetOdrerBook/useGetOdrerBook.js` | 8s | order book | همان |
| `MarketTable/RecentMarketTrades/index.js` | 10s | trades | `{id}.trades` |
| `MarketRecentTransactions/index.js` | 10s | trades | `{id}.trades` |
| `TVChartContainer/datafeeds/streaming.js` | — | stub (غیرفعال) | `{id}.kline-{period}` |
**تنظیمات interval:** `src/core/utils/IntervalApiTime.js`
```js
export const orderBookTime = 5;
export const statsTime = 5;
export const recentMarketTime = 10;
```
---
## ۳. پروتکل Rango (OpenDAX)
### ۳.۱. URL اتصال
```
wss://{domain}/api/v2/ranger/public/?stream=global.tickers&stream=btcirt.trades&...
wss://{domain}/api/v2/ranger/private/?stream=order&stream=trade&...
```
الگوی Gereh (`helpers.ts`):
```ts
generateSocketURI(baseUrl, streams) =>
`${baseUrl}/?stream=${streams.sort().join('&stream=')}`
```
### ۳.۲. کانال‌های public
| Stream | payload | مصرف Shahoo |
|--------|---------|-------------|
| `global.tickers` | `{ btcusdt: { last, open, high, low, volume, ... } }` | `StatsContext` / sidebar قیمت |
| `{market}.trades` | `{ trades: [...] }` | Recent trades |
| `{market}.update` | full order book snapshot | Order book (legacy) |
| `{market}.ob-snap` | snapshot | incremental OB |
| `{market}.ob-inc` | incremental + sequence | Order book (preferred) |
| `{market}.kline-{period}` | OHLCV bar | TradingView streaming |
`period`: `1h`, `4h`, `1d`, … (مطابق Gereh `periodsMapString`)
### ۳.۳. کانال‌های private (JWT / session)
| Stream | مصرف |
|--------|------|
| `order` | open orders update |
| `trade` | history push |
| `balances` | wallet realtime (Finex) |
| `deposit_address` | آدرس واریز |
### ۳.۴. پیام subscribe/unsubscribe
```json
{ "event": "subscribe", "streams": ["btcirt.trades"] }
{ "event": "unsubscribe", "streams": ["btcirt.trades"] }
```
پاسخ: `{ "success": { "message": "subscribed", "streams": [...] } }`
---
## ۴. شکاف‌های اتصال Shahoo ↔ Rango
### G1 — URL runtime ندارد 🔴
Shahoo `public/config/env.js` فقط `app`, `captcha` دارد — **`rangerUrl` نیست**.
**راه‌حل:** اضافه به `window.env`:
```js
window.env.api = {
rangerUrl: 'wss://shahoo.fibitex.com/api/v2/ranger',
};
```
Traefik روی `shahoo.*` باید `/api/v2/ranger` → Rango route داشته باشد (مثل Gereh روی `www.*`).
### G2 — Auth model متفاوت 🔴
| Gereh | Shahoo |
|-------|--------|
| Cookie + Envoy JWT | BFF opaque token + Barong cookies در server-side |
| WS private با session مرورگر | مرورگر JWT Peatio ندارد |
**گزینه‌ها:**
| # | رویکرد | + | |
|---|--------|---|---|
| **A** | Public WS مستقیم از browser؛ private همچنان polling/BFF | سریع MVP | orders/balances realtime نمی‌آید |
| **B** | BFF: `GET /api/1/ranger/credentials` → Peatio JWT از gateway | private channels | endpoint + امنیت |
| **C** | BFF WebSocket proxy | مخفی JWT | پیچیدگی ops |
**توصیه:** **A برای MVP** (public market data) → **B برای فاز ۲** (private).
### G3 — نگاشت ticker 🔴
Rango: `{ "btcirt": { last, open, ... } }` (market id lowercase)
Shahoo: `statMap["BTC_IRT"]` (uppercase key)
**راه‌حل:** mapper مشترک در `src/lib/ranger/mapTickers.js` (یا reuse BFF `marketIdToShahooStatKeys` logic در frontend).
### G4 — Order book format 🟡
BFF فعلی: `{ sells: [{unitPrice, size}], buys: [...] }`
Rango `ob-inc`: `{ asks, bids, sequence }` — نیاز adapter مثل Gereh `depthDataIncrement`.
### G5 — Incremental sequence 🟡
Gereh: اگر `sequence` break شود → disconnect + reconnect + REST prefetch.
Shahoo باید همان pattern را پیاده کند.
### G6 — Cloudflare / Traefik 🟡
WebSocket روی staging/production:
- Cloudflare: WebSocket proxy **فعال**
- Traefik: `Upgrade` header pass-through
- تست: `wscat -c "wss://shahoo.fibitex.com/api/v2/ranger/public/?stream=global.tickers"`
---
## ۵. معماری هدف
```
┌─────────────────────────────────────────────────────────┐
│ Shahoo SPA │
│ RangerContext (singleton WS manager) │
│ ├─ public: global.tickers + market streams │
│ ├─ private: (فاز ۲) order, trade │
│ └─ mappers → StatsContext, OrderBook, Trades, Chart │
└───────────────────────┬─────────────────────────────────┘
│ wss://shahoo.fibitex.com/api/v2/ranger
Traefik → Rango :8080
RabbitMQ ← Dena pushers
```
**Fallback:** اگر WS قطع شد → polling قبلی (feature flag `REACT_APP_RANGER_ENABLED`).
---
## ۶. فازبندی اجرایی
### فاز WS-0 — زیرساخت (DevOps + config) 🔴
| ID | تسک | repo | وضعیت |
|----|------|------|--------|
| **WS0.1** | `rangerUrl` در `public/config/env.js` + Alvand-P template `shahoo.env.js.erb` | Shahoo + Alvand-P | ✅ |
| **WS0.2** | Traefik route `/api/v2/ranger` روی host `shahoo.*` → rango | Alvand-P | ✅ |
| **WS0.3** | تست connectivity: `global.tickers` از staging | QA | ⬜ |
| **WS0.4** | Cloudflare WebSocket برای `shahoo.fibitex.com` | DevOps | ⬜ |
---
### فاز WS-1 — Core client (Shahoo) 🔴
| ID | تسک | فایل(ها) | وضعیت |
|----|------|----------|--------|
| **WS1.1** | `src/lib/ranger/rangerClient.js` — connect, reconnect, heartbeat | جدید | ✅ |
| **WS1.2** | `generateSocketURI`, subscribe/unsubscribe | `rangerClient.js` | ✅ |
| **WS1.3** | `RangerContext` + `useRanger()` | `src/context/rangerContext.js` | ✅ |
| **WS1.4** | Feature flag: `REACT_APP_RANGER_ENABLED` + fallback polling | `.env`, hooks | ✅ |
| **WS1.5** | `mapRangerTickersToStatMap` | `src/lib/ranger/mapTickers.js` | ✅ |
**مرجع پیاده‌سازی:** `Gereh/src/modules/public/ranger/sagas/rangerSaga.ts` (بدون Redux — Context + useReducer).
---
### فاز WS-2 — جایگزینی polling (public) 🔴
| ID | تسک | جایگزین | polling حذف‌شده | وضعیت |
|----|------|---------|-----------------|--------|
| **WS2.1** | Tickers → `StatsContext` | `global.tickers` | `User/context` stats loop | ✅ |
| **WS2.2** | Order book | `{id}.ob-inc` + REST prefetch | `RecentTransactionsList`, `useGetOdrerBook` | ✅ |
| **WS2.3** | Recent trades | `{id}.trades` | `RecentMarketTrades`, `MarketRecentTransactions` | ✅ |
| **WS2.4** | Market switch → resubscribe | `rangerSubscribeMarket` pattern | — | ✅ |
| **WS2.5** | Landing tickers (public page) | `global.tickers` | `Landing/pages` getStats | ✅ |
**WS2.4:** هنگام تغییر route `/market/BTC-IRT` → unsubscribe `ethirt.*` → subscribe `btcirt.*`.
---
### فاز WS-3 — Chart streaming 🟡
| ID | تسک | فایل | وضعیت |
|----|------|------|--------|
| **WS3.1** | فعال‌سازی `streaming.js` با Rango kline | `datafeeds/streaming.js` | ✅ |
| **WS3.2** | subscribe `{marketId}.kline-4h` (match TV resolution) | `rangerClient` + datafeed | ✅ |
| **WS3.3** | unsubscribe on symbol change / unmount | datafeed | ✅ |
---
### فاز WS-4 — Private channels 🟡
| ID | تسک | repo | وضعیت |
|----|------|------|--------|
| **WS4.1** | BFF: `GET /api/1/ranger/credentials` (+ alias `/token`) + private WS proxy | Shahoo-BFF | ✅ |
| **WS4.2** | اتصال private بعد از login (`connectPrivate`) | Shahoo `rangerContext` | ✅ |
| **WS4.3** | `order` stream → refresh open orders | Market / Orders UI | ✅ |
| **WS4.4** | `trade` stream → history refresh | MarketYourTransaction | ✅ |
| **WS4.5** | `balances` → wallets refetch | Wallets context | ✅ |
---
### فاز WS-5 — QA + Performance 🟢
| ID | تسک | وضعیت |
|----|------|--------|
| **WS5.1** | Latency: order book update < 500ms vs polling 5s | ⬜ staging |
| **WS5.2** | Reconnect بعد از sleep/tab background | ✅ (client 1s reconnect) |
| **WS5.3** | 50+ concurrent WS روی staging (load) | ⬜ ops |
| **WS5.4** | Fallback polling وقتی WS down | ✅ |
| **WS5.5** | حذف intervalهای unused از `IntervalApiTime.js` | ⏸️ نگه داشته (fallback) |
---
## ۷. مسیر بحرانی و وابستگی
```
doc-01: T3.1 MarketsContext (marketId از catalog)
WS0.* (infra + rangerUrl)
WS1.* (rangerClient + Context)
WS2.1 (tickers) → WS2.2 (orderbook) → WS2.3 (trades)
WS3.* (chart) — موازی با WS2.3
WS4.* (private) — بعد از تصمیم auth
WS5.* (QA)
```
**موازی با doc-01:** WS0 می‌تواند همزمان با فاز ۱ doc-01 (نمودار/CAD) شروع شود.
**WS2+** بعد از `MarketsContext` — market id باید از catalog بیاید نه hardcode.
---
## ۸. تخمین زمان
| فاز | تخمین |
|-----|--------|
| WS-0 | ۰.۵۱ روز (DevOps) |
| WS-1 | ۱–۲ روز |
| WS-2 | ۲–۳ روز |
| WS-3 | ۱ روز |
| WS-4 | ۲–۳ روز |
| WS-5 | ۱ روز |
| **جمع MVP (WS-0→2)** | **~۵۷ روز** |
---
## ۹. تست
```bash
# connectivity (بعد از WS0)
npx wscat -c "wss://shahoo.fibitex.com/api/v2/ranger/public/?stream=global.tickers"
# browser DevTools → Network → WS → frames
# انتظار: JSON با کلید btcirt, btcusdt, ...
```
**چک UI:**
- [ ] قیمت sidebar بدون 5s delay به‌روز شود
- [ ] order book بعد از trade دیگران فوری update
- [ ] Network tab: polling `/ohlcvs/stats` متوقف (با flag روشن)
- [ ] قطع WS → fallback polling کار کند
---
## ۱۰. Todo checklist
```
فاز WS-0 — Infra
[x] WS0.1 rangerUrl در env.js + template
[x] WS0.2 Traefik /api/v2/ranger روی shahoo.*
[ ] WS0.3 تست global.tickers staging
[ ] WS0.4 Cloudflare WebSocket
فاز WS-1 — Core
[x] WS1.1 rangerClient.js
[x] WS1.2 subscribe/unsubscribe
[x] WS1.3 RangerContext + useRanger
[x] WS1.4 REACT_APP_RANGER_ENABLED + fallback
[x] WS1.5 mapRangerTickersToStatMap
فاز WS-2 — Public replace polling
[x] WS2.1 tickers → StatsContext
[x] WS2.2 order book ob-inc
[x] WS2.3 recent trades
[x] WS2.4 market switch resubscribe
[x] WS2.5 landing tickers
فاز WS-3 — Chart
[x] WS3.1 streaming.js + Rango kline
[x] WS3.2 resolution mapping
[x] WS3.3 cleanup on unmount
فاز WS-4 — Private
[x] WS4.1 BFF ranger credentials + private WS proxy
[x] WS4.2 private WS connect after login
[x] WS4.3 order stream → refresh
[x] WS4.4 trade stream → refresh
[x] WS4.5 balances → wallets refresh
فاز WS-5 — QA
[x] WS5.2 reconnect + WS5.4 fallback
[ ] WS5.1 latency / WS5.3 load / Cloudflare
```
---
## ۱۱. commit message convention
```
WS1.1: add rangerClient with reconnect
WS2.1: replace stats polling with global.tickers
WS2.2: incremental order book via ob-inc
```
</div>