Skip to content

feat: major codebase refactor and CI/CD improvement#416

Merged
consistent-k merged 2 commits intomainfrom
dev
Mar 21, 2026
Merged

feat: major codebase refactor and CI/CD improvement#416
consistent-k merged 2 commits intomainfrom
dev

Conversation

@consistent-k
Copy link
Copy Markdown
Owner

@consistent-k consistent-k commented Mar 21, 2026

Summary by CodeRabbit

发布说明

  • 基础设施

    • 升级 Node.js 至 v24 版本
    • 新增 CI/CD 工作流,包括代码检查、类型验证与提交规范检查
    • 新增 CodeQL 安全分析和 Dependabot 自动更新机制
    • 配置依赖项自动审查和合并
  • 代码质量

    • 新增 ESLint 及 Prettier 代码风格规则
    • 增强错误日志记录和类型安全
    • 优化路由注册机制
  • 文档

    • 新增开发指南和项目约定文档

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 21, 2026

概述

此PR进行了项目结构的大规模重组和现代化更新,包括迁移ESLint配置至扁平模式、添加Cursor规则文档、重构路由架构(从单个路由文件转变为工厂函数模式)、增强GitHub Actions工作流、升级Node.js至v24、改进类型安全和错误处理。

变更汇总

Cohort / File(s) Summary
Cursor规则文件
.cursor/rules/code-style.mdc, .cursor/rules/error-handling.mdc, .cursor/rules/general.mdc, .cursor/rules/routes.mdc
新增4个Cursor规则文档,定义TypeScript/TSX代码风格、错误处理标准、项目概述和路由创建约定。
ESLint配置升级
.eslintrc.cjs, eslint.config.js
删除传统ESLint配置,迁移至扁平配置(flat config)格式,集成@eslint/jstypescript-eslintimport-x插件。
GitHub Actions工作流
.github/workflows/ci.yml, .github/workflows/codeql.yml, .github/workflows/dependabot-auto-merge.yml, .github/workflows/dependency-review.yml, .github/workflows/docker-image.yml
新增5个工作流:CI检查(lint/typecheck/commitlint)、CodeQL分析、Dependabot自动合并、依赖审查、更新Docker镜像构建流程(升级工具版本至v3/v6)。
项目配置
.github/dependabot.yml, .npmrc, pnpm-workspace.yaml, Dockerfile, AGENTS.md
更新Dependabot调度策略、添加npm镜像配置、升级Node.js版本(22→24)、新增项目文档。
依赖和脚本
package.json
新增lint/typecheck/format脚本,更新多个依赖版本,替换eslint-plugin-import为import-x,指定pnpm包管理器版本。
配置修复
src/config/index.ts, src/utils/logger/index.ts
修复logger配置属性名(loogerlogger),调整缓存TTL计算逻辑。
360kan路由优化
src/routes/360kan/detail.ts, src/routes/360kan/home.ts, src/routes/360kan/homeVod.ts, src/routes/360kan/play.ts, src/routes/360kan/search.ts
改进类型注解、使用const替代let、优化错误日志、实现动态年份列表生成。
路由架构重构(CMS工厂模式)
src/routes/360zy/index.ts, src/routes/bdzy/index.ts, src/routes/bfzy/index.ts, src/routes/feifan/index.ts, src/routes/guangsuzy/index.ts, src/routes/hongniuzy/index.ts, src/routes/huyazy/index.ts, src/routes/ikunzy/index.ts, src/routes/lzzy/index.ts, src/routes/mdzy/index.ts, src/routes/sdzy/index.ts, src/routes/subozy/index.ts
在12个提供者目录下新增index.ts,每个都通过createCMSRoutes(namespace)导出路由。
删除单个路由定义
src/routes/*/category.ts, src/routes/*/detail.ts, src/routes/*/home.ts, src/routes/*/homeVod.ts, src/routes/*/play.ts, src/routes/*/search.ts(跨多个提供者)
删除72个个别路由文件,统一由工厂函数生成。
命名空间更新
src/routes/feifan/namespace.ts, src/routes/hongniuzy/namespace.ts
将命名空间URL从HTTP升级至HTTPS。
路由注册系统
src/routes/registry.ts, src/routes/proxy.ts
支持routes: Route[]数组导出格式,添加代理目标允许列表控制(目标验证器、403响应)。
CMS工厂和类型系统
src/utils/cms/factory.ts, src/types/index.ts
新增工厂函数生成标准CMS路由定义,导出Namespace接口。
CMS处理程序类型化
src/utils/cms/*/index.ts(category、detail、home、homeVod、play、search)
为所有CMS处理程序的namespace参数添加Namespace类型注解,改进错误日志、使用type-only导入。
工具函数改进
src/utils/format/index.ts, src/utils/common-utils.ts, src/utils/request/index.ts, src/middleware/cache.ts, src/middleware/jsonReturn.ts
删除formatStrByReg、替换lodash trim、更新node:os导入、删除request模块、改进缓存错误处理。

Sequence Diagram(s)

sequenceDiagram
    actor Client
    participant Registry as src/routes/registry.ts
    participant Module as Route Module<br/>(index.ts)
    participant Factory as createCMSRoutes<br/>(factory.ts)
    participant Handlers as CMS Handlers<br/>(utils/cms/*)
    participant Hono as Hono App

    Client->>Registry: 应用启动
    Registry->>Module: 动态导入提供者module
    Module->>Factory: 调用 createCMSRoutes(namespace)
    Factory->>Handlers: 创建6个路由<br/>(home/homeVod/category/detail/play/search)
    Handlers-->>Factory: 返回Route[]
    Factory-->>Module: Route[] 数组
    Module-->>Registry: 导出 {routes: Route[]}
    Registry->>Hono: 遍历routes注册处理程序
    Hono-->>Client: 暴露路由端点
Loading
sequenceDiagram
    participant Client
    participant Proxy as proxy.ts
    participant Validator as isAllowedTarget()
    participant Upstream as Upstream Server

    Client->>Proxy: POST /proxy<br/>(含x-proxy-target)
    Proxy->>Validator: 检查目标主机名
    alt 目标在允许列表中
        Validator-->>Proxy: ✓ 允许
        Proxy->>Upstream: fetch(proxyRequest)
        Upstream-->>Proxy: 响应
        Proxy-->>Client: 转发响应<br/>(状态/头/body)
    else 目标不允许
        Validator-->>Proxy: ✗ 禁止
        Proxy-->>Client: 403 Forbidden
    end
Loading

预估代码审查工作量

🎯 4 (Complex) | ⏱️ ~75 minutes

可能相关的PR

诗歌

🐰 工厂模式兔兔欢,
一个函数生六路,
ESLint扁平新模样,
GitHub Actions筑高墙,
代码类型更安全!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR标题简洁准确地反映了本次变更的核心内容:重构代码库和改进CI/CD流程,涵盖了最主要的改动。

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch dev

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link
Copy Markdown

github-actions bot commented Mar 21, 2026

Dependency Review

The following issues were found:

  • ✅ 0 vulnerable package(s)
  • ✅ 0 package(s) with incompatible licenses
  • ✅ 0 package(s) with invalid SPDX license definitions
  • ⚠️ 10 package(s) with unknown licenses.
  • ⚠️ 1 packages with OpenSSF Scorecard issues.

View full job summary

@github-advanced-security
Copy link
Copy Markdown

You are seeing this message because GitHub Code Scanning has recently been set up for this repository, or this pull request contains the workflow file for the Code Scanning tool.

What Enabling Code Scanning Means:

  • The 'Security' tab will display more code scanning analysis results (e.g., for the default branch).
  • Depending on your configuration and choice of analysis tool, future pull requests will be annotated with code scanning analysis results.
  • You will be able to see the analysis results for the pull request's branch on this overview once the scans have completed and the checks have passed.

For more information about GitHub Code Scanning, check out the documentation.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 14

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/routes/360kan/detail.ts (1)

77-85: ⚠️ Potential issue | 🟠 Major

请先校验 id 格式再拆分。

当前直接 id.split('+'),当 id 缺失或格式不合法时会触发异常/错误请求参数,建议在入口做显式校验并返回业务错误。

建议修复
-        const { id } = body;
-
-        const tid_list = id.split('+');
+        const { id } = body;
+        if (typeof id !== 'string') {
+            return { code: ERROR_CODE, message: DETAIL_MESSAGE.ERROR, data: [] };
+        }
+        const [videoId, cat] = id.split('+');
+        if (!videoId || !cat) {
+            return { code: ERROR_CODE, message: DETAIL_MESSAGE.ERROR, data: [] };
+        }

         const res = await request.get<DetailDataOrigin>(`${namespace.url}/v1/detail`, {
             params: {
-                cat: tid_list[1],
-                id: tid_list[0]
+                cat,
+                id: videoId
             }
         });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/routes/360kan/detail.ts` around lines 77 - 85, 在使用 id 前先校验它的格式和存在性,避免直接执行
const tid_list = id.split('+') 导致异常:在 split 调用前检查 body.id 为非空字符串且包含
'+',并且拆分后得到两个部分 (例如 parts.length ===
2),否则在入口处返回一个明确的业务错误响应/抛出带有友好消息的错误;更新使用到的变量名(id / tid_list)以及调用
request.get<DetailDataOrigin>(...) 的逻辑以依赖校验后的 tid_list[0]/tid_list[1](或解构出的
idPart, catPart)。
🧹 Nitpick comments (6)
.github/workflows/docker-image.yml (1)

10-49: 建议增加并发控制,避免同标签并发推送互相覆盖。

当前在主分支连续提交时可能并发执行构建并同时推送 latest。建议为该 job 增加 concurrency

♻️ 建议修改
 jobs:
     build:
+        concurrency:
+            group: docker-image-${{ github.ref }}
+            cancel-in-progress: true
         runs-on: ubuntu-latest
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/docker-image.yml around lines 10 - 49, The build job can
run concurrently and cause multiple runners to push the same tag (latest) at
once; add a concurrency block to the job named build to serialize or cancel
in-progress runs for the same ref (so pushes via docker/build-push-action@v6
don't overwrite each other), e.g. define a concurrency group using the workflow
and branch/ref (or tag) like github.ref and set cancel-in-progress true; this
ensures the job that runs the step with id meta and the final push
(docker/build-push-action@v6) will not run in parallel for the same latest tag.
src/utils/cms/detail/index.ts (1)

14-20: 可优化:避免重复分割字符串

当前实现对每个 item 调用了两次 split('$'),可以优化为单次分割。

♻️ 建议优化
 function parseEpisodes(str: string): VodPlayList['urls'] {
     const arr = str.split('#')?.filter((item) => item);
-    return arr.map((item) => ({
-        name: item.split('$')[0],
-        url: item.split('$')[1]
-    }));
+    return arr.map((item) => {
+        const [name, url] = item.split('$');
+        return { name, url };
+    });
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/utils/cms/detail/index.ts` around lines 14 - 20, The parseEpisodes
function performs split('$') twice per item which is inefficient; update
parseEpisodes (return type VodPlayList['urls']) to split each item only once
(e.g., const [name, url] = item.split('$')) and use those variables to build the
returned object, also handle missing parts safely (default to '' or undefined)
so name/url won't throw if split yields fewer elements.
AGENTS.md (1)

43-55: 代码块缺少语言标识符

根据静态分析提示,架构目录结构的代码块应指定语言以便于语法高亮和工具解析。

📝 建议修复
-```
+```text
 src/
   index.ts          # Server entry: boots `@hono/node-server`
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@AGENTS.md` around lines 43 - 55, The markdown code block showing the project
directory tree in AGENTS.md lacks a language identifier; update the opening
fence to include a language (e.g., ```text or ```bash) so syntax highlighters
and tools can parse it — locate the directory-tree code fence in AGENTS.md (the
block that begins with the src/ listing) and change the triple-backtick line to
include the chosen language token.
.cursor/rules/code-style.mdc (3)

36-38: 导入分组描述与示例不完全一致(缺少相对路径组规则)。

这里写的是 builtins → externals → internals,但上方示例还包含了 relative imports。建议把 relative 组也纳入强制顺序(例如放在 internals 之后),避免执行时出现分歧。

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.cursor/rules/code-style.mdc around lines 36 - 38, The import-group rule
currently states "Import groups must be separated by blank lines: builtins →
externals → internals" but the examples include relative imports, so update the
rule text and examples to include a distinct "relative" group and enforce the
order builtins → externals → internals → relative; also update the
alphabetization note and the `import type` guidance to explicitly mention that
type-only imports in the relative group should use `import type`, and adjust any
sample import blocks and wording in the "Import groups must be separated by
blank lines" section and accompanying examples to reflect the new group and
ordering.

71-71: “禁止注释”建议放宽为“仅保留必要注释”。

完全禁止注释在复杂业务或边界条件下会降低可维护性。建议改成“默认不写冗余注释,但允许解释非直观决策/约束”。

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.cursor/rules/code-style.mdc at line 71, 将规则 ".cursor/rules/code-style.mdc"
中原始条目 "No comments unless explicitly requested"
放宽为“仅保留必要注释(默认避免冗余注释,但允许解释非直观决策或边界条件)”,并更新该规则的描述、示例和违反情形以说明何时允许注释(例如解释复杂算法、边界条件、设计权衡或外部约束),确保规则标题、正文和示例中的文本都一致且包含明确的允许场景。

59-60: 当前仅禁止显式 any,但 noImplicitAny: false 仍允许隐式 any 产生,存在类型安全缺口。

TypeScript 配置设置 noImplicitAny: false,但仅用 ESLint 规则 @typescript-eslint/no-explicit-any 限制,无法完全覆盖隐式 any 的场景(如函数参数无类型注解、复杂类型推断、JSON.parse 返回值等)。建议补充说明是否计划逐步迁移至 noImplicitAny: true,或明确本项目的类型安全策略。

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.cursor/rules/code-style.mdc around lines 59 - 60, 当前规则文件只通过 ESLint 的
`@typescript-eslint/no-explicit-any` 禁止显式 any,但 TypeScript 配置中的 noImplicitAny:
false 仍允许隐式 any,导致类型安全缺口;请在规则文档中明确类型策略:一是说明是否将逐步切换到 noImplicitAny:
true(并给出迁移计划或分阶段启用策略),二是或明确保持 noImplicitAny: false 时需补充的约束(例如强制函数参数和返回值显式注解、对
JSON.parse/unknown 的处理、以及使用 eslint-plugin `@typescript-eslint` 的辅助规则),并在文档中引用配置键
noImplicitAny 和规则 `@typescript-eslint/no-explicit-any` 以便维护者定位和执行变更。
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.cursor/rules/error-handling.mdc:
- Around line 50-60: The catch block logs the error by interpolating `${error}`
which is inconsistent with other modules; change the logger.error call in this
catch (and the returned message handling) to safely extract the error message
using a conditional like `error instanceof Error ? error.message :
String(error)` when logging and when forming any user-facing message (refer to
ctx.res.headers.set, logger.error, SEARCH_MESSAGE.ERROR, and SYSTEM_ERROR_CODE
to locate the catch block and return object); ensure the log includes the
descriptive context (`SEARCH_MESSAGE.ERROR - ${namespace.name}`) combined with
the safe error string.

In @.github/dependabot.yml:
- Around line 17-21: The Dependabot config currently sets the update interval to
monthly which lengthens vulnerability exposure; locate the npm package-ecosystem
block (look for package-ecosystem: "npm" and the interval field) and change
interval: monthly to interval: weekly so npm dependency updates run weekly; keep
existing day/time/timezone and open-pull-requests-limit values unchanged unless
you want different cadence.

In @.github/workflows/codeql.yml:
- Around line 15-17: The workflow permissions block currently only sets
security-events: write; update the permissions section (the permissions mapping
that contains security-events: write) to also include contents: read and
actions: read so checkout and CodeQL actions have sufficient token scopes; keep
YAML indentation consistent with the existing permissions mapping and ensure the
added keys are at the same level as security-events.

In `@AGENTS.md`:
- Around line 109-112: The AGENTS.md "Named exports only" rule is inconsistent
with the codebase, which uses default exports (e.g., export default app) in
route files like registry.ts and proxy.ts; update AGENTS.md to reflect the
actual convention — either allow default exports for route files (and document
the expected default export shape, e.g., the exported app/route object) or
change the documentation to state the explicit exception for route files —
ensure the doc text and examples match the code (mention the export form "export
default app" and the Route/Namespace expectations).

In `@src/config/index.ts`:
- Line 15: The ttl assignment using process.env.CACHE_TTL currently parses
blindly and can produce NaN/zero/negative values; update the logic that sets ttl
in src/config/index.ts to validate CACHE_TTL by parsing with parseInt(base 10),
checking that the result is a finite positive integer (>0), and only then
multiplying by 1000; otherwise fall back to a safe default (e.g., 60_000).
Ensure you update the ttl variable (and any related export) so invalid, zero or
negative env values do not become the effective TTL.

In `@src/middleware/cache.ts`:
- Around line 46-54: The code currently sets ctx.status(200) and
ctx.header('Vod-Hub-Cache-Status','HIT') before attempting JSON.parse(value) so
a parse failure leaves a misleading HIT; change the flow in the cache middleware
so JSON.parse(value) runs inside a try and only after a successful parse you
call ctx.set('data', parsed), ctx.status(200), and
ctx.header('Vod-Hub-Cache-Status','HIT'); on catch log the parsing error for
cacheKey (as already done), do NOT set the HIT header/status or ctx.data
(optionally set 'Vod-Hub-Cache-Status' to 'MISS' or leave unset), and allow the
middleware to continue to origin/recompute the response.
- Around line 16-20: The POST body hashing in the cache middleware is brittle
because calling clonedReq.json() outside a try/catch will throw on non-JSON or
malformed bodies and short-circuit the request; wrap the JSON parse in a
try/catch around the clonedReq.json() call used to compute bodyHash (the
clonedReq = ctx.req.raw.clone(), CryptoJS.MD5 and Hex usage) and on parse
failure either skip adding the bodyHash (leave bodyHash as '') or compute a hash
from clonedReq.text() as a fallback, ensuring the middleware continues without
throwing.

In `@src/routes/360kan/detail.ts`:
- Around line 105-113: The code assumes data.playlinksdetail[playFormat] exists
before accessing default_url/quality/title which can throw if undefined; add a
null check for items (e.g., const items = data.playlinksdetail[playFormat]; if
(!items) { /* skip this playFormat/line and continue processing others */ }) and
only build VodPlayList['urls'] when items is defined, ensuring you skip invalid
sources instead of accessing items.default_url, items.quality or items.title;
reference symbols: playlinksdetail, playFormat, items, VodPlayList['urls'],
default_url, quality, title.

