feat: establish NekoNest Cloud control and relay
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
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;
|
||||
}
|
||||
Reference in New Issue
Block a user