Skip to content

Commit

Permalink
Convert s3 middleware to service
Browse files Browse the repository at this point in the history
  • Loading branch information
Timothy-Gonzalez committed Sep 13, 2024
1 parent 85662a8 commit 06daa34
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 59 deletions.
17 changes: 0 additions & 17 deletions src/middleware/s3.ts

This file was deleted.

50 changes: 8 additions & 42 deletions src/services/s3/s3-router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,7 @@ import { strongJwtVerification } from "../../middleware/verify-jwt";
import { JwtPayload } from "../auth/auth-models";
import { StatusCode } from "status-code-enum";
import { hasElevatedPerms } from "../auth/auth-lib";

import Config from "../../config";
import { GetObjectCommand, type S3 } from "@aws-sdk/client-s3";
import { s3ClientMiddleware } from "../../middleware/s3";
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
import { createPresignedPost } from "@aws-sdk/s3-presigned-post";
import { createSignedPostUrl, getSignedDownloadUrl } from "./s3-service";

const s3Router = Router();

Expand All @@ -25,23 +20,11 @@ const s3Router = Router();
"url": "https://resume-bucket-dev.s3.us-east-2.amazonaws.com/randomuser?randomstuffs",
}
*/
s3Router.get("/upload/", strongJwtVerification, s3ClientMiddleware, async (_req: Request, res: Response) => {
s3Router.get("/upload/", strongJwtVerification, async (_req: Request, res: Response) => {
const payload = res.locals.payload as JwtPayload;
const s3 = res.locals.s3 as S3;
const userId = payload.id;

const { url, fields } = await createPresignedPost(s3, {
Bucket: Config.S3_BUCKET_NAME,
Key: `${userId}.pdf`,
Conditions: [
["content-length-range", 0, Config.MAX_RESUME_SIZE_BYTES], // 5 MB max
],
Fields: {
success_action_status: "201",
"Content-Type": "application/pdf",
},
Expires: Config.RESUME_URL_EXPIRY_SECONDS,
});
const { url, fields } = await createSignedPostUrl(userId);

return res.status(StatusCode.SuccessOK).send({ url: url, fields: fields });
});
Expand All @@ -59,19 +42,10 @@ s3Router.get("/upload/", strongJwtVerification, s3ClientMiddleware, async (_req:
"url": "https://resume-bucket-dev.s3.us-east-2.amazonaws.com/randomuser?randomstuffs",
}
*/
s3Router.get("/download/", strongJwtVerification, s3ClientMiddleware, async (_req: Request, res: Response) => {
s3Router.get("/download/", strongJwtVerification, async (_req: Request, res: Response) => {
const payload = res.locals.payload as JwtPayload;
const s3 = res.locals.s3 as S3;
const userId = payload.id;

const command = new GetObjectCommand({
Bucket: Config.S3_BUCKET_NAME,
Key: `${userId}.pdf`,
});

const downloadUrl = await getSignedUrl(s3, command, {
expiresIn: Config.RESUME_URL_EXPIRY_SECONDS,
});
const downloadUrl = getSignedDownloadUrl(userId);

return res.status(StatusCode.SuccessOK).send({ url: downloadUrl });
});
Expand All @@ -93,23 +67,15 @@ s3Router.get("/download/", strongJwtVerification, s3ClientMiddleware, async (_re
* HTTP/1.1 403 Forbidden
* {"error": "Forbidden"}
*/
s3Router.get("/download/:USERID", strongJwtVerification, s3ClientMiddleware, async (req: Request, res: Response) => {
const userId = req.params.USERID;
s3Router.get("/download/:USERID", strongJwtVerification, async (req: Request, res: Response) => {
const userId = req.params.USERID as string;
const payload = res.locals.payload as JwtPayload;
const s3 = res.locals.s3 as S3;

if (!hasElevatedPerms(payload)) {
return res.status(StatusCode.ClientErrorForbidden).send({ error: "Forbidden" });
}

const command = new GetObjectCommand({
Bucket: Config.S3_BUCKET_NAME,
Key: `${userId}.pdf`,
});

const downloadUrl = await getSignedUrl(s3, command, {
expiresIn: Config.RESUME_URL_EXPIRY_SECONDS,
});
const downloadUrl = await getSignedDownloadUrl(userId);

return res.status(StatusCode.SuccessOK).send({ url: downloadUrl });
});
Expand Down
50 changes: 50 additions & 0 deletions src/services/s3/s3-service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { GetObjectCommand, S3 } from "@aws-sdk/client-s3";
import Config from "../../config";
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
import { createPresignedPost, PresignedPost } from "@aws-sdk/s3-presigned-post";

let s3: S3 | undefined = undefined;

function getClient(): S3 {
if (!s3) {
s3 = new S3({
apiVersion: "2006-03-01",
credentials: {
accessKeyId: Config.S3_ACCESS_KEY,
secretAccessKey: Config.S3_SECRET_KEY,
},
region: Config.S3_REGION,
});
}
return s3;
}

export function getSignedDownloadUrl(userId: string): Promise<string> {
const s3 = getClient();

const command = new GetObjectCommand({
Bucket: Config.S3_BUCKET_NAME,
Key: `${userId}.pdf`,
});

return getSignedUrl(s3, command, {
expiresIn: Config.RESUME_URL_EXPIRY_SECONDS,
});
}

export function createSignedPostUrl(userId: string): Promise<PresignedPost> {
const s3 = getClient();

return createPresignedPost(s3, {
Bucket: Config.S3_BUCKET_NAME,
Key: `${userId}.pdf`,
Conditions: [
["content-length-range", 0, Config.MAX_RESUME_SIZE_BYTES], // 5 MB max
],
Fields: {
success_action_status: "201",
"Content-Type": "application/pdf",
},
Expires: Config.RESUME_URL_EXPIRY_SECONDS,
});
}

0 comments on commit 06daa34

Please sign in to comment.