In `@src/routes/360kan/home.ts`:
- Around line 14-17: The year array generation using Array.from({ length:
currentYear - 2006 }, (_, i) => { ... }) misses the 2006 boundary; update the
length calculation to be inclusive (e.g. use currentYear - 2006 + 1) so the loop
produces years from currentYear down through 2006, keeping the rest of the
mapping logic in that same function intact.

In `@src/routes/360kan/homeVod.ts`:
- Around line 51-52: The property assignment for type_name can return undefined
when item.cat exists but isn't a key in typeNameMap; update the expression that
sets type_name (where typeNameMap is referenced) to explicitly fall back to an
empty string if the lookup is missing (e.g., check whether typeNameMap[item.cat]
is present or use a nullish-coalescing-like check) so type_name is always a
string rather than undefined.

In `@src/routes/proxy.ts`:
- Around line 54-56: The current check returns ctx.json with a too-narrow cast
`response.status as 400 | 500`; remove that assertion and pass the actual
numeric status to ctx.json (or let TypeScript infer it) so any 4xx/5xx like
401/403/404/502 are preserved; locate the failing branch using response.ok,
response.status and the ctx.json call in this route handler and replace the cast
with the real response.status (or a safe fallback number) so the correct HTTP
status is returned.
- Around line 12-22: The isAllowedTarget function uses
hostname.endsWith(allowed) which allows malicious-example.com to match
example.com; change the logic in isAllowedTarget to allow only exact matches or
legitimate subdomains by checking url.hostname === allowed ||
url.hostname.endsWith('.' + allowed) for each allowed entry, and keep the
try/catch behavior; also remove the incorrect type assertion of response.status
as 400 | 500 and instead treat response.status as a number and check its range
(e.g., if (response.status >= 400 && response.status < 600) or test
Math.floor(response.status/100) === 4 || 5) wherever the code currently casts
response.status, so all 4xx/5xx codes are handled correctly.

