diff --git a/backend/src/config/request.ts b/backend/src/config/request.ts index afadd1c15..2a9a8279a 100644 --- a/backend/src/config/request.ts +++ b/backend/src/config/request.ts @@ -6,7 +6,7 @@ const axiosInstance = axios.create(); // add retry functionality to the axios instance axiosRetry(axiosInstance, { retries: 3, - retryDelay: (retryCount) => retryCount * 1000, // delay between retries (in milliseconds) + retryDelay: axiosRetry.exponentialDelay, // exponential back-off delay between retries retryCondition: (error) => { // only retry if the error is a network error or a 5xx server error return axiosRetry.isNetworkError(error) || axiosRetry.isRetryableError(error); diff --git a/backend/src/controllers/v2/signupController.ts b/backend/src/controllers/v2/signupController.ts index 0fb349edc..79cb6730d 100644 --- a/backend/src/controllers/v2/signupController.ts +++ b/backend/src/controllers/v2/signupController.ts @@ -8,7 +8,7 @@ import { import { issueAuthTokens } from '../../helpers/auth'; import { INVITED, ACCEPTED } from '../../variables'; import { NODE_ENV } from '../../config'; -import axios from 'axios'; +import request from '../../config/request'; /** * Complete setting up user by adding their personal and auth information as part of the @@ -109,7 +109,7 @@ export const completeAccountSignup = async (req: Request, res: Response) => { // sending a welcome email to new users if (process.env.LOOPS_API_KEY) { - await axios.post("https://app.loops.so/api/v1/events/send", { + await request.post("https://app.loops.so/api/v1/events/send", { "email": email, "eventName": "Sign Up", "firstName": firstName, diff --git a/backend/src/integrations/apps.ts b/backend/src/integrations/apps.ts index f18734805..1df5bdaa7 100644 --- a/backend/src/integrations/apps.ts +++ b/backend/src/integrations/apps.ts @@ -1,7 +1,7 @@ -import axios from "axios"; import * as Sentry from "@sentry/node"; import { Octokit } from "@octokit/rest"; import { IIntegrationAuth } from "../models"; +import request from '../config/request'; import { INTEGRATION_AZURE_KEY_VAULT, INTEGRATION_AWS_PARAMETER_STORE, @@ -118,7 +118,7 @@ const getAppsHeroku = async ({ accessToken }: { accessToken: string }) => { let apps; try { const res = ( - await axios.get(`${INTEGRATION_HEROKU_API_URL}/apps`, { + await request.get(`${INTEGRATION_HEROKU_API_URL}/apps`, { headers: { Accept: "application/vnd.heroku+json; version=3", Authorization: `Bearer ${accessToken}`, @@ -155,7 +155,7 @@ const getAppsVercel = async ({ let apps; try { const res = ( - await axios.get(`${INTEGRATION_VERCEL_API_URL}/v9/projects`, { + await request.get(`${INTEGRATION_VERCEL_API_URL}/v9/projects`, { headers: { Authorization: `Bearer ${accessToken}`, 'Accept-Encoding': 'application/json' @@ -193,7 +193,7 @@ const getAppsNetlify = async ({ accessToken }: { accessToken: string }) => { let apps; try { const res = ( - await axios.get(`${INTEGRATION_NETLIFY_API_URL}/api/v1/sites`, { + await request.get(`${INTEGRATION_NETLIFY_API_URL}/api/v1/sites`, { headers: { Authorization: `Bearer ${accessToken}`, 'Accept-Encoding': 'application/json' @@ -264,7 +264,7 @@ const getAppsRender = async ({ accessToken }: { accessToken: string }) => { let apps: any; try { const res = ( - await axios.get(`${INTEGRATION_RENDER_API_URL}/v1/services`, { + await request.get(`${INTEGRATION_RENDER_API_URL}/v1/services`, { headers: { Authorization: `Bearer ${accessToken}`, Accept: 'application/json', @@ -310,23 +310,18 @@ const getAppsFlyio = async ({ accessToken }: { accessToken: string }) => { } `; - const res = ( - await axios({ - url: INTEGRATION_FLYIO_API_URL, - method: "post", - headers: { - Authorization: "Bearer " + accessToken, + const res = (await request.post(INTEGRATION_FLYIO_API_URL, { + query, + variables: { + role: null, + }, + }, { + headers: { + Authorization: "Bearer " + accessToken, 'Accept': 'application/json', 'Accept-Encoding': 'application/json', - }, - data: { - query, - variables: { - role: null, - }, - }, - }) - ).data.data.apps.nodes; + }, + })).data.data.apps.nodes; apps = res.map((a: any) => ({ name: a.name, @@ -351,7 +346,7 @@ const getAppsCircleCI = async ({ accessToken }: { accessToken: string }) => { let apps: any; try { const res = ( - await axios.get( + await request.get( `${INTEGRATION_CIRCLECI_API_URL}/v1.1/projects`, { headers: { diff --git a/backend/src/integrations/exchange.ts b/backend/src/integrations/exchange.ts index 301259198..56e332944 100644 --- a/backend/src/integrations/exchange.ts +++ b/backend/src/integrations/exchange.ts @@ -1,4 +1,4 @@ -import axios from 'axios'; +import request from '../config/request'; import * as Sentry from '@sentry/node'; import { INTEGRATION_AZURE_KEY_VAULT, @@ -136,7 +136,7 @@ const exchangeCodeAzure = async ({ const accessExpiresAt = new Date(); let res: ExchangeCodeAzureResponse; try { - res = (await axios.post( + res = (await request.post( INTEGRATION_AZURE_TOKEN_URL, new URLSearchParams({ grant_type: 'authorization_code', @@ -182,7 +182,7 @@ const exchangeCodeHeroku = async ({ let res: ExchangeCodeHerokuResponse; const accessExpiresAt = new Date(); try { - res = (await axios.post( + res = (await request.post( INTEGRATION_HEROKU_TOKEN_URL, new URLSearchParams({ grant_type: 'authorization_code', @@ -221,7 +221,7 @@ const exchangeCodeVercel = async ({ code }: { code: string }) => { let res: ExchangeCodeVercelResponse; try { res = ( - await axios.post( + await request.post( INTEGRATION_VERCEL_TOKEN_URL, new URLSearchParams({ code: code, @@ -260,7 +260,7 @@ const exchangeCodeNetlify = async ({ code }: { code: string }) => { let accountId; try { res = ( - await axios.post( + await request.post( INTEGRATION_NETLIFY_TOKEN_URL, new URLSearchParams({ grant_type: 'authorization_code', @@ -272,14 +272,14 @@ const exchangeCodeNetlify = async ({ code }: { code: string }) => { ) ).data; - const res2 = await axios.get('https://api.netlify.com/api/v1/sites', { + const res2 = await request.get('https://api.netlify.com/api/v1/sites', { headers: { Authorization: `Bearer ${res.access_token}` } }); const res3 = ( - await axios.get('https://api.netlify.com/api/v1/accounts', { + await request.get('https://api.netlify.com/api/v1/accounts', { headers: { Authorization: `Bearer ${res.access_token}` } @@ -314,7 +314,7 @@ const exchangeCodeGithub = async ({ code }: { code: string }) => { let res: ExchangeCodeGithubResponse; try { res = ( - await axios.get(INTEGRATION_GITHUB_TOKEN_URL, { + await request.get(INTEGRATION_GITHUB_TOKEN_URL, { params: { client_id: CLIENT_ID_GITHUB, client_secret: CLIENT_SECRET_GITHUB, diff --git a/backend/src/integrations/refresh.ts b/backend/src/integrations/refresh.ts index 4fdbcdbbb..3e5bc712c 100644 --- a/backend/src/integrations/refresh.ts +++ b/backend/src/integrations/refresh.ts @@ -1,4 +1,4 @@ -import axios from 'axios'; +import request from '../config/request'; import * as Sentry from '@sentry/node'; import { INTEGRATION_AZURE_KEY_VAULT, INTEGRATION_HEROKU } from '../variables'; import { @@ -71,7 +71,7 @@ const exchangeRefreshAzure = async ({ refreshToken: string; }) => { try { - const res: RefreshTokenAzureResponse = (await axios.post( + const res: RefreshTokenAzureResponse = (await request.post( INTEGRATION_AZURE_TOKEN_URL, new URLSearchParams({ client_id: CLIENT_ID_AZURE, @@ -105,7 +105,7 @@ const exchangeRefreshHeroku = async ({ let accessToken; try { - const res = await axios.post( + const res = await request.post( INTEGRATION_HEROKU_TOKEN_URL, new URLSearchParams({ grant_type: 'refresh_token', diff --git a/backend/src/integrations/sync.ts b/backend/src/integrations/sync.ts index 2efa285e8..5aaba3128 100644 --- a/backend/src/integrations/sync.ts +++ b/backend/src/integrations/sync.ts @@ -1,4 +1,3 @@ -import axios from "axios"; import * as Sentry from "@sentry/node"; import _ from 'lodash'; import AWS from 'aws-sdk'; @@ -32,7 +31,7 @@ import { INTEGRATION_CIRCLECI_API_URL, INTEGRATION_TRAVISCI_API_URL, } from "../variables"; -import axiosWithRetry from '../config/request'; +import request from '../config/request'; /** * Sync/push [secrets] to [app] in integration named [integration] @@ -189,7 +188,7 @@ const syncSecretsAzureKeyVault = async ({ let result: GetAzureKeyVaultSecret[] = []; while (url) { - const res = await axios.get(url, { + const res = await request.get(url, { headers: { Authorization: `Bearer ${accessToken}`, 'Accept-Encoding': 'application/json' @@ -211,7 +210,7 @@ const syncSecretsAzureKeyVault = async ({ lastSlashIndex = getAzureKeyVaultSecret.id.lastIndexOf('/'); } - const azureKeyVaultSecret = await axios.get(`${getAzureKeyVaultSecret.id}?api-version=7.3`, { + const azureKeyVaultSecret = await request.get(`${getAzureKeyVaultSecret.id}?api-version=7.3`, { headers: { 'Authorization': `Bearer ${accessToken}`, 'Accept-Encoding': 'application/json' @@ -264,7 +263,7 @@ const syncSecretsAzureKeyVault = async ({ // Sync/push set secrets if (setSecrets.length > 0) { setSecrets.forEach(async ({ key, value }) => { - await axios.put( + await request.put( `${integration.app}/secrets/${key}?api-version=7.3`, { value @@ -281,7 +280,7 @@ const syncSecretsAzureKeyVault = async ({ if (deleteSecrets.length > 0) { deleteSecrets.forEach(async (secret) => { - await axios.delete(`${integration.app}/secrets/${secret.key}?api-version=7.3`, { + await request.delete(`${integration.app}/secrets/${secret.key}?api-version=7.3`, { headers: { 'Authorization': `Bearer ${accessToken}`, 'Accept-Encoding': 'application/json' @@ -496,7 +495,7 @@ const syncSecretsHeroku = async ({ }) => { try { const herokuSecrets = ( - await axios.get( + await request.get( `${INTEGRATION_HEROKU_API_URL}/apps/${integration.app}/config-vars`, { headers: { @@ -514,7 +513,7 @@ const syncSecretsHeroku = async ({ } }); - await axios.patch( + await request.patch( `${INTEGRATION_HEROKU_API_URL}/apps/${integration.app}/config-vars`, secrets, { @@ -572,7 +571,7 @@ const syncSecretsVercel = async ({ // const res = ( // await Promise.all( // ( - // await axios.get( + // await request.get( // `${INTEGRATION_VERCEL_API_URL}/v9/projects/${integration.app}/env`, // { // params, @@ -588,7 +587,7 @@ const syncSecretsVercel = async ({ // .map(async (secret: VercelSecret) => { // if (secret.type === 'encrypted') { // // case: secret is encrypted -> need to decrypt - // const decryptedSecret = (await axios.get( + // const decryptedSecret = (await request.get( // `${INTEGRATION_VERCEL_API_URL}/v9/projects/${integration.app}/env/${secret.id}`, // { // params, @@ -608,7 +607,7 @@ const syncSecretsVercel = async ({ // [secret.key]: secret // }), {}); - const vercelSecrets: VercelSecret[] = (await axiosWithRetry.get( + const vercelSecrets: VercelSecret[] = (await request.get( `${INTEGRATION_VERCEL_API_URL}/v9/projects/${integration.app}/env`, { params, @@ -627,7 +626,7 @@ const syncSecretsVercel = async ({ for await (const vercelSecret of vercelSecrets) { if (vercelSecret.type === 'encrypted') { // case: secret is encrypted -> need to decrypt - const decryptedSecret = (await axiosWithRetry.get( + const decryptedSecret = (await request.get( `${INTEGRATION_VERCEL_API_URL}/v9/projects/${integration.app}/env/${vercelSecret.id}`, { params, @@ -690,7 +689,7 @@ const syncSecretsVercel = async ({ // Sync/push new secrets if (newSecrets.length > 0) { - await axiosWithRetry.post( + await request.post( `${INTEGRATION_VERCEL_API_URL}/v10/projects/${integration.app}/env`, newSecrets, { @@ -706,7 +705,7 @@ const syncSecretsVercel = async ({ for await (const secret of updateSecrets) { if (secret.type !== 'sensitive') { const { id, ...updatedSecret } = secret; - await axiosWithRetry.patch( + await request.patch( `${INTEGRATION_VERCEL_API_URL}/v9/projects/${integration.app}/env/${secret.id}`, updatedSecret, { @@ -721,7 +720,7 @@ const syncSecretsVercel = async ({ } for await (const secret of deleteSecrets) { - await axiosWithRetry.delete( + await request.delete( `${INTEGRATION_VERCEL_API_URL}/v9/projects/${integration.app}/env/${secret.id}`, { params, @@ -780,7 +779,7 @@ const syncSecretsNetlify = async ({ }); const res = ( - await axios.get( + await request.get( `${INTEGRATION_NETLIFY_API_URL}/api/v1/accounts/${integrationAuth.accountId}/env`, { params: getParams, @@ -894,7 +893,7 @@ const syncSecretsNetlify = async ({ }); if (newSecrets.length > 0) { - await axios.post( + await request.post( `${INTEGRATION_NETLIFY_API_URL}/api/v1/accounts/${integrationAuth.accountId}/env`, newSecrets, { @@ -909,7 +908,7 @@ const syncSecretsNetlify = async ({ if (updateSecrets.length > 0) { updateSecrets.forEach(async (secret: NetlifySecret) => { - await axios.patch( + await request.patch( `${INTEGRATION_NETLIFY_API_URL}/api/v1/accounts/${integrationAuth.accountId}/env/${secret.key}`, { context: secret.values[0].context, @@ -928,7 +927,7 @@ const syncSecretsNetlify = async ({ if (deleteSecrets.length > 0) { deleteSecrets.forEach(async (key: string) => { - await axios.delete( + await request.delete( `${INTEGRATION_NETLIFY_API_URL}/api/v1/accounts/${integrationAuth.accountId}/env/${key}`, { params: syncParams, @@ -943,7 +942,7 @@ const syncSecretsNetlify = async ({ if (deleteSecretValues.length > 0) { deleteSecretValues.forEach(async (secret: NetlifySecret) => { - await axios.delete( + await request.delete( `${INTEGRATION_NETLIFY_API_URL}/api/v1/accounts/${integrationAuth.accountId}/env/${secret.key}/value/${secret.values[0].id}`, { params: syncParams, @@ -1094,7 +1093,7 @@ const syncSecretsRender = async ({ accessToken: string; }) => { try { - await axios.put( + await request.put( `${INTEGRATION_RENDER_API_URL}/v1/services/${integration.appId}/env-vars`, Object.keys(secrets).map((key) => ({ key, @@ -1152,24 +1151,21 @@ const syncSecretsFlyio = async ({ } `; - await axios({ - url: INTEGRATION_FLYIO_API_URL, - method: "post", + await request.post(INTEGRATION_FLYIO_API_URL, { + query: SetSecrets, + variables: { + input: { + appId: integration.app, + secrets: Object.entries(secrets).map(([key, value]) => ({ + key, + value, + })), + }, + }, + }, { headers: { Authorization: "Bearer " + accessToken, - 'Accept-Encoding': 'application/json' - }, - data: { - query: SetSecrets, - variables: { - input: { - appId: integration.app, - secrets: Object.entries(secrets).map(([key, value]) => ({ - key, - value, - })), - }, - }, + 'Accept-Encoding': 'application/json', }, }); @@ -1190,23 +1186,18 @@ const syncSecretsFlyio = async ({ } }`; - const getSecretsRes = ( - await axios({ - method: "post", - url: INTEGRATION_FLYIO_API_URL, - headers: { - 'Authorization': 'Bearer ' + accessToken, - 'Content-Type': 'application/json', - 'Accept-Encoding': 'application/json' - }, - data: { - query: GetSecrets, - variables: { - appName: integration.app, - }, - }, - }) - ).data.data.app.secrets; + const getSecretsRes = (await request.post(INTEGRATION_FLYIO_API_URL, { + query: GetSecrets, + variables: { + appName: integration.app, + }, + }, { + headers: { + Authorization: "Bearer " + accessToken, + 'Content-Type': 'application/json', + 'Accept-Encoding': 'application/json', + }, + })).data.data.app.secrets; const deleteSecretsKeys = getSecretsRes .filter((secret: FlyioSecret) => !(secret.name in secrets)) @@ -1231,24 +1222,22 @@ const syncSecretsFlyio = async ({ } }`; - await axios({ - method: "post", - url: INTEGRATION_FLYIO_API_URL, + await request.post(INTEGRATION_FLYIO_API_URL, { + query: DeleteSecrets, + variables: { + input: { + appId: integration.app, + keys: deleteSecretsKeys, + }, + }, + }, { headers: { Authorization: "Bearer " + accessToken, "Content-Type": "application/json", - 'Accept-Encoding': 'application/json' - }, - data: { - query: DeleteSecrets, - variables: { - input: { - appId: integration.app, - keys: deleteSecretsKeys, - }, - }, + 'Accept-Encoding': 'application/json', }, }); + } catch (err) { Sentry.setUser(null); Sentry.captureException(err); @@ -1274,7 +1263,7 @@ const syncSecretsCircleCI = async ({ }) => { try { const circleciOrganizationDetail = ( - await axios.get(`${INTEGRATION_CIRCLECI_API_URL}/v2/me/collaborations`, { + await request.get(`${INTEGRATION_CIRCLECI_API_URL}/v2/me/collaborations`, { headers: { "Circle-Token": accessToken, "Accept-Encoding": "application/json", @@ -1287,7 +1276,7 @@ const syncSecretsCircleCI = async ({ // sync secrets to CircleCI Object.keys(secrets).forEach( async (key) => - await axios.post( + await request.post( `${INTEGRATION_CIRCLECI_API_URL}/v2/project/${slug}/${integration.app}/envvar`, { name: key, @@ -1304,7 +1293,7 @@ const syncSecretsCircleCI = async ({ // get secrets from CircleCI const getSecretsRes = ( - await axios.get( + await request.get( `${INTEGRATION_CIRCLECI_API_URL}/v2/project/${slug}/${integration.app}/envvar`, { headers: { @@ -1318,7 +1307,7 @@ const syncSecretsCircleCI = async ({ // delete secrets from CircleCI getSecretsRes.forEach(async (sec: any) => { if (!(sec.name in secrets)) { - await axios.delete( + await request.delete( `${INTEGRATION_CIRCLECI_API_URL}/v2/project/${slug}/${integration.app}/envvar/${sec.name}`, { headers: {