- Analytics: <Analytics /> in root layout loads umami when NEXT_PUBLIC_UMAMI_* env are set - Subscription: free/pro tiers in Postgres, daily quotas (10msg/5tool free, 1000/500 pro) - Chat route: checkAndConsume(chat_message) gate before streamText, 429 when exhausted - Tool metering: getWeather wrapped with withLimit decorator that deducts tool_call quota - Pricing page at /pricing with upgrade button - /api/billing/checkout posts to EGBE payment gateway, /api/billing/webhook verifies HMAC and upgrades user on checkout.session.completed - UsageBadge in chat header polls /api/usage every 15s - Dropped now-unused entitlements.ts
70 lines
3.2 KiB
Markdown
70 lines
3.2 KiB
Markdown
# EGBE Chatbot Template
|
|
|
|
Next.js chatbot wired to the EGBE infra. Based on [vercel/chatbot](https://github.com/vercel/chatbot) (Apache 2.0).
|
|
|
|
## What's wired
|
|
|
|
- **AI** via `@ai-sdk/openai-compatible` → `EGBE_AI_API_URL` (LiteLLM proxy)
|
|
- **Auth** — NextAuth credentials + guest mode
|
|
- **DB** — Drizzle ORM + Postgres (provisioned by app-deploy.sh)
|
|
- **Analytics** — Umami `<Script>` mounted when `NEXT_PUBLIC_UMAMI_*` env are set
|
|
- **Subscriptions** — Free / Pro tiers, daily quotas, tool-call metering, real Stripe checkout via the EGBE payment gateway
|
|
|
|
## Models (via LiteLLM)
|
|
|
|
`gpt-5.4-mini` (default), `gpt-5.4`, `claude-sonnet`, `claude-haiku`, `kimi-k2.6`, `minimax-m2.7`, `qwen3-coder`. Edit `lib/ai/models.ts`.
|
|
|
|
## Subscription model
|
|
|
|
Two tiers (see `lib/subscription/config.ts`):
|
|
|
|
| Tier | Messages / day | Tool calls / day |
|
|
|------|----------------|------------------|
|
|
| Free | 10 | 5 |
|
|
| Pro | 1000 | 500 |
|
|
|
|
- **Message limit** — enforced in `app/(chat)/api/chat/route.ts` via `checkAndConsume(userId, "chat_message")` before `streamText`.
|
|
- **Tool limit** — `getWeather` is wrapped with `withLimit(tool, "tool_call", userId)` (see `lib/ai/tools/with-limit.ts`). Every tool call deducts 1 unit; if quota is hit the tool returns `{ error: "limit_exceeded", ... }` which the model sees and explains to the user.
|
|
- **Quota badge** — `<UsageBadge />` in the chat header polls `/api/usage` every 15s.
|
|
- **Upgrade flow** — `/pricing` → POST `/api/billing/checkout` → redirect to Stripe → webhook at `/api/billing/webhook` flips the user's tier on `checkout.session.completed`.
|
|
|
|
To add a new tool with metering, just wrap it the same way:
|
|
|
|
```ts
|
|
const meteredFoo = withLimit(myFooTool, "tool_call", session.user.id);
|
|
```
|
|
|
|
To add a new limit kind, edit `lib/subscription/config.ts` (`LimitKey`, `LIMITS`) and `lib/subscription/service.ts`.
|
|
|
|
## Deploy (from inside an OpenClaw instance)
|
|
|
|
```bash
|
|
# 1. provision Umami site (optional — chatbot works without analytics)
|
|
${SKILLS_DIR}/../analytics/scripts/analytics-site-create.sh chatbot chatbot.${EGBE_INSTANCE_PUBLIC_ID}.p.egbe.app "Chatbot"
|
|
|
|
# 2. (optional) create a Stripe product + price for the Pro plan
|
|
# Without PRO_STRIPE_PRICE_ID the upgrade button returns 501.
|
|
curl -s -H "Authorization: Bearer ${EGBE_GATEWAYS_TOKEN}" -H "Content-Type: application/json" \
|
|
-d '{"name":"Chatbot Pro"}' "${EGBE_PAYMENT_GATEWAY_URL}/v1/products"
|
|
# then create a price referencing the returned product id, copy the price id
|
|
|
|
# 3. deploy
|
|
APP_ENV_JSON='{"AUTH_SECRET":"'$(openssl rand -base64 32)'","PRO_STRIPE_PRICE_ID":"price_..."}' \
|
|
${SKILLS_DIR}/scripts/app-deploy.sh chatbot chatbot-template
|
|
```
|
|
|
|
`app-deploy.sh` auto-injects: `DATABASE_URL`, `EGBE_AI_API_*`, `NEXT_PUBLIC_BASE_URL`, `NEXT_PUBLIC_UMAMI_*`, `EGBE_PAYMENT_GATEWAY_*`, `EGBE_GATEWAYS_TOKEN`. You pass `AUTH_SECRET` and `PRO_STRIPE_PRICE_ID` (if billing is desired) via `APP_ENV_JSON`.
|
|
|
|
## Local dev
|
|
|
|
```bash
|
|
cp .env.example .env.local
|
|
# fill in DATABASE_URL, EGBE_AI_API_KEY, AUTH_SECRET
|
|
pnpm install
|
|
pnpm db:migrate
|
|
pnpm dev
|
|
```
|
|
|
|
## License
|
|
|
|
Apache 2.0 — derivative work of Vercel, Inc.'s [chatbot](https://github.com/vercel/chatbot).
|