In `@src/routes/registry.ts`:
- Around line 73-76: When dynamically importing route modules in the block that
assigns route.handler (variables mod, imported, route), add a defensive check
for the case where Array.isArray(imported) and imported.find(...) returns
undefined: if no matching route is found, do not assign undefined to
route.handler; instead set route.handler to a safe fallback (e.g., a handler
that returns a 404/throws a descriptive error) or throw/log a descriptive error
immediately so the later call to route.handler(ctx) cannot cause a TypeError.
Ensure this logic is applied where mod, imported and route.handler are handled
so any missing route entry is detected and handled before invoking
route.handler.

In `@src/utils/cms/factory.ts`:
- Around line 11-13: In createCMSRoutes, enforce and validate namespace.url at
the factory entry (right after extracting name) so downstream CMS handlers never
receive undefined; check that namespace.url is a non-empty string (optionally
normalize/trim) and either throw a clear Error (e.g. "Missing or invalid
namespace.url for namespace: <name>") or set a safe default only if intended;
assign the validated value to a local variable (e.g. baseUrl) and use that for
building all Route URLs so all handlers rely on a guaranteed URL.

---

Outside diff comments:
In `@src/routes/360kan/detail.ts`:
- Around line 77-85: 在使用 id 前先校验它的格式和存在性,避免直接执行 const tid_list = id.split('+')
导致异常:在 split 调用前检查 body.id 为非空字符串且包含 '+',并且拆分后得到两个部分 (例如 parts.length ===
2),否则在入口处返回一个明确的业务错误响应/抛出带有友好消息的错误;更新使用到的变量名(id / tid_list)以及调用
request.get<DetailDataOrigin>(...) 的逻辑以依赖校验后的 tid_list[0]/tid_list[1](或解构出的
idPart, catPart)。

---

Nitpick comments:
In @.cursor/rules/code-style.mdc:
- Around line 36-38: The import-group rule currently states "Import groups must
be separated by blank lines: builtins → externals → internals" but the examples
include relative imports, so update the rule text and examples to include a
distinct "relative" group and enforce the order builtins → externals → internals
→ relative; also update the alphabetization note and the `import type` guidance
to explicitly mention that type-only imports in the relative group should use
`import type`, and adjust any sample import blocks and wording in the "Import
groups must be separated by blank lines" section and accompanying examples to
reflect the new group and ordering.
- Line 71: 将规则 ".cursor/rules/code-style.mdc" 中原始条目 "No comments unless
explicitly requested"
放宽为“仅保留必要注释(默认避免冗余注释,但允许解释非直观决策或边界条件)”,并更新该规则的描述、示例和违反情形以说明何时允许注释(例如解释复杂算法、边界条件、设计权衡或外部约束),确保规则标题、正文和示例中的文本都一致且包含明确的允许场景。
- Around line 59-60: 当前规则文件只通过 ESLint 的 `@typescript-eslint/no-explicit-any` 禁止显式
any,但 TypeScript 配置中的 noImplicitAny: false 仍允许隐式
any,导致类型安全缺口;请在规则文档中明确类型策略:一是说明是否将逐步切换到 noImplicitAny:
true(并给出迁移计划或分阶段启用策略),二是或明确保持 noImplicitAny: false 时需补充的约束(例如强制函数参数和返回值显式注解、对
JSON.parse/unknown 的处理、以及使用 eslint-plugin `@typescript-eslint` 的辅助规则),并在文档中引用配置键
noImplicitAny 和规则 `@typescript-eslint/no-explicit-any` 以便维护者定位和执行变更。

In @.github/workflows/docker-image.yml:
- Around line 10-49: The build job can run concurrently and cause multiple
runners to push the same tag (latest) at once; add a concurrency block to the
job named build to serialize or cancel in-progress runs for the same ref (so
pushes via docker/build-push-action@v6 don't overwrite each other), e.g. define
a concurrency group using the workflow and branch/ref (or tag) like github.ref
and set cancel-in-progress true; this ensures the job that runs the step with id
meta and the final push (docker/build-push-action@v6) will not run in parallel
for the same latest tag.

In `@AGENTS.md`:
- Around line 43-55: The markdown code block showing the project directory tree
in AGENTS.md lacks a language identifier; update the opening fence to include a
language (e.g., ```text or ```bash) so syntax highlighters and tools can parse
it — locate the directory-tree code fence in AGENTS.md (the block that begins
with the src/ listing) and change the triple-backtick line to include the chosen
language token.

In `@src/utils/cms/detail/index.ts`:
- Around line 14-20: The parseEpisodes function performs split('$') twice per
item which is inefficient; update parseEpisodes (return type
VodPlayList['urls']) to split each item only once (e.g., const [name, url] =
item.split('$')) and use those variables to build the returned object, also
handle missing parts safely (default to '' or undefined) so name/url won't throw
if split yields fewer elements.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 3f9f7ad3-9285-494a-950d-ad19d3172da3

📥 Commits

Reviewing files that changed from the base of the PR and between 398717c and 17e0b17.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (126)
  • .cursor/rules/code-style.mdc
  • .cursor/rules/error-handling.mdc
  • .cursor/rules/general.mdc
  • .cursor/rules/routes.mdc
  • .eslintrc.cjs
  • .github/dependabot.yml
  • .github/workflows/ci.yml
  • .github/workflows/codeql.yml
  • .github/workflows/dependabot-auto-merge.yml
  • .github/workflows/dependency-review.yml
  • .github/workflows/docker-image.yml
  • .npmrc
  • AGENTS.md
  • Dockerfile
  • eslint.config.js
  • package.json
  • pnpm-workspace.yaml
  • src/config/index.ts
  • src/middleware/cache.ts
  • src/middleware/jsonReturn.ts
  • src/routes/360kan/detail.ts
  • src/routes/360kan/home.ts
  • src/routes/360kan/homeVod.ts
  • src/routes/360kan/play.ts
  • src/routes/360kan/search.ts
  • src/routes/360zy/category.ts
  • src/routes/360zy/detail.ts
  • src/routes/360zy/home.ts
  • src/routes/360zy/homeVod.ts
  • src/routes/360zy/index.ts
  • src/routes/360zy/play.ts
  • src/routes/360zy/search.ts
  • src/routes/bdzy/category.ts
  • src/routes/bdzy/detail.ts
  • src/routes/bdzy/home.ts
  • src/routes/bdzy/homeVod.ts
  • src/routes/bdzy/index.ts
  • src/routes/bdzy/play.ts
  • src/routes/bdzy/search.ts
  • src/routes/bfzy/category.ts
  • src/routes/bfzy/detail.ts
  • src/routes/bfzy/home.ts
  • src/routes/bfzy/homeVod.ts
  • src/routes/bfzy/index.ts
  • src/routes/bfzy/play.ts
  • src/routes/bfzy/search.ts
  • src/routes/feifan/category.ts
  • src/routes/feifan/detail.ts
  • src/routes/feifan/home.ts
  • src/routes/feifan/homeVod.ts
  • src/routes/feifan/index.ts
  • src/routes/feifan/namespace.ts
  • src/routes/feifan/play.ts
  • src/routes/feifan/search.ts
  • src/routes/guangsuzy/category.ts
  • src/routes/guangsuzy/detail.ts
  • src/routes/guangsuzy/home.ts
  • src/routes/guangsuzy/homeVod.ts
  • src/routes/guangsuzy/index.ts
  • src/routes/guangsuzy/play.ts
  • src/routes/guangsuzy/search.ts
  • src/routes/hongniuzy/category.ts
  • src/routes/hongniuzy/detail.ts
  • src/routes/hongniuzy/home.ts
  • src/routes/hongniuzy/homeVod.ts
  • src/routes/hongniuzy/index.ts
  • src/routes/hongniuzy/namespace.ts
  • src/routes/hongniuzy/play.ts
  • src/routes/hongniuzy/search.ts
  • src/routes/huyazy/category.ts
  • src/routes/huyazy/detail.ts
  • src/routes/huyazy/home.ts
  • src/routes/huyazy/homeVod.ts
  • src/routes/huyazy/index.ts
  • src/routes/huyazy/play.ts
  • src/routes/huyazy/search.ts
  • src/routes/ikunzy/category.ts
  • src/routes/ikunzy/detail.ts
  • src/routes/ikunzy/home.ts
  • src/routes/ikunzy/homeVod.ts
  • src/routes/ikunzy/index.ts
  • src/routes/ikunzy/play.ts
  • src/routes/ikunzy/search.ts
  • src/routes/lzzy/category.ts
  • src/routes/lzzy/detail.ts
  • src/routes/lzzy/home.ts
  • src/routes/lzzy/homeVod.ts
  • src/routes/lzzy/index.ts
  • src/routes/lzzy/play.ts
  • src/routes/lzzy/search.ts
  • src/routes/mdzy/category.ts
  • src/routes/mdzy/detail.ts
  • src/routes/mdzy/home.ts
  • src/routes/mdzy/homeVod.ts
  • src/routes/mdzy/index.ts
  • src/routes/mdzy/play.ts
  • src/routes/mdzy/search.ts
  • src/routes/proxy.ts
  • src/routes/registry.ts
  • src/routes/sdzy/category.ts
  • src/routes/sdzy/detail.ts
  • src/routes/sdzy/home.ts
  • src/routes/sdzy/homeVod.ts
  • src/routes/sdzy/index.ts
  • src/routes/sdzy/play.ts
  • src/routes/sdzy/search.ts
  • src/routes/subozy/category.ts
  • src/routes/subozy/detail.ts
  • src/routes/subozy/home.ts
  • src/routes/subozy/homeVod.ts
  • src/routes/subozy/index.ts
  • src/routes/subozy/play.ts
  • src/routes/subozy/search.ts
  • src/types/index.ts
  • src/utils/cms/category/index.ts
  • src/utils/cms/detail/index.ts
  • src/utils/cms/factory.ts
  • src/utils/cms/home/index.ts
  • src/utils/cms/homeVod/index.ts
  • src/utils/cms/play/index.ts
  • src/utils/cms/search/index.ts
  • src/utils/common-utils.ts
  • src/utils/filters/index.ts
  • src/utils/format/index.ts
  • src/utils/logger/index.ts
  • src/utils/request/index.ts
💤 Files with no reviewable changes (74)
  • .eslintrc.cjs
  • src/routes/360zy/home.ts
  • src/routes/bfzy/category.ts
  • src/routes/360zy/category.ts
  • src/routes/360zy/play.ts
  • src/routes/360zy/search.ts
  • src/routes/bfzy/play.ts
  • src/routes/bfzy/search.ts
  • src/routes/feifan/detail.ts
  • src/routes/bdzy/homeVod.ts
  • src/routes/360zy/homeVod.ts
  • src/routes/bdzy/detail.ts
  • src/routes/hongniuzy/detail.ts
  • src/routes/bdzy/category.ts
  • src/routes/huyazy/search.ts
  • src/routes/feifan/home.ts
  • src/routes/sdzy/category.ts
  • src/routes/bfzy/detail.ts
  • src/routes/bdzy/home.ts
  • src/routes/guangsuzy/detail.ts
  • src/routes/feifan/search.ts
  • src/routes/mdzy/homeVod.ts
  • src/routes/bfzy/homeVod.ts
  • src/routes/hongniuzy/search.ts
  • src/routes/ikunzy/home.ts
  • src/routes/bfzy/home.ts
  • src/routes/guangsuzy/category.ts
  • src/routes/ikunzy/category.ts
  • src/routes/lzzy/detail.ts
  • src/routes/360zy/detail.ts
  • src/routes/subozy/search.ts
  • src/routes/sdzy/homeVod.ts
  • src/routes/feifan/homeVod.ts
  • src/routes/hongniuzy/play.ts
  • src/routes/guangsuzy/play.ts
  • src/routes/hongniuzy/home.ts
  • src/routes/huyazy/home.ts
  • src/routes/feifan/category.ts
  • src/routes/feifan/play.ts
  • src/routes/sdzy/detail.ts
  • src/routes/guangsuzy/search.ts
  • src/routes/huyazy/homeVod.ts
  • src/routes/lzzy/search.ts
  • src/routes/ikunzy/search.ts
  • src/routes/subozy/home.ts
  • src/utils/request/index.ts
  • src/routes/hongniuzy/homeVod.ts
  • src/routes/lzzy/category.ts
  • src/routes/ikunzy/detail.ts
  • src/routes/bdzy/search.ts
  • src/routes/subozy/detail.ts
  • src/routes/sdzy/search.ts
  • src/routes/mdzy/play.ts
  • src/routes/subozy/play.ts
  • src/routes/guangsuzy/home.ts
  • src/routes/bdzy/play.ts
  • src/routes/lzzy/home.ts
  • src/routes/ikunzy/play.ts
  • src/routes/mdzy/search.ts
  • src/routes/sdzy/home.ts
  • src/routes/mdzy/detail.ts
  • src/routes/subozy/category.ts
  • src/routes/hongniuzy/category.ts
  • src/routes/mdzy/category.ts
  • src/routes/lzzy/play.ts
  • src/routes/mdzy/home.ts
  • src/routes/sdzy/play.ts
  • src/routes/lzzy/homeVod.ts
  • src/routes/subozy/homeVod.ts
  • src/routes/guangsuzy/homeVod.ts
  • src/routes/ikunzy/homeVod.ts
  • src/routes/huyazy/detail.ts
  • src/routes/huyazy/category.ts
  • src/routes/huyazy/play.ts

Comment on lines +50 to +60
} catch (error) {
// Exception path — prevent caching failed responses
ctx.res.headers.set('Cache-Control', 'no-cache');
logger.error(`${SEARCH_MESSAGE.ERROR} - ${namespace.name} - ${error}`);
return {
code: SYSTEM_ERROR_CODE,
message: SEARCH_MESSAGE.ERROR,
data: []
};
}
};
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

