Skip to content

Commit

Permalink
image upload is atomic-ish
Browse files Browse the repository at this point in the history
  • Loading branch information
azn-abel committed Mar 2, 2025
1 parent 2b0ca88 commit ca8f036
Show file tree
Hide file tree
Showing 2 changed files with 24 additions and 4 deletions.
18 changes: 14 additions & 4 deletions src/routes/image.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
} from '../repository/image'
import { ImageRowType, NewImageType, ImageRowsType } from '../models/image'
import { toCamelCaseBody } from '../util/routes'
import { generateGetSignedUrl, putToS3 } from '../util/s3'
import { deleteFromS3, generateGetSignedUrl, putToS3 } from '../util/s3'
import { randomUUID } from 'node:crypto'
import { adminAuthMiddleware } from '../middleware/auth'
import {
Expand Down Expand Up @@ -40,7 +40,8 @@ const upload = multer({ fileFilter, storage, limits })
export const imageRouter = express.Router()

// Takes an image in the body, uploads it to s3, and adds it to the database.
// Ensure that it acts like a transaction
// If s3 put fails, nothing is written to db. If s3 put succeeds and db write
// fails, attempts delete object from s3.
imageRouter.post(
'/',
upload.single('image'),
Expand Down Expand Up @@ -89,11 +90,20 @@ imageRouter.post(

try {
const result = await insertImage(newImage)
res.status(200).send(result)
return res.status(200).send(result), undefined
} catch (e) {
logger.error(`error inserting image into db: ${e}`)
res.status(500).send({ error: "Couldn't write to DB." })
}

// rollback s3 insertion if db write fails
try {
await deleteFromS3(fileLocation)
} catch (e) {
logger.warn(
`orphaned element in S3 with key: ${fileLocation}. s3 error: ${e}`
)
}
res.status(500).send({ error: "Couldn't write to DB." })
}
)

Expand Down
10 changes: 10 additions & 0 deletions src/util/s3.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
GetObjectCommand,
CreateBucketCommand,
PutObjectCommand,
DeleteObjectCommand,
} from '@aws-sdk/client-s3'
import { getSignedUrl } from '@aws-sdk/s3-request-presigner'
import { DeployEnv, getDeployEnv } from '.'
Expand Down Expand Up @@ -56,3 +57,12 @@ export const putToS3 = async (

await s3.send(command)
}

export const deleteFromS3 = async (key: string) => {
const command = new DeleteObjectCommand({
Bucket: s3BucketName,
Key: key,
})

await s3.send(command)
}

0 comments on commit ca8f036

Please sign in to comment.