feat!: add roles
system and lock presence updates behind a PRESENCE_UPDATES
role
This commit is contained in:
parent
800d778183
commit
b2a725d084
2 changed files with 34 additions and 9 deletions
|
@ -19,12 +19,19 @@ const wsCloseCodes = {
|
||||||
NO_PING: [4008, "No ping"],
|
NO_PING: [4008, "No ping"],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const roles = {
|
||||||
|
PRESENCE_UPDATES: "PRESENCE_UPDATES"
|
||||||
|
};
|
||||||
|
|
||||||
|
const supportedRoles = [roles.PRESENCE_UPDATES];
|
||||||
|
|
||||||
class GatewaySession {
|
class GatewaySession {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.authenticated = false;
|
this.authenticated = false;
|
||||||
this.user = null;
|
this.user = null;
|
||||||
this.token = null;
|
this.token = null;
|
||||||
this.sessionId = uuid.v4();
|
this.sessionId = uuid.v4();
|
||||||
|
this.roles = [];
|
||||||
|
|
||||||
// Specific to websocket sessions
|
// Specific to websocket sessions
|
||||||
this.isWebsocketConnection = false;
|
this.isWebsocketConnection = false;
|
||||||
|
@ -33,6 +40,11 @@ class GatewaySession {
|
||||||
this.channels = [];
|
this.channels = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hasRole(roleName) {
|
||||||
|
if (roleName.length < 1) return true; // TODO: HACK
|
||||||
|
return this.roles.includes(roleName);
|
||||||
|
}
|
||||||
|
|
||||||
setWebsocketClient(ws) {
|
setWebsocketClient(ws) {
|
||||||
this.ws = ws;
|
this.ws = ws;
|
||||||
this.isWebsocketConnection = true;
|
this.isWebsocketConnection = true;
|
||||||
|
@ -103,7 +115,7 @@ class GatewayHandler {
|
||||||
handleConnectionClose(ws) {
|
handleConnectionClose(ws) {
|
||||||
if (ws.session && ws.session.user && ws.session.channels) {
|
if (ws.session && ws.session.user && ws.session.channels) {
|
||||||
if (this.sessionCounters[ws.session.user._id] <= 1) {
|
if (this.sessionCounters[ws.session.user._id] <= 1) {
|
||||||
this.eachInChannel(ws.session.channels[0], (client) => {
|
this.eachInChannel({channelId: ws.session.channels[0], role: roles.PRESENCE_UPDATES}, (client) => {
|
||||||
if (client.session && client.session.isReady()) {
|
if (client.session && client.session.isReady()) {
|
||||||
client.session.send("EVENT_CHANNEL_MEMBERS", {
|
client.session.send("EVENT_CHANNEL_MEMBERS", {
|
||||||
[ws.session.user._id]: {
|
[ws.session.user._id]: {
|
||||||
|
@ -140,10 +152,10 @@ class GatewayHandler {
|
||||||
return this.wss.clients;
|
return this.wss.clients;
|
||||||
}
|
}
|
||||||
|
|
||||||
eachInChannel(channelId, callback) {
|
eachInChannel({channelId, role=""}, callback) {
|
||||||
const clients = this.getClients();
|
const clients = this.getClients();
|
||||||
clients.forEach((client) => {
|
clients.forEach((client) => {
|
||||||
if (client.session && client.session.isReady() && client.session.channels.includes(channelId))
|
if (client.session && client.session.isReady() && client.session.hasRole(role) && client.session.channels.includes(channelId))
|
||||||
callback(client);
|
callback(client);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -161,20 +173,28 @@ class GatewayHandler {
|
||||||
const channels = (await Channel.find().lean().sort({ _id: -1 }).limit(50).select("-posts -__v").populate("creator", User.getPulicFields(true))) || [];
|
const channels = (await Channel.find().lean().sort({ _id: -1 }).limit(50).select("-posts -__v").populate("creator", User.getPulicFields(true))) || [];
|
||||||
session.channels = channels.map(x => x._id.toString());
|
session.channels = channels.map(x => x._id.toString());
|
||||||
|
|
||||||
|
if (data.roles) {
|
||||||
|
if (!Array.isArray(data.roles) || data.roles.length > 8) return {error: wsCloseCodes.PAYLOAD_ERROR};
|
||||||
|
for (let i = 0; i < data.roles; i++) {
|
||||||
|
if (!supportedRoles.includes(data[i])) return {error: wsCloseCodes.PAYLOAD_ERROR};
|
||||||
|
}
|
||||||
|
session.roles = data.roles;
|
||||||
|
}
|
||||||
|
|
||||||
session.send("YOO_ACK", { session_id: session.sessionId, channels, user: { username: session.user.username, _id: session.user._id }, __global_experiments: experiments });
|
session.send("YOO_ACK", { session_id: session.sessionId, channels, user: { username: session.user.username, _id: session.user._id }, __global_experiments: experiments });
|
||||||
|
|
||||||
const channel = session.channels[0];
|
const channel = session.channels[0];
|
||||||
if (channel) {
|
if (channel) {
|
||||||
const presence = {};
|
const presence = {};
|
||||||
|
|
||||||
this.eachInChannel(channel, ({ session: remoteSession }) => {
|
this.eachInChannel({channelId: channel}, ({ session: remoteSession }) => {
|
||||||
presence[remoteSession.user._id] = {
|
presence[remoteSession.user._id] = {
|
||||||
_id: remoteSession.user._id,
|
_id: remoteSession.user._id,
|
||||||
username: remoteSession.user.username,
|
username: remoteSession.user.username,
|
||||||
status: 1,
|
status: 1,
|
||||||
status_text: "Online"
|
status_text: "Online"
|
||||||
};
|
};
|
||||||
if (remoteSession.sessionId !== session.sessionId) {
|
if (remoteSession.sessionId !== session.sessionId && remoteSession.hasRole(roles.PRESENCE_UPDATES)) {
|
||||||
remoteSession.send("EVENT_CHANNEL_MEMBERS", {
|
remoteSession.send("EVENT_CHANNEL_MEMBERS", {
|
||||||
[session.user._id]: {
|
[session.user._id]: {
|
||||||
_id: session.user._id,
|
_id: session.user._id,
|
||||||
|
@ -186,7 +206,7 @@ class GatewayHandler {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
session.send("EVENT_CHANNEL_MEMBERS", presence);
|
(session.hasRole(roles.PRESENCE_UPDATES)) && session.send("EVENT_CHANNEL_MEMBERS", presence);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -204,13 +224,13 @@ class GatewayHandler {
|
||||||
if (typeof data.content !== "string" || typeof data.channel !== "object" || typeof data.channel._id !== "string") throw new Error("msg: invalid fields in json payload");
|
if (typeof data.content !== "string" || typeof data.channel !== "object" || typeof data.channel._id !== "string") throw new Error("msg: invalid fields in json payload");
|
||||||
|
|
||||||
const messageContent = data.content.trim();
|
const messageContent = data.content.trim();
|
||||||
if (messageContent.length > 2000 || messageContent === "") return;
|
if (messageContent.length > 2000 || messageContent.length < 1) return;
|
||||||
if (data.channel._id.length !== 24) throw new Error("msg: payload has invalid id"); // MONGODB ONLY!!
|
if (data.channel._id.length !== 24) throw new Error("msg: payload has invalid id"); // MONGODB ONLY!!
|
||||||
|
|
||||||
// Check if the user is in that channel before broadcasting the message
|
// Check if the user is in that channel before broadcasting the message
|
||||||
if (!session.channels.includes(data.channel._id)) return {error: wsCloseCodes.NOT_AUTHORIZED};
|
if (!session.channels.includes(data.channel._id)) return {error: wsCloseCodes.NOT_AUTHORIZED};
|
||||||
|
|
||||||
this.eachInChannel(data.channel._id, ({ session: remoteSession }) => {
|
this.eachInChannel({channelId: data.channel._id}, ({ session: remoteSession }) => {
|
||||||
remoteSession.send("EVENT_CREATE_MESSAGE", {
|
remoteSession.send("EVENT_CREATE_MESSAGE", {
|
||||||
content: messageContent,
|
content: messageContent,
|
||||||
channel: {
|
channel: {
|
||||||
|
|
|
@ -43,10 +43,11 @@ JSON data format:
|
||||||
| Field | Description |
|
| Field | Description |
|
||||||
| - | - |
|
| - | - |
|
||||||
| token | The authentication token |
|
| token | The authentication token |
|
||||||
|
| roles | An array of attributes the client wants the server to enable. The current possible values are: `PRESENCE_UPDATES` (required for presence updates to be sent to the client) |
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
```json
|
```json
|
||||||
1@{"token":"my totally real token"}
|
1@{"token":"my totally real token","roles":["PRESENCE_UPDATES"]}
|
||||||
```
|
```
|
||||||
|
|
||||||
If the token is invalid, or the connection is otherwise rejected, the client should be disconnected as soon as possible, and no YOO\_ACK should be sent.
|
If the token is invalid, or the connection is otherwise rejected, the client should be disconnected as soon as possible, and no YOO\_ACK should be sent.
|
||||||
|
@ -97,6 +98,8 @@ JSON data format:
|
||||||
|
|
||||||
Sent by the client when updating status. Usually, this packet is sent as soon as possible after getting YOO\_ACK to indicate the client is online.
|
Sent by the client when updating status. Usually, this packet is sent as soon as possible after getting YOO\_ACK to indicate the client is online.
|
||||||
|
|
||||||
|
**This packet is deprecated and reserved for future use**
|
||||||
|
|
||||||
JSON data format:
|
JSON data format:
|
||||||
| Field | Description |
|
| Field | Description |
|
||||||
| - | - |
|
| - | - |
|
||||||
|
@ -114,6 +117,8 @@ Example:
|
||||||
|
|
||||||
An object containing a list of user presences which were updated, where the keys are the user ids and the values are [user presence objects](#user-presence-object).
|
An object containing a list of user presences which were updated, where the keys are the user ids and the values are [user presence objects](#user-presence-object).
|
||||||
|
|
||||||
|
**This packet is only sent if the user has `PRESENCE_UPDATES` in their `roles`, specified in YOO.**
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
```json
|
```json
|
||||||
6@{"userid":{_id:"userid",username:"username123",status:1,status_text:"hello"}}
|
6@{"userid":{_id:"userid",username:"username123",status:1,status_text:"hello"}}
|
||||||
|
|
Reference in a new issue