文档示例与实际实现不一致

示例代码中使用 ${error} 直接插值,但实际代码实现(如 category/index.tsdetail/index.ts 等)使用的是更安全的模式:${error instanceof Error ? error.message : String(error)}

建议更新文档以反映实际的最佳实践。

📝 建议修复
     } catch (error) {
         // Exception path — prevent caching failed responses
         ctx.res.headers.set('Cache-Control', 'no-cache');
-        logger.error(`${SEARCH_MESSAGE.ERROR} - ${namespace.name} - ${error}`);
+        logger.error(`${SEARCH_MESSAGE.ERROR} - ${namespace.name} - ${error instanceof Error ? error.message : String(error)}`);
         return {
             code: SYSTEM_ERROR_CODE,
             message: SEARCH_MESSAGE.ERROR,
             data: []
         };
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.cursor/rules/error-handling.mdc around lines 50 - 60, The catch block logs
the error by interpolating `${error}` which is inconsistent with other modules;
change the logger.error call in this catch (and the returned message handling)
to safely extract the error message using a conditional like `error instanceof
Error ? error.message : String(error)` when logging and when forming any
user-facing message (refer to ctx.res.headers.set, logger.error,
SEARCH_MESSAGE.ERROR, and SYSTEM_ERROR_CODE to locate the catch block and return
object); ensure the log includes the descriptive context (`SEARCH_MESSAGE.ERROR
- ${namespace.name}`) combined with the safe error string.

Comment on lines +17 to +21
interval: monthly
day: friday
time: '12:00'
timezone: Asia/Singapore
open-pull-requests-limit: 10
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

npm 改为月更会显著拉长漏洞暴露窗口。

对应用依赖建议至少保持 weekly,否则高危修复可能延迟数周。

建议修复
-          interval: monthly
+          interval: weekly
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
interval: monthly
day: friday
time: '12:00'
timezone: Asia/Singapore
open-pull-requests-limit: 10
interval: weekly
day: friday
time: '12:00'
timezone: Asia/Singapore
open-pull-requests-limit: 10
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/dependabot.yml around lines 17 - 21, The Dependabot config currently
sets the update interval to monthly which lengthens vulnerability exposure;
locate the npm package-ecosystem block (look for package-ecosystem: "npm" and
the interval field) and change interval: monthly to interval: weekly so npm
dependency updates run weekly; keep existing day/time/timezone and
open-pull-requests-limit values unchanged unless you want different cadence.

Comment on lines +15 to +17
permissions:
security-events: write

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

GitHub 官方文档中,使用 actions/checkout@v4 与 github/codeql-action@v3 时,job 级 permissions 的最小必需项是什么?如果只配置 security-events: write,checkout 是否会因权限不足失败?

💡 Result:

使用 actions/checkout@v4 和 github/codeql-action@v3 的 GitHub Actions job 级 permissions 的最小必需项是: permissions: contents: read actions: read security-events: write 如果只配置 security-events: write,checkout 会因缺少 contents: read 权限而失败,因为 checkout 需要读取仓库内容来检出代码。

Citations:


🏁 Script executed:

# 查看 CodeQL 工作流文件内容
if [ -f ".github/workflows/codeql.yml" ]; then
  echo "=== File exists, showing content with line numbers ==="
  cat -n ".github/workflows/codeql.yml"
else
  echo "File not found"
  find . -name "codeql.yml" -type f
fi

Repository: consistent-k/VodHub

Length of output: 927


补充 contents: readactions: read 权限,避免 CodeQL 作业因令牌权限不足失败。

Line 15-16 当前仅配置 security-events: write。根据官方文档,actions/checkout@v4 需要 contents: read 权限来读取仓库内容,codeql-action 需要 actions: read 权限。缺少这些权限会导致检出步骤和后续分析失败。

建议修复
         permissions:
+            actions: read
+            contents: read
             security-events: write
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
permissions:
security-events: write
permissions:
actions: read
contents: read
security-events: write
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/codeql.yml around lines 15 - 17, The workflow permissions
block currently only sets security-events: write; update the permissions section
(the permissions mapping that contains security-events: write) to also include
contents: read and actions: read so checkout and CodeQL actions have sufficient
token scopes; keep YAML indentation consistent with the existing permissions
mapping and ensure the added keys are at the same level as security-events.

Comment on lines +109 to +112
### Exports
- **Named exports only** (no default exports, except custom error classes)
- Each route file exports a `route` object conforming to the `Route` type
- Each namespace file exports a `namespace` object conforming to `Namespace`
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

导出规则与实际代码不完全一致

文档声明"仅使用命名导出(无默认导出)",但 src/routes/registry.tssrc/routes/proxy.ts 等文件使用了默认导出(export default app)。建议更新文档以反映实际情况。

📝 建议修改
 ### Exports
-- **Named exports only** (no default exports, except custom error classes)
+- **Prefer named exports** (default exports allowed for route modules and error classes)
 - Each route file exports a `route` object conforming to the `Route` type
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
### Exports
- **Named exports only** (no default exports, except custom error classes)
- Each route file exports a `route` object conforming to the `Route` type
- Each namespace file exports a `namespace` object conforming to `Namespace`
### Exports
- **Prefer named exports** (default exports allowed for route modules and error classes)
- Each route file exports a `route` object conforming to the `Route` type
- Each namespace file exports a `namespace` object conforming to `Namespace`
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@AGENTS.md` around lines 109 - 112, The AGENTS.md "Named exports only" rule is
inconsistent with the codebase, which uses default exports (e.g., export default
app) in route files like registry.ts and proxy.ts; update AGENTS.md to reflect
the actual convention — either allow default exports for route files (and
document the expected default export shape, e.g., the exported app/route object)
or change the documentation to state the explicit exception for route files —
ensure the doc text and examples match the code (mention the export form "export
default app" and the Route/Namespace expectations).

},
cache: {
ttl: process.env.CACHE_TTL ? parseInt(process.env.CACHE_TTL, 10) : 1000 * 60, // 默认缓存时间为60秒
ttl: process.env.CACHE_TTL ? parseInt(process.env.CACHE_TTL, 10) * 1000 : 1000 * 60,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

CACHE_TTL 缺少健壮性校验,非法值会导致 TTL 异常。

Line [15] 直接 parseInt(...)*1000,当环境变量为非法字符串、0 或负数时,会得到 NaN/无效 TTL,缓存行为会变得不可预期。

💡 建议修复
 import { BANNED_KEYWORDS } from '@/constant/word';
 import 'dotenv/config';

+const parsedCacheTtlSec = Number(process.env.CACHE_TTL);
+const cacheTtlMs = Number.isFinite(parsedCacheTtlSec) && parsedCacheTtlSec > 0 ? parsedCacheTtlSec * 1000 : 1000 * 60;
+
 export const config = {
@@
     cache: {
-        ttl: process.env.CACHE_TTL ? parseInt(process.env.CACHE_TTL, 10) * 1000 : 1000 * 60,
+        ttl: cacheTtlMs,
         redis: process.env.REDIS_URL
     },
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/config/index.ts` at line 15, The ttl assignment using
process.env.CACHE_TTL currently parses blindly and can produce NaN/zero/negative
values; update the logic that sets ttl in src/config/index.ts to validate
CACHE_TTL by parsing with parseInt(base 10), checking that the result is a
finite positive integer (>0), and only then multiplying by 1000; otherwise fall
back to a safe default (e.g., 60_000). Ensure you update the ttl variable (and
any related export) so invalid, zero or negative env values do not become the
effective TTL.

Comment on lines +51 to +52
type_name: item.cat ? typeNameMap[item.cat] : ''
}));
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

