From 9b4a780ea3c47a271d115d24e28b7f93234d575a Mon Sep 17 00:00:00 2001 From: Javier Cicchelli Date: Tue, 4 Aug 2026 16:17:32 +0200 Subject: [PATCH] Migrated the Website service and library targets to use Persistence package with PostgreSQL instead. --- Services/Website/Sources/App/App.swift | 2 +- .../Website/Sources/App/Extensions/App+Build.swift | 13 +++++++------ .../App/Extensions/ConfigReader+Properties.swift | 8 ++++---- .../Extensions/AbsoluteConfigKey+Constants.swift | 4 ++-- .../Public/Extensions/ConfigKey+Constants.swift | 6 +++--- .../Library/Public/Extensions/Int+Constants.swift | 4 ++-- .../Public/Extensions/String+Constants.swift | 6 +++--- Services/Website/Tests/App/AppTests.swift | 4 ++-- .../Public/Controllers/HealthControllerTests.swift | 6 +++--- 9 files changed, 27 insertions(+), 26 deletions(-) diff --git a/Services/Website/Sources/App/App.swift b/Services/Website/Sources/App/App.swift index d75a38f..f6b2409 100644 --- a/Services/Website/Sources/App/App.swift +++ b/Services/Website/Sources/App/App.swift @@ -42,7 +42,7 @@ struct App { return } - let app = await application( + let app = try await application( reader: reader ) diff --git a/Services/Website/Sources/App/Extensions/App+Build.swift b/Services/Website/Sources/App/Extensions/App+Build.swift index 2164f29..61c5894 100644 --- a/Services/Website/Sources/App/Extensions/App+Build.swift +++ b/Services/Website/Sources/App/Extensions/App+Build.swift @@ -12,13 +12,14 @@ import WebsiteLibrary /// Reads the log level, server name, static files location, minimum response size to compress, and security headers from the configuration, then assembles /// the router, server configuration, and logger. It warns when the localization catalog cannot be read, since pages would serve raw localization keys. /// It also builds the persistence driver, registers its migrations, and attaches the `Fluent` service so it starts -/// and stops alongside the HTTP server; the ephemeral in-memory backend is migrated on startup, while a MySQL/MariaDB backend is migrated out of +/// and stops alongside the HTTP server; the ephemeral in-memory backend is migrated on startup, while a PostgreSQL backend is migrated out of /// band (so a shared database is never migrated on boot). /// - Parameter reader: the configuration reader the values are read from. /// - Returns: the configured application, ready to run as a service. +/// - Throws: an error when the persistence service cannot be built (e.g. its TLS context fails to build). func application( reader: ConfigReader -) async -> some ApplicationProtocol { +) async throws -> some ApplicationProtocol { let languages = LanguageList() let logger = logger( serverName: reader.serverName, @@ -37,7 +38,7 @@ func application( driver: reader.driver, logger: logger ) - let fluent = persistence() + let fluent = try persistence() let fingerprintAssets = FingerprintAssets(logger: logger) let prepareDB = PrepareDB() @@ -63,7 +64,7 @@ func application( app.addServices(fluent) - // The in-memory backend is recreated on every launch, so it is migrated on startup. The MySQL/MariaDB backend is + // The in-memory backend is recreated on every launch, so it is migrated on startup. The PostgreSQL backend is // left untouched here: a shared database is migrated out of band to avoid multi-instance races. if case .inMemory = reader.driver { app.beforeServerStarts { @@ -77,7 +78,7 @@ func application( /// Runs every registered migration against the configured backend, then exits. /// /// This is the out-of-band migration path selected by the `database.migrate` flag: it builds the same driver the service would run against, applies the -/// migrations, and shuts the database down — so a shared MySQL/MariaDB database is migrated by a single deliberate invocation rather than by every +/// migrations, and shuts the database down — so a shared PostgreSQL database is migrated by a single deliberate invocation rather than by every /// booting instance. /// - Parameter reader: the configuration reader the values are read from. func migration( @@ -92,7 +93,7 @@ func migration( logger: logger ) - let fluent = service() + let fluent = try service() let prepareDB = PrepareDB() await prepareDB(for: fluent) diff --git a/Services/Website/Sources/App/Extensions/ConfigReader+Properties.swift b/Services/Website/Sources/App/Extensions/ConfigReader+Properties.swift index c596d3f..4f5bb32 100644 --- a/Services/Website/Sources/App/Extensions/ConfigReader+Properties.swift +++ b/Services/Website/Sources/App/Extensions/ConfigReader+Properties.swift @@ -59,7 +59,7 @@ package extension ConfigReader { /// The persistence backend the service runs against, derived from the `database.*` keys. /// - /// When `database.driver` selects MySQL, the connection parameters are assembled from the `database.host`, `database.port`, + /// When `database.driver` selects PostgreSQL, the connection parameters are assembled from the `database.host`, `database.port`, /// `database.name`, `database.username`, `database.password` (empty when unset), `database.tls`, and /// `database.pool.maxPerEventLoop` keys. Any other driver value falls back to the in-memory database. var driver: Driver { @@ -67,8 +67,8 @@ package extension ConfigReader { forKey: .Database.driver, default: .Database.driver ) { - case .Database.driverMySQL: - return .mysql( + case .Database.driverPostgres: + return .postgres( .init( host: string( forKey: .Database.host, @@ -199,7 +199,7 @@ private extension ConfigReader { // MARK: Properties - /// The TLS posture for the MySQL connection, mapped from the `database.tls` key: `off` and `require` map to their postures, and any + /// The TLS posture for the PostgreSQL connection, mapped from the `database.tls` key: `off` and `require` map to their postures, and any /// other value falls back to `prefer`. var tls: TLS { switch string( diff --git a/Services/Website/Sources/Library/Public/Extensions/AbsoluteConfigKey+Constants.swift b/Services/Website/Sources/Library/Public/Extensions/AbsoluteConfigKey+Constants.swift index 9e4a82e..b44f742 100644 --- a/Services/Website/Sources/Library/Public/Extensions/AbsoluteConfigKey+Constants.swift +++ b/Services/Website/Sources/Library/Public/Extensions/AbsoluteConfigKey+Constants.swift @@ -23,9 +23,9 @@ extension AbsoluteConfigKey { public static let migrate: AbsoluteConfigKey = .init(.Database.migrate) /// The absolute configuration key for the persistence driver. public static let driver: AbsoluteConfigKey = .init(.Database.driver) - /// The absolute configuration key for the MySQL/MariaDB host. + /// The absolute configuration key for the PostgreSQL host. public static let host: AbsoluteConfigKey = .init(.Database.host) - /// The absolute configuration key for the MySQL/MariaDB port. + /// The absolute configuration key for the PostgreSQL port. public static let port: AbsoluteConfigKey = .init(.Database.port) /// The absolute configuration key for the database name. public static let name: AbsoluteConfigKey = .init(.Database.name) diff --git a/Services/Website/Sources/Library/Public/Extensions/ConfigKey+Constants.swift b/Services/Website/Sources/Library/Public/Extensions/ConfigKey+Constants.swift index 289641c..a6a6492 100644 --- a/Services/Website/Sources/Library/Public/Extensions/ConfigKey+Constants.swift +++ b/Services/Website/Sources/Library/Public/Extensions/ConfigKey+Constants.swift @@ -21,11 +21,11 @@ extension ConfigKey { public enum Database { /// The configuration key selecting migrate-and-exit mode (run migrations, then exit) instead of serving. public static let migrate: ConfigKey = "database.migrate" - /// The configuration key for the persistence driver (`inMemory` or `mysql`). + /// The configuration key for the persistence driver (`inMemory` or `postgres`). public static let driver: ConfigKey = "database.driver" - /// The configuration key for the MySQL/MariaDB host. + /// The configuration key for the PostgreSQL host. public static let host: ConfigKey = "database.host" - /// The configuration key for the MySQL/MariaDB port. + /// The configuration key for the PostgreSQL port. public static let port: ConfigKey = "database.port" /// The configuration key for the database name. public static let name: ConfigKey = "database.name" diff --git a/Services/Website/Sources/Library/Public/Extensions/Int+Constants.swift b/Services/Website/Sources/Library/Public/Extensions/Int+Constants.swift index f1adf3f..a7eeda8 100644 --- a/Services/Website/Sources/Library/Public/Extensions/Int+Constants.swift +++ b/Services/Website/Sources/Library/Public/Extensions/Int+Constants.swift @@ -17,8 +17,8 @@ extension Int { } /// A namespace for the persistence's default configuration values. public enum Database { - /// The default MySQL/MariaDB port. - public static let port = 3_306 + /// The default PostgreSQL port. + public static let port = 5_432 /// The default maximum pooled connections per event loop. public static let poolMaxPerEventLoop = 4 } diff --git a/Services/Website/Sources/Library/Public/Extensions/String+Constants.swift b/Services/Website/Sources/Library/Public/Extensions/String+Constants.swift index a70db40..66940f5 100644 --- a/Services/Website/Sources/Library/Public/Extensions/String+Constants.swift +++ b/Services/Website/Sources/Library/Public/Extensions/String+Constants.swift @@ -3,9 +3,9 @@ extension String { public enum Database { /// The default persistence driver: in-memory SQLite, which needs no external infrastructure. public static let driver = "inMemory" - /// The driver token selecting the MySQL/MariaDB backend. - public static let driverMySQL = "mysql" - /// The default MySQL/MariaDB host. + /// The driver token selecting the PostgreSQL backend. + public static let driverPostgres = "postgres" + /// The default PostgreSQL host. public static let host = "localhost" /// The default database name. public static let name = "loud" diff --git a/Services/Website/Tests/App/AppTests.swift b/Services/Website/Tests/App/AppTests.swift index 846ccf2..e8b425c 100644 --- a/Services/Website/Tests/App/AppTests.swift +++ b/Services/Website/Tests/App/AppTests.swift @@ -395,8 +395,8 @@ private extension AppTests { func app( staticFilesPath: String, strictTransportSecurity: String? = nil - ) async -> some ApplicationProtocol { - await application( + ) async throws -> some ApplicationProtocol { + try await application( reader: reader( staticFilesPath: staticFilesPath, strictTransportSecurity: strictTransportSecurity diff --git a/Services/Website/Tests/Library/Cases/Public/Controllers/HealthControllerTests.swift b/Services/Website/Tests/Library/Cases/Public/Controllers/HealthControllerTests.swift index dcf3654..5c9d190 100644 --- a/Services/Website/Tests/Library/Cases/Public/Controllers/HealthControllerTests.swift +++ b/Services/Website/Tests/Library/Cases/Public/Controllers/HealthControllerTests.swift @@ -39,7 +39,7 @@ struct HealthControllerTests { driver: .inMemory, logger: Logger(label: "test") ) - let fluent = service() + let fluent = try service() do { try await app( @@ -70,7 +70,7 @@ struct HealthControllerTests { // Port 1 on the loopback interface has nothing listening, so the probe's connection is refused // immediately instead of timing out. let service = Service( - driver: .mysql( + driver: .postgres( .init( host: "127.0.0.1", port: 1, @@ -83,7 +83,7 @@ struct HealthControllerTests { ), logger: Logger(label: "test") ) - let fluent = service() + let fluent = try service() do { try await app(