erp_be/src/modules/settings/settings.service.js

249 lines
7.0 KiB
JavaScript

const path = require('path');
const fs = require('fs');
const prisma = require('../../config/prisma');
const env = require('../../config/env');
const ApiError = require('../../utils/ApiError');
const auditLog = require('../../utils/auditLog');
const { encrypt, decrypt } = require('../../utils/encryption');
const COMPANY_ID = BigInt(1);
const EMAIL_SETTINGS_ID = BigInt(1);
const buildPublicUrl = (filePath) => {
if (!filePath) return null;
const normalized = filePath.replace(/\\/g, '/');
return normalized.startsWith('/') ? normalized : `/${normalized}`;
};
const unlinkIfExists = (relativePath) => {
if (!relativePath) return;
const absolute = path.resolve(process.cwd(), relativePath);
if (fs.existsSync(absolute)) {
fs.unlinkSync(absolute);
}
};
const sanitizeCompany = (row) => {
if (!row) return null;
return {
id: row.id,
org_name: row.org_name,
gstin: row.gstin,
mobile: row.mobile,
email: row.email,
website: row.website,
logo_path: row.logo_path,
logo_url: buildPublicUrl(row.logo_path),
favicon_path: row.favicon_path,
favicon_url: buildPublicUrl(row.favicon_path),
address: row.address,
city: row.city,
state: row.state,
pincode: row.pincode,
created_at: row.created_at,
updated_at: row.updated_at,
};
};
const sanitizeEmailSettings = (row) => {
if (!row) return null;
return {
id: row.id,
smtp_host: row.smtp_host,
smtp_port: row.smtp_port,
smtp_username: row.smtp_username,
sender_email: row.sender_email,
sender_name: row.sender_name,
has_smtp_password: Boolean(row.smtp_password),
created_at: row.created_at,
updated_at: row.updated_at,
};
};
const getOrCreateCompany = async () => {
return prisma.company.upsert({
where: { id: COMPANY_ID },
create: { id: COMPANY_ID, org_name: '' },
update: {},
});
};
const getOrCreateEmailSettings = async () => {
return prisma.email_settings.upsert({
where: { id: EMAIL_SETTINGS_ID },
create: { id: EMAIL_SETTINGS_ID, smtp_port: 587 },
update: {},
});
};
const getCompany = async () => sanitizeCompany(await getOrCreateCompany());
const updateCompany = async (payload, userId, requestId) => {
const existing = await getOrCreateCompany();
const data = {
...(payload.org_name !== undefined ? { org_name: payload.org_name || null } : {}),
...(payload.gstin !== undefined ? { gstin: payload.gstin || null } : {}),
...(payload.mobile !== undefined ? { mobile: payload.mobile || null } : {}),
...(payload.email !== undefined ? { email: payload.email || null } : {}),
...(payload.website !== undefined ? { website: payload.website || null } : {}),
...(payload.address !== undefined ? { address: payload.address || null } : {}),
...(payload.city !== undefined ? { city: payload.city || null } : {}),
...(payload.state !== undefined ? { state: payload.state || null } : {}),
...(payload.pincode !== undefined ? { pincode: payload.pincode || null } : {}),
updated_by: userId ? BigInt(userId) : null,
};
const updated = await prisma.company.update({
where: { id: COMPANY_ID },
data,
});
await auditLog({
tableName: 'company',
recordId: COMPANY_ID,
action: 'UPDATE',
oldValue: sanitizeCompany(existing),
newValue: sanitizeCompany(updated),
userId,
requestId,
});
return sanitizeCompany(updated);
};
const updateCompanyLogo = async (file, userId, requestId) => {
if (!file) throw new ApiError(400, 'Logo file is required');
const existing = await getOrCreateCompany();
unlinkIfExists(existing.logo_path);
const logoPath = path.join(env.UPLOAD_DIR, file.filename).replace(/\\/g, '/');
const updated = await prisma.company.update({
where: { id: COMPANY_ID },
data: {
logo_path: logoPath,
updated_by: userId ? BigInt(userId) : null,
},
});
await auditLog({
tableName: 'company',
recordId: COMPANY_ID,
action: 'UPDATE',
oldValue: { logo_path: existing.logo_path },
newValue: { logo_path: updated.logo_path },
userId,
requestId,
});
return sanitizeCompany(updated);
};
const updateCompanyFavicon = async (file, userId, requestId) => {
if (!file) throw new ApiError(400, 'Favicon file is required');
const existing = await getOrCreateCompany();
unlinkIfExists(existing.favicon_path);
const faviconPath = path.join(env.UPLOAD_DIR, file.filename).replace(/\\/g, '/');
const updated = await prisma.company.update({
where: { id: COMPANY_ID },
data: {
favicon_path: faviconPath,
updated_by: userId ? BigInt(userId) : null,
},
});
await auditLog({
tableName: 'company',
recordId: COMPANY_ID,
action: 'UPDATE',
oldValue: { favicon_path: existing.favicon_path },
newValue: { favicon_path: updated.favicon_path },
userId,
requestId,
});
return sanitizeCompany(updated);
};
const getEmailSettings = async () => sanitizeEmailSettings(await getOrCreateEmailSettings());
const updateEmailSettings = async (payload, userId, requestId) => {
const existing = await getOrCreateEmailSettings();
const data = {
...(payload.smtp_host !== undefined ? { smtp_host: payload.smtp_host || null } : {}),
...(payload.smtp_port !== undefined ? { smtp_port: payload.smtp_port } : {}),
...(payload.smtp_username !== undefined ? { smtp_username: payload.smtp_username || null } : {}),
...(payload.sender_email !== undefined ? { sender_email: payload.sender_email || null } : {}),
...(payload.sender_name !== undefined ? { sender_name: payload.sender_name || null } : {}),
updated_by: userId ? BigInt(userId) : null,
};
if (payload.smtp_password !== undefined && payload.smtp_password !== '') {
data.smtp_password = encrypt(payload.smtp_password);
}
const updated = await prisma.email_settings.update({
where: { id: EMAIL_SETTINGS_ID },
data,
});
await auditLog({
tableName: 'email_settings',
recordId: EMAIL_SETTINGS_ID,
action: 'UPDATE',
oldValue: sanitizeEmailSettings(existing),
newValue: sanitizeEmailSettings(updated),
userId,
requestId,
});
return sanitizeEmailSettings(updated);
};
const getCompanyForDocuments = async () => {
const row = await getOrCreateCompany();
return {
name: row.org_name || 'Company',
address: row.address || '',
city: row.city || '',
state: row.state || '',
pincode: row.pincode || '',
gstin: row.gstin || '',
phone: row.mobile || '',
email: row.email || '',
website: row.website || '',
logo_url: buildPublicUrl(row.logo_path),
favicon_url: buildPublicUrl(row.favicon_path),
};
};
const getSmtpConfig = async () => {
const row = await getOrCreateEmailSettings();
if (!row.smtp_host) return null;
return {
host: row.smtp_host,
port: row.smtp_port,
username: row.smtp_username,
password: row.smtp_password ? decrypt(row.smtp_password) : null,
fromEmail: row.sender_email,
fromName: row.sender_name,
};
};
module.exports = {
getCompany,
updateCompany,
updateCompanyLogo,
updateCompanyFavicon,
getEmailSettings,
updateEmailSettings,
getCompanyForDocuments,
getSmtpConfig,
};