Skip to content

Commit 109efca

Browse files
authored
chore: Remove WEBSOCKETS_ENABLED flag (outline#1383)
* chore: Remove WEBSOCKETS_ENALBED flag * lint
1 parent 2cc6d7a commit 109efca

File tree

7 files changed

+144
-160
lines changed

7 files changed

+144
-160
lines changed

.env.sample

-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ PORT=3000
1818
FORCE_HTTPS=true
1919

2020
ENABLE_UPDATES=true
21-
WEBSOCKETS_ENABLED=true
2221
DEBUG=cache,presenters,events
2322

2423
# Third party signin credentials (at least one is required)

app.json

-5
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,6 @@
3939
"value": "true",
4040
"required": true
4141
},
42-
"WEBSOCKETS_ENABLED": {
43-
"value": "true",
44-
"required": true,
45-
"description": "Allow realtime data to be pushed to clients over websockets"
46-
},
4742
"URL": {
4843
"description": "https://{your app name}.herokuapp.com",
4944
"required": true

app/components/SocketProvider.js

-3
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import PoliciesStore from "stores/PoliciesStore";
1313
import ViewsStore from "stores/ViewsStore";
1414
import AuthStore from "stores/AuthStore";
1515
import UiStore from "stores/UiStore";
16-
import env from "env";
1716

1817
export const SocketContext: any = React.createContext();
1918

@@ -35,8 +34,6 @@ class SocketProvider extends React.Component<Props> {
3534
@observable socket;
3635

3736
componentDidMount() {
38-
if (!env.WEBSOCKETS_ENABLED) return;
39-
4037
this.socket = io(window.location.origin, {
4138
path: "/realtime",
4239
});

index.js

-6
Original file line numberDiff line numberDiff line change
@@ -62,12 +62,6 @@ if (!process.env.REDIS_URL) {
6262
process.exit(1);
6363
}
6464

65-
if (!process.env.WEBSOCKETS_ENABLED) {
66-
console.log(
67-
"WARNING: Websockets are disabled. Set WEBSOCKETS_ENABLED env variable to true to enable"
68-
);
69-
}
70-
7165
if (process.env.NODE_ENV === "production") {
7266
console.log("\n\x1b[33m%s\x1b[0m", "Running Outline in production mode.");
7367
} else if (process.env.NODE_ENV === "development") {

server/env.js

-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,5 @@ export default {
77
SLACK_KEY: process.env.SLACK_KEY,
88
SLACK_APP_ID: process.env.SLACK_APP_ID,
99
SUBDOMAINS_ENABLED: process.env.SUBDOMAINS_ENABLED === "true",
10-
WEBSOCKETS_ENABLED: process.env.WEBSOCKETS_ENABLED === "true",
1110
GOOGLE_ANALYTICS_ID: process.env.GOOGLE_ANALYTICS_ID,
1211
};

server/index.js

+141-143
Original file line numberDiff line numberDiff line change
@@ -12,168 +12,166 @@ import policy from "./policies";
1212
const server = http.createServer(app.callback());
1313
let io;
1414

15-
if (process.env.WEBSOCKETS_ENABLED === "true") {
16-
const { can } = policy;
17-
18-
io = IO(server, {
19-
path: "/realtime",
20-
serveClient: false,
21-
cookie: false,
22-
});
23-
24-
io.adapter(
25-
socketRedisAdapter({
26-
pubClient: client,
27-
subClient: subscriber,
28-
})
29-
);
30-
31-
SocketAuth(io, {
32-
authenticate: async (socket, data, callback) => {
33-
const { token } = data;
34-
35-
try {
36-
const user = await getUserForJWT(token);
37-
socket.client.user = user;
38-
39-
// store the mapping between socket id and user id in redis
40-
// so that it is accessible across multiple server nodes
41-
await client.hset(socket.id, "userId", user.id);
42-
43-
return callback(null, true);
44-
} catch (err) {
45-
return callback(err);
46-
}
47-
},
48-
postAuthenticate: async (socket, data) => {
49-
const { user } = socket.client;
50-
51-
// the rooms associated with the current team
52-
// and user so we can send authenticated events
53-
let rooms = [`team-${user.teamId}`, `user-${user.id}`];
54-
55-
// the rooms associated with collections this user
56-
// has access to on connection. New collection subscriptions
57-
// are managed from the client as needed through the 'join' event
58-
const collectionIds = await user.collectionIds();
59-
collectionIds.forEach(collectionId =>
60-
rooms.push(`collection-${collectionId}`)
61-
);
62-
63-
// join all of the rooms at once
64-
socket.join(rooms);
65-
66-
// allow the client to request to join rooms
67-
socket.on("join", async event => {
68-
// user is joining a collection channel, because their permissions have
69-
// changed, granting them access.
70-
if (event.collectionId) {
71-
const collection = await Collection.scope({
72-
method: ["withMembership", user.id],
73-
}).findByPk(event.collectionId);
74-
75-
if (can(user, "read", collection)) {
76-
socket.join(`collection-${event.collectionId}`);
77-
}
78-
}
15+
const { can } = policy;
7916

80-
// user is joining a document channel, because they have navigated to
81-
// view a document.
82-
if (event.documentId) {
83-
const document = await Document.findByPk(event.documentId, {
84-
userId: user.id,
85-
});
17+
io = IO(server, {
18+
path: "/realtime",
19+
serveClient: false,
20+
cookie: false,
21+
});
8622

87-
if (can(user, "read", document)) {
88-
const room = `document-${event.documentId}`;
23+
io.adapter(
24+
socketRedisAdapter({
25+
pubClient: client,
26+
subClient: subscriber,
27+
})
28+
);
29+
30+
SocketAuth(io, {
31+
authenticate: async (socket, data, callback) => {
32+
const { token } = data;
33+
34+
try {
35+
const user = await getUserForJWT(token);
36+
socket.client.user = user;
37+
38+
// store the mapping between socket id and user id in redis
39+
// so that it is accessible across multiple server nodes
40+
await client.hset(socket.id, "userId", user.id);
41+
42+
return callback(null, true);
43+
} catch (err) {
44+
return callback(err);
45+
}
46+
},
47+
postAuthenticate: async (socket, data) => {
48+
const { user } = socket.client;
49+
50+
// the rooms associated with the current team
51+
// and user so we can send authenticated events
52+
let rooms = [`team-${user.teamId}`, `user-${user.id}`];
53+
54+
// the rooms associated with collections this user
55+
// has access to on connection. New collection subscriptions
56+
// are managed from the client as needed through the 'join' event
57+
const collectionIds = await user.collectionIds();
58+
collectionIds.forEach(collectionId =>
59+
rooms.push(`collection-${collectionId}`)
60+
);
61+
62+
// join all of the rooms at once
63+
socket.join(rooms);
64+
65+
// allow the client to request to join rooms
66+
socket.on("join", async event => {
67+
// user is joining a collection channel, because their permissions have
68+
// changed, granting them access.
69+
if (event.collectionId) {
70+
const collection = await Collection.scope({
71+
method: ["withMembership", user.id],
72+
}).findByPk(event.collectionId);
73+
74+
if (can(user, "read", collection)) {
75+
socket.join(`collection-${event.collectionId}`);
76+
}
77+
}
8978

90-
await View.touch(event.documentId, user.id, event.isEditing);
91-
const editing = await View.findRecentlyEditingByDocument(
92-
event.documentId
93-
);
79+
// user is joining a document channel, because they have navigated to
80+
// view a document.
81+
if (event.documentId) {
82+
const document = await Document.findByPk(event.documentId, {
83+
userId: user.id,
84+
});
9485

95-
socket.join(room, () => {
96-
// let everyone else in the room know that a new user joined
97-
io.to(room).emit("user.join", {
98-
userId: user.id,
99-
documentId: event.documentId,
100-
isEditing: event.isEditing,
101-
});
86+
if (can(user, "read", document)) {
87+
const room = `document-${event.documentId}`;
10288

103-
// let this user know who else is already present in the room
104-
io.in(room).clients(async (err, sockets) => {
105-
if (err) throw err;
106-
107-
// because a single user can have multiple socket connections we
108-
// need to make sure that only unique userIds are returned. A Map
109-
// makes this easy.
110-
let userIds = new Map();
111-
for (const socketId of sockets) {
112-
const userId = await client.hget(socketId, "userId");
113-
userIds.set(userId, userId);
114-
}
115-
socket.emit("document.presence", {
116-
documentId: event.documentId,
117-
userIds: Array.from(userIds.keys()),
118-
editingIds: editing.map(view => view.userId),
119-
});
120-
});
121-
});
122-
}
123-
}
124-
});
89+
await View.touch(event.documentId, user.id, event.isEditing);
90+
const editing = await View.findRecentlyEditingByDocument(
91+
event.documentId
92+
);
12593

126-
// allow the client to request to leave rooms
127-
socket.on("leave", event => {
128-
if (event.collectionId) {
129-
socket.leave(`collection-${event.collectionId}`);
130-
}
131-
if (event.documentId) {
132-
const room = `document-${event.documentId}`;
133-
socket.leave(room, () => {
134-
io.to(room).emit("user.leave", {
94+
socket.join(room, () => {
95+
// let everyone else in the room know that a new user joined
96+
io.to(room).emit("user.join", {
13597
userId: user.id,
13698
documentId: event.documentId,
99+
isEditing: event.isEditing,
137100
});
138-
});
139-
}
140-
});
141101

142-
socket.on("disconnecting", () => {
143-
const rooms = Object.keys(socket.rooms);
144-
145-
rooms.forEach(room => {
146-
if (room.startsWith("document-")) {
147-
const documentId = room.replace("document-", "");
148-
io.to(room).emit("user.leave", {
149-
userId: user.id,
150-
documentId,
102+
// let this user know who else is already present in the room
103+
io.in(room).clients(async (err, sockets) => {
104+
if (err) throw err;
105+
106+
// because a single user can have multiple socket connections we
107+
// need to make sure that only unique userIds are returned. A Map
108+
// makes this easy.
109+
let userIds = new Map();
110+
for (const socketId of sockets) {
111+
const userId = await client.hget(socketId, "userId");
112+
userIds.set(userId, userId);
113+
}
114+
socket.emit("document.presence", {
115+
documentId: event.documentId,
116+
userIds: Array.from(userIds.keys()),
117+
editingIds: editing.map(view => view.userId),
118+
});
151119
});
152-
}
153-
});
154-
});
120+
});
121+
}
122+
}
123+
});
155124

156-
socket.on("presence", async event => {
125+
// allow the client to request to leave rooms
126+
socket.on("leave", event => {
127+
if (event.collectionId) {
128+
socket.leave(`collection-${event.collectionId}`);
129+
}
130+
if (event.documentId) {
157131
const room = `document-${event.documentId}`;
132+
socket.leave(room, () => {
133+
io.to(room).emit("user.leave", {
134+
userId: user.id,
135+
documentId: event.documentId,
136+
});
137+
});
138+
}
139+
});
158140

159-
if (event.documentId && socket.rooms[room]) {
160-
const view = await View.touch(
161-
event.documentId,
162-
user.id,
163-
event.isEditing
164-
);
165-
view.user = user;
141+
socket.on("disconnecting", () => {
142+
const rooms = Object.keys(socket.rooms);
166143

167-
io.to(room).emit("user.presence", {
144+
rooms.forEach(room => {
145+
if (room.startsWith("document-")) {
146+
const documentId = room.replace("document-", "");
147+
io.to(room).emit("user.leave", {
168148
userId: user.id,
169-
documentId: event.documentId,
170-
isEditing: event.isEditing,
149+
documentId,
171150
});
172151
}
173152
});
174-
},
175-
});
176-
}
153+
});
154+
155+
socket.on("presence", async event => {
156+
const room = `document-${event.documentId}`;
157+
158+
if (event.documentId && socket.rooms[room]) {
159+
const view = await View.touch(
160+
event.documentId,
161+
user.id,
162+
event.isEditing
163+
);
164+
view.user = user;
165+
166+
io.to(room).emit("user.presence", {
167+
userId: user.id,
168+
documentId: event.documentId,
169+
isEditing: event.isEditing,
170+
});
171+
}
172+
});
173+
},
174+
});
177175

178176
server.on("error", err => {
179177
throw err;

server/services/websockets.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@ import subHours from "date-fns/sub_hours";
1313

1414
export default class Websockets {
1515
async on(event: Event) {
16-
if (process.env.WEBSOCKETS_ENABLED !== "true" || !socketio) return;
16+
if (!socketio) {
17+
return;
18+
}
1719

1820
switch (event.name) {
1921
case "documents.publish":

0 commit comments

Comments
 (0)