claude-code 0de85cbc51
Build and push image / build (push) Failing after 15m20s
Localize the archive, add reminders, named challenges and a funnel
Archive and answer pages now exist in all six languages and are prerendered
instead of rendered per request, and each answer page carries a sentence of
real context per player (club, league, nationality, former clubs, honours)
plus Article and BreadcrumbList JSON-LD. The sitemap grows from 121 to 691
URLs, every one with hreflang alternates.

Adds an optional display name to shared results, so a challenge link reads
"<name> challenges you" in the page, the OG image and the head-to-head
verdict; the name is sanitised both when written and when parsed back.

Adds an opt-in daily reminder (Web Push without payload, so no content
encryption) and a PWA install prompt shown once the day is finished, plus
first-party event ingestion at /api/events that mirrors GA4 so the funnel
survives ad blockers.

Wires the missing N8N_PLAYS_WEBHOOK_URL into the deployment along with the
new events, push and search-verification secrets, and ships the n8n
workflows, the Postgres schema with funnel/retention views, and an IndexNow
submitter.

Refs #2 #3 #5 #6 #7 #8 #9 #10

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QvQ1ErZNRUcWgCEkJ9uyrn
2026-08-26 12:41:28 +00:00
2026-05-02 20:50:36 +02:00
2026-05-02 20:50:36 +02:00
2026-05-02 20:50:36 +02:00
2026-05-02 20:50:36 +02:00
2026-05-02 20:50:36 +02:00
2026-05-05 11:17:46 +02:00
2026-05-02 20:50:36 +02:00
2026-05-02 20:50:36 +02:00
2026-05-02 20:50:36 +02:00
2026-05-02 20:50:36 +02:00
2026-05-02 20:50:36 +02:00

hidden11

Daily football lineup puzzle built with Next.js, React, TypeScript and Tailwind CSS.

Player data provenance and clue rules are documented in docs/data-sources.md.

Growth features

  • Localized home pages: / (en) plus /es, /fr, /de, /it, /pt — server-rendered intro, how-to-play and FAQ (FAQPage JSON-LD) with hreflang alternates on every home.
  • Archive, in six languages: /archive and /<locale>/archive list past puzzles; /puzzle/<n> and /<locale>/puzzle/<n> publish answers for past days only (today and future 404). Answer pages carry a sentence of real context per player — club, league, nationality, former clubs and honours — plus Article and BreadcrumbList JSON-LD, and are prerendered (generateStaticParams + hourly ISR) rather than rendered per request. /play/<n> replays a past puzzle without touching stats or streak.
  • Challenge loop: shared result links (/r/<code>) offer "Accept the challenge" → /?challenge=<code>. If the challenge matches today's puzzle, the game shows the rival's score and a head-to-head verdict on the result screen.
  • Compact share text: one line per difficulty with the final attempt grid — built in lib/share.ts.
  • PWA: app/manifest.ts + icons in public/ (regenerate with node scripts/generate-icons.mjs).
  • Named challenges: the share sheet takes an optional display name that travels inside the share code, so /r/<code> reads " challenges you" in the page, the OG image and the head-to-head verdict. The name is sanitised on the way out and on the way in.
  • Daily reminder: an opt-in Web Push subscription plus a PWA install prompt, offered once the day is finished. The push is sent without a payload (VAPID header only, no content encryption); the notification copy lives in the service worker's Cache Storage (public/sw.js).
  • First-party funnel: every game event is mirrored to POST /api/events alongside GA4, so the funnel survives ad blockers. Schema and the funnel/retention/share-channel views are in deploy/sql/hidden11-events.sql.
  • Daily teaser: GET /api/teaser returns a spoiler-free localized teaser for today's puzzle (first Easy clue). deploy/n8n-daily-teaser.json is an importable n8n workflow that posts it daily through the Postiz public API (set POSTIZ_API_KEY in n8n and fill in the integration ids inside the Code node).
  • IndexNow: node scripts/submit-indexnow.mjs [--today] submits the sitemap URLs to Bing, Yandex and Seznam. The key file lives in public/<key>.txt; the key is in pass show hidden11/indexnow-key. Google has no ping endpoint any more — its sitemap is submitted once from Search Console.

Local development

npm install
npm run dev

Open http://localhost:3000.

Optional analytics env vars:

  • NEXT_PUBLIC_GA_MEASUREMENT_ID: GA4 measurement ID, e.g. G-XXXXXXXXXX
  • NEXT_PUBLIC_CLARITY_PROJECT_ID: Microsoft Clarity project ID

Optional feedback env vars:

  • N8N_FEEDBACK_WEBHOOK_URL: n8n webhook URL used to receive feedback submissions

