Files
dmail-api/src/modules/mail-server/services/mail-server.service.ts
T
2025-07-02 21:05:13 +03:30

346 lines
10 KiB
TypeScript

import { Injectable, Logger } from "@nestjs/common";
import { Observable, catchError, map, switchMap, throwError } from "rxjs";
import { WildDuckAddressesService } from "./wildduck-addresses.service";
import { WildDuckApplicationPasswordsService } from "./wildduck-application-passwords.service";
import { WildDuckArchiveService } from "./wildduck-archive.service";
import { WildDuckAuditService } from "./wildduck-audit.service";
import { WildDuckAuthService } from "./wildduck-auth.service";
import { WildDuckAutorepliesService } from "./wildduck-autoreplies.service";
import { WildDuckDKIMService } from "./wildduck-dkim.service";
import { WildDuckExportService } from "./wildduck-export.service";
import { WildDuckFiltersService } from "./wildduck-filters.service";
import { WildDuckHealthService } from "./wildduck-health.service";
import { WildDuckMailboxesService } from "./wildduck-mailboxes.service";
import { WildDuckMessagesService } from "./wildduck-messages.service";
import { WildDuckSettingsService } from "./wildduck-settings.service";
import { WildDuckStorageService } from "./wildduck-storage.service";
import { WildDuckSubmissionService } from "./wildduck-submission.service";
import { WildDuckTwoFactorService } from "./wildduck-two-factor.service";
import { WildDuckUsersService } from "./wildduck-users.service";
import { WildDuckWebhooksService } from "./wildduck-webhooks.service";
@Injectable()
export class MailServerService {
private readonly logger = new Logger(MailServerService.name);
constructor(
private readonly usersService: WildDuckUsersService,
private readonly messagesService: WildDuckMessagesService,
private readonly mailboxesService: WildDuckMailboxesService,
private readonly addressesService: WildDuckAddressesService,
private readonly authService: WildDuckAuthService,
private readonly filtersService: WildDuckFiltersService,
private readonly storageService: WildDuckStorageService,
private readonly submissionService: WildDuckSubmissionService,
private readonly settingsService: WildDuckSettingsService,
private readonly dkimService: WildDuckDKIMService,
private readonly webhooksService: WildDuckWebhooksService,
private readonly healthService: WildDuckHealthService,
private readonly auditService: WildDuckAuditService,
private readonly archiveService: WildDuckArchiveService,
private readonly applicationPasswordsService: WildDuckApplicationPasswordsService,
private readonly autorepliesService: WildDuckAutorepliesService,
private readonly exportService: WildDuckExportService,
private readonly twoFactorService: WildDuckTwoFactorService,
) {}
// Expose individual services for direct access
get users() {
return this.usersService;
}
get messages() {
return this.messagesService;
}
get mailboxes() {
return this.mailboxesService;
}
get addresses() {
return this.addressesService;
}
get auth() {
return this.authService;
}
get filters() {
return this.filtersService;
}
get storage() {
return this.storageService;
}
get submission() {
return this.submissionService;
}
get settings() {
return this.settingsService;
}
get dkim() {
return this.dkimService;
}
get webhooks() {
return this.webhooksService;
}
get health() {
return this.healthService;
}
get audit() {
return this.auditService;
}
get archive() {
return this.archiveService;
}
get applicationPasswords() {
return this.applicationPasswordsService;
}
get autoreplies() {
return this.autorepliesService;
}
get export() {
return this.exportService;
}
get twoFactor() {
return this.twoFactorService;
}
/**
* High-level method to create a complete email account
*/
createEmailAccount(data: { username: string; password: string; email?: string; name?: string; quota?: number }): Observable<{
user: { id: string };
address?: { id: string };
}> {
this.logger.log(`Creating email account for user: ${data.username}`);
return this.usersService
.createUser({
username: data.username,
password: data.password,
address: data.email,
name: data.name,
quota: data.quota,
})
.pipe(
map((userResult) => {
this.logger.log(`User created with ID: ${userResult.id}`);
return { user: { id: userResult.id } };
}),
catchError((error) => {
this.logger.error(`Failed to create email account: ${error.message}`);
return throwError(() => error);
}),
);
}
/**
* High-level method to get user's full mailbox structure with message counts
*/
getUserMailboxes(userId: string): Observable<any> {
this.logger.log(`Getting mailboxes for user: ${userId}`);
return this.mailboxesService.listMailboxes(userId, { counters: true }).pipe(
map((result) => {
this.logger.log(`Found ${result.results.length} mailboxes for user: ${userId}`);
return result.results;
}),
catchError((error) => {
this.logger.error(`Failed to get mailboxes: ${error.message}`);
return throwError(() => error);
}),
);
}
/**
* High-level method to authenticate user and get account info
*/
authenticateAndGetUser(
username: string,
password: string,
): Observable<{
auth: any;
user: any;
}> {
this.logger.log(`Authenticating user: ${username}`);
return this.authService.authenticate({ username, password }).pipe(
switchMap((authResult) => {
if (!authResult.success) {
throw new Error("Authentication failed");
}
this.logger.log(`User authenticated: ${authResult.id}`);
// Get user details after successful authentication
return this.usersService.getUser(authResult.id).pipe(
map((userDetails) => ({
auth: authResult,
user: userDetails,
})),
);
}),
catchError((error) => {
this.logger.error(`Authentication failed: ${error.message}`);
return throwError(() => error);
}),
);
}
/**
* High-level method to send a message
*/
sendMessage(
userId: string,
messageData: {
from: { name?: string; address: string };
to: Array<{ name?: string; address: string }>;
subject: string;
text?: string;
html?: string;
},
): Observable<{ id: string; queueId: string }> {
this.logger.log(`Sending message for user: ${userId}`);
return this.submissionService.submitMessage(userId, messageData).pipe(
map((result) => {
this.logger.log(`Message submitted with ID: ${result.messageId}`);
return { id: result.messageId, queueId: result.queueId };
}),
catchError((error) => {
this.logger.error(`Failed to send message: ${error.message}`);
return throwError(() => error);
}),
);
}
/**
* High-level method to get user's recent messages
*/
getRecentMessages(userId: string, mailboxId: string, limit: number = 20): Observable<any[]> {
this.logger.log(`Getting recent messages for user: ${userId}`);
return this.messagesService.listMessages(userId, mailboxId, { limit, order: "desc" }).pipe(
map((result) => {
this.logger.log(`Found ${result.results.length} messages`);
return result.results;
}),
catchError((error) => {
this.logger.error(`Failed to get messages: ${error.message}`);
return throwError(() => error);
}),
);
}
/**
* High-level method to search across all user's messages
*/
searchAllMessages(userId: string, searchQuery: string, limit: number = 50): Observable<any[]> {
this.logger.log(`Searching messages for user: ${userId}, query: ${searchQuery}`);
return this.messagesService.searchMessages(userId, { query: searchQuery, limit }).pipe(
map((result) => {
this.logger.log(`Found ${result.results.length} matching messages`);
return result.results;
}),
catchError((error) => {
this.logger.error(`Search failed: ${error.message}`);
return throwError(() => error);
}),
);
}
/**
* High-level method to check server health and get system status
*/
getSystemStatus(): Observable<{
health: any;
storage: any;
}> {
this.logger.log("Getting system status");
return this.healthService.getHealthStatus().pipe(
switchMap((healthStatus) => {
return this.storageService.getStorageInfo().pipe(
map((storageInfo) => ({
health: healthStatus,
storage: storageInfo,
})),
);
}),
catchError((error) => {
this.logger.error(`Failed to get system status: ${error.message}`);
return throwError(() => error);
}),
);
}
/**
* High-level method to setup complete 2FA for user
*/
setup2FAForUser(
userId: string,
issuer?: string,
): Observable<{
qrcode: string;
backupCodes: string[];
}> {
this.logger.log(`Setting up 2FA for user: ${userId}`);
return this.twoFactorService.setupTOTP(userId, { issuer }).pipe(
switchMap((totpResult) => {
return this.twoFactorService.generateBackupCodes(userId).pipe(
map((backupResult) => ({
qrcode: totpResult.qrcode,
backupCodes: backupResult.codes,
})),
);
}),
catchError((error) => {
this.logger.error(`Failed to setup 2FA: ${error.message}`);
return throwError(() => error);
}),
);
}
/**
* High-level method to create DKIM key and get DNS settings
*/
createDKIMForDomain(
domain: string,
selector: string = "default",
): Observable<{
id: string;
dnsTxt: { name: string; value: string };
}> {
this.logger.log(`Creating DKIM key for domain: ${domain}`);
return this.dkimService.createDKIMKey({ domain, selector, description: `DKIM key for ${domain}` }).pipe(
map((result) => {
this.logger.log(`DKIM key created for domain: ${domain}`);
return {
id: result.id,
dnsTxt: result.dnsTxt,
};
}),
catchError((error) => {
this.logger.error(`Failed to create DKIM key: ${error.message}`);
return throwError(() => error);
}),
);
}
}