-
Notifications
You must be signed in to change notification settings - Fork 55
Refactor/login register #222
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?
Changes from all commits
cbefec9
78dbc24
f2e5883
ae0295f
57c4189
61dfd89
29bc583
a8b4d8e
82d3b70
bde7d5e
ecc1ebd
8126097
4e96a8e
d3c0261
2a31781
53d7216
cdf07e2
c342d2b
649fb09
3fe6ed8
f521062
2ef2e05
0f47b6a
0bd1220
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 |
|---|---|---|
| @@ -1,4 +1,4 @@ | ||
| #!/usr/bin/env sh | ||
| . "$(dirname -- "$0")/_/husky.sh" | ||
|
|
||
| cd frontend && npx lint-staged && cd ../backend && npx lint-staged | ||
| npx lint-staged |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,116 @@ | ||
| const passport = require("passport"); | ||
| const GoogleStrategy = require("passport-google-oauth20").Strategy; | ||
| const LocalStrategy = require("passport-local").Strategy; | ||
| const isIITBhilaiEmail = require("../utils/isIITBhilaiEmail"); | ||
| const User = require("../models/userSchema"); | ||
| const { loginValidate } = require("../utils/authValidate"); | ||
| const bcrypt = require("bcrypt"); | ||
| // Google OAuth Strategy | ||
| passport.use( | ||
| new GoogleStrategy( | ||
| { | ||
| clientID: process.env.GOOGLE_CLIENT_ID, | ||
| clientSecret: process.env.GOOGLE_CLIENT_SECRET, | ||
| callbackURL: `${process.env.BACKEND_URL}/auth/google/verify`, // Update with your callback URL | ||
| }, | ||
| async (accessToken, refreshToken, profile, done) => { | ||
| // Check if the user already exists in your database | ||
| const email = profile.emails?.[0]?.value; | ||
| if (!email) { | ||
| //console.log("No email found in Google profile"); | ||
| return done(null, false, { message: "Email not available from Google." }); | ||
| } | ||
|
|
||
| if (!isIITBhilaiEmail(profile.emails[0].value)) { | ||
| console.log("Google OAuth blocked for: ", profile.emails[0].value); | ||
| return done(null, false, { | ||
| message: "Only @iitbhilai.ac.in emails are allowed.", | ||
| }); | ||
| } | ||
| try { | ||
| const user = await User.findOne({ username: email }); | ||
| //console.log("Looking for existing user with email:", email, "Found:", !!user); | ||
|
|
||
| if (user) { | ||
| // If user exists, return the user | ||
| //console.log("Returning existing user:", user.username); | ||
| return done(null, user); | ||
| } | ||
| // If user doesn't exist, create a new user in your database | ||
| const newUser = await User.create({ | ||
| username: email, | ||
| role: "STUDENT", | ||
| strategy: "google", | ||
| personal_info: { | ||
| name: profile.displayName || "No Name", | ||
| email: email, | ||
| profilePic: | ||
| profile.photos && profile.photos.length > 0 | ||
| ? profile.photos[0].value | ||
| : "https://www.gravatar.com/avatar/?d=mp", | ||
| }, | ||
| onboardingComplete: false, | ||
| }); | ||
| //console.log("User is",newUser); | ||
| return done(null, newUser); | ||
| } catch (error) { | ||
| console.error("Error in Google strategy:", error); | ||
| return done(error); | ||
| } | ||
| }, | ||
| ), | ||
| ); | ||
|
|
||
| //Local Strategy | ||
| passport.use(new LocalStrategy(async (username, password, done) => { | ||
|
|
||
| const result = loginValidate.safeParse({ username, password }); | ||
|
|
||
| if (!result.success) { | ||
| let errors = result.error.issues.map((issue) => issue.message); | ||
| return done(null, false, {message: errors}); | ||
| } | ||
|
|
||
| try{ | ||
|
|
||
| const user = await User.findOne({ username }); | ||
| if (!user) { | ||
| return done(null, false, {message: "Invalid user credentials"}); | ||
| } | ||
|
|
||
|
|
||
| if (user.strategy !== "local" || !user.password) { | ||
| return done(null, false, { message: "Invalid login method" }); | ||
| } | ||
|
|
||
| const isValid = await bcrypt.compare(password, user.password); | ||
| if (!isValid) { | ||
| return done(null, false, { message: "Invalid user credentials" }); | ||
| } | ||
| return done(null, user); | ||
| }catch(err){ | ||
| return done(err); | ||
| } | ||
|
|
||
| })); | ||
|
|
||
|
|
||
| //When login succeeds this will run | ||
| // serialize basically converts user obj into a format that can be transmitted(like a string, etc...) | ||
| // here take user obj and done callback and store only userId in session | ||
| passport.serializeUser((user, done) => { | ||
| done(null, user._id.toString()); | ||
| }); | ||
|
|
||
| //When a request comes in, take the stored id, fetch full user from DB, and attach it to req.user. | ||
| passport.deserializeUser(async (id, done) => { | ||
| try { | ||
| let user = await User.findById(id); | ||
| if(!user) return done(null, false); | ||
| done(null, user); | ||
| } catch (err) { | ||
| done(err, null); | ||
| } | ||
| }); | ||
|
|
||
| module.exports = passport; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,158 @@ | ||
|
|
||
| const User = require("../models/userSchema"); | ||
| const Position = require("../models/positionSchema"); | ||
| const PositionHolder = require("../models/positionHolderSchema"); | ||
| const OrganizationalUnit = require("../models/organizationSchema"); | ||
| const { CertificateBatch } = require("../models/certificateSchema"); | ||
| const { validateBatchSchema, zodObjectId } = require("../utils/batchValidate"); | ||
|
|
||
| async function createBatch(req, res) { | ||
| //console.log(req.user); | ||
| try{ | ||
| const id = req.user.id; | ||
| const user = await User.findById(id); | ||
| if (!user) { | ||
| return res.status(404).json({ messge: "Invalid data (User not found)" }); | ||
| } | ||
|
|
||
| if (user.role !== "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}) -> unit_id = "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 }); | ||
| } | ||
|
|
||
| // Get coordinator's position and unit | ||
| const positionHolder = await PositionHolder.findOne({ user_id: id }); | ||
| if (!positionHolder) { | ||
| return res.status(403).json({ message: "You are not part of any position in a unit" }); | ||
| } | ||
|
|
||
| const position = await Position.findById(positionHolder.position_id); | ||
| console.log(position._id); | ||
| if (!position) { | ||
| return res.status(403).json({ message: "Your position is invalid" }); | ||
| } | ||
|
|
||
| const userUnitId = position.unit_id.toString(); | ||
| if (userUnitId !== unit_id) { | ||
| 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: unit 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") { | ||
| return res.status(403).json({ message: "Invalid Data: council not found" }); | ||
| } | ||
|
|
||
| //const councilId = councilObj._id.toString(); | ||
| const presidentOrgUnitId = councilObj.parent_unit_id; | ||
| const category = councilObj.category.toUpperCase(); | ||
|
|
||
| // Resolve General Secretary and President for the council (server-side, tamper-proof) | ||
| const gensecObj = await User.findOne({ role: `GENSEC_${category}` }); | ||
| if(!gensecObj){ | ||
| return res.status(500).json({ message: "General Secretary not found" }); | ||
| } | ||
| //console.log(gensecObj._id); | ||
|
|
||
| const presidentPosition = await Position.findOne({ | ||
| unit_id: presidentOrgUnitId, | ||
| title: /president/i, | ||
| }); | ||
| if (!presidentPosition) { | ||
| return res | ||
| .status(500) | ||
| .json({ message: "President position not found for council" }); | ||
| } | ||
| //console.log(presidentPosition._id); | ||
|
|
||
| const presidentHolder = await PositionHolder.findOne({ | ||
| position_id: presidentPosition._id, | ||
| }); | ||
| const presidentId = presidentHolder.user_id.toString(); | ||
| //console.log(presidentId); | ||
| const presidentObj = await User.findById(presidentId); | ||
|
|
||
| console.log(presidentObj._id); | ||
| if (!presidentObj) { | ||
| return res.status(500).json({ message: "President not found" }); | ||
| } | ||
|
|
||
| const approverIds = [gensecObj._id.toString(), presidentId]; | ||
|
|
||
| const userChecks = await Promise.all( | ||
| users.map(async (uid) => { | ||
| const validation = zodObjectId.safeParse(uid); | ||
| if (!validation) { | ||
| 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 }); | ||
| }catch(err){ | ||
| res.status(500).json({message: err.message || "Internal server error"}); | ||
| } | ||
| } | ||
|
|
||
| module.exports = { | ||
| createBatch, | ||
| }; | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,17 +1,20 @@ | ||||||||||||||||||||
| const {Event} = require('../models/schema'); | ||||||||||||||||||||
| const Event = require('../models/eventSchema'); | ||||||||||||||||||||
|
|
||||||||||||||||||||
| // fetch 4 most recently updated events | ||||||||||||||||||||
| exports.getLatestEvents = async (req, res) => { | ||||||||||||||||||||
| try{ | ||||||||||||||||||||
| const latestEvents = await Event.find({}) | ||||||||||||||||||||
| .sort({updated_at: -1}) | ||||||||||||||||||||
| .limit(4) | ||||||||||||||||||||
| .select('title updated_at schedule.venue status'); | ||||||||||||||||||||
| .select('title updatedAt schedule.venue status'); | ||||||||||||||||||||
|
Comment on lines
6
to
+9
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: # Search for Event schema and model files
fd -t f -e js | grep -iE "(event|schema)" | head -20Repository: OpenLake/Student_Database_COSA Length of output: 541 🏁 Script executed: # Search for the Event model definition and timestamp configuration
rg -n "timestamps|updated_at|updatedAt" --type js -A2 -B2Repository: OpenLake/Student_Database_COSA Length of output: 4841 🏁 Script executed: # Get more context from eventControllers.js around the lines mentioned
fd -t f -name "*eventController*" -o -name "*eventcontroller*"Repository: OpenLake/Student_Database_COSA Length of output: 304 🏁 Script executed: cat -n backend/models/eventSchema.js | tail -20Repository: OpenLake/Student_Database_COSA Length of output: 629 🏁 Script executed: cat -n backend/controllers/eventControllers.js | head -30Repository: OpenLake/Student_Database_COSA Length of output: 1243 Sort field Lines 9 and 17 both reference 🐛 Proposed fix- .sort({updated_at: -1})
+ .sort({updatedAt: -1})📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||
|
|
||||||||||||||||||||
| if(!latestEvents){ | ||||||||||||||||||||
| return res.status(404).json({message: "No events are created"}); | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
Comment on lines
+11
to
+13
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.
Mongoose's 🐛 Proposed fix- if(!latestEvents){
- return res.status(404).json({message: "No events are created"});
- }
+ if(!latestEvents || latestEvents.length === 0){
+ return res.status(404).json({message: "No events are created"});
+ }📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||
| const formatedEvents =latestEvents.map(event=>({ | ||||||||||||||||||||
| id: event._id, | ||||||||||||||||||||
| title: event.title, | ||||||||||||||||||||
| date: event.updated_at.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }), | ||||||||||||||||||||
| date: event.updatedAt?.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }), | ||||||||||||||||||||
| venue: (event.schedule && event.schedule.venue) ? event.schedule.venue : 'TBA', | ||||||||||||||||||||
| status: event.status || 'TBD' | ||||||||||||||||||||
| })) | ||||||||||||||||||||
|
|
||||||||||||||||||||
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.
Typo in response key:
"messge"→"message".🐛 Proposed fix
🤖 Prompt for AI Agents