2weekmail/api/utils/helpers.js
2025-03-19 19:56:57 -05:00

121 lines
3.5 KiB
JavaScript

const fs = require("fs");
const path = require("path");
const { exec } = require("child_process");
const { MAILSERVER_CONTAINER, CONFIG_PATH, ROOT_PATH } = process.env;
const { models } = require(path.join(ROOT_PATH, "./db/db"));
const { Mailbox, Domain } = models;
async function generateMailserverConfig() {
try {
const mailboxes = await Mailbox.query().select("username", "password");
const accountLines = mailboxes.map(
(mailbox) => `${mailbox.username}|${mailbox.password}`
);
fs.writeFileSync(
path.join(CONFIG_PATH, "postfix/postfix-accounts.cf"),
accountLines.join("\n")
);
const domains = await Domain.query().select("domain");
fs.writeFileSync(
path.join(CONFIG_PATH, "postfix/postfix-virtual-mailbox-domains.cf"),
domains.map((d) => d.domain).join("\n")
);
await reloadMailserver();
return true;
} catch (error) {
console.error("Error generating mailserver config:", error);
return false;
}
}
async function reloadMailserver() {
return new Promise((resolve, reject) => {
exec(
`docker exec ${MAILSERVER_CONTAINER} postfix reload`,
(error, stdout, stderr) => {
if (error) {
console.error(`Error reloading Postfix: ${error.message}`);
return reject(error);
}
console.log("Postfix configuration reloaded successfully");
resolve(true);
}
);
});
}
async function hashPassword(password) {
// This is a simple bcrypt hash, for dovecot you might need a different format
// The docker-mailserver typically uses SHA512-CRYPT
return new Promise((resolve, reject) => {
exec(
`docker exec ${MAILSERVER_CONTAINER} doveadm pw -s SHA512-CRYPT -p "${password}"`,
(error, stdout, stderr) => {
if (error) {
console.error(`Error generating password hash: ${error.message}`);
return reject(error);
}
resolve(stdout.trim());
}
);
});
}
async function cleanupOrphanedMailboxes() {
try {
// Get all valid mailboxes from database
const validMailboxes = await Mailbox.query().select("username");
// Create a lookup map for quick checking
const validMailboxMap = {};
validMailboxes.forEach((username) => {
const [user, domain] = username.split("@");
if (!validMailboxMap[domain]) validMailboxMap[domain] = [];
validMailboxMap[domain].push(user);
});
// Scan mail directory
const mailPath = "/var/mail";
const domains = await fs.readdir(mailPath);
for (const domain of domains) {
const domainPath = path.join(mailPath, domain);
const stat = await fs.stat(domainPath);
if (stat.isDirectory()) {
const users = await fs.readdir(domainPath);
for (const user of users) {
const userPath = path.join(domainPath, user);
const userStat = await fs.stat(userPath);
if (userStat.isDirectory()) {
// Check if this is an orphaned mailbox
const isValid =
validMailboxMap[domain] && validMailboxMap[domain].includes(user);
if (!isValid) {
console.log(`Removing orphaned mailbox: ${user}@${domain}`);
await fs.rm(userPath, { recursive: true, force: true });
}
}
}
}
}
console.log("Mailbox cleanup completed");
} catch (error) {
console.error("Error during mailbox cleanup:", error);
}
}
module.exports = {
generateMailserverConfig,
reloadMailserver,
hashPassword,
cleanupOrphanedMailboxes,
};