Skip to content

Commit

Permalink
Merge pull request #603 from encorelab/master-merge-updates
Browse files Browse the repository at this point in the history
Updating the production server to Node 18, Angular 15 and other changes
  • Loading branch information
markiianbabiak authored Oct 5, 2024
2 parents c8fdf8c + 7963d29 commit 0ead400
Show file tree
Hide file tree
Showing 129 changed files with 16,519 additions and 35,683 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
node_modules
/.vscode/launch.json
.DS_Store
dump.rdb
4 changes: 3 additions & 1 deletion backend/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,6 @@
*.js

# misc
.DS_Store
.DS_Store

dump.rdp
10,523 changes: 3,879 additions & 6,644 deletions backend/package-lock.json

Large diffs are not rendered by default.

13 changes: 8 additions & 5 deletions backend/package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"dependencies": {
"@socket.io/redis-adapter": "^8.1.0",
"@typegoose/typegoose": "^9.8.1",
"@types/bcrypt": "^5.0.0",
"bcrypt": "^5.0.1",
Expand All @@ -9,14 +10,15 @@
"dotenv": "^16.0.0",
"express": "^4.17.3",
"express-jwt": "^7.7.0",
"ioredis": "^5.3.2",
"jsonwebtoken": "^8.5.1",
"migrate-mongo": "^9.0.0",
"mongo-dot-notation": "^3.1.0",
"mongoose": "^6.3.2",
"mongoose-autopopulate": "^0.17.1",
"nodemon": "^2.0.16",
"prettier": "^2.6.2",
"socket.io": "^4.4.1",
"socket.io": "^4.7.5",
"uuid": "^8.3.2"
},
"scripts": {
Expand All @@ -38,15 +40,16 @@
"@types/crypto-js": "^4.1.1",
"@types/express": "^4.17.13",
"@types/jsonwebtoken": "^8.5.8",
"@types/node": "^17.0.31",
"@types/node": "^18.19.50",
"@types/uuid": "^8.3.4",
"@typescript-eslint/eslint-plugin": "^5.22.0",
"@typescript-eslint/parser": "^5.22.0",
"cross-env": "^7.0.3",
"eslint": "^8.17.0",
"migrate-mongo": "^9.0.0",
"ts-node": "^10.7.0",
"ts-node-dev": "^1.1.8",
"typescript": "^4.6.4"
"ts-node": "^10.9.1",
"ts-node-dev": "^2.0.0",
"typescript": "^4.7.4"
},
"author": "",
"license": "ISC",
Expand Down
12 changes: 10 additions & 2 deletions backend/src/api/workflows.ts
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,11 @@ router.post('/task/:workflowID/groupTask/:groupTaskID', async (req, res) => {

await dalGroupTask.update(groupTaskID, task);

Socket.Instance.emit(SocketEvent.WORKFLOW_PROGRESS_UPDATE, [task], true);
Socket.Instance.emit(
SocketEvent.WORKFLOW_PROGRESS_UPDATE,
[task],
workflow.boardID
);

res.status(200).json(task);
});
Expand Down Expand Up @@ -360,7 +364,11 @@ router.post('/task/groupTask/:groupTaskID/submit', async (req, res) => {
}
}

