Implemented ICICI API endpoints
This commit is contained in:
16
ecosystem.config.json
Normal file
16
ecosystem.config.json
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"apps": [
|
||||
{
|
||||
"name": "Tanami-Backend",
|
||||
"script": "src/index.js",
|
||||
"instances": 1,
|
||||
"autorestart": true,
|
||||
"watch": false,
|
||||
"time": true,
|
||||
"env": {
|
||||
"NODE_ENV": "production"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
46
package.json
Normal file
46
package.json
Normal file
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"name": "Tanami-Backend",
|
||||
"version": "v1.0.0",
|
||||
"main": "src/index.js",
|
||||
"author": "Swapnil Bendal",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "pm2 start ecosystem.config.json --no-daemon",
|
||||
"dev": "cross-env NODE_ENV=development nodemon src/index.js",
|
||||
"test": "cross-env NODE_ENV=test nodemon src/index.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@onesignal/node-onesignal": "^5.0.0-alpha-01",
|
||||
"axios": "^1.7.5",
|
||||
"bcrypt": "^5.1.1",
|
||||
"compression": "^1.7.4",
|
||||
"cors": "^2.8.5",
|
||||
"cross-env": "^7.0.3",
|
||||
"crypto": "^1.0.1",
|
||||
"dotenv": "^16.4.5",
|
||||
"express": "^4.19.2",
|
||||
"express-rate-limit": "^7.3.1",
|
||||
"helmet": "^7.1.0",
|
||||
"http-status": "^1.7.4",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"moment": "^2.30.1",
|
||||
"morgan": "^1.10.0",
|
||||
"multer": "^1.4.5-lts.1",
|
||||
"mysql2": "^3.10.2",
|
||||
"nodemailer": "^6.9.14",
|
||||
"request-ip": "^3.3.0",
|
||||
"sequelize": "^6.37.3",
|
||||
"swagger-jsdoc": "^6.2.8",
|
||||
"swagger-ui-express": "^5.0.1",
|
||||
"twilio": "^5.2.2",
|
||||
"winston": "^3.13.0",
|
||||
"xss-clean": "^0.1.4",
|
||||
"yup": "^1.4.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"nodemon": "^3.1.4"
|
||||
}
|
||||
}
|
||||
3
src/DTOs/index.js
Normal file
3
src/DTOs/index.js
Normal file
@@ -0,0 +1,3 @@
|
||||
module.exports = {
|
||||
getAllCorporateDTO: require('./corporate/corporate.dto'),
|
||||
}
|
||||
64
src/api/ICICI/icici.api.js
Normal file
64
src/api/ICICI/icici.api.js
Normal file
@@ -0,0 +1,64 @@
|
||||
const axios = require("axios");
|
||||
const config = require("../../config/config");
|
||||
|
||||
const instance = axios.create({
|
||||
baseURL: config.icici.baseURL,
|
||||
headers: {
|
||||
'accept': "*",
|
||||
'Content-Type': 'text/plain',
|
||||
'apikey': config.icici.apikey,
|
||||
'x-forwarded-for': config.icici.x_forwarded_for,
|
||||
'Content-Length': 684,
|
||||
}
|
||||
})
|
||||
|
||||
module.exports = {
|
||||
createOtp: async (encryptedData) => {
|
||||
try {
|
||||
const { data } = await instance.post('/api/Corporate/CIB_SV/v1/Create', encryptedData); // Assuming `url` is relative to baseURL
|
||||
return data;
|
||||
} catch (error) {
|
||||
console.error("Error during registration API call:", error.message); // Log error message
|
||||
throw error; // Throw the actual error object
|
||||
}
|
||||
},
|
||||
|
||||
balanceInquiry: async (encryptedData) => {
|
||||
try {
|
||||
const { data } = await instance.post('/api/Corporate/CIB_SV/v1/BalanceInquiry', encryptedData); // Assuming `url` is relative to baseURL
|
||||
return data;
|
||||
} catch (error) {
|
||||
console.error("Error during Balance Inquiry API call:", error.message); // Log error message
|
||||
throw error; // Throw the actual error object
|
||||
}
|
||||
},
|
||||
|
||||
transactionOTP: async (encryptedData) => {
|
||||
try {
|
||||
const { data } = await instance.post('/api/Corporate/CIB_SV/v1/TransactionOTP', encryptedData); // Assuming `url` is relative to baseURL
|
||||
console.log("transactionOTP", data)
|
||||
return data;
|
||||
} catch (error) {
|
||||
console.error("Error during transaction OTP API call:", error.message); // Log error message
|
||||
throw error; // Throw the actual error object
|
||||
}
|
||||
},
|
||||
accountStatement: async (encryptedData) => {
|
||||
try {
|
||||
const {data} = await instance.post('/api/Corporate/CIB_SV/v1/AccountStatement', encryptedData); // Assuming `url` is relative to baseURL
|
||||
return data;
|
||||
} catch (error) {
|
||||
console.error("Error during account statement API call:", error.message); // Log error message
|
||||
throw error; // Throw the actual error object
|
||||
}
|
||||
},
|
||||
|
||||
RegistrationStatus: async (encryptedData) => {
|
||||
try {
|
||||
const { data } = await instance.post('registration-status', encryptedData);
|
||||
return data;
|
||||
} catch (error) {
|
||||
throw new Error('Error during registration status API call');
|
||||
}
|
||||
},
|
||||
};
|
||||
3
src/api/index.js
Normal file
3
src/api/index.js
Normal file
@@ -0,0 +1,3 @@
|
||||
module.exports = {
|
||||
iciciApi: require('./ICICI/icici.api'),
|
||||
}
|
||||
73
src/app.js
Normal file
73
src/app.js
Normal file
@@ -0,0 +1,73 @@
|
||||
const express = require('express');
|
||||
const requestIp = require('request-ip');
|
||||
const helmet = require('helmet');
|
||||
const xss = require('xss-clean');
|
||||
const compression = require('compression');
|
||||
const cors = require('cors');
|
||||
const httpStatus = require('http-status');
|
||||
const config = require('./config/config');
|
||||
const morgan = require('./config/morgan');
|
||||
const { authLimiter } = require('./middlewares/rateLimiter');
|
||||
const routes = require('./routes/v1');
|
||||
const { errorConverter, errorHandler } = require('./middlewares/error');
|
||||
const ApiError = require('./utils/handler/ApiError.handler');
|
||||
const path = require('path')
|
||||
|
||||
const app = express();
|
||||
|
||||
if (config.env !== 'test') {
|
||||
app.use(morgan.successHandler);
|
||||
app.use(morgan.errorHandler);
|
||||
}
|
||||
|
||||
// Middleware to set client IP
|
||||
app.use(requestIp.mw());
|
||||
|
||||
// Middleware to set dynamic Content Security Policy
|
||||
app.use((req, res, next) =>
|
||||
helmet.contentSecurityPolicy({
|
||||
directives: {
|
||||
defaultSrc: ["'self'"],
|
||||
imgSrc: ["'self'", 'data:', 'https:', `http://${req.hostname}`, `https://${req.hostname}`],
|
||||
},
|
||||
})(req, res, next)
|
||||
);
|
||||
|
||||
// parse json request body
|
||||
app.use(express.json());
|
||||
|
||||
// parse urlencoded request body
|
||||
app.use(express.urlencoded({ extended: true }));
|
||||
|
||||
// sanitize request data
|
||||
app.use(xss());
|
||||
|
||||
// gzip compression
|
||||
app.use(compression());
|
||||
|
||||
// enable cors
|
||||
app.use(cors());
|
||||
app.options('*', cors());
|
||||
|
||||
app.use('/public', express.static(path.join(__dirname, '../public')));
|
||||
|
||||
// limit repeated failed requests to auth endpoints
|
||||
if (config.env === 'production') {
|
||||
app.use('/api/v1/auth', authLimiter);
|
||||
}
|
||||
|
||||
// v1 api routes
|
||||
app.use('/api/v1', routes);
|
||||
|
||||
// send back a 404 error for any unknown api request
|
||||
app.use((req, res, next) => {
|
||||
next(new ApiError(httpStatus.NOT_FOUND, 'Not found'));
|
||||
});
|
||||
|
||||
// convert error to ApiError, if needed
|
||||
app.use(errorConverter);
|
||||
|
||||
// handle error
|
||||
app.use(errorHandler);
|
||||
|
||||
module.exports = app;
|
||||
179
src/config/config.js
Normal file
179
src/config/config.js
Normal file
@@ -0,0 +1,179 @@
|
||||
const dotenv = require('dotenv');
|
||||
const path = require('path');
|
||||
const yup = require('yup');
|
||||
const crypto = require('crypto');
|
||||
|
||||
dotenv.config({ path: path.join(__dirname, '../../.env') });
|
||||
|
||||
const envVarsSchema = yup.object().shape({
|
||||
NODE_ENV: yup.string().oneOf(['production', 'development', 'test']).required(),
|
||||
PORT: yup.number().default(3000),
|
||||
|
||||
// By Passes
|
||||
BY_PASS_OTP: yup.boolean().default(true).required('by pass otp is required'),
|
||||
BY_PASS_NOTIFICATION: yup.boolean().default(true).required('by pass notification is required'),
|
||||
BY_PASS_EMAIL: yup.boolean().default(true).required('by pass email is required'),
|
||||
|
||||
// JWT
|
||||
JWT_SECRET: yup.string().required('JWT secret key is required'),
|
||||
JWT_ACCESS_EXPIRATION_MINUTES: yup.number().default(30).required('minutes after which access tokens expire'),
|
||||
JWT_REFRESH_EXPIRATION_DAYS: yup.number().default(30).required('days after which refresh tokens expire'),
|
||||
JWT_RESET_PASSWORD_EXPIRATION_MINUTES: yup.number()
|
||||
.default(10)
|
||||
.required('minutes after which reset password token expires'),
|
||||
JWT_VERIFY_EMAIL_EXPIRATION_MINUTES: yup.number()
|
||||
.default(10)
|
||||
.required('minutes after which verify email token expires'),
|
||||
|
||||
// SMTP
|
||||
SMTP_HOST: yup.string().nullable().required('server that will send the emails'),
|
||||
SMTP_PORT: yup.number().nullable().required('port to connect to the email server'),
|
||||
SMTP_USERNAME: yup.string().nullable().required('username for email server'),
|
||||
SMTP_PASSWORD: yup.string().nullable().required('password for email server'),
|
||||
EMAIL_FROM: yup.string().nullable().required('the from field in the emails sent by the app'),
|
||||
|
||||
// SMS
|
||||
TWILIO_ACCOUNT_SID: yup.string().nullable().required(),
|
||||
TWILIO_AUTH_TOKEN: yup.string().nullable().required(),
|
||||
TWILIO_SMS_FROM: yup.string().nullable().required(),
|
||||
OTP_EXPIRE_IN_MIN: yup.number().default(5).nullable(),
|
||||
|
||||
// OneSignal
|
||||
ONESIGNAL_APPID: yup.string().required('app id key is required'),
|
||||
ONESIGNAL_REST_APIKEY: yup.string().required('api key is required'),
|
||||
ONESIGNAL_AUTHKEY: yup.string().required('auth key is required'),
|
||||
|
||||
// Code
|
||||
CODE_SECRET: yup.string().required('Code secret key is required'),
|
||||
|
||||
// DataBase
|
||||
DB_USERNAME: yup.string().required('DB Username is required'),
|
||||
DB_PASSWORD: yup.string().required('DB Password is required'),
|
||||
DB_DATABASE_NAME: yup.string().required('Database name is required'),
|
||||
DB_HOSTNAME: yup.string().default('127.0.0.1').required('DB Hostname is required'),
|
||||
DB_PORT: yup.number().default(3306).required('DB Port is required'),
|
||||
|
||||
|
||||
}).noUnknown();
|
||||
|
||||
try {
|
||||
const envVars = envVarsSchema.validateSync(process.env, { abortEarly: false, stripUnknown: true });
|
||||
|
||||
module.exports = {
|
||||
env: envVars?.NODE_ENV,
|
||||
port: envVars?.PORT,
|
||||
byPassOTP: envVars?.BY_PASS_OTP,
|
||||
byPassNotification: envVars?.BY_PASS_NOTIFICATION,
|
||||
byPassEmail: envVars?.BY_PASS_EMAIL,
|
||||
mysql: {
|
||||
development: {
|
||||
username: envVars.DB_USERNAME,
|
||||
password: envVars.DB_PASSWORD,
|
||||
database: envVars.DB_DATABASE_NAME,
|
||||
host: envVars.DB_HOSTNAME,
|
||||
port: envVars.DB_PORT,
|
||||
dialect: "mysql",
|
||||
dialectOptions: {
|
||||
bigNumberStrings: true,
|
||||
},
|
||||
logging: false,
|
||||
define: {
|
||||
freezeTableName: true,
|
||||
},
|
||||
pool: {
|
||||
max: 10, // Maximum number of connection in pool
|
||||
min: 0, // Minimum number of connection in pool
|
||||
acquire: 30000, // Maximum time in ms to acquire a connection
|
||||
idle: 10000, // Maximum time in ms a connection can be idle before being released
|
||||
}
|
||||
},
|
||||
test: {
|
||||
username: envVars.DB_USERNAME,
|
||||
password: envVars.DB_PASSWORD,
|
||||
database: envVars.DB_DATABASE_NAME,
|
||||
host: envVars.DB_HOSTNAME,
|
||||
port: envVars.DB_PORT,
|
||||
dialect: "mysql",
|
||||
dialectOptions: {
|
||||
bigNumberStrings: true,
|
||||
},
|
||||
logging: false,
|
||||
define: {
|
||||
freezeTableName: true,
|
||||
},
|
||||
pool: {
|
||||
max: 10, // Maximum number of connection in pool
|
||||
min: 0, // Minimum number of connection in pool
|
||||
acquire: 30000, // Maximum time in ms to acquire a connection
|
||||
idle: 10000, // Maximum time in ms a connection can be idle before being released
|
||||
}
|
||||
},
|
||||
production: {
|
||||
username: envVars.DB_USERNAME,
|
||||
password: envVars.DB_PASSWORD,
|
||||
database: envVars.DB_DATABASE_NAME,
|
||||
host: envVars.DB_HOSTNAME,
|
||||
port: envVars.DB_PORT,
|
||||
dialect: "mysql",
|
||||
dialectOptions: {
|
||||
bigNumberStrings: true,
|
||||
},
|
||||
logging: false,
|
||||
define: {
|
||||
freezeTableName: true,
|
||||
socketPath: "/var/run/mysqld/mysqld.sock",
|
||||
},
|
||||
pool: {
|
||||
max: 10, // Maximum number of connection in pool
|
||||
min: 0, // Minimum number of connection in pool
|
||||
acquire: 30000, // Maximum time in ms to acquire a connection
|
||||
idle: 10000, // Maximum time in ms a connection can be idle before being released
|
||||
}
|
||||
},
|
||||
},
|
||||
jwt: {
|
||||
secret: envVars?.JWT_SECRET,
|
||||
accessExpirationMinutes: envVars?.JWT_ACCESS_EXPIRATION_MINUTES,
|
||||
refreshExpirationDays: envVars?.JWT_REFRESH_EXPIRATION_DAYS,
|
||||
resetPasswordExpirationMinutes: envVars?.JWT_RESET_PASSWORD_EXPIRATION_MINUTES,
|
||||
verifyEmailExpirationMinutes: envVars?.JWT_VERIFY_EMAIL_EXPIRATION_MINUTES,
|
||||
},
|
||||
email: {
|
||||
smtp: {
|
||||
host: envVars?.SMTP_HOST,
|
||||
port: envVars?.SMTP_PORT,
|
||||
secure: process.env.SMTP_PORT == 465, // true for 465, false for other ports
|
||||
auth: {
|
||||
user: envVars?.SMTP_USERNAME,
|
||||
pass: envVars?.SMTP_PASSWORD,
|
||||
},
|
||||
},
|
||||
from: envVars?.EMAIL_FROM,
|
||||
},
|
||||
code: {
|
||||
secret: crypto.createHash('sha256').update(envVars.CODE_SECRET).digest(),
|
||||
blacklist: new Set(),
|
||||
},
|
||||
SMS: {
|
||||
accountSid: envVars.TWILIO_ACCOUNT_SID,
|
||||
authToken: envVars.TWILIO_AUTH_TOKEN,
|
||||
from: envVars.TWILIO_SMS_FROM,
|
||||
},
|
||||
expiryTime: {
|
||||
otp_in_Min: envVars.OTP_EXPIRE_IN_MIN || 2,
|
||||
},
|
||||
oneSignal: {
|
||||
appID: envVars.ONESIGNAL_APPID,
|
||||
userAuthKey: envVars.ONESIGNAL_AUTHKEY,
|
||||
restApiKey: envVars.ONESIGNAL_REST_APIKEY,
|
||||
},
|
||||
icici: {
|
||||
baseURL: "https://apibankingonesandbox.icicibank.com",
|
||||
apikey: "COIbAAYzt0SMosd3fFexJlk42uqnPvvu",
|
||||
x_forwarded_for: "122.179.140.110"
|
||||
}
|
||||
|
||||
};
|
||||
} catch (error) {
|
||||
throw new Error(`Config validation error: ${Array.isArray(error?.errors) ? error.errors?.join(', ') : error}`);
|
||||
}
|
||||
26
src/config/logger.js
Normal file
26
src/config/logger.js
Normal file
@@ -0,0 +1,26 @@
|
||||
const winston = require('winston');
|
||||
const config = require('./config');
|
||||
|
||||
const enumerateErrorFormat = winston.format((info) => {
|
||||
if (info instanceof Error) {
|
||||
Object.assign(info, { message: info.stack });
|
||||
}
|
||||
return info;
|
||||
});
|
||||
|
||||
const logger = winston.createLogger({
|
||||
level: config.env === 'development' ? 'debug' : 'info',
|
||||
format: winston.format.combine(
|
||||
enumerateErrorFormat(),
|
||||
config.env === 'development' ? winston.format.colorize() : winston.format.uncolorize(),
|
||||
winston.format.splat(),
|
||||
winston.format.printf(({ level, message }) => `${level}: ${message}`)
|
||||
),
|
||||
transports: [
|
||||
new winston.transports.Console({
|
||||
stderrLevels: ['error'],
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
module.exports = logger;
|
||||
33
src/config/morgan.js
Normal file
33
src/config/morgan.js
Normal file
@@ -0,0 +1,33 @@
|
||||
const morgan = require('morgan');
|
||||
const config = require('./config');
|
||||
const logger = require('./logger');
|
||||
|
||||
// Create a custom token for client IP
|
||||
morgan.token('clientIp', (req) => req.clientIp || '');
|
||||
|
||||
// Custom token for error message
|
||||
morgan.token('message', (req, res) => res.locals.errorMessage || '');
|
||||
|
||||
// Function to get IP format based on environment
|
||||
const getIpFormat = () => (config.env === 'production' ? ':clientIp - ' : '');
|
||||
|
||||
// Define the response formats using morgan tokens
|
||||
const successResponseFormat = `${getIpFormat()}:method :url :status - :response-time ms`;
|
||||
const errorResponseFormat = `${getIpFormat()}:method :url :status - :response-time ms - message: :message`;
|
||||
|
||||
// Success handler (for requests with status codes below 400)
|
||||
const successHandler = morgan(successResponseFormat, {
|
||||
skip: (req, res) => res.statusCode >= 400,
|
||||
stream: { write: (message) => logger.info(message.trim()) },
|
||||
});
|
||||
|
||||
// Error handler (for requests with status codes 400 and above)
|
||||
const errorHandler = morgan(errorResponseFormat, {
|
||||
skip: (req, res) => res.statusCode < 400,
|
||||
stream: { write: (message) => logger.error(message.trim()) },
|
||||
});
|
||||
|
||||
module.exports = {
|
||||
successHandler,
|
||||
errorHandler,
|
||||
};
|
||||
7
src/config/nodemailerConfig.js
Normal file
7
src/config/nodemailerConfig.js
Normal file
@@ -0,0 +1,7 @@
|
||||
const nodemailer = require('nodemailer');
|
||||
const config = require('./config');
|
||||
|
||||
const transporter = nodemailer.createTransport(config.email.smtp);
|
||||
|
||||
module.exports = { transporter }
|
||||
|
||||
10
src/config/oneSignal.config.js
Normal file
10
src/config/oneSignal.config.js
Normal file
@@ -0,0 +1,10 @@
|
||||
const OneSignal = require("@onesignal/node-onesignal");
|
||||
const config = require("./config");
|
||||
const configuration = OneSignal.createConfiguration({
|
||||
userAuthKey: config.oneSignal.userAuthKey, // Your User Auth Key
|
||||
restApiKey: config.oneSignal.restApiKey, // Your REST API Key
|
||||
});
|
||||
|
||||
const client = new OneSignal.DefaultApi(configuration);
|
||||
|
||||
module.exports = client;
|
||||
12
src/config/roles.js
Normal file
12
src/config/roles.js
Normal file
@@ -0,0 +1,12 @@
|
||||
const allRoles = {
|
||||
user: [],
|
||||
admin: ['getUsers', 'manageUsers'],
|
||||
};
|
||||
|
||||
const roles = Object.keys(allRoles);
|
||||
const roleRights = new Map(Object.entries(allRoles));
|
||||
|
||||
module.exports = {
|
||||
roles,
|
||||
roleRights,
|
||||
};
|
||||
10
src/config/tokens.js
Normal file
10
src/config/tokens.js
Normal file
@@ -0,0 +1,10 @@
|
||||
const tokenTypes = {
|
||||
ACCESS: 'access',
|
||||
REFRESH: 'refresh',
|
||||
RESET_PASSWORD: 'resetPassword',
|
||||
VERIFY_EMAIL: 'verifyEmail',
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
tokenTypes,
|
||||
};
|
||||
10
src/config/twilioClient.js
Normal file
10
src/config/twilioClient.js
Normal file
@@ -0,0 +1,10 @@
|
||||
|
||||
const twilio = require('twilio');
|
||||
const config = require('./config');
|
||||
|
||||
const accountSid = config.SMS.accountSid;
|
||||
const authToken = config.SMS.authToken;
|
||||
|
||||
const twilioClient = twilio(accountSid, authToken);
|
||||
|
||||
module.exports = twilioClient;
|
||||
115
src/controllers/icici/icicibank.controller.js
Normal file
115
src/controllers/icici/icicibank.controller.js
Normal file
@@ -0,0 +1,115 @@
|
||||
const { AsyncHandler } = require('../../utils/handler/Async.handler');
|
||||
const ApiError = require('../../utils/handler/ApiError.handler');
|
||||
const ApiResponse = require('../../utils/handler/ApiResponse.handler');
|
||||
const { cryptoService } = require('../../services');
|
||||
const { iciciApi } = require('../../api');
|
||||
|
||||
module.exports = {
|
||||
createOTP: AsyncHandler(async (req, res) => {
|
||||
try {
|
||||
const jsonData = {
|
||||
"AGGRNAME": "CIBTESTING",
|
||||
"AGGRID": "TXBCIBTEST001",
|
||||
"CORPID": "TXBCORP1",
|
||||
"USERID": "USER1",
|
||||
"URN": "TESTING123",
|
||||
"UNIQUEID": "ABC125"
|
||||
}
|
||||
const encryptedData = await cryptoService.encryptWithPublicKey(jsonData)
|
||||
const result = await iciciApi.createOtp(encryptedData)
|
||||
const decryptedData = await cryptoService.decryptWithPrivateKey(result);
|
||||
res.status(200).json(new ApiResponse(200, decryptedData, "Registration successful"));
|
||||
} catch (error) {
|
||||
throw new ApiError(500, "Registration failed");
|
||||
}
|
||||
}),
|
||||
|
||||
balanceInquiry: AsyncHandler(async (req, res) => {
|
||||
try {
|
||||
const jsonData = {
|
||||
"AGGRID": "TXBCIBTEST001",
|
||||
"CORPID": "TXBCORP1",
|
||||
"USERID": "USER1",
|
||||
"URN": "TESTING123",
|
||||
"ACCOUNTNO": "010205001809"
|
||||
}
|
||||
const encryptedData = await cryptoService.encryptWithPublicKey(jsonData)
|
||||
const result = await iciciApi.balanceInquiry(encryptedData)
|
||||
const decryptedData = await cryptoService.decryptWithPrivateKey(result);
|
||||
res.status(200).json(new ApiResponse(200, decryptedData, "Balance Inquiry successful"));
|
||||
} catch (error) {
|
||||
throw new ApiError(500, "Balance Inquiry failed");
|
||||
}
|
||||
}),
|
||||
|
||||
transactionOTP: AsyncHandler(async (req, res) => {
|
||||
try {
|
||||
const jsonData = {
|
||||
"AGGRNAME": "CIBTESTING",
|
||||
"AGGRID": "TXBCIBTEST001",
|
||||
"CORPID": "TXBCORP1",
|
||||
"USERID": "USER1",
|
||||
"URN": "TESTING123",
|
||||
"UNIQUEID": "ABC126",
|
||||
"DEBITACC": "010205001809",
|
||||
"CREDITACC": "000451000301",
|
||||
"IFSC": "ICIC0000011",
|
||||
"AMOUNT": "96.74",
|
||||
"CURRENCY": "INR",
|
||||
"TXNTYPE": "TPA",
|
||||
"OTP": "580601",
|
||||
"PAYEENAME": "WDI",
|
||||
"REMARKS": "Test"
|
||||
}
|
||||
const encryptedData = await cryptoService.encryptWithPublicKey(jsonData)
|
||||
const result = await iciciApi.transactionOTP(encryptedData)
|
||||
const decryptedData = await cryptoService.decryptWithPrivateKey(result);
|
||||
res.status(200).json(new ApiResponse(200, decryptedData, "Transaction OTP successful"));
|
||||
} catch (error) {
|
||||
throw new ApiError(500, "Transaction OTP failed");
|
||||
}
|
||||
}),
|
||||
|
||||
accountStatement: AsyncHandler(async (req, res) => {
|
||||
try {
|
||||
const jsonData = {
|
||||
"AGGRID": "TXBCIBTEST001",
|
||||
"CORPID": "TXBCORP1",
|
||||
"USERID": "USER1",
|
||||
"ACCOUNTNO": "010205001809",
|
||||
"FROMDATE": "01-01-2016",
|
||||
"TODATE": "30-12-2016",
|
||||
"URN": "TESTING123"
|
||||
}
|
||||
|
||||
const encryptedData = await cryptoService.encryptWithPublicKey(jsonData)
|
||||
const result = await iciciApi.accountStatement(encryptedData)
|
||||
console.log("result", result)
|
||||
|
||||
const decryptEncryptedKey = await cryptoService.decryptKey(result.encryptedKey)
|
||||
|
||||
const decodedData = await cryptoService.base64Decode(result.encryptedData);
|
||||
const getIV = await cryptoService.getIV(decodedData);
|
||||
const statement = await cryptoService.decryptData(decodedData, decryptEncryptedKey, getIV)
|
||||
res.status(200).json(new ApiResponse(200, statement, "Account Statement Data Fetched Successfully"));
|
||||
} catch (error) {
|
||||
throw new ApiError(500, "Account Statement Data failed");
|
||||
}
|
||||
}),
|
||||
|
||||
RegistrationStatus: AsyncHandler(async (req, res) => {
|
||||
try {
|
||||
const jsonData = {
|
||||
AGGRNAME: "CIBTESTING",
|
||||
AGGRID: "TXBCIBTEST001",
|
||||
CORPID: "TXBCORP1",
|
||||
USERID: "USER1",
|
||||
URN: "TESTING123"
|
||||
};
|
||||
const data = await registrationService.checkRegistrationStatus();
|
||||
res.status(200).json(new ApiResponse(200, data, "Registration status retrieved"));
|
||||
} catch (error) {
|
||||
throw new ApiError(500, "Failed to retrieve registration status");
|
||||
}
|
||||
}),
|
||||
};
|
||||
3
src/controllers/index.js
Normal file
3
src/controllers/index.js
Normal file
@@ -0,0 +1,3 @@
|
||||
module.exports = {
|
||||
iciciBankController: require('./icici/icicibank.controller')
|
||||
}
|
||||
106
src/data/data.json
Normal file
106
src/data/data.json
Normal file
@@ -0,0 +1,106 @@
|
||||
{
|
||||
"iamType": [
|
||||
{
|
||||
"principalTypeName": "OptifiiAdmin"
|
||||
},
|
||||
{
|
||||
"principalTypeName": "CorporateAdmin"
|
||||
},
|
||||
{
|
||||
"principalTypeName": "User"
|
||||
}
|
||||
],
|
||||
"iamSource": [
|
||||
{
|
||||
"sourceName": "Web"
|
||||
},
|
||||
{
|
||||
"sourceName": "Mobile"
|
||||
}
|
||||
],
|
||||
"administrator": {
|
||||
"firstName": "Vinay",
|
||||
"lastName": "Agarwal",
|
||||
"ISDcode": "+91",
|
||||
"mobileNumber": "88888888888",
|
||||
"emailAddress": "admin@optifii.com",
|
||||
"password": "Admin@123",
|
||||
"principalType": "OptifiiAdmin",
|
||||
"principalSource": "Web"
|
||||
},
|
||||
"corporate_status": [
|
||||
{
|
||||
"status": "Requested"
|
||||
},
|
||||
{
|
||||
"status": "Submitted"
|
||||
},
|
||||
{
|
||||
"status": "Registerd"
|
||||
},
|
||||
{
|
||||
"status": "Approved"
|
||||
},
|
||||
{
|
||||
"status": "Rejected"
|
||||
}
|
||||
],
|
||||
"corporate_type": [
|
||||
{
|
||||
"corporate_type": "C Corporation (C Corp)"
|
||||
},
|
||||
{
|
||||
"corporate_type": "S Corporation (S Corp)"
|
||||
},
|
||||
{
|
||||
"corporate_type": "Limited Liability Company (LLC)"
|
||||
},
|
||||
{
|
||||
"corporate_type": "Nonprofit Corporation"
|
||||
},
|
||||
{
|
||||
"corporate_type": "Benefit Corporation (B Corp)"
|
||||
},
|
||||
{
|
||||
"corporate_type": "Professional Corporation (PC)"
|
||||
},
|
||||
{
|
||||
"corporate_type": "Cooperative (Co-op)"
|
||||
}
|
||||
],
|
||||
"industry": [
|
||||
{
|
||||
"industry_name": "Agriculture and Natural Resources"
|
||||
},
|
||||
{
|
||||
"industry_name": "Manufacturing"
|
||||
},
|
||||
{
|
||||
"industry_name": "Construction and Real Estate"
|
||||
},
|
||||
{
|
||||
"industry_name": "Services"
|
||||
},
|
||||
{
|
||||
"industry_name": "Technology"
|
||||
},
|
||||
{
|
||||
"industry_name": "Finance and Insurance"
|
||||
},
|
||||
{
|
||||
"industry_name": "Media and Entertainment"
|
||||
},
|
||||
{
|
||||
"industry_name": "Energy and Utilities"
|
||||
},
|
||||
{
|
||||
"industry_name": "Professional Services"
|
||||
},
|
||||
{
|
||||
"industry_name": "Nonprofit and Community Services"
|
||||
},
|
||||
{
|
||||
"industry_name": "Emerging Industries"
|
||||
}
|
||||
]
|
||||
}
|
||||
18
src/docs/swaggerDef.js
Normal file
18
src/docs/swaggerDef.js
Normal file
@@ -0,0 +1,18 @@
|
||||
const { version } = require('../../package.json');
|
||||
const config = require('../config/config');
|
||||
|
||||
const swaggerDef = {
|
||||
openapi: '3.0.0',
|
||||
info: {
|
||||
title: 'Tanami Backend API documentation',
|
||||
version,
|
||||
description: 'Tanami Backend API documentation',
|
||||
},
|
||||
servers: [
|
||||
{
|
||||
url: `http://localhost:${config.port}/v1`,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
module.exports = swaggerDef;
|
||||
51
src/index.js
Normal file
51
src/index.js
Normal file
@@ -0,0 +1,51 @@
|
||||
const app = require('./app');
|
||||
const config = require('./config/config');
|
||||
const logger = require('./config/logger');
|
||||
const db = require('./models');
|
||||
// const seed = require('./seed');
|
||||
|
||||
let server;
|
||||
|
||||
db.sequelize
|
||||
.sync({ force: config.env === 'test' })
|
||||
.then(() => {
|
||||
logger.info('Connected to MySQL');
|
||||
// seed(db)
|
||||
server = app.listen(config.port, () => {
|
||||
logger.info(`Server listening on port ${config.port}`)
|
||||
logger.info(`Enviorment :- ${config.env}`);
|
||||
logger.info(`Is OTP by pass :- ${config.byPassOTP}`);
|
||||
logger.info(`Is Notification by pass :- ${config.byPassNotification}`);
|
||||
logger.info(`Is Email by pass :- ${config.byPassEmail}`);
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
logger.error('Error connecting to MySQL:', error);
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
const exitHandler = () => {
|
||||
if (server) {
|
||||
server.close(() => {
|
||||
logger.info('Server closed');
|
||||
process.exit(1);
|
||||
});
|
||||
} else {
|
||||
process.exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
const unexpectedErrorHandler = (error) => {
|
||||
logger.error(error);
|
||||
exitHandler();
|
||||
};
|
||||
|
||||
process.on('uncaughtException', unexpectedErrorHandler);
|
||||
process.on('unhandledRejection', unexpectedErrorHandler);
|
||||
|
||||
process.on('SIGTERM', () => {
|
||||
logger.info('SIGTERM received');
|
||||
if (server) {
|
||||
server.close();
|
||||
}
|
||||
});
|
||||
50
src/keys/certificate.pem
Normal file
50
src/keys/certificate.pem
Normal file
@@ -0,0 +1,50 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIFhDCCA2wCCQCIqfcsomoC1jANBgkqhkiG9w0BAQUFADCBgzELMAkGA1UEBhMCSU4xF
|
||||
DASBgNV
|
||||
BAgMC01BSEFSQVNIVFJBMQ8wDQYDVQQHDAZNVU1CQUkxGDAWBgNVBAoMD0lDSUN
|
||||
JIEJhbmsgTHRk
|
||||
LjEMMAoGA1UECwwDQ0lCMSUwIwYDVQQDDBx3d3cuY2libmV4dGFwaS5pY2ljaWJhbmsuY
|
||||
29tMB4X
|
||||
DTE3MDQxMzA3MTkyOVoXDTIyMDQxMjA3MTkyOVowgYMxCzAJBgNVBAYTAklOMRQwEgY
|
||||
DVQQIDAtN
|
||||
QUhBUkFTSFRSQTEPMA0GA1UEBwwGTVVNQkFJMRgwFgYDVQQKDA9JQ0lDSSBCYW5rI
|
||||
Ex0ZC4xDDAK
|
||||
BgNVBAsMA0NJQjElMCMGA1UEAwwcd3d3LmNpYm5leHRhcGkuaWNpY2liYW5rLmNvbTCC
|
||||
AiIwDQYJ
|
||||
KoZIhvcNAQEBBQADggIPADCCAgoCggIBAM+en2ErEsETmfoZJjf3I5DIc8KAt6dv/ZkKYHcpli1
|
||||
g
|
||||
yLFjJNbZnyk4Um7UaKvU0fpqnsLboapXKR0iHFp4/7SR9kTh4FfvFrrp2pKmQd8f/Rf6OPk2/48i
|
||||
X7sCs0nl8IZYMqe1Tt1YAMFPJjPIH/ERx3vnWYhgHUmRGJqjHfo4NeNR0IarF3HAYX4hh6K0L
|
||||
aAQ
|
||||
hUoq6SuWyWf9m9qzHRHpWq4eJRsbhPYLaTtt8XS+vPpBjFjfQreDtgWdIXwKuHq8EOS/KxBif
|
||||
ThC
|
||||
tEBMGZUSYZBoldq1kdaakkt5FaXhe+g0FWrLcxalaSS4bHK0QCv1Lbh3tcPetCO3XyR1Jj28SL
|
||||
+5
|
||||
gYm464jmjMGURJwocWUhuNd0qAKt8bMv9NCDgKiWSmAlzeznRYeNaay2ckg5aB5tNO5l/8pU
|
||||
h8Ew
|
||||
qLyKECFnCoNvBlcaoJIvZ0sprQO+dHzggT/Q9wl0XRFUkPh4SFGHIiqldy6VgA6I7uVWb7ve1Y3
|
||||
P
|
||||
4yhlfTDV/Hr4ZL4gTFVrorS1a4Tqap38iqHnfM3djwgwbnzv0TJZCywZ5ED8MRDmub6W4jNYMV
|
||||
ar
|
||||
uG1gLVf7gE2sUY/dgTRu1Hdw3/YlOY9XpQebBP37RD3+Up+oEYxjPe04Cy4rTFx9/8SluuBPvw
|
||||
NH
|
||||
WVmHkv1ULNHum0VQ3kej17jbEeO+FftJAgMBAAEwDQYJKoZIhvcNAQEFBQADggIBAMGX2
|
||||
dKuXsGj
|
||||
ujhKxZOFzo8A0QKu+nsw+pFtiJ5KjyOR1vW9pOdG7roJJGr6cU5fUDlUpYDDVIvPiVbPYgWLkV
|
||||
e3
|
||||
7+tpM8T77ZYSXdO7G9hhU8uw2pcRHiQMlDotV/RcTGZHyVVaw7TJty3xMH2j0/FIHejcFaYXZY
|
||||
QB
|
||||
A5+zKc7PBsvwn/KQgJ9R4BTqmdWeca1r0+iBXGq1iRg4IGePf0lIc+80AUneC1ceC07RfvI0PJp
|
||||
k
|
||||
LVTkDCXdNK7QtG/cIqjdZ1jtB+ne7cwtksw1ewu5dE3BFNmqdT3DmKHAupTc2ILSup2w/JEEep
|
||||
MI
|
||||
DHO8GvqR0dUXS5xCcXNKwXUMiLPYA56mRKoST5+e2RO5WtVQMHiizEF5iID+WjyXNlVtqM
|
||||
arEjih
|
||||
Z0+/vkABp/Q3AfKs3rtaXxU4crt+RLaaldG/dBXOoUDTpaNR+ktUkNmEPTe9zc7pwwRDC2zNylt
|
||||
4
|
||||
FnhNP2b2t+RLuP+smAROVaXA1owpte3zeh7aiUe02Y6udEzVrKCAvRUiCKoCDH9N101k3lzC
|
||||
Fy80
|
||||
rRquHZ7ZZmUrX4DksuPnSuLILR5ss6UkQTZbg7HXtMN2lDTgPjO2UMCjqI+5gPGTqdld4XWD
|
||||
TEW0
|
||||
xdyhJiEgATeqQllbn47B7C7603ltWFpoInafn2NwxBW89wv938bMKpxFxmQcseGH
|
||||
-----END CERTIFICATE-----
|
||||
14
src/keys/public_key.pem
Normal file
14
src/keys/public_key.pem
Normal file
@@ -0,0 +1,14 @@
|
||||
-----BEGIN PUBLIC KEY-----
|
||||
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAz56fYSsSwROZ+hkmN/cj
|
||||
kMhzwoC3p2/9mQpgdymWLWDIsWMk1tmfKThSbtRoq9TR+mqewtuhqlcpHSIcWnj/
|
||||
tJH2ROHgV+8WuunakqZB3x/9F/o4+Tb/jyJfuwKzSeXwhlgyp7VO3VgAwU8mM8gf
|
||||
8RHHe+dZiGAdSZEYmqMd+jg141HQhqsXccBhfiGHorQtoBCFSirpK5bJZ/2b2rMd
|
||||
Eelarh4lGxuE9gtpO23xdL68+kGMWN9Ct4O2BZ0hfAq4erwQ5L8rEGJ9OEK0QEwZ
|
||||
lRJhkGiV2rWR1pqSS3kVpeF76DQVastzFqVpJLhscrRAK/UtuHe1w960I7dfJHUm
|
||||
PbxIv7mBibjriOaMwZREnChxZSG413SoAq3xsy/00IOAqJZKYCXN7OdFh41prLZy
|
||||
SDloHm007mX/ylSHwTCovIoQIWcKg28GVxqgki9nSymtA750fOCBP9D3CXRdEVSQ
|
||||
+HhIUYciKqV3LpWADoju5VZvu97Vjc/jKGV9MNX8evhkviBMVWuitLVrhOpqnfyK
|
||||
oed8zd2PCDBufO/RMlkLLBnkQPwxEOa5vpbiM1gxVqu4bWAtV/uATaxRj92BNG7U
|
||||
d3Df9iU5j1elB5sE/ftEPf5Sn6gRjGM97TgLLitMXH3/xKW64E+/A0dZWYeS/VQs
|
||||
0e6bRVDeR6PXuNsR474V+0kCAwEAAQ==
|
||||
-----END PUBLIC KEY-----
|
||||
51
src/keys/rsa_private_key.pem
Normal file
51
src/keys/rsa_private_key.pem
Normal file
@@ -0,0 +1,51 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIJKQIBAAKCAgEAqj4v2DMCY3kl5cedsFzM9/jjiYfXbp5PiaXrtGfl9o1kNej4
|
||||
YGg6+uV4sLclxT66n2mknE7hKLRmMRbjpN3d2NU+KzVx0CKEmJ2RKtCYsTw0iCaA
|
||||
b2D4hDYZBhdzyBbqdhCgdUhzexFuTtlXlT8U5fZvv+nj8xXrm5Q6z8AuQy/q2AN1
|
||||
blGaifSbebPgdyhJ3DqgBSP0x67pvyEVWOVerhMw6L6eqfnDHEvaEopynEpm8eQx
|
||||
kUxroVbERyFfGP/poOb2LSlUqvhK4LOR3Zt1YW/KdcJ+a/8W2zSHyhHVuHwVb4ke
|
||||
LYC2IdYpcZuwyxN6PX4/k9mLcQbhGUerD+c59U/DhXnOjyHLMc6HtfO4MO5v6DbC
|
||||
XsZca5qjqBabKJbrpcGZrEQ+6ECriajYySfgUbxppytSBhcIjSzFPKyM4oUgEOFU
|
||||
LVmoVXo1UjuhaR7Q7NhDEqE4lg67dtGrCTZ/KAITgLhU9CIS6qRes4pO7S/lXfRE
|
||||
yBaYepcaLsS66dnq9zjXNnHY/L6zwCuyzjt2Im34TXDYfjxkKnGLDvLN15ZGt6Ji
|
||||
VAFh4Tb2hCF1tWs97VwaZJq/czIBt/jmabGBNJ1wZ2yFshTfeCnvtPlqO6VZeNP7
|
||||
HqoRF7rVPuePkRiff8E6s2bxisF2pt+2t/swNlZj4pHEoRiEhdJavWLuz+0CAwEA
|
||||
AQKCAgAUB1NtKdkk106qWSD36/8QSGeKmWDyymy8mrjfhV60cj+BKSq5euG/fUpE
|
||||
7hMjQMVHdnAPlKcgORMRwOngzlpohXP2NOgyLrFvXBBFW3uvVFAw2WjAUcYDFfM9
|
||||
OHqeZnXIlJ2wFqlBaBRUcfUAIccFZAgTKcLv6RsKN3bw1KEMtgE4zzVcWHzoMI8v
|
||||
Ewa8NhD0eDA10pxcdrtYyV1f00Jfk/Hr2+tCP5hhBpp+Fmwz73vrhud++uDpvxHB
|
||||
9Y7g29DCZZG+T+++Wo5Cn9WwHvdBEpwc3Rgu//iSKvyzvQPqG60Q7W6Pt2YWFoJT
|
||||
M5gp2B2ISQdVT2F8l2zZoskhCpjSjqMXJxwgg/jp/uXLaTEYNtKCbMM1DSrg1VnQ
|
||||
78OyhBV7TJBq+hsAwPeuxiaujSv+LjxbeBWHBUZa2Ru2bcN22zzbtAySrTH8TtOw
|
||||
1UeTkz0lG3alPW1alAYcZF7nNTOv7VAEUhFBgIKF96jgSzUDpkAVClW0gmAwng2u
|
||||
8zw+Hqtws/UQgeMxoE+Fs+wlwq/GK/219TMoARpdYdgN2l09q9dFZ2d0A2mzvAD1
|
||||
aUTdgnOrfcel/3IfIBXt8PN51JlASmp7XMVWjVolQ6iG90+zKwpzlZX3XoupyUZj
|
||||
ChaoR0kwk4GEQgwqlLHTJcrmbDvbJxMmi6f0HFK4s/D19c8xuQKCAQEA0x/mu3I4
|
||||
dRI46Cgx8BEKH9Isp2t1pCsn4Ai/GhwASRRryi9ZO0pQsR06xayulE1zTSPIg/Mq
|
||||
wT4Yo/e9QZrRGvhsMSFCU5jHhDHgr77IRoSibZ5C4v99P9Sbi3b3wGsxkiYuttDE
|
||||
exBy+9HSpRgJxDdXZGEOMSl3+IF/VsZe2eL5JArWBC4XZU/Lm4C3C/HlAA9aVQVb
|
||||
U+rjlJcL0T9iFzjDiJdzwVxK3oDRVpxyh/hHmD47DZmb7u8Ea1VjIlntrjKymWta
|
||||
Z/6tPJ81oJftQhSexRHZPFy+Wtm2xfPkENEfsVxtwQQ6Hg7bk9Iwme8Oj/pKFLpI
|
||||
4W5l4YIqJWX0WQKCAQEAzm3BhEFXOkAy89f9PLx6o1RFqCutx6Silt2rv/SpB4Ju
|
||||
VWvRLdPZxqQbHQ/5enZ6MEUTCoNzaM2uGbz4TP3718hWW7Sw6QlN1iOYS3eODNNQ
|
||||
YZIz+IpLJ9hbrWua7LS1Ypb7IO6F+F8lEbue9evyWD2EderA8NckWGEv5JTiBuTT
|
||||
sXsKNhRuBOBwht7nXwL8s1DEdkok5mLmFlqtIYDpQRm3N9IVa40wn2g+8ww8C7tR
|
||||
uygDO4PvPnTBhMVEz78MtVOk3MZYjIHhGsJR6iTB0zc3ze6gaVQ3eigyA1Ii/AUy
|
||||
fke/cPiw30oVRJHnyZFTo06sNXjqtlc+DvSNNIPVtQKCAQEAg/a4vgmTCHovX81g
|
||||
CXJdJa61gqBElCz1a6+L48IE26kMBwC5gbnd3hcrGUvqg4A1xc3ME832t3sc48CC
|
||||
Z3NhqL4Gwl7Lmn0wmIykqLVTceCNtn7pyAFyRGecIfxmt7tI7NU49cRgS5vog0aP
|
||||
p+nykNcWpQOVX11QQ+CNu6uatg6NM94iD9LPhN/voG8/+xNj1DnEeMg0Yau8PLB9
|
||||
DnnT0jgE4GfqTr5lfdZ/AugfHqYt+hdLyiBtu1djJ8PjhyE201+VCxhzFfW2SuIp
|
||||
HwxXnKpO41dYtcYypY7YE7tynqBYcwAXCkKeIROgZDFJd65ZJ92GWJZn36ClxeMa
|
||||
fO3RYQKCAQEAoQFiKnF8Q2bHVwFj9vdbAAE+w0guJsT2O41x5CpDnCPTQrTxVjki
|
||||
ZpigvS40e5vk+bkmfNDCN0AVRuXpFMQUpd0P3j80rM4g86CXrGT3WnGHBFMwTe4v
|
||||
aEiMWNrQ30ajIt53yNvBilNLamVYOp9pSgHRStdq3W4wXu6OmE63bIEVzBwXfs64
|
||||
cP1NRyScebKuvn2Efm6eEUOaCl13I+aUB76y9MrbAiQBhJOeZZtpSg00VjGDM+xX
|
||||
sfMG7TOf1BlDJDq1H61ka1Lx7BkcIu/Abaln0SsJ2p1hF9o6B/UMbFzxYSU84DRY
|
||||
YBA8Pls+2iyLe5hlIN/K0aWCNc/wQsGA8QKCAQB4Yg8kQ/Wz5oX0dmMHC/ygXKmi
|
||||
aJlXigCWzIwmwrp4uVeGeYh7WTzIyt42aYvbKa7liq0NyFiKBJYUuwmqDaHAu5Il
|
||||
R4CdaWFu8hD5rN2qYwGpUIJdVUwsR9g6bebDYg8TEknSFzLrDwgxKRCmcEnwsmho
|
||||
k2GnzUzIb+by/qNTKCByExEoYSsqo/V6oTmFmNp1tp3VEvZvQmy4Px7gehmBxbmx
|
||||
ONDFz27NOwspEE+TsbIds9W5NmBCIUsgGMiK3bV5F3dwy649Vt0cpyGXkxr8ksfX
|
||||
3visuVX+95IPVyoY6M+J/p2xcYhJwci1A7Cexw7QcalcmfOm8x8qPtnhH6Lk
|
||||
-----END RSA PRIVATE KEY-----
|
||||
39
src/middlewares/auth.js
Normal file
39
src/middlewares/auth.js
Normal file
@@ -0,0 +1,39 @@
|
||||
const jwt = require('jsonwebtoken');
|
||||
const httpStatus = require('http-status');
|
||||
const { roleRights } = require('../config/roles'); // Replace with your actual JWT secret configuration
|
||||
const ApiError = require('../utils/handler/ApiError.handler');
|
||||
const config = require('../config/config');
|
||||
|
||||
const verifyCallback = async (req, resolve, reject, requiredRights) => {
|
||||
const token = req.header("x-auth-token"); // Assuming the token is passed in the Authorization header
|
||||
if (!token) {
|
||||
return reject(new ApiError(httpStatus.UNAUTHORIZED, 'Please authenticate'));
|
||||
}
|
||||
|
||||
try {
|
||||
const decoded = jwt.verify(token, config.jwt.secret);
|
||||
req.user = decoded
|
||||
|
||||
if (requiredRights.length) {
|
||||
const userRights = roleRights.get(req.user.role);
|
||||
const hasRequiredRights = requiredRights.every((requiredRight) => userRights.includes(requiredRight));
|
||||
if (!hasRequiredRights && req.params.userId !== req.user.id) {
|
||||
return reject(new ApiError(httpStatus.FORBIDDEN, 'Forbidden'));
|
||||
}
|
||||
}
|
||||
|
||||
resolve();
|
||||
} catch (err) {
|
||||
return reject(new ApiError(httpStatus.FORBIDDEN, 'Please authenticate'));
|
||||
}
|
||||
};
|
||||
|
||||
const auth = (...requiredRights) => async (req, res, next) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
verifyCallback(req, resolve, reject, requiredRights);
|
||||
})
|
||||
.then(() => next())
|
||||
.catch((err) => next(err));
|
||||
};
|
||||
|
||||
module.exports = auth;
|
||||
11
src/middlewares/code.js
Normal file
11
src/middlewares/code.js
Normal file
@@ -0,0 +1,11 @@
|
||||
const { decryptUserId } = require("../services/code/code.service");
|
||||
const ApiError = require("../utils/handler/ApiError.handler")
|
||||
|
||||
const code = (err, req, res, next) => {
|
||||
const code = req.header("x-auth-code");
|
||||
if (!code) { throw new ApiError(400, "Code is Required") }
|
||||
req.corporate_code = decryptUserId(code)
|
||||
next()
|
||||
}
|
||||
|
||||
module.exports = code
|
||||
55
src/middlewares/error.js
Normal file
55
src/middlewares/error.js
Normal file
@@ -0,0 +1,55 @@
|
||||
const { UniqueConstraintError, BaseError, ValidationError } = require("sequelize");
|
||||
const ApiError = require("../utils/handler/ApiError.handler");
|
||||
const config = require("../config/config");
|
||||
const httpStatus = require("http-status");
|
||||
const logger = require("../config/logger");
|
||||
const multer = require("multer");
|
||||
|
||||
const errorConverter = (err, req, res, next) => {
|
||||
let error = err;
|
||||
|
||||
if (error instanceof multer.MulterError) {
|
||||
// Handle Multer errors
|
||||
error = new ApiError(httpStatus.BAD_REQUEST, error.message, error, true, err.stack);
|
||||
} else if (error instanceof ValidationError || error instanceof UniqueConstraintError) {
|
||||
// Handle Sequelize validation and unique constraint errors
|
||||
const messages = error.errors.map(e => e.message);
|
||||
error = new ApiError(httpStatus.BAD_REQUEST, messages.join(", "), messages, true, err.stack);
|
||||
} else if (!(error instanceof ApiError)) {
|
||||
// Handle other errors
|
||||
const statusCode =
|
||||
error.statusCode || error instanceof BaseError ? httpStatus.BAD_REQUEST : httpStatus.INTERNAL_SERVER_ERROR;
|
||||
const message = error.message || httpStatus[statusCode];
|
||||
error = new ApiError(statusCode, message, error, false, err.stack);
|
||||
}
|
||||
|
||||
next(error);
|
||||
};
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const errorHandler = (err, req, res, next) => {
|
||||
let { statusCode, message } = err;
|
||||
if (config.env === 'production' && !err.isOperational) {
|
||||
statusCode = httpStatus.INTERNAL_SERVER_ERROR;
|
||||
message = httpStatus[httpStatus.INTERNAL_SERVER_ERROR];
|
||||
}
|
||||
|
||||
res.locals.errorMessage = err.message;
|
||||
|
||||
const response = {
|
||||
code: statusCode,
|
||||
message,
|
||||
...(config.env === 'development' && { stack: err.stack }),
|
||||
};
|
||||
|
||||
if (config.env === 'development') {
|
||||
logger.error(err);
|
||||
}
|
||||
|
||||
res.status(statusCode).send(response);
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
errorConverter,
|
||||
errorHandler,
|
||||
};
|
||||
11
src/middlewares/rateLimiter.js
Normal file
11
src/middlewares/rateLimiter.js
Normal file
@@ -0,0 +1,11 @@
|
||||
const { default: rateLimit } = require('express-rate-limit');
|
||||
|
||||
const authLimiter = rateLimit({
|
||||
windowMs: 15 * 60 * 1000,
|
||||
max: 100,
|
||||
skipSuccessfulRequests: true,
|
||||
});
|
||||
|
||||
module.exports = {
|
||||
authLimiter,
|
||||
};
|
||||
92
src/middlewares/storage.js
Normal file
92
src/middlewares/storage.js
Normal file
@@ -0,0 +1,92 @@
|
||||
const multer = require('multer');
|
||||
const fs = require('fs')
|
||||
const path = require('path');
|
||||
const ApiError = require('../utils/handler/ApiError.handler');
|
||||
const maxFileSize = 10 * 1024 * 1024; // 10 MB (in bytes)
|
||||
const allowedFileTypes = [
|
||||
"image/jpeg",
|
||||
"image/png",
|
||||
"image/gif",
|
||||
"application/pdf",
|
||||
"application/msword",
|
||||
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
||||
"video/mp4",
|
||||
"video/x-msvideo",
|
||||
"video/x-matroska",
|
||||
"video/webm",
|
||||
"video/ogg",
|
||||
"video/quicktime"
|
||||
];
|
||||
|
||||
// Define the storage engine function with parameters
|
||||
const uploader = (folderName) => multer.diskStorage({
|
||||
destination: function (req, file, cb) {
|
||||
// Check if the 'public' directory exists, and create it if it doesn't
|
||||
if (!fs.existsSync("public")) {
|
||||
try {
|
||||
fs.mkdirSync("public");
|
||||
} catch (err) {
|
||||
return cb(err);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle directory structure based on corporate_code if available
|
||||
let directoryPath = `public`;
|
||||
if (req.corporate_code) {
|
||||
directoryPath += `/${req.corporate_code}`;
|
||||
}
|
||||
directoryPath += `/${folderName}`;
|
||||
|
||||
// Check if the directory exists, create it if it doesn't
|
||||
if (!fs.existsSync(directoryPath)) {
|
||||
try {
|
||||
fs.mkdirSync(directoryPath, { recursive: true });
|
||||
} catch (err) {
|
||||
return cb(err);
|
||||
}
|
||||
}
|
||||
|
||||
cb(null, directoryPath);
|
||||
},
|
||||
filename: function (req, file, cb) {
|
||||
const transformedFilename = file.originalname
|
||||
.trim()
|
||||
.toLowerCase()
|
||||
.replace(/[^a-z\d.]+/g, "_")
|
||||
.replace(/_+/g, "_")
|
||||
.replace(/\.[^.]+$/, "")
|
||||
.replace(/\./g, "_")
|
||||
.replace(/^_|_$/g, "");
|
||||
|
||||
cb(
|
||||
null,
|
||||
transformedFilename + path.extname(file.originalname)
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
// Define the file filter function with parameters
|
||||
const fileFilter = (allowedTypes = allowedFileTypes) => function (req, file, cb) {
|
||||
if (allowedTypes.includes(file.mimetype)) {
|
||||
cb(null, true); // Allow the file
|
||||
} else {
|
||||
cb(new ApiError(400, "File type not allowed")); // Reject the file
|
||||
}
|
||||
};
|
||||
|
||||
// Set up the multer storage with parameters and file filter
|
||||
const storage = (folderName, allowedTypes, options = {}) => multer({
|
||||
storage: uploader(folderName || "asset"),
|
||||
limits: {
|
||||
fileSize: options.fileSize || maxFileSize,
|
||||
},
|
||||
fileFilter: fileFilter(allowedTypes),
|
||||
...options, // Merge additional options
|
||||
});
|
||||
|
||||
// Example usage:
|
||||
// const upload = storage("document", allowedFileTypes, { fileSize: 5 * 1024 * 1024, preservePath: true });
|
||||
// You can then use `upload` as middleware in your routes.
|
||||
|
||||
module.exports = storage;
|
||||
27
src/middlewares/validate.js
Normal file
27
src/middlewares/validate.js
Normal file
@@ -0,0 +1,27 @@
|
||||
const httpStatus = require("http-status");
|
||||
const ApiError = require("../utils/handler/ApiError.handler");
|
||||
const pick = require("../utils/handler/pick.handler");
|
||||
|
||||
const validate = (schema) => (req, res, next) => {
|
||||
const validSchema = pick(schema, ['params', 'query', 'body', 'file', 'files']);
|
||||
const object = pick(req, Object.keys(validSchema));
|
||||
|
||||
const promises = Object.keys(validSchema).map((key) =>
|
||||
validSchema[key].validate(object[key], { abortEarly: false })
|
||||
);
|
||||
|
||||
Promise.all(promises)
|
||||
.then((validatedValues) => {
|
||||
validatedValues.forEach((value, index) => {
|
||||
const key = Object.keys(validSchema)[index];
|
||||
req[key] = value;
|
||||
});
|
||||
next();
|
||||
})
|
||||
.catch((err) => {
|
||||
const errorMessage = err.inner.map((detail) => detail.message).join(', ');
|
||||
next(new ApiError(httpStatus.BAD_REQUEST, errorMessage));
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = validate;
|
||||
39
src/models/index.js
Normal file
39
src/models/index.js
Normal file
@@ -0,0 +1,39 @@
|
||||
"use strict";
|
||||
|
||||
const { Sequelize } = require("sequelize");
|
||||
const config = require("../config/config");
|
||||
const logger = require("../config/logger");
|
||||
|
||||
// Initialize Sequelize instance
|
||||
const sequelize = new Sequelize(
|
||||
config.mysql[config.env].database,
|
||||
config.mysql[config.env].username,
|
||||
config.mysql[config.env].password,
|
||||
config.mysql[config.env]
|
||||
);
|
||||
|
||||
// Test database connection
|
||||
sequelize
|
||||
.authenticate()
|
||||
.then(() => logger.info("Connection has been established successfully."))
|
||||
.catch((error) => logger.error("Unable to connect to the database: ", error));
|
||||
|
||||
// Load models
|
||||
const db = {};
|
||||
|
||||
//main models
|
||||
// db.token = require('./main/token.model')(sequelize);
|
||||
|
||||
// Handle model associations
|
||||
(async () => {
|
||||
for (const modelName of Object.keys(db)) {
|
||||
if (db[modelName].associate) {
|
||||
await db[modelName].associate(db);
|
||||
}
|
||||
}
|
||||
})();
|
||||
|
||||
db.sequelize = sequelize;
|
||||
db.Sequelize = Sequelize;
|
||||
|
||||
module.exports = db;
|
||||
5
src/routes/v1/icici/index.js
Normal file
5
src/routes/v1/icici/index.js
Normal file
@@ -0,0 +1,5 @@
|
||||
const iciciRoutes = require('express').Router()
|
||||
|
||||
iciciRoutes.use('/', require('./user.routes'))
|
||||
|
||||
module.exports = iciciRoutes
|
||||
10
src/routes/v1/icici/user.routes.js
Normal file
10
src/routes/v1/icici/user.routes.js
Normal file
@@ -0,0 +1,10 @@
|
||||
const { iciciBankController } = require('../../../controllers/index')
|
||||
|
||||
const userRoutes = require('express').Router();
|
||||
|
||||
userRoutes.route('/create-otp').post(iciciBankController.createOTP);
|
||||
userRoutes.route('/balance-inquiry').post(iciciBankController.balanceInquiry);
|
||||
userRoutes.route('/transaction-otp').post(iciciBankController.transactionOTP);
|
||||
userRoutes.route('/account-statement').post(iciciBankController.accountStatement);
|
||||
|
||||
module.exports = userRoutes;
|
||||
27
src/routes/v1/index.js
Normal file
27
src/routes/v1/index.js
Normal file
@@ -0,0 +1,27 @@
|
||||
const express = require('express');;
|
||||
const config = require('../../config/config');
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
const defaultRoutes = [
|
||||
{
|
||||
path: '/icici',
|
||||
route: require('./icici'),
|
||||
},
|
||||
];
|
||||
|
||||
const devRoutes = [
|
||||
// routes available only in development mode
|
||||
];
|
||||
|
||||
defaultRoutes.forEach((route) => {
|
||||
router.use(route.path, route.route);
|
||||
});
|
||||
/* istanbul ignore next */
|
||||
if (config.env === 'development') {
|
||||
devRoutes.forEach((route) => {
|
||||
router.use(route.path, route.route);
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = router;
|
||||
60
src/services/IAM/iam_principal.service.js
Normal file
60
src/services/IAM/iam_principal.service.js
Normal file
@@ -0,0 +1,60 @@
|
||||
const db = require("../../models");
|
||||
const iamConstant = require("../../utils/constant/iam.constant");
|
||||
const ApiError = require("../../utils/handler/ApiError.handler");
|
||||
|
||||
module.exports = {
|
||||
create: async (data) => await db.iam_principal.create(data),
|
||||
createInvestor: async (data) => {
|
||||
const source = await db.iam_principal_source.findOne({ where: { sourceName: data?.source || "Mobile" } })
|
||||
const type = await db.iam_principal_type.findOne({ where: { principalTypeName: data?.type || "Investor" } })
|
||||
return await db.iam_principal.create({ principalSource_xid: source.id, principalType_xid: type.id, ...data })
|
||||
},
|
||||
update: async (data) => await db.iam_principal.update(data, { where: { id: data.id } }),
|
||||
updateBiometric: async (data) => {
|
||||
const user = await db.iam_principal.findByPk(data.id)
|
||||
if (!user) throw new ApiError(400, "User Not found")
|
||||
return await db.transaction.execute(async (transactionOption) => {
|
||||
await db.iam_principal.update(data, { where: { id: data.id }, ...transactionOption })
|
||||
await db.iam_principal_biometric.findCreateFind({
|
||||
where: { deviceId: data.deviceId, principal_xid: data.id },
|
||||
defaults: { deviceId: data.deviceId, biometric_type: data.biometric_type, principal_xid: data.id },
|
||||
...transactionOption
|
||||
})
|
||||
})
|
||||
},
|
||||
registerWithEmail: async (data) => {
|
||||
return await db.transaction.execute(async (transactionOptions) => {
|
||||
await db.iam_principal.update(data, { where: { id: data.id }, ...transactionOptions })
|
||||
return await db.investor_details.create({ principal_xid: data.id, country_xid: data.country_xid }, transactionOptions)
|
||||
})
|
||||
},
|
||||
getById_lite: async (id) => await db.iam_principal.findByPk(id),
|
||||
getByID: async (id) => await db.iam_principal.findByPk(id),
|
||||
getByIdWithBiometric: async (id, deviceId) => await db.iam_principal.findByPk(id, { include: [{ association: "biometrics", where: { deviceId } }] }),
|
||||
getByNumberWithISDCode: async (mobileNumber, ISDcode) => await db.iam_principal.findOne({ where: { mobileNumber, ISDcode }, include: [{ association: "investor_details" }] }),
|
||||
togglesStatus: async (id) => {
|
||||
const user = await db.iam_principal.findByPk(id)
|
||||
if (!user) throw new ApiError(404, "not Found")
|
||||
user.IsAppNotificationEnabled = !user.IsAppNotificationEnabled
|
||||
user.save()
|
||||
return user
|
||||
},
|
||||
togglesNotificationStatus: async (principal_xid, deviceId, playerId, IsAppNotificationEnabled) =>
|
||||
await db.transaction.execute(async (transactionOptions) => {
|
||||
await db.iam_principal.update({ IsAppNotificationEnabled: IsAppNotificationEnabled }, { where: { id: principal_xid }, ...transactionOptions })
|
||||
const [instane, boolean] = await db.iam_principal_notification.findOrCreate({
|
||||
where: { playerId, deviceId, principal_xid }, defaults: {
|
||||
principal_xid, deviceId, playerId, IsAppNotificationEnabled,
|
||||
}, ...transactionOptions
|
||||
})
|
||||
if (!boolean) await db.iam_principal_notification.update({ IsAppNotificationEnabled }, { where: { id: instane.id }, ...transactionOptions })
|
||||
}),
|
||||
ResetPassword: async (hashedPassword, id) => db.iam_principal.update({ password_hash: hashedPassword }, { where: { id } }),
|
||||
getByEmail: async (email) => {
|
||||
return await db.iam_principal.findOne({
|
||||
where: { emailAddress: email },
|
||||
include: [{ association: "principal_type", where: { id: iamConstant.principalTypeXid.OPTIFII_ADMIN }, through: { attributes: [] } }]
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
32
src/services/IAM/iam_principal_notification.service.js
Normal file
32
src/services/IAM/iam_principal_notification.service.js
Normal file
@@ -0,0 +1,32 @@
|
||||
const { Op } = require("sequelize")
|
||||
const db = require("../../models")
|
||||
|
||||
module.exports = {
|
||||
getPrincipalPlayerId: async (principal_xid) => {
|
||||
const results = await db.iam_principal_notification.findAll({
|
||||
where: { principal_xid, IsAppNotificationEnabled: true },
|
||||
attributes: ['playerId']
|
||||
});
|
||||
|
||||
// Map the results to extract playerId directly into an array
|
||||
return results.map(result => result.playerId);
|
||||
},
|
||||
|
||||
getAllPlayerId: async () => {
|
||||
return await db.iam_principal_notification.findAll({
|
||||
where: { IsAppNotificationEnabled: true },
|
||||
attributes: ['playerId']
|
||||
},)
|
||||
},
|
||||
|
||||
getSpecificPlayerIds: async (principal_xids) => {
|
||||
return await db.iam_principal_notification.findAll({
|
||||
where: {
|
||||
principal_xid: { [Op.in]: principal_xids }, // No need to wrap `principal_xids` in an array
|
||||
IsAppNotificationEnabled: true
|
||||
},
|
||||
attributes: ['playerId']
|
||||
})
|
||||
// .then(result => result.map(record => record.playerId)); // Extracting playerId from each result
|
||||
}
|
||||
}
|
||||
8
src/services/access/tokenService.js
Normal file
8
src/services/access/tokenService.js
Normal file
@@ -0,0 +1,8 @@
|
||||
const db = require("../../models");
|
||||
|
||||
module.exports={
|
||||
get: async (refreshToken) => {
|
||||
return await db.token.findOne({
|
||||
where: { token: refreshToken, isblacklisted: false } });
|
||||
}
|
||||
}
|
||||
13
src/services/bcrypt/bcrypt.service.js
Normal file
13
src/services/bcrypt/bcrypt.service.js
Normal file
@@ -0,0 +1,13 @@
|
||||
const bcrypt = require("bcrypt");
|
||||
|
||||
const { hashSync, compareSync, genSaltSync } = bcrypt;
|
||||
|
||||
module.exports = {
|
||||
hash: (data) => {
|
||||
return hashSync(data, genSaltSync(10));
|
||||
},
|
||||
|
||||
compare: (data, hash) => {
|
||||
return compareSync(data, hash);
|
||||
},
|
||||
};
|
||||
45
src/services/code/code.service.js
Normal file
45
src/services/code/code.service.js
Normal file
@@ -0,0 +1,45 @@
|
||||
const crypto = require('crypto');
|
||||
const config = require('../../config/config');
|
||||
const ApiError = require('../../utils/handler/ApiError.handler');
|
||||
const algorithm = 'aes-256-cbc';
|
||||
const secretKey = crypto.createHash('sha256').update(config.code.secret).digest();
|
||||
|
||||
// Initialize blacklist if it's not already a Set
|
||||
const blacklist = config.code.blacklist || new Set();
|
||||
|
||||
module.exports = {
|
||||
encryptUserId: (userId) => {
|
||||
try {
|
||||
const userIdString = String(userId); // Ensure userId is a string
|
||||
const iv = crypto.randomBytes(16); // New IV for each encryption
|
||||
const cipher = crypto.createCipheriv(algorithm, secretKey, iv);
|
||||
let encrypted = cipher.update(userIdString, 'utf8', 'hex');
|
||||
encrypted += cipher.final('hex');
|
||||
return `${encrypted}-${iv.toString('hex')}`;
|
||||
} catch (error) {
|
||||
throw new ApiError(400, `Encryption failed: ${error.message}`);
|
||||
}
|
||||
},
|
||||
decryptUserId: (encryptedString) => {
|
||||
try {
|
||||
if (blacklist.has(encryptedString)) {
|
||||
throw new ApiError(400, 'The code is blacklisted');
|
||||
}
|
||||
|
||||
const [encryptedData, iv] = encryptedString.split('-');
|
||||
if (!encryptedData || !iv) {
|
||||
throw new ApiError(400, 'Invalid code');
|
||||
}
|
||||
|
||||
const decipher = crypto.createDecipheriv(algorithm, secretKey, Buffer.from(iv, 'hex'));
|
||||
let decrypted = decipher.update(encryptedData, 'hex', 'utf8');
|
||||
decrypted += decipher.final('utf8');
|
||||
return decrypted;
|
||||
} catch (error) {
|
||||
throw new ApiError(400, "Invalid Code", [`Decryption failed: ${error.message}`]);
|
||||
}
|
||||
},
|
||||
addToBlacklist: (encryptedString) => {
|
||||
blacklist.add(encryptedString);
|
||||
}
|
||||
};
|
||||
52
src/services/corporate/corporate.service.js
Normal file
52
src/services/corporate/corporate.service.js
Normal file
@@ -0,0 +1,52 @@
|
||||
const { Op } = require("sequelize");
|
||||
const db = require("../../models");
|
||||
const corporateConstant = require("../../utils/constant/corporate.constant");
|
||||
const iamConstant = require("../../utils/constant/iam.constant");
|
||||
|
||||
module.exports = {
|
||||
getCorporateExist: async (mobileNumber, ISDcode, emailAddress) => await db.corporate.findOne({ where: { mobileNumber, ISDcode, emailAddress } }),
|
||||
create: async (data) => await db.corporate.create(data),
|
||||
createWithOtherDetails: async (data) => await db.corporate.create(data, { include: [{ association: 'other_details' }] }),
|
||||
update: async (data) => await db.corporate.update(data, { where: { id: data.id } }),
|
||||
updateWithPricipalAndOtherDeatils: async (data) => await db.transaction.execute(async (transactionObject) => {
|
||||
const principal = await db.iam_principal.create(data.principal, transactionObject)
|
||||
await db.iam_principal_type_link.create({ principal_xid: principal.id, principalType_xid: data.principal.principalType_xid })
|
||||
await db.corporate.update(data, { where: { id: data.id }, ...transactionObject })
|
||||
await db.corporate_other_detail.create(data.other_detail, transactionObject);
|
||||
await db.corporate_director.bulkCreate(data.directors, transactionObject)
|
||||
return principal
|
||||
}),
|
||||
getCorporateListing: async (query) => {
|
||||
return await db.pagination({
|
||||
model: db.corporate,
|
||||
...query,
|
||||
where: { is_active: true },
|
||||
attributes: ['id', 'entity_name', 'corporate_name', 'emailAddress', 'corporate_code', 'onboarding_date', 'opted_for_expence', 'opted_for_benefit', 'opted_for_gifting'],
|
||||
associations: [
|
||||
{ association: "industry", attributes: ['id', 'industry_name'], required: false },
|
||||
{ association: "corporate_type", attributes: ['id', 'corporate_type'], required: false },
|
||||
{ association: "corporate_status", attributes: ['id', 'status'], required: false }
|
||||
],
|
||||
order: [["updatedAt", "ASC"]],
|
||||
searchableFields: Object.keys(db.corporate.getAttributes()),
|
||||
})
|
||||
},
|
||||
|
||||
getById: async (id) => await db.corporate.findByPk(id, {
|
||||
include: [
|
||||
{ association: 'other_details', required: false },
|
||||
{ association: 'director', required: false },
|
||||
{ association: "principal", required: false, attributes: ["userName", "firstName", "lastName", "emailAddress", "ISDcode", "mobileNumber"] }
|
||||
]
|
||||
}),
|
||||
getByID_lite: async (id) => await db.corporate.findByPk(id),
|
||||
checkIsSubmited: async (id) => await db.corporate.findOne({
|
||||
where: { id, corporate_status_xid: { [Op.eq]: corporateConstant.statusXid.SUBMITTED } },
|
||||
include: [{
|
||||
association: "principal",
|
||||
attributes: ["fullName"],
|
||||
required: false,
|
||||
include: [{ association: "principal_type", where: { id: iamConstant.principalTypeXid.CORPORATE_ADMIN }, through: { attributes: [] } }]
|
||||
}]
|
||||
})
|
||||
}
|
||||
62
src/services/crypto/crypto.service.js
Normal file
62
src/services/crypto/crypto.service.js
Normal file
@@ -0,0 +1,62 @@
|
||||
const crypto = require("crypto");
|
||||
const path = require("path");
|
||||
const fs = require('fs');
|
||||
|
||||
const publicKeyPem = fs.readFileSync(path.join(__dirname, '../../keys/public_key.pem'), 'utf8');
|
||||
const privateKeyPem = fs.readFileSync(path.join(__dirname, '../../keys/rsa_private_key.pem'), 'utf8');
|
||||
|
||||
module.exports = {
|
||||
encryptWithPublicKey: async (jsonData) => {
|
||||
const buffer = Buffer.from(JSON.stringify(jsonData));
|
||||
const encrypted = crypto.publicEncrypt({
|
||||
key: publicKeyPem,
|
||||
padding: crypto.constants.RSA_PKCS1_PADDING
|
||||
}, buffer);
|
||||
return encrypted.toString("base64");
|
||||
},
|
||||
|
||||
decryptWithPrivateKey: async (encryptedData) => {
|
||||
try {
|
||||
const buffer = Buffer.from(encryptedData, 'base64');
|
||||
const decrypted = crypto.privateDecrypt({
|
||||
key: privateKeyPem,
|
||||
padding: crypto.constants.RSA_PKCS1_PADDING
|
||||
}, buffer);
|
||||
const value = decrypted.toString('utf8');
|
||||
console.log(value)
|
||||
return JSON.parse(value);
|
||||
} catch (error) {
|
||||
console.error('Decryption Error:', error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
decryptKey: async (encryptedKey) => {
|
||||
const decodedKey = Buffer.from(encryptedKey, 'base64');
|
||||
const decryptedKey = crypto.privateDecrypt(
|
||||
{
|
||||
key: privateKeyPem,
|
||||
padding: crypto.constants.RSA_PKCS1_PADDING,
|
||||
},
|
||||
decodedKey
|
||||
);
|
||||
return decryptedKey;
|
||||
},
|
||||
|
||||
getIV: async (decodedData) => {
|
||||
return decodedData.slice(0, 16);
|
||||
},
|
||||
|
||||
base64Decode: async (data) => {
|
||||
return Buffer.from(data, 'base64');
|
||||
},
|
||||
|
||||
decryptData: async (decodedData, sessionKey, iv) => {
|
||||
const decipher = crypto.createDecipheriv('aes-128-cbc', sessionKey, iv);
|
||||
decipher.setAutoPadding(true);
|
||||
let decrypted = decipher.update(decodedData.slice(16));
|
||||
decrypted = Buffer.concat([decrypted, decipher.final()]);
|
||||
const value = decrypted.toString('utf8');
|
||||
return JSON.parse(value);
|
||||
}
|
||||
};
|
||||
31
src/services/emailTemplate/otpSenderTemplate.js
Normal file
31
src/services/emailTemplate/otpSenderTemplate.js
Normal file
@@ -0,0 +1,31 @@
|
||||
const { transporter } = require("../../config/nodemailerConfig");
|
||||
const config = require("../../config/config");
|
||||
const logger = require("../../config/logger");
|
||||
|
||||
module.exports = {
|
||||
loginOtp: async (to, otp) => {
|
||||
try {
|
||||
if (config.byPassEmail){
|
||||
logger.info(`Email to ${to} has been bypassed due to bypassEmail configuration.`);
|
||||
return true;
|
||||
}
|
||||
const emailOptions = {
|
||||
from: config.email.from,
|
||||
to: to,
|
||||
subject: "Login OTP",
|
||||
text: "Login OTP",
|
||||
html: `<body style="font-family: 'Open Sans', sans-serif; margin: 15px 0; padding: 0; width: 100% !important;">
|
||||
<div style="padding: 40px 50px 0px 50px; text-align: left;">
|
||||
<p style="color: #202020; font-weight: 400; font-size: 18px; line-height: 32px;">Hi, your OTP for login is -> ${otp},<br>
|
||||
Please do not share this otp with anyone.</p>
|
||||
</div>
|
||||
</body>`,
|
||||
};
|
||||
const result = await transporter.sendMail(emailOptions);
|
||||
return result;
|
||||
} catch (error) {
|
||||
logger.error("Error sending email:", error);
|
||||
throw new Error("MPIN reset email not sent: " + error.message);
|
||||
}
|
||||
},
|
||||
}
|
||||
65
src/services/emailTemplate/resetOtpTemplate.js
Normal file
65
src/services/emailTemplate/resetOtpTemplate.js
Normal file
@@ -0,0 +1,65 @@
|
||||
const { transporter } = require("../../config/nodemailerConfig");
|
||||
const config = require("../../config/config");
|
||||
const logger = require("../../config/logger");
|
||||
|
||||
module.exports = {
|
||||
resetOtp: async (to, otp) => {
|
||||
try {
|
||||
const emailOptions = {
|
||||
from: config.email.from,
|
||||
to: to,
|
||||
subject: "Reset Your OTP",
|
||||
text: `We received a request to reset your MPIN. Your One-Time Password (OTP) is: ${otp}. Please use this OTP to reset your MPIN. If you did not request this change, please contact us immediately at <a href="mailto:info@tanamicapital.com" target="_blank" style="color: #468071;">info@tanamicapital.com</a> or +973 3633 1331.`,
|
||||
html: `<style>
|
||||
.before::before {
|
||||
content: url(https://wordpress.betadelivery.com/wdipl2.0/wp-content/uploads/2024/07/Mask.png);
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
.after::before {
|
||||
content: url(https://wordpress.betadelivery.com/wdipl2.0/wp-content/uploads/2024/07/Mask.png);
|
||||
position: absolute;
|
||||
bottom: -266px;
|
||||
left: -13px;
|
||||
transform: rotate(8deg);
|
||||
}
|
||||
</style>
|
||||
<body style="font-family: 'Open Sans', sans-serif; margin: 0; padding: 0; width: 100% !important;">
|
||||
<!-- header -->
|
||||
<div style="margin: 0; padding: 0; width: 100%; background-color: #f9f6f3; display: flex; justify-content: center;">
|
||||
<div style="width: 100%; max-width: 600px; background-color: white;">
|
||||
<div class="before" style="position: relative; background-color: #002E0F; text-align: center; padding: 30px 50px;">
|
||||
<a target="_blank">
|
||||
<img src="https://wordpress.betadelivery.com/wdipl2.0/wp-content/uploads/2024/07/tanami-logo.png" alt="Tanami Logo" style="width: auto;">
|
||||
</a>
|
||||
</div>
|
||||
<div style="padding: 40px 50px 0px 50px; text-align: center;">
|
||||
<p style="color: #202020; font-weight: 700; font-size: 18px; line-height: 32px;">Reset Your MPIN.</p>
|
||||
<p style="font-size: 18px; line-height: 28px;">We received a request to reset your MPIN.</p>
|
||||
<p style="font-size: 18px; line-height: 28px;">Your One-Time Password (OTP) is: <strong>${otp}</strong>.</p>
|
||||
<p style="padding: 5px 0px; font-size: 18px; line-height: 28px;">
|
||||
Please use this OTP to reset your MPIN. If you did not request this change, please contact us immediately <br> at
|
||||
<a href="mailto:info@tanamicapital.com" target="_blank" style="color: #468071;">info@tanamicapital.com</a> or +973 3633 1331
|
||||
</p>
|
||||
</div>
|
||||
<div style="padding: 1px 0px 0px 50px;"><p style="padding: 0px 0px 40px 0px; font-size: 18px; line-height: 32px; margin: 0;">Thank You! <br> Tanami Team</p></div>
|
||||
<div class="after" style="overflow: hidden; position: relative; background-color: #002E0F; padding: 30px 30px; display: flex; justify-content: flex-start;">
|
||||
<a href="" style="text-decoration: none; color: #FFFFFF; margin: 0 10px; font-size: 18px;">Disclaimer</a>
|
||||
<span style="text-decoration: none; color: #FFFFFF; font-size: 18px;">|</span>
|
||||
<a href="" style="text-decoration: none; color: #FFFFFF; margin: 0 10px; font-size: 18px;">Terms & Conditions</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-C6RzsynM9kWDrMNeT87bh95OGNyZPhcTNXj1NW7RuBCsyN/o0jlpcV8Qyq46cDfL" crossorigin="anonymous"></script>
|
||||
</body>`,
|
||||
};
|
||||
const result = await transporter.sendMail(emailOptions);
|
||||
return result;
|
||||
} catch (error) {
|
||||
logger.error("Error sending email:", error);
|
||||
throw new Error("MPIN reset email not sent: " + error.message);
|
||||
}
|
||||
},
|
||||
}
|
||||
20
src/services/iam_principal_otp/iamPrincipalOTP.service.js
Normal file
20
src/services/iam_principal_otp/iamPrincipalOTP.service.js
Normal file
@@ -0,0 +1,20 @@
|
||||
const { Op } = require("sequelize");
|
||||
const db = require("../../models");
|
||||
const ApiError = require("../../utils/handler/ApiError.handler");
|
||||
|
||||
module.exports = {
|
||||
create: async (data) => {
|
||||
await db.iam_principal_otp.destroy({
|
||||
where: { principal_xid: data.principal_xid, isUsed: { [Op.eq]: false } }, force: true
|
||||
});
|
||||
return await db.iam_principal_otp.create(data);
|
||||
},
|
||||
getOtp: async (principal_xid) => {
|
||||
const data = await db.iam_principal_otp.findOne({ where: { principal_xid, isUsed: false }, });
|
||||
if (!data) { throw new ApiError(400, "Invalid OTP") }
|
||||
const validTillTimestamp = new Date(data.validTill).getTime();
|
||||
const currentTime = Date.now();
|
||||
if (currentTime > validTillTimestamp) { throw new ApiError(400, "OTP has expired") }
|
||||
return data
|
||||
}
|
||||
}
|
||||
13
src/services/index.js
Normal file
13
src/services/index.js
Normal file
@@ -0,0 +1,13 @@
|
||||
module.exports = {
|
||||
tokenService: require('./token/token.service'),
|
||||
iamPrincipalService: require('./IAM/iam_principal.service'),
|
||||
otpSenderTemplate: require('./emailTemplate/otpSenderTemplate'),
|
||||
bcryptService: require('./bcrypt/bcrypt.service'),
|
||||
codeService: require('./code/code.service'),
|
||||
iamPrincipalOTPService: require('./iam_principal_otp/iamPrincipalOTP.service'),
|
||||
resetOtpTemplate: require('./emailTemplate/resetOtpTemplate'),
|
||||
tokenNewService: require('./access/tokenService'),
|
||||
corporateService: require('./corporate/corporate.service'),
|
||||
industryService: require('./industry/industry.service'),
|
||||
cryptoService: require('./crypto/crypto.service'),
|
||||
}
|
||||
5
src/services/industry/industry.service.js
Normal file
5
src/services/industry/industry.service.js
Normal file
@@ -0,0 +1,5 @@
|
||||
const db = require("../../models");
|
||||
|
||||
module.exports = {
|
||||
getAll: async () => await db.industry.findAll(),
|
||||
};
|
||||
50
src/services/token/token.service.js
Normal file
50
src/services/token/token.service.js
Normal file
@@ -0,0 +1,50 @@
|
||||
const jwt = require('jsonwebtoken')
|
||||
const config = require("../../config/config");
|
||||
const moment = require('moment');
|
||||
const db = require('../../models');
|
||||
const { tokenTypes } = require('../../config/tokens');
|
||||
const generateToken = (userId, countryId, languageId, expires, type, secret = config.jwt.secret) => {
|
||||
const payload = { userId, countryId, languageId, iat: moment().unix(), exp: moment(expires).unix(), type, };
|
||||
return jwt.sign(payload, secret)
|
||||
}
|
||||
|
||||
const saveToken = async (token, userId, expires, type, isblacklisted = false) => {
|
||||
return await db.token.create({ token, userId, expires: moment(expires).toDate(), type, isblacklisted })
|
||||
}
|
||||
module.exports = {
|
||||
verifyToken: (token, type) => {
|
||||
return jwt.verify(token, config.jwt.secret)
|
||||
},
|
||||
generateAuthTokens: async (user) => {
|
||||
const { id, investor_details } = user
|
||||
const accessTokenExpires = moment().add(config.jwt.accessExpirationMinutes, 'minutes');
|
||||
const accessToken = generateToken(id, investor_details?.country_xid, investor_details?.defaultLanguage_xid, accessTokenExpires, tokenTypes.ACCESS);
|
||||
|
||||
const refreshTokenExpires = moment().add(config.jwt.refreshExpirationDays, 'days');
|
||||
const refreshToken = generateToken(id, investor_details?.country_xid, investor_details?.defaultLanguage_xid, null, refreshTokenExpires, tokenTypes.REFRESH);
|
||||
await saveToken(refreshToken, id, refreshTokenExpires, tokenTypes.REFRESH);
|
||||
|
||||
return {
|
||||
access: {
|
||||
token: accessToken,
|
||||
expires: moment(accessTokenExpires).toDate(),
|
||||
},
|
||||
refresh: {
|
||||
token: refreshToken,
|
||||
expires: moment(refreshTokenExpires).toDate(),
|
||||
},
|
||||
};
|
||||
},
|
||||
reGenerateAccessTokens: async (user) => {
|
||||
const { id, investor_details } = user
|
||||
const accessTokenExpires = moment().add(config.jwt.accessExpirationMinutes, 'minutes');
|
||||
const accessToken = generateToken(id, investor_details?.country_xid, investor_details?.defaultLanguage_xid, accessTokenExpires, tokenTypes.ACCESS);
|
||||
return {
|
||||
access: {
|
||||
token: accessToken,
|
||||
expires: moment(accessTokenExpires).toDate(),
|
||||
}
|
||||
};
|
||||
},
|
||||
generateToken,
|
||||
}
|
||||
26
src/utils/constant/corporate.constant.js
Normal file
26
src/utils/constant/corporate.constant.js
Normal file
@@ -0,0 +1,26 @@
|
||||
module.exports = {
|
||||
corporateType: {
|
||||
PRIVATE: "Private",
|
||||
PUBLIC: "Public",
|
||||
LLP: "LLP"
|
||||
},
|
||||
industryType: {
|
||||
TECHNOLOGY: "Technology",
|
||||
HEALTHCARE: "Healthcare",
|
||||
FINANCE: "Finance",
|
||||
SERVICES: "Services"
|
||||
},
|
||||
corporateStatus: {
|
||||
REQUESTED: "Requested",
|
||||
APPROVED: "Approved",
|
||||
REJECTED: "Rejected",
|
||||
SUBMITTED: "Submitted"
|
||||
},
|
||||
statusXid: {
|
||||
REQUESTED: 1,
|
||||
SUBMITTED: 2,
|
||||
REGISTERD: 3,
|
||||
APPROVED: 4,
|
||||
REJECTED: 5,
|
||||
}
|
||||
}
|
||||
14
src/utils/constant/iam.constant.js
Normal file
14
src/utils/constant/iam.constant.js
Normal file
@@ -0,0 +1,14 @@
|
||||
module.exports = {
|
||||
|
||||
otpPurpose : {
|
||||
PASSWORD_RESET : "Password reset",
|
||||
RESEND: "Resend",
|
||||
LOGIN: "Login"
|
||||
},
|
||||
|
||||
principalTypeXid: {
|
||||
OPTIFII_ADMIN: 1,
|
||||
CORPORATE_ADMIN: 2,
|
||||
USER: 3
|
||||
}
|
||||
}
|
||||
41
src/utils/handler/ApiError.handler.js
Normal file
41
src/utils/handler/ApiError.handler.js
Normal file
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* Custom error class for handling API errors. Extends the native Error class.
|
||||
* @class ApiError
|
||||
* @extends Error
|
||||
*
|
||||
* @param {number} statusCode - HTTP status code associated with the error.
|
||||
* @param {string} [message="Something went wrong"] - Error message (default is a generic message).
|
||||
* @param {Array} [errors=[]] - Array containing specific error details or additional information.
|
||||
* @param {boolean} [isOperational=true] - Indicates if the error is operational or not.
|
||||
* @param {string} [stack=""] - Stack trace for the error (optional, defaults to an empty string).
|
||||
*/
|
||||
class ApiError extends Error {
|
||||
constructor(
|
||||
statusCode,
|
||||
message = "Something went wrong",
|
||||
errors = [],
|
||||
isOperational = true,
|
||||
stack = ""
|
||||
) {
|
||||
// Call the constructor of the parent class (Error)
|
||||
super(message);
|
||||
|
||||
// Custom properties for API error handling
|
||||
this.statusCode = statusCode; // HTTP status code associated with the error.
|
||||
this.data = null; // Additional data associated with the error (initially set to null).
|
||||
this.message = message; // Error message for the API response.
|
||||
this.success = false; // Indicates the failure nature of the API request.
|
||||
this.errors = errors; // Specific error details or additional information.
|
||||
this.isOperational = isOperational; // Indicates if the error is operational or not.
|
||||
|
||||
// Set the stack trace if provided; otherwise, capture the current stack trace.
|
||||
if (stack) {
|
||||
this.stack = stack; // Provided stack trace.
|
||||
} else {
|
||||
Error.captureStackTrace(this, this.constructor); // Capture the current stack trace.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Export the ApiError class for use in other modules.
|
||||
module.exports = ApiError;
|
||||
19
src/utils/handler/ApiResponse.handler.js
Normal file
19
src/utils/handler/ApiResponse.handler.js
Normal file
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* Class representing a standard API response.
|
||||
* @class ApiResponse
|
||||
*
|
||||
* @param {number} statusCode - HTTP status code associated with the response.
|
||||
* @param {any} data - Data to be returned in the response.
|
||||
* @param {string} [message="Success"] - Response message (default is "Success").
|
||||
*/
|
||||
class ApiResponse {
|
||||
constructor(statusCode, data, message = "Success") {
|
||||
this.statusCode = statusCode; // HTTP status code associated with the response.
|
||||
this.data = data; // Data to be returned in the response.
|
||||
this.message = message; // Response message (default is "Success").
|
||||
this.success = statusCode < 400; // Indicates if the response is successful based on the status code.
|
||||
}
|
||||
}
|
||||
|
||||
// Export the ApiResponse class for use in other modules.
|
||||
module.exports = ApiResponse;
|
||||
11
src/utils/handler/Async.handler.js
Normal file
11
src/utils/handler/Async.handler.js
Normal file
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* Asynchronous error handler for Express routes.
|
||||
* @function AsyncHandler
|
||||
*
|
||||
* @param {Function} fn - Asynchronous function to be executed.
|
||||
* @returns {Function} Middleware function that handles errors for asynchronous routes.
|
||||
*/
|
||||
module.exports.AsyncHandler = (fn) => (req, res, next) => {
|
||||
// Wrap the asynchronous function in a promise and handle any errors by passing them to the next middleware.
|
||||
Promise.resolve(fn(req, res, next)).catch(next);
|
||||
};
|
||||
21
src/utils/handler/pick.handler.js
Normal file
21
src/utils/handler/pick.handler.js
Normal file
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* Create an object composed of the picked object properties.
|
||||
* @function pick
|
||||
*
|
||||
* @param {Object} object - The source object to pick properties from.
|
||||
* @param {string[]} keys - The array of property names to pick from the source object.
|
||||
* @returns {Object} New object with only the picked properties.
|
||||
*/
|
||||
const pick = (object, keys) => {
|
||||
return keys.reduce((obj, key) => {
|
||||
// Check if the object has the specified property
|
||||
if (object && Object.prototype.hasOwnProperty.call(object, key)) {
|
||||
obj[key] = object[key]; // Assign the property to the new object
|
||||
}
|
||||
return obj; // Return the new object
|
||||
}, {}); // Initialize the new object as an empty object
|
||||
};
|
||||
|
||||
// Export the pick function for use in other modules.
|
||||
module.exports = pick;
|
||||
|
||||
21
src/utils/helper/aesCrypto.helper.js
Normal file
21
src/utils/helper/aesCrypto.helper.js
Normal file
@@ -0,0 +1,21 @@
|
||||
const crypto = require('crypto');
|
||||
|
||||
// AES encryption function using crypto
|
||||
function aesEncrypt(trandata, key) {
|
||||
const iv = Buffer.from("PGKEYENCDECIVSPC"); // 16-byte IV for AES
|
||||
const cipher = crypto.createCipheriv('aes-256-cbc', Buffer.from(key), iv);
|
||||
let encrypted = cipher.update(trandata, 'utf8', 'hex');
|
||||
encrypted += cipher.final('hex');
|
||||
return encrypted;
|
||||
}
|
||||
|
||||
// AES decryption function using crypto
|
||||
function aesDecryption(encryptedHex, key) {
|
||||
const iv = Buffer.from("PGKEYENCDECIVSPC");
|
||||
const decipher = crypto.createDecipheriv('aes-256-cbc', Buffer.from(key), iv);
|
||||
let decrypted = decipher.update(encryptedHex, 'hex', 'utf8');
|
||||
decrypted += decipher.final('utf8');
|
||||
return decrypted;
|
||||
}
|
||||
|
||||
module.exports = { aesEncrypt, aesDecryption };
|
||||
30
src/utils/helper/emailValidator.helper.js
Normal file
30
src/utils/helper/emailValidator.helper.js
Normal file
@@ -0,0 +1,30 @@
|
||||
const dns = require("node:dns");
|
||||
// Async function to resolve MX records for a domain
|
||||
async function resolveMx(domain, recordType) {
|
||||
return new Promise((resolve, reject) => {
|
||||
dns.resolveMx(domain, (err, mxRecords) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
return;
|
||||
}
|
||||
const addresses = mxRecords.map((mxRecord) => mxRecord.exchange);
|
||||
resolve(addresses);
|
||||
});
|
||||
});
|
||||
}
|
||||
// Async function to check email address validity
|
||||
async function checkEmailValidity(email) {
|
||||
try {
|
||||
const domain = email?.split("@")[1];
|
||||
const addresses = await resolveMx(domain, "MX");
|
||||
|
||||
if (addresses && addresses?.length > 0) {
|
||||
return true;
|
||||
}
|
||||
return false; // No MX record exists
|
||||
} catch (err) {
|
||||
return false; // Error occurred
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = checkEmailValidity
|
||||
12
src/utils/helper/fileValidator.helper.js
Normal file
12
src/utils/helper/fileValidator.helper.js
Normal file
@@ -0,0 +1,12 @@
|
||||
function isValidFileSize(value) {
|
||||
// Check the file size here, e.g., to ensure it's not larger than a specific limit.
|
||||
// Adjust the maximum size as needed (in bytes).
|
||||
const maxSizeInBytes = 10 * 1024 * 1024; // 10 MB
|
||||
|
||||
if (value && value.size <= maxSizeInBytes) {
|
||||
return true; // Valid file size
|
||||
}
|
||||
|
||||
return false; // Invalid file size
|
||||
}
|
||||
module.exports = isValidFileSize
|
||||
36
src/utils/helper/genrateCodeName.helper.js
Normal file
36
src/utils/helper/genrateCodeName.helper.js
Normal file
@@ -0,0 +1,36 @@
|
||||
function generateCompanyCode(input, count) {
|
||||
let abbreviation;
|
||||
|
||||
// Split the input by spaces to check if it's a single word or multiple words
|
||||
const words = input.split(' ');
|
||||
|
||||
// If it's a single word and more than 5 characters
|
||||
if (words.length === 1 && input.length > 5) {
|
||||
// Use the first 4 letters of the single word
|
||||
abbreviation = input.slice(0, 4).toUpperCase(); // "Accenture" -> "ACCE"
|
||||
} else if (words.length > 1) {
|
||||
// Multiple words: take the first letter of each word
|
||||
abbreviation = words.map(word => word[0].toUpperCase()).join(''); // "J. P. Morgan" -> "JPM"
|
||||
} else {
|
||||
// For other cases (like short single words), use the input as uppercase
|
||||
abbreviation = input.toUpperCase();
|
||||
}
|
||||
|
||||
// Get the current year
|
||||
const currentYear = new Date().getFullYear();
|
||||
|
||||
// Increment the count: If the passed count is 0, set it to 1; otherwise, add 1 to the count
|
||||
const incrementedCount = (count === 0) ? 1 : count + 1;
|
||||
|
||||
// Format the incremented count as '0001' with leading zeros
|
||||
const countString = String(incrementedCount).padStart(4, '0'); // Assuming count is up to 9999
|
||||
|
||||
|
||||
// Combine abbreviation, year, and count to form the code
|
||||
const code = `${abbreviation}${currentYear}${countString}`;
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
|
||||
module.exports = generateCompanyCode
|
||||
131
src/utils/helper/notificationMsg.helper.js
Normal file
131
src/utils/helper/notificationMsg.helper.js
Normal file
@@ -0,0 +1,131 @@
|
||||
const { sendNotification } = require("../../services/push_notification/onesignal.service");
|
||||
|
||||
// Function to get deposit received message
|
||||
const getDepositReceivedMessage = amount => ({
|
||||
en: `Funds Added to Your Wallet: A deposit of $${amount} has been credited to your account.`,
|
||||
ar: `تمت إضافة أموال إلى محفظتك: تمت إضافة إيداع بقيمة ${amount} دولار إلى حسابك.`
|
||||
});
|
||||
|
||||
// Function to get educational material published message
|
||||
const getEducationalMaterialPublishedMessage = () => ({
|
||||
en: 'Learn and Grow! Expand your knowledge with new educational material in our Academy.',
|
||||
ar: 'تعلم ونم! وسع معرفتك من خلال المواد التعليمية الجديدة في أكاديميتنا.'
|
||||
});
|
||||
|
||||
// Function to get investment opportunity cancelled message
|
||||
const investmentOpportunityCancelled = investmentName => ({
|
||||
en: `Investment Opportunity Cancelled: ${investmentName} has been cancelled.`,
|
||||
ar: `تم إلغاء فرصة الاستثمار: ${investmentName} قد تم إلغاؤه.`
|
||||
});
|
||||
|
||||
// Function to get investor qualification upgrade message
|
||||
const investorQualificationUpgrade = () => ({
|
||||
en: 'Congratulations, Investor! Your Investor Qualification has been upgraded. Enjoy enhanced benefits!',
|
||||
ar: 'تهانينا، مستثمر! تم ترقية مؤهلاتك كمستثمر. استمتع بالمزايا المعززة!'
|
||||
});
|
||||
|
||||
// Function to get KYC status approved message
|
||||
const kycStatusApproved = () => ({
|
||||
en: 'KYC Approved: Great news! Your KYC verification is successful.',
|
||||
ar: 'تمت الموافقة على KYC: أخبار رائعة! تم التحقق من KYC بنجاح.'
|
||||
});
|
||||
|
||||
// Function to get KYC status declined message
|
||||
const kycStatusDeclined = () => ({
|
||||
en: 'KYC Update: Your KYC status requires attention. Please pass the verification again.',
|
||||
ar: 'تحديث KYC: حالة KYC الخاصة بك تتطلب انتباهاً. يرجى إعادة إجراء التحقق.'
|
||||
});
|
||||
|
||||
// Function to get new investment opportunity message
|
||||
const newInvestmentOpportunity = () => ({
|
||||
en: 'Discover New Opportunities! A fresh investment opportunity awaits you. Dive in now!',
|
||||
ar: 'اكتشف الفرص الجديدة! تنتظرك فرصة استثمارية جديدة. ابدأ الآن!'
|
||||
});
|
||||
|
||||
// Function to get subscription investment closed message
|
||||
const subscriptionInvestmentClosed = () => ({
|
||||
en: 'Investment Update: Your investment opportunity has just closed.',
|
||||
ar: 'تحديث الاستثمار: لقد تم إغلاق فرصة استثمارك للتو.'
|
||||
});
|
||||
|
||||
// Function to get subscription investment updated message
|
||||
const subscriptionInvestmentUpdated = () => ({
|
||||
en: 'Investment Status Alert: Your investment subscription details have been updated.',
|
||||
ar: 'تنبيه حالة الاستثمار: تمت تحديث تفاصيل اشتراكك في الاستثمار.'
|
||||
});
|
||||
|
||||
// Function to get withdrawal completed message
|
||||
const withdrawalCompleted = () => ({
|
||||
en: 'Withdrawal Complete: Your withdrawal is processed. Check your bank for funds!',
|
||||
ar: 'اكتمل السحب: تمت معالجة سحبك. تحقق من بنكك للحصول على الأموال!'
|
||||
});
|
||||
|
||||
// Function to send new investment opportunity notification
|
||||
const sendNewInvestmentOpportunityNotification = async (investmentName, manualDate, manualTime, expectedReturn, playerIds = [], imageUrl = '', additionalData = {}) => {
|
||||
const title = 'New investment opportunity';
|
||||
const message = `${investmentName} launching on ${manualDate} at ${manualTime} with an expected return of ${expectedReturn}%. Tap for more details.`;
|
||||
return await sendNotification(title, message, playerIds, imageUrl, additionalData);
|
||||
};
|
||||
|
||||
// Function to send investment open notification
|
||||
const sendInvestmentOpenNotification = async (investmentName, playerIds = [], imageUrl = '', additionalData = {}) => {
|
||||
const title = `${investmentName} is LIVE`;
|
||||
const message = `${investmentName} is now open for investment! Tap for more details.`;
|
||||
return await sendNotification(title, message, playerIds, imageUrl, additionalData);
|
||||
};
|
||||
|
||||
// Function to send investment fully subscribed notification
|
||||
const sendInvestmentFullySubscribedNotification = async (investmentName, playerIds = [], imageUrl = '', additionalData = {}) => {
|
||||
const title = `${investmentName} fully subscribed`;
|
||||
const message = `${investmentName} has been fully subscribed and is now closed to new investors.`;
|
||||
return await sendNotification(title, message, playerIds, imageUrl, additionalData);
|
||||
};
|
||||
|
||||
// Function to send deposit received notification
|
||||
const sendDepositReceivedNotification = async (playerIds = [], imageUrl = '', additionalData = {}) => {
|
||||
const title = 'Deposit received';
|
||||
const message = 'A new deposit has been made into your Tanami wallet. Explore exclusive investment opportunities only at Tanami.';
|
||||
return await sendNotification(title, message, playerIds, imageUrl, additionalData);
|
||||
};
|
||||
|
||||
// Function to send distribution notice notification
|
||||
const sendDistributionNoticeNotification = async (investmentName, playerIds = [], imageUrl = '', additionalData = {}) => {
|
||||
const title = 'Distribution notice';
|
||||
const message = `New distribution received regarding your investment in ${investmentName}. Tap for more details.`;
|
||||
return await sendNotification(title, message, playerIds, imageUrl, additionalData);
|
||||
};
|
||||
|
||||
// Function to send KYC approved notification
|
||||
const sendKYCApprovedNotification = async (playerIds = [], imageUrl = '', additionalData = {}) => {
|
||||
const title = 'You\'re verified!';
|
||||
const message = 'KYC approved - You\'re all set to start investing! Tap to explore the latest exclusive opportunities available.';
|
||||
return await sendNotification(title, message, playerIds, imageUrl, additionalData);
|
||||
};
|
||||
|
||||
// Function to send investor status upgrade notification
|
||||
const sendInvestorStatusUpgradeNotification = async (playerIds = [], imageUrl = '', additionalData = {}) => {
|
||||
const title = 'You\'ve been upgraded!';
|
||||
const message = 'Congrats! You can now enjoy investing with no limits! Tap to explore the latest exclusive opportunities available.';
|
||||
return await sendNotification(title, message, playerIds, imageUrl, additionalData);
|
||||
};
|
||||
|
||||
// Exporting functions
|
||||
module.exports = {
|
||||
getDepositReceivedMessage,
|
||||
getEducationalMaterialPublishedMessage,
|
||||
investmentOpportunityCancelled,
|
||||
investorQualificationUpgrade,
|
||||
kycStatusApproved,
|
||||
kycStatusDeclined,
|
||||
newInvestmentOpportunity,
|
||||
subscriptionInvestmentClosed,
|
||||
subscriptionInvestmentUpdated,
|
||||
withdrawalCompleted,
|
||||
sendNewInvestmentOpportunityNotification,
|
||||
sendInvestmentOpenNotification,
|
||||
sendInvestmentFullySubscribedNotification,
|
||||
sendDepositReceivedNotification,
|
||||
sendDistributionNoticeNotification,
|
||||
sendKYCApprovedNotification,
|
||||
sendInvestorStatusUpgradeNotification
|
||||
};
|
||||
16
src/utils/helper/otpGenrator.helper.js
Normal file
16
src/utils/helper/otpGenrator.helper.js
Normal file
@@ -0,0 +1,16 @@
|
||||
const config = require("../../config/config");
|
||||
|
||||
const generateOTP = () => {
|
||||
var digits = "0123456789";
|
||||
let OTP = "";
|
||||
if (config.byPassOTP) {
|
||||
OTP = "123456"
|
||||
} else {
|
||||
for (let i = 0; i < 6; i++) {
|
||||
OTP += digits[Math.floor(Math.random() * 10)];
|
||||
}
|
||||
}
|
||||
return OTP;
|
||||
}
|
||||
module.exports = generateOTP
|
||||
|
||||
42
src/utils/helper/transactionNumberGenrator.helper.js
Normal file
42
src/utils/helper/transactionNumberGenrator.helper.js
Normal file
@@ -0,0 +1,42 @@
|
||||
const moment = require('moment');
|
||||
|
||||
class TransactionNumberGenerator {
|
||||
constructor() {
|
||||
this.timestampMap = new Map();
|
||||
this.lock = { isLocked: false };
|
||||
}
|
||||
|
||||
_acquireLock() {
|
||||
while (this.lock.isLocked) { }
|
||||
this.lock.isLocked = true;
|
||||
}
|
||||
|
||||
_releaseLock() {
|
||||
this.lock.isLocked = false;
|
||||
}
|
||||
|
||||
// Convert date and time to Unix timestamp
|
||||
_getUnixTimestamp(dateTime) {
|
||||
return moment(dateTime, 'YYYY-MM-DD HH:mm:ss').unix();
|
||||
}
|
||||
|
||||
generateUniqueTimestamp(dateTime) {
|
||||
this._acquireLock();
|
||||
|
||||
const timestamp = this._getUnixTimestamp(dateTime);
|
||||
if (!this.timestampMap.has(timestamp)) {
|
||||
this.timestampMap.set(timestamp, 0);
|
||||
}
|
||||
let counter = this.timestampMap.get(timestamp) + 1;
|
||||
this.timestampMap.set(timestamp, counter);
|
||||
|
||||
this._releaseLock();
|
||||
|
||||
// Create unique number with timestamp and counter
|
||||
const uniqueNumber = ((timestamp % 10000000000) * 1000 + counter).toString().padStart(11, '0');
|
||||
|
||||
return uniqueNumber;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = TransactionNumberGenerator
|
||||
15
src/validation/auth/admin.schema.js
Normal file
15
src/validation/auth/admin.schema.js
Normal file
@@ -0,0 +1,15 @@
|
||||
const yup = require('yup');
|
||||
|
||||
module.exports={
|
||||
body: yup.object().shape({
|
||||
emailAddress: yup
|
||||
.string()
|
||||
.email('Invalid email format') // Ensure the email address is in a valid format
|
||||
.required('Email address is required'), // Validation message for required field
|
||||
|
||||
password_hash: yup
|
||||
.string()
|
||||
.required('Password hash is required') // Validation message for required field
|
||||
})
|
||||
|
||||
}
|
||||
17
src/validation/auth/biometric.schema.js
Normal file
17
src/validation/auth/biometric.schema.js
Normal file
@@ -0,0 +1,17 @@
|
||||
const yup = require('yup');
|
||||
|
||||
module.exports = {
|
||||
body: yup.object().shape({
|
||||
is_2FA_on: yup
|
||||
.boolean()
|
||||
.required('2FA status is required'),
|
||||
deviceId: yup
|
||||
.string()
|
||||
// .matches(/^QKQ1\.\d{6}\.\d{3}$/, 'Invalid device ID format')
|
||||
.required('Device ID is required'),
|
||||
biometric_type: yup
|
||||
.string()
|
||||
.oneOf(['fingerprint', 'face'], 'Biometric type must be one of: fingerprint, face')
|
||||
.notRequired(),
|
||||
}),
|
||||
};
|
||||
10
src/validation/auth/forgetPassword.schema.js
Normal file
10
src/validation/auth/forgetPassword.schema.js
Normal file
@@ -0,0 +1,10 @@
|
||||
const yup = require("yup");
|
||||
|
||||
module.exports = {
|
||||
body: yup.object().shape({
|
||||
emailAddress: yup
|
||||
.string()
|
||||
.email("Invalid email format") // Ensure the email address is in a valid format
|
||||
.required("Email address is required"), // Validation message for required field
|
||||
}),
|
||||
};
|
||||
10
src/validation/auth/index.js
Normal file
10
src/validation/auth/index.js
Normal file
@@ -0,0 +1,10 @@
|
||||
module.exports = {
|
||||
otpVerification: require('./otpVerification.schema'),
|
||||
registration: require('./registration.schema'),
|
||||
login: require('./login.schema'),
|
||||
biometric: require('./biometric.schema'),
|
||||
updatePassword: require('./updatePassword.schema'),
|
||||
resetPassword: require('./resetPaasword.schema'),
|
||||
adminLogin: require('./admin.schema'),
|
||||
forgetPassword: require('./forgetPassword.schema')
|
||||
}
|
||||
16
src/validation/auth/login.schema.js
Normal file
16
src/validation/auth/login.schema.js
Normal file
@@ -0,0 +1,16 @@
|
||||
const yup = require('yup');
|
||||
module.exports = {
|
||||
body: yup.object().shape({
|
||||
mobileNumber: yup
|
||||
.string()
|
||||
.required('Mobile number is required')
|
||||
.matches(/^[0-9]+$/, 'Mobile number must be digits only')
|
||||
.min(8, 'Mobile number must be at least 8 digits')
|
||||
.max(10, 'Mobile number must be at most 10 digits'),
|
||||
ISDcode: yup
|
||||
.string()
|
||||
.required('ISD code is required')
|
||||
.matches(/^\+\d{1,3}$/, 'ISD code must be in the format + followed by 1 to 3 digits'),
|
||||
password: yup.string().required("Password is required")
|
||||
}),
|
||||
}
|
||||
10
src/validation/auth/otpVerification.schema.js
Normal file
10
src/validation/auth/otpVerification.schema.js
Normal file
@@ -0,0 +1,10 @@
|
||||
const yup = require('yup');
|
||||
module.exports = {
|
||||
body: yup.object().shape({
|
||||
otp: yup
|
||||
.string()
|
||||
.required('OTP is required')
|
||||
.matches(/^[0-9]{6}$/, 'OTP must be exactly 6 digits'),
|
||||
|
||||
}),
|
||||
}
|
||||
41
src/validation/auth/registration.schema.js
Normal file
41
src/validation/auth/registration.schema.js
Normal file
@@ -0,0 +1,41 @@
|
||||
const yup = require('yup');
|
||||
const checkEmailValidity = require('../../utils/helper/emailValidator.helper');
|
||||
module.exports = {
|
||||
body: yup.object().shape({
|
||||
firstName: yup
|
||||
.string()
|
||||
.required('First name is required')
|
||||
.min(2, 'First name must be at least 2 characters long')
|
||||
.max(35, 'First name must be at most 50 characters long'),
|
||||
lastName: yup
|
||||
.string()
|
||||
.required('Last name is required')
|
||||
.min(2, 'Last name must be at least 2 characters long')
|
||||
.max(35, 'Last name must be at most 50 characters long'),
|
||||
emailAddress: yup
|
||||
.string()
|
||||
.email("Invalid email address")
|
||||
.required("Email address is required")
|
||||
.min(6, "Email address must be at least 6 characters long")
|
||||
.max(255, "Email address can be at most 255 characters long")
|
||||
.test("emailValidity", "Email address is invalid", async function (value) {
|
||||
if (!value) {
|
||||
return true; // Allow if the field is empty
|
||||
}
|
||||
return await checkEmailValidity(value);
|
||||
}),
|
||||
password: yup
|
||||
.string()
|
||||
.required('Password is required')
|
||||
.min(8, 'Password must be at least 8 characters long')
|
||||
.max(16, 'Password must be at most 50 characters long')
|
||||
.matches(/[A-Z]/, 'Password must contain at least one uppercase letter')
|
||||
.matches(/[a-z]/, 'Password must contain at least one lowercase letter')
|
||||
.matches(/[0-9]/, 'Password must contain at least one number')
|
||||
.matches(/[@$!%*?&#]/, 'Password must contain at least one special character'),
|
||||
confirmPassword: yup
|
||||
.string()
|
||||
.oneOf([yup.ref('password'), null], 'Passwords must match')
|
||||
.required('Confirm password is required'),
|
||||
}),
|
||||
}
|
||||
27
src/validation/auth/resetPaasword.schema.js
Normal file
27
src/validation/auth/resetPaasword.schema.js
Normal file
@@ -0,0 +1,27 @@
|
||||
|
||||
const yup = require('yup');
|
||||
module.exports = {
|
||||
body: yup.object().shape({
|
||||
password: yup
|
||||
.string()
|
||||
.required('Password is required')
|
||||
.min(8, 'Password must be at least 8 characters long')
|
||||
.max(16, 'Password must be at most 50 characters long')
|
||||
.matches(/[A-Z]/, 'Password must contain at least one uppercase letter')
|
||||
.matches(/[a-z]/, 'Password must contain at least one lowercase letter')
|
||||
.matches(/[0-9]/, 'Password must contain at least one number')
|
||||
.matches(/[@$!%*?&#]/, 'Password must contain at least one special character'),
|
||||
confirmPassword: yup
|
||||
.string()
|
||||
.oneOf([yup.ref('password'), null], 'Passwords must match')
|
||||
.required('Confirm new password is required'),
|
||||
code: yup
|
||||
.string()
|
||||
// .matches(/^[A-Za-z0-9-]{36,64}$/, 'Code must be between 36 and 64 alphanumeric characters or hyphens')
|
||||
.required('Code is required'),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
22
src/validation/auth/updatePassword.schema.js
Normal file
22
src/validation/auth/updatePassword.schema.js
Normal file
@@ -0,0 +1,22 @@
|
||||
const yup = require('yup');
|
||||
module.exports = {
|
||||
body: yup.object().shape({
|
||||
oldPassword: yup
|
||||
.string()
|
||||
.required('Old password is required'),
|
||||
newPassword: yup
|
||||
.string()
|
||||
.required('Password is required')
|
||||
.min(8, 'Password must be at least 8 characters long')
|
||||
.max(16, 'Password must be at most 50 characters long')
|
||||
.matches(/[A-Z]/, 'Password must contain at least one uppercase letter')
|
||||
.matches(/[a-z]/, 'Password must contain at least one lowercase letter')
|
||||
.matches(/[0-9]/, 'Password must contain at least one number')
|
||||
.matches(/[@$!%*?&#]/, 'Password must contain at least one special character'),
|
||||
confirmNewPassword: yup
|
||||
.string()
|
||||
.oneOf([yup.ref('newPassword'), null], 'Passwords must match')
|
||||
.required('Confirm new password is required')
|
||||
|
||||
}),
|
||||
}
|
||||
9
src/validation/common/code.schema.js
Normal file
9
src/validation/common/code.schema.js
Normal file
@@ -0,0 +1,9 @@
|
||||
const yup = require('yup');
|
||||
module.exports = {
|
||||
body: yup.object().shape({
|
||||
code: yup
|
||||
.string()
|
||||
// .matches(/^[A-Za-z0-9-]{36,64}$/, 'Code must be between 36 and 64 alphanumeric characters or hyphens')
|
||||
.required('Code is required'),
|
||||
}),
|
||||
}
|
||||
11
src/validation/common/id.schema.js
Normal file
11
src/validation/common/id.schema.js
Normal file
@@ -0,0 +1,11 @@
|
||||
const yup = require('yup');
|
||||
module.exports = {
|
||||
params: yup.object().shape({
|
||||
id: yup
|
||||
.number()
|
||||
.nullable()
|
||||
.typeError("ID must be a number")
|
||||
.positive("ID must be a positive number")
|
||||
.required('ID is required')
|
||||
}),
|
||||
}
|
||||
5
src/validation/common/index.js
Normal file
5
src/validation/common/index.js
Normal file
@@ -0,0 +1,5 @@
|
||||
module.exports = {
|
||||
search: require('./search.schema'),
|
||||
id: require('./id.schema'),
|
||||
code: require('./code.schema')
|
||||
}
|
||||
8
src/validation/common/search.schema.js
Normal file
8
src/validation/common/search.schema.js
Normal file
@@ -0,0 +1,8 @@
|
||||
const yup = require('yup');
|
||||
module.exports = {
|
||||
query: yup.object().shape({
|
||||
page: yup.number().integer().min(1).optional().default(1),
|
||||
size: yup.number().integer().min(1).optional().default(10),
|
||||
search: yup.string().max(100).optional(),
|
||||
}),
|
||||
}
|
||||
38
src/validation/corporate/corporateQuickAdd.schema.js
Normal file
38
src/validation/corporate/corporateQuickAdd.schema.js
Normal file
@@ -0,0 +1,38 @@
|
||||
const yup = require('yup');
|
||||
const { email } = require('../../config/config');
|
||||
const checkEmailValidity = require('../../utils/helper/emailValidator.helper');
|
||||
|
||||
module.exports = {
|
||||
body: yup.object().shape({
|
||||
entity_name: yup
|
||||
.string()
|
||||
.required('Entity name is required')
|
||||
.max(100, 'Entity name should not be greater than 100 characters'),
|
||||
corporate_name: yup
|
||||
.string()
|
||||
.required('Company name is required.')
|
||||
.max(100, 'Corporate name should not be greater than 100 characters'),
|
||||
emailAddress: yup
|
||||
.string()
|
||||
.email("Invalid email address")
|
||||
.required("Email address is required")
|
||||
.min(6, "Email address must be at least 6 characters long")
|
||||
.max(255, "Email address can be at most 255 characters long")
|
||||
.test("emailValidity", "Email address is invalid", async function (value) {
|
||||
if (!value) {
|
||||
return true; // Allow if the field is empty
|
||||
}
|
||||
return await checkEmailValidity(value);
|
||||
}),
|
||||
mobileNumber: yup
|
||||
.string()
|
||||
.required('Mobile number is required')
|
||||
// .length(10, 'Mobile number must be exactly 10 digits')
|
||||
.matches(/^[0-9]+$/, 'Mobile number must be digits only')
|
||||
.max(15, 'Mobile number corporate must be at most 15 digits'),
|
||||
ISDcode: yup
|
||||
.string()
|
||||
.required('ISD code is required')
|
||||
.matches(/^\+\d{1,3}$/, 'ISD code must be in the format + followed by 1 to 3 digits'),
|
||||
}),
|
||||
}
|
||||
99
src/validation/corporate/createCorporate.schema.js
Normal file
99
src/validation/corporate/createCorporate.schema.js
Normal file
@@ -0,0 +1,99 @@
|
||||
const yup = require('yup');
|
||||
const checkEmailValidity = require('../../utils/helper/emailValidator.helper');
|
||||
module.exports = {
|
||||
body: yup.object().shape({
|
||||
full_name_principal: yup
|
||||
.string()
|
||||
.required('Full name is required')
|
||||
.max(100, 'Full name must be at most 100 characters long'),
|
||||
emailAddress_principal: yup
|
||||
.string()
|
||||
.email("Invalid email principal address")
|
||||
.required("Email address is required")
|
||||
.max(100, "Email address can be at most 100 characters long")
|
||||
.test("emailValidity", "Email address pricnipal is invalid", async function (value) {
|
||||
if (!value) {
|
||||
return true; // Allow if the field is empty
|
||||
}
|
||||
return await checkEmailValidity(value);
|
||||
}),
|
||||
ISDCode_principal: yup
|
||||
.string()
|
||||
.required('ISD code is required')
|
||||
.matches(/^\+\d{1,3}$/, 'ISD code must be in the format + followed by 1 to 3 digits'),
|
||||
mobileNumber_principal: yup
|
||||
.string()
|
||||
.required('Mobile number is required')
|
||||
.matches(/^[0-9]+$/, 'Mobile number must be digits only')
|
||||
.max(15, 'Mobile number must be at most 15 digits'),
|
||||
corporate_name: yup
|
||||
.string()
|
||||
.required('Corporate name is required')
|
||||
.max(100, 'Corporate name must be at most 100 characters long'),
|
||||
industry_xid: yup
|
||||
.number()
|
||||
.nullable()
|
||||
.typeError("Industry ID must be a number")
|
||||
.positive("Industry ID must be a positive number")
|
||||
.required('Industry ID is required'),
|
||||
ISDcode_corporate: yup
|
||||
.string()
|
||||
.required('ISD code corporate is required')
|
||||
.matches(/^\+\d{1,3}$/, 'ISD code corporate must be in the format + followed by 1 to 3 digits'),
|
||||
mobileNumber_corporate: yup
|
||||
.string()
|
||||
.required('Mobile number corporate is required')
|
||||
.matches(/^[0-9]+$/, 'Mobile number corporate must be digits only')
|
||||
.max(15, 'Mobile number corporate must be at most 15 digits'),
|
||||
logo_path_file_name: yup
|
||||
.string()
|
||||
.nullable()
|
||||
.notRequired(),
|
||||
cin_number: yup
|
||||
.string()
|
||||
.max(20, "CIN number must be at most 20 digits"),
|
||||
pancard_number: yup
|
||||
.string()
|
||||
.max(20, "CIN number must be at most 20 digits"),
|
||||
gst_number: yup
|
||||
.string()
|
||||
.max(20, "CIN number must be at most 20 digits"),
|
||||
gst_file_path_name: yup
|
||||
.string()
|
||||
.nullable()
|
||||
.notRequired(),
|
||||
pancard_file_path_name: yup
|
||||
.string()
|
||||
.nullable()
|
||||
.notRequired(),
|
||||
opted_for_expence: yup.boolean().default(false).required('Expence is required'),
|
||||
opted_for_gifting: yup.boolean().default(false).required('Gifting is required'),
|
||||
opted_for_benefit: yup.boolean().default(false).required('Benefit is required'),
|
||||
directors: yup.array().of(
|
||||
yup.object().shape({
|
||||
director_name: yup
|
||||
.string()
|
||||
.required('User name is required')
|
||||
.max(100, 'User name must be at most 100 characters long'),
|
||||
emailAddress: yup
|
||||
.string()
|
||||
.email("Invalid email director address")
|
||||
.required("Email address is required")
|
||||
.max(100, "Email address can be at most 100 characters long"),
|
||||
ISDcode: yup
|
||||
.string()
|
||||
.required('ISD code corporate is required')
|
||||
.matches(/^\+\d{1,3}$/, 'ISD code corporate must be in the format + followed by 1 to 3 digits'),
|
||||
mobileNumber: yup
|
||||
.string()
|
||||
.required('Mobile number corporate is required')
|
||||
.matches(/^[0-9]+$/, 'Mobile number corporate must be digits only')
|
||||
.max(15, 'Mobile number corporate must be at most 15 digits'),
|
||||
pancard_file_path_name: yup
|
||||
.string()
|
||||
.notRequired(),
|
||||
}).optional()
|
||||
)
|
||||
|
||||
}),
|
||||
}
|
||||
4
src/validation/corporate/index.js
Normal file
4
src/validation/corporate/index.js
Normal file
@@ -0,0 +1,4 @@
|
||||
module.exports = {
|
||||
corporateQuickAdd: require('./corporateQuickAdd.schema'),
|
||||
registerWithCode: require('./createCorporate.schema')
|
||||
}
|
||||
10
src/validation/fileUpload/all.schema.js
Normal file
10
src/validation/fileUpload/all.schema.js
Normal file
@@ -0,0 +1,10 @@
|
||||
const yup = require('yup');
|
||||
const isValidFileSize = require('../../utils/helper/fileValidator.helper');
|
||||
|
||||
module.exports = {
|
||||
file: yup.mixed().test('isFile', 'File size exceeds limit', (value) => {
|
||||
if (!value) return true; // No attachment provided is allowed.
|
||||
return isValidFileSize(value);
|
||||
}).required("Document is equired"),
|
||||
|
||||
}
|
||||
3
src/validation/fileUpload/index.js
Normal file
3
src/validation/fileUpload/index.js
Normal file
@@ -0,0 +1,3 @@
|
||||
module.exports = {
|
||||
allSchema: require('./all.schema')
|
||||
}
|
||||
3
src/validation/icici/index.js
Normal file
3
src/validation/icici/index.js
Normal file
@@ -0,0 +1,3 @@
|
||||
module.exports = {
|
||||
registrationSchema: require('./registration.schema')
|
||||
}
|
||||
99
src/validation/icici/registration.schema.js
Normal file
99
src/validation/icici/registration.schema.js
Normal file
@@ -0,0 +1,99 @@
|
||||
const yup = require('yup');
|
||||
const checkEmailValidity = require('../../utils/helper/emailValidator.helper');
|
||||
module.exports = {
|
||||
body: yup.object().shape({
|
||||
AGGRNAME: yup
|
||||
.string()
|
||||
.required('Full name is required')
|
||||
.max(100, 'Full name must be at most 100 characters long'),
|
||||
emailAddress_principal: yup
|
||||
.string()
|
||||
.email("Invalid email principal address")
|
||||
.required("Email address is required")
|
||||
.max(100, "Email address can be at most 100 characters long")
|
||||
.test("emailValidity", "Email address pricnipal is invalid", async function (value) {
|
||||
if (!value) {
|
||||
return true; // Allow if the field is empty
|
||||
}
|
||||
return await checkEmailValidity(value);
|
||||
}),
|
||||
ISDCode_principal: yup
|
||||
.string()
|
||||
.required('ISD code is required')
|
||||
.matches(/^\+\d{1,3}$/, 'ISD code must be in the format + followed by 1 to 3 digits'),
|
||||
mobileNumber_principal: yup
|
||||
.string()
|
||||
.required('Mobile number is required')
|
||||
.matches(/^[0-9]+$/, 'Mobile number must be digits only')
|
||||
.max(15, 'Mobile number must be at most 15 digits'),
|
||||
corporate_name: yup
|
||||
.string()
|
||||
.required('Corporate name is required')
|
||||
.max(100, 'Corporate name must be at most 100 characters long'),
|
||||
industry_xid: yup
|
||||
.number()
|
||||
.nullable()
|
||||
.typeError("Industry ID must be a number")
|
||||
.positive("Industry ID must be a positive number")
|
||||
.required('Industry ID is required'),
|
||||
ISDcode_corporate: yup
|
||||
.string()
|
||||
.required('ISD code corporate is required')
|
||||
.matches(/^\+\d{1,3}$/, 'ISD code corporate must be in the format + followed by 1 to 3 digits'),
|
||||
mobileNumber_corporate: yup
|
||||
.string()
|
||||
.required('Mobile number corporate is required')
|
||||
.matches(/^[0-9]+$/, 'Mobile number corporate must be digits only')
|
||||
.max(15, 'Mobile number corporate must be at most 15 digits'),
|
||||
logo_path_file_name: yup
|
||||
.string()
|
||||
.nullable()
|
||||
.notRequired(),
|
||||
cin_number: yup
|
||||
.string()
|
||||
.max(20, "CIN number must be at most 20 digits"),
|
||||
pancard_number: yup
|
||||
.string()
|
||||
.max(20, "CIN number must be at most 20 digits"),
|
||||
gst_number: yup
|
||||
.string()
|
||||
.max(20, "CIN number must be at most 20 digits"),
|
||||
gst_file_path_name: yup
|
||||
.string()
|
||||
.nullable()
|
||||
.notRequired(),
|
||||
pancard_file_path_name: yup
|
||||
.string()
|
||||
.nullable()
|
||||
.notRequired(),
|
||||
opted_for_expence: yup.boolean().default(false).required('Expence is required'),
|
||||
opted_for_gifting: yup.boolean().default(false).required('Gifting is required'),
|
||||
opted_for_benefit: yup.boolean().default(false).required('Benefit is required'),
|
||||
directors: yup.array().of(
|
||||
yup.object().shape({
|
||||
director_name: yup
|
||||
.string()
|
||||
.required('User name is required')
|
||||
.max(100, 'User name must be at most 100 characters long'),
|
||||
emailAddress: yup
|
||||
.string()
|
||||
.email("Invalid email director address")
|
||||
.required("Email address is required")
|
||||
.max(100, "Email address can be at most 100 characters long"),
|
||||
ISDcode: yup
|
||||
.string()
|
||||
.required('ISD code corporate is required')
|
||||
.matches(/^\+\d{1,3}$/, 'ISD code corporate must be in the format + followed by 1 to 3 digits'),
|
||||
mobileNumber: yup
|
||||
.string()
|
||||
.required('Mobile number corporate is required')
|
||||
.matches(/^[0-9]+$/, 'Mobile number corporate must be digits only')
|
||||
.max(15, 'Mobile number corporate must be at most 15 digits'),
|
||||
pancard_file_path_name: yup
|
||||
.string()
|
||||
.notRequired(),
|
||||
}).optional()
|
||||
)
|
||||
|
||||
}),
|
||||
}
|
||||
3
src/validation/index.js
Normal file
3
src/validation/index.js
Normal file
@@ -0,0 +1,3 @@
|
||||
module.exports = {
|
||||
iciciBankAPIVaildation: require('./icici')
|
||||
}
|
||||
Reference in New Issue
Block a user