1+ /* eslint-disable no-process-env */
12import { forwardRef , Inject } from '@nestjs/common'
23import { DocumentType , ReturnModelType } from '@typegoose/typegoose'
34import * as bcrypt from 'bcrypt'
@@ -7,14 +8,17 @@ import { ForbiddenError } from 'type-graphql'
78import { Service , InjectModel , Logger } from 'core'
89import { isObjectId } from 'core/utils/db'
910import {
11+ generateString ,
1012 removeExtraSpaces ,
1113 stringWithoutSpecialCharacters ,
1214} from 'core/utils/string'
1315import { AuthService } from 'modules/auth/auth.service'
1416import { OrgRoleName , Permission } from 'modules/auth/models'
17+ import { MailService } from 'modules/mail/mail.service'
1518import { OrgService } from 'modules/org/org.service'
1619import { ANY , Nullable } from 'types'
1720
21+ import { OTP_TIME } from './account.const'
1822import { CreateAccountServiceInput } from './account.type'
1923import { Account , AccountStatus } from './models/Account'
2024
@@ -29,6 +33,8 @@ export class AccountService {
2933 private readonly authService : AuthService ,
3034 @Inject ( forwardRef ( ( ) => OrgService ) )
3135 private readonly orgService : OrgService ,
36+ @Inject ( forwardRef ( ( ) => MailService ) )
37+ private readonly mailService : MailService ,
3238 ) { }
3339
3440 async createAccount (
@@ -62,20 +68,29 @@ export class AccountService {
6268 throw new Error ( 'displayName contains invalid characters' )
6369 }
6470
71+ const otp = generateString ( 20 )
72+
73+ const otpExpired = new Date ( )
74+ otpExpired . setMinutes ( otpExpired . getMinutes ( ) + OTP_TIME )
75+
6576 const account = await this . accountModel . create ( {
6677 username : accountInput . username ,
6778 email : accountInput . email ,
68- password : bcrypt . hashSync (
69- accountInput . password || accountInput . email ,
70- 10 ,
71- ) ,
79+ password : bcrypt . hashSync ( accountInput . password || '' , 10 ) ,
80+ otp,
81+ otpExpired,
7282 orgId : accountInput . orgId ,
7383 createdBy : accountInput . createdByAccountId ,
7484 status : accountInput . status ,
7585 roles : uniq ( accountInput . roles ) ,
7686 displayName : removeExtraSpaces ( accountInput . displayName ) ,
7787 } )
7888
89+ if ( process . env . NODE_ENV !== 'test' ) {
90+ this . mailService
91+ . sendOTP ( account , 'ACTIVE_ACCOUNT' )
92+ . then ( ( ) => this . logger . log ( 'Send mail success!' ) )
93+ }
7994 this . logger . log ( `[${ this . createAccount . name } ] Created account successfully` )
8095 this . logger . verbose ( account . toObject ( ) )
8196
@@ -366,4 +381,75 @@ export class AccountService {
366381
367382 return updateAccount
368383 }
384+
385+ async setPassword (
386+ usernameOrEmail : string ,
387+ password : string ,
388+ otp : string ,
389+ ) : Promise < DocumentType < Account > > {
390+ const account = await this . accountModel . findOne ( {
391+ $or : [ { email : usernameOrEmail } , { username : usernameOrEmail } ] ,
392+ } )
393+
394+ if ( ! account ) {
395+ throw new Error ( 'Account not found' )
396+ }
397+ const current = new Date ( )
398+ if ( account . status === AccountStatus . Deactivated ) {
399+ throw new Error ( 'Account has been deactivated' )
400+ }
401+ if ( account . otp !== otp ) {
402+ throw new Error ( 'OTP invalid' )
403+ }
404+ if ( account . otpExpired . getTime ( ) < current . getTime ( ) ) {
405+ throw new Error ( 'OTP expired' )
406+ }
407+
408+ if ( account . status === AccountStatus . Pending ) {
409+ account . status = AccountStatus . Active
410+ }
411+ account . otp = ''
412+ account . otpExpired = new Date ( current . setHours ( current . getHours ( ) - 1 ) )
413+ account . password = bcrypt . hashSync ( password , 10 )
414+ const afterAccount = await account . save ( )
415+
416+ return afterAccount
417+ }
418+
419+ async callOTP (
420+ usernameOrEmail : string ,
421+ type : 'ACTIVE_ACCOUNT' | 'RESET_PASSWORD' ,
422+ ) : Promise < DocumentType < Account > > {
423+ const account = await this . accountModel . findOne ( {
424+ $or : [ { email : usernameOrEmail } , { username : usernameOrEmail } ] ,
425+ } )
426+
427+ if ( ! account ) {
428+ throw new Error ( 'Account not found' )
429+ }
430+ if ( account . status === AccountStatus . Deactivated ) {
431+ throw new Error ( 'Account has been deactivated' )
432+ }
433+ const current = new Date ( )
434+ if ( account . otpExpired . getTime ( ) > current . getTime ( ) ) {
435+ throw new Error (
436+ `Don't spam, please try again after ${ account . otpExpired . getHours ( ) } :${ account . otpExpired . getMinutes ( ) } ` ,
437+ )
438+ }
439+ const otp = generateString ( 20 )
440+ const otpExpired = new Date ( )
441+ otpExpired . setMinutes ( otpExpired . getMinutes ( ) + OTP_TIME )
442+ account . otp = otp
443+ account . otpExpired = otpExpired
444+
445+ const afterAccount = await account . save ( )
446+
447+ if ( process . env . NODE_ENV !== 'test' ) {
448+ this . mailService
449+ . sendOTP ( afterAccount , type )
450+ . then ( ( ) => this . logger . log ( 'Send mail success!' ) )
451+ }
452+
453+ return afterAccount
454+ }
369455}
0 commit comments