package service import ( "baron-sso-backend/internal/domain" "context" "encoding/json" "fmt" "strings" "time" ) type IdentityUpdateRequest struct { IdentityID string Traits map[string]any State string Reason string Source string } type IdentityWriteService interface { GetIdentity(ctx context.Context, identityID string) (*KratosIdentity, error) UpdateIdentity(ctx context.Context, req IdentityUpdateRequest) (*KratosIdentity, error) } type identityWriteService struct { kratos KratosAdminService redis domain.RedisRepository } func NewIdentityWriteService(kratos KratosAdminService, redis domain.RedisRepository) IdentityWriteService { return &identityWriteService{ kratos: kratos, redis: redis, } } func (s *identityWriteService) GetIdentity(ctx context.Context, identityID string) (*KratosIdentity, error) { if s == nil || s.kratos == nil { return nil, fmt.Errorf("kratos admin service is required") } return s.kratos.GetIdentity(ctx, identityID) } func (s *identityWriteService) UpdateIdentity(ctx context.Context, req IdentityUpdateRequest) (*KratosIdentity, error) { if s == nil || s.kratos == nil { return nil, fmt.Errorf("kratos admin service is required") } updated, err := s.kratos.UpdateIdentity(ctx, req.IdentityID, req.Traits, req.State) if err != nil { return nil, err } _ = s.markIdentityMirrorStale(req) return updated, nil } func (s *identityWriteService) markIdentityMirrorStale(req IdentityUpdateRequest) error { if s == nil || s.redis == nil { return nil } now := time.Now().UTC() reason := strings.TrimSpace(req.Reason) if reason == "" { reason = "identity_write" } source := strings.TrimSpace(req.Source) if source != "" { reason = source + ": " + reason } state := domain.IdentityCacheStatus{ Status: "stale", LastError: reason, UpdatedAt: &now, } raw, err := json.Marshal(state) if err != nil { return err } return s.redis.Set("identity:mirror:state", string(raw), 0) }