@@ -0,0 +1,14 @@
|
||||
# Legacy development-only .env.local example. Prefer config/development.local.env.
|
||||
# Development uses this configured backend; browsers call the current site's /api/*.
|
||||
# Test uses this same backend; production uses http://172.104.56.28:26125.
|
||||
UPSTREAM_API_URL=http://172.104.181.99:21022
|
||||
# API_BASE_URL remains supported for existing local overrides.
|
||||
# Optional channel code passed when creating a guest account.
|
||||
API_CHANNEL=
|
||||
# Public configuration/navigation: 60s; homepage: 30s. Optional cap in milliseconds; 0 disables.
|
||||
# PUBLIC_DATA_CACHE_TTL_MS=60000
|
||||
|
||||
# Analytics application ID. Must match this site's reportSdkConfig and upstream app ID.
|
||||
NEXT_PUBLIC_TRACKER_APP_ID=1047
|
||||
# Optional same-project encrypted config fallback when config/info omits reportSdkConfig.
|
||||
NEXT_PUBLIC_TRACKER_ENCRYPTED_CONFIG=
|
||||
@@ -0,0 +1,17 @@
|
||||
node_modules/
|
||||
.next/
|
||||
.next-dev/
|
||||
.next-test/
|
||||
out/
|
||||
dist/
|
||||
dist.zip
|
||||
dist.zip.sha256
|
||||
web_91dj_h5.tar.gz
|
||||
web_91dj_h5.tar.gz.sha256
|
||||
web_91dj_h5.md5
|
||||
*.tsbuildinfo
|
||||
.DS_Store
|
||||
.env*
|
||||
!.env.example
|
||||
config/*.local.env
|
||||
.openai/.local/
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"project_id": "appgprj_6ad2273b8598819198e0c9f1959d5cfa",
|
||||
"static": {
|
||||
"directory": "out"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<!-- BEGIN:nextjs-agent-rules -->
|
||||
|
||||
## This is NOT the Next.js you know
|
||||
|
||||
This version has breaking changes — APIs, conventions, and file structure may all differ from your training data. Read the relevant guide in `node_modules/next/dist/docs/` (resolved from this file's directory; in monorepos the `next` package may not be visible from the repo root) before writing any code. Heed deprecation notices.
|
||||
|
||||
This block is written and re-added by `next dev` — verify at `node_modules/next/dist/server/lib/generate-agent-files.js`. Removing it from a diff only re-creates the uncommitted change; committing it with your work keeps the tree clean.
|
||||
|
||||
<!-- END:nextjs-agent-rules -->
|
||||
@@ -0,0 +1,281 @@
|
||||
# 91短剧 · Next.js
|
||||
|
||||
Next.js App Router、React、TypeScript。保留 HTML 原稿布局和移动端导航,业务数据通过 Swagger API 动态获取。
|
||||
|
||||
## 运行
|
||||
|
||||
已使用 Node.js **22.17.1** 验证;最低要求 20.9。
|
||||
|
||||
```sh
|
||||
nvm use 22.17.1
|
||||
npm install
|
||||
npm run dev
|
||||
```
|
||||
|
||||
开发、测试和生产启动的默认地址均为 http://localhost:21021,端口或环境变量修改后需要重启 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/*`,Next.js 服务端通过 `UPSTREAM_API_URL` 请求 Go 的 `/api/app/*`。开发、测试环境使用 `http://172.104.181.99:21022`,生产环境使用 `http://172.104.56.28:26125`。上游地址仅由服务端读取。默认应用端口均为 `21021`。公开接口缓存和 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_91dj_h5.tar.gz` 和 `web_91dj_h5.tar.gz.sha256`,上传相同文件及 `up_next_h5.sh`;同时刷新同名 `web_91dj_h5.md5` 供本地兼容校验。包内直接包含 `start.mjs`、`server.js`、`.next-test/`、`public/`、`config/test.env` 和 lockfile。测试包不带本机 `node_modules`,使用 `tar -xzf web_91dj_h5.tar.gz` 解压后,在 Linux 执行 `npm ci --omit=dev --no-audit --no-fund`,再执行 `node start.mjs`;默认端口为 21021。可通过 `DEPLOY_PACKAGE_NAME=自定义名称.tar.gz` 覆盖包名,校验文件随之生成。测试和生产使用同名压缩包,根目录文件对应最后一次执行的环境。
|
||||
|
||||
生产产物为 `web_91dj_h5.tar.gz` 和纯哈希格式的 `web_91dj_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 名使用 `91dj`:
|
||||
|
||||
| 环境 | SSH 目标 | 上传目录 | SCP / SSH 端口 |
|
||||
| --- | --- | --- | --- |
|
||||
| 测试 | `root@172.104.181.99` | `/server/web_91dj/h5` | `22889 / 22889` |
|
||||
| 生产 | `server@34.92.204.175` | `/home/server/web_91dj/h5` | `22889 / 22889` |
|
||||
|
||||
直接执行脚本会使用上述地址;仅打包继续使用 `--package-only`。需要其他目标时可覆盖:
|
||||
|
||||
```sh
|
||||
DEPLOY_HOST=user@test-host DEPLOY_DIR=/srv/91dj/h5 DEPLOY_RUN_DIR=/srv/91dj/h5 bash deploy_test.sh
|
||||
DEPLOY_JUMP_HOST=user@jump-host DEPLOY_JUMP_DIR=/srv/91dj/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_91dj/h5 && bash run.sh"`,由服务器现有的 `run.sh` 负责后续更新。上传目录和执行目录当前均为 `/server/web_91dj/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 程序为 `91dj_next_h5`,在服务器运行该脚本时可通过 `DEPLOY_PROGRAM` 覆盖;使用自定义包名时需设置对应的 `DEPLOY_PACKAGE_NAME`。服务器 `run.sh` 的解包、服务管理及包名规则以该脚本自身实现为准。
|
||||
|
||||
生产脚本将 `web_91dj_h5.tar.gz` 和 `web_91dj_h5.md5` 上传到 `server@34.92.204.175:/home/server/web_91dj/h5/`,SCP 与 SSH 默认端口均为 `22889`。上传成功后执行 `ssh -p 22889 server@34.92.204.175 "cd /home/server/web_91dj/h5 && bash rsupdate.sh"`。服务器现有的 `rsupdate.sh` 负责解包、保留生产私有配置和重启服务;本项目不上传或覆盖该脚本。上传失败不会触发远端更新,SSH 失败或返回失败状态会停止发布。可通过 `DEPLOY_JUMP_HOST`、`DEPLOY_JUMP_DIR`、`DEPLOY_SCP_PORT`、`DEPLOY_SSH_PORT` 覆盖目标,执行目录同步跟随上传目录。
|
||||
|
||||
生产 SSH 的远端命令文本与上述手动命令保持一致:通过路径校验后,`cd` 的目录不额外添加单引号,以兼容按原始命令文本匹配的 SSH 白名单。dry-run 与实际执行共用同一个命令,运行时会打印命令并保留远端日志。远端即使打印“发布成功”,只要此前输出含 `ERROR`(如 `curl` 报 HTTP 401)、`FATAL` 等失败状态,本地仍会报错;HTTP 401 的具体检查地址需核对服务器现有 `rsupdate.sh`,应用自身提供无需登录的 `/healthz`。
|
||||
|
||||
`npm run bundle:test` / `npm run bundle` 只整理已有构建;`npm run bundle:linux -- x64 glibc` 为生产产物准备 Linux 原生依赖。同一仓库的测试、生产脚本共用打包锁,压缩包及 `dist/` 已忽略提交。`/healthz` 是不触发游客登录、禁止缓存和索引的部署探针,不依赖上游接口。
|
||||
|
||||
## 页面路由
|
||||
|
||||
首页使用站点根地址,例如 `http://127.0.0.1:21021`,也支持 `http://127.0.0.1:21021/`;两者都直接显示首页。浏览器把纯域名的请求路径解析为 `/`,不需要跳转。短剧列表单独使用 `/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` 与最新地址字段。未配置或无效的链接不展示,不使用演示账号或首页链接兜底。
|
||||
- 个人中心 `/member/center/`,收藏、历史、点赞、已购和设置分别为 `/member/favorites/`、`/member/history/`、`/member/likes/`、`/member/purchased/`、`/member/settings/`。
|
||||
- 帮助与平台信息 `/info/:slug/`;充值 `/info/deposit/`;VIP `/info/deposit/vip/`。
|
||||
- 所有分页使用独立路径,例如 `/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` 保留;评论排序使用 `?commentSort=1`。切换排序时链接指向第一页,翻页时保留当前条件;第一页链接使用原页面路径,不执行自动跳转或地址替换。
|
||||
|
||||
页面直接根据 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-21 已按当前 91av Swagger 重新核对各页面,具体映射和接口缺项见 [页面接口对照](docs/api-contract.md)。短剧列表使用 `playlet/all/list`;专题采用顶层详情字段;首页未配置 `moreUrl` 的栏目使用首页真实数据兜底。按用户要求移除独立剧集入口及 `/series/`、`/video/:id/` 路由,旧地址直接返回 404。
|
||||
|
||||
页面 head 由本项目(`JHA-1047`)的 `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://91av-wz.80sygtm.com/swagger/index.html
|
||||
- `UPSTREAM_API_URL`:开发、测试环境为 `http://172.104.181.99:21022`,生产环境为 `http://172.104.56.28:26125`;兼容旧 `API_BASE_URL`。
|
||||
- `API_CHANNEL`:可选游客、登录和绑定的默认渠道码;已有首次渠道时优先保留。
|
||||
- 项目编号:`JHA-1047`。接口读取与业务处理位于 `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/*`,由 Next.js 按白名单代理到配置上游的 `/api/app/*`。Nginx 已有的 `/api/app/* → Go` 规则可以保留,其他 `/api/*` 交给 Next.js;不能将整个 `/api/` 都交给 Go。旧 Next.js `/api/app/*` 处理器仍保留供本地兼容访问。首页由服务端渲染,公开首页数据缓存 30 秒。公共接口不携带用户 token;用户接口使用 HttpOnly、SameSite Cookie。token 和密码不会写入浏览器本地存储或返回给前端脚本。
|
||||
|
||||
若显式配置为 `current`,则使用当前请求域名(默认配置不使用此模式)。该模式依赖 Nginx 将 `/api/app/*` 及 `/h5/*`、`/enkey`、`/newenkey` 等后端媒体路径转发到 Go,本站其他页面和 `/api/*` 由 Next.js 处理。上游请求若被再次转回 Next.js,会立即返回 `502` 和转发配置提示,避免递归请求。修改接口模式后重启服务即可,无须将当前域名写入前端构建产物。
|
||||
|
||||
请求处理参考 `jha-1045-hgdjnet-web`:Next.js 接口代理不根据 `Origin`、`Host` 或 `Sec-Fetch-Site` 拦截 POST,避免 CloudFront / Nginx 转发后域名或协议变化导致正常浏览器请求返回 403。浏览器继续请求当前站点的 `/api/*`,代理负责接口白名单、参数处理和会话传递,业务权限由上游校验。
|
||||
|
||||
## 登录与游客绑定
|
||||
|
||||
登录流程由本项目(`JHA-1047`)的 `src/lib/client/auth.ts` 和 `src/app/api/app/[...path]/route.ts` 实现,保留本站登录弹窗。
|
||||
|
||||
- 首次访问自动建立游客会话;已有 Cookie 时先读取 `auth/getInfo` 恢复身份、昵称和余额,接口未返回新 token 时继续使用原 token。
|
||||
- 游客、登录和绑定使用同一持久设备标识,发送 `devID` 和渠道;首次渠道取 `dc/ch/pc`,后续访问保持不变。绑定支持 `inviteCode/invite/code` 邀请参数。服务端按参考项目发送设备头,并透传有效的客户端 IP。
|
||||
- 绑定携带当前游客凭证以保留其数据;前端填写账号、密码和确认密码并同意协议,无需邮箱。上游要求的缺省邮箱由服务端生成为 `账号@91dj.local`;实际已绑定邮箱仍用于找回密码。
|
||||
- 绑定账号为 6–20 位字母或数字,密码为 8–64 位且包含字母和数字;登录兼容旧账号的 4 位密码及绑定允许的较长账号、密码,支持邮箱登录。
|
||||
- 登录失败保留原身份;成功后更新账户并重新读取当前页面。自动游客、登录和退出响应串行处理,旧请求的失效结果不会清除客户端刚登录的身份。
|
||||
- 游客请求明确返回会话失效时重建游客并重试一次;正式账号失效会清理会话并提示登录。登录、注册、找回密码自身的错误不触发游客恢复。
|
||||
- 退出清理会话,并跳过紧接着一次页面初始化的自动游客登录。当前页面保持未登录,主动登录或绑定可恢复使用。
|
||||
- 表单提交期间禁用重复提交;支持显示/隐藏密码,关闭和成功后均清除密码值。仅设备与渠道写本地存储,凭证保留在 HttpOnly Cookie。
|
||||
|
||||
## 已接入
|
||||
|
||||
- 首页轮播、公告、后台配置栏目、广告、题材及视频区块。
|
||||
- 短剧运营分类、剧集视频列表、榜单、专题、题材/标签、分页和更多列表。
|
||||
- 搜索、搜索联想、热搜、猜你喜欢;搜索记录单独保存在当前浏览器。
|
||||
- 短剧详情、章节列表、章节权限和实际播放地址;HLS 播放、媒体代理和播放上报。
|
||||
- 游客会话、登录、注册/游客绑定、找回密码、资料与头像更新。
|
||||
- 个人中心统计、收藏、历史、点赞、已购列表;点赞、收藏、评论、回复、评论点赞。
|
||||
- 金币/VIP 套餐、支付方式、订单创建、余额查询和逐集解锁。
|
||||
- 应用列表、下载配置、固定平台说明页、反馈提交。
|
||||
|
||||
首页“推荐 / 最新 / 热门 / 随机”筛选项保持移除。栏目排序、列表数据、总数与观看权限由接口决定,失败时显示错误或空状态。
|
||||
|
||||
首页动态栏目沿用 HTML 的模块间距(桌面 48px、移动端 32px)与卡片网格,首个栏目不再复用原稿图文混排区域的选择器。首页封面统一为 3:4,图片居中裁切,避免接口方图、竖图混排时卡片高度不齐,并在图片解密加载前预留封面高度。
|
||||
|
||||
全站以 `91短剧-2组 (3).html` 的页面结构和样式为基准:短剧、剧集、专题、题材/标签、更多列表和搜索页沿用原稿网格、标题、筛选栏和封面框;排行榜使用原稿数字角标、五列布局及窄屏断点,排名按接口分页顺序连续计算,榜单 tab 保持独立路由。搜索关键词只在已转义的标题中高亮。
|
||||
|
||||
专题列表与专题详情按 HTML 直接展示卡片和分页,不增加“最新专题 / 热门专题”或“编辑推荐 / 最新 / 最热”等分类选项。
|
||||
|
||||
搜索结果由本项目(`JHA-1047`)的 `src/lib/client/search.ts` 和 `src/lib/seo-server.ts` 实现:服务端准备会话后调用 `playlet/search`,传入 `keyword/pageNum/pageSize`,默认每页 25 条;展示接口总数、关键词和实际 `pageSize`,剧名优先使用 `playName`,不再本地筛选接口结果。无结果优先展示 `everyoneWatching`,缺少推荐时请求 `playlet/hot/list`;推荐不计入搜索数量。`/search/` 可直接进入,空关键词跳过搜索接口,翻页使用 `/search/{关键词}/page/{页码}/`,搜索框、热搜、搜索历史和联想词入口均直接生成关键词路径,不通过重定向转换。标题高亮沿用 HTML 的 NFKC、大小写和空白处理;无结果/失败状态使用原稿插图,接口错误保留重试入口,不伪装为零结果。
|
||||
|
||||
个人中心、收藏、历史、点赞与已购页共用原稿的头像/金币余额卡片、资料入口、书架导航与卡片列表;个人中心默认展示真实观看历史,分页链接进入历史页。设置页恢复头像编辑、昵称保存和账号安全卡片,资料、头像、密码仍通过原有接口保存;登录/绑定弹窗恢复原稿输入框、眼睛图标与按钮层级。页脚与桌面导航采用新版 HTML 的排版。
|
||||
|
||||
“关于我们、用户协议、隐私政策、常见问题、2257、DMCA”六页固定使用 `src/lib/info-pages.ts` 中的原 HTML 文案与结构,不再请求富文本接口,底部入口也保持固定。每次访问均由服务端输出正文、标题和对应样式标记,无须等待首页或富文本接口。保留分节间距、折叠问答、编号段落和 `mailto:` 邮箱链接,反馈链接直接使用 `/info/feedback/`。
|
||||
|
||||
“关于我们”的“联系我们”沿用 HTML 分节和段落样式:商务合作读取 `config.telegram`(兼容 `telegramAdd`),用户反馈读取 `config.tgGroup`,空值再取 `newAddressInfo`;不使用邮箱或在线客服地址代替 TG。服务时间等附加说明读取 `config/footer/info` 的 `footerInfo.contactList`。这些内容随正文一起由服务端输出,未配置的项目不展示。
|
||||
|
||||
反馈页恢复原稿的问题选项、环境/设备面板、描述计数、上传区域和提交区。实际提交通过 `feedback/add`,环境信息随描述保存;图片通过现有 `upload/image` 上传,支持最多 3 张、每张 5MB 以内的 JPG/PNG/WebP,不展示接口尚未支持的视频上传。上传失败不清空表单,提交时阻止重复写入,离开页面后隔离迟到的结果。
|
||||
|
||||
详情页结构按原 HTML 调整:封面与资料卡、热度图标和标签、播放/下载与互动操作栏、三行简介展开/收起、选集横向分段和金币角标、官方链接、评论及嵌套回复。移动端复用原稿的封面、四列操作栏与五列选集布局;无下载配置时只显示播放入口,无推荐数据时不留空侧栏。内容和章节 ID 继续由接口提供,评论分页保持独立路由。
|
||||
|
||||
支付和解锁均由用户在页面确认后发起;创建订单不会直接把本地余额改为“支付成功”,余额通过用户信息接口刷新。
|
||||
|
||||
充值页按 `91短剧-2组 (3).html#info/deposit` 展示余额与金币用途卡片、套餐面板、推荐角标、选中标记、独立支付按钮和交易明细。先选择接口套餐,再点击“立即支付”打开确认弹窗;价格、赠送金币与流水收支读取真实字段,不使用原稿的模拟金额和交易记录。桌面端四列、移动端三列套餐,交易明细继续使用独立分页路由。
|
||||
|
||||
充值确认弹窗按原 HTML 展示订单摘要、赠送/合计金币、金额徽标、支付渠道图标与选中圆点、“去支付”按钮和确认说明。金币数读取 `baseCoin/giftCoin/totalCoin`,价格按接口分单位换算;仅展示套餐配置的支付方式。切换样式按钮会同步实际下单参数,提交期间禁止重复下单。金币和 VIP 下单成功后自动在新标签页打开支付地址;原页保留“打开支付页面”供再次打开同一订单,以及“我已完成支付,刷新余额”。点击去支付时先预留标签页以避免异步弹窗拦截,下单失败或离开页面时关闭等待页,浏览器禁止新窗口时自动在当前页继续支付。创建订单后仍需用户完成真实支付。
|
||||
|
||||
详情和播放页点击金币集数,直接打开原 HTML 样式的解锁弹窗,读取所选章节的最新权限、余额和价格;价格优先使用 `costCoins`,其次 `singleCoins`。确认后提交该集的 `chapterId/playletId`,成功后刷新余额并进入对应播放页;余额不足进入充值,会员章节进入会员套餐。读取期间禁止支付,支付期间禁止重复提交或切换目标;关闭弹窗、切换集数或离开页面会隔离迟到的读取结果。已允许观看的章节仍使用真实播放链接。
|
||||
|
||||
App 下载入口(顶部、悬浮图标、首页底部、详情页和 `/navigation/`)统一校验配置。配置返回前隐藏入口;没有有效的 HTTP(S) 下载链接时不展示。电脑端直接点击链接下载,已移除悬浮二维码面板和触发脚本;移动端保留点击下载弹窗,二维码只在实际点击时生成。
|
||||
|
||||
图片解密由本项目(`JHA-1047`)的 `src/lib/client/images.ts` 实现:使用相同密钥对前 100 字节做 XOR,识别 JPEG、PNG、GIF、WebP,并兼容未加密图片。封面、轮播、题材、广告、应用图标、用户/评论头像和视频 poster 统一处理;接口原图放在 `data-src` / `data-poster`,完成解密后展示 Blob URL。本地素材和下载二维码保持直接加载。移动端并发 2、桌面端并发 4,首屏主图和弹窗优先;共享请求与缓存、视口懒加载、12 秒超时及占位图回退,页面离开时释放 Blob URL。
|
||||
|
||||
播放逻辑由本项目(`JHA-1047`)的 `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-1047`)的 `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:21021 npm run smoke:api
|
||||
```
|
||||
|
||||
若游客登录服务暂时超时,可仅验证公开内容:
|
||||
|
||||
```sh
|
||||
SMOKE_PUBLIC_ONLY=1 PREVIEW_URL=http://127.0.0.1:21021 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`:年龄确认、广告队列和会话进度,以及控制器与浏览器事件适配测试。
|
||||
|
||||
## 埋点
|
||||
|
||||
按照 [Web SDK v1.2.0 文档](https://doc2.adcenteryyds.cc/web/getting-started.html) 、[飞书埋点规范](https://djprsdgo4i4n.jp.larksuite.com/wiki/X8sjwbzs2ib01Ak2TWzj9Y94pFd)(已通过 Google Chrome 读取,包含 2026-10-17 更新)为本项目(`JHA-1047`)接入,官方 SDK 固定保存在 `public/scripts/web-sdk-v1.2.0.js`。应用 ID 默认 `1047`,可通过 `NEXT_PUBLIC_TRACKER_APP_ID` 配置,必须与当前站点密文和上游应用一致;初始化优先读取 `config/info.reportSdkConfig`,可选 `NEXT_PUBLIC_TRACKER_ENCRYPTED_CONFIG` 作为同项目的密文备用配置。缺少配置或 SDK 加载失败时,业务页面继续工作。
|
||||
|
||||
- SDK 自动采集生命周期、网络和会话;关闭自动 PV/点击,由接入层按真实路径上报,避免重复。支持首页、榜单、分页、专题、搜索、账户、信息页和独立导航页;登录、游客恢复和退出后同步 UID,渠道沿用首次归因。
|
||||
- 内容曝光以可见元素为准,关闭的弹窗、被模态弹窗遮挡的页面、后台标签页和轮播副本不计入;关闭弹窗或恢复标签页后重新观察尚未曝光的元素。短剧、专题和普通福利应用均有曝光、点击;内容无父级时 `parent_item_id` 使用 `0`,满足 SDK 的必填非空字符串校验。
|
||||
- 广告保留接口广告代码和广告位字段,上报 `adImpression/adClick`;页面广告、大图弹窗、宫格弹窗按实际展示位置分别去重,避免同一广告出现在宫格时被首页曝光记录吞掉。宫格仅统计滚动区域内可见的项目。两种弹窗另按参考项目协议上报 `advertising` 的 `show/click/close` 行为;只关闭实际曝光过的项目,同一展示只记一次关闭,关闭不算广告点击。福利应用有 `advitiseId` 时使用该广告 ID 上报,没有广告关联时按普通内容统计。
|
||||
- 搜索成功后上报关键词与实际总数,结果点击使用含页码的全局位置;未接推荐引擎的追踪字段保留空串。顶部、侧栏、独立入口页渠道和 App 下载入口记录导航点击。
|
||||
- 短剧使用 `dramaEvent/dramaStatusChange`,记录展示、播放、暂停、快进/快退、完成、分享、切集和成功解锁;独立视频使用 `videoEvent`。终止播放失败分别上报 `ep_failed/video_failed`(播放失败),自动恢复中的中间错误和预告失败不作为正片失败上报。短剧完播同时补报 `ep_play`,进度固定 100%,保留最后一段播放时长。播放时长按实际播放区间累计,排除缓冲和免费预告,进度为百分比,倍速乘以 100。
|
||||
- 所有同源业务 API 请求携带 SDK 原始公共字段 `X-SDK-Common`,保留 SDK 返回的完整公共字段,包括应用、渠道、UID、数值时间戳、会话、设备、指纹和版本等;注册、登录、充值及 VIP 下单都使用同一份字段格式。字段来自 `WebSDK.getCommonFields()`,不重新生成指纹或会话 ID,等待最多 1.5 秒。
|
||||
- 点赞、收藏、评论、购买由上游服务端上报,前端传入 `X-Tracker-Request-Id/X-Tracker-Event-Name/X-DC-Request-Context`,包含完整公共字段和目标剧集上下文。Next.js 代理校验应用 ID、事件对应接口和字段结构后透传;游客重试复用原始快照和请求 ID,不额外从客户端发送业务成功事件。
|
||||
|
||||
SDK 未初始化时最多缓存 1000 条事件,配置到达后按顺序补发;配置接口的首次请求不等待 SDK 加载,避免互相等待,首次 SDK 脚本失败后在取得配置时再尝试加载。SDK 返回字段校验失败、事件不在允许列表、地址不可用或异常时,仅触发脱敏的 `tracker:diagnostic` 事件,不向浏览器控制台输出。自带 SDK 仅移除了三处控制台警告,保留字段校验、HTTPS 地址限制和配置解密失败兜底;网络重试和持久队列由官方 SDK 负责。
|
||||
|
||||
埋点回归使用 SDK 替身及项目自带官方 SDK 的真实字段校验器;浏览器验证使用同一 SDK 的事件地址参数,将签名后的测试记录发送到本机接收端,不向真实统计后台发送测试事件。生产环境还需在广告中心为当前应用的 `reportSdkConfig` 配置业务域名反向代理地址(SDK 的 `batchUrls`),由部署层转发到文档指定的生产批量上报接口,不能让客户端直连生产上报域名。代理需保留请求体、真实来源 IP 和 SDK 签名;目前未变更线上域名或 Nginx 配置。
|
||||
|
||||
“我的点赞”入口位于头像菜单和个人中心书架导航,路由为 `/member/likes/`,分页使用 `/member/likes/page/N/`。服务端通过当前账号请求 `GET /api/app/playlet/like/list?pageNum=N&pageSize=24`,列表不进入公开缓存或搜索索引;空列表显示原 HTML 的空状态和发现入口,接口失败显示重试状态。
|
||||
@@ -0,0 +1,9 @@
|
||||
# Local development. Personal overrides belong in development.local.env.
|
||||
APP_ENV=development
|
||||
PORT=21021
|
||||
# Server-only Go API origin. Browser requests use the current site's /api/*.
|
||||
UPSTREAM_API_URL=http://172.104.181.99:21022
|
||||
API_CHANNEL=
|
||||
PUBLIC_DATA_CACHE_TTL_MS=0
|
||||
NEXT_PUBLIC_TRACKER_APP_ID=1047
|
||||
NEXT_PUBLIC_TRACKER_ENCRYPTED_CONFIG=
|
||||
@@ -0,0 +1,9 @@
|
||||
# Production defaults. Server overrides belong in production.local.env.
|
||||
APP_ENV=production
|
||||
PORT=21021
|
||||
# Server-only Go API origin. Browser requests use the current site's /api/*.
|
||||
UPSTREAM_API_URL=http://172.104.56.28:26125
|
||||
API_CHANNEL=
|
||||
PUBLIC_DATA_CACHE_TTL_MS=60000
|
||||
NEXT_PUBLIC_TRACKER_APP_ID=1047
|
||||
NEXT_PUBLIC_TRACKER_ENCRYPTED_CONFIG=
|
||||
@@ -0,0 +1,9 @@
|
||||
# Test deployment. Server overrides belong in test.local.env.
|
||||
APP_ENV=test
|
||||
PORT=21021
|
||||
# Server-only Go API origin. Browser requests use the current site's /api/*.
|
||||
UPSTREAM_API_URL=http://172.104.181.99:21022
|
||||
API_CHANNEL=
|
||||
PUBLIC_DATA_CACHE_TTL_MS=60000
|
||||
NEXT_PUBLIC_TRACKER_APP_ID=1047
|
||||
NEXT_PUBLIC_TRACKER_ENCRYPTED_CONFIG=
|
||||
@@ -0,0 +1,93 @@
|
||||
#!/bin/bash
|
||||
# Linux 测试服 tar.gz 更新入口;需预先配置 Supervisor 程序并手动调用。
|
||||
set -Eeuo pipefail
|
||||
DIR=$(cd "$(dirname "$0")" && pwd)
|
||||
PROGRAM="${DEPLOY_PROGRAM:-91dj_next_h5}"
|
||||
PACKAGE_NAME="${DEPLOY_PACKAGE_NAME:-web_91dj_h5.tar.gz}"
|
||||
CHECKSUM_NAME="$PACKAGE_NAME.sha256"
|
||||
[[ "$PROGRAM" =~ ^[a-zA-Z0-9_][a-zA-Z0-9_-]*$ ]] || { echo 'DEPLOY_PROGRAM 格式无效' >&2; exit 1; }
|
||||
[[ "$PACKAGE_NAME" =~ ^[a-zA-Z0-9_][a-zA-Z0-9_.-]*\.tar\.gz$ ]] || { echo 'DEPLOY_PACKAGE_NAME 必须是 .tar.gz 文件名' >&2; exit 1; }
|
||||
CURRENT="$DIR/next"
|
||||
LOCK="$DIR/.$PROGRAM.lock"
|
||||
STAGING=''; UPLOAD=''; PREVIOUS=''; FAILED=''
|
||||
MOVED_OLD=0; INSTALLED=0; STOPPED=0; WAS_RUNNING=0
|
||||
fail() { printf '错误:%s\n' "$*" >&2; exit 1; }
|
||||
for tool in node npm sha256sum tar supervisorctl mktemp; do command -v "$tool" >/dev/null || fail "缺少命令 $tool"; done
|
||||
node -e 'const [a,b]=process.versions.node.split(".").map(Number);if(a<20||a===20&&b<9)process.exit(1)' || fail '需要 Node.js >=20.9'
|
||||
mkdir "$LOCK" 2>/dev/null || fail "已有更新进程或残留锁:$LOCK"
|
||||
finish() {
|
||||
local result=$?
|
||||
trap - EXIT INT TERM
|
||||
if [ "$result" != 0 ] && { [ "$STOPPED" = 1 ] || [ "$INSTALLED" = 1 ] || [ "$MOVED_OLD" = 1 ]; }; then
|
||||
printf '更新失败,恢复上一版本。\n' >&2
|
||||
supervisorctl stop "$PROGRAM" >/dev/null 2>&1 || true
|
||||
if [ "$INSTALLED" = 1 ]; then
|
||||
FAILED="$DIR/next.failed.$(date +%Y%m%d%H%M%S).$$"
|
||||
mv "$CURRENT" "$FAILED" || { echo "无法保存失败版本:$CURRENT" >&2; result=1; }
|
||||
fi
|
||||
if [ "$MOVED_OLD" = 1 ]; then
|
||||
mv "$PREVIOUS" "$CURRENT" || { echo "需要手动恢复:$PREVIOUS → $CURRENT" >&2; result=1; }
|
||||
fi
|
||||
if [ "$WAS_RUNNING" = 1 ]; then supervisorctl start "$PROGRAM" || result=1; fi
|
||||
fi
|
||||
if [ -n "$STAGING" ] && [ -d "$STAGING" ]; then rm -rf "$STAGING"; fi
|
||||
if [ -n "$UPLOAD" ] && [ -d "$UPLOAD" ]; then rm -rf "$UPLOAD"; fi
|
||||
rmdir "$LOCK" 2>/dev/null || true
|
||||
exit "$result"
|
||||
}
|
||||
trap finish EXIT
|
||||
trap 'exit 130' INT
|
||||
trap 'exit 143' TERM
|
||||
[ ! -L "$CURRENT" ] && { [ ! -e "$CURRENT" ] || [ -d "$CURRENT" ]; } || fail 'next 必须是普通目录'
|
||||
STATUS=$(supervisorctl status "$PROGRAM" 2>/dev/null || true)
|
||||
[[ "$STATUS" == "$PROGRAM "* ]] && [[ "$STATUS" != *'ERROR'* ]] || fail "请先配置 Supervisor 程序 $PROGRAM,command 指向 node $CURRENT/start.mjs"
|
||||
if [[ "$STATUS" == *'RUNNING'* ]]; then WAS_RUNNING=1; fi
|
||||
[ -s "$DIR/$PACKAGE_NAME" ] && [ -s "$DIR/$CHECKSUM_NAME" ] || fail "缺少 $PACKAGE_NAME 或 SHA-256"
|
||||
# Freeze the uploaded pair before validation/extraction; concurrent uploads cannot replace this copy.
|
||||
UPLOAD=$(mktemp -d "$DIR/.91dj-upload.XXXXXX")
|
||||
cp "$DIR/$PACKAGE_NAME" "$DIR/$CHECKSUM_NAME" "$UPLOAD/"
|
||||
(cd "$UPLOAD" && sha256sum -c "$CHECKSUM_NAME") || fail 'SHA-256 校验失败'
|
||||
LC_ALL=C tar -tzf "$UPLOAD/$PACKAGE_NAME" > "$UPLOAD/members" || fail 'tar.gz 压缩包损坏'
|
||||
while IFS= read -r entry; do
|
||||
entry="${entry#./}"
|
||||
case "$entry" in
|
||||
/*|../*|*/../*|*/..|node_modules|node_modules/*|*/node_modules/*|*\\*) fail "非法 tar.gz 路径:$entry" ;;
|
||||
esac
|
||||
done < "$UPLOAD/members"
|
||||
# Reject links and special entries before extraction, including hard links to outside paths.
|
||||
LC_ALL=C tar -tvzf "$UPLOAD/$PACKAGE_NAME" > "$UPLOAD/types" || fail '无法检查 tar.gz 文件类型'
|
||||
while IFS= read -r entry; do
|
||||
case "$entry" in
|
||||
-?*|d?*) ;;
|
||||
*) fail '测试包只允许普通文件和目录,不允许链接或特殊文件' ;;
|
||||
esac
|
||||
done < "$UPLOAD/types"
|
||||
STAGING=$(mktemp -d "$DIR/.91dj-release.XXXXXX")
|
||||
tar -xzf "$UPLOAD/$PACKAGE_NAME" -C "$STAGING" --no-same-owner --no-same-permissions
|
||||
[ -z "$(find "$STAGING" -type l -print -quit)" ] || fail '测试包不允许符号链接'
|
||||
for file in start.mjs server.js .next-test/BUILD_ID config/test.env package.json package-lock.json scripts/next-env.mjs scripts/check-health.mjs; do
|
||||
[ -s "$STAGING/$file" ] || fail "包内缺少 $file"
|
||||
done
|
||||
[ -d "$STAGING/.next-test/static" ] && [ -d "$STAGING/public" ] || fail '包内缺少静态资源'
|
||||
# Install for the server's real OS/CPU, rather than shipping this Mac's node_modules.
|
||||
(cd "$STAGING" && npm ci --omit=dev --no-audit --no-fund)
|
||||
if [ -f "$CURRENT/config/test.local.env" ] && [ ! -L "$CURRENT/config/test.local.env" ]; then
|
||||
cp "$CURRENT/config/test.local.env" "$STAGING/config/test.local.env"
|
||||
chmod 600 "$STAGING/config/test.local.env"
|
||||
fi
|
||||
if [ -d "$CURRENT/.next-test/cache/seo" ] && [ ! -L "$CURRENT/.next-test/cache/seo" ]; then
|
||||
mkdir -p "$STAGING/.next-test/cache"
|
||||
cp -R "$CURRENT/.next-test/cache/seo" "$STAGING/.next-test/cache/seo"
|
||||
fi
|
||||
APP_PORT=$(cd "$STAGING" && node --input-type=module -e 'import{nextCommand}from"./scripts/next-env.mjs";console.log(nextCommand("test","start",[],{root:process.cwd()}).env.PORT)')
|
||||
# Stop only after checksum, installation and configuration validation have succeeded.
|
||||
if [ "$WAS_RUNNING" = 1 ]; then supervisorctl stop "$PROGRAM"; STOPPED=1; fi
|
||||
if [ -d "$CURRENT" ]; then
|
||||
PREVIOUS="$DIR/next.prev.$(date +%Y%m%d%H%M%S).$$"
|
||||
mv "$CURRENT" "$PREVIOUS"; MOVED_OLD=1
|
||||
fi
|
||||
mv "$STAGING" "$CURRENT"; STAGING=''; INSTALLED=1
|
||||
supervisorctl start "$PROGRAM"
|
||||
node "$CURRENT/scripts/check-health.mjs" "http://127.0.0.1:$APP_PORT" test
|
||||
supervisorctl status "$PROGRAM"
|
||||
printf '测试服更新完成:%s;上一版本:%s\n' "$CURRENT" "${PREVIOUS:-无}"
|
||||
@@ -0,0 +1,91 @@
|
||||
#!/bin/bash
|
||||
# 91短剧正式服:构建 → 测试 → Linux standalone + MD5 → 可选触发 rsupdate.sh。
|
||||
set -euo pipefail
|
||||
SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd)
|
||||
cd "$SCRIPT_DIR"
|
||||
source "$SCRIPT_DIR/scripts/deploy-common.sh"
|
||||
JUMP_HOST="${DEPLOY_JUMP_HOST:-server@34.92.204.175}"
|
||||
# 压缩包上传完成后,在同一目录执行服务器现有的 rsupdate.sh。
|
||||
JUMP_DIR="${DEPLOY_JUMP_DIR:-/home/server/web_91dj/h5}"
|
||||
# valid_path 限制目录为安全字符;命令文本与手动执行一致,兼容 SSH 命令白名单。
|
||||
REMOTE_UPDATE_COMMAND="cd ${JUMP_DIR%/} && bash rsupdate.sh"
|
||||
SCP_PORT="${DEPLOY_SCP_PORT:-22889}"
|
||||
SSH_PORT="${DEPLOY_SSH_PORT:-22889}"
|
||||
TARGET_ARCH="${DEPLOY_TARGET_ARCH:-x64}"
|
||||
TARGET_LIBC="${DEPLOY_TARGET_LIBC:-glibc}"
|
||||
PACKAGE_NAME="${DEPLOY_PACKAGE_NAME:-web_91dj_h5.tar.gz}"
|
||||
MD5_NAME="${PACKAGE_NAME%.tar.gz}.md5"
|
||||
RELEASE="$SCRIPT_DIR/dist/production-linux-$TARGET_ARCH-$TARGET_LIBC"
|
||||
PACKAGE_ONLY=0
|
||||
for argument in "$@"; do
|
||||
case "$argument" in
|
||||
--dry-run) DEPLOY_DRY_RUN=1 ;;
|
||||
--package-only) PACKAGE_ONLY=1 ;;
|
||||
-h|--help)
|
||||
printf '用法:%s [--dry-run] [--package-only]\n' "$0"
|
||||
printf '仅打包:--package-only。仅预览:--dry-run(不构建、不联网)。\n'
|
||||
printf '上传目标:%s:%s/;上传成功后在该目录执行 bash rsupdate.sh。\n' "$JUMP_HOST" "${JUMP_DIR%/}"
|
||||
printf '可覆盖 DEPLOY_JUMP_HOST、DEPLOY_JUMP_DIR、DEPLOY_SCP_PORT(%s)、DEPLOY_SSH_PORT(%s)、DEPLOY_PACKAGE_NAME(%s)、DEPLOY_TARGET_ARCH(x64/arm64)、DEPLOY_TARGET_LIBC(glibc/musl)。\n' "$SCP_PORT" "$SSH_PORT" "$PACKAGE_NAME"
|
||||
exit 0 ;;
|
||||
*) fail "不支持的参数 $argument" ;;
|
||||
esac
|
||||
done
|
||||
case "$TARGET_ARCH" in x64|arm64) ;; *) fail 'DEPLOY_TARGET_ARCH 仅支持 x64 或 arm64' ;; esac
|
||||
case "$TARGET_LIBC" in glibc|musl) ;; *) fail 'DEPLOY_TARGET_LIBC 仅支持 glibc 或 musl' ;; esac
|
||||
[[ "$PACKAGE_NAME" =~ ^[a-zA-Z0-9_][a-zA-Z0-9_.-]*\.tar\.gz$ ]] || fail 'DEPLOY_PACKAGE_NAME 必须是 .tar.gz 文件名'
|
||||
if [ "$PACKAGE_ONLY" = 0 ] && [ "${DEPLOY_DRY_RUN:-0}" != 1 ]; then
|
||||
valid_host "$JUMP_HOST" DEPLOY_JUMP_HOST; valid_path "$JUMP_DIR" DEPLOY_JUMP_DIR
|
||||
valid_port "$SCP_PORT" DEPLOY_SCP_PORT; valid_port "$SSH_PORT" DEPLOY_SSH_PORT
|
||||
need ssh; need scp; need tee
|
||||
fi
|
||||
check_local production
|
||||
if [ "${DEPLOY_DRY_RUN:-0}" = 1 ]; then
|
||||
printf 'DRY RUN:正式环境;Linux %s/%s;不构建、不连接服务器。\n' "$TARGET_ARCH" "$TARGET_LIBC"; public_profile production
|
||||
printf ' npm run check → npm run bundle → npm run bundle:linux -- %s %s\n' "$TARGET_ARCH" "$TARGET_LIBC"
|
||||
printf ' 保留 standalone 裁剪依赖,仅安装 lockfile 中目标 Linux 的 Sharp/libvips。\n'
|
||||
printf ' 生成 %s + %s(纯 MD5 hash)。\n' "$PACKAGE_NAME" "$MD5_NAME"
|
||||
if [ "$PACKAGE_ONLY" = 1 ]; then printf ' 仅生成本地包。\n'
|
||||
else
|
||||
printf ' scp -P %s %s %s %s:%s/\n' "$SCP_PORT" "$PACKAGE_NAME" "$MD5_NAME" "$JUMP_HOST" "${JUMP_DIR%/}"
|
||||
printf ' ssh -p %s %s "%s"\n' "$SSH_PORT" "$JUMP_HOST" "$REMOTE_UPDATE_COMMAND"
|
||||
fi
|
||||
exit 0
|
||||
fi
|
||||
need tar
|
||||
command -v md5sum >/dev/null || command -v md5 >/dev/null || fail '缺少 md5sum 或 macOS md5'
|
||||
lock_build
|
||||
step '构建正式环境并运行回归测试'
|
||||
npm run check
|
||||
step '整理正式环境 standalone 和目标 Linux 原生依赖'
|
||||
npm run bundle
|
||||
npm run bundle:linux -- "$TARGET_ARCH" "$TARGET_LIBC"
|
||||
for file in start.mjs server.js .next/BUILD_ID config/production.env runtime-target.json; do
|
||||
[ -s "$RELEASE/$file" ] || fail "正式包缺少 $file"
|
||||
done
|
||||
for directory in node_modules .next/static public; do [ -d "$RELEASE/$directory" ] || fail "正式包缺少 $directory"; done
|
||||
step "生成 $PACKAGE_NAME"
|
||||
TEMP_PACKAGE=$(mktemp "$SCRIPT_DIR/dist/.production-archive.XXXXXX")
|
||||
if ! COPYFILE_DISABLE=1 COPY_EXTENDED_ATTRIBUTES_DISABLE=1 tar --no-xattrs \
|
||||
--exclude='._*' --exclude='.DS_Store' --exclude='.env*' --exclude='*.local.env' --exclude='*.log' \
|
||||
-czf "$TEMP_PACKAGE" -C "$RELEASE" .; then
|
||||
rm -f "$TEMP_PACKAGE"; fail '压缩失败'
|
||||
fi
|
||||
tar -tzf "$TEMP_PACKAGE" >/dev/null
|
||||
mv -f "$TEMP_PACKAGE" "$PACKAGE_NAME"
|
||||
chmod 600 "$PACKAGE_NAME"
|
||||
if command -v md5sum >/dev/null; then md5sum "$PACKAGE_NAME" | awk '{print $1}' > "$MD5_NAME"
|
||||
else md5 -q "$PACKAGE_NAME" > "$MD5_NAME"; fi
|
||||
printf '正式包:%s/%s;校验:%s/%s\n' "$SCRIPT_DIR" "$PACKAGE_NAME" "$SCRIPT_DIR" "$MD5_NAME"
|
||||
if [ "$PACKAGE_ONLY" = 1 ]; then exit 0; fi
|
||||
step "上传到 $JUMP_HOST:$JUMP_DIR/"
|
||||
scp -P "$SCP_PORT" "$PACKAGE_NAME" "$MD5_NAME" "$JUMP_HOST:${JUMP_DIR%/}/"
|
||||
step '执行正式服 rsupdate.sh'
|
||||
printf '远端命令:%s\n' "$REMOTE_UPDATE_COMMAND"
|
||||
RUN_LOG=$(mktemp "$SCRIPT_DIR/dist/production-rsupdate.XXXXXX")
|
||||
if ! ssh -p "$SSH_PORT" "$JUMP_HOST" "$REMOTE_UPDATE_COMMAND" 2>&1 | tee "$RUN_LOG"; then fail "rsupdate.sh 执行失败,日志:$RUN_LOG"; fi
|
||||
if ! node - "$RUN_LOG" <<'NODE'
|
||||
const output=require('node:fs').readFileSync(process.argv[2],'utf8').replace(/\x1b\[[0-?]*[ -/]*[@-~]/g,'');
|
||||
if(/\b(?:BACKOFF|FATAL|ERROR)\b|\bspawn\s+error\b/i.test(output))process.exit(1);
|
||||
NODE
|
||||
then fail "远端输出包含失败状态,日志:$RUN_LOG"; fi
|
||||
printf '正式服 rsupdate.sh 执行完成;请核对远端服务状态。日志:%s\n' "$RUN_LOG"
|
||||
@@ -0,0 +1,94 @@
|
||||
#!/bin/bash
|
||||
# 91短剧测试服:构建 → 测试 → tar.gz + SHA-256 → 可选上传并执行 run.sh。
|
||||
set -euo pipefail
|
||||
SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd)
|
||||
cd "$SCRIPT_DIR"
|
||||
source "$SCRIPT_DIR/scripts/deploy-common.sh"
|
||||
HOST="${DEPLOY_HOST:-root@172.104.181.99}"
|
||||
SSH_PORT="${DEPLOY_SSH_PORT:-22889}"
|
||||
# 保留 91dj-web 已约定的上传位置;项目包和 Supervisor 标识使用 91dj。
|
||||
REMOTE_DIR="${DEPLOY_DIR:-/server/web_91dj/h5}"
|
||||
# 远端启动脚本目录独立于上传目录,按服务器约定执行。
|
||||
RUN_DIR="${DEPLOY_RUN_DIR:-/server/web_91dj/h5}"
|
||||
CHECK_URL="${DEPLOY_CHECK_URL:-}"
|
||||
PACKAGE_NAME="${DEPLOY_PACKAGE_NAME:-web_91dj_h5.tar.gz}"
|
||||
CHECKSUM_NAME="$PACKAGE_NAME.sha256"
|
||||
PACKAGE_ONLY=0
|
||||
for argument in "$@"; do
|
||||
case "$argument" in
|
||||
--dry-run) DEPLOY_DRY_RUN=1 ;;
|
||||
--package-only) PACKAGE_ONLY=1 ;;
|
||||
-h|--help)
|
||||
printf '用法:%s [--dry-run] [--package-only]\n' "$0"
|
||||
printf '仅打包:--package-only。仅预览:--dry-run(不构建、不联网)。\n'
|
||||
printf '上传目标:%s:%s %s/。\n' "$HOST" "$SSH_PORT" "${REMOTE_DIR%/}"
|
||||
printf '上传成功后:ssh -p %s %s "cd %s && bash run.sh"\n' "$SSH_PORT" "$HOST" "$RUN_DIR"
|
||||
printf '可覆盖 DEPLOY_HOST、DEPLOY_DIR、DEPLOY_RUN_DIR(%s)、DEPLOY_SSH_PORT、DEPLOY_PACKAGE_NAME(%s)、DEPLOY_CHECK_URL。\n' "$RUN_DIR" "$PACKAGE_NAME"
|
||||
exit 0 ;;
|
||||
*) fail "不支持的参数 $argument" ;;
|
||||
esac
|
||||
done
|
||||
[[ "$PACKAGE_NAME" =~ ^[a-zA-Z0-9_][a-zA-Z0-9_.-]*\.tar\.gz$ ]] || fail 'DEPLOY_PACKAGE_NAME 必须是 .tar.gz 文件名'
|
||||
if [ "$PACKAGE_ONLY" = 0 ] && [ "${DEPLOY_DRY_RUN:-0}" != 1 ]; then
|
||||
valid_host "$HOST" DEPLOY_HOST; valid_path "$REMOTE_DIR" DEPLOY_DIR; valid_path "$RUN_DIR" DEPLOY_RUN_DIR; valid_port "$SSH_PORT" DEPLOY_SSH_PORT
|
||||
need ssh; need scp
|
||||
fi
|
||||
check_local test
|
||||
if [ -n "$CHECK_URL" ]; then
|
||||
node --input-type=module -e 'const u=new URL(process.argv[1]);if(!["http:","https:"].includes(u.protocol)||u.username||u.password||u.search||u.hash||u.pathname!=="/")process.exit(1)' "$CHECK_URL" || fail 'DEPLOY_CHECK_URL 必须是 HTTP(S) 站点根地址'
|
||||
fi
|
||||
if [ "${DEPLOY_DRY_RUN:-0}" = 1 ]; then
|
||||
printf 'DRY RUN:测试环境;不构建、不连接服务器。\n'; public_profile test
|
||||
printf ' npm run build:test → npm test → npm run bundle:test → %s + %s\n' "$PACKAGE_NAME" "$CHECKSUM_NAME"
|
||||
printf ' 包内不含 node_modules、本地环境覆盖和缓存;服务端 npm ci --omit=dev 后 node start.mjs。\n'
|
||||
if [ "$PACKAGE_ONLY" = 1 ]; then printf ' 仅生成本地包。\n'
|
||||
else
|
||||
printf ' 发布目标:%s:%s(SSH %s)。\n' "$HOST" "$REMOTE_DIR" "$SSH_PORT"
|
||||
printf ' 上传 %s、%s 和 up_next_h5.sh。\n' "$PACKAGE_NAME" "$CHECKSUM_NAME"
|
||||
printf ' 上传成功后:ssh -p %s %s "cd %s && bash run.sh"\n' "$SSH_PORT" "$HOST" "$RUN_DIR"
|
||||
fi
|
||||
exit 0
|
||||
fi
|
||||
need tar
|
||||
[ -s deploy/up_next_h5.sh ] || fail '缺少 deploy/up_next_h5.sh'
|
||||
bash -n deploy/up_next_h5.sh
|
||||
lock_build
|
||||
step '构建测试环境并运行回归测试'
|
||||
npm run build:test
|
||||
TEST_APP_ENV=test npm test
|
||||
step '整理 standalone 测试包'
|
||||
npm run bundle:test
|
||||
for file in start.mjs server.js .next-test/BUILD_ID config/test.env; do
|
||||
[ -s "dist/test/$file" ] || fail "测试包缺少 $file"
|
||||
done
|
||||
[ -d dist/test/.next-test/static ] && [ -d dist/test/public ] || fail '测试包缺少静态资源'
|
||||
step "生成 $PACKAGE_NAME 和 SHA-256"
|
||||
TEMP_PACKAGE=$(mktemp "$SCRIPT_DIR/dist/.test-archive.XXXXXX")
|
||||
if ! COPYFILE_DISABLE=1 COPY_EXTENDED_ATTRIBUTES_DISABLE=1 tar --no-xattrs \
|
||||
--exclude='node_modules' --exclude='*/node_modules' --exclude='._*' --exclude='.DS_Store' \
|
||||
--exclude='*.local.env' --exclude='.env*' --exclude='*.log' \
|
||||
-czf "$TEMP_PACKAGE" -C dist/test .; then
|
||||
rm -f "$TEMP_PACKAGE"; fail '压缩失败'
|
||||
fi
|
||||
tar -tzf "$TEMP_PACKAGE" >/dev/null
|
||||
mv -f "$TEMP_PACKAGE" "$PACKAGE_NAME"
|
||||
chmod 600 "$PACKAGE_NAME"
|
||||
node --input-type=module - "$PACKAGE_NAME" "$CHECKSUM_NAME" <<'NODE'
|
||||
import {createHash} from 'node:crypto';
|
||||
import {readFileSync,writeFileSync} from 'node:fs';
|
||||
const [archive,checksum]=process.argv.slice(2);
|
||||
const bytes=readFileSync(archive);
|
||||
writeFileSync(checksum,createHash('sha256').update(bytes).digest('hex')+' '+archive+'\n');
|
||||
// The test and production commands share a package name; refresh its companion checksum too.
|
||||
writeFileSync(archive.replace(/\.tar\.gz$/,'.md5'),createHash('md5').update(bytes).digest('hex')+'\n');
|
||||
NODE
|
||||
printf '测试包:%s/%s;校验:%s/%s\n' "$SCRIPT_DIR" "$PACKAGE_NAME" "$SCRIPT_DIR" "$CHECKSUM_NAME"
|
||||
if [ "$PACKAGE_ONLY" = 1 ]; then exit 0; fi
|
||||
step "上传到 $HOST:$REMOTE_DIR/"
|
||||
ssh -p "$SSH_PORT" "$HOST" "mkdir -p '$REMOTE_DIR'"
|
||||
scp -P "$SSH_PORT" "$PACKAGE_NAME" "$CHECKSUM_NAME" deploy/up_next_h5.sh "$HOST:$REMOTE_DIR/"
|
||||
printf '测试包上传完成:%s:%s/%s\n' "$HOST" "${REMOTE_DIR%/}" "$PACKAGE_NAME"
|
||||
step "执行测试服 $RUN_DIR/run.sh"
|
||||
ssh -p "$SSH_PORT" "$HOST" "cd '$RUN_DIR' && bash run.sh"
|
||||
if [ -n "$CHECK_URL" ]; then node scripts/check-health.mjs "$CHECK_URL" test; fi
|
||||
printf '测试服 run.sh 执行完成:%s:%s/run.sh\n' "$HOST" "${RUN_DIR%/}"
|
||||
@@ -0,0 +1,53 @@
|
||||
# 91av 页面接口对照
|
||||
|
||||
核对日期:2026-10-21。来源:[当前 Swagger](https://91av-wz.80sygtm.com/swagger/index.html)。上游地址通过 `API_BASE_URL` 配置,默认 `https://91av-wz.80sygtm.com`;下表省略 `/api/app/` 前缀。
|
||||
|
||||
| 页面 | 接口及关键参数 |
|
||||
| --- | --- |
|
||||
| 顶部、侧栏和移动导航 | `navigation/header`;使用 `categoryList` 的顺序、`name,urlValue,icon,categoryType,translationList`。1 为 URL、2 为二级导航,3 不混入主导航;不生成固定菜单 |
|
||||
| 首页 | `playlet/home`;轮播 `homeBannerData`,栏目 `homeConfigData`,广告 `advitise` |
|
||||
| 短剧列表 | `playlet/all/list`;`pageNum,pageSize,sortType`,排序 1 最新、2 热播、3 独家、4 随机 |
|
||||
| 三个排行榜 | `playlet/hot/rank/list`;`rankType` 分别为 1 热播、2 新剧、3 追剧,分页使用 `pageNum,pageSize` |
|
||||
| 专题列表 | `playlet/topic/list`;`pageNum,pageSize`,没有专题分类和排序参数 |
|
||||
| 专题详情、题材页 | `playlet/topic/detail`;`id,pageNum,pageSize`。当前服务的题材和专题使用同一 ID;不再向全量列表传会被忽略的 `themeId` |
|
||||
| 标签页 | `playlet/tag/list`;`tagSlug` 或 `tagId`,以及 `pageNum,pageSize,sortType` |
|
||||
| 首页“更多” | 优先使用 `moreUrl`,继续视为站内业务入口。未配置时使用 `/config/{slug或id}/`,从 `playlet/home.homeConfigData` 按 slug 或 ID 提取对应已启用栏目;只展示实际返回的条目,不伪造完整目录或后续分页 |
|
||||
| 搜索结果 | `playlet/search`;`keyword,pageNum,pageSize`。无结果优先展示 `everyoneWatching`,缺少推荐时读取 `playlet/hot/list` |
|
||||
| 搜索联想 | `playlet/search/suggest`;`keyword`,返回短剧对象,点击进入实际详情页;热搜词继续进入搜索页 |
|
||||
| 搜索推荐换一批 | `playlet/all/list`;`sortType=4` 随机获取,避免缺少必填的短剧 ID |
|
||||
| 短剧详情 | `playlet/detail` 传 `slug` 或 `id`;`playlet/chapter/list` 传 `playletSlug` 或 `playletId` |
|
||||
| 详情推荐 | `playlet/guess-like/list`;必须传当前 `playletId`,不依赖详情中未定义的推荐字段 |
|
||||
| 章节播放 | `playlet/chapter/detail`;`chapterId` 加 `playletSlug` 或 `playletId`,使用实时观看权限、费用、余额及视频地址。章节 SEO 优先于整剧 SEO |
|
||||
| 评论 | `comment/list`;`objectId,objectType,pageNum,pageSize,sortOrder`;整剧类型 10、章节类型 11 |
|
||||
| 收藏、历史、已购 | `playlet/collect/list`、`playlet/history/list`、`playlet/bought/list`;`pageNum,pageSize`;服务端使用 `auth/getInfo` 恢复当前账号,直接输出列表 |
|
||||
| 账号设置 | `auth/profile`;头像上传 `upload/image`,使用返回的 `sourceNameFs` |
|
||||
| 充值、VIP | `recharge/package/list`;读取真实套餐金额(分)、金币及支付渠道;余额明细 `coin/trans/list` 使用 `page,pageSize,type` |
|
||||
| 福利应用 | `applist/list?categoryId=…`;保留分类锚点及空分类隐藏逻辑 |
|
||||
| 回家的路、独立导航 | `config/info` 的永久地址、备用线路、TG、App 链接;没有 App 地址时隐藏下载入口 |
|
||||
| 关于等六个信息页 | 按既有要求保留 HTML 静态正文;联系我们使用配置中的商务 TG 与 TG 群,服务时间可取 `config/footer/info` |
|
||||
|
||||
## 写接口
|
||||
|
||||
2026-10-22 本项目(`JHA-1047`)调整内容地址:链接优先使用非空 slug,缺少时使用 ID,保留尾斜杠和独立分页。专题详情接口仍必填 ID,slug 路由先分页查询专题目录解析 ID,再读取详情;目录故障返回服务错误,不伪装成专题不存在。详情评论和推荐等只支持数字 ID 的接口在详情返回后使用真实 ID;支持 slug 的章节请求继续并行。canonical、面包屑、选集和 ItemList 使用对应主地址,旧 ID 地址不新增转向 slug 的重定向。
|
||||
|
||||
登录、绑定和找回密码使用 `auth/login`、`auth/register`、`auth/resetPassword`;凭证保存在 HttpOnly Cookie。整剧点赞/收藏传 `id` 和布尔状态,章节点赞/收藏只传 `chapterId` 和布尔状态。解锁传 `playletId,chapterId`,仍在用户确认后提交。评论发送、点赞、观看报告的字段按各自请求模型发送。
|
||||
|
||||
金币下单 `coin/buy`、VIP 下单 `vip/pay` 均使用 `packageId,payMode`,支付渠道只取套餐配置。没有在真实服务上创建订单、付费解锁、注册账号、发送评论或提交反馈。
|
||||
|
||||
反馈按 `proto.FeedbackReq` 模型:1 建议、2 Bug、3 体验、4 账号、5 其他;操作描述仍写旧的四类,与模型不一致,本次以请求模型为准。问题正文与附加环境信息合计最多 500 字,优先完整保留用户正文。
|
||||
|
||||
## 已移除的旧接口依赖
|
||||
|
||||
`playlet/operate/list`、`playlet/home/config/detail/list`、`video/all/list` 在实际游客身份下返回 HTTP 404,且不在当前文档内。其余旧 `video/*`、`playlet/rank`、`playlet/today-update` 也不再开放代理。
|
||||
|
||||
当前文档没有独立视频列表和详情接口。按用户要求移除“剧集”的桌面、侧栏、移动导航入口及 `/series/`、`/video/{id}/` 路由(含分页);旧地址直接返回 404,不转换或重定向。首页和结构化数据不再输出旧类型的独立视频栏目。短剧详情、选集和章节播放继续保留。
|
||||
|
||||
## 验证方式
|
||||
|
||||
页面请求采用服务端渲染:服务端先准备会话与当前页数据,再直接返回完整正文;列表、搜索、详情/播放、工具、账户和充值均不使用浏览器整页取数及加载占位。原生链接请求下一页,浏览器只绑定首屏数据对应的交互。搜索联想、用户主动操作、实时播放权限和显式余额刷新仍按需调用 API。
|
||||
|
||||
公共配置、页脚、导航、分类和标签选项缓存 60 秒,首页缓存 30 秒,按上游和查询参数隔离并合并并发读取。详情、章节、选集、评论及推荐并行准备;账户、余额、套餐和观看权限保持实时。公开配置及页脚以字段白名单随 HTML 传递,SDK 配置继续单独读取。可用 `PUBLIC_DATA_CACHE_TTL_MS=0` 关闭公共缓存用于排查;页面和鉴权响应继续禁止共享缓存。
|
||||
|
||||
`tests/fixtures/91av-app-contract.json` 保存本次核对的端点、方法、查询参数及请求字段,不包含配置或凭证。合同测试检查页面请求参数及代理白名单;本地模拟服务另验证 SSR、分页、权限、登录、解锁及支付交互。
|
||||
|
||||
`PREVIEW_URL=http://127.0.0.1:3107 npm run smoke:api` 检查真实只读接口、游客握手及页面响应,凭证仅保存在进程内存。浏览器交互与真实扣费不包含在该脚本的验证范围内。
|
||||
|
After Width: | Height: | Size: 153 KiB |
@@ -0,0 +1,42 @@
|
||||
# 页面索引策略
|
||||
|
||||
调整日期:2026-10-22。规则由 `src/lib/seo.ts` 统一生成,服务端初始 HTML 与客户端更新共用。这里只控制允许索引的信号,不保证搜索引擎实际收录。
|
||||
|
||||
## 允许索引
|
||||
|
||||
- 首页、有效且非空的短剧列表、三个榜单、专题列表与详情、标签列表、作品详情。
|
||||
- 普通内容列表的有效分页继续自引用 canonical,不统一归并到第一页。
|
||||
- 关于我们、常见问题、用户协议、隐私政策、2257、DMCA。
|
||||
- `/config/{id}/` 必须有栏目自己的标题、正文简介和有效内容卡片。只有 SEO 标题/描述或空卡片不能满足条件;此规则不依赖额外接口请求,也不代表已自动判定其内容与全站其他栏目语义唯一。
|
||||
- `/play/{id}/{chapterId}/` 必须有作品名、真实正整数集数,以及当前访问身份有权观看的有效正片地址。共享预告、继承的作品简介、章节 SEO 字段不能单独使纯解锁/不可播放页开放索引。收费类型本身不作为排除条件,有权观看的金币/VIP 章节仍可索引。当前模板没有独立可见的章节正文,若以后增加此类内容,可再评估受限章节的索引范围。
|
||||
|
||||
## 不允许索引
|
||||
|
||||
| 范围 | 设置 |
|
||||
| --- | --- |
|
||||
| 内部搜索、导航入口、回家的路、福利应用 | `noindex,follow` |
|
||||
| 个人中心、收藏、历史、已购、设置、充值/VIP、反馈 | `noindex,nofollow` |
|
||||
| 非默认有效排序变体、非默认评论排序 | `noindex,follow`,沿用原有例外规则 |
|
||||
| 详情和播放页的评论分页,页码大于 1 | `noindex,follow` |
|
||||
| 接口成功确认 `totalCount=0` 的公开列表第一页 | `noindex,follow`,继续返回 200 |
|
||||
| 缺少独立栏目信息的更多列表、纯解锁或无有效正片的播放页 | `noindex,follow`,继续提供原页面 |
|
||||
|
||||
noindex 页面不输出 canonical、head 中的 prev/next 或内容 JSON-LD;正文的选集、评论、分页链接继续提供。作品详情没有评论时仍可索引,评论数为 0 不等于作品为空。
|
||||
|
||||
## 去重和结构化数据
|
||||
|
||||
- 同 ID 的 `/theme/{id}/` 与 `/topics/{id}/` 当前读取相同资源,以 `/topics/{id}/` 为 canonical。两条路由都可直接访问,不新增重定向。
|
||||
- 自动生成的专题卡片与 ItemList 链接使用 `/topics/{id}/`;后台明确配置的 `moreUrl` 保留原值。
|
||||
- 数字资源 ID 的前导零在 canonical 与播放实体 URL 中归一;普通分页仍保留自己的页码。搜索关键词不进行数字归一。
|
||||
- canonical 仍仅取配置的永久域名,去除跟踪参数;`/rank/`、`/page/1/` 继续使用既有主地址。
|
||||
- 2026-10-22 起内容主地址优先使用接口的非空 slug,缺少时使用 ID。ID 地址仍直接提供内容,其 canonical 与结构化数据指向该内容的 slug 主地址;评论与操作请求继续使用真实数字 ID。
|
||||
- 可索引的付费播放页标记 `isAccessibleForFree:false` 与 `hasPart.cssSelector:.play-paywalled-content`,选择器对应真实播放器容器,不改变样式。
|
||||
- VideoObject 只在真实媒体源、有效日期与封面均存在时输出。无权观看的媒体地址不进入 SEO 数据;不编造视频日期或媒体源。
|
||||
|
||||
## 状态码与验证
|
||||
|
||||
资源缺失和已证明越界的分页继续返回 404;主内容临时故障继续返回 500。noindex 不用来替代错误状态码。本次未修改 sitemap、robots.txt 或接口访问权限。
|
||||
|
||||
`npm run build` 及全部 274 项 `npm test` 通过。验证覆盖函数级索引矩阵、客户端 head 清理、真实 Next.js 生产服务的初始 HTML、Googlebot/YandexBot 请求、普通分页、评论分页、空列表、别名与前导零、更多栏目、付费章节权限和 JSON-LD。模拟接口不提交真实业务写入;线上响应头和实际收录结果仍以部署后的验证为准。
|
||||
|
||||
规则参考:[Google canonical](https://developers.google.com/search/docs/crawling-indexing/consolidate-duplicate-urls)、[Google 分页](https://developers.google.com/search/docs/specialty/ecommerce/pagination-and-incremental-page-loading)、[Google 付费内容结构化数据](https://developers.google.com/search/docs/appearance/structured-data/paywalled-content)。
|
||||
|
After Width: | Height: | Size: 21 KiB |
@@ -0,0 +1,222 @@
|
||||
# 后端 Sitemap 生成对接规则
|
||||
|
||||
核对时间:2026-10-22 22:53(Asia/Shanghai)。
|
||||
|
||||
本文根据当前工作区的前端代码(包含尚未提交的修改)整理固定页面、URL、canonical 和 noindex 规则,供后端实现 sitemap 生成。它描述现有页面行为及生成约定,不表示 sitemap、存储或 Nginx 已完成接入。
|
||||
|
||||
当前内容主地址仅优先使用符合英文/拼音字符规则的 slug;中文、已编码中文或其他无效 slug 回退到 ID。搜索关键词仍支持中文和特殊字符,但所有搜索地址均排除在 sitemap 外。
|
||||
|
||||
## 1. 域名与通用约定
|
||||
|
||||
- `D` 表示正式站点的 origin,即协议、域名和可选端口,不带末尾 `/`。
|
||||
- 域名优先取 `config.config.foreverUrlAddr`;无有效值时取 `config.newAddressInfo.foreverUrlAddr`。移除配置中的路径、查询参数和锚点,与前端 canonical 保持一致。
|
||||
- 不使用 API 域名、CDN 域名、请求 Host、临时访问域名或本地地址作为页面域名。
|
||||
- 生成任务没有有效正式域名时,不猜测域名;暂停本次发布并保留上一份成功结果。
|
||||
- 页面 URL 以 `/` 结尾;sitemap 中使用绝对地址 `D + 规范路径`。
|
||||
- sitemap 只提交正常、允许索引的页面及其 canonical 地址;别名、参数变体和重复地址不单独提交。
|
||||
- API、媒体源、静态资源和健康检查不属于本文的页面 sitemap 范围。
|
||||
|
||||
## 2. 固定页面清单
|
||||
|
||||
固定地址候选共 12 个。满足条件时,其 canonical 均为 `D + URL`。列表为空或页面异常时,按后续排除规则处理。
|
||||
|
||||
| 页面 | URL | 加入 sitemap 的条件 |
|
||||
| --- | --- | --- |
|
||||
| 首页 | `/` | 页面正常 |
|
||||
| 短剧列表 | `/shorts/` | 页面正常且列表非空 |
|
||||
| 专题列表 | `/topics/` | 页面正常且列表非空 |
|
||||
| 热播榜 | `/rank/hot/` | 页面正常且列表非空 |
|
||||
| 新剧榜 | `/rank/new/` | 页面正常且列表非空 |
|
||||
| 追剧榜 | `/rank/follow/` | 页面正常且列表非空 |
|
||||
| 关于我们 | `/info/about-us/` | 页面正常 |
|
||||
| 用户协议 | `/info/user-agreement/` | 页面正常 |
|
||||
| 隐私政策 | `/info/privacy-policy/` | 页面正常 |
|
||||
| 常见问题 | `/info/help-center/` | 页面正常 |
|
||||
| 2257 合规声明 | `/info/2257/` | 页面正常 |
|
||||
| DMCA 版权声明 | `/info/dmca/` | 页面正常 |
|
||||
|
||||
这些是页面地址,不是需要前端预建的 sitemap XML 文件。
|
||||
|
||||
## 3. 动态页面 URL 规则
|
||||
|
||||
| 类型 | 规范 URL | 加入 sitemap 的条件 |
|
||||
| --- | --- | --- |
|
||||
| 作品详情 | `/detail/{key}/` | 作品存在,页面正常且未被标记为需登录或禁止索引 |
|
||||
| 专题详情 | `/topics/{key}/` | 专题存在,页面正常且列表非空 |
|
||||
| 标签列表 | `/tag/{key}/` | 标签存在,页面正常且列表非空 |
|
||||
| 更多栏目 | `/config/{key}/` | 首页栏目存在、已启用且类型为 `1` 或 `3`,并具有自己的标题、正文简介和有效内容卡片 |
|
||||
| 单集播放 | `/play/{作品key}/{chapterId}/` | 满足下述播放页条件 |
|
||||
| 内容列表分页 | `{列表基础路径}page/{n}/` | `n >= 2`,分页真实存在且允许索引 |
|
||||
|
||||
### 3.1 内容标识
|
||||
|
||||
- `{key}` 优先使用内容接口返回的有效英文/拼音 `slug`;无有效 slug 时使用正整数 ID。前端不会自动把中文转成拼音。
|
||||
- slug 去掉首尾空格后,必须满足 `/^[a-z\d][a-z\d_-]*$/i`:以 ASCII 字母或数字开头,其余只能包含 ASCII 字母、数字、连字符 `-`、下划线 `_`。
|
||||
- 使用接口返回的规范大小写,作为一个路径段按 `encodeURIComponent` 的语义编码一次,避免重复编码;不得把编码后的中文当作英文 slug。
|
||||
- 中文、中文英文混合、内含空格、斜杠、点号、百分号、问号等不符合正则的 slug 均回退到 ID。`-city` 和 `_city` 也不符合开头规则。
|
||||
- 历史中文 slug 地址在能够匹配到真实内容时仍可读取,但 canonical 和新生成链接使用 ID;后端不应继续向 sitemap 写入这些兼容地址。
|
||||
- 纯数字路径按 ID 查询,不按 slug 查询。当前生成正则本身未单独排除纯数字 slug,因此后端应避免分配此类 slug;存量数据须核对身份,不能直接当作 slug 发布。
|
||||
- 使用数字 ID 时去除前导零;ID 必须是正整数,且处于 JavaScript 安全整数范围内。
|
||||
- `{chapterId}` 是真实章节 ID,不是集数,也不支持章节 slug。章节必须属于 URL 中的作品。
|
||||
|
||||
以作品 `id=9` 为例:
|
||||
|
||||
| 接口返回的 slug | 生成的详情路径 |
|
||||
| --- | --- |
|
||||
| `city-story` | `/detail/city-story/` |
|
||||
| `city_story-2026` | `/detail/city_story-2026/` |
|
||||
| `dushiaiqing` | `/detail/dushiaiqing/` |
|
||||
| `City-Story` | `/detail/City-Story/` |
|
||||
| `都市爱情` | `/detail/9/` |
|
||||
| `city-爱情` | `/detail/9/` |
|
||||
| `%E9%83%BD%E5%B8%82` | `/detail/9/` |
|
||||
| `city story`、`city/story`、`city.story`、空值 | `/detail/9/` |
|
||||
|
||||
以上规则同时用于作品、专题、标签和更多栏目;播放页的作品段使用同一规则,章节段仍使用章节 ID。
|
||||
|
||||
### 3.2 播放页条件
|
||||
|
||||
播放页同时满足以下条件时才可进入公共 sitemap:
|
||||
|
||||
1. 作品及章节真实存在,且两者对应。
|
||||
2. 有作品名和真实的正整数集数。
|
||||
3. 公开抓取身份有权观看当前章节。
|
||||
4. 存在有效的当前章节正片地址。
|
||||
|
||||
共享预告、继承的作品简介、章节 SEO 标题或纯解锁提示不能单独满足条件。收费类型本身不是排除依据,但不能使用管理员、VIP 或已购用户的个人权限生成公共 sitemap。
|
||||
|
||||
sitemap 提交页面 URL,不写入用户凭证或受限媒体源。
|
||||
|
||||
### 3.3 列表分页
|
||||
|
||||
- 普通内容目录当前每页 24 条,后端根据有效总数计算真实页数。
|
||||
- 更多栏目当前使用有限首页栏目快照:类型 `1` 取实际作品列表,类型 `3` 取实际专题列表,以实际条目数为 `totalCount`。SEO 层取 `pageSize = max(24, totalCount)`,因此当前只发布栏目基础地址,不生成 `/config/{key}/page/2/` 等额外分页。
|
||||
- 第一页只提交基础地址,不额外提交 `/page/1/`。
|
||||
- 第 2 页及以后保留自己的 canonical,例如 `/shorts/page/2/`,不统一归并到第一页。
|
||||
- 作品详情和播放页的 `/page/{n}/` 是评论分页;`n > 1` 时不进入 sitemap。
|
||||
- 搜索、收藏、历史、已购和充值即使有分页,也不进入 sitemap。
|
||||
|
||||
## 4. Canonical 规则
|
||||
|
||||
| 场景 | 规范化规则 |
|
||||
| --- | --- |
|
||||
| 正式域名 | 统一使用 `D`,与前端页面 canonical 的域名一致 |
|
||||
| 尾斜杠 | 页面路径以 `/` 结尾 |
|
||||
| 查询参数和锚点 | canonical 不含查询参数和锚点;sitemap 同样不提交这些变体 |
|
||||
| 第一页 | `/shorts/page/1/` 规范化为 `/shorts/`;其他支持分页的页面同理 |
|
||||
| 有效列表分页 | 第 2 页及以后自引用,保留 `/page/{n}/` |
|
||||
| 默认榜单 | `/rank/` 规范化为 `/rank/hot/`;`/rank/page/2/` 规范化为 `/rank/hot/page/2/` |
|
||||
| 专题别名 | `/theme/{key}/` 规范化为 `/topics/{规范key}/`,保留有效分页 |
|
||||
| ID 与 slug | 有符合英文/拼音字符正则的 slug 时,ID 地址指向 slug 主地址;中文或其他无效 slug 回退到规范 ID |
|
||||
| 历史中文 slug 地址 | 能匹配到资源时保留兼容读取,canonical 使用该资源的规范 ID 地址 |
|
||||
| 数字 ID 前导零 | 无 slug 时,`/detail/009/` 规范化为 `/detail/9/`;其他数字内容 ID 同理 |
|
||||
| noindex 页面 | 当前前端不输出 canonical,不进入 sitemap |
|
||||
|
||||
例如作品 `id=9`、`slug=city-story`,章节 ID 为 `90`:
|
||||
|
||||
```text
|
||||
/detail/9/ -> D/detail/city-story/
|
||||
/detail/city-story/ -> D/detail/city-story/
|
||||
/play/9/90/ -> D/play/city-story/90/
|
||||
/play/city-story/90/ -> D/play/city-story/90/
|
||||
```
|
||||
|
||||
例如专题 `id=7`、`slug=city-collection`:
|
||||
|
||||
```text
|
||||
/theme/7/ -> D/topics/city-collection/
|
||||
/topics/7/ -> D/topics/city-collection/
|
||||
/theme/7/page/2/ -> D/topics/city-collection/page/2/
|
||||
```
|
||||
|
||||
例如作品 `id=9`、`slug=都市爱情`,章节 ID 为 `90`,中文仅用于展示或兼容读取:
|
||||
|
||||
```text
|
||||
/detail/9/ -> D/detail/9/
|
||||
/detail/都市爱情/ -> D/detail/9/
|
||||
/play/9/90/ -> D/play/9/90/
|
||||
/play/都市爱情/90/ -> D/play/9/90/
|
||||
```
|
||||
|
||||
上述中文路径按可读形式展示,实际请求可使用百分号编码;sitemap 只写入右侧 ID 主地址。
|
||||
|
||||
别名页面仍可允许索引,canonical 归并不等于 noindex,也不表示已有重定向。sitemap 只提交最终 canonical 地址,并按完整地址去重。
|
||||
|
||||
`/home`、`/playlet`、`/list`、`/topic`、`/application` 等值只用于后台导航值转换,不是额外公开页面,不应据此生成 sitemap 地址。
|
||||
|
||||
## 5. 固定 Noindex 页面
|
||||
|
||||
| 页面范围 | URL | 当前 robots |
|
||||
| --- | --- | --- |
|
||||
| 搜索和搜索分页 | `/search/`、`/search/{关键词}/`、`/search/{关键词}/page/{n}/`、`/search/?q={关键词}`、`/search/page/{n}/?q={关键词}` | `noindex,follow` |
|
||||
| 独立入口 | `/navigation/` | `noindex,follow` |
|
||||
| 回家的路 | `/home-road/` | `noindex,follow` |
|
||||
| 福利应用 | `/apps/` | `noindex,follow` |
|
||||
| 个人中心 | `/member/center/`、`/info/user-center/` | `noindex,nofollow` |
|
||||
| 收藏 | `/member/favorites/`,包括分页 | `noindex,nofollow` |
|
||||
| 历史 | `/member/history/`,包括分页 | `noindex,nofollow` |
|
||||
| 已购 | `/member/purchased/`,包括分页 | `noindex,nofollow` |
|
||||
| 账号设置 | `/member/settings/`、`/info/settings/` | `noindex,nofollow` |
|
||||
| 充值及 VIP | `/info/deposit/`、`/info/deposit/vip/`,包括支持的分页 | `noindex,nofollow` |
|
||||
| 意见反馈 | `/info/feedback/` | `noindex,nofollow` |
|
||||
|
||||
以上页面及其参数变体均不进入 sitemap。
|
||||
|
||||
### 5.1 当前搜索地址的排除规则
|
||||
|
||||
- 新生成的搜索入口统一使用 `/search/{关键词}/`;关键词先去掉首尾空格,再通过 `encodeURIComponent` 整体编码为一个路径段。
|
||||
- 关键词可以包含中文、空格、斜杠、问号、百分号等字符。搜索关键词不受内容 slug 的 ASCII 正则限制,也不进行数字 ID 归一。
|
||||
- `/search/?q={关键词}` 和 `/search/page/{n}/?q={关键词}` 仍可解析;后续分页链接生成路径形式并移除 `q` 参数,不依赖重定向完成转换。
|
||||
- 路径本身已有关键词时优先使用路径关键词;只有路径没有关键词时才从 `q` 读取。
|
||||
- 不论入口形式、搜索结果数量或关键词字符,搜索页始终 `noindex,follow`,不输出 canonical,也不进入 sitemap。
|
||||
- 无关键词的 `/search/` 可以访问;无关键词且页码大于 1 时返回 404。
|
||||
|
||||
## 6. 条件性 Noindex 与异常处理
|
||||
|
||||
| 情况 | 当前前端行为 | 后端生成处理 |
|
||||
| --- | --- | --- |
|
||||
| 空公开列表 | 接口成功确认 `totalCount=0` 时,第一页 `noindex,follow`,仍返回 200 | 不提交 |
|
||||
| 详情或播放页评论分页 | `/detail/{key}/page/{n}/`、`/play/{key}/{chapterId}/page/{n}/`,`n>1` 时 `noindex,follow` | 不提交 |
|
||||
| 非默认列表排序 | 除榜单和更多栏目外,公开列表 `sort != 1` 时 noindex | 只生成无参数的默认地址 |
|
||||
| 非法列表排序 | 公开列表的 `sort` 不属于 `1、2、3` 时 noindex;未传时默认 `1` | 不生成非法变体 |
|
||||
| 非默认评论排序 | 存在 `commentSort` 且值不等于字符串 `2` 时 noindex | 不生成参数变体 |
|
||||
| 更多栏目内容不足或无效 | 已存在栏目缺少自己的标题、正文简介或有效卡片时 `noindex,follow`;找不到启用的类型 `1/3` 栏目时按资源缺失处理 | 不提交 |
|
||||
| 播放页条件不足 | 预告、纯解锁提示、无有效正片或无观看权限时 `noindex,follow` | 不提交 |
|
||||
| 明确禁止索引或需要登录 | `indexable=false` 或 `loginRequired=true` 时 noindex | 不提交 |
|
||||
| 无效路由或不存在的资源 | 返回 404,且无 canonical | 不提交 |
|
||||
| 已确认越界的分页 | 返回 404,且无 canonical | 不提交 |
|
||||
| 临时取数故障 | `failed` 或 `unavailable` 时返回 500,且无 canonical | 不将故障误判为删除;保留上次成功结果,稍后重试 |
|
||||
|
||||
补充约定:
|
||||
|
||||
- 榜单和更多栏目可以接受 `sort=2/3`,但 canonical 仍不含参数;sitemap 不重复提交这些参数地址。
|
||||
- 作品详情的评论数为 0,不等于作品内容为空,不能按空列表规则排除作品详情。
|
||||
- 不存在的内容与临时故障必须区分;无法可靠判断一批内容时,不发布因取数失败而缺项的新结果。
|
||||
|
||||
## 7. 建议的生成与发布约定
|
||||
|
||||
1. 合并本文的固定地址与后端数据库枚举的动态内容。
|
||||
2. 按公开访问时的页面状态判断是否允许索引。
|
||||
3. 按英文/拼音 slug 白名单和 ID 回退规则生成最终 canonical URL,不输出参数、锚点、别名、中文内容 slug 或非页面资源。
|
||||
4. 按完整 canonical URL 去重,只发布正常、有效的页面地址。
|
||||
5. `lastmod` 有可靠数据时使用页面内容实际更新时间;没有时省略,不统一伪造为生成时间。
|
||||
6. 生成文件保存到前端发布目录之外的持久化存储,不写入前端 `public` 或 `.next`。
|
||||
7. 完成分片生成和可访问性校验后,再切换 sitemap 索引;失败时保留上一份成功版本。
|
||||
8. 建议固定入口为 `/sitemap.xml`,分片通过 `/sitemaps/` 下的通用路径提供访问。现有入口不同时,优先保留已提交的地址。
|
||||
9. `robots.txt` 中声明正式站点的 sitemap 索引地址;前后端 URL 或索引规则变更时同步更新生成逻辑。
|
||||
|
||||
首次接入需要部署访问配置;后续内容和分片变化不应要求前端预留文件、重新构建或重启 H5。
|
||||
|
||||
## 8. 代码依据
|
||||
|
||||
- [路由解析、内容路径和分页支持](../src/lib/routing.ts)
|
||||
- [域名、canonical、noindex 和页面状态判断](../src/lib/seo.ts)
|
||||
- [服务端资源核对与 SEO 元数据输出](../src/lib/seo-server.ts)
|
||||
- [专题解析、首页栏目快照和列表分页](../src/lib/client/channel.ts)
|
||||
- [章节权限与正片地址判断](../src/lib/client/playback.ts)
|
||||
- [固定信息页](../src/lib/info-pages.ts)
|
||||
- [现有页面索引策略](indexation-policy.md)(其“非空 slug 优先”描述需结合当前代码的英文/拼音字符白名单理解)
|
||||
- [SEO 规则回归用例](../tests/seo.test.mjs)
|
||||
- [Slug 路由与 canonical 回归用例](../tests/slug-routing.test.mjs)
|
||||
- [路由与搜索参数兼容回归用例](../tests/routing.test.mjs)
|
||||
- [分页与搜索路径回归用例](../tests/pagination.test.mjs)
|
||||
@@ -0,0 +1,173 @@
|
||||
# API 对接验证
|
||||
|
||||
日期:2026-10-17。
|
||||
|
||||
## 2026-10-22 测试与生产环境配置
|
||||
|
||||
本项目增加 `config/test.env`、`config/production.env`,保留独立的开发配置。进程变量优先,其次为对应 `*.local.env`,最后为基础配置;本地覆盖文件忽略提交。接口均沿用 `https://91av-wz.80sygtm.com`,默认端口均为 21021。测试部署使用 `APP_ENV=test` 和 `NODE_ENV=production`,构建目录为 `.next-test/`;生产目录为 `.next/`。开发目录为 `.next-dev/`。SEO 快照随对应目录隔离,同 API 的两个环境也不会共用快照。
|
||||
|
||||
`npm run build:test`、`npm run check` 通过,全部 290 项测试通过。新增测试覆盖配置优先级、环境隔离、CLI 参数、错误配置、真实子进程退出状态,以及同上游的 SEO 快照隔离。另通过启动脚本分别启动两套实际 Next.js 服务,使用临时端口和本地模拟上游验证静态资源及 API 代理均返回 200,验证结束后关闭临时服务。未执行远程部署。
|
||||
|
||||
## 2026-10-22 slug 路由
|
||||
|
||||
本项目(`JHA-1047`)的内容链接、轮播、专题/标签/栏目、搜索、书架卡片、选集、评论分页及解锁后跳转统一优先 slug,缺少时用 ID。支持 slug 地址直接访问与刷新,保留原 ID 地址直接访问;业务写入及仅接受 ID 的读取继续使用真实数字 ID。专题详情按当前文档先从分页专题目录解析 ID,不向接口发送未定义的 slug 参数。
|
||||
|
||||
生产构建及全部 283 项测试通过。新增回归覆盖 URL 编码、中文及特殊字符、分页目录解析、目录异常与缺失资源、canonical/JSON-LD 一致性、客户端选集与解锁跳转,以及严格 Swagger 参数约束下的 Next.js 生产响应。当前 Swagger 的 slug 查询参数已只读核对;测试使用本地模拟接口,未提交真实付费解锁、订单或评论。
|
||||
|
||||
## 2026-10-21 跳转性能优化
|
||||
|
||||
公共数据服务增加短期进程内缓存和并发合并:配置、页脚、导航、分类及标签选项 60 秒,首页 30 秒;按上游及查询参数隔离,返回独立副本并限制条目数量。无效数据、错误响应、认证和私人业务读取不缓存,配置/首页可在刷新失败时使用最近一次有效数据。详情、章节、选集、评论和推荐在响应详情之前并行请求,仍在服务端输出完整 HTML。
|
||||
|
||||
浏览器直接复用首屏公开配置及页脚,SDK 配置不序列化。公开页从浏览器前进后退缓存恢复时保留文档,后台按当前 Cookie 恢复身份并补报页面访问;账户、播放、支付及打开弹窗的页面保留重新验证。类型检查、生产构建及全部 258 项测试通过,包括跨请求缓存、并发合并、过期、错误恢复、副本隔离、缓存边界、两个账号隔离、首屏配置白名单、并行请求屏障、后退恢复与埋点。
|
||||
|
||||
真实接口及 29 个页面检查通过。本机短期采样中,公共缓存预热后列表约 312ms、专题约 287ms、回家的路约 143ms(首字节中位数,各 3 次);首次游客会话曾超时,另一次交替对比也出现显著上游波动,不能将上述采样当作稳定延迟承诺。未提交真实订单、付费解锁、评论或反馈,未执行浏览器视觉验收。
|
||||
|
||||
## 2026-10-21 服务端直接渲染
|
||||
|
||||
请求与渲染方式:页面数据在服务端准备,页头、公开列表、搜索、详情/播放、应用目录、回家的路、反馈、个人中心及设置、充值/VIP 的正文随初始 HTML 返回。站内链接、分页和搜索使用原生文档请求,保留当前页面直到新响应到达;不再清空正文显示页面加载占位。浏览器复用首屏快照绑定交互,避免启动时重复读取整页数据。提交成功后重新请求当前页面,轮播、图片解密、实时权限、解锁和支付确认保留。
|
||||
|
||||
Proxy 为有效业务页面准备身份,并在本次请求中传递新 Cookie,供服务端读取账户内容;同一 Cookie 同时写回浏览器。客户端复用服务端用户快照,避免再次初始化游客;正式账号失效显示登录入口,服务异常直接提供重试。外部传入的内部身份头先清除,用户、列表、套餐和流水使用明确字段白名单,凭证不写入 HTML;账户响应继续禁止缓存和索引。独立 `/navigation/`、资源、无效地址和尾斜杠重定向不触发该会话流程。
|
||||
|
||||
类型检查、生产构建及全部 249 项测试通过,覆盖完整首屏正文、原生跳页保留旧文档、快照绑定不重复取数、两个账号隔离、伪造身份头、正式账号过期与退出、Cookie 传递、套餐及流水、播放器与广告/轮播清理。真实只读接口及 29 个页面检查通过,另确认真实免费与金币章节页面输出完整 HTML。没有创建真实订单、付费解锁、提交评论或反馈;未执行浏览器视觉验收。
|
||||
|
||||
## 2026-10-21 动态导航
|
||||
|
||||
顶部与侧栏共用 `navigation/header` 的服务端数据,移动抽屉继承同一菜单,底栏按同源数据生成。保留接口名称、顺序、图标和二级条目;过滤底部类型、不安全地址及已移除的独立剧集地址。首屏 HTML 已包含真实链接,原脚本不再插入固定顶部导航或覆盖接口名称。已知 API 的 `/hot` 与空系统入口直接生成本站路径,不新增跳转。
|
||||
|
||||
路由高亮由同一纯函数处理首屏及客户端导航:首页精确匹配,列表分页保持选中,排行榜覆盖三个榜单,具体专题及带查询条件的入口按对应地址匹配,顶部不借用详情页的列表高亮。接口空列表不回填静态菜单;异常只提示导航暂不可用,正文仍能加载。构建、类型检查和全部 248 项测试通过,覆盖动态顺序/名称、二级条目、外链、空列表、异常、旧剧集过滤和高亮规则。
|
||||
|
||||
## 2026-10-21 移除独立剧集
|
||||
|
||||
按后续确认移除桌面顶部、侧栏、移动端的“剧集”导航,以及独立剧集列表、视频详情和对应分页路由;旧地址无论是否带尾斜杠均直接返回 404,不请求内容接口,也不重定向。首页与 JSON-LD 不再展示旧类型的视频栏目。短剧详情、选集、金币解锁和章节播放保留。构建、类型检查与全部 242 项测试通过,包括入口清理、旧地址 404 和原有播放/解锁回归。
|
||||
|
||||
## 2026-10-21 当前 Swagger 对接
|
||||
|
||||
按 `91av-wz.80sygtm.com` 当前文档与实际只读响应核对页面接口,端点和参数映射见 [页面接口对照](api-contract.md)。修正短剧列表、专题顶层字段、题材筛选、搜索联想与空结果推荐、详情推荐及章节 SEO/统计字段;删除旧接口代理依赖。首页“更多”保留已确认的 moreUrl 优先规则,未配置时只展示真实首页栏目的数据。按用户后续要求移除独立剧集导航和路由,旧地址及分页直接返回 404;短剧章节播放保留。
|
||||
|
||||
构建、类型检查及全部 240 项接口合同与回归测试通过。真实服务验证游客身份、列表/三个榜单、专题、标签、配置、应用、套餐、个人列表和余额记录;另核对 29 个原生页面响应与实际有内容的免费/金币章节(`/play/10923/12723/`、`/play/10923/12726/`)。没有创建真实订单、付费解锁、注册账号或提交评论、反馈。未执行新的浏览器视觉验收。
|
||||
|
||||
## 2026-10-20 充值确认弹窗
|
||||
|
||||
恢复原 HTML 的支付标题/副标题、订单卡片、基础/赠送/合计金币、金额徽标、渠道图标按钮及选中圆点、确认按钮和底部说明,并复用原稿的尺寸、间距、圆角及移动布局。支付宝、微信、USDT 图标使用原始本地素材;渠道仅来自套餐配置,其他渠道保留名称和通用图标。使用隐藏表单值连接样式按钮和原下单参数,金币充值与 VIP 接口保持不变。
|
||||
|
||||
`tests/payment.test.mjs` 验证实际金币字段及金额换算、不注入演示赠送或未配置渠道、方法切换后的订单参数、重复提交拦截、VIP 接口、无效支付地址、无支付方式以及旧表单响应隔离。类型检查、生产构建和全部 152 项测试通过,仅使用本地模拟上游,没有创建真实订单或发起真实支付。
|
||||
|
||||
## 2026-10-20 详情选集解锁
|
||||
|
||||
对照参考项目 `playletser/chapter.go` 的列表权限规则和 `pages/play.js` 的解锁处理,金币集数使用按钮直接打开 `play-episode-lock`,保留原 HTML 的金币图标、余额/消耗摘要、确认和充值入口样式。弹窗先获取目标章节详情,再展示实际价格及余额;仅确认后提交解锁,成功刷新余额并跳转所选真实章节。已可观看和会员章节按最新权限处理,余额不足和登录过期分别进入充值或登录。
|
||||
|
||||
新增测试覆盖:列表权限优先级、详情页弹窗不提前跳转/扣费、最新价格覆盖列表旧价格、正确章节 ID、重复点击及确认防重、支付中禁止切换、迟到响应隔离、关闭/离开页面、余额不足、已可观看/VIP、章节信息不匹配、登录失效、失败重试,以及实际 Route Handler 的凭据和解锁参数转发。类型检查、生产构建与全部 147 项测试通过;测试仅连接本地模拟上游,未提交真实金币解锁。
|
||||
|
||||
## 2026-10-20 详情页样式调整
|
||||
|
||||
区块留白按原稿保留:横幅上下桌面端 26px、手机端 22px,官方链接至评论区 28px。无横幅数据时,选集与官方链接之间仍保留对应间距,不因条件渲染移除广告区而贴合。
|
||||
|
||||
对照原 HTML 的 `overviewMarkup`、操作栏、选集和评论 DOM 恢复结构,复用已有桌面/移动样式及原始 SVG 素材;修复 API 按钮属性与原样式选择器不一致的问题。简介支持三行截断与展开/收起,尺寸变化及离开页面时更新和清理;选集分段只横向调整滚动,保持桌面/手机切换前的章节位置。评论使用原稿的头像、正文、时间/操作行和嵌套回复容器,保留接口和独立分页路由。
|
||||
|
||||
`tests/detail.test.mjs` 验证动态字段与文本转义、图片解密入口、真实章节链接、配置官方链接和简介展开/尺寸变化/清理;播放测试补充详情页点赞接口、评论回复结构及移动端集数分段。类型检查、生产构建和全部 137 项测试通过。浏览器视觉复核仍受访问工具策略限制,未生成新的浏览器截图。
|
||||
|
||||
## 2026-10-20 登录逻辑调整
|
||||
|
||||
游客登录 403 当时通过匹配实际 HTTP Host 和协议修复了本地监听地址误判。2026-10-26 正式站点复核发现:空参数登录请求不带 Origin 时返回 400,携带正式域名 Origin 时却被本地检查拦截为 403。因此现已按 `jha-1045-hgdjnet-web` 移除代理层 POST 来源拦截,并以 CDN 域名、内部 Host 和协议变化的真实 HTTP 集成测试替代原来源校验单元测试;上游权限错误仍正常返回。
|
||||
|
||||
修复后对用户正在运行的 `http://localhost:3000/api/app/auth/guest` 进行一次正常游客握手,携带浏览器 Origin 和设备标识,真实响应为 HTTP 200 / code 200,返回用户信息及会话 Cookie。未记录或展示登录凭证,未提交正式账号注册、密码或其他业务写操作。
|
||||
|
||||
对照参考项目的 `common/auth.js`、登录/绑定弹窗及 `authser` 调整游客初始化、账号切换、绑定、失效恢复和退出。`tests/auth.test.mjs` 验证账号/邮箱和密码校验、密码确认、协议、设备持久化、首次渠道与邀请码、存储不可用、游客请求合并、登录响应顺序、失败保留原身份、旧请求隔离、正式账号过期只提示一次、手动退出,以及实际弹窗事件的防重复提交和密码清理。
|
||||
|
||||
HTTP 集成测试另验证真实 Route Handler 的登录与绑定参数、游客 Authorization、可选邮箱补全、设备头与客户端 IP、getInfo 不返回 token 时续用原凭证、正式账号初始化过期不降级、游客 GET/POST 失效最多重试一次、HTTP 401、缺少 token 的异常登录响应、Cookie 清理和退出后一次自动登录跳过。类型检查、生产构建和全部 127 项测试通过。测试只连接本地模拟上游,没有提交真实账号登录、绑定或密码重置。
|
||||
|
||||
## 真实接口
|
||||
|
||||
通过本地 Next.js 代理验证首页、配置、底部信息、短剧运营列表、热榜、专题、热搜、应用列表、充值套餐和搜索接口,均返回业务码 200。首页使用服务端请求的实际内容,首页筛选项保持移除。
|
||||
|
||||
上游 `POST /api/app/auth/guest` 在直接请求和代理请求中均出现超时(最长等待 25 秒)。无有效 token 的章节列表返回业务码 1000。真实登录态下的播放、用户资料、评论、收藏、订单和金币解锁尚未完成端到端联调;没有向真实接口提交评论、支付订单或解锁请求。
|
||||
|
||||
## 本地集成测试
|
||||
|
||||
`npm test` 启动独立模拟上游和 Next.js 生产服务,检查:
|
||||
|
||||
- 服务端渲染使用上游标题和栏目;API 文本转义;首页筛选项未恢复。
|
||||
- 首页广告列表显式返回 `null`、轮播/栏目/公告为非数组、数组夹有空项时仍返回 200;有效内容继续显示,空内容显示空状态。
|
||||
- 纯站点地址和带 `/` 的根地址均直接返回首页及 200,无 Location 响应头;首页入口指向根路径,短剧入口单独指向 `/shorts/`。
|
||||
- 游客 token 仅写 HttpOnly Cookie,不返回到前端脚本;设备头按参考格式发送。
|
||||
- 公共请求隔离过期 token;兼容二次编码的 JSON data。
|
||||
- 正式账号会话失效后,写请求不会自动降级为游客重试。
|
||||
- 接口白名单、请求方法、跨站写入拒绝。
|
||||
- 保留真实章节 ID、观看权限和单集金币数。
|
||||
- 退出登录清理会话 Cookie,不调用上游业务写接口。
|
||||
- 30 个页面路径以 `/` 结尾,直接返回 200 且没有 Location 响应头;不带尾斜杠的地址也直接返回 200;`/shorts/` 刷新返回 200;无效路径返回可见的 404;首页和业务页面链接直接包含尾斜杠。
|
||||
- `/navigation/` 直接访问与刷新返回 200,独立渲染入口线路、渠道和 App 下载链接,不加载业务页面运行脚本;首页及其余 30 个路径均不包含入口导航弹窗或隐藏页面的等待状态。
|
||||
- 顶部、悬浮工具和首页底部的下载入口在配置未知时保持隐藏;页面不包含悬浮二维码面板。独立入口页在下载地址为空或无效时不渲染下载按钮。
|
||||
|
||||
`tests/routing.test.mjs` 另检查所有路由类型、中文和含斜线的搜索词、hash 地址不转换或替换、站内链接识别,以及 History 导航的事件通知、重复地址和前进后退记录。
|
||||
|
||||
全部分页使用 `原页面路径/page/页码/`,第一页链接使用原路径;页码、上一页和下一页均为真实链接。`tests/pagination.test.mjs` 覆盖短剧、剧集、专题/题材/标签/栏目、榜单、搜索、个人收藏/历史/已购、充值和 VIP 余额明细,以及详情/播放页的评论分页,检查页码解析、首尾页、无效页码、中文搜索词、排序保留和会员接口参数。播放器测试检查评论页码与排序、真实分页链接;HTTP 集成测试检查 20 类路径的两种尾斜杠形式均直接返回 200、刷新不重定向,以及非法分页路径返回 404。本次生产构建及全部 109 项测试通过。
|
||||
|
||||
排行榜独立使用 `/rank/hot/`、`/rank/new/`、`/rank/follow/`,对应接口 `rankType=1/2/3`。`tests/rank-pages.test.mjs` 检查真实页面路由逻辑、链接与当前页高亮、标题、刷新和翻页后的榜单类型,以及加载中和请求失败时保留导航;HTTP 集成测试检查三条路径两种尾斜杠形式均直接返回 200、独立标题且没有 Location 响应头。原 `/rank/` 仍直接展示热播榜,不重定向。此次生产构建和全部 100 项测试通过。
|
||||
|
||||
`tests/download.test.mjs` 检查下载地址优先级和有效性、各下载入口随配置显示/隐藏、动态插入入口,以及桌面端直接打开链接、移动端点击弹窗与组合键保留浏览器默认操作。
|
||||
|
||||
`tests/render.test.mjs` 检查广告、首页列表及嵌套栏目中的空值和类型异常,保留有效内容顺序及 HTML 转义;弹窗测试另覆盖广告队列中的空项和非对象项。
|
||||
|
||||
`tests/images.test.mjs` 使用参考算法的固定密文验证逐字节解密、100 字节边界、MIME 识别和普通图片兼容,并检查重复请求合并与缓存、并发和优先级、失败重试、超时取消、异步切图、Blob 释放、初始/动态节点和视口懒加载。渲染与 SSR 测试另确认接口图片不直接写入 `src`,视频封面使用 `data-poster`。
|
||||
|
||||
2026-10-19 只读抽取真实首页接口的 3 张图片验证,均返回 HTTP 200,按参考密钥解密后识别为 WebP;图片 CDN 返回 `Access-Control-Allow-Origin: *`。未保存原始图片,也未提交业务写请求。
|
||||
|
||||
`tests/playback.test.mjs` 覆盖播放地址及签名保留、观看权限优先级与旧字段、正片/预告切换、Safari/Chromium/移动壳浏览器选择、HLS 恢复上限、重试重建、切换路由与异步导入竞态、拒绝自动播放后的重试、预告不计入正片记录、真实章节 ID、解锁确认与防重复提交,以及实际控件脚本的播放/重试/解锁事件。HTTP 集成测试覆盖媒体清单、密钥端点两种尾斜杠形式、原始 AES 字节、凭据隔离、Range 和错误状态。
|
||||
|
||||
H.264/H.265 兼容测试另覆盖 MSE/原生 HLS 编码错误回退、签名与播放位置保留、重复错误及重试次数限制、网络错误区别处理、MP4 不误走 HLS,以及回退过程中的路由取消。`tests/codec-manifest.test.mjs` 运行真实 hls.js 1.7.3 完整版的清单解析和 LevelController,在模拟设备能力下验证 H.264 回退、`hvc1`/`hev1` 保留、HEVC 单编码清单接受/拒绝;测试不访问网络,不代表真实浏览器或硬件的解码实播结果。
|
||||
|
||||
2026-10-19 只读验证真实免费预告:清单返回 200,保留 `id/t/ticket` 参数后密钥代理返回 200 和 16 字节 AES 密钥,首片成功通过 AES-128-CBC 解密,输出首字节为 MPEG-TS 同步字节 `0x47`。不带签名参数的密钥请求会被上游拒绝。本次未保存密钥/媒体,也未提交真实观看上报、解锁或订单请求;真实账号付费章节和浏览器实播仍需联调。
|
||||
|
||||
`tests/startup.test.mjs` 对照参考项目的实际脚本规则验证:会话内首次年龄确认、确认早于配置返回、完整广告队列、刷新恢复进度、相同队列不重复弹出、广告码变化后重置、无效图片与失败图片跳过、全部失败后可重试、存储不可用、过期异步结果隔离,以及图片加载超时和取消。DOM 事件适配测试覆盖年龄弹窗禁止 Esc 关闭、广告 Esc/按钮切换下一条、广告链接与展示/点击事件,以及退出行为。
|
||||
|
||||
浏览器访问工具受 URL 策略限制,本次未进行浏览器视觉复核。目录中的桌面、手机截图属于原 HTML 迁移阶段的布局基线,不是当前 API 数据截图。
|
||||
|
||||
## 2026-10-22 部署脚本与本地发布包
|
||||
|
||||
- `bash deploy_test.sh --package-only` 完成测试环境构建、294 项回归测试和 zip/SHA-256 生成;`bash deploy_prod.sh --package-only` 完成生产构建、299 项回归测试和 Linux x64/glibc 包/MD5 生成。构建包含 TypeScript 检查。
|
||||
- 两份脚本的 `--dry-run` 通过命令替身验证,不执行构建、SSH 或 SCP;缺失/非法部署目标在构建前拒绝。测试和生产共用构建锁。
|
||||
- 发布包测试覆盖基础配置隔离、本地覆盖/缓存/旧 dist 排除、符号链接目标保护。Next.js 自动追踪漏带的 Turbopack Route Handler 运行文件已显式包含。
|
||||
- 最终 `dist.zip`(29.87 MiB)解压到临时目录,使用本机裁剪依赖启动 `node start.mjs`;本地模拟 API 下首页、`/healthz`、Next 静态资源和 public 文件均返回 200,健康检查未触发上游请求。测试 zip 按约定不含依赖,真实 Linux 服务器需执行 `npm ci --omit=dev`。
|
||||
- 最终 `web_91dj_h5.tar.gz`(44.94 MiB)通过内容、MD5 和目标平台元信息检查;目标 Sharp/libvips 使用 lockfile 固定版本,并通过 Linux x64 ELF 校验,不含 macOS 原生模块。本机未运行 Linux 生产服务。
|
||||
- `deploy/up_next_h5.sh` 用临时目录和命令替身验证校验失败、安装失败不停止旧服务;健康检查失败恢复旧版本;成功更新保留测试私有覆盖和公开 SEO 缓存。新增部署测试共 8 项通过,已包含在最新生产环境的 299 项回归中。
|
||||
- 本次仅本地构建和打包,未执行任何真实 SSH/SCP、Supervisor 更新或远端 `running.sh`。部署前需配置本站目标地址,测试 Supervisor 程序及生产跳板机脚本由运维预先配置。
|
||||
|
||||
部署配置统一采用本站标识:测试 `root@172.104.181.99:22889` → `/server/web_91dj/h5`;生产 `server@116.212.120.2` → `/home/server/web_91dj/h5/nuxt`,SCP 22889 / SSH 22。包名 `web_91dj_h5.tar.gz`、校验文件和 Supervisor `91dj_next_h5` 使用本站标识;生产仍只通过完整路径调用原有 `running.sh`。部署测试验证当前配置、环境变量覆盖、无效目标提前失败及 dry-run 不连接远端;本次未执行实际上传或部署。
|
||||
|
||||
### 部署配置变化后的测试修复
|
||||
|
||||
已复现 `tests/deploy.test.mjs:72` 的目录断言失败:测试仍固定期望旧项目的上传目录,实际测试脚本已配置 `/server/web_91dj/h5`。修复后不再将主机、路径、端口或 API 域名作为测试不变量,保留对参数覆盖、命令与帮助输出一致、非法目标提前失败、dry-run 不构建/不连接远端的验证。测试服更新的模拟 Supervisor 使用固定测试服务,避免调用者的 `DEPLOY_PROGRAM` 污染测试。两份脚本的帮助内容统一从当前变量生成,修复当时测试 SSH 配置 21021、帮助文案却写 22889 的不一致;保留修复时的上传路径和端口。
|
||||
|
||||
`bash deploy_test.sh --package-only` 已完成测试环境构建、全部 299 项测试和 `dist.zip` 打包;压缩内容及 SHA-256 通过校验。另在设置自定义 `DEPLOY_PROGRAM`、`DEPLOY_DIR`、`DEPLOY_SSH_PORT` 后运行部署专项测试,8 项通过。当前 SSH 端口和应用监听端口分别配置,未连接真实服务器验证 SSH 端口或执行上传/部署。
|
||||
|
||||
### 测试部署改用 tar.gz
|
||||
|
||||
`deploy_test.sh` 现在生成并上传 `web_91dj_h5.tar.gz`、`web_91dj_h5.tar.gz.sha256` 和 `up_next_h5.sh`,包名可通过 `DEPLOY_PACKAGE_NAME` 覆盖;同时刷新对应 MD5 文件供本地兼容校验。测试包继续排除依赖、本地覆盖、缓存和 macOS 附加文件,包内使用 `.next-test` 与 `config/test.env`。保留当前上传目录和端口,以及用户注释的远端自动更新命令;实际脚本上传完成后结束。
|
||||
|
||||
服务器辅助脚本同步使用 tar.gz:先校验 SHA-256、检查成员路径与类型,再解压、安装依赖和切换版本;损坏包、路径越界、链接及特殊文件在停止旧服务前拒绝。测试覆盖真实 tar.gz 上传参数、校验值、自定义包名、自动更新保持禁用,以及安装/健康检查失败的回滚。
|
||||
|
||||
完整本地构建和 304 项测试通过;测试部署专项 13 项通过。生成的测试 tar.gz 为 29.78 MiB、414 个文件,内容、SHA-256 和 MD5 均通过校验。解压后使用本机裁剪依赖和本地模拟 API 验证启动、首页、健康检查、Next 静态资源与 public 文件均返回 200。未执行真实上传或服务器更新。
|
||||
|
||||
## 2026-10-22 埋点检查与补全
|
||||
|
||||
通过项目自带的官方 SDK v1.2.0 复现了内容事件因空 `parent_item_id` 被拒收的问题;此前测试替身未校验字段,所以没有发现。现在回归测试调用真实 SDK 的具名事件方法,仅使用其初始化前缓存和字段校验,不向外发送测试数据。只读获取当前业务接口配置并在本机解析后,确认页面、内容、搜索、广告、弹窗行为、播放事件均在事件允许列表内;未修改服务端配置或打印密文、签名密钥。
|
||||
|
||||
| 范围 | 事件与检查结果 |
|
||||
| --- | --- |
|
||||
| 页面、分页、后退恢复 | `appPageView/pageClick`,保留真实路径及原有访问去重规则 |
|
||||
| 导航与下载 | `navigation`,补齐独立入口页渠道、App 下载入口选择器 |
|
||||
| 首页及列表内容 | `appPageitemExposure/appPageitemClick`,无父级用字符串 `0`,通过真实 SDK 校验 |
|
||||
| 专题、福利应用 | 补齐专题卡片内容元数据;关联 `advitiseId` 的应用按广告上报,其他应用按内容并携带分类父级 |
|
||||
| 大图广告 | `adImpression/adClick`,保留原展示、点击触发;独立展示位置及延迟 SDK 队列防止漏报 |
|
||||
| 宫格广告 | 与首页分开去重,滚动后补报可见项;关闭、遮挡或后台标签页不记曝光,恢复后重新观察 |
|
||||
| 弹窗行为 | 补齐参考项目的 `advertising`:show/click/close,使用真实广告 ID;关闭不记 adClick,重复关闭不重报 |
|
||||
| 搜索 | `keywordSearch/keywordClick`,保留真实总数、首轮搜索及分页后的全局位置 |
|
||||
| 播放、切集、分享、失败 | `dramaEvent/dramaStatusChange`,全部现有行为的载荷通过真实 SDK 校验,预告不作为正片统计 |
|
||||
| 登录、游客、注册、充值、VIP | 沿用 SDK 原始公共字段透传及 UID 同步,成功业务事件由服务端负责 |
|
||||
| 点赞、收藏、评论、购买解锁 | 保留 BFF 校验后的事件上下文和请求 ID 透传,前端不重复发送服务端成功事件 |
|
||||
| 加载、排队、错误 | 配置首读跳过 SDK 等待;脚本失败重试加载;扩大初始化前队列并报告溢出;SDK 拒收和发送失败有脱敏诊断 |
|
||||
|
||||
浏览器使用真实页面及未修改的官方 SDK,测试专用包装通过 SDK 支持的单次事件地址参数把请求送入本机接收端,避开官方域名解析器对配置地址的 HTTPS 要求。已验证大图广告展示/点击、宫格展示/点击、页面访问、内容曝光,以及原生页面跳转前的内容点击均实际发出带 `event_sig` 的网络记录。UI 点击和弹窗滚动经浏览器执行,未向正式统计后台发测试事件。未验证正式部署的反向代理和统计后台入库结果。
|
||||
|
||||
最终验证:测试环境构建(含 TypeScript)及全部 323 项回归测试通过。最后一轮本机浏览器验证收到 78 条带签名的 SDK 事件,其中 `advertising` 为 show 21、click 2、close 21;大图广告的三种行为均到达,宫格关闭只覆盖实际曝光的广告,关闭未增加 `ad_click`。另外一轮已确认原生页面跳转前的 `app_pageitem_click` 实际到达,内容父级字段为字符串 `0`。所有网络验证仅使用本地模拟业务数据和本地测试签名配置。
|
||||
@@ -0,0 +1,7 @@
|
||||
/// <reference types="next" />
|
||||
/// <reference types="next/image-types/global" />
|
||||
import "./.next/types/routes.d.ts";
|
||||
import "./.next/types/root-params.d.ts";
|
||||
|
||||
// NOTE: This file should not be edited
|
||||
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
|
||||
@@ -0,0 +1,36 @@
|
||||
import type { NextConfig } from "next";
|
||||
|
||||
const appEnvironment=process.env.APP_ENV||(process.env.NODE_ENV==='development'?'development':'production');
|
||||
if(!['development','test','production'].includes(appEnvironment))throw new Error('Invalid APP_ENV');
|
||||
|
||||
const nextConfig: NextConfig = {
|
||||
output: 'standalone',
|
||||
outputFileTracingExcludes: {
|
||||
'/*': ['./dist/**/*', './tests/**/*', './.next/cache/**/*', './.next-test/cache/**/*', './.next-dev/cache/**/*'],
|
||||
},
|
||||
// Turbopack's route runtime is external; keep it in relocatable standalone releases.
|
||||
outputFileTracingIncludes: {
|
||||
'/*': ['./node_modules/next/dist/compiled/next-server/*runtime.prod.js'],
|
||||
},
|
||||
distDir: appEnvironment==='development'?'.next-dev':appEnvironment==='test'?'.next-test':'.next',
|
||||
trailingSlash: true,
|
||||
// Page redirects live in src/proxy.ts; API and media URLs keep their exact paths.
|
||||
skipTrailingSlashRedirect: true,
|
||||
// Serve the complete page metadata in <head> to browsers and crawlers alike.
|
||||
htmlLimitedBots: /.*/,
|
||||
images: { unoptimized: true },
|
||||
poweredByHeader: false,
|
||||
turbopack: { root: process.cwd() },
|
||||
async headers() {
|
||||
const xml=[{key:'Content-Type',value:'application/xml; charset=utf-8'}]
|
||||
const xsl=[{key:'Content-Type',value:'text/xsl; charset=utf-8'}]
|
||||
return [
|
||||
{source:'/sitemap.xml',headers:xml},
|
||||
{source:'/sitemaps/:file',headers:xml},
|
||||
{source:'/static/sitemapindex.xsl',headers:xsl},
|
||||
{source:'/static/sitemap.xsl',headers:xsl},
|
||||
]
|
||||
},
|
||||
};
|
||||
|
||||
export default nextConfig;
|
||||
@@ -0,0 +1,42 @@
|
||||
{
|
||||
"name": "91-drama-next",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "node scripts/next-env.mjs development dev",
|
||||
"dev:test": "node scripts/next-env.mjs test dev",
|
||||
"build": "node scripts/next-env.mjs production build",
|
||||
"build:dev": "node scripts/next-env.mjs development build",
|
||||
"build:test": "node scripts/next-env.mjs test build",
|
||||
"start": "node scripts/next-env.mjs production start",
|
||||
"start:dev": "node scripts/next-env.mjs development start",
|
||||
"start:test": "node scripts/next-env.mjs test start",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"test": "node --test tests/*.test.mjs",
|
||||
"check": "npm run build && npm test",
|
||||
"bundle": "node scripts/package-standalone.mjs production",
|
||||
"bundle:test": "node scripts/package-standalone.mjs test",
|
||||
"bundle:linux": "node scripts/prepare-production-runtime.mjs",
|
||||
"deploy:test": "bash deploy_test.sh",
|
||||
"deploy:prod": "bash deploy_prod.sh",
|
||||
"smoke:api": "node scripts/smoke-api.mjs"
|
||||
},
|
||||
"dependencies": {
|
||||
"dotenv": "17.2.3",
|
||||
"hls.js": "1.7.3",
|
||||
"next": "^16.0.0",
|
||||
"qrcode": "^1.5.4",
|
||||
"react": "^19.2.0",
|
||||
"react-dom": "^19.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^22.0.0",
|
||||
"@types/qrcode": "^1.5.6",
|
||||
"@types/react": "^19.2.0",
|
||||
"@types/react-dom": "^19.2.0",
|
||||
"typescript": "^5.9.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.9.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,215 @@
|
||||
---
|
||||
项目名称: 91短剧web
|
||||
一级部门: 北斗
|
||||
二级部门: 瑞升
|
||||
|
||||
DMP编码: JHA-1047
|
||||
|
||||
项目类型: PC
|
||||
# 可选值:
|
||||
# H5 / APP / PC / 服务 / 组件 / 工具 / AI / 中间件
|
||||
|
||||
开发语言: Node
|
||||
# 可选值:
|
||||
# GO / Java / PHP / Node
|
||||
|
||||
项目负责人: DN1425 # 员工编号, 多个用逗号隔开,这里的负责是这套代码的负责人
|
||||
|
||||
运维负责人: DN9460, DN9651, DN4792 # 负责部署、监控、K8S、发布、证书等
|
||||
|
||||
产品负责人: YY1166 # 项目产品经理
|
||||
|
||||
项目成员: DN7208, DN2185, DN8431
|
||||
|
||||
项目状态: 维护中
|
||||
# 可选值:
|
||||
# 开发中 / 维护中 / 已停止 / 已归档
|
||||
|
||||
项目级别: 中
|
||||
# 可选值:
|
||||
# 高 / 中 / 低 , 北斗体系 s和a为 高 b为中 c d为低
|
||||
|
||||
核心分支: main
|
||||
# 即部署时候使用的分支,可选值:
|
||||
# master / main / dev / release
|
||||
|
||||
代码仓库:
|
||||
- ssh://git@bd-git-sh.jsyyds.com:8082/ruisheng/jha-1021-91jhs/frontend/jha-1047-91dj-web.git
|
||||
|
||||
最后审计人:
|
||||
|
||||
最后审计时间:
|
||||
|
||||
告警群:
|
||||
|
||||
备注: Next.js 网站与 API 代理服务;业务数据由独立 Go 后端提供。
|
||||
|
||||
---
|
||||
|
||||
# 1. 项目说明
|
||||
|
||||
91短剧 Web 项目,DMP 编码 **JHA-1047**,应用及埋点 App ID 为 **1047**。采用 Next.js App Router、React、TypeScript,实现 PC 和移动端响应式页面,布局依据提供的 HTML 原稿还原。
|
||||
|
||||
本文按当前仓库代码整理,更新日期为 **2026-10-26**;该日期不代表安全审计或线上验收时间。人员信息沿用原登记信息,部署目标以当前脚本默认值为准。
|
||||
|
||||
## 1.1 功能范围
|
||||
|
||||
- 首页动态栏目、轮播、顶部及侧栏导航、广告与宫格广告弹窗。
|
||||
- 短剧列表、排行榜、专题、题材/标签、栏目更多页、搜索及独立分页。
|
||||
- 短剧详情、章节播放、HLS 播放、图片解密、点赞、收藏、评论与分享。
|
||||
- 游客/账号登录、个人中心、观看历史、我的收藏、我的点赞、已购剧集、账号设置。
|
||||
- 金币充值、VIP 套餐、章节解锁、反馈、App 下载、福利应用、回家的路和独立导航页。
|
||||
- 服务端 SEO 元数据、JSON-LD、robots.txt 与广告/页面/播放等埋点。
|
||||
|
||||
本仓库负责网站页面、浏览器交互和服务端 API 代理,不包含 Go 业务服务、管理后台或数据库部署代码。独立“剧集”入口及 `/series/`、`/video/*` 页面已移除。
|
||||
|
||||
## 1.2 主要目录与路由
|
||||
|
||||
| 位置 | 职责 |
|
||||
| --- | --- |
|
||||
| `src/app/` | App Router 页面、API/媒体代理、robots 和健康检查 |
|
||||
| `src/components/site/` | 首页、详情、播放、导航等页面组件 |
|
||||
| `src/lib/api/` | 上游配置、接口白名单、会话、公共缓存和媒体请求 |
|
||||
| `src/lib/client/` | 登录、账户、播放、解锁、广告、图片及埋点交互 |
|
||||
| `src/lib/routing.ts` | 路由解析、slug/ID、分页地址与导航匹配 |
|
||||
| `src/lib/seo*.ts`、`src/lib/structured-data.ts` | TDK、canonical、索引策略、SEO 快照和 JSON-LD |
|
||||
| `src/proxy.ts` | 页面尾斜杠规范化和首屏会话准备 |
|
||||
| `config/`、`scripts/`、`deploy/` | 环境配置、启动/打包脚本及辅助更新脚本 |
|
||||
| `public/`、`tests/`、`docs/` | 静态素材、回归测试、接口与收录规则说明 |
|
||||
|
||||
| 页面 | 主要路径 |
|
||||
| --- | --- |
|
||||
| 首页 / 短剧 | `/`、`/shorts/` |
|
||||
| 三个排行榜 | `/rank/hot/`、`/rank/new/`、`/rank/follow/` |
|
||||
| 专题 / 标签 / 更多栏目 | `/topics/`、`/topics/{key}/`、`/tag/{key}/`、`/config/{key}/` |
|
||||
| 详情 / 播放 | `/detail/{key}/`、`/play/{key}/{chapterId}/` |
|
||||
| 搜索 | `/search/{keyword}/` |
|
||||
| 个人中心及书架 | `/member/center/`、`/member/history/`、`/member/favorites/`、`/member/likes/`、`/member/purchased/` |
|
||||
| 充值 / VIP | `/info/deposit/`、`/info/deposit/vip/` |
|
||||
| 官方入口 / 回家的路 / 福利应用 | `/navigation/`、`/home-road/`、`/apps/` |
|
||||
|
||||
内容地址优先使用有效英文/拼音 slug,无效或缺失时使用 ID;播放页章节段使用真实章节 ID。搜索词作为单个路径段编码。支持分页的页面使用 `/page/{N}/`,第一页使用基础路径;详情和播放页的分页对应评论。有效页面无尾斜杠访问返回 308,API、媒体和静态资源保留原路径。确认不存在的资源或越界分页返回 404,上游业务服务异常与资源不存在分别处理。
|
||||
|
||||
# 2. 基础设施与中间件
|
||||
|
||||
## 2.1 运行依赖与请求链路
|
||||
|
||||
- Node.js 最低 **20.9.0**,推荐 **22.17.1**;生产跨平台打包要求 **npm >=10.9.2**。依赖版本以 [package-lock.json](package-lock.json) 为准,使用 `npm ci` 安装。
|
||||
- Next.js 16、React 19、TypeScript 5.9;主要运行库包括 `hls.js`、`qrcode`、`dotenv`。
|
||||
- 网站需要运行 Node.js 服务,使用 Next.js `standalone` 产物,不是静态 `out/` 部署。
|
||||
- 页面在服务端取数并输出当前页面正文与 head;浏览器复用首屏数据绑定交互,页面链接和分页使用原生路径访问。
|
||||
- 浏览器请求本站 `/api/*`,Next.js 按白名单转发到 Go 后端的 `/api/app/*`。`UPSTREAM_API_URL` 仅服务端使用,`API_BASE_URL` 是兼容别名。
|
||||
- `/h5/m3u8/*`、`/enkey`、`/newenkey` 由媒体代理处理,保留签名查询、客户端相关请求信息;媒体和密钥不缓存,不转发应用登录 Cookie 或 Authorization。
|
||||
|
||||
登录会话通过 HttpOnly Cookie 传递;SSR 启动数据按字段白名单输出,不向浏览器暴露后端登录凭证。公共接口和账户、余额、订单、付费权限数据采用不同缓存策略。
|
||||
|
||||
本仓库不直接连接 MongoDB、Redis 或 Elasticsearch。Go 后端是否使用这些中间件及其拓扑,由后端/运维文档确认;本仓库没有 Telegram Bot 部署通知实现。
|
||||
|
||||
## 2.2 环境配置
|
||||
|
||||
| 环境 | 配置文件 | Go API 上游 | 网站端口 | 构建目录 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| 开发 | `config/development.env` | `http://172.104.181.99:21022` | `21021` | `.next-dev/` |
|
||||
| 测试 | `config/test.env` | `http://172.104.181.99:21022` | `21021` | `.next-test/` |
|
||||
| 生产 | `config/production.env` | `http://172.104.56.28:26125` | `21021` | `.next/` |
|
||||
|
||||
配置由 [scripts/next-env.mjs](scripts/next-env.mjs) 加载,优先级为:进程环境变量 > `config/{环境}.local.env` > `config/{环境}.env`。开发环境兼容旧 `.env.local`,优先级低于 `config/development.local.env`;测试和生产不从旧 `.env.local` 加载本站业务配置。
|
||||
|
||||
`APP_ENV` 区分业务环境;测试构建/启动仍使用 `NODE_ENV=production`,`dev` 命令使用 `NODE_ENV=development`。服务端配置修改后需重启,`NEXT_PUBLIC_*` 修改后需重新构建。私有覆盖文件已忽略提交和打包。
|
||||
|
||||
## 2.3 缓存、SEO 与外部配置
|
||||
|
||||
- 公共配置、页脚、导航、分类和标签选项默认缓存 60 秒,首页缓存 30 秒,进程内最多 128 条并合并并发请求;开发配置关闭缓存。`PUBLIC_DATA_CACHE_TTL_MS=0` 可关闭,正数仅缩短既有缓存上限。
|
||||
- 公共缓存按上游地址隔离,各实例独立;配置和首页刷新失败可沿用已成功的数据。账户及付费权限不进入公共缓存。
|
||||
- canonical 和站点身份取后台配置的永久域名,优先 `config.foreverUrlAddr`,其次 `newAddressInfo.foreverUrlAddr`。有效 SEO 配置保存在对应构建目录的 `cache/seo/`,异常时复用最近一次有效快照;不以 API IP 或访问 Host 作为正式域名。
|
||||
- 生产 `/robots.txt` 允许抓取公开资源,限制 `/api/` 和 `/healthz`;开发、测试禁止全站抓取。账户、充值、搜索、工具等页面另有页面级 `noindex` 规则。
|
||||
- 生产 robots 在取得有效永久域名后声明 `https://{永久域名}/sitemap.xml` 形式的绝对 URL。**本仓库没有 sitemap XML 生成或读取路由**;现有 XSL 和响应头配置不等于已提供 XML,需由后端/反向代理另行接入。规则见 [后端 Sitemap 对接约定](docs/sitemap-backend-contract.md)。
|
||||
- 导航、官方入口、商务 TG、用户反馈 TG 群、广告和下载地址由接口动态配置;未配置有效 App 下载链接时隐藏入口。
|
||||
- 埋点使用 App ID `1047`,优先读取接口 `reportSdkConfig`,构建配置 `NEXT_PUBLIC_TRACKER_ENCRYPTED_CONFIG` 为兜底;配置及上报链路需与后端保持一致。
|
||||
|
||||
# 3. 运维部署
|
||||
|
||||
## 3.1 开发、验证和启动
|
||||
|
||||
```sh
|
||||
nvm use 22.17.1
|
||||
npm ci
|
||||
npm run dev # 开发配置,默认 http://localhost:21021
|
||||
npm run typecheck # 类型检查
|
||||
npm test # 本地回归测试
|
||||
npm run check # 生产构建 + 回归测试
|
||||
```
|
||||
|
||||
| 环境 | 构建 | 启动 |
|
||||
| --- | --- | --- |
|
||||
| 开发 | `npm run build:dev` | `npm run dev`;已有构建用 `npm run start:dev` |
|
||||
| 测试 | `npm run build:test` | `npm run start:test`;开发调试可用 `npm run dev:test` |
|
||||
| 生产 | `npm run build` | `npm start` |
|
||||
|
||||
启动前确认 `21021` 未被本项目的旧进程或其他服务占用。不同环境须使用对应构建产物,不能互换构建目录。
|
||||
|
||||
## 3.2 打包与发布目标
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
| 环境 | 默认 SSH 目标 | 上传及执行目录 | SCP / SSH 端口 | 上传后执行 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| 测试 | `root@172.104.181.99` | `/server/web_91dj/h5` | `22889 / 22889` | `bash run.sh` |
|
||||
| 生产 | `server@34.92.204.175` | `/home/server/web_91dj/h5` | `22889 / 22889` | `bash rsupdate.sh` |
|
||||
|
||||
直接执行 `bash deploy_test.sh` 或 `bash deploy_prod.sh` 会构建、测试、打包、上传并执行远端脚本。生产上传主机与生产 API 主机不同,不要混用。
|
||||
|
||||
- **测试脚本**:[deploy_test.sh](deploy_test.sh)。执行 `build:test`、测试、`bundle:test`,生成 `web_91dj_h5.tar.gz` 和 `web_91dj_h5.tar.gz.sha256`,上传这两份文件及 `deploy/up_next_h5.sh`;同时刷新本地兼容 MD5 文件。测试包不含 `node_modules`,Linux 解包后需 `npm ci --omit=dev --no-audit --no-fund`,再运行 `node start.mjs`。
|
||||
- **生产脚本**:[deploy_prod.sh](deploy_prod.sh)。执行 `check`、`bundle`、`bundle:linux`,生成 `web_91dj_h5.tar.gz` 和纯哈希格式 `web_91dj_h5.md5`,两份文件一并上传。包内含 standalone 裁剪依赖,默认 Linux x64/glibc,可运行 `node start.mjs`;打包时安装并校验目标平台 Sharp/libvips。
|
||||
- 两种包只带对应环境的基础配置,不包含私有 `*.local.env`、`.env*` 和 SEO 缓存。服务器覆盖配置放在实际运行目录的 `config/` 下。
|
||||
- 测试与生产使用**同名压缩包**,后一次打包会覆盖根目录旧产物;发布前核对环境和校验文件。`dist/`、压缩包和校验文件均已忽略提交,两份脚本共用 `dist/.deploy.lock` 防止并发打包。
|
||||
|
||||
测试目标可通过 `DEPLOY_HOST`、`DEPLOY_DIR`、`DEPLOY_RUN_DIR`、`DEPLOY_SSH_PORT` 覆盖;上传目录和执行目录分别配置。生产使用 `DEPLOY_JUMP_HOST`、`DEPLOY_JUMP_DIR`、`DEPLOY_SCP_PORT`、`DEPLOY_SSH_PORT`,执行目录跟随上传目录。两者支持 `DEPLOY_PACKAGE_NAME`;生产另支持 `DEPLOY_TARGET_ARCH=x64|arm64`、`DEPLOY_TARGET_LIBC=glibc|musl`。
|
||||
|
||||
上传失败不会执行远端更新;远端命令失败会使发布退出。生产脚本还检查远端输出中的失败状态,并将执行日志写入 `dist/production-rsupdate.*`。
|
||||
|
||||
## 3.3 服务管理、检查和回退
|
||||
|
||||
远端现有 `run.sh`、`rsupdate.sh` 不在本仓库,发布脚本不会覆盖它们。服务启动方式、私有配置保留、日志位置和线上回退步骤,需由运维结合服务器脚本确认。
|
||||
|
||||
仓库提供的 [deploy/up_next_h5.sh](deploy/up_next_h5.sh) 是测试环境辅助更新脚本,默认 Supervisor 程序为 `91dj_next_h5`,可通过 `DEPLOY_PROGRAM` 覆盖。它支持 SHA-256 校验、解包、安装依赖、保留本地配置和 SEO 缓存、切换版本、检查和失败回退;是否由服务器 `run.sh` 调用,应核对远端实现。
|
||||
|
||||
`/healthz` 返回进程状态、应用名和环境,不请求 Go 后端。发布后可执行:
|
||||
|
||||
```sh
|
||||
node scripts/check-health.mjs http://127.0.0.1:21021 production
|
||||
# 测试环境将最后一个参数改为 test
|
||||
PREVIEW_URL=http://127.0.0.1:21021 npm run smoke:api
|
||||
```
|
||||
|
||||
健康检查同时检查 `/healthz` 和首页;测试发布可设置 `DEPLOY_CHECK_URL` 自动执行。API 冒烟检查可能创建临时游客会话,使用 `SMOKE_PUBLIC_ONLY=1` 可仅检查公共部分。支付、解锁、媒体播放及埋点仍需对应业务验收,不能仅以进程存活判定全部功能正常。
|
||||
|
||||
# 4. 风险说明
|
||||
|
||||
| 风险点 | 影响及处理要求 |
|
||||
| --- | --- |
|
||||
| 上游及反向代理配置错误 | SSR、登录或媒体可能失败;区分网站 `21021`、API `21022/26125` 和 SSH `22889`。本站 `/api/*` 应进入 Next.js,避免上游回环;如启用可选 `current` 模式,必须先确认 `/api/app/*` 转发到 Go。 |
|
||||
| 播放签名、IP、UA 或密钥异常 | 可能出现播放 502;保留签名查询和客户端信息,检查真实上游响应,AES 密钥必须为 16 字节。H.265 解码受设备及浏览器限制,前端不提供转码。 |
|
||||
| 个人数据误入共享缓存 | 可能造成身份或权限混用;新增接口需延续公开数据与账户、订单、付费权限隔离,首屏输出遵守字段白名单。 |
|
||||
| 发布包平台或环境不匹配 | 生产需匹配 Linux 架构/libc,测试包需安装运行依赖;同名产物发布前核对配置和校验值。 |
|
||||
| SEO 配置或 sitemap 未接入 | 测试环境禁止抓取;生产需核对 `APP_ENV`、永久域名、robots、页面索引标记及实际 sitemap 响应,不能只确认 Sitemap 声明。 |
|
||||
| 服务器更新脚本未交接 | 本地脚本成功不等于业务验收通过;运维应提供远端脚本、服务管理、日志、监控告警及回退说明。 |
|
||||
|
||||
# 5. 历史事故(可选)
|
||||
|
||||
仓库内未提供经确认的生产事故台账。负责人可按“时间、环境、影响范围、原因、处置及预防措施”补充;开发调试错误不直接登记为生产事故。
|
||||
|
||||
# 6. 交接说明(可选)
|
||||
|
||||
- **应用标识**:DMP 为 `JHA-1047`;三套环境的 `NEXT_PUBLIC_TRACKER_APP_ID` 均为 `1047`,对接后台与 SDK 时保持一致。
|
||||
- **新增页面**:复用集中路由、slug/ID、分页和 SEO 规则,补充有效/缺失/异常状态处理。导航与下载入口优先消费动态配置,不写死演示链接。
|
||||
- **个人列表**:我的点赞入口为 `/member/likes/`,上游接口为 `GET /api/app/playlet/like/list`,与收藏、历史、已购共同保持私有响应及禁止索引。
|
||||
- **配置交接**:确认实际运行目录的私有覆盖、上游连通性、正式永久域名、TG 联系方式、广告、下载及埋点配置。新版本切换需保留服务器私有配置和对应 SEO 快照。
|
||||
- **发布交接**:确认 SSH 权限、远端 `run.sh`/`rsupdate.sh`、服务管理名称、日志与回退方法;本文记录的是仓库默认值,不替代服务器实际配置验收。
|
||||
- **待补登记**:最后审计人、最后审计时间、告警群,以及运维确认后的监控和事故台账;不将本次文档整理视为已完成审计。
|
||||
- **参考资料**:[运行与部署说明](README.md)、[页面接口对照](docs/api-contract.md)、[索引策略](docs/indexation-policy.md)、[Sitemap 对接约定](docs/sitemap-backend-contract.md)。历史核对记录以其日期为界,当前配置和行为以代码为准。
|
||||
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 106 KiB |
|
After Width: | Height: | Size: 61 KiB |
|
After Width: | Height: | Size: 184 KiB |
|
After Width: | Height: | Size: 39 KiB |
|
After Width: | Height: | Size: 452 KiB |
|
After Width: | Height: | Size: 114 KiB |
|
After Width: | Height: | Size: 134 KiB |
|
After Width: | Height: | Size: 60 KiB |
|
After Width: | Height: | Size: 91 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="37.0911" height="37.0911" viewBox="0 0 37.0911 37.0911" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path id="Star 7" d="M16.3279 0.979548C17.5179 -0.326515 19.5732 -0.326516 20.7632 0.979547L21.3914 1.66906C22.1808 2.53547 23.4024 2.86281 24.5193 2.50717L25.4081 2.22414C27.0916 1.68803 28.8716 2.71569 29.2491 4.44175L29.4483 5.35299C29.6988 6.49801 30.5931 7.39234 31.7381 7.64276L32.6494 7.84205C34.3754 8.21954 35.4031 9.99949 34.867 11.6831L34.5839 12.5719C34.2283 13.6887 34.5556 14.9104 35.422 15.6997L36.1116 16.3279C37.4176 17.5179 37.4176 19.5732 36.1116 20.7632L35.422 21.3914C34.5556 22.1807 34.2283 23.4024 34.5839 24.5193L34.867 25.408C35.4031 27.0916 34.3754 28.8716 32.6494 29.2491L31.7381 29.4483C30.5931 29.6988 29.6988 30.5931 29.4483 31.7381L29.2491 32.6494C28.8716 34.3754 27.0916 35.4031 25.4081 34.867L24.5193 34.5839C23.4024 34.2283 22.1808 34.5556 21.3914 35.422L20.7632 36.1116C19.5732 37.4176 17.5179 37.4176 16.3279 36.1116L15.6997 35.422C14.9104 34.5556 13.6887 34.2283 12.5719 34.5839L11.6831 34.867C9.9995 35.4031 8.21954 34.3754 7.84205 32.6494L7.64276 31.7381C7.39234 30.5931 6.49801 29.6988 5.35299 29.4483L4.44175 29.2491C2.71569 28.8716 1.68803 27.0916 2.22414 25.4081L2.50717 24.5193C2.86281 23.4024 2.53547 22.1808 1.66906 21.3914L0.979548 20.7632C-0.326515 19.5732 -0.326517 17.5179 0.979547 16.3279L1.66906 15.6997C2.53547 14.9104 2.86281 13.6887 2.50717 12.5719L2.22414 11.6831C1.68803 9.9995 2.71569 8.21954 4.44175 7.84205L5.35299 7.64276C6.49801 7.39234 7.39234 6.49801 7.64276 5.35299L7.84205 4.44175C8.21954 2.71569 9.99949 1.68803 11.6831 2.22414L12.5719 2.50717C13.6887 2.86281 14.9104 2.53547 15.6997 1.66906L16.3279 0.979548Z" fill="#FF0095"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.7 KiB |
@@ -0,0 +1,5 @@
|
||||
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="Frame">
|
||||
<path id="Vector" d="M42.0004 13.0039V33.3979C42.0048 33.7924 41.9292 34.1836 41.7783 34.548C41.6273 34.9125 41.404 35.2425 41.122 35.5183C40.8476 35.8023 40.5178 36.0267 40.153 36.1779C39.7883 36.329 39.3963 36.4035 39.0016 36.3967H28.7956C28.8034 36.8916 28.9061 37.3804 29.098 37.8367C29.2742 38.2906 29.4737 38.7352 29.6956 39.1687C29.8516 39.4161 29.9545 39.6931 29.998 39.9823C29.9951 40.3003 29.8676 40.6045 29.6427 40.8294C29.4178 41.0543 29.1136 41.1819 28.7956 41.1847H19.2016C18.8835 41.1819 18.5793 41.0543 18.3544 40.8294C18.1296 40.6045 18.002 40.3003 17.9992 39.9823C18.0395 39.6884 18.1439 39.407 18.3052 39.1579C18.528 38.7311 18.7275 38.2924 18.9028 37.8439C19.0975 37.381 19.2002 36.8845 19.2052 36.3823H9.00276C8.60816 36.3878 8.2166 36.3127 7.85201 36.1617C7.48743 36.0106 7.15748 35.7868 6.88236 35.5039C6.59954 35.2315 6.37542 34.9042 6.22374 34.542C6.07206 34.1799 5.99605 33.7905 6.00036 33.3979V13.0039C5.99488 12.6093 6.06996 12.2178 6.22099 11.8532C6.37203 11.4886 6.59584 11.1586 6.87876 10.8835C7.15207 10.5987 7.48101 10.3731 7.84515 10.2208C8.20929 10.0684 8.60086 9.99258 8.99556 9.99791H38.9944C39.7913 9.99886 40.5553 10.3159 41.1189 10.8794C41.6824 11.4429 41.9994 12.207 42.0004 13.0039ZM39.5956 28.6063V13.0039C39.5956 12.9255 39.58 12.8479 39.5497 12.7756C39.5194 12.7032 39.4751 12.6377 39.4192 12.5827C39.3641 12.5259 39.2981 12.4809 39.225 12.4506C39.1519 12.4202 39.0735 12.4052 38.9944 12.4063H9.00276C8.92435 12.4063 8.84673 12.4219 8.77441 12.4521C8.70209 12.4824 8.63653 12.5268 8.58156 12.5827C8.52516 12.6373 8.48045 12.7028 8.45013 12.7752C8.4198 12.8476 8.4045 12.9254 8.40516 13.0039V28.6027C8.40464 28.6817 8.41998 28.76 8.45028 28.833C8.48059 28.906 8.52522 28.9721 8.58156 29.0275C8.63616 29.0839 8.70165 29.1286 8.77405 29.1589C8.84645 29.1893 8.92426 29.2046 9.00276 29.2039H38.998C39.077 29.2044 39.1553 29.1891 39.2282 29.1588C39.3012 29.1285 39.3674 29.0838 39.4228 29.0275C39.479 28.9736 39.5237 28.9087 39.5541 28.8369C39.5844 28.7651 39.5997 28.6879 39.5992 28.6099L39.5956 28.6063Z" fill="#EEEEEE"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 109 KiB |
|
After Width: | Height: | Size: 241 KiB |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024" aria-hidden="true"><path d="M512 204.8C269.06624 204.8 40.96 358.68672 40.96 512s228.10624 307.2 471.04 307.2 471.04-153.88672 471.04-307.2-228.10624-307.2-471.04-307.2z" fill="#A3A3AD"/><path d="M512 337.92a174.08 174.08 0 1 0 0 348.16 174.08 174.08 0 0 0 0-348.16z m0 61.44a112.64 112.64 0 1 1 0 225.28 112.64 112.64 0 0 1 0-225.28z" fill="#232326"/></svg>
|
||||
|
After Width: | Height: | Size: 422 B |
@@ -0,0 +1,28 @@
|
||||
<svg width="250" height="176" viewBox="0 0 250 176" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_45235_124956)">
|
||||
<path d="M463.454 54.534C463.262 53.8027 463.416 53.1163 463.653 52.4203C464.577 49.7324 467.858 45.8321 469.446 43.4745C472.333 39.1893 477.91 30.1569 481.682 27.1515C488.12 22.0227 496.075 28.8579 490.991 35.4975C483.145 41.0849 475.668 47.9202 467.678 53.2414C466.36 54.1203 465.08 55.4931 463.454 54.5373V54.534Z" fill="#FCD02B"/>
|
||||
<path d="M477.637 63.0442C477.557 63.0634 477.541 63.458 477.287 63.5414C476.857 63.6793 472.784 63.891 472.3 63.814C468.188 63.1629 475.44 58.6595 476.331 58.0373C479.699 55.6926 490.817 48.2319 494.364 48.1389C499.839 47.9945 502.149 55.1248 497.52 58.0565C496.635 58.6178 495.708 58.8199 494.727 59.1118C489.158 60.7604 483.276 61.6521 477.64 63.0442H477.637Z" fill="#FCD02B"/>
|
||||
<path d="M25.7009 139.13C20.6202 135.371 15.6325 130.791 10.6576 126.765C7.80935 124.462 2.48809 121.742 2.46884 117.611C2.44318 112.409 7.91199 109.673 12.3416 111.931C15.0423 113.307 19.876 120.831 21.8101 123.632C24.0362 126.849 30.6533 135.99 31.6989 139.092C32.1191 140.346 31.7502 141.818 30.2363 141.805C29.8931 141.805 26.2301 139.518 25.7041 139.13H25.7009Z" fill="#FCD02B"/>
|
||||
<path d="M3.05646e-05 141.885C0.0128606 141.85 0.429837 141.828 0.535685 141.581C1.84756 138.582 3.2332 137.071 6.83203 137.324C10.3988 137.574 22.3243 146.126 25.5543 148.749C26.6609 149.651 28.6592 150.815 28.1043 152.515C27.4852 154.407 22.5104 152.416 21.2177 152.121C16.0184 150.937 8.95862 149.785 4.09923 148.069C2.29982 147.434 1.1804 146.398 0.5421 144.57C0.4138 144.201 -0.0512896 142.007 3.05646e-05 141.882V141.885Z" fill="#FCD02B"/>
|
||||
<path d="M62.6914 41.4902C91.9149 22.1875 138.542 29.013 150.805 64.8789C151.202 66.0399 151.398 68.4937 151.748 69.251H151.745C156.55 79.6689 152.717 100.139 148.759 111.202C136.041 146.745 104.726 173.682 66.1719 175.52C60.6742 175.783 55.2529 175.279 49.7969 174.868C47.7924 174.718 46.2465 175.311 44.4023 174.169C42.9301 173.258 42.2085 171.606 42.2373 169.894C42.3175 165.416 50.1729 151.525 53.6562 148.57C57.8612 145.004 60.337 146.356 65.209 146.01C74.0264 145.381 82.6614 142.241 89.7051 137.147C91.6456 135.746 93.4454 134.011 95.1807 132.362C83.8422 135.785 72.8274 136.369 61.582 132.292C49.0791 127.76 40.9418 118.557 37.0479 105.826C35.0657 99.3536 35.4402 95.803 35.2061 89.0674C34.6063 71.7757 48.1678 51.0839 62.6914 41.4902ZM83.6387 61.96H83.6377L83.6367 61.9609C81.8856 62.0572 79.509 63.9522 78.6172 65.4404C77.4304 67.4291 77.5809 69.3763 77.2217 71.583H77.2246C75.5439 81.8373 74.5848 92.2168 73.0869 102.5C73.6188 107.482 76.7179 109.783 80.9043 109.213C81.0412 109.194 81.1792 109.173 81.3184 109.148C81.4461 109.126 81.5746 109.1 81.7041 109.072C81.9433 109.021 82.1856 108.961 82.4307 108.892C82.7598 108.798 83.094 108.69 83.4326 108.564C83.468 108.551 83.5046 108.54 83.54 108.526C83.7078 108.463 83.8764 108.396 84.0469 108.33C85.092 107.923 86.1883 107.465 87.3223 106.966C95.4105 103.406 105.421 97.7665 112.517 93.8809C116.58 91.6581 120.673 90.1277 120.218 84.5596C120 81.8944 118.492 80.8585 116.757 79.2773C107.343 74.5816 98.2334 67.1369 88.7842 62.749C87.251 62.037 85.2713 61.8701 83.6387 61.96ZM65.6748 42.0742C65.1103 42.0486 64.5483 42.0327 63.9902 42.1514C61.9786 42.5723 54.6342 49.179 53.0049 51.0488C52.7096 51.389 51.1995 53.2195 51.2471 53.4541C51.587 55.1087 53.8085 51.8105 54.1143 51.5234C57.6906 48.1684 61.1839 45.7723 65.0713 42.8984C65.4305 42.6322 65.87 42.9015 65.6777 42.0742H65.6748ZM66.2266 41.3975L66.5186 42.0098L68.7822 40.9355L68.4902 40.3223L66.2266 41.3975ZM68.8916 40.3135C70.0687 39.9478 71.1336 39.3478 72.2979 38.8955C72.7565 38.7191 73.2311 39.2029 73.0547 38.2246H73.0518L68.8916 40.3135ZM74.0137 37.9072C73.5648 37.6767 73.1579 38.1094 73.376 38.2217C73.825 38.4524 74.2318 38.0195 74.0137 37.9072Z" fill="#FCD02B"/>
|
||||
<path d="M83.6384 61.9609C83.138 64.0939 86.7176 61.9032 86.5251 63.5615C82.9712 63.6705 80.3058 65.3032 79.4879 68.8763L78.1824 78.316C77.6211 78.316 77.8296 76.9112 77.852 76.5391C77.9899 74.3804 78.7084 72.1833 78.5064 69.9797L77.2266 71.5835C77.589 69.3799 77.4351 67.4297 78.6218 65.4411C79.5103 63.9528 81.8903 62.054 83.6416 61.9609H83.6384Z" fill="#FDFDFD"/>
|
||||
<path d="M65.6739 42.0729C65.8664 42.9037 65.4301 42.631 65.0677 42.8972C61.177 45.7712 57.6872 48.1672 54.1108 51.5222C53.8061 51.8077 51.5833 55.1082 51.2433 53.4532C51.1952 53.219 52.7059 51.3875 53.001 51.0475C54.6337 49.1743 61.9757 42.5701 63.9868 42.1499C64.5481 42.0312 65.1062 42.0472 65.6707 42.0729H65.6739Z" fill="#FCD02B"/>
|
||||
<path d="M68.4922 40.3143L66.2266 41.3906L66.5183 42.0048L68.784 40.9286L68.4922 40.3143Z" fill="#FCD02B"/>
|
||||
<path d="M73.0501 38.2246C73.2297 39.2029 72.7518 38.7154 72.2931 38.895C71.132 39.3472 70.0671 39.947 68.8867 40.3127L73.0469 38.2246H73.0501Z" fill="#FCD02B"/>
|
||||
<path d="M74.0153 37.9066C74.2334 38.0189 73.8261 38.4519 73.377 38.221C73.1589 38.1087 73.5662 37.6757 74.0153 37.9066Z" fill="#FCD02B"/>
|
||||
<path d="M470.626 65.4876H486.343C487.536 65.4876 486.282 68.6374 486.208 69.2019C485.342 75.726 483.924 82.0544 482.754 88.5207C479.184 108.276 476.396 128.73 472.288 148.299C470.972 154.563 469.045 159.859 462.534 162.281C458.479 163.788 447.702 164.186 443.346 163.65C442.506 163.548 442.204 163.496 442.224 162.528C442.564 158.496 442.105 153.604 442.554 149.675C442.865 146.952 444.241 148.174 445.931 148.235C449.729 148.376 455.92 148.819 456.847 144.081C460.243 123.662 464.009 103.224 467.492 82.8787C468.358 77.8173 468.843 72.31 469.924 67.3479C470.065 66.6968 470.071 65.9463 470.626 65.4844V65.4876Z" fill="#FDFDFD"/>
|
||||
<path d="M464.532 74.791C464.638 74.8199 465.1 75.2914 465.171 75.4389C465.562 76.2601 464.212 83.2492 463.958 84.8017C460.911 103.389 457.203 121.903 454.025 140.455C453.948 140.551 453.377 140.866 453.309 140.866H440.479C440.367 140.866 440.097 140.507 440.043 140.333C443.408 118.641 447.443 96.9678 451.199 75.317C451.292 75.1855 452.241 74.791 452.35 74.791H464.539H464.532Z" fill="#FDFDFD"/>
|
||||
<path d="M446.562 66.7787L389.462 66.7562C386.697 66.6247 387.044 69.5564 386.71 71.3815C383.647 88.1664 381.184 105.195 377.549 121.881C374.829 134.371 370.381 146.364 365.242 158.02L365.993 158.898C368.607 159.902 371.227 160.916 373.816 161.981C374.701 162.347 377.309 164.069 378.05 163.601C378.489 163.322 382.107 155.405 382.637 154.235C387.284 143.961 390.424 133.7 392.763 122.663C392.987 121.595 392.375 120.312 393.645 120.017C393.911 119.956 394.87 119.812 394.764 120.335L393.481 120.659H407.594L403.428 120.02L408.236 119.696L406.911 127.029C406.244 127.458 395.948 126.785 393.949 127.057C393.353 127.138 393.019 127.15 392.695 127.728L386.168 162.273C386.053 162.529 386.47 162.994 386.588 162.994H429.89C431.198 162.994 431.695 160.804 432.003 160.037C432.116 159.758 432.574 159.781 432.619 159.566C432.764 158.886 432.318 158.19 432.382 157.471C433.193 148.5 436.42 138.55 437.402 129.447C437.543 128.899 436.731 127.074 436.305 127.074H421.71L422.673 120.655C427.696 120.315 433.293 121.111 438.245 120.671C438.813 120.62 439.58 120.614 439.997 120.177C441.199 118.926 441.575 109.381 442.239 107.184H425.239C425.386 106.356 426.81 100.769 425.72 100.769H411.126L410.163 107.184H395.73C395.794 106.007 396.339 100.086 396.993 99.6268L424.777 99.4632C425.8 99.2868 425.608 100.409 426.349 100.474L441.26 100.272C442.559 99.6076 443.095 98.7448 443.502 97.3816C445.061 92.1854 446.065 84.2372 446.95 78.6978C447.338 76.2761 448.426 71.6573 448.362 69.4954C448.326 68.2926 447.791 67.1508 446.559 66.7787H446.562ZM421.072 139.904C420.2 142.441 420.427 147.39 419.472 149.616C419.388 149.808 418.468 150.168 418.346 150.168H402.95C402.854 150.168 402.376 149.69 402.517 149.43L404.393 139.904H421.072ZM429.251 86.9796H399.903C400.499 84.9011 400.454 82.4121 401.346 80.5646H430.695C430.457 81.4114 429.919 86.9796 429.251 86.9796Z" fill="#FDFDFD"/>
|
||||
<path d="M286.354 74.1494H309.127L306.263 89.0868L295.046 89.2568L292.128 105.904H303.354L300.743 120.613L288.612 120.671C288.817 121.99 287.374 123.702 287.791 124.806C287.977 125.293 288.75 126.102 289.084 126.746C292.262 132.869 295.05 139.214 298.527 145.189L289.392 162.035C283.817 157.24 281.049 149.565 278.65 142.79C278.053 142.652 278.027 142.992 277.742 143.323C274.663 146.886 272.032 151.874 268.7 155.771C267.763 156.868 261.464 163.745 260.787 163.925C260.11 164.104 251.261 156.207 249.814 155.409C249.442 154.174 254.115 150.321 255.081 149.208C256.325 147.771 257.903 145.748 259.077 144.224C264.601 137.055 269.316 129.126 272.558 120.658H258.445L261.027 105.92C261.541 105.423 273.258 106.173 275.301 105.92C275.901 105.846 276.388 105.766 276.587 105.118L279.618 89.228L278.64 89.4397L272.555 99.6716L259.728 98.2058L273.877 65.3641L274.643 64.8477L289.257 65.6335C289.69 65.9286 289.578 66.4161 289.501 66.8556C289.119 69.0784 286.659 71.747 286.354 74.1527V74.1494Z" fill="white"/>
|
||||
<path d="M309.77 147.28C309.251 143.034 308.353 138.829 307.493 134.643C307.317 133.777 306.268 132.308 307.025 131.224C307.451 130.611 320.028 129.588 321.802 128.969C322.074 128.988 322.402 128.978 322.623 129.139C323.329 129.649 325.317 144.256 325.446 146.28L323.242 147.28H331.582L341.352 128.975L356.6 130.925L347.94 147.284H359.647C360.141 147.284 360.106 148.249 360.064 148.637C359.737 151.607 358.326 155.671 357.659 158.773C357.524 159.398 357.54 161.717 356.764 161.717H295.34L298.387 147.284H309.774L309.77 147.28Z" fill="white"/>
|
||||
<path d="M369.384 81.8012L311.877 82.1733C311.027 82.0418 313.782 68.7819 314.144 67.7042C315.383 67.0531 316.656 67.7299 317.933 67.7459C335.577 67.9769 353.241 67.5535 370.889 67.717C371.225 67.7235 371.649 67.7395 371.86 68.0346C372.3 68.644 369.458 80.0114 369.384 81.8012Z" fill="white"/>
|
||||
<path d="M365.421 87.3001H313.138L305.289 126.634C305.148 126.974 305.514 127.715 305.761 127.715H357.081C357.309 127.715 358.512 126.74 358.669 126.416C361.231 113.785 363.47 101.057 365.882 88.3714C366.036 88.0314 365.664 87.2969 365.417 87.2969L365.421 87.3001ZM345.168 113.307C338.048 113.89 330.715 113.663 323.566 113.278L325.548 101.952L325.968 101.41H347.298L345.168 113.307Z" fill="white"/>
|
||||
<path d="M203.82 48.3031C204.529 48.4666 205.908 48.8932 206.944 49.1563C209.866 49.9004 218.613 51.8057 220.714 53.0277C222.786 54.2337 223.421 56.6747 223.177 58.9263L202.479 162.677C201.433 166.23 198.322 168.418 194.746 169.056L164.781 168.941C162.494 168.081 161.359 165.903 161.285 163.527L176.665 86.1934C176.671 85.9464 175.594 85.8181 175.337 85.8694C172.264 86.5045 167.722 89.8083 164.598 90.1643C161.542 90.5107 159.101 88.7017 159.332 85.5487C160.765 79.6116 160.801 72.2856 162.436 66.5089C162.847 65.0591 163.861 63.8274 165.214 63.1923C174.821 58.8044 184.341 52.7423 194.079 48.7553C198.277 47.0361 199.506 47.3119 203.817 48.3031H203.82Z" fill="#FCD02B"/>
|
||||
<path d="M217.721 2.13574C221.749 -3.29773 229.63 2.70001 225.274 8.0918C224.986 8.45104 224.287 8.63105 224.203 8.77539L224.197 8.77832C223.819 9.41982 224.566 11.8519 224.79 12.7051C225.518 15.4828 226.673 20.5563 227.661 22.9844C227.786 23.2923 227.767 23.6454 228.178 23.7705C228.992 24.0163 239.786 19.9539 241.585 19.5947C242.364 19.0142 241.854 17.6667 242.887 16.4062C246.733 11.6976 254.268 16.8877 250.435 22.0293C249.059 23.8734 248.009 23.2992 246.479 23.8477C245.158 24.3225 235.42 45.9666 233.492 48.709C233.146 49.3023 232.703 49.976 231.969 50.0947C230.564 50.3225 224.925 47.9902 223.09 47.4385C212.508 44.2599 202.321 41.5209 191.483 39.2725C189.594 38.8812 186.451 39.4552 186.624 36.5078L190.643 9.81738C188.327 8.07873 188.052 4.18555 190.287 2.29004C193.973 -0.837286 199.107 2.57577 197.687 7.12402C197.487 7.75886 196.507 8.23362 197.017 9.02246L205.369 18.3242C207.739 16.0022 210.135 13.6735 212.582 11.4316C213.432 10.6522 217.002 8.06931 217.079 7.25781C217.258 5.30796 215.928 4.55403 217.721 2.13574ZM219.479 11.9248C217.034 13.2302 206.575 24.6463 205.022 24.7461C204.48 24.7814 204.031 24.5848 203.617 24.248C202.257 23.1348 199.858 19.4505 198.46 17.8467C198.216 17.5677 197.793 17.487 197.604 17.2754C197.453 17.1054 195.249 13.4103 194.62 14.1641L192.051 34.3682V34.373C205.054 36.3168 217.502 40.5537 230.024 44.3418L240.161 25.0762L240.163 25.0771V25.0742L240.161 25.0762L240.16 25.0752L240.162 25.0723C239.572 24.5196 226.682 30.0984 225.399 29.749C224.575 29.5244 224.389 28.8194 224.116 28.1299C222.269 23.4502 221.711 17.4201 219.953 12.7275C219.834 12.41 219.831 12.1301 219.479 11.9248Z" fill="#FCD02B"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_45235_124956">
|
||||
<rect width="500" height="175.589" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 12 KiB |
@@ -0,0 +1 @@
|
||||
<svg class="svg_quick_icon svg_icon_app" width="22" height="22" viewBox="-2 -2 22 22" fill="none" xmlns="http://www.w3.org/2000/svg"><path stroke="currentColor" d="M1.5 4.5C1.5 2.84315 2.84315 1.5 4.5 1.5V1.5C6.15685 1.5 7.5 2.84315 7.5 4.5V7.47015C7.5 7.48664 7.48664 7.5 7.47015 7.5H4.5C2.84315 7.5 1.5 6.15685 1.5 4.5V4.5Z" stroke-width="1.2"/><path stroke="currentColor" d="M10.5 4.5C10.5 2.84315 11.8431 1.5 13.5 1.5V1.5C15.1569 1.5 16.5 2.84315 16.5 4.5V4.5C16.5 6.15685 15.1569 7.5 13.5 7.5H10.5299C10.5134 7.5 10.5 7.48664 10.5 7.47015V4.5Z" stroke-width="1.2"/><path stroke="currentColor" d="M1.5 13.5C1.5 11.8431 2.84315 10.5 4.5 10.5H7.47015C7.48664 10.5 7.5 10.5134 7.5 10.5299V13.5C7.5 15.1569 6.15685 16.5 4.5 16.5V16.5C2.84315 16.5 1.5 15.1569 1.5 13.5V13.5Z" stroke-width="1.2"/><path stroke="currentColor" d="M10.5 10.5299C10.5 10.5134 10.5134 10.5 10.5299 10.5H13.5C15.1569 10.5 16.5 11.8431 16.5 13.5V13.5C16.5 15.1569 15.1569 16.5 13.5 16.5V16.5C11.8431 16.5 10.5 15.1569 10.5 13.5V10.5299Z" stroke-width="1.2"/></svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 69 KiB |
@@ -0,0 +1,9 @@
|
||||
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="36" height="36" viewBox="0 0 36 36" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="关闭">
|
||||
<circle id="图标" cx="18" cy="18" r="14" stroke="white" stroke-width="2"/>
|
||||
<g id="x">
|
||||
<path id="Vector 434" d="M23.0903 12.9082L12.908 23.0905" stroke="white" stroke-width="2.25" stroke-linecap="round"/>
|
||||
<path id="Vector 435" d="M23.0922 23.0893L12.9098 12.907" stroke="white" stroke-width="2.25" stroke-linecap="round"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 583 B |
@@ -0,0 +1,10 @@
|
||||
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="96" height="96" viewBox="0 0 96 96" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="18+ · 年龄标识" clip-path="url(#clip0_0_4)">
|
||||
<path id="Vector" d="M46.6861 36.6866C46.6861 31.0846 48.5533 29.4371 53.1661 29.4371C57.7789 29.4371 59.6461 31.0846 59.6461 36.6866C59.6461 42.0687 57.7789 43.9359 53.1661 43.9359C48.5533 44.0458 46.6861 42.1786 46.6861 36.6866ZM53.1661 51.7346C47.7844 51.7346 45.9174 54.1511 45.9174 59.4234C45.9174 64.6955 47.7844 66.7826 53.1661 66.7826C58.5479 66.7826 60.4149 64.6955 60.4149 59.4234C60.4149 54.1511 58.4381 51.7346 53.1661 51.7346ZM88.5316 43.7163V39.762C88.5316 36.7964 88.0923 35.3685 85.5661 35.3685H83.04C80.5139 35.3685 80.0746 36.9062 80.0746 39.762V43.7163H76.1207C73.1553 43.7163 71.7276 44.1556 71.7276 46.6819V49.3181C71.7276 51.8444 73.2651 52.2837 76.1207 52.2837H80.1844V56.238C80.1844 59.2036 80.6237 60.6315 83.1498 60.6315H85.676C88.2021 60.6315 88.6414 59.0938 88.6414 56.238V52.2837H94.2426C95.3411 52.2837 95.8902 52.833 96 53.9313V66.6728C96 69.7482 95.1214 72.0549 92.9247 74.142L74.2535 92.8147C72.057 95.0114 69.6407 96 66.6753 96H29.333C26.3676 96 23.9512 95.0114 21.7546 92.8147L3.08348 74.142C0.886867 71.9451 -0.101607 69.7482 0.0082236 66.6728V29.7665C0.0082236 26.6911 0.996697 24.0549 3.30313 21.7482L22.194 2.52632C24.1709 0.549199 26.5872 0 29.333 0H66.5654C69.3112 0 71.7275 0.549199 73.7044 2.52632L92.7051 21.7482C94.9018 24.0549 96 26.6911 96 29.7665V41.7391C96 42.9473 95.4509 43.6065 94.2426 43.6065H88.5316V43.7163ZM31.0902 25.5926C31.0902 23.1761 29.5526 22.627 26.697 22.627H19.1187C16.1533 22.627 14.7255 23.1761 14.7255 25.5926V28.3386C14.7255 30.7551 16.2631 31.3043 19.1187 31.3043H20.876C21.9744 31.3043 22.4137 31.8536 22.4137 33.6109V69.6384C22.4137 72.604 22.9628 74.0321 25.3791 74.0321H28.2346C30.6509 74.0321 31.2 72.4942 31.2 69.6384V25.5926H31.0902ZM68.9818 60.6315C68.9818 54.3707 68.5425 49.6476 62.9411 47.4509C67.6637 44.0458 67.6637 40.5308 67.6637 37.675V34.8192C67.1146 25.373 64.0393 21.7482 53.0563 21.7482C42.0733 21.7482 38.9981 25.373 38.4488 34.8192V37.675C38.4488 40.5308 38.5588 44.0458 43.1716 47.4509C37.5702 49.6476 37.1309 54.3707 37.1309 60.6315C37.1309 69.858 40.4258 74.4714 52.9465 74.6911C65.6868 74.4714 68.9818 69.858 68.9818 60.6315Z" fill="#FFC000"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_0_4">
|
||||
<rect width="96" height="96" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.5 KiB |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" stroke="#fff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.4" d="m12 3.6 2.72 5.53 6.08.891-4.4 4.302 1.038 6.077L12 17.53 6.562 20.4 7.6 14.323l-4.4-4.302 6.08-.892z"/></svg>
|
||||
|
After Width: | Height: | Size: 294 B |
|
After Width: | Height: | Size: 86 KiB |
|
After Width: | Height: | Size: 293 KiB |
|
After Width: | Height: | Size: 508 KiB |
|
After Width: | Height: | Size: 367 KiB |
|
After Width: | Height: | Size: 147 KiB |
@@ -0,0 +1,5 @@
|
||||
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="ic_gf">
|
||||
<path id="Vector" d="M11.6304 16.2755L11.6268 16.2883L11.6322 16.2928L11.6304 16.2755ZM27.1194 10.7593C26.095 10.1861 24.6487 7.80189 24.6651 7.08054C24.7351 4.03674 22.6893 3.59109 21.6658 3.67464C19.3962 3.85929 17.9716 3.17889 16.8937 2.03994C15.8158 0.900085 14.8489 0.949285 14.8489 0.949285C14.0793 0.949285 13.4626 1.23219 12.6675 2.17554C11.4213 3.65379 9.44815 3.70659 8.8495 3.67374C8.2519 3.64194 6.8455 3.51729 5.98675 4.21959C5.128 4.92189 5.10625 6.13899 4.8961 7.48989C4.68595 8.83989 3.2833 10.1725 2.5783 10.7593C1.8733 11.3459 1.46035 11.8045 1.35115 12.394C1.24195 12.9842 1.04905 13.1917 2.16985 15.1192C3.4234 17.2751 2.7748 18.8051 2.5792 20.2961C2.3836 21.788 3.27415 22.7741 5.17 23.2934C7.06585 23.8129 8.29015 25.7669 8.4421 26.1544C8.59405 26.542 9.5719 28.0348 10.4871 28.1984C11.6751 28.4104 12.4429 27.5816 13.2142 27.3806C13.9848 27.1796 15.6777 27.0031 16.6228 27.5171C19.5348 29.0999 20.7637 27.4798 21.1221 26.4274C21.5077 25.294 22.9605 23.6837 24.5307 23.2936C28.0239 22.4267 26.9331 19.3666 26.8485 19.07C26.7639 18.7735 26.5092 16.2455 27.3943 15.2549C29.2491 13.1762 28.1437 11.3323 27.1194 10.7593ZM17.758 16.2791L17.7534 16.2628V16.2829L17.7589 16.2793C18.5085 18.7745 18.6741 20.9132 18.6741 20.9132L14.7006 18.4981C14.6985 18.4991 14.6964 18.5 14.6943 18.5008L14.6806 18.4963C12.7402 19.838 10.7145 20.9132 10.7145 20.9132C10.7145 20.9132 10.8792 18.7801 11.6269 16.2884C9.7948 14.914 8.3266 12.9536 8.3266 12.9536L12.7039 12.5561L14.6943 8.17794L16.6846 12.5552L21.0619 12.9536C21.0619 12.9533 19.5919 14.9027 17.758 16.2791Z" fill="#EEEEEE"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 107 KiB |
|
After Width: | Height: | Size: 3.9 KiB |
|
After Width: | Height: | Size: 78 KiB |
|
After Width: | Height: | Size: 129 KiB |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="white"><path d="M4 10h4v11H4a1 1 0 0 1-1-1v-9a1 1 0 0 1 1-1Zm6-6a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1v17h-4Zm7 10h3a1 1 0 0 1 1 1v5a1 1 0 0 1-1 1h-4v-6a1 1 0 0 1 1-1Z"/></svg>
|
||||
|
After Width: | Height: | Size: 234 B |
|
After Width: | Height: | Size: 64 KiB |
|
After Width: | Height: | Size: 73 KiB |
|
After Width: | Height: | Size: 75 KiB |
|
After Width: | Height: | Size: 126 KiB |
|
After Width: | Height: | Size: 69 KiB |
@@ -0,0 +1,16 @@
|
||||
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="78" height="78" viewBox="0 0 78 78" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="_图层_1" clip-path="url(#clip0_0_12)">
|
||||
|
||||
<path id="Vector" d="M78 39.0005C78 60.5399 60.5389 78.001 38.9995 78.001C17.4601 78.001 0 60.5389 0 39.0005C0 17.462 17.4611 0 39.0005 0C60.5399 0 78.001 17.4611 78.001 39.0005H78ZM53.0315 53.1697L51.3565 53.306C51.9924 51.7855 52.5434 50.3603 53.369 48.9274L64.235 59.803C75.0647 46.8044 73.9426 27.5901 62.0833 15.7947C50.2726 4.04781 31.1537 2.97532 18.1923 13.7631L27.8895 23.446L27.8676 32.3053L13.7622 18.198C2.95912 31.1708 4.04781 50.3193 15.8404 62.1319C27.6607 73.9722 46.8864 75.0304 59.8096 64.2273L53.0496 57.495L53.0315 53.1707V53.1697Z" fill="#FFC000"/>
|
||||
<path id="Vector_2" d="M50.5109 50.8522C48.8455 49.815 47.5928 48.364 46.4173 46.853L46.4707 49.9151L42.4706 49.917L42.5812 46.8473C41.7298 48.037 40.7174 49.1438 39.4876 50.1553L38.1558 47.9264L37.3226 49.447C36.4065 48.7072 35.6829 47.8006 34.9679 46.8663C34.9365 47.9331 34.9565 48.8617 35.0413 49.9141H31.0784L31.1499 46.8387L30.4244 47.7711C29.5635 48.9722 28.5244 50.0009 27.2879 50.8646C26.7789 49.5395 26.2393 48.3278 25.5491 47.1142C27.165 46.1027 28.6312 44.9692 29.7351 43.36C28.5377 43.3019 27.3785 43.3505 26.2155 43.4515L26.2069 39.1816C27.9209 39.358 29.4796 39.3561 31.1622 39.317L31.0431 37.3017L35.0165 37.2988L34.9593 39.3265C36.1329 39.3551 37.172 39.3294 38.3312 39.2093V43.4144L36.1272 43.3457C36.8831 44.3305 37.784 45.0531 38.7993 45.7595C39.7984 45.0493 40.5954 44.2952 41.3838 43.3257L39.0062 43.4372L38.9976 39.1807C40.2188 39.3132 41.3495 39.358 42.5754 39.3313L42.4744 37.3017L46.4736 37.2969L46.4202 39.3284C48.2506 39.3494 49.9561 39.3618 51.8131 39.1835L51.816 43.4535C50.4852 43.3391 49.1963 43.3038 47.8283 43.3591C49.0866 44.9645 50.6434 46.0408 52.4347 47.0599C51.7474 48.2592 51.0972 49.4108 50.5128 50.8512L50.5109 50.8522Z" fill="#FFC000"/>
|
||||
<path id="Vector_3" d="M51.4194 30.8057C49.7368 34.619 44.648 35.3998 41.1235 34.2796C38.3951 33.413 36.6801 31.1146 36.5342 28.3051C36.4084 25.8723 37.5562 23.6682 39.8251 22.4899C37.6191 20.9369 37.07 19.0045 37.4618 16.4248C37.8069 14.3466 39.1711 12.6802 41.1817 11.9728C44.892 10.6677 50.0857 11.5896 51.1287 16.0473C51.6263 18.4583 51.2326 21.0151 48.7844 22.2267C52.0076 23.8274 52.7503 27.7894 51.4194 30.8057ZM45.4211 20.3792C46.5212 20.1475 47.0885 19.2791 47.1295 18.2266C47.1676 17.2437 46.9321 16.2227 46.0455 15.6745C45.0341 15.0482 43.6413 15.0482 42.6079 15.6326C41.8071 16.0854 41.563 16.9749 41.5249 17.8376C41.4715 19.0512 42.1293 20.0827 43.3305 20.3716C43.9854 20.5289 44.7166 20.5279 45.4211 20.3802V20.3792ZM47.3945 29.1164C47.7272 28.2012 47.7262 27.204 47.4183 26.2822C46.8578 24.6034 45.0808 24.2335 43.4973 24.4585C41.9815 24.673 41.0168 25.8684 40.9596 27.3814C40.9138 28.6026 41.3333 29.7914 42.4172 30.433C43.9482 31.3386 46.6557 31.1479 47.3945 29.1174V29.1164Z" fill="#FFC000"/>
|
||||
<path id="Vector_4" d="M44.5421 59.2586L41.3933 59.2224L41.358 64.1005C41.3495 65.3256 40.4743 66.1435 39.3008 66.3227C37.703 66.5668 36.1005 66.5201 34.4865 66.4219C34.3044 65.173 34.0289 64.0624 33.5885 62.9213C32.1451 64.4485 30.535 65.5544 28.6626 66.5468C27.8618 65.1339 26.959 63.9032 25.7826 62.7401L27.8208 61.9346C29.1584 61.2587 30.3634 60.4827 31.3958 59.2453C29.82 59.1833 28.3147 59.2691 26.6654 59.353L26.6616 55.0869L30.1565 55.2203H47.7625C48.997 55.2194 50.0962 55.165 51.3107 55.0945L51.3012 59.3587L46.8978 59.2357C47.8016 60.15 48.8188 60.7506 49.9084 61.3569L52.2174 62.3655C51.1001 63.6449 50.265 64.9995 49.4918 66.5191C47.1552 65.2541 45.157 63.6134 43.4353 61.6515L42.6069 60.6953L44.5421 59.2596V59.2586ZM37.0347 61.7992L37.0309 59.2233L33.5675 59.2538L35.5418 60.4846L34.2186 62.2816C34.9937 62.3131 37.0357 62.8622 37.0347 61.7992Z" fill="#FFC000"/>
|
||||
<path id="Vector_5" d="M29.6226 34.3444L29.616 17.5516C28.3881 18.055 27.2527 18.3486 25.9571 18.5021L25.9504 14.869C27.9143 14.3752 29.6341 13.4095 30.9697 11.8403L34.089 11.8355L34.0909 34.3225L29.6236 34.3444H29.6226Z" fill="#FFC000"/>
|
||||
<path id="Vector_6" d="M48.6405 50.5271H29.3319V54.307H48.6405V50.5271Z" fill="#FFC000"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_0_12">
|
||||
<rect width="78" height="78" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.2 KiB |
@@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="30" height="30" viewBox="0 0 30 30" fill="none">
|
||||
<!-- Figma Subtract 复合路径:Safari 必须 evenodd,否则 nonzero 会把外形抵消成空白 -->
|
||||
<path fill="#EEEEEE" fill-rule="evenodd" clip-rule="evenodd" d="M24.6006 4.80005C25.0733 4.80005 25.5417 4.89355 25.9785 5.07446C26.4152 5.25538 26.8122 5.5205 27.1465 5.85474C27.4807 6.18899 27.7459 6.58602 27.9268 7.02271C28.1076 7.45928 28.2011 7.92713 28.2012 8.39966V21.5999C28.2012 22.5546 27.8216 23.4706 27.1465 24.1458C26.4714 24.8209 25.5554 25.2004 24.6006 25.2004H5.40039C4.44584 25.2003 3.53049 24.8207 2.85547 24.1458C2.18034 23.4706 1.80078 22.5546 1.80078 21.5999V8.39966C1.80088 7.44502 2.18043 6.52978 2.85547 5.85474C3.53051 5.1797 4.44575 4.80015 5.40039 4.80005H24.6006ZM4.7998 6.90747C3.86035 6.90759 3.54966 8.16535 4.37988 8.60278L14.6133 13.9963C14.743 14.0647 14.8876 14.1001 15.0342 14.0999C15.1808 14.0996 15.3255 14.0641 15.4551 13.9954L25.6221 8.59497C26.4501 8.15517 26.1376 6.90003 25.1992 6.90063L4.7998 6.90747Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 98 KiB |
|
After Width: | Height: | Size: 85 KiB |
@@ -0,0 +1,11 @@
|
||||
<svg width="36" height="36" viewBox="0 0 36 36" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M0 18C0 8.05888 8.05888 0 18 0C27.9411 0 36 8.05888 36 18C36 27.9411 27.9411 36 18 36C8.05888 36 0 27.9411 0 18Z" fill="#15BA11"/>
|
||||
<g clip-path="url(#clip0_40635_137444)">
|
||||
<path d="M16.3758 22.4796C16.2358 22.4811 16.0979 22.4391 15.9755 22.3577C15.8531 22.2763 15.7504 22.1583 15.6773 22.0152L15.6243 21.8844L11.4385 15.252C11.4133 15.1928 11.3997 15.1276 11.3985 15.0612C11.4017 14.9375 11.4455 14.8203 11.5205 14.7353C11.5956 14.6503 11.6956 14.6044 11.7988 14.6076C11.8888 14.6076 11.9719 14.6412 12.0389 14.6976L16.6199 17.9772C16.7781 18.093 16.9556 18.1656 17.1398 18.1896C17.324 18.2137 17.5104 18.1887 17.6856 18.1164L27.8162 10.8C25.6408 7.896 22.0605 6 18.0078 6C11.3755 6 6 11.076 6 17.3352C6 20.7504 7.61705 23.8248 10.1487 25.902C10.2509 25.9826 10.3349 26.092 10.3936 26.2207C10.4522 26.3494 10.4836 26.4934 10.4849 26.64C10.4824 26.7387 10.4683 26.8365 10.4429 26.9304L9.90154 29.2152C9.87552 29.3232 9.8365 29.4348 9.8365 29.5464C9.8365 29.7984 10.0166 30 10.2368 30C10.3228 30 10.3939 29.964 10.4669 29.916L13.0966 28.1976C13.2947 28.068 13.5029 27.9876 13.734 27.9876C13.8571 27.9876 13.9742 28.0092 14.0873 28.0464C15.3131 28.446 16.6369 28.6692 18.0068 28.6692C24.6391 28.668 30.0156 23.592 30.0156 17.3352C30.0156 15.438 29.5183 13.6524 28.6467 12.0828L16.823 22.3248L16.736 22.3812C16.6237 22.4463 16.5005 22.4799 16.3758 22.4796Z" fill="white"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_40635_137444">
|
||||
<rect width="24" height="24" fill="white" transform="translate(6 6)"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 90 KiB |
@@ -0,0 +1,5 @@
|
||||
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="Iconly/UntitledUI/chevron-right">
|
||||
<path id="Icon" d="M6.66667 4.66667L10 8L6.66667 11.3333" stroke="white" stroke-opacity="0.6" stroke-width="0.888889" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 386 B |
|
After Width: | Height: | Size: 368 KiB |
@@ -0,0 +1,26 @@
|
||||
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="Frame 2147231187">
|
||||
<g clip-path="url(#clip0_0_4)">
|
||||
<circle id="Ellipse 14975" cx="12" cy="12" r="12" fill="url(#paint0_linear_0_4)"/>
|
||||
<g id="Ellipse 14976" opacity="0.3" filter="url(#filter0_f_0_4)">
|
||||
<circle cx="12" cy="22" r="9" fill="white"/>
|
||||
</g>
|
||||
<path id="Vector 5628" d="M8.14286 11L6 13L11.1429 18L18 7.5L16.2857 6L10.2857 12.5L8.14286 11Z" fill="#2D251E"/>
|
||||
</g>
|
||||
<rect x="1" y="1" width="22" height="22" rx="11" stroke="#2D251E" stroke-width="2"/>
|
||||
</g>
|
||||
<defs>
|
||||
<filter id="filter0_f_0_4" x="-1" y="9" width="26" height="26" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
||||
<feGaussianBlur stdDeviation="2" result="effect1_foregroundBlur_0_4"/>
|
||||
</filter>
|
||||
<linearGradient id="paint0_linear_0_4" x1="12" y1="0" x2="12" y2="24" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#FEBF8B"/>
|
||||
<stop offset="1" stop-color="#FFB27D"/>
|
||||
</linearGradient>
|
||||
<clipPath id="clip0_0_4">
|
||||
<rect width="24" height="24" rx="12" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 128 KiB |
|
After Width: | Height: | Size: 259 KiB |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="white"><rect x="3" y="3" width="8" height="8" rx="2"/><rect x="13" y="3" width="8" height="8" rx="2"/><rect x="3" y="13" width="8" height="8" rx="2"/><rect x="13" y="13" width="8" height="8" rx="2"/></svg>
|
||||
|
After Width: | Height: | Size: 271 B |
|
After Width: | Height: | Size: 134 KiB |
@@ -0,0 +1 @@
|
||||
<svg class="svg_quick_icon svg_icon_history" xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 22 22" fill="none"><circle cx="11.0001" cy="10.9999" r="6.96667" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/><path d="M14.8704 11.7741H11V7.12964" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
||||
|
After Width: | Height: | Size: 403 B |
|
After Width: | Height: | Size: 62 KiB |
@@ -0,0 +1,5 @@
|
||||
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="ic_js">
|
||||
<path id="Vector" d="M9.99883 34.0138C8.90965 36.1763 8.08688 39.9081 8.08688 39.9081C8.08688 39.9081 11.8202 39.0866 13.9848 37.9976L15.9714 39.9832C15.7319 40.3943 15.4481 40.7891 15.0954 41.1426C12.8655 43.3713 2.00002 45.9907 2.00002 45.9907C2.00002 45.9907 4.62246 35.1335 6.85331 32.9047C7.20586 32.5515 7.59831 32.2659 8.01227 32.0284L9.99883 34.0138ZM29.5866 40.2725L30.9337 31.1604L39.4984 22.6L39.4537 22.5555C41.5337 20.2934 49.0528 10.7301 44.6297 3.37559C37.2728 -1.04319 27.7045 6.47094 25.4396 8.5498L25.418 8.52733L16.8534 17.0874L7.7373 18.4348L6.30882 19.8615L11.0752 24.6263L7.81342 26.1235C6.76324 27.1723 6.77956 28.8941 7.85226 29.9668L18.0501 40.1591C19.1219 41.2295 20.8453 41.2468 21.8955 40.1979L23.3927 36.9372L28.1582 41.7001L29.5866 40.2725ZM34.1989 21.1082C33.1478 22.1586 31.4244 22.1399 30.3526 21.0683L26.9533 17.671C25.8796 16.5979 25.8624 14.8766 26.9135 13.8266L30.2439 10.498C31.2952 9.44734 33.0167 9.46472 34.0905 10.5375L37.4898 13.935C38.5616 15.0063 38.5806 16.7289 37.5294 17.7795L34.1989 21.1082ZM17.963 31.5096L16.5056 30.0541L22.4529 24.1099C22.8462 23.7173 23.4927 23.723 23.8944 24.1245C24.2963 24.5262 24.3029 25.1734 23.9101 25.5655L17.963 31.5096Z" fill="#2ECC71"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 136 KiB |
|
After Width: | Height: | Size: 169 KiB |
|
After Width: | Height: | Size: 112 KiB |
|
After Width: | Height: | Size: 107 KiB |
@@ -0,0 +1 @@
|
||||
<svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="200" height="200" fill="#D68B13"><path d="M959.069091 480.116364a124.974545 124.974545 0 0 0-93.090909-40.96 122.88 122.88 0 0 0-79.825455 29.323636 123.345455 123.345455 0 0 0-79.825454-29.323636 125.44 125.44 0 0 0-93.090909 40.96 169.192727 169.192727 0 0 0-24.436364 184.32 360.261818 360.261818 0 0 0 167.098182 165.701818 67.258182 67.258182 0 0 0 30.487273 6.749091 64 64 0 0 0 31.883636-7.68 402.385455 402.385455 0 0 0 162.909091-162.909091 167.098182 167.098182 0 0 0-22.109091-186.181818z"/><path d="M536.436364 695.156364c-32.349091-71.68-42.356364-185.018182 35.84-269.265455a181.992727 181.992727 0 0 1 134.516363-59.810909A178.269091 178.269091 0 0 1 814.545455 403.083636c-4.887273-104.029091-72.145455-229.236364-197.352728-272.989091a235.287273 235.287273 0 0 0-190.836363 14.429091 246.690909 246.690909 0 0 0-110.312728 122.647273 235.054545 235.054545 0 0 0-160.116363 23.272727 249.716364 249.716364 0 0 0-121.018182 153.6c-41.658182 146.618182 36.072727 271.825455 116.363636 334.661819 179.2 141.963636 404.48 129.396364 424.029091 128a127.767273 127.767273 0 0 0 57.483637-16.29091 138.007273 138.007273 0 0 0 34.90909-27.694545 481.047273 481.047273 0 0 1-131.258181-167.563636z m-252.974546-53.76c-13.730909-74.705455-24.901818-86.109091-97.047273-100.538182 72.145455-14.429091 83.316364-25.832727 97.047273-100.538182 13.963636 74.705455 25.134545 86.109091 97.047273 100.538182-71.912727 14.429091-83.083636 25.832727-97.047273 100.538182z"/></svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 428 KiB |
|
After Width: | Height: | Size: 521 KiB |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24"><g fill="none" fill-rule="evenodd"><path fill="#00D1D1" fill-rule="nonzero" d="M18 7.28a2.72 2.72 0 0 1 2.715 2.554l.005.166v8a2.72 2.72 0 0 1-2.554 2.715L18 20.72H6a2.72 2.72 0 0 1-2.715-2.554L3.28 18v-8a2.72 2.72 0 0 1 2.554-2.715L6 7.28zM9.4 11v6a.8.8 0 0 0 1.25.661l4.4-3a.8.8 0 0 0 0-1.322l-4.4-3A.8.8 0 0 0 9.4 11m1.6 1.513 2.18 1.486L11 15.486z"/><path stroke="#00D1D1" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.4" d="m16 4-4 4-4-4"/></g></svg>
|
||||
|
After Width: | Height: | Size: 535 B |
|
After Width: | Height: | Size: 232 KiB |
@@ -0,0 +1,28 @@
|
||||
<svg width="500" height="176" viewBox="0 0 500 176" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_45235_124956)">
|
||||
<path d="M463.454 54.534C463.262 53.8027 463.416 53.1163 463.653 52.4203C464.577 49.7324 467.858 45.8321 469.446 43.4745C472.333 39.1893 477.91 30.1569 481.682 27.1515C488.12 22.0227 496.075 28.8579 490.991 35.4975C483.145 41.0849 475.668 47.9202 467.678 53.2414C466.36 54.1203 465.08 55.4931 463.454 54.5373V54.534Z" fill="#FCD02B"/>
|
||||
<path d="M477.637 63.0442C477.557 63.0634 477.541 63.458 477.287 63.5414C476.857 63.6793 472.784 63.891 472.3 63.814C468.188 63.1629 475.44 58.6595 476.331 58.0373C479.699 55.6926 490.817 48.2319 494.364 48.1389C499.839 47.9945 502.149 55.1248 497.52 58.0565C496.635 58.6178 495.708 58.8199 494.727 59.1118C489.158 60.7604 483.276 61.6521 477.64 63.0442H477.637Z" fill="#FCD02B"/>
|
||||
<path d="M25.7009 139.13C20.6202 135.371 15.6325 130.791 10.6576 126.765C7.80935 124.462 2.48809 121.742 2.46884 117.611C2.44318 112.409 7.91199 109.673 12.3416 111.931C15.0423 113.307 19.876 120.831 21.8101 123.632C24.0362 126.849 30.6533 135.99 31.6989 139.092C32.1191 140.346 31.7502 141.818 30.2363 141.805C29.8931 141.805 26.2301 139.518 25.7041 139.13H25.7009Z" fill="#FCD02B"/>
|
||||
<path d="M3.05646e-05 141.885C0.0128606 141.85 0.429837 141.828 0.535685 141.581C1.84756 138.582 3.2332 137.071 6.83203 137.324C10.3988 137.574 22.3243 146.126 25.5543 148.749C26.6609 149.651 28.6592 150.815 28.1043 152.515C27.4852 154.407 22.5104 152.416 21.2177 152.121C16.0184 150.937 8.95862 149.785 4.09923 148.069C2.29982 147.434 1.1804 146.398 0.5421 144.57C0.4138 144.201 -0.0512896 142.007 3.05646e-05 141.882V141.885Z" fill="#FCD02B"/>
|
||||
<path d="M62.6914 41.4902C91.9149 22.1875 138.542 29.013 150.805 64.8789C151.202 66.0399 151.398 68.4937 151.748 69.251H151.745C156.55 79.6689 152.717 100.139 148.759 111.202C136.041 146.745 104.726 173.682 66.1719 175.52C60.6742 175.783 55.2529 175.279 49.7969 174.868C47.7924 174.718 46.2465 175.311 44.4023 174.169C42.9301 173.258 42.2085 171.606 42.2373 169.894C42.3175 165.416 50.1729 151.525 53.6562 148.57C57.8612 145.004 60.337 146.356 65.209 146.01C74.0264 145.381 82.6614 142.241 89.7051 137.147C91.6456 135.746 93.4454 134.011 95.1807 132.362C83.8422 135.785 72.8274 136.369 61.582 132.292C49.0791 127.76 40.9418 118.557 37.0479 105.826C35.0657 99.3536 35.4402 95.803 35.2061 89.0674C34.6063 71.7757 48.1678 51.0839 62.6914 41.4902ZM83.6387 61.96H83.6377L83.6367 61.9609C81.8856 62.0572 79.509 63.9522 78.6172 65.4404C77.4304 67.4291 77.5809 69.3763 77.2217 71.583H77.2246C75.5439 81.8373 74.5848 92.2168 73.0869 102.5C73.6188 107.482 76.7179 109.783 80.9043 109.213C81.0412 109.194 81.1792 109.173 81.3184 109.148C81.4461 109.126 81.5746 109.1 81.7041 109.072C81.9433 109.021 82.1856 108.961 82.4307 108.892C82.7598 108.798 83.094 108.69 83.4326 108.564C83.468 108.551 83.5046 108.54 83.54 108.526C83.7078 108.463 83.8764 108.396 84.0469 108.33C85.092 107.923 86.1883 107.465 87.3223 106.966C95.4105 103.406 105.421 97.7665 112.517 93.8809C116.58 91.6581 120.673 90.1277 120.218 84.5596C120 81.8944 118.492 80.8585 116.757 79.2773C107.343 74.5816 98.2334 67.1369 88.7842 62.749C87.251 62.037 85.2713 61.8701 83.6387 61.96ZM65.6748 42.0742C65.1103 42.0486 64.5483 42.0327 63.9902 42.1514C61.9786 42.5723 54.6342 49.179 53.0049 51.0488C52.7096 51.389 51.1995 53.2195 51.2471 53.4541C51.587 55.1087 53.8085 51.8105 54.1143 51.5234C57.6906 48.1684 61.1839 45.7723 65.0713 42.8984C65.4305 42.6322 65.87 42.9015 65.6777 42.0742H65.6748ZM66.2266 41.3975L66.5186 42.0098L68.7822 40.9355L68.4902 40.3223L66.2266 41.3975ZM68.8916 40.3135C70.0687 39.9478 71.1336 39.3478 72.2979 38.8955C72.7565 38.7191 73.2311 39.2029 73.0547 38.2246H73.0518L68.8916 40.3135ZM74.0137 37.9072C73.5648 37.6767 73.1579 38.1094 73.376 38.2217C73.825 38.4524 74.2318 38.0195 74.0137 37.9072Z" fill="#FCD02B"/>
|
||||
<path d="M83.6384 61.9609C83.138 64.0939 86.7176 61.9032 86.5251 63.5615C82.9712 63.6705 80.3058 65.3032 79.4879 68.8763L78.1824 78.316C77.6211 78.316 77.8296 76.9112 77.852 76.5391C77.9899 74.3804 78.7084 72.1833 78.5064 69.9797L77.2266 71.5835C77.589 69.3799 77.4351 67.4297 78.6218 65.4411C79.5103 63.9528 81.8903 62.054 83.6416 61.9609H83.6384Z" fill="#FDFDFD"/>
|
||||
<path d="M65.6739 42.0729C65.8664 42.9037 65.4301 42.631 65.0677 42.8972C61.177 45.7712 57.6872 48.1672 54.1108 51.5222C53.8061 51.8077 51.5833 55.1082 51.2433 53.4532C51.1952 53.219 52.7059 51.3875 53.001 51.0475C54.6337 49.1743 61.9757 42.5701 63.9868 42.1499C64.5481 42.0312 65.1062 42.0472 65.6707 42.0729H65.6739Z" fill="#FCD02B"/>
|
||||
<path d="M68.4922 40.3143L66.2266 41.3906L66.5183 42.0048L68.784 40.9286L68.4922 40.3143Z" fill="#FCD02B"/>
|
||||
<path d="M73.0501 38.2246C73.2297 39.2029 72.7518 38.7154 72.2931 38.895C71.132 39.3472 70.0671 39.947 68.8867 40.3127L73.0469 38.2246H73.0501Z" fill="#FCD02B"/>
|
||||
<path d="M74.0153 37.9066C74.2334 38.0189 73.8261 38.4519 73.377 38.221C73.1589 38.1087 73.5662 37.6757 74.0153 37.9066Z" fill="#FCD02B"/>
|
||||
<path d="M470.626 65.4876H486.343C487.536 65.4876 486.282 68.6374 486.208 69.2019C485.342 75.726 483.924 82.0544 482.754 88.5207C479.184 108.276 476.396 128.73 472.288 148.299C470.972 154.563 469.045 159.859 462.534 162.281C458.479 163.788 447.702 164.186 443.346 163.65C442.506 163.548 442.204 163.496 442.224 162.528C442.564 158.496 442.105 153.604 442.554 149.675C442.865 146.952 444.241 148.174 445.931 148.235C449.729 148.376 455.92 148.819 456.847 144.081C460.243 123.662 464.009 103.224 467.492 82.8787C468.358 77.8173 468.843 72.31 469.924 67.3479C470.065 66.6968 470.071 65.9463 470.626 65.4844V65.4876Z" fill="#FDFDFD"/>
|
||||
<path d="M464.532 74.791C464.638 74.8199 465.1 75.2914 465.171 75.4389C465.562 76.2601 464.212 83.2492 463.958 84.8017C460.911 103.389 457.203 121.903 454.025 140.455C453.948 140.551 453.377 140.866 453.309 140.866H440.479C440.367 140.866 440.097 140.507 440.043 140.333C443.408 118.641 447.443 96.9678 451.199 75.317C451.292 75.1855 452.241 74.791 452.35 74.791H464.539H464.532Z" fill="#FDFDFD"/>
|
||||
<path d="M446.562 66.7787L389.462 66.7562C386.697 66.6247 387.044 69.5564 386.71 71.3815C383.647 88.1664 381.184 105.195 377.549 121.881C374.829 134.371 370.381 146.364 365.242 158.02L365.993 158.898C368.607 159.902 371.227 160.916 373.816 161.981C374.701 162.347 377.309 164.069 378.05 163.601C378.489 163.322 382.107 155.405 382.637 154.235C387.284 143.961 390.424 133.7 392.763 122.663C392.987 121.595 392.375 120.312 393.645 120.017C393.911 119.956 394.87 119.812 394.764 120.335L393.481 120.659H407.594L403.428 120.02L408.236 119.696L406.911 127.029C406.244 127.458 395.948 126.785 393.949 127.057C393.353 127.138 393.019 127.15 392.695 127.728L386.168 162.273C386.053 162.529 386.47 162.994 386.588 162.994H429.89C431.198 162.994 431.695 160.804 432.003 160.037C432.116 159.758 432.574 159.781 432.619 159.566C432.764 158.886 432.318 158.19 432.382 157.471C433.193 148.5 436.42 138.55 437.402 129.447C437.543 128.899 436.731 127.074 436.305 127.074H421.71L422.673 120.655C427.696 120.315 433.293 121.111 438.245 120.671C438.813 120.62 439.58 120.614 439.997 120.177C441.199 118.926 441.575 109.381 442.239 107.184H425.239C425.386 106.356 426.81 100.769 425.72 100.769H411.126L410.163 107.184H395.73C395.794 106.007 396.339 100.086 396.993 99.6268L424.777 99.4632C425.8 99.2868 425.608 100.409 426.349 100.474L441.26 100.272C442.559 99.6076 443.095 98.7448 443.502 97.3816C445.061 92.1854 446.065 84.2372 446.95 78.6978C447.338 76.2761 448.426 71.6573 448.362 69.4954C448.326 68.2926 447.791 67.1508 446.559 66.7787H446.562ZM421.072 139.904C420.2 142.441 420.427 147.39 419.472 149.616C419.388 149.808 418.468 150.168 418.346 150.168H402.95C402.854 150.168 402.376 149.69 402.517 149.43L404.393 139.904H421.072ZM429.251 86.9796H399.903C400.499 84.9011 400.454 82.4121 401.346 80.5646H430.695C430.457 81.4114 429.919 86.9796 429.251 86.9796Z" fill="#FDFDFD"/>
|
||||
<path d="M286.354 74.1494H309.127L306.263 89.0868L295.046 89.2568L292.128 105.904H303.354L300.743 120.613L288.612 120.671C288.817 121.99 287.374 123.702 287.791 124.806C287.977 125.293 288.75 126.102 289.084 126.746C292.262 132.869 295.05 139.214 298.527 145.189L289.392 162.035C283.817 157.24 281.049 149.565 278.65 142.79C278.053 142.652 278.027 142.992 277.742 143.323C274.663 146.886 272.032 151.874 268.7 155.771C267.763 156.868 261.464 163.745 260.787 163.925C260.11 164.104 251.261 156.207 249.814 155.409C249.442 154.174 254.115 150.321 255.081 149.208C256.325 147.771 257.903 145.748 259.077 144.224C264.601 137.055 269.316 129.126 272.558 120.658H258.445L261.027 105.92C261.541 105.423 273.258 106.173 275.301 105.92C275.901 105.846 276.388 105.766 276.587 105.118L279.618 89.228L278.64 89.4397L272.555 99.6716L259.728 98.2058L273.877 65.3641L274.643 64.8477L289.257 65.6335C289.69 65.9286 289.578 66.4161 289.501 66.8556C289.119 69.0784 286.659 71.747 286.354 74.1527V74.1494Z" fill="white"/>
|
||||
<path d="M309.77 147.28C309.251 143.034 308.353 138.829 307.493 134.643C307.317 133.777 306.268 132.308 307.025 131.224C307.451 130.611 320.028 129.588 321.802 128.969C322.074 128.988 322.402 128.978 322.623 129.139C323.329 129.649 325.317 144.256 325.446 146.28L323.242 147.28H331.582L341.352 128.975L356.6 130.925L347.94 147.284H359.647C360.141 147.284 360.106 148.249 360.064 148.637C359.737 151.607 358.326 155.671 357.659 158.773C357.524 159.398 357.54 161.717 356.764 161.717H295.34L298.387 147.284H309.774L309.77 147.28Z" fill="white"/>
|
||||
<path d="M369.384 81.8012L311.877 82.1733C311.027 82.0418 313.782 68.7819 314.144 67.7042C315.383 67.0531 316.656 67.7299 317.933 67.7459C335.577 67.9769 353.241 67.5535 370.889 67.717C371.225 67.7235 371.649 67.7395 371.86 68.0346C372.3 68.644 369.458 80.0114 369.384 81.8012Z" fill="white"/>
|
||||
<path d="M365.421 87.3001H313.138L305.289 126.634C305.148 126.974 305.514 127.715 305.761 127.715H357.081C357.309 127.715 358.512 126.74 358.669 126.416C361.231 113.785 363.47 101.057 365.882 88.3714C366.036 88.0314 365.664 87.2969 365.417 87.2969L365.421 87.3001ZM345.168 113.307C338.048 113.89 330.715 113.663 323.566 113.278L325.548 101.952L325.968 101.41H347.298L345.168 113.307Z" fill="white"/>
|
||||
<path d="M203.82 48.3031C204.529 48.4666 205.908 48.8932 206.944 49.1563C209.866 49.9004 218.613 51.8057 220.714 53.0277C222.786 54.2337 223.421 56.6747 223.177 58.9263L202.479 162.677C201.433 166.23 198.322 168.418 194.746 169.056L164.781 168.941C162.494 168.081 161.359 165.903 161.285 163.527L176.665 86.1934C176.671 85.9464 175.594 85.8181 175.337 85.8694C172.264 86.5045 167.722 89.8083 164.598 90.1643C161.542 90.5107 159.101 88.7017 159.332 85.5487C160.765 79.6116 160.801 72.2856 162.436 66.5089C162.847 65.0591 163.861 63.8274 165.214 63.1923C174.821 58.8044 184.341 52.7423 194.079 48.7553C198.277 47.0361 199.506 47.3119 203.817 48.3031H203.82Z" fill="#FCD02B"/>
|
||||
<path d="M217.721 2.13574C221.749 -3.29773 229.63 2.70001 225.274 8.0918C224.986 8.45104 224.287 8.63105 224.203 8.77539L224.197 8.77832C223.819 9.41982 224.566 11.8519 224.79 12.7051C225.518 15.4828 226.673 20.5563 227.661 22.9844C227.786 23.2923 227.767 23.6454 228.178 23.7705C228.992 24.0163 239.786 19.9539 241.585 19.5947C242.364 19.0142 241.854 17.6667 242.887 16.4062C246.733 11.6976 254.268 16.8877 250.435 22.0293C249.059 23.8734 248.009 23.2992 246.479 23.8477C245.158 24.3225 235.42 45.9666 233.492 48.709C233.146 49.3023 232.703 49.976 231.969 50.0947C230.564 50.3225 224.925 47.9902 223.09 47.4385C212.508 44.2599 202.321 41.5209 191.483 39.2725C189.594 38.8812 186.451 39.4552 186.624 36.5078L190.643 9.81738C188.327 8.07873 188.052 4.18555 190.287 2.29004C193.973 -0.837286 199.107 2.57577 197.687 7.12402C197.487 7.75886 196.507 8.23362 197.017 9.02246L205.369 18.3242C207.739 16.0022 210.135 13.6735 212.582 11.4316C213.432 10.6522 217.002 8.06931 217.079 7.25781C217.258 5.30796 215.928 4.55403 217.721 2.13574ZM219.479 11.9248C217.034 13.2302 206.575 24.6463 205.022 24.7461C204.48 24.7814 204.031 24.5848 203.617 24.248C202.257 23.1348 199.858 19.4505 198.46 17.8467C198.216 17.5677 197.793 17.487 197.604 17.2754C197.453 17.1054 195.249 13.4103 194.62 14.1641L192.051 34.3682V34.373C205.054 36.3168 217.502 40.5537 230.024 44.3418L240.161 25.0762L240.163 25.0771V25.0742L240.161 25.0762L240.16 25.0752L240.162 25.0723C239.572 24.5196 226.682 30.0984 225.399 29.749C224.575 29.5244 224.389 28.8194 224.116 28.1299C222.269 23.4502 221.711 17.4201 219.953 12.7275C219.834 12.41 219.831 12.1301 219.479 11.9248Z" fill="#FCD02B"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_45235_124956">
|
||||
<rect width="500" height="175.589" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 418 KiB |
|
After Width: | Height: | Size: 133 KiB |
|
After Width: | Height: | Size: 381 KiB |
@@ -0,0 +1,4 @@
|
||||
<svg width="36" height="36" viewBox="0 0 36 36" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M0 18C0 8.05888 8.05888 0 18 0C27.9411 0 36 8.05888 36 18C36 27.9411 27.9411 36 18 36C8.05888 36 0 27.9411 0 18Z" fill="#335AE8"/>
|
||||
<path d="M21.6004 20.5992C23.3828 17.45 24.0303 14.4566 24.0303 14.4566H18.0355V12.2516H25.1652V11.3074H18.0355V8H14.7957V11.3074H8.31341V12.2516H14.7957V14.4566H9.28587V15.4008H20.4679C20.4679 15.559 20.4679 15.559 20.3054 15.7147C20.3054 16.8172 19.4955 18.3918 18.848 19.6524C10.5833 16.5033 8.15345 18.3918 7.50345 18.7082C1.99366 22.4877 7.17845 27.2115 7.98841 27.0557C13.8232 28.3164 17.548 25.9533 20.1429 22.9598C20.3054 23.118 20.4679 23.118 20.6279 23.118C22.4103 24.0623 31 28 31 28V23.2762C29.7025 23.2762 25.0053 21.7017 21.6004 20.5992ZM17.388 22.0156C13.3357 27.0557 8.47591 25.4812 7.66595 25.1647C5.72102 24.6926 5.07356 21.2295 7.50345 20.1271C11.5558 18.8664 15.1207 20.2853 17.713 21.5434C17.5505 21.8574 17.388 22.0156 17.388 22.0156Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1016 B |
|
After Width: | Height: | Size: 812 KiB |
|
After Width: | Height: | Size: 328 KiB |
|
After Width: | Height: | Size: 309 KiB |
|
After Width: | Height: | Size: 145 KiB |
|
After Width: | Height: | Size: 123 KiB |
|
After Width: | Height: | Size: 158 KiB |
|
After Width: | Height: | Size: 78 KiB |
|
After Width: | Height: | Size: 168 KiB |
|
After Width: | Height: | Size: 115 KiB |
|
After Width: | Height: | Size: 157 KiB |
|
After Width: | Height: | Size: 85 KiB |