import { Octokit } from "@octokit/rest"; import { IIntegrationAuth } from "../models"; import { standardRequest } from "../config/request"; import { INTEGRATION_AWS_PARAMETER_STORE, INTEGRATION_AWS_SECRET_MANAGER, INTEGRATION_AZURE_KEY_VAULT, INTEGRATION_CHECKLY, INTEGRATION_CHECKLY_API_URL, INTEGRATION_CIRCLECI, INTEGRATION_CIRCLECI_API_URL, INTEGRATION_FLYIO, INTEGRATION_FLYIO_API_URL, INTEGRATION_GITHUB, INTEGRATION_GITLAB, INTEGRATION_GITLAB_API_URL, INTEGRATION_HEROKU, INTEGRATION_HEROKU_API_URL, INTEGRATION_NETLIFY, INTEGRATION_NETLIFY_API_URL, INTEGRATION_RAILWAY, INTEGRATION_RAILWAY_API_URL, INTEGRATION_RENDER, INTEGRATION_RENDER_API_URL, INTEGRATION_SUPABASE, INTEGRATION_SUPABASE_API_URL, INTEGRATION_TRAVISCI, INTEGRATION_TRAVISCI_API_URL, INTEGRATION_VERCEL, INTEGRATION_VERCEL_API_URL, } from "../variables"; interface App { name: string; appId?: string; owner?: string; } /** * Return list of names of apps for integration named [integration] * @param {Object} obj * @param {String} obj.integration - name of integration * @param {String} obj.accessToken - access token for integration * @param {String} obj.teamId - (optional) id of team for getting integration apps (used for integrations like GitLab) * @returns {Object[]} apps - names of integration apps * @returns {String} apps.name - name of integration app */ const getApps = async ({ integrationAuth, accessToken, accessId, teamId, }: { integrationAuth: IIntegrationAuth; accessToken: string; accessId?: string; teamId?: string; }) => { let apps: App[] = []; switch (integrationAuth.integration) { case INTEGRATION_AZURE_KEY_VAULT: apps = []; break; case INTEGRATION_AWS_PARAMETER_STORE: apps = []; break; case INTEGRATION_AWS_SECRET_MANAGER: apps = []; break; case INTEGRATION_HEROKU: apps = await getAppsHeroku({ accessToken, }); break; case INTEGRATION_VERCEL: apps = await getAppsVercel({ integrationAuth, accessToken, }); break; case INTEGRATION_NETLIFY: apps = await getAppsNetlify({ accessToken, }); break; case INTEGRATION_GITHUB: apps = await getAppsGithub({ accessToken, }); break; case INTEGRATION_GITLAB: apps = await getAppsGitlab({ accessToken, teamId, }); break; case INTEGRATION_RENDER: apps = await getAppsRender({ accessToken, }); break; case INTEGRATION_RAILWAY: apps = await getAppsRailway({ accessToken, }); break; case INTEGRATION_FLYIO: apps = await getAppsFlyio({ accessToken, }); break; case INTEGRATION_CIRCLECI: apps = await getAppsCircleCI({ accessToken, }); break; case INTEGRATION_TRAVISCI: apps = await getAppsTravisCI({ accessToken, }); break; case INTEGRATION_SUPABASE: apps = await getAppsSupabase({ accessToken, }); break; case INTEGRATION_CHECKLY: apps = await getAppsCheckly({ accessToken, }); break; } return apps; }; /** * Return list of apps for Heroku integration * @param {Object} obj * @param {String} obj.accessToken - access token for Heroku API * @returns {Object[]} apps - names of Heroku apps * @returns {String} apps.name - name of Heroku app */ const getAppsHeroku = async ({ accessToken }: { accessToken: string }) => { const res = ( await standardRequest.get(`${INTEGRATION_HEROKU_API_URL}/apps`, { headers: { Accept: "application/vnd.heroku+json; version=3", Authorization: `Bearer ${accessToken}`, }, }) ).data; const apps = res.map((a: any) => ({ name: a.name, })); return apps; }; /** * Return list of names of apps for Vercel integration * @param {Object} obj * @param {String} obj.accessToken - access token for Vercel API * @returns {Object[]} apps - names of Vercel apps * @returns {String} apps.name - name of Vercel app */ const getAppsVercel = async ({ integrationAuth, accessToken, }: { integrationAuth: IIntegrationAuth; accessToken: string; }) => { const res = ( await standardRequest.get(`${INTEGRATION_VERCEL_API_URL}/v9/projects`, { headers: { Authorization: `Bearer ${accessToken}`, "Accept-Encoding": "application/json", }, ...(integrationAuth?.teamId ? { params: { teamId: integrationAuth.teamId, }, } : {}), }) ).data; const apps = res.projects.map((a: any) => ({ name: a.name, appId: a.id, })); return apps; }; /** * Return list of sites for Netlify integration * @param {Object} obj * @param {String} obj.accessToken - access token for Netlify API * @returns {Object[]} apps - names of Netlify sites * @returns {String} apps.name - name of Netlify site */ const getAppsNetlify = async ({ accessToken }: { accessToken: string }) => { const apps: any = []; let page = 1; const perPage = 10; let hasMorePages = true; // paginate through all sites while (hasMorePages) { const params = new URLSearchParams({ page: String(page), per_page: String(perPage), filter: "all", }); const { data } = await standardRequest.get( `${INTEGRATION_NETLIFY_API_URL}/api/v1/sites`, { params, headers: { Authorization: `Bearer ${accessToken}`, "Accept-Encoding": "application/json", }, } ); data.map((a: any) => { apps.push({ name: a.name, appId: a.site_id, }); }); if (data.length < perPage) { hasMorePages = false; } page++; } return apps; }; /** * Return list of repositories for Github integration * @param {Object} obj * @param {String} obj.accessToken - access token for Github API * @returns {Object[]} apps - names of Github sites * @returns {String} apps.name - name of Github site */ const getAppsGithub = async ({ accessToken }: { accessToken: string }) => { interface GitHubApp { id: string; name: string; permissions: { admin: boolean; }; owner: { login: string; }; } const octokit = new Octokit({ auth: accessToken, }); const getAllRepos = async () => { let repos: GitHubApp[] = []; let page = 1; const per_page = 100; let hasMore = true; while (hasMore) { const response = await octokit.request( "GET /user/repos{?visibility,affiliation,type,sort,direction,per_page,page,since,before}", { per_page, page, } ); if (response.data.length > 0) { repos = repos.concat(response.data); page++; } else { hasMore = false; } } return repos; }; const repos = await getAllRepos(); const apps = repos .filter((a: GitHubApp) => a.permissions.admin === true) .map((a: GitHubApp) => { return { appId: a.id, name: a.name, owner: a.owner.login, }; }); return apps; }; /** * Return list of services for Render integration * @param {Object} obj * @param {String} obj.accessToken - access token for Render API * @returns {Object[]} apps - names and ids of Render services * @returns {String} apps.name - name of Render service * @returns {String} apps.appId - id of Render service */ const getAppsRender = async ({ accessToken }: { accessToken: string }) => { const res = ( await standardRequest.get(`${INTEGRATION_RENDER_API_URL}/v1/services`, { headers: { Authorization: `Bearer ${accessToken}`, Accept: "application/json", "Accept-Encoding": "application/json", }, }) ).data; const apps = res.map((a: any) => ({ name: a.service.name, appId: a.service.id, })); return apps; }; /** * Return list of projects for Railway integration * @param {Object} obj * @param {String} obj.accessToken - access token for Railway API * @returns {Object[]} apps - names and ids of Railway services * @returns {String} apps.name - name of Railway project * @returns {String} apps.appId - id of Railway project * */ const getAppsRailway = async ({ accessToken }: { accessToken: string }) => { const query = ` query GetProjects($userId: String, $teamId: String) { projects(userId: $userId, teamId: $teamId) { edges { node { id name } } } } `; const variables = {}; const { data: { data: { projects: { edges }, }, }, } = await standardRequest.post( INTEGRATION_RAILWAY_API_URL, { query, variables, }, { headers: { Authorization: `Bearer ${accessToken}`, "Content-Type": "application/json", "Accept-Encoding": "application/json", }, } ); const apps = edges.map((e: any) => ({ name: e.node.name, appId: e.node.id, })); return apps; }; /** * Return list of apps for Fly.io integration * @param {Object} obj * @param {String} obj.accessToken - access token for Fly.io API * @returns {Object[]} apps - names and ids of Fly.io apps * @returns {String} apps.name - name of Fly.io apps */ const getAppsFlyio = async ({ accessToken }: { accessToken: string }) => { const query = ` query($role: String) { apps(type: "container", first: 400, role: $role) { nodes { id name hostname } } } `; const res = ( await standardRequest.post( INTEGRATION_FLYIO_API_URL, { query, variables: { role: null, }, }, { headers: { Authorization: "Bearer " + accessToken, Accept: "application/json", "Accept-Encoding": "application/json", }, } ) ).data.data.apps.nodes; const apps = res.map((a: any) => ({ name: a.name, })); return apps; }; /** * Return list of projects for CircleCI integration * @param {Object} obj * @param {String} obj.accessToken - access token for CircleCI API * @returns {Object[]} apps - * @returns {String} apps.name - name of CircleCI apps */ const getAppsCircleCI = async ({ accessToken }: { accessToken: string }) => { const res = ( await standardRequest.get(`${INTEGRATION_CIRCLECI_API_URL}/v1.1/projects`, { headers: { "Circle-Token": accessToken, "Accept-Encoding": "application/json", }, }) ).data; const apps = res?.map((a: any) => { return { name: a?.reponame, }; }); return apps; }; const getAppsTravisCI = async ({ accessToken }: { accessToken: string }) => { const res = ( await standardRequest.get(`${INTEGRATION_TRAVISCI_API_URL}/repos`, { headers: { Authorization: `token ${accessToken}`, "Accept-Encoding": "application/json", }, }) ).data; const apps = res?.map((a: any) => { return { name: a?.slug?.split("/")[1], appId: a?.id, }; }); return apps; }; /** * Return list of repositories for GitLab integration * @param {Object} obj * @param {String} obj.accessToken - access token for GitLab API * @returns {Object[]} apps - names of GitLab sites * @returns {String} apps.name - name of GitLab site */ const getAppsGitlab = async ({ accessToken, teamId, }: { accessToken: string; teamId?: string; }) => { const apps: App[] = []; let page = 1; const perPage = 10; let hasMorePages = true; if (teamId) { // case: fetch projects for group with id [teamId] in GitLab while (hasMorePages) { const params = new URLSearchParams({ page: String(page), per_page: String(perPage), }); const { data } = await standardRequest.get( `${INTEGRATION_GITLAB_API_URL}/v4/groups/${teamId}/projects`, { params, headers: { Authorization: `Bearer ${accessToken}`, "Accept-Encoding": "application/json", }, } ); data.map((a: any) => { apps.push({ name: a.name, appId: a.id, }); }); if (data.length < perPage) { hasMorePages = false; } page++; } } else { // case: fetch projects for individual in GitLab const { id } = ( await standardRequest.get(`${INTEGRATION_GITLAB_API_URL}/v4/user`, { headers: { Authorization: `Bearer ${accessToken}`, "Accept-Encoding": "application/json", }, }) ).data; while (hasMorePages) { const params = new URLSearchParams({ page: String(page), per_page: String(perPage), }); const { data } = await standardRequest.get( `${INTEGRATION_GITLAB_API_URL}/v4/users/${id}/projects`, { params, headers: { Authorization: `Bearer ${accessToken}`, "Accept-Encoding": "application/json", }, } ); data.map((a: any) => { apps.push({ name: a.name, appId: a.id, }); }); if (data.length < perPage) { hasMorePages = false; } page++; } } return apps; }; /** * Return list of projects for Supabase integration * @param {Object} obj * @param {String} obj.accessToken - access token for Supabase API * @returns {Object[]} apps - names of Supabase apps * @returns {String} apps.name - name of Supabase app */ const getAppsSupabase = async ({ accessToken }: { accessToken: string }) => { const { data } = await standardRequest.get( `${INTEGRATION_SUPABASE_API_URL}/v1/projects`, { headers: { Authorization: `Bearer ${accessToken}`, "Accept-Encoding": "application/json", }, } ); const apps = data.map((a: any) => { return { name: a.name, appId: a.id, }; }); return apps; }; /** * Return list of projects for the Checkly integration * @param {Object} obj * @param {String} obj.accessToken - api key for the Checkly API * @returns {Object[]} apps - Сheckly accounts * @returns {String} apps.name - name of Checkly account */ const getAppsCheckly = async ({ accessToken }: { accessToken: string }) => { const { data } = await standardRequest.get( `${INTEGRATION_CHECKLY_API_URL}/v1/accounts`, { headers: { Authorization: `Bearer ${accessToken}`, "Accept": "application/json", }, } ); const apps = data.map((a: any) => { return { name: a.name, appId: a.id, }; }); return apps; }; export { getApps };