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