mirror of
https://github.com/Sea-Haven-Industries/shoc-backend.git
synced 2026-09-30 07:13:12 +00:00
144 lines
6.1 KiB
C#
144 lines
6.1 KiB
C#
using Data.SeaHavenIndustries;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using SeaHaven.DataServices.Dto;
|
|
using SeaHaven.DataServices.Interfaces;
|
|
|
|
namespace SeaHaven.DataServices.Implementation
|
|
{
|
|
public class TeamMemberInviteDataService : ITeamMemberInviteDataService
|
|
{
|
|
private readonly ApplicationDbContext _context;
|
|
|
|
public TeamMemberInviteDataService(ApplicationDbContext context)
|
|
{
|
|
_context = context;
|
|
}
|
|
|
|
public async Task AddAsync(TeamMemberInvite invite, CancellationToken cancellationToken)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(invite);
|
|
|
|
_context.TeamMemberInvites.Add(invite);
|
|
await _context.SaveChangesAsync(cancellationToken);
|
|
}
|
|
|
|
public async Task ReplaceOpenForUserAsync(
|
|
TeamMemberInvite invite,
|
|
DateTime now,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(invite);
|
|
|
|
await using var transaction = await _context.Database.BeginTransactionAsync(cancellationToken);
|
|
await _context.TeamMemberInvites
|
|
.Where(existing => existing.UserId == invite.UserId
|
|
&& existing.UsedAt == null
|
|
&& existing.RevokedAt == null)
|
|
.ExecuteUpdateAsync(
|
|
setters => setters.SetProperty(existing => existing.RevokedAt, now),
|
|
cancellationToken);
|
|
_context.TeamMemberInvites.Add(invite);
|
|
await _context.SaveChangesAsync(cancellationToken);
|
|
await transaction.CommitAsync(cancellationToken);
|
|
}
|
|
|
|
public Task<TeamMemberInviteData?> GetByTokenHashAsync(string tokenHash, CancellationToken cancellationToken)
|
|
{
|
|
return _context.TeamMemberInvites
|
|
.AsNoTracking()
|
|
.Where(invite => invite.TokenHash == tokenHash)
|
|
.Select(invite => new TeamMemberInviteData
|
|
{
|
|
Id = invite.Id,
|
|
UserId = invite.UserId,
|
|
ExpiresAt = invite.ExpiresAt,
|
|
UsedAt = invite.UsedAt,
|
|
RevokedAt = invite.RevokedAt,
|
|
CodeHash = invite.CodeHash,
|
|
CodeSalt = invite.CodeSalt,
|
|
CodeExpiresAt = invite.CodeExpiresAt,
|
|
CodeFailedAttempts = invite.CodeFailedAttempts,
|
|
CodeSentAt = invite.CodeSentAt,
|
|
CodeSendCount = invite.CodeSendCount,
|
|
EmailConfirmedAt = invite.EmailConfirmedAt
|
|
})
|
|
.SingleOrDefaultAsync(cancellationToken);
|
|
}
|
|
|
|
public async Task<bool> TryStartCodeAsync(
|
|
StartTeamMemberInviteCodeCommand command,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(command);
|
|
|
|
var updated = await OpenInvite(command.InviteId, command.Now)
|
|
.Where(invite => invite.CodeSendCount < command.MaxSends
|
|
&& (invite.CodeSentAt == null || invite.CodeSentAt <= command.LastSentNoLaterThan))
|
|
.ExecuteUpdateAsync(
|
|
setters => setters
|
|
.SetProperty(invite => invite.CodeHash, command.CodeHash)
|
|
.SetProperty(invite => invite.CodeSalt, command.CodeSalt)
|
|
.SetProperty(invite => invite.CodeExpiresAt, command.CodeExpiresAt)
|
|
.SetProperty(invite => invite.CodeFailedAttempts, 0)
|
|
.SetProperty(invite => invite.CodeSentAt, command.Now)
|
|
.SetProperty(invite => invite.CodeSendCount, invite => invite.CodeSendCount + 1),
|
|
cancellationToken);
|
|
return updated == 1;
|
|
}
|
|
|
|
public async Task<bool> TryReserveCodeAttemptAsync(
|
|
int inviteId,
|
|
int maxAttempts,
|
|
DateTime now,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
var updated = await OpenInvite(inviteId, now)
|
|
.Where(invite => invite.CodeHash != null
|
|
&& invite.CodeExpiresAt > now
|
|
&& invite.CodeFailedAttempts < maxAttempts)
|
|
.ExecuteUpdateAsync(
|
|
setters => setters.SetProperty(
|
|
invite => invite.CodeFailedAttempts,
|
|
invite => invite.CodeFailedAttempts + 1),
|
|
cancellationToken);
|
|
return updated == 1;
|
|
}
|
|
|
|
public async Task<bool> TryConfirmEmailAsync(
|
|
int inviteId,
|
|
string codeHash,
|
|
DateTime now,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
var updated = await OpenInvite(inviteId, now)
|
|
.Where(invite => invite.CodeHash == codeHash && invite.CodeExpiresAt > now)
|
|
.ExecuteUpdateAsync(
|
|
setters => setters
|
|
.SetProperty(invite => invite.EmailConfirmedAt, now)
|
|
.SetProperty(invite => invite.CodeHash, (string?)null)
|
|
.SetProperty(invite => invite.CodeSalt, (string?)null)
|
|
.SetProperty(invite => invite.CodeExpiresAt, (DateTime?)null)
|
|
.SetProperty(invite => invite.CodeFailedAttempts, 0),
|
|
cancellationToken);
|
|
return updated == 1;
|
|
}
|
|
|
|
public async Task<bool> TryClaimAsync(int inviteId, DateTime now, CancellationToken cancellationToken)
|
|
{
|
|
var updated = await OpenInvite(inviteId, now)
|
|
.Where(invite => invite.EmailConfirmedAt != null)
|
|
.ExecuteUpdateAsync(
|
|
setters => setters.SetProperty(invite => invite.UsedAt, now),
|
|
cancellationToken);
|
|
return updated == 1;
|
|
}
|
|
|
|
private IQueryable<TeamMemberInvite> OpenInvite(int inviteId, DateTime now)
|
|
{
|
|
return _context.TeamMemberInvites.Where(invite => invite.Id == inviteId
|
|
&& invite.UsedAt == null
|
|
&& invite.RevokedAt == null
|
|
&& invite.ExpiresAt > now);
|
|
}
|
|
}
|
|
}
|