UNPKG

@edge-store/react

Version:

Image Handling for React/Next.js

149 lines (148 loc) 5.56 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const client_s3_1 = require("@aws-sdk/client-s3"); const s3_request_presigner_1 = require("@aws-sdk/s3-request-presigner"); const uuid_1 = require("uuid"); const jose_1 = require("jose"); const hkdf_1 = require("@panva/hkdf"); const cookie_1 = require("cookie"); const DEFAULT_MAX_AGE = 30 * 24 * 60 * 60; function AWSProvider(options) { const { accessKeyId = process.env.ES_AWS_ACCESS_KEY_ID, secretAccessKey = process.env.ES_AWS_SECRET_ACCESS_KEY, region = process.env.ES_AWS_REGION, bucketName = process.env.ES_AWS_BUCKET_NAME, } = options || {}; const credentials = accessKeyId && secretAccessKey ? { accessKeyId, secretAccessKey, } : undefined; const s3Client = new client_s3_1.S3Client({ region, credentials }); const baseUrl = process.env.EDGE_STORE_BASE_URL || `https://${bucketName}.s3.${region}.amazonaws.com`; return { init: async (req, res) => { const ctx = (options === null || options === void 0 ? void 0 : options.createContext) ? await options.createContext({ req, res }) : {}; const token = await encryptJWT(ctx); res.setHeader("Set-Cookie", (0, cookie_1.serialize)("edgestore", token, { path: "/", maxAge: DEFAULT_MAX_AGE, })); res.json({ token, baseUrl, }); }, requestUpload: async (req, res) => { var _a, _b; console.log("requestUpload", req.body); const ctx = await getContext(req); const pathPrefix = (options === null || options === void 0 ? void 0 : options.pathPrefix) ? await options.pathPrefix({ req, res, ctx }) : ""; const fileName = (_a = req.body.name) !== null && _a !== void 0 ? _a : `${(0, uuid_1.v4)()}.${req.body.extension.replace(".", "")}`; const filePath = pathPrefix + ((_b = req.body.path) !== null && _b !== void 0 ? _b : "/"); // TODO: handle public path const fileKey = `${filePath}${fileName}`.replace(/^\//, ""); if (!req.body.overwrite) { // check if file already exists const command = new client_s3_1.HeadObjectCommand({ Bucket: bucketName, Key: fileKey, }); try { await s3Client.send(command); throw new Error("File already exists"); } catch { // file does not exist, continue } } const command = new client_s3_1.PutObjectCommand({ Bucket: bucketName, Key: fileKey, ContentType: getMimeType(req.body.extension), }); const signedUrl = await (0, s3_request_presigner_1.getSignedUrl)(s3Client, command, { expiresIn: 60 * 60, // 1 hour }); if (options === null || options === void 0 ? void 0 : options.onRequestUpload) { await options.onRequestUpload({ req, res, ctx, fileInfo: { key: fileKey, size: req.body.size, }, }); } const url = `${baseUrl}/${fileKey}`; res.json({ signedUrl, url, path: "/" + fileKey, }); }, requestAccess: (req, res) => { // TODO: Implement res.json({ success: true, }); }, }; } exports.default = AWSProvider; const getMimeType = (extension) => { const parsedExtension = extension.toLowerCase().replace(".", ""); switch (parsedExtension) { case "png": return "image/png"; case "jpg": case "jpeg": return "image/jpeg"; case "gif": return "image/gif"; case "webp": return "image/webp"; case "svg": return "image/svg+xml"; case "bmp": return "image/bmp"; default: return "application/octet-stream"; } }; async function encryptJWT(ctx) { const secret = process.env.ES_SECRET; if (!secret) { throw new Error("ES_SECRET is not defined"); } const encryptionSecret = await getDerivedEncryptionKey(secret); return await new jose_1.EncryptJWT(ctx) .setProtectedHeader({ alg: "dir", enc: "A256GCM" }) .setIssuedAt() .setExpirationTime(Date.now() / 1000 + DEFAULT_MAX_AGE) .setJti((0, uuid_1.v4)()) .encrypt(encryptionSecret); } async function decryptJWT(token) { const secret = process.env.ES_SECRET; if (!secret) { throw new Error("ES_SECRET is not defined"); } const encryptionSecret = await getDerivedEncryptionKey(secret); const { payload } = await (0, jose_1.jwtDecrypt)(token, encryptionSecret, { clockTolerance: 15, }); return payload; } async function getDerivedEncryptionKey(secret) { return await (0, hkdf_1.hkdf)("sha256", secret, "", "Edge Store Generated Encryption Key", 32); } async function getContext(req) { const token = req.cookies.edgestore; if (!token) { return {}; } return (await decryptJWT(token)); }