A movie-night website with a shared watchlist, anonymous voting, private host controls, verified-email RSVPs, movie suggestions, and scheduled invitations and reminders. The interface runs on GitHub Pages; Supabase provides the database, authentication, and scheduled functions; Resend delivers email. TMDB supplies remote posters.
Status: implemented locally, not deployed or connected to production services. Provider accounts, DNS records, secrets, and a live acceptance test are required below. Without configuration, the site displays a read-only preview and disables submissions. The old browser-only prototype data is not imported.
Requires Node.js 22 or newer.
npm ci
npm test
npm run build
npm run preview
Open http://127.0.0.1:4173. To connect a development Supabase project, copy
.env.example to .env, fill in the three public values, and build with:
node --env-file=.env scripts/build.mjs
For browser tests, build an unconfigured preview with npm run build, then run
npm run test:browser. Windows uses installed Microsoft Edge. On Linux/macOS first
run npx playwright install chromium. Tests mock external network services; they do
not send email or use production data.
sent job means provider acceptance, not inbox delivery.Create a Supabase project, then apply these files in order using its SQL editor:
supabase/migrations/202610020001_cinema.sqlsupabase/migrations/202610020002_mail.sqlsupabase/seed.sqlAlternatively, link the Supabase CLI to the project and use its migration/seed workflow. Migrations create new tables and should be applied only once. The seed only inserts missing movies; rerunning it does not overwrite refreshed posters or host changes.
In Authentication settings:
In both Confirm signup and Magic link email templates, display `` as a one-time code. The website uses code entry, not callback links. For example:
<h2>Your Living Room Cinema code</h2>
<p>Enter this code on the website: <strong></strong></p>
<p>If you did not request it, ignore this email.</p>
https://movies.wiederhold.dev/ (or the GitHub project URL while
staging). Keep email-code expiry short and retain authentication rate limits.movies.wiederhold.dev and,
while staging, shamayla38.github.io. Add localhost only to a development widget.
In Supabase Auth’s CAPTCHA settings, choose Turnstile and enter its secret key.
The site’s build uses the corresponding public site key.After each host has signed in through My email, grant their role in the SQL editor:
insert into public.hosts(user_id)
select id from auth.users
where lower(email) = lower('REPLACE_WITH_HOST_EMAIL')
and email_confirmed_at is not null and not is_anonymous
on conflict do nothing;
Run this for both hosts. A sender address is unrelated to a host’s login address. Granting host status via user-editable metadata is intentionally unsupported.
Recommended sender: The Living Room Cinema [email protected]. A new inbox
is not needed just to send through Resend. For replies, configure Cloudflare Email
Routing for [email protected] to either host’s existing inbox, or use a separate
EMAIL_REPLY_TO. Forwarding alone does not let you send manual replies from that
alias; use a mailbox provider if you want a shared inbox with send-and-reply support.
Create a Resend account and verify a domain you control. Add exactly the DNS records
Resend supplies in Cloudflare, including DKIM and its sending/return-path SPF records.
Do not replace unrelated MX records for an existing mailbox. If you choose a sending
subdomain instead, update EMAIL_FROM to use that verified subdomain.
Use Resend’s SMTP settings in Supabase Auth: host smtp.resend.com, port 465,
username resend, password your Resend API key, and the verified sender address.
Adjust the Supabase SMTP sending rate to suit the small guest list and your provider limits.
Using the Supabase CLI from this directory:
npx supabase login
npx supabase link --project-ref YOUR_PROJECT_REF
npx supabase functions deploy mailer
npx supabase functions deploy unsubscribe
npx supabase functions deploy refresh-posters
supabase/config.toml disables gateway JWT verification for these functions:
mailer and refresh-posters check a separate cron bearer secret themselves;
unsubscribe accepts only an unguessable per-subscriber token. Browser database
requests use Supabase authentication and explicitly restricted RPCs.
Set these server secrets in the Supabase Functions dashboard:
| Secret | Value |
|---|---|
RESEND_API_KEY |
Sending key from Resend |
EMAIL_FROM |
The Living Room Cinema <[email protected]> or your chosen verified sender |
EMAIL_REPLY_TO |
Optional receiving address/forwarding alias |
SITE_URL |
https://movies.wiederhold.dev/ including trailing slash; use the complete project path if staging |
CRON_SECRET |
Random high-entropy secret, e.g. 32 random bytes encoded as hex |
TMDB_TOKEN |
TMDB API Read Access Token from your TMDB account |
Supabase supplies SUPABASE_URL and SUPABASE_SERVICE_ROLE_KEY to deployed functions.
Never put server secrets, the service-role key, or a Supabase sb_secret_ key in
GitHub Pages, source control, or browser configuration.
In Supabase Vault, create cinema_function_url with
https://YOUR_PROJECT.supabase.co/functions/v1, and cinema_cron_secret with the
same value as CRON_SECRET. Then run supabase/schedule.sql in the SQL editor.
It installs the ten-minute email schedule and Sunday 08:00 UTC poster refresh.
Run refresh-posters once using the Functions dashboard with the cron Authorization
header to populate remote images immediately. Do not invoke mailer with real
subscribers until you are ready to send invitations.
In the repository’s Actions variables, add these public settings:
SUPABASE_URLSUPABASE_PUBLISHABLE_KEY (publishable key or legacy anon key)TURNSTILE_SITE_KEYThe build fails on missing deployment settings or a recognizable privileged key.
Only dist is uploaded. In Settings > Pages, select GitHub Actions. Push this
implementation and run Deploy website to GitHub Pages manually from Actions.
The default URL is https://shamayla38.github.io/living-room-cinema/.
Verify wiederhold.dev ownership in the GitHub owner’s Pages settings using the TXT
record GitHub supplies. Set movies.wiederhold.dev as the repository’s custom domain.
In Cloudflare DNS create:
| Type | Name | Target | Proxy |
|---|---|---|---|
| CNAME | movies | shamayla38.github.io | DNS only (gray cloud) |
Remove only conflicting records for the movies subdomain. Wait for GitHub’s DNS
check/certificate, then enable Enforce HTTPS. A CNAME file is unnecessary for
this Actions deployment. Set the final SITE_URL, Supabase Site URL, and Turnstile
allowed hostname consistently. No Cloudflare proxy is needed for this setup.
Use a staging project or only host-owned test email addresses for the first live run:
Review mail_jobs failures and Supabase function/cron logs periodically. Back up the
database and monitor the current provider quotas. Free-tier project availability is
not a guaranteed always-on service; pick a plan appropriate for unattended reminders.
The host UI reports mail status counts but does not automatically reconcile bounces.
For deletion requests, remove the user through Supabase Auth administration; application
rows cascade. Anonymous browser IDs and verified-email IDs are separate, so ask for
the anonymous ID too if a guest wants their ballots removed. No automatic retention job
is configured. Don’t prune anonymous users with active ballots or their votes disappear.
dist/: static UI, catalog fallback, styles, images, credits, privacy/unsubscribe pagesscripts/build.mjs: browser bundle and public configuration generationsupabase/migrations/: database constraints, authorization, RPCs, durable mail queuesupabase/functions/: Resend sender, unsubscribe endpoint, TMDB refreshsupabase/schedule.sql: cron setup using Vault secretstests/: actual PostgreSQL migration/RPC tests via PGlite, helpers, mocked browser flows.github/workflows/: checks and manual deploymentAutomated tests exercise SQL permissions, duplicate ballots, round cutoff, suggestions,
capacity/update/cancellation, email eligibility, leases, unsubscribe, and browser flows.
PGlite tests stub Supabase’s auth.users/auth.uid; browser tests mock Supabase and
Turnstile. They do not replace the live SMTP, DNS, concurrent-client, and Edge Function
acceptance checks above. No production resources or real email delivery have been tested.
The bundled catalog retains The Drama (2026), The Fall (2006), The Stepford Wives (1975), Something Wild (1986), Charade (1963), and The Player (1992). See poster credits for source attribution. Artwork remains the property of its respective owners.