From a756aacb84810f7372eef2348402c951437a7966 Mon Sep 17 00:00:00 2001 From: Mateusz Lis Date: Thu, 29 Feb 2024 18:39:23 +0100 Subject: [PATCH 1/3] Reapply "fix: add https-proxy-agent to fetch requests (#1433)" (#1458) This reverts commit 93e42db06280e72b7f655be84e25389ae91bd256. --- docs/installation.md | 24 ++++++++ package-lock.json | 44 +++++++++++++++ .../src/__mocks__/@redocly/openapi-core.ts | 1 + .../src/cms/api/__tests__/api.client.test.ts | 3 + packages/cli/src/cms/api/api-client.ts | 23 ++++++-- packages/cli/src/commands/push.ts | 11 +++- packages/cli/src/utils/fetch-with-timeout.ts | 7 ++- packages/core/package.json | 1 + packages/core/src/bundle.ts | 2 +- packages/core/src/config/config.ts | 24 +------- packages/core/src/config/load.ts | 3 +- .../common/registry-dependencies.ts | 2 +- packages/core/src/index.ts | 13 ++++- .../src/redocly/__tests__/domains.test.ts | 52 +++++++++++++++++ .../redocly/__tests__/redocly-client.test.ts | 8 ++- packages/core/src/redocly/domains.ts | 48 ++++++++++++++++ packages/core/src/redocly/index.ts | 38 +++++-------- packages/core/src/redocly/registry-api.ts | 56 +++++++++---------- packages/core/src/utils.ts | 8 ++- 19 files changed, 273 insertions(+), 95 deletions(-) create mode 100644 packages/core/src/redocly/__tests__/domains.test.ts create mode 100644 packages/core/src/redocly/domains.ts diff --git a/docs/installation.md b/docs/installation.md index a008a0725e..8571d81f12 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -78,6 +78,30 @@ To give a Docker container access to your OpenAPI description files, you need to docker run --rm -v $PWD:/spec redocly/cli lint openapi.yaml ``` +## Run CLI behind a proxy + +If you need to run the CLI tool behind a proxy, you can use the `HTTP_PROXY` and `HTTPS_PROXY` environment variables to configure the proxy settings. These environment variables are commonly used to specify the proxy server for HTTP and HTTPS traffic, respectively. + +### Set up Proxy Environment Variables + +Before running the CLI behind a proxy, make sure to set the appropriate proxy environment variables. Open a terminal and use the following commands: + +```bash +# For HTTP proxy +export HTTP_PROXY=http://your-http-proxy-server:port + +# For HTTPS proxy +export HTTPS_PROXY=http://your-https-proxy-server:port +``` + +### Use Environment Variables with CLI Commands + +You can also directly include the proxy environment variables in the command itself. For example: + +```bash +HTTPS_PROXY=https://your-https-proxy-server:port redocly lint --extends minimal openapi.yaml +``` + ## Next steps - Set up [autocomplete for Redocly CLI](./guides/autocomplete.md). diff --git a/package-lock.json b/package-lock.json index 347c878db9..cd6e6cd229 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12945,6 +12945,7 @@ "dependencies": { "@redocly/ajv": "^8.11.0", "colorette": "^1.2.0", + "https-proxy-agent": "^7.0.4", "js-levenshtein": "^1.1.6", "js-yaml": "^4.1.0", "lodash.isequal": "^4.5.0", @@ -12967,6 +12968,29 @@ "node": ">=14.19.0", "npm": ">=7.0.0" } + }, + "packages/core/node_modules/agent-base": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz", + "integrity": "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==", + "dependencies": { + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "packages/core/node_modules/https-proxy-agent": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.4.tgz", + "integrity": "sha512-wlwpilI7YdjSkWaQ/7omYBMTliDcmCN8OLihO6I9B86g06lMyAoqgoDpV0XqoaPOKj+0DIdAvnsWfyAAhmimcg==", + "dependencies": { + "agent-base": "^7.0.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } } }, "dependencies": { @@ -15610,6 +15634,7 @@ "@types/node-fetch": "^2.5.7", "@types/pluralize": "^0.0.29", "colorette": "^1.2.0", + "https-proxy-agent": "^7.0.4", "js-levenshtein": "^1.1.6", "js-yaml": "^4.1.0", "lodash.isequal": "^4.5.0", @@ -15618,6 +15643,25 @@ "pluralize": "^8.0.0", "typescript": "^5.2.2", "yaml-ast-parser": "0.0.43" + }, + "dependencies": { + "agent-base": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz", + "integrity": "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==", + "requires": { + "debug": "^4.3.4" + } + }, + "https-proxy-agent": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.4.tgz", + "integrity": "sha512-wlwpilI7YdjSkWaQ/7omYBMTliDcmCN8OLihO6I9B86g06lMyAoqgoDpV0XqoaPOKj+0DIdAvnsWfyAAhmimcg==", + "requires": { + "agent-base": "^7.0.2", + "debug": "4" + } + } } }, "@sinclair/typebox": { diff --git a/packages/cli/src/__mocks__/@redocly/openapi-core.ts b/packages/cli/src/__mocks__/@redocly/openapi-core.ts index 29dc9642c0..e89a8d75b5 100644 --- a/packages/cli/src/__mocks__/@redocly/openapi-core.ts +++ b/packages/cli/src/__mocks__/@redocly/openapi-core.ts @@ -21,6 +21,7 @@ export const __redoclyClient = { export const RedoclyClient = jest.fn(() => __redoclyClient); export const loadConfig = jest.fn(() => ConfigFixture); export const getMergedConfig = jest.fn(); +export const getProxyAgent = jest.fn(); export const lint = jest.fn(); export const bundle = jest.fn(() => ({ bundle: { parsed: null }, problems: null })); export const getTotals = jest.fn(() => ({ errors: 0 })); diff --git a/packages/cli/src/cms/api/__tests__/api.client.test.ts b/packages/cli/src/cms/api/__tests__/api.client.test.ts index f654ad70aa..cb7a2c13ef 100644 --- a/packages/cli/src/cms/api/__tests__/api.client.test.ts +++ b/packages/cli/src/cms/api/__tests__/api.client.test.ts @@ -42,6 +42,7 @@ describe('ApiClient', () => { 'Content-Type': 'application/json', Authorization: `Bearer ${testToken}`, }, + signal: expect.any(Object), } ); @@ -122,6 +123,8 @@ describe('ApiClient', () => { type: 'CICD', autoMerge: true, }), + signal: expect.any(Object), + agent: undefined, } ); diff --git a/packages/cli/src/cms/api/api-client.ts b/packages/cli/src/cms/api/api-client.ts index 671da6c2c6..4d9b5b20c0 100644 --- a/packages/cli/src/cms/api/api-client.ts +++ b/packages/cli/src/cms/api/api-client.ts @@ -1,7 +1,7 @@ import fetchWithTimeout from '../../utils/fetch-with-timeout'; import fetch from 'node-fetch'; import * as FormData from 'form-data'; - +import { getProxyAgent } from '@redocly/openapi-core'; import type { Response } from 'node-fetch'; import type { ReadStream } from 'fs'; import type { @@ -25,7 +25,7 @@ class RemotesApiClient { } async getDefaultBranch(organizationId: string, projectId: string) { - const response = await fetch( + const response = await fetchWithTimeout( `${this.domain}/api/orgs/${organizationId}/projects/${projectId}/source`, { method: 'GET', @@ -36,6 +36,10 @@ class RemotesApiClient { } ); + if (!response) { + throw new Error(`Failed to get default branch.`); + } + try { const source = await this.getParsedResponse(response); @@ -53,7 +57,7 @@ class RemotesApiClient { mountBranchName: string; } ): Promise { - const response = await fetch( + const response = await fetchWithTimeout( `${this.domain}/api/orgs/${organizationId}/projects/${projectId}/remotes`, { method: 'POST', @@ -70,6 +74,10 @@ class RemotesApiClient { } ); + if (!response) { + throw new Error(`Failed to upsert.`); + } + try { return await this.getParsedResponse(response); } catch (err) { @@ -110,6 +118,7 @@ class RemotesApiClient { Authorization: `Bearer ${this.apiKey}`, }, body: formData, + agent: getProxyAgent(), } ); @@ -121,7 +130,7 @@ class RemotesApiClient { } async getRemotesList(organizationId: string, projectId: string, mountPath: string) { - const response = await fetch( + const response = await fetchWithTimeout( `${this.domain}/api/orgs/${organizationId}/projects/${projectId}/remotes?filter=mountPath:/${mountPath}/`, { method: 'GET', @@ -132,6 +141,10 @@ class RemotesApiClient { } ); + if (!response) { + throw new Error(`Failed to get remotes list.`); + } + try { return await this.getParsedResponse(response); } catch (err) { @@ -160,7 +173,7 @@ class RemotesApiClient { ); if (!response) { - throw new Error(`Failed to get push status: Time is up`); + throw new Error(`Failed to get push status.`); } try { diff --git a/packages/cli/src/commands/push.ts b/packages/cli/src/commands/push.ts index 1be48c16e7..7ebd3c8746 100644 --- a/packages/cli/src/commands/push.ts +++ b/packages/cli/src/commands/push.ts @@ -14,6 +14,7 @@ import { slash, Region, getMergedConfig, + getProxyAgent, } from '@redocly/openapi-core'; import { exitWithError, @@ -62,8 +63,12 @@ export async function handlePush(argv: PushOptions, config: Config): Promise { controller.abort(); }, TIMEOUT); - const res = await nodeFetch(url, { signal: controller.signal, ...options }); + const res = await nodeFetch(url, { + signal: controller.signal, + ...options, + agent: getProxyAgent(), + }); clearTimeout(timeout); return res; } catch (e) { diff --git a/packages/core/package.json b/packages/core/package.json index 48ad6afe1d..cafe40e9c3 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -36,6 +36,7 @@ "dependencies": { "@redocly/ajv": "^8.11.0", "colorette": "^1.2.0", + "https-proxy-agent": "^7.0.4", "js-levenshtein": "^1.1.6", "js-yaml": "^4.1.0", "lodash.isequal": "^4.5.0", diff --git a/packages/core/src/bundle.ts b/packages/core/src/bundle.ts index 0c80f6e71d..2c6a45809a 100755 --- a/packages/core/src/bundle.ts +++ b/packages/core/src/bundle.ts @@ -14,7 +14,7 @@ import { isAbsoluteUrl, isRef, Location, refBaseName } from './ref-utils'; import { initRules } from './config/rules'; import { reportUnresolvedRef } from './rules/no-unresolved-refs'; import { isPlainObject, isTruthy } from './utils'; -import { isRedoclyRegistryURL } from './redocly'; +import { isRedoclyRegistryURL } from './redocly/domains'; import { RemoveUnusedComponents as RemoveUnusedComponentsOas2 } from './decorators/oas2/remove-unused-components'; import { RemoveUnusedComponents as RemoveUnusedComponentsOas3 } from './decorators/oas3/remove-unused-components'; import { ConfigTypes } from './types/redocly-yaml'; diff --git a/packages/core/src/config/config.ts b/packages/core/src/config/config.ts index 3728372ffd..cda45c6c05 100755 --- a/packages/core/src/config/config.ts +++ b/packages/core/src/config/config.ts @@ -10,7 +10,7 @@ import { Oas3RuleSet, Async2RuleSet, } from '../oas-types'; -import { isBrowser, env } from '../env'; +import { isBrowser } from '../env'; import type { NodeType } from '../types'; import type { @@ -35,25 +35,6 @@ const IGNORE_BANNER = `# This file instructs Redocly's linter to ignore the rules contained for specific parts of your API.\n` + `# See https://redoc.ly/docs/cli/ for more information.\n`; -export const DEFAULT_REGION = 'us'; - -function getDomains() { - const domains: { [region in Region]: string } = { - us: 'redocly.com', - eu: 'eu.redocly.com', - }; - - // FIXME: temporary fix for our lab environments - const domain = env.REDOCLY_DOMAIN; - if (domain?.endsWith('.redocly.host')) { - domains[domain.split('.')[0] as Region] = domain; - } - if (domain === 'redoc.online') { - domains[domain as Region] = domain; - } - return domains; -} - function getIgnoreFilePath(configFile?: string): string | undefined { if (configFile) { return doesYamlFileExist(configFile) @@ -64,9 +45,6 @@ function getIgnoreFilePath(configFile?: string): string | undefined { } } -export const DOMAINS = getDomains(); -export const AVAILABLE_REGIONS = Object.keys(DOMAINS) as Region[]; - export class StyleguideConfig { plugins: Plugin[]; ignore: Record>> = {}; diff --git a/packages/core/src/config/load.ts b/packages/core/src/config/load.ts index c7ba384fa9..0ff289635d 100644 --- a/packages/core/src/config/load.ts +++ b/packages/core/src/config/load.ts @@ -3,7 +3,7 @@ import * as path from 'path'; import { RedoclyClient } from '../redocly'; import { isEmptyObject, doesYamlFileExist } from '../utils'; import { parseYaml } from '../js-yaml'; -import { Config, DOMAINS } from './config'; +import { Config } from './config'; import { ConfigValidationError, transformConfig } from './utils'; import { resolveConfig, resolveConfigFileAndRefs } from './config-resolvers'; import { bundleConfig } from '../bundle'; @@ -12,6 +12,7 @@ import type { Document } from '../resolve'; import type { RegionalTokenWithValidity } from '../redocly/redocly-client-types'; import type { RawConfig, RawUniversalConfig, Region } from './types'; import type { BaseResolver, ResolvedRefMap } from '../resolve'; +import { DOMAINS } from '../redocly/domains'; async function addConfigMetadata({ rawConfig, diff --git a/packages/core/src/decorators/common/registry-dependencies.ts b/packages/core/src/decorators/common/registry-dependencies.ts index 2e58f0bb55..007361b9b2 100644 --- a/packages/core/src/decorators/common/registry-dependencies.ts +++ b/packages/core/src/decorators/common/registry-dependencies.ts @@ -1,5 +1,5 @@ import { UserContext } from '../../walk'; -import { isRedoclyRegistryURL } from '../../redocly'; +import { isRedoclyRegistryURL } from '../../redocly/domains'; import { Oas3Decorator, Oas2Decorator } from '../../visitors'; diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index cfdf870db1..d500311ab7 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -1,4 +1,11 @@ -export { BundleOutputFormat, readFileFromUrl, slash, doesYamlFileExist, isTruthy } from './utils'; +export { + BundleOutputFormat, + readFileFromUrl, + slash, + doesYamlFileExist, + isTruthy, + getProxyAgent, +} from './utils'; export { Oas3_1Types } from './types/oas3_1'; export { Oas3Types } from './types/oas3'; export { Oas2Types } from './types/oas2'; @@ -41,7 +48,9 @@ export { ResolvedApi, } from './config'; -export { RedoclyClient, isRedoclyRegistryURL } from './redocly'; +export { RedoclyClient } from './redocly'; + +export * from './redocly/domains'; export { Source, diff --git a/packages/core/src/redocly/__tests__/domains.test.ts b/packages/core/src/redocly/__tests__/domains.test.ts new file mode 100644 index 0000000000..848eb0a9c9 --- /dev/null +++ b/packages/core/src/redocly/__tests__/domains.test.ts @@ -0,0 +1,52 @@ +import { RedoclyClient } from '../../index'; +import { setRedoclyDomain, getRedoclyDomain, getDomains, AVAILABLE_REGIONS } from '../domains'; + +describe('domains', () => { + const REDOCLY_DOMAIN_US = 'redocly.com'; + const TEST_DOMAIN = 'redoclyDomain.com'; + const TEST_LAB_DOMAIN = 'lab.redocly.host'; + const TEST_REDOC_ONLINE_DOMAIN = 'redoc.online'; + + afterEach(() => { + delete process.env.REDOCLY_DOMAIN; + setRedoclyDomain(''); + }); + + it('should resolve the US domain by default', () => { + expect(getRedoclyDomain()).toBe(REDOCLY_DOMAIN_US); + }); + + it('should resolve the US and EU regions by default', () => { + expect(AVAILABLE_REGIONS).toStrictEqual(['us', 'eu']); + }); + + it('should resolve the specified domain if used with setter', () => { + setRedoclyDomain(TEST_DOMAIN); + expect(getRedoclyDomain()).toBe(TEST_DOMAIN); + }); + + it('should resolve the specified domain provided in environmental variable, after initializing RedoclyClient', () => { + process.env.REDOCLY_DOMAIN = TEST_DOMAIN; + const client = new RedoclyClient(); + expect(getRedoclyDomain()).toBe(TEST_DOMAIN); + expect(client.domain).toBe(TEST_DOMAIN); + }); + + it('should return correct object when redocly domain is set to lab env', () => { + setRedoclyDomain(TEST_LAB_DOMAIN); + const domains = getDomains(); + expect(domains).toEqual({ us: 'redocly.com', eu: 'eu.redocly.com', lab: 'lab.redocly.host' }); + expect(getRedoclyDomain()).toBe(TEST_LAB_DOMAIN); + }); + + it('should return correct object when redocly domain is set to redoc.online env', () => { + setRedoclyDomain(TEST_REDOC_ONLINE_DOMAIN); + const domains = getDomains(); + expect(domains).toEqual({ + us: 'redocly.com', + eu: 'eu.redocly.com', + 'redoc.online': 'redoc.online', + }); + expect(getRedoclyDomain()).toBe(TEST_REDOC_ONLINE_DOMAIN); + }); +}); diff --git a/packages/core/src/redocly/__tests__/redocly-client.test.ts b/packages/core/src/redocly/__tests__/redocly-client.test.ts index 0af0aaf59d..0e19752d6c 100644 --- a/packages/core/src/redocly/__tests__/redocly-client.test.ts +++ b/packages/core/src/redocly/__tests__/redocly-client.test.ts @@ -1,3 +1,4 @@ +import { setRedoclyDomain } from '../domains'; import { RedoclyClient } from '../index'; jest.mock('node-fetch', () => ({ @@ -16,6 +17,7 @@ describe('RedoclyClient', () => { afterEach(() => { delete process.env.REDOCLY_DOMAIN; + setRedoclyDomain(''); }); it('should resolve the US domain by default', () => { @@ -40,19 +42,19 @@ describe('RedoclyClient', () => { }); it('should resolve domain by EU region prioritizing flag over env variable', () => { - process.env.REDOCLY_DOMAIN = testRedoclyDomain; + setRedoclyDomain(testRedoclyDomain); const client = new RedoclyClient('eu'); expect(client.domain).toBe(REDOCLY_DOMAIN_EU); }); it('should resolve domain by US region prioritizing flag over env variable', () => { - process.env.REDOCLY_DOMAIN = testRedoclyDomain; + setRedoclyDomain(testRedoclyDomain); const client = new RedoclyClient('us'); expect(client.domain).toBe(REDOCLY_DOMAIN_US); }); it('should resolve domain by US region when REDOCLY_DOMAIN consists EU domain', () => { - process.env.REDOCLY_DOMAIN = REDOCLY_DOMAIN_EU; + setRedoclyDomain(REDOCLY_DOMAIN_EU); const client = new RedoclyClient(); expect(client.getRegion()).toBe('eu'); }); diff --git a/packages/core/src/redocly/domains.ts b/packages/core/src/redocly/domains.ts new file mode 100644 index 0000000000..3fa6856fdf --- /dev/null +++ b/packages/core/src/redocly/domains.ts @@ -0,0 +1,48 @@ +import { Region } from '../config/types'; + +let redoclyDomain = 'redocly.com'; + +export const DEFAULT_REGION = 'us'; + +export const DOMAINS = getDomains(); +export const AVAILABLE_REGIONS = Object.keys(DOMAINS) as Region[]; + +export function getDomains() { + const domains: { [region in Region]: string } = { + us: 'redocly.com', + eu: 'eu.redocly.com', + }; + + // FIXME: temporary fix for our lab environments + const domain = redoclyDomain; + if (domain?.endsWith('.redocly.host')) { + domains[domain.split('.')[0] as Region] = domain; + } + if (domain === 'redoc.online') { + domains[domain as Region] = domain; + } + return domains; +} + +export function setRedoclyDomain(domain: string) { + redoclyDomain = domain; +} + +export function getRedoclyDomain(): string { + return redoclyDomain; +} + +export function isRedoclyRegistryURL(link: string): boolean { + const domain = getRedoclyDomain() || DOMAINS[DEFAULT_REGION]; + + const legacyDomain = domain === 'redocly.com' ? 'redoc.ly' : domain; + + if ( + !link.startsWith(`https://api.${domain}/registry/`) && + !link.startsWith(`https://api.${legacyDomain}/registry/`) + ) { + return false; + } + + return true; +} diff --git a/packages/core/src/redocly/index.ts b/packages/core/src/redocly/index.ts index 4b1de8b63f..df28bdd60e 100644 --- a/packages/core/src/redocly/index.ts +++ b/packages/core/src/redocly/index.ts @@ -2,13 +2,19 @@ import { existsSync, readFileSync, writeFileSync, unlinkSync } from 'fs'; import { resolve } from 'path'; import { homedir } from 'os'; import { RegistryApi } from './registry-api'; -import { DEFAULT_REGION, DOMAINS, AVAILABLE_REGIONS } from '../config/config'; import { env } from '../env'; import { RegionalToken, RegionalTokenWithValidity } from './redocly-client-types'; import { isNotEmptyObject } from '../utils'; import { colorize } from '../logger'; import type { AccessTokens, Region } from '../config/types'; +import { + AVAILABLE_REGIONS, + DEFAULT_REGION, + DOMAINS, + getRedoclyDomain, + setRedoclyDomain, +} from './domains'; export const TOKEN_FILENAME = '.redocly-config.json'; @@ -23,7 +29,7 @@ export class RedoclyClient { this.loadTokens(); this.domain = region ? DOMAINS[region] : env.REDOCLY_DOMAIN || DOMAINS[DEFAULT_REGION]; - env.REDOCLY_DOMAIN = this.domain; // isRedoclyRegistryURL depends on the value to be set + setRedoclyDomain(this.domain); this.registryApi = new RegistryApi(this.accessTokens, this.region); } @@ -36,9 +42,9 @@ export class RedoclyClient { ); } - if (env.REDOCLY_DOMAIN) { + if (getRedoclyDomain()) { return (AVAILABLE_REGIONS.find( - (region) => DOMAINS[region as Region] === env.REDOCLY_DOMAIN + (region) => DOMAINS[region as Region] === getRedoclyDomain() ) || DEFAULT_REGION) as Region; } return region || DEFAULT_REGION; @@ -96,7 +102,7 @@ export class RedoclyClient { const allTokens = this.getAllTokens(); const verifiedTokens = await Promise.allSettled( - allTokens.map(({ token, region }) => this.verifyToken(token, region)) + allTokens.map(({ token }) => this.verifyToken(token)) ); return allTokens @@ -120,7 +126,7 @@ export class RedoclyClient { } try { - await this.verifyToken(accessToken, this.region); + await this.verifyToken(accessToken); return true; } catch (err) { @@ -138,17 +144,16 @@ export class RedoclyClient { async verifyToken( accessToken: string, - region: Region, verbose: boolean = false ): Promise<{ viewerId: string; organizations: string[] }> { - return this.registryApi.authStatus(accessToken, region, verbose); + return this.registryApi.authStatus(accessToken, verbose); } async login(accessToken: string, verbose: boolean = false) { const credentialsPath = resolve(homedir(), TOKEN_FILENAME); try { - await this.verifyToken(accessToken, this.region, verbose); + await this.verifyToken(accessToken, verbose); } catch (err) { throw new Error('Authorization failed. Please check if you entered a valid API key.'); } @@ -170,18 +175,3 @@ export class RedoclyClient { } } } - -export function isRedoclyRegistryURL(link: string): boolean { - const domain = env.REDOCLY_DOMAIN || DOMAINS[DEFAULT_REGION]; - - const legacyDomain = domain === 'redocly.com' ? 'redoc.ly' : domain; - - if ( - !link.startsWith(`https://api.${domain}/registry/`) && - !link.startsWith(`https://api.${legacyDomain}/registry/`) - ) { - return false; - } - - return true; -} diff --git a/packages/core/src/redocly/registry-api.ts b/packages/core/src/redocly/registry-api.ts index 63ce39a75e..5d77c6a446 100644 --- a/packages/core/src/redocly/registry-api.ts +++ b/packages/core/src/redocly/registry-api.ts @@ -6,8 +6,8 @@ import type { PushApiParams, } from './registry-api-types'; import type { AccessTokens, Region } from '../config/types'; -import { DEFAULT_REGION, DOMAINS } from '../config/config'; -import { isNotEmptyObject } from '../utils'; +import { getProxyAgent, isNotEmptyObject } from '../utils'; +import { getRedoclyDomain } from './domains'; const version = require('../../package.json').version; @@ -18,8 +18,8 @@ export class RegistryApi { return isNotEmptyObject(this.accessTokens) && this.accessTokens[this.region]; } - getBaseUrl(region: Region = DEFAULT_REGION) { - return `https://api.${DOMAINS[region]}/registry`; + getBaseUrl() { + return `https://api.${getRedoclyDomain()}/registry`; } setAccessTokens(accessTokens: AccessTokens) { @@ -27,7 +27,7 @@ export class RegistryApi { return this; } - private async request(path = '', options: RequestInit = {}, region?: Region) { + private async request(path = '', options: RequestInit = {}) { const currentCommand = typeof process !== 'undefined' ? process.env?.REDOCLY_CLI_COMMAND || '' : ''; const redoclyEnv = typeof process !== 'undefined' ? process.env?.REDOCLY_ENVIRONMENT || '' : ''; @@ -42,8 +42,8 @@ export class RegistryApi { } const response = await fetch( - `${this.getBaseUrl(region)}${path}`, - Object.assign({}, options, { headers }) + `${this.getBaseUrl()}${path}`, + Object.assign({}, options, { headers, agent: getProxyAgent() }) ); if (response.status === 401) { @@ -60,11 +60,10 @@ export class RegistryApi { async authStatus( accessToken: string, - region: Region, verbose = false ): Promise<{ viewerId: string; organizations: string[] }> { try { - const response = await this.request('', { headers: { authorization: accessToken } }, region); + const response = await this.request('', { headers: { authorization: accessToken } }); return await response.json(); } catch (error) { @@ -97,8 +96,7 @@ export class RegistryApi { filename, isUpsert, }), - }, - this.region + } ); if (response.ok) { @@ -120,26 +118,22 @@ export class RegistryApi { batchId, batchSize, }: PushApiParams) { - const response = await this.request( - `/${organizationId}/${name}/${version}`, - { - method: 'PUT', - headers: { - 'content-type': 'application/json', - authorization: this.accessToken, - } as HeadersInit, - body: JSON.stringify({ - rootFilePath, - filePaths, - branch, - isUpsert, - isPublic, - batchId, - batchSize, - }), - }, - this.region - ); + const response = await this.request(`/${organizationId}/${name}/${version}`, { + method: 'PUT', + headers: { + 'content-type': 'application/json', + authorization: this.accessToken, + } as HeadersInit, + body: JSON.stringify({ + rootFilePath, + filePaths, + branch, + isUpsert, + isPublic, + batchId, + batchSize, + }), + }); if (response.ok) { return; diff --git a/packages/core/src/utils.ts b/packages/core/src/utils.ts index 33034e14c3..a4b195a387 100644 --- a/packages/core/src/utils.ts +++ b/packages/core/src/utils.ts @@ -5,9 +5,10 @@ import fetch from 'node-fetch'; import * as pluralize from 'pluralize'; import { parseYaml } from './js-yaml'; import { UserContext } from './walk'; -import { HttpResolveConfig } from './config'; import { env } from './env'; import { logger, colorize } from './logger'; +import { HttpResolveConfig } from './config'; +import { HttpsProxyAgent } from 'https-proxy-agent'; export { parseYaml, stringifyYaml } from './js-yaml'; @@ -274,3 +275,8 @@ export function nextTick() { function getUpdatedFieldName(updatedField: string, updatedObject?: string) { return `${typeof updatedObject !== 'undefined' ? `${updatedObject}.` : ''}${updatedField}`; } + +export function getProxyAgent() { + const proxy = process.env.HTTPS_PROXY || process.env.HTTP_PROXY; + return proxy ? new HttpsProxyAgent(proxy) : undefined; +} From 47e2780083c5e676314f67b63ee9dcc982b25c7b Mon Sep 17 00:00:00 2001 From: Mateusz Lis Date: Thu, 29 Feb 2024 18:40:32 +0100 Subject: [PATCH 2/3] fix: add https-proxy-agent package to browser excluded packages --- packages/core/package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/core/package.json b/packages/core/package.json index cafe40e9c3..46b578f997 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -18,7 +18,8 @@ "path": "path-browserify", "os": false, "node-fetch": false, - "colorette": false + "colorette": false, + "https-proxy-agent": false }, "homepage": "https://github.com/Redocly/redocly-cli", "keywords": [ From 3df5ffe53664fc60fd456f07d921072a495832af Mon Sep 17 00:00:00 2001 From: Mateusz Lis Date: Thu, 29 Feb 2024 18:46:31 +0100 Subject: [PATCH 3/3] fix: add changeset --- .changeset/wild-pets-report.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changeset/wild-pets-report.md diff --git a/.changeset/wild-pets-report.md b/.changeset/wild-pets-report.md new file mode 100644 index 0000000000..69c0ebb3a4 --- /dev/null +++ b/.changeset/wild-pets-report.md @@ -0,0 +1,6 @@ +--- +"@redocly/openapi-core": patch +"@redocly/cli": patch +--- + +Users can run the CLI tool behind a proxy by using `HTTP_PROXY` or `HTTPS_PROXY` environment variables to configure the proxy settings.