Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 4 additions & 18 deletions app/api/callback/route.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
import { cookies } from "next/headers";
import { NextResponse } from "next/server";
import { createRouteHandlerClient } from "@supabase/auth-helpers-nextjs";
import { createClient } from "@/lib/supabase/server";
import type { NextRequest } from "next/server";

// Handles PKCE auth code exchange when Supabase redirects back with ?code=...
// The cookie-backed client reads the code verifier set during sign-in and
// writes the resulting session cookies onto the redirect response.
export async function GET(req: NextRequest) {
const { searchParams, origin } = new URL(req.url);
const code = searchParams.get("code");
const next = searchParams.get("next") ?? "/";

if (code) {
const supabase = createRouteHandlerClient({ cookies });
const supabase = createClient();
const { error } = await supabase.auth.exchangeCodeForSession(code);
if (!error) {
return NextResponse.redirect(`${origin}${next}`);
Expand All @@ -21,18 +22,3 @@ export async function GET(req: NextRequest) {
`${origin}/auth/email-link-sign-in?error=auth_callback_failed`,
);
}

// Syncs auth state changes (e.g. SIGNED_IN, SIGNED_OUT) sent from the client
export async function POST(req: NextRequest) {
const supabase = createRouteHandlerClient({ cookies });

const { event, session } = await req.json();

if (event === "SIGNED_IN" && session) {
await supabase.auth.setSession(session);
} else if (event === "SIGNED_OUT") {
await supabase.auth.signOut();
}

return NextResponse.json({ status: "success" });
}
14 changes: 7 additions & 7 deletions app/api/email-sign-in/route.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,7 @@
import { createClient } from "@supabase/supabase-js";
import { createClient } from "@/lib/supabase/server";
import { NextRequest, NextResponse } from "next/server";

export async function POST(req: NextRequest) {
const supabase = createClient(
process.env.NEXT_PUBLIC_SUPABASE_URL || "",
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY || "",
);

const { email } = await req.json();

if (!email || !email.endsWith("@pointblank.club")) {
Comment thread
jayantkageri marked this conversation as resolved.
Expand All @@ -19,7 +14,12 @@ export async function POST(req: NextRequest) {
);
}

const { data, error } = await supabase.auth.signInWithOtp({
// Cookie-backed client: signInWithOtp stores the PKCE code verifier in a
// cookie that travels back to the browser, so /api/callback can complete the
// exchange when the user clicks the email link.
const supabase = createClient();

const { error } = await supabase.auth.signInWithOtp({
email,
options: {
emailRedirectTo: `${process.env.NEXT_PUBLIC_DOMAIN || "https://careers.pointblank.club"}/api/callback`,
Expand Down
5 changes: 2 additions & 3 deletions app/api/member/[type]/[id]/route.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { NextRequest, NextResponse } from 'next/server';
import { createClient } from '@supabase/supabase-js';
import { cookies } from 'next/headers';
import { createServerComponentClient } from '@supabase/auth-helpers-nextjs';
import { createClient as createServerSupabaseClient } from '@/lib/supabase/server';

import {
AchievementService,
Expand Down Expand Up @@ -63,7 +62,7 @@ export async function DELETE(
return NextResponse.json({ error: 'DELETE not supported for this type' }, { status: 405 });
}

const supabase = createServerComponentClient({ cookies });
const supabase = createServerSupabaseClient();
const { data: { user } } = await supabase.auth.getUser();

if (!user) {
Expand Down
3 changes: 2 additions & 1 deletion app/directory/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ const getOrigin = (): string => {
if (typeof window !== "undefined") {
return window.location.origin;
}
return process.env.NEXT_PUBLIC_SITE_URL || "https://career.pointblank.club";
return process.env.NEXT_PUBLIC_DOMAIN
|| "https://careers.pointblank.club";
};

function DirectoryContent() {
Expand Down
62 changes: 1 addition & 61 deletions app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,46 +1,8 @@
"use client";
import { useEffect, Suspense } from "react";
import { supabase } from "@/lib/supabaseClient";
import { useAuthStore } from "@/lib/authStore";
import { useRouter, useSearchParams } from "next/navigation";
import { HeroSection } from "@/components/home/hero-section";
import { FeaturesSection } from "@/components/home/features-section";
import { AnimatedGradientBackground } from '@/components/ui/animated-gradient-background';
import Domains from '@/components/home/domains'

function AuthHandler() {
const router = useRouter();
const searchParams = useSearchParams();

useEffect(() => {
const code = searchParams.get("code");
if (code) {
supabase.auth
.exchangeCodeForSession(code)
.then(async ({ data, error }) => {
if (error) {
console.error("Exchange error:", error);
}
const { session } = data;
if (session) {
useAuthStore.getState().setUser(session.user);
router.replace(window.location.pathname);
}
});
} else {

supabase.auth.getSession().then(({ data: { session } }) => {
if (session) {
useAuthStore.getState().setUser(session.user);
}
});
}
}, [router, searchParams]);

return null;
}

function PageContent() {
export default function UploadPage() {
return (
<div className="relative min-h-screen pb-20 overflow-x-hidden">
{/* Animated gradient background with reduced opacity */}
Expand All @@ -60,25 +22,3 @@ function PageContent() {
</div>
);
}

function PageLoading() {
return (
<div className="container py-8 md:py-12">
<div className="animate-pulse">
<div className="h-32 bg-muted rounded mb-8"></div>
<div className="h-64 bg-muted rounded"></div>
</div>
</div>
);
}

export default function UploadPage() {
return (
<>
<Suspense fallback={null}>
<AuthHandler />
</Suspense>
<PageContent />
</>
);
}
7 changes: 3 additions & 4 deletions app/profile/[id]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { Metadata } from "next";
import { notFound, redirect } from "next/navigation";
import { createServerComponentClient } from "@supabase/auth-helpers-nextjs";
import { cookies } from "next/headers";
import { createClient } from "@/lib/supabase/server";
import {
MemberService,
SkillService,
Expand Down Expand Up @@ -42,7 +41,7 @@ function formatResumeDisplayName(fullName: string, year: number): string {

export async function generateMetadata({ params }: ProfilePageProps): Promise<Metadata> {
const { id } = await params;
const supabase = createServerComponentClient({ cookies });
const supabase = createClient();

let member = await MemberService.getMemberById(supabase, id);

Expand Down Expand Up @@ -70,7 +69,7 @@ export async function generateMetadata({ params }: ProfilePageProps): Promise<Me

export default async function ProfilePage({ params }: ProfilePageProps) {
const { id } = await params;
const supabase = createServerComponentClient({ cookies });
const supabase = createClient();
const { data: { user } } = await supabase.auth.getUser();

let member = await MemberService.getMemberById(supabase, id);
Expand Down
14 changes: 4 additions & 10 deletions app/providers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,12 @@

import { ThemeProvider } from "next-themes";
import { Toaster } from "@/components/ui/toaster";
import { SessionContextProvider } from "@supabase/auth-helpers-react";
import { createPagesBrowserClient } from "@supabase/auth-helpers-nextjs";

const supabase = createPagesBrowserClient();

export function Providers({ children }: { children: React.ReactNode }) {
return (
<SessionContextProvider supabaseClient={supabase}>
<ThemeProvider attribute="class" defaultTheme="dark" enableSystem>
{children}
<Toaster />
</ThemeProvider>
</SessionContextProvider>
<ThemeProvider attribute="class" defaultTheme="dark" enableSystem>
{children}
<Toaster />
</ThemeProvider>
);
}
3 changes: 1 addition & 2 deletions app/upload/confirm/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { v4 as uuidv4 } from 'uuid';
import { createClient } from '@supabase/supabase-js';
import { Checkbox } from "@/components/ui/checkbox";
import { Textarea } from "@/components/ui/textarea";
import { createClientComponentClient } from '@supabase/auth-helpers-nextjs';
import { supabase } from "@/lib/supabaseClient";
import {
Select,
SelectContent,
Expand Down Expand Up @@ -413,7 +413,6 @@ const handleSubmit = async (e: React.FormEvent) => {
setSaving(true);

try {
const supabase = createClientComponentClient();
const { data: { session }, error } = await supabase.auth.getSession();

if (!session || !session.user) throw new Error("Not authenticated");
Expand Down
3 changes: 1 addition & 2 deletions components/profile/resume-section.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { Button } from "@/components/ui/button";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Badge } from "@/components/ui/badge";
import { Dialog, DialogContent, DialogHeader, DialogTitle} from "@/components/ui/dialog";
import { createClientComponentClient } from '@supabase/auth-helpers-nextjs';
import { supabase } from "@/lib/supabaseClient";
import { useToast } from "@/hooks/use-toast";
import {
AlertDialog,
Expand Down Expand Up @@ -180,7 +180,6 @@ export function ResumeSection({ resumeUrl, isEditable, userId, displayFileName }
const [resumeFiles, setResumeFiles] = useState<ResumeFile[]>([]);
const [loading, setLoading] = useState(true);
const [uploading, setUploading] = useState(false);
const supabase = createClientComponentClient();
const { toast } = useToast();

useEffect(() => {
Expand Down
2 changes: 0 additions & 2 deletions components/upload/confirmation-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Separator } from "@/components/ui/separator";
import { useAuthStore } from "@/lib/authStore";
import { createClientComponentClient } from "@supabase/auth-helpers-nextjs";
import {
Card,
CardContent,
Expand Down Expand Up @@ -189,7 +188,6 @@ export function ConfirmationForm({ parsedData }: ConfirmationFormProps) {
setIsSubmitting(true);

try {
const supabase = createClientComponentClient();
const { data: { session }, error } = await supabase.auth.getSession();

if (!session || !session.access_token) {
Expand Down
5 changes: 2 additions & 3 deletions components/upload/resume-upload.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,13 @@ import { Input } from "@/components/ui/input";
import { Separator } from "@/components/ui/separator";
import { Badge } from "@/components/ui/badge";
import { useToast } from "@/hooks/use-toast";
import { createClientComponentClient } from '@supabase/auth-helpers-nextjs';
import { supabase } from "@/lib/supabaseClient";

export function ResumeUpload() {
const router = useRouter();
const { toast } = useToast();
const fileInputRef = useRef<HTMLInputElement>(null);
const supabase = createClientComponentClient();


const [file, setFile] = useState<File | null>(null);
const [isDragging, setIsDragging] = useState(false);
const [uploading, setUploading] = useState(false);
Expand Down
13 changes: 4 additions & 9 deletions lib/authStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,10 @@ export const initAuthListener = () => {
useAuthStore.getState().setUser(session?.user ?? null);
});

supabase.auth.onAuthStateChange(async (event, session) => {
// Cookies are now the single source of truth shared with the server, so no
// manual sync to /api/callback is needed — just mirror the user into the store
// so the UI reacts to auth changes.
supabase.auth.onAuthStateChange((_event, session) => {
useAuthStore.getState().setUser(session?.user ?? null);

await fetch("/api/callback", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ event, session }),
});
});
};
33 changes: 33 additions & 0 deletions lib/supabase/server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { createServerClient } from "@supabase/ssr";
import { cookies } from "next/headers";

// Cookie-backed Supabase client for Route Handlers and Server Components.
// In Route Handlers the cookie store is writable, so the PKCE code verifier
// (and refreshed session) round-trips through the browser's cookies. In Server
// Components writing is a no-op (the store is read-only) — the middleware
// refreshes the session there instead.
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 {
// Called from a Server Component — safe to ignore; the session is
// refreshed by middleware.
}
},
},
},
);
};
11 changes: 7 additions & 4 deletions lib/supabaseClient.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { createClient } from '@supabase/supabase-js';
import { createBrowserClient } from "@supabase/ssr";

export const supabase = createClient(
// Cookie-backed browser client. Sharing the cookie store with the server
// clients (middleware, route handlers, server components) gives the whole app a
// single source of truth for the session.
export const supabase = createBrowserClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
);
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
);
Loading
Loading