Merge pull request 'feature/email-conflict-detection-v1.1.1' (#25) from feature/email-conflict-detection-v1.1.1 into main
Reviewed-on: #25
This commit was merged in pull request #25.
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -1,8 +1,8 @@
|
||||
openapi: 3.0.3
|
||||
info:
|
||||
title: Pixel Game Server - Auth API
|
||||
description: 像素游戏服务器用户认证API接口文档 - 包含验证码登录功能
|
||||
version: 1.1.0
|
||||
description: 像素游戏服务器用户认证API接口文档 - 包含验证码登录功能和邮箱冲突检测
|
||||
version: 1.1.1
|
||||
contact:
|
||||
name: API Support
|
||||
email: support@example.com
|
||||
@@ -106,7 +106,7 @@ paths:
|
||||
tags:
|
||||
- auth
|
||||
summary: 用户注册
|
||||
description: 创建新用户账户
|
||||
description: 创建新用户账户。如果提供邮箱,需要先调用发送验证码接口获取验证码。发送验证码接口会自动检查邮箱是否已被注册,避免向已存在邮箱发送验证码。
|
||||
operationId: register
|
||||
requestBody:
|
||||
required: true
|
||||
@@ -128,17 +128,49 @@ paths:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RegisterResponse'
|
||||
'400':
|
||||
description: 请求参数错误
|
||||
description: 请求参数错误或验证码错误
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
examples:
|
||||
validation_error:
|
||||
summary: 参数验证错误
|
||||
value:
|
||||
success: false
|
||||
message: "密码必须包含字母和数字,长度8-128字符"
|
||||
error_code: "REGISTER_FAILED"
|
||||
verification_code_error:
|
||||
summary: 验证码错误
|
||||
value:
|
||||
success: false
|
||||
message: "验证码不存在或已过期"
|
||||
error_code: "REGISTER_FAILED"
|
||||
'409':
|
||||
description: 用户名或邮箱已存在
|
||||
description: 资源冲突 - 用户名、邮箱或手机号已存在
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
examples:
|
||||
username_exists:
|
||||
summary: 用户名已存在
|
||||
value:
|
||||
success: false
|
||||
message: "用户名已存在"
|
||||
error_code: "REGISTER_FAILED"
|
||||
email_exists:
|
||||
summary: 邮箱已存在
|
||||
value:
|
||||
success: false
|
||||
message: "邮箱已存在"
|
||||
error_code: "REGISTER_FAILED"
|
||||
phone_exists:
|
||||
summary: 手机号已存在
|
||||
value:
|
||||
success: false
|
||||
message: "手机号已存在"
|
||||
error_code: "REGISTER_FAILED"
|
||||
|
||||
/auth/github:
|
||||
post:
|
||||
@@ -293,7 +325,7 @@ paths:
|
||||
tags:
|
||||
- auth
|
||||
summary: 发送邮箱验证码
|
||||
description: 向指定邮箱发送验证码
|
||||
description: 向指定邮箱发送验证码。如果邮箱已被注册,将返回冲突错误。
|
||||
operationId: sendEmailVerification
|
||||
requestBody:
|
||||
required: true
|
||||
@@ -322,6 +354,16 @@ paths:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
'409':
|
||||
description: 邮箱已被注册
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
example:
|
||||
success: false
|
||||
message: "邮箱已被注册,请使用其他邮箱或直接登录"
|
||||
error_code: "SEND_EMAIL_VERIFICATION_FAILED"
|
||||
'429':
|
||||
description: 发送频率过高
|
||||
content:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "pixel-game-server",
|
||||
"version": "1.1.0",
|
||||
"description": "A 2D pixel art game server built with NestJS - 支持验证码登录功能",
|
||||
"version": "1.1.1",
|
||||
"description": "A 2D pixel art game server built with NestJS - 支持验证码登录功能和邮箱冲突检测",
|
||||
"main": "dist/main.js",
|
||||
"scripts": {
|
||||
"dev": "nest start --watch",
|
||||
|
||||
@@ -31,7 +31,7 @@ export class AppService {
|
||||
|
||||
return {
|
||||
service: 'Pixel Game Server',
|
||||
version: '1.1.0',
|
||||
version: '1.1.1',
|
||||
status: 'running',
|
||||
timestamp: new Date().toISOString(),
|
||||
uptime: Math.floor((Date.now() - this.startTime) / 1000),
|
||||
|
||||
@@ -145,7 +145,11 @@ export class LoginController {
|
||||
res.status(HttpStatus.CREATED).json(result);
|
||||
} else {
|
||||
// 根据错误类型设置不同的状态码
|
||||
if (result.error_code === 'REGISTER_FAILED') {
|
||||
if (result.message?.includes('已存在')) {
|
||||
// 资源冲突:用户名、邮箱、手机号已存在
|
||||
res.status(HttpStatus.CONFLICT).json(result);
|
||||
} else if (result.error_code === 'REGISTER_FAILED') {
|
||||
// 其他注册失败:参数错误、验证码错误等
|
||||
res.status(HttpStatus.BAD_REQUEST).json(result);
|
||||
} else {
|
||||
res.status(HttpStatus.BAD_REQUEST).json(result);
|
||||
@@ -385,6 +389,9 @@ export class LoginController {
|
||||
res.status(HttpStatus.OK).json(result);
|
||||
} else if (result.error_code === 'TEST_MODE_ONLY') {
|
||||
res.status(HttpStatus.PARTIAL_CONTENT).json(result); // 206 Partial Content
|
||||
} else if (result.message?.includes('已被注册') || result.message?.includes('已存在')) {
|
||||
// 邮箱已被注册
|
||||
res.status(HttpStatus.CONFLICT).json(result);
|
||||
} else {
|
||||
res.status(HttpStatus.BAD_REQUEST).json(result);
|
||||
}
|
||||
|
||||
@@ -516,6 +516,12 @@ export class LoginCoreService {
|
||||
* @returns 验证码结果
|
||||
*/
|
||||
async sendEmailVerification(email: string, nickname?: string): Promise<VerificationCodeResult> {
|
||||
// 首先检查邮箱是否已经被注册,避免发送无用的验证码
|
||||
const existingUser = await this.usersService.findByEmail(email);
|
||||
if (existingUser) {
|
||||
throw new ConflictException('邮箱已被注册,请使用其他邮箱或直接登录');
|
||||
}
|
||||
|
||||
// 生成验证码
|
||||
const verificationCode = await this.verificationService.generateCode(
|
||||
email,
|
||||
|
||||
@@ -58,8 +58,8 @@ async function bootstrap() {
|
||||
// 配置Swagger文档
|
||||
const config = new DocumentBuilder()
|
||||
.setTitle('Pixel Game Server API')
|
||||
.setDescription('像素游戏服务器API文档 - 包含用户认证、登录注册、验证码登录等功能')
|
||||
.setVersion('1.1.0')
|
||||
.setDescription('像素游戏服务器API文档 - 包含用户认证、登录注册、验证码登录、邮箱冲突检测等功能')
|
||||
.setVersion('1.1.1')
|
||||
.addTag('auth', '用户认证相关接口')
|
||||
.addTag('admin', '管理员后台相关接口')
|
||||
.addBearerAuth(
|
||||
|
||||
@@ -6,11 +6,14 @@
|
||||
# 2. 用户注册(有邮箱但无验证码)- 应该失败并返回正确错误信息
|
||||
# 3. 用户存在性检查 - 应该在验证码验证之前进行,返回"用户名已存在"
|
||||
# 4. 邮箱验证码完整流程 - 验证码生成、注册、重复邮箱检查
|
||||
# 5. 邮箱冲突检测 - 发送验证码前检查邮箱是否已注册
|
||||
#
|
||||
# 修复验证:
|
||||
# - 用户存在检查现在在验证码验证之前执行
|
||||
# - 邮箱冲突检测防止向已注册邮箱发送验证码
|
||||
# - 验证码不会因为用户已存在而被无效消费
|
||||
# - 错误信息更加准确和用户友好
|
||||
# - 返回正确的HTTP状态码(409 Conflict)
|
||||
$baseUrl = "http://localhost:3000"
|
||||
|
||||
Write-Host "🧪 Testing Register API Fix" -ForegroundColor Green
|
||||
@@ -100,25 +103,42 @@ if ($result1 -and $result1.success) {
|
||||
}
|
||||
}
|
||||
|
||||
# Test 4: Get verification code and register with email
|
||||
Write-Host "`n📋 Get verification code and register with email" -ForegroundColor Yellow
|
||||
# Test 4: Email conflict detection test
|
||||
Write-Host "`n📋 Email conflict detection test" -ForegroundColor Yellow
|
||||
try {
|
||||
$emailResponse = Invoke-RestMethod -Uri "$baseUrl/auth/send-email-verification" -Method POST -Body (@{email = "newuser@test.com"} | ConvertTo-Json) -ContentType "application/json"
|
||||
# First, try to get verification code for a new email
|
||||
$newEmail = "newuser_$(Get-Random)@test.com"
|
||||
$emailResponse = Invoke-RestMethod -Uri "$baseUrl/auth/send-email-verification" -Method POST -Body (@{email = $newEmail} | ConvertTo-Json) -ContentType "application/json"
|
||||
|
||||
if ($emailResponse.data.verification_code) {
|
||||
$verificationCode = $emailResponse.data.verification_code
|
||||
Write-Host "Got verification code: $verificationCode" -ForegroundColor Green
|
||||
|
||||
# Register user with this email
|
||||
$result4 = Test-ApiCall -TestName "Register with valid email and verification code" -Url "$baseUrl/auth/register" -Body (@{
|
||||
username = "emailuser_$(Get-Random)"
|
||||
password = "password123"
|
||||
nickname = "Email User"
|
||||
email = "newuser@test.com"
|
||||
email = $newEmail
|
||||
email_verification_code = $verificationCode
|
||||
} | ConvertTo-Json)
|
||||
|
||||
if ($result4 -and $result4.success) {
|
||||
Write-Host "✅ PASS: Email registration successful" -ForegroundColor Green
|
||||
|
||||
# Now test email conflict detection
|
||||
Write-Host "`n📋 Testing email conflict detection" -ForegroundColor Yellow
|
||||
try {
|
||||
$conflictResponse = Invoke-RestMethod -Uri "$baseUrl/auth/send-email-verification" -Method POST -Body (@{email = $newEmail} | ConvertTo-Json) -ContentType "application/json"
|
||||
Write-Host "❌ FAIL: Should have detected email conflict" -ForegroundColor Red
|
||||
} catch {
|
||||
$statusCode = $_.Exception.Response.StatusCode.value__
|
||||
if ($statusCode -eq 409) {
|
||||
Write-Host "✅ PASS: Email conflict detected (409 status)" -ForegroundColor Green
|
||||
} else {
|
||||
Write-Host "❌ FAIL: Wrong status code for email conflict ($statusCode)" -ForegroundColor Red
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
@@ -129,5 +149,7 @@ Write-Host "`n🎯 Test Summary" -ForegroundColor Green
|
||||
Write-Host "===============" -ForegroundColor Green
|
||||
Write-Host "✅ Registration logic has been fixed:" -ForegroundColor White
|
||||
Write-Host " • User existence checked BEFORE verification code validation" -ForegroundColor White
|
||||
Write-Host " • Email conflict detection prevents sending codes to registered emails" -ForegroundColor White
|
||||
Write-Host " • Proper error messages for different scenarios" -ForegroundColor White
|
||||
Write-Host " • Verification codes not wasted on existing users" -ForegroundColor White
|
||||
Write-Host " • Returns 409 Conflict for email/username conflicts" -ForegroundColor White
|
||||
Reference in New Issue
Block a user