@7365admin1/core 3.135.0 → 3.136.1

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,
@@ -6091,10 +6097,10 @@ function useAuthService() {
6091
6097
 
6092
6098
  // src/controllers/auth.controller.ts
6093
6099
  var import_joi26 = __toESM(require("joi"));
6094
- var import_node_server_utils63 = require("@7365admin1/node-server-utils");
6100
+ var import_node_server_utils64 = require("@7365admin1/node-server-utils");
6095
6101
 
6096
6102
  // src/services/verification.service.ts
6097
- var import_node_server_utils60 = require("@7365admin1/node-server-utils");
6103
+ var import_node_server_utils62 = require("@7365admin1/node-server-utils");
6098
6104
 
6099
6105
  // src/models/verification.model.ts
6100
6106
  var import_node_server_utils19 = require("@7365admin1/node-server-utils");
@@ -9760,6 +9766,64 @@ function filterSiteProviderMembers(items, { type = "", company = "", search = ""
9760
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)))
9761
9767
  );
9762
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
+ }
9763
9827
 
9764
9828
  // src/utils/invite-actor.util.ts
9765
9829
  var import_node_server_utils28 = require("@7365admin1/node-server-utils");
@@ -9847,9 +9911,12 @@ async function hasOrgInvitation(userId, orgId) {
9847
9911
  const invite = await db2.collection("verifications").findOne(
9848
9912
  {
9849
9913
  email,
9850
- type: {
9851
- $in: ["user-invite", "member-invite", "service-provider-invite"]
9852
- },
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"] },
9853
9920
  "metadata.org": { $in: orgCandidates }
9854
9921
  },
9855
9922
  // addresses are stored as typed, so the comparison is case-insensitive.
@@ -9875,7 +9942,8 @@ async function invitationOffersRole(userId, orgId, roleId) {
9875
9942
  const invites = await db2.collection("verifications").find(
9876
9943
  {
9877
9944
  email,
9878
- type: { $in: ["user-invite", "member-invite", "service-provider-invite"] },
9945
+ // not a service-provider invitation - see `hasOrgInvitation`
9946
+ type: { $in: ["user-invite", "member-invite"] },
9879
9947
  "metadata.org": { $in: [org, new import_mongodb26.ObjectId(org)] },
9880
9948
  status: { $ne: "cancelled" }
9881
9949
  },
@@ -10090,8 +10158,8 @@ async function holdsRole(userId, roleId, orgId) {
10090
10158
  }
10091
10159
 
10092
10160
  // src/services/service-provider-invite.service.ts
10093
- var import_node_server_utils59 = require("@7365admin1/node-server-utils");
10094
- var import_mongodb55 = require("mongodb");
10161
+ var import_node_server_utils61 = require("@7365admin1/node-server-utils");
10162
+ var import_mongodb56 = require("mongodb");
10095
10163
 
10096
10164
  // src/services/notification.service.ts
10097
10165
  var import_iservice365_expo_notifications = require("iservice365-expo-notifications");
@@ -10985,14 +11053,14 @@ function button(href, label2) {
10985
11053
  }
10986
11054
  function notificationEmail(p) {
10987
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.` : "";
10988
- 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.`;
10989
11057
  const html = frame(`<h2 style="font-size:26px;font-weight:400;margin:0 0 20px">${escapeHtml(p.title)}</h2>
10990
11058
  <p style="font-size:16px;line-height:1.5">${escapeHtml(p.body)}</p>
10991
11059
  ${button(p.openUrl, "Open in iService365")}
10992
11060
  ${pause ? `<p style="font-size:14px;color:#ddd">${escapeHtml(pause)}</p>` : ""}
10993
11061
  <hr style="border:none;border-top:1px solid #333;margin:30px 0 20px" />
10994
11062
  <p style="font-size:13px;color:#bbb"><strong>Why am I getting this?</strong> ${escapeHtml(why)}</p>
10995
- <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>`);
10996
11064
  const text2 = [
10997
11065
  p.title,
10998
11066
  "",
@@ -11002,7 +11070,7 @@ ${pause ? `<p style="font-size:14px;color:#ddd">${escapeHtml(pause)}</p>` : ""}
11002
11070
  ...pause ? ["", pause] : [],
11003
11071
  "",
11004
11072
  `Why am I getting this? ${why}`,
11005
- `Turn off email for ${p.moduleLabel}: ${p.offUrl}`,
11073
+ ...p.offUrl ? [`Turn off email for ${p.moduleLabel}: ${p.offUrl}`] : [],
11006
11074
  `Notification settings: ${p.settingsUrl}`
11007
11075
  ].join("\n");
11008
11076
  return { subject: p.title, html, text: text2 };
@@ -12536,6 +12604,8 @@ async function sendNotificationEmails(job) {
12536
12604
  return 0;
12537
12605
  const { emailRecipients, takeEmailSlot } = useNotificationPreferenceRepo();
12538
12606
  const all = await emailRecipients(job.userIds, category.key);
12607
+ if (job.required)
12608
+ return sendRequiredEmails(job, category, all.map((r) => r.userId));
12539
12609
  if (!all.length)
12540
12610
  return 0;
12541
12611
  if (all.length > MAX_RECIPIENTS_PER_NOTIFICATION) {
@@ -12578,6 +12648,36 @@ async function sendNotificationEmails(job) {
12578
12648
  }
12579
12649
  return sent;
12580
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
+ }
12581
12681
  var queue = [];
12582
12682
  var draining = null;
12583
12683
  async function drain() {
@@ -12737,20 +12837,25 @@ var NotificationService = class {
12737
12837
  const staff = await staffRecipients();
12738
12838
  if (!staff.length)
12739
12839
  return;
12740
- await send(
12741
- staff,
12742
- "Service provider invitation to approve",
12743
- 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}.`,
12744
- {
12745
- module: "platform",
12746
- kind: "spApproval",
12747
- screen: "",
12748
- params: { id: payload.invitationId.toString() }
12749
- },
12750
- false,
12751
- void 0,
12752
- "platform"
12753
- );
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
+ });
12754
12859
  }
12755
12860
  static async clientInvitationAccepted(payload) {
12756
12861
  const staff = await staffRecipients();
@@ -24238,8 +24343,570 @@ async function requireRoleAssigner(req, orgId) {
24238
24343
  throw new import_node_server_utils58.UnauthorizedError("You do not have permission to assign roles.");
24239
24344
  }
24240
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
+
24241
24907
  // src/services/service-provider-invite.service.ts
24242
24908
  var INVITE_VALID_HOURS = 72;
24909
+ var INVITE_EMAIL_TIMEOUT_MS = 3e4;
24243
24910
  async function siteClientOrgs(siteId, pmMemberOrgIds = []) {
24244
24911
  const site = await useSiteRepo().getSiteById(siteId).catch(() => null);
24245
24912
  const owner = String(site?.orgId ?? site?.org ?? "");
@@ -24277,11 +24944,11 @@ async function assertServiceProviderInviteScope(actor, orgId, siteId) {
24277
24944
  )
24278
24945
  });
24279
24946
  if (!scope.ok)
24280
- throw new import_node_server_utils59.UnauthorizedError(scope.reason);
24947
+ throw new import_node_server_utils61.UnauthorizedError(scope.reason);
24281
24948
  return { sender: scope.sender, siteOwnerOrg };
24282
24949
  }
24283
24950
  function useServiceProviderInviteService() {
24284
- const mailer2 = new import_node_server_utils59.useMailer({
24951
+ const mailer2 = new import_node_server_utils61.useMailer({
24285
24952
  host: MAILER_TRANSPORT_HOST,
24286
24953
  port: MAILER_TRANSPORT_PORT,
24287
24954
  secure: MAILER_TRANSPORT_SECURE,
@@ -24295,13 +24962,18 @@ function useServiceProviderInviteService() {
24295
24962
  getServiceProviderInvites: _list,
24296
24963
  countServiceProviderInvitesAwaitingApproval: _countAwaiting
24297
24964
  } = useVerificationRepoV2();
24298
- const { getById: getOrgById, getByEmail: getOrgByEmail } = useOrgRepo();
24965
+ const {
24966
+ getById: getOrgById,
24967
+ getByEmail: getOrgByEmail,
24968
+ add: addOrg,
24969
+ getByCreatorAndNature
24970
+ } = useOrgRepo();
24299
24971
  const { getUserByEmail, getUserById } = useUserRepo();
24300
24972
  function event(action, actor, reason) {
24301
24973
  return {
24302
24974
  action,
24303
24975
  at: (/* @__PURE__ */ new Date()).toISOString(),
24304
- by: actor.id ? new import_mongodb55.ObjectId(actor.id) : null,
24976
+ by: actor.id ? new import_mongodb56.ObjectId(actor.id) : null,
24305
24977
  byName: actor.name,
24306
24978
  ...reason ? { reason } : {}
24307
24979
  };
@@ -24337,36 +25009,81 @@ function useServiceProviderInviteService() {
24337
25009
  });
24338
25010
  }
24339
25011
  } catch (error) {
24340
- import_node_server_utils59.logger.log({
25012
+ import_node_server_utils61.logger.log({
24341
25013
  level: "error",
24342
25014
  message: `Invitation in-app notification failed: ${error?.message}`
24343
25015
  });
24344
25016
  }
24345
- const filePath = (0, import_node_server_utils59.getDirectory)(
24346
- __dirname,
24347
- `./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
+ })
24348
25080
  );
24349
- const emailContent = (0, import_node_server_utils59.compileHandlebar)({
24350
- context: {
24351
- email,
24352
- orgName,
24353
- siteName,
24354
- validity: VERIFICATION_USER_INVITE_DURATION,
24355
- link: `${APP_MAIN}/verify/service-provider-invite/${inviteId}`
24356
- },
24357
- filePath
24358
- });
24359
- await mailer2.sendMail({
24360
- to: email,
24361
- subject: invite.type === SERVICE_PROVIDER_SIGN_IN_TYPE ? SERVICE_PROVIDER_SIGN_IN_SUBJECT : SERVICE_PROVIDER_SIGN_UP_SUBJECT,
24362
- html: emailContent,
24363
- sender: "iService365" /* ISERVICE365 */
24364
- });
25081
+ return result;
24365
25082
  }
24366
25083
  async function load(id) {
24367
25084
  const invite = await _getById(id);
24368
25085
  if (!invite)
24369
- throw new import_node_server_utils59.NotFoundError("Invitation not found.");
25086
+ throw new import_node_server_utils61.NotFoundError("Invitation not found.");
24370
25087
  return invite;
24371
25088
  }
24372
25089
  function assertOwnerOrSuperAdmin(actor, invite) {
@@ -24375,13 +25092,13 @@ function useServiceProviderInviteService() {
24375
25092
  const org = inviteOrgId(invite);
24376
25093
  if (org && actor.orgIds.includes(org))
24377
25094
  return;
24378
- throw new import_node_server_utils59.UnauthorizedError(
25095
+ throw new import_node_server_utils61.UnauthorizedError(
24379
25096
  "This invitation belongs to another organisation."
24380
25097
  );
24381
25098
  }
24382
25099
  function assertSuperAdmin(actor) {
24383
25100
  if (!staffMayBypass(actor)) {
24384
- throw new import_node_server_utils59.UnauthorizedError(
25101
+ throw new import_node_server_utils61.UnauthorizedError(
24385
25102
  "Only a Seven365 administrator can approve or reject invitations."
24386
25103
  );
24387
25104
  }
@@ -24389,12 +25106,12 @@ function useServiceProviderInviteService() {
24389
25106
  function assertAllowed(action, invite) {
24390
25107
  const refusal = refuseServiceProviderInviteAction(action, invite.status);
24391
25108
  if (refusal)
24392
- throw new import_node_server_utils59.BadRequestError(refusal);
25109
+ throw new import_node_server_utils61.BadRequestError(refusal);
24393
25110
  }
24394
25111
  async function actorFor(userId) {
24395
25112
  const actor = await resolveInviteActor(userId);
24396
25113
  if (!actor.id)
24397
- throw new import_node_server_utils59.UnauthorizedError("Sign in to continue.");
25114
+ throw new import_node_server_utils61.UnauthorizedError("Sign in to continue.");
24398
25115
  return actor;
24399
25116
  }
24400
25117
  async function listApprovals({
@@ -24457,7 +25174,8 @@ function useServiceProviderInviteService() {
24457
25174
  inviterOrgName,
24458
25175
  inviterKind: fromClient ? "property-management" : serviceProviderInviterKind(inviterOrg, siteOwnerOrg),
24459
25176
  siteOwnerName,
24460
- service: String(invite.metadata?.app ?? "")
25177
+ service: String(invite.metadata?.app ?? ""),
25178
+ emailStatus: inviteEmailStatus(invite.history)
24461
25179
  };
24462
25180
  })
24463
25181
  );
@@ -24475,7 +25193,7 @@ function useServiceProviderInviteService() {
24475
25193
  let scope = actor.orgIds;
24476
25194
  if (orgId) {
24477
25195
  if (!staffMayBypass(actor) && !actor.orgIds.includes(orgId)) {
24478
- throw new import_node_server_utils59.UnauthorizedError(
25196
+ throw new import_node_server_utils61.UnauthorizedError(
24479
25197
  "You are not a member of that organisation."
24480
25198
  );
24481
25199
  }
@@ -24564,7 +25282,9 @@ function useServiceProviderInviteService() {
24564
25282
  answeredAt: status === "expired" ? invite.expireAt ?? null : status === "pending" || status === "awaiting-approval" /* AWAITING_APPROVAL */ ? null : answer?.at ?? invite.updatedAt ?? null,
24565
25283
  contactEmail,
24566
25284
  contactPhone,
24567
- onSite
25285
+ onSite,
25286
+ // Did the invitation email go out, and if not, why (latest attempt).
25287
+ emailStatus: inviteEmailStatus(history)
24568
25288
  };
24569
25289
  }
24570
25290
  async function approvalCount({ userId }) {
@@ -24596,13 +25316,23 @@ function useServiceProviderInviteService() {
24596
25316
  if (!updated) {
24597
25317
  return { message: "This invitation has already been approved." };
24598
25318
  }
24599
- 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
+ }
24600
25327
  await _transition({
24601
25328
  id,
24602
25329
  from: [],
24603
25330
  event: event("provider-notified", actor)
24604
25331
  });
24605
- 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
+ };
24606
25336
  }
24607
25337
  async function reject({
24608
25338
  id,
@@ -24613,7 +25343,7 @@ function useServiceProviderInviteService() {
24613
25343
  assertSuperAdmin(actor);
24614
25344
  const trimmed = String(reason ?? "").trim();
24615
25345
  if (!trimmed) {
24616
- throw new import_node_server_utils59.BadRequestError(
25346
+ throw new import_node_server_utils61.BadRequestError(
24617
25347
  "Give a reason for not approving this invitation. Whoever sent it will see it."
24618
25348
  );
24619
25349
  }
@@ -24657,6 +25387,12 @@ function useServiceProviderInviteService() {
24657
25387
  const invite = await load(id);
24658
25388
  assertOwnerOrSuperAdmin(actor, invite);
24659
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
+ }
24660
25396
  const expireAt = new Date(
24661
25397
  Date.now() + INVITE_VALID_HOURS * 60 * 60 * 1e3
24662
25398
  ).toISOString();
@@ -24667,12 +25403,17 @@ function useServiceProviderInviteService() {
24667
25403
  set: { expireAt }
24668
25404
  });
24669
25405
  if (!updated) {
24670
- throw new import_node_server_utils59.BadRequestError(
25406
+ throw new import_node_server_utils61.BadRequestError(
24671
25407
  refuseServiceProviderInviteAction("resend", invite.status) ?? "This invitation cannot be sent again."
24672
25408
  );
24673
25409
  }
24674
- await notifyProvider(updated);
24675
- 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 };
24676
25417
  }
24677
25418
  async function softDelete({ id, userId }) {
24678
25419
  const actor = await actorFor(userId);
@@ -24687,7 +25428,7 @@ function useServiceProviderInviteService() {
24687
25428
  event: event("deleted", actor),
24688
25429
  set: {
24689
25430
  deletedAt: (/* @__PURE__ */ new Date()).toISOString(),
24690
- deletedBy: actor.id ? new import_mongodb55.ObjectId(actor.id) : null
25431
+ deletedBy: actor.id ? new import_mongodb56.ObjectId(actor.id) : null
24691
25432
  }
24692
25433
  });
24693
25434
  if (!updated) {
@@ -24703,7 +25444,7 @@ function useServiceProviderInviteService() {
24703
25444
  );
24704
25445
  const invitedUserId = user?._id?.toString();
24705
25446
  if (!invitedUserId || invitedUserId !== userId) {
24706
- 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.");
24707
25448
  }
24708
25449
  }
24709
25450
  async function decline({ id, userId }) {
@@ -24714,11 +25455,11 @@ function useServiceProviderInviteService() {
24714
25455
  return { message: "You have already declined this invitation." };
24715
25456
  }
24716
25457
  if (invite.deletedAt) {
24717
- 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.");
24718
25459
  }
24719
25460
  assertAllowed("decline", invite);
24720
25461
  if (isServiceProviderInviteExpired(invite)) {
24721
- 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);
24722
25463
  }
24723
25464
  const updated = await _transition({
24724
25465
  id,
@@ -24747,16 +25488,16 @@ function useServiceProviderInviteService() {
24747
25488
  if (![SERVICE_PROVIDER_SIGN_IN_TYPE, SERVICE_PROVIDER_SIGN_UP_TYPE].includes(
24748
25489
  String(invite.type ?? "")
24749
25490
  )) {
24750
- throw new import_node_server_utils59.NotFoundError("Invitation not found.");
25491
+ throw new import_node_server_utils61.NotFoundError("Invitation not found.");
24751
25492
  }
24752
25493
  if (invite.status === "awaiting-approval" /* AWAITING_APPROVAL */) {
24753
- throw new import_node_server_utils59.NotFoundError("Invitation not found.");
25494
+ throw new import_node_server_utils61.NotFoundError("Invitation not found.");
24754
25495
  }
24755
25496
  if (invite.deletedAt) {
24756
- 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.");
24757
25498
  }
24758
25499
  if (isServiceProviderInviteExpired(invite)) {
24759
- 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);
24760
25501
  }
24761
25502
  let result;
24762
25503
  try {
@@ -24776,6 +25517,207 @@ function useServiceProviderInviteService() {
24776
25517
  await notifyInviter(invite, true, result.providerOrgName);
24777
25518
  return { message: result.message };
24778
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
+ }
24779
25721
  async function notifyInviter(invite, accepted, providerName) {
24780
25722
  try {
24781
25723
  const to = invite.metadata?.invitedBy?.toString?.() ?? "";
@@ -24793,7 +25735,7 @@ function useServiceProviderInviteService() {
24793
25735
  siteId: invite.metadata?.siteId?.toString?.()
24794
25736
  });
24795
25737
  } catch (error) {
24796
- import_node_server_utils59.logger.log({
25738
+ import_node_server_utils61.logger.log({
24797
25739
  level: "error",
24798
25740
  message: `Invitation answer notification failed: ${error?.message}`
24799
25741
  });
@@ -24803,11 +25745,11 @@ function useServiceProviderInviteService() {
24803
25745
  const id = String(org ?? "").trim();
24804
25746
  if (!id)
24805
25747
  return "";
24806
- const runs = import_mongodb55.ObjectId.isValid(id) && actor.memberships.some(
25748
+ const runs = import_mongodb56.ObjectId.isValid(id) && actor.memberships.some(
24807
25749
  (m) => m.org === id && (m.companyWideRole === true || !m.siteId && m.orgLevelRole === true)
24808
25750
  );
24809
25751
  if (!runs) {
24810
- throw new import_node_server_utils59.UnauthorizedError(
25752
+ throw new import_node_server_utils61.UnauthorizedError(
24811
25753
  "Only an administrator of that company can answer this invitation for it."
24812
25754
  );
24813
25755
  }
@@ -24924,15 +25866,15 @@ function useServiceProviderInviteService() {
24924
25866
  await assertInvitedParty(actor.id, invite);
24925
25867
  const forOrg = memberOrg(actor, org);
24926
25868
  if (!forOrg) {
24927
- 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.");
24928
25870
  }
24929
25871
  if (![SERVICE_PROVIDER_SIGN_IN_TYPE, SERVICE_PROVIDER_SIGN_UP_TYPE].includes(
24930
25872
  String(invite.type ?? "")
24931
25873
  ) || invite.status === "awaiting-approval" /* AWAITING_APPROVAL */) {
24932
- throw new import_node_server_utils59.NotFoundError("Invitation not found.");
25874
+ throw new import_node_server_utils61.NotFoundError("Invitation not found.");
24933
25875
  }
24934
25876
  if (invite.status !== "complete" /* COMPLETE */ || invite.deletedAt) {
24935
- throw new import_node_server_utils59.BadRequestError(
25877
+ throw new import_node_server_utils61.BadRequestError(
24936
25878
  "Only an accepted invitation can be added to your Sites."
24937
25879
  );
24938
25880
  }
@@ -24941,7 +25883,7 @@ function useServiceProviderInviteService() {
24941
25883
  return { message: "This site is already on your Sites list." };
24942
25884
  }
24943
25885
  if (!state.needsSiteLink) {
24944
- throw new import_node_server_utils59.BadRequestError(
25886
+ throw new import_node_server_utils61.BadRequestError(
24945
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."
24946
25888
  );
24947
25889
  }
@@ -24969,7 +25911,7 @@ function useServiceProviderInviteService() {
24969
25911
  const invite = await load(id);
24970
25912
  await assertInvitedParty(actor.id, invite);
24971
25913
  if (invite.status === "awaiting-approval" /* AWAITING_APPROVAL */ || invite.status === "rejected" /* REJECTED */) {
24972
- throw new import_node_server_utils59.NotFoundError("Invitation not found.");
25914
+ throw new import_node_server_utils61.NotFoundError("Invitation not found.");
24973
25915
  }
24974
25916
  const row = await receivedRow(invite, memberOrg(actor, org));
24975
25917
  return { ...row, tab: receivedInviteTab(invite) };
@@ -24985,6 +25927,10 @@ function useServiceProviderInviteService() {
24985
25927
  softDelete,
24986
25928
  decline,
24987
25929
  accept,
25930
+ acceptAsNewCompany,
25931
+ wrongJoinPreview,
25932
+ repairWrongJoin,
25933
+ undoWrongJoin,
24988
25934
  linkSite,
24989
25935
  listReceived,
24990
25936
  getForProvider,
@@ -24993,7 +25939,7 @@ function useServiceProviderInviteService() {
24993
25939
  }
24994
25940
 
24995
25941
  // src/services/verification.service.ts
24996
- var import_mongodb56 = require("mongodb");
25942
+ var import_mongodb57 = require("mongodb");
24997
25943
  function useVerificationService() {
24998
25944
  const MailerConfig = {
24999
25945
  host: MAILER_TRANSPORT_HOST,
@@ -25003,7 +25949,7 @@ function useVerificationService() {
25003
25949
  from: MAILER_FROM_EMAIL,
25004
25950
  password: MAILER_PASSWORD
25005
25951
  };
25006
- const mailer2 = new import_node_server_utils60.useMailer(MailerConfig);
25952
+ const mailer2 = new import_node_server_utils62.useMailer(MailerConfig);
25007
25953
  const {
25008
25954
  add,
25009
25955
  getById: _getById,
@@ -25042,11 +25988,11 @@ function useVerificationService() {
25042
25988
  value.type = type;
25043
25989
  const res = await add(value);
25044
25990
  const dir = __dirname;
25045
- 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}`);
25046
25992
  const isMember = type === "member-invite";
25047
25993
  const subject = isMember ? "Member Invite" : "User Invite";
25048
25994
  const link = `${APP_MAIN}/verify/${isMember ? "membership" : "invitation"}/${res}`;
25049
- const emailContent = (0, import_node_server_utils60.compileHandlebar)({
25995
+ const emailContent = (0, import_node_server_utils62.compileHandlebar)({
25050
25996
  context: {
25051
25997
  email: userName,
25052
25998
  validity: VERIFICATION_USER_INVITE_DURATION,
@@ -25060,7 +26006,7 @@ function useVerificationService() {
25060
26006
  html: emailContent,
25061
26007
  sender: "iService365"
25062
26008
  }).catch((error) => {
25063
- import_node_server_utils60.logger.log({
26009
+ import_node_server_utils62.logger.log({
25064
26010
  level: "error",
25065
26011
  message: `Error sending user ${type} email: ${error}`
25066
26012
  });
@@ -25114,7 +26060,7 @@ function useVerificationService() {
25114
26060
  const createdId = reissued ? pending._id : await add(value);
25115
26061
  verificationIds.push(createdId.toString());
25116
26062
  const link = `${APP_MAIN}/verify/invitation/${createdId}`;
25117
- const emailContent = (0, import_node_server_utils60.compileHandlebar)({
26063
+ const emailContent = (0, import_node_server_utils62.compileHandlebar)({
25118
26064
  context: {
25119
26065
  email,
25120
26066
  validity: VERIFICATION_USER_INVITE_DURATION,
@@ -25127,7 +26073,7 @@ function useVerificationService() {
25127
26073
  hasPestControl: app === "pest_control_services",
25128
26074
  hasPoolMaintenance: app === "pool_maintenance_services"
25129
26075
  },
25130
- 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")
25131
26077
  });
25132
26078
  await mailer2.sendMail({
25133
26079
  to: email,
@@ -25156,7 +26102,7 @@ function useVerificationService() {
25156
26102
  });
25157
26103
  if (existing) {
25158
26104
  if (existing.status === "complete") {
25159
- throw new import_node_server_utils60.BadRequestError(
26105
+ throw new import_node_server_utils62.BadRequestError(
25160
26106
  `User already completed member invite for app: ${metadata.app}`
25161
26107
  );
25162
26108
  }
@@ -25171,13 +26117,13 @@ function useVerificationService() {
25171
26117
  };
25172
26118
  const res = await add(value);
25173
26119
  const link = `${APP_MAIN}/verify/membership/${res}`;
25174
- const emailContent = (0, import_node_server_utils60.compileHandlebar)({
26120
+ const emailContent = (0, import_node_server_utils62.compileHandlebar)({
25175
26121
  context: {
25176
26122
  email,
25177
26123
  validity: VERIFICATION_USER_INVITE_DURATION,
25178
26124
  link
25179
26125
  },
25180
- 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")
25181
26127
  });
25182
26128
  await mailer2.sendMail({
25183
26129
  to: email,
@@ -25223,8 +26169,8 @@ function useVerificationService() {
25223
26169
  });
25224
26170
  if (error) {
25225
26171
  const messages = error.details.map((d) => d.message).join(", ");
25226
- import_node_server_utils60.logger.log({ level: "error", message: messages });
25227
- 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}`);
25228
26174
  }
25229
26175
  let subject = "Service Provider Invite" /* _SERVICE_PROVIDER_INVITE */;
25230
26176
  let type = "service-provider-invite" /* SERVICE_PROVIDER_INVITE */;
@@ -25247,7 +26193,7 @@ function useVerificationService() {
25247
26193
  const org = await getOrgById(orgId);
25248
26194
  const actor = await resolveInviteActor(invitedBy);
25249
26195
  if (!actor.id) {
25250
- throw new import_node_server_utils60.UnauthorizedError("Sign in to continue.");
26196
+ throw new import_node_server_utils62.UnauthorizedError("Sign in to continue.");
25251
26197
  }
25252
26198
  const { sender } = await assertServiceProviderInviteScope(
25253
26199
  actor,
@@ -25272,7 +26218,7 @@ function useVerificationService() {
25272
26218
  inviterIsServiceProvider: sender === "subcontract"
25273
26219
  });
25274
26220
  if (!decision.ok) {
25275
- throw new import_node_server_utils60.BadRequestError(decision.reason);
26221
+ throw new import_node_server_utils62.BadRequestError(decision.reason);
25276
26222
  }
25277
26223
  value.type = decision.type;
25278
26224
  subject = decision.subject;
@@ -25303,7 +26249,7 @@ function useVerificationService() {
25303
26249
  }
25304
26250
  await useServiceProviderInviteService().notifyProvider({
25305
26251
  ...value,
25306
- _id: new import_mongodb56.ObjectId(String(res))
26252
+ _id: new import_mongodb57.ObjectId(String(res))
25307
26253
  });
25308
26254
  return res;
25309
26255
  } catch (error2) {
@@ -25339,8 +26285,8 @@ function useVerificationService() {
25339
26285
  });
25340
26286
  if (error) {
25341
26287
  const messages = error.details.map((d) => d.message).join(", ");
25342
- import_node_server_utils60.logger.log({ level: "error", message: messages });
25343
- 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}`);
25344
26290
  }
25345
26291
  const subject = "Service Provider Invite" /* _SERVICE_PROVIDER_INVITE */;
25346
26292
  const type = "service-provider-invite" /* SERVICE_PROVIDER_INVITE */;
@@ -25360,12 +26306,12 @@ function useVerificationService() {
25360
26306
  };
25361
26307
  try {
25362
26308
  const id = await _add(value);
25363
- const filePath = (0, import_node_server_utils60.getDirectory)(
26309
+ const filePath = (0, import_node_server_utils62.getDirectory)(
25364
26310
  __dirname,
25365
26311
  `./public/handlebars/${value.type}`
25366
26312
  );
25367
26313
  const link = `${APP_MAIN}/verify/${value.type}/${id}`;
25368
- const emailContent = (0, import_node_server_utils60.compileHandlebar)({
26314
+ const emailContent = (0, import_node_server_utils62.compileHandlebar)({
25369
26315
  context: {
25370
26316
  email,
25371
26317
  app,
@@ -25380,682 +26326,226 @@ function useVerificationService() {
25380
26326
  to: email,
25381
26327
  subject,
25382
26328
  html: emailContent,
25383
- sender: "iService365" /* ISERVICE365 */
25384
- });
25385
- return id;
25386
- } catch (error2) {
25387
- throw error2;
25388
- }
25389
- }
25390
- async function createForgetPassword(email) {
25391
- const value = {
25392
- type: "forget-password",
25393
- email,
25394
- expireAt: new Date((/* @__PURE__ */ new Date()).getTime() + 10 * 60 * 1e3).toISOString(),
25395
- // 10 minutes from now
25396
- createdAt: (/* @__PURE__ */ new Date()).toISOString()
25397
- };
25398
- try {
25399
- const user = await getUserByEmail(email);
25400
- if (!user) {
25401
- throw new import_node_server_utils60.NotFoundError("Email not found");
25402
- }
25403
- const res = await add(value);
25404
- const dir = __dirname;
25405
- const filePath = (0, import_node_server_utils60.getDirectory)(dir, "./public/handlebars/forget-password");
25406
- const emailContent = (0, import_node_server_utils60.compileHandlebar)({
25407
- context: {
25408
- userName: user?.name,
25409
- validity: VERIFICATION_FORGET_PASSWORD_DURATION,
25410
- link: `${APP_MAIN}/reset-password/${res}`
25411
- },
25412
- filePath
25413
- });
25414
- mailer2.sendMail({
25415
- to: email,
25416
- subject: "Forget Password",
25417
- sender: "iService365",
25418
- html: emailContent
25419
- }).catch((error) => {
25420
- import_node_server_utils60.logger.log({
25421
- level: "error",
25422
- message: `Error sending forget password email: ${error}`
25423
- });
25424
- });
25425
- return "Successfully created a link to reset password. Please check your email.";
25426
- } catch (error) {
25427
- throw new import_node_server_utils60.InternalServerError("Failed to create forget password link.");
25428
- }
25429
- }
25430
- async function getById(id) {
25431
- try {
25432
- const _id = await _getById(id);
25433
- if (!_id) {
25434
- throw new import_node_server_utils60.NotFoundError("Verification not found.");
25435
- }
25436
- return _id;
25437
- } catch (error) {
25438
- throw error;
25439
- }
25440
- }
25441
- function errorByType(type, status) {
25442
- if ((type === "user-invite" || type === "member-invite" || type === "service-provider-invite" || type === "service-provider-create-org") && status === "expired") {
25443
- throw new import_node_server_utils60.BadRequestError(
25444
- "Invitation has already expired, please contact admin to resend the invitation."
25445
- );
25446
- }
25447
- if (type === "user-sign-up" && status === "expired") {
25448
- throw new import_node_server_utils60.BadRequestError(
25449
- "Sign up verification has expired, please sign up again to get a new verification link."
25450
- );
25451
- }
25452
- if ((type === "user-invite" || type === "member-invite" || type === "service-provider-invite" || type === "service-provider-create-org") && status === "complete") {
25453
- throw new import_node_server_utils60.BadRequestError(
25454
- "User already registered, please login to continue."
25455
- );
25456
- }
25457
- if (type === "forget-password" && status === "complete") {
25458
- throw new import_node_server_utils60.BadRequestError(
25459
- "Forget password verification has already been used, please request a new one."
25460
- );
25461
- }
25462
- if (type === "forget-password" && status === "expired") {
25463
- throw new import_node_server_utils60.BadRequestError(
25464
- "Forget password verification has expired, please request a new one."
25465
- );
25466
- }
25467
- throw new import_node_server_utils60.BadRequestError("Invalid verification.");
25468
- }
25469
- async function verify(id) {
25470
- const session = import_node_server_utils60.useAtlas.getClient()?.startSession();
25471
- session?.startTransaction();
25472
- try {
25473
- const _id = await _getById(id);
25474
- if (!_id) {
25475
- throw new import_node_server_utils60.NotFoundError("Verification not found.");
25476
- }
25477
- if (_id.status === "expired") {
25478
- errorByType(_id.type, "expired");
25479
- }
25480
- if (_id.status === "cancelled") {
25481
- throw new import_node_server_utils60.BadRequestError("Verification already cancelled.");
25482
- }
25483
- if (_id.status === "complete") {
25484
- throw new import_node_server_utils60.BadRequestError("Verification already completed.");
25485
- }
25486
- const expiration = new Date(_id.expireAt).getTime();
25487
- const now = (/* @__PURE__ */ new Date()).getTime();
25488
- if (now > expiration) {
25489
- await _updateStatusById(id, "expired");
25490
- errorByType(_id.type, "expired");
25491
- }
25492
- return _id;
25493
- } catch (error) {
25494
- await session?.abortTransaction();
25495
- import_node_server_utils60.logger.log({
25496
- level: "info",
25497
- message: `Error verifying user invitation: ${error}`
25498
- });
25499
- throw error;
25500
- } finally {
25501
- session?.endSession();
25502
- }
25503
- }
25504
- async function cancelUserInvitation(id) {
25505
- try {
25506
- await updateStatusById(id, "cancelled");
25507
- } catch (error) {
25508
- throw new import_node_server_utils60.InternalServerError(
25509
- `Error cancelling user invitation: ${error}`
25510
- );
25511
- }
25512
- }
25513
- async function updateStatusById(_id, status) {
25514
- try {
25515
- await _updateStatusById(_id, status);
25516
- return "Successfully updated verification status.";
25517
- } catch (error) {
25518
- throw error;
25519
- }
25520
- }
25521
- async function signUp({
25522
- email,
25523
- metadata
25524
- }) {
25525
- try {
25526
- const user = await getUserByEmail(email);
25527
- if (user) {
25528
- throw new import_node_server_utils60.BadRequestError(
25529
- `Email ${email} is already registered, please login to continue.`
25530
- );
25531
- }
25532
- const value = {
25533
- type: "user-sign-up",
25534
- email,
25535
- metadata,
25536
- expireAt: new Date((/* @__PURE__ */ new Date()).getTime() + 5 * 60 * 1e3).toISOString(),
25537
- // 5mins from now
25538
- createdAt: (/* @__PURE__ */ new Date()).toISOString()
25539
- };
25540
- const res = await add(value);
25541
- const dir = __dirname;
25542
- const filePath = (0, import_node_server_utils60.getDirectory)(dir, "./public/handlebars/sign-up");
25543
- const emailContent = (0, import_node_server_utils60.compileHandlebar)({
25544
- context: {
25545
- email,
25546
- validity: "5 minutes",
25547
- link: `${APP_MAIN}/sign-up/${res}`
25548
- },
25549
- filePath
25550
- });
25551
- mailer2.sendMail({
25552
- to: email,
25553
- subject: "Sign Up Verification",
25554
- html: emailContent,
25555
- sender: "iService365"
25556
- }).catch((error) => {
25557
- import_node_server_utils60.logger.log({
25558
- level: "error",
25559
- message: `Error sending user invite email: ${error}`
25560
- });
25561
- });
25562
- return res;
25563
- } catch (error) {
25564
- throw error;
25565
- }
25566
- }
25567
- async function checkExpiredInvitation() {
25568
- try {
25569
- const verifications = await _getByStatus("pending");
25570
- const now = Date.now();
25571
- for (const verification of verifications) {
25572
- if (now > new Date(verification.expireAt).getTime()) {
25573
- await _updateStatusById(verification._id.toString(), "expired");
25574
- }
25575
- }
25576
- return "Successfully checked for expired invitations.";
25577
- } catch (error) {
25578
- import_node_server_utils60.logger.log({
25579
- level: "info",
25580
- message: `Error checking expired user invitation: ${error}`
25581
- });
25582
- throw error;
25583
- }
25584
- }
25585
- return {
25586
- createUserInvite,
25587
- createForgetPassword,
25588
- createServiceProviderInvite,
25589
- getById,
25590
- verify,
25591
- cancelUserInvitation,
25592
- updateStatusById,
25593
- signUp,
25594
- checkExpiredInvitation,
25595
- createSimpleUserInvite,
25596
- createSimpleMemberInvite,
25597
- createSimpleServiceProviderInvite
25598
- };
25599
- }
25600
-
25601
- // src/services/user.service.ts
25602
- var import_node_server_utils62 = require("@7365admin1/node-server-utils");
25603
-
25604
- // src/repositories/role.repo.ts
25605
- var import_node_server_utils61 = require("@7365admin1/node-server-utils");
25606
- var import_mongodb57 = require("mongodb");
25607
- var HISTORY_LIMIT = 10;
25608
- function useRoleRepo() {
25609
- const db2 = import_node_server_utils61.useAtlas.getDb();
25610
- if (!db2) {
25611
- throw new import_node_server_utils61.InternalServerError("Unable to connect to server.");
25612
- }
25613
- const namespace_collection3 = "roles";
25614
- const collection = db2.collection(namespace_collection3);
25615
- async function createIndex() {
25616
- try {
25617
- await collection.createIndexes([
25618
- { key: { name: 1 } },
25619
- { key: { type: 1 } },
25620
- { key: { status: 1 } }
25621
- ]);
25622
- } catch (error) {
25623
- throw new import_node_server_utils61.InternalServerError("Failed to create index on role.");
25624
- }
25625
- }
25626
- async function createTextIndex() {
25627
- try {
25628
- await collection.createIndex({ name: "text" });
25629
- } catch (error) {
25630
- throw new import_node_server_utils61.InternalServerError("Failed to create text index on role.");
25631
- }
25632
- }
25633
- async function createUniqueIndex() {
25634
- try {
25635
- await collection.createIndex(
25636
- { name: 1, type: 1, org: 1, deletedAt: 1 },
25637
- { unique: true }
25638
- );
25639
- } catch (error) {
25640
- throw new import_node_server_utils61.InternalServerError("Failed to create unique index on role.");
25641
- }
25642
- }
25643
- const { delNamespace, setCache, getCache } = (0, import_node_server_utils61.useCache)(namespace_collection3);
25644
- const { delNamespace: _delDashboardNameSpace } = (0, import_node_server_utils61.useCache)("dashboard");
25645
- async function nameTakenMessage(value) {
25646
- const name = String(value?.name ?? "");
25647
- const fallback = `A role named "${name}" already exists in this organisation. Please choose a different name.`;
25648
- try {
25649
- const clash = await collection.findOne(
25650
- {
25651
- name,
25652
- type: value.type,
25653
- org: value.org,
25654
- deletedAt: value.deletedAt ?? ""
25655
- },
25656
- { projection: { site: 1 } }
25657
- );
25658
- if (!clash)
25659
- return fallback;
25660
- if (!clash.site) {
25661
- 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.`;
25662
- }
25663
- const siteName = async (id) => {
25664
- if (!id)
25665
- return "";
25666
- const doc = await db2?.collection("sites").findOne(
25667
- { _id: new import_mongodb57.ObjectId(String(id)) },
25668
- { projection: { name: 1 } }
25669
- ).catch(() => null);
25670
- return doc?.name ?? "";
25671
- };
25672
- if (value.site && String(value.site) === String(clash.site)) {
25673
- return `A role named "${name}" already exists at this site. Please choose a different name.`;
25674
- }
25675
- const [there, here] = await Promise.all([siteName(clash.site), siteName(value.site)]);
25676
- const suggestion = here ? `, for example "${name} - ${here}"` : "";
25677
- 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}.`;
25678
- } catch {
25679
- return fallback;
25680
- }
25681
- }
25682
- async function addRole(value, session) {
25683
- value = new MRole({ ...value, type: serviceAppType(value.type) });
25684
- try {
25685
- const res = await collection.insertOne(value, { session });
25686
- delNamespace().then(() => {
25687
- import_node_server_utils61.logger.info(`Cache cleared for namespace: ${namespace_collection3}`);
25688
- }).catch((err) => {
25689
- import_node_server_utils61.logger.error(
25690
- `Failed to clear cache for namespace: ${namespace_collection3}`,
25691
- err
25692
- );
25693
- });
25694
- return res.insertedId;
25695
- } catch (error) {
25696
- import_node_server_utils61.logger.log({ level: "error", message: `${error}` });
25697
- const isDuplicated = error.message.includes("duplicate");
25698
- if (isDuplicated) {
25699
- throw new import_node_server_utils61.BadRequestError(await nameTakenMessage(value));
25700
- }
25701
- throw new import_node_server_utils61.InternalServerError("Failed to create role.");
25702
- }
25703
- }
25704
- async function getRoleByUserId(value) {
25705
- try {
25706
- value = new import_mongodb57.ObjectId(value);
25707
- } catch (error) {
25708
- throw new import_node_server_utils61.BadRequestError("Invalid user ID format.");
25709
- }
25710
- try {
25711
- const cacheKey = (0, import_node_server_utils61.makeCacheKey)(namespace_collection3, { user: value });
25712
- const cachedData = await getCache(cacheKey);
25713
- if (cachedData) {
25714
- import_node_server_utils61.logger.info(`Cache hit for key: ${cacheKey}`);
25715
- return cachedData;
25716
- }
25717
- const data = await collection.findOne({ user: value });
25718
- setCache(cacheKey, data, 15 * 60).then(() => {
25719
- import_node_server_utils61.logger.info(`Cache set for key: ${cacheKey}`);
25720
- }).catch((err) => {
25721
- import_node_server_utils61.logger.error(`Failed to set cache for key: ${cacheKey}`, err);
26329
+ sender: "iService365" /* ISERVICE365 */
25722
26330
  });
25723
- return data;
25724
- } catch (error) {
25725
- throw new import_node_server_utils61.InternalServerError("Failed to retrieve role by user ID.");
26331
+ return id;
26332
+ } catch (error2) {
26333
+ throw error2;
25726
26334
  }
25727
26335
  }
25728
- async function getOwnerRolesByTypeOrg(type, org) {
25729
- try {
25730
- org = new import_mongodb57.ObjectId(org);
25731
- } catch (error) {
25732
- throw new import_node_server_utils61.BadRequestError("Invalid organization ID format.");
25733
- }
25734
- const cacheOptions = {
25735
- org: org.toString(),
25736
- permissions: "*",
25737
- type,
25738
- status: "active"
26336
+ async function createForgetPassword(email) {
26337
+ const value = {
26338
+ type: "forget-password",
26339
+ email,
26340
+ expireAt: new Date((/* @__PURE__ */ new Date()).getTime() + 10 * 60 * 1e3).toISOString(),
26341
+ // 10 minutes from now
26342
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
25739
26343
  };
25740
- const cacheKey = (0, import_node_server_utils61.makeCacheKey)(namespace_collection3, cacheOptions);
25741
- const cachedData = await getCache(cacheKey);
25742
- if (cachedData) {
25743
- import_node_server_utils61.logger.info(`Cache hit for key: ${cacheKey}`);
25744
- return cachedData;
25745
- }
25746
- try {
25747
- const data = await collection.find({ org, permissions: "*", type: appTypeQuery(type), status: "active" }).toArray();
25748
- setCache(cacheKey, data, 15 * 60).then(() => {
25749
- import_node_server_utils61.logger.info(`Cache set for key: ${cacheKey}`);
25750
- }).catch((err) => {
25751
- import_node_server_utils61.logger.error(`Failed to set cache for key: ${cacheKey}`, err);
25752
- });
25753
- return data;
25754
- } catch (error) {
25755
- throw new import_node_server_utils61.InternalServerError("Failed to retrieve role by user ID.");
25756
- }
25757
- }
25758
- async function getRoleById(_id) {
25759
26344
  try {
25760
- _id = new import_mongodb57.ObjectId(_id);
25761
- } catch (error) {
25762
- throw new import_node_server_utils61.BadRequestError("Invalid role ID format.");
25763
- }
25764
- try {
25765
- const cacheKey = (0, import_node_server_utils61.makeCacheKey)(namespace_collection3, { _id });
25766
- const cachedData = await getCache(cacheKey);
25767
- if (cachedData) {
25768
- import_node_server_utils61.logger.info(`Cache hit for key: ${cacheKey}`);
25769
- return cachedData;
26345
+ const user = await getUserByEmail(email);
26346
+ if (!user) {
26347
+ throw new import_node_server_utils62.NotFoundError("Email not found");
25770
26348
  }
25771
- const data = await collection.findOne({ _id });
25772
- setCache(cacheKey, data, 15 * 60).then(() => {
25773
- import_node_server_utils61.logger.info(`Cache set for key: ${cacheKey}`);
25774
- }).catch((err) => {
25775
- import_node_server_utils61.logger.error(`Failed to set cache for key: ${cacheKey}`, err);
26349
+ const res = await add(value);
26350
+ const dir = __dirname;
26351
+ const filePath = (0, import_node_server_utils62.getDirectory)(dir, "./public/handlebars/forget-password");
26352
+ const emailContent = (0, import_node_server_utils62.compileHandlebar)({
26353
+ context: {
26354
+ userName: user?.name,
26355
+ validity: VERIFICATION_FORGET_PASSWORD_DURATION,
26356
+ link: `${APP_MAIN}/reset-password/${res}`
26357
+ },
26358
+ filePath
25776
26359
  });
25777
- return data;
26360
+ mailer2.sendMail({
26361
+ to: email,
26362
+ subject: "Forget Password",
26363
+ sender: "iService365",
26364
+ html: emailContent
26365
+ }).catch((error) => {
26366
+ import_node_server_utils62.logger.log({
26367
+ level: "error",
26368
+ message: `Error sending forget password email: ${error}`
26369
+ });
26370
+ });
26371
+ return "Successfully created a link to reset password. Please check your email.";
25778
26372
  } catch (error) {
25779
- throw new import_node_server_utils61.InternalServerError("Failed to retrieve role by ID.");
26373
+ throw new import_node_server_utils62.InternalServerError("Failed to create forget password link.");
25780
26374
  }
25781
26375
  }
25782
- async function getRoleByName(name, type) {
25783
- if (!name) {
25784
- throw new import_node_server_utils61.BadRequestError("Role name is required.");
25785
- }
25786
- type = serviceAppType(type);
25787
- const query2 = type ? { name, type: appTypeQuery(type) } : { name };
26376
+ async function getById(id) {
25788
26377
  try {
25789
- const cacheKey = (0, import_node_server_utils61.makeCacheKey)(namespace_collection3, query2);
25790
- const cachedData = await getCache(cacheKey);
25791
- if (cachedData) {
25792
- import_node_server_utils61.logger.info(`Cache hit for key: ${cacheKey}`);
25793
- return cachedData;
26378
+ const _id = await _getById(id);
26379
+ if (!_id) {
26380
+ throw new import_node_server_utils62.NotFoundError("Verification not found.");
25794
26381
  }
25795
- const data = await collection.findOne(query2);
25796
- setCache(cacheKey, data, 15 * 60).then(() => {
25797
- import_node_server_utils61.logger.info(`Cache set for key: ${cacheKey}`);
25798
- }).catch((err) => {
25799
- import_node_server_utils61.logger.error(`Failed to set cache for key: ${cacheKey}`, err);
25800
- });
25801
- return data;
26382
+ return _id;
25802
26383
  } catch (error) {
25803
- throw new import_node_server_utils61.InternalServerError("Failed to retrieve role by name.");
26384
+ throw error;
25804
26385
  }
25805
26386
  }
25806
- async function getRoles({
25807
- search = "",
25808
- page: page2 = 1,
25809
- limit = 10,
25810
- sort = {},
25811
- type = "",
25812
- org = "",
25813
- site = "",
25814
- scope = ""
25815
- }) {
25816
- page2 = page2 > 0 ? page2 - 1 : 0;
25817
- if (org && typeof org === "string" && org.length === 24) {
25818
- try {
25819
- org = new import_mongodb57.ObjectId(org);
25820
- } catch (error) {
25821
- throw new import_node_server_utils61.BadRequestError("Invalid organization ID format.");
25822
- }
25823
- }
25824
- if (site && typeof site === "string" && site.length === 24) {
25825
- try {
25826
- site = new import_mongodb57.ObjectId(site);
25827
- } catch (error) {
25828
- throw new import_node_server_utils61.BadRequestError("Invalid site ID format.");
25829
- }
25830
- }
25831
- const query2 = { status: "active", org };
25832
- const cacheOptions = { status: "active", org };
25833
- if (site) {
25834
- query2.$or = [{ site }, { site: "" }, { site: null }];
25835
- cacheOptions.site = site.toString();
25836
- } else if (scope === "organisation") {
25837
- query2.site = { $in: ["", null] };
25838
- cacheOptions.scope = scope;
26387
+ function errorByType(type, status) {
26388
+ if ((type === "user-invite" || type === "member-invite" || type === "service-provider-invite" || type === "service-provider-create-org") && status === "expired") {
26389
+ throw new import_node_server_utils62.BadRequestError(
26390
+ "Invitation has already expired, please contact admin to resend the invitation."
26391
+ );
25839
26392
  }
25840
- sort = Object.keys(sort).length > 0 ? sort : { _id: -1 };
25841
- cacheOptions.sort = JSON.stringify(sort);
25842
- if (search) {
25843
- query2.$text = { $search: search };
25844
- cacheOptions.search = search;
26393
+ if (type === "user-sign-up" && status === "expired") {
26394
+ throw new import_node_server_utils62.BadRequestError(
26395
+ "Sign up verification has expired, please sign up again to get a new verification link."
26396
+ );
25845
26397
  }
25846
- if (type) {
25847
- query2.type = appTypeQuery(type);
25848
- cacheOptions.type = serviceAppType(type);
26398
+ if ((type === "user-invite" || type === "member-invite" || type === "service-provider-invite" || type === "service-provider-create-org") && status === "complete") {
26399
+ throw new import_node_server_utils62.BadRequestError(
26400
+ "User already registered, please login to continue."
26401
+ );
25849
26402
  }
25850
- const cacheKey = (0, import_node_server_utils61.makeCacheKey)(namespace_collection3, cacheOptions);
25851
- const cachedData = await getCache(cacheKey);
25852
- if (cachedData) {
25853
- import_node_server_utils61.logger.info(`Cache hit for key: ${cacheKey}`);
25854
- return cachedData;
26403
+ if (type === "forget-password" && status === "complete") {
26404
+ throw new import_node_server_utils62.BadRequestError(
26405
+ "Forget password verification has already been used, please request a new one."
26406
+ );
25855
26407
  }
25856
- try {
25857
- const items = await collection.aggregate([
25858
- { $match: query2 },
25859
- { $sort: sort },
25860
- { $skip: page2 * limit },
25861
- { $limit: limit }
25862
- ]).toArray();
25863
- const length = await collection.countDocuments(query2);
25864
- const data = (0, import_node_server_utils61.paginate)(items, page2, limit, length);
25865
- setCache(cacheKey, data, 15 * 60).then(() => {
25866
- import_node_server_utils61.logger.info(`Cache set for key: ${cacheKey}`);
25867
- }).catch((err) => {
25868
- import_node_server_utils61.logger.error(`Failed to set cache for key: ${cacheKey}`, err);
25869
- });
25870
- return data;
25871
- } catch (error) {
25872
- import_node_server_utils61.logger.log({ level: "error", message: `${error}` });
25873
- throw error;
26408
+ if (type === "forget-password" && status === "expired") {
26409
+ throw new import_node_server_utils62.BadRequestError(
26410
+ "Forget password verification has expired, please request a new one."
26411
+ );
25874
26412
  }
26413
+ throw new import_node_server_utils62.BadRequestError("Invalid verification.");
25875
26414
  }
25876
- async function getActiveByTypeOrg(type, org) {
26415
+ async function verify(id) {
26416
+ const session = import_node_server_utils62.useAtlas.getClient()?.startSession();
26417
+ session?.startTransaction();
25877
26418
  try {
25878
- if (typeof org === "string" && org) {
25879
- org = new import_mongodb57.ObjectId(org);
26419
+ const _id = await _getById(id);
26420
+ if (!_id) {
26421
+ throw new import_node_server_utils62.NotFoundError("Verification not found.");
26422
+ }
26423
+ if (_id.status === "expired") {
26424
+ errorByType(_id.type, "expired");
26425
+ }
26426
+ if (_id.status === "cancelled") {
26427
+ throw new import_node_server_utils62.BadRequestError("Verification already cancelled.");
26428
+ }
26429
+ if (_id.status === "complete") {
26430
+ throw new import_node_server_utils62.BadRequestError("Verification already completed.");
26431
+ }
26432
+ const expiration = new Date(_id.expireAt).getTime();
26433
+ const now = (/* @__PURE__ */ new Date()).getTime();
26434
+ if (now > expiration) {
26435
+ await _updateStatusById(id, "expired");
26436
+ errorByType(_id.type, "expired");
25880
26437
  }
26438
+ return _id;
25881
26439
  } catch (error) {
25882
- throw new import_node_server_utils61.BadRequestError("Invalid organization ID format.");
26440
+ await session?.abortTransaction();
26441
+ import_node_server_utils62.logger.log({
26442
+ level: "info",
26443
+ message: `Error verifying user invitation: ${error}`
26444
+ });
26445
+ throw error;
26446
+ } finally {
26447
+ session?.endSession();
25883
26448
  }
26449
+ }
26450
+ async function cancelUserInvitation(id) {
25884
26451
  try {
25885
- return await collection.find({ status: "active", type, org }).sort({ name: 1 }).toArray();
26452
+ await updateStatusById(id, "cancelled");
25886
26453
  } catch (error) {
25887
- throw new import_node_server_utils61.InternalServerError("Failed to retrieve replacement roles.");
25888
- }
25889
- }
25890
- async function assertNotSeededOwnerRole(_id) {
25891
- const role = await collection.findOne(
25892
- { _id },
25893
- { projection: { type: 1, default: 1 } }
25894
- );
25895
- if (role?.type === PLATFORM_STAFF_ROLE_TYPE && role?.default === true) {
25896
- throw new import_node_server_utils61.BadRequestError(
25897
- "The Seven365 platform owner role cannot be edited or deleted."
26454
+ throw new import_node_server_utils62.InternalServerError(
26455
+ `Error cancelling user invitation: ${error}`
25898
26456
  );
25899
26457
  }
25900
26458
  }
25901
- async function updateRole(_id, value, session) {
25902
- if (!_id) {
25903
- throw new import_node_server_utils61.BadRequestError("Role ID is required.");
25904
- }
26459
+ async function updateStatusById(_id, status) {
25905
26460
  try {
25906
- _id = new import_mongodb57.ObjectId(_id);
26461
+ await _updateStatusById(_id, status);
26462
+ return "Successfully updated verification status.";
25907
26463
  } catch (error) {
25908
- throw new import_node_server_utils61.BadRequestError("Invalid role ID format.");
25909
- }
25910
- await assertNotSeededOwnerRole(_id);
25911
- if (!value.name) {
25912
- delete value.name;
25913
- }
25914
- if (!value.permissions) {
25915
- delete value.permissions;
25916
- }
25917
- if (value.name || value.permissions) {
25918
- try {
25919
- await collection.updateOne({ _id }, { $set: value }, { session });
25920
- delNamespace().then(() => {
25921
- import_node_server_utils61.logger.info(`Cache cleared for namespace: ${namespace_collection3}`);
25922
- }).catch((err) => {
25923
- import_node_server_utils61.logger.error(
25924
- `Failed to clear cache for namespace: ${namespace_collection3}`,
25925
- err
25926
- );
25927
- });
25928
- return _id;
25929
- } catch (error) {
25930
- const isDuplicated = error.message.includes("duplicate");
25931
- if (isDuplicated) {
25932
- const current = await collection.findOne({ _id }, { projection: { type: 1, org: 1, site: 1, deletedAt: 1 } }).catch(() => null);
25933
- throw new import_node_server_utils61.BadRequestError(
25934
- await nameTakenMessage({ ...current ?? {}, name: value.name })
25935
- );
25936
- }
25937
- throw new import_node_server_utils61.InternalServerError("Failed to update role.");
25938
- }
25939
- } else {
25940
- throw new import_node_server_utils61.BadRequestError("No fields to update.");
26464
+ throw error;
25941
26465
  }
25942
26466
  }
25943
- async function updatePermissionsById(_id, permissions, session) {
25944
- if (!_id) {
25945
- throw new import_node_server_utils61.BadRequestError("Role ID is required.");
25946
- }
25947
- try {
25948
- _id = new import_mongodb57.ObjectId(_id);
25949
- } catch (error) {
25950
- throw new import_node_server_utils61.BadRequestError("Invalid role ID format.");
25951
- }
25952
- await assertNotSeededOwnerRole(_id);
25953
- if (!permissions) {
25954
- throw new import_node_server_utils61.BadRequestError("Permissions are required.");
25955
- }
25956
- if (permissions.length === 0) {
25957
- throw new import_node_server_utils61.BadRequestError("Permissions cannot be empty.");
25958
- }
26467
+ async function signUp({
26468
+ email,
26469
+ metadata
26470
+ }) {
25959
26471
  try {
25960
- await collection.updateOne(
25961
- { _id },
25962
- { $set: { permissions } },
25963
- { session }
25964
- );
25965
- delNamespace().then(() => {
25966
- import_node_server_utils61.logger.info(`Cache cleared for namespace: ${namespace_collection3}`);
25967
- }).catch((err) => {
25968
- import_node_server_utils61.logger.error(
25969
- `Failed to clear cache for namespace: ${namespace_collection3}`,
25970
- err
26472
+ const user = await getUserByEmail(email);
26473
+ if (user) {
26474
+ throw new import_node_server_utils62.BadRequestError(
26475
+ `Email ${email} is already registered, please login to continue.`
25971
26476
  );
26477
+ }
26478
+ const value = {
26479
+ type: "user-sign-up",
26480
+ email,
26481
+ metadata,
26482
+ expireAt: new Date((/* @__PURE__ */ new Date()).getTime() + 5 * 60 * 1e3).toISOString(),
26483
+ // 5mins from now
26484
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
26485
+ };
26486
+ const res = await add(value);
26487
+ const dir = __dirname;
26488
+ const filePath = (0, import_node_server_utils62.getDirectory)(dir, "./public/handlebars/sign-up");
26489
+ const emailContent = (0, import_node_server_utils62.compileHandlebar)({
26490
+ context: {
26491
+ email,
26492
+ validity: "5 minutes",
26493
+ link: `${APP_MAIN}/sign-up/${res}`
26494
+ },
26495
+ filePath
25972
26496
  });
25973
- return "Successfully updated role permissions.";
25974
- } catch (error) {
25975
- throw new import_node_server_utils61.InternalServerError("Failed to update role permissions.");
25976
- }
25977
- }
25978
- async function appendPermissionHistory(_id, entry, session) {
25979
- try {
25980
- _id = new import_mongodb57.ObjectId(_id);
26497
+ mailer2.sendMail({
26498
+ to: email,
26499
+ subject: "Sign Up Verification",
26500
+ html: emailContent,
26501
+ sender: "iService365"
26502
+ }).catch((error) => {
26503
+ import_node_server_utils62.logger.log({
26504
+ level: "error",
26505
+ message: `Error sending user invite email: ${error}`
26506
+ });
26507
+ });
26508
+ return res;
25981
26509
  } catch (error) {
25982
- throw new import_node_server_utils61.BadRequestError("Invalid role ID format.");
26510
+ throw error;
25983
26511
  }
25984
- await collection.updateOne(
25985
- { _id },
25986
- // cast: the collection's generic has no `permissionHistory` in its
25987
- // `PushOperator`, because the field is new and optional. The operator
25988
- // itself is ordinary `$push`/`$slice`.
25989
- { $push: { permissionHistory: { $each: [entry], $slice: -HISTORY_LIMIT } } },
25990
- { session }
25991
- );
25992
- delNamespace().then(() => {
25993
- import_node_server_utils61.logger.info(`Cache cleared for namespace: ${namespace_collection3}`);
25994
- }).catch((err) => {
25995
- import_node_server_utils61.logger.error(
25996
- `Failed to clear cache for namespace: ${namespace_collection3}`,
25997
- err
25998
- );
25999
- });
26000
26512
  }
26001
- async function deleteRole(_id, session) {
26002
- try {
26003
- _id = new import_mongodb57.ObjectId(_id);
26004
- } catch (error) {
26005
- throw new import_node_server_utils61.BadRequestError("Invalid role ID format.");
26006
- }
26007
- await assertNotSeededOwnerRole(_id);
26513
+ async function checkExpiredInvitation() {
26008
26514
  try {
26009
- const updateValue = {
26010
- status: "deleted",
26011
- updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
26012
- deletedAt: (/* @__PURE__ */ new Date()).toISOString()
26013
- };
26014
- const res = await collection.updateOne(
26015
- { _id },
26016
- { $set: updateValue },
26017
- { session }
26018
- );
26019
- if (res.modifiedCount === 0) {
26020
- throw new import_node_server_utils61.InternalServerError("Unable to update role status.");
26515
+ const verifications = await _getByStatus("pending");
26516
+ const now = Date.now();
26517
+ for (const verification of verifications) {
26518
+ if (now > new Date(verification.expireAt).getTime()) {
26519
+ await _updateStatusById(verification._id.toString(), "expired");
26520
+ }
26021
26521
  }
26022
- delNamespace().then(() => {
26023
- import_node_server_utils61.logger.info(`Cache cleared for namespace: ${namespace_collection3}`);
26024
- }).catch((err) => {
26025
- import_node_server_utils61.logger.error(
26026
- `Failed to clear cache for namespace: ${namespace_collection3}`,
26027
- err
26028
- );
26029
- });
26030
- _delDashboardNameSpace().then(() => {
26031
- import_node_server_utils61.logger.info(`Cache cleared for namespace: dashboard`);
26032
- }).catch((err) => {
26033
- import_node_server_utils61.logger.error(`Failed to clear cache for namespace: dashboard`, err);
26034
- });
26035
- return "Successfully deleted role permission.";
26522
+ return "Successfully checked for expired invitations.";
26036
26523
  } catch (error) {
26037
- throw new import_node_server_utils61.InternalServerError("Failed to delete role.");
26524
+ import_node_server_utils62.logger.log({
26525
+ level: "info",
26526
+ message: `Error checking expired user invitation: ${error}`
26527
+ });
26528
+ throw error;
26038
26529
  }
26039
26530
  }
26040
26531
  return {
26041
- createIndex,
26042
- createTextIndex,
26043
- createUniqueIndex,
26044
- addRole,
26045
- getRoleByUserId,
26046
- getRoleById,
26047
- getRoleByName,
26048
- getRoles,
26049
- getActiveByTypeOrg,
26050
- updateRole,
26051
- updatePermissionsById,
26052
- appendPermissionHistory,
26053
- deleteRole,
26054
- getOwnerRolesByTypeOrg
26532
+ createUserInvite,
26533
+ createForgetPassword,
26534
+ createServiceProviderInvite,
26535
+ getById,
26536
+ verify,
26537
+ cancelUserInvitation,
26538
+ updateStatusById,
26539
+ signUp,
26540
+ checkExpiredInvitation,
26541
+ createSimpleUserInvite,
26542
+ createSimpleMemberInvite,
26543
+ createSimpleServiceProviderInvite
26055
26544
  };
26056
26545
  }
26057
26546
 
26058
26547
  // src/services/user.service.ts
26548
+ var import_node_server_utils63 = require("@7365admin1/node-server-utils");
26059
26549
  var import_multer = require("multer");
26060
26550
  function useUserService() {
26061
26551
  const {
@@ -26082,7 +26572,7 @@ function useUserService() {
26082
26572
  role = result._id?.toString();
26083
26573
  }
26084
26574
  } catch (error) {
26085
- import_node_server_utils62.logger.error(error.message);
26575
+ import_node_server_utils63.logger.error(error.message);
26086
26576
  }
26087
26577
  if (!role) {
26088
26578
  try {
@@ -26094,15 +26584,15 @@ function useUserService() {
26094
26584
  });
26095
26585
  role = result.toString();
26096
26586
  } catch (error) {
26097
- import_node_server_utils62.logger.error(error.message);
26587
+ import_node_server_utils63.logger.error(error.message);
26098
26588
  }
26099
26589
  if (!role) {
26100
- import_node_server_utils62.logger.error("Failed to create default role.");
26590
+ import_node_server_utils63.logger.error("Failed to create default role.");
26101
26591
  }
26102
26592
  }
26103
26593
  let user = "";
26104
26594
  if (!DEFAULT_USER_EMAIL) {
26105
- 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.");
26106
26596
  }
26107
26597
  try {
26108
26598
  const result = await getUserByEmail(DEFAULT_USER_EMAIL);
@@ -26110,11 +26600,11 @@ function useUserService() {
26110
26600
  user = result._id?.toString();
26111
26601
  }
26112
26602
  } catch (error) {
26113
- import_node_server_utils62.logger.error(error.message);
26603
+ import_node_server_utils63.logger.error(error.message);
26114
26604
  }
26115
26605
  if (!user) {
26116
26606
  try {
26117
- 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);
26118
26608
  const userId = await addUser({
26119
26609
  email: DEFAULT_USER_EMAIL,
26120
26610
  password,
@@ -26122,10 +26612,10 @@ function useUserService() {
26122
26612
  });
26123
26613
  user = userId.toString();
26124
26614
  } catch (error) {
26125
- import_node_server_utils62.logger.error(error.message);
26615
+ import_node_server_utils63.logger.error(error.message);
26126
26616
  }
26127
26617
  if (!user) {
26128
- import_node_server_utils62.logger.error("Failed to create default user.");
26618
+ import_node_server_utils63.logger.error("Failed to create default user.");
26129
26619
  }
26130
26620
  }
26131
26621
  try {
@@ -26136,7 +26626,7 @@ function useUserService() {
26136
26626
  type: "admin"
26137
26627
  });
26138
26628
  } catch (error) {
26139
- import_node_server_utils62.logger.error(error.message);
26629
+ import_node_server_utils63.logger.error(error.message);
26140
26630
  }
26141
26631
  }
26142
26632
  async function createUserBySignUp({
@@ -26144,31 +26634,31 @@ function useUserService() {
26144
26634
  name = "",
26145
26635
  password = ""
26146
26636
  } = {}) {
26147
- const session = import_node_server_utils62.useAtlas.getClient()?.startSession();
26637
+ const session = import_node_server_utils63.useAtlas.getClient()?.startSession();
26148
26638
  session?.startTransaction();
26149
26639
  try {
26150
26640
  const signUp = await getVerificationById(id);
26151
26641
  if (!signUp) {
26152
- throw new import_node_server_utils62.BadRequestError("Invalid sign up link.");
26642
+ throw new import_node_server_utils63.BadRequestError("Invalid sign up link.");
26153
26643
  }
26154
26644
  if (signUp.status === "complete") {
26155
- throw new import_node_server_utils62.BadRequestError(
26645
+ throw new import_node_server_utils63.BadRequestError(
26156
26646
  "You have already an account created using this link."
26157
26647
  );
26158
26648
  }
26159
26649
  if (signUp.status === "cancelled") {
26160
- throw new import_node_server_utils62.BadRequestError("This invitation was cancelled.");
26650
+ throw new import_node_server_utils63.BadRequestError("This invitation was cancelled.");
26161
26651
  }
26162
26652
  const expired = new Date(signUp.expireAt) < /* @__PURE__ */ new Date();
26163
26653
  if (signUp.status === "expired" || expired) {
26164
- throw new import_node_server_utils62.BadRequestError("Sign up link expired.");
26654
+ throw new import_node_server_utils63.BadRequestError("Sign up link expired.");
26165
26655
  }
26166
26656
  const email = signUp.email;
26167
26657
  const _user = await getUserByEmail(signUp.email);
26168
26658
  if (_user) {
26169
- throw new import_node_server_utils62.BadRequestError(`User already exists: ${email}.`);
26659
+ throw new import_node_server_utils63.BadRequestError(`User already exists: ${email}.`);
26170
26660
  }
26171
- const hashedPassword = await (0, import_node_server_utils62.hashPassword)(password);
26661
+ const hashedPassword = await (0, import_node_server_utils63.hashPassword)(password);
26172
26662
  let org = null;
26173
26663
  if (signUp.metadata?.org) {
26174
26664
  org = await getOrgById(signUp.metadata.org);
@@ -26217,30 +26707,30 @@ function useUserService() {
26217
26707
  throw error;
26218
26708
  }
26219
26709
  if (newPassword !== passwordConfirmation) {
26220
- throw new import_node_server_utils62.BadRequestError("Passwords do not match.");
26710
+ throw new import_node_server_utils63.BadRequestError("Passwords do not match.");
26221
26711
  }
26222
26712
  let hashedPassword;
26223
- const session = import_node_server_utils62.useAtlas.getClient()?.startSession();
26713
+ const session = import_node_server_utils63.useAtlas.getClient()?.startSession();
26224
26714
  session?.startTransaction();
26225
26715
  try {
26226
- hashedPassword = await (0, import_node_server_utils62.hashPassword)(newPassword);
26716
+ hashedPassword = await (0, import_node_server_utils63.hashPassword)(newPassword);
26227
26717
  } catch (error) {
26228
- throw new import_node_server_utils62.InternalServerError(`Error hashing password: ${error}`);
26718
+ throw new import_node_server_utils63.InternalServerError(`Error hashing password: ${error}`);
26229
26719
  }
26230
26720
  try {
26231
26721
  const otpDoc = await getVerificationById(id);
26232
26722
  if (!otpDoc) {
26233
- 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.");
26234
26724
  }
26235
26725
  if (otpDoc.status === "complete") {
26236
- 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.");
26237
26727
  }
26238
26728
  const user = await getUserByEmail(otpDoc.email);
26239
26729
  if (!user) {
26240
- throw new import_node_server_utils62.NotFoundError("User not found.");
26730
+ throw new import_node_server_utils63.NotFoundError("User not found.");
26241
26731
  }
26242
26732
  if (!user._id) {
26243
- throw new import_node_server_utils62.InternalServerError("Invalid user ID.");
26733
+ throw new import_node_server_utils63.InternalServerError("Invalid user ID.");
26244
26734
  }
26245
26735
  await updateStatusById(id, "complete", session);
26246
26736
  await _resetPassword(
@@ -26260,7 +26750,7 @@ function useUserService() {
26260
26750
  session?.endSession();
26261
26751
  }
26262
26752
  }
26263
- const s3 = new import_node_server_utils62.useS3({
26753
+ const s3 = new import_node_server_utils63.useS3({
26264
26754
  accessKeyId: SPACES_ACCESS_KEY,
26265
26755
  secretAccessKey: SPACES_SECRET_KEY,
26266
26756
  endpoint: SPACES_ENDPOINT,
@@ -26273,7 +26763,7 @@ function useUserService() {
26273
26763
  user,
26274
26764
  previousProfile
26275
26765
  }) {
26276
- const session = import_node_server_utils62.useAtlas.getClient()?.startSession();
26766
+ const session = import_node_server_utils63.useAtlas.getClient()?.startSession();
26277
26767
  session?.startTransaction();
26278
26768
  const _file = {
26279
26769
  name: file.originalname,
@@ -26306,18 +26796,18 @@ function useUserService() {
26306
26796
  async function updatePasswordById(id, currentPassword, newPassword, passwordConfirmation) {
26307
26797
  let hashedPassword;
26308
26798
  const user = await getUserById(id);
26309
- const isPasswordValid = await (0, import_node_server_utils62.comparePassword)(
26799
+ const isPasswordValid = await (0, import_node_server_utils63.comparePassword)(
26310
26800
  currentPassword,
26311
26801
  user.password
26312
26802
  );
26313
26803
  if (!isPasswordValid)
26314
- throw new import_node_server_utils62.BadRequestError("Invalid current password.");
26804
+ throw new import_node_server_utils63.BadRequestError("Invalid current password.");
26315
26805
  if (newPassword !== passwordConfirmation)
26316
- throw new import_node_server_utils62.BadRequestError("Passwords do not match.");
26806
+ throw new import_node_server_utils63.BadRequestError("Passwords do not match.");
26317
26807
  try {
26318
- hashedPassword = await (0, import_node_server_utils62.hashPassword)(newPassword);
26808
+ hashedPassword = await (0, import_node_server_utils63.hashPassword)(newPassword);
26319
26809
  if (!hashedPassword)
26320
- throw new import_node_server_utils62.InternalServerError("Error hashing password.");
26810
+ throw new import_node_server_utils63.InternalServerError("Error hashing password.");
26321
26811
  return await updatePassword({ _id: id, password: hashedPassword });
26322
26812
  } catch (error) {
26323
26813
  throw error;
@@ -26355,8 +26845,8 @@ function useAuthController() {
26355
26845
  });
26356
26846
  if (error) {
26357
26847
  const messages = error.details.map((d) => d.message);
26358
- import_node_server_utils63.logger.log({ level: "error", message: messages.join(", ") });
26359
- 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(", ")));
26360
26850
  return;
26361
26851
  }
26362
26852
  const session = await _login(value);
@@ -26368,7 +26858,7 @@ function useAuthController() {
26368
26858
  res.cookie("sid", session.sid, cookieOptions).cookie("user", session.user, cookieOptions).json(session);
26369
26859
  return;
26370
26860
  } catch (error) {
26371
- import_node_server_utils63.logger.log({ level: "error", message: error.message });
26861
+ import_node_server_utils64.logger.log({ level: "error", message: error.message });
26372
26862
  next(error);
26373
26863
  return;
26374
26864
  }
@@ -26379,8 +26869,8 @@ function useAuthController() {
26379
26869
  });
26380
26870
  const { error, value } = validation.validate(req.body);
26381
26871
  if (error) {
26382
- import_node_server_utils63.logger.log({ level: "error", message: error.message });
26383
- 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));
26384
26874
  return;
26385
26875
  }
26386
26876
  try {
@@ -26388,7 +26878,7 @@ function useAuthController() {
26388
26878
  res.json(session);
26389
26879
  return;
26390
26880
  } catch (error2) {
26391
- import_node_server_utils63.logger.log({ level: "error", message: error2.message });
26881
+ import_node_server_utils64.logger.log({ level: "error", message: error2.message });
26392
26882
  next(error2);
26393
26883
  return;
26394
26884
  }
@@ -26396,7 +26886,7 @@ function useAuthController() {
26396
26886
  async function logout(req, res, next) {
26397
26887
  const sid = getSessionIdFromRequest(req);
26398
26888
  if (!sid) {
26399
- next(new import_node_server_utils63.BadRequestError("Session ID is required"));
26889
+ next(new import_node_server_utils64.BadRequestError("Session ID is required"));
26400
26890
  return;
26401
26891
  }
26402
26892
  const refreshToken2 = typeof req.body?.refreshToken === "string" ? req.body.refreshToken : void 0;
@@ -26405,7 +26895,7 @@ function useAuthController() {
26405
26895
  res.json({ message: "Logged out successfully" });
26406
26896
  return;
26407
26897
  } catch (error) {
26408
- import_node_server_utils63.logger.log({ level: "error", message: error.message });
26898
+ import_node_server_utils64.logger.log({ level: "error", message: error.message });
26409
26899
  next(error);
26410
26900
  return;
26411
26901
  }
@@ -26420,8 +26910,8 @@ function useAuthController() {
26420
26910
  });
26421
26911
  if (error) {
26422
26912
  const messages = error.details.map((d) => d.message);
26423
- import_node_server_utils63.logger.log({ level: "error", message: messages.join(", ") });
26424
- 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(", ")));
26425
26915
  return;
26426
26916
  }
26427
26917
  const email = value.email;
@@ -26436,7 +26926,7 @@ function useAuthController() {
26436
26926
  res.json({ message });
26437
26927
  return;
26438
26928
  } catch (error2) {
26439
- import_node_server_utils63.logger.log({ level: "error", message: error2.message });
26929
+ import_node_server_utils64.logger.log({ level: "error", message: error2.message });
26440
26930
  next(error2);
26441
26931
  return;
26442
26932
  }
@@ -26450,8 +26940,8 @@ function useAuthController() {
26450
26940
  const payload = { ...req.body };
26451
26941
  const { error } = validation.validate(payload);
26452
26942
  if (error) {
26453
- import_node_server_utils63.logger.log({ level: "error", message: error.message });
26454
- 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));
26455
26945
  return;
26456
26946
  }
26457
26947
  const otp = req.body.otp;
@@ -26466,7 +26956,7 @@ function useAuthController() {
26466
26956
  res.json({ message });
26467
26957
  return;
26468
26958
  } catch (error2) {
26469
- import_node_server_utils63.logger.log({ level: "error", message: error2.message });
26959
+ import_node_server_utils64.logger.log({ level: "error", message: error2.message });
26470
26960
  next(error2);
26471
26961
  return;
26472
26962
  }
@@ -26481,8 +26971,8 @@ function useAuthController() {
26481
26971
  const { error } = validation.validate(payload);
26482
26972
  if (error) {
26483
26973
  const messages = error.details.map((d) => d.message).join(", ");
26484
- import_node_server_utils63.logger.log({ level: "error", message: messages });
26485
- 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));
26486
26976
  return;
26487
26977
  }
26488
26978
  const password = req.body.password;
@@ -26491,7 +26981,7 @@ function useAuthController() {
26491
26981
  res.json({ message });
26492
26982
  return;
26493
26983
  } catch (error2) {
26494
- import_node_server_utils63.logger.log({ level: "error", message: error2.message });
26984
+ import_node_server_utils64.logger.log({ level: "error", message: error2.message });
26495
26985
  next(error2);
26496
26986
  return;
26497
26987
  }
@@ -26508,10 +26998,10 @@ function useAuthController() {
26508
26998
 
26509
26999
  // src/controllers/user.controller.ts
26510
27000
  var import_joi27 = __toESM(require("joi"));
26511
- var import_node_server_utils65 = require("@7365admin1/node-server-utils");
27001
+ var import_node_server_utils66 = require("@7365admin1/node-server-utils");
26512
27002
 
26513
27003
  // src/repositories/person.repo.ts
26514
- var import_node_server_utils64 = require("@7365admin1/node-server-utils");
27004
+ var import_node_server_utils65 = require("@7365admin1/node-server-utils");
26515
27005
  var import_mongodb58 = require("mongodb");
26516
27006
 
26517
27007
  // src/utils/sanitizer.ts
@@ -26525,13 +27015,13 @@ var makeContainsRegex = (input) => {
26525
27015
  // src/repositories/person.repo.ts
26526
27016
  var site_people_namespace_collection = "site.people";
26527
27017
  function usePersonRepo() {
26528
- const db2 = import_node_server_utils64.useAtlas.getDb();
27018
+ const db2 = import_node_server_utils65.useAtlas.getDb();
26529
27019
  if (!db2) {
26530
- 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.");
26531
27021
  }
26532
27022
  const collection = db2.collection(site_people_namespace_collection);
26533
27023
  const vehiclesCollection = db2.collection("vehicles");
26534
- const { delNamespace, getCache, setCache } = (0, import_node_server_utils64.useCache)(
27024
+ const { delNamespace, getCache, setCache } = (0, import_node_server_utils65.useCache)(
26535
27025
  site_people_namespace_collection
26536
27026
  );
26537
27027
  async function withoutDeletedPlates(people) {
@@ -26580,7 +27070,7 @@ function usePersonRepo() {
26580
27070
  { key: { unit: 1, status: 1 } }
26581
27071
  ]);
26582
27072
  } catch (error) {
26583
- 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.");
26584
27074
  }
26585
27075
  }
26586
27076
  async function createTextIndex() {
@@ -26593,7 +27083,7 @@ function usePersonRepo() {
26593
27083
  contact: "text"
26594
27084
  });
26595
27085
  } catch (error) {
26596
- throw new import_node_server_utils64.InternalServerError(
27086
+ throw new import_node_server_utils65.InternalServerError(
26597
27087
  "Failed to create text index on site people."
26598
27088
  );
26599
27089
  }
@@ -26603,11 +27093,11 @@ function usePersonRepo() {
26603
27093
  value = MPerson(value);
26604
27094
  const res = await collection.insertOne(value, { session });
26605
27095
  delNamespace().then(() => {
26606
- import_node_server_utils64.logger.info(
27096
+ import_node_server_utils65.logger.info(
26607
27097
  `Cache cleared for namespace: ${site_people_namespace_collection}`
26608
27098
  );
26609
27099
  }).catch((err) => {
26610
- import_node_server_utils64.logger.error(
27100
+ import_node_server_utils65.logger.error(
26611
27101
  `Failed to clear cache for namespace: ${site_people_namespace_collection}`,
26612
27102
  err
26613
27103
  );
@@ -26616,7 +27106,7 @@ function usePersonRepo() {
26616
27106
  } catch (error) {
26617
27107
  const isDuplicated = error.message.includes("duplicate");
26618
27108
  if (isDuplicated) {
26619
- throw new import_node_server_utils64.BadRequestError("Person already exists.");
27109
+ throw new import_node_server_utils65.BadRequestError("Person already exists.");
26620
27110
  }
26621
27111
  throw error;
26622
27112
  }
@@ -26744,7 +27234,7 @@ function usePersonRepo() {
26744
27234
  collection.aggregate([{ $match: query2 }, { $count: "total" }], { session }).toArray()
26745
27235
  ]);
26746
27236
  const totalCount = countResult[0]?.total || 0;
26747
- const data = (0, import_node_server_utils64.paginate)(
27237
+ const data = (0, import_node_server_utils65.paginate)(
26748
27238
  await withoutDeletedPlates(items),
26749
27239
  page2,
26750
27240
  limit,
@@ -26759,7 +27249,7 @@ function usePersonRepo() {
26759
27249
  try {
26760
27250
  _id = new import_mongodb58.ObjectId(_id);
26761
27251
  } catch (error) {
26762
- 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.");
26763
27253
  }
26764
27254
  value.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
26765
27255
  if (value.updatedBy) {
@@ -26775,11 +27265,11 @@ function usePersonRepo() {
26775
27265
  { session }
26776
27266
  );
26777
27267
  delNamespace().then(() => {
26778
- import_node_server_utils64.logger.info(
27268
+ import_node_server_utils65.logger.info(
26779
27269
  `Cache cleared for namespace: ${site_people_namespace_collection}`
26780
27270
  );
26781
27271
  }).catch((err) => {
26782
- import_node_server_utils64.logger.error(
27272
+ import_node_server_utils65.logger.error(
26783
27273
  `Failed to clear cache for namespace: ${site_people_namespace_collection}`,
26784
27274
  err
26785
27275
  );
@@ -26793,7 +27283,7 @@ function usePersonRepo() {
26793
27283
  try {
26794
27284
  _id = new import_mongodb58.ObjectId(_id);
26795
27285
  } catch (error) {
26796
- throw new import_node_server_utils64.BadRequestError("Invalid customer ID format.");
27286
+ throw new import_node_server_utils65.BadRequestError("Invalid customer ID format.");
26797
27287
  }
26798
27288
  try {
26799
27289
  const updateValue = {
@@ -26803,14 +27293,14 @@ function usePersonRepo() {
26803
27293
  };
26804
27294
  const res = await collection.updateOne({ _id }, { $set: updateValue });
26805
27295
  if (res.modifiedCount === 0) {
26806
- 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.");
26807
27297
  }
26808
27298
  delNamespace().then(() => {
26809
- import_node_server_utils64.logger.info(
27299
+ import_node_server_utils65.logger.info(
26810
27300
  `Cache cleared for namespace: ${site_people_namespace_collection}`
26811
27301
  );
26812
27302
  }).catch((err) => {
26813
- import_node_server_utils64.logger.error(
27303
+ import_node_server_utils65.logger.error(
26814
27304
  `Failed to clear cache for namespace: ${site_people_namespace_collection}`,
26815
27305
  err
26816
27306
  );
@@ -26824,15 +27314,15 @@ function usePersonRepo() {
26824
27314
  try {
26825
27315
  _id = new import_mongodb58.ObjectId(_id);
26826
27316
  } catch (error) {
26827
- throw new import_node_server_utils64.BadRequestError("Invalid person ID.");
27317
+ throw new import_node_server_utils65.BadRequestError("Invalid person ID.");
26828
27318
  }
26829
- 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, {
26830
27320
  _id: String(_id)
26831
27321
  });
26832
27322
  try {
26833
27323
  const cached4 = await getCache(cacheKey);
26834
27324
  if (cached4) {
26835
- import_node_server_utils64.logger.log({
27325
+ import_node_server_utils65.logger.log({
26836
27326
  level: "info",
26837
27327
  message: `Cache hit for getById person: ${cacheKey}`
26838
27328
  });
@@ -26842,34 +27332,34 @@ function usePersonRepo() {
26842
27332
  _id
26843
27333
  });
26844
27334
  setCache(cacheKey, result, 300).then(() => {
26845
- import_node_server_utils64.logger.log({
27335
+ import_node_server_utils65.logger.log({
26846
27336
  level: "info",
26847
27337
  message: `Cache set for person by id: ${cacheKey}`
26848
27338
  });
26849
27339
  }).catch((err) => {
26850
- import_node_server_utils64.logger.log({
27340
+ import_node_server_utils65.logger.log({
26851
27341
  level: "error",
26852
27342
  message: `Failed to set cache for person by id: ${err.message}`
26853
27343
  });
26854
27344
  });
26855
27345
  return result;
26856
27346
  } catch (error) {
26857
- if (error instanceof import_node_server_utils64.AppError) {
27347
+ if (error instanceof import_node_server_utils65.AppError) {
26858
27348
  throw error;
26859
27349
  } else {
26860
- 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.");
26861
27351
  }
26862
27352
  }
26863
27353
  }
26864
27354
  async function getPersonByPlateNumber(plateNumber) {
26865
- 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, {
26866
27356
  plateNumber,
26867
27357
  key: "get-person-plate-number"
26868
27358
  });
26869
27359
  try {
26870
27360
  const cacheData = await getCache(cacheKey);
26871
27361
  if (cacheData) {
26872
- import_node_server_utils64.logger.info(`Cache hit for key: ${cacheKey}`);
27362
+ import_node_server_utils65.logger.info(`Cache hit for key: ${cacheKey}`);
26873
27363
  return await withoutDeletedPlatesOne(cacheData);
26874
27364
  }
26875
27365
  const data = await collection.findOne(
@@ -26877,13 +27367,13 @@ function usePersonRepo() {
26877
27367
  { sort: { _id: -1 } }
26878
27368
  );
26879
27369
  setCache(cacheKey, data, 15 * 60).then(() => {
26880
- import_node_server_utils64.logger.info(`Cache set for key: ${cacheKey}`);
27370
+ import_node_server_utils65.logger.info(`Cache set for key: ${cacheKey}`);
26881
27371
  }).catch((err) => {
26882
- 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);
26883
27373
  });
26884
27374
  return await withoutDeletedPlatesOne(data);
26885
27375
  } catch (error) {
26886
- throw new import_node_server_utils64.InternalServerError(
27376
+ throw new import_node_server_utils65.InternalServerError(
26887
27377
  "Failed to fetch individual by plate number."
26888
27378
  );
26889
27379
  }
@@ -26895,11 +27385,11 @@ function usePersonRepo() {
26895
27385
  status: "approved"
26896
27386
  };
26897
27387
  if (excludePersonId) {
26898
- query2._id = { $ne: (0, import_node_server_utils64.toObjectId)(excludePersonId) };
27388
+ query2._id = { $ne: (0, import_node_server_utils65.toObjectId)(excludePersonId) };
26899
27389
  }
26900
27390
  return await collection.findOne(query2);
26901
27391
  } catch (error) {
26902
- throw new import_node_server_utils64.InternalServerError(
27392
+ throw new import_node_server_utils65.InternalServerError(
26903
27393
  "Failed to fetch approved person by plate number."
26904
27394
  );
26905
27395
  }
@@ -26907,47 +27397,47 @@ function usePersonRepo() {
26907
27397
  async function getByNRIC(value) {
26908
27398
  try {
26909
27399
  if (!value || value.trim() === "") {
26910
- 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.");
26911
27401
  return null;
26912
27402
  }
26913
- 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, {
26914
27404
  nric: value
26915
27405
  });
26916
27406
  const cachedData = await getCache(cacheKey);
26917
27407
  if (cachedData) {
26918
- import_node_server_utils64.logger.info(`Cache hit for key: ${cacheKey}`);
27408
+ import_node_server_utils65.logger.info(`Cache hit for key: ${cacheKey}`);
26919
27409
  return await withoutDeletedPlatesOne(cachedData);
26920
27410
  }
26921
27411
  const data = await collection.findOne({ nric: value });
26922
27412
  setCache(cacheKey, data, 15 * 60).then(() => {
26923
- import_node_server_utils64.logger.info(`Cache set for key: ${cacheKey}`);
27413
+ import_node_server_utils65.logger.info(`Cache set for key: ${cacheKey}`);
26924
27414
  }).catch((err) => {
26925
- 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);
26926
27416
  });
26927
27417
  return await withoutDeletedPlatesOne(data);
26928
27418
  } catch (error) {
26929
- 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.");
26930
27420
  }
26931
27421
  }
26932
27422
  async function getPersonByPhoneNumber(value) {
26933
27423
  try {
26934
- 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, {
26935
27425
  contact: value
26936
27426
  });
26937
27427
  const cachedData = await getCache(cacheKey);
26938
27428
  if (cachedData) {
26939
- import_node_server_utils64.logger.info(`Cache hit for key: ${cacheKey}`);
27429
+ import_node_server_utils65.logger.info(`Cache hit for key: ${cacheKey}`);
26940
27430
  return await withoutDeletedPlatesOne(cachedData);
26941
27431
  }
26942
27432
  const data = await collection.findOne({ contact: value });
26943
27433
  setCache(cacheKey, data, 15 * 60).then(() => {
26944
- import_node_server_utils64.logger.info(`Cache set for key: ${cacheKey}`);
27434
+ import_node_server_utils65.logger.info(`Cache set for key: ${cacheKey}`);
26945
27435
  }).catch((err) => {
26946
- 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);
26947
27437
  });
26948
27438
  return await withoutDeletedPlatesOne(data);
26949
27439
  } catch (error) {
26950
- throw new import_node_server_utils64.InternalServerError(
27440
+ throw new import_node_server_utils65.InternalServerError(
26951
27441
  "Failed to retrieve person by Phone Number."
26952
27442
  );
26953
27443
  }
@@ -26959,7 +27449,7 @@ function usePersonRepo() {
26959
27449
  }, session) {
26960
27450
  try {
26961
27451
  if (!unit || !import_mongodb58.ObjectId.isValid(unit)) {
26962
- throw new import_node_server_utils64.BadRequestError("Invalid unit ID.");
27452
+ throw new import_node_server_utils65.BadRequestError("Invalid unit ID.");
26963
27453
  }
26964
27454
  const query2 = {
26965
27455
  unit: new import_mongodb58.ObjectId(unit),
@@ -27004,14 +27494,14 @@ function usePersonRepo() {
27004
27494
  ]).toArray();
27005
27495
  return await withoutDeletedPlates(data);
27006
27496
  } catch (error) {
27007
- throw new import_node_server_utils64.InternalServerError(
27497
+ throw new import_node_server_utils65.InternalServerError(
27008
27498
  "Failed to fetch people type residents by unit."
27009
27499
  );
27010
27500
  }
27011
27501
  }
27012
27502
  async function getCompany(search, orgs) {
27013
27503
  try {
27014
- 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, {
27015
27505
  company: search
27016
27506
  });
27017
27507
  const baseQuery = {
@@ -27081,18 +27571,18 @@ function usePersonRepo() {
27081
27571
  ]).toArray();
27082
27572
  return data;
27083
27573
  } catch (error) {
27084
- 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.");
27085
27575
  }
27086
27576
  }
27087
27577
  async function getPeopleByPlateNumber(plateNumber) {
27088
- 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, {
27089
27579
  plateNumber,
27090
27580
  key: "get-people-plate-number"
27091
27581
  });
27092
27582
  try {
27093
27583
  const cacheData = await getCache(cacheKey);
27094
27584
  if (cacheData) {
27095
- import_node_server_utils64.logger.info(`Cache hit for key: ${cacheKey}`);
27585
+ import_node_server_utils65.logger.info(`Cache hit for key: ${cacheKey}`);
27096
27586
  return await withoutDeletedPlates(cacheData);
27097
27587
  }
27098
27588
  const data = await collection.find(
@@ -27100,13 +27590,13 @@ function usePersonRepo() {
27100
27590
  { sort: { _id: -1 } }
27101
27591
  ).toArray();
27102
27592
  setCache(cacheKey, data, 15 * 60).then(() => {
27103
- import_node_server_utils64.logger.info(`Cache set for key: ${cacheKey}`);
27593
+ import_node_server_utils65.logger.info(`Cache set for key: ${cacheKey}`);
27104
27594
  }).catch((err) => {
27105
- 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);
27106
27596
  });
27107
27597
  return await withoutDeletedPlates(data);
27108
27598
  } catch (error) {
27109
- 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.");
27110
27600
  }
27111
27601
  }
27112
27602
  async function getPeopleByNRIC({
@@ -27131,13 +27621,13 @@ function usePersonRepo() {
27131
27621
  sort: JSON.stringify(sort)
27132
27622
  };
27133
27623
  try {
27134
- const cacheKey = (0, import_node_server_utils64.makeCacheKey)(
27624
+ const cacheKey = (0, import_node_server_utils65.makeCacheKey)(
27135
27625
  site_people_namespace_collection,
27136
27626
  cacheOptions
27137
27627
  );
27138
27628
  const cachedData = await getCache(cacheKey);
27139
27629
  if (cachedData) {
27140
- import_node_server_utils64.logger.info(`Cache hit for key: ${cacheKey}`);
27630
+ import_node_server_utils65.logger.info(`Cache hit for key: ${cacheKey}`);
27141
27631
  const cachedPage = cachedData;
27142
27632
  return {
27143
27633
  ...cachedPage,
@@ -27146,26 +27636,26 @@ function usePersonRepo() {
27146
27636
  }
27147
27637
  const items = await collection.find(query2).sort(sort).skip(page2 * limit).limit(limit).toArray();
27148
27638
  const length = await collection.countDocuments(query2);
27149
- const data = (0, import_node_server_utils64.paginate)(
27639
+ const data = (0, import_node_server_utils65.paginate)(
27150
27640
  await withoutDeletedPlates(items),
27151
27641
  page2,
27152
27642
  limit,
27153
27643
  length
27154
27644
  );
27155
27645
  setCache(cacheKey, data, 15 * 60).then(() => {
27156
- import_node_server_utils64.logger.info(`Cache set for key: ${cacheKey}`);
27646
+ import_node_server_utils65.logger.info(`Cache set for key: ${cacheKey}`);
27157
27647
  }).catch((err) => {
27158
- 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);
27159
27649
  });
27160
27650
  return data;
27161
27651
  } catch (error) {
27162
- 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.");
27163
27653
  }
27164
27654
  }
27165
27655
  async function pushVehicleById(id, plate, session) {
27166
27656
  const { plateNumber, recNo } = plate;
27167
27657
  const updatedAt2 = (/* @__PURE__ */ new Date()).toISOString();
27168
- const _id = (0, import_node_server_utils64.toObjectId)(id);
27658
+ const _id = (0, import_node_server_utils65.toObjectId)(id);
27169
27659
  try {
27170
27660
  const updateExisting = await collection.updateOne(
27171
27661
  {
@@ -27198,15 +27688,15 @@ function usePersonRepo() {
27198
27688
  );
27199
27689
  }
27200
27690
  } catch (error) {
27201
- 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.");
27202
27692
  }
27203
27693
  }
27204
27694
  async function pullVehicleByRecNo(recNo, scope, session) {
27205
27695
  const updatedAt2 = (/* @__PURE__ */ new Date()).toISOString();
27206
- const owner = scope?.peopleId ? (0, import_node_server_utils64.toObjectId)(scope.peopleId) : null;
27207
- 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;
27208
27698
  if (!owner && !site) {
27209
- throw new import_node_server_utils64.BadRequestError(
27699
+ throw new import_node_server_utils65.BadRequestError(
27210
27700
  "Cannot remove a vehicle plate without a person or a site."
27211
27701
  );
27212
27702
  }
@@ -27247,24 +27737,24 @@ function usePersonRepo() {
27247
27737
  { session }
27248
27738
  );
27249
27739
  } catch (error) {
27250
- import_node_server_utils64.logger.log({
27740
+ import_node_server_utils65.logger.log({
27251
27741
  level: "error",
27252
27742
  message: `pullVehicleByRecNo failed on ${matchKey} for ${owner ? "person" : "site"} ${String(owner ?? site)}: ${error?.message ?? error}`
27253
27743
  });
27254
- throw new import_node_server_utils64.InternalServerError(
27744
+ throw new import_node_server_utils65.InternalServerError(
27255
27745
  "Could not take the plate off the resident's record."
27256
27746
  );
27257
27747
  }
27258
27748
  }
27259
27749
  async function getByUserId(userId) {
27260
- const user = (0, import_node_server_utils64.toObjectId)(userId);
27261
- 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, {
27262
27752
  user: userId
27263
27753
  });
27264
27754
  try {
27265
27755
  const cached4 = await getCache(cacheKey);
27266
27756
  if (cached4) {
27267
- import_node_server_utils64.logger.log({
27757
+ import_node_server_utils65.logger.log({
27268
27758
  level: "info",
27269
27759
  message: `Cache hit for getByUserId person: ${cacheKey}`
27270
27760
  });
@@ -27274,22 +27764,22 @@ function usePersonRepo() {
27274
27764
  user
27275
27765
  });
27276
27766
  setCache(cacheKey, result, 15 * 60).then(() => {
27277
- import_node_server_utils64.logger.log({
27767
+ import_node_server_utils65.logger.log({
27278
27768
  level: "info",
27279
27769
  message: `Cache set for person by user: ${cacheKey}`
27280
27770
  });
27281
27771
  }).catch((err) => {
27282
- import_node_server_utils64.logger.log({
27772
+ import_node_server_utils65.logger.log({
27283
27773
  level: "error",
27284
27774
  message: `Failed to set cache for person by user: ${err.message}`
27285
27775
  });
27286
27776
  });
27287
27777
  return result;
27288
27778
  } catch (error) {
27289
- if (error instanceof import_node_server_utils64.AppError) {
27779
+ if (error instanceof import_node_server_utils65.AppError) {
27290
27780
  throw error;
27291
27781
  } else {
27292
- 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.");
27293
27783
  }
27294
27784
  }
27295
27785
  }
@@ -27298,7 +27788,7 @@ function usePersonRepo() {
27298
27788
  try {
27299
27789
  _id = new import_mongodb58.ObjectId(_id);
27300
27790
  } catch (error) {
27301
- throw new import_node_server_utils64.BadRequestError("Invalid ID format.");
27791
+ throw new import_node_server_utils65.BadRequestError("Invalid ID format.");
27302
27792
  }
27303
27793
  try {
27304
27794
  const res = await collection.updateOne(
@@ -27307,14 +27797,14 @@ function usePersonRepo() {
27307
27797
  { session }
27308
27798
  );
27309
27799
  if (res.modifiedCount === 0) {
27310
- 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.`);
27311
27801
  }
27312
27802
  delNamespace().then(() => {
27313
- import_node_server_utils64.logger.info(
27803
+ import_node_server_utils65.logger.info(
27314
27804
  `Cache cleared for namespace: ${site_people_namespace_collection}`
27315
27805
  );
27316
27806
  }).catch((err) => {
27317
- import_node_server_utils64.logger.error(
27807
+ import_node_server_utils65.logger.error(
27318
27808
  `Failed to clear cache for namespace: ${site_people_namespace_collection}`,
27319
27809
  err
27320
27810
  );
@@ -27330,7 +27820,7 @@ function usePersonRepo() {
27330
27820
  const data = await collection.find(query2, { session }).limit(10).toArray();
27331
27821
  return data;
27332
27822
  } catch (error) {
27333
- 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);
27334
27824
  }
27335
27825
  }
27336
27826
  async function addFromVisitor(value, session) {
@@ -27365,7 +27855,7 @@ function usePersonRepo() {
27365
27855
  _id = new import_mongodb58.ObjectId(personId);
27366
27856
  acceptedTerms = new import_mongodb58.ObjectId(termsId);
27367
27857
  } catch {
27368
- throw new import_node_server_utils64.BadRequestError("Invalid ID format.");
27858
+ throw new import_node_server_utils65.BadRequestError("Invalid ID format.");
27369
27859
  }
27370
27860
  const now = (/* @__PURE__ */ new Date()).toISOString();
27371
27861
  try {
@@ -27374,7 +27864,7 @@ function usePersonRepo() {
27374
27864
  { $set: { acceptedTerms, acceptedTermsAt: now, updatedAt: now } }
27375
27865
  );
27376
27866
  } catch (error) {
27377
- 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.");
27378
27868
  }
27379
27869
  }
27380
27870
  async function getAcceptedTerms(personId) {
@@ -27382,7 +27872,7 @@ function usePersonRepo() {
27382
27872
  try {
27383
27873
  _id = new import_mongodb58.ObjectId(personId);
27384
27874
  } catch {
27385
- throw new import_node_server_utils64.BadRequestError("Invalid person ID format.");
27875
+ throw new import_node_server_utils65.BadRequestError("Invalid person ID format.");
27386
27876
  }
27387
27877
  try {
27388
27878
  return await collection.findOne(
@@ -27390,11 +27880,11 @@ function usePersonRepo() {
27390
27880
  { projection: { acceptedTerms: 1, acceptedTermsAt: 1, _id: 0 } }
27391
27881
  );
27392
27882
  } catch (error) {
27393
- 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.");
27394
27884
  }
27395
27885
  }
27396
27886
  async function updatePlateNumberByUserId(userId, plateNumber, session) {
27397
- const user = (0, import_node_server_utils64.toObjectId)(userId);
27887
+ const user = (0, import_node_server_utils65.toObjectId)(userId);
27398
27888
  try {
27399
27889
  const result = await collection.updateOne(
27400
27890
  { user },
@@ -27408,25 +27898,25 @@ function usePersonRepo() {
27408
27898
  { session }
27409
27899
  );
27410
27900
  if (result.matchedCount === 0) {
27411
- 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.");
27412
27902
  }
27413
27903
  try {
27414
27904
  await delNamespace();
27415
- import_node_server_utils64.logger.info(
27905
+ import_node_server_utils65.logger.info(
27416
27906
  `Cache cleared for namespace: ${site_people_namespace_collection}`
27417
27907
  );
27418
27908
  } catch (err) {
27419
- import_node_server_utils64.logger.error(
27909
+ import_node_server_utils65.logger.error(
27420
27910
  `Failed to clear cache for namespace: ${site_people_namespace_collection}`,
27421
27911
  err
27422
27912
  );
27423
27913
  }
27424
27914
  return "Successfully updated plate number.";
27425
27915
  } catch (error) {
27426
- if (error instanceof import_node_server_utils64.BadRequestError) {
27916
+ if (error instanceof import_node_server_utils65.BadRequestError) {
27427
27917
  throw error;
27428
27918
  }
27429
- 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.");
27430
27920
  }
27431
27921
  }
27432
27922
  return {
@@ -27489,8 +27979,8 @@ function useUserController() {
27489
27979
  const _id = req.params.id;
27490
27980
  const { error } = validation.validate(_id);
27491
27981
  if (error) {
27492
- import_node_server_utils65.logger.log({ level: "error", message: `${error.message}` });
27493
- 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));
27494
27984
  return;
27495
27985
  }
27496
27986
  try {
@@ -27499,7 +27989,7 @@ function useUserController() {
27499
27989
  res.json(user);
27500
27990
  return;
27501
27991
  } catch (error2) {
27502
- import_node_server_utils65.logger.log({ level: "error", message: `${error2.message}` });
27992
+ import_node_server_utils66.logger.log({ level: "error", message: `${error2.message}` });
27503
27993
  next(error2);
27504
27994
  return;
27505
27995
  }
@@ -27514,8 +28004,8 @@ function useUserController() {
27514
28004
  const query2 = { ...req.query };
27515
28005
  const { error } = validation.validate(query2);
27516
28006
  if (error) {
27517
- import_node_server_utils65.logger.log({ level: "error", message: `${error.message}` });
27518
- 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));
27519
28009
  return;
27520
28010
  }
27521
28011
  const search = req.query.search ?? "";
@@ -27533,7 +28023,7 @@ function useUserController() {
27533
28023
  res.json(data);
27534
28024
  return;
27535
28025
  } catch (error2) {
27536
- import_node_server_utils65.logger.log({ level: "error", message: `${error2.message}` });
28026
+ import_node_server_utils66.logger.log({ level: "error", message: `${error2.message}` });
27537
28027
  next(error2);
27538
28028
  return;
27539
28029
  }
@@ -27543,8 +28033,8 @@ function useUserController() {
27543
28033
  const email = req.params.email;
27544
28034
  const { error } = validation.validate(email);
27545
28035
  if (error) {
27546
- import_node_server_utils65.logger.log({ level: "error", message: `${error.message}` });
27547
- 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));
27548
28038
  return;
27549
28039
  }
27550
28040
  try {
@@ -27557,7 +28047,7 @@ function useUserController() {
27557
28047
  res.json(publicIdentity(user));
27558
28048
  return;
27559
28049
  } catch (error2) {
27560
- import_node_server_utils65.logger.log({ level: "error", message: `${error2.message}` });
28050
+ import_node_server_utils66.logger.log({ level: "error", message: `${error2.message}` });
27561
28051
  next(error2);
27562
28052
  return;
27563
28053
  }
@@ -27571,8 +28061,8 @@ function useUserController() {
27571
28061
  const query2 = { ...req.query };
27572
28062
  const { error } = validation.validate(query2);
27573
28063
  if (error) {
27574
- import_node_server_utils65.logger.log({ level: "error", message: `${error.message}` });
27575
- 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));
27576
28066
  return;
27577
28067
  }
27578
28068
  const search = req.query.search ?? "";
@@ -27584,7 +28074,7 @@ function useUserController() {
27584
28074
  res.json(data);
27585
28075
  return;
27586
28076
  } catch (error2) {
27587
- import_node_server_utils65.logger.log({ level: "error", message: `${error2.message}` });
28077
+ import_node_server_utils66.logger.log({ level: "error", message: `${error2.message}` });
27588
28078
  next(error2);
27589
28079
  return;
27590
28080
  }
@@ -27601,8 +28091,8 @@ function useUserController() {
27601
28091
  const payload = { ...req.body };
27602
28092
  const { error } = validation.validate({ id, ...payload });
27603
28093
  if (error) {
27604
- import_node_server_utils65.logger.log({ level: "error", message: `${error.message}` });
27605
- 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));
27606
28096
  return;
27607
28097
  }
27608
28098
  try {
@@ -27610,14 +28100,14 @@ function useUserController() {
27610
28100
  res.status(201).json({ message: "Successfully created account." });
27611
28101
  return;
27612
28102
  } catch (error2) {
27613
- import_node_server_utils65.logger.log({ level: "error", message: `${error2.message}` });
28103
+ import_node_server_utils66.logger.log({ level: "error", message: `${error2.message}` });
27614
28104
  next(error2);
27615
28105
  return;
27616
28106
  }
27617
28107
  }
27618
28108
  async function updateUserProfile(req, res, next) {
27619
28109
  if (!req.file) {
27620
- next(new import_node_server_utils65.BadRequestError("File is required!"));
28110
+ next(new import_node_server_utils66.BadRequestError("File is required!"));
27621
28111
  return;
27622
28112
  }
27623
28113
  const validation = import_joi27.default.object({
@@ -27626,8 +28116,8 @@ function useUserController() {
27626
28116
  const payload = { ...req.body };
27627
28117
  const { error } = validation.validate(payload);
27628
28118
  if (error) {
27629
- import_node_server_utils65.logger.log({ level: "error", message: `${error.message}` });
27630
- 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));
27631
28121
  return;
27632
28122
  }
27633
28123
  const user = req.headers.user ?? "";
@@ -27640,7 +28130,7 @@ function useUserController() {
27640
28130
  res.json({ message: "Successfully updated profile picture." });
27641
28131
  return;
27642
28132
  } catch (error2) {
27643
- import_node_server_utils65.logger.log({ level: "error", message: `${error2.message}` });
28133
+ import_node_server_utils66.logger.log({ level: "error", message: `${error2.message}` });
27644
28134
  next(error2);
27645
28135
  return;
27646
28136
  }
@@ -27669,8 +28159,8 @@ function useUserController() {
27669
28159
  const payload = { ...req.body };
27670
28160
  const { error } = validation.validate({ _id, ...payload });
27671
28161
  if (error) {
27672
- import_node_server_utils65.logger.log({ level: "error", message: `${error.message}` });
27673
- 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));
27674
28164
  return;
27675
28165
  }
27676
28166
  try {
@@ -27679,7 +28169,7 @@ function useUserController() {
27679
28169
  res.json({ message });
27680
28170
  return;
27681
28171
  } catch (error2) {
27682
- import_node_server_utils65.logger.log({ level: "error", message: `${error2.message}` });
28172
+ import_node_server_utils66.logger.log({ level: "error", message: `${error2.message}` });
27683
28173
  next(error2);
27684
28174
  return;
27685
28175
  }
@@ -27711,8 +28201,8 @@ function useUserController() {
27711
28201
  const payload = { ...req.body };
27712
28202
  const { error } = validation.validate({ _id, ...payload });
27713
28203
  if (error) {
27714
- import_node_server_utils65.logger.log({ level: "error", message: `${error.message}` });
27715
- 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));
27716
28206
  return;
27717
28207
  }
27718
28208
  try {
@@ -27732,7 +28222,7 @@ function useUserController() {
27732
28222
  res.json({ message });
27733
28223
  return;
27734
28224
  } catch (error2) {
27735
- import_node_server_utils65.logger.log({ level: "error", message: `${error2.message}` });
28225
+ import_node_server_utils66.logger.log({ level: "error", message: `${error2.message}` });
27736
28226
  next(error2);
27737
28227
  return;
27738
28228
  }
@@ -27751,7 +28241,7 @@ function useUserController() {
27751
28241
  ...payload
27752
28242
  });
27753
28243
  if (error) {
27754
- next(new import_node_server_utils65.BadRequestError(error.message));
28244
+ next(new import_node_server_utils66.BadRequestError(error.message));
27755
28245
  return;
27756
28246
  }
27757
28247
  const currentPassword = req.body.currentPassword ?? "";
@@ -27788,16 +28278,16 @@ var import_joi28 = __toESM(require("joi"));
27788
28278
  var import_node_server_utils71 = require("@7365admin1/node-server-utils");
27789
28279
 
27790
28280
  // src/repositories/org-role-seed.repo.ts
27791
- var import_node_server_utils66 = require("@7365admin1/node-server-utils");
28281
+ var import_node_server_utils67 = require("@7365admin1/node-server-utils");
27792
28282
  var import_mongodb59 = require("mongodb");
27793
28283
  var namespace_collection = "org-default-role-seeds";
27794
28284
  function toOrgId(org) {
27795
28285
  return typeof org === "string" ? new import_mongodb59.ObjectId(org) : org;
27796
28286
  }
27797
28287
  function useOrgRoleSeedRepo() {
27798
- const db2 = import_node_server_utils66.useAtlas.getDb();
28288
+ const db2 = import_node_server_utils67.useAtlas.getDb();
27799
28289
  if (!db2) {
27800
- 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.");
27801
28291
  }
27802
28292
  const collection = db2.collection(namespace_collection);
27803
28293
  const roles = db2.collection("roles");
@@ -27821,7 +28311,7 @@ function useOrgRoleSeedRepo() {
27821
28311
  }
27822
28312
 
27823
28313
  // src/services/role.service.ts
27824
- var import_node_server_utils67 = require("@7365admin1/node-server-utils");
28314
+ var import_node_server_utils68 = require("@7365admin1/node-server-utils");
27825
28315
  function useRoleService() {
27826
28316
  const {
27827
28317
  deleteRole: _deleteRole,
@@ -27833,7 +28323,7 @@ function useRoleService() {
27833
28323
  try {
27834
28324
  const count = await getByRoleId(_id);
27835
28325
  if (count > 0) {
27836
- throw new import_node_server_utils67.BadRequestError(
28326
+ throw new import_node_server_utils68.BadRequestError(
27837
28327
  "Cannot delete role that is assigned to members."
27838
28328
  );
27839
28329
  }
@@ -27845,16 +28335,16 @@ function useRoleService() {
27845
28335
  async function getDeletionPreview(_id) {
27846
28336
  const role = await getRoleById(_id);
27847
28337
  if (!role) {
27848
- throw new import_node_server_utils67.NotFoundError("Role not found.");
28338
+ throw new import_node_server_utils68.NotFoundError("Role not found.");
27849
28339
  }
27850
28340
  const members = await getAssignedByRoleId(_id);
27851
28341
  const replacementRoles = (await getActiveByTypeOrg(role.type ?? "", role.org ?? "")).filter((replacementRole) => replacementRole._id?.toString() !== _id);
27852
28342
  return { role, members, replacementRoles };
27853
28343
  }
27854
28344
  async function deleteWithReassignments(_id, reassignments) {
27855
- const session = import_node_server_utils67.useAtlas.getClient()?.startSession();
28345
+ const session = import_node_server_utils68.useAtlas.getClient()?.startSession();
27856
28346
  if (!session) {
27857
- 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.");
27858
28348
  }
27859
28349
  try {
27860
28350
  session.startTransaction();
@@ -27871,7 +28361,7 @@ function useRoleService() {
27871
28361
  ({ roleId }) => replacementRoleIds.has(roleId)
27872
28362
  );
27873
28363
  if (!hasExactMemberCoverage || !hasEligibleReplacementRoles) {
27874
- throw new import_node_server_utils67.BadRequestError("Invalid role reassignment request.");
28364
+ throw new import_node_server_utils68.BadRequestError("Invalid role reassignment request.");
27875
28365
  }
27876
28366
  await reassignRoles(reassignments, session);
27877
28367
  await _deleteRole(_id, session);
@@ -27888,7 +28378,7 @@ function useRoleService() {
27888
28378
  }
27889
28379
 
27890
28380
  // src/utils/role-scope.util.ts
27891
- var import_node_server_utils68 = require("@7365admin1/node-server-utils");
28381
+ var import_node_server_utils69 = require("@7365admin1/node-server-utils");
27892
28382
  function roleScope(role) {
27893
28383
  if ((role?.type ?? "") === PLATFORM_STAFF_ROLE_TYPE)
27894
28384
  return "platform";
@@ -27922,7 +28412,7 @@ function requireNoPlatformGrant(role, next) {
27922
28412
  const added = platformPermissionsAdded(next, role?.permissions);
27923
28413
  if (added.length === 0)
27924
28414
  return;
27925
- throw new import_node_server_utils68.AppError(
28415
+ throw new import_node_server_utils69.AppError(
27926
28416
  "These are Seven365 console permissions and cannot be granted on a client role: " + added.join(", "),
27927
28417
  403
27928
28418
  );
@@ -27937,7 +28427,7 @@ function refuseSharedClientTemplate(role, next) {
27937
28427
  return true;
27938
28428
  if (Array.isArray(next) && restorableFromHistory(role, next))
27939
28429
  return true;
27940
- throw new import_node_server_utils68.AppError(
28430
+ throw new import_node_server_utils69.AppError(
27941
28431
  "This role is shared by every client that was invited with it and cannot be changed here.",
27942
28432
  403
27943
28433
  );
@@ -27945,7 +28435,7 @@ function refuseSharedClientTemplate(role, next) {
27945
28435
  function refuseNewSharedClientTemplate(role) {
27946
28436
  if (roleScope(role) !== "client-template")
27947
28437
  return;
27948
- throw new import_node_server_utils68.AppError(
28438
+ throw new import_node_server_utils69.AppError(
27949
28439
  "A role with no organisation would be shared by every client and cannot be created. Choose the organisation this role belongs to.",
27950
28440
  403
27951
28441
  );
@@ -27983,113 +28473,6 @@ function recordablePriorPermissions(role) {
27983
28473
  return [...held];
27984
28474
  }
27985
28475
 
27986
- // src/utils/org-default-roles.util.ts
27987
- var import_node_server_utils69 = require("@7365admin1/node-server-utils");
27988
- var APP_SERVICE_TYPES = new Set(
27989
- Object.values(AppServiceType)
27990
- );
27991
- var ADMINISTRATOR_PERMISSIONS = Object.freeze([
27992
- "members:view-members",
27993
- "members:assign-member-role",
27994
- "members:suspend-member",
27995
- "members:activate-member",
27996
- "members:delete-member",
27997
- "invitations:create-invitation",
27998
- "invitations:view-invitations",
27999
- "invitations:cancel-invitation",
28000
- "roles:see-all-roles",
28001
- "roles:see-role-details",
28002
- "roles:add-role",
28003
- "roles:update-role",
28004
- "roles:delete-role"
28005
- ]);
28006
- function defaultOwnerRoles(org, nature, appOwnerPermissions) {
28007
- if (!org?.toString())
28008
- return [];
28009
- const owner = {
28010
- org,
28011
- name: "owner",
28012
- permissions: ["*"],
28013
- default: true
28014
- };
28015
- const administrator = {
28016
- org,
28017
- name: "Administrator",
28018
- permissions: [...ADMINISTRATOR_PERMISSIONS],
28019
- default: true
28020
- };
28021
- const roles = [
28022
- { ...owner, type: "organization" },
28023
- { ...administrator, type: "organization" }
28024
- ];
28025
- const appType = serviceAppType(nature);
28026
- if (appType && APP_SERVICE_TYPES.has(appType)) {
28027
- roles.push({
28028
- ...owner,
28029
- type: appType,
28030
- ...appOwnerPermissions?.length && { permissions: [...appOwnerPermissions] }
28031
- });
28032
- roles.push({ ...administrator, type: appType });
28033
- }
28034
- return roles;
28035
- }
28036
- async function seedDefaultOrgRoles(addRole, org, nature, appOwnerPermissions) {
28037
- const seeded = [];
28038
- try {
28039
- for (const role of defaultOwnerRoles(org, nature, appOwnerPermissions)) {
28040
- const id = await addRole(role);
28041
- if (id)
28042
- seeded.push({ type: String(role.type), id: String(id), name: role.name });
28043
- }
28044
- } catch (error) {
28045
- import_node_server_utils69.logger.log({
28046
- level: "error",
28047
- message: `Failed to seed default roles for organization ${org}: ${error?.message}`
28048
- });
28049
- }
28050
- return seeded;
28051
- }
28052
- async function healOrgDefaultRoles(deps, org) {
28053
- if (!org?.toString())
28054
- return 0;
28055
- let claimed = false;
28056
- try {
28057
- claimed = await deps.claim(org);
28058
- if (!claimed)
28059
- return 0;
28060
- const existing = await deps.countRoles(org);
28061
- if (existing > 0) {
28062
- import_node_server_utils69.logger.log({
28063
- level: "info",
28064
- message: `Default role self-heal skipped for organization ${org}: ${existing} role(s) already present`
28065
- });
28066
- return 0;
28067
- }
28068
- const roles = defaultOwnerRoles(org, await deps.getNature(org));
28069
- for (const role of roles) {
28070
- await deps.addRole(role);
28071
- }
28072
- import_node_server_utils69.logger.log({
28073
- level: "info",
28074
- message: `Seeded ${roles.length} default role(s) for existing organization ${org} on read`
28075
- });
28076
- return roles.length;
28077
- } catch (error) {
28078
- if (claimed) {
28079
- await deps.release(org).catch(() => void 0);
28080
- }
28081
- import_node_server_utils69.logger.log({
28082
- level: "error",
28083
- message: `Failed to self-heal default roles for organization ${org}: ${error?.message}`
28084
- });
28085
- return 0;
28086
- }
28087
- }
28088
- function clientInviteOwnerPermissionsOn(env2 = process.env) {
28089
- const value = String(env2.CLIENT_INVITE_OWNER_PERMISSIONS ?? "on").trim().toLowerCase();
28090
- return !["off", "false", "0", "no"].includes(value);
28091
- }
28092
-
28093
28476
  // src/utils/module-ceiling.util.ts
28094
28477
  var import_node_server_utils70 = require("@7365admin1/node-server-utils");
28095
28478
  async function moduleCeiling(org, site) {
@@ -64865,7 +65248,8 @@ function useRobotCloudService(credentials, options = {}) {
64865
65248
  async function getRobotLog(robotId, sceneId, timeFrom, timeTo) {
64866
65249
  return post(
64867
65250
  ROBOT_CLOUD_PATHS.robotLog,
64868
- { robotId: String(robotId), sceneId: String(sceneId), timeFrom, timeTo, key: "", pageNo: 1, pageSize: 50 },
65251
+ // Numbers: the document shows strings, but the portal refuses them ("bad json string", prod 2026-09-30).
65252
+ { robotId, sceneId, timeFrom, timeTo, key: "", pageNo: 1, pageSize: 50 },
64869
65253
  "logList"
64870
65254
  );
64871
65255
  }
@@ -65485,13 +65869,25 @@ var ACTION_WORDS = {
65485
65869
  \u56DE\u5145: "Sent back to charge",
65486
65870
  \u7ED3\u675F\u6E05\u6D01: "Ended the cleaning job",
65487
65871
  \u5F00\u673A: "Switched on",
65488
- \u5173\u673A: "Switched off"
65872
+ \u5173\u673A: "Switched off",
65873
+ \u8C03\u8BD5\u6A21\u5F0F: "Debug mode",
65874
+ \u6E05\u626B: "Started cleaning",
65875
+ \u5B9A\u65F6\u4EFB\u52A1\u8BBE\u7F6E: "Changed the schedule",
65876
+ \u6682\u505C\u6E05\u626B: "Paused cleaning",
65877
+ \u5F00\u59CB\u56DE\u5145: "Sent back to charge"
65489
65878
  };
65879
+ var ACTION_PREFIX_WORDS = [
65880
+ [/^编辑机器人保存成功[,,]\s*别名[::]\s*/, "Changed the robot's nickname"],
65881
+ [/^编辑机器人保存成功[,,]\s*名称[::]\s*/, "Changed the robot's name"],
65882
+ [/^设置当前楼层[::]\s*/, "Set the current floor"]
65883
+ ];
65490
65884
  function ounitechAppAction(a, tz = 480) {
65491
65885
  const raw = String(a.actionName ?? "").trim();
65886
+ const pre = ACTION_PREFIX_WORDS.find(([re]) => re.test(raw));
65887
+ const value = pre ? raw.replace(pre[0], "").trim() : "";
65492
65888
  return {
65493
65889
  at: ounitechTime(a.createTime, tz),
65494
- action: ACTION_WORDS[raw] ?? (raw ? /[㐀-鿿]/.test(raw) ? `App action: ${raw}` : raw : "App action"),
65890
+ action: pre ? `${pre[1]}${value ? `: ${value}` : ""}` : ACTION_WORDS[raw] ?? (raw ? /[㐀-鿿]/.test(raw) ? `App action: ${raw}` : raw : "App action"),
65495
65891
  by: String(a.userName ?? "").trim() || null
65496
65892
  };
65497
65893
  }
@@ -100871,6 +101267,10 @@ function useServiceProviderInviteController() {
100871
101267
  softDelete: _softDelete,
100872
101268
  decline: _decline,
100873
101269
  accept: _accept,
101270
+ acceptAsNewCompany: _acceptAsNewCompany,
101271
+ wrongJoinPreview: _wrongJoinPreview,
101272
+ repairWrongJoin: _repairWrongJoin,
101273
+ undoWrongJoin: _undoWrongJoin,
100874
101274
  linkSite: _linkSite,
100875
101275
  listReceived: _listReceived,
100876
101276
  getForProvider: _getForProvider
@@ -101042,6 +101442,42 @@ function useServiceProviderInviteController() {
101042
101442
  next(error);
101043
101443
  }
101044
101444
  }
101445
+ async function acceptNewCompany(req, res, next) {
101446
+ try {
101447
+ const { error, value } = import_joi149.default.object({
101448
+ companyName: import_joi149.default.string().trim().max(200).optional().allow("", null),
101449
+ country: import_joi149.default.string().trim().max(100).optional().allow("", null)
101450
+ }).validate(req.body ?? {}, { stripUnknown: true });
101451
+ if (error)
101452
+ throw new import_node_server_utils293.BadRequestError(`Invalid input: ${error.message}`);
101453
+ res.json(
101454
+ await _acceptAsNewCompany({
101455
+ id: inviteId(req),
101456
+ userId: callerId5(req),
101457
+ companyName: value.companyName ?? "",
101458
+ country: value.country ?? ""
101459
+ })
101460
+ );
101461
+ } catch (error) {
101462
+ next(error);
101463
+ }
101464
+ }
101465
+ function wrongJoin(action) {
101466
+ return async (req, res, next) => {
101467
+ try {
101468
+ await requireConsolePermission(req, "sp-approvals", "approve-sp");
101469
+ const args = { id: inviteId(req), userId: callerId5(req) };
101470
+ res.json(
101471
+ action === "preview" ? await _wrongJoinPreview(args) : action === "repair" ? await _repairWrongJoin(args) : await _undoWrongJoin(args)
101472
+ );
101473
+ } catch (error) {
101474
+ next(error);
101475
+ }
101476
+ };
101477
+ }
101478
+ const wrongJoinPreview = wrongJoin("preview");
101479
+ const wrongJoinRepair = wrongJoin("repair");
101480
+ const wrongJoinUndo = wrongJoin("undo");
101045
101481
  async function linkSite(req, res, next) {
101046
101482
  try {
101047
101483
  res.json(
@@ -101092,6 +101528,10 @@ function useServiceProviderInviteController() {
101092
101528
  remove,
101093
101529
  decline,
101094
101530
  accept,
101531
+ acceptNewCompany,
101532
+ wrongJoinPreview,
101533
+ wrongJoinRepair,
101534
+ wrongJoinUndo,
101095
101535
  linkSite,
101096
101536
  received,
101097
101537
  getOne
@@ -107665,6 +108105,8 @@ function usePersonalEmergencyChainController() {
107665
108105
  HID_PERMISSION_CATEGORIES,
107666
108106
  HID_UINT32_MAX,
107667
108107
  HID_UINT64_MAX,
108108
+ INVITE_EMAIL_EVENTS,
108109
+ INVITE_RESEND_MIN_MINUTES,
107668
108110
  LIFT_DOOR,
107669
108111
  LIFT_LEASE_MS,
107670
108112
  LIFT_MODE,
@@ -108073,6 +108515,9 @@ function usePersonalEmergencyChainController() {
108073
108515
  incidentReportLog,
108074
108516
  incidents_namespace_collection,
108075
108517
  invitationOffersRole,
108518
+ inviteEmailFailureReason,
108519
+ inviteEmailStatus,
108520
+ inviteResendWaitMinutes,
108076
108521
  isAvailableForWork,
108077
108522
  isCameraEntitled,
108078
108523
  isCrossSiteRole,
@@ -108461,6 +108906,7 @@ function usePersonalEmergencyChainController() {
108461
108906
  serviceProviderInviteScope,
108462
108907
  serviceProviderInviteViewStatus,
108463
108908
  serviceProviderInviterKind,
108909
+ serviceProviderNatureFor,
108464
108910
  serviceTypeLabel,
108465
108911
  sessionSchema,
108466
108912
  setIO,