更新构建流程,集成 undici 库并优化请求处理
This commit is contained in:
@@ -94,12 +94,46 @@ jobs:
|
|||||||
name: lx-music-desktop-win_arm64-green
|
name: lx-music-desktop-win_arm64-green
|
||||||
path: build/*win_arm64-green.7z
|
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
|
Windows_7:
|
||||||
run: npm install electron@22
|
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
|
- name: Get npm cache directory
|
||||||
run: pip.exe install setuptools
|
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
|
- name: Build Package win7 Setup x64
|
||||||
run: npm run pack:win7:setup:x64
|
run: npm run pack:win7:setup:x64
|
||||||
|
|||||||
@@ -98,7 +98,14 @@ jobs:
|
|||||||
NPM_CACHE: ${{ env.NPM_CACHE }}
|
NPM_CACHE: ${{ env.NPM_CACHE }}
|
||||||
uses: ./.github/actions/setup
|
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
|
- name: Build src code
|
||||||
|
env:
|
||||||
|
BUILD_WIN7: true
|
||||||
run: |
|
run: |
|
||||||
git status --porcelain
|
git status --porcelain
|
||||||
npm run build
|
npm run build
|
||||||
|
|||||||
@@ -19,7 +19,13 @@ const gitInfo = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
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_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()
|
gitInfo.commit_date = execSync('git log -1 --pretty=format:"%ad" --date=iso-strict').toString().trim()
|
||||||
} else if (process.env.IS_CI) {
|
} else if (process.env.IS_CI) {
|
||||||
|
|||||||
Generated
+13
-3
@@ -27,6 +27,7 @@
|
|||||||
"node-id3": "^0.2.9",
|
"node-id3": "^0.2.9",
|
||||||
"sortablejs": "^1.15.6",
|
"sortablejs": "^1.15.6",
|
||||||
"tunnel": "^0.0.6",
|
"tunnel": "^0.0.6",
|
||||||
|
"undici": "^7.16.0",
|
||||||
"utf-8-validate": "^6.0.5",
|
"utf-8-validate": "^6.0.5",
|
||||||
"vue": "~3.3.13",
|
"vue": "~3.3.13",
|
||||||
"vue-router": "^4.5.1",
|
"vue-router": "^4.5.1",
|
||||||
@@ -7297,9 +7298,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/electron/node_modules/@types/node": {
|
"node_modules/electron/node_modules/@types/node": {
|
||||||
"version": "22.17.0",
|
"version": "22.19.2",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.17.0.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.2.tgz",
|
||||||
"integrity": "sha512-bbAKTCqX5aNVryi7qXVMi+OkB3w/OyblodicMbvE38blyAz7GxXf6XYhklokijuPwwVg9sDLKRxt0ZHXQwZVfQ==",
|
"integrity": "sha512-LPM2G3Syo1GLzXLGJAKdqoU35XvrWzGJ21/7sgZTUpbkBaOasTj8tjwn6w+hCkqaa1TfJ/w67rJSwYItlJ2mYw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -17208,6 +17209,15 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"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": {
|
"node_modules/undici-types": {
|
||||||
"version": "6.21.0",
|
"version": "6.21.0",
|
||||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
|
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
|
||||||
|
|||||||
@@ -193,6 +193,7 @@
|
|||||||
"node-id3": "^0.2.9",
|
"node-id3": "^0.2.9",
|
||||||
"sortablejs": "^1.15.6",
|
"sortablejs": "^1.15.6",
|
||||||
"tunnel": "^0.0.6",
|
"tunnel": "^0.0.6",
|
||||||
|
"undici": "^7.16.0",
|
||||||
"utf-8-validate": "^6.0.5",
|
"utf-8-validate": "^6.0.5",
|
||||||
"vue": "~3.3.13",
|
"vue": "~3.3.13",
|
||||||
"vue-router": "^4.5.1",
|
"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 { existsSync, mkdirSync, renameSync } from 'fs'
|
||||||
import { app, shell, screen, nativeTheme, dialog } from 'electron'
|
import { app, shell, screen, nativeTheme, dialog } from 'electron'
|
||||||
import { URL_SCHEME_RXP } from '@common/constants'
|
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 { navigationUrlWhiteList } from '@common/config'
|
||||||
import defaultSetting from '@common/defaultSetting'
|
import defaultSetting from '@common/defaultSetting'
|
||||||
import { isExistWindow as isExistMainWindow, showWindow as showMainWindow } from './modules/winMain'
|
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 createWorkers from './worker'
|
||||||
import { migrateDBData } from './utils/migrate'
|
import { migrateDBData } from './utils/migrate'
|
||||||
import { openDirInExplorer } from '@common/utils/electron'
|
import { openDirInExplorer } from '@common/utils/electron'
|
||||||
|
import { setProxyByHost } from '@common/utils/request'
|
||||||
|
|
||||||
export const initGlobalData = () => {
|
export const initGlobalData = () => {
|
||||||
const envParams = parseEnvParams()
|
const envParams = parseEnvParams()
|
||||||
@@ -236,14 +237,27 @@ export const listenerAppEvent = (startApp: () => void) => {
|
|||||||
global.lx?.event_app.system_theme_change(shouldUseDarkColors)
|
global.lx?.event_app.system_theme_change(shouldUseDarkColors)
|
||||||
})
|
})
|
||||||
|
|
||||||
global.lx.event_app.on('updated_config', (config, setting) => {
|
const setProxy = () => {
|
||||||
if (config.includes('player.volume')) {
|
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) })
|
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.player_status({ mute: setting['player.isMute'] })
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
global.lx.event_app.on('app_inited', () => {
|
||||||
|
setProxy()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const initTheme = () => {
|
const initTheme = () => {
|
||||||
|
|||||||
@@ -3,10 +3,9 @@ import { httpFetch, type RequestOptions } from '@main/utils/request'
|
|||||||
import { decodeData, encodeData } from '../utils'
|
import { decodeData, encodeData } from '../utils'
|
||||||
|
|
||||||
export const request = async(url: string, options: RequestOptions = { }) => {
|
export const request = async(url: string, options: RequestOptions = { }) => {
|
||||||
return httpFetch(url, {
|
return httpFetch<string>(url, {
|
||||||
...options,
|
...options,
|
||||||
timeout: options.timeout ?? 10000,
|
timeout: options.timeout ?? 10000,
|
||||||
follow_max: 5,
|
|
||||||
}).then(response => {
|
}).then(response => {
|
||||||
return {
|
return {
|
||||||
text: response.body,
|
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 progress from 'request-progress'
|
||||||
import { httpOverHttp, httpsOverHttp } from 'tunnel'
|
import { request, type Options } from '@common/utils/request'
|
||||||
import { type ClientRequest } from 'node:http'
|
|
||||||
import { getProxy } from './index'
|
|
||||||
// import fs from 'fs'
|
// import fs from 'fs'
|
||||||
|
|
||||||
export const requestMsg = {
|
export const requestMsg = {
|
||||||
@@ -14,53 +11,6 @@ export const requestMsg = {
|
|||||||
cancelRequest: '取消http请求',
|
cancelRequest: '取消http请求',
|
||||||
} as const
|
} 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 proxyUrl = "http://" + user + ":" + password + "@" + host + ":" + port;
|
||||||
// var proxiedRequest = request.defaults({'proxy': proxyUrl});
|
// var proxiedRequest = request.defaults({'proxy': proxyUrl});
|
||||||
|
|
||||||
@@ -68,70 +18,13 @@ const defaultHeaders = {
|
|||||||
// abort: () => void
|
// 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 {*} url
|
||||||
* @param {*} options
|
* @param {*} options
|
||||||
*/
|
*/
|
||||||
export const httpFetch = async(url: string, options: RequestOptions = { method: 'get' }) => {
|
export const httpFetch = async<T = unknown> (url: string, options: Options) => {
|
||||||
return buildHttpPromose(url, options).catch(async(err: any) => {
|
return request<T>(url, options).catch(async(err: any) => {
|
||||||
// console.log('出错', err)
|
// console.log('出错', err)
|
||||||
if (err.message === 'socket hang up') {
|
if (err.message === 'socket hang up') {
|
||||||
// window.globalObj.apiSource = 'temp'
|
// window.globalObj.apiSource = 'temp'
|
||||||
@@ -166,24 +59,4 @@ export const httpFetch = async(url: string, options: RequestOptions = { method:
|
|||||||
// return requestPromise
|
// return requestPromise
|
||||||
}
|
}
|
||||||
|
|
||||||
const fetchData = async(url: string, method: RequestOptions['method'], {
|
export type RequestOptions = Options
|
||||||
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)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user