Files
view/README.md
T
0xcathiefish 83551d9b4d Add build.sh for Cloudflare Pages env-based config injection
- Add build.sh that generates auth-config.js from environment variables
  (API_BASE_URL, GOOGLE_CLIENT_ID, ALLOWED_EMAILS) at build time
- Move email whitelist from hardcoded login.html to window.ALLOWED_EMAILS
  in auth-config.js, so it can be configured via env vars
- Reset auth-config.js to placeholder defaults in repo
- Update README.md and README_CN.md deployment docs for both options

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 15:45:33 +00:00

387 lines
17 KiB
Markdown
Raw 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.
# 📊 View — Crypto chart Platform
[🇨🇳 中文版 README](README_CN.md)
A self-hosted, real-time cryptocurrency data collection and visualization platform for **Binance USDS-M Futures**. Combines a high-performance Rust backend with a professional charting frontend to deliver institutional-grade market data tooling.
This project is primarily built for my own use, and I don't have much bandwidth to maintain it. I'm also not a frontend developer, so the frontend code may not follow best practices. That said, pull requests and issues are very welcome — feel free to open an Issue or submit a Pull Request!
As you can see, this is not a simple one-click-to-deploy project, nor is it a beginner-friendly toy. I simply don't have the time or ability to simplify it further. The easiest way to deploy is to let **Claude Opus 4.6** study the entire project and have it guide you through the setup.
If deploying the backend is too difficult, you can temporarily use my public backend API endpoint: `api-view.cathiefish.org`. This way you only need to deploy the frontend to Cloudflare Pages. Note that this endpoint only guarantees data for **BTCUSDT, ETHUSDT, SOLUSDT, BNBUSDT, XRPUSDT, SUIUSDT**. See the [Deployment](#-deployment) section for how to configure this.
![tv-1](https://img.cathiefish.art/ns/tv-1.png)
![tv-2](https://img.cathiefish.art/ns/tv-2.png)
![tv-3](https://img.cathiefish.art/ns/tv-3.png)
![tv-3](https://img.cathiefish.art/ns/tv-4.png)
## ✨ Features
### Data Collection & Storage
- [x] **Real-time WebSocket streaming** — Live 1-minute K-line data from Binance combined streams, with auto-reconnect and gap backfill
- [x] **3-tier historical sync** — Monthly ZIP → Daily ZIP → REST API, for fastest possible backfill from [Binance Data Archive](https://data.binance.vision/)
- [x] **TimescaleDB time-series storage** — Hypertable-optimized with `time_bucket` aggregation across 8 timeframes (1m, 5m, 15m, 1h, 4h, 1D, 1W, 1M)
- [x] **Proxy pool support** — Up to 100 concurrent proxy clients for high-throughput parallel downloads
- [ ] **Binance Spot support** — Spot market data collection, using `.P` suffix to distinguish Futures from Spot symbols
- [ ] **MCP support** — Model Context Protocol integration
### Charting & Visualization
- [x] **Professional charting engine** — Full indicator and drawing tool support
- [x] **Custom indicators** — Net Volume (NV-C) and Cumulative Volume Delta (CVD-C)
- [x] **Multi-chart layouts** — Single, vertical split, horizontal split, and 1L+2R layouts with draggable dividers
- [x] **Chart save/load** — Built-in save/load via `save_load_adapter` (metadata in localStorage, chart content in backend API), with auto-save
- [x] **Dark & Light themes** — Full theme toggle synced across widget and UI
### Watchlist & Interaction
- [x] **Live watchlist sidebar** — Real-time price, 24h change tracking, drag-and-drop reordering
- [x] **Multiple custom lists** — Create and manage separate watchlists
- [ ] **Frontend symbol management** — Add/remove tracked symbols from the frontend UI (currently `TRACKED_SYMBOL` is only controlled via the backend `.env` file)
### Security & Deployment
- [x] **Google OAuth protection** — Email whitelist access control with 144-hour session persistence
- [x] **Docker ready** — Multi-stage builds, Docker Compose with shared network
- [ ] **Google Firebase integration** — Connect backend API via Firebase for managed authentication and hosting
## 📁 Project Structure
```
view/
├── backend/ # Rust data engine & API server
│ ├── src/
│ │ ├── main.rs # Axum HTTP server bootstrap
│ │ ├── binance_collector.rs # WebSocket real-time collection + REST sync
│ │ ├── historical_downloader.rs # Binance data archive (ZIP) downloader
│ │ ├── database.rs # TimescaleDB operations & aggregation
│ │ ├── scheduler.rs # Task coordination & collector lifecycle
│ │ ├── klinechart.rs # KlineChart REST API handlers
│ │ ├── tradingview.rs # TradingView UDF API + WebSocket + Canvas
│ │ ├── structs.rs # Data types (CandleData, Interval, WsMessage…)
│ │ ├── error.rs # Custom error types
│ │ └── lib.rs # Public module exports
│ ├── tests/
│ │ ├── connection_test.rs # Database connection tests
│ │ ├── database_test.rs # CRUD & query tests
│ │ ├── scheduler_test.rs # Scheduler command & lifecycle tests
│ │ ├── sync_test.rs # Single symbol sync tests
│ │ └── sync_full_history_test.rs
│ ├── examples/
│ │ ├── sync_all.rs # Sync all symbols (standard)
│ │ ├── sync_all_fast.rs # Sync all symbols (parallel with proxy pool)
│ │ └── sql.txt # Reference SQL for TimescaleDB setup
│ ├── Dockerfile
│ └── docker-compose.yml
├── frontend/ # Charting Library frontend
│ ├── index.html # Main app (widget + watchlist + layouts)
│ ├── login.html # Google OAuth login page
│ ├── auth.js # AuthGuard — session management
│ ├── auth-config.js # OAuth & API configuration
│ ├── charting_library/ # Charting Library assets
│ ├── datafeeds/ # UDF datafeed adapter
│ ├── Dockerfile
│ └── docker-compose.yml
└── references/ # Git submodules
├── binance-rust/ # Binance connector SDK
└── library/ # Charting Library source
```
## 🛠️ Tech Stack
| Layer | Technology |
|-------|-----------|
| **Backend** | Rust, Axum, sqlx, tokio, tokio-tungstenite |
| **Database** | PostgreSQL + TimescaleDB |
| **Frontend** | Charting Library v29.4, Vanilla JS |
| **Auth** | Google Identity Services (OAuth) |
| **Deployment** | Docker, Nginx, Docker Compose |
## 🚀 Deployment
For first-time users of Nginx Proxy Manager, TimescaleDB, PgAdmin, Docker, or Cloudflare — the easiest way to deploy is to let **Claude Opus 4.6** study this project and guide you through each step. If you encounter persistent issues, please open an Issue on this repository.
### Option 1: Full Self-Hosted Deployment
Deploy the entire stack on your own server with Docker and Nginx Proxy Manager as the reverse proxy.
> [!NOTE]
> If you are unsure about any of the steps below, ask **Claude Opus 4.6** for guidance — it can walk you through each step in detail.
#### Network Architecture
```
┌─────────────────── Docker Network: cycle ───────────────────┐
│ │
│ ┌──────────────────┐ ┌──────────────┐ │
│ │ Nginx Proxy Mgr │ │ TimescaleDB │ │
│ │ :80 / :443 │ │ :5432 │ │
│ └────────┬─────────┘ └──────┬───────┘ │
│ │ │ │
│ │ │ │
│ ┌────────▼──┐ ┌───────────┐ │ │
│ │ Frontend │ │ Backend │───┘ │
│ │ :80 │ │ :3000 │ │
│ │ (nginx) │ │ (axum) │ │
│ └───────────┘ └───────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
Public domain
view.yourdomain.com
```
All containers **must** run on the same Docker network `cycle` so they can communicate by container name. Only the **frontend** needs to be exposed to the public via Nginx Proxy Manager — the backend (port `3000`) is accessed internally by the frontend container through Docker DNS.
---
#### Step 1: Create Docker Network
```bash
docker network create cycle
```
---
#### Step 2: Database Setup (TimescaleDB)
Create a `docker-compose.yml` for TimescaleDB on the `cycle` network:
```yaml
# database/docker-compose.yml
services:
timescaledb:
image: timescale/timescaledb:latest-pg16
container_name: timescaledb
networks:
- cycle
environment:
- POSTGRES_USER=quant
- POSTGRES_PASSWORD=your_secure_password
- POSTGRES_DB=crypto_database
volumes:
- timescaledb_data:/var/lib/postgresql/data
restart: unless-stopped
volumes:
timescaledb_data:
networks:
cycle:
external: true
```
```bash
docker compose up -d
```
Then initialize the schema using the SQL in [backend/examples/sql.txt](backend/examples/sql.txt):
```bash
docker exec -i timescaledb psql -U quant -d crypto_database < backend/examples/sql.txt
```
> [!TIP]
> For database management, consider deploying **PgAdmin** alongside TimescaleDB. See [this guide](https://n8n.cathiefish.art/n8n-instances-twitter-ai-%E5%88%86%E6%9E%90%E7%9B%91%E6%8E%A7-5b706bb444d7) for PgAdmin setup instructions. For beginners, ask **Claude Opus 4.6** to walk you through it.
#### Step 3: Deploy Backend
**1. Configure `.env`**
```bash
cd backend
cp .env.example .env
```
Edit `backend/.env`:
```env
RUST_LOG="INFO,binance_sdk::common::utils=off,binance_sdk::common::websocket=off"
DATABASE_URL="postgres://quant:your_secure_password@timescaledb:5432/crypto_database"
TRACKED_SYMBOL=[BTCUSDT,ETHUSDT,BNBUSDT,SOLUSDT,XRPUSDT]
# Proxy settings (optional — leave empty for direct connection)
PROXY_HOST=dc.your-proxy-provider.com
PROXY_USERNAME=your_username
PROXY_PASSWORD=your_password
PROXY_PROTOCOL=https/http/socks5
PROXY_PORT_START=10000
PROXY_PORT_END=10099
```
> [!NOTE]
> `DATABASE_URL` uses the container name `timescaledb` as the hostname — this works because both containers are on the `cycle` network. You can also use the database container's absolute IP (find it with `docker inspect timescaledb --format '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'`). Make sure the database container is also on the `cycle` network. `TRACKED_SYMBOL` currently only supports **USDS-M Futures (Swap)**, not Spot.
> [!WARNING]
> **Proxy is strongly recommended.** Historical data sync downloads from [Binance Data Archive](https://data.binance.vision/) for all tracked symbols. With a multi-port proxy pool (e.g. 100 concurrent connections), a full sync completes in hours. **Without a proxy, syncing may take several days.** If `PROXY_HOST` is left empty, the backend falls back to a single direct connection.
**2. Build & Run**
```bash
docker build -t backend .
docker compose up -d
```
---
#### Step 4: Deploy Frontend
Before building the frontend container, you need to configure authentication and API connection.
**1. Configure `auth-config.js`**
Since the frontend runs inside the Docker network, it accesses the backend via the **container name**:
```js
window.API_CONFIG = { baseUrl: 'http://backend:3000' };
const AUTH_CONFIG = {
clientId: 'YOUR_CLIENT_ID.apps.googleusercontent.com',
onSuccess: (user) => { console.log('Auth successful:', user.email); },
onError: (error) => { console.error('Auth error:', error); }
};
window.AUTH_CONFIG = AUTH_CONFIG;
window.ALLOWED_EMAILS = ['your-email@gmail.com'];
```
Replace `clientId` with your Google OAuth Client ID from GCP Console, and `ALLOWED_EMAILS` with your email whitelist.
To find the backend container IP (useful for debugging):
```bash
$ docker inspect backend --format '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'
172.18.0.3
```
> [!IMPORTANT]
> Google OAuth requires a Client ID from GCP Console. Tutorial placeholder — to be added.
**3. Build & Run**
```bash
cd frontend
docker build -t frontend .
docker compose up -d
```
> [!WARNING]
> The frontend and backend containers **must** be on the same Docker network (`cycle`). This is already configured in both `docker-compose.yml` files via `networks: cycle: external: true`.
---
#### Step 5: Nginx Proxy Manager
Nginx Proxy Manager must also run on the `cycle` network. Create a `docker-compose.yml`:
```yaml
# npm/docker-compose.yml
services:
nginx-proxy-manager:
image: jc21/nginx-proxy-manager:latest
container_name: nginx-proxy-manager
networks:
- cycle
ports:
- "80:80"
- "443:443"
- "81:81"
volumes:
- npm_data:/data
- npm_letsencrypt:/etc/letsencrypt
restart: unless-stopped
volumes:
npm_data:
npm_letsencrypt:
networks:
cycle:
external: true
```
```bash
docker compose up -d
```
> [!TIP]
> For detailed Nginx Proxy Manager setup and Docker deployment best practices, see [this guide](https://n8n.cathiefish.art/rsshub-install-f5ad036a4dd9). For beginners, ask **Claude Opus 4.6** to walk you through it.
Access the admin panel at `http://your-server-ip:81` (default: `admin@example.com` / `changeme`).
**Create a Proxy Host for the frontend:**
| Domain | Forward Hostname | Forward Port | SSL |
|--------|-----------------|--------------|-----|
| `view.yourdomain.com` | `frontend_tv` | `80` | ✅ Let's Encrypt |
> [!TIP]
> The "Forward Hostname" uses the **container name** (not IP), which works because all containers share the `cycle` network. The backend does **not** need a public proxy — the frontend accesses it internally via Docker DNS (`http://backend:3000`).
After setup, your platform will be accessible at `https://view.yourdomain.com`.
---
### Option 2: Backend Self-Hosted + Frontend on Cloudflare Pages
Build on top of **Option 1** — keep the same server-side setup (Docker network, TimescaleDB, backend), but additionally expose the backend API to the public internet and deploy the frontend to Cloudflare Pages instead of self-hosting it.
> [!NOTE]
> If you are unsure about any of these steps, ask **Claude Opus 4.6** — this is a straightforward process.
> [!CAUTION]
> **Do NOT** include "tradingview" in your Cloudflare Pages project name or custom domain. TradingView actively enforces their trademark and your deployment **will be taken down**. Use a neutral name like `view`, `chart`, or `crypto-dash`.
#### Step 1: Backend + Database
Complete **Option 1 Steps 13** (create `cycle` network, deploy TimescaleDB, deploy backend).
#### Step 2: Expose Backend API via Nginx Proxy Manager
Since the frontend will be served from Cloudflare (outside your Docker network), the backend API must be publicly accessible. Add a **second Proxy Host** in Nginx Proxy Manager:
| Domain | Forward Hostname | Forward Port | SSL |
|--------|-----------------|--------------|-----|
| `api.yourdomain.com` | `backend` | `3000` | ✅ Let's Encrypt |
> [!IMPORTANT]
> Enable **Websockets Support** for this proxy host — required for real-time candle streaming.
#### Step 3: Deploy to Cloudflare Pages
1. Fork or clone this repository to your own GitHub account
2. Go to [Cloudflare Dashboard](https://dash.cloudflare.com/) → **Workers & Pages****Create**
3. Select **Pages****Connect to Git**
4. Authorize Cloudflare to access your GitHub account and select the repository
5. Configure the build settings:
| Setting | Value |
|---------|-------|
| Production branch | `main` |
| Build command | `sh build.sh` |
| Build output directory | `frontend` |
6. Add **Environment Variables** (Settings → Environment Variables):
| Variable | Value | Description |
|----------|-------|-------------|
| `API_BASE_URL` | `https://api.yourdomain.com` | Public backend API URL from Step 2 |
| `GOOGLE_CLIENT_ID` | `YOUR_CLIENT_ID.apps.googleusercontent.com` | Google OAuth Client ID from GCP Console |
| `ALLOWED_EMAILS` | `alice@gmail.com,bob@gmail.com` | Comma-separated email whitelist |
7. Click **Save and Deploy**
> [!NOTE]
> The `build.sh` script generates `auth-config.js` from environment variables at build time. No sensitive values are stored in the repository — all credentials are configured via Cloudflare's environment variables dashboard.
Cloudflare will assign a `*.pages.dev` domain. You can add a custom domain in **Pages****Custom domains**.
---
## 📜 License
MIT
Generated By Claude Opus 4.6