mirror of
https://github.com/aaronpo97/the-biergarten-app.git
synced 2026-02-16 10:42:08 +00:00
Merge pull request #74 from aaronpo97/cleanup
Refactor controller logic out of api routes, refactor services, update documentation
This commit is contained in:
@@ -2,7 +2,9 @@
|
|||||||
"extends": ["next/core-web-vitals", "airbnb-base", "airbnb-typescript", "prettier"],
|
"extends": ["next/core-web-vitals", "airbnb-base", "airbnb-typescript", "prettier"],
|
||||||
"rules": {
|
"rules": {
|
||||||
"arrow-body-style": "off",
|
"arrow-body-style": "off",
|
||||||
"import/extensions": "off"
|
"import/extensions": "warn",
|
||||||
|
"import/order": "warn",
|
||||||
|
"import/no-extraneous-dependencies": ["error", { "devDependencies": true }]
|
||||||
},
|
},
|
||||||
"parserOptions": {
|
"parserOptions": {
|
||||||
"project": ["./tsconfig.json"]
|
"project": ["./tsconfig.json"]
|
||||||
|
|||||||
171
package-lock.json
generated
171
package-lock.json
generated
@@ -16,11 +16,10 @@
|
|||||||
"@mapbox/search-js-core": "^1.0.0-beta.17",
|
"@mapbox/search-js-core": "^1.0.0-beta.17",
|
||||||
"@mapbox/search-js-react": "^1.0.0-beta.17",
|
"@mapbox/search-js-react": "^1.0.0-beta.17",
|
||||||
"@next/bundle-analyzer": "^14.0.3",
|
"@next/bundle-analyzer": "^14.0.3",
|
||||||
"@prisma/client": "^5.6.0",
|
"@prisma/client": "^5.7.0",
|
||||||
"@react-email/components": "^0.0.11",
|
"@react-email/components": "^0.0.11",
|
||||||
"@react-email/render": "^0.0.9",
|
"@react-email/render": "^0.0.9",
|
||||||
"@react-email/tailwind": "^0.0.12",
|
"@react-email/tailwind": "^0.0.12",
|
||||||
"@types/express": "^4.17.21",
|
|
||||||
"@vercel/analytics": "^1.1.0",
|
"@vercel/analytics": "^1.1.0",
|
||||||
"argon2": "^0.31.1",
|
"argon2": "^0.31.1",
|
||||||
"cloudinary": "^1.41.0",
|
"cloudinary": "^1.41.0",
|
||||||
@@ -54,6 +53,7 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@faker-js/faker": "^8.3.1",
|
"@faker-js/faker": "^8.3.1",
|
||||||
"@types/cookie": "^0.5.1",
|
"@types/cookie": "^0.5.1",
|
||||||
|
"@types/express": "^4.17.21",
|
||||||
"@types/jsonwebtoken": "^9.0.2",
|
"@types/jsonwebtoken": "^9.0.2",
|
||||||
"@types/lodash": "^4.14.195",
|
"@types/lodash": "^4.14.195",
|
||||||
"@types/mapbox__mapbox-sdk": "^0.13.4",
|
"@types/mapbox__mapbox-sdk": "^0.13.4",
|
||||||
@@ -79,7 +79,7 @@
|
|||||||
"prettier": "^3.0.0",
|
"prettier": "^3.0.0",
|
||||||
"prettier-plugin-jsdoc": "^1.0.2",
|
"prettier-plugin-jsdoc": "^1.0.2",
|
||||||
"prettier-plugin-tailwindcss": "^0.5.7",
|
"prettier-plugin-tailwindcss": "^0.5.7",
|
||||||
"prisma": "^5.6.0",
|
"prisma": "^5.7.0",
|
||||||
"tailwindcss": "^3.3.3",
|
"tailwindcss": "^3.3.3",
|
||||||
"tailwindcss-animate": "^1.0.6",
|
"tailwindcss-animate": "^1.0.6",
|
||||||
"ts-node": "^10.9.1",
|
"ts-node": "^10.9.1",
|
||||||
@@ -1633,13 +1633,10 @@
|
|||||||
"integrity": "sha512-C16M+IYz0rgRhWZdCmK+h58JMv8vijAA61gmz2rspCSwKwzBebpdcsiUmwrtJRdphuY30i6BSLEOP8ppbNLyLg=="
|
"integrity": "sha512-C16M+IYz0rgRhWZdCmK+h58JMv8vijAA61gmz2rspCSwKwzBebpdcsiUmwrtJRdphuY30i6BSLEOP8ppbNLyLg=="
|
||||||
},
|
},
|
||||||
"node_modules/@prisma/client": {
|
"node_modules/@prisma/client": {
|
||||||
"version": "5.6.0",
|
"version": "5.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/@prisma/client/-/client-5.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/@prisma/client/-/client-5.7.0.tgz",
|
||||||
"integrity": "sha512-mUDefQFa1wWqk4+JhKPYq8BdVoFk9NFMBXUI8jAkBfQTtgx8WPx02U2HB/XbAz3GSUJpeJOKJQtNvaAIDs6sug==",
|
"integrity": "sha512-cZmglCrfNbYpzUtz7HscVHl38e9CrUs31nrVoGUK1nIPXGgt8hT4jj2s657UXcNdQ/jBUxDgGmHyu2Nyrq1txg==",
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"dependencies": {
|
|
||||||
"@prisma/engines-version": "5.6.0-32.e95e739751f42d8ca026f6b910f5a2dc5adeaeee"
|
|
||||||
},
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=16.13"
|
"node": ">=16.13"
|
||||||
},
|
},
|
||||||
@@ -1652,17 +1649,50 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@prisma/debug": {
|
||||||
|
"version": "5.7.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-5.7.0.tgz",
|
||||||
|
"integrity": "sha512-tZ+MOjWlVvz1kOEhNYMa4QUGURY+kgOUBqLHYIV8jmCsMuvA1tWcn7qtIMLzYWCbDcQT4ZS8xDgK0R2gl6/0wA==",
|
||||||
|
"devOptional": true
|
||||||
|
},
|
||||||
"node_modules/@prisma/engines": {
|
"node_modules/@prisma/engines": {
|
||||||
"version": "5.6.0",
|
"version": "5.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-5.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-5.7.0.tgz",
|
||||||
"integrity": "sha512-Mt2q+GNJpU2vFn6kif24oRSBQv1KOkYaterQsi0k2/lA+dLvhRX6Lm26gon6PYHwUM8/h8KRgXIUMU0PCLB6bw==",
|
"integrity": "sha512-TkOMgMm60n5YgEKPn9erIvFX2/QuWnl3GBo6yTRyZKk5O5KQertXiNnrYgSLy0SpsKmhovEPQb+D4l0SzyE7XA==",
|
||||||
"devOptional": true,
|
"devOptional": true,
|
||||||
"hasInstallScript": true
|
"hasInstallScript": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@prisma/debug": "5.7.0",
|
||||||
|
"@prisma/engines-version": "5.7.0-41.79fb5193cf0a8fdbef536e4b4a159cad677ab1b9",
|
||||||
|
"@prisma/fetch-engine": "5.7.0",
|
||||||
|
"@prisma/get-platform": "5.7.0"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@prisma/engines-version": {
|
"node_modules/@prisma/engines-version": {
|
||||||
"version": "5.6.0-32.e95e739751f42d8ca026f6b910f5a2dc5adeaeee",
|
"version": "5.7.0-41.79fb5193cf0a8fdbef536e4b4a159cad677ab1b9",
|
||||||
"resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-5.6.0-32.e95e739751f42d8ca026f6b910f5a2dc5adeaeee.tgz",
|
"resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-5.7.0-41.79fb5193cf0a8fdbef536e4b4a159cad677ab1b9.tgz",
|
||||||
"integrity": "sha512-UoFgbV1awGL/3wXuUK3GDaX2SolqczeeJ5b4FVec9tzeGbSWJboPSbT0psSrmgYAKiKnkOPFSLlH6+b+IyOwAw=="
|
"integrity": "sha512-V6tgRVi62jRwTm0Hglky3Scwjr/AKFBFtS+MdbsBr7UOuiu1TKLPc6xfPiyEN1+bYqjEtjxwGsHgahcJsd1rNg==",
|
||||||
|
"devOptional": true
|
||||||
|
},
|
||||||
|
"node_modules/@prisma/fetch-engine": {
|
||||||
|
"version": "5.7.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-5.7.0.tgz",
|
||||||
|
"integrity": "sha512-zIn/qmO+N/3FYe7/L9o+yZseIU8ivh4NdPKSkQRIHfg2QVTVMnbhGoTcecbxfVubeTp+DjcbjS0H9fCuM4W04w==",
|
||||||
|
"devOptional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@prisma/debug": "5.7.0",
|
||||||
|
"@prisma/engines-version": "5.7.0-41.79fb5193cf0a8fdbef536e4b4a159cad677ab1b9",
|
||||||
|
"@prisma/get-platform": "5.7.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@prisma/get-platform": {
|
||||||
|
"version": "5.7.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-5.7.0.tgz",
|
||||||
|
"integrity": "sha512-ZeV/Op4bZsWXuw5Tg05WwRI8BlKiRFhsixPcAM+5BKYSiUZiMKIi713tfT3drBq8+T0E1arNZgYSA9QYcglWNA==",
|
||||||
|
"devOptional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@prisma/debug": "5.7.0"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@radix-ui/react-compose-refs": {
|
"node_modules/@radix-ui/react-compose-refs": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
@@ -2061,6 +2091,7 @@
|
|||||||
"version": "1.19.3",
|
"version": "1.19.3",
|
||||||
"resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.3.tgz",
|
"resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.3.tgz",
|
||||||
"integrity": "sha512-oyl4jvAfTGX9Bt6Or4H9ni1Z447/tQuxnZsytsCaExKlmJiU8sFgnIBRzJUpKwB5eWn9HuBYlUlVA74q/yN0eQ==",
|
"integrity": "sha512-oyl4jvAfTGX9Bt6Or4H9ni1Z447/tQuxnZsytsCaExKlmJiU8sFgnIBRzJUpKwB5eWn9HuBYlUlVA74q/yN0eQ==",
|
||||||
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/connect": "*",
|
"@types/connect": "*",
|
||||||
"@types/node": "*"
|
"@types/node": "*"
|
||||||
@@ -2087,6 +2118,7 @@
|
|||||||
"version": "3.4.36",
|
"version": "3.4.36",
|
||||||
"resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.36.tgz",
|
"resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.36.tgz",
|
||||||
"integrity": "sha512-P63Zd/JUGq+PdrM1lv0Wv5SBYeA2+CORvbrXbngriYY0jzLUWfQMQQxOhjONEz/wlHOAxOdY7CY65rgQdTjq2w==",
|
"integrity": "sha512-P63Zd/JUGq+PdrM1lv0Wv5SBYeA2+CORvbrXbngriYY0jzLUWfQMQQxOhjONEz/wlHOAxOdY7CY65rgQdTjq2w==",
|
||||||
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/node": "*"
|
"@types/node": "*"
|
||||||
}
|
}
|
||||||
@@ -2110,6 +2142,7 @@
|
|||||||
"version": "4.17.21",
|
"version": "4.17.21",
|
||||||
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz",
|
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz",
|
||||||
"integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==",
|
"integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==",
|
||||||
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/body-parser": "*",
|
"@types/body-parser": "*",
|
||||||
"@types/express-serve-static-core": "^4.17.33",
|
"@types/express-serve-static-core": "^4.17.33",
|
||||||
@@ -2121,6 +2154,7 @@
|
|||||||
"version": "4.17.37",
|
"version": "4.17.37",
|
||||||
"resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.37.tgz",
|
"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==",
|
"integrity": "sha512-ZohaCYTgGFcOP7u6aJOhY9uIZQgZ2vxC2yWoArY+FeDXlqeH66ZVBjgvg+RLVAS/DWNq4Ap9ZXu1+SUQiiWYMg==",
|
||||||
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/node": "*",
|
"@types/node": "*",
|
||||||
"@types/qs": "*",
|
"@types/qs": "*",
|
||||||
@@ -2141,7 +2175,8 @@
|
|||||||
"node_modules/@types/http-errors": {
|
"node_modules/@types/http-errors": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.2.tgz",
|
||||||
"integrity": "sha512-lPG6KlZs88gef6aD85z3HNkztpj7w2R7HmR3gygjfXCQmsLloWNARFkMuzKiiY8FGdh1XDpgBdrSf4aKDiA7Kg=="
|
"integrity": "sha512-lPG6KlZs88gef6aD85z3HNkztpj7w2R7HmR3gygjfXCQmsLloWNARFkMuzKiiY8FGdh1XDpgBdrSf4aKDiA7Kg==",
|
||||||
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/@types/json-schema": {
|
"node_modules/@types/json-schema": {
|
||||||
"version": "7.0.13",
|
"version": "7.0.13",
|
||||||
@@ -2216,7 +2251,8 @@
|
|||||||
"node_modules/@types/mime": {
|
"node_modules/@types/mime": {
|
||||||
"version": "1.3.3",
|
"version": "1.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.3.tgz",
|
||||||
"integrity": "sha512-Ys+/St+2VF4+xuY6+kDIXGxbNRO0mesVg0bbxEfB97Od1Vjpjx9KD1qxs64Gcb3CWPirk9Xe+PT4YiiHQ9T+eg=="
|
"integrity": "sha512-Ys+/St+2VF4+xuY6+kDIXGxbNRO0mesVg0bbxEfB97Od1Vjpjx9KD1qxs64Gcb3CWPirk9Xe+PT4YiiHQ9T+eg==",
|
||||||
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/@types/minimist": {
|
"node_modules/@types/minimist": {
|
||||||
"version": "1.2.3",
|
"version": "1.2.3",
|
||||||
@@ -2310,12 +2346,14 @@
|
|||||||
"node_modules/@types/qs": {
|
"node_modules/@types/qs": {
|
||||||
"version": "6.9.8",
|
"version": "6.9.8",
|
||||||
"resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.8.tgz",
|
"resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.8.tgz",
|
||||||
"integrity": "sha512-u95svzDlTysU5xecFNTgfFG5RUWu1A9P0VzgpcIiGZA9iraHOdSzcxMxQ55DyeRaGCSxQi7LxXDI4rzq/MYfdg=="
|
"integrity": "sha512-u95svzDlTysU5xecFNTgfFG5RUWu1A9P0VzgpcIiGZA9iraHOdSzcxMxQ55DyeRaGCSxQi7LxXDI4rzq/MYfdg==",
|
||||||
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/@types/range-parser": {
|
"node_modules/@types/range-parser": {
|
||||||
"version": "1.2.5",
|
"version": "1.2.5",
|
||||||
"resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.5.tgz",
|
"resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.5.tgz",
|
||||||
"integrity": "sha512-xrO9OoVPqFuYyR/loIHjnbvvyRZREYKLjxV4+dY6v3FQR3stQ9ZxIGkaclF7YhI9hfjpuTbu14hZEy94qKLtOA=="
|
"integrity": "sha512-xrO9OoVPqFuYyR/loIHjnbvvyRZREYKLjxV4+dY6v3FQR3stQ9ZxIGkaclF7YhI9hfjpuTbu14hZEy94qKLtOA==",
|
||||||
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/@types/react": {
|
"node_modules/@types/react": {
|
||||||
"version": "18.2.25",
|
"version": "18.2.25",
|
||||||
@@ -2393,6 +2431,7 @@
|
|||||||
"version": "0.17.2",
|
"version": "0.17.2",
|
||||||
"resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.2.tgz",
|
"resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.2.tgz",
|
||||||
"integrity": "sha512-aAG6yRf6r0wQ29bkS+x97BIs64ZLxeE/ARwyS6wrldMm3C1MdKwCcnnEwMC1slI8wuxJOpiUH9MioC0A0i+GJw==",
|
"integrity": "sha512-aAG6yRf6r0wQ29bkS+x97BIs64ZLxeE/ARwyS6wrldMm3C1MdKwCcnnEwMC1slI8wuxJOpiUH9MioC0A0i+GJw==",
|
||||||
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/mime": "^1",
|
"@types/mime": "^1",
|
||||||
"@types/node": "*"
|
"@types/node": "*"
|
||||||
@@ -2402,6 +2441,7 @@
|
|||||||
"version": "1.15.3",
|
"version": "1.15.3",
|
||||||
"resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.3.tgz",
|
"resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.3.tgz",
|
||||||
"integrity": "sha512-yVRvFsEMrv7s0lGhzrggJjNOSmZCdgCjw9xWrPr/kNNLp6FaDfMC1KaYl3TSJ0c58bECwNBMoQrZJ8hA8E1eFg==",
|
"integrity": "sha512-yVRvFsEMrv7s0lGhzrggJjNOSmZCdgCjw9xWrPr/kNNLp6FaDfMC1KaYl3TSJ0c58bECwNBMoQrZJ8hA8E1eFg==",
|
||||||
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/http-errors": "*",
|
"@types/http-errors": "*",
|
||||||
"@types/mime": "*",
|
"@types/mime": "*",
|
||||||
@@ -8522,13 +8562,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/prisma": {
|
"node_modules/prisma": {
|
||||||
"version": "5.6.0",
|
"version": "5.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/prisma/-/prisma-5.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/prisma/-/prisma-5.7.0.tgz",
|
||||||
"integrity": "sha512-EEaccku4ZGshdr2cthYHhf7iyvCcXqwJDvnoQRAJg5ge2Tzpv0e2BaMCp+CbbDUwoVTzwgOap9Zp+d4jFa2O9A==",
|
"integrity": "sha512-0rcfXO2ErmGAtxnuTNHQT9ztL0zZheQjOI/VNJzdq87C3TlGPQtMqtM+KCwU6XtmkoEr7vbCQqA7HF9IY0ST+Q==",
|
||||||
"devOptional": true,
|
"devOptional": true,
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@prisma/engines": "5.6.0"
|
"@prisma/engines": "5.7.0"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"prisma": "build/index.js"
|
"prisma": "build/index.js"
|
||||||
@@ -11843,23 +11883,54 @@
|
|||||||
"integrity": "sha512-C16M+IYz0rgRhWZdCmK+h58JMv8vijAA61gmz2rspCSwKwzBebpdcsiUmwrtJRdphuY30i6BSLEOP8ppbNLyLg=="
|
"integrity": "sha512-C16M+IYz0rgRhWZdCmK+h58JMv8vijAA61gmz2rspCSwKwzBebpdcsiUmwrtJRdphuY30i6BSLEOP8ppbNLyLg=="
|
||||||
},
|
},
|
||||||
"@prisma/client": {
|
"@prisma/client": {
|
||||||
"version": "5.6.0",
|
"version": "5.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/@prisma/client/-/client-5.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/@prisma/client/-/client-5.7.0.tgz",
|
||||||
"integrity": "sha512-mUDefQFa1wWqk4+JhKPYq8BdVoFk9NFMBXUI8jAkBfQTtgx8WPx02U2HB/XbAz3GSUJpeJOKJQtNvaAIDs6sug==",
|
"integrity": "sha512-cZmglCrfNbYpzUtz7HscVHl38e9CrUs31nrVoGUK1nIPXGgt8hT4jj2s657UXcNdQ/jBUxDgGmHyu2Nyrq1txg==",
|
||||||
"requires": {
|
"requires": {}
|
||||||
"@prisma/engines-version": "5.6.0-32.e95e739751f42d8ca026f6b910f5a2dc5adeaeee"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"@prisma/engines": {
|
"@prisma/debug": {
|
||||||
"version": "5.6.0",
|
"version": "5.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-5.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-5.7.0.tgz",
|
||||||
"integrity": "sha512-Mt2q+GNJpU2vFn6kif24oRSBQv1KOkYaterQsi0k2/lA+dLvhRX6Lm26gon6PYHwUM8/h8KRgXIUMU0PCLB6bw==",
|
"integrity": "sha512-tZ+MOjWlVvz1kOEhNYMa4QUGURY+kgOUBqLHYIV8jmCsMuvA1tWcn7qtIMLzYWCbDcQT4ZS8xDgK0R2gl6/0wA==",
|
||||||
"devOptional": true
|
"devOptional": true
|
||||||
},
|
},
|
||||||
|
"@prisma/engines": {
|
||||||
|
"version": "5.7.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-5.7.0.tgz",
|
||||||
|
"integrity": "sha512-TkOMgMm60n5YgEKPn9erIvFX2/QuWnl3GBo6yTRyZKk5O5KQertXiNnrYgSLy0SpsKmhovEPQb+D4l0SzyE7XA==",
|
||||||
|
"devOptional": true,
|
||||||
|
"requires": {
|
||||||
|
"@prisma/debug": "5.7.0",
|
||||||
|
"@prisma/engines-version": "5.7.0-41.79fb5193cf0a8fdbef536e4b4a159cad677ab1b9",
|
||||||
|
"@prisma/fetch-engine": "5.7.0",
|
||||||
|
"@prisma/get-platform": "5.7.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@prisma/engines-version": {
|
"@prisma/engines-version": {
|
||||||
"version": "5.6.0-32.e95e739751f42d8ca026f6b910f5a2dc5adeaeee",
|
"version": "5.7.0-41.79fb5193cf0a8fdbef536e4b4a159cad677ab1b9",
|
||||||
"resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-5.6.0-32.e95e739751f42d8ca026f6b910f5a2dc5adeaeee.tgz",
|
"resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-5.7.0-41.79fb5193cf0a8fdbef536e4b4a159cad677ab1b9.tgz",
|
||||||
"integrity": "sha512-UoFgbV1awGL/3wXuUK3GDaX2SolqczeeJ5b4FVec9tzeGbSWJboPSbT0psSrmgYAKiKnkOPFSLlH6+b+IyOwAw=="
|
"integrity": "sha512-V6tgRVi62jRwTm0Hglky3Scwjr/AKFBFtS+MdbsBr7UOuiu1TKLPc6xfPiyEN1+bYqjEtjxwGsHgahcJsd1rNg==",
|
||||||
|
"devOptional": true
|
||||||
|
},
|
||||||
|
"@prisma/fetch-engine": {
|
||||||
|
"version": "5.7.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-5.7.0.tgz",
|
||||||
|
"integrity": "sha512-zIn/qmO+N/3FYe7/L9o+yZseIU8ivh4NdPKSkQRIHfg2QVTVMnbhGoTcecbxfVubeTp+DjcbjS0H9fCuM4W04w==",
|
||||||
|
"devOptional": true,
|
||||||
|
"requires": {
|
||||||
|
"@prisma/debug": "5.7.0",
|
||||||
|
"@prisma/engines-version": "5.7.0-41.79fb5193cf0a8fdbef536e4b4a159cad677ab1b9",
|
||||||
|
"@prisma/get-platform": "5.7.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@prisma/get-platform": {
|
||||||
|
"version": "5.7.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-5.7.0.tgz",
|
||||||
|
"integrity": "sha512-ZeV/Op4bZsWXuw5Tg05WwRI8BlKiRFhsixPcAM+5BKYSiUZiMKIi713tfT3drBq8+T0E1arNZgYSA9QYcglWNA==",
|
||||||
|
"devOptional": true,
|
||||||
|
"requires": {
|
||||||
|
"@prisma/debug": "5.7.0"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"@radix-ui/react-compose-refs": {
|
"@radix-ui/react-compose-refs": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
@@ -12133,6 +12204,7 @@
|
|||||||
"version": "1.19.3",
|
"version": "1.19.3",
|
||||||
"resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.3.tgz",
|
"resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.3.tgz",
|
||||||
"integrity": "sha512-oyl4jvAfTGX9Bt6Or4H9ni1Z447/tQuxnZsytsCaExKlmJiU8sFgnIBRzJUpKwB5eWn9HuBYlUlVA74q/yN0eQ==",
|
"integrity": "sha512-oyl4jvAfTGX9Bt6Or4H9ni1Z447/tQuxnZsytsCaExKlmJiU8sFgnIBRzJUpKwB5eWn9HuBYlUlVA74q/yN0eQ==",
|
||||||
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@types/connect": "*",
|
"@types/connect": "*",
|
||||||
"@types/node": "*"
|
"@types/node": "*"
|
||||||
@@ -12159,6 +12231,7 @@
|
|||||||
"version": "3.4.36",
|
"version": "3.4.36",
|
||||||
"resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.36.tgz",
|
"resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.36.tgz",
|
||||||
"integrity": "sha512-P63Zd/JUGq+PdrM1lv0Wv5SBYeA2+CORvbrXbngriYY0jzLUWfQMQQxOhjONEz/wlHOAxOdY7CY65rgQdTjq2w==",
|
"integrity": "sha512-P63Zd/JUGq+PdrM1lv0Wv5SBYeA2+CORvbrXbngriYY0jzLUWfQMQQxOhjONEz/wlHOAxOdY7CY65rgQdTjq2w==",
|
||||||
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@types/node": "*"
|
"@types/node": "*"
|
||||||
}
|
}
|
||||||
@@ -12182,6 +12255,7 @@
|
|||||||
"version": "4.17.21",
|
"version": "4.17.21",
|
||||||
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz",
|
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz",
|
||||||
"integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==",
|
"integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==",
|
||||||
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@types/body-parser": "*",
|
"@types/body-parser": "*",
|
||||||
"@types/express-serve-static-core": "^4.17.33",
|
"@types/express-serve-static-core": "^4.17.33",
|
||||||
@@ -12193,6 +12267,7 @@
|
|||||||
"version": "4.17.37",
|
"version": "4.17.37",
|
||||||
"resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.37.tgz",
|
"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==",
|
"integrity": "sha512-ZohaCYTgGFcOP7u6aJOhY9uIZQgZ2vxC2yWoArY+FeDXlqeH66ZVBjgvg+RLVAS/DWNq4Ap9ZXu1+SUQiiWYMg==",
|
||||||
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@types/node": "*",
|
"@types/node": "*",
|
||||||
"@types/qs": "*",
|
"@types/qs": "*",
|
||||||
@@ -12213,7 +12288,8 @@
|
|||||||
"@types/http-errors": {
|
"@types/http-errors": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.2.tgz",
|
||||||
"integrity": "sha512-lPG6KlZs88gef6aD85z3HNkztpj7w2R7HmR3gygjfXCQmsLloWNARFkMuzKiiY8FGdh1XDpgBdrSf4aKDiA7Kg=="
|
"integrity": "sha512-lPG6KlZs88gef6aD85z3HNkztpj7w2R7HmR3gygjfXCQmsLloWNARFkMuzKiiY8FGdh1XDpgBdrSf4aKDiA7Kg==",
|
||||||
|
"dev": true
|
||||||
},
|
},
|
||||||
"@types/json-schema": {
|
"@types/json-schema": {
|
||||||
"version": "7.0.13",
|
"version": "7.0.13",
|
||||||
@@ -12288,7 +12364,8 @@
|
|||||||
"@types/mime": {
|
"@types/mime": {
|
||||||
"version": "1.3.3",
|
"version": "1.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.3.tgz",
|
||||||
"integrity": "sha512-Ys+/St+2VF4+xuY6+kDIXGxbNRO0mesVg0bbxEfB97Od1Vjpjx9KD1qxs64Gcb3CWPirk9Xe+PT4YiiHQ9T+eg=="
|
"integrity": "sha512-Ys+/St+2VF4+xuY6+kDIXGxbNRO0mesVg0bbxEfB97Od1Vjpjx9KD1qxs64Gcb3CWPirk9Xe+PT4YiiHQ9T+eg==",
|
||||||
|
"dev": true
|
||||||
},
|
},
|
||||||
"@types/minimist": {
|
"@types/minimist": {
|
||||||
"version": "1.2.3",
|
"version": "1.2.3",
|
||||||
@@ -12381,12 +12458,14 @@
|
|||||||
"@types/qs": {
|
"@types/qs": {
|
||||||
"version": "6.9.8",
|
"version": "6.9.8",
|
||||||
"resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.8.tgz",
|
"resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.8.tgz",
|
||||||
"integrity": "sha512-u95svzDlTysU5xecFNTgfFG5RUWu1A9P0VzgpcIiGZA9iraHOdSzcxMxQ55DyeRaGCSxQi7LxXDI4rzq/MYfdg=="
|
"integrity": "sha512-u95svzDlTysU5xecFNTgfFG5RUWu1A9P0VzgpcIiGZA9iraHOdSzcxMxQ55DyeRaGCSxQi7LxXDI4rzq/MYfdg==",
|
||||||
|
"dev": true
|
||||||
},
|
},
|
||||||
"@types/range-parser": {
|
"@types/range-parser": {
|
||||||
"version": "1.2.5",
|
"version": "1.2.5",
|
||||||
"resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.5.tgz",
|
"resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.5.tgz",
|
||||||
"integrity": "sha512-xrO9OoVPqFuYyR/loIHjnbvvyRZREYKLjxV4+dY6v3FQR3stQ9ZxIGkaclF7YhI9hfjpuTbu14hZEy94qKLtOA=="
|
"integrity": "sha512-xrO9OoVPqFuYyR/loIHjnbvvyRZREYKLjxV4+dY6v3FQR3stQ9ZxIGkaclF7YhI9hfjpuTbu14hZEy94qKLtOA==",
|
||||||
|
"dev": true
|
||||||
},
|
},
|
||||||
"@types/react": {
|
"@types/react": {
|
||||||
"version": "18.2.25",
|
"version": "18.2.25",
|
||||||
@@ -12463,6 +12542,7 @@
|
|||||||
"version": "0.17.2",
|
"version": "0.17.2",
|
||||||
"resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.2.tgz",
|
"resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.2.tgz",
|
||||||
"integrity": "sha512-aAG6yRf6r0wQ29bkS+x97BIs64ZLxeE/ARwyS6wrldMm3C1MdKwCcnnEwMC1slI8wuxJOpiUH9MioC0A0i+GJw==",
|
"integrity": "sha512-aAG6yRf6r0wQ29bkS+x97BIs64ZLxeE/ARwyS6wrldMm3C1MdKwCcnnEwMC1slI8wuxJOpiUH9MioC0A0i+GJw==",
|
||||||
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@types/mime": "^1",
|
"@types/mime": "^1",
|
||||||
"@types/node": "*"
|
"@types/node": "*"
|
||||||
@@ -12472,6 +12552,7 @@
|
|||||||
"version": "1.15.3",
|
"version": "1.15.3",
|
||||||
"resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.3.tgz",
|
"resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.3.tgz",
|
||||||
"integrity": "sha512-yVRvFsEMrv7s0lGhzrggJjNOSmZCdgCjw9xWrPr/kNNLp6FaDfMC1KaYl3TSJ0c58bECwNBMoQrZJ8hA8E1eFg==",
|
"integrity": "sha512-yVRvFsEMrv7s0lGhzrggJjNOSmZCdgCjw9xWrPr/kNNLp6FaDfMC1KaYl3TSJ0c58bECwNBMoQrZJ8hA8E1eFg==",
|
||||||
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@types/http-errors": "*",
|
"@types/http-errors": "*",
|
||||||
"@types/mime": "*",
|
"@types/mime": "*",
|
||||||
@@ -16745,12 +16826,12 @@
|
|||||||
"integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg=="
|
"integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg=="
|
||||||
},
|
},
|
||||||
"prisma": {
|
"prisma": {
|
||||||
"version": "5.6.0",
|
"version": "5.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/prisma/-/prisma-5.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/prisma/-/prisma-5.7.0.tgz",
|
||||||
"integrity": "sha512-EEaccku4ZGshdr2cthYHhf7iyvCcXqwJDvnoQRAJg5ge2Tzpv0e2BaMCp+CbbDUwoVTzwgOap9Zp+d4jFa2O9A==",
|
"integrity": "sha512-0rcfXO2ErmGAtxnuTNHQT9ztL0zZheQjOI/VNJzdq87C3TlGPQtMqtM+KCwU6XtmkoEr7vbCQqA7HF9IY0ST+Q==",
|
||||||
"devOptional": true,
|
"devOptional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@prisma/engines": "5.6.0"
|
"@prisma/engines": "5.7.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"process": {
|
"process": {
|
||||||
|
|||||||
23
package.json
23
package.json
@@ -5,10 +5,11 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "next dev",
|
"dev": "next dev",
|
||||||
"build": "next build",
|
"build": "next build",
|
||||||
|
"prestart": "npm run build",
|
||||||
"start": "next start",
|
"start": "next start",
|
||||||
"lint": "next lint",
|
"lint": "next lint",
|
||||||
"clear-db": "npx ts-node ./src/prisma/seed/clear/index.ts",
|
"clear-db": "npx ts-node ./src/prisma/seed/clear/index.ts",
|
||||||
"format": "npx prettier . --write",
|
"format": "npx prettier . --write; npx prisma format;",
|
||||||
"format-watch": "npx onchange \"**/*\" -- prettier --write --ignore-unknown {{changed}}",
|
"format-watch": "npx onchange \"**/*\" -- prettier --write --ignore-unknown {{changed}}",
|
||||||
"seed": "npx --max-old-space-size=4096 ts-node ./src/prisma/seed/index.ts"
|
"seed": "npx --max-old-space-size=4096 ts-node ./src/prisma/seed/index.ts"
|
||||||
},
|
},
|
||||||
@@ -21,11 +22,10 @@
|
|||||||
"@mapbox/search-js-core": "^1.0.0-beta.17",
|
"@mapbox/search-js-core": "^1.0.0-beta.17",
|
||||||
"@mapbox/search-js-react": "^1.0.0-beta.17",
|
"@mapbox/search-js-react": "^1.0.0-beta.17",
|
||||||
"@next/bundle-analyzer": "^14.0.3",
|
"@next/bundle-analyzer": "^14.0.3",
|
||||||
"@prisma/client": "^5.6.0",
|
"@prisma/client": "^5.7.0",
|
||||||
"@react-email/components": "^0.0.11",
|
"@react-email/components": "^0.0.11",
|
||||||
"@react-email/render": "^0.0.9",
|
"@react-email/render": "^0.0.9",
|
||||||
"@react-email/tailwind": "^0.0.12",
|
"@react-email/tailwind": "^0.0.12",
|
||||||
"@types/express": "^4.17.21",
|
|
||||||
"@vercel/analytics": "^1.1.0",
|
"@vercel/analytics": "^1.1.0",
|
||||||
"argon2": "^0.31.1",
|
"argon2": "^0.31.1",
|
||||||
"cloudinary": "^1.41.0",
|
"cloudinary": "^1.41.0",
|
||||||
@@ -36,13 +36,12 @@
|
|||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"mapbox-gl": "^2.15.0",
|
"mapbox-gl": "^2.15.0",
|
||||||
"multer": "^1.4.5-lts.1",
|
"multer": "^1.4.5-lts.1",
|
||||||
"next": "^14.0.3",
|
|
||||||
"next-cloudinary": "^5.10.0",
|
"next-cloudinary": "^5.10.0",
|
||||||
"next-connect": "^1.0.0-next.3",
|
"next-connect": "^1.0.0-next.3",
|
||||||
"passport": "^0.6.0",
|
"next": "^14.0.3",
|
||||||
"passport-local": "^1.0.0",
|
"passport-local": "^1.0.0",
|
||||||
|
"passport": "^0.6.0",
|
||||||
"pino": "^8.14.1",
|
"pino": "^8.14.1",
|
||||||
"react": "^18.2.0",
|
|
||||||
"react-daisyui": "^4.1.2",
|
"react-daisyui": "^4.1.2",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-email": "^1.9.5",
|
"react-email": "^1.9.5",
|
||||||
@@ -52,6 +51,7 @@
|
|||||||
"react-intersection-observer": "^9.5.2",
|
"react-intersection-observer": "^9.5.2",
|
||||||
"react-map-gl": "^7.1.2",
|
"react-map-gl": "^7.1.2",
|
||||||
"react-responsive-carousel": "^3.2.23",
|
"react-responsive-carousel": "^3.2.23",
|
||||||
|
"react": "^18.2.0",
|
||||||
"swr": "^2.2.0",
|
"swr": "^2.2.0",
|
||||||
"theme-change": "^2.5.0",
|
"theme-change": "^2.5.0",
|
||||||
"zod": "^3.21.4"
|
"zod": "^3.21.4"
|
||||||
@@ -59,34 +59,35 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@faker-js/faker": "^8.3.1",
|
"@faker-js/faker": "^8.3.1",
|
||||||
"@types/cookie": "^0.5.1",
|
"@types/cookie": "^0.5.1",
|
||||||
|
"@types/express": "^4.17.21",
|
||||||
"@types/jsonwebtoken": "^9.0.2",
|
"@types/jsonwebtoken": "^9.0.2",
|
||||||
"@types/lodash": "^4.14.195",
|
"@types/lodash": "^4.14.195",
|
||||||
"@types/mapbox__mapbox-sdk": "^0.13.4",
|
"@types/mapbox__mapbox-sdk": "^0.13.4",
|
||||||
"@types/multer": "^1.4.7",
|
"@types/multer": "^1.4.7",
|
||||||
"@types/node": "^20.4.2",
|
"@types/node": "^20.4.2",
|
||||||
"@types/passport-local": "^1.0.35",
|
"@types/passport-local": "^1.0.35",
|
||||||
"@types/react": "^18.2.15",
|
|
||||||
"@types/react-dom": "^18.2.7",
|
"@types/react-dom": "^18.2.7",
|
||||||
|
"@types/react": "^18.2.15",
|
||||||
"@types/sparkpost": "^2.1.5",
|
"@types/sparkpost": "^2.1.5",
|
||||||
"@vercel/fetch": "^7.0.0",
|
"@vercel/fetch": "^7.0.0",
|
||||||
"autoprefixer": "^10.4.14",
|
"autoprefixer": "^10.4.14",
|
||||||
"daisyui": "^3.9.2",
|
"daisyui": "^3.9.2",
|
||||||
"dotenv-cli": "^7.2.1",
|
"dotenv-cli": "^7.2.1",
|
||||||
"eslint": "^8.51.0",
|
|
||||||
"eslint-config-airbnb-base": "15.0.0",
|
"eslint-config-airbnb-base": "15.0.0",
|
||||||
"eslint-config-airbnb-typescript": "17.1.0",
|
"eslint-config-airbnb-typescript": "17.1.0",
|
||||||
"eslint-config-next": "^13.5.4",
|
"eslint-config-next": "^13.5.4",
|
||||||
"eslint-config-prettier": "^9.0.0",
|
"eslint-config-prettier": "^9.0.0",
|
||||||
"eslint-plugin-react": "^7.33.2",
|
"eslint-plugin-react": "^7.33.2",
|
||||||
|
"eslint": "^8.51.0",
|
||||||
"generate-password": "^1.7.1",
|
"generate-password": "^1.7.1",
|
||||||
"onchange": "^7.1.0",
|
"onchange": "^7.1.0",
|
||||||
"postcss": "^8.4.26",
|
"postcss": "^8.4.26",
|
||||||
"prettier": "^3.0.0",
|
|
||||||
"prettier-plugin-jsdoc": "^1.0.2",
|
"prettier-plugin-jsdoc": "^1.0.2",
|
||||||
"prettier-plugin-tailwindcss": "^0.5.7",
|
"prettier-plugin-tailwindcss": "^0.5.7",
|
||||||
"prisma": "^5.6.0",
|
"prettier": "^3.0.0",
|
||||||
"tailwindcss": "^3.3.3",
|
"prisma": "^5.7.0",
|
||||||
"tailwindcss-animate": "^1.0.6",
|
"tailwindcss-animate": "^1.0.6",
|
||||||
|
"tailwindcss": "^3.3.3",
|
||||||
"ts-node": "^10.9.1",
|
"ts-node": "^10.9.1",
|
||||||
"typescript": "^5.3.2"
|
"typescript": "^5.3.2"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -3,4 +3,3 @@ Disallow: /api/
|
|||||||
Disallow: /login/
|
Disallow: /login/
|
||||||
Disallow: /register/
|
Disallow: /register/
|
||||||
Disallow: /users/
|
Disallow: /users/
|
||||||
Disallow: /account/
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import validateEmailRequest from '@/requests/User/validateEmailRequest';
|
import validateEmailRequest from '@/requests/User/validateEmailRequest';
|
||||||
import validateUsernameRequest from '@/requests/validateUsernameRequest';
|
import validateUsernameRequest from '@/requests/validateUsernameRequest';
|
||||||
import { BaseCreateUserSchema } from '@/services/User/schema/CreateUserValidationSchemas';
|
import { BaseCreateUserSchema } from '@/services/users/auth/schema/CreateUserValidationSchemas';
|
||||||
import { Switch } from '@headlessui/react';
|
import { Switch } from '@headlessui/react';
|
||||||
import { zodResolver } from '@hookform/resolvers/zod';
|
import { zodResolver } from '@hookform/resolvers/zod';
|
||||||
import { Dispatch, FC, useContext } from 'react';
|
import { Dispatch, FC, useContext } from 'react';
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { Dispatch, FunctionComponent } from 'react';
|
|||||||
import { SubmitHandler, useForm } from 'react-hook-form';
|
import { SubmitHandler, useForm } from 'react-hook-form';
|
||||||
import { zodResolver } from '@hookform/resolvers/zod';
|
import { zodResolver } from '@hookform/resolvers/zod';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
import { UpdatePasswordSchema } from '@/services/User/schema/CreateUserValidationSchemas';
|
import { UpdatePasswordSchema } from '@/services/users/auth/schema/CreateUserValidationSchemas';
|
||||||
import sendUpdatePasswordRequest from '@/requests/User/sendUpdatePasswordRequest';
|
import sendUpdatePasswordRequest from '@/requests/User/sendUpdatePasswordRequest';
|
||||||
import { AccountPageState, AccountPageAction } from '@/reducers/accountPageReducer';
|
import { AccountPageState, AccountPageAction } from '@/reducers/accountPageReducer';
|
||||||
import toast from 'react-hot-toast';
|
import toast from 'react-hot-toast';
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import FormSegment from '@/components/ui/forms/FormSegment';
|
|||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import FormTextArea from '@/components/ui/forms/FormTextArea';
|
import FormTextArea from '@/components/ui/forms/FormTextArea';
|
||||||
import { FC } from 'react';
|
import { FC } from 'react';
|
||||||
import GetUserSchema from '@/services/User/schema/GetUserSchema';
|
import GetUserSchema from '@/services/users/auth/schema/GetUserSchema';
|
||||||
import type {
|
import type {
|
||||||
UseFormHandleSubmit,
|
UseFormHandleSubmit,
|
||||||
SubmitHandler,
|
SubmitHandler,
|
||||||
@@ -13,7 +13,7 @@ import type {
|
|||||||
UseFormRegister,
|
UseFormRegister,
|
||||||
} from 'react-hook-form';
|
} from 'react-hook-form';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
import UpdateProfileSchema from '../../services/User/schema/UpdateProfileSchema';
|
import UpdateProfileSchema from '@/services/users/auth/schema/UpdateProfileSchema';
|
||||||
|
|
||||||
type UpdateProfileSchemaT = z.infer<typeof UpdateProfileSchema>;
|
type UpdateProfileSchemaT = z.infer<typeof UpdateProfileSchema>;
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { FC } from 'react';
|
import { FC } from 'react';
|
||||||
import { CldImage } from 'next-cloudinary';
|
import { CldImage } from 'next-cloudinary';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
import GetUserSchema from '@/services/User/schema/GetUserSchema';
|
import GetUserSchema from '@/services/users/auth/schema/GetUserSchema';
|
||||||
import { FaUser } from 'react-icons/fa';
|
import { FaUser } from 'react-icons/fa';
|
||||||
|
|
||||||
interface UserAvatarProps {
|
interface UserAvatarProps {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import sendCreateBeerCommentRequest from '@/requests/BeerComment/sendCreateBeerCommentRequest';
|
import sendCreateBeerCommentRequest from '@/requests/BeerComment/sendCreateBeerCommentRequest';
|
||||||
|
|
||||||
import BeerPostQueryResult from '@/services/BeerPost/schema/BeerPostQueryResult';
|
import BeerPostQueryResult from '@/services/posts/beer-post/schema/BeerPostQueryResult';
|
||||||
import { zodResolver } from '@hookform/resolvers/zod';
|
import { zodResolver } from '@hookform/resolvers/zod';
|
||||||
|
|
||||||
import { FunctionComponent } from 'react';
|
import { FunctionComponent } from 'react';
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { FC, useContext } from 'react';
|
|||||||
|
|
||||||
import UserContext from '@/contexts/UserContext';
|
import UserContext from '@/contexts/UserContext';
|
||||||
import { FaRegEdit } from 'react-icons/fa';
|
import { FaRegEdit } from 'react-icons/fa';
|
||||||
import BeerPostQueryResult from '@/services/BeerPost/schema/BeerPostQueryResult';
|
import BeerPostQueryResult from '@/services/posts/beer-post/schema/BeerPostQueryResult';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
import useGetBeerPostLikeCount from '@/hooks/data-fetching/beer-likes/useBeerPostLikeCount';
|
import useGetBeerPostLikeCount from '@/hooks/data-fetching/beer-likes/useBeerPostLikeCount';
|
||||||
import useTimeDistance from '@/hooks/utilities/useTimeDistance';
|
import useTimeDistance from '@/hooks/utilities/useTimeDistance';
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import UserContext from '@/contexts/UserContext';
|
import UserContext from '@/contexts/UserContext';
|
||||||
|
|
||||||
import BeerPostQueryResult from '@/services/BeerPost/schema/BeerPostQueryResult';
|
import BeerPostQueryResult from '@/services/posts/beer-post/schema/BeerPostQueryResult';
|
||||||
|
|
||||||
import { FC, MutableRefObject, useContext, useRef } from 'react';
|
import { FC, MutableRefObject, useContext, useRef } from 'react';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { FC, MutableRefObject, useRef } from 'react';
|
|||||||
import { useInView } from 'react-intersection-observer';
|
import { useInView } from 'react-intersection-observer';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
import useBeerRecommendations from '@/hooks/data-fetching/beer-posts/useBeerRecommendations';
|
import useBeerRecommendations from '@/hooks/data-fetching/beer-posts/useBeerRecommendations';
|
||||||
import BeerPostQueryResult from '@/services/BeerPost/schema/BeerPostQueryResult';
|
import BeerPostQueryResult from '@/services/posts/beer-post/schema/BeerPostQueryResult';
|
||||||
import debounce from 'lodash/debounce';
|
import debounce from 'lodash/debounce';
|
||||||
import BeerRecommendationLoadingComponent from './BeerRecommendationLoadingComponent';
|
import BeerRecommendationLoadingComponent from './BeerRecommendationLoadingComponent';
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import { FC, useContext } from 'react';
|
import { FC, useContext } from 'react';
|
||||||
|
|
||||||
import BeerPostQueryResult from '@/services/BeerPost/schema/BeerPostQueryResult';
|
import BeerPostQueryResult from '@/services/posts/beer-post/schema/BeerPostQueryResult';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
import UserContext from '@/contexts/UserContext';
|
import UserContext from '@/contexts/UserContext';
|
||||||
import useGetBeerPostLikeCount from '@/hooks/data-fetching/beer-likes/useBeerPostLikeCount';
|
import useGetBeerPostLikeCount from '@/hooks/data-fetching/beer-likes/useBeerPostLikeCount';
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { FC, MutableRefObject, useRef } from 'react';
|
|||||||
import { useInView } from 'react-intersection-observer';
|
import { useInView } from 'react-intersection-observer';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
|
||||||
import BeerStyleQueryResult from '@/services/BeerStyles/schema/BeerStyleQueryResult';
|
import BeerStyleQueryResult from '@/services/posts/beer-style-post/schema/BeerStyleQueryResult';
|
||||||
import useBeerPostsByBeerStyle from '@/hooks/data-fetching/beer-posts/useBeerPostsByBeerStyles';
|
import useBeerPostsByBeerStyle from '@/hooks/data-fetching/beer-posts/useBeerPostsByBeerStyles';
|
||||||
import BeerRecommendationLoadingComponent from '../BeerById/BeerRecommendationLoadingComponent';
|
import BeerRecommendationLoadingComponent from '../BeerById/BeerRecommendationLoadingComponent';
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import CreateCommentValidationSchema from '@/services/schema/CommentSchema/Creat
|
|||||||
import toast from 'react-hot-toast';
|
import toast from 'react-hot-toast';
|
||||||
import createErrorToast from '@/util/createErrorToast';
|
import createErrorToast from '@/util/createErrorToast';
|
||||||
|
|
||||||
import BeerStyleQueryResult from '@/services/BeerStyles/schema/BeerStyleQueryResult';
|
import BeerStyleQueryResult from '@/services/posts/beer-style-post/schema/BeerStyleQueryResult';
|
||||||
import useBeerStyleComments from '@/hooks/data-fetching/beer-style-comments/useBeerStyleComments';
|
import useBeerStyleComments from '@/hooks/data-fetching/beer-style-comments/useBeerStyleComments';
|
||||||
import sendCreateBeerStyleCommentRequest from '@/requests/BeerStyleComment/sendCreateBeerStyleCommentRequest';
|
import sendCreateBeerStyleCommentRequest from '@/requests/BeerStyleComment/sendCreateBeerStyleCommentRequest';
|
||||||
import CommentForm from '../ui/CommentForm';
|
import CommentForm from '../ui/CommentForm';
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { z } from 'zod';
|
|||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import CreateCommentValidationSchema from '@/services/schema/CommentSchema/CreateCommentValidationSchema';
|
import CreateCommentValidationSchema from '@/services/schema/CommentSchema/CreateCommentValidationSchema';
|
||||||
|
|
||||||
import BeerStyleQueryResult from '@/services/BeerStyles/schema/BeerStyleQueryResult';
|
import BeerStyleQueryResult from '@/services/posts/beer-style-post/schema/BeerStyleQueryResult';
|
||||||
import useBeerStyleComments from '@/hooks/data-fetching/beer-style-comments/useBeerStyleComments';
|
import useBeerStyleComments from '@/hooks/data-fetching/beer-style-comments/useBeerStyleComments';
|
||||||
import LoadingComponent from '../BeerById/LoadingComponent';
|
import LoadingComponent from '../BeerById/LoadingComponent';
|
||||||
import CommentsComponent from '../ui/CommentsComponent';
|
import CommentsComponent from '../ui/CommentsComponent';
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { FaRegEdit } from 'react-icons/fa';
|
|||||||
|
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
import useTimeDistance from '@/hooks/utilities/useTimeDistance';
|
import useTimeDistance from '@/hooks/utilities/useTimeDistance';
|
||||||
import BeerStyleQueryResult from '@/services/BeerStyles/schema/BeerStyleQueryResult';
|
import BeerStyleQueryResult from '@/services/posts/beer-style-post/schema/BeerStyleQueryResult';
|
||||||
import useBeerStyleLikeCount from '@/hooks/data-fetching/beer-style-likes/useBeerStyleLikeCount';
|
import useBeerStyleLikeCount from '@/hooks/data-fetching/beer-style-likes/useBeerStyleLikeCount';
|
||||||
import BeerStyleLikeButton from './BeerStyleLikeButton';
|
import BeerStyleLikeButton from './BeerStyleLikeButton';
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import BeerStyleQueryResult from '@/services/BeerStyles/schema/BeerStyleQueryResult';
|
import BeerStyleQueryResult from '@/services/posts/beer-style-post/schema/BeerStyleQueryResult';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import { FC } from 'react';
|
import { FC } from 'react';
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import UseBeerPostsByBrewery from '@/hooks/data-fetching/beer-posts/useBeerPostsByBrewery';
|
import UseBeerPostsByBrewery from '@/hooks/data-fetching/beer-posts/useBeerPostsByBrewery';
|
||||||
import BreweryPostQueryResult from '@/services/BreweryPost/schema/BreweryPostQueryResult';
|
import BreweryPostQueryResult from '@/services/posts/brewery-post/schema/BreweryPostQueryResult';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import { FC, MutableRefObject, useContext, useRef } from 'react';
|
import { FC, MutableRefObject, useContext, useRef } from 'react';
|
||||||
import { useInView } from 'react-intersection-observer';
|
import { useInView } from 'react-intersection-observer';
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import useBreweryPostComments from '@/hooks/data-fetching/brewery-comments/useBreweryPostComments';
|
import useBreweryPostComments from '@/hooks/data-fetching/brewery-comments/useBreweryPostComments';
|
||||||
import BreweryPostQueryResult from '@/services/BreweryPost/schema/BreweryPostQueryResult';
|
import BreweryPostQueryResult from '@/services/posts/brewery-post/schema/BreweryPostQueryResult';
|
||||||
import CreateCommentValidationSchema from '@/services/schema/CommentSchema/CreateCommentValidationSchema';
|
import CreateCommentValidationSchema from '@/services/schema/CommentSchema/CreateCommentValidationSchema';
|
||||||
import { zodResolver } from '@hookform/resolvers/zod';
|
import { zodResolver } from '@hookform/resolvers/zod';
|
||||||
import { FC } from 'react';
|
import { FC } from 'react';
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import UserContext from '@/contexts/UserContext';
|
import UserContext from '@/contexts/UserContext';
|
||||||
import BreweryPostQueryResult from '@/services/BreweryPost/schema/BreweryPostQueryResult';
|
import BreweryPostQueryResult from '@/services/posts/brewery-post/schema/BreweryPostQueryResult';
|
||||||
import { FC, MutableRefObject, useContext, useRef } from 'react';
|
import { FC, MutableRefObject, useContext, useRef } from 'react';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
import CreateCommentValidationSchema from '@/services/schema/CommentSchema/CreateCommentValidationSchema';
|
import CreateCommentValidationSchema from '@/services/schema/CommentSchema/CreateCommentValidationSchema';
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import UserContext from '@/contexts/UserContext';
|
import UserContext from '@/contexts/UserContext';
|
||||||
import useGetBreweryPostLikeCount from '@/hooks/data-fetching/brewery-likes/useGetBreweryPostLikeCount';
|
import useGetBreweryPostLikeCount from '@/hooks/data-fetching/brewery-likes/useGetBreweryPostLikeCount';
|
||||||
import useTimeDistance from '@/hooks/utilities/useTimeDistance';
|
import useTimeDistance from '@/hooks/utilities/useTimeDistance';
|
||||||
import BreweryPostQueryResult from '@/services/BreweryPost/schema/BreweryPostQueryResult';
|
import BreweryPostQueryResult from '@/services/posts/brewery-post/schema/BreweryPostQueryResult';
|
||||||
import { format } from 'date-fns';
|
import { format } from 'date-fns';
|
||||||
import { FC, useContext } from 'react';
|
import { FC, useContext } from 'react';
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import UserContext from '@/contexts/UserContext';
|
import UserContext from '@/contexts/UserContext';
|
||||||
import useGetBreweryPostLikeCount from '@/hooks/data-fetching/brewery-likes/useGetBreweryPostLikeCount';
|
import useGetBreweryPostLikeCount from '@/hooks/data-fetching/brewery-likes/useGetBreweryPostLikeCount';
|
||||||
import BreweryPostQueryResult from '@/services/BreweryPost/schema/BreweryPostQueryResult';
|
import BreweryPostQueryResult from '@/services/posts/brewery-post/schema/BreweryPostQueryResult';
|
||||||
import { FC, useContext } from 'react';
|
import { FC, useContext } from 'react';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import sendUploadBreweryImagesRequest from '@/requests/BreweryImage/sendUploadBreweryImageRequest';
|
import sendUploadBreweryImagesRequest from '@/requests/BreweryImage/sendUploadBreweryImageRequest';
|
||||||
import sendCreateBreweryPostRequest from '@/requests/BreweryPost/sendCreateBreweryPostRequest';
|
import sendCreateBreweryPostRequest from '@/requests/BreweryPost/sendCreateBreweryPostRequest';
|
||||||
import CreateBreweryPostSchema from '@/services/BreweryPost/schema/CreateBreweryPostSchema';
|
import CreateBreweryPostSchema from '@/services/posts/brewery-post/schema/CreateBreweryPostSchema';
|
||||||
import UploadImageValidationSchema from '@/services/schema/ImageSchema/UploadImageValidationSchema';
|
import UploadImageValidationSchema from '@/services/schema/ImageSchema/UploadImageValidationSchema';
|
||||||
import createErrorToast from '@/util/createErrorToast';
|
import createErrorToast from '@/util/createErrorToast';
|
||||||
import { Tab } from '@headlessui/react';
|
import { Tab } from '@headlessui/react';
|
||||||
@@ -29,7 +29,7 @@ import FormTextInput from '../ui/forms/FormTextInput';
|
|||||||
import Button from '../ui/forms/Button';
|
import Button from '../ui/forms/Button';
|
||||||
|
|
||||||
const AddressAutofill = dynamic(
|
const AddressAutofill = dynamic(
|
||||||
// @ts-ignore
|
// @ts-expect-error
|
||||||
() => import('@mapbox/search-js-react').then((mod) => mod.AddressAutofill),
|
() => import('@mapbox/search-js-react').then((mod) => mod.AddressAutofill),
|
||||||
{ ssr: false },
|
{ ssr: false },
|
||||||
);
|
);
|
||||||
@@ -108,7 +108,8 @@ const LocationSection: FC<{
|
|||||||
errors: FieldErrors<z.infer<typeof CreateBreweryPostWithImagesSchema>>;
|
errors: FieldErrors<z.infer<typeof CreateBreweryPostWithImagesSchema>>;
|
||||||
isSubmitting: boolean;
|
isSubmitting: boolean;
|
||||||
setValue: UseFormSetValue<z.infer<typeof CreateBreweryPostWithImagesSchema>>;
|
setValue: UseFormSetValue<z.infer<typeof CreateBreweryPostWithImagesSchema>>;
|
||||||
}> = ({ register, errors, isSubmitting, setValue }) => {
|
mapboxAccessToken: string;
|
||||||
|
}> = ({ register, errors, isSubmitting, setValue, mapboxAccessToken }) => {
|
||||||
const onAutoCompleteChange = (address: string) => {
|
const onAutoCompleteChange = (address: string) => {
|
||||||
setValue('address', address);
|
setValue('address', address);
|
||||||
};
|
};
|
||||||
@@ -133,7 +134,7 @@ const LocationSection: FC<{
|
|||||||
</FormInfo>
|
</FormInfo>
|
||||||
<FormSegment>
|
<FormSegment>
|
||||||
<AddressAutofill
|
<AddressAutofill
|
||||||
accessToken={process.env.NEXT_PUBLIC_MAPBOX_ACCESS_TOKEN!}
|
accessToken={mapboxAccessToken}
|
||||||
onRetrieve={onAutoCompleteRetrieve}
|
onRetrieve={onAutoCompleteRetrieve}
|
||||||
onChange={onAutoCompleteChange}
|
onChange={onAutoCompleteChange}
|
||||||
>
|
>
|
||||||
@@ -201,7 +202,9 @@ const LocationSection: FC<{
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const CreateBreweryPostForm: FC = () => {
|
const CreateBreweryPostForm: FC<{
|
||||||
|
mapboxAccessToken: string;
|
||||||
|
}> = ({ mapboxAccessToken }) => {
|
||||||
const {
|
const {
|
||||||
register,
|
register,
|
||||||
handleSubmit,
|
handleSubmit,
|
||||||
@@ -268,6 +271,7 @@ const CreateBreweryPostForm: FC = () => {
|
|||||||
register={register}
|
register={register}
|
||||||
errors={errors}
|
errors={errors}
|
||||||
isSubmitting={isSubmitting}
|
isSubmitting={isSubmitting}
|
||||||
|
mapboxAccessToken={mapboxAccessToken}
|
||||||
/>
|
/>
|
||||||
</Tab.Panel>
|
</Tab.Panel>
|
||||||
</Tab.Panels>
|
</Tab.Panels>
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ import router from 'next/router';
|
|||||||
import { FunctionComponent } from 'react';
|
import { FunctionComponent } from 'react';
|
||||||
import { useForm, SubmitHandler, FieldError } from 'react-hook-form';
|
import { useForm, SubmitHandler, FieldError } from 'react-hook-form';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
import BreweryPostQueryResult from '@/services/BreweryPost/schema/BreweryPostQueryResult';
|
import BreweryPostQueryResult from '@/services/posts/brewery-post/schema/BreweryPostQueryResult';
|
||||||
import CreateBeerPostValidationSchema from '@/services/BeerPost/schema/CreateBeerPostValidationSchema';
|
import CreateBeerPostValidationSchema from '@/services/posts/beer-post/schema/CreateBeerPostValidationSchema';
|
||||||
import sendCreateBeerPostRequest from '@/requests/BeerPost/sendCreateBeerPostRequest';
|
import sendCreateBeerPostRequest from '@/requests/BeerPost/sendCreateBeerPostRequest';
|
||||||
import UploadImageValidationSchema from '@/services/schema/ImageSchema/UploadImageValidationSchema';
|
import UploadImageValidationSchema from '@/services/schema/ImageSchema/UploadImageValidationSchema';
|
||||||
import sendUploadBeerImagesRequest from '@/requests/BeerImage/sendUploadBeerImageRequest';
|
import sendUploadBeerImagesRequest from '@/requests/BeerImage/sendUploadBeerImageRequest';
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { useForm, SubmitHandler } from 'react-hook-form';
|
|||||||
import { zodResolver } from '@hookform/resolvers/zod';
|
import { zodResolver } from '@hookform/resolvers/zod';
|
||||||
|
|
||||||
import deleteBeerPostRequest from '@/requests/BeerPost/deleteBeerPostRequest';
|
import deleteBeerPostRequest from '@/requests/BeerPost/deleteBeerPostRequest';
|
||||||
import EditBeerPostValidationSchema from '@/services/BeerPost/schema/EditBeerPostValidationSchema';
|
import EditBeerPostValidationSchema from '@/services/posts/beer-post/schema/EditBeerPostValidationSchema';
|
||||||
import sendEditBeerPostRequest from '@/requests/BeerPost/sendEditBeerPostRequest';
|
import sendEditBeerPostRequest from '@/requests/BeerPost/sendEditBeerPostRequest';
|
||||||
import createErrorToast from '@/util/createErrorToast';
|
import createErrorToast from '@/util/createErrorToast';
|
||||||
import Button from './ui/forms/Button';
|
import Button from './ui/forms/Button';
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import sendLoginUserRequest from '@/requests/User/sendLoginUserRequest';
|
import sendLoginUserRequest from '@/requests/User/sendLoginUserRequest';
|
||||||
import LoginValidationSchema from '@/services/User/schema/LoginValidationSchema';
|
import LoginValidationSchema from '@/services/users/auth/schema/LoginValidationSchema';
|
||||||
import { zodResolver } from '@hookform/resolvers/zod';
|
import { zodResolver } from '@hookform/resolvers/zod';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import { useContext } from 'react';
|
import { useContext } from 'react';
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import sendRegisterUserRequest from '@/requests/User/sendRegisterUserRequest';
|
import sendRegisterUserRequest from '@/requests/User/sendRegisterUserRequest';
|
||||||
import { CreateUserValidationSchemaWithUsernameAndEmailCheck } from '@/services/User/schema/CreateUserValidationSchemas';
|
import { CreateUserValidationSchemaWithUsernameAndEmailCheck } from '@/services/users/auth/schema/CreateUserValidationSchemas';
|
||||||
import { zodResolver } from '@hookform/resolvers/zod';
|
import { zodResolver } from '@hookform/resolvers/zod';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import { FC } from 'react';
|
import { FC } from 'react';
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import useFollowStatus from '@/hooks/data-fetching/user-follows/useFollowStatus'
|
|||||||
import useGetUsersFollowedByUser from '@/hooks/data-fetching/user-follows/useGetUsersFollowedByUser';
|
import useGetUsersFollowedByUser from '@/hooks/data-fetching/user-follows/useGetUsersFollowedByUser';
|
||||||
import useGetUsersFollowingUser from '@/hooks/data-fetching/user-follows/useGetUsersFollowingUser';
|
import useGetUsersFollowingUser from '@/hooks/data-fetching/user-follows/useGetUsersFollowingUser';
|
||||||
import sendUserFollowRequest from '@/requests/UserFollow/sendUserFollowRequest';
|
import sendUserFollowRequest from '@/requests/UserFollow/sendUserFollowRequest';
|
||||||
import GetUserSchema from '@/services/User/schema/GetUserSchema';
|
import GetUserSchema from '@/services/users/auth/schema/GetUserSchema';
|
||||||
import { FC, useState } from 'react';
|
import { FC, useState } from 'react';
|
||||||
import { FaUserCheck, FaUserPlus } from 'react-icons/fa';
|
import { FaUserCheck, FaUserPlus } from 'react-icons/fa';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import useTimeDistance from '@/hooks/utilities/useTimeDistance';
|
|||||||
import { FC, useContext } from 'react';
|
import { FC, useContext } from 'react';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
import { format } from 'date-fns';
|
import { format } from 'date-fns';
|
||||||
import GetUserSchema from '@/services/User/schema/GetUserSchema';
|
import GetUserSchema from '@/services/users/auth/schema/GetUserSchema';
|
||||||
import useGetUsersFollowedByUser from '@/hooks/data-fetching/user-follows/useGetUsersFollowedByUser';
|
import useGetUsersFollowedByUser from '@/hooks/data-fetching/user-follows/useGetUsersFollowedByUser';
|
||||||
import useGetUsersFollowingUser from '@/hooks/data-fetching/user-follows/useGetUsersFollowingUser';
|
import useGetUsersFollowingUser from '@/hooks/data-fetching/user-follows/useGetUsersFollowingUser';
|
||||||
import UserContext from '@/contexts/UserContext';
|
import UserContext from '@/contexts/UserContext';
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import findUserByUsername from '@/services/User/findUserByUsername';
|
|
||||||
import Local from 'passport-local';
|
import Local from 'passport-local';
|
||||||
|
import { findUserByUsernameService } from '@/services/users/auth';
|
||||||
import ServerError from '../util/ServerError';
|
import ServerError from '../util/ServerError';
|
||||||
import { validatePassword } from './passwordFns';
|
import { validatePassword } from './passwordFns';
|
||||||
|
|
||||||
const localStrat = new Local.Strategy(async (username, password, done) => {
|
const localStrat = new Local.Strategy(async (username, password, done) => {
|
||||||
try {
|
try {
|
||||||
const user = await findUserByUsername(username);
|
const user = await findUserByUsernameService({ username });
|
||||||
if (!user) {
|
if (!user) {
|
||||||
throw new ServerError('Username or password is incorrect.', 401);
|
throw new ServerError('Username or password is incorrect.', 401);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import GetUserSchema from '@/services/User/schema/GetUserSchema';
|
import GetUserSchema from '@/services/users/auth/schema/GetUserSchema';
|
||||||
import { IncomingMessage } from 'http';
|
import { IncomingMessage } from 'http';
|
||||||
import { NextApiRequest } from 'next';
|
import { NextApiRequest } from 'next';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
|||||||
11
src/config/cloudinary/helpers/deleteImage.ts
Normal file
11
src/config/cloudinary/helpers/deleteImage.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import { cloudinary } from '..';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes an image from Cloudinary.
|
||||||
|
*
|
||||||
|
* @param path - The cloudinary path of the image to be deleted.
|
||||||
|
* @returns A promise that resolves when the image is successfully deleted.
|
||||||
|
*/
|
||||||
|
const deleteImage = (path: string) => cloudinary.uploader.destroy(path);
|
||||||
|
|
||||||
|
export default deleteImage;
|
||||||
@@ -4,6 +4,7 @@ import {
|
|||||||
NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME,
|
NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME,
|
||||||
CLOUDINARY_KEY,
|
CLOUDINARY_KEY,
|
||||||
CLOUDINARY_SECRET,
|
CLOUDINARY_SECRET,
|
||||||
|
NODE_ENV,
|
||||||
} from '../env';
|
} from '../env';
|
||||||
import CloudinaryStorage from './CloudinaryStorage';
|
import CloudinaryStorage from './CloudinaryStorage';
|
||||||
|
|
||||||
@@ -14,6 +15,9 @@ cloudinary.config({
|
|||||||
});
|
});
|
||||||
|
|
||||||
/** Cloudinary storage instance. */
|
/** Cloudinary storage instance. */
|
||||||
const storage = new CloudinaryStorage({ cloudinary, params: { folder: 'biergarten' } });
|
const storage = new CloudinaryStorage({
|
||||||
|
cloudinary,
|
||||||
|
params: { folder: NODE_ENV === 'production' ? 'biergarten' : 'biergarten-dev' },
|
||||||
|
});
|
||||||
|
|
||||||
export { cloudinary, storage };
|
export { cloudinary, storage };
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export const verifyConfirmationToken = async (token: string) => {
|
|||||||
const parsed = BasicUserInfoSchema.safeParse(decoded);
|
const parsed = BasicUserInfoSchema.safeParse(decoded);
|
||||||
|
|
||||||
if (!parsed.success) {
|
if (!parsed.success) {
|
||||||
throw new Error('Invalid token');
|
throw new ServerError('Invalid token.', 401);
|
||||||
}
|
}
|
||||||
|
|
||||||
return parsed.data;
|
return parsed.data;
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
import { NextApiResponse } from 'next';
|
import { NextApiResponse } from 'next';
|
||||||
import { NextHandler } from 'next-connect';
|
import { NextHandler } from 'next-connect';
|
||||||
import findUserById from '@/services/User/findUserById';
|
|
||||||
import ServerError from '@/config/util/ServerError';
|
import ServerError from '@/config/util/ServerError';
|
||||||
import { getLoginSession } from '../../auth/session';
|
import { findUserByIdService } from '@/services/users/auth';
|
||||||
import { UserExtendedNextApiRequest } from '../../auth/types';
|
import { getLoginSession } from '@/config/auth/session';
|
||||||
|
import { UserExtendedNextApiRequest } from '@/config/auth/types';
|
||||||
|
|
||||||
/** Get the current user from the session. Adds the user to the request object. */
|
/** Get the current user from the session. Adds the user to the request object. */
|
||||||
const getCurrentUser = async (
|
const getCurrentUser = async (
|
||||||
@@ -12,7 +13,7 @@ const getCurrentUser = async (
|
|||||||
next: NextHandler,
|
next: NextHandler,
|
||||||
) => {
|
) => {
|
||||||
const session = await getLoginSession(req);
|
const session = await getLoginSession(req);
|
||||||
const user = await findUserById(session?.id);
|
const user = await findUserByIdService({ userId: session?.id });
|
||||||
|
|
||||||
if (!user) {
|
if (!user) {
|
||||||
throw new ServerError('User is not logged in.', 401);
|
throw new ServerError('User is not logged in.', 401);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import useUser from '@/hooks/auth/useUser';
|
import useUser from '@/hooks/auth/useUser';
|
||||||
import GetUserSchema from '@/services/User/schema/GetUserSchema';
|
import GetUserSchema from '@/services/users/auth/schema/GetUserSchema';
|
||||||
import { ReactNode, createContext } from 'react';
|
import { ReactNode, createContext } from 'react';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
|||||||
114
src/controllers/comments/beer-comments/index.ts
Normal file
114
src/controllers/comments/beer-comments/index.ts
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
import { NextApiResponse } from 'next';
|
||||||
|
import { NextHandler } from 'next-connect';
|
||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
import ServerError from '@/config/util/ServerError';
|
||||||
|
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
||||||
|
|
||||||
|
import {
|
||||||
|
getBeerPostCommentByIdService,
|
||||||
|
editBeerPostCommentByIdService,
|
||||||
|
createBeerPostCommentService,
|
||||||
|
getAllBeerCommentsService,
|
||||||
|
deleteBeerCommentByIdService,
|
||||||
|
} from '@/services/comments/beer-comment';
|
||||||
|
|
||||||
|
import {
|
||||||
|
CommentRequest,
|
||||||
|
EditAndCreateCommentRequest,
|
||||||
|
GetAllCommentsRequest,
|
||||||
|
} from '../types';
|
||||||
|
|
||||||
|
export const checkIfBeerCommentOwner = async <T extends CommentRequest>(
|
||||||
|
req: T,
|
||||||
|
res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>,
|
||||||
|
next: NextHandler,
|
||||||
|
) => {
|
||||||
|
const { id } = req.query;
|
||||||
|
const user = req.user!;
|
||||||
|
const comment = await getBeerPostCommentByIdService({ beerPostCommentId: id });
|
||||||
|
|
||||||
|
if (!comment) {
|
||||||
|
throw new ServerError('Comment not found', 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (comment.postedBy.id !== user.id) {
|
||||||
|
throw new ServerError('You are not authorized to modify this comment', 403);
|
||||||
|
}
|
||||||
|
|
||||||
|
return next();
|
||||||
|
};
|
||||||
|
|
||||||
|
export const editBeerPostComment = async (
|
||||||
|
req: EditAndCreateCommentRequest,
|
||||||
|
res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>,
|
||||||
|
) => {
|
||||||
|
const { id } = req.query;
|
||||||
|
|
||||||
|
await editBeerPostCommentByIdService({ body: req.body, beerPostCommentId: id });
|
||||||
|
|
||||||
|
res.status(200).json({
|
||||||
|
success: true,
|
||||||
|
message: 'Comment updated successfully',
|
||||||
|
statusCode: 200,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const deleteBeerPostComment = async (
|
||||||
|
req: CommentRequest,
|
||||||
|
res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>,
|
||||||
|
) => {
|
||||||
|
const { id } = req.query;
|
||||||
|
|
||||||
|
await deleteBeerCommentByIdService({ beerPostCommentId: id });
|
||||||
|
|
||||||
|
res.status(200).json({
|
||||||
|
success: true,
|
||||||
|
message: 'Comment deleted successfully',
|
||||||
|
statusCode: 200,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createBeerPostComment = async (
|
||||||
|
req: EditAndCreateCommentRequest,
|
||||||
|
res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>,
|
||||||
|
) => {
|
||||||
|
const beerPostId = req.query.id;
|
||||||
|
|
||||||
|
const newBeerComment = await createBeerPostCommentService({
|
||||||
|
body: req.body,
|
||||||
|
userId: req.user!.id,
|
||||||
|
beerPostId,
|
||||||
|
});
|
||||||
|
|
||||||
|
res.status(201).json({
|
||||||
|
message: 'Beer comment created successfully',
|
||||||
|
statusCode: 201,
|
||||||
|
payload: newBeerComment,
|
||||||
|
success: true,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getAllBeerPostComments = async (
|
||||||
|
req: GetAllCommentsRequest,
|
||||||
|
res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>,
|
||||||
|
) => {
|
||||||
|
const beerPostId = req.query.id;
|
||||||
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||||
|
const { page_size, page_num } = req.query;
|
||||||
|
|
||||||
|
const { comments, count } = await getAllBeerCommentsService({
|
||||||
|
beerPostId,
|
||||||
|
pageNum: parseInt(page_num, 10),
|
||||||
|
pageSize: parseInt(page_size, 10),
|
||||||
|
});
|
||||||
|
|
||||||
|
res.setHeader('X-Total-Count', count);
|
||||||
|
|
||||||
|
res.status(200).json({
|
||||||
|
message: 'Beer comments fetched successfully',
|
||||||
|
statusCode: 200,
|
||||||
|
payload: comments,
|
||||||
|
success: true,
|
||||||
|
});
|
||||||
|
};
|
||||||
118
src/controllers/comments/beer-style-comments/index.ts
Normal file
118
src/controllers/comments/beer-style-comments/index.ts
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
import ServerError from '@/config/util/ServerError';
|
||||||
|
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
||||||
|
import { NextApiResponse } from 'next';
|
||||||
|
import { NextHandler } from 'next-connect';
|
||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
import {
|
||||||
|
updateBeerStyleCommentById,
|
||||||
|
createNewBeerStyleComment,
|
||||||
|
getAllBeerStyleComments,
|
||||||
|
findBeerStyleCommentById,
|
||||||
|
deleteBeerStyleCommentById,
|
||||||
|
} from '@/services/comments/beer-style-comment';
|
||||||
|
|
||||||
|
import {
|
||||||
|
CommentRequest,
|
||||||
|
EditAndCreateCommentRequest,
|
||||||
|
GetAllCommentsRequest,
|
||||||
|
} from '../types';
|
||||||
|
|
||||||
|
export const checkIfBeerStyleCommentOwner = async <
|
||||||
|
CommentRequestType extends CommentRequest,
|
||||||
|
>(
|
||||||
|
req: CommentRequestType,
|
||||||
|
res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>,
|
||||||
|
next: NextHandler,
|
||||||
|
) => {
|
||||||
|
const { id } = req.query;
|
||||||
|
const user = req.user!;
|
||||||
|
|
||||||
|
const beerStyleComment = await findBeerStyleCommentById({ beerStyleCommentId: id });
|
||||||
|
|
||||||
|
if (!beerStyleComment) {
|
||||||
|
throw new ServerError('Beer style comment not found.', 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (beerStyleComment.postedBy.id !== user.id) {
|
||||||
|
throw new ServerError(
|
||||||
|
'You are not authorized to modify this beer style comment.',
|
||||||
|
403,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return next();
|
||||||
|
};
|
||||||
|
|
||||||
|
export const editBeerStyleComment = async (
|
||||||
|
req: EditAndCreateCommentRequest,
|
||||||
|
res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>,
|
||||||
|
) => {
|
||||||
|
await updateBeerStyleCommentById({
|
||||||
|
beerStyleCommentId: req.query.id,
|
||||||
|
body: req.body,
|
||||||
|
});
|
||||||
|
|
||||||
|
return res.status(200).json({
|
||||||
|
success: true,
|
||||||
|
message: 'Comment updated successfully',
|
||||||
|
statusCode: 200,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const deleteBeerStyleComment = async (
|
||||||
|
req: CommentRequest,
|
||||||
|
res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>,
|
||||||
|
) => {
|
||||||
|
const { id } = req.query;
|
||||||
|
|
||||||
|
await deleteBeerStyleCommentById({ beerStyleCommentId: id });
|
||||||
|
|
||||||
|
res.status(200).json({
|
||||||
|
success: true,
|
||||||
|
message: 'Comment deleted successfully',
|
||||||
|
statusCode: 200,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createComment = async (
|
||||||
|
req: EditAndCreateCommentRequest,
|
||||||
|
res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>,
|
||||||
|
) => {
|
||||||
|
const newBeerStyleComment = await createNewBeerStyleComment({
|
||||||
|
body: req.body,
|
||||||
|
beerStyleId: req.query.id,
|
||||||
|
userId: req.user!.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
res.status(201).json({
|
||||||
|
message: 'Beer comment created successfully',
|
||||||
|
statusCode: 201,
|
||||||
|
payload: newBeerStyleComment,
|
||||||
|
success: true,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getAll = async (
|
||||||
|
req: GetAllCommentsRequest,
|
||||||
|
res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>,
|
||||||
|
) => {
|
||||||
|
const beerStyleId = req.query.id;
|
||||||
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||||
|
const { page_size, page_num } = req.query;
|
||||||
|
|
||||||
|
const { comments, count } = await getAllBeerStyleComments({
|
||||||
|
beerStyleId,
|
||||||
|
pageNum: parseInt(page_num, 10),
|
||||||
|
pageSize: parseInt(page_size, 10),
|
||||||
|
});
|
||||||
|
|
||||||
|
res.setHeader('X-Total-Count', count);
|
||||||
|
|
||||||
|
res.status(200).json({
|
||||||
|
message: 'Beer comments fetched successfully',
|
||||||
|
statusCode: 200,
|
||||||
|
payload: comments,
|
||||||
|
success: true,
|
||||||
|
});
|
||||||
|
};
|
||||||
121
src/controllers/comments/brewery-comments/index.ts
Normal file
121
src/controllers/comments/brewery-comments/index.ts
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
import ServerError from '@/config/util/ServerError';
|
||||||
|
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
||||||
|
import { NextApiResponse } from 'next';
|
||||||
|
import { NextHandler } from 'next-connect';
|
||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
import {
|
||||||
|
getBreweryCommentById,
|
||||||
|
createNewBreweryComment,
|
||||||
|
getAllBreweryComments,
|
||||||
|
deleteBreweryCommentByIdService,
|
||||||
|
updateBreweryCommentById,
|
||||||
|
} from '@/services/comments/brewery-comment';
|
||||||
|
|
||||||
|
import {
|
||||||
|
CommentRequest,
|
||||||
|
EditAndCreateCommentRequest,
|
||||||
|
GetAllCommentsRequest,
|
||||||
|
} from '../types';
|
||||||
|
|
||||||
|
export const checkIfBreweryCommentOwner = async <
|
||||||
|
CommentRequestType extends CommentRequest,
|
||||||
|
>(
|
||||||
|
req: CommentRequestType,
|
||||||
|
res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>,
|
||||||
|
next: NextHandler,
|
||||||
|
) => {
|
||||||
|
const { id } = req.query;
|
||||||
|
const user = req.user!;
|
||||||
|
const comment = await getBreweryCommentById({ breweryCommentId: id });
|
||||||
|
|
||||||
|
if (!comment) {
|
||||||
|
throw new ServerError('Comment not found', 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (comment.postedBy.id !== user.id) {
|
||||||
|
throw new ServerError('You are not authorized to modify this comment', 403);
|
||||||
|
}
|
||||||
|
|
||||||
|
return next();
|
||||||
|
};
|
||||||
|
|
||||||
|
export const editBreweryPostComment = async (
|
||||||
|
req: EditAndCreateCommentRequest,
|
||||||
|
res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>,
|
||||||
|
) => {
|
||||||
|
const { id } = req.query;
|
||||||
|
|
||||||
|
const updated = updateBreweryCommentById({
|
||||||
|
breweryCommentId: id,
|
||||||
|
body: req.body,
|
||||||
|
});
|
||||||
|
|
||||||
|
return res.status(200).json({
|
||||||
|
success: true,
|
||||||
|
message: 'Comment updated successfully',
|
||||||
|
statusCode: 200,
|
||||||
|
payload: updated,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const deleteBreweryPostComment = async (
|
||||||
|
req: CommentRequest,
|
||||||
|
res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>,
|
||||||
|
) => {
|
||||||
|
const { id } = req.query;
|
||||||
|
|
||||||
|
await deleteBreweryCommentByIdService({ breweryCommentId: id });
|
||||||
|
|
||||||
|
res.status(200).json({
|
||||||
|
success: true,
|
||||||
|
message: 'Brewery comment deleted successfully',
|
||||||
|
statusCode: 200,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createComment = async (
|
||||||
|
req: EditAndCreateCommentRequest,
|
||||||
|
res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>,
|
||||||
|
) => {
|
||||||
|
const breweryPostId = req.query.id;
|
||||||
|
|
||||||
|
const user = req.user!;
|
||||||
|
|
||||||
|
const newBreweryComment = await createNewBreweryComment({
|
||||||
|
body: req.body,
|
||||||
|
breweryPostId,
|
||||||
|
userId: user.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
res.status(201).json({
|
||||||
|
message: 'Brewery comment created successfully',
|
||||||
|
statusCode: 201,
|
||||||
|
payload: newBreweryComment,
|
||||||
|
success: true,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getAll = async (
|
||||||
|
req: GetAllCommentsRequest,
|
||||||
|
res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>,
|
||||||
|
) => {
|
||||||
|
const breweryPostId = req.query.id;
|
||||||
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||||
|
const { page_size, page_num } = req.query;
|
||||||
|
|
||||||
|
const { comments, count } = await getAllBreweryComments({
|
||||||
|
id: breweryPostId,
|
||||||
|
pageNum: parseInt(page_num, 10),
|
||||||
|
pageSize: parseInt(page_size, 10),
|
||||||
|
});
|
||||||
|
|
||||||
|
res.setHeader('X-Total-Count', count);
|
||||||
|
|
||||||
|
res.status(200).json({
|
||||||
|
message: 'Brewery comments fetched successfully',
|
||||||
|
statusCode: 200,
|
||||||
|
payload: comments,
|
||||||
|
success: true,
|
||||||
|
});
|
||||||
|
};
|
||||||
15
src/controllers/comments/types/index.ts
Normal file
15
src/controllers/comments/types/index.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import { UserExtendedNextApiRequest } from '@/config/auth/types';
|
||||||
|
import CreateCommentValidationSchema from '@/services/schema/CommentSchema/CreateCommentValidationSchema';
|
||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
export interface CommentRequest extends UserExtendedNextApiRequest {
|
||||||
|
query: { id: string };
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface EditAndCreateCommentRequest extends CommentRequest {
|
||||||
|
body: z.infer<typeof CreateCommentValidationSchema>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GetAllCommentsRequest extends UserExtendedNextApiRequest {
|
||||||
|
query: { id: string; page_size: string; page_num: string };
|
||||||
|
}
|
||||||
51
src/controllers/images/beer-images/index.ts
Normal file
51
src/controllers/images/beer-images/index.ts
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
import ServerError from '@/config/util/ServerError';
|
||||||
|
import {
|
||||||
|
addBeerImagesService,
|
||||||
|
deleteBeerImageService,
|
||||||
|
} from '@/services/images/beer-image';
|
||||||
|
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
||||||
|
import { NextApiResponse } from 'next';
|
||||||
|
import { z } from 'zod';
|
||||||
|
import { DeleteImageRequest, UploadImagesRequest } from '../types';
|
||||||
|
|
||||||
|
// eslint-disable-next-line import/prefer-default-export
|
||||||
|
export const processBeerImageData = async (
|
||||||
|
req: UploadImagesRequest,
|
||||||
|
res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>,
|
||||||
|
) => {
|
||||||
|
const { files, user, body } = req;
|
||||||
|
|
||||||
|
if (!files || !files.length) {
|
||||||
|
throw new ServerError('No images uploaded', 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
const beerImages = await addBeerImagesService({
|
||||||
|
beerPostId: req.query.id,
|
||||||
|
userId: user!.id,
|
||||||
|
body,
|
||||||
|
files,
|
||||||
|
});
|
||||||
|
|
||||||
|
res.status(200).json({
|
||||||
|
success: true,
|
||||||
|
message: `Successfully uploaded ${beerImages.length} image${
|
||||||
|
beerImages.length > 1 ? 's' : ''
|
||||||
|
}`,
|
||||||
|
statusCode: 200,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const deleteBeerImageData = async (
|
||||||
|
req: DeleteImageRequest,
|
||||||
|
res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>,
|
||||||
|
) => {
|
||||||
|
const { id } = req.query;
|
||||||
|
|
||||||
|
await deleteBeerImageService({ beerImageId: id });
|
||||||
|
|
||||||
|
res.status(200).json({
|
||||||
|
success: true,
|
||||||
|
message: `Successfully deleted image with id ${id}`,
|
||||||
|
statusCode: 200,
|
||||||
|
});
|
||||||
|
};
|
||||||
34
src/controllers/images/brewery-images/index.ts
Normal file
34
src/controllers/images/brewery-images/index.ts
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import ServerError from '@/config/util/ServerError';
|
||||||
|
|
||||||
|
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
||||||
|
import { NextApiResponse } from 'next';
|
||||||
|
import { z } from 'zod';
|
||||||
|
import { addBreweryImagesService } from '@/services/images/brewery-image';
|
||||||
|
import { UploadImagesRequest } from '../types';
|
||||||
|
|
||||||
|
// eslint-disable-next-line import/prefer-default-export
|
||||||
|
export const processBreweryImageData = async (
|
||||||
|
req: UploadImagesRequest,
|
||||||
|
res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>,
|
||||||
|
) => {
|
||||||
|
const { files, user, body } = req;
|
||||||
|
|
||||||
|
if (!files || !files.length) {
|
||||||
|
throw new ServerError('No images uploaded', 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
const breweryImages = await addBreweryImagesService({
|
||||||
|
breweryPostId: req.query.id,
|
||||||
|
userId: user!.id,
|
||||||
|
body,
|
||||||
|
files,
|
||||||
|
});
|
||||||
|
|
||||||
|
res.status(200).json({
|
||||||
|
success: true,
|
||||||
|
message: `Successfully uploaded ${breweryImages.length} image${
|
||||||
|
breweryImages.length > 1 ? 's' : ''
|
||||||
|
}`,
|
||||||
|
statusCode: 200,
|
||||||
|
});
|
||||||
|
};
|
||||||
13
src/controllers/images/types/index.ts
Normal file
13
src/controllers/images/types/index.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import { UserExtendedNextApiRequest } from '@/config/auth/types';
|
||||||
|
import ImageMetadataValidationSchema from '@/services/schema/ImageSchema/ImageMetadataValidationSchema';
|
||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
export interface UploadImagesRequest extends UserExtendedNextApiRequest {
|
||||||
|
files?: Express.Multer.File[];
|
||||||
|
query: { id: string };
|
||||||
|
body: z.infer<typeof ImageMetadataValidationSchema>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DeleteImageRequest extends UserExtendedNextApiRequest {
|
||||||
|
query: { id: string };
|
||||||
|
}
|
||||||
89
src/controllers/likes/beer-posts-likes/index.ts
Normal file
89
src/controllers/likes/beer-posts-likes/index.ts
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
import { UserExtendedNextApiRequest } from '@/config/auth/types';
|
||||||
|
import ServerError from '@/config/util/ServerError';
|
||||||
|
|
||||||
|
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
||||||
|
import { NextApiResponse, NextApiRequest } from 'next';
|
||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
import { getBeerPostById } from '@/services/posts/beer-post';
|
||||||
|
import {
|
||||||
|
findBeerPostLikeByIdService,
|
||||||
|
createBeerPostLikeService,
|
||||||
|
removeBeerPostLikeService,
|
||||||
|
getBeerPostLikeCountService,
|
||||||
|
} from '@/services/likes/beer-post-like';
|
||||||
|
import { LikeRequest } from '../types';
|
||||||
|
|
||||||
|
export const sendBeerPostLikeRequest = async (
|
||||||
|
req: LikeRequest,
|
||||||
|
res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>,
|
||||||
|
) => {
|
||||||
|
const user = req.user!;
|
||||||
|
const id = req.query.id as string;
|
||||||
|
|
||||||
|
const beer = await getBeerPostById({ beerPostId: id });
|
||||||
|
if (!beer) {
|
||||||
|
throw new ServerError('Could not find a beer post with that id.', 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
const liked = await findBeerPostLikeByIdService({
|
||||||
|
beerPostId: beer.id,
|
||||||
|
likedById: user.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (liked) {
|
||||||
|
await removeBeerPostLikeService({ beerPostLikeId: liked.id });
|
||||||
|
res.status(200).json({
|
||||||
|
success: true,
|
||||||
|
message: 'Successfully unliked beer post.',
|
||||||
|
statusCode: 200,
|
||||||
|
payload: { liked: false },
|
||||||
|
});
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await createBeerPostLikeService({ beerPostId: beer.id, likedById: user.id });
|
||||||
|
res.status(200).json({
|
||||||
|
success: true,
|
||||||
|
message: 'Successfully liked beer post.',
|
||||||
|
statusCode: 200,
|
||||||
|
payload: { liked: true },
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getBeerPostLikeCount = async (
|
||||||
|
req: NextApiRequest,
|
||||||
|
res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>,
|
||||||
|
) => {
|
||||||
|
const id = req.query.id as string;
|
||||||
|
|
||||||
|
const likeCount = await getBeerPostLikeCountService({ beerPostId: id });
|
||||||
|
|
||||||
|
res.status(200).json({
|
||||||
|
success: true,
|
||||||
|
message: 'Successfully retrieved like count.',
|
||||||
|
statusCode: 200,
|
||||||
|
payload: { likeCount },
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const checkIfBeerPostIsLiked = async (
|
||||||
|
req: UserExtendedNextApiRequest,
|
||||||
|
res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>,
|
||||||
|
) => {
|
||||||
|
const user = req.user!;
|
||||||
|
const beerPostId = req.query.id as string;
|
||||||
|
|
||||||
|
const alreadyLiked = await findBeerPostLikeByIdService({
|
||||||
|
beerPostId,
|
||||||
|
likedById: user.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
res.status(200).json({
|
||||||
|
success: true,
|
||||||
|
message: alreadyLiked ? 'Beer post is liked.' : 'Beer post is not liked.',
|
||||||
|
statusCode: 200,
|
||||||
|
payload: { isLiked: !!alreadyLiked },
|
||||||
|
});
|
||||||
|
};
|
||||||
82
src/controllers/likes/beer-style-likes/index.ts
Normal file
82
src/controllers/likes/beer-style-likes/index.ts
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
import ServerError from '@/config/util/ServerError';
|
||||||
|
|
||||||
|
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
||||||
|
import { NextApiResponse, NextApiRequest } from 'next';
|
||||||
|
import { z } from 'zod';
|
||||||
|
import { UserExtendedNextApiRequest } from '@/config/auth/types';
|
||||||
|
import {
|
||||||
|
createBeerStyleLikeService,
|
||||||
|
findBeerStyleLikeService,
|
||||||
|
getBeerStyleLikeCountService,
|
||||||
|
removeBeerStyleLikeService,
|
||||||
|
} from '@/services/likes/beer-style-like';
|
||||||
|
import { getBeerStyleByIdService } from '@/services/posts/beer-style-post';
|
||||||
|
import { LikeRequest } from '../types';
|
||||||
|
|
||||||
|
export const sendBeerStyleLikeRequest = async (
|
||||||
|
req: LikeRequest,
|
||||||
|
res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>,
|
||||||
|
) => {
|
||||||
|
const user = req.user!;
|
||||||
|
const { id } = req.query;
|
||||||
|
|
||||||
|
const beerStyle = await getBeerStyleByIdService({ beerStyleId: id });
|
||||||
|
if (!beerStyle) {
|
||||||
|
throw new ServerError('Could not find a beer style with that id.', 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
const beerStyleLike = await findBeerStyleLikeService({
|
||||||
|
beerStyleId: beerStyle.id,
|
||||||
|
likedById: user.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (beerStyleLike) {
|
||||||
|
await removeBeerStyleLikeService({ beerStyleLikeId: beerStyleLike.id });
|
||||||
|
res.status(200).json({
|
||||||
|
message: 'Successfully unliked beer style.',
|
||||||
|
success: true,
|
||||||
|
statusCode: 200,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
await createBeerStyleLikeService({ beerStyleId: beerStyle.id, likedById: user.id });
|
||||||
|
res.status(200).json({
|
||||||
|
message: 'Successfully liked beer style.',
|
||||||
|
success: true,
|
||||||
|
statusCode: 200,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getBeerStyleLikeCountRequest = async (
|
||||||
|
req: NextApiRequest,
|
||||||
|
res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>,
|
||||||
|
) => {
|
||||||
|
const id = req.query.id as string;
|
||||||
|
const likeCount = await getBeerStyleLikeCountService({ beerStyleId: id });
|
||||||
|
|
||||||
|
res.status(200).json({
|
||||||
|
success: true,
|
||||||
|
message: 'Successfully retrieved like count.',
|
||||||
|
statusCode: 200,
|
||||||
|
payload: { likeCount },
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const checkIfBeerStyleIsLiked = async (
|
||||||
|
req: UserExtendedNextApiRequest,
|
||||||
|
res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>,
|
||||||
|
) => {
|
||||||
|
const user = req.user!;
|
||||||
|
const beerStyleId = req.query.id as string;
|
||||||
|
|
||||||
|
const alreadyLiked = await findBeerStyleLikeService({
|
||||||
|
beerStyleId,
|
||||||
|
likedById: user.id,
|
||||||
|
});
|
||||||
|
res.status(200).json({
|
||||||
|
success: true,
|
||||||
|
message: alreadyLiked ? 'Beer style is liked.' : 'Beer style is not liked.',
|
||||||
|
statusCode: 200,
|
||||||
|
payload: { isLiked: !!alreadyLiked },
|
||||||
|
});
|
||||||
|
};
|
||||||
96
src/controllers/likes/brewery-post-likes/index.ts
Normal file
96
src/controllers/likes/brewery-post-likes/index.ts
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
import { UserExtendedNextApiRequest } from '@/config/auth/types';
|
||||||
|
import ServerError from '@/config/util/ServerError';
|
||||||
|
|
||||||
|
import {
|
||||||
|
createBreweryPostLikeService,
|
||||||
|
findBreweryPostLikeService,
|
||||||
|
getBreweryPostLikeCountService,
|
||||||
|
removeBreweryPostLikeService,
|
||||||
|
} from '@/services/likes/brewery-post-like';
|
||||||
|
import { getBreweryPostByIdService } from '@/services/posts/brewery-post';
|
||||||
|
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
||||||
|
import { NextApiResponse, NextApiRequest } from 'next';
|
||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
export const sendBreweryPostLikeRequest = async (
|
||||||
|
req: UserExtendedNextApiRequest,
|
||||||
|
res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>,
|
||||||
|
) => {
|
||||||
|
const id = req.query.id! as string;
|
||||||
|
const user = req.user!;
|
||||||
|
|
||||||
|
const breweryPost = await getBreweryPostByIdService({ breweryPostId: id });
|
||||||
|
|
||||||
|
if (!breweryPost) {
|
||||||
|
throw new ServerError('Could not find a brewery post with that id', 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
const like = await findBreweryPostLikeService({
|
||||||
|
breweryPostId: breweryPost.id,
|
||||||
|
likedById: user.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (like) {
|
||||||
|
await removeBreweryPostLikeService({ breweryPostLikeId: like.id });
|
||||||
|
res.status(200).json({
|
||||||
|
success: true,
|
||||||
|
message: 'Successfully removed like from brewery post.',
|
||||||
|
statusCode: 200,
|
||||||
|
});
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await createBreweryPostLikeService({
|
||||||
|
breweryPostId: breweryPost.id,
|
||||||
|
likedById: user.id,
|
||||||
|
});
|
||||||
|
res.status(200).json({
|
||||||
|
success: true,
|
||||||
|
message: 'Successfully liked brewery post.',
|
||||||
|
statusCode: 200,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getBreweryPostLikeCount = async (
|
||||||
|
req: NextApiRequest,
|
||||||
|
res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>,
|
||||||
|
) => {
|
||||||
|
const id = req.query.id! as string;
|
||||||
|
|
||||||
|
const breweryPost = await getBreweryPostByIdService({ breweryPostId: id });
|
||||||
|
if (!breweryPost) {
|
||||||
|
throw new ServerError('Could not find a brewery post with that id', 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
const likeCount = await getBreweryPostLikeCountService({
|
||||||
|
breweryPostId: breweryPost.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
res.status(200).json({
|
||||||
|
success: true,
|
||||||
|
message: 'Successfully retrieved like count',
|
||||||
|
statusCode: 200,
|
||||||
|
payload: { likeCount },
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getBreweryPostLikeStatus = async (
|
||||||
|
req: UserExtendedNextApiRequest,
|
||||||
|
res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>,
|
||||||
|
) => {
|
||||||
|
const user = req.user!;
|
||||||
|
const id = req.query.id as string;
|
||||||
|
|
||||||
|
const liked = await findBreweryPostLikeService({
|
||||||
|
breweryPostId: id,
|
||||||
|
likedById: user.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
res.status(200).json({
|
||||||
|
success: true,
|
||||||
|
message: liked ? 'Brewery post is liked.' : 'Brewery post is not liked.',
|
||||||
|
statusCode: 200,
|
||||||
|
payload: { isLiked: !!liked },
|
||||||
|
});
|
||||||
|
};
|
||||||
5
src/controllers/likes/types/index.ts
Normal file
5
src/controllers/likes/types/index.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import { UserExtendedNextApiRequest } from '@/config/auth/types';
|
||||||
|
|
||||||
|
export interface LikeRequest extends UserExtendedNextApiRequest {
|
||||||
|
query: { id: string };
|
||||||
|
}
|
||||||
170
src/controllers/posts/beer-posts/index.ts
Normal file
170
src/controllers/posts/beer-posts/index.ts
Normal file
@@ -0,0 +1,170 @@
|
|||||||
|
import ServerError from '@/config/util/ServerError';
|
||||||
|
|
||||||
|
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
||||||
|
import { NextApiResponse } from 'next';
|
||||||
|
import { NextHandler } from 'next-connect';
|
||||||
|
import { z } from 'zod';
|
||||||
|
import { GetAllPostsByConnectedPostId } from '@/controllers/posts/types';
|
||||||
|
import {
|
||||||
|
BeerPostRequest,
|
||||||
|
CreateBeerPostRequest,
|
||||||
|
EditBeerPostRequest,
|
||||||
|
GetAllBeerPostsRequest,
|
||||||
|
GetBeerRecommendationsRequest,
|
||||||
|
} from '@/controllers/posts/beer-posts/types';
|
||||||
|
import {
|
||||||
|
getBeerPostById,
|
||||||
|
editBeerPostByIdService,
|
||||||
|
deleteBeerPostByIdService,
|
||||||
|
getBeerRecommendationsService,
|
||||||
|
getAllBeerPostsService,
|
||||||
|
createNewBeerPost,
|
||||||
|
getBeerPostsByPostedByIdService,
|
||||||
|
} from '@/services/posts/beer-post';
|
||||||
|
|
||||||
|
export const checkIfBeerPostOwner = async <BeerPostRequestType extends BeerPostRequest>(
|
||||||
|
req: BeerPostRequestType,
|
||||||
|
res: NextApiResponse,
|
||||||
|
next: NextHandler,
|
||||||
|
) => {
|
||||||
|
const { user, query } = req;
|
||||||
|
const { id } = query;
|
||||||
|
|
||||||
|
const beerPost = await getBeerPostById({ beerPostId: id });
|
||||||
|
|
||||||
|
if (!beerPost) {
|
||||||
|
throw new ServerError('Beer post not found', 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (beerPost.postedBy.id !== user!.id) {
|
||||||
|
throw new ServerError('You cannot edit that beer post.', 403);
|
||||||
|
}
|
||||||
|
|
||||||
|
return next();
|
||||||
|
};
|
||||||
|
|
||||||
|
export const editBeerPost = async (
|
||||||
|
req: EditBeerPostRequest,
|
||||||
|
res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>,
|
||||||
|
) => {
|
||||||
|
await editBeerPostByIdService({ beerPostId: req.query.id, body: req.body });
|
||||||
|
|
||||||
|
res.status(200).json({
|
||||||
|
message: 'Beer post updated successfully',
|
||||||
|
success: true,
|
||||||
|
statusCode: 200,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const deleteBeerPost = async (req: BeerPostRequest, res: NextApiResponse) => {
|
||||||
|
const { id } = req.query;
|
||||||
|
|
||||||
|
const deleted = await deleteBeerPostByIdService({ beerPostId: id });
|
||||||
|
if (!deleted) {
|
||||||
|
throw new ServerError('Beer post not found', 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
res.status(200).json({
|
||||||
|
message: 'Beer post deleted successfully',
|
||||||
|
success: true,
|
||||||
|
statusCode: 200,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getBeerPostRecommendations = async (
|
||||||
|
req: GetBeerRecommendationsRequest,
|
||||||
|
res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>,
|
||||||
|
) => {
|
||||||
|
const { id } = req.query;
|
||||||
|
|
||||||
|
const beerPost = await getBeerPostById({ beerPostId: id });
|
||||||
|
|
||||||
|
if (!beerPost) {
|
||||||
|
throw new ServerError('Beer post not found', 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
const pageNum = parseInt(req.query.page_num as string, 10);
|
||||||
|
const pageSize = parseInt(req.query.page_size as string, 10);
|
||||||
|
|
||||||
|
const { beerRecommendations, count } = await getBeerRecommendationsService({
|
||||||
|
beerPost,
|
||||||
|
pageNum,
|
||||||
|
pageSize,
|
||||||
|
});
|
||||||
|
|
||||||
|
res.setHeader('X-Total-Count', count);
|
||||||
|
res.status(200).json({
|
||||||
|
success: true,
|
||||||
|
message: 'Recommendations fetched successfully',
|
||||||
|
statusCode: 200,
|
||||||
|
payload: beerRecommendations,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getBeerPosts = async (
|
||||||
|
req: GetAllBeerPostsRequest,
|
||||||
|
res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>,
|
||||||
|
) => {
|
||||||
|
const pageNum = parseInt(req.query.page_num, 10);
|
||||||
|
const pageSize = parseInt(req.query.page_size, 10);
|
||||||
|
|
||||||
|
const { beerPosts, count } = await getAllBeerPostsService({ pageNum, pageSize });
|
||||||
|
|
||||||
|
res.setHeader('X-Total-Count', count);
|
||||||
|
|
||||||
|
res.status(200).json({
|
||||||
|
message: 'Beer posts retrieved successfully',
|
||||||
|
statusCode: 200,
|
||||||
|
payload: beerPosts,
|
||||||
|
success: true,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createBeerPost = async (
|
||||||
|
req: CreateBeerPostRequest,
|
||||||
|
res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>,
|
||||||
|
) => {
|
||||||
|
const { name, description, styleId, abv, ibu, breweryId } = req.body;
|
||||||
|
|
||||||
|
const newBeerPost = await createNewBeerPost({
|
||||||
|
name,
|
||||||
|
description,
|
||||||
|
abv,
|
||||||
|
ibu,
|
||||||
|
styleId,
|
||||||
|
breweryId,
|
||||||
|
userId: req.user!.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
res.status(201).json({
|
||||||
|
message: 'Beer post created successfully',
|
||||||
|
statusCode: 201,
|
||||||
|
payload: newBeerPost,
|
||||||
|
success: true,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getBeerPostsByUserId = async (
|
||||||
|
req: GetAllPostsByConnectedPostId,
|
||||||
|
res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>,
|
||||||
|
) => {
|
||||||
|
const pageNum = parseInt(req.query.page_num, 10);
|
||||||
|
const pageSize = parseInt(req.query.page_size, 10);
|
||||||
|
|
||||||
|
const { id } = req.query;
|
||||||
|
|
||||||
|
const { beerPosts, count } = await getBeerPostsByPostedByIdService({
|
||||||
|
pageNum,
|
||||||
|
pageSize,
|
||||||
|
postedById: id,
|
||||||
|
});
|
||||||
|
|
||||||
|
res.setHeader('X-Total-Count', count);
|
||||||
|
|
||||||
|
res.status(200).json({
|
||||||
|
message: `Beer posts by user ${id} fetched successfully`,
|
||||||
|
statusCode: 200,
|
||||||
|
payload: beerPosts,
|
||||||
|
success: true,
|
||||||
|
});
|
||||||
|
};
|
||||||
25
src/controllers/posts/beer-posts/types/index.ts
Normal file
25
src/controllers/posts/beer-posts/types/index.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import { UserExtendedNextApiRequest } from '@/config/auth/types';
|
||||||
|
import CreateBeerPostValidationSchema from '@/services/posts/beer-post/schema/CreateBeerPostValidationSchema';
|
||||||
|
import EditBeerPostValidationSchema from '@/services/posts/beer-post/schema/EditBeerPostValidationSchema';
|
||||||
|
import { NextApiRequest } from 'next';
|
||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
export interface BeerPostRequest extends UserExtendedNextApiRequest {
|
||||||
|
query: { id: string };
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface EditBeerPostRequest extends BeerPostRequest {
|
||||||
|
body: z.infer<typeof EditBeerPostValidationSchema>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GetAllBeerPostsRequest extends NextApiRequest {
|
||||||
|
query: { page_num: string; page_size: string };
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GetBeerRecommendationsRequest extends BeerPostRequest {
|
||||||
|
query: { id: string; page_num: string; page_size: string };
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CreateBeerPostRequest extends UserExtendedNextApiRequest {
|
||||||
|
body: z.infer<typeof CreateBeerPostValidationSchema>;
|
||||||
|
}
|
||||||
105
src/controllers/posts/beer-styles/index.ts
Normal file
105
src/controllers/posts/beer-styles/index.ts
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
import { NextApiResponse } from 'next';
|
||||||
|
|
||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
||||||
|
|
||||||
|
import { getBeerPostsByBeerStyleIdService } from '@/services/posts/beer-post';
|
||||||
|
import {
|
||||||
|
createBeerStyleService,
|
||||||
|
getAllBeerStylesService,
|
||||||
|
getBeerStyleByIdService,
|
||||||
|
} from '@/services/posts/beer-style-post';
|
||||||
|
|
||||||
|
import { CreateBeerStyleRequest, GetBeerStyleByIdRequest } from './types';
|
||||||
|
import { GetAllPostsByConnectedPostId, GetAllPostsRequest } from '../types';
|
||||||
|
|
||||||
|
export const getBeerStyle = async (
|
||||||
|
req: GetBeerStyleByIdRequest,
|
||||||
|
res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>,
|
||||||
|
) => {
|
||||||
|
const { id } = req.query;
|
||||||
|
|
||||||
|
const beerStyle = await getBeerStyleByIdService({
|
||||||
|
beerStyleId: id,
|
||||||
|
});
|
||||||
|
|
||||||
|
res.status(200).json({
|
||||||
|
message: 'Beer style retrieved successfully.',
|
||||||
|
statusCode: 200,
|
||||||
|
payload: beerStyle,
|
||||||
|
success: true,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getAllBeersByBeerStyle = async (
|
||||||
|
req: GetAllPostsByConnectedPostId,
|
||||||
|
res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>,
|
||||||
|
) => {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||||
|
const { page_size, page_num, id } = req.query;
|
||||||
|
|
||||||
|
const { beerPosts, count } = await getBeerPostsByBeerStyleIdService({
|
||||||
|
pageNum: parseInt(page_num, 10),
|
||||||
|
pageSize: parseInt(page_size, 10),
|
||||||
|
styleId: id,
|
||||||
|
});
|
||||||
|
|
||||||
|
res.setHeader('X-Total-Count', count);
|
||||||
|
|
||||||
|
res.status(200).json({
|
||||||
|
message: `Beers with style id ${id} retrieved successfully.`,
|
||||||
|
statusCode: 200,
|
||||||
|
payload: beerPosts,
|
||||||
|
success: true,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getBeerStyles = async (
|
||||||
|
req: GetAllPostsRequest,
|
||||||
|
res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>,
|
||||||
|
) => {
|
||||||
|
const pageNum = parseInt(req.query.page_num, 10);
|
||||||
|
const pageSize = parseInt(req.query.page_size, 10);
|
||||||
|
|
||||||
|
const { beerStyles, beerStyleCount } = await getAllBeerStylesService({
|
||||||
|
pageNum,
|
||||||
|
pageSize,
|
||||||
|
});
|
||||||
|
|
||||||
|
res.setHeader('X-Total-Count', beerStyleCount);
|
||||||
|
|
||||||
|
res.status(200).json({
|
||||||
|
message: 'Beer styles retrieved successfully.',
|
||||||
|
statusCode: 200,
|
||||||
|
payload: beerStyles,
|
||||||
|
success: true,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createBeerStyle = async (
|
||||||
|
req: CreateBeerStyleRequest,
|
||||||
|
res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>,
|
||||||
|
) => {
|
||||||
|
const { abvRange, description, glasswareId, ibuRange, name } = req.body;
|
||||||
|
|
||||||
|
const user = req.user!;
|
||||||
|
|
||||||
|
const beerStyle = await createBeerStyleService({
|
||||||
|
glasswareId,
|
||||||
|
postedById: user.id,
|
||||||
|
body: {
|
||||||
|
abvRange,
|
||||||
|
description,
|
||||||
|
ibuRange,
|
||||||
|
name,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
message: 'Beer style created successfully.',
|
||||||
|
statusCode: 200,
|
||||||
|
payload: beerStyle,
|
||||||
|
success: true,
|
||||||
|
});
|
||||||
|
};
|
||||||
13
src/controllers/posts/beer-styles/types/index.ts
Normal file
13
src/controllers/posts/beer-styles/types/index.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import { NextApiRequest } from 'next';
|
||||||
|
|
||||||
|
import { UserExtendedNextApiRequest } from '@/config/auth/types';
|
||||||
|
import { z } from 'zod';
|
||||||
|
import CreateBeerStyleValidationSchema from '@/services/posts/beer-style-post/schema/CreateBeerStyleValidationSchema';
|
||||||
|
|
||||||
|
export interface GetBeerStyleByIdRequest extends NextApiRequest {
|
||||||
|
query: { id: string };
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CreateBeerStyleRequest extends UserExtendedNextApiRequest {
|
||||||
|
body: z.infer<typeof CreateBeerStyleValidationSchema>;
|
||||||
|
}
|
||||||
218
src/controllers/posts/breweries/index.ts
Normal file
218
src/controllers/posts/breweries/index.ts
Normal file
@@ -0,0 +1,218 @@
|
|||||||
|
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
||||||
|
import { NextApiResponse } from 'next';
|
||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
import geocode from '@/config/mapbox/geocoder';
|
||||||
|
import ServerError from '@/config/util/ServerError';
|
||||||
|
|
||||||
|
import {
|
||||||
|
getAllBreweryPostsByPostedByIdService,
|
||||||
|
getAllBreweryPostsService,
|
||||||
|
createNewBreweryPostService,
|
||||||
|
createBreweryPostLocationService,
|
||||||
|
getMapBreweryPostsService,
|
||||||
|
getBreweryPostByIdService,
|
||||||
|
updateBreweryPostService,
|
||||||
|
deleteBreweryPostService,
|
||||||
|
} from '@/services/posts/brewery-post';
|
||||||
|
import { getBeerPostsByBreweryIdService } from '@/services/posts/beer-post';
|
||||||
|
import { NextHandler } from 'next-connect';
|
||||||
|
|
||||||
|
import {
|
||||||
|
BreweryPostRequest,
|
||||||
|
CreateBreweryPostRequest,
|
||||||
|
EditBreweryPostRequest,
|
||||||
|
GetBreweryPostsRequest,
|
||||||
|
} from './types';
|
||||||
|
import { GetAllPostsByConnectedPostId } from '../types';
|
||||||
|
|
||||||
|
export const getBreweryPostsByUserId = async (
|
||||||
|
req: GetAllPostsByConnectedPostId,
|
||||||
|
res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>,
|
||||||
|
) => {
|
||||||
|
const pageNum = parseInt(req.query.page_num, 10);
|
||||||
|
const pageSize = parseInt(req.query.page_size, 10);
|
||||||
|
|
||||||
|
const { id } = req.query;
|
||||||
|
|
||||||
|
const { breweryPosts, count } = await getAllBreweryPostsByPostedByIdService({
|
||||||
|
pageNum,
|
||||||
|
pageSize,
|
||||||
|
postedById: id,
|
||||||
|
});
|
||||||
|
|
||||||
|
res.setHeader('X-Total-Count', count);
|
||||||
|
|
||||||
|
res.status(200).json({
|
||||||
|
message: `Brewery posts by user ${id} fetched successfully`,
|
||||||
|
statusCode: 200,
|
||||||
|
payload: breweryPosts,
|
||||||
|
success: true,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getBreweryPosts = async (
|
||||||
|
req: GetBreweryPostsRequest,
|
||||||
|
res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>,
|
||||||
|
) => {
|
||||||
|
const pageNum = parseInt(req.query.page_num, 10);
|
||||||
|
const pageSize = parseInt(req.query.page_size, 10);
|
||||||
|
|
||||||
|
const { breweryPosts, count } = await getAllBreweryPostsService({ pageNum, pageSize });
|
||||||
|
|
||||||
|
res.setHeader('X-Total-Count', count);
|
||||||
|
res.status(200).json({
|
||||||
|
message: 'Brewery posts retrieved successfully',
|
||||||
|
statusCode: 200,
|
||||||
|
payload: breweryPosts,
|
||||||
|
success: true,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createBreweryPost = async (
|
||||||
|
req: CreateBreweryPostRequest,
|
||||||
|
res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>,
|
||||||
|
) => {
|
||||||
|
const { name, description, dateEstablished, address, city, country, region } = req.body;
|
||||||
|
const userId = req.user!.id;
|
||||||
|
|
||||||
|
const fullAddress = `${address}, ${city}, ${region}, ${country}`;
|
||||||
|
|
||||||
|
const geocoded = await geocode(fullAddress);
|
||||||
|
|
||||||
|
if (!geocoded) {
|
||||||
|
throw new ServerError('Address is not valid', 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
const [latitude, longitude] = geocoded.center;
|
||||||
|
|
||||||
|
const location = await createBreweryPostLocationService({
|
||||||
|
body: {
|
||||||
|
address,
|
||||||
|
city,
|
||||||
|
country,
|
||||||
|
stateOrProvince: region,
|
||||||
|
coordinates: [latitude, longitude],
|
||||||
|
},
|
||||||
|
postedById: userId,
|
||||||
|
});
|
||||||
|
|
||||||
|
const newBreweryPost = await createNewBreweryPostService({
|
||||||
|
name,
|
||||||
|
description,
|
||||||
|
locationId: location.id,
|
||||||
|
dateEstablished,
|
||||||
|
userId,
|
||||||
|
});
|
||||||
|
|
||||||
|
res.status(201).json({
|
||||||
|
message: 'Brewery post created successfully',
|
||||||
|
statusCode: 201,
|
||||||
|
payload: newBreweryPost,
|
||||||
|
success: true,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getMapBreweryPosts = async (
|
||||||
|
req: GetBreweryPostsRequest,
|
||||||
|
res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>,
|
||||||
|
) => {
|
||||||
|
const pageNum = parseInt(req.query.page_num, 10);
|
||||||
|
const pageSize = parseInt(req.query.page_size, 10);
|
||||||
|
|
||||||
|
const { breweryPosts, count } = await getMapBreweryPostsService({
|
||||||
|
pageNum,
|
||||||
|
pageSize,
|
||||||
|
});
|
||||||
|
|
||||||
|
res.setHeader('X-Total-Count', count);
|
||||||
|
|
||||||
|
res.status(200).json({
|
||||||
|
message: 'Brewery posts retrieved successfully',
|
||||||
|
statusCode: 200,
|
||||||
|
payload: breweryPosts,
|
||||||
|
success: true,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getAllBeersByBrewery = async (
|
||||||
|
req: GetAllPostsByConnectedPostId,
|
||||||
|
res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>,
|
||||||
|
) => {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||||
|
const { page_size, page_num, id } = req.query;
|
||||||
|
|
||||||
|
const pageNum = parseInt(page_num, 10);
|
||||||
|
const pageSize = parseInt(page_size, 10);
|
||||||
|
|
||||||
|
const { beerPosts, count } = await getBeerPostsByBreweryIdService({
|
||||||
|
pageNum,
|
||||||
|
pageSize,
|
||||||
|
breweryId: id,
|
||||||
|
});
|
||||||
|
|
||||||
|
res.setHeader('X-Total-Count', count);
|
||||||
|
|
||||||
|
res.status(200).json({
|
||||||
|
message: 'Beers fetched successfully',
|
||||||
|
statusCode: 200,
|
||||||
|
payload: beerPosts,
|
||||||
|
success: true,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const checkIfBreweryPostOwner = async (
|
||||||
|
req: BreweryPostRequest,
|
||||||
|
res: NextApiResponse,
|
||||||
|
next: NextHandler,
|
||||||
|
) => {
|
||||||
|
const user = req.user!;
|
||||||
|
const { id } = req.query;
|
||||||
|
|
||||||
|
const breweryPost = await getBreweryPostByIdService({ breweryPostId: id });
|
||||||
|
if (!breweryPost) {
|
||||||
|
throw new ServerError('Brewery post not found', 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (breweryPost.postedBy.id !== user.id) {
|
||||||
|
throw new ServerError('You are not the owner of this brewery post', 403);
|
||||||
|
}
|
||||||
|
|
||||||
|
return next();
|
||||||
|
};
|
||||||
|
|
||||||
|
export const editBreweryPost = async (
|
||||||
|
req: EditBreweryPostRequest,
|
||||||
|
res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>,
|
||||||
|
) => {
|
||||||
|
const {
|
||||||
|
body,
|
||||||
|
query: { id },
|
||||||
|
} = req;
|
||||||
|
|
||||||
|
await updateBreweryPostService({ breweryPostId: id, body });
|
||||||
|
|
||||||
|
res.status(200).json({
|
||||||
|
message: 'Brewery post updated successfully',
|
||||||
|
success: true,
|
||||||
|
statusCode: 200,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const deleteBreweryPost = async (
|
||||||
|
req: BreweryPostRequest,
|
||||||
|
res: NextApiResponse,
|
||||||
|
) => {
|
||||||
|
const { id } = req.query;
|
||||||
|
const deleted = await deleteBreweryPostService({ breweryPostId: id });
|
||||||
|
|
||||||
|
if (!deleted) {
|
||||||
|
throw new ServerError('Brewery post not found', 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
res.status(200).json({
|
||||||
|
message: 'Brewery post deleted successfully',
|
||||||
|
success: true,
|
||||||
|
statusCode: 200,
|
||||||
|
});
|
||||||
|
};
|
||||||
22
src/controllers/posts/breweries/types/index.ts
Normal file
22
src/controllers/posts/breweries/types/index.ts
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import { UserExtendedNextApiRequest } from '@/config/auth/types';
|
||||||
|
import CreateBreweryPostSchema from '@/services/posts/brewery-post/schema/CreateBreweryPostSchema';
|
||||||
|
import EditBreweryPostValidationSchema from '@/services/posts/brewery-post/schema/EditBreweryPostValidationSchema';
|
||||||
|
import PaginatedQueryResponseSchema from '@/services/schema/PaginatedQueryResponseSchema';
|
||||||
|
import { NextApiRequest } from 'next';
|
||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
export interface GetBreweryPostsRequest extends NextApiRequest {
|
||||||
|
query: z.infer<typeof PaginatedQueryResponseSchema>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CreateBreweryPostRequest extends UserExtendedNextApiRequest {
|
||||||
|
body: z.infer<typeof CreateBreweryPostSchema>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface BreweryPostRequest extends UserExtendedNextApiRequest {
|
||||||
|
query: { id: string };
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface EditBreweryPostRequest extends BreweryPostRequest {
|
||||||
|
body: z.infer<typeof EditBreweryPostValidationSchema>;
|
||||||
|
}
|
||||||
38
src/controllers/posts/types/index.ts
Normal file
38
src/controllers/posts/types/index.ts
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
import { NextApiRequest } from 'next';
|
||||||
|
|
||||||
|
/** Represents the request object for getting all posts. */
|
||||||
|
export interface GetAllPostsRequest extends NextApiRequest {
|
||||||
|
query: { page_size: string; page_num: string };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents the request object for getting all posts by a connected post ID.
|
||||||
|
*
|
||||||
|
* This may include:
|
||||||
|
*
|
||||||
|
* - All beers by a brewery ID
|
||||||
|
* - All beers by a beer style ID
|
||||||
|
* - All beer styles by a user ID
|
||||||
|
* - And more...
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const getAllBeersByBeerStyle = async (
|
||||||
|
* req: GetAllPostsByConnectedPostId,
|
||||||
|
* res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>,
|
||||||
|
* ) => {
|
||||||
|
* const { page_size, page_num, id } = req.query;
|
||||||
|
* // ...
|
||||||
|
* };
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const getAllBeersByUserId = async (
|
||||||
|
* req: GetAllPostsByConnectedPostId,
|
||||||
|
* res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>,
|
||||||
|
* ) => {
|
||||||
|
* const { page_size, page_num, id } = req.query;
|
||||||
|
* // ...
|
||||||
|
* };
|
||||||
|
*/
|
||||||
|
export interface GetAllPostsByConnectedPostId extends NextApiRequest {
|
||||||
|
query: { id: string; page_size: string; page_num: string };
|
||||||
|
}
|
||||||
302
src/controllers/users/auth/index.ts
Normal file
302
src/controllers/users/auth/index.ts
Normal file
@@ -0,0 +1,302 @@
|
|||||||
|
import { removeTokenCookie } from '@/config/auth/cookie';
|
||||||
|
import localStrat from '@/config/auth/localStrat';
|
||||||
|
import { getLoginSession, setLoginSession } from '@/config/auth/session';
|
||||||
|
import { UserExtendedNextApiRequest } from '@/config/auth/types';
|
||||||
|
import ServerError from '@/config/util/ServerError';
|
||||||
|
|
||||||
|
import { NextApiRequest, NextApiResponse } from 'next';
|
||||||
|
import { expressWrapper } from 'next-connect';
|
||||||
|
import passport from 'passport';
|
||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
import GetUserSchema from '@/services/users/auth/schema/GetUserSchema';
|
||||||
|
|
||||||
|
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
||||||
|
import type { NextFunction } from 'express';
|
||||||
|
import { verifyConfirmationToken } from '@/config/jwt';
|
||||||
|
|
||||||
|
import { hashPassword } from '@/config/auth/passwordFns';
|
||||||
|
|
||||||
|
import {
|
||||||
|
createNewUserService,
|
||||||
|
deleteUserService,
|
||||||
|
findUserByEmailService,
|
||||||
|
findUserByUsernameService,
|
||||||
|
sendConfirmationEmailService,
|
||||||
|
sendResetPasswordEmailService,
|
||||||
|
updateUserService,
|
||||||
|
updateUserPasswordService,
|
||||||
|
confirmUserService,
|
||||||
|
} from '@/services/users/auth';
|
||||||
|
|
||||||
|
import { EditUserRequest, UserRouteRequest } from '@/controllers/users/profile/types';
|
||||||
|
import {
|
||||||
|
CheckEmailRequest,
|
||||||
|
CheckUsernameRequest,
|
||||||
|
RegisterUserRequest,
|
||||||
|
ResetPasswordRequest,
|
||||||
|
TokenValidationRequest,
|
||||||
|
UpdatePasswordRequest,
|
||||||
|
} from './types';
|
||||||
|
|
||||||
|
export const authenticateUser = expressWrapper(
|
||||||
|
async (
|
||||||
|
req: UserExtendedNextApiRequest,
|
||||||
|
res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>,
|
||||||
|
next: NextFunction,
|
||||||
|
) => {
|
||||||
|
passport.initialize();
|
||||||
|
passport.use(localStrat);
|
||||||
|
passport.authenticate(
|
||||||
|
'local',
|
||||||
|
{ session: false },
|
||||||
|
(error: unknown, token: z.infer<typeof GetUserSchema>) => {
|
||||||
|
if (error) {
|
||||||
|
next(error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
req.user = token;
|
||||||
|
next();
|
||||||
|
},
|
||||||
|
)(req, res, next);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
export const loginUser = async (
|
||||||
|
req: UserExtendedNextApiRequest,
|
||||||
|
res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>,
|
||||||
|
) => {
|
||||||
|
const user = req.user!;
|
||||||
|
await setLoginSession(res, user);
|
||||||
|
|
||||||
|
res.status(200).json({
|
||||||
|
message: 'Login successful.',
|
||||||
|
payload: user,
|
||||||
|
statusCode: 200,
|
||||||
|
success: true,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const logoutUser = async (
|
||||||
|
req: NextApiRequest,
|
||||||
|
res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>,
|
||||||
|
) => {
|
||||||
|
const session = await getLoginSession(req);
|
||||||
|
|
||||||
|
if (!session) {
|
||||||
|
throw new ServerError('You are not logged in.', 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
removeTokenCookie(res);
|
||||||
|
|
||||||
|
res.redirect('/');
|
||||||
|
};
|
||||||
|
|
||||||
|
export const registerUser = async (
|
||||||
|
req: RegisterUserRequest,
|
||||||
|
res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>,
|
||||||
|
) => {
|
||||||
|
const [usernameTaken, emailTaken] = (
|
||||||
|
await Promise.all([
|
||||||
|
findUserByUsernameService({ username: req.body.username }),
|
||||||
|
findUserByEmailService({ email: req.body.email }),
|
||||||
|
])
|
||||||
|
).map((user) => !!user);
|
||||||
|
|
||||||
|
if (usernameTaken) {
|
||||||
|
throw new ServerError(
|
||||||
|
'Could not register a user with that username as it is already taken.',
|
||||||
|
409,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (emailTaken) {
|
||||||
|
throw new ServerError(
|
||||||
|
'Could not register a user with that email as it is already taken.',
|
||||||
|
409,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const user = await createNewUserService(req.body);
|
||||||
|
|
||||||
|
await setLoginSession(res, {
|
||||||
|
id: user.id,
|
||||||
|
username: user.username,
|
||||||
|
});
|
||||||
|
|
||||||
|
await sendConfirmationEmailService({
|
||||||
|
email: user.email,
|
||||||
|
username: user.username,
|
||||||
|
userId: user.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
res.status(201).json({
|
||||||
|
success: true,
|
||||||
|
statusCode: 201,
|
||||||
|
message: 'User registered successfully.',
|
||||||
|
payload: user,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const confirmUser = async (
|
||||||
|
req: TokenValidationRequest,
|
||||||
|
res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>,
|
||||||
|
) => {
|
||||||
|
const { token } = req.query;
|
||||||
|
|
||||||
|
const user = req.user!;
|
||||||
|
const { id } = await verifyConfirmationToken(token);
|
||||||
|
|
||||||
|
if (user.accountIsVerified) {
|
||||||
|
throw new ServerError('Your account is already verified.', 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (user.id !== id) {
|
||||||
|
throw new ServerError('Could not confirm user.', 401);
|
||||||
|
}
|
||||||
|
|
||||||
|
await confirmUserService({ userId: id });
|
||||||
|
|
||||||
|
res.status(200).json({
|
||||||
|
message: 'User confirmed successfully.',
|
||||||
|
statusCode: 200,
|
||||||
|
success: true,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const resetPassword = async (
|
||||||
|
req: ResetPasswordRequest,
|
||||||
|
res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>,
|
||||||
|
) => {
|
||||||
|
const { email } = req.body;
|
||||||
|
|
||||||
|
const user = await findUserByEmailService({ email });
|
||||||
|
|
||||||
|
if (user) {
|
||||||
|
await sendResetPasswordEmailService({
|
||||||
|
email: user.email,
|
||||||
|
username: user.username,
|
||||||
|
userId: user.id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
res.status(200).json({
|
||||||
|
statusCode: 200,
|
||||||
|
success: true,
|
||||||
|
message:
|
||||||
|
'If an account with that email exists, we have sent you an email to reset your password.',
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const sendCurrentUser = async (
|
||||||
|
req: UserExtendedNextApiRequest,
|
||||||
|
res: NextApiResponse,
|
||||||
|
) => {
|
||||||
|
const { user } = req;
|
||||||
|
res.status(200).json({
|
||||||
|
message: `Currently logged in as ${user!.username}`,
|
||||||
|
statusCode: 200,
|
||||||
|
success: true,
|
||||||
|
payload: user,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const checkEmail = async (req: CheckEmailRequest, res: NextApiResponse) => {
|
||||||
|
const { email: emailToCheck } = req.query;
|
||||||
|
|
||||||
|
const email = await findUserByEmailService({ email: emailToCheck });
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
success: true,
|
||||||
|
payload: { emailIsTaken: !!email },
|
||||||
|
statusCode: 200,
|
||||||
|
message: 'Getting email availability.',
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const checkUsername = async (req: CheckUsernameRequest, res: NextApiResponse) => {
|
||||||
|
const { username: usernameToCheck } = req.query;
|
||||||
|
|
||||||
|
const username = await findUserByUsernameService({ username: usernameToCheck });
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
success: true,
|
||||||
|
payload: { usernameIsTaken: !!username },
|
||||||
|
statusCode: 200,
|
||||||
|
message: username ? 'Username is taken.' : 'Username is available.',
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const updatePassword = async (
|
||||||
|
req: UpdatePasswordRequest,
|
||||||
|
res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>,
|
||||||
|
) => {
|
||||||
|
const user = req.user!;
|
||||||
|
const { password } = req.body;
|
||||||
|
|
||||||
|
await updateUserPasswordService({
|
||||||
|
userId: user.id,
|
||||||
|
password: await hashPassword(password),
|
||||||
|
});
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
message: 'Updated user password.',
|
||||||
|
statusCode: 200,
|
||||||
|
success: true,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const resendConfirmation = async (
|
||||||
|
req: UserExtendedNextApiRequest,
|
||||||
|
res: NextApiResponse,
|
||||||
|
) => {
|
||||||
|
const user = req.user!;
|
||||||
|
|
||||||
|
await sendConfirmationEmailService({
|
||||||
|
userId: user.id,
|
||||||
|
username: user.username,
|
||||||
|
email: user.email,
|
||||||
|
});
|
||||||
|
res.status(200).json({
|
||||||
|
message: `Resent the confirmation email for ${user.username}.`,
|
||||||
|
statusCode: 200,
|
||||||
|
success: true,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const editUserInfo = async (
|
||||||
|
req: EditUserRequest,
|
||||||
|
res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>,
|
||||||
|
) => {
|
||||||
|
const { email, firstName, lastName, username } = req.body;
|
||||||
|
|
||||||
|
const updatedUser = await updateUserService({
|
||||||
|
userId: req.user!.id,
|
||||||
|
data: { email, firstName, lastName, username },
|
||||||
|
});
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
message: 'User edited successfully',
|
||||||
|
payload: updatedUser,
|
||||||
|
success: true,
|
||||||
|
statusCode: 200,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const deleteAccount = async (
|
||||||
|
req: UserRouteRequest,
|
||||||
|
res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>,
|
||||||
|
) => {
|
||||||
|
const { id } = req.query;
|
||||||
|
const deletedUser = await deleteUserService({ userId: id });
|
||||||
|
|
||||||
|
if (!deletedUser) {
|
||||||
|
throw new ServerError('Could not find a user with that id.', 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
res.send({
|
||||||
|
message: 'Successfully deleted user.',
|
||||||
|
statusCode: 200,
|
||||||
|
success: true,
|
||||||
|
});
|
||||||
|
};
|
||||||
31
src/controllers/users/auth/types/index.ts
Normal file
31
src/controllers/users/auth/types/index.ts
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import { UserExtendedNextApiRequest } from '@/config/auth/types';
|
||||||
|
import {
|
||||||
|
CreateUserValidationSchema,
|
||||||
|
UpdatePasswordSchema,
|
||||||
|
} from '@/services/users/auth/schema/CreateUserValidationSchemas';
|
||||||
|
import TokenValidationSchema from '@/services/users/auth/schema/TokenValidationSchema';
|
||||||
|
import { NextApiRequest } from 'next';
|
||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
export interface RegisterUserRequest extends NextApiRequest {
|
||||||
|
body: z.infer<typeof CreateUserValidationSchema>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TokenValidationRequest extends UserExtendedNextApiRequest {
|
||||||
|
query: z.infer<typeof TokenValidationSchema>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ResetPasswordRequest extends NextApiRequest {
|
||||||
|
body: { email: string };
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UpdatePasswordRequest extends UserExtendedNextApiRequest {
|
||||||
|
body: z.infer<typeof UpdatePasswordSchema>;
|
||||||
|
}
|
||||||
|
export interface CheckEmailRequest extends NextApiRequest {
|
||||||
|
query: { email: string };
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CheckUsernameRequest extends NextApiRequest {
|
||||||
|
query: { username: string };
|
||||||
|
}
|
||||||
216
src/controllers/users/profile/index.ts
Normal file
216
src/controllers/users/profile/index.ts
Normal file
@@ -0,0 +1,216 @@
|
|||||||
|
import ServerError from '@/config/util/ServerError';
|
||||||
|
|
||||||
|
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
||||||
|
import { NextApiResponse } from 'next';
|
||||||
|
import { z } from 'zod';
|
||||||
|
import { NextHandler } from 'next-connect';
|
||||||
|
|
||||||
|
import { UserExtendedNextApiRequest } from '@/config/auth/types';
|
||||||
|
|
||||||
|
import { findUserByIdService } from '@/services/users/auth';
|
||||||
|
|
||||||
|
import {
|
||||||
|
createUserFollow,
|
||||||
|
deleteUserFollow,
|
||||||
|
findUserFollow,
|
||||||
|
getUsersFollowedByUser,
|
||||||
|
getUsersFollowingUser,
|
||||||
|
updateUserAvatar,
|
||||||
|
updateUserProfileById,
|
||||||
|
} from '@/services/users/profile';
|
||||||
|
import {
|
||||||
|
UserRouteRequest,
|
||||||
|
GetUserFollowInfoRequest,
|
||||||
|
EditUserRequest,
|
||||||
|
UpdateAvatarRequest,
|
||||||
|
UpdateProfileRequest,
|
||||||
|
} from './types';
|
||||||
|
|
||||||
|
export const followUser = async (
|
||||||
|
req: UserRouteRequest,
|
||||||
|
res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>,
|
||||||
|
) => {
|
||||||
|
const { id } = req.query;
|
||||||
|
|
||||||
|
const user = await findUserByIdService({ userId: id });
|
||||||
|
if (!user) {
|
||||||
|
throw new ServerError('User not found', 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentUser = req.user!;
|
||||||
|
const userIsFollowedBySessionUser = await findUserFollow({
|
||||||
|
followerId: currentUser.id,
|
||||||
|
followingId: id,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!userIsFollowedBySessionUser) {
|
||||||
|
await createUserFollow({ followerId: currentUser.id, followingId: id });
|
||||||
|
res.status(200).json({
|
||||||
|
message: 'Now following user.',
|
||||||
|
success: true,
|
||||||
|
statusCode: 200,
|
||||||
|
});
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await deleteUserFollow({ followerId: currentUser.id, followingId: id });
|
||||||
|
|
||||||
|
res.status(200).json({
|
||||||
|
message: 'No longer following user.',
|
||||||
|
success: true,
|
||||||
|
statusCode: 200,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getUserFollowers = async (
|
||||||
|
req: GetUserFollowInfoRequest,
|
||||||
|
res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>,
|
||||||
|
) => {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||||
|
const { id, page_num, page_size } = req.query;
|
||||||
|
|
||||||
|
const user = await findUserByIdService({ userId: id });
|
||||||
|
if (!user) {
|
||||||
|
throw new ServerError('User not found', 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
const pageNum = parseInt(page_num, 10);
|
||||||
|
const pageSize = parseInt(page_size, 10);
|
||||||
|
|
||||||
|
const { follows, count } = await getUsersFollowingUser({
|
||||||
|
userId: id,
|
||||||
|
pageNum,
|
||||||
|
pageSize,
|
||||||
|
});
|
||||||
|
|
||||||
|
res.setHeader('X-Total-Count', count);
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
message: 'Retrieved users that are followed by queried user',
|
||||||
|
payload: follows,
|
||||||
|
success: true,
|
||||||
|
statusCode: 200,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getUsersFollowed = async (
|
||||||
|
req: GetUserFollowInfoRequest,
|
||||||
|
res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>,
|
||||||
|
) => {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||||
|
const { id, page_num, page_size } = req.query;
|
||||||
|
|
||||||
|
const user = await findUserByIdService({ userId: id });
|
||||||
|
if (!user) {
|
||||||
|
throw new ServerError('User not found', 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
const pageNum = parseInt(page_num, 10);
|
||||||
|
const pageSize = parseInt(page_size, 10);
|
||||||
|
|
||||||
|
const { follows, count } = await getUsersFollowedByUser({
|
||||||
|
userId: id,
|
||||||
|
pageNum,
|
||||||
|
pageSize,
|
||||||
|
});
|
||||||
|
|
||||||
|
res.setHeader('X-Total-Count', count);
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
message: 'Retrieved users that are followed by queried user',
|
||||||
|
payload: follows,
|
||||||
|
success: true,
|
||||||
|
statusCode: 200,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const checkIfUserIsFollowedBySessionUser = async (
|
||||||
|
req: GetUserFollowInfoRequest,
|
||||||
|
res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>,
|
||||||
|
) => {
|
||||||
|
const { id } = req.query;
|
||||||
|
|
||||||
|
const user = await findUserByIdService({ userId: id });
|
||||||
|
if (!user) {
|
||||||
|
throw new ServerError('User not found', 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentUser = req.user!;
|
||||||
|
|
||||||
|
const userFollow = await findUserFollow({
|
||||||
|
followerId: currentUser.id,
|
||||||
|
followingId: id,
|
||||||
|
});
|
||||||
|
|
||||||
|
const isFollowed = !!userFollow;
|
||||||
|
|
||||||
|
res.status(200).json({
|
||||||
|
message: isFollowed
|
||||||
|
? 'User is followed by the session user.'
|
||||||
|
: 'User is not followed by the session user.',
|
||||||
|
success: true,
|
||||||
|
statusCode: 200,
|
||||||
|
payload: { isFollowed },
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const checkIfUserCanEditUser = async (
|
||||||
|
req: EditUserRequest,
|
||||||
|
res: NextApiResponse,
|
||||||
|
next: NextHandler,
|
||||||
|
) => {
|
||||||
|
const authenticatedUser = req.user!;
|
||||||
|
|
||||||
|
const userToUpdate = await findUserByIdService({ userId: req.query.id });
|
||||||
|
if (!userToUpdate) {
|
||||||
|
throw new ServerError('User not found', 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (authenticatedUser.id !== userToUpdate.id) {
|
||||||
|
throw new ServerError('You are not permitted to modify this user', 403);
|
||||||
|
}
|
||||||
|
|
||||||
|
return next();
|
||||||
|
};
|
||||||
|
|
||||||
|
export const checkIfUserCanUpdateProfile = async <T extends UserExtendedNextApiRequest>(
|
||||||
|
req: T,
|
||||||
|
res: NextApiResponse,
|
||||||
|
next: NextHandler,
|
||||||
|
) => {
|
||||||
|
const user = req.user!;
|
||||||
|
|
||||||
|
if (user.id !== req.query.id) {
|
||||||
|
throw new ServerError('You can only update your own profile.', 403);
|
||||||
|
}
|
||||||
|
|
||||||
|
await next();
|
||||||
|
};
|
||||||
|
|
||||||
|
export const updateAvatar = async (req: UpdateAvatarRequest, res: NextApiResponse) => {
|
||||||
|
const { file, user } = req;
|
||||||
|
|
||||||
|
await updateUserAvatar({
|
||||||
|
userId: user!.id,
|
||||||
|
data: { alt: file.originalname, path: file.path, caption: '' },
|
||||||
|
});
|
||||||
|
res.status(200).json({
|
||||||
|
message: 'User avatar updated successfully.',
|
||||||
|
statusCode: 200,
|
||||||
|
success: true,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const updateProfile = async (req: UpdateProfileRequest, res: NextApiResponse) => {
|
||||||
|
const user = req.user!;
|
||||||
|
const { body } = req;
|
||||||
|
|
||||||
|
await updateUserProfileById({ userId: user!.id, data: { bio: body.bio } });
|
||||||
|
|
||||||
|
res.status(200).json({
|
||||||
|
message: 'Profile updated successfully.',
|
||||||
|
statusCode: 200,
|
||||||
|
success: true,
|
||||||
|
});
|
||||||
|
};
|
||||||
23
src/controllers/users/profile/types/index.ts
Normal file
23
src/controllers/users/profile/types/index.ts
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import { UserExtendedNextApiRequest } from '@/config/auth/types';
|
||||||
|
import EditUserSchema from '@/services/users/auth/schema/EditUserSchema';
|
||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
export interface UserRouteRequest extends UserExtendedNextApiRequest {
|
||||||
|
query: { id: string };
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GetUserFollowInfoRequest extends UserExtendedNextApiRequest {
|
||||||
|
query: { id: string; page_size: string; page_num: string };
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface EditUserRequest extends UserRouteRequest {
|
||||||
|
body: z.infer<typeof EditUserSchema>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UpdateAvatarRequest extends UserExtendedNextApiRequest {
|
||||||
|
file: Express.Multer.File;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UpdateProfileRequest extends UserExtendedNextApiRequest {
|
||||||
|
body: { bio: string };
|
||||||
|
}
|
||||||
@@ -3,12 +3,12 @@ import { Tailwind } from '@react-email/tailwind';
|
|||||||
|
|
||||||
import { FC } from 'react';
|
import { FC } from 'react';
|
||||||
|
|
||||||
interface ForgotEmailProps {
|
interface ResetPasswordEmailProps {
|
||||||
name?: string;
|
name?: string;
|
||||||
url?: string;
|
url?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ForgotEmail: FC<ForgotEmailProps> = ({ name, url }) => {
|
const ResetPasswordEmail: FC<ResetPasswordEmailProps> = ({ name, url }) => {
|
||||||
return (
|
return (
|
||||||
<Tailwind>
|
<Tailwind>
|
||||||
<Container className="mx-auto">
|
<Container className="mx-auto">
|
||||||
@@ -36,4 +36,4 @@ const ForgotEmail: FC<ForgotEmailProps> = ({ name, url }) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default ForgotEmail;
|
export default ResetPasswordEmail;
|
||||||
|
|||||||
@@ -3,13 +3,13 @@ import { Tailwind } from '@react-email/tailwind';
|
|||||||
|
|
||||||
import { FC } from 'react';
|
import { FC } from 'react';
|
||||||
|
|
||||||
interface WelcomeEmail {
|
interface WelcomeEmailProps {
|
||||||
subject?: string;
|
subject?: string;
|
||||||
name?: string;
|
name?: string;
|
||||||
url?: string;
|
url?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Welcome: FC<WelcomeEmail> = ({ name, url }) => (
|
const WelcomeEmail: FC<WelcomeEmailProps> = ({ name, url }) => (
|
||||||
<Tailwind>
|
<Tailwind>
|
||||||
<Container className="flex h-full w-full flex-col items-center justify-center">
|
<Container className="flex h-full w-full flex-col items-center justify-center">
|
||||||
<Section>
|
<Section>
|
||||||
@@ -43,4 +43,4 @@ const Welcome: FC<WelcomeEmail> = ({ name, url }) => (
|
|||||||
</Tailwind>
|
</Tailwind>
|
||||||
);
|
);
|
||||||
|
|
||||||
export default Welcome;
|
export default WelcomeEmail;
|
||||||
@@ -5,6 +5,14 @@ import { useState, useContext, useEffect } from 'react';
|
|||||||
import toast from 'react-hot-toast';
|
import toast from 'react-hot-toast';
|
||||||
import useSWR from 'swr';
|
import useSWR from 'swr';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A custom hook to confirm a user's account.
|
||||||
|
*
|
||||||
|
* @returns An object with the following properties:
|
||||||
|
*
|
||||||
|
* - `needsToLogin`: A boolean indicating whether the user needs to log in.
|
||||||
|
* - `tokenInvalid`: A boolean indicating whether the token is invalid.
|
||||||
|
*/
|
||||||
const useConfirmUser = () => {
|
const useConfirmUser = () => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { user, mutate } = useContext(UserContext);
|
const { user, mutate } = useContext(UserContext);
|
||||||
|
|||||||
@@ -3,14 +3,10 @@ import { useRouter } from 'next/router';
|
|||||||
import { useContext } from 'react';
|
import { useContext } from 'react';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Custom React hook that redirects the user to the home page if they are logged in.
|
* A custom hook to redirect the user to the home page if they are logged in.
|
||||||
*
|
*
|
||||||
* This hook is used to prevent logged in users from accessing the login and signup pages
|
* This hook is used to prevent logged in users from accessing the login and signup pages
|
||||||
* by redirecting them to the home page.
|
* and should only be used in a component that is under the UserContext provider.
|
||||||
*
|
|
||||||
* This hook should only be used in a component that is under the UserContext provider.
|
|
||||||
*
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
*/
|
||||||
const useRedirectWhenLoggedIn = (): void => {
|
const useRedirectWhenLoggedIn = (): void => {
|
||||||
const { user } = useContext(UserContext);
|
const { user } = useContext(UserContext);
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import GetUserSchema from '@/services/User/schema/GetUserSchema';
|
import GetUserSchema from '@/services/users/auth/schema/GetUserSchema';
|
||||||
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
||||||
import useSWR from 'swr';
|
import useSWR from 'swr';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A custom React hook that fetches the current user's data from the server.
|
* A custom hook to fetch the current user's data.
|
||||||
*
|
*
|
||||||
* @returns An object with the following properties:
|
* @returns An object with the following properties:
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ interface UseBeerPostCommentsProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A custom React hook that fetches comments for a specific beer post.
|
* A custom hook to fetch comments for a specific beer post.
|
||||||
*
|
*
|
||||||
* @param props - The props object.
|
* @param props - The props object.
|
||||||
* @param props.pageNum - The page number of the comments to fetch.
|
* @param props.pageNum - The page number of the comments to fetch.
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { z } from 'zod';
|
|||||||
import useSWR from 'swr';
|
import useSWR from 'swr';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Custom hook to fetch the like count for a beer post from the server.
|
* A custom hook to fetch the like count for a beer post from the server.
|
||||||
*
|
*
|
||||||
* @param beerPostId - The ID of the beer post to fetch the like count for.
|
* @param beerPostId - The ID of the beer post to fetch the like count for.
|
||||||
* @returns An object with the following properties:
|
* @returns An object with the following properties:
|
||||||
|
|||||||
@@ -5,10 +5,9 @@ import useSWR from 'swr';
|
|||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A custom React hook that checks if the current user has liked a beer post by fetching
|
* A custom hook to check if the current user has liked a beer post.
|
||||||
* data from the server.
|
|
||||||
*
|
*
|
||||||
* @param beerPostId The ID of the beer post to check for likes.
|
* @param beerPostId The ID of the beer post.
|
||||||
* @returns An object with the following properties:
|
* @returns An object with the following properties:
|
||||||
*
|
*
|
||||||
* - `error`: The error that occurred while fetching the data.
|
* - `error`: The error that occurred while fetching the data.
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import BeerPostQueryResult from '@/services/BeerPost/schema/BeerPostQueryResult';
|
import BeerPostQueryResult from '@/services/posts/beer-post/schema/BeerPostQueryResult';
|
||||||
import useSWR from 'swr';
|
import useSWR from 'swr';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A custom React hook that searches for beer posts that match a given query string.
|
* A custom hook to search for beer posts that match a given query string.
|
||||||
*
|
*
|
||||||
* @param query The search query string to match beer posts against.
|
* @param query The search query string to match beer posts against.
|
||||||
* @returns An object with the following properties:
|
* @returns An object with the following properties:
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import BeerPostQueryResult from '@/services/BeerPost/schema/BeerPostQueryResult';
|
import BeerPostQueryResult from '@/services/posts/beer-post/schema/BeerPostQueryResult';
|
||||||
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
||||||
import useSWRInfinite from 'swr/infinite';
|
import useSWRInfinite from 'swr/infinite';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A custom hook using SWR to fetch beer posts from the API.
|
* A custom hook to fetch beer posts from the API.
|
||||||
*
|
*
|
||||||
* @param options The options to use when fetching beer posts.
|
* @param options The options to use when fetching beer posts.
|
||||||
* @param options.pageSize The number of beer posts to fetch per page.
|
* @param options.pageSize The number of beer posts to fetch per page.
|
||||||
|
|||||||
@@ -1,66 +0,0 @@
|
|||||||
import BeerPostQueryResult from '@/services/BeerPost/schema/BeerPostQueryResult';
|
|
||||||
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
|
||||||
import useSWRInfinite from 'swr/infinite';
|
|
||||||
import { z } from 'zod';
|
|
||||||
|
|
||||||
interface UseBeerPostsByBeerStyleParams {
|
|
||||||
pageSize: number;
|
|
||||||
beerStyleId: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const useBeerPostsByBeerStyle = ({
|
|
||||||
pageSize,
|
|
||||||
beerStyleId,
|
|
||||||
}: UseBeerPostsByBeerStyleParams) => {
|
|
||||||
const fetcher = async (url: string) => {
|
|
||||||
const response = await fetch(url);
|
|
||||||
if (!response.ok) {
|
|
||||||
throw new Error(response.statusText);
|
|
||||||
}
|
|
||||||
|
|
||||||
const json = await response.json();
|
|
||||||
const count = response.headers.get('X-Total-Count');
|
|
||||||
|
|
||||||
const parsed = APIResponseValidationSchema.safeParse(json);
|
|
||||||
if (!parsed.success) {
|
|
||||||
throw new Error('API response validation failed');
|
|
||||||
}
|
|
||||||
|
|
||||||
const parsedPayload = z.array(BeerPostQueryResult).safeParse(parsed.data.payload);
|
|
||||||
if (!parsedPayload.success) {
|
|
||||||
throw new Error('API response validation failed');
|
|
||||||
}
|
|
||||||
|
|
||||||
const pageCount = Math.ceil(parseInt(count as string, 10) / pageSize);
|
|
||||||
return {
|
|
||||||
beerPosts: parsedPayload.data,
|
|
||||||
pageCount,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const { data, error, isLoading, setSize, size } = useSWRInfinite(
|
|
||||||
(index) =>
|
|
||||||
`/api/beers/styles/${beerStyleId}/beers?page_num=${
|
|
||||||
index + 1
|
|
||||||
}&page_size=${pageSize}`,
|
|
||||||
fetcher,
|
|
||||||
);
|
|
||||||
|
|
||||||
const beerPosts = data?.flatMap((d) => d.beerPosts) ?? [];
|
|
||||||
const pageCount = data?.[0].pageCount ?? 0;
|
|
||||||
const isLoadingMore = size > 0 && data && typeof data[size - 1] === 'undefined';
|
|
||||||
const isAtEnd = !(size < data?.[0].pageCount!);
|
|
||||||
|
|
||||||
return {
|
|
||||||
beerPosts,
|
|
||||||
pageCount,
|
|
||||||
size,
|
|
||||||
setSize,
|
|
||||||
isLoading,
|
|
||||||
isLoadingMore,
|
|
||||||
isAtEnd,
|
|
||||||
error: error as unknown,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export default useBeerPostsByBeerStyle;
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import BeerPostQueryResult from '@/services/BeerPost/schema/BeerPostQueryResult';
|
import BeerPostQueryResult from '@/services/posts/beer-post/schema/BeerPostQueryResult';
|
||||||
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
||||||
import useSWRInfinite from 'swr/infinite';
|
import useSWRInfinite from 'swr/infinite';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
@@ -8,6 +8,23 @@ interface UseBeerPostsByBeerStyleParams {
|
|||||||
beerStyleId: string;
|
beerStyleId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A custom hook to fetch beer posts by beer style.
|
||||||
|
*
|
||||||
|
* @param options The options for fetching beer posts.
|
||||||
|
* @param options.pageSize The number of beer posts to fetch per page.
|
||||||
|
* @param options.beerStyleId The ID of the beer style to fetch beer posts for.
|
||||||
|
* @returns An object with the following properties:
|
||||||
|
*
|
||||||
|
* - `beerPosts`: The beer posts fetched from the API.
|
||||||
|
* - `error`: The error that occurred while fetching the data.
|
||||||
|
* - `isAtEnd`: A boolean indicating whether all data has been fetched.
|
||||||
|
* - `isLoading`: A boolean indicating whether the data is being fetched.
|
||||||
|
* - `isLoadingMore`: A boolean indicating whether more data is being fetched.
|
||||||
|
* - `pageCount`: The total number of pages of data.
|
||||||
|
* - `setSize`: A function to set the size of the data.
|
||||||
|
* - `size`: The size of the data.`
|
||||||
|
*/
|
||||||
const useBeerPostsByBeerStyle = ({
|
const useBeerPostsByBeerStyle = ({
|
||||||
pageSize,
|
pageSize,
|
||||||
beerStyleId,
|
beerStyleId,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import BeerPostQueryResult from '@/services/BeerPost/schema/BeerPostQueryResult';
|
import BeerPostQueryResult from '@/services/posts/beer-post/schema/BeerPostQueryResult';
|
||||||
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
||||||
import useSWRInfinite from 'swr/infinite';
|
import useSWRInfinite from 'swr/infinite';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
@@ -9,7 +9,7 @@ interface UseBeerPostsByBreweryParams {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A custom hook using SWR to fetch beer posts from the API.
|
* A custom hook to fetch beer posts by brewery.
|
||||||
*
|
*
|
||||||
* @param options The options to use when fetching beer posts.
|
* @param options The options to use when fetching beer posts.
|
||||||
* @param options.pageSize The number of beer posts to fetch per page.
|
* @param options.pageSize The number of beer posts to fetch per page.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import BeerPostQueryResult from '@/services/BeerPost/schema/BeerPostQueryResult';
|
import BeerPostQueryResult from '@/services/posts/beer-post/schema/BeerPostQueryResult';
|
||||||
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
||||||
import useSWRInfinite from 'swr/infinite';
|
import useSWRInfinite from 'swr/infinite';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
@@ -8,6 +8,23 @@ interface UseBeerPostsByUserParams {
|
|||||||
userId: string;
|
userId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A custom hook to fetch beer posts by user.
|
||||||
|
*
|
||||||
|
* @param options The options for fetching beer posts.
|
||||||
|
* @param options.pageSize The number of beer posts to fetch per page.
|
||||||
|
* @param options.userId The ID of the user to fetch beer posts for.
|
||||||
|
* @returns An object with the following properties:
|
||||||
|
*
|
||||||
|
* - `beerPosts`: The beer posts fetched from the API.
|
||||||
|
* - `error`: The error that occurred while fetching the data.
|
||||||
|
* - `isAtEnd`: A boolean indicating whether all data has been fetched.
|
||||||
|
* - `isLoading`: A boolean indicating whether the data is being fetched.
|
||||||
|
* - `isLoadingMore`: A boolean indicating whether more data is being fetched.
|
||||||
|
* - `pageCount`: The total number of pages of data.
|
||||||
|
* - `setSize`: A function to set the size of the data.
|
||||||
|
* - `size`: The size of the data.`
|
||||||
|
*/
|
||||||
const useBeerPostsByUser = ({ pageSize, userId }: UseBeerPostsByUserParams) => {
|
const useBeerPostsByUser = ({ pageSize, userId }: UseBeerPostsByUserParams) => {
|
||||||
const fetcher = async (url: string) => {
|
const fetcher = async (url: string) => {
|
||||||
const response = await fetch(url);
|
const response = await fetch(url);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import BeerPostQueryResult from '@/services/BeerPost/schema/BeerPostQueryResult';
|
import BeerPostQueryResult from '@/services/posts/beer-post/schema/BeerPostQueryResult';
|
||||||
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
||||||
import useSWRInfinite from 'swr/infinite';
|
import useSWRInfinite from 'swr/infinite';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
@@ -9,7 +9,7 @@ interface UseBeerRecommendationsParams {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A custom hook using SWR to fetch beer recommendations from the API.
|
* A custom hook to fetch beer recommendations from the API.
|
||||||
*
|
*
|
||||||
* @param options The options to use when fetching beer recommendations.
|
* @param options The options to use when fetching beer recommendations.
|
||||||
* @param options.pageSize The number of beer recommendations to fetch per page.
|
* @param options.pageSize The number of beer recommendations to fetch per page.
|
||||||
|
|||||||
@@ -3,13 +3,31 @@ import APIResponseValidationSchema from '@/validation/APIResponseValidationSchem
|
|||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
import useSWRInfinite from 'swr/infinite';
|
import useSWRInfinite from 'swr/infinite';
|
||||||
|
|
||||||
interface UseBeerStyleCommentsProps {
|
interface UseBeerStyleCommentsOptions {
|
||||||
id: string;
|
id: string;
|
||||||
pageSize: number;
|
pageSize: number;
|
||||||
pageNum: number;
|
pageNum: number;
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
const useBeerStyleComments = ({ id, pageSize }: UseBeerStyleCommentsProps) => {
|
* A custom hook to fetch comments for a beer style post.
|
||||||
|
*
|
||||||
|
* @param options The options for fetching comments.
|
||||||
|
* @param options.id The ID of the beer style to fetch comments for.
|
||||||
|
* @param options.pageSize The number of comments to fetch per page.
|
||||||
|
* @param options.pageNum The page number to fetch.
|
||||||
|
* @returns An object with the following properties:
|
||||||
|
*
|
||||||
|
* - `comments`: The comments fetched from the API.
|
||||||
|
* - `error`: The error that occurred while fetching the data.
|
||||||
|
* - `isLoading`: A boolean indicating whether the data is being fetched.
|
||||||
|
* - `isLoadingMore`: A boolean indicating whether more data is being fetched.
|
||||||
|
* - `isAtEnd`: A boolean indicating whether all data has been fetched.
|
||||||
|
* - `mutate`: A function to mutate the data.
|
||||||
|
* - `pageCount`: The total number of pages of data.
|
||||||
|
* - `setSize`: A function to set the size of the data.
|
||||||
|
* - `size`: The size of the data.
|
||||||
|
*/
|
||||||
|
const useBeerStyleComments = ({ id, pageSize }: UseBeerStyleCommentsOptions) => {
|
||||||
const fetcher = async (url: string) => {
|
const fetcher = async (url: string) => {
|
||||||
const response = await fetch(url);
|
const response = await fetch(url);
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { z } from 'zod';
|
|||||||
import useSWR from 'swr';
|
import useSWR from 'swr';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Custom hook to fetch the like count for a beer style from the server.
|
* A custom hook to fetch the like count for a beer style post.
|
||||||
*
|
*
|
||||||
* @param beerStyleId - The ID of the beer style to fetch the like count for.
|
* @param beerStyleId - The ID of the beer style to fetch the like count for.
|
||||||
* @returns An object with the following properties:
|
* @returns An object with the following properties:
|
||||||
|
|||||||
@@ -5,8 +5,7 @@ import useSWR from 'swr';
|
|||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A custom React hook that checks if the current user has liked a beer style by fetching
|
* A custom hook to check if the current user has liked a beer style.
|
||||||
* data from the server.
|
|
||||||
*
|
*
|
||||||
* @param beerStyleId The ID of the beer style to check for likes.
|
* @param beerStyleId The ID of the beer style to check for likes.
|
||||||
* @returns An object with the following properties:
|
* @returns An object with the following properties:
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import BeerStyleQueryResult from '@/services/BeerStyles/schema/BeerStyleQueryResult';
|
import BeerStyleQueryResult from '@/services/posts/beer-style-post/schema/BeerStyleQueryResult';
|
||||||
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
||||||
import useSWRInfinite from 'swr/infinite';
|
import useSWRInfinite from 'swr/infinite';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A custom hook using SWR to fetch beer types from the API.
|
* A custom hook to fetch beer styles posts.
|
||||||
*
|
*
|
||||||
* @param options The options to use when fetching beer types.
|
* @param options The options to use when fetching beer types.
|
||||||
* @param options.pageSize The number of beer types to fetch per page.
|
* @param options.pageSize The number of beer types to fetch per page.
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ interface UseBreweryPostCommentsProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A custom React hook that fetches comments for a specific brewery post.
|
* A custom hook to fetch comments for a specific brewery post.
|
||||||
*
|
*
|
||||||
* @param props - The props object.
|
* @param props - The props object.
|
||||||
* @param props.pageNum - The page number of the comments to fetch.
|
* @param props.pageNum - The page number of the comments to fetch.
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import useSWR from 'swr';
|
|||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A custom React hook that checks if the current user likes a given brewery post.
|
* A custom hook to check if the current user likes a given brewery post.
|
||||||
*
|
*
|
||||||
* @param breweryPostId - The ID of the brewery post to check.
|
* @param breweryPostId - The ID of the brewery post to check.
|
||||||
* @returns An object with the following properties:
|
* @returns An object with the following properties:
|
||||||
|
|||||||
@@ -1,8 +1,24 @@
|
|||||||
import BreweryPostMapQueryResult from '@/services/BreweryPost/schema/BreweryPostMapQueryResult';
|
import BreweryPostMapQueryResult from '@/services/posts/brewery-post/schema/BreweryPostMapQueryResult';
|
||||||
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
||||||
import useSWRInfinite from 'swr/infinite';
|
import useSWRInfinite from 'swr/infinite';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A custom hook to fetch brewery posts for the map.
|
||||||
|
*
|
||||||
|
* @param options The options to use when fetching brewery posts.
|
||||||
|
* @param options.pageSize The number of brewery posts to fetch per page.
|
||||||
|
* @returns An object with the following properties:
|
||||||
|
*
|
||||||
|
* - `breweryPosts`: The brewery posts fetched from the API.
|
||||||
|
* - `error`: The error that occurred while fetching the data.
|
||||||
|
* - `isAtEnd`: A boolean indicating whether all data has been fetched.
|
||||||
|
* - `isLoading`: A boolean indicating whether the data is being fetched.
|
||||||
|
* - `isLoadingMore`: A boolean indicating whether more data is being fetched.
|
||||||
|
* - `pageCount`: The total number of pages of data.
|
||||||
|
* - `setSize`: A function to set the size of the data.
|
||||||
|
* - `size`: The size of the data.
|
||||||
|
*/
|
||||||
const useBreweryMapPagePosts = ({ pageSize }: { pageSize: number }) => {
|
const useBreweryMapPagePosts = ({ pageSize }: { pageSize: number }) => {
|
||||||
const fetcher = async (url: string) => {
|
const fetcher = async (url: string) => {
|
||||||
const response = await fetch(url);
|
const response = await fetch(url);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import BreweryPostQueryResult from '@/services/BreweryPost/schema/BreweryPostQueryResult';
|
import BreweryPostQueryResult from '@/services/posts/brewery-post/schema/BreweryPostQueryResult';
|
||||||
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
||||||
import useSWRInfinite from 'swr/infinite';
|
import useSWRInfinite from 'swr/infinite';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import BreweryPostQueryResult from '@/services/BreweryPost/schema/BreweryPostQueryResult';
|
import BreweryPostQueryResult from '@/services/posts/brewery-post/schema/BreweryPostQueryResult';
|
||||||
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
||||||
import useSWRInfinite from 'swr/infinite';
|
import useSWRInfinite from 'swr/infinite';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
@@ -8,6 +8,23 @@ interface UseBreweryPostsByUserParams {
|
|||||||
userId: string;
|
userId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A custom hook to fetch brewery posts by a specific user.
|
||||||
|
*
|
||||||
|
* @param options The options to use when fetching brewery posts.
|
||||||
|
* @param options.pageSize The number of brewery posts to fetch per page.
|
||||||
|
* @param options.userId The ID of the user to fetch brewery posts for.
|
||||||
|
* @returns An object with the following properties:
|
||||||
|
*
|
||||||
|
* - `breweryPosts`: The brewery posts fetched from the API.
|
||||||
|
* - `error`: The error that occurred while fetching the data.
|
||||||
|
* - `isAtEnd`: A boolean indicating whether all data has been fetched.
|
||||||
|
* - `isLoading`: A boolean indicating whether the data is being fetched.
|
||||||
|
* - `isLoadingMore`: A boolean indicating whether more data is being fetched.
|
||||||
|
* - `pageCount`: The total number of pages of data.
|
||||||
|
* - `setSize`: A function to set the size of the data.
|
||||||
|
* - `size`: The size of the data.
|
||||||
|
*/
|
||||||
const useBreweryPostsByUser = ({ pageSize, userId }: UseBreweryPostsByUserParams) => {
|
const useBreweryPostsByUser = ({ pageSize, userId }: UseBreweryPostsByUserParams) => {
|
||||||
const fetcher = async (url: string) => {
|
const fetcher = async (url: string) => {
|
||||||
const response = await fetch(url);
|
const response = await fetch(url);
|
||||||
|
|||||||
@@ -2,6 +2,17 @@ import APIResponseValidationSchema from '@/validation/APIResponseValidationSchem
|
|||||||
import useSWR from 'swr';
|
import useSWR from 'swr';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A custom hook to check if the current user follows a given user.
|
||||||
|
*
|
||||||
|
* @param userFollowedId - The ID of the user to check.
|
||||||
|
* @returns An object with the following properties:
|
||||||
|
*
|
||||||
|
* - `isFollowed`: A boolean indicating whether the current user follows the user.
|
||||||
|
* - `error`: The error that occurred while fetching the data.
|
||||||
|
* - `isLoading`: A boolean indicating whether the data is being fetched.
|
||||||
|
* - `mutate`: A function to mutate the data.
|
||||||
|
*/
|
||||||
const useFollowStatus = (userFollowedId: string) => {
|
const useFollowStatus = (userFollowedId: string) => {
|
||||||
const { data, error, isLoading, mutate } = useSWR(
|
const { data, error, isLoading, mutate } = useSWR(
|
||||||
`/api/users/${userFollowedId}/is-followed`,
|
`/api/users/${userFollowedId}/is-followed`,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/**
|
/**
|
||||||
* Custom hook using SWR for fetching users followed by a specific user.
|
* A custom hook to fetch the users followed by a given user.
|
||||||
*
|
*
|
||||||
* @param options - The options for fetching users.
|
* @param options - The options for fetching users.
|
||||||
* @param [options.pageSize=5] - The number of users to fetch per page. Default is `5`
|
* @param [options.pageSize=5] - The number of users to fetch per page. Default is `5`
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
* - `mutate` A function to mutate the data.
|
* - `mutate` A function to mutate the data.
|
||||||
* - `error` The error object, if any.
|
* - `error` The error object, if any.
|
||||||
*/
|
*/
|
||||||
import FollowInfoSchema from '@/services/UserFollows/schema/FollowInfoSchema';
|
import FollowInfoSchema from '@/services/users/profile/schema/FollowInfoSchema';
|
||||||
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
||||||
import useSWRInfinite from 'swr/infinite';
|
import useSWRInfinite from 'swr/infinite';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import FollowInfoSchema from '@/services/UserFollows/schema/FollowInfoSchema';
|
import FollowInfoSchema from '@/services/users/profile/schema/FollowInfoSchema';
|
||||||
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
||||||
import useSWRInfinite from 'swr/infinite';
|
import useSWRInfinite from 'swr/infinite';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
@@ -9,10 +9,7 @@ interface UseGetUsersFollowingUser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Custom hook using SWR for fetching users followed by a specific user.
|
* A custom hook to fetch users following a user.
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* const { followers, followerCount } = useGetUsersFollowingUser({ userId: '123' });
|
|
||||||
*
|
*
|
||||||
* @param options - The options for fetching users.
|
* @param options - The options for fetching users.
|
||||||
* @param [options.pageSize=5] - The number of users to fetch per page. Default is `5`
|
* @param [options.pageSize=5] - The number of users to fetch per page. Default is `5`
|
||||||
|
|||||||
@@ -1,85 +1,22 @@
|
|||||||
import { UserExtendedNextApiRequest } from '@/config/auth/types';
|
|
||||||
import getCurrentUser from '@/config/nextConnect/middleware/getCurrentUser';
|
import getCurrentUser from '@/config/nextConnect/middleware/getCurrentUser';
|
||||||
import validateRequest from '@/config/nextConnect/middleware/validateRequest';
|
import validateRequest from '@/config/nextConnect/middleware/validateRequest';
|
||||||
import NextConnectOptions from '@/config/nextConnect/NextConnectOptions';
|
import NextConnectOptions from '@/config/nextConnect/NextConnectOptions';
|
||||||
import ServerError from '@/config/util/ServerError';
|
|
||||||
import DBClient from '@/prisma/DBClient';
|
|
||||||
import findBeerCommentById from '@/services/BeerComment/findBeerCommentById';
|
|
||||||
import CreateCommentValidationSchema from '@/services/schema/CommentSchema/CreateCommentValidationSchema';
|
import CreateCommentValidationSchema from '@/services/schema/CommentSchema/CreateCommentValidationSchema';
|
||||||
import editBeerCommentById from '@/services/BeerComment/editBeerCommentById';
|
|
||||||
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
||||||
import { NextApiResponse } from 'next';
|
import { NextApiResponse } from 'next';
|
||||||
import { createRouter, NextHandler } from 'next-connect';
|
import { createRouter } from 'next-connect';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
|
||||||
interface DeleteCommentRequest extends UserExtendedNextApiRequest {
|
import { CommentRequest } from '@/controllers/comments/types';
|
||||||
query: { id: string };
|
import {
|
||||||
}
|
checkIfBeerCommentOwner,
|
||||||
|
deleteBeerPostComment,
|
||||||
interface EditCommentRequest extends UserExtendedNextApiRequest {
|
editBeerPostComment,
|
||||||
query: { id: string };
|
} from '@/controllers/comments/beer-comments';
|
||||||
body: z.infer<typeof CreateCommentValidationSchema>;
|
|
||||||
}
|
|
||||||
|
|
||||||
const checkIfCommentOwner = async (
|
|
||||||
req: DeleteCommentRequest | EditCommentRequest,
|
|
||||||
res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>,
|
|
||||||
next: NextHandler,
|
|
||||||
) => {
|
|
||||||
const { id } = req.query;
|
|
||||||
const user = req.user!;
|
|
||||||
const comment = await findBeerCommentById({ beerCommentId: id });
|
|
||||||
|
|
||||||
if (!comment) {
|
|
||||||
throw new ServerError('Comment not found', 404);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (comment.postedBy.id !== user.id) {
|
|
||||||
throw new ServerError('You are not authorized to modify this comment', 403);
|
|
||||||
}
|
|
||||||
|
|
||||||
return next();
|
|
||||||
};
|
|
||||||
|
|
||||||
const editComment = async (
|
|
||||||
req: EditCommentRequest,
|
|
||||||
res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>,
|
|
||||||
) => {
|
|
||||||
const { id } = req.query;
|
|
||||||
|
|
||||||
const updated = await editBeerCommentById({
|
|
||||||
content: req.body.content,
|
|
||||||
rating: req.body.rating,
|
|
||||||
id,
|
|
||||||
});
|
|
||||||
|
|
||||||
res.status(200).json({
|
|
||||||
success: true,
|
|
||||||
message: 'Comment updated successfully',
|
|
||||||
statusCode: 200,
|
|
||||||
payload: updated,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const deleteComment = async (
|
|
||||||
req: DeleteCommentRequest,
|
|
||||||
res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>,
|
|
||||||
) => {
|
|
||||||
const { id } = req.query;
|
|
||||||
|
|
||||||
await DBClient.instance.beerComment.delete({
|
|
||||||
where: { id },
|
|
||||||
});
|
|
||||||
|
|
||||||
res.status(200).json({
|
|
||||||
success: true,
|
|
||||||
message: 'Comment deleted successfully',
|
|
||||||
statusCode: 200,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const router = createRouter<
|
const router = createRouter<
|
||||||
DeleteCommentRequest,
|
CommentRequest,
|
||||||
NextApiResponse<z.infer<typeof APIResponseValidationSchema>>
|
NextApiResponse<z.infer<typeof APIResponseValidationSchema>>
|
||||||
>();
|
>();
|
||||||
|
|
||||||
@@ -87,8 +24,8 @@ router
|
|||||||
.delete(
|
.delete(
|
||||||
validateRequest({ querySchema: z.object({ id: z.string().cuid() }) }),
|
validateRequest({ querySchema: z.object({ id: z.string().cuid() }) }),
|
||||||
getCurrentUser,
|
getCurrentUser,
|
||||||
checkIfCommentOwner,
|
checkIfBeerCommentOwner,
|
||||||
deleteComment,
|
deleteBeerPostComment,
|
||||||
)
|
)
|
||||||
.put(
|
.put(
|
||||||
validateRequest({
|
validateRequest({
|
||||||
@@ -96,8 +33,8 @@ router
|
|||||||
bodySchema: CreateCommentValidationSchema,
|
bodySchema: CreateCommentValidationSchema,
|
||||||
}),
|
}),
|
||||||
getCurrentUser,
|
getCurrentUser,
|
||||||
checkIfCommentOwner,
|
checkIfBeerCommentOwner,
|
||||||
editComment,
|
editBeerPostComment,
|
||||||
);
|
);
|
||||||
|
|
||||||
const handler = router.handler(NextConnectOptions);
|
const handler = router.handler(NextConnectOptions);
|
||||||
|
|||||||
@@ -1,85 +1,21 @@
|
|||||||
import { UserExtendedNextApiRequest } from '@/config/auth/types';
|
|
||||||
import getCurrentUser from '@/config/nextConnect/middleware/getCurrentUser';
|
import getCurrentUser from '@/config/nextConnect/middleware/getCurrentUser';
|
||||||
import validateRequest from '@/config/nextConnect/middleware/validateRequest';
|
import validateRequest from '@/config/nextConnect/middleware/validateRequest';
|
||||||
import NextConnectOptions from '@/config/nextConnect/NextConnectOptions';
|
import NextConnectOptions from '@/config/nextConnect/NextConnectOptions';
|
||||||
import ServerError from '@/config/util/ServerError';
|
import {
|
||||||
import DBClient from '@/prisma/DBClient';
|
checkIfBeerStyleCommentOwner,
|
||||||
|
deleteBeerStyleComment,
|
||||||
|
editBeerStyleComment,
|
||||||
|
} from '@/controllers/comments/beer-style-comments';
|
||||||
|
import { CommentRequest } from '@/controllers/comments/types';
|
||||||
import CreateCommentValidationSchema from '@/services/schema/CommentSchema/CreateCommentValidationSchema';
|
import CreateCommentValidationSchema from '@/services/schema/CommentSchema/CreateCommentValidationSchema';
|
||||||
|
|
||||||
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
||||||
import { NextApiResponse } from 'next';
|
import { NextApiResponse } from 'next';
|
||||||
import { createRouter, NextHandler } from 'next-connect';
|
import { createRouter } from 'next-connect';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
|
||||||
interface DeleteCommentRequest extends UserExtendedNextApiRequest {
|
|
||||||
query: { id: string };
|
|
||||||
}
|
|
||||||
|
|
||||||
interface EditCommentRequest extends UserExtendedNextApiRequest {
|
|
||||||
query: { id: string };
|
|
||||||
body: z.infer<typeof CreateCommentValidationSchema>;
|
|
||||||
}
|
|
||||||
|
|
||||||
const checkIfCommentOwner = async (
|
|
||||||
req: DeleteCommentRequest | EditCommentRequest,
|
|
||||||
res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>,
|
|
||||||
next: NextHandler,
|
|
||||||
) => {
|
|
||||||
const { id } = req.query;
|
|
||||||
const user = req.user!;
|
|
||||||
const comment = await DBClient.instance.beerStyleComment.findFirst({ where: { id } });
|
|
||||||
|
|
||||||
if (!comment) {
|
|
||||||
throw new ServerError('Comment not found', 404);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (comment.postedById !== user.id) {
|
|
||||||
throw new ServerError('You are not authorized to modify this comment', 403);
|
|
||||||
}
|
|
||||||
|
|
||||||
return next();
|
|
||||||
};
|
|
||||||
|
|
||||||
const editComment = async (
|
|
||||||
req: EditCommentRequest,
|
|
||||||
res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>,
|
|
||||||
) => {
|
|
||||||
const { id } = req.query;
|
|
||||||
|
|
||||||
const updated = await DBClient.instance.beerStyleComment.update({
|
|
||||||
where: { id },
|
|
||||||
data: {
|
|
||||||
content: req.body.content,
|
|
||||||
rating: req.body.rating,
|
|
||||||
updatedAt: new Date(),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
return res.status(200).json({
|
|
||||||
success: true,
|
|
||||||
message: 'Comment updated successfully',
|
|
||||||
statusCode: 200,
|
|
||||||
payload: updated,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const deleteComment = async (
|
|
||||||
req: DeleteCommentRequest,
|
|
||||||
res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>,
|
|
||||||
) => {
|
|
||||||
const { id } = req.query;
|
|
||||||
|
|
||||||
await DBClient.instance.beerStyleComment.delete({ where: { id } });
|
|
||||||
|
|
||||||
res.status(200).json({
|
|
||||||
success: true,
|
|
||||||
message: 'Comment deleted successfully',
|
|
||||||
statusCode: 200,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const router = createRouter<
|
const router = createRouter<
|
||||||
DeleteCommentRequest,
|
CommentRequest,
|
||||||
NextApiResponse<z.infer<typeof APIResponseValidationSchema>>
|
NextApiResponse<z.infer<typeof APIResponseValidationSchema>>
|
||||||
>();
|
>();
|
||||||
|
|
||||||
@@ -89,8 +25,8 @@ router
|
|||||||
querySchema: z.object({ id: z.string().cuid() }),
|
querySchema: z.object({ id: z.string().cuid() }),
|
||||||
}),
|
}),
|
||||||
getCurrentUser,
|
getCurrentUser,
|
||||||
checkIfCommentOwner,
|
checkIfBeerStyleCommentOwner,
|
||||||
deleteComment,
|
deleteBeerStyleComment,
|
||||||
)
|
)
|
||||||
.put(
|
.put(
|
||||||
validateRequest({
|
validateRequest({
|
||||||
@@ -98,8 +34,8 @@ router
|
|||||||
bodySchema: CreateCommentValidationSchema,
|
bodySchema: CreateCommentValidationSchema,
|
||||||
}),
|
}),
|
||||||
getCurrentUser,
|
getCurrentUser,
|
||||||
checkIfCommentOwner,
|
checkIfBeerStyleCommentOwner,
|
||||||
editComment,
|
editBeerStyleComment,
|
||||||
);
|
);
|
||||||
|
|
||||||
const handler = router.handler(NextConnectOptions);
|
const handler = router.handler(NextConnectOptions);
|
||||||
|
|||||||
@@ -1,78 +1,19 @@
|
|||||||
import DBClient from '@/prisma/DBClient';
|
|
||||||
import getAllBeerComments from '@/services/BeerComment/getAllBeerComments';
|
|
||||||
import validateRequest from '@/config/nextConnect/middleware/validateRequest';
|
import validateRequest from '@/config/nextConnect/middleware/validateRequest';
|
||||||
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
||||||
import { UserExtendedNextApiRequest } from '@/config/auth/types';
|
|
||||||
import NextConnectOptions from '@/config/nextConnect/NextConnectOptions';
|
import NextConnectOptions from '@/config/nextConnect/NextConnectOptions';
|
||||||
import createNewBeerComment from '@/services/BeerComment/createNewBeerComment';
|
|
||||||
|
|
||||||
import { createRouter } from 'next-connect';
|
import { createRouter } from 'next-connect';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
import getCurrentUser from '@/config/nextConnect/middleware/getCurrentUser';
|
import getCurrentUser from '@/config/nextConnect/middleware/getCurrentUser';
|
||||||
import { NextApiResponse } from 'next';
|
import { NextApiResponse } from 'next';
|
||||||
import CommentQueryResult from '@/services/schema/CommentSchema/CommentQueryResult';
|
|
||||||
import CreateCommentValidationSchema from '@/services/schema/CommentSchema/CreateCommentValidationSchema';
|
import CreateCommentValidationSchema from '@/services/schema/CommentSchema/CreateCommentValidationSchema';
|
||||||
|
import {
|
||||||
interface CreateCommentRequest extends UserExtendedNextApiRequest {
|
createBeerPostComment,
|
||||||
body: z.infer<typeof CreateCommentValidationSchema>;
|
getAllBeerPostComments,
|
||||||
query: { id: string };
|
} from '@/controllers/comments/beer-comments';
|
||||||
}
|
|
||||||
|
|
||||||
interface GetAllCommentsRequest extends UserExtendedNextApiRequest {
|
|
||||||
query: { id: string; page_size: string; page_num: string };
|
|
||||||
}
|
|
||||||
|
|
||||||
const createComment = async (
|
|
||||||
req: CreateCommentRequest,
|
|
||||||
res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>,
|
|
||||||
) => {
|
|
||||||
const { content, rating } = req.body;
|
|
||||||
|
|
||||||
const beerPostId = req.query.id;
|
|
||||||
|
|
||||||
const newBeerComment: z.infer<typeof CommentQueryResult> = await createNewBeerComment({
|
|
||||||
content,
|
|
||||||
rating,
|
|
||||||
beerPostId,
|
|
||||||
userId: req.user!.id,
|
|
||||||
});
|
|
||||||
|
|
||||||
res.status(201).json({
|
|
||||||
message: 'Beer comment created successfully',
|
|
||||||
statusCode: 201,
|
|
||||||
payload: newBeerComment,
|
|
||||||
success: true,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const getAll = async (
|
|
||||||
req: GetAllCommentsRequest,
|
|
||||||
res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>,
|
|
||||||
) => {
|
|
||||||
const beerPostId = req.query.id;
|
|
||||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
||||||
const { page_size, page_num } = req.query;
|
|
||||||
|
|
||||||
const comments = await getAllBeerComments({
|
|
||||||
beerPostId,
|
|
||||||
pageNum: parseInt(page_num, 10),
|
|
||||||
pageSize: parseInt(page_size, 10),
|
|
||||||
});
|
|
||||||
|
|
||||||
const count = await DBClient.instance.beerComment.count({ where: { beerPostId } });
|
|
||||||
|
|
||||||
res.setHeader('X-Total-Count', count);
|
|
||||||
|
|
||||||
res.status(200).json({
|
|
||||||
message: 'Beer comments fetched successfully',
|
|
||||||
statusCode: 200,
|
|
||||||
payload: comments,
|
|
||||||
success: true,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const router = createRouter<
|
const router = createRouter<
|
||||||
// I don't want to use any, but I can't figure out how to get the types to work
|
// @TODO: Fix this any type
|
||||||
any,
|
any,
|
||||||
NextApiResponse<z.infer<typeof APIResponseValidationSchema>>
|
NextApiResponse<z.infer<typeof APIResponseValidationSchema>>
|
||||||
>();
|
>();
|
||||||
@@ -83,7 +24,7 @@ router.post(
|
|||||||
querySchema: z.object({ id: z.string().cuid() }),
|
querySchema: z.object({ id: z.string().cuid() }),
|
||||||
}),
|
}),
|
||||||
getCurrentUser,
|
getCurrentUser,
|
||||||
createComment,
|
createBeerPostComment,
|
||||||
);
|
);
|
||||||
|
|
||||||
router.get(
|
router.get(
|
||||||
@@ -94,7 +35,7 @@ router.get(
|
|||||||
page_num: z.coerce.number().int().positive(),
|
page_num: z.coerce.number().int().positive(),
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
getAll,
|
getAllBeerPostComments,
|
||||||
);
|
);
|
||||||
|
|
||||||
const handler = router.handler(NextConnectOptions);
|
const handler = router.handler(NextConnectOptions);
|
||||||
|
|||||||
@@ -1,53 +1,19 @@
|
|||||||
import NextConnectOptions from '@/config/nextConnect/NextConnectOptions';
|
import NextConnectOptions from '@/config/nextConnect/NextConnectOptions';
|
||||||
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
||||||
import { UserExtendedNextApiRequest } from '@/config/auth/types';
|
|
||||||
import { createRouter } from 'next-connect';
|
import { createRouter } from 'next-connect';
|
||||||
|
|
||||||
import getCurrentUser from '@/config/nextConnect/middleware/getCurrentUser';
|
import getCurrentUser from '@/config/nextConnect/middleware/getCurrentUser';
|
||||||
|
|
||||||
import { NextApiResponse } from 'next';
|
import { NextApiResponse } from 'next';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
import ServerError from '@/config/util/ServerError';
|
|
||||||
import validateRequest from '@/config/nextConnect/middleware/validateRequest';
|
import validateRequest from '@/config/nextConnect/middleware/validateRequest';
|
||||||
import addBeerImageToDB from '@/services/BeerImage/addBeerImageToDB';
|
|
||||||
import ImageMetadataValidationSchema from '@/services/schema/ImageSchema/ImageMetadataValidationSchema';
|
import ImageMetadataValidationSchema from '@/services/schema/ImageSchema/ImageMetadataValidationSchema';
|
||||||
import { uploadMiddlewareMultiple } from '@/config/multer/uploadMiddleware';
|
import { uploadMiddlewareMultiple } from '@/config/multer/uploadMiddleware';
|
||||||
|
import { UploadImagesRequest } from '@/controllers/images/types';
|
||||||
interface UploadBeerPostImagesRequest extends UserExtendedNextApiRequest {
|
import { processBeerImageData } from '@/controllers/images/beer-images';
|
||||||
files?: Express.Multer.File[];
|
|
||||||
query: { id: string };
|
|
||||||
body: z.infer<typeof ImageMetadataValidationSchema>;
|
|
||||||
}
|
|
||||||
|
|
||||||
const processImageData = async (
|
|
||||||
req: UploadBeerPostImagesRequest,
|
|
||||||
res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>,
|
|
||||||
) => {
|
|
||||||
const { files, user, body } = req;
|
|
||||||
|
|
||||||
if (!files || !files.length) {
|
|
||||||
throw new ServerError('No images uploaded', 400);
|
|
||||||
}
|
|
||||||
|
|
||||||
const beerImages = await addBeerImageToDB({
|
|
||||||
alt: body.alt,
|
|
||||||
caption: body.caption,
|
|
||||||
beerPostId: req.query.id,
|
|
||||||
userId: user!.id,
|
|
||||||
files,
|
|
||||||
});
|
|
||||||
|
|
||||||
res.status(200).json({
|
|
||||||
success: true,
|
|
||||||
message: `Successfully uploaded ${beerImages.length} image${
|
|
||||||
beerImages.length > 1 ? 's' : ''
|
|
||||||
}`,
|
|
||||||
statusCode: 200,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const router = createRouter<
|
const router = createRouter<
|
||||||
UploadBeerPostImagesRequest,
|
UploadImagesRequest,
|
||||||
NextApiResponse<z.infer<typeof APIResponseValidationSchema>>
|
NextApiResponse<z.infer<typeof APIResponseValidationSchema>>
|
||||||
>();
|
>();
|
||||||
|
|
||||||
@@ -56,7 +22,7 @@ router.post(
|
|||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
uploadMiddlewareMultiple,
|
uploadMiddlewareMultiple,
|
||||||
validateRequest({ bodySchema: ImageMetadataValidationSchema }),
|
validateRequest({ bodySchema: ImageMetadataValidationSchema }),
|
||||||
processImageData,
|
processBeerImageData,
|
||||||
);
|
);
|
||||||
|
|
||||||
const handler = router.handler(NextConnectOptions);
|
const handler = router.handler(NextConnectOptions);
|
||||||
|
|||||||
@@ -1,73 +1,21 @@
|
|||||||
import getCurrentUser from '@/config/nextConnect/middleware/getCurrentUser';
|
|
||||||
import getBeerPostById from '@/services/BeerPost/getBeerPostById';
|
|
||||||
import { UserExtendedNextApiRequest } from '@/config/auth/types';
|
|
||||||
import editBeerPostById from '@/services/BeerPost/editBeerPostById';
|
|
||||||
import EditBeerPostValidationSchema from '@/services/BeerPost/schema/EditBeerPostValidationSchema';
|
|
||||||
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
|
||||||
import { NextApiResponse } from 'next';
|
import { NextApiResponse } from 'next';
|
||||||
import { createRouter, NextHandler } from 'next-connect';
|
import { createRouter } from 'next-connect';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
import ServerError from '@/config/util/ServerError';
|
|
||||||
|
import getCurrentUser from '@/config/nextConnect/middleware/getCurrentUser';
|
||||||
import validateRequest from '@/config/nextConnect/middleware/validateRequest';
|
import validateRequest from '@/config/nextConnect/middleware/validateRequest';
|
||||||
import NextConnectOptions from '@/config/nextConnect/NextConnectOptions';
|
import NextConnectOptions from '@/config/nextConnect/NextConnectOptions';
|
||||||
import deleteBeerPostById from '@/services/BeerPost/deleteBeerPostById';
|
|
||||||
|
|
||||||
interface BeerPostRequest extends UserExtendedNextApiRequest {
|
import { EditBeerPostRequest } from '@/controllers/posts/beer-posts/types';
|
||||||
query: { id: string };
|
import {
|
||||||
}
|
checkIfBeerPostOwner,
|
||||||
|
editBeerPost,
|
||||||
|
deleteBeerPost,
|
||||||
|
} from '@/controllers/posts/beer-posts';
|
||||||
|
|
||||||
interface EditBeerPostRequest extends BeerPostRequest {
|
import EditBeerPostValidationSchema from '@/services/posts/beer-post/schema/EditBeerPostValidationSchema';
|
||||||
body: z.infer<typeof EditBeerPostValidationSchema>;
|
|
||||||
}
|
|
||||||
|
|
||||||
const checkIfBeerPostOwner = async (
|
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
||||||
req: BeerPostRequest,
|
|
||||||
res: NextApiResponse,
|
|
||||||
next: NextHandler,
|
|
||||||
) => {
|
|
||||||
const { user, query } = req;
|
|
||||||
const { id } = query;
|
|
||||||
|
|
||||||
const beerPost = await getBeerPostById(id);
|
|
||||||
|
|
||||||
if (!beerPost) {
|
|
||||||
throw new ServerError('Beer post not found', 404);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (beerPost.postedBy.id !== user!.id) {
|
|
||||||
throw new ServerError('You cannot edit that beer post.', 403);
|
|
||||||
}
|
|
||||||
|
|
||||||
return next();
|
|
||||||
};
|
|
||||||
|
|
||||||
const editBeerPost = async (
|
|
||||||
req: EditBeerPostRequest,
|
|
||||||
res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>,
|
|
||||||
) => {
|
|
||||||
await editBeerPostById({ id: req.query.id, data: req.body });
|
|
||||||
|
|
||||||
res.status(200).json({
|
|
||||||
message: 'Beer post updated successfully',
|
|
||||||
success: true,
|
|
||||||
statusCode: 200,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const deleteBeerPost = async (req: BeerPostRequest, res: NextApiResponse) => {
|
|
||||||
const { id } = req.query;
|
|
||||||
|
|
||||||
const deleted = await deleteBeerPostById({ beerPostId: id });
|
|
||||||
if (!deleted) {
|
|
||||||
throw new ServerError('Beer post not found', 404);
|
|
||||||
}
|
|
||||||
|
|
||||||
res.status(200).json({
|
|
||||||
message: 'Beer post deleted successfully',
|
|
||||||
success: true,
|
|
||||||
statusCode: 200,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const router = createRouter<
|
const router = createRouter<
|
||||||
EditBeerPostRequest,
|
EditBeerPostRequest,
|
||||||
|
|||||||
@@ -1,82 +1,31 @@
|
|||||||
import validateRequest from '@/config/nextConnect/middleware/validateRequest';
|
import validateRequest from '@/config/nextConnect/middleware/validateRequest';
|
||||||
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
||||||
import getBeerPostById from '@/services/BeerPost/getBeerPostById';
|
|
||||||
import { UserExtendedNextApiRequest } from '@/config/auth/types';
|
|
||||||
import { createRouter } from 'next-connect';
|
import { createRouter } from 'next-connect';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
import { NextApiRequest, NextApiResponse } from 'next';
|
import { NextApiResponse } from 'next';
|
||||||
import ServerError from '@/config/util/ServerError';
|
|
||||||
import createBeerPostLike from '@/services/BeerPostLike/createBeerPostLike';
|
|
||||||
import removeBeerPostLikeById from '@/services/BeerPostLike/removeBeerPostLikeById';
|
|
||||||
import findBeerPostLikeById from '@/services/BeerPostLike/findBeerPostLikeById';
|
|
||||||
import getCurrentUser from '@/config/nextConnect/middleware/getCurrentUser';
|
import getCurrentUser from '@/config/nextConnect/middleware/getCurrentUser';
|
||||||
import NextConnectOptions from '@/config/nextConnect/NextConnectOptions';
|
import NextConnectOptions from '@/config/nextConnect/NextConnectOptions';
|
||||||
import getBeerPostLikeCount from '@/services/BeerPostLike/getBeerPostLikeCount';
|
import {
|
||||||
|
sendBeerPostLikeRequest,
|
||||||
const sendLikeRequest = async (
|
getBeerPostLikeCount,
|
||||||
req: UserExtendedNextApiRequest,
|
} from '@/controllers/likes/beer-posts-likes';
|
||||||
res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>,
|
import { LikeRequest } from '@/controllers/likes/types';
|
||||||
) => {
|
|
||||||
const user = req.user!;
|
|
||||||
const id = req.query.id as string;
|
|
||||||
|
|
||||||
const beer = await getBeerPostById(id);
|
|
||||||
if (!beer) {
|
|
||||||
throw new ServerError('Could not find a beer post with that id', 404);
|
|
||||||
}
|
|
||||||
|
|
||||||
const alreadyLiked = await findBeerPostLikeById({
|
|
||||||
beerPostId: beer.id,
|
|
||||||
likedById: user.id,
|
|
||||||
});
|
|
||||||
|
|
||||||
const jsonResponse = {
|
|
||||||
success: true as const,
|
|
||||||
message: '',
|
|
||||||
statusCode: 200 as const,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (alreadyLiked) {
|
|
||||||
await removeBeerPostLikeById({ beerLikeId: alreadyLiked.id });
|
|
||||||
jsonResponse.message = 'Successfully unliked beer post';
|
|
||||||
} else {
|
|
||||||
await createBeerPostLike({ id, user });
|
|
||||||
jsonResponse.message = 'Successfully liked beer post';
|
|
||||||
}
|
|
||||||
|
|
||||||
res.status(200).json(jsonResponse);
|
|
||||||
};
|
|
||||||
|
|
||||||
const getLikeCount = async (
|
|
||||||
req: NextApiRequest,
|
|
||||||
res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>,
|
|
||||||
) => {
|
|
||||||
const id = req.query.id as string;
|
|
||||||
|
|
||||||
const likeCount = await getBeerPostLikeCount({ beerPostId: id });
|
|
||||||
|
|
||||||
res.status(200).json({
|
|
||||||
success: true,
|
|
||||||
message: 'Successfully retrieved like count.',
|
|
||||||
statusCode: 200,
|
|
||||||
payload: { likeCount },
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const router = createRouter<
|
const router = createRouter<
|
||||||
UserExtendedNextApiRequest,
|
LikeRequest,
|
||||||
NextApiResponse<z.infer<typeof APIResponseValidationSchema>>
|
NextApiResponse<z.infer<typeof APIResponseValidationSchema>>
|
||||||
>();
|
>();
|
||||||
|
|
||||||
router.post(
|
router.post(
|
||||||
getCurrentUser,
|
getCurrentUser,
|
||||||
validateRequest({ querySchema: z.object({ id: z.string().cuid() }) }),
|
validateRequest({ querySchema: z.object({ id: z.string().cuid() }) }),
|
||||||
sendLikeRequest,
|
sendBeerPostLikeRequest,
|
||||||
);
|
);
|
||||||
|
|
||||||
router.get(
|
router.get(
|
||||||
validateRequest({ querySchema: z.object({ id: z.string().cuid() }) }),
|
validateRequest({ querySchema: z.object({ id: z.string().cuid() }) }),
|
||||||
getLikeCount,
|
getBeerPostLikeCount,
|
||||||
);
|
);
|
||||||
|
|
||||||
const handler = router.handler(NextConnectOptions);
|
const handler = router.handler(NextConnectOptions);
|
||||||
|
|||||||
@@ -6,24 +6,8 @@ import APIResponseValidationSchema from '@/validation/APIResponseValidationSchem
|
|||||||
import { NextApiResponse } from 'next';
|
import { NextApiResponse } from 'next';
|
||||||
import { createRouter } from 'next-connect';
|
import { createRouter } from 'next-connect';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
import findBeerPostLikeById from '@/services/BeerPostLike/findBeerPostLikeById';
|
|
||||||
|
|
||||||
const checkIfLiked = async (
|
import { checkIfBeerPostIsLiked } from '@/controllers/likes/beer-posts-likes';
|
||||||
req: UserExtendedNextApiRequest,
|
|
||||||
res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>,
|
|
||||||
) => {
|
|
||||||
const user = req.user!;
|
|
||||||
const beerPostId = req.query.id as string;
|
|
||||||
|
|
||||||
const alreadyLiked = await findBeerPostLikeById({ beerPostId, likedById: user.id });
|
|
||||||
|
|
||||||
res.status(200).json({
|
|
||||||
success: true,
|
|
||||||
message: alreadyLiked ? 'Beer post is liked.' : 'Beer post is not liked.',
|
|
||||||
statusCode: 200,
|
|
||||||
payload: { isLiked: !!alreadyLiked },
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const router = createRouter<
|
const router = createRouter<
|
||||||
UserExtendedNextApiRequest,
|
UserExtendedNextApiRequest,
|
||||||
@@ -33,7 +17,7 @@ const router = createRouter<
|
|||||||
router.get(
|
router.get(
|
||||||
getCurrentUser,
|
getCurrentUser,
|
||||||
validateRequest({ querySchema: z.object({ id: z.string().cuid() }) }),
|
validateRequest({ querySchema: z.object({ id: z.string().cuid() }) }),
|
||||||
checkIfLiked,
|
checkIfBeerPostIsLiked,
|
||||||
);
|
);
|
||||||
|
|
||||||
const handler = router.handler(NextConnectOptions);
|
const handler = router.handler(NextConnectOptions);
|
||||||
|
|||||||
@@ -1,52 +1,17 @@
|
|||||||
import NextConnectOptions from '@/config/nextConnect/NextConnectOptions';
|
import NextConnectOptions from '@/config/nextConnect/NextConnectOptions';
|
||||||
import validateRequest from '@/config/nextConnect/middleware/validateRequest';
|
import validateRequest from '@/config/nextConnect/middleware/validateRequest';
|
||||||
import ServerError from '@/config/util/ServerError';
|
import { getBeerPostRecommendations } from '@/controllers/posts/beer-posts';
|
||||||
import getBeerPostById from '@/services/BeerPost/getBeerPostById';
|
import { GetBeerRecommendationsRequest } from '@/controllers/posts/beer-posts/types';
|
||||||
import getBeerRecommendations from '@/services/BeerPost/getBeerRecommendations';
|
|
||||||
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
||||||
import { NextApiRequest, NextApiResponse } from 'next';
|
import { NextApiResponse } from 'next';
|
||||||
import { createRouter } from 'next-connect';
|
import { createRouter } from 'next-connect';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
|
||||||
interface BeerPostRequest extends NextApiRequest {
|
|
||||||
query: { id: string; page_num: string; page_size: string };
|
|
||||||
}
|
|
||||||
|
|
||||||
const router = createRouter<
|
const router = createRouter<
|
||||||
BeerPostRequest,
|
GetBeerRecommendationsRequest,
|
||||||
NextApiResponse<z.infer<typeof APIResponseValidationSchema>>
|
NextApiResponse<z.infer<typeof APIResponseValidationSchema>>
|
||||||
>();
|
>();
|
||||||
|
|
||||||
const getBeerRecommendationsRequest = async (
|
|
||||||
req: BeerPostRequest,
|
|
||||||
res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>,
|
|
||||||
) => {
|
|
||||||
const { id } = req.query;
|
|
||||||
|
|
||||||
const beerPost = await getBeerPostById(id);
|
|
||||||
|
|
||||||
if (!beerPost) {
|
|
||||||
throw new ServerError('Beer post not found', 404);
|
|
||||||
}
|
|
||||||
|
|
||||||
const pageNum = parseInt(req.query.page_num as string, 10);
|
|
||||||
const pageSize = parseInt(req.query.page_size as string, 10);
|
|
||||||
|
|
||||||
const { count, beerRecommendations } = await getBeerRecommendations({
|
|
||||||
beerPost,
|
|
||||||
pageNum,
|
|
||||||
pageSize,
|
|
||||||
});
|
|
||||||
|
|
||||||
res.setHeader('X-Total-Count', count);
|
|
||||||
res.status(200).json({
|
|
||||||
success: true,
|
|
||||||
message: 'Recommendations fetched successfully',
|
|
||||||
statusCode: 200,
|
|
||||||
payload: beerRecommendations,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
router.get(
|
router.get(
|
||||||
validateRequest({
|
validateRequest({
|
||||||
querySchema: z.object({
|
querySchema: z.object({
|
||||||
@@ -55,7 +20,7 @@ router.get(
|
|||||||
page_size: z.string().regex(/^[0-9]+$/),
|
page_size: z.string().regex(/^[0-9]+$/),
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
getBeerRecommendationsRequest,
|
getBeerPostRecommendations,
|
||||||
);
|
);
|
||||||
|
|
||||||
const handler = router.handler(NextConnectOptions);
|
const handler = router.handler(NextConnectOptions);
|
||||||
|
|||||||
@@ -1,41 +1,14 @@
|
|||||||
import { UserExtendedNextApiRequest } from '@/config/auth/types';
|
|
||||||
import validateRequest from '@/config/nextConnect/middleware/validateRequest';
|
import validateRequest from '@/config/nextConnect/middleware/validateRequest';
|
||||||
import { createRouter } from 'next-connect';
|
import { createRouter } from 'next-connect';
|
||||||
import createNewBeerPost from '@/services/BeerPost/createNewBeerPost';
|
|
||||||
import CreateBeerPostValidationSchema from '@/services/BeerPost/schema/CreateBeerPostValidationSchema';
|
import CreateBeerPostValidationSchema from '@/services/posts/beer-post/schema/CreateBeerPostValidationSchema';
|
||||||
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
||||||
import { NextApiResponse } from 'next';
|
import { NextApiResponse } from 'next';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
import NextConnectOptions from '@/config/nextConnect/NextConnectOptions';
|
import NextConnectOptions from '@/config/nextConnect/NextConnectOptions';
|
||||||
import getCurrentUser from '@/config/nextConnect/middleware/getCurrentUser';
|
import getCurrentUser from '@/config/nextConnect/middleware/getCurrentUser';
|
||||||
|
import { createBeerPost } from '@/controllers/posts/beer-posts';
|
||||||
interface CreateBeerPostRequest extends UserExtendedNextApiRequest {
|
import { CreateBeerPostRequest } from '@/controllers/posts/beer-posts/types';
|
||||||
body: z.infer<typeof CreateBeerPostValidationSchema>;
|
|
||||||
}
|
|
||||||
|
|
||||||
const createBeerPost = async (
|
|
||||||
req: CreateBeerPostRequest,
|
|
||||||
res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>,
|
|
||||||
) => {
|
|
||||||
const { name, description, styleId: typeId, abv, ibu, breweryId } = req.body;
|
|
||||||
|
|
||||||
const newBeerPost = await createNewBeerPost({
|
|
||||||
name,
|
|
||||||
description,
|
|
||||||
abv,
|
|
||||||
ibu,
|
|
||||||
styleId: typeId,
|
|
||||||
breweryId,
|
|
||||||
userId: req.user!.id,
|
|
||||||
});
|
|
||||||
|
|
||||||
res.status(201).json({
|
|
||||||
message: 'Beer post created successfully',
|
|
||||||
statusCode: 201,
|
|
||||||
payload: newBeerPost,
|
|
||||||
success: true,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const router = createRouter<
|
const router = createRouter<
|
||||||
CreateBeerPostRequest,
|
CreateBeerPostRequest,
|
||||||
|
|||||||
@@ -1,40 +1,15 @@
|
|||||||
import validateRequest from '@/config/nextConnect/middleware/validateRequest';
|
import validateRequest from '@/config/nextConnect/middleware/validateRequest';
|
||||||
import DBClient from '@/prisma/DBClient';
|
import { getBeerPosts } from '@/controllers/posts/beer-posts';
|
||||||
import getAllBeerPosts from '@/services/BeerPost/getAllBeerPosts';
|
import { GetAllBeerPostsRequest } from '@/controllers/posts/beer-posts/types';
|
||||||
import PaginatedQueryResponseSchema from '@/services/schema/PaginatedQueryResponseSchema';
|
import PaginatedQueryResponseSchema from '@/services/schema/PaginatedQueryResponseSchema';
|
||||||
|
|
||||||
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
||||||
import { NextApiRequest, NextApiResponse } from 'next';
|
import { NextApiResponse } from 'next';
|
||||||
import { createRouter } from 'next-connect';
|
import { createRouter } from 'next-connect';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
|
||||||
interface GetBeerPostsRequest extends NextApiRequest {
|
|
||||||
query: z.infer<typeof PaginatedQueryResponseSchema>;
|
|
||||||
}
|
|
||||||
|
|
||||||
const getBeerPosts = async (
|
|
||||||
req: GetBeerPostsRequest,
|
|
||||||
res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>,
|
|
||||||
) => {
|
|
||||||
const pageNum = parseInt(req.query.page_num, 10);
|
|
||||||
const pageSize = parseInt(req.query.page_size, 10);
|
|
||||||
|
|
||||||
const beerPosts = await getAllBeerPosts({ pageNum, pageSize });
|
|
||||||
|
|
||||||
const beerPostCount = await DBClient.instance.beerPost.count();
|
|
||||||
|
|
||||||
res.setHeader('X-Total-Count', beerPostCount);
|
|
||||||
|
|
||||||
res.status(200).json({
|
|
||||||
message: 'Beer posts retrieved successfully',
|
|
||||||
statusCode: 200,
|
|
||||||
payload: beerPosts,
|
|
||||||
success: true,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const router = createRouter<
|
const router = createRouter<
|
||||||
GetBeerPostsRequest,
|
GetAllBeerPostsRequest,
|
||||||
NextApiResponse<z.infer<typeof APIResponseValidationSchema>>
|
NextApiResponse<z.infer<typeof APIResponseValidationSchema>>
|
||||||
>();
|
>();
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { NextApiRequest, NextApiResponse } from 'next';
|
|||||||
import { createRouter } from 'next-connect';
|
import { createRouter } from 'next-connect';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
import DBClient from '@/prisma/DBClient';
|
import DBClient from '@/prisma/DBClient';
|
||||||
import BeerPostQueryResult from '@/services/BeerPost/schema/BeerPostQueryResult';
|
import BeerPostQueryResult from '@/services/posts/beer-post/schema/BeerPostQueryResult';
|
||||||
|
|
||||||
const SearchSchema = z.object({
|
const SearchSchema = z.object({
|
||||||
search: z.string().min(1),
|
search: z.string().min(1),
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import NextConnectOptions from '@/config/nextConnect/NextConnectOptions';
|
import NextConnectOptions from '@/config/nextConnect/NextConnectOptions';
|
||||||
import validateRequest from '@/config/nextConnect/middleware/validateRequest';
|
import validateRequest from '@/config/nextConnect/middleware/validateRequest';
|
||||||
import DBClient from '@/prisma/DBClient';
|
import { getAllBeersByBeerStyle } from '@/controllers/posts/beer-styles';
|
||||||
import getBeerPostsByBeerStyleId from '@/services/BeerPost/getBeerPostsByBeerStyleId';
|
|
||||||
|
|
||||||
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
||||||
import { NextApiRequest, NextApiResponse } from 'next';
|
import { NextApiRequest, NextApiResponse } from 'next';
|
||||||
@@ -12,31 +11,6 @@ interface GetAllBeersByBeerStyleRequest extends NextApiRequest {
|
|||||||
query: { page_size: string; page_num: string; id: string };
|
query: { page_size: string; page_num: string; id: string };
|
||||||
}
|
}
|
||||||
|
|
||||||
const getAllBeersByBeerStyle = async (
|
|
||||||
req: GetAllBeersByBeerStyleRequest,
|
|
||||||
res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>,
|
|
||||||
) => {
|
|
||||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
||||||
const { page_size, page_num, id } = req.query;
|
|
||||||
|
|
||||||
const beers = await getBeerPostsByBeerStyleId({
|
|
||||||
pageNum: parseInt(page_num, 10),
|
|
||||||
pageSize: parseInt(page_size, 10),
|
|
||||||
styleId: id,
|
|
||||||
});
|
|
||||||
|
|
||||||
const count = await DBClient.instance.beerPost.count({ where: { styleId: id } });
|
|
||||||
|
|
||||||
res.setHeader('X-Total-Count', count);
|
|
||||||
|
|
||||||
res.status(200).json({
|
|
||||||
message: 'Beers fetched successfully',
|
|
||||||
statusCode: 200,
|
|
||||||
payload: beers,
|
|
||||||
success: true,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const router = createRouter<
|
const router = createRouter<
|
||||||
GetAllBeersByBeerStyleRequest,
|
GetAllBeersByBeerStyleRequest,
|
||||||
NextApiResponse<z.infer<typeof APIResponseValidationSchema>>
|
NextApiResponse<z.infer<typeof APIResponseValidationSchema>>
|
||||||
|
|||||||
@@ -1,77 +1,13 @@
|
|||||||
import DBClient from '@/prisma/DBClient';
|
|
||||||
|
|
||||||
import validateRequest from '@/config/nextConnect/middleware/validateRequest';
|
import validateRequest from '@/config/nextConnect/middleware/validateRequest';
|
||||||
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
||||||
import { UserExtendedNextApiRequest } from '@/config/auth/types';
|
|
||||||
import NextConnectOptions from '@/config/nextConnect/NextConnectOptions';
|
import NextConnectOptions from '@/config/nextConnect/NextConnectOptions';
|
||||||
|
|
||||||
import { createRouter } from 'next-connect';
|
import { createRouter } from 'next-connect';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
import getCurrentUser from '@/config/nextConnect/middleware/getCurrentUser';
|
import getCurrentUser from '@/config/nextConnect/middleware/getCurrentUser';
|
||||||
import { NextApiResponse } from 'next';
|
import { NextApiResponse } from 'next';
|
||||||
import CommentQueryResult from '@/services/schema/CommentSchema/CommentQueryResult';
|
|
||||||
import CreateCommentValidationSchema from '@/services/schema/CommentSchema/CreateCommentValidationSchema';
|
import CreateCommentValidationSchema from '@/services/schema/CommentSchema/CreateCommentValidationSchema';
|
||||||
import createNewBeerStyleComment from '@/services/BeerStyleComment/createNewBeerStyleComment';
|
import { createComment, getAll } from '@/controllers/comments/beer-style-comments';
|
||||||
import getAllBeerStyleComments from '@/services/BeerStyleComment/getAllBeerStyleComments';
|
|
||||||
|
|
||||||
interface CreateCommentRequest extends UserExtendedNextApiRequest {
|
|
||||||
body: z.infer<typeof CreateCommentValidationSchema>;
|
|
||||||
query: { id: string };
|
|
||||||
}
|
|
||||||
|
|
||||||
interface GetAllCommentsRequest extends UserExtendedNextApiRequest {
|
|
||||||
query: { id: string; page_size: string; page_num: string };
|
|
||||||
}
|
|
||||||
|
|
||||||
const createComment = async (
|
|
||||||
req: CreateCommentRequest,
|
|
||||||
res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>,
|
|
||||||
) => {
|
|
||||||
const { content, rating } = req.body;
|
|
||||||
|
|
||||||
const newBeerStyleComment: z.infer<typeof CommentQueryResult> =
|
|
||||||
await createNewBeerStyleComment({
|
|
||||||
content,
|
|
||||||
rating,
|
|
||||||
beerStyleId: req.query.id,
|
|
||||||
userId: req.user!.id,
|
|
||||||
});
|
|
||||||
|
|
||||||
res.status(201).json({
|
|
||||||
message: 'Beer comment created successfully',
|
|
||||||
statusCode: 201,
|
|
||||||
payload: newBeerStyleComment,
|
|
||||||
success: true,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const getAll = async (
|
|
||||||
req: GetAllCommentsRequest,
|
|
||||||
res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>,
|
|
||||||
) => {
|
|
||||||
const beerStyleId = req.query.id;
|
|
||||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
||||||
const { page_size, page_num } = req.query;
|
|
||||||
|
|
||||||
const comments = await getAllBeerStyleComments({
|
|
||||||
beerStyleId,
|
|
||||||
pageNum: parseInt(page_num, 10),
|
|
||||||
pageSize: parseInt(page_size, 10),
|
|
||||||
});
|
|
||||||
|
|
||||||
const count = await DBClient.instance.beerStyleComment.count({
|
|
||||||
where: { beerStyleId },
|
|
||||||
});
|
|
||||||
|
|
||||||
res.setHeader('X-Total-Count', count);
|
|
||||||
|
|
||||||
res.status(200).json({
|
|
||||||
message: 'Beer comments fetched successfully',
|
|
||||||
statusCode: 200,
|
|
||||||
payload: comments,
|
|
||||||
success: true,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const router = createRouter<
|
const router = createRouter<
|
||||||
// I don't want to use any, but I can't figure out how to get the types to work
|
// I don't want to use any, but I can't figure out how to get the types to work
|
||||||
|
|||||||
@@ -1,31 +1,12 @@
|
|||||||
import validateRequest from '@/config/nextConnect/middleware/validateRequest';
|
import validateRequest from '@/config/nextConnect/middleware/validateRequest';
|
||||||
import getBeerStyleById from '@/services/BeerStyles/getBeerStyleById';
|
import { getBeerStyle } from '@/controllers/posts/beer-styles';
|
||||||
|
import { GetBeerStyleByIdRequest } from '@/controllers/posts/beer-styles/types';
|
||||||
|
|
||||||
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
||||||
import { NextApiRequest, NextApiResponse } from 'next';
|
import { NextApiResponse } from 'next';
|
||||||
import { createRouter } from 'next-connect';
|
import { createRouter } from 'next-connect';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
|
||||||
interface GetBeerStyleByIdRequest extends NextApiRequest {
|
|
||||||
query: { id: string };
|
|
||||||
}
|
|
||||||
|
|
||||||
const getBeerStyle = async (
|
|
||||||
req: GetBeerStyleByIdRequest,
|
|
||||||
res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>,
|
|
||||||
) => {
|
|
||||||
const { id } = req.query;
|
|
||||||
|
|
||||||
const beerStyle = await getBeerStyleById(id);
|
|
||||||
|
|
||||||
res.status(200).json({
|
|
||||||
message: 'Beer types retrieved successfully',
|
|
||||||
statusCode: 200,
|
|
||||||
payload: beerStyle,
|
|
||||||
success: true,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const router = createRouter<
|
const router = createRouter<
|
||||||
GetBeerStyleByIdRequest,
|
GetBeerStyleByIdRequest,
|
||||||
NextApiResponse<z.infer<typeof APIResponseValidationSchema>>
|
NextApiResponse<z.infer<typeof APIResponseValidationSchema>>
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user