Roadmap buildout: rate limiting, history, drag-drop, onboarding, admin#48
Open
ChrisAdamsdevelopment wants to merge 2 commits into
Open
Roadmap buildout: rate limiting, history, drag-drop, onboarding, admin#48ChrisAdamsdevelopment wants to merge 2 commits into
ChrisAdamsdevelopment wants to merge 2 commits into
Conversation
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>
Reviewer's GuideImplements 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/jobssequenceDiagram
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
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
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>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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/healthand the Stripe webhook), 10/min on/api/login,/api/register,/api/auth/*. 429 → JSON{ error: "Too many requests" }./api/healthnow returns{ status, uptime, version, time }; documented in README alongside the new rate limiting./sitemap.xmland/robots.txtso they work in dev and prod without depending on Vite's static pipeline; both usehttps://spectracleanse.com.public/assets/og-image.png(1200×630) generated viascripts/generate-og-image.js(sharp). Already wired up inindex.html.cleansePolicyandprocessor(buildMetaToWrite, detectMarkers, verifyFinalState, buildQualityVerification, formatQuickTimeTimestamp). 28/28 tests pass.Core feature / UX
fileFilterwith a clean 415 ({ error: "WAV and FLAC are not yet supported. Supported formats: MP3, MP4, M4A." }) instead of 422-ing after upload. Frontendaccept=andvalidExtupdated to match.GET /api/jobs+ history modal inapp.tsxshowing file / date / status pill / markers, gated byrequireAuth. Additive migration addsforensic_statusandmarkers_removedcolumns tojobsand both single- and batch-process endpoints populate them.detailand the client fallback) with the spec'd Creator-plan wording.X-Forensic-Status, Copy-Link / Tweet-This share buttons, and a free-plan-only Creator upgrade nudge.server.jscalledcrypto.randomUUID()in/api/process-batchwithout importingcrypto.Onboarding + admin
app.tsx(Strip AI Fingerprints → How It Works → You're Ready), gated bylocalStorage.onboarding_seen. A new ✨ button in the navbar re-shows it.requireAdminmiddleware that checksADMIN_SECRETas a bearer token (never logged):GET /admin/health,/admin/recent-failures,/admin/usage-stats, plus an inline HTML/adminpage (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.examplewith a hint for generating one.Constraints preserved
express.raw()for the Stripe webhook still comes beforeexpress.json().exiftool.end()exit handler unchanged.requireAuthstill re-reads plan from the DB; no JWT plan caching.ALTER TABLE.ALLOWED_MIMEandCLEANSE_POLICYkept in sync (mp3 quick, mp4/m4a server).Test plan
npm run test:run— 28/28 passingnpx tsc --noEmit— cleannode --check server.js— oknpm run build— production bundle builds,og-image.pngships indist/assets//api/health,/sitemap.xml,/robots.txt,/api/jobs, drag-and-drop, onboarding first-run, history modal, results-card social proof.wav→ expect 415 with the new messageADMIN_SECRETand visit/admin— confirm dashboard renders and 401s without bearer tokenNeeds human action
STRIPE_CREATOR_PRICE_ID,STRIPE_STUDIO_PRICE_ID) on Render — unchanged.ADMIN_SECRETmust 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:
Bug Fixes:
Enhancements:
Build:
Documentation:
Tests: