feat: socketio event

This commit is contained in:
Methapon2001 2023-12-07 17:25:56 +07:00
parent 0a1265b78c
commit 25752dac19
No known key found for this signature in database
GPG key ID: 849924FEF46BD132
9 changed files with 200 additions and 8 deletions

View file

@ -28,6 +28,7 @@
"minio": "^7.1.3",
"prettier": "^3.1.0",
"promise.any": "^2.0.6",
"socket.io": "^4.7.2",
"swagger-ui-express": "^5.0.0",
"tsoa": "^5.1.1"
},

View file

@ -38,6 +38,9 @@ dependencies:
promise.any:
specifier: ^2.0.6
version: 2.0.6
socket.io:
specifier: ^4.7.2
version: 4.7.2
swagger-ui-express:
specifier: ^5.0.0
version: 5.0.0(express@4.18.2)
@ -154,6 +157,10 @@ packages:
engines: {node: '>=8'}
dev: false
/@socket.io/component-emitter@3.1.0:
resolution: {integrity: sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==}
dev: false
/@tsconfig/node10@1.0.9:
resolution: {integrity: sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==}
dev: true
@ -215,11 +222,14 @@ packages:
dependencies:
'@types/node': 20.9.0
/@types/cookie@0.4.1:
resolution: {integrity: sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==}
dev: false
/@types/cors@2.8.17:
resolution: {integrity: sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==}
dependencies:
'@types/node': 20.9.0
dev: true
/@types/express-serve-static-core@4.17.41:
resolution: {integrity: sha512-OaJ7XLaelTgrvlZD8/aa0vvvxZdUmlCn6MtWeB7TkiKW70BQLc9XEPpDLPdbo52ZhXUCrznlWdCHWxJWtdyajA==}
@ -420,6 +430,11 @@ packages:
/balanced-match@1.0.2:
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
/base64id@2.0.0:
resolution: {integrity: sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==}
engines: {node: ^4.5.0 || >= 5.9}
dev: false
/binary-extensions@2.2.0:
resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==}
engines: {node: '>=8'}
@ -576,6 +591,11 @@ packages:
resolution: {integrity: sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==}
dev: false
/cookie@0.4.2:
resolution: {integrity: sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==}
engines: {node: '>= 0.6'}
dev: false
/cookie@0.5.0:
resolution: {integrity: sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==}
engines: {node: '>= 0.6'}
@ -706,6 +726,31 @@ packages:
engines: {node: '>= 0.8'}
dev: false
/engine.io-parser@5.2.1:
resolution: {integrity: sha512-9JktcM3u18nU9N2Lz3bWeBgxVgOKpw7yhRaoxQA3FUDZzzw+9WlA6p4G4u0RixNkg14fH7EfEc/RhpurtiROTQ==}
engines: {node: '>=10.0.0'}
dev: false
/engine.io@6.5.4:
resolution: {integrity: sha512-KdVSDKhVKyOi+r5uEabrDLZw2qXStVvCsEB/LN3mw4WFi6Gx50jTyuxYVCwAAC0U46FdnzP/ScKRBTXb/NiEOg==}
engines: {node: '>=10.2.0'}
dependencies:
'@types/cookie': 0.4.1
'@types/cors': 2.8.17
'@types/node': 20.9.0
accepts: 1.3.8
base64id: 2.0.0
cookie: 0.4.2
cors: 2.8.5
debug: 4.3.4
engine.io-parser: 5.2.1
ws: 8.11.0
transitivePeerDependencies:
- bufferutil
- supports-color
- utf-8-validate
dev: false
/es-abstract@1.22.3:
resolution: {integrity: sha512-eiiY8HQeYfYH2Con2berK+To6GrK2RxbPawDkGq4UiCQQfZHb6wX9qQqkbpPqaxQFcl8d9QzZqo0tGE0VcrdwA==}
engines: {node: '>= 0.4'}
@ -1786,6 +1831,42 @@ packages:
semver: 7.5.4
dev: true
/socket.io-adapter@2.5.2:
resolution: {integrity: sha512-87C3LO/NOMc+eMcpcxUBebGjkpMDkNBS9tf7KJqcDsmL936EChtVva71Dw2q4tQcuVC+hAUy4an2NO/sYXmwRA==}
dependencies:
ws: 8.11.0
transitivePeerDependencies:
- bufferutil
- utf-8-validate
dev: false
/socket.io-parser@4.2.4:
resolution: {integrity: sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==}
engines: {node: '>=10.0.0'}
dependencies:
'@socket.io/component-emitter': 3.1.0
debug: 4.3.4
transitivePeerDependencies:
- supports-color
dev: false
/socket.io@4.7.2:
resolution: {integrity: sha512-bvKVS29/I5fl2FGLNHuXlQaUH/BlzX1IN6S+NKLNZpBsPZIDH+90eQmCs2Railn4YUiww4SzUedJ6+uzwFnKLw==}
engines: {node: '>=10.2.0'}
dependencies:
accepts: 1.3.8
base64id: 2.0.0
cors: 2.8.5
debug: 4.3.4
engine.io: 6.5.4
socket.io-adapter: 2.5.2
socket.io-parser: 4.2.4
transitivePeerDependencies:
- bufferutil
- supports-color
- utf-8-validate
dev: false
/source-map@0.6.1:
resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
engines: {node: '>=0.10.0'}
@ -2192,6 +2273,19 @@ packages:
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
dev: false
/ws@8.11.0:
resolution: {integrity: sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==}
engines: {node: '>=10.0.0'}
peerDependencies:
bufferutil: ^4.0.1
utf-8-validate: ^5.0.2
peerDependenciesMeta:
bufferutil:
optional: true
utf-8-validate:
optional: true
dev: false
/xml2js@0.5.0:
resolution: {integrity: sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==}
engines: {node: '>=4.0.0'}

