@flowerforce/flowerbase 1.7.6-beta.3 → 1.7.6-beta.4

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.
@@ -1 +1 @@
1
- {"version":3,"file":"controller.d.ts","sourceRoot":"","sources":["../../../../src/auth/providers/anon-user/controller.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AAOzC;;;;GAIG;AACH,wBAAsB,kBAAkB,CAAC,GAAG,EAAE,eAAe,iBAwE5D"}
1
+ {"version":3,"file":"controller.d.ts","sourceRoot":"","sources":["../../../../src/auth/providers/anon-user/controller.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AAOzC;;;;GAIG;AACH,wBAAsB,kBAAkB,CAAC,GAAG,EAAE,eAAe,iBAyE5D"}
@@ -54,6 +54,7 @@ function anonUserController(app) {
54
54
  email: anonEmail,
55
55
  status: 'confirmed',
56
56
  createdAt: now,
57
+ lastLoginAt: now,
57
58
  custom_data: {},
58
59
  identities: [
59
60
  {
@@ -1 +1 @@
1
- {"version":3,"file":"controller.d.ts","sourceRoot":"","sources":["../../../../src/auth/providers/custom-function/controller.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AAUzC;;;;GAIG;AACH,wBAAsB,wBAAwB,CAAC,GAAG,EAAE,eAAe,iBAqIlE"}
1
+ {"version":3,"file":"controller.d.ts","sourceRoot":"","sources":["../../../../src/auth/providers/custom-function/controller.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AAUzC;;;;GAIG;AACH,wBAAsB,wBAAwB,CAAC,GAAG,EAAE,eAAe,iBA0IlE"}
@@ -111,15 +111,17 @@ function customFunctionController(app) {
111
111
  user_data: Object.assign(Object.assign({}, (user || {})), { id: authUser._id.toString(), email: authUser.email }),
112
112
  custom_data: Object.assign({}, (user || {}))
113
113
  };
114
+ const now = new Date();
114
115
  const refreshToken = this.createRefreshToken(currentUserData);
115
116
  const refreshTokenHash = (0, crypto_1.hashToken)(refreshToken);
116
117
  yield authDb.collection(refreshTokensCollection).insertOne({
117
118
  userId: authUser._id,
118
119
  tokenHash: refreshTokenHash,
119
- createdAt: new Date(),
120
+ createdAt: now,
120
121
  expiresAt: new Date(Date.now() + refreshTokenTtlMs),
121
122
  revokedAt: null
122
123
  });
124
+ yield authDb.collection(authCollection).updateOne({ _id: authUser._id }, { $set: { lastLoginAt: now } });
123
125
  return {
124
126
  access_token: this.createAccessToken(currentUserData),
125
127
  refresh_token: refreshToken,
@@ -1 +1 @@
1
- {"version":3,"file":"controller.d.ts","sourceRoot":"","sources":["../../../../src/auth/providers/local-userpass/controller.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AAqCzC;;;;GAIG;AACH,wBAAsB,uBAAuB,CAAC,GAAG,EAAE,eAAe,iBAqXjE"}
1
+ {"version":3,"file":"controller.d.ts","sourceRoot":"","sources":["../../../../src/auth/providers/local-userpass/controller.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AAqCzC;;;;GAIG;AACH,wBAAsB,uBAAuB,CAAC,GAAG,EAAE,eAAe,iBA0XjE"}
@@ -214,15 +214,17 @@ function localUserPassController(app) {
214
214
  if (authUser && authUser.status !== 'confirmed') {
215
215
  throw new Error(utils_1.AUTH_ERRORS.USER_NOT_CONFIRMED);
216
216
  }
217
+ const now = new Date();
217
218
  const refreshToken = this.createRefreshToken(userWithCustomData);
218
219
  const refreshTokenHash = (0, crypto_1.hashToken)(refreshToken);
219
220
  yield authDb.collection(refreshTokensCollection).insertOne({
220
221
  userId: authUser._id,
221
222
  tokenHash: refreshTokenHash,
222
- createdAt: new Date(),
223
+ createdAt: now,
223
224
  expiresAt: new Date(Date.now() + refreshTokenTtlMs),
224
225
  revokedAt: null
225
226
  });
227
+ yield authDb.collection(authCollection).updateOne({ _id: authUser._id }, { $set: { lastLoginAt: now } });
226
228
  return {
227
229
  access_token: this.createAccessToken(userWithCustomData),
228
230
  refresh_token: refreshToken,
@@ -22,7 +22,7 @@ type Config = {
22
22
  isAutoTrigger?: boolean;
23
23
  match: Record<string, unknown>;
24
24
  operation_types: string[];
25
- operation_type?: 'CREATE' | 'DELETE' | 'LOGOUT';
25
+ operation_type?: 'CREATE' | 'DELETE' | 'LOGIN' | 'LOGOUT';
26
26
  providers?: string[];
27
27
  project: Record<string, unknown>;
28
28
  service_name: string;
@@ -1 +1 @@
1
- {"version":3,"file":"interface.d.ts","sourceRoot":"","sources":["../../../src/features/triggers/interface.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AACzC,OAAO,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAA;AACnD,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAA;AAE5D,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,WAAW,CAAA;IACjB,QAAQ,EAAE,OAAO,CAAA;IACjB,MAAM,EAAE,MAAM,CAAA;IACd,gBAAgB,EAAE;QAChB,QAAQ,EAAE;YACR,MAAM,EAAE;gBACN,aAAa,EAAE,MAAM,CAAA;aACtB,CAAA;SACF,CAAA;KACF,CAAA;CACF;AAED,KAAK,MAAM,GAAG;IACZ,UAAU,EAAE,MAAM,CAAA;IAClB,QAAQ,EAAE,MAAM,CAAA;IAChB,aAAa,EAAE,OAAO,CAAA;IACtB,2BAA2B,EAAE,OAAO,CAAA;IACpC,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC9B,eAAe,EAAE,MAAM,EAAE,CAAA;IACzB,cAAc,CAAC,EAAE,QAAQ,GAAG,QAAQ,GAAG,QAAQ,CAAA;IAC/C,SAAS,CAAC,EAAE,MAAM,EAAE,CAAA;IACpB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAChC,YAAY,EAAE,MAAM,CAAA;IACpB,mBAAmB,EAAE,OAAO,CAAA;IAC5B,sBAAsB,EAAE,OAAO,CAAA;IAC/B,SAAS,EAAE,OAAO,CAAA;IAClB,QAAQ,EAAE,MAAM,CAAA;CACjB,CAAA;AAED,MAAM,MAAM,WAAW,GAAG,WAAW,GAAG,UAAU,GAAG,gBAAgB,CAAA;AACrE,MAAM,MAAM,QAAQ,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,EAAE,CAAA;AAE/D,MAAM,MAAM,aAAa,GAAG;IAC1B,MAAM,EAAE,MAAM,CAAA;IACd,cAAc,EAAE,QAAQ,CAAA;IACxB,GAAG,EAAE,eAAe,CAAA;IACpB,QAAQ,EAAE,QAAQ,CAAA;IAClB,aAAa,EAAE,SAAS,CAAA;IACxB,WAAW,EAAE,MAAM,CAAA;IACnB,WAAW,EAAE,WAAW,CAAA;IACxB,YAAY,EAAE,MAAM,CAAA;CACrB,CAAA"}
1
+ {"version":3,"file":"interface.d.ts","sourceRoot":"","sources":["../../../src/features/triggers/interface.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AACzC,OAAO,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAA;AACnD,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAA;AAE5D,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,WAAW,CAAA;IACjB,QAAQ,EAAE,OAAO,CAAA;IACjB,MAAM,EAAE,MAAM,CAAA;IACd,gBAAgB,EAAE;QAChB,QAAQ,EAAE;YACR,MAAM,EAAE;gBACN,aAAa,EAAE,MAAM,CAAA;aACtB,CAAA;SACF,CAAA;KACF,CAAA;CACF;AAED,KAAK,MAAM,GAAG;IACZ,UAAU,EAAE,MAAM,CAAA;IAClB,QAAQ,EAAE,MAAM,CAAA;IAChB,aAAa,EAAE,OAAO,CAAA;IACtB,2BAA2B,EAAE,OAAO,CAAA;IACpC,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC9B,eAAe,EAAE,MAAM,EAAE,CAAA;IACzB,cAAc,CAAC,EAAE,QAAQ,GAAG,QAAQ,GAAG,OAAO,GAAG,QAAQ,CAAA;IACzD,SAAS,CAAC,EAAE,MAAM,EAAE,CAAA;IACpB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAChC,YAAY,EAAE,MAAM,CAAA;IACpB,mBAAmB,EAAE,OAAO,CAAA;IAC5B,sBAAsB,EAAE,OAAO,CAAA;IAC/B,SAAS,EAAE,OAAO,CAAA;IAClB,QAAQ,EAAE,MAAM,CAAA;CACjB,CAAA;AAED,MAAM,MAAM,WAAW,GAAG,WAAW,GAAG,UAAU,GAAG,gBAAgB,CAAA;AACrE,MAAM,MAAM,QAAQ,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,EAAE,CAAA;AAE/D,MAAM,MAAM,aAAa,GAAG;IAC1B,MAAM,EAAE,MAAM,CAAA;IACd,cAAc,EAAE,QAAQ,CAAA;IACxB,GAAG,EAAE,eAAe,CAAA;IACpB,QAAQ,EAAE,QAAQ,CAAA;IAClB,aAAa,EAAE,SAAS,CAAA;IACxB,WAAW,EAAE,MAAM,CAAA;IACnB,WAAW,EAAE,WAAW,CAAA;IACxB,YAAY,EAAE,MAAM,CAAA;CACrB,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../src/features/triggers/utils.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,aAAa,EAAW,QAAQ,EAAE,MAAM,aAAa,CAAA;AA0E9D;;;;;;;GAOG;AACH,eAAO,MAAM,YAAY,GAAU,gBAAuB,KAAG,OAAO,CAAC,QAAQ,CAkB5E,CAAA;AA+mBD,eAAO,MAAM,gBAAgB;kHAxlB1B,aAAa;iHAggBb,aAAa;uHA/Yb,aAAa;CA2ef,CAAA"}
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../src/features/triggers/utils.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,aAAa,EAAW,QAAQ,EAAE,MAAM,aAAa,CAAA;AA0E9D;;;;;;;GAOG;AACH,eAAO,MAAM,YAAY,GAAU,gBAAuB,KAAG,OAAO,CAAC,QAAQ,CAkB5E,CAAA;AA2qBD,eAAO,MAAM,gBAAgB;kHAppB1B,aAAa;iHA4jBb,aAAa;uHA1cb,aAAa;CAsiBf,CAAA"}
@@ -170,6 +170,7 @@ const handleCronTrigger = (_a) => __awaiter(void 0, [_a], void 0, function* ({ c
170
170
  const mapOpInverse = {
171
171
  CREATE: ['insert', 'update', 'replace'],
172
172
  DELETE: ['delete'],
173
+ LOGIN: ['insert', 'update'],
173
174
  LOGOUT: ['update'],
174
175
  };
175
176
  const normalizeOperationTypes = (operationTypes = []) => operationTypes.map((op) => op.toLowerCase());
@@ -270,6 +271,8 @@ const handleAuthenticationTrigger = (_a) => __awaiter(void 0, [_a], void 0, func
270
271
  const isUpdate = operationType === 'update';
271
272
  const isReplace = operationType === 'replace';
272
273
  const isDelete = operationType === 'delete';
274
+ const isLoginInsert = isInsert && !!(fullDocument === null || fullDocument === void 0 ? void 0 : fullDocument.lastLoginAt);
275
+ const isLoginUpdate = isUpdate && !!updatedFields && 'lastLoginAt' in updatedFields;
273
276
  const isLogoutUpdate = isUpdate && !!updatedFields && 'lastLogoutAt' in updatedFields;
274
277
  let confirmedCandidate = false;
275
278
  let confirmedDocument = fullDocument;
@@ -353,6 +356,63 @@ const handleAuthenticationTrigger = (_a) => __awaiter(void 0, [_a], void 0, func
353
356
  }
354
357
  return;
355
358
  }
359
+ if (operation_type === 'LOGIN') {
360
+ if (!isLoginInsert && !isLoginUpdate) {
361
+ return;
362
+ }
363
+ let loginDocument = fullDocument !== null && fullDocument !== void 0 ? fullDocument : confirmedDocument;
364
+ if (!loginDocument && (documentKey === null || documentKey === void 0 ? void 0 : documentKey._id)) {
365
+ loginDocument = (yield collection.findOne({
366
+ _id: documentKey._id
367
+ }));
368
+ }
369
+ if (!matchesProviderFilter(loginDocument, providerFilter)) {
370
+ return;
371
+ }
372
+ const userData = buildUserData(loginDocument);
373
+ if (!userData) {
374
+ return;
375
+ }
376
+ const op = {
377
+ operationType: 'LOGIN',
378
+ fullDocument,
379
+ fullDocumentBeforeChange,
380
+ documentKey,
381
+ updateDescription
382
+ };
383
+ try {
384
+ emitTriggerEvent({
385
+ status: 'fired',
386
+ triggerName,
387
+ triggerType,
388
+ functionName,
389
+ meta: Object.assign(Object.assign({}, baseMeta), { event: 'LOGIN' })
390
+ });
391
+ yield (0, context_1.GenerateContext)({
392
+ args: [Object.assign({ user: userData }, op)],
393
+ app,
394
+ rules: state_1.StateManager.select("rules"),
395
+ user: {}, // TODO from currentUser ??
396
+ currentFunction: triggerHandler,
397
+ functionName,
398
+ functionsList,
399
+ services,
400
+ runAsSystem: true
401
+ });
402
+ }
403
+ catch (error) {
404
+ emitTriggerEvent({
405
+ status: 'error',
406
+ triggerName,
407
+ triggerType,
408
+ functionName,
409
+ meta: Object.assign(Object.assign({}, baseMeta), { event: 'LOGIN' }),
410
+ error
411
+ });
412
+ console.log("🚀 ~ handleAuthenticationTrigger ~ error:", error);
413
+ }
414
+ return;
415
+ }
356
416
  if (isDelete) {
357
417
  if (isAutoTrigger || operation_type !== 'DELETE') {
358
418
  return;
@@ -1 +1 @@
1
- {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../../src/utils/context/helpers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAA;AAG1C,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAE3C,OAAO,EAAE,yBAAyB,EAAE,MAAM,aAAa,CAAA;AAEvD,KAAK,QAAQ,GAAG;IACd,MAAM,EAAE,CACN,aAAa,EAAE,MAAM,EACrB,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,MAAM,GAAG,MAAM,EACvB,kBAAkB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KACzC,MAAM,CAAA;IACX,MAAM,EAAE,CACN,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,MAAM,GAAG,MAAM,EACpB,YAAY,CAAC,EAAE,OAAO,EACtB,sBAAsB,CAAC,EAAE,MAAM,EAAE,KAC9B,OAAO,CAAA;CACb,CAAA;AAgFD;;;;;;;;;GASG;AACH,eAAO,MAAM,mBAAmB,GAAI,4GAUjC,yBAAyB;;;;;;;;;;;;;uBA4DP,SAAS;yBAGP,SAAS;;;;;;;;;;;;;;;;;;uBAcb,MAAM;;;;;;+BA5CU,MAAM,OAAO,QAAQ;;;;sCA1HrC,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8BAsGT,CAAC;iCAAa,CAAC;;;;;;;;;;;;;;;;;;;kCAtGP,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0BAsGT,CAAC;6BAAa,CAAC;;;;;;;;;;;;;;;;;;kCAtGP,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0BAsGT,CAAC;6BAAa,CAAC;;;;;;;;;;;;;;;4BAyEF,MAAM,OAAO,aAAa,WAAW,SAAS;;;CAiBrE,CAAA"}
1
+ {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../../src/utils/context/helpers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAA;AAG1C,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAE3C,OAAO,EAAE,yBAAyB,EAAE,MAAM,aAAa,CAAA;AAEvD,KAAK,QAAQ,GAAG;IACd,MAAM,EAAE,CACN,aAAa,EAAE,MAAM,EACrB,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,MAAM,GAAG,MAAM,EACvB,kBAAkB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KACzC,MAAM,CAAA;IACX,MAAM,EAAE,CACN,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,MAAM,GAAG,MAAM,EACpB,YAAY,CAAC,EAAE,OAAO,EACtB,sBAAsB,CAAC,EAAE,MAAM,EAAE,KAC9B,OAAO,CAAA;CACb,CAAA;AAgFD;;;;;;;;;GASG;AACH,eAAO,MAAM,mBAAmB,GAAI,4GAUjC,yBAAyB;;;;;;;;;;;;;uBA4DP,SAAS;yBAGP,SAAS;;;;;;;;;;;;;;;;;;uBAcb,MAAM;;;;;;+BA5CU,MAAM,OAAO,QAAQ;;;;sCA1HrC,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8BAsGT,CAAC;iCAAa,CAAC;;;;;;;;;;;;;;;;;;;kCAtGP,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0BAsGT,CAAC;6BAAa,CAAC;;;;;;;;;;;;;;;;;;kCAtGP,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0BAsGT,CAAC;6BAAa,CAAC;;;;;;;;;;;;;;;4BAyEF,MAAM,OAAO,aAAa,WAAW,SAAS;;;CAkBrE,CAAA"}
@@ -176,16 +176,17 @@ const generateContextData = ({ user, services, app, rules, currentFunction, func
176
176
  https: getService('api'),
177
177
  functions: {
178
178
  execute: (name, ...args) => {
179
- const currentFunction = functionsList[name];
179
+ const targetFunction = functionsList[name];
180
180
  return GenerateContextSync({
181
181
  args,
182
182
  app,
183
183
  rules,
184
184
  user,
185
- currentFunction,
185
+ currentFunction: targetFunction,
186
186
  functionName: String(name),
187
187
  functionsList,
188
188
  services,
189
+ runAsSystem: currentFunction.run_as_system,
189
190
  deserializeArgs: false
190
191
  });
191
192
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/utils/context/index.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAA;AA4JnD;;;;;;;;;;GAUG;AACH,wBAAsB,eAAe,CAAC,EACpC,IAAI,EACJ,GAAG,EACH,KAAK,EACL,IAAI,EACJ,eAAe,EACf,aAAa,EACb,QAAQ,EACR,YAAY,EACZ,WAAW,EACX,eAAsB,EACtB,OAAO,EACP,OAAO,EACR,EAAE,qBAAqB,GAAG,OAAO,CAAC,OAAO,CAAC,CA2G1C;AAED,wBAAgB,mBAAmB,CAAC,EAClC,IAAI,EACJ,GAAG,EACH,KAAK,EACL,IAAI,EACJ,eAAe,EACf,aAAa,EACb,QAAQ,EACR,YAAY,EACZ,WAAW,EACX,eAAsB,EACtB,OAAO,EACR,EAAE,qBAAqB,GAAG,OAAO,CA4BjC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/utils/context/index.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAA;AA4JnD;;;;;;;;;;GAUG;AACH,wBAAsB,eAAe,CAAC,EACpC,IAAI,EACJ,GAAG,EACH,KAAK,EACL,IAAI,EACJ,eAAe,EACf,aAAa,EACb,QAAQ,EACR,YAAY,EACZ,WAAW,EACX,eAAsB,EACtB,OAAO,EACP,OAAO,EACR,EAAE,qBAAqB,GAAG,OAAO,CAAC,OAAO,CAAC,CA2G1C;AAED,wBAAgB,mBAAmB,CAAC,EAClC,IAAI,EACJ,GAAG,EACH,KAAK,EACL,IAAI,EACJ,eAAe,EACf,aAAa,EACb,QAAQ,EACR,YAAY,EACZ,WAAW,EACX,eAAsB,EACtB,OAAO,EACR,EAAE,qBAAqB,GAAG,OAAO,CA6BjC"}
@@ -153,7 +153,8 @@ function GenerateContext(_a) {
153
153
  if (!currentFunction)
154
154
  return;
155
155
  const functionsQueue = state_1.StateManager.select("functionsQueue");
156
- const functionToRun = Object.assign({ run_as_system: runAsSystem }, currentFunction);
156
+ const effectiveRunAsSystem = Boolean(runAsSystem || currentFunction.run_as_system);
157
+ const functionToRun = Object.assign(Object.assign({}, currentFunction), { run_as_system: effectiveRunAsSystem });
157
158
  const run = () => __awaiter(this, void 0, void 0, function* () {
158
159
  var _a;
159
160
  const contextData = (0, helpers_1.generateContextData)({
@@ -232,7 +233,8 @@ function GenerateContextSync({ args, app, rules, user, currentFunction, function
232
233
  var _a;
233
234
  if (!currentFunction)
234
235
  return;
235
- const functionToRun = Object.assign({ run_as_system: runAsSystem }, currentFunction);
236
+ const effectiveRunAsSystem = Boolean(runAsSystem || currentFunction.run_as_system);
237
+ const functionToRun = Object.assign(Object.assign({}, currentFunction), { run_as_system: effectiveRunAsSystem });
236
238
  const contextData = (0, helpers_1.generateContextData)({
237
239
  user,
238
240
  services,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flowerforce/flowerbase",
3
- "version": "1.7.6-beta.3",
3
+ "version": "1.7.6-beta.4",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -50,6 +50,7 @@ export async function anonUserController(app: FastifyInstance) {
50
50
  email: anonEmail,
51
51
  status: 'confirmed',
52
52
  createdAt: now,
53
+ lastLoginAt: now,
53
54
  custom_data: {},
54
55
  identities: [
55
56
  {
@@ -128,15 +128,20 @@ export async function customFunctionController(app: FastifyInstance) {
128
128
  ...(user || {})
129
129
  }
130
130
  }
131
+ const now = new Date()
131
132
  const refreshToken = this.createRefreshToken(currentUserData)
132
133
  const refreshTokenHash = hashToken(refreshToken)
133
134
  await authDb.collection(refreshTokensCollection).insertOne({
134
135
  userId: authUser._id,
135
136
  tokenHash: refreshTokenHash,
136
- createdAt: new Date(),
137
+ createdAt: now,
137
138
  expiresAt: new Date(Date.now() + refreshTokenTtlMs),
138
139
  revokedAt: null
139
140
  })
141
+ await authDb.collection(authCollection!).updateOne(
142
+ { _id: authUser._id },
143
+ { $set: { lastLoginAt: now } }
144
+ )
140
145
  return {
141
146
  access_token: this.createAccessToken(currentUserData),
142
147
  refresh_token: refreshToken,
@@ -283,15 +283,20 @@ export async function localUserPassController(app: FastifyInstance) {
283
283
  throw new Error(AUTH_ERRORS.USER_NOT_CONFIRMED)
284
284
  }
285
285
 
286
+ const now = new Date()
286
287
  const refreshToken = this.createRefreshToken(userWithCustomData)
287
288
  const refreshTokenHash = hashToken(refreshToken)
288
289
  await authDb.collection(refreshTokensCollection).insertOne({
289
290
  userId: authUser._id,
290
291
  tokenHash: refreshTokenHash,
291
- createdAt: new Date(),
292
+ createdAt: now,
292
293
  expiresAt: new Date(Date.now() + refreshTokenTtlMs),
293
294
  revokedAt: null
294
295
  })
296
+ await authDb.collection(authCollection!).updateOne(
297
+ { _id: authUser._id },
298
+ { $set: { lastLoginAt: now } }
299
+ )
295
300
 
296
301
  return {
297
302
  access_token: this.createAccessToken(userWithCustomData),
@@ -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
@@ -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
 
@@ -306,6 +307,8 @@ const handleAuthenticationTrigger = async ({
306
307
  const isUpdate = operationType === 'update'
307
308
  const isReplace = operationType === 'replace'
308
309
  const isDelete = operationType === 'delete'
310
+ const isLoginInsert = isInsert && !!(fullDocument as Record<string, unknown> | null)?.lastLoginAt
311
+ const isLoginUpdate = isUpdate && !!updatedFields && 'lastLoginAt' in updatedFields
309
312
  const isLogoutUpdate = isUpdate && !!updatedFields && 'lastLogoutAt' in updatedFields
310
313
 
311
314
  let confirmedCandidate = false
@@ -397,6 +400,63 @@ const handleAuthenticationTrigger = async ({
397
400
  return
398
401
  }
399
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 {
428
+ emitTriggerEvent({
429
+ status: 'fired',
430
+ triggerName,
431
+ triggerType,
432
+ functionName,
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
445
+ })
446
+ } catch (error) {
447
+ emitTriggerEvent({
448
+ status: 'error',
449
+ triggerName,
450
+ triggerType,
451
+ functionName,
452
+ meta: { ...baseMeta, event: 'LOGIN' },
453
+ error
454
+ })
455
+ console.log("🚀 ~ handleAuthenticationTrigger ~ error:", error)
456
+ }
457
+ return
458
+ }
459
+
400
460
  if (isDelete) {
401
461
  if (isAutoTrigger || operation_type !== 'DELETE') {
402
462
  return
@@ -4,7 +4,7 @@ import { Functions } from '../../features/functions/interface'
4
4
  const mockServices = {
5
5
  api: jest.fn().mockReturnValue({}),
6
6
  aws: jest.fn().mockReturnValue({}),
7
- 'mongodb-atlas': jest.fn().mockReturnValue({})
7
+ 'mongodb-atlas': jest.fn((_app, options) => options ?? {})
8
8
  } as any
9
9
 
10
10
  describe('context.functions.execute compatibility', () => {
@@ -91,4 +91,30 @@ describe('context.functions.execute compatibility', () => {
91
91
 
92
92
  expect(result).toBe(true)
93
93
  })
94
+
95
+ it('propagates run_as_system to child functions executed through context.functions.execute', () => {
96
+ const functionsList = {
97
+ caller: {
98
+ run_as_system: true,
99
+ code: 'module.exports = function() { return context.functions.execute("target") }'
100
+ },
101
+ target: {
102
+ run_as_system: false,
103
+ code: 'module.exports = function() { return context.services.get("mongodb-atlas").run_as_system }'
104
+ }
105
+ } as Functions
106
+
107
+ const result = GenerateContextSync({
108
+ args: [],
109
+ app: {} as any,
110
+ rules: {} as any,
111
+ user: {} as any,
112
+ currentFunction: functionsList.caller,
113
+ functionsList,
114
+ services: mockServices,
115
+ functionName: 'caller'
116
+ })
117
+
118
+ expect(result).toBe(true)
119
+ })
94
120
  })
@@ -79,7 +79,11 @@ describe('generateContextData', () => {
79
79
  mockErrorLog.mockRestore()
80
80
 
81
81
  context.functions.execute('test')
82
- expect(GenerateContextSyncMock).toHaveBeenCalled()
82
+ expect(GenerateContextSyncMock).toHaveBeenCalledWith(expect.objectContaining({
83
+ currentFunction,
84
+ functionName: 'test',
85
+ runAsSystem: currentFunction.run_as_system
86
+ }))
83
87
 
84
88
  const token = jwt.sign(
85
89
  { sub: 'user', role: 'admin' },
@@ -205,16 +205,17 @@ export const generateContextData = ({
205
205
  https: getService('api'),
206
206
  functions: {
207
207
  execute: (name: keyof typeof functionsList, ...args: Arguments) => {
208
- const currentFunction = functionsList[name] as Function
208
+ const targetFunction = functionsList[name] as Function
209
209
  return GenerateContextSync({
210
210
  args,
211
211
  app,
212
212
  rules,
213
213
  user,
214
- currentFunction,
214
+ currentFunction: targetFunction,
215
215
  functionName: String(name),
216
216
  functionsList,
217
217
  services,
218
+ runAsSystem: currentFunction.run_as_system,
218
219
  deserializeArgs: false
219
220
  })
220
221
  }
@@ -189,8 +189,8 @@ export async function GenerateContext({
189
189
  if (!currentFunction) return
190
190
 
191
191
  const functionsQueue = StateManager.select("functionsQueue")
192
-
193
- const functionToRun = { run_as_system: runAsSystem, ...currentFunction }
192
+ const effectiveRunAsSystem = Boolean(runAsSystem || currentFunction.run_as_system)
193
+ const functionToRun = { ...currentFunction, run_as_system: effectiveRunAsSystem }
194
194
 
195
195
  const run = async () => {
196
196
 
@@ -309,7 +309,8 @@ export function GenerateContextSync({
309
309
  }: GenerateContextParams): unknown {
310
310
  if (!currentFunction) return
311
311
 
312
- const functionToRun = { run_as_system: runAsSystem, ...currentFunction }
312
+ const effectiveRunAsSystem = Boolean(runAsSystem || currentFunction.run_as_system)
313
+ const functionToRun = { ...currentFunction, run_as_system: effectiveRunAsSystem }
313
314
  const contextData = generateContextData({
314
315
  user,
315
316
  services,