diff --git a/components/photos/ListPhotos.tsx b/components/photos/ListPhotos.tsx
index d740229..a4641e1 100644
--- a/components/photos/ListPhotos.tsx
+++ b/components/photos/ListPhotos.tsx
@@ -48,7 +48,7 @@ export default function ListPhotos(
{isImage
? (
= {
+ async GET(request, context) {
+ if (!context.state.user) {
+ return new Response('Redirect', { status: 303, headers: { 'Location': `/login` } });
+ }
+
+ const { fileName } = context.params;
+
+ if (!fileName) {
+ return new Response('Not Found', { status: 404 });
+ }
+
+ const searchParams = new URL(request.url).searchParams;
+
+ let currentPath = searchParams.get('path') || '/';
+
+ // Send invalid paths back to root
+ if (!currentPath.startsWith('/') || currentPath.includes('../')) {
+ currentPath = '/';
+ }
+
+ // Always append a trailing slash
+ if (!currentPath.endsWith('/')) {
+ currentPath = `${currentPath}/`;
+ }
+
+ const fileResult = await getFile(context.state.user.id, currentPath, decodeURIComponent(fileName));
+
+ if (!fileResult.success) {
+ return new Response('Not Found', { status: 404 });
+ }
+
+ const width = parseInt(searchParams.get('width') || '500', 10);
+ const height = parseInt(searchParams.get('height') || '500', 10);
+
+ if (
+ fileResult.contentType?.split('/')[0] !== 'image' || isNaN(width) || isNaN(height) ||
+ width > MAX_WIDTH || height > MAX_HEIGHT || width < MIN_WIDTH || height < MIN_HEIGHT
+ ) {
+ return new Response('Bad Request', { status: 400 });
+ }
+
+ await initialize();
+
+ const sizingData = new MagickGeometry(
+ width,
+ height,
+ );
+ sizingData.ignoreAspectRatio = false;
+
+ const resizedImageContents = await new Promise((resolve) => {
+ ImageMagick.read(fileResult.contents!, (image) => {
+ image.resize(sizingData);
+ image.write((data) => resolve(data));
+ });
+ });
+
+ return new Response(resizedImageContents, {
+ status: 200,
+ headers: {
+ 'cache-control': `max-age=${604_800}`, // Tell browsers to cache for 1 week (60 * 60 * 24 * 7 = 604_800)
+ 'content-type': fileResult.contentType!,
+ 'content-length': resizedImageContents.byteLength.toString(),
+ },
+ });
+ },
+};