@7365admin1/core 2.50.0 → 2.52.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/dist/index.d.ts +8 -3
- package/dist/index.js +412 -53
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +412 -53
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -3952,6 +3952,9 @@ function useVerificationRepo() {
|
|
|
3952
3952
|
return Promise.reject(error);
|
|
3953
3953
|
}
|
|
3954
3954
|
}
|
|
3955
|
+
async function findOne(query) {
|
|
3956
|
+
return await collection.findOne(query);
|
|
3957
|
+
}
|
|
3955
3958
|
return {
|
|
3956
3959
|
createIndex,
|
|
3957
3960
|
createTextIndex,
|
|
@@ -3960,7 +3963,8 @@ function useVerificationRepo() {
|
|
|
3960
3963
|
getVerifications,
|
|
3961
3964
|
getByIdByType,
|
|
3962
3965
|
updateStatusById,
|
|
3963
|
-
getByStatus
|
|
3966
|
+
getByStatus,
|
|
3967
|
+
findOne
|
|
3964
3968
|
};
|
|
3965
3969
|
}
|
|
3966
3970
|
|
|
@@ -5297,29 +5301,28 @@ function useVerificationService() {
|
|
|
5297
5301
|
throw error;
|
|
5298
5302
|
}
|
|
5299
5303
|
}
|
|
5300
|
-
async function createSimpleUserInvite({
|
|
5301
|
-
email,
|
|
5302
|
-
metadata
|
|
5303
|
-
}) {
|
|
5304
|
+
async function createSimpleUserInvite({ email, metadata }) {
|
|
5304
5305
|
const type = "user-invite";
|
|
5306
|
+
if (metadata?.org)
|
|
5307
|
+
await getOrgById(metadata.org);
|
|
5308
|
+
if (metadata?.siteId)
|
|
5309
|
+
await getSiteById(metadata.siteId);
|
|
5310
|
+
const existing = await useVerificationRepo().findOne({
|
|
5311
|
+
type,
|
|
5312
|
+
email,
|
|
5313
|
+
"metadata.org": metadata?.org,
|
|
5314
|
+
"metadata.siteId": metadata?.siteId
|
|
5315
|
+
});
|
|
5316
|
+
if (existing)
|
|
5317
|
+
return existing._id;
|
|
5305
5318
|
const value = {
|
|
5306
5319
|
type,
|
|
5307
5320
|
email,
|
|
5308
5321
|
metadata,
|
|
5309
|
-
expireAt: new Date(
|
|
5310
|
-
(/* @__PURE__ */ new Date()).getTime() + 72 * 60 * 60 * 1e3
|
|
5311
|
-
).toISOString(),
|
|
5322
|
+
expireAt: new Date(Date.now() + 72 * 60 * 60 * 1e3).toISOString(),
|
|
5312
5323
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
5313
5324
|
};
|
|
5314
|
-
if (value.metadata?.org) {
|
|
5315
|
-
await getOrgById(value.metadata?.org);
|
|
5316
|
-
}
|
|
5317
|
-
if (value.metadata?.siteId) {
|
|
5318
|
-
await getSiteById(value.metadata?.siteId);
|
|
5319
|
-
}
|
|
5320
5325
|
const res = await add(value);
|
|
5321
|
-
const dir = __dirname;
|
|
5322
|
-
const filePath = getDirectory(dir, "./public/handlebars/user-invite");
|
|
5323
5326
|
const link = `${APP_MAIN}/verify/invitation/${res}`;
|
|
5324
5327
|
const emailContent = compileHandlebar({
|
|
5325
5328
|
context: {
|
|
@@ -5327,9 +5330,9 @@ function useVerificationService() {
|
|
|
5327
5330
|
validity: VERIFICATION_USER_INVITE_DURATION,
|
|
5328
5331
|
link
|
|
5329
5332
|
},
|
|
5330
|
-
filePath
|
|
5333
|
+
filePath: getDirectory(__dirname, "./public/handlebars/user-invite")
|
|
5331
5334
|
});
|
|
5332
|
-
mailer.sendMail({
|
|
5335
|
+
await mailer.sendMail({
|
|
5333
5336
|
to: email,
|
|
5334
5337
|
subject: "User Invite",
|
|
5335
5338
|
html: emailContent,
|
|
@@ -7181,7 +7184,7 @@ function useMemberService() {
|
|
|
7181
7184
|
getByRoles
|
|
7182
7185
|
} = useMemberRepo();
|
|
7183
7186
|
const { getById: _getVerificationById, updateStatusById } = useVerificationRepo();
|
|
7184
|
-
const { getUserByEmail, updateDefaultOrgByEmail } = useUserRepo();
|
|
7187
|
+
const { getUserByEmail, updateDefaultOrgByEmail, getUserById } = useUserRepo();
|
|
7185
7188
|
const { getById: getOrgById } = useOrgRepo();
|
|
7186
7189
|
const { getSiteById } = useSiteRepo();
|
|
7187
7190
|
const { getOwnerRolesByTypeOrg } = useRoleRepo();
|
|
@@ -7243,6 +7246,52 @@ function useMemberService() {
|
|
|
7243
7246
|
session?.endSession();
|
|
7244
7247
|
}
|
|
7245
7248
|
}
|
|
7249
|
+
async function createMemberDirect({
|
|
7250
|
+
userId,
|
|
7251
|
+
orgId,
|
|
7252
|
+
roleId,
|
|
7253
|
+
app,
|
|
7254
|
+
siteId,
|
|
7255
|
+
siteName
|
|
7256
|
+
}) {
|
|
7257
|
+
const session = useAtlas15.getClient()?.startSession();
|
|
7258
|
+
session?.startTransaction();
|
|
7259
|
+
try {
|
|
7260
|
+
const org = await getOrgById(orgId);
|
|
7261
|
+
if (!org)
|
|
7262
|
+
throw new BadRequestError29("Organization not found.");
|
|
7263
|
+
const user = await getUserById(userId);
|
|
7264
|
+
if (!user)
|
|
7265
|
+
throw new BadRequestError29("User not found.");
|
|
7266
|
+
const member = await addMember(
|
|
7267
|
+
{
|
|
7268
|
+
org: org._id?.toString() || "",
|
|
7269
|
+
orgName: org.name || "",
|
|
7270
|
+
user: user._id?.toString() || "",
|
|
7271
|
+
name: user?.email,
|
|
7272
|
+
role: roleId,
|
|
7273
|
+
type: app,
|
|
7274
|
+
siteId: siteId ?? "",
|
|
7275
|
+
siteName: siteName ?? ""
|
|
7276
|
+
},
|
|
7277
|
+
session
|
|
7278
|
+
);
|
|
7279
|
+
if (!user.defaultOrg) {
|
|
7280
|
+
await updateDefaultOrgByEmail(
|
|
7281
|
+
user.email,
|
|
7282
|
+
org._id?.toString() || "",
|
|
7283
|
+
session
|
|
7284
|
+
);
|
|
7285
|
+
}
|
|
7286
|
+
await session?.commitTransaction();
|
|
7287
|
+
return { member };
|
|
7288
|
+
} catch (error) {
|
|
7289
|
+
await session?.abortTransaction();
|
|
7290
|
+
throw error;
|
|
7291
|
+
} finally {
|
|
7292
|
+
session?.endSession();
|
|
7293
|
+
}
|
|
7294
|
+
}
|
|
7246
7295
|
async function updateRoleById(id, role, type, org) {
|
|
7247
7296
|
const owner = await getOwnerRolesByTypeOrg(type, org);
|
|
7248
7297
|
if (!owner.length) {
|
|
@@ -7270,6 +7319,7 @@ function useMemberService() {
|
|
|
7270
7319
|
}
|
|
7271
7320
|
return {
|
|
7272
7321
|
createMember,
|
|
7322
|
+
createMemberDirect,
|
|
7273
7323
|
updateRoleById
|
|
7274
7324
|
};
|
|
7275
7325
|
}
|
|
@@ -7284,7 +7334,7 @@ function useMemberController() {
|
|
|
7284
7334
|
updateMemberStatus: _updateMemberStatus,
|
|
7285
7335
|
updateStatusByUserId: _updateStatusByUserId
|
|
7286
7336
|
} = useMemberRepo();
|
|
7287
|
-
const { createMember: _createMember, updateRoleById: _updateRoleById } = useMemberService();
|
|
7337
|
+
const { createMember: _createMember, createMemberDirect: _createMemberDirect, updateRoleById: _updateRoleById } = useMemberService();
|
|
7288
7338
|
async function createMember(req, res, next) {
|
|
7289
7339
|
const validation = Joi15.string().hex().required();
|
|
7290
7340
|
const _id = req.params.id;
|
|
@@ -7473,6 +7523,32 @@ function useMemberController() {
|
|
|
7473
7523
|
return;
|
|
7474
7524
|
}
|
|
7475
7525
|
}
|
|
7526
|
+
async function createMemberDirect(req, res, next) {
|
|
7527
|
+
const validation = Joi15.object({
|
|
7528
|
+
userId: Joi15.string().hex().required(),
|
|
7529
|
+
orgId: Joi15.string().hex().required(),
|
|
7530
|
+
roleId: Joi15.string().hex().required(),
|
|
7531
|
+
app: Joi15.string().required(),
|
|
7532
|
+
siteId: Joi15.string().hex().optional().allow("", null),
|
|
7533
|
+
siteName: Joi15.string().optional().allow("", null)
|
|
7534
|
+
});
|
|
7535
|
+
const { error } = validation.validate(req.body);
|
|
7536
|
+
if (error) {
|
|
7537
|
+
logger21.log({ level: "error", message: error.message });
|
|
7538
|
+
next(new BadRequestError30(error.message));
|
|
7539
|
+
return;
|
|
7540
|
+
}
|
|
7541
|
+
const { userId, orgId, roleId, app, siteId, siteName } = req.body;
|
|
7542
|
+
try {
|
|
7543
|
+
const data = await _createMemberDirect({ userId, orgId, roleId, app, siteId, siteName });
|
|
7544
|
+
res.status(201).json(data);
|
|
7545
|
+
return;
|
|
7546
|
+
} catch (error2) {
|
|
7547
|
+
logger21.log({ level: "error", message: error2.message });
|
|
7548
|
+
next(error2);
|
|
7549
|
+
return;
|
|
7550
|
+
}
|
|
7551
|
+
}
|
|
7476
7552
|
return {
|
|
7477
7553
|
createMember,
|
|
7478
7554
|
getByUserId,
|
|
@@ -7480,7 +7556,8 @@ function useMemberController() {
|
|
|
7480
7556
|
getAll,
|
|
7481
7557
|
getOrgsByMembership,
|
|
7482
7558
|
updateMemberStatus,
|
|
7483
|
-
updateRoleById
|
|
7559
|
+
updateRoleById,
|
|
7560
|
+
createMemberDirect
|
|
7484
7561
|
};
|
|
7485
7562
|
}
|
|
7486
7563
|
|
|
@@ -13016,23 +13093,42 @@ var CameraType = /* @__PURE__ */ ((CameraType2) => {
|
|
|
13016
13093
|
})(CameraType || {});
|
|
13017
13094
|
var schemaUpdateSiteCamera = Joi34.object({
|
|
13018
13095
|
host: Joi34.string().required(),
|
|
13019
|
-
username: Joi34.string().
|
|
13020
|
-
|
|
13096
|
+
username: Joi34.string().when("type", {
|
|
13097
|
+
is: "anpr",
|
|
13098
|
+
then: Joi34.required(),
|
|
13099
|
+
otherwise: Joi34.optional().allow(null, "")
|
|
13100
|
+
}),
|
|
13101
|
+
password: Joi34.string().when("type", {
|
|
13102
|
+
is: "anpr",
|
|
13103
|
+
then: Joi34.required(),
|
|
13104
|
+
otherwise: Joi34.optional().allow(null, "")
|
|
13105
|
+
}),
|
|
13021
13106
|
category: Joi34.string().allow("standard", "resident", "visitor").required(),
|
|
13022
13107
|
guardPost: Joi34.number().integer().optional(),
|
|
13023
|
-
direction: Joi34.string().optional().allow("entry", "exit", "both", "none")
|
|
13108
|
+
direction: Joi34.string().optional().allow("entry", "exit", "both", "none"),
|
|
13109
|
+
status: Joi34.string().optional().allow("active", "inactive", "deleted"),
|
|
13110
|
+
name: Joi34.string().optional().allow(null, "")
|
|
13024
13111
|
});
|
|
13025
13112
|
var schemaSiteCamera = Joi34.object({
|
|
13026
13113
|
_id: Joi34.string().hex().optional(),
|
|
13027
13114
|
site: Joi34.string().hex().required(),
|
|
13028
13115
|
host: Joi34.string().required(),
|
|
13029
|
-
username: Joi34.string().
|
|
13030
|
-
|
|
13116
|
+
username: Joi34.string().when("type", {
|
|
13117
|
+
is: "anpr",
|
|
13118
|
+
then: Joi34.required(),
|
|
13119
|
+
otherwise: Joi34.optional().allow(null, "")
|
|
13120
|
+
}),
|
|
13121
|
+
password: Joi34.string().when("type", {
|
|
13122
|
+
is: "anpr",
|
|
13123
|
+
then: Joi34.required(),
|
|
13124
|
+
otherwise: Joi34.optional().allow(null, "")
|
|
13125
|
+
}),
|
|
13031
13126
|
type: Joi34.string().allow("ip", "anpr").required(),
|
|
13032
13127
|
category: Joi34.string().allow("standard", "resident", "visitor").required(),
|
|
13033
13128
|
direction: Joi34.string().required().allow("entry", "exit", "both", "none"),
|
|
13034
13129
|
guardPost: Joi34.number().integer().optional(),
|
|
13035
13130
|
status: Joi34.string().optional().allow("active", "inactive", "deleted"),
|
|
13131
|
+
name: Joi34.string().optional().allow(null, ""),
|
|
13036
13132
|
createdAt: Joi34.date().optional(),
|
|
13037
13133
|
updatedAt: Joi34.date().optional(),
|
|
13038
13134
|
deletedAt: Joi34.date().optional()
|
|
@@ -13061,6 +13157,7 @@ function MSiteCamera(value) {
|
|
|
13061
13157
|
guardPost: value.guardPost ?? 0,
|
|
13062
13158
|
status: value.status ?? "active",
|
|
13063
13159
|
direction: value.direction ?? "",
|
|
13160
|
+
name: value.name ?? "",
|
|
13064
13161
|
createdAt: value.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
13065
13162
|
updatedAt: value.updatedAt ?? "",
|
|
13066
13163
|
deletedAt: value.deletedAt ?? ""
|
|
@@ -13381,7 +13478,24 @@ var schemaUpdateVisTrans = Joi36.object({
|
|
|
13381
13478
|
checkInRemarks: Joi36.string().optional().allow("", null),
|
|
13382
13479
|
checkOutRemarks: Joi36.string().optional().allow("", null),
|
|
13383
13480
|
expectedCheckIn: Joi36.string().isoDate().optional(),
|
|
13384
|
-
purpose: Joi36.string().optional().allow(null, "")
|
|
13481
|
+
purpose: Joi36.string().optional().allow(null, ""),
|
|
13482
|
+
members: Joi36.array().items(
|
|
13483
|
+
Joi36.object({
|
|
13484
|
+
name: Joi36.string().optional().allow(null, ""),
|
|
13485
|
+
nric: Joi36.string().optional().allow(null, ""),
|
|
13486
|
+
visitorPass: Joi36.array().items(
|
|
13487
|
+
Joi36.object({
|
|
13488
|
+
keyId: Joi36.string().hex().length(24).required()
|
|
13489
|
+
})
|
|
13490
|
+
).optional().allow(null),
|
|
13491
|
+
passKeys: Joi36.array().items(
|
|
13492
|
+
Joi36.object({
|
|
13493
|
+
keyId: Joi36.string().hex().length(24).required()
|
|
13494
|
+
})
|
|
13495
|
+
).optional().allow(null),
|
|
13496
|
+
contact: Joi36.string().optional().allow(null, "")
|
|
13497
|
+
})
|
|
13498
|
+
).optional().allow(null)
|
|
13385
13499
|
});
|
|
13386
13500
|
function MVisitorTransaction(value) {
|
|
13387
13501
|
const { error } = schemaVisitorTransaction.validate(value, {
|
|
@@ -13831,14 +13945,14 @@ function useVisitorTransactionRepo() {
|
|
|
13831
13945
|
);
|
|
13832
13946
|
}
|
|
13833
13947
|
}
|
|
13834
|
-
async function updateById(_id, value, session,
|
|
13948
|
+
async function updateById(_id, value, session, isManualCheckOut = true) {
|
|
13835
13949
|
try {
|
|
13836
13950
|
_id = new ObjectId40(_id);
|
|
13837
13951
|
} catch (error) {
|
|
13838
13952
|
throw new BadRequestError66("Invalid visitor transaction ID format.");
|
|
13839
13953
|
}
|
|
13840
13954
|
value.updatedAt = /* @__PURE__ */ new Date();
|
|
13841
|
-
if (value.checkOut &&
|
|
13955
|
+
if (value.checkOut && isManualCheckOut) {
|
|
13842
13956
|
value.manualCheckout = true;
|
|
13843
13957
|
}
|
|
13844
13958
|
try {
|
|
@@ -17294,7 +17408,7 @@ function useDahuaService() {
|
|
|
17294
17408
|
);
|
|
17295
17409
|
}
|
|
17296
17410
|
}
|
|
17297
|
-
if (["entry"
|
|
17411
|
+
if (["entry"].includes(designation) && plateNumber) {
|
|
17298
17412
|
await closeOpenTransaction(plateNumber);
|
|
17299
17413
|
const vehicle = await getVehicleByPlateNumber(plateNumber);
|
|
17300
17414
|
const visitorTransaction = {
|
|
@@ -17349,11 +17463,11 @@ function useDahuaService() {
|
|
|
17349
17463
|
} catch (error) {
|
|
17350
17464
|
console.log("failed to create visitor transaction", error);
|
|
17351
17465
|
loggerDahua.error(
|
|
17352
|
-
`[${site}][${
|
|
17466
|
+
`[${site}][${host}] Error creating visitor transaction:`,
|
|
17353
17467
|
error
|
|
17354
17468
|
);
|
|
17355
17469
|
}
|
|
17356
|
-
} else if (["exit"
|
|
17470
|
+
} else if (["exit"].includes(designation) && plateNumber) {
|
|
17357
17471
|
const vehicle = await getVehicleByPlateNumber(plateNumber);
|
|
17358
17472
|
const existingOpenTransaction = await getOpenByPlateNumber(
|
|
17359
17473
|
plateNumber,
|
|
@@ -17378,6 +17492,92 @@ function useDahuaService() {
|
|
|
17378
17492
|
if (onDetected2) {
|
|
17379
17493
|
onDetected2({ reload: true, site: existingOpenTransaction?.site?.toString(), host, direction });
|
|
17380
17494
|
}
|
|
17495
|
+
} else if (["both"].includes(designation) && plateNumber) {
|
|
17496
|
+
if (direction.toLowerCase() === "leave") {
|
|
17497
|
+
const vehicle = await getVehicleByPlateNumber(plateNumber);
|
|
17498
|
+
const existingOpenTransaction = await getOpenByPlateNumber(
|
|
17499
|
+
plateNumber,
|
|
17500
|
+
site
|
|
17501
|
+
);
|
|
17502
|
+
if (existingOpenTransaction?._id) {
|
|
17503
|
+
currentTransactionId = existingOpenTransaction._id.toString();
|
|
17504
|
+
currentSnapshotField = "snapshotExitImage";
|
|
17505
|
+
}
|
|
17506
|
+
if (vehicle?.recNo) {
|
|
17507
|
+
const dahuaPayload = {
|
|
17508
|
+
host,
|
|
17509
|
+
username,
|
|
17510
|
+
password,
|
|
17511
|
+
plateNumber,
|
|
17512
|
+
mode: "TrafficRedList" /* TRAFFIC_REDLIST */,
|
|
17513
|
+
recno: vehicle?.recNo
|
|
17514
|
+
};
|
|
17515
|
+
await removePlateNumber(dahuaPayload);
|
|
17516
|
+
}
|
|
17517
|
+
await closeOpenTransaction(plateNumber);
|
|
17518
|
+
if (onDetected2 && existingOpenTransaction?.site) {
|
|
17519
|
+
onDetected2({ reload: true, site: existingOpenTransaction?.site?.toString(), host, direction });
|
|
17520
|
+
}
|
|
17521
|
+
} else if (direction.toLowerCase() === "approach") {
|
|
17522
|
+
await closeOpenTransaction(plateNumber);
|
|
17523
|
+
const vehicle = await getVehicleByPlateNumber(plateNumber);
|
|
17524
|
+
const visitorTransaction = {
|
|
17525
|
+
site,
|
|
17526
|
+
plateNumber,
|
|
17527
|
+
org,
|
|
17528
|
+
status: "unregistered" /* UNREGISTERED */
|
|
17529
|
+
};
|
|
17530
|
+
const typesForAutoRegister = Object.values(PersonTypes);
|
|
17531
|
+
let startDate = vehicle?.start ? new Date(vehicle?.start) : /* @__PURE__ */ new Date();
|
|
17532
|
+
let endDate = vehicle?.end ? new Date(vehicle?.end) : new Date(startDate.getTime() + 60 * 60 * 1e3);
|
|
17533
|
+
if (vehicle && typeof vehicle.category === "string" && typesForAutoRegister.includes(vehicle.category)) {
|
|
17534
|
+
visitorTransaction.name = vehicle.name;
|
|
17535
|
+
visitorTransaction.nric = vehicle.nric;
|
|
17536
|
+
visitorTransaction.contact = vehicle.phoneNumber;
|
|
17537
|
+
visitorTransaction.block = Number(vehicle.block);
|
|
17538
|
+
visitorTransaction.level = vehicle.level;
|
|
17539
|
+
visitorTransaction.unit = vehicle.unit?.toString();
|
|
17540
|
+
visitorTransaction.unitName = vehicle.unitName;
|
|
17541
|
+
visitorTransaction.type = vehicle.category;
|
|
17542
|
+
visitorTransaction.status = "registered" /* REGISTERED */;
|
|
17543
|
+
visitorTransaction.recNo = vehicle.recNo;
|
|
17544
|
+
visitorTransaction.expiredAt = vehicle.end;
|
|
17545
|
+
}
|
|
17546
|
+
const dahuaPayload = {
|
|
17547
|
+
host,
|
|
17548
|
+
username,
|
|
17549
|
+
password,
|
|
17550
|
+
plateNumber,
|
|
17551
|
+
mode: "TrafficRedList" /* TRAFFIC_REDLIST */,
|
|
17552
|
+
owner: vehicle?.name ?? ""
|
|
17553
|
+
};
|
|
17554
|
+
dahuaPayload.start = formatDahuaDate(startDate);
|
|
17555
|
+
dahuaPayload.end = formatDahuaDate(endDate);
|
|
17556
|
+
const shouldHaveTimeLimit = visitorTransaction.type === "guest" /* GUEST */ || visitorTransaction.status === "unregistered" /* UNREGISTERED */;
|
|
17557
|
+
if (shouldHaveTimeLimit) {
|
|
17558
|
+
const startDate2 = /* @__PURE__ */ new Date();
|
|
17559
|
+
const endDate2 = new Date(startDate2.getTime() + 60 * 60 * 1e3);
|
|
17560
|
+
dahuaPayload.start = formatDahuaDate(startDate2);
|
|
17561
|
+
dahuaPayload.end = formatDahuaDate(endDate2);
|
|
17562
|
+
visitorTransaction.expiredAt = endDate2.toISOString();
|
|
17563
|
+
}
|
|
17564
|
+
try {
|
|
17565
|
+
await addPlateNumber(dahuaPayload);
|
|
17566
|
+
const result = await add(visitorTransaction, void 0, true);
|
|
17567
|
+
const transactionId = result?._id;
|
|
17568
|
+
currentTransactionId = transactionId?.toString();
|
|
17569
|
+
currentSnapshotField = "snapshotEntryImage";
|
|
17570
|
+
if (onDetected2) {
|
|
17571
|
+
onDetected2({ _id: transactionId, site: result?.site?.toString(), plateNumber: result.plateNumber, host, direction });
|
|
17572
|
+
}
|
|
17573
|
+
} catch (error) {
|
|
17574
|
+
console.log("failed to create visitor transaction", error);
|
|
17575
|
+
loggerDahua.error(
|
|
17576
|
+
`[${site}][${host}] Error creating visitor transaction:`,
|
|
17577
|
+
error
|
|
17578
|
+
);
|
|
17579
|
+
}
|
|
17580
|
+
}
|
|
17381
17581
|
}
|
|
17382
17582
|
}
|
|
17383
17583
|
}
|
|
@@ -17509,6 +17709,9 @@ function useDahuaService() {
|
|
|
17509
17709
|
}
|
|
17510
17710
|
}
|
|
17511
17711
|
async function listenToCamera(camera, onDetected) {
|
|
17712
|
+
if (camera?.type != "anpr" && camera?.status != "deleted") {
|
|
17713
|
+
return;
|
|
17714
|
+
}
|
|
17512
17715
|
if (onDetected) {
|
|
17513
17716
|
_savedOnDetected = onDetected;
|
|
17514
17717
|
}
|
|
@@ -17517,6 +17720,14 @@ function useDahuaService() {
|
|
|
17517
17720
|
return;
|
|
17518
17721
|
}
|
|
17519
17722
|
const cameraId = camera._id.toString();
|
|
17723
|
+
if (camera.status !== "active") {
|
|
17724
|
+
if (cameraRegistry.has(cameraId)) {
|
|
17725
|
+
loggerDahua.info(`[${camera?.host ?? camera?._id}] Camera is ${camera.status}. Stopping connection.`);
|
|
17726
|
+
cameraRegistry.get(cameraId)?.abort();
|
|
17727
|
+
cameraRegistry.delete(cameraId);
|
|
17728
|
+
}
|
|
17729
|
+
return;
|
|
17730
|
+
}
|
|
17520
17731
|
if (cameraRegistry.has(cameraId)) {
|
|
17521
17732
|
loggerDahua.info(`[${camera.host}] Stopping existing connection for update.`);
|
|
17522
17733
|
cameraRegistry.get(cameraId)?.abort();
|
|
@@ -17664,6 +17875,9 @@ function useDahuaService() {
|
|
|
17664
17875
|
continue;
|
|
17665
17876
|
} else if ([400, 401, 403, 500].includes(response.statusCode)) {
|
|
17666
17877
|
loggerDahua.error(`[${host}] Connection error: ${response.statusCode}`);
|
|
17878
|
+
if (onDetected) {
|
|
17879
|
+
onDetected({ site, messagePermanent: `Camera with last 3 characters ${host?.slice(-3)} Connection Error. Check username and password.` });
|
|
17880
|
+
}
|
|
17667
17881
|
return;
|
|
17668
17882
|
}
|
|
17669
17883
|
loggerDahua.info(`[${host}] Successfully connected to ANPR.`);
|
|
@@ -17716,8 +17930,15 @@ function useDahuaService() {
|
|
|
17716
17930
|
break;
|
|
17717
17931
|
}
|
|
17718
17932
|
loggerDahua.error(
|
|
17719
|
-
`[${host}] Connection lost or error: ${error.message || error}. Retrying in
|
|
17933
|
+
`[${host}] Connection lost or error: ${error.message || error}. Retrying in 10 seconds...`
|
|
17720
17934
|
);
|
|
17935
|
+
if (onDetected) {
|
|
17936
|
+
onDetected({
|
|
17937
|
+
site,
|
|
17938
|
+
host,
|
|
17939
|
+
message: `Camera in ${gate} Connection Lost. Retrying in 10 seconds. Check the camera if this issue persists, Or set the camera to Inactive to stop this notification.`
|
|
17940
|
+
});
|
|
17941
|
+
}
|
|
17721
17942
|
} finally {
|
|
17722
17943
|
if (bufferQueue?.destroy) {
|
|
17723
17944
|
try {
|
|
@@ -17738,7 +17959,7 @@ function useDahuaService() {
|
|
|
17738
17959
|
}
|
|
17739
17960
|
}
|
|
17740
17961
|
if (!signal.aborted) {
|
|
17741
|
-
await new Promise((res) => setTimeout(res,
|
|
17962
|
+
await new Promise((res) => setTimeout(res, 1e4));
|
|
17742
17963
|
}
|
|
17743
17964
|
}
|
|
17744
17965
|
loggerDahua.info(`[${host}] ANPR Listener stopped.`);
|
|
@@ -18240,6 +18461,8 @@ function useSiteCameraRepo() {
|
|
|
18240
18461
|
}
|
|
18241
18462
|
try {
|
|
18242
18463
|
await collection.deleteOne({ _id }, { session });
|
|
18464
|
+
const { listenToCamera } = useDahuaService();
|
|
18465
|
+
await listenToCamera({ _id, status: "deleted" });
|
|
18243
18466
|
delCachedData();
|
|
18244
18467
|
return "Successfully deleted site camera.";
|
|
18245
18468
|
} catch (error) {
|
|
@@ -19091,7 +19314,7 @@ function useBuildingRepo() {
|
|
|
19091
19314
|
}
|
|
19092
19315
|
}
|
|
19093
19316
|
}
|
|
19094
|
-
async function getBuildingLevel(site, block) {
|
|
19317
|
+
async function getBuildingLevel(site, block, isVMS) {
|
|
19095
19318
|
try {
|
|
19096
19319
|
site = new ObjectId49(site);
|
|
19097
19320
|
} catch (error) {
|
|
@@ -19101,8 +19324,7 @@ function useBuildingRepo() {
|
|
|
19101
19324
|
site,
|
|
19102
19325
|
block
|
|
19103
19326
|
};
|
|
19104
|
-
const
|
|
19105
|
-
const cacheKey = makeCacheKey26(buildings_namespace_collection, cacheOptions);
|
|
19327
|
+
const cacheKey = makeCacheKey26(buildings_namespace_collection, { ...query, isVMS });
|
|
19106
19328
|
try {
|
|
19107
19329
|
const cached = await getCache(cacheKey);
|
|
19108
19330
|
if (cached) {
|
|
@@ -19112,7 +19334,24 @@ function useBuildingRepo() {
|
|
|
19112
19334
|
});
|
|
19113
19335
|
return cached;
|
|
19114
19336
|
}
|
|
19115
|
-
const
|
|
19337
|
+
const pipeline = [{ $match: { ...query } }];
|
|
19338
|
+
if (isVMS) {
|
|
19339
|
+
pipeline.push({
|
|
19340
|
+
$lookup: {
|
|
19341
|
+
from: "building-levels",
|
|
19342
|
+
let: { id: "$_id" },
|
|
19343
|
+
pipeline: [
|
|
19344
|
+
{
|
|
19345
|
+
$match: {
|
|
19346
|
+
$expr: { $eq: ["$block", "$$id"] }
|
|
19347
|
+
}
|
|
19348
|
+
}
|
|
19349
|
+
],
|
|
19350
|
+
as: "building_levels"
|
|
19351
|
+
}
|
|
19352
|
+
});
|
|
19353
|
+
}
|
|
19354
|
+
const result = await collection.aggregate(pipeline).toArray();
|
|
19116
19355
|
setCache(cacheKey, result, 300).then(() => {
|
|
19117
19356
|
logger60.log({
|
|
19118
19357
|
level: "info",
|
|
@@ -19305,6 +19544,9 @@ function useBuildingService() {
|
|
|
19305
19544
|
// src/controllers/building.controller.ts
|
|
19306
19545
|
import { BadRequestError as BadRequestError81, logger as logger62 } from "@7365admin1/node-server-utils";
|
|
19307
19546
|
import Joi44 from "joi";
|
|
19547
|
+
import ExcelJS from "exceljs";
|
|
19548
|
+
import csv from "csv-parser";
|
|
19549
|
+
import fs from "fs";
|
|
19308
19550
|
function useBuildingController() {
|
|
19309
19551
|
const {
|
|
19310
19552
|
getAll: _getAll,
|
|
@@ -19326,7 +19568,10 @@ function useBuildingController() {
|
|
|
19326
19568
|
serial: Joi44.string().optional().allow("", null),
|
|
19327
19569
|
status: Joi44.string().optional().allow("", null),
|
|
19328
19570
|
// buildingFloorPlan: Joi.array().items(Joi.string()).optional(),
|
|
19329
|
-
buildingFiles: Joi44.array().items({
|
|
19571
|
+
buildingFiles: Joi44.array().items({
|
|
19572
|
+
id: Joi44.string().hex().optional().allow("", null),
|
|
19573
|
+
name: Joi44.string().optional().allow("", null)
|
|
19574
|
+
}).optional().allow("", null)
|
|
19330
19575
|
});
|
|
19331
19576
|
const { error } = validation.validate(value);
|
|
19332
19577
|
if (error) {
|
|
@@ -19446,6 +19691,7 @@ function useBuildingController() {
|
|
|
19446
19691
|
async function getBuildingLevel(req, res, next) {
|
|
19447
19692
|
const site = req.params.site ?? "";
|
|
19448
19693
|
let block = req.query.block;
|
|
19694
|
+
const isVMS = req.query.isVMS;
|
|
19449
19695
|
const validation = Joi44.object({
|
|
19450
19696
|
site: Joi44.string().hex().required(),
|
|
19451
19697
|
block: Joi44.number().required()
|
|
@@ -19457,20 +19703,132 @@ function useBuildingController() {
|
|
|
19457
19703
|
}
|
|
19458
19704
|
block = parseInt(block) ?? 0;
|
|
19459
19705
|
try {
|
|
19460
|
-
const siteBlock = await _getBuildingLevel(site, block);
|
|
19706
|
+
const siteBlock = await _getBuildingLevel(site, block, isVMS);
|
|
19461
19707
|
res.json(siteBlock);
|
|
19462
19708
|
return;
|
|
19463
19709
|
} catch (error2) {
|
|
19464
19710
|
next(error2);
|
|
19465
19711
|
}
|
|
19466
19712
|
}
|
|
19713
|
+
async function uploadSpreadsheetBuilding(req, res, next) {
|
|
19714
|
+
try {
|
|
19715
|
+
if (!req.file) {
|
|
19716
|
+
next(new BadRequestError81("Spreadsheet file is required."));
|
|
19717
|
+
return;
|
|
19718
|
+
}
|
|
19719
|
+
const { originalname, path: path4 } = req.file;
|
|
19720
|
+
const lowerName = originalname.toLowerCase();
|
|
19721
|
+
const rowSchema = Joi44.object({
|
|
19722
|
+
block: Joi44.number().integer().min(0).required(),
|
|
19723
|
+
level: Joi44.string().optional().allow(null, ""),
|
|
19724
|
+
unit: Joi44.string().optional().allow(null, ""),
|
|
19725
|
+
category: Joi44.string().trim().required(),
|
|
19726
|
+
name: Joi44.string().trim().optional().allow(null, ""),
|
|
19727
|
+
email: Joi44.string().email().lowercase().optional().allow(null, ""),
|
|
19728
|
+
phoneNumber: Joi44.string().trim().optional().allow(null, "")
|
|
19729
|
+
});
|
|
19730
|
+
const querySchema = Joi44.object({
|
|
19731
|
+
site: Joi44.string().hex().length(24).required(),
|
|
19732
|
+
org: Joi44.string().hex().length(24).optional().allow(null, "")
|
|
19733
|
+
});
|
|
19734
|
+
const { error: queryError, value: queryValue } = querySchema.validate(
|
|
19735
|
+
req.query,
|
|
19736
|
+
{ abortEarly: false, convert: true }
|
|
19737
|
+
);
|
|
19738
|
+
if (queryError) {
|
|
19739
|
+
next(
|
|
19740
|
+
new BadRequestError81(
|
|
19741
|
+
queryError.details.map((d) => d.message).join(", ")
|
|
19742
|
+
)
|
|
19743
|
+
);
|
|
19744
|
+
return;
|
|
19745
|
+
}
|
|
19746
|
+
const { site, org } = queryValue;
|
|
19747
|
+
let rows = [];
|
|
19748
|
+
if (lowerName.endsWith(".xlsx") || lowerName.endsWith(".xls")) {
|
|
19749
|
+
const workbook = new ExcelJS.Workbook();
|
|
19750
|
+
await workbook.xlsx.readFile(path4);
|
|
19751
|
+
const worksheet = workbook.worksheets[0];
|
|
19752
|
+
if (!worksheet) {
|
|
19753
|
+
next(
|
|
19754
|
+
new BadRequestError81("No worksheet found in uploaded Excel file.")
|
|
19755
|
+
);
|
|
19756
|
+
return;
|
|
19757
|
+
}
|
|
19758
|
+
const headerRow = worksheet.getRow(1);
|
|
19759
|
+
const headers = (headerRow.values || []).slice(1).map(
|
|
19760
|
+
(header) => String(header ?? "").trim().replace(/\(.*\)/, "").trim()
|
|
19761
|
+
);
|
|
19762
|
+
worksheet.eachRow((row, rowNumber) => {
|
|
19763
|
+
if (rowNumber === 1)
|
|
19764
|
+
return;
|
|
19765
|
+
const rowData = {};
|
|
19766
|
+
headers.forEach((header, index) => {
|
|
19767
|
+
rowData[header] = row.getCell(index + 1).value ?? "";
|
|
19768
|
+
});
|
|
19769
|
+
if (Object.values(rowData).some(
|
|
19770
|
+
(v) => v !== "" && v !== null && v !== void 0
|
|
19771
|
+
)) {
|
|
19772
|
+
rows.push(rowData);
|
|
19773
|
+
}
|
|
19774
|
+
});
|
|
19775
|
+
} else if (lowerName.endsWith(".csv")) {
|
|
19776
|
+
rows = await new Promise((resolve, reject) => {
|
|
19777
|
+
const parsed = [];
|
|
19778
|
+
fs.createReadStream(path4).pipe(csv()).on("headers", (headers) => {
|
|
19779
|
+
headers = headers.map(
|
|
19780
|
+
(h) => h.trim().replace(/\(.*\)/, "").trim()
|
|
19781
|
+
);
|
|
19782
|
+
}).on("data", (row) => parsed.push(row)).on("end", () => resolve(parsed)).on("error", reject);
|
|
19783
|
+
});
|
|
19784
|
+
} else {
|
|
19785
|
+
next(
|
|
19786
|
+
new BadRequestError81("Only .xlsx, .xls, or .csv files are allowed.")
|
|
19787
|
+
);
|
|
19788
|
+
return;
|
|
19789
|
+
}
|
|
19790
|
+
const validRows = [];
|
|
19791
|
+
const invalidRows = [];
|
|
19792
|
+
rows.forEach((row, index) => {
|
|
19793
|
+
const { error, value } = rowSchema.validate(row, {
|
|
19794
|
+
abortEarly: false,
|
|
19795
|
+
stripUnknown: true
|
|
19796
|
+
});
|
|
19797
|
+
if (error) {
|
|
19798
|
+
invalidRows.push({
|
|
19799
|
+
row: index + 2,
|
|
19800
|
+
data: row,
|
|
19801
|
+
errors: error.details.map((d) => d.message)
|
|
19802
|
+
});
|
|
19803
|
+
} else {
|
|
19804
|
+
validRows.push(value);
|
|
19805
|
+
}
|
|
19806
|
+
});
|
|
19807
|
+
res.status(200).json({
|
|
19808
|
+
message: "Spreadsheet import completed (buildings).",
|
|
19809
|
+
fileName: originalname,
|
|
19810
|
+
totalRows: rows.length,
|
|
19811
|
+
validRows: validRows.length,
|
|
19812
|
+
invalidRows: invalidRows.length,
|
|
19813
|
+
validationErrors: invalidRows,
|
|
19814
|
+
data: validRows
|
|
19815
|
+
// testing: return the validated building rows directly
|
|
19816
|
+
});
|
|
19817
|
+
fs.unlink(path4, () => {
|
|
19818
|
+
});
|
|
19819
|
+
} catch (error) {
|
|
19820
|
+
logger62.log({ level: "error", message: error.message });
|
|
19821
|
+
next(error);
|
|
19822
|
+
}
|
|
19823
|
+
}
|
|
19467
19824
|
return {
|
|
19468
19825
|
createBuilding,
|
|
19469
19826
|
getAll,
|
|
19470
19827
|
getById,
|
|
19471
19828
|
updateById,
|
|
19472
19829
|
deleteById,
|
|
19473
|
-
getBuildingLevel
|
|
19830
|
+
getBuildingLevel,
|
|
19831
|
+
uploadSpreadsheetBuilding
|
|
19474
19832
|
};
|
|
19475
19833
|
}
|
|
19476
19834
|
|
|
@@ -19791,9 +20149,9 @@ function useBuildingUnitController() {
|
|
|
19791
20149
|
// src/controllers/vehicle.controller.ts
|
|
19792
20150
|
import { BadRequestError as BadRequestError84, logger as logger65 } from "@7365admin1/node-server-utils";
|
|
19793
20151
|
import Joi46 from "joi";
|
|
19794
|
-
import
|
|
19795
|
-
import
|
|
19796
|
-
import
|
|
20152
|
+
import ExcelJS2 from "exceljs";
|
|
20153
|
+
import csv2 from "csv-parser";
|
|
20154
|
+
import fs2 from "fs";
|
|
19797
20155
|
function useVehicleController() {
|
|
19798
20156
|
const {
|
|
19799
20157
|
add: _add,
|
|
@@ -19913,7 +20271,7 @@ function useVehicleController() {
|
|
|
19913
20271
|
const { site, org } = queryValue;
|
|
19914
20272
|
let rows = [];
|
|
19915
20273
|
if (lowerName.endsWith(".xlsx") || lowerName.endsWith(".xls")) {
|
|
19916
|
-
const workbook = new
|
|
20274
|
+
const workbook = new ExcelJS2.Workbook();
|
|
19917
20275
|
await workbook.xlsx.readFile(path4);
|
|
19918
20276
|
const worksheet = workbook.worksheets[0];
|
|
19919
20277
|
if (!worksheet) {
|
|
@@ -19942,7 +20300,7 @@ function useVehicleController() {
|
|
|
19942
20300
|
} else if (lowerName.endsWith(".csv")) {
|
|
19943
20301
|
rows = await new Promise((resolve, reject) => {
|
|
19944
20302
|
const parsed = [];
|
|
19945
|
-
|
|
20303
|
+
fs2.createReadStream(path4).pipe(csv2()).on("headers", (headers) => {
|
|
19946
20304
|
headers = headers.map(
|
|
19947
20305
|
(h) => h.trim().replace(/\(.*\)/, "").trim()
|
|
19948
20306
|
);
|
|
@@ -19985,7 +20343,7 @@ function useVehicleController() {
|
|
|
19985
20343
|
validationErrors: invalidRows,
|
|
19986
20344
|
data
|
|
19987
20345
|
});
|
|
19988
|
-
|
|
20346
|
+
fs2.unlink(path4, () => {
|
|
19989
20347
|
});
|
|
19990
20348
|
} catch (error) {
|
|
19991
20349
|
logger65.log({ level: "error", message: error.message });
|
|
@@ -20382,8 +20740,9 @@ function useSiteCameraService() {
|
|
|
20382
20740
|
try {
|
|
20383
20741
|
session.startTransaction();
|
|
20384
20742
|
await _add(value, session);
|
|
20743
|
+
const message = value.type === "ip" ? "CCTV(s) added successfully." : "ANPR(s) added successfully.";
|
|
20385
20744
|
await session.commitTransaction();
|
|
20386
|
-
return
|
|
20745
|
+
return message;
|
|
20387
20746
|
} catch (error2) {
|
|
20388
20747
|
await session.abortTransaction();
|
|
20389
20748
|
throw error2;
|
|
@@ -33716,7 +34075,7 @@ import {
|
|
|
33716
34075
|
import { ObjectId as ObjectId90 } from "mongodb";
|
|
33717
34076
|
|
|
33718
34077
|
// src/utils/access-management.ts
|
|
33719
|
-
import
|
|
34078
|
+
import fs3 from "fs";
|
|
33720
34079
|
import path2 from "path";
|
|
33721
34080
|
import axios from "axios";
|
|
33722
34081
|
import { parseStringPromise } from "xml2js";
|
|
@@ -33746,7 +34105,7 @@ var minifyXml = (xml) => {
|
|
|
33746
34105
|
return xml.replace(/>\s+</g, "><").replace(/\n/g, "").replace(/\r/g, "").replace(/\t/g, "").trim();
|
|
33747
34106
|
};
|
|
33748
34107
|
var readTemplate = (name, params) => {
|
|
33749
|
-
const template =
|
|
34108
|
+
const template = fs3.readFileSync(
|
|
33750
34109
|
path2.join(__dirname, `../dist/public/xml-templates/${name}.xml`),
|
|
33751
34110
|
"utf-8"
|
|
33752
34111
|
);
|
|
@@ -33877,14 +34236,14 @@ import { parseStringPromise as parseStringPromise2 } from "xml2js";
|
|
|
33877
34236
|
|
|
33878
34237
|
// src/utils/rsa-encryption.ts
|
|
33879
34238
|
import * as crypto2 from "crypto";
|
|
33880
|
-
import
|
|
34239
|
+
import fs4 from "fs";
|
|
33881
34240
|
import path3 from "path";
|
|
33882
34241
|
var pub = path3.resolve(process.cwd(), "./src/public/rsa-keys/new_rsa_512_pub.pem");
|
|
33883
34242
|
var priv = path3.resolve(process.cwd(), "./src/public/rsa-keys/new_rsa_512_priv.pem");
|
|
33884
34243
|
var EncryptionCredentials = class {
|
|
33885
34244
|
};
|
|
33886
|
-
EncryptionCredentials.RAW_PUBLIC_KEY =
|
|
33887
|
-
EncryptionCredentials.RAW_PRIVATE_KEY =
|
|
34245
|
+
EncryptionCredentials.RAW_PUBLIC_KEY = fs4.readFileSync(pub, "utf8");
|
|
34246
|
+
EncryptionCredentials.RAW_PRIVATE_KEY = fs4.readFileSync(priv, "utf8");
|
|
33888
34247
|
var EntrypassRSAEncryption = class extends EncryptionCredentials {
|
|
33889
34248
|
static hexToCardNumber(hex) {
|
|
33890
34249
|
if (!/^[0-9A-Fa-f]{8}$/.test(hex)) {
|