Files
homepage/src/lib/assets/private-images/README.md
T
Alexander 38c3df8187 feat(images): responsive <Image>, gated private images + prose
Build-time image optimization plus auth-gated private content.

- <Image> (src/lib/components/Image.svelte): wraps @sveltejs/enhanced-img
  for public images under src/lib/assets/images/ (AVIF/WebP, multiple
  widths, lazy by default), plus a `private` mode for auth-gated images.
- Private images: scripts/build-private-images.ts encodes sources from
  src/lib/assets/private-images/ into private-assets/ (outside the bundle)
  and a manifest; served only via the auth-checked /private-images/
  endpoint (X-Accel-Redirect in prod, disk read in dev).
- HikeImage gains a `src` prose mode: build-hikes encodes non-waypoint
  images referenced in .svx and exposes them by filename (imagesByName);
  a `private` attr routes them through the gated /hikes/<slug>/private/ path.
- <Private> (src/lib/components/Private.svelte): renders prose only to
  logged-in viewers (cosmetic gating — text still ships in the bundle).
- deploy.sh rsyncs private-assets/; prod needs an nginx internal
  /protected-images/ location.
2026-05-24 20:53:22 +02:00

1.6 KiB

Private (auth-gated) image sources

Drop private source images here, then render them with <Image src="…" private /> from $lib/components/Image.svelte.

These can't use @sveltejs/enhanced-img — its output is a public asset. Instead scripts/build-private-images.ts (runs at prebuild) encodes each image into AVIF/WebP at multiple widths into private-assets/ (gitignored, outside the client bundle) and writes src/lib/data/privateImages.generated.ts. The bytes are served only through the auth-gated endpoint src/routes/private-images/[...file]/+server.ts.

<script>
	import Image from '$lib/components/Image.svelte';
</script>

<!-- `src` is relative to THIS folder; shows a lock badge -->
<Image src="receipt.jpg" private alt="…" />

<!-- gate rendering behind your own auth check too -->
{#if data.session}
	<Image src="family/2024.jpg" private alt="…" sizes="min(1000px, 100vw)" />
{/if}

Setup / notes:

  • Dev: run pnpm exec vite-node scripts/build-private-images.ts once (and after adding/changing images) so the manifest + private-assets/ exist. You must be logged in for the gated endpoint to serve the bytes.

  • Prod (one-time): add an nginx internal location so the bytes are only reachable via the endpoint's X-Accel-Redirect:

    location /protected-images/ {
        internal;
        alias /var/www/static/private-images/;
    }
    

    scripts/deploy.sh rsyncs private-assets//var/www/static/private-images/.

  • These source images are gitignored (private + large). Back them up separately.

  • SVGs are not processed here.