Initial commit : GowthamManavalan
This commit is contained in:
commit
b236ca3ba9
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
node_modules
|
||||||
|
.env
|
||||||
|
app/writable/logs/*
|
||||||
|
app/writable/uploads/*
|
||||||
15
app/config/db.config.js
Normal file
15
app/config/db.config.js
Normal 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
|
||||||
|
}
|
||||||
|
};
|
||||||
47
app/controllers/auth.controller.js
Normal file
47
app/controllers/auth.controller.js
Normal 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 });
|
||||||
|
}
|
||||||
|
};
|
||||||
118
app/controllers/establishment.controller.js
Normal file
118
app/controllers/establishment.controller.js
Normal 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 });
|
||||||
|
}
|
||||||
|
};
|
||||||
83
app/controllers/establishment_products.controller.js
Normal file
83
app/controllers/establishment_products.controller.js
Normal 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 });
|
||||||
|
}
|
||||||
|
};
|
||||||
90
app/controllers/establishment_user.controller.js
Normal file
90
app/controllers/establishment_user.controller.js
Normal 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 });
|
||||||
|
}
|
||||||
|
};
|
||||||
41
app/controllers/establishment_users_auth.controller.js
Normal file
41
app/controllers/establishment_users_auth.controller.js
Normal 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 });
|
||||||
|
}
|
||||||
|
};
|
||||||
68
app/controllers/products.controller.js
Normal file
68
app/controllers/products.controller.js
Normal 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 });
|
||||||
|
}
|
||||||
|
};
|
||||||
150
app/controllers/submission.controller.js
Normal file
150
app/controllers/submission.controller.js
Normal 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 });
|
||||||
|
}
|
||||||
|
};
|
||||||
59
app/controllers/unitMasterController.js
Normal file
59
app/controllers/unitMasterController.js
Normal 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 });
|
||||||
|
}
|
||||||
|
};
|
||||||
94
app/controllers/user.controller.js
Normal file
94
app/controllers/user.controller.js
Normal 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 });
|
||||||
|
}
|
||||||
|
};
|
||||||
66
app/controllers/variationReasonMaster.controller.js
Normal file
66
app/controllers/variationReasonMaster.controller.js
Normal 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 });
|
||||||
|
}
|
||||||
|
};
|
||||||
62
app/controllers/zeroTargetReasonMaster.controller.js
Normal file
62
app/controllers/zeroTargetReasonMaster.controller.js
Normal 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 });
|
||||||
|
}
|
||||||
|
};
|
||||||
12
app/middleware/app.middleware.js
Normal file
12
app/middleware/app.middleware.js
Normal 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" });
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
15
app/middleware/auth.middleware.js
Normal file
15
app/middleware/auth.middleware.js
Normal 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();
|
||||||
|
});
|
||||||
|
};
|
||||||
54
app/models/UnitMaster.model.js
Normal file
54
app/models/UnitMaster.model.js
Normal 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;
|
||||||
|
};
|
||||||
|
|
||||||
74
app/models/establishment.model.js
Normal file
74
app/models/establishment.model.js
Normal 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;
|
||||||
|
};
|
||||||
|
|
||||||
51
app/models/establishmentProduct.model.js
Normal file
51
app/models/establishmentProduct.model.js
Normal 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;
|
||||||
|
};
|
||||||
|
|
||||||
56
app/models/establishment_user.model.js
Normal file
56
app/models/establishment_user.model.js
Normal 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
46
app/models/index.js
Normal 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;
|
||||||
61
app/models/product.model.js
Normal file
61
app/models/product.model.js
Normal 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;
|
||||||
|
};
|
||||||
|
|
||||||
73
app/models/submission.model.js
Normal file
73
app/models/submission.model.js
Normal 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;
|
||||||
|
};
|
||||||
|
|
||||||
61
app/models/submissionProduct.model.js
Normal file
61
app/models/submissionProduct.model.js
Normal 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
25
app/models/user.model.js
Normal 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;
|
||||||
|
};
|
||||||
|
|
||||||
44
app/models/variationReasonMaster.model.js
Normal file
44
app/models/variationReasonMaster.model.js
Normal 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;
|
||||||
|
};
|
||||||
|
|
||||||
39
app/models/zeroTargetReason.model.js
Normal file
39
app/models/zeroTargetReason.model.js
Normal 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
1423
app/routes/routes.js
Normal file
File diff suppressed because it is too large
Load Diff
15
app/services/logger.js
Normal file
15
app/services/logger.js
Normal 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
2186
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
30
package.json
Normal file
30
package.json
Normal 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
64
server.js
Normal 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}`);
|
||||||
|
});
|
||||||
Loading…
Reference in New Issue
Block a user