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_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
114
package-lock.json
generated
@ -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",
|
||||||
|
|||||||
@ -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",
|
||||||
|
|||||||
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 { 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 you’ve read our Privacy Policy.
|
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>
|
<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>
|
||||||
|
|||||||
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,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>
|
||||||
|
|||||||
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