diff --git a/@/components/ui/button.tsx b/@/components/ui/button.tsx new file mode 100644 index 0000000..6042281 --- /dev/null +++ b/@/components/ui/button.tsx @@ -0,0 +1,55 @@ +import * as React from "react" +import { Slot } from "@radix-ui/react-slot" +import { cva, type VariantProps } from "class-variance-authority" +import { cn } from "@/lib/utils" + +const buttonVariants = cva( + "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50", + { + variants: { + variant: { + default: "bg-primary text-primary-foreground hover:bg-primary/90", + destructive: + "bg-destructive text-destructive-foreground hover:bg-destructive/90", + outline: + "border border-input bg-background hover:bg-accent hover:text-accent-foreground", + secondary: + "bg-secondary text-secondary-foreground hover:bg-secondary/80", + ghost: "hover:bg-accent hover:text-accent-foreground", + link: "text-primary underline-offset-4 hover:underline", + }, + size: { + default: "h-10 px-4 py-2", + sm: "h-9 rounded-md px-3", + lg: "h-11 rounded-md px-8", + icon: "h-10 w-10", + }, + }, + defaultVariants: { + variant: "default", + size: "default", + }, + } +) + +export interface ButtonProps + extends React.ButtonHTMLAttributes, + VariantProps { + asChild?: boolean +} + +const Button = React.forwardRef( + ({ className, variant, size, asChild = false, ...props }, ref) => { + const Comp = asChild ? Slot : "button" + return ( + + ) + } +) +Button.displayName = "Button" + +export { Button, buttonVariants } diff --git a/@/components/ui/card.tsx b/@/components/ui/card.tsx new file mode 100644 index 0000000..c1da9be --- /dev/null +++ b/@/components/ui/card.tsx @@ -0,0 +1,78 @@ +import * as React from "react" +import { cn } from "@/lib/utils" + +const Card = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +Card.displayName = "Card" + +const CardHeader = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +CardHeader.displayName = "CardHeader" + +const CardTitle = React.forwardRef< + HTMLParagraphElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +

+)) +CardTitle.displayName = "CardTitle" + +const CardDescription = React.forwardRef< + HTMLParagraphElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +

+)) +CardDescription.displayName = "CardDescription" + +const CardContent = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +

