This PR contains the work done to introduce a _Fluent_-based persistence layer for the Website service, selectable at runtime alongside the existing in-memory default, plus the local dev tooling and docs to support it.
To provide further details about the work:
* Persistence package
* The `Driver` and `TLS` enumerations
* The `Configuration` type
* The `Service` factory that builds the service
* `PrepareDB` for migrations registration
* The `Probe` for readiness checks.
* App integration
* Builds the driver, registers migrations, and attaches `Fluent` to the service lifecycle so it starts/stops with the HTTP server.
* Migrate-on-boot is gated to the in-memory backend; MySQL/MariaDB is migrated out of band via --database-migrate so shared databases never race on startup.
* The `ConfigReader+Properties` extension maps database.* config keys onto the driver.
* Library
* Added database configuration constants.
* The `HealthController` controller gains a readiness probe: `GET /health/ready` checks whether the database is reachable, separate from the existing liveness check.
* Others
* Updated the `docker-compose` files to support a database service behind a database profile, and hardened for local development
* New database targets on the `Makefile` file and overall documentation updated
* Updated the `.env.local`, `Dockerfile`, and `README` files to document the persistence workflow, config keys, and local DB commands
Reviewed-on: rock-n-code/loud-amsterdam#13
Co-authored-by: Javier Cicchelli <javier@rock-n-code.com>
Co-committed-by: Javier Cicchelli <javier@rock-n-code.com>
This PR contains the work done to get the Website service building and running correctly in its Linux container. The service depends on the local _Localization_ package, whose String-Catalog localization was Darwin-only and broke the Docker build. Thus the localization internals have been reworked to be platform-agnostic and fixes the container build context so the local package is actually available during the build.
* Localization package
* Replaced the Darwin-only `String.LocalizationValue` / `String(localized:)` path with a StringCatalog type that reads raw JSON from a given `.xcstrings` file, so lookups resolve identically on macOS and Linux.
* The `LanguageList` now derives available languages from the catalog instead of `Bundle.localizations`
* The `Negotiate` does explicit _Accept-Language_ matching (exact tag, then primary subtag) instead of the Linux-broken `Bundle.preferredLocalizations`.
* Switched the catalog resource rule from `.process` to `.copy` (in both Localization and Website manifests) so the raw `.xcstrings` ships verbatim on every platform.
* Introduced a `CatalogResolving` protocol as a seam between the localizers and the storage backend, enabling test injection and a future native-Apple backend without changing callers.
* Website Docker build
* Build context moved to the repository root so the relative-path `Localization` package is inside the context; `Dockerfile`, `docker-compose.override.yml`, and the _img-release_ make target updated to the new context/paths.
* Added a root `.dockerignore` to keep the context lean.
Reviewed-on: rock-n-code/loud-amsterdam#12
Co-authored-by: Javier Cicchelli <javier@rock-n-code.com>
Co-committed-by: Javier Cicchelli <javier@rock-n-code.com>
This PR contains the work done to refactor the _Localization_ package and tidy the project a little bit.
To provider further details about the work:
* The `Negotiate` function was moved to the _Localization_ package.
* The `WebsiteRequestContext` context no longer resolves a default language at creation; it starts empty and relies on the `LocalizationMiddleware` middleware to fill it in.
* Fixed the local dependency path to Packages/Localization for the **Website** package as it only resolved inside the Xcode workspace before, breaking swift build, the Makefile, and the Docker build).
* Updated the `README` file to document language negotiation, the GET /health route, and the full middleware chain; doc comments across the moved/renamed types were brought back in sync with the code.
Reviewed-on: rock-n-code/loud-amsterdam#11
Co-authored-by: Javier Cicchelli <javier@rock-n-code.com>
Co-committed-by: Javier Cicchelli <javier@rock-n-code.com>
This PR contains the work done to introduce server-side localization support to the **Website** service so the landing and error pages are served in the visitor's negotiated language, backed by a new reusable Localization package.
To provide further details about the work:
* Created the _Localization_ package — a bundle-bound `Localize` method and a `LanguageList` type.
* Language negotiation — `NegotiateLanguage` method picks the best supported language from the request's _Accept-Language_ header (falling back to the default); the `LocalizationMiddleware` middleware resolves it per request and stores it on a new `LocalizedRequestContext` / `WebsiteRequestContext` context.
* Localized responses — `LocalizedHTMLCollectionResponse` pre-renders each page once per language and caches the bytes (with `Content-Language` + `Vary: Accept-Language`), reused by the `RootController` controller and `NotFoundMiddleware` middleware.
* The `CachedHTMLResponse` response gained custom-header support.
* Localized pages — the `IndexPage` and `ErrorPage` pages now resolve their strings via `Localize` method;
* Added `Localizable.xcstrings` catalogs.
* Wired the `LocalizationMiddleware` middlewaer into the router.
Reviewed-on: rock-n-code/loud-amsterdam#10
Co-authored-by: Javier Cicchelli <javier@rock-n-code.com>
Co-committed-by: Javier Cicchelli <javier@rock-n-code.com>
This PR contains the work done to provide optimizations to the current service, such as a health-check endpoint, pre-renders static HTML pages, and hardens the error page's CSP.
To provide further details about the work:
* Added the `HealthController` controller serving GET `/health` with a static JSON payload.
* Added the `CachedHTMLResponse` response, which renders a static HTMLDocument to bytes once and reuses them per request (no Content-Length, so responses stay compressible).
* Integrated the response into the `RootController` and the `NotFoundMiddleware` middleware to avoid re-rendering on hot paths.
* Added a `RouterMethods.addRoutes(_:)` extension and switched the router in App+build to use it.
* Moved the inline style from the `ErrorPage` page into a dedicated style file so the CSP needs no inline-style escape hatch.
* Fixed the `IndexPage` page path inconsistencies.
* Written the `README` file.
Reviewed-on: rock-n-code/loud-amsterdam#9
Co-authored-by: Javier Cicchelli <javier@rock-n-code.com>
Co-committed-by: Javier Cicchelli <javier@rock-n-code.com>
This PR contains the work done to add a `SecurityHeadersMiddleware` middleware that stamps hardened security-related HTTP headers onto every response.
To provide further details about the work:
* Implemented the `SecurityHeadersMiddleware` middleware, which precomputes headers once from a `Configuration` object and applies them to every response:
* _Content-Security-Policy_,
* _X-Content-Type-Options_,
* _X-Frame-Options_,
* _Referrer-Policy_,
* _Permissions-Policy_,
* _Strict-Transport-Security_ (optional).
* Integrated this middleware into the router (near the top of the chain), reading each value from configuration with hardened defaults.
* The _Strict-Transport-Security_ has no default value — omitted unless explicitly set, so it stays off in plain-HTTP during development and on only behind TLS.
* Added security-header constants keys and values.
Reviewed-on: rock-n-code/loud-amsterdam#8
Co-authored-by: Javier Cicchelli <javier@rock-n-code.com>
Co-committed-by: Javier Cicchelli <javier@rock-n-code.com>
This PR contains the work done to add response compression to the Website service by registering the `ResponseCompressionMiddleware` middleware so responses are compressed when the client advertises support and the body exceeds a minimum size.
To provide further details about the work done:
* Added the **HummingbirdCompression** package dependency.
* Integrated the `ResponseCompressionMiddleware` middleware into the router, ahead of the not-found and static file middleware.
* Made the `minimum-response-size-to-compress` threshold configurable, with a default.
Reviewed-on: rock-n-code/loud-amsterdam#7
Co-authored-by: Javier Cicchelli <javier@rock-n-code.com>
Co-committed-by: Javier Cicchelli <javier@rock-n-code.com>
This PR contains the work done to add cache-control headers to static file responses by configuring the `FileMiddleware` middleware to tag served static files with *Cache-Control* directives, tuned per media type.
To provide further details about the work done:
* Added a cache control that sets per-type policies: text assets (CSS/JS) get public, max-age, must-revalidate; images get public, max-age; everything else gets a default public, max-age.
* Files stay validated via `ETag/Last-Modified` header.
* Made the max-age values configurable, with defaults, via new cache constants.
Reviewed-on: rock-n-code/loud-amsterdam#6
Co-authored-by: Javier Cicchelli <javier@rock-n-code.com>
Co-committed-by: Javier Cicchelli <javier@rock-n-code.com>
This PR contains the work done to replace the use of static _HTML_ files with type-safe HTML rendered server-side via **Elementary** through **Hummingbird**.
To provide further details about the work done:
* Added the **Elementary** dependencies.
* Added the `IndexPage` and `ErrorPage` pages, ported from the old HTML boilerplate; removed the static files.
* Added the `RootController` controller serving GET / using the `IndexPage` page, wired into the router.
* Reworked the `NotFoundMiddleware` middleare to render `ErrorPage` page directly; non-notFound errors still propagate.
* the `FileMiddleware` middleware no longer searches for any static `index.html` file.
* Simplified the `StaticFile` enumeration, dropped unused constants and now-unnecessary throws.
Reviewed-on: rock-n-code/loud-amsterdam#5
Co-authored-by: Javier Cicchelli <javier@rock-n-code.com>
Co-committed-by: Javier Cicchelli <javier@rock-n-code.com>
This PR contains the work done to add a `NotFoundMiddleware` middleware to the Website service so requests matching neither a route nor a static file return a custom 404.html page with a 404 Not Found status. Also enables index.html fallback so the landing page is served at the site root.
To provider further details about the work done:
* NotFoundMiddleware — intercepts the `.notFound` error from the `FileMiddleware` middleware and serves the preloaded error page with the correct content type; all other errors propagate. Falls back to a minimal body if the file is missing.
* Router — wires the `NotFoundMiddleware` middleware ahead of the `FileMiddleware` and enables searchForIndexHtml.
* Package — adds the **Hummingbird** product to the Library target.
* Tooling — sets a custom working directory in the Xcode scheme; removes the unused `pkg-deps` target from the `Makefile` file.
Reviewed-on: rock-n-code/loud-amsterdam#4
Co-authored-by: Javier Cicchelli <javier@rock-n-code.com>
Co-committed-by: Javier Cicchelli <javier@rock-n-code.com>
This PR contains the work done to support the static files serving for the **Website** service, and also included the essential boilerplate assets from the **HTML5 boilerplate** project.
To provide further details about the work done:
* Serving: Added the `FileMiddleware` middlewqare to the router; `path`, `server name`, and `log level` now read from config with defaults.
* Library: Added the `StaticFile` enumeration with a `contentType` property, plus typed config-key/value constants and the `Configuration` dependency on WebsiteCore.
* Assets: Added the **HTML5 boilerplate** (HTML, CSS, JS, icons, manifest, robots) to the *Resources/Static* folder.
* Docker: Stage the Resources directory as read-only.
* Tests: Added a test plan, and a shared Xcode scheme.
Reviewed-on: rock-n-code/loud-amsterdam#3
Co-authored-by: Javier Cicchelli <javier@rock-n-code.com>
Co-committed-by: Javier Cicchelli <javier@rock-n-code.com>