diff --git a/package-lock.json b/package-lock.json index b819fe8..83d4ca1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,6 +20,7 @@ "@react-email/components": "^0.0.11", "@react-email/render": "^0.0.9", "@react-email/tailwind": "^0.0.12", + "@types/express": "^4.17.21", "@vercel/analytics": "^1.1.0", "argon2": "^0.31.1", "cloudinary": "^1.41.0", @@ -30,8 +31,8 @@ "lodash": "^4.17.21", "mapbox-gl": "^2.15.0", "multer": "^1.4.5-lts.1", - "multer-storage-cloudinary": "^4.0.0", "next": "^14.0.3", + "next-cloudinary": "^5.10.0", "next-connect": "^1.0.0-next.3", "passport": "^0.6.0", "passport-local": "^1.0.0", @@ -289,6 +290,36 @@ "integrity": "sha512-iZf+UWfL+DogJVpd/xMQyP6X6McYd6ArdYoPMiv/zlOTzeXXfQbYxBNJJBF6tThvsjLMbA8tLjkCdm9RWMFCCw==", "dev": true }, + "node_modules/@cloudinary-util/url-loader": { + "version": "3.16.0", + "resolved": "https://registry.npmjs.org/@cloudinary-util/url-loader/-/url-loader-3.16.0.tgz", + "integrity": "sha512-KqZYuSkQg5FnlREecay5sJTXYdfg/3PaS9Az+XZbCcrdbQQJtWotyE4K3S3F7YBSqTdBItE/XqmgzmmcGLZ3Pw==", + "dependencies": { + "@cloudinary-util/util": "2.3.0", + "@cloudinary/url-gen": "^1.10.2" + } + }, + "node_modules/@cloudinary-util/util": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@cloudinary-util/util/-/util-2.3.0.tgz", + "integrity": "sha512-0Gojd+ZRQjJQmlBEAa8Ua94amvx7uWHoUzVUEGi1S8bgF1wPcMqG07cSUGQfRwHsFQ/9XOesx76Df622E+CevA==" + }, + "node_modules/@cloudinary/transformation-builder-sdk": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@cloudinary/transformation-builder-sdk/-/transformation-builder-sdk-1.8.0.tgz", + "integrity": "sha512-/QLSDDI+rfYH3bFH+DpgEk8NgSoeqwiea9GW5VXplcpebQOOcFqOzNFM6Hlik3PDqq4JMP4s3E7BoPZ1Rdk/IQ==", + "dependencies": { + "@cloudinary/url-gen": "^1.7.0" + } + }, + "node_modules/@cloudinary/url-gen": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@cloudinary/url-gen/-/url-gen-1.13.0.tgz", + "integrity": "sha512-p7ASCX2fZw1JWtMG2ADd/Y0TfvE5SX9v0TDJDlzXX3OlNxLSuqzLWGgj5oJD4ZTo62FdxH24N8HPZ7NXEXtj5g==", + "dependencies": { + "@cloudinary/transformation-builder-sdk": "^1.7.0" + } + }, "node_modules/@cspotcode/source-map-support": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", @@ -2030,7 +2061,6 @@ "version": "1.19.3", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.3.tgz", "integrity": "sha512-oyl4jvAfTGX9Bt6Or4H9ni1Z447/tQuxnZsytsCaExKlmJiU8sFgnIBRzJUpKwB5eWn9HuBYlUlVA74q/yN0eQ==", - "dev": true, "dependencies": { "@types/connect": "*", "@types/node": "*" @@ -2057,7 +2087,6 @@ "version": "3.4.36", "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.36.tgz", "integrity": "sha512-P63Zd/JUGq+PdrM1lv0Wv5SBYeA2+CORvbrXbngriYY0jzLUWfQMQQxOhjONEz/wlHOAxOdY7CY65rgQdTjq2w==", - "dev": true, "dependencies": { "@types/node": "*" } @@ -2078,10 +2107,9 @@ } }, "node_modules/@types/express": { - "version": "4.17.18", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.18.tgz", - "integrity": "sha512-Sxv8BSLLgsBYmcnGdGjjEjqET2U+AKAdCRODmMiq02FgjwuV75Ut85DRpvFjyw/Mk0vgUOliGRU0UUmuuZHByQ==", - "dev": true, + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", + "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", "dependencies": { "@types/body-parser": "*", "@types/express-serve-static-core": "^4.17.33", @@ -2093,7 +2121,6 @@ "version": "4.17.37", "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.37.tgz", "integrity": "sha512-ZohaCYTgGFcOP7u6aJOhY9uIZQgZ2vxC2yWoArY+FeDXlqeH66ZVBjgvg+RLVAS/DWNq4Ap9ZXu1+SUQiiWYMg==", - "dev": true, "dependencies": { "@types/node": "*", "@types/qs": "*", @@ -2114,8 +2141,7 @@ "node_modules/@types/http-errors": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.2.tgz", - "integrity": "sha512-lPG6KlZs88gef6aD85z3HNkztpj7w2R7HmR3gygjfXCQmsLloWNARFkMuzKiiY8FGdh1XDpgBdrSf4aKDiA7Kg==", - "dev": true + "integrity": "sha512-lPG6KlZs88gef6aD85z3HNkztpj7w2R7HmR3gygjfXCQmsLloWNARFkMuzKiiY8FGdh1XDpgBdrSf4aKDiA7Kg==" }, "node_modules/@types/json-schema": { "version": "7.0.13", @@ -2190,8 +2216,7 @@ "node_modules/@types/mime": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.3.tgz", - "integrity": "sha512-Ys+/St+2VF4+xuY6+kDIXGxbNRO0mesVg0bbxEfB97Od1Vjpjx9KD1qxs64Gcb3CWPirk9Xe+PT4YiiHQ9T+eg==", - "dev": true + "integrity": "sha512-Ys+/St+2VF4+xuY6+kDIXGxbNRO0mesVg0bbxEfB97Od1Vjpjx9KD1qxs64Gcb3CWPirk9Xe+PT4YiiHQ9T+eg==" }, "node_modules/@types/minimist": { "version": "1.2.3", @@ -2285,14 +2310,12 @@ "node_modules/@types/qs": { "version": "6.9.8", "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.8.tgz", - "integrity": "sha512-u95svzDlTysU5xecFNTgfFG5RUWu1A9P0VzgpcIiGZA9iraHOdSzcxMxQ55DyeRaGCSxQi7LxXDI4rzq/MYfdg==", - "dev": true + "integrity": "sha512-u95svzDlTysU5xecFNTgfFG5RUWu1A9P0VzgpcIiGZA9iraHOdSzcxMxQ55DyeRaGCSxQi7LxXDI4rzq/MYfdg==" }, "node_modules/@types/range-parser": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.5.tgz", - "integrity": "sha512-xrO9OoVPqFuYyR/loIHjnbvvyRZREYKLjxV4+dY6v3FQR3stQ9ZxIGkaclF7YhI9hfjpuTbu14hZEy94qKLtOA==", - "dev": true + "integrity": "sha512-xrO9OoVPqFuYyR/loIHjnbvvyRZREYKLjxV4+dY6v3FQR3stQ9ZxIGkaclF7YhI9hfjpuTbu14hZEy94qKLtOA==" }, "node_modules/@types/react": { "version": "18.2.25", @@ -2370,7 +2393,6 @@ "version": "0.17.2", "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.2.tgz", "integrity": "sha512-aAG6yRf6r0wQ29bkS+x97BIs64ZLxeE/ARwyS6wrldMm3C1MdKwCcnnEwMC1slI8wuxJOpiUH9MioC0A0i+GJw==", - "dev": true, "dependencies": { "@types/mime": "^1", "@types/node": "*" @@ -2380,7 +2402,6 @@ "version": "1.15.3", "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.3.tgz", "integrity": "sha512-yVRvFsEMrv7s0lGhzrggJjNOSmZCdgCjw9xWrPr/kNNLp6FaDfMC1KaYl3TSJ0c58bECwNBMoQrZJ8hA8E1eFg==", - "dev": true, "dependencies": { "@types/http-errors": "*", "@types/mime": "*", @@ -7474,14 +7495,6 @@ "node": ">= 6.0.0" } }, - "node_modules/multer-storage-cloudinary": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/multer-storage-cloudinary/-/multer-storage-cloudinary-4.0.0.tgz", - "integrity": "sha512-25lm9R6o5dWrHLqLvygNX+kBOxprzpmZdnVKH4+r68WcfCt8XV6xfQaMuAg+kUE5Xmr8mJNA4gE0AcBj9FJyWA==", - "peerDependencies": { - "cloudinary": "^1.21.0" - } - }, "node_modules/murmurhash-js": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/murmurhash-js/-/murmurhash-js-1.0.0.tgz", @@ -7565,6 +7578,19 @@ } } }, + "node_modules/next-cloudinary": { + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/next-cloudinary/-/next-cloudinary-5.10.0.tgz", + "integrity": "sha512-FmLvteYJjpvE69pNZlp/xzwf32/iNuYM9INrh0GTNhgKxoKP/Ej/ij+kjKkZq8eAA8epSeDh3IUoewwRpJfuTQ==", + "dependencies": { + "@cloudinary-util/url-loader": "^3.16.0", + "@cloudinary-util/util": "^2.3.0" + }, + "peerDependencies": { + "next": "^12 || ^13 || ^14", + "react": "^17 || ^18" + } + }, "node_modules/next-connect": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/next-connect/-/next-connect-1.0.0.tgz", @@ -10941,6 +10967,36 @@ "integrity": "sha512-iZf+UWfL+DogJVpd/xMQyP6X6McYd6ArdYoPMiv/zlOTzeXXfQbYxBNJJBF6tThvsjLMbA8tLjkCdm9RWMFCCw==", "dev": true }, + "@cloudinary-util/url-loader": { + "version": "3.16.0", + "resolved": "https://registry.npmjs.org/@cloudinary-util/url-loader/-/url-loader-3.16.0.tgz", + "integrity": "sha512-KqZYuSkQg5FnlREecay5sJTXYdfg/3PaS9Az+XZbCcrdbQQJtWotyE4K3S3F7YBSqTdBItE/XqmgzmmcGLZ3Pw==", + "requires": { + "@cloudinary-util/util": "2.3.0", + "@cloudinary/url-gen": "^1.10.2" + } + }, + "@cloudinary-util/util": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@cloudinary-util/util/-/util-2.3.0.tgz", + "integrity": "sha512-0Gojd+ZRQjJQmlBEAa8Ua94amvx7uWHoUzVUEGi1S8bgF1wPcMqG07cSUGQfRwHsFQ/9XOesx76Df622E+CevA==" + }, + "@cloudinary/transformation-builder-sdk": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@cloudinary/transformation-builder-sdk/-/transformation-builder-sdk-1.8.0.tgz", + "integrity": "sha512-/QLSDDI+rfYH3bFH+DpgEk8NgSoeqwiea9GW5VXplcpebQOOcFqOzNFM6Hlik3PDqq4JMP4s3E7BoPZ1Rdk/IQ==", + "requires": { + "@cloudinary/url-gen": "^1.7.0" + } + }, + "@cloudinary/url-gen": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@cloudinary/url-gen/-/url-gen-1.13.0.tgz", + "integrity": "sha512-p7ASCX2fZw1JWtMG2ADd/Y0TfvE5SX9v0TDJDlzXX3OlNxLSuqzLWGgj5oJD4ZTo62FdxH24N8HPZ7NXEXtj5g==", + "requires": { + "@cloudinary/transformation-builder-sdk": "^1.7.0" + } + }, "@cspotcode/source-map-support": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", @@ -12079,7 +12135,6 @@ "version": "1.19.3", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.3.tgz", "integrity": "sha512-oyl4jvAfTGX9Bt6Or4H9ni1Z447/tQuxnZsytsCaExKlmJiU8sFgnIBRzJUpKwB5eWn9HuBYlUlVA74q/yN0eQ==", - "dev": true, "requires": { "@types/connect": "*", "@types/node": "*" @@ -12106,7 +12161,6 @@ "version": "3.4.36", "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.36.tgz", "integrity": "sha512-P63Zd/JUGq+PdrM1lv0Wv5SBYeA2+CORvbrXbngriYY0jzLUWfQMQQxOhjONEz/wlHOAxOdY7CY65rgQdTjq2w==", - "dev": true, "requires": { "@types/node": "*" } @@ -12127,10 +12181,9 @@ } }, "@types/express": { - "version": "4.17.18", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.18.tgz", - "integrity": "sha512-Sxv8BSLLgsBYmcnGdGjjEjqET2U+AKAdCRODmMiq02FgjwuV75Ut85DRpvFjyw/Mk0vgUOliGRU0UUmuuZHByQ==", - "dev": true, + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", + "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", "requires": { "@types/body-parser": "*", "@types/express-serve-static-core": "^4.17.33", @@ -12142,7 +12195,6 @@ "version": "4.17.37", "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.37.tgz", "integrity": "sha512-ZohaCYTgGFcOP7u6aJOhY9uIZQgZ2vxC2yWoArY+FeDXlqeH66ZVBjgvg+RLVAS/DWNq4Ap9ZXu1+SUQiiWYMg==", - "dev": true, "requires": { "@types/node": "*", "@types/qs": "*", @@ -12163,8 +12215,7 @@ "@types/http-errors": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.2.tgz", - "integrity": "sha512-lPG6KlZs88gef6aD85z3HNkztpj7w2R7HmR3gygjfXCQmsLloWNARFkMuzKiiY8FGdh1XDpgBdrSf4aKDiA7Kg==", - "dev": true + "integrity": "sha512-lPG6KlZs88gef6aD85z3HNkztpj7w2R7HmR3gygjfXCQmsLloWNARFkMuzKiiY8FGdh1XDpgBdrSf4aKDiA7Kg==" }, "@types/json-schema": { "version": "7.0.13", @@ -12239,8 +12290,7 @@ "@types/mime": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.3.tgz", - "integrity": "sha512-Ys+/St+2VF4+xuY6+kDIXGxbNRO0mesVg0bbxEfB97Od1Vjpjx9KD1qxs64Gcb3CWPirk9Xe+PT4YiiHQ9T+eg==", - "dev": true + "integrity": "sha512-Ys+/St+2VF4+xuY6+kDIXGxbNRO0mesVg0bbxEfB97Od1Vjpjx9KD1qxs64Gcb3CWPirk9Xe+PT4YiiHQ9T+eg==" }, "@types/minimist": { "version": "1.2.3", @@ -12333,14 +12383,12 @@ "@types/qs": { "version": "6.9.8", "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.8.tgz", - "integrity": "sha512-u95svzDlTysU5xecFNTgfFG5RUWu1A9P0VzgpcIiGZA9iraHOdSzcxMxQ55DyeRaGCSxQi7LxXDI4rzq/MYfdg==", - "dev": true + "integrity": "sha512-u95svzDlTysU5xecFNTgfFG5RUWu1A9P0VzgpcIiGZA9iraHOdSzcxMxQ55DyeRaGCSxQi7LxXDI4rzq/MYfdg==" }, "@types/range-parser": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.5.tgz", - "integrity": "sha512-xrO9OoVPqFuYyR/loIHjnbvvyRZREYKLjxV4+dY6v3FQR3stQ9ZxIGkaclF7YhI9hfjpuTbu14hZEy94qKLtOA==", - "dev": true + "integrity": "sha512-xrO9OoVPqFuYyR/loIHjnbvvyRZREYKLjxV4+dY6v3FQR3stQ9ZxIGkaclF7YhI9hfjpuTbu14hZEy94qKLtOA==" }, "@types/react": { "version": "18.2.25", @@ -12417,7 +12465,6 @@ "version": "0.17.2", "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.2.tgz", "integrity": "sha512-aAG6yRf6r0wQ29bkS+x97BIs64ZLxeE/ARwyS6wrldMm3C1MdKwCcnnEwMC1slI8wuxJOpiUH9MioC0A0i+GJw==", - "dev": true, "requires": { "@types/mime": "^1", "@types/node": "*" @@ -12427,7 +12474,6 @@ "version": "1.15.3", "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.3.tgz", "integrity": "sha512-yVRvFsEMrv7s0lGhzrggJjNOSmZCdgCjw9xWrPr/kNNLp6FaDfMC1KaYl3TSJ0c58bECwNBMoQrZJ8hA8E1eFg==", - "dev": true, "requires": { "@types/http-errors": "*", "@types/mime": "*", @@ -16062,12 +16108,6 @@ "xtend": "^4.0.0" } }, - "multer-storage-cloudinary": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/multer-storage-cloudinary/-/multer-storage-cloudinary-4.0.0.tgz", - "integrity": "sha512-25lm9R6o5dWrHLqLvygNX+kBOxprzpmZdnVKH4+r68WcfCt8XV6xfQaMuAg+kUE5Xmr8mJNA4gE0AcBj9FJyWA==", - "requires": {} - }, "murmurhash-js": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/murmurhash-js/-/murmurhash-js-1.0.0.tgz", @@ -16117,6 +16157,15 @@ "watchpack": "2.4.0" } }, + "next-cloudinary": { + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/next-cloudinary/-/next-cloudinary-5.10.0.tgz", + "integrity": "sha512-FmLvteYJjpvE69pNZlp/xzwf32/iNuYM9INrh0GTNhgKxoKP/Ej/ij+kjKkZq8eAA8epSeDh3IUoewwRpJfuTQ==", + "requires": { + "@cloudinary-util/url-loader": "^3.16.0", + "@cloudinary-util/util": "^2.3.0" + } + }, "next-connect": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/next-connect/-/next-connect-1.0.0.tgz", diff --git a/package.json b/package.json index e6f4f03..7c8276c 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "@react-email/components": "^0.0.11", "@react-email/render": "^0.0.9", "@react-email/tailwind": "^0.0.12", + "@types/express": "^4.17.21", "@vercel/analytics": "^1.1.0", "argon2": "^0.31.1", "cloudinary": "^1.41.0", @@ -35,8 +36,8 @@ "lodash": "^4.17.21", "mapbox-gl": "^2.15.0", "multer": "^1.4.5-lts.1", - "multer-storage-cloudinary": "^4.0.0", "next": "^14.0.3", + "next-cloudinary": "^5.10.0", "next-connect": "^1.0.0-next.3", "passport": "^0.6.0", "passport-local": "^1.0.0", @@ -64,28 +65,28 @@ "@types/multer": "^1.4.7", "@types/node": "^20.4.2", "@types/passport-local": "^1.0.35", - "@types/react-dom": "^18.2.7", "@types/react": "^18.2.15", + "@types/react-dom": "^18.2.7", "@types/sparkpost": "^2.1.5", "@vercel/fetch": "^7.0.0", "autoprefixer": "^10.4.14", "daisyui": "^3.9.2", "dotenv-cli": "^7.2.1", + "eslint": "^8.51.0", "eslint-config-airbnb-base": "15.0.0", "eslint-config-airbnb-typescript": "17.1.0", "eslint-config-next": "^13.5.4", "eslint-config-prettier": "^9.0.0", "eslint-plugin-react": "^7.33.2", - "eslint": "^8.51.0", "generate-password": "^1.7.1", "onchange": "^7.1.0", "postcss": "^8.4.26", + "prettier": "^3.0.0", "prettier-plugin-jsdoc": "^1.0.2", "prettier-plugin-tailwindcss": "^0.4.1", - "prettier": "^3.0.0", "prisma": "^5.6.0", - "tailwindcss-animate": "^1.0.6", "tailwindcss": "^3.3.3", + "tailwindcss-animate": "^1.0.6", "ts-node": "^10.9.1", "typescript": "^5.3.2" }, diff --git a/src/config/cloudinary/CloudinaryStorage.ts b/src/config/cloudinary/CloudinaryStorage.ts new file mode 100644 index 0000000..e60db02 --- /dev/null +++ b/src/config/cloudinary/CloudinaryStorage.ts @@ -0,0 +1,99 @@ +/* eslint-disable no-underscore-dangle */ + +import type { StorageEngine } from 'multer'; +import type { UploadApiOptions, UploadApiResponse, v2 as cloudinary } from 'cloudinary'; +import type { Request } from 'express'; + +/** + * Represents a storage engine for uploading files to Cloudinary. + * + * @example + * const storage = new CloudinaryStorage({ + * cloudinary, + * params: { + * folder: 'my-folder', + * allowed_formats: ['jpg', 'png'], + * }, + * }); + */ +class CloudinaryStorage implements StorageEngine { + private cloudinary: typeof cloudinary; + + private params: UploadApiOptions; + + /** + * Creates an instance of CloudinaryStorage. + * + * @param options - The options for configuring the Cloudinary storage engine. + * @param options.cloudinary - The Cloudinary instance. + * @param options.params - The parameters for uploading files to Cloudinary. + */ + constructor(options: { cloudinary: typeof cloudinary; params: UploadApiOptions }) { + this.cloudinary = options.cloudinary; + this.params = options.params; + } + + /** + * Removes the file from Cloudinary. + * + * @param req - The request object. + * @param file - The file to be removed. + * @param callback - The callback function to be called if an error occurs. + */ + public _removeFile( + req: Request, + file: Express.Multer.File, + callback: (error: Error) => void, + ) { + this.cloudinary.uploader.destroy(file.filename, { invalidate: true }, callback); + } + + /** + * Handles the file upload to Cloudinary. + * + * @param req - The request object. + * @param file - The file to be uploaded. + * @param callback - The callback function to be called after the file is uploaded. + */ + public _handleFile( + req: Request, + file: Express.Multer.File, + callback: (error?: unknown, info?: Partial) => void, + ) { + this.uploadFile(file) + .then((cloudResponse) => { + callback(null, { + path: cloudResponse.secure_url, + size: cloudResponse.bytes, + filename: cloudResponse.public_id, + }); + }) + .catch((error) => { + callback(error); + }); + } + + /** + * Uploads a file to Cloudinary. + * + * @param file - The file to be uploaded. + * @returns A promise that resolves to the upload response. + */ + private uploadFile(file: Express.Multer.File): Promise { + return new Promise((resolve, reject) => { + const stream = this.cloudinary.uploader.upload_stream( + this.params, + (err, response) => { + if (err != null) { + return reject(err); + } + return resolve(response!); + }, + ); + + file.stream.pipe(stream); + }); + } +} + +export default CloudinaryStorage; diff --git a/src/config/cloudinary/index.ts b/src/config/cloudinary/index.ts index 655c9bb..e45c40a 100644 --- a/src/config/cloudinary/index.ts +++ b/src/config/cloudinary/index.ts @@ -1,8 +1,7 @@ -/* eslint-disable @typescript-eslint/naming-convention */ import { v2 as cloudinary } from 'cloudinary'; -import { CloudinaryStorage } from 'multer-storage-cloudinary'; import { CLOUDINARY_CLOUD_NAME, CLOUDINARY_KEY, CLOUDINARY_SECRET } from '../env'; +import CloudinaryStorage from './CloudinaryStorage'; cloudinary.config({ cloud_name: CLOUDINARY_CLOUD_NAME, @@ -10,10 +9,7 @@ cloudinary.config({ api_secret: CLOUDINARY_SECRET, }); -// @ts-expect-error -const storage = new CloudinaryStorage({ cloudinary, params: { folder: 'BeerApp' } }); +/** Cloudinary storage instance. */ +const storage = new CloudinaryStorage({ cloudinary, params: { folder: 'biergarten' } }); -/** Configuration object for Cloudinary image upload. */ -const cloudinaryConfig = { cloudinary, storage }; - -export default cloudinaryConfig; +export { cloudinary, storage }; diff --git a/src/config/multer/uploadMiddleware.ts b/src/config/multer/uploadMiddleware.ts index 53a0f37..bdfe28b 100644 --- a/src/config/multer/uploadMiddleware.ts +++ b/src/config/multer/uploadMiddleware.ts @@ -1,8 +1,6 @@ import multer from 'multer'; import { expressWrapper } from 'next-connect'; -import cloudinaryConfig from '../cloudinary'; - -const { storage } = cloudinaryConfig; +import { storage } from '../cloudinary'; const fileFilter: multer.Options['fileFilter'] = (req, file, callback) => { const { mimetype } = file;