@edge-store/react
Version:
Image Handling for React/Next.js
149 lines (148 loc) • 5.56 kB
JavaScript
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));
}
;