Skip to content

Commit db2a3d1

Browse files
committed
feat: add S3 photo upload functionality and update dependencies
1 parent 8a41d48 commit db2a3d1

File tree

10 files changed

+5221
-1002
lines changed

10 files changed

+5221
-1002
lines changed

backend/app.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ app.use(express.static('public'));
1717
import userRoutes from './src/routes/user.routes.js';
1818
import cvRoutes from './src/routes/cv.routes.js';
1919
import conferenceRoutes from './src/routes/conference.routes.js';
20+
import photoRoutes from './src/routes/photo.routes.js';
21+
22+
app.use('/api/photos', photoRoutes);
2023

2124
app.use('/api/users', userRoutes);
2225
app.use('/api/cv', cvRoutes);

backend/package-lock.json

Lines changed: 2247 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

backend/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
"author": "",
1313
"license": "ISC",
1414
"dependencies": {
15+
"aws-sdk": "^2.1692.0",
1516
"bcrypt": "^6.0.0",
1617
"cookie-parser": "^1.4.7",
1718
"cors": "^2.8.5",
@@ -21,6 +22,7 @@
2122
"mongoose": "^8.18.0",
2223
"multer": "^1.4.4",
2324
"multer-gridfs-storage": "^5.0.2",
25+
"multer-s3": "^3.0.1",
2426
"nodemon": "^3.1.10",
2527
"pdfkit": "^0.17.2"
2628
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import { s3 } from '../middlewares/s3.upload.middleware.js';
2+
import User from '../models/user.model.js';
3+
4+
export const uploadProfilePhoto = async (req, res) => {
5+
try {
6+
if (!req.file) {
7+
return res.status(400).json({
8+
success: false,
9+
message: 'No photo uploaded'
10+
});
11+
}
12+
13+
const photoUrl = req.file.location;
14+
const photoKey = req.file.key;
15+
16+
res.status(200).json({
17+
success: true,
18+
data: {
19+
url: photoUrl,
20+
key: photoKey,
21+
message: 'Photo uploaded successfully'
22+
}
23+
});
24+
} catch (error) {
25+
res.status(500).json({
26+
success: false,
27+
message: error.message
28+
});
29+
}
30+
};
31+
32+
export const deleteProfilePhoto = async (req, res) => {
33+
try {
34+
const { photoKey } = req.params;
35+
36+
const deleteParams = {
37+
Bucket: process.env.S3_BUCKET_NAME,
38+
Key: photoKey
39+
};
40+
41+
await s3.deleteObject(deleteParams).promise();
42+
43+
res.status(200).json({
44+
success: true,
45+
message: 'Photo deleted successfully'
46+
});
47+
} catch (error) {
48+
res.status(500).json({
49+
success: false,
50+
message: error.message
51+
});
52+
}
53+
};
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import AWS from 'aws-sdk';
2+
import multe from 'multer';
3+
import multerS3 from 'multer-s3';
4+
import { v4 as uuidv4 } from 'uuid';
5+
6+
AWS.config.update({
7+
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
8+
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
9+
region: process.env.AWS_REGION,
10+
});
11+
12+
const s3 = new AWS.S3();
13+
const uploadPhoto = multer({
14+
storage: multerS3({
15+
s3: s3,
16+
bucket: process.env.S3_BUCKET_NAME,
17+
key: function (req, file, cb) {
18+
const fileName = `photos/${uuidv4()}-${file.originalname}`;
19+
cb(null, fileName);
20+
},
21+
contentType: multerS3.AUTO_CONTENT_TYPE,
22+
metadata: function (req, file, cb) {
23+
cb(null, {
24+
fieldName: file.fieldname,
25+
userId: req.user._id.toString(),
26+
uploadDate: new Date().toISOString()
27+
});
28+
}
29+
}),
30+
limits: {
31+
fileSize: 5 * 1024 * 1024 // 5MB limit
32+
},
33+
fileFilter: (req, file, cb) => {
34+
if (file.mimetype.startsWith('image/')) {
35+
cb(null, true);
36+
} else {
37+
cb(new Error('Only image files are allowed'), false);
38+
}
39+
}
40+
});
41+
42+
export { uploadPhoto, s3 };
43+
44+
45+

backend/src/models/cv.model.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,11 @@ const cvSchema = new mongoose.Schema({
5454
required: true
5555
},
5656
photo: {
57-
type: String, // GridFS file ID for profile photo
57+
type: String, // S3 URL
58+
default: null
59+
},
60+
photoKey: {
61+
type: String,
5862
default: null
5963
},
6064
languages: [{

backend/src/routes/photo.routes.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { Router } from 'express';
2+
import { uploadProfilePhoto, deleteProfilePhoto } from '../controllers/photo.controller.js';
3+
import { verifyJWT } from '../middlewares/auth.middleware.js';
4+
import { uploadPhoto } from '../middlewares/s3Upload.middleware.js';
5+
6+
const router = Router();
7+
8+
router.route('/upload').post(
9+
verifyJWT,
10+
uploadPhoto.single('photo'),
11+
uploadProfilePhoto
12+
);
13+
14+
router.route('/delete/:photoKey').delete(verifyJWT, deleteProfilePhoto);
15+
16+
export default router;

0 commit comments

Comments
 (0)