@budibase/backend-core 2.13.10 → 2.13.12

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 (49) hide show
  1. package/dist/index.js +477 -343
  2. package/dist/index.js.map +4 -4
  3. package/dist/index.js.meta.json +1 -1
  4. package/dist/package.json +4 -4
  5. package/dist/plugins.js.meta.json +1 -1
  6. package/dist/src/cache/generic.js +6 -2
  7. package/dist/src/cache/generic.js.map +1 -1
  8. package/dist/src/cache/index.d.ts +2 -0
  9. package/dist/src/cache/index.js +3 -1
  10. package/dist/src/cache/index.js.map +1 -1
  11. package/dist/src/cache/invite.d.ts +33 -0
  12. package/dist/src/cache/invite.js +119 -0
  13. package/dist/src/cache/invite.js.map +1 -0
  14. package/dist/src/cache/passwordReset.d.ts +19 -0
  15. package/dist/src/cache/passwordReset.js +72 -0
  16. package/dist/src/cache/passwordReset.js.map +1 -0
  17. package/dist/src/index.d.ts +1 -0
  18. package/dist/src/index.js +2 -1
  19. package/dist/src/index.js.map +1 -1
  20. package/dist/src/redis/init.d.ts +3 -0
  21. package/dist/src/redis/init.js +27 -2
  22. package/dist/src/redis/init.js.map +1 -1
  23. package/dist/src/redis/utils.js +3 -1
  24. package/dist/src/redis/utils.js.map +1 -1
  25. package/dist/src/security/permissions.d.ts +1 -1
  26. package/dist/src/users/db.d.ts +1 -1
  27. package/dist/src/users/db.js +1 -1
  28. package/dist/src/users/db.js.map +1 -1
  29. package/dist/src/users/lookup.js +3 -0
  30. package/dist/src/users/lookup.js.map +1 -1
  31. package/dist/src/utils/Duration.d.ts +5 -0
  32. package/dist/src/utils/Duration.js +3 -0
  33. package/dist/src/utils/Duration.js.map +1 -1
  34. package/dist/tests/core/utilities/structures/users.js +1 -1
  35. package/dist/tests/core/utilities/structures/users.js.map +1 -1
  36. package/package.json +4 -4
  37. package/src/cache/generic.ts +3 -2
  38. package/src/cache/index.ts +2 -0
  39. package/src/cache/invite.ts +86 -0
  40. package/src/cache/passwordReset.ts +38 -0
  41. package/src/db/tests/index.spec.js +0 -1
  42. package/src/index.ts +2 -0
  43. package/src/redis/init.ts +22 -2
  44. package/src/redis/utils.ts +3 -1
  45. package/src/users/db.ts +2 -2
  46. package/src/users/lookup.ts +4 -0
  47. package/src/utils/Duration.ts +3 -0
  48. package/tests/core/users/users.spec.js +9 -10
  49. package/tests/core/utilities/structures/users.ts +1 -1
package/dist/index.js CHANGED
@@ -2766,10 +2766,12 @@ function getRedisConnectionDetails() {
2766
2766
  url = url[0];
2767
2767
  }
2768
2768
  const [host, port] = url.split(":");
2769
+ const portNumber = parseInt(port);
2769
2770
  return {
2770
2771
  host,
2771
2772
  password,
2772
- port: parseInt(port)
2773
+ // assume default port for redis if invalid found
2774
+ port: isNaN(portNumber) ? 6379 : portNumber
2773
2775
  };
2774
2776
  }
