feat(backend): module scaffold, config, gin router with healthz
This commit is contained in:
@@ -0,0 +1,24 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"booklib/internal/config"
|
||||
)
|
||||
|
||||
type api struct {
|
||||
cfg *config.Config
|
||||
// st, rdb, sc 字段在 Task 4/9 加入
|
||||
}
|
||||
|
||||
func NewRouter(cfg *config.Config) *gin.Engine {
|
||||
gin.SetMode(gin.ReleaseMode)
|
||||
_ = &api{cfg: cfg} // Task 4/9 起此处为 a := &api{...},handler 挂在 a 上
|
||||
r := gin.New()
|
||||
r.Use(gin.Recovery())
|
||||
g := r.Group("/api")
|
||||
g.GET("/healthz", func(c *gin.Context) { c.String(http.StatusOK, "ok") })
|
||||
return r
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"booklib/internal/config"
|
||||
)
|
||||
|
||||
func testCfg() *config.Config {
|
||||
return &config.Config{Addr: ":8080", JWTSecret: []byte("s3cret"), ScanInterval: time.Minute, UploadMaxMB: 200}
|
||||
}
|
||||
|
||||
func TestHealthz(t *testing.T) {
|
||||
r := NewRouter(testCfg())
|
||||
req := httptest.NewRequest(http.MethodGet, "/api/healthz", nil)
|
||||
w := httptest.NewRecorder()
|
||||
r.ServeHTTP(w, req)
|
||||
if w.Code != http.StatusOK {
|
||||
t.Fatalf("healthz = %d, want 200", w.Code)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Addr string
|
||||
DatabaseURL string
|
||||
RedisURL string
|
||||
JWTSecret []byte
|
||||
AdminUser string
|
||||
AdminPassword string
|
||||
BooksDir string
|
||||
CacheDir string
|
||||
ScanInterval time.Duration
|
||||
UploadMaxMB int64
|
||||
}
|
||||
|
||||
func Load() (*Config, error) {
|
||||
env := func(k, def string) string {
|
||||
if v := os.Getenv(k); v != "" {
|
||||
return v
|
||||
}
|
||||
return def
|
||||
}
|
||||
scanSec, err := strconv.Atoi(env("SCAN_INTERVAL_SEC", "60"))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("SCAN_INTERVAL_SEC: %w", err)
|
||||
}
|
||||
uploadMB, err := strconv.ParseInt(env("UPLOAD_MAX_MB", "200"), 10, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("UPLOAD_MAX_MB: %w", err)
|
||||
}
|
||||
secret := os.Getenv("JWT_SECRET")
|
||||
if secret == "" {
|
||||
return nil, fmt.Errorf("JWT_SECRET required")
|
||||
}
|
||||
return &Config{
|
||||
Addr: env("ADDR", ":8080"),
|
||||
DatabaseURL: env("DATABASE_URL", ""),
|
||||
RedisURL: env("REDIS_URL", ""),
|
||||
JWTSecret: []byte(secret),
|
||||
AdminUser: env("ADMIN_USER", ""),
|
||||
AdminPassword: env("ADMIN_PASSWORD", ""),
|
||||
BooksDir: env("BOOKS_DIR", "/data/books"),
|
||||
CacheDir: env("CACHE_DIR", "/data/cache"),
|
||||
ScanInterval: time.Duration(scanSec) * time.Second,
|
||||
UploadMaxMB: uploadMB,
|
||||
}, nil
|
||||
}
|
||||
Reference in New Issue
Block a user