Files
ERP/frontend/src/store/auth.ts
T
MMD 934b74ebbd Squashed 'pm/' content from commit 91f3271
git-subtree-dir: pm
git-subtree-split: 91f3271d866fa8ee471cfb6793e334b83e6d2b6e
2026-10-07 15:14:20 +03:30

134 lines
3.8 KiB
TypeScript

import { create } from "zustand";
import type { User } from "../lib/api";
const TOKEN_KEY = "pm_token";
const USER_KEY = "pm_user";
/** نشست ERP روی همین دامنه (zustand persist) — منبع تک‌احراز هویت مجموعه */
const ERP_SESSION_KEY = "procurement-auth-v1";
function readErpToken(): string | null {
try {
const raw = localStorage.getItem(ERP_SESSION_KEY);
if (!raw) return null;
const parsed = JSON.parse(raw) as { state?: { token?: string | null } };
return parsed.state?.token ?? null;
} catch {
return null;
}
}
function readToken(): string | null {
try {
const legacy = localStorage.getItem(TOKEN_KEY);
if (legacy) return legacy;
// SSO — توکن Casdoorِ نشست ERP همان نشست PM است
return readErpToken();
} catch {
return null;
}
}
interface AuthState {
token: string | null;
user: User | null;
setAuth: (token: string, user: User) => void;
/** به‌روزرسانی کاربر جاری (بعد از تغییر نقش/دسترسی‌ها یا خواندن /auth/me) */
setUser: (user: User) => void;
clear: () => void;
}
function readErpUser(): User | null {
try {
const raw = localStorage.getItem(ERP_SESSION_KEY);
if (!raw) return null;
const parsed = JSON.parse(raw) as {
state?: {
user?: {
first_name?: string;
last_name?: string;
display_name?: string;
username?: string;
casdoor_id?: string;
role?: string;
role_label?: string;
} | null;
};
};
const eu = parsed.state?.user;
if (!eu) return null;
const role: import("../lib/api").Role =
eu.role === "supervisor" ? "project_supervisor" : "project_manager";
return {
id: eu.username || eu.casdoor_id || "sso",
username: eu.username || eu.casdoor_id || "user",
display_name: eu.display_name || `${eu.first_name || ""} ${eu.last_name || ""}`.trim() || eu.username || "کاربر",
role,
role_label: eu.role_label || (role === "project_supervisor" ? "سرپرست پروژه" : "مدیر پروژه"),
permissions: [],
scope: { all_projects: role === "project_manager", project_count: null },
};
} catch {
return null;
}
}
function readUser(): User | null {
try {
const erpUser = readErpUser();
const raw = localStorage.getItem(USER_KEY);
if (raw) {
const pmUser = JSON.parse(raw) as User;
if (erpUser && (pmUser.username !== erpUser.username || pmUser.role !== erpUser.role)) {
localStorage.removeItem(USER_KEY);
return erpUser;
}
return pmUser;
}
return erpUser;
} catch {
return null;
}
}
function writeUser(user: User | null): void {
try {
if (user) localStorage.setItem(USER_KEY, JSON.stringify(user));
else localStorage.removeItem(USER_KEY);
} catch {
/* حافظه در دسترس نیست */
}
}
/** وضعیت احراز هویت — توکن در localStorage نگه‌داری می‌شود */
export const useAuthStore = create<AuthState>()((set) => ({
token: readToken(),
user: readUser(),
setAuth: (token, user) => {
try {
localStorage.setItem(TOKEN_KEY, token);
} catch {
/* حافظه در دسترس نیست */
}
writeUser(user);
set({ token, user });
},
setUser: (user) => {
writeUser(user);
set({ user });
},
clear: () => {
try {
localStorage.removeItem(TOKEN_KEY);
} catch {
/* حافظه در دسترس نیست */
}
writeUser(null);
set({ token: null, user: null });
},
}));
/** آیا نشست ذخیره‌شده اطلاعات نقش را دارد؟ (نشست‌های پیش از RBAC ندارند) */
export function sessionNeedsRefresh(user: User | null): boolean {
return !user || !user.role || !Array.isArray(user.permissions);
}