@7365admin1/core 2.51.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 +6 -0
- package/dist/index.d.ts +6 -3
- package/dist/index.js +314 -32
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +314 -32
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -13093,23 +13093,42 @@ var CameraType = /* @__PURE__ */ ((CameraType2) => {
|
|
|
13093
13093
|
})(CameraType || {});
|
|
13094
13094
|
var schemaUpdateSiteCamera = Joi34.object({
|
|
13095
13095
|
host: Joi34.string().required(),
|
|
13096
|
-
username: Joi34.string().
|
|
13097
|
-
|
|
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
|
+
}),
|
|
13098
13106
|
category: Joi34.string().allow("standard", "resident", "visitor").required(),
|
|
13099
13107
|
guardPost: Joi34.number().integer().optional(),
|
|
13100
|
-
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, "")
|
|
13101
13111
|
});
|
|
13102
13112
|
var schemaSiteCamera = Joi34.object({
|
|
13103
13113
|
_id: Joi34.string().hex().optional(),
|
|
13104
13114
|
site: Joi34.string().hex().required(),
|
|
13105
13115
|
host: Joi34.string().required(),
|
|
13106
|
-
username: Joi34.string().
|
|
13107
|
-
|
|
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
|
+
}),
|
|
13108
13126
|
type: Joi34.string().allow("ip", "anpr").required(),
|
|
13109
13127
|
category: Joi34.string().allow("standard", "resident", "visitor").required(),
|
|
13110
13128
|
direction: Joi34.string().required().allow("entry", "exit", "both", "none"),
|
|
13111
13129
|
guardPost: Joi34.number().integer().optional(),
|
|
13112
13130
|
status: Joi34.string().optional().allow("active", "inactive", "deleted"),
|
|
13131
|
+
name: Joi34.string().optional().allow(null, ""),
|
|
13113
13132
|
createdAt: Joi34.date().optional(),
|
|
13114
13133
|
updatedAt: Joi34.date().optional(),
|
|
13115
13134
|
deletedAt: Joi34.date().optional()
|
|
@@ -13138,6 +13157,7 @@ function MSiteCamera(value) {
|
|
|
13138
13157
|
guardPost: value.guardPost ?? 0,
|
|
13139
13158
|
status: value.status ?? "active",
|
|
13140
13159
|
direction: value.direction ?? "",
|
|
13160
|
+
name: value.name ?? "",
|
|
13141
13161
|
createdAt: value.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
13142
13162
|
updatedAt: value.updatedAt ?? "",
|
|
13143
13163
|
deletedAt: value.deletedAt ?? ""
|
|
@@ -13458,7 +13478,24 @@ var schemaUpdateVisTrans = Joi36.object({
|
|
|
13458
13478
|
checkInRemarks: Joi36.string().optional().allow("", null),
|
|
13459
13479
|
checkOutRemarks: Joi36.string().optional().allow("", null),
|
|
13460
13480
|
expectedCheckIn: Joi36.string().isoDate().optional(),
|
|
13461
|
-
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)
|
|
13462
13499
|
});
|
|
13463
13500
|
function MVisitorTransaction(value) {
|
|
13464
13501
|
const { error } = schemaVisitorTransaction.validate(value, {
|
|
@@ -13908,14 +13945,14 @@ function useVisitorTransactionRepo() {
|
|
|
13908
13945
|
);
|
|
13909
13946
|
}
|
|
13910
13947
|
}
|
|
13911
|
-
async function updateById(_id, value, session,
|
|
13948
|
+
async function updateById(_id, value, session, isManualCheckOut = true) {
|
|
13912
13949
|
try {
|
|
13913
13950
|
_id = new ObjectId40(_id);
|
|
13914
13951
|
} catch (error) {
|
|
13915
13952
|
throw new BadRequestError66("Invalid visitor transaction ID format.");
|
|
13916
13953
|
}
|
|
13917
13954
|
value.updatedAt = /* @__PURE__ */ new Date();
|
|
13918
|
-
if (value.checkOut &&
|
|
13955
|
+
if (value.checkOut && isManualCheckOut) {
|
|
13919
13956
|
value.manualCheckout = true;
|
|
13920
13957
|
}
|
|
13921
13958
|
try {
|
|
@@ -17371,7 +17408,7 @@ function useDahuaService() {
|
|
|
17371
17408
|
);
|
|
17372
17409
|
}
|
|
17373
17410
|
}
|
|
17374
|
-
if (["entry"
|
|
17411
|
+
if (["entry"].includes(designation) && plateNumber) {
|
|
17375
17412
|
await closeOpenTransaction(plateNumber);
|
|
17376
17413
|
const vehicle = await getVehicleByPlateNumber(plateNumber);
|
|
17377
17414
|
const visitorTransaction = {
|
|
@@ -17426,11 +17463,11 @@ function useDahuaService() {
|
|
|
17426
17463
|
} catch (error) {
|
|
17427
17464
|
console.log("failed to create visitor transaction", error);
|
|
17428
17465
|
loggerDahua.error(
|
|
17429
|
-
`[${site}][${
|
|
17466
|
+
`[${site}][${host}] Error creating visitor transaction:`,
|
|
17430
17467
|
error
|
|
17431
17468
|
);
|
|
17432
17469
|
}
|
|
17433
|
-
} else if (["exit"
|
|
17470
|
+
} else if (["exit"].includes(designation) && plateNumber) {
|
|
17434
17471
|
const vehicle = await getVehicleByPlateNumber(plateNumber);
|
|
17435
17472
|
const existingOpenTransaction = await getOpenByPlateNumber(
|
|
17436
17473
|
plateNumber,
|
|
@@ -17455,6 +17492,92 @@ function useDahuaService() {
|
|
|
17455
17492
|
if (onDetected2) {
|
|
17456
17493
|
onDetected2({ reload: true, site: existingOpenTransaction?.site?.toString(), host, direction });
|
|
17457
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
|
+
}
|
|
17458
17581
|
}
|
|
17459
17582
|
}
|
|
17460
17583
|
}
|
|
@@ -17586,6 +17709,9 @@ function useDahuaService() {
|
|
|
17586
17709
|
}
|
|
17587
17710
|
}
|
|
17588
17711
|
async function listenToCamera(camera, onDetected) {
|
|
17712
|
+
if (camera?.type != "anpr" && camera?.status != "deleted") {
|
|
17713
|
+
return;
|
|
17714
|
+
}
|
|
17589
17715
|
if (onDetected) {
|
|
17590
17716
|
_savedOnDetected = onDetected;
|
|
17591
17717
|
}
|
|
@@ -17594,6 +17720,14 @@ function useDahuaService() {
|
|
|
17594
17720
|
return;
|
|
17595
17721
|
}
|
|
17596
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
|
+
}
|
|
17597
17731
|
if (cameraRegistry.has(cameraId)) {
|
|
17598
17732
|
loggerDahua.info(`[${camera.host}] Stopping existing connection for update.`);
|
|
17599
17733
|
cameraRegistry.get(cameraId)?.abort();
|
|
@@ -17741,6 +17875,9 @@ function useDahuaService() {
|
|
|
17741
17875
|
continue;
|
|
17742
17876
|
} else if ([400, 401, 403, 500].includes(response.statusCode)) {
|
|
17743
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
|
+
}
|
|
17744
17881
|
return;
|
|
17745
17882
|
}
|
|
17746
17883
|
loggerDahua.info(`[${host}] Successfully connected to ANPR.`);
|
|
@@ -17793,8 +17930,15 @@ function useDahuaService() {
|
|
|
17793
17930
|
break;
|
|
17794
17931
|
}
|
|
17795
17932
|
loggerDahua.error(
|
|
17796
|
-
`[${host}] Connection lost or error: ${error.message || error}. Retrying in
|
|
17933
|
+
`[${host}] Connection lost or error: ${error.message || error}. Retrying in 10 seconds...`
|
|
17797
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
|
+
}
|
|
17798
17942
|
} finally {
|
|
17799
17943
|
if (bufferQueue?.destroy) {
|
|
17800
17944
|
try {
|
|
@@ -17815,7 +17959,7 @@ function useDahuaService() {
|
|
|
17815
17959
|
}
|
|
17816
17960
|
}
|
|
17817
17961
|
if (!signal.aborted) {
|
|
17818
|
-
await new Promise((res) => setTimeout(res,
|
|
17962
|
+
await new Promise((res) => setTimeout(res, 1e4));
|
|
17819
17963
|
}
|
|
17820
17964
|
}
|
|
17821
17965
|
loggerDahua.info(`[${host}] ANPR Listener stopped.`);
|
|
@@ -18317,6 +18461,8 @@ function useSiteCameraRepo() {
|
|
|
18317
18461
|
}
|
|
18318
18462
|
try {
|
|
18319
18463
|
await collection.deleteOne({ _id }, { session });
|
|
18464
|
+
const { listenToCamera } = useDahuaService();
|
|
18465
|
+
await listenToCamera({ _id, status: "deleted" });
|
|
18320
18466
|
delCachedData();
|
|
18321
18467
|
return "Successfully deleted site camera.";
|
|
18322
18468
|
} catch (error) {
|
|
@@ -19168,7 +19314,7 @@ function useBuildingRepo() {
|
|
|
19168
19314
|
}
|
|
19169
19315
|
}
|
|
19170
19316
|
}
|
|
19171
|
-
async function getBuildingLevel(site, block) {
|
|
19317
|
+
async function getBuildingLevel(site, block, isVMS) {
|
|
19172
19318
|
try {
|
|
19173
19319
|
site = new ObjectId49(site);
|
|
19174
19320
|
} catch (error) {
|
|
@@ -19178,8 +19324,7 @@ function useBuildingRepo() {
|
|
|
19178
19324
|
site,
|
|
19179
19325
|
block
|
|
19180
19326
|
};
|
|
19181
|
-
const
|
|
19182
|
-
const cacheKey = makeCacheKey26(buildings_namespace_collection, cacheOptions);
|
|
19327
|
+
const cacheKey = makeCacheKey26(buildings_namespace_collection, { ...query, isVMS });
|
|
19183
19328
|
try {
|
|
19184
19329
|
const cached = await getCache(cacheKey);
|
|
19185
19330
|
if (cached) {
|
|
@@ -19189,7 +19334,24 @@ function useBuildingRepo() {
|
|
|
19189
19334
|
});
|
|
19190
19335
|
return cached;
|
|
19191
19336
|
}
|
|
19192
|
-
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();
|
|
19193
19355
|
setCache(cacheKey, result, 300).then(() => {
|
|
19194
19356
|
logger60.log({
|
|
19195
19357
|
level: "info",
|
|
@@ -19382,6 +19544,9 @@ function useBuildingService() {
|
|
|
19382
19544
|
// src/controllers/building.controller.ts
|
|
19383
19545
|
import { BadRequestError as BadRequestError81, logger as logger62 } from "@7365admin1/node-server-utils";
|
|
19384
19546
|
import Joi44 from "joi";
|
|
19547
|
+
import ExcelJS from "exceljs";
|
|
19548
|
+
import csv from "csv-parser";
|
|
19549
|
+
import fs from "fs";
|
|
19385
19550
|
function useBuildingController() {
|
|
19386
19551
|
const {
|
|
19387
19552
|
getAll: _getAll,
|
|
@@ -19403,7 +19568,10 @@ function useBuildingController() {
|
|
|
19403
19568
|
serial: Joi44.string().optional().allow("", null),
|
|
19404
19569
|
status: Joi44.string().optional().allow("", null),
|
|
19405
19570
|
// buildingFloorPlan: Joi.array().items(Joi.string()).optional(),
|
|
19406
|
-
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)
|
|
19407
19575
|
});
|
|
19408
19576
|
const { error } = validation.validate(value);
|
|
19409
19577
|
if (error) {
|
|
@@ -19523,6 +19691,7 @@ function useBuildingController() {
|
|
|
19523
19691
|
async function getBuildingLevel(req, res, next) {
|
|
19524
19692
|
const site = req.params.site ?? "";
|
|
19525
19693
|
let block = req.query.block;
|
|
19694
|
+
const isVMS = req.query.isVMS;
|
|
19526
19695
|
const validation = Joi44.object({
|
|
19527
19696
|
site: Joi44.string().hex().required(),
|
|
19528
19697
|
block: Joi44.number().required()
|
|
@@ -19534,20 +19703,132 @@ function useBuildingController() {
|
|
|
19534
19703
|
}
|
|
19535
19704
|
block = parseInt(block) ?? 0;
|
|
19536
19705
|
try {
|
|
19537
|
-
const siteBlock = await _getBuildingLevel(site, block);
|
|
19706
|
+
const siteBlock = await _getBuildingLevel(site, block, isVMS);
|
|
19538
19707
|
res.json(siteBlock);
|
|
19539
19708
|
return;
|
|
19540
19709
|
} catch (error2) {
|
|
19541
19710
|
next(error2);
|
|
19542
19711
|
}
|
|
19543
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
|
+
}
|
|
19544
19824
|
return {
|
|
19545
19825
|
createBuilding,
|
|
19546
19826
|
getAll,
|
|
19547
19827
|
getById,
|
|
19548
19828
|
updateById,
|
|
19549
19829
|
deleteById,
|
|
19550
|
-
getBuildingLevel
|
|
19830
|
+
getBuildingLevel,
|
|
19831
|
+
uploadSpreadsheetBuilding
|
|
19551
19832
|
};
|
|
19552
19833
|
}
|
|
19553
19834
|
|
|
@@ -19868,9 +20149,9 @@ function useBuildingUnitController() {
|
|
|
19868
20149
|
// src/controllers/vehicle.controller.ts
|
|
19869
20150
|
import { BadRequestError as BadRequestError84, logger as logger65 } from "@7365admin1/node-server-utils";
|
|
19870
20151
|
import Joi46 from "joi";
|
|
19871
|
-
import
|
|
19872
|
-
import
|
|
19873
|
-
import
|
|
20152
|
+
import ExcelJS2 from "exceljs";
|
|
20153
|
+
import csv2 from "csv-parser";
|
|
20154
|
+
import fs2 from "fs";
|
|
19874
20155
|
function useVehicleController() {
|
|
19875
20156
|
const {
|
|
19876
20157
|
add: _add,
|
|
@@ -19990,7 +20271,7 @@ function useVehicleController() {
|
|
|
19990
20271
|
const { site, org } = queryValue;
|
|
19991
20272
|
let rows = [];
|
|
19992
20273
|
if (lowerName.endsWith(".xlsx") || lowerName.endsWith(".xls")) {
|
|
19993
|
-
const workbook = new
|
|
20274
|
+
const workbook = new ExcelJS2.Workbook();
|
|
19994
20275
|
await workbook.xlsx.readFile(path4);
|
|
19995
20276
|
const worksheet = workbook.worksheets[0];
|
|
19996
20277
|
if (!worksheet) {
|
|
@@ -20019,7 +20300,7 @@ function useVehicleController() {
|
|
|
20019
20300
|
} else if (lowerName.endsWith(".csv")) {
|
|
20020
20301
|
rows = await new Promise((resolve, reject) => {
|
|
20021
20302
|
const parsed = [];
|
|
20022
|
-
|
|
20303
|
+
fs2.createReadStream(path4).pipe(csv2()).on("headers", (headers) => {
|
|
20023
20304
|
headers = headers.map(
|
|
20024
20305
|
(h) => h.trim().replace(/\(.*\)/, "").trim()
|
|
20025
20306
|
);
|
|
@@ -20062,7 +20343,7 @@ function useVehicleController() {
|
|
|
20062
20343
|
validationErrors: invalidRows,
|
|
20063
20344
|
data
|
|
20064
20345
|
});
|
|
20065
|
-
|
|
20346
|
+
fs2.unlink(path4, () => {
|
|
20066
20347
|
});
|
|
20067
20348
|
} catch (error) {
|
|
20068
20349
|
logger65.log({ level: "error", message: error.message });
|
|
@@ -20459,8 +20740,9 @@ function useSiteCameraService() {
|
|
|
20459
20740
|
try {
|
|
20460
20741
|
session.startTransaction();
|
|
20461
20742
|
await _add(value, session);
|
|
20743
|
+
const message = value.type === "ip" ? "CCTV(s) added successfully." : "ANPR(s) added successfully.";
|
|
20462
20744
|
await session.commitTransaction();
|
|
20463
|
-
return
|
|
20745
|
+
return message;
|
|
20464
20746
|
} catch (error2) {
|
|
20465
20747
|
await session.abortTransaction();
|
|
20466
20748
|
throw error2;
|
|
@@ -33793,7 +34075,7 @@ import {
|
|
|
33793
34075
|
import { ObjectId as ObjectId90 } from "mongodb";
|
|
33794
34076
|
|
|
33795
34077
|
// src/utils/access-management.ts
|
|
33796
|
-
import
|
|
34078
|
+
import fs3 from "fs";
|
|
33797
34079
|
import path2 from "path";
|
|
33798
34080
|
import axios from "axios";
|
|
33799
34081
|
import { parseStringPromise } from "xml2js";
|
|
@@ -33823,7 +34105,7 @@ var minifyXml = (xml) => {
|
|
|
33823
34105
|
return xml.replace(/>\s+</g, "><").replace(/\n/g, "").replace(/\r/g, "").replace(/\t/g, "").trim();
|
|
33824
34106
|
};
|
|
33825
34107
|
var readTemplate = (name, params) => {
|
|
33826
|
-
const template =
|
|
34108
|
+
const template = fs3.readFileSync(
|
|
33827
34109
|
path2.join(__dirname, `../dist/public/xml-templates/${name}.xml`),
|
|
33828
34110
|
"utf-8"
|
|
33829
34111
|
);
|
|
@@ -33954,14 +34236,14 @@ import { parseStringPromise as parseStringPromise2 } from "xml2js";
|
|
|
33954
34236
|
|
|
33955
34237
|
// src/utils/rsa-encryption.ts
|
|
33956
34238
|
import * as crypto2 from "crypto";
|
|
33957
|
-
import
|
|
34239
|
+
import fs4 from "fs";
|
|
33958
34240
|
import path3 from "path";
|
|
33959
34241
|
var pub = path3.resolve(process.cwd(), "./src/public/rsa-keys/new_rsa_512_pub.pem");
|
|
33960
34242
|
var priv = path3.resolve(process.cwd(), "./src/public/rsa-keys/new_rsa_512_priv.pem");
|
|
33961
34243
|
var EncryptionCredentials = class {
|
|
33962
34244
|
};
|
|
33963
|
-
EncryptionCredentials.RAW_PUBLIC_KEY =
|
|
33964
|
-
EncryptionCredentials.RAW_PRIVATE_KEY =
|
|
34245
|
+
EncryptionCredentials.RAW_PUBLIC_KEY = fs4.readFileSync(pub, "utf8");
|
|
34246
|
+
EncryptionCredentials.RAW_PRIVATE_KEY = fs4.readFileSync(priv, "utf8");
|
|
33965
34247
|
var EntrypassRSAEncryption = class extends EncryptionCredentials {
|
|
33966
34248
|
static hexToCardNumber(hex) {
|
|
33967
34249
|
if (!/^[0-9A-Fa-f]{8}$/.test(hex)) {
|