@flowerforce/flowerbase 1.7.6-beta.0 → 1.7.6-beta.10
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 +125 -1
- package/dist/auth/providers/anon-user/controller.d.ts.map +1 -1
- package/dist/auth/providers/anon-user/controller.js +1 -0
- package/dist/auth/providers/custom-function/controller.d.ts.map +1 -1
- package/dist/auth/providers/custom-function/controller.js +6 -9
- package/dist/auth/providers/local-userpass/controller.d.ts.map +1 -1
- package/dist/auth/providers/local-userpass/controller.js +58 -18
- package/dist/auth/providers/local-userpass/dtos.d.ts +5 -1
- package/dist/auth/providers/local-userpass/dtos.d.ts.map +1 -1
- package/dist/auth/utils.d.ts +1 -0
- package/dist/auth/utils.d.ts.map +1 -1
- package/dist/auth/utils.js +1 -0
- package/dist/constants.d.ts +10 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +11 -1
- package/dist/features/encryption/interface.d.ts +36 -0
- package/dist/features/encryption/interface.d.ts.map +1 -0
- package/dist/features/encryption/interface.js +2 -0
- package/dist/features/encryption/utils.d.ts +9 -0
- package/dist/features/encryption/utils.d.ts.map +1 -0
- package/dist/features/encryption/utils.js +34 -0
- package/dist/features/endpoints/utils.d.ts.map +1 -1
- package/dist/features/endpoints/utils.js +3 -0
- package/dist/features/functions/controller.d.ts +2 -0
- package/dist/features/functions/controller.d.ts.map +1 -1
- package/dist/features/functions/controller.js +7 -1
- package/dist/features/rules/interface.d.ts +6 -5
- package/dist/features/rules/interface.d.ts.map +1 -1
- package/dist/features/rules/utils.d.ts.map +1 -1
- package/dist/features/rules/utils.js +1 -11
- package/dist/features/triggers/index.d.ts.map +1 -1
- package/dist/features/triggers/index.js +4 -0
- package/dist/features/triggers/interface.d.ts +1 -1
- 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 +85 -33
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -4
- package/dist/monitoring/plugin.d.ts.map +1 -1
- package/dist/monitoring/plugin.js +31 -0
- package/dist/services/mongodb-atlas/index.d.ts +3 -0
- package/dist/services/mongodb-atlas/index.d.ts.map +1 -1
- package/dist/services/mongodb-atlas/index.js +136 -43
- package/dist/services/mongodb-atlas/model.d.ts +2 -1
- package/dist/services/mongodb-atlas/model.d.ts.map +1 -1
- package/dist/utils/context/helpers.d.ts.map +1 -1
- package/dist/utils/context/helpers.js +3 -2
- package/dist/utils/context/index.d.ts.map +1 -1
- package/dist/utils/context/index.js +4 -2
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +14 -3
- package/dist/utils/initializer/mongodbCSFLE.d.ts +69 -0
- package/dist/utils/initializer/mongodbCSFLE.d.ts.map +1 -0
- package/dist/utils/initializer/mongodbCSFLE.js +131 -0
- package/dist/utils/initializer/registerPlugins.d.ts +5 -1
- package/dist/utils/initializer/registerPlugins.d.ts.map +1 -1
- package/dist/utils/initializer/registerPlugins.js +27 -5
- package/dist/utils/roles/helpers.d.ts.map +1 -1
- package/dist/utils/roles/helpers.js +6 -3
- package/dist/utils/roles/machines/fieldPermissions.d.ts.map +1 -1
- package/dist/utils/roles/machines/fieldPermissions.js +19 -10
- package/dist/utils/rules-matcher/interface.d.ts +2 -0
- package/dist/utils/rules-matcher/interface.d.ts.map +1 -1
- package/dist/utils/rules-matcher/interface.js +1 -0
- package/dist/utils/rules-matcher/utils.d.ts.map +1 -1
- package/dist/utils/rules-matcher/utils.js +23 -6
- package/package.json +4 -2
- package/src/auth/providers/anon-user/controller.ts +1 -0
- package/src/auth/providers/custom-function/controller.ts +10 -11
- package/src/auth/providers/local-userpass/__tests__/controller.test.ts +200 -0
- package/src/auth/providers/local-userpass/controller.ts +87 -34
- package/src/auth/providers/local-userpass/dtos.ts +6 -1
- package/src/auth/utils.ts +1 -0
- package/src/constants.ts +11 -2
- package/src/features/encryption/interface.ts +46 -0
- package/src/features/encryption/utils.ts +22 -0
- package/src/features/endpoints/__tests__/utils.test.ts +65 -0
- package/src/features/endpoints/utils.ts +3 -0
- package/src/features/functions/__tests__/watch-filter.test.ts +11 -1
- package/src/features/functions/controller.ts +8 -0
- package/src/features/rules/interface.ts +18 -17
- package/src/features/rules/utils.ts +1 -11
- package/src/features/triggers/__tests__/index.test.ts +6 -4
- package/src/features/triggers/index.ts +5 -1
- package/src/features/triggers/interface.ts +1 -1
- package/src/features/triggers/utils.ts +86 -37
- package/src/index.ts +10 -2
- package/src/monitoring/plugin.ts +33 -0
- package/src/monitoring/ui.collections.js +7 -10
- package/src/monitoring/ui.css +378 -0
- package/src/monitoring/ui.endpoints.js +5 -10
- package/src/monitoring/ui.events.js +3 -5
- package/src/monitoring/ui.functions.js +64 -71
- package/src/monitoring/ui.html +8 -0
- package/src/monitoring/ui.js +189 -0
- package/src/monitoring/ui.shared.js +237 -2
- package/src/monitoring/ui.triggers.js +2 -3
- package/src/monitoring/ui.users.js +5 -9
- package/src/services/mongodb-atlas/__tests__/realmCompatibility.test.ts +205 -7
- package/src/services/mongodb-atlas/__tests__/utils.test.ts +27 -0
- package/src/services/mongodb-atlas/__tests__/watch-filter.test.ts +78 -0
- package/src/services/mongodb-atlas/index.ts +379 -182
- package/src/services/mongodb-atlas/model.ts +3 -1
- package/src/types/fastify-raw-body.d.ts +0 -9
- package/src/utils/__tests__/checkIsValidFieldNameFn.test.ts +74 -5
- package/src/utils/__tests__/contextExecuteCompatibility.test.ts +27 -1
- package/src/utils/__tests__/evaluateExpression.test.ts +33 -0
- package/src/utils/__tests__/generateContextData.test.ts +5 -1
- package/src/utils/__tests__/mongodbCSFLE.test.ts +105 -0
- package/src/utils/__tests__/rule.test.ts +38 -0
- package/src/utils/context/helpers.ts +3 -2
- package/src/utils/context/index.ts +4 -3
- package/src/utils/index.ts +12 -1
- package/src/utils/initializer/mongodbCSFLE.ts +224 -0
- package/src/utils/initializer/registerPlugins.ts +45 -10
- package/src/utils/roles/helpers.ts +10 -5
- package/src/utils/roles/machines/fieldPermissions.ts +17 -8
- package/src/utils/rules-matcher/interface.ts +2 -0
- package/src/utils/rules-matcher/utils.ts +33 -17
- package/src/utils/__tests__/readFileContent.test.ts +0 -35
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { Document } from 'mongodb'
|
|
2
|
+
export type PermissionExpression = boolean | Record<string, unknown>
|
|
3
|
+
|
|
2
4
|
export interface Filter {
|
|
3
5
|
name: string
|
|
4
6
|
query: Record<string, unknown>
|
|
@@ -9,11 +11,11 @@ export type Projection = Record<string, 0 | 1>
|
|
|
9
11
|
export interface Role {
|
|
10
12
|
name: string
|
|
11
13
|
apply_when: Record<string, unknown>
|
|
12
|
-
insert:
|
|
13
|
-
delete:
|
|
14
|
-
search:
|
|
15
|
-
read:
|
|
16
|
-
write:
|
|
14
|
+
insert: PermissionExpression
|
|
15
|
+
delete: PermissionExpression
|
|
16
|
+
search: PermissionExpression
|
|
17
|
+
read: PermissionExpression
|
|
18
|
+
write: PermissionExpression
|
|
17
19
|
}
|
|
18
20
|
|
|
19
21
|
export interface RulesConfig {
|
|
@@ -21,7 +23,6 @@ export interface RulesConfig {
|
|
|
21
23
|
collection: string
|
|
22
24
|
filters: Filter[]
|
|
23
25
|
roles: Role[]
|
|
24
|
-
|
|
25
26
|
}
|
|
26
27
|
|
|
27
28
|
export type Rules = Record<string, RulesConfig>
|
|
@@ -38,21 +39,21 @@ export type AggregationPipelineStage =
|
|
|
38
39
|
| { $unionWith: UnionWithStage }
|
|
39
40
|
|
|
40
41
|
export interface LookupStage {
|
|
41
|
-
from: string
|
|
42
|
-
localField?: string
|
|
43
|
-
foreignField?: string
|
|
44
|
-
as: string
|
|
45
|
-
let?: Record<string, unknown
|
|
46
|
-
pipeline?: AggregationPipelineStage[]
|
|
42
|
+
from: string
|
|
43
|
+
localField?: string
|
|
44
|
+
foreignField?: string
|
|
45
|
+
as: string
|
|
46
|
+
let?: Record<string, unknown>
|
|
47
|
+
pipeline?: AggregationPipelineStage[]
|
|
47
48
|
}
|
|
48
49
|
|
|
49
50
|
export type AggregationPipeline = Document[]
|
|
50
51
|
|
|
51
52
|
export type UnionWithStage = string | UnionWithNestedStage
|
|
52
|
-
type UnionWithNestedStage = { coll: string
|
|
53
|
+
type UnionWithNestedStage = { coll: string; pipeline: AggregationPipelineStage[] }
|
|
53
54
|
|
|
54
55
|
export enum STAGES_TO_SEARCH {
|
|
55
|
-
LOOKUP =
|
|
56
|
-
UNION_WITH =
|
|
57
|
-
FACET =
|
|
58
|
-
}
|
|
56
|
+
LOOKUP = '$lookup',
|
|
57
|
+
UNION_WITH = '$unionWith',
|
|
58
|
+
FACET = '$facet'
|
|
59
|
+
}
|
|
@@ -1,19 +1,9 @@
|
|
|
1
|
-
import fs from 'fs'
|
|
2
1
|
import path from 'node:path'
|
|
3
|
-
import { readJsonContent } from '../../utils'
|
|
2
|
+
import { readJsonContent, recursivelyCollectFiles } from '../../utils'
|
|
4
3
|
import { Rules, RulesConfig } from './interface'
|
|
5
4
|
|
|
6
5
|
export const loadRules = async (rootDir = process.cwd()): Promise<Rules> => {
|
|
7
6
|
const rulesRoot = path.join(rootDir, 'data_sources', 'mongodb-atlas')
|
|
8
|
-
const recursivelyCollectFiles = (dir: string): string[] => {
|
|
9
|
-
return fs.readdirSync(dir, { withFileTypes: true }).flatMap((entry) => {
|
|
10
|
-
const fullPath = path.join(dir, entry.name)
|
|
11
|
-
if (entry.isDirectory()) {
|
|
12
|
-
return recursivelyCollectFiles(fullPath)
|
|
13
|
-
}
|
|
14
|
-
return entry.isFile() ? [fullPath] : []
|
|
15
|
-
})
|
|
16
|
-
}
|
|
17
7
|
const files = recursivelyCollectFiles(rulesRoot)
|
|
18
8
|
const rulesFiles = files.filter((x) => (x as string).endsWith('rules.json'))
|
|
19
9
|
|
|
@@ -9,9 +9,9 @@ jest.mock('../../../constants', () => ({
|
|
|
9
9
|
|
|
10
10
|
jest.mock('../utils', () => ({
|
|
11
11
|
TRIGGER_HANDLERS: {
|
|
12
|
-
SCHEDULED: jest.fn(async () => {}),
|
|
13
|
-
DATABASE: jest.fn(async () => {}),
|
|
14
|
-
AUTHENTICATION: jest.fn(async () => {})
|
|
12
|
+
SCHEDULED: jest.fn(async () => { }),
|
|
13
|
+
DATABASE: jest.fn(async () => { }),
|
|
14
|
+
AUTHENTICATION: jest.fn(async () => { })
|
|
15
15
|
}
|
|
16
16
|
}))
|
|
17
17
|
|
|
@@ -27,7 +27,9 @@ describe('activateTriggers', () => {
|
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
await activateTriggers({
|
|
30
|
-
fastify: {
|
|
30
|
+
fastify: {
|
|
31
|
+
mongo: {}
|
|
32
|
+
} as any,
|
|
31
33
|
functionsList,
|
|
32
34
|
triggersList: [
|
|
33
35
|
{
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AUTH_CONFIG, AUTH_DB_NAME } from '../../constants'
|
|
1
|
+
import { AUTH_CONFIG, AUTH_DB_NAME, CHANGESTREAM } from '../../constants'
|
|
2
2
|
import { services } from '../../services'
|
|
3
3
|
import { Function, Functions } from '../functions/interface'
|
|
4
4
|
import { ActivateTriggersParams } from './dtos'
|
|
@@ -18,6 +18,10 @@ export const activateTriggers = async ({
|
|
|
18
18
|
}: ActivateTriggersParams) => {
|
|
19
19
|
console.log('START ACTIVATION TRIGGERS')
|
|
20
20
|
try {
|
|
21
|
+
// Ensure the changestream MongoDB client exist, or use the main client
|
|
22
|
+
if (!fastify.mongo[CHANGESTREAM]) {
|
|
23
|
+
fastify.mongo[CHANGESTREAM] = fastify.mongo
|
|
24
|
+
}
|
|
21
25
|
const triggersToActivate = [...triggersList]
|
|
22
26
|
if (AUTH_CONFIG.on_user_creation_function_name) {
|
|
23
27
|
const alreadyDeclared = triggersToActivate.some(
|
|
@@ -24,7 +24,7 @@ type Config = {
|
|
|
24
24
|
isAutoTrigger?: boolean
|
|
25
25
|
match: Record<string, unknown>
|
|
26
26
|
operation_types: string[]
|
|
27
|
-
operation_type?: 'CREATE' | 'DELETE' | 'LOGOUT'
|
|
27
|
+
operation_type?: 'CREATE' | 'DELETE' | 'LOGIN' | 'LOGOUT'
|
|
28
28
|
providers?: string[]
|
|
29
29
|
project: Record<string, unknown>
|
|
30
30
|
service_name: string
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import fs from 'fs'
|
|
2
2
|
import path from 'node:path'
|
|
3
3
|
import cron from 'node-cron'
|
|
4
|
-
import { AUTH_CONFIG, AUTH_DB_NAME, DB_NAME } from '../../constants'
|
|
4
|
+
import { AUTH_CONFIG, AUTH_DB_NAME, DB_NAME, CHANGESTREAM } from '../../constants'
|
|
5
5
|
import { createEventId, sanitize } from '../../monitoring/utils'
|
|
6
6
|
import { StateManager } from '../../state'
|
|
7
7
|
import { readJsonContent } from '../../utils'
|
|
@@ -172,6 +172,7 @@ const handleCronTrigger = async ({
|
|
|
172
172
|
const mapOpInverse = {
|
|
173
173
|
CREATE: ['insert', 'update', 'replace'],
|
|
174
174
|
DELETE: ['delete'],
|
|
175
|
+
LOGIN: ['insert', 'update'],
|
|
175
176
|
LOGOUT: ['update'],
|
|
176
177
|
}
|
|
177
178
|
|
|
@@ -246,7 +247,7 @@ const handleAuthenticationTrigger = async ({
|
|
|
246
247
|
const { database, isAutoTrigger, operation_types = [], operation_type } = config
|
|
247
248
|
const providerFilter = normalizeProviders(config.providers ?? [])
|
|
248
249
|
const authCollection = AUTH_CONFIG.authCollection ?? 'auth_users'
|
|
249
|
-
const collection = app.mongo.client.db(database || AUTH_DB_NAME).collection(authCollection)
|
|
250
|
+
const collection = app.mongo[CHANGESTREAM].client.db(database || AUTH_DB_NAME).collection(authCollection)
|
|
250
251
|
const operationCandidates = operation_type ? mapOpInverse[operation_type] : operation_types
|
|
251
252
|
const normalizedOps = normalizeOperationTypes(operationCandidates)
|
|
252
253
|
const baseMeta = {
|
|
@@ -277,17 +278,6 @@ const handleAuthenticationTrigger = async ({
|
|
|
277
278
|
changeStream.on('error', (error) => {
|
|
278
279
|
if (shouldIgnoreStreamError(error)) return
|
|
279
280
|
console.error('Authentication trigger change stream error', error)
|
|
280
|
-
emitTriggerEvent({
|
|
281
|
-
status: 'error',
|
|
282
|
-
triggerName,
|
|
283
|
-
triggerType,
|
|
284
|
-
functionName,
|
|
285
|
-
meta: {
|
|
286
|
-
...baseMeta,
|
|
287
|
-
event: 'CHANGE_STREAM'
|
|
288
|
-
},
|
|
289
|
-
error
|
|
290
|
-
})
|
|
291
281
|
})
|
|
292
282
|
changeStream.on('change', async function (change) {
|
|
293
283
|
const operationType = change['operationType' as keyof typeof change] as
|
|
@@ -317,6 +307,8 @@ const handleAuthenticationTrigger = async ({
|
|
|
317
307
|
const isUpdate = operationType === 'update'
|
|
318
308
|
const isReplace = operationType === 'replace'
|
|
319
309
|
const isDelete = operationType === 'delete'
|
|
310
|
+
const isLoginInsert = isInsert && !!(fullDocument as Record<string, unknown> | null)?.lastLoginAt
|
|
311
|
+
const isLoginUpdate = isUpdate && !!updatedFields && 'lastLoginAt' in updatedFields
|
|
320
312
|
const isLogoutUpdate = isUpdate && !!updatedFields && 'lastLogoutAt' in updatedFields
|
|
321
313
|
|
|
322
314
|
let confirmedCandidate = false
|
|
@@ -376,6 +368,13 @@ const handleAuthenticationTrigger = async ({
|
|
|
376
368
|
updateDescription
|
|
377
369
|
}
|
|
378
370
|
try {
|
|
371
|
+
emitTriggerEvent({
|
|
372
|
+
status: 'fired',
|
|
373
|
+
triggerName,
|
|
374
|
+
triggerType,
|
|
375
|
+
functionName,
|
|
376
|
+
meta: { ...baseMeta, event: 'LOGOUT' }
|
|
377
|
+
})
|
|
379
378
|
await GenerateContext({
|
|
380
379
|
args: [{ user: userData, ...op }],
|
|
381
380
|
app,
|
|
@@ -387,12 +386,62 @@ const handleAuthenticationTrigger = async ({
|
|
|
387
386
|
services,
|
|
388
387
|
runAsSystem: true
|
|
389
388
|
})
|
|
389
|
+
} catch (error) {
|
|
390
|
+
emitTriggerEvent({
|
|
391
|
+
status: 'error',
|
|
392
|
+
triggerName,
|
|
393
|
+
triggerType,
|
|
394
|
+
functionName,
|
|
395
|
+
meta: { ...baseMeta, event: 'LOGOUT' },
|
|
396
|
+
error
|
|
397
|
+
})
|
|
398
|
+
console.log("🚀 ~ handleAuthenticationTrigger ~ error:", error)
|
|
399
|
+
}
|
|
400
|
+
return
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
if (operation_type === 'LOGIN') {
|
|
404
|
+
if (!isLoginInsert && !isLoginUpdate) {
|
|
405
|
+
return
|
|
406
|
+
}
|
|
407
|
+
let loginDocument = fullDocument ?? confirmedDocument
|
|
408
|
+
if (!loginDocument && documentKey?._id) {
|
|
409
|
+
loginDocument = await collection.findOne({
|
|
410
|
+
_id: documentKey._id
|
|
411
|
+
}) as Record<string, unknown> | null
|
|
412
|
+
}
|
|
413
|
+
if (!matchesProviderFilter(loginDocument, providerFilter)) {
|
|
414
|
+
return
|
|
415
|
+
}
|
|
416
|
+
const userData = buildUserData(loginDocument)
|
|
417
|
+
if (!userData) {
|
|
418
|
+
return
|
|
419
|
+
}
|
|
420
|
+
const op = {
|
|
421
|
+
operationType: 'LOGIN',
|
|
422
|
+
fullDocument,
|
|
423
|
+
fullDocumentBeforeChange,
|
|
424
|
+
documentKey,
|
|
425
|
+
updateDescription
|
|
426
|
+
}
|
|
427
|
+
try {
|
|
390
428
|
emitTriggerEvent({
|
|
391
429
|
status: 'fired',
|
|
392
430
|
triggerName,
|
|
393
431
|
triggerType,
|
|
394
432
|
functionName,
|
|
395
|
-
meta: { ...baseMeta, event: '
|
|
433
|
+
meta: { ...baseMeta, event: 'LOGIN' }
|
|
434
|
+
})
|
|
435
|
+
await GenerateContext({
|
|
436
|
+
args: [{ user: userData, ...op }],
|
|
437
|
+
app,
|
|
438
|
+
rules: StateManager.select("rules"),
|
|
439
|
+
user: {}, // TODO from currentUser ??
|
|
440
|
+
currentFunction: triggerHandler,
|
|
441
|
+
functionName,
|
|
442
|
+
functionsList,
|
|
443
|
+
services,
|
|
444
|
+
runAsSystem: true
|
|
396
445
|
})
|
|
397
446
|
} catch (error) {
|
|
398
447
|
emitTriggerEvent({
|
|
@@ -400,7 +449,7 @@ const handleAuthenticationTrigger = async ({
|
|
|
400
449
|
triggerName,
|
|
401
450
|
triggerType,
|
|
402
451
|
functionName,
|
|
403
|
-
meta: { ...baseMeta, event: '
|
|
452
|
+
meta: { ...baseMeta, event: 'LOGIN' },
|
|
404
453
|
error
|
|
405
454
|
})
|
|
406
455
|
console.log("🚀 ~ handleAuthenticationTrigger ~ error:", error)
|
|
@@ -428,6 +477,13 @@ const handleAuthenticationTrigger = async ({
|
|
|
428
477
|
updateDescription
|
|
429
478
|
}
|
|
430
479
|
try {
|
|
480
|
+
emitTriggerEvent({
|
|
481
|
+
status: 'fired',
|
|
482
|
+
triggerName,
|
|
483
|
+
triggerType,
|
|
484
|
+
functionName,
|
|
485
|
+
meta: { ...baseMeta, event: 'DELETE' }
|
|
486
|
+
})
|
|
431
487
|
await GenerateContext({
|
|
432
488
|
args: isAutoTrigger ? [userData] : [{ user: userData, ...op }],
|
|
433
489
|
app,
|
|
@@ -439,13 +495,6 @@ const handleAuthenticationTrigger = async ({
|
|
|
439
495
|
services,
|
|
440
496
|
runAsSystem: true
|
|
441
497
|
})
|
|
442
|
-
emitTriggerEvent({
|
|
443
|
-
status: 'fired',
|
|
444
|
-
triggerName,
|
|
445
|
-
triggerType,
|
|
446
|
-
functionName,
|
|
447
|
-
meta: { ...baseMeta, event: 'DELETE' }
|
|
448
|
-
})
|
|
449
498
|
} catch (error) {
|
|
450
499
|
emitTriggerEvent({
|
|
451
500
|
status: 'error',
|
|
@@ -482,6 +531,13 @@ const handleAuthenticationTrigger = async ({
|
|
|
482
531
|
updateDescription
|
|
483
532
|
}
|
|
484
533
|
try {
|
|
534
|
+
emitTriggerEvent({
|
|
535
|
+
status: 'fired',
|
|
536
|
+
triggerName,
|
|
537
|
+
triggerType,
|
|
538
|
+
functionName,
|
|
539
|
+
meta: { ...baseMeta, event: 'UPDATE' }
|
|
540
|
+
})
|
|
485
541
|
await GenerateContext({
|
|
486
542
|
args: isAutoTrigger ? [userData] : [{ user: userData, ...op }],
|
|
487
543
|
app,
|
|
@@ -493,13 +549,6 @@ const handleAuthenticationTrigger = async ({
|
|
|
493
549
|
services,
|
|
494
550
|
runAsSystem: true
|
|
495
551
|
})
|
|
496
|
-
emitTriggerEvent({
|
|
497
|
-
status: 'fired',
|
|
498
|
-
triggerName,
|
|
499
|
-
triggerType,
|
|
500
|
-
functionName,
|
|
501
|
-
meta: { ...baseMeta, event: 'UPDATE' }
|
|
502
|
-
})
|
|
503
552
|
} catch (error) {
|
|
504
553
|
emitTriggerEvent({
|
|
505
554
|
status: 'error',
|
|
@@ -586,6 +635,13 @@ const handleAuthenticationTrigger = async ({
|
|
|
586
635
|
}
|
|
587
636
|
|
|
588
637
|
try {
|
|
638
|
+
emitTriggerEvent({
|
|
639
|
+
status: 'fired',
|
|
640
|
+
triggerName,
|
|
641
|
+
triggerType,
|
|
642
|
+
functionName,
|
|
643
|
+
meta: { ...baseMeta, event: 'CREATE' }
|
|
644
|
+
})
|
|
589
645
|
await GenerateContext({
|
|
590
646
|
args: isAutoTrigger ? [userData] : [{ user: userData, ...op }],
|
|
591
647
|
app,
|
|
@@ -597,13 +653,6 @@ const handleAuthenticationTrigger = async ({
|
|
|
597
653
|
services,
|
|
598
654
|
runAsSystem: true
|
|
599
655
|
})
|
|
600
|
-
emitTriggerEvent({
|
|
601
|
-
status: 'fired',
|
|
602
|
-
triggerName,
|
|
603
|
-
triggerType,
|
|
604
|
-
functionName,
|
|
605
|
-
meta: { ...baseMeta, event: 'CREATE' }
|
|
606
|
-
})
|
|
607
656
|
} catch (error) {
|
|
608
657
|
emitTriggerEvent({
|
|
609
658
|
status: 'error',
|
|
@@ -663,7 +712,7 @@ const handleDataBaseTrigger = async ({
|
|
|
663
712
|
|
|
664
713
|
const normalizedOperations = normalizeOperationTypes(operation_types)
|
|
665
714
|
|
|
666
|
-
const collection = app.mongo.client.db(database).collection(collectionName)
|
|
715
|
+
const collection = app.mongo[CHANGESTREAM].client.db(database).collection(collectionName)
|
|
667
716
|
const pipeline = [
|
|
668
717
|
{
|
|
669
718
|
$match: {
|
package/src/index.ts
CHANGED
|
@@ -6,12 +6,14 @@ import { loadEndpoints } from './features/endpoints/utils'
|
|
|
6
6
|
import { registerFunctions } from './features/functions'
|
|
7
7
|
import { loadFunctions } from './features/functions/utils'
|
|
8
8
|
import { loadRules } from './features/rules/utils'
|
|
9
|
+
import { loadEncryptionSchemas } from './features/encryption/utils'
|
|
9
10
|
import { activateTriggers } from './features/triggers'
|
|
10
11
|
import { loadTriggers } from './features/triggers/utils'
|
|
11
12
|
import { services } from './services'
|
|
12
13
|
import { StateManager } from './state'
|
|
13
14
|
import { exposeRoutes } from './utils/initializer/exposeRoutes'
|
|
14
15
|
import { registerPlugins } from './utils/initializer/registerPlugins'
|
|
16
|
+
import { type MongoDbEncryptionConfig } from './utils/initializer/mongodbCSFLE'
|
|
15
17
|
export * from './model'
|
|
16
18
|
|
|
17
19
|
|
|
@@ -30,6 +32,7 @@ export type InitializeConfig = {
|
|
|
30
32
|
host?: string
|
|
31
33
|
corsConfig?: CorsConfig
|
|
32
34
|
basePath?: string
|
|
35
|
+
mongodbEncryptionConfig?: MongoDbEncryptionConfig
|
|
33
36
|
}
|
|
34
37
|
|
|
35
38
|
/**
|
|
@@ -47,7 +50,8 @@ export async function initialize({
|
|
|
47
50
|
port = DEFAULT_CONFIG.PORT,
|
|
48
51
|
mongodbUrl = DEFAULT_CONFIG.MONGODB_URL,
|
|
49
52
|
corsConfig = DEFAULT_CONFIG.CORS_OPTIONS,
|
|
50
|
-
basePath
|
|
53
|
+
basePath,
|
|
54
|
+
mongodbEncryptionConfig
|
|
51
55
|
}: InitializeConfig) {
|
|
52
56
|
if (!jwtSecret || jwtSecret.trim().length === 0) {
|
|
53
57
|
throw new Error('JWT secret missing: set JWT_SECRET or pass jwtSecret to initialize()')
|
|
@@ -77,6 +81,8 @@ export async function initialize({
|
|
|
77
81
|
logInfo("Endpoints LOADED")
|
|
78
82
|
const rulesList = await loadRules(resolvedBasePath)
|
|
79
83
|
logInfo("Rules LOADED")
|
|
84
|
+
const encryptionSchemas = await loadEncryptionSchemas(resolvedBasePath)
|
|
85
|
+
logInfo("Encryption schemas LOADED")
|
|
80
86
|
|
|
81
87
|
const stateConfig = {
|
|
82
88
|
functions: functionsList,
|
|
@@ -152,7 +158,9 @@ export async function initialize({
|
|
|
152
158
|
mongodbUrl,
|
|
153
159
|
jwtSecret,
|
|
154
160
|
functionsList,
|
|
155
|
-
corsConfig
|
|
161
|
+
corsConfig,
|
|
162
|
+
encryptionSchemas,
|
|
163
|
+
mongodbEncryptionConfig
|
|
156
164
|
})
|
|
157
165
|
|
|
158
166
|
logInfo('Plugins registration COMPLETED')
|
package/src/monitoring/plugin.ts
CHANGED
|
@@ -2,6 +2,7 @@ import fastifyWebsocket from '@fastify/websocket'
|
|
|
2
2
|
import type { FastifyInstance, FastifyReply, FastifyRequest } from 'fastify'
|
|
3
3
|
import fp from 'fastify-plugin'
|
|
4
4
|
import '@fastify/websocket'
|
|
5
|
+
import fs from 'fs'
|
|
5
6
|
import { DEFAULT_CONFIG } from '../constants'
|
|
6
7
|
import { StateManager } from '../state'
|
|
7
8
|
import { registerCollectionRoutes } from './routes/collections'
|
|
@@ -316,6 +317,38 @@ const createMonitoringPlugin = fp(async (
|
|
|
316
317
|
})
|
|
317
318
|
})
|
|
318
319
|
|
|
320
|
+
const resolveCodeMirrorAsset = (internalPath: string) => {
|
|
321
|
+
try {
|
|
322
|
+
return require.resolve(`codemirror/${internalPath}`)
|
|
323
|
+
} catch {
|
|
324
|
+
return ''
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
const codemirrorAssets: Record<string, string> = {
|
|
329
|
+
'codemirror.js': resolveCodeMirrorAsset('lib/codemirror.js'),
|
|
330
|
+
'codemirror.css': resolveCodeMirrorAsset('lib/codemirror.css'),
|
|
331
|
+
'javascript.js': resolveCodeMirrorAsset('mode/javascript/javascript.js'),
|
|
332
|
+
'foldcode.js': resolveCodeMirrorAsset('addon/fold/foldcode.js'),
|
|
333
|
+
'foldgutter.js': resolveCodeMirrorAsset('addon/fold/foldgutter.js'),
|
|
334
|
+
'brace-fold.js': resolveCodeMirrorAsset('addon/fold/brace-fold.js'),
|
|
335
|
+
'comment-fold.js': resolveCodeMirrorAsset('addon/fold/comment-fold.js'),
|
|
336
|
+
'foldgutter.css': resolveCodeMirrorAsset('addon/fold/foldgutter.css')
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
Object.entries(codemirrorAssets).forEach(([assetName, relativePath]) => {
|
|
340
|
+
app.get(`${prefix}/vendor/codemirror/${assetName}`, async (_req, reply) => {
|
|
341
|
+
const assetPath = relativePath || ''
|
|
342
|
+
if (!assetPath || !fs.existsSync(assetPath)) {
|
|
343
|
+
reply.code(404).send(`${assetName} not found`)
|
|
344
|
+
return
|
|
345
|
+
}
|
|
346
|
+
const asset = fs.readFileSync(assetPath, 'utf8')
|
|
347
|
+
reply.header('Cache-Control', 'no-store')
|
|
348
|
+
reply.type(assetName.endsWith('.css') ? 'text/css' : 'application/javascript').send(asset)
|
|
349
|
+
})
|
|
350
|
+
})
|
|
351
|
+
|
|
319
352
|
app.get(`${prefix}/ws`, { websocket: true }, (connection) => {
|
|
320
353
|
const socket =
|
|
321
354
|
(connection as {
|
|
@@ -91,7 +91,7 @@
|
|
|
91
91
|
collectionTabButtons,
|
|
92
92
|
collectionTabPanels
|
|
93
93
|
} = dom;
|
|
94
|
-
const { api, parseJsonObject, highlightJson, safeStringify } = utils;
|
|
94
|
+
const { api, parseJsonObject, highlightJson, renderJsonViewer, clearJsonViewer, safeStringify } = utils;
|
|
95
95
|
|
|
96
96
|
const TABLE_TRUNCATE_LIMIT = 200;
|
|
97
97
|
|
|
@@ -337,22 +337,21 @@
|
|
|
337
337
|
const highlight = state.collectionResultHighlight;
|
|
338
338
|
if (payload === null || payload === undefined) {
|
|
339
339
|
collectionResult.classList.remove('table-view', 'json-highlight');
|
|
340
|
-
collectionResult
|
|
340
|
+
clearJsonViewer(collectionResult, '');
|
|
341
341
|
return;
|
|
342
342
|
}
|
|
343
343
|
if (state.collectionResultView === 'table') {
|
|
344
|
+
clearJsonViewer(collectionResult, '');
|
|
344
345
|
renderCollectionTable(payload);
|
|
345
346
|
return;
|
|
346
347
|
}
|
|
347
348
|
collectionResult.classList.remove('table-view');
|
|
348
349
|
if (highlight) {
|
|
349
350
|
const text = typeof payload === 'string' ? payload : JSON.stringify(payload, null, 2);
|
|
350
|
-
collectionResult
|
|
351
|
-
collectionResult.innerHTML = highlightJson(text || '');
|
|
351
|
+
renderJsonViewer(collectionResult, text || '', { collapsible: true });
|
|
352
352
|
return;
|
|
353
353
|
}
|
|
354
|
-
collectionResult
|
|
355
|
-
collectionResult.textContent = typeof payload === 'string' ? payload : String(payload ?? '');
|
|
354
|
+
clearJsonViewer(collectionResult, typeof payload === 'string' ? payload : String(payload ?? ''));
|
|
356
355
|
};
|
|
357
356
|
|
|
358
357
|
const setCollectionResult = (value, highlight) => {
|
|
@@ -365,12 +364,10 @@
|
|
|
365
364
|
if (!collectionRules) return;
|
|
366
365
|
if (highlight) {
|
|
367
366
|
const text = typeof value === 'string' ? value : JSON.stringify(value, null, 2);
|
|
368
|
-
collectionRules
|
|
369
|
-
collectionRules.innerHTML = highlightJson(text || '');
|
|
367
|
+
renderJsonViewer(collectionRules, text || '', { collapsible: true });
|
|
370
368
|
return;
|
|
371
369
|
}
|
|
372
|
-
collectionRules
|
|
373
|
-
collectionRules.textContent = typeof value === 'string' ? value : String(value ?? '');
|
|
370
|
+
clearJsonViewer(collectionRules, typeof value === 'string' ? value : String(value ?? ''));
|
|
374
371
|
};
|
|
375
372
|
|
|
376
373
|
const updateCollectionModeView = () => {
|