From cd45c5d98ffff979828ffcdf0fba90aac48f5641 Mon Sep 17 00:00:00 2001 From: Felix Klement Date: Mon, 5 Oct 2026 21:04:51 +0200 Subject: [PATCH] wasdf --- .env.example | 5 + Dockerfile | 3 +- README.md | 26 ++-- astro.config.mjs | 4 +- docker/nginx.conf | 16 ++ package-lock.json | 32 ---- server/contact.mjs | 203 ++++++++++++++++++++----- server/mail/confirm.mjs | 107 +++++++++++++ server/mail/logo.png | Bin 0 -> 17090 bytes src/components/Toast.astro | 124 +++++++++++++++ src/components/home/Contact.astro | 203 +------------------------ src/components/verifier/SoonHero.astro | 26 +++- src/i18n/ui.ts | 48 ++++++ src/pages/_NotifyPage.astro | 45 ++++++ src/pages/de/notify/confirm.astro | 4 + src/pages/de/notify/confirmed.astro | 4 + src/pages/de/notify/error.astro | 4 + src/pages/de/privacy.astro | 2 +- src/pages/notify/confirm.astro | 4 + src/pages/notify/confirmed.astro | 4 + src/pages/notify/error.astro | 4 + src/pages/privacy.astro | 2 +- src/scripts/altcha.ts | 34 +++++ src/scripts/send-form.ts | 66 ++++++++ 24 files changed, 689 insertions(+), 281 deletions(-) create mode 100644 server/mail/confirm.mjs create mode 100644 server/mail/logo.png create mode 100644 src/components/Toast.astro create mode 100644 src/pages/_NotifyPage.astro create mode 100644 src/pages/de/notify/confirm.astro create mode 100644 src/pages/de/notify/confirmed.astro create mode 100644 src/pages/de/notify/error.astro create mode 100644 src/pages/notify/confirm.astro create mode 100644 src/pages/notify/confirmed.astro create mode 100644 src/pages/notify/error.astro create mode 100644 src/scripts/altcha.ts create mode 100644 src/scripts/send-form.ts diff --git a/.env.example b/.env.example index 51b4233..605dd8c 100644 --- a/.env.example +++ b/.env.example @@ -9,3 +9,8 @@ CONTACT_TO=office@ciphercell.de # start makes its own. Generate with: openssl rand -hex 32 ALTCHA_HMAC_SECRET= ALTCHA_HMAC_KEY_SECRET= +# Signs the alpha sign-up confirmation links (openssl rand -hex 32). Set it, or links sent +# before a restart stop working. +NOTIFY_SECRET= +# Where confirmation links point. Default: https://www.ciphercell.de. Locally, uncomment: +# SITE_URL=http://localhost:4321 diff --git a/Dockerfile b/Dockerfile index 021b0d8..56a884a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,12 +10,13 @@ RUN npm ci --no-audit --no-fund COPY . . RUN npm run build -# The contact handler's production dependencies (nodemailer only). +# The form handler and its production dependencies (nodemailer, altcha-lib). FROM node:22-alpine AS contact WORKDIR /srv/contact COPY server/package.json server/package-lock.json ./ RUN npm ci --omit=dev --no-audit --no-fund COPY server/contact.mjs ./ +COPY server/mail ./mail # Serve dist/ with nginx; POST /contact goes to the handler on 127.0.0.1:3000. FROM nginx:1.27-alpine AS runtime diff --git a/README.md b/README.md index 2cbe36c..cc1b031 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ Environment; see *Contact form* below. | `src/components/verifier/*` | The CipherVerifier product page, in page order: Hero (line burst), Intro (image band), HowItWorks (five group cards, one schematic each; on wide screens the heading is sticky and the cards stack under it as you scroll), Cards, Facts, Outro. | | `src/components/verifier/SoonHero.astro`, `ComingSoon.astro` | The coming-soon version of `/cipherverifier`, shown while `verifierComingSoon` in `src/data/flags.ts` is true: hero on a build-time SVG relief of ridge lines in the logo blues, under the floating header, "Coming soon." with the alpha sign-up beside it, a status strip, and the product name set across the full width and cut by the hero's lower edge, release path, spec sheet and the first schematic. Copy in `ui.verifier.soon`. | | `src/components/verifier/diagrams.ts` | The five schematics as data: a hand-placed wide layout and an auto-generated tall spine per group. `Schematic.astro` renders either. Labels come from `ui.verifier.how.groups`; the tuple shape is in `types.ts`. Every element carries the `group` of the node it belongs to, which drives the hover focus and the draw-in order. | -| `server/contact.mjs` | The contact form handler: validates, rate-limits and sends by SMTP. Its own `package.json`, so the image only carries nodemailer. | +| `server/contact.mjs`, `server/mail/` | The handler for both forms (contact, alpha sign-up): validates, checks the proof of work, rate-limits and sends by SMTP. `server/mail/confirm.mjs` lays out the confirmation email (tables, inline styles, logo as an inline attachment). Its own `package.json`, so the image only carries nodemailer and altcha-lib. | | `scripts/*.mjs` | Review tooling: `shot.mjs` (full page), `scroll-shots.mjs` (viewport by viewport), `tabs-test.mjs` (keyboard test for the sectors tabs), `contact-sheet.mjs`. Need `npx astro preview --port 4322`. | | `src/pages/_*.astro` | Shared page bodies. `index.astro` and `de/index.astro` render `_Home.astro` with a `lang` prop, and so on. | | `src/assets/images/` | Source images. Astro emits AVIF with a WebP fallback and explicit dimensions. | @@ -59,7 +59,7 @@ Environment; see *Contact form* below. ## Things the host has to provide - **Contact form.** The form posts `first_name`, `last_name`, `email`, `message`, `lang` and a - honeypot field `website` to `POST /contact`. That one path is handled by `server/contact.mjs` + honeypot field `website` to `POST /contact`. That path is handled by `server/contact.mjs` (Node, nodemailer), which sends the message by SMTP to `CONTACT_TO` with the sender as Reply-To. The form sends in place and shows the result in a toast; a post that asks for HTML is redirected to `/contact/sent` or `/contact/error` (under `/de` for German). Both pages are @@ -74,7 +74,7 @@ Environment; see *Contact form* below. and no third party is involved, so there is nothing to consent to. The form therefore needs JavaScript; without it a `