2026-04-04 20:00:53 +00:00
|
|
|
import { createServerClient } from "@supabase/ssr";
|
|
|
|
|
import { NextResponse, type NextRequest } from "next/server";
|
|
|
|
|
import { hasEnvVars } from "../utils";
|
|
|
|
|
|
|
|
|
|
export async function updateSession(request: NextRequest) {
|
|
|
|
|
let supabaseResponse = NextResponse.next({
|
|
|
|
|
request,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (!hasEnvVars) {
|
|
|
|
|
return supabaseResponse;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const supabase = createServerClient(
|
|
|
|
|
process.env.NEXT_PUBLIC_SUPABASE_URL!,
|
|
|
|
|
process.env.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY!,
|
|
|
|
|
{
|
|
|
|
|
cookies: {
|
|
|
|
|
getAll() {
|
|
|
|
|
return request.cookies.getAll();
|
|
|
|
|
},
|
|
|
|
|
setAll(cookiesToSet) {
|
|
|
|
|
cookiesToSet.forEach(({ name, value }) =>
|
|
|
|
|
request.cookies.set(name, value),
|
|
|
|
|
);
|
|
|
|
|
supabaseResponse = NextResponse.next({
|
|
|
|
|
request,
|
|
|
|
|
});
|
|
|
|
|
cookiesToSet.forEach(({ name, value, options }) =>
|
|
|
|
|
supabaseResponse.cookies.set(name, value, options),
|
|
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const { data } = await supabase.auth.getClaims();
|
|
|
|
|
const user = data?.claims;
|
|
|
|
|
|
2026-04-05 00:59:25 +00:00
|
|
|
// Paths that do not require auth OR do not enforce organization check
|
|
|
|
|
const isAuthPath = request.nextUrl.pathname.startsWith("/auth") || request.nextUrl.pathname.startsWith("/login");
|
|
|
|
|
const isApiPath = request.nextUrl.pathname.startsWith("/api");
|
|
|
|
|
// The sign up callback or standard paths like favicon Next.js ignores, but just in case
|
|
|
|
|
const isPublicResource = request.nextUrl.pathname.startsWith("/_next") || request.nextUrl.pathname.includes(".");
|
|
|
|
|
|
|
|
|
|
if (isPublicResource) {
|
|
|
|
|
return supabaseResponse;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!user && !isAuthPath && !isApiPath) {
|
|
|
|
|
// potentially respond by redirecting the user to the login page
|
2026-04-04 20:00:53 +00:00
|
|
|
const url = request.nextUrl.clone();
|
|
|
|
|
url.pathname = "/auth/login";
|
|
|
|
|
return NextResponse.redirect(url);
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-05 00:59:25 +00:00
|
|
|
if (user && !isAuthPath && !isApiPath) {
|
|
|
|
|
// Logged in, check if they are going to an app route
|
|
|
|
|
// Fetch profile to see if they have an organization
|
|
|
|
|
const { data: profile } = await supabase
|
|
|
|
|
.from('profiles')
|
|
|
|
|
.select('organization_id')
|
|
|
|
|
.eq('id', user.sub)
|
|
|
|
|
.single();
|
|
|
|
|
|
|
|
|
|
const hasOrg = !!profile?.organization_id;
|
|
|
|
|
const isOnboarding = request.nextUrl.pathname.startsWith("/onboarding");
|
|
|
|
|
|
|
|
|
|
if (!hasOrg && !isOnboarding) {
|
|
|
|
|
// Force user to onboarding if they have no organization
|
|
|
|
|
const url = request.nextUrl.clone();
|
|
|
|
|
url.pathname = "/onboarding";
|
|
|
|
|
return NextResponse.redirect(url);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (hasOrg && isOnboarding) {
|
|
|
|
|
// If they already have an organization, they shouldn't be in onboarding
|
|
|
|
|
const url = request.nextUrl.clone();
|
|
|
|
|
url.pathname = "/dashboard";
|
|
|
|
|
return NextResponse.redirect(url);
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-04-04 20:00:53 +00:00
|
|
|
|
|
|
|
|
return supabaseResponse;
|
|
|
|
|
}
|