This commit is contained in:
2025-11-17 15:39:44 +08:00
parent abfa51f12e
commit 1ddda89499
46 changed files with 2185 additions and 751 deletions

View File

@@ -0,0 +1,34 @@
# Specification Quality Checklist: Module Hook Refactor
**Purpose**: Validate specification completeness and quality before proceeding to planning
**Created**: 2025-11-17
**Feature**: specs/006-module-hook-refactor/spec.md
## Content Quality
- [x] No implementation details (languages, frameworks, APIs)
- [x] Focused on user value and business needs
- [x] Written for non-technical stakeholders
- [x] All mandatory sections completed
## Requirement Completeness
- [x] No [NEEDS CLARIFICATION] markers remain
- [x] Requirements are testable and unambiguous
- [x] Success criteria are measurable
- [x] Success criteria are technology-agnostic (no implementation details)
- [x] All acceptance scenarios are defined
- [x] Edge cases are identified
- [x] Scope is clearly bounded
- [x] Dependencies and assumptions identified
## Feature Readiness
- [x] All functional requirements have clear acceptance criteria
- [x] User scenarios cover primary flows
- [x] Feature meets measurable outcomes defined in Success Criteria
- [x] No implementation details leak into specification
## Notes
- Checklist complete; ready for `/speckit.plan`.

View File

@@ -0,0 +1,16 @@
# Contracts: Module Hook Refactor
## `/ - /modules` Diagnostics
- **Purpose**: 列出所有模块的 metadata 与 Hook 注册状态SRE 可检查模块是否迁移到 Hook 模式。
- **Response Additions**:
- `hook_status`: `registered | legacy-only | missing`
- `handler_status`: `ok | missing | panic`
- **Usage**: SRE 通过 `curl http://host:port/-/modules` 观察所有模块状态;缺失 Hook 或 handler 时需在日志与响应中同步体现。
## Error Responses
- `module_handler_missing`: 500 JSON `{ "error": "module_handler_missing" }`
- `module_handler_panic`: 500 JSON `{ "error": "module_handler_panic" }`
这些错误需出现在日志中并附带 `hub/domain/module_key/request_id`

View File

@@ -0,0 +1,37 @@
# Data Model: Module Hook Refactor
## Entities
- **ModuleHook**
- Attributes: `module_key`, `normalize_path`, `resolve_upstream`, `rewrite_response`, `cache_policy`, `content_type`(函数指针/接口)。
- Behavior: 由模块在 init() 或启动阶段注册proxy handler 在请求生命周期调用。
- Constraints: 所有函数可选;若未实现则 fallback 到通用逻辑;禁止造成路径逃逸或空 handler。
- **HookRegistry**
- Attributes: `map[module_key]ModuleHook`、并发安全读写锁。
- Behavior: 提供 `Register`, `MustRegister`, `Fetch`;在启动时验证唯一性。
- Constraints: module_key 小写唯一;重复注册报错。
- **LegacyHandler**
- Attributes: 使用旧行为的 handler默认缓存策略、路径重写
- Behavior: 作为默认 handlerHook 缺失时退回,并在日志/诊断中标记。
- **ProxyDispatcher**
- Attributes: handler mapmodule_key→handler默认 handler日志指针。
- Behavior: lookup handler → 调用并做错误捕获;缺失时返回 `module_handler_missing`
- **Diagnostics Snapshot**
- Attributes: 模块元数据 + Hook 状态(`registered`/`legacy`/`missing`)。
- Behavior: `/ - /modules` 接口读取 HookRegistry 与 HubRegistry生成 JSON。
## Relationships
- Hub module 注册时同时在 HookRegistry 与 Forwarder handler map 建立关联。
- ProxyDispatcher 在请求进入后根据 route.ModuleKey 查询 Hook + handler。
- Diagnostics 依赖 HookRegistry 与 HubRegistry 联合输出状态。
## Lifecycle
1. 启动:加载 `config.toml` → 初始化 HookRegistrylegacy 默认) → 模块 init() 注册 Hook。
2. 运行时:请求 → Dispatcher 查找 handler + Hook → 调用 Hook 执行特定逻辑 → 通用缓存/回源流程。
3. 诊断:`/-/modules` 读取当前 Hook 状态并输出。

