feat(backend): directory scanner — diff, error state, covers, cache sweep, redis lock

This commit is contained in:
2026-09-05 00:31:32 +08:00
parent 4e84a8aeb7
commit 193d7612b5
7 changed files with 410 additions and 7 deletions
+2
View File
@@ -9,6 +9,7 @@ import (
"booklib/internal/auth"
"booklib/internal/config"
"booklib/internal/redispkg"
"booklib/internal/scanner"
"booklib/internal/store"
)
@@ -16,6 +17,7 @@ type api struct {
cfg *config.Config
st *store.Store
rdb *redispkg.R
sc *scanner.Scanner
}
func err(c *gin.Context, status int, code, msg string) {
+3 -1
View File
@@ -15,6 +15,7 @@ import (
"booklib/internal/auth"
"booklib/internal/db"
"booklib/internal/redispkg"
"booklib/internal/scanner"
"booklib/internal/store"
)
@@ -43,7 +44,8 @@ func setupAPI(t *testing.T) (*store.Store, http.Handler) {
cfg := testCfg()
cfg.BooksDir = filepath.Clean(os.TempDir())
cfg.CacheDir = t.TempDir()
r := NewRouter(cfg, st, redispkg.New(os.Getenv("REDIS_URL")))
rdb := redispkg.New(os.Getenv("REDIS_URL"))
r := NewRouter(cfg, st, rdb, scanner.New(st, cfg, rdb))
if u := os.Getenv("REDIS_URL"); u != "" { // 测试卫生: 共享 redis 上重置登录限流桶, 防跨测试累计 429
if opt, e := redis.ParseURL(u); e == nil {
rc := redis.NewClient(opt)
+8 -3
View File
@@ -1,6 +1,7 @@
package api
import (
"context"
"io"
"log"
"net/http"
@@ -82,11 +83,15 @@ func (a *api) getLibrary(c *gin.Context) (store.Library, bool) {
}
func (a *api) scanLibrary(c *gin.Context) {
if _, ok := a.getLibrary(c); !ok {
lib, ok := a.getLibrary(c)
if !ok {
return
}
// Task 9 接线: go a.sc.ScanLibraryByID(...)
err(c, http.StatusNotImplemented, "not_ready", "scanner not wired yet")
if _, ok := a.libRoot(c, lib); !ok {
return
}
go a.sc.ScanLibraryByID(context.WithoutCancel(c), lib.ID)
c.JSON(http.StatusAccepted, gin.H{"accepted": true})
}
func (a *api) upload(c *gin.Context) {
+3 -2
View File
@@ -7,12 +7,13 @@ import (
"booklib/internal/config"
"booklib/internal/redispkg"
"booklib/internal/scanner"
"booklib/internal/store"
)
func NewRouter(cfg *config.Config, st *store.Store, rdb *redispkg.R) *gin.Engine {
func NewRouter(cfg *config.Config, st *store.Store, rdb *redispkg.R, sc *scanner.Scanner) *gin.Engine {
gin.SetMode(gin.ReleaseMode)
a := &api{cfg: cfg, st: st, rdb: rdb}
a := &api{cfg: cfg, st: st, rdb: rdb, sc: sc}
r := gin.New()
r.Use(gin.Recovery())
g := r.Group("/api")
+1 -1
View File
@@ -15,7 +15,7 @@ func testCfg() *config.Config {
}
func TestHealthz(t *testing.T) {
r := NewRouter(testCfg(), nil, redispkg.New(""))
r := NewRouter(testCfg(), nil, redispkg.New(""), nil)
req := httptest.NewRequest(http.MethodGet, "/api/healthz", nil)
w := httptest.NewRecorder()
r.ServeHTTP(w, req)