From 74498e71178b003ec3638c09b6f9700c54e6cd23 Mon Sep 17 00:00:00 2001 From: Joshua Silva <72359611+joshuasilva414@users.noreply.github.com> Date: Fri, 3 Jan 2025 14:48:30 -0600 Subject: [PATCH] Redis Refactor (#155) --- apps/web/src/actions/admin/modify-nav-item.ts | 13 ++--- .../src/actions/admin/registration-actions.ts | 18 ++++--- .../app/admin/toggles/registration/page.tsx | 9 +--- apps/web/src/app/register/page.tsx | 4 +- apps/web/src/app/rsvp/page.tsx | 11 +++-- .../src/app/sign-up/[[...sign-up]]/page.tsx | 4 +- apps/web/src/env.ts | 1 + apps/web/src/lib/utils/server/redis.ts | 47 ++++++++++++++++++- package.json | 2 +- turbo.json | 1 + 10 files changed, 78 insertions(+), 32 deletions(-) diff --git a/apps/web/src/actions/admin/modify-nav-item.ts b/apps/web/src/actions/admin/modify-nav-item.ts index 5dc3a48e..2b6b05ad 100644 --- a/apps/web/src/actions/admin/modify-nav-item.ts +++ b/apps/web/src/actions/admin/modify-nav-item.ts @@ -2,7 +2,7 @@ import { z } from "zod"; import { adminAction } from "@/lib/safe-action"; -import { kv } from "@vercel/kv"; +import { redisSAdd, redisHSet, removeNavItem } from "@/lib/utils/server/redis"; import { revalidatePath } from "next/cache"; const metadataSchema = z.object({ @@ -16,8 +16,8 @@ const navAdminPage = "/admin/toggles/landing"; export const setItem = adminAction .schema(metadataSchema) .action(async ({ parsedInput: { name, url }, ctx: { user, userId } }) => { - await kv.sadd("config:navitemslist", encodeURIComponent(name)); - await kv.hset(`config:navitems:${encodeURIComponent(name)}`, { + await redisSAdd("config:navitemslist", encodeURIComponent(name)); + await redisHSet(`config:navitems:${encodeURIComponent(name)}`, { url, name, enabled: true, @@ -29,10 +29,7 @@ export const setItem = adminAction export const removeItem = adminAction .schema(z.string()) .action(async ({ parsedInput: name, ctx: { user, userId } }) => { - const pipe = kv.pipeline(); - pipe.srem("config:navitemslist", encodeURIComponent(name)); - pipe.del(`config:navitems:${encodeURIComponent(name)}`); - await pipe.exec(); + await removeNavItem(name); // await new Promise((resolve) => setTimeout(resolve, 1500)); revalidatePath(navAdminPage); return { success: true }; @@ -45,7 +42,7 @@ export const toggleItem = adminAction parsedInput: { name, statusToSet }, ctx: { user, userId }, }) => { - await kv.hset(`config:navitems:${encodeURIComponent(name)}`, { + await redisHSet(`config:navitems:${encodeURIComponent(name)}`, { enabled: statusToSet, }); revalidatePath(navAdminPage); diff --git a/apps/web/src/actions/admin/registration-actions.ts b/apps/web/src/actions/admin/registration-actions.ts index a3c52211..1d069199 100644 --- a/apps/web/src/actions/admin/registration-actions.ts +++ b/apps/web/src/actions/admin/registration-actions.ts @@ -2,7 +2,7 @@ import { z } from "zod"; import { adminAction } from "@/lib/safe-action"; -import { kv } from "@vercel/kv"; +import { redisSet } from "@/lib/utils/server/redis"; import { revalidatePath } from "next/cache"; const defaultRegistrationToggleSchema = z.object({ @@ -16,7 +16,7 @@ const defaultRSVPLimitSchema = z.object({ export const toggleRegistrationEnabled = adminAction .schema(defaultRegistrationToggleSchema) .action(async ({ parsedInput: { enabled }, ctx: { user, userId } }) => { - await kv.set("config:registration:registrationEnabled", enabled); + await redisSet("config:registration:registrationEnabled", enabled); revalidatePath("/admin/toggles/registration"); return { success: true, statusSet: enabled }; }); @@ -24,7 +24,10 @@ export const toggleRegistrationEnabled = adminAction export const toggleRegistrationMessageEnabled = adminAction .schema(defaultRegistrationToggleSchema) .action(async ({ parsedInput: { enabled }, ctx: { user, userId } }) => { - await kv.set("config:registration:registrationMessageEnabled", enabled); + await redisSet( + "config:registration:registrationMessageEnabled", + enabled, + ); revalidatePath("/admin/toggles/registration"); return { success: true, statusSet: enabled }; }); @@ -32,7 +35,10 @@ export const toggleRegistrationMessageEnabled = adminAction export const toggleSecretRegistrationEnabled = adminAction .schema(defaultRegistrationToggleSchema) .action(async ({ parsedInput: { enabled }, ctx: { user, userId } }) => { - await kv.set("config:registration:secretRegistrationEnabled", enabled); + await redisSet( + "config:registration:secretRegistrationEnabled", + enabled, + ); revalidatePath("/admin/toggles/registration"); return { success: true, statusSet: enabled }; }); @@ -40,7 +46,7 @@ export const toggleSecretRegistrationEnabled = adminAction export const toggleRSVPs = adminAction .schema(defaultRegistrationToggleSchema) .action(async ({ parsedInput: { enabled }, ctx: { user, userId } }) => { - await kv.set("config:registration:allowRSVPs", enabled); + await redisSet("config:registration:allowRSVPs", enabled); revalidatePath("/admin/toggles/registration"); return { success: true, statusSet: enabled }; }); @@ -48,7 +54,7 @@ export const toggleRSVPs = adminAction export const setRSVPLimit = adminAction .schema(defaultRSVPLimitSchema) .action(async ({ parsedInput: { rsvpLimit }, ctx: { user, userId } }) => { - await kv.set("config:registration:maxRSVPs", rsvpLimit); + await redisSet("config:registration:maxRSVPs", rsvpLimit); revalidatePath("/admin/toggles/registration"); return { success: true, statusSet: rsvpLimit }; }); diff --git a/apps/web/src/app/admin/toggles/registration/page.tsx b/apps/web/src/app/admin/toggles/registration/page.tsx index 8d7dbf42..841fe64d 100644 --- a/apps/web/src/app/admin/toggles/registration/page.tsx +++ b/apps/web/src/app/admin/toggles/registration/page.tsx @@ -1,20 +1,15 @@ import { RegistrationToggles } from "@/components/admin/toggles/RegistrationSettings"; -import { kv } from "@vercel/kv"; +import { redisMGet } from "@/lib/utils/server/redis"; import { parseRedisBoolean, parseRedisNumber } from "@/lib/utils/server/redis"; import c from "config"; export default async function Page() { - const pipe = kv.pipeline(); - pipe.get("config:registration:registrationEnabled"); - pipe.get("config:registration:secretRegistrationEnabled"); - // const result = await pipe.exec(); - const [ defaultRegistrationEnabled, defaultSecretRegistrationEnabled, defaultRSVPsEnabled, defaultRSVPLimit, - ]: (string | null)[] = await kv.mget( + ]: (string | null)[] = await redisMGet( "config:registration:registrationEnabled", "config:registration:secretRegistrationEnabled", "config:registration:allowRSVPs", diff --git a/apps/web/src/app/register/page.tsx b/apps/web/src/app/register/page.tsx index db808b98..26a24250 100644 --- a/apps/web/src/app/register/page.tsx +++ b/apps/web/src/app/register/page.tsx @@ -4,7 +4,7 @@ import { auth, currentUser } from "@clerk/nextjs"; import { redirect } from "next/navigation"; import Navbar from "@/components/shared/Navbar"; import Link from "next/link"; -import { kv } from "@vercel/kv"; +import { redisMGet } from "@/lib/utils/server/redis"; import { parseRedisBoolean } from "@/lib/utils/server/redis"; import { Button } from "@/components/shadcn/ui/button"; import { getUser } from "db/functions"; @@ -22,7 +22,7 @@ export default async function Page() { const [defaultRegistrationEnabled, defaultSecretRegistrationEnabled]: ( | string | null - )[] = await kv.mget( + )[] = await redisMGet( "config:registration:registrationEnabled", "config:registration:secretRegistrationEnabled", ); diff --git a/apps/web/src/app/rsvp/page.tsx b/apps/web/src/app/rsvp/page.tsx index 4de7c7bf..841f315c 100644 --- a/apps/web/src/app/rsvp/page.tsx +++ b/apps/web/src/app/rsvp/page.tsx @@ -7,8 +7,11 @@ import { eq } from "db/drizzle"; import { userCommonData } from "db/schema"; import ClientToast from "@/components/shared/ClientToast"; import { SignedOut, RedirectToSignIn } from "@clerk/nextjs"; -import { kv } from "@vercel/kv"; -import { parseRedisBoolean, parseRedisNumber } from "@/lib/utils/server/redis"; +import { + parseRedisBoolean, + parseRedisNumber, + redisGet, +} from "@/lib/utils/server/redis"; import Link from "next/link"; import { Button } from "@/components/shadcn/ui/button"; import { getUser } from "db/functions"; @@ -41,7 +44,7 @@ export default async function RsvpPage({ } const rsvpEnabled = parseRedisBoolean( - (await kv.get("config:registration:allowRSVPs")) as + (await redisGet("config:registration:allowRSVPs")) as | string | boolean | null @@ -53,7 +56,7 @@ export default async function RsvpPage({ if (rsvpEnabled === true) { const rsvpLimit = parseRedisNumber( - await kv.get("config:registration:maxRSVPs"), + await redisGet("config:registration:maxRSVPs"), c.rsvpDefaultLimit, ); diff --git a/apps/web/src/app/sign-up/[[...sign-up]]/page.tsx b/apps/web/src/app/sign-up/[[...sign-up]]/page.tsx index 057a6407..49244c2e 100644 --- a/apps/web/src/app/sign-up/[[...sign-up]]/page.tsx +++ b/apps/web/src/app/sign-up/[[...sign-up]]/page.tsx @@ -1,5 +1,5 @@ import { SignUp } from "@clerk/nextjs"; -import { kv } from "@vercel/kv"; +import { redisMGet } from "@/lib/utils/server/redis"; import { parseRedisBoolean } from "@/lib/utils/server/redis"; import c from "config"; import { Button } from "@/components/shadcn/ui/button"; @@ -9,7 +9,7 @@ export default async function Page() { const [defaultRegistrationEnabled, defaultSecretRegistrationEnabled]: ( | string | null - )[] = await kv.mget( + )[] = await redisMGet( "config:registration:registrationEnabled", "config:registration:secretRegistrationEnabled", ); diff --git a/apps/web/src/env.ts b/apps/web/src/env.ts index 9b6c7a54..0130bcfe 100644 --- a/apps/web/src/env.ts +++ b/apps/web/src/env.ts @@ -10,6 +10,7 @@ export const env = createEnv({ AWS_SES_EMAIL_FROM: z.string().min(1), INTERNAL_AUTH_KEY: z.string().min(64), BOT_API_URL: z.string().min(1), + HK_ENV: z.string().min(1), NODE_ENV: z .enum(["development", "test", "production"]) .default("development"), diff --git a/apps/web/src/lib/utils/server/redis.ts b/apps/web/src/lib/utils/server/redis.ts index d25f55c5..90b8d6ba 100644 --- a/apps/web/src/lib/utils/server/redis.ts +++ b/apps/web/src/lib/utils/server/redis.ts @@ -1,8 +1,40 @@ import { kv } from "@vercel/kv"; import type { NavItemToggleType } from "@/validators/shared/navitemtoggle"; +export function includeEnvPrefix(key: string) { + return `${process.env.HK_ENV}_${key}`; +} + +export async function redisSAdd(key: string, value: string): Promise { + return kv.sadd(includeEnvPrefix(key), value); +} + +export async function redisHSet( + key: string, + value: Record, +): Promise { + return kv.hset(includeEnvPrefix(key), value); +} + +export async function redisSet( + key: string, + value: TData, +): Promise { + return kv.set(includeEnvPrefix(key), value); +} + +export async function redisGet(key: string): Promise { + return kv.get(includeEnvPrefix(key)); +} + +export async function redisMGet(...keys: string[]): Promise { + return kv.mget(keys.map(includeEnvPrefix)); +} + export async function getAllNavItems() { - const keys = await kv.smembers("config:navitemslist"); + const keys = await kv.smembers( + includeEnvPrefix("config:navitemslist"), + ); if (!keys || keys.length < 1) { return { keys: [], @@ -11,7 +43,7 @@ export async function getAllNavItems() { } const pipe = kv.pipeline(); for (const key of keys) { - pipe.hgetall(`config:navitems:${key}`); + pipe.hgetall(includeEnvPrefix(`config:navitems:${key}`)); } const items = await pipe.exec(); return { @@ -20,6 +52,17 @@ export async function getAllNavItems() { }; } +export function removeNavItem(name: string) { + console.log("Removing: ", includeEnvPrefix("config:navitemslist")); + const pipe = kv.pipeline(); + pipe.srem( + includeEnvPrefix("config:navitemslist"), + encodeURIComponent(name), + ); + pipe.del(includeEnvPrefix(`config:navitems:${encodeURIComponent(name)}`)); + return pipe.exec(); +} + export function parseRedisBoolean( value: string | boolean | undefined | null, defaultValue?: boolean, diff --git a/package.json b/package.json index ed954ddf..e0144114 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "prettier": "^3.3.3", "turbo": "^2.0.9" }, - "packageManager": "pnpm@8.3.1", + "packageManager": "pnpm@8.3.1+sha512.d264f6640bf4f09de7cfcc547568515bcf0613cf485a03e8ff16616fa69c4172b6f9a0a2925ee44fb060df565c9c9a8eaf061749e77af318cb77f6684a7051f3", "name": "hackkit", "dependencies": { "prettier-plugin-tailwindcss": "^0.6.5" diff --git a/turbo.json b/turbo.json index ca6370bf..71c7b38a 100644 --- a/turbo.json +++ b/turbo.json @@ -22,6 +22,7 @@ "DISCORD_PROD_SERVER_ID", "DISCORD_PROD_VERIFY_CHANNEL_ID", "DISCORD_SECRET_TOKEN", + "HK_ENV", "INTERNAL_AUTH_KEY", "KV_REST_API_READ_ONLY_TOKEN", "KV_REST_API_TOKEN",