Collaborative editing (#1660)
This commit is contained in:
28
server/services/admin.js
Normal file
28
server/services/admin.js
Normal file
@@ -0,0 +1,28 @@
|
||||
// @flow
|
||||
import http from "http";
|
||||
import { createBullBoard } from "@bull-board/api";
|
||||
import { BullAdapter } from "@bull-board/api/bullAdapter";
|
||||
import { KoaAdapter } from "@bull-board/koa";
|
||||
import Koa from "koa";
|
||||
import {
|
||||
emailsQueue,
|
||||
globalEventQueue,
|
||||
processorEventQueue,
|
||||
websocketsQueue,
|
||||
} from "../queues";
|
||||
|
||||
export default function init(app: Koa, server?: http.Server) {
|
||||
const serverAdapter = new KoaAdapter();
|
||||
createBullBoard({
|
||||
queues: [
|
||||
new BullAdapter(globalEventQueue),
|
||||
new BullAdapter(processorEventQueue),
|
||||
new BullAdapter(emailsQueue),
|
||||
new BullAdapter(websocketsQueue),
|
||||
],
|
||||
serverAdapter,
|
||||
});
|
||||
|
||||
serverAdapter.setBasePath("/admin");
|
||||
app.use(serverAdapter.registerPlugin());
|
||||
}
|
||||
37
server/services/collaboration.js
Normal file
37
server/services/collaboration.js
Normal file
@@ -0,0 +1,37 @@
|
||||
// @flow
|
||||
import http from "http";
|
||||
import { Logger } from "@hocuspocus/extension-logger";
|
||||
import { Server } from "@hocuspocus/server";
|
||||
import Koa from "koa";
|
||||
import websocket from "koa-easy-ws";
|
||||
import Router from "koa-router";
|
||||
import AuthenticationExtension from "../collaboration/authentication";
|
||||
import PersistenceExtension from "../collaboration/persistence";
|
||||
|
||||
export default function init(app: Koa, server: http.Server) {
|
||||
const router = new Router();
|
||||
|
||||
const hocuspocus = Server.configure({
|
||||
extensions: [
|
||||
new AuthenticationExtension(),
|
||||
new PersistenceExtension(),
|
||||
new Logger(),
|
||||
],
|
||||
});
|
||||
|
||||
// Websockets for collaborative editing
|
||||
router.get("/collaboration/:documentName", async (ctx) => {
|
||||
let { documentName } = ctx.params;
|
||||
|
||||
if (ctx.ws) {
|
||||
const ws = await ctx.ws();
|
||||
hocuspocus.handleConnection(ws, ctx.request, documentName);
|
||||
}
|
||||
|
||||
ctx.response.status = 101;
|
||||
});
|
||||
|
||||
app.use(websocket());
|
||||
app.use(router.routes());
|
||||
app.use(router.allowedMethods());
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
// @flow
|
||||
import admin from "./admin";
|
||||
import collaboration from "./collaboration";
|
||||
import web from "./web";
|
||||
import websockets from "./websockets";
|
||||
import worker from "./worker";
|
||||
|
||||
export default { web, websockets, worker };
|
||||
export default { websockets, collaboration, admin, web, worker };
|
||||
|
||||
@@ -7,14 +7,12 @@ import {
|
||||
referrerPolicy,
|
||||
} from "koa-helmet";
|
||||
import mount from "koa-mount";
|
||||
import onerror from "koa-onerror";
|
||||
import enforceHttps from "koa-sslify";
|
||||
import emails from "../emails";
|
||||
import env from "../env";
|
||||
import routes from "../routes";
|
||||
import api from "../routes/api";
|
||||
import auth from "../routes/auth";
|
||||
import Sentry from "../sentry";
|
||||
|
||||
const isProduction = env.NODE_ENV === "production";
|
||||
const isTest = env.NODE_ENV === "test";
|
||||
@@ -101,44 +99,6 @@ export default function init(app: Koa = new Koa(), server?: http.Server): Koa {
|
||||
app.use(mount("/emails", emails));
|
||||
}
|
||||
|
||||
// catch errors in one place, automatically set status and response headers
|
||||
onerror(app);
|
||||
|
||||
app.on("error", (error, ctx) => {
|
||||
// we don't need to report every time a request stops to the bug tracker
|
||||
if (error.code === "EPIPE" || error.code === "ECONNRESET") {
|
||||
console.warn("Connection error", { error });
|
||||
return;
|
||||
}
|
||||
|
||||
if (process.env.SENTRY_DSN) {
|
||||
Sentry.withScope(function (scope) {
|
||||
const requestId = ctx.headers["x-request-id"];
|
||||
if (requestId) {
|
||||
scope.setTag("request_id", requestId);
|
||||
}
|
||||
|
||||
const authType = ctx.state ? ctx.state.authType : undefined;
|
||||
if (authType) {
|
||||
scope.setTag("auth_type", authType);
|
||||
}
|
||||
|
||||
const userId =
|
||||
ctx.state && ctx.state.user ? ctx.state.user.id : undefined;
|
||||
if (userId) {
|
||||
scope.setUser({ id: userId });
|
||||
}
|
||||
|
||||
scope.addEventProcessor(function (event) {
|
||||
return Sentry.Handlers.parseRequest(event, ctx.request);
|
||||
});
|
||||
Sentry.captureException(error);
|
||||
});
|
||||
} else {
|
||||
console.error(error);
|
||||
}
|
||||
});
|
||||
|
||||
app.use(mount("/auth", auth));
|
||||
app.use(mount("/api", api));
|
||||
|
||||
|
||||
@@ -10,14 +10,14 @@ import policy from "../policies";
|
||||
import { websocketsQueue } from "../queues";
|
||||
import WebsocketsProcessor from "../queues/processors/websockets";
|
||||
import { client, subscriber } from "../redis";
|
||||
import Sentry from "../sentry";
|
||||
import { getUserForJWT } from "../utils/jwt";
|
||||
import * as metrics from "../utils/metrics";
|
||||
import Sentry from "../utils/sentry";
|
||||
|
||||
const { can } = policy;
|
||||
const websockets = new WebsocketsProcessor();
|
||||
|
||||
export default function init(app: Koa, server: http.Server) {
|
||||
// Websockets for events and non-collaborative documents
|
||||
const io = IO(server, {
|
||||
path: "/realtime",
|
||||
serveClient: false,
|
||||
@@ -226,6 +226,9 @@ export default function init(app: Koa, server: http.Server) {
|
||||
},
|
||||
});
|
||||
|
||||
// Handle events from event queue that should be sent to the clients down ws
|
||||
const websockets = new WebsocketsProcessor();
|
||||
|
||||
websocketsQueue.process(async function websocketEventsProcessor(job) {
|
||||
const event = job.data;
|
||||
websockets.on(event, io).catch((error) => {
|
||||
|
||||
@@ -16,7 +16,7 @@ import Imports from "../queues/processors/imports";
|
||||
import Notifications from "../queues/processors/notifications";
|
||||
import Revisions from "../queues/processors/revisions";
|
||||
import Slack from "../queues/processors/slack";
|
||||
import Sentry from "../sentry";
|
||||
import Sentry from "../utils/sentry";
|
||||
|
||||
const log = debug("queue");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user