conflict fixed
This commit is contained in:
@@ -0,0 +1,11 @@
|
||||
-- CreateTable
|
||||
CREATE TABLE "ContactMessage" (
|
||||
"id" TEXT NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
"email" TEXT NOT NULL,
|
||||
"subject" TEXT,
|
||||
"message" TEXT NOT NULL,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "ContactMessage_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
@@ -135,3 +135,12 @@ model File {
|
||||
|
||||
@@index([folder, originalName])
|
||||
}
|
||||
|
||||
model ContactMessage {
|
||||
id String @id @default(uuid())
|
||||
name String
|
||||
email String
|
||||
subject String?
|
||||
message String
|
||||
createdAt DateTime @default(now())
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import { ItemModule } from './item/item.module';
|
||||
import { FileModule } from './file/file.module';
|
||||
import { RedisModule } from './redis/redis.module';
|
||||
import { CacheModule } from './cache/cache.module';
|
||||
import { ContactModule } from './contact/contact.module';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
@@ -21,6 +22,7 @@ import { CacheModule } from './cache/cache.module';
|
||||
FileModule,
|
||||
RedisModule,
|
||||
CacheModule,
|
||||
ContactModule,
|
||||
],
|
||||
})
|
||||
export class AppModule {}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
import { Body, Controller, Get, Post, UseGuards } from '@nestjs/common';
|
||||
import { ContactService } from './contact.service';
|
||||
import { AllowAnonymous, AuthGuard } from '@thallesp/nestjs-better-auth';
|
||||
import { CreateContactDto } from './dto/createContant.dto';
|
||||
|
||||
@Controller('contact')
|
||||
@UseGuards(AuthGuard)
|
||||
export class ContactController {
|
||||
constructor(private readonly contactService: ContactService) {}
|
||||
|
||||
@Post()
|
||||
@AllowAnonymous()
|
||||
async create(@Body() data: CreateContactDto) {
|
||||
return this.contactService.create(data);
|
||||
}
|
||||
|
||||
@Get()
|
||||
async findAll() {
|
||||
return this.contactService.findAll();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { ContactService } from './contact.service';
|
||||
import { ContactController } from './contact.controller';
|
||||
import { PrismaModule } from 'src/prisma/prisma.module';
|
||||
|
||||
@Module({
|
||||
imports: [PrismaModule],
|
||||
providers: [ContactService],
|
||||
controllers: [ContactController],
|
||||
})
|
||||
export class ContactModule {}
|
||||
@@ -0,0 +1,44 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { PrismaService } from '../prisma/prisma.service';
|
||||
import { Prisma } from '@prisma/client';
|
||||
import { emailService } from '../shared/services/email.service';
|
||||
import { CreateContactDto } from './dto/createContant.dto';
|
||||
|
||||
@Injectable()
|
||||
export class ContactService {
|
||||
constructor(private prisma: PrismaService) {}
|
||||
|
||||
async create(data: CreateContactDto) {
|
||||
const message = await this.prisma.contactMessage.create({
|
||||
data: {
|
||||
name: data.name,
|
||||
email: data.email,
|
||||
subject: data.subject,
|
||||
message: data.message,
|
||||
},
|
||||
});
|
||||
|
||||
// Send email notification (fire and forget, or await if critical)
|
||||
// We'll catch errors so it doesn't fail the request if email fails
|
||||
try {
|
||||
await emailService.sendContactMessageNotification({
|
||||
name: message.name,
|
||||
email: message.email,
|
||||
subject: message.subject || undefined,
|
||||
message: message.message,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Failed to send contact notification email:', error);
|
||||
}
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
async findAll() {
|
||||
return this.prisma.contactMessage.findMany({
|
||||
orderBy: {
|
||||
createdAt: 'desc',
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import { IsString, IsEmail, IsNotEmpty, IsOptional, MaxLength } from 'class-validator';
|
||||
|
||||
export class CreateContactDto {
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
@MaxLength(100)
|
||||
name: string;
|
||||
|
||||
@IsEmail()
|
||||
@IsNotEmpty()
|
||||
email: string;
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
@MaxLength(20)
|
||||
subject?: string;
|
||||
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
@MaxLength(1000)
|
||||
message: string;
|
||||
}
|
||||
@@ -47,5 +47,8 @@ export const config = {
|
||||
user: process.env.EMAIL_USER,
|
||||
password: process.env.EMAIL_PASSWORD,
|
||||
from: process.env.EMAIL_FROM || '"Zemenu" <no-reply@zemenu.com>',
|
||||
contactNotificationEmails: process.env.CONTACT_NOTIFICATION_EMAILS
|
||||
? process.env.CONTACT_NOTIFICATION_EMAILS.split(',').map(email => email.trim())
|
||||
: [process.env.EMAIL_FROM || 'no-reply@zemenu.com'],
|
||||
},
|
||||
};
|
||||
|
||||
@@ -52,6 +52,21 @@ export class EmailService {
|
||||
await this.sendEmail(to, 'Reset your password', html);
|
||||
}
|
||||
|
||||
async sendContactMessageNotification(data: { name: string; email: string; subject?: string; message: string }) {
|
||||
const content = `
|
||||
<p>You have received a new contact message from <strong>${data.name}</strong> (${data.email}).</p>
|
||||
<p><strong>Subject:</strong> ${data.subject || 'No Subject'}</p>
|
||||
<hr style="border: 0; border-top: 1px solid #eee; margin: 20px 0;">
|
||||
<p style="white-space: pre-wrap;">${data.message}</p>
|
||||
`;
|
||||
|
||||
const html = this.getHtmlTemplate('New Contact Message', content);
|
||||
|
||||
// Send to the configured notification emails
|
||||
const recipients = config.email.contactNotificationEmails;
|
||||
await this.sendEmail(recipients.join(','), `New Contact Message: ${data.subject || 'No Subject'}`, html);
|
||||
}
|
||||
|
||||
private getHtmlTemplate(title: string, content: string): string {
|
||||
const logoUrl = 'https://res.cloudinary.com/dmxoohiwo/image/upload/v1764315105/logo_fpczsd.png';
|
||||
const currentYear = new Date().getFullYear();
|
||||
|
||||
Reference in New Issue
Block a user