Files
gopeed/phase.txt
T

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).