mirror of
https://github.com/aaronpo97/the-biergarten-app.git
synced 2026-06-01 10:04:00 +00:00
Move next js project to archive (#207)
This commit is contained in:
@@ -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 { commentId } = req.query;
|
||||
const user = req.user!;
|
||||
const comment = await getBeerPostCommentByIdService({ beerPostCommentId: commentId });
|
||||
|
||||
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 { commentId } = req.query;
|
||||
|
||||
await editBeerPostCommentByIdService({ body: req.body, beerPostCommentId: commentId });
|
||||
|
||||
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 { commentId } = req.query;
|
||||
|
||||
await deleteBeerCommentByIdService({ beerPostCommentId: commentId });
|
||||
|
||||
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.postId;
|
||||
|
||||
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.postId;
|
||||
// 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,
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,120 @@
|
||||
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 { commentId } = req.query;
|
||||
const user = req.user!;
|
||||
|
||||
const beerStyleComment = await findBeerStyleCommentById({
|
||||
beerStyleCommentId: commentId,
|
||||
});
|
||||
|
||||
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.commentId,
|
||||
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 { commentId } = req.query;
|
||||
|
||||
await deleteBeerStyleCommentById({ beerStyleCommentId: commentId });
|
||||
|
||||
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.postId,
|
||||
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.postId;
|
||||
// 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,
|
||||
});
|
||||
};
|
||||
@@ -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 { commentId } = req.query;
|
||||
const user = req.user!;
|
||||
const comment = await getBreweryCommentById({ breweryCommentId: commentId });
|
||||
|
||||
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 { commentId } = req.query;
|
||||
|
||||
const updated = await updateBreweryCommentById({
|
||||
breweryCommentId: commentId,
|
||||
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 { commentId } = req.query;
|
||||
|
||||
await deleteBreweryCommentByIdService({ breweryCommentId: commentId });
|
||||
|
||||
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.postId;
|
||||
|
||||
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.postId;
|
||||
// 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,
|
||||
});
|
||||
};
|
||||
@@ -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: { postId: string; commentId: string };
|
||||
}
|
||||
|
||||
export interface EditAndCreateCommentRequest extends CommentRequest {
|
||||
body: z.infer<typeof CreateCommentValidationSchema>;
|
||||
}
|
||||
|
||||
export interface GetAllCommentsRequest extends UserExtendedNextApiRequest {
|
||||
query: { postId: string; page_size: string; page_num: string };
|
||||
}
|
||||
@@ -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.postId,
|
||||
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,
|
||||
});
|
||||
};
|
||||
@@ -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.postId,
|
||||
userId: user!.id,
|
||||
body,
|
||||
files,
|
||||
});
|
||||
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
message: `Successfully uploaded ${breweryImages.length} image${
|
||||
breweryImages.length > 1 ? 's' : ''
|
||||
}`,
|
||||
statusCode: 200,
|
||||
});
|
||||
};
|
||||
@@ -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: { postId: string };
|
||||
body: z.infer<typeof ImageMetadataValidationSchema>;
|
||||
}
|
||||
|
||||
export interface DeleteImageRequest extends UserExtendedNextApiRequest {
|
||||
query: { id: string };
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
import { UserExtendedNextApiRequest } from '@/config/auth/types';
|
||||
import ServerError from '@/config/util/ServerError';
|
||||
|
||||
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
||||
import { NextApiResponse } 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 { postId } = req.query;
|
||||
|
||||
const beer = await getBeerPostById({ beerPostId: postId });
|
||||
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: LikeRequest,
|
||||
res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>,
|
||||
) => {
|
||||
const { postId } = req.query;
|
||||
|
||||
const likeCount = await getBeerPostLikeCountService({ beerPostId: postId });
|
||||
|
||||
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.postId 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 },
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,82 @@
|
||||
import ServerError from '@/config/util/ServerError';
|
||||
|
||||
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
||||
import { NextApiResponse } 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 { postId } = req.query;
|
||||
|
||||
const beerStyle = await getBeerStyleByIdService({ beerStyleId: postId });
|
||||
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: LikeRequest,
|
||||
res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>,
|
||||
) => {
|
||||
const { postId } = req.query;
|
||||
const likeCount = await getBeerStyleLikeCountService({ beerStyleId: postId });
|
||||
|
||||
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 },
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,96 @@
|
||||
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 } from 'next';
|
||||
import { z } from 'zod';
|
||||
import { LikeRequest } from '../types';
|
||||
|
||||
export const sendBreweryPostLikeRequest = async (
|
||||
req: LikeRequest,
|
||||
res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>,
|
||||
) => {
|
||||
const { postId } = req.query;
|
||||
const user = req.user!;
|
||||
|
||||
const breweryPost = await getBreweryPostByIdService({ breweryPostId: postId });
|
||||
|
||||
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: LikeRequest,
|
||||
res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>,
|
||||
) => {
|
||||
const { postId } = req.query;
|
||||
|
||||
const breweryPost = await getBreweryPostByIdService({ breweryPostId: postId });
|
||||
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: LikeRequest,
|
||||
res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>,
|
||||
) => {
|
||||
const user = req.user!;
|
||||
const { postId } = req.query;
|
||||
|
||||
const liked = await findBreweryPostLikeService({
|
||||
breweryPostId: postId,
|
||||
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 },
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,5 @@
|
||||
import { UserExtendedNextApiRequest } from '@/config/auth/types';
|
||||
|
||||
export interface LikeRequest extends UserExtendedNextApiRequest {
|
||||
query: { postId: string };
|
||||
}
|
||||
@@ -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 { postId } = query;
|
||||
|
||||
const beerPost = await getBeerPostById({ beerPostId: postId });
|
||||
|
||||
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.postId, 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 { postId } = req.query;
|
||||
|
||||
const deleted = await deleteBeerPostByIdService({ beerPostId: postId });
|
||||
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 { postId } = req.query;
|
||||
|
||||
const beerPost = await getBeerPostById({ beerPostId: postId });
|
||||
|
||||
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,
|
||||
});
|
||||
};
|
||||
@@ -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: { postId: 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: { postId: string; page_num: string; page_size: string };
|
||||
}
|
||||
|
||||
export interface CreateBeerPostRequest extends UserExtendedNextApiRequest {
|
||||
body: z.infer<typeof CreateBeerPostValidationSchema>;
|
||||
}
|
||||
@@ -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,
|
||||
});
|
||||
};
|
||||
@@ -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
archive/next-js-web-app/src/controllers/posts/breweries/index.ts
Normal file
218
archive/next-js-web-app/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 { postId } = req.query;
|
||||
|
||||
const breweryPost = await getBreweryPostByIdService({ breweryPostId: postId });
|
||||
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: { postId },
|
||||
} = req;
|
||||
|
||||
await updateBreweryPostService({ breweryPostId: postId, body });
|
||||
|
||||
res.status(200).json({
|
||||
message: 'Brewery post updated successfully',
|
||||
success: true,
|
||||
statusCode: 200,
|
||||
});
|
||||
};
|
||||
|
||||
export const deleteBreweryPost = async (
|
||||
req: BreweryPostRequest,
|
||||
res: NextApiResponse,
|
||||
) => {
|
||||
const { postId } = req.query;
|
||||
const deleted = await deleteBreweryPostService({ breweryPostId: postId });
|
||||
|
||||
if (!deleted) {
|
||||
throw new ServerError('Brewery post not found', 404);
|
||||
}
|
||||
|
||||
res.status(200).json({
|
||||
message: 'Brewery post deleted successfully',
|
||||
success: true,
|
||||
statusCode: 200,
|
||||
});
|
||||
};
|
||||
@@ -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: { postId: string };
|
||||
}
|
||||
|
||||
export interface EditBreweryPostRequest extends BreweryPostRequest {
|
||||
body: z.infer<typeof EditBreweryPostValidationSchema>;
|
||||
}
|
||||
38
archive/next-js-web-app/src/controllers/posts/types/index.ts
Normal file
38
archive/next-js-web-app/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
archive/next-js-web-app/src/controllers/users/auth/index.ts
Normal file
302
archive/next-js-web-app/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,
|
||||
});
|
||||
};
|
||||
@@ -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
archive/next-js-web-app/src/controllers/users/profile/index.ts
Normal file
216
archive/next-js-web-app/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,
|
||||
});
|
||||
};
|
||||
@@ -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 };
|
||||
}
|
||||
Reference in New Issue
Block a user