Change config JWT duration fields to time.Duration

- fix: The AccessTTL and RefreshTTL fields in JWTConfig now use
  time.Duration type directly instead of string with ParseDuration
  methods. The config validation now checks for positive durations
  rather than parsing strings.
This commit is contained in:
2026-04-29 17:02:49 +08:00
parent b4ab864f80
commit f4212cddf0
4 changed files with 23 additions and 33 deletions

View File

@@ -44,8 +44,8 @@ func Bootstrap(cfg *config.Config) (*WebApp, error) {
authService := service.NewAuthService( authService := service.NewAuthService(
userRepo, sessionRepo, credentialRepo, userRepo, sessionRepo, credentialRepo,
jwtSecret, jwtSecret,
cfg.JWT.AccessDuration(), cfg.JWT.AccessTTL,
cfg.JWT.RefreshDuration(), cfg.JWT.RefreshTTL,
) )
return &WebApp{ return &WebApp{

View File

@@ -48,19 +48,9 @@ type LocalStorageConfig struct {
} }
type JWTConfig struct { type JWTConfig struct {
Secret string `mapstructure:"secret"` Secret string `mapstructure:"secret"`
AccessTTL string `mapstructure:"access_ttl"` AccessTTL time.Duration `mapstructure:"access_ttl"`
RefreshTTL string `mapstructure:"refresh_ttl"` RefreshTTL time.Duration `mapstructure:"refresh_ttl"`
}
func (j JWTConfig) AccessDuration() time.Duration {
d, _ := time.ParseDuration(j.AccessTTL)
return d
}
func (j JWTConfig) RefreshDuration() time.Duration {
d, _ := time.ParseDuration(j.RefreshTTL)
return d
} }
func (c *Config) Validate() error { func (c *Config) Validate() error {
@@ -104,12 +94,12 @@ func (c *Config) Validate() error {
errs = append(errs, errors.New("jwt.secret: must not be empty")) errs = append(errs, errors.New("jwt.secret: must not be empty"))
} }
if _, err := time.ParseDuration(c.JWT.AccessTTL); err != nil { if c.JWT.AccessTTL <= 0 {
errs = append(errs, fmt.Errorf("jwt.access_ttl: %w", err)) errs = append(errs, errors.New("jwt.access_ttl: must be positive"))
} }
if _, err := time.ParseDuration(c.JWT.RefreshTTL); err != nil { if c.JWT.RefreshTTL <= 0 {
errs = append(errs, fmt.Errorf("jwt.refresh_ttl: %w", err)) errs = append(errs, errors.New("jwt.refresh_ttl: must be positive"))
} }
return errors.Join(errs...) return errors.Join(errs...)

View File

@@ -25,8 +25,8 @@ func TestDefaults(t *testing.T) {
{"database.sqlite.path", cfg.Database.SQLite.Path, "data/mygo.db"}, {"database.sqlite.path", cfg.Database.SQLite.Path, "data/mygo.db"},
{"storage.driver", cfg.Storage.Driver, "local"}, {"storage.driver", cfg.Storage.Driver, "local"},
{"storage.local.path", cfg.Storage.Local.Path, "data/files"}, {"storage.local.path", cfg.Storage.Local.Path, "data/files"},
{"jwt.access_ttl", cfg.JWT.AccessTTL, "15m"}, {"jwt.access_ttl", cfg.JWT.AccessTTL, 15 * time.Minute},
{"jwt.refresh_ttl", cfg.JWT.RefreshTTL, "168h"}, {"jwt.refresh_ttl", cfg.JWT.RefreshTTL, 168 * time.Hour},
} }
for _, tt := range tests { for _, tt := range tests {
@@ -87,11 +87,11 @@ jwt:
if cfg.JWT.Secret != "test-secret" { if cfg.JWT.Secret != "test-secret" {
t.Errorf("jwt.secret = %q, want %q", cfg.JWT.Secret, "test-secret") t.Errorf("jwt.secret = %q, want %q", cfg.JWT.Secret, "test-secret")
} }
if cfg.JWT.AccessTTL != "30m" { if cfg.JWT.AccessTTL != 30*time.Minute {
t.Errorf("jwt.access_ttl = %q, want %q", cfg.JWT.AccessTTL, "30m") t.Errorf("jwt.access_ttl = %v, want %v", cfg.JWT.AccessTTL, 30*time.Minute)
} }
if cfg.JWT.RefreshTTL != "72h" { if cfg.JWT.RefreshTTL != 72*time.Hour {
t.Errorf("jwt.refresh_ttl = %q, want %q", cfg.JWT.RefreshTTL, "72h") t.Errorf("jwt.refresh_ttl = %v, want %v", cfg.JWT.RefreshTTL, 72*time.Hour)
} }
} }
@@ -198,15 +198,15 @@ func TestExplicitConfigFileNotFound(t *testing.T) {
} }
func TestJWTConfigAccessDuration(t *testing.T) { func TestJWTConfigAccessDuration(t *testing.T) {
j := JWTConfig{AccessTTL: "15m"} j := JWTConfig{AccessTTL: 15 * time.Minute}
if got := j.AccessDuration(); got != 15*time.Minute { if j.AccessTTL != 15*time.Minute {
t.Errorf("AccessDuration() = %v, want %v", got, 15*time.Minute) t.Errorf("AccessTTL = %v, want %v", j.AccessTTL, 15*time.Minute)
} }
} }
func TestJWTConfigRefreshDuration(t *testing.T) { func TestJWTConfigRefreshDuration(t *testing.T) {
j := JWTConfig{RefreshTTL: "168h"} j := JWTConfig{RefreshTTL: 168 * time.Hour}
if got := j.RefreshDuration(); got != 168*time.Hour { if j.RefreshTTL != 168*time.Hour {
t.Errorf("RefreshDuration() = %v, want %v", got, 168*time.Hour) t.Errorf("RefreshTTL = %v, want %v", j.RefreshTTL, 168*time.Hour)
} }
} }

View File

@@ -16,8 +16,8 @@ func TestVersionRoute(t *testing.T) {
cfg := &config.Config{ cfg := &config.Config{
JWT: config.JWTConfig{ JWT: config.JWTConfig{
Secret: "test-secret", Secret: "test-secret",
AccessTTL: "15m", AccessTTL: 15 * time.Minute,
RefreshTTL: "168h", RefreshTTL: 168 * time.Hour,
}, },
} }
authService := service.NewAuthService(nil, nil, nil, nil, 15*time.Minute, 7*24*time.Hour) authService := service.NewAuthService(nil, nil, nil, nil, 15*time.Minute, 7*24*time.Hour)