+)) +CardContent.displayName = "CardContent" + +const CardFooter = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +CardFooter.displayName = "CardFooter" + +export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent } diff --git a/app/components/GoogleLoginButton.tsx b/app/components/GoogleLoginButton.tsx new file mode 100644 index 0000000..54c0d8f --- /dev/null +++ b/app/components/GoogleLoginButton.tsx @@ -0,0 +1,46 @@ +// import { SocialsProvider } from "remix-auth-socials"; +import { Form, useSearchParams } from "@remix-run/react"; +import { Button } from "../../@/components/ui/button"; +// import { Button } from "@/components/ui/button"; + +// const GOGLE_ACTION_STRING = `/api/auth/${SocialsProvider.GOOGLE}`; +const GOGLE_ACTION_STRING = `/api/auth/google`; + +const GoogleLoginButton = () => { + const [searchParams] = useSearchParams(); + const redirectTo = searchParams.get("redirectTo") || ""; + + return ( +
+
+ + + +
+
+ ); +}; + +export default GoogleLoginButton; diff --git a/app/hooks/useSupabaseClient.ts b/app/hooks/useSupabaseClient.ts new file mode 100644 index 0000000..0c1b5bb --- /dev/null +++ b/app/hooks/useSupabaseClient.ts @@ -0,0 +1,46 @@ +import React from "react"; +import { useRevalidator } from "@remix-run/react"; +import { createBrowserClient } from "@supabase/ssr"; +import type { Session } from "@supabase/supabase-js"; +// import type { Database } from "database.types"; + +/** + * This code was referenced from the following tutorial: + * YouTube: https://youtu.be/ocWc_FFc5jE?si=70KZNTDwPLKZ7Pwz&t=5550 + * Github Repo : https://github.com/rajeshdavidbabu/remix-supabase-social/blob/master/youtube%20course/app/lib/supabase.ts + */ + +type SupabaseEnv = { + SUPABASE_URL: string; + SUPABASE_ANON_KEY: string; +}; + +type UseSupabase = { + env: SupabaseEnv; + serverSession: Session | null; +}; + +export const useSupabaseClient = ({ env, serverSession }: UseSupabase) => { + const [supabase] = React.useState(() => + createBrowserClient(env.SUPABASE_URL, env.SUPABASE_ANON_KEY) + ); + const serverAccessToken = serverSession?.access_token; + const revalidator = useRevalidator(); + + React.useEffect(() => { + const { + data: { subscription }, + } = supabase.auth.onAuthStateChange((event, session) => { + if (session?.access_token !== serverAccessToken) { + // Revalidate the app. + revalidator.revalidate(); + } + }); + + return () => { + subscription.unsubscribe(); + }; + }, [supabase.auth, serverAccessToken, revalidator]); + + return { supabase }; +}; \ No newline at end of file diff --git a/app/root.tsx b/app/root.tsx index a2e1714..0f5acd3 100644 --- a/app/root.tsx +++ b/app/root.tsx @@ -22,6 +22,8 @@ import { AuthenticityTokenProvider } from "remix-utils/csrf/react"; import { HoneypotProvider } from "remix-utils/honeypot/react"; import { honeypot } from "utils/honeypot.server"; import { getToast, type Toast } from "utils/toast.server"; +import { getSupabaseEnv, getSupabaseWithSessionAndHeaders } from "./services/supabase.server"; +import { useSupabaseClient } from "./hooks/useSupabaseClient"; import "./tailwind.css"; import "./globals.css"; @@ -33,6 +35,13 @@ export async function loader({ request }: LoaderFunctionArgs) { const { toast, headers: toastHeaders } = await getToast(request); + + const { serverSession, headers: supabaseHeaders } = await getSupabaseWithSessionAndHeaders({ + request, + }); + + const { DATABASE_URL, DATABASE_ANON_KEY } = getSupabaseEnv() + return json( { // username: os.userInfo().username, @@ -42,11 +51,15 @@ export async function loader({ request }: LoaderFunctionArgs) { ENV: getEnv(), csrfToken, honeyProps, + DATABASE_URL, + DATABASE_ANON_KEY, + serverSession, + domainUrl: process.env.ORIGIN || '', }, { headers: combineHeaders( csrfCookieHeader ? { "set-cookie": csrfCookieHeader } : null, - toastHeaders + toastHeaders, supabaseHeaders ), } ); @@ -85,13 +98,16 @@ function Document({ export function Layout({ children }: { children: React.ReactNode }) { const data = useLoaderData(); - console.log(data); - const location = useLocation(); - const isHome = location.pathname === "/"; + - return ( - <> - +console.log(data); +const location = useLocation(); +const isHome = location.pathname === "/"; + +return ( + <> + + {/* */} {!isHome && } {children} {data && data.toast ? : null} @@ -101,10 +117,20 @@ export function Layout({ children }: { children: React.ReactNode }) { export default function App() { const data = useLoaderData(); + const {supabase} = useSupabaseClient( + { + env: { + SUPABASE_URL: data.DATABASE_URL, + SUPABASE_ANON_KEY: data.DATABASE_ANON_KEY, + }, + serverSession: data.serverSession, + }) + return ( - ; + {/* ; */} + ); diff --git a/app/routes/auth.callback.tsx b/app/routes/auth.callback.tsx new file mode 100644 index 0000000..6936692 --- /dev/null +++ b/app/routes/auth.callback.tsx @@ -0,0 +1,135 @@ +// import { redirect, type LoaderFunctionArgs } from '@remix-run/node' +// // import { createServerClient, parseCookieHeader, serializeCookieHeader } from '@supabase/ssr' +// import { getSupabaseWithHeaders } from '~/services/supabase.server' + +// export async function loader({ request }: LoaderFunctionArgs) { +// console.log('Hit auth callback.................'); + +// const requestUrl = new URL(request.url) +// const code = requestUrl.searchParams.get('code') +// const next = requestUrl.searchParams.get('next') || '/' +// // const headers = new Headers() + +// if (code) { +// // const cookies = parse(request.headers.get('Cookie') ?? '') +// // const supabase = createServerClient(process.env.SUPABASE_URL!, process.env.SUPABASE_ANON_KEY!, { +// // cookies: { +// // getAll() { +// // return parseCookieHeader(request.headers.get('Cookie') ?? '') +// // }, +// // setAll(cookiesToSet) { +// // cookiesToSet.forEach(({ name, value, options }) => +// // headers.append('Set-Cookie', serializeCookieHeader(name, value, options)) +// // ) +// // }, +// // }, +// // }) + +// const { supabase, headers } = getSupabaseWithHeaders({ +// request, +// }) + +// console.log('supabase', supabase); +// console.log('headers', headers); + +// const { error } = await supabase.auth.exchangeCodeForSession(code) + +// if (!error) { +// return redirect(next, { headers }) +// } +// } + +// // return the user to an error page with instructions +// // return redirect('/auth/auth-code-error', { headers }) +// return redirect('/login') +// } + +import { redirect, type LoaderFunctionArgs } from '@remix-run/node' +import { json, useOutletContext } from '@remix-run/react'; +import { createServerClient, parseCookieHeader, serializeCookieHeader } from '@supabase/ssr' +import React from 'react'; +import { getSupabaseEnv } from '~/services/supabase.server'; + +export async function loader({ request }: LoaderFunctionArgs) { + console.log('Hit auth callback.................'); + + const response = new Response() + const url = new URL(request.url) + const code = url.searchParams.get('code') + + if (code) { + const { DATABASE_URL, DATABASE_ANON_KEY } = getSupabaseEnv(); + const supabaseClient = createServerClient( + DATABASE_URL, + DATABASE_ANON_KEY, + { request, response } + ) + await supabaseClient.auth.exchangeCodeForSession(code) + } + + return redirect('/', { + headers: response.headers, + }) + + const requestUrl = new URL(request.url) +// const code = requestUrl.searchParams.get('code') + const next = requestUrl.searchParams.get('next') || '/' + const headers = new Headers() + + if (code) { + const { DATABASE_URL, DATABASE_ANON_KEY } = getSupabaseEnv(); + // const cookies = parse(request.headers.get('Cookie') ?? '') + // const supabase = createServerClient(DATABASE_URL!, DATABASE_ANON_KEY!, { + // cookies: { + // getAll() { + // return parseCookieHeader(request.headers.get('Cookie') ?? '') + // }, + // setAll(cookiesToSet) { + // cookiesToSet.forEach(({ name, value, options }) => + // headers.append('Set-Cookie', serializeCookieHeader(name, value, options)) + // ) + // }, + // }, + // }) + const supabaseClient = createServerClient( + DATABASE_URL, + DATABASE_ANON_KEY, + { request, response } + ) + await supabaseClient.auth.exchangeCodeForSession(code) + + // const { error } = await supabase.auth.exchangeCodeForSession(code) + + if (!error) { + return redirect(next, { headers }) + } + } + + // return the user to an error page with instructions +// return redirect('/auth/auth-code-error', { headers }) +// return redirect('/login') + return json({ success: true }, { headers }); +} + + + +export default function Index() { + + const { supabase, domainUrl } = useOutletContext<{ supabase: Record; domainUrl: string }>(); + + + React.useEffect(() => { + const handleAuth = async () => { + const { error } = await supabase.auth.getSessionFromUrl(); + if (error) { + console.error('Error retrieving session:', error.message); + } else { + // Redirect the user to their dashboard or desired page after successful login + window.location.replace('/dashboard'); // Example redirection + } + }; + handleAuth(); + }, []); + + return
Logging you in...
; +} \ No newline at end of file diff --git a/app/routes/auth.google.callback.ts b/app/routes/auth.google.callback.ts new file mode 100644 index 0000000..a96b46e --- /dev/null +++ b/app/routes/auth.google.callback.ts @@ -0,0 +1,15 @@ +import { MetaFunction, type LoaderFunctionArgs } from "@remix-run/node"; +import { authenticator } from "~/services/auth.server"; +// import { SocialsProvider } from "remix-auth-socials"; + +export const meta: MetaFunction = () => { + return [{ title: "User Login" }]; +}; + +export const loader = ({ request }: LoaderFunctionArgs) => { + // return authenticator.authenticate(SocialsProvider.GOOGLE, request, { + return authenticator.authenticate('google', request, { + successRedirect: "/create", + failureRedirect: "/", + }); +}; diff --git a/app/routes/auth.google.ts b/app/routes/auth.google.ts new file mode 100644 index 0000000..ecf9e25 --- /dev/null +++ b/app/routes/auth.google.ts @@ -0,0 +1,15 @@ +import { authenticator } from "~/services/auth.server"; +// import { SocialsProvider } from "remix-auth-socials"; +import { type ActionFunctionArgs } from "@remix-run/node"; + +export const action = async ({ request }: ActionFunctionArgs) => { + // initiating authentication using Google Strategy + // on success --> redirect to dashboard + // on failure --> back to homepage/login + // return authenticator.authenticate(SocialsProvider.GOOGLE, request, { + + return authenticator.authenticate('google', request, { + successRedirect: "/create", + failureRedirect: "/", + }); +}; diff --git a/app/routes/login.tsx b/app/routes/login.tsx new file mode 100644 index 0000000..8188abd --- /dev/null +++ b/app/routes/login.tsx @@ -0,0 +1,106 @@ +import PageContainer from "components/PageContainer"; +import PixelStudioIcon from "components/PixelStudioIcon"; +import type { LoaderFunctionArgs } from "@remix-run/node"; +// import GoogleLoginButton from "../components/GoogleLoginButton"; +import { Card } from "@/components/ui/card"; +import { json, redirect, useOutletContext } from "@remix-run/react"; +import { getSupabaseWithSessionAndHeaders } from "~/services/supabase.server"; +import { Button } from "@/components/ui/button"; + +export async function loader({ request }: LoaderFunctionArgs) { + const { headers, serverSession } = await getSupabaseWithSessionAndHeaders({ + request, + }); + + if (serverSession) { + return redirect('/explore', { headers }); + } + + return json({ success: true }); + // return json({ success: true }, { headers }); + // If 'request' is not used, we can remove it from the destructuring + return {}; +} + +export default function Index() { + // const { supabase, domainUrl } = useOutletContext(); + const { supabase, domainUrl } = useOutletContext<{ supabase: Record; domainUrl: string }>(); + + + const handleSignIn = async () => { + console.log('clicked login......'); + try { + await supabase.auth.signInWithOAuth({ + provider: 'google', + options: { + redirectTo: `${domainUrl}/auth/callback`, + }, + }); + } catch (error) { + console.error('Error signing in with Google:', error); + } + }; + + return ( + +
+
+
+ +
+ {/*

+ Pixel Studio AI{" "} +

*/} +

