@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.
- package/.turbo/turbo-build.log +1 -1
- package/AGENTS.md +8 -5
- package/dist/modules/onboarding/__integration__/TC-ONB-001-self-service-consent.spec.js +25 -2
- package/dist/modules/onboarding/__integration__/TC-ONB-001-self-service-consent.spec.js.map +2 -2
- package/dist/modules/onboarding/__integration__/TC-ONB-002-multi-tenant-parallel-login.spec.js +3 -2
- package/dist/modules/onboarding/__integration__/TC-ONB-002-multi-tenant-parallel-login.spec.js.map +2 -2
- package/dist/modules/onboarding/api/post/onboarding.js +9 -1
- package/dist/modules/onboarding/api/post/onboarding.js.map +2 -2
- package/dist/modules/onboarding/data/entities.js +4 -0
- package/dist/modules/onboarding/data/entities.js.map +2 -2
- package/dist/modules/onboarding/encryption.js +4 -2
- package/dist/modules/onboarding/encryption.js.map +2 -2
- package/dist/modules/onboarding/lib/service.js +18 -6
- package/dist/modules/onboarding/lib/service.js.map +2 -2
- package/dist/modules/onboarding/migrations/Migration20260710133207_onboarding.js +15 -0
- package/dist/modules/onboarding/migrations/Migration20260710133207_onboarding.js.map +7 -0
- package/generated/entities/onboarding_request/index.ts +1 -0
- package/generated/entity-fields-registry.ts +1 -0
- package/package.json +6 -5
- package/src/__tests__/backfill-legacy-request.test.ts +213 -0
- package/src/__tests__/encryption.test.ts +19 -0
- package/src/__tests__/service.test.ts +16 -0
- package/src/modules/onboarding/__integration__/TC-ONB-001-self-service-consent.spec.ts +37 -2
- package/src/modules/onboarding/__integration__/TC-ONB-002-multi-tenant-parallel-login.spec.ts +3 -2
- package/src/modules/onboarding/api/post/onboarding.ts +9 -1
- package/src/modules/onboarding/data/entities.ts +4 -0
- package/src/modules/onboarding/encryption.ts +3 -1
- package/src/modules/onboarding/i18n/ko.json +100 -0
- package/src/modules/onboarding/lib/service.ts +17 -5
- package/src/modules/onboarding/migrations/.snapshot-open-mercato.json +19 -0
- 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
|
|
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
|
|
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
|
|
96
|
+
return findOneWithDecryption(this.em, OnboardingRequest, { tokenHash })
|
|
87
97
|
}
|
|
88
98
|
|
|
89
99
|
async findById(id: string) {
|
|
90
|
-
return this.em
|
|
100
|
+
return findOneWithDecryption(this.em, OnboardingRequest, { id })
|
|
91
101
|
}
|
|
92
102
|
|
|
93
103
|
async findLatestByTenantId(tenantId: string) {
|
|
94
|
-
return
|
|
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
|
+
}
|