GWM : password reset and QuarterlyWindowsConfiguration
This commit is contained in:
parent
59975c709a
commit
b0c7e1c8d3
@ -54,13 +54,15 @@ exports.getUserById = async (req, res) => {
|
||||
// Update user
|
||||
exports.updateUser = async (req, res) => {
|
||||
try {
|
||||
|
||||
const { name, email, password, is_active } = req.body;
|
||||
|
||||
const { name, email, is_active, password } = 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);
|
||||
|
||||
// Allow updates only if fields are provided
|
||||
if (name !== undefined && name !== null) data.name = name;
|
||||
if (email !== undefined && email !== null) data.email = email;
|
||||
if (is_active !== undefined && is_active !== null) data.is_active = is_active;
|
||||
if (password && password.trim() !== "") data.password = await bcrypt.hash(password, 10);
|
||||
|
||||
const [updated] = await EstablishmentUser.update(data, {
|
||||
where: { id: req.params.id },
|
||||
@ -75,6 +77,48 @@ exports.updateUser = async (req, res) => {
|
||||
}
|
||||
};
|
||||
|
||||
// Change Establishment User Password
|
||||
exports.changeUserPassword = async (req, res) => {
|
||||
try {
|
||||
const { old_password, new_password, confirm_password } = req.body;
|
||||
const userId = req.params.id;
|
||||
|
||||
// Validate inputs
|
||||
if (!old_password || !new_password || !confirm_password) {
|
||||
return res.status(400).send({ status: "failed", message: "All password fields are required" });
|
||||
}
|
||||
|
||||
if (new_password !== confirm_password) {
|
||||
return res.status(400).send({ status: "failed", message: "New password and confirm password do not match" });
|
||||
}
|
||||
|
||||
// Find user
|
||||
const user = await EstablishmentUser.findByPk(userId);
|
||||
if (!user) {
|
||||
return res.status(404).send({ status: "failed", message: "User not found" });
|
||||
}
|
||||
|
||||
// Verify old password
|
||||
const isMatch = await bcrypt.compare(old_password, user.password);
|
||||
if (!isMatch) {
|
||||
return res.status(400).send({ status: "failed", message: "Old password is incorrect" });
|
||||
}
|
||||
|
||||
// Hash and update new password
|
||||
const hashedPassword = await bcrypt.hash(new_password, 10);
|
||||
await EstablishmentUser.update(
|
||||
{ password: hashedPassword },
|
||||
{ where: { id: userId } }
|
||||
);
|
||||
|
||||
return res.status(200).send({status: "success", message: "Password updated successfully",});
|
||||
|
||||
} catch (err) {
|
||||
return res.status(500).send({ status: "failed", message: err.message });
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// Delete user
|
||||
exports.deleteUser = async (req, res) => {
|
||||
try {
|
||||
|
||||
85
app/controllers/quarterlyWindowsConfiguration.controller.js
Normal file
85
app/controllers/quarterlyWindowsConfiguration.controller.js
Normal file
@ -0,0 +1,85 @@
|
||||
const db = require("../models");
|
||||
const QuarterlyWindowsConfiguration = db.QuarterlyWindowsConfiguration;
|
||||
|
||||
// Create new configuration
|
||||
exports.createConfig = async (req, res) => {
|
||||
try {
|
||||
const data = await QuarterlyWindowsConfiguration.create(req.body);
|
||||
res.status(201).send({
|
||||
status: "success",
|
||||
message: "Quarterly window configuration created successfully",
|
||||
data,
|
||||
});
|
||||
} catch (err) {
|
||||
res.status(500).send({ status: "failed", message: err.message });
|
||||
}
|
||||
};
|
||||
|
||||
// Get all configurations
|
||||
exports.getAllConfigs = async (req, res) => {
|
||||
try {
|
||||
const data = await QuarterlyWindowsConfiguration.findAll({
|
||||
order: [["year", "DESC"]],
|
||||
});
|
||||
res
|
||||
.status(200)
|
||||
.send({ status: "success", message: "Fetched successfully", data });
|
||||
} catch (err) {
|
||||
res.status(500).send({ status: "failed", message: err.message });
|
||||
}
|
||||
};
|
||||
|
||||
// Get single configuration by ID
|
||||
exports.getConfigById = async (req, res) => {
|
||||
try {
|
||||
const data = await QuarterlyWindowsConfiguration.findByPk(req.params.id);
|
||||
if (!data)
|
||||
return res
|
||||
.status(404)
|
||||
.send({ status: "failed", message: "Record not found" });
|
||||
|
||||
res
|
||||
.status(200)
|
||||
.send({ status: "success", message: "Fetched successfully", data });
|
||||
} catch (err) {
|
||||
res.status(500).send({ status: "failed", message: err.message });
|
||||
}
|
||||
};
|
||||
|
||||
// Update configuration
|
||||
exports.updateConfig = async (req, res) => {
|
||||
try {
|
||||
const [updated] = await QuarterlyWindowsConfiguration.update(req.body, {
|
||||
where: { id: req.params.id },
|
||||
});
|
||||
if (!updated)
|
||||
return res
|
||||
.status(404)
|
||||
.send({ status: "failed", message: "Record not found" });
|
||||
|
||||
res
|
||||
.status(200)
|
||||
.send({ status: "success", message: "Updated successfully" });
|
||||
} catch (err) {
|
||||
res.status(500).send({ status: "failed", message: err.message });
|
||||
}
|
||||
};
|
||||
|
||||
// Delete configuration
|
||||
exports.deleteConfig = async (req, res) => {
|
||||
try {
|
||||
const deleted = await QuarterlyWindowsConfiguration.destroy({
|
||||
where: { id: req.params.id },
|
||||
});
|
||||
if (!deleted)
|
||||
return res
|
||||
.status(404)
|
||||
.send({ status: "failed", message: "Record not found" });
|
||||
|
||||
res
|
||||
.status(200)
|
||||
.send({ status: "success", message: "Deleted successfully" });
|
||||
} catch (err) {
|
||||
res.status(500).send({ status: "failed", message: err.message });
|
||||
}
|
||||
};
|
||||
@ -3,6 +3,7 @@ const Submission = db.Submission;
|
||||
const SubmissionProduct = db.SubmissionProduct;
|
||||
const Product = db.Product;
|
||||
const { Op } = require("sequelize");
|
||||
const { sendEmail } = require("../services/email.service");
|
||||
|
||||
|
||||
|
||||
|
||||
@ -24,6 +24,7 @@ db.SubmissionProduct = require("./submissionProduct.model")(sequelize, DataTypes
|
||||
db.UnitMaster = require("./UnitMaster.model")(sequelize, DataTypes);
|
||||
db.SubmissionDeadline = require("./submissionDeadline.model")(sequelize, DataTypes);
|
||||
db.NotificationTemplate = require("./notificationTemplate.model")(sequelize, DataTypes);
|
||||
db.QuarterlyWindowsConfiguration = require("./quarterlyWindowsConfiguration.model")(sequelize, DataTypes);
|
||||
|
||||
|
||||
// Associations
|
||||
|
||||
59
app/models/quarterlyWindowsConfiguration.model.js
Normal file
59
app/models/quarterlyWindowsConfiguration.model.js
Normal file
@ -0,0 +1,59 @@
|
||||
module.exports = (sequelize, DataTypes) => {
|
||||
const QuarterlyWindowsConfiguration = sequelize.define(
|
||||
"quarterly_windows_configuration_master",
|
||||
{
|
||||
id: {
|
||||
type: DataTypes.INTEGER,
|
||||
autoIncrement: true,
|
||||
primaryKey: true,
|
||||
},
|
||||
year: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: false,
|
||||
},
|
||||
quarter: {
|
||||
type: DataTypes.STRING(10),
|
||||
allowNull: false,
|
||||
},
|
||||
start_date: {
|
||||
type: DataTypes.DATE,
|
||||
allowNull: false,
|
||||
},
|
||||
end_date: {
|
||||
type: DataTypes.DATE,
|
||||
allowNull: false,
|
||||
},
|
||||
grace_periods_days: {
|
||||
type: DataTypes.INTEGER,
|
||||
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: "quarterly_windows_configuration_master",
|
||||
timestamps: false,
|
||||
}
|
||||
);
|
||||
|
||||
return QuarterlyWindowsConfiguration;
|
||||
};
|
||||
|
||||
@ -16,6 +16,7 @@ const submissionController = require("../controllers/submission.controller");
|
||||
const ConfigController = require("../controllers/config.controller");
|
||||
const dashboardController = require("../controllers/dashboard.controller");
|
||||
const notificationTemplateController = require("../controllers/notificationTemplate.controller");
|
||||
const quarterlyWindowsController = require("../controllers/quarterlyWindowsConfiguration.controller");
|
||||
|
||||
|
||||
|
||||
@ -48,6 +49,8 @@ const notificationTemplateController = require("../controllers/notificationTempl
|
||||
* description:
|
||||
* - name: Notification Templates
|
||||
* description: Manage notification templates
|
||||
* - name: Quarterly Windows Configuration
|
||||
* description: Manage quarterly windows configuration master data
|
||||
*/
|
||||
|
||||
|
||||
@ -652,6 +655,55 @@ router.get("/establishment-users/:id",[verifySignature, verifyToken], establishm
|
||||
*/
|
||||
router.put("/establishment-users/:id",[verifySignature, verifyToken], establishmentUserController.updateUser);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/establishment-users/{id}/change-password:
|
||||
* put:
|
||||
* summary: Change Establishment User Password
|
||||
* description: Allows an establishment user to change their password after verifying the old password.
|
||||
* tags: [Establishment Users]
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* parameters:
|
||||
* - name: id
|
||||
* in: path
|
||||
* required: true
|
||||
* description: ID of the establishment user
|
||||
* schema:
|
||||
* type: integer
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* required:
|
||||
* - old_password
|
||||
* - new_password
|
||||
* - confirm_password
|
||||
* properties:
|
||||
* old_password:
|
||||
* type: string
|
||||
* example: "OldPassword@123"
|
||||
* new_password:
|
||||
* type: string
|
||||
* example: "NewPassword@123"
|
||||
* confirm_password:
|
||||
* type: string
|
||||
* example: "NewPassword@123"
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Password updated successfully
|
||||
* 400:
|
||||
* description: Validation or password mismatch error
|
||||
* 404:
|
||||
* description: User not found
|
||||
* 500:
|
||||
* description: Internal server error
|
||||
*/
|
||||
router.put("/establishment-users/:id/change-password", establishmentUserController.changeUserPassword);
|
||||
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/establishment-users/{id}:
|
||||
@ -1832,11 +1884,14 @@ router.get("/establishment_dashboard",[verifySignature, verifyToken],dashboardCo
|
||||
* get:
|
||||
* summary: Get all notification templates
|
||||
* tags: [Notification Templates]
|
||||
* security:
|
||||
* - appSignature: []
|
||||
* - bearerAuth: []
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Fetched successfully
|
||||
*/
|
||||
router.get("/notification_templates", notificationTemplateController.getAllTemplates);
|
||||
router.get("/notification_templates",[verifySignature, verifyToken], notificationTemplateController.getAllTemplates);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
@ -1844,6 +1899,9 @@ router.get("/notification_templates", notificationTemplateController.getAllTempl
|
||||
* get:
|
||||
* summary: Get a single notification template
|
||||
* tags: [Notification Templates]
|
||||
* security:
|
||||
* - appSignature: []
|
||||
* - bearerAuth: []
|
||||
* parameters:
|
||||
* - name: id
|
||||
* in: path
|
||||
@ -1852,31 +1910,9 @@ router.get("/notification_templates", notificationTemplateController.getAllTempl
|
||||
* 200:
|
||||
* description: Fetched successfully
|
||||
*/
|
||||
router.get("/notification_templates/:id", notificationTemplateController.getTemplateById);
|
||||
router.get("/notification_templates/:id",[verifySignature, verifyToken], notificationTemplateController.getTemplateById);
|
||||
|
||||
|
||||
// /**
|
||||
// * @swagger
|
||||
// * /api/notification_templates:
|
||||
// * post:
|
||||
// * summary: Create a new notification template
|
||||
// * tags: [Notification Templates]
|
||||
// * requestBody:
|
||||
// * required: true
|
||||
// * content:
|
||||
// * application/json:
|
||||
// * schema:
|
||||
// * type: object
|
||||
// * properties:
|
||||
// * title: { type: string }
|
||||
// * template_code: { type: string }
|
||||
// * subject: { type: string }
|
||||
// * body_html: { type: string }
|
||||
// * placeholders: { type: array, items: { type: string } }
|
||||
// * responses:
|
||||
// * 201:
|
||||
// * description: Created successfully
|
||||
// */
|
||||
// router.post("/notification_templates", notificationTemplateController.createTemplate);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
@ -1884,6 +1920,9 @@ router.get("/notification_templates/:id", notificationTemplateController.getTemp
|
||||
* put:
|
||||
* summary: Update a notification template
|
||||
* tags: [Notification Templates]
|
||||
* security:
|
||||
* - appSignature: []
|
||||
* - bearerAuth: []
|
||||
* parameters:
|
||||
* - name: id
|
||||
* in: path
|
||||
@ -1903,23 +1942,113 @@ router.get("/notification_templates/:id", notificationTemplateController.getTemp
|
||||
* 200:
|
||||
* description: Updated successfully
|
||||
*/
|
||||
router.put("/notification_templates/:id", notificationTemplateController.updateTemplate);
|
||||
router.put("/notification_templates/:id",[verifySignature, verifyToken], notificationTemplateController.updateTemplate);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/quarterly_windows:
|
||||
* post:
|
||||
* summary: Create new quarterly configuration
|
||||
* tags: [Quarterly Windows Configuration]
|
||||
* security:
|
||||
* - appSignature: []
|
||||
* - bearerAuth: []
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* year: { type: integer }
|
||||
* quarter: { type: string, example: "Q1" }
|
||||
* start_date: { type: string, format: date }
|
||||
* end_date: { type: string, format: date }
|
||||
* grace_periods_days: { type: integer }
|
||||
* is_active: { type: boolean }
|
||||
* responses:
|
||||
* 201:
|
||||
* description: Created successfully
|
||||
*/
|
||||
router.post("/quarterly_windows",[verifySignature, verifyToken], quarterlyWindowsController.createConfig);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/quarterly_windows:
|
||||
* get:
|
||||
* summary: Get all quarterly configurations
|
||||
* tags: [Quarterly Windows Configuration]
|
||||
* security:
|
||||
* - appSignature: []
|
||||
* - bearerAuth: []
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Fetched successfully
|
||||
*/
|
||||
router.get("/quarterly_windows",[verifySignature, verifyToken], quarterlyWindowsController.getAllConfigs);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/quarterly_windows/{id}:
|
||||
* get:
|
||||
* summary: Get configuration by ID
|
||||
* tags: [Quarterly Windows Configuration]
|
||||
* security:
|
||||
* - appSignature: []
|
||||
* - bearerAuth: []
|
||||
* parameters:
|
||||
* - name: id
|
||||
* in: path
|
||||
* required: true
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Fetched successfully
|
||||
*/
|
||||
router.get("/quarterly_windows/:id",[verifySignature, verifyToken], quarterlyWindowsController.getConfigById);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/quarterly_windows/{id}:
|
||||
* put:
|
||||
* summary: Update configuration by ID
|
||||
* tags: [Quarterly Windows Configuration]
|
||||
* security:
|
||||
* - appSignature: []
|
||||
* - bearerAuth: []
|
||||
* parameters:
|
||||
* - name: id
|
||||
* in: path
|
||||
* required: true
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Updated successfully
|
||||
*/
|
||||
router.put("/quarterly_windows/:id",[verifySignature, verifyToken], quarterlyWindowsController.updateConfig);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// /**
|
||||
// * @swagger
|
||||
// * /api/notification_templates/{id}:
|
||||
// * delete:
|
||||
// * summary: Delete a notification template
|
||||
// * tags: [Notification Templates]
|
||||
// * parameters:
|
||||
// * - name: id
|
||||
// * in: path
|
||||
// * required: true
|
||||
// * responses:
|
||||
// * 200:
|
||||
// * description: Deleted successfully
|
||||
// */
|
||||
// router.delete("/notification_templates/:id", notificationTemplateController.deleteTemplate);
|
||||
|
||||
|
||||
|
||||
|
||||
95
app/services/email.service.js
Normal file
95
app/services/email.service.js
Normal file
@ -0,0 +1,95 @@
|
||||
const nodemailer = require("nodemailer");
|
||||
const { NotificationTemplate } = require("../models"); // adjust path if needed
|
||||
const logger = require("../services/logger");
|
||||
require("dotenv").config();
|
||||
|
||||
const transporter = nodemailer.createTransport({
|
||||
host: process.env.SMTP_HOST,
|
||||
port: process.env.SMTP_PORT,
|
||||
secure: process.env.SMTP_SECURE === "true", // true for 465, false for 587
|
||||
auth: {
|
||||
user: process.env.SMTP_USER,
|
||||
pass: process.env.SMTP_PASS,
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* Replace placeholders in template HTML with actual data
|
||||
*/
|
||||
function replacePlaceholders(templateHtml, data) {
|
||||
let html = templateHtml;
|
||||
for (const key in data) {
|
||||
const regex = new RegExp(`{{\\s*${key}\\s*}}`, "g");
|
||||
html = html.replace(regex, data[key]);
|
||||
}
|
||||
return html;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
exports.sendEmail = async (to, templateCode, data = {}) => {
|
||||
const logPrefix = `[EMAIL SERVICE][${new Date().toISOString()}]`;
|
||||
|
||||
logger.info(`${logPrefix} → Starting email send process`);
|
||||
logger.info(`${logPrefix} → Template Code: ${templateCode}`);
|
||||
logger.info(`${logPrefix} → Recipient: ${to}`);
|
||||
logger.info(`${logPrefix} → Placeholder Data: ${JSON.stringify(data)}`);
|
||||
|
||||
try {
|
||||
// 1. Fetch template from DB
|
||||
const template = await NotificationTemplate.findOne({
|
||||
where: { template_code: templateCode },
|
||||
});
|
||||
|
||||
if (!template) {
|
||||
logger.error(`${logPrefix} ❌ Template not found for code: ${templateCode}`);
|
||||
throw new Error(`Template not found for code: ${templateCode}`);
|
||||
}
|
||||
|
||||
// 2. Replace placeholders
|
||||
const html = replacePlaceholders(template.template_html, data);
|
||||
|
||||
// 3. Prepare mail options
|
||||
const mailOptions = {
|
||||
from: process.env.SMTP_FROM || process.env.SMTP_USER,
|
||||
to,
|
||||
subject: template.subject || "Notification",
|
||||
html,
|
||||
};
|
||||
|
||||
logger.info(`${logPrefix} ✉️ Sending email using SMTP...`);
|
||||
|
||||
// 4. Send mail
|
||||
const info = await transporter.sendMail(mailOptions);
|
||||
|
||||
logger.info(`${logPrefix} ✅ Email sent successfully`);
|
||||
logger.info(`${logPrefix} Message ID: ${info.messageId}`);
|
||||
logger.info(`${logPrefix} Response: ${info.response}`);
|
||||
|
||||
return {
|
||||
status: "success",
|
||||
message: "Email sent successfully",
|
||||
messageId: info.messageId,
|
||||
response: info.response,
|
||||
to,
|
||||
subject: template.subject,
|
||||
templateCode,
|
||||
};
|
||||
|
||||
} catch (err) {
|
||||
logger.error(`${logPrefix} ❌ Email sending failed`);
|
||||
logger.error(`${logPrefix} Error: ${err.message}`);
|
||||
if (err.stack) logger.error(`${logPrefix} Stack: ${err.stack}`);
|
||||
|
||||
return {
|
||||
status: "failed",
|
||||
message: err.message,
|
||||
templateCode,
|
||||
to,
|
||||
error: err.stack || err.message,
|
||||
};
|
||||
}
|
||||
};
|
||||
@ -1,15 +1,32 @@
|
||||
const { createLogger, transports, format } = require('winston');
|
||||
const path = require('path');
|
||||
const { createLogger, format, transports } = require("winston");
|
||||
const path = require("path");
|
||||
require("winston-daily-rotate-file");
|
||||
|
||||
const logDir = path.join(__dirname, "../writable/logs");
|
||||
|
||||
const dailyRotateFileTransport = new transports.DailyRotateFile({
|
||||
filename: path.join(logDir, "app-%DATE%.log"),
|
||||
datePattern: "YYYY-MM-DD",
|
||||
zippedArchive: false,
|
||||
maxSize: "20m",
|
||||
maxFiles: "30d", // keep logs for 30 days
|
||||
});
|
||||
|
||||
const logger = createLogger({
|
||||
level: "info",
|
||||
format: format.combine(
|
||||
format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),
|
||||
format.printf(info => `${info.timestamp} ${info.level}: ${info.message}`)
|
||||
format.timestamp({ format: "YYYY-MM-DD HH:mm:ss" }),
|
||||
format.printf(
|
||||
(info) => `${info.timestamp} [${info.level.toUpperCase()}]: ${info.message}`
|
||||
)
|
||||
),
|
||||
transports: [
|
||||
new transports.File({ filename: path.join(__dirname, '../writable/logs/app.log') }),
|
||||
new transports.Console()
|
||||
]
|
||||
dailyRotateFileTransport,
|
||||
new transports.Console({
|
||||
format: format.combine(format.colorize(), format.simple()),
|
||||
}),
|
||||
],
|
||||
exitOnError: false,
|
||||
});
|
||||
|
||||
module.exports = logger;
|
||||
|
||||
45
package-lock.json
generated
45
package-lock.json
generated
@ -19,11 +19,13 @@
|
||||
"morgan": "^1.10.1",
|
||||
"multer": "^2.0.2",
|
||||
"mysql2": "^3.15.2",
|
||||
"nodemailer": "^7.0.10",
|
||||
"nodemon": "^3.1.10",
|
||||
"sequelize": "^6.37.7",
|
||||
"swagger-jsdoc": "^6.2.8",
|
||||
"swagger-ui-express": "^5.0.1",
|
||||
"winston": "^3.18.3"
|
||||
"winston": "^3.18.3",
|
||||
"winston-daily-rotate-file": "^5.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@apidevtools/json-schema-ref-parser": {
|
||||
@ -670,6 +672,14 @@
|
||||
"resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz",
|
||||
"integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw=="
|
||||
},
|
||||
"node_modules/file-stream-rotator": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/file-stream-rotator/-/file-stream-rotator-0.6.1.tgz",
|
||||
"integrity": "sha512-u+dBid4PvZw17PmDeRcNOtCP9CCK/9lRN2w+r1xIS7yOL9JFrIBKTvrYsxT4P0pGtThYTn++QS5ChHaUov3+zQ==",
|
||||
"dependencies": {
|
||||
"moment": "^2.29.1"
|
||||
}
|
||||
},
|
||||
"node_modules/fill-range": {
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
|
||||
@ -1400,6 +1410,14 @@
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/nodemailer": {
|
||||
"version": "7.0.10",
|
||||
"resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-7.0.10.tgz",
|
||||
"integrity": "sha512-Us/Se1WtT0ylXgNFfyFSx4LElllVLJXQjWi2Xz17xWw7amDKO2MLtFnVp1WACy7GkVGs+oBlRopVNUzlrGSw1w==",
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/nodemon": {
|
||||
"version": "3.1.10",
|
||||
"resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.10.tgz",
|
||||
@ -1443,6 +1461,14 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/object-hash": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz",
|
||||
"integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==",
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/object-inspect": {
|
||||
"version": "1.13.4",
|
||||
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
|
||||
@ -2112,6 +2138,23 @@
|
||||
"node": ">= 12.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/winston-daily-rotate-file": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/winston-daily-rotate-file/-/winston-daily-rotate-file-5.0.0.tgz",
|
||||
"integrity": "sha512-JDjiXXkM5qvwY06733vf09I2wnMXpZEhxEVOSPenZMii+g7pcDcTBt2MRugnoi8BwVSuCT2jfRXBUy+n1Zz/Yw==",
|
||||
"dependencies": {
|
||||
"file-stream-rotator": "^0.6.1",
|
||||
"object-hash": "^3.0.0",
|
||||
"triple-beam": "^1.4.1",
|
||||
"winston-transport": "^4.7.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"winston": "^3"
|
||||
}
|
||||
},
|
||||
"node_modules/winston-transport": {
|
||||
"version": "4.9.0",
|
||||
"resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.9.0.tgz",
|
||||
|
||||
@ -21,10 +21,12 @@
|
||||
"morgan": "^1.10.1",
|
||||
"multer": "^2.0.2",
|
||||
"mysql2": "^3.15.2",
|
||||
"nodemailer": "^7.0.10",
|
||||
"nodemon": "^3.1.10",
|
||||
"sequelize": "^6.37.7",
|
||||
"swagger-jsdoc": "^6.2.8",
|
||||
"swagger-ui-express": "^5.0.1",
|
||||
"winston": "^3.18.3"
|
||||
"winston": "^3.18.3",
|
||||
"winston-daily-rotate-file": "^5.0.0"
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user