feat(dashboard): Task 1E — Mobile-first review dashboard with approve/reject, config panel, pipeline status#666
Conversation
Task 1E deliverables: - Review Queue page (mobile-first card layout, quality badges) - Review Detail page (inline editing, approve/reject with touch-friendly buttons) - Review server actions (approve, reject, updateVideoField with field whitelist) - Config Panel page with grouped form sections (6 fieldsets) - Config Form client component (save to API, success toast) - Config API route (GET/PUT with fail-closed auth, field whitelist validation) - Pipeline Status page (12 status count cards, active workflows, recent completions) - Review API route (POST approve/reject with fail-closed auth, CF Workflow webhook) - Updated sidebar navigation with Review Queue, Pipeline, Config links Co-authored-by: dashboard <dashboard@miriad.systems>
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
Sorry, something went wrong.
codercatdev
left a comment
There was a problem hiding this comment.
PR #666 Review — Task 1E: Review Dashboard
Verdict: 🔴 REQUEST_CHANGES
🚨 BLOCKER: Server Actions Missing Auth (1 issue, 3 functions affected)
app/(dashboard)/dashboard/review/actions.ts — The three server actions (approveVideo, rejectVideo, updateVideoField) have zero auth checks. Server actions are exposed as POST endpoints by Next.js and can be called by anyone without authentication.
This is the #1 recurring issue on this project (caught 4+ times before). The API routes (/api/dashboard/config and /api/dashboard/review) correctly implement requireAuth() with fail-closed behavior, but the server actions bypass all of that.
Fix: Add requireAuth() at the top of each server action, identical to the pattern used in the API routes.
What's Good ✅
- API route auth is correct — Both
/api/dashboard/configand/api/dashboard/reviewuserequireAuth()that returns 503 when Supabase env vars are missing and 401 when user is not authenticated. Fail-closed. ✅ - Field whitelists — Config PUT has 28 allowed fields via
Set. Server actions haveALLOWED_FIELDSforupdateVideoField. Review API validatesactiontoapprove/rejectonly. ✅ getEngineConfig()integration — Correctly uses the new config API from PR #665, includinginvalidateEngineConfig()after writes. ✅- GROQ queries are mostly bounded — Pipeline page uses
[0..19]and[0..9]. Review detail uses[0]for single doc. ✅ - Mobile-first design — 44px min-height on all interactive buttons, responsive grids (
grid-cols-2 sm:grid-cols-3 md:grid-cols-4),flex-col gap-3 sm:flex-rowfor approve/reject. ✅ - CF Workflow webhook — Fire-and-forget with error logging, doesn't block approval. ✅
force-dynamicon all pages. ✅"use client"only on interactive components (config-form, review-detail-client). ✅- Sidebar navigation — Clean additions with appropriate icons. ✅
- rateCardTiers — Properly adds
_keyand_typefor Sanity array items. ✅
Issues to Fix 🔶
| # | Severity | File | Issue |
|---|---|---|---|
| 1 | 🚨 Blocker | review/actions.ts |
Server actions have NO auth — anyone can approve/reject/edit videos |
| 2 | 🔶 Medium | review/page.tsx |
Unbounded GROQ query (no [0..N] limit) |
| 3 | 🟡 Minor | config/config-form.tsx:371 |
as any type cast on toggle |
| 4 | 🟡 Minor | review/[id]/page.tsx:49 |
video as any bypasses type safety |
| 5 | 🟡 Minor | api/dashboard/config/route.ts:140 |
as any[] on rateCardTiers |
| 6 | 🟡 Minor | api/dashboard/config/route.ts |
notificationEmails, string fields not type-validated |
Architecture Notes
- No middleware.ts exists — Dashboard route protection relies entirely on per-route auth checks. This is acceptable as long as every route/action checks auth, but a middleware would provide defense-in-depth. Consider adding one in a follow-up.
- Dashboard layout has a comment saying "the proxy will have already redirected to login for protected routes" but no proxy/middleware exists. The layout itself doesn't block unauthenticated access — it just renders without the sidebar. This means unauthenticated users can see page content if the page itself doesn't check auth. The server component pages use
dashboardQuerywhich returns empty results without a Sanity token, so data leakage is limited, but the server actions are the real risk.
Summary
The API routes are well-secured. The UI is excellent — mobile-first, good UX, proper component architecture. But the server actions are a critical auth gap. Fix the auth on actions.ts and add a limit to the review queue query, then this is ready to merge.
Sorry, something went wrong.
- Add requireAuth() to all 3 server actions (approveVideo, rejectVideo, updateVideoField) - Fail closed: throws if Supabase not configured or user not authenticated - Use actual user email for reviewedBy instead of hardcoded string - Add [0..49] limit to review queue GROQ query Co-authored-by: dashboard <dashboard@miriad.systems>
Task 1E: Review Dashboard — Mobile-First Control Center
Spec:
41bSaBJF(Content Engine v2)Task:
i9AiY0S7Branch:
feat/review-dashboard10 files, +1,686 lines
What's Built
Review Queue (
/dashboard/review)pending_reviewvideosReview Detail (
/dashboard/review/[id])Config Panel (
/dashboard/config)Pipeline Status (
/dashboard/pipeline)API Routes
POST /api/dashboard/review— approve/reject with CF Workflow webhookGET/PUT /api/dashboard/config— read/write engineConfigServer Actions
approveVideo(id)— patches status + reviewedAt/reviewedByrejectVideo(id, reason)— patches status + flaggedReasonupdateVideoField(id, field, value)— whitelist: title, script.hook, script.ctaAuth & Security
_keyfor Sanity arraysPatterns
force-dynamicon all pages"use client"only on interactive componentsDepends On