@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.
Files changed (97) hide show
  1. package/README.md +37 -6
  2. package/dist/auth/controller.d.ts.map +1 -1
  3. package/dist/auth/controller.js +55 -4
  4. package/dist/auth/plugins/jwt.d.ts.map +1 -1
  5. package/dist/auth/plugins/jwt.js +52 -6
  6. package/dist/auth/providers/anon-user/controller.d.ts +8 -0
  7. package/dist/auth/providers/anon-user/controller.d.ts.map +1 -0
  8. package/dist/auth/providers/anon-user/controller.js +90 -0
  9. package/dist/auth/providers/anon-user/dtos.d.ts +10 -0
  10. package/dist/auth/providers/anon-user/dtos.d.ts.map +1 -0
  11. package/dist/auth/providers/anon-user/dtos.js +2 -0
  12. package/dist/auth/providers/custom-function/controller.d.ts.map +1 -1
  13. package/dist/auth/providers/custom-function/controller.js +35 -25
  14. package/dist/auth/providers/custom-function/dtos.d.ts +4 -1
  15. package/dist/auth/providers/custom-function/dtos.d.ts.map +1 -1
  16. package/dist/auth/providers/local-userpass/controller.d.ts.map +1 -1
  17. package/dist/auth/providers/local-userpass/controller.js +159 -73
  18. package/dist/auth/providers/local-userpass/dtos.d.ts +17 -2
  19. package/dist/auth/providers/local-userpass/dtos.d.ts.map +1 -1
  20. package/dist/auth/utils.d.ts +76 -14
  21. package/dist/auth/utils.d.ts.map +1 -1
  22. package/dist/auth/utils.js +55 -61
  23. package/dist/constants.d.ts +12 -0
  24. package/dist/constants.d.ts.map +1 -1
  25. package/dist/constants.js +16 -4
  26. package/dist/features/functions/controller.d.ts.map +1 -1
  27. package/dist/features/functions/controller.js +31 -12
  28. package/dist/features/functions/dtos.d.ts +3 -0
  29. package/dist/features/functions/dtos.d.ts.map +1 -1
  30. package/dist/features/functions/interface.d.ts +3 -0
  31. package/dist/features/functions/interface.d.ts.map +1 -1
  32. package/dist/features/functions/utils.d.ts +3 -2
  33. package/dist/features/functions/utils.d.ts.map +1 -1
  34. package/dist/features/functions/utils.js +19 -7
  35. package/dist/features/triggers/index.d.ts.map +1 -1
  36. package/dist/features/triggers/index.js +49 -7
  37. package/dist/features/triggers/interface.d.ts +1 -0
  38. package/dist/features/triggers/interface.d.ts.map +1 -1
  39. package/dist/features/triggers/utils.d.ts.map +1 -1
  40. package/dist/features/triggers/utils.js +67 -26
  41. package/dist/index.d.ts.map +1 -1
  42. package/dist/index.js +48 -13
  43. package/dist/services/mongodb-atlas/index.d.ts.map +1 -1
  44. package/dist/services/mongodb-atlas/index.js +72 -2
  45. package/dist/services/mongodb-atlas/model.d.ts +3 -2
  46. package/dist/services/mongodb-atlas/model.d.ts.map +1 -1
  47. package/dist/shared/handleUserRegistration.d.ts.map +1 -1
  48. package/dist/shared/handleUserRegistration.js +66 -1
  49. package/dist/shared/models/handleUserRegistration.model.d.ts +2 -1
  50. package/dist/shared/models/handleUserRegistration.model.d.ts.map +1 -1
  51. package/dist/shared/models/handleUserRegistration.model.js +1 -0
  52. package/dist/utils/context/helpers.d.ts +6 -6
  53. package/dist/utils/context/helpers.d.ts.map +1 -1
  54. package/dist/utils/context/index.d.ts +1 -1
  55. package/dist/utils/context/index.d.ts.map +1 -1
  56. package/dist/utils/context/index.js +176 -9
  57. package/dist/utils/context/interface.d.ts +1 -1
  58. package/dist/utils/context/interface.d.ts.map +1 -1
  59. package/dist/utils/crypto/index.d.ts +1 -0
  60. package/dist/utils/crypto/index.d.ts.map +1 -1
  61. package/dist/utils/crypto/index.js +6 -2
  62. package/dist/utils/initializer/exposeRoutes.js +1 -1
  63. package/dist/utils/initializer/registerPlugins.d.ts.map +1 -1
  64. package/dist/utils/initializer/registerPlugins.js +12 -4
  65. package/dist/utils/roles/helpers.js +2 -1
  66. package/package.json +1 -2
  67. package/src/auth/controller.ts +71 -5
  68. package/src/auth/plugins/jwt.test.ts +93 -0
  69. package/src/auth/plugins/jwt.ts +67 -8
  70. package/src/auth/providers/anon-user/controller.ts +91 -0
  71. package/src/auth/providers/anon-user/dtos.ts +10 -0
  72. package/src/auth/providers/custom-function/controller.ts +40 -31
  73. package/src/auth/providers/custom-function/dtos.ts +5 -1
  74. package/src/auth/providers/local-userpass/controller.ts +211 -101
  75. package/src/auth/providers/local-userpass/dtos.ts +20 -2
  76. package/src/auth/utils.ts +66 -83
  77. package/src/constants.ts +14 -2
  78. package/src/features/functions/controller.ts +42 -12
  79. package/src/features/functions/dtos.ts +3 -0
  80. package/src/features/functions/interface.ts +3 -0
  81. package/src/features/functions/utils.ts +29 -8
  82. package/src/features/triggers/index.ts +44 -1
  83. package/src/features/triggers/interface.ts +1 -0
  84. package/src/features/triggers/utils.ts +89 -37
  85. package/src/index.ts +49 -13
  86. package/src/services/mongodb-atlas/__tests__/findOneAndUpdate.test.ts +95 -0
  87. package/src/services/mongodb-atlas/index.ts +100 -2
  88. package/src/services/mongodb-atlas/model.ts +16 -3
  89. package/src/shared/handleUserRegistration.ts +83 -2
  90. package/src/shared/models/handleUserRegistration.model.ts +2 -1
  91. package/src/utils/__tests__/registerPlugins.test.ts +5 -1
  92. package/src/utils/context/index.ts +238 -18
  93. package/src/utils/context/interface.ts +1 -1
  94. package/src/utils/crypto/index.ts +5 -1
  95. package/src/utils/initializer/exposeRoutes.ts +1 -1
  96. package/src/utils/initializer/registerPlugins.ts +8 -0
  97. package/src/utils/roles/helpers.ts +3 -2
