fix(账号): 固化超级管理员身份
This commit is contained in:
@@ -3,17 +3,10 @@
|
||||
import { create } from 'zustand';
|
||||
import { loadServerData } from '@/lib/server-data';
|
||||
import type { Member } from '@/lib/members';
|
||||
import { SYSTEM_ADMIN_MEMBER, ensureSystemAdminMember } from '@/lib/member-system';
|
||||
import { resolveAuthUserFromMembers, toAuthUser, type AuthUser } from '@/lib/auth-user';
|
||||
import { useMemberStore } from './useMemberStore';
|
||||
|
||||
interface AuthUser {
|
||||
id: string;
|
||||
name: string;
|
||||
roleId: string;
|
||||
departmentId: string;
|
||||
phone: string;
|
||||
email: string;
|
||||
}
|
||||
|
||||
interface AuthState {
|
||||
user: AuthUser | null;
|
||||
isAuthenticated: boolean;
|
||||
@@ -30,38 +23,42 @@ interface StoredMembersData {
|
||||
const SESSION_KEY = 'ftb_auth_session';
|
||||
const PERSIST_KEY = 'ftb_auth_persist';
|
||||
|
||||
const FALLBACK_MEMBERS: Member[] = [
|
||||
{
|
||||
id: 'm-8',
|
||||
name: '\u8d85\u7ea7\u7ba1\u7406\u5458',
|
||||
departmentId: 'dept-1',
|
||||
roleId: 'role-admin',
|
||||
phone: '13200132008',
|
||||
email: 'chenshi@company.com',
|
||||
password: 'Ftb@2024',
|
||||
createdAt: '2024-01-01',
|
||||
},
|
||||
];
|
||||
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 stored = await loadServerData<StoredMembersData>('members');
|
||||
if (stored?.members?.length) return stored.members;
|
||||
if (stored?.members?.length) return ensureSystemAdminMember(stored.members).members;
|
||||
} catch {}
|
||||
|
||||
const inMemoryMembers = useMemberStore.getState().members;
|
||||
return inMemoryMembers.length > 0 ? inMemoryMembers : FALLBACK_MEMBERS;
|
||||
return inMemoryMembers.length > 0 ? ensureSystemAdminMember(inMemoryMembers).members : FALLBACK_MEMBERS;
|
||||
}
|
||||
|
||||
function toAuthUser(member: Member): AuthUser {
|
||||
return {
|
||||
id: member.id,
|
||||
name: member.name,
|
||||
roleId: member.roleId,
|
||||
departmentId: member.departmentId,
|
||||
phone: member.phone,
|
||||
email: member.email,
|
||||
};
|
||||
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) => ({
|
||||
@@ -75,30 +72,29 @@ export const useAuthStore = create<AuthState>((set) => ({
|
||||
|
||||
const user = toAuthUser(member);
|
||||
set({ user, isAuthenticated: true });
|
||||
sessionStorage.setItem(SESSION_KEY, JSON.stringify(user));
|
||||
if (remember) {
|
||||
localStorage.setItem(PERSIST_KEY, JSON.stringify(user));
|
||||
}
|
||||
writeAuthStorage(user, remember);
|
||||
return true;
|
||||
},
|
||||
|
||||
logout: () => {
|
||||
set({ user: null, isAuthenticated: false });
|
||||
sessionStorage.removeItem(SESSION_KEY);
|
||||
localStorage.removeItem(PERSIST_KEY);
|
||||
clearAuthStorage();
|
||||
},
|
||||
|
||||
checkAuth: () => {
|
||||
const session = sessionStorage.getItem(SESSION_KEY);
|
||||
if (session) {
|
||||
set({ user: JSON.parse(session), isAuthenticated: true });
|
||||
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 });
|
||||
sessionStorage.setItem(SESSION_KEY, JSON.stringify(user));
|
||||
writeAuthStorage(user, true);
|
||||
void refreshStoredAuthUser(user, true, set);
|
||||
return;
|
||||
}
|
||||
},
|
||||
@@ -114,10 +110,7 @@ export const useAuthStore = create<AuthState>((set) => ({
|
||||
|
||||
const next = toAuthUser(member);
|
||||
useAuthStore.setState({ user: next });
|
||||
sessionStorage.setItem(SESSION_KEY, JSON.stringify(next));
|
||||
if (localStorage.getItem(PERSIST_KEY)) {
|
||||
localStorage.setItem(PERSIST_KEY, JSON.stringify(next));
|
||||
}
|
||||
writeAuthStorage(next, !!localStorage.getItem(PERSIST_KEY));
|
||||
} catch {}
|
||||
},
|
||||
}));
|
||||
|
||||
@@ -2,6 +2,13 @@
|
||||
import { create } from 'zustand';
|
||||
import type { Department, Member, RoleItem, PasswordRule } from '@/lib/members';
|
||||
import { DEFAULT_PASSWORD_RULE, generatePassword } from '@/lib/members';
|
||||
import {
|
||||
SYSTEM_ADMIN_MEMBER,
|
||||
SYSTEM_ADMIN_ROLE_ID,
|
||||
canDeleteSystemMember,
|
||||
ensureSystemAdminMember,
|
||||
sanitizeSystemAdminPatch,
|
||||
} from '@/lib/member-system';
|
||||
import { DEFAULT_ROLE_PERMISSIONS } from '@/lib/permissions';
|
||||
import { loadServerData, saveServerData } from '@/lib/server-data';
|
||||
|
||||
@@ -24,7 +31,7 @@ const PRESET_ROLES: RoleItem[] = [
|
||||
];
|
||||
|
||||
const MOCK_MEMBERS: Member[] = [
|
||||
{ id: 'm-8', name: '超级管理员', departmentId: 'dept-1', roleId: 'role-admin', phone: '13200132008', email: 'chenshi@company.com', password: 'Ftb@2024', createdAt: '2024-01-01' },
|
||||
SYSTEM_ADMIN_MEMBER,
|
||||
];
|
||||
|
||||
interface MemberState {
|
||||
@@ -74,7 +81,22 @@ export const useMemberStore = create<MemberState>((set, get) => ({
|
||||
}
|
||||
return r;
|
||||
});
|
||||
set({ departments: cached.departments, members: cached.members, roles: migratedRoles, passwordRule: cached.passwordRule || DEFAULT_PASSWORD_RULE });
|
||||
const adminRole = PRESET_ROLES.find((r) => r.id === SYSTEM_ADMIN_ROLE_ID)!;
|
||||
const roles = migratedRoles.some((r) => r.id === SYSTEM_ADMIN_ROLE_ID)
|
||||
? migratedRoles.map((r) => r.id === SYSTEM_ADMIN_ROLE_ID ? { ...adminRole, ...r, isSystem: true, permissions: DEFAULT_ROLE_PERMISSIONS[SYSTEM_ADMIN_ROLE_ID] } : r)
|
||||
: [adminRole, ...migratedRoles];
|
||||
const rolesChanged =
|
||||
roles.length !== migratedRoles.length ||
|
||||
roles.some((role, index) => JSON.stringify(role) !== JSON.stringify(migratedRoles[index]));
|
||||
const ensured = ensureSystemAdminMember(cached.members);
|
||||
const next = {
|
||||
departments: cached.departments,
|
||||
members: ensured.members,
|
||||
roles,
|
||||
passwordRule: cached.passwordRule || DEFAULT_PASSWORD_RULE,
|
||||
};
|
||||
set(next);
|
||||
if (ensured.changed || rolesChanged) saveStored(next);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -107,11 +129,14 @@ export const useMemberStore = create<MemberState>((set, get) => ({
|
||||
saveStored({ departments: get().departments, members, roles: get().roles, passwordRule: get().passwordRule });
|
||||
},
|
||||
updateMember: (id, data) => {
|
||||
const members = get().members.map((m) => m.id === id ? { ...m, ...data } : m);
|
||||
set({ members });
|
||||
saveStored({ departments: get().departments, members, roles: get().roles, passwordRule: get().passwordRule });
|
||||
const safeData = sanitizeSystemAdminPatch(id, data);
|
||||
const updatedMembers = get().members.map((m) => m.id === id ? { ...m, ...safeData } : m);
|
||||
const ensured = ensureSystemAdminMember(updatedMembers);
|
||||
set({ members: ensured.members });
|
||||
saveStored({ departments: get().departments, members: ensured.members, roles: get().roles, passwordRule: get().passwordRule });
|
||||
},
|
||||
deleteMember: (id) => {
|
||||
if (!canDeleteSystemMember(id)) return;
|
||||
const members = get().members.filter((m) => m.id !== id);
|
||||
set({ members });
|
||||
saveStored({ departments: get().departments, members, roles: get().roles, passwordRule: get().passwordRule });
|
||||
@@ -142,6 +167,7 @@ export const useMemberStore = create<MemberState>((set, get) => ({
|
||||
saveStored({ departments: get().departments, members: get().members, roles, passwordRule: get().passwordRule });
|
||||
},
|
||||
deleteRole: (id) => {
|
||||
if (id === SYSTEM_ADMIN_ROLE_ID) return;
|
||||
const roles = get().roles.filter((r) => r.id !== id);
|
||||
set({ roles });
|
||||
saveStored({ departments: get().departments, members: get().members, roles, passwordRule: get().passwordRule });
|
||||
|
||||
Reference in New Issue
Block a user