33 Post ,
44 Body ,
55 Get ,
6+ Query ,
7+ Req ,
8+ Res ,
69 UseGuards ,
710 HttpCode ,
811 HttpStatus ,
@@ -12,13 +15,20 @@ import {
1215 ApiOperation ,
1316 ApiResponse ,
1417 ApiBearerAuth ,
18+ ApiExcludeEndpoint ,
1519} from '@nestjs/swagger' ;
20+ // Fastify types imported as values (not 'import type') for decorator metadata
1621import { AuthService } from './auth.service' ;
1722import { RegisterDto } from './dto/register.dto' ;
1823import { LoginDto } from './dto/login.dto' ;
1924import { AuthResponseDto , UserResponseDto } from './dto/auth-response.dto' ;
2025import { ChangePasswordDto } from './dto/change-password.dto' ;
26+ import { ForgotPasswordDto } from './dto/forgot-password.dto' ;
27+ import { ResetPasswordDto } from './dto/reset-password.dto' ;
28+ import { DeleteAccountDto } from './dto/delete-account.dto' ;
2129import { JwtAuthGuard } from './guards/jwt-auth.guard' ;
30+ import { GoogleAuthGuard } from './guards/google-auth.guard' ;
31+ import { GithubAuthGuard } from './guards/github-auth.guard' ;
2232import { CurrentUser } from '../common/decorators/current-user.decorator' ;
2333
2434@ApiTags ( 'auth' )
@@ -79,4 +89,120 @@ export class AuthController {
7989 ) {
8090 return this . authService . changePassword ( user . id , changePasswordDto ) ;
8191 }
92+
93+ // ==================== 邮箱验证 ====================
94+
95+ @Get ( 'verify-email' )
96+ @ApiOperation ( { summary : '验证邮箱' , description : '通过邮件链接验证邮箱地址' } )
97+ @ApiResponse ( { status : 200 , description : '验证成功' } )
98+ @ApiResponse ( { status : 400 , description : '无效的验证令牌' } )
99+ async verifyEmail ( @Query ( 'token' ) token : string ) {
100+ return this . authService . verifyEmail ( token ) ;
101+ }
102+
103+ @Post ( 'resend-verification' )
104+ @UseGuards ( JwtAuthGuard )
105+ @ApiBearerAuth ( 'JWT-auth' )
106+ @HttpCode ( HttpStatus . OK )
107+ @ApiOperation ( { summary : '重新发送验证邮件' } )
108+ @ApiResponse ( { status : 200 , description : '验证邮件已发送' } )
109+ async resendVerification ( @CurrentUser ( ) user : UserResponseDto ) {
110+ return this . authService . resendVerification ( user . id ) ;
111+ }
112+
113+ // ==================== 密码重置 ====================
114+
115+ @Post ( 'forgot-password' )
116+ @HttpCode ( HttpStatus . OK )
117+ @ApiOperation ( { summary : '忘记密码' , description : '发送密码重置邮件' } )
118+ @ApiResponse ( { status : 200 , description : '如果邮箱存在将发送重置邮件' } )
119+ async forgotPassword ( @Body ( ) dto : ForgotPasswordDto ) {
120+ return this . authService . forgotPassword ( dto . email ) ;
121+ }
122+
123+ @Post ( 'reset-password' )
124+ @HttpCode ( HttpStatus . OK )
125+ @ApiOperation ( { summary : '重置密码' , description : '使用重置令牌设置新密码' } )
126+ @ApiResponse ( { status : 200 , description : '密码重置成功' } )
127+ @ApiResponse ( { status : 400 , description : '无效或已过期的重置令牌' } )
128+ async resetPassword ( @Body ( ) dto : ResetPasswordDto ) {
129+ return this . authService . resetPassword ( dto . token , dto . newPassword ) ;
130+ }
131+
132+ // ==================== 账号删除 ====================
133+
134+ @Post ( 'delete-account' )
135+ @UseGuards ( JwtAuthGuard )
136+ @ApiBearerAuth ( 'JWT-auth' )
137+ @HttpCode ( HttpStatus . OK )
138+ @ApiOperation ( { summary : '删除账号' , description : '永久删除当前用户账号及所有数据' } )
139+ @ApiResponse ( { status : 200 , description : '账号已删除' } )
140+ @ApiResponse ( { status : 401 , description : '密码错误' } )
141+ async deleteAccount (
142+ @CurrentUser ( ) user : UserResponseDto ,
143+ @Body ( ) dto : DeleteAccountDto ,
144+ ) {
145+ return this . authService . deleteAccount ( user . id , dto . password ) ;
146+ }
147+
148+ // ==================== Refresh Token ====================
149+
150+ @Post ( 'refresh' )
151+ @HttpCode ( HttpStatus . OK )
152+ @ApiOperation ( { summary : '刷新令牌' , description : '使用 refresh_token 获取新的 access_token' } )
153+ @ApiResponse ( { status : 200 , description : '刷新成功' } )
154+ @ApiResponse ( { status : 401 , description : '无效的刷新令牌' } )
155+ async refreshToken ( @Body ( 'refresh_token' ) refreshToken : string ) {
156+ return this . authService . refreshTokens ( refreshToken ) ;
157+ }
158+
159+ // ==================== OAuth 登录 ====================
160+
161+ @Get ( 'google' )
162+ @UseGuards ( GoogleAuthGuard )
163+ @ApiExcludeEndpoint ( )
164+ googleLogin ( ) {
165+ // Guard 会自动重定向到 Google
166+ }
167+
168+ @Get ( 'google/callback' )
169+ @UseGuards ( GoogleAuthGuard )
170+ @ApiExcludeEndpoint ( )
171+ async googleCallback ( @Req ( ) req : any , @Res ( ) res : any ) {
172+ const profile = req . user ;
173+ const result = await this . authService . oauthLogin ( {
174+ googleId : profile . googleId ,
175+ email : profile . email ,
176+ name : profile . name ,
177+ avatarUrl : profile . avatarUrl ,
178+ } ) ;
179+ const frontendUrl = process . env . FRONTEND_URL || 'http://localhost:3000' ;
180+ return res . redirect (
181+ `${ frontendUrl } /auth/oauth-callback?token=${ result . access_token } ` ,
182+ ) ;
183+ }
184+
185+ @Get ( 'github' )
186+ @UseGuards ( GithubAuthGuard )
187+ @ApiExcludeEndpoint ( )
188+ githubLogin ( ) {
189+ // Guard 会自动重定向到 GitHub
190+ }
191+
192+ @Get ( 'github/callback' )
193+ @UseGuards ( GithubAuthGuard )
194+ @ApiExcludeEndpoint ( )
195+ async githubCallback ( @Req ( ) req : any , @Res ( ) res : any ) {
196+ const profile = req . user ;
197+ const result = await this . authService . oauthLogin ( {
198+ githubId : profile . githubId ,
199+ email : profile . email ,
200+ name : profile . name ,
201+ avatarUrl : profile . avatarUrl ,
202+ } ) ;
203+ const frontendUrl = process . env . FRONTEND_URL || 'http://localhost:3000' ;
204+ return res . redirect (
205+ `${ frontendUrl } /auth/oauth-callback?token=${ result . access_token } ` ,
206+ ) ;
207+ }
82208}
0 commit comments