Skip to content

Roadmap buildout: rate limiting, history, drag-drop, onboarding, admin#48

Open
ChrisAdamsdevelopment wants to merge 2 commits into
mainfrom
buildout/full-codebase-roadmap
Open

Roadmap buildout: rate limiting, history, drag-drop, onboarding, admin#48
ChrisAdamsdevelopment wants to merge 2 commits into
mainfrom
buildout/full-codebase-roadmap

Conversation

@ChrisAdamsdevelopment
Copy link
Copy Markdown
Owner

@ChrisAdamsdevelopment ChrisAdamsdevelopment commented May 28, 2026

Summary

Implements the next chunk of the SpectraCleanse roadmap across three phases — infrastructure, core UX, and onboarding/admin — while preserving the project's critical constraints (Stripe webhook ordering, ExifTool cleanup, JWT plan freshness, SQLite WAL, ALLOWED_MIME ↔ CLEANSE_POLICY sync, secret hygiene).

What's new

Infrastructure

  • express-rate-limit: 60/min/IP on /api/* (excluding /api/health and the Stripe webhook), 10/min on /api/login, /api/register, /api/auth/*. 429 → JSON { error: "Too many requests" }.
  • /api/health now returns { status, uptime, version, time }; documented in README alongside the new rate limiting.
  • New Express routes for /sitemap.xml and /robots.txt so they work in dev and prod without depending on Vite's static pipeline; both use https://spectracleanse.com.
  • public/assets/og-image.png (1200×630) generated via scripts/generate-og-image.js (sharp). Already wired up in index.html.
  • New unit tests for cleansePolicy and processor (buildMetaToWrite, detectMarkers, verifyFinalState, buildQualityVerification, formatQuickTimeTimestamp). 28/28 tests pass.

Core feature / UX

  • Reject WAV/FLAC at Multer fileFilter with a clean 415 ({ error: "WAV and FLAC are not yet supported. Supported formats: MP3, MP4, M4A." }) instead of 422-ing after upload. Frontend accept= and validExt updated to match.
  • New GET /api/jobs + history modal in app.tsx showing file / date / status pill / markers, gated by requireAuth. Additive migration adds forensic_status and markers_removed columns to jobs and both single- and batch-process endpoints populate them.
  • Drag-and-drop on the main panel with cyan ring/background highlight; drops share the existing validation path.
  • Replaced the generic "Upgrade to continue" copy (both the 402 detail and the client fallback) with the spec'd Creator-plan wording.
  • Reworked results card: big green "X AI markers removed — your file is clean." headline, status pill from X-Forensic-Status, Copy-Link / Tweet-This share buttons, and a free-plan-only Creator upgrade nudge.
  • Fixed a latent bug: server.js called crypto.randomUUID() in /api/process-batch without importing crypto.

Onboarding + admin

  • First-run three-step onboarding modal in app.tsx (Strip AI Fingerprints → How It Works → You're Ready), gated by localStorage.onboarding_seen. A new ✨ button in the navbar re-shows it.
  • Admin dashboard behind a requireAdmin middleware that checks ADMIN_SECRET as a bearer token (never logged): GET /admin/health, /admin/recent-failures, /admin/usage-stats, plus an inline HTML /admin page (no JS framework) with uptime, DB counts, today's volume, plan distribution bars, and recent failures. No user PII in any response.
  • ADMIN_SECRET= added to .env.example with a hint for generating one.

Constraints preserved

  • express.raw() for the Stripe webhook still comes before express.json().
  • exiftool.end() exit handler unchanged.
  • requireAuth still re-reads plan from the DB; no JWT plan caching.
  • WAL pragma untouched; all jobs-table changes are additive ALTER TABLE.
  • ALLOWED_MIME and CLEANSE_POLICY kept in sync (mp3 quick, mp4/m4a server).
  • No secrets are logged or echoed.

Test plan

  • npm run test:run — 28/28 passing
  • npx tsc --noEmit — clean
  • node --check server.js — ok
  • npm run build — production bundle builds, og-image.png ships in dist/assets/
  • Boot on Node 20 (Render) and verify /api/health, /sitemap.xml, /robots.txt, /api/jobs, drag-and-drop, onboarding first-run, history modal, results-card social proof
  • Verify rate limiting fires (login burst returns 429 JSON)
  • Upload .wav → expect 415 with the new message
  • Set ADMIN_SECRET and visit /admin — confirm dashboard renders and 401s without bearer token

Needs human action

  • Stripe prices (STRIPE_CREATOR_PRICE_ID, STRIPE_STUDIO_PRICE_ID) on Render — unchanged.
  • SMTP credentials for password reset / verification — unchanged; gracefully falls back to dev logging when unset.
  • ADMIN_SECRET must be set in Render before /admin/* is reachable.

🤖 Generated with Claude Code

Summary by Sourcery

Implement rate limiting, richer health/SEO endpoints, onboarding and admin dashboards, upload/history UX improvements, and supporting tests/assets for SpectraCleanse.

New Features:

  • Add authenticated job history API with a frontend history modal showing recent cleanses and forensic status.
  • Introduce a first-run, multi-step onboarding modal with a navbar shortcut to re-open it.
  • Add an admin dashboard protected by an ADMIN_SECRET bearer token, exposing health, usage stats, and recent failure views via JSON and an HTML page.
  • Serve sitemap.xml and robots.txt directly from the backend and generate a committed Open Graph image asset for social previews.

Bug Fixes:

  • Prevent WAV/FLAC uploads end-to-end by tightening MIME/extension validation and returning a clear 415 error response.
  • Fix missing crypto import used for UUID generation in batch processing.

Enhancements:

  • Refine cleanse result presentation with a status pill, social sharing actions, and an upgrade nudge for free users.
  • Tighten free-plan limit messaging on both client and server to reflect the Creator plan positioning.
  • Improve main panel UX with drag-and-drop uploads and clearer accepted-format hints.
  • Record forensic status and marker counts on jobs for both single and batch processing to support history and admin views.

Build:

  • Add express-rate-limit for API throttling and sharp for OG image generation.

Documentation:

  • Document the public health endpoint, rate-limiting behavior, and backend-served SEO endpoints in the README.

Tests:

  • Add targeted tests for cleanse policy helpers and processor functions around metadata construction, marker detection, quality verification, and timestamp formatting.

Adds the next chunk of the SpectraCleanse roadmap across three phases.

Infrastructure
- Add express-rate-limit: 60/min/IP global on /api/* (excluding /api/health
  and the Stripe webhook), 10/min on /api/login, /api/register, /api/auth/*.
  429 responses return JSON { error: "Too many requests" }.
- Expand /api/health to return { status, uptime, version, time } and document
  it (plus rate limiting) in the README.
- Serve /sitemap.xml and /robots.txt from Express so they work in dev and
  prod without depending on Vite's static pipeline; both reference the
  canonical https://spectracleanse.com domain.
- Generate public/assets/og-image.png (1200x630) via a sharp-backed script
  (scripts/generate-og-image.js) so social previews finally have an image.
- Add unit tests for cleansePolicy and processor (buildMetaToWrite,
  detectMarkers, verifyFinalState, buildQualityVerification,
  formatQuickTimeTimestamp). 28/28 tests pass.

Core feature / UX
- Reject WAV/FLAC at the Multer fileFilter stage with a 415 and a clear
  message ("WAV and FLAC are not yet supported. Supported formats: MP3,
  MP4, M4A.") instead of wasting bandwidth and 422-ing post-upload. Keep
  ALLOWED_MIME in sync with CLEANSE_POLICY. Tighten the frontend accept=
  attribute and validExt regex to match.
- Add GET /api/jobs (requireAuth, last 50) and a history modal in app.tsx
  showing file, date, status pill, markers. New jobs columns
  forensic_status + markers_removed (additive migration) are populated by
  both /api/process and /api/process-batch.
- Add drag-and-drop on the main panel with a cyan ring/background
  highlight; drops go through the same addFiles validation path.
- Replace the generic "Upgrade to continue" message (both the 402 detail
  and the client-side fallback) with the spec'd copy referencing the
  Creator plan price.
- Rework the results card with a big green "X AI markers removed - your
  file is clean." headline, a status pill driven by X-Forensic-Status
  (Clean / Clean with Notes / Review Required), Copy-Link / Tweet-This
  share buttons, and a free-plan-only Creator upgrade nudge.
- Fix a latent bug: server.js called crypto.randomUUID() in
  /api/process-batch without importing crypto.

Onboarding + admin
- Add a three-step first-run onboarding modal (Strip AI Fingerprints ->
  How It Works -> You're Ready) gated by localStorage.onboarding_seen
  with a Sparkles button in the navbar to re-show it.
- Add an admin health dashboard behind a requireAdmin middleware backed
  by ADMIN_SECRET (bearer token, never logged): /admin/health,
  /admin/recent-failures, /admin/usage-stats, and an inline HTML /admin
  page (no JS framework) with uptime, DB counts, today's processing
  volume, plan distribution bars, and a recent-failures table. None of
  the admin routes expose user PII.

Constraints preserved
- express.raw() for the Stripe webhook still comes before express.json().
- exiftool.end() exit handler unchanged.
- requireAuth re-reads plan from DB; no caching of JWT plan field.
- SQLite WAL mode pragma untouched; jobs schema changes are additive.
- ALLOWED_MIME and CLEANSE_POLICY are kept in sync.
- No JWT_SECRET / STRIPE_WEBHOOK_SECRET / GEMINI_API_KEY / ADMIN_SECRET
  in logs or responses.

Verification
- npm run test:run: 28/28 passing
- npx tsc --noEmit: clean
- node --check server.js: ok
- npm run build: production bundle builds, og-image.png ships in dist/

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@sourcery-ai
Copy link
Copy Markdown

sourcery-ai Bot commented May 28, 2026

Reviewer's Guide

Implements rate limiting and health/SEO endpoints on the backend, extends the jobs schema and processing pipeline to track forensic status/history, adds first‑run onboarding and drag‑and‑drop UX on the frontend, introduces an admin health dashboard, tightens media-type handling (WAV/FLAC rejection, MP3/MP4/M4A alignment), and adds tests plus an OG image generator script.

Sequence diagram for fetching and displaying job history via GET /api/jobs

sequenceDiagram
  actor User
  participant App_tsx as App
  participant Server_js as Server
  participant requireAuth as requireAuth
  participant DB as Database

  User->>App_tsx: Click history button
  App_tsx->>App_tsx: setHistoryOpen(true)
  App_tsx->>Server_js: GET /api/jobs
  Server_js->>requireAuth: requireAuth(req)
  requireAuth-->>Server_js: userId from JWT
  Server_js->>DB: SELECT id, filename, platform,
  Server_js->>DB: forensic_status, markers_removed,
  Server_js->>DB: created_at FROM jobs
  DB-->>Server_js: rows (max 50)
  Server_js-->>App_tsx: 200 { jobs: rows }
  App_tsx->>App_tsx: setHistory(jobs)
  App_tsx->>User: Render history modal with
  App_tsx->>User: filename, date, status pill,
  App_tsx->>User: markers_removed
Loading

File-Level Changes

Change Details Files
Add global and auth-specific API rate limiting plus richer health/SEO endpoints, and wire in needed dependencies.
  • Introduce express-rate-limit with a 60/min IP limiter for most /api/* routes and a 10/min limiter for login/register/auth routes, returning uniform 429 JSON
  • Enhance /api/health to return status, uptime, version (from package.json), and current time
  • Serve /sitemap.xml and /robots.txt directly from the Express app using a canonical host constant
  • Add sharp dependency and a scripts/generate-og-image.js utility to build public/assets/og-image.png for social previews
  • Document health and rate-limit behavior in README
server.js
package.json
package-lock.json
scripts/generate-og-image.js
public/assets/og-image.png
README.md
Extend jobs persistence and API surface to expose a per-user processing history including forensic status and marker counts.
  • ALTER TABLE jobs at startup to ensure forensic_status (TEXT) and markers_removed (INTEGER) columns exist
  • Populate forensic_status and markers_removed in both single-file and batch processing flows using processor report values
  • Add GET /api/jobs (requireAuth) to return the most recent 50 jobs for the authenticated user, ordered by created_at/id
server.js
Add frontend UX for drag-and-drop uploads, processing history modal, and a first-run onboarding modal tied to localStorage.
  • Track onboardingStep, drag state, and history modal state in App component; initialize onboarding on first load if onboarding_seen is not set in localStorage
  • Implement drag-enter/leave/over/drop handlers on the main panel to accept drops and visually highlight the drop zone
  • Implement a history modal that calls /api/jobs on open/refresh and renders a table of filename/date/status/markers with status pills
  • Add navbar buttons to open history and to re-show onboarding, clearing onboarding_seen
app.tsx
Tighten media-type handling and messaging so only MP3/M4A/MP4 are accepted and WAV/FLAC are rejected early with a clear 415 response.
  • Update frontend file extension regex and input accept= attribute to only allow .mp3, .m4a, .mp4 with specific audio/video MIME types
  • Refactor Multer ALLOWED_MIME to mirror CLEANSE_POLICY (mp3 for quick, mp4/m4a for server) and add a REJECTED_LEGACY_MIME set for WAV/FLAC and aliases
  • Adjust Multer fileFilter to set specific error codes for WAV/FLAC vs generic unsupported types and extend the Express error handler to map them to structured 415 JSON
  • Add cleansePolicy tests to assert supported extensions and isServerSupportedFormat behavior across mp3/mp4/m4a/wav/flac
app.tsx
server.js
server/cleansePolicy.js or .ts (via tests)
tests/cleanse-policy.test.ts
Improve results card UX, upgrade messaging, and social sharing on the frontend based on forensic report data and current plan.
  • Update 402 error handling copy on the client and server to use new Creator-plan marketing text indicating 3 free cleanses and $9.99/month Creator pricing
  • Augment the forensic report card with a dynamic headline based on removedCount, a status pill derived from report.status, and better tag counts
  • Add "Copy link" button that copies the SpectraCleanse URL and a prefilled "Tweet This" link, plus a free-plan-only Creator upgrade nudge section
app.tsx
server.js
Introduce an admin-only observability surface with JSON APIs and a lightweight HTML dashboard, authenticated via ADMIN_SECRET.
  • Add requireAdmin middleware that checks a static bearer token against ADMIN_SECRET without logging it
  • Implement /admin/health, /admin/recent-failures, and /admin/usage-stats JSON endpoints that aggregate counts, memory usage, plan distribution, and recent review_required jobs without exposing PII beyond numeric IDs
  • Serve a minimal /admin HTML dashboard that renders health cards, plan distribution bars, and a table of recent failures using inline styles
  • Document ADMIN_SECRET in .env.example and mention its necessity in the PR description
server.js
.env.example
Add backend tests for processor behavior and fix a latent crypto import bug.
  • Add tests for processor helpers (buildMetaToWrite, detectMarkers, verifyFinalState, buildQualityVerification, formatQuickTimeTimestamp) to validate metadata synthesis and marker detection behavior
  • Fix missing crypto import used for randomUUID in /api/process-batch
  • Confirm test suite passes (28/28) and tsc/build checks remain clean
server.js
tests/processor-extra.test.ts

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@vercel
Copy link
Copy Markdown

vercel Bot commented May 28, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
spectracleanseai Ready Ready Preview, Comment, Open in v0 May 28, 2026 3:53am

Copy link
Copy Markdown

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 1 issue

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="app.tsx" line_range="1107-1116" />
<code_context>
+            ),
+            footnote: 'Your audio is never stored. Files are processed in memory and immediately deleted.',
+          },
+          {
+            title: "You're Ready",
+            body: 'You have 3 free cleanses this month. No credit card required.',
+            visual: null as any,
</code_context>
<issue_to_address>
**suggestion:** Avoid using `null as any` for the onboarding step `visual` field by making the property optional instead.

Since only the last step omits `visual`, you can update the step type to something like `{ title: string; body: string; visual?: React.ReactNode; footnote?: string }` and render it with a guard, e.g. `{current.visual && current.visual}`. This keeps the types accurate and removes the need for the unsafe `as any` cast.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread app.tsx
CI: bump exiftool-vendored ^28.3.1 -> ^35.20.0 and nodemailer ^6.10.1 ->
^8.0.9 to clear two high-severity advisories
(GHSA-cw26-7653-2rp5, GHSA-mm7p-fcc7-pg87 / -rcmh-qjqh-p98v /
-c7w3-x93f-qmm8 / -vvjj-xcjg-gr5g). Our usage is limited to
exiftool.{read,write,end,version,readRaw} and
nodemailer.{createTransport,sendMail} — both stable across the bump and
verified by smoke-importing each module.

`npm audit --audit-level=high` now exits 0 (7 moderate vulns remain in
dev-only tooling, below the CI threshold).

Code review: type the onboarding step array as
`{ title; body; visual?; footnote? }[]` and drop the `null as any` cast +
`'footnote' in current` guard.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant