@smartsoft001/crud-shell-nestjs 2.183.0 → 2.184.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/README.md
CHANGED
|
@@ -36,5 +36,32 @@
|
|
|
36
36
|
|
|
37
37
|
### CrudGateway
|
|
38
38
|
<table>
|
|
39
|
-
<tr><td>changes (WebSocket)</td><td>handleFilter — Subscribes to changes
|
|
39
|
+
<tr><td>changes (WebSocket)</td><td>handleFilter — Subscribes to the changes of one entity, <code>{ id }</code>, and emits <code>{ id, type }</code> per change. Requires a JWT and a <code>changePolicy</code>.</td></tr>
|
|
40
40
|
</table>
|
|
41
|
+
|
|
42
|
+
### Change subscriptions
|
|
43
|
+
|
|
44
|
+
A subscription is refused unless the socket handshake carries a valid JWT in `auth.token`
|
|
45
|
+
(`io(url, { auth: { token } })`), the message names one entity with `{ id }`, and
|
|
46
|
+
`SharedConfig.changePolicy` returns exactly `true`. The policy receives `{ id, user, type }`. It runs
|
|
47
|
+
once on subscribe without `type`, and again before every event, with a fresh token check, with the
|
|
48
|
+
change `type` (`create`, `update` or `delete`). A refusal on an event ends the subscription. Decide
|
|
49
|
+
from server-side ownership or tenant data, never from the id alone.
|
|
50
|
+
|
|
51
|
+
- **Events carry no document fields.** Clients receive `{ id, type }` and refetch the entity through
|
|
52
|
+
an authorized endpoint.
|
|
53
|
+
- **Deletes.** Mongo emits a `delete` after the document is gone, so a policy that loads the
|
|
54
|
+
document would deny it and the client would never learn about the delete. Branch on
|
|
55
|
+
`type === 'delete'` and decide without loading the document.
|
|
56
|
+
- **Cost.** The policy runs per event and per subscriber; keep it cheap or cache it.
|
|
57
|
+
- **Token expiry.** The token from the handshake is rechecked on every event. After it expires the
|
|
58
|
+
next event ends the subscription; the client must reconnect with a fresh `auth.token`.
|
|
59
|
+
- **Core module.** `CrudShellNestjsCoreModule` always registers the gateway, so every subscription
|
|
60
|
+
is refused until `changePolicy` is set.
|
|
61
|
+
|
|
62
|
+
#### Migration
|
|
63
|
+
|
|
64
|
+
Subscriptions used to be open to anyone, accepted an empty filter and carried the full change,
|
|
65
|
+
including the inserted document or the update delta. After upgrading: configure `changePolicy`,
|
|
66
|
+
connect with the JWT in `auth.token` (and reconnect after it expires), always subscribe with an
|
|
67
|
+
`id`, and refetch the entity on each event instead of applying a payload.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@smartsoft001/crud-shell-nestjs",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.184.0",
|
|
4
4
|
"type": "commonjs",
|
|
5
5
|
"main": "./src/index.js",
|
|
6
6
|
"typings": "./src/index.d.ts",
|
|
@@ -34,14 +34,14 @@
|
|
|
34
34
|
"@nestjs/jwt": "^11.0.0",
|
|
35
35
|
"@nestjs/passport": "^11.0.5",
|
|
36
36
|
"@nestjs/websockets": "^11.2.1",
|
|
37
|
-
"@smartsoft001/crud-domain": "2.
|
|
38
|
-
"@smartsoft001/crud-shell-app-services": "2.
|
|
39
|
-
"@smartsoft001/crud-shell-dtos": "2.
|
|
40
|
-
"@smartsoft001/domain-core": "2.
|
|
41
|
-
"@smartsoft001/mongo": "2.
|
|
42
|
-
"@smartsoft001/nestjs": "2.
|
|
43
|
-
"@smartsoft001/users": "2.
|
|
44
|
-
"@smartsoft001/utils": "2.
|
|
37
|
+
"@smartsoft001/crud-domain": "2.184.0",
|
|
38
|
+
"@smartsoft001/crud-shell-app-services": "2.184.0",
|
|
39
|
+
"@smartsoft001/crud-shell-dtos": "2.184.0",
|
|
40
|
+
"@smartsoft001/domain-core": "2.184.0",
|
|
41
|
+
"@smartsoft001/mongo": "2.184.0",
|
|
42
|
+
"@smartsoft001/nestjs": "2.184.0",
|
|
43
|
+
"@smartsoft001/users": "2.184.0",
|
|
44
|
+
"@smartsoft001/utils": "2.184.0",
|
|
45
45
|
"express": "^5.2.1",
|
|
46
46
|
"rxjs": "^7.8.1",
|
|
47
47
|
"socket.io": "^4.8.1"
|
|
@@ -1,16 +1,26 @@
|
|
|
1
|
+
import { JwtService } from '@nestjs/jwt';
|
|
1
2
|
import { OnGatewayConnection, OnGatewayDisconnect, OnGatewayInit, WsResponse } from '@nestjs/websockets';
|
|
2
3
|
import { Observable } from 'rxjs';
|
|
3
4
|
import type { Socket } from 'socket.io';
|
|
4
5
|
import { CrudService } from '@smartsoft001/crud-shell-app-services';
|
|
5
6
|
import { ItemChangedData } from '@smartsoft001/crud-shell-dtos';
|
|
6
7
|
import { IEntity } from '@smartsoft001/domain-core';
|
|
8
|
+
import { SharedConfig } from '@smartsoft001/nestjs';
|
|
7
9
|
export declare class CrudGateway<T extends IEntity<string>> implements OnGatewayInit, OnGatewayConnection, OnGatewayDisconnect {
|
|
8
10
|
private service;
|
|
11
|
+
private config?;
|
|
12
|
+
private jwt?;
|
|
9
13
|
private _clientsSubscriptions;
|
|
10
|
-
constructor(service: CrudService<T
|
|
14
|
+
constructor(service: CrudService<T>, config?: SharedConfig, jwt?: JwtService);
|
|
11
15
|
handleFilter(data: {
|
|
12
16
|
id?: string;
|
|
13
|
-
}, client: Socket): Observable<WsResponse<ItemChangedData
|
|
17
|
+
}, client: Socket): Observable<WsResponse<Pick<ItemChangedData, 'id' | 'type'>>>;
|
|
18
|
+
/**
|
|
19
|
+
* Verifies the token and asks `changePolicy`. `type` is absent when the subscription opens and
|
|
20
|
+
* carries the change type before each event, so a policy can allow a delete without loading
|
|
21
|
+
* a resource that no longer exists.
|
|
22
|
+
*/
|
|
23
|
+
private authorize;
|
|
14
24
|
afterInit(server: any): void;
|
|
15
25
|
handleDisconnect(client: any): void;
|
|
16
26
|
private clearSubscription;
|
|
@@ -2,24 +2,75 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.CrudGateway = void 0;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
|
+
const common_1 = require("@nestjs/common");
|
|
6
|
+
const jwt_1 = require("@nestjs/jwt");
|
|
5
7
|
const websockets_1 = require("@nestjs/websockets");
|
|
6
8
|
const rxjs_1 = require("rxjs");
|
|
7
9
|
const crud_shell_app_services_1 = require("@smartsoft001/crud-shell-app-services");
|
|
10
|
+
const nestjs_1 = require("@smartsoft001/nestjs");
|
|
8
11
|
let CrudGateway = class CrudGateway {
|
|
9
12
|
service;
|
|
13
|
+
config;
|
|
14
|
+
jwt;
|
|
10
15
|
_clientsSubscriptions = new Map();
|
|
11
|
-
constructor(service) {
|
|
16
|
+
constructor(service, config, jwt) {
|
|
12
17
|
this.service = service;
|
|
18
|
+
this.config = config;
|
|
19
|
+
this.jwt = jwt;
|
|
13
20
|
}
|
|
14
21
|
handleFilter(data, client) {
|
|
15
22
|
const event = 'changes';
|
|
16
23
|
return new rxjs_1.Observable((observer) => {
|
|
17
24
|
this.clearSubscription(client);
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
25
|
+
const subscription = (0, rxjs_1.from)(this.authorize(data, client))
|
|
26
|
+
.pipe((0, rxjs_1.switchMap)(() => this.service.changes({ id: data.id })), (0, rxjs_1.filter)((change) => change.id === data.id), (0, rxjs_1.concatMap)((change) => (0, rxjs_1.from)(this.authorize(data, client, change.type)).pipe((0, rxjs_1.map)(() => ({
|
|
27
|
+
event,
|
|
28
|
+
data: { id: change.id, type: change.type },
|
|
29
|
+
})))))
|
|
30
|
+
.subscribe(observer);
|
|
31
|
+
this._clientsSubscriptions.set(client.id, subscription);
|
|
32
|
+
return () => {
|
|
33
|
+
subscription.unsubscribe();
|
|
34
|
+
if (this._clientsSubscriptions.get(client.id) === subscription) {
|
|
35
|
+
this._clientsSubscriptions.delete(client.id);
|
|
36
|
+
}
|
|
37
|
+
};
|
|
21
38
|
});
|
|
22
39
|
}
|
|
40
|
+
/**
|
|
41
|
+
* Verifies the token and asks `changePolicy`. `type` is absent when the subscription opens and
|
|
42
|
+
* carries the change type before each event, so a policy can allow a delete without loading
|
|
43
|
+
* a resource that no longer exists.
|
|
44
|
+
*/
|
|
45
|
+
async authorize(data, client, type) {
|
|
46
|
+
try {
|
|
47
|
+
const token = client.handshake?.auth?.['token'];
|
|
48
|
+
if (!data ||
|
|
49
|
+
typeof data.id !== 'string' ||
|
|
50
|
+
!data.id ||
|
|
51
|
+
data.id.length > 128 ||
|
|
52
|
+
!this.jwt ||
|
|
53
|
+
!this.config?.changePolicy ||
|
|
54
|
+
typeof token !== 'string' ||
|
|
55
|
+
token.length > 8192) {
|
|
56
|
+
throw new Error('Denied');
|
|
57
|
+
}
|
|
58
|
+
const payload = await this.jwt.verifyAsync(token);
|
|
59
|
+
if (typeof payload.sub !== 'string' || !payload.sub)
|
|
60
|
+
throw new Error('Denied');
|
|
61
|
+
const user = {
|
|
62
|
+
username: payload.sub,
|
|
63
|
+
permissions: Array.isArray(payload.permissions)
|
|
64
|
+
? payload.permissions.filter((p) => typeof p === 'string')
|
|
65
|
+
: [],
|
|
66
|
+
};
|
|
67
|
+
if ((await this.config.changePolicy({ id: data.id, user, type })) !== true)
|
|
68
|
+
throw new Error('Denied');
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
throw new websockets_1.WsException('Change subscription denied');
|
|
72
|
+
}
|
|
73
|
+
}
|
|
23
74
|
afterInit(server) {
|
|
24
75
|
this._clientsSubscriptions = new Map();
|
|
25
76
|
console.log('CrudGateway Init');
|
|
@@ -54,6 +105,10 @@ exports.CrudGateway = CrudGateway = tslib_1.__decorate([
|
|
|
54
105
|
path: '/' + process.env.URL_PREFIX + '/_socket',
|
|
55
106
|
namespace: '/' + process.env.URL_PREFIX,
|
|
56
107
|
}),
|
|
57
|
-
tslib_1.
|
|
108
|
+
tslib_1.__param(1, (0, common_1.Optional)()),
|
|
109
|
+
tslib_1.__param(2, (0, common_1.Optional)()),
|
|
110
|
+
tslib_1.__metadata("design:paramtypes", [crud_shell_app_services_1.CrudService,
|
|
111
|
+
nestjs_1.SharedConfig,
|
|
112
|
+
jwt_1.JwtService])
|
|
58
113
|
], CrudGateway);
|
|
59
114
|
//# sourceMappingURL=crud.gateway.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"crud.gateway.js","sourceRoot":"","sources":["../../../../../../../../../packages/crud/shell/nestjs/src/lib/gateways/crud/crud.gateway.ts"],"names":[],"mappings":";;;;AAAA,
|
|
1
|
+
{"version":3,"file":"crud.gateway.js","sourceRoot":"","sources":["../../../../../../../../../packages/crud/shell/nestjs/src/lib/gateways/crud/crud.gateway.ts"],"names":[],"mappings":"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"}
|
package/src/lib/nestjs.module.js
CHANGED
|
@@ -27,7 +27,8 @@ let CrudShellNestjsModule = CrudShellNestjsModule_1 = class CrudShellNestjsModul
|
|
|
27
27
|
auth_guard_1.AuthJwtGuard,
|
|
28
28
|
],
|
|
29
29
|
imports: [
|
|
30
|
-
...(options.restApi &&
|
|
30
|
+
...((options.restApi || options.socket) &&
|
|
31
|
+
tokenConfig?.secretOrPrivateKey
|
|
31
32
|
? [
|
|
32
33
|
passport_1.PassportModule.register({
|
|
33
34
|
defaultStrategy: 'jwt',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"nestjs.module.js","sourceRoot":"","sources":["../../../../../../../packages/crud/shell/nestjs/src/lib/nestjs.module.ts"],"names":[],"mappings":";;;;;AAAA,2CAAuD;AACvD,qCAAwC;AACxC,+CAAkD;AAElD,mFAAiE;AACjE,+CAAkD;AAClD,iDAAkE;AAElE,+CAA4C;AAC5C,yCAAsC;AACtC,yDAAwD;AAGjD,IAAM,qBAAqB,6BAA3B,MAAM,qBAAqB;IAChC,MAAM,CAAC,OAAO,CACZ,OAaC;QAED,6EAA6E;QAC7E,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC;QACxC,2EAA2E;QAC3E,MAAM,MAAM,GAAG,EAAE,GAAG,OAAO,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,IAAI,OAAO,CAAC,EAAE,EAAE,IAAI,EAAE,CAAC;QAEtE,OAAO;YACL,MAAM,EAAE,uBAAqB;YAC7B,WAAW,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,yBAAW,CAAC,CAAC,CAAC,EAAE;YAC/C,SAAS,EAAE;gBACT,GAAG,kCAAQ;gBACX,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,mBAAQ,CAAC,CAAC,CAAC,EAAE,CAAC;gBACnC,yBAAY;aACb;YACD,OAAO,EAAE;gBACP,GAAG,CAAC,OAAO,CAAC,OAAO,IAAI,WAAW,EAAE,kBAAkB;
|
|
1
|
+
{"version":3,"file":"nestjs.module.js","sourceRoot":"","sources":["../../../../../../../packages/crud/shell/nestjs/src/lib/nestjs.module.ts"],"names":[],"mappings":";;;;;AAAA,2CAAuD;AACvD,qCAAwC;AACxC,+CAAkD;AAElD,mFAAiE;AACjE,+CAAkD;AAClD,iDAAkE;AAElE,+CAA4C;AAC5C,yCAAsC;AACtC,yDAAwD;AAGjD,IAAM,qBAAqB,6BAA3B,MAAM,qBAAqB;IAChC,MAAM,CAAC,OAAO,CACZ,OAaC;QAED,6EAA6E;QAC7E,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC;QACxC,2EAA2E;QAC3E,MAAM,MAAM,GAAG,EAAE,GAAG,OAAO,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,IAAI,OAAO,CAAC,EAAE,EAAE,IAAI,EAAE,CAAC;QAEtE,OAAO;YACL,MAAM,EAAE,uBAAqB;YAC7B,WAAW,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,yBAAW,CAAC,CAAC,CAAC,EAAE;YAC/C,SAAS,EAAE;gBACT,GAAG,kCAAQ;gBACX,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,mBAAQ,CAAC,CAAC,CAAC,EAAE,CAAC;gBACnC,yBAAY;aACb;YACD,OAAO,EAAE;gBACP,GAAG,CAAC,CAAC,OAAO,CAAC,OAAO,IAAI,OAAO,CAAC,MAAM,CAAC;oBACvC,WAAW,EAAE,kBAAkB;oBAC7B,CAAC,CAAC;wBACE,yBAAc,CAAC,QAAQ,CAAC;4BACtB,eAAe,EAAE,KAAK;4BACtB,OAAO,EAAE,KAAK;yBACf,CAAC;wBACF,eAAS,CAAC,QAAQ,CAAC;4BACjB,MAAM,EAAE,WAAW,CAAC,kBAAkB;4BACtC,WAAW,EAAE;gCACX,SAAS,EAAE,WAAW,CAAC,SAAS;6BACjC;yBACF,CAAC;qBACH;oBACH,CAAC,CAAC,EAAE,CAAC;gBACP,qBAAY,CAAC,UAAU,CAAC,MAAM,CAAC;gBAC/B,mBAAW,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;aAChC;YACD,OAAO,EAAE,CAAC,GAAG,kCAAQ,EAAE,yBAAY,EAAE,mBAAW,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;SACtE,CAAC;IACJ,CAAC;CACF,CAAA;AApDY,sDAAqB;gCAArB,qBAAqB;IADjC,IAAA,eAAM,EAAC,EAAE,CAAC;GACE,qBAAqB,CAoDjC;AAGM,IAAM,yBAAyB,iCAA/B,MAAM,yBAAyB;IACpC;;;OAGG;IACH,MAAM,CAAC,OAAO,CACZ,OAWC;QAED,MAAM,MAAM,GAAG,EAAE,GAAG,OAAO,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,IAAI,OAAO,CAAC,EAAE,EAAE,IAAI,EAAE,CAAC;QAEtE,OAAO;YACL,MAAM,EAAE,2BAAyB;YACjC,SAAS,EAAE,CAAC,GAAG,kCAAQ,EAAE,GAAG,mBAAQ,EAAE,yBAAY,CAAC;YACnD,OAAO,EAAE;gBACP,yBAAc,CAAC,QAAQ,CAAC,EAAE,eAAe,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;gBACnE,eAAS,CAAC,QAAQ,CAAC;oBACjB,MAAM,EAAE,OAAO,CAAC,WAAW,CAAC,kBAAkB;oBAC9C,WAAW,EAAE;wBACX,SAAS,EAAE,OAAO,CAAC,WAAW,CAAC,SAAS;qBACzC;iBACF,CAAC;gBACF,qBAAY,CAAC,OAAO,CAAC,MAAM,CAAC;gBAC5B,mBAAW,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;aAChC;YACD,OAAO,EAAE,EAAE;SACZ,CAAC;IACJ,CAAC;CACF,CAAA;AAtCY,8DAAyB;oCAAzB,yBAAyB;IADrC,IAAA,eAAM,EAAC,EAAE,CAAC;GACE,yBAAyB,CAsCrC"}
|