feat(voting): attendee talk voting via guild.host OAuth#58
Merged
Conversation
…lk cards Drop Stripe entirely (webhook route, client, STRIPE_* secrets, test) - voting is guild-only; the attendees API is the sole tier source. Simplify auth: session cookie is now a jose HS256 JWT (alg-pinned, lib-enforced exp) instead of hand-rolled HMAC+base64url; cookie parsing deduped into helpers/cookies.ts. The OAuth flow (helpers/oauth.ts) stays hand-rolled until the real Log in with Guild flow ships. Steal from #40: parseRequest() DRYs the 415/413/400/422 gauntlet across the c4p and vote routes; the /api/vote payload and /vote page now surface duration and audience_level.
Request profile:read + event_tickets:read + event_attendees:read at authorize time (matches the registered OAuth app). Rename IDENTITY_SCOPE to OAUTH_SCOPE. Add .dev.vars.example documenting the worker secrets for local wrangler dev.
…en machinery
Read the voter's tier from their own event_tickets:read token at login and bake
{userId, budget} into the session JWT, instead of fetching the full attendee list
with an organizer token. Removes the userinfo->attendees id-join blocker and unblocks
local testing (no organizer consent needed).
Deletes repositories/oauth-token.ts, repositories/attendees.ts, the oauth_tokens
table, GUILD_ORG_REFRESH_TOKEN, and the event_attendees:read scope. Adds
fetchTicketTier + vote().budgetForTier; getUserId becomes getSession.
Ticket response shape is undocumented; the tier path is a first guess pending one
live login (see TODO.md runbook).
GET /api/vote/logout clears the vote_session cookie (matching Path/SameSite/Secure attributes so the browser drops it) and redirects back to /vote.
Links to GET /api/vote/logout in the ready state, next to the remaining-votes count.
Read the ?error= param the callback bounces back (denied/state/token/identity/ notattendee), toast a pt-BR message, and strip it from the URL. Guards run inside useEffect so SSR stays window-free.
…nt branch The remote voting-system had backed the voting feature out in favor of an account page; this keeps the voting feature and folds in the still-useful pieces: a /account page showing the logged-in guild.host user id (GET /api/vote/me, from getSession), the navbar link, and the i18n strings for all three locales.
53f25e6 to
1a3c5ad
Compare
closesAt was fetched but never used. Now render the deadline while open, show a closed notice and disable the checkboxes once past closesAt, and no-op the toggle.
Both pages were hardcoded pt-BR while the site is trilingual. Route all strings through the shared Text/text helpers, add en-US and es-419 catalog entries, and format the deadline with the current Docusaurus locale.
Route every user-facing string in the C4P flow through Text/text helpers and add en-US + es-419 catalog entries (117 keys per locale). definitions.ts options carry a labelId translation key instead of a literal label (value unchanged), rendered via Text at each consumer including the vote page. schema.ts zod messages became translation keys resolved lazily in validateStep to follow the current locale. Export TranslationId for that cast.
# Conflicts: # package.json
There was a problem hiding this comment.
Pull request overview
Implements a community talk voting system tied to guild.host OAuth, including a server-side ticket-tier→budget lookup and a D1-backed vote store, plus the corresponding /vote and /account website UIs and i18n work.
Changes:
- Adds Cloudflare Worker endpoints for guild.host OAuth login/session + vote listing/casting, backed by new D1 tables (
ticket_tiers,c4p_votes,manager_oauth). - Adds website pages/styles for voting and account UX, plus navbar auth dropdown and login error toast handling.
- Refactors C4P UI strings + validation messages to use i18n translation keys across locales.
Reviewed changes
Copilot reviewed 46 out of 48 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| TODO.md | Deployment checklist + caveats for the voting system rollout. |
| test/server/routes/vote.test.ts | New test coverage for session, budget lookup, and vote route behaviors. |
| test/server/routes/stripe-webhook.test.ts | Removes Stripe webhook tests (feature removed). |
| src/website/scss/pages/voting.scss | Styling for /vote and /account pages. |
| src/website/scss/pages/partial/_navbar.scss | Styles for the new account button + dropdown in the navbar. |
| src/website/pages/vote/index.tsx | Voting UI: fetch session/talks, optimistic vote toggling with debounce, budget/closed state rendering. |
| src/website/pages/c4p/index.tsx | C4P step titles/progress/page title moved to i18n. |
| src/website/pages/c4p/_components/talk.tsx | C4P talk step labels/options moved to i18n; updates aria-labels accordingly. |
| src/website/pages/c4p/_components/success.tsx | Success screen content moved to i18n. |
| src/website/pages/c4p/_components/introduction.tsx | Intro copy + toast strings moved to i18n; topic rendering now uses translation IDs. |
| src/website/pages/c4p/_components/field-status.tsx | Valid/invalid aria-labels moved to i18n. |
| src/website/pages/c4p/_components/diversity.tsx | Diversity step copy/options moved to i18n with interpolated link. |
| src/website/pages/c4p/_components/about.tsx | About step labels/options moved to i18n; updates aria-labels accordingly. |
| src/website/pages/account/index.tsx | New /account page consuming /api/vote/me. |
| src/website/hooks/c4p/useSubmit.tsx | C4P submit toasts moved to i18n. |
| src/website/contexts/c4p/schema.ts | Zod error messages changed to translation keys, resolved via text() at validation time. |
| src/website/contexts/c4p/index.tsx | “required fields” toast moved to i18n. |
| src/website/contexts/c4p/definitions.ts | C4P option/topic labels now reference translation IDs instead of hardcoded strings. |
| src/website/components/shared/i18n.tsx | Exports TranslationId type for reuse. |
| src/website/components/_partial/Navbar.tsx | Adds global login error toast handler + integrates new AuthButton. |
| src/website/components/_partial/AuthButton.tsx | New navbar auth button: login link or account dropdown based on /api/vote/me. |
| src/server/types.ts | Replaces Stripe env requirements with voting/OAuth env fields. |
| src/server/routes/vote.ts | New vote routes (GET talks/votes; POST add/remove vote) with session + budget enforcement. |
| src/server/routes/stripe-webhook.ts | Removes Stripe webhook handler. |
| src/server/routes/c4p.ts | Uses shared parseRequest helper for JSON/content-type/size/schema validation. |
| src/server/routes/auth.ts | New OAuth login/callback/session-cookie flow + /api/vote/me + logout. |
| src/server/routes.ts | Registers new auth/vote routes; removes Stripe webhook route. |
| src/server/repositories/vote.ts | D1 repository for votable talks, user votes, casting/removing votes, tier budget lookup. |
| src/server/index.ts | Adds routing for auth/vote endpoints; enables GET in CORS and credentials when origin is explicit. |
| src/server/helpers/session.ts | New JWT session signing/verification + dev header bypass. |
| src/server/helpers/request.ts | Adds parseRequest helper shared across routes. |
| src/server/helpers/oauth.ts | OAuth helper: build authorize URL, exchange code, fetch userinfo, manager token cache/refresh, attendee tier lookup. |
| src/server/helpers/cookies.ts | Adds cookie reader helper. |
| src/server/configs/vote.ts | Voting window (VOTE_CLOSES_AT) + votable talk status constant. |
| src/server/configs/stripe.ts | Removes Stripe config. |
| src/server/configs/oauth.ts | Adds guild.host OAuth endpoint + cookie + scope constants. |
| resources/schema.sql | Adds tables for tier budgets, votes, and cached manager OAuth tokens. |
| README.md | Mentions community voting as part of repo scope. |
| package.json | Removes Stripe dependency; adds jose. |
| package-lock.json | Lockfile updated for jose and removal of Stripe. |
| i18n/pt-BR/code.json | Adds vote/account/auth + expanded C4P translations (pt-BR). |
| i18n/es-419/code.json | Adds vote/account/auth + expanded C4P translations (es-419). |
| i18n/en-US/code.json | Adds vote/account/auth + expanded C4P translations (en-US). |
| DEVELOPMENT.md | Adds deep-dive documentation on the voting system architecture/ops. |
| CLAUDE.md | Adds repo guidance/documentation (tooling, conventions, voting system overview). |
| .gitignore | Ignores /.serena. |
| .env.example | Clarifies build/deploy vars vs runtime secrets; removes Stripe vars. |
| .dev.vars.example | Adds runtime secret template for guild.host OAuth + session config. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…s, fallback for non-key validation messages
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.
WIP do sistema de votação