From 31d107ff2e7c3975bb2d73e2161773c19a3cea48 Mon Sep 17 00:00:00 2001 From: chan Date: Mon, 1 Jun 2026 15:34:08 +0900 Subject: [PATCH] feat(user): support fixed UUID registration and enhance bulk import results - Added support for fixed UUIDs during bulk registration (Search-first + ExternalID mapping) - Implemented idempotency and visibility restoration for soft-deleted users - Enhanced bulk upload UI to show 'New/Updated/Unchanged' status and modified fields - Added logic to reclaim identifiers (login_id) from colliding records - Added frontend E2E and backend unit tests for UUID integrity and conflict handling - Fixed i18n, formatting, and mock tests to satisfy code-check - Applied 'go fix' for 'omitzero' tags and general Go standards --- .../users/components/UserBulkUploadModal.tsx | 151 ++++++-- .../src/features/users/utils/csvParser.ts | 19 +- adminfront/src/lib/adminApi.ts | 3 + adminfront/tests/users_bulk_secondary.spec.ts | 2 +- adminfront/tests/users_bulk_uuid.spec.ts | 178 +++++++++ backend/cmd/server/headless_login_e2e_test.go | 4 +- backend/cmd/server/main.go | 8 +- backend/internal/bootstrap/admin_account.go | 4 +- backend/internal/bootstrap/kratos_seed.go | 4 +- backend/internal/bootstrap/sync_admin.go | 2 +- backend/internal/domain/hanmac_email.go | 8 +- backend/internal/domain/hydra_models.go | 34 +- backend/internal/domain/idp_models.go | 2 +- backend/internal/domain/user.go | 7 +- .../internal/handler/api_key_handler_test.go | 6 +- backend/internal/handler/auth_handler.go | 145 ++++---- .../handler/auth_handler_async_test.go | 5 + .../handler/auth_handler_client_test.go | 14 +- .../handler/auth_handler_consent_test.go | 70 ++-- .../auth_handler_dynamic_claims_test.go | 184 ++++----- .../handler/auth_handler_link_test.go | 68 ++-- .../handler/auth_handler_linked_test.go | 30 +- .../handler/auth_handler_login_test.go | 47 +-- .../handler/auth_handler_oidc_test.go | 16 +- .../internal/handler/auth_handler_otp_test.go | 12 +- .../auth_handler_profile_cache_test.go | 31 +- .../internal/handler/auth_handler_qr_test.go | 24 +- .../handler/auth_handler_signup_test.go | 2 +- .../internal/handler/client_tenant_access.go | 8 +- .../handler/client_tenant_access_test.go | 2 +- backend/internal/handler/common_test.go | 8 +- backend/internal/handler/dev_handler.go | 160 ++++---- .../handler/dev_handler_isolation_test.go | 16 +- .../handler/dev_handler_rp_metadata_test.go | 4 +- backend/internal/handler/dev_handler_test.go | 150 ++++---- .../internal/handler/hanmac_email_policy.go | 7 +- backend/internal/handler/tenant_handler.go | 14 +- .../internal/handler/tenant_handler_test.go | 14 +- backend/internal/handler/user_handler.go | 349 ++++++++++++++---- backend/internal/handler/user_handler_test.go | 322 ++++++++-------- .../handler/user_handler_uuid_test.go | 257 +++++++++++++ .../dev_handler_headless_secret_test.go | 9 +- backend/internal/logger/client_log_policy.go | 12 +- .../internal/logger/client_log_policy_test.go | 14 +- backend/internal/logger/logger_test.go | 1 - .../internal/middleware/audit_middleware.go | 9 +- .../repository/client_consent_repository.go | 2 +- .../repository/keto_outbox_repository.go | 4 +- .../repository/user_projection_repository.go | 10 +- .../internal/repository/user_repository.go | 37 +- backend/internal/response/error_response.go | 8 +- .../backchannel_logout_service_test.go | 16 +- backend/internal/service/developer_service.go | 6 +- .../internal/service/headless_jwks_cache.go | 9 +- .../service/headless_jwks_cache_test.go | 15 +- .../internal/service/hydra_admin_service.go | 24 +- .../service/hydra_admin_service_test.go | 4 +- backend/internal/service/keto_service.go | 8 +- backend/internal/service/keto_service_test.go | 2 +- .../internal/service/kratos_admin_service.go | 117 ++++-- backend/internal/service/mock_common_test.go | 2 +- backend/internal/service/ory_service.go | 130 ++++++- backend/internal/service/ory_service_test.go | 100 ++++- .../internal/service/relying_party_service.go | 2 +- .../service/relying_party_service_test.go | 6 +- backend/internal/service/sms_service.go | 4 +- backend/internal/service/tenant_service.go | 6 +- .../internal/service/tenant_service_test.go | 5 + .../internal/service/user_group_service.go | 8 +- .../service/user_group_service_test.go | 14 +- .../service/user_projection_sync_service.go | 6 +- .../user_projection_sync_service_test.go | 6 +- .../internal/service/worksmobile_client.go | 2 +- .../service/worksmobile_sync_service_test.go | 5 + backend/internal/utils/masking.go | 12 +- backend/internal/utils/password_policy.go | 16 +- .../internal/validator/schema_validator.go | 12 +- common/locales/en.toml | 3 + common/locales/ko.toml | 3 + common/locales/template.toml | 3 + common/pnpm-lock.yaml | 78 ++-- locales/en.toml | 13 + locales/ko.toml | 13 + locales/template.toml | 13 + page_content.txt | 93 ----- 85 files changed, 2104 insertions(+), 1149 deletions(-) create mode 100644 adminfront/tests/users_bulk_uuid.spec.ts create mode 100644 backend/internal/handler/user_handler_uuid_test.go delete mode 100644 page_content.txt diff --git a/adminfront/src/features/users/components/UserBulkUploadModal.tsx b/adminfront/src/features/users/components/UserBulkUploadModal.tsx index 1c473094..6367653b 100644 --- a/adminfront/src/features/users/components/UserBulkUploadModal.tsx +++ b/adminfront/src/features/users/components/UserBulkUploadModal.tsx @@ -127,9 +127,9 @@ function hanmacEmailStatusClass(preview?: HanmacImportEmailPreview) { export const downloadUserTemplate = () => { const headers = - "email,sub_email,name,phone,role,tenant_slug,department,grade,position,jobTitle,employee_id,tenant_slug1,department1,grade1,position1,jobTitle1,employee_id1"; + "uuid,email,sub_email,name,phone,role,tenant_slug,department,grade,position,jobTitle,employee_id,tenant_slug1,department1,grade1,position1,jobTitle1,employee_id1"; const example = - "user1@example.com,sub1@test.com;sub2@test.com,홍길동,010-1234-5678,user,tenant-slug,개발팀,수석,팀장,프론트엔드,EMP001,second-tenant,센터,책임,,Architecture,EMP002"; + ",user1@example.com,sub1@test.com;sub2@test.com,홍길동,010-1234-5678,user,tenant-slug,개발팀,수석,팀장,프론트엔드,EMP001,second-tenant,센터,책임,,Architecture,EMP002"; const blob = new Blob([`${headers}\n${example}`], { type: "text/csv;charset=utf-8;", }); @@ -295,22 +295,6 @@ export function UserBulkUploadModal({ }); }; - const downloadTemplate = () => { - const headers = - "email,sub_email,name,phone,role,tenant_slug,department,grade,position,jobTitle,employee_id,tenant_slug1,department1,grade1,position1,jobTitle1,employee_id1"; - const example = - "user1@example.com,sub1@test.com;sub2@test.com,홍길동,010-1234-5678,user,tenant-slug,개발팀,수석,팀장,프론트엔드,EMP001,second-tenant,센터,책임,,Architecture,EMP002"; - const blob = new Blob([`${headers}\n${example}`], { - type: "text/csv;charset=utf-8;", - }); - const url = URL.createObjectURL(blob); - const a = document.createElement("a"); - a.href = url; - a.download = "user_bulk_template.csv"; - a.click(); - URL.revokeObjectURL(url); - }; - const reset = () => { setFile(null); setPreviewData([]); @@ -410,7 +394,7 @@ export function UserBulkUploadModal({