-
-
Notifications
You must be signed in to change notification settings - Fork 1
/
index.js
271 lines (241 loc) · 8.18 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
/**
* Bookstore Library App
*
* @project : MI LIBRO
* @version : 1.0.1
* @link : https://github.com/Ajayos/MI_LIBRO
* @authors : Ajay, Akarsh, Abinas, Saran, Yasir
* @created : 2023-05-17 10:02:42
* @modified : 2023-06-07 15:25:38
* @editor : Ajayos
* @file : index.js
* @path : /index.js
*
* Description: Main entry point for the Bookstore Library App backend.
*
* GitHub Repository: https://github.com/Ajayos/MI_LIBRO
*
* All rights reserved. (C) 2023 Ajayos and co-authors (Akarsh, Abinas, Saran, Yasir)
*/
// Import dependencies
const os = require("os");
const fs = require("fs");
const path = require("path");
const http = require("http");
const cors = require("cors");
const dotenv = require("dotenv");
const express = require("express");
const socketIO = require("socket.io");
const { v4: uuidv4 } = require("uuid");
const bodyParser = require("body-parser");
const schedule = require("node-schedule");
const { log } = require("@ajayos/nodelogger");
// Import local modules
const setupLogger = require("./lib/Logger");
const apiRouter = require("./Routers");
const { connectDB, User, Book, Admin } = require("./Models");
const errorHandler = require("./middleware/errorHandler");
const sendEmail = require("./lib/Email");
// config env file
dotenv.config();
// Define
const SERVER_PORT = process.env.PORT || 3000;
const publicPath = path.join(__dirname, "/client/build");
// Set up the logger
setupLogger();
// connect to Database
connectDB(process.env.MONGO_URL);
// Create Express app
const app = express();
const server = http.createServer(app);
const io = socketIO(server, {
pingTimeout: 60000,
});
// Setup app for the data handling
app.use(cors());
app.use(express.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
// setup api
// v1 api
app.use("/", apiRouter);
app.use(express.static(publicPath));
app.get("*", (req, res) => {
res.sendFile(path.join(publicPath, "index.html"));
});
// Error handler middleware
app.use(errorHandler);
const users = {}; // Object to store online users
// Set up Socket.IO for real-time updates
io.on("connection", (socket) => {
io.emit("online", users.length)
socket.on("hehe", () => {
io.emit("online", users.length)
});
// Add new user to the users list and notify other users
socket.on("userOnline", async (data) => {
try {
if (!data || !data.user || data.user === "undefined") return;
const { user, token, pic } = data;
let ho = await User.findById(user);
// Add the user to the users object with socket ID as the key
users[socket.id] = { user, pic };
// Emit a notification to all connected clients about the new user
if (ho) {
socket.broadcast.emit("notification", {
title: ho.name + ` has joined`,
avatar: ho.pic,
createdAt: new Date(),
isUnread: true,
});
}
// Update the user's status in the database (assuming you have a model named 'User')
try {
const updatedUser = await User.findByIdAndUpdate(
user._id,
{ status: "active" },
{ new: true }
);
if (updatedUser) {
// Emit the updated user data to the current client
socket.emit("userData", updatedUser);
}
} catch (error) {
console.error(error);
}
} catch (error) {
// Ignore
}
});
// Handle client disconnection
socket.on("disconnect", async () => {
io.emit("online", users.length)
// Get the user associated with the disconnected socket
const user = users[socket.id];
// Remove the user from the users object
delete users[socket.id];
// Emit the updated number of online users to all clients
io.emit("online", Object.keys(users).length);
if (user) {
// Update the user's status in the database (assuming you have a model named 'User')
try {
const updatedUser = await User.findByIdAndUpdate(
user._id,
{ status: "inactive" },
{ new: true }
);
if (updatedUser) {
// Emit the updated user data to the current client
socket.emit("userData", updatedUser);
}
} catch (error) {
//console.error(error);
}
}
});
});
// Function to check online users and update their status
const checkOnlineUsers = async () => {
for (const socketId in users) {
const user = users[socketId].user;
// Check if the user is online in the socket.io
if (!io.sockets.sockets[socketId]) {
// User is offline, update their status in the database (assuming you have a model named 'User')
try {
const updatedUser = await User.findByIdAndUpdate(
user._id,
{ status: "offline" },
{ new: true }
);
if (updatedUser) {
// Emit the updated user data to the current client
io.emit("userData", updatedUser);
}
} catch (error) {
console.error(error);
}
}
}
};
// Run the checkOnlineUsers function every 30 minutes (adjust the interval as needed)
setInterval(checkOnlineUsers, 30 * 60 * 1000);
// Function to send book rent messages
const sendRentMessages = async () => {
// Logic to retrieve books nearing the completion of their rental period
const books = await Book.find({ rentEndDate: { $gte: new Date() } });
for (const book of books) {
const daysRemaining = Math.ceil(
(book.rentEndDate - new Date()) / (1000 * 60 * 60 * 24)
);
// Check if the book is 25 days away from completion
if (daysRemaining === 25) {
const message = `Your rented book "${book.title}" will expire in 5 days.`;
// Send the message to the user's email (assuming you have a function named 'sendEmail')
sendEmail(book.user.email, "Rent Expiration Notification", message);
// Check if the user associated with the book is online
const userSocket = Object.values(users).find(
(u) => u.user._id === book.user._id
);
if (userSocket) {
// Send a notification message through Socket.IO
const notification = {
title: "Rent Expiration",
message: message,
createdAt: new Date(),
isUnread: true,
};
io.to(userSocket.socketId).emit("notification", notification);
}
}
// Check if the book is 30 days away from completion
if (daysRemaining === 30) {
const message = `Your rented book "${book.title}" will expire in 1 day. Please return the book to avoid late fees.`;
// Send the message to the user's email
sendEmail(book.user.email, "Rent Expiration Warning", message);
// Check if the user associated with the book is online
const userSocket = Object.values(users).find(
(u) => u.user._id === book.user._id
);
if (userSocket) {
// Send a notification message through Socket.IO
const notification = {
title: "Rent Expiration Warning",
message: message,
createdAt: new Date(),
isUnread: true,
};
io.to(userSocket.socketId).emit("notification", notification);
}
}
// Check if the book is overdue by 30 days or more
if (daysRemaining < 0) {
const daysOverdue = Math.abs(daysRemaining);
const lateFee = daysOverdue * 1; // Assuming late fee is 1 rs per day
const message = `Your rented book "${book.title}" is ${daysOverdue} days overdue. Please return the book and pay the late fee of ${lateFee} rs.`;
// Send the message to the user's email
sendEmail(book.user.email, "Rent Overdue", message);
// Check if the user associated with the book is online
const userSocket = Object.values(users).find(
(u) => u.user._id === book.user._id
);
if (userSocket) {
// Send a notification message through Socket.IO
const notification = {
title: "Rent Overdue",
message: message,
createdAt: new Date(),
isUnread: true,
};
io.to(userSocket.socketId).emit("notification", notification);
}
}
}
};
// Run the sendRentMessages function every day at a specific time (adjust the time as needed)
const sendRentMessagesJob = schedule.scheduleJob("0 0 * * *", sendRentMessages);
// Start the server
server.listen(SERVER_PORT, () => {
log(`Server running on port ${SERVER_PORT}`, "i");
log(`Server is running at http://127.0.0.1:${SERVER_PORT}`, "i");
log(`Open above url to view the app :)`, "i");
});