2775
2777
  function getRedisOptions() {
@@ -3151,11 +3153,14 @@ var init_exports = {};
3151
3153
  __export(init_exports, {
3152
3154
  getAppClient: () => getAppClient,
3153
3155
  getCacheClient: () => getCacheClient,
3156
+ getInviteClient: () => getInviteClient,
3154
3157
  getLockClient: () => getLockClient,
3158
+ getPasswordResetClient: () => getPasswordResetClient,
3155
3159
  getSessionClient: () => getSessionClient,
3156
3160
  getSocketClient: () => getSocketClient,
3157
3161
  getUserClient: () => getUserClient,
3158
3162
  getWritethroughClient: () => getWritethroughClient,
3163
+ init: () => init3,
3159
3164
  shutdown: () => shutdown
3160
3165
  });
3161
3166
  async function init3() {
@@ -3165,6 +3170,8 @@ async function init3() {
3165
3170
  cacheClient = await new redis_default("data_cache" /* GENERIC_CACHE */).init();
3166
3171
  lockClient = await new redis_default("locks" /* LOCKS */).init();
3167
3172
  writethroughClient = await new redis_default("writeThrough" /* WRITE_THROUGH */).init();
3173
+ inviteClient = await new redis_default("invitation" /* INVITATIONS */).init();
3174
+ passwordResetClient = await new redis_default("pwReset" /* PW_RESETS */).init();
3168
3175
  socketClient = await new redis_default(
3169
3176
  "socket_io" /* SOCKET_IO */,
3170
3177
  1 /* SOCKET_IO */
@@ -3183,6 +3190,10 @@ async function shutdown() {
3183
3190
  await writethroughClient.finish();
3184
3191
  if (lockClient)
3185
3192
  await lockClient.finish();
3193
+ if (inviteClient)
3194
+ await inviteClient.finish();
3195
+ if (passwordResetClient)
3196
+ await passwordResetClient.finish();
3186
3197
  if (socketClient)
3187
3198
  await socketClient.finish();
3188
3199
  }
@@ -3228,7 +3239,19 @@ async function getSocketClient() {
3228
3239
  }
3229
3240
  return socketClient;
3230
3241
  }
3231
- var userClient, sessionClient, appClient, cacheClient, writethroughClient, lockClient, socketClient;
3242
+ async function getInviteClient() {
3243
+ if (!inviteClient) {
3244
+ await init3();
3245
+ }
3246
+ return inviteClient;
3247
+ }
3248
+ async function getPasswordResetClient() {
3249
+ if (!passwordResetClient) {
3250
+ await init3();
3251
+ }
3252
+ return passwordResetClient;
3253
+ }
3254
+ var userClient, sessionClient, appClient, cacheClient, writethroughClient, lockClient, socketClient, inviteClient, passwordResetClient;
3232
3255
  var init_init = __esm({
3233
3256
  "src/redis/init.ts"() {
3234
3257
  "use strict";
@@ -3240,86 +3263,6 @@ var init_init = __esm({
3240
3263
  }
3241
3264
  });
3242
3265
 
3243
- // src/cache/base/index.ts
3244
- var base_exports = {};
3245
- __export(base_exports, {
3246
- default: () => BaseCache
3247
- });
3248
- function generateTenantKey(key) {
3249
- const tenantId = getTenantId();
3250
- return `${key}:${tenantId}`;
3251
- }
3252
- var BaseCache;
3253
- var init_base = __esm({
3254
- "src/cache/base/index.ts"() {
3255
- "use strict";
3256
- init_context2();
3257
- init_init();
3258
- BaseCache = class {
3259
- constructor(client = void 0) {
3260
- this.client = client;
3261
- }
3262
- async getClient() {
3263
- return !this.client ? await getCacheClient() : this.client;
3264
- }
3265
- async keys(pattern) {
3266
- const client = await this.getClient();
3267
- return client.keys(pattern);
3268
- }
3269
- /**
3270
- * Read only from the cache.
3271
- */
3272
- async get(key, opts = { useTenancy: true }) {
3273
- key = opts.useTenancy ? generateTenantKey(key) : key;
3274
- const client = await this.getClient();
3275
- return client.get(key);
3276
- }
3277
- /**
3278
- * Write to the cache.
3279
- */
3280
- async store(key, value, ttl = null, opts = { useTenancy: true }) {
3281
- key = opts.useTenancy ? generateTenantKey(key) : key;
3282
- const client = await this.getClient();
3283
- await client.store(key, value, ttl);
3284
- }
3285
- /**
3286
- * Remove from cache.
3287
- */
3288
- async delete(key, opts = { useTenancy: true }) {
3289
- key = opts.useTenancy ? generateTenantKey(key) : key;
3290
- const client = await this.getClient();
3291
- return client.delete(key);
3292
- }
3293
- /**
3294
- * Read from the cache. Write to the cache if not exists.
3295
- */
3296
- async withCache(key, ttl, fetchFn, opts = { useTenancy: true }) {
3297
- const cachedValue = await this.get(key, opts);
3298
- if (cachedValue) {
3299
- return cachedValue;
3300
- }
3301
- try {
3302
- const fetchedValue = await fetchFn();
3303
- await this.store(key, fetchedValue, ttl, opts);
3304
- return fetchedValue;
3305
- } catch (err) {
3306
- console.error("Error fetching before cache - ", err);
3307
- throw err;
3308
- }
3309
- }
3310
- async bustCache(key, opts = { client: null }) {
3311
- const client = await this.getClient();
3312
- try {
3313
- await client.delete(generateTenantKey(key));
3314
- } catch (err) {
3315
- console.error("Error busting cache - ", err);
3316
- throw err;
3317
- }
3318
- }
3319
- };
3320
- }
3321
- });
3322
-
3323
3266
  // src/logging/correlation/correlation.ts
3324
3267
  var correlation_exports = {};
3325
3268
  __export(correlation_exports, {
@@ -3606,6 +3549,35 @@ var init_params = __esm({
3606
3549
  });
3607
3550
 
3608
3551
  // src/docIds/index.ts
3552
+ var docIds_exports = {};
3553
+ __export(docIds_exports, {
3554
+ generateAppID: () => generateAppID,
3555
+ generateAppUserID: () => generateAppUserID,
3556
+ generateDevInfoID: () => generateDevInfoID,
3557
+ generateGlobalUserID: () => generateGlobalUserID,
3558
+ generatePluginID: () => generatePluginID,
3559
+ generateRoleID: () => generateRoleID,
3560
+ generateRowID: () => generateRowID,
3561
+ generateTemplateID: () => generateTemplateID,
3562
+ generateUserMetadataID: () => generateUserMetadataID,
3563
+ generateWorkspaceID: () => generateWorkspaceID,
3564
+ getDocParams: () => getDocParams,
3565
+ getGlobalIDFromUserMetadataID: () => getGlobalIDFromUserMetadataID,
3566
+ getGlobalUserParams: () => getGlobalUserParams,
3567
+ getPluginParams: () => getPluginParams,
3568
+ getQueryIndex: () => getQueryIndex,
3569
+ getRoleParams: () => getRoleParams,
3570
+ getRowParams: () => getRowParams,
3571
+ getStartEndKeyURL: () => getStartEndKeyURL,
3572
+ getTemplateParams: () => getTemplateParams,
3573
+ getUserMetadataParams: () => getUserMetadataParams,
3574
+ getUsersByAppParams: () => getUsersByAppParams,
3575
+ getWorkspaceParams: () => getWorkspaceParams,
3576
+ isDatasourceId: () => isDatasourceId,
3577
+ isGlobalUserID: () => isGlobalUserID,
3578
+ isTableId: () => isTableId,
3579
+ prefixRoleID: () => prefixRoleID
3580
+ });
3609
3581
  var init_docIds = __esm({
3610
3582
  "src/docIds/index.ts"() {
3611
3583
  "use strict";
@@ -5672,6 +5644,7 @@ __export(src_exports, {
5672
5644
  constants: () => constants_exports,
5673
5645
  context: () => context_exports,
5674
5646
  db: () => db_exports,
5647
+ docIds: () => docIds_exports,
5675
5648
  docUpdates: () => docUpdates_exports,
5676
5649
  encryption: () => encryption_exports,
5677
5650
  env: () => environment_default,
@@ -5697,7 +5670,7 @@ __export(src_exports, {
5697
5670
  tenancy: () => tenancy,
5698
5671
  timers: () => timers_exports,
5699
5672
  users: () => users_exports3,
5700
- utils: () => utils_exports3
5673
+ utils: () => utils_exports2
5701
5674
  });
5702
5675
  module.exports = __toCommonJS(src_exports);
5703
5676
 
@@ -5736,7 +5709,9 @@ __export(cache_exports, {
5736
5709
  destroy: () => destroy,
5737
5710
  generic: () => generic_exports,
5738
5711
  get: () => get,
5712
+ invite: () => invite_exports,
5739
5713
  keys: () => keys,
5714
+ passwordReset: () => passwordReset_exports,
5740
5715
  store: () => store,
5741
5716
  user: () => user_exports,
5742
5717
  withCache: () => withCache,
@@ -5755,8 +5730,79 @@ __export(generic_exports, {
5755
5730
  store: () => store,
5756
5731
  withCache: () => withCache
5757
5732
  });
5758
- var BaseCache2 = (init_base(), __toCommonJS(base_exports));
5759
- var GENERIC = new BaseCache2.default();
5733
+
5734
+ // src/cache/base/index.ts
5735
+ init_context2();
5736
+ init_init();
5737
+ function generateTenantKey(key) {
5738
+ const tenantId = getTenantId();
5739
+ return `${key}:${tenantId}`;
5740
+ }
5741
+ var BaseCache = class {
5742
+ constructor(client = void 0) {
5743
+ this.client = client;
5744
+ }
5745
+ async getClient() {
5746
+ return !this.client ? await getCacheClient() : this.client;
5747
+ }
5748
+ async keys(pattern) {
5749
+ const client = await this.getClient();
5750
+ return client.keys(pattern);
5751
+ }
5752
+ /**
5753
+ * Read only from the cache.
5754
+ */
5755
+ async get(key, opts = { useTenancy: true }) {
5756
+ key = opts.useTenancy ? generateTenantKey(key) : key;
5757
+ const client = await this.getClient();
5758
+ return client.get(key);
5759
+ }
5760
+ /**
5761
+ * Write to the cache.
5762
+ */
5763
+ async store(key, value, ttl = null, opts = { useTenancy: true }) {
5764
+ key = opts.useTenancy ? generateTenantKey(key) : key;
5765
+ const client = await this.getClient();
5766
+ await client.store(key, value, ttl);
5767
+ }
5768
+ /**
5769
+ * Remove from cache.
5770
+ */
5771
+ async delete(key, opts = { useTenancy: true }) {
5772
+ key = opts.useTenancy ? generateTenantKey(key) : key;
5773
+ const client = await this.getClient();
5774
+ return client.delete(key);
5775
+ }
5776
+ /**
5777
+ * Read from the cache. Write to the cache if not exists.
5778
+ */
5779
+ async withCache(key, ttl, fetchFn, opts = { useTenancy: true }) {
5780
+ const cachedValue = await this.get(key, opts);
5781
+ if (cachedValue) {
5782
+ return cachedValue;
5783
+ }
5784
+ try {
5785
+ const fetchedValue = await fetchFn();
5786
+ await this.store(key, fetchedValue, ttl, opts);
5787
+ return fetchedValue;
5788
+ } catch (err) {
5789
+ console.error("Error fetching before cache - ", err);
5790
+ throw err;
5791
+ }
5792
+ }
5793
+ async bustCache(key, opts = { client: null }) {
5794
+ const client = await this.getClient();
5795
+ try {
5796
+ await client.delete(generateTenantKey(key));
5797
+ } catch (err) {
5798
+ console.error("Error busting cache - ", err);
5799
+ throw err;
5800
+ }
5801
+ }
5802
+ };
5803
+
5804
+ // src/cache/generic.ts
5805
+ var GENERIC = new BaseCache();
5760
5806
  var CacheKey = /* @__PURE__ */ ((CacheKey2) => {
5761
5807
  CacheKey2["CHECKLIST"] = "checklist";
5762
5808
  CacheKey2["INSTALLATION"] = "installation";
@@ -6371,8 +6417,320 @@ init_context2();
6371
6417
  init_environment2();
6372
6418
 
6373
6419
  // src/users/lookup.ts
6374
- init_db4();
6375
- init_constants3();
6420
+ init_db4();
6421
+ init_constants3();
6422
+
6423
+ // src/cache/invite.ts
6424
+ var invite_exports = {};
6425
+ __export(invite_exports, {
6426
+ createCode: () => createCode,
6427
+ deleteCode: () => deleteCode,
6428
+ getCode: () => getCode,
6429
+ getExistingInvites: () => getExistingInvites,
6430
+ getInviteCodes: () => getInviteCodes,
6431
+ updateCode: () => updateCode
6432
+ });
6433
+
6434
+ // src/utils/index.ts
6435
+ var utils_exports2 = {};
6436
+ __export(utils_exports2, {
6437
+ Duration: () => Duration,
6438
+ DurationType: () => DurationType,
6439
+ clearCookie: () => clearCookie,
6440
+ compare: () => compare,
6441
+ getAppIdFromCtx: () => getAppIdFromCtx,
6442
+ getCookie: () => getCookie,
6443
+ hasCircularStructure: () => hasCircularStructure,
6444
+ hash: () => hash,
6445
+ isAudited: () => isAudited,
6446
+ isClient: () => isClient,
6447
+ isPublicApiRequest: () => isPublicApiRequest,
6448
+ isServingApp: () => isServingApp,
6449
+ isServingBuilder: () => isServingBuilder,
6450
+ isServingBuilderPreview: () => isServingBuilderPreview,
6451
+ isValidInternalAPIKey: () => isValidInternalAPIKey,
6452
+ newid: () => newid,
6453
+ openJwt: () => openJwt,
6454
+ resolveAppUrl: () => resolveAppUrl,
6455
+ setCookie: () => setCookie,
6456
+ timeout: () => timeout,
6457
+ validEmail: () => validEmail
6458
+ });
6459
+
6460
+ // src/utils/hashing.ts
6461
+ init_environment2();
6462
+ init_newid();
6463
+ var bcrypt = environment_default.JS_BCRYPT ? require("bcryptjs") : require("bcrypt");
6464
+ var SALT_ROUNDS = environment_default.SALT_ROUNDS || 10;
6465
+ async function hash(data) {
6466
+ const salt = await bcrypt.genSalt(SALT_ROUNDS);
6467
+ return bcrypt.hash(data, salt);
6468
+ }
6469
+ async function compare(data, encrypted) {
6470
+ return bcrypt.compare(data, encrypted);
6471
+ }
6472
+
6473
+ // src/utils/utils.ts
6474
+ init_db4();
6475
+ init_constants3();
6476
+ init_environment2();
6477
+ init_context2();
6478
+ init_src();
6479
+ var jwt = require("jsonwebtoken");
6480
+ var APP_PREFIX2 = "app" /* APP */ + SEPARATOR;
6481
+ var PROD_APP_PREFIX = "/app/";
6482
+ var BUILDER_PREVIEW_PATH = "/app/preview";
6483
+ var BUILDER_PREFIX = "/builder";
6484
+ var BUILDER_APP_PREFIX = `${BUILDER_PREFIX}/app/`;
6485
+ var PUBLIC_API_PREFIX = "/api/public/v";
6486
+ function confirmAppId(possibleAppId) {
6487
+ return possibleAppId && possibleAppId.startsWith(APP_PREFIX2) ? possibleAppId : void 0;
6488
+ }
6489
+ async function resolveAppUrl(ctx) {
6490
+ const appUrl = ctx.path.split("/")[2];
6491
+ let possibleAppUrl = `/${appUrl.toLowerCase()}`;
6492
+ let tenantId = getTenantId();
6493
+ if (environment_default.MULTI_TENANCY) {
6494
+ tenantId = getTenantIDFromCtx(ctx, {
6495
+ includeStrategies: ["subdomain" /* SUBDOMAIN */]
6496
+ });
6497
+ }
6498
+ const apps = await doInTenant(
6499
+ tenantId,
6500
+ () => getAllApps({ dev: false })
6501
+ );
6502
+ const app = apps.filter(
6503
+ (a) => a.url && a.url.toLowerCase() === possibleAppUrl
6504
+ )[0];
6505
+ return app && app.appId ? app.appId : void 0;
6506
+ }
6507
+ function isServingApp(ctx) {
6508
+ if (ctx.path.startsWith(`/${APP_PREFIX2}`)) {
6509
+ return true;
6510
+ }
6511
+ if (ctx.path.startsWith(PROD_APP_PREFIX)) {
6512
+ return true;
6513
+ }
6514
+ return false;
6515
+ }
6516
+ function isServingBuilder(ctx) {
6517
+ return ctx.path.startsWith(BUILDER_APP_PREFIX);
6518
+ }
6519
+ function isServingBuilderPreview(ctx) {
6520
+ return ctx.path.startsWith(BUILDER_PREVIEW_PATH);
6521
+ }
6522
+ function isPublicApiRequest(ctx) {
6523
+ return ctx.path.startsWith(PUBLIC_API_PREFIX);
6524
+ }
6525
+ async function getAppIdFromCtx(ctx) {
6526
+ const options2 = [ctx.request.headers["x-budibase-app-id" /* APP_ID */]];
6527
+ let appId;
6528
+ for (let option of options2) {
6529
+ appId = confirmAppId(option);
6530
+ if (appId) {
6531
+ break;
6532
+ }
6533
+ }
6534
+ if (!appId && ctx.request.body && ctx.request.body.appId) {
6535
+ appId = confirmAppId(ctx.request.body.appId);
6536
+ }
6537
+ const pathId = parseAppIdFromUrl(ctx.path);
6538
+ if (!appId && pathId) {
6539
+ appId = confirmAppId(pathId);
6540
+ }
6541
+ const isBuilderPreview = ctx.path.startsWith(BUILDER_PREVIEW_PATH);
6542
+ const isViewingProdApp = ctx.path.startsWith(PROD_APP_PREFIX) && !isBuilderPreview;
6543
+ if (!appId && isViewingProdApp) {
6544
+ appId = confirmAppId(await resolveAppUrl(ctx));
6545
+ }
6546
+ const referer = ctx.request.headers.referer;
6547
+ if (!appId && referer?.includes(BUILDER_APP_PREFIX)) {
6548
+ const refererId = parseAppIdFromUrl(ctx.request.headers.referer);
6549
+ appId = confirmAppId(refererId);
6550
+ }
6551
+ return appId;
6552
+ }
6553
+ function parseAppIdFromUrl(url) {
6554
+ if (!url) {
6555
+ return;
6556
+ }
6557
+ return url.split("/").find((subPath) => subPath.startsWith(APP_PREFIX2));
6558
+ }
6559
+ function openJwt(token) {
6560
+ if (!token) {
6561
+ return token;
6562
+ }
6563
+ try {
6564
+ return jwt.verify(token, environment_default.JWT_SECRET);
6565
+ } catch (e) {
6566
+ if (environment_default.JWT_SECRET_FALLBACK) {
6567
+ return jwt.verify(token, environment_default.JWT_SECRET_FALLBACK);
6568
+ } else {
6569
+ throw e;
6570
+ }
6571
+ }
6572
+ }
6573
+ function isValidInternalAPIKey(apiKey) {
6574
+ if (environment_default.INTERNAL_API_KEY && environment_default.INTERNAL_API_KEY === apiKey) {
6575
+ return true;
6576
+ }
6577
+ if (environment_default.INTERNAL_API_KEY_FALLBACK && environment_default.INTERNAL_API_KEY_FALLBACK === apiKey) {
6578
+ return true;
6579
+ }
6580
+ return false;
6581
+ }
6582
+ function getCookie(ctx, name) {
6583
+ const cookie = ctx.cookies.get(name);
6584
+ if (!cookie) {
6585
+ return cookie;
6586
+ }
6587
+ return openJwt(cookie);
6588
+ }
6589
+ function setCookie(ctx, value, name = "builder", opts = { sign: true }) {
6590
+ if (value && opts && opts.sign) {
6591
+ value = jwt.sign(value, environment_default.JWT_SECRET);
6592
+ }
6593
+ const config = {
6594
+ expires: MAX_VALID_DATE,
6595
+ path: "/",
6596
+ httpOnly: false,
6597
+ overwrite: true
6598
+ };
6599
+ if (environment_default.COOKIE_DOMAIN) {
6600
+ config.domain = environment_default.COOKIE_DOMAIN;
6601
+ }
6602
+ ctx.cookies.set(name, value, config);
6603
+ }
6604
+ function clearCookie(ctx, name) {
6605
+ setCookie(ctx, null, name);
6606
+ }
6607
+ function isClient(ctx) {
6608
+ return ctx.headers["x-budibase-type" /* TYPE */] === "client";
6609
+ }
6610
+ function timeout(timeMs) {
6611
+ return new Promise((resolve) => setTimeout(resolve, timeMs));
6612
+ }
6613
+ function isAudited(event) {
6614
+ return !!AuditedEventFriendlyName[event];
6615
+ }
6616
+ function hasCircularStructure(json) {
6617
+ if (typeof json !== "object") {
6618
+ return false;
6619
+ }
6620
+ try {
6621
+ JSON.stringify(json);
6622
+ } catch (err) {
6623
+ if (err instanceof Error && err?.message.includes("circular structure")) {
6624
+ return true;
6625
+ }
6626
+ }
6627
+ return false;
6628
+ }
6629
+
6630
+ // src/utils/stringUtils.ts
6631
+ function validEmail(value) {
6632
+ return value && !!value.match(
6633
+ /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
6634
+ );
6635
+ }
6636
+
6637
+ // src/utils/Duration.ts
6638
+ var DurationType = /* @__PURE__ */ ((DurationType4) => {
6639
+ DurationType4["MILLISECONDS"] = "milliseconds";
6640
+ DurationType4["SECONDS"] = "seconds";
6641
+ DurationType4["MINUTES"] = "minutes";
6642
+ DurationType4["HOURS"] = "hours";
6643
+ DurationType4["DAYS"] = "days";
6644
+ return DurationType4;
6645
+ })(DurationType || {});
6646
+ var conversion = {
6647
+ milliseconds: 1,
6648
+ seconds: 1e3,
6649
+ minutes: 60 * 1e3,
6650
+ hours: 60 * 60 * 1e3,
6651
+ days: 24 * 60 * 60 * 1e3
6652
+ };
6653
+ var Duration = class _Duration {
6654
+ static convert(from, to, duration) {
6655
+ const milliseconds = duration * conversion[from];
6656
+ return milliseconds / conversion[to];
6657
+ }
6658
+ static from(from, duration) {
6659
+ return {
6660
+ to: (to) => {
6661
+ return _Duration.convert(from, to, duration);
6662
+ },
6663
+ toMs: () => {
6664
+ return _Duration.convert(from, "milliseconds" /* MILLISECONDS */, duration);
6665
+ },
6666
+ toSeconds: () => {
6667
+ return _Duration.convert(from, "seconds" /* SECONDS */, duration);
6668
+ }
6669
+ };
6670
+ }
6671
+ static fromSeconds(duration) {
6672
+ return _Duration.from("seconds" /* SECONDS */, duration);
6673
+ }
6674
+ static fromMinutes(duration) {
6675
+ return _Duration.from("minutes" /* MINUTES */, duration);
6676
+ }
6677
+ static fromHours(duration) {
6678
+ return _Duration.from("hours" /* HOURS */, duration);
6679
+ }
6680
+ static fromDays(duration) {
6681
+ return _Duration.from("days" /* DAYS */, duration);
6682
+ }
6683
+ };
6684
+
6685
+ // src/cache/invite.ts
6686
+ init_environment2();
6687
+ init_context2();
6688
+ init_init();
6689
+ var TTL_SECONDS = Duration.fromDays(7).toSeconds();
6690
+ async function updateCode(code, value) {
6691
+ const client = await getInviteClient();
6692
+ await client.store(code, value, TTL_SECONDS);
6693
+ }
6694
+ async function createCode(email, info) {
6695
+ const code = newid();
6696
+ const client = await getInviteClient();
6697
+ await client.store(code, { email, info }, TTL_SECONDS);
6698
+ return code;
6699
+ }
6700
+ async function getCode(code) {
6701
+ const client = await getInviteClient();
6702
+ const value = await client.get(code);
6703
+ if (!value) {
6704
+ throw "Invitation is not valid or has expired, please request a new one.";
6705
+ }
6706
+ return value;
6707
+ }
6708
+ async function deleteCode(code) {
6709
+ const client = await getInviteClient();
6710
+ await client.delete(code);
6711
+ }
6712
+ async function getInviteCodes() {
6713
+ const client = await getInviteClient();
6714
+ const invites = await client.scan();
6715
+ const results = invites.map((invite) => {
6716
+ return {
6717
+ ...invite.value,
6718
+ code: invite.key
6719
+ };
6720
+ });
6721
+ if (!environment_default.MULTI_TENANCY) {
6722
+ return results;
6723
+ }
6724
+ const tenantId = getTenantId();
6725
+ return results.filter((invite) => tenantId === invite.info.tenantId);
6726
+ }
6727
+ async function getExistingInvites(emails) {
6728
+ return (await getInviteCodes()).filter(
6729
+ (invite) => emails.includes(invite.email)
6730
+ );
6731
+ }
6732
+
6733
+ // src/users/lookup.ts
6376
6734
  async function searchExistingEmails(emails) {
6377
6735
  let matchedEmails = [];
6378
6736
  const existingTenantUsers = await getExistingTenantUsers(emails);
@@ -6381,6 +6739,8 @@ async function searchExistingEmails(emails) {
6381
6739
  matchedEmails.push(...existingPlatformUsers.map((user) => user._id));
6382
6740
  const existingAccounts = await getExistingAccounts(emails);
6383
6741
  matchedEmails.push(...existingAccounts.map((account) => account.email));
6742
+ const invitedEmails = await getExistingInvites(emails);
6743
+ matchedEmails.push(...invitedEmails.map((invite) => invite.email));
6384
6744
  return [...new Set(matchedEmails.map((email) => email.toLowerCase()))];
6385
6745
  }
6386
6746
  async function getPlatformUser(identifier) {
@@ -6618,7 +6978,7 @@ __export(applications_exports, {
6618
6978
  getProdAppID: () => getProdAppID2
6619
6979
  });
6620
6980
  init_src();
6621
- var APP_PREFIX2 = prefixed("app" /* APP */);
6981
+ var APP_PREFIX3 = prefixed("app" /* APP */);
6622
6982
  var APP_DEV_PREFIX2 = prefixed("app_dev" /* APP_DEV */);
6623
6983
  function getDevAppID2(appId) {
6624
6984
  if (!appId) {
@@ -6627,9 +6987,9 @@ function getDevAppID2(appId) {
6627
6987
  if (appId.startsWith(APP_DEV_PREFIX2)) {
6628
6988
  return appId;
6629
6989
  }
6630
- const split = appId.split(APP_PREFIX2);
6990
+ const split = appId.split(APP_PREFIX3);
6631
6991
  split.shift();
6632
- const rest = split.join(APP_PREFIX2);
6992
+ const rest = split.join(APP_PREFIX3);
6633
6993
  return `${APP_DEV_PREFIX2}${rest}`;
6634
6994
  }
6635
6995
  function getProdAppID2(appId) {
@@ -6642,7 +7002,7 @@ function getProdAppID2(appId) {
6642
7002
  const split = appId.split(APP_DEV_PREFIX2);
6643
7003
  split.shift();
6644
7004
  const rest = split.join(APP_DEV_PREFIX2);
6645
- return `${APP_PREFIX2}${rest}`;
7005
+ return `${APP_PREFIX3}${rest}`;
6646
7006
  }
6647
7007
 
6648
7008
  // ../shared-core/src/sdk/documents/users.ts
@@ -7359,256 +7719,6 @@ init_utils2();
7359
7719
 
7360
7720
  // src/queue/inMemoryQueue.ts
7361
7721
  var import_events2 = __toESM(require("events"));
7362
-
7363
- // src/utils/index.ts
7364
- var utils_exports3 = {};
7365
- __export(utils_exports3, {
7366
- Duration: () => Duration,
7367
- DurationType: () => DurationType,
7368
- clearCookie: () => clearCookie,
7369
- compare: () => compare,
7370
- getAppIdFromCtx: () => getAppIdFromCtx,
7371
- getCookie: () => getCookie,
7372
- hasCircularStructure: () => hasCircularStructure,
7373
- hash: () => hash,
7374
- isAudited: () => isAudited,
7375
- isClient: () => isClient,
7376
- isPublicApiRequest: () => isPublicApiRequest,
7377
- isServingApp: () => isServingApp,
7378
- isServingBuilder: () => isServingBuilder,
7379
- isServingBuilderPreview: () => isServingBuilderPreview,
7380
- isValidInternalAPIKey: () => isValidInternalAPIKey,
7381
- newid: () => newid,
7382
- openJwt: () => openJwt,
7383
- resolveAppUrl: () => resolveAppUrl,
7384
- setCookie: () => setCookie,
7385
- timeout: () => timeout,
7386
- validEmail: () => validEmail
7387
- });
7388
-
7389
- // src/utils/hashing.ts
7390
- init_environment2();
7391
- init_newid();
7392
- var bcrypt = environment_default.JS_BCRYPT ? require("bcryptjs") : require("bcrypt");
7393
- var SALT_ROUNDS = environment_default.SALT_ROUNDS || 10;
7394
- async function hash(data) {
7395
- const salt = await bcrypt.genSalt(SALT_ROUNDS);
7396
- return bcrypt.hash(data, salt);
7397
- }
7398
- async function compare(data, encrypted) {
7399
- return bcrypt.compare(data, encrypted);
7400
- }
7401
-
7402
- // src/utils/utils.ts
7403
- init_db4();
7404
- init_constants3();
7405
- init_environment2();
7406
- init_context2();
7407
- init_src();
7408
- var jwt = require("jsonwebtoken");
7409
- var APP_PREFIX3 = "app" /* APP */ + SEPARATOR;
7410
- var PROD_APP_PREFIX = "/app/";
7411
- var BUILDER_PREVIEW_PATH = "/app/preview";
7412
- var BUILDER_PREFIX = "/builder";
7413
- var BUILDER_APP_PREFIX = `${BUILDER_PREFIX}/app/`;
7414
- var PUBLIC_API_PREFIX = "/api/public/v";
7415
- function confirmAppId(possibleAppId) {
7416
- return possibleAppId && possibleAppId.startsWith(APP_PREFIX3) ? possibleAppId : void 0;
7417
- }
7418
- async function resolveAppUrl(ctx) {
7419
- const appUrl = ctx.path.split("/")[2];
7420
- let possibleAppUrl = `/${appUrl.toLowerCase()}`;
7421
- let tenantId = getTenantId();
7422
- if (environment_default.MULTI_TENANCY) {
7423
- tenantId = getTenantIDFromCtx(ctx, {
7424
- includeStrategies: ["subdomain" /* SUBDOMAIN */]
7425
- });
7426
- }
7427
- const apps = await doInTenant(
7428
- tenantId,
7429
- () => getAllApps({ dev: false })
7430
- );
7431
- const app = apps.filter(
7432
- (a) => a.url && a.url.toLowerCase() === possibleAppUrl
7433
- )[0];
7434
- return app && app.appId ? app.appId : void 0;
7435
- }
7436
- function isServingApp(ctx) {
7437
- if (ctx.path.startsWith(`/${APP_PREFIX3}`)) {
7438
- return true;
7439
- }
7440
- if (ctx.path.startsWith(PROD_APP_PREFIX)) {
7441
- return true;
7442
- }
7443
- return false;
7444
- }
7445
- function isServingBuilder(ctx) {
7446
- return ctx.path.startsWith(BUILDER_APP_PREFIX);
7447
- }
7448
- function isServingBuilderPreview(ctx) {
7449
- return ctx.path.startsWith(BUILDER_PREVIEW_PATH);
7450
- }
7451
- function isPublicApiRequest(ctx) {
7452
- return ctx.path.startsWith(PUBLIC_API_PREFIX);
7453
- }
7454
- async function getAppIdFromCtx(ctx) {
7455
- const options2 = [ctx.request.headers["x-budibase-app-id" /* APP_ID */]];
7456
- let appId;
7457
- for (let option of options2) {
7458
- appId = confirmAppId(option);
7459
- if (appId) {
7460
- break;
7461
- }
7462
- }
7463
- if (!appId && ctx.request.body && ctx.request.body.appId) {
7464
- appId = confirmAppId(ctx.request.body.appId);
7465
- }
7466
- const pathId = parseAppIdFromUrl(ctx.path);
7467
- if (!appId && pathId) {
7468
- appId = confirmAppId(pathId);
7469
- }
7470
- const isBuilderPreview = ctx.path.startsWith(BUILDER_PREVIEW_PATH);
7471
- const isViewingProdApp = ctx.path.startsWith(PROD_APP_PREFIX) && !isBuilderPreview;
7472
- if (!appId && isViewingProdApp) {
7473
- appId = confirmAppId(await resolveAppUrl(ctx));
7474
- }
7475
- const referer = ctx.request.headers.referer;
7476
- if (!appId && referer?.includes(BUILDER_APP_PREFIX)) {
7477
- const refererId = parseAppIdFromUrl(ctx.request.headers.referer);
7478
- appId = confirmAppId(refererId);
7479
- }
7480
- return appId;
7481
- }
7482
- function parseAppIdFromUrl(url) {
7483
- if (!url) {
7484
- return;
7485
- }
7486
- return url.split("/").find((subPath) => subPath.startsWith(APP_PREFIX3));
7487
- }
7488
- function openJwt(token) {
7489
- if (!token) {
7490
- return token;
7491
- }
7492
- try {
7493
- return jwt.verify(token, environment_default.JWT_SECRET);
7494
- } catch (e) {
7495
- if (environment_default.JWT_SECRET_FALLBACK) {
7496
- return jwt.verify(token, environment_default.JWT_SECRET_FALLBACK);
7497
- } else {
7498
- throw e;
7499
- }
7500
- }
7501
- }
7502
- function isValidInternalAPIKey(apiKey) {
7503
- if (environment_default.INTERNAL_API_KEY && environment_default.INTERNAL_API_KEY === apiKey) {
7504
- return true;
7505
- }
7506
- if (environment_default.INTERNAL_API_KEY_FALLBACK && environment_default.INTERNAL_API_KEY_FALLBACK === apiKey) {
7507
- return true;
7508
- }
7509
- return false;
7510
- }
7511
- function getCookie(ctx, name) {
7512
- const cookie = ctx.cookies.get(name);
7513
- if (!cookie) {
7514
- return cookie;
7515
- }
7516
- return openJwt(cookie);
7517
- }
7518
- function setCookie(ctx, value, name = "builder", opts = { sign: true }) {
7519
- if (value && opts && opts.sign) {
7520
- value = jwt.sign(value, environment_default.JWT_SECRET);
7521
- }
7522
- const config = {
7523
- expires: MAX_VALID_DATE,
7524
- path: "/",
7525
- httpOnly: false,
7526
- overwrite: true
7527
- };
7528
- if (environment_default.COOKIE_DOMAIN) {
7529
- config.domain = environment_default.COOKIE_DOMAIN;
7530
- }
7531
- ctx.cookies.set(name, value, config);
7532
- }
7533
- function clearCookie(ctx, name) {
7534
- setCookie(ctx, null, name);
7535
- }
7536
- function isClient(ctx) {
7537
- return ctx.headers["x-budibase-type" /* TYPE */] === "client";
7538
- }
7539
- function timeout(timeMs) {
7540
- return new Promise((resolve) => setTimeout(resolve, timeMs));
7541
- }
7542
- function isAudited(event) {
7543
- return !!AuditedEventFriendlyName[event];
7544
- }
7545
- function hasCircularStructure(json) {
7546
- if (typeof json !== "object") {
7547
- return false;
7548
- }
7549
- try {
7550
- JSON.stringify(json);
7551
- } catch (err) {
7552
- if (err instanceof Error && err?.message.includes("circular structure")) {
7553
- return true;
7554
- }
7555
- }
7556
- return false;
7557
- }
7558
-
7559
- // src/utils/stringUtils.ts
7560
- function validEmail(value) {
7561
- return value && !!value.match(
7562
- /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
7563
- );
7564
- }
7565
-
7566
- // src/utils/Duration.ts
7567
- var DurationType = /* @__PURE__ */ ((DurationType2) => {
7568
- DurationType2["MILLISECONDS"] = "milliseconds";
7569
- DurationType2["SECONDS"] = "seconds";
7570
- DurationType2["MINUTES"] = "minutes";
7571
- DurationType2["HOURS"] = "hours";
7572
- DurationType2["DAYS"] = "days";
7573
- return DurationType2;
7574
- })(DurationType || {});
7575
- var conversion = {
7576
- milliseconds: 1,
7577
- seconds: 1e3,
7578
- minutes: 60 * 1e3,
7579
- hours: 60 * 60 * 1e3,
7580
- days: 24 * 60 * 60 * 1e3
7581
- };
7582
- var Duration = class _Duration {
7583
- static convert(from, to, duration) {
7584
- const milliseconds = duration * conversion[from];
7585
- return milliseconds / conversion[to];
7586
- }
7587
- static from(from, duration) {
7588
- return {
7589
- to: (to) => {
7590
- return _Duration.convert(from, to, duration);
7591
- },
7592
- toMs: () => {
7593
- return _Duration.convert(from, "milliseconds" /* MILLISECONDS */, duration);
7594
- }
7595
- };
7596
- }
7597
- static fromSeconds(duration) {
7598
- return _Duration.from("seconds" /* SECONDS */, duration);
7599
- }
7600
- static fromMinutes(duration) {
7601
- return _Duration.from("minutes" /* MINUTES */, duration);
7602
- }
7603
- static fromHours(duration) {
7604
- return _Duration.from("hours" /* HOURS */, duration);
7605
- }
7606
- static fromDays(duration) {
7607
- return _Duration.from("days" /* DAYS */, duration);
7608
- }
7609
- };
7610
-
7611
- // src/queue/inMemoryQueue.ts
7612
7722
  function newJob(queue, message) {
7613
7723
  return {
7614
7724
  timestamp: Date.now(),
@@ -10162,7 +10272,7 @@ var UserDB = class _UserDB {
10162
10272
  });
10163
10273
  continue;
10164
10274
  }
10165
- newUser.userGroups = groups;
10275
+ newUser.userGroups = groups || [];
10166
10276
  newUsers.push(newUser);
10167
10277
  if (isCreator2(newUser)) {
10168
10278
  newCreators.push(newUser);
@@ -10395,7 +10505,6 @@ var writethrough_exports = {};
10395
10505
  __export(writethrough_exports, {
10396
10506
  Writethrough: () => Writethrough
10397
10507
  });
10398
- init_base();
10399
10508
  init_init();
10400
10509
  init_logging();
10401
10510
  init_src();
@@ -10501,6 +10610,29 @@ var Writethrough = class {
10501
10610
  }
10502
10611
  };
10503
10612
 
10613
+ // src/cache/passwordReset.ts
10614
+ var passwordReset_exports = {};
10615
+ __export(passwordReset_exports, {
10616
+ createCode: () => createCode2,
10617
+ getCode: () => getCode2
10618
+ });
10619
+ init_init();
10620
+ var TTL_SECONDS2 = Duration.fromHours(1).toSeconds();
10621
+ async function createCode2(userId, info) {
10622
+ const code = newid();
10623
+ const client = await getPasswordResetClient();
10624
+ await client.store(code, { userId, info }, TTL_SECONDS2);
10625
+ return code;
10626
+ }
10627
+ async function getCode2(code) {
10628
+ const client = await getPasswordResetClient();
10629
+ const value = await client.get(code);
10630
+ if (!value) {
10631
+ throw "Provided information is not valid, cannot reset password - please try again.";
10632
+ }
10633
+ return value;
10634
+ }
10635
+
10504
10636
  // src/configs/configs.ts
10505
10637
  init_context2();
10506
10638
  init_environment2();
@@ -12844,6 +12976,7 @@ function init7(processors2) {
12844
12976
 
12845
12977
  // src/index.ts
12846
12978
  init_db4();
12979
+ init_docIds();
12847
12980
  init_context2();
12848
12981
  init_constants3();
12849
12982
  init_db4();
@@ -12896,6 +13029,7 @@ var init8 = (opts = {}) => {
12896
13029
  constants,
12897
13030
  context,
12898
13031
  db,
13032
+ docIds,
12899
13033
  docUpdates,
12900
13034
  encryption,
12901
13035
  env,