Optional funnel-analytics env vars:

  • N8N_EVENTS_WEBHOOK_URL: n8n webhook backing POST /api/events (workflow: deploy/n8n-events.json, schema: deploy/sql/hidden11-events.sql). When unset the endpoint accepts and discards, so the game is unaffected.

Optional daily-reminder env vars:

  • VAPID_PUBLIC_KEY: application server key served by GET /api/push (pass show hidden11/vapid-public-key)
  • N8N_PUSH_WEBHOOK_URL: n8n webhook that stores push subscriptions (workflow: deploy/n8n-push-subscriptions.json). Sending is deploy/n8n-daily-push.json; the VAPID private key never leaves n8n (pass show hidden11/vapid-private-key).

Optional search-verification env vars:

  • GOOGLE_SITE_VERIFICATION: token for the google-site-verification meta tag. hidden11.app is already verified by DNS TXT, so this is only needed for a second property.
  • BING_SITE_VERIFICATION: token for the msvalidate.01 meta tag

Optional social-proof counter env vars:

  • N8N_PLAYS_WEBHOOK_URL: n8n webhook URL backing the daily play counter shown on the result screen ("N people played today"). The webhook receives { action: "get" | "increment", date: "YYYY-MM-DD" } and must respond with { count: number }. When unset, the counter line is simply hidden — no number is ever fabricated.

Optional sponsor env vars:

  • NEXT_PUBLIC_SPONSOR_ENABLED: set to false to hide the sponsor slot
  • NEXT_PUBLIC_SPONSOR_NAME: sponsor name shown in the slot
  • NEXT_PUBLIC_SPONSOR_TAGLINE: short sponsor copy
  • NEXT_PUBLIC_SPONSOR_CTA: sponsor button text
  • NEXT_PUBLIC_SPONSOR_URL: outbound sponsor URL
  • NEXT_PUBLIC_SPONSOR_LABEL: small label above the sponsor name

Production build

npm run build
npm start

Docker

docker build -t hidden11:latest .
docker run --rm -p 3000:3000 hidden11:latest

CI/CD

Gitea Actions workflow:

  • .gitea/workflows/build-and-push.yaml
  • Builds on main
  • Pushes:
    • gitea.alexandre-vazquez.cloud/alexandrev/hidden11:<git-sha>
    • gitea.alexandre-vazquez.cloud/alexandrev/hidden11:latest

Required Gitea repository secrets:

  • REGISTRY_USERNAME: alexandrev
  • REGISTRY_TOKEN: token with package/container registry permissions

Create/read tokens from pass; never commit credentials.

Kubernetes

kubectl apply -f deploy/k8s

The app is configured for:

  • Namespace: hidden11
  • Ingress host: hidden11.app
  • Ingress class: public
  • TLS secret: hidden11-tls

To enable analytics in Kubernetes, create a secret named hidden11-analytics in the hidden11 namespace with:

  • NEXT_PUBLIC_GA_MEASUREMENT_ID
  • NEXT_PUBLIC_CLARITY_PROJECT_ID

To enable feedback submission, create a secret named hidden11-feedback in the hidden11 namespace with:

  • N8N_FEEDBACK_WEBHOOK_URL

For the social-proof play counter, create hidden11-plays with:

  • N8N_PLAYS_WEBHOOK_URL

For the funnel pipeline, create hidden11-events with:

  • N8N_EVENTS_WEBHOOK_URL

For the daily reminder, create hidden11-push with:

  • VAPID_PUBLIC_KEY
  • N8N_PUSH_WEBHOOK_URL

For search-engine verification meta tags, create hidden11-seo with:

  • GOOGLE_SITE_VERIFICATION
  • BING_SITE_VERIFICATION

To configure the sponsor slot in Kubernetes, create a secret named hidden11-sponsor in the hidden11 namespace with any of:

  • NEXT_PUBLIC_SPONSOR_ENABLED
  • NEXT_PUBLIC_SPONSOR_NAME
  • NEXT_PUBLIC_SPONSOR_TAGLINE
  • NEXT_PUBLIC_SPONSOR_CTA
  • NEXT_PUBLIC_SPONSOR_URL
  • NEXT_PUBLIC_SPONSOR_LABEL

ArgoCD

kubectl apply -f deploy/argocd/application.yaml

The ArgoCD Application includes ArgoCD Image Updater annotations for:

  • image: gitea.alexandre-vazquez.cloud/alexandrev/hidden11
  • strategy: digest
  • write-back: argocd
S
Description
No description provided
Readme 1 MiB
Languages
TypeScript 87.9%
JavaScript 11.8%
Dockerfile 0.2%
CSS 0.1%