Skip to content
Open
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
4 changes: 4 additions & 0 deletions backend/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import express from 'express';
import dotenv from 'dotenv';
import cors from 'cors';
import citizenRouter from './routes/citizen.routes.js';
import adminRoutes from './routes/admin.routes.js'
import ownerRoutes from './routes/owner.routes.js'
import cookieParser from 'cookie-parser';
// import tenderRoutes from './routes/tenderRoutes.js';

Expand All @@ -24,5 +26,7 @@ app.use(express.static("public")) //to store the components in the server
// app.listen(PORT, () => console.log(`Backend running on port ${PORT}`));

app.use('/api/citizens',citizenRouter)
app.use('/api/admins',adminRoutes)
app.use('/api/owners',ownerRoutes)

export {app};
155 changes: 155 additions & 0 deletions backend/controllers/admin.controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
import { asyncHandler } from "../utils/asyncHandler.js";
import { ApiError } from "../utils/ApiError.js";
import { Admin } from "../models/admin.model.js";
import { PendingApproval } from "../models/pending_approval.model.js";

const generateAccessAndRefreshToken = async (adminId) => {
try {
const admin = await Admin.findById(adminId);
let AccessToken = await admin.generateAccessToken()
let RefreshToken = await admin.generateRefreshToken()
admin.refreshToken = RefreshToken;
await admin.save({ validateBeforeSave: false });
return { AccessToken, RefreshToken }
} catch (e) {
throw new ApiError(500, "Something went wrong generating tokens")
}
}

const sendVerificationRequestToOwner = async (adminId) => {
// Fetch the admin details
const admin = await Admin.findById(adminId).select("userid");
if (!admin) {
throw new ApiError(404, "Admin not found");
}

// Fetch the owner's email (assuming owner is stored in the database)
// const owner = await User.findOne({ role: "owner" }).select("email");
// if (!owner) {
// throw new ApiError(404, "Owner not found");
// }

// Send an email to the owner
const emailSubject = "New Admin Registration Request";
const emailBody = `
A new admin registration request has been submitted.
Admin ID: ${admin.userid}
Please verify and approve the request.
`;

// await sendEmail(owner.email, emailSubject, emailBody);

// Alternatively, you can add the request to a "pending approvals" collection
await PendingApproval.create({
adminId: admin._id,
status: "pending",
});
};

export const registerAdmin = asyncHandler(async (req, res) => {
console.log(req.body)
const { userid, password } = req.body;
console.log(userid, password)

if ([userid, password].some((fields) => fields?.trim() === "")) {
throw new ApiError(400, "All Fields are required")
}

const ExistedAdmin = await Admin.findOne({ userid });

if (ExistedAdmin) {
throw new ApiError(400, "User with email or username already exist")
}

const admin = await Admin.create({
userid,
password,
})

const createdAdmin = await Admin.findById(admin._id).select("-password -refreshToken")

await sendVerificationRequestToOwner(createdAdmin._id);

if (!createdAdmin) {
throw new ApiError(500, "Something went wrong while registering the user")
}

const { AccessToken, RefreshToken } = await generateAccessAndRefreshToken(createdAdmin._id);
res.status(201).json({
status: "success",
data: {
admin: createdAdmin,
AccessToken,
RefreshToken
}
})
})

export const loginAdmin = asyncHandler(async (req, res) => {
if (!req.body || Object.keys(req.body).length === 0) {
throw new ApiError(400, "request body is empty");
}
const { userid, password} = req.body
if (!userid || !password) {
throw new ApiError(400, "Email and password are required")
}
// if(!isVerified){
// throw new ApiError(400, "Account not verified by the owner....Try again later")
// }
const admin = await Admin.findOne({ userid }).select("+password +refreshToken")
console.log(admin)
if (!admin) {
throw new ApiError(400, "Invalid email or password")
}
if(admin.isVerified===false){
throw new ApiError(400, "Account not verified by the owner....Try again later")
}
const checkPassword = await admin.isPasswordCorrect(password)
if (!checkPassword) {
throw new ApiError(400, "Invalid email or password")
}
const { AccessToken, RefreshToken } = await generateAccessAndRefreshToken(admin._id)
res.status(200)
.cookie("accessToken", AccessToken, {
httpOnly: true,
secure: true,
})
.cookie("refreshToken", RefreshToken, {
httpOnly: true,
secure: true,
})
.json({
status: "success",
data: {
admin: {
_id: admin._id,
userid: admin.userid,
isVerified: admin.isVerified,
},
AccessToken,
RefreshToken
}
})
})

export const logoutAdmin = asyncHandler(async (req, res) => {
const adminid = await req.admin._id
await Admin.findByIdAndUpdate((adminid), {
$set: {
refreshToken: undefined
},
},
{
new: true,
}
)
const option = {
httpOnly: true,
secure: true
}
return res
.status(200)
.clearCookie("accessToken", option)
.clearCookie("refreshToken", option)
.json({ message: "User logged out succesfully" })
})
50 changes: 50 additions & 0 deletions backend/controllers/owner.controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { asyncHandler } from "../utils/asyncHandler.js";
import { ApiError } from "../utils/ApiError.js";
import { Owner } from "../models/owner.model.js";
import { Admin } from "../models/admin.model.js";
import { PendingApproval } from "../models/pending_approval.model.js";

