API updates
This commit is contained in:
parent
c4b8488bc5
commit
c3222f0bdf
4 changed files with 57 additions and 20 deletions
|
@ -2,6 +2,7 @@ import { roomRouter } from "~/server/api/routers/room";
|
||||||
import { createTRPCRouter } from "~/server/api/trpc";
|
import { createTRPCRouter } from "~/server/api/trpc";
|
||||||
import { voteRouter } from "./routers/vote";
|
import { voteRouter } from "./routers/vote";
|
||||||
import { restRouter } from "./routers/rest";
|
import { restRouter } from "./routers/rest";
|
||||||
|
import { webhookRouter } from "./routers/webhook";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is the primary router for your server.
|
* This is the primary router for your server.
|
||||||
|
@ -12,6 +13,7 @@ export const appRouter = createTRPCRouter({
|
||||||
room: roomRouter,
|
room: roomRouter,
|
||||||
vote: voteRouter,
|
vote: voteRouter,
|
||||||
rest: restRouter,
|
rest: restRouter,
|
||||||
|
webhook: webhookRouter,
|
||||||
});
|
});
|
||||||
|
|
||||||
// export type definition of API
|
// export type definition of API
|
||||||
|
|
|
@ -1,27 +1,11 @@
|
||||||
import { validateApiKey } from "~/server/unkey";
|
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { createTRPCRouter, publicProcedure } from "~/server/api/trpc";
|
import { createTRPCRouter, publicProcedure } from "~/server/api/trpc";
|
||||||
import { TRPCError } from "@trpc/server";
|
|
||||||
import { fetchCache, setCache } from "~/server/redis";
|
import { fetchCache, setCache } from "~/server/redis";
|
||||||
import { sql } from "drizzle-orm";
|
import { sql } from "drizzle-orm";
|
||||||
import { rooms, votes } from "~/server/schema";
|
import { rooms, votes } from "~/server/schema";
|
||||||
|
|
||||||
export const restRouter = createTRPCRouter({
|
export const restRouter = createTRPCRouter({
|
||||||
dbWarmer: publicProcedure
|
|
||||||
.meta({ openapi: { method: "POST", path: "/rest/dbwarmer" } })
|
|
||||||
.input(z.object({ key: z.string() }))
|
|
||||||
.output(z.string())
|
|
||||||
.query(async ({ ctx, input }) => {
|
|
||||||
const isValidKey = await validateApiKey(input.key);
|
|
||||||
if (isValidKey) {
|
|
||||||
await ctx.db.query.votes.findMany();
|
|
||||||
return "Toasted the DB";
|
|
||||||
} else {
|
|
||||||
throw new TRPCError({ code: "UNAUTHORIZED" });
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
|
|
||||||
voteCount: publicProcedure
|
voteCount: publicProcedure
|
||||||
.meta({ openapi: { method: "GET", path: "/rest/votes/count" } })
|
.meta({ openapi: { method: "GET", path: "/rest/votes/count" } })
|
||||||
.input(z.void())
|
.input(z.void())
|
||||||
|
|
24
src/server/api/routers/webhook.ts
Normal file
24
src/server/api/routers/webhook.ts
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { createTRPCRouter, keyProtectedProcedure } from "~/server/api/trpc";
|
||||||
|
|
||||||
|
export const webhookRouter = createTRPCRouter({
|
||||||
|
onUserDelete: keyProtectedProcedure
|
||||||
|
.meta({ openapi: { method: "POST", path: "/webhook/user/delete" } })
|
||||||
|
.input(
|
||||||
|
z.object({
|
||||||
|
data: z.object({
|
||||||
|
deleted: z.boolean(),
|
||||||
|
id: z.string(),
|
||||||
|
object: z.string(),
|
||||||
|
}),
|
||||||
|
object: z.string(),
|
||||||
|
type: z.string(),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.output(z.string())
|
||||||
|
.query(({ input }) => {
|
||||||
|
console.log(input.data.deleted);
|
||||||
|
return `Deleted: ${input.data.deleted}`;
|
||||||
|
}),
|
||||||
|
});
|
|
@ -25,8 +25,9 @@ import type {
|
||||||
|
|
||||||
import { db } from "../db";
|
import { db } from "../db";
|
||||||
|
|
||||||
interface AuthContext {
|
interface ContextType {
|
||||||
auth: SignedInAuthObject | SignedOutAuthObject;
|
auth: SignedInAuthObject | SignedOutAuthObject;
|
||||||
|
key: string | null;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* This helper generates the "internals" for a tRPC context. If you need to use
|
* This helper generates the "internals" for a tRPC context. If you need to use
|
||||||
|
@ -37,8 +38,9 @@ interface AuthContext {
|
||||||
* - trpc's `createSSGHelpers` where we don't have req/res
|
* - trpc's `createSSGHelpers` where we don't have req/res
|
||||||
* @see https://create.t3.gg/en/usage/trpc#-servertrpccontextts
|
* @see https://create.t3.gg/en/usage/trpc#-servertrpccontextts
|
||||||
*/
|
*/
|
||||||
const createInnerTRPCContext = ({ auth }: AuthContext) => {
|
const createInnerTRPCContext = ({ auth, key }: ContextType) => {
|
||||||
return {
|
return {
|
||||||
|
key,
|
||||||
auth,
|
auth,
|
||||||
db,
|
db,
|
||||||
};
|
};
|
||||||
|
@ -49,8 +51,19 @@ const createInnerTRPCContext = ({ auth }: AuthContext) => {
|
||||||
* process every request that goes through your tRPC endpoint
|
* process every request that goes through your tRPC endpoint
|
||||||
* @link https://trpc.io/docs/context
|
* @link https://trpc.io/docs/context
|
||||||
*/
|
*/
|
||||||
export const createTRPCContext = (opts: CreateNextContextOptions) => {
|
export const createTRPCContext = async (opts: CreateNextContextOptions) => {
|
||||||
return createInnerTRPCContext({ auth: getAuth(opts.req) });
|
let keyValue: string | null = null;
|
||||||
|
|
||||||
|
if (opts.req.headers.authorization) {
|
||||||
|
const key = opts.req.headers.authorization.split("Bearer ").at(1);
|
||||||
|
if (key) {
|
||||||
|
const isValidKey = await validateApiKey(key);
|
||||||
|
if (isValidKey) {
|
||||||
|
keyValue = key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return createInnerTRPCContext({ auth: getAuth(opts.req), key: keyValue });
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -62,6 +75,7 @@ export const createTRPCContext = (opts: CreateNextContextOptions) => {
|
||||||
import { initTRPC, TRPCError } from "@trpc/server";
|
import { initTRPC, TRPCError } from "@trpc/server";
|
||||||
import superjson from "superjson";
|
import superjson from "superjson";
|
||||||
import type { OpenApiMeta } from "trpc-openapi";
|
import type { OpenApiMeta } from "trpc-openapi";
|
||||||
|
import { validateApiKey } from "../unkey";
|
||||||
|
|
||||||
const t = initTRPC
|
const t = initTRPC
|
||||||
.context<typeof createTRPCContext>()
|
.context<typeof createTRPCContext>()
|
||||||
|
@ -85,6 +99,18 @@ const isAuthed = t.middleware(({ next, ctx }) => {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const isKeyAuthed = t.middleware(({ next, ctx }) => {
|
||||||
|
if (!ctx.key) {
|
||||||
|
throw new TRPCError({ code: "UNAUTHORIZED" });
|
||||||
|
}
|
||||||
|
|
||||||
|
return next({
|
||||||
|
ctx: {
|
||||||
|
key: ctx.key,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
/**
|
/**
|
||||||
* 3. ROUTER & PROCEDURE (THE IMPORTANT BIT)
|
* 3. ROUTER & PROCEDURE (THE IMPORTANT BIT)
|
||||||
*
|
*
|
||||||
|
@ -107,3 +133,4 @@ export const createTRPCRouter = t.router;
|
||||||
*/
|
*/
|
||||||
export const publicProcedure = t.procedure;
|
export const publicProcedure = t.procedure;
|
||||||
export const protectedProcedure = t.procedure.use(isAuthed);
|
export const protectedProcedure = t.procedure.use(isAuthed);
|
||||||
|
export const keyProtectedProcedure = t.procedure.use(isKeyAuthed);
|
||||||
|
|
Loading…
Add table
Reference in a new issue