fix: Incorrect error shown to user when connection limit is reached (#5695)
This commit is contained in:
@@ -14,15 +14,48 @@ function ConnectionStatus() {
|
|||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const codeToMessage = {
|
||||||
|
1009: {
|
||||||
|
title: t("Document is too large"),
|
||||||
|
body: t(
|
||||||
|
"This document has reached the maximum size and can no longer be edited"
|
||||||
|
),
|
||||||
|
},
|
||||||
|
4401: {
|
||||||
|
title: t("Authentication failed"),
|
||||||
|
body: t("Please try logging out and back in again"),
|
||||||
|
},
|
||||||
|
4403: {
|
||||||
|
title: t("Authorization failed"),
|
||||||
|
body: t("You may have lost access to this document, try reloading"),
|
||||||
|
},
|
||||||
|
4503: {
|
||||||
|
title: t("Too many users connected to document"),
|
||||||
|
body: t("Your edits will sync once other users leave the document"),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const message = ui.multiplayerErrorCode
|
||||||
|
? codeToMessage[ui.multiplayerErrorCode]
|
||||||
|
: undefined;
|
||||||
|
|
||||||
return ui.multiplayerStatus === "connecting" ||
|
return ui.multiplayerStatus === "connecting" ||
|
||||||
ui.multiplayerStatus === "disconnected" ? (
|
ui.multiplayerStatus === "disconnected" ? (
|
||||||
<Tooltip
|
<Tooltip
|
||||||
tooltip={
|
tooltip={
|
||||||
<Centered>
|
message ? (
|
||||||
<strong>{t("Server connection lost")}</strong>
|
<Centered>
|
||||||
<br />
|
<strong>{message.title}</strong>
|
||||||
{t("Edits you make will sync once you’re online")}
|
<br />
|
||||||
</Centered>
|
{message.body}
|
||||||
|
</Centered>
|
||||||
|
) : (
|
||||||
|
<Centered>
|
||||||
|
<strong>{t("Server connection lost")}</strong>
|
||||||
|
<br />
|
||||||
|
{t("Edits you make will sync once you’re online")}
|
||||||
|
</Centered>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
placement="bottom"
|
placement="bottom"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -135,13 +135,10 @@ function MultiplayerEditor({ onSynced, ...props }: Props, ref: any) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
provider.on("close", (ev: MessageEvent) => {
|
provider.on("close", (ev: MessageEvent) => {
|
||||||
if ("code" in ev.event && ev.event.code === 1009) {
|
if ("code" in ev.event) {
|
||||||
provider.shouldConnect = false;
|
provider.shouldConnect =
|
||||||
showToast(
|
ev.event.code !== 1009 && ev.event.code !== 4401;
|
||||||
t(
|
ui.setMultiplayerStatus("disconnected", ev.event.code);
|
||||||
"Sorry, this document is too large - edits will no longer be persisted."
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -164,9 +161,11 @@ function MultiplayerEditor({ onSynced, ...props }: Props, ref: any) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
provider.on("status", (ev: ConnectionStatusEvent) =>
|
provider.on("status", (ev: ConnectionStatusEvent) => {
|
||||||
ui.setMultiplayerStatus(ev.status)
|
if (ui.multiplayerStatus !== ev.status) {
|
||||||
);
|
ui.setMultiplayerStatus(ev.status, undefined);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
setRemoteProvider(provider);
|
setRemoteProvider(provider);
|
||||||
|
|
||||||
@@ -177,7 +176,7 @@ function MultiplayerEditor({ onSynced, ...props }: Props, ref: any) {
|
|||||||
provider?.destroy();
|
provider?.destroy();
|
||||||
void localProvider?.destroy();
|
void localProvider?.destroy();
|
||||||
setRemoteProvider(null);
|
setRemoteProvider(null);
|
||||||
ui.setMultiplayerStatus(undefined);
|
ui.setMultiplayerStatus(undefined, undefined);
|
||||||
};
|
};
|
||||||
}, [
|
}, [
|
||||||
history,
|
history,
|
||||||
|
|||||||
@@ -69,6 +69,9 @@ class UiStore {
|
|||||||
@observable
|
@observable
|
||||||
multiplayerStatus: ConnectionStatus;
|
multiplayerStatus: ConnectionStatus;
|
||||||
|
|
||||||
|
@observable
|
||||||
|
multiplayerErrorCode?: number;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
// Rehydrate
|
// Rehydrate
|
||||||
const data: Partial<UiStore> = Storage.get(UI_STORE) || {};
|
const data: Partial<UiStore> = Storage.get(UI_STORE) || {};
|
||||||
@@ -133,8 +136,12 @@ class UiStore {
|
|||||||
};
|
};
|
||||||
|
|
||||||
@action
|
@action
|
||||||
setMultiplayerStatus = (status: ConnectionStatus): void => {
|
setMultiplayerStatus = (
|
||||||
|
status: ConnectionStatus,
|
||||||
|
errorCode?: number
|
||||||
|
): void => {
|
||||||
this.multiplayerStatus = status;
|
this.multiplayerStatus = status;
|
||||||
|
this.multiplayerErrorCode = errorCode;
|
||||||
};
|
};
|
||||||
|
|
||||||
@action
|
@action
|
||||||
|
|||||||
4
server/collaboration/CloseEvents.ts
Normal file
4
server/collaboration/CloseEvents.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
export const TooManyConnections = {
|
||||||
|
code: 4503,
|
||||||
|
reason: "Too Many Connections",
|
||||||
|
};
|
||||||
@@ -6,28 +6,36 @@ import {
|
|||||||
import env from "@server/env";
|
import env from "@server/env";
|
||||||
import Logger from "@server/logging/Logger";
|
import Logger from "@server/logging/Logger";
|
||||||
import { trace } from "@server/logging/tracing";
|
import { trace } from "@server/logging/tracing";
|
||||||
|
import { TooManyConnections } from "./CloseEvents";
|
||||||
|
|
||||||
@trace()
|
@trace()
|
||||||
export class ConnectionLimitExtension implements Extension {
|
export class ConnectionLimitExtension implements Extension {
|
||||||
/**
|
/**
|
||||||
* Map of documentId -> connection count
|
* Map of documentId -> connection count
|
||||||
*/
|
*/
|
||||||
connectionsByDocument: Map<string, number> = new Map();
|
connectionsByDocument: Map<string, Set<string>> = new Map();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* onDisconnect hook
|
* onDisconnect hook
|
||||||
* @param data The disconnect payload
|
* @param data The disconnect payload
|
||||||
*/
|
*/
|
||||||
onDisconnect(data: onDisconnectPayload) {
|
onDisconnect(data: onDisconnectPayload) {
|
||||||
const { documentName } = data;
|
const { documentName, socketId } = data;
|
||||||
|
|
||||||
const currConnections = this.connectionsByDocument.get(documentName) || 0;
|
const connections = this.connectionsByDocument.get(documentName);
|
||||||
const newConnections = currConnections - 1;
|
if (connections) {
|
||||||
this.connectionsByDocument.set(documentName, newConnections);
|
connections.delete(socketId);
|
||||||
|
|
||||||
|
if (connections.size === 0) {
|
||||||
|
this.connectionsByDocument.delete(documentName);
|
||||||
|
} else {
|
||||||
|
this.connectionsByDocument.set(documentName, connections);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Logger.debug(
|
Logger.debug(
|
||||||
"multiplayer",
|
"multiplayer",
|
||||||
`${newConnections} connections to "${documentName}"`
|
`${connections?.size} connections to "${documentName}"`
|
||||||
);
|
);
|
||||||
|
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
@@ -40,23 +48,24 @@ export class ConnectionLimitExtension implements Extension {
|
|||||||
onConnect(data: onConnectPayload) {
|
onConnect(data: onConnectPayload) {
|
||||||
const { documentName } = data;
|
const { documentName } = data;
|
||||||
|
|
||||||
const currConnections = this.connectionsByDocument.get(documentName) || 0;
|
const connections =
|
||||||
if (currConnections >= env.COLLABORATION_MAX_CLIENTS_PER_DOCUMENT) {
|
this.connectionsByDocument.get(documentName) || new Set();
|
||||||
|
if (connections?.size >= env.COLLABORATION_MAX_CLIENTS_PER_DOCUMENT) {
|
||||||
Logger.info(
|
Logger.info(
|
||||||
"multiplayer",
|
"multiplayer",
|
||||||
`Rejected connection to "${documentName}" because it has reached the maximum number of connections`
|
`Rejected connection to "${documentName}" because it has reached the maximum number of connections`
|
||||||
);
|
);
|
||||||
|
|
||||||
// Rejecting the promise will cause the connection to be dropped.
|
// Rejecting the promise will cause the connection to be dropped.
|
||||||
return Promise.reject();
|
return Promise.reject(TooManyConnections);
|
||||||
}
|
}
|
||||||
|
|
||||||
const newConnections = currConnections + 1;
|
connections.add(data.socketId);
|
||||||
this.connectionsByDocument.set(documentName, newConnections);
|
this.connectionsByDocument.set(documentName, connections);
|
||||||
|
|
||||||
Logger.debug(
|
Logger.debug(
|
||||||
"multiplayer",
|
"multiplayer",
|
||||||
`${newConnections} connections to "${documentName}"`
|
`${connections.size} connections to "${documentName}"`
|
||||||
);
|
);
|
||||||
|
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
|
|||||||
@@ -117,6 +117,14 @@
|
|||||||
"Type a command or search": "Type a command or search",
|
"Type a command or search": "Type a command or search",
|
||||||
"Are you sure you want to permanently delete this entire comment thread?": "Are you sure you want to permanently delete this entire comment thread?",
|
"Are you sure you want to permanently delete this entire comment thread?": "Are you sure you want to permanently delete this entire comment thread?",
|
||||||
"Are you sure you want to permanently delete this comment?": "Are you sure you want to permanently delete this comment?",
|
"Are you sure you want to permanently delete this comment?": "Are you sure you want to permanently delete this comment?",
|
||||||
|
"Document is too large": "Document is too large",
|
||||||
|
"This document has reached the maximum size and can no longer be edited": "This document has reached the maximum size and can no longer be edited",
|
||||||
|
"Authentication failed": "Authentication failed",
|
||||||
|
"Please try logging out and back in again": "Please try logging out and back in again",
|
||||||
|
"Authorization failed": "Authorization failed",
|
||||||
|
"You may have lost access to this document, try reloading": "You may have lost access to this document, try reloading",
|
||||||
|
"Too many users connected to document": "Too many users connected to document",
|
||||||
|
"Your edits will sync once other users leave the document": "Your edits will sync once other users leave the document",
|
||||||
"Server connection lost": "Server connection lost",
|
"Server connection lost": "Server connection lost",
|
||||||
"Edits you make will sync once you’re online": "Edits you make will sync once you’re online",
|
"Edits you make will sync once you’re online": "Edits you make will sync once you’re online",
|
||||||
"Submenu": "Submenu",
|
"Submenu": "Submenu",
|
||||||
@@ -524,7 +532,6 @@
|
|||||||
"Viewed {{ count }} times by {{ teamMembers }} people_plural": "Viewed {{ count }} times by {{ teamMembers }} people",
|
"Viewed {{ count }} times by {{ teamMembers }} people_plural": "Viewed {{ count }} times by {{ teamMembers }} people",
|
||||||
"Viewer insights": "Viewer insights",
|
"Viewer insights": "Viewer insights",
|
||||||
"As an admin you can manage if team members can see who has viewed this document": "As an admin you can manage if team members can see who has viewed this document",
|
"As an admin you can manage if team members can see who has viewed this document": "As an admin you can manage if team members can see who has viewed this document",
|
||||||
"Sorry, this document is too large - edits will no longer be persisted.": "Sorry, this document is too large - edits will no longer be persisted.",
|
|
||||||
"Sorry, the last change could not be persisted – please reload the page": "Sorry, the last change could not be persisted – please reload the page",
|
"Sorry, the last change could not be persisted – please reload the page": "Sorry, the last change could not be persisted – please reload the page",
|
||||||
"This template will be permanently deleted in <2></2> unless restored.": "This template will be permanently deleted in <2></2> unless restored.",
|
"This template will be permanently deleted in <2></2> unless restored.": "This template will be permanently deleted in <2></2> unless restored.",
|
||||||
"This document will be permanently deleted in <2></2> unless restored.": "This document will be permanently deleted in <2></2> unless restored.",
|
"This document will be permanently deleted in <2></2> unless restored.": "This document will be permanently deleted in <2></2> unless restored.",
|
||||||
|
|||||||
Reference in New Issue
Block a user