permission.ts 4.66 KB
export const LOGIN_PATH = '/login'
export const FORBIDDEN_PATH = '/forbidden'

export const APP_PAGE_PATHS = [
  '/',
  '/m/workbench',
  '/m/production/sn-query',
  '/work-orders',
  '/sn-management',
  '/operations',
  '/inbox',
  '/customers',
  '/base-info/device-types',
  '/settings',
  '/settings/members',
  '/settings/notifications',
  '/settings/security'
] as const

export const BUTTON_PERMISSION_KEYS = [
  'work-orders.create',
  'work-orders.edit',
  'work-orders.dispatch',
  'work-orders.pause',
  'work-orders.resume',
  'work-orders.close',
  'sn.import',
  'sn.freeze',
  'sn.unfreeze',
  'sn.scrap',
  'operations.skip',
  'members.invite',
  'members.edit',
  'members.role.update',
  'members.remove',
  'customers.create',
  'customers.delete',
  'device-types.create',
  'device-types.update',
  'device-types.delete'
] as const

export type AppPagePath = (typeof APP_PAGE_PATHS)[number]
export type ButtonPermissionKey = (typeof BUTTON_PERMISSION_KEYS)[number]

export type RolePermissionMatrix = Record<string, Partial<Record<AppPagePath, ButtonPermissionKey[]>>>

const APP_PAGE_PATH_SET = new Set<string>(APP_PAGE_PATHS)
const PUBLIC_PATH_SET = new Set<string>([LOGIN_PATH, FORBIDDEN_PATH])

const MANAGER_WORK_ORDER_BUTTONS: ButtonPermissionKey[] = [
  'work-orders.create',
  'work-orders.edit',
  'work-orders.dispatch',
  'work-orders.pause',
  'work-orders.resume',
  'work-orders.close'
]

const MANAGER_SN_BUTTONS: ButtonPermissionKey[] = [
  'sn.import',
  'sn.freeze',
  'sn.unfreeze',
  'sn.scrap'
]

const MANAGER_OPERATION_BUTTONS: ButtonPermissionKey[] = [
  'operations.skip'
]

const ADMIN_PAGE_PERMISSIONS = APP_PAGE_PATHS.reduce<Partial<Record<AppPagePath, ButtonPermissionKey[]>>>((permissions, pagePath) => {
  permissions[pagePath] = [...BUTTON_PERMISSION_KEYS]
  return permissions
}, {})

const ROLE_PERMISSION_MATRIX: RolePermissionMatrix = {
  // admin:支持所有页面和所有按钮
  admin: ADMIN_PAGE_PERMISSIONS,
  // 管理员:仅支持工单管理、SN 管理、工序管理页面及对应按钮
  qa_manager: {
    '/work-orders': MANAGER_WORK_ORDER_BUTTONS,
    '/sn-management': MANAGER_SN_BUTTONS,
    '/operations': MANAGER_OPERATION_BUTTONS
  },
  // 操作员:只能访问移动端页面
  operator: {
    '/m/workbench': [],
    '/m/production/sn-query': []
  }
}

function normalizePath(path: string) {
  const [pathname] = path.split('?')
  const purePath = pathname ? pathname.split('#')[0] : '/'
  const trimmed = (purePath ?? '/').trim() || '/'

  if (trimmed.length > 1 && trimmed.endsWith('/')) {
    return trimmed.slice(0, -1)
  }

  return trimmed
}

export function normalizePagePath(path: string) {
  return normalizePath(path)
}

export function toPagePath(path: string): AppPagePath | null {
  const normalized = normalizePath(path)

  if (!APP_PAGE_PATH_SET.has(normalized)) {
    return null
  }

  return normalized as AppPagePath
}

function getRolePagePermissions(role: string) {
  return ROLE_PERMISSION_MATRIX[role] ?? null
}

export function getAccessiblePagesForRoles(roles: string[]): Set<AppPagePath> {
  const pages = new Set<AppPagePath>()

  for (const role of roles) {
    const rolePermissions = getRolePagePermissions(role)
    if (!rolePermissions) {
      continue
    }

    for (const rawPagePath of Object.keys(rolePermissions)) {
      const pagePath = toPagePath(rawPagePath)
      if (pagePath) {
        pages.add(pagePath)
      }
    }
  }

  return pages
}

export function getAllowedButtonsForPage(roles: string[], pagePath: AppPagePath): Set<ButtonPermissionKey> {
  const buttons = new Set<ButtonPermissionKey>()

  for (const role of roles) {
    const rolePermissions = getRolePagePermissions(role)
    if (!rolePermissions || !rolePermissions[pagePath]) {
      continue
    }

    for (const button of rolePermissions[pagePath] ?? []) {
      buttons.add(button)
    }
  }

  return buttons
}

export function isPublicPath(path: string) {
  return PUBLIC_PATH_SET.has(normalizePath(path))
}

export function isProtectedPage(path: string) {
  return toPagePath(path) !== null
}

export function canAccessPageByRoles(roles: string[], pagePath: string) {
  const pageKey = toPagePath(pagePath)
  if (!pageKey) {
    return true
  }

  const accessiblePages = getAccessiblePagesForRoles(roles)
  return accessiblePages.has(pageKey)
}

export function canUseButtonByRoles(roles: string[], pagePath: string, buttonKey: ButtonPermissionKey) {
  const pageKey = toPagePath(pagePath)
  if (!pageKey) {
    return false
  }

  const accessiblePages = getAccessiblePagesForRoles(roles)
  if (!accessiblePages.has(pageKey)) {
    return false
  }

  const allowedButtons = getAllowedButtonsForPage(roles, pageKey)

  return allowedButtons.has(buttonKey)
}