Files
pleskSaas/lib/agency.ts

140 lines
3.6 KiB
TypeScript

import { redirect } from "next/navigation";
import type { Database } from "@/lib/types";
import { createSupabaseAdminClient } from "@/lib/supabase/admin";
import { createSupabaseRouteClient } from "@/lib/supabase/route";
import { createSupabaseServerClient } from "@/lib/supabase/server";
export type AgencyContext = {
userId: string;
agencyId: string;
role: "owner" | "admin" | "member";
};
export function assertAgencyAdmin(context: AgencyContext) {
if (context.role !== "owner" && context.role !== "admin") {
throw new Error("Forbidden");
}
}
type AgencyMemberSummary = Pick<
Database["public"]["Tables"]["agency_members"]["Row"],
"agency_id" | "role"
>;
async function getOrCreateAgencyMembership(
userId: string,
email?: string | null,
) {
const supabase = createSupabaseServerClient() as any;
const admin = createSupabaseAdminClient() as any;
const { data: existingMembership } = (await supabase
.from("agency_members")
.select("agency_id, role")
.eq("user_id", userId)
.order("created_at", { ascending: true })
.limit(1)
.maybeSingle()) as { data: AgencyMemberSummary | null };
if (existingMembership) {
return existingMembership;
}
const defaultAgencyName = email
? `${email.split("@")[0] ?? "New"} Agency`
: "New Agency";
const { data: createdAgency, error: agencyError } = (await admin
.from("agencies")
.insert({ name: defaultAgencyName })
.select("id")
.single()) as {
data: Pick<Database["public"]["Tables"]["agencies"]["Row"], "id"> | null;
error: { message: string } | null;
};
if (agencyError || !createdAgency) {
throw new Error(
`Unable to create agency: ${agencyError?.message ?? "no row returned"}`,
);
}
const { error: membershipError } = await admin.from("agency_members").insert({
agency_id: createdAgency.id,
user_id: userId,
role: "owner",
});
if (!membershipError) {
return {
agency_id: createdAgency.id,
role: "owner" as const,
};
}
// If another request created membership first, fall back to a fresh read.
const { data: retriedMembership } = (await supabase
.from("agency_members")
.select("agency_id, role")
.eq("user_id", userId)
.order("created_at", { ascending: true })
.limit(1)
.maybeSingle()) as { data: AgencyMemberSummary | null };
if (retriedMembership) {
return retriedMembership;
}
throw new Error("Unable to create agency membership");
}
export async function getServerAgencyContextOrRedirect(): Promise<AgencyContext> {
const supabase = createSupabaseServerClient();
const {
data: { user },
} = await supabase.auth.getUser();
if (!user) {
redirect("/login");
}
const membership = await getOrCreateAgencyMembership(user.id, user.email);
return {
userId: user.id,
agencyId: membership.agency_id,
role: membership.role,
};
}
export async function getRouteAgencyContextOrThrow(): Promise<AgencyContext> {
const supabase = createSupabaseRouteClient();
const db = supabase as any;
const {
data: { session },
} = await supabase.auth.getSession();
if (!session?.user) {
throw new Error("Unauthorized");
}
const { data: membership } = (await db
.from("agency_members")
.select("agency_id, role")
.eq("user_id", session.user.id)
.order("created_at", { ascending: true })
.limit(1)
.maybeSingle()) as { data: AgencyMemberSummary | null };
if (!membership) {
throw new Error("No agency membership");
}
return {
userId: session.user.id,
agencyId: membership.agency_id,
role: membership.role,
};
}