/// 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 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
/// 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.
// The in-memory backend is recreated on every launch, so it is migrated on startup. The MySQL/MariaDB
// backend is left untouched here: a shared database is migrated out of band to avoid multi-instance races.
ifcase.inMemory=reader.driver{
app.beforeServerStarts{
tryawaitfluent.migrate()
}
}
returnapp
}
/// 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
/// booting instance.
/// - Parameter reader: the configuration reader the values are read from.
funcmigration(
reader:ConfigReader
)asyncthrows{
letlogger=logger(
serverName:reader.serverName,
logLevel:reader.logLevel
)
letservice=Service(
driver:reader.driver,
logger:logger
)
letfluent=service()
letprepareDB=PrepareDB()
awaitprepareDB(for:fluent)
do{
tryawaitfluent.migrate()
}
catch{
try?awaitfluent.shutdown()
throwerror
}
tryawaitfluent.shutdown()
}
// MARK: - Helpers
/// The request context type the application serves its routes with.
/// - serverName: the label applied to the logger.
/// - logLevel: the minimum level the logger emits.
/// - Returns: the configured logger.
privatefunclogger(
serverName:String,
logLevel:Logger.Level
)->Logger{
varlogger=Logger(label:serverName)
logger.logLevel=logLevel
returnlogger
}
/// Builds the application's router.
///
/// Registers the request-logging middleware, the security-headers middleware that stamps the given `securityHeaders` onto every response, the
/// response-compression middleware that compresses responses larger than `minimumResponseSizeToCompress` when the client advertises support,
/// the localization middleware that negotiates the request's language from its `Accept-Language` header, the not-found middleware that serves the
/// error page, and the static file middleware that serves the contents of `staticFilesPath` (tagging responses with the given `cacheControl`
/// directives), then adds the `RootController` routes that render the landing page and the `HealthController` routes that serve the health check.
///
/// The security-headers middleware sits just inside request logging so it covers every response that reaches a client — the landing page, the compressed
/// responses, the rendered error page, and the served static files.
/// - Parameters:
/// - staticFilesPath: the folder, relative to the working directory, the static files are served from.
/// - cacheControl: the cache-control directives applied to the served static files.
/// - compressionMinResponseSize: the minimum response body size, in bytes, before compression is applied.
/// - securityHeaders: the security headers applied to every response.
/// - logLevel: the level the request-logging middleware logs at.
/// - probe: the probe consulted by the `HealthController` readiness route.