Merge pull request #18 from Sosokker/main

Get middleware and auth logic from backend
This commit is contained in:
Sirin Puenggun 2024-09-10 01:42:09 +07:00 committed by GitHub
commit a9aedcf3c2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 345 additions and 109 deletions

View File

@ -1,2 +1,4 @@
NEXT_PUBLIC_SUPABASE_URL=your-project-url NEXT_PUBLIC_SUPABASE_URL=your-project-url
NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key
NEXT_PUBLIC_AUTH_GOOGLE_ID=your-google-id
NEXT_PUBLIC_AUTH_GOOGLE_SECRET=your-google-secret

114
package-lock.json generated
View File

@ -10,15 +10,19 @@
"dependencies": { "dependencies": {
"@radix-ui/react-dropdown-menu": "^2.1.1", "@radix-ui/react-dropdown-menu": "^2.1.1",
"@radix-ui/react-navigation-menu": "^1.2.0", "@radix-ui/react-navigation-menu": "^1.2.0",
"@radix-ui/react-progress": "^1.1.0",
"@radix-ui/react-separator": "^1.1.0",
"@radix-ui/react-slot": "^1.1.0", "@radix-ui/react-slot": "^1.1.0",
"@supabase/ssr": "^0.4.0", "@supabase/ssr": "^0.4.1",
"@supabase/supabase-js": "^2.45.1", "@supabase/supabase-js": "^2.45.2",
"class-variance-authority": "^0.7.0", "class-variance-authority": "^0.7.0",
"clsx": "^2.1.1", "clsx": "^2.1.1",
"embla-carousel-react": "^8.2.0",
"lucide-react": "^0.428.0", "lucide-react": "^0.428.0",
"next": "14.2.5", "next": "14.2.5",
"next-themes": "^0.3.0", "next-themes": "^0.3.0",
"react": "^18", "react": "^18",
"react-countup": "^6.5.3",
"react-dom": "^18", "react-dom": "^18",
"tailwind-merge": "^2.5.2", "tailwind-merge": "^2.5.2",
"tailwindcss-animate": "^1.0.7" "tailwindcss-animate": "^1.0.7"
@ -820,6 +824,29 @@
} }
} }
}, },
"node_modules/@radix-ui/react-progress": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@radix-ui/react-progress/-/react-progress-1.1.0.tgz",
"integrity": "sha512-aSzvnYpP725CROcxAOEBVZZSIQVQdHgBr2QQFKySsaD14u8dNT0batuXI+AAGDdAHfXH8rbnHmjYFqVJ21KkRg==",
"dependencies": {
"@radix-ui/react-context": "1.1.0",
"@radix-ui/react-primitive": "2.0.0"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-roving-focus": { "node_modules/@radix-ui/react-roving-focus": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.0.tgz", "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.0.tgz",
@ -850,6 +877,28 @@
} }
} }
}, },
"node_modules/@radix-ui/react-separator": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@radix-ui/react-separator/-/react-separator-1.1.0.tgz",
"integrity": "sha512-3uBAs+egzvJBDZAzvb/n4NxxOYpnspmWxO2u5NbZ8Y6FM/NdrGSF9bop3Cf6F6C71z1rTSn8KV0Fo2ZVd79lGA==",
"dependencies": {
"@radix-ui/react-primitive": "2.0.0"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-slot": { "node_modules/@radix-ui/react-slot": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.0.tgz", "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.0.tgz",
@ -1069,9 +1118,9 @@
} }
}, },
"node_modules/@supabase/ssr": { "node_modules/@supabase/ssr": {
"version": "0.4.0", "version": "0.4.1",
"resolved": "https://registry.npmjs.org/@supabase/ssr/-/ssr-0.4.0.tgz", "resolved": "https://registry.npmjs.org/@supabase/ssr/-/ssr-0.4.1.tgz",
"integrity": "sha512-6WS3NUvHDhCPAFN2kJ79AQDO8+M9fJ7y2fYpxgZqIuJEpnnGsHDNnB5Xnv8CiaJIuRU+0pKboy62RVZBMfZ0Lg==", "integrity": "sha512-000i7y4ITXjXU0T1JytZYU33VbUNklX9YN47hCweaLKsTBAEigJJJCeq3G+/IiwEggBt58Vu0KQ3UGXON7OmDQ==",
"dependencies": { "dependencies": {
"cookie": "^0.6.0" "cookie": "^0.6.0"
}, },
@ -1083,24 +1132,24 @@
} }
}, },
"node_modules/@supabase/storage-js": { "node_modules/@supabase/storage-js": {
"version": "2.6.0", "version": "2.7.0",
"resolved": "https://registry.npmjs.org/@supabase/storage-js/-/storage-js-2.6.0.tgz", "resolved": "https://registry.npmjs.org/@supabase/storage-js/-/storage-js-2.7.0.tgz",
"integrity": "sha512-REAxr7myf+3utMkI2oOmZ6sdplMZZ71/2NEIEMBZHL9Fkmm3/JnaOZVSRqvG4LStYj2v5WhCruCzuMn6oD/Drw==", "integrity": "sha512-iZenEdO6Mx9iTR6T7wC7sk6KKsoDPLq8rdu5VRy7+JiT1i8fnqfcOr6mfF2Eaqky9VQzhP8zZKQYjzozB65Rig==",
"dependencies": { "dependencies": {
"@supabase/node-fetch": "^2.6.14" "@supabase/node-fetch": "^2.6.14"
} }
}, },
"node_modules/@supabase/supabase-js": { "node_modules/@supabase/supabase-js": {
"version": "2.45.1", "version": "2.45.2",
"resolved": "https://registry.npmjs.org/@supabase/supabase-js/-/supabase-js-2.45.1.tgz", "resolved": "https://registry.npmjs.org/@supabase/supabase-js/-/supabase-js-2.45.2.tgz",
"integrity": "sha512-/PVe3lXmalazD8BGMIoI7+ttvT1mLXy13lNcoAPtjP1TDDY83g8csZbVR6l+0/RZtvJxl3LGXfTJT4bjWgC5Nw==", "integrity": "sha512-kJKY3ISFusVKQWCP8Kqo20Ebxy2WLp6Ry/Suco0aQsPXH7bvn7clswsdhcfcH/5Tr0MYz/jcCjF0n/27SetiCw==",
"dependencies": { "dependencies": {
"@supabase/auth-js": "2.64.4", "@supabase/auth-js": "2.64.4",
"@supabase/functions-js": "2.4.1", "@supabase/functions-js": "2.4.1",
"@supabase/node-fetch": "2.6.15", "@supabase/node-fetch": "2.6.15",
"@supabase/postgrest-js": "1.15.8", "@supabase/postgrest-js": "1.15.8",
"@supabase/realtime-js": "2.10.2", "@supabase/realtime-js": "2.10.2",
"@supabase/storage-js": "2.6.0" "@supabase/storage-js": "2.7.0"
} }
}, },
"node_modules/@swc/counter": { "node_modules/@swc/counter": {
@ -1830,6 +1879,11 @@
"node": ">= 0.6" "node": ">= 0.6"
} }
}, },
"node_modules/countup.js": {
"version": "2.8.0",
"resolved": "https://registry.npmjs.org/countup.js/-/countup.js-2.8.0.tgz",
"integrity": "sha512-f7xEhX0awl4NOElHulrl4XRfKoNH3rB+qfNSZZyjSZhaAoUk6elvhH+MNxMmlmuUJ2/QNTWPSA7U4mNtIAKljQ=="
},
"node_modules/cross-spawn": { "node_modules/cross-spawn": {
"version": "7.0.3", "version": "7.0.3",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
@ -2050,6 +2104,31 @@
"resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
"integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="
}, },
"node_modules/embla-carousel": {
"version": "8.2.0",
"resolved": "https://registry.npmjs.org/embla-carousel/-/embla-carousel-8.2.0.tgz",
"integrity": "sha512-rf2GIX8rab9E6ZZN0Uhz05746qu2KrDje9IfFyHzjwxLwhvGjUt6y9+uaY1Sf+B0OPSa3sgas7BE2hWZCtopTA=="
},
"node_modules/embla-carousel-react": {
"version": "8.2.0",
"resolved": "https://registry.npmjs.org/embla-carousel-react/-/embla-carousel-react-8.2.0.tgz",
"integrity": "sha512-dWqbmaEBQjeAcy/EKrcAX37beVr0ubXuHPuLZkx27z58V1FIvRbbMb4/c3cLZx0PAv/ofngX2QFrwUB+62SPnw==",
"dependencies": {
"embla-carousel": "8.2.0",
"embla-carousel-reactive-utils": "8.2.0"
},
"peerDependencies": {
"react": "^16.8.0 || ^17.0.1 || ^18.0.0"
}
},
"node_modules/embla-carousel-reactive-utils": {
"version": "8.2.0",
"resolved": "https://registry.npmjs.org/embla-carousel-reactive-utils/-/embla-carousel-reactive-utils-8.2.0.tgz",
"integrity": "sha512-ZdaPNgMydkPBiDRUv+wRIz3hpZJ3LKrTyz+XWi286qlwPyZFJDjbzPBiXnC3czF9N/nsabSc7LTRvGauUzwKEg==",
"peerDependencies": {
"embla-carousel": "8.2.0"
}
},
"node_modules/emoji-regex": { "node_modules/emoji-regex": {
"version": "9.2.2", "version": "9.2.2",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
@ -4470,6 +4549,17 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/react-countup": {
"version": "6.5.3",
"resolved": "https://registry.npmjs.org/react-countup/-/react-countup-6.5.3.tgz",
"integrity": "sha512-udnqVQitxC7QWADSPDOxVWULkLvKUWrDapn5i53HE4DPRVgs+Y5rr4bo25qEl8jSh+0l2cToJgGMx+clxPM3+w==",
"dependencies": {
"countup.js": "^2.8.0"
},
"peerDependencies": {
"react": ">= 16.3.0"
}
},
"node_modules/react-dom": { "node_modules/react-dom": {
"version": "18.3.1", "version": "18.3.1",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",

View File

@ -15,8 +15,8 @@
"@radix-ui/react-progress": "^1.1.0", "@radix-ui/react-progress": "^1.1.0",
"@radix-ui/react-separator": "^1.1.0", "@radix-ui/react-separator": "^1.1.0",
"@radix-ui/react-slot": "^1.1.0", "@radix-ui/react-slot": "^1.1.0",
"@supabase/ssr": "^0.4.0", "@supabase/ssr": "^0.4.1",
"@supabase/supabase-js": "^2.45.1", "@supabase/supabase-js": "^2.45.2",
"class-variance-authority": "^0.7.0", "class-variance-authority": "^0.7.0",
"clsx": "^2.1.1", "clsx": "^2.1.1",
"embla-carousel-react": "^8.2.0", "embla-carousel-react": "^8.2.0",

View File

@ -0,0 +1,24 @@
import { createSupabaseClient } from "@/lib/supabase/serverComponentClient";
import { NextResponse } from "next/server";
export async function GET(request: Request) {
const { searchParams, origin } = new URL(request.url);
const code = searchParams.get("code");
// if "next" is in param, use it in the redirect URL
const next = searchParams.get("next") ?? "/";
if (code) {
const supabase = createSupabaseClient();
const { error } = await supabase.auth.exchangeCodeForSession(code);
if (!error) {
return NextResponse.redirect(`${origin}${next}`);
}
}
// return the user to an error page with instructions
return NextResponse.redirect(`${origin}/auth/error`);
}

View File

@ -0,0 +1,3 @@
export default function AuthError() {
return <div>Authentication Error</div>;
}

View File

@ -2,6 +2,9 @@ import Image from "next/image";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Card, CardContent, CardFooter, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import { Card, CardContent, CardFooter, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
import { LoginButton } from "@/components/auth/loginButton";
import { LogoutButton } from "@/components/auth/logoutButton";
export default function Login() { export default function Login() {
return ( return (
<div <div
@ -16,14 +19,8 @@ export default function Login() {
</CardHeader> </CardHeader>
<CardContent className="flex flex-col gap-y-2 mx-28"> <CardContent className="flex flex-col gap-y-2 mx-28">
<p className="self-center font-semibold text-slate-800">Continue With</p> <p className="self-center font-semibold text-slate-800">Continue With</p>
<Button className="bg-foreground gap-2 rounded-xl"> <LoginButton />
<Image src={"/logo/google.svg"} width={30} height={30} alt={"Google"} /> <LogoutButton />
Continue with Google
</Button>
<Button className="gap-2 rounded-xl">
<Image src={"/logo/facebook.svg"} width={30} height={30} alt={"Google"} />
Continue with Facebook
</Button>
</CardContent> </CardContent>
<CardFooter className="text-xs justify-center"> <CardFooter className="text-xs justify-center">
By signing up, you agree to the Terms of Service and acknowledge youve read our Privacy Policy. By signing up, you agree to the Terms of Service and acknowledge youve read our Privacy Policy.

View File

@ -19,7 +19,7 @@ export default function Home() {
<p>Together, we turn ideas into impact.</p> <p>Together, we turn ideas into impact.</p>
</span> </span>
<Button className="font-bold mt-4"> <Button className="font-bold mt-4">
<Link href="/login">Start Investing</Link> <Link href="/">Start Investing</Link>
</Button> </Button>
</span> </span>
</div> </div>
@ -79,10 +79,40 @@ export default function Home() {
<p className="text-lg">The deals attracting the most interest right now</p> <p className="text-lg">The deals attracting the most interest right now</p>
</span> </span>
<div className="grid grid-cols-4 gap-4"> <div className="grid grid-cols-4 gap-4">
<BusinessCard name={"NVDA"} description={"Founded in 1993, NVIDIA is a key innovator of computer graphics and AI technology"} joinDate={"December 2021"} location={"Bangkok, Thailand"} tags={null} /> <BusinessCard
<BusinessCard name={"Apple Inc."} description={"Founded in 1976, Apple Inc. is a leading innovator in consumer electronics, software, and online services, known for products like the iPhone, MacBook, and the App Store."} joinDate={"February 2020"} location={"Cupertino, California, USA"} tags={null} /> name={"NVDA"}
<BusinessCard name={"Google LLC"} description={"Founded in 1998, Google LLC specializes in internet-related services and products, including search engines, online advertising, cloud computing, and the Android operating system."} joinDate={"April 2019"} location={"Mountain View, California, USA"} tags={null} /> description={"Founded in 1993, NVIDIA is a key innovator of computer graphics and AI technology"}
<BusinessCard name={"Microsoft Corporation"} description={"Founded in 1975, Microsoft Corporation is a multinational technology company that develops, manufactures, and licenses software, hardware, and services, including Windows, Office, and Azure."} joinDate={"January 2018"} location={""} tags={null} /> joinDate={"December 2021"}
location={"Bangkok, Thailand"}
tags={null}
/>
<BusinessCard
name={"Apple Inc."}
description={
"Founded in 1976, Apple Inc. is a leading innovator in consumer electronics, software, and online services, known for products like the iPhone, MacBook, and the App Store."
}
joinDate={"February 2020"}
location={"Cupertino, California, USA"}
tags={null}
/>
<BusinessCard
name={"Google LLC"}
description={
"Founded in 1998, Google LLC specializes in internet-related services and products, including search engines, online advertising, cloud computing, and the Android operating system."
}
joinDate={"April 2019"}
location={"Mountain View, California, USA"}
tags={null}
/>
<BusinessCard
name={"Microsoft Corporation"}
description={
"Founded in 1975, Microsoft Corporation is a multinational technology company that develops, manufactures, and licenses software, hardware, and services, including Windows, Office, and Azure."
}
joinDate={"January 2018"}
location={""}
tags={null}
/>
</div> </div>
<div className="self-center py-5"> <div className="self-center py-5">
<Button> <Button>

View File

@ -0,0 +1,25 @@
"use client";
import Image from "next/image";
import { createSupabaseClient } from "@/lib/supabase/clientComponentClient";
import { Button } from "@/components/ui/button";
export function LoginButton(props: { nextUrl?: string }) {
const supabase = createSupabaseClient();
const handleLogin = async () => {
await supabase.auth.signInWithOAuth({
provider: "google",
options: {
redirectTo: `${location.origin}/auth/callback?next=${props.nextUrl || ""}`,
},
});
};
return (
<Button className="bg-foreground gap-2 rounded-xl" onClick={handleLogin}>
<Image src={"/logo/google.svg"} width={30} height={30} alt={"Google"} />
Continue with Google
</Button>
);
}

View File

@ -0,0 +1,16 @@
"use client";
import { createSupabaseClient } from "@/lib/supabase/clientComponentClient";
import { useRouter } from "next/navigation";
export function LogoutButton() {
const supabase = createSupabaseClient();
const router = useRouter();
const handleLogout = async () => {
await supabase.auth.signOut();
router.push("/");
};
return <button onClick={handleLogout}>Logout</button>;
}

View File

@ -31,31 +31,27 @@ const landings = [
route: "/crm-landing", route: "/crm-landing",
}, },
]; ];
const ListItem = React.forwardRef< const ListItem = React.forwardRef<React.ElementRef<"a">, React.ComponentPropsWithoutRef<"a">>(
React.ElementRef<"a">, ({ className, title, children, ...props }, ref) => {
React.ComponentPropsWithoutRef<"a"> return (
>(({ className, title, children, ...props }, ref) => { <li>
return ( <NavigationMenuLink asChild>
<li> <a
<NavigationMenuLink asChild> ref={ref}
<a className={cn(
ref={ref} "block select-none space-y-1 rounded-md p-3 leading-none no-underline outline-none transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground",
className={cn( className
"block select-none space-y-1 rounded-md p-3 leading-none no-underline outline-none transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground", )}
className {...props}>
)} <div className="text-sm font-medium leading-none">{title}</div>
{...props} <hr />
> <p className="line-clamp-2 text-sm leading-snug text-muted-foreground">{children}</p>
<div className="text-sm font-medium leading-none">{title}</div> </a>
<hr /> </NavigationMenuLink>
<p className="line-clamp-2 text-sm leading-snug text-muted-foreground"> </li>
{children} );
</p> }
</a> );
</NavigationMenuLink>
</li>
);
});
ListItem.displayName = "ListItem"; ListItem.displayName = "ListItem";
export function UnsignedNav() { export function UnsignedNav() {
@ -90,8 +86,7 @@ export function UnsignedNav() {
<Link <Link
className="flex-none text-xl font-semibold dark:text-white focus:outline-none focus:opacity-80" className="flex-none text-xl font-semibold dark:text-white focus:outline-none focus:opacity-80"
href="/" href="/"
aria-label="Brand" aria-label="Brand">
>
<span className="inline-flex items-center gap-x-2 text-xl font-semibold dark:text-white"> <span className="inline-flex items-center gap-x-2 text-xl font-semibold dark:text-white">
<Image src="./logo.svg" alt="logo" width={50} height={50} /> <Image src="./logo.svg" alt="logo" width={50} height={50} />
B2DVentures B2DVentures
@ -103,17 +98,11 @@ export function UnsignedNav() {
<NavigationMenu> <NavigationMenu>
<NavigationMenuList> <NavigationMenuList>
<NavigationMenuItem> <NavigationMenuItem>
<NavigationMenuTrigger className="text-base"> <NavigationMenuTrigger className="text-base">Businesses</NavigationMenuTrigger>
Businesses
</NavigationMenuTrigger>
<NavigationMenuContent> <NavigationMenuContent>
<ul className="grid w-[400px] "> <ul className="grid w-[400px] ">
{businessComponents.map((component) => ( {businessComponents.map((component) => (
<ListItem <ListItem key={component.title} title={component.title} href={component.href}>
key={component.title}
title={component.title}
href={component.href}
>
{component.description} {component.description}
</ListItem> </ListItem>
))} ))}
@ -122,17 +111,11 @@ export function UnsignedNav() {
</NavigationMenuItem> </NavigationMenuItem>
<NavigationMenuItem> <NavigationMenuItem>
<NavigationMenuTrigger className="text-base font-medium "> <NavigationMenuTrigger className="text-base font-medium ">Projects</NavigationMenuTrigger>
Projects
</NavigationMenuTrigger>
<NavigationMenuContent> <NavigationMenuContent>
<ul className="grid w-[400px] "> <ul className="grid w-[400px] ">
{projectComponents.map((component) => ( {projectComponents.map((component) => (
<ListItem <ListItem key={component.title} title={component.title} href={component.href}>
key={component.title}
title={component.title}
href={component.href}
>
{component.description} {component.description}
</ListItem> </ListItem>
))} ))}
@ -141,17 +124,11 @@ export function UnsignedNav() {
</NavigationMenuItem> </NavigationMenuItem>
<NavigationMenuItem> <NavigationMenuItem>
<NavigationMenuTrigger className="text-base"> <NavigationMenuTrigger className="text-base">Blogs</NavigationMenuTrigger>
Blogs
</NavigationMenuTrigger>
<NavigationMenuContent> <NavigationMenuContent>
<ul className="grid w-[400px] "> <ul className="grid w-[400px] ">
{blogComponents.map((component) => ( {blogComponents.map((component) => (
<ListItem <ListItem key={component.title} title={component.title} href={component.href}>
key={component.title}
title={component.title}
href={component.href}
>
{component.description} {component.description}
</ListItem> </ListItem>
))} ))}
@ -160,10 +137,7 @@ export function UnsignedNav() {
</NavigationMenuItem> </NavigationMenuItem>
<NavigationMenuItem> <NavigationMenuItem>
<NavigationMenuLink <NavigationMenuLink className="text-base font-medium" href="docs">
className="text-base font-medium"
href="docs"
>
Docs Docs
</NavigationMenuLink> </NavigationMenuLink>
</NavigationMenuItem> </NavigationMenuItem>
@ -177,7 +151,7 @@ export function UnsignedNav() {
<div className="flex gap-2 pl-2"> <div className="flex gap-2 pl-2">
<ThemeToggle /> <ThemeToggle />
<Separator orientation="vertical" className="mx-3" /> <Separator orientation="vertical" className="mx-3" />
<Link href="/login"> <Link href="/auth">
<Button variant="secondary" className="border-2 border-border"> <Button variant="secondary" className="border-2 border-border">
Login Login
</Button> </Button>

View File

@ -0,0 +1,5 @@
import { createBrowserClient } from "@supabase/ssr";
export function createSupabaseClient() {
return createBrowserClient(process.env.NEXT_PUBLIC_SUPABASE_URL!, process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!);
}

View File

@ -0,0 +1,57 @@
import { createServerClient } from "@supabase/ssr";
import { NextResponse, type NextRequest } from "next/server";
export async function updateSession(request: NextRequest) {
let supabaseResponse = NextResponse.next({
request,
});
const supabase = createServerClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
{
cookies: {
getAll() {
return request.cookies.getAll();
},
setAll(cookiesToSet) {
cookiesToSet.forEach(({ name, value, options }) => request.cookies.set(name, value));
supabaseResponse = NextResponse.next({
request,
});
cookiesToSet.forEach(({ name, value, options }) => supabaseResponse.cookies.set(name, value, options));
},
},
}
);
// IMPORTANT: Avoid writing any logic between createServerClient and
// supabase.auth.getUser(). A simple mistake could make it very hard to debug
// issues with users being randomly logged out.
const {
data: { user },
} = await supabase.auth.getUser();
if (!user && !request.nextUrl.pathname.startsWith("/auth") && !request.nextUrl.pathname.startsWith("/auth")) {
// no user, potentially respond by redirecting the user to the login page
const url = request.nextUrl.clone();
url.pathname = "/auth";
return NextResponse.redirect(url);
}
// IMPORTANT: You *must* return the supabaseResponse object as it is. If you're
// creating a new response object with NextResponse.next() make sure to:
// 1. Pass the request in it, like so:
// const myNewResponse = NextResponse.next({ request })
// 2. Copy over the cookies, like so:
// myNewResponse.cookies.setAll(supabaseResponse.cookies.getAll())
// 3. Change the myNewResponse object to fit your needs, but avoid changing
// the cookies!
// 4. Finally:
// return myNewResponse
// If this is not done, you may be causing the browser and server to go out
// of sync and terminate the user's session prematurely!
return supabaseResponse;
}

View File

@ -1,25 +0,0 @@
import { createServerClient } from "@supabase/ssr";
import { cookies } from "next/headers";
export const createClient = () => {
const cookieStore = cookies();
return createServerClient(process.env.NEXT_PUBLIC_SUPABASE_URL!, process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!, {
cookies: {
getAll() {
return cookieStore.getAll();
},
setAll(cookiesToSet) {
try {
cookiesToSet.forEach(({ name, value, options }) => {
cookieStore.set(name, value, options);
});
} catch (error) {
// The `set` method was called from a Server Component.
// This can be ignored if you have middleware refreshing
// user sessions.
}
},
},
});
};

View File

@ -0,0 +1,19 @@
import { createServerClient, type CookieOptions } from "@supabase/ssr";
import { cookies } from "next/headers";
export function createSupabaseClient() {
const cookieStore = cookies();
return createServerClient(process.env.NEXT_PUBLIC_SUPABASE_URL!, process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!, {
cookies: {
getAll() {
return cookieStore.getAll();
},
setAll(cookiesToSet) {
try {
cookiesToSet.forEach(({ name, value, options }) => cookieStore.set(name, value, options));
} catch {}
},
},
});
}

19
src/middleware.ts Normal file
View File

@ -0,0 +1,19 @@
import { type NextRequest } from "next/server";
import { updateSession } from "@/lib/supabase/middleware";
export async function middleware(request: NextRequest) {
return await updateSession(request);
}
export const config = {
matcher: [
/*
* Match all request paths except for the ones starting with:
* - _next/static (static files)
* - _next/image (image optimization files)
* - favicon.ico (favicon file)
* Feel free to modify this pattern to include more paths.
*/
"/((?!_next/static|_next/image|$|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)",
],
};