study / web /src /store /auth.ts
xiaolongmr
官网来源取证 + 生图终局判定 + 对话卡片渲染
2aabbd0
Raw History Blame Contribute Delete
4.93 kB
"use client";
import localforage from "localforage";
export type AuthRole = "admin" | "user" | "normal";
export type StoredAuthSession = {
key: string;
role: AuthRole;
subjectId: string;
name: string;
accountPoolEnabled?: boolean;
imageMemoryButtonEnabled?: boolean;
imageShareEvidenceEnabled?: boolean;
wechatOpenid?: string;
expiresAt?: string;
};
export const AUTH_KEY_STORAGE_KEY = "chatgpt2api_auth_key";
export const AUTH_SESSION_STORAGE_KEY = "chatgpt2api_auth_session";
const authStorage = localforage.createInstance({
name: "chatgpt2api",
storeName: "auth",
});
let authSessionClearedInMemory = false;
function emitAuthSessionUpdated() {
if (typeof window === "undefined") {
return;
}
window.dispatchEvent(new Event("chatgpt2api:auth-session-updated"));
}
function normalizeSession(value: unknown, fallbackKey = ""): StoredAuthSession | null {
if (!value || typeof value !== "object") {
return null;
}
const candidate = value as Partial<StoredAuthSession>;
const key = String(candidate.key || fallbackKey || "").trim();
const role = candidate.role === "admin" || candidate.role === "user" || candidate.role === "normal" ? candidate.role : null;
if (!key || !role) {
return null;
}
return {
key,
role,
subjectId: String(candidate.subjectId || "").trim(),
name: String(candidate.name || "").trim(),
accountPoolEnabled: candidate.accountPoolEnabled === true,
imageMemoryButtonEnabled: candidate.imageMemoryButtonEnabled === true,
imageShareEvidenceEnabled: candidate.imageShareEvidenceEnabled === true,
wechatOpenid: String(candidate.wechatOpenid || "").trim(),
expiresAt: String(candidate.expiresAt || "").trim(),
};
}
function isSessionExpired(session: StoredAuthSession | null) {
if (!session?.expiresAt) {
return false;
}
const expiresAt = new Date(session.expiresAt).getTime();
return Number.isFinite(expiresAt) && expiresAt <= Date.now();
}
export function buildSessionExpiresAt(durationHours: unknown) {
const hours = Number(durationHours);
if (!Number.isFinite(hours) || hours <= 0) {
return "";
}
return new Date(Date.now() + hours * 60 * 60 * 1000).toISOString();
}
export function getDefaultRouteForRole(role: AuthRole) {
return role === "admin" ? "/accounts" : "/image";
}
export async function getStoredAuthKey() {
if (typeof window === "undefined") {
return "";
}
if (authSessionClearedInMemory) {
return "";
}
const [storedKey, storedSession] = await Promise.all([
authStorage.getItem<string>(AUTH_KEY_STORAGE_KEY),
authStorage.getItem<StoredAuthSession>(AUTH_SESSION_STORAGE_KEY),
]);
const normalizedSession = normalizeSession(storedSession, String(storedKey || ""));
if (isSessionExpired(normalizedSession)) {
await clearStoredAuthSession();
return "";
}
return String(normalizedSession?.key || storedKey || "").trim();
}
export async function getStoredAuthSession() {
if (typeof window === "undefined") {
return null;
}
if (authSessionClearedInMemory) {
return null;
}
const [storedKey, storedSession] = await Promise.all([
authStorage.getItem<string>(AUTH_KEY_STORAGE_KEY),
authStorage.getItem<StoredAuthSession>(AUTH_SESSION_STORAGE_KEY),
]);
const normalizedSession = normalizeSession(storedSession, String(storedKey || ""));
if (normalizedSession) {
if (isSessionExpired(normalizedSession)) {
await clearStoredAuthSession();
return null;
}
if (normalizedSession.key !== String(storedKey || "").trim()) {
await authStorage.setItem(AUTH_KEY_STORAGE_KEY, normalizedSession.key);
}
return normalizedSession;
}
if (String(storedKey || "").trim()) {
await clearStoredAuthSession();
}
return null;
}
export async function setStoredAuthSession(session: StoredAuthSession) {
const normalizedSession = normalizeSession(session);
if (!normalizedSession) {
await clearStoredAuthSession();
return;
}
authSessionClearedInMemory = false;
await Promise.all([
authStorage.setItem(AUTH_KEY_STORAGE_KEY, normalizedSession.key),
authStorage.setItem(AUTH_SESSION_STORAGE_KEY, normalizedSession),
]);
emitAuthSessionUpdated();
}
export async function setStoredAuthKey(authKey: string) {
const normalizedAuthKey = String(authKey || "").trim();
if (!normalizedAuthKey) {
await clearStoredAuthSession();
return;
}
authSessionClearedInMemory = false;
await authStorage.setItem(AUTH_KEY_STORAGE_KEY, normalizedAuthKey);
emitAuthSessionUpdated();
}
export async function clearStoredAuthSession() {
if (typeof window === "undefined") {
return;
}
authSessionClearedInMemory = true;
await Promise.all([
authStorage.removeItem(AUTH_KEY_STORAGE_KEY),
authStorage.removeItem(AUTH_SESSION_STORAGE_KEY),
]);
}
export async function clearStoredAuthKey() {
await clearStoredAuthSession();
}