# 云服务器部署说明

本文说明在 **云服务器（VPS）** 上部署 `crypto_monitor` 中控与三实例的推荐配置：硬件、软件、防火墙、宝塔反代、环境变量、PM2 启动与验收。

云上标准做法：**域名 + 宝塔/Nginx 反代 + HTTPS**；业务端口（5100、5000～5004、15200～15202）**不对公网直连**。

相关文档：

- **[本地数据迁移到云端.md](./本地数据迁移到云端.md)** — 备份 `crypto.db`、图片、`hub_settings` 与恢复步骤
- [局域网与反代部署说明.md](./局域网与反代部署说明.md) — 局域网 IP:端口 与反代域名对照、SSO 行为
- [部署文档.md](./部署文档.md) — PM2、依赖安装、日常运维
- [使用说明.md](./使用说明.md) — 中控功能说明
- [常见问题.md](./常见问题.md) — 故障排查
- 环境变量模板：[.env.example](./.env.example)

---

## 一、服务器硬件与系统

| 项目 | 建议 |
|------|------|
| 配置 | **2 核 4G** 起步；三实例 + 中控 + PM2 同时运行，**4G～8G 更稳** |
| 系统 | **Ubuntu 22.04 / 24.04**（项目文档按 Linux 编写） |
| 磁盘 | **20G+**；日志、SQLite、上传图片会占空间 |
| 网络 | 需能访问各交易所 API；若走代理，在对应 `crypto_monitor_*/.env` 配置 `OKX_SOCKS_PROXY`、`BINANCE_SOCKS_PROXY` 等 |

---

## 二、软件环境

```bash
sudo apt update
sudo apt install -y python3 python3-venv python3-pip git curl

# 进程守护（推荐）
sudo npm i -g pm2
```

**宝塔面板（可选但推荐）**：安装 **Nginx**，用于反向代理与 **SSL**（Let’s Encrypt）。

Python 虚拟环境（分开安装，互不替代）：

| 目录 | 用途 |
|------|------|
| `manual_trading_hub/.venv` | 中控 `hub.py` + 子代理 `agent.py` |
| `crypto_monitor_binance/.venv` | 币安 Flask |
| `crypto_monitor_okx/.venv` | OKX Flask |
| `crypto_monitor_gate/.venv` | Gate Flask |
| `crypto_monitor_gate/.venv` | Gate Flask |

各实例 `ecosystem.config.cjs` 一般已设置 **`PYTHONPATH=..`**（仓库根），以便加载 `hub_bridge.py`、`hub_auth.py` 等。

---

## 三、网络与端口（云上最重要）

**原则：公网只暴露 Nginx 的 80/443；Flask 与 agent 只监听本机。**

| 服务 | 本机端口（示例） | 是否对公网开放 |
|------|------------------|----------------|
| 中控 hub | 5100 | **否** → 仅 `https://hub.你的域名` 反代 |
| 币安 Flask | 5001 | **否** → `https://binance.你的域名` |
| OKX Flask | 5004 | **否** → `https://okx.你的域名` |
| Gate Flask | 5000 | **否** → `https://gate.你的域名` |
| 子代理 agent | 15200～15202 | **否**，必须 **127.0.0.1** |

### 云厂商安全组 / 系统防火墙

- **放行**：`80`、`443`（给宝塔/Nginx）
- **不要放行**：`5100`、`5000`～`5004`、`15200`～`15202`（除非临时本机调试，用完即关）

---

## 四、域名与宝塔反代

为 **中控 + 每个要对外打开的实例** 各建一个站点（子域名示例）：

| 站点（浏览器访问） | 反代目标 |
|--------------------|----------|
| `https://hub.example.com` | `http://127.0.0.1:5100` |
| `https://okx.example.com` | `http://127.0.0.1:5004` |
| `https://binance.example.com` | `http://127.0.0.1:5001` |
| `https://gate.example.com` | `http://127.0.0.1:5000` |

### 宝塔操作要点

1. 每个域名 → **网站** → **反向代理** → 目标 `http://127.0.0.1:对应端口`。
2. 申请 **SSL**（Let’s Encrypt），强制 HTTPS。
3. **不要**再给实例站加一层宝塔「访问密码」（会与 Flask `/login` 重复）；直链鉴权用下文 **`APP_USERNAME` / `APP_PASSWORD`**。
4. Nginx 建议保留常见代理头（宝塔默认通常已带）：

