feat : Implement last las login feature

feat : add transaction password column and write logic for transaction password.
This commit is contained in:
2025-07-22 12:49:44 +05:30
parent 32f6430a5c
commit d4d741b8cc
10 changed files with 141 additions and 9 deletions

2
.env
View File

@@ -1,3 +1,3 @@
PORT=8080
DATABASE_URL=postgresql://kmobile_app_rw:kmobile@localhost:5432/kmobile_banking
DATABASE_URL=postgresql://kmobile_app_rw:kmobile@localhost:5431/kmobile_banking
JWT_SECRET=supersecret

6
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,6 @@
{
"cSpell.words": [
"tpassword",
"tpin"
]
}

View File

@@ -3,7 +3,7 @@
"version": "1.0.0",
"main": "index.js",
"scripts": {
"dev": "nodemon src/server.js",
"dev": "npx nodemon src/server.js",
"start": "node src/server.js",
"lint": "eslint .",
"format": "prettier --write ."

View File

@@ -1,6 +1,7 @@
const authService = require('../services/auth.service');
const { generateToken } = require('../util/jwt');
const { logger } = require('../util/logger');
const db = require('../config/db');
async function login(req, res) {
const { customerNo, password } = req.body;
@@ -10,18 +11,36 @@ async function login(req, res) {
.status(400)
.json({ error: 'customerNo and password are required' });
}
const currentTime = new Date().toISOString();
try {
const user = await authService.validateUser(customerNo, password);
if (!user) return res.status(401).json({ error: 'invalid credentials' });
const token = generateToken(user.customer_no, '1d');
res.json({ token });
const FirstTimeLogin = await authService.CheckFirstTimeLogin(customerNo);
await db.query('UPDATE users SET last_login = $1 WHERE customer_no = $2', [
currentTime,
customerNo,
]);
res.json({ token, FirstTimeLogin });
} catch (err) {
logger.error(err, 'login failed');
res.status(500).json({ error: 'something went wrong' });
}
}
async function fetchUserDetails(req, res) {
const customerNo = req.user;
try {
const user = await authService.findUserByCustomerNo(customerNo);
if (!user) return res.status(404).json({ message: 'USER_NOT_FOUND' });
return res.json(user);
} catch (err) {
logger.error(err, 'error occured while fetching user details');
res.status(500).json({ error: 'something went wrong' });
}
}
async function tpin(req, res) {
const customerNo = req.user;
try {
@@ -56,4 +75,31 @@ async function setTpin(req, res) {
}
}
module.exports = { login, tpin, setTpin };
async function setLoginPassword(req, res) {
const customerNo = req.user;
try {
const user = await authService.findUserByCustomerNo(customerNo);
if (!user) return res.status(404).json({ error: 'USER_NOT_FOUND' });
const { login_password } = req.body;
authService.setLoginPassword(customerNo, login_password);
return res.json({ message: 'Login Password set' });
} catch (error) {
logger.error(error);
return res.status(500).json({ error: 'SOMETHING_WENT_WRONG' });
}
}
async function setTransactionPassword(req, res) {
const customerNo = req.user;
try {
const user = await authService.findUserByCustomerNo(customerNo);
if (!user) return res.status(404).json({ error: 'USER_NOT_FOUND' });
const { transaction_password } = req.body;
authService.setTransactionPassword(customerNo, transaction_password);
return res.json({ message: 'Transaction Password set' });
} catch (error) {
logger.error(error);
return res.status(500).json({ error: 'SOMETHING_WENT_WRONG' });
}
}
module.exports = { login, tpin, setTpin, setLoginPassword, setTransactionPassword,fetchUserDetails };

View File

@@ -5,7 +5,8 @@ async function transfer(
toAccountNo,
toAccountType,
amount,
narration = 'tranfer from mobile'
// narration = 'transfer from mobile'
narration
) {
try {
const response = await axios.post(

View File

@@ -5,7 +5,10 @@ const express = require('express');
const router = express.Router();
router.post('/login', authController.login);
router.get('/user_details', authenticate, authController.fetchUserDetails);
router.get('/tpin', authenticate, authController.tpin);
router.post('/tpin', authenticate, authController.setTpin);
router.post('/login_password', authenticate, authController.setLoginPassword);
router.post('/transaction_password', authenticate, authController.setTransactionPassword);
module.exports = router;

View File

@@ -2,10 +2,24 @@ const transferController = require('../controllers/transfer.controller');
const { logger } = require('../util/logger');
const express = require('express');
const tpinValidator = require('../validators/tpin.validator');
const tpasswordValidator = require('../validators/tpassword.validator');
const transferValidator = require('../validators/transfer.validator');
const router = express.Router();
router.use(tpinValidator, transferValidator);
// Added for tpassword
const passwordValidator=async(req,res,next)=>{
const{tpin,tpassword} =req.body;
if(tpin){
return tpinValidator(req,res,next);
}
else if(tpassword){
return tpasswordValidator(req,res,next);
}
else{
return res.status(400).json({error:"tpin or tpassword is required"})
}
}
router.use(passwordValidator, transferValidator);
const transferRoute = async (req, res) => {
const { fromAccount, toAccount, toAccountType, amount } = req.body;

View File

@@ -15,6 +15,14 @@ async function validateUser(customerNo, password) {
return isMatch ? user : null;
}
// Check First time user Login
async function CheckFirstTimeLogin(customerNo) {
const user = await findUserByCustomerNo(customerNo);
const is_first_time_login = user.is_first_login;
if (!user) return null;
return is_first_time_login;
}
async function validateTpin(customerNo, tpin) {
const user = await findUserByCustomerNo(customerNo);
if (!user?.tpin) return null;
@@ -36,4 +44,42 @@ async function setTpin(customerNo, tpin) {
}
}
module.exports = { validateUser, findUserByCustomerNo, setTpin, validateTpin };
// Set login password
async function setLoginPassword(customerNo, login_psw) {
const hashedLoginPassword = await hashPassword(login_psw);
try {
await db.query('UPDATE users SET password_hash = $1 ,is_first_login = false WHERE customer_no = $2', [
hashedLoginPassword,
customerNo,
]);
} catch (error) {
throw new Error(
`error occured while while setting new Login Password ${error.message}`
);
}
}
// Validate Transaction Password
async function validateTransactionPassword(customerNo, tpassword) {
const user = await findUserByCustomerNo(customerNo);
if (!user?.transaction_password) return null;
const isMatch = await comparePassword(tpassword, user.transaction_password );
return isMatch;
}
// Set transaction password
async function setTransactionPassword(customerNo, trans_psw) {
const hashedTransPassword = await hashPassword(trans_psw);
try {
await db.query('UPDATE users SET transaction_password = $1 WHERE customer_no = $2', [
hashedTransPassword,
customerNo,
]);
} catch (error) {
throw new Error(
`error occured while while setting new Transaction Password ${error.message}`
);
}
}
module.exports = { validateUser, findUserByCustomerNo, setTpin, validateTpin,
CheckFirstTimeLogin, setLoginPassword, validateTransactionPassword,setTransactionPassword };

View File

@@ -0,0 +1,16 @@
const authService = require('../services/auth.service');
const tpasswordValidator = async (req, res, next) => {
const customerNo = req.user;
const { tpassword } = req.body;
if (!tpassword) {
return res.status(400).json({ error: 'BAD_REQUEST' });
}
const valid = await authService.validateTransactionPassword(customerNo, tpassword);
if (valid === null) res.status(400).json({ error: 'TransactionPassword_NOT_SET_FOR_USER' });
if (!valid) return res.status(401).json({ error: 'INCORRECT_TransactionPassword' });
next();
};
module.exports = tpasswordValidator;

View File

@@ -1,7 +1,7 @@
const transferValidator = async (req, res, next) => {
const { fromAccount, toAccount, toAccountType, amount } = req.body;
const accountTypes = ['SB', 'LN'];
const accountTypes = ['SB', 'LN','Savings','Current'];
if (!fromAccount || fromAccount.length != 11) {
return res.status(400).json({ error: 'INVALID_ACCOUNT_NUMBER_FORMAT' });
}