You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
docker-infisical/backend/src/integrations/apps.ts

722 lines
16 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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_CLOUDFLARE_PAGES,
INTEGRATION_CLOUDFLARE_PAGES_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_NORTHFLANK,
INTEGRATION_NORTHFLANK_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;
case INTEGRATION_CLOUDFLARE_PAGES:
apps = await getAppsCloudflarePages({
accessToken,
accountId: accessId
});
break;
case INTEGRATION_NORTHFLANK:
apps = await getAppsNorthflank({
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;
};
/**
* Return list of projects for the Cloudflare Pages integration
* @param {Object} obj
* @param {String} obj.accessToken - api key for the Cloudflare API
* @returns {Object[]} apps - Cloudflare Pages projects
* @returns {String} apps.name - name of Cloudflare Pages project
*/
const getAppsCloudflarePages = async ({
accessToken,
accountId
}: {
accessToken: string;
accountId?: string;
}) => {
const { data } = await standardRequest.get(
`${INTEGRATION_CLOUDFLARE_PAGES_API_URL}/client/v4/accounts/${accountId}/pages/projects`,
{
headers: {
Authorization: `Bearer ${accessToken}`,
"Accept": "application/json",
},
}
);
const apps = data.result.map((a: any) => {
return {
name: a.name,
appId: a.id,
};
});
return apps;
}
/* Return list of projects for Northflank integration
* @param {Object} obj
* @param {String} obj.accessToken - access token for Northflank API
* @returns {Object[]} apps - names of Northflank apps
* @returns {String} apps.name - name of Northflank app
*/
const getAppsNorthflank = async ({ accessToken }: { accessToken: string }) => {
const {
data: {
data: {
projects
}
}
} = await standardRequest.get(
`${INTEGRATION_NORTHFLANK_API_URL}/v1/projects`,
{
headers: {
Authorization: `Bearer ${accessToken}`,
"Accept-Encoding": "application/json",
},
}
);
const apps = projects.map((a: any) => {
return {
name: a.name,
appId: a.id,
};
});
return apps;
};
export { getApps };