chore: add email and mailbox module

This commit is contained in:
mahyargdz
2025-07-08 09:37:26 +03:30
parent b66114c900
commit b3ab6c8a10
26 changed files with 976 additions and 60 deletions
@@ -0,0 +1,137 @@
import { EntityManager } from "@mikro-orm/core";
import { Injectable, Logger } from "@nestjs/common";
import { firstValueFrom } from "rxjs";
import { MailServerService } from "../../mail-server/services/mail-server.service";
import { Role } from "../../users/entities/role.entity";
import { User } from "../../users/entities/user.entity";
import { RoleEnum } from "../../users/enums/role.enum";
import { PasswordService } from "../../utils/services/password.service";
import { Domain } from "../entities/domain.entity";
@Injectable()
export class DomainAutomationService {
private readonly logger = new Logger(DomainAutomationService.name);
constructor(
private readonly em: EntityManager,
private readonly mailServerService: MailServerService,
private readonly passwordService: PasswordService,
) {}
/**
* Create info@domain.com address when domain is verified
*/
async createInfoAddress(domain: Domain): Promise<void> {
const infoEmail = `info@${domain.name}`;
try {
this.logger.log(`Creating info address for domain: ${domain.name}`);
// Check if info address already exists in local database
const existingUser = await this.em.findOne(User, {
emailAddress: infoEmail,
business: { id: domain.business.id },
});
if (existingUser) {
this.logger.warn(`Info address ${infoEmail} already exists, skipping creation`);
return;
}
// Generate a secure password for the info account
const password = this.generateSecurePassword();
const hashedPassword = await this.passwordService.hashPassword(password);
// Get user role
const userRole = await this.em.findOne(Role, { name: RoleEnum.USER });
if (!userRole) {
throw new Error("User role not found in database");
}
// Create user in mail server (WildDuck)
const mailServerUser = await firstValueFrom(
this.mailServerService.users.createUser({
username: "info",
password,
address: infoEmail,
name: `Info - ${domain.business.name}`,
quota: 1073741824, // 1GB default quota
language: "fa",
}),
);
if (!mailServerUser.success) {
throw new Error(`Failed to create mail server user`);
}
// Create user in local database
const user = this.em.create(User, {
title: `Info - ${domain.business.name}`,
userName: "info",
password: hashedPassword,
emailAddress: infoEmail,
emailEnabled: true,
emailQuota: 1073741824, // 1GB default
wildduckUserId: mailServerUser.id,
displayName: `Info - ${domain.business.name}`,
isActive: true,
business: domain.business,
domain: domain,
role: userRole,
});
await this.em.persistAndFlush(user);
this.logger.log(`Successfully created info address: ${infoEmail} for business ${domain.business.name}`);
this.logger.log(`Info account credentials - Email: ${infoEmail}, Password: [GENERATED]`);
} catch (error) {
this.logger.error(`Failed to create info address for domain ${domain.name}:`, error);
// Don't throw the error to prevent domain verification from failing
// The domain should still be marked as verified even if info address creation fails
}
}
/**
* Generate a secure password for the info account
*/
private generateSecurePassword(): string {
const length = 16;
const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*";
let password = "";
for (let i = 0; i < length; i++) {
password += charset.charAt(Math.floor(Math.random() * charset.length));
}
return password;
}
/**
* Check if info address exists for a domain
*/
async infoAddressExists(domain: Domain): Promise<boolean> {
const infoEmail = `info@${domain.name}`;
const existingUser = await this.em.findOne(User, {
emailAddress: infoEmail,
business: { id: domain.business.id },
});
return !!existingUser;
}
/**
* Get info address details for a domain
*/
async getInfoAddressDetails(domain: Domain): Promise<User | null> {
const infoEmail = `info@${domain.name}`;
return this.em.findOne(
User,
{
emailAddress: infoEmail,
business: { id: domain.business.id },
},
{ populate: ["role", "business", "domain"] },
);
}
}