• TypeScript 38.6%
  • Vue 20.7%
  • Astro 15.4%
  • JavaScript 9.7%
  • CSS 9.6%
  • Other 6%
Find a file
hicham 33eabcf30e
All checks were successful
/ build (push) Successful in 52s
Add the event RSVP tutorial with screenshots captured by playwright
docs/event-rsvp-tutorial.md walks an organizer from signing in to reading the
roster: fill the event, publish, run "Set up RSVP rosters", announce, change or
cancel. The screenshots come from docs/capture-rsvp-tutorial.ts against the
local stack: it seeds a venue, a host and a sample event via the api, logs in,
and shoots. Two gotchas baked into the script: the BSL project-owner dialog
overlays every page until dismissed, and the form scrolls inside an inner
container, so crops are taken in a viewport tall enough that nothing scrolls.

The respondent side (form, status page, mail, listmonk roster) has no
screenshots: neither formbricks nor listmonk runs locally, and no workshop has
had rsvp on prod yet.
2026-09-08 20:01:47 +02:00
.forgejo/workflows Create the RSVP survey from the event, directus as source of truth 2026-08-11 14:24:09 +02:00
directus Document the schema change order in the directus readme 2026-09-08 17:17:18 +02:00
docs Add the event RSVP tutorial with screenshots captured by playwright 2026-09-08 20:01:47 +02:00
formbricks Open call form to create a workshop together, linked from the landing page 2026-09-08 18:49:10 +02:00
listmonk Hide the listmonk password form, sso only 2026-08-04 17:29:45 +02:00
nginx/templates Retire Huly and the moc-cms sidecar: Directus is the CMS 2026-08-03 00:00:56 +02:00
postgres/init Add Directus at cms subdomain: postgres, valkey cache, minio storage 2026-08-02 23:03:51 +02:00
site Open call form to create a workshop together, linked from the landing page 2026-09-08 18:49:10 +02:00
.env Tear down the formbricks analytics stack 2026-08-12 14:55:07 +02:00
.env.production Add the listmonk api and rsvp hook credentials 2026-08-05 00:35:47 +02:00
.gitignore Ignore decrypted env files, keep .env.production tracked 2026-08-04 20:03:55 +02:00
.prettierignore Add prettier at the repo root 2026-08-04 19:00:44 +02:00
.prettierrc Add prettier at the repo root 2026-08-04 19:00:44 +02:00
compose.override.yml Turn password login back on for the local directus 2026-09-08 20:01:46 +02:00
compose.yml Tear down the formbricks analytics stack 2026-08-12 14:55:07 +02:00
moc.code-workspace Run prettier on the repo 2026-08-04 19:00:57 +02:00
package-lock.json Add the event RSVP tutorial with screenshots captured by playwright 2026-09-08 20:01:47 +02:00
package.json Add the event RSVP tutorial with screenshots captured by playwright 2026-09-08 20:01:47 +02:00
README.md Add the event RSVP tutorial with screenshots captured by playwright 2026-09-08 20:01:47 +02:00
tsconfig.json Add the event RSVP tutorial with screenshots captured by playwright 2026-09-08 20:01:47 +02:00

MOC

Build status

Website, CMS, newsletter and forms of Musique Ordinateur Club, one compose.yml.

Service Role
musiqueordinateur.club nginx serving the Astro site CI builds into html/
cms.musiqueordinateur.club Directus — content and the flows that drive the rest
list.musiqueordinateur.club Listmonk — newsletter, workshop rosters, all mail
forms.musiqueordinateur.club Formbricks — RSVP and call forms (fork image, formbricks/)
storage.musiqueordinateur.club MinIO — uploads for Directus and Formbricks

Signing in

Everyone signs in with their Forgejo account (git.hicham.party). Directus self-registration is open to @musiqueordinateur.club addresses and lands in the Editors role. Formbricks accounts are invited by an existing member from the organization settings. Listmonk keeps its password login as the break-glass path if Forgejo is down: the form is still on the login page, hidden by the last two rules of listmonk/public-custom.css — unhide it with the browser's devtools.

Local development

git clone https://git.hicham.party/moc/musiqueordinateur.club
cd musiqueordinateur.club
docker compose up -d
cd site && npm install && DIRECTUS_URL=http://localhost:8055 npm run dev

compose.override.yml publishes the site on :8080, Listmonk on :8081 and Directus on :8055, and turns password login back on so the .env admin (admin@example.com / change-me) can sign in. .env holds plaintext dev defaults. Formbricks SSO only works against prod, so local work is site + Directus; apply the schema and run the seed once, see directus/README.md.

