@7365admin1/core 3.48.1-staging.155 → 3.48.1-staging.156

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.
@@ -0,0 +1,41 @@
1
+ ---
2
+ "@7365admin1/core": minor
3
+ ---
4
+
5
+ Scope the manpower remarks routes to the site the remarks belong to.
6
+
7
+ A remarks row is the shift-attendance exception record for one site on one day:
8
+ which named guards were late or absent, and whether the estate has acknowledged
9
+ it. Every route on `/api/remarks` carried `requireAuth` and nothing more — the
10
+ controller held no authorization identifier of any kind — so being signed in
11
+ anywhere on the platform was enough to read another client's daily manning
12
+ failures, to write an acknowledgement into their record in a name of the
13
+ caller's choosing, or to switch a row to `inactive` so the estate's own console
14
+ stops listing it.
15
+
16
+ All five routes now go through `requireSiteReach`, the rule
17
+ `/api/manpower-monitoring`, `/api/attendances`, `/api/vehicles`,
18
+ `/api/documents` and `/api/incident-reports` already use:
19
+
20
+ - `GET /:id/:serviceProviderId` names the site in the URL, so it is checked
21
+ before the read.
22
+ - `PUT /:id` and `PATCH /:id` name only a record, so the site is read off the
23
+ **stored** row — a `siteId` in the caller's own body cannot stand in for it.
24
+ - `GET /` names no site at all. Its only filter is a `serviceProviderId` the
25
+ caller supplies, which is not a claim about the caller, and one agency works
26
+ for several clients — so the page came back holding every one of those
27
+ clients' rows. It is narrowed after the read, the way
28
+ `/api/manpower-monitoring`'s own `GET /search` is. The count in the envelope
29
+ stays the unfiltered one, as it does there; both callers read it only for the
30
+ pager.
31
+ - `POST /` writes a row for whatever `siteId` the body carries. It has no caller
32
+ in any web app or mobile app — the rows are written internally by
33
+ `manpower-monitoring.service.ts` when a site's monitoring settings are created
34
+ — and it is guarded all the same rather than left open.
35
+
36
+ `requireSiteReach` matters here because the contracted agency that actually runs
37
+ these shifts holds no membership in the estate's organisation and reaches its
38
+ own site through `customer.sites`.
39
+
40
+ A row the caller cannot reach answers exactly as one that does not exist, so
41
+ site ids cannot be enumerated by watching the difference.
package/dist/index.d.ts CHANGED
@@ -8369,11 +8369,45 @@ declare function useManpowerRemarksRepo(): {
8369
8369
  pageRange: string;
8370
8370
  }>;
8371
8371
  getManpowerRemarksBySiteId: (_id: string | ObjectId, date: string, serviceProviderId?: string | ObjectId) => Promise<mongodb.WithId<bson.Document> | null>;
8372
+ getManpowerRemarksById: (_id: string | ObjectId) => Promise<mongodb.WithId<bson.Document> | null>;
8372
8373
  updateManpowerRemarks: (_id: string | ObjectId, value: TManpowerRemarksUpdate) => Promise<number>;
8373
8374
  updateRemarksStatus: (_id: string | ObjectId, value: TManpowerRemarksStatusUpdate) => Promise<number>;
8374
8375
  createIndexes: () => Promise<string>;
8375
8376
  };
8376
8377
 
8378
+ /**
8379
+ * Who may see, or change, a site's manpower remarks.
8380
+ *
8381
+ * A remarks row is the shift-attendance exception record for one site on one
8382
+ * day: which named guards were late or absent, and whether the estate has
8383
+ * acknowledged it. Every route on this mount carried `requireAuth` and NOTHING
8384
+ * else — this controller held no authorization identifier of any kind — so
8385
+ * being signed in anywhere on the platform was enough to read another client's
8386
+ * daily manning failures, to write an acknowledgement into their record in
8387
+ * somebody else's name, or to switch a row to `inactive` so the estate stops
8388
+ * seeing it.
8389
+ *
8390
+ * The estate is the scope, the same rule `/api/manpower-monitoring`,
8391
+ * `/api/attendances`, `/api/vehicles`, `/api/documents` and
8392
+ * `/api/incident-reports` already use, so the contracted agency that actually
8393
+ * runs these shifts reaches its own sites through `customer.sites` and needs no
8394
+ * membership in the estate's own organisation.
8395
+ *
8396
+ * Three shapes, because the routes ask three different questions:
8397
+ * - `GET /:id/:serviceProviderId` names the site in the URL, so it is checked
8398
+ * BEFORE the read.
8399
+ * - `PUT /:id` and `PATCH /:id` name only a record, so the site is read off
8400
+ * the STORED row (`requireRemarkReach`) and a `siteId` in the body cannot
8401
+ * stand in for it.
8402
+ * - `GET /` names no site at all — its only filter is a `serviceProviderId`
8403
+ * the caller supplies — so the page has to be narrowed AFTER the read, the
8404
+ * way `/api/manpower-monitoring`'s own `GET /search` is.
8405
+ *
8406
+ * `POST /` writes a row for whatever `siteId` the body carries. It has no
8407
+ * caller in any web app or mobile app; the rows are written internally by
8408
+ * `manpower-monitoring.service.ts` when a site's monitoring settings are
8409
+ * created. It is guarded all the same rather than left open.
8410
+ */
8377
8411
  declare function useManpowerRemarkCtrl(): {
8378
8412
  createManpowerRemark: (req: Request, res: Response, next: NextFunction) => Promise<Response<any, Record<string, any>> | undefined>;
8379
8413
  getManpowerRemarksAllSite: (req: Request, res: Response, next: NextFunction) => Promise<Response<any, Record<string, any>> | undefined>;
package/dist/index.js CHANGED
@@ -75335,6 +75335,14 @@ function useManpowerRemarksRepo() {
75335
75335
  throw error;
75336
75336
  }
75337
75337
  }
