Feat: add create brewery post, brewery image upload

Add address autocomplete, using MapBox
This commit is contained in:
Aaron William Po
2023-06-10 22:09:51 -04:00
parent 140abaa5a1
commit aa994f0067
19 changed files with 855 additions and 39 deletions

View File

@@ -12,7 +12,7 @@ import { NextApiResponse } from 'next';
import { z } from 'zod';
import ServerError from '@/config/util/ServerError';
import validateRequest from '@/config/nextConnect/middleware/validateRequest';
import processImageDataIntoDB from '@/services/BeerImage/processImageDataIntoDB';
import addBeerImageToDB from '@/services/BeerImage/addBeerImageToDB';
import ImageMetadataValidationSchema from '@/services/types/ImageSchema/ImageMetadataValidationSchema';
const { storage } = cloudinaryConfig;
@@ -50,7 +50,7 @@ const processImageData = async (
throw new ServerError('No images uploaded', 400);
}
const beerImages = await processImageDataIntoDB({
const beerImages = await addBeerImageToDB({
alt: body.alt,
caption: body.caption,
beerPostId: req.query.id,

View File

@@ -0,0 +1,86 @@
import NextConnectOptions from '@/config/nextConnect/NextConnectOptions';
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
import { UserExtendedNextApiRequest } from '@/config/auth/types';
import { createRouter, expressWrapper } from 'next-connect';
import getCurrentUser from '@/config/nextConnect/middleware/getCurrentUser';
import multer from 'multer';
import cloudinaryConfig from '@/config/cloudinary';
import { NextApiResponse } from 'next';
import { z } from 'zod';
import ServerError from '@/config/util/ServerError';
import validateRequest from '@/config/nextConnect/middleware/validateRequest';
import ImageMetadataValidationSchema from '@/services/types/ImageSchema/ImageMetadataValidationSchema';
import addBreweryImageToDB from '@/services/BreweryImage/addBreweryImageToDB';
const { storage } = cloudinaryConfig;
const fileFilter: multer.Options['fileFilter'] = (req, file, cb) => {
const { mimetype } = file;
const isImage = mimetype.startsWith('image/');
if (!isImage) {
cb(null, false);
}
cb(null, true);
};
const uploadMiddleware = expressWrapper(
multer({ storage, fileFilter, limits: { files: 5, fileSize: 15 * 1024 * 1024 } }).array(
'images',
),
);
interface UploadBreweryPostImagesRequest extends UserExtendedNextApiRequest {
files?: Express.Multer.File[];
query: { id: string };
body: z.infer<typeof ImageMetadataValidationSchema>;
}
const processImageData = async (
req: UploadBreweryPostImagesRequest,
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 addBreweryImageToDB({
alt: body.alt,
caption: body.caption,
breweryPostId: req.query.id,
userId: user!.id,
files,
});
res.status(200).json({
success: true,
message: `Successfully uploaded ${breweryImages.length} image${
breweryImages.length > 1 ? 's' : ''
}`,
statusCode: 200,
});
};
const router = createRouter<
UploadBreweryPostImagesRequest,
NextApiResponse<z.infer<typeof APIResponseValidationSchema>>
>();
router.post(
getCurrentUser,
// @ts-expect-error
uploadMiddleware,
validateRequest({ bodySchema: ImageMetadataValidationSchema }),
processImageData,
);
const handler = router.handler(NextConnectOptions);
export default handler;
export const config = { api: { bodyParser: false } };

View File

@@ -0,0 +1,76 @@
import { UserExtendedNextApiRequest } from '@/config/auth/types';
import validateRequest from '@/config/nextConnect/middleware/validateRequest';
import { createRouter } from 'next-connect';
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
import { NextApiResponse } from 'next';
import { z } from 'zod';
import NextConnectOptions from '@/config/nextConnect/NextConnectOptions';
import getCurrentUser from '@/config/nextConnect/middleware/getCurrentUser';
import CreateBreweryPostSchema from '@/services/BreweryPost/types/CreateBreweryPostSchema';
import createNewBreweryPost from '@/services/BreweryPost/createNewBreweryPost';
import geocode from '@/config/mapbox/geocoder';
import ServerError from '@/config/util/ServerError';
import DBClient from '@/prisma/DBClient';
interface CreateBreweryPostRequest extends UserExtendedNextApiRequest {
body: z.infer<typeof CreateBreweryPostSchema>;
}
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 DBClient.instance.location.create({
data: {
address,
city,
country,
stateOrProvince: region,
coordinates: [latitude, longitude],
postedBy: { connect: { id: userId } },
},
select: { id: true },
});
const newBreweryPost = await createNewBreweryPost({
name,
description,
locationId: location.id,
dateEstablished,
userId,
});
res.status(201).json({
message: 'Brewery post created successfully',
statusCode: 201,
payload: newBreweryPost,
success: true,
});
};
const router = createRouter<
CreateBreweryPostRequest,
NextApiResponse<z.infer<typeof APIResponseValidationSchema>>
>();
router.post(
validateRequest({ bodySchema: CreateBreweryPostSchema }),
getCurrentUser,
createBreweryPost,
);
const handler = router.handler(NextConnectOptions);
export default handler;

View File

@@ -11,6 +11,7 @@ import findUserByEmail from '@/services/User/findUserByEmail';
import validateRequest from '@/config/nextConnect/middleware/validateRequest';
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
import { NODE_ENV } from '@/config/env';
import sendConfirmationEmail from '@/services/User/sendConfirmationEmail';
interface RegisterUserRequest extends NextApiRequest {
@@ -44,7 +45,9 @@ const registerUser = async (req: RegisterUserRequest, res: NextApiResponse) => {
username: user.username,
});
await sendConfirmationEmail(user);
if (NODE_ENV === 'production') {
await sendConfirmationEmail(user);
}
res.status(201).json({
success: true,