346 lines
10 KiB
TypeScript
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);
|
|
}),
|
|
);
|
|
}
|
|
}
|