Refactor: further extract controller logic from routers

This commit is contained in:
Aaron William Po
2023-12-04 00:59:13 -05:00
parent da8d5806bc
commit 4b2ce394c1
28 changed files with 325 additions and 308 deletions

38
package-lock.json generated
View File

@@ -20,7 +20,6 @@
"@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",
@@ -2061,6 +2061,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 +2088,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 +2112,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 +2124,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 +2145,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 +2221,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 +2316,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 +2401,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 +2411,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": "*",
@@ -12133,6 +12143,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 +12170,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 +12194,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 +12206,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 +12227,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 +12303,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 +12397,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 +12481,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 +12491,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": "*",

View File

@@ -5,6 +5,7 @@
"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",
@@ -25,7 +26,6 @@
"@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",
"prettier": "^3.0.0",
"prisma": "^5.6.0", "prisma": "^5.6.0",
"tailwindcss": "^3.3.3",
"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"
}, },

View File

@@ -1,16 +1,17 @@
import { UserExtendedNextApiRequest } from "@/config/auth/types"; import { UserExtendedNextApiRequest } from '@/config/auth/types';
import ServerError from "@/config/util/ServerError"; import ServerError from '@/config/util/ServerError';
import getBeerPostById from "@/services/BeerPost/getBeerPostById"; import getBeerPostById from '@/services/BeerPost/getBeerPostById';
import createBeerPostLike from "@/services/BeerPostLike/createBeerPostLike"; import createBeerPostLike from '@/services/BeerPostLike/createBeerPostLike';
import findBeerPostLikeById from "@/services/BeerPostLike/findBeerPostLikeById"; import findBeerPostLikeById from '@/services/BeerPostLike/findBeerPostLikeById';
import getBeerPostLikeCount from "@/services/BeerPostLike/getBeerPostLikeCount"; import getBeerPostLikeCountByBeerPostId from '@/services/BeerPostLike/getBeerPostLikeCount';
import removeBeerPostLikeById from "@/services/BeerPostLike/removeBeerPostLikeById"; import removeBeerPostLikeById from '@/services/BeerPostLike/removeBeerPostLikeById';
import APIResponseValidationSchema from "@/validation/APIResponseValidationSchema"; import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
import { NextApiResponse, NextApiRequest } from "next"; import { NextApiResponse, NextApiRequest } from 'next';
import { z } from "zod"; import { z } from 'zod';
import { LikeRequest } from '../types';
export const sendLikeRequest = async ( export const sendBeerPostLikeRequest = async (
req: UserExtendedNextApiRequest, req: LikeRequest,
res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>, res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>,
) => { ) => {
const user = req.user!; const user = req.user!;
@@ -43,13 +44,13 @@ export const sendLikeRequest = async (
res.status(200).json(jsonResponse); res.status(200).json(jsonResponse);
}; };
export const getLikeCount = async ( export const getBeerPostLikeCount = async (
req: NextApiRequest, req: NextApiRequest,
res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>, res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>,
) => { ) => {
const id = req.query.id as string; const id = req.query.id as string;
const likeCount = await getBeerPostLikeCount({ beerPostId: id }); const likeCount = await getBeerPostLikeCountByBeerPostId({ beerPostId: id });
res.status(200).json({ res.status(200).json({
success: true, success: true,
@@ -59,7 +60,7 @@ export const getLikeCount = async (
}); });
}; };
export const checkIfLiked = async ( export const checkIfBeerPostIsLiked = async (
req: UserExtendedNextApiRequest, req: UserExtendedNextApiRequest,
res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>, res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>,
) => { ) => {

View File

@@ -0,0 +1,76 @@
import ServerError from '@/config/util/ServerError';
import createBeerStyleLike from '@/services/BeerStyleLike/createBeerStyleLike';
import findBeerStyleLikeById from '@/services/BeerStyleLike/findBeerStyleLikeById';
import getBeerStyleLikeCount from '@/services/BeerStyleLike/getBeerStyleLikeCount';
import removeBeerStyleLikeById from '@/services/BeerStyleLike/removeBeerStyleLikeById';
import getBeerStyleById from '@/services/BeerStyles/getBeerStyleById';
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
import { NextApiResponse, NextApiRequest } from 'next';
import { z } from 'zod';
import { UserExtendedNextApiRequest } from '@/config/auth/types';
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 getBeerStyleById(id);
if (!beerStyle) {
throw new ServerError('Could not find a beer style with that id.', 404);
}
const beerStyleLike = await findBeerStyleLikeById({
beerStyleId: beerStyle.id,
likedById: user.id,
});
if (beerStyleLike) {
await removeBeerStyleLikeById({ beerStyleLikeId: beerStyleLike.id });
res.status(200).json({
message: 'Successfully unliked beer style.',
success: true,
statusCode: 200,
});
} else {
await createBeerStyleLike({ beerStyleId: beerStyle.id, user });
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 getBeerStyleLikeCount({ 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 findBeerStyleLikeById({ 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 },
});
};

View File

@@ -0,0 +1,5 @@
import { UserExtendedNextApiRequest } from '@/config/auth/types';
export interface LikeRequest extends UserExtendedNextApiRequest {
query: { id: string };
}

View File

@@ -0,0 +1,116 @@
import { NextApiResponse } from 'next';
import { z } from 'zod';
import DBClient from '@/prisma/DBClient';
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
import getBeerStyleById from '@/services/BeerStyles/getBeerStyleById';
import getBeerPostsByBeerStyleId from '@/services/BeerPost/getBeerPostsByBeerStyleId';
import getAllBeerStyles from '@/services/BeerStyles/getAllBeerStyles';
import ServerError from '@/config/util/ServerError';
import {
CreateBeerStyleRequest,
GetAllBeersByBeerStyleRequest,
GetBeerStyleByIdRequest,
} from './types';
import { GetAllPostsRequest } from '../types';
export 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 style retrieved successfully.',
statusCode: 200,
payload: beerStyle,
success: true,
});
};
export 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 with style id ${id} retrieved successfully.`,
statusCode: 200,
payload: beers,
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 = await getAllBeerStyles({ pageNum, pageSize });
const beerStyleCount = await DBClient.instance.beerStyle.count();
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 glassware = await DBClient.instance.glassware.findUnique({
where: { id: glasswareId },
select: { id: true },
});
if (!glassware) {
throw new ServerError('Glassware not found.', 404);
}
const beerStyle = await DBClient.instance.beerStyle.create({
data: {
abvRange,
description,
glassware: { connect: { id: glasswareId } },
ibuRange,
name,
postedBy: { connect: { id: user.id } },
},
});
res.json({
message: 'Beer style created successfully.',
statusCode: 200,
payload: beerStyle,
success: true,
});
};

View File

@@ -0,0 +1,17 @@
import { NextApiRequest } from 'next';
import { GetAllPostsRequest } from '@/controllers/posts/types';
import { UserExtendedNextApiRequest } from '@/config/auth/types';
import { z } from 'zod';
import CreateBeerStyleValidationSchema from '@/services/BeerStyles/schema/CreateBeerStyleValidationSchema';
export interface GetBeerStyleByIdRequest extends NextApiRequest {
query: { id: string };
}
export interface GetAllBeersByBeerStyleRequest extends GetAllPostsRequest {
query: { page_size: string; page_num: string; id: string };
}
export interface CreateBeerStyleRequest extends UserExtendedNextApiRequest {
body: z.infer<typeof CreateBeerStyleValidationSchema>;
}

View File

@@ -0,0 +1,5 @@
import { NextApiRequest } from 'next';
export interface GetAllPostsRequest extends NextApiRequest {
query: { page_size: string; page_num: string };
}

View File

@@ -9,8 +9,8 @@ import { z } from 'zod';
import validateRequest from '@/config/nextConnect/middleware/validateRequest'; import validateRequest from '@/config/nextConnect/middleware/validateRequest';
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 { UploadBeerPostImagesRequest } from '@/controllers/beerImages/types'; import { UploadBeerPostImagesRequest } from '@/controllers/images/beerImages/types';
import { processBeerImageData } from '@/controllers/beerImages'; import { processBeerImageData } from '@/controllers/images/beerImages';
const router = createRouter< const router = createRouter<
UploadBeerPostImagesRequest, UploadBeerPostImagesRequest,

View File

@@ -6,12 +6,12 @@ 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 { EditBeerPostRequest } from '@/controllers/beerPosts/types'; import { EditBeerPostRequest } from '@/controllers/posts/beerPosts/types';
import { import {
checkIfBeerPostOwner, checkIfBeerPostOwner,
editBeerPost, editBeerPost,
deleteBeerPost, deleteBeerPost,
} from '@/controllers/beerPosts'; } from '@/controllers/posts/beerPosts';
import EditBeerPostValidationSchema from '@/services/BeerPost/schema/EditBeerPostValidationSchema'; import EditBeerPostValidationSchema from '@/services/BeerPost/schema/EditBeerPostValidationSchema';

View File

@@ -1,27 +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 { UserExtendedNextApiRequest } from '@/config/auth/types';
import { createRouter } from 'next-connect'; import { createRouter } from 'next-connect';
import { z } from 'zod'; import { z } from 'zod';
import { NextApiResponse } from 'next'; import { NextApiResponse } from 'next';
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 { sendLikeRequest, getLikeCount } from '@/controllers/beerPostLikes'; import {
sendBeerPostLikeRequest,
getBeerPostLikeCount,
} from '@/controllers/likes/beerPostLikes';
import { LikeRequest } from '@/controllers/likes/types';
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);

View File

@@ -7,7 +7,7 @@ import { NextApiResponse } from 'next';
import { createRouter } from 'next-connect'; import { createRouter } from 'next-connect';
import { z } from 'zod'; import { z } from 'zod';
import { checkIfLiked } from '@/controllers/beerPostLikes'; import { checkIfBeerPostIsLiked } from '@/controllers/likes/beerPostLikes';
const router = createRouter< const router = createRouter<
UserExtendedNextApiRequest, UserExtendedNextApiRequest,
@@ -17,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);

View File

@@ -1,7 +1,7 @@
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 { getBeerPostRecommendations } from '@/controllers/beerPosts'; import { getBeerPostRecommendations } from '@/controllers/posts/beerPosts';
import { GetBeerRecommendationsRequest } from '@/controllers/beerPosts/types'; import { GetBeerRecommendationsRequest } from '@/controllers/posts/beerPosts/types';
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema'; import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
import { NextApiResponse } from 'next'; import { NextApiResponse } from 'next';
import { createRouter } from 'next-connect'; import { createRouter } from 'next-connect';

View File

@@ -7,8 +7,8 @@ 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/beerPosts'; import { createBeerPost } from '@/controllers/posts/beerPosts';
import { CreateBeerPostRequest } from '@/controllers/beerPosts/types'; import { CreateBeerPostRequest } from '@/controllers/posts/beerPosts/types';
const router = createRouter< const router = createRouter<
CreateBeerPostRequest, CreateBeerPostRequest,

View File

@@ -1,6 +1,6 @@
import validateRequest from '@/config/nextConnect/middleware/validateRequest'; import validateRequest from '@/config/nextConnect/middleware/validateRequest';
import { getBeerPosts } from '@/controllers/beerPosts'; import { getBeerPosts } from '@/controllers/posts/beerPosts';
import { GetAllBeerPostsRequest } from '@/controllers/beerPosts/types'; import { GetAllBeerPostsRequest } from '@/controllers/posts/beerPosts/types';
import PaginatedQueryResponseSchema from '@/services/schema/PaginatedQueryResponseSchema'; import PaginatedQueryResponseSchema from '@/services/schema/PaginatedQueryResponseSchema';
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema'; import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';

View File

@@ -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/beerStyles';
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>>

View File

@@ -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/beerStyles';
import { GetBeerStyleByIdRequest } from '@/controllers/posts/beerStyles/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>>

View File

@@ -1,84 +1,32 @@
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 APIResponseValidationSchema from '@/validation/APIResponseValidationSchema'; import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
import validateRequest from '@/config/nextConnect/middleware/validateRequest'; import validateRequest from '@/config/nextConnect/middleware/validateRequest';
import { UserExtendedNextApiRequest } from '@/config/auth/types';
import ServerError from '@/config/util/ServerError';
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 {
import getBeerStyleById from '@/services/BeerStyles/getBeerStyleById'; getBeerStyleLikeCountRequest,
import findBeerStyleLikeById from '@/services/BeerStyleLike/findBeerStyleLikeById'; sendBeerStyleLikeRequest,
import getBeerStyleLikeCount from '@/services/BeerStyleLike/getBeerStyleLikeCount'; } from '@/controllers/likes/beerStyleLikes';
import createBeerStyleLike from '@/services/BeerStyleLike/createBeerStyleLike'; import { LikeRequest } from '@/controllers/likes/types';
import removeBeerStyleLikeById from '@/services/BeerStyleLike/removeBeerStyleLikeById';
const sendLikeRequest = async (
req: UserExtendedNextApiRequest,
res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>,
) => {
const user = req.user!;
const id = req.query.id as string;
const beerStyle = await getBeerStyleById(id);
if (!beerStyle) {
throw new ServerError('Could not find a beer style with that id', 404);
}
const alreadyLiked = await findBeerStyleLikeById({
beerStyleId: beerStyle.id,
likedById: user.id,
});
const jsonResponse = {
success: true as const,
message: '',
statusCode: 200 as const,
};
if (alreadyLiked) {
await removeBeerStyleLikeById({ beerStyleLikeId: alreadyLiked.id });
jsonResponse.message = 'Successfully unliked beer style.';
} else {
await createBeerStyleLike({ beerStyleId: beerStyle.id, user });
jsonResponse.message = 'Successfully liked beer style.';
}
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 getBeerStyleLikeCount({ beerStyleId: 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, sendBeerStyleLikeRequest,
); );
router.get( router.get(
validateRequest({ querySchema: z.object({ id: z.string().cuid() }) }), validateRequest({ querySchema: z.object({ id: z.string().cuid() }) }),
getLikeCount, getBeerStyleLikeCountRequest,
); );
const handler = router.handler(NextConnectOptions); const handler = router.handler(NextConnectOptions);

View File

@@ -6,37 +6,7 @@ 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 DBClient from '@/prisma/DBClient'; import { checkIfBeerStyleIsLiked } from '@/controllers/likes/beerStyleLikes';
interface FindBeerStyleLikeByIdArgs {
beerStyleId: string;
likedById: string;
}
const findBeerStyleLikeById = async ({
beerStyleId,
likedById,
}: FindBeerStyleLikeByIdArgs) => {
return DBClient.instance.beerStyleLike.findFirst({
where: { beerStyleId, likedById },
});
};
const checkIfLiked = async (
req: UserExtendedNextApiRequest,
res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>,
) => {
const user = req.user!;
const beerStyleId = req.query.id as string;
const alreadyLiked = await findBeerStyleLikeById({ 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 },
});
};
const router = createRouter< const router = createRouter<
UserExtendedNextApiRequest, UserExtendedNextApiRequest,
@@ -46,7 +16,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, checkIfBeerStyleIsLiked,
); );
const handler = router.handler(NextConnectOptions); const handler = router.handler(NextConnectOptions);

View File

@@ -1,97 +1,19 @@
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 ServerError from '@/config/util/ServerError'; import { createBeerStyle } from '@/controllers/posts/beerStyles';
import DBClient from '@/prisma/DBClient'; import { CreateBeerStyleRequest } from '@/controllers/posts/beerStyles/types';
import CreateBeerStyleValidationSchema from '@/services/BeerStyles/schema/CreateBeerStyleValidationSchema';
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';
const BeerStyleValidationSchema = z.object({
id: z.string().cuid(),
name: z.string(),
postedBy: z.object({
id: z.string().cuid(),
username: z.string(),
}),
glassware: z.object({
id: z.string().cuid(),
name: z.string(),
description: z.string(),
}),
description: z.string(),
createdAt: z.date(),
updatedAt: z.date().nullable(),
});
const CreateBeerStyleValidationSchema = BeerStyleValidationSchema.omit({
id: true,
postedBy: true,
createdAt: true,
updatedAt: true,
glassware: true,
}).extend({
glasswareId: z.string().cuid(),
});
interface CreateBeerStyleRequest extends UserExtendedNextApiRequest {
body: z.infer<typeof CreateBeerStyleValidationSchema>;
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
interface GetBeerStyleRequest extends NextApiRequest {
query: {
id: string;
};
}
const createBeerStyle = async (
req: CreateBeerStyleRequest,
res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>,
) => {
const user = req.user!;
const { name, description, glasswareId } = req.body;
const glassware = await DBClient.instance.glassware.findUnique({
where: { id: glasswareId },
});
if (!glassware) {
throw new ServerError('Glassware not found', 404);
}
const newBeerStyle = await DBClient.instance.beerStyle.create({
data: {
description,
name,
postedBy: { connect: { id: user.id } },
glassware: { connect: { id: glassware.id } },
},
select: {
id: true,
name: true,
postedBy: { select: { id: true, username: true } },
createdAt: true,
updatedAt: true,
},
});
res.status(200).json({
message: 'Beer posts retrieved successfully',
statusCode: 200,
payload: newBeerStyle,
success: true,
});
};
const router = createRouter< const router = createRouter<
CreateBeerStyleRequest, CreateBeerStyleRequest,
NextApiResponse<z.infer<typeof APIResponseValidationSchema>> NextApiResponse<z.infer<typeof APIResponseValidationSchema>>
>(); >();
router.get( router.post(
validateRequest({ bodySchema: CreateBeerStyleValidationSchema }), validateRequest({ bodySchema: CreateBeerStyleValidationSchema }),
getCurrentUser, getCurrentUser,
createBeerStyle, createBeerStyle,

View File

@@ -1,39 +1,16 @@
import validateRequest from '@/config/nextConnect/middleware/validateRequest'; import validateRequest from '@/config/nextConnect/middleware/validateRequest';
import DBClient from '@/prisma/DBClient'; import { getBeerStyles } from '@/controllers/posts/beerStyles';
import getAllBeerStyles from '@/services/BeerStyles/getAllBeerStyles'; import { GetAllPostsRequest } from '@/controllers/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 GetBeerStylesRequest extends NextApiRequest {
query: z.infer<typeof PaginatedQueryResponseSchema>;
}
const getBeerStyles = async (
req: GetBeerStylesRequest,
res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>,
) => {
const pageNum = parseInt(req.query.page_num, 10);
const pageSize = parseInt(req.query.page_size, 10);
const beerStyles = await getAllBeerStyles({ pageNum, pageSize });
const beerStyleCount = await DBClient.instance.beerStyle.count();
res.setHeader('X-Total-Count', beerStyleCount);
res.status(200).json({
message: 'Beer types retrieved successfully',
statusCode: 200,
payload: beerStyles,
success: true,
});
};
const router = createRouter< const router = createRouter<
GetBeerStylesRequest, GetAllPostsRequest,
NextApiResponse<z.infer<typeof APIResponseValidationSchema>> NextApiResponse<z.infer<typeof APIResponseValidationSchema>>
>(); >();

View File

@@ -8,9 +8,9 @@ const sendUpdateUserAvatarRequest = async ({
userId, userId,
}: UpdateProfileRequestParams) => { }: UpdateProfileRequestParams) => {
const formData = new FormData(); const formData = new FormData();
formData.append('file', file); formData.append('image', file);
const response = await fetch(`/api/users/${userId}/`, { const response = await fetch(`/api/users/${userId}/profile/update-avatar`, {
method: 'PUT', method: 'PUT',
body: formData, body: formData,
}); });

View File

@@ -8,7 +8,7 @@ interface GetBeerPostsByBeerStyleIdArgs {
pageNum: number; pageNum: number;
} }
const getBeerPostsByBeerStyleId = async ({ const getAllBeerPostsByBreweryId = async ({
pageNum, pageNum,
pageSize, pageSize,
breweryId, breweryId,
@@ -44,4 +44,4 @@ const getBeerPostsByBeerStyleId = async ({
return beers; return beers;
}; };
export default getBeerPostsByBeerStyleId; export default getAllBeerPostsByBreweryId;

View File

@@ -1,6 +1,6 @@
import DBClient from '@/prisma/DBClient'; import DBClient from '@/prisma/DBClient';
const getBeerPostLikeCount = async ({ beerPostId }: { beerPostId: string }) => const getBeerPostLikeCountByBeerPostId = async ({ beerPostId }: { beerPostId: string }) =>
DBClient.instance.beerPostLike.count({ where: { beerPostId } }); DBClient.instance.beerPostLike.count({ where: { beerPostId } });
export default getBeerPostLikeCount; export default getBeerPostLikeCountByBeerPostId;