@flowerforce/flowerbase 1.2.1-beta.2 → 1.2.1-beta.20
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/README.md +37 -6
- package/dist/auth/controller.d.ts.map +1 -1
- package/dist/auth/controller.js +55 -4
- package/dist/auth/plugins/jwt.d.ts.map +1 -1
- package/dist/auth/plugins/jwt.js +52 -6
- package/dist/auth/providers/anon-user/controller.d.ts +8 -0
- package/dist/auth/providers/anon-user/controller.d.ts.map +1 -0
- package/dist/auth/providers/anon-user/controller.js +90 -0
- package/dist/auth/providers/anon-user/dtos.d.ts +10 -0
- package/dist/auth/providers/anon-user/dtos.d.ts.map +1 -0
- package/dist/auth/providers/anon-user/dtos.js +2 -0
- package/dist/auth/providers/custom-function/controller.d.ts.map +1 -1
- package/dist/auth/providers/custom-function/controller.js +35 -25
- package/dist/auth/providers/custom-function/dtos.d.ts +4 -1
- package/dist/auth/providers/custom-function/dtos.d.ts.map +1 -1
- package/dist/auth/providers/local-userpass/controller.d.ts.map +1 -1
- package/dist/auth/providers/local-userpass/controller.js +159 -73
- package/dist/auth/providers/local-userpass/dtos.d.ts +17 -2
- package/dist/auth/providers/local-userpass/dtos.d.ts.map +1 -1
- package/dist/auth/utils.d.ts +76 -14
- package/dist/auth/utils.d.ts.map +1 -1
- package/dist/auth/utils.js +55 -61
- package/dist/constants.d.ts +12 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +16 -4
- package/dist/features/functions/controller.d.ts.map +1 -1
- package/dist/features/functions/controller.js +31 -12
- package/dist/features/functions/dtos.d.ts +3 -0
- package/dist/features/functions/dtos.d.ts.map +1 -1
- package/dist/features/functions/interface.d.ts +3 -0
- package/dist/features/functions/interface.d.ts.map +1 -1
- package/dist/features/functions/utils.d.ts +3 -2
- package/dist/features/functions/utils.d.ts.map +1 -1
- package/dist/features/functions/utils.js +19 -7
- package/dist/features/triggers/index.d.ts.map +1 -1
- package/dist/features/triggers/index.js +49 -7
- package/dist/features/triggers/interface.d.ts +1 -0
- package/dist/features/triggers/interface.d.ts.map +1 -1
- package/dist/features/triggers/utils.d.ts.map +1 -1
- package/dist/features/triggers/utils.js +67 -26
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +48 -13
- package/dist/services/mongodb-atlas/index.d.ts.map +1 -1
- package/dist/services/mongodb-atlas/index.js +72 -2
- package/dist/services/mongodb-atlas/model.d.ts +3 -2
- package/dist/services/mongodb-atlas/model.d.ts.map +1 -1
- package/dist/shared/handleUserRegistration.d.ts.map +1 -1
- package/dist/shared/handleUserRegistration.js +66 -1
- package/dist/shared/models/handleUserRegistration.model.d.ts +2 -1
- package/dist/shared/models/handleUserRegistration.model.d.ts.map +1 -1
- package/dist/shared/models/handleUserRegistration.model.js +1 -0
- package/dist/utils/context/helpers.d.ts +6 -6
- package/dist/utils/context/helpers.d.ts.map +1 -1
- package/dist/utils/context/index.d.ts +1 -1
- package/dist/utils/context/index.d.ts.map +1 -1
- package/dist/utils/context/index.js +176 -9
- package/dist/utils/context/interface.d.ts +1 -1
- package/dist/utils/context/interface.d.ts.map +1 -1
- package/dist/utils/crypto/index.d.ts +1 -0
- package/dist/utils/crypto/index.d.ts.map +1 -1
- package/dist/utils/crypto/index.js +6 -2
- package/dist/utils/initializer/exposeRoutes.js +1 -1
- package/dist/utils/initializer/registerPlugins.d.ts.map +1 -1
- package/dist/utils/initializer/registerPlugins.js +12 -4
- package/dist/utils/roles/helpers.js +2 -1
- package/package.json +1 -2
- package/src/auth/controller.ts +71 -5
- package/src/auth/plugins/jwt.test.ts +93 -0
- package/src/auth/plugins/jwt.ts +67 -8
- package/src/auth/providers/anon-user/controller.ts +91 -0
- package/src/auth/providers/anon-user/dtos.ts +10 -0
- package/src/auth/providers/custom-function/controller.ts +40 -31
- package/src/auth/providers/custom-function/dtos.ts +5 -1
- package/src/auth/providers/local-userpass/controller.ts +211 -101
- package/src/auth/providers/local-userpass/dtos.ts +20 -2
- package/src/auth/utils.ts +66 -83
- package/src/constants.ts +14 -2
- package/src/features/functions/controller.ts +42 -12
- package/src/features/functions/dtos.ts +3 -0
- package/src/features/functions/interface.ts +3 -0
- package/src/features/functions/utils.ts +29 -8
- package/src/features/triggers/index.ts +44 -1
- package/src/features/triggers/interface.ts +1 -0
- package/src/features/triggers/utils.ts +89 -37
- package/src/index.ts +49 -13
- package/src/services/mongodb-atlas/__tests__/findOneAndUpdate.test.ts +95 -0
- package/src/services/mongodb-atlas/index.ts +100 -2
- package/src/services/mongodb-atlas/model.ts +16 -3
- package/src/shared/handleUserRegistration.ts +83 -2
- package/src/shared/models/handleUserRegistration.model.ts +2 -1
- package/src/utils/__tests__/registerPlugins.test.ts +5 -1
- package/src/utils/context/index.ts +238 -18
- package/src/utils/context/interface.ts +1 -1
- package/src/utils/crypto/index.ts +5 -1
- package/src/utils/initializer/exposeRoutes.ts +1 -1
- package/src/utils/initializer/registerPlugins.ts +8 -0
- package/src/utils/roles/helpers.ts +3 -2
package/src/auth/utils.ts
CHANGED
|
@@ -8,19 +8,45 @@ export const LOGIN_SCHEMA = {
|
|
|
8
8
|
body: {
|
|
9
9
|
type: 'object',
|
|
10
10
|
properties: {
|
|
11
|
-
username: {
|
|
12
|
-
|
|
11
|
+
username: {
|
|
12
|
+
type: 'string',
|
|
13
|
+
pattern: '^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$',
|
|
14
|
+
minLength: 3,
|
|
15
|
+
maxLength: 254
|
|
16
|
+
},
|
|
17
|
+
password: { type: 'string', minLength: 8, maxLength: 128 }
|
|
13
18
|
},
|
|
14
19
|
required: ['username', 'password']
|
|
15
20
|
}
|
|
16
21
|
}
|
|
17
22
|
|
|
18
|
-
export const
|
|
23
|
+
export const RESET_SEND_SCHEMA = {
|
|
19
24
|
body: {
|
|
20
25
|
type: 'object',
|
|
21
26
|
properties: {
|
|
22
|
-
email: {
|
|
23
|
-
|
|
27
|
+
email: {
|
|
28
|
+
type: 'string',
|
|
29
|
+
pattern: '^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$',
|
|
30
|
+
minLength: 3,
|
|
31
|
+
maxLength: 254
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
required: ['email']
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export const RESET_CALL_SCHEMA = {
|
|
39
|
+
body: {
|
|
40
|
+
type: 'object',
|
|
41
|
+
properties: {
|
|
42
|
+
email: {
|
|
43
|
+
type: 'string',
|
|
44
|
+
pattern: '^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$',
|
|
45
|
+
minLength: 3,
|
|
46
|
+
maxLength: 254
|
|
47
|
+
},
|
|
48
|
+
password: { type: 'string', minLength: 8, maxLength: 128 },
|
|
49
|
+
arguments: { type: 'array' }
|
|
24
50
|
},
|
|
25
51
|
required: ['email', 'password']
|
|
26
52
|
}
|
|
@@ -30,7 +56,7 @@ export const CONFIRM_RESET_SCHEMA = {
|
|
|
30
56
|
body: {
|
|
31
57
|
type: 'object',
|
|
32
58
|
properties: {
|
|
33
|
-
password: { type: 'string' },
|
|
59
|
+
password: { type: 'string', minLength: 8, maxLength: 128 },
|
|
34
60
|
token: { type: 'string' },
|
|
35
61
|
tokenId: { type: 'string' }
|
|
36
62
|
},
|
|
@@ -38,12 +64,30 @@ export const CONFIRM_RESET_SCHEMA = {
|
|
|
38
64
|
}
|
|
39
65
|
}
|
|
40
66
|
|
|
67
|
+
export const CONFIRM_USER_SCHEMA = {
|
|
68
|
+
body: {
|
|
69
|
+
type: 'object',
|
|
70
|
+
properties: {
|
|
71
|
+
token: { type: 'string' },
|
|
72
|
+
tokenId: { type: 'string' }
|
|
73
|
+
},
|
|
74
|
+
required: ['token', 'tokenId']
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export const RESET_SCHEMA = RESET_SEND_SCHEMA
|
|
79
|
+
|
|
41
80
|
export const REGISTRATION_SCHEMA = {
|
|
42
81
|
body: {
|
|
43
82
|
type: 'object',
|
|
44
83
|
properties: {
|
|
45
|
-
email: {
|
|
46
|
-
|
|
84
|
+
email: {
|
|
85
|
+
type: 'string',
|
|
86
|
+
pattern: '^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$',
|
|
87
|
+
minLength: 3,
|
|
88
|
+
maxLength: 254
|
|
89
|
+
},
|
|
90
|
+
password: { type: 'string', minLength: 8, maxLength: 128 }
|
|
47
91
|
},
|
|
48
92
|
required: ['email', 'password']
|
|
49
93
|
}
|
|
@@ -52,9 +96,11 @@ export const REGISTRATION_SCHEMA = {
|
|
|
52
96
|
export enum AUTH_ENDPOINTS {
|
|
53
97
|
LOGIN = '/login',
|
|
54
98
|
REGISTRATION = '/register',
|
|
99
|
+
CONFIRM = '/confirm',
|
|
55
100
|
PROFILE = '/profile',
|
|
56
101
|
SESSION = '/session',
|
|
57
|
-
RESET = '/reset/
|
|
102
|
+
RESET = '/reset/send',
|
|
103
|
+
RESET_CALL = '/reset/call',
|
|
58
104
|
CONFIRM_RESET = "/reset",
|
|
59
105
|
FIRST_USER = '/setup/first-user'
|
|
60
106
|
}
|
|
@@ -62,7 +108,9 @@ export enum AUTH_ENDPOINTS {
|
|
|
62
108
|
export enum AUTH_ERRORS {
|
|
63
109
|
INVALID_CREDENTIALS = 'Invalid credentials',
|
|
64
110
|
INVALID_TOKEN = 'Invalid refresh token provided',
|
|
65
|
-
INVALID_RESET_PARAMS = 'Invalid token or tokenId provided'
|
|
111
|
+
INVALID_RESET_PARAMS = 'Invalid token or tokenId provided',
|
|
112
|
+
MISSING_RESET_FUNCTION = 'Missing reset function',
|
|
113
|
+
USER_NOT_CONFIRMED = 'User not confirmed'
|
|
66
114
|
}
|
|
67
115
|
|
|
68
116
|
export interface AuthConfig {
|
|
@@ -70,6 +118,7 @@ export interface AuthConfig {
|
|
|
70
118
|
'api-key': ApiKey
|
|
71
119
|
'local-userpass': LocalUserpass
|
|
72
120
|
'custom-function': CustomFunction
|
|
121
|
+
'anon-user'?: AnonUser
|
|
73
122
|
}
|
|
74
123
|
|
|
75
124
|
interface ApiKey {
|
|
@@ -93,17 +142,19 @@ interface CustomFunction {
|
|
|
93
142
|
}
|
|
94
143
|
}
|
|
95
144
|
|
|
145
|
+
export interface AnonUser {
|
|
146
|
+
name: "anon-user"
|
|
147
|
+
type: "anon-user"
|
|
148
|
+
disabled: boolean
|
|
149
|
+
}
|
|
150
|
+
|
|
96
151
|
export interface Config {
|
|
97
152
|
autoConfirm: boolean
|
|
153
|
+
confirmationFunctionName?: string
|
|
98
154
|
resetFunctionName: string
|
|
99
155
|
resetPasswordUrl: string
|
|
100
156
|
runConfirmationFunction: boolean
|
|
101
157
|
runResetFunction: boolean
|
|
102
|
-
mailConfig: {
|
|
103
|
-
from: string
|
|
104
|
-
subject: string
|
|
105
|
-
mailToken: string
|
|
106
|
-
}
|
|
107
158
|
}
|
|
108
159
|
|
|
109
160
|
export interface CustomUserDataConfig {
|
|
@@ -137,74 +188,6 @@ export const loadCustomUserData = (): CustomUserDataConfig => {
|
|
|
137
188
|
return JSON.parse(fs.readFileSync(userDataPath, 'utf-8'))
|
|
138
189
|
}
|
|
139
190
|
|
|
140
|
-
export const getMailConfig = (
|
|
141
|
-
resetPasswordConfig: Config,
|
|
142
|
-
token: string,
|
|
143
|
-
tokenId: string
|
|
144
|
-
) => {
|
|
145
|
-
const { mailConfig, resetPasswordUrl } = resetPasswordConfig
|
|
146
|
-
const ENV_PREFIX = 'ENV'
|
|
147
|
-
const { from, subject, mailToken } = mailConfig
|
|
148
|
-
|
|
149
|
-
const [fromPrefix, fromPath] = from.split('.')
|
|
150
|
-
|
|
151
|
-
if (!fromPath) {
|
|
152
|
-
throw new Error(`Invalid fromPath: ${fromPath}`)
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
const currentSender = (fromPrefix === ENV_PREFIX ? process.env[fromPath] : from) ?? ''
|
|
156
|
-
const [subjectPrefix, subjectPath] = subject.split('.')
|
|
157
|
-
|
|
158
|
-
if (!subjectPath) {
|
|
159
|
-
throw new Error(`Invalid subjectPath: ${subjectPath}`)
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
const currentSubject =
|
|
163
|
-
(subjectPrefix === ENV_PREFIX ? process.env[subjectPath] : subject) ?? ''
|
|
164
|
-
const [mailTokenPrefix, mailTokenPath] = mailToken.split('.')
|
|
165
|
-
|
|
166
|
-
if (!mailTokenPath) {
|
|
167
|
-
throw new Error(`Invalid mailTokenPath: ${mailTokenPath}`)
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
const currentMailToken =
|
|
171
|
-
(mailTokenPrefix === 'ENV' ? process.env[mailTokenPath] : mailToken) ?? ''
|
|
172
|
-
|
|
173
|
-
const link = `${resetPasswordUrl}/${token}/${tokenId}`
|
|
174
|
-
const body = `<body style="font-family: Arial, sans-serif; background-color: #f4f4f4; text-align: center; padding: 20px;">
|
|
175
|
-
<table width="100%" cellspacing="0" cellpadding="0">
|
|
176
|
-
<tr>
|
|
177
|
-
<td align="center">
|
|
178
|
-
<table width="600" cellspacing="0" cellpadding="0" style="background: #ffffff; padding: 20px; border-radius: 8px; box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);">
|
|
179
|
-
<tr>
|
|
180
|
-
<td align="center">
|
|
181
|
-
<h2>Password Reset Request</h2>
|
|
182
|
-
<p>If you requested a password reset, click the button below to reset your password.</p>
|
|
183
|
-
<p>If you did not request this, please ignore this email.</p>
|
|
184
|
-
<p>
|
|
185
|
-
<a href="${link}" style="display: inline-block; padding: 12px 20px; font-size: 16px; color: #ffffff; background: #007bff; text-decoration: none; border-radius: 5px;">Reset Password</a>
|
|
186
|
-
</p>
|
|
187
|
-
<p style="margin-top: 20px; font-size: 12px; color: #777;">If the button does not work, copy and paste the following link into your browser:</p>
|
|
188
|
-
<p style="font-size: 12px; color: #777;">${link}</p>
|
|
189
|
-
</td>
|
|
190
|
-
</tr>
|
|
191
|
-
</table>
|
|
192
|
-
</td>
|
|
193
|
-
</tr>
|
|
194
|
-
</table>
|
|
195
|
-
</body>`
|
|
196
|
-
return {
|
|
197
|
-
from: currentSender ?? '',
|
|
198
|
-
subject: currentSubject,
|
|
199
|
-
mailToken: currentMailToken,
|
|
200
|
-
body
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
191
|
export const generatePassword = (length = 20) => {
|
|
209
192
|
const bytes = crypto.randomBytes(length);
|
|
210
193
|
return Array.from(bytes, (b) => CHARSET[b % CHARSET.length]).join("");
|
package/src/constants.ts
CHANGED
|
@@ -17,6 +17,15 @@ export const DEFAULT_CONFIG = {
|
|
|
17
17
|
HTTPS_SCHEMA: process.env.HTTPS_SCHEMA || 'https',
|
|
18
18
|
HOST: process.env.HOST || '0.0.0.0',
|
|
19
19
|
ENABLE_LOGGER: process.env.ENABLE_LOGGER,
|
|
20
|
+
RESET_PASSWORD_TTL_SECONDS: Number(process.env.RESET_PASSWORD_TTL_SECONDS) || 3600,
|
|
21
|
+
AUTH_RATE_LIMIT_WINDOW_MS: Number(process.env.AUTH_RATE_LIMIT_WINDOW_MS) || 15 * 60 * 1000,
|
|
22
|
+
AUTH_LOGIN_MAX_ATTEMPTS: Number(process.env.AUTH_LOGIN_MAX_ATTEMPTS) || 10,
|
|
23
|
+
AUTH_REGISTER_MAX_ATTEMPTS: Number(process.env.AUTH_REGISTER_MAX_ATTEMPTS) || 5,
|
|
24
|
+
AUTH_RESET_MAX_ATTEMPTS: Number(process.env.AUTH_RESET_MAX_ATTEMPTS) || 5,
|
|
25
|
+
REFRESH_TOKEN_TTL_DAYS: Number(process.env.REFRESH_TOKEN_TTL_DAYS) || 60,
|
|
26
|
+
ANON_USER_TTL_SECONDS: Number(process.env.ANON_USER_TTL_SECONDS) || 3 * 60 * 60,
|
|
27
|
+
SWAGGER_UI_USER: process.env.SWAGGER_UI_USER || '',
|
|
28
|
+
SWAGGER_UI_PASSWORD: process.env.SWAGGER_UI_PASSWORD || '',
|
|
20
29
|
CORS_OPTIONS: {
|
|
21
30
|
origin: "*",
|
|
22
31
|
methods: ["GET", "POST", "PUT", "DELETE"] as ALLOWED_METHODS[]
|
|
@@ -30,12 +39,15 @@ export const DB_NAME = database_name
|
|
|
30
39
|
export const AUTH_CONFIG = {
|
|
31
40
|
authCollection: auth_collection,
|
|
32
41
|
userCollection: collection_name,
|
|
33
|
-
resetPasswordCollection: '
|
|
42
|
+
resetPasswordCollection: 'reset_password_requests',
|
|
43
|
+
refreshTokensCollection: 'auth_refresh_tokens',
|
|
34
44
|
resetPasswordConfig: configuration['local-userpass']?.config,
|
|
45
|
+
localUserpassConfig: configuration['local-userpass']?.config,
|
|
35
46
|
user_id_field,
|
|
36
47
|
on_user_creation_function_name,
|
|
37
48
|
providers: {
|
|
38
|
-
"custom-function": configuration['custom-function']?.config
|
|
49
|
+
"custom-function": configuration['custom-function']?.config,
|
|
50
|
+
"anon-user": configuration['anon-user']
|
|
39
51
|
}
|
|
40
52
|
}
|
|
41
53
|
|
|
@@ -34,6 +34,13 @@ const logFunctionCall = (method: string, user: Record<string, any> | undefined,
|
|
|
34
34
|
console.log('[functions-debug]', method, user ? { id: user.id, role: user.role, email: user.email } : 'no-user', args)
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
+
const formatFunctionExecutionError = (error: unknown) => {
|
|
38
|
+
const err = error as { message?: string; name?: string }
|
|
39
|
+
const message = typeof err?.message === 'string' ? err.message : String(error)
|
|
40
|
+
const name = typeof err?.name === 'string' ? err.name : 'Error'
|
|
41
|
+
return JSON.stringify({ message, name })
|
|
42
|
+
}
|
|
43
|
+
|
|
37
44
|
/**
|
|
38
45
|
* > Creates a pre handler for every query
|
|
39
46
|
* @param app -> the fastify instance
|
|
@@ -61,7 +68,18 @@ export const functionsController: FunctionController = async (
|
|
|
61
68
|
if (!serviceFn) {
|
|
62
69
|
throw new Error(`Service "${req.body.service}" does not exist`)
|
|
63
70
|
}
|
|
64
|
-
const [{
|
|
71
|
+
const [{
|
|
72
|
+
database,
|
|
73
|
+
collection,
|
|
74
|
+
query,
|
|
75
|
+
filter,
|
|
76
|
+
update,
|
|
77
|
+
options,
|
|
78
|
+
returnNewDocument,
|
|
79
|
+
document,
|
|
80
|
+
documents,
|
|
81
|
+
pipeline = []
|
|
82
|
+
}] = args
|
|
65
83
|
|
|
66
84
|
const currentMethod = serviceFn(app, { rules, user })
|
|
67
85
|
.db(database)
|
|
@@ -71,7 +89,10 @@ export const functionsController: FunctionController = async (
|
|
|
71
89
|
const operatorsByType = await executeQuery({
|
|
72
90
|
currentMethod,
|
|
73
91
|
query,
|
|
92
|
+
filter,
|
|
74
93
|
update,
|
|
94
|
+
options,
|
|
95
|
+
returnNewDocument,
|
|
75
96
|
document,
|
|
76
97
|
documents,
|
|
77
98
|
pipeline,
|
|
@@ -91,17 +112,26 @@ export const functionsController: FunctionController = async (
|
|
|
91
112
|
}
|
|
92
113
|
|
|
93
114
|
logFunctionCall(`function:${method}`, user, args)
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
115
|
+
try {
|
|
116
|
+
const result = await GenerateContext({
|
|
117
|
+
args: req.body.arguments,
|
|
118
|
+
app,
|
|
119
|
+
rules,
|
|
120
|
+
user: { ...user, _id: new ObjectId(user.id) },
|
|
121
|
+
currentFunction,
|
|
122
|
+
functionsList,
|
|
123
|
+
services
|
|
124
|
+
})
|
|
125
|
+
res.type('application/json')
|
|
126
|
+
return JSON.stringify(result)
|
|
127
|
+
} catch (error) {
|
|
128
|
+
res.status(500)
|
|
129
|
+
res.type('application/json')
|
|
130
|
+
return JSON.stringify({
|
|
131
|
+
error: formatFunctionExecutionError(error),
|
|
132
|
+
error_code: 'FunctionExecutionError'
|
|
133
|
+
})
|
|
134
|
+
}
|
|
105
135
|
})
|
|
106
136
|
app.get<{
|
|
107
137
|
Querystring: FunctionCallBase64Dto
|
|
@@ -23,8 +23,11 @@ export type FunctionCallBase64Dto = {
|
|
|
23
23
|
type ArgumentsData = Arguments<{
|
|
24
24
|
database: string
|
|
25
25
|
collection: string
|
|
26
|
+
filter?: Document
|
|
26
27
|
query: Parameters<GetOperatorsFunction>
|
|
27
28
|
update: Document
|
|
29
|
+
options?: Document
|
|
30
|
+
returnNewDocument?: boolean
|
|
28
31
|
document: Document
|
|
29
32
|
documents: Document[]
|
|
30
33
|
pipeline?: Document[]
|
|
@@ -24,6 +24,9 @@ export type ExecuteQueryParams = {
|
|
|
24
24
|
currentMethod: ReturnType<GetOperatorsFunction>[keyof ReturnType<GetOperatorsFunction>]
|
|
25
25
|
query: Parameters<GetOperatorsFunction>
|
|
26
26
|
update: Document
|
|
27
|
+
filter?: Document
|
|
28
|
+
options?: Document
|
|
29
|
+
returnNewDocument?: boolean
|
|
27
30
|
document: Document
|
|
28
31
|
documents: Document[]
|
|
29
32
|
pipeline: Document[]
|
|
@@ -44,29 +44,51 @@ export const executeQuery = async ({
|
|
|
44
44
|
currentMethod,
|
|
45
45
|
query,
|
|
46
46
|
update,
|
|
47
|
+
filter,
|
|
48
|
+
options,
|
|
49
|
+
returnNewDocument,
|
|
47
50
|
document,
|
|
48
51
|
documents,
|
|
49
52
|
pipeline,
|
|
50
53
|
isClient = false
|
|
51
54
|
}: ExecuteQueryParams) => {
|
|
55
|
+
const resolvedQuery =
|
|
56
|
+
typeof query !== 'undefined'
|
|
57
|
+
? query
|
|
58
|
+
: typeof filter !== 'undefined'
|
|
59
|
+
? filter
|
|
60
|
+
: {}
|
|
61
|
+
const resolvedUpdate = typeof update !== 'undefined' ? update : {}
|
|
62
|
+
const resolvedOptions =
|
|
63
|
+
typeof options !== 'undefined'
|
|
64
|
+
? options
|
|
65
|
+
: typeof returnNewDocument === 'boolean'
|
|
66
|
+
? { returnDocument: returnNewDocument ? 'after' : 'before' }
|
|
67
|
+
: undefined
|
|
52
68
|
return {
|
|
53
69
|
find: async () =>
|
|
54
70
|
await (currentMethod as ReturnType<GetOperatorsFunction>['find'])(
|
|
55
|
-
EJSON.deserialize(
|
|
71
|
+
EJSON.deserialize(resolvedQuery)
|
|
56
72
|
).toArray(),
|
|
57
73
|
findOne: () =>
|
|
58
74
|
(currentMethod as ReturnType<GetOperatorsFunction>['findOne'])(
|
|
59
|
-
EJSON.deserialize(
|
|
75
|
+
EJSON.deserialize(resolvedQuery)
|
|
60
76
|
),
|
|
61
77
|
deleteOne: () =>
|
|
62
78
|
(currentMethod as ReturnType<GetOperatorsFunction>['deleteOne'])(
|
|
63
|
-
EJSON.deserialize(
|
|
79
|
+
EJSON.deserialize(resolvedQuery)
|
|
64
80
|
),
|
|
65
81
|
insertOne: () =>
|
|
66
82
|
(currentMethod as ReturnType<GetOperatorsFunction>['insertOne'])(
|
|
67
83
|
EJSON.deserialize(document)
|
|
68
84
|
),
|
|
69
|
-
updateOne: () => currentMethod(EJSON.deserialize(
|
|
85
|
+
updateOne: () => currentMethod(EJSON.deserialize(resolvedQuery), EJSON.deserialize(resolvedUpdate)),
|
|
86
|
+
findOneAndUpdate: () =>
|
|
87
|
+
(currentMethod as ReturnType<GetOperatorsFunction>['findOneAndUpdate'])(
|
|
88
|
+
EJSON.deserialize(resolvedQuery),
|
|
89
|
+
EJSON.deserialize(resolvedUpdate),
|
|
90
|
+
resolvedOptions ? EJSON.deserialize(resolvedOptions) : undefined
|
|
91
|
+
),
|
|
70
92
|
aggregate: async () =>
|
|
71
93
|
(await (currentMethod as ReturnType<GetOperatorsFunction>['aggregate'])(
|
|
72
94
|
EJSON.deserialize(pipeline),
|
|
@@ -79,13 +101,12 @@ export const executeQuery = async ({
|
|
|
79
101
|
),
|
|
80
102
|
updateMany: () =>
|
|
81
103
|
(currentMethod as ReturnType<GetOperatorsFunction>['updateMany'])(
|
|
82
|
-
EJSON.deserialize(
|
|
83
|
-
EJSON.deserialize(
|
|
104
|
+
EJSON.deserialize(resolvedQuery),
|
|
105
|
+
EJSON.deserialize(resolvedUpdate)
|
|
84
106
|
),
|
|
85
107
|
deleteMany: () =>
|
|
86
108
|
(currentMethod as ReturnType<GetOperatorsFunction>['deleteMany'])(
|
|
87
|
-
EJSON.deserialize(
|
|
109
|
+
EJSON.deserialize(resolvedQuery)
|
|
88
110
|
)
|
|
89
111
|
}
|
|
90
112
|
}
|
|
91
|
-
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { AUTH_CONFIG, DB_NAME } from '../../constants'
|
|
1
2
|
import { services } from '../../services'
|
|
2
3
|
import { Function, Functions } from '../functions/interface'
|
|
3
4
|
import { ActivateTriggersParams } from './dtos'
|
|
@@ -17,7 +18,49 @@ export const activateTriggers = async ({
|
|
|
17
18
|
}: ActivateTriggersParams) => {
|
|
18
19
|
console.log('START ACTIVATION TRIGGERS')
|
|
19
20
|
try {
|
|
20
|
-
|
|
21
|
+
const triggersToActivate = [...triggersList]
|
|
22
|
+
if (AUTH_CONFIG.on_user_creation_function_name) {
|
|
23
|
+
const alreadyDeclared = triggersToActivate.some(
|
|
24
|
+
(trigger) =>
|
|
25
|
+
trigger.content.type === 'AUTHENTICATION' &&
|
|
26
|
+
trigger.content.event_processors?.FUNCTION?.config?.function_name ===
|
|
27
|
+
AUTH_CONFIG.on_user_creation_function_name
|
|
28
|
+
)
|
|
29
|
+
if (!alreadyDeclared) {
|
|
30
|
+
triggersToActivate.push({
|
|
31
|
+
fileName: '__auto_on_user_creation_trigger__.json',
|
|
32
|
+
content: {
|
|
33
|
+
name: 'onUserCreation',
|
|
34
|
+
type: 'AUTHENTICATION',
|
|
35
|
+
disabled: false,
|
|
36
|
+
config: {
|
|
37
|
+
isAutoTrigger: true,
|
|
38
|
+
collection: AUTH_CONFIG.authCollection ?? 'auth_users',
|
|
39
|
+
database: DB_NAME,
|
|
40
|
+
full_document: true,
|
|
41
|
+
full_document_before_change: false,
|
|
42
|
+
match: {},
|
|
43
|
+
operation_types: ['insert', 'update', 'replace'],
|
|
44
|
+
project: {},
|
|
45
|
+
service_name: 'mongodb-atlas',
|
|
46
|
+
skip_catchup_events: false,
|
|
47
|
+
tolerate_resume_errors: false,
|
|
48
|
+
unordered: false,
|
|
49
|
+
schedule: ''
|
|
50
|
+
},
|
|
51
|
+
event_processors: {
|
|
52
|
+
FUNCTION: {
|
|
53
|
+
config: {
|
|
54
|
+
function_name: AUTH_CONFIG.on_user_creation_function_name
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
})
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
for await (const trigger of triggersToActivate) {
|
|
21
64
|
const { content } = trigger
|
|
22
65
|
const { type, config, event_processors } = content
|
|
23
66
|
|
|
@@ -110,55 +110,107 @@ const handleAuthenticationTrigger = async ({
|
|
|
110
110
|
services,
|
|
111
111
|
app
|
|
112
112
|
}: HandlerParams) => {
|
|
113
|
-
const { database } = config
|
|
113
|
+
const { database, isAutoTrigger } = config
|
|
114
|
+
const authCollection = AUTH_CONFIG.authCollection ?? 'auth_users'
|
|
115
|
+
const collection = app.mongo.client.db(database || DB_NAME).collection(authCollection)
|
|
114
116
|
const pipeline = [
|
|
115
117
|
{
|
|
116
118
|
$match: {
|
|
117
|
-
operationType: { $in: ['insert'] }
|
|
119
|
+
operationType: { $in: ['insert', 'update', 'replace'] }
|
|
118
120
|
}
|
|
119
121
|
}
|
|
120
122
|
]
|
|
121
|
-
const changeStream =
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
.watch(pipeline, {
|
|
125
|
-
fullDocument: 'whenAvailable'
|
|
126
|
-
})
|
|
123
|
+
const changeStream = collection.watch(pipeline, {
|
|
124
|
+
fullDocument: 'whenAvailable'
|
|
125
|
+
})
|
|
127
126
|
changeStream.on('error', (error) => {
|
|
128
127
|
if (shouldIgnoreStreamError(error)) return
|
|
129
128
|
console.error('Authentication trigger change stream error', error)
|
|
130
129
|
})
|
|
131
130
|
changeStream.on('change', async function (change) {
|
|
132
|
-
const
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
delete currentUser.password
|
|
142
|
-
await GenerateContext({
|
|
143
|
-
args: [{
|
|
144
|
-
user: {
|
|
145
|
-
...currentUser,
|
|
146
|
-
id: currentUser._id.toString(),
|
|
147
|
-
data: {
|
|
148
|
-
_id: currentUser._id.toString(),
|
|
149
|
-
email: currentUser.email
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
}],
|
|
153
|
-
app,
|
|
154
|
-
rules: StateManager.select("rules"),
|
|
155
|
-
user: {}, // TODO from currentUser ??
|
|
156
|
-
currentFunction: triggerHandler,
|
|
157
|
-
functionsList,
|
|
158
|
-
services,
|
|
159
|
-
runAsSystem: true
|
|
160
|
-
})
|
|
131
|
+
const operationType = change['operationType' as keyof typeof change] as string | undefined
|
|
132
|
+
const documentKey = change['documentKey' as keyof typeof change] as
|
|
133
|
+
| { _id?: unknown }
|
|
134
|
+
| undefined
|
|
135
|
+
const fullDocument = change['fullDocument' as keyof typeof change] as
|
|
136
|
+
| Record<string, unknown>
|
|
137
|
+
| null
|
|
138
|
+
if (!documentKey?._id) {
|
|
139
|
+
return
|
|
161
140
|
}
|
|
141
|
+
|
|
142
|
+
const updateDescription = change[
|
|
143
|
+
'updateDescription' as keyof typeof change
|
|
144
|
+
] as { updatedFields?: Record<string, unknown> } | undefined
|
|
145
|
+
const updatedStatus = updateDescription?.updatedFields?.status
|
|
146
|
+
let confirmedCandidate = false
|
|
147
|
+
let confirmedDocument =
|
|
148
|
+
fullDocument as Record<string, unknown> | null
|
|
149
|
+
|
|
150
|
+
if (operationType === 'update') {
|
|
151
|
+
if (updatedStatus === 'confirmed') {
|
|
152
|
+
confirmedCandidate = true
|
|
153
|
+
} else if (updatedStatus === undefined) {
|
|
154
|
+
const fetched = await collection.findOne({
|
|
155
|
+
_id: documentKey._id
|
|
156
|
+
}) as Record<string, unknown> | null
|
|
157
|
+
confirmedDocument = fetched ?? confirmedDocument
|
|
158
|
+
confirmedCandidate = (confirmedDocument as { status?: string } | null)?.status === 'confirmed'
|
|
159
|
+
}
|
|
160
|
+
} else {
|
|
161
|
+
confirmedCandidate = (confirmedDocument as { status?: string } | null)?.status === 'confirmed'
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if (!confirmedCandidate) {
|
|
165
|
+
return
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const updateResult = await collection.findOneAndUpdate(
|
|
169
|
+
{
|
|
170
|
+
_id: documentKey._id,
|
|
171
|
+
status: 'confirmed',
|
|
172
|
+
on_user_creation_triggered_at: { $exists: false }
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
$set: {
|
|
176
|
+
on_user_creation_triggered_at: new Date()
|
|
177
|
+
}
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
returnDocument: 'after'
|
|
181
|
+
}
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
const document =
|
|
185
|
+
(updateResult?.value as Record<string, unknown> | null) ?? confirmedDocument
|
|
186
|
+
if (!document) {
|
|
187
|
+
return
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
delete (document as { password?: unknown }).password
|
|
191
|
+
|
|
192
|
+
const currentUser = { ...document }
|
|
193
|
+
delete (currentUser as { password?: unknown }).password
|
|
194
|
+
|
|
195
|
+
const userData = {
|
|
196
|
+
...currentUser,
|
|
197
|
+
id: (currentUser as { _id: { toString: () => string } })._id.toString(),
|
|
198
|
+
data: {
|
|
199
|
+
_id: (currentUser as { _id: { toString: () => string } })._id.toString(),
|
|
200
|
+
email: (currentUser as { email?: string }).email
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
// TODO change va ripulito
|
|
204
|
+
await GenerateContext({
|
|
205
|
+
args: isAutoTrigger ? [userData] : [{ user: userData /*, ...change */ }],
|
|
206
|
+
app,
|
|
207
|
+
rules: StateManager.select("rules"),
|
|
208
|
+
user: {}, // TODO from currentUser ??
|
|
209
|
+
currentFunction: triggerHandler,
|
|
210
|
+
functionsList,
|
|
211
|
+
services,
|
|
212
|
+
runAsSystem: true
|
|
213
|
+
})
|
|
162
214
|
})
|
|
163
215
|
registerOnClose(
|
|
164
216
|
app,
|