File size: 3,160 Bytes
00a912e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
84
85
86
87
88
89
90
91
92
93
import { SERVER_URL } from '@/lib/config'
import { fetchCurrentProfile } from '@/lib/profileApi'
import { storage } from '@/lib/storage'
import type { AccountProfile } from '@/stores/accountStore'
import type { TypedSocket } from '@/lib/socket'

export async function requestJson<T>(path: string, init?: RequestInit): Promise<T> {
  const response = await fetch(`${SERVER_URL}${path}`, {
    ...init,
    credentials: 'include',
    headers: { 'Content-Type': 'application/json', ...init?.headers },
  })
  if (!response.ok) {
    const body = (await response.json().catch(() => null)) as { error?: string } | null
    throw new Error(body?.error ?? `Request failed: ${response.status}`)
  }
  if (response.status === 204) return undefined as T
  return (await response.json()) as T
}

function reconnectSocket(socket: TypedSocket): Promise<void> {
  return new Promise((resolve) => {
    let finished = false
    const finish = () => {
      if (finished) return
      finished = true
      socket.off('connect', finish)
      window.clearTimeout(timeout)
      resolve()
    }
    const timeout = window.setTimeout(finish, 3_000)
    socket.on('connect', finish)
    if (socket.connected) socket.disconnect()
    socket.connect()
  })
}

function clearIdentityBoundBrowserState(): void {
  storage.setAuthCookies([])
  storage.clearRejoinToken()
}

export async function setInitialPassword(password: string): Promise<AccountProfile> {
  await requestJson<{ accountId: string }>('/api/auth/me/password', {
    method: 'POST',
    body: JSON.stringify({ password }),
  })
  const profile = await fetchCurrentProfile()
  if (!profile) throw new Error('请先设置昵称')
  return profile
}

export async function updateAccountId(
  socket: TypedSocket,
  accountId: string,
  currentPassword?: string,
): Promise<AccountProfile> {
  const result = await requestJson<AccountProfile>('/api/auth/me/account-id', {
    method: 'PATCH',
    body: JSON.stringify({ accountId: accountId.trim().toLowerCase(), currentPassword }),
  })
  storage.setUserId(result.id)
  storage.clearRejoinToken()
  const profile = await fetchCurrentProfile()
  if (!profile) throw new Error('账号资料恢复失败')
  await reconnectSocket(socket)
  return profile
}

export async function loginIdentity(socket: TypedSocket, accountId: string, password: string): Promise<AccountProfile> {
  const result = await requestJson<{ userId: string; expiresAt: number }>('/api/auth/identity/recover', {
    method: 'POST',
    body: JSON.stringify({ accountId: accountId.trim(), password }),
  })
  clearIdentityBoundBrowserState()
  storage.setUserId(result.userId)
  const profile = await fetchCurrentProfile()
  if (!profile) throw new Error('账号资料恢复失败')
  await reconnectSocket(socket)
  return profile
}

export async function logoutIdentity(socket: TypedSocket): Promise<AccountProfile | null> {
  await requestJson<{ userId: string; expiresAt: number }>('/api/auth/identity/logout', {
    method: 'POST',
  })
  clearIdentityBoundBrowserState()
  storage.clearNickname()
  const profile = await fetchCurrentProfile()
  await reconnectSocket(socket)
  return profile
}