diff --git a/backend/app.js b/backend/app.js index 1547feb..33c45b7 100644 --- a/backend/app.js +++ b/backend/app.js @@ -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'; @@ -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}; diff --git a/backend/controllers/admin.controller.js b/backend/controllers/admin.controller.js new file mode 100644 index 0000000..a62dc2b --- /dev/null +++ b/backend/controllers/admin.controller.js @@ -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" }) +}) \ No newline at end of file diff --git a/backend/controllers/owner.controller.js b/backend/controllers/owner.controller.js new file mode 100644 index 0000000..72f48c9 --- /dev/null +++ b/backend/controllers/owner.controller.js @@ -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, + }, + }, + }); +}); + diff --git a/backend/middlewares/auth.middleware.js b/backend/middlewares/auth.middleware.js index 707484c..3a4f306 100644 --- a/backend/middlewares/auth.middleware.js +++ b/backend/middlewares/auth.middleware.js @@ -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{ @@ -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") } }) \ No newline at end of file diff --git a/backend/models/admin.model.js b/backend/models/admin.model.js new file mode 100644 index 0000000..6cba347 --- /dev/null +++ b/backend/models/admin.model.js @@ -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) \ No newline at end of file diff --git a/backend/models/owner.model.js b/backend/models/owner.model.js new file mode 100644 index 0000000..9072608 --- /dev/null +++ b/backend/models/owner.model.js @@ -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) + diff --git a/backend/models/pending_approval.model.js b/backend/models/pending_approval.model.js new file mode 100644 index 0000000..763afc3 --- /dev/null +++ b/backend/models/pending_approval.model.js @@ -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); \ No newline at end of file diff --git a/backend/models/request.model.js b/backend/models/request.model.js new file mode 100644 index 0000000..9cc1a4d --- /dev/null +++ b/backend/models/request.model.js @@ -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); diff --git a/backend/public/temp/WhatsApp Image 2023-12-13 at 22.48.58_4e24b60f.jpg b/backend/public/temp/WhatsApp Image 2023-12-13 at 22.48.58_4e24b60f.jpg deleted file mode 100644 index 82599de..0000000 Binary files a/backend/public/temp/WhatsApp Image 2023-12-13 at 22.48.58_4e24b60f.jpg and /dev/null differ diff --git a/backend/routes/admin.routes.js b/backend/routes/admin.routes.js new file mode 100644 index 0000000..c57ede2 --- /dev/null +++ b/backend/routes/admin.routes.js @@ -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; \ No newline at end of file diff --git a/backend/routes/owner.routes.js b/backend/routes/owner.routes.js new file mode 100644 index 0000000..a714581 --- /dev/null +++ b/backend/routes/owner.routes.js @@ -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; \ No newline at end of file