Repository navigation
Expand file tree
/
Copy pathioManager.js
More file actions
76 lines (67 loc) · 3.03 KB
/
Copy pathioManager.js
File metadata and controls
76 lines (67 loc) · 3.03 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
const mongoose = require("mongoose");
const Doc = require("./models/doc");
const { readToken } = require("./config/auth");
/**
* Live editing. Each doc is a room: edits (Quill deltas) go to everyone else in
* it, and saves write the whole document. Only the owner and collaborators (people
* who opened the share link) can join.
*/
module.exports = function attach(io) {
// Every socket must carry a valid login token.
io.use((socket, next) => {
const user = readToken(socket.handshake.auth && socket.handshake.auth.token);
if (!user) return next(new Error("unauthorized"));
socket.data.user = user;
next();
});
io.on("connection", (socket) => {
const { user } = socket.data;
socket.on("get-doc", async (docId, reply = () => {}) => {
try {
if (!mongoose.isValidObjectId(docId)) return reply({ error: "That doc doesn't exist." });
const doc = await Doc.findById(docId);
if (!doc) return reply({ error: "That doc doesn't exist." });
if (!doc.canEdit(user._id)) return reply({ error: "This doc hasn't been shared with you." });
if (socket.data.docId) leave(socket);
socket.data.docId = String(doc._id);
await socket.join(socket.data.docId);
reply({ content: doc.content, name: doc.name });
sendPresence(io, socket.data.docId);
} catch (err) {
console.error("get-doc failed:", err.message);
reply({ error: "Couldn't open the doc. Please try again." });
}
});
socket.on("send-changes", (delta) => {
if (socket.data.docId) socket.to(socket.data.docId).emit("receive-changes", delta);
});
socket.on("save-doc", async ({ content, wordCount } = {}, reply = () => {}) => {
const docId = socket.data.docId;
if (!docId || !content || !Array.isArray(content.ops)) return reply({ error: "Nothing to save." });
try {
await Doc.updateOne(
{ _id: docId, $or: [{ user: user._id }, { collaborators: user._id }] },
{ content, wordCount: Math.max(0, Number(wordCount) || 0) }
);
reply({ ok: true, savedAt: Date.now() });
} catch (err) {
console.error("save-doc failed:", err.message);
reply({ error: "Couldn't save. Your changes are still here; we'll retry." });
}
});
socket.on("disconnect", () => leave(socket));
});
};
function leave(socket) {
const docId = socket.data.docId;
if (!docId) return;
socket.leave(docId);
socket.data.docId = null;
sendPresence(socket.nsp.server, docId);
}
// How many different people have the doc open.
async function sendPresence(io, docId) {
const sockets = await io.in(docId).fetchSockets();
const names = [...new Map(sockets.map((s) => [s.data.user._id, s.data.user.name])).values()];
io.to(docId).emit("presence", names);
}