1
1
import type { BindMfa , CreateUser , Scope , User } from '@logto/schemas' ;
2
- import { RoleType , UsersPasswordEncryptionMethod } from '@logto/schemas' ;
2
+ import {
3
+ adminTenantId ,
4
+ ProductEvent ,
5
+ RoleType ,
6
+ UsersPasswordEncryptionMethod ,
7
+ } from '@logto/schemas' ;
3
8
import { generateStandardShortId , generateStandardId } from '@logto/shared' ;
4
9
import type { Nullable } from '@silverhand/essentials' ;
5
10
import { deduplicateByKey , condArray } from '@silverhand/essentials' ;
@@ -15,13 +20,15 @@ import assertThat from '#src/utils/assert-that.js';
15
20
import { legacyVerify } from '#src/utils/password.js' ;
16
21
import type { OmitAutoSetFields } from '#src/utils/sql.js' ;
17
22
23
+ import { captureDeveloperEvent } from '../utils/posthog.js' ;
24
+
18
25
import { convertBindMfaToMfaVerification , encryptUserPassword } from './user.utils.js' ;
19
26
20
27
export type InsertUserResult = [ User ] ;
21
28
22
29
export type UserLibrary = ReturnType < typeof createUserLibrary > ;
23
30
24
- export const createUserLibrary = ( queries : Queries ) => {
31
+ export const createUserLibrary = ( tenantId : string , queries : Queries ) => {
25
32
const {
26
33
pool,
27
34
roles : { findDefaultRoles, findRolesByRoleNames, findRoleByRoleName, findRolesByRoleIds } ,
@@ -59,19 +66,30 @@ export const createUserLibrary = (queries: Queries) => {
59
66
{ retries, factor : 0 } // No need for exponential backoff
60
67
) ;
61
68
69
+ type InsertUserOptions = {
70
+ /** Additional role names to assign to the user upon creation. */
71
+ roleNames ?: string [ ] ;
72
+ /**
73
+ * Whether the user is created via an interactive flow (e.g. sign up).
74
+ * @default false
75
+ */
76
+ isInteractive ?: boolean ;
77
+ } ;
78
+
62
79
const insertUser = async (
63
80
data : OmitAutoSetFields < CreateUser > ,
64
- additionalRoleNames : string [ ]
81
+ options ?: InsertUserOptions
65
82
) : Promise < InsertUserResult > => {
66
- const roleNames = [ ...EnvSet . values . userDefaultRoleNames , ...additionalRoleNames ] ;
83
+ const { isInteractive = false } = options ?? { } ;
84
+ const roleNames = [ ...EnvSet . values . userDefaultRoleNames , ...( options ?. roleNames ?? [ ] ) ] ;
67
85
const [ parameterRoles , defaultRoles ] = await Promise . all ( [
68
86
findRolesByRoleNames ( roleNames ) ,
69
87
findDefaultRoles ( RoleType . User ) ,
70
88
] ) ;
71
89
72
90
assertThat ( parameterRoles . length === roleNames . length , 'role.default_role_missing' ) ;
73
91
74
- return pool . transaction ( async ( connection ) => {
92
+ const result = await pool . transaction < [ User ] > ( async ( connection ) => {
75
93
const user = await insertUserQuery ( data ) ;
76
94
const roles = deduplicateByKey ( [ ...parameterRoles , ...defaultRoles ] , 'id' ) ;
77
95
@@ -84,6 +102,12 @@ export const createUserLibrary = (queries: Queries) => {
84
102
85
103
return [ user ] ;
86
104
} ) ;
105
+
106
+ if ( tenantId === adminTenantId ) {
107
+ captureDeveloperEvent ( result [ 0 ] . id , ProductEvent . DeveloperCreated , { isInteractive } ) ;
108
+ }
109
+
110
+ return result ;
87
111
} ;
88
112
89
113
const checkIdentifierCollision = async (
0 commit comments