export const verifyAdmin = asyncHandler(async (req, res) => {
const { adminId } = req.body;

// Validate input
if (!adminId) {
throw new ApiError(400, "Admin ID is required");
}

// Find the admin
const admin = await Admin.findById(adminId);
if (!admin) {
throw new ApiError(404, "Admin not found");
}

// Update isVerified to true
admin.isVerified = true;
await admin.save();

await PendingApproval.findOneAndDelete({
adminId: admin._id,
});

// Notify the admin that their account has been verified
const emailSubject = "Admin Account Verified";
const emailBody = `
Your admin account has been verified by the owner.
You can now log in and access the admin dashboard.
`;
// await sendEmail(admin.userid, emailSubject, emailBody);

// Respond to the owner
res.status(200).json({
status: "success",
message: "Admin account verified successfully.",
data: {
admin: {
_id: admin._id,
userid: admin.userid,
isVerified: admin.isVerified,
},
},
});
});

8 changes: 6 additions & 2 deletions backend/middlewares/auth.middleware.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { ApiError } from "../utils/ApiError.js";
import { asyncHandler } from "../utils/asyncHandler.js";
import jwt from "jsonwebtoken"
import { Citizen } from "../models/citizen.model.js";
import { Admin } from "../models/admin.model.js";

export const verifyJWT= asyncHandler(async(req, _,next)=>{
try{
Expand All @@ -13,12 +14,15 @@ export const verifyJWT= asyncHandler(async(req, _,next)=>{
const decoded= jwt.verify(token, process.env.ACCESS_TOKEN_SECRET)
console.log(decoded)
const citizen= await Citizen.findById(decoded._id).select("-password -refreshToken")
if (!citizen) {
const admin= await Admin.findById(decoded._id).select("-password -refreshToken")
if (!(citizen || admin)) {
throw new ApiError(401, "Unauthorized")
}

req.citizen=citizen;
req.admin=admin;
next()
} catch(e){
throw new ApiError(401, "Invalid access token")
throw new ApiError(401, e,"Invalid access token")
}
})
65 changes: 65 additions & 0 deletions backend/models/admin.model.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import mongoose, { Schema } from "mongoose"
import jwt from "jsonwebtoken"
import bcrypt from "bcrypt"

const adminSchema = new Schema({
isVerified: {
type: Boolean,
default: false,
},
userid : {
type:Number,
required:true,
unique:true,
index:true,
trim:true,
},
password: {
type: String,
required: [true, "Password is required"],
},
refreshToken: {
type: String,
// select: false,
}
},
{
timestamps: true,
})

adminSchema.pre("save", async function (next) {
if (!this.isModified("password")) return next();
this.password = await bcrypt.hash(this.password, 10);
next()
}) //to encrypt the password

adminSchema.methods.isPasswordCorrect = async function (password) {
const result = await bcrypt.compare(password, this.password)
return result
}

adminSchema.methods.generateAccessToken = async function () {
return jwt.sign({
_id: this._id,
email: this.email,
username: this.username,
fullName: this.fullName
},
process.env.ACCESS_TOKEN_SECRET,
{
expiresIn: process.env.ACCESS_TOKEN_EXPIRY
}
)
}
adminSchema.methods.generateRefreshToken = async function () {
return jwt.sign({
_id: this._id,
},
process.env.REFRESH_TOKEN_SECRET,
{
expiresIn: process.env.REFRESH_TOKEN_EXPIRY
}
)
}

export const Admin=mongoose.model("Admin", adminSchema)
20 changes: 20 additions & 0 deletions backend/models/owner.model.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import mongoose from "mongoose";
import bcrypt from "bcrypt";

const ownerSchema = new mongoose.Schema({
password: {
type: String,
required: [true, "Password is true"],
},
requests: [{
type: mongoose.Schema.Types.ObjectId,
ref: "Request"
}],
},
{
timestamps: true,
}
)

export const Owner=mongoose.model("Owner", ownerSchema)

10 changes: 10 additions & 0 deletions backend/models/pending_approval.model.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import mongoose from "mongoose";

const pendingApprovalSchema = new mongoose.Schema({
status: {
type: String,
required: true,
},
})

export const PendingApproval = mongoose.model("PendingApproval", pendingApprovalSchema);
11 changes: 11 additions & 0 deletions backend/models/request.model.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import mongoose from "mongoose";

const requestSchema = new mongoose.Schema({
sender: {
type: mongoose.Schema.Types.ObjectId,
ref: "Admin",
},

})

export const Request = mongoose.model("Request", requestSchema);
Binary file not shown.
13 changes: 13 additions & 0 deletions backend/routes/admin.routes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { Router } from "express";
import { loginAdmin, logoutAdmin, registerAdmin } from "../controllers/admin.controller.js";
// import { upload } from "../middlewares/multer.middleware.js";
import { verifyJWT } from "../middlewares/auth.middleware.js";
const router = Router();

router.route("/register").post(registerAdmin)

router.route("/login").post(loginAdmin)
//secured route
router.route("/logout").post(verifyJWT,logoutAdmin)

export default router;
12 changes: 12 additions & 0 deletions backend/routes/owner.routes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Router } from "express";
import { verifyAdmin } from "../controllers/owner.controller.js";

// import { upload } from "../middlewares/multer.middleware.js";
// import { verifyJWT } from "../middlewares/auth.middleware.js";
const router = Router();

router.route("/verify").post(verifyAdmin)



export default router;