Add profile media kit and appearance controls#149
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Storybook Component Screenshot PreviewOutcome: success Screenshots: primitive component stories captured on desktop and mobile. This lane is separate from full-route Playwright screenshots and focuses on design-system component regressions. |
Storybook Image DiffOutcome: success Changed Storybook baselines: none in this PR. This check compares design-system component screenshots against committed baselines. Inline images show only added or modified Storybook baseline PNGs. |
Playwright Hosted Data-FlowOutcome: success This optional check runs the mutation-backed profile flow against a configured hosted dev/staging target with isolated E2E test data. |
Playwright Data-Flow PreviewOutcome: success Captured flow:
Artifacts include screenshots, traces, and recorded video for the flow run. |
Playwright Public Screenshot PreviewOutcome: success Screenshots: all public route checks passed on desktop and mobile. Full screenshot set is available in the artifact. Pixel diff baselines are handled by the separate Playwright Image Diff check. |
Playwright Image DiffOutcome: success Changed screenshot baselines: 32 Showing 12 of 32; see the artifact for the rest. This check compares public route screenshots against committed baselines. Inline images show only added or modified baseline PNGs. |
Vercel Preview DeploymentPreview: https://vr-dex-r085k2o6t-basicbit.vercel.app |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: fd592dabeb
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Greptile SummaryThis PR adds a complete profile media-kit system: S3-backed asset upload/import with streaming SSRF protection, per-profile avatar appearance controls, public asset/logo/zip API routes, and matching frontend rendering on profile and account pages.
Confidence Score: 5/5Safe to merge; the one finding is a minor hardening gap in the SSRF IP blocklist for a long-deprecated IPv6 range with no practical presence in modern infrastructure. The upload pipeline correctly prevents DNS rebinding, streams with a hard byte cap, validates MIME types, and gates all mutations behind Convex auth and upload tokens. The S3 client cache is now keyed by region, SVG assets are served under a CSP sandbox, and the avatar appearance controls are clamped server-side. The only gap found — fec0::/10 not being blocked in isPrivateIpv6 — applies only to deprecated site-local IPv6 addresses that have not been allocated since 2004. The SSRF IP-validation logic in apps/web/src/app/api/v0/profile-assets/upload-intents/[intentId]/route.ts (isPrivateIpv6) deserves a second look to close the fec0::/10 gap before the URL import feature reaches production. Important Files Changed
Sequence Diagram%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
participant Client
participant NextAPI as Next.js API Routes
participant Convex
participant S3
Note over Client,S3: Upload Flow
Client->>Convex: createUploadIntent(mimeType, fileName/sourceUrl)
Convex-->>Client: intentId + uploadToken + storageKey
alt File Upload
Client->>NextAPI: "POST /upload-intents/{intentId} multipart/form-data"
NextAPI->>Convex: validateUploadIntentForStorage
NextAPI->>S3: putProfileAssetObject
NextAPI->>Convex: markUploadIntentUploaded
else URL Import
Client->>NextAPI: "POST /upload-intents/{intentId} sourceUrl intent"
NextAPI->>NextAPI: DNS resolve + IP blocklist check
NextAPI->>ExternalServer: HTTPS GET pinned to resolved IP
ExternalServer-->>NextAPI: streamed bytes up to 12 MB
NextAPI->>S3: putProfileAssetObject
NextAPI->>Convex: markUploadIntentUploaded
end
Client->>Convex: submitCommunityProfile with assets
Convex->>Convex: consumeProfileAssetUploads active+public
Note over Client,S3: Retrieval Flow
Client->>NextAPI: "GET /profiles/{slug}/assets/{assetId}/file"
NextAPI->>Convex: getPublicAssetForStorage
Convex-->>NextAPI: storageKey for public active asset
NextAPI->>S3: GetObjectCommand
NextAPI-->>Client: bytes with CSP sandbox header
Client->>NextAPI: "GET /profiles/{slug}/logos.zip"
NextAPI->>Convex: listPublicBySlug
NextAPI->>Convex: getPublicAssetForStorage x N parallel
NextAPI->>S3: getProfileAssetObject x N parallel
NextAPI-->>Client: ZIP archive
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
participant Client
participant NextAPI as Next.js API Routes
participant Convex
participant S3
Note over Client,S3: Upload Flow
Client->>Convex: createUploadIntent(mimeType, fileName/sourceUrl)
Convex-->>Client: intentId + uploadToken + storageKey
alt File Upload
Client->>NextAPI: "POST /upload-intents/{intentId} multipart/form-data"
NextAPI->>Convex: validateUploadIntentForStorage
NextAPI->>S3: putProfileAssetObject
NextAPI->>Convex: markUploadIntentUploaded
else URL Import
Client->>NextAPI: "POST /upload-intents/{intentId} sourceUrl intent"
NextAPI->>NextAPI: DNS resolve + IP blocklist check
NextAPI->>ExternalServer: HTTPS GET pinned to resolved IP
ExternalServer-->>NextAPI: streamed bytes up to 12 MB
NextAPI->>S3: putProfileAssetObject
NextAPI->>Convex: markUploadIntentUploaded
end
Client->>Convex: submitCommunityProfile with assets
Convex->>Convex: consumeProfileAssetUploads active+public
Note over Client,S3: Retrieval Flow
Client->>NextAPI: "GET /profiles/{slug}/assets/{assetId}/file"
NextAPI->>Convex: getPublicAssetForStorage
Convex-->>NextAPI: storageKey for public active asset
NextAPI->>S3: GetObjectCommand
NextAPI-->>Client: bytes with CSP sandbox header
Client->>NextAPI: "GET /profiles/{slug}/logos.zip"
NextAPI->>Convex: listPublicBySlug
NextAPI->>Convex: getPublicAssetForStorage x N parallel
NextAPI->>S3: getProfileAssetObject x N parallel
NextAPI-->>Client: ZIP archive
Reviews (3): Last reviewed commit: "Address final profile asset review findi..." | Re-trigger Greptile |
|
AI review recycle note: Greptile's outside-diff ZIP latency item was also addressed in |
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: bde1773b8e
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 18cbf982b0
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f05592f025
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
AI review recycle note: Greptile's updated summary noted the deprecated IPv6 site-local |












Summary
Notes
501until the profile asset bucket/region environment variables are configured.Verification