mirror of
https://github.com/Sosokker/B2D-Ventures.git
synced 2025-12-19 05:54:06 +01:00
Merge pull request #18 from Sosokker/main
Get middleware and auth logic from backend
This commit is contained in:
commit
a9aedcf3c2
@ -1,2 +1,4 @@
|
||||
NEXT_PUBLIC_SUPABASE_URL=your-project-url
|
||||
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
114
package-lock.json
generated
@ -10,15 +10,19 @@
|
||||
"dependencies": {
|
||||
"@radix-ui/react-dropdown-menu": "^2.1.1",
|
||||
"@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",
|
||||
"@supabase/ssr": "^0.4.0",
|
||||
"@supabase/supabase-js": "^2.45.1",
|
||||
"@supabase/ssr": "^0.4.1",
|
||||
"@supabase/supabase-js": "^2.45.2",
|
||||
"class-variance-authority": "^0.7.0",
|
||||
"clsx": "^2.1.1",
|
||||
"embla-carousel-react": "^8.2.0",
|
||||
"lucide-react": "^0.428.0",
|
||||
"next": "14.2.5",
|
||||
"next-themes": "^0.3.0",
|
||||
"react": "^18",
|
||||
"react-countup": "^6.5.3",
|
||||
"react-dom": "^18",
|
||||
"tailwind-merge": "^2.5.2",
|
||||
"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": {
|
||||
"version": "1.1.0",
|
||||
"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": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.0.tgz",
|
||||
@ -1069,9 +1118,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@supabase/ssr": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@supabase/ssr/-/ssr-0.4.0.tgz",
|
||||
"integrity": "sha512-6WS3NUvHDhCPAFN2kJ79AQDO8+M9fJ7y2fYpxgZqIuJEpnnGsHDNnB5Xnv8CiaJIuRU+0pKboy62RVZBMfZ0Lg==",
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@supabase/ssr/-/ssr-0.4.1.tgz",
|
||||
"integrity": "sha512-000i7y4ITXjXU0T1JytZYU33VbUNklX9YN47hCweaLKsTBAEigJJJCeq3G+/IiwEggBt58Vu0KQ3UGXON7OmDQ==",
|
||||
"dependencies": {
|
||||
"cookie": "^0.6.0"
|
||||
},
|
||||
@ -1083,24 +1132,24 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@supabase/storage-js": {
|
||||
"version": "2.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@supabase/storage-js/-/storage-js-2.6.0.tgz",
|
||||
"integrity": "sha512-REAxr7myf+3utMkI2oOmZ6sdplMZZ71/2NEIEMBZHL9Fkmm3/JnaOZVSRqvG4LStYj2v5WhCruCzuMn6oD/Drw==",
|
||||
"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.1",
|
||||
"resolved": "https://registry.npmjs.org/@supabase/supabase-js/-/supabase-js-2.45.1.tgz",
|
||||
"integrity": "sha512-/PVe3lXmalazD8BGMIoI7+ttvT1mLXy13lNcoAPtjP1TDDY83g8csZbVR6l+0/RZtvJxl3LGXfTJT4bjWgC5Nw==",
|
||||
"version": "2.45.2",
|
||||
"resolved": "https://registry.npmjs.org/@supabase/supabase-js/-/supabase-js-2.45.2.tgz",
|
||||
"integrity": "sha512-kJKY3ISFusVKQWCP8Kqo20Ebxy2WLp6Ry/Suco0aQsPXH7bvn7clswsdhcfcH/5Tr0MYz/jcCjF0n/27SetiCw==",
|
||||
"dependencies": {
|
||||
"@supabase/auth-js": "2.64.4",
|
||||
"@supabase/functions-js": "2.4.1",
|
||||
"@supabase/node-fetch": "2.6.15",
|
||||
"@supabase/postgrest-js": "1.15.8",
|
||||
"@supabase/realtime-js": "2.10.2",
|
||||
"@supabase/storage-js": "2.6.0"
|
||||
"@supabase/storage-js": "2.7.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@swc/counter": {
|
||||
@ -1830,6 +1879,11 @@
|
||||
"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": {
|
||||
"version": "7.0.3",
|
||||
"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",
|
||||
"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": {
|
||||
"version": "9.2.2",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
|
||||
@ -4470,6 +4549,17 @@
|
||||
"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": {
|
||||
"version": "18.3.1",
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
|
||||
|
||||
@ -15,8 +15,8 @@
|
||||
"@radix-ui/react-progress": "^1.1.0",
|
||||
"@radix-ui/react-separator": "^1.1.0",
|
||||
"@radix-ui/react-slot": "^1.1.0",
|
||||
"@supabase/ssr": "^0.4.0",
|
||||
"@supabase/supabase-js": "^2.45.1",
|
||||
"@supabase/ssr": "^0.4.1",
|
||||
"@supabase/supabase-js": "^2.45.2",
|
||||
"class-variance-authority": "^0.7.0",
|
||||
"clsx": "^2.1.1",
|
||||
"embla-carousel-react": "^8.2.0",
|
||||
|
||||
24
src/app/auth/callback/route.ts
Normal file
24
src/app/auth/callback/route.ts
Normal 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`);
|
||||
}
|
||||
3
src/app/auth/error/page.tsx
Normal file
3
src/app/auth/error/page.tsx
Normal file
@ -0,0 +1,3 @@
|
||||
export default function AuthError() {
|
||||
return <div>Authentication Error</div>;
|
||||
}
|
||||
@ -2,6 +2,9 @@ import Image from "next/image";
|
||||
import { Button } from "@/components/ui/button";
|
||||
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() {
|
||||
return (
|
||||
<div
|
||||
@ -16,14 +19,8 @@ export default function Login() {
|
||||
</CardHeader>
|
||||
<CardContent className="flex flex-col gap-y-2 mx-28">
|
||||
<p className="self-center font-semibold text-slate-800">Continue With</p>
|
||||
<Button className="bg-foreground gap-2 rounded-xl">
|
||||
<Image src={"/logo/google.svg"} width={30} height={30} alt={"Google"} />
|
||||
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>
|
||||
<LoginButton />
|
||||
<LogoutButton />
|
||||
</CardContent>
|
||||
<CardFooter className="text-xs justify-center">
|
||||
By signing up, you agree to the Terms of Service and acknowledge you’ve read our Privacy Policy.
|
||||
@ -19,7 +19,7 @@ export default function Home() {
|
||||
<p>Together, we turn ideas into impact.</p>
|
||||
</span>
|
||||
<Button className="font-bold mt-4">
|
||||
<Link href="/login">Start Investing</Link>
|
||||
<Link href="/">Start Investing</Link>
|
||||
</Button>
|
||||
</span>
|
||||
</div>
|
||||
@ -79,10 +79,40 @@ export default function Home() {
|
||||
<p className="text-lg">The deals attracting the most interest right now</p>
|
||||
</span>
|
||||
<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 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} />
|
||||
<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
|
||||
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 className="self-center py-5">
|
||||
<Button>
|
||||
|
||||
25
src/components/auth/loginButton.tsx
Normal file
25
src/components/auth/loginButton.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
16
src/components/auth/logoutButton.tsx
Normal file
16
src/components/auth/logoutButton.tsx
Normal 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>;
|
||||
}
|
||||
@ -31,10 +31,8 @@ const landings = [
|
||||
route: "/crm-landing",
|
||||
},
|
||||
];
|
||||
const ListItem = React.forwardRef<
|
||||
React.ElementRef<"a">,
|
||||
React.ComponentPropsWithoutRef<"a">
|
||||
>(({ className, title, children, ...props }, ref) => {
|
||||
const ListItem = React.forwardRef<React.ElementRef<"a">, React.ComponentPropsWithoutRef<"a">>(
|
||||
({ className, title, children, ...props }, ref) => {
|
||||
return (
|
||||
<li>
|
||||
<NavigationMenuLink asChild>
|
||||
@ -44,18 +42,16 @@ const ListItem = React.forwardRef<
|
||||
"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}
|
||||
>
|
||||
{...props}>
|
||||
<div className="text-sm font-medium leading-none">{title}</div>
|
||||
<hr />
|
||||
<p className="line-clamp-2 text-sm leading-snug text-muted-foreground">
|
||||
{children}
|
||||
</p>
|
||||
<p className="line-clamp-2 text-sm leading-snug text-muted-foreground">{children}</p>
|
||||
</a>
|
||||
</NavigationMenuLink>
|
||||
</li>
|
||||
);
|
||||
});
|
||||
}
|
||||
);
|
||||
ListItem.displayName = "ListItem";
|
||||
|
||||
export function UnsignedNav() {
|
||||
@ -90,8 +86,7 @@ export function UnsignedNav() {
|
||||
<Link
|
||||
className="flex-none text-xl font-semibold dark:text-white focus:outline-none focus:opacity-80"
|
||||
href="/"
|
||||
aria-label="Brand"
|
||||
>
|
||||
aria-label="Brand">
|
||||
<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} />
|
||||
B2DVentures
|
||||
@ -103,17 +98,11 @@ export function UnsignedNav() {
|
||||
<NavigationMenu>
|
||||
<NavigationMenuList>
|
||||
<NavigationMenuItem>
|
||||
<NavigationMenuTrigger className="text-base">
|
||||
Businesses
|
||||
</NavigationMenuTrigger>
|
||||
<NavigationMenuTrigger className="text-base">Businesses</NavigationMenuTrigger>
|
||||
<NavigationMenuContent>
|
||||
<ul className="grid w-[400px] ">
|
||||
{businessComponents.map((component) => (
|
||||
<ListItem
|
||||
key={component.title}
|
||||
title={component.title}
|
||||
href={component.href}
|
||||
>
|
||||
<ListItem key={component.title} title={component.title} href={component.href}>
|
||||
{component.description}
|
||||
</ListItem>
|
||||
))}
|
||||
@ -122,17 +111,11 @@ export function UnsignedNav() {
|
||||
</NavigationMenuItem>
|
||||
|
||||
<NavigationMenuItem>
|
||||
<NavigationMenuTrigger className="text-base font-medium ">
|
||||
Projects
|
||||
</NavigationMenuTrigger>
|
||||
<NavigationMenuTrigger className="text-base font-medium ">Projects</NavigationMenuTrigger>
|
||||
<NavigationMenuContent>
|
||||
<ul className="grid w-[400px] ">
|
||||
{projectComponents.map((component) => (
|
||||
<ListItem
|
||||
key={component.title}
|
||||
title={component.title}
|
||||
href={component.href}
|
||||
>
|
||||
<ListItem key={component.title} title={component.title} href={component.href}>
|
||||
{component.description}
|
||||
</ListItem>
|
||||
))}
|
||||
@ -141,17 +124,11 @@ export function UnsignedNav() {
|
||||
</NavigationMenuItem>
|
||||
|
||||
<NavigationMenuItem>
|
||||
<NavigationMenuTrigger className="text-base">
|
||||
Blogs
|
||||
</NavigationMenuTrigger>
|
||||
<NavigationMenuTrigger className="text-base">Blogs</NavigationMenuTrigger>
|
||||
<NavigationMenuContent>
|
||||
<ul className="grid w-[400px] ">
|
||||
{blogComponents.map((component) => (
|
||||
<ListItem
|
||||
key={component.title}
|
||||
title={component.title}
|
||||
href={component.href}
|
||||
>
|
||||
<ListItem key={component.title} title={component.title} href={component.href}>
|
||||
{component.description}
|
||||
</ListItem>
|
||||
))}
|
||||
@ -160,10 +137,7 @@ export function UnsignedNav() {
|
||||
</NavigationMenuItem>
|
||||
|
||||
<NavigationMenuItem>
|
||||
<NavigationMenuLink
|
||||
className="text-base font-medium"
|
||||
href="docs"
|
||||
>
|
||||
<NavigationMenuLink className="text-base font-medium" href="docs">
|
||||
Docs
|
||||
</NavigationMenuLink>
|
||||
</NavigationMenuItem>
|
||||
@ -177,7 +151,7 @@ export function UnsignedNav() {
|
||||
<div className="flex gap-2 pl-2">
|
||||
<ThemeToggle />
|
||||
<Separator orientation="vertical" className="mx-3" />
|
||||
<Link href="/login">
|
||||
<Link href="/auth">
|
||||
<Button variant="secondary" className="border-2 border-border">
|
||||
Login
|
||||
</Button>
|
||||
|
||||
5
src/lib/supabase/clientComponentClient.ts
Normal file
5
src/lib/supabase/clientComponentClient.ts
Normal 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!);
|
||||
}
|
||||
57
src/lib/supabase/middleware.ts
Normal file
57
src/lib/supabase/middleware.ts
Normal 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;
|
||||
}
|
||||
@ -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.
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
19
src/lib/supabase/serverComponentClient.ts
Normal file
19
src/lib/supabase/serverComponentClient.ts
Normal 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
19
src/middleware.ts
Normal 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)$).*)",
|
||||
],
|
||||
};
|
||||
Loading…
Reference in New Issue
Block a user