View File

@@ -0,0 +1,68 @@
# Implementation Plan: Module Hook Refactor
**Branch**: `006-module-hook-refactor` | **Date**: 2025-11-17 | **Spec**: specs/006-module-hook-refactor/spec.md
**Input**: Feature specification from `/specs/006-module-hook-refactor/spec.md`
**Note**: This file captures planning up to Phase 2 (tasks generated separately).
## Summary
目标:让每个 hubmodule 完整自管缓存/代理逻辑路径重写、缓存策略、上游解析、响应重写等proxy handler 仅负责调度、缓存读写及统一日志/错误包装。技术路线:\n1. 定义 Hook/Handler 契约与注册机制。\n2. 将 Docker/NPM/PyPI/Composer/Go 的特化逻辑迁移到各自 Hook。\n3. legacy handler 仅做兜底proxy handler 移除所有 `hub_type` 分支并加强错误观测。
## Technical Context
**Language/Version**: Go 1.25+ (静态链接,单二进制交付)
**Primary Dependencies**: Fiber v3HTTP 服务、Viper配置、Logrus + Lumberjack结构化日志 & 滚动)、标准库 `net/http`/`io`
**Storage**: 本地文件系统缓存目录 `StoragePath/<Hub>/<path>`(由模块 Hook 定义布局)
**Testing**: `go test ./...`,使用 `httptest`、临时目录与模块 Hook 示例验证缓存/代理路径
**Target Platform**: Linux/Unix CLI 进程,由 systemd/supervisor 管理,匿名下游客户端
**Project Type**: 单 Go 项目(`cmd/` 入口 + `internal/*` 包)
**Performance Goals**: 缓存命中直接返回;回源路径需流式转发,单请求常驻内存 <256MB命中/回源日志可追踪
**Constraints**: 禁止 Web UI 或账号体系;所有行为受单一 TOML 控制;每个 Hub 需独立 Domain 绑定;仅匿名访问
**Scale/Scope**: 支撑 Docker/NPM/Go/PyPI/Composer多仓 Hook 自治,面向弱网及离线缓存复用场景
## Constitution Check
*GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.*
- Feature 仍然是“轻量多仓 CLI 代理”,未引入 Web UI、账号体系或与代理无关的能力。
- 仅使用 Go + 宪法指定依赖;任何新第三方库都已在本计划中说明理由与审核结论。
- 行为完全由 `config.toml` 控制,新增配置项已规划默认值、校验与迁移策略。
- 方案维持缓存优先 + 流式回源路径,并给出命中/回源/失败的日志与观测手段。
- 计划内列出了配置解析、缓存读写、Host Header 路由等强制测试与中文注释交付范围。
## Project Structure
### Documentation (this feature)
```text
specs/[###-feature]/
├── plan.md # This file (/speckit.plan command output)
├── research.md # Phase 0 output (/speckit.plan command)
├── data-model.md # Phase 1 output (/speckit.plan command)
├── quickstart.md # Phase 1 output (/speckit.plan command)
├── contracts/ # Phase 1 output (/speckit.plan command)
└── tasks.md # Phase 2 output (/speckit.tasks command - NOT created by /speckit.plan)
```
### Source Code (repository root)
```text
cmd/any-hub/main.go # CLI 入口、参数解析
internal/config/ # TOML 加载、默认值、校验
internal/server/ # Fiber 服务、路由、中间件
internal/cache/ # 磁盘/内存缓存与 .meta 管理
internal/proxy/ # 上游访问、缓存策略、流式复制
configs/ # 示例 config.toml如需
tests/ # `go test` 下的单元/集成测试,用临时目录
```
**Structure Decision**: 采用单 Go 项目结构,特性代码应放入上述现有目录;如需新增包或目录,必须解释其与 `internal/*` 的关系并给出后续维护策略。
## Complexity Tracking
> **Fill ONLY if Constitution Check has violations that must be justified**
| Violation | Why Needed | Simpler Alternative Rejected Because |
|-----------|------------|-------------------------------------|
| [e.g., 4th project] | [current need] | [why 3 projects insufficient] |
| [e.g., Repository pattern] | [specific problem] | [why direct DB access insufficient] |

