Configure for register.collaborative-finance.net

- Update all URLs/domains from placeholders to collaborative-finance.net
- Traefik Host rule: register.collaborative-finance.net
- Add embed-snippet.html with ready-to-paste registration button
- Update CLAUDE.md with deployment and DNS instructions

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Jeff Emmett 2026-03-19 13:46:23 -07:00
parent 84b4e78a7e
commit e2404eaaf1
8 changed files with 74 additions and 23 deletions

View File

@ -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 <newsletter@cofi.example.com>
INTERNAL_NOTIFY_EMAIL=team@cofi.example.com
EMAIL_FROM=CoFi <newsletter@collaborative-finance.net>
INTERNAL_NOTIFY_EMAIL=team@collaborative-finance.net
# ── Listmonk (direct PostgreSQL) ──
LISTMONK_DB_HOST=listmonk-db

View File

@ -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 `<a>` 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 <your-cloudflare-tunnel-id>.cfargotunnel.com
```
Or if using Cloudflare proxy on their domain, they can add the CNAME to point to your tunnel.

View File

@ -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 {

View File

@ -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",

View File

@ -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:

36
embed-snippet.html Normal file
View File

@ -0,0 +1,36 @@
<!--
CoFi Registration Button — Embed Snippet
Copy-paste this anywhere on collaborative-finance.net to add a registration button.
The button links to the standalone registration app at register.collaborative-finance.net.
Customize: Change the button text, colors, or styling below to match your site.
-->
<!-- Option 1: Styled button (self-contained, no external CSS needed) -->
<a
href="https://register.collaborative-finance.net/register"
style="
display: inline-block;
padding: 14px 32px;
background: #2563eb;
color: #ffffff;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
font-size: 16px;
font-weight: 600;
text-decoration: none;
border-radius: 8px;
transition: background 0.2s;
"
onmouseover="this.style.background='#1d4ed8'"
onmouseout="this.style.background='#2563eb'"
>
Register for CoFi 2026
</a>
<!-- Option 2: Minimal link (use your own CSS) -->
<!--
<a href="https://register.collaborative-finance.net/register" class="your-button-class">
Register for CoFi 2026
</a>
-->

View File

@ -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 },

View File

@ -171,17 +171,18 @@ export const EMAIL_BRANDING = {
taglineColor: "#1e40af", // blue-800
highlightBg: "#eff6ff", // blue-50
highlightBorder: "#2563eb", // blue-600
fromDefault: `${EVENT_NAME} <newsletter@cofi.example.com>`,
internalNotifyDefault: "team@cofi.example.com",
fromDefault: `${EVENT_NAME} <newsletter@collaborative-finance.net>`,
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 ──────────────────────────────────