@open-mercato/onboarding 0.6.7 → 0.6.8-develop.6875.1.871a4afc94

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/AGENTS.md +8 -5
  3. package/dist/modules/onboarding/__integration__/TC-ONB-001-self-service-consent.spec.js +25 -2
  4. package/dist/modules/onboarding/__integration__/TC-ONB-001-self-service-consent.spec.js.map +2 -2
  5. package/dist/modules/onboarding/__integration__/TC-ONB-002-multi-tenant-parallel-login.spec.js +3 -2
  6. package/dist/modules/onboarding/__integration__/TC-ONB-002-multi-tenant-parallel-login.spec.js.map +2 -2
  7. package/dist/modules/onboarding/api/post/onboarding.js +9 -1
  8. package/dist/modules/onboarding/api/post/onboarding.js.map +2 -2
  9. package/dist/modules/onboarding/data/entities.js +4 -0
  10. package/dist/modules/onboarding/data/entities.js.map +2 -2
  11. package/dist/modules/onboarding/encryption.js +4 -2
  12. package/dist/modules/onboarding/encryption.js.map +2 -2
  13. package/dist/modules/onboarding/lib/service.js +18 -6
  14. package/dist/modules/onboarding/lib/service.js.map +2 -2
  15. package/dist/modules/onboarding/migrations/Migration20260710133207_onboarding.js +15 -0
  16. package/dist/modules/onboarding/migrations/Migration20260710133207_onboarding.js.map +7 -0
  17. package/generated/entities/onboarding_request/index.ts +1 -0
  18. package/generated/entity-fields-registry.ts +1 -0
  19. package/package.json +6 -5
  20. package/src/__tests__/backfill-legacy-request.test.ts +213 -0
  21. package/src/__tests__/encryption.test.ts +19 -0
  22. package/src/__tests__/service.test.ts +16 -0
  23. package/src/modules/onboarding/__integration__/TC-ONB-001-self-service-consent.spec.ts +37 -2
  24. package/src/modules/onboarding/__integration__/TC-ONB-002-multi-tenant-parallel-login.spec.ts +3 -2
  25. package/src/modules/onboarding/api/post/onboarding.ts +9 -1
  26. package/src/modules/onboarding/data/entities.ts +4 -0
  27. package/src/modules/onboarding/encryption.ts +3 -1
  28. package/src/modules/onboarding/i18n/ko.json +100 -0
  29. package/src/modules/onboarding/lib/service.ts +17 -5
  30. package/src/modules/onboarding/migrations/.snapshot-open-mercato.json +19 -0
  31. package/src/modules/onboarding/migrations/Migration20260710133207_onboarding.ts +15 -0
@@ -1,6 +1,8 @@
1
1
  import { randomBytes, createHash } from 'node:crypto'
2
2
  import { hash } from 'bcryptjs'
3
3
  import { EntityManager } from '@mikro-orm/postgresql'
