Migrate Auth/Brewery/Location repositories to pure Dapper; rename Timer to RowVersion (#237)

* Integrate Spectre.Console into seed application, use async data loading

* Replace stored-procedure repositories with Dapper-based repositories

Removes the USP_* stored procedures for Auth, Location, and Brewery
data access in favour of services built with Dapper.

Adds a new Features.Locations slice and optimistic concurrency support
for the Brewery slice.

* Add new documentation for Dapper migration/database schema

* Update FluentAssertions to most recent open source ver

* Update XML doc comment in backend projects

* Modify brewery location dto to include denormalized data

* - Updated column names from 'Timer' to 'RowVersion' in schema.sql for
- Introduced new BeerPost entity and updated BreweryPost and BreweryPostLocation entities to use 'RowVersion' for concurrency.
- Modified UserAccount, UserCredential, UserVerification, City, StateProvince, and Country entities to use 'RowVersion' instead of 'Timer'.
- Updated tests and command handlers to reflect changes in concurrency token from 'Timer' to 'RowVersion'.
- Adjusted BreweryDto and BreweryDtoMapper to include 'RowVersion' and updated mapping logic accordingly.
- Ensured all repository methods and interfaces are aligned with the new concurrency handling approach.

* updates

* Add BeerStyle entity and related properties; refactor BreweryDto to remove BeerPosts

* Fix Api.Specs test failures: stale Timer column refs and missing test.user fixture

* Remove repository tests, usages of registration dto used in repo

* archive old stored procs/functions

* update stale docs

* Add instructions for generating cryptographically secure base64 secrets on macOS/Linux and Windows
This commit is contained in:
2026-08-21 13:09:13 -04:00
committed by Aaron Po
parent b27b5bdb40
commit de1e9ac02b
151 changed files with 2790 additions and 1988 deletions

View File

@@ -48,6 +48,7 @@ public class TokenService : ITokenService
);
}
/// <inheritdoc />
public string GenerateAccessToken(UserAccount user)
{
DateTime expiresAt = DateTime.UtcNow.AddHours(TokenServiceExpirationHours.AccessTokenHours);
@@ -59,6 +60,7 @@ public class TokenService : ITokenService
);
}
/// <inheritdoc />
public string GenerateRefreshToken(UserAccount user)
{
DateTime expiresAt = DateTime.UtcNow.AddHours(
@@ -72,6 +74,7 @@ public class TokenService : ITokenService
);
}
/// <inheritdoc />
public string GenerateConfirmationToken(UserAccount user)
{
DateTime expiresAt = DateTime.UtcNow.AddHours(
@@ -85,12 +88,7 @@ public class TokenService : ITokenService
);
}
/// <summary>
/// Generates a token of the kind specified by <typeparamref name="T" />, dispatching to
/// <see cref="GenerateAccessToken" />, <see cref="GenerateRefreshToken" />, or
/// <see cref="GenerateConfirmationToken" />.
/// </summary>
/// <typeparam name="T">Must be <see cref="TokenType" />.</typeparam>
/// <inheritdoc />
/// <exception cref="InvalidOperationException">
/// Thrown when <typeparamref name="T" /> is not <see cref="TokenType" /> or does not resolve to a known token type.
/// </exception>
@@ -114,33 +112,25 @@ public class TokenService : ITokenService
};
}
/// <exception cref="Domain.Exceptions.UnauthorizedException">
/// Thrown when the token is missing required claims, has a malformed user ID, or otherwise fails validation.
/// </exception>
/// <inheritdoc />
public async Task<ValidatedToken> ValidateAccessTokenAsync(string token)
{
return await ValidateTokenInternalAsync(token, _accessTokenSecret, "access");
}
/// <exception cref="Domain.Exceptions.UnauthorizedException">
/// Thrown when the token is missing required claims, has a malformed user ID, or otherwise fails validation.
/// </exception>
/// <inheritdoc />
public async Task<ValidatedToken> ValidateRefreshTokenAsync(string token)
{
return await ValidateTokenInternalAsync(token, _refreshTokenSecret, "refresh");
}
/// <exception cref="Domain.Exceptions.UnauthorizedException">
/// Thrown when the token is missing required claims, has a malformed user ID, or otherwise fails validation.
/// </exception>
/// <inheritdoc />
public async Task<ValidatedToken> ValidateConfirmationTokenAsync(string token)
{
return await ValidateTokenInternalAsync(token, _confirmationTokenSecret, "confirmation");
}
/// <exception cref="Domain.Exceptions.UnauthorizedException">
/// Thrown when the refresh token is invalid, or when the user account it refers to no longer exists.
/// </exception>
/// <inheritdoc />
public async Task<RefreshTokenResult> RefreshTokenAsync(string refreshTokenString)
{
ValidatedToken validated = await ValidateRefreshTokenAsync(refreshTokenString);