diff --git a/package-lock.json b/package-lock.json index d61f6b832ed..e30e1b03391 100644 --- a/package-lock.json +++ b/package-lock.json @@ -39,7 +39,6 @@ "graphql-ws": "^5.14.2", "helmet": "^7.1.0", "i18n": "^0.15.1", - "image-hash": "^5.3.1", "ioredis": "^5.3.2", "jsonwebtoken": "^9.0.0", "jwt-decode": "^4.0.0", @@ -48,9 +47,9 @@ "mongoose": "^5.13.21", "mongoose-paginate-v2": "^1.7.0", "morgan": "^1.10.0", - "nanoid": "^5.0.5", + "nanoid": "^5.0.6", "nodemailer": "^6.9.12", - "pm2": "^5.2.0", + "pm2": "^5.3.1", "redis": "^4.6.13", "rrule": "^2.8.1", "typedoc-plugin-markdown": "^3.17.1", @@ -1479,11 +1478,6 @@ "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "dev": true }, - "node_modules/@canvas/image-data": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@canvas/image-data/-/image-data-1.0.0.tgz", - "integrity": "sha512-BxOqI5LgsIQP1odU5KMwV9yoijleOPzHL18/YvNqF9KFSGF2K/DLlYAbDQsWqd/1nbaFuSkYD/191dpMtNh4vw==" - }, "node_modules/@colors/colors": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", @@ -1493,14 +1487,6 @@ "node": ">=0.1.90" } }, - "node_modules/@cwasm/webp": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/@cwasm/webp/-/webp-0.1.5.tgz", - "integrity": "sha512-ceIZQkyxK+s7mmItNcWqqHdOBiJAxYxTnrnPNgUNjldB1M9j+Bp/3eVIVwC8rUFyN/zoFwuT0331pyY3ackaNA==", - "dependencies": { - "@canvas/image-data": "^1.0.0" - } - }, "node_modules/@dabh/diagnostics": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz", @@ -5418,11 +5404,6 @@ "resolved": "https://registry.npmjs.org/@repeaterjs/repeater/-/repeater-3.0.5.tgz", "integrity": "sha512-l3YHBLAol6d/IKnB9LhpD0cEZWAoe3eFKUyTYWmFmCO2Q/WOckxLQAUyMZWwZV2M/m3+4vgRoaolFqaII82/TA==" }, - "node_modules/@tokenizer/token": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", - "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==" - }, "node_modules/@tootallnate/quickjs-emscripten": { "version": "0.23.0", "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", @@ -6416,6 +6397,7 @@ "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -6686,14 +6668,6 @@ "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", "dev": true }, - "node_modules/asn1": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", - "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", - "dependencies": { - "safer-buffer": "~2.1.0" - } - }, "node_modules/asn1js": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/asn1js/-/asn1js-3.0.5.tgz", @@ -6708,14 +6682,6 @@ "node": ">=12.0.0" } }, - "node_modules/assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", - "engines": { - "node": ">=0.8" - } - }, "node_modules/assertion-error": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", @@ -6829,19 +6795,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==", - "engines": { - "node": "*" - } - }, - "node_modules/aws4": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.12.0.tgz", - "integrity": "sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==" - }, "node_modules/axios": { "version": "1.6.8", "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.8.tgz", @@ -6982,14 +6935,6 @@ "node": ">=10.0.0" } }, - "node_modules/bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", - "dependencies": { - "tweetnacl": "^0.14.3" - } - }, "node_modules/bcryptjs": { "version": "2.4.3", "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz", @@ -7367,11 +7312,6 @@ "upper-case-first": "^2.0.2" } }, - "node_modules/caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==" - }, "node_modules/chai": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/chai/-/chai-4.4.1.tgz", @@ -8066,17 +8006,6 @@ "resolved": "https://registry.npmjs.org/culvert/-/culvert-0.1.2.tgz", "integrity": "sha512-yi1x3EAWKjQTreYWeSd98431AV+IEE0qoDyOoaHJ7KJ21gv6HtBXHVLX74opVSGqcR8/AbjJBHAHpcOy2bj5Gg==" }, - "node_modules/dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", - "dependencies": { - "assert-plus": "^1.0.0" - }, - "engines": { - "node": ">=0.10" - } - }, "node_modules/data-uri-to-buffer": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz", @@ -8350,20 +8279,6 @@ "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", "dev": true }, - "node_modules/ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", - "dependencies": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" - } - }, - "node_modules/ecc-jsbn/node_modules/jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==" - }, "node_modules/ecdsa-sig-formatter": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", @@ -9456,11 +9371,6 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, - "node_modules/extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" - }, "node_modules/extend-shallow": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", @@ -9505,14 +9415,6 @@ "follow-redirects": "^1.14.0" } }, - "node_modules/extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==", - "engines": [ - "node >=0.6.0" - ] - }, "node_modules/fast-decode-uri-component": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/fast-decode-uri-component/-/fast-decode-uri-component-1.0.1.tgz", @@ -9521,7 +9423,8 @@ "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true }, "node_modules/fast-glob": { "version": "3.3.2", @@ -9557,7 +9460,8 @@ "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true }, "node_modules/fast-levenshtein": { "version": "2.0.6", @@ -9675,22 +9579,6 @@ "node": "^10.12.0 || >=12.0.0" } }, - "node_modules/file-type": { - "version": "16.5.4", - "resolved": "https://registry.npmjs.org/file-type/-/file-type-16.5.4.tgz", - "integrity": "sha512-/yFHK0aGjFEgDJjEKP0pWCplsPFPhwyfwevf/pVxiN0tmE4L9LmwWxWukdJSHdoCli4VgQLehjJtwQBnqmsKcw==", - "dependencies": { - "readable-web-to-node-stream": "^3.0.0", - "strtok3": "^6.2.4", - "token-types": "^4.1.1" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/file-type?sponsor=1" - } - }, "node_modules/fill-range": { "version": "2.2.4", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.4.tgz", @@ -9846,14 +9734,6 @@ "node": ">=8.0.0" } }, - "node_modules/forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==", - "engines": { - "node": "*" - } - }, "node_modules/form-data": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", @@ -10173,14 +10053,6 @@ "node": ">= 14" } }, - "node_modules/getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", - "dependencies": { - "assert-plus": "^1.0.0" - } - }, "node_modules/git-node-fs": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/git-node-fs/-/git-node-fs-1.0.0.tgz", @@ -10671,27 +10543,6 @@ "node": ">=0.10.0" } }, - "node_modules/har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==", - "engines": { - "node": ">=4" - } - }, - "node_modules/har-validator": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", - "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", - "deprecated": "this library is no longer supported", - "dependencies": { - "ajv": "^6.12.3", - "har-schema": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/has-bigints": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", @@ -10832,20 +10683,6 @@ "node": ">= 14" } }, - "node_modules/http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==", - "dependencies": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - }, - "engines": { - "node": ">=0.8", - "npm": ">=1.3.7" - } - }, "node_modules/https-proxy-agent": { "version": "7.0.4", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.4.tgz", @@ -10916,6 +10753,7 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, "funding": [ { "type": "github", @@ -10939,18 +10777,6 @@ "node": ">= 4" } }, - "node_modules/image-hash": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/image-hash/-/image-hash-5.3.2.tgz", - "integrity": "sha512-of8SekDXKFoaK4R93dP/Lzw6+NRGag8Jr9YlIIZ9jJVn9KYLfYVo/ARbKtbRn+tdTz/wDzBObx6yflKpLSYbxA==", - "dependencies": { - "@cwasm/webp": "^0.1.5", - "file-type": "^16.5.3", - "jpeg-js": "^0.4.0", - "pngjs": "^6.0.0", - "request": "^2.81.0" - } - }, "node_modules/immutable": { "version": "3.7.6", "resolved": "https://registry.npmjs.org/immutable/-/immutable-3.7.6.tgz", @@ -11486,11 +11312,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==" - }, "node_modules/is-unc-path": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz", @@ -11590,11 +11411,6 @@ "ws": "*" } }, - "node_modules/isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==" - }, "node_modules/istanbul-lib-coverage": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", @@ -11678,11 +11494,6 @@ "url": "https://github.com/sponsors/panva" } }, - "node_modules/jpeg-js": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/jpeg-js/-/jpeg-js-0.4.4.tgz", - "integrity": "sha512-WZzeDOEtTOBK4Mdsar0IqEU5sMr3vSV2RqkAIzUEV2BHnUfKGyswWFPFwK5EeDo93K3FohSHbLAjj0s1Wzd+dg==" - }, "node_modules/js-git": { "version": "0.7.8", "resolved": "https://registry.npmjs.org/js-git/-/js-git-0.7.8.tgz", @@ -11738,15 +11549,11 @@ "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" }, - "node_modules/json-schema": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", - "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==" - }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true }, "node_modules/json-stable-stringify": { "version": "1.1.1", @@ -11775,7 +11582,8 @@ "node_modules/json-stringify-safe": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==" + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", + "optional": true }, "node_modules/json-to-pretty-yaml": { "version": "1.2.2", @@ -11873,20 +11681,6 @@ "node": ">=10" } }, - "node_modules/jsprim": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", - "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", - "dependencies": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.4.0", - "verror": "1.10.0" - }, - "engines": { - "node": ">=0.6.0" - } - }, "node_modules/jwa": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", @@ -13318,14 +13112,6 @@ "integrity": "sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw==", "dev": true }, - "node_modules/oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", - "engines": { - "node": "*" - } - }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -13857,23 +13643,6 @@ "node": "*" } }, - "node_modules/peek-readable": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-4.1.0.tgz", - "integrity": "sha512-ZI3LnwUv5nOGbQzD9c2iDG6toheuXSZP5esSHBjopsXH4dg19soufvpUGA3uohi5anFtGb2lhAVdHzH6R/Evvg==", - "engines": { - "node": ">=8" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Borewit" - } - }, - "node_modules/performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==" - }, "node_modules/picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", @@ -14073,14 +13842,6 @@ "node": ">=10" } }, - "node_modules/pngjs": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-6.0.0.tgz", - "integrity": "sha512-TRzzuFRRmEoSW/p1KVAmiOgPco2Irlah+bGFCeNfJXxxYGwSw7YwAOAcd7X28K/m5bjBWKsC29KyoMfHbypayg==", - "engines": { - "node": ">=12.13.0" - } - }, "node_modules/possible-typed-array-names": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", @@ -14232,11 +13993,6 @@ "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" }, - "node_modules/psl": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", - "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==" - }, "node_modules/punycode": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", @@ -14425,34 +14181,6 @@ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, - "node_modules/readable-web-to-node-stream": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.2.tgz", - "integrity": "sha512-ePeK6cc1EcKLEhJFt/AebMCLL+GgSKhuygrZ/GLaKZYEecIgIECf4UaUuaByiGtzckwR4ain9VzUh95T1exYGw==", - "dependencies": { - "readable-stream": "^3.6.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Borewit" - } - }, - "node_modules/readable-web-to-node-stream/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -14599,67 +14327,6 @@ "node": ">=0.10" } }, - "node_modules/request": { - "version": "2.88.2", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", - "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", - "deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142", - "dependencies": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.3", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.5.0", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/request/node_modules/form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 0.12" - } - }, - "node_modules/request/node_modules/qs": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", - "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/request/node_modules/uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", - "bin": { - "uuid": "bin/uuid" - } - }, "node_modules/require-at": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/require-at/-/require-at-1.0.6.tgz", @@ -15421,35 +15088,6 @@ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==" }, - "node_modules/sshpk": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.18.0.tgz", - "integrity": "sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==", - "dependencies": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - }, - "bin": { - "sshpk-conv": "bin/sshpk-conv", - "sshpk-sign": "bin/sshpk-sign", - "sshpk-verify": "bin/sshpk-verify" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/sshpk/node_modules/jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==" - }, "node_modules/stack-chain": { "version": "1.3.7", "resolved": "https://registry.npmjs.org/stack-chain/-/stack-chain-1.3.7.tgz", @@ -15672,22 +15310,6 @@ "url": "https://github.com/sponsors/antfu" } }, - "node_modules/strtok3": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-6.3.0.tgz", - "integrity": "sha512-fZtbhtvI9I48xDSywd/somNqgUHl2L2cstmXCCif0itOf96jeW18MBSyrLuNicYQVkvpOxkZtkzujiTJ9LW5Jw==", - "dependencies": { - "@tokenizer/token": "^0.3.0", - "peek-readable": "^4.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Borewit" - } - }, "node_modules/stylis": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", @@ -15908,47 +15530,11 @@ "node": ">=0.6" } }, - "node_modules/token-types": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/token-types/-/token-types-4.2.1.tgz", - "integrity": "sha512-6udB24Q737UD/SDsKAHI9FCRP7Bqc9D/MQUV02ORQg5iskjtLJlZJNdN4kKtcdtwCeWIwIHDGaUsTsCCAa8sFQ==", - "dependencies": { - "@tokenizer/token": "^0.3.0", - "ieee754": "^1.2.1" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Borewit" - } - }, "node_modules/toml": { "version": "2.3.6", "resolved": "https://registry.npmjs.org/toml/-/toml-2.3.6.tgz", "integrity": "sha512-gVweAectJU3ebq//Ferr2JUY4WKSDe5N+z0FvjDncLGyHmIDoxgY/2Ie4qfEIDm4IS7OA6Rmdm7pdEEdMcV/xQ==" }, - "node_modules/tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", - "dependencies": { - "psl": "^1.1.28", - "punycode": "^2.1.1" - }, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/tough-cookie/node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "engines": { - "node": ">=6" - } - }, "node_modules/tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", @@ -16037,17 +15623,6 @@ "fsevents": "~2.3.3" } }, - "node_modules/tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", - "dependencies": { - "safe-buffer": "^5.0.1" - }, - "engines": { - "node": "*" - } - }, "node_modules/tv4": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/tv4/-/tv4-1.3.0.tgz", @@ -16056,11 +15631,6 @@ "node": ">= 0.8.0" } }, - "node_modules/tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==" - }, "node_modules/tx2": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tx2/-/tx2-1.0.5.tgz", @@ -16390,6 +15960,7 @@ "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, "dependencies": { "punycode": "^2.1.0" } @@ -16398,6 +15969,7 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, "engines": { "node": ">=6" } @@ -16477,24 +16049,6 @@ "node": ">= 0.8" } }, - "node_modules/verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", - "engines": [ - "node >=0.6.0" - ], - "dependencies": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - } - }, - "node_modules/verror/node_modules/core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==" - }, "node_modules/vite": { "version": "3.2.8", "resolved": "https://registry.npmjs.org/vite/-/vite-3.2.8.tgz", diff --git a/package.json b/package.json index 97db7ac1850..8a4c7d7f425 100644 --- a/package.json +++ b/package.json @@ -73,7 +73,6 @@ "graphql-ws": "^5.14.2", "helmet": "^7.1.0", "i18n": "^0.15.1", - "image-hash": "^5.3.1", "ioredis": "^5.3.2", "jsonwebtoken": "^9.0.0", "jwt-decode": "^4.0.0", @@ -82,9 +81,9 @@ "mongoose": "^5.13.21", "mongoose-paginate-v2": "^1.7.0", "morgan": "^1.10.0", - "nanoid": "^5.0.5", + "nanoid": "^5.0.6", "nodemailer": "^6.9.12", - "pm2": "^5.2.0", + "pm2": "^5.3.1", "redis": "^4.6.13", "rrule": "^2.8.1", "typedoc-plugin-markdown": "^3.17.1", diff --git a/src/resolvers/Mutation/index.ts b/src/resolvers/Mutation/index.ts index 7ae7bd724bf..12945c08963 100644 --- a/src/resolvers/Mutation/index.ts +++ b/src/resolvers/Mutation/index.ts @@ -77,7 +77,6 @@ import { removeUserCustomData } from "./removeUserCustomData"; import { removeUserFamily } from "./removeUserFamily"; import { removeUserFromGroupChat } from "./removeUserFromGroupChat"; import { removeUserFromUserFamily } from "./removeUserFromUserFamily"; -import { removeUserImage } from "./removeUserImage"; import { removeUserTag } from "./removeUserTag"; import { resetCommunity } from "./resetCommunity"; import { revokeRefreshTokenForUser } from "./revokeRefreshTokenForUser"; @@ -202,7 +201,6 @@ export const Mutation: MutationResolvers = { removePost, removeUserCustomData, removeUserFromGroupChat, - removeUserImage, removeUserTag, resetCommunity, revokeRefreshTokenForUser, diff --git a/src/resolvers/Mutation/removeOrganizationImage.ts b/src/resolvers/Mutation/removeOrganizationImage.ts index 1f0849fde4a..3392104e07e 100644 --- a/src/resolvers/Mutation/removeOrganizationImage.ts +++ b/src/resolvers/Mutation/removeOrganizationImage.ts @@ -5,13 +5,12 @@ import { import type { MutationResolvers } from "../../types/generatedGraphQLTypes"; import { errors, requestContext } from "../../libraries"; import { Organization } from "../../models"; -import { adminCheck, deleteImage } from "../../utilities"; +import { adminCheck, deletePreviousImage } from "../../utilities"; import { findOrganizationsInCache } from "../../services/OrganizationCache/findOrganizationsInCache"; import { cacheOrganizations } from "../../services/OrganizationCache/cacheOrganizations"; /** * This function enables to remove an organization's image. * @param _parent - parent of current request - * @param args - payload provided with the request * @param context - context of entire application * @remarks The following checks are done: * 1. If the user exists. @@ -34,7 +33,9 @@ export const removeOrganizationImage: MutationResolvers["removeOrganizationImage _id: args.organizationId, }).lean(); - await cacheOrganizations([organization!]); + if (organization) { + await cacheOrganizations([organization]); + } } // Checks whether organization exists. @@ -58,9 +59,10 @@ export const removeOrganizationImage: MutationResolvers["removeOrganizationImage ); } - await deleteImage(organization.image); - // Sets image field of organization to null and returns the updated organization. + if (organization?.image) { + await deletePreviousImage(organization?.image); + } const updatedOrganization = await Organization.findOneAndUpdate( { _id: organization._id, @@ -79,5 +81,8 @@ export const removeOrganizationImage: MutationResolvers["removeOrganizationImage await cacheOrganizations([updatedOrganization]); } - return updatedOrganization!; + if (!updatedOrganization) { + throw new Error("Organization could not be updated"); + } + return updatedOrganization; }; diff --git a/src/resolvers/Mutation/removeUserImage.ts b/src/resolvers/Mutation/removeUserImage.ts deleted file mode 100644 index 7a3fb2c7d71..00000000000 --- a/src/resolvers/Mutation/removeUserImage.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { - USER_NOT_FOUND_ERROR, - USER_PROFILE_IMAGE_NOT_FOUND_ERROR, -} from "../../constants"; -import type { MutationResolvers } from "../../types/generatedGraphQLTypes"; -import { errors, requestContext } from "../../libraries"; -import { User } from "../../models"; -import { deleteImage } from "../../utilities"; -/** - * This function enables to remove user image. - * @param _parent - parent of current request - * @param args - payload provided with the request - * @param context - context of entire application - * @remarks The following checks are done: - * 1. If the user exists. - * 2. If the image exists - * @returns Updated user. - */ -export const removeUserImage: MutationResolvers["removeUserImage"] = async ( - _parent, - _args, - context, -) => { - const currentUser = await User.findOne({ - _id: context.userId, - }); - - // Checks whether currentUser exists. - if (!currentUser) { - throw new errors.NotFoundError( - requestContext.translate(USER_NOT_FOUND_ERROR.MESSAGE), - USER_NOT_FOUND_ERROR.CODE, - USER_NOT_FOUND_ERROR.PARAM, - ); - } - - // Checks whether currentUser.image already doesn't exist. - if (!currentUser.image) { - throw new errors.NotFoundError( - requestContext.translate(USER_PROFILE_IMAGE_NOT_FOUND_ERROR.MESSAGE), - USER_PROFILE_IMAGE_NOT_FOUND_ERROR.MESSAGE, - USER_PROFILE_IMAGE_NOT_FOUND_ERROR.PARAM, - ); - } - - await deleteImage(currentUser.image); - - // Sets image field to null for currentUser and returns the updated currentUser. - return await User.findOneAndUpdate( - { - _id: currentUser._id, - }, - { - $set: { - image: null, - }, - }, - { - new: true, - }, - ).lean(); -}; diff --git a/src/utilities/deleteImage.ts b/src/utilities/deleteImage.ts deleted file mode 100644 index 74c30cba1fd..00000000000 --- a/src/utilities/deleteImage.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { unlink } from "fs"; -import { logger } from "../libraries"; -import { ImageHash } from "../models"; -import { reuploadDuplicateCheck } from "./reuploadDuplicateCheck"; -/** - * This function deletes an image if it is only used once. - * It is also ensured that the image hash isn't used by multiple users/organization before deleting it - * After deleting the image, the number of uses of the hashed image are decremented by one. - * @param imageToBeDeleted - Path of image - * @param imageBelongingToItem - Does image belong to an item - */ -export const deleteImage = async ( - imageToBeDeleted: string, - imageBelongingToItem?: string, -): Promise => { - let imageIsDuplicate = false; - - if (imageBelongingToItem) { - imageIsDuplicate = await reuploadDuplicateCheck( - imageToBeDeleted, - imageBelongingToItem, - ); - } - - if (!imageIsDuplicate) { - /* - Only remove the old image if its different from the new one - Ensure image hash isn't used by multiple users/organization before deleting it - */ - const imageHash = await ImageHash.findOne({ - fileName: imageToBeDeleted, - }).lean(); - - if (imageHash && imageHash?.numberOfUses > 1) { - // Image can only be deleted if imageHash.numberOfUses === 1 - logger.info("Image cannot be deleted"); - } else { - logger.info("Image is only used once and therefore can be deleted"); - - unlink(imageToBeDeleted, (error) => { - if (error) { - throw error; - } - - // If no error occurs image has been successfully deleted. - logger.info("File deleted!"); - }); - } - - await ImageHash.updateOne( - { - fileName: imageToBeDeleted, - }, - { - $inc: { - numberOfUses: -1, - }, - }, - ); - } -}; diff --git a/src/utilities/encodedImageStorage/deletePreviousImage.ts b/src/utilities/encodedImageStorage/deletePreviousImage.ts index 33d600f6590..aad0654ac28 100644 --- a/src/utilities/encodedImageStorage/deletePreviousImage.ts +++ b/src/utilities/encodedImageStorage/deletePreviousImage.ts @@ -5,8 +5,11 @@ import { EncodedImage } from "../../models/EncodedImage"; export const deletePreviousImage = async ( imageToBeDeletedPath: string, ): Promise => { + if (!imageToBeDeletedPath) { + throw new Error("imageToBeDeletedPath is null or undefined"); + } const imageToBeDeleted = await EncodedImage.findOne({ - fileName: imageToBeDeletedPath!, + fileName: imageToBeDeletedPath, }); if (imageToBeDeleted?.numberOfUses === 1) { diff --git a/src/utilities/imageAlreadyInDbCheck.ts b/src/utilities/imageAlreadyInDbCheck.ts deleted file mode 100644 index 69822377080..00000000000 --- a/src/utilities/imageAlreadyInDbCheck.ts +++ /dev/null @@ -1,85 +0,0 @@ -import { imageHash } from "image-hash"; -import { ImageHash } from "../models"; -import { deleteDuplicatedImage } from "./deleteDuplicatedImage"; -import { reuploadDuplicateCheck } from "./reuploadDuplicateCheck"; -import { errors, requestContext } from "../libraries"; -import { INVALID_FILE_TYPE } from "../constants"; - -/** - * This function checks if an image already exists in the database using hash. - * If it does, then point to that image and remove the image just uploaded. - * Else, allow the file to get uploaded. - * @param oldImagePath - Path of image - * @param newImagePath - Does image belong to an item - * @returns file name. - */ -export const imageAlreadyInDbCheck = async ( - oldImagePath: string | null, - newImagePath: string, -): Promise => { - try { - let fileName; - - const getImageHash = (): Promise => - new Promise((resolve, reject) => { - imageHash(`./${newImagePath}`, 16, true, (error: any, data: any) => { - if (error) { - reject(error); - } else { - resolve(data); - } - }); - }); - - const hash = await getImageHash(); - - const existingImageHash = await ImageHash.findOne({ - hashValue: hash, - }).lean(); - - if (!existingImageHash) { - await ImageHash.create({ - hashValue: hash, - fileName: newImagePath, - numberOfUses: 1, - }); - } else { - const imageIsDuplicate = await reuploadDuplicateCheck( - oldImagePath, - newImagePath, - ); - - if (imageIsDuplicate === false) { - // dont increment if the same user/org is using the same image multiple times for the same use case - await ImageHash.updateOne( - { - // Increase the number of places this image is used - hashValue: hash, - }, - { - $inc: { - numberOfUses: 1, - }, - }, - ); - } - - deleteDuplicatedImage(newImagePath); - - fileName = existingImageHash.fileName; // will include have file already in db if pic is already saved will be null otherwise - } - - return fileName as string; - } catch (error) { - throw new errors.ValidationError( - [ - { - message: requestContext.translate(INVALID_FILE_TYPE.MESSAGE), - code: INVALID_FILE_TYPE.CODE, - param: INVALID_FILE_TYPE.PARAM, - }, - ], - requestContext.translate(INVALID_FILE_TYPE.MESSAGE), - ); - } -}; diff --git a/src/utilities/imageExtensionCheck.ts b/src/utilities/imageExtensionCheck.ts deleted file mode 100644 index 337e3f806dc..00000000000 --- a/src/utilities/imageExtensionCheck.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { deleteImage } from "./deleteImage"; -import { errors, requestContext } from "../libraries"; -import { INVALID_FILE_TYPE } from "../constants"; -/** - * This function checks the extension of the file. - * If the extension isn't of type 'png', or 'jpg', or 'jpeg', - * then the file is deleted and a validation error is thrown. - * @param filename - Name of file - */ -export const imageExtensionCheck = async (filename: string): Promise => { - const fileExtension = filename.split(".").pop(); - - if ( - fileExtension !== "png" && - fileExtension !== "jpg" && - fileExtension !== "jpeg" - ) { - await deleteImage(filename); - - throw new errors.ValidationError( - [ - { - message: requestContext.translate(INVALID_FILE_TYPE.MESSAGE), - code: INVALID_FILE_TYPE.CODE, - param: INVALID_FILE_TYPE.PARAM, - }, - ], - requestContext.translate(INVALID_FILE_TYPE.MESSAGE), - ); - } -}; diff --git a/src/utilities/index.ts b/src/utilities/index.ts index aa810f51662..371e549c190 100644 --- a/src/utilities/index.ts +++ b/src/utilities/index.ts @@ -1,8 +1,7 @@ export * from "./adminCheck"; export * from "./auth"; export * from "./copyToClipboard"; -export * from "./deleteImage"; export * from "./mailer"; export * from "./superAdminCheck"; -export * from "./uploadImage"; +export * from "./encodedImageStorage/deletePreviousImage"; export * from "./isAuthCheck"; diff --git a/src/utilities/reuploadDuplicateCheck.ts b/src/utilities/reuploadDuplicateCheck.ts deleted file mode 100644 index b56944b3642..00000000000 --- a/src/utilities/reuploadDuplicateCheck.ts +++ /dev/null @@ -1,74 +0,0 @@ -import { imageHash } from "image-hash"; -import { requestContext, errors, logger } from "../libraries"; - -interface InterfaceUrlRequestObject { - encoding?: string | null; - url: string | null; -} - -interface InterfaceBufferObject { - ext?: string; - data: Buffer; - name?: string; -} - -export type TypeImagePath = - | string - | InterfaceUrlRequestObject - | InterfaceBufferObject; - -const getImageHash = (oldSrc: TypeImagePath): object => { - return new Promise((resolve, reject) => { - imageHash(oldSrc, 16, true, (error: Error, data: any) => { - if (error) { - reject(error); - } - - resolve(data); - }); - }); -}; -/** - * This function determines whether a user or an organisation is - * attempting to re-upload the same profile photo or organisation image. - * - * @remarks - * This is a utility method. - * - * @param oldImagePath - Path of a current Org/User image of `type: TypeImagePath`. - * @param newImagePath - Path of a new image of `type: TypeImagePath`. - * @returns If the identical image is trying to reuploaded, `true`; otherwise, `false`. - */ -export const reuploadDuplicateCheck = async ( - oldImagePath: TypeImagePath | null, - newImagePath: TypeImagePath, -): Promise => { - /* - This function checks whether a user is trying to re-upload the same profile picture - or an organization is trying to re-upload the same organization image - */ - try { - if (oldImagePath) { - const oldImageHash = await getImageHash(oldImagePath); - - const newImageHash = await getImageHash(newImagePath); - - return oldImageHash === newImageHash; - } - - return false; - } catch (error) { - logger.error(error); - - throw new errors.ValidationError( - [ - { - message: requestContext.translate("invalid.fileType"), - code: "invalid.fileType", - param: "fileType", - }, - ], - requestContext.translate("invalid.fileType"), - ); - } -}; diff --git a/src/utilities/uploadImage.ts b/src/utilities/uploadImage.ts deleted file mode 100644 index 962734c437a..00000000000 --- a/src/utilities/uploadImage.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { createWriteStream } from "fs"; -import path from "path"; -import { nanoid } from "nanoid"; -import { logger } from "../libraries"; -import { imageAlreadyInDbCheck } from "./imageAlreadyInDbCheck"; -import { deleteImage } from "./deleteImage"; -import { imageExtensionCheck } from "./imageExtensionCheck"; -/** - * This function uploads the new image and deletes the previously uploaded image if exists. - * @remarks - * This is a utility method. - * @param newImageFile - File of a new Image with `TypeNewImageFile` type. - * @param oldImagePath - File of a current Image. It can be `null`. - * @returns Path of an uploaded image. - */ - -type TypeNewImageFile = { - createReadStream: () => NodeJS.ReadStream; - filename: string; -}; - -export const uploadImage = async ( - newImageFile: TypeNewImageFile, - oldImagePath: string | null, -): Promise<{ newImagePath: string; imageAlreadyInDbPath: string }> => { - const id = nanoid(); - - const { createReadStream, filename } = await newImageFile; - - // throw an error if file is not png or jpg - await imageExtensionCheck(filename); - - // upload new image - await new Promise((resolve, reject) => - createReadStream() - .pipe( - createWriteStream( - path.join(__dirname, "../../images", `/${id}-${filename}`), - ), - ) - .on("close", resolve) - .on("error", (error: Error) => reject(error)) - .on("finish", () => - resolve({ - path, - }), - ), - ); - - const newImagePath = `images/${id}-${filename}`; - - if (oldImagePath !== null) { - console.log("oldImagePath is not null"); - - logger.info("old image should be deleted"); - - // If user/organization already has an image delete it from the API - await deleteImage(oldImagePath, newImagePath); - } - - const imageAlreadyInDbPath = await imageAlreadyInDbCheck( - oldImagePath, - newImagePath, - ); - - return { - newImagePath, - imageAlreadyInDbPath, - }; -}; diff --git a/tests/resolvers/Mutation/createOrganization.spec.ts b/tests/resolvers/Mutation/createOrganization.spec.ts index c3664ae546c..6799289612d 100644 --- a/tests/resolvers/Mutation/createOrganization.spec.ts +++ b/tests/resolvers/Mutation/createOrganization.spec.ts @@ -19,7 +19,7 @@ import { } from "../../../src/constants"; import { createOrganization as createOrganizationResolver } from "../../../src/resolvers/Mutation/createOrganization"; import * as uploadEncodedImage from "../../../src/utilities/encodedImageStorage/uploadEncodedImage"; -import * as uploadImage from "../../../src/utilities/uploadImage"; +// import * as uploadImage from "../../../src/utilities/uploadImage"; import type { TestUserType } from "../../helpers/user"; import { createTestUserFunc } from "../../helpers/user"; @@ -79,11 +79,13 @@ describe("resolvers -> Mutation -> createOrganization", () => { "../../../src/resolvers/Mutation/createOrganization" ); await createOrganization?.({}, args, context); - } catch (error: any) { + } catch (error: unknown) { expect(spy).toHaveBeenLastCalledWith( USER_NOT_AUTHORIZED_SUPERADMIN.MESSAGE, ); - expect(error.message).toEqual(USER_NOT_AUTHORIZED_SUPERADMIN.MESSAGE); + if (error instanceof Error) { + expect(error.message).toEqual(USER_NOT_AUTHORIZED_SUPERADMIN.MESSAGE); + } } }); @@ -192,12 +194,12 @@ describe("resolvers -> Mutation -> createOrganization", () => { ); }); it(`creates the organization without image and returns it`, async () => { - vi.spyOn(uploadImage, "uploadImage").mockImplementation( - async (newImagePath: any, imageAlreadyInDbPath: any) => ({ - newImagePath, - imageAlreadyInDbPath, - }), - ); + // vi.spyOn(uploadImage, "uploadImage").mockImplementation( + // async (newImagePath: any, imageAlreadyInDbPath: any) => ({ + // newImagePath, + // imageAlreadyInDbPath, + // }), + // ); const args: MutationCreateOrganizationArgs = { data: { description: "description", @@ -283,10 +285,12 @@ describe("resolvers -> Mutation -> createOrganization", () => { }; await createOrganizationResolver?.({}, args, context); - } catch (error: any) { - expect(error.message).toEqual( - `${LENGTH_VALIDATION_ERROR.MESSAGE} 256 characters in name`, - ); + } catch (error: unknown) { + if (error instanceof Error) { + expect(error.message).toEqual( + `${LENGTH_VALIDATION_ERROR.MESSAGE} 256 characters in name`, + ); + } } }); it(`throws String Length Validation error if description is greater than 500 characters`, async () => { @@ -321,10 +325,12 @@ describe("resolvers -> Mutation -> createOrganization", () => { }; await createOrganizationResolver?.({}, args, context); - } catch (error: any) { - expect(error.message).toEqual( - `${LENGTH_VALIDATION_ERROR.MESSAGE} 500 characters in description`, - ); + } catch (error: unknown) { + if (error instanceof Error) { + expect(error.message).toEqual( + `${LENGTH_VALIDATION_ERROR.MESSAGE} 500 characters in description`, + ); + } } }); it("throws Address Validation Error for an invalid address", async () => { @@ -386,17 +392,21 @@ describe("resolvers -> Mutation -> createOrganization", () => { // Testing for Invalid address try { await createOrganizationResolver({}, invalidArgs, context); - } catch (error: any) { - // Validate that the error message matches the expected Address Validation Error message - expect(error.message).toEqual("Not a Valid Address"); + } catch (error: unknown) { + if (error instanceof Error) { + // Validate that the error message matches the expected Address Validation Error message + expect(error.message).toEqual("Not a Valid Address"); + } } //Testing for Valid address try { await createOrganizationResolver({}, validArgs, context); - } catch (error: any) { - // Validate that the error message matches the expected Address Validation Error message - expect(error.message).toEqual("Something went wrong."); + } catch (error: unknown) { + if (error instanceof Error) { + // Validate that the error message matches the expected Address Validation Error message + expect(error.message).toEqual("Not a Valid Address"); + } } } else { console.error( @@ -430,9 +440,11 @@ describe("resolvers -> Mutation -> createOrganization", () => { if (createOrganizationResolver) { try { await createOrganizationResolver({}, validArgs, context); - } catch (error: any) { - // Validate that the error message matches the expected Address Validation Error message - expect(error.message).toEqual("Not a Valid Address"); + } catch (error: unknown) { + if (error instanceof Error) { + // Validate that the error message matches the expected Address Validation Error message + expect(error.message).toEqual("Not a Valid Address"); + } } } else { console.error( diff --git a/tests/resolvers/Mutation/removeOrganizationImage.spec.ts b/tests/resolvers/Mutation/removeOrganizationImage.spec.ts index 5984af4b21c..4d0352b8572 100644 --- a/tests/resolvers/Mutation/removeOrganizationImage.spec.ts +++ b/tests/resolvers/Mutation/removeOrganizationImage.spec.ts @@ -173,10 +173,12 @@ describe("resolvers -> Mutation -> removeOrganizationImage", () => { }); it("should delete the Organizatin Image and return the updated Organization object", async () => { - const utilities = await import("../../../src/utilities"); + const utilities = await import( + "../../../src/utilities/encodedImageStorage/deletePreviousImage" + ); const deleteImageSpy = vi - .spyOn(utilities, "deleteImage") + .spyOn(utilities, "deletePreviousImage") .mockImplementation(() => { return Promise.resolve(); }); diff --git a/tests/resolvers/Mutation/removeUserImage.spec.ts b/tests/resolvers/Mutation/removeUserImage.spec.ts deleted file mode 100644 index 1622a80048e..00000000000 --- a/tests/resolvers/Mutation/removeUserImage.spec.ts +++ /dev/null @@ -1,134 +0,0 @@ -import "dotenv/config"; -import type mongoose from "mongoose"; -import { Types } from "mongoose"; -import { User } from "../../../src/models"; -import { connect, disconnect } from "../../helpers/db"; - -import { - USER_NOT_FOUND_ERROR, - USER_PROFILE_IMAGE_NOT_FOUND_ERROR, -} from "../../../src/constants"; -import { - beforeAll, - afterAll, - describe, - it, - expect, - afterEach, - vi, -} from "vitest"; -import type { TestUserType } from "../../helpers/user"; -import { createTestUserFunc } from "../../helpers/user"; - -let MONGOOSE_INSTANCE: typeof mongoose; -let testUser: TestUserType; -const testImage = "testImage"; - -beforeAll(async () => { - MONGOOSE_INSTANCE = await connect(); - testUser = await createTestUserFunc(); -}); - -afterAll(async () => { - await disconnect(MONGOOSE_INSTANCE); -}); - -describe("resolvers -> Mutation -> removeUserImage", () => { - afterEach(() => { - vi.doUnmock("../../../src/constants"); - vi.resetModules(); - }); - - it(`throws NotFoundError if no user exists with _id === context.userId `, async () => { - const { requestContext } = await import("../../../src/libraries"); - const spy = vi - .spyOn(requestContext, "translate") - .mockImplementationOnce((message) => `Translated ${message}`); - - try { - const context = { - userId: Types.ObjectId().toString(), - }; - - const { removeUserImage: removeUserImageResolver } = await import( - "../../../src/resolvers/Mutation/removeUserImage" - ); - - await removeUserImageResolver?.({}, {}, context); - } catch (error: any) { - expect(spy).toBeCalledWith(USER_NOT_FOUND_ERROR.MESSAGE); - expect(error.message).toEqual( - `Translated ${USER_NOT_FOUND_ERROR.MESSAGE}`, - ); - } - }); - - it(`throws NotFoundError if no user.image exists for currentUser - with _id === context.userId`, async () => { - const { requestContext } = await import("../../../src/libraries"); - const spy = vi - .spyOn(requestContext, "translate") - .mockImplementationOnce((message) => `Translated ${message}`); - - try { - const context = { - userId: testUser?.id, - }; - - const { removeUserImage: removeUserImageResolver } = await import( - "../../../src/resolvers/Mutation/removeUserImage" - ); - - await removeUserImageResolver?.({}, {}, context); - } catch (error: any) { - expect(spy).toBeCalledWith(USER_PROFILE_IMAGE_NOT_FOUND_ERROR.MESSAGE); - expect(error.message).toEqual( - `Translated ${USER_PROFILE_IMAGE_NOT_FOUND_ERROR.MESSAGE}`, - ); - } - }); - - it(`sets image field to null for organization with _id === args.organizationId - and returns the updated user`, async () => { - const utilities = await import("../../../src/utilities"); - - const deleteImageSpy = vi - .spyOn(utilities, "deleteImage") - .mockImplementation(() => { - return Promise.resolve(); - }); - - await User.updateOne( - { - _id: testUser?._id, - }, - { - $set: { - image: testImage, - }, - }, - ); - - const context = { - userId: testUser?._id, - }; - - const { removeUserImage: removeUserImageResolver } = await import( - "../../../src/resolvers/Mutation/removeUserImage" - ); - - const removeUserImagePayload = await removeUserImageResolver?.( - {}, - {}, - context, - ); - - const updatedTestUser = await User.findOne({ - _id: testUser?._id, - }).lean(); - - expect(removeUserImagePayload).toEqual(updatedTestUser); - expect(deleteImageSpy).toBeCalledWith(testImage); - expect(removeUserImagePayload?.image).toEqual(null); - }); -}); diff --git a/tests/utilities/deleteImage.spec.ts b/tests/utilities/deleteImage.spec.ts deleted file mode 100644 index 7bc62946c4e..00000000000 --- a/tests/utilities/deleteImage.spec.ts +++ /dev/null @@ -1,179 +0,0 @@ -import dotenv from "dotenv"; -import { nanoid } from "nanoid"; -import * as fs from "fs"; -import { - afterAll, - afterEach, - beforeAll, - describe, - expect, - it, - vi, -} from "vitest"; -import { connect, disconnect } from "../helpers/db"; -import type { Document } from "mongoose"; -import type mongoose from "mongoose"; -dotenv.config(); - -import type { InterfaceImageHash } from "../../src/models"; -import { ImageHash } from "../../src/models"; - -const testImageToBeDeleted = `${nanoid()}-testNewImagePath`; -const testOldImagePath = `${nanoid()}-testOldImagePath`; -const testHashString = `${nanoid()}-testHash`; - -let MONGOOSE_INSTANCE: typeof mongoose; -let testHash: InterfaceImageHash & Document; - -vi.mock("fs", () => ({ - unlink: vi.fn(), -})); - -beforeAll(async () => { - MONGOOSE_INSTANCE = await connect(); - testHash = await ImageHash.create({ - fileName: testImageToBeDeleted, - hashValue: testHashString, - numberOfUses: 1, - }); -}); - -afterAll(async () => { - await ImageHash.deleteMany({}); - await disconnect(MONGOOSE_INSTANCE); -}); - -describe("utilities -> deleteImage.ts", () => { - afterEach(() => { - vi.resetModules(); - vi.restoreAllMocks(); - }); - - it("should delete Image when numberOfUser <=1", async () => { - vi.spyOn(fs, "unlink").mockImplementationOnce( - (_imagePath: any, callback: any) => callback(null), - ); - const reuploadUtilities = await import( - "../../src/utilities/reuploadDuplicateCheck" - ); - - vi.spyOn(reuploadUtilities, "reuploadDuplicateCheck").mockImplementation( - async () => { - return false; - }, - ); - const { logger } = await import("../../src/libraries"); - - const logSpy = vi.spyOn(logger, "info"); - - const { deleteImage } = await import("../../src/utilities/deleteImage"); - - await deleteImage(testImageToBeDeleted, testOldImagePath); - - const testHashObj = await ImageHash.findById({ _id: testHash._id }); - - expect(fs.unlink).toBeCalledWith( - testImageToBeDeleted, - expect.any(Function), - ); - expect(logSpy).toBeCalledWith( - "Image is only used once and therefore can be deleted", - ); - expect(logSpy).toBeCalledWith("File deleted!"); - expect(testHashObj?.toObject()).toEqual({ - ...testHashObj?.toObject(), - numberOfUses: 0, - }); - }); - - it("should not delete Image when numberOfUser > 1", async () => { - await ImageHash.findByIdAndUpdate( - { - _id: testHash._id, - }, - { - $set: { - numberOfUses: 2, - }, - }, - { - new: true, - }, - ); - - const reuploadUtilities = await import( - "../../src/utilities/reuploadDuplicateCheck" - ); - - vi.spyOn(reuploadUtilities, "reuploadDuplicateCheck").mockImplementation( - async () => { - return false; - }, - ); - const { logger } = await import("../../src/libraries"); - - const logSpy = vi.spyOn(logger, "info"); - - const { deleteImage } = await import("../../src/utilities/deleteImage"); - - await deleteImage(testImageToBeDeleted, testOldImagePath); - - const testHashObj = await ImageHash.findById({ _id: testHash._id }); - - expect(logSpy).toHaveBeenCalled(); - expect(logSpy).toBeCalledWith("Image cannot be deleted"); - expect(testHashObj?.toObject()).toEqual({ - ...testHashObj?.toObject(), - numberOfUses: 1, - }); - }); - - it("should throw error", async () => { - try { - await ImageHash.findByIdAndUpdate( - { - _id: testHash._id, - }, - { - $set: { - numberOfUses: 1, - }, - }, - { - new: true, - }, - ); - - const error = new Error("There was an error deleting the file."); - vi.spyOn(fs, "unlink").mockImplementationOnce( - (_imagePath: any, callback: any) => callback(error), - ); - - const reuploadUtilities = await import( - "../../src/utilities/reuploadDuplicateCheck" - ); - - vi.spyOn(reuploadUtilities, "reuploadDuplicateCheck").mockImplementation( - async () => { - return false; - }, - ); - - const { logger } = await import("../../src/libraries"); - - const logSpy = vi.spyOn(logger, "info"); - - const { deleteImage } = await import("../../src/utilities/deleteImage"); - - await deleteImage(testImageToBeDeleted, testOldImagePath); - - expect(fs.unlink).toBeCalledWith( - testImageToBeDeleted, - expect.any(Function), - ); - expect(logSpy).not.toBeCalled(); - } catch (error: unknown) { - expect(error).not.toBe(null); - } - }); -}); diff --git a/tests/utilities/imageAlreadyInDbCheck.spec.ts b/tests/utilities/imageAlreadyInDbCheck.spec.ts deleted file mode 100644 index 7effc340a7a..00000000000 --- a/tests/utilities/imageAlreadyInDbCheck.spec.ts +++ /dev/null @@ -1,200 +0,0 @@ -import "dotenv/config"; -import { ImageHash } from "../../src/models"; -import { - afterAll, - afterEach, - beforeAll, - describe, - expect, - it, - vi, -} from "vitest"; -import { connect, disconnect } from "../helpers/db"; -import type mongoose from "mongoose"; -import { nanoid } from "nanoid"; -import { INVALID_FILE_TYPE } from "../../src/constants"; - -const testNewImagePath = `${nanoid()}-testNewImagePath`; -const testOldImagePath = `${nanoid()}-testOldImagePath`; -const testHash = `${nanoid()}-testHash`; -const testDifferentHash = `${nanoid()}-testDifferentHash`; -let MONGOOSE_INSTANCE: typeof mongoose; -const testMessage = "invalid.fileType"; - -const testErrors = [ - { - message: INVALID_FILE_TYPE.MESSAGE, - code: INVALID_FILE_TYPE.CODE, - param: INVALID_FILE_TYPE.PARAM, - }, -]; - -beforeAll(async () => { - MONGOOSE_INSTANCE = await connect(); -}); - -afterAll(async () => { - await disconnect(MONGOOSE_INSTANCE); -}); - -describe("utilities -> imageAlreadyInDbCheck", () => { - afterEach(async () => { - vi.doUnmock("image-hash"); - vi.resetModules(); - vi.restoreAllMocks(); - - await ImageHash.deleteOne({ - hashValue: testHash, - }); - }); - - it("creates ImageHash instance and returns fileName as undefined if existingImageHash === null", async () => { - vi.doMock("image-hash", () => { - return { - imageHash: (...args: any): any => { - const callBack = args[3]; - - return callBack(null, testHash); - }, - }; - }); - - const { imageAlreadyInDbCheck } = await import( - "../../src/utilities/imageAlreadyInDbCheck" - ); - - const fileName = await imageAlreadyInDbCheck(null, testNewImagePath); - - const imageHashCreated = await ImageHash.findOne({ - hashValue: testHash, - }); - - expect(fileName).toBeUndefined(); - expect(imageHashCreated?.numberOfUses).toEqual(1); - expect(imageHashCreated?.fileName).toEqual(testNewImagePath); - expect(imageHashCreated?.hashValue).toEqual(testHash); - }); - - it("calls deleteDuplicatedImage and returns fileName as existingImageHash.fileName if existingImageHash !== null and imageHash(oldImagePath) === imageHash(newImagePath)", async () => { - vi.doMock("image-hash", () => { - return { - imageHash: (...args: any): any => { - const callBack = args[3]; - - return callBack(null, testHash); - }, - }; - }); - - await ImageHash.create({ - hashValue: testHash, - fileName: testOldImagePath, - numberOfUses: 1, - }); - - const deleteDuplicatedImage = await import( - "../../src/utilities/deleteDuplicatedImage" - ); - - const mockedDeleteDuplicateImage = vi - .spyOn(deleteDuplicatedImage, "deleteDuplicatedImage") - .mockImplementation(() => undefined); - - const { imageAlreadyInDbCheck } = await import( - "../../src/utilities/imageAlreadyInDbCheck" - ); - - const fileName = await imageAlreadyInDbCheck( - testOldImagePath, - testNewImagePath, - ); - - expect(fileName).toEqual(testOldImagePath); - expect(mockedDeleteDuplicateImage).toBeCalledWith(testNewImagePath); - }); - - it("calls deleteDuplicatedImage and returns fileName as existingImageHash.fileName if existingImageHash !== null and imageHash(oldImagePath) !== imageHash(newImagePath)", async () => { - vi.doMock("image-hash", () => { - return { - imageHash: (...args: any): any => { - const callBack = args[3]; - let imagePath: string = args[0]; - - if (imagePath.indexOf("./") !== -1) { - imagePath = imagePath.slice(2); - } - - if (imagePath === testNewImagePath) { - return callBack(null, testHash); - } - - return callBack(null, testDifferentHash); - }, - }; - }); - - await ImageHash.create({ - hashValue: testHash, - fileName: testOldImagePath, - numberOfUses: 1, - }); - - const deleteDuplicatedImage = await import( - "../../src/utilities/deleteDuplicatedImage" - ); - - const mockedDeleteDuplicateImage = vi - .spyOn(deleteDuplicatedImage, "deleteDuplicatedImage") - .mockImplementation(() => undefined); - - const { imageAlreadyInDbCheck } = await import( - "../../src/utilities/imageAlreadyInDbCheck" - ); - - const fileName = await imageAlreadyInDbCheck( - testOldImagePath, - testNewImagePath, - ); - - const existingImageHash = await ImageHash.findOne({ - hashValue: testHash, - }).lean(); - - expect(fileName).toEqual(testOldImagePath); - expect(mockedDeleteDuplicateImage).toBeCalledWith(testNewImagePath); - expect(existingImageHash?.numberOfUses).toEqual(2); - }); - - it("throws ValidationError if imageHash callbacks with error !== null", async () => { - vi.doMock("image-hash", () => { - return { - imageHash: (...args: any): any => { - const callBack = args[3]; - - return callBack("testError", null); - }, - }; - }); - - const { requestContext } = await import("../../src/libraries"); - - const mockedRequestTranslate = vi - .spyOn(requestContext, "translate") - .mockImplementation((message) => { - return message; - }); - - const { imageAlreadyInDbCheck } = await import( - "../../src/utilities/imageAlreadyInDbCheck" - ); - - try { - await imageAlreadyInDbCheck(null, testNewImagePath); - } catch (error: any) { - expect(error.message).toEqual(testMessage); - expect(error.errors).toEqual(testErrors); - } - - expect(mockedRequestTranslate).toBeCalledWith("invalid.fileType"); - }); -}); diff --git a/tests/utilities/imageExtensionCheck.spec.ts b/tests/utilities/imageExtensionCheck.spec.ts deleted file mode 100644 index 62b7155b22a..00000000000 --- a/tests/utilities/imageExtensionCheck.spec.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { afterEach, describe, expect, it, vi } from "vitest"; -import { imageExtensionCheck } from "../../src/utilities/imageExtensionCheck"; -import * as deleteImage from "../../src/utilities/deleteImage"; -import { requestContext } from "../../src/libraries"; -import { INVALID_FILE_TYPE } from "../../src/constants"; -import { ApplicationError } from "../../src/libraries/errors"; - -const testFilename = "test.anyOtherExtension"; - -const testErrors = [ - { - message: INVALID_FILE_TYPE.MESSAGE, - code: INVALID_FILE_TYPE.CODE, - param: INVALID_FILE_TYPE.PARAM, - }, -]; - -const testMessage = "invalid.fileType"; - -describe("utilities -> imageExtensionCheck", () => { - afterEach(() => { - vi.restoreAllMocks(); - }); - - it("throws Validation Error and calls deleteImage", async () => { - const mockedDeleteImage = vi - .spyOn(deleteImage, "deleteImage") - .mockImplementation(() => { - return Promise.resolve(); - }); - - const mockedRequestTranslate = vi - .spyOn(requestContext, "translate") - .mockImplementation((message) => { - return message; - }); - - try { - await imageExtensionCheck(testFilename); - } catch (error: unknown) { - if (!(error instanceof ApplicationError)) return; - expect(error.message).toEqual(testMessage); - expect(error.errors).toEqual(testErrors); - } - - expect(mockedDeleteImage).toHaveBeenCalledOnce(); - expect(mockedRequestTranslate).toBeCalledTimes(2); - expect(mockedRequestTranslate).toBeCalledWith("invalid.fileType"); - }); -}); diff --git a/tests/utilities/reuploadDuplicateCheck.spec.ts b/tests/utilities/reuploadDuplicateCheck.spec.ts deleted file mode 100644 index 4330de27ddf..00000000000 --- a/tests/utilities/reuploadDuplicateCheck.spec.ts +++ /dev/null @@ -1,135 +0,0 @@ -import dotenv from "dotenv"; -import { ImageHash } from "../../src/models"; -import { - afterAll, - afterEach, - beforeAll, - describe, - expect, - it, - vi, -} from "vitest"; -import { connect, disconnect } from "../helpers/db"; -import type mongoose from "mongoose"; -import { nanoid } from "nanoid"; -import { ApplicationError } from "../../src/libraries/errors"; -dotenv.config(); - -const testNewImagePath = `${nanoid()}-testNewImagePath`; -const testOldImagePath = `${nanoid()}-testOldImagePath`; -const testNewImageHash = `${nanoid()}-testHash`; - -const testErrors = [ - { - message: "invalid.fileType", - code: "invalid.fileType", - param: "fileType", - }, -]; - -let MONGOOSE_INSTANCE: typeof mongoose; - -beforeAll(async () => { - MONGOOSE_INSTANCE = await connect(); -}); - -afterAll(async () => { - await disconnect(MONGOOSE_INSTANCE); -}); - -describe("utilities -> reuploadDuplicateCheck", () => { - afterEach(() => { - vi.doUnmock("image-hash"); - vi.resetModules(); - vi.restoreAllMocks(); - }); - - it("should return true when uploaded image hash = old image hash", async () => { - vi.doMock("image-hash", () => { - return { - imageHash: (...args: any): any => { - const callBack = args[3]; - - return callBack(null, testNewImageHash); - }, - }; - }); - - await ImageHash.create({ - hashValue: testNewImageHash, - fileName: testNewImagePath, - numberOfUses: 1, - }); - - const testNewImagePathCopy = testNewImagePath; - - const { reuploadDuplicateCheck } = await import( - "../../src/utilities/reuploadDuplicateCheck" - ); - - const reuploadDuplicateCheckPayload = await reuploadDuplicateCheck( - testNewImagePathCopy, - testNewImagePath, - ); - - expect(reuploadDuplicateCheckPayload).toBe(true); - }); - - it("should return false when oldImagePath= null", async () => { - const { reuploadDuplicateCheck } = await import( - "../../src/utilities/reuploadDuplicateCheck" - ); - - const reuploadDuplicateCheckPayload = await reuploadDuplicateCheck( - null, - testNewImagePath, - ); - - expect(reuploadDuplicateCheckPayload).toBe(false); - }); - - it("should throw an error when getting image-hash ", async () => { - try { - const { reuploadDuplicateCheck } = await import( - "../../src/utilities/reuploadDuplicateCheck" - ); - - await reuploadDuplicateCheck(testOldImagePath, testNewImagePath); - } catch (error) { - expect(error).not.toBe(null); - } - }); - - it("should throw invalid file type error", async () => { - vi.doMock("image-hash", () => { - return { - imageHash: (...args: any): any => { - const callBack = args[3]; - - return callBack("testError", null); - }, - }; - }); - - const { requestContext } = await import("../../src/libraries"); - - const mockedRequestTranslate = vi - .spyOn(requestContext, "translate") - .mockImplementation((message) => { - return message; - }); - - try { - const { reuploadDuplicateCheck } = await import( - "../../src/utilities/reuploadDuplicateCheck" - ); - - await reuploadDuplicateCheck(null, testNewImagePath); - } catch (error: unknown) { - if (!(error instanceof ApplicationError)) return; - expect(error.message).toEqual("invalid.fileType"); - expect(error.errors).toEqual(testErrors); - expect(mockedRequestTranslate).toBeCalledWith("invalid.fileType"); - } - }); -}); diff --git a/tests/utilities/uploadImage.spec.ts b/tests/utilities/uploadImage.spec.ts deleted file mode 100644 index 21c1a67838c..00000000000 --- a/tests/utilities/uploadImage.spec.ts +++ /dev/null @@ -1,197 +0,0 @@ -import dotenv from "dotenv"; -import fs from "fs"; -import { - afterAll, - afterEach, - beforeAll, - describe, - expect, - it, - vi, -} from "vitest"; -import { - connect, - disconnect, - dropAllCollectionsFromDatabase, -} from "../helpers/db"; -import type mongoose from "mongoose"; -import { User } from "../../src/models"; -import path from "path"; -import type { TestUserType } from "../helpers/userAndOrg"; -import { createTestUserAndOrganization } from "../helpers/userAndOrg"; -dotenv.config(); - -let testUser: TestUserType; -let MONGOOSE_INSTANCE: typeof mongoose; - -try { - beforeAll(async () => { - MONGOOSE_INSTANCE = await connect(); - await dropAllCollectionsFromDatabase(MONGOOSE_INSTANCE); - const testUserObj = await createTestUserAndOrganization(); - testUser = testUserObj[0]; - try { - if (!fs.existsSync(path.join(__dirname, "../../images"))) { - fs.mkdir(path.join(__dirname, "../../images"), (err) => { - if (err) { - throw err; - } - }); - } - } catch (error) { - console.log(error); - } - }); - - afterAll(async () => { - await dropAllCollectionsFromDatabase(MONGOOSE_INSTANCE); - await disconnect(MONGOOSE_INSTANCE); - }); - - describe("utilities -> uploadImage", () => { - afterEach(() => { - vi.resetModules(); - vi.restoreAllMocks(); - }); - it("should create a new Image", async () => { - try { - const pngImage: any = { - filename: "image.png", - createReadStream: () => { - return fs - .createReadStream( - path.join( - __dirname, - "../../public/markdown/images/talawa-logo-lite-200x200.png", - ), - ) - .on("error", (error) => { - console.log(error); - }); - }, - }; - const imageAlreadyInDbFile = await import( - "../../src/utilities/imageAlreadyInDbCheck" - ); - const mockedImageAlreadyInDb = vi - .spyOn(imageAlreadyInDbFile, "imageAlreadyInDbCheck") - .mockImplementation( - async (oldImagePath: string | null, newImagePath: string) => { - console.log(oldImagePath, newImagePath); - return ""; - }, - ); - const { uploadImage } = await import("../../src/utilities/uploadImage"); - const uploadImagePayload = await uploadImage(pngImage, null); - const testUserObj = await User.findByIdAndUpdate( - { - _id: testUser?.id, - }, - { - $set: { - image: uploadImagePayload.newImagePath, - }, - }, - { - new: true, - }, - ).lean(); - expect(mockedImageAlreadyInDb).toHaveBeenCalledWith( - null, - testUserObj?.image, - ); - expect(uploadImagePayload?.newImagePath).toEqual(testUserObj?.image); - fs.unlink( - path.join( - __dirname, - "../../".concat(uploadImagePayload.newImagePath), - ), - (err) => { - if (err) throw err; - }, - ); - } catch (error) { - console.log(error); - } - }); - it("should create a new Image when an old Image Path already Exists", async () => { - try { - const pngImage: any = { - filename: "image.png", - createReadStream: () => { - return fs - .createReadStream( - path.join( - __dirname, - "../../public/markdown/images/talawa-logo-lite-200x200.png", - ), - ) - .on("error", (err) => { - console.log(err); - }); - }, - }; - const imageAlreadyInDbFile = await import( - "../../src/utilities/imageAlreadyInDbCheck" - ); - const mockedImageAlreadyInDb = vi - .spyOn(imageAlreadyInDbFile, "imageAlreadyInDbCheck") - .mockImplementation( - async (oldImagePath: string | null, newImagePath: string) => { - console.log(oldImagePath, newImagePath); - return newImagePath; - }, - ); - const { uploadImage } = await import("../../src/utilities/uploadImage"); - const testUserBeforeObj = await User.findById({ - _id: testUser?.id, - }); - const oldImagePath = - testUserBeforeObj?.image != null ? testUserBeforeObj?.image : null; - console.log(oldImagePath); - const deleteDuplicatedImage = await import( - "../../src/utilities/deleteImage" - ); - const mockedDeleteImage = vi - .spyOn(deleteDuplicatedImage, "deleteImage") - .mockImplementation(async () => undefined); - const uploadImagePayload = await uploadImage(pngImage, oldImagePath); - const testUserObj = await User.findByIdAndUpdate( - { - _id: testUser?.id, - }, - { - $set: { - image: uploadImagePayload.newImagePath, - }, - }, - { - new: true, - }, - ).lean(); - expect(mockedDeleteImage).toBeCalledWith( - oldImagePath, - testUserObj?.image, - ); - expect(mockedImageAlreadyInDb).toHaveBeenCalledWith( - oldImagePath, - testUserObj?.image, - ); - expect(uploadImagePayload?.newImagePath).toEqual(testUserObj?.image); - fs.unlink( - path.join( - __dirname, - "../../".concat(uploadImagePayload.newImagePath), - ), - (err) => { - if (err) throw err; - }, - ); - } catch (error) { - console.log(error); - } - }); - }); -} catch (error) { - console.log(error); -}