type_name 建议加兜底,避免返回 undefined

cat 不在映射表中时,当前表达式可能产生 undefined,建议显式回退为空字符串。

建议修复
-                type_name: item.cat ? typeNameMap[item.cat] : ''
+                type_name: typeNameMap[String(item.cat)] ?? ''
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
type_name: item.cat ? typeNameMap[item.cat] : ''
}));
type_name: typeNameMap[String(item.cat)] ?? ''
}));
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/routes/360kan/homeVod.ts` around lines 51 - 52, The property assignment
for type_name can return undefined when item.cat exists but isn't a key in
typeNameMap; update the expression that sets type_name (where typeNameMap is
referenced) to explicitly fall back to an empty string if the lookup is missing
(e.g., check whether typeNameMap[item.cat] is present or use a
nullish-coalescing-like check) so type_name is always a string rather than
undefined.

Comment on lines +12 to +22
function isAllowedTarget(target: string): boolean {
if (ALLOWED_PROXY_TARGETS.length === 0) {
return false;
}
try {
const url = new URL(target);
return ALLOWED_PROXY_TARGETS.some((allowed) => url.hostname.endsWith(allowed));
} catch {
return false;
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# 检查是否有其他地方设置或引用 ALLOWED_PROXY_TARGETS
rg -n "ALLOWED_PROXY_TARGETS" --type-add 'config:*.{env,yaml,yml,json}' -t config -t ts

Repository: consistent-k/VodHub

Length of output: 364


🏁 Script executed:

# 查找README、文档和测试文件
find . -type f \( -name "README*" -o -name "*.md" -o -name "*.test.ts" -o -name "*.spec.ts" -o -name ".env*" \) | head -20

Repository: consistent-k/VodHub

Length of output: 1457


🏁 Script executed:

# 查找代理相关的测试文件
fd -e test.ts -e spec.ts | xargs rg -l "proxy|isAllowedTarget" 2>/dev/null

Repository: consistent-k/VodHub

Length of output: 109


🏁 Script executed:

# 查看proxy.ts的完整内容以理解上下文和使用场景
wc -l src/routes/proxy.ts

Repository: consistent-k/VodHub

Length of output: 85


🏁 Script executed:

# 读取主README文件查看是否有ALLOWED_PROXY_TARGETS配置说明
cat README.md | head -100

Repository: consistent-k/VodHub

Length of output: 2195


🏁 Script executed:

# 读取完整的proxy.ts文件
cat -n src/routes/proxy.ts

Repository: consistent-k/VodHub

Length of output: 2870


修复主机名匹配安全问题及类型断言错误

当前代码在第 18 行使用 endsWith 匹配主机名存在安全风险:配置 example.com 会意外匹配 malicious-example.com。应改为精确匹配或仅允许认可的子域名。

另外,第 55 行的类型断言 response.status as 400 | 500 不准确。响应状态码可能是任何 4xx/5xx 值(如 401、403、404、502、503 等),不仅限于 400 或 500。

建议修复
 function isAllowedTarget(target: string): boolean {
     if (ALLOWED_PROXY_TARGETS.length === 0) {
         return false;
     }
     try {
         const url = new URL(target);
-        return ALLOWED_PROXY_TARGETS.some((allowed) => url.hostname.endsWith(allowed));
+        return ALLOWED_PROXY_TARGETS.some((allowed) => 
+            url.hostname === allowed || url.hostname.endsWith('.' + allowed)
+        );
     } catch {
         return false;
     }
 }
-            return ctx.json({ error: 'Failed to proxy request' }, response.status as 400 | 500);
+            return ctx.json({ error: 'Failed to proxy request' }, response.status);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function isAllowedTarget(target: string): boolean {
if (ALLOWED_PROXY_TARGETS.length === 0) {
return false;
}
try {
const url = new URL(target);
return ALLOWED_PROXY_TARGETS.some((allowed) => url.hostname.endsWith(allowed));
} catch {
return false;
}
}
function isAllowedTarget(target: string): boolean {
if (ALLOWED_PROXY_TARGETS.length === 0) {
return false;
}
try {
const url = new URL(target);
return ALLOWED_PROXY_TARGETS.some((allowed) =>
url.hostname === allowed || url.hostname.endsWith('.' + allowed)
);
} catch {
return false;
}
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/routes/proxy.ts` around lines 12 - 22, The isAllowedTarget function uses
hostname.endsWith(allowed) which allows malicious-example.com to match
example.com; change the logic in isAllowedTarget to allow only exact matches or
legitimate subdomains by checking url.hostname === allowed ||
url.hostname.endsWith('.' + allowed) for each allowed entry, and keep the
try/catch behavior; also remove the incorrect type assertion of response.status
as 400 | 500 and instead treat response.status as a number and check its range
(e.g., if (response.status >= 400 && response.status < 600) or test
Math.floor(response.status/100) === 4 || 5) wherever the code currently casts
response.status, so all 4xx/5xx codes are handled correctly.

