From 0cfd200ac5a2da79c350537293e25072fef32d62 Mon Sep 17 00:00:00 2001 From: Alex Rennie-Lis Date: Sun, 28 Jun 2026 23:21:59 +0100 Subject: [PATCH] Naturalised env import --- src/routes/api/download/+server.js | 31 +++++++++++-------------- src/routes/api/photos/+server.js | 37 ++++++++++++++---------------- src/routes/login/+page.server.js | 5 ++-- 3 files changed, 32 insertions(+), 41 deletions(-) diff --git a/src/routes/api/download/+server.js b/src/routes/api/download/+server.js index 2cf4733..0a022f4 100644 --- a/src/routes/api/download/+server.js +++ b/src/routes/api/download/+server.js @@ -1,30 +1,27 @@ -// src/routes/api/download/+server.js import { redirect, error } from '@sveltejs/kit'; import { S3Client, GetObjectCommand } from '@aws-sdk/client-s3'; import { getSignedUrl } from '@aws-sdk/s3-request-presigner'; -import { - B2_ENDPOINT, B2_REGION, B2_ACCESS_KEY_ID, B2_SECRET_ACCESS_KEY, B2_BUCKET_NAME -} from '$env/dynamic/private'; - -const s3 = new S3Client({ - endpoint: B2_ENDPOINT, - region: B2_REGION, - credentials: { - accessKeyId: B2_ACCESS_KEY_ID, - secretAccessKey: B2_SECRET_ACCESS_KEY - } -}); +import { env } from '$env/dynamic/private'; export async function GET({ url }) { const id = url.searchParams.get('id'); if (!id) throw error(400, 'Missing photo identity ID'); try { + const s3 = new S3Client({ + endpoint: env.B2_ENDPOINT, + region: env.B2_REGION, + credentials: { + accessKeyId: env.B2_ACCESS_KEY_ID, + secretAccessKey: env.B2_SECRET_ACCESS_KEY + } + }); + const fullSizeKey = `images/${id}`; - // Create a command that forces the browser to treat this as an attachment download + // Force the browser to treat this as an attachment download const command = new GetObjectCommand({ - Bucket: B2_BUCKET_NAME, + Bucket: env.B2_BUCKET_NAME, Key: fullSizeKey, ResponseContentDisposition: `attachment; filename="${id}"` }); @@ -32,12 +29,10 @@ export async function GET({ url }) { // Generate a quick 60-second valid link const downloadUrl = await getSignedUrl(s3, command, { expiresIn: 60 }); - // Redirect the user directly to the freshly generated download link + // Redirect browser directly to the download stream throw redirect(307, downloadUrl); } catch (err) { - // SvelteKit uses redirect exceptions, so let them pass through cleanly if (err.status === 307) throw err; - console.error('Server download redirection failed:', err); throw error(500, 'Could not generate download routing asset.'); } diff --git a/src/routes/api/photos/+server.js b/src/routes/api/photos/+server.js index eff21d5..e3bbf53 100644 --- a/src/routes/api/photos/+server.js +++ b/src/routes/api/photos/+server.js @@ -1,48 +1,46 @@ import { json, error } from '@sveltejs/kit'; import { S3Client, ListObjectsV2Command, GetObjectCommand } from '@aws-sdk/client-s3'; import { getSignedUrl } from '@aws-sdk/s3-request-presigner'; -import { - B2_ENDPOINT, B2_REGION, B2_ACCESS_KEY_ID, B2_SECRET_ACCESS_KEY, B2_BUCKET_NAME -} from '$env/dynamic/private'; - -const s3 = new S3Client({ - endpoint: B2_ENDPOINT, - region: B2_REGION, - credentials: { - accessKeyId: B2_ACCESS_KEY_ID, - secretAccessKey: B2_SECRET_ACCESS_KEY - } -}); +import { env } from '$env/dynamic/private'; export async function GET({ url }) { const nextToken = url.searchParams.get('next') || undefined; try { - // 1. Fetch a batch of files from the thumbs/ folder + const s3 = new S3Client({ + endpoint: env.B2_ENDPOINT, + region: env.B2_REGION, + credentials: { + accessKeyId: env.B2_ACCESS_KEY_ID, + secretAccessKey: env.B2_SECRET_ACCESS_KEY + } + }); + + // Fetch a batch of files from the thumbs/ folder const listCommand = new ListObjectsV2Command({ - Bucket: B2_BUCKET_NAME, + Bucket: env.B2_BUCKET_NAME, Prefix: 'thumbs/', - MaxKeys: 40, // Increased slight headroom since we filter on the server side now + MaxKeys: 40, ContinuationToken: nextToken }); const listResponse = await s3.send(listCommand); const contents = listResponse.Contents || []; - // 2. Strict Filter: Only allow actual .jpg or .jpeg extensions + // Strict Filter: Only allow actual .jpg or .jpeg extensions const imageFiles = contents.filter(file => { const lowerKey = file.Key.toLowerCase(); return lowerKey.endsWith('.jpg') || lowerKey.endsWith('.jpeg'); }); - // 3. Generate presigned URLs for validated items + // Generate presigned URLs for validated items const photos = await Promise.all( imageFiles.map(async (file) => { const id = file.Key.replace('thumbs/', ''); const fullSizeKey = `images/${id}`; - const thumbCommand = new GetObjectCommand({ Bucket: B2_BUCKET_NAME, Key: file.Key }); - const fullCommand = new GetObjectCommand({ Bucket: B2_BUCKET_NAME, Key: fullSizeKey }); + const thumbCommand = new GetObjectCommand({ Bucket: env.B2_BUCKET_NAME, Key: file.Key }); + const fullCommand = new GetObjectCommand({ Bucket: env.B2_BUCKET_NAME, Key: fullSizeKey }); // 10 minutes = 600 seconds const [thumbUrl, fullUrl] = await Promise.all([ @@ -56,7 +54,6 @@ export async function GET({ url }) { return json({ photos, - // Pass the original continuation token back so pagination structural flow remains intact nextContinuationToken: listResponse.NextContinuationToken || null }); } catch (err) { diff --git a/src/routes/login/+page.server.js b/src/routes/login/+page.server.js index 5eea3f3..c14e0ea 100644 --- a/src/routes/login/+page.server.js +++ b/src/routes/login/+page.server.js @@ -1,13 +1,12 @@ -// src/routes/login/+page.server.js import { fail, redirect } from '@sveltejs/kit'; -import { GALLERY_PASSWORD } from '$env/dynamic/private'; +import { env } from '$env/dynamic/private'; export const actions = { default: async ({ request, cookies }) => { const data = await request.formData(); const password = data.get('password'); - if (password === GALLERY_PASSWORD) { + if (password === env.GALLERY_PASSWORD) { cookies.set('gallery_session', 'authenticated', { path: '/', httpOnly: true,