diff --git a/.gitignore b/.gitignore index 2597e4c..6c4ea10 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,30 @@ /.ignore -# 系统文件 -.DS_Store -Thumbs.db \ No newline at end of file +# 系统文件 +.DS_Store +Thumbs.db + +# Node +node_modules/ +dist/ +build/ +.turbo/ +*.log +npm-debug.log* +pnpm-debug.log* + +# 环境变量与密钥(禁止入库) +.env +.env.* +!.env.example + +# 小程序构建产物 +yelaymaths/miniprogram_npm/ +yelaymaths/project.private.config.json + +# 课程数据端(大文件不入库,仅保留目录骨架与说明) +yelaymaths_data.yeomai.site/** +!yelaymaths_data.yeomai.site/**/ +!yelaymaths_data.yeomai.site/**/README.md +!yelaymaths_data.yeomai.site/README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..4fc6ebc --- /dev/null +++ b/README.md @@ -0,0 +1,67 @@ +# 叶雷讲数学 + +微信小程序在线数学课程项目。 + +## 目录结构 + +四个子项目**各自独立**:各自安装依赖、各自持有 lockfile,互不依赖。 +仓库根目录不放任何 Node 配置,也不会有 `node_modules`。 + +| 目录 | 角色 | 技术栈 | +|---|---|---| +| `yelaymaths/` | 微信小程序 | 原生小程序 | +| `yelaymaths_api/` | 服务端 API | NestJS + Prisma + MySQL 5.7 | +| `yelaymaths_admin/` | 管理后台 | Next.js 15 + Tailwind | +| `yelaymaths_data.yeomai.site/` | 课程数据(图片 / 视频) | 静态文件 | +| `docs/` | 设计与部署文档 | — | + +历史版本代码保留在 `前端/`、`服务端/`、`数据库/`,仅作参考,迁移完成后可删除。 + +## 快速开始 + +### 1. 服务端 + +```bash +cd yelaymaths_api +pnpm install +cp .env.example .env # 填写数据库连接串与各类密钥 +pnpm prisma:generate +pnpm prisma:migrate # 建表 +pnpm dev # http://127.0.0.1:3000 +``` + +### 2. 迁移旧数据 + +```bash +cd yelaymaths_api +pnpm legacy:check # 体检,确认旧数据可无损映射 +pnpm legacy:migrate -- --dry-run # 试运行,核对行数 +pnpm legacy:migrate # 正式迁移 +``` + +详见 [`yelaymaths_api/tools/legacy/README.md`](yelaymaths_api/tools/legacy/README.md)。 + +### 3. 管理后台 + +```bash +cd yelaymaths_admin +pnpm install +cp .env.example .env.local # 填写 API 地址 +pnpm dev # http://127.0.0.1:3001 +``` + +首次打开用登录页的「创建管理员账号」入口初始化超管。 + +### 4. 小程序 + +用微信开发者工具打开 `yelaymaths/`,先在 `yelaymaths/config/index.js` 里填写 API 与静态资源域名, +然后执行【工具 → 构建 npm】。 + +## 部署 + +见 [`docs/部署指南.md`](docs/部署指南.md)。 + +## 设计文档 + +见 [`docs/重构开发文档.md`](docs/重构开发文档.md)。 + diff --git a/docs/部署指南.md b/docs/部署指南.md new file mode 100644 index 0000000..148de28 --- /dev/null +++ b/docs/部署指南.md @@ -0,0 +1,263 @@ +# 部署指南 + +面向接手部署的人。按顺序执行即可,每一步都有验证方法。 + +--- + +## 0. 变量约定 + +下文用占位符代替真实域名,部署时替换: + +只需要**三个域名**:管理后台直接调用 ``(服务端同一进程内含 `/admin/v1/*`), +不需要单独的接口域名。 + +| 占位符 | 含义 | 举例 | +|---|---|---| +| `` | 服务端 API 域名 | `yelaymaths-api.example.com` | +| `<后台域名>` | 管理后台域名 | `yelaymaths-admin.example.com` | +| `<数据域名>` | 课程数据(图片 / 视频)域名 | `yelaymaths-data.example.com` | + +> ⚠️ **三个域名都不能含下划线。** 下划线不是合法的主机名字符(RFC 1034/1035 只允许字母、数字、连字符), +> 后果是 Let's Encrypt 拒签证书、微信「request 合法域名」校验也会拒绝。 +> +> 用中划线:`yelaymaths-api.example.com`,不要用 `yelaymaths_api.example.com`。 + +--- + +## 1. 服务器准备(宝塔面板) + +1. 软件商店安装:**MySQL 5.7**、**Nginx**、**Node 版本管理器**(选 Node 22 LTS)。 +2. 防火墙放行 80 / 443。 +3. 建 3 个站点,各配 Let's Encrypt 证书: + + | 站点 | 反向代理到 | + |---|---| + | `` | `http://127.0.0.1:3000` | + | `<后台域名>` | `http://127.0.0.1:3001` | + + > 这两个站点**都不是静态站点**,反向代理必须配,只上传文件不会工作。 + +4. 建数据端站点 `<数据域名>`,根目录指向课程数据目录(见第 6 节)。 + +--- + +## 2. 数据库 + +1. 新建数据库 `yelaymaths`,字符集 `utf8mb4`。 +2. 新建数据库账号并授权。 + +> **关于 Prisma 权限**:`prisma migrate dev` 需要 `CREATE DATABASE` 权限来建影子库,宝塔默认账号没有。 +> 生产环境**只用** `pnpm prisma:migrate`(即 `prisma migrate deploy`),它不建影子库,无需额外权限。 + +3. **备份旧库**(迁移前必做): + + ```bash + mysqldump -u <用户> -p api_yeomai_site > /root/backup/api_yeomai_site_$(date +%F).sql + ``` + +--- + +## 3. 部署服务端 + +```bash +cd /www/wwwroot +git clone <仓库地址> yelaymaths +cd yelaymaths/yelaymaths_api +pnpm install +cp .env.example .env +vi .env # 按下表填写 +``` + +### 必须填写的环境变量 + +| 变量 | 说明 | +|---|---| +| `DATABASE_URL` | `mysql://用户:密码@127.0.0.1:3306/yelaymaths` | +| `LEGACY_DATABASE_URL` | 旧库连接串,仅迁移脚本使用 | +| `API_BASE_URL` | `https://` | +| `STATIC_BASE_URL` | `https://<数据域名>` | +| `ADMIN_ORIGIN` | `https://<后台域名>`(跨域白名单) | +| `JWT_ACCESS_SECRET` | 随机 32 位以上 | +| `JWT_REFRESH_SECRET` | 随机 32 位以上 | +| `SESSION_KEY_ENC_SECRET` | 随机 32 位以上,用于加密存储用户 sessionKey | +| `MEDIA_SIGN_SECRET` | 随机 32 位以上,用于播放地址签名 | +| `WX_APPID` | `wx3fbaf897825bf04f` | +| `WX_APPSECRET` | 小程序密钥 | +| `WX_OFFER_ID` / `WX_PAY_APPKEY` | 虚拟支付参数,未开通可留空 | +| `DATA_ROOT` | 课程数据目录的**绝对路径** | + +> 生成随机密钥:`openssl rand -hex 32` +> +> ⚠️ `STATIC_BASE_URL` 必须在跑数据迁移**之前**填对。旧库里有 48 处内容写死了旧域名, +> 迁移时会按这个变量改写;填错会导致图片与视频全部 404。 + +### 建表、迁移、启动 + +```bash +pnpm prisma:generate +pnpm prisma:migrate # 建表 +pnpm legacy:check # 体检 +pnpm legacy:migrate -- --dry-run # 核对行数 +pnpm legacy:migrate # 正式迁移 +pnpm build +``` + +在宝塔「Node 项目」里新建项目: + +- 项目目录:`/www/wwwroot/yelaymaths/yelaymaths_api` +- 启动文件:`dist/main.js` +- 端口:`3000` +- 运行方式:PM2 + +**验证**:`curl https:///healthz` 应返回 `{"code":0,...,"db":"ok"}`。 +若 `db` 为 `down`,说明数据库连接串不对。 + +--- + +## 4. 部署管理后台 + +```bash +cd /www/wwwroot/yelaymaths/yelaymaths_admin +pnpm install +cp .env.example .env.local +vi .env.local # NEXT_PUBLIC_API_BASE_URL=https:// +pnpm build +``` + +同样用宝塔「Node 项目」启动: + +- 启动文件:`node_modules/next/dist/bin/next`,参数 `start -p 3001` +- 或直接用 `pnpm start` +- 端口:`3001` + +**验证**:打开 `https://<后台域名>`,应看到登录页。 +首次使用点「创建管理员账号」,创建完成后切回登录。 + +> 该接口只在系统中没有任何管理员时生效,之后调用会返回「已存在」,因此可以安全保留。 + +--- + +## 5. 部署课程数据端 + +把旧服务器的资源目录复制到 `<数据域名>` 站点根目录: + +| 来源 | 目标 | 体积参考 | +|---|---|---| +| 旧站 `Images/` | `<根>/Images/` | 约 195 MB | +| 旧站 `WebPages/` | `<根>/WebPages/` | 约 21 MB | +| 旧站 `Course/` | `<根>/Course/` | 视频文件 | +| 旧站 `FreeVideos/` | `<根>/FreeVideos/` | 视频文件 | + +再从旧小程序的 `static/image/` 复制三张占位图到 `<根>/Images/Placeholder/`: + +``` +Images/Placeholder/user.png +Images/Placeholder/defaultAvatar.png +Images/Placeholder/weixin.png +``` + +**目录名与文件名不可更改**,前端是拼路径访问的。完整约定见 +[`yelaymaths_data.yeomai.site/README.md`](../yelaymaths_data.yeomai.site/README.md)。 + +### ⚠️ 付费视频目录必须挡住 + +付费视频的鉴权由服务端 `/media/*` 签名通道提供(地址有效期 30 分钟,HLS 的 `.ts` 分片也逐行带签名)。 +但如果 `Course/` 目录同时被数据域名公开暴露,任何人猜到路径就能直接下载,这套防护等于没有。 + +**在数据端站点的 Nginx 配置里加一条:** + +```nginx +location ^~ /Course/ { + deny all; +} +``` + +免费内容(`Images/`、`WebPages/`、`FreeVideos/`)保持公开即可。 + +> 若后续改用 CDN,把 `MediaService.buildSignedUrl` 换成 CDN 厂商的签名方式,并在 CDN 侧对 `Course/` 开鉴权。 + +--- + +## 6. 配置小程序 + +1. 编辑 `yelaymaths/config/index.js`: + + ```js + API_BASE_URL: 'https://', + STATIC_BASE_URL: 'https://<数据域名>', + ``` + +2. 用微信开发者工具打开 `yelaymaths/`,执行【工具 → 构建 npm】。 +3. 上传并提交审核。 + +> 小程序**不打包任何业务图片**,只保留 6 个 tabBar 图标(约 19 KB)。 +> tabBar 图标必须本地,这是微信的限制,无法改为网络图。 + +--- + +## 7. 微信小程序后台配置 + +| 配置项 | 位置 | 值 | +|---|---|---| +| request 合法域名 | 开发 → 开发管理 → 开发设置 | `https://` | +| 发货推送 URL | 虚拟支付 → 基本配置 | `https:///v1/payments/notify` | +| 小程序简称 | 虚拟支付 → 基础配置 | iOS 支付必需 | +| 苹果 IAP 支付 | 虚拟支付 → 基础配置 | iOS 支付需开启 | +| 支付道具 | 虚拟支付 → 道具管理 | 每门课建一个,记下道具 ID 与价格 | + +> `` 与 `