View File

@@ -0,0 +1,44 @@
# Quickstart: Module Hook Refactor
1) 检出分支并安装依赖
```bash
git checkout 006-module-hook-refactor
/home/rogee/.local/go/bin/go test ./...
```
2) 定义模块 Hook
```go
func init() {
proxy.RegisterModule(proxy.ModuleRegistration{
Key: "npm",
Handler: proxyHandler,
})
hooks.MustRegister("npm", hooks.Hooks{
NormalizePath: myNormalize,
ResolveUpstream: myResolve,
RewriteResponse: myRewrite,
CachePolicy: myPolicy,
ContentType: myContentType,
})
}
```
3) 迁移逻辑
- 读取 `internal/proxy/handler.go` 里的类型分支,对应迁移到模块 Hook。
- 更新模块单元测试验证缓存、路径、响应重写等行为。
4) 验证
```bash
/home/rogee/.local/go/bin/go test ./...
```
- 针对迁移模块执行“第一次 miss → 第二次 hit”端到端测试。
- 触发缺失 handler/panic确保返回 `module_handler_missing`/`module_handler_panic`
5) 诊断检查
```bash
curl -s http://localhost:8080/-/modules | jq '.modules[].hook_status'
```
- 确认新模块标记为 `registered`,未注册模块显示 `missing`legacy handler 仍可作为兜底。
- 如果需要查看全局状态,可检查 `hook_registry` 字段,它返回每个 module_key 的注册情况。
- `hubs[].legacy_only``true` 时表示该 Hub 仍绑定 legacy 模块;迁移完成后应显式设置 `[[Hub]].Module`
- 启动阶段会验证每个模块是否注册 Hook缺失则直接退出避免运行期静默回退。

View File

@@ -0,0 +1,23 @@
# Research: Module Hook Refactor
## Decisions
- **Hook Contract Scope**: 模块 Hook 将覆盖 5 个扩展点(路径/locator 重写、上游 URL 解析、响应重写、缓存策略、内容类型推断),并提供统一注册 API。
- *Rationale*: 这些环节是当前 `handler.go` 中所有类型分支的来源;一次性覆盖可保证 proxy handler 只保留调度/缓存写入。
- *Alternatives*: 仅重写部分(如响应)会导致残余分支;完全改写为“每模块自己的 ProxyHandler”则重复缓存代码风险更高。
- **Legacy Handler Role**: 保留 legacy handler 作为默认兜底(未迁移模块或外部插件),但日志/诊断会标记为 `legacy-only`
- *Rationale*: 确保迁移期间功能可用,同时提示 SRE 识别未迁移模块。
- *Alternatives*: 强制所有模块一次迁移;风险大且不利于渐进上线。
- **Diagnostics Visibility**: `/ - /modules` 输出增加 Hook 状态(已注册/缺失/legacy 默认),并用于 SRE 排查。
- *Rationale*: 迁移阶段需要监控 Hook 注册情况,避免静默回退。
- *Alternatives*: 单靠日志搜索,排查效率低。
- **Testing Approach**: 每个模块迁移后需要端到端“Miss → Hit”回归同时新增 Hook 单元测试覆盖缺失/panic 等异常路径。
- *Rationale*: 确认行为等价且新错误处理生效。
- *Alternatives*: 仅依赖现有集成测试,不足以验证 Hook 入口。
## Clarifications
- 无须额外澄清;假设各模块团队可修改其包并维护 Hook 实现。

View File

