email set-up complete

This commit is contained in:
dimitar 2024-11-02 18:54:11 +01:00
parent befb521604
commit 609475271e
15 changed files with 8441 additions and 89 deletions

View File

@ -10,4 +10,10 @@ AWS_REGION=EU2
AWS_ACCESS_KEY_ID=4d2f5655369a02100375e3247d7e1fe6
AWS_SECRET_ACCESS_KEY=6d4723e14c0d799b89948c24dbe983e4
AWS_S3_BUCKET_NAME=imk-data
AWS_ENDPOINT_URL=https://eu2.contabostorage.com
AWS_ENDPOINT_URL=https://eu2.contabostorage.com
SMTP_HOST=imk.mk
SMTP_PORT=465
SMTP_USER=mailer@imk.mk
SMTP_PASSWORD=76Avtostoperski76
SMTP_FROM=mailer@imk.mk
FRONTEND_URL=https://imk.mk

File diff suppressed because it is too large Load Diff

View File

@ -23,6 +23,7 @@
"@aws-sdk/client-s3": "^3.679.0",
"@aws-sdk/lib-storage": "^3.679.0",
"@aws-sdk/s3-request-presigner": "^3.679.0",
"@nestjs-modules/mailer": "^1.6.1",
"@nestjs/common": "^10.0.0",
"@nestjs/config": "^3.3.0",
"@nestjs/core": "^10.0.0",
@ -36,6 +37,7 @@
"bcrypt": "^5.1.1",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.1",
"nodemailer": "^6.9.16",
"passport": "^0.7.0",
"passport-jwt": "^4.0.1",
"passport-local": "^1.0.0",
@ -45,13 +47,14 @@
"typeorm": "^0.3.20"
},
"devDependencies": {
"@nestjs/cli": "^10.0.0",
"@nestjs/cli": "^10.4.5",
"@nestjs/schematics": "^10.0.0",
"@nestjs/testing": "^10.0.0",
"@types/bcrypt": "^5.0.2",
"@types/express": "^4.17.17",
"@types/jest": "^29.5.2",
"@types/node": "^20.3.1",
"@types/nodemailer": "^6.4.16",
"@types/passport-jwt": "^4.0.1",
"@types/passport-local": "^1.0.38",
"@types/supertest": "^2.0.12",

View File

@ -3,10 +3,11 @@ import { AdminController } from './admin.controller';
import { AdminService } from './admin.service';
import { PrismaModule } from '../prisma/prisma.module';
import { S3Module } from '../s3/s3.module';
import { EmailModule } from '../email/email.module';
@Module({
controllers: [AdminController],
providers: [AdminService],
imports: [PrismaModule, S3Module],
imports: [PrismaModule, S3Module, EmailModule],
})
export class AdminModule {}

View File

@ -4,12 +4,14 @@ import { S3Service } from '../s3/s3.service';
import { UpdateDocumentDto } from '../dto/update-document.dto';
import { CreateUserDto } from '../dto/create-user.dto';
import * as bcrypt from 'bcrypt';
import { EmailService } from 'src/email/email.service';
@Injectable()
export class AdminService {
constructor(
private readonly prisma: PrismaService,
private readonly s3Service: S3Service,
private readonly emailService: EmailService,
) {}
async getAllDocuments() {
@ -47,6 +49,7 @@ export class AdminService {
data: {
...createUserDto,
password: hashedPassword,
isAdmin: true,
},
});
}
@ -71,14 +74,31 @@ export class AdminService {
}
async shareDocument(documentId: number, userId: number) {
return this.prisma.document.update({
const document = await this.prisma.document.update({
where: { id: documentId },
data: {
sharedWith: {
connect: { id: userId },
},
},
include: {
uploadedBy: true,
sharedWith: true,
},
});
const sharedWithUser = await this.prisma.user.findUnique({
where: { id: userId },
});
// Send email notification
await this.emailService.sendDocumentSharedNotification(
sharedWithUser.email,
document.title,
document.uploadedBy.name,
);
return document;
}
async updateDocumentStatus(documentId: number, status: string) {

View File

@ -15,18 +15,11 @@ import { ConfigModule } from '@nestjs/config';
import { AuthController } from './auth/auth.controller';
import { DocumentsController } from './documents/documents.controller';
import { JwtModule } from '@nestjs/jwt';
import { EmailModule } from './email/email.module';
@Module({
imports: [
// TypeOrmModule.forRoot({
// type: 'postgres',
// host: 'localhost',
// port: 5432,
// username: 'root',
// password: 'admin',
// database: 'imk',
// synchronize: true,
// }),
EmailModule,
ConfigModule.forRoot({
isGlobal: true,
}),

View File

@ -37,7 +37,7 @@ export class AuthController {
//@UseGuards(JwtAuthGuard)
@Post('create-admin')
async createAdmin(@Body() createUserDto: CreateUserDto) {
return this.authService.createUser(createUserDto, true);
return this.authService.createUser(createUserDto);
}
@UseGuards(JwtAuthGuard)

View File

@ -7,6 +7,7 @@ import { PassportModule } from '@nestjs/passport';
import { JwtModule } from '@nestjs/jwt';
import { jwtConstants } from './constants';
import { PrismaModule } from 'src/prisma/prisma.module';
import { EmailModule } from 'src/email/email.module';
@Module({
imports: [
@ -16,6 +17,7 @@ import { PrismaModule } from 'src/prisma/prisma.module';
secret: jwtConstants.secret,
signOptions: { expiresIn: '60m' },
}),
EmailModule,
],
providers: [AuthService, LocalStrategy, JwtStrategy],
exports: [AuthService],

View File

@ -4,13 +4,15 @@ import { PrismaService } from '../prisma/prisma.service';
import * as bcrypt from 'bcrypt';
import { CreateUserDto } from '../dto/create-user.dto';
import { ConfigService } from '@nestjs/config';
import { EmailService } from 'src/email/email.service';
@Injectable()
export class AuthService {
constructor(
private prisma: PrismaService,
private jwtService: JwtService,
private jwtService: JwtService,
private configService: ConfigService,
private emailService: EmailService,
) {}
async validateUser(username: string, password: string): Promise<any> {
@ -39,46 +41,59 @@ export class AuthService {
};
}
async createUser(
createUserDto: CreateUserDto,
isAdmin: boolean = false,
): Promise<any> {
const existingUser = await this.prisma.user.findUnique({
where: { email: createUserDto.email },
});
// async createUser(
// createUserDto: CreateUserDto,
// isAdmin: boolean = false,
// ): Promise<any> {
// const existingUser = await this.prisma.user.findUnique({
// where: { email: createUserDto.email },
// });
if (existingUser) {
throw new ConflictException('Email already exists');
}
// if (existingUser) {
// throw new ConflictException('Email already exists');
// }
const hashedPassword = await bcrypt.hash(createUserDto.password, 10);
// const hashedPassword = await bcrypt.hash(createUserDto.password, 10);
const newUser = await this.prisma.user.create({
// const newUser = await this.prisma.user.create({
// data: {
// email: createUserDto.email,
// password: hashedPassword,
// name: createUserDto.name,
// isAdmin: isAdmin,
// },
// });
// // eslint-disable-next-line @typescript-eslint/no-unused-vars
// const { password, ...result } = newUser;
// console.log(result);
// return result;
// }
async createUser(createUserDto: CreateUserDto) {
const user = await this.prisma.user.create({
data: {
email: createUserDto.email,
password: hashedPassword,
name: createUserDto.name,
isAdmin: isAdmin,
...createUserDto,
password: await bcrypt.hash(createUserDto.password, 10),
},
});
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { password, ...result } = newUser;
console.log(result);
return result;
// Send welcome email
await this.emailService.sendWelcomeEmail(user.email, user.name);
return user;
}
// async getUserInfo(userId: number) {
// return this.prisma.user.findUnique({
// where: { id: userId },
// select: {
// id: true,
// name: true,
// email: true,
// isAdmin: true,
// },
// });
// }
async requestPasswordReset(email: string) {
const user = await this.prisma.user.findUnique({ where: { email } });
if (!user) return;
const resetToken = this.jwtService.sign(
{ email },
{ expiresIn: '1h', secret: process.env.JWT_RESET_SECRET },
);
await this.emailService.sendPasswordResetEmail(email, resetToken);
}
async getUserInfo(userId: number) {
if (!userId) {
throw new Error('User ID is required');

View File

@ -0,0 +1,28 @@
import { Controller, Post, Body } from '@nestjs/common';
import { EmailService } from './email.service';
import { MailerService } from '@nestjs-modules/mailer';
@Controller('contact')
export class EmailController {
constructor(private emailService: MailerService) {}
@Post()
async sendContactEmail(@Body() contactData: {
name: string;
email: string;
message: string;
}) {
await this.emailService.sendMail({
to: process.env.CONTACT_EMAIL,
subject: `Contact Form: ${contactData.name}`,
html: `
<h1>New Contact Form Submission</h1>
<p><strong>From:</strong> ${contactData.name}</p>
<p><strong>Email:</strong> ${contactData.email}</p>
<p><strong>Message:</strong></p>
<p>${contactData.message}</p>
`,
});
return { message: 'Contact form submitted successfully' };
}
}

View File

@ -0,0 +1,30 @@
import { Module } from '@nestjs/common';
import { MailerModule } from '@nestjs-modules/mailer';
import { EmailService } from './email.service';
import { ConfigModule, ConfigService } from '@nestjs/config';
@Module({
imports: [
MailerModule.forRootAsync({
imports: [ConfigModule],
useFactory: async (config: ConfigService) => ({
transport: {
host: config.get('SMTP_HOST'),
port: config.get('SMTP_PORT'),
secure: true,
auth: {
user: config.get('SMTP_USER'),
pass: config.get('SMTP_PASSWORD'),
},
},
defaults: {
from: `"No Reply" <${config.get('SMTP_FROM')}>`,
},
}),
inject: [ConfigService],
}),
],
providers: [EmailService],
exports: [EmailService],
})
export class EmailModule {}

View File

@ -0,0 +1,45 @@
import { Injectable } from '@nestjs/common';
import { MailerService } from '@nestjs-modules/mailer';
@Injectable()
export class EmailService {
constructor(private mailerService: MailerService) {}
async sendWelcomeEmail(email: string, name: string) {
await this.mailerService.sendMail({
to: email,
subject: 'Welcome to IMK Platform',
html: `
<h1>Welcome ${name}!</h1>
<p>Your account has been created successfully.</p>
<p>You can now login to access your documents.</p>
`,
});
}
async sendPasswordResetEmail(email: string, resetToken: string) {
const resetLink = `${process.env.FRONTEND_URL}/reset-password?token=${resetToken}`;
await this.mailerService.sendMail({
to: email,
subject: 'Password Reset Request',
html: `
<h1>Password Reset</h1>
<p>Click the link below to reset your password:</p>
<a href="${resetLink}">Reset Password</a>
<p>This link will expire in 1 hour.</p>
`,
});
}
async sendDocumentSharedNotification(email: string, documentTitle: string, sharedByName: string) {
await this.mailerService.sendMail({
to: email,
subject: 'New Document Shared With You',
html: `
<h1>New Document Available</h1>
<p>${sharedByName} has shared a document with you: "${documentTitle}"</p>
<p>Login to your account to view the document.</p>
`,
});
}
}

2962
node_modules/.package-lock.json generated vendored

File diff suppressed because it is too large Load Diff

2977
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,6 @@
{
"dependencies": {
"@nestjs/cli": "^10.4.5",
"axios": "^1.7.7",
"date-fns": "^4.1.0",
"framer-motion": "^11.11.11",