Replaces em dashes with en dashes across all hardcoded copy, emails, and
seed content, then reworks the site's visual language so it reads as
deliberately designed rather than assembled from generic components:
- New ArrowLink component replaces ad hoc "Mehr erfahren ->" links and
ghost buttons; one shared FormField (TextField/TextAreaField) replaces
7 near-duplicate field styles across every form
- Navbar loses its glassmorphism blur for a solid warm background, gets a
subtle active-link underline and a softer, warmer mega-menu
- PageHeader supports per-page blob tone/position and a compact mode
instead of an identical band on every interior page
- Homepage: the core offer is featured large instead of four equal cards,
the philosophy section gets a single calmer accent blob, blog teasers
now show their cover image like the real /aktuelles page does
- Booking calendar and widget: 44px touch targets throughout (month nav,
day cells, time slots, cancel action), a visible "today" ring, and
responsive padding so the calendar isn't cramped on small screens
- Quieter, warmer empty states on /termine and /aktuelles
No functional changes: all interaction logic (nav menus, forms, booking,
CMS data fetching) is untouched, only markup and styling.
Adds a first-party analytics system with no third-party trackers and no
persistent per-visitor identifiers: events are grouped into sessions via a
daily-rotating pseudonymous hash (IP+UA+host, HMAC-salted, never stored raw).
- New "Analytics" section in the Payload admin, gated by a canViewAnalytics
permission on Users, following the existing custom-admin-view pattern
used by the calendar feature
- Sessions, page views, unique visitors, top/entry/exit pages, events,
device/browser breakdown, date range filter with period comparison
- Raw events and daily aggregate rollups as their own collections, with
configurable retention and a cron endpoint that rolls up before purging
- Cookie consent banner (necessary/preferences/statistics) with categories
and copy editable from a new CMS global; a track() helper that is a
complete no-op without statistics consent, so nothing is ever sent
before a visitor opts in
- track() calls wired into the booking flow, contact form, and navigation
- Restrict meetingPassword field so it isn't leaked via the public REST/GraphQL API
- Fix booking slot computation to use Europe/Berlin time instead of the server's UTC clock
- Reset email verification when a customer changes their email address
- Fix ICS calendar injection via unescaped carriage returns in event descriptions
- Bind WebRTC meeting kicks to a persistent client id so they survive a rejoin
- Move the meeting signaling token out of the WebSocket query string into the first message
- Prevent duplicate signaling connections from leaking when the same participant reconnects
- Use the rightmost X-Forwarded-For entry for rate limiting (single trusted reverse proxy)
- Use constant-time comparison for the cron endpoint's bearer token
- Remove SVG from allowed Media upload types (stored-XSS risk, unused anyway)
- Add baseline security headers and a CSP to next.config.ts
- Fix CI/CD script injection via unsanitized git ref names in release.yml
deploy.sh sources .env directly as bash (`set -a; source .env`), but
setup.sh wrote raw unquoted values. The default SMTP_FROM ("ANOUMA
<no-reply@...>") contains `<`/`>`, which bash parses as redirections,
failing with "syntax error near unexpected token `newline'" on every
deploy. Both scripts' env_set now write double-quoted values with
backslashes/quotes escaped — valid for both `source` and Docker
Compose's env_file parsing (which strips matching quotes).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Gitea Actions runs `run:` steps with `sh` by default, which doesn't
support bash's `set -o pipefail` ("Illegal option -o pipefail",
exit 2). node:22-bookworm-slim (Debian) includes bash, so declaring
shell: bash for this step fixes it without changing the base image.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
docker:27-cli is Alpine with no Node.js, but actions/checkout@v4 is a
JS action that needs a Node runtime in the job container — it failed
at Checkout with exit 127. node:22-bookworm-slim already has Node
(same reason the test job's node:22-alpine works); Docker CLI and
openssh-client are added via apt instead.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The runner (gitea-anouma-runner) runs as a Docker container itself
(gitea/runner:3) with the host's docker.sock mounted in, but no
`docker` CLI binary. A job with no `container:` runs directly inside
that minimal runner image, so `docker login/build/push` failed with
"docker: command not found". Using docker:27-cli gives the job the
CLI; the runner mounts the same host socket into it automatically
(sibling containers), and openssh-client is installed for the deploy
step's ssh call.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This Next.js version generates LayoutProps/PageProps/RouteContext
into .next/types only via `next dev`, `next build`, or `next typegen`
(see node_modules/next/dist/docs .../06-cli/next.md). Typecheck ran
`tsc --noEmit` standalone before Build, so those globals didn't exist
yet, failing with "Cannot find name 'LayoutProps'".
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Setting it job-wide made `npm ci` treat NODE_ENV=production as an
implicit --omit=dev, silently skipping eslint/typescript/test
devDependencies and failing Lint with "eslint: not found".
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Production no longer builds from source or self-updates via cron/git pull:
setup.sh now only provisions the server once (Docker, /opt/anouma, a
restricted `anouma-deploy` SSH user whose key can only ever run
deploy.sh, generated secrets). All future deployments run through
.gitea/workflows/ci.yml (lint/typecheck/test/build on every push) and
release.yml (on a vX.Y.Z tag: build the image, push it to the Gitea
registry, then SSH-trigger deploy.sh on the server), which pulls,
migrates, restarts, healthchecks, backs up the database first, and
automatically rolls back the code on a failed healthcheck.
docker-compose.yml's app service now runs a registry image
(${ANOUMA_IMAGE}) instead of building locally.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Customer accounts now require email verification (hashed, single-use,
time-limited tokens) before they can request/confirm bookings, with
resend flows on login/account/booking widget and rate limiting.
- Admins get a private, rotatable iCalendar (ICS) subscription feed of
their confirmed bookings and public events, timezone-correct for
Europe/Berlin including DST, never exposing meeting passwords.
- Adds a full SEO layer: per-page canonical/OG/Twitter metadata with
CMS-editable overrides and content-derived fallbacks, a dynamic
sitemap.xml and robots.txt driven by real published content, JSON-LD
(Organization/LocalBusiness, WebSite, WebPage, BreadcrumbList, Service,
Event, BlogPosting) that never fabricates data, and a CMS-managed
redirect table for changed slugs.
- Global ANOUMA-naming audit: the brand name is never used to label
personal account/calendar areas anywhere in the app, CMS, or emails.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- setup.sh: interactive/non-interactive one-shot installer (build, DB
healthcheck, migrate, seed, start), idempotent secret generation, NPM
reverse-proxy network auto-detection and optional join, optional
AUTO_UPDATE cron install.
- update.sh: release-tag-gated updates only (never bare main), DB backup
with retention before every update, lock file against concurrent runs,
automatic code rollback on failed post-update healthcheck.
- Dockerfile: multi-stage build, non-root user, built-in HEALTHCHECK against
the new /api/health route, wholesale COPY so new source dirs (e.g.
scripts/) never silently go missing at runtime.
- docker-compose.yml: internal anouma-network (configurable), named volume
for Postgres, app depends_on postgres healthy, no unnecessary published
ports; docker-compose.override.yml.example documents joining an existing
NPM network without ever touching NPM itself.
- Fix host-detection: isHost was Boolean(user), wrongly granting host
privileges to logged-in customers; now checks user.collection === "users".
- Wire configurable STUN/TURN servers through to the WebRTC client
(lib/meeting/iceServers.ts) so a TURN server can be added later via env
vars only, no code changes.
- DEPLOYMENT.md, updated README.md and .env.example documenting the whole
flow: NPM integration, env vars, WebRTC, updates, backups, rollback.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Next.js 16 App Router site with the ANOUMA design system
- Payload CMS (PostgreSQL) for offers, events, posts and page content
- WebRTC video-call system with custom signaling server
- SMTP email reminders and booking-request notifications
- Customer accounts, calendar-based availability, and booking workflow