feat : customer can set User Name
feat : customer can update username. wip : update the SMS template for user name.
This commit is contained in:
1
.vscode/settings.json
vendored
1
.vscode/settings.json
vendored
@@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
"cSpell.words": [
|
"cSpell.words": [
|
||||||
"MPIN",
|
"MPIN",
|
||||||
|
"occured",
|
||||||
"otpgenerator",
|
"otpgenerator",
|
||||||
"tpassword",
|
"tpassword",
|
||||||
"tpin",
|
"tpin",
|
||||||
|
|||||||
@@ -12,11 +12,9 @@ async function login(req, res) {
|
|||||||
const { customerNo, password, otp } = req.body;
|
const { customerNo, password, otp } = req.body;
|
||||||
const loginType = req.headers['x-login-type'] || 'standard';
|
const loginType = req.headers['x-login-type'] || 'standard';
|
||||||
|
|
||||||
|
|
||||||
if (!customerNo || !password) {
|
if (!customerNo || !password) {
|
||||||
return res.status(400).json({ error: 'customerNo and password are required' });
|
return res.status(400).json({ error: 'customerNo and password are required' });
|
||||||
}
|
}
|
||||||
|
|
||||||
const currentTime = new Date().toISOString();
|
const currentTime = new Date().toISOString();
|
||||||
const MAX_ATTEMPTS = 3; // Max invalid attempts before lock
|
const MAX_ATTEMPTS = 3; // Max invalid attempts before lock
|
||||||
const BLOCK_DURATION = 15 * 60 * 60; // 1 day
|
const BLOCK_DURATION = 15 * 60 * 60; // 1 day
|
||||||
@@ -25,7 +23,6 @@ async function login(req, res) {
|
|||||||
const blockedKey = `login:blocked:${customerNo}`;
|
const blockedKey = `login:blocked:${customerNo}`;
|
||||||
const attemptsKey = `login:attempts:${customerNo}`;
|
const attemptsKey = `login:attempts:${customerNo}`;
|
||||||
|
|
||||||
|
|
||||||
const userCheck = await authService.findUserByCustomerNo(customerNo);
|
const userCheck = await authService.findUserByCustomerNo(customerNo);
|
||||||
|
|
||||||
if (loginType.toUpperCase() === 'IB') {
|
if (loginType.toUpperCase() === 'IB') {
|
||||||
@@ -70,7 +67,6 @@ async function login(req, res) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// --- Step 4: If login successful, reset Redis attempts ---
|
// --- Step 4: If login successful, reset Redis attempts ---
|
||||||
await setJson(attemptsKey, 0); // reset counter
|
await setJson(attemptsKey, 0); // reset counter
|
||||||
|
|
||||||
@@ -103,12 +99,10 @@ async function login(req, res) {
|
|||||||
ibAccess: user.ib_access_level,
|
ibAccess: user.ib_access_level,
|
||||||
mbAccess: user.mb_access_level,
|
mbAccess: user.mb_access_level,
|
||||||
};
|
};
|
||||||
|
|
||||||
await db.query('UPDATE users SET last_login = $1 WHERE customer_no = $2', [
|
await db.query('UPDATE users SET last_login = $1 WHERE customer_no = $2', [
|
||||||
currentTime,
|
currentTime,
|
||||||
customerNo,
|
customerNo,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
logger.info(`Login successful | Type: ${loginType}`);
|
logger.info(`Login successful | Type: ${loginType}`);
|
||||||
return res.json({ token, FirstTimeLogin, loginPswExpiry, rights });
|
return res.json({ token, FirstTimeLogin, loginPswExpiry, rights });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -117,7 +111,6 @@ async function login(req, res) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async function fetchUserDetails(req, res) {
|
async function fetchUserDetails(req, res) {
|
||||||
const customerNo = req.user;
|
const customerNo = req.user;
|
||||||
try {
|
try {
|
||||||
@@ -146,7 +139,6 @@ async function tpin(req, res) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async function setTpin(req, res) {
|
async function setTpin(req, res) {
|
||||||
const customerNo = req.user;
|
const customerNo = req.user;
|
||||||
try {
|
try {
|
||||||
@@ -259,6 +251,59 @@ async function changeTransPassword(req, res) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function isUserNameExits(req, res) {
|
||||||
|
try {
|
||||||
|
const customerNo = req.user;
|
||||||
|
const user = await authService.findUserByCustomerNo(customerNo);
|
||||||
|
if (!user) {
|
||||||
|
return res.status(404).json({ error: 'USER_NOT_FOUND' });
|
||||||
|
}
|
||||||
|
const userName = await authService.CheckUserName(customerNo);
|
||||||
|
return res.json({ user_name: userName });
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
return res.status(500).json({ error: 'INTERNAL_SERVER_ERROR' });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function setUserName(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 userNameIsExits = await authService.CheckUserName(customerNo);
|
||||||
|
const { user_name } = req.body;
|
||||||
|
if (!userNameIsExits) {
|
||||||
|
await authService.setUserName(customerNo, user_name);
|
||||||
|
logger.info('User name has been set for first time.');
|
||||||
|
return res.json({ message: 'All set! Your username has been saved.' });
|
||||||
|
}
|
||||||
|
if (userNameIsExits) {
|
||||||
|
const historyRes = await db.query('SELECT preferred_name FROM preferred_name_history WHERE customer_no = $1 ORDER BY changed_at DESC LIMIT 5',
|
||||||
|
[customerNo]
|
||||||
|
);
|
||||||
|
// maximum 5 times can changed username
|
||||||
|
const history = historyRes.rows.map((r) => r.preferred_name.toLowerCase());
|
||||||
|
if (history.length >= 5) {
|
||||||
|
return res.status(429).json({ error: "Preferred name change limit reached -5 times" });
|
||||||
|
}
|
||||||
|
// Cannot match last 2
|
||||||
|
const lastTwo = history.slice(0, 2);
|
||||||
|
if (lastTwo.includes(user_name.toLowerCase())) {
|
||||||
|
return res.status(409).json({ error: "Preferred name cannot match last 2 preferred names"});
|
||||||
|
}
|
||||||
|
await authService.setUserName(customerNo, user_name);
|
||||||
|
logger.info('User name has been updated.');
|
||||||
|
return res.json({ message: 'All set! Your username has been updated.' });
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
logger.error(error);
|
||||||
|
return res.status(500).json({ error: 'CANNOT UPDATE USER NAME' });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
login,
|
login,
|
||||||
tpin,
|
tpin,
|
||||||
@@ -268,4 +313,6 @@ module.exports = {
|
|||||||
fetchUserDetails,
|
fetchUserDetails,
|
||||||
changeLoginPassword,
|
changeLoginPassword,
|
||||||
changeTransPassword,
|
changeTransPassword,
|
||||||
|
isUserNameExits,
|
||||||
|
setUserName,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ async function SendOtp(req, res) {
|
|||||||
ref,
|
ref,
|
||||||
date,
|
date,
|
||||||
userOtp,
|
userOtp,
|
||||||
|
PreferName,
|
||||||
} = req.body;
|
} = req.body;
|
||||||
|
|
||||||
if (!mobileNumber || !type) {
|
if (!mobileNumber || !type) {
|
||||||
@@ -99,6 +100,13 @@ async function SendOtp(req, res) {
|
|||||||
otp = generateOTP(6);
|
otp = generateOTP(6);
|
||||||
message = templates.EMandate(otp);
|
message = templates.EMandate(otp);
|
||||||
break;
|
break;
|
||||||
|
case 'USERNAME_UPDATED':
|
||||||
|
otp = generateOTP(6);
|
||||||
|
message = templates.USERNAME_UPDATED(otp);
|
||||||
|
break;
|
||||||
|
case 'USERNAME_SAVED':
|
||||||
|
message = templates.USERNAME_SAVED(PreferName);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
return res.status(400).json({ error: 'Invalid OTP type' });
|
return res.status(400).json({ error: 'Invalid OTP type' });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ router.post('/login_password', authenticate, authController.setLoginPassword);
|
|||||||
router.post('/transaction_password',authenticate,authController.setTransactionPassword);
|
router.post('/transaction_password',authenticate,authController.setTransactionPassword);
|
||||||
router.post('/change/login_password',authenticate,authController.changeLoginPassword);
|
router.post('/change/login_password',authenticate,authController.changeLoginPassword);
|
||||||
router.post('/change/transaction_password',authenticate,authController.changeTransPassword);
|
router.post('/change/transaction_password',authenticate,authController.changeTransPassword);
|
||||||
|
router.get('/user_name',authenticate,authController.isUserNameExits);
|
||||||
|
router.post('/user_name',authenticate,authController.setUserName);
|
||||||
|
|
||||||
|
|
||||||
module.exports = router;
|
module.exports = router;
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
const db = require('../config/db');
|
const db = require('../config/db');
|
||||||
const { comparePassword, hashPassword } = require('../util/hash');
|
const { comparePassword, hashPassword } = require('../util/hash');
|
||||||
const dayjs = require('dayjs');
|
const dayjs = require('dayjs');
|
||||||
|
const { logger } = require('../util/logger');
|
||||||
|
|
||||||
async function findUserByCustomerNo(customerNo) {
|
async function findUserByCustomerNo(customerNo) {
|
||||||
const result = await db.query('SELECT * FROM users WHERE customer_no = $1', [
|
const result = await db.query('SELECT * FROM users WHERE customer_no = $1', [
|
||||||
@@ -124,6 +125,44 @@ async function changeTransPassword(customerNo, trans_psw) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function CheckUserName(customerNo) {
|
||||||
|
try {
|
||||||
|
const result = await db.query('SELECT preferred_name from users WHERE customer_no = $1',
|
||||||
|
[customerNo]
|
||||||
|
);
|
||||||
|
if (result.rows.length > 0) {
|
||||||
|
return result.rows[0].preferred_name;;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
throw new Error(
|
||||||
|
`error occurred while fetch the preferred name ${error.message}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function setUserName(customerNo, username) {
|
||||||
|
const currentTime = dayjs().toISOString();
|
||||||
|
try {
|
||||||
|
await db.query(
|
||||||
|
'UPDATE users SET preferred_name = $1 ,updated_at = $2 WHERE customer_no = $3',
|
||||||
|
[username, currentTime, customerNo]
|
||||||
|
);
|
||||||
|
logger.info("user table updated");
|
||||||
|
await db.query(
|
||||||
|
"INSERT INTO preferred_name_history (customer_no, preferred_name) VALUES ($1, $2)",
|
||||||
|
[customerNo, username]
|
||||||
|
);
|
||||||
|
logger.info("preferred_name_history table updated");
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(
|
||||||
|
`error occured while setting new preferred name ${error.message}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
validateUser,
|
validateUser,
|
||||||
findUserByCustomerNo,
|
findUserByCustomerNo,
|
||||||
@@ -136,4 +175,7 @@ module.exports = {
|
|||||||
changeLoginPassword,
|
changeLoginPassword,
|
||||||
changeTransPassword,
|
changeTransPassword,
|
||||||
isMigratedUser,
|
isMigratedUser,
|
||||||
|
CheckUserName,
|
||||||
|
setUserName,
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
const templates = {
|
const templates = {
|
||||||
LOGIN_OTP :(otp,username) =>`Dear Customer, Your username ${username} have been verified. Please enter the OTP: ${otp} to complete your login. -KCCB `,
|
LOGIN_OTP: (otp, username) => `Dear Customer, Your username ${username} have been verified. Please enter the OTP: ${otp} to complete your login. -KCCB `,
|
||||||
|
|
||||||
IMPS: (otp) => `Dear Customer, Please complete the fund transfer with OTP ${otp} -KCCB`,
|
IMPS: (otp) => `Dear Customer, Please complete the fund transfer with OTP ${otp} -KCCB`,
|
||||||
|
|
||||||
@@ -46,7 +46,13 @@ const templates = {
|
|||||||
`Dear Customer, Your CIF rights have been updated. Please log in again to access the features. -KCCB`,
|
`Dear Customer, Your CIF rights have been updated. Please log in again to access the features. -KCCB`,
|
||||||
|
|
||||||
EMandate: (otp) =>
|
EMandate: (otp) =>
|
||||||
`Dear Customer, Your OTP for e-Mandate is ${otp}.It is valid for 1 minute.Do not share this OTP with anyone. -KCCB`
|
`Dear Customer, Your OTP for e-Mandate is ${otp}.It is valid for 1 minute.Do not share this OTP with anyone. -KCCB`,
|
||||||
|
|
||||||
|
USERNAME_UPDATED: (otp) =>
|
||||||
|
`Dear Customer, Your OTP for updating your Preferred Name is ${otp}. It is valid for 1 minute. Do not share this OTP with anyone. -KCCB`,
|
||||||
|
|
||||||
|
USERNAME_SAVED: (PreferName) =>
|
||||||
|
`Dear Customer, Your Preferred Name -${PreferName} has been updated successfully. If this change was not made by you, please contact our support team immediately.`
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = templates;
|
module.exports = templates;
|
||||||
Reference in New Issue
Block a user