From 38e2e7b522aa8f1608f85124d5e033bbb74a9ba8 Mon Sep 17 00:00:00 2001 From: Stefano Pigozzi Date: Tue, 31 May 2022 12:00:55 +0200 Subject: [PATCH] this turned out to be pretty clean tbh --- package.json | 2 +- pages/api/events/[slug].ts | 16 ++++++++++++---- utils/restInPeace.ts | 21 ++++++++++++++++----- 3 files changed, 29 insertions(+), 10 deletions(-) diff --git a/package.json b/package.json index b3e0190..3fb8864 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "version": "0.1.0", "private": true, "scripts": { - "dev": "dotenv -e .env.local prisma db push && dotenv -e .env.local prisma generate && dotenv -e .env.local next dev", + "dev": "dotenv -e .env.local prisma db push && dotenv -e .env.local prisma generate && NODE_OPTIONS=--inspect dotenv -e .env.local next dev", "build": "next build", "start": "next start", "lint": "next lint", diff --git a/pages/api/events/[slug].ts b/pages/api/events/[slug].ts index 95aa242..5ed2a18 100644 --- a/pages/api/events/[slug].ts +++ b/pages/api/events/[slug].ts @@ -1,17 +1,23 @@ import { client } from "../../../utils/prismaClient"; import { NextApiRequest, NextApiResponse } from "next"; import { ApiResult } from "../../../types/api"; -import { restInPeace } from "../../../utils/restInPeace"; +import { Model, restInPeace } from "../../../utils/restInPeace"; import { default as cryptoRandomString} from "crypto-random-string"; -import { handleInterrupts } from "../../../utils/interrupt"; +import { handleInterrupts, Interrupt } from "../../../utils/interrupt"; import { authorizeUser } from "../../../utils/apiAuth"; -import { User } from "@prisma/client"; +import { Event } from "@prisma/client"; export default async function handler(req: NextApiRequest, res: NextApiResponse>) { handleInterrupts(res, async () => { const user = await authorizeUser(req, res) + const canEdit = async (model: Model, obj?: Event) => { + if(obj && obj.creatorId !== user.id) { + throw new Interrupt(403, {error: "Only the creator can edit an event"}) + } + } + const which = { slug: req.query.slug } @@ -28,7 +34,9 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse< model: client.event, retrieve: {which}, create: {create}, - // TODO: this might prove problematic + upsert: {create, update, before: canEdit}, + update: {update, before: canEdit}, + destroy: {before: canEdit}, }) }) } \ No newline at end of file diff --git a/utils/restInPeace.ts b/utils/restInPeace.ts index 67c7199..c4c9041 100644 --- a/utils/restInPeace.ts +++ b/utils/restInPeace.ts @@ -3,7 +3,7 @@ import { ApiError, ApiResult } from "../types/api"; // I don't know what the typing of a Prisma model is. -type Model = any +export type Model = any type RestInPeaceOptions = { @@ -97,7 +97,7 @@ export async function restInPeace(req: NextApiRequest, res: NextApiResponse { - before?: (model: T) => Promise, + before?: (model: T, obj?: any) => Promise, after?: (model: T, obj?: any) => Promise, } @@ -106,6 +106,7 @@ interface OperationOptions { interface HeadOptions extends OperationOptions { + before?: (model: T) => Promise, after?: (model: T) => Promise, } @@ -123,6 +124,7 @@ async function handleHead(res: NextApiResponse<"">, model: Model, options: He interface OptionsOptions extends OperationOptions { + before?: (model: T) => Promise, after?: (model: T) => Promise, } @@ -145,6 +147,7 @@ interface ListOptions extends OperationOptions { */ where?: object, + before?: (model: T) => Promise, after?: (model: T, obj: T[]) => Promise, } @@ -170,6 +173,7 @@ interface RetrieveOptions extends OperationOptions { */ which?: object, + before?: (model: T) => Promise, after?: (model: T, obj: T) => Promise, } @@ -196,6 +200,7 @@ interface CreateOptions extends OperationOptions { */ create: object, + before?: (model: T) => Promise, after?: (model: T, obj: T) => Promise, } @@ -231,6 +236,7 @@ interface UpsertOptions extends OperationOptions { */ update: object, + before?: (model: T, obj?: T) => Promise, after?: (model: T, obj: T) => Promise, } @@ -238,7 +244,8 @@ interface UpsertOptions extends OperationOptions { * Handle a `PUT` HTTP request where a single item is either created or updated. */ async function handleUpsert(res: NextApiResponse>, model: Model, options: UpsertOptions) { - await options.before?.(model) + const initialObj = await model.findUnique({ where: options.which }) + await options.before?.(model, initialObj) const obj = await model.upsert({ where: options.which, create: options.create, @@ -265,6 +272,7 @@ interface UpdateOptions extends OperationOptions { */ update: object, + before?: (model: T, obj?: T) => Promise, after?: (model: T, obj: T) => Promise, } @@ -272,7 +280,8 @@ interface UpdateOptions extends OperationOptions { * Handle a `PATCH` HTTP request where a single item is updated. */ async function handleUpdate(res: NextApiResponse>, model: Model, options: UpdateOptions) { - await options.before?.(model) + const initialObj = await model.findUnique({ where: options.which }) + await options.before?.(model, initialObj) const obj = await model.update({ where: options.which, data: options.update, @@ -293,6 +302,7 @@ interface DestroyOptions extends OperationOptions { */ which?: object, + before?: (model: T, obj?: T) => Promise, after?: (model: T) => Promise, } @@ -300,7 +310,8 @@ interface DestroyOptions extends OperationOptions { * Handle a `DELETE` HTTP request where a single item is destroyed. */ async function handleDestroy(res: NextApiResponse>, model: Model, options: DestroyOptions) { - await options.before?.(model) + const initialObj = await model.findUnique({ where: options.which }) + await options.before?.(model, initialObj) await model.delete({ where: options.which, })