@7365admin1/core 3.48.1-staging.161 → 3.48.1-staging.163
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/.changeset/nfc-patrol-log-site-scope.md +56 -0
- package/.changeset/occurrence-subject-site-scope.md +58 -0
- package/dist/index.d.ts +70 -0
- package/dist/index.js +30 -2
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +30 -2
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/test/e2e/harness.mjs +33 -0
- package/test/e2e/nfc-patrol-log-scope.e2e.test.mjs +484 -0
- package/test/e2e/occurrence-subject-scope.e2e.test.mjs +414 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
---
|
|
2
|
+
"@7365admin1/core": minor
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
Scope the NFC patrol log routes to the estate the round was walked at, and take
|
|
6
|
+
the author from the session.
|
|
7
|
+
|
|
8
|
+
An NFC patrol log is the proof that a round was actually walked: the route, the
|
|
9
|
+
day, each checkpoint the guard tapped a tag against, the ones that were skipped
|
|
10
|
+
and why, and the signature that closes the round off. It is the document a
|
|
11
|
+
client is shown when they ask whether their estate was patrolled.
|
|
12
|
+
|
|
13
|
+
Every route on `/api/nfc-patrol-log` carried `requireAuth` and nothing more, and
|
|
14
|
+
`nfc-patrol-log.controller.ts` held no authorization identifier of any kind.
|
|
15
|
+
`requireAuth` proves a session exists; it makes no organisation or site
|
|
16
|
+
decision. So being signed in anywhere on the platform was enough to read another
|
|
17
|
+
client's patrol history, **mark a checkpoint on another estate's round as
|
|
18
|
+
completed**, or **sign that round off as done** — falsifying the patrol proof,
|
|
19
|
+
not merely reading it.
|
|
20
|
+
|
|
21
|
+
All five handlers now decide before any work is done. The route count was
|
|
22
|
+
verified at the registration site — `nfc-patrol-log.route.ts` mounts **five**
|
|
23
|
+
routes, not the four the earlier inventory recorded.
|
|
24
|
+
|
|
25
|
+
- `POST /` and `GET /` already REQUIRE a site (`schemaCreateNfcPatrolLog` and
|
|
26
|
+
the query schema both have `site` required), so the guard checks a site the
|
|
27
|
+
request already names and no caller's contract changes. The repository does
|
|
28
|
+
genuinely apply that `site` — it is the first term of the aggregation match,
|
|
29
|
+
not a parameter it accepts and ignores.
|
|
30
|
+
- `PATCH /:id/checkpoint`, `GET /:id` and `PATCH /:id/sign` name only a record,
|
|
31
|
+
so the site is read off the STORED log through `requireNfcPatrolLogReach`.
|
|
32
|
+
`getById` on this repository is NOT cached, so no separate raw reader was
|
|
33
|
+
needed here, unlike the occurrence mounts.
|
|
34
|
+
- `POST /`'s `createdBy` came from the request body and was never compared to
|
|
35
|
+
the session. It comes from `callerId(req)` now. The field is still accepted,
|
|
36
|
+
so no caller starts to 400; it is simply ignored.
|
|
37
|
+
|
|
38
|
+
None of the three record routes can MOVE a log: `updateCheckpoint` sets only the
|
|
39
|
+
checkpoint fields and `sign` sets only the signature fields, so there is no
|
|
40
|
+
destination to check.
|
|
41
|
+
|
|
42
|
+
The rule is the existing one, `requireSiteReach` -> `entitleSite`. The
|
|
43
|
+
`customer.sites` branch is load-bearing here more than on any other mount: the
|
|
44
|
+
people who walk these rounds are the contracted security agency's guards and
|
|
45
|
+
hold no membership in the estate's own organisation.
|
|
46
|
+
|
|
47
|
+
`GET /:id` previously answered **200 with `null`** for an id matching nothing,
|
|
48
|
+
because the handler passed the repository's `null` straight to `res.json`. It
|
|
49
|
+
now refuses — a log that does not exist has no site, and `requireSiteReach`
|
|
50
|
+
refuses a missing site.
|
|
51
|
+
|
|
52
|
+
Sibling mounts on the same family — `/nfc-patrol-tag`, `/nfc-patrol-settings`,
|
|
53
|
+
`/nfc-patrol-route` and `/patrol-routes` — are NOT touched here. The first three
|
|
54
|
+
carry `/public` routes behind `requireSessionOrLegacyToken`, which is inert
|
|
55
|
+
while `ENFORCE_NFC_PUBLIC_AUTH` is off, so guarding their shared handlers would
|
|
56
|
+
refuse a live legacy screen. That needs the SEC-028 decision first.
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
---
|
|
2
|
+
"@7365admin1/core": minor
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
Scope the occurrence subject routes to the estate the subject belongs to, and
|
|
6
|
+
take the author from the session.
|
|
7
|
+
|
|
8
|
+
An occurrence subject is one of the named headings the guards pick from when
|
|
9
|
+
they write a line into an estate's daily security log — "Lift breakdown",
|
|
10
|
+
"Trespasser", "Fire alarm". The list belongs to one site. Every route on
|
|
11
|
+
`/api/occurrence-subjects` carried `requireAuth` and nothing more, and
|
|
12
|
+
`occurrence-subject.controller.ts` held no authorization identifier of any kind.
|
|
13
|
+
`requireAuth` proves a session exists; it makes no organisation or site
|
|
14
|
+
decision. So being signed in anywhere on the platform was enough to read another
|
|
15
|
+
client's subject list, add a heading to it, rename one, or delete one out from
|
|
16
|
+
under the guards who are picking from it.
|
|
17
|
+
|
|
18
|
+
Two defects, not one:
|
|
19
|
+
|
|
20
|
+
1. **No scope.** All five handlers now decide before any work is done. The route
|
|
21
|
+
count was verified at the registration site — `occurrence-subject.route.ts`
|
|
22
|
+
mounts **five** routes, not the three the earlier inventory recorded.
|
|
23
|
+
2. **Identity from the caller's body.** `POST /` took `addedBy` straight out of
|
|
24
|
+
the request body and never compared it to the session, so a heading could be
|
|
25
|
+
filed under somebody else's name. It comes from `callerId(req)` now. The
|
|
26
|
+
shipped mobile client's own comment records the old behaviour — "`addedBy` is
|
|
27
|
+
sent because the model stores it; the server does not take it from the
|
|
28
|
+
session". Same defect already fixed on `/api/attendances`,
|
|
29
|
+
`/api/site-entrypass-settings` and `/api/feedbacks`.
|
|
30
|
+
|
|
31
|
+
- `POST /` and `GET /` already REQUIRE a site (`schemaOccurrenceSubject` and the
|
|
32
|
+
query schema both have `site` required), so the guard checks a site the
|
|
33
|
+
request already names and no caller's contract changes.
|
|
34
|
+
- `GET /id/:id`, `PUT /id/:id` and `DELETE /id/:id` name only a record, so the
|
|
35
|
+
site is read off the STORED row through the new
|
|
36
|
+
`requireOccurrenceSubjectReach`, built on a new raw reader
|
|
37
|
+
`getRawOccurrenceSubjectById` (the existing `getOccurrenceSubjectById` is
|
|
38
|
+
CACHED, so a guard built on it would decide reach from a cache entry).
|
|
39
|
+
|
|
40
|
+
`GET /`'s `site` is genuinely applied by the repository — it is part of the
|
|
41
|
+
`baseQuery` and of the cache key — so the list needed a reach check on the site
|
|
42
|
+
it names, not a narrowing pass after the read.
|
|
43
|
+
|
|
44
|
+
The rule is the existing one, `requireSiteReach` -> `entitleSite`. The
|
|
45
|
+
`customer.sites` branch is load-bearing: the guards who maintain this list are
|
|
46
|
+
the contracted security agency's staff and hold no membership in the estate's
|
|
47
|
+
own organisation.
|
|
48
|
+
|
|
49
|
+
Each guard sits AFTER the handler's own Joi validation, so a malformed id still
|
|
50
|
+
answers 400 exactly as before. A row that does not exist has no site, and
|
|
51
|
+
`requireSiteReach` refuses a missing site, so an id matching nothing is refused
|
|
52
|
+
rather than falling through.
|
|
53
|
+
|
|
54
|
+
`PUT /id/:id` cannot MOVE a subject — `schemaUpdateOccurrenceSubject` accepts
|
|
55
|
+
only `_id` and `subject` — so there is no destination to check.
|
|
56
|
+
|
|
57
|
+
This closes the occurrence-book family: `/occurrence-books`,
|
|
58
|
+
`/occurrence-entries` and `/occurrence-subjects`.
|
package/dist/index.d.ts
CHANGED
|
@@ -7765,6 +7765,7 @@ declare function useOccurrenceSubjectRepo(): {
|
|
|
7765
7765
|
pageRange: string;
|
|
7766
7766
|
} | TOccurrenceSubject>;
|
|
7767
7767
|
getOccurrenceSubjectById: (_id: string | ObjectId, session?: ClientSession) => Promise<mongodb.WithId<bson.Document> | TOccurrenceSubject>;
|
|
7768
|
+
getRawOccurrenceSubjectById: (_id: string | ObjectId) => Promise<mongodb.WithId<bson.Document> | null>;
|
|
7768
7769
|
updateOccurrenceSubjectById: (_id: ObjectId | string, value: Partial<TOccurrenceSubject>, session?: ClientSession) => Promise<mongodb.UpdateResult<bson.Document>>;
|
|
7769
7770
|
deleteOccurrenceSubjectById: (_id: string | ObjectId) => Promise<number>;
|
|
7770
7771
|
createIndexes: () => Promise<void>;
|
|
@@ -7776,6 +7777,41 @@ declare function useOccurrenceSubjectService(): {
|
|
|
7776
7777
|
updateOccurrenceSubjectById: (id: string | ObjectId, value: Partial<TOccurrenceSubject>) => Promise<string>;
|
|
7777
7778
|
};
|
|
7778
7779
|
|
|
7780
|
+
/**
|
|
7781
|
+
* Who may see, or change, the list of subjects an estate logs against.
|
|
7782
|
+
*
|
|
7783
|
+
* An occurrence subject is one of the named headings the guards pick from when
|
|
7784
|
+
* they write a line into the estate's daily security log - "Lift breakdown",
|
|
7785
|
+
* "Trespasser", "Fire alarm". The list belongs to one site. Every route on
|
|
7786
|
+
* `/api/occurrence-subjects` carried `requireAuth` and nothing more, and this
|
|
7787
|
+
* controller held no authorization identifier of any kind, so being signed in
|
|
7788
|
+
* anywhere on the platform was enough to read another client's subject list,
|
|
7789
|
+
* add a heading to it, rename one, or delete one out from under the guards
|
|
7790
|
+
* who are picking from it.
|
|
7791
|
+
*
|
|
7792
|
+
* Two defects, not one:
|
|
7793
|
+
*
|
|
7794
|
+
* 1. **No scope.** The estate is the scope, and the rule is the existing
|
|
7795
|
+
* `requireSiteReach` -> `entitleSite` that `/api/occurrence-books`,
|
|
7796
|
+
* `/api/occurrence-entries`, `/api/work-orders`, `/api/feedbacks` and the
|
|
7797
|
+
* rest already use. The `customer.sites` branch is load-bearing: the guards
|
|
7798
|
+
* who maintain this list are the contracted security agency's staff and
|
|
7799
|
+
* hold no membership in the estate's own organisation.
|
|
7800
|
+
* 2. **Identity from the caller's body.** `POST /` took `addedBy` straight out
|
|
7801
|
+
* of the request body and never compared it to the session, so a subject
|
|
7802
|
+
* could be filed under somebody else's name. It comes from `callerId(req)`
|
|
7803
|
+
* now. This is the same defect already fixed on `/api/attendances`,
|
|
7804
|
+
* `/api/site-entrypass-settings` and `/api/feedbacks`.
|
|
7805
|
+
*
|
|
7806
|
+
* - `POST /` and `GET /` already REQUIRE a site (`schemaOccurrenceSubject` and
|
|
7807
|
+
* the query schema both have `site` required), so the guard checks a site
|
|
7808
|
+
* the request already names and no caller's contract changes.
|
|
7809
|
+
* - `GET /id/:id`, `PUT /id/:id` and `DELETE /id/:id` name only a record, so
|
|
7810
|
+
* the site is read off the STORED row via `requireOccurrenceSubjectReach`.
|
|
7811
|
+
*
|
|
7812
|
+
* `PUT /id/:id` cannot MOVE a subject: `schemaUpdateOccurrenceSubject` accepts
|
|
7813
|
+
* only `_id` and `subject`, so there is no destination to check.
|
|
7814
|
+
*/
|
|
7779
7815
|
declare function useOccurrenceSubjectController(): {
|
|
7780
7816
|
add: (req: Request, res: Response, next: NextFunction) => Promise<void>;
|
|
7781
7817
|
getAll: (req: Request, res: Response, next: NextFunction) => Promise<void>;
|
|
@@ -7880,6 +7916,40 @@ declare function useNfcPatrolLogService(): {
|
|
|
7880
7916
|
sign: (id: string, value: TSignNfcPatrolLog) => Promise<string>;
|
|
7881
7917
|
};
|
|
7882
7918
|
|
|
7919
|
+
/**
|
|
7920
|
+
* Who may see, or change, one estate's guard-patrol record.
|
|
7921
|
+
*
|
|
7922
|
+
* An NFC patrol log is the proof that a round was actually walked: the route,
|
|
7923
|
+
* the day, each checkpoint the guard tapped a tag against, the ones that were
|
|
7924
|
+
* skipped and why, and the signature that closes the round off. It is the
|
|
7925
|
+
* document a client is shown when they ask whether their estate was patrolled.
|
|
7926
|
+
*
|
|
7927
|
+
* Every route on `/api/nfc-patrol-log` carried `requireAuth` and nothing more,
|
|
7928
|
+
* and this controller held no authorization identifier of any kind.
|
|
7929
|
+
* `requireAuth` proves a session exists; it makes no organisation or site
|
|
7930
|
+
* decision. So being signed in anywhere on the platform was enough to read
|
|
7931
|
+
* another client's patrol record, mark a checkpoint on it as completed or
|
|
7932
|
+
* skipped, or sign a round off as done - which is falsifying the estate's
|
|
7933
|
+
* patrol proof, not merely reading it.
|
|
7934
|
+
*
|
|
7935
|
+
* The estate is the scope, and the rule is the existing `requireSiteReach` ->
|
|
7936
|
+
* `entitleSite` that `/api/occurrence-books`, `/api/occurrence-entries`,
|
|
7937
|
+
* `/api/occurrence-subjects`, `/api/work-orders`, `/api/feedbacks` and the rest
|
|
7938
|
+
* already use. The `customer.sites` branch is load-bearing here more than
|
|
7939
|
+
* anywhere: the people who walk these rounds are the contracted security
|
|
7940
|
+
* agency's guards and hold no membership in the estate's own organisation.
|
|
7941
|
+
*
|
|
7942
|
+
* - `POST /` and `GET /` already REQUIRE a site (`schemaCreateNfcPatrolLog`
|
|
7943
|
+
* and the query schema both have `site` required), so the guard checks a
|
|
7944
|
+
* site the request already names and no caller's contract changes.
|
|
7945
|
+
* - `PATCH /:id/checkpoint`, `GET /:id` and `PATCH /:id/sign` name only a
|
|
7946
|
+
* record, so the site is read off the STORED log via
|
|
7947
|
+
* `requireNfcPatrolLogReach`.
|
|
7948
|
+
*
|
|
7949
|
+
* None of the three record routes can MOVE a log: `updateCheckpoint` sets only
|
|
7950
|
+
* the checkpoint fields and `sign` sets only the signature fields, so there is
|
|
7951
|
+
* no destination to check.
|
|
7952
|
+
*/
|
|
7883
7953
|
declare function useNfcPatrolLogController(): {
|
|
7884
7954
|
add: (req: Request, res: Response, next: NextFunction) => Promise<void>;
|
|
7885
7955
|
getAllBySite: (req: Request, res: Response, next: NextFunction) => Promise<void | Response<any, Record<string, any>>>;
|
package/dist/index.js
CHANGED
|
@@ -71298,6 +71298,14 @@ function useOccurrenceSubjectRepo() {
|
|
|
71298
71298
|
throw error;
|
|
71299
71299
|
}
|
|
71300
71300
|
}
|
|
71301
|
+
async function getRawOccurrenceSubjectById(_id) {
|
|
71302
|
+
try {
|
|
71303
|
+
_id = new import_mongodb137.ObjectId(_id);
|
|
71304
|
+
} catch (error) {
|
|
71305
|
+
throw new import_node_server_utils218.BadRequestError("Invalid occurrence subject ID format.");
|
|
71306
|
+
}
|
|
71307
|
+
return await collection.findOne({ _id });
|
|
71308
|
+
}
|
|
71301
71309
|
async function updateOccurrenceSubjectById(_id, value, session) {
|
|
71302
71310
|
value.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
71303
71311
|
try {
|
|
@@ -71362,6 +71370,7 @@ function useOccurrenceSubjectRepo() {
|
|
|
71362
71370
|
add,
|
|
71363
71371
|
getAll,
|
|
71364
71372
|
getOccurrenceSubjectById,
|
|
71373
|
+
getRawOccurrenceSubjectById,
|
|
71365
71374
|
updateOccurrenceSubjectById,
|
|
71366
71375
|
deleteOccurrenceSubjectById,
|
|
71367
71376
|
createIndexes,
|
|
@@ -72268,8 +72277,13 @@ function useOccurrenceSubjectController() {
|
|
|
72268
72277
|
const {
|
|
72269
72278
|
getAll: _getAll,
|
|
72270
72279
|
getOccurrenceSubjectById: _getOccurrenceSubjectById,
|
|
72280
|
+
getRawOccurrenceSubjectById: _getRawOccurrenceSubjectById,
|
|
72271
72281
|
deleteOccurrenceSubjectById: _deleteOccurrenceSubjectById
|
|
72272
72282
|
} = useOccurrenceSubjectRepo();
|
|
72283
|
+
async function requireOccurrenceSubjectReach(req, _id) {
|
|
72284
|
+
const row = await _getRawOccurrenceSubjectById(_id);
|
|
72285
|
+
await requireSiteReach(req, row?.site);
|
|
72286
|
+
}
|
|
72273
72287
|
async function add(req, res, next) {
|
|
72274
72288
|
try {
|
|
72275
72289
|
const { error, value } = schemaOccurrenceSubject.validate(req.body, {
|
|
@@ -72281,7 +72295,8 @@ function useOccurrenceSubjectController() {
|
|
|
72281
72295
|
next(new import_node_server_utils224.BadRequestError(messages));
|
|
72282
72296
|
return;
|
|
72283
72297
|
}
|
|
72284
|
-
|
|
72298
|
+
await requireSiteReach(req, value.site);
|
|
72299
|
+
const data = await _add({ ...value, addedBy: callerId(req) });
|
|
72285
72300
|
res.status(201).json(data);
|
|
72286
72301
|
return;
|
|
72287
72302
|
} catch (error) {
|
|
@@ -72310,6 +72325,7 @@ function useOccurrenceSubjectController() {
|
|
|
72310
72325
|
return;
|
|
72311
72326
|
}
|
|
72312
72327
|
const { search, page, limit, site, sort, order } = value;
|
|
72328
|
+
await requireSiteReach(req, site);
|
|
72313
72329
|
const sortObj = {
|
|
72314
72330
|
[sort ? sort : "_id" /* ID */]: order === "asc" /* ASC */ ? 1 : -1
|
|
72315
72331
|
};
|
|
@@ -72340,6 +72356,7 @@ function useOccurrenceSubjectController() {
|
|
|
72340
72356
|
return;
|
|
72341
72357
|
}
|
|
72342
72358
|
const { _id } = value;
|
|
72359
|
+
await requireOccurrenceSubjectReach(req, _id);
|
|
72343
72360
|
const data = await _getOccurrenceSubjectById(_id);
|
|
72344
72361
|
res.status(200).json(data);
|
|
72345
72362
|
return;
|
|
@@ -72364,6 +72381,7 @@ function useOccurrenceSubjectController() {
|
|
|
72364
72381
|
return;
|
|
72365
72382
|
}
|
|
72366
72383
|
const { _id, ...rest } = value;
|
|
72384
|
+
await requireOccurrenceSubjectReach(req, _id);
|
|
72367
72385
|
const result = await _updateOccurrenceSubjectById(_id, rest);
|
|
72368
72386
|
res.status(200).json({ message: result });
|
|
72369
72387
|
return;
|
|
@@ -72383,6 +72401,7 @@ function useOccurrenceSubjectController() {
|
|
|
72383
72401
|
return;
|
|
72384
72402
|
}
|
|
72385
72403
|
try {
|
|
72404
|
+
await requireOccurrenceSubjectReach(req, _id);
|
|
72386
72405
|
await _deleteOccurrenceSubjectById(_id);
|
|
72387
72406
|
res.status(200).json({ message: "Successfully deleted occurrence subject." });
|
|
72388
72407
|
return;
|
|
@@ -72769,6 +72788,10 @@ var import_joi119 = __toESM(require("joi"));
|
|
|
72769
72788
|
function useNfcPatrolLogController() {
|
|
72770
72789
|
const { add: _add, completeCheckpoint: _completeCheckpoint, sign: _sign } = useNfcPatrolLogService();
|
|
72771
72790
|
const { getAllBySite: _getAllBySite, getById: _getById } = useNfcPatrolLogRepo();
|
|
72791
|
+
async function requireNfcPatrolLogReach(req, id) {
|
|
72792
|
+
const log = await _getById(id);
|
|
72793
|
+
await requireSiteReach(req, log?.site);
|
|
72794
|
+
}
|
|
72772
72795
|
async function add(req, res, next) {
|
|
72773
72796
|
try {
|
|
72774
72797
|
const payload = req.body;
|
|
@@ -72776,7 +72799,8 @@ function useNfcPatrolLogController() {
|
|
|
72776
72799
|
if (error) {
|
|
72777
72800
|
return next(new import_node_server_utils227.BadRequestError(error.message));
|
|
72778
72801
|
}
|
|
72779
|
-
|
|
72802
|
+
await requireSiteReach(req, value.site);
|
|
72803
|
+
const data = await _add({ ...value, createdBy: callerId(req) });
|
|
72780
72804
|
res.status(201).json(data);
|
|
72781
72805
|
return;
|
|
72782
72806
|
} catch (error) {
|
|
@@ -72825,6 +72849,7 @@ function useNfcPatrolLogController() {
|
|
|
72825
72849
|
);
|
|
72826
72850
|
}
|
|
72827
72851
|
try {
|
|
72852
|
+
await requireSiteReach(req, value.site);
|
|
72828
72853
|
const data = await _getAllBySite({
|
|
72829
72854
|
page: value.page,
|
|
72830
72855
|
limit: value.limit,
|
|
@@ -72847,6 +72872,7 @@ function useNfcPatrolLogController() {
|
|
|
72847
72872
|
skippedRemarks: req.body.skippedRemarks
|
|
72848
72873
|
};
|
|
72849
72874
|
try {
|
|
72875
|
+
await requireNfcPatrolLogReach(req, id);
|
|
72850
72876
|
const data = await _completeCheckpoint(id, payload);
|
|
72851
72877
|
res.status(200).json(data);
|
|
72852
72878
|
return;
|
|
@@ -72857,6 +72883,7 @@ function useNfcPatrolLogController() {
|
|
|
72857
72883
|
}
|
|
72858
72884
|
async function getLog(req, res, next) {
|
|
72859
72885
|
try {
|
|
72886
|
+
await requireNfcPatrolLogReach(req, req.params.id);
|
|
72860
72887
|
const log = await _getById(req.params.id);
|
|
72861
72888
|
res.json(log);
|
|
72862
72889
|
} catch (error) {
|
|
@@ -72866,6 +72893,7 @@ function useNfcPatrolLogController() {
|
|
|
72866
72893
|
async function sign(req, res, next) {
|
|
72867
72894
|
try {
|
|
72868
72895
|
const value = await schemaSignNfcPatrolLog.validateAsync(req.body);
|
|
72896
|
+
await requireNfcPatrolLogReach(req, req.params.id);
|
|
72869
72897
|
const result = await _sign(
|
|
72870
72898
|
req.params.id,
|
|
72871
72899
|
value
|