Implemented phase 1 for the POC prototype in the Website service.

This commit is contained in:
javier committed 2026-09-21 11:16:52 +02:00
1 parent 6d7ab4bcba
commit ea89ea892a
37 files changed
+4173 -43

No files matched your search

+12 -2
View File
@@ -1,9 +1,10 @@
# CCN Website
The **CCN** public website service — a [Hummingbird](https://github.com/hummingbird-project/hummingbird) server that renders a static landing page and serves the site's static assets.
The **CCN** public website service — a [Hummingbird](https://github.com/hummingbird-project/hummingbird) server that renders the CCN marketplace prototype and serves the site's static assets.
## Overview
The service:
- Serves the landing page at `GET /` (rendered once per supported language with [Elementary](https://github.com/elementary-swift/elementary) and cached), plus one prefixed route per non-default catalog language — `GET /nl` and so on (see [Language editions](#language-editions)).
- Serves the public catalogue, rendered per request: search results at `GET /search` (filtered through its query parameters), a listing at `GET /listing/{slug}`, and a seller's shop at `GET /shop/{slug}`; an unknown slug gets the not-found page (see [Catalogue](#catalogue)).
- Negotiates each request's language from the `lang` query parameter, the leading path segment, then its `Accept-Language` header, against the languages in the `WebsiteLibrary` String Catalog, falling back to the default (`en`) — asked for only where the answer is used, the bare landing route and the not-found page; pages are served from the per-language cache with `Content-Language` and `Vary: Accept-Language` headers.
- Builds every page on the shared `Page` scaffolding from `Infrastructure`, which assembles the document head around the page's own markup: the viewport declaration, the optional `description` summary and `rel="canonical"` link, the Open Graph / Twitter link-preview tags, the JSON-LD structured-data script, and the optional analytics tracker (see [Page metadata](#page-metadata)).
- Answers a liveness check at `GET /health` with a static JSON payload, and a readiness check at `GET /health/ready` that reports whether the database is reachable (`200` ready / `503` unavailable).
@@ -46,6 +47,7 @@ LogRequestsMiddleware
→ NotFoundMiddleware (negotiates the language, renders the not-found page on .notFound)
→ FileMiddleware (serves Resources/Static)
RootController (GET / → landing page, language negotiated; GET /<lang> → its other editions)
CatalogueController (GET /search, GET /listing/{slug}, GET /shop/{slug} → rendered per request)
HealthController (GET /health → liveness, GET /health/ready → readiness)
```
@@ -58,6 +60,14 @@ The screens follow the **CCN marketplace** design canvas:
- **Shared frame**: `SiteHeader`, `SiteFooter`, and the `SiteLayout` a page wraps its `content` in, under `Sources/Library/Internal/Components`.
- **Paths**: `String.Route` lists every screen's path; screens not built yet answer with the not-found page.
### Catalogue
- **Data**: `Persistence`'s `CatalogueRepository` reads the shops and listings; the in-memory backend is migrated and seeded with demo data at launch.
- **Landing page**: rendered once and cached, so its listing rows are a snapshot taken at launch (`HomeListings`).
- **Search and shop**: `SearchParameters` turns the query string into a `ListingFilter` and builds every link that changes it — filter pills, tabs, pagination — so the screens work as plain `GET` forms and links. Searches default to listings shipping from the EU.
- **Gated actions**: buy, offer, message, save, and follow link to the sign-in path with a `return` parameter; the sign-in screen itself is not built yet.
- **Covers**: listings have no photos yet, so `Cover` draws a placeholder from the listing's lettering and tone.
- **Scripts**: `js/shared.js` only submits the sort select on change and opens the filter rail on older desktop browsers; everything works without it.
### Page metadata
Each page conforms to `Infrastructure`'s `Page` protocol and supplies only its `title`, `content`, `stylesheets`, and `scripts`; the protocol assembles the document around them and renders the head in a fixed order: the viewport declaration, the `analytics` origin preconnect hint, the `summary`, the `canonicalURL` link, the `socialCard` tags, the `structuredData` script, the `analytics` tracker script, then the page `metadata` and the stylesheet links. The body is the content followed by the script tags.
@@ -260,7 +270,7 @@ Unlike a direct `swift run`, these targets inherit the exported `.env` values (s
The service persists data through Fluent and selects its backend at runtime with `database.driver`.
### In-memory (default)
With no configuration, the service uses an ephemeral in-memory SQLite database. It is created and **migrated on startup** every launch, so `swift run Website` and `docker compose up` work with no external database — ideal for local development and tests.
With no configuration, the service uses an ephemeral in-memory SQLite database. It is created, **migrated, and seeded with the demo catalogue** on every launch, so `swift run Website` and `docker compose up` work with no external database — ideal for local development and tests.
### PostgreSQL
Set `DATABASE_DRIVER=postgres` and the connection values (`DATABASE_HOST`, `DATABASE_NAME`, `DATABASE_USERNAME`, `DATABASE_PASSWORD`, …). Unlike the in-memory backend, PostgreSQL is **not** migrated on boot — a shared database is migrated out of band so multiple instances never race: