api.js
2.22 KB
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
/**
* voko-server 网页接口的唯一调用点。access token 存在 sessionStorage(Alpha 内部使用)。
*/
import { reactive } from 'vue'
const TOKEN_KEY = 'voko.access_token'
/** 登录状态(给顶栏用)。 */
export const session = reactive({ loggedIn: sessionStorage.getItem(TOKEN_KEY) !== null })
export function getToken() {
return sessionStorage.getItem(TOKEN_KEY)
}
/** @param {string|null} token */
export function setToken(token) {
if (token === null) sessionStorage.removeItem(TOKEN_KEY)
else sessionStorage.setItem(TOKEN_KEY, token)
session.loggedIn = token !== null
}
/**
* @param {string} method
* @param {string} path
* @param {unknown} [body]
* @param {string} [idempotencyKey] 写接口的幂等键:同一次提交重试不会重复扣积分
*/
export async function api(method, path, body, idempotencyKey) {
const token = getToken()
const response = await fetch(path, {
method,
headers: {
...(body === undefined ? {} : { 'content-type': 'application/json' }),
...(token === null ? {} : { authorization: `Bearer ${token}` }),
...(idempotencyKey === undefined ? {} : { 'idempotency-key': idempotencyKey }),
},
body: body === undefined ? undefined : JSON.stringify(body),
})
// 登录已过期:清掉 token 回登录页,登录后回到当前页(否则页面只会空白)。
if (response.status === 401 && token !== null) {
setToken(null)
window.location.assign(`/login?next=${encodeURIComponent(window.location.pathname)}`)
}
const json = await response.json()
if (!response.ok) throw new Error(json.error.message)
return json
}
/**
* 带登录态下载一个文件并交给浏览器保存(交付文件需要鉴权,不能直接用链接)。
* @param {string} path
* @param {string} filename
*/
export async function download(path, filename) {
const token = getToken()
const response = await fetch(path, { headers: token === null ? {} : { authorization: `Bearer ${token}` } })
if (!response.ok) throw new Error((await response.json()).error.message)
const url = URL.createObjectURL(await response.blob())
const link = document.createElement('a')
link.href = url
link.download = filename
link.click()
URL.revokeObjectURL(url)
}