@@ -0,0 +1,99 @@
# Feature Specification: Module Hook Refactor
**Feature Branch**: `006-module-hook-refactor`
**Created**: 2025-11-17
**Status**: Draft
**Input**: User description: "模块内部自管理缓存/代理逻辑、proxy handler 仅负责调度"
> 宪法对齐v1.0.0
> - 保持 CLI 多仓代理定位,不引入 UI 或账号体系。
> - 仅依赖 Go 1.25+ 单二进制及 Fiber/Viper/Logrus/Lumberjack/标准库,不新增无关依赖。
> - 全局 `config.toml` 控制所有行为;若新增配置项需描述字段、默认值、验证与迁移。
> - 缓存优先 + 流式回源是基础能力;必须定义命中/回源/失败时的结构化日志与观测策略。
> - 验收需覆盖配置解析、缓存读写、Host Header 绑定及中文注释要求。
## User Scenarios & Testing *(mandatory)*
### User Story 1 - 定义模块 Hook 契约 (Priority: P1)
作为平台架构师,我可以定义一套模块 Hook/Handler 契约使缓存键、路径重写、上游解析、响应重写等逻辑都由模块实现proxy handler 只负责调度与共享的缓存写入。
**Why this priority**: 没有统一 Hook就无法让模块自控逻辑后续迁移无法落地。
**Independent Test**: 提供一个示例模块实现 Hook注册后能独立覆盖缓存策略/路径重写proxy handler 不含类型分支仍可处理请求。
**Acceptance Scenarios**:
1. **Given** 定义好 Hook 接口与注册机制,**When** 模块注册自定义 Hook**Then** proxy handler 在日志/缓存流程中调用模块 Hook代码中不再出现 `hub_type` 分支。
2. **Given** 模块缺少 Hook**When** 注册时,**Then** 启动/测试阶段给出明确错误提示,防止运行期回退到旧逻辑。
---
### User Story 2 - 迁移现有模块 (Priority: P1)
作为平台工程师,我希望 Docker/NPM/PyPI/Composer/Go 等模块全部迁移到 Hook 模式,自行管理缓存与代理逻辑,保证行为与改造前一致。
**Why this priority**: 生产仓库必须可用,迁移后必须验证命中/回源与日志字段无差异。
**Independent Test**: 对每个仓库执行“首次 miss、二次 hit”流程并观察日志字段与改造前对比一致。
**Acceptance Scenarios**:
1. **Given** Docker 模块已实现 Hook**When** 请求 manifest/层文件,**Then** 路径重写、缓存键、内容类型判断都由模块完成proxy handler 不含 Docker 特化。
2. **Given** PyPI/Composer 模块已迁移,**When** 请求 simple index、packages.json、dist 文件,**Then** 响应重写与内容类型准确,日志字段/缓存命中行为与现状一致。
---
### User Story 3 - 清理 legacy 逻辑并增强观测 (Priority: P2)
作为 SRE我希望 proxy handler 只提供通用调度和错误包装;模块未注册或 Hook panic 时能输出统一 5xx 与结构化日志legacy 模块成为纯兜底实现。
**Why this priority**: 防止运行期静默回退,简化排查路径。
**Independent Test**: 刻意注入未注册模块或 Hook panic观察返回 `module_handler_missing`/`module_handler_panic` 错误及日志字段完整。
**Acceptance Scenarios**:
1. **Given** 模块未注册 Hook**When** 发起请求,**Then** proxy handler 返回 5xx JSON 并记录 hub/module_key/request_id 等字段。
2. **Given** Hook panic**When** 请求执行,**Then** panic 被捕获,返回统一错误并在日志中包含 panic 信息。
---
### Edge Cases
- 模块未注册或重复注册时需在启动阶段失败,避免运行期 fallback。
- Hook 返回非法路径/URL 时需防止逃逸缓存目录或访问非预期上游。
- 不同模块并行迁移时,需保证 legacy 模块仍可作为默认 handler。
- Hook 不得影响 diagnostics (`/-/modules`) 或健康检查路径。
- 模块 Hook 需兼容 HEAD/GET不支持方法应返回合规状态码。
## Requirements *(mandatory)*
### Functional Requirements
- **FR-001**: 定义模块 Hook/Handler 契约覆盖路径重写、缓存策略、上游解析、响应重写、内容类型推断等扩展点proxy handler 不再含 `hub_type` 分支。
- **FR-002**: 提供 Hook 注册/验证机制,要求模块在注册时同时提供元数据与 Hook缺失或重复时启动失败并输出明确日志。
- **FR-003**: 将 Docker/NPM/PyPI/Composer/Go 模块现有特化逻辑全部迁移到各自 Hook实现缓存键/TTL/验证、路径 fallback、响应重写的等价行为。
- **FR-004**: legacy/default 模块作为兜底 handler确保未迁移模块仍可运行但会记录“legacy-only”状态便于观测。
- **FR-005**: proxy handler 仅负责调度、缓存读写和日志包装;模块 Hook panic 或缺失时返回统一 5xx JSON`module_handler_panic`/`module_handler_missing`),日志包含 hub/domain/module_key/request_id。
- **FR-006**: Diagnostics (`/-/modules`) 需展示模块 Hook 注册状态(正常/缺失),并保持现有输出字段。
- **FR-007**: 文档/quickstart 更新,说明如何实现 Hook、注册模块以及如何验证新模块缓存/日志。
### Key Entities
- **Module Hook**: 一组可选函数normalize path、resolve upstream、rewrite response、cache policy、content type
- **Module Registration**: 绑定 module_key、元数据、Hook handler 的机构,负责唯一性与完整性校验。
- **Proxy Dispatcher**: 使用 module_key→Hook/handler map 调度请求,输出统一日志与错误。
### Assumptions
- 现有模块的缓存策略及接口稳定,可迁移到 Hook 而无需额外外部依赖。
- 模块团队可接受在各自包内实现 Hook无需跨团队共享逻辑。
## Success Criteria *(mandatory)*
- **SC-001**: proxy handler 代码中不包含任何 `hub_type`/类型特化分支;静态分析或代码审查确认类型判断被完全移除。
- **SC-002**: 对 docker/npm/pypi/composer/go 每个仓执行“首次 miss、二次 hit”测试首/次响应头与日志字段与改造前一致,功能回归通过。
- **SC-003**: 新增模块仅需在 hubmodule 中实现 Hook 并注册,无需修改 proxy handler示例模块演示该流程并通过集成测试。
- **SC-004**: 缺失 handler 或 Hook panic 时返回统一 5xx JSON日志包含 hub/domain/module_key/request_id错误率控制在 0测试场景
- **SC-005**: `/ - /modules` 诊断接口展示所有模块 Hook 状态SRE 可识别缺失或 legacy-only 模块;与文档描述一致。

