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/render": "^0.0.9",
"@react-email/tailwind": "^0.0.12",
"@types/express": "^4.17.21",
"@vercel/analytics": "^1.1.0",
"argon2": "^0.31.1",
"cloudinary": "^1.41.0",
@@ -54,6 +53,7 @@
"devDependencies": {
"@faker-js/faker": "^8.3.1",
"@types/cookie": "^0.5.1",
"@types/express": "^4.17.21",
"@types/jsonwebtoken": "^9.0.2",
"@types/lodash": "^4.14.195",
"@types/mapbox__mapbox-sdk": "^0.13.4",
@@ -2061,6 +2061,7 @@
"version": "1.19.3",
"resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.3.tgz",
"integrity": "sha512-oyl4jvAfTGX9Bt6Or4H9ni1Z447/tQuxnZsytsCaExKlmJiU8sFgnIBRzJUpKwB5eWn9HuBYlUlVA74q/yN0eQ==",
"dev": true,
"dependencies": {
"@types/connect": "*",
"@types/node": "*"
@@ -2087,6 +2088,7 @@
"version": "3.4.36",
"resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.36.tgz",
"integrity": "sha512-P63Zd/JUGq+PdrM1lv0Wv5SBYeA2+CORvbrXbngriYY0jzLUWfQMQQxOhjONEz/wlHOAxOdY7CY65rgQdTjq2w==",
"dev": true,
"dependencies": {
"@types/node": "*"
}
@@ -2110,6 +2112,7 @@
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz",
"integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==",
"dev": true,
"dependencies": {
"@types/body-parser": "*",
"@types/express-serve-static-core": "^4.17.33",
@@ -2121,6 +2124,7 @@
"version": "4.17.37",
"resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.37.tgz",
"integrity": "sha512-ZohaCYTgGFcOP7u6aJOhY9uIZQgZ2vxC2yWoArY+FeDXlqeH66ZVBjgvg+RLVAS/DWNq4Ap9ZXu1+SUQiiWYMg==",
"dev": true,
"dependencies": {
"@types/node": "*",
"@types/qs": "*",
@@ -2141,7 +2145,8 @@
"node_modules/@types/http-errors": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.2.tgz",
"integrity": "sha512-lPG6KlZs88gef6aD85z3HNkztpj7w2R7HmR3gygjfXCQmsLloWNARFkMuzKiiY8FGdh1XDpgBdrSf4aKDiA7Kg=="
"integrity": "sha512-lPG6KlZs88gef6aD85z3HNkztpj7w2R7HmR3gygjfXCQmsLloWNARFkMuzKiiY8FGdh1XDpgBdrSf4aKDiA7Kg==",
"dev": true
},
"node_modules/@types/json-schema": {
"version": "7.0.13",
@@ -2216,7 +2221,8 @@
"node_modules/@types/mime": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.3.tgz",
"integrity": "sha512-Ys+/St+2VF4+xuY6+kDIXGxbNRO0mesVg0bbxEfB97Od1Vjpjx9KD1qxs64Gcb3CWPirk9Xe+PT4YiiHQ9T+eg=="
"integrity": "sha512-Ys+/St+2VF4+xuY6+kDIXGxbNRO0mesVg0bbxEfB97Od1Vjpjx9KD1qxs64Gcb3CWPirk9Xe+PT4YiiHQ9T+eg==",
"dev": true
},
"node_modules/@types/minimist": {
"version": "1.2.3",
@@ -2310,12 +2316,14 @@
"node_modules/@types/qs": {
"version": "6.9.8",
"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": {
"version": "1.2.5",
"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": {
"version": "18.2.25",
@@ -2393,6 +2401,7 @@
"version": "0.17.2",
"resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.2.tgz",
"integrity": "sha512-aAG6yRf6r0wQ29bkS+x97BIs64ZLxeE/ARwyS6wrldMm3C1MdKwCcnnEwMC1slI8wuxJOpiUH9MioC0A0i+GJw==",
"dev": true,
"dependencies": {
"@types/mime": "^1",
"@types/node": "*"
@@ -2402,6 +2411,7 @@
"version": "1.15.3",
"resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.3.tgz",
"integrity": "sha512-yVRvFsEMrv7s0lGhzrggJjNOSmZCdgCjw9xWrPr/kNNLp6FaDfMC1KaYl3TSJ0c58bECwNBMoQrZJ8hA8E1eFg==",
"dev": true,
"dependencies": {
"@types/http-errors": "*",
"@types/mime": "*",
@@ -12133,6 +12143,7 @@
"version": "1.19.3",
"resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.3.tgz",
"integrity": "sha512-oyl4jvAfTGX9Bt6Or4H9ni1Z447/tQuxnZsytsCaExKlmJiU8sFgnIBRzJUpKwB5eWn9HuBYlUlVA74q/yN0eQ==",
"dev": true,
"requires": {
"@types/connect": "*",
"@types/node": "*"
@@ -12159,6 +12170,7 @@
"version": "3.4.36",
"resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.36.tgz",
"integrity": "sha512-P63Zd/JUGq+PdrM1lv0Wv5SBYeA2+CORvbrXbngriYY0jzLUWfQMQQxOhjONEz/wlHOAxOdY7CY65rgQdTjq2w==",
"dev": true,
"requires": {
"@types/node": "*"
}
@@ -12182,6 +12194,7 @@
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz",
"integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==",
"dev": true,
"requires": {
"@types/body-parser": "*",
"@types/express-serve-static-core": "^4.17.33",
@@ -12193,6 +12206,7 @@
"version": "4.17.37",
"resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.37.tgz",
"integrity": "sha512-ZohaCYTgGFcOP7u6aJOhY9uIZQgZ2vxC2yWoArY+FeDXlqeH66ZVBjgvg+RLVAS/DWNq4Ap9ZXu1+SUQiiWYMg==",
"dev": true,
"requires": {
"@types/node": "*",
"@types/qs": "*",
@@ -12213,7 +12227,8 @@
"@types/http-errors": {
"version": "2.0.2",
"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": {
"version": "7.0.13",
@@ -12288,7 +12303,8 @@
"@types/mime": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.3.tgz",
"integrity": "sha512-Ys+/St+2VF4+xuY6+kDIXGxbNRO0mesVg0bbxEfB97Od1Vjpjx9KD1qxs64Gcb3CWPirk9Xe+PT4YiiHQ9T+eg=="
"integrity": "sha512-Ys+/St+2VF4+xuY6+kDIXGxbNRO0mesVg0bbxEfB97Od1Vjpjx9KD1qxs64Gcb3CWPirk9Xe+PT4YiiHQ9T+eg==",
"dev": true
},
"@types/minimist": {
"version": "1.2.3",
@@ -12381,12 +12397,14 @@
"@types/qs": {
"version": "6.9.8",
"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": {
"version": "1.2.5",
"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": {
"version": "18.2.25",
@@ -12463,6 +12481,7 @@
"version": "0.17.2",
"resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.2.tgz",
"integrity": "sha512-aAG6yRf6r0wQ29bkS+x97BIs64ZLxeE/ARwyS6wrldMm3C1MdKwCcnnEwMC1slI8wuxJOpiUH9MioC0A0i+GJw==",
"dev": true,
"requires": {
"@types/mime": "^1",
"@types/node": "*"
@@ -12472,6 +12491,7 @@
"version": "1.15.3",
"resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.3.tgz",
"integrity": "sha512-yVRvFsEMrv7s0lGhzrggJjNOSmZCdgCjw9xWrPr/kNNLp6FaDfMC1KaYl3TSJ0c58bECwNBMoQrZJ8hA8E1eFg==",
"dev": true,
"requires": {
"@types/http-errors": "*",
"@types/mime": "*",

View File

@@ -5,6 +5,7 @@
"scripts": {
"dev": "next dev",
"build": "next build",
"prestart": "npm run build",
"start": "next start",
"lint": "next lint",
"clear-db": "npx ts-node ./src/prisma/seed/clear/index.ts",
@@ -25,7 +26,6 @@
"@react-email/components": "^0.0.11",
"@react-email/render": "^0.0.9",
"@react-email/tailwind": "^0.0.12",
"@types/express": "^4.17.21",
"@vercel/analytics": "^1.1.0",
"argon2": "^0.31.1",
"cloudinary": "^1.41.0",
@@ -36,13 +36,12 @@
"lodash": "^4.17.21",
"mapbox-gl": "^2.15.0",
"multer": "^1.4.5-lts.1",
"next": "^14.0.3",
"next-cloudinary": "^5.10.0",
"next-connect": "^1.0.0-next.3",
"passport": "^0.6.0",
"next": "^14.0.3",
"passport-local": "^1.0.0",
"passport": "^0.6.0",
"pino": "^8.14.1",
"react": "^18.2.0",
"react-daisyui": "^4.1.2",
"react-dom": "^18.2.0",
"react-email": "^1.9.5",
@@ -52,6 +51,7 @@
"react-intersection-observer": "^9.5.2",
"react-map-gl": "^7.1.2",
"react-responsive-carousel": "^3.2.23",
"react": "^18.2.0",
"swr": "^2.2.0",
"theme-change": "^2.5.0",
"zod": "^3.21.4"
@@ -59,34 +59,35 @@
"devDependencies": {
"@faker-js/faker": "^8.3.1",
"@types/cookie": "^0.5.1",
"@types/express": "^4.17.21",
"@types/jsonwebtoken": "^9.0.2",
"@types/lodash": "^4.14.195",
"@types/mapbox__mapbox-sdk": "^0.13.4",
"@types/multer": "^1.4.7",
"@types/node": "^20.4.2",
"@types/passport-local": "^1.0.35",
"@types/react": "^18.2.15",
"@types/react-dom": "^18.2.7",
"@types/react": "^18.2.15",
"@types/sparkpost": "^2.1.5",
"@vercel/fetch": "^7.0.0",
"autoprefixer": "^10.4.14",
"daisyui": "^3.9.2",
"dotenv-cli": "^7.2.1",
"eslint": "^8.51.0",
"eslint-config-airbnb-base": "15.0.0",
"eslint-config-airbnb-typescript": "17.1.0",
"eslint-config-next": "^13.5.4",
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-react": "^7.33.2",
"eslint": "^8.51.0",
"generate-password": "^1.7.1",
"onchange": "^7.1.0",
"postcss": "^8.4.26",
"prettier": "^3.0.0",
"prettier-plugin-jsdoc": "^1.0.2",
"prettier-plugin-tailwindcss": "^0.5.7",
"prettier": "^3.0.0",
"prisma": "^5.6.0",
"tailwindcss": "^3.3.3",
"tailwindcss-animate": "^1.0.6",
"tailwindcss": "^3.3.3",
"ts-node": "^10.9.1",
"typescript": "^5.3.2"
},

View File

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

View File

@@ -6,12 +6,12 @@ import getCurrentUser from '@/config/nextConnect/middleware/getCurrentUser';
import validateRequest from '@/config/nextConnect/middleware/validateRequest';
import NextConnectOptions from '@/config/nextConnect/NextConnectOptions';
import { EditBeerPostRequest } from '@/controllers/beerPosts/types';
import { EditBeerPostRequest } from '@/controllers/posts/beerPosts/types';
import {
checkIfBeerPostOwner,
editBeerPost,
deleteBeerPost,
} from '@/controllers/beerPosts';
} from '@/controllers/posts/beerPosts';
import EditBeerPostValidationSchema from '@/services/BeerPost/schema/EditBeerPostValidationSchema';

View File

@@ -1,27 +1,31 @@
import validateRequest from '@/config/nextConnect/middleware/validateRequest';
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
import { UserExtendedNextApiRequest } from '@/config/auth/types';
import { createRouter } from 'next-connect';
import { z } from 'zod';
import { NextApiResponse } from 'next';
import getCurrentUser from '@/config/nextConnect/middleware/getCurrentUser';
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<
UserExtendedNextApiRequest,
LikeRequest,
NextApiResponse<z.infer<typeof APIResponseValidationSchema>>
>();
router.post(
getCurrentUser,
validateRequest({ querySchema: z.object({ id: z.string().cuid() }) }),
sendLikeRequest,
sendBeerPostLikeRequest,
);
router.get(
validateRequest({ querySchema: z.object({ id: z.string().cuid() }) }),
getLikeCount,
getBeerPostLikeCount,
);
const handler = router.handler(NextConnectOptions);

View File

@@ -7,7 +7,7 @@ import { NextApiResponse } from 'next';
import { createRouter } from 'next-connect';
import { z } from 'zod';
import { checkIfLiked } from '@/controllers/beerPostLikes';
import { checkIfBeerPostIsLiked } from '@/controllers/likes/beerPostLikes';
const router = createRouter<
UserExtendedNextApiRequest,
@@ -17,7 +17,7 @@ const router = createRouter<
router.get(
getCurrentUser,
validateRequest({ querySchema: z.object({ id: z.string().cuid() }) }),
checkIfLiked,
checkIfBeerPostIsLiked,
);
const handler = router.handler(NextConnectOptions);

View File

@@ -1,7 +1,7 @@
import NextConnectOptions from '@/config/nextConnect/NextConnectOptions';
import validateRequest from '@/config/nextConnect/middleware/validateRequest';
import { getBeerPostRecommendations } from '@/controllers/beerPosts';
import { GetBeerRecommendationsRequest } from '@/controllers/beerPosts/types';
import { getBeerPostRecommendations } from '@/controllers/posts/beerPosts';
import { GetBeerRecommendationsRequest } from '@/controllers/posts/beerPosts/types';
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
import { NextApiResponse } from 'next';
import { createRouter } from 'next-connect';

View File

@@ -7,8 +7,8 @@ import { NextApiResponse } from 'next';
import { z } from 'zod';
import NextConnectOptions from '@/config/nextConnect/NextConnectOptions';
import getCurrentUser from '@/config/nextConnect/middleware/getCurrentUser';
import { createBeerPost } from '@/controllers/beerPosts';
import { CreateBeerPostRequest } from '@/controllers/beerPosts/types';
import { createBeerPost } from '@/controllers/posts/beerPosts';
import { CreateBeerPostRequest } from '@/controllers/posts/beerPosts/types';
const router = createRouter<
CreateBeerPostRequest,

View File

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

View File

@@ -1,7 +1,6 @@
import NextConnectOptions from '@/config/nextConnect/NextConnectOptions';
import validateRequest from '@/config/nextConnect/middleware/validateRequest';
import DBClient from '@/prisma/DBClient';
import getBeerPostsByBeerStyleId from '@/services/BeerPost/getBeerPostsByBeerStyleId';
import { getAllBeersByBeerStyle } from '@/controllers/posts/beerStyles';
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
import { NextApiRequest, NextApiResponse } from 'next';
@@ -12,31 +11,6 @@ interface GetAllBeersByBeerStyleRequest extends NextApiRequest {
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<
GetAllBeersByBeerStyleRequest,
NextApiResponse<z.infer<typeof APIResponseValidationSchema>>

View File

@@ -1,31 +1,12 @@
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 { NextApiRequest, NextApiResponse } from 'next';
import { NextApiResponse } from 'next';
import { createRouter } from 'next-connect';
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<
GetBeerStyleByIdRequest,
NextApiResponse<z.infer<typeof APIResponseValidationSchema>>

View File

@@ -1,84 +1,32 @@
import { createRouter } from 'next-connect';
import { z } from 'zod';
import { NextApiRequest, NextApiResponse } from 'next';
import { NextApiResponse } from 'next';
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
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 NextConnectOptions from '@/config/nextConnect/NextConnectOptions';
import getBeerStyleById from '@/services/BeerStyles/getBeerStyleById';
import findBeerStyleLikeById from '@/services/BeerStyleLike/findBeerStyleLikeById';
import getBeerStyleLikeCount from '@/services/BeerStyleLike/getBeerStyleLikeCount';
import createBeerStyleLike from '@/services/BeerStyleLike/createBeerStyleLike';
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 },
});
};
import {
getBeerStyleLikeCountRequest,
sendBeerStyleLikeRequest,
} from '@/controllers/likes/beerStyleLikes';
import { LikeRequest } from '@/controllers/likes/types';
const router = createRouter<
UserExtendedNextApiRequest,
LikeRequest,
NextApiResponse<z.infer<typeof APIResponseValidationSchema>>
>();
router.post(
getCurrentUser,
validateRequest({ querySchema: z.object({ id: z.string().cuid() }) }),
sendLikeRequest,
sendBeerStyleLikeRequest,
);
router.get(
validateRequest({ querySchema: z.object({ id: z.string().cuid() }) }),
getLikeCount,
getBeerStyleLikeCountRequest,
);
const handler = router.handler(NextConnectOptions);

View File

@@ -6,37 +6,7 @@ import APIResponseValidationSchema from '@/validation/APIResponseValidationSchem
import { NextApiResponse } from 'next';
import { createRouter } from 'next-connect';
import { z } from 'zod';
import DBClient from '@/prisma/DBClient';
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 },
});
};
import { checkIfBeerStyleIsLiked } from '@/controllers/likes/beerStyleLikes';
const router = createRouter<
UserExtendedNextApiRequest,
@@ -46,7 +16,7 @@ const router = createRouter<
router.get(
getCurrentUser,
validateRequest({ querySchema: z.object({ id: z.string().cuid() }) }),
checkIfLiked,
checkIfBeerStyleIsLiked,
);
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 validateRequest from '@/config/nextConnect/middleware/validateRequest';
import ServerError from '@/config/util/ServerError';
import DBClient from '@/prisma/DBClient';
import { createBeerStyle } from '@/controllers/posts/beerStyles';
import { CreateBeerStyleRequest } from '@/controllers/posts/beerStyles/types';
import CreateBeerStyleValidationSchema from '@/services/BeerStyles/schema/CreateBeerStyleValidationSchema';
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
import { NextApiRequest, NextApiResponse } from 'next';
import { NextApiResponse } from 'next';
import { createRouter } from 'next-connect';
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<
CreateBeerStyleRequest,
NextApiResponse<z.infer<typeof APIResponseValidationSchema>>
>();
router.get(
router.post(
validateRequest({ bodySchema: CreateBeerStyleValidationSchema }),
getCurrentUser,
createBeerStyle,

View File

@@ -1,39 +1,16 @@
import validateRequest from '@/config/nextConnect/middleware/validateRequest';
import DBClient from '@/prisma/DBClient';
import getAllBeerStyles from '@/services/BeerStyles/getAllBeerStyles';
import { getBeerStyles } from '@/controllers/posts/beerStyles';
import { GetAllPostsRequest } from '@/controllers/posts/types';
import PaginatedQueryResponseSchema from '@/services/schema/PaginatedQueryResponseSchema';
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
import { NextApiRequest, NextApiResponse } from 'next';
import { NextApiResponse } from 'next';
import { createRouter } from 'next-connect';
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<
GetBeerStylesRequest,
GetAllPostsRequest,
NextApiResponse<z.infer<typeof APIResponseValidationSchema>>
>();

View File

@@ -8,9 +8,9 @@ const sendUpdateUserAvatarRequest = async ({
userId,
}: UpdateProfileRequestParams) => {
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',
body: formData,
});

View File

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

View File

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