From cd1193c7461902716fcfd8eb717825f1ddbc73f3 Mon Sep 17 00:00:00 2001 From: "tomosa.sarkar" Date: Fri, 8 Aug 2025 13:06:08 +0530 Subject: [PATCH] feat : admin feature --- .env | 3 -- .gitignore | 2 + src/controllers/admin_auth.controller.js | 62 ++++++++++++++++++++++++ src/middlewares/admin.middleware.js | 28 +++++++++++ src/middlewares/auth.middleware.js | 1 - src/routes/admin_auth.route.js | 10 ++++ src/routes/index.js | 3 ++ src/services/admin.auth.service.js | 40 +++++++++++++++ src/util/jwt.js | 6 +-- 9 files changed, 148 insertions(+), 7 deletions(-) delete mode 100644 .env create mode 100644 src/controllers/admin_auth.controller.js create mode 100644 src/middlewares/admin.middleware.js create mode 100644 src/routes/admin_auth.route.js create mode 100644 src/services/admin.auth.service.js diff --git a/.env b/.env deleted file mode 100644 index 34e3e37..0000000 --- a/.env +++ /dev/null @@ -1,3 +0,0 @@ -PORT=8080 -DATABASE_URL=postgresql://kmobile_app_rw:kmobile@localhost:5431/kmobile_banking -JWT_SECRET=supersecret diff --git a/.gitignore b/.gitignore index c2658d7..28d7714 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ node_modules/ +.vscode/ +.env/ diff --git a/src/controllers/admin_auth.controller.js b/src/controllers/admin_auth.controller.js new file mode 100644 index 0000000..08cdbcc --- /dev/null +++ b/src/controllers/admin_auth.controller.js @@ -0,0 +1,62 @@ +const adminAuthService = require('../services/admin.auth.service'); +const { generateToken } = require('../util/jwt'); +const { logger } = require('../util/logger'); +const db = require('../config/db'); +// const authenticate = require('../middlewares/auth.middleware'); + +async function login(req, res) { + const { userName, password } = req.body; + + if (!userName || !password) { + return res + .status(400) + .json({ error: 'UserName and Password are required' }); + } + const currentTime = new Date().toISOString(); + try { + const admin = await adminAuthService.validateAdmin(userName, password); + if (!admin) return res.status(401).json({ error: 'invalid credentials' }); + + const token = generateToken(admin.username, 'admin', '1d'); + await db.query('UPDATE admin SET last_login = $1 WHERE username = $2', [ + currentTime, + userName, + ]); + res.json({ token }); + } catch (err) { + logger.error(err, 'login failed'); + res.status(500).json({ error: 'something went wrong' }); + } +} + +async function fetchAdminDetails(req, res) { + const customerNo = req.admin; + try { + const admin = await adminAuthService.findAdminByUserName(customerNo); + if (!admin) return res.status(404).json({ message: 'ADMIN_USER_NOT_FOUND' }); + return res.json(admin); + + } catch (err) { + logger.error(err, 'error occurred while fetching admin details'); + res.status(500).json({ error: 'something went wrong' }); + } +} + +async function getUserDetails(req, res) { + const { CIF } = req.query; + if (!CIF) { + res.status(400).json({ + error: 'CIF number is required', + }); + } + try { + const userDetails = await adminAuthService.getCustomerDetails(CIF); + if (!userDetails) + return res.status(401).json({ error: 'invalid CIF number' }); + return res.json(userDetails); + } catch (error) { + logger.error('while fetching customer details', error); + res.status(500).json({ error: 'invalid CIF number'}); + } +} +module.exports = { login, fetchAdminDetails, getUserDetails }; diff --git a/src/middlewares/admin.middleware.js b/src/middlewares/admin.middleware.js new file mode 100644 index 0000000..0c823e4 --- /dev/null +++ b/src/middlewares/admin.middleware.js @@ -0,0 +1,28 @@ +const { verifyToken } = require('../util/jwt'); +const { logger } = require('../util/logger'); + +function checkAdmin (req,res,next){ + const authHeader = req.headers.authorization; + + if (!authHeader || !authHeader.startsWith('Bearer ')) { + return res + .status(401) + .json({ error: 'missing or malformed authorization header' }); + } + + const token = authHeader.split(' ')[1]; + try { + const payload = verifyToken(token); + // console.log("hi",payload); + if(payload.customerNo && payload.role === 'admin'){ + req.admin = payload.customerNo; + next(); + } + else + return res.status(403).json({error :'Only admin can access'}) + } catch (err) { + logger.error(err, 'error verifying token'); + return res.status(401).json({ error: 'invalid or expired token' }); + } +} +module.exports = checkAdmin; diff --git a/src/middlewares/auth.middleware.js b/src/middlewares/auth.middleware.js index ee2b4c2..dec4e69 100644 --- a/src/middlewares/auth.middleware.js +++ b/src/middlewares/auth.middleware.js @@ -21,5 +21,4 @@ function auth(req, res, next) { return res.status(401).json({ error: 'invalid or expired token' }); } } - module.exports = auth; diff --git a/src/routes/admin_auth.route.js b/src/routes/admin_auth.route.js new file mode 100644 index 0000000..0125f7e --- /dev/null +++ b/src/routes/admin_auth.route.js @@ -0,0 +1,10 @@ +const adminAuthController = require('../controllers/admin_auth.controller'); +const adminAuthenticate = require('../middlewares/admin.middleware'); +const express = require('express'); + +const router = express.Router(); + +router.post('/login', adminAuthController.login); +router.get('/admin_details', adminAuthenticate, adminAuthController.fetchAdminDetails); +router.get('/fetch/customer_details',adminAuthenticate,adminAuthController.getUserDetails); +module.exports = router; diff --git a/src/routes/index.js b/src/routes/index.js index 7a31d85..c503594 100644 --- a/src/routes/index.js +++ b/src/routes/index.js @@ -1,5 +1,6 @@ const express = require('express'); const authRoute = require('./auth.route'); +const adminAuthRoute =require('./admin_auth.route'); const detailsRoute = require('./customer_details.route'); const transactionRoute = require('./transactions.route'); const authenticate = require('../middlewares/auth.middleware'); @@ -9,9 +10,11 @@ const beneficiaryRoute = require('./beneficiary.route'); const router = express.Router(); router.use('/auth', authRoute); +router.use('/auth/admin',adminAuthRoute); router.use('/customer', authenticate, detailsRoute); router.use('/transactions/account/:accountNo', authenticate, transactionRoute); router.use('/payment/transfer', authenticate, transferRoute); router.use('/beneficiary', beneficiaryRoute); + module.exports = router; diff --git a/src/services/admin.auth.service.js b/src/services/admin.auth.service.js new file mode 100644 index 0000000..3f28b16 --- /dev/null +++ b/src/services/admin.auth.service.js @@ -0,0 +1,40 @@ +const db = require('../config/db'); +const { comparePassword, hashPassword } = require('../util/hash'); +const axios = require('axios'); + +async function findAdminByUserName(customerNo) { + const result = await db.query('SELECT * FROM admin WHERE username = $1', [ + customerNo, + ]); + return result.rows[0]; +} + +async function validateAdmin(customerNo, password) { + const user = await findAdminByUserName(customerNo); + if (!user) return null; + const isMatch = await comparePassword(password, user.password); + return isMatch ? user : null; +} + +async function getCustomerDetails(customerNo) { + try { + const response = await axios.get( + 'http://localhost:8686/kccb/cbs/custInfo/details', + { params: { stcustno: customerNo } } + ); + const details = response.data; + const processedDetails = details.map((acc) => ({ + ...acc, + activeAccounts: details.length, + cifNumber: customerNo, + })); + return processedDetails; + } catch (error) { + logger.error('while fetching customer details', error); + throw new Error( + 'API call failed: ' + (error.response?.data?.message || error.message) + ); + } +} + +module.exports = { validateAdmin, findAdminByUserName,getCustomerDetails }; diff --git a/src/util/jwt.js b/src/util/jwt.js index cea8ad0..e26a641 100644 --- a/src/util/jwt.js +++ b/src/util/jwt.js @@ -2,9 +2,9 @@ const jwt = require('jsonwebtoken'); const { jwtSecret } = require('../config/config'); const { logger } = require('./logger'); -function generateToken(customerNo, expiresIn = '10d') { - logger.info({ customerNo }, 'payload to encode'); - return jwt.sign({ customerNo }, jwtSecret, { expiresIn }); +function generateToken(customerNo, role = 'user', expiresIn = '10d') { + logger.info({ customerNo, role }, 'payload to encode'); + return jwt.sign({ customerNo, role }, jwtSecret, { expiresIn }); } function verifyToken(token) {