105 lines
2.9 KiB
TypeScript
105 lines
2.9 KiB
TypeScript
import { cookies } from "next/headers";
|
|
import { SESSION_COOKIE_NAME, getAuthRuntimeConfig } from "@/lib/auth/config";
|
|
import { hasAdminAccess } from "@/lib/auth/permissions";
|
|
import { parseSessionCookieValue, readChunkedCookieValue, type AuthSession, type AuthUser } from "@/lib/auth/session";
|
|
import { DEFAULT_OWNER_ID } from "@/lib/server/runtime";
|
|
|
|
export class AuthRequiredError extends Error {
|
|
status = 401;
|
|
|
|
constructor(message = "请先登录。") {
|
|
super(message);
|
|
this.name = "AuthRequiredError";
|
|
}
|
|
}
|
|
|
|
export class AuthConfigurationError extends Error {
|
|
status = 503;
|
|
|
|
constructor(message: string) {
|
|
super(message);
|
|
this.name = "AuthConfigurationError";
|
|
}
|
|
}
|
|
|
|
const localUser: AuthUser = {
|
|
id: DEFAULT_OWNER_ID,
|
|
subject: DEFAULT_OWNER_ID,
|
|
username: "demo",
|
|
displayName: "智念演示用户",
|
|
clientId: "local-dev",
|
|
authorities: ["zhinian_admin"],
|
|
scope: []
|
|
};
|
|
|
|
function localSession(): AuthSession {
|
|
const now = Math.floor(Date.now() / 1000);
|
|
return {
|
|
version: 1,
|
|
user: localUser,
|
|
issuedAt: now,
|
|
expiresAt: now + 24 * 60 * 60
|
|
};
|
|
}
|
|
|
|
export async function getOptionalAuthSession(): Promise<AuthSession | null> {
|
|
const config = getAuthRuntimeConfig();
|
|
if (!config.sessionSecret) return null;
|
|
const cookieStore = await cookies();
|
|
return parseSessionCookieValue(
|
|
readChunkedCookieValue(SESSION_COOKIE_NAME, (name) => cookieStore.get(name)?.value),
|
|
config.sessionSecret
|
|
);
|
|
}
|
|
|
|
export async function requireAppSession(): Promise<AuthSession> {
|
|
const session = await getOptionalAuthSession();
|
|
if (session) return session;
|
|
const config = getAuthRuntimeConfig();
|
|
if (!config.required) return localSession();
|
|
if (!config.configured) {
|
|
throw new AuthConfigurationError(`认证配置不完整:${config.missing.join(", ") || "未知配置"}`);
|
|
}
|
|
throw new AuthRequiredError();
|
|
}
|
|
|
|
export async function requireAppUser(): Promise<AuthUser> {
|
|
return (await requireAppSession()).user;
|
|
}
|
|
|
|
export class AdminRequiredError extends Error {
|
|
status = 403;
|
|
|
|
constructor(message = "需要管理员权限。") {
|
|
super(message);
|
|
this.name = "AdminRequiredError";
|
|
}
|
|
}
|
|
|
|
export async function requireAdminUser(): Promise<AuthUser> {
|
|
return (await requireAdminSession()).user;
|
|
}
|
|
|
|
export async function requireAdminSession(): Promise<AuthSession> {
|
|
const session = await requireAppSession();
|
|
if (!hasAdminAccess(session.user)) throw new AdminRequiredError();
|
|
return session;
|
|
}
|
|
|
|
export async function getShellAuthState(): Promise<{
|
|
user: AuthUser | null;
|
|
authRequired: boolean;
|
|
authConfigured: boolean;
|
|
isAdmin: boolean;
|
|
}> {
|
|
const config = getAuthRuntimeConfig();
|
|
const session = await getOptionalAuthSession();
|
|
const user = session?.user || (!config.required ? localUser : null);
|
|
return {
|
|
user,
|
|
authRequired: config.required,
|
|
authConfigured: config.configured,
|
|
isAdmin: hasAdminAccess(user)
|
|
};
|
|
}
|