mirror of
https://github.com/GopeedLab/gopeed.git
synced 2026-11-06 16:40:32 +00:00
324 lines
16 KiB
Plaintext
324 lines
16 KiB
Plaintext
GOPEED BACKEND CONTRIBUTION — MILESTONE & PHASE PLAN
|
|
======================================================
|
|
Repo: https://github.com/GopeedLab/gopeed
|
|
Scope: Backend only (Go). No Flutter/UI work.
|
|
Target environment: Windows 10/11, Go 1.25+, no cgo/MinGW required for this scope.
|
|
|
|
This document is written so an implementing agent (human or AI) can pick it up
|
|
and execute phase by phase without needing outside context. Each phase lists:
|
|
GOAL, FILES INVOLVED, STEPS, and DONE-WHEN (acceptance criteria).
|
|
|
|
Work through the milestones in order. Do not start Milestone 2 until Milestone
|
|
1 is fully working end to end.
|
|
|
|
|
|
======================================================
|
|
MILESTONE 0: ENVIRONMENT SETUP (Windows)
|
|
======================================================
|
|
Purpose: Get a working local backend you can build, run, and test, with zero
|
|
dependency on Flutter/cgo/MinGW.
|
|
|
|
--- Phase 0.1: Install prerequisites ---
|
|
GOAL: Have Go, Git, and a code editor ready.
|
|
STEPS:
|
|
1. Install Go 1.25+ from https://go.dev/dl/ (Windows MSI installer).
|
|
Verify with: go version
|
|
2. Install Git for Windows from https://git-scm.com/download/win
|
|
Verify with: git --version
|
|
3. Install VS Code (or any editor) with the Go extension for
|
|
gofmt/go vet/debugging support.
|
|
DONE-WHEN: `go version` and `git --version` both print correctly in a fresh
|
|
PowerShell window.
|
|
|
|
--- Phase 0.2: Fork and clone ---
|
|
GOAL: Have your own copy of the repo checked out locally.
|
|
STEPS:
|
|
1. On GitHub, fork GopeedLab/gopeed into your own account (see contribute.txt
|
|
for the exact fork/PR process — this phase only covers the technical clone).
|
|
2. In PowerShell:
|
|
git clone https://github.com/<your-username>/gopeed.git
|
|
cd gopeed
|
|
git remote add upstream https://github.com/GopeedLab/gopeed.git
|
|
DONE-WHEN: `git remote -v` shows both `origin` (your fork) and `upstream`
|
|
(GopeedLab/gopeed).
|
|
|
|
--- Phase 0.3: Run the backend standalone ---
|
|
GOAL: Confirm the Go backend runs independently of Flutter.
|
|
STEPS:
|
|
1. From the repo root, run:
|
|
go run cmd/api/main.go
|
|
2. The API server starts on port 9999. Note: on Windows this uses TCP
|
|
(not a unix socket, which is what's used on Linux/macOS) — this is
|
|
already handled by the existing code, nothing to configure.
|
|
3. Open http://localhost:9999 in a browser, or hit an API endpoint with
|
|
curl/Postman to confirm it responds.
|
|
DONE-WHEN: The server starts without errors and responds to a basic HTTP
|
|
request.
|
|
|
|
--- Phase 0.4: Run existing tests ---
|
|
GOAL: Confirm the test suite passes on a clean checkout before you change
|
|
anything (this is your baseline).
|
|
STEPS:
|
|
1. Run: go test ./... 2>&1 | Select-String -Pattern "FAIL|ok"
|
|
2. Note any pre-existing failures (there may be a few environment-specific
|
|
ones, e.g. tests requiring network access or specific ports free).
|
|
DONE-WHEN: You have a clear list of which tests pass/fail BEFORE your changes,
|
|
so you can tell later whether you introduced regressions.
|
|
|
|
|
|
======================================================
|
|
MILESTONE 1: HTTP DOWNLOAD CHECKSUM VERIFICATION
|
|
======================================================
|
|
Purpose: Gopeed currently has NO production checksum/integrity verification
|
|
anywhere in the download path (confirmed by grepping the source — md5/sha256
|
|
only appear in test utilities, never in internal/protocol/http/fetcher.go or
|
|
pkg/download/). This is the root cause behind reports like issue #1425
|
|
(file size matches after a flaky download, but MD5 doesn't). This milestone
|
|
adds an opt-in checksum-verification feature to HTTP downloads.
|
|
|
|
--- Phase 1.1: Explore the existing download model ---
|
|
GOAL: Understand where task options and request structs live, so you know
|
|
where to add a new field.
|
|
FILES:
|
|
- pkg/download/model.go (task/options structs)
|
|
- pkg/rest/model/task.go (REST API request/response shapes)
|
|
- pkg/base/model.go (base Request/Options types shared across protocols)
|
|
STEPS:
|
|
1. Open pkg/base/model.go and find the `Options` struct (this is what
|
|
gets passed down into protocol-specific fetchers, e.g. HTTP's Fetcher).
|
|
2. Open pkg/rest/model/task.go and find the struct used when a client
|
|
POSTs a new download task (likely something like CreateTaskReq).
|
|
3. Trace how `Options` flows from the REST layer down into
|
|
internal/protocol/http/fetcher.go's `Resolve`/`Start` functions.
|
|
DONE-WHEN: You can draw (even just in comments) the path: REST request ->
|
|
pkg/download task creation -> base.Options -> http.Fetcher.
|
|
|
|
--- Phase 1.2: Add the checksum option to the data model ---
|
|
GOAL: Add a new, optional field so existing tasks/clients are unaffected.
|
|
FILES: pkg/base/model.go, pkg/rest/model/task.go
|
|
STEPS:
|
|
1. In pkg/base/model.go, add to the Options struct (or an ExtraOptions-style
|
|
sub-struct if one exists):
|
|
Checksum *ChecksumOption `json:"checksum,omitempty"`
|
|
Define:
|
|
type ChecksumOption struct {
|
|
Algorithm string `json:"algorithm"` // "md5" | "sha1" | "sha256"
|
|
Expected string `json:"expected"` // hex-encoded expected hash
|
|
}
|
|
2. Mirror the same optional field in pkg/rest/model/task.go so it's
|
|
accepted from the REST API / MCP layer without breaking existing
|
|
callers that omit it.
|
|
DONE-WHEN: The project still compiles (`go build ./...`) with the new fields
|
|
added and unused.
|
|
|
|
--- Phase 1.3: Implement the verification step in the HTTP fetcher ---
|
|
GOAL: After a download completes (all chunks written), if a checksum option
|
|
was provided, hash the completed file and compare.
|
|
FILES: internal/protocol/http/fetcher.go
|
|
STEPS:
|
|
1. Locate where the fetcher considers the download "complete" — this is
|
|
after all `f.file.WriteAt(...)` calls across connections have finished
|
|
and chunks are reconciled (search for where the Fetcher transitions to
|
|
a "done"/completed state, near the download-loop / connection-completion
|
|
logic).
|
|
2. Add a new unexported method, e.g.:
|
|
func (f *Fetcher) verifyChecksum() error
|
|
It should:
|
|
a. Check if f's Options has a non-nil Checksum field; if nil, return
|
|
nil immediately (feature is opt-in, zero cost otherwise).
|
|
b. Open the completed file for reading (os.Open on the final file
|
|
path — check how `f.file` / final path is referenced elsewhere
|
|
in this file for the correct pattern).
|
|
c. Stream the file through the correct hash.Hash implementation based
|
|
on Algorithm (crypto/md5, crypto/sha1, or crypto/sha256), using a
|
|
buffered copy (io.Copy into the hasher) — do NOT load the whole
|
|
file into memory.
|
|
d. Compare the resulting hex digest (encoding/hex) against Expected
|
|
(case-insensitive compare).
|
|
e. Return a descriptive error on mismatch, e.g.
|
|
fmt.Errorf("checksum mismatch: expected %s, got %s", expected, actual)
|
|
3. Call verifyChecksum() at the point where the fetcher currently marks
|
|
itself complete/successful, BEFORE it reports success upward. If it
|
|
returns an error, the task must transition to a failed state with that
|
|
error message surfaced (follow the existing pattern used for other
|
|
terminal errors in this file, e.g. how isTerminalRangeError is handled).
|
|
DONE-WHEN: A download with a correct checksum finishes as "done"; a download
|
|
with a deliberately wrong checksum finishes as "failed" with a clear error
|
|
message, and the partial/complete file is NOT silently reported as good.
|
|
|
|
--- Phase 1.4: Wire it through the REST/MCP layers ---
|
|
GOAL: Make the new option actually reachable from the public API and from
|
|
the MCP `create_task` tool, not just internally.
|
|
FILES: pkg/rest/api.go, pkg/mcpserver/server.go
|
|
STEPS:
|
|
1. In pkg/rest/api.go, find the handler for task creation and confirm the
|
|
new Checksum field on the request struct is passed through unchanged
|
|
into the base.Options that gets handed to the downloader.
|
|
2. In pkg/mcpserver/server.go, find the `create_task` tool's input schema
|
|
and handler; add the optional checksum fields there too, following the
|
|
exact pattern used for existing optional fields in that tool.
|
|
DONE-WHEN: You can create a task via a raw REST POST (curl/Postman) with a
|
|
`checksum` object in the request body and see it take effect.
|
|
|
|
--- Phase 1.5: Tests ---
|
|
GOAL: Prove the feature works and doesn't regress existing behavior.
|
|
FILES: internal/protocol/http/fetcher_test.go (create if it doesn't exist;
|
|
check internal/test/util.go for existing FileMd5/DirMd5 helpers you can
|
|
reuse), plus any relevant pkg/download tests.
|
|
STEPS:
|
|
1. Write a test that downloads a small known-content file (use the
|
|
existing test HTTP server helpers in internal/test/httptest.go) with a
|
|
correct checksum option -> assert task succeeds.
|
|
2. Write a test with an intentionally wrong checksum -> assert task fails
|
|
with the expected error.
|
|
3. Write a test with no checksum option set -> assert behavior is
|
|
byte-for-byte identical to before this change (no regression).
|
|
4. Run go test ./internal/protocol/http/... and go test ./pkg/download/...
|
|
DONE-WHEN: All three test cases pass, and Phase 0.4's baseline test suite
|
|
still passes (no regressions elsewhere).
|
|
|
|
--- Phase 1.6: Documentation and PR prep ---
|
|
GOAL: Make the change reviewable and mergeable.
|
|
STEPS:
|
|
1. Add/update any relevant API documentation (check if there's an OpenAPI
|
|
spec or docs folder referenced from the REST layer).
|
|
2. Write a clear PR description referencing issue #1425 and explaining
|
|
that this is an opt-in feature (no behavior change for existing
|
|
clients/tasks that don't set a checksum).
|
|
3. Run `go vet ./...` and `gofmt -l .` to confirm formatting/lint clean.
|
|
DONE-WHEN: PR is ready to open (see contribute.txt for the actual submission
|
|
steps).
|
|
|
|
|
|
======================================================
|
|
MILESTONE 2: NEW MCP TOOL (e.g. get_download_speed / set_task_priority)
|
|
======================================================
|
|
Purpose: Extend the AI-agent-facing MCP surface with a genuinely useful new
|
|
tool, following the existing pattern exactly.
|
|
|
|
--- Phase 2.1: Study the existing tool pattern ---
|
|
GOAL: Understand how a tool is registered end to end.
|
|
FILES: pkg/mcpserver/server.go
|
|
STEPS:
|
|
1. Read the full file (525 lines) once, top to bottom.
|
|
2. Pick one simple existing tool (e.g. `pause_task` or `get_task_status`)
|
|
and trace: schema definition -> handler function -> which internal
|
|
download-manager function it calls -> how the result is shaped and
|
|
returned to the MCP caller.
|
|
DONE-WHEN: You can explain, in your own words, the 4-step shape every tool
|
|
follows in this file.
|
|
|
|
--- Phase 2.2: Pick and scope the new tool ---
|
|
GOAL: Decide exactly what the tool does and its input/output shape.
|
|
STEPS:
|
|
1. Recommended: `get_download_speed` — returns current instantaneous
|
|
download speed (and maybe ETA) for one task or all active tasks.
|
|
This likely already has underlying data available via
|
|
pkg/base/stats.go or the existing `get_task_stats` tool's data source
|
|
— check there first so you're not duplicating logic, just exposing it
|
|
differently.
|
|
2. Write down: tool name, input parameters (e.g. optional task ID),
|
|
output fields, and error cases (task not found, task not running).
|
|
DONE-WHEN: You have a one-paragraph spec written before touching code.
|
|
|
|
--- Phase 2.3: Implement ---
|
|
GOAL: Add the tool following the exact existing pattern.
|
|
FILES: pkg/mcpserver/server.go
|
|
STEPS:
|
|
1. Add the tool's schema definition alongside the others.
|
|
2. Add the handler function; reuse existing download-manager/controller
|
|
functions (pkg/download/downloader.go, internal/controller/controller.go)
|
|
rather than writing new state-access logic — this tool should be a
|
|
thin read-only wrapper.
|
|
3. Register the tool in the same place the others are registered.
|
|
DONE-WHEN: The MCP server lists the new tool and it can be invoked
|
|
successfully against a running download.
|
|
|
|
--- Phase 2.4: Tests + PR ---
|
|
STEPS:
|
|
1. Add a test in the mcpserver package covering the happy path and the
|
|
"task not found" error path.
|
|
2. Run go test ./pkg/mcpserver/...
|
|
3. Prepare PR description explaining the tool's purpose and giving an
|
|
example natural-language use case (matching the style of the README's
|
|
AI Integration table).
|
|
DONE-WHEN: Tests pass and PR is ready.
|
|
|
|
|
|
======================================================
|
|
MILESTONE 3: WEBHOOK ENHANCEMENTS
|
|
======================================================
|
|
Purpose: pkg/download/webhook.go is small (229 lines) and self-contained —
|
|
a good second/parallel track. Add a new event type or richer payload data.
|
|
|
|
--- Phase 3.1: Study current webhook events ---
|
|
FILES: pkg/download/webhook.go, pkg/rest/model/webhook.go
|
|
STEPS:
|
|
1. List all current event types fired (e.g. task completed, task error).
|
|
2. Identify a gap — e.g. no "task paused" event, or payload lacks final
|
|
file size / duration / average speed.
|
|
DONE-WHEN: You have a specific, small gap identified (do not try to redesign
|
|
the whole webhook system).
|
|
|
|
--- Phase 3.2: Implement ---
|
|
STEPS:
|
|
1. Add the new event constant/type.
|
|
2. Add the trigger call at the correct point in the download lifecycle
|
|
(pkg/download/downloader.go or wherever the corresponding state
|
|
transition already happens for existing events).
|
|
3. Extend the payload struct in pkg/rest/model/webhook.go with the new
|
|
field(s), keeping existing fields untouched for backward compatibility.
|
|
DONE-WHEN: Triggering the relevant download state change fires the new/
|
|
enhanced webhook, verifiable by pointing it at a local test HTTP listener
|
|
(e.g. `python -m http.server` or a tiny Go test server) and inspecting the
|
|
received payload.
|
|
|
|
--- Phase 3.3: Tests + PR ---
|
|
DONE-WHEN: A test covers the new event firing with the correct payload
|
|
shape, and the PR is ready.
|
|
|
|
|
|
======================================================
|
|
MILESTONE 4: CLI PARITY
|
|
======================================================
|
|
Purpose: cmd/gopeed/main.go is only ~98 lines — thin compared to the REST
|
|
API surface. Close a specific gap.
|
|
|
|
--- Phase 4.1: Gap analysis ---
|
|
FILES: cmd/gopeed/main.go, cmd/gopeed/flags.go, pkg/rest/model/task.go
|
|
STEPS:
|
|
1. List every REST endpoint/capability in pkg/rest/model/task.go.
|
|
2. List every command currently supported by the CLI.
|
|
3. Pick ONE missing capability (e.g. `gopeed pause <task-id>`,
|
|
`gopeed list --status=running`) to implement — keep scope small.
|
|
DONE-WHEN: One specific missing CLI command is chosen and written down.
|
|
|
|
--- Phase 4.2: Implement ---
|
|
STEPS:
|
|
1. Add the new flag/subcommand in cmd/gopeed/flags.go following the
|
|
existing pattern.
|
|
2. Wire it to call the corresponding REST endpoint (the CLI is a thin
|
|
HTTP client over the same API the Flutter app uses).
|
|
DONE-WHEN: Running the new CLI command against a live `go run cmd/api/main.go`
|
|
instance produces the expected effect and output.
|
|
|
|
--- Phase 4.3: Tests + PR ---
|
|
DONE-WHEN: Manual verification documented in the PR description (CLI tools
|
|
are often tested manually/via a small integration test rather than heavy
|
|
unit testing); PR ready.
|
|
|
|
|
|
======================================================
|
|
GENERAL RULES FOR EVERY MILESTONE
|
|
======================================================
|
|
1. Never break backward compatibility — new fields must be optional
|
|
(`omitempty` in JSON, nil-safe checks in Go) so existing clients/tasks
|
|
are unaffected.
|
|
2. Run `gofmt -l .` and `go vet ./...` before every commit.
|
|
3. Run the full test suite (`go test ./...`) before opening a PR; compare
|
|
against the Phase 0.4 baseline to catch regressions.
|
|
4. Keep each PR scoped to ONE milestone (or even one phase within a
|
|
milestone) — small, reviewable PRs merge faster than large ones.
|
|
5. Reference the specific GitHub issue number in the PR description when
|
|
the milestone addresses a reported bug (e.g. Milestone 1 -> #1425).
|