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": [
|
||||
"MPIN",
|
||||
"occured",
|
||||
"otpgenerator",
|
||||
"tpassword",
|
||||
"tpin",
|
||||
|
||||
@@ -12,11 +12,9 @@ async function login(req, res) {
|
||||
const { customerNo, password, otp } = req.body;
|
||||
const loginType = req.headers['x-login-type'] || 'standard';
|
||||
|
||||
|
||||
if (!customerNo || !password) {
|
||||
return res.status(400).json({ error: 'customerNo and password are required' });
|
||||
}
|
||||
|
||||
const currentTime = new Date().toISOString();
|
||||
const MAX_ATTEMPTS = 3; // Max invalid attempts before lock
|
||||
const BLOCK_DURATION = 15 * 60 * 60; // 1 day
|
||||
@@ -25,7 +23,6 @@ async function login(req, res) {
|
||||
const blockedKey = `login:blocked:${customerNo}`;
|
||||
const attemptsKey = `login:attempts:${customerNo}`;
|
||||
|
||||
|
||||
const userCheck = await authService.findUserByCustomerNo(customerNo);
|
||||
|
||||
if (loginType.toUpperCase() === 'IB') {
|
||||
@@ -70,7 +67,6 @@ async function login(req, res) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// --- Step 4: If login successful, reset Redis attempts ---
|
||||
await setJson(attemptsKey, 0); // reset counter
|
||||
|
||||
@@ -103,12 +99,10 @@ async function login(req, res) {
|
||||
ibAccess: user.ib_access_level,
|
||||
mbAccess: user.mb_access_level,
|
||||
};
|
||||
|
||||
await db.query('UPDATE users SET last_login = $1 WHERE customer_no = $2', [
|
||||
currentTime,
|
||||
customerNo,
|
||||
]);
|
||||
|
||||
logger.info(`Login successful | Type: ${loginType}`);
|
||||
return res.json({ token, FirstTimeLogin, loginPswExpiry, rights });
|
||||
} catch (err) {
|
||||
@@ -117,7 +111,6 @@ async function login(req, res) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async function fetchUserDetails(req, res) {
|
||||
const customerNo = req.user;
|
||||
try {
|
||||
@@ -146,7 +139,6 @@ async function tpin(req, res) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async function setTpin(req, res) {
|
||||
const customerNo = req.user;
|
||||
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 = {
|
||||
login,
|
||||
tpin,
|
||||
@@ -268,4 +313,6 @@ module.exports = {
|
||||
fetchUserDetails,
|
||||
changeLoginPassword,
|
||||
changeTransPassword,
|
||||
isUserNameExits,
|
||||
setUserName,
|
||||
};
|
||||
|
||||
@@ -21,6 +21,7 @@ async function SendOtp(req, res) {
|
||||
ref,
|
||||
date,
|
||||
userOtp,
|
||||
PreferName,
|
||||
} = req.body;
|
||||
|
||||
if (!mobileNumber || !type) {
|
||||
@@ -99,6 +100,13 @@ async function SendOtp(req, res) {
|
||||
otp = generateOTP(6);
|
||||
message = templates.EMandate(otp);
|
||||
break;
|
||||
case 'USERNAME_UPDATED':
|
||||
otp = generateOTP(6);
|
||||
message = templates.USERNAME_UPDATED(otp);
|
||||
break;
|
||||
case 'USERNAME_SAVED':
|
||||
message = templates.USERNAME_SAVED(PreferName);
|
||||
break;
|
||||
default:
|
||||
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('/change/login_password',authenticate,authController.changeLoginPassword);
|
||||
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;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
const db = require('../config/db');
|
||||
const { comparePassword, hashPassword } = require('../util/hash');
|
||||
const dayjs = require('dayjs');
|
||||
const { logger } = require('../util/logger');
|
||||
|
||||
async function findUserByCustomerNo(customerNo) {
|
||||
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 = {
|
||||
validateUser,
|
||||
findUserByCustomerNo,
|
||||
@@ -136,4 +175,7 @@ module.exports = {
|
||||
changeLoginPassword,
|
||||
changeTransPassword,
|
||||
isMigratedUser,
|
||||
CheckUserName,
|
||||
setUserName,
|
||||
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
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`,
|
||||
|
||||
@@ -46,7 +46,13 @@ const templates = {
|
||||
`Dear Customer, Your CIF rights have been updated. Please log in again to access the features. -KCCB`,
|
||||
|
||||
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;
|
||||
Reference in New Issue
Block a user