diff --git a/controllers/users.js b/controllers/users.js index 2ae4cd123..f1dfd0db9 100644 --- a/controllers/users.js +++ b/controllers/users.js @@ -561,6 +561,30 @@ const updateSelf = async (req, res, next) => { } }; +const handleUserPictureUpload = async (req, res) => { + if (req.body.type === "application") { + return postApplicationUserPicture(req, res); + } + return postUserPicture(req, res); +}; + +const postApplicationUserPicture = async (req, res) => { + const { file } = req; + const { id: userId } = req.userData; + const { coordinates } = req.body; + try { + const coordinatesObject = coordinates && JSON.parse(coordinates); + const imageData = await imageService.uploadProfilePicture({ file, userId, coordinates: coordinatesObject }); + return res.status(201).json({ + message: "Application picture uploaded successfully!", + image: imageData, + }); + } catch (error) { + logger.error(`Error while uploading application picture: ${error}`); + return res.boom.badImplementation(INTERNAL_SERVER_ERROR); + } +}; + /** * Post user profile picture * @@ -1186,4 +1210,5 @@ module.exports = { getIdentityStats, updateUsernames, updateProfile, + handleUserPictureUpload, }; diff --git a/middlewares/pictureRouteMiddleware.js b/middlewares/pictureRouteMiddleware.js new file mode 100644 index 000000000..11f8e84de --- /dev/null +++ b/middlewares/pictureRouteMiddleware.js @@ -0,0 +1,8 @@ +const skipWhenApplicationType = (middleware) => { + return (req, res, next) => { + if (req.body.type === "application") return next(); + return middleware(req, res, next); + }; +}; + +module.exports = skipWhenApplicationType; diff --git a/routes/users.js b/routes/users.js index ebf1e4ca2..e151273b4 100644 --- a/routes/users.js +++ b/routes/users.js @@ -15,6 +15,7 @@ const authenticateProfile = require("../middlewares/authenticateProfile"); const { devFlagMiddleware } = require("../middlewares/devFlag"); const { userAuthorization } = require("../middlewares/userAuthorization"); const conditionalMiddleware = require("../middlewares/conditionalMiddleware"); +const skipWhenApplicationType = require("../middlewares/pictureRouteMiddleware"); router.post("/", authorizeAndAuthenticate([ROLES.SUPERUSER], [Services.CRON_JOB_HANDLER]), users.markUnverified); router.post("/update-in-discord", authenticate, authorizeRoles([SUPERUSER]), users.setInDiscordScript); @@ -65,7 +66,13 @@ router.patch( ); // upload.single('profile') -> multer inmemory storage of file for type multipart/form-data -router.post("/picture", authenticate, checkIsVerifiedDiscord, upload.single("profile"), users.postUserPicture); +router.post( + "/picture", + authenticate, + upload.single("profile"), + skipWhenApplicationType(checkIsVerifiedDiscord), + users.handleUserPictureUpload +); router.patch( "/picture/verify/:id", authenticate, diff --git a/test/integration/application.test.ts b/test/integration/application.test.ts index 8773ed9ea..9bb9d38bb 100644 --- a/test/integration/application.test.ts +++ b/test/integration/application.test.ts @@ -13,6 +13,8 @@ const applicationModel = require("../../models/applications"); const applicationsData = require("../fixtures/applications/applications")(); const cookieName = config.get("userToken.cookieName"); const { APPLICATION_ERROR_MESSAGES, API_RESPONSE_MESSAGES, APPLICATION_SCORE } = require("../../constants/application"); +const imageService = require("../../services/imageService"); +const { Buffer } = require("node:buffer"); const appOwner = userData[3]; const superUser = userData[4]; @@ -996,4 +998,44 @@ describe("Application", function () { }); }); }); + + describe("POST /users/picture (application type)", function () { + it("should return 201 when uploading with type=application and valid file", function (done) { + const mockImageResponse = { publicId: "profile/test-id/image", url: "https://res.cloudinary.com/example/image.png" }; + const uploadStub = sinon.stub(imageService, "uploadProfilePicture").resolves(mockImageResponse); + chai + .request(app) + .post("/users/picture") + .type("form") + .set("cookie", `${cookieName}=${jwt}`) + .attach("profile", Buffer.from("fake-image-data", "utf-8"), "image.png") + .field("type", "application") + .end((err, res) => { + uploadStub.restore(); + if (err) return done(err); + expect(res).to.have.status(201); + expect(res.body.message).to.equal("Application picture uploaded successfully!"); + expect(res.body.image).to.deep.equal(mockImageResponse); + return done(); + }); + }); + + it("should return 500 when upload fails", function (done) { + const uploadStub = sinon.stub(imageService, "uploadProfilePicture").rejects(new Error("Upload failed")); + chai + .request(app) + .post("/users/picture") + .type("form") + .set("cookie", `${cookieName}=${jwt}`) + .attach("profile", Buffer.from("fake-image-data", "utf-8"), "image.png") + .field("type", "application") + .end((err, res) => { + uploadStub.restore(); + if (err) return done(err); + expect(res).to.have.status(500); + expect(res.body.error).to.equal("Internal Server Error"); + return done(); + }); + }); + }); });