- Astro 75%
- TypeScript 18.4%
- CSS 4.8%
- Dockerfile 0.7%
- JavaScript 0.7%
- Andere 0.4%
- Groene tekstkleur naar #557519 (WCAG AA, 5.3:1 op wit, was 3.77) - Poppins laadt alleen de latin-subset, kleinere fontdownload - Hero krijgt srcset/sizes zodat mobiel een kleiner beeld laadt - /img: schijf-cache voor webp zodat conversies bewaard blijven over herstarts - Lange streepjes uit alle teksten gehaald Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|---|---|---|
| public | ||
| src | ||
| .dockerignore | ||
| .env.example | ||
| .gitignore | ||
| astro.config.mjs | ||
| docker-compose.yml | ||
| Dockerfile | ||
| package-lock.json | ||
| package.json | ||
| README.md | ||
| tsconfig.json | ||
Riesewijk Tweewielers — website
Moderne herbouw van riesewijk.nl met Astro + DecapCMS (self-hosted), gekoppeld aan de bestaande RiesewijkAPI voor het fietsassortiment. Deploybaar op Coolify via Docker, met login/contentbeheer via Forgejo/Gitea (PKCE OAuth).
Stack
| Onderdeel | Keuze |
|---|---|
| Framework | Astro 5 (SSR via @astrojs/node, standalone) |
| Styling | Eigen design-tokens (CSS), font Poppins (self-host) |
| CMS | DecapCMS — Gitea/Forgejo-backend met PKCE |
| Assortiment | RiesewijkAPI /api/fietsen (server-side, gecachet) |
| Formulieren | Brevo (transactionele e-mail + nieuwsbrief) |
| Deploy | Docker → Coolify (poort 4321) |
Het hele site draait server-side (on-demand): één Node-server serveert alle pagina's én de statische assets. Assortiment en tweedehands halen live data uit de API; alle teksten, nieuws, FAQ, merken en pagina's komen uit Git via DecapCMS.
Lokaal ontwikkelen
npm install
npm run dev # http://localhost:4321
Productiebuild lokaal draaien:
npm run build
npm run start # node ./dist/server/entry.mjs
Maak een .env op basis van .env.example voor live API/mail. Zonder
ASSORTIMENT_API_URL tonen de assortimentspagina's een nette "tijdelijk niet beschikbaar"-melding.
Omgevingsvariabelen
| Variabele | Verplicht | Omschrijving |
|---|---|---|
SITE_URL |
– | Publieke URL (canonical/OG/sitemap/robots). Test: https://testomgeving.riesewijk.nl, productie: https://riesewijk.nl. |
ASSORTIMENT_API_URL |
✓ | Basis-URL van de RiesewijkAPI (bv. https://api.riesewijk.nl). |
ASSORTIMENT_CACHE_TTL |
– | Cache in seconden voor API-data (default 300). |
BREVO_API_KEY |
✓¹ | Brevo API-key (SMTP & API → API Keys). |
BREVO_SENDER_EMAIL |
✓¹ | Geverifieerde afzender in Brevo. |
BREVO_SENDER_NAME |
– | Weergavenaam afzender. |
BREVO_CONTACT_TO |
✓¹ | Ontvanger contactformulier (bv. info@riesewijk.nl). |
BREVO_NEWSLETTER_LIST_ID |
– | Lijst-ID voor nieuwsbriefinschrijvingen. |
HOST / PORT |
– | Default 0.0.0.0 / 4321. |
¹ Nodig om contact- en nieuwsbriefformulieren werkend te krijgen.
De Forgejo OAuth ClientID staat niet in env maar in
public/admin/config.yml(app_id). PKCE heeft geen secret nodig.
DecapCMS + Forgejo/Gitea (PKCE)
Het CMS leeft op /admin en commit content rechtstreeks naar deze repo
(Riesewijk/RiesewijkWebsite op code.securityasaservice.org).
1. OAuth-applicatie aanmaken in Forgejo Ga naar Instellingen → Toepassingen → OAuth2-toepassingen (of op org-niveau) en maak een app:
- Redirect URI:
https://testomgeving.riesewijk.nl/admin/(voeg voor productie óókhttps://riesewijk.nl/admin/toe — meerdere redirect-URI's mogen) - Confidential Client: UIT (belangrijk — PKCE, geen secret)
- Kopieer de gegenereerde Client ID.
2. ClientID invullen
Zet de Client ID in public/admin/config.yml bij app_id
(vervang REPLACE_WITH_FORGEJO_OAUTH_CLIENT_ID).
3. CORS aanzetten in Forgejo (app.ini), zodat de browser tokens mag uitwisselen:
[cors]
ENABLED = true
ALLOW_DOMAIN = testomgeving.riesewijk.nl, riesewijk.nl
METHODS = GET,HEAD,POST,PUT,PATCH,DELETE,OPTIONS
Vereist Forgejo ≥ 1.21.4 / Gitea ≥ 1.20. Inloggen op /admin gaat daarna via je Forgejo-account.
Lokaal CMS testen zonder Forgejo: zet local_backend: true in config.yml en draai in een
tweede terminal npx decap-server.
Wat is bewerkbaar?
| Collectie | Locatie |
|---|---|
| Nieuws & blog | src/content/nieuws/ |
| FAQ | src/content/faq/ |
| Merken | src/content/merken/ |
| Vacatures | src/content/vacatures/ |
| Pagina's | src/content/pages/ |
| Homepage | src/data/home.json |
| Site-instellingen | src/data/settings.json |
De mediabibliotheek toont de hele beeldbank in public/images/; geüploade afbeeldingen komen daar ook terecht.
Brevo (formulieren)
- Contact →
POST /api/contactstuurt een transactionele mail naarBREVO_CONTACT_TO, met de inzender als reply-to. Honeypot + validatie ingebouwd. - Nieuwsbrief →
POST /api/newslettervoegt het adres toe aanBREVO_NEWSLETTER_LIST_ID.
Zet BREVO_API_KEY en een geverifieerde BREVO_SENDER_EMAIL in Brevo. Beide formulieren
werken ook zonder JavaScript (native POST met server-side feedback).
Assortiment
src/lib/assortiment.ts bevraagt server-side ${ASSORTIMENT_API_URL}/api/fietsen, cachet het
resultaat (TTL) en splitst op state:
/assortiment— nieuwe fietsen, met filters (merk, kleur, maat, prijs, sorteren)./tweedehands— tweedehands fietsen./fiets/[id]— detailpagina per fiets.
Fietsfoto's komen uit de API (via de /img-endpoint, zie hieronder). Valt de API weg, dan tonen
de pagina's een vriendelijke fallback in plaats van een fout.
Afbeeldingen (WebP + cache)
Alle inhoudsafbeeldingen lopen via de endpoint /img (component src/components/Img.astro),
die ze on-the-fly naar WebP omzet, schaalt en met lange cache-headers serveert
(Cache-Control: public, max-age=…, immutable). Werkt voor eigen afbeeldingen én voor
assortimentsfoto's uit de API. Instelbaar in DecapCMS onder Site-instellingen → Afbeeldingen:
| Instelling | Effect |
|---|---|
| Optimalisatie aan | Pijplijn aan/uit (uit = originele bestanden direct). |
| WebP gebruiken | Converteren naar WebP. |
| Kwaliteit (1–100) | WebP-kwaliteit (default 78). |
| Max. breedte (px) | Bovengrens voor schaalbreedte. |
| Cache (dagen) | Hoe lang browsers/CDN de afbeelding cachen. |
Mislukt de conversie of staat sharp niet aan, dan wordt het origineel geserveerd — nooit
een gebroken afbeelding. Conversies worden in het geheugen gecachet voor de serverlevensduur.
WebP-conversie gebruikt sharp; daarom draait de Docker-image op node:22-slim (libvips).
Integraties (CMS → Site-instellingen → Integraties)
Allemaal aan/uit te zetten en te configureren zonder code. Externe, cookie-zettende onderdelen laden pas na toestemming via de consent-banner.
- WhatsApp — zwevende chatknop. Vul een internationaal nummer in (bv.
31570677750), standaardbericht en knoptekst. Uit of leeg nummer = geen knop. - Cookie-consent — AVG-banner (titel/tekst/privacy-link); opties "statistieken" en "externe content" apart toonbaar. Bezoekers wijzigen later via Cookie-instellingen in de footer.
- Statistieken — privacyvriendelijk: Plausible/Umami (cookieloos → geen toestemming nodig) of Matomo/GA4 (cookies → achter consent). Kies aanbieder + script-URL/ID.
- SEO-verificatie — Google Search Console- en Bing-code (meta-tag in
<head>). - Reviews — cijfer/sterren-kaart (cijfer, aantal, link) en/of widget-embed van Google/KiyOh/The Feedback Company/Trustpilot (embed achter consent).
- Instagram — feed via LightWidget/Elfsight/Behold (Widget-ID) of eigen embed (achter consent).
Deployen op Coolify
- Nieuwe Application → bron = deze Git-repo → build pack Dockerfile.
- Port
4321(staat in de Dockerfile alsEXPOSE/PORT). - Zet de environment variables uit de tabel hierboven (minimaal
ASSORTIMENT_API_URL,BREVO_*, enSITE_URL=https://testomgeving.riesewijk.nl(productie:https://riesewijk.nl)). - Koppel het domein (test:
testomgeving.riesewijk.nl) en laat Coolify TLS regelen. - Deploy. De healthcheck in de Dockerfile pingt
/.
Let op:
SITE_URLwordt op build-time ingebakken (canonical/OG/sitemap/robots). De Dockerfile heefttestomgeving.riesewijk.nlals default; voor productie geef je de build-arg mee:--build-arg SITE_URL=https://riesewijk.nl(in Coolify als build-argument). De overige variabelen (ASSORTIMENT_API_URL,BREVO_*) zijn gewone runtime-env.
Lokaal de container testen:
docker compose up --build # http://localhost:4321
Logo & afbeeldingen
Het originele logo (public/brand/logo_riesewijk_light.svg) en de sfeer-/contentfoto's
(public/images/) zijn overgenomen van de huidige site en blijven ongewijzigd. Alleen
assortimentsafbeeldingen komen dynamisch uit de API.
Projectstructuur (kort)
public/admin/ DecapCMS (index.html + config.yml)
public/brand/ Logo + favicons (ongewijzigd)
public/images/ Sfeer-/contentfoto's + CMS-uploads
src/components/ Header, Footer, BikeCard, FilterBar, ContactForm, ...
src/content/ Nieuws, FAQ, merken, vacatures, pagina's (Markdown)
src/data/ home.json, settings.json (singletons)
src/lib/ assortiment.ts (API), brevo.ts (mail)
src/pages/ Routes (+ api/contact, api/newsletter)
Aandachtspunten
- Juridische teksten (
voorwaarden,privacy) zijn algemene basisversies — laat ze juridisch controleren vóór livegang. - Social-media-URL's in
settings.jsonzijn aannames; controleer/pas ze aan.