Skip to content
Merged

speed #387

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@
"@types/multer": "^2.0.0",
"@types/node": "^22.10.5",
"@types/passport-jwt": "^4.0.1",
"@types/sharp": "^0.31.1",
"@types/sharp": "^0.32.0",
"@types/supertest": "^6.0.2",
"@typescript-eslint/eslint-plugin": "^8.19.1",
"@typescript-eslint/parser": "^8.19.1",
Expand Down
7 changes: 7 additions & 0 deletions src/modules/photo/photo.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,12 @@ export class PhotoController {
createdAt: { type: "string", format: "date-time" },
uploadedBy: { type: "string" },
approvalStatus: { type: "string" },
derivatives: {
type: "object",
additionalProperties: { type: "string" },
description:
"Responsive image URLs (e.g., webp_480, webp_960, webp_1600)",
},
},
},
},
Expand All @@ -242,6 +248,7 @@ export class PhotoController {
createdAt: Date;
uploadedBy: string;
approvalStatus: string;
derivatives: Record<string, string>;
}[]
> {
try {
Expand Down
79 changes: 53 additions & 26 deletions src/modules/photo/photo.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,15 +76,21 @@ export class PhotoService {
format: "webp",
): Promise<Buffer> {
const image = sharp(buffer);
const metadata = await image.metadata();

// Automatically rotate based on EXIF orientation
const rotatedImage = image.rotate();
const metadata = await rotatedImage.metadata();

// Only resize if image is wider than target width
if (metadata.width && metadata.width > width) {
return image.resize(width, null, { withoutEnlargement: true }).toFormat(format, { quality: 80 }).toBuffer();
return rotatedImage
.resize(width, null, { withoutEnlargement: true })
.toFormat(format, { quality: 80 })
.toBuffer();
}

// If image is smaller, just convert format without resizing
return image.toFormat(format, { quality: 80 }).toBuffer();
return rotatedImage.toFormat(format, { quality: 80 }).toBuffer();
}

private async uploadDerivatives(
Expand Down Expand Up @@ -215,23 +221,33 @@ export class PhotoService {
createdAt: Date;
uploadedBy: string;
approvalStatus: string;
derivatives: Record<string, string>;
}[]
> {
const [files] = await this.photoBucket.getFiles();

// Filter out derivative files
const photoFiles = files.filter(
(file) => !file.name.startsWith("derivatives/"),
);

// Get all photos with their approval status
return files.map((file) => ({
name: file.name,
url: this.getPublicPhotoUrl(file.name),
createdAt: file.metadata.timeCreated
? new Date(file.metadata.timeCreated)
: new Date(),
uploadedBy: String(file.metadata.metadata?.uploadedBy || "unknown"),
// If metadata is missing, treat as pending (backward compatibility)
approvalStatus: String(
file.metadata.metadata?.approvalStatus || "pending",
),
}));
return photoFiles.map((file) => {
const photoId = this.extractPhotoIdFromFilename(file.name);
return {
name: file.name,
url: this.getPublicPhotoUrl(file.name),
createdAt: file.metadata.timeCreated
? new Date(file.metadata.timeCreated)
: new Date(),
uploadedBy: String(file.metadata.metadata?.uploadedBy || "unknown"),
// If metadata is missing, treat as pending (backward compatibility)
approvalStatus: String(
file.metadata.metadata?.approvalStatus || "pending",
),
derivatives: this.getDerivativeUrls(photoId),
};
});
}

async updatePhotoApprovalStatus(
Expand Down Expand Up @@ -267,10 +283,15 @@ export class PhotoService {
): Promise<PaginatedPhotosResponse> {
const [files] = await this.photoBucket.getFiles();

// Filter out derivative files first
const photoFiles = files.filter(
(file) => !file.name.startsWith("derivatives/"),
);

// Filter files based on status if provided
let filteredFiles = files;
let filteredFiles = photoFiles;
if (status) {
filteredFiles = files.filter((file) => {
filteredFiles = photoFiles.filter((file) => {
const fileStatus = file.metadata.metadata?.approvalStatus || "pending";
return fileStatus === status;
});
Expand All @@ -291,15 +312,21 @@ export class PhotoService {
const paginatedFiles = filteredFiles.slice(startIndex, endIndex);

// Map files to response format
const photos = paginatedFiles.map((file) => ({
name: file.name,
url: this.getPublicPhotoUrl(file.name),
createdAt: file.metadata.timeCreated
? new Date(file.metadata.timeCreated)
: new Date(),
uploadedBy: String(file.metadata.metadata?.uploadedBy || ""),
approvalStatus: String(file.metadata.metadata?.approvalStatus || "pending"),
}));
const photos = paginatedFiles.map((file) => {
const photoId = this.extractPhotoIdFromFilename(file.name);
return {
name: file.name,
url: this.getPublicPhotoUrl(file.name),
createdAt: file.metadata.timeCreated
? new Date(file.metadata.timeCreated)
: new Date(),
uploadedBy: String(file.metadata.metadata?.uploadedBy || ""),
approvalStatus: String(
file.metadata.metadata?.approvalStatus || "pending",
),
derivatives: this.getDerivativeUrls(photoId),
};
});

return {
photos,
Expand Down
1 change: 1 addition & 0 deletions src/modules/photo/photo.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export interface PhotoItem {
createdAt: Date;
uploadedBy?: string;
approvalStatus?: string;
derivatives?: Record<string, string>;
}

export interface PaginationMeta {
Expand Down
130 changes: 70 additions & 60 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2245,12 +2245,12 @@
"@types/node" "*"
"@types/send" "<1"

"@types/sharp@^0.31.1":
version "0.31.1"
resolved "https://registry.npmjs.org/@types/sharp/-/sharp-0.31.1.tgz"
integrity sha512-5nWwamN9ZFHXaYEincMSuza8nNfOof8nmO+mcI+Agx1uMUk4/pQnNIcix+9rLPXzKrm1pS34+6WRDbDV0Jn7ag==
"@types/sharp@^0.32.0":
version "0.32.0"
resolved "https://registry.yarnpkg.com/@types/sharp/-/sharp-0.32.0.tgz#fc3ac6df6b456319bae807c3d24efdc6631cdd6f"
integrity sha512-OOi3kL+FZDnPhVzsfD37J88FNeZh6gQsGcLc95NbeURRGvmSjeXiDcyWzF2o3yh/gQAUn2uhh/e+CPCa5nwAxw==
dependencies:
"@types/node" "*"
sharp "*"

"@types/stack-utils@^2.0.3":
version "2.0.3"
Expand Down Expand Up @@ -2323,76 +2323,86 @@
"@typescript-eslint/visitor-keys" "8.46.1"
debug "^4.3.4"

"@typescript-eslint/project-service@8.46.2":
version "8.46.2"
resolved "https://registry.yarnpkg.com/@typescript-eslint/project-service/-/project-service-8.46.2.tgz#ab2f02a0de4da6a7eeb885af5e059be57819d608"
integrity sha512-PULOLZ9iqwI7hXcmL4fVfIsBi6AN9YxRc0frbvmg8f+4hQAjQ5GYNKK0DIArNo+rOKmR/iBYwkpBmnIwin4wBg==
"@typescript-eslint/project-service@8.46.1":
version "8.46.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/project-service/-/project-service-8.46.1.tgz#07be0e6f27fa90a17d8e5f6996ee02329c9a8c2e"
integrity sha512-FOIaFVMHzRskXr5J4Jp8lFVV0gz5ngv3RHmn+E4HYxSJ3DgDzU7fVI1/M7Ijh1zf6S7HIoaIOtln1H5y8V+9Zg==
dependencies:
"@typescript-eslint/tsconfig-utils" "^8.46.2"
"@typescript-eslint/types" "^8.46.2"
"@typescript-eslint/tsconfig-utils" "^8.46.1"
"@typescript-eslint/types" "^8.46.1"
debug "^4.3.4"

"@typescript-eslint/scope-manager@8.46.2":
version "8.46.2"
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.46.2.tgz#7d37df2493c404450589acb3b5d0c69cc0670a88"
integrity sha512-LF4b/NmGvdWEHD2H4MsHD8ny6JpiVNDzrSZr3CsckEgCbAGZbYM4Cqxvi9L+WqDMT+51Ozy7lt2M+d0JLEuBqA==
"@typescript-eslint/scope-manager@8.46.1":
version "8.46.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.46.1.tgz#590dd2e65e95af646bdaf50adeae9af39e25e8c1"
integrity sha512-weL9Gg3/5F0pVQKiF8eOXFZp8emqWzZsOJuWRUNtHT+UNV2xSJegmpCNQHy37aEQIbToTq7RHKhWvOsmbM680A==
dependencies:
"@typescript-eslint/types" "8.46.2"
"@typescript-eslint/visitor-keys" "8.46.2"
"@typescript-eslint/types" "8.46.1"
"@typescript-eslint/visitor-keys" "8.46.1"

"@typescript-eslint/tsconfig-utils@8.46.2", "@typescript-eslint/tsconfig-utils@^8.46.2":
"@typescript-eslint/tsconfig-utils@8.46.1":
version "8.46.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.46.1.tgz#24405888560175c6c209c39df11ac06a2efef9d7"
integrity sha512-X88+J/CwFvlJB+mK09VFqx5FE4H5cXD+H/Bdza2aEWkSb8hnWIQorNcscRl4IEo1Cz9VI/+/r/jnGWkbWPx54g==

"@typescript-eslint/tsconfig-utils@^8.46.1":
version "8.46.2"
resolved "https://registry.yarnpkg.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.46.2.tgz#d110451cb93bbd189865206ea37ef677c196828c"
integrity sha512-a7QH6fw4S57+F5y2FIxxSDyi5M4UfGF+Jl1bCGd7+L4KsaUY80GsiF/t0UoRFDHAguKlBaACWJRmdrc6Xfkkag==

"@typescript-eslint/type-utils@8.46.2":
version "8.46.2"
resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.46.2.tgz#802d027864e6fb752e65425ed09f3e089fb4d384"
integrity sha512-HbPM4LbaAAt/DjxXaG9yiS9brOOz6fabal4uvUmaUYe6l3K1phQDMQKBRUrr06BQkxkvIZVVHttqiybM9nJsLA==
"@typescript-eslint/type-utils@8.46.1":
version "8.46.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.46.1.tgz#14d4307dd6045f6b48a888cde1513d6ec305537f"
integrity sha512-+BlmiHIiqufBxkVnOtFwjah/vrkF4MtKKvpXrKSPLCkCtAp8H01/VV43sfqA98Od7nJpDcFnkwgyfQbOG0AMvw==
dependencies:
"@typescript-eslint/types" "8.46.2"
"@typescript-eslint/typescript-estree" "8.46.2"
"@typescript-eslint/utils" "8.46.2"
"@typescript-eslint/types" "8.46.1"
"@typescript-eslint/typescript-estree" "8.46.1"
"@typescript-eslint/utils" "8.46.1"
debug "^4.3.4"
ts-api-utils "^2.1.0"

"@typescript-eslint/types@8.46.2", "@typescript-eslint/types@^8.46.2":
"@typescript-eslint/types@8.46.1":
version "8.46.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.46.1.tgz#4c5479538ec10b5508b8e982e172911c987446d8"
integrity sha512-C+soprGBHwWBdkDpbaRC4paGBrkIXxVlNohadL5o0kfhsXqOC6GYH2S/Obmig+I0HTDl8wMaRySwrfrXVP8/pQ==

"@typescript-eslint/types@^8.46.1":
version "8.46.2"
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.46.2.tgz#2bad7348511b31e6e42579820e62b73145635763"
integrity sha512-lNCWCbq7rpg7qDsQrd3D6NyWYu+gkTENkG5IKYhUIcxSb59SQC/hEQ+MrG4sTgBVghTonNWq42bA/d4yYumldQ==

"@typescript-eslint/typescript-estree@8.46.2":
version "8.46.2"
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.46.2.tgz#ab547a27e4222bb6a3281cb7e98705272e2c7d08"
integrity sha512-f7rW7LJ2b7Uh2EiQ+7sza6RDZnajbNbemn54Ob6fRwQbgcIn+GWfyuHDHRYgRoZu1P4AayVScrRW+YfbTvPQoQ==
"@typescript-eslint/typescript-estree@8.46.1":
version "8.46.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.46.1.tgz#1c146573b942ebe609c156c217ceafdc7a88e6ed"
integrity sha512-uIifjT4s8cQKFQ8ZBXXyoUODtRoAd7F7+G8MKmtzj17+1UbdzFl52AzRyZRyKqPHhgzvXunnSckVu36flGy8cg==
dependencies:
"@typescript-eslint/project-service" "8.46.2"
"@typescript-eslint/tsconfig-utils" "8.46.2"
"@typescript-eslint/types" "8.46.2"
"@typescript-eslint/visitor-keys" "8.46.2"
"@typescript-eslint/project-service" "8.46.1"
"@typescript-eslint/tsconfig-utils" "8.46.1"
"@typescript-eslint/types" "8.46.1"
"@typescript-eslint/visitor-keys" "8.46.1"
debug "^4.3.4"
fast-glob "^3.3.2"
is-glob "^4.0.3"
minimatch "^9.0.4"
semver "^7.6.0"
ts-api-utils "^2.1.0"

"@typescript-eslint/utils@8.46.2":
version "8.46.2"
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.46.2.tgz#b313d33d67f9918583af205bd7bcebf20f231732"
integrity sha512-sExxzucx0Tud5tE0XqR0lT0psBQvEpnpiul9XbGUB1QwpWJJAps1O/Z7hJxLGiZLBKMCutjTzDgmd1muEhBnVg==
"@typescript-eslint/utils@8.46.1":
version "8.46.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.46.1.tgz#c572184d9227d66b10a954b90249a20c48b22452"
integrity sha512-vkYUy6LdZS7q1v/Gxb2Zs7zziuXN0wxqsetJdeZdRe/f5dwJFglmuvZBfTUivCtjH725C1jWCDfpadadD95EDQ==
dependencies:
"@eslint-community/eslint-utils" "^4.7.0"
"@typescript-eslint/scope-manager" "8.46.2"
"@typescript-eslint/types" "8.46.2"
"@typescript-eslint/typescript-estree" "8.46.2"
"@typescript-eslint/scope-manager" "8.46.1"
"@typescript-eslint/types" "8.46.1"
"@typescript-eslint/typescript-estree" "8.46.1"

"@typescript-eslint/visitor-keys@8.46.2":
version "8.46.2"
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.46.2.tgz#803fa298948c39acf810af21bdce6f8babfa9738"
integrity sha512-tUFMXI4gxzzMXt4xpGJEsBsTox0XbNQ1y94EwlD/CuZwFcQP79xfQqMhau9HsRc/J0cAPA/HZt1dZPtGn9V/7w==
"@typescript-eslint/visitor-keys@8.46.1":
version "8.46.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.46.1.tgz#da35f1d58ec407419d68847cfd358b32746ac315"
integrity sha512-ptkmIf2iDkNUjdeu2bQqhFPV1m6qTnFFjg7PPDjxKWaMaP0Z6I9l30Jr3g5QqbZGdw8YdYvLp+XnqnWWZOg/NA==
dependencies:
"@typescript-eslint/types" "8.46.2"
"@typescript-eslint/types" "8.46.1"
eslint-visitor-keys "^4.2.1"

"@ungap/structured-clone@^1.3.0":
Expand Down Expand Up @@ -4468,10 +4478,10 @@ gcp-metadata@^6.1.0:
google-logging-utils "^0.0.2"
json-bigint "^1.0.0"

gcp-metadata@^8.0.0:
version "8.1.1"
resolved "https://registry.yarnpkg.com/gcp-metadata/-/gcp-metadata-8.1.1.tgz#672f48cca050d543bfe185ec0b7978a14d775298"
integrity sha512-dTCcAe9fRQf06ELwel6lWWFrEbstwjUBYEhr5VRGoC+iPDZQucHppCowaIp8b8v92tU1G4X4H3b/Y6zXZxkMsQ==
gcp-metadata@^7.0.0:
version "7.0.1"
resolved "https://registry.yarnpkg.com/gcp-metadata/-/gcp-metadata-7.0.1.tgz#43bb9cd482cf0590629b871ab9133af45b78382d"
integrity sha512-UcO3kefx6dCcZkgcTGgVOTFb7b1LlQ02hY1omMjjrrBzkajRMCFgYOjs7J71WqnuG1k2b+9ppGL7FsOfhZMQKQ==
dependencies:
gaxios "^7.0.0"
google-logging-utils "^1.0.0"
Expand Down Expand Up @@ -5681,10 +5691,10 @@ lines-and-columns@^1.1.6:
resolved "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz"
integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==

load-esm@1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/load-esm/-/load-esm-1.0.3.tgz#2073afe3da63902c323e80d9f135c301173ac92c"
integrity sha512-v5xlu8eHD1+6r8EHTg6hfmO97LN8ugKtiXcy5e6oN72iD2r6u0RPfLl6fxM+7Wnh2ZRq15o0russMst44WauPA==
load-esm@1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/load-esm/-/load-esm-1.0.2.tgz#35dbac8a1a3abdb802cf236008048fcc8a9289a6"
integrity sha512-nVAvWk/jeyrWyXEAs84mpQCYccxRqgKY4OznLuJhJCa0XsPSfdOIr2zvBZEj3IHEHbX97jjscKRRV539bW0Gpw==

loader-runner@^4.2.0:
version "4.3.0"
Expand Down Expand Up @@ -6725,16 +6735,16 @@ path-scurry@^2.0.0:
lru-cache "^11.0.0"
minipass "^7.1.2"

path-to-regexp@8.2.0, path-to-regexp@^8.0.0:
version "8.2.0"
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-8.2.0.tgz#73990cc29e57a3ff2a0d914095156df5db79e8b4"
integrity sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==

path-to-regexp@8.3.0:
version "8.3.0"
resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz"
integrity sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==

path-to-regexp@^8.0.0:
version "8.2.0"
resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.2.0.tgz"
integrity sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==

path-type@^4.0.0:
version "4.0.0"
resolved "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz"
Expand Down Expand Up @@ -7234,7 +7244,7 @@ setprototypeof@1.2.0:
resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424"
integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==

sharp@^0.34.4:
sharp@*, sharp@^0.34.4:
version "0.34.4"
resolved "https://registry.npmjs.org/sharp/-/sharp-0.34.4.tgz"
integrity sha512-FUH39xp3SBPnxWvd5iib1X8XY7J0K0X7d93sie9CJg2PO8/7gmg89Nve6OjItK53/MlAushNNxteBYfM6DEuoA==
Expand Down