Files
Shahoo/docs/02-rango-websocket-plan.md
2026-08-13 20:26:04 +03:30

367 lines
13 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<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>