add registration and emails
This commit is contained in:
parent
70859f6871
commit
aaa546bccd
@ -20,6 +20,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@godaddy/terminus": "^4.1.2",
|
||||
"@nest-modules/mailer": "^1.1.3",
|
||||
"@nestjs/common": "^6.5.2",
|
||||
"@nestjs/core": "^6.5.2",
|
||||
"@nestjs/jwt": "^6.1.1",
|
||||
|
||||
@ -9,6 +9,7 @@ import { UserModule } from "./user/user.module"
|
||||
import { FormModule } from "./form/form.module"
|
||||
import { AuthModule } from './auth/auth.module';
|
||||
import { MailModule } from "./mail/mail.module"
|
||||
import { MailerModule } from "@nest-modules/mailer"
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
@ -17,6 +18,12 @@ import { MailModule } from "./mail/mail.module"
|
||||
TerminusModule.forRootAsync({
|
||||
useClass: TerminusOptionsService,
|
||||
}),
|
||||
MailerModule.forRoot({
|
||||
transport: 'smtp://localhost:1025',
|
||||
defaults: {
|
||||
from:'"OhMyForm" <noreply@ohmyform.com>',
|
||||
}
|
||||
}),
|
||||
UserModule,
|
||||
FormModule,
|
||||
AuthModule,
|
||||
|
||||
@ -1,12 +1,20 @@
|
||||
import { ApiModelProperty } from "@nestjs/swagger"
|
||||
import { IsEmail, IsNotEmpty, Validate } from "class-validator"
|
||||
import { UsernameAlreadyInUse } from "../../user/validators/UsernameAlreadyInUse"
|
||||
import { EmailAlreadyInUse } from "../../user/validators/EmailAlreadyInUse"
|
||||
|
||||
export class RegisterDto {
|
||||
@ApiModelProperty()
|
||||
@IsNotEmpty()
|
||||
@Validate(UsernameAlreadyInUse)
|
||||
readonly username: string;
|
||||
|
||||
@ApiModelProperty()
|
||||
@IsNotEmpty()
|
||||
readonly password: string;
|
||||
|
||||
@ApiModelProperty()
|
||||
@Validate(EmailAlreadyInUse)
|
||||
@IsEmail()
|
||||
readonly email: string;
|
||||
}
|
||||
|
||||
@ -24,7 +24,7 @@ export class PasswordService {
|
||||
).toString('base64');
|
||||
}
|
||||
|
||||
hash (password): Promise<String> {
|
||||
hash (password): Promise<string> {
|
||||
return bcrypt.hash(password, 4)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,16 +1,30 @@
|
||||
import { Injectable } from "@nestjs/common"
|
||||
import { MailService } from "../../mail/services/mail.service"
|
||||
import { User } from "../../user/models/user.model"
|
||||
import { PasswordService } from "./password.service"
|
||||
import { UserService } from "../../user/services/user.service"
|
||||
|
||||
@Injectable()
|
||||
export class RegisterService {
|
||||
constructor(private readonly mailService: MailService) {}
|
||||
constructor(
|
||||
private readonly mailService: MailService,
|
||||
private readonly passwordService: PasswordService,
|
||||
private readonly userService: UserService
|
||||
) {}
|
||||
|
||||
async register (username: string, email: string, password: string): Promise<void> {
|
||||
// TODO actually create user
|
||||
|
||||
let user = new User()
|
||||
user.email = email
|
||||
user.username = username
|
||||
user.passwordHash = await this.passwordService.hash(password)
|
||||
|
||||
await this.userService.save(user)
|
||||
|
||||
await this.mailService.sendEmail(
|
||||
{
|
||||
template: 'auth/register.hbs',
|
||||
template: 'auth/register',
|
||||
to: email
|
||||
},
|
||||
{
|
||||
|
||||
@ -34,5 +34,5 @@ export class RatingField {
|
||||
@arrayProp({
|
||||
items: String
|
||||
})
|
||||
validShapes: [String];
|
||||
validShapes: [string];
|
||||
}
|
||||
|
||||
@ -1,9 +1,11 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import providers from './mail.providers'
|
||||
import exportList from './mail.exports'
|
||||
import { HandlebarsAdapter, MailerModule } from "@nest-modules/mailer"
|
||||
|
||||
@Module({
|
||||
imports: [],
|
||||
imports: [
|
||||
],
|
||||
providers,
|
||||
exports: exportList,
|
||||
})
|
||||
|
||||
@ -1,10 +1,35 @@
|
||||
import { Injectable, NotFoundException } from '@nestjs/common';
|
||||
import {Inject, Injectable, NotFoundException} from '@nestjs/common';
|
||||
import { OptionsDto } from "../dto/options.dto"
|
||||
import { MailerService } from '@nest-modules/mailer';
|
||||
import * as Handlebars from 'handlebars'
|
||||
import * as fs from 'fs';
|
||||
|
||||
@Injectable()
|
||||
export class MailService {
|
||||
// TODO
|
||||
constructor(
|
||||
private readonly nodeMailer: MailerService
|
||||
) {}
|
||||
|
||||
async sendEmail(options:OptionsDto, placeholders:any): Promise<boolean> {
|
||||
const template = fs.readFileSync(`${__dirname}/../../../views/en/mail/${options.template}.hbs`, 'UTF-8');
|
||||
|
||||
const parts = Handlebars
|
||||
.compile(template)
|
||||
(placeholders)
|
||||
.split('---')
|
||||
|
||||
const mail:any = {
|
||||
to: options.to,
|
||||
subject: parts[0],
|
||||
text: parts[1],
|
||||
}
|
||||
|
||||
if (parts.length > 2) {
|
||||
mail.html = parts[2]
|
||||
}
|
||||
|
||||
await this.nodeMailer.sendMail(mail)
|
||||
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +0,0 @@
|
||||
Hi,
|
||||
|
||||
if you have not requested a new password you can ignore this email. To set a new password for your account
|
||||
just follow this link: {{ recover }}
|
||||
|
||||
See you soon
|
||||
@ -1,5 +0,0 @@
|
||||
Welcome to OhMyForm!
|
||||
|
||||
please confirm your account by following the following link: {{ confirm }}
|
||||
|
||||
enjoy!
|
||||
@ -1,7 +1,8 @@
|
||||
import {NestFactory, Reflector} from '@nestjs/core';
|
||||
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
|
||||
import { AppModule } from './app.module';
|
||||
import {ClassSerializerInterceptor, ValidationPipe} from '@nestjs/common';
|
||||
import { ClassSerializerInterceptor, ValidationPipe } from '@nestjs/common';
|
||||
import { useContainer } from "class-validator"
|
||||
const pkg = require('../package.json')
|
||||
|
||||
async function bootstrap() {
|
||||
@ -10,6 +11,8 @@ async function bootstrap() {
|
||||
// app.enableCors({ origin: '*' });
|
||||
// app.getHttpAdapter().options('*', cors());
|
||||
|
||||
useContainer(app.select(AppModule), { fallbackOnErrors: true });
|
||||
|
||||
app.useGlobalPipes(new ValidationPipe({
|
||||
disableErrorMessages: false,
|
||||
transform: true,
|
||||
|
||||
@ -1,7 +1,11 @@
|
||||
import {arrayProp, prop, Typegoose} from 'typegoose';
|
||||
import {arrayProp, pre, prop, Typegoose} from 'typegoose';
|
||||
import { IsString } from 'class-validator';
|
||||
import {Exclude} from "class-transformer"
|
||||
|
||||
@pre<User>('save', (next) => {
|
||||
this.lastModified = new Date()
|
||||
next()
|
||||
})
|
||||
export class User extends Typegoose {
|
||||
@Exclude()
|
||||
readonly _id: string;
|
||||
@ -10,13 +14,13 @@ export class User extends Typegoose {
|
||||
trim: true,
|
||||
default: ''
|
||||
})
|
||||
readonly firstName: string;
|
||||
firstName: string;
|
||||
|
||||
@prop({
|
||||
trim: true,
|
||||
default: ''
|
||||
})
|
||||
readonly lastName: string;
|
||||
lastName: string;
|
||||
|
||||
@prop({
|
||||
trim: true,
|
||||
@ -28,7 +32,7 @@ export class User extends Typegoose {
|
||||
],
|
||||
required: [true, 'Email is required']
|
||||
})
|
||||
readonly email: string;
|
||||
email: string;
|
||||
|
||||
@prop({
|
||||
unique: true,
|
||||
@ -39,33 +43,33 @@ export class User extends Typegoose {
|
||||
],
|
||||
required: [true, 'Username is required']
|
||||
})
|
||||
readonly username: string;
|
||||
username: string;
|
||||
|
||||
@prop({
|
||||
default: ''
|
||||
})
|
||||
readonly passwordHash: string;
|
||||
passwordHash: string;
|
||||
|
||||
@prop()
|
||||
readonly salt: string;
|
||||
salt: string;
|
||||
|
||||
@prop({
|
||||
default: 'local'
|
||||
})
|
||||
readonly provider: string;
|
||||
provider: string;
|
||||
|
||||
@arrayProp({
|
||||
items: String,
|
||||
enum: ['user', 'admin', 'superuser'],
|
||||
default: ['user']
|
||||
})
|
||||
readonly roles: [string];
|
||||
roles: [string];
|
||||
|
||||
@prop({
|
||||
enum: ['en', 'fr', 'es', 'it', 'de'],
|
||||
default: 'en',
|
||||
})
|
||||
readonly language: string;
|
||||
language: string;
|
||||
|
||||
@prop({
|
||||
default: Date.now
|
||||
@ -76,18 +80,18 @@ export class User extends Typegoose {
|
||||
readonly lastModified: Date;
|
||||
|
||||
@prop()
|
||||
readonly resetPasswordToken: string;
|
||||
resetPasswordToken: string;
|
||||
|
||||
@prop()
|
||||
readonly resetPasswordExpires: Date;
|
||||
resetPasswordExpires: Date;
|
||||
|
||||
@prop()
|
||||
readonly token: string;
|
||||
token: string;
|
||||
|
||||
@prop({
|
||||
unique: true,
|
||||
index: true,
|
||||
sparse: true
|
||||
})
|
||||
readonly apiKey: string;
|
||||
apiKey: string;
|
||||
}
|
||||
|
||||
@ -28,4 +28,13 @@ export class UserService {
|
||||
async findById(id: string): Promise<User> {
|
||||
return await this.userModel.findById(id).exec()
|
||||
}
|
||||
|
||||
async findOneBy(conditions): Promise<User> {
|
||||
return await this.userModel.findOne(conditions).exec()
|
||||
}
|
||||
|
||||
async save(user: User): Promise<User> {
|
||||
let model = new this.userModel(user)
|
||||
return await model.save()
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,9 @@
|
||||
import {UserService} from "./services/user.service"
|
||||
import { UserService } from "./services/user.service"
|
||||
import { UsernameAlreadyInUse } from "./validators/UsernameAlreadyInUse"
|
||||
import { EmailAlreadyInUse } from "./validators/EmailAlreadyInUse"
|
||||
|
||||
export default [
|
||||
UserService
|
||||
UserService,
|
||||
UsernameAlreadyInUse,
|
||||
EmailAlreadyInUse,
|
||||
]
|
||||
|
||||
@ -1,5 +1,9 @@
|
||||
import {UserService} from "./services/user.service"
|
||||
import { UserService } from "./services/user.service"
|
||||
import { UsernameAlreadyInUse } from "./validators/UsernameAlreadyInUse"
|
||||
import { EmailAlreadyInUse } from "./validators/EmailAlreadyInUse"
|
||||
|
||||
export default [
|
||||
UserService
|
||||
UserService,
|
||||
UsernameAlreadyInUse,
|
||||
EmailAlreadyInUse,
|
||||
]
|
||||
|
||||
20
api/src/user/validators/EmailAlreadyInUse.ts
Normal file
20
api/src/user/validators/EmailAlreadyInUse.ts
Normal file
@ -0,0 +1,20 @@
|
||||
import { ValidationArguments, ValidatorConstraint } from "class-validator"
|
||||
import { Inject, Injectable } from "@nestjs/common"
|
||||
import { UserService } from "../services/user.service"
|
||||
|
||||
@ValidatorConstraint({ name: 'EmailAlreadyInUse', async: true })
|
||||
@Injectable()
|
||||
export class EmailAlreadyInUse {
|
||||
constructor(
|
||||
@Inject('UserService') private readonly userService: UserService,
|
||||
) {}
|
||||
|
||||
async validate(text: string) {
|
||||
const user = await this.userService.findOneBy({email: text});
|
||||
return !user;
|
||||
}
|
||||
|
||||
defaultMessage(args: ValidationArguments) {
|
||||
return 'User with this email already exists.';
|
||||
}
|
||||
}
|
||||
20
api/src/user/validators/UsernameAlreadyInUse.ts
Normal file
20
api/src/user/validators/UsernameAlreadyInUse.ts
Normal file
@ -0,0 +1,20 @@
|
||||
import { ValidationArguments, ValidatorConstraint } from "class-validator"
|
||||
import { Inject, Injectable } from "@nestjs/common"
|
||||
import { UserService } from "../services/user.service"
|
||||
|
||||
@ValidatorConstraint({ name: 'UsernameAlreadyInUse', async: true })
|
||||
@Injectable()
|
||||
export class UsernameAlreadyInUse {
|
||||
constructor(
|
||||
@Inject('UserService') private readonly userService: UserService,
|
||||
) {}
|
||||
|
||||
async validate(text: string) {
|
||||
const user = await this.userService.findOneBy({username: text});
|
||||
return !user;
|
||||
}
|
||||
|
||||
defaultMessage(args: ValidationArguments) {
|
||||
return 'User with this username already exists.';
|
||||
}
|
||||
}
|
||||
19
api/views/en/mail/auth/recover.hbs
Normal file
19
api/views/en/mail/auth/recover.hbs
Normal file
@ -0,0 +1,19 @@
|
||||
Password Reset Request
|
||||
---
|
||||
Hi,
|
||||
|
||||
if you have not requested a new password you can ignore this email. To set a new password for your account
|
||||
just follow this link: {{ recover }}
|
||||
|
||||
See you soon
|
||||
---
|
||||
<h1>Hi, </h1>
|
||||
|
||||
<p>
|
||||
if you have not requested a new password you can ignore this email. To set a new password for your account
|
||||
just follow this link: <a href="{{ recover }}">{{ recover }}</a>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
See you soon
|
||||
</p>
|
||||
14
api/views/en/mail/auth/register.hbs
Normal file
14
api/views/en/mail/auth/register.hbs
Normal file
@ -0,0 +1,14 @@
|
||||
Welcome to OhMyForm
|
||||
---
|
||||
Welcome to OhMyForm!
|
||||
|
||||
please confirm your account by following the following link: {{ confirm }}
|
||||
|
||||
enjoy!
|
||||
---
|
||||
<h1>Welcome to OhMyForm!</h1>
|
||||
<p>
|
||||
please confirm your account by following the following link: <a href="{{ confirm }}">{{ confirm }}</a>
|
||||
</p>
|
||||
|
||||
<p>enjoy!</p>
|
||||
Loading…
x
Reference in New Issue
Block a user