更新构建流程,集成 undici 库并优化请求处理
This commit is contained in:
@@ -94,12 +94,46 @@ jobs:
|
||||
name: lx-music-desktop-win_arm64-green
|
||||
path: build/*win_arm64-green.7z
|
||||
|
||||
- name: Generate file MD5
|
||||
run: |
|
||||
cd build
|
||||
Get-FileHash *.exe,*.7z -Algorithm MD5 | Format-List
|
||||
|
||||
- name: Install old electron
|
||||
run: npm install electron@22
|
||||
Windows_7:
|
||||
name: Windows_7
|
||||
runs-on: windows-latest
|
||||
env:
|
||||
BUILD_WIN7: true
|
||||
# needs: CheckCode
|
||||
steps:
|
||||
- name: Check out git repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install python setuptools
|
||||
run: pip.exe install setuptools
|
||||
- name: Get npm cache directory
|
||||
shell: pwsh
|
||||
run: echo "NPM_CACHE=$(npm config get cache)" >> $env:GITHUB_ENV
|
||||
|
||||
- name: Setup Node Env
|
||||
env:
|
||||
NPM_CACHE: ${{ env.NPM_CACHE }}
|
||||
uses: ./.github/actions/setup
|
||||
|
||||
- name: Prepare win7 undici env
|
||||
run: |
|
||||
npm install undici@5
|
||||
Set-Content -Path .\src\common\utils\request.ts -Value "export * from './request_node16'"
|
||||
|
||||
- name: Build src code
|
||||
env:
|
||||
BUILD_WIN7: true
|
||||
run: |
|
||||
git status --porcelain
|
||||
npm run build
|
||||
|
||||
- name: Prepare win7 electron env
|
||||
run: |
|
||||
npm install electron@22
|
||||
pip.exe install setuptools
|
||||
|
||||
- name: Build Package win7 Setup x64
|
||||
run: npm run pack:win7:setup:x64
|
||||
|
||||
@@ -98,7 +98,14 @@ jobs:
|
||||
NPM_CACHE: ${{ env.NPM_CACHE }}
|
||||
uses: ./.github/actions/setup
|
||||
|
||||
- name: Prepare win7 undici env
|
||||
run: |
|
||||
npm install undici@5
|
||||
Set-Content -Path .\src\common\utils\request.ts -Value "export * from './request_node16'"
|
||||
|
||||
- name: Build src code
|
||||
env:
|
||||
BUILD_WIN7: true
|
||||
run: |
|
||||
git status --porcelain
|
||||
npm run build
|
||||
|
||||
@@ -19,7 +19,13 @@ const gitInfo = {
|
||||
}
|
||||
|
||||
try {
|
||||
if (!execSync('git status --porcelain').toString().trim()) {
|
||||
let isClean = !execSync('git status --porcelain').toString().trim()
|
||||
if (process.env.BUILD_WIN7) {
|
||||
console.warn('BUILD_WIN7 is set, skipping git status check.')
|
||||
console.log('Workspace status:', execSync('git status --porcelain').toString().trim())
|
||||
isClean = true
|
||||
}
|
||||
if (isClean) {
|
||||
gitInfo.commit_id = execSync('git log -1 --pretty=format:"%H"').toString().trim()
|
||||
gitInfo.commit_date = execSync('git log -1 --pretty=format:"%ad" --date=iso-strict').toString().trim()
|
||||
} else if (process.env.IS_CI) {
|
||||
|
||||
Generated
+13
-3
@@ -27,6 +27,7 @@
|
||||
"node-id3": "^0.2.9",
|
||||
"sortablejs": "^1.15.6",
|
||||
"tunnel": "^0.0.6",
|
||||
"undici": "^7.16.0",
|
||||
"utf-8-validate": "^6.0.5",
|
||||
"vue": "~3.3.13",
|
||||
"vue-router": "^4.5.1",
|
||||
@@ -7297,9 +7298,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/electron/node_modules/@types/node": {
|
||||
"version": "22.17.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.17.0.tgz",
|
||||
"integrity": "sha512-bbAKTCqX5aNVryi7qXVMi+OkB3w/OyblodicMbvE38blyAz7GxXf6XYhklokijuPwwVg9sDLKRxt0ZHXQwZVfQ==",
|
||||
"version": "22.19.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.2.tgz",
|
||||
"integrity": "sha512-LPM2G3Syo1GLzXLGJAKdqoU35XvrWzGJ21/7sgZTUpbkBaOasTj8tjwn6w+hCkqaa1TfJ/w67rJSwYItlJ2mYw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -17208,6 +17209,15 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/undici": {
|
||||
"version": "7.16.0",
|
||||
"resolved": "https://registry.npmjs.org/undici/-/undici-7.16.0.tgz",
|
||||
"integrity": "sha512-QEg3HPMll0o3t2ourKwOeUAZ159Kn9mx5pnzHRQO8+Wixmh88YdZRiIwat0iNzNNXn0yoEtXJqFpyW7eM8BV7g==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=20.18.1"
|
||||
}
|
||||
},
|
||||
"node_modules/undici-types": {
|
||||
"version": "6.21.0",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
|
||||
|
||||
@@ -193,6 +193,7 @@
|
||||
"node-id3": "^0.2.9",
|
||||
"sortablejs": "^1.15.6",
|
||||
"tunnel": "^0.0.6",
|
||||
"undici": "^7.16.0",
|
||||
"utf-8-validate": "^6.0.5",
|
||||
"vue": "~3.3.13",
|
||||
"vue-router": "^4.5.1",
|
||||
|
||||
@@ -0,0 +1,274 @@
|
||||
import qs from 'node:querystring'
|
||||
import {
|
||||
FormData,
|
||||
getGlobalDispatcher,
|
||||
interceptors,
|
||||
request as nodeRrequest,
|
||||
ProxyAgent,
|
||||
setGlobalDispatcher,
|
||||
type Dispatcher,
|
||||
} from 'undici'
|
||||
|
||||
const defaultOptions: Options = {
|
||||
timeout: 15000,
|
||||
headers: {
|
||||
'User-Agent':
|
||||
'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36',
|
||||
},
|
||||
maxRedirect: 5,
|
||||
} as const
|
||||
const redirectDispatcher = interceptors.redirect({ maxRedirections: defaultOptions.maxRedirect })
|
||||
const dispatchers = [
|
||||
interceptors.retry({
|
||||
maxRetries: 3,
|
||||
minTimeout: 1000,
|
||||
maxTimeout: 10000,
|
||||
timeoutFactor: 2,
|
||||
retryAfter: true,
|
||||
}),
|
||||
// interceptors.responseError(),
|
||||
] as const
|
||||
let proxyAgent: ProxyAgent | null = null
|
||||
let globalDispatcher = getGlobalDispatcher()
|
||||
const buildDispatcher = (redirectDispatcher: Dispatcher.DispatcherComposeInterceptor | null, retryNum = 3) => {
|
||||
const otherInterceptors =
|
||||
retryNum == 3
|
||||
? dispatchers
|
||||
: [
|
||||
interceptors.retry({
|
||||
maxRetries: retryNum,
|
||||
minTimeout: 1000,
|
||||
maxTimeout: 6000,
|
||||
timeoutFactor: 2,
|
||||
retryAfter: true,
|
||||
}),
|
||||
]
|
||||
if (redirectDispatcher) {
|
||||
return (proxyAgent ?? globalDispatcher).compose(redirectDispatcher, ...otherInterceptors)
|
||||
}
|
||||
return (proxyAgent ?? globalDispatcher).compose(...otherInterceptors)
|
||||
}
|
||||
|
||||
setGlobalDispatcher(buildDispatcher(redirectDispatcher))
|
||||
|
||||
export const setProxy = (url?: string) => {
|
||||
proxyAgent = url ? new ProxyAgent(url) : null
|
||||
setGlobalDispatcher(buildDispatcher(redirectDispatcher))
|
||||
}
|
||||
export const setProxyByHost = (host?: string, port?: string) => {
|
||||
setProxy(host ? `http://${host}:${port}` : undefined)
|
||||
}
|
||||
const CONTENT_TYPE = {
|
||||
json: 'application/json',
|
||||
form: 'application/x-www-form-urlencoded',
|
||||
text: 'text/plain',
|
||||
formdata: 'multipart/form-data',
|
||||
xml: 'application/xml',
|
||||
binary: 'application/octet-stream',
|
||||
}
|
||||
type ParamsData = Record<string, string | number | null | undefined | boolean>
|
||||
export interface Options {
|
||||
method?:
|
||||
| 'GET'
|
||||
| 'HEAD'
|
||||
| 'POST'
|
||||
| 'PUT'
|
||||
| 'DELETE'
|
||||
| 'OPTIONS'
|
||||
| 'PATCH'
|
||||
| 'PROPFIND'
|
||||
| 'COPY'
|
||||
| 'MOVE'
|
||||
| 'MKCOL'
|
||||
| 'PROPPATCH'
|
||||
| 'QUOTA'
|
||||
query?: ParamsData
|
||||
headers?: Record<string, string | string[]>
|
||||
timeout?: number
|
||||
maxRedirect?: number
|
||||
signal?: AbortController['signal']
|
||||
json?: Record<string, unknown>
|
||||
form?: ParamsData
|
||||
binary?: Buffer | Uint8Array
|
||||
text?: string
|
||||
formdata?: Record<string, string | Buffer | Uint8Array>
|
||||
xml?: string
|
||||
needBody?: boolean
|
||||
needRaw?: boolean
|
||||
retryNum?: number
|
||||
}
|
||||
export interface Response<Res> {
|
||||
headers: {
|
||||
accept?: string | undefined
|
||||
'accept-language'?: string | undefined
|
||||
'accept-patch'?: string | undefined
|
||||
'accept-ranges'?: string | undefined
|
||||
'access-control-allow-credentials'?: string | undefined
|
||||
'access-control-allow-headers'?: string | undefined
|
||||
'access-control-allow-methods'?: string | undefined
|
||||
'access-control-allow-origin'?: string | undefined
|
||||
'access-control-expose-headers'?: string | undefined
|
||||
'access-control-max-age'?: string | undefined
|
||||
'access-control-request-headers'?: string | undefined
|
||||
'access-control-request-method'?: string | undefined
|
||||
age?: string | undefined
|
||||
allow?: string | undefined
|
||||
'alt-svc'?: string | undefined
|
||||
authorization?: string | undefined
|
||||
'cache-control'?: string | undefined
|
||||
connection?: string | undefined
|
||||
'content-disposition'?: string | undefined
|
||||
'content-encoding'?: string | undefined
|
||||
'content-language'?: string | undefined
|
||||
'content-length'?: string | undefined
|
||||
'content-location'?: string | undefined
|
||||
'content-range'?: string | undefined
|
||||
'content-type'?: string | undefined
|
||||
cookie?: string | undefined
|
||||
date?: string | undefined
|
||||
etag?: string | undefined
|
||||
expect?: string | undefined
|
||||
expires?: string | undefined
|
||||
forwarded?: string | undefined
|
||||
from?: string | undefined
|
||||
host?: string | undefined
|
||||
'if-match'?: string | undefined
|
||||
'if-modified-since'?: string | undefined
|
||||
'if-none-match'?: string | undefined
|
||||
'if-unmodified-since'?: string | undefined
|
||||
'last-modified'?: string | undefined
|
||||
location?: string | undefined
|
||||
origin?: string | undefined
|
||||
pragma?: string | undefined
|
||||
'proxy-authenticate'?: string | undefined
|
||||
'proxy-authorization'?: string | undefined
|
||||
'public-key-pins'?: string | undefined
|
||||
range?: string | undefined
|
||||
referer?: string | undefined
|
||||
'retry-after'?: string | undefined
|
||||
'sec-websocket-accept'?: string | undefined
|
||||
'sec-websocket-extensions'?: string | undefined
|
||||
'sec-websocket-key'?: string | undefined
|
||||
'sec-websocket-protocol'?: string | undefined
|
||||
'sec-websocket-version'?: string | undefined
|
||||
'set-cookie'?: string[] | undefined
|
||||
'strict-transport-security'?: string | undefined
|
||||
tk?: string | undefined
|
||||
trailer?: string | undefined
|
||||
'transfer-encoding'?: string | undefined
|
||||
upgrade?: string | undefined
|
||||
'user-agent'?: string | undefined
|
||||
vary?: string | undefined
|
||||
via?: string | undefined
|
||||
warning?: string | undefined
|
||||
'www-authenticate'?: string | undefined
|
||||
}
|
||||
body: Res
|
||||
raw: Uint8Array
|
||||
statusCode?: number
|
||||
statusMessage?: string
|
||||
}
|
||||
|
||||
const buildRequestBody = (options: Options) => {
|
||||
let contentType: string | undefined
|
||||
let body: string | Buffer | Uint8Array | FormData | undefined
|
||||
if (options.json != null) {
|
||||
contentType = CONTENT_TYPE.json
|
||||
body = JSON.stringify(options.json)
|
||||
} else if (options.form != null) {
|
||||
contentType = CONTENT_TYPE.form
|
||||
body = qs.stringify(options.form)
|
||||
} else if (options.binary != null) {
|
||||
contentType = CONTENT_TYPE.binary
|
||||
body = options.binary
|
||||
} else if (options.formdata != null) {
|
||||
const formdata = new FormData()
|
||||
// eslint-disable-next-line guard-for-in
|
||||
for (const key in options.formdata) {
|
||||
formdata.append(key, options.formdata[key])
|
||||
}
|
||||
contentType = CONTENT_TYPE.formdata
|
||||
body = formdata
|
||||
} else if (options.xml != null) {
|
||||
contentType = CONTENT_TYPE.xml
|
||||
body = options.xml
|
||||
} else if (options.text != null) {
|
||||
contentType = CONTENT_TYPE.text
|
||||
body = options.text
|
||||
}
|
||||
const headers = options.headers ?? {}
|
||||
if (headers['Content-Type']) {
|
||||
contentType = headers['Content-Type'] as string
|
||||
}
|
||||
const finalHeaders: NonNullable<Options['headers']> = { ...defaultOptions.headers, ...headers }
|
||||
if (contentType) finalHeaders['Content-Type'] = contentType
|
||||
return [finalHeaders, body] as const
|
||||
}
|
||||
|
||||
const buildRequestDispatcher = (options: Options) => {
|
||||
let dispatcher: Dispatcher.ComposedDispatcher | undefined
|
||||
|
||||
if (options.maxRedirect != null) {
|
||||
if (options.maxRedirect != defaultOptions.maxRedirect) {
|
||||
if (options.maxRedirect) {
|
||||
dispatcher = buildDispatcher(interceptors.redirect({ maxRedirections: options.maxRedirect }), options.retryNum)
|
||||
} else {
|
||||
dispatcher = buildDispatcher(null, options.retryNum)
|
||||
}
|
||||
}
|
||||
}
|
||||
return dispatcher
|
||||
}
|
||||
|
||||
export const request = async <T = unknown>(url: string, options: Options = {}): Promise<Response<T>> => {
|
||||
const method = options.method?.toUpperCase() ?? 'GET'
|
||||
const timeout = options.timeout ?? defaultOptions.timeout
|
||||
const [headers, body] = buildRequestBody(options)
|
||||
// console.log(url, {
|
||||
// method,
|
||||
// bodyTimeout: timeout,
|
||||
// headersTimeout: timeout,
|
||||
// headers,
|
||||
// query: options.query,
|
||||
// body,
|
||||
// signal: options.signal,
|
||||
// dispatcher: buildRequestDispatcher(options),
|
||||
// })
|
||||
return nodeRrequest(url, {
|
||||
method,
|
||||
bodyTimeout: timeout,
|
||||
headersTimeout: timeout,
|
||||
headers,
|
||||
query: options.query,
|
||||
body,
|
||||
signal: options.signal,
|
||||
dispatcher: buildRequestDispatcher(options),
|
||||
}).then(async(response) => {
|
||||
if (options.needBody) {
|
||||
return {
|
||||
headers: response.headers,
|
||||
statusCode: response.statusCode,
|
||||
body: response.body as unknown as T,
|
||||
} satisfies Omit<Response<T>, 'raw'> as Response<T>
|
||||
}
|
||||
if (options.needRaw) {
|
||||
return {
|
||||
headers: response.headers,
|
||||
statusCode: response.statusCode,
|
||||
raw: await response.body.bytes(),
|
||||
} satisfies Omit<Response<T>, 'body'> as Response<T>
|
||||
}
|
||||
// console.log(response)
|
||||
let body = (await response.body.text()) as T
|
||||
if (!headers['Content-Type'] || headers['Content-Type'].includes(CONTENT_TYPE.json)) {
|
||||
try {
|
||||
body = JSON.parse(body as string) as T
|
||||
} catch {}
|
||||
}
|
||||
return {
|
||||
body,
|
||||
headers: response.headers,
|
||||
statusCode: response.statusCode,
|
||||
} satisfies Omit<Response<T>, 'raw'> as Response<T>
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,237 @@
|
||||
import qs from 'node:querystring'
|
||||
import {
|
||||
FormData,
|
||||
getGlobalDispatcher,
|
||||
request as nodeRrequest,
|
||||
ProxyAgent,
|
||||
setGlobalDispatcher,
|
||||
type Dispatcher,
|
||||
} from 'undici'
|
||||
|
||||
const defaultOptions: Options = {
|
||||
timeout: 15000,
|
||||
headers: {
|
||||
'User-Agent':
|
||||
'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36',
|
||||
},
|
||||
maxRedirect: 5,
|
||||
} as const
|
||||
let proxyAgent: ProxyAgent | null = null
|
||||
let globalDispatcher = getGlobalDispatcher()
|
||||
const buildDispatcher = () => {
|
||||
return proxyAgent ?? globalDispatcher
|
||||
}
|
||||
|
||||
setGlobalDispatcher(buildDispatcher())
|
||||
|
||||
export const setProxy = (url?: string) => {
|
||||
proxyAgent = url ? new ProxyAgent(url) : null
|
||||
setGlobalDispatcher(buildDispatcher())
|
||||
}
|
||||
export const setProxyByHost = (host?: string, port?: string) => {
|
||||
console.log(host)
|
||||
setProxy(host ? `http://${host}:${port}` : undefined)
|
||||
}
|
||||
const CONTENT_TYPE = {
|
||||
json: 'application/json',
|
||||
form: 'application/x-www-form-urlencoded',
|
||||
text: 'text/plain',
|
||||
formdata: 'multipart/form-data',
|
||||
xml: 'application/xml',
|
||||
binary: 'application/octet-stream',
|
||||
}
|
||||
type ParamsData = Record<string, string | number | null | undefined | boolean>
|
||||
export interface Options {
|
||||
method?:
|
||||
| 'GET'
|
||||
| 'HEAD'
|
||||
| 'POST'
|
||||
| 'PUT'
|
||||
| 'DELETE'
|
||||
| 'OPTIONS'
|
||||
| 'PATCH'
|
||||
| 'PROPFIND'
|
||||
| 'COPY'
|
||||
| 'MOVE'
|
||||
| 'MKCOL'
|
||||
| 'PROPPATCH'
|
||||
| 'QUOTA'
|
||||
query?: ParamsData
|
||||
headers?: Record<string, string | string[]>
|
||||
timeout?: number
|
||||
maxRedirect?: number
|
||||
signal?: AbortController['signal']
|
||||
json?: Record<string, unknown>
|
||||
form?: ParamsData
|
||||
binary?: Buffer | Uint8Array
|
||||
text?: string
|
||||
formdata?: Record<string, string | Buffer | Uint8Array>
|
||||
xml?: string
|
||||
needBody?: boolean
|
||||
needRaw?: boolean
|
||||
retryNum?: number
|
||||
}
|
||||
export interface Response<Res> {
|
||||
headers: {
|
||||
accept?: string | undefined
|
||||
'accept-language'?: string | undefined
|
||||
'accept-patch'?: string | undefined
|
||||
'accept-ranges'?: string | undefined
|
||||
'access-control-allow-credentials'?: string | undefined
|
||||
'access-control-allow-headers'?: string | undefined
|
||||
'access-control-allow-methods'?: string | undefined
|
||||
'access-control-allow-origin'?: string | undefined
|
||||
'access-control-expose-headers'?: string | undefined
|
||||
'access-control-max-age'?: string | undefined
|
||||
'access-control-request-headers'?: string | undefined
|
||||
'access-control-request-method'?: string | undefined
|
||||
age?: string | undefined
|
||||
allow?: string | undefined
|
||||
'alt-svc'?: string | undefined
|
||||
authorization?: string | undefined
|
||||
'cache-control'?: string | undefined
|
||||
connection?: string | undefined
|
||||
'content-disposition'?: string | undefined
|
||||
'content-encoding'?: string | undefined
|
||||
'content-language'?: string | undefined
|
||||
'content-length'?: string | undefined
|
||||
'content-location'?: string | undefined
|
||||
'content-range'?: string | undefined
|
||||
'content-type'?: string | undefined
|
||||
cookie?: string | undefined
|
||||
date?: string | undefined
|
||||
etag?: string | undefined
|
||||
expect?: string | undefined
|
||||
expires?: string | undefined
|
||||
forwarded?: string | undefined
|
||||
from?: string | undefined
|
||||
host?: string | undefined
|
||||
'if-match'?: string | undefined
|
||||
'if-modified-since'?: string | undefined
|
||||
'if-none-match'?: string | undefined
|
||||
'if-unmodified-since'?: string | undefined
|
||||
'last-modified'?: string | undefined
|
||||
location?: string | undefined
|
||||
origin?: string | undefined
|
||||
pragma?: string | undefined
|
||||
'proxy-authenticate'?: string | undefined
|
||||
'proxy-authorization'?: string | undefined
|
||||
'public-key-pins'?: string | undefined
|
||||
range?: string | undefined
|
||||
referer?: string | undefined
|
||||
'retry-after'?: string | undefined
|
||||
'sec-websocket-accept'?: string | undefined
|
||||
'sec-websocket-extensions'?: string | undefined
|
||||
'sec-websocket-key'?: string | undefined
|
||||
'sec-websocket-protocol'?: string | undefined
|
||||
'sec-websocket-version'?: string | undefined
|
||||
'set-cookie'?: string[] | undefined
|
||||
'strict-transport-security'?: string | undefined
|
||||
tk?: string | undefined
|
||||
trailer?: string | undefined
|
||||
'transfer-encoding'?: string | undefined
|
||||
upgrade?: string | undefined
|
||||
'user-agent'?: string | undefined
|
||||
vary?: string | undefined
|
||||
via?: string | undefined
|
||||
warning?: string | undefined
|
||||
'www-authenticate'?: string | undefined
|
||||
}
|
||||
body: Res
|
||||
raw: Uint8Array
|
||||
statusCode?: number
|
||||
statusMessage?: string
|
||||
}
|
||||
|
||||
const buildRequestBody = (options: Options) => {
|
||||
let contentType: string | undefined
|
||||
let body: string | Buffer | Uint8Array | FormData | undefined
|
||||
if (options.json != null) {
|
||||
contentType = CONTENT_TYPE.json
|
||||
body = JSON.stringify(options.json)
|
||||
} else if (options.form != null) {
|
||||
contentType = CONTENT_TYPE.form
|
||||
body = qs.stringify(options.form)
|
||||
} else if (options.binary != null) {
|
||||
contentType = CONTENT_TYPE.binary
|
||||
body = options.binary
|
||||
} else if (options.formdata != null) {
|
||||
const formdata = new FormData()
|
||||
// eslint-disable-next-line guard-for-in
|
||||
for (const key in options.formdata) {
|
||||
formdata.append(key, options.formdata[key])
|
||||
}
|
||||
contentType = CONTENT_TYPE.formdata
|
||||
body = formdata
|
||||
} else if (options.xml != null) {
|
||||
contentType = CONTENT_TYPE.xml
|
||||
body = options.xml
|
||||
} else if (options.text != null) {
|
||||
contentType = CONTENT_TYPE.text
|
||||
body = options.text
|
||||
}
|
||||
const headers = options.headers ?? {}
|
||||
if (headers['Content-Type']) {
|
||||
contentType = headers['Content-Type'] as string
|
||||
}
|
||||
const finalHeaders: NonNullable<Options['headers']> = { ...defaultOptions.headers, ...headers }
|
||||
if (contentType) finalHeaders['Content-Type'] = contentType
|
||||
return [finalHeaders, body] as const
|
||||
}
|
||||
|
||||
const buildRequestDispatcher = (options: Options) => {
|
||||
return buildDispatcher()
|
||||
}
|
||||
|
||||
export const request = async <T = unknown>(url: string, options: Options = {}): Promise<Response<T>> => {
|
||||
const method = (options.method?.toUpperCase() ?? 'GET') as Dispatcher.RequestOptions['method']
|
||||
const timeout = options.timeout ?? defaultOptions.timeout
|
||||
const [headers, body] = buildRequestBody(options)
|
||||
// console.log(url, {
|
||||
// method,
|
||||
// bodyTimeout: timeout,
|
||||
// headersTimeout: timeout,
|
||||
// headers,
|
||||
// query: options.query,
|
||||
// body,
|
||||
// signal: options.signal,
|
||||
// dispatcher: buildRequestDispatcher(options),
|
||||
// })
|
||||
return nodeRrequest(url, {
|
||||
method,
|
||||
bodyTimeout: timeout,
|
||||
headersTimeout: timeout,
|
||||
headers,
|
||||
query: options.query,
|
||||
body,
|
||||
signal: options.signal,
|
||||
dispatcher: buildRequestDispatcher(options),
|
||||
}).then(async(response) => {
|
||||
if (options.needBody) {
|
||||
return {
|
||||
headers: response.headers,
|
||||
statusCode: response.statusCode,
|
||||
body: response.body as unknown as T,
|
||||
} satisfies Omit<Response<T>, 'raw'> as Response<T>
|
||||
}
|
||||
if (options.needRaw) {
|
||||
return {
|
||||
headers: response.headers,
|
||||
statusCode: response.statusCode,
|
||||
raw: new Uint8Array(await response.body.arrayBuffer()),
|
||||
} satisfies Omit<Response<T>, 'body'> as unknown as Response<T>
|
||||
}
|
||||
// console.log(response)
|
||||
let body = (await response.body.text()) as T
|
||||
if (!headers['Content-Type'] || headers['Content-Type'].includes(CONTENT_TYPE.json)) {
|
||||
try {
|
||||
body = JSON.parse(body as string) as T
|
||||
} catch {}
|
||||
}
|
||||
return {
|
||||
body,
|
||||
headers: response.headers,
|
||||
statusCode: response.statusCode,
|
||||
} satisfies Omit<Response<T>, 'raw'> as Response<T>
|
||||
})
|
||||
}
|
||||
+18
-4
@@ -2,7 +2,7 @@ import path from 'node:path'
|
||||
import { existsSync, mkdirSync, renameSync } from 'fs'
|
||||
import { app, shell, screen, nativeTheme, dialog } from 'electron'
|
||||
import { URL_SCHEME_RXP } from '@common/constants'
|
||||
import { getTheme, initHotKey, initSetting, parseEnvParams } from './utils'
|
||||
import { getProxy, getTheme, initHotKey, initSetting, parseEnvParams } from './utils'
|
||||
import { navigationUrlWhiteList } from '@common/config'
|
||||
import defaultSetting from '@common/defaultSetting'
|
||||
import { isExistWindow as isExistMainWindow, showWindow as showMainWindow } from './modules/winMain'
|
||||
@@ -11,6 +11,7 @@ import { isMac, log } from '@common/utils'
|
||||
import createWorkers from './worker'
|
||||
import { migrateDBData } from './utils/migrate'
|
||||
import { openDirInExplorer } from '@common/utils/electron'
|
||||
import { setProxyByHost } from '@common/utils/request'
|
||||
|
||||
export const initGlobalData = () => {
|
||||
const envParams = parseEnvParams()
|
||||
@@ -236,14 +237,27 @@ export const listenerAppEvent = (startApp: () => void) => {
|
||||
global.lx?.event_app.system_theme_change(shouldUseDarkColors)
|
||||
})
|
||||
|
||||
global.lx.event_app.on('updated_config', (config, setting) => {
|
||||
if (config.includes('player.volume')) {
|
||||
const setProxy = () => {
|
||||
const proxy = getProxy()
|
||||
if (proxy) {
|
||||
setProxyByHost(proxy.host, proxy.port ? String(proxy.port) : undefined)
|
||||
} else setProxyByHost()
|
||||
}
|
||||
global.lx.event_app.on('updated_config', (keys, setting) => {
|
||||
if (keys.includes('network.proxy.enable') || (global.lx.appSetting['network.proxy.enable'] && keys.some(k => k.includes('network.proxy.')))) {
|
||||
setProxy()
|
||||
}
|
||||
|
||||
if (keys.includes('player.volume')) {
|
||||
global.lx.event_app.player_status({ volume: Math.trunc(setting['player.volume']! * 100) })
|
||||
}
|
||||
if (config.includes('player.isMute')) {
|
||||
if (keys.includes('player.isMute')) {
|
||||
global.lx.event_app.player_status({ mute: setting['player.isMute'] })
|
||||
}
|
||||
})
|
||||
global.lx.event_app.on('app_inited', () => {
|
||||
setProxy()
|
||||
})
|
||||
}
|
||||
|
||||
const initTheme = () => {
|
||||
|
||||
@@ -3,10 +3,9 @@ import { httpFetch, type RequestOptions } from '@main/utils/request'
|
||||
import { decodeData, encodeData } from '../utils'
|
||||
|
||||
export const request = async(url: string, options: RequestOptions = { }) => {
|
||||
return httpFetch(url, {
|
||||
return httpFetch<string>(url, {
|
||||
...options,
|
||||
timeout: options.timeout ?? 10000,
|
||||
follow_max: 5,
|
||||
}).then(response => {
|
||||
return {
|
||||
text: response.body,
|
||||
|
||||
+4
-131
@@ -1,8 +1,5 @@
|
||||
import needle, { type NeedleHttpVerbs, type NeedleOptions, type BodyData, type NeedleCallback, type NeedleResponse } from 'needle'
|
||||
// import progress from 'request-progress'
|
||||
import { httpOverHttp, httpsOverHttp } from 'tunnel'
|
||||
import { type ClientRequest } from 'node:http'
|
||||
import { getProxy } from './index'
|
||||
import { request, type Options } from '@common/utils/request'
|
||||
// import fs from 'fs'
|
||||
|
||||
export const requestMsg = {
|
||||
@@ -14,53 +11,6 @@ export const requestMsg = {
|
||||
cancelRequest: '取消http请求',
|
||||
} as const
|
||||
|
||||
|
||||
const httpsRxp = /^https:/
|
||||
const getRequestAgent = (url: string) => {
|
||||
const proxy = getProxy()
|
||||
return proxy ? (httpsRxp.test(url) ? httpsOverHttp : httpOverHttp)({ proxy }) : undefined
|
||||
}
|
||||
|
||||
export interface RequestOptions extends NeedleOptions {
|
||||
method?: NeedleHttpVerbs
|
||||
body?: BodyData
|
||||
form?: BodyData
|
||||
formData?: BodyData
|
||||
}
|
||||
export type RequestCallback = NeedleCallback
|
||||
type RequestResponse = NeedleResponse
|
||||
const request = (url: string, options: RequestOptions, callback: RequestCallback): ClientRequest => {
|
||||
let data: BodyData = null
|
||||
if (options.body) {
|
||||
data = options.body
|
||||
} else if (options.form) {
|
||||
data = options.form
|
||||
// data.content_type = 'application/x-www-form-urlencoded'
|
||||
options.json = false
|
||||
} else if (options.formData) {
|
||||
data = options.formData
|
||||
// data.content_type = 'multipart/form-data'
|
||||
options.json = false
|
||||
}
|
||||
options.response_timeout = options.timeout
|
||||
|
||||
return needle.request(options.method ?? 'get', url, data, options, (err, resp, body) => {
|
||||
if (!err) {
|
||||
body = resp.body = resp.raw.toString()
|
||||
try {
|
||||
resp.body = JSON.parse(resp.body)
|
||||
} catch (_) {}
|
||||
body = resp.body
|
||||
}
|
||||
callback(err, resp, body)
|
||||
// @ts-expect-error
|
||||
}).request
|
||||
}
|
||||
|
||||
|
||||
const defaultHeaders = {
|
||||
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36',
|
||||
}
|
||||
// var proxyUrl = "http://" + user + ":" + password + "@" + host + ":" + port;
|
||||
// var proxiedRequest = request.defaults({'proxy': proxyUrl});
|
||||
|
||||
@@ -68,70 +18,13 @@ const defaultHeaders = {
|
||||
// abort: () => void
|
||||
// }
|
||||
|
||||
/**
|
||||
* promise 形式的请求方法
|
||||
* @param {*} url
|
||||
* @param {*} options
|
||||
*/
|
||||
const buildHttpPromose = async(url: string, options: RequestOptions): Promise<RequestResponse> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
void fetchData(url, options.method, options, (err, resp, body) => {
|
||||
// options.isShowProgress && window.api.hideProgress()
|
||||
// debugRequest && console.log(`\n---response------${url}------------`)
|
||||
// debugRequest && console.log(body)
|
||||
// obj.requestObj = null
|
||||
// obj.cancelFn = null
|
||||
if (err) {
|
||||
reject(err)
|
||||
return
|
||||
}
|
||||
resolve(resp)
|
||||
})
|
||||
// .then(request => {
|
||||
// // obj.requestObj = ro
|
||||
// // if (obj.isCancelled) obj.cancelHttp()
|
||||
// promise.abort = () => {
|
||||
// request.destroy(new Error('cancelled'))
|
||||
// }
|
||||
// })
|
||||
})
|
||||
// let obj = {
|
||||
// isCancelled: false,
|
||||
// cancelHttp: () => {
|
||||
// if (!obj.requestObj) return obj.isCancelled = true
|
||||
// cancelHttp(obj.requestObj)
|
||||
// obj.requestObj = null
|
||||
// obj.promise = obj.cancelHttp = null
|
||||
// obj.cancelFn(new Error(requestMsg.cancelRequest))
|
||||
// obj.cancelFn = null
|
||||
// },
|
||||
// }
|
||||
// obj.promise = new Promise((resolve, reject) => {
|
||||
// obj.cancelFn = reject
|
||||
// debugRequest && console.log(`\n---send request------${url}------------`)
|
||||
// fetchData(url, options.method, options, (err, resp, body) => {
|
||||
// // options.isShowProgress && window.api.hideProgress()
|
||||
// debugRequest && console.log(`\n---response------${url}------------`)
|
||||
// debugRequest && console.log(body)
|
||||
// obj.requestObj = null
|
||||
// obj.cancelFn = null
|
||||
// if (err) { reject(err); return }
|
||||
// resolve(resp)
|
||||
// }).then(ro => {
|
||||
// obj.requestObj = ro
|
||||
// if (obj.isCancelled) obj.cancelHttp()
|
||||
// })
|
||||
// })
|
||||
// return obj
|
||||
}
|
||||
|
||||
/**
|
||||
* 请求超时自动重试
|
||||
* @param {*} url
|
||||
* @param {*} options
|
||||
*/
|
||||
export const httpFetch = async(url: string, options: RequestOptions = { method: 'get' }) => {
|
||||
return buildHttpPromose(url, options).catch(async(err: any) => {
|
||||
export const httpFetch = async<T = unknown> (url: string, options: Options) => {
|
||||
return request<T>(url, options).catch(async(err: any) => {
|
||||
// console.log('出错', err)
|
||||
if (err.message === 'socket hang up') {
|
||||
// window.globalObj.apiSource = 'temp'
|
||||
@@ -166,24 +59,4 @@ export const httpFetch = async(url: string, options: RequestOptions = { method:
|
||||
// return requestPromise
|
||||
}
|
||||
|
||||
const fetchData = async(url: string, method: RequestOptions['method'], {
|
||||
headers = {},
|
||||
format = 'json',
|
||||
timeout = 15000,
|
||||
...options
|
||||
}, callback: RequestCallback) => {
|
||||
// console.log(url, options)
|
||||
console.log('---start---', url)
|
||||
headers = Object.assign({}, headers)
|
||||
|
||||
return request(url, {
|
||||
...options,
|
||||
method,
|
||||
headers: Object.assign({}, defaultHeaders, headers),
|
||||
timeout,
|
||||
agent: getRequestAgent(url),
|
||||
json: format === 'json',
|
||||
}, (err, resp, body) => {
|
||||
callback(err, resp, body)
|
||||
})
|
||||
}
|
||||
export type RequestOptions = Options
|
||||
|
||||
Reference in New Issue
Block a user