75338
+ async function getManpowerRemarksById(_id) {
75339
+ try {
75340
+ _id = new import_mongodb146.ObjectId(_id);
75341
+ } catch (error) {
75342
+ throw new import_node_server_utils231.BadRequestError("Invalid ID format.");
75343
+ }
75344
+ return await collection.findOne({ _id });
75345
+ }
75338
75346
  async function updateManpowerRemarks(_id, value) {
75339
75347
  try {
75340
75348
  _id = new import_mongodb146.ObjectId(_id);
@@ -75382,6 +75390,7 @@ function useManpowerRemarksRepo() {
75382
75390
  createManpowerRemarks,
75383
75391
  getManpowerRemarksAllSite,
75384
75392
  getManpowerRemarksBySiteId,
75393
+ getManpowerRemarksById,
75385
75394
  updateManpowerRemarks,
75386
75395
  updateRemarksStatus,
75387
75396
  createIndexes
@@ -77349,8 +77358,13 @@ function useManpowerRemarkCtrl() {
77349
77358
  getManpowerRemarksAllSite: _getManpowerRemarksAllSite,
77350
77359
  updateRemarksStatus: _updateRemarksStatus,
77351
77360
  getManpowerRemarksBySiteId: _getManpowerRemarksBySiteId,
77361
+ getManpowerRemarksById: _getManpowerRemarksById,
77352
77362
  updateManpowerRemarks: _updateManpowerRemarks
77353
77363
  } = useManpowerRemarksRepo();
77364
+ async function requireRemarkReach(req, _id) {
77365
+ const remark = await _getManpowerRemarksById(_id);
77366
+ await requireSiteReach(req, remark?.siteId);
77367
+ }
77354
77368
  async function createManpowerRemark(req, res, next) {
77355
77369
  try {
77356
77370
  const payload = { ...req.body };
@@ -77359,6 +77373,7 @@ function useManpowerRemarkCtrl() {
77359
77373
  next(new import_node_server_utils244.BadRequestError(error.message));
77360
77374
  return;
77361
77375
  }
77376
+ await requireSiteReach(req, payload.siteId);
77362
77377
  const result = await _createManpowerRemarks(payload);
77363
77378
  return res.json(result);
77364
77379
  } catch (error) {
@@ -77398,7 +77413,17 @@ function useManpowerRemarkCtrl() {
77398
77413
  status: status?.toString() || "",
77399
77414
  date: date?.toString() || ""
77400
77415
  });
77401
- return res.json(result);
77416
+ const { only } = await siteReachOf(req);
77417
+ const items = await only(
77418
+ (result?.items ?? []).map((row) => ({
77419
+ row,
77420
+ site: row?.siteId
77421
+ }))
77422
+ );
77423
+ return res.json({
77424
+ ...result,
77425
+ items: items.map((entry) => entry.row)
77426
+ });
77402
77427
  } catch (error) {
77403
77428
  import_node_server_utils244.logger.log({ level: "error", message: error.message });
77404
77429
  next(error);
@@ -77423,6 +77448,7 @@ function useManpowerRemarkCtrl() {
77423
77448
  next(new import_node_server_utils244.BadRequestError(error.message));
77424
77449
  return;
77425
77450
  }
77451
+ await requireSiteReach(req, id);
77426
77452
  const result = await _getManpowerRemarksBySiteId(
77427
77453
  id,
77428
77454
  date,
@@ -77450,6 +77476,7 @@ function useManpowerRemarkCtrl() {
77450
77476
  next(new import_node_server_utils244.BadRequestError(error.message));
77451
77477
  return;
77452
77478
  }
77479
+ await requireRemarkReach(req, _id);
77453
77480
  const result = await _updateManpowerRemarks(_id, payload);
77454
77481
  return res.json(result);
77455
77482
  } catch (error) {
@@ -77471,6 +77498,7 @@ function useManpowerRemarkCtrl() {
77471
77498
  next(new import_node_server_utils244.BadRequestError(error.message));
77472
77499
  return;
77473
77500
  }
77501
+ await requireRemarkReach(req, _id);
77474
77502
  const result = await _updateRemarksStatus(_id, payload);
77475
77503
  return res.json(result);
77476
77504
  } catch (error) {