broadcast synced
This commit is contained in:
parent
3da02a89a7
commit
3bc56bf91e
8 changed files with 170 additions and 42 deletions
51
package-lock.json
generated
51
package-lock.json
generated
|
@ -35,7 +35,8 @@
|
||||||
"speakeasy": "^2.0.0",
|
"speakeasy": "^2.0.0",
|
||||||
"sqlite3": "^5.1.7",
|
"sqlite3": "^5.1.7",
|
||||||
"typeorm": "^0.3.20",
|
"typeorm": "^0.3.20",
|
||||||
"uuid": "^10.0.0"
|
"uuid": "^10.0.0",
|
||||||
|
"yjs": "^13.6.23"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/cors": "^2.8.14",
|
"@types/cors": "^2.8.14",
|
||||||
|
@ -2163,6 +2164,16 @@
|
||||||
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
|
||||||
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="
|
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="
|
||||||
},
|
},
|
||||||
|
"node_modules/isomorphic.js": {
|
||||||
|
"version": "0.2.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/isomorphic.js/-/isomorphic.js-0.2.5.tgz",
|
||||||
|
"integrity": "sha512-PIeMbHqMt4DnUP3MA/Flc0HElYjMXArsw1qwJZcm9sqR8mq3l8NYizFMty0pWwE/tzIGH3EKK5+jes5mAr85yw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"funding": {
|
||||||
|
"type": "GitHub Sponsors ❤",
|
||||||
|
"url": "https://github.com/sponsors/dmonad"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/jackspeak": {
|
"node_modules/jackspeak": {
|
||||||
"version": "3.4.3",
|
"version": "3.4.3",
|
||||||
"resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz",
|
"resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz",
|
||||||
|
@ -2220,6 +2231,27 @@
|
||||||
"safe-buffer": "^5.0.1"
|
"safe-buffer": "^5.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/lib0": {
|
||||||
|
"version": "0.2.99",
|
||||||
|
"resolved": "https://registry.npmjs.org/lib0/-/lib0-0.2.99.tgz",
|
||||||
|
"integrity": "sha512-vwztYuUf1uf/1zQxfzRfO5yzfNKhTtgOByCruuiQQxWQXnPb8Itaube5ylofcV0oM0aKal9Mv+S1s1Ky0UYP1w==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"isomorphic.js": "^0.2.4"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"0ecdsa-generate-keypair": "bin/0ecdsa-generate-keypair.js",
|
||||||
|
"0gentesthtml": "bin/gentesthtml.js",
|
||||||
|
"0serve": "bin/0serve.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "GitHub Sponsors ❤",
|
||||||
|
"url": "https://github.com/sponsors/dmonad"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/locate-path": {
|
"node_modules/locate-path": {
|
||||||
"version": "5.0.0",
|
"version": "5.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
|
||||||
|
@ -4799,6 +4831,23 @@
|
||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/yjs": {
|
||||||
|
"version": "13.6.23",
|
||||||
|
"resolved": "https://registry.npmjs.org/yjs/-/yjs-13.6.23.tgz",
|
||||||
|
"integrity": "sha512-ExtnT5WIOVpkL56bhLeisG/N5c4fmzKn4k0ROVfJa5TY2QHbH7F0Wu2T5ZhR7ErsFWQEFafyrnSI8TPKVF9Few==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"lib0": "^0.2.99"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16.0.0",
|
||||||
|
"npm": ">=8.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "GitHub Sponsors ❤",
|
||||||
|
"url": "https://github.com/sponsors/dmonad"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/yn": {
|
"node_modules/yn": {
|
||||||
"version": "3.1.1",
|
"version": "3.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
|
||||||
|
|
|
@ -50,7 +50,8 @@
|
||||||
"speakeasy": "^2.0.0",
|
"speakeasy": "^2.0.0",
|
||||||
"sqlite3": "^5.1.7",
|
"sqlite3": "^5.1.7",
|
||||||
"typeorm": "^0.3.20",
|
"typeorm": "^0.3.20",
|
||||||
"uuid": "^10.0.0"
|
"uuid": "^10.0.0",
|
||||||
|
"yjs": "^13.6.23"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/cors": "^2.8.14",
|
"@types/cors": "^2.8.14",
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
|
import * as Y from "yjs";
|
||||||
|
|
||||||
export interface missionStoreModel {
|
export interface missionStoreModel {
|
||||||
missionId: string;
|
missionId: string;
|
||||||
doc: any;
|
doc: Y.Doc;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { Server, Socket } from "socket.io";
|
import { Server, Socket } from "socket.io";
|
||||||
import { SocketMap } from "../../storage/socketMap";
|
import { SocketMap } from "../../storage/socketMap";
|
||||||
import CustomRequestException from "../../exceptions/customRequestException";
|
import CustomRequestException from "../../exceptions/customRequestException";
|
||||||
|
import { MissionMap } from "../../storage/missionMap";
|
||||||
|
|
||||||
export default (io: Server, socket: Socket) => {
|
export default (io: Server, socket: Socket) => {
|
||||||
socket.on("ping", (callback: Function) => {
|
socket.on("ping", (callback: Function) => {
|
||||||
|
@ -28,6 +29,13 @@ export default (io: Server, socket: Socket) => {
|
||||||
|
|
||||||
socket.on("disconnecting", () => {
|
socket.on("disconnecting", () => {
|
||||||
console.log("socket disconnection: ", socket.id);
|
console.log("socket disconnection: ", socket.id);
|
||||||
|
const socketRooms = Array.from(socket.rooms).filter((room) => room !== socket.id && room !== "home");
|
||||||
|
socket.leave(socketRooms[0]);
|
||||||
|
|
||||||
|
const clients = io.sockets.adapter.rooms.get(socketRooms[0]);
|
||||||
|
if (!clients || clients.size == 0) {
|
||||||
|
MissionMap.delete(socketRooms[0]);
|
||||||
|
}
|
||||||
SocketMap.delete(socket.id);
|
SocketMap.delete(socket.id);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
101
src/websocket/endpoints/missionManagement.ts
Normal file
101
src/websocket/endpoints/missionManagement.ts
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
import { Server, Socket } from "socket.io";
|
||||||
|
import { handleEvent } from "../handleEvent";
|
||||||
|
import { MissionMap } from "../../storage/missionMap";
|
||||||
|
import * as Y from "yjs";
|
||||||
|
|
||||||
|
export default (io: Server, socket: Socket) => {
|
||||||
|
socket.on(
|
||||||
|
"mission:join",
|
||||||
|
handleEvent(
|
||||||
|
{ type: "read", section: "operation", module: "mission" },
|
||||||
|
async (data: any) => {
|
||||||
|
socket.rooms.forEach((room) => {
|
||||||
|
if (room !== socket.id && room != "home") {
|
||||||
|
socket.leave(room);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
const doc = new Y.Doc();
|
||||||
|
doc.getMap("form");
|
||||||
|
doc.getText("editor");
|
||||||
|
|
||||||
|
// const ymap = ydoc.getMap('myMap');
|
||||||
|
// ymap.set('titel', 'Mein Dokument');
|
||||||
|
// ymap.set('inhalt', 'Hier ist der initiale Inhalt');
|
||||||
|
// ymap.set('erstelltAm', new Date().toISOString());
|
||||||
|
|
||||||
|
// const yarray = ydoc.getArray('meineArray');
|
||||||
|
// yarray.push(['Element 1', 'Element 2', 'Element 3']);
|
||||||
|
|
||||||
|
// const ytext = ydoc.getText('meinText');
|
||||||
|
// ytext.insert(0, 'Hier ist ein initialer Text');
|
||||||
|
|
||||||
|
// get mission data
|
||||||
|
MissionMap.write(data, {
|
||||||
|
missionId: data,
|
||||||
|
doc,
|
||||||
|
});
|
||||||
|
|
||||||
|
const mission = MissionMap.read(data);
|
||||||
|
|
||||||
|
socket.join(data);
|
||||||
|
socket.emit("status-mission:join", { status: "success" });
|
||||||
|
return {
|
||||||
|
type: "package-mission",
|
||||||
|
answer: Y.encodeStateAsUpdate(mission.doc),
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
return { type: "status-join:join", answer: { status: "failed", msg: error.message } };
|
||||||
|
}
|
||||||
|
},
|
||||||
|
socket
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
socket.on(
|
||||||
|
"mission:sync",
|
||||||
|
handleEvent(
|
||||||
|
{ type: "read", section: "operation", module: "mission" },
|
||||||
|
async (data: any) => {
|
||||||
|
const socketRooms = Array.from(socket.rooms).filter((room) => room !== socket.id && room !== "home");
|
||||||
|
try {
|
||||||
|
const mission = MissionMap.read(socketRooms[0]);
|
||||||
|
Y.applyUpdate(mission.doc, new Uint8Array(data));
|
||||||
|
|
||||||
|
socket.emit("status-mission:sync", { status: "success" });
|
||||||
|
return {
|
||||||
|
type: "package-sync",
|
||||||
|
answer: data,
|
||||||
|
room: socketRooms[0],
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
return { type: "status-mission:sync", answer: { status: "failed", msg: error.message } };
|
||||||
|
}
|
||||||
|
},
|
||||||
|
socket
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
socket.on(
|
||||||
|
"mission:leave",
|
||||||
|
handleEvent(
|
||||||
|
{ type: "read", section: "operation", module: "mission" },
|
||||||
|
async (data: any) => {
|
||||||
|
const socketRooms = Array.from(socket.rooms).filter((room) => room !== socket.id && room !== "home");
|
||||||
|
socket.leave(socketRooms[0]);
|
||||||
|
|
||||||
|
const clients = io.sockets.adapter.rooms.get(socketRooms[0]);
|
||||||
|
if (!clients || clients.size == 0) {
|
||||||
|
MissionMap.delete(socketRooms[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: "deleted",
|
||||||
|
answer: "leave instance",
|
||||||
|
};
|
||||||
|
},
|
||||||
|
socket
|
||||||
|
)
|
||||||
|
);
|
||||||
|
};
|
|
@ -1,36 +0,0 @@
|
||||||
import { Server, Socket } from "socket.io";
|
|
||||||
import { SocketMap } from "../../storage/socketMap";
|
|
||||||
import CustomRequestException from "../../exceptions/customRequestException";
|
|
||||||
import { handleEvent } from "../handleEvent";
|
|
||||||
|
|
||||||
export default (io: Server, socket: Socket) => {
|
|
||||||
socket.on(
|
|
||||||
"join:mission",
|
|
||||||
|
|
||||||
handleEvent(
|
|
||||||
{ type: "read", section: "operation", module: "mission" },
|
|
||||||
async (data: any) => {
|
|
||||||
socket.rooms.forEach((room) => {
|
|
||||||
if (room !== socket.id && room != "home") {
|
|
||||||
socket.leave(room);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
try {
|
|
||||||
socket.join(data);
|
|
||||||
socket.emit("status-mission:room", { status: "success" });
|
|
||||||
// get mission data
|
|
||||||
// create yjs sync doc
|
|
||||||
// provide yjs sync doc to client
|
|
||||||
return {
|
|
||||||
type: "package-mission",
|
|
||||||
answer: "mission-data by yjs",
|
|
||||||
};
|
|
||||||
} catch (error) {
|
|
||||||
return { type: "status-join:room", answer: { status: "failed", msg: error.message } };
|
|
||||||
}
|
|
||||||
},
|
|
||||||
socket
|
|
||||||
)
|
|
||||||
);
|
|
||||||
};
|
|
|
@ -38,7 +38,10 @@ export let handleEvent = (
|
||||||
throw new ForbiddenRequestException(`missing admin permission`);
|
throw new ForbiddenRequestException(`missing admin permission`);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!PermissionHelper.can(socketData.permissions, permissions.type, permissions.section, permissions.module)) {
|
if (
|
||||||
|
!socketData.isOwner &&
|
||||||
|
!PermissionHelper.can(socketData.permissions, permissions.type, permissions.section, permissions.module)
|
||||||
|
) {
|
||||||
throw new ForbiddenRequestException(`missing required permission`);
|
throw new ForbiddenRequestException(`missing required permission`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { Server } from "socket.io";
|
||||||
import authenticateSocket from "./middleware/authenticateSocket";
|
import authenticateSocket from "./middleware/authenticateSocket";
|
||||||
import base from "./endpoints/base";
|
import base from "./endpoints/base";
|
||||||
import perRequestCheck from "./middleware/perRequestCheck";
|
import perRequestCheck from "./middleware/perRequestCheck";
|
||||||
import roomManagement from "./endpoints/roomManagement";
|
import missionManagement from "./endpoints/missionManagement";
|
||||||
|
|
||||||
export default abstract class SocketServer {
|
export default abstract class SocketServer {
|
||||||
private static io: Server;
|
private static io: Server;
|
||||||
|
@ -28,7 +28,7 @@ export default abstract class SocketServer {
|
||||||
socket.use((packet, next) => perRequestCheck(socket, packet, next));
|
socket.use((packet, next) => perRequestCheck(socket, packet, next));
|
||||||
|
|
||||||
base(this.io, socket);
|
base(this.io, socket);
|
||||||
roomManagement(this.io, socket);
|
missionManagement(this.io, socket);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue