Skip to content

Conversation

@yujonglee
Copy link
Contributor

@yujonglee yujonglee commented Nov 28, 2025

Summary

This PR adds a server-side proxy route (/api/images/$) to serve images from Supabase storage, replacing direct Supabase URLs throughout the web app. The proxy fetches images from https://ijoptyyjrfqwaqhyxkxj.supabase.co/storage/v1/object/public/public_images/ and forwards them with appropriate caching headers.

Changes include:

  • New /api/images/$ catch-all server route in apps/web/src/routes/api/images.$.ts
  • Updated ~100 image references across 20 files to use the proxy path
  • Netlify edge function (og.tsx) intentionally left unchanged as it runs on Netlify's edge network

Updates since last revision

Added security hardening based on CodeRabbit feedback:

  • Path sanitization: Added sanitizePath() function that validates path parameters before proxying
    • Decodes URL-encoded paths and rejects malformed encoding
    • Blocks path traversal attempts (.., \, absolute paths)
    • Only allows safe characters: [A-Za-z0-9._+-] per segment (updated to include + for symbol+logo.png)
    • Rejects empty path segments (e.g., //)
  • Improved error handling: Returns 404 for upstream 404s, 502 for other upstream errors

Review & Testing Checklist for Human

  • CRITICAL: OG meta tags now use relative paths - The __root.tsx changes og:image and twitter:image to /api/images/hyprnote/og-image.jpg. Social media crawlers require absolute URLs. This will break social sharing previews. Consider reverting these specific changes or making them absolute URLs.
  • Verify the proxy route works correctly by visiting /api/images/hyprnote/icon.png on the deployed preview
  • Test that images load correctly across the site (header logo, about page photos, product pages)
  • Visit /brand page and verify symbol+logo.png loads correctly

Recommended test plan:

  1. Deploy to preview environment
  2. Check browser network tab to confirm images load via /api/images/ paths
  3. Test social sharing preview using Facebook/Twitter debugger tools (will likely fail due to relative OG image URLs)
  4. Verify Netlify edge function OG images still work

Notes

- Add /api/images/$ server route to proxy requests to supabase storage
- Replace all direct supabase storage URLs with proxy URLs in apps/web
- Keeps og.tsx edge function unchanged as it runs on Netlify edge

Co-Authored-By: yujonglee <yujonglee.dev@gmail.com>
@devin-ai-integration
Copy link
Contributor

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR that start with 'DevinAI' or '@devin'.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

@netlify
Copy link

netlify bot commented Nov 28, 2025

Deploy Preview for hyprnote ready!

Name Link
🔨 Latest commit 735e8c8
🔍 Latest deploy log https://app.netlify.com/projects/hyprnote/deploys/6928fe37f787c0000836081f
😎 Deploy Preview https://deploy-preview-1958--hyprnote.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@netlify
Copy link

netlify bot commented Nov 28, 2025

Deploy Preview for hyprnote-storybook ready!

Name Link
🔨 Latest commit 735e8c8
🔍 Latest deploy log https://app.netlify.com/projects/hyprnote-storybook/deploys/6928fe37daa6c0000848d4ba
😎 Deploy Preview https://deploy-preview-1958--hyprnote-storybook.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 28, 2025

📝 Walkthrough

Walkthrough

This PR migrates image asset references across the web application from external Supabase storage URLs to internal API-proxied paths. A new API route proxies image requests to Supabase storage with proper path sanitization and caching headers. Only image source strings are modified; no control flow or business logic changes.

Changes

Cohort / File(s) Summary
API Proxy Route
apps/web/src/routes/api/images.$.ts
New GET route handler that proxies /api/images/$ requests to Supabase storage, with path sanitization, upstream fetch, 404 forwarding, error-to-502 mapping, and Cache-Control header management (defaults to public, max-age=31536000, immutable).
Global Components
apps/web/src/components/footer.tsx, apps/web/src/components/header.tsx, apps/web/src/components/video-thumbnail.tsx
Logo and poster image URLs updated from external Supabase URLs to /api/images/... paths.
Root Layout & Meta Tags
apps/web/src/routes/__root.tsx
og:image and twitter:image meta tags updated to use /api/images/hyprnote/og-image.jpg instead of external URLs.
Marketing & View Routes
apps/web/src/routes/_view/about.tsx, apps/web/src/routes/_view/brand.tsx, apps/web/src/routes/_view/changelog/$slug.tsx, apps/web/src/routes/_view/changelog/index.tsx, apps/web/src/routes/_view/download/index.tsx, apps/web/src/routes/_view/index.tsx, apps/web/src/routes/_view/press-kit.tsx, apps/web/src/routes/_view/pricing.tsx
Founder avatars, team photos, icons, logo variants, OG images and branding assets switched from external Supabase storage URLs to /api/images/... proxy paths.
Product Routes
apps/web/src/routes/_view/product/ai-assistant.tsx, apps/web/src/routes/_view/product/ai-notetaking.tsx, apps/web/src/routes/_view/product/bot.tsx, apps/web/src/routes/_view/product/mini-apps.tsx, apps/web/src/routes/_view/product/workflows.tsx
Feature hero images, CTAs, integration icons, avatars and background textures updated to use /api/images/... endpoints.
Comparison & Auth Routes
apps/web/src/routes/_view/vs/$slug.tsx, apps/web/src/routes/auth.tsx, apps/web/src/routes/join-waitlist.tsx
Hyprnote icon, textures and background images updated to internal API paths.

Sequence Diagram(s)

sequenceDiagram
    participant Browser
    participant AppServer
    participant Supabase
    Browser->>AppServer: GET /api/images/hyprnote/icon.png
    Note right of AppServer: sanitizePath(params.path)\nvalidate segments
    AppServer->>Supabase: GET https://.../public_images/hyprnote/icon.png
    Supabase-->>AppServer: 200 + Content-Type, Cache-Control?, body
    Note right of AppServer: if no Cache-Control -> set public, max-age=31536000, immutable
    AppServer-->>Browser: 200 + Content-Type + Cache-Control + body
    alt Supabase 404
      Supabase-->>AppServer: 404
      AppServer-->>Browser: 404
    else Supabase error
      Supabase-->>AppServer: error/5xx
      AppServer-->>Browser: 502
    end
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10–15 minutes

  • Highly repetitive pattern: same URL replacement applied across many files.
  • New API route introduces straightforward proxy logic; verify sanitization, header handling, and error mappings.
  • Review focus: confirm all replaced image paths match proxy endpoints and are reachable.

Possibly related PRs

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: adding a proxy for Supabase storage images, which is the central objective of this PR.
Description check ✅ Passed The description is comprehensive and directly related to the changeset, explaining the proxy implementation, security hardening, affected files, and known issues.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch devin/1764293014-supabase-proxy

Comment @coderabbitai help to get the list of available commands and usage tips.

@argos-ci
Copy link

argos-ci bot commented Nov 28, 2025

The latest updates on your projects. Learn more about Argos notifications ↗︎

Build Status Details Updated (UTC)
web (Inspect) ⚠️ Changes detected (Review) 3 changed Nov 28, 2025, 1:45 AM

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (5)
apps/web/src/components/footer.tsx (1)

21-27: Footer logo path is correct; consider centralizing logo markup

Using /api/images/hyprnote/logo.svg aligns with the header and new proxy. As a small cleanup, you could eventually factor a shared <Logo /> component that encapsulates this src + sizing and reuse it in header/footer/auth to avoid future drift.

apps/web/src/components/header.tsx (1)

47-51: Header logo URLs are consistent; consider reusing a shared Logo component

Both desktop and mobile headers correctly use /api/images/hyprnote/logo.svg, matching the footer and the new image proxy. To avoid future inconsistencies, you might later extract a small <Logo /> component used in header/footer/auth instead of repeating the raw <img> blocks.

Also applies to: 147-151

apps/web/src/routes/_view/product/workflows.tsx (1)

370-385: Icon proxy paths look correct; a11y alt text could be refined later

Using /api/images/icons/${...} for both the integrations grid and draggable icons is a clean way to route these through the new proxy; the data arrays line up with the constructed paths. For accessibility, the grid images already use alt={integration.name}, which is good. The draggable icons use a generic "Integration" alt; if these are purely decorative, you could later change that to alt="" so they’re ignored by screen readers, or pass a more descriptive label.

Also applies to: 562-570

apps/web/src/routes/api/images.$.ts (2)

34-34: Reconsider aggressive cache-control defaults.

The 1-year immutable cache is appropriate for versioned assets but prevents cache invalidation if an asset needs updating. Consider whether all images are truly immutable or if a shorter TTL with revalidation would be more appropriate.

Options:

  1. If assets are versioned/hashed in their paths: keep current strategy
  2. If assets may change: use public, max-age=86400, must-revalidate (24 hours)
  3. If dynamic: use public, max-age=3600, stale-while-revalidate=86400 (1 hour + stale-while-revalidate)

Additionally, consider respecting the upstream cache-control header when present (which the code already does on line 32).


6-44: Consider adding observability and security enhancements.

The proxy route would benefit from additional production-ready features:

Consider these improvements:

  1. Request logging for monitoring and debugging:
console.log(`[API] Image request: ${path}`);
  1. Security headers on the response:
headers["X-Content-Type-Options"] = "nosniff";
headers["X-Frame-Options"] = "DENY";
  1. Error handling for fetch failures:
try {
  const response = await fetch(url);
  // ... existing logic
} catch (error) {
  console.error(`[API] Failed to fetch image: ${path}`, error);
  return new Response("Service unavailable", { status: 503 });
}
  1. File type validation to restrict to expected image formats:
const allowedExtensions = ['.jpg', '.jpeg', '.png', '.svg', '.webp', '.gif'];
const ext = path.toLowerCase().match(/\.[^.]+$/)?.[0];
if (!ext || !allowedExtensions.includes(ext)) {
  return new Response("Invalid file type", { status: 400 });
}

These additions improve security, observability, and maintainability.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ad61840 and fa5109b.

📒 Files selected for processing (21)
  • apps/web/src/components/footer.tsx (1 hunks)
  • apps/web/src/components/header.tsx (2 hunks)
  • apps/web/src/components/video-thumbnail.tsx (1 hunks)
  • apps/web/src/routes/__root.tsx (2 hunks)
  • apps/web/src/routes/_view/about.tsx (6 hunks)
  • apps/web/src/routes/_view/brand.tsx (1 hunks)
  • apps/web/src/routes/_view/changelog/$slug.tsx (1 hunks)
  • apps/web/src/routes/_view/changelog/index.tsx (1 hunks)
  • apps/web/src/routes/_view/download/index.tsx (1 hunks)
  • apps/web/src/routes/_view/index.tsx (15 hunks)
  • apps/web/src/routes/_view/press-kit.tsx (3 hunks)
  • apps/web/src/routes/_view/pricing.tsx (1 hunks)
  • apps/web/src/routes/_view/product/ai-assistant.tsx (2 hunks)
  • apps/web/src/routes/_view/product/ai-notetaking.tsx (8 hunks)
  • apps/web/src/routes/_view/product/bot.tsx (1 hunks)
  • apps/web/src/routes/_view/product/mini-apps.tsx (2 hunks)
  • apps/web/src/routes/_view/product/workflows.tsx (3 hunks)
  • apps/web/src/routes/_view/vs/$slug.tsx (1 hunks)
  • apps/web/src/routes/api/images.$.ts (1 hunks)
  • apps/web/src/routes/auth.tsx (1 hunks)
  • apps/web/src/routes/join-waitlist.tsx (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Avoid creating a bunch of types/interfaces if they are not shared. Especially for function props, just inline them instead.
Never do manual state management for form/mutation. Use useForm (from tanstack-form) and useQuery/useMutation (from tanstack-query) instead for 99% of cases. Avoid patterns like setError.
If there are many classNames with conditional logic, use cn (import from @hypr/utils). It is similar to clsx. Always pass an array and split by logical grouping.
Use motion/react instead of framer-motion.

Files:

  • apps/web/src/routes/_view/product/bot.tsx
  • apps/web/src/components/footer.tsx
  • apps/web/src/routes/_view/product/mini-apps.tsx
  • apps/web/src/routes/_view/download/index.tsx
  • apps/web/src/routes/__root.tsx
  • apps/web/src/routes/_view/vs/$slug.tsx
  • apps/web/src/routes/auth.tsx
  • apps/web/src/routes/_view/product/workflows.tsx
  • apps/web/src/routes/_view/changelog/$slug.tsx
  • apps/web/src/components/header.tsx
  • apps/web/src/routes/_view/brand.tsx
  • apps/web/src/routes/_view/index.tsx
  • apps/web/src/routes/_view/changelog/index.tsx
  • apps/web/src/routes/_view/product/ai-assistant.tsx
  • apps/web/src/routes/_view/press-kit.tsx
  • apps/web/src/routes/_view/about.tsx
  • apps/web/src/routes/join-waitlist.tsx
  • apps/web/src/routes/_view/pricing.tsx
  • apps/web/src/routes/_view/product/ai-notetaking.tsx
  • apps/web/src/components/video-thumbnail.tsx
  • apps/web/src/routes/api/images.$.ts
**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.ts: Agent implementations should use TypeScript and follow the established architectural patterns defined in the agent framework
Agent communication should use defined message protocols and interfaces

Files:

  • apps/web/src/routes/api/images.$.ts
🧠 Learnings (2)
📚 Learning: 2025-11-24T16:32:29.314Z
Learnt from: CR
Repo: fastrepl/hyprnote PR: 0
File: apps/web/content/changelog/AGENTS.md:0-0
Timestamp: 2025-11-24T16:32:29.314Z
Learning: Applies to apps/web/content/changelog/** : Only keep desktop-related changes when maintaining changelog entries from commits and diffs

Applied to files:

  • apps/web/src/routes/_view/changelog/$slug.tsx
📚 Learning: 2025-11-24T16:32:30.770Z
Learnt from: CR
Repo: fastrepl/hyprnote PR: 0
File: apps/web/content/changelog/AGENTS.md:0-0
Timestamp: 2025-11-24T16:32:30.770Z
Learning: Applies to apps/web/content/changelog/** : Only include desktop-related changes in the changelog when reading through commits and diffs

Applied to files:

  • apps/web/src/routes/_view/changelog/$slug.tsx
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
  • GitHub Check: Redirect rules - hyprnote-storybook
  • GitHub Check: Redirect rules - hyprnote
  • GitHub Check: Header rules - hyprnote-storybook
  • GitHub Check: Header rules - hyprnote
  • GitHub Check: Pages changed - hyprnote-storybook
  • GitHub Check: Pages changed - hyprnote
  • GitHub Check: ci (macos, macos-14)
  • GitHub Check: fmt
🔇 Additional comments (29)
apps/web/src/components/video-thumbnail.tsx (1)

25-36: Poster path change looks good; just ensure the asset exists behind the proxy

Switching poster to /api/images/hyprnote/poster-image.png is consistent with the new image proxy and keeps everything same-origin. Please just double‑check that this path is wired correctly in the new /api/images route and that the asset exists so we don’t regress thumbnails.

apps/web/src/routes/_view/download/index.tsx (1)

201-213: CTA icon now uses proxied asset; consistent with rest of app

Updating the CTASection icon to /api/images/hyprnote/icon.png matches the new proxy pattern and other usages of the Hyprnote icon. Looks good as long as the proxy serves this path correctly.

apps/web/src/routes/_view/product/bot.tsx (1)

177-183: Draggable icon image now uses proxied Hyprnote icon

Pointing the draggable bot icon at /api/images/hyprnote/icon.png is consistent with other views and keeps it on the same origin. No issues from a layout/interaction standpoint.

apps/web/src/routes/auth.tsx (1)

53-71: Auth header icon now goes through /api/images; matches global pattern

The auth Header now uses /api/images/hyprnote/icon.png, which keeps it aligned with other screens and the new proxy route. No behavioral changes to the auth flow; just ensure this asset is present in the backing storage and correctly mapped by the /api/images handler.

apps/web/src/routes/_view/product/workflows.tsx (1)

256-266: Workflows hero icon uses shared proxied Hyprnote icon

The fixed center icon now points at /api/images/hyprnote/icon.png, which is consistent with other pages and the CTA sections. No issues from the draggable/connection-line logic perspective.

apps/web/src/routes/_view/product/ai-assistant.tsx (1)

47-53: AI Assistant hero + CTA images correctly migrated to /api/images

Both the hero GIF (/api/images/hyprnote/ai-assistant.gif) and the CTA icon (/api/images/hyprnote/icon.png) now use the internal image proxy, aligning with the rest of the app. Alt text is appropriate; just ensure these two assets are present and correctly mapped in the backing storage.

Also applies to: 420-427

apps/web/src/routes/join-waitlist.tsx (1)

30-36: Background texture URLs updated correctly.

Both texture asset URLs have been migrated to the internal API proxy paths. The changes are consistent and straightforward.

apps/web/src/routes/__root.tsx (1)

38-50: Social media preview images migrated successfully.

Both og:image and twitter:image meta tags now reference the internal API proxy. This ensures consistent image serving across social platforms.

apps/web/src/routes/_view/product/mini-apps.tsx (3)

49-73: Floating panel tab images migrated correctly.

All five tab image URLs have been updated to use the internal API proxy paths. The changes are consistent and maintain the existing structure.


626-639: Search demo images updated successfully.

All three advanced search image URLs now reference the internal API proxy. No structural changes to the images array.


705-705: CTA section icon URL migrated.

The Hyprnote icon in the call-to-action section now uses the internal API proxy path.

apps/web/src/routes/_view/pricing.tsx (1)

333-333: Icon URL updated correctly.

The CTA section icon now uses the internal API proxy path, consistent with the broader migration pattern.

apps/web/src/routes/_view/about.tsx (1)

51-686: All team and brand asset URLs migrated successfully.

All founder images, team photos, icons, and signature assets have been updated to use internal API proxy paths. The changes are consistent throughout and maintain the existing data structure.

apps/web/src/routes/_view/product/ai-notetaking.tsx (6)

49-177: Hero and tab images migrated correctly.

All image URLs in the tabs array and hero section have been updated to use internal API proxy paths. Changes are consistent and straightforward.


624-668: Transcription section assets updated.

Both on-device transcription illustration images now reference the internal API proxy.


1031-1031: Search section background image migrated.

The background texture for the search section now uses the internal API proxy path.


1095-1122: Mock collaborator avatars updated.

All six mock user avatar URLs have been migrated to the internal API proxy paths.


2070-2093: Floating panel tab images array updated.

All five floating panel images in the second tabs array now reference internal API proxy paths.


2315-2315: CTA section icon migrated.

The Hyprnote icon in the call-to-action section now uses the internal API proxy.

apps/web/src/routes/_view/vs/$slug.tsx (1)

139-139: Asset URL migration is correctly implemented and verified.

The icon source has been successfully updated to use the internal API proxy at /api/images/hyprnote/icon.png. The underlying API route (/api/images/$.ts) is properly implemented with a GET handler that proxies requests to Supabase storage, includes appropriate error handling, and preserves content-type and cache-control headers.

apps/web/src/routes/_view/press-kit.tsx (1)

80-122: LGTM! Consistent proxy path migration.

The changes correctly migrate folder and icon images to the internal API proxy, using appropriate path prefixes:

  • /api/images/icons/ for macOS system icons (folders, mail, GitHub)
  • /api/images/hyprnote/ for the app icon
apps/web/src/routes/_view/changelog/$slug.tsx (1)

263-268: LGTM! Icon path logic preserved.

The conditional icon selection logic remains intact; only the URL paths have been updated to use the internal API proxy.

apps/web/src/routes/_view/changelog/index.tsx (1)

145-150: LGTM! Consistent with changelog detail page.

Icon path updates match the pattern used in the changelog detail page ($slug.tsx), ensuring consistency across the changelog views.

apps/web/src/routes/_view/index.tsx (5)

28-103: LGTM! Main feature and detail feature arrays updated consistently.

All feature image URLs in both mainFeatures and detailsFeatures arrays have been migrated to the internal API proxy paths. The structure and logic remain unchanged.


638-734: LGTM! CoolStuffSection images migrated.

The "No bots" and "No internet" feature images have been updated to use the internal proxy paths consistently across both mobile and desktop layouts.


1073-1282: LGTM! Main features section images updated.

App icon and static placeholder images now use the internal API proxy. The conditional rendering logic and hover states are preserved.


1575-1657: LGTM! Manifesto section assets migrated.

Texture backgrounds (white-leather.png, paper.png), team photos, and signature assets have all been updated to use the internal API proxy paths. The styling and layout remain unchanged.


1762-1854: LGTM! CTA section icon updated.

The final app icon reference in the CTA section has been migrated consistently with all other icon references throughout the page.

apps/web/src/routes/_view/brand.tsx (1)

28-53: Clarify that this change adds an API proxy layer, not a migration away from external storage.

The review mischaracterizes the change. The /api/images/hyprnote/... URLs are not a migration away from Supabase; they still proxy to the same Supabase storage backend (https://ijoptyyjrfqwaqhyxkxj.supabase.co/storage/v1/object/public/public_images) via the API route handler at apps/web/src/routes/api/images.$.ts.

The actual change is:

  • Before: Likely direct Supabase URLs in the VISUAL_ASSETS array (not visible in current state)
  • Now: Client-side URLs route through /api/images/$ which proxies to Supabase

The API route implementation is sound—it properly preserves Content-Type headers, handles 404s, and sets aggressive cache headers (max-age=31536000, immutable). However, this is a proxy layer addition, not an external dependency removal.

The verification requests are valid:

  1. ✓ API proxy route exists and is functional
  2. ✓ Assets are accessible via the new paths (route handles 404s and fetch errors correctly)
  3. ✓ Caching headers are properly configured

Approve the code structure, but correct the narrative to accurately reflect that this centralizes image serving through an API proxy while maintaining the same external Supabase backend.

Likely an incorrect or invalid review comment.

devin-ai-integration bot and others added 2 commits November 28, 2025 01:39
…roxy

- Add sanitizePath function to validate and sanitize the path parameter
- Reject paths with .., backslashes, empty segments, or unsafe characters
- Only allow alphanumeric characters, dots, hyphens, and underscores
- Return 404 for upstream 404 errors, 502 for other upstream errors

Co-Authored-By: yujonglee <yujonglee.dev@gmail.com>
Co-Authored-By: yujonglee <yujonglee.dev@gmail.com>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (1)
apps/web/src/routes/api/images.$.ts (1)

3-4: Make Supabase storage URL environment‑configurable.

Hardcoding the storage URL makes it harder to switch buckets/environments (staging, prod) or change providers. Prefer reading from env with a sane default.

-const SUPABASE_STORAGE_URL =
-  "https://ijoptyyjrfqwaqhyxkxj.supabase.co/storage/v1/object/public/public_images";
+const SUPABASE_STORAGE_URL =
+  process.env.SUPABASE_STORAGE_URL ??
+  "https://ijoptyyjrfqwaqhyxkxj.supabase.co/storage/v1/object/public/public_images";
🧹 Nitpick comments (3)
apps/web/src/routes/api/images.$.ts (3)

6-32: Path sanitization is solid; only tiny nits.

The decode + traversal checks + strict segment whitelist give good protection against path traversal and malformed encodings, and the + inclusion addresses the symbol+logo.png case. The only nits are that segments.length === 0 is unreachable (split always returns at least one element) and could be dropped, and you might add a brief comment explaining the SAFE_SEGMENT policy for future maintainers.


46-53: Add network error handling and propagate upstream status on success.

Two robustness points here:

  1. fetch(url) can throw (DNS, timeout, etc.). Right now that would bubble as a generic 500. Wrapping it in try/catch and returning a 502 keeps the proxy behavior consistent with your explicit non‑OK handling.
  2. For successful responses, forcing status: 200 discards upstream statuses like 206 Partial Content, which can break range requests/partial downloads. Returning response.status preserves upstream semantics.
-        const response = await fetch(url);
+        let response: Response;
+        try {
+          response = await fetch(url);
+        } catch {
+          return new Response("Upstream service error", { status: 502 });
+        }
@@
-        return new Response(response.body, {
-          status: 200,
-          headers,
-        });
+        return new Response(response.body, {
+          status: response.status,
+          headers,
+        });

Also applies to: 68-71


55-66: Confirm assets are truly immutable before using 1‑year Cache-Control.

The default public, max-age=31536000, immutable is great for static, never‑changing assets, but will cause very sticky caching if content ever changes without a path change. Make sure Supabase keys for these images are treated as immutable (e.g., new filenames/paths on change) so you don’t need to shorten the TTL later.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fa5109b and 735e8c8.

📒 Files selected for processing (1)
  • apps/web/src/routes/api/images.$.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.ts: Agent implementations should use TypeScript and follow the established architectural patterns defined in the agent framework
Agent communication should use defined message protocols and interfaces

Files:

  • apps/web/src/routes/api/images.$.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Avoid creating a bunch of types/interfaces if they are not shared. Especially for function props, just inline them instead.
Never do manual state management for form/mutation. Use useForm (from tanstack-form) and useQuery/useMutation (from tanstack-query) instead for 99% of cases. Avoid patterns like setError.
If there are many classNames with conditional logic, use cn (import from @hypr/utils). It is similar to clsx. Always pass an array and split by logical grouping.
Use motion/react instead of framer-motion.

Files:

  • apps/web/src/routes/api/images.$.ts
🧬 Code graph analysis (1)
apps/web/src/routes/api/images.$.ts (1)
apps/web/src/routes/__root.tsx (1)
  • Route (22-63)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: Redirect rules - hyprnote
  • GitHub Check: Header rules - hyprnote
  • GitHub Check: fmt
  • GitHub Check: ci (macos, macos-14)
  • GitHub Check: Pages changed - hyprnote

@yujonglee yujonglee merged commit 858b715 into main Nov 28, 2025
13 of 14 checks passed
@yujonglee yujonglee deleted the devin/1764293014-supabase-proxy branch November 28, 2025 01:50
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.

2 participants