```nginx
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
```

中控请求实例 `/api/hub/*` 时会带 **`X-Hub-Token`**，一般无需额外配置。

---

## 五、环境变量（必配）

### 5.1 中控 `manual_trading_hub/.env`

```env
HUB_HOST=0.0.0.0
HUB_PORT=5100

# 与三实例 .env 完全相同（API + SSO 签名）
HUB_BRIDGE_TOKEN=请填一长串随机字符

# 中控网页登录（公网务必设置）
HUB_USERNAME=admin
HUB_PASSWORD=强密码
HUB_SESSION_SECRET=另一串随机字符

# 中控为 HTTPS 时建议 true
HUB_COOKIE_SECURE=true

# 公网用域名访问中控（宝塔反代）时必设其一：
#   HUB_ALLOW_PUBLIC=true   （推荐：反代 + 中控密码）
# 或反代目标必须是 http://127.0.0.1:5100 且可保持 HUB_TRUST_LAN=false
HUB_ALLOW_PUBLIC=true
HUB_TRUST_LAN=false

# 从中控打开实例的 SSO 链接有效期（秒），默认 7200 = 2 小时
HUB_SSO_TTL_SEC=7200

# 各实例 hub_settings 里 flask_url 已写 https 域名时，一般可不设
# HUB_PUBLIC_ORIGIN=https://hub.example.com
```

完整项见 [`.env.example`](./.env.example)。

### 5.2 三个实例 `crypto_monitor_*/.env`

每个目录都要有（**直链** `https://okx.域名` 时用这套登录网页）：

```env
# 各所 API 密钥（按交易所填写）
# APP_PORT=5004

# 与中控 manual_trading_hub/.env 中 HUB_BRIDGE_TOKEN 完全一致
HUB_BRIDGE_TOKEN=与中控相同

# 三实例建议统一（直链登录用）
APP_USERNAME=统一用户名
APP_PASSWORD=统一强密码

# 云服务器切勿开启（会跳过网页登录）：
# APP_AUTH_DISABLED=true
```

### 5.3 子代理

- `CONTROL_TOKEN` 可与 `HUB_BRIDGE_TOKEN` 相同。
- 由 PM2 在对应 `crypto_monitor_*` 目录启动，`run_agent.sh` 加载该目录 `.env`。
- 只监听 **127.0.0.1:1520x**，不映射到公网。

---

## 六、中控「系统设置」`hub_settings.json`

在网页 **系统设置** 保存，或编辑 `manual_trading_hub/hub_settings.json`。

云上 **`flask_url` 必须写浏览器能打开的 HTTPS 域名**（不要写 `127.0.0.1`，除非配合 `HUB_PUBLIC_ORIGIN` 做替换）：

| 字段 | 云上填法 | 说明 |
|------|----------|------|
| `flask_url` | `https://okx.example.com` | 用户浏览器、SSO 打开实例 |
| `agent_url` | `http://127.0.0.1:15201` | 仅中控本机访问子代理 |
| `enabled` | 按需 | 不参与监控的户可关 |
| `capabilities` | 按需 | `key` / `trend` 等 |

**同机部署的两种写法（二选一）：**

1. **推荐**：每个实例 `flask_url` 直接写该实例的 `https://子域名`。
2. **备选**：`flask_url` 写 `http://127.0.0.1:5004`，中控 `.env` 设 `HUB_PUBLIC_ORIGIN=https://okx.example.com`（适合共用一个 IP、靠端口区分时）。

`agent_url` 始终用 **`http://127.0.0.1:1520x`**。

---

## 七、PM2 启动顺序

代码路径示例：`/opt/crypto_monitor/`（按实际替换）。

```bash
cd /opt/crypto_monitor

# 1）三个实例 Flask（各目录 ecosystem.config.cjs，进程名以你机器为准）
cd crypto_monitor_okx && pm2 start ecosystem.config.cjs
cd ../crypto_monitor_binance && pm2 start ecosystem.config.cjs
cd ../crypto_monitor_gate && pm2 start ecosystem.config.cjs

# 2）中控 + 三个子代理（一条拉起 4 个进程：hub + 3 agent）
cd ../manual_trading_hub
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
cp .env.example .env   # 编辑填入真实值
chmod +x scripts/run_hub.sh scripts/run_agent.sh
pm2 start ecosystem.config.cjs
pm2 save
pm2 startup   # 按提示执行 sudo 命令后再 pm2 save
```

