Merge pull request 'feat: add message storage and fetching api' (#21) from message-history into master
Reviewed-on: #21
This commit is contained in:
commit
8984598ade
5 changed files with 92 additions and 21 deletions
|
@ -1,6 +1,7 @@
|
|||
const User = require("../../models/User");
|
||||
const Channel = require("../../models/Channel");
|
||||
const Post = require("../../models/Post");
|
||||
const Message = require("../../models/Message");
|
||||
const config = require("../../config");
|
||||
const { authenticateEndpoint } = require("./../../common/auth/authfunctions");
|
||||
|
||||
|
@ -38,7 +39,7 @@ app.post("/channel/create", [
|
|||
|
||||
res.status(200).json({
|
||||
error: false,
|
||||
message: "SUCCESS_CATEGORY_CREATED",
|
||||
message: "SUCCESS_CHANNEL_CREATED",
|
||||
channel: channel.getPublicObject()
|
||||
});
|
||||
}, undefined, config.roleMap.USER));
|
||||
|
@ -76,7 +77,7 @@ app.post("/post/create", [
|
|||
if (r.n < 1) {
|
||||
res.status(404).json({
|
||||
error: true,
|
||||
message: "ERROR_CATEGORY_NOT_FOUND"
|
||||
message: "ERROR_CHANNEL_NOT_FOUND"
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
@ -90,6 +91,43 @@ app.post("/post/create", [
|
|||
});
|
||||
}, undefined, config.roleMap.USER));
|
||||
|
||||
app.get("/channel/:channel/messages", [
|
||||
param("channel").not().isEmpty().trim().escape().isLength({ min: 24, max: 24 })
|
||||
], authenticateEndpoint(async (req, res) => {
|
||||
if (!config.policies.allowSavingMessages) {
|
||||
// TODO: hack
|
||||
res.status(200).json({
|
||||
error: false,
|
||||
message: "SUCCESS_CHANNEL_MESSAGES_FETCHED",
|
||||
channelMessages: []
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const errors = validationResult(req);
|
||||
if (!errors.isEmpty()) {
|
||||
res.status(400).json({ error: true, message: "ERROR_REQUEST_INVALID_DATA", errors: errors.array() });
|
||||
return;
|
||||
}
|
||||
|
||||
let idSearch = {};
|
||||
if (req.query.before) {
|
||||
idSearch = { _id: { $lt: req.query.before } };
|
||||
}
|
||||
|
||||
const messages = await Message.find({ channel: req.params.channel, ...idSearch })
|
||||
.sort({ _id: -1 })
|
||||
.limit(50)
|
||||
.select("-__v -channel")
|
||||
.populate("author", "_id username");
|
||||
|
||||
res.status(200).json({
|
||||
error: false,
|
||||
message: "SUCCESS_CHANNEL_MESSAGES_FETCHED",
|
||||
channelMessages: messages
|
||||
});
|
||||
}, undefined, config.roleMap.USER));
|
||||
|
||||
app.get("/channel/:channel/info", [
|
||||
param("channel").not().isEmpty().trim().escape().isLength({ min: 24, max: 24 })
|
||||
], authenticateEndpoint(async (req, res) => {
|
||||
|
@ -99,23 +137,21 @@ app.get("/channel/:channel/info", [
|
|||
return;
|
||||
}
|
||||
|
||||
const channelId = req.params.channel;
|
||||
const channel = await Channel.findById(channelId).populate("posts.creator", User.getPulicFields());
|
||||
const channel = await Channel.findById(req.params.channel).populate("posts.creator", User.getPulicFields());
|
||||
|
||||
// TODO: Implement subscribing to a channel and stuff
|
||||
const users = await User.find().sort({ _id: -1 }).limit(50).select(User.getPulicFields());
|
||||
|
||||
if (!channel) {
|
||||
res.status(404).json({
|
||||
error: true,
|
||||
message: "ERROR_CATEGORY_NOT_FOUND"
|
||||
message: "ERROR_CHANNEL_NOT_FOUND"
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
res.status(200).json({
|
||||
error: false,
|
||||
message: "SUCCESS_CATEGORY_DATA_FETCHED",
|
||||
message: "SUCCESS_CHANNEL_DATA_FETCHED",
|
||||
channel: channel.getPublicObject(),
|
||||
userInfo: {
|
||||
userListLimit: 50,
|
||||
|
@ -135,7 +171,7 @@ app.get("/channel/list", authenticateEndpoint(async (req, res) => {
|
|||
|
||||
res.status(200).json({
|
||||
error: false,
|
||||
message: "SUCCESS_CATEGORY_LIST_FETCHED",
|
||||
message: "SUCCESS_CHANNEL_LIST_FETCHED",
|
||||
channels
|
||||
});
|
||||
}, undefined, config.roleMap.USER));
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
const websockets = require("ws");
|
||||
const uuid = require("uuid");
|
||||
const { v4 } = require("uuid");
|
||||
const mongoose = require("mongoose");
|
||||
|
||||
const { policies, gatewayPingInterval, gatewayPingCheckInterval, clientFacingPingInterval } = require("../../../config");
|
||||
const { experiments } = require("../../../experiments");
|
||||
const User = require("../../../models/User");
|
||||
const Channel = require("../../../models/Channel");
|
||||
const Message = require("../../../models/Message");
|
||||
const { parseMessage, packet } = require("./messageparser");
|
||||
const { checkToken } = require("../../../common/auth/authfunctions");
|
||||
|
||||
|
@ -17,10 +19,11 @@ const wsCloseCodes = {
|
|||
NOT_AUTHORIZED: [4006, "Not authorized"],
|
||||
FLOODING: [4007, "Flooding"],
|
||||
NO_PING: [4008, "No ping"],
|
||||
UNSUPPORTED_ATTRIBUTE: [4009, "Unsupported attribute."],
|
||||
};
|
||||
|
||||
const attributes = {
|
||||
PRESENCE_UPDATES: "PRESENCE_UPDATES"
|
||||
PRESENCE_UPDATES: "PRESENCE_UPDATES",
|
||||
};
|
||||
|
||||
const supportedAttributes = [attributes.PRESENCE_UPDATES];
|
||||
|
@ -30,7 +33,7 @@ class GatewaySession {
|
|||
this.authenticated = false;
|
||||
this.user = null;
|
||||
this.token = null;
|
||||
this.sessionId = uuid.v4();
|
||||
this.sessionId = v4();
|
||||
this.attributes = [];
|
||||
|
||||
// Specific to websocket sessions
|
||||
|
@ -108,7 +111,7 @@ class GatewayHandler {
|
|||
|
||||
const session = new GatewaySession();
|
||||
session.setWebsocketClient(ws);
|
||||
session.send("HELLO", { pingInterval: clientFacingPingInterval });
|
||||
session.send("HELLO", { pingInterval: clientFacingPingInterval, supportedAttributes });
|
||||
return session;
|
||||
}
|
||||
|
||||
|
@ -175,8 +178,9 @@ class GatewayHandler {
|
|||
|
||||
if (data.attributes) {
|
||||
if (!Array.isArray(data.attributes) || data.attributes.length > 8) return {error: wsCloseCodes.PAYLOAD_ERROR};
|
||||
for (let i = 0; i < data.attributes; i++) {
|
||||
if (!supportedAttributes.includes(data[i])) return {error: wsCloseCodes.PAYLOAD_ERROR};
|
||||
for (let i = 0; i < data.attributes.length; i++) {
|
||||
if (!supportedAttributes.includes(data.attributes[i]))
|
||||
return {error: wsCloseCodes.UNSUPPORTED_ATTRIBUTE};
|
||||
}
|
||||
session.attributes = data.attributes;
|
||||
}
|
||||
|
@ -230,7 +234,20 @@ class GatewayHandler {
|
|||
// Check if the user is in that channel before broadcasting the message
|
||||
if (!session.channels.includes(data.channel._id)) return {error: wsCloseCodes.NOT_AUTHORIZED};
|
||||
|
||||
this.eachInChannel({channelId: data.channel._id}, ({ session: remoteSession }) => {
|
||||
this.eachInChannel({channelId: data.channel._id}, async ({ session: remoteSession }) => {
|
||||
let id;
|
||||
if (policies.allowSavingMessages) {
|
||||
const message = await Message.create({
|
||||
author: session.user._id,
|
||||
channel: data.channel._id,
|
||||
content: messageContent,
|
||||
createdAt: new Date().getTime()
|
||||
});
|
||||
id = message._id;
|
||||
} else {
|
||||
id = new mongoose.Types.ObjectId();
|
||||
}
|
||||
|
||||
remoteSession.send("EVENT_CREATE_MESSAGE", {
|
||||
content: messageContent,
|
||||
channel: {
|
||||
|
@ -240,7 +257,7 @@ class GatewayHandler {
|
|||
_id: session.user._id,
|
||||
username: session.user.username
|
||||
},
|
||||
_id: uuid.v4()
|
||||
_id: id
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -10,7 +10,6 @@ module.exports = {
|
|||
// "https://example.com"
|
||||
|
||||
"http://localhost:3005", // Allow the server itself (provided it's listening on 3005)
|
||||
//"http://localhost:3000" // Optionally allow the react app development server (which listens on 3000 by default)
|
||||
],
|
||||
policies: {
|
||||
// Currently, policies apply to all users - no matter the role.
|
||||
|
@ -19,7 +18,10 @@ module.exports = {
|
|||
allowAccountCreation: true,
|
||||
allowLogin: true,
|
||||
allowGatewayConnection: true,
|
||||
perUserMaxGatewayConnections: 4,
|
||||
// The policy below will make all messages sent over the gateway to be in plain text saved to the database.
|
||||
// This is experimental and dangerous, and, as such, should generally not be used.
|
||||
allowSavingMessages: false,
|
||||
perUserMaxGatewayConnections: 4
|
||||
},
|
||||
/*
|
||||
--- Adding a special code requirement for account creation
|
||||
|
|
12
brainlet/models/Message.js
Normal file
12
brainlet/models/Message.js
Normal file
|
@ -0,0 +1,12 @@
|
|||
const mongoose = require("mongoose");
|
||||
|
||||
const messageSchema = new mongoose.Schema({
|
||||
author: {type: mongoose.Schema.Types.ObjectId, ref: "User"},
|
||||
channel: {type: mongoose.Schema.Types.ObjectId, ref: "Channel"},
|
||||
content: String,
|
||||
createdAt: Number
|
||||
});
|
||||
|
||||
const Message = mongoose.model("Message", messageSchema);
|
||||
|
||||
module.exports = Message;
|
|
@ -26,11 +26,15 @@ Packets can also have JSON as a payload:
|
|||
|
||||
Sent by the server to the client as soon as possible after they connect to the gateway.
|
||||
|
||||
This payload contains a `pingInterval` property. Every *pingInterval*, the client must send a packet simply containing `7@1`. This is the ACTION_PING payload. If the client does not send this payload at the right time, it is disconnected.
|
||||
JSON data format:
|
||||
| Field | Description |
|
||||
| - | - |
|
||||
| pingInterval | Every *pingInterval*, the client must send a packet simply containing `7@1`. This is the ACTION_PING payload. If the client does not send this payload at the right time, it is disconnected. |
|
||||
| supportedAttributes | An array of attributes supported by the server. If a client requests an unsupported attribute, it is disconnected from the server. |
|
||||
|
||||
Example:
|
||||
```json
|
||||
0@{"pingInterval":14750}
|
||||
0@{"pingInterval":14750,"supportedAttributes":["PRESENCE_UPDATES"]}
|
||||
```
|
||||
|
||||
## 1:YOO
|
||||
|
@ -194,7 +198,7 @@ Voice server signaling is done through a websocket gateway. This gateway is spec
|
|||
| content | The text content of the message (max 2000 characters, min 1 character, trimmed) |
|
||||
| channel | A [message channel object](#message-channel-object) |
|
||||
| author | A [message author object](#message-author-object) |
|
||||
| _id | A UUIDv4 |
|
||||
| _id | An ObjectId |
|
||||
|
||||
## Message channel object
|
||||
|
||||
|
|
Reference in a new issue