Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"express": "^4.19.2",
"ioredis": "^5.4.2",
"jsonwebtoken": "^9.0.2",
"luxon": "^3.5.0",
"mongoose": "^8.4.4",
"node-cron": "^3.0.3",
"nodemailer": "^6.9.16",
Expand Down
18 changes: 18 additions & 0 deletions Backend/src/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,28 @@ const TOURNAMENT_TYPES = Object.freeze([
"Squad",
]);

const USER_ROLES = Object.freeze([
"USER",
"ADMIN",
]);

const REGION = `Asia/Kolkata`;

const GAME_ID = Object.freeze({
"Battlegrounds Mobile India": "bgmiId",
"Call of Duty Mobile": "codmId",
"Valorant": "valorantId",
"Free Fire": "freefireId",
"Asphalt 9": "asphaltId",
});

export {
DB_NAME,
COOKIE_OPTIONS,
USER_BADGES,
GAMES,
TOURNAMENT_TYPES,
USER_ROLES,
REGION,
GAME_ID,
};
222 changes: 81 additions & 141 deletions Backend/src/controllers/tournament.controller.js
Original file line number Diff line number Diff line change
@@ -1,209 +1,149 @@
import { DateTime } from "luxon";

import asyncHandler from "../utils/asyncHandler.js";
import ApiError from "../utils/ApiError.js";
import ApiResponse from "../utils/ApiResponse.js";
import { Game } from "../models/gameId.models.js";
import { Tournament } from "../models/tournament.models.js";
import { Result } from "../models/result.models.js";
import mongoose from "mongoose";
import { REGION, GAME_ID } from "../constants.js";
import { check24HourFormat, checkDateFormat } from "../helpers/checkDateTime.js";