View File

@@ -0,0 +1,100 @@
# Tasks: Module Hook Refactor
**Input**: Design documents from `/specs/006-module-hook-refactor/`
**Prerequisites**: plan.md, spec.md, research.md, data-model.md, contracts/, quickstart.md
**Tests**: 包含配置解析、缓存读写、代理命中/回源、Host Header 绑定、模块 Hook 行为(缺失/异常)的端到端与单元测试。
**Organization**: Tasks are grouped by user story so each delivers independent value.
## Phase 1: Setup
- [X] T001 阅读 spec/plan/research整理 Hook 目标与迁移范围specs/006-module-hook-refactor/
- [X] T002 运行现有基线 `GOCACHE=$(pwd)/.cache/go-build /home/rogee/.local/go/bin/go test ./...`,记录失败用例(当前因 handler 尚未实现 Hook 接口导致编译失败)
---
## Phase 2: Foundational
- [X] T003a 定义 Hook/RequestContext/CachePolicy 接口骨架internal/proxy/hooks/hooks.go
- [X] T003b [P] 实现 HookRegistry 注册/查询/重复检测,并暴露 diagnostics 所需状态internal/proxy/hooks/registry.go, internal/server/routes/modules.go
- [X] T003c [P] 添加 Hook 契约单元测试internal/proxy/hooks/hooks_test.go
- [X] T004 更新 diagnostics 接口,显示注册状态但仍未接入 handlerinternal/server/routes/modules.go
- [X] T005 建立示例模块 Hookinternal/hubmodule/template/, internal/hubmodule/template/module_test.go
- [X] T006 更新 quickstart/README 说明 Hook 用法specs/006-module-hook-refactor/quickstart.md, README.md
**Checkpoint**: Hook 契约与注册机制 ready。
---
## Phase 3: User Story 1 - 定义模块 Hook 契约 (Priority: P1) 🎯 MVP
**Goal**: proxy handler 仅调度 + 缓存读写Hook 提供各种扩展点。
**Independent Test**: 使用示例模块覆盖路径/缓存策略 → proxy handler 中无 `hub_type` 分支也可完成请求。
- [X] T007 [US1] 重构 handler接入 Hook 扩展点internal/proxy/handler.go
- [X] T008 [P] [US1] 在 forwarder 中注入 Hook/handler 错误处理internal/proxy/forwarder.go
- [X] T009 [US1] 编写 Hook 单元测试覆盖缺失/重复/panic 场景internal/proxy/hooks/, internal/proxy/forwarder_test.go
- [X] T010 [US1] 更新 diagnostics `/ - /modules` 输出 Hook 状态internal/server/routes/modules.go, docs
**Checkpoint**: Hook 契约落地并可验证。
---
## Phase 4: User Story 2 - 迁移现有模块 (Priority: P1)
**Goal**: Docker/NPM/PyPI/Composer/Go 等模块将特化逻辑迁移到 Hook行为保持等价。
**Independent Test**: 对每个仓执行“第一次 miss、第二次 hit”测试比对日志与响应头与改造前一致。
- [X] T011 [US2] 迁移 Docker Hook路径 fallback、内容类型、缓存策略等internal/hubmodule/docker/
- [X] T012 [P] [US2] 迁移 npm Hook包 metadata、tarball 缓存internal/hubmodule/npm/
- [X] T013 [P] [US2] 迁移 PyPI Hooksimple HTML/JSON 重写、files 路径internal/hubmodule/pypi/
- [X] T014 [P] [US2] 迁移 Composer Hookpackages.json/p2 重写、dist URLinternal/hubmodule/composer/
- [X] T015 [US2] 迁移 Go Hook模组路径、sumdb 重写internal/hubmodule/go/
- [X] T016 [US2] 更新 legacy/default handler 说明及行为internal/hubmodule/legacy/, docs
- [X] T017 [US2] 为每个模块增加/更新 e2e 测试覆盖 miss/hit 及日志字段tests/integration/*
**Checkpoint**: 现有仓库 Hook 化并通过回归。
---
## Phase 5: User Story 3 - 清理 legacy 逻辑并增强观测 (Priority: P2)
**Goal**: proxy handler 统一错误路径legacy 仅兜底并在诊断输出 legacy-only。
**Independent Test**: 模拟缺失 handler 或 Hook panic返回 `module_handler_missing`/`module_handler_panic`,日志含 hub/domain/module_key/request_id。
- [X] T018 [US3] 实现 handler 缺失/重复时启动失败与运行期 5xx 响应internal/proxy/forwarder.go, internal/hubmodule/registry.go
- [X] T019 [P] [US3] 添加 Hook panic 捕获与结构化日志internal/proxy/forwarder.go
- [X] T020 [US3] 扩展 diagnostics 与日志写入,使 legacy-only 模块可观测internal/server/routes/modules.go, internal/logging/fields.go
- [X] T021 [P] [US3] 更新文档/quickstart描述错误处理与 legacy-only 标记specs/006-module-hook-refactor/contracts/README.md, quickstart.md
**Checkpoint**: Hook 错误与 legacy 观测全面覆盖。
---
## Phase 6: Polish & Cross-Cutting
- [X] T022 整理 README/DEVELOPMENT 文档、样例配置,指导如何创建 HookREADME.md, configs/config.example.toml
- [X] T023 [P] 最终 `gofmt` + `GOCACHE=$(pwd)/.cache/go-build /home/rogee/.local/go/bin/go test ./...`,确保无回归
---
## Dependencies & Order
1. Phase1 Setup → Phase2 Hook 契约 → Phase3 (US1) → Phase4 (US2) → Phase5 (US3) → Phase6 polish。
2. US2 依赖 Hook 契约完成US3 依赖 US1/US2。
## Parallel Execution Examples
- T012/T013/T014/T015各模块 Hook可并行互不干扰。
- 文档更新T016/T021/T022可与测试任务并行。
- Hook 契约T003完成后可并行推进 diagnostics (T010) 与 forwarder 错误处理 (T008)。
## Implementation Strategy
- **MVP**:完成 US1T007-T010即可让 proxy handler 不再依赖类型分支。
- **迭代**:依次迁移模块 (US2) 并加强观测 (US3);每阶段运行 `go test ./...`
- **验证**每个模块迁移后执行“Miss→Hit”回归 + 特殊错误场景测试。