From 6a44afd6b61e76f58500b452e9078bc002430bdc Mon Sep 17 00:00:00 2001 From: Aaron Po Date: Sat, 22 Aug 2026 20:26:13 -0400 Subject: [PATCH] Replace raw environment variable reads with IConfiguration in backend --- .../JwtAuthenticationHandler.cs | 2 +- web/backend/API/API.Specs/Steps/AuthSteps.cs | 33 ++++++--- .../Database.Migrations.csproj | 5 ++ .../Database/Database.Migrations/Program.cs | 26 ++++--- .../DatabaseHelpers/ConnectionStrings.cs | 5 +- web/backend/Database/Database.Seed/Program.cs | 7 +- .../Services/TokenServiceRefreshTests.cs | 31 ++++---- .../Services/TokenServiceValidationTests.cs | 28 ++++---- .../Features.Auth/Services/TokenService.cs | 14 ++-- .../Features.Emails/Features.Emails.csproj | 7 ++ .../Services/EmailDispatcher.cs | 12 ++-- .../Infrastructure.Email.csproj | 4 ++ .../Infrastructure.Email/SmtpEmailProvider.cs | 23 +++--- .../DefaultSqlConnectionFactory.cs | 8 +-- .../SqlConnectionStringHelper.cs | 44 ++++++------ .../types/app/routes/+types/account.ts | 71 +++++++++++++++++++ .../types/app/routes/+types/brewery-detail.ts | 71 +++++++++++++++++++ 17 files changed, 291 insertions(+), 100 deletions(-) create mode 100644 web/frontend/.react-router/types/app/routes/+types/account.ts create mode 100644 web/frontend/.react-router/types/app/routes/+types/brewery-detail.ts diff --git a/web/backend/API/API.Core/Authentication/JwtAuthenticationHandler.cs b/web/backend/API/API.Core/Authentication/JwtAuthenticationHandler.cs index 621dcd5a..5a535eb5 100644 --- a/web/backend/API/API.Core/Authentication/JwtAuthenticationHandler.cs +++ b/web/backend/API/API.Core/Authentication/JwtAuthenticationHandler.cs @@ -32,7 +32,7 @@ public class JwtAuthenticationHandler( protected override async Task HandleAuthenticateAsync() { // Use the same access-token secret source as TokenService to avoid mismatched validation. - string? secret = Environment.GetEnvironmentVariable("ACCESS_TOKEN_SECRET"); + string? secret = configuration["ACCESS_TOKEN_SECRET"]; if (string.IsNullOrWhiteSpace(secret)) secret = configuration["Jwt:SecretKey"]; diff --git a/web/backend/API/API.Specs/Steps/AuthSteps.cs b/web/backend/API/API.Specs/Steps/AuthSteps.cs index 24f8ef62..3efe67c0 100644 --- a/web/backend/API/API.Specs/Steps/AuthSteps.cs +++ b/web/backend/API/API.Specs/Steps/AuthSteps.cs @@ -2,6 +2,8 @@ using System.Text; using System.Text.Json; using FluentAssertions; using Infrastructure.Jwt; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; using Reqnroll; namespace API.Specs.Steps; @@ -19,11 +21,8 @@ public class AuthSteps(ScenarioContext scenario) private const string PreviousAccessTokenKey = "previousAccessToken"; private const string PreviousRefreshTokenKey = "previousRefreshToken"; - private HttpClient GetClient() + private TestApiFactory GetFactory() { - if (scenario.TryGetValue(ClientKey, out HttpClient? client)) - return client; - TestApiFactory? factory = scenario.TryGetValue( FactoryKey, out TestApiFactory? f @@ -31,16 +30,28 @@ public class AuthSteps(ScenarioContext scenario) ? f : new TestApiFactory(); scenario[FactoryKey] = factory; + return factory; + } - client = factory.CreateClient(); + private HttpClient GetClient() + { + if (scenario.TryGetValue(ClientKey, out HttpClient? client)) + return client; + + client = GetFactory().CreateClient(); scenario[ClientKey] = client; return client; } - private static string GetRequiredEnvVar(string name) + /// + /// Reads a required configuration value from the running 's own + /// , so forged tokens are always signed with the exact secret the + /// app under test resolved -- rather than assuming this process's environment variables match it. + /// + private string GetRequiredConfigValue(string key) { - return Environment.GetEnvironmentVariable(name) - ?? throw new InvalidOperationException($"{name} environment variable is not set"); + return GetFactory().Services.GetRequiredService()[key] + ?? throw new InvalidOperationException($"{key} configuration value is not set"); } private static string GenerateJwtToken( @@ -420,7 +431,7 @@ public class AuthSteps(ScenarioContext scenario) ? user : throw new InvalidOperationException("registered username not found in scenario"); - string secret = GetRequiredEnvVar("ACCESS_TOKEN_SECRET"); + string secret = GetRequiredConfigValue("ACCESS_TOKEN_SECRET"); scenario["accessToken"] = GenerateJwtToken( userId, username, @@ -439,7 +450,7 @@ public class AuthSteps(ScenarioContext scenario) ? user : throw new InvalidOperationException("registered username not found in scenario"); - string secret = GetRequiredEnvVar("CONFIRMATION_TOKEN_SECRET"); + string secret = GetRequiredConfigValue("CONFIRMATION_TOKEN_SECRET"); scenario["confirmationToken"] = GenerateJwtToken( userId, username, @@ -458,7 +469,7 @@ public class AuthSteps(ScenarioContext scenario) ? user : throw new InvalidOperationException("registered username not found in scenario"); - string secret = GetRequiredEnvVar("CONFIRMATION_TOKEN_SECRET"); + string secret = GetRequiredConfigValue("CONFIRMATION_TOKEN_SECRET"); scenario["confirmationToken"] = GenerateJwtToken( userId, username, diff --git a/web/backend/Database/Database.Migrations/Database.Migrations.csproj b/web/backend/Database/Database.Migrations/Database.Migrations.csproj index 35e0da20..c3bfe487 100644 --- a/web/backend/Database/Database.Migrations/Database.Migrations.csproj +++ b/web/backend/Database/Database.Migrations/Database.Migrations.csproj @@ -11,6 +11,11 @@ + + diff --git a/web/backend/Database/Database.Migrations/Program.cs b/web/backend/Database/Database.Migrations/Program.cs index 202d5e06..e034d79f 100644 --- a/web/backend/Database/Database.Migrations/Program.cs +++ b/web/backend/Database/Database.Migrations/Program.cs @@ -4,13 +4,14 @@ using DbUp; using DbUp.Engine; using Infrastructure.Sql; using Microsoft.Data.SqlClient; +using Microsoft.Extensions.Configuration; namespace Database.Migrations; /// -/// Entry point for the database migration runner. Reads connection details from environment -/// variables, optionally drops and recreates the target database, and applies all SQL -/// migration scripts embedded in this assembly using DbUp. +/// Entry point for the database migration runner. Reads connection details from configuration +/// (backed by environment variables), optionally drops and recreates the target database, and +/// applies all SQL migration scripts embedded in this assembly using DbUp. /// public static class Program { @@ -27,7 +28,10 @@ public static class Program try { - DatabaseSettings settings = DatabaseSettings.FromEnvironment(); + IConfiguration configuration = new ConfigurationBuilder() + .AddEnvironmentVariables() + .Build(); + DatabaseSettings settings = DatabaseSettings.FromConfiguration(configuration); if (settings.ClearDatabase) { @@ -159,28 +163,28 @@ public static class Program /// /// Builds the settings from the DB_SERVER, DB_NAME, DB_USER, /// DB_PASSWORD, DB_TRUST_SERVER_CERTIFICATE, and CLEAR_DATABASE - /// environment variables. + /// configuration values. /// /// - /// Thrown when a required environment variable is not set. + /// Thrown when a required configuration value is not set. /// - public static DatabaseSettings FromEnvironment() + public static DatabaseSettings FromConfiguration(IConfiguration configuration) { string databaseName = - Environment.GetEnvironmentVariable("DB_NAME") + configuration["DB_NAME"] ?? throw new InvalidOperationException( "The DB_NAME environment variable is not set." ); bool clearDatabase = string.Equals( - Environment.GetEnvironmentVariable("CLEAR_DATABASE"), + configuration["CLEAR_DATABASE"], "true", StringComparison.OrdinalIgnoreCase ); return new DatabaseSettings( - SqlConnectionStringHelper.BuildConnectionString(databaseName), - SqlConnectionStringHelper.BuildMasterConnectionString(), + SqlConnectionStringHelper.BuildConnectionString(configuration, databaseName), + SqlConnectionStringHelper.BuildMasterConnectionString(configuration), databaseName, clearDatabase ); diff --git a/web/backend/Database/Database.Seed/DatabaseHelpers/ConnectionStrings.cs b/web/backend/Database/Database.Seed/DatabaseHelpers/ConnectionStrings.cs index b95affa0..6fcfcac5 100644 --- a/web/backend/Database/Database.Seed/DatabaseHelpers/ConnectionStrings.cs +++ b/web/backend/Database/Database.Seed/DatabaseHelpers/ConnectionStrings.cs @@ -1,6 +1,7 @@ #region using Infrastructure.Sql; +using Microsoft.Extensions.Configuration; #endregion @@ -8,7 +9,9 @@ namespace Database.Seed.DatabaseHelpers; public class ConnectionStrings { - public static string SqlServerConnectionString => SqlConnectionStringHelper.BuildConnectionString(); + public static string GetSqlServerConnectionString(IConfiguration configuration) => + SqlConnectionStringHelper.BuildConnectionString(configuration); + public static string SqliteConnectionString => "Data Source=SeedData/biergarten_seed_2026-07-20T05-46-02.993511Z.sqlite"; } diff --git a/web/backend/Database/Database.Seed/Program.cs b/web/backend/Database/Database.Seed/Program.cs index f713c07b..e6be1a44 100644 --- a/web/backend/Database/Database.Seed/Program.cs +++ b/web/backend/Database/Database.Seed/Program.cs @@ -22,9 +22,12 @@ static async Task RunAsync() { try { + IConfiguration configuration = new ConfigurationBuilder() + .AddEnvironmentVariables() + .Build(); + ServiceCollection services = []; - services.AddSingleton( - new ConfigurationBuilder().Build()); + services.AddSingleton(configuration); services.AddInfrastructureSql(); services.AddFeaturesBreweries(); services.AddFeaturesLocations(); diff --git a/web/backend/Features/Features.Auth.Tests/Services/TokenServiceRefreshTests.cs b/web/backend/Features/Features.Auth.Tests/Services/TokenServiceRefreshTests.cs index 9e9215d2..a9bae466 100644 --- a/web/backend/Features/Features.Auth.Tests/Services/TokenServiceRefreshTests.cs +++ b/web/backend/Features/Features.Auth.Tests/Services/TokenServiceRefreshTests.cs @@ -7,6 +7,7 @@ using Features.Auth.Tests.TestSupport; using FluentAssertions; using Infrastructure.Jwt; using Microsoft.AspNetCore.Identity; +using Microsoft.Extensions.Configuration; using Moq; namespace Features.Auth.Tests.Services; @@ -22,21 +23,23 @@ public class TokenServiceRefreshTests _tokenInfraMock = new Mock(); _userManagerMock = UserManagerMockFactory.Create(); - // Set environment variables for tokens - Environment.SetEnvironmentVariable( - "ACCESS_TOKEN_SECRET", - "test-access-secret-that-is-very-long-1234567890" - ); - Environment.SetEnvironmentVariable( - "REFRESH_TOKEN_SECRET", - "test-refresh-secret-that-is-very-long-1234567890" - ); - Environment.SetEnvironmentVariable( - "CONFIRMATION_TOKEN_SECRET", - "test-confirmation-secret-that-is-very-long-1234567890" - ); + IConfiguration configuration = new ConfigurationBuilder() + .AddInMemoryCollection( + new Dictionary + { + ["ACCESS_TOKEN_SECRET"] = "test-access-secret-that-is-very-long-1234567890", + ["REFRESH_TOKEN_SECRET"] = "test-refresh-secret-that-is-very-long-1234567890", + ["CONFIRMATION_TOKEN_SECRET"] = + "test-confirmation-secret-that-is-very-long-1234567890", + } + ) + .Build(); - _tokenService = new TokenService(_tokenInfraMock.Object, _userManagerMock.Object); + _tokenService = new TokenService( + _tokenInfraMock.Object, + _userManagerMock.Object, + configuration + ); } [Fact] diff --git a/web/backend/Features/Features.Auth.Tests/Services/TokenServiceValidationTests.cs b/web/backend/Features/Features.Auth.Tests/Services/TokenServiceValidationTests.cs index 213ef7fd..cb8e621b 100644 --- a/web/backend/Features/Features.Auth.Tests/Services/TokenServiceValidationTests.cs +++ b/web/backend/Features/Features.Auth.Tests/Services/TokenServiceValidationTests.cs @@ -5,6 +5,7 @@ using Features.Auth.Services; using Features.Auth.Tests.TestSupport; using FluentAssertions; using Infrastructure.Jwt; +using Microsoft.Extensions.Configuration; using Moq; namespace Features.Auth.Tests.Services; @@ -18,23 +19,22 @@ public class TokenServiceValidationTests { _tokenInfraMock = new Mock(); - // Set environment variables for tokens - Environment.SetEnvironmentVariable( - "ACCESS_TOKEN_SECRET", - "test-access-secret-that-is-very-long-1234567890" - ); - Environment.SetEnvironmentVariable( - "REFRESH_TOKEN_SECRET", - "test-refresh-secret-that-is-very-long-1234567890" - ); - Environment.SetEnvironmentVariable( - "CONFIRMATION_TOKEN_SECRET", - "test-confirmation-secret-that-is-very-long-1234567890" - ); + IConfiguration configuration = new ConfigurationBuilder() + .AddInMemoryCollection( + new Dictionary + { + ["ACCESS_TOKEN_SECRET"] = "test-access-secret-that-is-very-long-1234567890", + ["REFRESH_TOKEN_SECRET"] = "test-refresh-secret-that-is-very-long-1234567890", + ["CONFIRMATION_TOKEN_SECRET"] = + "test-confirmation-secret-that-is-very-long-1234567890", + } + ) + .Build(); _tokenService = new TokenService( _tokenInfraMock.Object, - UserManagerMockFactory.Create().Object + UserManagerMockFactory.Create().Object, + configuration ); } diff --git a/web/backend/Features/Features.Auth/Services/TokenService.cs b/web/backend/Features/Features.Auth/Services/TokenService.cs index 1f6e4d4c..1c697874 100644 --- a/web/backend/Features/Features.Auth/Services/TokenService.cs +++ b/web/backend/Features/Features.Auth/Services/TokenService.cs @@ -4,12 +4,13 @@ using Domain.Exceptions; using Features.Auth.Identity; using Infrastructure.Jwt; using Microsoft.AspNetCore.Identity; +using Microsoft.Extensions.Configuration; namespace Features.Auth.Services; /// /// Default implementation of that generates and validates JWTs -/// for access, refresh, and confirmation flows using secrets read from environment variables. +/// for access, refresh, and confirmation flows using secrets read from configuration. /// public class TokenService : ITokenService { @@ -22,30 +23,31 @@ public class TokenService : ITokenService /// Initializes a new instance of the class. /// /// Thrown when any of the ACCESS_TOKEN_SECRET, REFRESH_TOKEN_SECRET, or - /// CONFIRMATION_TOKEN_SECRET environment variables are not set. + /// CONFIRMATION_TOKEN_SECRET configuration values are not set. /// public TokenService( ITokenInfrastructure tokenInfrastructure, - UserManager userManager + UserManager userManager, + IConfiguration configuration ) { _tokenInfrastructure = tokenInfrastructure; _userManager = userManager; _accessTokenSecret = - Environment.GetEnvironmentVariable("ACCESS_TOKEN_SECRET") + configuration["ACCESS_TOKEN_SECRET"] ?? throw new InvalidOperationException( "ACCESS_TOKEN_SECRET environment variable is not set" ); _refreshTokenSecret = - Environment.GetEnvironmentVariable("REFRESH_TOKEN_SECRET") + configuration["REFRESH_TOKEN_SECRET"] ?? throw new InvalidOperationException( "REFRESH_TOKEN_SECRET environment variable is not set" ); _confirmationTokenSecret = - Environment.GetEnvironmentVariable("CONFIRMATION_TOKEN_SECRET") + configuration["CONFIRMATION_TOKEN_SECRET"] ?? throw new InvalidOperationException( "CONFIRMATION_TOKEN_SECRET environment variable is not set" ); diff --git a/web/backend/Features/Features.Emails/Features.Emails.csproj b/web/backend/Features/Features.Emails/Features.Emails.csproj index 2f261aa6..9271696c 100644 --- a/web/backend/Features/Features.Emails/Features.Emails.csproj +++ b/web/backend/Features/Features.Emails/Features.Emails.csproj @@ -6,6 +6,13 @@ Features.Emails + + + + diff --git a/web/backend/Features/Features.Emails/Services/EmailDispatcher.cs b/web/backend/Features/Features.Emails/Services/EmailDispatcher.cs index 1d05fc11..32395220 100644 --- a/web/backend/Features/Features.Emails/Services/EmailDispatcher.cs +++ b/web/backend/Features/Features.Emails/Services/EmailDispatcher.cs @@ -1,5 +1,6 @@ using Infrastructure.Email; using Infrastructure.Email.Templates.Rendering; +using Microsoft.Extensions.Configuration; namespace Features.Emails.Services; @@ -8,18 +9,19 @@ namespace Features.Emails.Services; /// public class EmailDispatcher( IEmailProvider emailProvider, - IEmailTemplateProvider emailTemplateProvider + IEmailTemplateProvider emailTemplateProvider, + IConfiguration configuration ) : IEmailDispatcher { /// /// The base URL of the website, used to build confirmation links. Read from the - /// WEBSITE_BASE_URL environment variable. + /// WEBSITE_BASE_URL configuration value. /// /// - /// Thrown at type initialization time when the WEBSITE_BASE_URL environment variable is not set. + /// Thrown when the WEBSITE_BASE_URL configuration value is not set. /// - private static readonly string WebsiteBaseUrl = - Environment.GetEnvironmentVariable("WEBSITE_BASE_URL") + private string WebsiteBaseUrl => + configuration["WEBSITE_BASE_URL"] ?? throw new InvalidOperationException("WEBSITE_BASE_URL environment variable is not set"); /// diff --git a/web/backend/Infrastructure/Infrastructure.Email/Infrastructure.Email.csproj b/web/backend/Infrastructure/Infrastructure.Email/Infrastructure.Email.csproj index 0cea9faf..24c165b5 100644 --- a/web/backend/Infrastructure/Infrastructure.Email/Infrastructure.Email.csproj +++ b/web/backend/Infrastructure/Infrastructure.Email/Infrastructure.Email.csproj @@ -8,5 +8,9 @@ + diff --git a/web/backend/Infrastructure/Infrastructure.Email/SmtpEmailProvider.cs b/web/backend/Infrastructure/Infrastructure.Email/SmtpEmailProvider.cs index 4928ca16..70f6741e 100644 --- a/web/backend/Infrastructure/Infrastructure.Email/SmtpEmailProvider.cs +++ b/web/backend/Infrastructure/Infrastructure.Email/SmtpEmailProvider.cs @@ -1,12 +1,13 @@ using MailKit.Net.Smtp; using MailKit.Security; +using Microsoft.Extensions.Configuration; using MimeKit; namespace Infrastructure.Email; /// /// SMTP email service implementation using MailKit. -/// Configured via environment variables. +/// Configured via application configuration (backed by environment variables). /// public class SmtpEmailProvider : IEmailProvider { @@ -20,35 +21,35 @@ public class SmtpEmailProvider : IEmailProvider /// /// Initializes a new instance of the class, reading SMTP configuration - /// from environment variables (SMTP_HOST, SMTP_PORT, SMTP_USERNAME, - /// SMTP_PASSWORD, SMTP_USE_SSL, SMTP_FROM_EMAIL, SMTP_FROM_NAME). + /// (SMTP_HOST, SMTP_PORT, SMTP_USERNAME, SMTP_PASSWORD, SMTP_USE_SSL, + /// SMTP_FROM_EMAIL, SMTP_FROM_NAME) from . /// /// /// Thrown when SMTP_HOST or SMTP_FROM_EMAIL is not set, or when SMTP_PORT is not a valid integer. /// - public SmtpEmailProvider() + public SmtpEmailProvider(IConfiguration configuration) { _host = - Environment.GetEnvironmentVariable("SMTP_HOST") + configuration["SMTP_HOST"] ?? throw new InvalidOperationException("SMTP_HOST environment variable is not set"); - string portString = Environment.GetEnvironmentVariable("SMTP_PORT") ?? "587"; + string portString = configuration["SMTP_PORT"] ?? "587"; if (!int.TryParse(portString, out _port)) throw new InvalidOperationException($"SMTP_PORT '{portString}' is not a valid integer"); - _username = Environment.GetEnvironmentVariable("SMTP_USERNAME"); - _password = Environment.GetEnvironmentVariable("SMTP_PASSWORD"); + _username = configuration["SMTP_USERNAME"]; + _password = configuration["SMTP_PASSWORD"]; - string useSslString = Environment.GetEnvironmentVariable("SMTP_USE_SSL") ?? "true"; + string useSslString = configuration["SMTP_USE_SSL"] ?? "true"; _useSsl = bool.Parse(useSslString); _fromEmail = - Environment.GetEnvironmentVariable("SMTP_FROM_EMAIL") + configuration["SMTP_FROM_EMAIL"] ?? throw new InvalidOperationException( "SMTP_FROM_EMAIL environment variable is not set" ); - _fromName = Environment.GetEnvironmentVariable("SMTP_FROM_NAME") ?? "The Biergarten"; + _fromName = configuration["SMTP_FROM_NAME"] ?? "The Biergarten"; } /// diff --git a/web/backend/Infrastructure/Infrastructure.Sql/DefaultSqlConnectionFactory.cs b/web/backend/Infrastructure/Infrastructure.Sql/DefaultSqlConnectionFactory.cs index ed59f78e..e93396eb 100644 --- a/web/backend/Infrastructure/Infrastructure.Sql/DefaultSqlConnectionFactory.cs +++ b/web/backend/Infrastructure/Infrastructure.Sql/DefaultSqlConnectionFactory.cs @@ -20,21 +20,21 @@ public class DefaultSqlConnectionFactory(IConfiguration configuration) : ISqlCon /// /// Resolves the SQL Server connection string, preferring (in order): the DB_CONNECTION_STRING - /// environment variable, a connection string built from individual DB_* environment variables, + /// configuration value, a connection string built from individual DB_* configuration values, /// and finally the "Default" connection string from configuration. /// /// Thrown when no connection string can be resolved from any source. private static string GetConnectionString(IConfiguration configuration) { // Check for full connection string first - string? fullConnectionString = Environment.GetEnvironmentVariable("DB_CONNECTION_STRING"); + string? fullConnectionString = configuration["DB_CONNECTION_STRING"]; if (!string.IsNullOrEmpty(fullConnectionString)) return fullConnectionString; - // Try to build from individual environment variables (preferred method for Docker) + // Try to build from individual configuration values (preferred method for Docker) try { - return SqlConnectionStringHelper.BuildConnectionString(); + return SqlConnectionStringHelper.BuildConnectionString(configuration); } catch (InvalidOperationException) { diff --git a/web/backend/Infrastructure/Infrastructure.Sql/SqlConnectionStringHelper.cs b/web/backend/Infrastructure/Infrastructure.Sql/SqlConnectionStringHelper.cs index 047f014b..8512ce33 100644 --- a/web/backend/Infrastructure/Infrastructure.Sql/SqlConnectionStringHelper.cs +++ b/web/backend/Infrastructure/Infrastructure.Sql/SqlConnectionStringHelper.cs @@ -1,23 +1,29 @@ using Microsoft.Data.SqlClient; +using Microsoft.Extensions.Configuration; namespace Infrastructure.Sql; /// -/// Helper for building SQL Server connection strings from environment variables. +/// Helper for building SQL Server connection strings from configuration +/// (backed by the DB_* environment variables). /// public static class SqlConnectionStringHelper { /// - /// Builds a SQL Server connection string from environment variables. + /// Builds a SQL Server connection string from configuration. /// /// Throws if DB_SERVER, DB_NAME, DB_USER, or DB_PASSWORD is unset. - /// If null, falls back to the DB_NAME environment variable. - public static string BuildConnectionString(string? databaseName = null) + /// The configuration to read the DB_* values from. + /// If null, falls back to the DB_NAME configuration value. + public static string BuildConnectionString( + IConfiguration configuration, + string? databaseName = null + ) { - string server = Required("DB_SERVER"); - string dbName = databaseName ?? Required("DB_NAME"); - string user = Required("DB_USER"); - string password = Required("DB_PASSWORD"); + string server = Required(configuration, "DB_SERVER"); + string dbName = databaseName ?? Required(configuration, "DB_NAME"); + string user = Required(configuration, "DB_USER"); + string password = Required(configuration, "DB_PASSWORD"); SqlConnectionStringBuilder builder = new() { @@ -25,7 +31,7 @@ public static class SqlConnectionStringHelper InitialCatalog = dbName, UserID = user, Password = password, - TrustServerCertificate = ResolveTrustServerCertificate(), + TrustServerCertificate = ResolveTrustServerCertificate(configuration), Encrypt = true, }; @@ -33,29 +39,27 @@ public static class SqlConnectionStringHelper } /// - /// Builds a connection string to the master database using environment variables. + /// Builds a connection string to the master database using configuration. /// - public static string BuildMasterConnectionString() + public static string BuildMasterConnectionString(IConfiguration configuration) { - return BuildConnectionString("master"); + return BuildConnectionString(configuration, "master"); } /// - /// Resolves the DB_TRUST_SERVER_CERTIFICATE environment variable, defaulting to + /// Resolves the DB_TRUST_SERVER_CERTIFICATE configuration value, defaulting to /// true when it is unset or not a valid boolean. /// - private static bool ResolveTrustServerCertificate() + private static bool ResolveTrustServerCertificate(IConfiguration configuration) { return !bool.TryParse( - Environment.GetEnvironmentVariable("DB_TRUST_SERVER_CERTIFICATE"), + configuration["DB_TRUST_SERVER_CERTIFICATE"], out bool trustServerCertificate ) || trustServerCertificate; } - private static string Required(string variableName) => - Environment.GetEnvironmentVariable(variableName) - ?? throw new InvalidOperationException( - $"The {variableName} environment variable is not set." - ); + private static string Required(IConfiguration configuration, string key) => + configuration[key] + ?? throw new InvalidOperationException($"The {key} environment variable is not set."); } diff --git a/web/frontend/.react-router/types/app/routes/+types/account.ts b/web/frontend/.react-router/types/app/routes/+types/account.ts new file mode 100644 index 00000000..17f37b01 --- /dev/null +++ b/web/frontend/.react-router/types/app/routes/+types/account.ts @@ -0,0 +1,71 @@ +// Generated by React Router + +import type { GetInfo, GetAnnotations } from "react-router/internal"; + +type Module = typeof import("../account.js") + +type Info = GetInfo<{ + file: "routes/account.tsx", + module: Module +}> + +type Matches = [{ + id: "root"; + module: typeof import("../../root.js"); +}, { + id: "routes/account"; + module: typeof import("../account.js"); +}]; + +type Annotations = GetAnnotations; + +export namespace Route { + // links + export type LinkDescriptors = Annotations["LinkDescriptors"]; + export type LinksFunction = Annotations["LinksFunction"]; + + // meta + export type MetaArgs = Annotations["MetaArgs"]; + export type MetaDescriptors = Annotations["MetaDescriptors"]; + export type MetaFunction = Annotations["MetaFunction"]; + + // headers + export type HeadersArgs = Annotations["HeadersArgs"]; + export type HeadersFunction = Annotations["HeadersFunction"]; + + // middleware + export type MiddlewareFunction = Annotations["MiddlewareFunction"]; + + // clientMiddleware + export type ClientMiddlewareFunction = Annotations["ClientMiddlewareFunction"]; + + // loader + export type LoaderArgs = Annotations["LoaderArgs"]; + + // clientLoader + export type ClientLoaderArgs = Annotations["ClientLoaderArgs"]; + + // action + export type ActionArgs = Annotations["ActionArgs"]; + + // clientAction + export type ClientActionArgs = Annotations["ClientActionArgs"]; + + // HydrateFallback + export type HydrateFallbackProps = Annotations["HydrateFallbackProps"]; + + // ServerHydrateFallback + export type ServerHydrateFallbackProps = Annotations["ServerHydrateFallbackProps"]; + + // Component + export type ComponentProps = Annotations["ComponentProps"]; + + // ServerComponent + export type ServerComponentProps = Annotations["ServerComponentProps"]; + + // ErrorBoundary + export type ErrorBoundaryProps = Annotations["ErrorBoundaryProps"]; + + // ServerErrorBoundary + export type ServerErrorBoundaryProps = Annotations["ServerErrorBoundaryProps"]; +} \ No newline at end of file diff --git a/web/frontend/.react-router/types/app/routes/+types/brewery-detail.ts b/web/frontend/.react-router/types/app/routes/+types/brewery-detail.ts new file mode 100644 index 00000000..54aeca27 --- /dev/null +++ b/web/frontend/.react-router/types/app/routes/+types/brewery-detail.ts @@ -0,0 +1,71 @@ +// Generated by React Router + +import type { GetInfo, GetAnnotations } from "react-router/internal"; + +type Module = typeof import("../brewery-detail.js") + +type Info = GetInfo<{ + file: "routes/brewery-detail.tsx", + module: Module +}> + +type Matches = [{ + id: "root"; + module: typeof import("../../root.js"); +}, { + id: "routes/brewery-detail"; + module: typeof import("../brewery-detail.js"); +}]; + +type Annotations = GetAnnotations; + +export namespace Route { + // links + export type LinkDescriptors = Annotations["LinkDescriptors"]; + export type LinksFunction = Annotations["LinksFunction"]; + + // meta + export type MetaArgs = Annotations["MetaArgs"]; + export type MetaDescriptors = Annotations["MetaDescriptors"]; + export type MetaFunction = Annotations["MetaFunction"]; + + // headers + export type HeadersArgs = Annotations["HeadersArgs"]; + export type HeadersFunction = Annotations["HeadersFunction"]; + + // middleware + export type MiddlewareFunction = Annotations["MiddlewareFunction"]; + + // clientMiddleware + export type ClientMiddlewareFunction = Annotations["ClientMiddlewareFunction"]; + + // loader + export type LoaderArgs = Annotations["LoaderArgs"]; + + // clientLoader + export type ClientLoaderArgs = Annotations["ClientLoaderArgs"]; + + // action + export type ActionArgs = Annotations["ActionArgs"]; + + // clientAction + export type ClientActionArgs = Annotations["ClientActionArgs"]; + + // HydrateFallback + export type HydrateFallbackProps = Annotations["HydrateFallbackProps"]; + + // ServerHydrateFallback + export type ServerHydrateFallbackProps = Annotations["ServerHydrateFallbackProps"]; + + // Component + export type ComponentProps = Annotations["ComponentProps"]; + + // ServerComponent + export type ServerComponentProps = Annotations["ServerComponentProps"]; + + // ErrorBoundary + export type ErrorBoundaryProps = Annotations["ErrorBoundaryProps"]; + + // ServerErrorBoundary + export type ServerErrorBoundaryProps = Annotations["ServerErrorBoundaryProps"]; +} \ No newline at end of file