Comment on lines 54 to 56
if (!response.ok) {
return ctx.json({ error: 'Failed to proxy request' }, response.status);
return ctx.json({ error: 'Failed to proxy request' }, response.status as 400 | 500);
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

类型断言过于狭窄

response.status as 400 | 500 的断言不正确。当 response.okfalse 时,状态码可能是任何 4xx 或 5xx 值(如 401、403、404、502 等)。

🔧 建议修复
         if (!response.ok) {
-            return ctx.json({ error: 'Failed to proxy request' }, response.status as 400 | 500);
+            return ctx.json({ error: 'Failed to proxy request' }, response.status as Parameters<typeof ctx.json>[1]);
         }

或者移除类型断言,让 TypeScript 推断类型。

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (!response.ok) {
return ctx.json({ error: 'Failed to proxy request' }, response.status);
return ctx.json({ error: 'Failed to proxy request' }, response.status as 400 | 500);
}
if (!response.ok) {
return ctx.json({ error: 'Failed to proxy request' }, response.status as Parameters<typeof ctx.json>[1]);
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/routes/proxy.ts` around lines 54 - 56, The current check returns ctx.json
with a too-narrow cast `response.status as 400 | 500`; remove that assertion and
pass the actual numeric status to ctx.json (or let TypeScript infer it) so any
4xx/5xx like 401/403/404/502 are preserved; locate the failing branch using
response.ok, response.status and the ctx.json call in this route handler and
replace the cast with the real response.status (or a safe fallback number) so
the correct HTTP status is returned.

Comment on lines +73 to 76
const mod = await import(`./routes/${namespace}/${route.location}`);
const imported = mod.default ?? mod;
route.handler = Array.isArray(imported) ? imported.find((r: Route) => r.name === route.name)?.handler : imported.handler;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

潜在的运行时错误:未找到匹配路由时的处理

imported 是数组时,find() 可能返回 undefined,此时访问 ?.handler 会得到 undefined,后续第 77 行调用 route.handler(ctx) 将抛出 TypeError

🐛 建议添加防御性检查
                     const mod = await import(`./routes/${namespace}/${route.location}`);
                     const imported = mod.default ?? mod;
-                    route.handler = Array.isArray(imported) ? imported.find((r: Route) => r.name === route.name)?.handler : imported.handler;
+                    if (Array.isArray(imported)) {
+                        const matchedRoute = imported.find((r: Route) => r.name === route.name);
+                        if (!matchedRoute?.handler) {
+                            throw new Error(`Handler not found for route: ${route.name}`);
+                        }
+                        route.handler = matchedRoute.handler;
+                    } else {
+                        route.handler = imported.handler;
+                    }
                 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const mod = await import(`./routes/${namespace}/${route.location}`);
const imported = mod.default ?? mod;
route.handler = Array.isArray(imported) ? imported.find((r: Route) => r.name === route.name)?.handler : imported.handler;
}
const mod = await import(`./routes/${namespace}/${route.location}`);
const imported = mod.default ?? mod;
if (Array.isArray(imported)) {
const matchedRoute = imported.find((r: Route) => r.name === route.name);
if (!matchedRoute?.handler) {
throw new Error(`Handler not found for route: ${route.name}`);
}
route.handler = matchedRoute.handler;
} else {
route.handler = imported.handler;
}
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/routes/registry.ts` around lines 73 - 76, When dynamically importing
route modules in the block that assigns route.handler (variables mod, imported,
route), add a defensive check for the case where Array.isArray(imported) and
imported.find(...) returns undefined: if no matching route is found, do not
assign undefined to route.handler; instead set route.handler to a safe fallback
(e.g., a handler that returns a 404/throws a descriptive error) or throw/log a
descriptive error immediately so the later call to route.handler(ctx) cannot
cause a TypeError. Ensure this logic is applied where mod, imported and
route.handler are handled so any missing route entry is detected and handled
before invoking route.handler.

Comment on lines +11 to +13
export function createCMSRoutes(namespace: Namespace): Route[] {
const { name } = namespace;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

应在工厂层强制保证 namespace.url,避免整组 CMS 路由运行时失效。

当前 Namespace.url 是可选字段,但所有下游 CMS handler 都依赖它拼接请求地址;当某个命名空间漏配 url 时,会在运行时统一失败(如 undefined/api.php/...)。建议在工厂入口一次性做类型与运行时兜底。

💡 建议修复
 import type { Context } from 'hono';

 import type { Namespace, Route } from '@/types';
@@
-export function createCMSRoutes(namespace: Namespace): Route[] {
-    const { name } = namespace;
+type CMSNamespace = Namespace & { url: string };
+
+export function createCMSRoutes(namespace: CMSNamespace): Route[] {
+    const { name, url } = namespace;
+    if (!url) {
+        throw new Error(`Namespace "${name}" 缺少 url 配置`);
+    }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/utils/cms/factory.ts` around lines 11 - 13, In createCMSRoutes, enforce
and validate namespace.url at the factory entry (right after extracting name) so
downstream CMS handlers never receive undefined; check that namespace.url is a
non-empty string (optionally normalize/trim) and either throw a clear Error
(e.g. "Missing or invalid namespace.url for namespace: <name>") or set a safe
default only if intended; assign the validated value to a local variable (e.g.
baseUrl) and use that for building all Route URLs so all handlers rely on a
guaranteed URL.

@consistent-k consistent-k merged commit 9d90835 into main Mar 21, 2026
5 checks passed
@consistent-k consistent-k deleted the dev branch March 26, 2026 16:48
@consistent-k consistent-k restored the dev branch March 26, 2026 16:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants