Skip to content
Open
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
7 changes: 6 additions & 1 deletion apps/backend/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@

import express from "express";
import userRouter from "./routes/user"
import contestRouter from "./routes/contest"
import adminRouter from "./routes/admin"
import contestRouter from "./routes/contest"
import cors from "cors"

const app = express();
app.use(cors())
app.use(express.json());


app.use("/user", userRouter);
app.use("/admin", adminRouter);
Expand Down
67 changes: 67 additions & 0 deletions apps/backend/middleware/admin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { client } from "db/client";
import { type NextFunction, type Request, type Response } from "express"
import jwt from "jsonwebtoken"



interface authenticatedRequest extends Request {
userId?: string,
userRole?: string
}

export const adminMiddleware = async(
req: authenticatedRequest,
res: Response,
next: NextFunction
) => {

try {
const token = req.headers.authorization?.replace("Bearer ", "");

if (!token) {
return res.status(401).json({ error: "Missing token"});
}

const decoded = jwt.verify(token, process.env.JWT_SECRET!) as any;

if (!decoded) {
return res.status(401).json({ error: "Invalid token"});
}

const user = await client.user.findUnique({
where: {
id: decoded.userId
},
select: {
role: true,
id: true
}
})


if (!user || user.role !== "Admin"){
return res.status(401).json({ error: "Unauthorized"});
}


req.userId = user.id;
req.userRole = user.role;




next();








} catch (error) {
res.status(401).json({ error: "Invalid token"});
}


}
58 changes: 58 additions & 0 deletions apps/backend/middleware/user.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@

import { client } from "db/client"
import { type Request, type Response, type NextFunction } from "express"
import jwt from "jsonwebtoken"


export interface authenticatedRequest extends Request {
userId?: string
userRole?: string
}

export const userMiddleware = async(
req: authenticatedRequest,
res: Response,
next: NextFunction
) => {
try {

const token = req.headers.authorization?.replace("Bearer ", "");


if (!token) {
return res.status(401).json({ error: "Missing token"});
}

const decoded = jwt.verify(token, process.env.JWT_SECRET!) as any;

const user = await client.user.findUnique({
where: {
id: decoded.userId
},
select: {
role: true,
id: true
}
});


if (!user) {
return res.status(401).json({ error: "Unauthorized"});
}


req.userId = user.id;
req.userRole = user.role;



next();




} catch (error) {
res.status(401).json({ error: "Unauthorized" })
}

}
12 changes: 11 additions & 1 deletion apps/backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,18 @@
"typescript": "^5.0.0"
},
"dependencies": {
"@types/bcrypt": "^6.0.0",
"@types/cors": "^2.8.19",
"@types/express": "^5.0.3",
"@types/express-rate-limit": "^6.0.2",
"@types/jsonwebtoken": "^9.0.10",
"bcrypt": "^6.0.0",
"cors": "^2.8.5",
"db": "*",
"express": "^5.1.0",
"db": "*"
"express-rate-limit": "^8.0.1",
"jsonwebtoken": "^9.0.2",
"nodemailer": "^7.0.6",
"zod": "^4.1.5"
}
}
121 changes: 121 additions & 0 deletions apps/backend/routes/admin-contest.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import { client } from "db/client";
import { Router } from "express";
import { adminMiddleware } from "../middleware/admin";
import z from "zod"

const router = Router();

// Admin

router.post("/contest", adminMiddleware, async(req, res ) => {
try {

const createContestSchema = z.object({
title: z.string().min(1, "title is missing "),
startTime: z.string().datetime("Invalid datetime format"),
challengeIds: z.array(z.string()).min(1, "one challenge required")
})


const { title, startTime, challengeIds } = createContestSchema.parse(req.body);


const challenges = await client.challenge.findMany({
where: {
id: {
in: challengeIds
}
}
})


if (challenges.length !== challengeIds.length) {
return res.status(404).json({ error: "Challenge not found"});
}

const contest = await client.contest.create({
data: {
title,
startTime: new Date(startTime),
contestToChallengeMapping: {
create: challengeIds.map((challengeId, index ) => ({
challengeId,
index
}))
}
},
include: {
contestToChallengeMapping: {
include: {
challenge: true
}
}
}
})

res.status(201).json({
"msg": "Contest created successfully",
"contest": contest
});




} catch (error) {
res.status(500).json({ error: "Internal server error"});
}
})


//create challenge
router.post("/challenge", adminMiddleware, async(req, res) => {
try {

const createChallengeSchema = z.object({
title: z.string().min(1, "Title is required"),
notionDocId: z.string().min(1, "Notion doc ID is required"),
maxPoints: z.number().int().positive("Max points must be positive")
});

const { title, notionDocId, maxPoints } = createChallengeSchema.parse(req.body);


const challenge = await client.challenge.create({
data: {
title,
notionDocId,
maxPoints
}
})

res.status(201).json({
"msg": "Challenge created successfully",
"challenge": challenge
});



} catch (error) {
res.status(500).json({ error: "Internal server error"});
}
});


// getting all challenges
router.get("/challenges", adminMiddleware, async(req, res) => {
const challengs = await client.challenge.findMany({
orderBy: {
title: "asc"
}
})

res.status(200).json({
"challenges": challengs
});
})





export default router;
37 changes: 34 additions & 3 deletions apps/backend/routes/admin.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,46 @@
import { Router } from "express";
import { client } from "db/client";
import z from "zod"
import bcrypt from "bcrypt"

const router = Router();

router.post("/signup", (req, res) => {
router.post("/signup", async(req, res) => {

const signupSchema = z.object({
email: z.string().email(),
password: z.string().min(6),
});

})
console.log(req.body);

const { email, password } = signupSchema.parse(req.body);

console.log(email, password);

const existingUser = await client.user.findUnique({
where: {
email: email
}
})

router.post("/signin", (req, res) => {
if(existingUser) {
return res.status(400).json({ error: "User already exists"});
}

const hashedPassword = await bcrypt.hash(password, 10);

const user = await client.user.create({
data: {
email: email,
password: hashedPassword,
role: "Admin"
}
})

res.status(201).json(user);

})


export default router;
Loading