Initial commit: restructure project with Docker Compose setup
This commit is contained in:
@@ -0,0 +1,11 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { ConfigModule } from '@nestjs/config';
|
||||
import { MailModule } from './mail/mail.module';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
ConfigModule.forRoot({ isGlobal: true }),
|
||||
MailModule,
|
||||
],
|
||||
})
|
||||
export class AppModule {}
|
||||
@@ -0,0 +1,19 @@
|
||||
import { IsString, IsOptional, IsEmail, MinLength } from 'class-validator';
|
||||
|
||||
export class SendApplicationDto {
|
||||
@IsString()
|
||||
@MinLength(1)
|
||||
name: string;
|
||||
|
||||
@IsString()
|
||||
@MinLength(1)
|
||||
phone: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsEmail()
|
||||
email?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
message?: string;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import { Controller, Post, Body } from '@nestjs/common';
|
||||
import { MailService } from './mail.service';
|
||||
import { SendApplicationDto } from './dto/send-application.dto';
|
||||
|
||||
@Controller('api/mail')
|
||||
export class MailController {
|
||||
constructor(private readonly mailService: MailService) {}
|
||||
|
||||
@Post('application')
|
||||
async sendApplication(@Body() dto: SendApplicationDto) {
|
||||
await this.mailService.sendApplication(dto);
|
||||
return { success: true, message: 'Application sent successfully' };
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { MailService } from './mail.service';
|
||||
import { MailController } from './mail.controller';
|
||||
|
||||
@Module({
|
||||
providers: [MailService],
|
||||
controllers: [MailController],
|
||||
})
|
||||
export class MailModule {}
|
||||
@@ -0,0 +1,84 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import * as nodemailer from 'nodemailer';
|
||||
import { SendApplicationDto } from './dto/send-application.dto';
|
||||
|
||||
@Injectable()
|
||||
export class MailService {
|
||||
private transporter: nodemailer.Transporter;
|
||||
|
||||
constructor(private configService: ConfigService) {
|
||||
this.transporter = nodemailer.createTransport({
|
||||
host: this.configService.get('SMTP_HOST', 'smtp.yandex.ru'),
|
||||
port: parseInt(this.configService.get('SMTP_PORT', '465'), 10),
|
||||
secure: this.configService.get('SMTP_SECURE', 'true') === 'true',
|
||||
auth: {
|
||||
user: this.configService.getOrThrow('SMTP_USER'),
|
||||
pass: this.configService.getOrThrow('SMTP_PASS'),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async sendApplication(dto: SendApplicationDto) {
|
||||
const recipientEmail = this.configService.get('RECIPIENT_EMAIL') || this.configService.get('SMTP_USER');
|
||||
const siteName = this.configService.get('SITE_NAME', 'Сайт психолога');
|
||||
|
||||
const html = `
|
||||
<h2>Новая заявка с сайта «${siteName}»</h2>
|
||||
<table style="border-collapse:collapse;width:100%;max-width:600px;font-family:sans-serif">
|
||||
<tr style="border-bottom:1px solid #eee">
|
||||
<td style="padding:10px 0;font-weight:bold;width:140px">Имя:</td>
|
||||
<td style="padding:10px 0">${this.escapeHtml(dto.name)}</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #eee">
|
||||
<td style="padding:10px 0;font-weight:bold">Телефон:</td>
|
||||
<td style="padding:10px 0">${this.escapeHtml(dto.phone)}</td>
|
||||
</tr>
|
||||
${dto.email ? `
|
||||
<tr style="border-bottom:1px solid #eee">
|
||||
<td style="padding:10px 0;font-weight:bold">Email:</td>
|
||||
<td style="padding:10px 0">${this.escapeHtml(dto.email)}</td>
|
||||
</tr>
|
||||
` : ''}
|
||||
${dto.message ? `
|
||||
<tr style="border-bottom:1px solid #eee">
|
||||
<td style="padding:10px 0;font-weight:bold">Сообщение:</td>
|
||||
<td style="padding:10px 0">${this.escapeHtml(dto.message).replace(/\n/g, '<br>')}</td>
|
||||
</tr>
|
||||
` : ''}
|
||||
<tr>
|
||||
<td style="padding:10px 0;font-weight:bold">Дата:</td>
|
||||
<td style="padding:10px 0">${new Date().toLocaleString('ru-RU')}</td>
|
||||
</tr>
|
||||
</table>
|
||||
`;
|
||||
|
||||
const text = `
|
||||
Новая заявка с сайта «${siteName}»
|
||||
|
||||
Имя: ${dto.name}
|
||||
Телефон: ${dto.phone}
|
||||
${dto.email ? `Email: ${dto.email}\n` : ''}${dto.message ? `Сообщение: ${dto.message}\n` : ''}
|
||||
Дата: ${new Date().toLocaleString('ru-RU')}
|
||||
`.trim();
|
||||
|
||||
await this.transporter.sendMail({
|
||||
from: `"${siteName}" <${this.configService.get('SMTP_USER')}>`,
|
||||
to: recipientEmail,
|
||||
subject: `Новая заявка от ${dto.name}`,
|
||||
text,
|
||||
html,
|
||||
});
|
||||
|
||||
return { success: true };
|
||||
}
|
||||
|
||||
private escapeHtml(text: string): string {
|
||||
return text
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import { NestFactory } from '@nestjs/core';
|
||||
import { AppModule } from './app.module';
|
||||
import { ValidationPipe } from '@nestjs/common';
|
||||
|
||||
async function bootstrap() {
|
||||
const app = await NestFactory.create(AppModule);
|
||||
|
||||
app.enableCors({
|
||||
origin: process.env.FRONTEND_URL || true,
|
||||
credentials: true,
|
||||
});
|
||||
|
||||
app.useGlobalPipes(new ValidationPipe({ whitelist: true, transform: true }));
|
||||
|
||||
const port = process.env.PORT || 3001;
|
||||
await app.listen(port);
|
||||
console.log(`Server running on http://localhost:${port}`);
|
||||
}
|
||||
bootstrap();
|
||||
Reference in New Issue
Block a user