From b6daaa41defd53f69a8d8a476e947a9839c109cf Mon Sep 17 00:00:00 2001 From: Henry Date: Mon, 5 May 2025 15:16:30 +0100 Subject: [PATCH] refactor: remove supabase auth --- .env.example | 9 +- README.md | 4 +- apps/web/next.config.js | 8 +- apps/web/package.json | 3 +- apps/web/src/components/AuthForm.tsx | 29 +- apps/web/src/components/UserMenu.tsx | 10 +- apps/web/src/env.ts | 8 +- apps/web/src/pages/api/auth/[...all].ts | 4 + apps/web/src/pages/api/auth/confirm.ts | 168 ++-- .../api/stripe/create_billing_session.ts | 34 +- .../api/stripe/create_checkout_session.ts | 36 +- apps/web/src/pages/api/stripe/webhook.ts | 21 +- apps/web/src/utils/supabase/client.ts | 12 - apps/web/src/utils/supabase/getPublicUrl.ts | 7 - apps/web/src/views/board/components/Card.tsx | 5 +- .../src/views/board/components/Filters.tsx | 5 +- .../views/board/components/NewCardForm.tsx | 17 +- apps/web/src/views/card/index.tsx | 19 +- apps/web/src/views/home/components/Layout.tsx | 4 +- apps/web/src/views/members/index.tsx | 9 +- .../src/views/settings/components/Avatar.tsx | 17 +- packages/api/package.json | 2 +- packages/api/src/index.ts | 8 +- packages/api/src/root.ts | 2 - packages/api/src/routers/auth.ts | 74 -- packages/api/src/routers/member.ts | 98 +- packages/api/src/trpc.ts | 14 +- packages/auth/package.json | 2 + packages/auth/src/auth.ts | 33 +- packages/auth/src/clients.ts | 5 +- packages/db/src/repository/card.repo.ts | 2 - packages/db/src/repository/list.repo.ts | 2 - packages/db/src/repository/user.repo.ts | 4 +- packages/db/src/types/database.types.ts | 940 ------------------ .../{supabase => stripe}/eslint.config.js | 0 packages/{supabase => stripe}/package.json | 11 +- packages/stripe/src/index.ts | 20 + packages/{supabase => stripe}/tsconfig.json | 0 packages/supabase/src/clients.ts | 103 -- packages/supabase/src/index.ts | 4 - pnpm-lock.yaml | 151 +-- turbo.json | 6 +- 42 files changed, 319 insertions(+), 1591 deletions(-) delete mode 100644 apps/web/src/utils/supabase/client.ts delete mode 100644 apps/web/src/utils/supabase/getPublicUrl.ts delete mode 100644 packages/api/src/routers/auth.ts delete mode 100644 packages/db/src/types/database.types.ts rename packages/{supabase => stripe}/eslint.config.js (100%) rename packages/{supabase => stripe}/package.json (72%) create mode 100644 packages/stripe/src/index.ts rename packages/{supabase => stripe}/tsconfig.json (100%) delete mode 100644 packages/supabase/src/clients.ts delete mode 100644 packages/supabase/src/index.ts diff --git a/.env.example b/.env.example index c0e41ec1..6e082f29 100644 --- a/.env.example +++ b/.env.example @@ -5,7 +5,8 @@ EMAIL_FROM= EMAIL_URL= EMAIL_TOKEN= -NEXT_PUBLIC_SUPABASE_URL= -NEXT_PUBLIC_SUPABASE_ANON_KEY= -NEXT_PUBLIC_SUPABASE_AUTH_COOKIE_NAME= -SUPABASE_SERVICE_API_KEY= +BETTER_AUTH_SECRET= +BETTER_AUTH_URL= + +GOOGLE_CLIENT_ID= +GOOGLE_CLIENT_SECRET= diff --git a/README.md b/README.md index 56e13745..92476be5 100644 --- a/README.md +++ b/README.md @@ -23,9 +23,7 @@ - [Next.js](https://nextjs.org/?ref=kan.bn) - [tRPC](https://trpc.io/?ref=kan.bn) -- [Supabase](https://supabase.com/?ref=kan.bn) +- [Better Auth](https://better-auth.com/?ref=kan.bn) - [Tailwind CSS](https://tailwindcss.com/?ref=kan.bn) - [Drizzle ORM](https://orm.drizzle.team/?ref=kan.bn) - [React Email](https://react.email/?ref=kan.bn) - - diff --git a/apps/web/next.config.js b/apps/web/next.config.js index 6d8f2935..997ad27a 100644 --- a/apps/web/next.config.js +++ b/apps/web/next.config.js @@ -9,7 +9,13 @@ const config = { reactStrictMode: true, /** Enables hot reloading for local packages without a build step */ - transpilePackages: ["@kan/api", "@kan/db", "@kan/shared"], + transpilePackages: [ + "@kan/api", + "@kan/db", + "@kan/shared", + "@kan/auth", + "@kan/stripe", + ], /** We already do linting and typechecking as separate tasks in CI */ eslint: { ignoreDuringBuilds: true }, diff --git a/apps/web/package.json b/apps/web/package.json index bca5bec0..8f79592c 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -20,7 +20,6 @@ "@kan/auth": "workspace:*", "@kan/db": "workspace:^", "@kan/shared": "workspace:^", - "@kan/supabase": "workspace:^", "@t3-oss/env-nextjs": "^0.11.1", "@tanstack/react-query": "catalog:", "@trpc/client": "catalog:", @@ -39,7 +38,6 @@ "react-hook-form": "^7.51.1", "react-icons": "^4.12.0", "react-lottie-player": "^1.5.5", - "stripe": "^17.5.0", "superjson": "2.2.1", "tailwind-merge": "^2.5.2", "zod": "catalog:" @@ -47,6 +45,7 @@ "devDependencies": { "@kan/eslint-config": "workspace:*", "@kan/prettier-config": "workspace:*", + "@kan/stripe": "workspace:*", "@kan/tailwind-config": "workspace:*", "@kan/tsconfig": "workspace:*", "@types/js-cookie": "^3.0.6", diff --git a/apps/web/src/components/AuthForm.tsx b/apps/web/src/components/AuthForm.tsx index dcdf03b1..a2e41bcd 100644 --- a/apps/web/src/components/AuthForm.tsx +++ b/apps/web/src/components/AuthForm.tsx @@ -26,7 +26,7 @@ export function Auth({ setIsMagicLinkSent }: AuthProps) { watch, formState: { errors }, } = useForm({ - resolver: zodResolver(EmailSchema), // Apply the zodResolver + resolver: zodResolver(EmailSchema), }); const email = watch("email"); @@ -37,22 +37,21 @@ export function Auth({ setIsMagicLinkSent }: AuthProps) { }, }); - const loginWithOAuth = api.auth.loginWithOAuth.useMutation({ - onSuccess: (data) => { - if (data.url) window.open(data.url); - }, - }); + const handleLoginWithEmail = async (email: string) => { + const { data, error } = await authClient.signIn.magicLink({ + email, + callbackURL: "/boards", + }); - const onSubmit = (values: FormValues) => { - try { - loginWithEmail.mutate({ - email: values.email, - }); - } catch (error) { - console.error(error); + if (!error) { + setIsMagicLinkSent(true, email); } }; + const onSubmit = async (values: FormValues) => { + await handleLoginWithEmail(values.email); + }; + return (
@@ -80,12 +79,12 @@ export function Auth({ setIsMagicLinkSent }: AuthProps) { {...register("email", { required: true })} placeholder="Enter your email address" /> - {!loginWithEmail.error && !loginWithOAuth.error && errors.email && ( + {!loginWithEmail.error && errors.email && (

Please enter a valid email address

)} - {(loginWithEmail.error ?? loginWithOAuth.error) ? ( + {loginWithEmail.error ? (

Something went wrong, please try again later or contact customer support. diff --git a/apps/web/src/components/UserMenu.tsx b/apps/web/src/components/UserMenu.tsx index b955b3a4..31ba9bde 100644 --- a/apps/web/src/components/UserMenu.tsx +++ b/apps/web/src/components/UserMenu.tsx @@ -3,9 +3,9 @@ import { useRouter } from "next/navigation"; import { Menu, Transition } from "@headlessui/react"; import { Fragment } from "react"; +import { authClient } from "@kan/auth"; + import { useTheme } from "~/providers/theme"; -import createClient from "~/utils/supabase/client"; -import { getPublicUrl } from "~/utils/supabase/getPublicUrl"; interface UserMenuProps { imageUrl: string | undefined; @@ -22,17 +22,17 @@ export default function UserMenu({ email, isLoading, }: UserMenuProps) { - const db = createClient(); const router = useRouter(); const { themePreference, switchTheme } = useTheme(); const handleLogout = async () => { - await db.auth.signOut(); + await authClient.signOut(); router.push("/login"); }; - const avatarUrl = imageUrl ? getPublicUrl(imageUrl) : null; + // const avatarUrl = imageUrl ? getPublicUrl(imageUrl) : null; + const avatarUrl = ""; return (

diff --git a/apps/web/src/env.ts b/apps/web/src/env.ts index a3fc2b26..53cbd5b9 100644 --- a/apps/web/src/env.ts +++ b/apps/web/src/env.ts @@ -25,19 +25,15 @@ export const env = createEnv({ * For them to be exposed to the client, prefix them with `NEXT_PUBLIC_`. */ client: { - NEXT_PUBLIC_SUPABASE_AUTH_COOKIE_NAME: z.string(), - NEXT_PUBLIC_SUPABASE_STORAGE_URL: z.string(), + NEXT_PUBLIC_KAN_ENV: z.string(), NEXT_PUBLIC_UMAMI_ID: z.string().optional(), }, /** * Destructure all variables from `process.env` to make sure they aren't tree-shaken away. */ experimental__runtimeEnv: { + NEXT_PUBLIC_KAN_ENV: process.env.NEXT_PUBLIC_KAN_ENV, NODE_ENV: process.env.NODE_ENV, - NEXT_PUBLIC_SUPABASE_AUTH_COOKIE_NAME: - process.env.NEXT_PUBLIC_SUPABASE_AUTH_COOKIE_NAME, - NEXT_PUBLIC_SUPABASE_STORAGE_URL: - process.env.NEXT_PUBLIC_SUPABASE_STORAGE_URL, NEXT_PUBLIC_UMAMI_ID: process.env.NEXT_PUBLIC_UMAMI_ID, }, skipValidation: diff --git a/apps/web/src/pages/api/auth/[...all].ts b/apps/web/src/pages/api/auth/[...all].ts index 347aed95..dd677d59 100644 --- a/apps/web/src/pages/api/auth/[...all].ts +++ b/apps/web/src/pages/api/auth/[...all].ts @@ -4,3 +4,7 @@ import { auth } from "@kan/auth"; export const config = { api: { bodyParser: false } }; export default toNodeHandler(auth.handler); + +// export const runtime = "edge"; +// export const preferredRegion = "lhr1"; +// export const dynamic = "force-dynamic"; diff --git a/apps/web/src/pages/api/auth/confirm.ts b/apps/web/src/pages/api/auth/confirm.ts index 162e8be0..c9f4144a 100644 --- a/apps/web/src/pages/api/auth/confirm.ts +++ b/apps/web/src/pages/api/auth/confirm.ts @@ -1,112 +1,102 @@ -import type { EmailOtpType } from "@supabase/supabase-js"; -import type { NextRequest } from "next/server"; -import { NextResponse } from "next/server"; -import { Stripe } from "stripe"; +// import type { EmailOtpType } from "@supabase/supabase-js"; +// import type { NextRequest } from "next/server"; +// import { NextResponse } from "next/server"; -import { createDrizzleClient } from "@kan/db/client"; -import * as memberRepo from "@kan/db/repository/member.repo"; -import * as userRepo from "@kan/db/repository/user.repo"; -import { createNextClient } from "@kan/supabase/clients"; +// import { createDrizzleClient } from "@kan/db/client"; +// import * as memberRepo from "@kan/db/repository/member.repo"; +// import * as userRepo from "@kan/db/repository/user.repo"; +// import { stripe } from "@kan/stripe"; +// import { createNextApiClient } from "@kan/"; -const stripeSecretKey = process.env.STRIPE_SECRET_KEY; +// export default async function handler(req: NextRequest) { +// if (req.method !== "GET") { +// return new NextResponse(null, { +// status: 405, +// headers: { Allow: "GET" }, +// }); +// } -if (!stripeSecretKey) { - throw new Error("STRIPE_SECRET_KEY is not defined"); -} +// if (!req.url) { +// return new NextResponse(null, { +// status: 400, +// }); +// } -const stripe = new Stripe(stripeSecretKey, { - apiVersion: "2024-12-18.acacia", -}); +// const url = new URL(req.url); +// const queryParams = Object.fromEntries(url.searchParams.entries()); -export default async function handler(req: NextRequest) { - if (req.method !== "GET") { - return new NextResponse(null, { - status: 405, - headers: { Allow: "GET" }, - }); - } +// const tokenHash = queryParams.token_hash; +// const type = queryParams.type; +// const code = queryParams.code; +// const memberPublicId = queryParams.memberPublicId; - if (!req.url) { - return new NextResponse(null, { - status: 400, - }); - } +// let next = "/error"; - const url = new URL(req.url); - const queryParams = Object.fromEntries(url.searchParams.entries()); +// let authRes; - const tokenHash = queryParams.token_hash; - const type = queryParams.type; - const code = queryParams.code; - const memberPublicId = queryParams.memberPublicId; +// const response = NextResponse.next(); - let next = "/error"; +// if ((tokenHash && type) ?? code) { +// const supabaseClient = createNextClient(req, response); - let authRes; +// if (tokenHash && type) { +// authRes = await supabaseClient.auth.verifyOtp({ +// type: type as EmailOtpType, +// token_hash: tokenHash, +// }); +// } - const response = NextResponse.next(); +// if (code) { +// authRes = await supabaseClient.auth.exchangeCodeForSession(code); +// } - if ((tokenHash && type) ?? code) { - const supabaseClient = createNextClient(req, response); +// const user = authRes?.data.user; - if (tokenHash && type) { - authRes = await supabaseClient.auth.verifyOtp({ - type: type as EmailOtpType, - token_hash: tokenHash, - }); - } +// const db = createDrizzleClient(); - if (code) { - authRes = await supabaseClient.auth.exchangeCodeForSession(code); - } +// if (user?.id && user.email) { +// const existingUser = await userRepo.getById(db, user.id); - const user = authRes?.data.user; +// if (!existingUser) { +// const stripeCustomer = await stripe.customers.create({ +// email: user.email, +// metadata: { +// userId: user.id, +// }, +// }); - const db = createDrizzleClient(); +// await userRepo.create(db, { +// id: user.id, +// email: user.email, +// stripeCustomerId: stripeCustomer.id, +// }); +// } +// } - if (user?.id && user.email) { - const existingUser = await userRepo.getById(db, user.id); +// if (memberPublicId) { +// const member = await memberRepo.getByPublicId(db, memberPublicId); - if (!existingUser) { - const stripeCustomer = await stripe.customers.create({ - email: user.email, - metadata: { - userId: user.id, - }, - }); +// if (member?.id) { +// await memberRepo.acceptInvite(db, member.id); +// } +// } - await userRepo.create(db, { - id: user.id, - email: user.email, - stripeCustomerId: stripeCustomer.id, - }); - } - } +// if (authRes?.error) { +// console.error(authRes.error); +// } else { +// next = queryParams.next ?? "/boards"; +// } +// } - if (memberPublicId) { - const member = await memberRepo.getByPublicId(db, memberPublicId); +// const redirectResponse = NextResponse.redirect(new URL(next, req.url)); - if (member?.id) { - await memberRepo.acceptInvite(db, member.id); - } - } +// response.headers.getSetCookie().forEach((cookie) => { +// redirectResponse.headers.append("Set-Cookie", cookie); +// }); - if (authRes?.error) { - console.error(authRes.error); - } else { - next = queryParams.next ?? "/boards"; - } - } +// return redirectResponse; +// } - const redirectResponse = NextResponse.redirect(new URL(next, req.url)); - - response.headers.getSetCookie().forEach((cookie) => { - redirectResponse.headers.append("Set-Cookie", cookie); - }); - - return redirectResponse; -} - -export const runtime = "edge"; -export const preferredRegion = "lhr1"; -export const dynamic = "force-dynamic"; +// export const runtime = "edge"; +// export const preferredRegion = "lhr1"; +// export const dynamic = "force-dynamic"; diff --git a/apps/web/src/pages/api/stripe/create_billing_session.ts b/apps/web/src/pages/api/stripe/create_billing_session.ts index 723a3443..409815cc 100644 --- a/apps/web/src/pages/api/stripe/create_billing_session.ts +++ b/apps/web/src/pages/api/stripe/create_billing_session.ts @@ -1,22 +1,11 @@ import type { NextRequest } from "next/server"; -import { NextResponse } from "next/server"; -import { Stripe } from "stripe"; -import { createDrizzleClient } from "@kan/db/client"; -import * as userRepo from "@kan/db/repository/user.repo"; -import { createNextClient } from "@kan/supabase/clients"; - -const stripeSecretKey = process.env.STRIPE_SECRET_KEY; - -if (!stripeSecretKey) { - throw new Error("STRIPE_SECRET_KEY is not defined"); -} - -const stripe = new Stripe(stripeSecretKey, { - apiVersion: "2024-12-18.acacia", -}); +import { createNextApiContext } from "@kan/api/trpc"; +import { createStripeClient } from "@kan/stripe"; export default async function handler(req: NextRequest) { + const stripe = createStripeClient(); + if (req.method !== "POST") { return new Response(JSON.stringify({ error: "Method not allowed" }), { status: 405, @@ -25,20 +14,7 @@ export default async function handler(req: NextRequest) { } try { - const response = NextResponse.next(); - const supabaseClient = createNextClient(req, response); - const { data } = await supabaseClient.auth.getUser(); - - if (!data.user) { - return new Response(JSON.stringify({ error: "Unauthorized" }), { - status: 403, - headers: { "Content-Type": "application/json" }, - }); - } - - const db = createDrizzleClient(); - - const user = await userRepo.getById(db, data.user.id); + const { user } = await createNextApiContext(req); if (!user?.stripeCustomerId) { return new Response( diff --git a/apps/web/src/pages/api/stripe/create_checkout_session.ts b/apps/web/src/pages/api/stripe/create_checkout_session.ts index 8c26ffd2..9d46e7d3 100644 --- a/apps/web/src/pages/api/stripe/create_checkout_session.ts +++ b/apps/web/src/pages/api/stripe/create_checkout_session.ts @@ -1,22 +1,9 @@ import type { NextRequest } from "next/server"; -import { NextResponse } from "next/server"; -import { Stripe } from "stripe"; import { z } from "zod"; -import { createDrizzleClient } from "@kan/db/client"; -import * as userRepo from "@kan/db/repository/user.repo"; +import { createNextApiContext } from "@kan/api/trpc"; import * as workspaceRepo from "@kan/db/repository/workspace.repo"; -import { createNextClient } from "@kan/supabase/clients"; - -const stripeSecretKey = process.env.STRIPE_SECRET_KEY; - -if (!stripeSecretKey) { - throw new Error("STRIPE_SECRET_KEY is not defined"); -} - -const stripe = new Stripe(stripeSecretKey, { - apiVersion: "2024-12-18.acacia", -}); +import { createStripeClient } from "@kan/stripe"; const workspaceSlugSchema = z .string() @@ -33,6 +20,8 @@ interface CheckoutSessionRequest { } export default async function handler(req: NextRequest) { + const stripe = createStripeClient(); + if (req.method !== "POST") { return new Response(JSON.stringify({ error: "Method not allowed" }), { status: 405, @@ -41,22 +30,7 @@ export default async function handler(req: NextRequest) { } try { - const response = NextResponse.next(); - - const supabaseClient = createNextClient(req, response); - - const { data } = await supabaseClient.auth.getUser(); - - if (!data.user) { - return new Response(JSON.stringify({ error: "Unauthorized" }), { - status: 403, - headers: { "Content-Type": "application/json" }, - }); - } - - const db = createDrizzleClient(); - - const user = await userRepo.getById(db, data.user.id); + const { user, db } = await createNextApiContext(req); if (!user) { return new Response(JSON.stringify({ error: "User not found" }), { diff --git a/apps/web/src/pages/api/stripe/webhook.ts b/apps/web/src/pages/api/stripe/webhook.ts index 41b59f3d..0b138f6a 100644 --- a/apps/web/src/pages/api/stripe/webhook.ts +++ b/apps/web/src/pages/api/stripe/webhook.ts @@ -1,24 +1,15 @@ import type { NextRequest } from "next/server"; -import { NextResponse } from "next/server"; import { Stripe } from "stripe"; +import { createNextApiContext } from "@kan/api/trpc"; import * as workspaceRepo from "@kan/db/repository/workspace.repo"; -import { createNextClient } from "@kan/supabase/clients"; - -const stripeSecretKey = process.env.STRIPE_SECRET_KEY; - -if (!stripeSecretKey) { - throw new Error("STRIPE_SECRET_KEY is not defined"); -} +import { createStripeClient } from "@kan/stripe"; export const webCrypto = Stripe.createSubtleCryptoProvider(); -const stripe: Stripe = new Stripe(stripeSecretKey, { - apiVersion: "2024-12-18.acacia", - httpClient: Stripe.createFetchHttpClient(), -}); - export default async function handler(req: NextRequest) { + const stripe = createStripeClient(); + if (req.method !== "POST") { return new Response(JSON.stringify({ message: "Method not allowed" }), { status: 405, @@ -44,9 +35,7 @@ export default async function handler(req: NextRequest) { webCrypto, ); - const response = NextResponse.next(); - - const db = createNextClient(req, response); + const { db } = await createNextApiContext(req); switch (event.type) { case "checkout.session.completed": { diff --git a/apps/web/src/utils/supabase/client.ts b/apps/web/src/utils/supabase/client.ts deleted file mode 100644 index 8f20c394..00000000 --- a/apps/web/src/utils/supabase/client.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { createBrowserClient } from "@supabase/ssr"; - -import type { Database } from "@kan/db/types/database.types"; - -export default function createClient() { - const supabase = createBrowserClient( - process.env.NEXT_PUBLIC_SUPABASE_URL!, - process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!, - ); - - return supabase; -} diff --git a/apps/web/src/utils/supabase/getPublicUrl.ts b/apps/web/src/utils/supabase/getPublicUrl.ts deleted file mode 100644 index c62aba4c..00000000 --- a/apps/web/src/utils/supabase/getPublicUrl.ts +++ /dev/null @@ -1,7 +0,0 @@ -import createClient from "~/utils/supabase/client"; - -export const getPublicUrl = (fileName: string) => { - const supabase = createClient(); - - return supabase.storage.from("avatars").getPublicUrl(fileName).data.publicUrl; -}; diff --git a/apps/web/src/views/board/components/Card.tsx b/apps/web/src/views/board/components/Card.tsx index e10bf829..ad4b0de5 100644 --- a/apps/web/src/views/board/components/Card.tsx +++ b/apps/web/src/views/board/components/Card.tsx @@ -1,7 +1,6 @@ import Avatar from "~/components/Avatar"; import Badge from "~/components/Badge"; import LabelIcon from "~/components/LabelIcon"; -import { getPublicUrl } from "~/utils/supabase/getPublicUrl"; const Card = ({ title, @@ -32,9 +31,7 @@ const Card = ({ {members.map(({ user }) => { if (!user) return null; - const avatarUrl = user.image - ? getPublicUrl(user.image) - : undefined; + const avatarUrl = user.image ? "" : undefined; return ( ), diff --git a/apps/web/src/views/board/components/NewCardForm.tsx b/apps/web/src/views/board/components/NewCardForm.tsx index fa6f49c9..61c74372 100644 --- a/apps/web/src/views/board/components/NewCardForm.tsx +++ b/apps/web/src/views/board/components/NewCardForm.tsx @@ -19,7 +19,6 @@ import { useModal } from "~/providers/modal"; import { usePopup } from "~/providers/popup"; import { api } from "~/utils/api"; import { formatMemberDisplayName } from "~/utils/helpers"; -import { getPublicUrl } from "~/utils/supabase/getPublicUrl"; type NewCardFormInput = NewCardInput & { isCreateAnotherEnabled: boolean; @@ -89,7 +88,7 @@ export function NewCardForm({ args.labelPublicIds.includes(label.publicId), ), members: - oldBoard.workspace?.members.filter((member) => + oldBoard.workspace.members.filter((member) => args.memberPublicIds.includes(member.publicId), ) ?? [], _filteredLabels: labelPublicIds.map((id) => ({ publicId: id })), @@ -146,20 +145,18 @@ export function NewCardForm({ })) ?? []; const formattedMembers = - boardData?.workspace?.members.map((member) => ({ + boardData?.workspace.members.map((member) => ({ key: member.publicId, value: formatMemberDisplayName( - member.user?.name ?? null, - member.user?.email ?? null, + member.user.name ?? null, + member.user.email ?? null, ), leftIcon: ( ), })) ?? []; diff --git a/apps/web/src/views/card/index.tsx b/apps/web/src/views/card/index.tsx index cddd76bf..f0610fa6 100644 --- a/apps/web/src/views/card/index.tsx +++ b/apps/web/src/views/card/index.tsx @@ -15,7 +15,6 @@ import { usePopup } from "~/providers/popup"; import { useWorkspace } from "~/providers/workspace"; import { api } from "~/utils/api"; import { formatMemberDisplayName } from "~/utils/helpers"; -import { getPublicUrl } from "~/utils/supabase/getPublicUrl"; import { DeleteLabelConfirmation } from "../../components/DeleteLabelConfirmation"; import ActivityList from "./components/ActivityList"; import { DeleteCardConfirmation } from "./components/DeleteCardConfirmation"; @@ -51,11 +50,11 @@ export default function CardPage() { if (cardId) await utils.card.byId.refetch({ cardPublicId: cardId }); }; - const board = card?.list?.board; + const board = card?.list.board; const boardId = board?.publicId; const labels = board?.labels; const activities = card?.activities; - const workspaceMembers = board?.workspace?.members; + const workspaceMembers = board?.workspace.members; const selectedLabels = card?.labels; const selectedMembers = card?.members; @@ -77,7 +76,7 @@ export default function CardPage() { board?.lists.map((list) => ({ key: list.publicId, value: list.name, - selected: list.publicId === card?.list?.publicId, + selected: list.publicId === card?.list.publicId, })) ?? []; const formattedMembers = @@ -89,21 +88,21 @@ export default function CardPage() { return { key: member.publicId, value: formatMemberDisplayName( - member.user?.name ?? null, - member.user?.email ?? null, + member.user.name ?? null, + member.user.email ?? null, ), - imageUrl: member.user?.image + imageUrl: member.user.image ? getPublicUrl(member.user.image) : undefined, selected: isSelected ?? false, leftIcon: ( ), }; diff --git a/apps/web/src/views/home/components/Layout.tsx b/apps/web/src/views/home/components/Layout.tsx index c0cdf00d..2e6d63c1 100644 --- a/apps/web/src/views/home/components/Layout.tsx +++ b/apps/web/src/views/home/components/Layout.tsx @@ -12,9 +12,11 @@ export default function Layout({ children }: { children: React.ReactNode }) { const token = typeof window !== "undefined" - ? Cookies.get(env.NEXT_PUBLIC_SUPABASE_AUTH_COOKIE_NAME) + ? Cookies.get('kan.session_token') : null; + const getSession = async () => { + const { data } = api.user.getUser.useQuery(undefined, { enabled: !!token, }); diff --git a/apps/web/src/views/members/index.tsx b/apps/web/src/views/members/index.tsx index 6e0e85d8..9db98ce3 100644 --- a/apps/web/src/views/members/index.tsx +++ b/apps/web/src/views/members/index.tsx @@ -11,7 +11,6 @@ import { useModal } from "~/providers/modal"; import { useWorkspace } from "~/providers/workspace"; import { api } from "~/utils/api"; import { getInitialsFromName, inferInitialsFromEmail } from "~/utils/helpers"; -import { getPublicUrl } from "~/utils/supabase/getPublicUrl"; import { DeleteMemberConfirmation } from "./components/DeleteMemberConfirmation"; import { InviteMemberForm } from "./components/InviteMemberForm"; @@ -58,7 +57,7 @@ export default function MembersPage() { )}
@@ -189,9 +188,9 @@ export default function MembersPage() { ) => { try { @@ -65,13 +62,13 @@ export default function Avatar({ const fileName = `${userId}/avatar.${fileExt}`; const filePath = `${fileName}`; - const { error: uploadError } = await supabase.storage - .from("avatars") - .upload(filePath, file, { upsert: true }); + // const { error: uploadError } = await supabase.storage + // .from("avatars") + // .upload(filePath, file, { upsert: true }); - if (uploadError) { - throw uploadError; - } + // if (uploadError) { + // throw uploadError; + // } updateUser.mutate({ image: filePath }); } catch (error) { diff --git a/packages/api/package.json b/packages/api/package.json index aff6e5a0..b80ababc 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -39,7 +39,7 @@ "@kan/db": "workspace:*", "@kan/email": "workspace:^", "@kan/shared": "workspace:^", - "@kan/supabase": "workspace:^", + "@kan/stripe": "workspace:^", "@trpc/server": "catalog:", "superjson": "2.2.1", "trpc-to-openapi": "^2.1.0", diff --git a/packages/api/src/index.ts b/packages/api/src/index.ts index 17e28a90..c488f252 100644 --- a/packages/api/src/index.ts +++ b/packages/api/src/index.ts @@ -2,12 +2,16 @@ import type { inferRouterInputs, inferRouterOutputs } from "@trpc/server"; import type { AppRouter } from "./root"; import { appRouter } from "./root"; -import { createCallerFactory, createTRPCContext } from "./trpc"; +import { + createCallerFactory, + createNextApiContext, + createTRPCContext, +} from "./trpc"; const createCaller = createCallerFactory(appRouter); type RouterInputs = inferRouterInputs; type RouterOutputs = inferRouterOutputs; -export { createTRPCContext, appRouter, createCaller }; +export { createTRPCContext, appRouter, createCaller, createNextApiContext }; export type { AppRouter, RouterInputs, RouterOutputs }; diff --git a/packages/api/src/root.ts b/packages/api/src/root.ts index e9744362..691a6e3f 100644 --- a/packages/api/src/root.ts +++ b/packages/api/src/root.ts @@ -1,4 +1,3 @@ -import { authRouter } from "./routers/auth"; import { boardRouter } from "./routers/board"; import { cardRouter } from "./routers/card"; import { feedbackRouter } from "./routers/feedback"; @@ -11,7 +10,6 @@ import { workspaceRouter } from "./routers/workspace"; import { createTRPCRouter } from "./trpc"; export const appRouter = createTRPCRouter({ - auth: authRouter, board: boardRouter, card: cardRouter, feedback: feedbackRouter, diff --git a/packages/api/src/routers/auth.ts b/packages/api/src/routers/auth.ts deleted file mode 100644 index ce25ca33..00000000 --- a/packages/api/src/routers/auth.ts +++ /dev/null @@ -1,74 +0,0 @@ -import { TRPCError } from "@trpc/server"; -import { z } from "zod"; - -import { createTRPCRouter, publicProcedure } from "../trpc"; - -export const authRouter = createTRPCRouter({ - loginWithEmail: publicProcedure - .meta({ - openapi: { - method: "POST", - path: "/auth/login/email", - summary: "Login with email", - description: "Sends a login URL to the provided email address", - tags: ["Auth"], - }, - }) - .input(z.object({ email: z.string() })) - .output(z.object({ success: z.boolean() })) - .mutation(async ({ ctx, input }) => { - const { data } = await ctx.supabaseClient.auth.signInWithOtp({ - email: input.email, - options: { - emailRedirectTo: `${process.env.WEBSITE_URL}`, - }, - }); - - if (!data) - throw new TRPCError({ - message: `Failed to login with email`, - code: "INTERNAL_SERVER_ERROR", - }); - - return { success: true }; - }), - loginWithOAuth: publicProcedure - .meta({ - openapi: { - method: "POST", - path: "/auth/login/oauth", - summary: "Login with OAuth", - description: - "Initiates the login process for a user with the given OAuth provider", - tags: ["Auth"], - }, - }) - .input(z.object({ provider: z.string() })) - .output(z.object({ url: z.string() })) - .mutation(async ({ ctx, input }) => { - if (input.provider !== "google") - throw new TRPCError({ - message: `Unsupported OAuth provider: ${input.provider}`, - code: "BAD_REQUEST", - }); - - const { data } = await ctx.supabaseClient.auth.signInWithOAuth({ - provider: "google", - options: { - queryParams: { - access_type: "offline", - prompt: "consent", - }, - redirectTo: `${process.env.WEBSITE_URL}/api/auth/confirm`, - }, - }); - - if (!data.url) - throw new TRPCError({ - message: `Failed to login with OAuth`, - code: "INTERNAL_SERVER_ERROR", - }); - - return { url: data.url }; - }), -}); diff --git a/packages/api/src/routers/member.ts b/packages/api/src/routers/member.ts index 881d36fc..99115d33 100644 --- a/packages/api/src/routers/member.ts +++ b/packages/api/src/routers/member.ts @@ -1,24 +1,14 @@ import { TRPCError } from "@trpc/server"; -import { Stripe } from "stripe"; import { z } from "zod"; import * as memberRepo from "@kan/db/repository/member.repo"; import * as userRepo from "@kan/db/repository/user.repo"; import * as workspaceRepo from "@kan/db/repository/workspace.repo"; import { sendEmail } from "@kan/email"; +import { createStripeClient } from "@kan/stripe"; import { createTRPCRouter, protectedProcedure } from "../trpc"; -const stripeSecretKey = process.env.STRIPE_SECRET_KEY; - -if (!stripeSecretKey) { - throw new Error("STRIPE_SECRET_KEY is not defined"); -} - -const stripe = new Stripe(stripeSecretKey, { - apiVersion: "2024-12-18.acacia", -}); - export const memberRouter = createTRPCRouter({ invite: protectedProcedure .meta({ @@ -75,57 +65,57 @@ export const memberRouter = createTRPCRouter({ const existingUser = await userRepo.getByEmail(ctx.db, input.email); - if (existingUser) { - invitedUserId = existingUser.id; + // if (existingUser) { + // invitedUserId = existingUser.id; - const magicLink = await ctx.supabaseClient.auth.admin.generateLink({ - type: "magiclink", - email: input.email, - options: { - redirectTo: process.env.WEBSITE_URL, - }, - }); + // const magicLink = await ctx.supabaseClient.auth.admin.generateLink({ + // type: "magiclink", + // email: input.email, + // options: { + // redirectTo: process.env.WEBSITE_URL, + // }, + // }); - hashedToken = magicLink.data.properties?.hashed_token; - verificationType = magicLink.data.properties?.verification_type; - } else { - const invite = await ctx.supabaseClient.auth.admin.generateLink({ - type: "invite", - email: input.email, - options: { - redirectTo: process.env.WEBSITE_URL, - }, - }); + // hashedToken = magicLink.data.properties?.hashed_token; + // verificationType = magicLink.data.properties?.verification_type; + // } else { + // const invite = await ctx.supabaseClient.auth.admin.generateLink({ + // type: "invite", + // email: input.email, + // options: { + // redirectTo: process.env.WEBSITE_URL, + // }, + // }); - hashedToken = invite.data.properties?.hashed_token; - verificationType = invite.data.properties?.verification_type; + // hashedToken = invite.data.properties?.hashed_token; + // verificationType = invite.data.properties?.verification_type; - const invitedUserAuthId = invite.data.user?.id; - const invitedUserEmail = invite.data.user?.email; + // const invitedUserAuthId = invite.data.user?.id; + // const invitedUserEmail = invite.data.user?.email; - if (invitedUserAuthId && invitedUserEmail) { - const stripeCustomer = await stripe.customers.create({ - email: invitedUserEmail, - metadata: { - userId: invitedUserAuthId, - }, - }); + // if (invitedUserAuthId && invitedUserEmail) { + // const stripeCustomer = await stripe.customers.create({ + // email: invitedUserEmail, + // metadata: { + // userId: invitedUserAuthId, + // }, + // }); - const newUser = await userRepo.create(ctx.db, { - email: invitedUserEmail, - id: invitedUserAuthId, - stripeCustomerId: stripeCustomer.id, - }); + // const newUser = await userRepo.create(ctx.db, { + // email: invitedUserEmail, + // id: invitedUserAuthId, + // stripeCustomerId: stripeCustomer.id, + // }); - if (!newUser) - throw new TRPCError({ - message: `Failed to create a new user for email ${invitedUserEmail}`, - code: "INTERNAL_SERVER_ERROR", - }); + // if (!newUser) + // throw new TRPCError({ + // message: `Failed to create a new user for email ${invitedUserEmail}`, + // code: "INTERNAL_SERVER_ERROR", + // }); - invitedUserId = newUser.id; - } - } + // invitedUserId = newUser.id; + // } + // } if (!invitedUserId) throw new TRPCError({ diff --git a/packages/api/src/trpc.ts b/packages/api/src/trpc.ts index dd3a13fe..1885f783 100644 --- a/packages/api/src/trpc.ts +++ b/packages/api/src/trpc.ts @@ -1,5 +1,6 @@ import type { FetchCreateContextFnOptions } from "@trpc/server/adapters/fetch"; import type { CreateNextContextOptions } from "@trpc/server/adapters/next"; +import type { NextRequest } from "next/server"; import type { OpenApiMeta } from "trpc-to-openapi"; import { initTRPC, TRPCError } from "@trpc/server"; import superjson from "superjson"; @@ -44,6 +45,16 @@ export const createTRPCContext = async ({ return createInnerTRPCContext({ db, user: session?.user }); }; +export const createNextApiContext = async (req: NextRequest) => { + const session = await auth.api.getSession({ + headers: req.headers, + }); + + const db = createDrizzleClient(); + + return createInnerTRPCContext({ db, user: session?.user }); +}; + export const createRESTContext = async ({ req }: CreateNextContextOptions) => { const authHeader = req.headers.authorization; const accessToken = authHeader?.startsWith("Bearer ") @@ -57,7 +68,8 @@ export const createRESTContext = async ({ req }: CreateNextContextOptions) => { } const session = await auth.api.getSession({ - headers: req.headers, + // @ts-expect-error + headers: new Headers(req.headers), }); return createInnerTRPCContext({ db, user: session?.user }); diff --git a/packages/auth/package.json b/packages/auth/package.json index 5481db3c..6e806ced 100644 --- a/packages/auth/package.json +++ b/packages/auth/package.json @@ -17,9 +17,11 @@ }, "devDependencies": { "@kan/db": "workspace:*", + "@kan/email": "workspace:*", "@kan/eslint-config": "workspace:*", "@kan/prettier-config": "workspace:*", "@kan/shared": "workspace:*", + "@kan/stripe": "workspace:*", "@kan/tsconfig": "workspace:*", "eslint": "catalog:", "prettier": "catalog:", diff --git a/packages/auth/src/auth.ts b/packages/auth/src/auth.ts index ac493ed7..98f27f98 100644 --- a/packages/auth/src/auth.ts +++ b/packages/auth/src/auth.ts @@ -1,10 +1,14 @@ import { betterAuth } from "better-auth"; import { drizzleAdapter } from "better-auth/adapters/drizzle"; +import { createAuthMiddleware } from "better-auth/api"; import { apiKey } from "better-auth/plugins"; import { magicLink } from "better-auth/plugins/magic-link"; import { createDrizzleClient } from "@kan/db/client"; +import * as userRepo from "@kan/db/repository/user.repo"; import * as schema from "@kan/db/schema"; +import { sendEmail } from "@kan/email"; +import { createStripeClient } from "@kan/stripe"; const db = createDrizzleClient(); @@ -38,11 +42,36 @@ export const auth = betterAuth({ plugins: [ apiKey(), magicLink({ - sendMagicLink: async ({ email, token, url }, request) => { - // send email to user + sendMagicLink: async ({ email, url }) => { + await sendEmail(email, "Sign in to kan.bn", "MAGIC_LINK", { + magicLoginUrl: url, + }); }, }), ], + hooks: { + // after: createAuthMiddleware(async (ctx) => { + // if (ctx.path.startsWith("/sign-up") || ctx.path.startsWith("/sign-in")) { + // const session = ctx.context.session; + // if ( + // session && + // process.env.NEXT_PUBLIC_KAN_ENV === "cloud" && + // !session.user.stripeCustomerId + // ) { + // const stripe = createStripeClient(); + // const stripeCustomer = await stripe.customers.create({ + // email: session.user.email, + // metadata: { + // userId: session.user.id, + // }, + // }); + // await userRepo.update(db, session.user.id, { + // stripeCustomerId: stripeCustomer.id, + // }); + // } + // } + // }), + }, advanced: { cookiePrefix: "kan", database: { diff --git a/packages/auth/src/clients.ts b/packages/auth/src/clients.ts index f1012dd4..d0f471c0 100644 --- a/packages/auth/src/clients.ts +++ b/packages/auth/src/clients.ts @@ -1,3 +1,6 @@ +import { magicLinkClient } from "better-auth/client/plugins"; import { createAuthClient } from "better-auth/react"; -export const authClient = createAuthClient(); +export const authClient = createAuthClient({ + plugins: [magicLinkClient()], +}); diff --git a/packages/db/src/repository/card.repo.ts b/packages/db/src/repository/card.repo.ts index bb37c1ed..9e534f20 100644 --- a/packages/db/src/repository/card.repo.ts +++ b/packages/db/src/repository/card.repo.ts @@ -1,8 +1,6 @@ -import type { SupabaseClient } from "@supabase/supabase-js"; import { and, asc, desc, eq, gt, inArray, isNull, sql } from "drizzle-orm"; import type { dbClient } from "@kan/db/client"; -import type { Database } from "@kan/db/types/database.types"; import { cardActivities, cards, diff --git a/packages/db/src/repository/list.repo.ts b/packages/db/src/repository/list.repo.ts index a4052739..94c37a09 100644 --- a/packages/db/src/repository/list.repo.ts +++ b/packages/db/src/repository/list.repo.ts @@ -1,8 +1,6 @@ -import type { SupabaseClient } from "@supabase/supabase-js"; import { and, desc, eq, gt, isNull, sql } from "drizzle-orm"; import type { dbClient } from "@kan/db/client"; -import type { Database } from "@kan/db/types/database.types"; import { lists } from "@kan/db/schema"; import { generateUID } from "@kan/shared/utils"; diff --git a/packages/db/src/repository/user.repo.ts b/packages/db/src/repository/user.repo.ts index 091e9325..fcc86211 100644 --- a/packages/db/src/repository/user.repo.ts +++ b/packages/db/src/repository/user.repo.ts @@ -47,18 +47,20 @@ export const create = async ( export const update = async ( db: dbClient, userId: string, - updates: { image?: string; name?: string }, + updates: { image?: string; name?: string; stripeCustomerId?: string }, ) => { const [result] = await db .update(users) .set({ name: updates.name, image: updates.image, + stripeCustomerId: updates.stripeCustomerId, }) .where(eq(users.id, userId)) .returning({ name: users.name, image: users.image, + stripeCustomerId: users.stripeCustomerId, }); return result; diff --git a/packages/db/src/types/database.types.ts b/packages/db/src/types/database.types.ts deleted file mode 100644 index ff8fde21..00000000 --- a/packages/db/src/types/database.types.ts +++ /dev/null @@ -1,940 +0,0 @@ -export type Json = - | string - | number - | boolean - | null - | { [key: string]: Json | undefined } - | Json[] - -export type Database = { - public: { - Tables: { - _card_labels: { - Row: { - cardId: number - labelId: number - } - Insert: { - cardId: number - labelId: number - } - Update: { - cardId?: number - labelId?: number - } - Relationships: [ - { - foreignKeyName: "_card_labels_cardId_card_id_fk" - columns: ["cardId"] - isOneToOne: false - referencedRelation: "card" - referencedColumns: ["id"] - }, - { - foreignKeyName: "_card_labels_labelId_label_id_fk" - columns: ["labelId"] - isOneToOne: false - referencedRelation: "label" - referencedColumns: ["id"] - }, - ] - } - _card_workspace_members: { - Row: { - cardId: number - workspaceMemberId: number - } - Insert: { - cardId: number - workspaceMemberId: number - } - Update: { - cardId?: number - workspaceMemberId?: number - } - Relationships: [ - { - foreignKeyName: "_card_workspace_members_cardId_card_id_fk" - columns: ["cardId"] - isOneToOne: false - referencedRelation: "card" - referencedColumns: ["id"] - }, - { - foreignKeyName: "_card_workspace_members_workspaceMemberId_workspace_members_id_" - columns: ["workspaceMemberId"] - isOneToOne: false - referencedRelation: "workspace_members" - referencedColumns: ["id"] - }, - ] - } - board: { - Row: { - createdAt: string - createdBy: string - deletedAt: string | null - deletedBy: string | null - description: string | null - id: number - importId: number | null - name: string - publicId: string - slug: string - updatedAt: string | null - visibility: Database["public"]["Enums"]["board_visibility"] - workspaceId: number - } - Insert: { - createdAt?: string - createdBy: string - deletedAt?: string | null - deletedBy?: string | null - description?: string | null - id?: number - importId?: number | null - name: string - publicId: string - slug: string - updatedAt?: string | null - visibility?: Database["public"]["Enums"]["board_visibility"] - workspaceId: number - } - Update: { - createdAt?: string - createdBy?: string - deletedAt?: string | null - deletedBy?: string | null - description?: string | null - id?: number - importId?: number | null - name?: string - publicId?: string - slug?: string - updatedAt?: string | null - visibility?: Database["public"]["Enums"]["board_visibility"] - workspaceId?: number - } - Relationships: [ - { - foreignKeyName: "board_createdBy_user_id_fk" - columns: ["createdBy"] - isOneToOne: false - referencedRelation: "user" - referencedColumns: ["id"] - }, - { - foreignKeyName: "board_deletedBy_user_id_fk" - columns: ["deletedBy"] - isOneToOne: false - referencedRelation: "user" - referencedColumns: ["id"] - }, - { - foreignKeyName: "board_importId_import_id_fk" - columns: ["importId"] - isOneToOne: false - referencedRelation: "import" - referencedColumns: ["id"] - }, - { - foreignKeyName: "board_workspaceId_workspace_id_fk" - columns: ["workspaceId"] - isOneToOne: false - referencedRelation: "workspace" - referencedColumns: ["id"] - }, - ] - } - card: { - Row: { - createdAt: string - createdBy: string - deletedAt: string | null - deletedBy: string | null - description: string | null - id: number - importId: number | null - index: number - listId: number - publicId: string - title: string - updatedAt: string | null - } - Insert: { - createdAt?: string - createdBy: string - deletedAt?: string | null - deletedBy?: string | null - description?: string | null - id?: number - importId?: number | null - index: number - listId: number - publicId: string - title: string - updatedAt?: string | null - } - Update: { - createdAt?: string - createdBy?: string - deletedAt?: string | null - deletedBy?: string | null - description?: string | null - id?: number - importId?: number | null - index?: number - listId?: number - publicId?: string - title?: string - updatedAt?: string | null - } - Relationships: [ - { - foreignKeyName: "card_createdBy_user_id_fk" - columns: ["createdBy"] - isOneToOne: false - referencedRelation: "user" - referencedColumns: ["id"] - }, - { - foreignKeyName: "card_deletedBy_user_id_fk" - columns: ["deletedBy"] - isOneToOne: false - referencedRelation: "user" - referencedColumns: ["id"] - }, - { - foreignKeyName: "card_importId_import_id_fk" - columns: ["importId"] - isOneToOne: false - referencedRelation: "import" - referencedColumns: ["id"] - }, - { - foreignKeyName: "card_listId_list_id_fk" - columns: ["listId"] - isOneToOne: false - referencedRelation: "list" - referencedColumns: ["id"] - }, - ] - } - card_activity: { - Row: { - cardId: number - commentId: number | null - createdAt: string - createdBy: string - fromComment: string | null - fromDescription: string | null - fromIndex: number | null - fromListId: number | null - fromTitle: string | null - id: number - labelId: number | null - publicId: string - toComment: string | null - toDescription: string | null - toIndex: number | null - toListId: number | null - toTitle: string | null - type: Database["public"]["Enums"]["card_activity_type"] - workspaceMemberId: number | null - } - Insert: { - cardId: number - commentId?: number | null - createdAt?: string - createdBy: string - fromComment?: string | null - fromDescription?: string | null - fromIndex?: number | null - fromListId?: number | null - fromTitle?: string | null - id?: number - labelId?: number | null - publicId: string - toComment?: string | null - toDescription?: string | null - toIndex?: number | null - toListId?: number | null - toTitle?: string | null - type: Database["public"]["Enums"]["card_activity_type"] - workspaceMemberId?: number | null - } - Update: { - cardId?: number - commentId?: number | null - createdAt?: string - createdBy?: string - fromComment?: string | null - fromDescription?: string | null - fromIndex?: number | null - fromListId?: number | null - fromTitle?: string | null - id?: number - labelId?: number | null - publicId?: string - toComment?: string | null - toDescription?: string | null - toIndex?: number | null - toListId?: number | null - toTitle?: string | null - type?: Database["public"]["Enums"]["card_activity_type"] - workspaceMemberId?: number | null - } - Relationships: [ - { - foreignKeyName: "card_activity_cardId_card_id_fk" - columns: ["cardId"] - isOneToOne: false - referencedRelation: "card" - referencedColumns: ["id"] - }, - { - foreignKeyName: "card_activity_commentId_card_comments_id_fk" - columns: ["commentId"] - isOneToOne: false - referencedRelation: "card_comments" - referencedColumns: ["id"] - }, - { - foreignKeyName: "card_activity_createdBy_user_id_fk" - columns: ["createdBy"] - isOneToOne: false - referencedRelation: "user" - referencedColumns: ["id"] - }, - { - foreignKeyName: "card_activity_fromListId_list_id_fk" - columns: ["fromListId"] - isOneToOne: false - referencedRelation: "list" - referencedColumns: ["id"] - }, - { - foreignKeyName: "card_activity_labelId_label_id_fk" - columns: ["labelId"] - isOneToOne: false - referencedRelation: "label" - referencedColumns: ["id"] - }, - { - foreignKeyName: "card_activity_toListId_list_id_fk" - columns: ["toListId"] - isOneToOne: false - referencedRelation: "list" - referencedColumns: ["id"] - }, - { - foreignKeyName: "card_activity_workspaceMemberId_workspace_members_id_fk" - columns: ["workspaceMemberId"] - isOneToOne: false - referencedRelation: "workspace_members" - referencedColumns: ["id"] - }, - ] - } - card_comments: { - Row: { - cardId: number - comment: string - createdAt: string - createdBy: string - deletedAt: string | null - deletedBy: string | null - id: number - publicId: string - updatedAt: string | null - } - Insert: { - cardId: number - comment: string - createdAt?: string - createdBy: string - deletedAt?: string | null - deletedBy?: string | null - id?: number - publicId: string - updatedAt?: string | null - } - Update: { - cardId?: number - comment?: string - createdAt?: string - createdBy?: string - deletedAt?: string | null - deletedBy?: string | null - id?: number - publicId?: string - updatedAt?: string | null - } - Relationships: [ - { - foreignKeyName: "card_comments_cardId_card_id_fk" - columns: ["cardId"] - isOneToOne: false - referencedRelation: "card" - referencedColumns: ["id"] - }, - { - foreignKeyName: "card_comments_createdBy_user_id_fk" - columns: ["createdBy"] - isOneToOne: false - referencedRelation: "user" - referencedColumns: ["id"] - }, - { - foreignKeyName: "card_comments_deletedBy_user_id_fk" - columns: ["deletedBy"] - isOneToOne: false - referencedRelation: "user" - referencedColumns: ["id"] - }, - ] - } - feedback: { - Row: { - createdAt: string - createdBy: string - feedback: string - id: number - reviewed: boolean - updatedAt: string | null - url: string - } - Insert: { - createdAt?: string - createdBy: string - feedback: string - id?: number - reviewed?: boolean - updatedAt?: string | null - url: string - } - Update: { - createdAt?: string - createdBy?: string - feedback?: string - id?: number - reviewed?: boolean - updatedAt?: string | null - url?: string - } - Relationships: [ - { - foreignKeyName: "feedback_createdBy_user_id_fk" - columns: ["createdBy"] - isOneToOne: false - referencedRelation: "user" - referencedColumns: ["id"] - }, - ] - } - import: { - Row: { - createdAt: string - createdBy: string - id: number - publicId: string - source: Database["public"]["Enums"]["source"] - status: Database["public"]["Enums"]["status"] - } - Insert: { - createdAt?: string - createdBy: string - id?: number - publicId: string - source: Database["public"]["Enums"]["source"] - status: Database["public"]["Enums"]["status"] - } - Update: { - createdAt?: string - createdBy?: string - id?: number - publicId?: string - source?: Database["public"]["Enums"]["source"] - status?: Database["public"]["Enums"]["status"] - } - Relationships: [ - { - foreignKeyName: "import_createdBy_user_id_fk" - columns: ["createdBy"] - isOneToOne: false - referencedRelation: "user" - referencedColumns: ["id"] - }, - ] - } - label: { - Row: { - boardId: number - colourCode: string | null - createdAt: string - createdBy: string - id: number - importId: number | null - name: string - publicId: string - updatedAt: string | null - } - Insert: { - boardId: number - colourCode?: string | null - createdAt?: string - createdBy: string - id?: number - importId?: number | null - name: string - publicId: string - updatedAt?: string | null - } - Update: { - boardId?: number - colourCode?: string | null - createdAt?: string - createdBy?: string - id?: number - importId?: number | null - name?: string - publicId?: string - updatedAt?: string | null - } - Relationships: [ - { - foreignKeyName: "label_boardId_board_id_fk" - columns: ["boardId"] - isOneToOne: false - referencedRelation: "board" - referencedColumns: ["id"] - }, - { - foreignKeyName: "label_createdBy_user_id_fk" - columns: ["createdBy"] - isOneToOne: false - referencedRelation: "user" - referencedColumns: ["id"] - }, - { - foreignKeyName: "label_importId_import_id_fk" - columns: ["importId"] - isOneToOne: false - referencedRelation: "import" - referencedColumns: ["id"] - }, - ] - } - list: { - Row: { - boardId: number - createdAt: string - createdBy: string - deletedAt: string | null - deletedBy: string | null - id: number - importId: number | null - index: number - name: string - publicId: string - updatedAt: string | null - } - Insert: { - boardId: number - createdAt?: string - createdBy: string - deletedAt?: string | null - deletedBy?: string | null - id?: number - importId?: number | null - index: number - name: string - publicId: string - updatedAt?: string | null - } - Update: { - boardId?: number - createdAt?: string - createdBy?: string - deletedAt?: string | null - deletedBy?: string | null - id?: number - importId?: number | null - index?: number - name?: string - publicId?: string - updatedAt?: string | null - } - Relationships: [ - { - foreignKeyName: "list_boardId_board_id_fk" - columns: ["boardId"] - isOneToOne: false - referencedRelation: "board" - referencedColumns: ["id"] - }, - { - foreignKeyName: "list_createdBy_user_id_fk" - columns: ["createdBy"] - isOneToOne: false - referencedRelation: "user" - referencedColumns: ["id"] - }, - { - foreignKeyName: "list_deletedBy_user_id_fk" - columns: ["deletedBy"] - isOneToOne: false - referencedRelation: "user" - referencedColumns: ["id"] - }, - { - foreignKeyName: "list_importId_import_id_fk" - columns: ["importId"] - isOneToOne: false - referencedRelation: "import" - referencedColumns: ["id"] - }, - ] - } - user: { - Row: { - email: string - emailVerified: string | null - id: string - image: string | null - name: string | null - stripeCustomerId: string | null - } - Insert: { - email: string - emailVerified?: string | null - id: string - image?: string | null - name?: string | null - stripeCustomerId?: string | null - } - Update: { - email?: string - emailVerified?: string | null - id?: string - image?: string | null - name?: string | null - stripeCustomerId?: string | null - } - Relationships: [] - } - workspace: { - Row: { - createdAt: string - createdBy: string - deletedAt: string | null - deletedBy: string | null - description: string | null - id: number - name: string - plan: Database["public"]["Enums"]["workspace_plan"] - publicId: string - slug: string - updatedAt: string | null - } - Insert: { - createdAt?: string - createdBy: string - deletedAt?: string | null - deletedBy?: string | null - description?: string | null - id?: number - name: string - plan?: Database["public"]["Enums"]["workspace_plan"] - publicId: string - slug: string - updatedAt?: string | null - } - Update: { - createdAt?: string - createdBy?: string - deletedAt?: string | null - deletedBy?: string | null - description?: string | null - id?: number - name?: string - plan?: Database["public"]["Enums"]["workspace_plan"] - publicId?: string - slug?: string - updatedAt?: string | null - } - Relationships: [ - { - foreignKeyName: "workspace_createdBy_user_id_fk" - columns: ["createdBy"] - isOneToOne: false - referencedRelation: "user" - referencedColumns: ["id"] - }, - { - foreignKeyName: "workspace_deletedBy_user_id_fk" - columns: ["deletedBy"] - isOneToOne: false - referencedRelation: "user" - referencedColumns: ["id"] - }, - ] - } - workspace_members: { - Row: { - createdAt: string - createdBy: string - deletedAt: string | null - deletedBy: string | null - id: number - publicId: string - role: Database["public"]["Enums"]["role"] - status: Database["public"]["Enums"]["member_status"] - updatedAt: string | null - userId: string - workspaceId: number - } - Insert: { - createdAt?: string - createdBy: string - deletedAt?: string | null - deletedBy?: string | null - id?: number - publicId: string - role: Database["public"]["Enums"]["role"] - status?: Database["public"]["Enums"]["member_status"] - updatedAt?: string | null - userId: string - workspaceId: number - } - Update: { - createdAt?: string - createdBy?: string - deletedAt?: string | null - deletedBy?: string | null - id?: number - publicId?: string - role?: Database["public"]["Enums"]["role"] - status?: Database["public"]["Enums"]["member_status"] - updatedAt?: string | null - userId?: string - workspaceId?: number - } - Relationships: [ - { - foreignKeyName: "workspace_members_deletedBy_user_id_fk" - columns: ["deletedBy"] - isOneToOne: false - referencedRelation: "user" - referencedColumns: ["id"] - }, - { - foreignKeyName: "workspace_members_userId_user_id_fk" - columns: ["userId"] - isOneToOne: false - referencedRelation: "user" - referencedColumns: ["id"] - }, - { - foreignKeyName: "workspace_members_workspaceId_workspace_id_fk" - columns: ["workspaceId"] - isOneToOne: false - referencedRelation: "workspace" - referencedColumns: ["id"] - }, - ] - } - workspace_slugs: { - Row: { - slug: string - type: Database["public"]["Enums"]["slug_type"] - } - Insert: { - slug: string - type: Database["public"]["Enums"]["slug_type"] - } - Update: { - slug?: string - type?: Database["public"]["Enums"]["slug_type"] - } - Relationships: [] - } - } - Views: { - [_ in never]: never - } - Functions: { - is_workspace_admin: { - Args: { - user_id: string - workspace_id: number - } - Returns: boolean - } - push_card_index: { - Args: { - list_id: number - card_index: number - } - Returns: undefined - } - reorder_cards: { - Args: { - card_id: number - current_list_id: number - new_list_id: number - current_index: number - new_index: number - } - Returns: boolean - } - reorder_lists: { - Args: { - board_id: number - list_id: number - current_index: number - new_index: number - } - Returns: boolean - } - shift_card_index: { - Args: { - list_id: number - card_index: number - } - Returns: undefined - } - shift_list_index: { - Args: { - board_id: number - list_index: number - } - Returns: undefined - } - } - Enums: { - board_visibility: "private" | "public" - card_activity_type: - | "card.created" - | "card.updated.title" - | "card.updated.description" - | "card.updated.index" - | "card.updated.list" - | "card.updated.label.added" - | "card.updated.label.removed" - | "card.updated.member.added" - | "card.updated.member.removed" - | "card.archived" - | "card.updated.comment.added" - | "card.updated.comment.updated" - | "card.updated.comment.deleted" - member_status: "invited" | "active" | "removed" - role: "admin" | "member" | "guest" - slug_type: "reserved" | "premium" - source: "trello" - status: "started" | "success" | "failed" - workspace_invite_status: "pending" | "accepted" | "cancelled" - workspace_plan: "free" | "pro" | "enterprise" - } - CompositeTypes: { - [_ in never]: never - } - } -} - -type PublicSchema = Database[Extract] - -export type Tables< - PublicTableNameOrOptions extends - | keyof (PublicSchema["Tables"] & PublicSchema["Views"]) - | { schema: keyof Database }, - TableName extends PublicTableNameOrOptions extends { schema: keyof Database } - ? keyof (Database[PublicTableNameOrOptions["schema"]]["Tables"] & - Database[PublicTableNameOrOptions["schema"]]["Views"]) - : never = never, -> = PublicTableNameOrOptions extends { schema: keyof Database } - ? (Database[PublicTableNameOrOptions["schema"]]["Tables"] & - Database[PublicTableNameOrOptions["schema"]]["Views"])[TableName] extends { - Row: infer R - } - ? R - : never - : PublicTableNameOrOptions extends keyof (PublicSchema["Tables"] & - PublicSchema["Views"]) - ? (PublicSchema["Tables"] & - PublicSchema["Views"])[PublicTableNameOrOptions] extends { - Row: infer R - } - ? R - : never - : never - -export type TablesInsert< - PublicTableNameOrOptions extends - | keyof PublicSchema["Tables"] - | { schema: keyof Database }, - TableName extends PublicTableNameOrOptions extends { schema: keyof Database } - ? keyof Database[PublicTableNameOrOptions["schema"]]["Tables"] - : never = never, -> = PublicTableNameOrOptions extends { schema: keyof Database } - ? Database[PublicTableNameOrOptions["schema"]]["Tables"][TableName] extends { - Insert: infer I - } - ? I - : never - : PublicTableNameOrOptions extends keyof PublicSchema["Tables"] - ? PublicSchema["Tables"][PublicTableNameOrOptions] extends { - Insert: infer I - } - ? I - : never - : never - -export type TablesUpdate< - PublicTableNameOrOptions extends - | keyof PublicSchema["Tables"] - | { schema: keyof Database }, - TableName extends PublicTableNameOrOptions extends { schema: keyof Database } - ? keyof Database[PublicTableNameOrOptions["schema"]]["Tables"] - : never = never, -> = PublicTableNameOrOptions extends { schema: keyof Database } - ? Database[PublicTableNameOrOptions["schema"]]["Tables"][TableName] extends { - Update: infer U - } - ? U - : never - : PublicTableNameOrOptions extends keyof PublicSchema["Tables"] - ? PublicSchema["Tables"][PublicTableNameOrOptions] extends { - Update: infer U - } - ? U - : never - : never - -export type Enums< - PublicEnumNameOrOptions extends - | keyof PublicSchema["Enums"] - | { schema: keyof Database }, - EnumName extends PublicEnumNameOrOptions extends { schema: keyof Database } - ? keyof Database[PublicEnumNameOrOptions["schema"]]["Enums"] - : never = never, -> = PublicEnumNameOrOptions extends { schema: keyof Database } - ? Database[PublicEnumNameOrOptions["schema"]]["Enums"][EnumName] - : PublicEnumNameOrOptions extends keyof PublicSchema["Enums"] - ? PublicSchema["Enums"][PublicEnumNameOrOptions] - : never - -export type CompositeTypes< - PublicCompositeTypeNameOrOptions extends - | keyof PublicSchema["CompositeTypes"] - | { schema: keyof Database }, - CompositeTypeName extends PublicCompositeTypeNameOrOptions extends { - schema: keyof Database - } - ? keyof Database[PublicCompositeTypeNameOrOptions["schema"]]["CompositeTypes"] - : never = never, -> = PublicCompositeTypeNameOrOptions extends { schema: keyof Database } - ? Database[PublicCompositeTypeNameOrOptions["schema"]]["CompositeTypes"][CompositeTypeName] - : PublicCompositeTypeNameOrOptions extends keyof PublicSchema["CompositeTypes"] - ? PublicSchema["CompositeTypes"][PublicCompositeTypeNameOrOptions] - : never diff --git a/packages/supabase/eslint.config.js b/packages/stripe/eslint.config.js similarity index 100% rename from packages/supabase/eslint.config.js rename to packages/stripe/eslint.config.js diff --git a/packages/supabase/package.json b/packages/stripe/package.json similarity index 72% rename from packages/supabase/package.json rename to packages/stripe/package.json index c95e54b7..6b96582a 100644 --- a/packages/supabase/package.json +++ b/packages/stripe/package.json @@ -1,11 +1,10 @@ { - "name": "@kan/supabase", + "name": "@kan/stripe", "private": true, "version": "0.1.0", "type": "module", "exports": { - ".": "./src/index.ts", - "./clients": "./src/clients.ts" + ".": "./src/index.ts" }, "license": "MIT", "scripts": { @@ -21,15 +20,11 @@ "@kan/prettier-config": "workspace:*", "@kan/tsconfig": "workspace:*", "eslint": "catalog:", - "next": "^14.2.15", "prettier": "catalog:", "typescript": "catalog:" }, "prettier": "@kan/prettier-config", "dependencies": { - "@edge-runtime/cookies": "^6.0.0", - "@kan/db": "workspace:^", - "@supabase/ssr": "^0.5.2", - "@supabase/supabase-js": "^2.47.3" + "stripe": "^18.1.0" } } diff --git a/packages/stripe/src/index.ts b/packages/stripe/src/index.ts new file mode 100644 index 00000000..f604dd1f --- /dev/null +++ b/packages/stripe/src/index.ts @@ -0,0 +1,20 @@ +import { Stripe } from "stripe"; + +export const name = "stripe"; + +const stripeSecretKey = process.env.STRIPE_SECRET_KEY; + +const createStripeClient = () => { + if (!stripeSecretKey) { + throw new Error("STRIPE_SECRET_KEY is not set"); + } + + const stripe = new Stripe(stripeSecretKey, { + apiVersion: "2025-04-30.basil", + httpClient: Stripe.createFetchHttpClient(), + }); + + return stripe; +}; + +export { createStripeClient }; diff --git a/packages/supabase/tsconfig.json b/packages/stripe/tsconfig.json similarity index 100% rename from packages/supabase/tsconfig.json rename to packages/stripe/tsconfig.json diff --git a/packages/supabase/src/clients.ts b/packages/supabase/src/clients.ts deleted file mode 100644 index 6b2eb35f..00000000 --- a/packages/supabase/src/clients.ts +++ /dev/null @@ -1,103 +0,0 @@ -import type { CookieOptions } from "@supabase/ssr"; -import type { NextApiRequest } from "next"; -import type { NextRequest, NextResponse } from "next/server"; -import { RequestCookies } from "@edge-runtime/cookies"; -import { createServerClient, serialize } from "@supabase/ssr"; - -import type { Database } from "@kan/db/types/database.types"; - -export function createNextClient(req: NextRequest, res: NextResponse) { - const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL; - const serviceKey = process.env.SUPABASE_SERVICE_API_KEY; - - if (!supabaseUrl || !serviceKey) { - throw new Error("Missing Supabase environment variables"); - } - - const supabase = createServerClient( - supabaseUrl, - serviceKey, - { - cookies: { - get(name: string) { - return req.cookies.get(name)?.value; - }, - set(name: string, value: string, options: CookieOptions) { - res.headers.append("Set-Cookie", serialize(name, value, options)); - }, - remove(name: string, options: CookieOptions) { - res.headers.append("Set-Cookie", serialize(name, "", options)); - }, - }, - }, - ); - - return supabase; -} - -export function createNextApiClient(req: NextApiRequest) { - const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL; - const serviceKey = process.env.SUPABASE_SERVICE_API_KEY; - - if (!supabaseUrl || !serviceKey) { - throw new Error("Missing Supabase environment variables"); - } - - const authHeader = req.headers.authorization; - const accessToken = authHeader?.startsWith("Bearer ") - ? authHeader.substring(7) - : null; - - const supabase = createServerClient( - supabaseUrl, - serviceKey, - { - auth: { - persistSession: false, - ...(accessToken && { - autoRefreshToken: false, - detectSessionInUrl: false, - access_token: accessToken, - }), - }, - cookies: { - get: (_name: string) => "", - set: (_name: string, _value: string, _options: CookieOptions) => - undefined, - remove: (_name: string, _options: CookieOptions) => undefined, - }, - }, - ); - - return supabase; -} - -export function createTRPCClient(req: Request, resHeaders: Headers) { - const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL; - const serviceKey = process.env.SUPABASE_SERVICE_API_KEY; - - if (!supabaseUrl || !serviceKey) { - throw new Error("Missing Supabase environment variables"); - } - - const supabase = createServerClient( - supabaseUrl, - serviceKey, - { - cookies: { - get(name: string) { - const cookies = new RequestCookies(req.headers); - return cookies.get(name)?.value; - }, - set(name: string, value: string, options: CookieOptions) { - resHeaders.set("Set-Cookie", serialize(name, value, options)); - }, - remove(name: string, options: CookieOptions) { - resHeaders.set("Set-Cookie", serialize(name, "", options)); - }, - }, - }, - ); - - return supabase; -} diff --git a/packages/supabase/src/index.ts b/packages/supabase/src/index.ts deleted file mode 100644 index 6d82b4c4..00000000 --- a/packages/supabase/src/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export const name = "supabase"; - -export * from "@supabase/supabase-js"; -export * from "./clients"; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 36165b25..58113864 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -93,9 +93,6 @@ importers: '@kan/shared': specifier: workspace:^ version: link:../../packages/shared - '@kan/supabase': - specifier: workspace:^ - version: link:../../packages/supabase '@t3-oss/env-nextjs': specifier: ^0.11.1 version: 0.11.1(typescript@5.7.2)(zod@3.24.0) @@ -150,9 +147,6 @@ importers: react-lottie-player: specifier: ^1.5.5 version: 1.5.6(react@18.3.1) - stripe: - specifier: ^17.5.0 - version: 17.5.0 superjson: specifier: 2.2.1 version: 2.2.1 @@ -169,6 +163,9 @@ importers: '@kan/prettier-config': specifier: workspace:* version: link:../../tooling/prettier + '@kan/stripe': + specifier: workspace:* + version: link:../../packages/stripe '@kan/tailwind-config': specifier: workspace:* version: link:../../tooling/tailwind @@ -223,9 +220,9 @@ importers: '@kan/shared': specifier: workspace:^ version: link:../shared - '@kan/supabase': + '@kan/stripe': specifier: workspace:^ - version: link:../supabase + version: link:../stripe '@trpc/server': specifier: 'catalog:' version: 11.0.0-rc.660(typescript@5.7.2) @@ -267,6 +264,9 @@ importers: '@kan/db': specifier: workspace:* version: link:../db + '@kan/email': + specifier: workspace:* + version: link:../email '@kan/eslint-config': specifier: workspace:* version: link:../../tooling/eslint @@ -276,6 +276,9 @@ importers: '@kan/shared': specifier: workspace:* version: link:../shared + '@kan/stripe': + specifier: workspace:* + version: link:../stripe '@kan/tsconfig': specifier: workspace:* version: link:../../tooling/typescript @@ -388,20 +391,11 @@ importers: specifier: 'catalog:' version: 5.7.2 - packages/supabase: + packages/stripe: dependencies: - '@edge-runtime/cookies': - specifier: ^6.0.0 - version: 6.0.0 - '@kan/db': - specifier: workspace:^ - version: link:../db - '@supabase/ssr': - specifier: ^0.5.2 - version: 0.5.2(@supabase/supabase-js@2.47.3(bufferutil@4.0.8)(utf-8-validate@6.0.3)) - '@supabase/supabase-js': - specifier: ^2.47.3 - version: 2.47.3(bufferutil@4.0.8)(utf-8-validate@6.0.3) + stripe: + specifier: ^18.1.0 + version: 18.1.0(@types/node@20.17.9) devDependencies: '@kan/eslint-config': specifier: workspace:* @@ -415,9 +409,6 @@ importers: eslint: specifier: 'catalog:' version: 9.16.0(jiti@1.21.6) - next: - specifier: ^14.2.15 - version: 14.2.20(react-dom@18.3.1(react@18.3.1))(react@18.3.1) prettier: specifier: 'catalog:' version: 3.4.2 @@ -640,10 +631,6 @@ packages: '@drizzle-team/brocli@0.10.2': resolution: {integrity: sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w==} - '@edge-runtime/cookies@6.0.0': - resolution: {integrity: sha512-VVO/8AwC2qVbygLb2IOkX1zWFx2yWIHzFv4D602CTnoRffd/+cdcXqpSydKaedFrk7a1dRYXbWwjzfV/gwZ2Gw==} - engines: {node: '>=18'} - '@emnapi/runtime@1.3.1': resolution: {integrity: sha512-kEBmG8KyqtxJZv+ygbEim+KCGtIq1fC22Ms3S4ziXmYKm8uyoLX0MHONVKwp+9opg390VaKRNt4a7A9NwmpNhw==} @@ -1744,33 +1731,6 @@ packages: '@socket.io/component-emitter@3.1.2': resolution: {integrity: sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==} - '@supabase/auth-js@2.66.1': - resolution: {integrity: sha512-kOW+04SuDXmP2jRX9JL1Rgzduj8BcOG1qC3RaWdZsxnv89svNCdLRv8PfXW3QPKJdw0k1jF30OlQDPkzbDEL9w==} - - '@supabase/functions-js@2.4.3': - resolution: {integrity: sha512-sOLXy+mWRyu4LLv1onYydq+10mNRQ4rzqQxNhbrKLTLTcdcmS9hbWif0bGz/NavmiQfPs4ZcmQJp4WqOXlR4AQ==} - - '@supabase/node-fetch@2.6.15': - resolution: {integrity: sha512-1ibVeYUacxWYi9i0cf5efil6adJ9WRyZBLivgjs+AUpewx1F3xPi7gLgaASI2SmIQxPoCEjAsLAzKPgMJVgOUQ==} - engines: {node: 4.x || >=6.0.0} - - '@supabase/postgrest-js@1.16.3': - resolution: {integrity: sha512-HI6dsbW68AKlOPofUjDTaosiDBCtW4XAm0D18pPwxoW3zKOE2Ru13Z69Wuys9fd6iTpfDViNco5sgrtnP0666A==} - - '@supabase/realtime-js@2.11.2': - resolution: {integrity: sha512-u/XeuL2Y0QEhXSoIPZZwR6wMXgB+RQbJzG9VErA3VghVt7uRfSVsjeqd7m5GhX3JR6dM/WRmLbVR8URpDWG4+w==} - - '@supabase/ssr@0.5.2': - resolution: {integrity: sha512-n3plRhr2Bs8Xun1o4S3k1CDv17iH5QY9YcoEvXX3bxV1/5XSasA0mNXYycFmADIdtdE6BG9MRjP5CGIs8qxC8A==} - peerDependencies: - '@supabase/supabase-js': ^2.43.4 - - '@supabase/storage-js@2.7.1': - resolution: {integrity: sha512-asYHcyDR1fKqrMpytAS1zjyEfvxuOIp1CIXX7ji4lHHcJKqyk+sLl/Vxgm4sN6u8zvuUtae9e4kDxQP2qrwWBA==} - - '@supabase/supabase-js@2.47.3': - resolution: {integrity: sha512-AmwTyHtOXdfjLVKiM+neYItB62T4gAl1jV8ZrIg3yp1Z1NICzYfsujJDSuELkrLkYvU/RGfZXpIBheDTt7fmwA==} - '@swc/counter@0.1.3': resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==} @@ -1890,9 +1850,6 @@ packages: '@types/cookie@0.4.1': resolution: {integrity: sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==} - '@types/cookie@0.6.0': - resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==} - '@types/cors@2.8.17': resolution: {integrity: sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==} @@ -1950,9 +1907,6 @@ packages: '@types/pg@8.11.6': resolution: {integrity: sha512-/2WmmBXHLsfRqzfHW7BNZ8SbYzE8OSk7i3WjFYvfgRHj7S1xj+16Je5fUKv3lVdVzk/zn9TXOqf+avFCFIE0yQ==} - '@types/phoenix@1.6.6': - resolution: {integrity: sha512-PIzZZlEppgrpoT2QgbnDU+MMzuR6BbCjllj0bM70lWoejMeNJAxCchxnv7J3XFkI8MpygtRpzXrIlmWUBclP5A==} - '@types/prop-types@15.7.14': resolution: {integrity: sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==} @@ -1979,9 +1933,6 @@ packages: '@types/unist@2.0.11': resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==} - '@types/ws@8.5.13': - resolution: {integrity: sha512-osM/gWBTPKgHV8XkTunnegTRIsvF6owmf5w+JtAfOw472dptdm0dlGv4xCt6GwQRcC2XVOvvRE/0bAoQcL2QkA==} - '@typescript-eslint/eslint-plugin@8.18.0': resolution: {integrity: sha512-NR2yS7qUqCL7AIxdJUQf2MKKNDVNaig/dEB0GBLU7D+ZdHgK1NoH/3wsgO3OnPVipn51tG3MAwaODEGil70WEw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -4871,9 +4822,14 @@ packages: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} - stripe@17.5.0: - resolution: {integrity: sha512-kcyeAkDFjGsVl17FqnG7q/+xIjt0ZjOo9Dm+q8deAvs2Xe4iAHrhxyoP4etUVFc+/LZJANjIPVR+ZOnt9hr/Ug==} + stripe@18.1.0: + resolution: {integrity: sha512-MLDiniPTHqcfIT3anyBPmOEcaiDhYa7/jRaNypQ3Rt2SJnayQZBvVbFghIziUCZdltGAndm/ZxVOSw6uuSCDig==} engines: {node: '>=12.*'} + peerDependencies: + '@types/node': '>=12.x.x' + peerDependenciesMeta: + '@types/node': + optional: true styled-jsx@5.1.1: resolution: {integrity: sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==} @@ -5498,8 +5454,6 @@ snapshots: '@drizzle-team/brocli@0.10.2': {} - '@edge-runtime/cookies@6.0.0': {} - '@emnapi/runtime@1.3.1': dependencies: tslib: 2.8.1 @@ -6424,54 +6378,6 @@ snapshots: '@socket.io/component-emitter@3.1.2': {} - '@supabase/auth-js@2.66.1': - dependencies: - '@supabase/node-fetch': 2.6.15 - - '@supabase/functions-js@2.4.3': - dependencies: - '@supabase/node-fetch': 2.6.15 - - '@supabase/node-fetch@2.6.15': - dependencies: - whatwg-url: 5.0.0 - - '@supabase/postgrest-js@1.16.3': - dependencies: - '@supabase/node-fetch': 2.6.15 - - '@supabase/realtime-js@2.11.2(bufferutil@4.0.8)(utf-8-validate@6.0.3)': - dependencies: - '@supabase/node-fetch': 2.6.15 - '@types/phoenix': 1.6.6 - '@types/ws': 8.5.13 - ws: 8.18.0(bufferutil@4.0.8)(utf-8-validate@6.0.3) - transitivePeerDependencies: - - bufferutil - - utf-8-validate - - '@supabase/ssr@0.5.2(@supabase/supabase-js@2.47.3(bufferutil@4.0.8)(utf-8-validate@6.0.3))': - dependencies: - '@supabase/supabase-js': 2.47.3(bufferutil@4.0.8)(utf-8-validate@6.0.3) - '@types/cookie': 0.6.0 - cookie: 0.7.2 - - '@supabase/storage-js@2.7.1': - dependencies: - '@supabase/node-fetch': 2.6.15 - - '@supabase/supabase-js@2.47.3(bufferutil@4.0.8)(utf-8-validate@6.0.3)': - dependencies: - '@supabase/auth-js': 2.66.1 - '@supabase/functions-js': 2.4.3 - '@supabase/node-fetch': 2.6.15 - '@supabase/postgrest-js': 1.16.3 - '@supabase/realtime-js': 2.11.2(bufferutil@4.0.8)(utf-8-validate@6.0.3) - '@supabase/storage-js': 2.7.1 - transitivePeerDependencies: - - bufferutil - - utf-8-validate - '@swc/counter@0.1.3': {} '@swc/helpers@0.5.13': @@ -6600,8 +6506,6 @@ snapshots: '@types/cookie@0.4.1': {} - '@types/cookie@0.6.0': {} - '@types/cors@2.8.17': dependencies: '@types/node': 20.17.9 @@ -6670,8 +6574,6 @@ snapshots: pg-protocol: 1.7.0 pg-types: 4.0.2 - '@types/phoenix@1.6.6': {} - '@types/prop-types@15.7.14': {} '@types/react-beautiful-dnd@13.1.8': @@ -6702,10 +6604,6 @@ snapshots: '@types/unist@2.0.11': {} - '@types/ws@8.5.13': - dependencies: - '@types/node': 20.17.9 - '@typescript-eslint/eslint-plugin@8.18.0(@typescript-eslint/parser@8.18.0(eslint@9.16.0(jiti@1.21.6))(typescript@5.7.2))(eslint@9.16.0(jiti@1.21.6))(typescript@5.7.2)': dependencies: '@eslint-community/regexpp': 4.12.1 @@ -10161,10 +10059,11 @@ snapshots: strip-json-comments@3.1.1: {} - stripe@17.5.0: + stripe@18.1.0(@types/node@20.17.9): dependencies: - '@types/node': 20.17.9 qs: 6.13.1 + optionalDependencies: + '@types/node': 20.17.9 styled-jsx@5.1.1(react@18.3.1): dependencies: diff --git a/turbo.json b/turbo.json index d8561c8a..4439e889 100644 --- a/turbo.json +++ b/turbo.json @@ -53,11 +53,9 @@ "EMAIL_FROM", "EMAIL_URL", "EMAIL_TOKEN", - "NEXT_PUBLIC_SUPABASE_URL", - "NEXT_PUBLIC_SUPABASE_ANON_KEY", - "NEXT_PUBLIC_SUPABASE_AUTH_COOKIE_NAME", - "SUPABASE_SERVICE_API_KEY", + "NEXT_PUBLIC_KAN_ENV", "STRIPE_SECRET_KEY", + "STRIPE_WEBHOOK_SECRET", "PORT" ], "globalPassThroughEnv": [