@7365admin1/core 3.64.3-staging.284 → 3.64.3-staging.285
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +0 -1
- package/dist/index.js +127 -74
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +127 -74
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
- package/test/resident-login-account.test.mjs +126 -0
package/dist/index.d.ts
CHANGED
|
@@ -4851,7 +4851,6 @@ declare function MPerson(value: TPerson): {
|
|
|
4851
4851
|
end: string | Date | undefined;
|
|
4852
4852
|
type: "resident" | "walk-in" | "drop-off" | "contractor" | "delivery" | "pick-up" | "guest" | "tenant" | undefined;
|
|
4853
4853
|
email: string | undefined;
|
|
4854
|
-
password: string;
|
|
4855
4854
|
status: string;
|
|
4856
4855
|
nric: string | undefined;
|
|
4857
4856
|
remarks: string | undefined;
|
package/dist/index.js
CHANGED
|
@@ -9035,6 +9035,14 @@ var MVerificationV2 = class {
|
|
|
9035
9035
|
}
|
|
9036
9036
|
};
|
|
9037
9037
|
|
|
9038
|
+
// src/utils/email-match.util.ts
|
|
9039
|
+
function escapeRegex(value) {
|
|
9040
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
9041
|
+
}
|
|
9042
|
+
function emailFilter(email) {
|
|
9043
|
+
return { $regex: `^${escapeRegex(email)}$`, $options: "i" };
|
|
9044
|
+
}
|
|
9045
|
+
|
|
9038
9046
|
// src/repositories/user.repo.ts
|
|
9039
9047
|
function useUserRepo() {
|
|
9040
9048
|
const { updateFeedbackCreatedByName } = useFeedbackRepo();
|
|
@@ -9099,7 +9107,7 @@ function useUserRepo() {
|
|
|
9099
9107
|
async function getUserByEmail(email) {
|
|
9100
9108
|
try {
|
|
9101
9109
|
const data = await collection.findOne({
|
|
9102
|
-
email:
|
|
9110
|
+
email: emailFilter(email)
|
|
9103
9111
|
});
|
|
9104
9112
|
return data;
|
|
9105
9113
|
} catch (error) {
|
|
@@ -9109,7 +9117,7 @@ function useUserRepo() {
|
|
|
9109
9117
|
async function userExistsByEmail(email) {
|
|
9110
9118
|
try {
|
|
9111
9119
|
const data = await collection.findOne(
|
|
9112
|
-
{ email:
|
|
9120
|
+
{ email: emailFilter(email) },
|
|
9113
9121
|
{ projection: { _id: 1 } }
|
|
9114
9122
|
);
|
|
9115
9123
|
return Boolean(data);
|
|
@@ -9130,7 +9138,7 @@ function useUserRepo() {
|
|
|
9130
9138
|
return cachedData;
|
|
9131
9139
|
}
|
|
9132
9140
|
const query2 = {
|
|
9133
|
-
email:
|
|
9141
|
+
email: emailFilter(email),
|
|
9134
9142
|
status: "complete" /* COMPLETE */
|
|
9135
9143
|
};
|
|
9136
9144
|
const data = await collection.findOne(query2);
|
|
@@ -18301,7 +18309,12 @@ function MPerson(value) {
|
|
|
18301
18309
|
end: value.end,
|
|
18302
18310
|
type: value.type,
|
|
18303
18311
|
email: value.email,
|
|
18304
|
-
password
|
|
18312
|
+
// No `password` here, deliberately. This return is the whitelist that
|
|
18313
|
+
// decides what reaches the `people` collection, and it used to carry the
|
|
18314
|
+
// admin-typed password in clear text — into a document that
|
|
18315
|
+
// `GET /api/people` hands back to the console. The only copy that should
|
|
18316
|
+
// exist is the hash `person.service` writes to the `users` row before the
|
|
18317
|
+
// insert, and nothing anywhere reads it back off the person.
|
|
18305
18318
|
status: value.status ?? "active",
|
|
18306
18319
|
nric: value.nric,
|
|
18307
18320
|
remarks: value.remarks,
|
|
@@ -22720,7 +22733,7 @@ function useVehicleRepo() {
|
|
|
22720
22733
|
let query2 = { ...baseQuery };
|
|
22721
22734
|
if (search)
|
|
22722
22735
|
query2.$text = { $search: search };
|
|
22723
|
-
const
|
|
22736
|
+
const escapeRegex2 = (input) => input.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
22724
22737
|
const buildGroupedPipeline = (matchQuery) => [
|
|
22725
22738
|
{ $match: matchQuery },
|
|
22726
22739
|
{
|
|
@@ -22892,7 +22905,7 @@ function useVehicleRepo() {
|
|
|
22892
22905
|
const countResult = await collection.aggregate(buildGroupedCountPipeline(query2)).toArray();
|
|
22893
22906
|
length = countResult[0]?.total || 0;
|
|
22894
22907
|
if ((!items || items.length === 0) && search) {
|
|
22895
|
-
const escaped =
|
|
22908
|
+
const escaped = escapeRegex2(search);
|
|
22896
22909
|
const regexQuery = {
|
|
22897
22910
|
...baseQuery,
|
|
22898
22911
|
$or: [
|
|
@@ -23498,7 +23511,7 @@ function useVehicleRepo() {
|
|
|
23498
23511
|
...baseQuery,
|
|
23499
23512
|
...search && { $text: { $search: search } }
|
|
23500
23513
|
};
|
|
23501
|
-
const
|
|
23514
|
+
const escapeRegex2 = (input) => input.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
23502
23515
|
const buildPipeline = (matchQuery) => [
|
|
23503
23516
|
{ $match: matchQuery },
|
|
23504
23517
|
{
|
|
@@ -23526,7 +23539,7 @@ function useVehicleRepo() {
|
|
|
23526
23539
|
const countResult = await collection.aggregate(buildCountPipeline(query2)).toArray();
|
|
23527
23540
|
length = countResult[0]?.total || 0;
|
|
23528
23541
|
if ((!items || items.length === 0) && search) {
|
|
23529
|
-
const escaped =
|
|
23542
|
+
const escaped = escapeRegex2(search);
|
|
23530
23543
|
const regexQuery = {
|
|
23531
23544
|
...baseQuery,
|
|
23532
23545
|
$or: [
|
|
@@ -44769,6 +44782,24 @@ var import_mongodb88 = require("mongodb");
|
|
|
44769
44782
|
var import_node_server_utils134 = require("@7365admin1/node-server-utils");
|
|
44770
44783
|
var import_mongodb83 = require("mongodb");
|
|
44771
44784
|
|
|
44785
|
+
// src/utils/resident-account.util.ts
|
|
44786
|
+
function residentAccountGaps(value) {
|
|
44787
|
+
const gaps = [];
|
|
44788
|
+
if (!value.name)
|
|
44789
|
+
gaps.push("a name");
|
|
44790
|
+
if (!value.email)
|
|
44791
|
+
gaps.push("an email address");
|
|
44792
|
+
if (!value.password)
|
|
44793
|
+
gaps.push("a password");
|
|
44794
|
+
return gaps;
|
|
44795
|
+
}
|
|
44796
|
+
function residentAccountGapMessage(value) {
|
|
44797
|
+
const gaps = residentAccountGaps(value);
|
|
44798
|
+
if (gaps.length === 0)
|
|
44799
|
+
return "";
|
|
44800
|
+
return `A ${value.type} needs ${gaps.join(", ")} to be able to sign in. Please complete the form and try again.`;
|
|
44801
|
+
}
|
|
44802
|
+
|
|
44772
44803
|
// src/services/platform-terms.service.ts
|
|
44773
44804
|
var import_node_server_utils133 = require("@7365admin1/node-server-utils");
|
|
44774
44805
|
|
|
@@ -45116,6 +45147,69 @@ function usePersonService() {
|
|
|
45116
45147
|
acceptByUserId: _acceptTermsByUserId,
|
|
45117
45148
|
assertLatestTerms: _assertLatestTerms
|
|
45118
45149
|
} = usePlatformTermsService();
|
|
45150
|
+
async function ensureResidentAccount(value, session) {
|
|
45151
|
+
const gap = residentAccountGapMessage(value);
|
|
45152
|
+
if (gap) {
|
|
45153
|
+
throw new import_node_server_utils134.BadRequestError(gap);
|
|
45154
|
+
}
|
|
45155
|
+
const _user = await getUserByEmail(value.email);
|
|
45156
|
+
if (_user) {
|
|
45157
|
+
throw new import_node_server_utils134.BadRequestError(`User already exists: ${value.email}.`);
|
|
45158
|
+
}
|
|
45159
|
+
const hashedPassword = await (0, import_node_server_utils134.hashPassword)(value.password);
|
|
45160
|
+
const user = {
|
|
45161
|
+
email: value.email,
|
|
45162
|
+
password: hashedPassword,
|
|
45163
|
+
name: value.name,
|
|
45164
|
+
status: value.platform == "mobile" ? "pending" : "active",
|
|
45165
|
+
defaultOrg: value.org?.toString() || "",
|
|
45166
|
+
block: value.block,
|
|
45167
|
+
level: value.level,
|
|
45168
|
+
type: value.type,
|
|
45169
|
+
unitName: value.unitName,
|
|
45170
|
+
contact: value.contact,
|
|
45171
|
+
site: value.site?.toString() || "",
|
|
45172
|
+
unitId: value.unit?.toString() || ""
|
|
45173
|
+
};
|
|
45174
|
+
const userId = await addUser(user, session);
|
|
45175
|
+
if (!userId) {
|
|
45176
|
+
throw new import_node_server_utils134.BadRequestError(
|
|
45177
|
+
`Could not create the sign-in account for ${value.email}.`
|
|
45178
|
+
);
|
|
45179
|
+
}
|
|
45180
|
+
let org = null;
|
|
45181
|
+
if (value?.org) {
|
|
45182
|
+
org = await getOrgById(value.org);
|
|
45183
|
+
if (!org) {
|
|
45184
|
+
value.org = "";
|
|
45185
|
+
}
|
|
45186
|
+
}
|
|
45187
|
+
let site;
|
|
45188
|
+
if (value.site) {
|
|
45189
|
+
site = await getSiteById(value.site);
|
|
45190
|
+
}
|
|
45191
|
+
await addMember(
|
|
45192
|
+
{
|
|
45193
|
+
org: value.org?.toString() || "",
|
|
45194
|
+
orgName: org?.name || "",
|
|
45195
|
+
user: userId.toString(),
|
|
45196
|
+
name: value.name,
|
|
45197
|
+
role: "",
|
|
45198
|
+
type: "resident",
|
|
45199
|
+
siteId: value.site?.toString() || "",
|
|
45200
|
+
siteName: site?.name || ""
|
|
45201
|
+
},
|
|
45202
|
+
session
|
|
45203
|
+
);
|
|
45204
|
+
if (value.acceptedTerms) {
|
|
45205
|
+
await _acceptTermsByUserId(
|
|
45206
|
+
userId.toString(),
|
|
45207
|
+
value.acceptedTerms,
|
|
45208
|
+
session
|
|
45209
|
+
);
|
|
45210
|
+
}
|
|
45211
|
+
return userId.toString();
|
|
45212
|
+
}
|
|
45119
45213
|
async function add(value) {
|
|
45120
45214
|
const session = import_node_server_utils134.useAtlas.getClient()?.startSession();
|
|
45121
45215
|
if (!session) {
|
|
@@ -45139,61 +45233,8 @@ function usePersonService() {
|
|
|
45139
45233
|
const isValidType = ["resident" /* RESIDENT */, "tenant" /* TENANT */].includes(
|
|
45140
45234
|
value?.type
|
|
45141
45235
|
);
|
|
45142
|
-
if (
|
|
45143
|
-
|
|
45144
|
-
if (_user) {
|
|
45145
|
-
throw new import_node_server_utils134.BadRequestError(`User already exists: ${value.email}.`);
|
|
45146
|
-
}
|
|
45147
|
-
const hashedPassword = await (0, import_node_server_utils134.hashPassword)(value.password);
|
|
45148
|
-
const user = {
|
|
45149
|
-
email: value.email,
|
|
45150
|
-
password: hashedPassword,
|
|
45151
|
-
name: value.name,
|
|
45152
|
-
status: value.platform == "mobile" ? "pending" : "active",
|
|
45153
|
-
defaultOrg: value.org?.toString() || "",
|
|
45154
|
-
block: value.block,
|
|
45155
|
-
level: value.level,
|
|
45156
|
-
type: value.type,
|
|
45157
|
-
unitName: value.unitName,
|
|
45158
|
-
contact: value.contact,
|
|
45159
|
-
site: value.site?.toString() || "",
|
|
45160
|
-
unitId: value.unit?.toString() || ""
|
|
45161
|
-
};
|
|
45162
|
-
const userId = await addUser(user, session);
|
|
45163
|
-
value.user = userId.toString();
|
|
45164
|
-
let org = null;
|
|
45165
|
-
if (userId) {
|
|
45166
|
-
if (value?.org) {
|
|
45167
|
-
org = await getOrgById(value.org);
|
|
45168
|
-
if (!org) {
|
|
45169
|
-
value.org = "";
|
|
45170
|
-
}
|
|
45171
|
-
}
|
|
45172
|
-
let site;
|
|
45173
|
-
if (value.site) {
|
|
45174
|
-
site = await getSiteById(value.site);
|
|
45175
|
-
}
|
|
45176
|
-
await addMember(
|
|
45177
|
-
{
|
|
45178
|
-
org: value.org?.toString() || "",
|
|
45179
|
-
orgName: org?.name || "",
|
|
45180
|
-
user: userId.toString(),
|
|
45181
|
-
name: value.name,
|
|
45182
|
-
role: "",
|
|
45183
|
-
type: "resident",
|
|
45184
|
-
siteId: value.site?.toString() || "",
|
|
45185
|
-
siteName: site?.name || ""
|
|
45186
|
-
},
|
|
45187
|
-
session
|
|
45188
|
-
);
|
|
45189
|
-
if (value.acceptedTerms) {
|
|
45190
|
-
await _acceptTermsByUserId(
|
|
45191
|
-
userId.toString(),
|
|
45192
|
-
value.acceptedTerms,
|
|
45193
|
-
session
|
|
45194
|
-
);
|
|
45195
|
-
}
|
|
45196
|
-
}
|
|
45236
|
+
if (isValidType) {
|
|
45237
|
+
value.user = await ensureResidentAccount(value, session);
|
|
45197
45238
|
}
|
|
45198
45239
|
if (value.isOwner && value.unit) {
|
|
45199
45240
|
const unit = await _getUnitById(value.unit.toString());
|
|
@@ -45264,6 +45305,18 @@ function usePersonService() {
|
|
|
45264
45305
|
const isBlockChanged = "block" in value && toKey(value.block) !== toKey(person.block);
|
|
45265
45306
|
const isLevelChanged = "level" in value && toKey(value.level) !== toKey(person.level);
|
|
45266
45307
|
const isUnitChanged = "unit" in value && toKey(value.unit) !== toKey(person.unit);
|
|
45308
|
+
const personType = value.type ?? person.type;
|
|
45309
|
+
const carriesLogin = [
|
|
45310
|
+
"resident" /* RESIDENT */,
|
|
45311
|
+
"tenant" /* TENANT */
|
|
45312
|
+
].includes(personType);
|
|
45313
|
+
if (carriesLogin && !person.user && value.password) {
|
|
45314
|
+
value.user = await ensureResidentAccount(
|
|
45315
|
+
{ ...person, ...value, type: personType },
|
|
45316
|
+
session
|
|
45317
|
+
);
|
|
45318
|
+
}
|
|
45319
|
+
delete value.password;
|
|
45267
45320
|
await _updateById(_id, value, session);
|
|
45268
45321
|
if (isOrgChanged && person.user) {
|
|
45269
45322
|
await _updateUserOrgById(
|
|
@@ -45510,7 +45563,7 @@ function usePersonService() {
|
|
|
45510
45563
|
let set = {};
|
|
45511
45564
|
const cleanInput = (str) => str.replace(/\s+/g, "");
|
|
45512
45565
|
const cleanPhone = (str) => str.replace(/\D/g, "");
|
|
45513
|
-
const
|
|
45566
|
+
const escapeRegex2 = (str) => str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
45514
45567
|
const nric = value.nric ? cleanInput(value.nric) : "";
|
|
45515
45568
|
const name = value.name?.trim();
|
|
45516
45569
|
const contact = value.contact?.trim();
|
|
@@ -45518,8 +45571,8 @@ function usePersonService() {
|
|
|
45518
45571
|
const email = value.email?.trim();
|
|
45519
45572
|
const type = value.type?.trim();
|
|
45520
45573
|
const company = value.company?.trim();
|
|
45521
|
-
const nricQuery = nric ? { nric: { $regex: `^${
|
|
45522
|
-
const nameQuery = name ? { name: { $regex: `^${
|
|
45574
|
+
const nricQuery = nric ? { nric: { $regex: `^${escapeRegex2(nric)}$`, $options: "i" } } : null;
|
|
45575
|
+
const nameQuery = name ? { name: { $regex: `^${escapeRegex2(name)}$`, $options: "i" } } : null;
|
|
45523
45576
|
let nricCondition = null;
|
|
45524
45577
|
if (nric) {
|
|
45525
45578
|
if (nric.length < 9) {
|
|
@@ -45532,8 +45585,8 @@ function usePersonService() {
|
|
|
45532
45585
|
}
|
|
45533
45586
|
const orConditions = [
|
|
45534
45587
|
...nricCondition ? [nricCondition] : [],
|
|
45535
|
-
...contact ? [{ contacts: { $regex: `^\\+?${cleanPhone(
|
|
45536
|
-
...plateNumber ? [{ plateNumbers: { $regex: `^${
|
|
45588
|
+
...contact ? [{ contacts: { $regex: `^\\+?${cleanPhone(escapeRegex2(contact))}$`, $options: "i" } }] : [],
|
|
45589
|
+
...plateNumber ? [{ plateNumbers: { $regex: `^${escapeRegex2(plateNumber)}$`, $options: "i" } }] : []
|
|
45537
45590
|
];
|
|
45538
45591
|
const existingPerson2 = await findFirstTen(
|
|
45539
45592
|
{
|
|
@@ -47442,7 +47495,7 @@ function useHidAmicoRepo() {
|
|
|
47442
47495
|
passwordSet: Boolean(password)
|
|
47443
47496
|
};
|
|
47444
47497
|
}
|
|
47445
|
-
function
|
|
47498
|
+
function escapeRegex2(value) {
|
|
47446
47499
|
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
47447
47500
|
}
|
|
47448
47501
|
function buildIdentityKeyQuery(value) {
|
|
@@ -47651,7 +47704,7 @@ function useHidAmicoRepo() {
|
|
|
47651
47704
|
{ serviceProvider: { $in: subjectIds } },
|
|
47652
47705
|
{ user: { $in: subjectIds } }
|
|
47653
47706
|
] : null;
|
|
47654
|
-
const search = value.search ?
|
|
47707
|
+
const search = value.search ? escapeRegex2(value.search) : "";
|
|
47655
47708
|
const searchOr = search ? [
|
|
47656
47709
|
{ hidUserId: { $regex: search, $options: "i" } },
|
|
47657
47710
|
{ registration: { $regex: search, $options: "i" } },
|
|
@@ -47915,7 +47968,7 @@ function useHidAmicoRepo() {
|
|
|
47915
47968
|
const text2 = search.trim();
|
|
47916
47969
|
if (!text2)
|
|
47917
47970
|
return {};
|
|
47918
|
-
const pattern = { $regex:
|
|
47971
|
+
const pattern = { $regex: escapeRegex2(text2), $options: "i" };
|
|
47919
47972
|
return {
|
|
47920
47973
|
$or: [
|
|
47921
47974
|
{ name: pattern },
|
|
@@ -62789,7 +62842,7 @@ function usePatrolEmailRepo() {
|
|
|
62789
62842
|
throw new import_node_server_utils163.BadRequestError(`Invalid ${label} format.`);
|
|
62790
62843
|
}
|
|
62791
62844
|
}
|
|
62792
|
-
function
|
|
62845
|
+
function escapeRegex2(value) {
|
|
62793
62846
|
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
62794
62847
|
}
|
|
62795
62848
|
async function createIndexes() {
|
|
@@ -62926,8 +62979,8 @@ function usePatrolEmailRepo() {
|
|
|
62926
62979
|
...status ? { status } : { status: { $ne: "deleted" } },
|
|
62927
62980
|
...search && {
|
|
62928
62981
|
$or: [
|
|
62929
|
-
{ subject: { $regex:
|
|
62930
|
-
{ recipients: { $regex:
|
|
62982
|
+
{ subject: { $regex: escapeRegex2(search), $options: "i" } },
|
|
62983
|
+
{ recipients: { $regex: escapeRegex2(search), $options: "i" } }
|
|
62931
62984
|
]
|
|
62932
62985
|
}
|
|
62933
62986
|
};
|