Files

72 lines
2.0 KiB
TypeScript

import { env } from "cloudflare:workers";
import { redirect } from "next/navigation";
import {
getChatGPTUser,
requireChatGPTUser,
type ChatGPTUser,
} from "./chatgpt-auth";
import type { CloudIdentity } from "@/db/repository";
export type CloudViewer = CloudIdentity & {
isLocalDemo: boolean;
isAdmin: boolean;
};
function toIdentity(user: ChatGPTUser): CloudIdentity {
return {
userId: user.userId,
email: user.email,
displayName: user.displayName,
};
}
function localDemoIdentity(): CloudIdentity | null {
if (process.env.NODE_ENV === "production") return null;
return {
userId: "local-demo-user",
email: "demo@nekonest.local",
displayName: "本地演示账户",
};
}
function isAdminEmail(email: string, isLocalDemo: boolean): boolean {
if (isLocalDemo) return true;
const configured = env.NEKONEST_CLOUD_ADMIN_EMAILS ?? "";
const allowed = configured
.split(",")
.map((item) => item.trim().toLowerCase())
.filter(Boolean);
return allowed.includes(email.toLowerCase());
}
export async function getCloudViewer(): Promise<CloudViewer | null> {
const signedIn = await getChatGPTUser();
const demo = signedIn ? null : localDemoIdentity();
const identity = signedIn ? toIdentity(signedIn) : demo;
if (!identity) return null;
const isLocalDemo = Boolean(demo);
return {
...identity,
isLocalDemo,
isAdmin: isAdminEmail(identity.email, isLocalDemo),
};
}
export async function requireCloudViewer(returnTo: string): Promise<CloudViewer> {
const viewer = await getCloudViewer();
if (viewer) return viewer;
const user = await requireChatGPTUser(returnTo);
const identity = toIdentity(user);
return {
...identity,
isLocalDemo: false,
isAdmin: isAdminEmail(identity.email, false),
};
}
export async function requireAdminViewer(returnTo = "/admin"): Promise<CloudViewer> {
const viewer = await requireCloudViewer(returnTo);
if (!viewer.isAdmin) redirect("/dashboard?admin=denied");
return viewer;
}