From 53107f1b486ec995c1b4a0a005ed1eea91338f20 Mon Sep 17 00:00:00 2001 From: kisfenyo Date: Fri, 30 Oct 2026 10:54:29 +0100 Subject: [PATCH] D35/R-1228: publish-site-cloudflare.sh (dry run, refusals); the contact form posts to api.felhom.eu --- scripts/CHANGELOG.md | 7 +++ scripts/publish-site-cloudflare.sh | 70 ++++++++++++++++++++++++++++++ website/CHANGELOG.md | 10 ++++- website/en/contact.html | 2 +- website/kapcsolat.html | 2 +- 5 files changed, 88 insertions(+), 3 deletions(-) create mode 100755 scripts/publish-site-cloudflare.sh diff --git a/scripts/CHANGELOG.md b/scripts/CHANGELOG.md index 229e4c6f..9eaea27f 100644 --- a/scripts/CHANGELOG.md +++ b/scripts/CHANGELOG.md @@ -1,3 +1,10 @@ +## 2026-10-30 — `publish-site-cloudflare.sh`: the website and the installer to Cloudflare Pages (D35 / R-1228) + +- Stages `website/` from `origin/main` and `scripts/` from the installer tag named in `manifests/webpage.yaml`; REFUSES + when the staged installer is not the tag's byte for byte, when a must-have page is missing, when the manifest names + more than one tag, or when a file is over Pages' 25 MiB; `--dry-run` uploads nothing. The upload is wrangler 3.80.0 in + `node:20.17.0-alpine`; the token (`sec run CF_PAGES_TOKEN`) reaches it as an environment variable name only. + ## 2026-10-28 (midday) — the watch asks Docker Hub gently: spaced, retried on 429, tag lists cached 24 h (R-1334) - **`security_watch.py`:** Docker Hub requests (`hub.docker.com`, `registry-1.docker.io`, `auth.docker.io`) go through diff --git a/scripts/publish-site-cloudflare.sh b/scripts/publish-site-cloudflare.sh new file mode 100755 index 00000000..c9664e70 --- /dev/null +++ b/scripts/publish-site-cloudflare.sh @@ -0,0 +1,70 @@ +#!/usr/bin/env bash +# publish-site-cloudflare.sh [--dry-run] [--project NAME] — D35 / R-1228 (2026-10-30): the public website and the +# installer, uploaded to Cloudflare Pages. It replaces „push to main and DooPlex's git-sync serves it" for the copy on +# Cloudflare; DooPlex keeps serving its own copy until the DNS switch, and for 7 days after it as the way back. +# +# WHAT IT UPLOADS — the same files DooPlex serves today, from the same refs: +# / website/ from origin/main (DooPlex: git-sync --ref=main, nginx root website/) +# /scripts/ scripts/ from the installer TAG (DooPlex: git-sync --ref=installer-v, nginx location /scripts/) +# The tag is read from manifests/webpage.yaml, so the two copies can never name different installers. Clean URLs +# (/kapcsolat → kapcsolat.html), 404.html and compression are Pages' own behaviour — nothing to configure. +# +# Run: sec run CF_PAGES_TOKEN -- scripts/publish-site-cloudflare.sh [--dry-run] +# --dry-run stage and check everything, print what WOULD be uploaded, upload nothing (no token needed). +# The token reaches wrangler as an environment variable NAME (docker -e NAME) — never on a command line. +set -euo pipefail + +PROJECT=felhom-site +DRY=0 +while [ $# -gt 0 ]; do + case "$1" in + --dry-run) DRY=1 ;; + --project) PROJECT="$2"; shift ;; + *) echo "usage: $0 [--dry-run] [--project NAME]" >&2; exit 2 ;; + esac + shift +done +ACCOUNT_ID="${CF_ACCOUNT_ID:-9e432dea3b6befc7c87a41316049f162}" # an account identifier, not a secret +WRANGLER="wrangler@3.80.0" +NODE_IMAGE="node:20.17.0-alpine" + +REPO=$(git -C "$(dirname "$0")" rev-parse --show-toplevel) +git -C "$REPO" fetch -q origin main --tags +TAG=$(grep -oE -- '--ref=installer-v[0-9]+\.[0-9]+\.[0-9]+' "$REPO/manifests/webpage.yaml" | sort -u | sed 's/--ref=//') +if [ "$(printf '%s\n' "$TAG" | wc -l)" != 1 ] || [ -z "$TAG" ]; then + echo "REFUSED: manifests/webpage.yaml must name exactly ONE installer tag (found: ${TAG:-none})" >&2; exit 1 +fi +git -C "$REPO" rev-parse -q --verify "refs/tags/$TAG" >/dev/null || { echo "REFUSED: tag $TAG does not exist" >&2; exit 1; } +SITE_REV=$(git -C "$REPO" rev-parse --short origin/main) + +STAGE=$(mktemp -d) +trap 'rm -rf "$STAGE"' EXIT +mkdir -p "$STAGE/site" +git -C "$REPO" archive origin/main website | tar -x -C "$STAGE" +cp -a "$STAGE/website/." "$STAGE/site/" +git -C "$REPO" archive "$TAG" scripts | tar -x -C "$STAGE/site" + +WANT=$(git -C "$REPO" show "$TAG:scripts/felhom-host-install.sh" | sha256sum | cut -d' ' -f1) +GOT=$(sha256sum "$STAGE/site/scripts/felhom-host-install.sh" | cut -d' ' -f1) +[ "$WANT" = "$GOT" ] || { echo "REFUSED: the staged installer is not the tag's ($GOT != $WANT)" >&2; exit 1; } +for must in index.html 404.html kapcsolat.html en/index.html sitemap.xml robots.txt; do + [ -f "$STAGE/site/$must" ] || { echo "REFUSED: $must is missing from the staged site" >&2; exit 1; } +done +FILES=$(find "$STAGE/site" -type f | wc -l) +BIG=$(find "$STAGE/site" -type f -size +25M | wc -l) +[ "$BIG" = 0 ] || { echo "REFUSED: $BIG file(s) over Pages' 25 MiB limit" >&2; exit 1; } + +echo "project : $PROJECT (account $ACCOUNT_ID)" +echo "website : origin/main $SITE_REV" +echo "installer : $TAG — scripts/felhom-host-install.sh sha256 $GOT" +echo "files : $FILES ($(du -sh "$STAGE/site" | cut -f1))" +if [ "$DRY" = 1 ]; then + echo "DRY RUN — nothing uploaded." + exit 0 +fi +: "${CF_PAGES_TOKEN:?run under: sec run CF_PAGES_TOKEN -- $0}" +export CLOUDFLARE_API_TOKEN="$CF_PAGES_TOKEN" CLOUDFLARE_ACCOUNT_ID="$ACCOUNT_ID" +docker run --rm -e CLOUDFLARE_API_TOKEN -e CLOUDFLARE_ACCOUNT_ID -v "$STAGE/site":/site "$NODE_IMAGE" \ + npx -y "$WRANGLER" pages deploy /site --project-name "$PROJECT" --branch main \ + --commit-hash "$(git -C "$REPO" rev-parse origin/main)" --commit-message "website $SITE_REV + $TAG" --commit-dirty=true +echo "UPLOADED — check it from outside: the pages answer, and the installer's sha256 is $GOT" diff --git a/website/CHANGELOG.md b/website/CHANGELOG.md index e35d7822..cc43f37e 100644 --- a/website/CHANGELOG.md +++ b/website/CHANGELOG.md @@ -1,4 +1,12 @@ -## 2026-10-23 — the privacy wording says what is true; the minimum machine is written down (D3 / R-985, D5 / R-991; operator rulings 2026-10-22 15:47) and D36's two sentences (R-1225, R-1226) — published on the operator's yes (day brief) +## 2026-10-30 — the site moves to Cloudflare Pages (D35 / R-1228); the contact form posts to api.felhom.eu + +- **D35 (operator yes 2026-10-23; the switch approved 2026-10-30).** The site is uploaded to the Cloudflare Pages project + `felhom-site` by `scripts/publish-site-cloudflare.sh` (website from `main`, `/scripts/` from the installer tag). After + the DNS switch a push to `main` alone no longer changes the public site — run the script too. +- `kapcsolat` / `en/contact`: `formEndpoint` is `https://api.felhom.eu/api/contact` (the mailer stays on DooPlex, its + CORS already allows felhom.eu and www). + +## 2026-10-23 — the privacy wording says what is true; the minimum machine is written down (D3 / R-985, D5 / R-991; operator rulings 2026-10-22 15:47) and D36's two sentences (R-1225, R-1226) — published on the operator's yes (day brief) - **D36 (operator yes 2026-10-23, R-1225, R-1226).** `letoltes` / `en/download`: „A 8 GB az alapokat futtatja; a fotóalkalmazáshoz 16 GB kell.” / „8 GB runs the basics; the photo app needs 16 GB.” `gyik` / `en/faq` („Ti hozzáfértek az adataimhoz?”, the visible answer and its JSON-LD twin): „…csak titkosítva hagyják el az otthonodat (a távoli mentés)” became „Amit az otthonodon kívülről nyitsz meg, az a Cloudflare hálózatán halad át, és ott olvasható; a mentések csak titkosítva hagyják el az otthonodat.” / „What you open from outside your home passes through Cloudflare's network and is readable there; the backups leave your home only encrypted.” (`13-threat-model.md` §6, R-904). diff --git a/website/en/contact.html b/website/en/contact.html index 5bbba812..7ebfa905 100644 --- a/website/en/contact.html +++ b/website/en/contact.html @@ -225,7 +225,7 @@ // 3. Cloudflare Workers: Deploy a worker that forwards to email // // For now, falls back to mailto: if no endpoint is configured. - formEndpoint: '/api/contact', + formEndpoint: 'https://api.felhom.eu/api/contact', // D35/R-1228: the site is on Cloudflare; the mailer stays on DooPlex maxTotalFileSize: 20 * 1024 * 1024, // 20 MB maxSingleFileSize: 10 * 1024 * 1024, // 10 MB per file diff --git a/website/kapcsolat.html b/website/kapcsolat.html index a76e893c..1be759c5 100644 --- a/website/kapcsolat.html +++ b/website/kapcsolat.html @@ -225,7 +225,7 @@ // 3. Cloudflare Workers: Deploy a worker that forwards to email // // For now, falls back to mailto: if no endpoint is configured. - formEndpoint: '/api/contact', + formEndpoint: 'https://api.felhom.eu/api/contact', // D35/R-1228: the site is on Cloudflare; the mailer stays on DooPlex maxTotalFileSize: 20 * 1024 * 1024, // 20 MB maxSingleFileSize: 10 * 1024 * 1024, // 10 MB per file