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