From 453d95ef3528f3c40e9708cee63cb29aa6574b2b Mon Sep 17 00:00:00 2001 From: sunlei Date: Sat, 4 Jul 2026 07:49:18 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0Blog=20Live2D?= =?UTF-8?q?=E8=B5=84=E6=BA=90=E9=98=B2=E7=9B=97=E9=93=BE=E7=BD=91=E5=85=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env.example | 3 + API.md | 5 +- README.md | 6 +- .../application/blog-live2d-asset.service.ts | 190 +++++++++++++++ src/modules/asset/asset.module.ts | 9 +- .../contract/blog-live2d-asset.controller.ts | 62 +++++ .../asset/contract/blog-live2d-asset.dto.ts | 18 ++ .../asset/domain/blog-live2d-asset.types.ts | 13 + test/asset/blog-live2d-asset.service.spec.ts | 225 ++++++++++++++++++ .../asset/asset-module-contract.spec.ts | 31 ++- 10 files changed, 552 insertions(+), 10 deletions(-) create mode 100644 src/modules/asset/application/blog-live2d-asset.service.ts create mode 100644 src/modules/asset/contract/blog-live2d-asset.controller.ts create mode 100644 src/modules/asset/contract/blog-live2d-asset.dto.ts create mode 100644 src/modules/asset/domain/blog-live2d-asset.types.ts create mode 100644 test/asset/blog-live2d-asset.service.spec.ts diff --git a/.env.example b/.env.example index 9a49f06..4257524 100644 --- a/.env.example +++ b/.env.example @@ -11,6 +11,9 @@ MINIO_PORT=9000 MINIO_ACCESS_KEY=minioadmin MINIO_SECRET_KEY=minioadmin MINIO_BUCKET=kt-template-online +BLOG_LIVE2D_ALLOWED_ORIGINS=https://blog.kwitsukasa.top,http://localhost:5999 +BLOG_LIVE2D_BUCKET=kt-template-online +BLOG_LIVE2D_PREFIX=blog/live2d/pio WORDPRESS_BASE_URL=http://localhost WORDPRESS_HOST_HEADER= diff --git a/API.md b/API.md index 5fd670b..8c266da 100644 --- a/API.md +++ b/API.md @@ -93,7 +93,7 @@ Admin、Component、Dict、MinIO、Blog 管理、WordPress 管理和 QQBot 管 | 分组 | 关键变量 | | ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | MySQL | `DB_HOST`、`DB_PORT`、`DB_USERNAME`、`DB_PASSWORD`、`DB_DATABASE`、`DB_SYNC` | -| MinIO | `MINIO_ENDPOINT`、`MINIO_PORT`、`MINIO_ACCESS_KEY`、`MINIO_SECRET_KEY`、`MINIO_BUCKET` | +| MinIO | `MINIO_ENDPOINT`、`MINIO_PORT`、`MINIO_ACCESS_KEY`、`MINIO_SECRET_KEY`、`MINIO_BUCKET`、`BLOG_LIVE2D_ALLOWED_ORIGINS`、`BLOG_LIVE2D_BUCKET`、`BLOG_LIVE2D_PREFIX` | | Admin | `ADMIN_TOKEN_SECRET`、`ADMIN_COOKIE_SECURE`、`SNOWFLAKE_WORKER_ID`、`SNOWFLAKE_DATACENTER_ID` | | WordPress | `WORDPRESS_BASE_URL`、`WORDPRESS_HOST_HEADER`、`WORDPRESS_ADMIN_USERNAME`、`WORDPRESS_ADMIN_PASSWORD` | | Loki | `LOG_LEVEL`、`LOG_APP_NAME`、`LOKI_URL`、`LOKI_QUERY_HOST`、`LOKI_QUERY_SELECTOR` | @@ -331,9 +331,12 @@ WordPress rewrite 未开启导致 `/wp-json/*` 返回 404 时,后端会回退 | `GET` | `/minio/resource-proxy` | 代理读取资源 | | `GET` | `/minio/download` | 下载文件流 | | `DELETE` | `/minio/remove` | 删除文件 | +| `GET` | `/blog/live2d/pio/:version/*assetPath` | 公开读取 Pio Live2D 运行包资源,按 Referer/Origin 白名单防盗链 | `bucketName` 不传时使用 `MINIO_BUCKET`。 +Blog Live2D 运行包读取入口不使用 Vben 响应包装,直接返回 MinIO 文件流。`BLOG_LIVE2D_ALLOWED_ORIGINS` 配置允许的 Blog 源,例如 `https://blog.kwitsukasa.top,http://localhost:5999`;`BLOG_LIVE2D_BUCKET` 和 `BLOG_LIVE2D_PREFIX` 决定对象位置。路由支持嵌套资源路径,如 `textures/texture_00.png` 和 `motions/idle.motion3.json`,并拒绝绝对 URL、反斜杠、`.`/`..` 和多重编码后的路径逃逸。 + ## QQBot 管理 QQBot 运行态包括 NapCat 容器登录、OneBot v11 反向 WebSocket、MQTT 事件总线、账号能力绑定、在线命令、自动回复规则、权限名单、发送/接收日志和插件生态。 diff --git a/README.md b/README.md index f702a70..cd621b0 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ | `qqbot/plugins/bilibili-card` | 解析 QQ/NapCat Bilibili 卡片和短链,按账号事件绑定回复封面图和视频文字摘要 | | `qqbot/plugins/ff14-market` | XIVAPI + Universalis 物品解析和 FF14 市场查价 | | `qqbot/plugins/fflogs` | FFLogs v2 GraphQL 角色排名和指定高难最近记录查询 | -| `minio` | Bucket 检查、上传、列表、临时 URL、代理下载、删除 | +| `minio` | Bucket 检查、上传、列表、临时 URL、代理下载、删除,以及 Blog Live2D 运行包受控读取入口 | | `common` | 响应封装、异常过滤、请求日志、日期格式化、字典解码、Snowflake、工具服务 | ## 目录结构 @@ -59,7 +59,7 @@ ci/ Jenkins Agent/Docker 辅助文件 | 分组 | 变量 | | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | MySQL | `DB_HOST`、`DB_PORT`、`DB_USERNAME`、`DB_PASSWORD`、`DB_DATABASE`、`DB_SYNC` | -| MinIO | `MINIO_ENDPOINT`、`MINIO_PORT`、`MINIO_ACCESS_KEY`、`MINIO_SECRET_KEY`、`MINIO_BUCKET` | +| MinIO | `MINIO_ENDPOINT`、`MINIO_PORT`、`MINIO_ACCESS_KEY`、`MINIO_SECRET_KEY`、`MINIO_BUCKET`、`BLOG_LIVE2D_ALLOWED_ORIGINS`、`BLOG_LIVE2D_BUCKET`、`BLOG_LIVE2D_PREFIX` | | Admin | `ADMIN_TOKEN_SECRET`、`ADMIN_COOKIE_SECURE`、`SNOWFLAKE_WORKER_ID`、`SNOWFLAKE_DATACENTER_ID` | | WordPress | `WORDPRESS_BASE_URL`、`WORDPRESS_HOST_HEADER`、`WORDPRESS_ADMIN_USERNAME`、`WORDPRESS_ADMIN_PASSWORD`、`WORDPRESS_*_TIMEOUT_MS` | | Logging/Loki | `LOG_LEVEL`、`LOG_APP_NAME`、`LOKI_URL`、`LOKI_QUERY_HOST`、`LOKI_*` | @@ -71,6 +71,8 @@ ci/ Jenkins Agent/Docker 辅助文件 `DB_SYNC=true` 只适合本地开发或明确允许自动同步表结构的环境;生产应关闭并使用 SQL/迁移脚本。 +Blog Live2D 运行包存放在 MinIO,公开读取入口为 `/blog/live2d/pio/:version/*assetPath`。`BLOG_LIVE2D_ALLOWED_ORIGINS` 是允许加载 Pio runtime 的 Blog 域名白名单,`BLOG_LIVE2D_BUCKET` 和 `BLOG_LIVE2D_PREFIX` 指向运行包对象位置;未通过 Referer/Origin 白名单的请求会在读取 MinIO 前拒绝。 + QQBot 插件 worker 使用 BullMQ 队列串行执行同一插件安装实例的请求。K8s 生产清单包含内部服务 `kt-qqbot-plugin-redis`,生产 env 可将 `QQBOT_PLUGIN_QUEUE_REDIS_HOST` 配为该服务名。`QQBOT_PLUGIN_QUEUE_WAIT_TIMEOUT_MS` 控制排队等待窗口,插件 `operation.timeoutMs` 仍表示单次执行预算。 QQBot 插件定时任务由 manifest 的 `tasks` 声明,平台持久化到 `qqbot_plugin_task` / `qqbot_plugin_task_run`,通过 BullMQ Job Scheduler 调度并经插件 worker 的 `executeTask` 边界执行。`sql/qqbot-init.sql` 可为既有环境增量创建任务表和 Admin 菜单。Admin 页面路径为 `/qqbot/plugin-task`。定时任务队列可用 `QQBOT_PLUGIN_TASK_QUEUE_REDIS_*` 单独配置;留空时复用插件 worker 队列的 Redis 连接。BangDream Bestdori 主数据缓存使用 `BANGDREAM_TSUGU_CACHE_ROOT`,生产清单挂载到容器内 `/data/qqbot/plugins/bangdream/cache`,对应 k3d 节点可写 hostPath `/var/lib/rancher/k3s/kt-template-online-api/qqbot-plugins`。 diff --git a/src/modules/asset/application/blog-live2d-asset.service.ts b/src/modules/asset/application/blog-live2d-asset.service.ts new file mode 100644 index 0000000..e1ee2a1 --- /dev/null +++ b/src/modules/asset/application/blog-live2d-asset.service.ts @@ -0,0 +1,190 @@ +import { BadRequestException, Injectable } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import { MinioClientService } from './asset-minio.service'; +import type { + BlogLive2DAssetResult, + BlogLive2DRuntimeAssetPath, +} from '../domain/blog-live2d-asset.types'; + +const DEFAULT_ALLOWED_ORIGINS = 'https://blog.kwitsukasa.top'; +const DEFAULT_LIVE2D_BUCKET = 'kt-template-online'; +const DEFAULT_LIVE2D_PREFIX = 'blog/live2d/pio'; +const MAX_DECODE_DEPTH = 6; + +@Injectable() +export class BlogLive2DAssetService { + /** + * Creates the guarded Blog Live2D asset service. + * @param minioClientService - Existing MinIO helper used to stream versioned Pio runtime files. + * @param configService - Runtime config source for bucket, object prefix, and allowed browser origins. + */ + constructor( + private readonly minioClientService: MinioClientService, + private readonly configService: ConfigService, + ) {} + + /** + * Rejects browser asset requests that do not come from the configured Blog origins. + * @param referer - Browser Referer header; used for normal `