diff --git a/.github/workflows/beta-pack.yml b/.github/workflows/beta-pack.yml index d9777c25..a7398e29 100644 --- a/.github/workflows/beta-pack.yml +++ b/.github/workflows/beta-pack.yml @@ -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 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f24f0329..0b3b4557 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -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 diff --git a/build-config/renderer/webpack.config.prod.js b/build-config/renderer/webpack.config.prod.js index 873ddf2f..cef4d595 100644 --- a/build-config/renderer/webpack.config.prod.js +++ b/build-config/renderer/webpack.config.prod.js @@ -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) { diff --git a/package-lock.json b/package-lock.json index bc450db9..1d643bff 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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", diff --git a/package.json b/package.json index 3e5c8846..b4e54608 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/src/common/utils/request.ts b/src/common/utils/request.ts new file mode 100644 index 00000000..2c224232 --- /dev/null +++ b/src/common/utils/request.ts @@ -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 +export interface Options { + method?: + | 'GET' + | 'HEAD' + | 'POST' + | 'PUT' + | 'DELETE' + | 'OPTIONS' + | 'PATCH' + | 'PROPFIND' + | 'COPY' + | 'MOVE' + | 'MKCOL' + | 'PROPPATCH' + | 'QUOTA' + query?: ParamsData + headers?: Record + timeout?: number + maxRedirect?: number + signal?: AbortController['signal'] + json?: Record + form?: ParamsData + binary?: Buffer | Uint8Array + text?: string + formdata?: Record + xml?: string + needBody?: boolean + needRaw?: boolean + retryNum?: number +} +export interface Response { + 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 = { ...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 (url: string, options: Options = {}): Promise> => { + 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, 'raw'> as Response + } + if (options.needRaw) { + return { + headers: response.headers, + statusCode: response.statusCode, + raw: await response.body.bytes(), + } satisfies Omit, 'body'> as Response + } + // 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, 'raw'> as Response + }) +} diff --git a/src/common/utils/request_node16.ts b/src/common/utils/request_node16.ts new file mode 100644 index 00000000..364fd659 --- /dev/null +++ b/src/common/utils/request_node16.ts @@ -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 +export interface Options { + method?: + | 'GET' + | 'HEAD' + | 'POST' + | 'PUT' + | 'DELETE' + | 'OPTIONS' + | 'PATCH' + | 'PROPFIND' + | 'COPY' + | 'MOVE' + | 'MKCOL' + | 'PROPPATCH' + | 'QUOTA' + query?: ParamsData + headers?: Record + timeout?: number + maxRedirect?: number + signal?: AbortController['signal'] + json?: Record + form?: ParamsData + binary?: Buffer | Uint8Array + text?: string + formdata?: Record + xml?: string + needBody?: boolean + needRaw?: boolean + retryNum?: number +} +export interface Response { + 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 = { ...defaultOptions.headers, ...headers } + if (contentType) finalHeaders['Content-Type'] = contentType + return [finalHeaders, body] as const +} + +const buildRequestDispatcher = (options: Options) => { + return buildDispatcher() +} + +export const request = async (url: string, options: Options = {}): Promise> => { + 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, 'raw'> as Response + } + if (options.needRaw) { + return { + headers: response.headers, + statusCode: response.statusCode, + raw: new Uint8Array(await response.body.arrayBuffer()), + } satisfies Omit, 'body'> as unknown as Response + } + // 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, 'raw'> as Response + }) +} diff --git a/src/main/app.ts b/src/main/app.ts index 815a5001..09efe94d 100644 --- a/src/main/app.ts +++ b/src/main/app.ts @@ -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 = () => { diff --git a/src/main/modules/sync/client/utils.ts b/src/main/modules/sync/client/utils.ts index 488602bb..42270f79 100644 --- a/src/main/modules/sync/client/utils.ts +++ b/src/main/modules/sync/client/utils.ts @@ -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(url, { ...options, timeout: options.timeout ?? 10000, - follow_max: 5, }).then(response => { return { text: response.body, diff --git a/src/main/utils/request.ts b/src/main/utils/request.ts index 2bb080d2..956feb4a 100644 --- a/src/main/utils/request.ts +++ b/src/main/utils/request.ts @@ -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 => { - 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 (url: string, options: Options) => { + return request(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