mock-auth.ts 6.65 KB
interface MockAuthUserRecord {
  id: number
  username: string
  password: string
  name: string
  email: string
  roles: string[]
  station: string
  avatar: {
    src: string
    alt: string
  }
}

interface MockTokenSession {
  token: string
  username: string
  expiresAt: number
}

export interface PublicAuthUser {
  id: number
  username: string
  name: string
  email: string
  roles: string[]
  station: string
  avatar: {
    src: string
    alt: string
  }
}

const SESSION_TTL_MS = 8 * 60 * 60 * 1000
const DEFAULT_MEMBER_PASSWORD = '123456'

const mockUsers: MockAuthUserRecord[] = [{
  id: 1001,
  username: 'admin',
  password: '123456',
  name: '系统管理员',
  email: 'admin@robot.local',
  roles: ['admin', 'qa_manager'],
  station: '总控台',
  avatar: {
    src: 'https://i.pravatar.cc/128?u=robot-admin',
    alt: '系统管理员'
  }
}, {
  id: 1002,
  username: 'operator',
  password: '123456',
  name: '装配工位操作员',
  email: 'operator@robot.local',
  roles: ['operator'],
  station: '装配工位 A01',
  avatar: {
    src: 'https://i.pravatar.cc/128?u=robot-operator',
    alt: '装配工位操作员'
  }
}]

const sessions = new Map<string, MockTokenSession>()

function clearExpiredSessions() {
  const now = Date.now()

  for (const [token, session] of sessions.entries()) {
    if (session.expiresAt <= now) {
      sessions.delete(token)
    }
  }
}

function toPublicUser(user: MockAuthUserRecord): PublicAuthUser {
  return {
    id: user.id,
    username: user.username,
    name: user.name,
    email: user.email,
    roles: user.roles,
    station: user.station,
    avatar: user.avatar
  }
}

function revokeSessionsByUsername(username: string) {
  for (const [token, session] of sessions.entries()) {
    if (session.username === username) {
      sessions.delete(token)
    }
  }
}

function mapMemberRoleToAuthRoles(role: 'member' | 'admin' | 'customer') {
  if (role === 'admin') {
    return ['admin']
  }

  if (role === 'customer') {
    return ['customer']
  }

  return ['member']
}

function nextMockUserId() {
  return mockUsers.reduce((max, user) => Math.max(max, user.id), 0) + 1
}

interface MemberAuthSyncPayload {
  username: string
  name: string
  email: string
  role: 'member' | 'admin' | 'customer'
  avatar?: {
    src?: string
    alt?: string
  }
}

export function hasMockAuthUserByUsername(username: string, excludeUsername?: string) {
  return mockUsers.some(user => user.username === username && user.username !== excludeUsername)
}

export function hasMockAuthUserByEmail(email: string, excludeUsername?: string) {
  return mockUsers.some(user => user.email === email && user.username !== excludeUsername)
}

export function createMockAuthUserFromMember(payload: MemberAuthSyncPayload) {
  if (hasMockAuthUserByUsername(payload.username)) {
    return {
      success: false as const,
      errorCode: 'USERNAME_EXISTS' as const,
      message: '用户名已存在。'
    }
  }

  if (hasMockAuthUserByEmail(payload.email)) {
    return {
      success: false as const,
      errorCode: 'EMAIL_EXISTS' as const,
      message: '邮箱已存在。'
    }
  }

  mockUsers.push({
    id: nextMockUserId(),
    username: payload.username,
    password: DEFAULT_MEMBER_PASSWORD,
    name: payload.name,
    email: payload.email,
    roles: mapMemberRoleToAuthRoles(payload.role),
    station: '成员中心',
    avatar: {
      src: payload.avatar?.src ?? `https://i.pravatar.cc/128?u=${encodeURIComponent(payload.username)}`,
      alt: payload.avatar?.alt ?? payload.name
    }
  })

  return {
    success: true as const,
    errorCode: null,
    message: '登录用户已创建。'
  }
}

export function updateMockAuthUserFromMember(username: string, payload: MemberAuthSyncPayload) {
  const index = mockUsers.findIndex(user => user.username === username)
  if (index === -1) {
    return {
      success: false as const,
      errorCode: 'AUTH_USER_NOT_FOUND' as const,
      message: '登录用户不存在。'
    }
  }

  if (hasMockAuthUserByUsername(payload.username, username)) {
    return {
      success: false as const,
      errorCode: 'USERNAME_EXISTS' as const,
      message: '用户名已存在。'
    }
  }

  if (hasMockAuthUserByEmail(payload.email, username)) {
    return {
      success: false as const,
      errorCode: 'EMAIL_EXISTS' as const,
      message: '邮箱已存在。'
    }
  }

  const target = mockUsers[index]!
  mockUsers[index] = {
    ...target,
    username: payload.username,
    name: payload.name,
    email: payload.email,
    roles: mapMemberRoleToAuthRoles(payload.role),
    avatar: {
      src: payload.avatar?.src ?? target.avatar.src,
      alt: payload.avatar?.alt ?? payload.name
    }
  }

  if (payload.username !== username) {
    revokeSessionsByUsername(username)
  }

  return {
    success: true as const,
    errorCode: null,
    message: '登录用户已更新。'
  }
}

export function removeMockAuthUser(username: string) {
  const index = mockUsers.findIndex(user => user.username === username)
  if (index === -1) {
    return {
      success: false as const,
      errorCode: 'AUTH_USER_NOT_FOUND' as const,
      message: '登录用户不存在。'
    }
  }

  mockUsers.splice(index, 1)
  revokeSessionsByUsername(username)

  return {
    success: true as const,
    errorCode: null,
    message: '登录用户已移除。'
  }
}

export function authenticateMockUser(username: string, password: string) {
  return mockUsers.find(user => user.username === username && user.password === password) ?? null
}

export function createMockSession(username: string) {
  clearExpiredSessions()

  const token = `mock-${username}-${Math.random().toString(36).slice(2, 10)}-${Date.now()}`
  const expiresAt = Date.now() + SESSION_TTL_MS

  sessions.set(token, {
    token,
    username,
    expiresAt
  })

  return {
    token,
    expiresAt
  }
}

export function getMockUserByToken(token: string) {
  clearExpiredSessions()

  const session = sessions.get(token)
  if (!session) {
    return {
      success: false as const,
      errorCode: 'TOKEN_INVALID' as const
    }
  }

  if (session.expiresAt <= Date.now()) {
    sessions.delete(token)
    return {
      success: false as const,
      errorCode: 'TOKEN_EXPIRED' as const
    }
  }

  const user = mockUsers.find(item => item.username === session.username)
  if (!user) {
    sessions.delete(token)
    return {
      success: false as const,
      errorCode: 'TOKEN_INVALID' as const
    }
  }

  return {
    success: true as const,
    user: toPublicUser(user),
    expiresAt: session.expiresAt
  }
}

export function getMockUsersForHint() {
  return mockUsers.map(user => ({
    username: user.username,
    password: user.password,
    name: user.name
  }))
}

export { toPublicUser }