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, };