@flowerforce/flowerbase 1.2.1-beta.9 → 1.3.0
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/CHANGELOG.md +74 -0
- package/README.md +9 -3
- package/dist/auth/controller.d.ts.map +1 -1
- package/dist/auth/controller.js +2 -2
- package/dist/auth/plugins/jwt.js +3 -3
- 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 +32 -35
- 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 +41 -33
- package/dist/auth/providers/local-userpass/dtos.d.ts +6 -0
- package/dist/auth/providers/local-userpass/dtos.d.ts.map +1 -1
- package/dist/auth/utils.d.ts +24 -1
- package/dist/auth/utils.d.ts.map +1 -1
- package/dist/auth/utils.js +13 -1
- package/dist/constants.d.ts +4 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +7 -3
- package/dist/features/functions/controller.d.ts.map +1 -1
- package/dist/features/functions/controller.js +27 -11
- package/dist/features/functions/utils.d.ts +1 -1
- 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 +18 -12
- package/dist/services/mongodb-atlas/index.d.ts.map +1 -1
- package/dist/services/mongodb-atlas/index.js +2 -2
- package/dist/services/mongodb-atlas/model.d.ts +2 -2
- package/dist/services/mongodb-atlas/model.d.ts.map +1 -1
- package/dist/services/mongodb-atlas/utils.d.ts.map +1 -1
- package/dist/services/mongodb-atlas/utils.js +3 -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 +5 -5
- package/dist/utils/context/helpers.d.ts.map +1 -1
- package/dist/utils/context/index.d.ts.map +1 -1
- package/dist/utils/context/index.js +12 -14
- 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/rules-matcher/utils.d.ts.map +1 -1
- package/dist/utils/rules-matcher/utils.js +3 -0
- package/package.json +1 -1
- package/src/auth/controller.ts +5 -2
- package/src/auth/plugins/jwt.ts +5 -5
- 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 +34 -39
- package/src/auth/providers/custom-function/dtos.ts +5 -1
- package/src/auth/providers/local-userpass/controller.ts +56 -43
- package/src/auth/providers/local-userpass/dtos.ts +7 -0
- package/src/auth/utils.ts +22 -1
- package/src/constants.ts +5 -1
- package/src/features/functions/controller.ts +27 -11
- 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 +18 -12
- package/src/services/mongodb-atlas/index.ts +654 -654
- package/src/services/mongodb-atlas/model.ts +2 -2
- package/src/services/mongodb-atlas/utils.ts +3 -0
- 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 +32 -36
- package/src/utils/initializer/exposeRoutes.ts +1 -1
- package/src/utils/initializer/registerPlugins.ts +8 -0
- package/src/utils/rules-matcher/utils.ts +3 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { FastifyInstance } from 'fastify'
|
|
2
|
+
import { ObjectId } from 'mongodb'
|
|
2
3
|
import { AUTH_CONFIG, DB_NAME, DEFAULT_CONFIG } from '../../../constants'
|
|
3
|
-
import { services } from '../../../services'
|
|
4
4
|
import handleUserRegistration from '../../../shared/handleUserRegistration'
|
|
5
5
|
import { PROVIDER } from '../../../shared/models/handleUserRegistration.model'
|
|
6
6
|
import { StateManager } from '../../../state'
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
AUTH_ENDPOINTS,
|
|
11
11
|
AUTH_ERRORS,
|
|
12
12
|
CONFIRM_RESET_SCHEMA,
|
|
13
|
+
CONFIRM_USER_SCHEMA,
|
|
13
14
|
LOGIN_SCHEMA,
|
|
14
15
|
REGISTRATION_SCHEMA,
|
|
15
16
|
RESET_CALL_SCHEMA,
|
|
@@ -17,6 +18,7 @@ import {
|
|
|
17
18
|
} from '../../utils'
|
|
18
19
|
import {
|
|
19
20
|
ConfirmResetPasswordDto,
|
|
21
|
+
ConfirmUserDto,
|
|
20
22
|
LoginDto,
|
|
21
23
|
RegistrationDto,
|
|
22
24
|
ResetPasswordCallDto,
|
|
@@ -39,20 +41,14 @@ const isRateLimited = (key: string, maxAttempts: number, windowMs: number) => {
|
|
|
39
41
|
* @param {FastifyInstance} app - The Fastify instance.
|
|
40
42
|
*/
|
|
41
43
|
export async function localUserPassController(app: FastifyInstance) {
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
const {
|
|
45
|
-
authCollection,
|
|
46
|
-
userCollection,
|
|
47
|
-
user_id_field,
|
|
48
|
-
on_user_creation_function_name
|
|
49
|
-
} = AUTH_CONFIG
|
|
44
|
+
const { authCollection, userCollection, user_id_field } = AUTH_CONFIG
|
|
50
45
|
const { resetPasswordCollection } = AUTH_CONFIG
|
|
51
46
|
const { refreshTokensCollection } = AUTH_CONFIG
|
|
52
47
|
const db = app.mongo.client.db(DB_NAME)
|
|
53
48
|
const resetPasswordTtlSeconds = DEFAULT_CONFIG.RESET_PASSWORD_TTL_SECONDS
|
|
54
49
|
const rateLimitWindowMs = DEFAULT_CONFIG.AUTH_RATE_LIMIT_WINDOW_MS
|
|
55
50
|
const loginMaxAttempts = DEFAULT_CONFIG.AUTH_LOGIN_MAX_ATTEMPTS
|
|
51
|
+
const registerMaxAttempts = DEFAULT_CONFIG.AUTH_REGISTER_MAX_ATTEMPTS
|
|
56
52
|
const resetMaxAttempts = DEFAULT_CONFIG.AUTH_RESET_MAX_ATTEMPTS
|
|
57
53
|
const refreshTokenTtlMs = DEFAULT_CONFIG.REFRESH_TOKEN_TTL_DAYS * 24 * 60 * 60 * 1000
|
|
58
54
|
|
|
@@ -136,6 +132,11 @@ export async function localUserPassController(app: FastifyInstance) {
|
|
|
136
132
|
schema: REGISTRATION_SCHEMA
|
|
137
133
|
},
|
|
138
134
|
async (req, res) => {
|
|
135
|
+
const key = `register:${req.ip}`
|
|
136
|
+
if (isRateLimited(key, registerMaxAttempts, rateLimitWindowMs)) {
|
|
137
|
+
res.status(429).send({ message: 'Too many requests' })
|
|
138
|
+
return
|
|
139
|
+
}
|
|
139
140
|
|
|
140
141
|
const result = await handleUserRegistration(app, { run_as_system: true, provider: PROVIDER.LOCAL_USERPASS })({ email: req.body.email.toLowerCase(), password: req.body.password })
|
|
141
142
|
|
|
@@ -149,6 +150,50 @@ export async function localUserPassController(app: FastifyInstance) {
|
|
|
149
150
|
}
|
|
150
151
|
)
|
|
151
152
|
|
|
153
|
+
/**
|
|
154
|
+
* Endpoint for confirming a user registration.
|
|
155
|
+
*
|
|
156
|
+
* @route {POST} /confirm
|
|
157
|
+
* @param {ConfirmUserDto} req - The request object with confirmation data.
|
|
158
|
+
* @returns {Promise<Object>} A promise resolving with confirmation status.
|
|
159
|
+
*/
|
|
160
|
+
app.post<ConfirmUserDto>(
|
|
161
|
+
AUTH_ENDPOINTS.CONFIRM,
|
|
162
|
+
{
|
|
163
|
+
schema: CONFIRM_USER_SCHEMA
|
|
164
|
+
},
|
|
165
|
+
async (req, res) => {
|
|
166
|
+
const key = `confirm:${req.ip}`
|
|
167
|
+
if (isRateLimited(key, resetMaxAttempts, rateLimitWindowMs)) {
|
|
168
|
+
res.status(429).send({ message: 'Too many requests' })
|
|
169
|
+
return
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const existing = await db.collection(authCollection!).findOne({
|
|
173
|
+
confirmationToken: req.body.token,
|
|
174
|
+
confirmationTokenId: req.body.tokenId
|
|
175
|
+
}) as { _id: ObjectId; status?: string } | null
|
|
176
|
+
|
|
177
|
+
if (!existing) {
|
|
178
|
+
res.status(500)
|
|
179
|
+
throw new Error(AUTH_ERRORS.INVALID_TOKEN)
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (existing.status !== 'confirmed') {
|
|
183
|
+
await db.collection(authCollection!).updateOne(
|
|
184
|
+
{ _id: existing._id },
|
|
185
|
+
{
|
|
186
|
+
$set: { status: 'confirmed' },
|
|
187
|
+
$unset: { confirmationToken: '', confirmationTokenId: '' }
|
|
188
|
+
}
|
|
189
|
+
)
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
res.status(200)
|
|
193
|
+
return { status: 'confirmed' }
|
|
194
|
+
}
|
|
195
|
+
)
|
|
196
|
+
|
|
152
197
|
/**
|
|
153
198
|
* Endpoint for user login.
|
|
154
199
|
*
|
|
@@ -199,40 +244,8 @@ export async function localUserPassController(app: FastifyInstance) {
|
|
|
199
244
|
id: authUser._id.toString()
|
|
200
245
|
}
|
|
201
246
|
|
|
202
|
-
if (authUser && authUser.status
|
|
203
|
-
|
|
204
|
-
await db?.collection(authCollection!).updateOne(
|
|
205
|
-
{ _id: authUser._id },
|
|
206
|
-
{
|
|
207
|
-
$set: {
|
|
208
|
-
status: 'confirmed'
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
)
|
|
212
|
-
} catch (error) {
|
|
213
|
-
console.log('>>> 🚀 ~ localUserPassController ~ error:', error)
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
if (
|
|
218
|
-
authUser &&
|
|
219
|
-
authUser.status === 'pending' &&
|
|
220
|
-
on_user_creation_function_name &&
|
|
221
|
-
functionsList[on_user_creation_function_name]
|
|
222
|
-
) {
|
|
223
|
-
try {
|
|
224
|
-
await GenerateContext({
|
|
225
|
-
args: [userWithCustomData],
|
|
226
|
-
app,
|
|
227
|
-
rules: {},
|
|
228
|
-
user: userWithCustomData,
|
|
229
|
-
currentFunction: functionsList[on_user_creation_function_name],
|
|
230
|
-
functionsList,
|
|
231
|
-
services
|
|
232
|
-
})
|
|
233
|
-
} catch (error) {
|
|
234
|
-
console.log('localUserPassController - /login - GenerateContext - CATCH:', error)
|
|
235
|
-
}
|
|
247
|
+
if (authUser && authUser.status !== 'confirmed') {
|
|
248
|
+
throw new Error(AUTH_ERRORS.USER_NOT_CONFIRMED)
|
|
236
249
|
}
|
|
237
250
|
|
|
238
251
|
const refreshToken = this.createRefreshToken(userWithCustomData)
|
package/src/auth/utils.ts
CHANGED
|
@@ -64,6 +64,17 @@ export const CONFIRM_RESET_SCHEMA = {
|
|
|
64
64
|
}
|
|
65
65
|
}
|
|
66
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
|
+
|
|
67
78
|
export const RESET_SCHEMA = RESET_SEND_SCHEMA
|
|
68
79
|
|
|
69
80
|
export const REGISTRATION_SCHEMA = {
|
|
@@ -85,6 +96,7 @@ export const REGISTRATION_SCHEMA = {
|
|
|
85
96
|
export enum AUTH_ENDPOINTS {
|
|
86
97
|
LOGIN = '/login',
|
|
87
98
|
REGISTRATION = '/register',
|
|
99
|
+
CONFIRM = '/confirm',
|
|
88
100
|
PROFILE = '/profile',
|
|
89
101
|
SESSION = '/session',
|
|
90
102
|
RESET = '/reset/send',
|
|
@@ -97,7 +109,8 @@ export enum AUTH_ERRORS {
|
|
|
97
109
|
INVALID_CREDENTIALS = 'Invalid credentials',
|
|
98
110
|
INVALID_TOKEN = 'Invalid refresh token provided',
|
|
99
111
|
INVALID_RESET_PARAMS = 'Invalid token or tokenId provided',
|
|
100
|
-
MISSING_RESET_FUNCTION = 'Missing reset function'
|
|
112
|
+
MISSING_RESET_FUNCTION = 'Missing reset function',
|
|
113
|
+
USER_NOT_CONFIRMED = 'User not confirmed'
|
|
101
114
|
}
|
|
102
115
|
|
|
103
116
|
export interface AuthConfig {
|
|
@@ -105,6 +118,7 @@ export interface AuthConfig {
|
|
|
105
118
|
'api-key': ApiKey
|
|
106
119
|
'local-userpass': LocalUserpass
|
|
107
120
|
'custom-function': CustomFunction
|
|
121
|
+
'anon-user'?: AnonUser
|
|
108
122
|
}
|
|
109
123
|
|
|
110
124
|
interface ApiKey {
|
|
@@ -128,8 +142,15 @@ interface CustomFunction {
|
|
|
128
142
|
}
|
|
129
143
|
}
|
|
130
144
|
|
|
145
|
+
export interface AnonUser {
|
|
146
|
+
name: "anon-user"
|
|
147
|
+
type: "anon-user"
|
|
148
|
+
disabled: boolean
|
|
149
|
+
}
|
|
150
|
+
|
|
131
151
|
export interface Config {
|
|
132
152
|
autoConfirm: boolean
|
|
153
|
+
confirmationFunctionName?: string
|
|
133
154
|
resetFunctionName: string
|
|
134
155
|
resetPasswordUrl: string
|
|
135
156
|
runConfirmationFunction: boolean
|
package/src/constants.ts
CHANGED
|
@@ -20,8 +20,10 @@ export const DEFAULT_CONFIG = {
|
|
|
20
20
|
RESET_PASSWORD_TTL_SECONDS: Number(process.env.RESET_PASSWORD_TTL_SECONDS) || 3600,
|
|
21
21
|
AUTH_RATE_LIMIT_WINDOW_MS: Number(process.env.AUTH_RATE_LIMIT_WINDOW_MS) || 15 * 60 * 1000,
|
|
22
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,
|
|
23
24
|
AUTH_RESET_MAX_ATTEMPTS: Number(process.env.AUTH_RESET_MAX_ATTEMPTS) || 5,
|
|
24
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,
|
|
25
27
|
SWAGGER_UI_USER: process.env.SWAGGER_UI_USER || '',
|
|
26
28
|
SWAGGER_UI_PASSWORD: process.env.SWAGGER_UI_PASSWORD || '',
|
|
27
29
|
CORS_OPTIONS: {
|
|
@@ -40,10 +42,12 @@ export const AUTH_CONFIG = {
|
|
|
40
42
|
resetPasswordCollection: 'reset_password_requests',
|
|
41
43
|
refreshTokensCollection: 'auth_refresh_tokens',
|
|
42
44
|
resetPasswordConfig: configuration['local-userpass']?.config,
|
|
45
|
+
localUserpassConfig: configuration['local-userpass']?.config,
|
|
43
46
|
user_id_field,
|
|
44
47
|
on_user_creation_function_name,
|
|
45
48
|
providers: {
|
|
46
|
-
"custom-function": configuration['custom-function']?.config
|
|
49
|
+
"custom-function": configuration['custom-function']?.config,
|
|
50
|
+
"anon-user": configuration['anon-user']
|
|
47
51
|
}
|
|
48
52
|
}
|
|
49
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
|
|
@@ -105,17 +112,26 @@ export const functionsController: FunctionController = async (
|
|
|
105
112
|
}
|
|
106
113
|
|
|
107
114
|
logFunctionCall(`function:${method}`, user, args)
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
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
|
+
}
|
|
119
135
|
})
|
|
120
136
|
app.get<{
|
|
121
137
|
Querystring: FunctionCallBase64Dto
|
|
@@ -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,
|
package/src/index.ts
CHANGED
|
@@ -58,19 +58,25 @@ export async function initialize({
|
|
|
58
58
|
logger: !!DEFAULT_CONFIG.ENABLE_LOGGER
|
|
59
59
|
})
|
|
60
60
|
|
|
61
|
-
|
|
61
|
+
const isTest = process.env.NODE_ENV === 'test' || process.env.JEST_WORKER_ID !== undefined
|
|
62
|
+
const logInfo = (...args: unknown[]) => {
|
|
63
|
+
if (!isTest) {
|
|
64
|
+
console.log(...args)
|
|
65
|
+
}
|
|
66
|
+
}
|
|
62
67
|
|
|
63
|
-
|
|
64
|
-
|
|
68
|
+
logInfo("BASE PATH", resolvedBasePath)
|
|
69
|
+
logInfo("CURRENT PORT", port)
|
|
70
|
+
logInfo("CURRENT HOST", host)
|
|
65
71
|
|
|
66
72
|
const functionsList = await loadFunctions(resolvedBasePath)
|
|
67
|
-
|
|
73
|
+
logInfo("Functions LOADED")
|
|
68
74
|
const triggersList = await loadTriggers(resolvedBasePath)
|
|
69
|
-
|
|
75
|
+
logInfo("Triggers LOADED")
|
|
70
76
|
const endpointsList = await loadEndpoints(resolvedBasePath)
|
|
71
|
-
|
|
77
|
+
logInfo("Endpoints LOADED")
|
|
72
78
|
const rulesList = await loadRules(resolvedBasePath)
|
|
73
|
-
|
|
79
|
+
logInfo("Rules LOADED")
|
|
74
80
|
|
|
75
81
|
const stateConfig = {
|
|
76
82
|
functions: functionsList,
|
|
@@ -137,15 +143,15 @@ export async function initialize({
|
|
|
137
143
|
corsConfig
|
|
138
144
|
})
|
|
139
145
|
|
|
140
|
-
|
|
146
|
+
logInfo('Plugins registration COMPLETED')
|
|
141
147
|
await exposeRoutes(fastify)
|
|
142
|
-
|
|
148
|
+
logInfo('APP Routes registration COMPLETED')
|
|
143
149
|
await registerFunctions({ app: fastify, functionsList, rulesList })
|
|
144
|
-
|
|
150
|
+
logInfo('Functions registration COMPLETED')
|
|
145
151
|
await generateEndpoints({ app: fastify, functionsList, endpointsList, rulesList })
|
|
146
|
-
|
|
152
|
+
logInfo('HTTP Endpoints registration COMPLETED')
|
|
147
153
|
fastify.ready(() => {
|
|
148
|
-
|
|
154
|
+
logInfo("FASTIFY IS READY")
|
|
149
155
|
if (triggersList?.length > 0) activateTriggers({ fastify, triggersList, functionsList })
|
|
150
156
|
})
|
|
151
157
|
await fastify.listen({ port, host })
|