View file

@ -2,6 +2,8 @@ import "dotenv/config";
import express from "express";
import swaggerUi from "swagger-ui-express";
import cors from "cors";
import { createServer } from "http";
import { Server } from "socket.io";
import { RegisterRoutes } from "./routes";
import errorHandler from "./middlewares/exception";
@ -9,6 +11,7 @@ import rabbitmq from "./rabbitmq";
import swaggerSpecs from "./swagger.json";
import { handler as amqHandler } from "./rabbitmq/handler";
import { setInstance } from "./lib/websocket";
const PORT = +(process.env.PORT || 80);
@ -31,7 +34,24 @@ app.use((_req, res, _next) => {
res.sendFile(`${process.cwd()}/static/index.html`);
});
app.listen(PORT, "0.0.0.0", () =>
const server = createServer(app);
const io = new Server(server, {
cors: {
origin: "*",
},
});
setInstance(io);
io.on("connection", (socket) => {
console.log("User Connected");
socket.on("disconnected", () => {
console.log("User Disconnected");
});
});
server.listen(PORT, "0.0.0.0", () =>
console.log(`[APP] Application is running on http://localhost:${PORT}`),
);

View file

@ -22,6 +22,7 @@ import { copyCond, listFolder, listItem, replaceIllegalChars } from "../utils/mi
import HttpStatusCode from "../interfaces/http-status";
import { StorageFile, StorageFolder } from "../interfaces/storage-fs";
import { getInstance } from "../lib/websocket";
const DEFAULT_BUCKET = process.env.MINIO_BUCKET;
const DEFAULT_INDEX = process.env.ELASTICSEARCH_INDEX;
@ -86,6 +87,9 @@ export class CabinetController extends Controller {
if (!created) throw new Error("เกิดข้อผิดพลาดกับระบบจัดการไฟล์");
const io = getInstance();
io?.emit("CreateFolder", { pathname: `${replaceIllegalChars(body.name)}/` });
return this.setStatus(HttpStatusCode.CREATED);
}
@ -155,6 +159,9 @@ export class CabinetController extends Controller {
}),
);
const io = getInstance();
io?.emit("EditFolder", { from: `${cabinetName}/`, to: `${replaceIllegalChars(body.name)}/` });
return this.setStatus(HttpStatusCode.NO_CONTENT);
}
@ -180,6 +187,9 @@ export class CabinetController extends Controller {
stream.on("error", () => reject(new Error("เกิดข้อผิดพลาด ไม่สามารถลบไฟล์ได้")));
});
const io = getInstance();
io?.emit("DeleteFolder", { pathname: `${cabinetName}/` });
return this.setStatus(HttpStatusCode.NO_CONTENT);
}

