-
Notifications
You must be signed in to change notification settings - Fork 0
api created for employees,authentication and attendance. #10
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
Changes from all commits
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,5 @@ | ||
| -->claudinary ,express-upload ,multer | ||
|
|
||
| -->Make sure to include type:module | ||
|
|
||
| -->correct file name extensions i.e .js .ts etc |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,113 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import Attendance from "../models/Attendance.js"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { Employee } from "../models/Employee.js" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| //clock in/out for employee | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| //POST api/attendance | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export const clockInOut = async (req, res) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const session = req.session; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const employee = await Employee.findOne({ userId: session.userId }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!employee) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return res.status(404).json({ message: "Employee not found" }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (employee.isDeleted) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return res.status(403).json({ message: "Your account id deactivated.You cannot clock in/out" }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const today = new Date(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| today.setHours(0, 0, 0, 0); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const existing = await getAttendance.findOne({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| employeeId: employee._id, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| date: today | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+27
to
+30
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. Query the Attendance model, not the controller function. Line 27 calls Suggested fix-const existing = await getAttendance.findOne({
+const existing = await Attendance.findOne({📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const now = new Date(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!existing) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const isLate = now.getHours() >= 9 && now.getMinutes() > 0; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
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. Correct late-status time condition. Current check marks 10:00 as not late ( Suggested fix-const isLate = now.getHours() >= 9 && now.getMinutes() > 0;
+const isLate = now.getHours() > 9 || (now.getHours() === 9 && now.getMinutes() > 0);📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const attendance = await Attendance.create({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| employeeId: employee._id, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| date: today, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| checkIn: now, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| status: isLate ? "LATE" : "PRESENT", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return res.json({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| success: true, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| type: "CHECK_IN", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| data: attendance | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else if (!existing.checkOut) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const checkInTime = new Date(existing.checkIn).getTime() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const diffMs = now.getTime() - checkInTime; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const diffHours = diffMs / (1000 * 60 * 60); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| existing.checkOut = now; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const workingHours = parseFloat(diffHours.toFixed(2)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let dayType = "Half Day"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (workingHours >= 8) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| dayType = "Full Day"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| else if (workingHours >= 6) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| dayType = "Three Quarter Day"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| else if (workingHours >= 4) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| dayType = "Half Day"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| else | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| dayType = "Short Day"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await existing.save(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+56
to
+69
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. Persist computed You compute both values but never assign them to Suggested fix const workingHours = parseFloat(diffHours.toFixed(2));
@@
else
dayType = "Short Day";
+existing.workingHours = workingHours;
+existing.dayType = dayType;
await existing.save();📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return res.json({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| success: true, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| type: "CHECK_OUT", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| data: existing | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return res.json({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| success: true, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| type: "CHECK_OUT", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| data: existing | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch (error) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| console.log(error); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| console.error("Attendance Error:", error); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return res.status(500).json({ error: "Operation failed" }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| //Get attendance for employee | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| //GET /api/attendance | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export const getAttendance = async (req, res) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const session = req.session; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const employee = await Employee.findOne({ userId: session.userId }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!employee) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return res.status(404).json({ message: "Employee not found" }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const limit = parseInt(req.query.limit || 30); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const history = await Attendance.find({ employeeId: employee._id }).sort({ date: -1 }).limit(limit) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+101
to
+102
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. Bound and sanitize attendance history limit.
Suggested fix-const limit = parseInt(req.query.limit || 30);
+const parsed = Number.parseInt(req.query.limit ?? "30", 10);
+const limit = Number.isNaN(parsed) ? 30 : Math.min(Math.max(parsed, 1), 100);📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return res.json({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| data: history, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| employee: { isDeleted: employee.isDeleted } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch (error) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return res.status(500).json({ error: "Failed to Fetch Attendance" }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,103 @@ | ||||||
| import User from "../models/User"; | ||||||
|
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🌐 Web query:
💡 Result: In Node.js ES modules, relative imports like import User from "../models/User"; do not work without a file extension. They must include the extension, such as import User from "../models/User.js";. This is a mandatory requirement for relative specifiers in ESM, as stated in the official Node.js documentation across multiple versions (v18, v19, v22, v25). The docs explicitly say: "The file extension is always necessary for these [relative specifiers]." and "A file extension must be provided when using the import keyword to resolve relative or absolute specifiers." By default, Node.js uses the explicit specifier resolution mode, which requires full paths with extensions. Using --experimental-specifier-resolution=node enables automatic extension resolution like CommonJS, but this is experimental and not recommended for standard use. Extensionless relative imports are a CommonJS feature not supported in ESM without customization. This behavior ensures compatibility with browsers and unambiguous resolution, avoiding guesswork for files like index.js. For TypeScript projects, extensions (.js) are also required under moduleResolution: "nodenext" or "node16", even if source files are .ts. Citations:
Add the In Node.js ES modules, relative imports require explicit file extensions. Suggested fix-import User from "../models/User";
+import User from "../models/User.js";📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||
| import bcrypt from "bcryptjs"; | ||||||
| import jwt from "jsonwebtoken"; | ||||||
|
|
||||||
|
|
||||||
| //Login for employee and admin | ||||||
| //POST /api/auth/login | ||||||
|
|
||||||
| export const login = async (req, res) => { | ||||||
| try { | ||||||
| const { email, password, role_type } = req.body; | ||||||
|
|
||||||
| if (!email || !password) { | ||||||
| return res.status(400).json({ message: "Please provide email and password" }) | ||||||
| } | ||||||
|
|
||||||
| const user = await User.findOne({ email }); | ||||||
|
|
||||||
| if (!user) { | ||||||
| return res.status(401).json({ message: "Invalid email or password" }) | ||||||
| } | ||||||
|
|
||||||
| if (role_type === "admin" && user.role !== "ADMIN") { | ||||||
| return res.status(401).json({ message: "Not authorized as admin" }) | ||||||
| } | ||||||
|
|
||||||
| if (role_type === "employee" && user.role !== "EMPLOYEE") { | ||||||
| return res.status(401).json({ message: "Not authorized as employee" }) | ||||||
| } | ||||||
|
|
||||||
| const isValid = await bcrypt.compare(password, user.password); | ||||||
|
|
||||||
| if (!isValid) { | ||||||
| return res.status(401).json({ message: "Invalid email or password" }) | ||||||
| } | ||||||
|
|
||||||
|
|
||||||
| const payload = { | ||||||
| userId: user._id.toString(), | ||||||
| role: user.role, | ||||||
| email: user.email, | ||||||
| } | ||||||
|
|
||||||
| const token = jwt.sign(payload, process.env.JWT_SECRET, { expiresIn: "7d" }); | ||||||
|
|
||||||
| return res.json({ user: payload, token }) | ||||||
| } catch (err) { | ||||||
| console.log("Login Error:", err); | ||||||
| return res.status(500).json({ message: "Failed to login" }); | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
|
|
||||||
| //Get session for employee and admin | ||||||
| //GET /api/auth/session | ||||||
|
|
||||||
|
|
||||||
| export const session = (req, res) => { | ||||||
| const session = req.session; | ||||||
|
|
||||||
| if (!req.session) return res.status(401).json({ message: "No session" }); | ||||||
|
|
||||||
| return res.json({ user: session }); | ||||||
| } | ||||||
|
|
||||||
|
|
||||||
| //change password for employee and admin | ||||||
| //POST /api/auth/change-password | ||||||
|
|
||||||
| export const changePassword = async (req, res) => { | ||||||
| try { | ||||||
| const userSession = req.session; | ||||||
| const { currentPassword, newPassword } = req.body; | ||||||
|
|
||||||
| if (!userSession || !userSession.userId) | ||||||
| return res.status(401).json({ message: "Unauthorized" }); | ||||||
|
|
||||||
|
|
||||||
| if (!currentPassword || !newPassword) | ||||||
| return res.status(400).json({ message: "Both passwords field required" }) | ||||||
|
|
||||||
| const user = await User.findById(userSession.userId); | ||||||
|
|
||||||
| if (!user) | ||||||
| return res.status(404).json({ error: "User not found" }); | ||||||
|
|
||||||
|
|
||||||
| const isValid = await bcrypt.compare(currentPassword, user.password); | ||||||
|
|
||||||
| if (!isValid) | ||||||
| return res.status(400).json({ error: "Current password is incorrect" }); | ||||||
|
|
||||||
| const hashed = await bcrypt.hash(newPassword, 10); | ||||||
|
|
||||||
| await User.findByIdAndUpdate(userSession.userId, { password: hashed }); | ||||||
|
|
||||||
|
|
||||||
| return res.json({ success: true }) | ||||||
|
|
||||||
| } catch (error) { | ||||||
| return res.status(500).json({ error: "Failed to change password" }) | ||||||
| } | ||||||
| } | ||||||
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.
🧩 Analysis chain
🏁 Script executed:
Repository: harshGupta090722/EMS
Length of output: 670
Fix Employee import to match model export type.
Line 2 uses named import
{ Employee }, but the model is exported as default.Employeewill beundefinedandEmployee.findOne(...)will crash.Suggested fix
📝 Committable suggestion
🤖 Prompt for AI Agents