mirror of
https://github.com/Steffo99/festa.git
synced 2024-12-22 14:44:21 +00:00
woahhhhhhh
This commit is contained in:
parent
14308da110
commit
9f351b1e36
10 changed files with 288 additions and 161 deletions
|
@ -1,22 +1,34 @@
|
||||||
import { prisma } from "../../../utils/prismaClient";
|
import { client } from "../../../utils/prismaClient";
|
||||||
import { NextApiRequest, NextApiResponse } from "next";
|
import { NextApiRequest, NextApiResponse } from "next";
|
||||||
import { ApiResult } from "../../../types/api";
|
import { ApiResult } from "../../../types/api";
|
||||||
import { restInPeace } from "../../../utils/restInPeace";
|
import { restInPeace } from "../../../utils/restInPeace";
|
||||||
|
import { default as cryptoRandomString} from "crypto-random-string";
|
||||||
|
import { handleInterrupts } from "../../../utils/interrupt";
|
||||||
|
import { authorizeUser } from "../../../utils/apiAuth";
|
||||||
|
import { User } from "@prisma/client";
|
||||||
|
|
||||||
|
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<ApiResult<Event | Event[]>>) {
|
export default async function handler(req: NextApiRequest, res: NextApiResponse<ApiResult<Event | Event[]>>) {
|
||||||
restInPeace(req, res, {
|
handleInterrupts(res, async () => {
|
||||||
model: prisma.event,
|
const user = await authorizeUser(req, res)
|
||||||
isList: false,
|
|
||||||
whereList: {},
|
const which = {
|
||||||
whereDetail: {
|
slug: req.query.slug
|
||||||
slug: req.query.slug,
|
}
|
||||||
},
|
const create = {
|
||||||
create: {
|
slug: cryptoRandomString({length: 12, type: "url-safe"}),
|
||||||
slug: req.query.slug,
|
creatorId: user.id,
|
||||||
// TODO
|
name: req.body.name
|
||||||
},
|
}
|
||||||
update: {
|
const update = {
|
||||||
// TODO
|
name: req.body.name
|
||||||
},
|
}
|
||||||
|
|
||||||
|
restInPeace(req, res, {
|
||||||
|
model: client.event,
|
||||||
|
retrieve: {which},
|
||||||
|
create: {create},
|
||||||
|
// TODO: this might prove problematic
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
import { NextApiRequest, NextApiResponse } from "next"
|
import { NextApiRequest, NextApiResponse } from "next"
|
||||||
import { prisma } from "../../../utils/prismaClient"
|
import { client } from "../../../utils/prismaClient"
|
||||||
import { TelegramUserDataClass } from "../../../utils/TelegramUserDataClass"
|
import { TelegramUserDataClass } from "../../../utils/TelegramUserDataClass"
|
||||||
import { default as cryptoRandomString } from "crypto-random-string"
|
import { default as cryptoRandomString } from "crypto-random-string"
|
||||||
import { ApiResult } from "../../../types/api"
|
import { ApiResult } from "../../../types/api"
|
||||||
|
@ -54,7 +54,7 @@ async function loginTelegram(req: NextApiRequest, res: NextApiResponse<ApiResult
|
||||||
return res.status(401).json({ error: "Telegram login data has been tampered" })
|
return res.status(401).json({ error: "Telegram login data has been tampered" })
|
||||||
}
|
}
|
||||||
|
|
||||||
const accountTelegram = await prisma.accountTelegram.upsert({
|
const accountTelegram = await client.accountTelegram.upsert({
|
||||||
where: {
|
where: {
|
||||||
telegramId: userData.id
|
telegramId: userData.id
|
||||||
},
|
},
|
||||||
|
@ -75,7 +75,7 @@ async function loginTelegram(req: NextApiRequest, res: NextApiResponse<ApiResult
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const token = await prisma.token.create({
|
const token = await client.token.create({
|
||||||
data: {
|
data: {
|
||||||
userId: accountTelegram.userId,
|
userId: accountTelegram.userId,
|
||||||
token: cryptoRandomString({ length: 16, type: "base64" }),
|
token: cryptoRandomString({ length: 16, type: "base64" }),
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.postcard {
|
.postcard {
|
||||||
filter: blur(16px) contrast(25%) brightness(175%);
|
filter: blur(7px) contrast(25%) brightness(175%);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Dark theme */
|
/* Dark theme */
|
||||||
|
@ -36,6 +36,6 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.postcard {
|
.postcard {
|
||||||
filter: blur(16px) contrast(50%) brightness(50%);
|
filter: blur(7px) contrast(50%) brightness(50%);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
"isolatedModules": true,
|
"isolatedModules": true,
|
||||||
"jsx": "preserve",
|
"jsx": "preserve",
|
||||||
"incremental": true,
|
"incremental": true,
|
||||||
|
"experimentalDecorators": true,
|
||||||
},
|
},
|
||||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
|
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
|
||||||
"exclude": ["node_modules"]
|
"exclude": ["node_modules"]
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { prisma } from "./prismaClient"
|
import { client } from "./prismaClient"
|
||||||
import { AccountTelegram, Token, User } from "@prisma/client"
|
import { AccountTelegram, Token, User } from "@prisma/client"
|
||||||
import nodecrypto from "crypto"
|
import nodecrypto from "crypto"
|
||||||
import { TelegramLoginData } from "../types/user"
|
import { TelegramLoginData } from "../types/user"
|
||||||
|
|
27
utils/apiAuth.ts
Normal file
27
utils/apiAuth.ts
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
import { User } from "@prisma/client"
|
||||||
|
import { NextApiRequest, NextApiResponse } from "next"
|
||||||
|
import { client } from "./prismaClient"
|
||||||
|
import { Interrupt } from "./interrupt"
|
||||||
|
|
||||||
|
|
||||||
|
export async function authorizeUser(req: NextApiRequest, res: NextApiResponse): Promise<User> {
|
||||||
|
const authorization = req.headers.authorization
|
||||||
|
|
||||||
|
if (!authorization) {
|
||||||
|
throw new Interrupt(401, {error: "Missing Authorization header" })
|
||||||
|
}
|
||||||
|
|
||||||
|
const token = authorization.match(/^Bearer (\S+)$/)?.[1]
|
||||||
|
|
||||||
|
if(!(token)) {
|
||||||
|
throw new Interrupt(401, {error: "Invalid Authorization header" })
|
||||||
|
}
|
||||||
|
|
||||||
|
const dbToken = await client.token.findUnique({where: {token}, include: {user: true}})
|
||||||
|
|
||||||
|
if(!(dbToken)) {
|
||||||
|
throw new Interrupt(401, {error: "No such Authorization token" })
|
||||||
|
}
|
||||||
|
|
||||||
|
return dbToken.user
|
||||||
|
}
|
30
utils/interrupt.ts
Normal file
30
utils/interrupt.ts
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
import { NextApiResponse } from "next"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pseudo-decorator which intercepts thrown {@link Interrupt}s and turns them into HTTP responses.
|
||||||
|
*/
|
||||||
|
export async function handleInterrupts(res: NextApiResponse, f: () => Promise<void>) {
|
||||||
|
try {
|
||||||
|
return await f()
|
||||||
|
}
|
||||||
|
catch(e) {
|
||||||
|
if(e instanceof Interrupt) {
|
||||||
|
return res.status(e.status).json(e.response)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Error which interrupts the regular flow of a function to return a specific HTTP response.
|
||||||
|
*
|
||||||
|
* Caught by {@link interruptHandler}.
|
||||||
|
*/
|
||||||
|
export class Interrupt<R extends {}> {
|
||||||
|
status: number
|
||||||
|
response: R
|
||||||
|
|
||||||
|
constructor(status: number, response: R) {
|
||||||
|
this.status = status
|
||||||
|
this.response = response
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,3 +1,3 @@
|
||||||
import { PrismaClient } from "@prisma/client";
|
import { PrismaClient } from "@prisma/client";
|
||||||
|
|
||||||
export const prisma = new PrismaClient()
|
export const client = new PrismaClient()
|
||||||
|
|
|
@ -1,252 +1,309 @@
|
||||||
/**
|
|
||||||
* 3 AM coding
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { NextApiRequest, NextApiResponse } from "next";
|
import { NextApiRequest, NextApiResponse } from "next";
|
||||||
import { ApiError, ApiResult } from "../types/api";
|
import { ApiError, ApiResult } from "../types/api";
|
||||||
|
|
||||||
|
|
||||||
type RestInOptions<T> = {
|
// I don't know what the typing of a Prisma model is.
|
||||||
|
type Model = any
|
||||||
|
|
||||||
|
|
||||||
|
type RestInPeaceOptions<T> = {
|
||||||
/**
|
/**
|
||||||
* Prisma delegate to operate on.
|
* Prisma delegate to operate on.
|
||||||
*/
|
*/
|
||||||
model: any,
|
model: Model,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* What kind of request is being performed: `true` for list, `false` for detail.
|
* Options for the "head" operation.
|
||||||
*/
|
*/
|
||||||
isList: boolean
|
head?: HeadOptions<T>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Where clause for Prisma queries about multiple objects.
|
* Options for the "options" operation.
|
||||||
*
|
|
||||||
* Cannot be set together with `whereDetail`.
|
|
||||||
*/
|
*/
|
||||||
whereList: object,
|
options?: OptionsOptions<T>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Where clause for Prisma queries about a single object.
|
* Options for the "list" operation.
|
||||||
*
|
|
||||||
* Cannot be set together with `whereList`.
|
|
||||||
*/
|
*/
|
||||||
whereDetail: object,
|
list?: ListOptions<T>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The same as Prisma's `create`.
|
* Options for the "retrieve" operation.
|
||||||
*/
|
*/
|
||||||
create: any,
|
retrieve?: RetrieveOptions<T>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The same as Prisma's `update`.
|
* Options for the "create" operation.
|
||||||
*/
|
*/
|
||||||
update: any,
|
create?: CreateOptions<T>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Operations not allowed.
|
* Options for the "upsert" operation.
|
||||||
*/
|
*/
|
||||||
disallow?: {
|
upsert?: UpsertOptions<T>
|
||||||
head?: boolean,
|
|
||||||
options?: boolean,
|
|
||||||
retrieve?: boolean,
|
|
||||||
list?: boolean,
|
|
||||||
create?: boolean,
|
|
||||||
upsert?: boolean,
|
|
||||||
update?: boolean,
|
|
||||||
destroy?: boolean,
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hooks ran after a specific operation is completed.
|
* Options for the "update" operation.
|
||||||
*
|
|
||||||
* If a {@link BreakYourBones} is thrown, it will be caught and returned to the user.
|
|
||||||
*
|
|
||||||
* ```
|
|
||||||
* throw new BreakYourBones(403, {error: "Not allowed"})
|
|
||||||
* ```
|
|
||||||
*/
|
*/
|
||||||
hooks?: {
|
update?: UpdateOptions<T>
|
||||||
head?: () => T,
|
|
||||||
options?: () => T,
|
/**
|
||||||
retrieve?: (obj: T) => T,
|
* Options for the "destroy" operation.
|
||||||
list?: (obj: T[]) => T[],
|
*/
|
||||||
create?: (obj: T) => T,
|
destroy?: DestroyOptions<T>
|
||||||
upsert?: (obj: T) => T,
|
|
||||||
update?: (obj: T) => T,
|
|
||||||
destroy?: () => T,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle an API route in a [REST](https://en.wikipedia.org/wiki/Representational_state_transfer)ful way.
|
* Handle an API route in a [REST](https://en.wikipedia.org/wiki/Representational_state_transfer)ful way.
|
||||||
*/
|
*/
|
||||||
export function restInPeace<T>(req: NextApiRequest, res: NextApiResponse<ApiResult<T>>, options: RestInOptions<T>) {
|
export async function restInPeace<T>(req: NextApiRequest, res: NextApiResponse<ApiResult<T>>, options: RestInPeaceOptions<T>) {
|
||||||
// Ensure the wheres are set correctly
|
|
||||||
if (options.whereList && options.whereDetail) {
|
|
||||||
return res.status(500).json({ error: "Request is being handled as both a list operation and a detail operation" })
|
|
||||||
}
|
|
||||||
else if (!(options.whereList || options.whereDetail)) {
|
|
||||||
return res.status(500).json({ error: "Request is not being handled as any kind of operation" })
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle HEAD by returning an empty body
|
// Handle HEAD by returning an empty body
|
||||||
else if (req.method === "HEAD") {
|
if (options.head && req.method === "HEAD") {
|
||||||
return restInHead(res, options)
|
return await handleHead(res, options.model, options.head)
|
||||||
}
|
}
|
||||||
// Same thing for OPTIONS, but beware of weird CORS things!
|
// Same thing for OPTIONS, but beware of weird CORS things!
|
||||||
else if (req.method === "OPTIONS") {
|
else if (options.options && req.method === "OPTIONS") {
|
||||||
return restInOptions(res, options)
|
return await handleOptions(res, options.model, options.options)
|
||||||
}
|
}
|
||||||
// GET can be both "list" and "retrieve"
|
// GET can be both "list" and "retrieve"
|
||||||
else if (req.method === "GET") {
|
else if (options.list && options.list.where && req.method === "GET") {
|
||||||
return options.isList ? restInList(res, options) : restInRetrieve(res, options)
|
return await handleList(res, options.model, options.list)
|
||||||
|
}
|
||||||
|
else if(options.retrieve && options.retrieve.which && req.method === "GET") {
|
||||||
|
return await handleRetrieve(res, options.model, options.retrieve)
|
||||||
}
|
}
|
||||||
// POST is always "create"
|
// POST is always "create"
|
||||||
else if (req.method === "POST") {
|
else if (options.create && req.method === "POST") {
|
||||||
return options.isList ? noRestForTheWicked(res) : restInCreate(res, options)
|
return await handleCreate(res, options.model, options.create)
|
||||||
}
|
}
|
||||||
// PUT is always "upsert"
|
// PUT is always "upsert"
|
||||||
else if (req.method === "PUT") {
|
else if (options.upsert && options.upsert.which && req.method === "PUT") {
|
||||||
return options.isList ? noRestForTheWicked(res) : restInUpsert(res, options)
|
return await handleUpsert(res, options.model, options.upsert)
|
||||||
}
|
}
|
||||||
// PATCH is always "update"
|
// PATCH is always "update"
|
||||||
else if (req.method === "PATCH") {
|
else if (options.update && options.update.which && req.method === "PATCH") {
|
||||||
return options.isList ? noRestForTheWicked(res) : restInUpdate(res, options)
|
return await handleUpdate(res, options.model, options.update)
|
||||||
}
|
}
|
||||||
// DELETE is always "destroy"
|
// DELETE is always "destroy"
|
||||||
else if (req.method === "DELETE") {
|
else if (options.destroy && options.destroy.which && req.method === "DELETE") {
|
||||||
return options.isList ? noRestForTheWicked(res) : restInDestroy(res, options)
|
return await handleDestroy(res, options.model, options.destroy)
|
||||||
}
|
}
|
||||||
|
|
||||||
// What kind of weird HTTP methods are you using?!
|
// What kind of weird HTTP methods are you using?!
|
||||||
else {
|
else {
|
||||||
return noRestForTheWicked(res)
|
return res.status(405).json({ error: "Method not allowed" })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @returns Method not allowed.
|
interface OperationOptions<T> {
|
||||||
*/
|
before?: (model: T) => Promise<void>,
|
||||||
function noRestForTheWicked(res: NextApiResponse) {
|
after?: (model: T, obj?: any) => Promise<any>,
|
||||||
return res.status(405).json({ error: "Method not allowed" })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Error which interrupts the regular flow of a hook to return something different.
|
|
||||||
*
|
|
||||||
* Caught by {@link theButcher}.
|
|
||||||
*/
|
|
||||||
export class BreakYourBones<AT> {
|
|
||||||
status: number
|
|
||||||
response: AT
|
|
||||||
|
|
||||||
constructor(status: number, response: AT) {
|
// === HEAD ===
|
||||||
this.status = status
|
|
||||||
this.response = response
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle a {@link restInPeace} hook, catching possible {@link BreakYourBones}.
|
interface HeadOptions<T> extends OperationOptions<T> {
|
||||||
*/
|
after?: (model: T) => Promise<void>,
|
||||||
function theButcher<T>(obj: any, res: NextApiResponse, options: RestInOptions<T>, method: keyof RestInOptions<T>["hooks"]) {
|
|
||||||
try {
|
|
||||||
var mutated = options?.hooks?.[method]?.(obj) ?? obj
|
|
||||||
}
|
|
||||||
catch (e) {
|
|
||||||
if (e instanceof BreakYourBones) {
|
|
||||||
return res.status(e.status).json(e.response)
|
|
||||||
}
|
|
||||||
throw e
|
|
||||||
}
|
|
||||||
return mutated
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle an `HEAD` HTTP request.
|
* Handle an `HEAD` HTTP request.
|
||||||
*/
|
*/
|
||||||
function restInHead<T>(res: NextApiResponse<"">, options: RestInOptions<T>) {
|
async function handleHead<T>(res: NextApiResponse<"">, model: Model, options: HeadOptions<T>) {
|
||||||
if (options.disallow?.head) return noRestForTheWicked(res)
|
await options.before?.(model)
|
||||||
theButcher(undefined, res, options, "head")
|
await options.after?.(model)
|
||||||
return res.status(200).send("")
|
return res.status(200).send("")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// === OPTIONS ===
|
||||||
|
|
||||||
|
|
||||||
|
interface OptionsOptions<T> extends OperationOptions<T> {
|
||||||
|
after?: (model: T) => Promise<void>,
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle an `OPTIONS` HTTP request.
|
* Handle an `OPTIONS` HTTP request.
|
||||||
*/
|
*/
|
||||||
function restInOptions<T>(res: NextApiResponse<ApiResult<T>>, options: RestInOptions<T>) {
|
async function handleOptions<T>(res: NextApiResponse<"">, model: Model, options: OptionsOptions<T>) {
|
||||||
if (options.disallow?.options) return noRestForTheWicked(res)
|
await options.before?.(model)
|
||||||
theButcher(undefined, res, options, "options")
|
await options.after?.(model)
|
||||||
return res.status(200).send("")
|
return res.status(200).send("")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// === LIST ===
|
||||||
|
|
||||||
|
|
||||||
|
interface ListOptions<T> extends OperationOptions<T> {
|
||||||
|
/**
|
||||||
|
* Prisma Where clause used to list objects available in a API route.
|
||||||
|
*/
|
||||||
|
where?: object,
|
||||||
|
|
||||||
|
after?: (model: T, obj: T[]) => Promise<T[]>,
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle a `GET` HTTP request where a list of items is requested.
|
* Handle a `GET` HTTP request where a list of items is requested.
|
||||||
*/
|
*/
|
||||||
function restInList<T>(res: NextApiResponse<ApiResult<T>>, options: RestInOptions<T>) {
|
async function handleList<T>(res: NextApiResponse<ApiResult<T>>, model: Model, options: ListOptions<T>) {
|
||||||
if (options.disallow?.list) return noRestForTheWicked(res)
|
await options.before?.(model)
|
||||||
const objs = options.model.findMany({ where: options.whereList })
|
const objs = await model.findMany({ where: options.where })
|
||||||
const mutatedObjs = theButcher(objs, res, options, "list")
|
const mutatedObjs = await options.after?.(model, objs) ?? objs
|
||||||
return res.status(200).json(mutatedObjs)
|
return res.status(200).json(mutatedObjs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// === RETRIEVE ===
|
||||||
|
|
||||||
|
|
||||||
|
interface RetrieveOptions<T> extends OperationOptions<T> {
|
||||||
|
/**
|
||||||
|
* Prisma Where clause used to select the object to display.
|
||||||
|
*
|
||||||
|
* See also `findUnique`.
|
||||||
|
*/
|
||||||
|
which?: object,
|
||||||
|
|
||||||
|
after?: (model: T, obj: T) => Promise<T>,
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle a `GET` HTTP request where a single item is requested.
|
* Handle a `GET` HTTP request where a single item is requested.
|
||||||
*/
|
*/
|
||||||
function restInRetrieve<T>(res: NextApiResponse<ApiResult<T>>, options: RestInOptions<T>) {
|
async function handleRetrieve<T>(res: NextApiResponse<ApiResult<T>>, model: Model, options: RetrieveOptions<T>) {
|
||||||
if (options.disallow?.retrieve) return noRestForTheWicked(res)
|
await options.before?.(model)
|
||||||
const obj = options.model.findUnique({ where: options.whereDetail })
|
const obj = await model.findUnique({ where: options.which })
|
||||||
const mutatedObj = theButcher(obj, res, options, "retrieve")
|
const mutatedObj = await options.after?.(model, obj) ?? obj
|
||||||
if (!obj) {
|
if (!mutatedObj) {
|
||||||
return res.status(404).json({ error: "Not found" })
|
return res.status(404).json({ error: "Not found" })
|
||||||
}
|
}
|
||||||
return res.status(200).json(mutatedObj)
|
return res.status(200).json(mutatedObj)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// === CREATE ===
|
||||||
|
|
||||||
|
|
||||||
|
interface CreateOptions<T> extends OperationOptions<T> {
|
||||||
|
/**
|
||||||
|
* Prisma Create clause used to create the object.
|
||||||
|
*/
|
||||||
|
create: object,
|
||||||
|
|
||||||
|
after?: (model: T, obj: T) => Promise<T>,
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle a `POST` HTTP request where a single item is created.
|
* Handle a `POST` HTTP request where a single item is created.
|
||||||
*/
|
*/
|
||||||
function restInCreate<T>(res: NextApiResponse<ApiResult<T>>, options: RestInOptions<T>) {
|
async function handleCreate<T>(res: NextApiResponse<ApiResult<T>>, model: Model, options: CreateOptions<T>) {
|
||||||
if (options.disallow?.create) return noRestForTheWicked(res)
|
await options.before?.(model)
|
||||||
const obj = options.model.create({ data: options.create })
|
const obj = await model.create({ data: options.create })
|
||||||
const mutatedObj = theButcher(obj, res, options, "create")
|
const mutatedObj = await options.after?.(model, obj) ?? obj
|
||||||
return res.status(200).json(mutatedObj)
|
return res.status(200).json(mutatedObj)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// === UPSERT ===
|
||||||
|
|
||||||
|
|
||||||
|
interface UpsertOptions<T> extends OperationOptions<T> {
|
||||||
|
/**
|
||||||
|
* Prisma Where clause used to select the object to operate on.
|
||||||
|
*
|
||||||
|
* See also `findUnique`.
|
||||||
|
*/
|
||||||
|
which?: object,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prisma Create clause used to create the object if it doesn't exist.
|
||||||
|
*/
|
||||||
|
create: object,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prisma Update clause used to update the object if it exists.
|
||||||
|
*/
|
||||||
|
update: object,
|
||||||
|
|
||||||
|
after?: (model: T, obj: T) => Promise<T>,
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle a `PUT` HTTP request where a single item is either created or updated.
|
* Handle a `PUT` HTTP request where a single item is either created or updated.
|
||||||
*/
|
*/
|
||||||
function restInUpsert<T>(res: NextApiResponse<ApiResult<T>>, options: RestInOptions<T>) {
|
async function handleUpsert<T>(res: NextApiResponse<ApiResult<T>>, model: Model, options: UpsertOptions<T>) {
|
||||||
if (options.disallow?.upsert) return noRestForTheWicked(res)
|
await options.before?.(model)
|
||||||
const obj = options.model.upsert({
|
const obj = await model.upsert({
|
||||||
where: options.whereDetail,
|
where: options.which,
|
||||||
create: options.create,
|
create: options.create,
|
||||||
update: options.update,
|
update: options.update,
|
||||||
})
|
})
|
||||||
const mutatedObj = theButcher(obj, res, options, "upsert")
|
const mutatedObj = await options.after?.(model, obj) ?? obj
|
||||||
return res.status(200).json(mutatedObj)
|
return res.status(200).json(mutatedObj)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// === UPDATE ===
|
||||||
|
|
||||||
|
|
||||||
|
interface UpdateOptions<T> extends OperationOptions<T> {
|
||||||
|
/**
|
||||||
|
* Prisma Where clause used to select the object to operate on.
|
||||||
|
*
|
||||||
|
* See also `findUnique`.
|
||||||
|
*/
|
||||||
|
which?: object,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prisma Update clause used to update the object if it exists.
|
||||||
|
*/
|
||||||
|
update: object,
|
||||||
|
|
||||||
|
after?: (model: T, obj: T) => Promise<T>,
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle a `PATCH` HTTP request where a single item is updated.
|
* Handle a `PATCH` HTTP request where a single item is updated.
|
||||||
*/
|
*/
|
||||||
function restInUpdate<T>(res: NextApiResponse<ApiResult<T>>, options: RestInOptions<T>) {
|
async function handleUpdate<T>(res: NextApiResponse<ApiResult<T>>, model: Model, options: UpdateOptions<T>) {
|
||||||
if (options.disallow?.update) return noRestForTheWicked(res)
|
await options.before?.(model)
|
||||||
const obj = options.model.update({
|
const obj = await model.update({
|
||||||
where: options.whereDetail,
|
where: options.which,
|
||||||
data: options.update,
|
data: options.update,
|
||||||
})
|
})
|
||||||
const mutatedObj = theButcher(obj, res, options, "update")
|
const mutatedObj = await options.after?.(model, obj) ?? obj
|
||||||
return res.status(200).json(mutatedObj)
|
return res.status(200).json(mutatedObj)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// === DESTROY ===
|
||||||
|
|
||||||
|
|
||||||
|
interface DestroyOptions<T> extends OperationOptions<T> {
|
||||||
|
/**
|
||||||
|
* Prisma Where clause used to select the object to operate on.
|
||||||
|
*
|
||||||
|
* See also `findUnique`.
|
||||||
|
*/
|
||||||
|
which?: object,
|
||||||
|
|
||||||
|
after?: (model: T) => Promise<void>,
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle a `DELETE` HTTP request where a single item is destroyed.
|
* Handle a `DELETE` HTTP request where a single item is destroyed.
|
||||||
*/
|
*/
|
||||||
function restInDestroy<T>(res: NextApiResponse<ApiResult<T>>, options: RestInOptions<T>) {
|
async function handleDestroy<T>(res: NextApiResponse<ApiResult<T>>, model: Model, options: DestroyOptions<T>) {
|
||||||
if (options.disallow?.destroy) return noRestForTheWicked(res)
|
await options.before?.(model)
|
||||||
options.model.delete({
|
await model.delete({
|
||||||
where: options.whereDetail,
|
where: options.which,
|
||||||
})
|
})
|
||||||
theButcher(undefined, res, options, "destroy")
|
await options.after?.(model)
|
||||||
return res.status(204).send("")
|
return res.status(204).send("")
|
||||||
}
|
}
|
Loading…
Reference in a new issue