优化
This commit is contained in:
@@ -81,10 +81,13 @@ func NewLogViewerUsecase(repo LogFileRepo) *LogViewerUsecase {
|
||||
}
|
||||
|
||||
type ErrorRecord struct {
|
||||
ID uint
|
||||
CreatedAt, UpdatedAt time.Time
|
||||
Form, Info, Level, RequestID, TraceID, Solution, Status string
|
||||
CreatedAtRange []time.Time
|
||||
ID uint
|
||||
CreatedAt, UpdatedAt time.Time
|
||||
Form, Info, Solution *string
|
||||
Level string
|
||||
RequestID, TraceID string
|
||||
Status string
|
||||
CreatedAtRange []time.Time
|
||||
}
|
||||
|
||||
type AuditRecordRepo interface {
|
||||
|
||||
@@ -173,17 +173,13 @@ func (uc *MediaUsecase) CleanupStale(ctx context.Context, ttlHours int) error {
|
||||
if ttlHours <= 0 {
|
||||
ttlHours = 24
|
||||
}
|
||||
ids, err := uc.StaleUploadSessionIDs(ctx, time.Now().Add(-time.Duration(ttlHours)*time.Hour))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// The reference cleanup job is best-effort: a stale-session query or an
|
||||
// individual storage/database cleanup failure is not propagated to the
|
||||
// scheduler. Keep the endpoint-independent background behavior compatible.
|
||||
ids, _ := uc.StaleUploadSessionIDs(ctx, time.Now().Add(-time.Duration(ttlHours)*time.Hour))
|
||||
for _, id := range ids {
|
||||
if err = uc.DeleteUploadData(ctx, id); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = uc.files.DeletePrefix(ctx, uc.chunkPrefix(id)); err != nil {
|
||||
return err
|
||||
}
|
||||
_ = uc.DeleteUploadData(ctx, id)
|
||||
_ = uc.files.DeletePrefix(ctx, uc.chunkPrefix(id))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -40,6 +40,7 @@ type SecurityConfig struct {
|
||||
type SecurityRepo interface {
|
||||
SecurityConfig(context.Context) (*SecurityConfig, error)
|
||||
SaveSecurityConfig(context.Context, *SecurityConfig) error
|
||||
BackfillPasswordUpdatedAt(context.Context, time.Time) error
|
||||
}
|
||||
|
||||
type SecurityUsecase struct {
|
||||
@@ -63,6 +64,13 @@ func NewSecurityUsecase(repo SecurityRepo, cache Cache, settings RuntimeSettings
|
||||
}
|
||||
|
||||
func (uc *SecurityUsecase) UpdateSecurity(ctx context.Context, value *SecurityConfig) error {
|
||||
previous, err := uc.repo.SecurityConfig(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
value.ID = previous.ID
|
||||
value.CreatedAt = previous.CreatedAt
|
||||
value.UpdatedAt = previous.UpdatedAt
|
||||
if err := uc.repo.SaveSecurityConfig(ctx, value); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -70,6 +78,14 @@ func (uc *SecurityUsecase) UpdateSecurity(ctx context.Context, value *SecurityCo
|
||||
copy := *value
|
||||
uc.cachedConfig = ©
|
||||
uc.mu.Unlock()
|
||||
// Keep the same observable ordering as the administration backend:
|
||||
// persist and activate the new configuration first, then backfill legacy
|
||||
// users when password expiration changes from disabled to enabled. A
|
||||
// backfill failure is returned to the caller without rolling back the
|
||||
// already effective configuration.
|
||||
if value.PwdExpireEnable && !previous.PwdExpireEnable {
|
||||
return uc.repo.BackfillPasswordUpdatedAt(ctx, time.Now())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -83,6 +99,13 @@ func (uc *SecurityUsecase) Current(ctx context.Context) (*SecurityConfig, error)
|
||||
uc.mu.RUnlock()
|
||||
value, err := uc.repo.SecurityConfig(ctx)
|
||||
if err != nil {
|
||||
// GVA returns its default value together with the database-not-ready
|
||||
// error. Callers such as login/runtime policy consumers intentionally
|
||||
// ignore the error and continue with that default, while the HTTP
|
||||
// settings endpoint still reports the failure.
|
||||
if value != nil {
|
||||
return value, err
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
uc.mu.Lock()
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
package biz
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
type securityUpdateRepo struct {
|
||||
current *SecurityConfig
|
||||
backfillErr error
|
||||
callOrder []string
|
||||
backfillAt time.Time
|
||||
persistedCopy *SecurityConfig
|
||||
}
|
||||
|
||||
func (r *securityUpdateRepo) SecurityConfig(context.Context) (*SecurityConfig, error) {
|
||||
r.callOrder = append(r.callOrder, "get")
|
||||
copy := *r.current
|
||||
return ©, nil
|
||||
}
|
||||
|
||||
func (r *securityUpdateRepo) SaveSecurityConfig(_ context.Context, value *SecurityConfig) error {
|
||||
r.callOrder = append(r.callOrder, "save")
|
||||
value.UpdatedAt = value.UpdatedAt.Add(time.Second)
|
||||
copy := *value
|
||||
r.persistedCopy = ©
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *securityUpdateRepo) BackfillPasswordUpdatedAt(_ context.Context, at time.Time) error {
|
||||
r.callOrder = append(r.callOrder, "backfill")
|
||||
r.backfillAt = at
|
||||
return r.backfillErr
|
||||
}
|
||||
|
||||
func TestUpdateSecurityKeepsSavedConfigWhenPasswordBackfillFails(t *testing.T) {
|
||||
createdAt := time.Date(2026, time.August, 16, 12, 0, 0, 0, time.Local)
|
||||
backfillErr := errors.New("backfill failed")
|
||||
repo := &securityUpdateRepo{
|
||||
current: &SecurityConfig{ID: 1, CreatedAt: createdAt, UpdatedAt: createdAt, PwdExpireEnable: false},
|
||||
backfillErr: backfillErr,
|
||||
}
|
||||
uc := NewSecurityUsecase(repo, nil, nil, nil)
|
||||
next := &SecurityConfig{ID: 99, PwdExpireEnable: true, PwdExpireDays: 30}
|
||||
|
||||
err := uc.UpdateSecurity(context.Background(), next)
|
||||
if !errors.Is(err, backfillErr) {
|
||||
t.Fatalf("UpdateSecurity() error = %v, want %v", err, backfillErr)
|
||||
}
|
||||
if got := repo.callOrder; len(got) != 3 || got[0] != "get" || got[1] != "save" || got[2] != "backfill" {
|
||||
t.Fatalf("call order = %v, want [get save backfill]", got)
|
||||
}
|
||||
if repo.persistedCopy == nil || repo.persistedCopy.ID != 1 || !repo.persistedCopy.PwdExpireEnable {
|
||||
t.Fatalf("persisted config = %+v", repo.persistedCopy)
|
||||
}
|
||||
if repo.backfillAt.IsZero() {
|
||||
t.Fatal("password timestamp backfill was not attempted")
|
||||
}
|
||||
cached, currentErr := uc.Current(context.Background())
|
||||
if currentErr != nil {
|
||||
t.Fatalf("Current() error = %v", currentErr)
|
||||
}
|
||||
if !cached.PwdExpireEnable || cached.ID != 1 {
|
||||
t.Fatalf("effective config = %+v, want newly saved config", cached)
|
||||
}
|
||||
if got := repo.callOrder; len(got) != 3 {
|
||||
t.Fatalf("Current() unexpectedly reloaded repository: %v", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateSecurityDoesNotBackfillWithoutDisabledToEnabledTransition(t *testing.T) {
|
||||
repo := &securityUpdateRepo{current: &SecurityConfig{ID: 1, PwdExpireEnable: true}}
|
||||
uc := NewSecurityUsecase(repo, nil, nil, nil)
|
||||
|
||||
if err := uc.UpdateSecurity(context.Background(), &SecurityConfig{PwdExpireEnable: true}); err != nil {
|
||||
t.Fatalf("UpdateSecurity() error = %v", err)
|
||||
}
|
||||
if got := repo.callOrder; len(got) != 2 || got[0] != "get" || got[1] != "save" {
|
||||
t.Fatalf("call order = %v, want [get save]", got)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user