mirror of https://github.com/veypi/OneAuth.git
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
130 lines
3.1 KiB
JavaScript
130 lines
3.1 KiB
JavaScript
|
7 months ago
|
/*
|
||
|
|
* api.js
|
||
|
|
* Copyright (C) 2024 veypi <i@veypi.com>
|
||
|
|
*
|
||
|
|
* Distributed under terms of the MIT license.
|
||
|
|
*/
|
||
|
|
|
||
|
|
class API {
|
||
|
|
prefix = ''
|
||
|
|
fetch = window.fetch
|
||
|
|
constructor(prefix, fetch) {
|
||
|
|
if (fetch) {
|
||
|
|
this.fetch = fetch
|
||
|
|
}
|
||
|
|
if (typeof prefix === 'string') {
|
||
|
|
this.prefix = prefix
|
||
|
|
}
|
||
|
|
if (!this.prefix.endsWith('/')) {
|
||
|
|
this.prefix += '/'
|
||
|
|
}
|
||
|
|
}
|
||
|
|
wrapFetch(fetch) {
|
||
|
|
this.fetch = fetch
|
||
|
|
}
|
||
|
|
wrapUrl(url) {
|
||
|
|
if (url.startsWith('http')) {
|
||
|
|
return url
|
||
|
|
} else if (url.startsWith('/')) {
|
||
|
|
return this.prefix + url.slice(1)
|
||
|
|
} else {
|
||
|
|
return this.prefix + url
|
||
|
|
}
|
||
|
|
}
|
||
|
|
async Fetch(url, method, opts, body) {
|
||
|
|
url = this.wrapUrl(url)
|
||
|
|
if (!opts) {
|
||
|
|
opts = {}
|
||
|
|
}
|
||
|
|
if (!opts.headers) {
|
||
|
|
opts.headers = {}
|
||
|
|
}
|
||
|
|
opts.method = method
|
||
|
|
opts.body = JSON.stringify(body)
|
||
|
|
if (!opts.headers['Content-Type']) {
|
||
|
|
opts.headers['Content-Type'] = 'application/json'
|
||
|
|
}
|
||
|
|
if (opts.query && Object.keys(opts.query).length && url.indexOf('?') === -1) {
|
||
|
|
url += '?' + new URLSearchParams(opts.query).toString()
|
||
|
|
}
|
||
|
|
return this.fetch.bind(window)(url, opts).then((response) => {
|
||
|
|
if (!response.ok) {
|
||
|
|
throw response
|
||
|
|
}
|
||
|
|
let contentType = response.headers.get('content-type')
|
||
|
|
if (contentType && contentType.includes('application/json')) {
|
||
|
|
return response.json()
|
||
|
|
}
|
||
|
|
return response.text()
|
||
|
|
}).then(data => {
|
||
|
|
if (typeof data === 'string') {
|
||
|
|
return data
|
||
|
|
}
|
||
|
|
if (data.code === 0) {
|
||
|
|
return data.data
|
||
|
|
} else if (data.code >= 0) {
|
||
|
|
throw new Error(JSON.stringify(data))
|
||
|
|
} else {
|
||
|
|
return data
|
||
|
|
}
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
async Get(url, opts) {
|
||
|
|
return this.Fetch(url, 'GET', opts)
|
||
|
|
}
|
||
|
|
|
||
|
|
async Post(url, data, opts) {
|
||
|
|
return this.Fetch(url, 'POST', opts, data)
|
||
|
|
}
|
||
|
|
|
||
|
|
async Put(url, data, opts) {
|
||
|
|
return this.Fetch(url, 'PUT', opts, data)
|
||
|
|
}
|
||
|
|
|
||
|
|
async Delete(url, opts) {
|
||
|
|
return this.Fetch(url, 'DELETE', opts)
|
||
|
|
}
|
||
|
|
|
||
|
|
async Patch(url, data, opts) {
|
||
|
|
return this.Fetch(url, 'PATCH', opts, data)
|
||
|
|
}
|
||
|
|
|
||
|
|
async SSE(url, opts, cb) {
|
||
|
|
url = this.wrapUrl(url)
|
||
|
|
let response = await this.fetch.bind(window)(url, opts)
|
||
|
|
const reader = response.body.getReader();
|
||
|
|
const decoder = new TextDecoder('utf-8');
|
||
|
|
let count = 0
|
||
|
|
let partialLine = ''
|
||
|
|
try {
|
||
|
|
while (true) {
|
||
|
|
const { done, value } = await reader.read();
|
||
|
|
if (done) break; // 结束时退出循环
|
||
|
|
// 将Uint8Array转换为字符串
|
||
|
|
const chunk = decoder.decode(value, { stream: true });
|
||
|
|
// 处理可能跨多个块的消息
|
||
|
|
const lines = (partialLine + chunk).split('\n');
|
||
|
|
partialLine = lines.pop(); // 最后一行可能是未完成的消息
|
||
|
|
for (const line of lines) {
|
||
|
|
cb(line, count++)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
// 处理最后一个不完整的行
|
||
|
|
if (partialLine) {
|
||
|
|
cb(partialLine, count++)
|
||
|
|
}
|
||
|
|
} catch (error) {
|
||
|
|
console.error(error);
|
||
|
|
} finally {
|
||
|
|
reader.releaseLock();
|
||
|
|
cb('', -1)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
New(prefix) {
|
||
|
|
return new API(prefix, this.fetch)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
export default new API()
|