84 lines
2.7 KiB
TypeScript
84 lines
2.7 KiB
TypeScript
import "server-only";
|
|
import { createClient } from "@supabase/supabase-js";
|
|
import { createServerClient } from "@supabase/ssr";
|
|
import { cookies } from "next/headers";
|
|
import { cache } from "react";
|
|
import type { Database } from "@/shared/database";
|
|
import { ServiceError, checkServiceError } from "./service-error";
|
|
let singleton: ReturnType<typeof createClient<Database>> | undefined;
|
|
export function admin() {
|
|
if (!process.env.NEXT_PUBLIC_SUPABASE_URL || !process.env.SUPABASE_SECRET_KEY)
|
|
throw new ServiceError(
|
|
"MineGeo is not configured yet. Please try again later.",
|
|
);
|
|
return (singleton ??= createClient<Database>(
|
|
process.env.NEXT_PUBLIC_SUPABASE_URL,
|
|
process.env.SUPABASE_SECRET_KEY,
|
|
{ auth: { persistSession: false, autoRefreshToken: false } },
|
|
));
|
|
}
|
|
export async function authClient() {
|
|
const jar = await cookies();
|
|
return createServerClient(
|
|
process.env.NEXT_PUBLIC_SUPABASE_URL!,
|
|
process.env.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY!,
|
|
{
|
|
cookies: {
|
|
getAll: () => jar.getAll(),
|
|
setAll: (values) => {
|
|
try {
|
|
values.forEach(({ name, value, options }) =>
|
|
jar.set(name, value, options),
|
|
);
|
|
} catch {
|
|
/* Server components refresh through proxy. */
|
|
}
|
|
},
|
|
},
|
|
},
|
|
);
|
|
}
|
|
/** Authentication identity only. Restricted accounts use this solely for appeals/auth. */
|
|
export const rawUser = cache(async function rawUser() {
|
|
if (
|
|
!process.env.NEXT_PUBLIC_SUPABASE_URL ||
|
|
!process.env.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY
|
|
)
|
|
return null;
|
|
const client = await authClient();
|
|
const {
|
|
data: { user },
|
|
} = await client.auth.getUser();
|
|
return user;
|
|
});
|
|
export const accountState = cache(async function accountState(id: string) {
|
|
const result = await admin()
|
|
.from("profiles")
|
|
.select("account_status")
|
|
.eq("id", id)
|
|
.maybeSingle();
|
|
checkServiceError(result.error);
|
|
return result.data?.account_status ?? "active";
|
|
});
|
|
export async function assertAccountActive(id: string) {
|
|
const status = await accountState(id);
|
|
if (status !== "active") {
|
|
const { restrictionMessage } = await import("@/shared/account-controls");
|
|
throw new ServiceError(restrictionMessage(status), 403);
|
|
}
|
|
}
|
|
export const user = cache(async function user() {
|
|
const account = await rawUser();
|
|
if (account) await assertAccountActive(account.id);
|
|
return account;
|
|
});
|
|
export function required<T>(result: {
|
|
data: T;
|
|
error: { message: string; code?: string } | null;
|
|
}): NonNullable<T> {
|
|
checkServiceError(result.error);
|
|
if (result.data === null || result.data === undefined)
|
|
throw new Error("Record not found");
|
|
return result.data as NonNullable<T>;
|
|
}
|