Reliable IPFS, rationale drafting/caching, and ballot CSV#300
Merged
Conversation
Fix flaky IPFS up/download, add rationale drafting persisted to the DB, surface rationale in pending-tx review, and add ballot CSV import/export. IPFS reliability - New src/lib/ipfs.ts gateway helpers + /api/ipfs/resolve, a multi-gateway server proxy (dedicated Pinata gateway first, then public fallbacks, 6s/ gateway, 2MB cap, cached). Reads route through it instead of hitting the frequently-504ing ipfs.io directly. - Uploads (pinata-storage put + image/put) return a dedicated-gateway URL via ipfsGatewayUrl() instead of a hardcoded ipfs.io URL. - NEXT_PUBLIC_PINATA_GATEWAY_URL added to env (optional, bare host accepted; scheme normalised in code). Rationale drafting + DB cache - Draft comments persist on blur via updateProposalRationale; uploads also cache the comment in the Ballot row (no schema change). - transaction-card VoteRationale shows each vote's rationale, preferring the DB cache and falling back to the IPFS proxy, with a gateway "source" link. Ballot CSV - New BallotCsv (papaparse + react-dropzone): columns proposal_id,title,vote,comment,anchor_url,anchor_hash. Import merges by proposal_id; blank cells preserve existing values; export is quoted via Papa.unparse. Hardening (from adversarial review) - resolve.ts follows redirects manually and only to allow-listed gateway hosts (SSRF guard), and pins Content-Type: application/json + nosniff. - fetchIpfsJson rejects non-https non-IPFS anchors and adds a timeout backstop. - Auto-load effect merges by proposalId+anchor so refetches don't clobber in-progress edits, aborts in-flight fetches, and de-dupes re-fetches. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
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
Fixes the flaky IPFS upload/download on the ballot voting UI, adds rationale drafting persisted to the DB, surfaces the rationale for review in the pending transaction, and adds ballot CSV import/export — the four asks from the "Manage Ballots" report (IPFS 504s loading rationale from
ipfs.io).IPFS reliability
src/lib/ipfs.tsgateway helpers +/api/ipfs/resolve, a multi-gateway server proxy: tries the dedicated Pinata gateway first, then public fallbacks, 6 s per gateway, 2 MB cap, long-cached. Browser reads go through it instead of hitting the frequently-504ingipfs.iodirectly (no CORS, no single-gateway failure).pinata-storage/put.ts,image/put.ts) now return a dedicated-gateway URL viaipfsGatewayUrl()rather than a hardcodedipfs.ioURL.NEXT_PUBLIC_PINATA_GATEWAY_URLadded tosrc/env.js(optional; accepts a bare host — scheme normalised in code). On-chain anchor hashes are unaffected (hashDrepAnchorhashes the JSON body, not the URL).Rationale drafting + DB cache
updateProposalRationale; uploading also caches the comment in theBallotrow. No schema migration — the existing parallel arrays already hold it.transaction-card.tsxgains aVoteRationaleblock per vote that prefers the DB-cached comment (no network) and falls back to the IPFS proxy, with a gateway "source" link.Ballot CSV
BallotCsv.tsx(papaparse + react-dropzone). Columns:proposal_id,title,vote,comment,anchor_url,anchor_hash. Import merges byproposal_id(blank cells preserve existing values; new rows default to Abstain); export is correctly quoted viaPapa.unparse. Available on both the populated and empty-ballot views.Hardening (from an adversarial multi-agent review of this branch)
resolve.tsfollows redirects manually and only to allow-listed gateway hosts (SSRF guard incl.*.ipfs.dweb.link/w3s.linksubdomain forms), and pinsContent-Type: application/json+nosniff.fetchIpfsJsonrejects non-httpsnon-IPFS anchors (blockshttp://localhost/169.254.169.254from a hostile co-signer's anchor) and adds a timeout backstop.proposalId + anchorso refetches (e.g. changing a vote) don't clobber in-progress edits, aborts in-flight fetches on cleanup, and de-dupes re-fetches.Verification
tsc --noEmitclean;next build --webpacksucceeds (/api/ipfs/resolveemitted as a function route).updateBallotfull-overwrite vs. concurrent per-row-edit race (architectural; worst-case silent vote loss already removed by the blank-cell-preserve fix) and a low-severity O(n·m) cache lookup that tRPC already dedupes.Notes
.env'sNEXT_PUBLIC_PINATA_GATEWAY_URLis already set (bare host) and now wired through.🤖 Generated with Claude Code