4
+ import { hashForLookup, lookupHashCandidates } from '@open-mercato/shared/lib/encryption/aes'
5
+ import { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'
4
6
  import { OnboardingRequest } from '../data/entities'
5
7
  import type { OnboardingStartInput } from '../data/validators'
6
8
 
@@ -18,8 +20,14 @@ export class OnboardingService {
18
20
  const expiresAt = new Date(Date.now() + expiresInHours * 60 * 60 * 1000)
19
21
  const now = new Date()
20
22
  const passwordHash = await hash(input.password, 10)
23
+ const emailHash = hashForLookup(input.email)
21
24
 
22
- const existing = await this.em.findOne(OnboardingRequest, { email: input.email })
25
+ const existing = await findOneWithDecryption(this.em, OnboardingRequest, {
26
+ $or: [
27
+ { emailHash: { $in: lookupHashCandidates(input.email) } },
28
+ { email: input.email, emailHash: null },
29
+ ],
30
+ })
23
31
  if (existing) {
24
32
  const lastSentAt = existing.lastEmailSentAt ?? existing.updatedAt ?? existing.createdAt
25
33
  if (['pending', 'processing'].includes(existing.status) && lastSentAt && lastSentAt.getTime() > Date.now() - 10 * 60 * 1000) {
@@ -36,6 +44,7 @@ export class OnboardingService {
36
44
  existing.termsAccepted = true
37
45
  existing.marketingConsent = input.marketingConsent ?? false
38
46
  existing.passwordHash = passwordHash
47
+ existing.emailHash = emailHash
39
48
  existing.expiresAt = expiresAt
40
49
  existing.completedAt = null
41
50
  existing.processingStartedAt = null
@@ -52,6 +61,7 @@ export class OnboardingService {
52
61
 
53
62
  const request = this.em.create(OnboardingRequest, {
54
63
  email: input.email,
64
+ emailHash,
55
65
  tokenHash,
56
66
  status: 'pending',
57
67
  firstName: input.firstName,
@@ -74,7 +84,7 @@ export class OnboardingService {
74
84
  async findPendingByToken(token: string) {
75
85
  const tokenHash = hashToken(token)
76
86
  const now = new Date()
77
- return this.em.findOne(OnboardingRequest, {
87
+ return findOneWithDecryption(this.em, OnboardingRequest, {
78
88
  tokenHash,
79
89
  status: 'pending',
80
90
  expiresAt: { $gt: now } as any,
@@ -83,18 +93,20 @@ export class OnboardingService {
83
93
 
84
94
  async findByToken(token: string) {
85
95
  const tokenHash = hashToken(token)
86
- return this.em.findOne(OnboardingRequest, { tokenHash })
96
+ return findOneWithDecryption(this.em, OnboardingRequest, { tokenHash })
87
97
  }
88
98
 
89
99
  async findById(id: string) {
90
- return this.em.findOne(OnboardingRequest, { id })
100
+ return findOneWithDecryption(this.em, OnboardingRequest, { id })
91
101
  }
92
102
 
93
103
  async findLatestByTenantId(tenantId: string) {
94
- return this.em.findOne(
104
+ return findOneWithDecryption(
105
+ this.em,
95
106
  OnboardingRequest,
96
107
  { tenantId, deletedAt: null },
97
108
  { orderBy: { updatedAt: 'DESC', createdAt: 'DESC' } },
109
+ { tenantId, organizationId: null },
98
110
  )
99
111
  }
100
112
 
@@ -25,6 +25,15 @@
25
25
  "nullable": false,
26
26
  "mappedType": "text"
27
27
  },
28
+ "email_hash": {
29
+ "name": "email_hash",
30
+ "type": "text",
31
+ "unsigned": false,
32
+ "autoincrement": false,
33
+ "primary": false,
34
+ "nullable": true,
35
+ "mappedType": "text"
36
+ },
28
37
  "token_hash": {
29
38
  "name": "token_hash",
30
39
  "type": "text",
@@ -260,6 +269,16 @@
260
269
  "primary": false,
261
270
  "unique": true
262
271
  },
272
+ {
273
+ "keyName": "onboarding_requests_email_hash_unique",
274
+ "columnNames": [
275
+ "email_hash"
276
+ ],
277
+ "composite": false,
278
+ "constraint": true,
279
+ "primary": false,
280
+ "unique": true
281
+ },
263
282
  {
264
283
  "keyName": "onboarding_requests_pkey",
265
284
  "columnNames": [
@@ -0,0 +1,15 @@
1
+ import { Migration } from '@mikro-orm/migrations';
2
+
3
+ export class Migration20260710133207_onboarding extends Migration {
4
+
5
+ override up(): void | Promise<void> {
6
+ this.addSql(`alter table "onboarding_requests" add "email_hash" text null;`);
7
+ this.addSql(`alter table "onboarding_requests" add constraint "onboarding_requests_email_hash_unique" unique ("email_hash");`);
8
+ }
9
+
10
+ override down(): void | Promise<void> {
11
+ this.addSql(`alter table "onboarding_requests" drop constraint if exists "onboarding_requests_email_hash_unique";`);
12
+ this.addSql(`alter table "onboarding_requests" drop column "email_hash";`);
13
+ }
14
+
15
+ }