Conversation
Deploys are automatic on merge to main (Railway + Vercel GitHub integrations), so the manual one-command path is obsolete — and dangerous. - Delete server/scripts/deploy-all.js: it re-ran stale one-off Symbiosis/ Plata-Mia prod data mutations (whose db:* npm scripts no longer exist), did supabase db push, and auto-committed/pushed main. - Remove the deploy:all script from server/package.json. - Rewrite the PRODUCTION_DEPLOYMENT.md top section to document the auto-deploy-on-merge flow + the manual Supabase data step. - Fix stale defaults: verify scripts' FRONTEND_URL and client/.env.example's prod API host now point at stadium.joinwebzero.com / stadium-production-996a. - CLAUDE.md: drop deploy:all from the operational list; note auto-deploy.
…oling chore: remove stale/dangerous deploy-all.js + fix stale prod URLs
'Units' read oddly; projects/entries are now labelled ENTRY / ENTRIES. User-facing strings only — internal identifiers (UnitCard, unit-card.tsx, toUnit, unitNumber, UnitForCard, UnitDetailModal) are unchanged. Covers card labels, the detail modal header, 'NOW SHOWING / ENTRY NNN', M2 'ENTRY / TEAM' + 'NO ENTRIES MATCH' + row labels, project-detail breadcrumb, admin card label, the 'N ENTRIES' counts, the 'Total Entries' stat, search placeholders, and the 404 message.
copy: rename user-facing 'UNIT' label to 'ENTRY'
… view
Makes the landing page an entry point to all WebZero programs and lets a
program show its projects.
- New ProgramSpaces component: 4 evergreen type 'spaces' (Hackathons, M2
Incubator, Dogfooding, PitchOff) with icon + blurb + per-type event count
+ a culture blurb, in the existing rack/LCD aesthetic. Rendered on HomePage
after the stats panel; reuses the programs already fetched there.
- ProgramsPage: optional ?type= filter (useSearchParams) so the spaces are
real category links; M2 keeps its dedicated /m2-program page.
- ProgramDetailPage: fetch + render the program's Stadium project entries via
getProjects({hackathonId: slug}) as a UnitCard grid linking to /m2-program/:id;
falls back to the existing signup-derived project cards (PitchOff/Dogfooding).
Client-only; no server/schema/data changes.
feat(landing): program-type spaces entry point + per-program projects view
- helmet for X-Frame-Options / X-Content-Type-Options / etc. CSP disabled (JSON API, no HTML) and crossOriginResourcePolicy set to 'cross-origin' so the separately-hosted SPA can still read responses. - express-rate-limit: app-wide 200/min/IP default + a tight 10/min/IP on the unauthenticated, signature-verifying /api/admin/session endpoint. - trust proxy = 1 so client IPs are correct behind Railway's proxy. helmet + express-rate-limit added to deps.
Lets someone without a Stadium project apply to a program. A public form
emails the team; they approve manually and reply (matching the original ask).
- POST /api/programs/:slug/applications/non-member (public, no auth): validates
{name, email, walletAddress?, pitch}, honeypot field, then emails the team.
- non-member-application.service: sends ONE email to info@joinwebzero.com with
sacha@joinwebzero.com cc'd and applicant details in the body. Fixed recipients
only (applicant address never used as a recipient) so it can't be abused as an
open relay. HTML-escaped body. Returns 503 if Resend isn't configured.
- email-transport: forward cc to Resend.
- Client: NonMemberApplyModal + a 'Don't have a Stadium project yet?' CTA on
open program detail pages; api.submitNonMemberApplication.
- Tests: validation + the email to/cc/body + HTML-escaping.
Note: the original confirmation-to-applicant was intentionally dropped — it'd
let the endpoint email arbitrary addresses from our verified domain.
feat(programs): non-member apply flow — emails the team (#140)
…ng artwork The audio used to restart on every page change because each page renders its own <Navigation> (which held the brightness-rack + the iframe), so it remounted. - Move the SoundCloud iframe + widget + state into a SoundCloudAudioProvider mounted ABOVE the router in App.tsx, so playback persists across client-side navigation. brightness-rack reads state via useSoundCloudAudio() context. - Playback view is now a 'now playing' card: track artwork (artwork_url, falling back to the profile avatar; upscaled to 200px) + title + 'pommeshdrms · genre' with discreet SoundCloud / Instagram links underneath + the mute toggle. - Context/hook split into use-sound-cloud-audio.ts (react-refresh clean).
…ount-linking
Adds Google/Apple/passkey sign-in (via Supabase Auth) alongside wallet SIWS,
keeping the wallet as the authorization principal via account-linking.
- Migration auth_identity_links (supabase_user_id ↔ wallet).
- supabaseUser.js: verify a Supabase access token via supabase.auth.getUser
(no local JWT secret needed); x-supabase-token header.
- identity-link repo + service (optionally mirrors email into wallet_contacts).
- auth.controller + /api/auth routes:
- POST /link-wallet: requires a valid Supabase session AND a SIWS signature,
records the link, returns the existing HMAC session bearer for the wallet.
- POST /session-from-social: Supabase session → linked wallet → session bearer
(409 needsLink if not linked). Reuses issueSessionToken so ALL existing
route middleware authorize unchanged.
- CORS allows x-supabase-token. 7 new tests; full suite green (271).
Add a nullable `content` JSONB column to programs holding an ordered list of typed sections (text / steps / schedule / lineup / stats / feedback / cta) rendered as on-brand panels on the program detail page. Reusable for every program without a per-section schema change. - supabase migration adds `content` (additive, idempotent) - program.repository maps content both ways - validateProgramContent enforces typed sections, size caps, http(s) URLs - ProgramContent renderer reuses LCDStat for stat tiles - Dogfooding Denver fixture populated as a worked template, incl. Typeform feedback highlights + stats snapshot - admin UI editing of content logged to the improvement backlog (deferred)
RESEND_API_KEY / RESEND_FROM_EMAIL power all outbound email through one transport: project notifications (m2 approved / changes requested, application accepted / rejected) and non-member program applications. The old comment said it was "unused"; document that an unset key means no email goes out and that RESEND_FROM_EMAIL must be a verified domain.
Add previous/next track buttons and a seek slider to the AUDIO row, wired to the SoundCloud Widget API (next/prev/seekTo, PLAY_PROGRESS for the live position, getDuration for length). Shows elapsed/total time and a "FEATURING ARTISTS WE LOVE" caption to frame the music as curated.
fix(audio): persist SoundCloud playback across navigation + now-playing artwork
feat(auth): social sign-in server core — Supabase + wallet account-linking (part 1)
feat(programs): templatable rich content sections per program
docs(env): correct stale Resend comment — email transport is live
The merge of develop (which moved the SC widget into a provider above the router) auto-combined with this branch's in-component transport edits and produced a broken brightness-rack (duplicate `muted`, unterminated region) that failed the build. Re-implement prev/next + seek on the new architecture instead: - provider owns positionMs/durationMs + next/prev/seek (PLAY_PROGRESS + getDuration), exposed via useSoundCloudAudio() - brightness-rack renders the seek slider + prev/next + elapsed/total time and the "FEATURING ARTISTS WE LOVE" caption under the now-playing card
The SoundCloud profile loads a single long DJ set, so next/prev-track had nowhere to go. Repurpose the two buttons to skip ±15s within the current track via seekTo (reliable), with rewind/forward icons. Drop the unused next/prev from the audio provider + context.
Lead with WebZero as the room where people build something cool, and call out that each program is tied to a specific event with prizes (cash, event tickets, merch) — clearer framing than the old "ship then keep going" line.
feat(brightness-rack): audio transport — prev/next + seek slider
copy(programs): reframe WHAT WE DO around events + prizes
Replace em-dashes (—) with commas, periods, colons, or parentheses across user-facing copy: JSX text, toasts, labels, placeholders, aria-labels, and WebZero-authored content (program descriptions + the Denver content). House style: no em-dashes in our own copy. Left untouched: code/CSS comments, builder-written project descriptions (mockWinners, PitchOff submissions), numeric en-dash ranges, and the bare "—" empty-value placeholder glyph in admin tables.
Lets an admin grant program access by email (no wallet). Foundation for social sign-in onboarding (PR 2 adds the client magic-link UI). - migration: program_admin_emails (program_id, email, invited_by); email stored lowercased so the PK is case-insensitive - repository: list/add/remove/isAdminByEmail - middleware: requireProgramViewer — a valid Supabase token whose email is a program admin gets VIEW access; otherwise defers to the wallet requireProgramAdmin. Applied to read routes only; mutations stay wallet/global-gated, so email admins are view-only - controller + routes: POST /:slug/admins/invite, GET /:slug/admins/emails, DELETE /:slug/admins/emails/:email - onboarding email: program-admin-invite template + service (Resend), best-effort send so a missing key never loses the grant - tests: requireProgramViewer (valid email passes view-only, wrong email / bad token / no token fall back to wallet and are denied) - env: document FRONTEND_URL for email links
Completes the email-admin onboarding (client side of the backend in the previous commit). - supabase client (null when VITE_SUPABASE_* unset) + useSocialAuth hook (email magic link via signInWithOtp; exposes token/email/authHeader) - api: listProgramAdminEmails / inviteProgramAdminEmail / removeProgramAdminEmail - ProgramAdminsSection: invite-by-email form + email-admin list (global admins), with emailSent feedback - AdminProgramPage: three-way gate — wallet admin (unchanged) / social view-only viewer / sign-in panel (connect wallet OR email magic link). After social sign-in, probes authorization and renders applications read-only; ApplicationCard gains a readOnly prop - env: document VITE_SUPABASE_URL / VITE_SUPABASE_ANON_KEY
feat(admin): add program admins by email + onboarding sign-in (full feature)
copy: remove em-dashes from first-party app copy
The unauthenticated POST /:slug/applications/non-member route emails the team on every call and was only under the generous global 200/min limiter, so a single IP could flood the inbox. Add a tight per-IP limiter (5 per 15 min) on this route only. It can't relay to arbitrary addresses (fixed recipients), so this just stops inbox flooding.
#158 shipped email magic-link admin sign-in via a social-token-direct + view-only middleware design, making the #152 wallet account-linking bridge dead code — it had zero client consumers. Removed: - /api/auth routes + auth.controller (link-wallet, session-from-social) - identity-link service + repository - auth_identity_links migration (never wired to a live consumer; if it was ever applied to a Supabase env, drop the table manually — nothing reads it) - the obsolete auth.controller test Kept (reused by #158): api/auth/supabaseUser.js and the x-supabase-token CORS header. Server tests: 284 pass (was 291; -7 for the deleted controller's tests). Client build + lint clean (no client changes).
- Logo: the ||| icon (favicon) now sits left of "STADIUM" in both the top-nav brand and the landing hero headline. - Audio: bind the SoundCloud FINISH event to restart playback so the music loops forever (it already survives navigation — the iframe lives above the router). - Footer: extract a shared <SiteFooter> (kills the copy that had drifted between Layout and ProjectDetailsPage). New credit line "Created with ❤️ by sachalansky from WebZero" (sachalansky → x.com/sachalansky, WebZero → joinwebzero.com), app mono/LCD typography, and a SOURCE link to the Stadium repo alongside the existing org GitHub + X.
fix(security): rate-limit the public non-member apply route
…k-bridge chore(auth): remove unused social account-linking bridge (#152)
feat(landing): logo by STADIUM + looping audio + unified footer
…e ruler - Project details: convert the Overview/Milestones tab content (Final Deliverables, Submission Status, placeholders) from shadcn Card + sans/muted type to the app's panel + mono/LCD aesthetic (label-hw headers, hardware link rows, text-body). Tab labels become mono/uppercase. Drop the now-unused Card import. Align TeamPaymentSection's leftover muted token to text-label-dim. - Hero tagline: "Stuff people build here. Browse our programs, leave your mark." - Brightness rack: lay the hour scale out by time of day (00h…20h left→right in order, instead of by solar brightness which made 8h/16h collide). Phase anchors (NIGHT/DAWN/NOON/DUSK) sit at their real clock times and the green "now" tick marks the current time on the ruler.
design: re-land round-2 polish onto develop (#162 was stranded)
Three release-hardening fixes: - #129: validate ADMIN_SESSION_SECRET at boot via assertSessionSecret(), called in server.js startup, instead of lazily on the first admin sign-in. - #130: requireAdmin and requireTeamMemberOrAdmin now set req.user.chain on every grant path (admin + team-member), so audit logging has the chain. - #134: factor csvCell + formula-injection defense into a shared api/utils/csv.js (csvCell + csvRow); program-inbox CSV export now imports it so future exports inherit the same RFC-4180 quoting + injection guard. Tests: csv util (formula injection + quoting), assertSessionSecret (missing / short / ok), and middleware req.user.chain assertions. Full suite 293 pass.
First official release. CHANGELOG.md documents the v1.0.0 feature set grouped by what you can do with Stadium (discover, build/participate, run an event, trust/safety). Client package.json bumped 0.0.0 -> 1.0.0 to match the server.
Stadium is for builders broadly, not Polkadot-only; keep the accurate multi-chain (Substrate/Ethereum/Solana) detail which shows the breadth.
chore(release): v1.0.0 — CHANGELOG + version bump
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stadium v1.0.0 — first official release
Promotes
develop→main(production deploy to Railway + Vercel). Full notes inCHANGELOG.md.What ships in v1.0.0
Stadium becomes a recurring-events platform for WebZero's builder programs (multi-chain, not Polkadot-only): hackathons, M2 incubator, dogfooding, PitchOff — where builders show their work, the community explores it, and organizers run each event end to end.
Highlights since the last promotion (#145):
req.user.chainfor audit (audit-log forensics: requireAdmin and requireTeamMemberOrAdmin should set req.user.chain #130), shared injection-safe CSV util (hardening: factor csvCell into a shared util so all future CSV exports inherit formula-injection defense #134), non-member apply rate limiting (fix(security): rate-limit the public non-member apply route #159).v1.0badge linking to the release notes.PRs included: #146–#165.
After merge
v1.0.0onmain.v1.0link resolve).