Skip to content
Merged
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
5 changes: 5 additions & 0 deletions Learnings.md
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
4 changes: 2 additions & 2 deletions server/config/db.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import mongoose, { connect } from "mongoose";
import mongoose from "mongoose";


export const connectDb = async () => {
try {
await mongoose.connect(MONGODB_URI);
await mongoose.connect(process.env.MONGODB_URI);
console.log("connect to database")
} catch (error) {
console.log(error);
Expand Down
1 change: 1 addition & 0 deletions server/constants/departments.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

113 changes: 113 additions & 0 deletions server/controllers/attendanceController.js
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"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify Employee model export/import forms
rg -n --type=js 'export default Employee|export const Employee|import\s+\{\s*Employee\s*\}\s+from|import\s+Employee\s+from'

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. Employee will be undefined and Employee.findOne(...) will crash.

Suggested fix
-import { Employee } from "../models/Employee.js"
+import Employee from "../models/Employee.js"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import { Employee } from "../models/Employee.js"
import Employee from "../models/Employee.js"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@server/controllers/attendanceController.js` at line 2, The file imports the
model with a named import { Employee } but the model is exported as default,
causing Employee to be undefined and calls like Employee.findOne(...) to crash;
change the import to use the default export (i.e., import Employee from
"../models/Employee.js") or update the model to export a named Employee to match
the existing import so that the symbol Employee is defined for use in functions
that call Employee.findOne, Employee.create, etc.



//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
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Query the Attendance model, not the controller function.

Line 27 calls getAttendance.findOne(...); getAttendance is a handler function, so this throws at runtime. Use Attendance.findOne(...).

Suggested fix
-const existing = await getAttendance.findOne({
+const existing = await Attendance.findOne({
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const existing = await getAttendance.findOne({
employeeId: employee._id,
date: today
})
const existing = await Attendance.findOne({
employeeId: employee._id,
date: today
})
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@server/controllers/attendanceController.js` around lines 27 - 30, The code is
calling the handler function getAttendance instead of the Mongoose model;
replace the erroneous call so the Attendance model is queried (use
Attendance.findOne) when looking up an existing record for employee._id and
today (where existing is assigned). Update the line that uses
getAttendance.findOne(...) to call Attendance.findOne(...) so the correct model
is queried at runtime.


const now = new Date();

if (!existing) {
const isLate = now.getHours() >= 9 && now.getMinutes() > 0;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Correct late-status time condition.

Current check marks 10:00 as not late (minutes === 0). Use hour > 9 || (hour === 9 && minutes > 0).

Suggested fix
-const isLate = now.getHours() >= 9 && now.getMinutes() > 0;
+const isLate = now.getHours() > 9 || (now.getHours() === 9 && now.getMinutes() > 0);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const isLate = now.getHours() >= 9 && now.getMinutes() > 0;
const isLate = now.getHours() > 9 || (now.getHours() === 9 && now.getMinutes() > 0);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@server/controllers/attendanceController.js` at line 35, The isLate
calculation currently uses "now.getHours() >= 9 && now.getMinutes() > 0" which
incorrectly treats 9:00 as not late and 10:00 as late only when minutes > 0;
update the condition in the attendanceController.js where isLate is computed to
use "hour > 9 || (hour === 9 && minutes > 0)" logic by reading now.getHours()
and now.getMinutes() into variables (or inline) and setting isLate accordingly
so 9:01+ is late but 9:00 is not.


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
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Persist computed workingHours and dayType on checkout.

You compute both values but never assign them to existing before save(), so checkout metadata is silently dropped.

Suggested fix
 const workingHours = parseFloat(diffHours.toFixed(2));
@@
 else
     dayType = "Short Day";

+existing.workingHours = workingHours;
+existing.dayType = dayType;

 await existing.save();
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
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();
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";
existing.workingHours = workingHours;
existing.dayType = dayType;
await existing.save();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@server/controllers/attendanceController.js` around lines 56 - 69, The
computed values workingHours and dayType are never assigned to the attendance
record before saving, so set them on the existing object (e.g.,
existing.workingHours = workingHours and existing.dayType = dayType) immediately
before calling existing.save() in the checkout flow to persist the metadata
(refer to variables workingHours, dayType and method existing.save).

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
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Bound and sanitize attendance history limit.

parseInt(req.query.limit || 30) allows huge values and can degrade response/database performance. Clamp to a safe max (e.g., 100) and guard invalid numbers.

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

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const limit = parseInt(req.query.limit || 30);
const history = await Attendance.find({ employeeId: employee._id }).sort({ date: -1 }).limit(limit)
const parsed = Number.parseInt(req.query.limit ?? "30", 10);
const limit = Number.isNaN(parsed) ? 30 : Math.min(Math.max(parsed, 1), 100);
const history = await Attendance.find({ employeeId: employee._id }).sort({ date: -1 }).limit(limit)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@server/controllers/attendanceController.js` around lines 101 - 102, The limit
parsing in attendanceController.js (variable limit used in
Attendance.find(...).limit(limit)) is unsafe; parseInt(req.query.limit || 30)
can produce NaN, negative, or huge values. Fix by parsing with a radix
(parseInt(req.query.limit, 10)), defaulting to 30 when invalid (NaN or <=0),
then clamp to a safeMax (e.g., 100) using Math.min to ensure limit is an integer
within range before passing it to Attendance.find(...).limit(limit).



return res.json({
data: history,
employee: { isDeleted: employee.isDeleted }
})

} catch (error) {
return res.status(500).json({ error: "Failed to Fetch Attendance" });
}
}
103 changes: 103 additions & 0 deletions server/controllers/authController.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import User from "../models/User";
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🌐 Web query:

In Node.js ES modules, do relative imports like import User from "../models/User";work without a file extension, or must they include../models/User.js?

💡 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 .js extension to the relative ESM import.

In Node.js ES modules, relative imports require explicit file extensions. ../models/User will not resolve; use ../models/User.js instead.

Suggested fix
-import User from "../models/User";
+import User from "../models/User.js";
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import User from "../models/User";
import User from "../models/User.js";
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@server/controllers/authController.js` at line 1, The ES module import at the
top uses a relative path without an extension ("import User from
\"../models/User\""); update this import to include the .js extension so Node
can resolve it (change the import statement referencing User to use
"../models/User.js").

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" })
}
}
Loading