Some checks failed
Deploy Production / Build, push, deploy, verify (push) Has been cancelled
- 移除已迁移业务 AppData 运行时 fallback,改走领域 API 和关系表快读 - 补齐需求产品负责人、版本计划任务 JSON 和成员 username 回填迁移 - 统一治理字典入口,并补充 AI provider、数据源契约和领域服务测试 Co-Authored-By: Codex GPT-5 <codex@openai.com>
118 lines
3.7 KiB
TypeScript
118 lines
3.7 KiB
TypeScript
'use client';
|
|
|
|
import { create } from 'zustand';
|
|
import type { Member } from '@/lib/members';
|
|
import { ensureMemberUsernames } from '@/lib/members';
|
|
import { SYSTEM_ADMIN_MEMBER, ensureSystemAdminMember } from '@/lib/member-system';
|
|
import { findMemberByUsernameCredentials, resolveAuthUserFromMembers, toAuthUser, type AuthUser } from '@/lib/auth-user';
|
|
import { listMembersDomain } from '@/lib/domain-api';
|
|
import { useMemberStore } from './useMemberStore';
|
|
|
|
interface AuthState {
|
|
user: AuthUser | null;
|
|
isAuthenticated: boolean;
|
|
login: (username: string, password: string, remember: boolean) => Promise<boolean>;
|
|
logout: () => void;
|
|
checkAuth: () => void;
|
|
refreshUser: () => Promise<void>;
|
|
}
|
|
|
|
const SESSION_KEY = 'ftb_auth_session';
|
|
const PERSIST_KEY = 'ftb_auth_persist';
|
|
|
|
const FALLBACK_MEMBERS: Member[] = [SYSTEM_ADMIN_MEMBER];
|
|
|
|
function writeAuthStorage(user: AuthUser, remember: boolean) {
|
|
sessionStorage.setItem(SESSION_KEY, JSON.stringify(user));
|
|
if (remember) {
|
|
localStorage.setItem(PERSIST_KEY, JSON.stringify(user));
|
|
}
|
|
}
|
|
|
|
function clearAuthStorage() {
|
|
sessionStorage.removeItem(SESSION_KEY);
|
|
localStorage.removeItem(PERSIST_KEY);
|
|
}
|
|
|
|
async function loadMembersForAuth(): Promise<Member[]> {
|
|
try {
|
|
const domainMembers = await listMembersDomain();
|
|
if (domainMembers.length) {
|
|
const ensured = ensureSystemAdminMember(domainMembers);
|
|
return ensureMemberUsernames(ensured.members).members;
|
|
}
|
|
} catch {}
|
|
|
|
const inMemoryMembers = useMemberStore.getState().members;
|
|
const members = inMemoryMembers.length > 0 ? ensureSystemAdminMember(inMemoryMembers).members : FALLBACK_MEMBERS;
|
|
return ensureMemberUsernames(members).members;
|
|
}
|
|
|
|
async function refreshStoredAuthUser(user: AuthUser, remember: boolean, set: (state: Partial<AuthState>) => void) {
|
|
try {
|
|
const members = await loadMembersForAuth();
|
|
const next = resolveAuthUserFromMembers(user, members);
|
|
if (!next) {
|
|
clearAuthStorage();
|
|
set({ user: null, isAuthenticated: false });
|
|
return;
|
|
}
|
|
set({ user: next, isAuthenticated: true });
|
|
writeAuthStorage(next, remember);
|
|
} catch {}
|
|
}
|
|
|
|
export const useAuthStore = create<AuthState>((set) => ({
|
|
user: null,
|
|
isAuthenticated: false,
|
|
|
|
login: async (username, password, remember) => {
|
|
const members = await loadMembersForAuth();
|
|
const member = findMemberByUsernameCredentials(username, password, members);
|
|
if (!member) return false;
|
|
|
|
const user = toAuthUser(member);
|
|
set({ user, isAuthenticated: true });
|
|
writeAuthStorage(user, remember);
|
|
return true;
|
|
},
|
|
|
|
logout: () => {
|
|
set({ user: null, isAuthenticated: false });
|
|
clearAuthStorage();
|
|
},
|
|
|
|
checkAuth: () => {
|
|
const session = sessionStorage.getItem(SESSION_KEY);
|
|
if (session) {
|
|
const user = JSON.parse(session);
|
|
set({ user, isAuthenticated: true });
|
|
void refreshStoredAuthUser(user, !!localStorage.getItem(PERSIST_KEY), set);
|
|
return;
|
|
}
|
|
const persist = localStorage.getItem(PERSIST_KEY);
|
|
if (persist) {
|
|
const user = JSON.parse(persist);
|
|
set({ user, isAuthenticated: true });
|
|
writeAuthStorage(user, true);
|
|
void refreshStoredAuthUser(user, true, set);
|
|
return;
|
|
}
|
|
},
|
|
|
|
refreshUser: async () => {
|
|
const cur = useAuthStore.getState().user;
|
|
if (!cur) return;
|
|
try {
|
|
const memoryMember = useMemberStore.getState().members.find((x) => x.id === cur.id);
|
|
const members = memoryMember ? [memoryMember] : await loadMembersForAuth();
|
|
const member = members.find((x) => x.id === cur.id);
|
|
if (!member) return;
|
|
|
|
const next = toAuthUser(member);
|
|
useAuthStore.setState({ user: next });
|
|
writeAuthStorage(next, !!localStorage.getItem(PERSIST_KEY));
|
|
} catch {}
|
|
},
|
|
}));
|