mirror of
https://github.com/hanzo-apps/waitlist.git
synced 2026-10-30 14:48:32 +00:00
Drops the Next.js + Upstash Redis stack. The viral waitlist now ships as a brand-neutral React widget against a Hanzo Base Go plugin. What changed - packages/widget/: new @hanzo/waitlist — React component, framework-free fetch client, drop-in <hanzo-waitlist> custom element. Zero brand colors, zero logos, zero brand names. Consumer brings logo via `logo` prop or `<slot name="logo">`. Theming via --hw-* CSS variables with fallback chain at usage so ancestor selectors win cleanly. - apps/web/: Next.js demo rebuilt monochrome. Five generic presets (Neutral, Preset A/B/C/D) prove brand swap works without per-brand builds. Demo's placeholder logo lives in the demo, not the package. - apps/api/: removed. Backend logic now lives in ~/work/hanzo/base/plugins/waitlist (committed separately). - packages/sdk/: removed (Preact + Vite). Replaced by packages/widget. - docs/: collapsed to architecture / api / widget. Old planning docs moved to docs/archive/. - Latest deps: Next 16, React 19.2, Vite 8, TS 5.9, vite-plugin-dts 5, @vitejs/plugin-react 6, eslint-config-next 16. Build output - ESM 6.76kB, CJS 5.22kB, IIFE 196.99kB / 62.65kB gzip - Widget styles: 3.89kB / 1.18kB gzip - Demo first-load: 106kB
3.1 KiB
3.1 KiB
CLAUDE.md
本文件为 Claude Code (claude.ai/code) 在此代码库中工作时提供指导。
项目概述
Hanzo Waitlist 是单色、品牌中立的等待名单组件。包内零品牌色、 零 logo、零身份标识 —— 每个使用方自行实现品牌层(变量覆盖)。
- 前端: React 组件 + 原生 fetch 客户端 + Web Component 三种入口,
以
@hanzo/waitlist名义发布,位于packages/widget/。 - 后端: Hanzo Base 插件,位于
~/work/hanzo/base/plugins/waitlist/。 不依赖 Redis、Upstash 或任何第三方 SaaS。 - 演示站点:
apps/web/(Next.js)。
架构原则
- 一种做法: 后端逻辑全部集中在一个 Go 插件里。前端只有一个组件
包,既输出 React、又输出 vanilla 客户端、又输出
<hanzo-waitlist>自定义元素 —— 同一份源码,三个出口。 - 单色: 组件内部不写任何品牌色。所有颜色都派生自
--hw-*CSS 变量,默认走中性色。任何品牌想要自定义,覆盖几个变量即可。 - 原子性: 推荐计数 / 邀请码分配 / 条目持久化全部在 SQL 事务里
完成(
app.RunInTransaction),并发竞争由 SQLite 解决,不需要 Redis Lua 脚本。
仓库结构
packages/widget/ @hanzo/waitlist 源码
apps/web/ 演示站点(Next.js + React 19)
docs/ architecture / api / widget 三份文档
docs/archive/ 历史规划文档(已归档)
服务端在另一个仓库:~/work/hanzo/base/plugins/waitlist/。
数据模型(Base 集合)
| 集合 | 字段 |
|---|---|
waitlists |
id, slug(唯一), name, createdAt, updatedAt |
waitlist_entries |
id, waitlist→relation, email, refCode, referredBy, referralCount, createdAt |
索引:UNIQUE(waitlist,email)、UNIQUE(waitlist,refCode)、
(waitlist, referralCount DESC, createdAt ASC)。
API 端点
POST /v1/waitlist/join注册条目,可带referrerCode与 Turnstile tokenGET /v1/waitlist/status?waitlist=&email=查询排名GET /v1/waitlist/export?waitlist=管理员导出 CSV(超级用户或共享密钥)
防滥用
- Cloudflare Turnstile 验证(可选,留空则跳过)
- 进程内滑动窗口限流(默认 5 次/IP/小时)
- 一次性邮箱域名拦截
环境变量
演示站点(apps/web/.env.local):
NEXT_PUBLIC_BASE_URL指向 Base 实例NEXT_PUBLIC_WAITLIST_SLUG默认demo
Base 服务端:
TURNSTILE_SECRET_KEY(可选)WAITLIST_ADMIN_SECRET(可选,启用共享密钥导出)
开发命令
pnpm install
pnpm --filter @hanzo/waitlist dev # 组件库 Vite dev
pnpm --filter @hanzo/waitlist typecheck # tsc 校验
pnpm --filter @hanzo/waitlist build # 输出 dist/
pnpm --filter @hanzo/waitlist-demo dev # Next.js 演示站点
不要做的事
- 不要重新引入 Redis / Upstash。Base 即数据库,SQL 事务即原子性。
- 不要在组件源码里写任何具体颜色值;品牌色全部走 CSS 变量。
- 不要为每个品牌写单独的组件包;参数化即可。
- 不要在组件里挂载 Turnstile —— 站点宿主负责挂载,组件只接 token。