Skip to content

Commit

Permalink
feat(server): support modifying user info (username, phone, avatar) (#…
Browse files Browse the repository at this point in the history
…1355)

* feat(server): support modifying user info (username, phone, avatar)

* chore(server): hide powered-by for security

* fix(server): fix bind new phone =
  • Loading branch information
0fatal authored Jul 5, 2023
1 parent 3dbd761 commit 8d88d0a
Show file tree
Hide file tree
Showing 15 changed files with 880 additions and 175 deletions.
564 changes: 544 additions & 20 deletions server/package-lock.json

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
"decimal.js": "^10.4.3",
"dotenv": "^16.0.3",
"fast-json-patch": "^3.1.1",
"helmet": "^7.0.0",
"lodash": "^4.17.21",
"mongodb": "^5.1.0",
"mongodb-uri": "^0.9.7",
Expand All @@ -60,6 +61,7 @@
"reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2",
"rxjs": "^7.2.0",
"sharp": "^0.32.1",
"typescript": "^4.9.3",
"wechatpay-node-v3": "^2.1.1"
},
Expand All @@ -74,10 +76,12 @@
"@types/jest": "28.1.8",
"@types/lodash": "^4.14.191",
"@types/mongodb-uri": "^0.9.1",
"@types/multer": "^1.4.7",
"@types/node": "^16.0.0",
"@types/npm-package-arg": "^6.1.1",
"@types/passport-jwt": "^3.0.7",
"@types/passport-local": "^1.0.34",
"@types/sharp": "^0.32.0",
"@types/supertest": "^2.0.11",
"@typescript-eslint/eslint-plugin": "^5.0.0",
"@typescript-eslint/parser": "^5.0.0",
Expand Down
2 changes: 2 additions & 0 deletions server/src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { AcceptLanguageResolver, I18nModule, QueryResolver } from 'nestjs-i18n'
import { BillingModule } from './billing/billing.module'
import { AuthenticationModule } from './authentication/authentication.module'
import { FunctionTemplateModule } from './function-template/function-template.module'
import { MulterModule } from '@nestjs/platform-express'