View file

@ -23,6 +23,7 @@ import { copyCond, listFolder, listItem, pathExist, replaceIllegalChars } from "
import HttpStatusCode from "../interfaces/http-status";
import { StorageFile, StorageFolder } from "../interfaces/storage-fs";
import HttpError from "../interfaces/http-error";
import { getInstance } from "../lib/websocket";
const DEFAULT_BUCKET = process.env.MINIO_BUCKET;
const DEFAULT_INDEX = process.env.ELASTICSEARCH_INDEX;
@ -101,6 +102,9 @@ export class DrawerController extends Controller {
if (!created) throw new Error("เกิดข้อผิดพลาดกับระบบจัดการไฟล์");
const io = getInstance();
io?.emit("CreateFolder", { pathname: `${basePath}${replaceIllegalChars(body.name)}/` });
return this.setStatus(HttpStatusCode.CREATED);
}
@ -172,6 +176,12 @@ export class DrawerController extends Controller {
}),
);
const io = getInstance();
io?.emit("EditFolder", {
from: `${cabinetName}/${drawerName}/`,
to: `${cabinetName}/${replaceIllegalChars(body.name)}/`,
});
return this.setStatus(HttpStatusCode.NO_CONTENT);
}
@ -201,6 +211,11 @@ export class DrawerController extends Controller {
stream.on("error", () => reject(new Error("เกิดข้อผิดพลาด ไม่สามารถลบไฟล์ได้")));
});
const io = getInstance();
io?.emit("DeleteFolder", {
pathname: `${cabinetName}/${drawerName}/`,
});
return this.setStatus(HttpStatusCode.NO_CONTENT);
}

View file

@ -23,6 +23,7 @@ import { copyCond, listFolder, listItem, pathExist, replaceIllegalChars } from "
import HttpStatusCode from "../interfaces/http-status";
import { StorageFile, StorageFolder } from "../interfaces/storage-fs";
import HttpError from "../interfaces/http-error";
import { getInstance } from "../lib/websocket";
const DEFAULT_BUCKET = process.env.MINIO_BUCKET;
const DEFAULT_INDEX = process.env.ELASTICSEARCH_INDEX;
@ -106,6 +107,9 @@ export class FolderController extends Controller {
if (!created) throw new Error("เกิดข้อผิดพลาดกับระบบจัดการไฟล์");
const io = getInstance();
io?.emit("CreateFolder", { pathname: `${basePath}${replaceIllegalChars(body.name)}/` });
return this.setStatus(HttpStatusCode.CREATED);
}
@ -179,6 +183,12 @@ export class FolderController extends Controller {
}),
);
const io = getInstance();
io?.emit("EditFolder", {
from: `${cabinetName}/${drawerName}/${folderName}`,
to: `${cabinetName}/${drawerName}/${replaceIllegalChars(body.name)}/`,
});
return this.setStatus(HttpStatusCode.NO_CONTENT);
}
@ -213,6 +223,9 @@ export class FolderController extends Controller {
stream.on("error", () => reject(new Error("เกิดข้อผิดพลาด ไม่สามารถลบไฟล์ได้")));
});
const io = getInstance();
io?.emit("DeleteFolder", { pathname: `${cabinetName}/${drawerName}/${folderName}/` });
return this.setStatus(HttpStatusCode.NO_CONTENT);
}

View file