const createTournaments = asyncHandler(async(req,res)=>{
const now = new Date();

const {name,
// admin controlled routes
const createTournament = asyncHandler(async (req, res) => {
const {
name,
matchDate,
matchTime,
registrationEndDate,
registrationEndTime,
totalSlots,
prizePool,
type,
game,
entryFee,
description,
instructions,} = req.body;

const now = new Date();
if(new Date(registrationEndDate) <= now){
throw new ApiError(400,"Registration end date must be in future!")
instructions
} = req.body;
if (!check24HourFormat(matchTime) || !check24HourFormat(registrationEndTime)) {
throw new ApiError(400, "Invalid Time Format. Please Use hh:mm Format.");
}

if (new Date(matchDate) <= new Date(registrationEndDate)) {
throw new ApiError(400,"Match must be after registration ends!")
if (!checkDateFormat(matchDate) || !checkDateFormat(registrationEndDate)) {
throw new ApiError(400, "Invalid Date Format. Please Use yyyy-MM-dd Format.");
}
if(new Date(`${registrationEndDate}T${registrationEndTime}`) <= now){
throw new ApiError(400, "Registration End Date Must Be Greater Than Current Date and Time.");
}
if (new Date(`${matchDate}T${matchTime}`) <= new Date(`${registrationEndDate}T${registrationEndTime}`)) {
throw new ApiError(400, "Match Date Must Be Greater Than Registration End Date and Time.");
}

const existingTournament = await Tournament.findOne({ name });

if (existingTournament) {
throw new ApiError(400, "Tournament already exists with this name");
throw new ApiError(400, "Tournament With This Name Already Exists.");
}

try {
const matchTiming = DateTime.fromISO(`${matchDate}T${matchTime}`, { zone: REGION });
const registrationTiming = DateTime.fromISO(`${registrationEndDate}T${registrationEndTime}`, { zone: REGION });
const tournament = await Tournament.create({
name,
matchDate,
matchTime,
registrationEndDate,
matchDate: matchTiming.toFormat("dd-MM-yyyy"),
matchTime: matchTiming.toFormat("HH:mm"),
registrationEndDate: registrationTiming.toFormat("dd-MM-yyyy"),
registrationEndTime: registrationTiming.toFormat("HH:mm"),
totalSlots,
prizePool,
type,
game,
entryFee,
description,
instructions,
})

});
return res
.status(201)
.json( new ApiResponse(201,tournament,"Tournament Created Successfully!!"))

.json( new ApiResponse(201, tournament, "Tournament Created Successfully!!"));
} catch (error) {
throw new ApiError(500, error.message || "Internal Server Error")
throw new ApiError(500, error.message || "Internal Server Error");
}
})

const getTournaments = asyncHandler(async(_,res)=>{
});

// user accessed routes
const getTournaments = asyncHandler(async (req, res) => {
try {
const tournaments = await Tournament.find({ isActive:true })
.sort({createdAt:-1})
.select("name matchDate matchTime registrationEndDate totalSlots filledSlots prizePool type game entryFee rating description instructions")
const tournaments = await Tournament.find({
isActive: true,
isOngoing: true,
}).sort({
createdAt:-1,
}).select(
"name matchDate matchTime registrationEndDate registrationEndTime totalSlots filledSlots prizePool type game entryFee rating description instructions"
);
return res
.status(200)
.json(new ApiResponse(200,tournaments,"Tournaments fetched successfully!!"))
.json(new ApiResponse(200, tournaments, "Tournaments Fetched Successfully!!"));
} catch (error) {
throw new ApiError(500, error.message || "Internal Server Error")
throw new ApiError(500, error.message || "Internal Server Error");
}
})

const tournamentInfo = asyncHandler(async(req,res)=>{
const getTournamentInfo = asyncHandler(async (req,res) => {
const { tournamentName } = req.body;

if (!tournamentName) {
throw new ApiError(400, "Tournament Name is Required.");
}
const tournament = await Tournament.findOne({ name: tournamentName });
if (!tournament) {
throw new ApiError(404, "Tournament not found");
}

return res.
status(200).
json(new ApiResponse(200, tournament, "Tournament fetched successfully!!"));

try {
return res
.status(200)
.json(new ApiResponse(200, tournament, "Tournament fetched successfully!!"));
} catch (error) {
throw new ApiError(500, error.message || "Internal Server Error");
}
})

const registerPlayer = asyncHandler(async (req, res) => {
const registerUser = asyncHandler(async (req, res) => {
const { tournamentName } = req.body;
const user = req.user;

const tournament = await Tournament.findOne({ name: tournamentName });
if (!tournament) {
throw new ApiError(404, "Tournament not found");
throw new ApiError(404, "Tournament Not Found.");
}

if (!tournament.isActive) {
throw new ApiError(400, "Tournament is not active");
}

if (tournament.registeredPlayers.includes(user._id)) {
throw new ApiError(400, "You are already registered for this tournament");
throw new ApiError(400, "Tournament Is Not Active.");
}

if (tournament.filledSlots >= tournament.totalSlots) {
throw new ApiError(400, "Tournament is full");
throw new ApiError(400, "Tournament Is Full.");
}
const tournamentGame = GAME_ID[tournament.game];
const userHasGameId = await Game.findOne({ owner: user._id });
if (!userHasGameId || !userHasGameId[tournamentGame]) {
throw new ApiError(400, "Please Add Game Id First.");
}

const userRegistered = await Tournament.findOne({ registeredPlayers: user._id });
if (userRegistered) {
throw new ApiError(400, "User Already Registered For This Tournament.");
}

try {
user.registeredTournaments.push(tournament._id);
await user.save();
tournament.registeredPlayers.push(user._id);
tournament.filledSlots += 1;
await tournament.save();

return res.status(200).json(new ApiResponse(200, "Registered Successfully!!"));
} catch (error) {
throw new ApiError(500, error.message || "Internal Server Error");
}
});

const postResult = asyncHandler(async (req, res) => {
const { tournamentName, leaderboard } = req.body;

const tournament = await Tournament.findOne({ name: tournamentName });
if (!tournament) {
throw new ApiError(404, "Tournament not found");
}

if (!tournament.isActive) {
throw new ApiError(400, "Tournament is not active");
}

if(!leaderboard || leaderboard.length === 0){
throw new ApiError(400, "Leaderboard is required");
}

try {
await Result.create({
tournament: tournament._id,
leaderboard,
});

return res
.status(201)
.json(new ApiResponse(201, "Result Posted Successfully!!"));
.status(200)
.json(new ApiResponse(200, null,"Registered Successfully!!"));
} catch (error) {
throw new ApiError(500, error.message || "Internal Server Error");
}
});

//for admin
const getResults = asyncHandler(async (req, res) => {
const { tournamentName } = req.body;

const tournament = await Tournament.findOne({ name: tournamentName });

if (!tournament) {
throw new ApiError(404, "Tournament not found");
}

const results = await Result.find({ tournament: tournament._id })

if (!results) {
throw new ApiError(404, "Results not found");
}

return res
.status(200)
.json(new ApiResponse(200, results, "Results fetched successfully!!"));
});

//for user
const getIndividualResult = asyncHandler(async (req, res) => {
const { tournamentName } = req.body;
const user = req.user;

if(!tournamentName){
throw new ApiError(400, "Tournament name is required");
}

const tournament = await Tournament.findOne({ name: tournamentName });

if (!tournament) {
throw new ApiError(404, "Tournament not found");
}

const position = await Result.findOne(
{ tournament: tournament._id, "leaderboard.player": user._id },
{ "leaderboard.$": 1 }
).select("-tournament -_id -createdAt -updatedAt -__v");

if (!position) {
throw new ApiError(404, "Result not found");
}

return res
.status(200)
.json(new ApiResponse(200, position, "Result fetched successfully!!"));
});

export {
createTournaments,
getTournaments,
registerPlayer,
tournamentInfo,
postResult,
getResults,
getIndividualResult,
}
export {
createTournament,
getTournaments,
getTournamentInfo,
registerUser,
};
22 changes: 22 additions & 0 deletions Backend/src/helpers/checkDateTime.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { DateTime } from "luxon";

const check24HourFormat = (time) => {
const parsedTime = DateTime.fromFormat(time, "HH:mm");
if (!parsedTime.isValid) {
return false;
}
return true;
};

const checkDateFormat = (date) => {
const parsedDate = DateTime.fromFormat(date, "yyyy-MM-dd");
if (!parsedDate.isValid) {
return false;
}
return true;
}

export {
check24HourFormat,
checkDateFormat,
};
27 changes: 27 additions & 0 deletions Backend/src/middlewares/isAdmin.middleware.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import ApiError from "../utils/ApiError.js";
import jwt from "jsonwebtoken";
import asyncHandler from "../utils/asyncHandler.js";
import { User } from "../models/user.models.js";
import { USER_ROLES } from "../constants.js";

const isAdmin = asyncHandler(async (req, res, next) => {
const token = req.cookies?.token || req.header("Authorization")?.replace("Bearer ", "");
if (!token) {
throw new ApiError(401, "Unauthorized Access.");
}
const decoded = jwt.verify(token, process.env.JWT_SECRET);
const user = await User.findById(decoded.id);
if (!user) {
throw new ApiError(401, "Unauthorized Access.");
}
if (user.role !== USER_ROLES[1]) {
throw new ApiError(403, "Forbidden Access.");
}
try {
next();
} catch (error) {
throw new ApiError(500, error.message || "Internal Server Error");
}
});

export default isAdmin;
Loading