feat: moderator-scoped admin access + fix push notifications not displaying (v0.66.0)
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>
This commit is contained in:
+4
-4
@@ -88,10 +88,10 @@ Status legend: **Exists** (fully working) · **Partial** (works but with a real
|
||||
| Trending feed / explore | Exists | Favorite-count driven, no editorial curation, **no separate "featured" surface** | `apps/web/app/api/v1/feed/trending` |
|
||||
| Notification categories | Exists | 8 categories, independent push + email toggle each, plus email-only weekly digest | `packages/db/src/schema/users.ts` (`userNotificationPrefs`) |
|
||||
| Push notifications | Exists | Web Push + VAPID, end-to-end, real trigger call sites (not just plumbing) | `apps/web/lib/push.ts` |
|
||||
| Push click-through handling | **Missing** | No `notificationclick` listener in the service worker — the `url` field encoded in push payloads isn't consumed client-side | `apps/web/public/sw.js` |
|
||||
| Push display + click-through | Exists | `sw.js` previously had no `push` listener at all — push messages never displayed. Now has both `push` (calls `showNotification`) and `notificationclick` (focuses an existing tab on the payload's `url`, or opens one) | `apps/web/public/sw.js` |
|
||||
| Email notifications + weekly digest cron | Exists | | `apps/web/lib/notifications.ts`, `apps/web/app/api/internal/cron/weekly-digest` |
|
||||
| Reports/moderation | Exists | Report → admin webhook → admin queue → resolve (reviewed/dismissed) + audit log | `apps/web/app/api/v1/reports`, `apps/web/app/api/v1/admin/reports` |
|
||||
| Moderator role | Partial | Enum value exists (`user`/`moderator`/`admin`) but no moderator-scoped route was found — report review currently requires full admin | `packages/db/src/schema/users.ts` |
|
||||
| Moderator role | Exists | Moderators get `/admin/reports` (view/resolve) and `/admin/recipes` (view + unpublish takedown); every other `/admin/*` page redirects them out via `requireFullAdminPage()`. Comment deletion already allowed moderator before this. | `apps/web/lib/require-admin-page.ts`, `apps/web/app/admin/layout.tsx`, `apps/web/app/api/v1/admin/recipes/[id]/route.ts` |
|
||||
|
||||
---
|
||||
|
||||
@@ -145,11 +145,11 @@ Ranked roughly by likely value:
|
||||
3. **Nutrition trend/history view** — diary is single-day only, no multi-day chart.
|
||||
4. **USDA/nutrition-database lookup** — all nutrition numbers are AI-estimated; barcode scan only gets product name, not nutrition facts.
|
||||
5. **OS Share Target** — can't share a recipe link into Epicure from another app's share sheet.
|
||||
6. **Push click-through handling** in the service worker — payload carries a `url`, nothing consumes it.
|
||||
6. ~~Push click-through handling~~ — closed 2026-07-21 (`push`/`notificationclick` listeners added to `sw.js`).
|
||||
7. **Anonymous public link for meal plans** — shopping lists have this, meal plans don't.
|
||||
8. **Grocery delivery/live pricing** beyond the Instacart stub (which needs a partnership agreement to go live).
|
||||
9. **Offline coverage for shopping lists / meal plan** — currently recipe-viewing + mark-cooked only.
|
||||
10. **Moderator-scoped admin routes** — role exists in the schema, not wired to any route.
|
||||
10. ~~Moderator-scoped admin routes~~ — closed 2026-07-21 (reports + recipe-unpublish).
|
||||
|
||||
## Deliberate design choices (not gaps)
|
||||
- Direct messaging is 1:1 only — enforced at the schema level, not a missing feature.
|
||||
|
||||
Reference in New Issue
Block a user