initial commit
This commit is contained in:
83
backend/src/services/dms.ts
Normal file
83
backend/src/services/dms.ts
Normal file
@@ -0,0 +1,83 @@
|
||||
import { existsSync } from 'node:fs';
|
||||
import { stat } from 'node:fs/promises';
|
||||
import { join } from 'node:path';
|
||||
import { config } from '../config.js';
|
||||
import { run } from '../utils/shell.js';
|
||||
import { domainFromEmail, localPartFromEmail, normalizeEmail } from '../utils/email.js';
|
||||
|
||||
export interface DmsAccount {
|
||||
email: string;
|
||||
localPart: string;
|
||||
domain: string;
|
||||
}
|
||||
|
||||
function parseAccounts(output: string): DmsAccount[] {
|
||||
const accounts: DmsAccount[] = [];
|
||||
for (const line of output.split('\n')) {
|
||||
const match = line.match(/([A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,})/i);
|
||||
if (!match) continue;
|
||||
const email = normalizeEmail(match[1]);
|
||||
accounts.push({ email, localPart: localPartFromEmail(email), domain: domainFromEmail(email) });
|
||||
}
|
||||
return [...new Map(accounts.map((a) => [a.email, a])).values()].sort((a, b) => a.email.localeCompare(b.email));
|
||||
}
|
||||
|
||||
export class DmsService {
|
||||
async listAccounts(): Promise<DmsAccount[]> {
|
||||
const { stdout } = await run('docker', ['exec', config.dmsContainer, 'setup', 'email', 'list']);
|
||||
return parseAccounts(stdout);
|
||||
}
|
||||
|
||||
async addMailbox(email: string, password: string): Promise<void> {
|
||||
const normalized = normalizeEmail(email);
|
||||
if (config.manageMailUserScript && existsSync(config.manageMailUserScript)) {
|
||||
await run(config.manageMailUserScript, ['add', normalized, password]);
|
||||
return;
|
||||
}
|
||||
await run('docker', ['exec', config.dmsContainer, 'setup', 'email', 'add', normalized, password]);
|
||||
}
|
||||
|
||||
async deleteMailbox(email: string): Promise<void> {
|
||||
const normalized = normalizeEmail(email);
|
||||
if (config.manageMailUserScript && existsSync(config.manageMailUserScript)) {
|
||||
await run(config.manageMailUserScript, ['del', normalized]);
|
||||
return;
|
||||
}
|
||||
await run('docker', ['exec', config.dmsContainer, 'setup', 'email', 'del', normalized]);
|
||||
}
|
||||
|
||||
async updatePassword(email: string, password: string): Promise<void> {
|
||||
const normalized = normalizeEmail(email);
|
||||
// docker-mailserver supports setup email update <email> <password> in current versions.
|
||||
await run('docker', ['exec', config.dmsContainer, 'setup', 'email', 'update', normalized, password]);
|
||||
}
|
||||
|
||||
async syncSesDomain(domain: string): Promise<void> {
|
||||
if (config.manageMailUserScript && existsSync(config.manageMailUserScript)) {
|
||||
await run(config.manageMailUserScript, ['sync', domain.toLowerCase()]);
|
||||
}
|
||||
}
|
||||
|
||||
async getMailboxUsageBytes(email: string): Promise<number> {
|
||||
const domain = domainFromEmail(email);
|
||||
const local = localPartFromEmail(email);
|
||||
const candidates = [
|
||||
join(config.mailDataPath, domain, local),
|
||||
join(config.mailDataPath, domain, `${local}/`),
|
||||
join(config.mailDataPath, domain, `${local}/Maildir`),
|
||||
join(config.mailDataPath, email),
|
||||
];
|
||||
|
||||
for (const p of candidates) {
|
||||
try {
|
||||
await stat(p);
|
||||
const { stdout } = await run('du', ['-sb', p], 60000);
|
||||
const value = parseInt(stdout.trim().split(/\s+/)[0] ?? '0', 10);
|
||||
return Number.isFinite(value) ? value : 0;
|
||||
} catch {
|
||||
// try next path
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user