API Keys
This commit is contained in:
parent
e430763d90
commit
6b7c15413c
11 changed files with 77 additions and 37 deletions
|
@ -27,6 +27,9 @@ NEXT_PUBLIC_ABLY_PUBLIC_KEY=""
|
||||||
# Email
|
# Email
|
||||||
RESEND_API_KEY=""
|
RESEND_API_KEY=""
|
||||||
|
|
||||||
|
# Unkey
|
||||||
|
UNKEY_ROOT_KEY=""
|
||||||
|
|
||||||
# Misc
|
# Misc
|
||||||
APP_ENV=""
|
APP_ENV=""
|
||||||
NEXT_PUBLIC_APP_ENV=""
|
NEXT_PUBLIC_APP_ENV=""
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
"@trpc/next": "10.37.1",
|
"@trpc/next": "10.37.1",
|
||||||
"@trpc/react-query": "10.37.1",
|
"@trpc/react-query": "10.37.1",
|
||||||
"@trpc/server": "10.37.1",
|
"@trpc/server": "10.37.1",
|
||||||
|
"@unkey/api": "^0.5.0",
|
||||||
"@upstash/ratelimit": "^0.4.3",
|
"@upstash/ratelimit": "^0.4.3",
|
||||||
"@upstash/redis": "^1.22.0",
|
"@upstash/redis": "^1.22.0",
|
||||||
"ably": "^1.2.42",
|
"ably": "^1.2.42",
|
||||||
|
|
7
pnpm-lock.yaml
generated
7
pnpm-lock.yaml
generated
|
@ -32,6 +32,9 @@ dependencies:
|
||||||
'@trpc/server':
|
'@trpc/server':
|
||||||
specifier: 10.37.1
|
specifier: 10.37.1
|
||||||
version: 10.37.1
|
version: 10.37.1
|
||||||
|
'@unkey/api':
|
||||||
|
specifier: ^0.5.0
|
||||||
|
version: 0.5.0
|
||||||
'@upstash/ratelimit':
|
'@upstash/ratelimit':
|
||||||
specifier: ^0.4.3
|
specifier: ^0.4.3
|
||||||
version: 0.4.3
|
version: 0.4.3
|
||||||
|
@ -1359,6 +1362,10 @@ packages:
|
||||||
eslint-visitor-keys: 3.4.2
|
eslint-visitor-keys: 3.4.2
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/@unkey/api@0.5.0:
|
||||||
|
resolution: {integrity: sha512-C3q2ITvBR5jpK68KNAhhqcR+BzRhXW9ppubdrbaXawEnwxE+F+I2qMU+KqkCM1MbP+ZUv4/E1gX8cobH4X0sWg==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/@upstash/core-analytics@0.0.6:
|
/@upstash/core-analytics@0.0.6:
|
||||||
resolution: {integrity: sha512-cpPSR0XJAJs4Ddz9nq3tINlPS5aLfWVCqhhtHnXt4p7qr5+/Znlt1Es736poB/9rnl1hAHrOsOvVj46NEXcVqA==}
|
resolution: {integrity: sha512-cpPSR0XJAJs4Ddz9nq3tINlPS5aLfWVCqhhtHnXt4p7qr5+/Znlt1Es736poB/9rnl1hAHrOsOvVj46NEXcVqA==}
|
||||||
engines: {node: '>=16.0.0'}
|
engines: {node: '>=16.0.0'}
|
||||||
|
|
|
@ -30,6 +30,7 @@ const server = z.object({
|
||||||
ABLY_PRIVATE_KEY: z.string(),
|
ABLY_PRIVATE_KEY: z.string(),
|
||||||
APP_ENV: z.string(),
|
APP_ENV: z.string(),
|
||||||
RESEND_API_KEY: z.string(),
|
RESEND_API_KEY: z.string(),
|
||||||
|
UNKEY_ROOT_KEY: z.string(),
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -66,6 +67,7 @@ const processEnv = {
|
||||||
APP_ENV: process.env.APP_ENV,
|
APP_ENV: process.env.APP_ENV,
|
||||||
NEXT_PUBLIC_APP_ENV: process.env.NEXT_PUBLIC_APP_ENV,
|
NEXT_PUBLIC_APP_ENV: process.env.NEXT_PUBLIC_APP_ENV,
|
||||||
RESEND_API_KEY: process.env.RESEND_API_KEY,
|
RESEND_API_KEY: process.env.RESEND_API_KEY,
|
||||||
|
UNKEY_ROOT_KEY: process.env.UNKEY_ROOT_KEY,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Don't touch the part below
|
// Don't touch the part below
|
||||||
|
|
|
@ -1,13 +0,0 @@
|
||||||
import { prisma } from "~/server/db";
|
|
||||||
import type { NextApiRequest, NextApiResponse } from "next";
|
|
||||||
|
|
||||||
export default async function handler(
|
|
||||||
req: NextApiRequest,
|
|
||||||
res: NextApiResponse
|
|
||||||
) {
|
|
||||||
console.log("Cron Request: ", req);
|
|
||||||
// the most useless call... nothing exists here
|
|
||||||
await prisma.verificationToken.findMany();
|
|
||||||
|
|
||||||
res.status(200);
|
|
||||||
}
|
|
|
@ -3,6 +3,7 @@ import { createTRPCRouter } from "~/server/api/trpc";
|
||||||
import { sessionRouter } from "./routers/session";
|
import { sessionRouter } from "./routers/session";
|
||||||
import { userRouter } from "./routers/user";
|
import { userRouter } from "./routers/user";
|
||||||
import { voteRouter } from "./routers/vote";
|
import { voteRouter } from "./routers/vote";
|
||||||
|
import { hookRouter } from "./routers/hook";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is the primary router for your server.
|
* This is the primary router for your server.
|
||||||
|
@ -14,6 +15,7 @@ export const appRouter = createTRPCRouter({
|
||||||
vote: voteRouter,
|
vote: voteRouter,
|
||||||
user: userRouter,
|
user: userRouter,
|
||||||
session: sessionRouter,
|
session: sessionRouter,
|
||||||
|
hook: hookRouter,
|
||||||
});
|
});
|
||||||
|
|
||||||
// export type definition of API
|
// export type definition of API
|
||||||
|
|
21
src/server/api/routers/hook.ts
Normal file
21
src/server/api/routers/hook.ts
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
import { validateApiKey } from "~/server/unkey";
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { createTRPCRouter, publicProcedure } from "~/server/api/trpc";
|
||||||
|
import { TRPCError } from "@trpc/server";
|
||||||
|
|
||||||
|
export const hookRouter = createTRPCRouter({
|
||||||
|
dbWarmer: publicProcedure
|
||||||
|
.meta({ openapi: { method: "GET", path: "/rest/test" } })
|
||||||
|
.input(z.object({ key: z.string() }))
|
||||||
|
.output(z.string())
|
||||||
|
.query(async ({ ctx, input }) => {
|
||||||
|
const isValidKey = await validateApiKey(input.key);
|
||||||
|
if (isValidKey) {
|
||||||
|
await ctx.prisma.verificationToken.findMany();
|
||||||
|
return "Toasted the DB";
|
||||||
|
} else {
|
||||||
|
throw new TRPCError({ code: "UNAUTHORIZED" });
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
});
|
|
@ -10,23 +10,19 @@ import {
|
||||||
import { fetchCache, invalidateCache, setCache } from "~/server/redis";
|
import { fetchCache, invalidateCache, setCache } from "~/server/redis";
|
||||||
|
|
||||||
export const voteRouter = createTRPCRouter({
|
export const voteRouter = createTRPCRouter({
|
||||||
countAll: adminProcedure
|
countAll: adminProcedure.query(async ({ ctx }) => {
|
||||||
.input(z.void())
|
const cachedResult = await fetchCache<number>(`kv_votecount_admin`);
|
||||||
.output(z.number())
|
|
||||||
.meta({ openapi: { method: "GET", path: "/votes/count" } })
|
|
||||||
.query(async ({ ctx }) => {
|
|
||||||
const cachedResult = await fetchCache<number>(`kv_votecount_admin`);
|
|
||||||
|
|
||||||
if (cachedResult) {
|
if (cachedResult) {
|
||||||
return cachedResult;
|
return cachedResult;
|
||||||
} else {
|
} else {
|
||||||
const votesCount = await ctx.prisma.vote.count();
|
const votesCount = await ctx.prisma.vote.count();
|
||||||
|
|
||||||
await setCache(`kv_votecount_admin`, votesCount);
|
await setCache(`kv_votecount_admin`, votesCount);
|
||||||
|
|
||||||
return votesCount;
|
return votesCount;
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
getAllByRoomId: protectedProcedure
|
getAllByRoomId: protectedProcedure
|
||||||
.input(z.object({ roomId: z.string() }))
|
.input(z.object({ roomId: z.string() }))
|
||||||
.query(async ({ ctx, input }) => {
|
.query(async ({ ctx, input }) => {
|
||||||
|
|
|
@ -107,7 +107,7 @@ export const createTRPCRouter = t.router;
|
||||||
export const publicProcedure = t.procedure;
|
export const publicProcedure = t.procedure;
|
||||||
|
|
||||||
/** Reusable middleware that enforces users are logged in before running the procedure. */
|
/** Reusable middleware that enforces users are logged in before running the procedure. */
|
||||||
const enforceUserIsAuthed = t.middleware(async ({ ctx, next }) => {
|
const enforceAuthSession = t.middleware(async ({ ctx, next }) => {
|
||||||
// Auth
|
// Auth
|
||||||
if (!ctx.session || !ctx.session.user) {
|
if (!ctx.session || !ctx.session.user) {
|
||||||
throw new TRPCError({ code: "UNAUTHORIZED" });
|
throw new TRPCError({ code: "UNAUTHORIZED" });
|
||||||
|
@ -146,6 +146,20 @@ const enforceAdminRole = t.middleware(async ({ ctx, next }) => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// const enforceApiToken = t.middleware(async ({ ctx, next, path }) => {
|
||||||
|
// const res = await unkey.keys.verify({
|
||||||
|
// key: ""
|
||||||
|
// })
|
||||||
|
// if (!ctx.session || !ctx.session.user || !ctx.session?.user.isAdmin)
|
||||||
|
// throw new TRPCError({ code: "UNAUTHORIZED" });
|
||||||
|
|
||||||
|
// return next({
|
||||||
|
// ctx: {
|
||||||
|
// session: { ...ctx.session, user: ctx.session.user },
|
||||||
|
// },
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Protected (authenticated) procedure
|
* Protected (authenticated) procedure
|
||||||
*
|
*
|
||||||
|
@ -154,6 +168,6 @@ const enforceAdminRole = t.middleware(async ({ ctx, next }) => {
|
||||||
*
|
*
|
||||||
* @see https://trpc.io/docs/procedures
|
* @see https://trpc.io/docs/procedures
|
||||||
*/
|
*/
|
||||||
export const protectedProcedure = t.procedure.use(enforceUserIsAuthed);
|
export const protectedProcedure = t.procedure.use(enforceAuthSession);
|
||||||
|
|
||||||
export const adminProcedure = t.procedure.use(enforceAdminRole);
|
export const adminProcedure = t.procedure.use(enforceAdminRole);
|
||||||
|
|
15
src/server/unkey.ts
Normal file
15
src/server/unkey.ts
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
import { Unkey } from "@unkey/api";
|
||||||
|
import { env } from "~/env.mjs";
|
||||||
|
|
||||||
|
export const unkey = new Unkey({ token: env.UNKEY_ROOT_KEY });
|
||||||
|
|
||||||
|
export const validateApiKey = async (key: string) => {
|
||||||
|
try {
|
||||||
|
const res = await unkey.keys.verify({
|
||||||
|
key,
|
||||||
|
});
|
||||||
|
return res.valid;
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
|
@ -1,8 +0,0 @@
|
||||||
{
|
|
||||||
"crons": [
|
|
||||||
{
|
|
||||||
"path": "/api/cron/dbWarmer",
|
|
||||||
"schedule": "0 0 */6 * *"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
Loading…
Add table
Reference in a new issue