IO-3092 WIP on img proxy thumbnail generation.
This commit is contained in:
@@ -2252,7 +2252,7 @@ exports.UPDATE_PARTS_CRITICAL = `mutation UPDATE_PARTS_CRITICAL ($IdsToMarkCriti
|
||||
notcritical: update_joblines(where: {id: {_nin: $IdsToMarkCritical}, jobid: {_eq: $jobid}}, _set: {critical: false}) {
|
||||
affected_rows
|
||||
}
|
||||
}`
|
||||
}`;
|
||||
|
||||
exports.ACTIVE_SHOP_BY_USER = `query ACTIVE_SHOP_BY_USER($user: String) {
|
||||
associations(where: {active: {_eq: true}, useremail: {_eq: $user}}) {
|
||||
@@ -2618,7 +2618,6 @@ exports.CREATE_CONVERSATION = `mutation CREATE_CONVERSATION($conversation: [conv
|
||||
}
|
||||
`;
|
||||
|
||||
|
||||
exports.STATUS_UPDATE = `query STATUS_UPDATE($period: timestamptz!, $today: timestamptz!) {
|
||||
bodyshops(where: { created_at: { _gte: $period } }) {
|
||||
shopname
|
||||
@@ -2689,4 +2688,37 @@ exports.STATUS_UPDATE = `query STATUS_UPDATE($period: timestamptz!, $today: time
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
`;
|
||||
|
||||
exports.GET_DOCUMENTS_BY_JOB = `
|
||||
query GET_DOCUMENTS_BY_JOB($jobId: uuid!) {
|
||||
jobs_by_pk(id: $jobId) {
|
||||
id
|
||||
ro_number
|
||||
}
|
||||
documents_aggregate(where: { jobid: { _eq: $jobId } }) {
|
||||
aggregate {
|
||||
sum {
|
||||
size
|
||||
}
|
||||
}
|
||||
}
|
||||
documents(order_by: { takenat: desc }, where: { jobid: { _eq: $jobId } }) {
|
||||
id
|
||||
name
|
||||
key
|
||||
type
|
||||
size
|
||||
takenat
|
||||
extension
|
||||
bill {
|
||||
id
|
||||
invoice_number
|
||||
date
|
||||
vendor {
|
||||
id
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
}`;
|
||||
|
||||
@@ -3,13 +3,16 @@ require("dotenv").config({
|
||||
path: path.resolve(process.cwd(), `.env.${process.env.NODE_ENV || "development"}`)
|
||||
});
|
||||
const logger = require("../utils/logger");
|
||||
const { S3Client, PutObjectCommand } = require("@aws-sdk/client-s3");
|
||||
const { S3Client, PutObjectCommand, GetObjectCommand } = require("@aws-sdk/client-s3");
|
||||
const { getSignedUrl } = require("@aws-sdk/s3-request-presigner");
|
||||
const crypto = require("crypto");
|
||||
const { InstanceRegion } = require("../utils/instanceMgr");
|
||||
|
||||
const { GET_DOCUMENTS_BY_JOB } = require("../graphql-client/queries");
|
||||
//TODO: Remove hardcoded values.
|
||||
const imgproxyBaseUrl = process.env.IMGPROXY_BASE_URL || `https://d3ictiiutovkvi.cloudfront.net`;
|
||||
const imgproxyBaseUrl =
|
||||
process.env.IMGPROXY_BASE_URL ||
|
||||
// `https://k2car6fha7w5cbgry3j2td56ra0kdmwn.lambda-url.ca-central-1.on.aws` ||
|
||||
`https://d3ictiiutovkvi.cloudfront.net`;
|
||||
const imgproxyKey = process.env.IMGPROXY_KEY || `secret`;
|
||||
const imgproxySalt = process.env.IMGPROXY_SALT || `salt`;
|
||||
const imgproxyDestinationBucket = process.env.IMGPROXY_DESTINATION_BUCKET || `imex-shop-media`;
|
||||
@@ -36,7 +39,7 @@ exports.generateSignedUploadUrls = async (req, res) => {
|
||||
const client = new S3Client({ region: InstanceRegion() });
|
||||
const command = new PutObjectCommand({ Bucket: imgproxyDestinationBucket, Key: key });
|
||||
const presignedUrl = await getSignedUrl(client, command, { expiresIn: 360 });
|
||||
signedUrls.push({ filename, presignedUrl });
|
||||
signedUrls.push({ filename, presignedUrl, key });
|
||||
}
|
||||
|
||||
logger.log("imgproxy-upload-success", "DEBUG", req.user?.email, jobid, { signedUrls });
|
||||
@@ -58,30 +61,25 @@ exports.generateSignedUploadUrls = async (req, res) => {
|
||||
};
|
||||
|
||||
exports.getThumbnailUrls = async (req, res) => {
|
||||
const { jobid } = req.body;
|
||||
const { jobid, billid } = req.body;
|
||||
|
||||
try {
|
||||
//TODO: Query for all documents related to the job.
|
||||
//Delayed as the key structure may change slightly from what it is currently and will require evaluating mobile components.
|
||||
// const { data } = await client.query({
|
||||
// query: queries.GET_DOCUMENTS_BY_JOBID,
|
||||
// variables: { jobid }
|
||||
// });
|
||||
|
||||
//Mocked Keys.
|
||||
const keys = [
|
||||
"shopid/jobid/test2.jpg-1737502469411",
|
||||
"shopid/jobid/test2.jpg-1737502469411",
|
||||
"shopid/jobid/movie.mov-1737504997897",
|
||||
"shopid/jobid/pdf.pdf-1737504944260"
|
||||
];
|
||||
const client = req.userGraphQLClient;
|
||||
const data = await client.request(GET_DOCUMENTS_BY_JOB, { jobId: jobid });
|
||||
|
||||
const thumbResizeParams = `rs:fill:250:250:1/g:ce`;
|
||||
const proxiedUrls = keys.map((key) => {
|
||||
const s3client = new S3Client({ region: InstanceRegion() });
|
||||
const proxiedUrls = [];
|
||||
|
||||
for (const document of data.documents) {
|
||||
//Format to follow:
|
||||
//<Cloudfront_to_lambdal>/<hmac with SHA of entire request URI path (with base64 encoded URL if needed), beginning with unencoded/unhashed Salt>/<remainder of url - resize params >/< base 64 URL encoded to image path>
|
||||
|
||||
// Build the S3 path to the object.
|
||||
const fullS3Path = `s3://${imgproxyDestinationBucket}/${key}`;
|
||||
const fullS3Path = `s3://${imgproxyDestinationBucket}/${document.key}`;
|
||||
const base64UrlEncodedKeyString = base64UrlEncode(fullS3Path);
|
||||
//Thumbnail Generation Block
|
||||
const thumbProxyPath = `${thumbResizeParams}/${base64UrlEncodedKeyString}`;
|
||||
@@ -92,15 +90,30 @@ exports.getThumbnailUrls = async (req, res) => {
|
||||
const fullSizeProxyPath = `${base64UrlEncodedKeyString}`;
|
||||
const fullSizeHmacSalt = createHmacSha256(`${imgproxySalt}/${fullSizeProxyPath}`);
|
||||
|
||||
//If not a picture, we need to get a signed download link to the file using S3 (or cloudfront preferably)
|
||||
const s3Props = {};
|
||||
if (!document.type.startsWith("image")) {
|
||||
//If not a picture, we need to get a signed download link to the file using S3 (or cloudfront preferably)
|
||||
const command = new GetObjectCommand({ Bucket: imgproxyDestinationBucket, Key: document.key });
|
||||
const presignedGetUrl = await getSignedUrl(s3client, command, { expiresIn: 360 });
|
||||
s3Props.presignedGetUrl = presignedGetUrl;
|
||||
|
||||
return {
|
||||
const originalProxyPath = `raw:1/${base64UrlEncodedKeyString}`;
|
||||
const originalHmacSalt = createHmacSha256(`${imgproxySalt}/${originalProxyPath}`);
|
||||
s3Props.originalUrlViaProxyPath = `${imgproxyBaseUrl}/${originalHmacSalt}/${originalProxyPath}`;
|
||||
}
|
||||
|
||||
proxiedUrls.push({
|
||||
originalUrl: `${imgproxyBaseUrl}/${fullSizeHmacSalt}/${fullSizeProxyPath}`,
|
||||
thumbnailUrl: `${imgproxyBaseUrl}/${thumbHmacSalt}/${thumbProxyPath}`
|
||||
};
|
||||
});
|
||||
thumbnailUrl: `${imgproxyBaseUrl}/${thumbHmacSalt}/${thumbProxyPath}`,
|
||||
fullS3Path,
|
||||
base64UrlEncodedKeyString,
|
||||
thumbProxyPath,
|
||||
...s3Props,
|
||||
...document
|
||||
});
|
||||
}
|
||||
|
||||
res.json({ proxiedUrls });
|
||||
res.json(proxiedUrls);
|
||||
//Iterate over them, build the link based on the media type, and return the array.
|
||||
} catch (error) {
|
||||
logger.log("imgproxy-get-proxied-urls-error", "ERROR", req.user?.email, jobid, {
|
||||
@@ -124,8 +137,12 @@ exports.deleteFiles = async (req, res) => {
|
||||
//Mark as deleted from the documents section of the database.
|
||||
};
|
||||
|
||||
//Gerneate a key for the s3 bucket by popping off the extension, add a timestamp, and add back the extension.
|
||||
//This is to prevent any collisions/duplicates in the bucket.
|
||||
function GenerateKey({ bodyshopid, jobid, filename }) {
|
||||
return `${bodyshopid}/${jobid}/${filename}-${Date.now()}`;
|
||||
let nameArray = filename.split(".");
|
||||
let extension = nameArray.pop();
|
||||
return `${bodyshopid}/${jobid}/${nameArray.join(".")}-${Date.now()}.${extension}`;
|
||||
}
|
||||
|
||||
function base64UrlEncode(str) {
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
const express = require("express");
|
||||
const router = express.Router();
|
||||
const { createSignedUploadURL, downloadFiles, renameKeys, deleteFiles } = require("../media/media");
|
||||
const {
|
||||
generateSignedUploadUrls,
|
||||
getThumbnailUrls
|
||||
} = require("../media/imgprox-media");
|
||||
const { generateSignedUploadUrls, getThumbnailUrls } = require("../media/imgprox-media");
|
||||
const validateFirebaseIdTokenMiddleware = require("../middleware/validateFirebaseIdTokenMiddleware");
|
||||
const withUserGraphQLClientMiddleware = require("../middleware/withUserGraphQLClientMiddleware");
|
||||
|
||||
router.use(validateFirebaseIdTokenMiddleware);
|
||||
router.use(withUserGraphQLClientMiddleware);
|
||||
|
||||
router.post("/sign", createSignedUploadURL);
|
||||
router.post("/download", downloadFiles);
|
||||
@@ -15,6 +14,6 @@ router.post("/rename", renameKeys);
|
||||
router.post("/delete", deleteFiles);
|
||||
|
||||
router.post("/proxy/sign", generateSignedUploadUrls);
|
||||
router.post("/proxy/get", getThumbnailUrls);
|
||||
router.post("/proxy/thumbnails", getThumbnailUrls);
|
||||
|
||||
module.exports = router;
|
||||
|
||||
Reference in New Issue
Block a user