Feature: E-Mail verify #8
|
@ -0,0 +1,33 @@
|
||||||
|
import {
|
||||||
|
Column,
|
||||||
|
CreateDateColumn,
|
||||||
|
Entity,
|
||||||
|
JoinColumn,
|
||||||
|
OneToOne,
|
||||||
|
PrimaryGeneratedColumn,
|
||||||
|
UpdateDateColumn,
|
||||||
|
} from 'typeorm';
|
||||||
|
|
||||||
|
import { UserCredentials } from './user-credentials.entity';
|
||||||
|
|
||||||
|
@Entity()
|
||||||
|
export class EmailVerification {
|
||||||
|
@PrimaryGeneratedColumn('uuid')
|
||||||
|
public id: string;
|
||||||
|
|
||||||
|
@Column()
|
||||||
|
public token: string;
|
||||||
|
|
||||||
|
@Column()
|
||||||
|
public expiresAt: Date;
|
||||||
|
|
||||||
|
@OneToOne(() => UserCredentials)
|
||||||
|
@JoinColumn({ name: 'userCredentialsId' })
|
||||||
|
public user: UserCredentials;
|
||||||
|
|
||||||
|
@CreateDateColumn()
|
||||||
|
public createdAt: Date;
|
||||||
|
|
||||||
|
@UpdateDateColumn()
|
||||||
|
public updatedAt: Date;
|
||||||
|
}
|
|
@ -1,2 +1,3 @@
|
||||||
export * from './user-credentials.entity';
|
export * from './user-credentials.entity';
|
||||||
export * from './user-data.entity';
|
export * from './user-data.entity';
|
||||||
|
export * from './email-verification.entity';
|
||||||
|
|
|
@ -5,6 +5,7 @@ import { UserCredentials } from 'src/entities';
|
||||||
|
|
||||||
import { SendgridModule } from '../sendgrid-module/sendgrid.module';
|
import { SendgridModule } from '../sendgrid-module/sendgrid.module';
|
||||||
import { UserModule } from '../user-module/user.module';
|
import { UserModule } from '../user-module/user.module';
|
||||||
|
import { VerifyModule } from '../verify-module/verify.module';
|
||||||
|
|
||||||
import { AuthController } from './controller/auth.controller';
|
import { AuthController } from './controller/auth.controller';
|
||||||
import { UserCredentialsRepository } from './repositories/user-credentials.repository';
|
import { UserCredentialsRepository } from './repositories/user-credentials.repository';
|
||||||
|
@ -17,6 +18,7 @@ import { AccessTokenStrategy, RefreshTokenStrategy } from './strategies';
|
||||||
imports: [
|
imports: [
|
||||||
UserModule,
|
UserModule,
|
||||||
SendgridModule,
|
SendgridModule,
|
||||||
|
VerifyModule,
|
||||||
JwtModule.register({}),
|
JwtModule.register({}),
|
||||||
TypeOrmModule.forFeature([UserCredentials]),
|
TypeOrmModule.forFeature([UserCredentials]),
|
||||||
],
|
],
|
||||||
|
|
|
@ -31,12 +31,15 @@ export class AuthService {
|
||||||
|
|
||||||
await this.userDataRepository.createInitialUserData(user);
|
await this.userDataRepository.createInitialUserData(user);
|
||||||
|
|
||||||
// TODO Send email confirmation
|
const token =
|
||||||
// await this.passwordConfirmationMailService.sendPasswordConfirmationMail(
|
await this.emailVerificationService.generateEmailVerificationToken(
|
||||||
// user.email
|
user.id
|
||||||
// );
|
);
|
||||||
|
|
||||||
// await this.emailVerificationService.generateEmailVerificationToken(user.id);
|
await this.passwordConfirmationMailService.sendPasswordConfirmationMail(
|
||||||
|
user.email,
|
||||||
|
token
|
||||||
|
);
|
||||||
|
|
||||||
return this.generateAndPersistTokens(user.id, user.email);
|
return this.generateAndPersistTokens(user.id, user.email);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { ConfigService } from '@nestjs/config';
|
import { ConfigService } from '@nestjs/config';
|
||||||
import { TypeOrmModuleOptions } from '@nestjs/typeorm';
|
import { TypeOrmModuleOptions } from '@nestjs/typeorm';
|
||||||
import { UserCredentials, UserData } from 'src/entities';
|
import { EmailVerification, UserCredentials, UserData } from 'src/entities';
|
||||||
|
|
||||||
export const databaseConfigFactory = (
|
export const databaseConfigFactory = (
|
||||||
configService: ConfigService
|
configService: ConfigService
|
||||||
|
@ -13,5 +13,5 @@ export const databaseConfigFactory = (
|
||||||
database: configService.get('DB_NAME'),
|
database: configService.get('DB_NAME'),
|
||||||
synchronize: true,
|
synchronize: true,
|
||||||
logging: true,
|
logging: true,
|
||||||
entities: [UserCredentials, UserData],
|
entities: [UserCredentials, UserData, EmailVerification],
|
||||||
});
|
});
|
||||||
|
|
|
@ -16,18 +16,23 @@ export class PasswordConfirmationMailService extends BaseMailService {
|
||||||
super(sendGridApiKey);
|
super(sendGridApiKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async sendPasswordConfirmationMail(to: string): Promise<void> {
|
public async sendPasswordConfirmationMail(
|
||||||
|
to: string,
|
||||||
|
token: string
|
||||||
|
): Promise<void> {
|
||||||
const templateId: string = this.templateConfigService.getTemplateId(
|
const templateId: string = this.templateConfigService.getTemplateId(
|
||||||
this.PASSWORD_CONFIRMATION_EMAIL
|
this.PASSWORD_CONFIRMATION_EMAIL
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const encodedToken = encodeURIComponent(token);
|
||||||
|
|
||||||
const mailoptions: SendGridMailApi.MailDataRequired = {
|
const mailoptions: SendGridMailApi.MailDataRequired = {
|
||||||
to,
|
to,
|
||||||
from: { email: 'info@igor-propisnov.com', name: 'Ticket App' },
|
from: { email: 'info@igor-propisnov.com', name: 'Ticket App' },
|
||||||
templateId: templateId,
|
templateId: templateId,
|
||||||
dynamicTemplateData: {
|
dynamicTemplateData: {
|
||||||
name: 'Mara',
|
name: 'Mara',
|
||||||
buttonUrl: 'https://igor-propisnov.com',
|
buttonUrl: `http://localhost:4200/?token=${encodedToken}`,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
|
import { EmailVerification } from 'src/entities';
|
||||||
|
import { Repository } from 'typeorm';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class EmailVerifyRepository {
|
||||||
|
public constructor(
|
||||||
|
@InjectRepository(EmailVerification)
|
||||||
|
private readonly repository: Repository<EmailVerification>
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public async createEmailVerification(
|
||||||
|
token: string,
|
||||||
|
expiresAt: Date,
|
||||||
|
userId: string
|
||||||
|
): Promise<void> {
|
||||||
|
await this.repository.save({
|
||||||
|
token,
|
||||||
|
expiresAt,
|
||||||
|
user: { id: userId },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
export * from './email-verify.repository';
|
|
@ -0,0 +1,27 @@
|
||||||
|
import { randomBytes } from 'crypto';
|
||||||
|
|
||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
|
||||||
|
import { EmailVerifyRepository } from '../repositories';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class EmailVerificationService {
|
||||||
|
public constructor(
|
||||||
|
private readonly emailVerifyRepository: EmailVerifyRepository
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public async generateEmailVerificationToken(userId: string): Promise<string> {
|
||||||
|
const token = randomBytes(32).toString('hex');
|
||||||
|
|
||||||
|
// TODO Check users local time zone and set expiration time accordingly
|
||||||
|
const expiration = new Date(Date.now() + 24 * 60 * 60 * 1000);
|
||||||
|
|
||||||
|
this.emailVerifyRepository.createEmailVerification(
|
||||||
|
token,
|
||||||
|
expiration,
|
||||||
|
userId
|
||||||
|
);
|
||||||
|
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { ConfigModule } from '@nestjs/config';
|
||||||
|
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||||
|
import { EmailVerification } from 'src/entities';
|
||||||
|
|
||||||
|
import { EmailVerifyRepository } from './repositories';
|
||||||
|
import { EmailVerificationService } from './services/email-verification.service';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [ConfigModule, TypeOrmModule.forFeature([EmailVerification])],
|
||||||
|
providers: [EmailVerifyRepository, EmailVerificationService],
|
||||||
|
controllers: [],
|
||||||
|
exports: [EmailVerificationService],
|
||||||
|
})
|
||||||
|
export class VerifyModule {}
|
Loading…
Reference in New Issue