-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathapp.js
228 lines (183 loc) · 6.44 KB
/
app.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
// libraries
import express from "express";
import http from "http";
import { Server } from "socket.io";
import cors from "cors";
import path from "path";
import { fileURLToPath } from "url";
import cookieParser from "cookie-parser";
import Agenda from "agenda";
//utils
import sendErrorMail from "./utils/sendErrorMail.js";
import responseHandler from "./middlewares/responseHandler.js";
import connectMongo from "./config/db.js";
import config from "./config/config.js";
import getIstDate from "./utils/getIstDate.js";
import jobDefinitions from "./utils/jobDefinitions.js";
//routes
import teamRoutes from "./routes/team.routes.js";
import userRoutes from "./routes/user.routes.js";
import settingsRoutes from "./routes/settings.routes.js";
import handRoutes from "./routes/hand.routes.js";
import adminRoutes from "./routes/admin.routes.js";
import gameRoutes from "./routes/game.routes.js";
import ApiError from "./utils/errorClass.js";
console.log(getIstDate());
const app = express();
const server = http.createServer(app);
export const io = new Server(server);
io.on("connection", (socket) => {
console.log("a user connected", socket.id);
});
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
(async () => {
await connectMongo();
})();
export const agenda = new Agenda({
db: { address: config.database.uri },
processEvery: "20 seconds",
});
agenda.on("ready", () => {
console.log("Agenda started");
});
agenda.on("error", (error) => {
console.error("Agenda error:", error);
});
(async () => {
await agenda.start();
jobDefinitions(agenda);
})();
const allowedOrigins = [
"https://admin.mlsc.tech",
"https://app.mlsc.tech",
"https://canard.mlsc.tech",
"https://game.mlsc.tech",
"http://localhost:5173",
"https://api.mlsc.tech",
"http://localhost:4140",
"https://test-backend-7n6jp.ondigitalocean.app/"
];
app.use((req, res, next) => {
const secretKeyHeader = req.headers["api-key"];
const userAgent = req.headers["user-agent"] || "";
const origin = req.headers.origin;
const allowedHostnames = ["localhost", "api.mlsc.tech", "game.mlsc.tech", "test-backend-7n6jp.ondigitalocean.app"];
// Check if request is coming from an allowed hostname
const isAllowedHost =
allowedHostnames.includes(req.hostname) || req.ip === "127.0.0.1";
// Allow browser requests (with Origin) OR requests from allowed hostnames
if (origin || isAllowedHost) {
return next();
}
// If it's a non-browser request, only allow Postman and require API key
if (!userAgent.includes("Postman")) {
return res.status(403).json({ message: "Forbidden: Only Postman is allowed" });
}
if (secretKeyHeader !== process.env.APIKey) {
return res.status(401).json({ message: "Unauthorized: Invalid API key" });
}
next();
});
// CORS Middleware for browser-based requests
app.use(
cors({
origin: function (origin, callback) {
if (!origin || allowedOrigins.includes(origin)) {
callback(null, true);
} else {
callback(new ApiError(400, "Not allowed by CORS", "CORS_ERROR"));
}
},
})
);
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(cookieParser());
app.use(responseHandler);
app.use(express.static(path.join(__dirname, "public")));
app.set("view engine", "ejs");
app.set("views", path.join(__dirname, "./public/views"));
app.use((req, res, next) => {
// console.log(path.join(__dirname, "./public/adminApp/index.html"));
// console.log(path.join(__dirname, "./public/userApp/index.html"));
// console.log("Authorization header", req.headers["authorization"]);
console.log("printing body", req.body);
console.log(req.url, req.method);
next();
});
app.use((req, res, next) => {
const host = req.headers.host;
if (host === "game.mlsc.tech") {
gameRoutes(req, res, next);
} else {
next();
}
});
app.get("/", (req, res) => {
res.send("<h1>Canard 2025</h1>");
});
app.use("/team", teamRoutes);
app.use("/user", userRoutes);
app.use("/settings", settingsRoutes);
app.use("/hand", handRoutes);
app.use("/admin", adminRoutes);
app.use("/game", gameRoutes);
// app.use("/", extraRoutes);
app.use((error, req, res, next) => {
console.log(error);
if (error.errors && error.errors[0].message) {
// return res.error(400, error.errors[0].message, "VALIDATION_ERROR");
return res.status(400).json({ message: error.errors[0].message });
}
if (error.isOperational) {
const statusCode = error.statusCode || 500;
const message = error.message || "Internal Server Error";
return res.status(statusCode).json({
status: false,
errorCode: error.errorCode,
message,
data: error.data,
});
} else {
// sendErrorMail(error);
console.error("ALERT ALERT ALERT");
console.error("Unhandled error:", error);
return res.status(500).json({
status: false,
errorCode: "UNHANDLED_ERROR",
message: "Internal Server Error",
});
}
});
server.listen(config.server.port, () => {
console.log(`Server is running on http://localhost:${config.server.port}`);
});
// prepare the role based access control and role priority
// add a failsafe that the distribution script runs if it has not already before the event starts
// check for blocked teams everywhere
// what all information about the team do you want after completion of the event
// see if you can add a task time limit if its possible
//STATEMENTS
// completed tasks are the no of major tasks done
// I am not noting down the time of completion of minor tasks by any team
// the completed time of a phase is defined as the point of time when the team enters the correct answer for the phase (NOT WHEN THEY COMPLETE ALL THE MAJOR AND MINOR TASKS)
// dont make team idle after completion of major tasks, only after completion of all tasks(i.e. minor included)
// Allow dynamic task changing and completion
// shaders cache karne hain before rebuilding the app
// send rebuild to everyone on task completion
// different admins for different tasks
// fix kdratio - 45:5 - 9:1
// send rebuild on answer completion too
// add time check in agenda
// remove left hand right hand decresion for major tasks and add for minor tasks
//TEST POWERUPS
// add title to each
// has the response popup been added?
// security
//answer to be trimmed and lowercased - PLEASE CHECK THIS
//adjust 2nd phase answer for different hours
// check lowercase user login
// check all stats by opening them once
// make answers case sensitive
// make all three answers