yeg
2026-08-26 9f48020dfd3e29d1c0049a3da9df3203c40bc264
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
export interface ApiResponse {
  code: number
  msg: string
  [key: string]: unknown
}
 
export class ApiError extends Error {
  readonly code: number
 
  constructor(message: string, code = 500) {
    super(message)
    this.name = 'ApiError'
    this.code = code
  }
}
 
const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || '/dev-api'
const AUTH_TOKEN_KEY = 'battery_detection_auth_token'
const AUTH_USER_KEY = 'battery_detection_auth_user'
 
type UnauthorizedHandler = () => void | Promise<void>
 
let unauthorizedHandler: UnauthorizedHandler | undefined
let unauthorizedHandling: Promise<void> | null = null
 
export function setUnauthorizedHandler(handler: UnauthorizedHandler) {
  unauthorizedHandler = handler
}
 
function clearInvalidSession() {
  localStorage.removeItem(AUTH_TOKEN_KEY)
  localStorage.removeItem(AUTH_USER_KEY)
}
 
async function handleUnauthorized() {
  clearInvalidSession()
  if (!unauthorizedHandler) return
 
  if (!unauthorizedHandling) {
    unauthorizedHandling = Promise.resolve(unauthorizedHandler())
      .finally(() => {
        unauthorizedHandling = null
      })
  }
 
  await unauthorizedHandling
}
 
export async function request<T extends ApiResponse>(path: string, init: RequestInit = {}): Promise<T> {
  const headers = new Headers(init.headers)
  const token = localStorage.getItem(AUTH_TOKEN_KEY)
 
  if (init.body && !headers.has('Content-Type')) {
    headers.set('Content-Type', 'application/json')
  }
  if (token) {
    headers.set('Authorization', `Bearer ${token}`)
  }
 
  let response: Response
  try {
    response = await fetch(`${API_BASE_URL}${path}`, { ...init, headers })
  } catch {
    throw new ApiError('无法连接服务器,请检查后端服务是否已启动', 0)
  }
 
  let result: T
  try {
    result = await response.json() as T
  } catch {
    throw new ApiError(`服务器响应异常(HTTP ${response.status})`, response.status)
  }
 
  if (response.status === 401 || result.code === 401) {
    await handleUnauthorized()
  }
 
  if (!response.ok || result.code !== 200) {
    throw new ApiError(result.msg || `请求失败(HTTP ${response.status})`, result.code || response.status)
  }
 
  return result
}