Webhooks, self-serve API keys, and BYOK AI provider keys had zero
access gating -- any logged-in user, any tier. Adds users.isDeveloper
(boolean, admin-toggled in admin/users/[id] alongside role/tier),
checked via a single hasDeveloperAccess() (lib/permissions.ts) so a
future subscription-tier auto-grant is a one-line change there, not
a redesign across call sites.
requireDeveloper() (lib/api-auth.ts) wraps requireSession() with a
fresh isDeveloper check (same reasoning as requireAdmin re-querying
role: session.user's cookieCache can be up to 5 minutes stale) and
replaces requireSession in all 8 gated routes: webhooks CRUD +
deliveries + redeliver, api-keys CRUD, ai-keys CRUD.
Settings UI: the sidebar hides API Keys/Webhooks nav entries for
non-developers; those pages and the BYOK section of Settings -> AI
show a locked notice instead of the manager component when accessed
directly.
Migration grandfathers in anyone who already has a webhook, API key,
or BYOK key row -- ships as a new gate on existing features, not a
silent lockout of active integrations.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
All literal "team" tier-value references renamed to "family" across
API routes, admin UI, OpenAPI schemas, and lib/tiers.ts. The DB enum
value itself is renamed in place via ALTER TYPE ... RENAME VALUE
(migration 0044) rather than drizzle-kit's auto-generated
drop-and-recreate-the-enum migration, which would have failed against
any existing row still holding 'team' — RENAME VALUE preserves
existing data with no cast/backfill needed.
Also adds STRIPE_PLAN.md — a full Stripe billing integration plan
(Checkout+Portal, tier→Price mapping, admin billing dashboard, and a
multi-user Family-group design since Family is meant to cover several
accounts under one subscription, not one payer). Planning only, no
Stripe code yet.
v0.47.0
Widens the tier enum from free/pro to free/pro/team and every
"free" | "pro" cast that assumed exactly two tiers (~30 call sites:
every AI route's withAiQuota/checkAndIncrementTierLimit call, admin
user/invite management, upload quota checks, OpenAPI schemas). Team
sits above Pro with genuinely unlimited recipes/public-recipes (the
-1 sentinel, which Pro doesn't actually use — Pro uses large finite
numbers instead) and a higher AI-call/storage cap. Seeded via
db:seed, editable afterward from Admin > Tiers.
role (user/moderator/admin — permissions) and tier (free/pro/team —
billing limits) stay separate concepts, as they already were; this
does not touch role-based permissions.
Requires migration 0043 to run against a live DB — not applied in
this sandbox (no Docker here); run `pnpm db:migrate` then `pnpm db:seed`.
v0.44.0
Sticky sidebar with back-to-app link. Overview: user counts, recipe photos, AI calls/month.
User management with role/tier editing. Audit log (all admin actions).
Storage page with photo breakdown by tier. AI config showing DB vs .env key source.
Site settings page to override .env values at runtime (encrypted in DB).