quotes: store uploaded files in our own R2 bucket, not litterbox.catbox.moe - #34
Merged
Conversation
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
3d-printing | ab3d927 | Commit Preview URL Branch Preview URL |
Sep 03 2026, 09:48 AM |
Aswincloud-Bot
approved these changes
Sep 3, 2026
Aswincloud-Bot
left a comment
There was a problem hiding this comment.
Auto-approved: @Aswinmcw is a member of @Aswincloud/admins.
…ox.moe The quote form posted the customer's file — an STL of their part, a photo, a drawing — from the browser to an anonymous public host with a 72-hour expiry. Anyone with the URL could fetch it, it vanished whether or not the job had been priced, and the privacy policy said nothing about a third party. - POST /api/quote/upload takes the raw bytes and writes them to the UPLOADS bucket under quotes/<year>/<uuid>/<safe-name>. Extension allowlist matching the form's accept=, 100 MB cap, same RL_QUOTE limiter as the form. - The quote carries file_key; the Worker turns it into the owner-only route GET /api/admin/quotes/file?key=… and stores that as file_url, so the email "Download" button and the dashboard link work unchanged and both require the owner to be signed in. Served as an attachment with nosniff. - CSP connect-src drops litterbox.catbox.moe. Privacy policy says where files go. wrangler.toml gains the r2_buckets binding. Co-authored-by: Cursor <cursoragent@cursor.com>
Aswinmcw
force-pushed
the
feat/quote-uploads-r2
branch
from
September 3, 2026 09:48
8c38b35 to
ab3d927
Compare
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
The quote form's file upload went straight from the browser to
litterbox.catbox.moe— an anonymous public file host with a 72-hour expiry. Anyone holding the URL could download the customer's part design; the file disappeared after three days whether or not the quote had been answered; andprivacy.htmldid not mention that a third party received uploads at all.Uploads now go to a private R2 bucket owned by this account:
POST /api/quote/upload(new, insrc/uploads.js) takes the file as the raw request body with the name inX-File-Name, and writes it to theUPLOADSbucket underquotes/<year>/<uuid>/<safe-name>. Extension allowlist mirrors the form'saccept=(STL/OBJ/3MF/STEP/STP/JPG/PNG/PDF); 100 MB cap (the form already says "Max 100MB", and it is the Workers body limit); rate-limited by the sameRL_QUOTElimiter as the form.POST /api/quotenow acceptsfile_key(validated against the exact key shape the uploader mints) instead of a browser-suppliedfile_url. The Worker storesfile_url=https://…/api/admin/quotes/file?key=…, so the owner email's Download button and the dashboard's attachment link are unchanged, and both now require the owner session.GET /api/admin/quotes/file?key=…streams the object back asContent-Disposition: attachmentwithnosniffandno-store. Sits under/api/admin/so the existing owner gate covers it; unauthenticated → 401.connect-srcdropslitterbox.catbox.moe. Privacy policy now says uploads are stored privately on our Cloudflare account and never posted to a third-party host.Before merging / deploying
npx wrangler r2 bucket create 3dprints-uploads. The Worker returns a clear 503 on upload if the binding is missing rather than failing the quote.quotes/objects after 180 days. There is no default expiry; files are kept.Test plan
npm testgreen; newtest/uploads.mjs(44 checks): key shape, filename sanitising (paths, unicode, header-breaking characters), extension refusals (.html,.exe,.svg,.stl.js), empty body, 413 over cap, 503 without binding, download headers, 400/404 paths, and that no storefront/Worker file references a catbox URLwrangler devwith local R2: 300 KB.stluploads and returns a key;.htmlis refused; unauthenticated download is 401; a full quote-form submission through the browser POSTs to/api/quote/uploadthen/api/quote, and the D1 row'sfile_urlis the owner-only route/shopMade with Cursor