Skip to content

Commit e3a1eb6

Browse files
committed
feat: add swagger and fix the bugs after testing
1 parent 73c1e30 commit e3a1eb6

22 files changed

+1583
-860
lines changed

Readme.md

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# Codefest'25 - IIT (BHU)
2+
3+
This is the backend for Codefest'25, fest conducted by CSE department of IIT (BHU) Varanasi.
4+
5+
## Setup
6+
7+
- Fork the repository into your account
8+
- Clone the forked repository
9+
- Install the dependencies using the following command:
10+
11+
```bash
12+
npm i
13+
```
14+
15+
- Create a .env in the root folder using the .env.example given
16+
- Run the development server using:
17+
```bash
18+
npm run dev
19+
```
20+
- Open `http://localhost:{PORT}/api-docs` to see the API documentation
21+
22+
## Contribution
23+
24+
- Create a branch for the task you're assigned with
25+
- Make changes in that branch
26+
- Commit your changes and push to the your forked repo.
27+
- Open a pull request to the upstream (this main repo) and wait for a maintainer to merge your PR.
28+
29+
- Every now and then, do `git fetch --all` and `git rebase upstream/main` to keep your fork synced with the upstream.
30+
31+
- If you can't see upstream in `git remote -v`, do `git remote add upstream "https://github.com/codefest-iit-bhu/codefest_backend_25"` to add the upstream

app.js

+10-4
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ import memberRouter from "./routes/members.js";
1313
import caRouter from "./routes/ca.js";
1414
import eventRouter from "./routes/events.js";
1515
import userRouter from "./routes/user.js";
16+
import swaggerUi from "swagger-ui-express";
17+
import { loadSwaggerWithDynamicUrl } from "./utils/features.js";
1618

1719
export const app = express();
1820

@@ -22,19 +24,23 @@ app.use(express.json());
2224
app.use(cookieParser());
2325
app.use(
2426
cors({
25-
origin: [process.env.LOCAL_FRONTEND_URL, process.env.FRONTEND_URL],
27+
origin: ["*"],
2628
methods: ["GET", "POST", "PUT", "PATCH", "DELETE"],
2729
credentials: true,
2830
})
2931
);
3032

