feat(upload): extract upload subsystem + move sweep to scanner ticker (B16)

- internal/upload owns chunked-upload domain logic (fingerprint resume,
  part tmp+rename, assemble, session sweep) and the shared UniquePath
  helper used by both single-file and chunked completion paths
- handlers/uploads.go is now HTTP-only: bind params, call upload.U, map
  sentinel errors to the unchanged status/code/message contract
- B16: session sweep moved out of the UploadInit request path onto the
  scanner ticker via a Sweeper hook (upload.U satisfies it)
- unit tests for the package without PG/Redis; contract pinned by the
  existing handler integration tests (all green)
This commit is contained in:
2026-09-14 22:00:25 +08:00
parent 443f4acfa9
commit fbd5cd243d
10 changed files with 708 additions and 263 deletions
+3 -2
View File
@@ -10,11 +10,12 @@ import (
"booklib/internal/redispkg"
"booklib/internal/scanner"
"booklib/internal/store"
"booklib/internal/upload"
)
func NewRouter(cfg *config.Config, st *store.Store, rdb *redispkg.R, sc *scanner.Scanner) *gin.Engine {
func NewRouter(cfg *config.Config, st *store.Store, rdb *redispkg.R, sc *scanner.Scanner, up *upload.U) *gin.Engine {
gin.SetMode(gin.ReleaseMode)
h := handlers.New(cfg, st, rdb, sc)
h := handlers.New(cfg, st, rdb, sc, up)
r := gin.New()
if e := r.SetTrustedProxies(cfg.TrustedProxies); e != nil {
panic(e)
+1 -1
View File
@@ -16,7 +16,7 @@ func testCfg() *config.Config {
}
func TestHealthz(t *testing.T) {
r := NewRouter(testCfg(), nil, redispkg.New(""), nil)
r := NewRouter(testCfg(), nil, redispkg.New(""), nil, nil)
req := httptest.NewRequest(http.MethodGet, "/api/healthz", nil)
w := httptest.NewRecorder()
r.ServeHTTP(w, req)