From 220ec757d42b54f498d031436e5b4b03931b165c Mon Sep 17 00:00:00 2001 From: Maze Winther Date: Sun, 29 Mar 2026 14:15:21 +0200 Subject: refactor: move packages sub-folders into web app --- apps/web/src/lib/auth/client.ts | 12 +++--- apps/web/src/lib/auth/server.ts | 86 ++++++++++++++++++++--------------------- apps/web/src/lib/db/index.ts | 2 +- apps/web/src/lib/env/web.ts | 34 ++++++++++++++++ apps/web/src/lib/rate-limit.ts | 42 ++++++++++---------- 5 files changed, 105 insertions(+), 71 deletions(-) create mode 100644 apps/web/src/lib/env/web.ts (limited to 'apps/web/src/lib') diff --git a/apps/web/src/lib/auth/client.ts b/apps/web/src/lib/auth/client.ts index a4380e54..3bec5072 100644 --- a/apps/web/src/lib/auth/client.ts +++ b/apps/web/src/lib/auth/client.ts @@ -1,6 +1,6 @@ -import { createAuthClient } from "better-auth/react"; -import { webEnv } from "@opencut/env/web"; - -export const { signIn, signUp, useSession } = createAuthClient({ - baseURL: webEnv.NEXT_PUBLIC_SITE_URL, -}); +import { createAuthClient } from "better-auth/react"; +import { webEnv } from "@/lib/env/web"; + +export const { signIn, signUp, useSession } = createAuthClient({ + baseURL: webEnv.NEXT_PUBLIC_SITE_URL, +}); diff --git a/apps/web/src/lib/auth/server.ts b/apps/web/src/lib/auth/server.ts index aaca7936..39fce38f 100644 --- a/apps/web/src/lib/auth/server.ts +++ b/apps/web/src/lib/auth/server.ts @@ -1,43 +1,43 @@ -import { betterAuth, type RateLimit } from "better-auth"; -import { drizzleAdapter } from "better-auth/adapters/drizzle"; -import { Redis } from "@upstash/redis"; -import { db } from "@/lib/db"; -import { webEnv } from "@opencut/env/web"; - -const redis = new Redis({ - url: webEnv.UPSTASH_REDIS_REST_URL, - token: webEnv.UPSTASH_REDIS_REST_TOKEN, -}); - -export const auth = betterAuth({ - database: drizzleAdapter(db, { - provider: "pg", - usePlural: true, - }), - secret: webEnv.BETTER_AUTH_SECRET, - user: { - deleteUser: { - enabled: true, - }, - }, - emailAndPassword: { - enabled: true, - }, - rateLimit: { - storage: "secondary-storage", - customStorage: { - get: async (key) => { - const value = await redis.get(key); - return value as RateLimit | undefined; - }, - set: async (key, value) => { - await redis.set(key, value); - }, - }, - }, - baseURL: webEnv.NEXT_PUBLIC_SITE_URL, - appName: "OpenCut", - trustedOrigins: [webEnv.NEXT_PUBLIC_SITE_URL], -}); - -export type Auth = typeof auth; +import { betterAuth, type RateLimit } from "better-auth"; +import { drizzleAdapter } from "better-auth/adapters/drizzle"; +import { Redis } from "@upstash/redis"; +import { db } from "@/lib/db"; +import { webEnv } from "@/lib/env/web"; + +const redis = new Redis({ + url: webEnv.UPSTASH_REDIS_REST_URL, + token: webEnv.UPSTASH_REDIS_REST_TOKEN, +}); + +export const auth = betterAuth({ + database: drizzleAdapter(db, { + provider: "pg", + usePlural: true, + }), + secret: webEnv.BETTER_AUTH_SECRET, + user: { + deleteUser: { + enabled: true, + }, + }, + emailAndPassword: { + enabled: true, + }, + rateLimit: { + storage: "secondary-storage", + customStorage: { + get: async (key) => { + const value = await redis.get(key); + return value as RateLimit | undefined; + }, + set: async (key, value) => { + await redis.set(key, value); + }, + }, + }, + baseURL: webEnv.NEXT_PUBLIC_SITE_URL, + appName: "OpenCut", + trustedOrigins: [webEnv.NEXT_PUBLIC_SITE_URL], +}); + +export type Auth = typeof auth; diff --git a/apps/web/src/lib/db/index.ts b/apps/web/src/lib/db/index.ts index 290e7c2a..b1ffd119 100644 --- a/apps/web/src/lib/db/index.ts +++ b/apps/web/src/lib/db/index.ts @@ -1,7 +1,7 @@ import { drizzle } from "drizzle-orm/postgres-js"; import postgres from "postgres"; import * as schema from "./schema"; -import { webEnv } from "@opencut/env/web"; +import { webEnv } from "@/lib/env/web"; let _db: ReturnType | null = null; diff --git a/apps/web/src/lib/env/web.ts b/apps/web/src/lib/env/web.ts new file mode 100644 index 00000000..4e8b550e --- /dev/null +++ b/apps/web/src/lib/env/web.ts @@ -0,0 +1,34 @@ +import { z } from "zod"; + +const webEnvSchema = z.object({ + // Node + NODE_ENV: z.enum(["development", "production", "test"]), + ANALYZE: z.string().optional(), + NEXT_RUNTIME: z.enum(["nodejs", "edge"]).optional(), + + // Public + NEXT_PUBLIC_SITE_URL: z.url().default("http://localhost:3000"), + NEXT_PUBLIC_MARBLE_API_URL: z.url(), + + // Server + DATABASE_URL: z + .string() + .startsWith("postgres://") + .or(z.string().startsWith("postgresql://")), + + BETTER_AUTH_SECRET: z.string(), + UPSTASH_REDIS_REST_URL: z.url(), + UPSTASH_REDIS_REST_TOKEN: z.string(), + MARBLE_WORKSPACE_KEY: z.string(), + FREESOUND_CLIENT_ID: z.string(), + FREESOUND_API_KEY: z.string(), + CLOUDFLARE_ACCOUNT_ID: z.string(), + R2_ACCESS_KEY_ID: z.string(), + R2_SECRET_ACCESS_KEY: z.string(), + R2_BUCKET_NAME: z.string(), + MODAL_TRANSCRIPTION_URL: z.url(), +}); + +export type WebEnv = z.infer; + +export const webEnv = webEnvSchema.parse(process.env); diff --git a/apps/web/src/lib/rate-limit.ts b/apps/web/src/lib/rate-limit.ts index 6b59c473..234bf48f 100644 --- a/apps/web/src/lib/rate-limit.ts +++ b/apps/web/src/lib/rate-limit.ts @@ -1,21 +1,21 @@ -import { Ratelimit } from "@upstash/ratelimit"; -import { Redis } from "@upstash/redis"; -import { webEnv } from "@opencut/env/web"; - -const redis = new Redis({ - url: webEnv.UPSTASH_REDIS_REST_URL, - token: webEnv.UPSTASH_REDIS_REST_TOKEN, -}); - -export const baseRateLimit = new Ratelimit({ - redis, - limiter: Ratelimit.slidingWindow(100, "1 m"), // 100 requests per minute - analytics: true, - prefix: "rate-limit", -}); - -export async function checkRateLimit({ request }: { request: Request }) { - const ip = request.headers.get("x-forwarded-for") ?? "anonymous"; - const { success } = await baseRateLimit.limit(ip); - return { success, limited: !success }; -} +import { Ratelimit } from "@upstash/ratelimit"; +import { Redis } from "@upstash/redis"; +import { webEnv } from "@/lib/env/web"; + +const redis = new Redis({ + url: webEnv.UPSTASH_REDIS_REST_URL, + token: webEnv.UPSTASH_REDIS_REST_TOKEN, +}); + +export const baseRateLimit = new Ratelimit({ + redis, + limiter: Ratelimit.slidingWindow(100, "1 m"), // 100 requests per minute + analytics: true, + prefix: "rate-limit", +}); + +export async function checkRateLimit({ request }: { request: Request }) { + const ip = request.headers.get("x-forwarded-for") ?? "anonymous"; + const { success } = await baseRateLimit.limit(ip); + return { success, limited: !success }; +} -- cgit v1.3.1