package/src/index.ts CHANGED
@@ -49,24 +49,34 @@ export async function initialize({
49
49
  corsConfig = DEFAULT_CONFIG.CORS_OPTIONS,
50
50
  basePath
51
51
  }: InitializeConfig) {
52
+ if (!jwtSecret || jwtSecret.trim().length === 0) {
53
+ throw new Error('JWT secret missing: set JWT_SECRET or pass jwtSecret to initialize()')
54
+ }
55
+
52
56
  const resolvedBasePath = basePath ?? require.main?.path ?? process.cwd()
53
57
  const fastify = Fastify({
54
58
  logger: !!DEFAULT_CONFIG.ENABLE_LOGGER
55
59
  })
56
60
 
57
- console.log("BASE PATH", resolvedBasePath)
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
+ }
58
67
 
59
- console.log("CURRENT PORT", port)
60
- console.log("CURRENT HOST", host)
68
+ logInfo("BASE PATH", resolvedBasePath)
69
+ logInfo("CURRENT PORT", port)
70
+ logInfo("CURRENT HOST", host)
61
71
 
62
72
  const functionsList = await loadFunctions(resolvedBasePath)
63
- console.log("Functions LOADED")
73
+ logInfo("Functions LOADED")
64
74
  const triggersList = await loadTriggers(resolvedBasePath)
