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