diff --git a/.env.local.example b/.env.local.example index 3765cf0..06ff0e5 100644 --- a/.env.local.example +++ b/.env.local.example @@ -1,2 +1,4 @@ NEXT_PUBLIC_SUPABASE_URL=your-project-url -NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key \ No newline at end of file +NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key +NEXT_PUBLIC_AUTH_GOOGLE_ID=your-google-id +NEXT_PUBLIC_AUTH_GOOGLE_SECRET=your-google-secret \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 575d9d4..361a165 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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", diff --git a/package.json b/package.json index 16cb4e5..0b37c81 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/src/app/auth/callback/route.ts b/src/app/auth/callback/route.ts new file mode 100644 index 0000000..451854e --- /dev/null +++ b/src/app/auth/callback/route.ts @@ -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`); +} diff --git a/src/app/auth/error/page.tsx b/src/app/auth/error/page.tsx new file mode 100644 index 0000000..7552b02 --- /dev/null +++ b/src/app/auth/error/page.tsx @@ -0,0 +1,3 @@ +export default function AuthError() { + return
Continue With
- - +Together, we turn ideas into impact.
The deals attracting the most interest right now