Moderator role existed in the schema and was already respected by
comment deletion, but every admin page/route treated moderator
identically to a regular user (403/redirect). Wires it up narrowly:
admin/layout.tsx now lets admin+moderator through and filters the
nav by role, while every admin-only page (users, tiers, settings,
webhooks, insights, etc.) explicitly redirects moderators away via a
new requireFullAdminPage() helper -- the nav filter is UX, this is
the actual gate. Moderators land on Reports and Recipes: reports
GET/PATCH now accept requireAdmin({allowModerator: true}), and a new
PATCH /api/v1/admin/recipes/[id] lets admin+moderator unpublish a
public recipe (flip to private) as a takedown action, audit-logged.
Also found and fixed a real bug while auditing the PWA push pipeline
for a "push click-through" gap: public/sw.js had no `push` event
listener at all, so incoming push messages never displayed anything
-- push was silently non-functional end-to-end despite the
subscribe/send plumbing all working. Added the push listener
(showNotification) and a notificationclick listener that focuses an
existing tab or opens one at the payload's url.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Recipe count and storage usage shared the monthly user_usage bucket with
AI calls, so both incorrectly reset every month even though nothing was
deleted. Only AI calls should be monthly.
Recipe count and storage are now derived live from real data (recipes,
recipe/review photos, avatar) instead of a counter — deleting a photo or
recipe is itself the "decrement", no extra wiring needed. Storage size is
tracked per-row (recipePhotos.sizeMb, ratings.photoSizeMb, users.avatarSizeMb)
and threaded through presign -> upload -> save.
Also fixes avatar removal silently no-oping (client sent a field the PATCH
schema didn't recognize).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Signups toggle was inverted (on = disabled) — flipped so on means
open, matching how every other on/off toggle in the app reads.
- Moved AI provider keys and default-model settings from Site
Settings to AI Config, so all AI setup lives in one place instead
of split across two pages with a cross-link.
- Admin overview: added new users/recipes (7d), recipes cooked (7d),
pending reports (linked, highlighted if > 0), storage used this
month, active webhooks, and API keys issued — previously just 4
lifetime totals.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Bumps version to 0.2.0 (root + apps/web package.json) and adds
CHANGELOG.md as the canonical human-readable history, mirrored by
apps/web/lib/changelog.ts as the in-app data source (single
ChangelogList component renders both).
- /changelog: user-facing page, linked from the account dropdown.
- /admin/changelog: same list, plus a "Version" card on the admin
overview dashboard linking to it.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Root layout used title.template ("%s | Epicure") and 38 pages set
their own title, producing "Recipes | Epicure", "Messages — Epicure",
etc. Drop the template, set a flat "Epicure" default, and clear every
page's title override so they all inherit it.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
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).