High-severity issues fixed
This commit is contained in:
parent
e635c9787a
commit
3a6c3c6c8f
@ -2,6 +2,7 @@ const db = require("../models");
|
||||
const bcrypt = require("bcryptjs");
|
||||
const jwt = require("jsonwebtoken");
|
||||
require("dotenv").config();
|
||||
const sanitize = require("sanitize-html");
|
||||
|
||||
const User = db.user;
|
||||
const EstablishmentUser = db.EstablishmentUser;
|
||||
@ -115,10 +116,18 @@ exports.logout = (req, res) => {
|
||||
});
|
||||
};
|
||||
|
||||
const sanitizeStringValue = (value) =>
|
||||
typeof value === "string"
|
||||
? sanitize(value, { allowedTags: [], allowedAttributes: {} })
|
||||
: value;
|
||||
|
||||
// Register new user
|
||||
exports.register = async (req, res) => {
|
||||
try {
|
||||
const { name, email, password } = req.body;
|
||||
const name = sanitizeStringValue(req.body.name);
|
||||
const email = sanitizeStringValue(req.body.email);
|
||||
const password = req.body.password;
|
||||
|
||||
if (!name || !email || !password)
|
||||
return res.status(400).send({
|
||||
status: "error",
|
||||
@ -141,8 +150,7 @@ exports.register = async (req, res) => {
|
||||
return res.status(201).send({
|
||||
status: "ok",
|
||||
code: "REGISTERED",
|
||||
message: "User registered successfully",
|
||||
data: newUser
|
||||
message: "User registered successfully"
|
||||
});
|
||||
|
||||
} catch (err) {
|
||||
|
||||
@ -19,6 +19,12 @@ const path = require("path");
|
||||
const { version } = require("os");
|
||||
const sequelize = db.sequelize;
|
||||
const { sanitizeForLog } = require("../utils/sanitize");
|
||||
const sanitize = require("sanitize-html");
|
||||
|
||||
const sanitizeStringValue = (value) =>
|
||||
typeof value === "string"
|
||||
? sanitize(value, { allowedTags: [], allowedAttributes: {} })
|
||||
: value;
|
||||
|
||||
exports.testEmail = async (req, res) => {
|
||||
placeHolderData = {
|
||||
@ -137,39 +143,39 @@ exports.createEstablishment = async (req, res) => {
|
||||
|
||||
// Create establishment record
|
||||
const establishment = await Establishment.create({
|
||||
establishment_code,
|
||||
factory_name,
|
||||
permanent_factory_code: permanent_factory_code || null,
|
||||
industry_code: industry_code || null,
|
||||
industry_code_production: industry_code_production || null,
|
||||
industry_code_mismatch_remarks,
|
||||
license_number,
|
||||
isic_code,
|
||||
description,
|
||||
establishment_address,
|
||||
establishment_code: sanitizeStringValue(establishment_code),
|
||||
factory_name: sanitizeStringValue(factory_name),
|
||||
permanent_factory_code: sanitizeStringValue(permanent_factory_code),
|
||||
industry_code: sanitizeStringValue(industry_code),
|
||||
industry_code_production: sanitizeStringValue(industry_code_production),
|
||||
industry_code_mismatch_remarks: sanitizeStringValue(industry_code_mismatch_remarks),
|
||||
license_number: sanitizeStringValue(license_number),
|
||||
isic_code: sanitizeStringValue(isic_code),
|
||||
description: sanitizeStringValue(description),
|
||||
establishment_address: sanitizeStringValue(establishment_address),
|
||||
establishment_city_town_id,
|
||||
establishment_emirate_id,
|
||||
establishment_postal_code,
|
||||
establishment_po_box,
|
||||
establishment_makani_number,
|
||||
establishment_contact_person_name,
|
||||
establishment_contact_person_designation,
|
||||
establishment_mobile_number,
|
||||
establishment_contact_email,
|
||||
establishment_website,
|
||||
establishment_postal_code: sanitizeStringValue(establishment_postal_code),
|
||||
establishment_po_box: sanitizeStringValue(establishment_po_box),
|
||||
establishment_makani_number: sanitizeStringValue(establishment_makani_number),
|
||||
establishment_contact_person_name: sanitizeStringValue(establishment_contact_person_name),
|
||||
establishment_contact_person_designation: sanitizeStringValue(establishment_contact_person_designation),
|
||||
establishment_mobile_number: sanitizeStringValue(establishment_mobile_number),
|
||||
establishment_contact_email: sanitizeStringValue(establishment_contact_email),
|
||||
establishment_website: sanitizeStringValue(establishment_website),
|
||||
corporate_same_as_establishment,
|
||||
corporate_name,
|
||||
corporate_address,
|
||||
corporate_name: sanitizeStringValue(corporate_name),
|
||||
corporate_address: sanitizeStringValue(corporate_address),
|
||||
corporate_city_town_id,
|
||||
corporate_emirate_id,
|
||||
corporate_postal_code,
|
||||
corporate_po_box,
|
||||
corporate_makani_number,
|
||||
corporate_contact_person_name,
|
||||
corporate_contact_person_designation,
|
||||
corporate_mobile_number,
|
||||
corporate_email,
|
||||
corporate_website,
|
||||
corporate_postal_code: sanitizeStringValue(corporate_postal_code),
|
||||
corporate_po_box: sanitizeStringValue(corporate_po_box),
|
||||
corporate_makani_number: sanitizeStringValue(corporate_makani_number),
|
||||
corporate_contact_person_name: sanitizeStringValue(corporate_contact_person_name),
|
||||
corporate_contact_person_designation: sanitizeStringValue(corporate_contact_person_designation),
|
||||
corporate_mobile_number: sanitizeStringValue(corporate_mobile_number),
|
||||
corporate_email: sanitizeStringValue(corporate_email),
|
||||
corporate_website: sanitizeStringValue(corporate_website),
|
||||
emirati_male,
|
||||
emirati_female,
|
||||
non_emirati_male,
|
||||
@ -179,7 +185,6 @@ exports.createEstablishment = async (req, res) => {
|
||||
created_by: req.body.created_by || req.user.id,
|
||||
created_at: new Date(),
|
||||
});
|
||||
|
||||
// Hash password
|
||||
const hashedPassword = await bcrypt.hash(establishment_user.password, 10);
|
||||
|
||||
@ -194,46 +199,15 @@ exports.createEstablishment = async (req, res) => {
|
||||
|
||||
//send email to user
|
||||
placeHolderData = {
|
||||
contact_name : sanitizeForLog(establishment_user.name),
|
||||
contact_name : sanitizeStringValue(establishment_user.name),
|
||||
portal_url : process.env.FE_BASE_URL,
|
||||
username : sanitizeForLog(establishment_user.email),
|
||||
username : sanitizeStringValue(establishment_user.email),
|
||||
password : establishment_user.password,
|
||||
support_email : process.env.SUPPORT_EMAIL,
|
||||
support_phone : process.env.SUPPORT_PHONE,
|
||||
|
||||
}
|
||||
await sendEmailService(sanitizeForLog(establishment_user.email), 'establishment_user_creation_to_user', placeHolderData);
|
||||
|
||||
// insert establishment_products
|
||||
// if (Array.isArray(establishment_products) && establishment_products.length > 0) {
|
||||
|
||||
// // remove duplicates from request itself
|
||||
// let uniqueProducts = [...new Set(establishment_products.map(x => x.product_id))];
|
||||
// // now check which already exist for this establishment
|
||||
// const existing = await EstablishmentProduct.findAll({
|
||||
// where: {
|
||||
// establishment_id: establishment.id,
|
||||
// product_id: uniqueProducts
|
||||
// },
|
||||
// attributes: ['product_id']
|
||||
// });
|
||||
|
||||
// const existingIds = existing.map(x => x.product_id);
|
||||
|
||||
// // filter only new ones (not existing)
|
||||
// const newProducts = uniqueProducts
|
||||
// .filter(pid => !existingIds.includes(pid))
|
||||
// .map(pid => ({
|
||||
// establishment_id: establishment.id,
|
||||
// product_id: pid,
|
||||
// created_at: new Date(),
|
||||
// }));
|
||||
|
||||
// // only insert if have new ones
|
||||
// if (newProducts.length) {
|
||||
// await EstablishmentProduct.bulkCreate(newProducts);
|
||||
// }
|
||||
// }
|
||||
await sendEmailService(sanitizeStringValue(establishment_user.email), 'establishment_user_creation_to_user', placeHolderData);
|
||||
|
||||
// insert establishment_products
|
||||
if (Array.isArray(establishment_products) && establishment_products.length > 0)
|
||||
@ -273,14 +247,15 @@ exports.createEstablishment = async (req, res) => {
|
||||
await EstablishmentProduct.bulkCreate(insertData);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//Return success response
|
||||
return res.status(201).send({
|
||||
status: "success",
|
||||
message: "Establishment and linked user created successfully",
|
||||
data: {
|
||||
establishment,
|
||||
data: {
|
||||
establishment:{
|
||||
id:establishment.id,
|
||||
establishment_code: establishment.establishment_code,
|
||||
},
|
||||
user: {
|
||||
id: user.id,
|
||||
name: user.name,
|
||||
@ -290,18 +265,14 @@ exports.createEstablishment = async (req, res) => {
|
||||
});
|
||||
|
||||
} catch (err) {
|
||||
|
||||
if (err.name === "SequelizeUniqueConstraintError") {
|
||||
|
||||
// extract exact field
|
||||
const field = err.errors[0].path; // <-- this gives the column name
|
||||
const field = err.errors[0].path;
|
||||
return res.status(400).json({
|
||||
status: "failed",
|
||||
message: `${field} already exists`
|
||||
});
|
||||
}
|
||||
|
||||
return res.status(500).send({status: "failed",message: err.message || "Internal server error",});
|
||||
return res.status(500).send({status: "failed",message: err.message,});
|
||||
}
|
||||
};
|
||||
|
||||
@ -807,7 +778,29 @@ exports.getAllEmirates = async (req, res) => {
|
||||
|
||||
exports.getAllCityTowns = async (req, res) => {
|
||||
try {
|
||||
const { emirate_id } = req.query;
|
||||
let { emirate_id } = req.query;
|
||||
|
||||
if (emirate_id) {
|
||||
|
||||
emirate_id = Number(emirate_id);
|
||||
if (isNaN(emirate_id)) {
|
||||
return res.status(400).send({
|
||||
status: "failed",
|
||||
message: "Invalid emirate_id"
|
||||
});
|
||||
}
|
||||
|
||||
const emirateExists = await Emirate.findOne({
|
||||
where: { id: emirate_id }
|
||||
});
|
||||
|
||||
if (!emirateExists) {
|
||||
return res.status(404).send({
|
||||
status: "failed",
|
||||
message: "Emirate not found"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const whereClause = {};
|
||||
if (emirate_id) {
|
||||
@ -819,8 +812,13 @@ exports.getAllCityTowns = async (req, res) => {
|
||||
attributes: ["id", "name", "emirate_id"],
|
||||
order: [["name", "ASC"]],
|
||||
});
|
||||
const sanitizedCities = cities.map(city => ({
|
||||
id: city.id,
|
||||
name: sanitizeStringValue(city.name),
|
||||
emirate_id: city.emirate_id,
|
||||
}));
|
||||
|
||||
return res.status(200).send({status: "success", message: "City/Town fetched successfully", data: cities, });
|
||||
return res.status(200).send({status: "success", message: "City/Town fetched successfully", data: sanitizedCities, });
|
||||
|
||||
} catch (err) {
|
||||
return res.status(500).send({ status: "failed", message: err.message || "Internal server error", });
|
||||
|
||||
@ -8,16 +8,69 @@ const UnitMaster = db.UnitMaster;
|
||||
exports.createEstablishmentProduct = async (req, res) => {
|
||||
try {
|
||||
|
||||
const { establishment_id , product_id, action_done_by , created_by } = req.body;
|
||||
let { establishment_id , product_id, action_done_by , created_by } = req.body;
|
||||
|
||||
if (establishment_id === undefined) {
|
||||
return res.status(400).json({
|
||||
status: "failed",
|
||||
message: "establishment_id is required"
|
||||
});
|
||||
}
|
||||
|
||||
establishment_id = Number(establishment_id);
|
||||
if (!Number.isInteger(establishment_id) || establishment_id <= 0) {
|
||||
return res.status(400).json({
|
||||
status: "failed",
|
||||
message: "Invalid establishment_id"
|
||||
});
|
||||
}
|
||||
if (product_id === undefined) {
|
||||
return res.status(400).json({
|
||||
status: "failed",
|
||||
message: "product_id is required"
|
||||
});
|
||||
}
|
||||
product_id = Number(product_id);
|
||||
if (!Number.isInteger(product_id) || (product_id <= 0 && product_id > 10)) {
|
||||
return res.status(400).json({
|
||||
status: "failed",
|
||||
message: "Invalid product_id"
|
||||
});
|
||||
}
|
||||
if (action_done_by === undefined) {
|
||||
return res.status(400).json({
|
||||
status: "failed",
|
||||
message: "action_done_by is required"
|
||||
});
|
||||
}
|
||||
if (typeof action_done_by !== "string" || action_done_by.trim().length === 0) {
|
||||
return res.status(400).json({
|
||||
status: "failed",
|
||||
message: "Invalid action_done_by"
|
||||
});
|
||||
}
|
||||
action_done_by = action_done_by.trim();
|
||||
|
||||
const existingMapping = await EstablishmentProduct.findOne({
|
||||
where: {
|
||||
establishment_id,
|
||||
product_id
|
||||
}
|
||||
});
|
||||
|
||||
if (existingMapping) {
|
||||
return res.status(409).json({
|
||||
status: "failed",
|
||||
message: "This product is already mapped to the establishment"
|
||||
});
|
||||
}
|
||||
const data = await EstablishmentProduct.create({
|
||||
establishment_id,
|
||||
product_id,
|
||||
action_done_by,
|
||||
created_by: created_by || req.user?.id || null,
|
||||
});
|
||||
|
||||
res.status(201).send({'status':"success",'message':"Creation successful",'data': data });
|
||||
res.status(201).send({'status':"success",'message':"Establishment Product Created successfully."});
|
||||
|
||||
} catch (err) {
|
||||
res.status(500).send({'status':"failed",'message':err.message });
|
||||
@ -28,8 +81,16 @@ exports.getAllEstablishmentProducts = async (req, res) => {
|
||||
try {
|
||||
|
||||
const whereClause = {};
|
||||
if (req.query.establishment_id) {
|
||||
whereClause.establishment_id = req.query.establishment_id;
|
||||
if (typeof req.query.establishment_id !== "undefined") {
|
||||
const establishmentId = parseInt(req.query.establishment_id, 10);
|
||||
|
||||
if (!Number.isInteger(establishmentId) || establishmentId <= 0) {
|
||||
return res.status(400).json({
|
||||
status: "failed",
|
||||
message: "Invalid establishment_id"
|
||||
});
|
||||
}
|
||||
whereClause.establishment_id = establishmentId;
|
||||
}
|
||||
|
||||
const data = await EstablishmentProduct.findAll({
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
const db = require("../models");
|
||||
const bcrypt = require("bcryptjs");
|
||||
const EstablishmentUser = db.EstablishmentUser;
|
||||
const Establishment = db.Establishment;
|
||||
|
||||
const { sendEmailService } = require("../services/email.service");
|
||||
const { sanitizeForLog } = require("../utils/sanitize");
|
||||
@ -8,64 +9,110 @@ const { sanitizeForLog } = require("../utils/sanitize");
|
||||
// Create User
|
||||
exports.createUser = async (req, res) => {
|
||||
try {
|
||||
|
||||
const { establishment_id, name, email, password , gender} = req.body;
|
||||
const hashed = await bcrypt.hash(password, 10);
|
||||
|
||||
email = sanitizeForLog(email || "");
|
||||
name = sanitizeForLog(name || "");
|
||||
gender = sanitizeForLog(gender || "");
|
||||
|
||||
if (!establishment_id || !email) {
|
||||
return res.status(400).send({
|
||||
let { establishment_id, name, email, password, gender } = req.body;
|
||||
establishment_id = Number(establishment_id);
|
||||
if (!Number.isInteger(establishment_id) || establishment_id <= 0) {
|
||||
return res.status(400).json({
|
||||
status: "error",
|
||||
code: "MISSING_FIELDS",
|
||||
message: "establishment_id and email are required"
|
||||
message: "Invalid establishment_id"
|
||||
});
|
||||
}
|
||||
|
||||
const existing = await EstablishmentUser.findOne({ where: { email, is_active: true } });
|
||||
if (existing) {
|
||||
return res.status(400).send({
|
||||
const establishment = await Establishment.findByPk(establishment_id);
|
||||
if (!establishment) {
|
||||
return res.status(404).json({
|
||||
status: "error",
|
||||
message: "Establishment not found"
|
||||
});
|
||||
}
|
||||
if (typeof email !== "string" || email.trim().length === 0) {
|
||||
return res.status(400).json({
|
||||
status: "error",
|
||||
message: "Email is required"
|
||||
});
|
||||
}
|
||||
email = email.trim().toLowerCase();
|
||||
if (typeof password !== "string" || password.length < 6) {
|
||||
return res.status(400).json({
|
||||
status: "error",
|
||||
message: "Password must be at least 6 characters long"
|
||||
});
|
||||
}
|
||||
name = typeof name === "string" ? name.trim() : null;
|
||||
|
||||
if (gender) {
|
||||
gender = gender.toString().toLowerCase();
|
||||
if (!["male", "female", "other"].includes(gender)) {
|
||||
return res.status(400).json({
|
||||
status: "error",
|
||||
message: "Invalid gender"
|
||||
});
|
||||
}
|
||||
gender = gender.charAt(0).toUpperCase() + gender.slice(1);
|
||||
}
|
||||
const existingUser = await EstablishmentUser.findOne({
|
||||
where: { email, is_active: true }
|
||||
});
|
||||
|
||||
if (existingUser) {
|
||||
return res.status(409).json({
|
||||
status: "error",
|
||||
code: "EMAIL_EXISTS",
|
||||
message: "Email already exists"
|
||||
});
|
||||
}
|
||||
const hashedPassword = await bcrypt.hash(password, 10);
|
||||
|
||||
const user = await EstablishmentUser.create({
|
||||
establishment_id,
|
||||
name,
|
||||
email,
|
||||
password: hashed,
|
||||
password: hashedPassword,
|
||||
gender,
|
||||
created_by: req.user.id,
|
||||
created_by: req.user.id
|
||||
});
|
||||
|
||||
//send email to user
|
||||
placeHolderData = {
|
||||
contact_name : name,
|
||||
portal_url : process.env.FE_BASE_URL,
|
||||
username : email,
|
||||
password : password,
|
||||
support_email : process.env.SUPPORT_EMAIL,
|
||||
support_phone : process.env.SUPPORT_PHONE,
|
||||
|
||||
}
|
||||
await sendEmailService(email, 'establishment_user_creation_to_user', placeHolderData);
|
||||
await sendEmailService(email, "establishment_user_creation_to_user", {
|
||||
contact_name: name,
|
||||
portal_url: process.env.FE_BASE_URL,
|
||||
username: email,
|
||||
password,
|
||||
support_email: process.env.SUPPORT_EMAIL,
|
||||
support_phone: process.env.SUPPORT_PHONE
|
||||
});
|
||||
|
||||
return res.status(201).send({'status':"ok", 'message':"Creation successful",'data': user });
|
||||
|
||||
} catch (err) {
|
||||
return res.status(500).send({'status':"error",'message':err.message });
|
||||
return res.status(201).json({
|
||||
status: "success",
|
||||
message: "Establishment User created successfully",
|
||||
data: {
|
||||
name: user.name,
|
||||
email: user.email,
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
return res.status(500).json({
|
||||
status: "error",
|
||||
message: error.message
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// Get all users
|
||||
exports.getAllUsers = async (req, res) => {
|
||||
try {
|
||||
try {
|
||||
let users;
|
||||
if (typeof req.query.establishment_id !== 'undefined') {
|
||||
const establishmentId = parseInt(req.query.establishment_id, 10);
|
||||
if (Number.isNaN(establishmentId)) {
|
||||
return res.status(400).json({
|
||||
status: "failed",
|
||||
message: "Invalid establishment_id"
|
||||
});
|
||||
}
|
||||
users = await EstablishmentUser.findAll({ where:{ establishment_id : establishmentId} });
|
||||
}else{
|
||||
users = await EstablishmentUser.findAll();
|
||||
}
|
||||
|
||||
const users = await EstablishmentUser.findAll({ where:{ establishment_id : req.query.establishment_id} });
|
||||
return res.status(200).send({'status':"success",'message':"Fetched successfully",'data': users });
|
||||
|
||||
} catch (err) {
|
||||
|
||||
@ -1,12 +1,19 @@
|
||||
const bcrypt = require("bcryptjs");
|
||||
const jwt = require("jsonwebtoken");
|
||||
const db = require("../models");
|
||||
const sanitize = require("sanitize-html");
|
||||
const EstablishmentUser = db.EstablishmentUser;
|
||||
|
||||
const sanitizeStringValue = (value) =>
|
||||
typeof value === "string"
|
||||
? sanitize(value, { allowedTags: [], allowedAttributes: {} })
|
||||
: value;
|
||||
|
||||
// User Login
|
||||
exports.login = async (req, res) => {
|
||||
try {
|
||||
const { email, password } = req.body;
|
||||
const email = sanitizeStringValue(req.body.email);
|
||||
const password = req.body.password;
|
||||
if (!email || !password)
|
||||
return res.status(400).send({'status':"failed",'message':"Email and password required",'data': ""});
|
||||
|
||||
@ -16,11 +23,17 @@ exports.login = async (req, res) => {
|
||||
const validPassword = await bcrypt.compare(password, user.password);
|
||||
if (!validPassword)
|
||||
return res.status(401).json({ message: "Invalid password" });
|
||||
|
||||
// Create JWT token
|
||||
const token = jwt.sign({ id: user.id, email: user.email , name: user.name, establishment_id: user.establishment_id }, process.env.JWT_SECRET, {
|
||||
expiresIn: "6h",
|
||||
});
|
||||
const token = jwt.sign(
|
||||
{
|
||||
id: user.id,
|
||||
email: sanitizeStringValue(user.email),
|
||||
name: sanitizeStringValue(user.name),
|
||||
establishment_id: user.establishment_id
|
||||
},
|
||||
process.env.JWT_SECRET,
|
||||
{ expiresIn: "6h" }
|
||||
);
|
||||
|
||||
res.status(200).send({'status':"success",'message':"Login successful",'data': token });
|
||||
|
||||
|
||||
@ -5,12 +5,20 @@ const db = require("../models");
|
||||
const { Sequelize } = require("sequelize");
|
||||
const Product = db.Product;
|
||||
const UnitMaster = db.UnitMaster;
|
||||
const sanitize = require("sanitize-html");
|
||||
|
||||
|
||||
const cleanString = (value) =>
|
||||
typeof value === "string"
|
||||
? sanitize(value, { allowedTags: [], allowedAttributes: {} })
|
||||
: value;
|
||||
|
||||
exports.createProduct = async (req, res) => {
|
||||
try {
|
||||
const { product_name, hs_code, hs_description } = req.body;
|
||||
const product_name = cleanString(req.body.product_name);
|
||||
const hs_description = cleanString(req.body.hs_description);
|
||||
const hs_code = req.body.hs_code;
|
||||
const unit_id = req.body.unit_id;
|
||||
|
||||
if (hs_description && hs_description.length > 1000) {
|
||||
return res.status(400).json({
|
||||
status: "failed",
|
||||
@ -51,9 +59,16 @@ exports.createProduct = async (req, res) => {
|
||||
message: `HS Code '${hs_code}' already exists.`
|
||||
});
|
||||
}
|
||||
req.body.created_by = req.user.id;
|
||||
const data = await Product.create(req.body);
|
||||
res.status(201).send({'status':"success",'message':"created successfully",'data': data });
|
||||
|
||||
await Product.create({
|
||||
product_name,
|
||||
hs_code,
|
||||
hs_description,
|
||||
unit_id,
|
||||
created_by: req.user.id
|
||||
});
|
||||
|
||||
res.status(201).send({'status':"success",'message':"created successfully." });
|
||||
} catch (error) {
|
||||
res.status(500).send({'status':"failed",'message':error.message });
|
||||
}
|
||||
|
||||
@ -5,6 +5,7 @@ const QuarterlyWindowsConfiguration = db.QuarterlyWindowsConfiguration;
|
||||
const Establishment = db.Establishment;
|
||||
const EstablishmentUser = db.EstablishmentUser;
|
||||
const { sendEmailService } = require("../services/email.service");
|
||||
const sanitize = require("sanitize-html");
|
||||
|
||||
// Create new configuration
|
||||
|
||||
@ -23,13 +24,31 @@ exports.createConfig = async (req, res) => {
|
||||
// get active establishment count
|
||||
const assignedCount = await Establishment.count({
|
||||
where: { is_active: true }
|
||||
});
|
||||
|
||||
// set auto values before inserting
|
||||
req.body.assigned = assignedCount;
|
||||
req.body.responded = 0;
|
||||
req.body.not_responded = assignedCount;
|
||||
req.body.created_by = req.body.created_by || req.user.id;
|
||||
});
|
||||
|
||||
function sanitizeValue(value) {
|
||||
if (typeof value === "string") {
|
||||
return sanitize(value, {
|
||||
allowedTags: [],
|
||||
allowedAttributes: {},
|
||||
});
|
||||
}
|
||||
if (Array.isArray(value)) {
|
||||
return value.map(sanitizeValue);
|
||||
}}
|
||||
|
||||
const payload = {
|
||||
survey_name: sanitizeValue(req.body.survey_name),
|
||||
quarter: sanitizeValue(req.body.quarter),
|
||||
year: req.body.year,
|
||||
start_date: req.body.start_date,
|
||||
end_date: req.body.end_date,
|
||||
grace_periods_days: req.body.grace_periods_days,
|
||||
assigned: assignedCount,
|
||||
responded: 0,
|
||||
not_responded: assignedCount,
|
||||
created_by: req.user.id,
|
||||
};
|
||||
|
||||
const placeholder = {
|
||||
survey_name: survey_name,
|
||||
@ -42,11 +61,10 @@ exports.createConfig = async (req, res) => {
|
||||
support_phone: process.env.SUPPORT_PHONE
|
||||
};
|
||||
|
||||
const data = await QuarterlyWindowsConfiguration.create(req.body);
|
||||
const data = await QuarterlyWindowsConfiguration.create(payload);
|
||||
res.status(201).send({
|
||||
status: "success",
|
||||
message: "Quarterly window configuration created successfully",
|
||||
data,
|
||||
message: "Quarterly window configuration created successfully"
|
||||
});
|
||||
sendEmailsToEstablishments(placeholder);
|
||||
} catch (err) {
|
||||
|
||||
@ -5,11 +5,26 @@ const { UnitMaster } = require("../models");
|
||||
const fs = require("fs");
|
||||
const csv = require("csv-parser");
|
||||
const path = require("path");
|
||||
const sanitize = require("sanitize-html");
|
||||
|
||||
const sanitizeStringValue = (value) =>
|
||||
typeof value === "string"
|
||||
? sanitize(value, { allowedTags: [], allowedAttributes: {} }).trim()
|
||||
: value;
|
||||
|
||||
// Create new Unit
|
||||
exports.createUnit = async (req, res) => {
|
||||
try {
|
||||
const { uom } = req.body;
|
||||
let {
|
||||
uom,
|
||||
description,
|
||||
is_base_unit,
|
||||
base_unit_id,
|
||||
factor
|
||||
} = req.body;
|
||||
|
||||
uom = sanitizeStringValue(uom);
|
||||
description = sanitizeStringValue(description);
|
||||
factor = sanitizeStringValue(factor);
|
||||
|
||||
if (!uom || typeof uom !== "string" || !uom.trim()) {
|
||||
return res.status(400).json({
|
||||
@ -17,27 +32,47 @@ exports.createUnit = async (req, res) => {
|
||||
message: "UOM (Unit Name) is required and must be a non-empty string.",
|
||||
});
|
||||
}
|
||||
const cleaned = uom.replace(/[^a-zA-Z]/g, "").toUpperCase();
|
||||
if (!description) {
|
||||
return res.status(400).json({
|
||||
status: "error",
|
||||
message: "Description is required and must be a non-empty string.",
|
||||
});
|
||||
}
|
||||
const cleaned = uom.replace(/[^A-Za-z]/g, "").toUpperCase();
|
||||
|
||||
if (!cleaned) {
|
||||
return res.status(400).json({
|
||||
status: "error",
|
||||
message: "UOM must contain at least one letter (A-Z).",
|
||||
message: "UOM must contain at least one alphabetic character.",
|
||||
});
|
||||
}
|
||||
is_base_unit = Boolean(is_base_unit);
|
||||
|
||||
if (base_unit_id !== null && base_unit_id !== undefined) {
|
||||
base_unit_id = Number(base_unit_id);
|
||||
if (Number.isNaN(base_unit_id)) {
|
||||
return res.status(400).json({
|
||||
status: "error",
|
||||
message: "base_unit_id must be a valid number."
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// SAFE: no Sequelize.where, no SQL functions
|
||||
const existingUOM = await UnitMaster.findOne({
|
||||
where: Sequelize.where(
|
||||
Sequelize.fn("LOWER", Sequelize.col("uom")),
|
||||
uom.toLowerCase()
|
||||
),
|
||||
where: {
|
||||
uom: cleaned
|
||||
}
|
||||
});
|
||||
|
||||
if (existingUOM)
|
||||
if (existingUOM) {
|
||||
return res.status(400).json({
|
||||
status: "error",
|
||||
message: `Unit '${uom}' already exists with short key '${existingUOM.uom_short_name}'.`,
|
||||
message: `Unit '${cleaned}' already exists with short key '${existingUOM.uom_short_name}'.`,
|
||||
});
|
||||
}
|
||||
|
||||
//Generate short name (same rule as CSV)
|
||||
// Abbreviation map
|
||||
const abbreviationMap = {
|
||||
METER: "MT",
|
||||
METRE: "MT",
|
||||
@ -58,44 +93,51 @@ exports.createUnit = async (req, res) => {
|
||||
UNIT: "UNT",
|
||||
};
|
||||
|
||||
let uomShort;
|
||||
let uomShort =
|
||||
cleaned.length <= 5
|
||||
? cleaned
|
||||
: (abbreviationMap[cleaned] || cleaned.substring(0, 3)) +
|
||||
Math.random().toString(36).substring(2, 4).toUpperCase();
|
||||
|
||||
if (cleaned.length <= 5) {
|
||||
uomShort = cleaned;
|
||||
} else {
|
||||
// If >5 letters, use abbreviation or auto-generate
|
||||
const baseShort = abbreviationMap[cleaned] || cleaned.substring(0, 3);
|
||||
const randomLetters = () =>
|
||||
Array.from({ length: 2 }, () =>
|
||||
String.fromCharCode(65 + Math.floor(Math.random() * 26))
|
||||
).join("");
|
||||
uomShort = `${baseShort}${randomLetters()}`.substring(0, 5);
|
||||
}
|
||||
uomShort = uomShort.substring(0, 5);
|
||||
|
||||
let exists = await UnitMaster.findOne({
|
||||
where: Sequelize.where(
|
||||
Sequelize.fn("LOWER", Sequelize.col("uom_short_name")),
|
||||
uomShort.toLowerCase()
|
||||
),
|
||||
where: {
|
||||
uom_short_name: uomShort
|
||||
}
|
||||
});
|
||||
|
||||
while (exists) {
|
||||
const randomSuffix = Math.random().toString(36).substring(2, 3).toUpperCase();
|
||||
uomShort = (uomShort.substring(0, 4) + randomSuffix).substring(0, 5);
|
||||
const suffix = Math.random().toString(36).substring(2, 3).toUpperCase();
|
||||
uomShort = (uomShort.substring(0, 4) + suffix).substring(0, 5);
|
||||
|
||||
exists = await UnitMaster.findOne({
|
||||
where: Sequelize.where(
|
||||
Sequelize.fn("LOWER", Sequelize.col("uom_short_name")),
|
||||
uomShort.toLowerCase()
|
||||
),
|
||||
where: {
|
||||
uom_short_name: uomShort
|
||||
}
|
||||
});
|
||||
}
|
||||
req.body.uom_short_name = uomShort;
|
||||
req.body.created_by = req.body.created_by || req.user.id;
|
||||
|
||||
const unit = await UnitMaster.create(req.body);
|
||||
res.status(201).json({ status: "success", data: unit });
|
||||
|
||||
const unit = await UnitMaster.create({
|
||||
uom: cleaned,
|
||||
uom_short_name: uomShort,
|
||||
description,
|
||||
is_base_unit,
|
||||
base_unit_id,
|
||||
factor: factor || null,
|
||||
created_by: req.body.created_by || req.user.id
|
||||
});
|
||||
|
||||
return res.status(201).json({
|
||||
status: "success",
|
||||
message: "Unit created successfully."
|
||||
});
|
||||
|
||||
} catch (err) {
|
||||
res.status(400).json({ status: "error", message: err.message });
|
||||
return res.status(400).json({
|
||||
status: "error",
|
||||
message: err.message
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@ -385,19 +427,16 @@ exports.uploadUnitMasterFromCSV = async (req, res) => {
|
||||
let uomShort = row._generatedShort;
|
||||
const description = row._description;
|
||||
|
||||
const normalizedUom = sanitizeStringValue(uom).toUpperCase();
|
||||
const normalizedShort = uomShort.toUpperCase();
|
||||
|
||||
const existing = await UnitMaster.findOne({
|
||||
where: {
|
||||
[Op.or]: [
|
||||
Sequelize.where(
|
||||
Sequelize.fn("LOWER", Sequelize.col("uom")),
|
||||
uom.toLowerCase()
|
||||
),
|
||||
Sequelize.where(
|
||||
Sequelize.fn("LOWER", Sequelize.col("uom_short_name")),
|
||||
uomShort.toLowerCase()
|
||||
),
|
||||
],
|
||||
},
|
||||
{ uom: normalizedUom },
|
||||
{ uom_short_name: normalizedShort }
|
||||
]
|
||||
}
|
||||
});
|
||||
|
||||
if (existing) {
|
||||
|
||||
@ -5,15 +5,43 @@ const VariationReasonMaster = db.VariationReasonMaster;
|
||||
// Create
|
||||
exports.createReason = async (req, res) => {
|
||||
try {
|
||||
const { reason, high_or_low } = req.body;
|
||||
let { reason, high_or_low } = req.body;
|
||||
if (typeof reason !== "string" || reason.trim().length === 0) {
|
||||
return res.status(400).json({
|
||||
status: "failed",
|
||||
message: "Reason is required"
|
||||
});
|
||||
}
|
||||
reason = reason.trim();
|
||||
|
||||
if (!reason)
|
||||
return res.status(400).send({ status: "failed", message: "Reason is required" });
|
||||
if (high_or_low) {
|
||||
high_or_low = high_or_low.toString().toLowerCase();
|
||||
|
||||
if (!["low", "high"].includes(high_or_low)) {
|
||||
return res.status(400).json({
|
||||
status: "failed",
|
||||
message: "Invalid high_or_low value"
|
||||
});
|
||||
}
|
||||
high_or_low = high_or_low === "low" ? "Low" : "High";
|
||||
}
|
||||
|
||||
const newReason = await VariationReasonMaster.create({
|
||||
reason,
|
||||
high_or_low,
|
||||
created_by: req.body.created_by || req.user.id
|
||||
});
|
||||
|
||||
return res.status(201).json({
|
||||
status: "success",
|
||||
message: "Reason created successfully"
|
||||
});
|
||||
|
||||
const newReason = await VariationReasonMaster.create({ reason, high_or_low, created_by: req.body.created_by || req.user.id });
|
||||
res.status(201).send({ status: "success", message: "Reason created successfully", data: newReason });
|
||||
} catch (err) {
|
||||
res.status(500).send({ status: "failed", message: err.message });
|
||||
return res.status(500).json({
|
||||
status: "failed",
|
||||
message: "Internal server error"
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -4,11 +4,20 @@ const ZeroTargetReasonMaster = db.ZeroTargetReasonMaster;
|
||||
// Create
|
||||
exports.create = async (req, res) => {
|
||||
try {
|
||||
const { reason, is_active } = req.body;
|
||||
if (!reason) return res.status(400).send({ status: "failed", message: "Reason is required" });
|
||||
let { reason, is_active } = req.body;
|
||||
if (typeof reason !== "string" || reason.trim().length === 0) {
|
||||
return res.status(400).json({
|
||||
status: "failed",
|
||||
message: "Reason is required"
|
||||
});
|
||||
}
|
||||
reason = reason.trim();
|
||||
|
||||
if (typeof is_active !== "boolean") {
|
||||
is_active = true;
|
||||
}
|
||||
const data = await ZeroTargetReasonMaster.create({ reason, is_active, created_by: req.user.id });
|
||||
res.status(201).send({ status: "success", data });
|
||||
res.status(201).send({ status: "success", message: "Record created successfully",});
|
||||
} catch (err) {
|
||||
res.status(500).send({ status: "failed", message: err.message });
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user