@7365admin1/core 3.134.0 → 3.136.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -120,6 +120,8 @@ __export(src_exports, {
120
120
  HID_PERMISSION_CATEGORIES: () => HID_PERMISSION_CATEGORIES,
121
121
  HID_UINT32_MAX: () => HID_UINT32_MAX,
122
122
  HID_UINT64_MAX: () => HID_UINT64_MAX,
123
+ INVITE_EMAIL_EVENTS: () => INVITE_EMAIL_EVENTS,
124
+ INVITE_RESEND_MIN_MINUTES: () => INVITE_RESEND_MIN_MINUTES,
123
125
  LIFT_DOOR: () => LIFT_DOOR,
124
126
  LIFT_LEASE_MS: () => LIFT_LEASE_MS,
125
127
  LIFT_MODE: () => LIFT_MODE,
@@ -528,6 +530,9 @@ __export(src_exports, {
528
530
  incidentReportLog: () => incidentReportLog,
529
531
  incidents_namespace_collection: () => incidents_namespace_collection,
530
532
  invitationOffersRole: () => invitationOffersRole,
533
+ inviteEmailFailureReason: () => inviteEmailFailureReason,
534
+ inviteEmailStatus: () => inviteEmailStatus,
535
+ inviteResendWaitMinutes: () => inviteResendWaitMinutes,
531
536
  isAvailableForWork: () => isAvailableForWork,
532
537
  isCameraEntitled: () => isCameraEntitled,
533
538
  isCrossSiteRole: () => isCrossSiteRole,
@@ -916,6 +921,7 @@ __export(src_exports, {
916
921
  serviceProviderInviteScope: () => serviceProviderInviteScope,
917
922
  serviceProviderInviteViewStatus: () => serviceProviderInviteViewStatus,
918
923
  serviceProviderInviterKind: () => serviceProviderInviterKind,
924
+ serviceProviderNatureFor: () => serviceProviderNatureFor,
919
925
  serviceTypeLabel: () => serviceTypeLabel,
920
926
  sessionSchema: () => sessionSchema,
921
927
  setIO: () => setIO,
@@ -5440,7 +5446,7 @@ function useMemberRepo() {
5440
5446
  throw error;
5441
5447
  }
5442
5448
  }
5443
- async function updateRoleById(_id, role) {
5449
+ async function updateRoleById(_id, role, createdBy) {
5444
5450
  try {
5445
5451
  _id = new import_mongodb14.ObjectId(_id);
5446
5452
  } catch (error) {
@@ -5456,6 +5462,9 @@ function useMemberRepo() {
5456
5462
  role,
5457
5463
  updatedAt: /* @__PURE__ */ new Date()
5458
5464
  };
5465
+ if (createdBy && import_mongodb14.ObjectId.isValid(String(createdBy))) {
5466
+ updateValue.createdBy = new import_mongodb14.ObjectId(String(createdBy));
5467
+ }
5459
5468
  const res = await collection.updateOne({ _id }, { $set: updateValue });
5460
5469
  if (res.modifiedCount === 0) {
5461
5470
  throw new import_node_server_utils15.InternalServerError("Unable to update feedback status.");
@@ -6088,10 +6097,10 @@ function useAuthService() {
6088
6097
 
6089
6098
  // src/controllers/auth.controller.ts
6090
6099
  var import_joi26 = __toESM(require("joi"));
6091
- var import_node_server_utils63 = require("@7365admin1/node-server-utils");
6100
+ var import_node_server_utils64 = require("@7365admin1/node-server-utils");
6092
6101
 
6093
6102
  // src/services/verification.service.ts
6094
- var import_node_server_utils60 = require("@7365admin1/node-server-utils");
6103
+ var import_node_server_utils62 = require("@7365admin1/node-server-utils");
6095
6104
 
6096
6105
  // src/models/verification.model.ts
6097
6106
  var import_node_server_utils19 = require("@7365admin1/node-server-utils");
@@ -9757,6 +9766,64 @@ function filterSiteProviderMembers(items, { type = "", company = "", search = ""
9757
9766
  (m) => (!type || m.type === type) && (!company || m.companyId === company) && (!needle || [m.name, m.email, m.company, m.role, m.invitedVia].map((v) => String(v ?? "").toLowerCase()).some((v) => v.includes(needle)))
9758
9767
  );
9759
9768
  }
9769
+ var INVITE_EMAIL_EVENTS = ["email-sent", "email-failed"];
9770
+ var INVITE_RESEND_MIN_MINUTES = 2;
9771
+ function inviteEmailStatus(history) {
9772
+ const last = [...history ?? []].reverse().find((h) => INVITE_EMAIL_EVENTS.includes(h?.action));
9773
+ if (!last)
9774
+ return null;
9775
+ return last.action === "email-sent" ? { state: "sent", at: String(last.at ?? "") } : { state: "failed", at: String(last.at ?? ""), reason: String(last.reason ?? "unknown error") };
9776
+ }
9777
+ function inviteEmailFailureReason(error) {
9778
+ const code = String(error?.code ?? "").toUpperCase();
9779
+ const smtp = Number(error?.responseCode ?? 0);
9780
+ if (code === "EAUTH")
9781
+ return "the mail server refused Seven365's login";
9782
+ if (code === "ETIMEOUT" || code === "ETIMEDOUT")
9783
+ return "the mail server did not answer in time";
9784
+ if (code === "ECONNECTION" || code === "ESOCKET" || code === "EDNS" || code === "ECONNREFUSED") {
9785
+ return "could not reach the mail server";
9786
+ }
9787
+ if (code === "EENVELOPE" || smtp >= 550 && smtp < 560) {
9788
+ return "the email address was refused - check it is spelled correctly";
9789
+ }
9790
+ if (code === "ETEMPLATE")
9791
+ return "the email could not be prepared";
9792
+ if (smtp >= 400 && smtp < 500)
9793
+ return "the mail server is busy - try Resend in a few minutes";
9794
+ return "the email could not be sent";
9795
+ }
9796
+ function inviteResendWaitMinutes(history, now = /* @__PURE__ */ new Date()) {
9797
+ const last = inviteEmailStatus(history);
9798
+ if (!last?.at)
9799
+ return 0;
9800
+ const at2 = new Date(last.at).getTime();
9801
+ if (!Number.isFinite(at2))
9802
+ return 0;
9803
+ const left = at2 + INVITE_RESEND_MIN_MINUTES * 6e4 - now.getTime();
9804
+ return left > 0 ? Math.ceil(left / 6e4) : 0;
9805
+ }
9806
+ var PROVIDER_NATURE_ALIASES = {
9807
+ security: "security_agency",
9808
+ cleaning: "cleaning_services",
9809
+ hygiene: "cleaning_services",
9810
+ "mechanical-electrical": "mechanical_electrical_services",
9811
+ mechanical_electrical: "mechanical_electrical_services",
9812
+ landscape: "landscaping_services",
9813
+ landscaping: "landscaping_services",
9814
+ "pest-control": "pest_control_services",
9815
+ pest_control: "pest_control_services",
9816
+ pool: "pool_maintenance_services",
9817
+ "pool-mgmt": "pool_maintenance_services",
9818
+ pool_maintenance: "pool_maintenance_services"
9819
+ };
9820
+ function serviceProviderNatureFor(app) {
9821
+ const key = String(app ?? "").trim().toLowerCase();
9822
+ if (!key)
9823
+ return "";
9824
+ const nature = PROVIDER_NATURE_ALIASES[key] ?? key;
9825
+ return nature !== "property_management_agency" && SERVICE_TYPE_LABELS[nature] ? nature : "";
9826
+ }
9760
9827
 
9761
9828
  // src/utils/invite-actor.util.ts
9762
9829
  var import_node_server_utils28 = require("@7365admin1/node-server-utils");
@@ -9844,9 +9911,12 @@ async function hasOrgInvitation(userId, orgId) {
9844
9911
  const invite = await db2.collection("verifications").findOne(
9845
9912
  {
9846
9913
  email,
9847
- type: {
9848
- $in: ["user-invite", "member-invite", "service-provider-invite"]
9849
- },
9914
+ // NOT a service-provider invitation: its `metadata.org` is the company
9915
+ // that SENT it, and the invited company joins as its OWN organisation
9916
+ // (accept / accept-new-company). Counting it let an invited provider
9917
+ // enrol into the client's organisation - on production an invitee became
9918
+ // an owner of the property manager that invited them (2026-09-30).
9919
+ type: { $in: ["user-invite", "member-invite"] },
9850
9920
  "metadata.org": { $in: orgCandidates }
9851
9921
  },
9852
9922
  // addresses are stored as typed, so the comparison is case-insensitive.
@@ -9872,7 +9942,8 @@ async function invitationOffersRole(userId, orgId, roleId) {
9872
9942
  const invites = await db2.collection("verifications").find(
9873
9943
  {
9874
9944
  email,
9875
- type: { $in: ["user-invite", "member-invite", "service-provider-invite"] },
9945
+ // not a service-provider invitation - see `hasOrgInvitation`
9946
+ type: { $in: ["user-invite", "member-invite"] },
9876
9947
  "metadata.org": { $in: [org, new import_mongodb26.ObjectId(org)] },
9877
9948
  status: { $ne: "cancelled" }
9878
9949
  },
@@ -9928,11 +9999,27 @@ async function siteInvitedPins(db2, userId, memberships) {
9928
9999
  const keys = /* @__PURE__ */ new Set();
9929
10000
  if (!pinned.length || !import_mongodb26.ObjectId.isValid(userId))
9930
10001
  return keys;
10002
+ const both3 = (ids2) => [...new Set(ids2.map(String))].flatMap((v) => [v, new import_mongodb26.ObjectId(v)]);
10003
+ const wanted = new Set(pinned.map((m) => pinKey(m.org, m.siteId)));
10004
+ const placed = await db2.collection("members").find(
10005
+ {
10006
+ user: new import_mongodb26.ObjectId(userId),
10007
+ status: { $ne: "deleted" },
10008
+ org: { $in: both3(pinned.map((m) => m.org)) },
10009
+ siteId: { $in: both3(pinned.map((m) => m.siteId)) },
10010
+ createdBy: { $type: "objectId", $ne: new import_mongodb26.ObjectId(userId) }
10011
+ },
10012
+ { projection: { org: 1, siteId: 1 } }
10013
+ ).toArray();
10014
+ for (const row of placed) {
10015
+ const key = pinKey(row.org, row.siteId);
10016
+ if (wanted.has(key))
10017
+ keys.add(key);
10018
+ }
9931
10019
  const user = await db2.collection("users").findOne({ _id: new import_mongodb26.ObjectId(userId) }, { projection: { email: 1 } });
9932
10020
  const email = String(user?.email ?? "").trim();
9933
10021
  if (!email)
9934
10022
  return keys;
9935
- const both3 = (ids2) => [...new Set(ids2.map(String))].flatMap((v) => [v, new import_mongodb26.ObjectId(v)]);
9936
10023
  const invites = await db2.collection("verifications").find(
9937
10024
  {
9938
10025
  email,
@@ -9948,7 +10035,6 @@ async function siteInvitedPins(db2, userId, memberships) {
9948
10035
  collation: { locale: "en", strength: 2 }
9949
10036
  }
9950
10037
  ).toArray();
9951
- const wanted = new Set(pinned.map((m) => pinKey(m.org, m.siteId)));
9952
10038
  for (const invite of invites) {
9953
10039
  const key = pinKey(invite.metadata?.org, invite.metadata?.siteId);
9954
10040
  if (wanted.has(key))
@@ -10072,8 +10158,8 @@ async function holdsRole(userId, roleId, orgId) {
10072
10158
  }
10073
10159
 
10074
10160
  // src/services/service-provider-invite.service.ts
10075
- var import_node_server_utils59 = require("@7365admin1/node-server-utils");
10076
- var import_mongodb55 = require("mongodb");
10161
+ var import_node_server_utils61 = require("@7365admin1/node-server-utils");
10162
+ var import_mongodb56 = require("mongodb");
10077
10163
 
10078
10164
  // src/services/notification.service.ts
10079
10165
  var import_iservice365_expo_notifications = require("iservice365-expo-notifications");
@@ -10967,14 +11053,14 @@ function button(href, label2) {
10967
11053
  }
10968
11054
  function notificationEmail(p) {
10969
11055
  const pause = p.lastThisHour ? `You have had ${EMAIL_PER_MODULE_PER_HOUR} ${p.moduleLabel} emails in the last hour, so we will not email you about ${p.moduleLabel} again for up to an hour. Everything still arrives in the app.` : "";
10970
- const why = `You are getting this because you turned on email for ${p.moduleLabel} in your iService365 notification settings.`;
11056
+ const why = p.why || `You are getting this because you turned on email for ${p.moduleLabel} in your iService365 notification settings.`;
10971
11057
  const html = frame(`<h2 style="font-size:26px;font-weight:400;margin:0 0 20px">${escapeHtml(p.title)}</h2>
10972
11058
  <p style="font-size:16px;line-height:1.5">${escapeHtml(p.body)}</p>
10973
11059
  ${button(p.openUrl, "Open in iService365")}
10974
11060
  ${pause ? `<p style="font-size:14px;color:#ddd">${escapeHtml(pause)}</p>` : ""}
10975
11061
  <hr style="border:none;border-top:1px solid #333;margin:30px 0 20px" />
10976
11062
  <p style="font-size:13px;color:#bbb"><strong>Why am I getting this?</strong> ${escapeHtml(why)}</p>
10977
- <p style="font-size:13px"><a href="${escapeHtml(p.offUrl)}" style="color:#6cb4ff">Turn off email for ${escapeHtml(p.moduleLabel)}</a> &nbsp;\xB7&nbsp; <a href="${escapeHtml(p.settingsUrl)}" style="color:#6cb4ff">Notification settings</a></p>`);
11063
+ <p style="font-size:13px">${p.offUrl ? `<a href="${escapeHtml(p.offUrl)}" style="color:#6cb4ff">Turn off email for ${escapeHtml(p.moduleLabel)}</a> &nbsp;\xB7&nbsp; ` : ""}<a href="${escapeHtml(p.settingsUrl)}" style="color:#6cb4ff">Notification settings</a></p>`);
10978
11064
  const text2 = [
10979
11065
  p.title,
10980
11066
  "",
@@ -10984,7 +11070,7 @@ ${pause ? `<p style="font-size:14px;color:#ddd">${escapeHtml(pause)}</p>` : ""}
10984
11070
  ...pause ? ["", pause] : [],
10985
11071
  "",
10986
11072
  `Why am I getting this? ${why}`,
10987
- `Turn off email for ${p.moduleLabel}: ${p.offUrl}`,
11073
+ ...p.offUrl ? [`Turn off email for ${p.moduleLabel}: ${p.offUrl}`] : [],
10988
11074
  `Notification settings: ${p.settingsUrl}`
10989
11075
  ].join("\n");
10990
11076
  return { subject: p.title, html, text: text2 };
@@ -12518,6 +12604,8 @@ async function sendNotificationEmails(job) {
12518
12604
  return 0;
12519
12605
  const { emailRecipients, takeEmailSlot } = useNotificationPreferenceRepo();
12520
12606
  const all = await emailRecipients(job.userIds, category.key);
12607
+ if (job.required)
12608
+ return sendRequiredEmails(job, category, all.map((r) => r.userId));
12521
12609
  if (!all.length)
12522
12610
  return 0;
12523
12611
  if (all.length > MAX_RECIPIENTS_PER_NOTIFICATION) {
@@ -12560,6 +12648,36 @@ async function sendNotificationEmails(job) {
12560
12648
  }
12561
12649
  return sent;
12562
12650
  }
12651
+ async function sendRequiredEmails(job, category, optedIn) {
12652
+ const skip = new Set(optedIn);
12653
+ const ids2 = job.userIds.filter((id) => !skip.has(String(id)) && import_mongodb34.ObjectId.isValid(String(id))).slice(0, MAX_RECIPIENTS_PER_NOTIFICATION).map((id) => new import_mongodb34.ObjectId(String(id)));
12654
+ if (!ids2.length)
12655
+ return 0;
12656
+ const users = await import_node_server_utils39.useAtlas.getDb()?.collection("users").find({ _id: { $in: ids2 }, deletedAt: { $in: [null, ""] } }, { projection: { email: 1 } }).toArray();
12657
+ const base = webBase();
12658
+ const openUrl = openLink(base, {
12659
+ kind: String(job.data?.params?.kind || notificationKind(job.data, category.key) || ""),
12660
+ id: String(job.data?.params?.id ?? "")
12661
+ });
12662
+ const message = notificationEmail({
12663
+ title: job.title,
12664
+ body: job.body,
12665
+ moduleLabel: category.label,
12666
+ openUrl,
12667
+ settingsUrl: `${base}/notification-settings`,
12668
+ why: job.required?.why
12669
+ });
12670
+ let sent = 0;
12671
+ for (const u of users ?? []) {
12672
+ const to = String(u.email ?? "").trim();
12673
+ if (!to)
12674
+ continue;
12675
+ const tag2 = `user=${u._id} category=${category.key} required`;
12676
+ if (await deliverEmail(to, message, tag2) === "sent")
12677
+ sent++;
12678
+ }
12679
+ return sent;
12680
+ }
12563
12681
  var queue = [];
12564
12682
  var draining = null;
12565
12683
  async function drain() {
@@ -12719,20 +12837,25 @@ var NotificationService = class {
12719
12837
  const staff = await staffRecipients();
12720
12838
  if (!staff.length)
12721
12839
  return;
12722
- await send(
12723
- staff,
12724
- "Service provider invitation to approve",
12725
- payload.subcontract ? `${payload.inviterOrgName} (a service provider) wants to bring in another service provider at ${payload.siteName}.` : `${payload.inviterOrgName} invited a service provider to ${payload.siteName}.`,
12726
- {
12727
- module: "platform",
12728
- kind: "spApproval",
12729
- screen: "",
12730
- params: { id: payload.invitationId.toString() }
12731
- },
12732
- false,
12733
- void 0,
12734
- "platform"
12735
- );
12840
+ const title = "Service provider invitation to approve";
12841
+ const body = `${payload.subcontract ? `${payload.inviterOrgName} (a service provider) wants to bring in another service provider at ${payload.siteName}.` : `${payload.inviterOrgName} invited a service provider to ${payload.siteName}.`} The invitation email is sent only after a Seven365 administrator approves it in SP Approvals.`;
12842
+ const data = {
12843
+ module: "platform",
12844
+ kind: "spApproval",
12845
+ screen: "",
12846
+ params: { id: payload.invitationId.toString() }
12847
+ };
12848
+ await send(staff, title, body, data, false, void 0, "platform");
12849
+ queueNotificationEmails({
12850
+ userIds: staff,
12851
+ category: "platform",
12852
+ title,
12853
+ body,
12854
+ data: withNotificationKind(data, "platform"),
12855
+ required: {
12856
+ why: "You are getting this because you are a Seven365 administrator and this invitation waits for your approval."
12857
+ }
12858
+ });
12736
12859
  }
12737
12860
  static async clientInvitationAccepted(payload) {
12738
12861
  const staff = await staffRecipients();
@@ -24220,8 +24343,570 @@ async function requireRoleAssigner(req, orgId) {
24220
24343
  throw new import_node_server_utils58.UnauthorizedError("You do not have permission to assign roles.");
24221
24344
  }
24222
24345
 
24346
+ // src/repositories/role.repo.ts
24347
+ var import_node_server_utils59 = require("@7365admin1/node-server-utils");
24348
+ var import_mongodb55 = require("mongodb");
24349
+ var HISTORY_LIMIT = 10;
24350
+ function useRoleRepo() {
24351
+ const db2 = import_node_server_utils59.useAtlas.getDb();
24352
+ if (!db2) {
24353
+ throw new import_node_server_utils59.InternalServerError("Unable to connect to server.");
24354
+ }
24355
+ const namespace_collection3 = "roles";
24356
+ const collection = db2.collection(namespace_collection3);
24357
+ async function createIndex() {
24358
+ try {
24359
+ await collection.createIndexes([
24360
+ { key: { name: 1 } },
24361
+ { key: { type: 1 } },
24362
+ { key: { status: 1 } }
24363
+ ]);
24364
+ } catch (error) {
24365
+ throw new import_node_server_utils59.InternalServerError("Failed to create index on role.");
24366
+ }
24367
+ }
24368
+ async function createTextIndex() {
24369
+ try {
24370
+ await collection.createIndex({ name: "text" });
24371
+ } catch (error) {
24372
+ throw new import_node_server_utils59.InternalServerError("Failed to create text index on role.");
24373
+ }
24374
+ }
24375
+ async function createUniqueIndex() {
24376
+ try {
24377
+ await collection.createIndex(
24378
+ { name: 1, type: 1, org: 1, deletedAt: 1 },
24379
+ { unique: true }
24380
+ );
24381
+ } catch (error) {
24382
+ throw new import_node_server_utils59.InternalServerError("Failed to create unique index on role.");
24383
+ }
24384
+ }
24385
+ const { delNamespace, setCache, getCache } = (0, import_node_server_utils59.useCache)(namespace_collection3);
24386
+ const { delNamespace: _delDashboardNameSpace } = (0, import_node_server_utils59.useCache)("dashboard");
24387
+ async function nameTakenMessage(value) {
24388
+ const name = String(value?.name ?? "");
24389
+ const fallback = `A role named "${name}" already exists in this organisation. Please choose a different name.`;
24390
+ try {
24391
+ const clash = await collection.findOne(
24392
+ {
24393
+ name,
24394
+ type: value.type,
24395
+ org: value.org,
24396
+ deletedAt: value.deletedAt ?? ""
24397
+ },
24398
+ { projection: { site: 1 } }
24399
+ );
24400
+ if (!clash)
24401
+ return fallback;
24402
+ if (!clash.site) {
24403
+ return `A role named "${name}" already exists for the whole organisation. Role names must be different across the organisation and all its sites - please choose a different name.`;
24404
+ }
24405
+ const siteName = async (id) => {
24406
+ if (!id)
24407
+ return "";
24408
+ const doc = await db2?.collection("sites").findOne(
24409
+ { _id: new import_mongodb55.ObjectId(String(id)) },
24410
+ { projection: { name: 1 } }
24411
+ ).catch(() => null);
24412
+ return doc?.name ?? "";
24413
+ };
24414
+ if (value.site && String(value.site) === String(clash.site)) {
24415
+ return `A role named "${name}" already exists at this site. Please choose a different name.`;
24416
+ }
24417
+ const [there, here] = await Promise.all([siteName(clash.site), siteName(value.site)]);
24418
+ const suggestion = here ? `, for example "${name} - ${here}"` : "";
24419
+ return `A role named "${name}" already exists in this organisation${there ? ` at ${there}` : " at another site"}. Role names must be different across all your sites - please choose a different name${suggestion}.`;
24420
+ } catch {
24421
+ return fallback;
24422
+ }
24423
+ }
24424
+ async function addRole(value, session) {
24425
+ value = new MRole({ ...value, type: serviceAppType(value.type) });
24426
+ try {
24427
+ const res = await collection.insertOne(value, { session });
24428
+ delNamespace().then(() => {
24429
+ import_node_server_utils59.logger.info(`Cache cleared for namespace: ${namespace_collection3}`);
24430
+ }).catch((err) => {
24431
+ import_node_server_utils59.logger.error(
24432
+ `Failed to clear cache for namespace: ${namespace_collection3}`,
24433
+ err
24434
+ );
24435
+ });
24436
+ return res.insertedId;
24437
+ } catch (error) {
24438
+ import_node_server_utils59.logger.log({ level: "error", message: `${error}` });
24439
+ const isDuplicated = error.message.includes("duplicate");
24440
+ if (isDuplicated) {
24441
+ throw new import_node_server_utils59.BadRequestError(await nameTakenMessage(value));
24442
+ }
24443
+ throw new import_node_server_utils59.InternalServerError("Failed to create role.");
24444
+ }
24445
+ }
24446
+ async function getRoleByUserId(value) {
24447
+ try {
24448
+ value = new import_mongodb55.ObjectId(value);
24449
+ } catch (error) {
24450
+ throw new import_node_server_utils59.BadRequestError("Invalid user ID format.");
24451
+ }
24452
+ try {
24453
+ const cacheKey = (0, import_node_server_utils59.makeCacheKey)(namespace_collection3, { user: value });
24454
+ const cachedData = await getCache(cacheKey);
24455
+ if (cachedData) {
24456
+ import_node_server_utils59.logger.info(`Cache hit for key: ${cacheKey}`);
24457
+ return cachedData;
24458
+ }
24459
+ const data = await collection.findOne({ user: value });
24460
+ setCache(cacheKey, data, 15 * 60).then(() => {
24461
+ import_node_server_utils59.logger.info(`Cache set for key: ${cacheKey}`);
24462
+ }).catch((err) => {
24463
+ import_node_server_utils59.logger.error(`Failed to set cache for key: ${cacheKey}`, err);
24464
+ });
24465
+ return data;
24466
+ } catch (error) {
24467
+ throw new import_node_server_utils59.InternalServerError("Failed to retrieve role by user ID.");
24468
+ }
24469
+ }
24470
+ async function getOwnerRolesByTypeOrg(type, org) {
24471
+ try {
24472
+ org = new import_mongodb55.ObjectId(org);
24473
+ } catch (error) {
24474
+ throw new import_node_server_utils59.BadRequestError("Invalid organization ID format.");
24475
+ }
24476
+ const cacheOptions = {
24477
+ org: org.toString(),
24478
+ permissions: "*",
24479
+ type,
24480
+ status: "active"
24481
+ };
24482
+ const cacheKey = (0, import_node_server_utils59.makeCacheKey)(namespace_collection3, cacheOptions);
24483
+ const cachedData = await getCache(cacheKey);
24484
+ if (cachedData) {
24485
+ import_node_server_utils59.logger.info(`Cache hit for key: ${cacheKey}`);
24486
+ return cachedData;
24487
+ }
24488
+ try {
24489
+ const data = await collection.find({ org, permissions: "*", type: appTypeQuery(type), status: "active" }).toArray();
24490
+ setCache(cacheKey, data, 15 * 60).then(() => {
24491
+ import_node_server_utils59.logger.info(`Cache set for key: ${cacheKey}`);
24492
+ }).catch((err) => {
24493
+ import_node_server_utils59.logger.error(`Failed to set cache for key: ${cacheKey}`, err);
24494
+ });
24495
+ return data;
24496
+ } catch (error) {
24497
+ throw new import_node_server_utils59.InternalServerError("Failed to retrieve role by user ID.");
24498
+ }
24499
+ }
24500
+ async function getRoleById(_id) {
24501
+ try {
24502
+ _id = new import_mongodb55.ObjectId(_id);
24503
+ } catch (error) {
24504
+ throw new import_node_server_utils59.BadRequestError("Invalid role ID format.");
24505
+ }
24506
+ try {
24507
+ const cacheKey = (0, import_node_server_utils59.makeCacheKey)(namespace_collection3, { _id });
24508
+ const cachedData = await getCache(cacheKey);
24509
+ if (cachedData) {
24510
+ import_node_server_utils59.logger.info(`Cache hit for key: ${cacheKey}`);
24511
+ return cachedData;
24512
+ }
24513
+ const data = await collection.findOne({ _id });
24514
+ setCache(cacheKey, data, 15 * 60).then(() => {
24515
+ import_node_server_utils59.logger.info(`Cache set for key: ${cacheKey}`);
24516
+ }).catch((err) => {
24517
+ import_node_server_utils59.logger.error(`Failed to set cache for key: ${cacheKey}`, err);
24518
+ });
24519
+ return data;
24520
+ } catch (error) {
24521
+ throw new import_node_server_utils59.InternalServerError("Failed to retrieve role by ID.");
24522
+ }
24523
+ }
24524
+ async function getRoleByName(name, type) {
24525
+ if (!name) {
24526
+ throw new import_node_server_utils59.BadRequestError("Role name is required.");
24527
+ }
24528
+ type = serviceAppType(type);
24529
+ const query2 = type ? { name, type: appTypeQuery(type) } : { name };
24530
+ try {
24531
+ const cacheKey = (0, import_node_server_utils59.makeCacheKey)(namespace_collection3, query2);
24532
+ const cachedData = await getCache(cacheKey);
24533
+ if (cachedData) {
24534
+ import_node_server_utils59.logger.info(`Cache hit for key: ${cacheKey}`);
24535
+ return cachedData;
24536
+ }
24537
+ const data = await collection.findOne(query2);
24538
+ setCache(cacheKey, data, 15 * 60).then(() => {
24539
+ import_node_server_utils59.logger.info(`Cache set for key: ${cacheKey}`);
24540
+ }).catch((err) => {
24541
+ import_node_server_utils59.logger.error(`Failed to set cache for key: ${cacheKey}`, err);
24542
+ });
24543
+ return data;
24544
+ } catch (error) {
24545
+ throw new import_node_server_utils59.InternalServerError("Failed to retrieve role by name.");
24546
+ }
24547
+ }
24548
+ async function getRoles({
24549
+ search = "",
24550
+ page: page2 = 1,
24551
+ limit = 10,
24552
+ sort = {},
24553
+ type = "",
24554
+ org = "",
24555
+ site = "",
24556
+ scope = ""
24557
+ }) {
24558
+ page2 = page2 > 0 ? page2 - 1 : 0;
24559
+ if (org && typeof org === "string" && org.length === 24) {
24560
+ try {
24561
+ org = new import_mongodb55.ObjectId(org);
24562
+ } catch (error) {
24563
+ throw new import_node_server_utils59.BadRequestError("Invalid organization ID format.");
24564
+ }
24565
+ }
24566
+ if (site && typeof site === "string" && site.length === 24) {
24567
+ try {
24568
+ site = new import_mongodb55.ObjectId(site);
24569
+ } catch (error) {
24570
+ throw new import_node_server_utils59.BadRequestError("Invalid site ID format.");
24571
+ }
24572
+ }
24573
+ const query2 = { status: "active", org };
24574
+ const cacheOptions = { status: "active", org };
24575
+ if (site) {
24576
+ query2.$or = [{ site }, { site: "" }, { site: null }];
24577
+ cacheOptions.site = site.toString();
24578
+ } else if (scope === "organisation") {
24579
+ query2.site = { $in: ["", null] };
24580
+ cacheOptions.scope = scope;
24581
+ }
24582
+ sort = Object.keys(sort).length > 0 ? sort : { _id: -1 };
24583
+ cacheOptions.sort = JSON.stringify(sort);
24584
+ if (search) {
24585
+ query2.$text = { $search: search };
24586
+ cacheOptions.search = search;
24587
+ }
24588
+ if (type) {
24589
+ query2.type = appTypeQuery(type);
24590
+ cacheOptions.type = serviceAppType(type);
24591
+ }
24592
+ const cacheKey = (0, import_node_server_utils59.makeCacheKey)(namespace_collection3, cacheOptions);
24593
+ const cachedData = await getCache(cacheKey);
24594
+ if (cachedData) {
24595
+ import_node_server_utils59.logger.info(`Cache hit for key: ${cacheKey}`);
24596
+ return cachedData;
24597
+ }
24598
+ try {
24599
+ const items = await collection.aggregate([
24600
+ { $match: query2 },
24601
+ { $sort: sort },
24602
+ { $skip: page2 * limit },
24603
+ { $limit: limit }
24604
+ ]).toArray();
24605
+ const length = await collection.countDocuments(query2);
24606
+ const data = (0, import_node_server_utils59.paginate)(items, page2, limit, length);
24607
+ setCache(cacheKey, data, 15 * 60).then(() => {
24608
+ import_node_server_utils59.logger.info(`Cache set for key: ${cacheKey}`);
24609
+ }).catch((err) => {
24610
+ import_node_server_utils59.logger.error(`Failed to set cache for key: ${cacheKey}`, err);
24611
+ });
24612
+ return data;
24613
+ } catch (error) {
24614
+ import_node_server_utils59.logger.log({ level: "error", message: `${error}` });
24615
+ throw error;
24616
+ }
24617
+ }
24618
+ async function getActiveByTypeOrg(type, org) {
24619
+ try {
24620
+ if (typeof org === "string" && org) {
24621
+ org = new import_mongodb55.ObjectId(org);
24622
+ }
24623
+ } catch (error) {
24624
+ throw new import_node_server_utils59.BadRequestError("Invalid organization ID format.");
24625
+ }
24626
+ try {
24627
+ return await collection.find({ status: "active", type, org }).sort({ name: 1 }).toArray();
24628
+ } catch (error) {
24629
+ throw new import_node_server_utils59.InternalServerError("Failed to retrieve replacement roles.");
24630
+ }
24631
+ }
24632
+ async function assertNotSeededOwnerRole(_id) {
24633
+ const role = await collection.findOne(
24634
+ { _id },
24635
+ { projection: { type: 1, default: 1 } }
24636
+ );
24637
+ if (role?.type === PLATFORM_STAFF_ROLE_TYPE && role?.default === true) {
24638
+ throw new import_node_server_utils59.BadRequestError(
24639
+ "The Seven365 platform owner role cannot be edited or deleted."
24640
+ );
24641
+ }
24642
+ }
24643
+ async function updateRole(_id, value, session) {
24644
+ if (!_id) {
24645
+ throw new import_node_server_utils59.BadRequestError("Role ID is required.");
24646
+ }
24647
+ try {
24648
+ _id = new import_mongodb55.ObjectId(_id);
24649
+ } catch (error) {
24650
+ throw new import_node_server_utils59.BadRequestError("Invalid role ID format.");
24651
+ }
24652
+ await assertNotSeededOwnerRole(_id);
24653
+ if (!value.name) {
24654
+ delete value.name;
24655
+ }
24656
+ if (!value.permissions) {
24657
+ delete value.permissions;
24658
+ }
24659
+ if (value.name || value.permissions) {
24660
+ try {
24661
+ await collection.updateOne({ _id }, { $set: value }, { session });
24662
+ delNamespace().then(() => {
24663
+ import_node_server_utils59.logger.info(`Cache cleared for namespace: ${namespace_collection3}`);
24664
+ }).catch((err) => {
24665
+ import_node_server_utils59.logger.error(
24666
+ `Failed to clear cache for namespace: ${namespace_collection3}`,
24667
+ err
24668
+ );
24669
+ });
24670
+ return _id;
24671
+ } catch (error) {
24672
+ const isDuplicated = error.message.includes("duplicate");
24673
+ if (isDuplicated) {
24674
+ const current = await collection.findOne({ _id }, { projection: { type: 1, org: 1, site: 1, deletedAt: 1 } }).catch(() => null);
24675
+ throw new import_node_server_utils59.BadRequestError(
24676
+ await nameTakenMessage({ ...current ?? {}, name: value.name })
24677
+ );
24678
+ }
24679
+ throw new import_node_server_utils59.InternalServerError("Failed to update role.");
24680
+ }
24681
+ } else {
24682
+ throw new import_node_server_utils59.BadRequestError("No fields to update.");
24683
+ }
24684
+ }
24685
+ async function updatePermissionsById(_id, permissions, session) {
24686
+ if (!_id) {
24687
+ throw new import_node_server_utils59.BadRequestError("Role ID is required.");
24688
+ }
24689
+ try {
24690
+ _id = new import_mongodb55.ObjectId(_id);
24691
+ } catch (error) {
24692
+ throw new import_node_server_utils59.BadRequestError("Invalid role ID format.");
24693
+ }
24694
+ await assertNotSeededOwnerRole(_id);
24695
+ if (!permissions) {
24696
+ throw new import_node_server_utils59.BadRequestError("Permissions are required.");
24697
+ }
24698
+ if (permissions.length === 0) {
24699
+ throw new import_node_server_utils59.BadRequestError("Permissions cannot be empty.");
24700
+ }
24701
+ try {
24702
+ await collection.updateOne(
24703
+ { _id },
24704
+ { $set: { permissions } },
24705
+ { session }
24706
+ );
24707
+ delNamespace().then(() => {
24708
+ import_node_server_utils59.logger.info(`Cache cleared for namespace: ${namespace_collection3}`);
24709
+ }).catch((err) => {
24710
+ import_node_server_utils59.logger.error(
24711
+ `Failed to clear cache for namespace: ${namespace_collection3}`,
24712
+ err
24713
+ );
24714
+ });
24715
+ return "Successfully updated role permissions.";
24716
+ } catch (error) {
24717
+ throw new import_node_server_utils59.InternalServerError("Failed to update role permissions.");
24718
+ }
24719
+ }
24720
+ async function appendPermissionHistory(_id, entry, session) {
24721
+ try {
24722
+ _id = new import_mongodb55.ObjectId(_id);
24723
+ } catch (error) {
24724
+ throw new import_node_server_utils59.BadRequestError("Invalid role ID format.");
24725
+ }
24726
+ await collection.updateOne(
24727
+ { _id },
24728
+ // cast: the collection's generic has no `permissionHistory` in its
24729
+ // `PushOperator`, because the field is new and optional. The operator
24730
+ // itself is ordinary `$push`/`$slice`.
24731
+ { $push: { permissionHistory: { $each: [entry], $slice: -HISTORY_LIMIT } } },
24732
+ { session }
24733
+ );
24734
+ delNamespace().then(() => {
24735
+ import_node_server_utils59.logger.info(`Cache cleared for namespace: ${namespace_collection3}`);
24736
+ }).catch((err) => {
24737
+ import_node_server_utils59.logger.error(
24738
+ `Failed to clear cache for namespace: ${namespace_collection3}`,
24739
+ err
24740
+ );
24741
+ });
24742
+ }
24743
+ async function deleteRole(_id, session) {
24744
+ try {
24745
+ _id = new import_mongodb55.ObjectId(_id);
24746
+ } catch (error) {
24747
+ throw new import_node_server_utils59.BadRequestError("Invalid role ID format.");
24748
+ }
24749
+ await assertNotSeededOwnerRole(_id);
24750
+ try {
24751
+ const updateValue = {
24752
+ status: "deleted",
24753
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
24754
+ deletedAt: (/* @__PURE__ */ new Date()).toISOString()
24755
+ };
24756
+ const res = await collection.updateOne(
24757
+ { _id },
24758
+ { $set: updateValue },
24759
+ { session }
24760
+ );
24761
+ if (res.modifiedCount === 0) {
24762
+ throw new import_node_server_utils59.InternalServerError("Unable to update role status.");
24763
+ }
24764
+ delNamespace().then(() => {
24765
+ import_node_server_utils59.logger.info(`Cache cleared for namespace: ${namespace_collection3}`);
24766
+ }).catch((err) => {
24767
+ import_node_server_utils59.logger.error(
24768
+ `Failed to clear cache for namespace: ${namespace_collection3}`,
24769
+ err
24770
+ );
24771
+ });
24772
+ _delDashboardNameSpace().then(() => {
24773
+ import_node_server_utils59.logger.info(`Cache cleared for namespace: dashboard`);
24774
+ }).catch((err) => {
24775
+ import_node_server_utils59.logger.error(`Failed to clear cache for namespace: dashboard`, err);
24776
+ });
24777
+ return "Successfully deleted role permission.";
24778
+ } catch (error) {
24779
+ throw new import_node_server_utils59.InternalServerError("Failed to delete role.");
24780
+ }
24781
+ }
24782
+ return {
24783
+ createIndex,
24784
+ createTextIndex,
24785
+ createUniqueIndex,
24786
+ addRole,
24787
+ getRoleByUserId,
24788
+ getRoleById,
24789
+ getRoleByName,
24790
+ getRoles,
24791
+ getActiveByTypeOrg,
24792
+ updateRole,
24793
+ updatePermissionsById,
24794
+ appendPermissionHistory,
24795
+ deleteRole,
24796
+ getOwnerRolesByTypeOrg
24797
+ };
24798
+ }
24799
+
24800
+ // src/utils/org-default-roles.util.ts
24801
+ var import_node_server_utils60 = require("@7365admin1/node-server-utils");
24802
+ var APP_SERVICE_TYPES = new Set(
24803
+ Object.values(AppServiceType)
24804
+ );
24805
+ var ADMINISTRATOR_PERMISSIONS = Object.freeze([
24806
+ "members:view-members",
24807
+ "members:assign-member-role",
24808
+ "members:suspend-member",
24809
+ "members:activate-member",
24810
+ "members:delete-member",
24811
+ "invitations:create-invitation",
24812
+ "invitations:view-invitations",
24813
+ "invitations:cancel-invitation",
24814
+ "roles:see-all-roles",
24815
+ "roles:see-role-details",
24816
+ "roles:add-role",
24817
+ "roles:update-role",
24818
+ "roles:delete-role"
24819
+ ]);
24820
+ function defaultOwnerRoles(org, nature, appOwnerPermissions) {
24821
+ if (!org?.toString())
24822
+ return [];
24823
+ const owner = {
24824
+ org,
24825
+ name: "owner",
24826
+ permissions: ["*"],
24827
+ default: true
24828
+ };
24829
+ const administrator = {
24830
+ org,
24831
+ name: "Administrator",
24832
+ permissions: [...ADMINISTRATOR_PERMISSIONS],
24833
+ default: true
24834
+ };
24835
+ const roles = [
24836
+ { ...owner, type: "organization" },
24837
+ { ...administrator, type: "organization" }
24838
+ ];
24839
+ const appType = serviceAppType(nature);
24840
+ if (appType && APP_SERVICE_TYPES.has(appType)) {
24841
+ roles.push({
24842
+ ...owner,
24843
+ type: appType,
24844
+ ...appOwnerPermissions?.length && { permissions: [...appOwnerPermissions] }
24845
+ });
24846
+ roles.push({ ...administrator, type: appType });
24847
+ }
24848
+ return roles;
24849
+ }
24850
+ async function seedDefaultOrgRoles(addRole, org, nature, appOwnerPermissions) {
24851
+ const seeded = [];
24852
+ try {
24853
+ for (const role of defaultOwnerRoles(org, nature, appOwnerPermissions)) {
24854
+ const id = await addRole(role);
24855
+ if (id)
24856
+ seeded.push({ type: String(role.type), id: String(id), name: role.name });
24857
+ }
24858
+ } catch (error) {
24859
+ import_node_server_utils60.logger.log({
24860
+ level: "error",
24861
+ message: `Failed to seed default roles for organization ${org}: ${error?.message}`
24862
+ });
24863
+ }
24864
+ return seeded;
24865
+ }
24866
+ async function healOrgDefaultRoles(deps, org) {
24867
+ if (!org?.toString())
24868
+ return 0;
24869
+ let claimed = false;
24870
+ try {
24871
+ claimed = await deps.claim(org);
24872
+ if (!claimed)
24873
+ return 0;
24874
+ const existing = await deps.countRoles(org);
24875
+ if (existing > 0) {
24876
+ import_node_server_utils60.logger.log({
24877
+ level: "info",
24878
+ message: `Default role self-heal skipped for organization ${org}: ${existing} role(s) already present`
24879
+ });
24880
+ return 0;
24881
+ }
24882
+ const roles = defaultOwnerRoles(org, await deps.getNature(org));
24883
+ for (const role of roles) {
24884
+ await deps.addRole(role);
24885
+ }
24886
+ import_node_server_utils60.logger.log({
24887
+ level: "info",
24888
+ message: `Seeded ${roles.length} default role(s) for existing organization ${org} on read`
24889
+ });
24890
+ return roles.length;
24891
+ } catch (error) {
24892
+ if (claimed) {
24893
+ await deps.release(org).catch(() => void 0);
24894
+ }
24895
+ import_node_server_utils60.logger.log({
24896
+ level: "error",
24897
+ message: `Failed to self-heal default roles for organization ${org}: ${error?.message}`
24898
+ });
24899
+ return 0;
24900
+ }
24901
+ }
24902
+ function clientInviteOwnerPermissionsOn(env2 = process.env) {
24903
+ const value = String(env2.CLIENT_INVITE_OWNER_PERMISSIONS ?? "on").trim().toLowerCase();
24904
+ return !["off", "false", "0", "no"].includes(value);
24905
+ }
24906
+
24223
24907
  // src/services/service-provider-invite.service.ts
24224
24908
  var INVITE_VALID_HOURS = 72;
24909
+ var INVITE_EMAIL_TIMEOUT_MS = 3e4;
24225
24910
  async function siteClientOrgs(siteId, pmMemberOrgIds = []) {
24226
24911
  const site = await useSiteRepo().getSiteById(siteId).catch(() => null);
24227
24912
  const owner = String(site?.orgId ?? site?.org ?? "");
@@ -24259,11 +24944,11 @@ async function assertServiceProviderInviteScope(actor, orgId, siteId) {
24259
24944
  )
24260
24945
  });
24261
24946
  if (!scope.ok)
24262
- throw new import_node_server_utils59.UnauthorizedError(scope.reason);
24947
+ throw new import_node_server_utils61.UnauthorizedError(scope.reason);
24263
24948
  return { sender: scope.sender, siteOwnerOrg };
24264
24949
  }
24265
24950
  function useServiceProviderInviteService() {
24266
- const mailer2 = new import_node_server_utils59.useMailer({
24951
+ const mailer2 = new import_node_server_utils61.useMailer({
24267
24952
  host: MAILER_TRANSPORT_HOST,
24268
24953
  port: MAILER_TRANSPORT_PORT,
24269
24954
  secure: MAILER_TRANSPORT_SECURE,
@@ -24277,13 +24962,18 @@ function useServiceProviderInviteService() {
24277
24962
  getServiceProviderInvites: _list,
24278
24963
  countServiceProviderInvitesAwaitingApproval: _countAwaiting
24279
24964
  } = useVerificationRepoV2();
24280
- const { getById: getOrgById, getByEmail: getOrgByEmail } = useOrgRepo();
24965
+ const {
24966
+ getById: getOrgById,
24967
+ getByEmail: getOrgByEmail,
24968
+ add: addOrg,
24969
+ getByCreatorAndNature
24970
+ } = useOrgRepo();
24281
24971
  const { getUserByEmail, getUserById } = useUserRepo();
24282
24972
  function event(action, actor, reason) {
24283
24973
  return {
24284
24974
  action,
24285
24975
  at: (/* @__PURE__ */ new Date()).toISOString(),
24286
- by: actor.id ? new import_mongodb55.ObjectId(actor.id) : null,
24976
+ by: actor.id ? new import_mongodb56.ObjectId(actor.id) : null,
24287
24977
  byName: actor.name,
24288
24978
  ...reason ? { reason } : {}
24289
24979
  };
@@ -24319,36 +25009,81 @@ function useServiceProviderInviteService() {
24319
25009
  });
24320
25010
  }
24321
25011
  } catch (error) {
24322
- import_node_server_utils59.logger.log({
25012
+ import_node_server_utils61.logger.log({
24323
25013
  level: "error",
24324
25014
  message: `Invitation in-app notification failed: ${error?.message}`
24325
25015
  });
24326
25016
  }
24327
- const filePath = (0, import_node_server_utils59.getDirectory)(
24328
- __dirname,
24329
- `./public/handlebars/${invite.type}`
25017
+ let result;
25018
+ let timer;
25019
+ try {
25020
+ let emailContent;
25021
+ try {
25022
+ const filePath = (0, import_node_server_utils61.getDirectory)(
25023
+ __dirname,
25024
+ `./public/handlebars/${invite.type}`
25025
+ );
25026
+ emailContent = (0, import_node_server_utils61.compileHandlebar)({
25027
+ context: {
25028
+ email,
25029
+ orgName,
25030
+ siteName,
25031
+ validity: VERIFICATION_USER_INVITE_DURATION,
25032
+ link: `${APP_MAIN}/verify/service-provider-invite/${inviteId}`
25033
+ },
25034
+ filePath
25035
+ });
25036
+ } catch (error) {
25037
+ throw Object.assign(new Error("template"), { code: "ETEMPLATE" });
25038
+ }
25039
+ await Promise.race([
25040
+ mailer2.sendMail({
25041
+ to: email,
25042
+ subject: invite.type === SERVICE_PROVIDER_SIGN_IN_TYPE ? SERVICE_PROVIDER_SIGN_IN_SUBJECT : SERVICE_PROVIDER_SIGN_UP_SUBJECT,
25043
+ html: emailContent,
25044
+ sender: "iService365" /* ISERVICE365 */
25045
+ }),
25046
+ new Promise((_, reject2) => {
25047
+ timer = setTimeout(
25048
+ () => reject2(Object.assign(new Error("timeout"), { code: "ETIMEOUT" })),
25049
+ INVITE_EMAIL_TIMEOUT_MS
25050
+ );
25051
+ })
25052
+ ]);
25053
+ result = { sent: true };
25054
+ import_node_server_utils61.logger.log({ level: "info", message: `[sp-invite-email] sent invite=${inviteId}` });
25055
+ } catch (error) {
25056
+ result = { sent: false, reason: inviteEmailFailureReason(error) };
25057
+ import_node_server_utils61.logger.log({
25058
+ level: "error",
25059
+ message: `[sp-invite-email] failed invite=${inviteId} code=${error?.code || error?.responseCode || error?.name || "error"}`
25060
+ });
25061
+ } finally {
25062
+ if (timer)
25063
+ clearTimeout(timer);
25064
+ }
25065
+ await _transition({
25066
+ id: inviteId,
25067
+ from: [],
25068
+ event: {
25069
+ action: result.sent ? "email-sent" : "email-failed",
25070
+ at: (/* @__PURE__ */ new Date()).toISOString(),
25071
+ by: null,
25072
+ byName: "system",
25073
+ ...result.reason ? { reason: result.reason } : {}
25074
+ }
25075
+ }).catch(
25076
+ (error) => import_node_server_utils61.logger.log({
25077
+ level: "error",
25078
+ message: `[sp-invite-email] could not record the result invite=${inviteId}: ${error?.name || "error"}`
25079
+ })
24330
25080
  );
24331
- const emailContent = (0, import_node_server_utils59.compileHandlebar)({
24332
- context: {
24333
- email,
24334
- orgName,
24335
- siteName,
24336
- validity: VERIFICATION_USER_INVITE_DURATION,
24337
- link: `${APP_MAIN}/verify/service-provider-invite/${inviteId}`
24338
- },
24339
- filePath
24340
- });
24341
- await mailer2.sendMail({
24342
- to: email,
24343
- subject: invite.type === SERVICE_PROVIDER_SIGN_IN_TYPE ? SERVICE_PROVIDER_SIGN_IN_SUBJECT : SERVICE_PROVIDER_SIGN_UP_SUBJECT,
24344
- html: emailContent,
24345
- sender: "iService365" /* ISERVICE365 */
24346
- });
25081
+ return result;
24347
25082
  }
24348
25083
  async function load(id) {
24349
25084
  const invite = await _getById(id);
24350
25085
  if (!invite)
24351
- throw new import_node_server_utils59.NotFoundError("Invitation not found.");
25086
+ throw new import_node_server_utils61.NotFoundError("Invitation not found.");
24352
25087
  return invite;
24353
25088
  }
24354
25089
  function assertOwnerOrSuperAdmin(actor, invite) {
@@ -24357,13 +25092,13 @@ function useServiceProviderInviteService() {
24357
25092
  const org = inviteOrgId(invite);
24358
25093
  if (org && actor.orgIds.includes(org))
24359
25094
  return;
24360
- throw new import_node_server_utils59.UnauthorizedError(
25095
+ throw new import_node_server_utils61.UnauthorizedError(
24361
25096
  "This invitation belongs to another organisation."
24362
25097
  );
24363
25098
  }
24364
25099
  function assertSuperAdmin(actor) {
24365
25100
  if (!staffMayBypass(actor)) {
24366
- throw new import_node_server_utils59.UnauthorizedError(
25101
+ throw new import_node_server_utils61.UnauthorizedError(
24367
25102
  "Only a Seven365 administrator can approve or reject invitations."
24368
25103
  );
24369
25104
  }
@@ -24371,12 +25106,12 @@ function useServiceProviderInviteService() {
24371
25106
  function assertAllowed(action, invite) {
24372
25107
  const refusal = refuseServiceProviderInviteAction(action, invite.status);
24373
25108
  if (refusal)
24374
- throw new import_node_server_utils59.BadRequestError(refusal);
25109
+ throw new import_node_server_utils61.BadRequestError(refusal);
24375
25110
  }
24376
25111
  async function actorFor(userId) {
24377
25112
  const actor = await resolveInviteActor(userId);
24378
25113
  if (!actor.id)
24379
- throw new import_node_server_utils59.UnauthorizedError("Sign in to continue.");
25114
+ throw new import_node_server_utils61.UnauthorizedError("Sign in to continue.");
24380
25115
  return actor;
24381
25116
  }
24382
25117
  async function listApprovals({
@@ -24439,7 +25174,8 @@ function useServiceProviderInviteService() {
24439
25174
  inviterOrgName,
24440
25175
  inviterKind: fromClient ? "property-management" : serviceProviderInviterKind(inviterOrg, siteOwnerOrg),
24441
25176
  siteOwnerName,
24442
- service: String(invite.metadata?.app ?? "")
25177
+ service: String(invite.metadata?.app ?? ""),
25178
+ emailStatus: inviteEmailStatus(invite.history)
24443
25179
  };
24444
25180
  })
24445
25181
  );
@@ -24457,7 +25193,7 @@ function useServiceProviderInviteService() {
24457
25193
  let scope = actor.orgIds;
24458
25194
  if (orgId) {
24459
25195
  if (!staffMayBypass(actor) && !actor.orgIds.includes(orgId)) {
24460
- throw new import_node_server_utils59.UnauthorizedError(
25196
+ throw new import_node_server_utils61.UnauthorizedError(
24461
25197
  "You are not a member of that organisation."
24462
25198
  );
24463
25199
  }
@@ -24546,7 +25282,9 @@ function useServiceProviderInviteService() {
24546
25282
  answeredAt: status === "expired" ? invite.expireAt ?? null : status === "pending" || status === "awaiting-approval" /* AWAITING_APPROVAL */ ? null : answer?.at ?? invite.updatedAt ?? null,
24547
25283
  contactEmail,
24548
25284
  contactPhone,
24549
- onSite
25285
+ onSite,
25286
+ // Did the invitation email go out, and if not, why (latest attempt).
25287
+ emailStatus: inviteEmailStatus(history)
24550
25288
  };
24551
25289
  }
24552
25290
  async function approvalCount({ userId }) {
@@ -24578,13 +25316,23 @@ function useServiceProviderInviteService() {
24578
25316
  if (!updated) {
24579
25317
  return { message: "This invitation has already been approved." };
24580
25318
  }
24581
- await notifyProvider(updated);
25319
+ const email = await notifyProvider(updated);
25320
+ if (!email.sent) {
25321
+ return {
25322
+ message: `Invitation approved, but the email could not be sent: ${email.reason}. Use Resend on the invitation to try again.`,
25323
+ emailSent: false,
25324
+ emailError: email.reason
25325
+ };
25326
+ }
24582
25327
  await _transition({
24583
25328
  id,
24584
25329
  from: [],
24585
25330
  event: event("provider-notified", actor)
24586
25331
  });
24587
- return { message: "Invitation approved. The service provider has been notified." };
25332
+ return {
25333
+ message: "Invitation approved. The invitation email has been sent to the service provider.",
25334
+ emailSent: true
25335
+ };
24588
25336
  }
24589
25337
  async function reject({
24590
25338
  id,
@@ -24595,7 +25343,7 @@ function useServiceProviderInviteService() {
24595
25343
  assertSuperAdmin(actor);
24596
25344
  const trimmed = String(reason ?? "").trim();
24597
25345
  if (!trimmed) {
24598
- throw new import_node_server_utils59.BadRequestError(
25346
+ throw new import_node_server_utils61.BadRequestError(
24599
25347
  "Give a reason for not approving this invitation. Whoever sent it will see it."
24600
25348
  );
24601
25349
  }
@@ -24639,6 +25387,12 @@ function useServiceProviderInviteService() {
24639
25387
  const invite = await load(id);
24640
25388
  assertOwnerOrSuperAdmin(actor, invite);
24641
25389
  assertAllowed("resend", invite);
25390
+ const wait = inviteResendWaitMinutes(invite.history);
25391
+ if (wait > 0) {
25392
+ throw new import_node_server_utils61.BadRequestError(
25393
+ `The invitation email was sent moments ago. You can resend it in ${wait} minute${wait === 1 ? "" : "s"}.`
25394
+ );
25395
+ }
24642
25396
  const expireAt = new Date(
24643
25397
  Date.now() + INVITE_VALID_HOURS * 60 * 60 * 1e3
24644
25398
  ).toISOString();
@@ -24649,12 +25403,17 @@ function useServiceProviderInviteService() {
24649
25403
  set: { expireAt }
24650
25404
  });
24651
25405
  if (!updated) {
24652
- throw new import_node_server_utils59.BadRequestError(
25406
+ throw new import_node_server_utils61.BadRequestError(
24653
25407
  refuseServiceProviderInviteAction("resend", invite.status) ?? "This invitation cannot be sent again."
24654
25408
  );
24655
25409
  }
24656
- await notifyProvider(updated);
24657
- return { message: "Invitation sent again." };
25410
+ const email = await notifyProvider(updated);
25411
+ if (!email.sent) {
25412
+ throw new import_node_server_utils61.BadRequestError(
25413
+ `The invitation email could not be sent: ${email.reason}.`
25414
+ );
25415
+ }
25416
+ return { message: "Invitation email sent again.", emailSent: true };
24658
25417
  }
24659
25418
  async function softDelete({ id, userId }) {
24660
25419
  const actor = await actorFor(userId);
@@ -24669,7 +25428,7 @@ function useServiceProviderInviteService() {
24669
25428
  event: event("deleted", actor),
24670
25429
  set: {
24671
25430
  deletedAt: (/* @__PURE__ */ new Date()).toISOString(),
24672
- deletedBy: actor.id ? new import_mongodb55.ObjectId(actor.id) : null
25431
+ deletedBy: actor.id ? new import_mongodb56.ObjectId(actor.id) : null
24673
25432
  }
24674
25433
  });
24675
25434
  if (!updated) {
@@ -24685,7 +25444,7 @@ function useServiceProviderInviteService() {
24685
25444
  );
24686
25445
  const invitedUserId = user?._id?.toString();
24687
25446
  if (!invitedUserId || invitedUserId !== userId) {
24688
- throw new import_node_server_utils59.UnauthorizedError("This invitation was sent to someone else.");
25447
+ throw new import_node_server_utils61.UnauthorizedError("This invitation was sent to someone else.");
24689
25448
  }
24690
25449
  }
24691
25450
  async function decline({ id, userId }) {
@@ -24696,11 +25455,11 @@ function useServiceProviderInviteService() {
24696
25455
  return { message: "You have already declined this invitation." };
24697
25456
  }
24698
25457
  if (invite.deletedAt) {
24699
- throw new import_node_server_utils59.BadRequestError("This invitation is no longer valid.");
25458
+ throw new import_node_server_utils61.BadRequestError("This invitation is no longer valid.");
24700
25459
  }
24701
25460
  assertAllowed("decline", invite);
24702
25461
  if (isServiceProviderInviteExpired(invite)) {
24703
- throw new import_node_server_utils59.BadRequestError(SERVICE_PROVIDER_INVITE_EXPIRED_MESSAGE);
25462
+ throw new import_node_server_utils61.BadRequestError(SERVICE_PROVIDER_INVITE_EXPIRED_MESSAGE);
24704
25463
  }
24705
25464
  const updated = await _transition({
24706
25465
  id,
@@ -24729,16 +25488,16 @@ function useServiceProviderInviteService() {
24729
25488
  if (![SERVICE_PROVIDER_SIGN_IN_TYPE, SERVICE_PROVIDER_SIGN_UP_TYPE].includes(
24730
25489
  String(invite.type ?? "")
24731
25490
  )) {
24732
- throw new import_node_server_utils59.NotFoundError("Invitation not found.");
25491
+ throw new import_node_server_utils61.NotFoundError("Invitation not found.");
24733
25492
  }
24734
25493
  if (invite.status === "awaiting-approval" /* AWAITING_APPROVAL */) {
24735
- throw new import_node_server_utils59.NotFoundError("Invitation not found.");
25494
+ throw new import_node_server_utils61.NotFoundError("Invitation not found.");
24736
25495
  }
24737
25496
  if (invite.deletedAt) {
24738
- throw new import_node_server_utils59.BadRequestError("This invitation is no longer valid. Ask for a new one.");
25497
+ throw new import_node_server_utils61.BadRequestError("This invitation is no longer valid. Ask for a new one.");
24739
25498
  }
24740
25499
  if (isServiceProviderInviteExpired(invite)) {
24741
- throw new import_node_server_utils59.BadRequestError(SERVICE_PROVIDER_INVITE_EXPIRED_MESSAGE);
25500
+ throw new import_node_server_utils61.BadRequestError(SERVICE_PROVIDER_INVITE_EXPIRED_MESSAGE);
24742
25501
  }
24743
25502
  let result;
24744
25503
  try {
@@ -24758,6 +25517,207 @@ function useServiceProviderInviteService() {
24758
25517
  await notifyInviter(invite, true, result.providerOrgName);
24759
25518
  return { message: result.message };
24760
25519
  }
25520
+ async function acceptAsNewCompany({
25521
+ id,
25522
+ userId,
25523
+ companyName,
25524
+ country
25525
+ }) {
25526
+ const actor = await actorFor(userId);
25527
+ const invite = await load(id);
25528
+ await assertInvitedParty(actor.id, invite);
25529
+ if (![SERVICE_PROVIDER_SIGN_IN_TYPE, SERVICE_PROVIDER_SIGN_UP_TYPE].includes(
25530
+ String(invite.type ?? "")
25531
+ ) || invite.status === "awaiting-approval" /* AWAITING_APPROVAL */) {
25532
+ throw new import_node_server_utils61.NotFoundError("Invitation not found.");
25533
+ }
25534
+ if (invite.deletedAt) {
25535
+ throw new import_node_server_utils61.BadRequestError("This invitation is no longer valid. Ask for a new one.");
25536
+ }
25537
+ if (isServiceProviderInviteExpired(invite)) {
25538
+ throw new import_node_server_utils61.BadRequestError(SERVICE_PROVIDER_INVITE_EXPIRED_MESSAGE);
25539
+ }
25540
+ if (invite.status !== "pending" /* PENDING */) {
25541
+ throw new import_node_server_utils61.BadRequestError(
25542
+ refuseServiceProviderInviteAction("accept", invite.status) ?? "This invitation cannot be accepted."
25543
+ );
25544
+ }
25545
+ const nature = serviceProviderNatureFor(invite.metadata?.app);
25546
+ if (!nature) {
25547
+ throw new import_node_server_utils61.BadRequestError(
25548
+ "This invitation does not say which service your company provides. Ask the sender for a new one."
25549
+ );
25550
+ }
25551
+ const existing = await getByCreatorAndNature(actor.id, nature).catch(() => null);
25552
+ if (existing) {
25553
+ throw new import_node_server_utils61.BadRequestError(
25554
+ `You already have a ${serviceTypeLabel(nature)} company on iService365 (${existing.name}). Accept the invitation from that company instead.`
25555
+ );
25556
+ }
25557
+ const user = await getUserById(actor.id).catch(() => null);
25558
+ const name = String(companyName ?? "").trim() || String(invite.metadata?.name ?? "").trim() || String(user?.name ?? "").trim();
25559
+ if (!name)
25560
+ throw new import_node_server_utils61.BadRequestError("Enter your company's name.");
25561
+ const email = String(invite.email ?? "").trim().toLowerCase();
25562
+ const orgId = String(
25563
+ await addOrg({
25564
+ name,
25565
+ type: nature,
25566
+ nature,
25567
+ email,
25568
+ contact: "",
25569
+ country: String(country ?? "").trim(),
25570
+ createdBy: actor.id,
25571
+ invitedFrom: String(invite._id)
25572
+ })
25573
+ );
25574
+ const roles = await seedDefaultOrgRoles(useRoleRepo().addRole, orgId, nature);
25575
+ const appType = serviceAppType(nature);
25576
+ const orgOwner = roles.find((r) => r.type === "organization" && r.name === "owner");
25577
+ const appOwner = roles.find((r) => r.type === appType && r.name === "owner");
25578
+ if (!orgOwner || !appOwner) {
25579
+ import_node_server_utils61.logger.log({
25580
+ level: "error",
25581
+ message: `[sp-invite] new company ${orgId}: owner roles missing after seeding`
25582
+ });
25583
+ throw new import_node_server_utils61.BadRequestError("Your company was created but could not be set up. Contact Seven365.");
25584
+ }
25585
+ const { add: addMember } = useMemberRepo();
25586
+ const base = {
25587
+ org: orgId,
25588
+ orgName: name,
25589
+ user: actor.id,
25590
+ name: String(user?.name || email),
25591
+ email,
25592
+ createdBy: actor.id,
25593
+ // The owner runs the whole company, not one site.
25594
+ siteId: "",
25595
+ siteName: "",
25596
+ onboardingRequired: false
25597
+ };
25598
+ await addMember({ ...base, role: orgOwner.id, type: "organization" });
25599
+ await addMember({ ...base, role: appOwner.id, type: appType });
25600
+ await useUserRepo().updateUserFieldById({ _id: actor.id, field: "defaultOrg", value: orgId }).catch(() => void 0);
25601
+ import_node_server_utils61.logger.log({
25602
+ level: "info",
25603
+ message: `[sp-invite] invite=${id} new company org=${orgId} nature=${nature}`
25604
+ });
25605
+ const result = await accept({ id, userId: actor.id, org: orgId });
25606
+ return {
25607
+ ...result,
25608
+ orgId,
25609
+ nature,
25610
+ siteId: invite.metadata?.siteId?.toString?.() ?? ""
25611
+ };
25612
+ }
25613
+ async function wrongJoinContext(id, userId) {
25614
+ const actor = await actorFor(userId);
25615
+ assertSuperAdmin(actor);
25616
+ const invite = await load(id);
25617
+ if (![SERVICE_PROVIDER_SIGN_IN_TYPE, SERVICE_PROVIDER_SIGN_UP_TYPE].includes(
25618
+ String(invite.type ?? "")
25619
+ )) {
25620
+ throw new import_node_server_utils61.NotFoundError("Invitation not found.");
25621
+ }
25622
+ const senderOrg = inviteOrgId(invite);
25623
+ const user = await getUserByEmail(
25624
+ String(invite.email ?? "").trim().toLowerCase()
25625
+ ).catch(() => null);
25626
+ return { actor, invite, senderOrg, user };
25627
+ }
25628
+ async function wrongJoinRows(invite, senderOrg, user) {
25629
+ const db2 = import_node_server_utils61.useAtlas.getDb();
25630
+ if (!db2 || !user?._id || !import_mongodb56.ObjectId.isValid(senderOrg))
25631
+ return [];
25632
+ const since = new Date(String(invite.createdAt ?? 0)).getTime();
25633
+ const rows = await db2.collection("members").find({
25634
+ user: { $in: [user._id, String(user._id)] },
25635
+ org: { $in: [new import_mongodb56.ObjectId(senderOrg), senderOrg] },
25636
+ status: "active"
25637
+ }).toArray();
25638
+ const recent = rows.filter(
25639
+ (m) => !Number.isFinite(since) || new Date(m.createdAt ?? 0).getTime() >= since
25640
+ );
25641
+ const roleIds = recent.map((m) => m.role?.toString?.() ?? "").filter((r) => import_mongodb56.ObjectId.isValid(r)).map((r) => new import_mongodb56.ObjectId(r));
25642
+ const roles = roleIds.length ? await db2.collection("roles").find({ _id: { $in: roleIds } }, { projection: { name: 1, permissions: 1 } }).toArray() : [];
25643
+ const roleOf = new Map(roles.map((r) => [r._id.toString(), r]));
25644
+ return recent.map((m) => {
25645
+ const role = roleOf.get(m.role?.toString?.() ?? "");
25646
+ return {
25647
+ _id: m._id.toString(),
25648
+ type: String(m.type ?? ""),
25649
+ roleName: String(role?.name ?? ""),
25650
+ fullAccess: Array.isArray(role?.permissions) && role.permissions.includes("*"),
25651
+ siteName: String(m.siteName ?? ""),
25652
+ createdAt: m.createdAt ?? null
25653
+ };
25654
+ });
25655
+ }
25656
+ function lastRepair(invite) {
25657
+ const history = invite.history ?? [];
25658
+ const last = [...history].reverse().find((e) => ["wrong-join-repaired", "wrong-join-undone"].includes(e?.action));
25659
+ return last?.action === "wrong-join-repaired" ? last : null;
25660
+ }
25661
+ async function wrongJoinPreview({ id, userId }) {
25662
+ const { invite, senderOrg, user } = await wrongJoinContext(id, userId);
25663
+ const org = await getOrgById(senderOrg).catch(() => null);
25664
+ const memberships = await wrongJoinRows(invite, senderOrg, user);
25665
+ const repaired = lastRepair(invite);
25666
+ return {
25667
+ invitationId: id,
25668
+ email: String(invite.email ?? ""),
25669
+ account: user?._id ? String(user.name || user.email || "") : "",
25670
+ senderOrgId: senderOrg,
25671
+ senderOrgName: String(org?.name ?? ""),
25672
+ memberships,
25673
+ defaultOrgIsSender: String(user?.defaultOrg ?? "") === senderOrg,
25674
+ repaired: repaired ? { at: repaired.at, by: repaired.byName, memberIds: repaired.memberIds ?? [] } : null
25675
+ };
25676
+ }
25677
+ async function repairWrongJoin({ id, userId }) {
25678
+ const { actor, invite, senderOrg, user } = await wrongJoinContext(id, userId);
25679
+ const rows = await wrongJoinRows(invite, senderOrg, user);
25680
+ if (!rows.length) {
25681
+ return {
25682
+ message: "Nothing to repair: this account is not in the sender's organisation.",
25683
+ changed: 0
25684
+ };
25685
+ }
25686
+ const { updateMemberStatus } = useMemberRepo();
25687
+ for (const row of rows)
25688
+ await updateMemberStatus(row._id, { status: "inactive" });
25689
+ await _transition({
25690
+ id,
25691
+ from: [],
25692
+ event: {
25693
+ ...event("wrong-join-repaired", actor),
25694
+ memberIds: rows.map((row) => row._id)
25695
+ }
25696
+ });
25697
+ import_node_server_utils61.logger.log({
25698
+ level: "info",
25699
+ message: `[sp-invite] wrong-join repaired invite=${id} memberships=${rows.length} by=${actor.id}`
25700
+ });
25701
+ return {
25702
+ message: `Repaired: ${rows.length} membership${rows.length === 1 ? "" : "s"} in the sender's organisation set inactive. Undo is available.`,
25703
+ changed: rows.length
25704
+ };
25705
+ }
25706
+ async function undoWrongJoin({ id, userId }) {
25707
+ const { actor, invite } = await wrongJoinContext(id, userId);
25708
+ const repaired = lastRepair(invite);
25709
+ if (!repaired)
25710
+ throw new import_node_server_utils61.BadRequestError("There is no repair to undo on this invitation.");
25711
+ const { updateMemberStatus } = useMemberRepo();
25712
+ const ids2 = (repaired.memberIds ?? []).filter((m) => import_mongodb56.ObjectId.isValid(m));
25713
+ for (const m of ids2)
25714
+ await updateMemberStatus(m, { status: "active" }).catch(() => 0);
25715
+ await _transition({ id, from: [], event: event("wrong-join-undone", actor) });
25716
+ return {
25717
+ message: `Undone: ${ids2.length} membership${ids2.length === 1 ? "" : "s"} active again.`,
25718
+ changed: ids2.length
25719
+ };
25720
+ }
24761
25721
  async function notifyInviter(invite, accepted, providerName) {
24762
25722
  try {
24763
25723
  const to = invite.metadata?.invitedBy?.toString?.() ?? "";
@@ -24775,7 +25735,7 @@ function useServiceProviderInviteService() {
24775
25735
  siteId: invite.metadata?.siteId?.toString?.()
24776
25736
  });
24777
25737
  } catch (error) {
24778
- import_node_server_utils59.logger.log({
25738
+ import_node_server_utils61.logger.log({
24779
25739
  level: "error",
24780
25740
  message: `Invitation answer notification failed: ${error?.message}`
24781
25741
  });
@@ -24785,11 +25745,11 @@ function useServiceProviderInviteService() {
24785
25745
  const id = String(org ?? "").trim();
24786
25746
  if (!id)
24787
25747
  return "";
24788
- const runs = import_mongodb55.ObjectId.isValid(id) && actor.memberships.some(
25748
+ const runs = import_mongodb56.ObjectId.isValid(id) && actor.memberships.some(
24789
25749
  (m) => m.org === id && (m.companyWideRole === true || !m.siteId && m.orgLevelRole === true)
24790
25750
  );
24791
25751
  if (!runs) {
24792
- throw new import_node_server_utils59.UnauthorizedError(
25752
+ throw new import_node_server_utils61.UnauthorizedError(
24793
25753
  "Only an administrator of that company can answer this invitation for it."
24794
25754
  );
24795
25755
  }
@@ -24906,15 +25866,15 @@ function useServiceProviderInviteService() {
24906
25866
  await assertInvitedParty(actor.id, invite);
24907
25867
  const forOrg = memberOrg(actor, org);
24908
25868
  if (!forOrg) {
24909
- throw new import_node_server_utils59.BadRequestError("Choose the company to add the site to.");
25869
+ throw new import_node_server_utils61.BadRequestError("Choose the company to add the site to.");
24910
25870
  }
24911
25871
  if (![SERVICE_PROVIDER_SIGN_IN_TYPE, SERVICE_PROVIDER_SIGN_UP_TYPE].includes(
24912
25872
  String(invite.type ?? "")
24913
25873
  ) || invite.status === "awaiting-approval" /* AWAITING_APPROVAL */) {
24914
- throw new import_node_server_utils59.NotFoundError("Invitation not found.");
25874
+ throw new import_node_server_utils61.NotFoundError("Invitation not found.");
24915
25875
  }
24916
25876
  if (invite.status !== "complete" /* COMPLETE */ || invite.deletedAt) {
24917
- throw new import_node_server_utils59.BadRequestError(
25877
+ throw new import_node_server_utils61.BadRequestError(
24918
25878
  "Only an accepted invitation can be added to your Sites."
24919
25879
  );
24920
25880
  }
@@ -24923,7 +25883,7 @@ function useServiceProviderInviteService() {
24923
25883
  return { message: "This site is already on your Sites list." };
24924
25884
  }
24925
25885
  if (!state.needsSiteLink) {
24926
- throw new import_node_server_utils59.BadRequestError(
25886
+ throw new import_node_server_utils61.BadRequestError(
24927
25887
  state.providerOrgId && state.providerOrgId !== forOrg ? `This invitation was accepted for ${state.providerOrgName || "another company"}. The site is on that company's Sites list.` : "This site was removed from your Sites list. Ask the client to invite you again."
24928
25888
  );
24929
25889
  }
@@ -24951,7 +25911,7 @@ function useServiceProviderInviteService() {
24951
25911
  const invite = await load(id);
24952
25912
  await assertInvitedParty(actor.id, invite);
24953
25913
  if (invite.status === "awaiting-approval" /* AWAITING_APPROVAL */ || invite.status === "rejected" /* REJECTED */) {
24954
- throw new import_node_server_utils59.NotFoundError("Invitation not found.");
25914
+ throw new import_node_server_utils61.NotFoundError("Invitation not found.");
24955
25915
  }
24956
25916
  const row = await receivedRow(invite, memberOrg(actor, org));
24957
25917
  return { ...row, tab: receivedInviteTab(invite) };
@@ -24967,6 +25927,10 @@ function useServiceProviderInviteService() {
24967
25927
  softDelete,
24968
25928
  decline,
24969
25929
  accept,
25930
+ acceptAsNewCompany,
25931
+ wrongJoinPreview,
25932
+ repairWrongJoin,
25933
+ undoWrongJoin,
24970
25934
  linkSite,
24971
25935
  listReceived,
24972
25936
  getForProvider,
@@ -24975,7 +25939,7 @@ function useServiceProviderInviteService() {
24975
25939
  }
24976
25940
 
24977
25941
  // src/services/verification.service.ts
24978
- var import_mongodb56 = require("mongodb");
25942
+ var import_mongodb57 = require("mongodb");
24979
25943
  function useVerificationService() {
24980
25944
  const MailerConfig = {
24981
25945
  host: MAILER_TRANSPORT_HOST,
@@ -24985,7 +25949,7 @@ function useVerificationService() {
24985
25949
  from: MAILER_FROM_EMAIL,
24986
25950
  password: MAILER_PASSWORD
24987
25951
  };
24988
- const mailer2 = new import_node_server_utils60.useMailer(MailerConfig);
25952
+ const mailer2 = new import_node_server_utils62.useMailer(MailerConfig);
24989
25953
  const {
24990
25954
  add,
24991
25955
  getById: _getById,
@@ -25024,11 +25988,11 @@ function useVerificationService() {
25024
25988
  value.type = type;
25025
25989
  const res = await add(value);
25026
25990
  const dir = __dirname;
25027
- const filePath = (0, import_node_server_utils60.getDirectory)(dir, `./public/handlebars/${type}`);
25991
+ const filePath = (0, import_node_server_utils62.getDirectory)(dir, `./public/handlebars/${type}`);
25028
25992
  const isMember = type === "member-invite";
25029
25993
  const subject = isMember ? "Member Invite" : "User Invite";
25030
25994
  const link = `${APP_MAIN}/verify/${isMember ? "membership" : "invitation"}/${res}`;
25031
- const emailContent = (0, import_node_server_utils60.compileHandlebar)({
25995
+ const emailContent = (0, import_node_server_utils62.compileHandlebar)({
25032
25996
  context: {
25033
25997
  email: userName,
25034
25998
  validity: VERIFICATION_USER_INVITE_DURATION,
@@ -25042,7 +26006,7 @@ function useVerificationService() {
25042
26006
  html: emailContent,
25043
26007
  sender: "iService365"
25044
26008
  }).catch((error) => {
25045
- import_node_server_utils60.logger.log({
26009
+ import_node_server_utils62.logger.log({
25046
26010
  level: "error",
25047
26011
  message: `Error sending user ${type} email: ${error}`
25048
26012
  });
@@ -25096,7 +26060,7 @@ function useVerificationService() {
25096
26060
  const createdId = reissued ? pending._id : await add(value);
25097
26061
  verificationIds.push(createdId.toString());
25098
26062
  const link = `${APP_MAIN}/verify/invitation/${createdId}`;
25099
- const emailContent = (0, import_node_server_utils60.compileHandlebar)({
26063
+ const emailContent = (0, import_node_server_utils62.compileHandlebar)({
25100
26064
  context: {
25101
26065
  email,
25102
26066
  validity: VERIFICATION_USER_INVITE_DURATION,
@@ -25109,7 +26073,7 @@ function useVerificationService() {
25109
26073
  hasPestControl: app === "pest_control_services",
25110
26074
  hasPoolMaintenance: app === "pool_maintenance_services"
25111
26075
  },
25112
- filePath: (0, import_node_server_utils60.getDirectory)(__dirname, "./public/handlebars/user-invite")
26076
+ filePath: (0, import_node_server_utils62.getDirectory)(__dirname, "./public/handlebars/user-invite")
25113
26077
  });
25114
26078
  await mailer2.sendMail({
25115
26079
  to: email,
@@ -25138,7 +26102,7 @@ function useVerificationService() {
25138
26102
  });
25139
26103
  if (existing) {
25140
26104
  if (existing.status === "complete") {
25141
- throw new import_node_server_utils60.BadRequestError(
26105
+ throw new import_node_server_utils62.BadRequestError(
25142
26106
  `User already completed member invite for app: ${metadata.app}`
25143
26107
  );
25144
26108
  }
@@ -25153,13 +26117,13 @@ function useVerificationService() {
25153
26117
  };
25154
26118
  const res = await add(value);
25155
26119
  const link = `${APP_MAIN}/verify/membership/${res}`;
25156
- const emailContent = (0, import_node_server_utils60.compileHandlebar)({
26120
+ const emailContent = (0, import_node_server_utils62.compileHandlebar)({
25157
26121
  context: {
25158
26122
  email,
25159
26123
  validity: VERIFICATION_USER_INVITE_DURATION,
25160
26124
  link
25161
26125
  },
25162
- filePath: (0, import_node_server_utils60.getDirectory)(__dirname, "./public/handlebars/member-invite")
26126
+ filePath: (0, import_node_server_utils62.getDirectory)(__dirname, "./public/handlebars/member-invite")
25163
26127
  });
25164
26128
  await mailer2.sendMail({
25165
26129
  to: email,
@@ -25205,8 +26169,8 @@ function useVerificationService() {
25205
26169
  });
25206
26170
  if (error) {
25207
26171
  const messages = error.details.map((d) => d.message).join(", ");
25208
- import_node_server_utils60.logger.log({ level: "error", message: messages });
25209
- throw new import_node_server_utils60.BadRequestError(`Invalid input: ${error.message}`);
26172
+ import_node_server_utils62.logger.log({ level: "error", message: messages });
26173
+ throw new import_node_server_utils62.BadRequestError(`Invalid input: ${error.message}`);
25210
26174
  }
25211
26175
  let subject = "Service Provider Invite" /* _SERVICE_PROVIDER_INVITE */;
25212
26176
  let type = "service-provider-invite" /* SERVICE_PROVIDER_INVITE */;
@@ -25229,7 +26193,7 @@ function useVerificationService() {
25229
26193
  const org = await getOrgById(orgId);
25230
26194
  const actor = await resolveInviteActor(invitedBy);
25231
26195
  if (!actor.id) {
25232
- throw new import_node_server_utils60.UnauthorizedError("Sign in to continue.");
26196
+ throw new import_node_server_utils62.UnauthorizedError("Sign in to continue.");
25233
26197
  }
25234
26198
  const { sender } = await assertServiceProviderInviteScope(
25235
26199
  actor,
@@ -25254,7 +26218,7 @@ function useVerificationService() {
25254
26218
  inviterIsServiceProvider: sender === "subcontract"
25255
26219
  });
25256
26220
  if (!decision.ok) {
25257
- throw new import_node_server_utils60.BadRequestError(decision.reason);
26221
+ throw new import_node_server_utils62.BadRequestError(decision.reason);
25258
26222
  }
25259
26223
  value.type = decision.type;
25260
26224
  subject = decision.subject;
@@ -25285,7 +26249,7 @@ function useVerificationService() {
25285
26249
  }
25286
26250
  await useServiceProviderInviteService().notifyProvider({
25287
26251
  ...value,
25288
- _id: new import_mongodb56.ObjectId(String(res))
26252
+ _id: new import_mongodb57.ObjectId(String(res))
25289
26253
  });
25290
26254
  return res;
25291
26255
  } catch (error2) {
@@ -25321,8 +26285,8 @@ function useVerificationService() {
25321
26285
  });
25322
26286
  if (error) {
25323
26287
  const messages = error.details.map((d) => d.message).join(", ");
25324
- import_node_server_utils60.logger.log({ level: "error", message: messages });
25325
- throw new import_node_server_utils60.BadRequestError(`Invalid input: ${error.message}`);
26288
+ import_node_server_utils62.logger.log({ level: "error", message: messages });
26289
+ throw new import_node_server_utils62.BadRequestError(`Invalid input: ${error.message}`);
25326
26290
  }
25327
26291
  const subject = "Service Provider Invite" /* _SERVICE_PROVIDER_INVITE */;
25328
26292
  const type = "service-provider-invite" /* SERVICE_PROVIDER_INVITE */;
@@ -25342,12 +26306,12 @@ function useVerificationService() {
25342
26306
  };
25343
26307
  try {
25344
26308
  const id = await _add(value);
25345
- const filePath = (0, import_node_server_utils60.getDirectory)(
26309
+ const filePath = (0, import_node_server_utils62.getDirectory)(
25346
26310
  __dirname,
25347
26311
  `./public/handlebars/${value.type}`
25348
26312
  );
25349
26313
  const link = `${APP_MAIN}/verify/${value.type}/${id}`;
25350
- const emailContent = (0, import_node_server_utils60.compileHandlebar)({
26314
+ const emailContent = (0, import_node_server_utils62.compileHandlebar)({
25351
26315
  context: {
25352
26316
  email,
25353
26317
  app,
@@ -25380,12 +26344,12 @@ function useVerificationService() {
25380
26344
  try {
25381
26345
  const user = await getUserByEmail(email);
25382
26346
  if (!user) {
25383
- throw new import_node_server_utils60.NotFoundError("Email not found");
26347
+ throw new import_node_server_utils62.NotFoundError("Email not found");
25384
26348
  }
25385
26349
  const res = await add(value);
25386
26350
  const dir = __dirname;
25387
- const filePath = (0, import_node_server_utils60.getDirectory)(dir, "./public/handlebars/forget-password");
25388
- const emailContent = (0, import_node_server_utils60.compileHandlebar)({
26351
+ const filePath = (0, import_node_server_utils62.getDirectory)(dir, "./public/handlebars/forget-password");
26352
+ const emailContent = (0, import_node_server_utils62.compileHandlebar)({
25389
26353
  context: {
25390
26354
  userName: user?.name,
25391
26355
  validity: VERIFICATION_FORGET_PASSWORD_DURATION,
@@ -25399,21 +26363,21 @@ function useVerificationService() {
25399
26363
  sender: "iService365",
25400
26364
  html: emailContent
25401
26365
  }).catch((error) => {
25402
- import_node_server_utils60.logger.log({
26366
+ import_node_server_utils62.logger.log({
25403
26367
  level: "error",
25404
26368
  message: `Error sending forget password email: ${error}`
25405
26369
  });
25406
26370
  });
25407
26371
  return "Successfully created a link to reset password. Please check your email.";
25408
26372
  } catch (error) {
25409
- throw new import_node_server_utils60.InternalServerError("Failed to create forget password link.");
26373
+ throw new import_node_server_utils62.InternalServerError("Failed to create forget password link.");
25410
26374
  }
25411
26375
  }
25412
26376
  async function getById(id) {
25413
26377
  try {
25414
26378
  const _id = await _getById(id);
25415
26379
  if (!_id) {
25416
- throw new import_node_server_utils60.NotFoundError("Verification not found.");
26380
+ throw new import_node_server_utils62.NotFoundError("Verification not found.");
25417
26381
  }
25418
26382
  return _id;
25419
26383
  } catch (error) {
@@ -25422,48 +26386,48 @@ function useVerificationService() {
25422
26386
  }
25423
26387
  function errorByType(type, status) {
25424
26388
  if ((type === "user-invite" || type === "member-invite" || type === "service-provider-invite" || type === "service-provider-create-org") && status === "expired") {
25425
- throw new import_node_server_utils60.BadRequestError(
26389
+ throw new import_node_server_utils62.BadRequestError(
25426
26390
  "Invitation has already expired, please contact admin to resend the invitation."
25427
26391
  );
25428
26392
  }
25429
26393
  if (type === "user-sign-up" && status === "expired") {
25430
- throw new import_node_server_utils60.BadRequestError(
26394
+ throw new import_node_server_utils62.BadRequestError(
25431
26395
  "Sign up verification has expired, please sign up again to get a new verification link."
25432
26396
  );
25433
26397
  }
25434
26398
  if ((type === "user-invite" || type === "member-invite" || type === "service-provider-invite" || type === "service-provider-create-org") && status === "complete") {
25435
- throw new import_node_server_utils60.BadRequestError(
26399
+ throw new import_node_server_utils62.BadRequestError(
25436
26400
  "User already registered, please login to continue."
25437
26401
  );
25438
26402
  }
25439
26403
  if (type === "forget-password" && status === "complete") {
25440
- throw new import_node_server_utils60.BadRequestError(
26404
+ throw new import_node_server_utils62.BadRequestError(
25441
26405
  "Forget password verification has already been used, please request a new one."
25442
26406
  );
25443
26407
  }
25444
26408
  if (type === "forget-password" && status === "expired") {
25445
- throw new import_node_server_utils60.BadRequestError(
26409
+ throw new import_node_server_utils62.BadRequestError(
25446
26410
  "Forget password verification has expired, please request a new one."
25447
26411
  );
25448
26412
  }
25449
- throw new import_node_server_utils60.BadRequestError("Invalid verification.");
26413
+ throw new import_node_server_utils62.BadRequestError("Invalid verification.");
25450
26414
  }
25451
26415
  async function verify(id) {
25452
- const session = import_node_server_utils60.useAtlas.getClient()?.startSession();
26416
+ const session = import_node_server_utils62.useAtlas.getClient()?.startSession();
25453
26417
  session?.startTransaction();
25454
26418
  try {
25455
26419
  const _id = await _getById(id);
25456
26420
  if (!_id) {
25457
- throw new import_node_server_utils60.NotFoundError("Verification not found.");
26421
+ throw new import_node_server_utils62.NotFoundError("Verification not found.");
25458
26422
  }
25459
26423
  if (_id.status === "expired") {
25460
26424
  errorByType(_id.type, "expired");
25461
26425
  }
25462
26426
  if (_id.status === "cancelled") {
25463
- throw new import_node_server_utils60.BadRequestError("Verification already cancelled.");
26427
+ throw new import_node_server_utils62.BadRequestError("Verification already cancelled.");
25464
26428
  }
25465
26429
  if (_id.status === "complete") {
25466
- throw new import_node_server_utils60.BadRequestError("Verification already completed.");
26430
+ throw new import_node_server_utils62.BadRequestError("Verification already completed.");
25467
26431
  }
25468
26432
  const expiration = new Date(_id.expireAt).getTime();
25469
26433
  const now = (/* @__PURE__ */ new Date()).getTime();
@@ -25474,7 +26438,7 @@ function useVerificationService() {
25474
26438
  return _id;
25475
26439
  } catch (error) {
25476
26440
  await session?.abortTransaction();
25477
- import_node_server_utils60.logger.log({
26441
+ import_node_server_utils62.logger.log({
25478
26442
  level: "info",
25479
26443
  message: `Error verifying user invitation: ${error}`
25480
26444
  });
@@ -25487,7 +26451,7 @@ function useVerificationService() {
25487
26451
  try {
25488
26452
  await updateStatusById(id, "cancelled");
25489
26453
  } catch (error) {
25490
- throw new import_node_server_utils60.InternalServerError(
26454
+ throw new import_node_server_utils62.InternalServerError(
25491
26455
  `Error cancelling user invitation: ${error}`
25492
26456
  );
25493
26457
  }
@@ -25507,7 +26471,7 @@ function useVerificationService() {
25507
26471
  try {
25508
26472
  const user = await getUserByEmail(email);
25509
26473
  if (user) {
25510
- throw new import_node_server_utils60.BadRequestError(
26474
+ throw new import_node_server_utils62.BadRequestError(
25511
26475
  `Email ${email} is already registered, please login to continue.`
25512
26476
  );
25513
26477
  }
@@ -25521,8 +26485,8 @@ function useVerificationService() {
25521
26485
  };
25522
26486
  const res = await add(value);
25523
26487
  const dir = __dirname;
25524
- const filePath = (0, import_node_server_utils60.getDirectory)(dir, "./public/handlebars/sign-up");
25525
- const emailContent = (0, import_node_server_utils60.compileHandlebar)({
26488
+ const filePath = (0, import_node_server_utils62.getDirectory)(dir, "./public/handlebars/sign-up");
26489
+ const emailContent = (0, import_node_server_utils62.compileHandlebar)({
25526
26490
  context: {
25527
26491
  email,
25528
26492
  validity: "5 minutes",
@@ -25536,7 +26500,7 @@ function useVerificationService() {
25536
26500
  html: emailContent,
25537
26501
  sender: "iService365"
25538
26502
  }).catch((error) => {
25539
- import_node_server_utils60.logger.log({
26503
+ import_node_server_utils62.logger.log({
25540
26504
  level: "error",
25541
26505
  message: `Error sending user invite email: ${error}`
25542
26506
  });
@@ -25557,7 +26521,7 @@ function useVerificationService() {
25557
26521
  }
25558
26522
  return "Successfully checked for expired invitations.";
25559
26523
  } catch (error) {
25560
- import_node_server_utils60.logger.log({
26524
+ import_node_server_utils62.logger.log({
25561
26525
  level: "info",
25562
26526
  message: `Error checking expired user invitation: ${error}`
25563
26527
  });
@@ -25581,459 +26545,7 @@ function useVerificationService() {
25581
26545
  }
25582
26546
 
25583
26547
  // src/services/user.service.ts
25584
- var import_node_server_utils62 = require("@7365admin1/node-server-utils");
25585
-
25586
- // src/repositories/role.repo.ts
25587
- var import_node_server_utils61 = require("@7365admin1/node-server-utils");
25588
- var import_mongodb57 = require("mongodb");
25589
- var HISTORY_LIMIT = 10;
25590
- function useRoleRepo() {
25591
- const db2 = import_node_server_utils61.useAtlas.getDb();
25592
- if (!db2) {
25593
- throw new import_node_server_utils61.InternalServerError("Unable to connect to server.");
25594
- }
25595
- const namespace_collection3 = "roles";
25596
- const collection = db2.collection(namespace_collection3);
25597
- async function createIndex() {
25598
- try {
25599
- await collection.createIndexes([
25600
- { key: { name: 1 } },
25601
- { key: { type: 1 } },
25602
- { key: { status: 1 } }
25603
- ]);
25604
- } catch (error) {
25605
- throw new import_node_server_utils61.InternalServerError("Failed to create index on role.");
25606
- }
25607
- }
25608
- async function createTextIndex() {
25609
- try {
25610
- await collection.createIndex({ name: "text" });
25611
- } catch (error) {
25612
- throw new import_node_server_utils61.InternalServerError("Failed to create text index on role.");
25613
- }
25614
- }
25615
- async function createUniqueIndex() {
25616
- try {
25617
- await collection.createIndex(
25618
- { name: 1, type: 1, org: 1, deletedAt: 1 },
25619
- { unique: true }
25620
- );
25621
- } catch (error) {
25622
- throw new import_node_server_utils61.InternalServerError("Failed to create unique index on role.");
25623
- }
25624
- }
25625
- const { delNamespace, setCache, getCache } = (0, import_node_server_utils61.useCache)(namespace_collection3);
25626
- const { delNamespace: _delDashboardNameSpace } = (0, import_node_server_utils61.useCache)("dashboard");
25627
- async function nameTakenMessage(value) {
25628
- const name = String(value?.name ?? "");
25629
- const fallback = `A role named "${name}" already exists in this organisation. Please choose a different name.`;
25630
- try {
25631
- const clash = await collection.findOne(
25632
- {
25633
- name,
25634
- type: value.type,
25635
- org: value.org,
25636
- deletedAt: value.deletedAt ?? ""
25637
- },
25638
- { projection: { site: 1 } }
25639
- );
25640
- if (!clash)
25641
- return fallback;
25642
- if (!clash.site) {
25643
- return `A role named "${name}" already exists for the whole organisation. Role names must be different across the organisation and all its sites - please choose a different name.`;
25644
- }
25645
- const siteName = async (id) => {
25646
- if (!id)
25647
- return "";
25648
- const doc = await db2?.collection("sites").findOne(
25649
- { _id: new import_mongodb57.ObjectId(String(id)) },
25650
- { projection: { name: 1 } }
25651
- ).catch(() => null);
25652
- return doc?.name ?? "";
25653
- };
25654
- if (value.site && String(value.site) === String(clash.site)) {
25655
- return `A role named "${name}" already exists at this site. Please choose a different name.`;
25656
- }
25657
- const [there, here] = await Promise.all([siteName(clash.site), siteName(value.site)]);
25658
- const suggestion = here ? `, for example "${name} - ${here}"` : "";
25659
- return `A role named "${name}" already exists in this organisation${there ? ` at ${there}` : " at another site"}. Role names must be different across all your sites - please choose a different name${suggestion}.`;
25660
- } catch {
25661
- return fallback;
25662
- }
25663
- }
25664
- async function addRole(value, session) {
25665
- value = new MRole({ ...value, type: serviceAppType(value.type) });
25666
- try {
25667
- const res = await collection.insertOne(value, { session });
25668
- delNamespace().then(() => {
25669
- import_node_server_utils61.logger.info(`Cache cleared for namespace: ${namespace_collection3}`);
25670
- }).catch((err) => {
25671
- import_node_server_utils61.logger.error(
25672
- `Failed to clear cache for namespace: ${namespace_collection3}`,
25673
- err
25674
- );
25675
- });
25676
- return res.insertedId;
25677
- } catch (error) {
25678
- import_node_server_utils61.logger.log({ level: "error", message: `${error}` });
25679
- const isDuplicated = error.message.includes("duplicate");
25680
- if (isDuplicated) {
25681
- throw new import_node_server_utils61.BadRequestError(await nameTakenMessage(value));
25682
- }
25683
- throw new import_node_server_utils61.InternalServerError("Failed to create role.");
25684
- }
25685
- }
25686
- async function getRoleByUserId(value) {
25687
- try {
25688
- value = new import_mongodb57.ObjectId(value);
25689
- } catch (error) {
25690
- throw new import_node_server_utils61.BadRequestError("Invalid user ID format.");
25691
- }
25692
- try {
25693
- const cacheKey = (0, import_node_server_utils61.makeCacheKey)(namespace_collection3, { user: value });
25694
- const cachedData = await getCache(cacheKey);
25695
- if (cachedData) {
25696
- import_node_server_utils61.logger.info(`Cache hit for key: ${cacheKey}`);
25697
- return cachedData;
25698
- }
25699
- const data = await collection.findOne({ user: value });
25700
- setCache(cacheKey, data, 15 * 60).then(() => {
25701
- import_node_server_utils61.logger.info(`Cache set for key: ${cacheKey}`);
25702
- }).catch((err) => {
25703
- import_node_server_utils61.logger.error(`Failed to set cache for key: ${cacheKey}`, err);
25704
- });
25705
- return data;
25706
- } catch (error) {
25707
- throw new import_node_server_utils61.InternalServerError("Failed to retrieve role by user ID.");
25708
- }
25709
- }
25710
- async function getOwnerRolesByTypeOrg(type, org) {
25711
- try {
25712
- org = new import_mongodb57.ObjectId(org);
25713
- } catch (error) {
25714
- throw new import_node_server_utils61.BadRequestError("Invalid organization ID format.");
25715
- }
25716
- const cacheOptions = {
25717
- org: org.toString(),
25718
- permissions: "*",
25719
- type,
25720
- status: "active"
25721
- };
25722
- const cacheKey = (0, import_node_server_utils61.makeCacheKey)(namespace_collection3, cacheOptions);
25723
- const cachedData = await getCache(cacheKey);
25724
- if (cachedData) {
25725
- import_node_server_utils61.logger.info(`Cache hit for key: ${cacheKey}`);
25726
- return cachedData;
25727
- }
25728
- try {
25729
- const data = await collection.find({ org, permissions: "*", type: appTypeQuery(type), status: "active" }).toArray();
25730
- setCache(cacheKey, data, 15 * 60).then(() => {
25731
- import_node_server_utils61.logger.info(`Cache set for key: ${cacheKey}`);
25732
- }).catch((err) => {
25733
- import_node_server_utils61.logger.error(`Failed to set cache for key: ${cacheKey}`, err);
25734
- });
25735
- return data;
25736
- } catch (error) {
25737
- throw new import_node_server_utils61.InternalServerError("Failed to retrieve role by user ID.");
25738
- }
25739
- }
25740
- async function getRoleById(_id) {
25741
- try {
25742
- _id = new import_mongodb57.ObjectId(_id);
25743
- } catch (error) {
25744
- throw new import_node_server_utils61.BadRequestError("Invalid role ID format.");
25745
- }
25746
- try {
25747
- const cacheKey = (0, import_node_server_utils61.makeCacheKey)(namespace_collection3, { _id });
25748
- const cachedData = await getCache(cacheKey);
25749
- if (cachedData) {
25750
- import_node_server_utils61.logger.info(`Cache hit for key: ${cacheKey}`);
25751
- return cachedData;
25752
- }
25753
- const data = await collection.findOne({ _id });
25754
- setCache(cacheKey, data, 15 * 60).then(() => {
25755
- import_node_server_utils61.logger.info(`Cache set for key: ${cacheKey}`);
25756
- }).catch((err) => {
25757
- import_node_server_utils61.logger.error(`Failed to set cache for key: ${cacheKey}`, err);
25758
- });
25759
- return data;
25760
- } catch (error) {
25761
- throw new import_node_server_utils61.InternalServerError("Failed to retrieve role by ID.");
25762
- }
25763
- }
25764
- async function getRoleByName(name, type) {
25765
- if (!name) {
25766
- throw new import_node_server_utils61.BadRequestError("Role name is required.");
25767
- }
25768
- type = serviceAppType(type);
25769
- const query2 = type ? { name, type: appTypeQuery(type) } : { name };
25770
- try {
25771
- const cacheKey = (0, import_node_server_utils61.makeCacheKey)(namespace_collection3, query2);
25772
- const cachedData = await getCache(cacheKey);
25773
- if (cachedData) {
25774
- import_node_server_utils61.logger.info(`Cache hit for key: ${cacheKey}`);
25775
- return cachedData;
25776
- }
25777
- const data = await collection.findOne(query2);
25778
- setCache(cacheKey, data, 15 * 60).then(() => {
25779
- import_node_server_utils61.logger.info(`Cache set for key: ${cacheKey}`);
25780
- }).catch((err) => {
25781
- import_node_server_utils61.logger.error(`Failed to set cache for key: ${cacheKey}`, err);
25782
- });
25783
- return data;
25784
- } catch (error) {
25785
- throw new import_node_server_utils61.InternalServerError("Failed to retrieve role by name.");
25786
- }
25787
- }
25788
- async function getRoles({
25789
- search = "",
25790
- page: page2 = 1,
25791
- limit = 10,
25792
- sort = {},
25793
- type = "",
25794
- org = "",
25795
- site = ""
25796
- }) {
25797
- page2 = page2 > 0 ? page2 - 1 : 0;
25798
- if (org && typeof org === "string" && org.length === 24) {
25799
- try {
25800
- org = new import_mongodb57.ObjectId(org);
25801
- } catch (error) {
25802
- throw new import_node_server_utils61.BadRequestError("Invalid organization ID format.");
25803
- }
25804
- }
25805
- if (site && typeof site === "string" && site.length === 24) {
25806
- try {
25807
- site = new import_mongodb57.ObjectId(site);
25808
- } catch (error) {
25809
- throw new import_node_server_utils61.BadRequestError("Invalid site ID format.");
25810
- }
25811
- }
25812
- const query2 = { status: "active", org };
25813
- const cacheOptions = { status: "active", org };
25814
- if (site) {
25815
- query2.$or = [{ site }, { site: "" }, { site: null }];
25816
- cacheOptions.site = site.toString();
25817
- }
25818
- sort = Object.keys(sort).length > 0 ? sort : { _id: -1 };
25819
- cacheOptions.sort = JSON.stringify(sort);
25820
- if (search) {
25821
- query2.$text = { $search: search };
25822
- cacheOptions.search = search;
25823
- }
25824
- if (type) {
25825
- query2.type = appTypeQuery(type);
25826
- cacheOptions.type = serviceAppType(type);
25827
- }
25828
- const cacheKey = (0, import_node_server_utils61.makeCacheKey)(namespace_collection3, cacheOptions);
25829
- const cachedData = await getCache(cacheKey);
25830
- if (cachedData) {
25831
- import_node_server_utils61.logger.info(`Cache hit for key: ${cacheKey}`);
25832
- return cachedData;
25833
- }
25834
- try {
25835
- const items = await collection.aggregate([
25836
- { $match: query2 },
25837
- { $sort: sort },
25838
- { $skip: page2 * limit },
25839
- { $limit: limit }
25840
- ]).toArray();
25841
- const length = await collection.countDocuments(query2);
25842
- const data = (0, import_node_server_utils61.paginate)(items, page2, limit, length);
25843
- setCache(cacheKey, data, 15 * 60).then(() => {
25844
- import_node_server_utils61.logger.info(`Cache set for key: ${cacheKey}`);
25845
- }).catch((err) => {
25846
- import_node_server_utils61.logger.error(`Failed to set cache for key: ${cacheKey}`, err);
25847
- });
25848
- return data;
25849
- } catch (error) {
25850
- import_node_server_utils61.logger.log({ level: "error", message: `${error}` });
25851
- throw error;
25852
- }
25853
- }
25854
- async function getActiveByTypeOrg(type, org) {
25855
- try {
25856
- if (typeof org === "string" && org) {
25857
- org = new import_mongodb57.ObjectId(org);
25858
- }
25859
- } catch (error) {
25860
- throw new import_node_server_utils61.BadRequestError("Invalid organization ID format.");
25861
- }
25862
- try {
25863
- return await collection.find({ status: "active", type, org }).sort({ name: 1 }).toArray();
25864
- } catch (error) {
25865
- throw new import_node_server_utils61.InternalServerError("Failed to retrieve replacement roles.");
25866
- }
25867
- }
25868
- async function assertNotSeededOwnerRole(_id) {
25869
- const role = await collection.findOne(
25870
- { _id },
25871
- { projection: { type: 1, default: 1 } }
25872
- );
25873
- if (role?.type === PLATFORM_STAFF_ROLE_TYPE && role?.default === true) {
25874
- throw new import_node_server_utils61.BadRequestError(
25875
- "The Seven365 platform owner role cannot be edited or deleted."
25876
- );
25877
- }
25878
- }
25879
- async function updateRole(_id, value, session) {
25880
- if (!_id) {
25881
- throw new import_node_server_utils61.BadRequestError("Role ID is required.");
25882
- }
25883
- try {
25884
- _id = new import_mongodb57.ObjectId(_id);
25885
- } catch (error) {
25886
- throw new import_node_server_utils61.BadRequestError("Invalid role ID format.");
25887
- }
25888
- await assertNotSeededOwnerRole(_id);
25889
- if (!value.name) {
25890
- delete value.name;
25891
- }
25892
- if (!value.permissions) {
25893
- delete value.permissions;
25894
- }
25895
- if (value.name || value.permissions) {
25896
- try {
25897
- await collection.updateOne({ _id }, { $set: value }, { session });
25898
- delNamespace().then(() => {
25899
- import_node_server_utils61.logger.info(`Cache cleared for namespace: ${namespace_collection3}`);
25900
- }).catch((err) => {
25901
- import_node_server_utils61.logger.error(
25902
- `Failed to clear cache for namespace: ${namespace_collection3}`,
25903
- err
25904
- );
25905
- });
25906
- return _id;
25907
- } catch (error) {
25908
- const isDuplicated = error.message.includes("duplicate");
25909
- if (isDuplicated) {
25910
- const current = await collection.findOne({ _id }, { projection: { type: 1, org: 1, site: 1, deletedAt: 1 } }).catch(() => null);
25911
- throw new import_node_server_utils61.BadRequestError(
25912
- await nameTakenMessage({ ...current ?? {}, name: value.name })
25913
- );
25914
- }
25915
- throw new import_node_server_utils61.InternalServerError("Failed to update role.");
25916
- }
25917
- } else {
25918
- throw new import_node_server_utils61.BadRequestError("No fields to update.");
25919
- }
25920
- }
25921
- async function updatePermissionsById(_id, permissions, session) {
25922
- if (!_id) {
25923
- throw new import_node_server_utils61.BadRequestError("Role ID is required.");
25924
- }
25925
- try {
25926
- _id = new import_mongodb57.ObjectId(_id);
25927
- } catch (error) {
25928
- throw new import_node_server_utils61.BadRequestError("Invalid role ID format.");
25929
- }
25930
- await assertNotSeededOwnerRole(_id);
25931
- if (!permissions) {
25932
- throw new import_node_server_utils61.BadRequestError("Permissions are required.");
25933
- }
25934
- if (permissions.length === 0) {
25935
- throw new import_node_server_utils61.BadRequestError("Permissions cannot be empty.");
25936
- }
25937
- try {
25938
- await collection.updateOne(
25939
- { _id },
25940
- { $set: { permissions } },
25941
- { session }
25942
- );
25943
- delNamespace().then(() => {
25944
- import_node_server_utils61.logger.info(`Cache cleared for namespace: ${namespace_collection3}`);
25945
- }).catch((err) => {
25946
- import_node_server_utils61.logger.error(
25947
- `Failed to clear cache for namespace: ${namespace_collection3}`,
25948
- err
25949
- );
25950
- });
25951
- return "Successfully updated role permissions.";
25952
- } catch (error) {
25953
- throw new import_node_server_utils61.InternalServerError("Failed to update role permissions.");
25954
- }
25955
- }
25956
- async function appendPermissionHistory(_id, entry, session) {
25957
- try {
25958
- _id = new import_mongodb57.ObjectId(_id);
25959
- } catch (error) {
25960
- throw new import_node_server_utils61.BadRequestError("Invalid role ID format.");
25961
- }
25962
- await collection.updateOne(
25963
- { _id },
25964
- // cast: the collection's generic has no `permissionHistory` in its
25965
- // `PushOperator`, because the field is new and optional. The operator
25966
- // itself is ordinary `$push`/`$slice`.
25967
- { $push: { permissionHistory: { $each: [entry], $slice: -HISTORY_LIMIT } } },
25968
- { session }
25969
- );
25970
- delNamespace().then(() => {
25971
- import_node_server_utils61.logger.info(`Cache cleared for namespace: ${namespace_collection3}`);
25972
- }).catch((err) => {
25973
- import_node_server_utils61.logger.error(
25974
- `Failed to clear cache for namespace: ${namespace_collection3}`,
25975
- err
25976
- );
25977
- });
25978
- }
25979
- async function deleteRole(_id, session) {
25980
- try {
25981
- _id = new import_mongodb57.ObjectId(_id);
25982
- } catch (error) {
25983
- throw new import_node_server_utils61.BadRequestError("Invalid role ID format.");
25984
- }
25985
- await assertNotSeededOwnerRole(_id);
25986
- try {
25987
- const updateValue = {
25988
- status: "deleted",
25989
- updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
25990
- deletedAt: (/* @__PURE__ */ new Date()).toISOString()
25991
- };
25992
- const res = await collection.updateOne(
25993
- { _id },
25994
- { $set: updateValue },
25995
- { session }
25996
- );
25997
- if (res.modifiedCount === 0) {
25998
- throw new import_node_server_utils61.InternalServerError("Unable to update role status.");
25999
- }
26000
- delNamespace().then(() => {
26001
- import_node_server_utils61.logger.info(`Cache cleared for namespace: ${namespace_collection3}`);
26002
- }).catch((err) => {
26003
- import_node_server_utils61.logger.error(
26004
- `Failed to clear cache for namespace: ${namespace_collection3}`,
26005
- err
26006
- );
26007
- });
26008
- _delDashboardNameSpace().then(() => {
26009
- import_node_server_utils61.logger.info(`Cache cleared for namespace: dashboard`);
26010
- }).catch((err) => {
26011
- import_node_server_utils61.logger.error(`Failed to clear cache for namespace: dashboard`, err);
26012
- });
26013
- return "Successfully deleted role permission.";
26014
- } catch (error) {
26015
- throw new import_node_server_utils61.InternalServerError("Failed to delete role.");
26016
- }
26017
- }
26018
- return {
26019
- createIndex,
26020
- createTextIndex,
26021
- createUniqueIndex,
26022
- addRole,
26023
- getRoleByUserId,
26024
- getRoleById,
26025
- getRoleByName,
26026
- getRoles,
26027
- getActiveByTypeOrg,
26028
- updateRole,
26029
- updatePermissionsById,
26030
- appendPermissionHistory,
26031
- deleteRole,
26032
- getOwnerRolesByTypeOrg
26033
- };
26034
- }
26035
-
26036
- // src/services/user.service.ts
26548
+ var import_node_server_utils63 = require("@7365admin1/node-server-utils");
26037
26549
  var import_multer = require("multer");
26038
26550
  function useUserService() {
26039
26551
  const {
@@ -26060,7 +26572,7 @@ function useUserService() {
26060
26572
  role = result._id?.toString();
26061
26573
  }
26062
26574
  } catch (error) {
26063
- import_node_server_utils62.logger.error(error.message);
26575
+ import_node_server_utils63.logger.error(error.message);
26064
26576
  }
26065
26577
  if (!role) {
26066
26578
  try {
@@ -26072,15 +26584,15 @@ function useUserService() {
26072
26584
  });
26073
26585
  role = result.toString();
26074
26586
  } catch (error) {
26075
- import_node_server_utils62.logger.error(error.message);
26587
+ import_node_server_utils63.logger.error(error.message);
26076
26588
  }
26077
26589
  if (!role) {
26078
- import_node_server_utils62.logger.error("Failed to create default role.");
26590
+ import_node_server_utils63.logger.error("Failed to create default role.");
26079
26591
  }
26080
26592
  }
26081
26593
  let user = "";
26082
26594
  if (!DEFAULT_USER_EMAIL) {
26083
- import_node_server_utils62.logger.error("Default user email is not set.");
26595
+ import_node_server_utils63.logger.error("Default user email is not set.");
26084
26596
  }
26085
26597
  try {
26086
26598
  const result = await getUserByEmail(DEFAULT_USER_EMAIL);
@@ -26088,11 +26600,11 @@ function useUserService() {
26088
26600
  user = result._id?.toString();
26089
26601
  }
26090
26602
  } catch (error) {
26091
- import_node_server_utils62.logger.error(error.message);
26603
+ import_node_server_utils63.logger.error(error.message);
26092
26604
  }
26093
26605
  if (!user) {
26094
26606
  try {
26095
- const password = await (0, import_node_server_utils62.hashPassword)(DEFAULT_USER_PASSWORD);
26607
+ const password = await (0, import_node_server_utils63.hashPassword)(DEFAULT_USER_PASSWORD);
26096
26608
  const userId = await addUser({
26097
26609
  email: DEFAULT_USER_EMAIL,
26098
26610
  password,
@@ -26100,10 +26612,10 @@ function useUserService() {
26100
26612
  });
26101
26613
  user = userId.toString();
26102
26614
  } catch (error) {
26103
- import_node_server_utils62.logger.error(error.message);
26615
+ import_node_server_utils63.logger.error(error.message);
26104
26616
  }
26105
26617
  if (!user) {
26106
- import_node_server_utils62.logger.error("Failed to create default user.");
26618
+ import_node_server_utils63.logger.error("Failed to create default user.");
26107
26619
  }
26108
26620
  }
26109
26621
  try {
@@ -26114,7 +26626,7 @@ function useUserService() {
26114
26626
  type: "admin"
26115
26627
  });
26116
26628
  } catch (error) {
26117
- import_node_server_utils62.logger.error(error.message);
26629
+ import_node_server_utils63.logger.error(error.message);
26118
26630
  }
26119
26631
  }
26120
26632
  async function createUserBySignUp({
@@ -26122,31 +26634,31 @@ function useUserService() {
26122
26634
  name = "",
26123
26635
  password = ""
26124
26636
  } = {}) {
26125
- const session = import_node_server_utils62.useAtlas.getClient()?.startSession();
26637
+ const session = import_node_server_utils63.useAtlas.getClient()?.startSession();
26126
26638
  session?.startTransaction();
26127
26639
  try {
26128
26640
  const signUp = await getVerificationById(id);
26129
26641
  if (!signUp) {
26130
- throw new import_node_server_utils62.BadRequestError("Invalid sign up link.");
26642
+ throw new import_node_server_utils63.BadRequestError("Invalid sign up link.");
26131
26643
  }
26132
26644
  if (signUp.status === "complete") {
26133
- throw new import_node_server_utils62.BadRequestError(
26645
+ throw new import_node_server_utils63.BadRequestError(
26134
26646
  "You have already an account created using this link."
26135
26647
  );
26136
26648
  }
26137
26649
  if (signUp.status === "cancelled") {
26138
- throw new import_node_server_utils62.BadRequestError("This invitation was cancelled.");
26650
+ throw new import_node_server_utils63.BadRequestError("This invitation was cancelled.");
26139
26651
  }
26140
26652
  const expired = new Date(signUp.expireAt) < /* @__PURE__ */ new Date();
26141
26653
  if (signUp.status === "expired" || expired) {
26142
- throw new import_node_server_utils62.BadRequestError("Sign up link expired.");
26654
+ throw new import_node_server_utils63.BadRequestError("Sign up link expired.");
26143
26655
  }
26144
26656
  const email = signUp.email;
26145
26657
  const _user = await getUserByEmail(signUp.email);
26146
26658
  if (_user) {
26147
- throw new import_node_server_utils62.BadRequestError(`User already exists: ${email}.`);
26659
+ throw new import_node_server_utils63.BadRequestError(`User already exists: ${email}.`);
26148
26660
  }
26149
- const hashedPassword = await (0, import_node_server_utils62.hashPassword)(password);
26661
+ const hashedPassword = await (0, import_node_server_utils63.hashPassword)(password);
26150
26662
  let org = null;
26151
26663
  if (signUp.metadata?.org) {
26152
26664
  org = await getOrgById(signUp.metadata.org);
@@ -26195,30 +26707,30 @@ function useUserService() {
26195
26707
  throw error;
26196
26708
  }
26197
26709
  if (newPassword !== passwordConfirmation) {
26198
- throw new import_node_server_utils62.BadRequestError("Passwords do not match.");
26710
+ throw new import_node_server_utils63.BadRequestError("Passwords do not match.");
26199
26711
  }
26200
26712
  let hashedPassword;
26201
- const session = import_node_server_utils62.useAtlas.getClient()?.startSession();
26713
+ const session = import_node_server_utils63.useAtlas.getClient()?.startSession();
26202
26714
  session?.startTransaction();
26203
26715
  try {
26204
- hashedPassword = await (0, import_node_server_utils62.hashPassword)(newPassword);
26716
+ hashedPassword = await (0, import_node_server_utils63.hashPassword)(newPassword);
26205
26717
  } catch (error) {
26206
- throw new import_node_server_utils62.InternalServerError(`Error hashing password: ${error}`);
26718
+ throw new import_node_server_utils63.InternalServerError(`Error hashing password: ${error}`);
26207
26719
  }
26208
26720
  try {
26209
26721
  const otpDoc = await getVerificationById(id);
26210
26722
  if (!otpDoc) {
26211
- throw new import_node_server_utils62.NotFoundError("You are using an invalid reset link.");
26723
+ throw new import_node_server_utils63.NotFoundError("You are using an invalid reset link.");
26212
26724
  }
26213
26725
  if (otpDoc.status === "complete") {
26214
- throw new import_node_server_utils62.BadRequestError("This link has already been invalidated.");
26726
+ throw new import_node_server_utils63.BadRequestError("This link has already been invalidated.");
26215
26727
  }
26216
26728
  const user = await getUserByEmail(otpDoc.email);
26217
26729
  if (!user) {
26218
- throw new import_node_server_utils62.NotFoundError("User not found.");
26730
+ throw new import_node_server_utils63.NotFoundError("User not found.");
26219
26731
  }
26220
26732
  if (!user._id) {
26221
- throw new import_node_server_utils62.InternalServerError("Invalid user ID.");
26733
+ throw new import_node_server_utils63.InternalServerError("Invalid user ID.");
26222
26734
  }
26223
26735
  await updateStatusById(id, "complete", session);
26224
26736
  await _resetPassword(
@@ -26238,7 +26750,7 @@ function useUserService() {
26238
26750
  session?.endSession();
26239
26751
  }
26240
26752
  }
26241
- const s3 = new import_node_server_utils62.useS3({
26753
+ const s3 = new import_node_server_utils63.useS3({
26242
26754
  accessKeyId: SPACES_ACCESS_KEY,
26243
26755
  secretAccessKey: SPACES_SECRET_KEY,
26244
26756
  endpoint: SPACES_ENDPOINT,
@@ -26251,7 +26763,7 @@ function useUserService() {
26251
26763
  user,
26252
26764
  previousProfile
26253
26765
  }) {
26254
- const session = import_node_server_utils62.useAtlas.getClient()?.startSession();
26766
+ const session = import_node_server_utils63.useAtlas.getClient()?.startSession();
26255
26767
  session?.startTransaction();
26256
26768
  const _file = {
26257
26769
  name: file.originalname,
@@ -26284,18 +26796,18 @@ function useUserService() {
26284
26796
  async function updatePasswordById(id, currentPassword, newPassword, passwordConfirmation) {
26285
26797
  let hashedPassword;
26286
26798
  const user = await getUserById(id);
26287
- const isPasswordValid = await (0, import_node_server_utils62.comparePassword)(
26799
+ const isPasswordValid = await (0, import_node_server_utils63.comparePassword)(
26288
26800
  currentPassword,
26289
26801
  user.password
26290
26802
  );
26291
26803
  if (!isPasswordValid)
26292
- throw new import_node_server_utils62.BadRequestError("Invalid current password.");
26804
+ throw new import_node_server_utils63.BadRequestError("Invalid current password.");
26293
26805
  if (newPassword !== passwordConfirmation)
26294
- throw new import_node_server_utils62.BadRequestError("Passwords do not match.");
26806
+ throw new import_node_server_utils63.BadRequestError("Passwords do not match.");
26295
26807
  try {
26296
- hashedPassword = await (0, import_node_server_utils62.hashPassword)(newPassword);
26808
+ hashedPassword = await (0, import_node_server_utils63.hashPassword)(newPassword);
26297
26809
  if (!hashedPassword)
26298
- throw new import_node_server_utils62.InternalServerError("Error hashing password.");
26810
+ throw new import_node_server_utils63.InternalServerError("Error hashing password.");
26299
26811
  return await updatePassword({ _id: id, password: hashedPassword });
26300
26812
  } catch (error) {
26301
26813
  throw error;
@@ -26333,8 +26845,8 @@ function useAuthController() {
26333
26845
  });
26334
26846
  if (error) {
26335
26847
  const messages = error.details.map((d) => d.message);
26336
- import_node_server_utils63.logger.log({ level: "error", message: messages.join(", ") });
26337
- next(new import_node_server_utils63.BadRequestError(messages.join(", ")));
26848
+ import_node_server_utils64.logger.log({ level: "error", message: messages.join(", ") });
26849
+ next(new import_node_server_utils64.BadRequestError(messages.join(", ")));
26338
26850
  return;
26339
26851
  }
26340
26852
  const session = await _login(value);
@@ -26346,7 +26858,7 @@ function useAuthController() {
26346
26858
  res.cookie("sid", session.sid, cookieOptions).cookie("user", session.user, cookieOptions).json(session);
26347
26859
  return;
26348
26860
  } catch (error) {
26349
- import_node_server_utils63.logger.log({ level: "error", message: error.message });
26861
+ import_node_server_utils64.logger.log({ level: "error", message: error.message });
26350
26862
  next(error);
26351
26863
  return;
26352
26864
  }
@@ -26357,8 +26869,8 @@ function useAuthController() {
26357
26869
  });
26358
26870
  const { error, value } = validation.validate(req.body);
26359
26871
  if (error) {
26360
- import_node_server_utils63.logger.log({ level: "error", message: error.message });
26361
- next(new import_node_server_utils63.BadRequestError(error.message));
26872
+ import_node_server_utils64.logger.log({ level: "error", message: error.message });
26873
+ next(new import_node_server_utils64.BadRequestError(error.message));
26362
26874
  return;
26363
26875
  }
26364
26876
  try {
@@ -26366,7 +26878,7 @@ function useAuthController() {
26366
26878
  res.json(session);
26367
26879
  return;
26368
26880
  } catch (error2) {
26369
- import_node_server_utils63.logger.log({ level: "error", message: error2.message });
26881
+ import_node_server_utils64.logger.log({ level: "error", message: error2.message });
26370
26882
  next(error2);
26371
26883
  return;
26372
26884
  }
@@ -26374,7 +26886,7 @@ function useAuthController() {
26374
26886
  async function logout(req, res, next) {
26375
26887
  const sid = getSessionIdFromRequest(req);
26376
26888
  if (!sid) {
26377
- next(new import_node_server_utils63.BadRequestError("Session ID is required"));
26889
+ next(new import_node_server_utils64.BadRequestError("Session ID is required"));
26378
26890
  return;
26379
26891
  }
26380
26892
  const refreshToken2 = typeof req.body?.refreshToken === "string" ? req.body.refreshToken : void 0;
@@ -26383,7 +26895,7 @@ function useAuthController() {
26383
26895
  res.json({ message: "Logged out successfully" });
26384
26896
  return;
26385
26897
  } catch (error) {
26386
- import_node_server_utils63.logger.log({ level: "error", message: error.message });
26898
+ import_node_server_utils64.logger.log({ level: "error", message: error.message });
26387
26899
  next(error);
26388
26900
  return;
26389
26901
  }
@@ -26398,8 +26910,8 @@ function useAuthController() {
26398
26910
  });
26399
26911
  if (error) {
26400
26912
  const messages = error.details.map((d) => d.message);
26401
- import_node_server_utils63.logger.log({ level: "error", message: messages.join(", ") });
26402
- next(new import_node_server_utils63.BadRequestError(messages.join(", ")));
26913
+ import_node_server_utils64.logger.log({ level: "error", message: messages.join(", ") });
26914
+ next(new import_node_server_utils64.BadRequestError(messages.join(", ")));
26403
26915
  return;
26404
26916
  }
26405
26917
  const email = value.email;
@@ -26414,7 +26926,7 @@ function useAuthController() {
26414
26926
  res.json({ message });
26415
26927
  return;
26416
26928
  } catch (error2) {
26417
- import_node_server_utils63.logger.log({ level: "error", message: error2.message });
26929
+ import_node_server_utils64.logger.log({ level: "error", message: error2.message });
26418
26930
  next(error2);
26419
26931
  return;
26420
26932
  }
@@ -26428,8 +26940,8 @@ function useAuthController() {
26428
26940
  const payload = { ...req.body };
26429
26941
  const { error } = validation.validate(payload);
26430
26942
  if (error) {
26431
- import_node_server_utils63.logger.log({ level: "error", message: error.message });
26432
- next(new import_node_server_utils63.BadRequestError(error.message));
26943
+ import_node_server_utils64.logger.log({ level: "error", message: error.message });
26944
+ next(new import_node_server_utils64.BadRequestError(error.message));
26433
26945
  return;
26434
26946
  }
26435
26947
  const otp = req.body.otp;
@@ -26444,7 +26956,7 @@ function useAuthController() {
26444
26956
  res.json({ message });
26445
26957
  return;
26446
26958
  } catch (error2) {
26447
- import_node_server_utils63.logger.log({ level: "error", message: error2.message });
26959
+ import_node_server_utils64.logger.log({ level: "error", message: error2.message });
26448
26960
  next(error2);
26449
26961
  return;
26450
26962
  }
@@ -26459,8 +26971,8 @@ function useAuthController() {
26459
26971
  const { error } = validation.validate(payload);
26460
26972
  if (error) {
26461
26973
  const messages = error.details.map((d) => d.message).join(", ");
26462
- import_node_server_utils63.logger.log({ level: "error", message: messages });
26463
- next(new import_node_server_utils63.BadRequestError(messages));
26974
+ import_node_server_utils64.logger.log({ level: "error", message: messages });
26975
+ next(new import_node_server_utils64.BadRequestError(messages));
26464
26976
  return;
26465
26977
  }
26466
26978
  const password = req.body.password;
@@ -26469,7 +26981,7 @@ function useAuthController() {
26469
26981
  res.json({ message });
26470
26982
  return;
26471
26983
  } catch (error2) {
26472
- import_node_server_utils63.logger.log({ level: "error", message: error2.message });
26984
+ import_node_server_utils64.logger.log({ level: "error", message: error2.message });
26473
26985
  next(error2);
26474
26986
  return;
26475
26987
  }
@@ -26486,10 +26998,10 @@ function useAuthController() {
26486
26998
 
26487
26999
  // src/controllers/user.controller.ts
26488
27000
  var import_joi27 = __toESM(require("joi"));
26489
- var import_node_server_utils65 = require("@7365admin1/node-server-utils");
27001
+ var import_node_server_utils66 = require("@7365admin1/node-server-utils");
26490
27002
 
26491
27003
  // src/repositories/person.repo.ts
26492
- var import_node_server_utils64 = require("@7365admin1/node-server-utils");
27004
+ var import_node_server_utils65 = require("@7365admin1/node-server-utils");
26493
27005
  var import_mongodb58 = require("mongodb");
26494
27006
 
26495
27007
  // src/utils/sanitizer.ts
@@ -26503,13 +27015,13 @@ var makeContainsRegex = (input) => {
26503
27015
  // src/repositories/person.repo.ts
26504
27016
  var site_people_namespace_collection = "site.people";
26505
27017
  function usePersonRepo() {
26506
- const db2 = import_node_server_utils64.useAtlas.getDb();
27018
+ const db2 = import_node_server_utils65.useAtlas.getDb();
26507
27019
  if (!db2) {
26508
- throw new import_node_server_utils64.InternalServerError("Unable to connect to server.");
27020
+ throw new import_node_server_utils65.InternalServerError("Unable to connect to server.");
26509
27021
  }
26510
27022
  const collection = db2.collection(site_people_namespace_collection);
26511
27023
  const vehiclesCollection = db2.collection("vehicles");
26512
- const { delNamespace, getCache, setCache } = (0, import_node_server_utils64.useCache)(
27024
+ const { delNamespace, getCache, setCache } = (0, import_node_server_utils65.useCache)(
26513
27025
  site_people_namespace_collection
26514
27026
  );
26515
27027
  async function withoutDeletedPlates(people) {
@@ -26558,7 +27070,7 @@ function usePersonRepo() {
26558
27070
  { key: { unit: 1, status: 1 } }
26559
27071
  ]);
26560
27072
  } catch (error) {
26561
- throw new import_node_server_utils64.InternalServerError("Failed to create index on site people.");
27073
+ throw new import_node_server_utils65.InternalServerError("Failed to create index on site people.");
26562
27074
  }
26563
27075
  }
26564
27076
  async function createTextIndex() {
@@ -26571,7 +27083,7 @@ function usePersonRepo() {
26571
27083
  contact: "text"
26572
27084
  });
26573
27085
  } catch (error) {
26574
- throw new import_node_server_utils64.InternalServerError(
27086
+ throw new import_node_server_utils65.InternalServerError(
26575
27087
  "Failed to create text index on site people."
26576
27088
  );
26577
27089
  }
@@ -26581,11 +27093,11 @@ function usePersonRepo() {
26581
27093
  value = MPerson(value);
26582
27094
  const res = await collection.insertOne(value, { session });
26583
27095
  delNamespace().then(() => {
26584
- import_node_server_utils64.logger.info(
27096
+ import_node_server_utils65.logger.info(
26585
27097
  `Cache cleared for namespace: ${site_people_namespace_collection}`
26586
27098
  );
26587
27099
  }).catch((err) => {
26588
- import_node_server_utils64.logger.error(
27100
+ import_node_server_utils65.logger.error(
26589
27101
  `Failed to clear cache for namespace: ${site_people_namespace_collection}`,
26590
27102
  err
26591
27103
  );
@@ -26594,7 +27106,7 @@ function usePersonRepo() {
26594
27106
  } catch (error) {
26595
27107
  const isDuplicated = error.message.includes("duplicate");
26596
27108
  if (isDuplicated) {
26597
- throw new import_node_server_utils64.BadRequestError("Person already exists.");
27109
+ throw new import_node_server_utils65.BadRequestError("Person already exists.");
26598
27110
  }
26599
27111
  throw error;
26600
27112
  }
@@ -26722,7 +27234,7 @@ function usePersonRepo() {
26722
27234
  collection.aggregate([{ $match: query2 }, { $count: "total" }], { session }).toArray()
26723
27235
  ]);
26724
27236
  const totalCount = countResult[0]?.total || 0;
26725
- const data = (0, import_node_server_utils64.paginate)(
27237
+ const data = (0, import_node_server_utils65.paginate)(
26726
27238
  await withoutDeletedPlates(items),
26727
27239
  page2,
26728
27240
  limit,
@@ -26737,7 +27249,7 @@ function usePersonRepo() {
26737
27249
  try {
26738
27250
  _id = new import_mongodb58.ObjectId(_id);
26739
27251
  } catch (error) {
26740
- throw new import_node_server_utils64.BadRequestError("Invalid person transaction ID format.");
27252
+ throw new import_node_server_utils65.BadRequestError("Invalid person transaction ID format.");
26741
27253
  }
26742
27254
  value.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
26743
27255
  if (value.updatedBy) {
@@ -26753,11 +27265,11 @@ function usePersonRepo() {
26753
27265
  { session }
26754
27266
  );
26755
27267
  delNamespace().then(() => {
26756
- import_node_server_utils64.logger.info(
27268
+ import_node_server_utils65.logger.info(
26757
27269
  `Cache cleared for namespace: ${site_people_namespace_collection}`
26758
27270
  );
26759
27271
  }).catch((err) => {
26760
- import_node_server_utils64.logger.error(
27272
+ import_node_server_utils65.logger.error(
26761
27273
  `Failed to clear cache for namespace: ${site_people_namespace_collection}`,
26762
27274
  err
26763
27275
  );
@@ -26771,7 +27283,7 @@ function usePersonRepo() {
26771
27283
  try {
26772
27284
  _id = new import_mongodb58.ObjectId(_id);
26773
27285
  } catch (error) {
26774
- throw new import_node_server_utils64.BadRequestError("Invalid customer ID format.");
27286
+ throw new import_node_server_utils65.BadRequestError("Invalid customer ID format.");
26775
27287
  }
26776
27288
  try {
26777
27289
  const updateValue = {
@@ -26781,14 +27293,14 @@ function usePersonRepo() {
26781
27293
  };
26782
27294
  const res = await collection.updateOne({ _id }, { $set: updateValue });
26783
27295
  if (res.modifiedCount === 0) {
26784
- throw new import_node_server_utils64.InternalServerError("Unable to delete visitor transaction.");
27296
+ throw new import_node_server_utils65.InternalServerError("Unable to delete visitor transaction.");
26785
27297
  }
26786
27298
  delNamespace().then(() => {
26787
- import_node_server_utils64.logger.info(
27299
+ import_node_server_utils65.logger.info(
26788
27300
  `Cache cleared for namespace: ${site_people_namespace_collection}`
26789
27301
  );
26790
27302
  }).catch((err) => {
26791
- import_node_server_utils64.logger.error(
27303
+ import_node_server_utils65.logger.error(
26792
27304
  `Failed to clear cache for namespace: ${site_people_namespace_collection}`,
26793
27305
  err
26794
27306
  );
@@ -26802,15 +27314,15 @@ function usePersonRepo() {
26802
27314
  try {
26803
27315
  _id = new import_mongodb58.ObjectId(_id);
26804
27316
  } catch (error) {
26805
- throw new import_node_server_utils64.BadRequestError("Invalid person ID.");
27317
+ throw new import_node_server_utils65.BadRequestError("Invalid person ID.");
26806
27318
  }
26807
- const cacheKey = (0, import_node_server_utils64.makeCacheKey)(site_people_namespace_collection, {
27319
+ const cacheKey = (0, import_node_server_utils65.makeCacheKey)(site_people_namespace_collection, {
26808
27320
  _id: String(_id)
26809
27321
  });
26810
27322
  try {
26811
27323
  const cached4 = await getCache(cacheKey);
26812
27324
  if (cached4) {
26813
- import_node_server_utils64.logger.log({
27325
+ import_node_server_utils65.logger.log({
26814
27326
  level: "info",
26815
27327
  message: `Cache hit for getById person: ${cacheKey}`
26816
27328
  });
@@ -26820,34 +27332,34 @@ function usePersonRepo() {
26820
27332
  _id
26821
27333
  });
26822
27334
  setCache(cacheKey, result, 300).then(() => {
26823
- import_node_server_utils64.logger.log({
27335
+ import_node_server_utils65.logger.log({
26824
27336
  level: "info",
26825
27337
  message: `Cache set for person by id: ${cacheKey}`
26826
27338
  });
26827
27339
  }).catch((err) => {
26828
- import_node_server_utils64.logger.log({
27340
+ import_node_server_utils65.logger.log({
26829
27341
  level: "error",
26830
27342
  message: `Failed to set cache for person by id: ${err.message}`
26831
27343
  });
26832
27344
  });
26833
27345
  return result;
26834
27346
  } catch (error) {
26835
- if (error instanceof import_node_server_utils64.AppError) {
27347
+ if (error instanceof import_node_server_utils65.AppError) {
26836
27348
  throw error;
26837
27349
  } else {
26838
- throw new import_node_server_utils64.InternalServerError("Failed to get person by id.");
27350
+ throw new import_node_server_utils65.InternalServerError("Failed to get person by id.");
26839
27351
  }
26840
27352
  }
26841
27353
  }
26842
27354
  async function getPersonByPlateNumber(plateNumber) {
26843
- const cacheKey = (0, import_node_server_utils64.makeCacheKey)(site_people_namespace_collection, {
27355
+ const cacheKey = (0, import_node_server_utils65.makeCacheKey)(site_people_namespace_collection, {
26844
27356
  plateNumber,
26845
27357
  key: "get-person-plate-number"
26846
27358
  });
26847
27359
  try {
26848
27360
  const cacheData = await getCache(cacheKey);
26849
27361
  if (cacheData) {
26850
- import_node_server_utils64.logger.info(`Cache hit for key: ${cacheKey}`);
27362
+ import_node_server_utils65.logger.info(`Cache hit for key: ${cacheKey}`);
26851
27363
  return await withoutDeletedPlatesOne(cacheData);
26852
27364
  }
26853
27365
  const data = await collection.findOne(
@@ -26855,13 +27367,13 @@ function usePersonRepo() {
26855
27367
  { sort: { _id: -1 } }
26856
27368
  );
26857
27369
  setCache(cacheKey, data, 15 * 60).then(() => {
26858
- import_node_server_utils64.logger.info(`Cache set for key: ${cacheKey}`);
27370
+ import_node_server_utils65.logger.info(`Cache set for key: ${cacheKey}`);
26859
27371
  }).catch((err) => {
26860
- import_node_server_utils64.logger.error(`Failed to set cache for key: ${cacheKey}`, err);
27372
+ import_node_server_utils65.logger.error(`Failed to set cache for key: ${cacheKey}`, err);
26861
27373
  });
26862
27374
  return await withoutDeletedPlatesOne(data);
26863
27375
  } catch (error) {
26864
- throw new import_node_server_utils64.InternalServerError(
27376
+ throw new import_node_server_utils65.InternalServerError(
26865
27377
  "Failed to fetch individual by plate number."
26866
27378
  );
26867
27379
  }
@@ -26873,11 +27385,11 @@ function usePersonRepo() {
26873
27385
  status: "approved"
26874
27386
  };
26875
27387
  if (excludePersonId) {
26876
- query2._id = { $ne: (0, import_node_server_utils64.toObjectId)(excludePersonId) };
27388
+ query2._id = { $ne: (0, import_node_server_utils65.toObjectId)(excludePersonId) };
26877
27389
  }
26878
27390
  return await collection.findOne(query2);
26879
27391
  } catch (error) {
26880
- throw new import_node_server_utils64.InternalServerError(
27392
+ throw new import_node_server_utils65.InternalServerError(
26881
27393
  "Failed to fetch approved person by plate number."
26882
27394
  );
26883
27395
  }
@@ -26885,47 +27397,47 @@ function usePersonRepo() {
26885
27397
  async function getByNRIC(value) {
26886
27398
  try {
26887
27399
  if (!value || value.trim() === "") {
26888
- import_node_server_utils64.logger.warn("getByNRIC called with an empty or invalid NRIC value.");
27400
+ import_node_server_utils65.logger.warn("getByNRIC called with an empty or invalid NRIC value.");
26889
27401
  return null;
26890
27402
  }
26891
- const cacheKey = (0, import_node_server_utils64.makeCacheKey)(site_people_namespace_collection, {
27403
+ const cacheKey = (0, import_node_server_utils65.makeCacheKey)(site_people_namespace_collection, {
26892
27404
  nric: value
26893
27405
  });
26894
27406
  const cachedData = await getCache(cacheKey);
26895
27407
  if (cachedData) {
26896
- import_node_server_utils64.logger.info(`Cache hit for key: ${cacheKey}`);
27408
+ import_node_server_utils65.logger.info(`Cache hit for key: ${cacheKey}`);
26897
27409
  return await withoutDeletedPlatesOne(cachedData);
26898
27410
  }
26899
27411
  const data = await collection.findOne({ nric: value });
26900
27412
  setCache(cacheKey, data, 15 * 60).then(() => {
26901
- import_node_server_utils64.logger.info(`Cache set for key: ${cacheKey}`);
27413
+ import_node_server_utils65.logger.info(`Cache set for key: ${cacheKey}`);
26902
27414
  }).catch((err) => {
26903
- import_node_server_utils64.logger.error(`Failed to set cache for key: ${cacheKey}`, err);
27415
+ import_node_server_utils65.logger.error(`Failed to set cache for key: ${cacheKey}`, err);
26904
27416
  });
26905
27417
  return await withoutDeletedPlatesOne(data);
26906
27418
  } catch (error) {
26907
- throw new import_node_server_utils64.InternalServerError("Failed to retrieve person by NRIC.");
27419
+ throw new import_node_server_utils65.InternalServerError("Failed to retrieve person by NRIC.");
26908
27420
  }
26909
27421
  }
26910
27422
  async function getPersonByPhoneNumber(value) {
26911
27423
  try {
26912
- const cacheKey = (0, import_node_server_utils64.makeCacheKey)(site_people_namespace_collection, {
27424
+ const cacheKey = (0, import_node_server_utils65.makeCacheKey)(site_people_namespace_collection, {
26913
27425
  contact: value
26914
27426
  });
26915
27427
  const cachedData = await getCache(cacheKey);
26916
27428
  if (cachedData) {
26917
- import_node_server_utils64.logger.info(`Cache hit for key: ${cacheKey}`);
27429
+ import_node_server_utils65.logger.info(`Cache hit for key: ${cacheKey}`);
26918
27430
  return await withoutDeletedPlatesOne(cachedData);
26919
27431
  }
26920
27432
  const data = await collection.findOne({ contact: value });
26921
27433
  setCache(cacheKey, data, 15 * 60).then(() => {
26922
- import_node_server_utils64.logger.info(`Cache set for key: ${cacheKey}`);
27434
+ import_node_server_utils65.logger.info(`Cache set for key: ${cacheKey}`);
26923
27435
  }).catch((err) => {
26924
- import_node_server_utils64.logger.error(`Failed to set cache for key: ${cacheKey}`, err);
27436
+ import_node_server_utils65.logger.error(`Failed to set cache for key: ${cacheKey}`, err);
26925
27437
  });
26926
27438
  return await withoutDeletedPlatesOne(data);
26927
27439
  } catch (error) {
26928
- throw new import_node_server_utils64.InternalServerError(
27440
+ throw new import_node_server_utils65.InternalServerError(
26929
27441
  "Failed to retrieve person by Phone Number."
26930
27442
  );
26931
27443
  }
@@ -26937,7 +27449,7 @@ function usePersonRepo() {
26937
27449
  }, session) {
26938
27450
  try {
26939
27451
  if (!unit || !import_mongodb58.ObjectId.isValid(unit)) {
26940
- throw new import_node_server_utils64.BadRequestError("Invalid unit ID.");
27452
+ throw new import_node_server_utils65.BadRequestError("Invalid unit ID.");
26941
27453
  }
26942
27454
  const query2 = {
26943
27455
  unit: new import_mongodb58.ObjectId(unit),
@@ -26982,14 +27494,14 @@ function usePersonRepo() {
26982
27494
  ]).toArray();
26983
27495
  return await withoutDeletedPlates(data);
26984
27496
  } catch (error) {
26985
- throw new import_node_server_utils64.InternalServerError(
27497
+ throw new import_node_server_utils65.InternalServerError(
26986
27498
  "Failed to fetch people type residents by unit."
26987
27499
  );
26988
27500
  }
26989
27501
  }
26990
27502
  async function getCompany(search, orgs) {
26991
27503
  try {
26992
- const cacheKey = (0, import_node_server_utils64.makeCacheKey)(site_people_namespace_collection, {
27504
+ const cacheKey = (0, import_node_server_utils65.makeCacheKey)(site_people_namespace_collection, {
26993
27505
  company: search
26994
27506
  });
26995
27507
  const baseQuery = {
@@ -27059,18 +27571,18 @@ function usePersonRepo() {
27059
27571
  ]).toArray();
27060
27572
  return data;
27061
27573
  } catch (error) {
27062
- throw new import_node_server_utils64.InternalServerError("Failed to fetch visitor by company.");
27574
+ throw new import_node_server_utils65.InternalServerError("Failed to fetch visitor by company.");
27063
27575
  }
27064
27576
  }
27065
27577
  async function getPeopleByPlateNumber(plateNumber) {
27066
- const cacheKey = (0, import_node_server_utils64.makeCacheKey)(site_people_namespace_collection, {
27578
+ const cacheKey = (0, import_node_server_utils65.makeCacheKey)(site_people_namespace_collection, {
27067
27579
  plateNumber,
27068
27580
  key: "get-people-plate-number"
27069
27581
  });
27070
27582
  try {
27071
27583
  const cacheData = await getCache(cacheKey);
27072
27584
  if (cacheData) {
27073
- import_node_server_utils64.logger.info(`Cache hit for key: ${cacheKey}`);
27585
+ import_node_server_utils65.logger.info(`Cache hit for key: ${cacheKey}`);
27074
27586
  return await withoutDeletedPlates(cacheData);
27075
27587
  }
27076
27588
  const data = await collection.find(
@@ -27078,13 +27590,13 @@ function usePersonRepo() {
27078
27590
  { sort: { _id: -1 } }
27079
27591
  ).toArray();
27080
27592
  setCache(cacheKey, data, 15 * 60).then(() => {
27081
- import_node_server_utils64.logger.info(`Cache set for key: ${cacheKey}`);
27593
+ import_node_server_utils65.logger.info(`Cache set for key: ${cacheKey}`);
27082
27594
  }).catch((err) => {
27083
- import_node_server_utils64.logger.error(`Failed to set cache for key: ${cacheKey}`, err);
27595
+ import_node_server_utils65.logger.error(`Failed to set cache for key: ${cacheKey}`, err);
27084
27596
  });
27085
27597
  return await withoutDeletedPlates(data);
27086
27598
  } catch (error) {
27087
- throw new import_node_server_utils64.InternalServerError("Failed to fetch people by plate number.");
27599
+ throw new import_node_server_utils65.InternalServerError("Failed to fetch people by plate number.");
27088
27600
  }
27089
27601
  }
27090
27602
  async function getPeopleByNRIC({
@@ -27109,13 +27621,13 @@ function usePersonRepo() {
27109
27621
  sort: JSON.stringify(sort)
27110
27622
  };
27111
27623
  try {
27112
- const cacheKey = (0, import_node_server_utils64.makeCacheKey)(
27624
+ const cacheKey = (0, import_node_server_utils65.makeCacheKey)(
27113
27625
  site_people_namespace_collection,
27114
27626
  cacheOptions
27115
27627
  );
27116
27628
  const cachedData = await getCache(cacheKey);
27117
27629
  if (cachedData) {
27118
- import_node_server_utils64.logger.info(`Cache hit for key: ${cacheKey}`);
27630
+ import_node_server_utils65.logger.info(`Cache hit for key: ${cacheKey}`);
27119
27631
  const cachedPage = cachedData;
27120
27632
  return {
27121
27633
  ...cachedPage,
@@ -27124,26 +27636,26 @@ function usePersonRepo() {
27124
27636
  }
27125
27637
  const items = await collection.find(query2).sort(sort).skip(page2 * limit).limit(limit).toArray();
27126
27638
  const length = await collection.countDocuments(query2);
27127
- const data = (0, import_node_server_utils64.paginate)(
27639
+ const data = (0, import_node_server_utils65.paginate)(
27128
27640
  await withoutDeletedPlates(items),
27129
27641
  page2,
27130
27642
  limit,
27131
27643
  length
27132
27644
  );
27133
27645
  setCache(cacheKey, data, 15 * 60).then(() => {
27134
- import_node_server_utils64.logger.info(`Cache set for key: ${cacheKey}`);
27646
+ import_node_server_utils65.logger.info(`Cache set for key: ${cacheKey}`);
27135
27647
  }).catch((err) => {
27136
- import_node_server_utils64.logger.error(`Failed to set cache for key: ${cacheKey}`, err);
27648
+ import_node_server_utils65.logger.error(`Failed to set cache for key: ${cacheKey}`, err);
27137
27649
  });
27138
27650
  return data;
27139
27651
  } catch (error) {
27140
- throw new import_node_server_utils64.InternalServerError("Failed to retrieve person by NRIC.");
27652
+ throw new import_node_server_utils65.InternalServerError("Failed to retrieve person by NRIC.");
27141
27653
  }
27142
27654
  }
27143
27655
  async function pushVehicleById(id, plate, session) {
27144
27656
  const { plateNumber, recNo } = plate;
27145
27657
  const updatedAt2 = (/* @__PURE__ */ new Date()).toISOString();
27146
- const _id = (0, import_node_server_utils64.toObjectId)(id);
27658
+ const _id = (0, import_node_server_utils65.toObjectId)(id);
27147
27659
  try {
27148
27660
  const updateExisting = await collection.updateOne(
27149
27661
  {
@@ -27176,15 +27688,15 @@ function usePersonRepo() {
27176
27688
  );
27177
27689
  }
27178
27690
  } catch (error) {
27179
- throw new import_node_server_utils64.InternalServerError("Failed to push vehicle plates by nric.");
27691
+ throw new import_node_server_utils65.InternalServerError("Failed to push vehicle plates by nric.");
27180
27692
  }
27181
27693
  }
27182
27694
  async function pullVehicleByRecNo(recNo, scope, session) {
27183
27695
  const updatedAt2 = (/* @__PURE__ */ new Date()).toISOString();
27184
- const owner = scope?.peopleId ? (0, import_node_server_utils64.toObjectId)(scope.peopleId) : null;
27185
- const site = scope?.site ? (0, import_node_server_utils64.toObjectId)(scope.site) : null;
27696
+ const owner = scope?.peopleId ? (0, import_node_server_utils65.toObjectId)(scope.peopleId) : null;
27697
+ const site = scope?.site ? (0, import_node_server_utils65.toObjectId)(scope.site) : null;
27186
27698
  if (!owner && !site) {
27187
- throw new import_node_server_utils64.BadRequestError(
27699
+ throw new import_node_server_utils65.BadRequestError(
27188
27700
  "Cannot remove a vehicle plate without a person or a site."
27189
27701
  );
27190
27702
  }
@@ -27225,24 +27737,24 @@ function usePersonRepo() {
27225
27737
  { session }
27226
27738
  );
27227
27739
  } catch (error) {
27228
- import_node_server_utils64.logger.log({
27740
+ import_node_server_utils65.logger.log({
27229
27741
  level: "error",
27230
27742
  message: `pullVehicleByRecNo failed on ${matchKey} for ${owner ? "person" : "site"} ${String(owner ?? site)}: ${error?.message ?? error}`
27231
27743
  });
27232
- throw new import_node_server_utils64.InternalServerError(
27744
+ throw new import_node_server_utils65.InternalServerError(
27233
27745
  "Could not take the plate off the resident's record."
27234
27746
  );
27235
27747
  }
27236
27748
  }
27237
27749
  async function getByUserId(userId) {
27238
- const user = (0, import_node_server_utils64.toObjectId)(userId);
27239
- const cacheKey = (0, import_node_server_utils64.makeCacheKey)(site_people_namespace_collection, {
27750
+ const user = (0, import_node_server_utils65.toObjectId)(userId);
27751
+ const cacheKey = (0, import_node_server_utils65.makeCacheKey)(site_people_namespace_collection, {
27240
27752
  user: userId
27241
27753
  });
27242
27754
  try {
27243
27755
  const cached4 = await getCache(cacheKey);
27244
27756
  if (cached4) {
27245
- import_node_server_utils64.logger.log({
27757
+ import_node_server_utils65.logger.log({
27246
27758
  level: "info",
27247
27759
  message: `Cache hit for getByUserId person: ${cacheKey}`
27248
27760
  });
@@ -27252,22 +27764,22 @@ function usePersonRepo() {
27252
27764
  user
27253
27765
  });
27254
27766
  setCache(cacheKey, result, 15 * 60).then(() => {
27255
- import_node_server_utils64.logger.log({
27767
+ import_node_server_utils65.logger.log({
27256
27768
  level: "info",
27257
27769
  message: `Cache set for person by user: ${cacheKey}`
27258
27770
  });
27259
27771
  }).catch((err) => {
27260
- import_node_server_utils64.logger.log({
27772
+ import_node_server_utils65.logger.log({
27261
27773
  level: "error",
27262
27774
  message: `Failed to set cache for person by user: ${err.message}`
27263
27775
  });
27264
27776
  });
27265
27777
  return result;
27266
27778
  } catch (error) {
27267
- if (error instanceof import_node_server_utils64.AppError) {
27779
+ if (error instanceof import_node_server_utils65.AppError) {
27268
27780
  throw error;
27269
27781
  } else {
27270
- throw new import_node_server_utils64.InternalServerError("Failed to get person by user.");
27782
+ throw new import_node_server_utils65.InternalServerError("Failed to get person by user.");
27271
27783
  }
27272
27784
  }
27273
27785
  }
@@ -27276,7 +27788,7 @@ function usePersonRepo() {
27276
27788
  try {
27277
27789
  _id = new import_mongodb58.ObjectId(_id);
27278
27790
  } catch (error) {
27279
- throw new import_node_server_utils64.BadRequestError("Invalid ID format.");
27791
+ throw new import_node_server_utils65.BadRequestError("Invalid ID format.");
27280
27792
  }
27281
27793
  try {
27282
27794
  const res = await collection.updateOne(
@@ -27285,14 +27797,14 @@ function usePersonRepo() {
27285
27797
  { session }
27286
27798
  );
27287
27799
  if (res.modifiedCount === 0) {
27288
- throw new import_node_server_utils64.InternalServerError(`Unable to ${value.status} person.`);
27800
+ throw new import_node_server_utils65.InternalServerError(`Unable to ${value.status} person.`);
27289
27801
  }
27290
27802
  delNamespace().then(() => {
27291
- import_node_server_utils64.logger.info(
27803
+ import_node_server_utils65.logger.info(
27292
27804
  `Cache cleared for namespace: ${site_people_namespace_collection}`
27293
27805
  );
27294
27806
  }).catch((err) => {
27295
- import_node_server_utils64.logger.error(
27807
+ import_node_server_utils65.logger.error(
27296
27808
  `Failed to clear cache for namespace: ${site_people_namespace_collection}`,
27297
27809
  err
27298
27810
  );
@@ -27308,7 +27820,7 @@ function usePersonRepo() {
27308
27820
  const data = await collection.find(query2, { session }).limit(10).toArray();
27309
27821
  return data;
27310
27822
  } catch (error) {
27311
- throw new import_node_server_utils64.InternalServerError("Failed to find persons" + error);
27823
+ throw new import_node_server_utils65.InternalServerError("Failed to find persons" + error);
27312
27824
  }
27313
27825
  }
27314
27826
  async function addFromVisitor(value, session) {
@@ -27343,7 +27855,7 @@ function usePersonRepo() {
27343
27855
  _id = new import_mongodb58.ObjectId(personId);
27344
27856
  acceptedTerms = new import_mongodb58.ObjectId(termsId);
27345
27857
  } catch {
27346
- throw new import_node_server_utils64.BadRequestError("Invalid ID format.");
27858
+ throw new import_node_server_utils65.BadRequestError("Invalid ID format.");
27347
27859
  }
27348
27860
  const now = (/* @__PURE__ */ new Date()).toISOString();
27349
27861
  try {
@@ -27352,7 +27864,7 @@ function usePersonRepo() {
27352
27864
  { $set: { acceptedTerms, acceptedTermsAt: now, updatedAt: now } }
27353
27865
  );
27354
27866
  } catch (error) {
27355
- throw new import_node_server_utils64.InternalServerError("Failed to record terms acceptance.");
27867
+ throw new import_node_server_utils65.InternalServerError("Failed to record terms acceptance.");
27356
27868
  }
27357
27869
  }
27358
27870
  async function getAcceptedTerms(personId) {
@@ -27360,7 +27872,7 @@ function usePersonRepo() {
27360
27872
  try {
27361
27873
  _id = new import_mongodb58.ObjectId(personId);
27362
27874
  } catch {
27363
- throw new import_node_server_utils64.BadRequestError("Invalid person ID format.");
27875
+ throw new import_node_server_utils65.BadRequestError("Invalid person ID format.");
27364
27876
  }
27365
27877
  try {
27366
27878
  return await collection.findOne(
@@ -27368,11 +27880,11 @@ function usePersonRepo() {
27368
27880
  { projection: { acceptedTerms: 1, acceptedTermsAt: 1, _id: 0 } }
27369
27881
  );
27370
27882
  } catch (error) {
27371
- throw new import_node_server_utils64.InternalServerError("Failed to read terms acceptance.");
27883
+ throw new import_node_server_utils65.InternalServerError("Failed to read terms acceptance.");
27372
27884
  }
27373
27885
  }
27374
27886
  async function updatePlateNumberByUserId(userId, plateNumber, session) {
27375
- const user = (0, import_node_server_utils64.toObjectId)(userId);
27887
+ const user = (0, import_node_server_utils65.toObjectId)(userId);
27376
27888
  try {
27377
27889
  const result = await collection.updateOne(
27378
27890
  { user },
@@ -27386,25 +27898,25 @@ function usePersonRepo() {
27386
27898
  { session }
27387
27899
  );
27388
27900
  if (result.matchedCount === 0) {
27389
- throw new import_node_server_utils64.BadRequestError("Person not found for this user.");
27901
+ throw new import_node_server_utils65.BadRequestError("Person not found for this user.");
27390
27902
  }
27391
27903
  try {
27392
27904
  await delNamespace();
27393
- import_node_server_utils64.logger.info(
27905
+ import_node_server_utils65.logger.info(
27394
27906
  `Cache cleared for namespace: ${site_people_namespace_collection}`
27395
27907
  );
27396
27908
  } catch (err) {
27397
- import_node_server_utils64.logger.error(
27909
+ import_node_server_utils65.logger.error(
27398
27910
  `Failed to clear cache for namespace: ${site_people_namespace_collection}`,
27399
27911
  err
27400
27912
  );
27401
27913
  }
27402
27914
  return "Successfully updated plate number.";
27403
27915
  } catch (error) {
27404
- if (error instanceof import_node_server_utils64.BadRequestError) {
27916
+ if (error instanceof import_node_server_utils65.BadRequestError) {
27405
27917
  throw error;
27406
27918
  }
27407
- throw new import_node_server_utils64.InternalServerError("Failed to update plate number.");
27919
+ throw new import_node_server_utils65.InternalServerError("Failed to update plate number.");
27408
27920
  }
27409
27921
  }
27410
27922
  return {
@@ -27467,8 +27979,8 @@ function useUserController() {
27467
27979
  const _id = req.params.id;
27468
27980
  const { error } = validation.validate(_id);
27469
27981
  if (error) {
27470
- import_node_server_utils65.logger.log({ level: "error", message: `${error.message}` });
27471
- next(new import_node_server_utils65.BadRequestError(error.message));
27982
+ import_node_server_utils66.logger.log({ level: "error", message: `${error.message}` });
27983
+ next(new import_node_server_utils66.BadRequestError(error.message));
27472
27984
  return;
27473
27985
  }
27474
27986
  try {
@@ -27477,7 +27989,7 @@ function useUserController() {
27477
27989
  res.json(user);
27478
27990
  return;
27479
27991
  } catch (error2) {
27480
- import_node_server_utils65.logger.log({ level: "error", message: `${error2.message}` });
27992
+ import_node_server_utils66.logger.log({ level: "error", message: `${error2.message}` });
27481
27993
  next(error2);
27482
27994
  return;
27483
27995
  }
@@ -27492,8 +28004,8 @@ function useUserController() {
27492
28004
  const query2 = { ...req.query };
27493
28005
  const { error } = validation.validate(query2);
27494
28006
  if (error) {
27495
- import_node_server_utils65.logger.log({ level: "error", message: `${error.message}` });
27496
- next(new import_node_server_utils65.BadRequestError(error.message));
28007
+ import_node_server_utils66.logger.log({ level: "error", message: `${error.message}` });
28008
+ next(new import_node_server_utils66.BadRequestError(error.message));
27497
28009
  return;
27498
28010
  }
27499
28011
  const search = req.query.search ?? "";
@@ -27511,7 +28023,7 @@ function useUserController() {
27511
28023
  res.json(data);
27512
28024
  return;
27513
28025
  } catch (error2) {
27514
- import_node_server_utils65.logger.log({ level: "error", message: `${error2.message}` });
28026
+ import_node_server_utils66.logger.log({ level: "error", message: `${error2.message}` });
27515
28027
  next(error2);
27516
28028
  return;
27517
28029
  }
@@ -27521,8 +28033,8 @@ function useUserController() {
27521
28033
  const email = req.params.email;
27522
28034
  const { error } = validation.validate(email);
27523
28035
  if (error) {
27524
- import_node_server_utils65.logger.log({ level: "error", message: `${error.message}` });
27525
- next(new import_node_server_utils65.BadRequestError(error.message));
28036
+ import_node_server_utils66.logger.log({ level: "error", message: `${error.message}` });
28037
+ next(new import_node_server_utils66.BadRequestError(error.message));
27526
28038
  return;
27527
28039
  }
27528
28040
  try {
@@ -27535,7 +28047,7 @@ function useUserController() {
27535
28047
  res.json(publicIdentity(user));
27536
28048
  return;
27537
28049
  } catch (error2) {
27538
- import_node_server_utils65.logger.log({ level: "error", message: `${error2.message}` });
28050
+ import_node_server_utils66.logger.log({ level: "error", message: `${error2.message}` });
27539
28051
  next(error2);
27540
28052
  return;
27541
28053
  }
@@ -27549,8 +28061,8 @@ function useUserController() {
27549
28061
  const query2 = { ...req.query };
27550
28062
  const { error } = validation.validate(query2);
27551
28063
  if (error) {
27552
- import_node_server_utils65.logger.log({ level: "error", message: `${error.message}` });
27553
- next(new import_node_server_utils65.BadRequestError(error.message));
28064
+ import_node_server_utils66.logger.log({ level: "error", message: `${error.message}` });
28065
+ next(new import_node_server_utils66.BadRequestError(error.message));
27554
28066
  return;
27555
28067
  }
27556
28068
  const search = req.query.search ?? "";
@@ -27562,7 +28074,7 @@ function useUserController() {
27562
28074
  res.json(data);
27563
28075
  return;
27564
28076
  } catch (error2) {
27565
- import_node_server_utils65.logger.log({ level: "error", message: `${error2.message}` });
28077
+ import_node_server_utils66.logger.log({ level: "error", message: `${error2.message}` });
27566
28078
  next(error2);
27567
28079
  return;
27568
28080
  }
@@ -27579,8 +28091,8 @@ function useUserController() {
27579
28091
  const payload = { ...req.body };
27580
28092
  const { error } = validation.validate({ id, ...payload });
27581
28093
  if (error) {
27582
- import_node_server_utils65.logger.log({ level: "error", message: `${error.message}` });
27583
- next(new import_node_server_utils65.BadRequestError(error.message));
28094
+ import_node_server_utils66.logger.log({ level: "error", message: `${error.message}` });
28095
+ next(new import_node_server_utils66.BadRequestError(error.message));
27584
28096
  return;
27585
28097
  }
27586
28098
  try {
@@ -27588,14 +28100,14 @@ function useUserController() {
27588
28100
  res.status(201).json({ message: "Successfully created account." });
27589
28101
  return;
27590
28102
  } catch (error2) {
27591
- import_node_server_utils65.logger.log({ level: "error", message: `${error2.message}` });
28103
+ import_node_server_utils66.logger.log({ level: "error", message: `${error2.message}` });
27592
28104
  next(error2);
27593
28105
  return;
27594
28106
  }
27595
28107
  }
27596
28108
  async function updateUserProfile(req, res, next) {
27597
28109
  if (!req.file) {
27598
- next(new import_node_server_utils65.BadRequestError("File is required!"));
28110
+ next(new import_node_server_utils66.BadRequestError("File is required!"));
27599
28111
  return;
27600
28112
  }
27601
28113
  const validation = import_joi27.default.object({
@@ -27604,8 +28116,8 @@ function useUserController() {
27604
28116
  const payload = { ...req.body };
27605
28117
  const { error } = validation.validate(payload);
27606
28118
  if (error) {
27607
- import_node_server_utils65.logger.log({ level: "error", message: `${error.message}` });
27608
- next(new import_node_server_utils65.BadRequestError(error.message));
28119
+ import_node_server_utils66.logger.log({ level: "error", message: `${error.message}` });
28120
+ next(new import_node_server_utils66.BadRequestError(error.message));
27609
28121
  return;
27610
28122
  }
27611
28123
  const user = req.headers.user ?? "";
@@ -27618,7 +28130,7 @@ function useUserController() {
27618
28130
  res.json({ message: "Successfully updated profile picture." });
27619
28131
  return;
27620
28132
  } catch (error2) {
27621
- import_node_server_utils65.logger.log({ level: "error", message: `${error2.message}` });
28133
+ import_node_server_utils66.logger.log({ level: "error", message: `${error2.message}` });
27622
28134
  next(error2);
27623
28135
  return;
27624
28136
  }
@@ -27647,8 +28159,8 @@ function useUserController() {
27647
28159
  const payload = { ...req.body };
27648
28160
  const { error } = validation.validate({ _id, ...payload });
27649
28161
  if (error) {
27650
- import_node_server_utils65.logger.log({ level: "error", message: `${error.message}` });
27651
- next(new import_node_server_utils65.BadRequestError(error.message));
28162
+ import_node_server_utils66.logger.log({ level: "error", message: `${error.message}` });
28163
+ next(new import_node_server_utils66.BadRequestError(error.message));
27652
28164
  return;
27653
28165
  }
27654
28166
  try {
@@ -27657,7 +28169,7 @@ function useUserController() {
27657
28169
  res.json({ message });
27658
28170
  return;
27659
28171
  } catch (error2) {
27660
- import_node_server_utils65.logger.log({ level: "error", message: `${error2.message}` });
28172
+ import_node_server_utils66.logger.log({ level: "error", message: `${error2.message}` });
27661
28173
  next(error2);
27662
28174
  return;
27663
28175
  }
@@ -27689,8 +28201,8 @@ function useUserController() {
27689
28201
  const payload = { ...req.body };
27690
28202
  const { error } = validation.validate({ _id, ...payload });
27691
28203
  if (error) {
27692
- import_node_server_utils65.logger.log({ level: "error", message: `${error.message}` });
27693
- next(new import_node_server_utils65.BadRequestError(error.message));
28204
+ import_node_server_utils66.logger.log({ level: "error", message: `${error.message}` });
28205
+ next(new import_node_server_utils66.BadRequestError(error.message));
27694
28206
  return;
27695
28207
  }
27696
28208
  try {
@@ -27710,7 +28222,7 @@ function useUserController() {
27710
28222
  res.json({ message });
27711
28223
  return;
27712
28224
  } catch (error2) {
27713
- import_node_server_utils65.logger.log({ level: "error", message: `${error2.message}` });
28225
+ import_node_server_utils66.logger.log({ level: "error", message: `${error2.message}` });
27714
28226
  next(error2);
27715
28227
  return;
27716
28228
  }
@@ -27729,7 +28241,7 @@ function useUserController() {
27729
28241
  ...payload
27730
28242
  });
27731
28243
  if (error) {
27732
- next(new import_node_server_utils65.BadRequestError(error.message));
28244
+ next(new import_node_server_utils66.BadRequestError(error.message));
27733
28245
  return;
27734
28246
  }
27735
28247
  const currentPassword = req.body.currentPassword ?? "";
@@ -27766,16 +28278,16 @@ var import_joi28 = __toESM(require("joi"));
27766
28278
  var import_node_server_utils71 = require("@7365admin1/node-server-utils");
27767
28279
 
27768
28280
  // src/repositories/org-role-seed.repo.ts
27769
- var import_node_server_utils66 = require("@7365admin1/node-server-utils");
28281
+ var import_node_server_utils67 = require("@7365admin1/node-server-utils");
27770
28282
  var import_mongodb59 = require("mongodb");
27771
28283
  var namespace_collection = "org-default-role-seeds";
27772
28284
  function toOrgId(org) {
27773
28285
  return typeof org === "string" ? new import_mongodb59.ObjectId(org) : org;
27774
28286
  }
27775
28287
  function useOrgRoleSeedRepo() {
27776
- const db2 = import_node_server_utils66.useAtlas.getDb();
28288
+ const db2 = import_node_server_utils67.useAtlas.getDb();
27777
28289
  if (!db2) {
27778
- throw new import_node_server_utils66.InternalServerError("Unable to connect to server.");
28290
+ throw new import_node_server_utils67.InternalServerError("Unable to connect to server.");
27779
28291
  }
27780
28292
  const collection = db2.collection(namespace_collection);
27781
28293
  const roles = db2.collection("roles");
@@ -27799,7 +28311,7 @@ function useOrgRoleSeedRepo() {
27799
28311
  }
27800
28312
 
27801
28313
  // src/services/role.service.ts
27802
- var import_node_server_utils67 = require("@7365admin1/node-server-utils");
28314
+ var import_node_server_utils68 = require("@7365admin1/node-server-utils");
27803
28315
  function useRoleService() {
27804
28316
  const {
27805
28317
  deleteRole: _deleteRole,
@@ -27811,7 +28323,7 @@ function useRoleService() {
27811
28323
  try {
27812
28324
  const count = await getByRoleId(_id);
27813
28325
  if (count > 0) {
27814
- throw new import_node_server_utils67.BadRequestError(
28326
+ throw new import_node_server_utils68.BadRequestError(
27815
28327
  "Cannot delete role that is assigned to members."
27816
28328
  );
27817
28329
  }
@@ -27823,16 +28335,16 @@ function useRoleService() {
27823
28335
  async function getDeletionPreview(_id) {
27824
28336
  const role = await getRoleById(_id);
27825
28337
  if (!role) {
27826
- throw new import_node_server_utils67.NotFoundError("Role not found.");
28338
+ throw new import_node_server_utils68.NotFoundError("Role not found.");
27827
28339
  }
27828
28340
  const members = await getAssignedByRoleId(_id);
27829
28341
  const replacementRoles = (await getActiveByTypeOrg(role.type ?? "", role.org ?? "")).filter((replacementRole) => replacementRole._id?.toString() !== _id);
27830
28342
  return { role, members, replacementRoles };
27831
28343
  }
27832
28344
  async function deleteWithReassignments(_id, reassignments) {
27833
- const session = import_node_server_utils67.useAtlas.getClient()?.startSession();
28345
+ const session = import_node_server_utils68.useAtlas.getClient()?.startSession();
27834
28346
  if (!session) {
27835
- throw new import_node_server_utils67.InternalServerError("Unable to start role deletion transaction.");
28347
+ throw new import_node_server_utils68.InternalServerError("Unable to start role deletion transaction.");
27836
28348
  }
27837
28349
  try {
27838
28350
  session.startTransaction();
@@ -27849,7 +28361,7 @@ function useRoleService() {
27849
28361
  ({ roleId }) => replacementRoleIds.has(roleId)
27850
28362
  );
27851
28363
  if (!hasExactMemberCoverage || !hasEligibleReplacementRoles) {
27852
- throw new import_node_server_utils67.BadRequestError("Invalid role reassignment request.");
28364
+ throw new import_node_server_utils68.BadRequestError("Invalid role reassignment request.");
27853
28365
  }
27854
28366
  await reassignRoles(reassignments, session);
27855
28367
  await _deleteRole(_id, session);
@@ -27866,7 +28378,7 @@ function useRoleService() {
27866
28378
  }
27867
28379
 
27868
28380
  // src/utils/role-scope.util.ts
27869
- var import_node_server_utils68 = require("@7365admin1/node-server-utils");
28381
+ var import_node_server_utils69 = require("@7365admin1/node-server-utils");
27870
28382
  function roleScope(role) {
27871
28383
  if ((role?.type ?? "") === PLATFORM_STAFF_ROLE_TYPE)
27872
28384
  return "platform";
@@ -27900,7 +28412,7 @@ function requireNoPlatformGrant(role, next) {
27900
28412
  const added = platformPermissionsAdded(next, role?.permissions);
27901
28413
  if (added.length === 0)
27902
28414
  return;
27903
- throw new import_node_server_utils68.AppError(
28415
+ throw new import_node_server_utils69.AppError(
27904
28416
  "These are Seven365 console permissions and cannot be granted on a client role: " + added.join(", "),
27905
28417
  403
27906
28418
  );
@@ -27915,7 +28427,7 @@ function refuseSharedClientTemplate(role, next) {
27915
28427
  return true;
27916
28428
  if (Array.isArray(next) && restorableFromHistory(role, next))
27917
28429
  return true;
27918
- throw new import_node_server_utils68.AppError(
28430
+ throw new import_node_server_utils69.AppError(
27919
28431
  "This role is shared by every client that was invited with it and cannot be changed here.",
27920
28432
  403
27921
28433
  );
@@ -27923,7 +28435,7 @@ function refuseSharedClientTemplate(role, next) {
27923
28435
  function refuseNewSharedClientTemplate(role) {
27924
28436
  if (roleScope(role) !== "client-template")
27925
28437
  return;
27926
- throw new import_node_server_utils68.AppError(
28438
+ throw new import_node_server_utils69.AppError(
27927
28439
  "A role with no organisation would be shared by every client and cannot be created. Choose the organisation this role belongs to.",
27928
28440
  403
27929
28441
  );
@@ -27961,113 +28473,6 @@ function recordablePriorPermissions(role) {
27961
28473
  return [...held];
27962
28474
  }
27963
28475
 
27964
- // src/utils/org-default-roles.util.ts
27965
- var import_node_server_utils69 = require("@7365admin1/node-server-utils");
27966
- var APP_SERVICE_TYPES = new Set(
27967
- Object.values(AppServiceType)
27968
- );
27969
- var ADMINISTRATOR_PERMISSIONS = Object.freeze([
27970
- "members:view-members",
27971
- "members:assign-member-role",
27972
- "members:suspend-member",
27973
- "members:activate-member",
27974
- "members:delete-member",
27975
- "invitations:create-invitation",
27976
- "invitations:view-invitations",
27977
- "invitations:cancel-invitation",
27978
- "roles:see-all-roles",
27979
- "roles:see-role-details",
27980
- "roles:add-role",
27981
- "roles:update-role",
27982
- "roles:delete-role"
27983
- ]);
27984
- function defaultOwnerRoles(org, nature, appOwnerPermissions) {
27985
- if (!org?.toString())
27986
- return [];
27987
- const owner = {
27988
- org,
27989
- name: "owner",
27990
- permissions: ["*"],
27991
- default: true
27992
- };
27993
- const administrator = {
27994
- org,
27995
- name: "Administrator",
27996
- permissions: [...ADMINISTRATOR_PERMISSIONS],
27997
- default: true
27998
- };
27999
- const roles = [
28000
- { ...owner, type: "organization" },
28001
- { ...administrator, type: "organization" }
28002
- ];
28003
- const appType = serviceAppType(nature);
28004
- if (appType && APP_SERVICE_TYPES.has(appType)) {
28005
- roles.push({
28006
- ...owner,
28007
- type: appType,
28008
- ...appOwnerPermissions?.length && { permissions: [...appOwnerPermissions] }
28009
- });
28010
- roles.push({ ...administrator, type: appType });
28011
- }
28012
- return roles;
28013
- }
28014
- async function seedDefaultOrgRoles(addRole, org, nature, appOwnerPermissions) {
28015
- const seeded = [];
28016
- try {
28017
- for (const role of defaultOwnerRoles(org, nature, appOwnerPermissions)) {
28018
- const id = await addRole(role);
28019
- if (id)
28020
- seeded.push({ type: String(role.type), id: String(id), name: role.name });
28021
- }
28022
- } catch (error) {
28023
- import_node_server_utils69.logger.log({
28024
- level: "error",
28025
- message: `Failed to seed default roles for organization ${org}: ${error?.message}`
28026
- });
28027
- }
28028
- return seeded;
28029
- }
28030
- async function healOrgDefaultRoles(deps, org) {
28031
- if (!org?.toString())
28032
- return 0;
28033
- let claimed = false;
28034
- try {
28035
- claimed = await deps.claim(org);
28036
- if (!claimed)
28037
- return 0;
28038
- const existing = await deps.countRoles(org);
28039
- if (existing > 0) {
28040
- import_node_server_utils69.logger.log({
28041
- level: "info",
28042
- message: `Default role self-heal skipped for organization ${org}: ${existing} role(s) already present`
28043
- });
28044
- return 0;
28045
- }
28046
- const roles = defaultOwnerRoles(org, await deps.getNature(org));
28047
- for (const role of roles) {
28048
- await deps.addRole(role);
28049
- }
28050
- import_node_server_utils69.logger.log({
28051
- level: "info",
28052
- message: `Seeded ${roles.length} default role(s) for existing organization ${org} on read`
28053
- });
28054
- return roles.length;
28055
- } catch (error) {
28056
- if (claimed) {
28057
- await deps.release(org).catch(() => void 0);
28058
- }
28059
- import_node_server_utils69.logger.log({
28060
- level: "error",
28061
- message: `Failed to self-heal default roles for organization ${org}: ${error?.message}`
28062
- });
28063
- return 0;
28064
- }
28065
- }
28066
- function clientInviteOwnerPermissionsOn(env2 = process.env) {
28067
- const value = String(env2.CLIENT_INVITE_OWNER_PERMISSIONS ?? "on").trim().toLowerCase();
28068
- return !["off", "false", "0", "no"].includes(value);
28069
- }
28070
-
28071
28476
  // src/utils/module-ceiling.util.ts
28072
28477
  var import_node_server_utils70 = require("@7365admin1/node-server-utils");
28073
28478
  async function moduleCeiling(org, site) {
@@ -28250,7 +28655,9 @@ function useRoleController() {
28250
28655
  limit: import_joi28.default.number().integer().min(1).max(100).allow("", null).default(10),
28251
28656
  type: import_joi28.default.string().optional().allow("", null),
28252
28657
  org: import_joi28.default.string().hex().optional().allow("", null),
28253
- site: import_joi28.default.string().hex().optional().allow("", null)
28658
+ site: import_joi28.default.string().hex().optional().allow("", null),
28659
+ // "organisation": only the roles with no site (Organization app, service roles made once)
28660
+ scope: import_joi28.default.string().valid("organisation").optional().allow("", null)
28254
28661
  });
28255
28662
  const query2 = { ...req.query };
28256
28663
  const { error } = validation.validate(query2);
@@ -28265,18 +28672,19 @@ function useRoleController() {
28265
28672
  const type = req.query.type ?? "";
28266
28673
  const org = req.query.org ?? "";
28267
28674
  const site = req.query.site ?? "";
28675
+ const scope = req.query.scope ?? "";
28268
28676
  try {
28269
28677
  if (org) {
28270
28678
  await requireOrgReach(req, org);
28271
28679
  }
28272
- let data = await _getRoles({ search, page: page2, limit, type, org, site });
28273
- const listIsWholeOrg = org && !search && !site && page2 === 1;
28680
+ let data = await _getRoles({ search, page: page2, limit, type, org, site, scope });
28681
+ const listIsWholeOrg = org && !search && !site && !scope && page2 === 1;
28274
28682
  const listed = data?.items;
28275
28683
  if (listIsWholeOrg && listed?.length === 0) {
28276
28684
  const seeded = await healDefaultRoles(org).catch(() => 0);
28277
28685
  if (seeded > 0) {
28278
28686
  try {
28279
- data = await _getRoles({ search, page: page2, limit, type, org, site });
28687
+ data = await _getRoles({ search, page: page2, limit, type, org, site, scope });
28280
28688
  } catch (error2) {
28281
28689
  import_node_server_utils71.logger.log({ level: "error", message: error2.message });
28282
28690
  }
@@ -28724,7 +29132,8 @@ function useMemberService() {
28724
29132
  }
28725
29133
  }
28726
29134
  }
28727
- await _updateRoleById(id, role);
29135
+ const placedBy = member.siteId && !newRole?.site && callerId5 && import_mongodb60.ObjectId.isValid(callerId5) && String(member.user ?? "") !== callerId5 ? callerId5 : void 0;
29136
+ await _updateRoleById(id, role, placedBy);
28728
29137
  return { message: "Member role updated successfully." };
28729
29138
  }
28730
29139
  async function updateSiteById(id, siteId, siteName) {
@@ -28772,6 +29181,7 @@ function useMemberService() {
28772
29181
  var import_node_server_utils73 = require("@7365admin1/node-server-utils");
28773
29182
  var import_mongodb61 = require("mongodb");
28774
29183
  var EXCLUDED_TYPES = ["resident", PLATFORM_STAFF_MEMBER_TYPE];
29184
+ var ORG_ROLE_EXCLUDED = [...EXCLUDED_TYPES, "organization"];
28775
29185
  var both = (id) => [id, new import_mongodb61.ObjectId(id)];
28776
29186
  function useMemberSiteAccessService() {
28777
29187
  const { add, updateMemberStatus, updateRoleById } = useMemberRepo();
@@ -28811,10 +29221,13 @@ function useMemberSiteAccessService() {
28811
29221
  const roles = await db2().collection("roles").find({
28812
29222
  $or: [
28813
29223
  { _id: { $in: roleIds.map((r) => new import_mongodb61.ObjectId(r)) } },
28814
- { org: { $in: both(org) }, site: { $in: [...sites.keys()].flatMap(both) }, status: "active" }
29224
+ { org: { $in: both(org) }, site: { $in: [...sites.keys()].flatMap(both) }, status: "active" },
29225
+ // organisation-level service roles (no site; never the org-console type)
29226
+ { org: { $in: both(org) }, site: { $in: ["", null] }, status: "active", type: { $nin: ORG_ROLE_EXCLUDED } }
28815
29227
  ]
28816
29228
  }).project({ name: 1, type: 1, site: 1, org: 1, status: 1 }).toArray();
28817
29229
  const byId = new Map(roles.map((r) => [String(r._id), r]));
29230
+ const orgRoles = roles.filter((r) => !String(r.site ?? "") && String(r.org ?? "") === org && r.status === "active" && !ORG_ROLE_EXCLUDED.includes(r.type)).map((r) => ({ _id: String(r._id), name: r.name ?? "", type: r.type ?? "", scope: "organisation" })).sort((a, b) => a.name.localeCompare(b.name));
28818
29231
  const view = (m) => ({
28819
29232
  _id: String(m._id),
28820
29233
  role: String(m.role ?? ""),
@@ -28828,7 +29241,7 @@ function useMemberSiteAccessService() {
28828
29241
  ...s,
28829
29242
  membership: here[0] ?? null,
28830
29243
  memberships: here,
28831
- roles: roles.filter((r) => String(r.site ?? "") === s.site && String(r.org ?? "") === org && r.status === "active" && !EXCLUDED_TYPES.includes(r.type)).map((r) => ({ _id: String(r._id), name: r.name ?? "", type: r.type ?? "" }))
29244
+ roles: roles.filter((r) => String(r.site ?? "") === s.site && String(r.org ?? "") === org && r.status === "active" && !EXCLUDED_TYPES.includes(r.type)).map((r) => ({ _id: String(r._id), name: r.name ?? "", type: r.type ?? "", scope: "site" })).concat(orgRoles)
28832
29245
  };
28833
29246
  }).sort((a, b) => a.name.localeCompare(b.name));
28834
29247
  const orgWide = members.filter((m) => !String(m.siteId ?? "")).map(view);
@@ -28854,7 +29267,7 @@ function useMemberSiteAccessService() {
28854
29267
  throw new import_node_server_utils73.BadRequestError("A site is listed twice.");
28855
29268
  seen2.add(choice.site);
28856
29269
  if (!row.roles.some((r) => r._id === choice.role)) {
28857
- throw new import_node_server_utils73.BadRequestError(`Choose a role created for ${row.name || "that site"}.`);
29270
+ throw new import_node_server_utils73.BadRequestError(`Choose a role created for ${row.name || "that site"}, or one of the organisation's service roles.`);
28858
29271
  }
28859
29272
  }
28860
29273
  const userDoc = await db2().collection("users").findOne({ _id: new import_mongodb61.ObjectId(user) }, { projection: { email: 1, name: 1 } });
@@ -28868,10 +29281,11 @@ function useMemberSiteAccessService() {
28868
29281
  const choice = sites.find((c) => c.site === row.site);
28869
29282
  const role = choice ? row.roles.find((r) => r._id === choice.role) : null;
28870
29283
  const keep = role ? row.memberships.find((m) => m.type === role.type) : void 0;
28871
- if (role && keep && keep.role !== role._id) {
29284
+ if (role && keep && (keep.role !== role._id || role.scope === "organisation")) {
28872
29285
  reroles.push(async () => {
28873
- await updateRoleById(keep._id, role._id);
28874
- applied.changed.push(row.site);
29286
+ await updateRoleById(keep._id, role._id, actor);
29287
+ if (keep.role !== role._id)
29288
+ applied.changed.push(row.site);
28875
29289
  });
28876
29290
  } else if (role && !keep) {
28877
29291
  adds.push(async () => {
@@ -28880,7 +29294,7 @@ function useMemberSiteAccessService() {
28880
29294
  { projection: { _id: 1 } }
28881
29295
  );
28882
29296
  if (old) {
28883
- await updateRoleById(old._id, role._id);
29297
+ await updateRoleById(old._id, role._id, actor);
28884
29298
  await updateMemberStatus(old._id, { status: suspended ? "suspended" : "active" });
28885
29299
  } else {
28886
29300
  await add({
@@ -28894,6 +29308,8 @@ function useMemberSiteAccessService() {
28894
29308
  siteId: row.site,
28895
29309
  siteName: row.name,
28896
29310
  dateInvited: (/* @__PURE__ */ new Date()).toISOString(),
29311
+ // "placed at this site by the editor": confines an organisation role to it (siteInvitedPins)
29312
+ createdBy: actor,
28897
29313
  ...suspended ? { status: "suspended" } : {}
28898
29314
  });
28899
29315
  }
@@ -100838,6 +101254,10 @@ function useServiceProviderInviteController() {
100838
101254
  softDelete: _softDelete,
100839
101255
  decline: _decline,
100840
101256
  accept: _accept,
101257
+ acceptAsNewCompany: _acceptAsNewCompany,
101258
+ wrongJoinPreview: _wrongJoinPreview,
101259
+ repairWrongJoin: _repairWrongJoin,
101260
+ undoWrongJoin: _undoWrongJoin,
100841
101261
  linkSite: _linkSite,
100842
101262
  listReceived: _listReceived,
100843
101263
  getForProvider: _getForProvider
@@ -101009,6 +101429,42 @@ function useServiceProviderInviteController() {
101009
101429
  next(error);
101010
101430
  }
101011
101431
  }
101432
+ async function acceptNewCompany(req, res, next) {
101433
+ try {
101434
+ const { error, value } = import_joi149.default.object({
101435
+ companyName: import_joi149.default.string().trim().max(200).optional().allow("", null),
101436
+ country: import_joi149.default.string().trim().max(100).optional().allow("", null)
101437
+ }).validate(req.body ?? {}, { stripUnknown: true });
101438
+ if (error)
101439
+ throw new import_node_server_utils293.BadRequestError(`Invalid input: ${error.message}`);
101440
+ res.json(
101441
+ await _acceptAsNewCompany({
101442
+ id: inviteId(req),
101443
+ userId: callerId5(req),
101444
+ companyName: value.companyName ?? "",
101445
+ country: value.country ?? ""
101446
+ })
101447
+ );
101448
+ } catch (error) {
101449
+ next(error);
101450
+ }
101451
+ }
101452
+ function wrongJoin(action) {
101453
+ return async (req, res, next) => {
101454
+ try {
101455
+ await requireConsolePermission(req, "sp-approvals", "approve-sp");
101456
+ const args = { id: inviteId(req), userId: callerId5(req) };
101457
+ res.json(
101458
+ action === "preview" ? await _wrongJoinPreview(args) : action === "repair" ? await _repairWrongJoin(args) : await _undoWrongJoin(args)
101459
+ );
101460
+ } catch (error) {
101461
+ next(error);
101462
+ }
101463
+ };
101464
+ }
101465
+ const wrongJoinPreview = wrongJoin("preview");
101466
+ const wrongJoinRepair = wrongJoin("repair");
101467
+ const wrongJoinUndo = wrongJoin("undo");
101012
101468
  async function linkSite(req, res, next) {
101013
101469
  try {
101014
101470
  res.json(
@@ -101059,6 +101515,10 @@ function useServiceProviderInviteController() {
101059
101515
  remove,
101060
101516
  decline,
101061
101517
  accept,
101518
+ acceptNewCompany,
101519
+ wrongJoinPreview,
101520
+ wrongJoinRepair,
101521
+ wrongJoinUndo,
101062
101522
  linkSite,
101063
101523
  received,
101064
101524
  getOne
@@ -107632,6 +108092,8 @@ function usePersonalEmergencyChainController() {
107632
108092
  HID_PERMISSION_CATEGORIES,
107633
108093
  HID_UINT32_MAX,
107634
108094
  HID_UINT64_MAX,
108095
+ INVITE_EMAIL_EVENTS,
108096
+ INVITE_RESEND_MIN_MINUTES,
107635
108097
  LIFT_DOOR,
107636
108098
  LIFT_LEASE_MS,
107637
108099
  LIFT_MODE,
@@ -108040,6 +108502,9 @@ function usePersonalEmergencyChainController() {
108040
108502
  incidentReportLog,
108041
108503
  incidents_namespace_collection,
108042
108504
  invitationOffersRole,
108505
+ inviteEmailFailureReason,
108506
+ inviteEmailStatus,
108507
+ inviteResendWaitMinutes,
108043
108508
  isAvailableForWork,
108044
108509
  isCameraEntitled,
108045
108510
  isCrossSiteRole,
@@ -108428,6 +108893,7 @@ function usePersonalEmergencyChainController() {
108428
108893
  serviceProviderInviteScope,
108429
108894
  serviceProviderInviteViewStatus,
108430
108895
  serviceProviderInviterKind,
108896
+ serviceProviderNatureFor,
108431
108897
  serviceTypeLabel,
108432
108898
  sessionSchema,
108433
108899
  setIO,