Pixel Studio AI

+ +

Sign in to your account

+
+ + +
+ + + + +
+ + +

Sign in with

+
+ {/* */} + + +
+
+
+ +
+
+ + +
+ ); + } + \ No newline at end of file diff --git a/app/services/auth.server.ts b/app/services/auth.server.ts index fa32376..341f8a5 100644 --- a/app/services/auth.server.ts +++ b/app/services/auth.server.ts @@ -1,22 +1,45 @@ +import { Authenticator } from "remix-auth"; // import { GoogleStrategy, SocialsProvider } from "remix-auth-socials"; -// import { sessionStorage } from "~/services/session.server"; +import { GoogleStrategy } from "remix-auth-google"; +import { sessionStorage } from "~/services/session.server"; import { prisma } from "./prisma.server"; import { redirect } from "@remix-run/node"; -// import // getSessionUserId, -// // combineResponseInits, -// // getSessionExpirationDate, -// "utils"; -import { type Password, type User } from "@prisma/client"; import bcrypt from "bcryptjs"; -import { logout } from "~/server/logout"; -export const SESSION_KEY = "sessionId"; -// export const authenticator = new Authenticator( -// connectionSessionStorage -// ); +export const SESSION_KEY = "_session"; +export const USER_ID_KEY = "userId"; const SESSION_EXPIRATION_TIME = 1000 * 60 * 60 * 24 * 30; +// Create an instance of the authenticator +// It will take session storage as an input parameter and creates the user session on successful authentication +export const authenticator = new Authenticator(sessionStorage, { + sessionKey: SESSION_KEY, +}); +// You may specify a type which the strategies will return (this will be stored in the session) +// export let authenticator = new Authenticator(sessionStorage, { sessionKey: '_session' }); + +// const getCallback = (provider: SocialsProvider) => { + const getCallback = (provider: string) => { + return `${process.env.ORIGIN}/auth/${provider}/callback`; +}; + +authenticator.use( + new GoogleStrategy( + { + clientID: process.env.GOOGLE_CLIENT_ID!, + clientSecret: process.env.GOOGLE_CLIENT_SECRET!, + // callbackURL: getCallback(SocialsProvider.GOOGLE), + callbackURL: getCallback('google'), + }, + async ({ profile }) => { + // here you would find or create a user in your database + // profile object contains all the user data like image, displayName, id + return profile; + }, + ), +); + export const getSessionExpirationDate = () => new Date(Date.now() + SESSION_EXPIRATION_TIME); @@ -36,35 +59,12 @@ export async function getUserId(request: Request) { where: { id: sessionId, expirationDate: { gt: new Date() } }, }); if (!session?.user) { - throw await logout({ request }); + // throw await logout({ request }); + throw redirect("/login"); } return session.user.id; } -export async function verifyUserPassword( - where: Pick | Pick, - password: Password["hash"] -) { - const userWithPassword = await prisma.user.findUnique({ - where, - select: { id: true, password: { select: { hash: true } } }, - }); - - if (!userWithPassword || !userWithPassword.password) { - return null; - } - - const isValid = await bcrypt.compare( - password, - userWithPassword.password.hash - ); - - if (!isValid) { - return null; - } - - return { id: userWithPassword.id }; -} export async function requireAnonymous(request: Request) { const userId = await getUserId(request); diff --git a/app/services/index.ts b/app/services/index.ts new file mode 100644 index 0000000..9a21cc1 --- /dev/null +++ b/app/services/index.ts @@ -0,0 +1,3 @@ +export * from "./session.server"; +export * from "./auth.server"; +export * from "./prisma.server"; \ No newline at end of file diff --git a/app/services/session.server.ts b/app/services/session.server.ts new file mode 100644 index 0000000..16bb693 --- /dev/null +++ b/app/services/session.server.ts @@ -0,0 +1,22 @@ +import { createCookieSessionStorage } from "@remix-run/node"; + +export const sessionStorage = createCookieSessionStorage({ + cookie: { + name: "pixel_studio_session", + sameSite: "lax", + path: "/", + httpOnly: true, + secrets: process.env.SESSION_SECRET!.split(","), + secure: process.env.NODE_ENV === "production", + }, +}); + +// TODO: Need to look into this and confirm if session is being created as expected +export const getSessionUserId = async (request: Request) => { + const cookieSession = await sessionStorage.getSession( + request.headers.get("cookie"), + ); + return cookieSession.get("userId"); +}; + +export const { getSession, commitSession, destroySession } = sessionStorage; diff --git a/app/services/supabase.server.ts b/app/services/supabase.server.ts new file mode 100644 index 0000000..bb1f1f0 --- /dev/null +++ b/app/services/supabase.server.ts @@ -0,0 +1,67 @@ +import { createServerClient, parseCookieHeader, serializeCookieHeader } from "@supabase/ssr"; + +export const getSupabaseEnv = () => ({ + DATABASE_URL: process.env.DATABASE_URL!, + DATABASE_ANON_KEY: process.env.DATABASE_ANON_KEY!, +}); + +export function getSupabaseWithHeaders({ request }: { request: Request }) { +// const cookies = parseCookieHeader(request.headers.get("Cookie") ?? ""); + const headers = new Headers(); + + const { DATABASE_URL, DATABASE_ANON_KEY } = getSupabaseEnv(); + +// const supabase = createServerClient( +// DATABASE_URL, +// DATABASE_ANON_KEY, +// { +// cookies: { +// get(key) { +// return cookies[key]; +// }, +// set(key, value, options) { +// headers.append("Set-Cookie", serializeCookieHeader(key, value, options)); +// }, +// remove(key, options) { +// headers.append("Set-Cookie", serializeCookieHeader(key, "", options)); +// }, +// }, +// } +// ); + +const supabase = createServerClient(DATABASE_URL!, DATABASE_ANON_KEY!, { + cookies: { + getAll() { + return parseCookieHeader(request.headers.get('Cookie') ?? '') + }, + setAll(cookiesToSet) { + cookiesToSet.forEach(({ name, value, options }) => + headers.append('Set-Cookie', serializeCookieHeader(name, value, options)) + ) + }, + }, +}) + + return { supabase, headers }; +} + +// ! Current error getting +// login:1 Failed to launch +// 'postgres://postgres.uxqbliwogwtwdnuiodbt:lII2L2xdQrCP0aUn@aws-0-us-east-1.pooler.supabase.com:6543/postgres?pgbouncer=true&connection_limit=1/auth/v1/authorize?provider=google&redirect_to=http%3A%2F%2Flocalhost%3A5173%2Fauth%2Fcallback&code_challenge=v9WEmEh6ZBa132TGXH-_IfxZtXJCf9SYWsiruyaoniA&code_challenge_method=s256' +// because the scheme does not have a registered handler. + + +export async function getSupabaseWithSessionAndHeaders({ + request, +}: { + request: Request; +}) { + const { supabase, headers } = getSupabaseWithHeaders({ + request, + }); + const { + data: { session: serverSession }, + } = await supabase.auth.getSession(); + + return { serverSession, headers, supabase }; +} \ No newline at end of file diff --git a/app/utils/index.ts b/app/utils/index.ts index 1bc7ee4..1b889a3 100644 --- a/app/utils/index.ts +++ b/app/utils/index.ts @@ -7,7 +7,6 @@ export * from "./singleton"; export * from "./honeypot.server"; export * from "./isValidPassword.server"; -export * from "./session.server"; export * from "./toast.server"; export * from "./csrf.server"; export * from "./env.server"; diff --git a/package-lock.json b/package-lock.json index 8525d0e..048394a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,6 +18,8 @@ "@remix-run/react": "^2.11.2", "@sentry/remix": "^8.26.0", "@sentry/vite-plugin": "^2.22.2", + "@supabase/ssr": "^0.5.1", + "@supabase/supabase-js": "^2.45.4", "@vercel/remix": "^2.11.2", "bcryptjs": "^2.4.3", "class-variance-authority": "^0.7.0", @@ -30,7 +32,8 @@ "prisma": "^5.18.0", "react": "^18.3.1", "react-dom": "^18.3.1", - "remix-auth-socials": "^2.1.0", + "remix-auth": "^3.7.0", + "remix-auth-google": "^2.0.0", "remix-utils": "^7.6.0", "sonner": "^1.5.0", "tailwind-merge": "^2.5.2", @@ -1787,38 +1790,6 @@ "@opentelemetry/api": "^1.1.0" } }, - "node_modules/@oslojs/asn1": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@oslojs/asn1/-/asn1-0.2.3.tgz", - "integrity": "sha512-cZOLRzKfv3JTxIzNTAoiN0+utB0is9cQOOp5Xq++SE8MtIXXDLFblZGFrHCdTXLGXvUZmopeB/dFZrkh5MpKqQ==", - "dependencies": { - "@oslojs/binary": "0.4.0" - } - }, - "node_modules/@oslojs/binary": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@oslojs/binary/-/binary-0.4.0.tgz", - "integrity": "sha512-xi1MOhP7t9TFV1nOmzG0qIdCXdFXbeMYskhfI5eakBlU8re/xO+qjdyiBO+hl+nK4z4lZWCDjlgH8BXUWQDWFw==" - }, - "node_modules/@oslojs/crypto": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/@oslojs/crypto/-/crypto-0.6.2.tgz", - "integrity": "sha512-2y39MOLkkCvaBdi2TrBDtk7106fayDHRLz11K1/UvtW28VgL5/TbV/tPRTmSA5dhvQQqLYDJj3zsBkjeP+aD2w==", - "dependencies": { - "@oslojs/asn1": "0.2.3", - "@oslojs/binary": "0.4.0" - } - }, - "node_modules/@oslojs/encoding": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@oslojs/encoding/-/encoding-0.4.1.tgz", - "integrity": "sha512-hkjo6MuIK/kQR5CrGNdAPZhS01ZCXuWDRJ187zh6qqF2+yMHZpD9fAYpX8q2bOO6Ryhl3XpCT6kUX76N8hhm4Q==" - }, - "node_modules/@oslojs/oauth2": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/@oslojs/oauth2/-/oauth2-0.5.0.tgz", - "integrity": "sha512-t70+e4EgnzTbU4MrUWXzqWN2A6RJrlSSvwwuBv6E0Ap6/nsIXrjsdRWeTcSvvXTcC6fi0YdWaqEWLipcEm2Cgw==" - }, "node_modules/@paralleldrive/cuid2": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/@paralleldrive/cuid2/-/cuid2-2.2.2.tgz", @@ -3327,6 +3298,104 @@ "node": ">= 14" } }, + "node_modules/@supabase/auth-js": { + "version": "2.65.0", + "resolved": "https://registry.npmjs.org/@supabase/auth-js/-/auth-js-2.65.0.tgz", + "integrity": "sha512-+wboHfZufAE2Y612OsKeVP4rVOeGZzzMLD/Ac3HrTQkkY4qXNjI6Af9gtmxwccE5nFvTiF114FEbIQ1hRq5uUw==", + "dependencies": { + "@supabase/node-fetch": "^2.6.14" + } + }, + "node_modules/@supabase/functions-js": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@supabase/functions-js/-/functions-js-2.4.1.tgz", + "integrity": "sha512-8sZ2ibwHlf+WkHDUZJUXqqmPvWQ3UHN0W30behOJngVh/qHHekhJLCFbh0AjkE9/FqqXtf9eoVvmYgfCLk5tNA==", + "dependencies": { + "@supabase/node-fetch": "^2.6.14" + } + }, + "node_modules/@supabase/node-fetch": { + "version": "2.6.15", + "resolved": "https://registry.npmjs.org/@supabase/node-fetch/-/node-fetch-2.6.15.tgz", + "integrity": "sha512-1ibVeYUacxWYi9i0cf5efil6adJ9WRyZBLivgjs+AUpewx1F3xPi7gLgaASI2SmIQxPoCEjAsLAzKPgMJVgOUQ==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + } + }, + "node_modules/@supabase/postgrest-js": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/@supabase/postgrest-js/-/postgrest-js-1.16.1.tgz", + "integrity": "sha512-EOSEZFm5pPuCPGCmLF1VOCS78DfkSz600PBuvBND/IZmMciJ1pmsS3ss6TkB6UkuvTybYiBh7gKOYyxoEO3USA==", + "dependencies": { + "@supabase/node-fetch": "^2.6.14" + } + }, + "node_modules/@supabase/realtime-js": { + "version": "2.10.2", + "resolved": "https://registry.npmjs.org/@supabase/realtime-js/-/realtime-js-2.10.2.tgz", + "integrity": "sha512-qyCQaNg90HmJstsvr2aJNxK2zgoKh9ZZA8oqb7UT2LCh3mj9zpa3Iwu167AuyNxsxrUE8eEJ2yH6wLCij4EApA==", + "dependencies": { + "@supabase/node-fetch": "^2.6.14", + "@types/phoenix": "^1.5.4", + "@types/ws": "^8.5.10", + "ws": "^8.14.2" + } + }, + "node_modules/@supabase/realtime-js/node_modules/ws": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/@supabase/ssr": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@supabase/ssr/-/ssr-0.5.1.tgz", + "integrity": "sha512-+G94H/GZG0nErZ3FQV9yJmsC5Rj7dmcfCAwOt37hxeR1La+QTl8cE9whzYwPUrTJjMLGNXoO+1BMvVxwBAbz4g==", + "dependencies": { + "cookie": "^0.6.0" + }, + "peerDependencies": { + "@supabase/supabase-js": "^2.43.4" + } + }, + "node_modules/@supabase/storage-js": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@supabase/storage-js/-/storage-js-2.7.0.tgz", + "integrity": "sha512-iZenEdO6Mx9iTR6T7wC7sk6KKsoDPLq8rdu5VRy7+JiT1i8fnqfcOr6mfF2Eaqky9VQzhP8zZKQYjzozB65Rig==", + "dependencies": { + "@supabase/node-fetch": "^2.6.14" + } + }, + "node_modules/@supabase/supabase-js": { + "version": "2.45.4", + "resolved": "https://registry.npmjs.org/@supabase/supabase-js/-/supabase-js-2.45.4.tgz", + "integrity": "sha512-E5p8/zOLaQ3a462MZnmnz03CrduA5ySH9hZyL03Y+QZLIOO4/Gs8Rdy4ZCKDHsN7x0xdanVEWWFN3pJFQr9/hg==", + "dependencies": { + "@supabase/auth-js": "2.65.0", + "@supabase/functions-js": "2.4.1", + "@supabase/node-fetch": "2.6.15", + "@supabase/postgrest-js": "1.16.1", + "@supabase/realtime-js": "2.10.2", + "@supabase/storage-js": "2.7.0" + } + }, "node_modules/@ts-morph/common": { "version": "0.11.1", "resolved": "https://registry.npmjs.org/@ts-morph/common/-/common-0.11.1.tgz", @@ -3471,6 +3540,11 @@ "@types/pg": "*" } }, + "node_modules/@types/phoenix": { + "version": "1.6.5", + "resolved": "https://registry.npmjs.org/@types/phoenix/-/phoenix-1.6.5.tgz", + "integrity": "sha512-xegpDuR+z0UqG9fwHqNoy3rI7JDlvaPh2TY47Fl80oq6g+hXT+c/LEuE43X48clZ6lOfANl5WrPur9fYO1RJ/w==" + }, "node_modules/@types/prop-types": { "version": "15.7.12", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz", @@ -3512,6 +3586,14 @@ "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==" }, + "node_modules/@types/ws": { + "version": "8.5.12", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.12.tgz", + "integrity": "sha512-3tPRkv1EtkDpzlgyKyI8pGsGZAGPEaXeu0DOj5DI25Ja91bdAYddYHbADRYVrZMRbfW+1l5YwXVDKohDJNQxkQ==", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "6.21.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz", @@ -4335,9 +4417,9 @@ } }, "node_modules/body-parser": { - "version": "1.20.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", - "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", + "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", "dependencies": { "bytes": "3.1.2", "content-type": "~1.0.5", @@ -4347,7 +4429,7 @@ "http-errors": "2.0.0", "iconv-lite": "0.4.24", "on-finished": "2.4.1", - "qs": "6.11.0", + "qs": "6.13.0", "raw-body": "2.5.2", "type-is": "~1.6.18", "unpipe": "1.0.0" @@ -4982,7 +5064,9 @@ "node_modules/crypto-js": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz", - "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==" + "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==", + "optional": true, + "peer": true }, "node_modules/css-what": { "version": "6.1.0", @@ -5363,9 +5447,9 @@ "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" }, "node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", "engines": { "node": ">= 0.8" } @@ -6322,36 +6406,36 @@ } }, "node_modules/express": { - "version": "4.19.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz", - "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==", + "version": "4.21.0", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.0.tgz", + "integrity": "sha512-VqcNGcj/Id5ZT1LZ/cfihi3ttTn+NJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng==", "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.20.2", + "body-parser": "1.20.3", "content-disposition": "0.5.4", "content-type": "~1.0.4", "cookie": "0.6.0", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", - "finalhandler": "1.2.0", + "finalhandler": "1.3.1", "fresh": "0.5.2", "http-errors": "2.0.0", - "merge-descriptors": "1.0.1", + "merge-descriptors": "1.0.3", "methods": "~1.1.2", "on-finished": "2.4.1", "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", + "path-to-regexp": "0.1.10", "proxy-addr": "~2.0.7", - "qs": "6.11.0", + "qs": "6.13.0", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", - "send": "0.18.0", - "serve-static": "1.15.0", + "send": "0.19.0", + "serve-static": "1.16.2", "setprototypeof": "1.2.0", "statuses": "2.0.1", "type-is": "~1.6.18", @@ -6480,12 +6564,12 @@ } }, "node_modules/finalhandler": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", - "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", + "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", "dependencies": { "debug": "2.6.9", - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "on-finished": "2.4.1", "parseurl": "~1.3.3", @@ -8283,9 +8367,12 @@ } }, "node_modules/merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, "node_modules/merge-stream": { "version": "2.0.0", @@ -8892,9 +8979,9 @@ ] }, "node_modules/micromatch": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz", - "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" @@ -9812,9 +9899,9 @@ "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==" }, "node_modules/path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" + "version": "0.1.10", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz", + "integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==" }, "node_modules/path-type": { "version": "4.0.0", @@ -10393,11 +10480,11 @@ } }, "node_modules/qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", "dependencies": { - "side-channel": "^1.0.4" + "side-channel": "^1.0.6" }, "engines": { "node": ">=0.6" @@ -10756,105 +10843,16 @@ "@remix-run/server-runtime": "^1.0.0 || ^2.0.0" } }, - "node_modules/remix-auth-discord": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/remix-auth-discord/-/remix-auth-discord-1.4.0.tgz", - "integrity": "sha512-hrIOblSFstbwhXc0KJZZf0TfhGRehpTJ9f5uRAKsxS53M9HvNkKCs1aIgKEdkODbN77+/XCd3kudQ+J8fU6U6w==", - "dependencies": { - "remix-auth": "^3.6.0", - "remix-auth-oauth2": "^1.11.2" - }, - "peerDependencies": { - "@remix-run/server-runtime": "^2.1.0" - } - }, - "node_modules/remix-auth-github": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/remix-auth-github/-/remix-auth-github-1.8.0.tgz", - "integrity": "sha512-gC2yrGtac04373/Ib59EN32NMSINfqvkXUPlwaHsYKT3AYAxvZV2H2Y3DDRvNB0fRVrjZHgHVCXCPiqtVr8Y6g==", - "funding": [ - "https://github.com/sponsors/sergiodxa" - ], - "dependencies": { - "remix-auth-oauth2": "^2.2.0" - }, - "engines": { - "node": "^18.0.0 || ^20.0.0 || >=20.0.0" - }, - "peerDependencies": { - "@remix-run/cloudflare": "^1.0.0 || ^2.0.0", - "@remix-run/deno": "^1.0.0 || ^2.0.0", - "@remix-run/node": "^1.0.0 || ^2.0.0", - "remix-auth": "^3.6.0" - }, - "peerDependenciesMeta": { - "@remix-run/cloudflare": { - "optional": true - }, - "@remix-run/deno": { - "optional": true - }, - "@remix-run/node": { - "optional": true - } - } - }, - "node_modules/remix-auth-github/node_modules/remix-auth-oauth2": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/remix-auth-oauth2/-/remix-auth-oauth2-2.3.0.tgz", - "integrity": "sha512-94PztjpER9hethzZOpF1lqwlmLccGY7RUKp9ua3Ii3DBPqVIO/hFedeqpcvZyysbPaW3sK3ztqfe8b8nnF4F/Q==", - "funding": [ - "https://github.com/sponsors/sergiodxa" - ], - "dependencies": { - "@oslojs/crypto": "^0.6.2", - "@oslojs/encoding": "^0.4.1", - "@oslojs/oauth2": "^0.5.0", - "debug": "^4.3.4" - }, - "engines": { - "node": "^18.0.0 || ^20.0.0 || >=20.0.0" - }, - "peerDependencies": { - "@remix-run/cloudflare": "^1.0.0 || ^2.0.0", - "@remix-run/deno": "^1.0.0 || ^2.0.0", - "@remix-run/node": "^1.0.0 || ^2.0.0", - "remix-auth": "^3.6.0" - }, - "peerDependenciesMeta": { - "@remix-run/cloudflare": { - "optional": true - }, - "@remix-run/deno": { - "optional": true - }, - "@remix-run/node": { - "optional": true - } - } - }, - "node_modules/remix-auth-linkedin": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/remix-auth-linkedin/-/remix-auth-linkedin-2.0.1.tgz", - "integrity": "sha512-EBbyhBZEDP2MaRluLiFOqk8NX8YYu7aXYXC9Aky0PTAU6H1zYw0WI85/j66xiL94aL/i6TgGiT+c//SWpW/pHA==", - "dependencies": { - "remix-auth-oauth2": "^1.11.2" - }, - "peerDependencies": { - "@remix-run/server-runtime": "^1.0.0 || ^2.0.0", - "remix-auth": "^3.6.0" - } - }, - "node_modules/remix-auth-microsoft": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/remix-auth-microsoft/-/remix-auth-microsoft-2.0.1.tgz", - "integrity": "sha512-7utTr9/5ax9f8VKrl4x2khguD0xRtBIaJSRaHJ2qOkan0WlcwHpcC18i3bktr6UfcXqjsgZVzBDnh0eOkEbruA==", + "node_modules/remix-auth-google": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/remix-auth-google/-/remix-auth-google-2.0.0.tgz", + "integrity": "sha512-qP38N1ZJADz+HlH2lrEn/rSL6m5Dwcnz8xUFDhbHecDMl9FU/UpkUx3w8jj5XhNSnkSue/GWT+p7OEkatgbXNA==", "dependencies": { - "remix-auth": "^3.6.0", "remix-auth-oauth2": "^1.11.0" }, "peerDependencies": { - "@remix-run/server-runtime": "^1.0.0 || ^2.0.0" + "@remix-run/server-runtime": "^2.0.1", + "remix-auth": "^3.2.1" } }, "node_modules/remix-auth-oauth2": { @@ -10882,100 +10880,6 @@ "uuid": "dist/bin/uuid" } }, - "node_modules/remix-auth-socials": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/remix-auth-socials/-/remix-auth-socials-2.1.0.tgz", - "integrity": "sha512-a6XeFGixHXL7o5CMRL8A7RkFlSdwqAweNHM1zDVqL2nGCNBpUKbEa8LzmGcVG36FfaRbhhZhp1OfCahKJmeThQ==", - "dependencies": { - "remix-auth": "^3.7.0", - "remix-auth-discord": "^1.3.3", - "remix-auth-facebook": "^1.0.1", - "remix-auth-github": "^1.7.0", - "remix-auth-google": "^2.0.0", - "remix-auth-linkedin": "^2.0.1", - "remix-auth-microsoft": "^2.0.1", - "remix-auth-twitter": "^2.1.0" - } - }, - "node_modules/remix-auth-socials/node_modules/@remix-run/router": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.7.2.tgz", - "integrity": "sha512-7Lcn7IqGMV+vizMPoEl5F0XDshcdDYtMI6uJLQdQz5CfZAwy3vvGKYSUk789qndt5dEC4HfSjviSYlSoHGL2+A==", - "peer": true, - "engines": { - "node": ">=14" - } - }, - "node_modules/remix-auth-socials/node_modules/@remix-run/server-runtime": { - "version": "1.19.3", - "resolved": "https://registry.npmjs.org/@remix-run/server-runtime/-/server-runtime-1.19.3.tgz", - "integrity": "sha512-KzQ+htUsKqpBgKE2tWo7kIIGy3MyHP58Io/itUPvV+weDjApwr9tQr9PZDPA3yAY6rAzLax7BU0NMSYCXWFY5A==", - "peer": true, - "dependencies": { - "@remix-run/router": "1.7.2", - "@types/cookie": "^0.4.1", - "@web3-storage/multipart-parser": "^1.0.0", - "cookie": "^0.4.1", - "set-cookie-parser": "^2.4.8", - "source-map": "^0.7.3" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/remix-auth-socials/node_modules/@types/cookie": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", - "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==", - "peer": true - }, - "node_modules/remix-auth-socials/node_modules/cookie": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", - "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", - "peer": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/remix-auth-socials/node_modules/remix-auth-facebook": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/remix-auth-facebook/-/remix-auth-facebook-1.0.1.tgz", - "integrity": "sha512-i8uTyNGV97fRKzZ1+HTaEDjvH6rg3oyVPRbvHVU2FxWzjUtTCbpqudsQZbCSL40oO5ct+V7NIt+lT+DRCl3dlA==", - "dependencies": { - "remix-auth-oauth2": "^1.6.0" - }, - "peerDependencies": { - "@remix-run/server-runtime": "^1.5.1", - "remix-auth": "^3.2.2" - } - }, - "node_modules/remix-auth-socials/node_modules/remix-auth-google": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/remix-auth-google/-/remix-auth-google-2.0.0.tgz", - "integrity": "sha512-qP38N1ZJADz+HlH2lrEn/rSL6m5Dwcnz8xUFDhbHecDMl9FU/UpkUx3w8jj5XhNSnkSue/GWT+p7OEkatgbXNA==", - "dependencies": { - "remix-auth-oauth2": "^1.11.0" - }, - "peerDependencies": { - "@remix-run/server-runtime": "^2.0.1", - "remix-auth": "^3.2.1" - } - }, - "node_modules/remix-auth-socials/node_modules/remix-auth-twitter": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/remix-auth-twitter/-/remix-auth-twitter-2.1.0.tgz", - "integrity": "sha512-uoayITEtVJRYjBy9N9c14ahZrBnCr+LYzGTOqtD/yqM0qKRvylruqfUFA00DMJNSg3Zsy0qu41ko/DPzw64/hA==", - "dependencies": { - "crypto-js": "^4.1.1", - "debug": "^4.3.3", - "remix-auth": "^3.2.2", - "uuid": "^8.3.2" - }, - "peerDependencies": { - "@remix-run/server-runtime": "^1.19" - } - }, "node_modules/remix-utils": { "version": "7.6.0", "resolved": "https://registry.npmjs.org/remix-utils/-/remix-utils-7.6.0.tgz", @@ -11341,9 +11245,9 @@ } }, "node_modules/send": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", - "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", + "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", "dependencies": { "debug": "2.6.9", "depd": "2.0.0", @@ -11376,20 +11280,28 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, + "node_modules/send/node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/send/node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, "node_modules/serve-static": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", - "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", + "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", "dependencies": { - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "parseurl": "~1.3.3", - "send": "0.18.0" + "send": "0.19.0" }, "engines": { "node": ">= 0.8.0" diff --git a/package.json b/package.json index 9279eed..55cc6ed 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,8 @@ "@remix-run/react": "^2.11.2", "@sentry/remix": "^8.26.0", "@sentry/vite-plugin": "^2.22.2", + "@supabase/ssr": "^0.5.1", + "@supabase/supabase-js": "^2.45.4", "@vercel/remix": "^2.11.2", "bcryptjs": "^2.4.3", "class-variance-authority": "^0.7.0", @@ -35,7 +37,8 @@ "prisma": "^5.18.0", "react": "^18.3.1", "react-dom": "^18.3.1", - "remix-auth-socials": "^2.1.0", + "remix-auth": "^3.7.0", + "remix-auth-google": "^2.0.0", "remix-utils": "^7.6.0", "sonner": "^1.5.0", "tailwind-merge": "^2.5.2",