using Microsoft.EntityFrameworkCore;
using System.Security.Cryptography;
using System.Text.Json;
using bitforum.Constants;
using bitforum.Models.Account;
namespace bitforum.Repository
{
public interface IEmailVerifyTokenRepository
{
///
/// 새 토큰 생성 및 저장
///
public Task GenerateTokenAsync(string email, VerificationType type, AdditionalData? additional = null);
///
/// 토큰 검증 및 사용 처리
///
public Task ValidateTokenAsync(string token, VerificationType type);
///
/// 만료된 토큰 삭제
///
public Task CleanupExpiredTokensAsync();
}
public class EmailVerifyTokenRepository : IEmailVerifyTokenRepository
{
private readonly DefaultDbContext _db;
private readonly int _tokenExpirationMinutes = 60;
public EmailVerifyTokenRepository(DefaultDbContext db)
{
_db = db;
}
public async Task GenerateTokenAsync(string email, VerificationType type, AdditionalData? additional = null)
{
_db.EmailVerifyToken.RemoveRange(
_db.EmailVerifyToken.Where(x => x.Email == email && x.Type == type)
);
var token = Convert.ToBase64String(RandomNumberGenerator.GetBytes(64));
var expiration = DateTime.UtcNow.AddMinutes(_tokenExpirationMinutes);
_db.EmailVerifyToken.Add(new EmailVerifyToken
{
Email = email,
Token = token,
Expiration = expiration,
Type = type,
Additional = additional == null ? null : JsonSerializer.Serialize(additional)
});
await _db.SaveChangesAsync();
return token;
}
public async Task ValidateTokenAsync(string token, VerificationType type)
{
var data = await _db.EmailVerifyToken.FirstOrDefaultAsync(x => x.Token == token && x.Type == type && !x.IsVerified && x.Expiration > DateTime.UtcNow);
if (data == null)
{
return null;
}
data.IsVerified = true;
await _db.SaveChangesAsync();
return data;
}
public async Task CleanupExpiredTokensAsync()
{
_db.EmailVerifyToken.RemoveRange(
_db.EmailVerifyToken.Where(x => x.Expiration < DateTime.UtcNow)
);
await _db.SaveChangesAsync();
}
}
}