- TypeScript 38.6%
- Vue 20.7%
- Astro 15.4%
- JavaScript 9.7%
- CSS 9.6%
- Other 6%
|
All checks were successful
/ build (push) Successful in 52s
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. |
||
|---|---|---|
| .forgejo/workflows | ||
| directus | ||
| docs | ||
| formbricks | ||
| listmonk | ||
| nginx/templates | ||
| postgres/init | ||
| site | ||
| .env | ||
| .env.production | ||
| .gitignore | ||
| .prettierignore | ||
| .prettierrc | ||
| compose.override.yml | ||
| compose.yml | ||
| moc.code-workspace | ||
| package-lock.json | ||
| package.json | ||
| README.md | ||
| tsconfig.json | ||
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.
- Create the item, fill the translations, set
statusto published. - 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.htmlinto Campaigns → Templates, type Campaign.listmonk/public-custom.cssinto 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.
- 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 1–2 under the real room. - Run Set up RSVP rosters (⚡ menu on the event). A CI run creates the
survey
Workshop <date> <title>, two private Listmonk lists… · participantsand… · waiting list, wires the form to Directus, and setsrsvp_urlon 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