첫 커밋: 로컬 프로젝트 업로드
This commit is contained in:
@@ -0,0 +1,153 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"baron-sso-backend/internal/domain"
|
||||
"baron-sso-backend/internal/repository"
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type UserProjectionSyncService struct {
|
||||
kratos KratosAdminService
|
||||
repo repository.UserProjectionRepository
|
||||
}
|
||||
|
||||
type UserProjectionReconciler interface {
|
||||
Reconcile(ctx context.Context) (int, error)
|
||||
}
|
||||
|
||||
func NewUserProjectionSyncService(kratos KratosAdminService, repo repository.UserProjectionRepository) *UserProjectionSyncService {
|
||||
return &UserProjectionSyncService{
|
||||
kratos: kratos,
|
||||
repo: repo,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *UserProjectionSyncService) Reconcile(ctx context.Context) (int, error) {
|
||||
if s == nil || s.kratos == nil || s.repo == nil {
|
||||
return 0, fmt.Errorf("user projection sync dependencies are not configured")
|
||||
}
|
||||
|
||||
identities, err := s.kratos.ListIdentities(ctx)
|
||||
if err != nil {
|
||||
_ = s.repo.MarkFailed(ctx, err)
|
||||
return 0, err
|
||||
}
|
||||
|
||||
users := make([]domain.User, 0, len(identities))
|
||||
for _, identity := range identities {
|
||||
users = append(users, MapKratosIdentityToLocalUser(identity))
|
||||
}
|
||||
if err := s.repo.ReplaceAllFromKratos(ctx, users); err != nil {
|
||||
_ = s.repo.MarkFailed(ctx, err)
|
||||
return 0, err
|
||||
}
|
||||
return len(users), nil
|
||||
}
|
||||
|
||||
func MapKratosIdentityToLocalUser(identity KratosIdentity) domain.User {
|
||||
traits := identity.Traits
|
||||
now := time.Now()
|
||||
createdAt := identity.CreatedAt
|
||||
if createdAt.IsZero() {
|
||||
createdAt = now
|
||||
}
|
||||
updatedAt := identity.UpdatedAt
|
||||
if updatedAt.IsZero() {
|
||||
updatedAt = now
|
||||
}
|
||||
|
||||
role, ok := domain.NormalizeRoleAlias(kratosProjectionTraitString(traits, "role"))
|
||||
if !ok {
|
||||
role, ok = domain.NormalizeRoleAlias(kratosProjectionTraitString(traits, "grade"))
|
||||
if !ok {
|
||||
role = domain.RoleUser
|
||||
}
|
||||
}
|
||||
grade := kratosProjectionTraitString(traits, "grade")
|
||||
if _, ok := domain.NormalizeRoleAlias(grade); ok {
|
||||
grade = ""
|
||||
}
|
||||
|
||||
user := domain.User{
|
||||
ID: identity.ID,
|
||||
Email: kratosProjectionTraitString(traits, "email"),
|
||||
Name: kratosProjectionTraitString(traits, "name"),
|
||||
Phone: domain.NormalizePhoneNumber(kratosProjectionTraitString(traits, "phone_number")),
|
||||
Role: role,
|
||||
Status: normalizeProjectionStatus(identity.State),
|
||||
Department: kratosProjectionTraitString(traits, "department"),
|
||||
Grade: grade,
|
||||
Position: kratosProjectionTraitString(traits, "position"),
|
||||
JobTitle: kratosProjectionTraitString(traits, "jobTitle"),
|
||||
AffiliationType: kratosProjectionTraitString(traits, "affiliationType"),
|
||||
CreatedAt: createdAt,
|
||||
UpdatedAt: updatedAt,
|
||||
Metadata: make(domain.JSONMap),
|
||||
}
|
||||
if tenantID := kratosProjectionTraitString(traits, "tenant_id"); tenantID != "" {
|
||||
user.TenantID = &tenantID
|
||||
}
|
||||
if relyingPartyID := kratosProjectionTraitString(traits, "relying_party_id"); relyingPartyID != "" {
|
||||
user.RelyingPartyID = &relyingPartyID
|
||||
}
|
||||
|
||||
coreTraits := map[string]bool{
|
||||
"email": true, "name": true, "phone_number": true,
|
||||
"grade": true, "role": true,
|
||||
"companyCode": true, "company_code": true, "companyCodes": true,
|
||||
"tenant_id": true, "department": true,
|
||||
"position": true, "jobTitle": true, "affiliationType": true,
|
||||
"relying_party_id": true, "custom_login_ids": true, "id": true,
|
||||
}
|
||||
for key, value := range traits {
|
||||
if !coreTraits[key] {
|
||||
user.Metadata[key] = value
|
||||
}
|
||||
}
|
||||
return user
|
||||
}
|
||||
|
||||
func kratosProjectionTraitString(traits map[string]any, key string) string {
|
||||
if traits == nil {
|
||||
return ""
|
||||
}
|
||||
value, ok := traits[key]
|
||||
if !ok || value == nil {
|
||||
return ""
|
||||
}
|
||||
if str, ok := value.(string); ok {
|
||||
return str
|
||||
}
|
||||
return fmt.Sprint(value)
|
||||
}
|
||||
|
||||
func kratosProjectionTraitStringArray(traits map[string]any, key string) []string {
|
||||
if traits == nil {
|
||||
return nil
|
||||
}
|
||||
switch value := traits[key].(type) {
|
||||
case []string:
|
||||
return value
|
||||
case []any:
|
||||
items := make([]string, 0, len(value))
|
||||
for _, item := range value {
|
||||
if str, ok := item.(string); ok && strings.TrimSpace(str) != "" {
|
||||
items = append(items, str)
|
||||
}
|
||||
}
|
||||
return items
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func normalizeProjectionStatus(state string) string {
|
||||
normalized := domain.NormalizeUserStatus(state)
|
||||
if normalized == "" {
|
||||
return domain.UserStatusActive
|
||||
}
|
||||
return normalized
|
||||
}
|
||||
Reference in New Issue
Block a user