尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證Daily layers
翻譯準備中
取自儲存庫 README
正在載入完整 README。
檢視完整 README ↗Local places, events, products and communities for everyone. A Houston-first, English/Traditional Chinese discovery platform built around a map, shareable layers and a list fallback. Houston is the launch city; this is not a claim of worldwide coverage, and the feed-extraction geocoder is the US Census geocoder, which resolves US street addresses only. Taiwanese restaurants, groceries, organizations and product sightings remain supported as one set of local content among many.
The product was previously named TaiwanHub. Technical identifiers were deliberately left unchanged for compatibility: the @taiwanhub/* workspace packages, the repository directory, browser storage keys (taiwanhub:map, taiwanhub:map-view), cryptographic purpose strings, database/CI/Docker names, the .invalid demo and test mail domains, and existing environment variables. See docs/plans/locallink-rebrand-implementation-plan.md.
Requirements: Node.js 22, pnpm 10.15+, Docker Desktop with Linux containers.
pnpm install
cp .env.example .env
# Windows PowerShell: Copy-Item .env.example .env
docker compose up -d
pnpm db:migrate
pnpm db:seed
pnpm dev
Open http://localhost:3000. Set AUTH_SECRET in .env to a random value before running a shared environment. Generate one with node -e "console.log(require('crypto').randomBytes(32).toString('hex'))". .env is ignored by Git. Next loads the workspace .env through its config; database tools load it directly.
Membership is invite-only - there is no public sign-up page. Seed users have no passwords and cannot log in, and no default admin or backdoor login is installed. To create your own account in a fresh environment, set MEMBERSHIP_MODE=invite_only in .env, then bootstrap the first invitation:
pnpm membership:bootstrap [email protected]
# follow the one-time link it prints to /join, complete the Email OTP flow, then:
pnpm admin:grant [email protected]
Returning members sign in at /sign-in with an email code by default (password and Google remain for accounts that use them). Only admitted members receive a code, and application privileges require a membership admission, not just a session. Locally, with MAIL_PROVIDER unset, codes and invitations go to an in-memory test transport that nothing displays; read them from the test suites, or configure a real provider for manual testing.
Visit /admin to approve pending events, places, sightings and notes; edit content; hide, reject or soft-delete content; and use /admin/membership to open batches, invite people directly, and grant/revoke reviewer access. Moderator roles can review and edit; only admins can soft-delete. Roles are never accepted from account creation or profile requests. See docs/plans/invitation-membership-implementation-plan.md for how membership works end to end.
apps/web: Next.js 16 App Router, React 19, strict TypeScript, Tailwind 4, responsive custom components, React Hook Form and Zod.packages/database: Drizzle schema, checked-in SQL migrations, PostgreSQL/PostGIS, repeatable demo seeds.packages/shared: domain types, Zod boundaries, authorization and transparent ranking functions. This is the starting point for a future Expo app.src/features/catalog: public data queries and home feed composition.src/features/community: transactional mutations and moderation, separate from HTTP routes.Drizzle keeps PostgreSQL features explicit, including typed PostGIS geometry columns and GiST indexes. Complex capacity and ranking queries use parameterized SQL through its underlying pg pool. There is one database and one ORM, no microservice or second store.
The seed contains 20 places, 10 events, 5 organizations, 10 products, 20 sightings, 15 users and 50 recommendation votes. All catalog content is marked demo. Even real brand or store names in the seed have fictional reports and location details. Never use demo addresses or dates to plan visits. Product images are original local demo illustrations; food/event photos are generic Unsplash imagery, not photos of the named businesses.
Seeds insert deterministic UUIDs and do not erase user data. Running again refreshes the demo product artwork and grocery classifications. Event dates are relative to the first seed run; existing events retain their dates. Use a fresh development database when you want a fresh demo schedule. Never run demo seeds against the production database.
Map-first home with applied layers (system presets, personal and group collections, My saves projection), synchronized results/details and a full list fallback; layer library, creation, curation, follow and reviewed publication; groups with roles and email-bound invitations; local content with explicit location status; guest browsing; invite-only membership (member nominations, reviewer approval, admin batches/direct invites); English/繁體中文; Houston city selection; place and event filtering; bilingual unified search; place recommendation voting and moderated short notes; capacity-safe RSVP/cancellation; saves; organization follows; product sightings; place/event submissions; role-protected moderation with audit history; optional Mapbox; image upload abstraction; persisted analytics; public metadata and sitemap excluding demo listings.
See .env.example for the complete list.
| Variable | Purpose |
|---|---|
DATABASE_URL | PostgreSQL connection, PostGIS required |
AUTH_SECRET | Random secret, 32+ characters |
BETTER_AUTH_URL, NEXT_PUBLIC_APP_URL | Exact public origin; localhost for development |
GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET | Optional Google OAuth; both required to enable |
NEXT_PUBLIC_MAPBOX_TOKEN | Optional restricted public Mapbox token |
IMAGE_STORAGE_PROVIDER | local for dev or s3 for AWS S3/R2/S3-compatible storage |
IMAGE_STORAGE_* | Bucket, public base URL, endpoint, region and server-only credentials |
ANALYTICS_CONSOLE | Log event names/properties locally; DB events always persisted |
FEATURE_PRODUCTS, FEATURE_ORGANIZATIONS, FEATURE_SUBMISSIONS | Set false to disable optional feature entry points |
FEATURE_MAP_HOME, FEATURE_LAYER_WRITES, FEATURE_CONTENT | Map-first home (false restores the legacy entry screen), layer/group writes, local content |
MEMBERSHIP_MODE | closed (default; no new accounts) or invite_only to enable the invitation flow |
MEMBERSHIP_ISSUANCE_PAUSED | true pauses new approvals/invites without affecting already-issued ones |
MAIL_PROVIDER, MAIL_FROM, RESEND_API_KEY | resend with a verified sender and key; unset uses the local test transport (dev/CI only) |
MAIL_OUTBOX_ENCRYPTION_KEY | 32 random bytes, base64-encoded, encrypting queued invitation email payloads at rest |
TRUSTED_CLIENT_IP_HEADER | Platform client-IP header for per-IP sign-in limits (Vercel: x-vercel-forwarded-for) |
MAIL_CAPTURE_DIR | Browser tests only (); set by |
Mapbox/Google/S3 are optional for local startup. Missing Mapbox displays a clear fallback with external directions. Local uploads support JPEG, PNG and WebP up to 5 MB. Production must configure object storage to upload files; HTTPS image URLs can also be submitted. Untrusted image URLs are rendered directly, not fetched by the server image proxy.
pnpm lint
pnpm typecheck
pnpm test
pnpm test:integration # requires migrated and seeded Postgres
pnpm exec playwright install chromium
pnpm test:e2e # starts its own dev server (E2E_REUSE_SERVER=true to reuse one)
pnpm build
pnpm start
Browser tests set their own origin (BETTER_AUTH_URL/NEXT_PUBLIC_APP_URL = http://localhost:$E2E_PORT) and membership/mail settings (invite-only mode, a fresh per-run capture mail directory under the OS temp directory removed by the global teardown, a test-only outbox key) for both the dev server and the test process, so local .env values never decide the result. Because a running server can't be shown to have those settings, an existing server is reused only when E2E_REUSE_SERVER=true and you started it with the same values.
Use a dedicated database for integration/browser tests. Database tests clean up their own fixtures; browser tests intentionally leave contribution fixtures for inspection and may change scores. E2E captures desktop/mobile screenshots under ignored test-results/. CI provides a fresh PostGIS service, caches pnpm dependencies and runs every check above. Do not run dev and start on the same port. Stop dev before testing production startup.
Target Vercel plus Neon PostgreSQL with PostGIS enabled; any compatible PostgreSQL/PostGIS provider also works. Run migrations with an appropriately privileged direct database connection before deploying. Never migrate on every request. Set the Vercel project root to apps/web, enable access to workspace files outside that directory, use pnpm install --frozen-lockfile at workspace root and pnpm --filter @taiwanhub/web build. See deployment instructions.
Docs are grouped by scope: docs/reference/ (stable contracts and specs), docs/guides/ (how-to and architecture-direction guides), docs/plans/ (implementation plans and design proposals), docs/adr/ (accepted architecture decisions), and docs/design/ (design system assets).
MAIL_PROVIDER=captureplaywright.config.ts