Compare commits

..

No commits in common. "spyX" and "main" have entirely different histories.
spyX ... main

6 changed files with 17 additions and 254 deletions

BIN
bun.lockb

Binary file not shown.

View File

@ -15,8 +15,7 @@
"bcrypt": "^5.1.1", "bcrypt": "^5.1.1",
"elysia": "latest", "elysia": "latest",
"elysia-helmet": "^1.0.2", "elysia-helmet": "^1.0.2",
"mongoose": "^8.1.0", "mongoose": "^8.1.0"
"uuid": "^9.0.1"
}, },
"devDependencies": { "devDependencies": {
"bun-types": "latest" "bun-types": "latest"

View File

@ -4,8 +4,7 @@ import { cors } from '@elysiajs/cors';
import { helmet } from 'elysia-helmet'; import { helmet } from 'elysia-helmet';
import { jwt } from '@elysiajs/jwt'; import { jwt } from '@elysiajs/jwt';
import { logger } from '@grotto/logysia'; import { logger } from '@grotto/logysia';
import { v4 } from 'uuid'; import './config/mongo';
// import './config/mongo';
import { isAuthenticatedHttp, isAuthenticatedWS } from './middleware/jwt'; import { isAuthenticatedHttp, isAuthenticatedWS } from './middleware/jwt';
// Routers // Routers
@ -13,10 +12,7 @@ import authRouter from './modules/auth';
import userRouter from './modules/user'; import userRouter from './modules/user';
// Ws // Ws
import ClientHandle from "./ws/clientHandle"; import { getDevices } from './ws/device';
import ClientConnectionManager from "./ws/clientConnectionManager";
const clientConnectionManager = new ClientConnectionManager();
const app = new Elysia() const app = new Elysia()
.use(swagger({ .use(swagger({
@ -51,25 +47,17 @@ const app = new Elysia()
// ws dùng hệ thống auth riêng nên không phải đặt trước onBeforeHandle auth của http // ws dùng hệ thống auth riêng nên không phải đặt trước onBeforeHandle auth của http
.ws('/ws', { .ws('/ws', {
body: t.Object({ body: t.Object({
type: t.Optional(t.String()), type: t.String(),
content: t.Optional(t.Any()), d: t.String(),
}), }),
open: (ws) => {
const idSocket = v4();
const clientHandle = new ClientHandle(idSocket, clientConnectionManager);
clientHandle.initialize(ws);
clientConnectionManager.addClient(idSocket, clientHandle);
ws.data.store.clientHandle = clientHandle;
},
close: (ws) => {
ws.data.store.clientHandle.handleClientDisconnection();
},
message(ws, message) { message(ws, message) {
ws.data.store.clientHandle.handleClientMessage(message); switch(message.type) {
case 'get-device':
getDevices(ws, message.d);
}
}, },
response: t.String(),
maxPayloadLength: 1024 * 1024, // 1 MB maxPayloadLength: 1024 * 1024, // 1 MB
// beforeHandle: ({ headers, jwt, set, store }) => isAuthenticatedWS({ headers, jwt, set, store }), beforeHandle: ({ headers, jwt, set, store }) => isAuthenticatedWS({ headers, jwt, set, store }),
}) })
// không auth // không auth
.use(authRouter) .use(authRouter)

View File

@ -1,41 +0,0 @@
export default class ClientConnectionManager {
instance;
clients = new Map()
getClient(id) {
return this.clients.get(id)
}
addClient(id, client) {
if (client.connectionState !== 'OPEN') {
console.warn(`WebSocket is not open for client ${id}. Retrying...`)
setTimeout(() => this.addClient(id, client), 100) // Retry after 100ms
return
}
this.clients.set(id, client)
}
removeClient(id) {
const client = this.clients.get(id)
if (!client) {
return
}
// Close the WebSocket, regardless of its state
client.close()
// Remove from all maps
this.clients.delete(id)
}
isClientActive(id) {
const client = this.clients.get(id)
return client !== undefined && client.connectionState === 'OPEN'
}
getAllClients() {
return Array.from(this.clients.values()).filter(
(clientHandler) => clientHandler.ws.raw.readyState === WebSocket.OPEN,
)
}
}

View File

@ -1,190 +0,0 @@
class ClientHandle {
id;
ws;
clientConnectionManager;
connectionState = "CLOSE";
constructor(id, clientConnectionManager) {
this.id = id;
this.clientConnectionManager = clientConnectionManager;
}
initialize(ws) {
if (!ws) {
return;
}
this.ws = ws;
this.connectionState = "OPEN"; // Set the state to OPEN here
}
isValidJSON(json) {
json = JSON.stringify(json);
return /^[\],:{}\s]*$/.test(
json
.replace(/\\["\\\/bfnrtu]/g, "@")
.replace(
/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,
"]"
)
.replace(/(?:^|:|,)(?:\s*\[)+/g, "")
);
}
handleClientMessage(message) {
if (!this.isValidJSON(message)) {
return this.sendToClient({ ok: 0, e: "Không đúng định dạng JSON" });
}
const { type, content } = message;
switch (type) {
case "info": {
this.sendToClient({
type: "info",
content: { ok: 1, d: this.id },
});
break;
}
case "devices-lists": {
const allClient = this.clientConnectionManager.getAllClients();
const idList = [];
allClient.forEach((client) => {
if (client.id !== this.id) {
idList.push(client.id);
}
});
this.sendToClient({
type: "devices-lists",
content: { ok: 1, d: idList },
});
break;
}
case "send-to-web-offer": {
const { toDevice, offer } = content;
this.clientConnectionManager.getClient(toDevice)?.sendToClient({
type: "send-to-web-offer",
content: {
offer,
fromDevice: this.id,
},
});
break;
}
case "send-to-web-answer": {
const { toDevice, answer } = content;
this.clientConnectionManager.getClient(toDevice)?.sendToClient({
type: "send-to-web-answer",
content: {
answer,
fromDevice: this.id,
},
});
break;
}
case "ice": {
const { toDevice, ice } = content;
this.clientConnectionManager.getClient(toDevice)?.sendToClient({
type: "ice",
content: {
ice,
},
});
break;
}
case "start": {
const { toDevice, fromDevice } = content;
this.clientConnectionManager.getClient(toDevice)?.sendToClient({
type: "start",
content: {
fromDevice,
},
});
break;
}
case "stop": {
const { toDevice } = content;
this.clientConnectionManager.getClient(toDevice)?.sendToClient({
type: "stop",
content: {},
});
break;
}
case "actionmouse": {
const { toDevice, type, x, y } = content;
this.clientConnectionManager.getClient(toDevice)?.sendToClient({
type: "actionmouse",
content: { type, x, y },
});
break;
}
case "action": {
const { toDevice } = content;
this.clientConnectionManager.getClient(toDevice)?.sendToClient({
type: "action",
content: {
type: content.type,
},
});
}
case "start_stream_socket": {
const { toDevice, fromDevice } = content;
this.clientConnectionManager.getClient(toDevice)?.sendToClient({
type: "start_stream_socket",
content: { fromDevice },
});
break;
}
case "frame_stream_socket": {
const { toDevice, image } = content;
this.clientConnectionManager.getClient(toDevice)?.sendToClient({
type: "frame_stream_socket",
content: { image },
});
break;
}
case "stop_stream_socket": {
const { toDevice } = content;
this.clientConnectionManager.getClient(toDevice)?.sendToClient({
type: "stop_stream_socket",
content: {},
});
break;
}
default:
break;
}
}
handleClientDisconnection() {
this.clientConnectionManager.removeClient(this.id.toString());
this.connectionState = "CLOSED";
}
sendToClient(data) {
try {
if (this.ws && this.connectionState === "OPEN") {
this.ws.send(JSON.stringify(data));
} else {
this.clientConnectionManager.removeClient(this.id.toString());
}
} catch (error) {
// Handle error
}
}
close() {
try {
if (this.ws && this.ws.readyState === this.ws.OPEN) {
this.sendToClient({
type: "a_device_disconnect",
content: {
device: this.id,
},
});
this.ws.close();
}
} catch (error) {}
}
}
export default ClientHandle;

7
src/ws/device.js Normal file
View File

@ -0,0 +1,7 @@
async function getDevices(ws, d) {
const { data: { store } } = ws;
console.log(store);
ws.send({ d });
}
export { getDevices };