polfan-server-js-client 0.1.23 → 0.1.25
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/build/index.js +1020 -421
- package/build/index.js.map +1 -1
- package/build/types/AbstractChatClient.d.ts +11 -9
- package/build/types/Permission.d.ts +15 -0
- package/build/types/WebSocketChatClient.d.ts +1 -1
- package/build/types/index.d.ts +2 -1
- package/build/types/state-tracker/ChatStateTracker.d.ts +25 -0
- package/build/types/state-tracker/DeferredTask.d.ts +5 -0
- package/build/types/state-tracker/PermissionsManager.d.ts +22 -0
- package/build/types/state-tracker/RoomsManager.d.ts +59 -0
- package/build/types/state-tracker/SpacesManager.d.ts +47 -0
- package/index.html +6 -0
- package/package.json +65 -65
- package/src/AbstractChatClient.ts +20 -15
- package/src/Permission.ts +15 -0
- package/src/WebSocketChatClient.ts +1 -1
- package/src/index.ts +2 -0
- package/src/state-tracker/ChatStateTracker.ts +38 -0
- package/src/state-tracker/DeferredTask.ts +8 -0
- package/src/state-tracker/PermissionsManager.ts +205 -0
- package/src/state-tracker/RoomsManager.ts +222 -0
- package/src/state-tracker/SpacesManager.ts +201 -0
- package/build/types/ChatStateTracker.d.ts +0 -56
- package/src/ChatStateTracker.ts +0 -346
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { ObservableIndexedObjectCollection } from "../IndexedObjectCollection";
|
|
2
|
+
import { Message, Room, RoomMember, SpaceMember, Topic } from "pserv-ts-types";
|
|
3
|
+
import { ChatStateTracker } from "./ChatStateTracker";
|
|
4
|
+
export declare class RoomsManager {
|
|
5
|
+
private tracker;
|
|
6
|
+
private readonly list;
|
|
7
|
+
private readonly topics;
|
|
8
|
+
private readonly topicsMessages;
|
|
9
|
+
private readonly members;
|
|
10
|
+
private readonly deferredSession;
|
|
11
|
+
constructor(tracker: ChatStateTracker);
|
|
12
|
+
/**
|
|
13
|
+
* Get collection of room members.
|
|
14
|
+
*/
|
|
15
|
+
getMembers(roomId: string): Promise<ObservableIndexedObjectCollection<RoomMember> | null>;
|
|
16
|
+
/**
|
|
17
|
+
* Get a room member representing the current user.
|
|
18
|
+
*/
|
|
19
|
+
getMe(roomId: string): Promise<RoomMember | null>;
|
|
20
|
+
/**
|
|
21
|
+
* Get collection of all the rooms you are in.
|
|
22
|
+
*/
|
|
23
|
+
get(): Promise<ObservableIndexedObjectCollection<Room>>;
|
|
24
|
+
/**
|
|
25
|
+
* Get collection of room topics.
|
|
26
|
+
*/
|
|
27
|
+
getTopics(roomId: string): Promise<ObservableIndexedObjectCollection<Topic> | null>;
|
|
28
|
+
/**
|
|
29
|
+
* Get collection of the messages written in topic.
|
|
30
|
+
*/
|
|
31
|
+
getMessages(topicId: string): Promise<ObservableIndexedObjectCollection<Message> | null>;
|
|
32
|
+
/**
|
|
33
|
+
* For internal use. If you want to leave the room, execute a proper command on client object.
|
|
34
|
+
* @internal
|
|
35
|
+
*/
|
|
36
|
+
_delete(...roomIds: string[]): void;
|
|
37
|
+
/**
|
|
38
|
+
* For internal use. If you want to leave the room, execute a proper command on client object.
|
|
39
|
+
* @internal
|
|
40
|
+
*/
|
|
41
|
+
_deleteBySpaceId(spaceId: string): void;
|
|
42
|
+
/**
|
|
43
|
+
* For internal use.
|
|
44
|
+
* @internal
|
|
45
|
+
*/
|
|
46
|
+
_handleSpaceMemberUpdate(spaceId: string, member: SpaceMember): void;
|
|
47
|
+
private handleRoomMemberUpdated;
|
|
48
|
+
private handleTopicDeleted;
|
|
49
|
+
private handleNewMessage;
|
|
50
|
+
private handleNewTopic;
|
|
51
|
+
private addJoinedRoomTopics;
|
|
52
|
+
private handleRoomJoined;
|
|
53
|
+
private addJoinedRooms;
|
|
54
|
+
private handleRoomLeft;
|
|
55
|
+
private handleRoomMemberJoined;
|
|
56
|
+
private handleRoomMemberLeft;
|
|
57
|
+
private handleRoomMembers;
|
|
58
|
+
private handleSession;
|
|
59
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { ChatStateTracker } from "./ChatStateTracker";
|
|
2
|
+
import { ObservableIndexedObjectCollection } from "../IndexedObjectCollection";
|
|
3
|
+
import { Role, RoomSummary, Space, SpaceMember } from "pserv-ts-types";
|
|
4
|
+
export declare class SpacesManager {
|
|
5
|
+
private tracker;
|
|
6
|
+
private readonly list;
|
|
7
|
+
private readonly roles;
|
|
8
|
+
private readonly rooms;
|
|
9
|
+
private readonly members;
|
|
10
|
+
private readonly deferredSession;
|
|
11
|
+
constructor(tracker: ChatStateTracker);
|
|
12
|
+
/**
|
|
13
|
+
* Get collection of all the spaces you are in.
|
|
14
|
+
*/
|
|
15
|
+
get(): Promise<ObservableIndexedObjectCollection<Space>>;
|
|
16
|
+
/**
|
|
17
|
+
* Get collection of space roles.
|
|
18
|
+
*/
|
|
19
|
+
getRoles(spaceId: string): Promise<ObservableIndexedObjectCollection<Role> | null>;
|
|
20
|
+
/**
|
|
21
|
+
* Get collection of the all available rooms inside given space.
|
|
22
|
+
*/
|
|
23
|
+
getRooms(spaceId: string): Promise<ObservableIndexedObjectCollection<RoomSummary> | null>;
|
|
24
|
+
/**
|
|
25
|
+
* Get collection of space members.
|
|
26
|
+
*/
|
|
27
|
+
getMembers(spaceId: string): Promise<ObservableIndexedObjectCollection<SpaceMember> | null>;
|
|
28
|
+
/**
|
|
29
|
+
* Get a space member representing the current user.
|
|
30
|
+
*/
|
|
31
|
+
getMe(spaceId: string): Promise<SpaceMember | null>;
|
|
32
|
+
private handleNewRole;
|
|
33
|
+
private handleNewRoom;
|
|
34
|
+
private handleRoomDeleted;
|
|
35
|
+
private handleRoleDeleted;
|
|
36
|
+
private handleSpaceDeleted;
|
|
37
|
+
private handleSpaceJoined;
|
|
38
|
+
private addJoinedSpaces;
|
|
39
|
+
private handleSpaceLeft;
|
|
40
|
+
private handleSpaceMemberJoined;
|
|
41
|
+
private handleSpaceMemberLeft;
|
|
42
|
+
private handleSpaceMembers;
|
|
43
|
+
private handleSpaceRooms;
|
|
44
|
+
private handleSpaceMemberUpdated;
|
|
45
|
+
private handleRoleUpdated;
|
|
46
|
+
private handleSession;
|
|
47
|
+
}
|
package/index.html
ADDED
package/package.json
CHANGED
|
@@ -1,65 +1,65 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "polfan-server-js-client",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "JavaScript client library for handling communication with Polfan chat server.",
|
|
5
|
-
"author": "Jarosław Żak",
|
|
6
|
-
"license": "MIT",
|
|
7
|
-
"main": "build/index.js",
|
|
8
|
-
"types": "build/types/index.d.ts",
|
|
9
|
-
"scripts": {
|
|
10
|
-
"start": "webpack serve --config webpack.config.demo.js",
|
|
11
|
-
"build": "webpack && tsc",
|
|
12
|
-
"build:demo": "webpack --config webpack.config.demo.js",
|
|
13
|
-
"test": "jest --silent",
|
|
14
|
-
"coverage": "npm run test -- --coverage",
|
|
15
|
-
"prepare": "npm run build",
|
|
16
|
-
"trypublish": "npm publish || true"
|
|
17
|
-
},
|
|
18
|
-
"devDependencies": {
|
|
19
|
-
"@babel/cli": "^7.20.7",
|
|
20
|
-
"@babel/core": "^7.20.12",
|
|
21
|
-
"@babel/plugin-proposal-class-properties": "^7.16.0",
|
|
22
|
-
"@babel/plugin-transform-typescript": "^7.20.2",
|
|
23
|
-
"@babel/polyfill": "^7.12.1",
|
|
24
|
-
"@babel/preset-env": "^7.20.2",
|
|
25
|
-
"@types/jest": "^29.2.5",
|
|
26
|
-
"@typescript-eslint/eslint-plugin": "^4.33.0",
|
|
27
|
-
"@typescript-eslint/parser": "^4.33.0",
|
|
28
|
-
"babel-eslint": "^10.1.0",
|
|
29
|
-
"babel-loader": "^9.1.2",
|
|
30
|
-
"babel-preset-minify": "^0.5.2",
|
|
31
|
-
"css-loader": "^6.7.3",
|
|
32
|
-
"css-minimizer-webpack-plugin": "^4.2.2",
|
|
33
|
-
"eslint": "^7.32.0",
|
|
34
|
-
"file-loader": "^6.2.0",
|
|
35
|
-
"html-webpack-plugin": "^5.5.0",
|
|
36
|
-
"jest": "^29.3.1",
|
|
37
|
-
"mini-css-extract-plugin": "^2.7.2",
|
|
38
|
-
"style-loader": "^3.3.1",
|
|
39
|
-
"terser-webpack-plugin": "^5.3.5",
|
|
40
|
-
"typescript": "^4.9.4",
|
|
41
|
-
"url-loader": "^4.1.1",
|
|
42
|
-
"webpack": "^5.75.0",
|
|
43
|
-
"webpack-cli": "^5.0.1",
|
|
44
|
-
"webpack-dev-server": "4.11.1",
|
|
45
|
-
"pserv-ts-types": "^0.0.
|
|
46
|
-
},
|
|
47
|
-
"jest": {
|
|
48
|
-
"moduleNameMapper": {
|
|
49
|
-
"\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/scripts/testMock.js",
|
|
50
|
-
"\\.(css|less)$": "<rootDir>/scripts/testMock.js"
|
|
51
|
-
},
|
|
52
|
-
"moduleFileExtensions": [
|
|
53
|
-
"web.js",
|
|
54
|
-
"js",
|
|
55
|
-
"web.ts",
|
|
56
|
-
"ts",
|
|
57
|
-
"web.tsx",
|
|
58
|
-
"tsx",
|
|
59
|
-
"json",
|
|
60
|
-
"web.jsx",
|
|
61
|
-
"jsx",
|
|
62
|
-
"node"
|
|
63
|
-
]
|
|
64
|
-
}
|
|
65
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "polfan-server-js-client",
|
|
3
|
+
"version": "0.1.25",
|
|
4
|
+
"description": "JavaScript client library for handling communication with Polfan chat server.",
|
|
5
|
+
"author": "Jarosław Żak",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"main": "build/index.js",
|
|
8
|
+
"types": "build/types/index.d.ts",
|
|
9
|
+
"scripts": {
|
|
10
|
+
"start": "webpack serve --config webpack.config.demo.js",
|
|
11
|
+
"build": "webpack && tsc",
|
|
12
|
+
"build:demo": "webpack --config webpack.config.demo.js",
|
|
13
|
+
"test": "jest --silent",
|
|
14
|
+
"coverage": "npm run test -- --coverage",
|
|
15
|
+
"prepare": "npm run build",
|
|
16
|
+
"trypublish": "npm publish || true"
|
|
17
|
+
},
|
|
18
|
+
"devDependencies": {
|
|
19
|
+
"@babel/cli": "^7.20.7",
|
|
20
|
+
"@babel/core": "^7.20.12",
|
|
21
|
+
"@babel/plugin-proposal-class-properties": "^7.16.0",
|
|
22
|
+
"@babel/plugin-transform-typescript": "^7.20.2",
|
|
23
|
+
"@babel/polyfill": "^7.12.1",
|
|
24
|
+
"@babel/preset-env": "^7.20.2",
|
|
25
|
+
"@types/jest": "^29.2.5",
|
|
26
|
+
"@typescript-eslint/eslint-plugin": "^4.33.0",
|
|
27
|
+
"@typescript-eslint/parser": "^4.33.0",
|
|
28
|
+
"babel-eslint": "^10.1.0",
|
|
29
|
+
"babel-loader": "^9.1.2",
|
|
30
|
+
"babel-preset-minify": "^0.5.2",
|
|
31
|
+
"css-loader": "^6.7.3",
|
|
32
|
+
"css-minimizer-webpack-plugin": "^4.2.2",
|
|
33
|
+
"eslint": "^7.32.0",
|
|
34
|
+
"file-loader": "^6.2.0",
|
|
35
|
+
"html-webpack-plugin": "^5.5.0",
|
|
36
|
+
"jest": "^29.3.1",
|
|
37
|
+
"mini-css-extract-plugin": "^2.7.2",
|
|
38
|
+
"style-loader": "^3.3.1",
|
|
39
|
+
"terser-webpack-plugin": "^5.3.5",
|
|
40
|
+
"typescript": "^4.9.4",
|
|
41
|
+
"url-loader": "^4.1.1",
|
|
42
|
+
"webpack": "^5.75.0",
|
|
43
|
+
"webpack-cli": "^5.0.1",
|
|
44
|
+
"webpack-dev-server": "4.11.1",
|
|
45
|
+
"pserv-ts-types": "^0.0.21"
|
|
46
|
+
},
|
|
47
|
+
"jest": {
|
|
48
|
+
"moduleNameMapper": {
|
|
49
|
+
"\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/scripts/testMock.js",
|
|
50
|
+
"\\.(css|less)$": "<rootDir>/scripts/testMock.js"
|
|
51
|
+
},
|
|
52
|
+
"moduleFileExtensions": [
|
|
53
|
+
"web.js",
|
|
54
|
+
"js",
|
|
55
|
+
"web.ts",
|
|
56
|
+
"ts",
|
|
57
|
+
"web.tsx",
|
|
58
|
+
"tsx",
|
|
59
|
+
"json",
|
|
60
|
+
"web.jsx",
|
|
61
|
+
"jsx",
|
|
62
|
+
"node"
|
|
63
|
+
]
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -2,14 +2,13 @@ import {
|
|
|
2
2
|
Bye,
|
|
3
3
|
GetSession,
|
|
4
4
|
JoinSpace,
|
|
5
|
-
Ok,
|
|
6
5
|
Session,
|
|
7
6
|
SpaceJoined,
|
|
8
7
|
Error as ErrorType,
|
|
9
8
|
SpaceLeft,
|
|
10
9
|
SpaceMemberJoined,
|
|
11
10
|
SpaceMemberLeft,
|
|
12
|
-
|
|
11
|
+
SpaceMemberUpdated,
|
|
13
12
|
SpaceDeleted,
|
|
14
13
|
SpaceMembers,
|
|
15
14
|
SpaceRooms,
|
|
@@ -24,8 +23,7 @@ import {
|
|
|
24
23
|
NewTopic,
|
|
25
24
|
TopicDeleted,
|
|
26
25
|
NewMessage,
|
|
27
|
-
|
|
28
|
-
SetUserPermissions,
|
|
26
|
+
GetPermissionOverwrites,
|
|
29
27
|
GetComputedPermissions,
|
|
30
28
|
LeaveSpace,
|
|
31
29
|
CreateSpace,
|
|
@@ -36,8 +34,7 @@ import {
|
|
|
36
34
|
DeleteRole,
|
|
37
35
|
AssignRole,
|
|
38
36
|
DeassignRole,
|
|
39
|
-
|
|
40
|
-
GetRolePermissions,
|
|
37
|
+
SetPermissionOverwrites,
|
|
41
38
|
JoinRoom,
|
|
42
39
|
LeaveRoom,
|
|
43
40
|
CreateRoom,
|
|
@@ -45,7 +42,13 @@ import {
|
|
|
45
42
|
GetRoomMembers,
|
|
46
43
|
CreateTopic,
|
|
47
44
|
DeleteTopic,
|
|
48
|
-
CreateMessage,
|
|
45
|
+
CreateMessage,
|
|
46
|
+
Envelope,
|
|
47
|
+
PermissionOverwrites,
|
|
48
|
+
PermissionOverwritesChanged,
|
|
49
|
+
RoomMemberUpdated,
|
|
50
|
+
UpdateRole,
|
|
51
|
+
RoleUpdated,
|
|
49
52
|
} from "pserv-ts-types";
|
|
50
53
|
import {EventTarget} from "./EventTarget";
|
|
51
54
|
|
|
@@ -110,26 +113,29 @@ export type CommandResult<ResultT> = {data?: ResultT, error?: Error};
|
|
|
110
113
|
export type EventsMap = {
|
|
111
114
|
// General Events
|
|
112
115
|
Bye: Bye,
|
|
113
|
-
Ok: Ok,
|
|
114
116
|
Error: ErrorType,
|
|
115
117
|
Session: Session,
|
|
116
118
|
Permissions: Permissions,
|
|
119
|
+
PermissionOverwrites: PermissionOverwrites,
|
|
120
|
+
PermissionOverwritesChanged: PermissionOverwritesChanged,
|
|
117
121
|
// Space events
|
|
118
122
|
SpaceJoined: SpaceJoined,
|
|
119
123
|
SpaceLeft: SpaceLeft,
|
|
120
124
|
SpaceMemberJoined: SpaceMemberJoined,
|
|
121
125
|
SpaceMemberLeft: SpaceMemberLeft,
|
|
122
|
-
|
|
126
|
+
SpaceMemberUpdated: SpaceMemberUpdated,
|
|
123
127
|
SpaceDeleted: SpaceDeleted,
|
|
124
128
|
SpaceMembers: SpaceMembers,
|
|
125
129
|
SpaceRooms: SpaceRooms,
|
|
126
130
|
NewRole: NewRole,
|
|
127
131
|
RoleDeleted: RoomDeleted,
|
|
132
|
+
RoleUpdated: RoleUpdated,
|
|
128
133
|
// Room events
|
|
129
134
|
RoomJoined: RoomJoined,
|
|
130
135
|
RoomLeft: RoomLeft,
|
|
131
136
|
RoomMemberJoined: RoomMemberJoined,
|
|
132
137
|
RoomMemberLeft: RoomMemberLeft,
|
|
138
|
+
RoomMemberUpdated: RoomMemberUpdated,
|
|
133
139
|
RoomMembers: RoomMembers,
|
|
134
140
|
NewRoom: NewRoom,
|
|
135
141
|
RoomDeleted: RoomDeleted,
|
|
@@ -145,8 +151,8 @@ export type EventsMap = {
|
|
|
145
151
|
export type CommandsMap = {
|
|
146
152
|
// General commands
|
|
147
153
|
GetSession: [GetSession, EventsMap['Session']],
|
|
148
|
-
|
|
149
|
-
|
|
154
|
+
SetPermissionOverwrites: [SetPermissionOverwrites, EventsMap['PermissionOverwritesChanged']],
|
|
155
|
+
GetPermissionOverwrites: [GetPermissionOverwrites, EventsMap['PermissionOverwrites']],
|
|
150
156
|
GetComputedPermissions: [GetComputedPermissions, EventsMap['Permissions']],
|
|
151
157
|
// Space commands
|
|
152
158
|
JoinSpace: [JoinSpace, EventsMap['SpaceJoined']],
|
|
@@ -157,10 +163,9 @@ export type CommandsMap = {
|
|
|
157
163
|
GetSpaceRooms: [GetSpaceRooms, EventsMap['SpaceRooms']],
|
|
158
164
|
CreateRole: [CreateRole, EventsMap['NewRole']],
|
|
159
165
|
DeleteRole: [DeleteRole, EventsMap['RoleDeleted']],
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
GetRolePermissions: [GetRolePermissions, EventsMap['Permissions']],
|
|
166
|
+
UpdateRole: [UpdateRole, EventsMap['RoleUpdated']],
|
|
167
|
+
AssignRole: [AssignRole, EventsMap['SpaceMemberUpdated'] | EventsMap['RoomMemberUpdated']],
|
|
168
|
+
DeassignRole: [DeassignRole, EventsMap['SpaceMemberUpdated'] | EventsMap['RoomMemberUpdated']],
|
|
164
169
|
// Room commands
|
|
165
170
|
JoinRoom: [JoinRoom, EventsMap['RoomJoined']],
|
|
166
171
|
LeaveRoom: [LeaveRoom, EventsMap['RoomLeft']],
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export enum Permission {
|
|
2
|
+
Root = 1 << 0,
|
|
3
|
+
CreateSpaces = 1 << 1,
|
|
4
|
+
ManageSpaces = 1 << 2,
|
|
5
|
+
ManageRoles = 1 << 3,
|
|
6
|
+
ChangeNick = 1 << 4,
|
|
7
|
+
ManageRooms = 1 << 5,
|
|
8
|
+
ManageTopics = 1 << 6,
|
|
9
|
+
ManageMembers = 1 << 7,
|
|
10
|
+
SendMessages = 1 << 8,
|
|
11
|
+
ViewMessages = 1 << 9,
|
|
12
|
+
ChangeMessages = 1 << 10,
|
|
13
|
+
ManageMessages = 1 << 11,
|
|
14
|
+
ManagePermissions = 1 << 12,
|
|
15
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {ObservableInterface} from "./EventTarget";
|
|
2
2
|
import {AbstractChatClient, CommandResult, CommandsMap} from "./AbstractChatClient";
|
|
3
3
|
import {Envelope} from "pserv-ts-types";
|
|
4
|
-
import {ChatStateTracker} from "./ChatStateTracker";
|
|
4
|
+
import {ChatStateTracker} from "./state-tracker/ChatStateTracker";
|
|
5
5
|
|
|
6
6
|
export interface WebSocketClientOptions {
|
|
7
7
|
url: string;
|
package/src/index.ts
CHANGED
|
@@ -7,10 +7,12 @@ import {
|
|
|
7
7
|
ObservableIndexedObjectCollection
|
|
8
8
|
} from "./IndexedObjectCollection";
|
|
9
9
|
import { AuthClient } from "./AuthClient";
|
|
10
|
+
import { Permission } from "./Permission";
|
|
10
11
|
|
|
11
12
|
export {
|
|
12
13
|
IndexedCollection, ObservableIndexedCollection,
|
|
13
14
|
IndexedObjectCollection, ObservableIndexedObjectCollection,
|
|
15
|
+
Permission,
|
|
14
16
|
WebSocketChatClient, WebApiChatClient,
|
|
15
17
|
AuthClient
|
|
16
18
|
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import {WebSocketChatClient} from "../WebSocketChatClient";
|
|
2
|
+
import {Session, User} from "pserv-ts-types";
|
|
3
|
+
import {RoomsManager} from "./RoomsManager";
|
|
4
|
+
import {SpacesManager} from "./SpacesManager";
|
|
5
|
+
import {PermissionsManager} from "./PermissionsManager";
|
|
6
|
+
import {DeferredTask} from "./DeferredTask";
|
|
7
|
+
|
|
8
|
+
export class ChatStateTracker {
|
|
9
|
+
/**
|
|
10
|
+
* State of the rooms you are in.
|
|
11
|
+
*/
|
|
12
|
+
public readonly rooms: RoomsManager = new RoomsManager(this);
|
|
13
|
+
/**
|
|
14
|
+
* State of the spaces you are in.
|
|
15
|
+
*/
|
|
16
|
+
public readonly spaces = new SpacesManager(this);
|
|
17
|
+
/**
|
|
18
|
+
* State of your permissions.
|
|
19
|
+
*/
|
|
20
|
+
public readonly permissions = new PermissionsManager(this);
|
|
21
|
+
|
|
22
|
+
private me: User = null;
|
|
23
|
+
private readonly deferredSession = new DeferredTask();
|
|
24
|
+
|
|
25
|
+
public constructor(public readonly client: WebSocketChatClient) {
|
|
26
|
+
this.client.on('Session', ev => this.handleSession(ev));
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
public async getMe(): Promise<User> {
|
|
30
|
+
await this.deferredSession.promise;
|
|
31
|
+
return this.me;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
private handleSession(ev: Session): void {
|
|
35
|
+
this.me = ev.user;
|
|
36
|
+
this.deferredSession.resolve();
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import {ChatStateTracker} from "./ChatStateTracker";
|
|
2
|
+
import {
|
|
3
|
+
PermissionOverwrites,
|
|
4
|
+
PermissionOverwritesChanged,
|
|
5
|
+
PermissionOverwritesValue,
|
|
6
|
+
Role
|
|
7
|
+
} from "pserv-ts-types";
|
|
8
|
+
import {EventHandler, EventTarget} from "../EventTarget";
|
|
9
|
+
import {IndexedCollection} from "../IndexedObjectCollection";
|
|
10
|
+
import {Permission} from "../Permission";
|
|
11
|
+
|
|
12
|
+
const getOvId = (
|
|
13
|
+
layer: PermissionOverwrites['layer'],
|
|
14
|
+
layerId: PermissionOverwrites['layerId'],
|
|
15
|
+
target: PermissionOverwrites['target'],
|
|
16
|
+
targetId: PermissionOverwrites['targetId'],
|
|
17
|
+
) => layer + (layerId ?? '') + target + targetId;
|
|
18
|
+
|
|
19
|
+
const getOvIdByObject = (overwrites: PermissionOverwrites | PermissionOverwritesChanged): string => getOvId(
|
|
20
|
+
overwrites.layer, overwrites.layerId, overwrites.target, overwrites.targetId
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
export class PermissionsManager extends EventTarget {
|
|
24
|
+
private readonly overwrites = new IndexedCollection<string, PermissionOverwrites>();
|
|
25
|
+
|
|
26
|
+
public constructor(private tracker: ChatStateTracker) {
|
|
27
|
+
super();
|
|
28
|
+
this.tracker.client.on('PermissionOverwrites', ev => this.handlePermissionOverwrites(ev));
|
|
29
|
+
this.tracker.client.on('PermissionOverwritesChanged', ev => this.handlePermissionOverwrites(ev));
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
public async getOverwrites(
|
|
33
|
+
layer: PermissionOverwrites['layer'],
|
|
34
|
+
layerId: PermissionOverwrites['layerId'],
|
|
35
|
+
target: PermissionOverwrites['target'],
|
|
36
|
+
targetId: PermissionOverwrites['targetId'],
|
|
37
|
+
): Promise<PermissionOverwrites | null> {
|
|
38
|
+
const id = getOvId(layer, layerId, target, targetId);
|
|
39
|
+
|
|
40
|
+
if (this.overwrites.has(id)) {
|
|
41
|
+
return this.overwrites.get(id);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const result = await this.tracker.client.send(
|
|
45
|
+
'GetPermissionOverwrites',
|
|
46
|
+
{layer, layerId, target, targetId},
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
return result.error ? null : result.data;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
public on(eventName: 'change', handler: EventHandler<any>): this {
|
|
53
|
+
return super.on(eventName, handler);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
public async check(
|
|
57
|
+
permissionNames: (keyof typeof Permission)[],
|
|
58
|
+
spaceId?: string,
|
|
59
|
+
roomId?: string,
|
|
60
|
+
topicId?: string,
|
|
61
|
+
): Promise<{ok: boolean, missing: string[]}> {
|
|
62
|
+
const ownedPermissions = await this.calculatePermissions(spaceId, roomId, topicId);
|
|
63
|
+
const missing: string[] = [];
|
|
64
|
+
|
|
65
|
+
permissionNames.forEach(name => {
|
|
66
|
+
if (~ ownedPermissions & Permission[name]) {
|
|
67
|
+
missing.push(name);
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
return {ok: missing.length === 0, missing};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
public async calculatePermissions(spaceId?: string, roomId?: string, topicId?: string): Promise<number> {
|
|
75
|
+
if (topicId && ! roomId || roomId && ! spaceId) {
|
|
76
|
+
throw new Error('Corrupted arguments hierarchy');
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const userId = (await this.tracker.getMe()).id;
|
|
80
|
+
|
|
81
|
+
const userRoles: string[] = [];
|
|
82
|
+
|
|
83
|
+
const promises: Promise<PermissionOverwritesValue>[] = [
|
|
84
|
+
// Global user overwrites
|
|
85
|
+
this.getOverwrites('Global', null, 'User', userId).then(v => v.overwrites),
|
|
86
|
+
];
|
|
87
|
+
|
|
88
|
+
if (spaceId) {
|
|
89
|
+
userRoles.push(...(await this.tracker.spaces.getMe(spaceId)).roles);
|
|
90
|
+
promises.push(this.collectRoleOverwrites(spaceId, 'Space', spaceId, userRoles));
|
|
91
|
+
promises.push(this.getOverwrites('Space', spaceId, 'User', userId).then(v => v.overwrites));
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (roomId) {
|
|
95
|
+
const roomMember = await this.tracker.rooms.getMe(roomId);
|
|
96
|
+
|
|
97
|
+
if (roomMember.roles !== null) { // Room overwrites from roles (only for space rooms)
|
|
98
|
+
userRoles.push(...roomMember.roles);
|
|
99
|
+
promises.push(this.collectRoleOverwrites(spaceId, 'Room', roomId, userRoles));
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
promises.push(this.getOverwrites('Room', roomId, 'User', userId).then(v => v.overwrites));
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (topicId) {
|
|
106
|
+
if (userRoles.length) {
|
|
107
|
+
promises.push(this.collectRoleOverwrites(spaceId, 'Topic', topicId, userRoles));
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
promises.push(this.getOverwrites('Topic', topicId, 'User', userId).then(v => v.overwrites));
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return this.resolveOverwritesHierarchy(await Promise.all(promises));
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
private handlePermissionOverwrites(ev: PermissionOverwritesChanged | PermissionOverwrites) {
|
|
117
|
+
this.overwrites.set([getOvIdByObject(ev), ev]);
|
|
118
|
+
this.emit('change');
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
private async collectRoleOverwrites(
|
|
122
|
+
spaceId: string,
|
|
123
|
+
layer: PermissionOverwrites['layer'],
|
|
124
|
+
layerId: PermissionOverwrites['layerId'],
|
|
125
|
+
userRoles: string[],
|
|
126
|
+
): Promise<PermissionOverwritesValue> {
|
|
127
|
+
const roleOverwrites = await Promise.all(userRoles.map(
|
|
128
|
+
roleId => this.getOverwrites(layer, layerId, 'Role', roleId)
|
|
129
|
+
));
|
|
130
|
+
|
|
131
|
+
return this.resolveOverwritesFromRolesByOrder(spaceId, roleOverwrites);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
private async resolveOverwritesFromRolesByOrder(
|
|
135
|
+
spaceId: string,
|
|
136
|
+
overwrites: PermissionOverwrites[],
|
|
137
|
+
): Promise<PermissionOverwritesValue> {
|
|
138
|
+
let allows = 0, denies = 0;
|
|
139
|
+
const roles = await this.tracker.spaces.getRoles(spaceId);
|
|
140
|
+
const sortedOverwrites = overwrites.sort(
|
|
141
|
+
(a, b) =>
|
|
142
|
+
roles.get(a.targetId).priority - roles.get(b.targetId).priority
|
|
143
|
+
);
|
|
144
|
+
|
|
145
|
+
// Max length of bit word
|
|
146
|
+
const permissionsLength = overwrites.reduce(
|
|
147
|
+
(previousValue: number, currentValue: PermissionOverwrites) =>
|
|
148
|
+
Math.max(
|
|
149
|
+
previousValue,
|
|
150
|
+
currentValue.overwrites.allow.toString(2).length,
|
|
151
|
+
currentValue.overwrites.deny.toString(2).length
|
|
152
|
+
),
|
|
153
|
+
0,
|
|
154
|
+
);
|
|
155
|
+
|
|
156
|
+
sortedOverwrites.forEach(overwriteEvent => {
|
|
157
|
+
const overwrites = overwriteEvent.overwrites;
|
|
158
|
+
const revDecDenies = overwrites.deny.toString(2).split('').reverse().join('');
|
|
159
|
+
const revDecAllows = overwrites.allow.toString(2).split('').reverse().join('');
|
|
160
|
+
|
|
161
|
+
for (let i = 0; i < permissionsLength; i++) {
|
|
162
|
+
const deny = parseInt(revDecDenies[i] ?? '0');
|
|
163
|
+
const allow = parseInt(revDecAllows[i] ?? '0');
|
|
164
|
+
|
|
165
|
+
if (deny) {
|
|
166
|
+
denies |= 1 << i;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (allow) {
|
|
170
|
+
allows |= 1 << i;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
return {allow: allows, deny: denies};
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
private resolveOverwritesHierarchy(permissionOverwritesValues: PermissionOverwritesValue[]): number {
|
|
179
|
+
let result = 0;
|
|
180
|
+
|
|
181
|
+
for (const value of permissionOverwritesValues) {
|
|
182
|
+
if (value.allow & Permission.Root) {
|
|
183
|
+
return this.getRootAccessValue();
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
result = (result & ~value.deny) | value.allow;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return result;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
private getRootAccessValue(): number {
|
|
193
|
+
let result = 0;
|
|
194
|
+
|
|
195
|
+
for (const name of this.getPermissionNames()) {
|
|
196
|
+
result |= Permission[name];
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return result;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
private getPermissionNames(): string[] {
|
|
203
|
+
return Object.keys(Permission).filter(key => Number.isNaN(parseInt(key)));
|
|
204
|
+
}
|
|
205
|
+
}
|