33+
const swaggerDocument = loadSwaggerWithDynamicUrl("./docs/swagger.yaml")
34+
35+
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerDocument));
36+
3137
app.use(passport.initialize());
3238
passport.use(
3339
new GoogleStrategy(
3440
{
3541
clientID: process.env.GOOGLE_CLIENT_ID,
3642
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
37-
callbackURL: `${backendUrl}/Oauth2/google/callback`,
43+
callbackURL: `${backendUrl}/api/v1/Oauth2/google/callback`,
3844
scope: ["profile", "email"],
3945
},
4046
async function (accessToken, refreshToken, profile, cb) {
@@ -44,12 +50,12 @@ passport.use(
4450
);
4551

4652
app.get(
47-
"/Oauth2/google",
53+
"/api/v1/Oauth2/google",
4854
passport.authenticate("google", { scope: ["profile", "email"] })
4955
);
5056

5157
app.get(
52-
"/Oauth2/google/callback",
58+
"/api/v1/Oauth2/google/callback",
5359
passport.authenticate("google", {
5460
failureRedirect: `${frontendUrl}/`,
5561
session: false,

controllers/auth.js

+29-14
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ export const signup = async (req, res, next) => {
3131

3232
let user = await User.findOne({ email });
3333
if (user) {
34-
return next(new ErrorHandler("User Already Exist", 404));
34+
return next(new ErrorHandler("User Already Exist", 400));
3535
}
3636

3737
const hashedpswd = await bcrypt.hash(password, 10);
@@ -71,7 +71,18 @@ export const googleCallback = async (user, req, res, next) => {
7171

7272
if (!googleUser.password)
7373
res.redirect(`${frontendUrl}/setPassword?email=${email}`);
74-
else res.redirect(`${frontendUrl}/main?email=${email}`);
74+
else {
75+
const refreshToken = await generateRefreshToken(googleUser);
76+
const token = jwt.sign({ _id: user._id }, process.env.JWT_SECRET, {
77+
expiresIn: "30m",
78+
});
79+
res.cookie("token", token, {
80+
httpOnly: true,
81+
maxAge: 30 * 60 * 1000, // 30 minutes
82+
sameSite: process.env.NODE_ENV === "development" ? "lax" : "none",
83+
secure: process.env.NODE_ENV === "development" ? false : true,
84+
}).redirect(`${frontendUrl}/main?refreshToken=${refreshToken}`);
85+
}
7586
} catch (error) {
7687
next(error);
7788
}
@@ -84,12 +95,12 @@ export const login = async (req, res, next) => {
8495
const user = await User.findOne({ email }).select("+password");
8596

8697
if (!user) {
87-
return next(new ErrorHandler("Invalid Email!", 404));
98+
return next(new ErrorHandler("Invalid Email!", 400));
8899
}
89100

90101
const isMatch = await bcrypt.compare(password, user.password);
91102
if (!isMatch) {
92-
return next(new ErrorHandler("Invalid Password!", 404));
103+
return next(new ErrorHandler("Invalid Password!", 400));
93104
}
94105

95106
const refreshToken = await generateRefreshToken(user);
@@ -107,15 +118,19 @@ export const login = async (req, res, next) => {
107118
};
108119

109120
export const profile = (req, res) => {
110-
res.status(200).json({
111-
success: true,
112-
user: req.user,
113-
});
121+
res.status(200).json(req.user);
114122
};
115123

116124
export const passwordSetter = async (req, res, next) => {
117125
try {
118126
const { email, password } = req.body;
127+
128+
if (!validator.isLength(password, { min: 8 })) {
129+
return next(
130+
new ErrorHandler("Password must be minimum 8 characters", 400)
131+
);
132+
}
133+
119134
const user = await User.findOne({ email }).select("+password");
120135

121136
if (!user) {
@@ -140,9 +155,9 @@ export const passwordSetter = async (req, res, next) => {
140155

141156
export const logout = async (req, res, next) => {
142157
try {
143-
const refreshToken = req.headers["X-Refresh-Token"];
158+
const refreshToken = req.headers["x-refresh-token"];
144159
if (!refreshToken)
145-
return next(new ErrorHandler("Please provide refresh token", 404));
160+
return next(new ErrorHandler("Please provide refresh token", 400));
146161

147162
await Session.deleteOne({ user: req.user._id, refreshToken });
148163

@@ -164,18 +179,18 @@ export const logout = async (req, res, next) => {
164179

165180
export const refreshJwt = async (req, res, next) => {
166181
try {
167-
const refreshToken = req.headers["X-Refresh-Token"];
182+
const refreshToken = req.headers["x-refresh-token"];
168183
if (!refreshToken)
169184
return next(new ErrorHandler("Please provide refresh token", 404));
170185

171-
const decoded = jwt.decode(refToken, process.env.JWT_SECRET);
186+
const decoded = jwt.decode(refreshToken, process.env.JWT_SECRET);
172187
if (!decoded) return next(new ErrorHandler("Invalid refresh token", 404));
173188

174189
const session = Session.findOne({ user: decoded._id });
175190
if (!session) return next(new ErrorHandler("No session found", 404));
176191

177192
saveCookie(
178-
user,
193+
{ "_id": session.user },
179194
res,
180195
next,
181196
200,
@@ -192,7 +207,7 @@ export const verifyEmail = async (req, res, next) => {
192207
const { email, otp } = req.body;
193208
const verification = await Verification.findOne({ email });
194209
if (!verification || verification.code != otp)
195-
return next(new ErrorHandler("OTP Invalid or Expired", 404));
210+
return next(new ErrorHandler("OTP Invalid or Expired", 400));
196211
const user = await User.create({
197212
name: verification.name,
198213
email: verification.email,

controllers/ca.js

+27-7
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
import ErrorHandler from "../middlewares/error.js";
22
import { CARequest } from "../models/ca_request.js";
3+
import { User } from "../models/user.js";
34

45
export const register = async (req, res, next) => {
56
try {
67
const request = await CARequest.findOne({ user: req.user._id });
78
if (request)
8-
return next(new ErrorHandler("CA Request already exists", 404));
9+
return next(new ErrorHandler("CA Request already exists", 400));
910
const { institute, userDescription } = req.body;
1011
await CARequest.create({
1112
user: req.user._id,
@@ -45,19 +46,38 @@ export const getAllRequests = async (req, res, next) => {
4546

4647
export const updateRequest = async (req, res, next) => {
4748
try {
49+
const { status, institute, userDescription, adminMessage } = req.body
4850
let request = await CARequest.findOne({ _id: req.params.id });
4951
if (!request) return next(new ErrorHandler("CA request not found", 404));
50-
if (req.body.status) request.status = req.body.status;
52+
if (req.user.role !== "admin" && status != "pending") {
53+
return next(
54+
new ErrorHandler(
55+
"You are not allowed to approve or reject this request",
56+
403
57+
)
58+
);
59+
}
60+
if (status) {
61+
request.status = status;
62+
if (req.user.role === "admin" && status === "approved") {
63+
await User.findByIdAndUpdate(request.user, { role: "ca" });
64+
}
65+
66+
if (req.user.role === "admin" && status !== "approved" && request.status === "approved") {
67+
await User.findByIdAndUpdate(request.user, { role: "user" });
68+
}
69+
}
70+
5171
if (req.user.role !== "admin") {
52-
if (req.body.institute) request.institute = req.body.institute;
53-
if (req.body.userDescription)
54-
request.userDescription = req.body.userDescription;
72+
if (institute) request.institute = institute;
73+
if (userDescription)
74+
request.userDescription = userDescription;
5575
} else {
56-
if (req.body.adminMessage) request.adminMessage = req.body.adminMessage;
76+
if (adminMessage) request.adminMessage = adminMessage;
5777
}
5878
const updatedRequest = await request.save();
5979
if (!updatedRequest)
60-
return next(new ErrorHandler("CA request couldn't be updated", 404));
80+
return next(new ErrorHandler("CA request couldn't be updated", 500));
6181
res.status(200).json(updatedRequest);
6282
} catch (error) {
6383
next(error);

controllers/events.js

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,22 @@
11
import { Events } from "../models/events.js";
22
import ErrorHandler from "../middlewares/error.js";
3+
import { convertToDate } from "../utils/features.js";
34

45
export const addEvent = async (req, res, next) => {
56
try {
67
if (req.user.role !== "admin") {
78
return next(new ErrorHandler("Only admin can access", 403));
89
}
910

10-
const { eventId, maxMembers, eventDeadline } = req.body;
11+
// eventDeadline in the format DD-MM-YYYY
12+
let { eventId, maxMembers, eventDeadline } = req.body;
1113

1214
if (await Events.findOne({ eventId })) {
1315
return next(new ErrorHandler("Event already exists", 400));
1416
}
1517

18+
eventDeadline = convertToDate(eventDeadline);
19+
1620
const event = await Events.create({
1721
eventId,
1822
maxMembers,

controllers/members.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ export const joinTeam = async (req, res, next) => {
77
try {
88
const { teamCode } = req.body;
99
const team = await Team.findOne({ teamCode });
10+
if (!team) return next(new ErrorHandler("Team not found", 404))
1011
const members = await Members.find({ team: team._id });
1112

1213
if (!team) {
@@ -51,7 +52,7 @@ export const joinTeam = async (req, res, next) => {
5152

5253
export const getMembers = async (req, res, next) => {
5354
try {
54-
const { teamId } = req.body;
55+
const { teamId } = req.params;
5556

5657
const members = await Members.find({ team: teamId }).populate("user");
5758
if (members.length === 0) {

controllers/team.js

+53-6
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ export const changeLeader = async (req, res, next) => {
8686
team: team._id,
8787
});
8888
if (!member) {
89-
return next(new ErrorHandler("Member Not Found", 404));
89+
return next(new ErrorHandler("Member Not Found in the team", 404));
9090
}
9191

9292
team.teamLeader = newLeader;
@@ -102,10 +102,56 @@ export const changeLeader = async (req, res, next) => {
102102

103103
export const getTeams = async (req, res, next) => {
104104
try {
105-
const teams = await Members.find({ user: req.user._id }).populate(
106-
"team",
107-
"user"
108-
);
105+
const teams = await Team.aggregate([
106+
{
107+
$lookup: {
108+
from: "members",
109+
localField: "_id",
110+
foreignField: "team",
111+
as: "members",
112+
},
113+
},
114+
{
115+
$match: {
116+
"members.user": req.user._id,
117+
},
118+
},
119+
{
120+
$lookup: {
121+
from: "users",
122+
localField: "members.user",
123+
foreignField: "_id",
124+
as: "userDetails",
125+
},
126+
},
127+
{
128+
$addFields: {
129+
members: {
130+
$map: {
131+
input: "$members",
132+
as: "member",
133+
in: {
134+
$mergeObjects: [
135+
"$$member",
136+
{
137+
user: {
138+
$arrayElemAt: [
139+
"$userDetails",
140+
{ $indexOfArray: ["$userDetails._id", "$$member.user"] },
141+
],
142+
},
143+
},
144+
],
145+
},
146+
},
147+
},
148+
},
149+
},
150+
{
151+
$unset: "userDetails",
152+
},
153+
]);
154+
109155
res.status(200).json(teams);
110156
} catch (error) {
111157
next(error);
@@ -115,7 +161,8 @@ export const getTeams = async (req, res, next) => {
115161
export const nameAvailable = async (req, res, next) => {
116162
try {
117163
const { name } = req.body;
118-
if (await Team.findOne({ name: name }))
164+
const team = await Team.findOne({ teamName: name })
165+
if (team)
119166
return res.status(200).json({ status: "failure" });
120167
else return res.status(200).json({ status: "success" });
121168
} catch (error) {

0 commit comments

Comments
 (0)