@ -23,6 +23,7 @@ import { copyCond, listFolder, listItem, pathExist, replaceIllegalChars } from "
import HttpStatusCode from "../interfaces/http-status";
import { StorageFile, StorageFolder } from "../interfaces/storage-fs";
import HttpError from "../interfaces/http-error";
import { getInstance } from "../lib/websocket";
const DEFAULT_BUCKET = process.env.MINIO_BUCKET;
const DEFAULT_INDEX = process.env.ELASTICSEARCH_INDEX;
@ -105,6 +106,9 @@ export class SubFolderController extends Controller {
if (!created) throw new Error("เกิดข้อผิดพลาดกับระบบจัดการไฟล์");
const io = getInstance();
io?.emit("CreateFolder", { pathname: `${basePath}${replaceIllegalChars(body.name)}/` });
return this.setStatus(HttpStatusCode.CREATED);
}
@ -182,6 +186,12 @@ export class SubFolderController extends Controller {
}),
);
const io = getInstance();
io?.emit("EditFolder", {
from: `${cabinetName}/${drawerName}/${folderName}/${subFolderName}/`,
to: `${cabinetName}/${drawerName}/${folderName}/${replaceIllegalChars(body.name)}/`,
});
return this.setStatus(HttpStatusCode.NO_CONTENT);
}
@ -218,6 +228,11 @@ export class SubFolderController extends Controller {
stream.on("error", () => reject(new Error("เกิดข้อผิดพลาด ไม่สามารถลบไฟล์ได้")));
});
const io = getInstance();
io?.emit("DeleteFolder", {
pathname: `${cabinetName}/${drawerName}/${folderName}/${subFolderName}`,
});
return this.setStatus(HttpStatusCode.NO_CONTENT);
}

View file

@ -0,0 +1,11 @@
import { Server } from "socket.io";
let io: Server | null = null;
export function setInstance(server: Server) {
io = server;
}
export function getInstance() {
return io;
}

View file

@ -1,6 +1,7 @@
import { StorageFile } from "../interfaces/storage-fs";
import esClient from "../elasticsearch";
import minioClient from "../minio";
import { getInstance } from "../lib/websocket";
const DEFAULT_INDEX = process.env.ELASTICSEARCH_INDEX;
@ -27,7 +28,7 @@ export async function handler(key: string, event: string): Promise<boolean> {
cachedBuffer[key] = buffer;
} catch (e: any) {
if (e.code === "NoSuchKey") {
console.info(`[AMQ] Key: ${key} received but cannot be found.`)
console.info(`[AMQ] Key: ${key} received but cannot be found.`);
delete cachedBuffer[key];
delete cachedMetadata[key];
await ensureDelete(pathname);
@ -43,7 +44,7 @@ export async function handler(key: string, event: string): Promise<boolean> {
const rec = await popInfo(pathname);
console.info(`[AMQ] Key: ${key} - ${rec ?? 'Not Found.'}`)
console.info(`[AMQ] Key: ${key} - ${rec ?? "Not Found."}`);
const result = rec
? await handleFoundRecord(rec, cachedBuffer[key], cachedMetadata[key])
@ -94,6 +95,10 @@ async function ensureDelete(pathname: string) {
conflicts: "proceed",
})
.catch((e) => console.error(e));
const io = getInstance();
io?.send("FileDelete", { pathname });
return true;
}
@ -132,12 +137,16 @@ async function handleNotFoundRecord(
pipeline: "attachment",
index: DEFAULT_INDEX!,
document: { data: base64, ...metadata },
refresh: "wait_for",
})
.catch((e) => console.error(e));
if (result) return true;
if (!result) return false;
return false;
const io = getInstance();
io?.send("FileUpdate", metadata);
return true;
}
async function handleFoundRecord(
@ -154,10 +163,14 @@ async function handleFoundRecord(
pipeline: "attachment",
index: DEFAULT_INDEX!,
document: { data: Buffer.from(buffer).toString("base64"), ...metadata },
refresh: "wait_for",
})
.catch((e) => console.error(e));
if (result) return true;
if (!result) return false;
return false;
const io = getInstance();
io?.send("FileUpdate", metadata);
return true;
}