尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證製作者的介紹僅有上面的一句話。 更多詳情請閱讀儲存庫 README。 儲存庫 README ↗
正在載入完整 README。
檢視完整 README ↗Public voting platform. This repo is the Next.js 16 front end. App data (polls, ballots, comments, accounts) comes from vomatt-api; site content (settings, the sign-up terms) comes from Sanity. Editors use the Sanity Studio in vomatt-admin.
Browser ──► vomatt-web (Vercel) ──► vomatt-api (server-side only, Bearer token from httpOnly cookies)
│
└────────────► Sanity (published content, expired per change on publish)
pnpm-lock.yaml; corepack enable picks the version pinned in package.json)pnpm install
cp .env.example .env.local # then fill in the values, see below
pnpm dev
NEXT_PUBLIC_SANITY_PROJECT_ID / NEXT_PUBLIC_SANITY_DATASET.Run it locally (see its README; Postgres + Redis via docker compose up -d), then set
API_URL=http://localhost:8080 and SESSION_SECRET to the API's JWT_SECRET.
Sign-in codes are emailed; with email disabled locally, read the code from Redis
(otp:email:<address>).
| Variable | Required | Example | What it's for |
|---|---|---|---|
NEXT_PUBLIC_SANITY_PROJECT_ID | Yes | abc123de | Sanity project. Also used to allow Sanity CDN images. |
NEXT_PUBLIC_SANITY_DATASET | Yes | production | Sanity dataset. |
API_URL | Yes | https://api.vomatt.com | vomatt-api origin only — no path, no trailing slash. Called from the server only. |
SESSION_SECRET | Yes | 64 random chars | Must equal the API's JWT_SECRET. Used to verify access tokens. A mismatch signs everyone out on every request. Server-only; never prefix with NEXT_PUBLIC_. |
CLIENT_IP_SECRET | Production | 48 random chars | Same value as the API's CLIENT_IP_SECRET. Sent with each user's IP so the API's rate limits and OTP lockouts apply per user. Unset: the API sees every user as this server's IP, so one abuser can rate-limit or lock out everyone. Server-only; mark Sensitive. |
SITE_URL | Yes | https://vomatt.com | Public origin, no trailing slash. Used for metadata, Open Graph and canonical URLs; the site errors if it is missing. |
API_LOG | No | 1 | Logs each API request/response from the server. On by default in pnpm dev (0 silences it); off in deployments unless 1 (e.g. a Preview scoped to the dev branch, read in Vercel Runtime Logs). |
IOS_APP_IDS | No | ABCDE12345.com.vomatt.app | iOS universal links: comma-separated <TeamID>.<bundle ID> values served in /.well-known/apple-app-site-association, so /poll/<id> and /<username> links open the app. Unset: that file 404s and links stay on the web. Set it in Production (the domain in the app's applinks: entitlement). |
SANITY_REVALIDATE_SECRET |
Not needed (leave unset): DATABASE_URL, PAYLOAD_SECRET (Payload was removed);
SANITY_API_READ_TOKEN (the site reads only published content, without a token); SANITY_STUDIO_PREVIEW_ORIGIN (the Presentation
tool was removed);
NEXT_PUBLIC_API_URL (the followers/following sheet calls this site's own /api routes when it is
unset).
On vomatt-api, set at least JWT_SECRET (same value as SESSION_SECRET), CLIENT_IP_SECRET (same value as here), the database and
Redis settings, and email (EMAIL_ENABLED=true + SMTP) — sign-in is email-code only.
The browser never calls the API directly, so its CORS_ALLOWED_ORIGINS doesn't need this site.
next build), output directory and install command at their defaults.pnpm-lock.yaml is in the repo. Don't commit a
package-lock.json alongside it. Add ENABLE_EXPERIMENTAL_COREPACK=1 (all environments) so Vercel uses
the pnpm version pinned in package.json instead of picking one from the lockfile.SITE_URL = your production domain, API_URL = production API.SITE_URL can be the preview's domain, API_URL a staging API.SESSION_SECRET and CLIENT_IP_SECRET as Sensitive.Before the first production deploy, run through docs/release/release-checklist.md.
pnpm dev # dev server (Turbopack, port 3002)
pnpm build # production build
pnpm start:web # serve the production build locally
pnpm start # run through `vercel dev` (needs the Vercel CLI and a linked project)
pnpm lint # ESLint
pnpm test # Jest (tests in src/__tests__/)
pnpm test:watch # Jest in watch mode
pnpm test:coverage # Jest with coverage
pnpm exec jest <path> # one test file
pnpm exec tsc --noEmit # type-check
pnpm generate:openapi # regenerate openapi.json from the Zod schemas
| File | What's in it |
|---|---|
| DESIGN.md | Design tokens, type, UI building blocks, don'ts — read before UI work |
| PRODUCT.md | Users, brand, product principles |
| CLAUDE.md | Architecture notes: route groups, data layer, auth/token refresh, i18n |
| api-docs.json | vomatt-api contract (what the API serves at /v3/api-docs) |
| docs/release/ | Release checklist, API integration notes, open backend requests |
SESSION_SECRET doesn't match the API's
JWT_SECRET.//api/... — API_URL has a trailing slash or a path.SITE_URL is missing.| No |
| 48 random chars |
Production only. Lets vomatt-admin's Sanity Function expire just the changed Sanity content on publish (POST /api/revalidate); while set, Sanity content refreshes only hourly as a fallback. Same value as the function's SANITY_REVALIDATE_SECRET (npx sanity functions env add invalidate-tags SANITY_REVALIDATE_SECRET <value> in vomatt-admin). Outside Production (local dev, Previews, which the function never calls) edits show up within about a minute, set or not. |
NEXT_PUBLIC_SANITY_API_VERSION | No | 2025-08-08 | Sanity API version; defaults to 2025-08-08. |
NEXT_PUBLIC_ABOUT_URL | No | https://about.vomatt.com | The about site's origin, no trailing slash. /about and the legal pages redirect there, and the app's legal links point there. Defaults to https://about.vomatt.com. |