或：

```bash
cd /opt/crypto_monitor/manual_trading_hub
bash scripts/pm2_hub.sh start
```

### PM2 进程一览

| 进程名 | 说明 |
|--------|------|
| `manual-trading-hub` | 中控 :5100 |
| `manual-agent-binance` | :15200 |
| `manual-agent-okx` | :15201 |
| `manual-agent-gate` | :15202 |
| `crypto_*`（各目录自定） | 各 Flask `APP_PORT` |

不用 OKX 时可在 `.env` 设 `HUB_DISABLED_IDS=1`，或 `pm2 stop manual-agent-okx`。

---

## 八、访问与登录（云上行为）

| 访问方式 | 地址示例 | 需要什么 |
|----------|----------|----------|
| 中控监控 | `https://hub.example.com/monitor` | **中控** `HUB_USERNAME` / `HUB_PASSWORD` |
| 中控点「实例 / 策略交易 / 复盘」 | 自动打开 `https://okx.example.com/hub-sso?...` | 已登中控即可；**2 小时内、单次** SSO，**免输**实例密码 |
| 浏览器直链实例 | `https://okx.example.com` | 实例 **`APP_USERNAME` / `APP_PASSWORD`**（`/login`） |

SSO 复用 **`HUB_BRIDGE_TOKEN`** 签名，详见 [局域网与反代部署说明.md §五](./局域网与反代部署说明.md)。

---

## 九、安全建议（云服务器必看）

1. **SSH**：密钥登录，关闭密码登录；必要时改 SSH 端口。
2. **`HUB_BRIDGE_TOKEN`**：足够长、随机；勿提交 Git、勿写进前端页面。
3. **交易所 API Key**：仅放在各实例 `.env`；权限尽量最小化（勿随意开提币）。
4. **中控**：公网必须设 `HUB_PASSWORD`；`HUB_TRUST_LAN=false`。
5. **实例**：云上 **`APP_AUTH_DISABLED` 必须为 false**（或未设置）。
6. **备份**：定期备份各实例数据库 / SQLite 与 `hub_settings.json`。
7. **`.env` 换行**：Linux 上勿用 Windows CRLF；可用 `bash scripts/fix_env_crlf.sh`。

---

## 十、部署后验收清单

- [ ] `https://hub.你的域名` 能打开并登录中控
- [ ] 监控卡片有持仓/余额（子代理在线）
- [ ] 已登录中控 → 点「实例」→ **无**实例登录页，直接进入
- [ ] 隐身窗口直开 `https://okx.你的域名` → 出现 **`/login`**，统一账号密码可进
- [ ] `pm2 status`：hub、4×agent、用到的 `crypto_*` 均为 online
- [ ] 云安全组 **未** 对公网开放 5100、5000～5004、15200～15202
- [ ] 三实例 `.env` 与中控 `HUB_BRIDGE_TOKEN` 一致
- [ ] 实例启动日志无长期 `[hub_bridge] ImportError`

---

## 十一、常见问题速查

| 现象 | 处理 |
|------|------|
| 从中控打开仍要实例密码 | 见 [常见问题.md §4.3](./常见问题.md)；检查 token、重启 Flask、`hub_settings` 的 `key` |
| 监控无持仓 / 子代理不可用 | `curl http://127.0.0.1:15201/status`；查 `.env` CRLF、API 密钥 |
| 复盘/实例链接是 127.0.0.1 | `flask_url` 改为 https 域名，或设 `HUB_PUBLIC_ORIGIN` |
| 仅 Gate 子代理反复重启 | `.env` CRLF：`bash manual_trading_hub/scripts/fix_env_crlf.sh` |

---

## 十二、与局域网部署的区别（简要）

| 项目 | 云服务器 | 局域网 |
|------|----------|--------|
| 对外地址 | `https://子域名` | `http://内网IP:端口` |
| `flask_url` | 写 **域名** | 写 **内网 IP:端口** |
| 防火墙 | 只开 80/443 | 内网可开 5100、500x |
| SSL | 必须（宝塔证书） | 通常 HTTP 即可 |
| `HUB_COOKIE_SECURE` | 建议 `true` | HTTP 时用 `false` |

局域网详细步骤见 [局域网与反代部署说明.md §三](./局域网与反代部署说明.md)。
