using Microsoft.AspNetCore.Mvc; using bitforum.DTOs.Request; using bitforum.DTOs.Response; using bitforum.Helpers; using bitforum.Services; using bitforum.Constants; using bitforum.Repository; using bitforum.Models.Account; namespace bitforum.Controllers.API.Auth { [ApiController] [Route("api/auth")] public class RegisterController : ControllerBase { private readonly ILogger _logger; private readonly DefaultDbContext _db; private readonly IConfigService _configService; private readonly IMailService _mailService; private readonly IMemberRepository _memberRepository; private ResultDto _result = new ResultDto(); public RegisterController(ILogger logger, DefaultDbContext db, IConfigService configService, IMailService mailService, IMemberRepository memberRepository) { _logger = logger; _db = db; _configService = configService; _mailService = mailService; _memberRepository = memberRepository; } // 회원가입 요청 [HttpPost("register")] public async Task> Register([FromBody] RegisterDto request) { try { // 회원 가입 차단 if (_configService.IsRegisterBlock()) { throw new Exception("현재 회원가입이 일시 중단되었습니다."); } // 유효성 검사 if (!ModelState.IsValid) { _result.Errors = ModelState.GetErrors(); throw new Exception("유효성 검사에 실패하였습니다."); } // 이메일 거부 목록 확인 if (_configService.IsDeniedEmail(request.Email)) { throw new Exception($"`{request.Email}`은 사용할 수 없는 이메일입니다."); } // 이메일 중복 확인 if (await _memberRepository.IsExistEmail(request.Email)) { throw new Exception("이미 가입된 이메일입니다."); } // 비밀번호 정책 확인 if (!_configService.IsPasswordPolicyValid(request.Password)) { throw new Exception("비밀번호 구성이 적절하지 않습니다."); } bool isNew = false; var member = await _memberRepository.FindNonMemberByEmail(request.Email); if (member == null) { member = new Models.Account.Member { Email = request.Email, CreatedAt = DateTime.UtcNow }; isNew = true; } member.Password = BCrypt.Net.BCrypt.HashPassword(request.Password); member.SignupIP = HttpContext.GetClientIP(); member.PasswordUpdatedAt = DateTime.UtcNow; if (isNew) { await _db.Member.AddAsync(member); } await _db.SaveChangesAsync(); if (member.ID > 0) { await _db.MemberApprove.AddAsync(new MemberApprove { MemberID = member.ID }); } bool isRegisterEmailAuth = _configService.IsRegisterEmailAuth(); if (isRegisterEmailAuth) { // 회원가입 이메일 인증 메일 발송 await _mailService.SendRegisterEmailAsync(member); _result.Message = "이메일 인증 후 회원가입이 완료됩니다."; } else { // 회원가입 완료 메일 발송 await _mailService.SendRegistrationEmailAsync(member); _result.Message = "회원가입을 환영합니다."; } _result.Data = new { isRegisterEmailAuth }; } catch (Exception e) { _logger.LogError(e, e.Message); _result.Ok = false; _result.Status = StatusCodes.Status400BadRequest; _result.Message = e.Message; } return _result; } // 회원가입 수락 [HttpGet("registration/{email}")] public async Task> Registration([FromRoute] string email) { try { var cookieName = $"isVerified-{VerificationType.Registration}"; if (!Request.Cookies.ContainsKey(cookieName) || Request.Cookies[cookieName] != "true") { throw new Exception("사전 인증을 먼저 수행하세요."); } if (string.IsNullOrEmpty(email)) { throw new Exception("이메일을 입력해주세요."); } // 회원 확인 var member = await _memberRepository.FindNonMemberByEmail(email); if (member is null) { throw new Exception("회원 정보를 찾을 수 없습니다."); } member.IsEmailVerified = true; member.EmailVerifiedAt = DateTime.UtcNow; await _db.SaveChangesAsync(); await _mailService.SendRegistrationEmailAsync(member); _result.Message = "회원가입을 환영합니다."; // 쿠키 삭제 Response.Cookies.Delete(cookieName); } catch (Exception e) { _logger.LogError(e, e.Message); _result.Ok = false; _result.Status = StatusCodes.Status400BadRequest; _result.Message = e.Message; } return _result; } } }