How the pieces connect

Directus is the source of truth: every workflow below starts by editing an item there and pressing a button. Listmonk holds every address and sends every mail. Formbricks only collects answers and posts them back into Directus.

editor ── Directus ──"Rebuild site"──▶ Forgejo CI ──rsync──▶ nginx (site)
             │
             └─"Set up RSVP rosters"─▶ Forgejo CI ─▶ Formbricks survey
                                                 └▶ Listmonk rosters
                                                 └▶ webhook ─▶ Directus rsvp-hook
                                                                  └▶ Listmonk tx mail
site subscribe form ─────────────────────────────▶ Listmonk newsletter list

Publish content

Directus holds news and events with per-language translations (en/fr/nl), plus hosts and venues. Saving, publishing or deleting any of them fires the Rebuild site flow, which dispatches the CI workflow; the site is live a couple of minutes later, the badge above shows the run. Rebuild site now ( menu on news or events) forces a deploy without a content change.

  1. Create the item, fill the translations, set status to published.
  2. For an event: start/end, venue, hosts, the fact fields (duration, price, language, participants), tags.

If no run shows up, read the flow's run log: directus/README.md § Debugging flows and extensions.

Schema and permissions are code, reapplied on every deploy — fields added in the prod UI are reverted. Before changing either, read directus/README.md § Changing the schema.

Newsletter

Campaigns are written and sent from the Listmonk UI. When announcing a workshop, link the event's rsvp_url with ?via=newsletter so signups record where they came from (the site adds ?via=site). Workshop rosters are Listmonk lists too, but people joined them to reserve a seat: never send a campaign to one.

The site's subscribe form (site/src/components/SubscribeForm.astro) posts straight to Listmonk's public subscription form with the newsletter list preselected; double opt-in is Listmonk's.

Listmonk settings (SMTP, OIDC, templates, CSS) live in its database, not in the repo — redeploys keep them, a fresh install needs them pasted again:

  • listmonk/email-template.html into Campaigns → Templates, type Campaign.
  • listmonk/public-custom.css into Settings → Appearance → Public pages.

Workshop RSVP

The event carries the seat limit and the form link; a button creates everything else. Step by step with screenshots: docs/event-rsvp-tutorial.md.

  1. On the event in Directus, set capacity (empty = unlimited) and the fact fields, then publish. The limit is soft — two simultaneous signups can both get the last seat — so set it 12 under the real room.
  2. Run Set up RSVP rosters ( menu on the event). A CI run creates the survey Workshop <date> <title>, two private Listmonk lists … · participants and … · waiting list, wires the form to Directus, and sets rsvp_url on the event, which rebuilds the site with the button.

Re-run the flow after changing capacity or the facts; survey and lists are matched by name and refreshed in place. Renaming the title or moving the date changes that name — retitle the survey in Formbricks first or the flow creates a second one.

What a respondent gets: a status page that guesses "you're in" / "looks full" / "check your mail", then a mail from Listmonk — confirmation and the participants list under capacity, waitlist and the waiting list at or over. The mail is the authority, and its facts are read from the event at send time, so editing the event changes the next mail. Rosters are read in Listmonk (Subscribers, filter by list).

For developers: the flow dispatches .forgejo/workflows/rsvp.yml, which runs formbricks/workshop-rsvp.ts; responses land in the rsvp-hook Directus extension. One-time setup, the signup path, retries and dedup, and replaying a dropped delivery: formbricks/README.md § One-time setup, § How a signup flows, § Rosters and the hook, § Troubleshooting.

Workshop call

The landing page links an open call, Create a workshop together, a Formbricks survey created once by formbricks/workshop-call.ts and edited in Formbricks since. Proposals are read there (Responses); nothing is wired behind it. The link on the site adds ?via=site; use ?via=newsletter in campaigns.

Deploy

Push to main (or a workflow_dispatch) runs .forgejo/workflows/ci.yml: builds the site, rsyncs it and the compose stack to the VPS, brings the services up and reapplies the Directus schema. Queued runs collapse to the newest; a deploy in flight is never cancelled.

Production secrets are dotenvx-encrypted in .env.production:

dotenvx set KEY value -f .env.production

CI does not docker compose pull, so a rebuilt :latest Formbricks image needs a manual pull on the VPS, through the jump host (directus/README.md § Reaching prod):

docker compose pull moc-formbricks && docker compose up -d moc-formbricks