mirror of
https://github.com/aaronpo97/the-biergarten-app.git
synced 2026-10-14 17:54:42 +00:00
formatting
This commit is contained in:
@@ -6,21 +6,15 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<SupportedPlatform Include="browser"/>
|
||||
<SupportedPlatform Include="browser" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.Web" Version="10.0.1" />
|
||||
<PackageReference
|
||||
Include="Microsoft.AspNetCore.Components.Web"
|
||||
Version="10.0.1"
|
||||
/>
|
||||
<PackageReference
|
||||
Include="Microsoft.Extensions.DependencyInjection.Abstractions"
|
||||
Version="10.0.1"
|
||||
/>
|
||||
<PackageReference
|
||||
Include="Microsoft.Extensions.Logging.Abstractions"
|
||||
Version="10.0.1"
|
||||
Include="Microsoft.Extensions.DependencyInjection.Abstractions"
|
||||
Version="10.0.1"
|
||||
/>
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.1" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -14,10 +14,8 @@ namespace Infrastructure.Email.Templates.Rendering;
|
||||
/// pipeline.
|
||||
/// </param>
|
||||
/// <param name="loggerFactory">The logger factory passed to the <see cref="HtmlRenderer" /> used to render components.</param>
|
||||
public class EmailTemplateProvider(
|
||||
IServiceProvider serviceProvider,
|
||||
ILoggerFactory loggerFactory
|
||||
) : IEmailTemplateProvider
|
||||
public class EmailTemplateProvider(IServiceProvider serviceProvider, ILoggerFactory loggerFactory)
|
||||
: IEmailTemplateProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Renders the UserRegisteredEmail template with the specified parameters.
|
||||
@@ -33,7 +31,7 @@ public class EmailTemplateProvider(
|
||||
Dictionary<string, object?> parameters = new()
|
||||
{
|
||||
{ nameof(UserRegistration.Username), username },
|
||||
{ nameof(UserRegistration.ConfirmationLink), confirmationLink }
|
||||
{ nameof(UserRegistration.ConfirmationLink), confirmationLink },
|
||||
};
|
||||
|
||||
return await RenderComponentAsync<UserRegistration>(parameters);
|
||||
@@ -53,7 +51,7 @@ public class EmailTemplateProvider(
|
||||
Dictionary<string, object?> parameters = new()
|
||||
{
|
||||
{ nameof(ResendConfirmation.Username), username },
|
||||
{ nameof(ResendConfirmation.ConfirmationLink), confirmationLink }
|
||||
{ nameof(ResendConfirmation.ConfirmationLink), confirmationLink },
|
||||
};
|
||||
|
||||
return await RenderComponentAsync<ResendConfirmation>(parameters);
|
||||
@@ -72,10 +70,7 @@ public class EmailTemplateProvider(
|
||||
)
|
||||
where TComponent : IComponent
|
||||
{
|
||||
await using HtmlRenderer htmlRenderer = new(
|
||||
serviceProvider,
|
||||
loggerFactory
|
||||
);
|
||||
await using HtmlRenderer htmlRenderer = new(serviceProvider, loggerFactory);
|
||||
|
||||
string html = await htmlRenderer.Dispatcher.InvokeAsync(async () =>
|
||||
{
|
||||
@@ -89,4 +84,4 @@ public class EmailTemplateProvider(
|
||||
|
||||
return html;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,10 +11,7 @@ public interface IEmailTemplateProvider
|
||||
/// <param name="username">The username to include in the email</param>
|
||||
/// <param name="confirmationLink">The email confirmation link</param>
|
||||
/// <returns>The rendered HTML string</returns>
|
||||
Task<string> RenderUserRegisteredEmailAsync(
|
||||
string username,
|
||||
string confirmationLink
|
||||
);
|
||||
Task<string> RenderUserRegisteredEmailAsync(string username, string confirmationLink);
|
||||
|
||||
/// <summary>
|
||||
/// Renders the ResendConfirmation template with the specified parameters.
|
||||
@@ -22,8 +19,5 @@ public interface IEmailTemplateProvider
|
||||
/// <param name="username">The username to include in the email</param>
|
||||
/// <param name="confirmationLink">The new confirmation link</param>
|
||||
/// <returns>The rendered HTML string</returns>
|
||||
Task<string> RenderResendConfirmationEmailAsync(
|
||||
string username,
|
||||
string confirmationLink
|
||||
);
|
||||
}
|
||||
Task<string> RenderResendConfirmationEmailAsync(string username, string confirmationLink);
|
||||
}
|
||||
|
||||
@@ -21,10 +21,5 @@ public interface IEmailProvider
|
||||
/// <param name="subject">Email subject line</param>
|
||||
/// <param name="body">Email body (HTML or plain text)</param>
|
||||
/// <param name="isHtml">Whether the body is HTML (default: true)</param>
|
||||
Task SendAsync(
|
||||
IEnumerable<string> to,
|
||||
string subject,
|
||||
string body,
|
||||
bool isHtml = true
|
||||
);
|
||||
}
|
||||
Task SendAsync(IEnumerable<string> to, string subject, string body, bool isHtml = true);
|
||||
}
|
||||
|
||||
@@ -7,6 +7,6 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="MailKit" Version="4.15.1"/>
|
||||
<PackageReference Include="MailKit" Version="4.15.1" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -58,22 +58,16 @@ public class SmtpEmailProvider : IEmailProvider
|
||||
{
|
||||
_host =
|
||||
Environment.GetEnvironmentVariable("SMTP_HOST")
|
||||
?? throw new InvalidOperationException(
|
||||
"SMTP_HOST environment variable is not set"
|
||||
);
|
||||
?? throw new InvalidOperationException("SMTP_HOST environment variable is not set");
|
||||
|
||||
string portString =
|
||||
Environment.GetEnvironmentVariable("SMTP_PORT") ?? "587";
|
||||
string portString = Environment.GetEnvironmentVariable("SMTP_PORT") ?? "587";
|
||||
if (!int.TryParse(portString, out _port))
|
||||
throw new InvalidOperationException(
|
||||
$"SMTP_PORT '{portString}' is not a valid integer"
|
||||
);
|
||||
throw new InvalidOperationException($"SMTP_PORT '{portString}' is not a valid integer");
|
||||
|
||||
_username = Environment.GetEnvironmentVariable("SMTP_USERNAME");
|
||||
_password = Environment.GetEnvironmentVariable("SMTP_PASSWORD");
|
||||
|
||||
string useSslString =
|
||||
Environment.GetEnvironmentVariable("SMTP_USE_SSL") ?? "true";
|
||||
string useSslString = Environment.GetEnvironmentVariable("SMTP_USE_SSL") ?? "true";
|
||||
_useSsl = bool.Parse(useSslString);
|
||||
|
||||
_fromEmail =
|
||||
@@ -82,9 +76,7 @@ public class SmtpEmailProvider : IEmailProvider
|
||||
"SMTP_FROM_EMAIL environment variable is not set"
|
||||
);
|
||||
|
||||
_fromName =
|
||||
Environment.GetEnvironmentVariable("SMTP_FROM_NAME")
|
||||
?? "The Biergarten";
|
||||
_fromName = Environment.GetEnvironmentVariable("SMTP_FROM_NAME") ?? "The Biergarten";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -95,12 +87,7 @@ public class SmtpEmailProvider : IEmailProvider
|
||||
/// <param name="body">Email body (HTML or plain text)</param>
|
||||
/// <param name="isHtml">Whether the body is HTML (default: true)</param>
|
||||
/// <exception cref="InvalidOperationException">Thrown when connecting, authenticating, or sending via SMTP fails.</exception>
|
||||
public async Task SendAsync(
|
||||
string to,
|
||||
string subject,
|
||||
string body,
|
||||
bool isHtml = true
|
||||
)
|
||||
public async Task SendAsync(string to, string subject, string body, bool isHtml = true)
|
||||
{
|
||||
await SendAsync([to], subject, body, isHtml);
|
||||
}
|
||||
@@ -128,7 +115,8 @@ public class SmtpEmailProvider : IEmailProvider
|
||||
MimeMessage message = new();
|
||||
message.From.Add(new MailboxAddress(_fromName, _fromEmail));
|
||||
|
||||
foreach (string recipient in to) message.To.Add(MailboxAddress.Parse(recipient));
|
||||
foreach (string recipient in to)
|
||||
message.To.Add(MailboxAddress.Parse(recipient));
|
||||
|
||||
message.Subject = subject;
|
||||
|
||||
@@ -152,10 +140,7 @@ public class SmtpEmailProvider : IEmailProvider
|
||||
await client.ConnectAsync(_host, _port, secureSocketOptions);
|
||||
|
||||
// Authenticate if credentials are provided
|
||||
if (
|
||||
!string.IsNullOrEmpty(_username)
|
||||
&& !string.IsNullOrEmpty(_password)
|
||||
)
|
||||
if (!string.IsNullOrEmpty(_username) && !string.IsNullOrEmpty(_password))
|
||||
await client.AuthenticateAsync(_username, _password);
|
||||
|
||||
await client.SendAsync(message);
|
||||
@@ -163,10 +148,7 @@ public class SmtpEmailProvider : IEmailProvider
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"Failed to send email: {ex.Message}",
|
||||
ex
|
||||
);
|
||||
throw new InvalidOperationException($"Failed to send email: {ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,12 +15,7 @@ public interface ITokenInfrastructure
|
||||
/// <param name="expiry">The date and time at which the token expires.</param>
|
||||
/// <param name="secret">The symmetric secret used to sign the token.</param>
|
||||
/// <returns>The serialized, signed JWT string.</returns>
|
||||
string GenerateJwt(
|
||||
Guid userId,
|
||||
string username,
|
||||
DateTime expiry,
|
||||
string secret
|
||||
);
|
||||
string GenerateJwt(Guid userId, string username, DateTime expiry, string secret);
|
||||
|
||||
/// <summary>
|
||||
/// Validates a JWT and returns the resulting claims principal.
|
||||
@@ -33,4 +28,4 @@ public interface ITokenInfrastructure
|
||||
/// validation.
|
||||
/// </exception>
|
||||
Task<ClaimsPrincipal> ValidateJwtAsync(string token, string secret);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,17 +7,11 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference
|
||||
Include="Microsoft.IdentityModel.JsonWebTokens"
|
||||
Version="8.2.1"
|
||||
/>
|
||||
<PackageReference
|
||||
Include="System.IdentityModel.Tokens.Jwt"
|
||||
Version="8.2.1"
|
||||
/>
|
||||
<PackageReference Include="Microsoft.IdentityModel.JsonWebTokens" Version="8.2.1" />
|
||||
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.2.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Domain\Domain.Exceptions\Domain.Exceptions.csproj"/>
|
||||
<ProjectReference Include="..\..\Domain\Domain.Exceptions\Domain.Exceptions.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -25,12 +25,7 @@ public class JwtInfrastructure : ITokenInfrastructure
|
||||
/// <param name="expiry">The date and time at which the token expires.</param>
|
||||
/// <param name="secret">The symmetric secret used to sign the token (encoded as UTF-8 bytes).</param>
|
||||
/// <returns>The serialized, signed JWT string.</returns>
|
||||
public string GenerateJwt(
|
||||
Guid userId,
|
||||
string username,
|
||||
DateTime expiry,
|
||||
string secret
|
||||
)
|
||||
public string GenerateJwt(Guid userId, string username, DateTime expiry, string secret)
|
||||
{
|
||||
JsonWebTokenHandler handler = new();
|
||||
byte[] key = Encoding.UTF8.GetBytes(secret);
|
||||
@@ -46,7 +41,7 @@ public class JwtInfrastructure : ITokenInfrastructure
|
||||
JwtRegisteredClaimNames.Exp,
|
||||
new DateTimeOffset(expiry).ToUnixTimeSeconds().ToString()
|
||||
),
|
||||
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())
|
||||
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
|
||||
};
|
||||
|
||||
SecurityTokenDescriptor tokenDescriptor = new()
|
||||
@@ -56,13 +51,12 @@ public class JwtInfrastructure : ITokenInfrastructure
|
||||
SigningCredentials = new SigningCredentials(
|
||||
new SymmetricSecurityKey(key),
|
||||
SecurityAlgorithms.HmacSha256
|
||||
)
|
||||
),
|
||||
};
|
||||
|
||||
return handler.CreateToken(tokenDescriptor);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Validates a JWT's signature and lifetime (issuer and audience validation are disabled),
|
||||
/// using the provided secret as the HMAC-SHA256 symmetric signing key.
|
||||
@@ -74,21 +68,16 @@ public class JwtInfrastructure : ITokenInfrastructure
|
||||
/// Thrown when the token is invalid, has no claims identity, is expired, or otherwise fails validation
|
||||
/// (including when validation itself throws, e.g. due to a malformed token or signature mismatch).
|
||||
/// </exception>
|
||||
public async Task<ClaimsPrincipal> ValidateJwtAsync(
|
||||
string token,
|
||||
string secret
|
||||
)
|
||||
public async Task<ClaimsPrincipal> ValidateJwtAsync(string token, string secret)
|
||||
{
|
||||
JsonWebTokenHandler handler = new();
|
||||
byte[] keyBytes = Encoding.UTF8.GetBytes(
|
||||
secret
|
||||
);
|
||||
byte[] keyBytes = Encoding.UTF8.GetBytes(secret);
|
||||
TokenValidationParameters parameters = new()
|
||||
{
|
||||
ValidateIssuer = false,
|
||||
ValidateAudience = false,
|
||||
ValidateLifetime = true,
|
||||
IssuerSigningKey = new SymmetricSecurityKey(keyBytes)
|
||||
IssuerSigningKey = new SymmetricSecurityKey(keyBytes),
|
||||
};
|
||||
|
||||
try
|
||||
@@ -104,4 +93,4 @@ public class JwtInfrastructure : ITokenInfrastructure
|
||||
throw new UnauthorizedException("Invalid token");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ public class Argon2Infrastructure : IPasswordInfrastructure
|
||||
Salt = salt,
|
||||
DegreeOfParallelism = Math.Max(Environment.ProcessorCount, 1),
|
||||
MemorySize = ArgonMemoryKb,
|
||||
Iterations = ArgonIterations
|
||||
Iterations = ArgonIterations,
|
||||
};
|
||||
|
||||
byte[] hash = argon2.GetBytes(HashSize);
|
||||
@@ -57,10 +57,7 @@ public class Argon2Infrastructure : IPasswordInfrastructure
|
||||
{
|
||||
try
|
||||
{
|
||||
string[] parts = stored.Split(
|
||||
':',
|
||||
StringSplitOptions.RemoveEmptyEntries
|
||||
);
|
||||
string[] parts = stored.Split(':', StringSplitOptions.RemoveEmptyEntries);
|
||||
if (parts.Length != 2)
|
||||
return false;
|
||||
|
||||
@@ -72,7 +69,7 @@ public class Argon2Infrastructure : IPasswordInfrastructure
|
||||
Salt = salt,
|
||||
DegreeOfParallelism = Math.Max(Environment.ProcessorCount, 1),
|
||||
MemorySize = ArgonMemoryKb,
|
||||
Iterations = ArgonIterations
|
||||
Iterations = ArgonIterations,
|
||||
};
|
||||
|
||||
byte[] actual = argon2.GetBytes(expected.Length);
|
||||
@@ -83,4 +80,4 @@ public class Argon2Infrastructure : IPasswordInfrastructure
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,4 +19,4 @@ public interface IPasswordInfrastructure
|
||||
/// <param name="stored">The stored salt/hash string previously produced by <see cref="Hash" />.</param>
|
||||
/// <returns><c>true</c> if the password matches the stored hash; otherwise <c>false</c>.</returns>
|
||||
public bool Verify(string password, string stored);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,9 +7,6 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference
|
||||
Include="Konscious.Security.Cryptography.Argon2"
|
||||
Version="1.3.1"
|
||||
/>
|
||||
<PackageReference Include="Konscious.Security.Cryptography.Argon2" Version="1.3.1" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -9,12 +9,9 @@ namespace Infrastructure.Sql;
|
||||
/// resolving the connection string from environment variables or application configuration.
|
||||
/// </summary>
|
||||
/// <param name="configuration">The application configuration, used as a fallback source for the connection string.</param>
|
||||
public class DefaultSqlConnectionFactory(IConfiguration configuration)
|
||||
: ISqlConnectionFactory
|
||||
public class DefaultSqlConnectionFactory(IConfiguration configuration) : ISqlConnectionFactory
|
||||
{
|
||||
private readonly string _connectionString = GetConnectionString(
|
||||
configuration
|
||||
);
|
||||
private readonly string _connectionString = GetConnectionString(configuration);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new, unopened <see cref="SqlConnection" /> using the resolved connection string.
|
||||
@@ -39,10 +36,9 @@ public class DefaultSqlConnectionFactory(IConfiguration configuration)
|
||||
private static string GetConnectionString(IConfiguration configuration)
|
||||
{
|
||||
// Check for full connection string first
|
||||
string? fullConnectionString = Environment.GetEnvironmentVariable(
|
||||
"DB_CONNECTION_STRING"
|
||||
);
|
||||
if (!string.IsNullOrEmpty(fullConnectionString)) return fullConnectionString;
|
||||
string? fullConnectionString = Environment.GetEnvironmentVariable("DB_CONNECTION_STRING");
|
||||
if (!string.IsNullOrEmpty(fullConnectionString))
|
||||
return fullConnectionString;
|
||||
|
||||
// Try to build from individual environment variables (preferred method for Docker)
|
||||
try
|
||||
@@ -53,11 +49,12 @@ public class DefaultSqlConnectionFactory(IConfiguration configuration)
|
||||
{
|
||||
// Fall back to configuration-based connection string if env vars are not set
|
||||
string? connString = configuration.GetConnectionString("Default");
|
||||
if (!string.IsNullOrEmpty(connString)) return connString;
|
||||
if (!string.IsNullOrEmpty(connString))
|
||||
return connString;
|
||||
|
||||
throw new InvalidOperationException(
|
||||
"Database connection string not configured. Set DB_CONNECTION_STRING or DB_SERVER, DB_NAME, DB_USER, DB_PASSWORD env vars or ConnectionStrings:Default."
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,4 +12,4 @@ public interface ISqlConnectionFactory
|
||||
/// </summary>
|
||||
/// <returns>A new <see cref="DbConnection" /> instance.</returns>
|
||||
DbConnection CreateConnection();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,15 +6,12 @@
|
||||
<RootNamespace>Infrastructure.Sql</RootNamespace>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Data.SqlClient" Version="6.1.4"/>
|
||||
<PackageReference Include="Microsoft.Data.SqlClient" Version="6.1.4" />
|
||||
<PackageReference Include="Microsoft.SqlServer.Types" Version="160.1000.6" />
|
||||
<PackageReference Include="System.Data.SqlClient" Version="4.9.0" />
|
||||
<PackageReference
|
||||
Include="Microsoft.SqlServer.Types"
|
||||
Version="160.1000.6"
|
||||
/>
|
||||
<PackageReference Include="System.Data.SqlClient" Version="4.9.0"/>
|
||||
<PackageReference
|
||||
Include="Microsoft.Extensions.Configuration.Abstractions"
|
||||
Version="9.0.0"
|
||||
Include="Microsoft.Extensions.Configuration.Abstractions"
|
||||
Version="9.0.0"
|
||||
/>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -29,4 +29,4 @@ public abstract class Repository<T>(ISqlConnectionFactory connectionFactory)
|
||||
/// <param name="reader">The data reader positioned on the row to map.</param>
|
||||
/// <returns>The mapped entity instance.</returns>
|
||||
protected abstract T MapToEntity(DbDataReader reader);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,28 +17,20 @@ public static class SqlConnectionStringHelper
|
||||
{
|
||||
string server =
|
||||
Environment.GetEnvironmentVariable("DB_SERVER")
|
||||
?? throw new InvalidOperationException(
|
||||
"DB_SERVER environment variable is not set"
|
||||
);
|
||||
?? throw new InvalidOperationException("DB_SERVER environment variable is not set");
|
||||
|
||||
string dbName =
|
||||
databaseName
|
||||
?? Environment.GetEnvironmentVariable("DB_NAME")
|
||||
?? throw new InvalidOperationException(
|
||||
"DB_NAME environment variable is not set"
|
||||
);
|
||||
?? throw new InvalidOperationException("DB_NAME environment variable is not set");
|
||||
|
||||
string user =
|
||||
Environment.GetEnvironmentVariable("DB_USER")
|
||||
?? throw new InvalidOperationException(
|
||||
"DB_USER environment variable is not set"
|
||||
);
|
||||
?? throw new InvalidOperationException("DB_USER environment variable is not set");
|
||||
|
||||
string password =
|
||||
Environment.GetEnvironmentVariable("DB_PASSWORD")
|
||||
?? throw new InvalidOperationException(
|
||||
"DB_PASSWORD environment variable is not set"
|
||||
);
|
||||
?? throw new InvalidOperationException("DB_PASSWORD environment variable is not set");
|
||||
|
||||
SqlConnectionStringBuilder builder = new()
|
||||
{
|
||||
@@ -47,7 +39,7 @@ public static class SqlConnectionStringHelper
|
||||
UserID = user,
|
||||
Password = password,
|
||||
TrustServerCertificate = true,
|
||||
Encrypt = true
|
||||
Encrypt = true,
|
||||
};
|
||||
|
||||
return builder.ConnectionString;
|
||||
@@ -61,4 +53,4 @@ public static class SqlConnectionStringHelper
|
||||
{
|
||||
return BuildConnectionString("master");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user