up
This commit is contained in:
+270
-124
@@ -1,62 +1,166 @@
|
||||
# 04-部署运行文档
|
||||
|
||||
> 本文说明如何在服务器上把项目跑起来。项目共三端:**服务端**(FastAPI,无需编译)、**管理后台**(Vue3,构建为静态文件)、**App**(Flutter,**必须编译**出 APK/IPA)。
|
||||
> 本文记录**真实生产环境**的部署架构与运维操作手册(截至 2026-10-18)。
|
||||
> 服务器:腾讯云 CVM(14G 内存),已装宝塔面板;系统 Linux。
|
||||
> 项目根目录:`/root/project/video`,三个业务域名均启用 HTTPS。
|
||||
|
||||
## 1. 组成与依赖
|
||||
## 1. 总体架构
|
||||
|
||||
| 端 | 目录 | 技术栈 | 运行/构建产物 |
|
||||
|---|---|---|---|
|
||||
| 服务端 | `server/` | Python ≥ 3.11 + FastAPI + MySQL 8 + Redis 7 | 直接运行,无需编译 |
|
||||
| 管理后台 | `admin/` | Node ≥ 18 + Vue3 + Vite | `npm run build` → `admin/dist/` 静态文件 |
|
||||
| App | `app/` | Flutter 3.x(Dart ≥ 3.9) | `flutter build apk` / `flutter build ipa` |
|
||||
```
|
||||
┌─────────────────────────────────────────┐
|
||||
用户浏览器/App │ 腾讯云服务器 │
|
||||
│ │
|
||||
h5.256kb.top ───────► │ nginx(宝塔) ──► /root/project/video/h5 │
|
||||
(下载页/邀请落地页) │ ├─ /downloads /videos /uploads │
|
||||
│ │ → /root/project/video/runtime/* │
|
||||
│ └─ /r/ /api/v1/invite/ ──┐ │
|
||||
vm.256kb.top ───────► │ root:/root/project/video/admin/dist │
|
||||
(管理后台 SPA) │ /api/ /static/ /r/ ──────┤ │
|
||||
│ ▼ │
|
||||
vapi.256kb.top ──────► │ 全量反代 ──────────► uvicorn 127.0.0.1:8000
|
||||
(App API 入口) │ (video-api.service,│
|
||||
│ 2 workers) │
|
||||
│ │ │ │
|
||||
│ ┌───────────┘ └────┐ │
|
||||
│ ▼ ▼ │
|
||||
│ docker: mysql8 docker: my-redis
|
||||
│ 127.0.0.1:3307 127.0.0.1:6379
|
||||
│ 数据卷 /opt/mysql8-data 配置/数据 /home/redis
|
||||
└─────────────────────────────────────────┘
|
||||
|
||||
服务器需先装好:MySQL 8(utf8mb4)、Redis 7、Python 3.11+、Nginx(托管后台 + 反向代理)。
|
||||
视频 ts 分片流量:App → 直连源站 CDN(不经过本服务器,见 02-技术文档)
|
||||
```
|
||||
|
||||
## 2. 服务端部署(server/)
|
||||
| 域名 | 用途 | nginx 要点 |
|
||||
|---|---|---|
|
||||
| `h5.256kb.top` | App 下载页 + 邀请落地页 + 静态媒体 | root=`/root/project/video/h5`;`/downloads/` `/videos/` `/uploads/` alias 到 `runtime/`;`/r/`、`/api/v1/invite/` 反代 8000 |
|
||||
| `vm.256kb.top` | 管理后台 | root=`/root/project/video/admin/dist`(SPA 回退 index.html);`/api/` `/static/` `/r/` 反代 8000 |
|
||||
| `vapi.256kb.top` | App 业务 API | 全量反代 127.0.0.1:8000 |
|
||||
| `codeserver.256kb.top` | code-server(运维用,默认容器未自启) | 反代 code-server 容器 |
|
||||
|
||||
### 2.1 安装依赖
|
||||
证书:Let's Encrypt ECC,由 acme.sh/宝塔申请到 `/www/server/panel/vhost/cert/<域名>/fullchain.pem`,crontab 有续期任务(`5 4,10,16,22 * * * acme.sh --cron` + 宝塔 acme_v2.py),80 端口仅用于 acme 挑战与跳转 HTTPS。
|
||||
|
||||
## 2. 目录规划
|
||||
|
||||
| 路径 | 用途 |
|
||||
|---|---|
|
||||
| `/root/project/video/server` | FastAPI 后端源码 + `.venv` 虚拟环境 + `.env` + `config.yaml` |
|
||||
| `/root/project/video/admin` | Vue3 后台源码;构建产物 `admin/dist/` 即 vm 站点根目录 |
|
||||
| `/root/project/video/app` | Flutter App 源码(编译机即本服务器) |
|
||||
| `/root/project/video/h5` | H5 下载页(`index.html`) |
|
||||
| `/root/project/video/runtime` | 运行期产出:`downloads/`(APK)、`videos/`(转码视频)、`uploads/`(上传文件) |
|
||||
| `/root/project/video/docs` | 项目文档(本目录) |
|
||||
| `/root/project/video/deploy.sh` | App 一键发版脚本(见 §7) |
|
||||
| `/root/project/video/project-ctl.sh` | 项目一键启停脚本(见 §8) |
|
||||
| `/opt/mysql8-data` | MySQL 8 数据卷(Docker 挂载) |
|
||||
| `/home/redis/conf`、`/home/redis/data` | Redis 配置与数据(Docker 挂载) |
|
||||
| `/root/backups/video-app-signing` | App 签名 keystore 备份(**极其重要**,见 §7.3) |
|
||||
|
||||
## 3. Docker 组件(MySQL / Redis)
|
||||
|
||||
两个容器均为 `--restart unless-stopped`:服务器重启自动拉起;**手动 `docker stop` 后重启不会恢复**(project-ctl.sh stop 利用此语义实现"暂停")。
|
||||
|
||||
### 3.1 MySQL 8(容器名 mysql8)
|
||||
|
||||
- 镜像 `mysql:8`(当前 8.4.11),端口映射 **宿主机 3307 → 容器 3306**
|
||||
- 数据卷 `/opt/mysql8-data:/var/lib/mysql`,时区 Asia/Shanghai
|
||||
- 业务库 `video`,账号 `root` / 密码 `256kb.cn`
|
||||
- **连接必须加 `-h 127.0.0.1`**:不加时 mysql 客户端走 unix socket 会连到宿主机旧版 MySQL 5.7,曾导致 deploy.sh 把版本号写错库
|
||||
|
||||
```bash
|
||||
cd server
|
||||
python3.11 -m venv .venv
|
||||
source .venv/bin/activate
|
||||
pip install . # 核心依赖
|
||||
pip install ".[pay]" # 可选:支付渠道 SDK(支付宝/微信/Tron),不接支付可跳过
|
||||
# 日常连接
|
||||
mysql -h 127.0.0.1 -uroot -p256kb.cn -P 3307 video
|
||||
# 或容器内
|
||||
docker exec -it mysql8 mysql -uroot -p256kb.cn video
|
||||
|
||||
# 如需重建容器(数据在 /opt/mysql8-data,不丢)
|
||||
docker run -d --name mysql8 --restart unless-stopped \
|
||||
-p 3307:3306 -v /opt/mysql8-data:/var/lib/mysql \
|
||||
-e TZ=Asia/Shanghai -e MYSQL_ROOT_PASSWORD=256kb.cn mysql:8
|
||||
```
|
||||
|
||||
### 2.2 准备数据库与 .env
|
||||
### 3.2 Redis(容器名 my-redis)
|
||||
|
||||
```sql
|
||||
CREATE DATABASE video CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
- 镜像 `redis:latest`(7.x),端口 **6379**,挂载 `/home/redis/conf` 与 `/home/redis/data`
|
||||
- 启动命令 `redis-server /usr/local/etc/redis/redis.conf`
|
||||
- **注意:宿主机曾装过 Redis 5.0.3 并已禁用自启**。若它抢占了 6379,新版 redis-py 客户端默认 RESP3 的 `HELLO` 握手会失败 → 后端全接口 500。排查时先确认 `docker ps` 里 my-redis 在跑、且 6379 被 docker-proxy 占用
|
||||
- 后端 redis-py 客户端显式指定 `protocol=2`(RESP2),新旧服务端都兼容
|
||||
- 清业务缓存一律容器内执行:`docker exec my-redis redis-cli DEL config:system`
|
||||
|
||||
```bash
|
||||
# 如需重建容器
|
||||
docker run -d --name my-redis --restart unless-stopped \
|
||||
-p 6379:6379 -v /home/redis/conf:/usr/local/etc/redis -v /home/redis/data:/data \
|
||||
redis:latest redis-server /usr/local/etc/redis/redis.conf
|
||||
```
|
||||
|
||||
在 `server/.env` 配置环境项(不入库的连接串/密钥):
|
||||
## 4. 后端服务(video-api.service)
|
||||
|
||||
### 4.1 systemd 单元(/etc/systemd/system/video-api.service)
|
||||
|
||||
```ini
|
||||
APP_ENV=prod # prod 时 /docs 自动关闭
|
||||
APP_HOST=0.0.0.0
|
||||
APP_PORT=8000
|
||||
DATABASE_URL=mysql+aiomysql://user:pass@127.0.0.1:3306/video?charset=utf8mb4
|
||||
REDIS_URL=redis://127.0.0.1:6379/0
|
||||
JWT_SECRET=换成足够长的随机串 # 生产必须更换
|
||||
[Unit]
|
||||
Description=Video App FastAPI Backend (uvicorn)
|
||||
After=network.target docker.service redis.service
|
||||
Requires=docker.service
|
||||
|
||||
# 以下按需配置(不配置则对应功能自动降级:短信/邮件发不出去、支付走 dev_mock、USDT 不启用)
|
||||
# ALIPAY_APP_ID / ALIPAY_PRIVATE_KEY / ALIPAY_PUBLIC_KEY / ALIPAY_NOTIFY_URL
|
||||
# WECHAT_MCHID / WECHAT_APP_ID / WECHAT_API_V3_KEY / WECHAT_PRIVATE_KEY / WECHAT_CERT_SERIAL_NO / WECHAT_PLATFORM_CERT / WECHAT_NOTIFY_URL
|
||||
# TRON_API_KEY(USDT 链上查账)
|
||||
# SMS_ACCESS_KEY_ID / SMS_ACCESS_KEY_SECRET / SMS_SIGN_NAME(短信验证码)
|
||||
# SMTP_HOST / SMTP_PORT / SMTP_USER / SMTP_PASSWORD(邮箱验证码)
|
||||
[Service]
|
||||
Type=simple
|
||||
User=root
|
||||
WorkingDirectory=/root/project/video/server
|
||||
ExecStart=/root/project/video/server/.venv/bin/uvicorn app.main:app --host 127.0.0.1 --port 8000 --workers 2
|
||||
Restart=always
|
||||
RestartSec=3
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
业务参数(积分/会员/激励/提现规则)在 `server/config.yaml`,其中部分项上线后可在管理后台「系统配置」热修改。
|
||||
- 只监听 **127.0.0.1:8000**,对外全靠 nginx 反代
|
||||
- `--workers 2`:⚠️ 结算周期任务(推荐激活/创作收益/超时关单)随每个 worker 的 lifespan 各启动一份(`app/services/settle_service.py`)。当前任务均为条件更新、重复执行无副作用;**修改周期任务逻辑时必须保证幂等**
|
||||
- 日志走 journald:`journalctl -u video-api -f`
|
||||
|
||||
### 2.3 初始化表结构 + 首个管理员
|
||||
常用命令:
|
||||
|
||||
```bash
|
||||
cd server
|
||||
.venv/bin/alembic upgrade head # 建 27 张表
|
||||
systemctl restart video-api # 改后端代码后重启
|
||||
systemctl enable --now video-api # 启动并恢复开机自启
|
||||
journalctl -u video-api -n 100 --no-pager
|
||||
```
|
||||
|
||||
# 创建后台管理员(代码不自动播种,需手动执行一次)
|
||||
### 4.2 环境配置(server/.env)
|
||||
|
||||
键清单(敏感值不入文档;`.env` 本身不入 git):
|
||||
|
||||
```
|
||||
APP_ENV=prod APP_HOST APP_PORT
|
||||
DATABASE_URL=mysql+aiomysql://root:256kb.cn@127.0.0.1:3307/video?charset=utf8mb4
|
||||
REDIS_URL=redis://127.0.0.1:6379/0
|
||||
CELERY_BROKER_URL CELERY_RESULT_BACKEND # 预留,当前未启用
|
||||
JWT_SECRET ACCESS_TOKEN_TTL REFRESH_TOKEN_TTL ADMIN_TOKEN_TTL
|
||||
OSS_ENDPOINT OSS_BUCKET OSS_ACCESS_KEY_ID OSS_ACCESS_KEY_SECRET # 未启用则为占位
|
||||
ALIPAY_APP_ID ALIPAY_PRIVATE_KEY ALIPAY_PUBLIC_KEY # 支付占位→dev_mock
|
||||
WECHAT_MCHID WECHAT_APP_ID WECHAT_PRIVATE_KEY
|
||||
TRON_API_KEY # USDT 查账
|
||||
SMSBAO_USERNAME SMSBAO_API_KEY SMS_SIGN_NAME # 短信宝验证码
|
||||
SMTP_HOST SMTP_PORT SMTP_USER SMTP_PASSWORD # 邮箱验证码(已启用)
|
||||
```
|
||||
|
||||
业务参数在 `server/config.yaml`(顶层键:`points` / `membership` / `recharge` / `referral` / `asset` / `withdraw` / `upload`),其中白名单项可在后台「系统配置」热改。
|
||||
|
||||
### 4.3 数据库初始化(新环境一次性)
|
||||
|
||||
```bash
|
||||
cd /root/project/video/server
|
||||
python3.11 -m venv .venv && .venv/bin/pip install .
|
||||
docker exec mysql8 mysql -uroot -p256kb.cn -e \
|
||||
"CREATE DATABASE IF NOT EXISTS video CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
|
||||
.venv/bin/alembic upgrade head # 4 个迁移,建 30 张业务表(含 series 补漏迁移 e7a1f3c58d02)
|
||||
```
|
||||
|
||||
创建首个超管(代码不自动播种):
|
||||
|
||||
```bash
|
||||
.venv/bin/python - <<'EOF'
|
||||
import asyncio
|
||||
from sqlalchemy.ext.asyncio import create_async_engine, async_sessionmaker
|
||||
@@ -77,127 +181,169 @@ asyncio.run(main())
|
||||
EOF
|
||||
```
|
||||
|
||||
### 2.4 启动
|
||||
后台角色:`super`(全部权限)、`operator`(仪表盘/套餐/赠送)、`reviewer`(视频审核)。
|
||||
|
||||
```bash
|
||||
cd server
|
||||
.venv/bin/uvicorn app.main:app --host 0.0.0.0 --port 8000
|
||||
```
|
||||
|
||||
- 健康检查:`curl http://127.0.0.1:8000/health` → `{"status":"ok"}`
|
||||
- **生产请单 worker 运行**(不要加 `--workers > 1`):结算周期任务(推荐激活/创作收益/超时关单)随进程内 lifespan 启动,多 worker 会重复执行。
|
||||
- 生产建议用 systemd/supervisor 托管进程。
|
||||
|
||||
服务暴露的路径(Nginx 反代时用):
|
||||
### 4.4 服务暴露路径(反代依据)
|
||||
|
||||
| 路径 | 说明 |
|
||||
|---|---|
|
||||
| `/api/v1/*` | App 全部业务接口 |
|
||||
| `/api/v1/*` | App 业务接口(9 个路由文件 + admin,共 100+ 端点) |
|
||||
| `/api/v1/admin/*` | 管理后台接口 |
|
||||
| `/r/{code}` | 推广 H5 落地页(邀请链接) |
|
||||
| `/static/*` | 本地文件存储(头像/封面/视频,未接 OSS 时) |
|
||||
| `/r/{code}` | 邀请落地页 |
|
||||
| `/static/*` | 本地媒体文件 |
|
||||
| `/health` | 健康检查 |
|
||||
|
||||
## 3. 管理后台部署(admin/)
|
||||
## 5. 管理后台发布(admin/)
|
||||
|
||||
```bash
|
||||
cd admin
|
||||
cd /root/project/video/admin
|
||||
npm ci
|
||||
npm run build # 产物在 admin/dist/
|
||||
npm run build # 产物直接覆盖 admin/dist/,即 vm 站点根目录
|
||||
```
|
||||
|
||||
Nginx 参考配置(同一站点托管后台 + 反代 API):
|
||||
- 纯静态站点,**构建即发布,无需重启任何服务**;浏览器强刷(Ctrl+F5)生效
|
||||
- 前端 API baseURL 为 `/api/v1`,由 vm 站点的 `/api/` 反代到后端
|
||||
- 菜单按角色显示:仪表盘/套餐/赠送=operator,视频审核=reviewer,其余=super
|
||||
|
||||
## 6. nginx 配置(宝塔管理)
|
||||
|
||||
配置文件:`/www/server/panel/vhost/nginx/{h5,vm,vapi,codeserver}.256kb.top.conf`。
|
||||
改配置在宝塔面板「网站 → 设置 → 配置文件」编辑,或改文件后 `nginx -t && nginx -s reload`。
|
||||
|
||||
关键规则摘要:
|
||||
|
||||
**h5.256kb.top**
|
||||
|
||||
```nginx
|
||||
server {
|
||||
listen 443 ssl;
|
||||
server_name admin.example.com;
|
||||
# ssl_certificate / ssl_certificate_key ...
|
||||
|
||||
root /path/to/video/admin/dist;
|
||||
index index.html;
|
||||
|
||||
location / { try_files $uri $uri/ /index.html; } # SPA 回退
|
||||
location /api/ { proxy_pass http://127.0.0.1:8000; } # 后台接口
|
||||
location /static/ { proxy_pass http://127.0.0.1:8000; } # 媒体文件
|
||||
location /r/ { proxy_pass http://127.0.0.1:8000; } # 推广落地页
|
||||
}
|
||||
root /root/project/video/h5; # index.html 下载页
|
||||
location /downloads/ { alias /root/project/video/runtime/downloads/; } # APK
|
||||
location /videos/ { alias /root/project/video/runtime/videos/; }
|
||||
location /uploads/ { alias /root/project/video/runtime/uploads/; }
|
||||
location /r/ { proxy_pass http://127.0.0.1:8000; } # 邀请落地
|
||||
location /api/v1/invite/ { proxy_pass http://127.0.0.1:8000; }
|
||||
```
|
||||
|
||||
浏览器访问 `https://admin.example.com`,用 2.3 创建的账号登录。
|
||||
**vm.256kb.top**
|
||||
|
||||
## 4. App 编译(app/)—— 需要编译
|
||||
```nginx
|
||||
root /root/project/video/admin/dist;
|
||||
location / { try_files $uri $uri/ /index.html; } # SPA 回退
|
||||
location /api/ { proxy_pass http://127.0.0.1:8000; }
|
||||
location /static/ { proxy_pass http://127.0.0.1:8000; }
|
||||
location /r/ { proxy_pass http://127.0.0.1:8000; }
|
||||
```
|
||||
|
||||
App 是 Flutter 工程,**必须编译出安装包**分发给用户,服务器上不运行 App。
|
||||
**vapi.256kb.top**:`location / { proxy_pass http://127.0.0.1:8000; }` 全量反代。
|
||||
|
||||
### 4.1 关键:App 如何找到服务器(不硬编码域名)
|
||||
站点日志在 `/www/wwwlogs/<域名>.log`(排查 App 接口 401/500 时非常管用)。
|
||||
|
||||
启动时按优先级确定 API Base URL([api_domain.dart](../app/lib/core/network/api_domain.dart)):
|
||||
## 7. App 编译与发版
|
||||
|
||||
1. **`--dart-define=API_BASE` 编译期指定**(测试/自建最简单);
|
||||
2. 本地缓存的上次成功域名;
|
||||
3. DNS TXT 发现:解析 `txt.256kb.top` 的 TXT 记录,记录内容即 Base URL 主体;
|
||||
4. 兜底域名:`md5(yyyyMM) + "_jsnbhh.vip"`(每月变化)。
|
||||
### 7.1 域名发现(不硬编码 API 地址)
|
||||
|
||||
> 注意:客户端业务路径**不带** `/api/v1` 前缀,因此 Base URL 必须包含前缀,例如 `https://api.example.com/api/v1`(TXT 记录内容写 `api.example.com/api/v1` 即可,客户端自动补 `https://`)。
|
||||
启动时优先级:`--dart-define=API_BASE` > 本地缓存 > DNS TXT(`txt.256kb.top`) > 兜底域名(md5(yyyyMM)+后缀)。
|
||||
正式包**不带 dart-define 直接构建**,走 TXT 发现(当前指向 `vapi.256kb.top/api/v1`)。
|
||||
|
||||
上线二选一:
|
||||
|
||||
- **方式 A(推荐,免改代码)**:把 `txt.256kb.top` 的 TXT 记录指到你的 API 入口(内容:`api.example.com/api/v1`),并按第 3 节方式用 Nginx 把 `/api/v1/` 反代到 uvicorn。
|
||||
- **方式 B(自己的域名体系)**:修改 [app_config.dart](../app/lib/core/config/app_config.dart) 的 `dnsTxtHost` / `fallbackDomainSuffix` 为你控制的域名后重新编译。
|
||||
|
||||
### 4.2 Android
|
||||
### 7.2 一键发版 deploy.sh
|
||||
|
||||
```bash
|
||||
cd app
|
||||
flutter pub get
|
||||
# 联调/内测:直连服务器
|
||||
flutter build apk --release --dart-define=API_BASE=https://api.example.com/api/v1
|
||||
# 正式(走 TXT 域名发现):不带 dart-define 直接构建
|
||||
flutter build apk --release
|
||||
# 1. 改 app/pubspec.yaml 的 version(如 1.3.7+23,+后为 versionCode)
|
||||
# 2. 执行
|
||||
bash /root/project/video/deploy.sh
|
||||
```
|
||||
|
||||
产物:`build/app/outputs/flutter-apk/app-release.apk`,直接分发安装即可。
|
||||
脚本自动完成:`flutter analyze`(warning 不阻断)→ `flutter build apk --release` → `gradlew --stop` 清理 Gradle Daemon(防内存泄漏)→ 覆盖 `runtime/downloads/video-app.apk` → 同步 DB `system_configs.app.version.android`(latest_version/code 更新、**min_version_code 保留**、连接指定 `-h 127.0.0.1`)→ `docker exec my-redis` 清配置缓存。
|
||||
老用户打开 App 触发 `/api/v1/app/version` 检查 → 弹更新提示 → 跳 h5 下载页。强制更新:把 `min_version_code` 调大于用户版本。
|
||||
|
||||
### 4.3 iOS
|
||||
### 7.3 正式签名(2026-10-17 起,v1.3.7+23 为首个正式签名版)
|
||||
|
||||
```bash
|
||||
flutter build ipa --release
|
||||
```
|
||||
|
||||
需要 macOS + Xcode + Apple 开发者证书签名(本机未装 Xcode 时可只交付 Android)。iOS 端上架前在管理后台「系统配置」确认 `ios_payment.enabled` 开关(规避苹果虚拟支付审核)。
|
||||
|
||||
### 4.4 支付渠道打包注意
|
||||
|
||||
`app/pubspec.yaml` 中 `tobias.url_scheme`、`fluwx.app_id`、两处 `universal_link` 是占位值,**正式支付上线前必须替换为商户申请的真实值**并重新编译;未接真实支付时服务端返回 `dev_mock`,App 自动走轮询查单兜底。
|
||||
|
||||
## 5. 上线后必改的后台配置
|
||||
|
||||
登录管理后台「系统配置」:
|
||||
|
||||
| 配置 key | 说明 |
|
||||
| 项 | 值 |
|
||||
|---|---|
|
||||
| `app.invite_base_url` | 推广落地页地址,改为 `https://你的域名/r/`(影响 App 邀请链接) |
|
||||
| `app.version.android` / `app.version.ios` | JSON:`latest_version` / `latest_version_code` / `min_version_code` / `update_url` / `notice`,控制版本检查与强制更新 |
|
||||
| `ios_payment.enabled` | iOS 支付开关(审核期可关闭) |
|
||||
| 敏感词、轮播公告等 | 按需 |
|
||||
| keystore | `app/android/app/upload-keystore.jks`(PKCS12,RSA 2048,有效期 30 年) |
|
||||
| 密码配置 | `app/android/key.properties`(alias=`video_release`) |
|
||||
| 证书指纹 SHA-256 | `8fb75f6a:3a9f:bc39:...:aef4`(完整值见 `apksigner verify --print-certs`) |
|
||||
| git | 两者均已 gitignore,**不入库** |
|
||||
| 备份 | `/root/backups/video-app-signing/`(keystore + key.properties) |
|
||||
|
||||
## 6. 端到端验证清单
|
||||
`build.gradle.kts` 逻辑:key.properties 存在 → release 签名;否则回退 debug(保证新环境可编译)。
|
||||
|
||||
> ⚠️ **keystore 丢失 = 永远无法给老用户推更新**。除服务器备份外,务必另存一份到本地电脑/网盘。
|
||||
> ⚠️ 首个正式签名版与旧 debug 签名不一致,**老用户必须先卸载再安装**(版本 notice 已注明)。
|
||||
> 侧载安装时小米等厂商会提示"未备案",勾选"已了解风险"即可继续安装;消除该提示需办理 App ICP 备案。
|
||||
|
||||
### 7.4 iOS
|
||||
|
||||
`flutter build ipa --release`(需 macOS + Xcode + 开发者证书,当前仅交付 Android)。上架前确认后台 `ios_payment.enabled` 开关规避虚拟支付审核。
|
||||
|
||||
## 8. 项目启停 project-ctl.sh
|
||||
|
||||
```bash
|
||||
# 服务端
|
||||
curl http://127.0.0.1:8000/health # ok
|
||||
curl "http://127.0.0.1:8000/api/v1/videos/feed?type=hot&limit=1" # code:0
|
||||
curl "http://127.0.0.1:8000/api/v1/search/hot" # code:0
|
||||
|
||||
# 管理后台:浏览器打开 → 登录 → 视频审核/用户列表有数据
|
||||
|
||||
# App:安装 APK → 启动页过版本检查 → Feed 出视频 → 注册/登录 → 观看计费
|
||||
bash /root/project/video/project-ctl.sh start # docker mysql8/my-redis → 等待就绪 → video-api(enable+start) → 健康检查
|
||||
bash /root/project/video/project-ctl.sh stop # video-api 停止并 disable + 停两个容器(重启服务器也不会拉起)
|
||||
bash /root/project/video/project-ctl.sh restart # 停 → 启
|
||||
bash /root/project/video/project-ctl.sh status # 服务/容器/接口探测
|
||||
```
|
||||
|
||||
## 7. 已知限制(当前版本)
|
||||
- 管理范围:`video-api.service` + `mysql8` + `my-redis`;**不含 nginx**(系统组件)
|
||||
- 服务器重启后如项目处于"运行态",docker 与 systemd 会自动拉起全部组件,无需人工干预
|
||||
- 项目当前(2026-10-18 起)处于**暂停态**,恢复运行执行 `start` 即可
|
||||
|
||||
- **媒体存储为本地文件**(`server/uploads`,经 `/static` 访问),接口返回相对路径;生产接入 OSS/CDN 使服务端返回绝对 URL 需二次开发(`storage_service` 已按此边界隔离)。
|
||||
- 短信/邮件/USDT/支付宝/微信未配置密钥时自动降级(验证码发不出、支付走 dev_mock),不影响其余功能。
|
||||
- CORS 当前为 `*`,生产可在 `app/main.py` 按需收紧。
|
||||
- 结算周期任务在 API 进程内运行,见 2.4 单 worker 说明。
|
||||
## 9. 备份策略
|
||||
|
||||
| 对象 | 方式 | 频率建议 |
|
||||
|---|---|---|
|
||||
| MySQL 数据 | `docker exec mysql8 mysqldump -uroot -p256kb.cn video \| gzip > backup-$(date +%F).sql.gz`(或宝塔计划任务) | 每日 |
|
||||
| keystore + key.properties | 已备份 `/root/backups/video-app-signing/`,另离线保存 | 变更即备 |
|
||||
| `runtime/`(视频/APK/上传) | 文件级备份(rsync/对象存储) | 每周 |
|
||||
| `server/.env`、`config.yaml` | 纳入文件备份 | 变更即备 |
|
||||
|
||||
恢复:导入 sql → `alembic upgrade head` 校验版本 → 启动服务。
|
||||
|
||||
## 10. 日常运维与故障排查
|
||||
|
||||
### 10.1 常用命令
|
||||
|
||||
```bash
|
||||
journalctl -u video-api -f # 后端日志
|
||||
docker logs -f mysql8 / my-redis # 容器日志
|
||||
tail -f /www/wwwlogs/vapi.256kb.top.log # App 接口访问日志
|
||||
docker exec my-redis redis-cli DEL config:system # 清系统配置缓存
|
||||
docker exec my-redis redis-cli KEYS 'auth:*' # 查登录态键
|
||||
free -h && docker stats --no-stream # 资源占用
|
||||
```
|
||||
|
||||
### 10.2 历史故障速查表
|
||||
|
||||
| 症状 | 根因 | 处置 |
|
||||
|---|---|---|
|
||||
| 后端全接口 500,Redis 报错 | 服务器重启后 my-redis 未自启,宿主机 Redis 5.0.3 抢 6379,RESP3 握手失败 | 停本机 redis 自启;docker start my-redis;客户端已固定 protocol=2 |
|
||||
| 重启后服务缺失/端口错乱 | Docker 容器未配 restart 策略 | 两容器均已 `unless-stopped` |
|
||||
| App 更新提示反复弹 | 硬编码版本号与 APK 不一致 | 已改用 package_info_plus 读真实版本号 |
|
||||
| 发版后 DB 版本没更新 | deploy.sh mysql 未加 `-h 127.0.0.1` 写错宿主机库 | 已修复;注意检查 |
|
||||
| 编译后内存爆满 | Gradle Daemon 常驻 | deploy.sh 已自动 `gradlew --stop` |
|
||||
| IDE 里 OpenJDK 吃内存 | Trae 的 fwcd.kotlin 扩展语言服务器 | 禁用该扩展并重载窗口 |
|
||||
| 编译卡死无输出 | Gradle 进程僵死 | 杀掉重跑即可 |
|
||||
| 会员/钱包页加载失败 | 僵尸登录态(access 过期+无 refresh) | App 端已修复(401 清态+启动检测);临时:退出重登 |
|
||||
| 切"我的"仍听到视频声 | 播放器池 open 竞态 | 已修复(suspend/resume) |
|
||||
| 进度条不走/拖动无效 | 本地代理未支持 Range | 已修复(Accept-Ranges + 206) |
|
||||
|
||||
### 10.3 端到端验证清单(发版/重启后)
|
||||
|
||||
```bash
|
||||
curl http://127.0.0.1:8000/health # {"status":"ok"}
|
||||
curl "http://127.0.0.1:8000/api/v1/videos/feed?type=hot&limit=1" # code:0
|
||||
curl -I https://h5.256kb.top/downloads/video-app.apk # 200
|
||||
# 浏览器开 https://vm.256kb.top → 登录 → 仪表盘有数据
|
||||
# App:启动过版本检查 → Feed 出视频 → 登录 → 播放/计费正常
|
||||
```
|
||||
|
||||
## 11. 新环境从零部署速查
|
||||
|
||||
1. 装 Docker / Python3.11 / Node18+ / Flutter / nginx(或宝塔)
|
||||
2. 建 mysql8、my-redis 容器(§3 命令)
|
||||
3. `server/` 建 venv、装依赖、写 `.env`、`alembic upgrade head`、建超管(§4.3)
|
||||
4. 写 systemd unit(§4.1)→ `systemctl enable --now video-api`
|
||||
5. `admin/` 构建 dist;配三个 nginx 站点 + 证书(§6)
|
||||
6. App:放入 keystore + key.properties → `deploy.sh` 发版(§7)
|
||||
7. 后台「系统配置」:`app.invite_base_url`、`app.version.android/ios` 等(见 03 接口文档配置键)
|
||||
8. 跑 §10.3 验证清单
|
||||
|
||||
Reference in New Issue
Block a user