11import mongoose from "mongoose" ;
2+ import { DeleteObjectCommand } from "@aws-sdk/client-s3" ;
3+ import { s3 } from "../config/Aws.js" ;
24
35const OBJECT_ID_PATTERN = / ^ [ a - f \d ] { 24 } $ / i;
46
@@ -273,6 +275,22 @@ const pickAllowed = (payload, allowedFields) => {
273275
274276const isAdmin = ( req ) => req . user && req . user . role === "admin" ;
275277
278+ const deleteS3Object = async ( key ) => {
279+ const bucket = process . env . AWS_BUCKET_NAME ;
280+ if ( ! bucket || ! key ) return ;
281+
282+ try {
283+ await s3 . send (
284+ new DeleteObjectCommand ( {
285+ Bucket : bucket ,
286+ Key : key ,
287+ } )
288+ ) ;
289+ } catch ( error ) {
290+ console . error ( `Failed to delete S3 object: ${ key } ` , error ) ;
291+ }
292+ } ;
293+
276294const ensureOwner = ( doc , req , ownerField ) => {
277295 if ( ! ownerField || isAdmin ( req ) ) return true ;
278296 const ownerId = doc ?. [ ownerField ] ?. toString ( ) ;
@@ -388,16 +406,26 @@ export const buildCrudController = (
388406 const create = async ( req , res , next ) => {
389407 try {
390408 let payload = sanitizePayload ( req . body || { } , req , ownerField , allowedFields ) ;
409+
391410 if ( assignOwnerOnCreate ) {
392411 payload = assignOwner ( payload , req , ownerField , allowAdminOverride ) ;
393412 }
413+
414+ if ( req . file ) {
415+ const uploadedUrl = req . file . location ;
416+ payload . image = uploadedUrl ;
417+ payload . images = [ uploadedUrl ] ;
418+ payload . imageKey = req . file . key ;
419+ }
420+
394421 const created = await Model . create ( payload ) ;
422+
395423 res . status ( 201 ) . json ( created ) ;
396424 } catch ( error ) {
397425 next ( error ) ;
398426 }
399427 } ;
400-
428+
401429 const update = async ( req , res , next ) => {
402430 try {
403431 const doc = await Model . findById ( req . params . id ) ;
@@ -410,8 +438,23 @@ export const buildCrudController = (
410438 throw new Error ( "Not authorized to update this resource" ) ;
411439 }
412440 const updates = sanitizePayload ( req . body || { } , req , ownerField , allowedFields ) ;
441+ let previousImageKey = null ;
442+
443+ if ( req . file ) {
444+ previousImageKey = doc . imageKey || null ;
445+ const uploadedUrl = req . file . location ;
446+ updates . image = uploadedUrl ;
447+ updates . images = [ uploadedUrl ] ;
448+ updates . imageKey = req . file . key ;
449+ }
450+
413451 Object . assign ( doc , updates ) ;
414452 const updated = await doc . save ( ) ;
453+
454+ if ( req . file && previousImageKey && previousImageKey !== updated . imageKey ) {
455+ await deleteS3Object ( previousImageKey ) ;
456+ }
457+
415458 res . json ( updated ) ;
416459 } catch ( error ) {
417460 next ( error ) ;
@@ -429,7 +472,14 @@ export const buildCrudController = (
429472 res . status ( 403 ) ;
430473 throw new Error ( "Not authorized to delete this resource" ) ;
431474 }
475+
476+ const imageKeyToDelete = doc . imageKey || null ;
432477 await doc . deleteOne ( ) ;
478+
479+ if ( imageKeyToDelete ) {
480+ await deleteS3Object ( imageKeyToDelete ) ;
481+ }
482+
433483 res . json ( { message : `${ resourceName } deleted` , id : req . params . id } ) ;
434484 } catch ( error ) {
435485 next ( error ) ;
0 commit comments