diff --git a/.env.example b/.env.example index 8a6e7f2..92eefa4 100644 --- a/.env.example +++ b/.env.example @@ -5,7 +5,7 @@ MOLLIE_API_KEY=test_xxxxxxxxxxxxxxxxxxxxxxxxxxxx # ── Public URL (used for Mollie redirects and webhooks) ── -NEXT_PUBLIC_BASE_URL=https://cofi.example.com +NEXT_PUBLIC_BASE_URL=https://register.collaborative-finance.net # ── Google Sheets (registration data) ── # JSON string of the service account key @@ -20,10 +20,10 @@ BOOKING_SHEET_NAME=Sheet1 # ── SMTP (Mailcow) ── SMTP_HOST=mail.rmail.online SMTP_PORT=587 -SMTP_USER=newsletter@cofi.example.com +SMTP_USER=newsletter@collaborative-finance.net SMTP_PASS= -EMAIL_FROM=CoFi -INTERNAL_NOTIFY_EMAIL=team@cofi.example.com +EMAIL_FROM=CoFi +INTERNAL_NOTIFY_EMAIL=team@collaborative-finance.net # ── Listmonk (direct PostgreSQL) ── LISTMONK_DB_HOST=listmonk-db diff --git a/CLAUDE.md b/CLAUDE.md index 1d03a8b..accdc8d 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -2,23 +2,32 @@ ## Overview Standalone event registration + payment app for CoFi (Collaborative Finance). +Runs on Netcup at `register.collaborative-finance.net`, linked from the main site at `www.collaborative-finance.net` via a simple button. + Adapted from `crypto-commons-gather.ing-website` with centralized config. ## Architecture - **Stack**: Next.js 16 (App Router, standalone) + Mollie + Google Sheets + Mailcow SMTP + Listmonk - **Deploy**: Docker on Netcup with Traefik labels +- **Domain**: `register.collaborative-finance.net` (CNAME → Cloudflare tunnel on Netcup) - **Key pattern**: All event-specific config centralized in `lib/event.config.ts` ## Flow -1. User fills `/register` form → POST `/api/register` → Google Sheets (Pending) -2. User picks accommodation/payment → POST `/api/create-checkout-session` → Mollie redirect -3. Mollie webhook POST `/api/webhook` → verify payment → assign booking → update sheet → email → Listmonk +1. User clicks "Register" button on `www.collaborative-finance.net` → opens `register.collaborative-finance.net/register` +2. User fills form → POST `/api/register` → Google Sheets (Pending) +3. User picks accommodation/payment → POST `/api/create-checkout-session` → Mollie redirect +4. Mollie webhook POST `/api/webhook` → verify payment → assign booking → update sheet → email → Listmonk ## Key Files -- `lib/event.config.ts` — **Edit this to configure a new event** (pricing, dates, venues, branding) +- `lib/event.config.ts` — **Edit this to configure event** (pricing, dates, venues, branding, links) - `lib/mollie.ts` — Shared Mollie client singleton - `app/globals.css` — Blue/teal OKLch color theme - `docker-compose.yml` — Traefik-labeled deployment +- `embed-snippet.html` — Copy-paste button HTML for the main website + +## Embedding on the main site +Send `embed-snippet.html` to the website owner. It contains a self-contained styled button +that links to the registration page. No iframe, no JS, no CORS — just a simple `` tag. ## Dev Workflow ```bash @@ -27,7 +36,12 @@ pnpm dev # localhost:3000 → redirects to /register ``` ## Deployment -```bash -docker compose up -d --build +1. Your friend adds a CNAME: `register.collaborative-finance.net` → Cloudflare tunnel +2. On Netcup: `docker compose up -d --build` +3. Traefik auto-discovers via labels, Cloudflare tunnel handles TLS + +## DNS Setup (friend's side) ``` -Traefik auto-discovers via labels. Update Host rule in `docker-compose.yml` for your domain. +register.collaborative-finance.net CNAME .cfargotunnel.com +``` +Or if using Cloudflare proxy on their domain, they can add the CNAME to point to your tunnel. diff --git a/app/api/create-checkout-session/route.ts b/app/api/create-checkout-session/route.ts index 9ed17b2..becfd52 100644 --- a/app/api/create-checkout-session/route.ts +++ b/app/api/create-checkout-session/route.ts @@ -9,7 +9,7 @@ import { } from "@/lib/event.config" // Public base URL -const BASE_URL = process.env.NEXT_PUBLIC_BASE_URL || "https://cofi.example.com" +const BASE_URL = process.env.NEXT_PUBLIC_BASE_URL || "https://register.collaborative-finance.net" export async function POST(request: NextRequest) { try { diff --git a/app/layout.tsx b/app/layout.tsx index 0ebc197..3922369 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -7,7 +7,7 @@ const _geist = Geist({ subsets: ["latin"] }) const _geistMono = Geist_Mono({ subsets: ["latin"] }) export const metadata: Metadata = { - metadataBase: new URL("https://cofi.example.com"), + metadataBase: new URL("https://register.collaborative-finance.net"), title: "CoFi 2026 — Registration", description: "Register for CoFi 2026 — Collaborative Finance. Reimagining finance for the commons.", @@ -22,7 +22,7 @@ export const metadata: Metadata = { title: "CoFi 2026 — Registration", description: "Register for CoFi 2026 — Collaborative Finance. Reimagining finance for the commons.", - url: "https://cofi.example.com", + url: "https://register.collaborative-finance.net", siteName: "CoFi", locale: "en_US", type: "website", diff --git a/docker-compose.yml b/docker-compose.yml index cbd14be..cfaa5d0 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -6,7 +6,7 @@ services: environment: - NODE_ENV=production - MOLLIE_API_KEY=${MOLLIE_API_KEY} - - NEXT_PUBLIC_BASE_URL=${NEXT_PUBLIC_BASE_URL:-https://cofi.example.com} + - NEXT_PUBLIC_BASE_URL=${NEXT_PUBLIC_BASE_URL:-https://register.collaborative-finance.net} - GOOGLE_SERVICE_ACCOUNT_KEY=${GOOGLE_SERVICE_ACCOUNT_KEY} - GOOGLE_SHEET_ID=${GOOGLE_SHEET_ID} - GOOGLE_SHEET_NAME=${GOOGLE_SHEET_NAME:-Registrations} @@ -26,7 +26,7 @@ services: - BOOKING_SHEET_NAME=${BOOKING_SHEET_NAME:-Sheet1} labels: - "traefik.enable=true" - - "traefik.http.routers.cofi-register.rule=Host(`cofi.example.com`)" + - "traefik.http.routers.cofi-register.rule=Host(`register.collaborative-finance.net`)" - "traefik.http.routers.cofi-register.entrypoints=web,websecure" - "traefik.http.services.cofi-register.loadbalancer.server.port=3000" healthcheck: diff --git a/embed-snippet.html b/embed-snippet.html new file mode 100644 index 0000000..de8d5c5 --- /dev/null +++ b/embed-snippet.html @@ -0,0 +1,36 @@ + + + + + Register for CoFi 2026 + + + + diff --git a/lib/email.ts b/lib/email.ts index f4e891d..b010568 100644 --- a/lib/email.ts +++ b/lib/email.ts @@ -18,7 +18,7 @@ function getTransporter() { port: parseInt(process.env.SMTP_PORT || "587"), secure: false, auth: { - user: process.env.SMTP_USER || "newsletter@cofi.example.com", + user: process.env.SMTP_USER || "newsletter@collaborative-finance.net", pass: process.env.SMTP_PASS, }, tls: { rejectUnauthorized: false }, diff --git a/lib/event.config.ts b/lib/event.config.ts index 641cda4..ade56f0 100644 --- a/lib/event.config.ts +++ b/lib/event.config.ts @@ -171,17 +171,18 @@ export const EMAIL_BRANDING = { taglineColor: "#1e40af", // blue-800 highlightBg: "#eff6ff", // blue-50 highlightBorder: "#2563eb", // blue-600 - fromDefault: `${EVENT_NAME} `, - internalNotifyDefault: "team@cofi.example.com", + fromDefault: `${EVENT_NAME} `, + internalNotifyDefault: "team@collaborative-finance.net", } // ── External links ──────────────────────────────────────────────── export const LINKS = { - website: "https://cofi.example.com", - telegram: "https://t.me/cofi_example", - community: "https://t.me/cofi_community", - contactEmail: "team@cofi.example.com", + website: "https://www.collaborative-finance.net", + register: "https://register.collaborative-finance.net/register", + telegram: "", // TBD — set when community channel is created + community: "", // TBD — set when community channel is created + contactEmail: "team@collaborative-finance.net", } // ── Payment description template ──────────────────────────────────