feat(backend): main wiring, graceful shutdown, local smoke green

This commit is contained in:
2026-09-06 19:52:42 +08:00
parent 8f18072843
commit 04095b5438
2 changed files with 99 additions and 0 deletions
+62
View File
@@ -0,0 +1,62 @@
package main
import (
"context"
"errors"
"log"
"net/http"
"os/signal"
"syscall"
"time"
"booklib/internal/api"
"booklib/internal/config"
"booklib/internal/db"
"booklib/internal/redispkg"
"booklib/internal/scanner"
"booklib/internal/seed"
"booklib/internal/store"
)
func main() {
cfg, err := config.Load()
if err != nil {
log.Fatalf("config: %v", err)
}
ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
defer stop()
p, err := db.Connect(ctx, cfg.DatabaseURL)
if err != nil {
log.Fatalf("db connect: %v", err)
}
defer p.Close()
if err := db.Migrate(ctx, p); err != nil {
log.Fatalf("migrate: %v", err)
}
st := store.New(p)
if err := seed.Admin(ctx, st, cfg.AdminUser, cfg.AdminPassword); err != nil {
log.Fatalf("seed: %v", err)
}
rdb := redispkg.New(cfg.RedisURL)
sc := scanner.New(st, cfg, rdb)
go sc.Run(ctx)
srv := &http.Server{Addr: cfg.Addr, Handler: api.NewRouter(cfg, st, rdb, sc),
ReadHeaderTimeout: 10 * time.Second}
go func() {
log.Printf("listening on %s", cfg.Addr)
if err := srv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
log.Fatalf("serve: %v", err)
}
}()
<-ctx.Done()
// stop() 先取消 ctx → scanner 循环退出;再等 HTTP 收尾。
// 在途 ScanLibraryByID(WithoutCancel)不受 ctx 控制,靠 redis 锁 TTL 兜底(已文档化的上限)。
stop()
shutdownCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
if err := srv.Shutdown(shutdownCtx); err != nil {
log.Printf("shutdown: %v", err)
}
}