42 lines
1.8 KiB
TypeScript
42 lines
1.8 KiB
TypeScript
import { db } from "../db";
|
||
import { roles, roleMembers } from "@shared/schema";
|
||
import { and, eq } from "drizzle-orm";
|
||
|
||
/**
|
||
* Доступ к модулям (GPS / Финансы) по флагам users.<flag> и roles.<flag>.
|
||
*
|
||
* Effective-доступ = users.<flag> OR любая организационная роль пользователя
|
||
* с флагом OR appRole = 'admin'. Оба флага NOT NULL DEFAULT true — при выкатке
|
||
* никто не блокируется; админ снимает галки в матрице на странице /users.
|
||
*
|
||
* Для Финансов флаг работает как ДОПОЛНИТЕЛЬНЫЙ ограничитель поверх
|
||
* существующего app-role права finance.manage (см. requirePermission) —
|
||
* только отбирает доступ, не добавляет.
|
||
*/
|
||
|
||
export type ModuleAccessKey = "gps" | "finance";
|
||
|
||
export interface ModuleAccessUser {
|
||
id: number;
|
||
organizationId: number;
|
||
appRole?: string;
|
||
gpsAccess?: boolean;
|
||
financeAccess?: boolean;
|
||
}
|
||
|
||
export async function hasModuleAccess(user: ModuleAccessUser, module: ModuleAccessKey): Promise<boolean> {
|
||
if (user.appRole === "admin") return true;
|
||
|
||
const userFlag = module === "gps" ? user.gpsAccess : user.financeAccess;
|
||
if (userFlag) return true;
|
||
|
||
// Флаг любой организационной роли пользователя
|
||
const rows = await db
|
||
.select({ gpsAccess: roles.gpsAccess, financeAccess: roles.financeAccess })
|
||
.from(roleMembers)
|
||
.innerJoin(roles, eq(roleMembers.roleId, roles.id))
|
||
.where(and(eq(roleMembers.userId, user.id), eq(roles.organizationId, user.organizationId)));
|
||
|
||
return rows.some((r) => (module === "gps" ? r.gpsAccess : r.financeAccess));
|
||
}
|