The Missing Pieces
Yesterday's copyright management work gave Now I Get It! the backend for handling DMCA takedown requests -- a form for reporters, a DynamoDB table for tracking, and Lambda functions for admin actions. But the only way to actually review and act on requests was through the AWS Console or raw API calls with curl. And once a reporter submitted a request, they had no way to check what happened. Two gaps, two sides of the same coin.
The Admin Dashboard
The dashboard is a single HTML file with three views that swap via JavaScript -- consistent with the no-build-tools approach across the rest of the frontend. Same design system too: dark theme, glass cards, amber accents.
The first view is an auth gate. Enter the admin token, and it validates against the same Secrets Manager-backed header the API uses. Valid tokens get stored in localStorage so you don't have to re-enter them on every visit.
The dashboard view shows four stat cards (pending, approved, rejected, restored counts) and a filterable list of requests. Each card shows the claimant, work type, submission date, and a link to the reported page. Click a card to see the full detail view -- everything from the legal declarations to the electronic signature, plus metadata like source IP and whether the submission was flagged by the input sanitizer.
The most useful addition is the comment timeline. Every request has a chronological comment history. Admin comments are highlighted in amber, system comments in gray. Approve, reject, and restore actions auto-append system comments -- "Takedown approved. Notes: ..." -- so the timeline tells the full story without anyone having to manually document what happened. Comments are stored as a list attribute on the DynamoDB record using list_append with an if_not_exists guard, so it works safely on records that predate the feature.
The Reporter Status Page
The other side of the loop: letting reporters check what happened. A new public API endpoint returns only public-safe fields -- the reporter's own data (name, work type, description, document URL) plus status information (current status, dates, admin notes from the resolution action). No source IPs, no internal comments, no flagged status. The Lambda uses a whitelist approach: it explicitly picks fields from a PUBLIC_FIELDS set rather than stripping fields from the full record. That way, new fields added to takedown records in the future won't accidentally leak to the public.
The UUID request ID serves as the access control -- 128 bits of entropy makes brute-forcing impractical, and rate limiting adds defense in depth. The status page URL goes into the Postmark confirmation email, so reporters get it automatically when they submit.
The page itself shows a color-coded status badge (amber pulse for pending, green for approved, red for rejected, blue for restored), the original report details, a timeline of key dates, and any admin notes. A refresh button lets reporters manually re-check.
The Deploy Script Pattern
Both features hit the same deploy script issue: the scripts explicitly list every file in three separate places -- the zip packaging step, the Lambda update loop, and the S3 upload step. Miss any of the three and CloudFormation creates a Lambda pointing at a handler that doesn't exist in the zip, or an HTML page never makes it to S3. The status page required three incremental fixes before everything landed. A glob-based approach would be less error-prone, but the explicit listing does serve as a kind of documentation.
Lessons
- Whitelist beats blacklist for public APIs. Explicitly listing safe fields is safer than stripping dangerous ones. New fields default to private.
- Broad exception handlers mask real errors. During admin dashboard testing, the S3 backup operation silently failed because
head_objectthrewAccessDenied(missing IAM permission), but the broadexcept ClientErrorhandler caught it as "object doesn't exist." The DynamoDB updates succeeded, so the record said "approved" but the page stayed live. - Deploy scripts need the same attention as application code. When you explicitly list files in three places, forgetting one gives you a deployment that looks successful but isn't.