65
- console.log("Triggers LOADED")
75
+ logInfo("Triggers LOADED")
66
76
  const endpointsList = await loadEndpoints(resolvedBasePath)
67
- console.log("Endpoints LOADED")
77
+ logInfo("Endpoints LOADED")
68
78
  const rulesList = await loadRules(resolvedBasePath)
69
- console.log("Rules LOADED")
79
+ logInfo("Rules LOADED")
70
80
 
71
81
  const stateConfig = {
72
82
  functions: functionsList,
@@ -90,7 +100,33 @@ export async function initialize({
90
100
  deepLinking: false
91
101
  },
92
102
  uiHooks: {
93
- onRequest: function (request, reply, next) { next() },
103
+ onRequest: function (request, reply, next) {
104
+ const swaggerUser = DEFAULT_CONFIG.SWAGGER_UI_USER
105
+ const swaggerPassword = DEFAULT_CONFIG.SWAGGER_UI_PASSWORD
106
+ if (!swaggerUser && !swaggerPassword) {
107
+ next()
108
+ return
109
+ }
110
+ const authHeader = request.headers.authorization
111
+ if (!authHeader || !authHeader.startsWith('Basic ')) {
112
+ reply
113
+ .code(401)
114
+ .header('WWW-Authenticate', 'Basic realm="Swagger UI"')
115
+ .send({ message: 'Unauthorized' })
116
+ return
117
+ }
118
+ const encoded = authHeader.slice('Basic '.length)
119
+ const decoded = Buffer.from(encoded, 'base64').toString('utf8')
120
+ const [user, pass] = decoded.split(':')
121
+ if (user !== swaggerUser || pass !== swaggerPassword) {
122
+ reply
123
+ .code(401)
124
+ .header('WWW-Authenticate', 'Basic realm="Swagger UI"')
125
+ .send({ message: 'Unauthorized' })
126
+ return
127
+ }
128
+ next()
129
+ },
94
130
  preHandler: function (request, reply, next) { next() }
95
131
  },
96
132
  staticCSP: true,
@@ -107,15 +143,15 @@ export async function initialize({
107
143
  corsConfig
108
144
  })
109
145
 
110
- console.log('Plugins registration COMPLETED')
146
+ logInfo('Plugins registration COMPLETED')
111
147
  await exposeRoutes(fastify)
112
- console.log('APP Routes registration COMPLETED')
148
+ logInfo('APP Routes registration COMPLETED')
113
149
  await registerFunctions({ app: fastify, functionsList, rulesList })
114
- console.log('Functions registration COMPLETED')
150
+ logInfo('Functions registration COMPLETED')
115
151
  await generateEndpoints({ app: fastify, functionsList, endpointsList, rulesList })
116
- console.log('HTTP Endpoints registration COMPLETED')
152
+ logInfo('HTTP Endpoints registration COMPLETED')
117
153
  fastify.ready(() => {
118
- console.log("FASTIFY IS READY")
154
+ logInfo("FASTIFY IS READY")
119
155
  if (triggersList?.length > 0) activateTriggers({ fastify, triggersList, functionsList })
120
156
  })
121
157
  await fastify.listen({ port, host })
@@ -0,0 +1,95 @@
1
+ import { Document, ObjectId } from 'mongodb'
2
+ import MongoDbAtlas from '..'
3
+ import { Role, Rules } from '../../../features/rules/interface'
4
+
5
+ const createAppWithCollection = (collection: Record<string, unknown>) => ({
6
+ mongo: {
7
+ client: {
8
+ db: jest.fn().mockReturnValue({
9
+ collection: jest.fn().mockReturnValue(collection)
10
+ })
11
+ }
12
+ }
13
+ })
14
+
15
+ const createRules = (roleOverrides: Partial<Role> = {}): Rules => ({
16
+ todos: {
17
+ database: 'db',
18
+ collection: 'todos',
19
+ filters: [],
20
+ roles: [
21
+ {
22
+ name: 'owner',
23
+ apply_when: {},
24
+ insert: true,
25
+ delete: true,
26
+ search: true,
27
+ read: true,
28
+ write: true,
29
+ ...roleOverrides
30
+ }
31
+ ]
32
+ }
33
+ })
34
+
35
+ describe('mongodb-atlas findOneAndUpdate', () => {
36
+ it('applies write/read validation and returns the updated document', async () => {
37
+ const id = new ObjectId()
38
+ const existingDoc = { _id: id, title: 'Old', userId: 'user-1' }
39
+ const updatedDoc = { _id: id, title: 'New', userId: 'user-1' }
40
+ const findOne = jest.fn().mockResolvedValue(existingDoc)
41
+ const aggregate = jest.fn().mockReturnValue({
42
+ toArray: jest.fn().mockResolvedValue([updatedDoc])
43
+ })
44
+ const findOneAndUpdate = jest.fn().mockResolvedValue(updatedDoc)
45
+ const collection = {
46
+ collectionName: 'todos',
47
+ findOne,
48
+ aggregate,
49
+ findOneAndUpdate
50
+ }
51
+
52
+ const app = createAppWithCollection(collection)
53
+ const operators = MongoDbAtlas(app as any, {
54
+ rules: createRules(),
55
+ user: { id: 'user-1' }
56
+ })
57
+ .db('db')
58
+ .collection('todos')
59
+
60
+ const result = await operators.findOneAndUpdate({ _id: id }, { $set: { title: 'New' } })
61
+
62
+ expect(findOne).toHaveBeenCalled()
63
+ expect(aggregate).toHaveBeenCalled()
64
+ expect(findOneAndUpdate).toHaveBeenCalledWith(
65
+ { $and: [{ _id: id }] },
66
+ { $set: { title: 'New' } }
67
+ )
68
+ expect(result).toEqual(updatedDoc)
69
+ })
70
+
71
+ it('rejects updates when write permission is denied', async () => {
72
+ const id = new ObjectId()
73
+ const existingDoc = { _id: id, title: 'Old', userId: 'user-1' }
74
+ const findOne = jest.fn().mockResolvedValue(existingDoc)
75
+ const findOneAndUpdate = jest.fn()
76
+ const collection = {
77
+ collectionName: 'todos',
78
+ findOne,
79
+ findOneAndUpdate
80
+ }
81
+
82
+ const app = createAppWithCollection(collection)
83
+ const operators = MongoDbAtlas(app as any, {
84
+ rules: createRules({ write: false }),
85
+ user: { id: 'user-1' }
86
+ })
87
+ .db('db')
88
+ .collection('todos')
89
+
90
+ await expect(
91
+ operators.findOneAndUpdate({ _id: id }, { title: 'Denied' } as Document)
92
+ ).rejects.toThrow('Update not permitted')
93
+ expect(findOneAndUpdate).not.toHaveBeenCalled()
94
+ })
95
+ })
@@ -1,5 +1,13 @@
1
1
  import isEqual from 'lodash/isEqual'
2
- import { Collection, Document, EventsDescription, WithId } from 'mongodb'
2
+ import {
3
+ Collection,
4
+ Document,
5
+ EventsDescription,
6
+ Filter as MongoFilter,
7
+ FindOneAndUpdateOptions,
8
+ UpdateFilter,
9
+ WithId
10
+ } from 'mongodb'
3
11
  import { checkValidation } from '../../utils/roles/machines'
4
12
  import { getWinningRole } from '../../utils/roles/machines/utils'
5
13
  import { CRUD_OPERATIONS, GetOperatorsFunction, MongodbAtlasFunction } from './model'
@@ -305,6 +313,96 @@ const getOperators: GetOperatorsFunction = (
305
313
  }
306
314
  return collection.updateOne(query, data, options)
307
315
  },
316
+ /**
317
+ * Finds and updates a single document with role-based validation and access control.
318
+ *
319
+ * @param {Filter<Document>} query - The MongoDB query used to match the document to update.
320
+ * @param {UpdateFilter<Document> | Partial<Document>} data - The update operations or replacement document.
321
+ * @param {FindOneAndUpdateOptions} [options] - Optional settings for the findOneAndUpdate operation.
322
+ * @returns {Promise<FindAndModifyResult<Document>>} The result of the findOneAndUpdate operation.
323
+ *
324
+ * @throws {Error} If the user is not authorized to update the document.
325
+ */
326
+ findOneAndUpdate: async (
327
+ query: MongoFilter<Document>,
328
+ data: UpdateFilter<Document> | Document[],
329
+ options?: FindOneAndUpdateOptions
330
+ ) => {
331
+ if (!run_as_system) {
332
+ checkDenyOperation(normalizedRules, collection.collectionName, CRUD_OPERATIONS.UPDATE)
333
+ const formattedQuery = getFormattedQuery(filters, query, user)
334
+ const safeQuery = Array.isArray(formattedQuery)
335
+ ? normalizeQuery(formattedQuery)
336
+ : formattedQuery
337
+
338
+ const result = await collection.findOne({ $and: safeQuery })
339
+
340
+ if (!result) {
341
+ throw new Error('Update not permitted')
342
+ }
343
+
344
+ const winningRole = getWinningRole(result, user, roles)
345
+ const hasOperators = Object.keys(data).some((key) => key.startsWith('$'))
346
+ const pipeline = [
347
+ {
348
+ $match: { $and: safeQuery }
349
+ },
350
+ {
351
+ $limit: 1
352
+ },
353
+ ...Object.entries(data).map(([key, value]) => ({ [key]: value }))
354
+ ]
355
+ const [docToCheck] = hasOperators
356
+ ? await collection.aggregate(pipeline).toArray()
357
+ : ([data] as [Document])
358
+
359
+ const { status, document } = winningRole
360
+ ? await checkValidation(
361
+ winningRole,
362
+ {
363
+ type: 'write',
364
+ roles,
365
+ cursor: docToCheck,
366
+ expansions: {}
367
+ },
368
+ user
369
+ )
370
+ : fallbackAccess(docToCheck)
371
+
372
+ const areDocumentsEqual = isEqual(document, docToCheck)
373
+ if (!status || !areDocumentsEqual) {
374
+ throw new Error('Update not permitted')
375
+ }
376
+
377
+ const updateResult = options
378
+ ? await collection.findOneAndUpdate({ $and: safeQuery }, data, options)
379
+ : await collection.findOneAndUpdate({ $and: safeQuery }, data)
380
+ if (!updateResult) {
381
+ return updateResult
382
+ }
383
+
384
+ const readRole = getWinningRole(updateResult, user, roles)
385
+ const readResult = readRole
386
+ ? await checkValidation(
387
+ readRole,
388
+ {
389
+ type: 'read',
390
+ roles,
391
+ cursor: updateResult,
392
+ expansions: {}
393
+ },
394
+ user
395
+ )
396
+ : fallbackAccess(updateResult)
397
+
398
+ const sanitizedDoc = readResult.status ? (readResult.document ?? updateResult) : {}
399
+ return sanitizedDoc
400
+ }
401
+
402
+ return options
403
+ ? collection.findOneAndUpdate(query, data, options)
404
+ : collection.findOneAndUpdate(query, data)
405
+ },
308
406
  /**
309
407
  * Finds documents in a MongoDB collection with optional role-based access control and post-query validation.
310
408
  *
@@ -481,7 +579,7 @@ const getOperators: GetOperatorsFunction = (
481
579
  return collection.watch(pipeline, options)
482
580
  },
483
581
  //TODO -> add filter & rules in aggregate
484
- aggregate: async (pipeline = [], options, isClient) => {
582
+ aggregate: (pipeline = [], options, isClient) => {
485
583
  if (run_as_system || !isClient) {
486
584
  return collection.aggregate(pipeline, options)
487
585
  }
@@ -1,5 +1,13 @@
1
1
  import { FastifyInstance } from 'fastify'
2
- import { Collection, Document, FindCursor, WithId } from 'mongodb'
2
+ import {
3
+ Collection,
4
+ Document,
5
+ Filter as MongoFilter,
6
+ FindCursor,
7
+ FindOneAndUpdateOptions,
8
+ UpdateFilter,
9
+ WithId
10
+ } from 'mongodb'
3
11
  import { User } from '../../auth/dtos'
4
12
  import { Filter, Rules } from '../../features/rules/interface'
5
13
  import { Role } from '../../utils/roles/interface'
@@ -50,11 +58,16 @@ export type GetOperatorsFunction = (
50
58
  updateOne: (
51
59
  ...params: Parameters<Method<'updateOne'>>
52
60
  ) => ReturnType<Method<'updateOne'>>
61
+ findOneAndUpdate: (
62
+ filter: MongoFilter<Document>,
63
+ update: UpdateFilter<Document> | Document[],
64
+ options?: FindOneAndUpdateOptions
65
+ ) => Promise<Document | null>
53
66
  find: (...params: Parameters<Method<'find'>>) => FindCursor
54
67
  watch: (...params: Parameters<Method<'watch'>>) => ReturnType<Method<'watch'>>
55
68
  aggregate: (
56
69
  ...params: [...Parameters<Method<'aggregate'>>, isClient: boolean]
57
- ) => Promise<ReturnType<Method<'aggregate'>>>
70
+ ) => ReturnType<Method<'aggregate'>>
58
71
  insertMany: (
59
72
  ...params: Parameters<Method<'insertMany'>>
60
73
  ) => ReturnType<Method<'insertMany'>>
@@ -73,4 +86,4 @@ export enum CRUD_OPERATIONS {
73
86
  UPDATE = "UPDATE",
74
87
  DELETE = "DELETE"
75
88
 
76
- }
89
+ }
@@ -1,5 +1,7 @@
1
1
  import { AUTH_CONFIG, DB_NAME } from "../constants"
2
- import { hashPassword } from "../utils/crypto"
2
+ import { StateManager } from "../state"
3
+ import { GenerateContext } from "../utils/context"
4
+ import { generateToken, hashPassword } from "../utils/crypto"
3
5
  import { HandleUserRegistration } from "./models/handleUserRegistration.model"
4
6
 
5
7
  /**
@@ -17,6 +19,10 @@ const handleUserRegistration: HandleUserRegistration = (app, opt) => async ({ em
17
19
  }
18
20
 
19
21
  const { authCollection } = AUTH_CONFIG
22
+ const localUserpassConfig = AUTH_CONFIG.localUserpassConfig
23
+ const autoConfirm = localUserpassConfig?.autoConfirm === true
24
+ const runConfirmationFunction = localUserpassConfig?.runConfirmationFunction === true
25
+ const confirmationFunctionName = localUserpassConfig?.confirmationFunctionName
20
26
  const mongo = app?.mongo
21
27
  const db = mongo.client.db(DB_NAME)
22
28
  const hashedPassword = await hashPassword(password)
@@ -29,7 +35,7 @@ const handleUserRegistration: HandleUserRegistration = (app, opt) => async ({ em
29
35
  const result = await db?.collection(authCollection!).insertOne({
30
36
  email,
31
37
  password: hashedPassword,
32
- status: skipUserCheck ? 'confirmed' : 'pending',
38
+ status: skipUserCheck || autoConfirm ? 'confirmed' : 'pending',
33
39
  createdAt: new Date(),
34
40
  custom_data: {
35
41
  // TODO: aggiungere dati personalizzati alla registrazione
@@ -58,6 +64,81 @@ const handleUserRegistration: HandleUserRegistration = (app, opt) => async ({ em
58
64
  }
59
65
  )
60
66
 
67
+ if (!result?.insertedId || skipUserCheck || autoConfirm) {
68
+ return result
69
+ }
70
+
71
+ if (!runConfirmationFunction) {
72
+ throw new Error('Missing confirmation function')
73
+ }
74
+
75
+ if (!confirmationFunctionName) {
76
+ throw new Error('Missing confirmation function name')
77
+ }
78
+
79
+ const functionsList = StateManager.select('functions')
80
+ const services = StateManager.select('services')
81
+ const confirmationFunction = functionsList[confirmationFunctionName]
82
+ if (!confirmationFunction) {
83
+ throw new Error(`Confirmation function not found: ${confirmationFunctionName}`)
84
+ }
85
+
86
+ const token = generateToken()
87
+ const tokenId = generateToken()
88
+ await db?.collection(authCollection!).updateOne(
89
+ { _id: result.insertedId },
90
+ {
91
+ $set: {
92
+ confirmationToken: token,
93
+ confirmationTokenId: tokenId
94
+ }
95
+ }
96
+ )
97
+
98
+ type ConfirmationResult = { status?: 'success' | 'pending' | 'fail' }
99
+ let confirmationStatus: ConfirmationResult['status'] = 'fail'
100
+ try {
101
+ const response = await GenerateContext({
102
+ args: [{
103
+ token,
104
+ tokenId,
105
+ username: email
106
+ }],
107
+ app,
108
+ rules: {},
109
+ user: {},
110
+ currentFunction: confirmationFunction,
111
+ functionsList,
112
+ services,
113
+ runAsSystem: true
114
+ }) as ConfirmationResult
115
+ confirmationStatus = response?.status ?? 'fail'
116
+ } catch {
117
+ confirmationStatus = 'fail'
118
+ }
119
+
120
+ if (confirmationStatus === 'success') {
121
+ await db?.collection(authCollection!).updateOne(
122
+ { _id: result.insertedId },
123
+ {
124
+ $set: { status: 'confirmed' },
125
+ $unset: { confirmationToken: '', confirmationTokenId: '' }
126
+ }
127
+ )
128
+ return result
129
+ }
130
+
131
+ if (confirmationStatus === 'pending') {
132
+ return result
133
+ }
134
+
135
+ await db?.collection(authCollection!).updateOne(
136
+ { _id: result.insertedId },
137
+ {
138
+ $set: { status: 'failed' },
139
+ $unset: { confirmationToken: '', confirmationTokenId: '' }
140
+ }
141
+ )
61
142
  return result
62
143
 
63
144
  }
@@ -28,5 +28,6 @@ export type HandleUserRegistration = (
28
28
 
29
29
  export enum PROVIDER {
30
30
  LOCAL_USERPASS = "local-userpass",
31
- CUSTOM_FUNCTION = "custom-function"
31
+ CUSTOM_FUNCTION = "custom-function",
32
+ ANON_USER = "anon-user"
32
33
  }
@@ -3,6 +3,7 @@ import fastifyMongodb from '@fastify/mongodb'
3
3
  import { authController } from '../../auth/controller'
4
4
  import jwtAuthPlugin from '../../auth/plugins/jwt'
5
5
  import fastifyRawBody from 'fastify-raw-body'
6
+ import { anonUserController } from '../../auth/providers/anon-user/controller'
6
7
  import { customFunctionController } from '../../auth/providers/custom-function/controller'
7
8
  import { localUserPassController } from '../../auth/providers/local-userpass/controller'
8
9
  import { Functions } from '../../features/functions/interface'
@@ -36,7 +37,7 @@ describe('registerPlugins', () => {
36
37
  })
37
38
 
38
39
  // Check Plugins Registration
39
- expect(registerMock).toHaveBeenCalledTimes(7)
40
+ expect(registerMock).toHaveBeenCalledTimes(8)
40
41
  expect(registerMock).toHaveBeenCalledWith(cors, {
41
42
  origin: '*',
42
43
  methods: ['POST', 'GET']
@@ -63,6 +64,9 @@ describe('registerPlugins', () => {
63
64
  expect(registerMock).toHaveBeenCalledWith(customFunctionController, {
64
65
  prefix: `${MOCKED_API_VERSION}/app/:appId/auth/providers/custom-function`
65
66
  })
67
+ expect(registerMock).toHaveBeenCalledWith(anonUserController, {
68
+ prefix: `${MOCKED_API_VERSION}/app/:appId/auth/providers/anon-user`
69
+ })
66
70
  })
67
71
 
68
72
  it('should handle errors in the catch block', async () => {