Socket.Instance.emit(SocketEvent.WORKFLOW_POST_SUBMIT, post, true);
Socket.Instance.emit(
SocketEvent.WORKFLOW_POST_SUBMIT,
post,
workflow.boardID
);
return res.status(200).json(updatedGroupTask);
} catch (e) {
return res.status(500).end('Unable to submit post!');
Expand Down
26 changes: 20 additions & 6 deletions backend/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import groups from './api/groups';
import todoItems from './api/todoItem';
import learner from './api/learner';
import { isAuthenticated } from './utils/auth';
import RedisClient from './utils/redis';
dotenv.config();

const port = process.env.PORT || 8001;
Expand All @@ -29,14 +30,25 @@ const dbUrl = process.env.DB_URL;
const dbName = process.env.DB_NAME;
const dbURI = `mongodb+srv://${dbUsername}:${dbPassword}@${dbUrl}.mongodb.net/${dbName}?retryWrites=true&w=majority`;

const redisHost = process.env.REDIS_HOST || 'localhost';
const redisPort = (process.env.REDIS_PORT || 6379) as number;
const redisPassword = process.env.REDIS_PASSWORD || '';

const app = express();
app.use(cors());
app.use(bodyParser.json());
app.use(express.static(path.join(__dirname, '../../frontend/dist/ck-board')));
const server = http.createServer(app);

const redis = new RedisClient({
host: redisHost,
port: redisPort,
password: redisPassword,
});

const socket = Socket.Instance;
socket.init(server);

socket.init(redis);

app.use('/api/projects', isAuthenticated, projects);
app.use('/api/boards', isAuthenticated, boards);
Expand All @@ -52,11 +64,13 @@ app.use('/api/trace', isAuthenticated, trace);
app.use('/api/todoItems', isAuthenticated, todoItems);
app.use('/api/learner', isAuthenticated, learner);

app.get('*', (req, res) => {
res.sendFile(
path.join(__dirname + '/../../frontend/dist/ck-board/index.html')
);
});
const shutdown = async () => {
await redis.disconnect();
process.exit(0);
};

// Handle termination signals
process.on('SIGINT', shutdown);

mongoose
.connect(dbURI)
Expand Down
95 changes: 56 additions & 39 deletions backend/src/socket/socket.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@ import * as socketIO from 'socket.io';
import { SocketEvent } from '../constants';
import events from './events';
import SocketManager from './socketManager';
import RedisClient from '../utils/redis';
import { createAdapter } from '@socket.io/redis-adapter';

class Socket {
private static _instance: Socket;

private _socketManager: SocketManager;
private _io: socketIO.Server | null = null;
private _socket: socketIO.Socket | null = null;
private _currentRoom: string | null = null;

// eslint-disable-next-line @typescript-eslint/no-empty-function
private constructor() {
Expand All @@ -28,42 +28,46 @@ class Socket {
* @param server the http server
* @returns void
*/
init(server: Server) {
const io = new socketIO.Server(server, {

init(redis: RedisClient) {
const io = new socketIO.Server(8000, {
cors: {
origin: '*',
},
adapter: createAdapter(redis.getPublisher, redis.getSubscriber),
});

this._io = io;

console.log('Socket server running...');

io.on('connection', (socket) => {
this._socket = socket;
// this._socket = socket;

socket.on('join', (user: string, room: string) => {
socket.data.room = room;
this._safeJoin(socket, user, room);
this._listenForEvents(io, socket);
this._logUserSocketsAndRooms(user);
});

socket.on('leave', (user: string, room: string) => {
if (this._currentRoom && this._currentRoom == room) {
socket.leave(this._currentRoom);
console.log(`Socket ${socket.id} left room ${room}`);

events.map((e) => socket.removeAllListeners(e.type.toString()));
socket.data.room = null;
this._currentRoom = null;
this._socketManager.removeByUserId(user);
}
socket.leave(room);
console.log(`Socket ${socket.id} left room ${room}`);
events.map((e) => socket.removeAllListeners(e.type.toString()));

// Remove the specific socketId for the user from the SocketManager
this._socketManager.removeBySocketId(socket.id);
this._logUserSocketsAndRooms(user);
});

socket.on('disconnect', () => {
if (this._socket) {
this._currentRoom = null;
this._socketManager.removeBySocketId(this._socket.id);
}
const rooms = socket.rooms;
rooms.forEach((room) => {
socket.leave(room);
// Potentially update or notify the room of the disconnect
});
this._socketManager.removeBySocketId(socket.id);
});

socket.on('disconnectAll', async (room: string) => {
Expand All @@ -74,27 +78,19 @@ class Socket {
}

/**
* Sends an event to all users connected to socket (except sender).
* Emits an event to a specific room.
*
* @param event the type of event being emitted
* @param eventData data associated with event
* @param toSender send event to room and sender
* @returns void
* @param event The type of event being emitted.
* @param eventData Data associated with the event.
* @param roomId The ID of the room to which the event should be emitted.
* @param toSender Indicates whether the event should also be sent to the sender.
*/
emit(event: SocketEvent, eventData: unknown, toSender = false): void {
if (this._socket == null) {
throw new Error('Socket not initialized. Please invoke init() first.');
} else if (this._currentRoom == null) {
throw new Error('Socket not connected to any rooms.');
} else if (this._io == null) {
emit(event: SocketEvent, eventData: unknown, roomId: string): void {
if (!this._io) {
throw new Error('IO not initialized. Please invoke init() first.');
}

if (toSender) {
this._io.to(this._currentRoom).emit(event, eventData);
} else {
this._socket.to(this._currentRoom).emit(event, eventData);
}
this._io.to(roomId).emit(event, eventData);
}

/**
Expand Down Expand Up @@ -123,13 +119,34 @@ class Socket {
* @returns void
*/
private _safeJoin(socket: socketIO.Socket, user: string, nextRoom: string) {
if (this._currentRoom) socket.leave(this._currentRoom);

socket.join(nextRoom);

this._socketManager.add(user, socket.id);
this._currentRoom = nextRoom;
console.log(`Socket ${socket.id} joined room ${nextRoom}`);
console.log(`Socket ${socket.id} (userID ${user}) joined room ${nextRoom}`);
}

/**
* Logs the sockets and their corresponding rooms for a given user.
*
* @param userId The ID of the user whose sockets and rooms to log.
*/
private _logUserSocketsAndRooms(userId: string): void {
const socketIds = this._socketManager.get(userId);
if (!socketIds) {
return;
}

console.log(`User ${userId} =>`);
socketIds.forEach((socketId) => {
const socket = this._io?.sockets.sockets.get(socketId);
if (socket && socket.data.room) {
console.log(`\tSocket ID: ${socketId}, Room: ${socket.data.room}`);
} else {
console.log(
`\tSocket ID: ${socketId} is not currently connected or has no room.`
);
}
});
console.log('');
}
}

Expand Down
36 changes: 29 additions & 7 deletions backend/src/socket/socketManager.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
class SocketManager {
private static _instance: SocketManager;

private _userToSocket: Map<string, string>;
private _userToSocket: Map<string, string[]>;

private constructor() {
this._userToSocket = new Map();
Expand All @@ -11,24 +11,46 @@ class SocketManager {
return this._instance || (this._instance = new this());
}

add(userID: string, socketID: string): Map<string, string> {
return this._userToSocket.set(userID, socketID);
/**
* Adds a socket ID to a user's list of socket IDs.
* If the user does not exist, creates a new entry.
*/
add(userID: string, socketID: string): void {
const sockets = this._userToSocket.get(userID) || [];
sockets.push(socketID);
this._userToSocket.set(userID, sockets);
}

removeByUserId(userID: string): boolean {
return this._userToSocket.delete(userID);
}

/**
* Removes a specific socket ID from a user's list.
* If the user has no more socket IDs left after removal, deletes the user's entry.
*/
removeBySocketId(socketID: string): boolean {
for (const [user, socket] of this._userToSocket.entries()) {
if (socket == socketID) {
return this._userToSocket.delete(user);
for (const [user, sockets] of this._userToSocket.entries()) {
const index = sockets.indexOf(socketID);
if (index !== -1) {
sockets.splice(index, 1);
if (sockets.length === 0) {
// If no sockets left, remove the user entry
this._userToSocket.delete(user);
} else {
// Update with the modified array
this._userToSocket.set(user, sockets);
}
return true;
}
}
return false;
}

get(userID: string) {
/**
* Retrieves the list of socket IDs for a user.
*/
get(userID: string): string[] | undefined {
return this._userToSocket.get(userID);
}
}
Expand Down
Loading

0 comments on commit 0ead400

Please sign in to comment.