Project updates from Template #1

Merged
javier merged 123 commits from project/update-from-template into main 2026-09-04 13:40:36 +00:00
9 changed files with 27 additions and 26 deletions
Showing only changes of commit 9b4a780ea3 - Show all commits
+1 -1
View File
@@ -42,7 +42,7 @@ struct App {
return return
} }
let app = await application( let app = try await application(
reader: reader reader: reader
) )
@@ -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 /// 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. /// 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 /// 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). /// band (so a shared database is never migrated on boot).
/// - Parameter reader: the configuration reader the values are read from. /// - Parameter reader: the configuration reader the values are read from.
/// - Returns: the configured application, ready to run as a service. /// - 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( func application(
reader: ConfigReader reader: ConfigReader
) async -> some ApplicationProtocol { ) async throws -> some ApplicationProtocol {
let languages = LanguageList() let languages = LanguageList()
let logger = logger( let logger = logger(
serverName: reader.serverName, serverName: reader.serverName,
@@ -37,7 +38,7 @@ func application(
driver: reader.driver, driver: reader.driver,
logger: logger logger: logger
) )
let fluent = persistence() let fluent = try persistence()
let fingerprintAssets = FingerprintAssets(logger: logger) let fingerprintAssets = FingerprintAssets(logger: logger)
let prepareDB = PrepareDB() let prepareDB = PrepareDB()
@@ -63,7 +64,7 @@ func application(
app.addServices(fluent) 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. // left untouched here: a shared database is migrated out of band to avoid multi-instance races.
if case .inMemory = reader.driver { if case .inMemory = reader.driver {
app.beforeServerStarts { app.beforeServerStarts {
@@ -77,7 +78,7 @@ func application(
/// Runs every registered migration against the configured backend, then exits. /// 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 /// 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. /// booting instance.
/// - Parameter reader: the configuration reader the values are read from. /// - Parameter reader: the configuration reader the values are read from.
func migration( func migration(
@@ -92,7 +93,7 @@ func migration(
logger: logger logger: logger
) )
let fluent = service() let fluent = try service()
let prepareDB = PrepareDB() let prepareDB = PrepareDB()
await prepareDB(for: fluent) await prepareDB(for: fluent)
@@ -59,7 +59,7 @@ package extension ConfigReader {
/// The persistence backend the service runs against, derived from the `database.*` keys. /// 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.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. /// `database.pool.maxPerEventLoop` keys. Any other driver value falls back to the in-memory database.
var driver: Driver { var driver: Driver {
@@ -67,8 +67,8 @@ package extension ConfigReader {
forKey: .Database.driver, forKey: .Database.driver,
default: .Database.driver default: .Database.driver
) { ) {
case .Database.driverMySQL: case .Database.driverPostgres:
return .mysql( return .postgres(
.init( .init(
host: string( host: string(
forKey: .Database.host, forKey: .Database.host,
@@ -199,7 +199,7 @@ private extension ConfigReader {
// MARK: Properties // 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`. /// other value falls back to `prefer`.
var tls: TLS { var tls: TLS {
switch string( switch string(
@@ -23,9 +23,9 @@ extension AbsoluteConfigKey {
public static let migrate: AbsoluteConfigKey = .init(.Database.migrate) public static let migrate: AbsoluteConfigKey = .init(.Database.migrate)
/// The absolute configuration key for the persistence driver. /// The absolute configuration key for the persistence driver.
public static let driver: AbsoluteConfigKey = .init(.Database.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) 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) public static let port: AbsoluteConfigKey = .init(.Database.port)
/// The absolute configuration key for the database name. /// The absolute configuration key for the database name.
public static let name: AbsoluteConfigKey = .init(.Database.name) public static let name: AbsoluteConfigKey = .init(.Database.name)
@@ -21,11 +21,11 @@ extension ConfigKey {
public enum Database { public enum Database {
/// The configuration key selecting migrate-and-exit mode (run migrations, then exit) instead of serving. /// The configuration key selecting migrate-and-exit mode (run migrations, then exit) instead of serving.
public static let migrate: ConfigKey = "database.migrate" 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" 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" 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" public static let port: ConfigKey = "database.port"
/// The configuration key for the database name. /// The configuration key for the database name.
public static let name: ConfigKey = "database.name" public static let name: ConfigKey = "database.name"
@@ -17,8 +17,8 @@ extension Int {
} }
/// A namespace for the persistence's default configuration values. /// A namespace for the persistence's default configuration values.
public enum Database { public enum Database {
/// The default MySQL/MariaDB port. /// The default PostgreSQL port.
public static let port = 3_306 public static let port = 5_432
/// The default maximum pooled connections per event loop. /// The default maximum pooled connections per event loop.
public static let poolMaxPerEventLoop = 4 public static let poolMaxPerEventLoop = 4
} }
@@ -3,9 +3,9 @@ extension String {
public enum Database { public enum Database {
/// The default persistence driver: in-memory SQLite, which needs no external infrastructure. /// The default persistence driver: in-memory SQLite, which needs no external infrastructure.
public static let driver = "inMemory" public static let driver = "inMemory"
/// The driver token selecting the MySQL/MariaDB backend. /// The driver token selecting the PostgreSQL backend.
public static let driverMySQL = "mysql" public static let driverPostgres = "postgres"
/// The default MySQL/MariaDB host. /// The default PostgreSQL host.
public static let host = "localhost" public static let host = "localhost"
/// The default database name. /// The default database name.
public static let name = "loud" public static let name = "loud"
+2 -2
View File
@@ -395,8 +395,8 @@ private extension AppTests {
func app( func app(
staticFilesPath: String, staticFilesPath: String,
strictTransportSecurity: String? = nil strictTransportSecurity: String? = nil
) async -> some ApplicationProtocol { ) async throws -> some ApplicationProtocol {
await application( try await application(
reader: reader( reader: reader(
staticFilesPath: staticFilesPath, staticFilesPath: staticFilesPath,
strictTransportSecurity: strictTransportSecurity strictTransportSecurity: strictTransportSecurity
@@ -39,7 +39,7 @@ struct HealthControllerTests {
driver: .inMemory, driver: .inMemory,
logger: Logger(label: "test") logger: Logger(label: "test")
) )
let fluent = service() let fluent = try service()
do { do {
try await app( 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 // Port 1 on the loopback interface has nothing listening, so the probe's connection is refused
// immediately instead of timing out. // immediately instead of timing out.
let service = Service( let service = Service(
driver: .mysql( driver: .postgres(
.init( .init(
host: "127.0.0.1", host: "127.0.0.1",
port: 1, port: 1,
@@ -83,7 +83,7 @@ struct HealthControllerTests {
), ),
logger: Logger(label: "test") logger: Logger(label: "test")
) )
let fluent = service() let fluent = try service()
do { do {
try await app( try await app(