Webhook maintanability update

This commit is contained in:
Atridad Lahiji 2023-08-14 21:16:11 -06:00
parent 47aa1a6ed1
commit 53310baf50
No known key found for this signature in database
8 changed files with 153 additions and 137 deletions

View file

@ -29,7 +29,7 @@
"dotenv": "^16.3.1",
"drizzle-orm": "^0.28.2",
"json2csv": "6.0.0-alpha.2",
"next": "^13.4.15",
"next": "^13.4.13",
"nextjs-cors": "^2.1.2",
"postcss": "^8.4.27",
"react": "18.2.0",

86
pnpm-lock.yaml generated
View file

@ -10,7 +10,7 @@ dependencies:
version: 2.1.1(react-dom@18.2.0)(react@18.2.0)
'@clerk/nextjs':
specifier: ^4.23.2
version: 4.23.2(next@13.4.15)(react-dom@18.2.0)(react@18.2.0)
version: 4.23.2(next@13.4.13)(react-dom@18.2.0)(react@18.2.0)
'@paralleldrive/cuid2':
specifier: ^2.2.2
version: 2.2.2
@ -28,7 +28,7 @@ dependencies:
version: 10.37.1(@trpc/server@10.37.1)
'@trpc/next':
specifier: 10.37.1
version: 10.37.1(@tanstack/react-query@4.32.6)(@trpc/client@10.37.1)(@trpc/react-query@10.37.1)(@trpc/server@10.37.1)(next@13.4.15)(react-dom@18.2.0)(react@18.2.0)
version: 10.37.1(@tanstack/react-query@4.32.6)(@trpc/client@10.37.1)(@trpc/react-query@10.37.1)(@trpc/server@10.37.1)(next@13.4.13)(react-dom@18.2.0)(react@18.2.0)
'@trpc/react-query':
specifier: 10.37.1
version: 10.37.1(@tanstack/react-query@4.32.6)(@trpc/client@10.37.1)(@trpc/server@10.37.1)(react-dom@18.2.0)(react@18.2.0)
@ -60,11 +60,11 @@ dependencies:
specifier: 6.0.0-alpha.2
version: 6.0.0-alpha.2
next:
specifier: ^13.4.15
version: 13.4.15(react-dom@18.2.0)(react@18.2.0)
specifier: ^13.4.13
version: 13.4.13(react-dom@18.2.0)(react@18.2.0)
nextjs-cors:
specifier: ^2.1.2
version: 2.1.2(next@13.4.15)
version: 2.1.2(next@13.4.13)
postcss:
specifier: ^8.4.27
version: 8.4.27
@ -231,7 +231,7 @@ packages:
tslib: 2.4.1
dev: false
/@clerk/nextjs@4.23.2(next@13.4.15)(react-dom@18.2.0)(react@18.2.0):
/@clerk/nextjs@4.23.2(next@13.4.13)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-99bSVu9r1E9MxybO/6mmPAufSKq4KU7SFeMVkylX7UF8sy5t/LE9cLHyc+9jitcCGgZNai9Om4sj1WIgkNOP8w==}
engines: {node: '>=14'}
peerDependencies:
@ -243,7 +243,7 @@ packages:
'@clerk/clerk-react': 4.23.2(react@18.2.0)
'@clerk/clerk-sdk-node': 4.12.2
'@clerk/types': 3.49.0
next: 13.4.15(react-dom@18.2.0)(react@18.2.0)
next: 13.4.13(react-dom@18.2.0)(react@18.2.0)
path-to-regexp: 6.2.1
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
@ -995,8 +995,8 @@ packages:
read-yaml-file: 1.1.0
dev: false
/@next/env@13.4.15:
resolution: {integrity: sha512-GQXUy/y5NW4VbrKQMbLjsuTykJ7+Jtb9zcKH1WrmgI1zG7yCoZQaoI65YFNksEh+9EPSHX6Xr7U1/StIIAOXog==}
/@next/env@13.4.13:
resolution: {integrity: sha512-fwz2QgVg08v7ZL7KmbQBLF2PubR/6zQdKBgmHEl3BCyWTEDsAQEijjw2gbFhI1tcKfLdOOJUXntz5vZ4S0Polg==}
dev: false
/@next/eslint-plugin-next@13.4.15:
@ -1005,8 +1005,8 @@ packages:
glob: 7.1.7
dev: true
/@next/swc-darwin-arm64@13.4.15:
resolution: {integrity: sha512-S4/5CwFxUtV+MnXXv5a+zGpx8Swm6WfzKYrcKbyYgl71Ek7bj0TrqZXASD2HeXIlHwr+0SJnnhSFcU09Y4tHjA==}
/@next/swc-darwin-arm64@13.4.13:
resolution: {integrity: sha512-ZptVhHjzUuivnXMNCJ6lER33HN7lC+rZ01z+PM10Ows21NHFYMvGhi5iXkGtBDk6VmtzsbqnAjnx4Oz5um0FjA==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [darwin]
@ -1014,8 +1014,8 @@ packages:
dev: false
optional: true
/@next/swc-darwin-x64@13.4.15:
resolution: {integrity: sha512-6bAi8s3vVRMFPU5qr4hZsfsQ0tzLU/Ig3tHjpJyjIN9AV3PMbTnX716P7hj1QioLfzZxIcUVnDczW7L45xHVfQ==}
/@next/swc-darwin-x64@13.4.13:
resolution: {integrity: sha512-t9nTiWCLApw8W4G1kqJyYP7y6/7lyal3PftmRturIxAIBlZss9wrtVN8nci50StDHmIlIDxfguYIEGVr9DbFTg==}
engines: {node: '>= 10'}
cpu: [x64]
os: [darwin]
@ -1023,8 +1023,8 @@ packages:
dev: false
optional: true
/@next/swc-linux-arm64-gnu@13.4.15:
resolution: {integrity: sha512-LpMjwqsQAj269h0PIqw8drRGGpyg6cRHi8JEAKtD/putcCalQWCplfUzjtML8jplwMN1lcdPDNtFIab53t8AzQ==}
/@next/swc-linux-arm64-gnu@13.4.13:
resolution: {integrity: sha512-xEHUqC8eqR5DHe8SOmMnDU1K3ggrJ28uIKltrQAwqFSSSmzjnN/XMocZkcVhuncuxYrpbri0iMQstRyRVdQVWg==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
@ -1032,8 +1032,8 @@ packages:
dev: false
optional: true
/@next/swc-linux-arm64-musl@13.4.15:
resolution: {integrity: sha512-zpV8C34OtLhu+1oiXIdDC576s88/TYxN325kipuL64Zs/j2kSEinR28mcLCZQTYI2g4OwtE3XIERy4vFoY3WiA==}
/@next/swc-linux-arm64-musl@13.4.13:
resolution: {integrity: sha512-sNf3MnLAm8rquSSAoeD9nVcdaDeRYOeey4stOWOyWIgbBDtP+C93amSgH/LPTDoUV7gNiU6f+ghepTjTjRgIUQ==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
@ -1041,8 +1041,8 @@ packages:
dev: false
optional: true
/@next/swc-linux-x64-gnu@13.4.15:
resolution: {integrity: sha512-Xk7QEfbX3zotvLmCoLzeGefbdyPpclCR8WyWHQOMMwUpftfmAuEVyU29WegcdfNCqYwc2QDXseVVI7xW/VwHCA==}
/@next/swc-linux-x64-gnu@13.4.13:
resolution: {integrity: sha512-WhcRaJJSHyx9OWmKjjz+OWHumiPZWRqmM/09Bt7Up4UqUJFFhGExeztR4trtv3rflvULatu9IH/nTV8fUUgaMA==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
@ -1050,8 +1050,8 @@ packages:
dev: false
optional: true
/@next/swc-linux-x64-musl@13.4.15:
resolution: {integrity: sha512-SxXxWBIUICkbHPUthg+T/FuRgOp75wE0e6eiLGs4P9Miq/P3kSXHv6x5LRnDreGDvYnKOlpzXlsXTxcPTRWttg==}
/@next/swc-linux-x64-musl@13.4.13:
resolution: {integrity: sha512-+Y4LLhOWWZQIDKVwr2R17lq2KSN0F1c30QVgGIWfnjjHpH8nrIWHEndhqYU+iFuW8It78CiJjQKTw4f51HD7jA==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
@ -1059,8 +1059,8 @@ packages:
dev: false
optional: true
/@next/swc-win32-arm64-msvc@13.4.15:
resolution: {integrity: sha512-Pu8zaW59XKbycyW/vzKRQOcbaZA2n3BK1zAFxv+hAO2bQ29FSGrtRb2nfhMDLwN0ggWIHKVKb+h4WMFHZkS9Qw==}
/@next/swc-win32-arm64-msvc@13.4.13:
resolution: {integrity: sha512-rWurdOR20uxjfqd1X9vDAgv0Jb26KjyL8akF9CBeFqX8rVaBAnW/Wf6A2gYEwyYY4Bai3T7p1kro6DFrsvBAAw==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [win32]
@ -1068,8 +1068,8 @@ packages:
dev: false
optional: true
/@next/swc-win32-ia32-msvc@13.4.15:
resolution: {integrity: sha512-V3y1Sc0X31mwK4cx0JQBi0wSTbPpmp/qRXjS0Vz3+2oSrbV06Oz27c/r2tW7ph0qhVp8+Jt03ZLnSc7KjjECiA==}
/@next/swc-win32-ia32-msvc@13.4.13:
resolution: {integrity: sha512-E8bSPwRuY5ibJ3CzLQmJEt8qaWrPYuUTwnrwygPUEWoLzD5YRx9SD37oXRdU81TgGwDzCxpl7z5Nqlfk50xAog==}
engines: {node: '>= 10'}
cpu: [ia32]
os: [win32]
@ -1077,8 +1077,8 @@ packages:
dev: false
optional: true
/@next/swc-win32-x64-msvc@13.4.15:
resolution: {integrity: sha512-mUGemqDIuD2PjnqEkqMpeI8cXOTVjedo9cqoaMkrOVcoK1IX/w7h4qYeniMUCImamsDAoLms86Fq9LzxT24StQ==}
/@next/swc-win32-x64-msvc@13.4.13:
resolution: {integrity: sha512-4KlyC6jWRubPnppgfYsNTPeWfGCxtWLh5vaOAW/kdzAk9widqho8Qb5S4K2vHmal1tsURi7Onk2MMCV1phvyqA==}
engines: {node: '>= 10'}
cpu: [x64]
os: [win32]
@ -1514,7 +1514,7 @@ packages:
'@trpc/server': 10.37.1
dev: false
/@trpc/next@10.37.1(@tanstack/react-query@4.32.6)(@trpc/client@10.37.1)(@trpc/react-query@10.37.1)(@trpc/server@10.37.1)(next@13.4.15)(react-dom@18.2.0)(react@18.2.0):
/@trpc/next@10.37.1(@tanstack/react-query@4.32.6)(@trpc/client@10.37.1)(@trpc/react-query@10.37.1)(@trpc/server@10.37.1)(next@13.4.13)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-0KEgr09mBfao56lkj7ZBfVOY86d3+bDH1o0zJkDHSH60Dp/hIJ7wLCnZJIhePlZxEwknCQjVeLsTy4Pqlu8NyQ==}
peerDependencies:
'@tanstack/react-query': ^4.18.0
@ -1529,7 +1529,7 @@ packages:
'@trpc/client': 10.37.1(@trpc/server@10.37.1)
'@trpc/react-query': 10.37.1(@tanstack/react-query@4.32.6)(@trpc/client@10.37.1)(@trpc/server@10.37.1)(react-dom@18.2.0)(react@18.2.0)
'@trpc/server': 10.37.1
next: 13.4.15(react-dom@18.2.0)(react@18.2.0)
next: 13.4.13(react-dom@18.2.0)(react@18.2.0)
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
react-ssr-prepass: 1.5.0(react@18.2.0)
@ -4470,8 +4470,8 @@ packages:
resolution: {integrity: sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==}
dev: true
/next@13.4.15(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-s4ZSBwZrvpY0IDzRtD7C5CY8FA/8ZIYqrJZHnwrf6mkUVA+Y+A6CtwBYyxml6VKgP/3DFNqCqkBQGInZuPHzmQ==}
/next@13.4.13(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-A3YVbVDNeXLhWsZ8Nf6IkxmNlmTNz0yVg186NJ97tGZqPDdPzTrHotJ+A1cuJm2XfuWPrKOUZILl5iBQkIf8Jw==}
engines: {node: '>=16.8.0'}
hasBin: true
peerDependencies:
@ -4485,7 +4485,7 @@ packages:
sass:
optional: true
dependencies:
'@next/env': 13.4.15
'@next/env': 13.4.13
'@swc/helpers': 0.5.1
busboy: 1.6.0
caniuse-lite: 1.0.30001520
@ -4496,27 +4496,27 @@ packages:
watchpack: 2.4.0
zod: 3.21.4
optionalDependencies:
'@next/swc-darwin-arm64': 13.4.15
'@next/swc-darwin-x64': 13.4.15
'@next/swc-linux-arm64-gnu': 13.4.15
'@next/swc-linux-arm64-musl': 13.4.15
'@next/swc-linux-x64-gnu': 13.4.15
'@next/swc-linux-x64-musl': 13.4.15
'@next/swc-win32-arm64-msvc': 13.4.15
'@next/swc-win32-ia32-msvc': 13.4.15
'@next/swc-win32-x64-msvc': 13.4.15
'@next/swc-darwin-arm64': 13.4.13
'@next/swc-darwin-x64': 13.4.13
'@next/swc-linux-arm64-gnu': 13.4.13
'@next/swc-linux-arm64-musl': 13.4.13
'@next/swc-linux-x64-gnu': 13.4.13
'@next/swc-linux-x64-musl': 13.4.13
'@next/swc-win32-arm64-msvc': 13.4.13
'@next/swc-win32-ia32-msvc': 13.4.13
'@next/swc-win32-x64-msvc': 13.4.13
transitivePeerDependencies:
- '@babel/core'
- babel-plugin-macros
dev: false
/nextjs-cors@2.1.2(next@13.4.15):
/nextjs-cors@2.1.2(next@13.4.13):
resolution: {integrity: sha512-2yOVivaaf2ILe4f/qY32hnj3oC77VCOsUQJQfhVMGsXE/YMEWUY2zy78sH9FKUCM7eG42/l3pDofIzMD781XGA==}
peerDependencies:
next: ^8.1.1-canary.54 || ^9.0.0 || ^10.0.0-0 || ^11.0.0 || ^12.0.0 || ^13.0.0
dependencies:
cors: 2.8.5
next: 13.4.15(react-dom@18.2.0)(react@18.2.0)
next: 13.4.13(react-dom@18.2.0)(react@18.2.0)
dev: false
/no-case@3.0.4:

View file

@ -0,0 +1,51 @@
import type { NextApiRequest, NextApiResponse } from "next";
import { validateRequest } from "~/server/unkey";
import {
onUserCreatedHandler,
onUserDeletedHandler,
} from "~/server/webhookHelpers";
import { WebhookEvent, WebhookEvents } from "~/utils/types";
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
const isValid = await validateRequest(req, res);
if (!isValid) {
return;
}
const requestBody = req.body as {
data: {
id: string;
email_addresses:
| [
{
email_address: string;
id: string;
verification: {
status: string;
strategy: string;
};
}
]
| null;
};
type: WebhookEvent;
};
switch (requestBody.type) {
case WebhookEvents.USER_CREATED:
onUserCreatedHandler(requestBody.data.id, res);
break;
case WebhookEvents.USER_DELETED:
onUserDeletedHandler(requestBody.data.id, res);
break;
default:
res.status(400).json({ error: "INVALID WEBHOOK EVENT" });
break;
}
}

View file

@ -1,55 +0,0 @@
import type { NextApiRequest, NextApiResponse } from "next";
import { env } from "~/env.mjs";
import { validateRequest } from "~/server/unkey";
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
const success = await validateRequest(req, res);
if (success) {
const requestBody = req.body as {
data: {
id: string;
email_addresses: [
{
email_address: string;
id: string;
verification: {
status: string;
strategy: string;
};
}
];
};
object: string;
type: string;
};
const userUpdateResponse = await fetch(
`https://api.clerk.com/v1/users/${requestBody.data.id}/metadata`,
{
method: "PATCH",
headers: {
Authorization: `Bearer ${env.CLERK_SECRET_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
public_metadata: {
isVIP: false,
isAdmin: false,
},
private_metadata: {},
unsafe_metadata: {},
}),
}
);
if (userUpdateResponse.status === 200) {
res.status(200).json({ result: "METADATA UPDATED" });
} else {
res.status(500).json({ error: "ERROR UPDATING METADATA" });
}
}
}

View file

@ -1,37 +0,0 @@
import { eq } from "drizzle-orm";
import type { NextApiRequest, NextApiResponse } from "next";
import { db } from "~/server/db";
import { logs, rooms, votes } from "~/server/schema";
import { validateRequest } from "~/server/unkey";
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
const success = await validateRequest(req, res);
if (success) {
const requestBody = req.body as {
data: {
deleted: string;
id: string;
object: string;
};
object: string;
type: string;
};
const deletedRoom = await db
.delete(rooms)
.where(eq(rooms.userId, requestBody.data.id));
if (deletedRoom.rowsAffected > 0) {
await db.delete(logs).where(eq(logs.userId, requestBody.data.id));
await db.delete(votes).where(eq(votes.userId, requestBody.data.id));
res.status(200).json({ result: "USER DELETED" });
} else {
res.status(500).json({ result: "ERROR DELETING USER" });
}
}
}

View file

@ -28,7 +28,6 @@ export const validateRequest = async (
}
}
// Error if the key is not valid
if (!isValidKey) {
res.status(403).json({ error: "UNAUTHORIZED" });
}

View file

@ -0,0 +1,52 @@
import { eq } from "drizzle-orm";
import { db } from "./db";
import { logs, rooms, votes } from "./schema";
import { env } from "~/env.mjs";
import { NextApiResponse } from "next";
export const onUserDeletedHandler = async (
userId: string,
res: NextApiResponse
) => {
const deletedRoom = await db.delete(rooms).where(eq(rooms.userId, userId));
if (deletedRoom.rowsAffected > 0) {
await db.delete(logs).where(eq(logs.userId, userId));
await db.delete(votes).where(eq(votes.userId, userId));
// res.status(200).json({ result: "USER DELETED" });
res.status(200).json({ result: "USER DELETED" });
} else {
res.status(404).json({ error: "USER WITH THIS ID NOT FOUND" });
}
};
export const onUserCreatedHandler = async (
userId: string,
res: NextApiResponse
) => {
const userUpdateResponse = await fetch(
`https://api.clerk.com/v1/users/${userId}/metadata`,
{
method: "PATCH",
headers: {
Authorization: `Bearer ${env.CLERK_SECRET_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
public_metadata: {
isVIP: false,
isAdmin: false,
},
private_metadata: {},
unsafe_metadata: {},
}),
}
);
if (userUpdateResponse.ok) {
res.status(200).json({ result: "USER CREATED" });
} else {
res.status(404).json({ error: "USER WITH THIS ID NOT FOUND" });
}
};

View file

@ -8,6 +8,12 @@ export const EventTypes = {
} as const;
export type EventType = BetterEnum<typeof EventTypes>;
export const WebhookEvents = {
USER_CREATED: "user.created",
USER_DELETED: "user.deleted",
} as const;
export type WebhookEvent = BetterEnum<typeof WebhookEvents>;
export interface PresenceItem {
name: string;
image: string;