263 lines
34 KiB
Markdown
263 lines
34 KiB
Markdown
# 黄果短剧 · Next.js
|
||
|
||
项目编号 `JHA-1050`,应用及埋点 App ID `1050`,npm 包与 `/healthz` 应用标识均为 `jha-1050-jgdj-web`。品牌名称统一为「黄果短剧」。
|
||
|
||
部署包、默认目录与 Supervisor 名已统一使用 `jgdj`。服务器地址和端口沿用原配置;首次发布前需准备对应目录、远端更新脚本和服务配置,并核对 Go 上游及后台 `reportSdkConfig` 均属于应用 `1050`。仓库地址登记为新项目的目标路径,本次不修改 Git remote。
|
||
|
||
Next.js App Router、React、TypeScript。保留 HTML 原稿布局和移动端导航,业务数据通过 Swagger API 动态获取。
|
||
|
||
当前视觉基准为 `禁果短剧_2组.html`:采用粉色主题、桌面双层品牌页头、首页主推轮播与六张横版推荐、历史悬浮面板及统一页脚。移动端沿用参考稿的菜单、搜索、双列内容和抽屉导航。栏目、剧集、广告及观看历史继续读取真实接口,HTML 内的示例剧目、账号、金额和联系方式不作为业务配置导入。
|
||
|
||
## 运行
|
||
|
||
已使用 Node.js **22.17.1** 验证;最低要求 20.9。
|
||
|
||
```sh
|
||
nvm use 22.17.1
|
||
npm ci # 或:pnpm install
|
||
npm run lock:check
|
||
npm run dev
|
||
```
|
||
|
||
本地可用 **npm** 或 **pnpm** 安装依赖;发版与测试服安装以 `package-lock.json` + `npm ci` 为准。两份 lock(`package-lock.json` / `pnpm-lock.yaml`)中的运行时版本须一致,`npm run lock:check`(构建/打包前会自动执行)会校验已安装版本与两份 lock,避免出现 Next 小版本漂移导致 `/healthz` 500。变更依赖后请同时更新两份 lock(`npm install` 与 `pnpm install`)。
|
||
|
||
开发、测试和生产启动的默认地址均为 http://localhost:21030,端口或环境变量修改后需要重启 Next.js。
|
||
|
||
```sh
|
||
npm run typecheck
|
||
npm run build
|
||
npm test
|
||
npm start
|
||
```
|
||
|
||
现在需要运行 Next.js 服务以提供 API 代理与动态首页,不再导出静态 `out/`。旧的静态预览进程需要停止后用新命令启动。
|
||
|
||
## 环境配置
|
||
|
||
本项目使用 `config/*.env` 区分业务环境,启动脚本在运行 Next.js 前加载配置。
|
||
|
||
| 环境 | 配置文件 | 构建命令 | 启动命令 | 构建目录 |
|
||
| ---- | ------------------------ | -------------------- | --------------------------------------------- | ------------- |
|
||
| 开发 | `config/development.env` | `npm run build:dev` | `npm run dev`;构建后可用 `npm run start:dev` | `.next-dev/` |
|
||
| 测试 | `config/test.env` | `npm run build:test` | `npm run start:test` | `.next-test/` |
|
||
| 生产 | `config/production.env` | `npm run build` | `npm start` | `.next/` |
|
||
|
||
测试环境按生产模式构建、启动,使用 `APP_ENV=test` 和 `NODE_ENV=production`;`npm run dev:test` 则使用测试配置启动开发服务器。业务环境通过 `APP_ENV` 区分,不把测试部署误设为单元测试使用的 `NODE_ENV=test`。
|
||
|
||
浏览器始终访问当前站点的相对地址 `/api/app/*`,Next.js 服务端通过 `UPSTREAM_API_URL` 请求 Go 的 `/api/app/*`。开发、测试环境使用 `http://172.104.181.99:26792`,生产环境使用 `http://139.162.49.252:21040`(app;web `21041`、h5 `21042`,H5 CDN `d23iqbsywbvs3m.cloudfront.net`)。上游地址仅由服务端读取。开发与测试默认端口 `21030`,生产 H5 端口 `21042`。公开接口缓存和 SEO 快照按解析后的上游域名隔离。修改服务器配置时,在对应目录新增 `config/test.local.env` 或 `config/production.local.env`;这些本地文件已忽略提交。优先级为:进程环境变量 > 对应 `*.local.env` > 对应基础配置。开发环境兼容旧 `.env.local`,其优先级低于 `config/development.local.env`;测试和生产不从旧 `.env.local` 加载本站业务配置。
|
||
|
||
原有 `API_BASE_URL` 仍作为兼容别名。在同一配置层内 `UPSTREAM_API_URL` 优先;较高配置层的旧变量仍可覆盖较低层的新变量,启动后统一为同一个上游地址。上游地址仅在服务端读取,不写入浏览器代码。
|
||
|
||
例如需要测试服务同时运行在另一端口,可在 `config/test.local.env` 写入:
|
||
|
||
```dotenv
|
||
PORT=21022
|
||
```
|
||
|
||
也可以临时运行 `npm run start:test -- --port 21022`。不同环境使用各自的构建产物及 SEO 快照缓存;先执行对应环境的构建命令,再执行其启动命令。`UPSTREAM_API_URL` 等服务端配置在启动时读取,修改后重启即可;`NEXT_PUBLIC_*` 在构建时写入浏览器代码,修改后应重新构建。
|
||
|
||
开发环境默认关闭公共接口缓存,测试和生产保留原有缓存上限;可用 `PUBLIC_DATA_CACHE_TTL_MS` 覆盖。`npm run check` 会先完成生产构建(含类型检查),再运行完整本地回归测试。
|
||
|
||
## 打包与部署
|
||
|
||
使用 Node.js 22.17.1;生产跨平台打包需要 npm >=10.9.2。两份脚本均先构建、运行回归测试,再整理 Next.js standalone、静态资源和对应环境配置。
|
||
|
||
```sh
|
||
# 预览流程,不构建、不连接服务器
|
||
bash deploy_test.sh --dry-run
|
||
bash deploy_prod.sh --dry-run
|
||
|
||
# 只生成本地压缩包,不上传
|
||
bash deploy_test.sh --package-only
|
||
bash deploy_prod.sh --package-only
|
||
```
|
||
|
||
测试产物为根目录的 `web_jgdj_h5.tar.gz` 和 `web_jgdj_h5.tar.gz.sha256`,上传相同文件及 `up_next_h5.sh`;同时刷新同名 `web_jgdj_h5.md5` 供本地兼容校验。包内直接包含 `start.mjs`、`server.js`、`.next-test/`、`public/`、`config/test.env` 和 lockfile。测试包不带本机 `node_modules`,使用 `tar -xzf web_jgdj_h5.tar.gz` 解压后,在 Linux 执行 `npm ci --omit=dev --no-audit --no-fund`,再执行 `node start.mjs`;默认端口为 21030。可通过 `DEPLOY_PACKAGE_NAME=自定义名称.tar.gz` 覆盖包名,校验文件随之生成。测试和生产使用同名压缩包,根目录文件对应最后一次执行的环境。
|
||
|
||
生产产物为 `web_jgdj_h5.tar.gz` 和纯哈希格式的 `web_jgdj_h5.md5`,包含 `.next/` 和 standalone 裁剪依赖,默认适配 Linux x64/glibc,可直接执行 `node start.mjs`。可通过 `DEPLOY_TARGET_ARCH=arm64`、`DEPLOY_TARGET_LIBC=musl` 选择目标架构和 libc。打包时仅从 lockfile 安装对应 Linux Sharp/libvips,并校验 ELF 架构,不携带 macOS 原生模块。
|
||
|
||
两种包仅包含对应的基础 `config/*.env`,不打包 `.env*`、`*.local.env` 和 SEO 缓存。服务器的私有覆盖配置放在发布目录下对应的 `config/test.local.env` / `config/production.local.env`;修改 `NEXT_PUBLIC_*` 必须重新构建。本站媒体密钥由上游接口代理提供,无需额外的本地 `m3u8.key` 文件。
|
||
|
||
部署默认值由两份脚本开头的变量定义,并支持环境变量覆盖;当前上传配置如下。本项目包名和 Supervisor 名使用 `jgdj`:
|
||
|
||
| 环境 | SSH 目标 | 上传目录 | SCP / SSH 端口 |
|
||
| ---- | ---------------------- | -------------------------- | --------------- |
|
||
| 测试 | `root@172.104.181.99` | `/server/web_jgdj/h5` | `22889 / 22889` |
|
||
| 生产 | `server@34.92.204.175` | `/home/server/web_jgdj/h5` | `22889 / 22889` |
|
||
|
||
直接执行脚本会使用上述地址;仅打包继续使用 `--package-only`。需要其他目标时可覆盖:
|
||
|
||
```sh
|
||
DEPLOY_HOST=user@test-host DEPLOY_DIR=/srv/jgdj/h5 DEPLOY_RUN_DIR=/srv/jgdj/h5 bash deploy_test.sh
|
||
DEPLOY_JUMP_HOST=user@jump-host DEPLOY_JUMP_DIR=/srv/jgdj/h5 bash deploy_prod.sh
|
||
```
|
||
|
||
测试当前 SSH 端口为 22889,可通过 `DEPLOY_SSH_PORT` 修改;它与 `config/test.env` 中的应用监听端口 `PORT` 分别配置。`deploy_test.sh` 上传成功后执行 `ssh -p 22889 root@172.104.181.99 "cd /server/web_jgdj/h5 && bash run.sh"`,由服务器现有的 `run.sh` 负责后续更新。上传目录和执行目录当前均为 `/server/web_jgdj/h5`,分别可用 `DEPLOY_DIR`、`DEPLOY_RUN_DIR` 覆盖;脚本不上传或覆盖远端 `run.sh`。部署测试从脚本帮助信息读取当前默认执行目录,并单独验证目录覆盖及上传、执行失败时的退出行为,修改服务器目录无需同步修改测试断言。上传失败不会执行远端更新,远端命令失败会立即退出;`--package-only` 和 `--dry-run` 均不执行 SSH。可通过 `DEPLOY_CHECK_URL` 在远端命令成功后增加公网健康检查。
|
||
|
||
随包上传的 `up_next_h5.sh` 保留供服务器按需调用:该脚本校验 SHA-256、检查并解压 tar.gz、安装运行依赖、保留本地覆盖与 SEO 缓存,然后切换版本、启动服务并检查 `/healthz` 与首页,失败自动恢复旧版本。默认 Supervisor 程序为 `jgdj_next_h5`,在服务器运行该脚本时可通过 `DEPLOY_PROGRAM` 覆盖;使用自定义包名时需设置对应的 `DEPLOY_PACKAGE_NAME`。服务器 `run.sh` 的解包、服务管理及包名规则以该脚本自身实现为准。
|
||
|
||
生产脚本将 `web_jgdj_h5.tar.gz` 和 `web_jgdj_h5.md5` 上传到 `server@34.92.204.175:/home/server/web_jgdj/h5/`,SCP 与 SSH 默认端口均为 `22889`。上传成功后执行 `ssh -p 22889 server@34.92.204.175 "cd /home/server/web_jgdj/h5 && bash rsupdate.sh"`。服务器现有的 `rsupdate.sh` 负责解包、保留生产私有配置和重启服务;本项目不上传或覆盖该脚本。上传失败不会触发远端更新,SSH 失败或返回失败状态会停止发布。可通过 `DEPLOY_JUMP_HOST`、`DEPLOY_JUMP_DIR`、`DEPLOY_SCP_PORT`、`DEPLOY_SSH_PORT` 覆盖目标,执行目录同步跟随上传目录。
|
||
|
||
生产 SSH 命令按参考项目统一由 `REMOTE_UPDATE_COMMAND` 生成,预览与实际执行的命令文本一致,目录经过安全字符校验后不再额外添加单引号,兼容服务器按文本匹配的命令白名单。执行前打印远端命令,便于核对权限规则和自定义目录;完整输出继续保存在发布日志中。
|
||
|
||
`npm run bundle:test` / `npm run bundle` 只整理已有构建;`npm run bundle:linux -- x64 glibc` 为生产产物准备 Linux 原生依赖。同一仓库的测试、生产脚本共用打包锁,压缩包及 `dist/` 已忽略提交。`/healthz` 是不触发游客登录、禁止缓存和索引的部署探针,不依赖上游接口。
|
||
|
||
## 页面路由
|
||
|
||
首页使用站点根地址,例如 `http://127.0.0.1:21030`,也支持 `http://127.0.0.1:21030/`;两者都直接显示首页。浏览器把纯域名的请求路径解析为 `/`,不需要跳转。短剧列表单独使用 `/shorts/`。
|
||
|
||
其他页面统一使用以 `/` 结尾的路径路由,可直接打开、刷新、在新标签页打开,并使用浏览器前进和后退。链接生成时就包含尾斜杠,不通过跳转或地址替换补齐。
|
||
|
||
- 首页 `/`;短剧 `/shorts/`;专题 `/topics/`;热播榜 `/rank/hot/`、新剧榜 `/rank/new/`、追剧榜 `/rank/follow/`;应用 `/apps/`;回家的路 `/home-road/`。榜单 tab 使用页面链接,数据类型由路径决定;原 `/rank/` 仍可直接访问热播榜,不重定向。
|
||
- 官方入口 `/navigation/`:独立展示线路、官方渠道与 App 下载,不加载首页导航、播放器或账户交互;首页和业务页面不再弹出入口导航页。
|
||
- 专题、题材、标签、栏目:`/topics/:id/`、`/theme/:id/`、`/tag/:id/`、`/config/:id/`。
|
||
- 详情 `/detail/:id/`;章节播放 `/play/:id/:chapterId/`。
|
||
- 内容路由优先使用接口返回的英文或拼音 `slug`(字母、数字、连字符、下划线),中文或无效 slug 回退到 ID,例如 `/detail/city-story/`、`/play/city-story/90001/`、`/topics/urban/`、`/tag/urban/`、`/config/editors-choice/`。章节段仍是章节 ID。卡片、轮播、选集、评论/列表分页、解锁后跳转和 SEO 地址使用同一规则;原 ID 地址继续直接访问。有配置的 `moreUrl` 优先级不变。
|
||
- 搜索 `/search/关键词/`;分页 `/search/关键词/page/2/`。关键词通过 `encodeURIComponent` 作为单个路径段编码,支持中文和特殊字符。
|
||
- 独立导航页 `/navigation/` 从 `config/info` 读取站名、介绍、`navPageList`、永久入口、TG/X 和 App 下载地址。线路按 `line` 排序;旧配置兼容 `dhNewUrlAddr1/2/3` 与最新地址字段。未配置或无效的链接不展示,不使用演示账号或首页链接兜底。
|
||
- 帮助与平台信息 `/info/:slug/`(关于我们、用户协议、隐私政策、常见问题、2257、DMCA)。个人中心、收藏/历史/点赞/已购、账号设置、充值/VIP、意见反馈页面已移除,访问返回 404。
|
||
- 所有分页使用独立路径,例如 `/shorts/page/2/`、`/rank/new/page/2/`、`/topics/31/page/2/`、`/member/history/page/2/`、`/info/deposit/vip/page/2/`;详情和播放页的 `/page/2/` 对应评论第 2 页。页码、上一页和下一页均为真实链接,可直接访问、刷新和前进后退。
|
||
- 排序通过 `?sort=2` 保留。切换排序时链接指向第一页,翻页时保留当前条件。
|
||
|
||
页面直接根据 URL 路径加载,不解析或转换旧 hash 路由。站内导航、动态卡片、播放选集、搜索和会员入口均使用路径地址;无效路径返回 404。
|
||
|
||
页面地址统一使用尾斜杠。`src/proxy.ts` 对有效页面的无尾斜杠地址返回 `308` 永久重定向,例如 `/shorts` → `/shorts/`,分页、搜索关键词和查询参数完整保留;首页 `/` 直接响应。Next.js 保留 `trailingSlash: true` 和 `skipTrailingSlashRedirect: true`,由 Proxy 仅处理页面,API、播放列表、密钥、静态资源地址不受影响;无效路径直接返回 404。
|
||
|
||
## 接口配置
|
||
|
||
顶部、侧栏和移动导航由 `navigation/header.categoryList` 在服务端渲染,按接口顺序显示名称、地址和二级导航。URL 导航与二级导航显示在主导航,底部类型不混入;成功返回空列表时不注入固定菜单。`/hot` 直接生成本站 `/rank/hot/` 链接;已知系统入口的空地址按本站路由生成。导航公共数据缓存 60 秒,后台更新在缓存过期后的页面请求生效;页面高亮仍按当前路由判断。接口图标优先,缺失时沿用本站图标;外链添加 nofollow 等属性。
|
||
|
||
2026-10-26 已按 SGDJ Swagger 核对页面接口,具体映射见 [页面接口对照](docs/api-contract.md)。首页读取 `modules`,更多栏目使用 `playlet/home-config/list` 真实分页;短剧四种推荐使用 `playlet/operate/list`,题材使用 `playlet/all/list`。专题列表按区块显示,详情直接传 `topicSlug` 或 ID。此前移除的独立剧集入口及 `/series/`、`/video/:id/` 路由继续返回 404。
|
||
|
||
页面 head 由本项目(`JHA-1050`)的 `src/lib/seo-server.ts` 和 `src/lib/seo.ts` 生成:全站名称与首页 TDK 取 `config/info.seoInfo`,栏目 TDK 优先使用 `translation/category/list` 的导航配置,具体专题、剧集使用对应接口信息。兼容字符串形式的 SEO 配置及 `keyworld` 等旧字段。`/navigation/` 使用 `seoNavInfo`。
|
||
|
||
canonical 的域名优先取 `config.foreverUrlAddr`,其次取 `newAddressInfo.foreverUrlAddr`,只使用配置中的永久域名,不回退到请求 Host、API 或 CDN 域名。配置沿用 60 秒公共缓存,成功取得有效永久域名后,将公开 SEO 信息保存到 `.next/cache/seo/`,按 API 地址隔离;接口超时、报错、配置或永久域名暂缺时沿用最近一次有效值,服务重启也可读取已有快照。快照不包含 SDK 配置、账号或凭证;部署时需保留该缓存目录,完全没有有效配置/快照时不输出猜测的 canonical 和站点 JSON-LD。后台更新为有效永久域名后,缓存过期后的新页面请求采用新配置;同一浏览器文档沿用服务端给出的 canonical、site_name 和发布者身份。
|
||
|
||
canonical 不带查询参数,分页各自生成规范地址和有边界的 `prev/next`,第 1 页使用栏目地址。搜索、账户、充值和工具页按参考项目输出 `noindex`。服务端将 TDK、robots、Open Graph、Twitter 和图标信息直接写入 `<head>`,每次页面请求都返回对应页面的完整 head。本次未新增统计脚本。
|
||
|
||
`/robots.txt` 由 `src/app/robots.ts` 提供纯文本响应,不依赖账户会话或请求 Host。生产环境(`APP_ENV=production`)允许页面和 JS/CSS、图片、播放资源抓取,仅限制 `/api/` 和 `/healthz`;开发、测试环境禁止全站抓取。搜索、账户、充值、工具和重复页面继续使用页面级 `noindex`,保留爬虫读取这些标记的权限。生产环境声明 `Sitemap: https://永久域名/sitemap.xml`,域名与 canonical 共用配置及最近一次有效快照;完全没有有效域名时仅省略 Sitemap 行,抓取规则继续返回。开发、测试环境不声明 Sitemap。本项只添加声明,XML 文件仍由后续 sitemap 服务提供。
|
||
|
||
公开正文与 SEO 使用同一次服务端取数:短剧、三个榜单、专题列表/详情、题材/标签、首页更多栏目、短剧详情、播放页及各自分页直接输出当前页的标题、简介、内容卡片、集数和已有评论,分页与免费集数保留原生链接。搜索结果也直接输出首屏正文,继续保持 `noindex`。详情/播放和栏目模板在服务端与站内导航间共用,沿用现有 HTML 样式。
|
||
|
||
请求与渲染方式:服务端先完成当前页面取数,再返回页头和完整正文;福利应用、回家的路、反馈、个人中心、收藏/历史/点赞/已购、设置及充值/VIP 页面也直接输出内容。链接和搜索使用浏览器原生页面请求,旧页面保留到新响应到达,不清空正文或插入页面加载占位。浏览器复用首屏快照绑定轮播、播放、表单和支付等交互,不再重复获取整页数据;提交成功后重新请求当前页面。
|
||
|
||
公共配置、导航、分类、页脚和标签选项使用 60 秒进程内缓存,首页使用 30 秒;同一上游地址及查询参数的并发请求合并,返回独立数据副本,缓存有数量上限。配置及首页刷新失败可沿用已成功取得的数据;错误和无效响应不写入缓存。账户、余额、套餐及付费观看权限不使用公共缓存,HTML 继续保持私有响应。可用 `PUBLIC_DATA_CACHE_TTL_MS` 缩短公共缓存时间,设为 `0` 可关闭;多实例各自维护缓存。
|
||
|
||
详情、章节、选集、评论和推荐按 URL 中的 ID 并行请求。浏览器复用首屏中的公开配置和页脚数据,下载与广告交互不再等待重复取数;SDK 配置仍单独读取,不写入 HTML。支持浏览器前进后退缓存时,公开页保留 DOM、滚动位置和交互,并在后台恢复当前会话和页面埋点;账户、播放及打开弹窗的页面仍重新请求,防止恢复已处置的支付/权限状态。
|
||
|
||
有效业务页面在 Proxy 中恢复账号或建立游客会话,将同一身份的 Cookie 传给本次服务端渲染并写回浏览器。客户端复用已准备的用户信息,避免首屏再次初始化游客。正式账号过期显示登录入口,接口异常直接返回重试状态;账户页面保持 `private, no-store` 和 `noindex`。首屏账号、账户列表、套餐、流水和播放器数据均按字段白名单传递,未授权付费视频源、服务端账号凭证及 SDK 私密配置不写入 HTML;点击金币集数仍读取实时权限、价格并由用户确认解锁。当前 Next.js 动态参数在路由入口只解码一次,中文和含空格、斜杠、百分号的搜索词与浏览器保持一致。
|
||
|
||
JSON-LD 参考 `huangguo.com` 的品牌节点及参考项目 `seoser/jsonld.go`:收录页输出 `Organization`、`WebSite` 和对应 `WebPage` / `CollectionPage`,列表输出实际内容的 `ItemList`,详情使用 `TVSeries`,播放页关联 `TVEpisode` 与具备真实封面、发布时间的 `VideoObject`。关于页使用 `AboutPage`,常见问题直接从页面可见问答生成 `FAQPage`。URL、站名与分页序号沿用现有动态 SEO 配置;不使用参考站的品牌、域名、伪造日期或评分。每份页面响应包含本页的原生 JSON-LD script,不收录页不输出旧页面的数据。测试覆盖脚本安全转义和重复节点清理。
|
||
|
||
- 文档:https://sgdj-h5.80sygtm.com/swagger/index.html
|
||
- `UPSTREAM_API_URL`:开发、测试环境为 `http://172.104.181.99:26792`,生产环境为 `http://139.162.49.252:21040`;兼容旧 `API_BASE_URL`。
|
||
- `API_CHANNEL`:可选游客、登录和绑定的默认渠道码;已有首次渠道时优先保留。
|
||
- 项目编号:`JHA-1050`。接口读取与业务处理位于 `src/lib/api/server.ts`、`src/lib/client/config.ts`、`src/lib/client/channel.ts`、`src/lib/client/auth.ts` 和 `src/lib/client/account.ts`。
|
||
|
||
浏览器与服务端业务接口统一使用 `/api/app/*` 前缀。请求进入 Next.js 时,由 `src/app/api/app/[...path]/route.ts` 按白名单代理到配置上游的同名路径,并负责参数处理及 HttpOnly、SameSite 会话 Cookie;旧 `/api/*` 入口保留兼容。首页由服务端渲染,公开首页数据缓存 30 秒。公共接口不携带用户 token;Next.js 代理不向前端脚本返回 token,也不将 token 或密码写入浏览器本地存储。
|
||
|
||
部署时需核对 `/api/app/*` 的实际处理服务:若 Nginx 将该路径直接转发给 Go,请求会跳过 Next.js 的参数处理、Cookie 与 Authorization 转换及 token 过滤,登录、绑定、退出和受保护接口需按 Go 的鉴权约定单独验证。若该路径由 Next.js 处理,`UPSTREAM_API_URL` 应指向可独立访问的 Go 上游,避免请求再次回到本站。
|
||
|
||
若显式配置为 `current`,则使用当前请求域名(默认配置不使用此模式)。该模式依赖 Nginx 将上游的 `/api/app/*` 及 `/h5/*`、`/enkey`、`/newenkey` 等后端媒体路径转发到 Go,本站其他页面由 Next.js 处理;浏览器业务请求也使用 `/api/app/*`,需满足上一段的后端鉴权约定。上游请求若被再次转回 Next.js,会立即返回 `502` 和转发配置提示,避免递归请求。修改接口模式后重启服务即可,无须将当前域名写入前端构建产物。
|
||
|
||
POST 请求按参考项目转发,不在 Next.js 代理中比较 `Origin`、`Host` 或 `Sec-Fetch-Site`。CDN、反向代理修改内部域名或协议时,不再因来源不匹配提前返回 403;上游返回的鉴权及业务错误继续传回。接口白名单、请求方法限制、会话 Cookie 和代理循环检测继续生效。
|
||
|
||
请求头按 `jha-1045-hgdjnet-web` 的 `common/request.js` 与 `useragent/build.go` 对齐:浏览器业务请求使用 `Accept: application/json`、`credentials: include`,SDK 就绪后由其原始字段生成 `X-User-Agent`;JSON 设置 Content-Type,上传表单由运行时生成 boundary。Next.js 的 API、SSR 和媒体代理共用 `src/lib/api/client-headers.ts`,透传原生 User-Agent,优先保留客户端已有 X-User-Agent,缺失时解析 UA 中的设备、系统和浏览器信息,并读取 `sid` / `_s_token_` Cookie 补充 SID。移除固定的 `jgdj-web/1.0` 和 `server-public` 设备头;设备 ID 仍通过业务参数和 SDK 原始字段传递。
|
||
|
||
有效客户端 IP 优先取 X-Real-IP,其次取 X-Forwarded-For 首个地址,上游只发送 X-Real-IP。公开接口仍不附带账号凭证,私有接口使用当前会话 Authorization;签名媒体和密钥使用 `Accept: */*`,不转发业务 Cookie 或 Authorization。合法 SDK 公共字段保持原值,短剧服务端事件上下文继续按业务路由校验后透传。共享缓存只保存公开响应数据,不保存访客请求头。
|
||
|
||
## 游客会话
|
||
|
||
站点只保留游客会话:`src/lib/client/api.ts` 的 `bootstrapGuestSession` 在进入站点时调用 `auth/guest`(已有凭证时先软读取 `auth/getInfo`),`src/lib/api/page-session.ts` 与 `src/lib/seo-server.ts` 在服务端读取会话 Cookie。登录、注册、找回密码、资料修改、个人中心、金币充值、意见反馈与评论均已移除。
|
||
|
||
## 已接入
|
||
|
||
- 首页轮播、公告、后台配置栏目、广告、题材及视频区块。
|
||
- 短剧运营分类、剧集视频列表、榜单、专题、题材/标签、分页和更多列表。
|
||
- 搜索、搜索联想、热搜、猜你喜欢;搜索记录单独保存在当前浏览器。
|
||
- 短剧详情、章节列表、章节权限和实际播放地址;HLS 播放、媒体代理和播放上报。
|
||
- 游客会话;点赞、收藏、分享。
|
||
- 需付费的剧集不在网页解锁:播放区显示“本集请在 黄果短剧 App 观看”并提供 App 下载按钮,选集中以锁图标标出。
|
||
- 详情页与播放页底部“推荐视频”读取 `playlet/guess-like/list`(12 条,排除当前剧)。
|
||
- 应用列表、下载配置、固定平台说明页。全站唯一对外联系方式为商务合作 Telegram `@jbd125`(`src/lib/contact.ts`)。
|
||
|
||
首页“推荐 / 最新 / 热门 / 随机”筛选项保持移除。栏目排序、列表数据、总数与观看权限由接口决定,失败时显示错误或空状态。
|
||
|
||
首页动态栏目沿用 HTML 的模块间距(桌面 48px、移动端 32px)与卡片网格,首个栏目不再复用原稿图文混排区域的选择器。首页封面统一为 3:4,图片居中裁切,避免接口方图、竖图混排时卡片高度不齐,并在图片解密加载前预留封面高度。
|
||
|
||
全站以原始 HTML 设计稿的页面结构和样式为基准:短剧、剧集、专题、题材/标签、更多列表和搜索页沿用原稿网格、标题、筛选栏和封面框;排行榜使用原稿数字角标、五列布局及窄屏断点,排名按接口分页顺序连续计算,榜单 tab 保持独立路由。搜索关键词只在已转义的标题中高亮。
|
||
|
||
专题列表与专题详情按 HTML 直接展示卡片和分页,不增加“最新专题 / 热门专题”或“编辑推荐 / 最新 / 最热”等分类选项。
|
||
|
||
搜索结果由本项目(`JHA-1050`)的 `src/lib/client/search.ts` 和 `src/lib/seo-server.ts` 实现:服务端准备会话后调用 `playlet/search`,传入 `keyword/pageNum/pageSize`,默认每页 25 条;展示接口总数、关键词和实际 `pageSize`,剧名优先使用 `playName`,不再本地筛选接口结果。无结果优先展示 `everyoneWatching`,缺少推荐时请求 `playlet/operate/list?type=2`;推荐不计入搜索数量。`/search/` 可直接进入,空关键词跳过搜索接口,翻页使用 `/search/{关键词}/page/{页码}/`,搜索框、热搜、搜索历史和联想词入口均直接生成关键词路径,不通过重定向转换。标题高亮沿用 HTML 的 NFKC、大小写和空白处理;无结果/失败状态使用原稿插图,接口错误保留重试入口,不伪装为零结果。
|
||
|
||
|
||
“关于我们、用户协议、隐私政策、常见问题、2257、DMCA”六页固定使用 `src/lib/info-pages.ts` 中的原 HTML 文案与结构,不再请求富文本接口,底部入口也保持固定。每次访问均由服务端输出正文、标题和对应样式标记,无须等待首页或富文本接口。保留分节间距、折叠问答、编号段落和 `mailto:` 邮箱链接,反馈链接直接使用 `/info/feedback/`。
|
||
|
||
“关于我们”的“联系我们”沿用 HTML 分节和段落样式,只展示唯一的商务合作 Telegram `@jbd125`,不再读取后台邮箱、QQ、X 或交流群配置。
|
||
|
||
|
||
详情页使用全宽电影海报式横幅:右侧约 48% 为清晰海报并向左渐隐,左侧为分类标签、超大标题、“·”分隔的集数/热度/收藏/点赞、#标签、可展开简介与操作(立即观看、下载 App、点赞/收藏/分享)。播放页在播放器左右两侧悬浮上一集/下一集圆形按钮,播放器下方为描边大号集数标题、胶片式横向选集条和“推荐视频”区(`src/app/hg-media.css`)。卡片为 9:16 竖版封面,右上角“1/N”计数或“单集”,左下角方块角标,标题下方点线分隔与热度/题材。
|
||
|
||
|
||
|
||
|
||
|
||
App 下载入口(顶部、悬浮图标、首页底部、详情页和 `/navigation/`)统一校验配置。配置返回前隐藏入口;没有有效的 HTTP(S) 下载链接时不展示。电脑端直接点击链接下载,已移除悬浮二维码面板和触发脚本;移动端保留点击下载弹窗,二维码只在实际点击时生成。
|
||
|
||
图片解密由本项目(`JHA-1050`)的 `src/lib/client/images.ts` 实现:使用相同密钥对前 100 字节做 XOR,识别 JPEG、PNG、GIF、WebP,并兼容未加密图片。封面、轮播、题材、广告、应用图标、用户/评论头像和视频 poster 统一处理;接口原图放在 `data-src` / `data-poster`,完成解密后展示 Blob URL。本地素材和下载二维码保持直接加载。移动端并发 2、桌面端并发 4,首屏主图和弹窗优先;共享请求与缓存、视口懒加载、12 秒超时及占位图回退,页面离开时释放 Blob URL。
|
||
|
||
播放逻辑由本项目(`JHA-1050`)的 `src/lib/client/player.ts`、`src/lib/client/playback.ts` 和 `src/lib/api/media.ts` 实现:绝对地址直接播放,相对地址走 `/h5/m3u8/`;Safari 优先原生 HLS,其余浏览器优先 hls.js,无 MSE 时再尝试原生 HLS。保留移动端内联属性,HLS 致命错误最多恢复 3 次,重试会重建播放器;切换预告、正片或离开路由时销毁旧实例。锁定章节可看配置的免费预告,正片按 `canWatch` 和参考项目的旧字段兼容规则判定;金币解锁需用户确认,成功后重新获取账户与章节,预告不提交正片观看上报。上一集/下一集使用接口的真实章节 ID。
|
||
|
||
H.264/AVC 与 H.265/HEVC 使用 `hls.js` 1.7.3 完整版解析,保留 HEVC MPEG-2 TS 支持(参考项目为 1.6.13 完整版)。多编码清单按设备能力保留 H.264、`hvc1`、`hev1`,不强制改写编码或限制为 H.264。编码不兼容时在 hls.js 与原生 HLS 之间各尝试一次,保留签名与播放位置;普通网络/密钥错误仍走原有恢复逻辑。无可用解码方式时显示编码不支持提示。H.265 的实际解码依赖浏览器、系统及设备能力,前端不提供转码或软件解码器,参见 [hls.js 官方说明](https://github.com/video-dev/hls.js/blob/v1.7.3/README.md)。
|
||
|
||
媒体和 `/enkey`、`/newenkey` 使用 Route Handler 读取运行时 `UPSTREAM_API_URL`,保留完整签名查询参数,不转发应用登录 Cookie 或 Authorization。密钥上游先请求无尾斜杠路径,兼容不同服务的尾斜杠规则:只允许同一域名、同一密钥路径增加或删除尾斜杠的一次跳转,签名查询、IP 和 UA 保持不变,返回值必须是 16 字节 AES 密钥。媒体支持 Range,清单和密钥均不缓存,业务错误响应不会作为视频内容返回。
|
||
|
||
## 年龄确认与广告弹窗
|
||
|
||
弹窗逻辑由本项目(`JHA-1050`)的 `src/lib/client/startup.ts` 实现,保留本项目的弹窗样式。
|
||
|
||
- 年龄确认使用会话记录 `hg_age_ok`,当前标签页会话首次进入时展示;确认后刷新、切换业务页面不再重复展示。存储不可用时仍可正常确认并继续;退出行为与参考项目一致,打开 Google。
|
||
- 大图广告使用配置接口 `advitise.dialogList`,按接口顺序逐条展示;先通过年龄确认,再显示已加载完成的广告,配置晚于确认返回时也能继续展示。
|
||
- 点击关闭或按 Esc 进入下一条;点击广告在新标签页打开配置链接。全部关闭后,同一会话内不再展示相同广告队列;刷新时从当前广告恢复。广告队列签名优先使用 `advertiseCode`,队列变化后重新展示。
|
||
- 图片加载失败、解码失败或 12 秒超时会跳过当前广告;全部失败时不记为已完成,后续页面可以重试。
|
||
- 大图广告结束后展示 `advitise.nineGridList` 宫格弹窗,保留 HTML 的四列图标、名称、间距、模糊遮罩和底部关闭按钮;没有大图广告时,年龄确认后直接展示宫格。按配置顺序渲染,不补齐模拟数据,图片复用解密逻辑,失败项不展示;没有有效图片时不弹出。点击应用打开配置链接并关闭弹窗,关闭按钮和 Esc 均可关闭;`gridAdDismissed` 独立记录当前宫格配置,同一会话不重复展示,配置变化后可重新展示。
|
||
- 宫格弹窗最大高度为视口的 80%,预留底部关闭按钮空间,超出的广告在宫格区域内纵向滚动;每次展示从顶部开始。
|
||
|
||
`/navigation/` 保持独立入口页,不加载年龄确认和广告队列。
|
||
|
||
## 验证
|
||
|
||
`npm test` 使用独立本地模拟上游和生产构建验证代理与 SSR,不写入真实业务数据。
|
||
|
||
启动服务后可执行真实接口检查:
|
||
|
||
```sh
|
||
PREVIEW_URL=http://127.0.0.1:21030 npm run smoke:api
|
||
```
|
||
|
||
若游客登录服务暂时超时,可仅验证公开内容:
|
||
|
||
```sh
|
||
SMOKE_PUBLIC_ONLY=1 PREVIEW_URL=http://127.0.0.1:21030 npm run smoke:api
|
||
```
|
||
|
||
2026-10-21 的类型检查、生产构建和全部 258 项测试通过;真实接口检查通过游客会话、公开与个人只读接口及 29 个页面。页面检查确认账户、充值、搜索和工具页的首个响应包含完整正文。真实支付、付费解锁及浏览器视觉验收不在此次验证范围内,详情见 [验证记录](docs/verification.md)。
|
||
|
||
## 目录
|
||
|
||
- `src/app/api/app/[...path]/route.ts`:业务代理、会话 Cookie、白名单和上游错误处理。
|
||
- `src/app/[...path]/page.tsx` 与 `src/lib/routing.ts`:页面路径入口、校验与解析。
|
||
- `src/app/navigation/page.tsx`:独立入口导航页,服务端读取线路、渠道和下载配置。
|
||
- `src/lib/api/`:接口类型、响应兼容和服务端请求。
|
||
- `src/lib/seo-server.ts`:服务端页面取数、正文模板和 SEO。
|
||
- `src/lib/api/page-session.ts`:页面会话准备及同一次请求的 Cookie 传递。
|
||
- `src/lib/api/public-cache.ts`:有上限的公共数据缓存、过期刷新及并发合并。
|
||
- `src/lib/site-shared-data.ts`:首屏公开配置和页脚数据白名单。
|
||
- `src/lib/client/`:共享页面模板及轮播、播放、登录、支付、下载等浏览器交互。
|
||
- `public/scripts/site-runtime.js`:原稿布局、移动端菜单和播放器控件。
|
||
- `public/assets/`:本地品牌、图标和空状态素材。
|
||
- `tests/api-integration.test.mjs`:SSR、鉴权隔离和代理边界测试。
|
||
- `tests/routing.test.mjs`:路径解析、搜索关键词编码、hash 地址不转换与原生页面导航测试。
|
||
- `src/lib/client/startup.ts` 与 `tests/startup.test.mjs`:年龄确认、广告队列和会话进度,以及控制器与浏览器事件适配测试。
|