尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證製作者的介紹僅有上面的一句話。 更多詳情請閱讀儲存庫 README。 儲存庫 README ↗
正在載入完整 README。
檢視完整 README ↗Find your artists. Catch the moment. Go together.
A mobile-first music and concert discovery app. It imports your top artists from Spotify, lets you curate a fan list, watches official ticket providers for concerts and sale dates near you, and lets accepted friends see shared artists and coordinate plans.
This repository implements the Phase 1 foundation of the product spec (v0.2), with the social data model, access control and core social flows already in place.
| Layer | Choice |
|---|---|
| App | Next.js 16 (App Router, TypeScript, Server Actions), Tailwind CSS v4 |
| Data | Supabase Postgres with row-level security on every table |
| Auth | Supabase Auth with Spotify OAuth (authorisation code + PKCE) |
| Jobs | Daily Vercel cron for data, pg_cron every 5 min for reminder delivery, or pnpm worker locally; all state in Postgres |
| Tests | Vitest (unit) and pgTAP (database access control and pipelines) |
Prerequisites: Node 22+, pnpm 10, Docker (for local Supabase).
pnpm install
cp .env.example .env.local # then fill in the values (see below)
pnpm db:start # local Postgres, Auth and API in Docker; applies migrations
pnpm dev # http://127.0.0.1:3000
pnpm worker # background jobs, in a second terminal
pnpm exec supabase status prints the local PUBLISHABLE_KEY and SECRET_KEY for .env.local.
http://127.0.0.1:54321/auth/v1/callback. Spotify rejects localhost.SPOTIFY_CLIENT_ID and SPOTIFY_CLIENT_SECRET in .env.local, and export them (or place them in supabase/.env) before pnpm db:start, because supabase/config.toml reads them for the Auth provider.Set TICKETMASTER_API_KEY. Without it, the event jobs log skipped and the app shows explicit "no concerts yet" states. Nothing is faked.
pnpm dev:fixtures (local Supabase only; it refuses any other URL) creates two test accounts and pushes Ticketmaster-shaped payloads through the real pipeline: normaliser → ingest_provider_event → notification scheduling → delivery. Use it to exercise every screen. It is never imported by the app.
| Script | Purpose |
|---|---|
pnpm dev / build / start | Next.js |
pnpm check | Typecheck + lint + unit tests |
pnpm test | Vitest unit tests |
pnpm db:test | pgTAP: RLS, privilege and pipeline tests against the local database |
pnpm db:reset | Recreate the local database from migrations |
pnpm db:types | Regenerate src/lib/database.types.ts from the local schema |
pnpm worker / worker:once | Run background jobs continuously or once (--job=<name> for one job, e.g. --job=concert-search) |
src/
app/ routes (App Router)
(app)/ authenticated screens: discover, artists, friends, saved, profile, events, notifications
onboarding/ five-step first-run flow
auth/callback/ OAuth callback: stores encrypted Spotify tokens server-side
api/cron/[job]/ authenticated hosted-cron entry point
components/ shared design-system components
lib/ pure, client-safe logic: events, ranking, formatting, env, types
server/
actions/ server actions (session re-verified, input validated with zod)
data/ per-user read models (RLS-scoped queries)
providers/ provider adapter contract, Ticketmaster client + normaliser, artist matching
spotify/ Spotify API client and sync
notifications/ pure delivery decision, delivery, Web Push
jobs/ job registry and runner (logs to job_runs)
supabase/
migrations/ schema, RLS/grants/social RPCs, pipelines, reference cities
tests/database/ pgTAP tests
scripts/ worker and local fixtures
tests/ unit tests
security definer SQL functions that only service_role may execute. They are transactional and idempotent, so retries never duplicate artists, events or notifications.events.lifecycle_status holds the provider status. Sale phases live in event_sales and ticket categories in ticket_offers. The UI derives "presale scheduled", "on sale" and so on (src/lib/events.ts).decide() re-checks event status, sale time, preferences, friendship and quiet hours. Stale reminders are cancelled when an event changes. Time-critical alerts during quiet hours go to the inbox without push; others are deferred.security definer RPC that checks friendship, visibility and blocks. Blocking overrides everything. Tokens are AES-256-GCM encrypted, and their columns are not even selectable by the owner's client.src/lib/ranking.ts is rules-based, and every point carries a human-readable reason. Cancelled, postponed and stale events always sink. Affiliate status is not an input.text-muted is lightened from #897C7D to #9C8E8F: the original measured 4.16:1 on surface and 3.54:1 on surface-elevated, below WCAG AA. A border-strong token (#857579) is added for 3:1 control boundaries. tests/foundation.test.ts verifies every pair.users.home_location_id → user_locations → reference cities. No precise coordinates are ever stored for users.user_artists.is_dismissed instead of deleting the row, so the weekly Spotify import does not silently re-add it. The canonical artist is untouched.supabase link and supabase db push, and enable the Spotify provider in Auth (same client ID/secret; add the hosted callback URL in Spotify)..env.example. Set NEXT_PUBLIC_APP_URL to the public URL and add <app-url>/auth/callback to Supabase Auth → URL configuration → redirect URLs.vercel.json runs /api/cron/daily at 05:00 UTC. It runs the weekly Spotify sync for accounts that are due, matches new artists on Ticketmaster, refreshes concert lists (one request per artist), delivers anything already due and cleans up, all within a 270 s budget. Work that doesn't fit is picked up the next day, oldest first.after(), so new users don't wait for the next daily run.pg_cron + pg_net) calls /api/cron/notifications-deliver, which only delivers reminders that were already scheduled. It needs two Vault secrets, set once in the Supabase SQL editor:
select vault.create_secret('https://<your-app>.vercel.app', 'encore_app_url');
select vault.create_secret('<same value as CRON_SECRET>', 'encore_cron_secret');
Without them the trigger does nothing (local development uses pnpm worker instead).pnpm worker on any Node host.Jobs are safe to run concurrently or retry: claims use FOR UPDATE SKIP LOCKED leases and every write is idempotent. Failed runs are recorded in job_runs.
Ticket availability (sold out / few left): the Discovery API has no such field, and Ticketmaster's Inventory Status API rejects standard keys ("Invalid ApiKey for given resource"). It needs partner access from Ticketmaster; Encore only shows the provider's "off sale" status until then.
Spotify and Ticketmaster against live APIs. Both integrations are implemented against the documented APIs, but no real credentials were available while building, so neither has been exercised live. Spotify development-mode and quota restrictions change over time; check the current Web API policies before launch.
Song previews come from Deezer's public API (popular tracks + 30-second previews), because Spotify no longer offers top tracks or preview URLs to development-mode apps. Audio streams from Deezer and is never stored. Review Deezer's API terms for commercial use before a public launch.
Phase 2/3 items: companion invitations ("Ask to join"), reporting, artist swipe discovery, EVENTIM adapter, affiliate links.
Legal: /legal holds draft product notes and must be replaced by a reviewed privacy policy, imprint and terms before public launch.
Native push reliability differs per platform: iOS supports Web Push only for installed PWAs. The in-app inbox is always the source of truth.