Initial commit : GowthamManavalan

This commit is contained in:
Gowtham M 2025-10-17 10:25:59 +05:30
commit b236ca3ba9
31 changed files with 5226 additions and 0 deletions

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
node_modules
.env
app/writable/logs/*
app/writable/uploads/*

15
app/config/db.config.js Normal file
View File

@ -0,0 +1,15 @@
require('dotenv').config();
module.exports = {
HOST: process.env.DB_HOST,
USER: process.env.DB_USER,
PASSWORD: process.env.DB_PASSWORD,
DB: process.env.DB_NAME,
dialect: process.env.DB_DIALECT || 'mysql',
pool: {
max: 5,
min: 0,
acquire: 30000,
idle: 10000
}
};

View File

@ -0,0 +1,47 @@
const db = require("../models");
const bcrypt = require("bcryptjs");
const jwt = require("jsonwebtoken");
require("dotenv").config();
const User = db.user;
// Register new user
exports.register = async (req, res) => {
try {
const { name, email, password } = req.body;
if (!name || !email || !password)
return res.status(400).send({'status':"failed",'message':"All fields required",'data': ""});
const existing = await User.findOne({ where: { email } });
if (existing) res.status(400).send({'status':"failed",'message':"Email already used",'data': ""});
const hashedPassword = await bcrypt.hash(password, 10);
const newUser = await User.create({ name, email, password: hashedPassword });
res.status(201).send({'status':"success",'message':"User registered successfully",'data': newUser });
} catch (err) {
res.status(500).send({'status':"failed",'message':err.message });
}
};
// Login user
exports.login = async (req, res) => {
try {
const { email, password } = req.body;
const user = await User.findOne({ where: { email } });
if (!user) res.status(404).send({'status':"failed",'message':"User not found",'data': ""});
const validPass = await bcrypt.compare(password, user.password);
if (!validPass) res.status(401).send({'status':"failed",'message':"Invalid password",'data': ""});
const token = jwt.sign({ id: user.id, email: user.email }, process.env.JWT_SECRET, {
expiresIn: "6h",
});
res.status(200).send({'status':"success",'message':"Login successful",'data': token });
} catch (err) {
res.status(500).send({'status':"failed",'message':err.message });
}
};

View File

@ -0,0 +1,118 @@
const db = require("../models");
const bcrypt = require("bcryptjs");
const Establishment = db.establishment;
const EstablishmentUser = db.establishment_user;
// Create Establishment + linked user
exports.createEstablishment = async (req, res) => {
try {
const { establishment_code, factory_name, email, permanent_factory_code, industry_code, license_number, isic_code, emirate, total_employment, establishment_user, } = req.body;
if ( !establishment_code || !factory_name || !email || !establishment_user?.name || !establishment_user?.email || !establishment_user?.password )
{
res.status(400).send({'status':"failed",'message':"Missing required fields",'data': "" });
}
// Create Establishment
const establishment = await Establishment.create({
establishment_code,
factory_name,
email,
permanent_factory_code,
industry_code,
license_number,
isic_code,
emirate,
total_employment,
});
// Hash password for user
const hashedPassword = await bcrypt.hash(establishment_user.password, 10);
// Create related Establishment User
const user = await EstablishmentUser.create({
establishment_id: establishment.id,
name: establishment_user.name,
email: establishment_user.email,
password: hashedPassword,
});
res.status(201).send({'status':"success",'message':"Establishment and linked user created successfully",'data': { establishment, user } });
} catch (err) {
res.status(500).send({'status':"failed",'message':err.message });
}
};
// Get all establishments (with user details)
exports.getAllEstablishments = async (req, res) => {
try {
const data = await Establishment.findAll({
include: [
{
model: EstablishmentUser,
as: "users",
attributes: ["id", "name", "email", "is_active"],
},
],
});
res.status(200).send({'status':"success",'message':"Fetched successfully",'data': data });
} catch (err) {
res.status(500).send({'status':"failed",'message':err.message });
}
};
// Get one establishment
exports.getEstablishmentById = async (req, res) => {
try {
const data = await Establishment.findByPk(req.params.id, {
include: [{ model: EstablishmentUser, as: "users" }],
});
if (!data) res.status(404).send({'status':"failed",'message':"Record Not found",'data': "" });
res.status(200).send({'status':"success",'message':"Fetched successfully",'data': data });
} catch (err) {
res.status(500).send({'status':"failed",'message':err.message });
}
};
// Update establishment
exports.updateEstablishment = async (req, res) => {
try {
const [updated] = await Establishment.update(req.body, {
where: { id: req.params.id },
});
if (!updated) res.status(404).send({'status':"failed",'message':"Record Not found",'data': "" });
res.status(200).send({'status':"success",'message':"Updated successfully",'data': "" });
} catch (err) {
res.status(500).send({'status':"failed",'message':err.message });
}
};
// Delete establishment
exports.deleteEstablishment = async (req, res) => {
try {
const deleted = await Establishment.destroy({ where: { id: req.params.id } });
if (!deleted) res.status(404).send({'status':"failed",'message':"Record Not found",'data': "" });
res.status(200).send({'status':"success",'message':"Deleted successfully",'data': "" });
} catch (err) {
res.status(500).send({'status':"failed",'message':err.message });
}
};

View File

@ -0,0 +1,83 @@
const { where } = require("sequelize");
const db = require("../models");
const EstablishmentProduct = db.EstablishmentProduct;
const Product = db.Product;
exports.createEstablishmentProduct = async (req, res) => {
try {
const data = await EstablishmentProduct.create(req.body);
res.status(201).send({'status':"success",'message':"Creation successful",'data': data });
} catch (err) {
res.status(500).send({'status':"failed",'message':err.message });
}
};
exports.getAllEstablishmentProducts = async (req, res) => {
try {
const whereClause = {};
if (req.query.establishment_id) {
whereClause.establishment_id = req.query.establishment_id;
}
const data = await EstablishmentProduct.findAll({
raw: true ,
where: whereClause,
include: [
{
model: Product,
as: "product",
attributes: ["product_name", "hs_code", "hs_description"],
},
],
});
res.status(200).send({'status':"success",'message':"Fetched successfully",'data': data });
} catch (err) {
res.status(500).send({'status':"failed",'message':err.message });
}
};
exports.getEstablishmentProductById = async (req, res) => {
try {
const data = await EstablishmentProduct.findByPk(req.params.id);
if (!data) res.status(404).send({'status':"failed",'message':"Record not found",'data': "" });
res.status(200).send({'status':"success",'message':"Fetched successfully",'data': data });
} catch (err) {
res.status(500).send({'status':"failed",'message':err.message });
}
};
exports.updateEstablishmentProduct = async (req, res) => {
try {
const [updated] = await EstablishmentProduct.update(req.body, { where: { id: req.params.id } });
if (!updated) res.status(404).send({'status':"failed",'message':"Record not found",'data': "" });
res.status(200).send({'status':"success",'message':"Record updated successfully",'data': "" });
} catch (err) {
res.status(500).send({'status':"failed",'message':err.message });
}
};
exports.deleteEstablishmentProduct = async (req, res) => {
try {
const deleted = await EstablishmentProduct.destroy({ where: { id: req.params.id } });
if (!deleted) res.status(404).send({'status':"failed",'message':"Record not found",'data': "" });
res.status(200).send({'status':"success",'message':"Record deleted successfully",'data': "" });
} catch (err) {
res.status(500).send({'status':"failed",'message':err.message });
}
};

View File

@ -0,0 +1,90 @@
const db = require("../models");
const bcrypt = require("bcryptjs");
const EstablishmentUser = db.establishment_user;
// Create User
exports.createUser = async (req, res) => {
try {
const { establishment_id, name, email, password } = req.body;
const hashed = await bcrypt.hash(password, 10);
const user = await EstablishmentUser.create({
establishment_id,
name,
email,
password: hashed,
});
res.status(201).send({'status':"success",'message':"Creation successful",'data': user });
} catch (err) {
res.status(500).send({'status':"failed",'message':err.message });
}
};
// Get all users
exports.getAllUsers = async (req, res) => {
try {
const users = await EstablishmentUser.findAll({ where:{ establishment_id : req.query.establishment_id} });
res.status(200).send({'status':"success",'message':"Fetched successfully",'data': users });
} catch (err) {
res.status(500).send({'status':"failed",'message':err.message });
}
};
// Get user by id
exports.getUserById = async (req, res) => {
try {
const user = await EstablishmentUser.findByPk(req.params.id);
if (!user) res.status(404).send({'status':"failed",'message':"Record not found",'data': "" });
res.status(200).send({'status':"success",'message':"Fetched successfully",'data': user });
} catch (err) {
res.status(500).send({'status':"failed",'message':err.message });
}
};
// Update user
exports.updateUser = async (req, res) => {
try {
const { name, email, password, is_active } = req.body;
const data = {};
if (name) data.name = name;
if (email) data.email = email;
if (is_active !== undefined) data.is_active = is_active;
if (password) data.password = await bcrypt.hash(password, 10);
const [updated] = await EstablishmentUser.update(data, {
where: { id: req.params.id },
});
if (!updated) res.status(404).send({'status':"failed",'message':"Record not found",'data': "" });
res.status(200).send({'status':"success",'message':"Record updated successfully",'data': "" });
} catch (err) {
res.status(500).send({'status':"failed",'message':err.message });
}
};
// Delete user
exports.deleteUser = async (req, res) => {
try {
const deleted = await EstablishmentUser.destroy({ where: { id: req.params.id } });
if (!deleted) res.status(404).send({'status':"failed",'message':"Record not found",'data': "" });
res.status(200).send({'status':"success",'message':"Record deleted successfully",'data': "" });
} catch (err) {
res.status(500).send({'status':"failed",'message':err.message });
}
};

View File

@ -0,0 +1,41 @@
const bcrypt = require("bcryptjs");
const jwt = require("jsonwebtoken");
const { EstablishmentUser } = require("../models");
// User Login
exports.login = async (req, res) => {
try {
const { email, password } = req.body;
if (!email || !password)
return res.status(400).send({'status':"failed",'message':"Email and password required",'data': ""});
const user = await EstablishmentUser.findOne({ where: { email } });
if (!user) res.status(404).send({'status':"failed",'message':"User not found",'data': ""});
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",
});
res.status(200).send({'status':"success",'message':"Login successful",'data': token });
} catch (err) {
res.status(500).send({'status':"failed",'message':err.message });
}
};
// User Logout (JWT-based — just a client-side operation)
exports.logout = async (req, res) => {
try {
// For JWT, logout is handled on the client (by deleting token)
// But you can still log or blacklist token if needed
res.status(200).send({'status':"success",'message':"Logout successful",'data': "" });
res.status(200).json({ message: "Logout successful (client should discard token)" });
} catch (err) {
res.status(500).send({'status':"failed",'message':err.message });
}
};

View File

@ -0,0 +1,68 @@
const db = require("../models");
const Product = db.Product;
exports.createProduct = async (req, res) => {
try {
const data = await Product.create(req.body);
res.status(201).send({'status':"success",'message':"created successfully",'data': data });
} catch (error) {
res.status(500).send({'status':"failed",'message':err.message });
}
};
exports.getAllProducts = async (req, res) => {
try {
const data = await Product.findAll();
res.status(200).send({'status':"success",'message':"Fetched successfully",'data': data });
} catch (error) {
res.status(500).send({'status':"failed",'message':err.message });
}
};
exports.getProductById = async (req, res) => {
try {
const data = await Product.findByPk(req.params.id);
if (!data) res.status(404).send({'status':"failed",'message':"Record Not found",'data': "" });
res.status(200).send({'status':"success",'message':"Fetched successfully",'data': data });
} catch (error) {
res.status(500).send({'status':"failed",'message':err.message });
}
};
exports.updateProduct = async (req, res) => {
try {
const [updated] = await Product.update(req.body, { where: { id: req.params.id } });
if (!updated) res.status(404).send({'status':"failed",'message':"Record Not found",'data': "" });
res.status(200).send({'status':"success",'message':"Updated successfully",'data': "" });
} catch (error) {
res.status(500).send({'status':"failed",'message':err.message });
}
};
exports.deleteProduct = async (req, res) => {
try {
const deleted = await Product.destroy({ where: { id: req.params.id } });
if (!deleted) res.status(404).send({'status':"failed",'message':"Record Not found",'data': "" });
res.status(200).send({'status':"success",'message':"Deleted successfully",'data': "" });
} catch (error) {
res.status(500).send({'status':"failed",'message':err.message });
}
};

View File

@ -0,0 +1,150 @@
const db = require("../models");
const Submission = db.Submission;
const SubmissionProduct = db.SubmissionProduct;
const Product = db.Product;
const { Op } = require("sequelize");
exports.createSubmission = async (req, res) => {
try {
const { products = [], ...submissionData } = req.body;
if (products.length > 10)
return res.status(400).json({ status: "failed", message: "Max 10 products allowed" });
const submission = await Submission.create(submissionData);
if (products.length) {
products.forEach((p) => (p.submission_id = submission.id));
await SubmissionProduct.bulkCreate(products);
}
const result = await Submission.findByPk(submission.id, {
include: [{ model: SubmissionProduct, as: "products" }],
});
res.status(201).json({ status: "success", data: result });
} catch (err) {
res.status(500).json({ status: "failed", message: err.message });
}
};
exports.updateSubmission = async (req, res) => {
try {
const { id } = req.params;
const { products = [], ...submissionData } = req.body;
// Check submission exists
const submission = await Submission.findByPk(id);
if (!submission)
return res.status(404).json({ status: "failed", message: "Submission not found" });
// Update submission main data
await submission.update(submissionData);
// Process products
for (const product of products) {
if (product.id) {
// If product ID exists, update
const existingProduct = await SubmissionProduct.findOne({
where: { id: product.id, submission_id: id },
});
if (existingProduct) {
await existingProduct.update(product);
} else {
// fallback: if given ID not found, create new
product.submission_id = id;
await SubmissionProduct.create(product);
}
} else {
// If product has no ID → create new record
product.submission_id = id;
await SubmissionProduct.create(product);
}
}
// Get updated full record
const updatedSubmission = await Submission.findByPk(id, {
include: [{ model: SubmissionProduct, as: "products" }],
});
res.status(200).json({
status: "success",
message: "Submission updated successfully",
data: updatedSubmission,
});
} catch (err) {
console.error(err);
res.status(500).json({ status: "failed", message: err.message });
}
};
exports.submissionHistory = async (req, res) => {
try {
const { establishment_id } = req.params;
const data = await Submission.findAll({
where: { establishment_id },
order: [["id", "DESC"]],
});
res.status(200).json({ status: "success", data });
} catch (err) {
res.status(500).json({ status: "failed", message: err.message });
}
};
exports.submissionList = async (req, res) => {
try {
const data = await Submission.findAll({
// include: [{ model: SubmissionProduct, as: "products" }],
order: [["id", "DESC"]],
});
res.status(200).json({ status: "success", data });
} catch (err) {
res.status(500).json({ status: "failed", message: err.message });
}
};
exports.viewSubmissionDetails = async (req, res) => {
try {
const { id } = req.params;
const data = await Submission.findByPk(id, {
include: [
{
model: SubmissionProduct, as: "products" ,
include: [
{
model: Product,
as: "product",
attributes: ["product_name", "hs_code", "hs_description"],
},
],
}
],
});
if (!data) return res.status(404).json({ status: "failed", message: "Not found" });
res.status(200).json({ status: "success", data });
} catch (err) {
res.status(500).json({ status: "failed", message: err.message });
}
};

View File

@ -0,0 +1,59 @@
// controllers/unitMasterController.js
const { UnitMaster } = require("../models");
// Create new Unit
exports.createUnit = async (req, res) => {
try {
const unit = await UnitMaster.create(req.body);
res.status(201).json({ status: "success", data: unit });
} catch (err) {
res.status(400).json({ status: "error", message: err.message });
}
};
// Get all Units
exports.getAllUnits = async (req, res) => {
try {
const units = await UnitMaster.findAll();
res.status(200).json({ status: "success", data: units });
} catch (err) {
res.status(500).json({ status: "error", message: err.message });
}
};
// Get Unit by ID
exports.getUnitById = async (req, res) => {
try {
const unit = await UnitMaster.findByPk(req.params.id);
if (!unit) return res.status(404).json({ status: "error", message: "Unit not found" });
res.status(200).json({ status: "success", data: unit });
} catch (err) {
res.status(500).json({ status: "error", message: err.message });
}
};
// Update Unit
exports.updateUnit = async (req, res) => {
try {
const unit = await UnitMaster.findByPk(req.params.id);
if (!unit) return res.status(404).json({ status: "error", message: "Unit not found" });
await unit.update(req.body);
res.status(200).json({ status: "success", data: unit });
} catch (err) {
res.status(400).json({ status: "error", message: err.message });
}
};
// Delete Unit
exports.deleteUnit = async (req, res) => {
try {
const unit = await UnitMaster.findByPk(req.params.id);
if (!unit) return res.status(404).json({ status: "error", message: "Unit not found" });
await unit.destroy();
res.status(200).json({ status: "success", message: "Unit deleted successfully" });
} catch (err) {
res.status(500).json({ status: "error", message: err.message });
}
};

View File

@ -0,0 +1,94 @@
const db = require("../models");
const bcrypt = require("bcryptjs");
const jwt = require("jsonwebtoken");
const logger = require("../services/logger");
require("dotenv").config();
const User = db.user;
// Create User
exports.createUser = async (req, res) => {
try {
const { name, email, password } = req.body;
const hashedPassword = await bcrypt.hash(password, 10);
const user = await User.create({ name, email, password: hashedPassword });
logger.info(`User created: ${email}`);
res.status(201).send({'status':"success",'message':"created successfully",'data': user });
} catch (err) {
logger.error(err.message);
res.status(500).send({'status':"failed",'message':err.message });
}
};
// Get All Users
exports.getAllUsers = async (req, res) => {
try {
const users = await User.findAll();
res.status(200).send({'status':"success",'message':"Fetched successfully",'data': users });
} catch (err) {
logger.error(err.message);
res.status(500).send({'status':"failed",'message':err.message });
}
};
// Get User by ID
exports.getUserById = async (req, res) => {
try {
const user = await User.findByPk(req.params.id);
if (!user) res.status(404).send({'status':"failed",'message':"Record Not found",'data': "" });
res.status(200).send({'status':"success",'message':"Fetched successfully",'data': user });
} catch (err) {
logger.error(err.message);
res.status(500).send({'status':"failed",'message':err.message });
}
};
// Update User
exports.updateUser = async (req, res) => {
try {
const { name, email } = req.body;
const [updated] = await User.update({ name, email }, { where: { id: req.params.id } });
if (updated) {
const updatedUser = await User.findByPk(req.params.id);
res.status(200).send({'status':"success",'message':"Updated successfully",'data': updatedUser });
} else {
res.status(404).send({'status':"failed",'message':"Record Not found",'data': "" });
}
} catch (err) {
logger.error(err.message);
res.status(500).send({'status':"failed",'message':err.message });
}
};
// Delete User
exports.deleteUser = async (req, res) => {
try {
const deleted = await User.destroy({ where: { id: req.params.id } });
if (deleted) {
res.status(200).send({'status':"success",'message':"Deleted successfully",'data': "" });
} else {
res.status(404).send({'status':"failed",'message':"Record Not found",'data': "" });
}
} catch (err) {
logger.error(err.message);
res.status(500).send({'status':"failed",'message':err.message });
}
};

View File

@ -0,0 +1,66 @@
// app/controllers/variationReasonMaster.controller.js
const db = require("../models");
const VariationReasonMaster = db.VariationReasonMaster;
// Create
exports.createReason = async (req, res) => {
try {
const { reason, high_or_low } = req.body;
if (!reason)
return res.status(400).send({ status: "failed", message: "Reason is required" });
const newReason = await VariationReasonMaster.create({ reason, high_or_low });
res.status(201).send({ status: "success", message: "Reason created successfully", data: newReason });
} catch (err) {
res.status(500).send({ status: "failed", message: err.message });
}
};
// Get all
exports.getAllReasons = async (req, res) => {
try {
const data = await VariationReasonMaster.findAll();
res.status(200).send({ status: "success", message: "Fetched successfully", data });
} catch (err) {
res.status(500).send({ status: "failed", message: err.message });
}
};
// Get by ID
exports.getReasonById = async (req, res) => {
try {
const data = await VariationReasonMaster.findByPk(req.params.id);
if (!data) return res.status(404).send({ status: "failed", message: "Reason not found" });
res.status(200).send({ status: "success", message: "Fetched successfully", data });
} catch (err) {
res.status(500).send({ status: "failed", message: err.message });
}
};
// Update
exports.updateReason = async (req, res) => {
try {
const { reason, high_or_low, is_active } = req.body;
const updated = await VariationReasonMaster.update(
{ reason, high_or_low, is_active, updated_at: new Date(), updated_on: new Date() },
{ where: { id: req.params.id } }
);
if (updated[0] === 0) return res.status(404).send({ status: "failed", message: "Reason not found" });
res.status(200).send({ status: "success", message: "Updated successfully" });
} catch (err) {
res.status(500).send({ status: "failed", message: err.message });
}
};
// Delete
exports.deleteReason = async (req, res) => {
try {
const deleted = await VariationReasonMaster.destroy({ where: { id: req.params.id } });
if (!deleted) return res.status(404).send({ status: "failed", message: "Reason not found" });
res.status(200).send({ status: "success", message: "Deleted successfully" });
} catch (err) {
res.status(500).send({ status: "failed", message: err.message });
}
};

View File

@ -0,0 +1,62 @@
const db = require("../models");
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" });
const data = await ZeroTargetReasonMaster.create({ reason, is_active });
res.status(201).send({ status: "success", data });
} catch (err) {
res.status(500).send({ status: "failed", message: err.message });
}
};
// ✅ Get All
exports.getAll = async (req, res) => {
try {
const data = await ZeroTargetReasonMaster.findAll();
res.status(200).send({ status: "success", data });
} catch (err) {
res.status(500).send({ status: "failed", message: err.message });
}
};
// ✅ Get By ID
exports.getById = async (req, res) => {
try {
const { id } = req.params;
const data = await ZeroTargetReasonMaster.findByPk(id);
if (!data) return res.status(404).send({ status: "failed", message: "Not found" });
res.status(200).send({ status: "success", data });
} catch (err) {
res.status(500).send({ status: "failed", message: err.message });
}
};
// ✅ Update
exports.update = async (req, res) => {
try {
const { id } = req.params;
const [updated] = await ZeroTargetReasonMaster.update(req.body, { where: { id } });
if (!updated) return res.status(404).send({ status: "failed", message: "Not found" });
const data = await ZeroTargetReasonMaster.findByPk(id);
res.status(200).send({ status: "success", data });
} catch (err) {
res.status(500).send({ status: "failed", message: err.message });
}
};
// ✅ Delete
exports.delete = async (req, res) => {
try {
const { id } = req.params;
const deleted = await ZeroTargetReasonMaster.destroy({ where: { id } });
if (!deleted) return res.status(404).send({ status: "failed", message: "Not found" });
res.status(200).send({ status: "success", message: "Deleted successfully" });
} catch (err) {
res.status(500).send({ status: "failed", message: err.message });
}
};

View File

@ -0,0 +1,12 @@
require("dotenv").config();
module.exports = function (req, res, next) {
const APP_SIGNATURE = req.headers["APP_SIGNATURE"];
if(APP_SIGNATURE != process.env.APP_SIGNATURE)
{
return res.status(403).json({ message: "Access denied, SIGNATURE missing" });
}
};

View File

@ -0,0 +1,15 @@
const jwt = require("jsonwebtoken");
require("dotenv").config();
module.exports = function (req, res, next) {
const authHeader = req.headers["authorization"];
const token = authHeader && authHeader.split(" ")[1]; // Expect "Bearer <token>"
if (!token) return res.status(403).json({ message: "Access denied, token missing" });
jwt.verify(token, process.env.JWT_SECRET, (err, decoded) => {
if (err) return res.status(401).json({ message: "Invalid or expired token" });
req.user = decoded;
next();
});
};

View File

@ -0,0 +1,54 @@
// models/UnitMaster.js
module.exports = (sequelize, DataTypes) => {
const UnitMaster = sequelize.define(
"UnitMaster",
{
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true,
},
uom_short_name: {
type: DataTypes.STRING(20),
allowNull: false,
},
uom: {
type: DataTypes.STRING(100),
allowNull: false,
},
is_base_unit: {
type: DataTypes.BOOLEAN,
defaultValue: false,
},
base_unit_id: {
type: DataTypes.INTEGER,
allowNull: true,
},
factor: {
type: DataTypes.STRING,
allowNull: true,
},
is_active: {
type: DataTypes.BOOLEAN,
defaultValue: true,
},
created_by: {
type: DataTypes.INTEGER,
allowNull: true,
},
updated_by: {
type: DataTypes.INTEGER,
allowNull: true,
},
},
{
tableName: "unit_master",
timestamps: true,
createdAt: "created_at",
updatedAt: "updated_at",
}
);
return UnitMaster;
};

View File

@ -0,0 +1,74 @@
module.exports = (sequelize, DataTypes) => {
const Establishment = sequelize.define(
"establishments",
{
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true,
},
establishment_code: {
type: DataTypes.STRING,
allowNull: false,
},
factory_name: {
type: DataTypes.STRING,
allowNull: false,
},
email: {
type: DataTypes.STRING,
allowNull: false,
},
permanent_factory_code: {
type: DataTypes.STRING,
},
industry_code: {
type: DataTypes.STRING,
},
license_number: {
type: DataTypes.STRING,
},
isic_code: {
type: DataTypes.STRING,
},
emirate: {
type: DataTypes.STRING,
},
emirati_male: DataTypes.INTEGER,
emirati_female: DataTypes.INTEGER,
non_emirati_male: DataTypes.INTEGER,
non_emirati_female: DataTypes.INTEGER,
total_emirati: DataTypes.INTEGER,
total_employees: {
type: DataTypes.INTEGER,
},
is_active: {
type: DataTypes.BOOLEAN,
defaultValue: true,
},
created_at: {
type: DataTypes.DATE,
defaultValue: DataTypes.NOW,
},
created_by: {
type: DataTypes.INTEGER,
allowNull: true,
},
updated_at: {
type: DataTypes.DATE,
allowNull: true,
},
updated_by: {
type: DataTypes.INTEGER,
allowNull: true,
},
},
{
timestamps: false,
tableName: "establishments",
}
);
return Establishment;
};

View File

@ -0,0 +1,51 @@
module.exports = (sequelize, DataTypes) => {
const EstablishmentProduct = sequelize.define("establishment_products", {
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true,
},
establishment_id: {
type: DataTypes.INTEGER,
allowNull: false,
},
product_id: {
type: DataTypes.INTEGER,
allowNull: false,
},
is_active: {
type: DataTypes.BOOLEAN,
defaultValue: true,
},
created_at: {
type: DataTypes.DATE,
defaultValue: DataTypes.NOW,
},
created_by: {
type: DataTypes.INTEGER,
allowNull: true,
},
updated_at: {
type: DataTypes.DATE,
allowNull: true,
},
updated_by: {
type: DataTypes.INTEGER,
allowNull: true,
},
}, {
tableName: "establishment_products",
timestamps: false,
});
EstablishmentProduct.associate = (models) => {
EstablishmentProduct.belongsTo(models.Product, { // 'Product' with capital P
foreignKey: "product_id",
as: "product",
});
};
return EstablishmentProduct;
};

View File

@ -0,0 +1,56 @@
module.exports = (sequelize, DataTypes) => {
const EstablishmentUser = sequelize.define(
"establishment_users",
{
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true,
},
establishment_id: {
type: DataTypes.INTEGER,
allowNull: false,
},
name: {
type: DataTypes.STRING,
allowNull: false,
},
email: {
type: DataTypes.STRING,
allowNull: false,
unique: true,
},
password: {
type: DataTypes.STRING,
allowNull: false,
},
is_active: {
type: DataTypes.BOOLEAN,
defaultValue: true,
},
created_at: {
type: DataTypes.DATE,
defaultValue: DataTypes.NOW,
},
created_by: {
type: DataTypes.INTEGER,
allowNull: true,
},
updated_at: {
type: DataTypes.DATE,
allowNull: true,
},
updated_by: {
type: DataTypes.INTEGER,
allowNull: true,
},
},
{
timestamps: false,
tableName: "establishment_users",
}
);
return EstablishmentUser;
};

46
app/models/index.js Normal file
View File

@ -0,0 +1,46 @@
const dbConfig = require("../config/db.config.js");
const { Sequelize, DataTypes } = require("sequelize");
const sequelize = new Sequelize(dbConfig.DB, dbConfig.USER, dbConfig.PASSWORD, {
host: dbConfig.HOST,
dialect: dbConfig.dialect,
pool: dbConfig.pool,
logging: false,
});
const db = {};
db.Sequelize = Sequelize;
db.sequelize = sequelize;
db.user = require("./user.model.js")(sequelize, DataTypes);
db.establishment = require("./establishment.model")(sequelize, DataTypes);
db.establishment_user = require("./establishment_user.model")(sequelize, DataTypes);
db.Product = require("./product.model.js")(sequelize, DataTypes);
db.EstablishmentProduct = require("./establishmentProduct.model")(sequelize, DataTypes);
db.VariationReasonMaster = require("./variationReasonMaster.model")(sequelize, DataTypes);
db.ZeroTargetReasonMaster = require("./zeroTargetReason.model")(sequelize, DataTypes);
db.Submission = require("./submission.model")(sequelize, DataTypes);
db.SubmissionProduct = require("./submissionProduct.model")(sequelize, DataTypes);
db.UnitMaster = require("./UnitMaster.model")(sequelize, DataTypes);
// Associations
db.establishment.hasMany(db.establishment_user, {
foreignKey: "establishment_id",
as: "users",
});
db.establishment_user.belongsTo(db.establishment, {
foreignKey: "establishment_id",
as: "establishment",
});
// **Call associate methods defined in models**
Object.keys(db).forEach((modelName) => {
if (db[modelName].associate) {
db[modelName].associate(db);
}
});
module.exports = db;

View File

@ -0,0 +1,61 @@
module.exports = (sequelize, DataTypes) => {
const Product = sequelize.define("products", {
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true,
},
product_name: {
type: DataTypes.STRING,
allowNull: false,
},
hs_code: {
type: DataTypes.STRING,
allowNull: true,
},
hs_description: {
type: DataTypes.TEXT,
allowNull: true,
},
is_active: {
type: DataTypes.BOOLEAN,
defaultValue: true,
},
created_at: {
type: DataTypes.DATE,
defaultValue: DataTypes.NOW,
},
created_by: {
type: DataTypes.INTEGER,
allowNull: true,
},
updated_at: {
type: DataTypes.DATE,
allowNull: true,
},
updated_by: {
type: DataTypes.INTEGER,
allowNull: true,
},
}, {
tableName: "products",
timestamps: false,
});
Product.associate = (models) => {
Product.hasMany(models.EstablishmentProduct, {
foreignKey: "product_id",
as: "establishment_products",
});
};
Product.associate = (models) => {
Product.hasMany(models.SubmissionProduct, {
foreignKey: "product_id",
as: "submission_products",
});
};
return Product;
};

View File

@ -0,0 +1,73 @@
module.exports = (sequelize, DataTypes) => {
const Submission = sequelize.define("submission", {
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true,
},
establishment_id: {
type: DataTypes.INTEGER,
allowNull: false,
},
quarter: {
type: DataTypes.ENUM("Q1", "Q2", "Q3", "Q4"),
allowNull: false,
},
year: {
type: DataTypes.INTEGER,
allowNull: false,
},
emirati_male: DataTypes.INTEGER,
emirati_female: DataTypes.INTEGER,
non_emirati_male: DataTypes.INTEGER,
non_emirati_female: DataTypes.INTEGER,
total_emirati: DataTypes.INTEGER,
total_employees: DataTypes.INTEGER,
edit_request: {
type: DataTypes.BOOLEAN,
defaultValue: false,
},
edit_access: {
type: DataTypes.BOOLEAN,
defaultValue: false,
},
edit_access_close_date: {
type: DataTypes.DATE,
allowNull: true,
},
status: {
type: DataTypes.STRING(50),
allowNull: true,
},
created_by: {
type: DataTypes.INTEGER,
allowNull: true,
},
updated_by: {
type: DataTypes.INTEGER,
allowNull: true,
},
created_at: {
type: DataTypes.DATE,
defaultValue: DataTypes.NOW,
},
updated_at: {
type: DataTypes.DATE,
allowNull: true,
},
}, {
tableName: "submission",
timestamps: false,
});
Submission.associate = (models) => {
Submission.hasMany(models.SubmissionProduct, {
foreignKey: "submission_id",
as: "products",
onDelete: "CASCADE",
});
};
return Submission;
};

View File

@ -0,0 +1,61 @@
module.exports = (sequelize, DataTypes) => {
const SubmissionProduct = sequelize.define("submission_products", {
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true,
},
submission_id: {
type: DataTypes.INTEGER,
allowNull: false,
},
product_id: DataTypes.INTEGER,
unit_id: DataTypes.INTEGER,
annual_installed_capacity: DataTypes.STRING(100),
previous_quantity: DataTypes.STRING(100),
previous_cost: DataTypes.STRING(100),
current_quantity: DataTypes.STRING(100),
current_cost: DataTypes.STRING(100),
forecast_quantity: DataTypes.STRING(100),
forecast_cost: DataTypes.STRING(100),
variation_reason_master_id: DataTypes.INTEGER,
other_variation_reason: DataTypes.STRING(255),
zero_target_reason_master_id: DataTypes.INTEGER,
other_zero_target_reason: DataTypes.STRING(255),
remarks: DataTypes.STRING(255),
created_by: DataTypes.INTEGER,
updated_by: DataTypes.INTEGER,
is_active: {
type: DataTypes.BOOLEAN,
defaultValue: true,
},
created_at: {
type: DataTypes.DATE,
defaultValue: DataTypes.NOW,
},
updated_at: {
type: DataTypes.DATE,
allowNull: true,
},
}, {
tableName: "submission_products",
timestamps: false,
});
SubmissionProduct.associate = (models) => {
SubmissionProduct.belongsTo(models.Submission, {
foreignKey: "submission_id",
as: "submission",
});
};
SubmissionProduct.associate = (models) => {
SubmissionProduct.belongsTo(models.Product, { // 'Product' with capital P
foreignKey: "product_id",
as: "product",
});
};
return SubmissionProduct;
};

25
app/models/user.model.js Normal file
View File

@ -0,0 +1,25 @@
module.exports = (sequelize, DataTypes) => {
const User = sequelize.define("admin_users", {
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true,
},
name: {
type: DataTypes.STRING,
allowNull: false,
},
email: {
type: DataTypes.STRING,
allowNull: false,
unique: true,
},
password: {
type: DataTypes.STRING,
allowNull: false,
},
});
return User;
};

View File

@ -0,0 +1,44 @@
// app/models/variationReasonMaster.model.js
module.exports = (sequelize, DataTypes) => {
const VariationReasonMaster = sequelize.define("variation_reason_master", {
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true,
},
reason: {
type: DataTypes.STRING(255),
allowNull: false,
},
high_or_low: {
type: DataTypes.STRING(20),
allowNull: true,
},
is_active: {
type: DataTypes.BOOLEAN,
defaultValue: true,
},
created_at: {
type: DataTypes.DATE,
defaultValue: DataTypes.NOW,
},
created_by: {
type: DataTypes.INTEGER,
allowNull: true,
},
updated_at: {
type: DataTypes.DATE,
allowNull: true,
},
updated_by: {
type: DataTypes.INTEGER,
allowNull: true,
},
}, {
tableName: "variation_reason_master",
timestamps: false,
});
return VariationReasonMaster;
};

View File

@ -0,0 +1,39 @@
module.exports = (sequelize, DataTypes) => {
const ZeroTargetReasonMaster = sequelize.define("zero_target_reason_master", {
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true,
},
reason: {
type: DataTypes.STRING(255),
allowNull: false,
},
is_active: {
type: DataTypes.BOOLEAN,
defaultValue: true,
},
created_at: {
type: DataTypes.DATE,
defaultValue: DataTypes.NOW,
},
created_by: {
type: DataTypes.INTEGER,
allowNull: true,
},
updated_at: {
type: DataTypes.DATE,
allowNull: true,
},
updated_by: {
type: DataTypes.INTEGER,
allowNull: true,
},
}, {
tableName: "zero_target_reason_master",
timestamps: false,
});
return ZeroTargetReasonMaster;
};

1423
app/routes/routes.js Normal file

File diff suppressed because it is too large Load Diff

15
app/services/logger.js Normal file
View File

@ -0,0 +1,15 @@
const { createLogger, transports, format } = require('winston');
const path = require('path');
const logger = createLogger({
format: format.combine(
format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),
format.printf(info => `${info.timestamp} ${info.level}: ${info.message}`)
),
transports: [
new transports.File({ filename: path.join(__dirname, '../writable/logs/app.log') }),
new transports.Console()
]
});
module.exports = logger;

2186
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

30
package.json Normal file
View File

@ -0,0 +1,30 @@
{
"name": "fcsc_ipi_backend",
"version": "1.0.0",
"description": "",
"main": "server.js",
"scripts": {
"start": "node server.js",
"dev": "nodemon server.js"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"bcryptjs": "^3.0.2",
"cors": "^2.8.5",
"dotenv": "^17.2.3",
"express": "^5.1.0",
"express-validator": "^7.2.1",
"helmet": "^8.1.0",
"jsonwebtoken": "^9.0.2",
"morgan": "^1.10.1",
"multer": "^2.0.2",
"mysql2": "^3.15.2",
"nodemon": "^3.1.10",
"sequelize": "^6.37.7",
"swagger-jsdoc": "^6.2.8",
"swagger-ui-express": "^5.0.1",
"winston": "^3.18.3"
}
}

64
server.js Normal file
View File

@ -0,0 +1,64 @@
const express = require("express");
const cors = require("cors");
const helmet = require("helmet");
const morgan = require("morgan");
const swaggerUi = require("swagger-ui-express");
const swaggerJsdoc = require("swagger-jsdoc");
const routes = require("./app/routes/routes");
const db = require("./app/models");
require("dotenv").config();
const app = express();
app.use(express.json());
app.use(cors());
app.use(helmet());
app.use(morgan("dev"));
// Swagger setup
const swaggerOptions = {
definition: {
openapi: "3.0.0",
info: {
title: "FCSC IPI Survey",
version: "1.0.0",
description: "User management + Auth API using Sequelize and Swagger",
},
components: {
securitySchemes: {
bearerAuth: {
type: "http",
scheme: "bearer",
bearerFormat: "JWT",
},
},
},
security: [
{
bearerAuth: [],
},
],
},
apis: ["./app/routes/*.js"],
};
const swaggerDocs = swaggerJsdoc(swaggerOptions);
app.use("/api-docs", swaggerUi.serve, swaggerUi.setup(swaggerDocs));
// Routes
app.use("/api", routes);
// Sync DB
db.sequelize.sync({ alter: true }).then(() => {
console.log("✅ Database connected & synced.");
});
// Start server
const PORT = process.env.PORT || 5000;
app.listen(PORT, () => {
console.log(`🚀 Server running on port ${PORT}`);
});