Tweaks and fixes throughout the project (#27)
This PR contains the work done to do a little bit of housekeeping pass across all packages and the Website service. To provide further details about the work: * Refreshed the READMEs and source documentation to match the current code; * Tagged every test case consistently across the Infrastructure, Localization, Persistence, and Website test targets; * Removed Website middleware tests now covered by Infrastructure's own suite; * Conformed the `PrepareDB` method to Sendable; * Relaxes the production Compose DATABASE_TLS default from require to prefer; * Added Persistence test verifying the prefer posture falls back to plaintext connections. Reviewed-on: rock-n-code/loud-amsterdam#27 Co-authored-by: Javier Cicchelli <javier@rock-n-code.com> Co-committed-by: Javier Cicchelli <javier@rock-n-code.com>
This commit is contained in:
@@ -31,9 +31,9 @@ struct App {
|
||||
]
|
||||
)
|
||||
|
||||
// Migrate-and-exit mode runs the registered migrations against the configured backend and returns,
|
||||
// so a shared database is migrated by a single deliberate invocation (`--database-migrate`) rather
|
||||
// than by every booting instance.
|
||||
// Migrate-and-exit mode runs the registered migrations against the configured backend and returns, so a shared
|
||||
// database is migrated by a single deliberate invocation (`--database-migrate`) rather than by every booting
|
||||
// instance.
|
||||
guard !reader.migrate else {
|
||||
try await migration(
|
||||
reader: reader
|
||||
|
||||
@@ -25,8 +25,8 @@ func application(
|
||||
logLevel: reader.logLevel
|
||||
)
|
||||
|
||||
// A broken catalog degrades to serving raw localization keys rather than failing, so it is
|
||||
// only ever visible to visitors — surface it here instead.
|
||||
// A broken catalog degrades to serving raw localization keys rather than failing, so it is only ever visible to
|
||||
// visitors — surface it here instead.
|
||||
if languages.catalogState != .loaded {
|
||||
let isCatalogMissing = languages.catalogState == .missing
|
||||
|
||||
@@ -63,8 +63,8 @@ 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 left untouched here: a shared database is migrated out of band to avoid multi-instance races.
|
||||
// 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.
|
||||
if case .inMemory = reader.driver {
|
||||
app.beforeServerStarts {
|
||||
try await fluent.migrate()
|
||||
@@ -137,8 +137,7 @@ private func logger(
|
||||
/// 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, the `SubscriptionController` routes that register newsletter subscriptions, and the `HealthController` routes
|
||||
/// that serve the health check.
|
||||
/// 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.
|
||||
@@ -162,8 +161,8 @@ private func router(
|
||||
logLevel: Logger.Level,
|
||||
probe: Probe
|
||||
) -> Router<AppRequestContext> {
|
||||
// HEAD siblings are generated for every GET route, so uptime monitors and crawlers probing
|
||||
// with HEAD requests get the page's status and headers instead of a 404.
|
||||
// HEAD siblings are generated for every GET route, so uptime monitors and crawlers probing with HEAD requests get
|
||||
// the page's status and headers instead of a 404.
|
||||
let router = Router(
|
||||
context: AppRequestContext.self,
|
||||
options: .autoGenerateHeadEndpoints
|
||||
|
||||
@@ -123,9 +123,9 @@ package extension ConfigReader {
|
||||
|
||||
/// The rate limit applied to the subscription endpoint, built from the `rateLimit.*` keys.
|
||||
///
|
||||
/// `rateLimit.limit` requests are admitted per client per `rateLimit.window` seconds. When
|
||||
/// `rateLimit.trustForwardedFor` is set, clients are keyed by the first `X-Forwarded-For` entry —
|
||||
/// enable it only behind a reverse proxy that sets the header, since clients can forge it otherwise.
|
||||
/// `rateLimit.limit` requests are admitted per client per `rateLimit.window` seconds. When `rateLimit.trustForwardedFor` is set,
|
||||
/// clients are keyed by the first `X-Forwarded-For` entry — enable it only behind a reverse proxy that sets the header, since clients can forge it
|
||||
/// otherwise.
|
||||
var rateLimit: RateLimitMiddleware<AppRequestContext>.Configuration {
|
||||
.init(
|
||||
limit: int(
|
||||
|
||||
@@ -2,9 +2,8 @@ import Infrastructure
|
||||
|
||||
/// A static file shipped with the website service.
|
||||
///
|
||||
/// Each case identifies a file name stored under the static files root (the `Resources/Static`
|
||||
/// directory) and served by Hummingbird's `FileMiddleware` middleware. A name can be available
|
||||
/// with more than one extension (see ``fileExtensions``), each resolving to its own file.
|
||||
/// Each case identifies a file name stored under the static files root (the `Resources/Static` directory) and served by Hummingbird's
|
||||
/// `FileMiddleware` middleware. A name can be available with more than one extension (see ``fileExtensions``), each resolving to its own file.
|
||||
enum StaticFile: Asset, CaseIterable {
|
||||
/// The `apple-touch-icon.png` icon.
|
||||
case appleTouchIcon
|
||||
|
||||
@@ -22,8 +22,7 @@ struct IndexPage {
|
||||
/// Creates a landing page localized to the given locale.
|
||||
/// - Parameters:
|
||||
/// - locale: the locale the page content is localized to.
|
||||
/// - assetVersion: the version token appended to the page's asset URLs, or `nil` (the
|
||||
/// default) to leave them unversioned.
|
||||
/// - assetVersion: the version token appended to the page's asset URLs, or `nil` (the default) to leave them unversioned.
|
||||
init(
|
||||
locale: Locale,
|
||||
assetVersion: String? = nil
|
||||
|
||||
@@ -4,9 +4,8 @@ import NIOCore
|
||||
|
||||
/// The website's request context.
|
||||
///
|
||||
/// Extends the core request storage with the negotiated language, defaulting to the default
|
||||
/// supported language until ``LocalizationMiddleware`` resolves it from the request, and with
|
||||
/// the connected client's address, so ``RateLimitMiddleware`` can key its budgets per client.
|
||||
/// Extends the core request storage with the negotiated language, defaulting to the default supported language until ``LocalizationMiddleware``
|
||||
/// resolves it from the request, and with the connected client's address, so ``RateLimitMiddleware`` can key its budgets per client.
|
||||
public struct WebsiteRequestContext: LocalizedRequestContext, RemoteAddressRequestContext {
|
||||
|
||||
// MARK: Properties
|
||||
|
||||
@@ -28,8 +28,7 @@ public struct HealthController<Context: RequestContext> {
|
||||
// MARK: Initializers
|
||||
|
||||
/// Creates a health controller.
|
||||
/// - Parameter probe: the probe consulted by the readiness route; when `nil`, only the liveness
|
||||
/// route is served.
|
||||
/// - Parameter probe: the probe consulted by the readiness route; when `nil`, only the liveness route is served.
|
||||
public init(
|
||||
probe: Probe? = nil
|
||||
) {
|
||||
@@ -72,9 +71,8 @@ private extension HealthController {
|
||||
|
||||
/// Handles a request for the liveness check.
|
||||
///
|
||||
/// Returns a constant JSON body built directly per request — the payload is a tiny literal with no
|
||||
/// rendering step, so there is nothing to pre-render or cache. It reports only that the process is up,
|
||||
/// with no dependency check, so an orchestrator restarts the process only when the process itself is
|
||||
/// Returns a constant JSON body built directly per request — the payload is a tiny literal with no rendering step, so there is nothing to pre-render or
|
||||
/// cache. It reports only that the process is up, with no dependency check, so an orchestrator restarts the process only when the process itself is
|
||||
/// unresponsive.
|
||||
/// - Parameters:
|
||||
/// - request: the incoming request.
|
||||
@@ -93,9 +91,8 @@ private extension HealthController {
|
||||
|
||||
/// Handles a request for the readiness check.
|
||||
///
|
||||
/// Consults the `Probe` supplied at initialization and reports `200 OK` when the service's
|
||||
/// database is reachable, or `503 Service Unavailable` otherwise, so a load balancer withholds
|
||||
/// traffic from an instance that cannot yet serve it without restarting the process.
|
||||
/// Consults the `Probe` supplied at initialization and reports `200 OK` when the service's database is reachable, or `503 Service Unavailable`
|
||||
/// otherwise, so a load balancer withholds traffic from an instance that cannot yet serve it without restarting the process.
|
||||
/// - Parameters:
|
||||
/// - request: the incoming request.
|
||||
/// - context: the context the request is resolved against.
|
||||
|
||||
@@ -4,8 +4,7 @@ import Infrastructure
|
||||
|
||||
/// Serves the website's root routes.
|
||||
///
|
||||
/// The controller exposes its routes through its `RouterController` conformance, so the
|
||||
/// application that composes it registers them declaratively:
|
||||
/// The controller exposes its routes through its `RouterController` conformance, so the application that composes it registers them declaratively:
|
||||
///
|
||||
/// ```swift
|
||||
/// router.addController {
|
||||
@@ -13,8 +12,7 @@ import Infrastructure
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// - Note: `Context` is the request context the routes are resolved against, and must match the
|
||||
/// context of the router the routes are added to.
|
||||
/// - Note: `Context` is the request context the routes are resolved against, and must match the context of the router the routes are added to.
|
||||
public struct RootController<Context: LocalizedRequestContext> {
|
||||
|
||||
// MARK: Properties
|
||||
@@ -25,8 +23,7 @@ public struct RootController<Context: LocalizedRequestContext> {
|
||||
// MARK: Initializers
|
||||
|
||||
/// Creates a root controller.
|
||||
/// - Parameter assetVersion: the version token appended to the page's asset URLs, or `nil`
|
||||
/// (the default) to leave them unversioned.
|
||||
/// - Parameter assetVersion: the version token appended to the page's asset URLs, or `nil` (the default) to leave them unversioned.
|
||||
public init(
|
||||
assetVersion: String? = nil
|
||||
) {
|
||||
@@ -67,8 +64,7 @@ private extension RootController {
|
||||
|
||||
/// Handles a request for the landing page.
|
||||
///
|
||||
/// Renders the ``IndexPage`` in the language stored on the context by ``LocalizationMiddleware``,
|
||||
/// falling back to the default language.
|
||||
/// Renders the ``IndexPage`` in the language stored on the context by ``LocalizationMiddleware``, falling back to the default language.
|
||||
/// - Parameters:
|
||||
/// - request: the incoming request.
|
||||
/// - context: the context the request is resolved against.
|
||||
|
||||
Reference in New Issue
Block a user