creation thread and comments in socket
This commit is contained in:
59
src/shared/V1Models/Thread/thread-Model.ts
Normal file
59
src/shared/V1Models/Thread/thread-Model.ts
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
import { Document, Schema } from "mongoose";
|
||||||
|
import { User } from "../Auth/userAuthModel.ts";
|
||||||
|
import { Project } from "../Project/project-model.ts";
|
||||||
|
import { Version } from "../Version/versionModel.ts";
|
||||||
|
import MainModel from "../../connect/mongoose.ts";
|
||||||
|
interface IComment {
|
||||||
|
userId: User["_id"];
|
||||||
|
// createdAt: string;
|
||||||
|
comment: string;
|
||||||
|
// lastUpdatedAt: string;
|
||||||
|
timestamp:Number
|
||||||
|
}
|
||||||
|
export interface IThread extends Document {
|
||||||
|
projectId: Project["_id"];
|
||||||
|
versionId: Version["_id"];
|
||||||
|
state: string;
|
||||||
|
commentId: string;
|
||||||
|
createdBy: User["_id"];
|
||||||
|
createdAt: number;
|
||||||
|
lastUpdatedAt: string;
|
||||||
|
position: [number, number, number];
|
||||||
|
rotation: [number, number, number];
|
||||||
|
replies: IComment[];
|
||||||
|
|
||||||
|
}
|
||||||
|
const CommentSchema = new Schema<IComment>(
|
||||||
|
{
|
||||||
|
userId: { type: Schema.Types.ObjectId, ref: 'User', required: true },
|
||||||
|
// createdAt: { type: String, },
|
||||||
|
comment: { type: String,},
|
||||||
|
// lastUpdatedAt: { type: String, },
|
||||||
|
timestamp:{
|
||||||
|
type: Number,
|
||||||
|
default:Date.now()}
|
||||||
|
},
|
||||||
|
// { _id: false } // Prevent automatic _id for each reply object
|
||||||
|
);
|
||||||
|
const threadSchema = new Schema<IThread>({
|
||||||
|
projectId: { type: Schema.Types.ObjectId, ref: 'Project', required: true },
|
||||||
|
state: { type: String, enum: ['active', 'inactive'], required: true },
|
||||||
|
commentId: { type: String, },
|
||||||
|
createdBy: { type: Schema.Types.ObjectId, ref: 'User', required: true },
|
||||||
|
createdAt: { type: Number,},
|
||||||
|
lastUpdatedAt: { type: String, },
|
||||||
|
position: {
|
||||||
|
type: [Number],
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
rotation: {
|
||||||
|
type: [Number],
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
replies: { type: [CommentSchema ], default: [] },
|
||||||
|
});
|
||||||
|
|
||||||
|
const ThreadModel = (db: string) => {
|
||||||
|
return MainModel(db, "Threads", threadSchema, "Threads");
|
||||||
|
};
|
||||||
|
export default ThreadModel;
|
||||||
173
src/shared/services/Thread/ThreadService.ts
Normal file
173
src/shared/services/Thread/ThreadService.ts
Normal file
@@ -0,0 +1,173 @@
|
|||||||
|
import ThreadModel from "../../V1Models/Thread/thread-Model.ts";
|
||||||
|
import { existingProjectByIdWithoutUser, existingUser } from "../helpers/v1projecthelperFns.ts";
|
||||||
|
|
||||||
|
|
||||||
|
interface IThread {
|
||||||
|
projectId: string;
|
||||||
|
versionId: string;
|
||||||
|
state: string
|
||||||
|
commentId: string;
|
||||||
|
threadId: string;
|
||||||
|
userId: string;
|
||||||
|
createdAt: string;
|
||||||
|
lastUpdatedAt: string;
|
||||||
|
position: [number, number, number];
|
||||||
|
rotation: [number, number, number];
|
||||||
|
comments: string
|
||||||
|
timestamp: number;
|
||||||
|
|
||||||
|
organization: string;
|
||||||
|
}
|
||||||
|
export const createThread = async (data: IThread) => {
|
||||||
|
try {
|
||||||
|
const { projectId, versionId, state, userId, position, rotation, comments, organization, threadId } = data
|
||||||
|
const userExisting = await existingUser(userId, organization);
|
||||||
|
if (!userExisting) {
|
||||||
|
return {
|
||||||
|
status: "user_not_found",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const projectExisting = await existingProjectByIdWithoutUser(
|
||||||
|
projectId,
|
||||||
|
organization,
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!projectExisting) {
|
||||||
|
return { status: "Project not found" };
|
||||||
|
}
|
||||||
|
const newThread = await ThreadModel(organization).create({
|
||||||
|
projectId,
|
||||||
|
versionId,
|
||||||
|
state,
|
||||||
|
createdBy: userId,
|
||||||
|
position,
|
||||||
|
rotation,
|
||||||
|
comments,
|
||||||
|
createdAt: Date.now()
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
status: "Success",
|
||||||
|
data: newThread,
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
return {
|
||||||
|
status: error,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export const deleteThread = async (data: IThread) => {
|
||||||
|
try {
|
||||||
|
const { projectId, versionId, state, userId, organization, threadId } = data
|
||||||
|
const userExisting = await existingUser(userId, organization);
|
||||||
|
if (!userExisting) {
|
||||||
|
return {
|
||||||
|
status: "user_not_found",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const projectExisting = await existingProjectByIdWithoutUser(
|
||||||
|
projectId,
|
||||||
|
organization,
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!projectExisting) {
|
||||||
|
return { status: "Project not found" };
|
||||||
|
}
|
||||||
|
const findThreadId = await ThreadModel(organization).findOne({ _id: threadId, createdBy: userId })
|
||||||
|
if (!findThreadId) {
|
||||||
|
return { status: "can't deleted" };
|
||||||
|
}
|
||||||
|
const deleteThread = await ThreadModel(organization).findOneAndDelete({ _id: threadId, createdBy: userId })
|
||||||
|
return {
|
||||||
|
status: "Success",
|
||||||
|
data: deleteThread,
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
console.log("error: ", error);
|
||||||
|
return {
|
||||||
|
status: error,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export const addComments = async (data: IThread) => {
|
||||||
|
try {
|
||||||
|
const { projectId, versionId, userId, comments, organization, threadId } = data
|
||||||
|
const userExisting = await existingUser(userId, organization);
|
||||||
|
if (!userExisting) {
|
||||||
|
return {
|
||||||
|
status: "user_not_found",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const projectExisting = await existingProjectByIdWithoutUser(
|
||||||
|
projectId,
|
||||||
|
organization,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!projectExisting) {
|
||||||
|
return { status: "Project not found" };
|
||||||
|
}
|
||||||
|
const findThreadId = await ThreadModel(organization).findById(threadId)
|
||||||
|
|
||||||
|
const newComment = { userId, comment: comments, timestamp: Date.now() };
|
||||||
|
findThreadId?.replies.push(newComment)
|
||||||
|
await findThreadId?.save()
|
||||||
|
return {
|
||||||
|
status: "Success",
|
||||||
|
data: newComment.comment,
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
console.log("error: ", error);
|
||||||
|
return {
|
||||||
|
status: error,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export const deleteComments = async (data: IThread) => {
|
||||||
|
try {
|
||||||
|
const { projectId, versionId, userId, commentId, organization, threadId } = data
|
||||||
|
const userExisting = await existingUser(userId, organization);
|
||||||
|
if (!userExisting) {
|
||||||
|
return {
|
||||||
|
status: "user_not_found",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const projectExisting = await existingProjectByIdWithoutUser(
|
||||||
|
projectId,
|
||||||
|
organization,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!projectExisting) {
|
||||||
|
return { status: "Project not found" };
|
||||||
|
}
|
||||||
|
const findThreadId = await ThreadModel(organization).findOne({ _id: threadId })
|
||||||
|
if (!findThreadId) {
|
||||||
|
return { status: "thread not found" };
|
||||||
|
}
|
||||||
|
|
||||||
|
const deleted = await ThreadModel(organization).updateOne(
|
||||||
|
{ _id: threadId },
|
||||||
|
{
|
||||||
|
$pull: {
|
||||||
|
replies: {
|
||||||
|
_id: commentId,
|
||||||
|
userId: userId,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (deleted.modifiedCount === 0) {
|
||||||
|
return { status: "unauthorized" };
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
status: "Success",
|
||||||
|
data: deleted
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
console.log("error: ", error);
|
||||||
|
return {
|
||||||
|
status: error,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -176,38 +176,38 @@ export const DeleteLayer = async (
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const DeleteLinePoints = async (
|
// export const DeleteLinePoints = async (
|
||||||
data: ILinePointsDelete
|
// data: ILinePointsDelete
|
||||||
): Promise<{ status: string; data?: object }> => {
|
// ): Promise<{ status: string; data?: object }> => {
|
||||||
try {
|
// try {
|
||||||
const { organization, projectId, uuid, userId } = data;
|
// const { organization, projectId, uuid, userId } = data;
|
||||||
const findValue = await lineModel(organization).deleteMany({
|
// const findValue = await lineModel(organization).deleteMany({
|
||||||
"line.uuid": uuid,
|
// "line.uuid": uuid,
|
||||||
});
|
// });
|
||||||
|
|
||||||
if (!findValue) {
|
// // if (!findValue) {
|
||||||
return {
|
// // return {
|
||||||
success: false,
|
// // success: false,
|
||||||
message: "line not found",
|
// // message: "line not found",
|
||||||
organization: organization,
|
// // organization: organization,
|
||||||
};
|
// // };
|
||||||
} else {
|
// // } else {
|
||||||
return {
|
// // return {
|
||||||
success: true,
|
// // success: true,
|
||||||
message: "point deleted",
|
// // message: "point deleted",
|
||||||
data: uuid,
|
// // data: uuid,
|
||||||
organization: organization,
|
// // organization: organization,
|
||||||
};
|
// // };
|
||||||
}
|
// // }
|
||||||
} catch (error: unknown) {
|
// } catch (error: unknown) {
|
||||||
if (error instanceof Error) {
|
// if (error instanceof Error) {
|
||||||
return {
|
// return {
|
||||||
status: error.message,
|
// status: error.message,
|
||||||
};
|
// };
|
||||||
} else {
|
// } else {
|
||||||
return {
|
// return {
|
||||||
status: "An unexpected error occurred",
|
// status: "An unexpected error occurred",
|
||||||
};
|
// };
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
};
|
// };
|
||||||
|
|||||||
@@ -88,3 +88,13 @@ export const existingProjectById = async (
|
|||||||
});
|
});
|
||||||
return projectData;
|
return projectData;
|
||||||
};
|
};
|
||||||
|
export const existingProjectByIdWithoutUser = async (
|
||||||
|
projectId: string,
|
||||||
|
organization: string,
|
||||||
|
) => {
|
||||||
|
const projectData = await projectModel(organization).findOne({
|
||||||
|
_id: projectId,
|
||||||
|
isArchive: false,
|
||||||
|
});
|
||||||
|
return projectData;
|
||||||
|
};
|
||||||
51
src/socket-server/controllers/builder/cameraController.ts
Normal file
51
src/socket-server/controllers/builder/cameraController.ts
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
import { Socket, Server } from "socket.io";
|
||||||
|
import { EVENTS } from "../../socket/events.ts";
|
||||||
|
import { emitEventResponse } from "../../utils/emitEventResponse.ts";
|
||||||
|
import { SetCamera } from "../../../shared/services/builder/cameraService.ts";
|
||||||
|
export const SetCameraHandleEvent = async ( event: string,socket: Socket,data: any,) => {
|
||||||
|
if (event !== EVENTS.setCamera || !data?.organization) return;
|
||||||
|
const requiredFields = ["userId", "position", "target", "rotation", "organization","projectId","versionId"];
|
||||||
|
const missingFields = requiredFields.filter(field => !data?.[field]);
|
||||||
|
|
||||||
|
if (missingFields.length > 0) {
|
||||||
|
const response = {
|
||||||
|
success: false,
|
||||||
|
message: `Missing required field(s): ${missingFields.join(", ")}`,
|
||||||
|
status: "MissingFields",
|
||||||
|
socketId: socket.id,
|
||||||
|
organization: data?.organization ?? "unknown",
|
||||||
|
};
|
||||||
|
|
||||||
|
emitEventResponse(socket, data?.organization, EVENTS.cameraCreateResponse, response);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const result = await SetCamera(data);
|
||||||
|
const status = typeof result?.status === "string" ? result.status : "unknown";
|
||||||
|
|
||||||
|
const messages: Record<string, { message: string }> = {
|
||||||
|
"Creation Success": { message: "Camera created Successfully" },
|
||||||
|
"User not found": { message: "User not found" },
|
||||||
|
"Project not found": { message: "Project not found" },
|
||||||
|
"Update Success": { message: "Camera updated Successfully" },
|
||||||
|
};
|
||||||
|
const msg = messages[status] || { message: "Internal server error" };
|
||||||
|
const isSuccess = status === "Creation Success" || status === "Update Success";
|
||||||
|
|
||||||
|
|
||||||
|
const response = {
|
||||||
|
success: status === "Success",
|
||||||
|
message: msg.message,
|
||||||
|
status,
|
||||||
|
socketId: socket.id,
|
||||||
|
organization: data.organization,
|
||||||
|
...(isSuccess && result?.data ? { data: result.data }: {}),
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const responseEvent =
|
||||||
|
status === "Creation Success"
|
||||||
|
? EVENTS.cameraCreateResponse
|
||||||
|
: EVENTS.cameraUpdateResponse;
|
||||||
|
|
||||||
|
emitEventResponse(socket, data.organization, responseEvent, response);
|
||||||
|
}
|
||||||
177
src/socket-server/controllers/thread/threadController.ts
Normal file
177
src/socket-server/controllers/thread/threadController.ts
Normal file
@@ -0,0 +1,177 @@
|
|||||||
|
import { Socket, Server } from "socket.io";
|
||||||
|
import { EVENTS } from "../../socket/events.ts";
|
||||||
|
import { emitEventResponse } from "../../utils/emitEventResponse.ts";
|
||||||
|
import { addComments, createThread, deleteComments, deleteThread } from "../../../shared/services/Thread/ThreadService.ts";
|
||||||
|
export const createThreadHandleEvent = async (event: string, socket: Socket, data: any,) => {
|
||||||
|
if (event !== EVENTS.createThread || !data?.organization) return;
|
||||||
|
const requiredFields = ["userId", "position", "rotation", "organization", "projectId", "versionId"];
|
||||||
|
const missingFields = requiredFields.filter(field => !data?.[field]);
|
||||||
|
|
||||||
|
if (missingFields.length > 0) {
|
||||||
|
const response = {
|
||||||
|
success: false,
|
||||||
|
message: `Missing required field(s): ${missingFields.join(", ")}`,
|
||||||
|
status: "MissingFields",
|
||||||
|
socketId: socket.id,
|
||||||
|
organization: data?.organization ?? "unknown",
|
||||||
|
};
|
||||||
|
|
||||||
|
emitEventResponse(socket, data?.organization, EVENTS.ThreadCreateResponse, response);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await createThread(data);
|
||||||
|
const status = typeof result?.status === "string" ? result.status : "unknown";
|
||||||
|
|
||||||
|
const messages: Record<string, { message: string }> = {
|
||||||
|
Success: { message: "Thread created Successfully" },
|
||||||
|
"User not found": { message: "User not found" },
|
||||||
|
"Project not found": { message: "Project not found" },
|
||||||
|
};
|
||||||
|
const msg = messages[status] || { message: "Internal server error" };
|
||||||
|
const threadDatas =
|
||||||
|
status === "Success" && result?.data
|
||||||
|
|
||||||
|
const response = {
|
||||||
|
success: status === "Success",
|
||||||
|
message: msg.message,
|
||||||
|
status,
|
||||||
|
socketId: socket.id,
|
||||||
|
organization: data.organization,
|
||||||
|
...(threadDatas ? { data: threadDatas } : {}),
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
emitEventResponse(socket, data.organization, EVENTS.ThreadCreateResponse, response);
|
||||||
|
}
|
||||||
|
export const deleteThreadHandleEvent = async (event: string, socket: Socket, data: any,) => {
|
||||||
|
if (event !== EVENTS.deleteThread || !data?.organization) return;
|
||||||
|
const requiredFields = ["userId", "threadId", "organization", "projectId", "versionId"];
|
||||||
|
const missingFields = requiredFields.filter(field => !data?.[field]);
|
||||||
|
|
||||||
|
if (missingFields.length > 0) {
|
||||||
|
const response = {
|
||||||
|
success: false,
|
||||||
|
message: `Missing required field(s): ${missingFields.join(", ")}`,
|
||||||
|
status: "MissingFields",
|
||||||
|
socketId: socket.id,
|
||||||
|
organization: data?.organization ?? "unknown",
|
||||||
|
};
|
||||||
|
|
||||||
|
emitEventResponse(socket, data?.organization, EVENTS.deleteThreadResponse, response);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await deleteThread(data);
|
||||||
|
const status = typeof result?.status === "string" ? result.status : "unknown";
|
||||||
|
|
||||||
|
const messages: Record<string, { message: string }> = {
|
||||||
|
Success: { message: "Thread deleted Successfully" },
|
||||||
|
"User not found": { message: "User not found" },
|
||||||
|
"Project not found": { message: "Project not found" },
|
||||||
|
"can't deleted": { message: "Thread could not be deleted" },
|
||||||
|
};
|
||||||
|
const msg = messages[status] || { message: "Internal server error" };
|
||||||
|
const threadDatas =
|
||||||
|
status === "Success" && result?.data
|
||||||
|
|
||||||
|
const response = {
|
||||||
|
success: status === "Success",
|
||||||
|
message: msg.message,
|
||||||
|
status,
|
||||||
|
socketId: socket.id,
|
||||||
|
organization: data.organization,
|
||||||
|
...(threadDatas ? { data: threadDatas } : {}),
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
emitEventResponse(socket, data.organization, EVENTS.deleteThreadResponse, response);
|
||||||
|
}
|
||||||
|
export const addCommentHandleEvent = async (event: string, socket: Socket, data: any,) => {
|
||||||
|
if (event !== EVENTS.addComment || !data?.organization) return;
|
||||||
|
const requiredFields = ["userId", "comments", "organization", "commentId", "projectId", "versionId"];
|
||||||
|
const missingFields = requiredFields.filter(field => !data?.[field]);
|
||||||
|
|
||||||
|
if (missingFields.length > 0) {
|
||||||
|
const response = {
|
||||||
|
success: false,
|
||||||
|
message: `Missing required field(s): ${missingFields.join(", ")}`,
|
||||||
|
status: "MissingFields",
|
||||||
|
socketId: socket.id,
|
||||||
|
organization: data?.organization ?? "unknown",
|
||||||
|
};
|
||||||
|
|
||||||
|
emitEventResponse(socket, data?.organization, EVENTS.addCommentResponse, response);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await addComments(data);
|
||||||
|
const status = typeof result?.status === "string" ? result.status : "unknown";
|
||||||
|
|
||||||
|
const messages: Record<string, { message: string }> = {
|
||||||
|
Success: { message: "Thread created Successfully" },
|
||||||
|
"User not found": { message: "User not found" },
|
||||||
|
"Project not found": { message: "Project not found" },
|
||||||
|
};
|
||||||
|
const msg = messages[status] || { message: "Internal server error" };
|
||||||
|
const commentsData =
|
||||||
|
status === "Success" && result?.data
|
||||||
|
|
||||||
|
|
||||||
|
const response = {
|
||||||
|
success: status === "Success",
|
||||||
|
message: msg.message,
|
||||||
|
status,
|
||||||
|
socketId: socket.id,
|
||||||
|
organization: data.organization,
|
||||||
|
...(commentsData ? { data: commentsData } : {}),
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
emitEventResponse(socket, data.organization, EVENTS.addCommentResponse, response);
|
||||||
|
}
|
||||||
|
export const deleteCommentHandleEvent = async (event: string, socket: Socket, data: any,) => {
|
||||||
|
if (event !== EVENTS.deleteComment || !data?.organization) return;
|
||||||
|
const requiredFields = ["userId", "organization", "commentId", "projectId", "versionId"];
|
||||||
|
const missingFields = requiredFields.filter(field => !data?.[field]);
|
||||||
|
|
||||||
|
if (missingFields.length > 0) {
|
||||||
|
const response = {
|
||||||
|
success: false,
|
||||||
|
message: `Missing required field(s): ${missingFields.join(", ")}`,
|
||||||
|
status: "MissingFields",
|
||||||
|
socketId: socket.id,
|
||||||
|
organization: data?.organization ?? "unknown",
|
||||||
|
};
|
||||||
|
|
||||||
|
emitEventResponse(socket, data?.organization, EVENTS.deleteCommentResponse, response);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await deleteComments(data);
|
||||||
|
const status = typeof result?.status === "string" ? result.status : "unknown";
|
||||||
|
|
||||||
|
const messages: Record<string, { message: string }> = {
|
||||||
|
Success: { message: "Thread created Successfully" },
|
||||||
|
"User not found": { message: "User not found" },
|
||||||
|
"Project not found": { message: "Project not found" },
|
||||||
|
"unauthorized": { message: "You can only delete your own comment." },
|
||||||
|
"thread not found": { message: "thread not found" },
|
||||||
|
};
|
||||||
|
const msg = messages[status] || { message: "Internal server error" };
|
||||||
|
const commentsData =
|
||||||
|
status === "Success" && result?.data
|
||||||
|
|
||||||
|
|
||||||
|
const response = {
|
||||||
|
success: status === "Success",
|
||||||
|
message: msg.message,
|
||||||
|
status,
|
||||||
|
socketId: socket.id,
|
||||||
|
organization: data.organization,
|
||||||
|
...(commentsData ? { data: commentsData } : {}),
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
emitEventResponse(socket, data.organization, EVENTS.deleteCommentResponse, response);
|
||||||
|
}
|
||||||
@@ -16,6 +16,7 @@ app.get('/', (req: Request, res: Response) => {
|
|||||||
res.send('Hello, I am Major-Dwinzo RealTime!');
|
res.send('Hello, I am Major-Dwinzo RealTime!');
|
||||||
});
|
});
|
||||||
initSocketServer(server);
|
initSocketServer(server);
|
||||||
|
// SocketServer(server)
|
||||||
server.listen(PORT, () => {
|
server.listen(PORT, () => {
|
||||||
console.log(`socket-Server is running on http://localhost:${PORT}`);
|
console.log(`socket-Server is running on http://localhost:${PORT}`);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
import { Server, Socket } from 'socket.io';
|
import { Server, Socket } from 'socket.io';
|
||||||
|
import jwt from 'jsonwebtoken';
|
||||||
|
import dotenv from 'dotenv';
|
||||||
|
import AuthModel from '../../shared/V1Models/Auth/userAuthModel.ts';
|
||||||
|
dotenv.config();
|
||||||
import { projectDeleteHandleEvent, projectHandleEvent, projecUpdateHandleEvent } from '../controllers/project/projectController.ts';
|
import { projectDeleteHandleEvent, projectHandleEvent, projecUpdateHandleEvent } from '../controllers/project/projectController.ts';
|
||||||
|
import { addCommentHandleEvent, createThreadHandleEvent, deleteCommentHandleEvent, deleteThreadHandleEvent } from '../controllers/thread/threadController.ts';
|
||||||
export const SocketServer = (httpServer: any) => {
|
export const SocketServer = (httpServer: any) => {
|
||||||
const io = new Server(httpServer, {
|
const io = new Server(httpServer, {
|
||||||
cors: {
|
cors: {
|
||||||
@@ -9,12 +13,9 @@ export const SocketServer = (httpServer: any) => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const namespaces = {
|
const namespaces = {
|
||||||
|
|
||||||
project: io.of('/project'),
|
project: io.of('/project'),
|
||||||
|
thread: io.of('/thread'),
|
||||||
};
|
};
|
||||||
|
|
||||||
// const onlineUsers = new Map<string, Set<string>>();
|
// const onlineUsers = new Map<string, Set<string>>();
|
||||||
@@ -22,14 +23,53 @@ export const SocketServer = (httpServer: any) => {
|
|||||||
|
|
||||||
|
|
||||||
const handleNamespace = (namespace: any, ...eventHandlers: Function[]) => {
|
const handleNamespace = (namespace: any, ...eventHandlers: Function[]) => {
|
||||||
|
namespace.use(async (socket: Socket, next: any) => {
|
||||||
|
const token = socket.handshake.auth.token;
|
||||||
|
// const token = socket.handshake.query.token as string;
|
||||||
|
const jwt_secret = process.env.JWT_SECRET as string;
|
||||||
|
|
||||||
|
if (!token) {
|
||||||
|
console.log(" No token provided");
|
||||||
|
return next(new Error("Authentication error: No token"));
|
||||||
|
}
|
||||||
|
if (!jwt_secret) {
|
||||||
|
console.error(" JWT secret is not defined in environment variables");
|
||||||
|
return next(new Error("Server configuration error: Missing secret"));
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
// 1. Verify token
|
||||||
|
const decoded = jwt.verify(token, jwt_secret) as {
|
||||||
|
userId: string;
|
||||||
|
Email: string;
|
||||||
|
organization: string
|
||||||
|
};
|
||||||
|
// 2. Find user in DB
|
||||||
|
const user = await AuthModel(decoded.organization).findOne({ _id: decoded.userId, Email: decoded.Email, });
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
console.log(" User not found in DB");
|
||||||
|
return next(new Error("Authentication error: User not found"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Attach to socket for later use
|
||||||
|
(socket as any).user = {
|
||||||
|
// ...user.toObject(), // convert Mongoose doc to plain object
|
||||||
|
organization: decoded.organization, // manually add org
|
||||||
|
Email: decoded.Email, // manually add org
|
||||||
|
};
|
||||||
|
|
||||||
|
next();
|
||||||
|
} catch (error: any) {
|
||||||
|
console.error("❌ Authentication failed:", error.message);
|
||||||
|
return next(new Error("Authentication error"));
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
namespace.on("connection", (socket: Socket) => {
|
namespace.on("connection", (socket: Socket) => {
|
||||||
console.log(`✅ Client connected to ${namespace.name}: ${socket.id}`);
|
|
||||||
// Extract organization from query parameters
|
// Extract organization from query parameters
|
||||||
|
const user = (socket as any).user;
|
||||||
const organization = socket.handshake.query.organization as string;
|
const organization = user.organization;
|
||||||
const email = socket.handshake.query.email as string;
|
const email = user.email;
|
||||||
// const {organization,email} = socket.handshake.auth
|
|
||||||
console.log(`🔍 Received organization: ${organization}`);
|
console.log(`🔍 Received organization: ${organization}`);
|
||||||
|
|
||||||
if (organization) {
|
if (organization) {
|
||||||
@@ -74,5 +114,6 @@ export const SocketServer = (httpServer: any) => {
|
|||||||
|
|
||||||
|
|
||||||
handleNamespace(namespaces.project,projectHandleEvent,projectDeleteHandleEvent,projecUpdateHandleEvent)
|
handleNamespace(namespaces.project,projectHandleEvent,projectDeleteHandleEvent,projecUpdateHandleEvent)
|
||||||
|
handleNamespace(namespaces.thread,createThreadHandleEvent,deleteThreadHandleEvent,addCommentHandleEvent,deleteCommentHandleEvent)
|
||||||
return io;
|
return io;
|
||||||
};
|
};
|
||||||
@@ -98,6 +98,7 @@ export const EVENTS = {
|
|||||||
update3dPositionResponse:"viz-widget3D:response:modifyPositionRotation",
|
update3dPositionResponse:"viz-widget3D:response:modifyPositionRotation",
|
||||||
delete3DWidget:"v2:viz-3D-widget:delete",
|
delete3DWidget:"v2:viz-3D-widget:delete",
|
||||||
widget3DDeleteResponse:"viz-widget3D:response:delete",
|
widget3DDeleteResponse:"viz-widget3D:response:delete",
|
||||||
|
/////////////////........................................................
|
||||||
|
|
||||||
//PROJECT
|
//PROJECT
|
||||||
addProject: "v1:project:add",
|
addProject: "v1:project:add",
|
||||||
@@ -106,4 +107,14 @@ export const EVENTS = {
|
|||||||
deleteProjectResponse: "v1-project:response:delete",
|
deleteProjectResponse: "v1-project:response:delete",
|
||||||
ProjectUpdate: "v1:project:update",
|
ProjectUpdate: "v1:project:update",
|
||||||
projectUpdateResponse: "v1-project:response:update",
|
projectUpdateResponse: "v1-project:response:update",
|
||||||
|
|
||||||
|
//THREAD
|
||||||
|
createThread:"v1:thread:create",
|
||||||
|
ThreadCreateResponse:"v1-thread:response:create",
|
||||||
|
deleteThread:"v1:thread:delete",
|
||||||
|
deleteThreadResponse:"v1-thread:response:delete",
|
||||||
|
addComment:"v1-Comment:response:add",
|
||||||
|
addCommentResponse:"v1-Comment:response:add",
|
||||||
|
deleteComment:"v1-Comment:response:delete",
|
||||||
|
deleteCommentResponse:"v1-Comment:response:delete",
|
||||||
}
|
}
|
||||||
@@ -24,6 +24,7 @@ export const emitEventResponse = (
|
|||||||
console.log(`Organization missing in response for event: ${event}`);
|
console.log(`Organization missing in response for event: ${event}`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
console.log('result: ', result);
|
||||||
|
|
||||||
socket.to(organization).emit(event, {
|
socket.to(organization).emit(event, {
|
||||||
// success: result.success,
|
// success: result.success,
|
||||||
|
|||||||
Reference in New Issue
Block a user