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(
userRepo, sessionRepo, credentialRepo,
jwtSecret,
cfg.JWT.AccessDuration(),
cfg.JWT.RefreshDuration(),
cfg.JWT.AccessTTL,
cfg.JWT.RefreshTTL,
)
return &WebApp{

View File

@@ -49,18 +49,8 @@ type LocalStorageConfig struct {
type JWTConfig struct {
Secret string `mapstructure:"secret"`
AccessTTL string `mapstructure:"access_ttl"`
RefreshTTL string `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
AccessTTL time.Duration `mapstructure:"access_ttl"`
RefreshTTL time.Duration `mapstructure:"refresh_ttl"`
}
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"))
}
if _, err := time.ParseDuration(c.JWT.AccessTTL); err != nil {
errs = append(errs, fmt.Errorf("jwt.access_ttl: %w", err))
if c.JWT.AccessTTL <= 0 {
errs = append(errs, errors.New("jwt.access_ttl: must be positive"))
}
if _, err := time.ParseDuration(c.JWT.RefreshTTL); err != nil {
errs = append(errs, fmt.Errorf("jwt.refresh_ttl: %w", err))
if c.JWT.RefreshTTL <= 0 {
errs = append(errs, errors.New("jwt.refresh_ttl: must be positive"))
}
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"},
{"storage.driver", cfg.Storage.Driver, "local"},
{"storage.local.path", cfg.Storage.Local.Path, "data/files"},
{"jwt.access_ttl", cfg.JWT.AccessTTL, "15m"},
{"jwt.refresh_ttl", cfg.JWT.RefreshTTL, "168h"},
{"jwt.access_ttl", cfg.JWT.AccessTTL, 15 * time.Minute},
{"jwt.refresh_ttl", cfg.JWT.RefreshTTL, 168 * time.Hour},
}
for _, tt := range tests {
@@ -87,11 +87,11 @@ jwt:
if cfg.JWT.Secret != "test-secret" {
t.Errorf("jwt.secret = %q, want %q", cfg.JWT.Secret, "test-secret")
}
if cfg.JWT.AccessTTL != "30m" {
t.Errorf("jwt.access_ttl = %q, want %q", cfg.JWT.AccessTTL, "30m")
if cfg.JWT.AccessTTL != 30*time.Minute {
t.Errorf("jwt.access_ttl = %v, want %v", cfg.JWT.AccessTTL, 30*time.Minute)
}
if cfg.JWT.RefreshTTL != "72h" {
t.Errorf("jwt.refresh_ttl = %q, want %q", cfg.JWT.RefreshTTL, "72h")
if cfg.JWT.RefreshTTL != 72*time.Hour {
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) {
j := JWTConfig{AccessTTL: "15m"}
if got := j.AccessDuration(); got != 15*time.Minute {
t.Errorf("AccessDuration() = %v, want %v", got, 15*time.Minute)
j := JWTConfig{AccessTTL: 15 * time.Minute}
if j.AccessTTL != 15*time.Minute {
t.Errorf("AccessTTL = %v, want %v", j.AccessTTL, 15*time.Minute)
}
}
func TestJWTConfigRefreshDuration(t *testing.T) {
j := JWTConfig{RefreshTTL: "168h"}
if got := j.RefreshDuration(); got != 168*time.Hour {
t.Errorf("RefreshDuration() = %v, want %v", got, 168*time.Hour)
j := JWTConfig{RefreshTTL: 168 * time.Hour}
if j.RefreshTTL != 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{
JWT: config.JWTConfig{
Secret: "test-secret",
AccessTTL: "15m",
RefreshTTL: "168h",
AccessTTL: 15 * time.Minute,
RefreshTTL: 168 * time.Hour,
},
}
authService := service.NewAuthService(nil, nil, nil, nil, 15*time.Minute, 7*24*time.Hour)