-
Notifications
You must be signed in to change notification settings - Fork 55
Feature/create batch #223
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Feature/create batch #223
Changes from all commits
cbefec9
78dbc24
f2e5883
ae0295f
57c4189
61dfd89
29bc583
a8b4d8e
82d3b70
bde7d5e
4c6c048
10f256f
23d678a
a8a3181
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,187 @@ | ||||||
| const { | ||||||
| User, | ||||||
| PositionHolder, | ||||||
| Position, | ||||||
| OrganizationalUnit, | ||||||
| } = require("../models/schema"); | ||||||
| const { CertificateBatch } = require("../models/certificateSchema"); | ||||||
| const { validateBatchSchema, zodObjectId } = require("../utils/batchValidate"); | ||||||
|
|
||||||
| async function createBatch(req, res) { | ||||||
| try{ | ||||||
| //console.log(req.user); | ||||||
|
|
||||||
| /* | ||||||
| Get the id of user trying to initiate the request | ||||||
| and ensure if the person is of right authority | ||||||
| */ | ||||||
| const id = req.user.id; | ||||||
| const user = await User.findById(id); | ||||||
| if (!user) { | ||||||
| return res.status(404).json({ message: "User not found" }); | ||||||
| } | ||||||
| if (!user.role || user.role.toUpperCase() !== "CLUB_COORDINATOR") { | ||||||
| return res | ||||||
| .status(403) | ||||||
| .json({ message: "Not authorized to perform the task" }); | ||||||
| } | ||||||
|
|
||||||
| //to get user club | ||||||
| /* | ||||||
|
|
||||||
| positionHolders({user_id: id}) | ||||||
| -> positions({_id: position_id}) | ||||||
| -> organizationalUnit({_id: unit_id}) | ||||||
| -> {type === "Club" name} | ||||||
| */ | ||||||
| const { title, unit_id, commonData, template_id, users } = req.body; | ||||||
| const validation = validateBatchSchema.safeParse({ | ||||||
| title, | ||||||
| unit_id, | ||||||
| commonData, | ||||||
| template_id, | ||||||
| users, | ||||||
| }); | ||||||
|
|
||||||
| if (!validation.success) { | ||||||
| let errors = validation.error.issues.map((issue) => issue.message); | ||||||
| return res.status(400).json({ message: errors }); | ||||||
| } | ||||||
|
|
||||||
| //console.log(id); | ||||||
| // Get coordinator's position and unit | ||||||
| const positionHolder = await PositionHolder.findOne({ user_id: id }); | ||||||
| //console.log(positionHolder._id); | ||||||
| if (!positionHolder) { | ||||||
| return res | ||||||
| .status(403) | ||||||
| .json({ | ||||||
| message: | ||||||
| "Unauthorized to do the task as user doesn't hold any position in any unit", | ||||||
| }); | ||||||
| } | ||||||
|
|
||||||
| const position = await Position.findById(positionHolder.position_id); | ||||||
| //console.log(position._id); | ||||||
| if (!position) { | ||||||
| return res.status(403).json({ message: "Invalid user position" }); | ||||||
| } | ||||||
|
|
||||||
| /* | ||||||
| Check if the organization obtained by fetching related docs accross various collections | ||||||
| is same as the input OrgId received | ||||||
| */ | ||||||
| const userOrgId = position.unit_id.toString(); | ||||||
| if (unit_id !== userOrgId) { | ||||||
| return res.status(403).json({ | ||||||
| message: | ||||||
| "You are not authorized to initiate batches outside of your club", | ||||||
| }); | ||||||
| } | ||||||
|
|
||||||
| //const clubId = unit_id; | ||||||
| // Ensure unit_id is a Club | ||||||
| const unitObj = await OrganizationalUnit.findById(unit_id); | ||||||
| if (!unitObj || unitObj.type !== "Club") { | ||||||
| return res | ||||||
| .status(403) | ||||||
| .json({ message: "Invalid Data: Organization is not a Club" }); | ||||||
| } | ||||||
| //console.log(unitObj._id); | ||||||
|
|
||||||
| // Get council (parent unit) and ensure it's a Council | ||||||
| if (!unitObj.parent_unit_id) { | ||||||
| return res | ||||||
| .status(403) | ||||||
| .json({ message: "Invalid Data: club does not belong to a council" }); | ||||||
| } | ||||||
| //console.log(unitObj.parent_unit_id); | ||||||
|
|
||||||
| const councilObj = await OrganizationalUnit.findById(unitObj.parent_unit_id); | ||||||
| if ( | ||||||
| !councilObj || | ||||||
| councilObj.type !== "Council" || | ||||||
| !councilObj.parent_unit_id | ||||||
| ) { | ||||||
| return res | ||||||
| .status(403) | ||||||
| .json({ | ||||||
| message: | ||||||
| "Invalid Data: Organization is not a council or it's parent organization not found", | ||||||
| }); | ||||||
| } | ||||||
|
|
||||||
| //const councilId = councilObj._id.toString(); | ||||||
| const presidentOrgId = councilObj.parent_unit_id; | ||||||
|
|
||||||
| const presidentPosition = await Position.findOne({ | ||||||
| unit_id: presidentOrgId, | ||||||
| title: "President", | ||||||
| }); | ||||||
|
|
||||||
| if (!presidentPosition) { | ||||||
| return res.status(500).json({ message: "President position not found" }); | ||||||
| } | ||||||
|
|
||||||
| const presidentHolder = await PositionHolder.findOne({ | ||||||
| position_id: presidentPosition._id, | ||||||
| }); | ||||||
|
|
||||||
| if (!presidentHolder) { | ||||||
| return res | ||||||
| .status(500) | ||||||
| .json({ message: "President position holder not found" }); | ||||||
| } | ||||||
|
|
||||||
| const presidentObj = await User.findById(presidentHolder.user_id); | ||||||
|
|
||||||
| //console.log(presidentPosition._id); | ||||||
| const category = councilObj.category.toUpperCase(); | ||||||
| const gensecObj = await User.findOne({ role: `GENSEC_${category}` }); | ||||||
| if (!gensecObj || !presidentObj) { | ||||||
| return res.status(500).json({ message: "Approvers not found" }); | ||||||
| } | ||||||
|
|
||||||
| const approverIds = [gensecObj._id.toString(), presidentObj._id.toString()]; | ||||||
|
|
||||||
| const userChecks = await Promise.all( | ||||||
| users.map(async (uid) => { | ||||||
| const validation = zodObjectId.safeParse(uid); | ||||||
| if (!validation.success) { | ||||||
| return { uid, ok: false, reason: "Invalid ID" }; | ||||||
| } | ||||||
|
|
||||||
| const userObj = await User.findById(uid); | ||||||
| if (!userObj) return { uid, ok: false, reason: "User not found" }; | ||||||
|
|
||||||
| return { uid, ok: true }; | ||||||
| }), | ||||||
| ); | ||||||
|
|
||||||
| const invalidData = userChecks.filter((c) => !c.ok); | ||||||
| if (invalidData.length > 0) { | ||||||
| return res | ||||||
| .status(400) | ||||||
| .json({ message: "Invalid user data sent", details: invalidData }); | ||||||
| } | ||||||
|
|
||||||
| const newBatch = await CertificateBatch.create({ | ||||||
| title, | ||||||
| unit_id, | ||||||
| commonData, | ||||||
| templateId: template_id, | ||||||
| initiatedBy: id, | ||||||
| approverIds, | ||||||
| users, | ||||||
| }); | ||||||
|
|
||||||
| res.json({ message: "New Batch created successfully", details: newBatch }); | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Return 201 for resource creation.
Proposed fix- res.json({ message: "New Batch created successfully", details: newBatch });
+ res.status(201).json({ message: "New Batch created successfully", details: newBatch });📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||
| }catch(err){ | ||||||
| console.error(err); | ||||||
| return res.status(500).json({message: "Internal server error"}); | ||||||
| } | ||||||
| } | ||||||
coderabbitai[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
|
|
||||||
| module.exports = { | ||||||
| createBatch, | ||||||
| }; | ||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,7 +1,76 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const jwt = require("jsonwebtoken"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| //Passport based middleware to check whether the req are coming from authenticated users | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| function isAuthenticated(req, res, next) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (req.isAuthenticated && req.isAuthenticated()) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return next(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return res.status(401).json({ message: "Unauthorized: Please login first" }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| module.exports = isAuthenticated; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| //Token based middleware to check whether the req are coming from authenticated users or not | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| function jwtIsAuthenticated(req, res, next) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let token; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const headerData = req.headers.authorization; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!headerData || !headerData.startsWith("Bearer ")) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return res.status(401).json({ message: "User not authenticated " }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| token = headerData.split(" ")[1]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const userData = jwt.verify(token, process.env.JWT_SECRET_TOKEN); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| req.user = userData; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| //console.log(userData); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| next(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch (err) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| res.status(401).json({ message: "Invalid or expired token sent" }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+13
to
+28
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: cat package.json | grep -A 5 -B 5 jsonwebtokenRepository: OpenLake/Student_Database_COSA Length of output: 118 🌐 Web query:
💡 Result:
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| function jwtIsAuthenticated(req, res, next) { | |
| let token; | |
| const headerData = req.headers.authorization; | |
| if (!headerData || !headerData.startsWith("Bearer ")) { | |
| return res.status(401).json({ message: "User not authenticated " }); | |
| } | |
| token = headerData.split(" ")[1]; | |
| try { | |
| const userData = jwt.verify(token, process.env.JWT_SECRET_TOKEN); | |
| req.user = userData; | |
| //console.log(userData); | |
| next(); | |
| } catch (err) { | |
| res.status(401).json({ message: "Invalid or expired token sent" }); | |
| } | |
| function jwtIsAuthenticated(req, res, next) { | |
| let token; | |
| const headerData = req.headers.authorization; | |
| if (!headerData || !headerData.startsWith("Bearer ")) { | |
| return res.status(401).json({ message: "User not authenticated " }); | |
| } | |
| token = headerData.split(" ")[1]; | |
| try { | |
| const secret = process.env.JWT_SECRET_TOKEN; | |
| if (!secret) { | |
| return res.status(500).json({ message: "JWT secret not configured" }); | |
| } | |
| const userData = jwt.verify(token, secret, { | |
| algorithms: ["HS256"], | |
| }); | |
| req.user = userData; | |
| //console.log(userData); | |
| next(); | |
| } catch (err) { | |
| res.status(401).json({ message: "Invalid or expired token sent" }); | |
| } | |
| } |
🤖 Prompt for AI Agents
In `@backend/middlewares/isAuthenticated.js` around lines 13 - 28, In
jwtIsAuthenticated, ensure process.env.JWT_SECRET_TOKEN is present and return
res.status(500).json({ message: "Server misconfiguration: JWT secret missing" })
if not; when calling jwt.verify(token, process.env.JWT_SECRET_TOKEN) pass an
explicit algorithms allow-list (e.g. algorithms: ['HS256']) to prevent algorithm
downgrade attacks, assign the verified payload to req.user as before, and keep
the existing catch to return 401 for invalid/expired tokens.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Guard against
councilObj.categorybeing null/undefined.If
categoryis missing on the council document,councilObj.category.toUpperCase()throws aTypeError. The outertry-catchcatches it, but the client gets a generic 500 instead of a useful error. Add an explicit check.Proposed fix
🤖 Prompt for AI Agents