@Module({
imports: [
Expand Down Expand Up @@ -64,6 +65,7 @@ import { FunctionTemplateModule } from './function-template/function-template.mo
}),
BillingModule,
FunctionTemplateModule,
MulterModule.register(),
],
controllers: [AppController],
providers: [AppService],
Expand Down
107 changes: 5 additions & 102 deletions server/src/authentication/authentication.controller.ts
Original file line number Diff line number Diff line change
@@ -1,35 +1,14 @@
import { AuthenticationService } from './authentication.service'
import { Body, Controller, Get, Post, Req, UseGuards } from '@nestjs/common'
import {
ApiBearerAuth,
ApiOperation,
ApiResponse,
ApiTags,
} from '@nestjs/swagger'
import {
ApiResponseObject,
ApiResponseString,
ResponseUtil,
} from 'src/utils/response'
import { JwtAuthGuard } from './jwt.auth.guard'
import { BindUsernameDto } from './dto/bind-username.dto'
import { IRequest } from 'src/utils/interface'
import { BindPhoneDto } from './dto/bind-phone.dto'
import { SmsService } from './phone/sms.service'
import { UserService } from 'src/user/user.service'
import { ObjectId } from 'mongodb'
import { SmsVerifyCodeType } from './entities/sms-verify-code'
import { Body, Controller, Get, Post } from '@nestjs/common'
import { ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger'
import { ApiResponseString, ResponseUtil } from 'src/utils/response'

import { Pat2TokenDto } from './dto/pat2token.dto'
import { UserWithProfile } from 'src/user/entities/user'

@ApiTags('Authentication')
@Controller('auth')
export class AuthenticationController {
constructor(
private readonly authService: AuthenticationService,
private readonly smsService: SmsService,
private readonly userService: UserService,
) {}
constructor(private readonly authService: AuthenticationService) {}

/**
* Auth providers
Expand All @@ -42,67 +21,6 @@ export class AuthenticationController {
return ResponseUtil.ok(providers)
}

/**
* Bind phone
*/
@ApiOperation({ summary: 'Bind username' })
@ApiResponse({ type: ResponseUtil })
@UseGuards(JwtAuthGuard)
@Post('bind/phone')
async bindPhone(@Body() dto: BindPhoneDto, @Req() req: IRequest) {
const { phone, code } = dto
// check code valid
const err = await this.smsService.validateCode(
phone,
code,
SmsVerifyCodeType.Bind,
)
if (err) {
return ResponseUtil.error(err)
}

// check phone if have already been bound
const user = await this.userService.findOneByUsernameOrPhoneOrEmail(phone)
if (user) {
return ResponseUtil.error('phone already been bound')
}

// bind phone
await this.userService.updateUser(new ObjectId(req.user._id), { phone })
}

/**
* Bind username, not support bind existed username
*/
@ApiOperation({ summary: 'Bind username' })
@ApiResponse({ type: ResponseUtil })
@UseGuards(JwtAuthGuard)
@Post('bind/username')
async bindUsername(@Body() dto: BindUsernameDto, @Req() req: IRequest) {
const { username, phone, code } = dto

// check code valid
const err = await this.smsService.validateCode(
phone,
code,
SmsVerifyCodeType.Bind,
)
if (err) {
return ResponseUtil.error(err)
}

// check username if have already been bound
const user = await this.userService.findOneByUsernameOrPhoneOrEmail(
username,
)
if (user) {
return ResponseUtil.error('username already been bound')
}

// bind username
await this.userService.updateUser(new ObjectId(req.user._id), { username })
}

/**
* Get user token by PAT
* @param pat
Expand All @@ -119,19 +37,4 @@ export class AuthenticationController {

return ResponseUtil.ok(token)
}

/**
* Get current user profile
* @param request
* @returns
*/
@UseGuards(JwtAuthGuard)
@Get('profile')
@ApiResponseObject(UserWithProfile)
@ApiOperation({ summary: 'Get current user profile' })
@ApiBearerAuth('Authorization')
async getProfile(@Req() request: IRequest) {
const user = request.user
return ResponseUtil.ok(user)
}
}
2 changes: 1 addition & 1 deletion server/src/authentication/authentication.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ import { SmsService } from './phone/sms.service'
SmsService,
AuthenticationService,
],
exports: [],
exports: [SmsService],
controllers: [
UserPasswordController,
PhoneController,
Expand Down
21 changes: 0 additions & 21 deletions server/src/authentication/dto/bind-phone.dto.ts

This file was deleted.

30 changes: 0 additions & 30 deletions server/src/authentication/dto/bind-username.dto.ts

This file was deleted.

2 changes: 2 additions & 0 deletions server/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { ValidationPipe, VersioningType } from '@nestjs/common'
import { ServerConfig } from './constants'
import { InitializerService } from './initializer/initializer.service'
import { SystemDatabase } from './system-database'
import * as helmet from 'helmet'

async function bootstrap() {
await SystemDatabase.ready
Expand All @@ -24,6 +25,7 @@ async function bootstrap() {
})

app.use(compression())
app.use(helmet.hidePoweredBy())

// for swagger api
const config = new DocumentBuilder()
Expand Down
38 changes: 38 additions & 0 deletions server/src/user/dto/bind-phone.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { ApiProperty } from '@nestjs/swagger'
import { IsMobilePhone, IsNotEmpty, IsString, Length } from 'class-validator'

export class BindPhoneDto {
@ApiProperty({
description: 'old phone number',
example: '13805718888',
})
@IsString()
@IsNotEmpty()
@IsMobilePhone('zh-CN')
oldPhoneNumber: string

@ApiProperty({
description: 'new phone number',
example: '13805718888',
})
@IsString()
@IsNotEmpty()
@IsMobilePhone('zh-CN')
newPhoneNumber: string

@ApiProperty({
description: 'sms verify code for old phone number',
example: '032476',
})
@IsNotEmpty()
@Length(6, 6)
oldSmsCode: string

@ApiProperty({
description: 'sms verify code for new phone number',
example: '032476',
})
@IsNotEmpty()
@Length(6, 6)
newSmsCode: string
}
13 changes: 13 additions & 0 deletions server/src/user/dto/bind-username.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { ApiProperty } from '@nestjs/swagger'
import { IsNotEmpty, IsString, Length } from 'class-validator'

export class BindUsernameDto {
@ApiProperty({
description: 'username',
example: 'laf-user',
})
@IsString()
@IsNotEmpty()
@Length(3, 64)
username: string
}
6 changes: 6 additions & 0 deletions server/src/user/dto/update-avatar.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { ApiProperty } from '@nestjs/swagger'

export class UpdateAvatarDto {
@ApiProperty({ type: 'string', format: 'binary' })
avatar: any
}
13 changes: 13 additions & 0 deletions server/src/user/entities/user-avatar.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { ApiProperty } from '@nestjs/swagger'
import { ObjectId } from 'mongodb'

export class UserAvatar {
@ApiProperty({ type: Buffer })
data: Buffer

@ApiProperty({ type: String })
createdBy: ObjectId

@ApiProperty()
createdAt: Date
}
Loading

0 comments on commit 8d88d0a

Please sign in to comment.