This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { and, eq, inArray } from 'drizzle-orm';
|
||||
|
||||
import {
|
||||
imageDerivativeJobs,
|
||||
inventoryImages,
|
||||
inventoryItems,
|
||||
listings,
|
||||
@@ -42,16 +43,19 @@ export function createInventoryImageRepository(db) {
|
||||
sortOrder: inventoryImages.sortOrder,
|
||||
isPrimary: inventoryImages.isPrimary,
|
||||
processingStatus: inventoryImages.processingStatus,
|
||||
derivatives: imageDerivativeJobs.derivatives,
|
||||
})
|
||||
.from(inventoryImages)
|
||||
.innerJoin(inventoryItems, eq(inventoryItems.id, inventoryImages.inventoryItemId))
|
||||
.innerJoin(products, eq(products.id, inventoryItems.productId))
|
||||
.innerJoin(listings, eq(listings.inventoryItemId, inventoryItems.id))
|
||||
.innerJoin(imageDerivativeJobs, eq(imageDerivativeJobs.inventoryImageId, inventoryImages.id))
|
||||
.where(and(
|
||||
eq(inventoryImages.inventoryItemId, inventoryItemId),
|
||||
eq(inventoryItems.status, 'ACTIVE'),
|
||||
inArray(listings.status, ['AVAILABLE', 'PENDING']),
|
||||
eq(inventoryImages.processingStatus, 'COMPLETED'),
|
||||
eq(imageDerivativeJobs.status, 'COMPLETED'),
|
||||
))
|
||||
.orderBy(inventoryImages.sortOrder);
|
||||
},
|
||||
@@ -62,19 +66,22 @@ export function createInventoryImageRepository(db) {
|
||||
id: inventoryImages.id,
|
||||
storageKey: inventoryImages.storageKey,
|
||||
mimeType: inventoryImages.mimeType,
|
||||
derivatives: imageDerivativeJobs.derivatives,
|
||||
})
|
||||
.from(inventoryImages)
|
||||
.innerJoin(inventoryItems, eq(inventoryItems.id, inventoryImages.inventoryItemId))
|
||||
.innerJoin(products, eq(products.id, inventoryItems.productId))
|
||||
.innerJoin(listings, eq(listings.inventoryItemId, inventoryItems.id))
|
||||
.innerJoin(imageDerivativeJobs, eq(imageDerivativeJobs.inventoryImageId, inventoryImages.id))
|
||||
.where(and(
|
||||
eq(inventoryImages.id, imageId),
|
||||
eq(inventoryItems.status, 'ACTIVE'),
|
||||
inArray(listings.status, ['AVAILABLE', 'PENDING']),
|
||||
eq(inventoryImages.processingStatus, 'COMPLETED'),
|
||||
eq(imageDerivativeJobs.status, 'COMPLETED'),
|
||||
));
|
||||
|
||||
return image ?? null;
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,12 @@ export async function registerPublicImageRoutes(app, options) {
|
||||
const { publicImageService } = options;
|
||||
|
||||
app.get('/public/images/:id', async (request, reply) => {
|
||||
const image = await publicImageService.getContent(request.params.id);
|
||||
const image = await publicImageService.getContent(request.params.id, 'original');
|
||||
return reply.type(image.mimeType).send(image.data);
|
||||
});
|
||||
|
||||
app.get('/public/images/:id/:variant', async (request, reply) => {
|
||||
const image = await publicImageService.getContent(request.params.id, request.params.variant);
|
||||
return reply.type(image.mimeType).send(image.data);
|
||||
});
|
||||
}
|
||||
@@ -6,18 +6,31 @@ export function createPublicImageService({ inventoryImageRepository, imageStorag
|
||||
const images = await inventoryImageRepository.listPublicByInventoryItemId(inventoryItemId);
|
||||
return images.map((image) => ({
|
||||
...image,
|
||||
url: `/api/v1/public/images/${image.id}`,
|
||||
url: `/api/v1/public/images/${image.id}/large`,
|
||||
variants: Object.fromEntries(
|
||||
(Array.isArray(image.derivatives) ? image.derivatives : []).map(({ name }) => [
|
||||
name,
|
||||
`/api/v1/public/images/${image.id}/${name}`,
|
||||
]),
|
||||
),
|
||||
}));
|
||||
},
|
||||
|
||||
async getContent(imageId) {
|
||||
async getContent(imageId, variant = 'original') {
|
||||
const image = await inventoryImageRepository.findPublicById(imageId);
|
||||
|
||||
if (!image) {
|
||||
throw createApplicationError('IMAGE_NOT_FOUND', 'Image was not found.', 404);
|
||||
}
|
||||
|
||||
return { ...image, data: await imageStorage.get(image.storageKey) };
|
||||
const derivative = (Array.isArray(image.derivatives) ? image.derivatives : [])
|
||||
.find((candidate) => candidate.name === variant);
|
||||
const storageKey = derivative?.key ?? image.storageKey;
|
||||
return {
|
||||
...image,
|
||||
mimeType: derivative ? 'image/webp' : image.mimeType,
|
||||
data: await imageStorage.get(storageKey),
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -18,7 +18,8 @@ test('returns public image URLs without exposing storage keys', async () => {
|
||||
id: 'image-1',
|
||||
altText: 'Front of card',
|
||||
sortOrder: 0,
|
||||
url: '/api/v1/public/images/image-1',
|
||||
variants: {},
|
||||
url: '/api/v1/public/images/image-1/large',
|
||||
}]);
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user