mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-15 00:52:09 +00:00
feat: fixed eslint and resolved all eslint issues
This commit is contained in:
@@ -7,8 +7,6 @@ import { ormify } from "@app/lib/knex";
|
||||
|
||||
import { TDeleteTokenForUserDALDTO } from "./auth-token-types";
|
||||
|
||||
export type TTokenDALConfig = {};
|
||||
|
||||
export type TTokenDALFactory = ReturnType<typeof tokenDALFactory>;
|
||||
|
||||
export const tokenDALFactory = (db: TDbClient) => {
|
||||
|
||||
@@ -41,6 +41,11 @@ export type AuthModeJwtTokenPayload = {
|
||||
accessVersion: number;
|
||||
};
|
||||
|
||||
export type AuthModeMfaJwtTokenPayload = {
|
||||
authTokenType: AuthTokenType.MFA_TOKEN;
|
||||
userId: string;
|
||||
};
|
||||
|
||||
export type AuthModeRefreshJwtTokenPayload = {
|
||||
authTokenType: AuthTokenType.REFRESH_TOKEN;
|
||||
userId: string;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Knex } from "knex";
|
||||
|
||||
import { TDbClient } from "@app/db";
|
||||
import { TableName,TIdentityAccessTokens } from "@app/db/schemas";
|
||||
import { TableName, TIdentityAccessTokens } from "@app/db/schemas";
|
||||
import { DatabaseError } from "@app/lib/errors";
|
||||
import { ormify, selectAllTableCols } from "@app/lib/knex";
|
||||
|
||||
|
||||
@@ -33,7 +33,11 @@ export const identityAccessTokenServiceFactory = ({
|
||||
createdAt: accessTokenCreatedAt
|
||||
} = identityAccessToken;
|
||||
|
||||
if (accessTokenNumUsesLimit > 0 && accessTokenNumUses > 0 && accessTokenNumUses >= accessTokenNumUsesLimit) {
|
||||
if (
|
||||
accessTokenNumUsesLimit > 0 &&
|
||||
accessTokenNumUses > 0 &&
|
||||
accessTokenNumUses >= accessTokenNumUsesLimit
|
||||
) {
|
||||
throw new BadRequestError({
|
||||
message: "Unable to renew because access token number of uses limit reached"
|
||||
});
|
||||
@@ -88,7 +92,9 @@ export const identityAccessTokenServiceFactory = ({
|
||||
const renewAccessToken = async ({ accessToken }: TRenewAccessTokenDTO) => {
|
||||
const appCfg = getConfig();
|
||||
|
||||
const decodedToken = jwt.verify(accessToken, appCfg.AUTH_SECRET) as JwtPayload;
|
||||
const decodedToken = jwt.verify(accessToken, appCfg.AUTH_SECRET) as JwtPayload & {
|
||||
identityAccessTokenId: string;
|
||||
};
|
||||
if (decodedToken.authTokenType !== AuthTokenType.IDENTITY_ACCESS_TOKEN)
|
||||
throw new UnauthorizedError();
|
||||
|
||||
|
||||
@@ -56,7 +56,6 @@ export const identityServiceFactory = ({
|
||||
return newIdentity;
|
||||
});
|
||||
|
||||
|
||||
return identity;
|
||||
};
|
||||
|
||||
|
||||
@@ -59,8 +59,8 @@ const getAppsGCPSecretManager = async ({ accessToken }: { accessToken: string })
|
||||
...(pageToken ? { pageToken } : {})
|
||||
});
|
||||
|
||||
const res: GCPGetProjectsRes = (
|
||||
await request.get(`${IntegrationUrls.GCP_API_URL}/v1/projects`, {
|
||||
const res = (
|
||||
await request.get<GCPGetProjectsRes>(`${IntegrationUrls.GCP_API_URL}/v1/projects`, {
|
||||
params,
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
@@ -81,8 +81,8 @@ const getAppsGCPSecretManager = async ({ accessToken }: { accessToken: string })
|
||||
// eslint-disable-next-line
|
||||
for await (const gcpApp of gcpApps) {
|
||||
try {
|
||||
const res: GCPGetServiceRes = (
|
||||
await request.get(
|
||||
const res = (
|
||||
await request.get<GCPGetServiceRes>(
|
||||
`${IntegrationUrls.GCP_SERVICE_USAGE_URL}/v1/projects/${gcpApp.projectId}/services/${IntegrationUrls.GCP_SECRET_MANAGER_SERVICE_NAME}`,
|
||||
{
|
||||
headers: {
|
||||
@@ -113,7 +113,7 @@ const getAppsGCPSecretManager = async ({ accessToken }: { accessToken: string })
|
||||
*/
|
||||
const getAppsHeroku = async ({ accessToken }: { accessToken: string }) => {
|
||||
const res = (
|
||||
await request.get(`${IntegrationUrls.HEROKU_API_URL}/apps`, {
|
||||
await request.get<{ name: string }[]>(`${IntegrationUrls.HEROKU_API_URL}/apps`, {
|
||||
headers: {
|
||||
Accept: "application/vnd.heroku+json; version=3",
|
||||
Authorization: `Bearer ${accessToken}`
|
||||
@@ -121,7 +121,7 @@ const getAppsHeroku = async ({ accessToken }: { accessToken: string }) => {
|
||||
})
|
||||
).data;
|
||||
|
||||
const apps = res.map((a: any) => ({
|
||||
const apps = res.map((a) => ({
|
||||
name: a.name
|
||||
}));
|
||||
|
||||
@@ -139,22 +139,25 @@ const getAppsVercel = async ({
|
||||
accessToken: string;
|
||||
}) => {
|
||||
const res = (
|
||||
await request.get(`${IntegrationUrls.VERCEL_API_URL}/v9/projects`, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
"Accept-Encoding": "application/json"
|
||||
},
|
||||
...(teamId
|
||||
? {
|
||||
params: {
|
||||
teamId
|
||||
await request.get<{ projects: { name: string; id: string }[] }>(
|
||||
`${IntegrationUrls.VERCEL_API_URL}/v9/projects`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
"Accept-Encoding": "application/json"
|
||||
},
|
||||
...(teamId
|
||||
? {
|
||||
params: {
|
||||
teamId
|
||||
}
|
||||
}
|
||||
}
|
||||
: {})
|
||||
})
|
||||
: {})
|
||||
}
|
||||
)
|
||||
).data;
|
||||
|
||||
const apps = res.projects.map((a: any) => ({
|
||||
const apps = res.projects.map((a) => ({
|
||||
name: a.name,
|
||||
appId: a.id
|
||||
}));
|
||||
@@ -166,7 +169,7 @@ const getAppsVercel = async ({
|
||||
* Return list of sites for Netlify integration
|
||||
*/
|
||||
const getAppsNetlify = async ({ accessToken }: { accessToken: string }) => {
|
||||
const apps: any = [];
|
||||
const apps: Array<{ name: string; appId: string }> = [];
|
||||
let page = 1;
|
||||
const perPage = 10;
|
||||
let hasMorePages = true;
|
||||
@@ -179,15 +182,18 @@ const getAppsNetlify = async ({ accessToken }: { accessToken: string }) => {
|
||||
filter: "all"
|
||||
});
|
||||
|
||||
const { data } = await request.get(`${IntegrationUrls.NETLIFY_API_URL}/api/v1/sites`, {
|
||||
params,
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
"Accept-Encoding": "application/json"
|
||||
const { data } = await request.get<{ name: string; site_id: string }[]>(
|
||||
`${IntegrationUrls.NETLIFY_API_URL}/api/v1/sites`,
|
||||
{
|
||||
params,
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
"Accept-Encoding": "application/json"
|
||||
}
|
||||
}
|
||||
});
|
||||
);
|
||||
|
||||
data.forEach((a: any) => {
|
||||
data.forEach((a) => {
|
||||
apps.push({
|
||||
name: a.name,
|
||||
appId: a.site_id
|
||||
@@ -238,8 +244,8 @@ const getAppsGithub = async ({ accessToken }: { accessToken: string }) => {
|
||||
}
|
||||
);
|
||||
|
||||
if (response.data.length > 0) {
|
||||
repos = repos.concat(response.data);
|
||||
if ((response.data as GitHubApp[]).length > 0) {
|
||||
repos = repos.concat(response.data as GitHubApp[]);
|
||||
page += 1;
|
||||
} else {
|
||||
hasMore = false;
|
||||
@@ -267,16 +273,19 @@ const getAppsGithub = async ({ accessToken }: { accessToken: string }) => {
|
||||
*/
|
||||
const getAppsRender = async ({ accessToken }: { accessToken: string }) => {
|
||||
const res = (
|
||||
await request.get(`${IntegrationUrls.RENDER_API_URL}/v1/services`, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
Accept: "application/json",
|
||||
"Accept-Encoding": "application/json"
|
||||
await request.get<{ service: { name: string; id: string } }[]>(
|
||||
`${IntegrationUrls.RENDER_API_URL}/v1/services`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
Accept: "application/json",
|
||||
"Accept-Encoding": "application/json"
|
||||
}
|
||||
}
|
||||
})
|
||||
)
|
||||
).data;
|
||||
|
||||
const apps = res.map((a: any) => ({
|
||||
const apps = res.map((a) => ({
|
||||
name: a.service.name,
|
||||
appId: a.service.id
|
||||
}));
|
||||
@@ -309,7 +318,9 @@ const getAppsRailway = async ({ accessToken }: { accessToken: string }) => {
|
||||
projects: { edges }
|
||||
}
|
||||
}
|
||||
} = await request.post(
|
||||
} = await request.post<{
|
||||
data: { projects: { edges: { node: { name: string; id: string } }[] } };
|
||||
}>(
|
||||
IntegrationUrls.RAILWAY_API_URL,
|
||||
{
|
||||
query,
|
||||
@@ -324,7 +335,7 @@ const getAppsRailway = async ({ accessToken }: { accessToken: string }) => {
|
||||
}
|
||||
);
|
||||
|
||||
const apps = edges.map((e: any) => ({
|
||||
const apps = edges.map((e) => ({
|
||||
name: e.node.name,
|
||||
appId: e.node.id
|
||||
}));
|
||||
@@ -343,16 +354,19 @@ const getAppsLaravelForge = async ({
|
||||
serverId?: string;
|
||||
}) => {
|
||||
const res = (
|
||||
await request.get(`${IntegrationUrls.LARAVELFORGE_API_URL}/api/v1/servers/${serverId}/sites`, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
Accept: "application/json",
|
||||
"Content-Type": "application/json"
|
||||
await request.get<{ sites: { name: string; id: string }[] }>(
|
||||
`${IntegrationUrls.LARAVELFORGE_API_URL}/api/v1/servers/${serverId}/sites`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
Accept: "application/json",
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
}
|
||||
})
|
||||
)
|
||||
).data.sites;
|
||||
|
||||
const apps = res.map((a: any) => ({
|
||||
const apps = res.map((a) => ({
|
||||
name: a.name,
|
||||
appId: a.id
|
||||
}));
|
||||
@@ -382,8 +396,8 @@ const getAppsFlyio = async ({ accessToken }: { accessToken: string }) => {
|
||||
}
|
||||
`;
|
||||
|
||||
const res: FlyioApp[] = (
|
||||
await request.post(
|
||||
const res = (
|
||||
await request.post<{ data: { apps: { nodes: FlyioApp[] } } }>(
|
||||
IntegrationUrls.FLYIO_API_URL,
|
||||
{
|
||||
query,
|
||||
@@ -401,7 +415,7 @@ const getAppsFlyio = async ({ accessToken }: { accessToken: string }) => {
|
||||
)
|
||||
).data.data.apps.nodes;
|
||||
|
||||
const apps = res.map((a: FlyioApp) => ({
|
||||
const apps = res.map((a) => ({
|
||||
name: a.name,
|
||||
appId: a.id
|
||||
}));
|
||||
@@ -414,7 +428,7 @@ const getAppsFlyio = async ({ accessToken }: { accessToken: string }) => {
|
||||
*/
|
||||
const getAppsCircleCI = async ({ accessToken }: { accessToken: string }) => {
|
||||
const res = (
|
||||
await request.get(`${IntegrationUrls.CIRCLECI_API_URL}/v1.1/projects`, {
|
||||
await request.get<{ reponame: string }[]>(`${IntegrationUrls.CIRCLECI_API_URL}/v1.1/projects`, {
|
||||
headers: {
|
||||
"Circle-Token": accessToken,
|
||||
"Accept-Encoding": "application/json"
|
||||
@@ -422,7 +436,7 @@ const getAppsCircleCI = async ({ accessToken }: { accessToken: string }) => {
|
||||
})
|
||||
).data;
|
||||
|
||||
const apps = res?.map((a: any) => ({
|
||||
const apps = res?.map((a) => ({
|
||||
name: a?.reponame
|
||||
}));
|
||||
|
||||
@@ -431,7 +445,7 @@ const getAppsCircleCI = async ({ accessToken }: { accessToken: string }) => {
|
||||
|
||||
const getAppsTravisCI = async ({ accessToken }: { accessToken: string }) => {
|
||||
const res = (
|
||||
await request.get(`${IntegrationUrls.TRAVISCI_API_URL}/repos`, {
|
||||
await request.get<{ id: string; slug: string }[]>(`${IntegrationUrls.TRAVISCI_API_URL}/repos`, {
|
||||
headers: {
|
||||
Authorization: `token ${accessToken}`,
|
||||
"Accept-Encoding": "application/json"
|
||||
@@ -439,7 +453,7 @@ const getAppsTravisCI = async ({ accessToken }: { accessToken: string }) => {
|
||||
})
|
||||
).data;
|
||||
|
||||
const apps = res?.map((a: any) => ({
|
||||
const apps = res?.map((a) => ({
|
||||
name: a?.slug?.split("/")[1],
|
||||
appId: a?.id
|
||||
}));
|
||||
@@ -458,7 +472,7 @@ const getAppsTerraformCloud = async ({
|
||||
workspacesId?: string;
|
||||
}) => {
|
||||
const res = (
|
||||
await request.get(
|
||||
await request.get<{ data: { attributes: { name: string }; id: string } }>(
|
||||
`${IntegrationUrls.TERRAFORM_CLOUD_API_URL}/api/v2/workspaces/${workspacesId}`,
|
||||
{
|
||||
headers: {
|
||||
@@ -510,15 +524,18 @@ const getAppsGitlab = async ({
|
||||
per_page: String(perPage)
|
||||
});
|
||||
|
||||
const { data } = await request.get(`${gitLabApiUrl}/v4/groups/${teamId}/projects`, {
|
||||
params,
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
"Accept-Encoding": "application/json"
|
||||
const { data } = await request.get<{ name: string; id: string }[]>(
|
||||
`${gitLabApiUrl}/v4/groups/${teamId}/projects`,
|
||||
{
|
||||
params,
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
"Accept-Encoding": "application/json"
|
||||
}
|
||||
}
|
||||
});
|
||||
);
|
||||
|
||||
data.forEach((a: any) => {
|
||||
data.forEach((a) => {
|
||||
apps.push({
|
||||
name: a.name,
|
||||
appId: a.id
|
||||
@@ -535,7 +552,7 @@ const getAppsGitlab = async ({
|
||||
// case: fetch projects for individual in GitLab
|
||||
|
||||
const { id } = (
|
||||
await request.get(`${gitLabApiUrl}/v4/user`, {
|
||||
await request.get<{ id: string }>(`${gitLabApiUrl}/v4/user`, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
"Accept-Encoding": "application/json"
|
||||
@@ -549,15 +566,18 @@ const getAppsGitlab = async ({
|
||||
per_page: String(perPage)
|
||||
});
|
||||
|
||||
const { data } = await request.get(`${gitLabApiUrl}/v4/users/${id}/projects`, {
|
||||
params,
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
"Accept-Encoding": "application/json"
|
||||
const { data } = await request.get<{ name: string; id: string }[]>(
|
||||
`${gitLabApiUrl}/v4/users/${id}/projects`,
|
||||
{
|
||||
params,
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
"Accept-Encoding": "application/json"
|
||||
}
|
||||
}
|
||||
});
|
||||
);
|
||||
|
||||
data.forEach((a: any) => {
|
||||
data.forEach((a) => {
|
||||
apps.push({
|
||||
name: a.name,
|
||||
appId: a.id
|
||||
@@ -580,7 +600,7 @@ const getAppsGitlab = async ({
|
||||
*/
|
||||
const getAppsTeamCity = async ({ accessToken, url }: { url: string; accessToken: string }) => {
|
||||
const res = (
|
||||
await request.get(`${url}/app/rest/projects`, {
|
||||
await request.get<{ project: { name: string; id: string }[] }>(`${url}/app/rest/projects`, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
Accept: "application/json"
|
||||
@@ -588,7 +608,7 @@ const getAppsTeamCity = async ({ accessToken, url }: { url: string; accessToken:
|
||||
})
|
||||
).data.project.slice(1);
|
||||
|
||||
const apps = res.map((a: any) => ({
|
||||
const apps = res.map((a) => ({
|
||||
name: a.name,
|
||||
appId: a.id
|
||||
}));
|
||||
@@ -600,14 +620,17 @@ const getAppsTeamCity = async ({ accessToken, url }: { url: string; accessToken:
|
||||
* Return list of projects for Supabase integration
|
||||
*/
|
||||
const getAppsSupabase = async ({ accessToken }: { accessToken: string }) => {
|
||||
const { data } = await request.get(`${IntegrationUrls.SUPABASE_API_URL}/v1/projects`, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
"Accept-Encoding": "application/json"
|
||||
const { data } = await request.get<{ name: string; id: string }[]>(
|
||||
`${IntegrationUrls.SUPABASE_API_URL}/v1/projects`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
"Accept-Encoding": "application/json"
|
||||
}
|
||||
}
|
||||
});
|
||||
);
|
||||
|
||||
const apps = data.map((a: any) => ({
|
||||
const apps = data.map((a) => ({
|
||||
name: a.name,
|
||||
appId: a.id
|
||||
}));
|
||||
@@ -619,14 +642,17 @@ const getAppsSupabase = async ({ accessToken }: { accessToken: string }) => {
|
||||
* Return list of accounts for the Checkly integration
|
||||
*/
|
||||
const getAppsCheckly = async ({ accessToken }: { accessToken: string }) => {
|
||||
const { data } = await request.get(`${IntegrationUrls.CHECKLY_API_URL}/v1/accounts`, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
Accept: "application/json"
|
||||
const { data } = await request.get<{ name: string; id: string }[]>(
|
||||
`${IntegrationUrls.CHECKLY_API_URL}/v1/accounts`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
Accept: "application/json"
|
||||
}
|
||||
}
|
||||
});
|
||||
);
|
||||
|
||||
const apps = data.map((a: any) => ({
|
||||
const apps = data.map((a) => ({
|
||||
name: a.name,
|
||||
appId: a.id
|
||||
}));
|
||||
@@ -644,7 +670,7 @@ const getAppsCloudflarePages = async ({
|
||||
accessToken: string;
|
||||
accountId?: string;
|
||||
}) => {
|
||||
const { data } = await request.get(
|
||||
const { data } = await request.get<{ result: { name: string; id: string }[] }>(
|
||||
`${IntegrationUrls.CLOUDFLARE_PAGES_API_URL}/client/v4/accounts/${accountId}/pages/projects`,
|
||||
{
|
||||
headers: {
|
||||
@@ -654,7 +680,7 @@ const getAppsCloudflarePages = async ({
|
||||
}
|
||||
);
|
||||
|
||||
const apps = data.result.map((a: any) => ({
|
||||
const apps = data.result.map((a) => ({
|
||||
name: a.name,
|
||||
appId: a.id
|
||||
}));
|
||||
@@ -671,7 +697,7 @@ const getAppsCloudflareWorkers = async ({
|
||||
accessToken: string;
|
||||
accountId?: string;
|
||||
}) => {
|
||||
const { data } = await request.get(
|
||||
const { data } = await request.get<{ result: { id: string }[] }>(
|
||||
`${IntegrationUrls.CLOUDFLARE_WORKERS_API_URL}/client/v4/accounts/${accountId}/workers/services`,
|
||||
{
|
||||
headers: {
|
||||
@@ -681,7 +707,7 @@ const getAppsCloudflareWorkers = async ({
|
||||
}
|
||||
);
|
||||
|
||||
const apps = data.result.map((a: any) => ({
|
||||
const apps = data.result.map((a) => ({
|
||||
name: a.id,
|
||||
appId: a.id
|
||||
}));
|
||||
@@ -759,14 +785,17 @@ const getAppsNorthflank = async ({ accessToken }: { accessToken: string }) => {
|
||||
data: {
|
||||
data: { projects }
|
||||
}
|
||||
} = await request.get(`${IntegrationUrls.NORTHFLANK_API_URL}/v1/projects`, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
"Accept-Encoding": "application/json"
|
||||
} = await request.get<{ data: { projects: { name: string; id: string }[] } }>(
|
||||
`${IntegrationUrls.NORTHFLANK_API_URL}/v1/projects`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
"Accept-Encoding": "application/json"
|
||||
}
|
||||
}
|
||||
});
|
||||
);
|
||||
|
||||
const apps = projects.map((a: any) => ({
|
||||
const apps = projects.map((a) => ({
|
||||
name: a.name,
|
||||
appId: a.id
|
||||
}));
|
||||
@@ -779,15 +808,18 @@ const getAppsNorthflank = async ({ accessToken }: { accessToken: string }) => {
|
||||
*/
|
||||
const getAppsCodefresh = async ({ accessToken }: { accessToken: string }) => {
|
||||
const res = (
|
||||
await request.get(`${IntegrationUrls.CODEFRESH_API_URL}/projects`, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
"Accept-Encoding": "application/json"
|
||||
await request.get<{ projects: { projectName: string; id: string }[] }>(
|
||||
`${IntegrationUrls.CODEFRESH_API_URL}/projects`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
"Accept-Encoding": "application/json"
|
||||
}
|
||||
}
|
||||
})
|
||||
)
|
||||
).data;
|
||||
|
||||
const apps = res.projects.map((a: any) => ({
|
||||
const apps = res.projects.map((a) => ({
|
||||
name: a.projectName,
|
||||
appId: a.id
|
||||
}));
|
||||
@@ -799,20 +831,23 @@ const getAppsCodefresh = async ({ accessToken }: { accessToken: string }) => {
|
||||
* Return list of projects for Windmill integration
|
||||
*/
|
||||
const getAppsWindmill = async ({ accessToken }: { accessToken: string }) => {
|
||||
const { data } = await request.get(`${IntegrationUrls.WINDMILL_API_URL}/workspaces/list`, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
"Accept-Encoding": "application/json"
|
||||
const { data } = await request.get<{ id: string; name: string }[]>(
|
||||
`${IntegrationUrls.WINDMILL_API_URL}/workspaces/list`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
"Accept-Encoding": "application/json"
|
||||
}
|
||||
}
|
||||
});
|
||||
);
|
||||
|
||||
// check for write access of secrets in windmill workspaces
|
||||
const writeAccessCheck = data.map(async (app: any) => {
|
||||
const writeAccessCheck = data.map(async (app) => {
|
||||
try {
|
||||
const userPath = "u/user/variable";
|
||||
const folderPath = "f/folder/variable";
|
||||
|
||||
const { data: writeUser } = await request.post(
|
||||
const { data: writeUser } = await request.post<object>(
|
||||
`${IntegrationUrls.WINDMILL_API_URL}/w/${app.id}/variables/create`,
|
||||
{
|
||||
path: userPath,
|
||||
@@ -828,7 +863,7 @@ const getAppsWindmill = async ({ accessToken }: { accessToken: string }) => {
|
||||
}
|
||||
);
|
||||
|
||||
const { data: writeFolder } = await request.post(
|
||||
const { data: writeFolder } = await request.post<object>(
|
||||
`${IntegrationUrls.WINDMILL_API_URL}/w/${app.id}/variables/create`,
|
||||
{
|
||||
path: folderPath,
|
||||
@@ -869,15 +904,17 @@ const getAppsWindmill = async ({ accessToken }: { accessToken: string }) => {
|
||||
return app;
|
||||
}
|
||||
return { error: "cannot write secret" };
|
||||
} catch (err: any) {
|
||||
return { error: err.message };
|
||||
} catch (err) {
|
||||
return { error: (err as Error).message };
|
||||
}
|
||||
});
|
||||
|
||||
const appsWriteResponses = await Promise.all(writeAccessCheck);
|
||||
const appsWithWriteAccess = appsWriteResponses.filter((appRes: any) => !appRes.error);
|
||||
const appsWithWriteAccess = appsWriteResponses.filter(
|
||||
(appRes) => !(appRes as { error: string })?.error
|
||||
);
|
||||
|
||||
const apps = appsWithWriteAccess.map((a: any) => ({
|
||||
const apps = (appsWithWriteAccess as { id: string; name: string }[]).map((a) => ({
|
||||
name: a.name,
|
||||
appId: a.id
|
||||
}));
|
||||
@@ -908,22 +945,27 @@ const getAppsDigitalOceanAppPlatform = async ({ accessToken }: { accessToken: st
|
||||
}
|
||||
|
||||
const res = (
|
||||
await request.get(`${IntegrationUrls.DIGITAL_OCEAN_API_URL}/v2/apps`, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
"Accept-Encoding": "application/json"
|
||||
await request.get<{ apps: DigitalOceanApp[] }>(
|
||||
`${IntegrationUrls.DIGITAL_OCEAN_API_URL}/v2/apps`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
"Accept-Encoding": "application/json"
|
||||
}
|
||||
}
|
||||
})
|
||||
)
|
||||
).data;
|
||||
|
||||
return (res.apps ?? []).map((a: DigitalOceanApp) => ({
|
||||
return (res.apps ?? []).map((a) => ({
|
||||
name: a.spec.name,
|
||||
appId: a.id
|
||||
}));
|
||||
};
|
||||
|
||||
const getAppsHasuraCloud = async ({ accessToken }: { accessToken: string }) => {
|
||||
const res = await request.post(
|
||||
const res = await request.post<{
|
||||
data: { projects: { name: string; tenant: { id: string } }[] };
|
||||
}>(
|
||||
IntegrationUrls.HASURA_CLOUD_API_URL,
|
||||
{
|
||||
query: "query MyQuery { projects { name tenant { id } } }"
|
||||
@@ -936,19 +978,15 @@ const getAppsHasuraCloud = async ({ accessToken }: { accessToken: string }) => {
|
||||
}
|
||||
);
|
||||
|
||||
const data = (res?.data?.data?.projects ?? []).map(
|
||||
({ name, tenant: { id: appId } }: { name: string; tenant: { id: string } }) => ({ name, appId })
|
||||
);
|
||||
const data = (res?.data?.data?.projects ?? []).map(({ name, tenant: { id: appId } }) => ({
|
||||
name,
|
||||
appId
|
||||
}));
|
||||
return data;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return list of applications for Cloud66 integration
|
||||
* @param {Object} obj
|
||||
* @param {String} obj.accessToken - personal access token for Cloud66 API
|
||||
* @returns {Object[]} apps - Cloud66 apps
|
||||
* @returns {String} apps.name - name of Cloud66 app
|
||||
* @returns {String} apps.appId - uid of Cloud66 app
|
||||
*/
|
||||
const getAppsCloud66 = async ({ accessToken }: { accessToken: string }) => {
|
||||
interface Cloud66Apps {
|
||||
@@ -979,19 +1017,19 @@ const getAppsCloud66 = async ({ accessToken }: { accessToken: string }) => {
|
||||
account_name: string;
|
||||
is_cluster: boolean;
|
||||
is_inside_cluster: boolean;
|
||||
cluster_name: any;
|
||||
cluster_name: string;
|
||||
application_address: string;
|
||||
configstore_namespace: string;
|
||||
}
|
||||
|
||||
const stacks = (
|
||||
await request.get(`${IntegrationUrls.CLOUD_66_API_URL}/3/stacks`, {
|
||||
await request.get<{ response: Cloud66Apps[] }>(`${IntegrationUrls.CLOUD_66_API_URL}/3/stacks`, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
"Accept-Encoding": "application/json"
|
||||
}
|
||||
})
|
||||
).data.response as Cloud66Apps[];
|
||||
).data.response;
|
||||
|
||||
const apps = stacks.map((app) => ({
|
||||
name: app.name,
|
||||
@@ -1016,7 +1054,7 @@ export const getApps = async ({
|
||||
workspaceSlug?: string;
|
||||
url?: string | null;
|
||||
}): Promise<App[]> => {
|
||||
switch (integration) {
|
||||
switch (integration as Integrations) {
|
||||
case Integrations.GCP_SECRET_MANAGER:
|
||||
return getAppsGCPSecretManager({
|
||||
accessToken
|
||||
|
||||
@@ -906,7 +906,7 @@ export const integrationAuthServiceFactory = ({
|
||||
}
|
||||
);
|
||||
|
||||
secrets.forEach((a: any) => {
|
||||
secrets.forEach((a) => {
|
||||
secretGroups.push({
|
||||
name: a.name,
|
||||
groupId: a.id
|
||||
|
||||
@@ -67,6 +67,7 @@ export enum IntegrationUrls {
|
||||
QOVERY_API_URL = "https://api.qovery.com",
|
||||
TERRAFORM_CLOUD_API_URL = "https://app.terraform.io",
|
||||
CLOUDFLARE_PAGES_API_URL = "https://api.cloudflare.com",
|
||||
// eslint-disable-next-line
|
||||
CLOUDFLARE_WORKERS_API_URL = "https://api.cloudflare.com",
|
||||
BITBUCKET_API_URL = "https://api.bitbucket.org",
|
||||
CODEFRESH_API_URL = "https://g.codefresh.io/api",
|
||||
|
||||
@@ -1,4 +1,11 @@
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-return */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-argument */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||
/* eslint-disable no-param-reassign,no-await-in-loop */
|
||||
// Taken from old code and too much work at present thus disabling the above any rules
|
||||
// resolve it later: akhilmhdh - TODO
|
||||
|
||||
import {
|
||||
CreateSecretCommand,
|
||||
GetSecretValueCommand,
|
||||
@@ -8,6 +15,7 @@ import {
|
||||
} from "@aws-sdk/client-secrets-manager";
|
||||
import { Octokit } from "@octokit/rest";
|
||||
import AWS from "aws-sdk";
|
||||
import { AxiosError } from "axios";
|
||||
import sodium from "libsodium-wrappers";
|
||||
import isEqual from "lodash.isequal";
|
||||
import { z } from "zod";
|
||||
@@ -87,8 +95,8 @@ const syncSecretsGCPSecretManager = async ({
|
||||
...(pageToken ? { pageToken } : {})
|
||||
});
|
||||
|
||||
const res: GCPSMListSecretsRes = (
|
||||
await request.get(
|
||||
const res = (
|
||||
await request.get<GCPSMListSecretsRes>(
|
||||
`${IntegrationUrls.GCP_SECRET_MANAGER_URL}/v1/projects/${integration.appId}/secrets${filterParam}`,
|
||||
{
|
||||
params,
|
||||
@@ -309,7 +317,7 @@ const syncSecretsAzureKeyVault = async ({
|
||||
})
|
||||
)
|
||||
).reduce(
|
||||
(obj: any, secret: any) => ({
|
||||
(obj, secret) => ({
|
||||
...obj,
|
||||
[secret.key]: secret
|
||||
}),
|
||||
@@ -378,8 +386,9 @@ const syncSecretsAzureKeyVault = async ({
|
||||
|
||||
isSecretSet = true;
|
||||
} catch (err) {
|
||||
const error: any = err;
|
||||
if (error?.response?.data?.error?.innererror?.code === "ObjectIsDeletedButRecoverable") {
|
||||
const error = err as AxiosError;
|
||||
// eslint-disable-next-line
|
||||
if ((error?.response?.data as any)?.error?.innererror?.code === "ObjectIsDeletedButRecoverable") {
|
||||
await request.post(
|
||||
`${azIntegration.app}/deletedsecrets/${key}/recover?api-version=7.3`,
|
||||
{},
|
||||
@@ -405,7 +414,7 @@ const syncSecretsAzureKeyVault = async ({
|
||||
// Sync/push set secrets
|
||||
for await (const setSecret of setSecrets) {
|
||||
const { key, value } = setSecret;
|
||||
setSecretAzureKeyVault({
|
||||
await setSecretAzureKeyVault({
|
||||
key,
|
||||
value,
|
||||
integration,
|
||||
@@ -425,11 +434,6 @@ const syncSecretsAzureKeyVault = async ({
|
||||
|
||||
/**
|
||||
* Sync/push [secrets] to AWS parameter store
|
||||
* @param {Object} obj
|
||||
* @param {TIntegrations} obj.integration - integration details
|
||||
* @param {Object} obj.secrets - secrets to push to integration (object where keys are secret keys and values are secret values)
|
||||
* @param {String} obj.accessId - access id for AWS parameter store integration
|
||||
* @param {String} obj.accessToken - access token for AWS parameter store integration
|
||||
*/
|
||||
const syncSecretsAWSParameterStore = async ({
|
||||
integration,
|
||||
@@ -463,60 +467,60 @@ const syncSecretsAWSParameterStore = async ({
|
||||
|
||||
const parameterList = (await ssm.getParametersByPath(params).promise()).Parameters;
|
||||
|
||||
let awsParameterStoreSecretsObj: {
|
||||
[key: string]: any;
|
||||
} = {};
|
||||
|
||||
if (parameterList) {
|
||||
awsParameterStoreSecretsObj = parameterList.reduce(
|
||||
(obj: any, secret: any) => ({
|
||||
const awsParameterStoreSecretsObj = (parameterList || [])
|
||||
.filter(({ Name }) => Boolean(Name))
|
||||
.reduce(
|
||||
(obj, secret) => ({
|
||||
...obj,
|
||||
[secret.Name.substring((integration.path as string).length)]: secret
|
||||
[(secret.Name as string).substring((integration.path as string).length)]: secret
|
||||
}),
|
||||
{}
|
||||
{} as Record<string, AWS.SSM.Parameter>
|
||||
);
|
||||
}
|
||||
|
||||
// Identify secrets to create
|
||||
Object.keys(secrets).map(async (key) => {
|
||||
if (!(key in awsParameterStoreSecretsObj)) {
|
||||
// case: secret does not exist in AWS parameter store
|
||||
// -> create secret
|
||||
await ssm
|
||||
.putParameter({
|
||||
Name: `${integration.path}${key}`,
|
||||
Type: "SecureString",
|
||||
Value: secrets[key].value,
|
||||
Overwrite: true
|
||||
})
|
||||
.promise();
|
||||
// case: secret exists in AWS parameter store
|
||||
} else if (awsParameterStoreSecretsObj[key].Value !== secrets[key].value) {
|
||||
// case: secret value doesn't match one in AWS parameter store
|
||||
// -> update secret
|
||||
await ssm
|
||||
.putParameter({
|
||||
Name: `${integration.path}${key}`,
|
||||
Type: "SecureString",
|
||||
Value: secrets[key].value,
|
||||
Overwrite: true
|
||||
})
|
||||
.promise();
|
||||
}
|
||||
});
|
||||
await Promise.all(
|
||||
Object.keys(secrets).map(async (key) => {
|
||||
if (!(key in awsParameterStoreSecretsObj)) {
|
||||
// case: secret does not exist in AWS parameter store
|
||||
// -> create secret
|
||||
await ssm
|
||||
.putParameter({
|
||||
Name: `${integration.path}${key}`,
|
||||
Type: "SecureString",
|
||||
Value: secrets[key].value,
|
||||
Overwrite: true
|
||||
})
|
||||
.promise();
|
||||
// case: secret exists in AWS parameter store
|
||||
} else if (awsParameterStoreSecretsObj[key].Value !== secrets[key].value) {
|
||||
// case: secret value doesn't match one in AWS parameter store
|
||||
// -> update secret
|
||||
await ssm
|
||||
.putParameter({
|
||||
Name: `${integration.path}${key}`,
|
||||
Type: "SecureString",
|
||||
Value: secrets[key].value,
|
||||
Overwrite: true
|
||||
})
|
||||
.promise();
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
// Identify secrets to delete
|
||||
Object.keys(awsParameterStoreSecretsObj).map(async (key) => {
|
||||
if (!(key in secrets)) {
|
||||
// case:
|
||||
// -> delete secret
|
||||
await ssm
|
||||
.deleteParameter({
|
||||
Name: awsParameterStoreSecretsObj[key].Name
|
||||
})
|
||||
.promise();
|
||||
}
|
||||
});
|
||||
await Promise.all(
|
||||
Object.keys(awsParameterStoreSecretsObj).map(async (key) => {
|
||||
if (!(key in secrets)) {
|
||||
// case:
|
||||
// -> delete secret
|
||||
await ssm
|
||||
.deleteParameter({
|
||||
Name: awsParameterStoreSecretsObj[key].Name as string
|
||||
})
|
||||
.promise();
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
AWS.config.update({
|
||||
region: undefined,
|
||||
@@ -564,7 +568,7 @@ const syncSecretsAWSSecretManager = async ({
|
||||
})
|
||||
);
|
||||
|
||||
let awsSecretManagerSecretObj: { [key: string]: any } = {};
|
||||
let awsSecretManagerSecretObj: { [key: string]: AWS.SecretsManager } = {};
|
||||
|
||||
if (awsSecretManagerSecret?.SecretString) {
|
||||
awsSecretManagerSecretObj = JSON.parse(awsSecretManagerSecret.SecretString);
|
||||
@@ -680,15 +684,18 @@ const syncSecretsVercel = async ({
|
||||
: {})
|
||||
};
|
||||
|
||||
const vercelSecrets: VercelSecret[] = (
|
||||
await request.get(`${IntegrationUrls.VERCEL_API_URL}/v9/projects/${integration.app}/env`, {
|
||||
params,
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
"Accept-Encoding": "application/json"
|
||||
const vercelSecrets = (
|
||||
await request.get<{ envs: VercelSecret[] }>(
|
||||
`${IntegrationUrls.VERCEL_API_URL}/v9/projects/${integration.app}/env`,
|
||||
{
|
||||
params,
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
"Accept-Encoding": "application/json"
|
||||
}
|
||||
}
|
||||
})
|
||||
).data.envs.filter((secret: VercelSecret) => {
|
||||
)
|
||||
).data.envs.filter((secret) => {
|
||||
if (!secret.target.includes(integration.targetEnvironment as string)) {
|
||||
// case: secret does not have the same target environment
|
||||
return false;
|
||||
@@ -866,7 +873,7 @@ const syncSecretsNetlify = async ({
|
||||
});
|
||||
|
||||
const res = (
|
||||
await request.get(
|
||||
await request.get<NetlifySecret[]>(
|
||||
`${IntegrationUrls.NETLIFY_API_URL}/api/v1/accounts/${integrationAuth.accountId}/env`,
|
||||
{
|
||||
params: getParams,
|
||||
@@ -877,11 +884,11 @@ const syncSecretsNetlify = async ({
|
||||
}
|
||||
)
|
||||
).data.reduce(
|
||||
(obj: any, secret: any) => ({
|
||||
(obj, secret) => ({
|
||||
...obj,
|
||||
[secret.key]: secret
|
||||
}),
|
||||
{}
|
||||
{} as Record<string, NetlifySecret>
|
||||
);
|
||||
|
||||
const newSecrets: NetlifySecret[] = []; // createEnvVars
|
||||
@@ -905,16 +912,16 @@ const syncSecretsNetlify = async ({
|
||||
} else {
|
||||
// case: Infisical secret exists in Netlify
|
||||
const contexts = res[key].values.reduce(
|
||||
(obj: any, value: NetlifyValue) => ({
|
||||
(obj, value) => ({
|
||||
...obj,
|
||||
[value.context]: value
|
||||
}),
|
||||
{}
|
||||
{} as Record<string, NetlifyValue>
|
||||
);
|
||||
|
||||
if ((integration.targetEnvironment as string) in contexts) {
|
||||
// case: Netlify secret value exists in integration context
|
||||
if (secrets[key] !== contexts[integration.targetEnvironment as string].value) {
|
||||
if (secrets[key].value !== contexts[integration.targetEnvironment as string].value) {
|
||||
// case: Infisical and Netlify secret values are different
|
||||
// -> update Netlify secret context and value
|
||||
updateSecrets.push({
|
||||
@@ -994,62 +1001,63 @@ const syncSecretsNetlify = async ({
|
||||
}
|
||||
|
||||
if (updateSecrets.length > 0) {
|
||||
updateSecrets.forEach(async (secret: NetlifySecret) => {
|
||||
await request.patch(
|
||||
`${IntegrationUrls.NETLIFY_API_URL}/api/v1/accounts/${integrationAuth.accountId}/env/${secret.key}`,
|
||||
{
|
||||
context: secret.values[0].context,
|
||||
value: secret.values[0].value
|
||||
},
|
||||
{
|
||||
params: syncParams,
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
"Accept-Encoding": "application/json"
|
||||
await Promise.all(
|
||||
updateSecrets.map(async (secret: NetlifySecret) => {
|
||||
await request.patch(
|
||||
`${IntegrationUrls.NETLIFY_API_URL}/api/v1/accounts/${integrationAuth.accountId}/env/${secret.key}`,
|
||||
{
|
||||
context: secret.values[0].context,
|
||||
value: secret.values[0].value
|
||||
},
|
||||
{
|
||||
params: syncParams,
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
"Accept-Encoding": "application/json"
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
if (deleteSecrets.length > 0) {
|
||||
deleteSecrets.forEach(async (key: string) => {
|
||||
await request.delete(
|
||||
`${IntegrationUrls.NETLIFY_API_URL}/api/v1/accounts/${integrationAuth.accountId}/env/${key}`,
|
||||
{
|
||||
params: syncParams,
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
"Accept-Encoding": "application/json"
|
||||
await Promise.all(
|
||||
deleteSecrets.map(async (key: string) => {
|
||||
await request.delete(
|
||||
`${IntegrationUrls.NETLIFY_API_URL}/api/v1/accounts/${integrationAuth.accountId}/env/${key}`,
|
||||
{
|
||||
params: syncParams,
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
"Accept-Encoding": "application/json"
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
if (deleteSecretValues.length > 0) {
|
||||
deleteSecretValues.forEach(async (secret: NetlifySecret) => {
|
||||
await request.delete(
|
||||
`${IntegrationUrls.NETLIFY_API_URL}/api/v1/accounts/${integrationAuth.accountId}/env/${secret.key}/value/${secret.values[0].id}`,
|
||||
{
|
||||
params: syncParams,
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
"Accept-Encoding": "application/json"
|
||||
await Promise.all(
|
||||
deleteSecretValues.map(async (secret: NetlifySecret) => {
|
||||
await request.delete(
|
||||
`${IntegrationUrls.NETLIFY_API_URL}/api/v1/accounts/${integrationAuth.accountId}/env/${secret.key}/value/${secret.values[0].id}`,
|
||||
{
|
||||
params: syncParams,
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
"Accept-Encoding": "application/json"
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
);
|
||||
})
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Sync/push [secrets] to GitHub repo with name [integration.app]
|
||||
* @param {Object} obj
|
||||
* @param {TIntegrations} obj.integration - integration details
|
||||
* @param {TIntegrationAuth} obj.integrationAuth - integration auth details
|
||||
* @param {Object} obj.secrets - secrets to push to integration (object where keys are secret keys and values are secret values)
|
||||
* @param {String} obj.accessToken - access token for GitHub integration
|
||||
*/
|
||||
const syncSecretsGitHub = async ({
|
||||
integration,
|
||||
@@ -1096,7 +1104,7 @@ const syncSecretsGitHub = async ({
|
||||
repo: integration.app as string
|
||||
})
|
||||
).data.secrets.reduce(
|
||||
(obj: any, secret: any) => ({
|
||||
(obj, secret) => ({
|
||||
...obj,
|
||||
[secret.name]: secret
|
||||
}),
|
||||
@@ -1121,46 +1129,46 @@ const syncSecretsGitHub = async ({
|
||||
{}
|
||||
);
|
||||
|
||||
Object.keys(encryptedSecrets).map(async (key) => {
|
||||
if (!(key in secrets)) {
|
||||
await octokit.request("DELETE /repos/{owner}/{repo}/actions/secrets/{secret_name}", {
|
||||
owner: integration.owner as string,
|
||||
repo: integration.app as string,
|
||||
secret_name: key
|
||||
await Promise.all(
|
||||
Object.keys(encryptedSecrets).map(async (key) => {
|
||||
if (!(key in secrets)) {
|
||||
return octokit.request("DELETE /repos/{owner}/{repo}/actions/secrets/{secret_name}", {
|
||||
owner: integration.owner as string,
|
||||
repo: integration.app as string,
|
||||
secret_name: key
|
||||
});
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
await Promise.all(
|
||||
Object.keys(secrets).map((key) => {
|
||||
// let encryptedSecret;
|
||||
return sodium.ready.then(async () => {
|
||||
// convert secret & base64 key to Uint8Array.
|
||||
const binkey = sodium.from_base64(repoPublicKey.key, sodium.base64_variants.ORIGINAL);
|
||||
const binsec = sodium.from_string(secrets[key].value);
|
||||
|
||||
// encrypt secret using libsodium
|
||||
const encBytes = sodium.crypto_box_seal(binsec, binkey);
|
||||
|
||||
// convert encrypted Uint8Array to base64
|
||||
const encryptedSecret = sodium.to_base64(encBytes, sodium.base64_variants.ORIGINAL);
|
||||
|
||||
await octokit.request("PUT /repos/{owner}/{repo}/actions/secrets/{secret_name}", {
|
||||
owner: integration.owner as string,
|
||||
repo: integration.app as string,
|
||||
secret_name: key,
|
||||
encrypted_value: encryptedSecret,
|
||||
key_id: repoPublicKey.key_id
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
Object.keys(secrets).forEach((key) => {
|
||||
// let encryptedSecret;
|
||||
sodium.ready.then(async () => {
|
||||
// convert secret & base64 key to Uint8Array.
|
||||
const binkey = sodium.from_base64(repoPublicKey.key, sodium.base64_variants.ORIGINAL);
|
||||
const binsec = sodium.from_string(secrets[key].value);
|
||||
|
||||
// encrypt secret using libsodium
|
||||
const encBytes = sodium.crypto_box_seal(binsec, binkey);
|
||||
|
||||
// convert encrypted Uint8Array to base64
|
||||
const encryptedSecret = sodium.to_base64(encBytes, sodium.base64_variants.ORIGINAL);
|
||||
|
||||
await octokit.request("PUT /repos/{owner}/{repo}/actions/secrets/{secret_name}", {
|
||||
owner: integration.owner as string,
|
||||
repo: integration.app as string,
|
||||
secret_name: key,
|
||||
encrypted_value: encryptedSecret,
|
||||
key_id: repoPublicKey.key_id
|
||||
});
|
||||
});
|
||||
});
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Sync/push [secrets] to Render service with id [integration.appId]
|
||||
* @param {Object} obj
|
||||
* @param {TIntegrations} obj.integration - integration details
|
||||
* @param {Object} obj.secrets - secrets to push to integration (object where keys are secret keys and values are secret values)
|
||||
* @param {String} obj.accessToken - access token for Render integration
|
||||
*/
|
||||
const syncSecretsRender = async ({
|
||||
integration,
|
||||
@@ -1188,10 +1196,6 @@ const syncSecretsRender = async ({
|
||||
|
||||
/**
|
||||
* Sync/push [secrets] to Laravel Forge sites with id [integration.appId]
|
||||
* @param {Object} obj
|
||||
* @param {TIntegrations} obj.integration - integration details
|
||||
* @param {Object} obj.secrets - secrets to push to integration (object where keys are secret keys and values are secret values)
|
||||
* @param {String} obj.accessToken - access token for Laravel Forge integration
|
||||
*/
|
||||
const syncSecretsLaravelForge = async ({
|
||||
integration,
|
||||
@@ -1204,13 +1208,11 @@ const syncSecretsLaravelForge = async ({
|
||||
accessId: string | null;
|
||||
accessToken: string;
|
||||
}) => {
|
||||
function transformObjectToString(obj: any) {
|
||||
function transformObjectToString(obj: Record<string, { value: string }>) {
|
||||
let result = "";
|
||||
for (const key in obj) {
|
||||
if (obj.hasOwnPropery(key)) {
|
||||
result += `${key}=${obj[key].value}\n`;
|
||||
}
|
||||
}
|
||||
Object.keys(obj).forEach((key) => {
|
||||
result += `${key}=${obj[key].value}\n`;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -1231,10 +1233,6 @@ const syncSecretsLaravelForge = async ({
|
||||
|
||||
/**
|
||||
* Sync/push [secrets] to Railway project with id [integration.appId]
|
||||
* @param {Object} obj
|
||||
* @param {TIntegrations} obj.integration - integration details
|
||||
* @param {Object} obj.secrets - secrets to push to integration (object where keys are secret keys and values are secret values)
|
||||
* @param {String} obj.accessToken - access token for Railway integration
|
||||
*/
|
||||
const syncSecretsRailway = async ({
|
||||
integration,
|
||||
@@ -1279,10 +1277,6 @@ const syncSecretsRailway = async ({
|
||||
|
||||
/**
|
||||
* Sync/push [secrets] to Fly.io app
|
||||
* @param {Object} obj
|
||||
* @param {TIntegrations} obj.integration - integration details
|
||||
* @param {Object} obj.secrets - secrets to push to integration (object where keys are secret keys and values are secret values)
|
||||
* @param {String} obj.accessToken - access token for Render integration
|
||||
*/
|
||||
const syncSecretsFlyio = async ({
|
||||
integration,
|
||||
@@ -1354,7 +1348,7 @@ const syncSecretsFlyio = async ({
|
||||
}`;
|
||||
|
||||
const getSecretsRes = (
|
||||
await request.post(
|
||||
await request.post<{ data: { app: { secrets: FlyioSecret[] } } }>(
|
||||
IntegrationUrls.FLYIO_API_URL,
|
||||
{
|
||||
query: GetSecrets,
|
||||
@@ -1373,8 +1367,8 @@ const syncSecretsFlyio = async ({
|
||||
).data.data.app.secrets;
|
||||
|
||||
const deleteSecretsKeys = getSecretsRes
|
||||
.filter((secret: FlyioSecret) => !(secret.name in secrets))
|
||||
.map((secret: FlyioSecret) => secret.name);
|
||||
.filter((secret) => !(secret.name in secrets))
|
||||
.map((secret) => secret.name);
|
||||
|
||||
// unset (delete) secrets
|
||||
const DeleteSecrets = `mutation($input: UnsetSecretsInput!) {
|
||||
@@ -1460,7 +1454,7 @@ const syncSecretsCircleCI = async ({
|
||||
|
||||
// get secrets from CircleCI
|
||||
const getSecretsRes = (
|
||||
await request.get(
|
||||
await request.get<{ items: { name: string }[] }>(
|
||||
`${IntegrationUrls.CIRCLECI_API_URL}/v2/project/${slug}/${integration.app}/envvar`,
|
||||
{
|
||||
headers: {
|
||||
@@ -1472,19 +1466,21 @@ const syncSecretsCircleCI = async ({
|
||||
).data?.items;
|
||||
|
||||
// delete secrets from CircleCI
|
||||
getSecretsRes.forEach(async (sec: any) => {
|
||||
if (!(sec.name in secrets)) {
|
||||
await request.delete(
|
||||
`${IntegrationUrls.CIRCLECI_API_URL}/v2/project/${slug}/${integration.app}/envvar/${sec.name}`,
|
||||
{
|
||||
headers: {
|
||||
"Circle-Token": accessToken,
|
||||
"Content-Type": "application/json"
|
||||
await Promise.all(
|
||||
getSecretsRes.map(async (sec) => {
|
||||
if (!(sec.name in secrets)) {
|
||||
return request.delete(
|
||||
`${IntegrationUrls.CIRCLECI_API_URL}/v2/project/${slug}/${integration.app}/envvar/${sec.name}`,
|
||||
{
|
||||
headers: {
|
||||
"Circle-Token": accessToken,
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
});
|
||||
);
|
||||
}
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -1501,21 +1497,20 @@ const syncSecretsTravisCI = async ({
|
||||
}) => {
|
||||
// get secrets from travis-ci
|
||||
const getSecretsRes = (
|
||||
await request.get(
|
||||
`${IntegrationUrls.TRAVISCI_API_URL}/settings/env_vars?repository_id=${integration.appId}`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `token ${accessToken}`,
|
||||
"Accept-Encoding": "application/json"
|
||||
}
|
||||
await request.get<{
|
||||
env_vars: { name: string; value: string; repository_id: string; id: string }[];
|
||||
}>(`${IntegrationUrls.TRAVISCI_API_URL}/settings/env_vars?repository_id=${integration.appId}`, {
|
||||
headers: {
|
||||
Authorization: `token ${accessToken}`,
|
||||
"Accept-Encoding": "application/json"
|
||||
}
|
||||
)
|
||||
})
|
||||
).data?.env_vars.reduce(
|
||||
(obj: any, secret: any) => ({
|
||||
(obj, secret) => ({
|
||||
...obj,
|
||||
[secret.name]: secret
|
||||
}),
|
||||
{}
|
||||
{} as Record<string, { name: string; value: string; repository_id: string; id: string }>
|
||||
);
|
||||
|
||||
// add secrets
|
||||
@@ -1614,10 +1609,10 @@ const syncSecretsGitLab = async ({
|
||||
`${gitLabApiUrl}/v4/projects/${integrationAppId}/variables?per_page=100`;
|
||||
|
||||
while (url) {
|
||||
const response: any = await request.get(url, { headers });
|
||||
const response = await request.get(url, { headers });
|
||||
allEnvVariables = [...allEnvVariables, ...response.data];
|
||||
|
||||
const linkHeader = response.headers.link;
|
||||
const linkHeader = response.headers.link as string;
|
||||
const nextLink = linkHeader?.split(",").find((part: string) => part.includes('rel="next"'));
|
||||
|
||||
if (nextLink) {
|
||||
@@ -1649,7 +1644,7 @@ const syncSecretsGitLab = async ({
|
||||
});
|
||||
|
||||
for await (const key of Object.keys(secrets)) {
|
||||
const existingSecret = getSecretsRes.find((s: any) => s.key === key);
|
||||
const existingSecret = getSecretsRes.find((s) => s.key === key);
|
||||
if (!existingSecret) {
|
||||
await request.post(
|
||||
`${gitLabApiUrl}/v4/projects/${integration?.appId}/variables`,
|
||||
@@ -1714,7 +1709,7 @@ const syncSecretsSupabase = async ({
|
||||
secrets: Record<string, { value: string; comment?: string }>;
|
||||
accessToken: string;
|
||||
}) => {
|
||||
const { data: getSecretsRes } = await request.get(
|
||||
const { data: getSecretsRes } = await request.get<{ name: string; value: string }[]>(
|
||||
`${IntegrationUrls.SUPABASE_API_URL}/v1/projects/${integration.appId}/secrets`,
|
||||
{
|
||||
headers: {
|
||||
@@ -1741,8 +1736,7 @@ const syncSecretsSupabase = async ({
|
||||
}
|
||||
);
|
||||
|
||||
const secretsToDelete: any = [];
|
||||
getSecretsRes?.forEach((secretObj: any) => {
|
||||
const secretsToDelete = getSecretsRes?.flatMap((secretObj) => {
|
||||
if (
|
||||
!(secretObj.name in secrets) &&
|
||||
// supbase reserved secret ref: https://supabase.com/docs/guides/functions/secrets#default-secrets
|
||||
@@ -1753,8 +1747,9 @@ const syncSecretsSupabase = async ({
|
||||
"SUPABASE_URL"
|
||||
].includes(secretObj.name)
|
||||
) {
|
||||
secretsToDelete.push(secretObj.name);
|
||||
return secretObj.name;
|
||||
}
|
||||
return [];
|
||||
});
|
||||
|
||||
await request.delete(
|
||||
@@ -1788,7 +1783,7 @@ const syncSecretsCheckly = async ({
|
||||
// sync secrets to checkly group envars
|
||||
|
||||
let getGroupSecretsRes = (
|
||||
await request.get(
|
||||
await request.get<{ environmentVariables: { key: string; value: string }[] }>(
|
||||
`${IntegrationUrls.CHECKLY_API_URL}/v1/check-groups/${integration.targetServiceId}`,
|
||||
{
|
||||
headers: {
|
||||
@@ -1799,11 +1794,11 @@ const syncSecretsCheckly = async ({
|
||||
}
|
||||
)
|
||||
).data.environmentVariables.reduce(
|
||||
(obj: any, secret: any) => ({
|
||||
(obj, secret) => ({
|
||||
...obj,
|
||||
[secret.key]: secret.value
|
||||
}),
|
||||
{}
|
||||
{} as Record<string, string>
|
||||
);
|
||||
|
||||
getGroupSecretsRes = Object.keys(getGroupSecretsRes).reduce(
|
||||
@@ -1846,19 +1841,22 @@ const syncSecretsCheckly = async ({
|
||||
// sync secrets to checkly global envars
|
||||
|
||||
let getSecretsRes = (
|
||||
await request.get(`${IntegrationUrls.CHECKLY_API_URL}/v1/variables`, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
"Accept-Encoding": "application/json",
|
||||
"X-Checkly-Account": integration.appId
|
||||
await request.get<{ key: string; value: string }[]>(
|
||||
`${IntegrationUrls.CHECKLY_API_URL}/v1/variables`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
"Accept-Encoding": "application/json",
|
||||
"X-Checkly-Account": integration.appId
|
||||
}
|
||||
}
|
||||
})
|
||||
)
|
||||
).data.reduce(
|
||||
(obj: any, secret: any) => ({
|
||||
(obj, secret) => ({
|
||||
...obj,
|
||||
[secret.key]: secret.value
|
||||
}),
|
||||
{}
|
||||
{} as Record<string, string>
|
||||
);
|
||||
|
||||
getSecretsRes = Object.keys(getSecretsRes).reduce(
|
||||
@@ -1901,7 +1899,7 @@ const syncSecretsCheckly = async ({
|
||||
);
|
||||
// case: secret exists in checkly
|
||||
// -> update/set secret
|
||||
} else if (secrets[key] !== getSecretsRes[key]) {
|
||||
} else if (secrets[key].value !== getSecretsRes[key]) {
|
||||
await request.put(
|
||||
`${IntegrationUrls.CHECKLY_API_URL}/v1/variables/${key}`,
|
||||
{
|
||||
@@ -1951,7 +1949,7 @@ const syncSecretsQovery = async ({
|
||||
accessToken: string;
|
||||
}) => {
|
||||
const getSecretsRes = (
|
||||
await request.get(
|
||||
await request.get<{ results: { id: string; value: string; key: string }[] }>(
|
||||
`${IntegrationUrls.QOVERY_API_URL}/${integration.scope}/${integration.appId}/environmentVariable`,
|
||||
{
|
||||
headers: {
|
||||
@@ -1961,11 +1959,11 @@ const syncSecretsQovery = async ({
|
||||
}
|
||||
)
|
||||
).data.results.reduce(
|
||||
(obj: any, secret: any) => ({
|
||||
(obj, secret) => ({
|
||||
...obj,
|
||||
[secret.key]: { id: secret.id, value: secret.value }
|
||||
}),
|
||||
{}
|
||||
{} as Record<string, { id: string; value: string }>
|
||||
);
|
||||
|
||||
// add secrets
|
||||
@@ -2042,7 +2040,7 @@ const syncSecretsTerraformCloud = async ({
|
||||
}) => {
|
||||
// get secrets from Terraform Cloud
|
||||
const getSecretsRes = (
|
||||
await request.get(
|
||||
await request.get<{ data: { attributes: { key: string; value: string }; id: string }[] }>(
|
||||
`${IntegrationUrls.TERRAFORM_CLOUD_API_URL}/api/v2/workspaces/${integration.appId}/vars`,
|
||||
{
|
||||
headers: {
|
||||
@@ -2052,11 +2050,11 @@ const syncSecretsTerraformCloud = async ({
|
||||
}
|
||||
)
|
||||
).data.data.reduce(
|
||||
(obj: any, secret: any) => ({
|
||||
(obj, secret) => ({
|
||||
...obj,
|
||||
[secret.attributes.key]: secret
|
||||
}),
|
||||
{}
|
||||
{} as Record<string, { attributes: { key: string; value: string }; id: string }>
|
||||
);
|
||||
|
||||
// create or update secrets on Terraform Cloud
|
||||
@@ -2138,7 +2136,7 @@ const syncSecretsTeamCity = async ({
|
||||
}: {
|
||||
integrationAuth: TIntegrationAuths;
|
||||
integration: TIntegrations;
|
||||
secrets: any;
|
||||
secrets: Record<string, { value: string; comment?: string }>;
|
||||
accessToken: string;
|
||||
}) => {
|
||||
interface TeamCitySecret {
|
||||
@@ -2171,13 +2169,16 @@ const syncSecretsTeamCity = async ({
|
||||
)
|
||||
).data.property
|
||||
.filter((parameter) => !parameter.inherited)
|
||||
.reduce((obj: any, secret: TeamCitySecret) => {
|
||||
const secretName = secret.name.replace(/^env\./, "");
|
||||
return {
|
||||
...obj,
|
||||
[secretName]: secret.value
|
||||
};
|
||||
}, {});
|
||||
.reduce(
|
||||
(obj, secret) => {
|
||||
const secretName = secret.name.replace(/^env\./, "");
|
||||
return {
|
||||
...obj,
|
||||
[secretName]: secret.value
|
||||
};
|
||||
},
|
||||
{} as Record<string, string>
|
||||
);
|
||||
|
||||
for await (const key of Object.keys(secrets)) {
|
||||
if (!(key in res) || (key in res && secrets[key].value !== res[key])) {
|
||||
@@ -2216,7 +2217,7 @@ const syncSecretsTeamCity = async ({
|
||||
} else {
|
||||
// case: sync to TeamCity project
|
||||
const res = (
|
||||
await request.get(
|
||||
await request.get<{ property: TeamCitySecret[] }>(
|
||||
`${integrationAuth.url}/app/rest/projects/id:${integration.appId}/parameters`,
|
||||
{
|
||||
headers: {
|
||||
@@ -2225,16 +2226,19 @@ const syncSecretsTeamCity = async ({
|
||||
}
|
||||
}
|
||||
)
|
||||
).data.property.reduce((obj: any, secret: TeamCitySecret) => {
|
||||
const secretName = secret.name.replace(/^env\./, "");
|
||||
return {
|
||||
...obj,
|
||||
[secretName]: secret.value
|
||||
};
|
||||
}, {});
|
||||
).data.property.reduce(
|
||||
(obj, secret) => {
|
||||
const secretName = secret.name.replace(/^env\./, "");
|
||||
return {
|
||||
...obj,
|
||||
[secretName]: secret.value
|
||||
};
|
||||
},
|
||||
{} as Record<string, string>
|
||||
);
|
||||
|
||||
for await (const key of Object.keys(secrets)) {
|
||||
if (!(key in res) || (key in res && secrets[key] !== res[key])) {
|
||||
if (!(key in res) || (key in res && secrets[key].value !== res[key])) {
|
||||
// case: secret does not exist in TeamCity or secret value has changed
|
||||
// -> create/update secret
|
||||
await request.post(
|
||||
@@ -2347,7 +2351,9 @@ const syncSecretsCloudflarePages = async ({
|
||||
}) => {
|
||||
// get secrets from cloudflare pages
|
||||
const getSecretsRes = (
|
||||
await request.get(
|
||||
await request.get<{
|
||||
result: { deployment_configs: Record<string, { env_vars: Record<string, unknown> }> };
|
||||
}>(
|
||||
`${IntegrationUrls.CLOUDFLARE_PAGES_API_URL}/client/v4/accounts/${accessId}/pages/projects/${integration.app}`,
|
||||
{
|
||||
headers: {
|
||||
@@ -2359,21 +2365,12 @@ const syncSecretsCloudflarePages = async ({
|
||||
).data.result.deployment_configs[integration.targetEnvironment as string].env_vars;
|
||||
|
||||
// copy the secrets object, so we can set deleted keys to null
|
||||
const secretsObj: any = getSecretKeyValuePair(secrets);
|
||||
|
||||
for (const [key, val] of Object.entries(secretsObj)) {
|
||||
secretsObj[key] = { type: "secret_text", value: val };
|
||||
}
|
||||
|
||||
if (getSecretsRes) {
|
||||
for await (const key of Object.keys(getSecretsRes)) {
|
||||
if (!(key in secrets)) {
|
||||
// case: secret does not exist in infisical
|
||||
// -> delete secret from cloudflare pages
|
||||
secretsObj[key] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
const secretsObj = Object.fromEntries(
|
||||
Object.entries(getSecretKeyValuePair(secrets)).map(([key, val]) => [
|
||||
key,
|
||||
key in Object.keys(getSecretsRes) ? { type: "secret_text", value: val } : null
|
||||
])
|
||||
);
|
||||
|
||||
const data = {
|
||||
deployment_configs: {
|
||||
@@ -2411,7 +2408,7 @@ const syncSecretsCloudflareWorkers = async ({
|
||||
}) => {
|
||||
// get secrets from cloudflare workers
|
||||
const getSecretsRes = (
|
||||
await request.get(
|
||||
await request.get<{ result: { name: string }[] }>(
|
||||
`${IntegrationUrls.CLOUDFLARE_WORKERS_API_URL}/client/v4/accounts/${accessId}/workers/scripts/${integration.app}/secrets`,
|
||||
{
|
||||
headers: {
|
||||
@@ -2422,33 +2419,36 @@ const syncSecretsCloudflareWorkers = async ({
|
||||
)
|
||||
).data.result;
|
||||
|
||||
const secretsObj: any = getSecretKeyValuePair(secrets);
|
||||
|
||||
for (const [key, val] of Object.entries(secretsObj)) {
|
||||
secretsObj[key] = { type: "secret_text", value: val };
|
||||
}
|
||||
const secretsObj = Object.fromEntries(
|
||||
Object.entries(getSecretKeyValuePair(secrets)).map(([key, val]) => [
|
||||
key,
|
||||
{ type: "secret_text", value: val }
|
||||
])
|
||||
);
|
||||
|
||||
// get deleted secrets list
|
||||
const deletedSecretKeys: string[] = [];
|
||||
if (getSecretsRes) {
|
||||
getSecretsRes.forEach((secretRes: any) => {
|
||||
getSecretsRes.forEach((secretRes) => {
|
||||
if (!Object.keys(secrets).includes(secretRes.name)) {
|
||||
deletedSecretKeys.push(secretRes.name);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
deletedSecretKeys.forEach(async (secretKey) => {
|
||||
await request.delete(
|
||||
`${IntegrationUrls.CLOUDFLARE_WORKERS_API_URL}/client/v4/accounts/${accessId}/workers/scripts/${integration.app}/secrets/${secretKey}`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
Accept: "application/json"
|
||||
await Promise.all(
|
||||
deletedSecretKeys.map(async (secretKey) => {
|
||||
return request.delete(
|
||||
`${IntegrationUrls.CLOUDFLARE_WORKERS_API_URL}/client/v4/accounts/${accessId}/workers/scripts/${integration.app}/secrets/${secretKey}`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
Accept: "application/json"
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
);
|
||||
})
|
||||
);
|
||||
|
||||
interface ConvertedSecret {
|
||||
name: string;
|
||||
@@ -2471,27 +2471,24 @@ const syncSecretsCloudflareWorkers = async ({
|
||||
})
|
||||
);
|
||||
|
||||
data.forEach(async (secret) => {
|
||||
await request.put(
|
||||
`${IntegrationUrls.CLOUDFLARE_WORKERS_API_URL}/client/v4/accounts/${accessId}/workers/scripts/${integration.app}/secrets`,
|
||||
secret,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
Accept: "application/json"
|
||||
await Promise.all(
|
||||
data.map(async (secret) => {
|
||||
return request.put(
|
||||
`${IntegrationUrls.CLOUDFLARE_WORKERS_API_URL}/client/v4/accounts/${accessId}/workers/scripts/${integration.app}/secrets`,
|
||||
secret,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
Accept: "application/json"
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
);
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Sync/push [secrets] to BitBucket repo with name [integration.app]
|
||||
* @param {Object} obj
|
||||
* @param {TIntegrations} obj.integration - integration details
|
||||
* @param {TIntegrationAuth} obj.integrationAuth - integration auth details
|
||||
* @param {Object} obj.secrets - secrets to push to integration (object where keys are secret keys and values are secret values)
|
||||
* @param {String} obj.accessToken - access token for BitBucket integration
|
||||
*/
|
||||
const syncSecretsBitBucket = async ({
|
||||
integration,
|
||||
@@ -2700,18 +2697,21 @@ const syncSecretsWindmill = async ({
|
||||
|
||||
// get secrets stored in windmill workspace
|
||||
const res = (
|
||||
await request.get(`${IntegrationUrls.WINDMILL_API_URL}/w/${integration.appId}/variables/list`, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
"Accept-Encoding": "application/json"
|
||||
await request.get<WindmillSecret[]>(
|
||||
`${IntegrationUrls.WINDMILL_API_URL}/w/${integration.appId}/variables/list`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
"Accept-Encoding": "application/json"
|
||||
}
|
||||
}
|
||||
})
|
||||
)
|
||||
).data.reduce(
|
||||
(obj: any, secret: WindmillSecret) => ({
|
||||
(obj, secret) => ({
|
||||
...obj,
|
||||
[secret.path]: secret
|
||||
}),
|
||||
{}
|
||||
{} as Record<string, WindmillSecret>
|
||||
);
|
||||
|
||||
// eslint-disable-next-line
|
||||
@@ -2778,11 +2778,6 @@ const syncSecretsWindmill = async ({
|
||||
|
||||
/**
|
||||
* Sync/push [secrets] to Cloud66 application with name [integration.app]
|
||||
* @param {Object} obj
|
||||
* @param {TIntegrations} obj.integration - integration details
|
||||
* @param {TIntegrationAuth} obj.integrationAuth - integration auth details
|
||||
* @param {Object} obj.secrets - secrets to push to integration (object where keys are secret keys and values are secret values)
|
||||
* @param {String} obj.accessToken - access token for Cloud66 integration
|
||||
*/
|
||||
const syncSecretsCloud66 = async ({
|
||||
integration,
|
||||
@@ -2802,12 +2797,12 @@ const syncSecretsCloud66 = async ({
|
||||
updated_at: string;
|
||||
is_password: boolean;
|
||||
is_generated: boolean;
|
||||
history: any[];
|
||||
history: unknown[];
|
||||
}
|
||||
|
||||
// get all current secrets
|
||||
const res = (
|
||||
await request.get(
|
||||
await request.get<{ response: Cloud66Secret[] }>(
|
||||
`${IntegrationUrls.CLOUD_66_API_URL}/3/stacks/${integration.appId}/environments`,
|
||||
{
|
||||
headers: {
|
||||
@@ -2817,9 +2812,9 @@ const syncSecretsCloud66 = async ({
|
||||
}
|
||||
)
|
||||
).data.response
|
||||
.filter((secret: Cloud66Secret) => !secret.readonly || !secret.is_generated)
|
||||
.filter((secret) => !secret.readonly || !secret.is_generated)
|
||||
.reduce(
|
||||
(obj: any, secret: any) => ({
|
||||
(obj, secret) => ({
|
||||
...obj,
|
||||
[secret.key]: secret
|
||||
}),
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { request } from "@app/lib/config/request";
|
||||
import { BadRequestError } from "@app/lib/errors";
|
||||
|
||||
import { Integrations,IntegrationUrls } from "./integration-list";
|
||||
import { Integrations, IntegrationUrls } from "./integration-list";
|
||||
|
||||
type Team = {
|
||||
name: string;
|
||||
@@ -12,7 +12,7 @@ const getTeamsGitLab = async ({ url, accessToken }: { url: string; accessToken:
|
||||
|
||||
let teams: Team[] = [];
|
||||
const res = (
|
||||
await request.get(`${gitLabApiUrl}/v4/groups`, {
|
||||
await request.get<{ name: string; id: string }[]>(`${gitLabApiUrl}/v4/groups`, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
"Accept-Encoding": "application/json"
|
||||
@@ -20,7 +20,7 @@ const getTeamsGitLab = async ({ url, accessToken }: { url: string; accessToken:
|
||||
})
|
||||
).data;
|
||||
|
||||
teams = res.map((t: any) => ({
|
||||
teams = res.map((t) => ({
|
||||
name: t.name,
|
||||
teamId: t.id
|
||||
}));
|
||||
|
||||
@@ -80,8 +80,8 @@ const exchangeCodeGCP = async ({ code }: { code: string }) => {
|
||||
throw new BadRequestError({ message: "Missing client id and client secret" });
|
||||
}
|
||||
|
||||
const res: ExchangeCodeGCPResponse = (
|
||||
await request.post(
|
||||
const res = (
|
||||
await request.post<ExchangeCodeGCPResponse>(
|
||||
IntegrationUrls.GCP_TOKEN_URL,
|
||||
new URLSearchParams({
|
||||
grant_type: "authorization_code",
|
||||
@@ -108,8 +108,8 @@ const exchangeCodeAzure = async ({ code }: { code: string }) => {
|
||||
if (!appCfg.CLIENT_ID_AZURE || !appCfg.CLIENT_SECRET_AZURE) {
|
||||
throw new BadRequestError({ message: "Missing client id and client secret" });
|
||||
}
|
||||
const res: ExchangeCodeAzureResponse = (
|
||||
await request.post(
|
||||
const res = (
|
||||
await request.post<ExchangeCodeAzureResponse>(
|
||||
IntegrationUrls.AZURE_TOKEN_URL,
|
||||
new URLSearchParams({
|
||||
grant_type: "authorization_code",
|
||||
@@ -118,7 +118,7 @@ const exchangeCodeAzure = async ({ code }: { code: string }) => {
|
||||
client_id: appCfg.CLIENT_ID_AZURE,
|
||||
client_secret: appCfg.CLIENT_SECRET_AZURE,
|
||||
redirect_uri: `${appCfg.SITE_URL}/integrations/azure-key-vault/oauth2/callback`
|
||||
} as any)
|
||||
})
|
||||
)
|
||||
).data;
|
||||
|
||||
@@ -138,8 +138,8 @@ const exchangeCodeHeroku = async ({ code }: { code: string }) => {
|
||||
throw new BadRequestError({ message: "Missing client id and client secret" });
|
||||
}
|
||||
|
||||
const res: ExchangeCodeHerokuResponse = (
|
||||
await request.post(
|
||||
const res = (
|
||||
await request.post<ExchangeCodeHerokuResponse>(
|
||||
IntegrationUrls.HEROKU_TOKEN_URL,
|
||||
new URLSearchParams({
|
||||
grant_type: "authorization_code",
|
||||
@@ -160,12 +160,6 @@ const exchangeCodeHeroku = async ({ code }: { code: string }) => {
|
||||
/**
|
||||
* Return [accessToken], [accessExpiresAt], and [refreshToken] for Vercel
|
||||
* code-token exchange
|
||||
* @param {Object} obj1
|
||||
* @param {Object} obj1.code - code for code-token exchange
|
||||
* @returns {Object} obj2
|
||||
* @returns {String} obj2.accessToken - access token for Heroku API
|
||||
* @returns {String} obj2.refreshToken - refresh token for Heroku API
|
||||
* @returns {Date} obj2.accessExpiresAt - date of expiration for access token
|
||||
*/
|
||||
const exchangeCodeVercel = async ({ code }: { code: string }) => {
|
||||
const appCfg = getConfig();
|
||||
@@ -173,15 +167,15 @@ const exchangeCodeVercel = async ({ code }: { code: string }) => {
|
||||
throw new BadRequestError({ message: "Missing client id and client secret" });
|
||||
}
|
||||
|
||||
const res: ExchangeCodeVercelResponse = (
|
||||
await request.post(
|
||||
const res = (
|
||||
await request.post<ExchangeCodeVercelResponse>(
|
||||
IntegrationUrls.VERCEL_TOKEN_URL,
|
||||
new URLSearchParams({
|
||||
code,
|
||||
client_id: appCfg.CLIENT_ID_VERCEL,
|
||||
client_secret: appCfg.CLIENT_SECRET_VERCEL,
|
||||
redirect_uri: `${appCfg.SITE_URL}/integrations/vercel/oauth2/callback`
|
||||
} as any)
|
||||
})
|
||||
)
|
||||
).data;
|
||||
|
||||
@@ -196,12 +190,6 @@ const exchangeCodeVercel = async ({ code }: { code: string }) => {
|
||||
/**
|
||||
* Return [accessToken], [accessExpiresAt], and [refreshToken] for Vercel
|
||||
* code-token exchange
|
||||
* @param {Object} obj1
|
||||
* @param {Object} obj1.code - code for code-token exchange
|
||||
* @returns {Object} obj2
|
||||
* @returns {String} obj2.accessToken - access token for Heroku API
|
||||
* @returns {String} obj2.refreshToken - refresh token for Heroku API
|
||||
* @returns {Date} obj2.accessExpiresAt - date of expiration for access token
|
||||
*/
|
||||
const exchangeCodeNetlify = async ({ code }: { code: string }) => {
|
||||
const appCfg = getConfig();
|
||||
@@ -209,8 +197,8 @@ const exchangeCodeNetlify = async ({ code }: { code: string }) => {
|
||||
throw new BadRequestError({ message: "Missing client id and client secret" });
|
||||
}
|
||||
|
||||
const res: ExchangeCodeNetlifyResponse = (
|
||||
await request.post(
|
||||
const res = (
|
||||
await request.post<ExchangeCodeNetlifyResponse>(
|
||||
IntegrationUrls.NETLIFY_TOKEN_URL,
|
||||
new URLSearchParams({
|
||||
grant_type: "authorization_code",
|
||||
@@ -218,7 +206,7 @@ const exchangeCodeNetlify = async ({ code }: { code: string }) => {
|
||||
client_id: appCfg.CLIENT_ID_NETLIFY,
|
||||
client_secret: appCfg.CLIENT_SECRET_NETLIFY,
|
||||
redirect_uri: `${appCfg.SITE_URL}/integrations/netlify/oauth2/callback`
|
||||
} as any)
|
||||
})
|
||||
)
|
||||
).data;
|
||||
|
||||
@@ -230,7 +218,7 @@ const exchangeCodeNetlify = async ({ code }: { code: string }) => {
|
||||
// });
|
||||
|
||||
const res3 = (
|
||||
await request.get("https://api.netlify.com/api/v1/accounts", {
|
||||
await request.get<Array<{ id: string }>>("https://api.netlify.com/api/v1/accounts", {
|
||||
headers: {
|
||||
Authorization: `Bearer ${res.access_token}`
|
||||
}
|
||||
@@ -252,8 +240,8 @@ const exchangeCodeGithub = async ({ code }: { code: string }) => {
|
||||
throw new BadRequestError({ message: "Missing client id and client secret" });
|
||||
}
|
||||
|
||||
const res: ExchangeCodeGithubResponse = (
|
||||
await request.get(IntegrationUrls.GITHUB_TOKEN_URL, {
|
||||
const res = (
|
||||
await request.get<ExchangeCodeGithubResponse>(IntegrationUrls.GITHUB_TOKEN_URL, {
|
||||
params: {
|
||||
client_id: appCfg.CLIENT_ID_GITHUB,
|
||||
client_secret: appCfg.CLIENT_SECRET_GITHUB,
|
||||
@@ -285,8 +273,8 @@ const exchangeCodeGitlab = async ({ code, url }: { code: string; url?: string })
|
||||
throw new BadRequestError({ message: "Missing client id and client secret" });
|
||||
}
|
||||
|
||||
const res: ExchangeCodeGitlabResponse = (
|
||||
await request.post(
|
||||
const res = (
|
||||
await request.post<ExchangeCodeGitlabResponse>(
|
||||
url ? `${url}/oauth/token` : IntegrationUrls.GITLAB_TOKEN_URL,
|
||||
new URLSearchParams({
|
||||
grant_type: "authorization_code",
|
||||
@@ -294,7 +282,7 @@ const exchangeCodeGitlab = async ({ code, url }: { code: string; url?: string })
|
||||
client_id: appCfg.CLIENT_ID_GITLAB,
|
||||
client_secret: appCfg.CLIENT_SECRET_GITLAB,
|
||||
redirect_uri: `${appCfg.SITE_URL}/integrations/gitlab/oauth2/callback`
|
||||
} as any),
|
||||
}),
|
||||
{
|
||||
headers: {
|
||||
"Accept-Encoding": "application/json"
|
||||
@@ -324,8 +312,8 @@ const exchangeCodeBitBucket = async ({ code }: { code: string }) => {
|
||||
throw new BadRequestError({ message: "Missing client id and client secret" });
|
||||
}
|
||||
|
||||
const res: ExchangeCodeBitBucketResponse = (
|
||||
await request.post(
|
||||
const res = (
|
||||
await request.post<ExchangeCodeBitBucketResponse>(
|
||||
IntegrationUrls.BITBUCKET_TOKEN_URL,
|
||||
new URLSearchParams({
|
||||
grant_type: "authorization_code",
|
||||
@@ -333,7 +321,7 @@ const exchangeCodeBitBucket = async ({ code }: { code: string }) => {
|
||||
client_id: appCfg.CLIENT_ID_BITBUCKET,
|
||||
client_secret: appCfg.CLIENT_SECRET_BITBUCKET,
|
||||
redirect_uri: `${appCfg.SITE_URL}/integrations/bitbucket/oauth2/callback`
|
||||
} as any),
|
||||
}),
|
||||
{
|
||||
headers: {
|
||||
"Accept-Encoding": "application/json"
|
||||
@@ -593,7 +581,7 @@ const exchangeRefreshBitBucket = async ({ refreshToken }: { refreshToken: string
|
||||
client_id: appCfg.CLIENT_ID_BITBUCKET,
|
||||
client_secret: appCfg.CLIENT_SECRET_BITBUCKET,
|
||||
redirect_uri: `${appCfg.SITE_URL}/integrations/bitbucket/oauth2/callback`
|
||||
} as any),
|
||||
}),
|
||||
{
|
||||
headers: {
|
||||
"Accept-Encoding": "application/json"
|
||||
@@ -624,7 +612,11 @@ const exchangeRefreshGCPSecretManager = async ({
|
||||
const accessExpiresAt = new Date();
|
||||
|
||||
if (metadata?.authMethod === "serviceAccount") {
|
||||
const serviceAccount = JSON.parse(refreshToken);
|
||||
const serviceAccount = JSON.parse(refreshToken) as {
|
||||
client_email: string;
|
||||
token_uri: string;
|
||||
private_key: string;
|
||||
};
|
||||
|
||||
const payload = {
|
||||
iss: serviceAccount.client_email,
|
||||
@@ -663,14 +655,14 @@ const exchangeRefreshGCPSecretManager = async ({
|
||||
if (!appCfg.CLIENT_SECRET_GCP_SECRET_MANAGER || !appCfg.CLIENT_ID_GCP_SECRET_MANAGER) {
|
||||
throw new BadRequestError({ message: "Missing client id and client secret" });
|
||||
}
|
||||
const { data }: { data: RefreshTokenGCPSecretManagerResponse } = await request.post(
|
||||
const { data } = await request.post<RefreshTokenGCPSecretManagerResponse>(
|
||||
IntegrationUrls.GCP_TOKEN_URL,
|
||||
new URLSearchParams({
|
||||
client_id: appCfg.CLIENT_ID_GCP_SECRET_MANAGER,
|
||||
client_secret: appCfg.CLIENT_SECRET_GCP_SECRET_MANAGER,
|
||||
refresh_token: refreshToken,
|
||||
grant_type: "refresh_token"
|
||||
} as any)
|
||||
})
|
||||
);
|
||||
|
||||
accessExpiresAt.setSeconds(accessExpiresAt.getSeconds() + data.expires_in);
|
||||
|
||||
@@ -7,7 +7,8 @@ import {
|
||||
TOrganizationsInsert,
|
||||
TOrgMemberships,
|
||||
TOrgMembershipsInsert,
|
||||
TOrgMembershipsUpdate
|
||||
TOrgMembershipsUpdate,
|
||||
TUserEncryptionKeys
|
||||
} from "@app/db/schemas";
|
||||
import { DatabaseError } from "@app/lib/errors";
|
||||
import {
|
||||
@@ -40,7 +41,7 @@ export const orgDALFactory = (db: TDbClient) => {
|
||||
`${TableName.OrgMembership}.orgId`,
|
||||
`${TableName.Organization}.id`
|
||||
)
|
||||
.select(`${TableName.Organization}.*`);
|
||||
.select(selectAllTableCols(TableName.Organization));
|
||||
return org;
|
||||
} catch (error) {
|
||||
throw new DatabaseError({ error, name: "Find all org by user id" });
|
||||
@@ -50,9 +51,9 @@ export const orgDALFactory = (db: TDbClient) => {
|
||||
const findOrgByProjectId = async (projectId: string): Promise<TOrganizations> => {
|
||||
try {
|
||||
const [org] = await db(TableName.Project)
|
||||
.where({ [`${[TableName.Project]}.id`]: projectId })
|
||||
.where({ [`${TableName.Project}.id` as "id"]: projectId })
|
||||
.join(TableName.Organization, `${TableName.Project}.orgId`, `${TableName.Organization}.id`)
|
||||
.select(`${TableName.Organization}.*`);
|
||||
.select(selectAllTableCols(TableName.Organization));
|
||||
|
||||
return org;
|
||||
} catch (error) {
|
||||
@@ -66,7 +67,7 @@ export const orgDALFactory = (db: TDbClient) => {
|
||||
const members = await db(TableName.OrgMembership)
|
||||
.where({ orgId })
|
||||
.join(TableName.Users, `${TableName.OrgMembership}.userId`, `${TableName.Users}.id`)
|
||||
.leftJoin(
|
||||
.leftJoin<TUserEncryptionKeys>(
|
||||
TableName.UserEncryptionKey,
|
||||
`${TableName.UserEncryptionKey}.userId`,
|
||||
`${TableName.Users}.id`
|
||||
@@ -185,16 +186,17 @@ export const orgDALFactory = (db: TDbClient) => {
|
||||
) => {
|
||||
try {
|
||||
const query = (tx || db)(TableName.OrgMembership)
|
||||
// eslint-disable-next-line
|
||||
.where(buildFindFilter(filter))
|
||||
.join(TableName.Users, `${TableName.Users}.id`, `${TableName.OrgMembership}.userId`)
|
||||
.select(
|
||||
selectAllTableCols(TableName.OrgMembership),
|
||||
db.ref("email").withSchema(TableName.Users)
|
||||
);
|
||||
if (limit) query.limit(limit);
|
||||
if (offset) query.offset(offset);
|
||||
if (limit) void query.limit(limit);
|
||||
if (offset) void query.offset(offset);
|
||||
if (sort) {
|
||||
query.orderBy(
|
||||
void query.orderBy(
|
||||
sort.map(([column, order, nulls]) => ({ column: column as string, order, nulls }))
|
||||
);
|
||||
}
|
||||
|
||||
@@ -209,7 +209,7 @@ export const orgServiceFactory = ({
|
||||
* */
|
||||
const deleteOrganizationById = async (userId: string, orgId: string) => {
|
||||
const { membership } = await permissionService.getUserOrgPermission(userId, orgId);
|
||||
if (membership.role !== OrgMembershipRole.Admin)
|
||||
if ((membership.role as OrgMembershipRole) !== OrgMembershipRole.Admin)
|
||||
throw new UnauthorizedError({ name: "Delete org by id", message: "Not an admin" });
|
||||
|
||||
const organization = await orgDAL.deleteById(orgId);
|
||||
|
||||
@@ -43,7 +43,7 @@ export const projectBotServiceFactory = ({
|
||||
if (!bot.encryptedProjectKeyNonce || !bot.encryptedProjectKey)
|
||||
throw new BadRequestError({ message: "Encryption key missing" });
|
||||
|
||||
if (rootEncryptionKey && bot.keyEncoding === SecretKeyEncoding.BASE64) {
|
||||
if (rootEncryptionKey && (bot.keyEncoding as SecretKeyEncoding) === SecretKeyEncoding.BASE64) {
|
||||
const privateKeyBot = decryptSymmetric({
|
||||
iv: bot.iv,
|
||||
tag: bot.tag,
|
||||
@@ -57,7 +57,7 @@ export const projectBotServiceFactory = ({
|
||||
publicKey: bot.sender.publicKey
|
||||
});
|
||||
}
|
||||
if (encryptionKey && bot.keyEncoding === SecretKeyEncoding.UTF8) {
|
||||
if (encryptionKey && (bot.keyEncoding as SecretKeyEncoding) === SecretKeyEncoding.UTF8) {
|
||||
const privateKeyBot = decryptSymmetric128BitHexKeyUTF8({
|
||||
iv: bot.iv,
|
||||
tag: bot.tag,
|
||||
|
||||
@@ -26,7 +26,7 @@ export const projectEnvDALFactory = (db: TDbClient) => {
|
||||
const findLastEnvPosition = async (projectId: string, tx?: Knex) => {
|
||||
const lastPos = await (tx || db)(TableName.Environment)
|
||||
.where({ projectId })
|
||||
.max({ position: "position" })
|
||||
.max("position", { as: "position" })
|
||||
.first();
|
||||
return lastPos?.position || 0;
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { TDbClient } from "@app/db";
|
||||
import { TableName, TProjectKeys } from "@app/db/schemas";
|
||||
import { DatabaseError } from "@app/lib/errors";
|
||||
import { ormify } from "@app/lib/knex";
|
||||
import { ormify, selectAllTableCols } from "@app/lib/knex";
|
||||
|
||||
export type TProjectKeyDALFactory = ReturnType<typeof projectKeyDALFactory>;
|
||||
|
||||
@@ -11,25 +11,23 @@ export const projectKeyDALFactory = (db: TDbClient) => {
|
||||
const findLatestProjectKey = async (
|
||||
userId: string,
|
||||
projectId: string
|
||||
): Promise<TProjectKeys & { sender: { publicKey: string } }> => {
|
||||
): Promise<(TProjectKeys & { sender: { publicKey: string } }) | undefined> => {
|
||||
try {
|
||||
const projectKey = await db(TableName.ProjectKeys)
|
||||
.where({ projectId, receiverId: userId })
|
||||
.join(TableName.Users, `${TableName.ProjectKeys}.senderId`, `${TableName.Users}.id`)
|
||||
.join(
|
||||
TableName.UserEncryptionKey,
|
||||
`${TableName.UserEncryptionKey}.userId`,
|
||||
`${TableName.Users}.id`
|
||||
)
|
||||
.where({ projectId, receiverId: userId })
|
||||
.orderBy("createdAt", "desc", "last")
|
||||
.select(`${TableName.ProjectKeys}.*`, `${TableName.UserEncryptionKey}.publicKey`)
|
||||
.select(selectAllTableCols(TableName.ProjectKeys))
|
||||
.select(db.ref("publicKey").withSchema(TableName.UserEncryptionKey))
|
||||
.first();
|
||||
if (projectKey) {
|
||||
projectKey.sender = {
|
||||
publicKey: projectKey.publicKey
|
||||
};
|
||||
return { ...projectKey, sender: { publicKey: projectKey.publicKey } };
|
||||
}
|
||||
return projectKey;
|
||||
} catch (error) {
|
||||
throw new DatabaseError({ error, name: "Find latest project key" });
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { TDbClient } from "@app/db";
|
||||
import { TableName } from "@app/db/schemas";
|
||||
import { TableName, TUserEncryptionKeys } from "@app/db/schemas";
|
||||
import { DatabaseError } from "@app/lib/errors";
|
||||
import { ormify } from "@app/lib/knex";
|
||||
|
||||
@@ -14,7 +14,7 @@ export const projectMembershipDALFactory = (db: TDbClient) => {
|
||||
const members = await db(TableName.ProjectMembership)
|
||||
.where({ projectId })
|
||||
.join(TableName.Users, `${TableName.ProjectMembership}.userId`, `${TableName.Users}.id`)
|
||||
.join(
|
||||
.join<TUserEncryptionKeys>(
|
||||
TableName.UserEncryptionKey,
|
||||
`${TableName.UserEncryptionKey}.userId`,
|
||||
`${TableName.Users}.id`
|
||||
@@ -25,10 +25,10 @@ export const projectMembershipDALFactory = (db: TDbClient) => {
|
||||
db.ref("role").withSchema(TableName.ProjectMembership),
|
||||
db.ref("roleId").withSchema(TableName.ProjectMembership),
|
||||
db.ref("email").withSchema(TableName.Users),
|
||||
db.ref("publicKey").withSchema(TableName.UserEncryptionKey),
|
||||
db.ref("firstName").withSchema(TableName.Users),
|
||||
db.ref("lastName").withSchema(TableName.Users),
|
||||
db.ref("id").withSchema(TableName.Users).as("userId"),
|
||||
db.ref("publicKey").withSchema(TableName.UserEncryptionKey)
|
||||
db.ref("id").withSchema(TableName.Users).as("userId")
|
||||
);
|
||||
return members.map(({ email, firstName, lastName, publicKey, ...data }) => ({
|
||||
...data,
|
||||
|
||||
@@ -184,7 +184,7 @@ export const projectMembershipServiceFactory = ({
|
||||
await smtpService.sendMail({
|
||||
template: SmtpTemplates.WorkspaceInvite,
|
||||
subjectLine: "Infisical workspace invitation",
|
||||
recipients: orgMembers.map(({ email }) => email).filter(Boolean) as string[],
|
||||
recipients: orgMembers.map(({ email }) => email).filter(Boolean),
|
||||
substitutions: {
|
||||
inviterFirstName: sender.firstName,
|
||||
inviterEmail: sender.email,
|
||||
|
||||
@@ -25,8 +25,8 @@ export const secretBlindIndexDALFactory = (db: TDbClient) => {
|
||||
)
|
||||
.where({ projectId })
|
||||
.whereNull("secretBlindIndex")
|
||||
.count(`${TableName.Secret}.id`);
|
||||
return (doc as any)?.[0]?.count || 0;
|
||||
.count(`${TableName.Secret}.id` as "id");
|
||||
return doc?.[0]?.count || 0;
|
||||
} catch (error) {
|
||||
throw new DatabaseError({ error, name: "CountOfSecretWillNullSecretBlindIndex" });
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ const sqlFindMultipleFolderByEnvPathQuery = (
|
||||
// Thus each node has connection to parent node
|
||||
// for a given path from root we recursively reach to the leaf path or till we get null
|
||||
// the below query is the base case where we select root folder which has parent folder id as null
|
||||
baseQb
|
||||
void baseQb
|
||||
.select({
|
||||
depth: 1,
|
||||
// latestFolderVerId: db.raw("NULL::uuid"),
|
||||
@@ -57,34 +57,35 @@ const sqlFindMultipleFolderByEnvPathQuery = (
|
||||
formatedQuery.map(({ envId }) => envId)
|
||||
)
|
||||
.select(selectAllTableCols(TableName.SecretFolder))
|
||||
.union((qb) =>
|
||||
// for here on we keep going to next child node.
|
||||
// we also keep a measure of depth then we check the depth matches the array path segment and folder name
|
||||
// that is at depth 1 for a path /folder1/folder2 -> the name should be folder1
|
||||
qb
|
||||
.select({
|
||||
depth: db.raw("parent.depth + 1"),
|
||||
path: db.raw(
|
||||
"CONCAT((CASE WHEN parent.path = '/' THEN '' ELSE parent.path END),'/', secret_folders.name)"
|
||||
)
|
||||
})
|
||||
.select(selectAllTableCols(TableName.SecretFolder))
|
||||
.where((wb) =>
|
||||
formatedQuery.map(({ secretPath }) =>
|
||||
wb.orWhereRaw(
|
||||
`depth = array_position(ARRAY[${secretPath
|
||||
.map(() => "?")
|
||||
.join(",")}]::varchar[], ${TableName.SecretFolder}.name,depth)`,
|
||||
[...secretPath]
|
||||
.union(
|
||||
(qb) =>
|
||||
// for here on we keep going to next child node.
|
||||
// we also keep a measure of depth then we check the depth matches the array path segment and folder name
|
||||
// that is at depth 1 for a path /folder1/folder2 -> the name should be folder1
|
||||
void qb
|
||||
.select({
|
||||
depth: db.raw("parent.depth + 1"),
|
||||
path: db.raw(
|
||||
"CONCAT((CASE WHEN parent.path = '/' THEN '' ELSE parent.path END),'/', secret_folders.name)"
|
||||
)
|
||||
})
|
||||
.select(selectAllTableCols(TableName.SecretFolder))
|
||||
.where((wb) =>
|
||||
formatedQuery.map(({ secretPath }) =>
|
||||
wb.orWhereRaw(
|
||||
`depth = array_position(ARRAY[${secretPath
|
||||
.map(() => "?")
|
||||
.join(",")}]::varchar[], ${TableName.SecretFolder}.name,depth)`,
|
||||
[...secretPath]
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
.from(TableName.SecretFolder)
|
||||
.join("parent", (bd) =>
|
||||
bd
|
||||
.on("parent.id", `${TableName.SecretFolder}.parentId`)
|
||||
.andOn("parent.envId", `${TableName.SecretFolder}.envId`)
|
||||
)
|
||||
.from(TableName.SecretFolder)
|
||||
.join("parent", (bd) =>
|
||||
bd
|
||||
.on("parent.id", `${TableName.SecretFolder}.parentId`)
|
||||
.andOn("parent.envId", `${TableName.SecretFolder}.envId`)
|
||||
)
|
||||
);
|
||||
})
|
||||
.select("*")
|
||||
@@ -113,7 +114,7 @@ const sqlFindFolderByPathQuery = (
|
||||
// Thus each node has connection to parent node
|
||||
// for a given path from root we recursively reach to the leaf path or till we get null
|
||||
// the below query is the base case where we select root folder which has parent folder id as null
|
||||
baseQb
|
||||
void baseQb
|
||||
.select({
|
||||
depth: 1,
|
||||
// latestFolderVerId: db.raw("NULL::uuid"),
|
||||
@@ -131,26 +132,27 @@ const sqlFindFolderByPathQuery = (
|
||||
})
|
||||
.where(`${TableName.Environment}.slug`, environment)
|
||||
.select(selectAllTableCols(TableName.SecretFolder))
|
||||
.union((qb) =>
|
||||
// for here on we keep going to next child node.
|
||||
// we also keep a measure of depth then we check the depth matches the array path segment and folder name
|
||||
// that is at depth 1 for a path /folder1/folder2 -> the name should be folder1
|
||||
qb
|
||||
.select({
|
||||
depth: db.raw("parent.depth + 1"),
|
||||
path: db.raw(
|
||||
"CONCAT((CASE WHEN parent.path = '/' THEN '' ELSE parent.path END),'/', secret_folders.name)"
|
||||
.union(
|
||||
(qb) =>
|
||||
// for here on we keep going to next child node.
|
||||
// we also keep a measure of depth then we check the depth matches the array path segment and folder name
|
||||
// that is at depth 1 for a path /folder1/folder2 -> the name should be folder1
|
||||
void qb
|
||||
.select({
|
||||
depth: db.raw("parent.depth + 1"),
|
||||
path: db.raw(
|
||||
"CONCAT((CASE WHEN parent.path = '/' THEN '' ELSE parent.path END),'/', secret_folders.name)"
|
||||
)
|
||||
})
|
||||
.select(selectAllTableCols(TableName.SecretFolder))
|
||||
.whereRaw(
|
||||
`depth = array_position(ARRAY[${pathSegments
|
||||
.map(() => "?")
|
||||
.join(",")}]::varchar[], secret_folders.name,depth)`,
|
||||
[...pathSegments]
|
||||
)
|
||||
})
|
||||
.select(selectAllTableCols(TableName.SecretFolder))
|
||||
.whereRaw(
|
||||
`depth = array_position(ARRAY[${pathSegments
|
||||
.map(() => "?")
|
||||
.join(",")}]::varchar[], secret_folders.name,depth)`,
|
||||
[...pathSegments]
|
||||
)
|
||||
.from(TableName.SecretFolder)
|
||||
.join("parent", "parent.id", `${TableName.SecretFolder}.parentId`)
|
||||
.from(TableName.SecretFolder)
|
||||
.join("parent", "parent.id", `${TableName.SecretFolder}.parentId`)
|
||||
);
|
||||
})
|
||||
.from<TSecretFolders & { depth: number; path: string }>("parent")
|
||||
@@ -183,7 +185,7 @@ const sqlFindSecretPathByFolderId = (db: Knex, projectId: string, folderIds: str
|
||||
// first remember our folders are connected as a link list or known as adjacency list
|
||||
// Thus each node has connection to parent node
|
||||
// we first find the folder given in folder id
|
||||
baseQb
|
||||
void baseQb
|
||||
.from(TableName.SecretFolder)
|
||||
.select(selectAllTableCols(TableName.SecretFolder))
|
||||
.select({
|
||||
@@ -202,24 +204,25 @@ const sqlFindSecretPathByFolderId = (db: Knex, projectId: string, folderIds: str
|
||||
)
|
||||
.where({ projectId })
|
||||
.whereIn(`${TableName.SecretFolder}.id`, folderIds)
|
||||
.union((qb) =>
|
||||
// then we keep going up
|
||||
// until parent id is null
|
||||
qb
|
||||
.select(selectAllTableCols(TableName.SecretFolder))
|
||||
.select({
|
||||
// then we join join this folder name behind previous as we are going from child to parent
|
||||
// the root folder check is used to avoid last / and also root name in folders
|
||||
path: db.raw(
|
||||
`CONCAT( CASE
|
||||
.union(
|
||||
(qb) =>
|
||||
// then we keep going up
|
||||
// until parent id is null
|
||||
void qb
|
||||
.select(selectAllTableCols(TableName.SecretFolder))
|
||||
.select({
|
||||
// then we join join this folder name behind previous as we are going from child to parent
|
||||
// the root folder check is used to avoid last / and also root name in folders
|
||||
path: db.raw(
|
||||
`CONCAT( CASE
|
||||
WHEN ${TableName.SecretFolder}."parentId" is NULL THEN ''
|
||||
ELSE CONCAT('/', secret_folders.name)
|
||||
END, parent.path )`
|
||||
),
|
||||
child: db.raw("COALESCE(parent.child, parent.id)")
|
||||
})
|
||||
.from(TableName.SecretFolder)
|
||||
.join("parent", "parent.parentId", `${TableName.SecretFolder}.id`)
|
||||
),
|
||||
child: db.raw("COALESCE(parent.child, parent.id)")
|
||||
})
|
||||
.from(TableName.SecretFolder)
|
||||
.join("parent", "parent.parentId", `${TableName.SecretFolder}.id`)
|
||||
);
|
||||
})
|
||||
.select("*")
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Knex } from "knex";
|
||||
|
||||
import { TDbClient } from "@app/db";
|
||||
import { TableName,TSecretFolderVersions } from "@app/db/schemas";
|
||||
import { TableName, TSecretFolderVersions } from "@app/db/schemas";
|
||||
import { DatabaseError } from "@app/lib/errors";
|
||||
import { ormify, selectAllTableCols } from "@app/lib/knex";
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Knex } from "knex";
|
||||
|
||||
import { TDbClient } from "@app/db";
|
||||
import { TableName,TSecretImports } from "@app/db/schemas";
|
||||
import { TableName, TSecretImports } from "@app/db/schemas";
|
||||
import { DatabaseError } from "@app/lib/errors";
|
||||
import { ormify } from "@app/lib/knex";
|
||||
|
||||
@@ -15,7 +15,7 @@ export const secretImportDALFactory = (db: TDbClient) => {
|
||||
const findLastImportPosition = async (folderId: string, tx?: Knex) => {
|
||||
const lastPos = await (tx || db)(TableName.SecretImport)
|
||||
.where({ folderId })
|
||||
.max({ position: "position" })
|
||||
.max("position", { as: "position" })
|
||||
.first();
|
||||
return lastPos?.position || 0;
|
||||
};
|
||||
|
||||
@@ -63,7 +63,7 @@ export const secretDALFactory = (db: TDbClient) => {
|
||||
.where({ folderId })
|
||||
.where((bd) => {
|
||||
data.forEach((el) => {
|
||||
bd.orWhere({
|
||||
void bd.orWhere({
|
||||
secretBlindIndex: el.blindIndex,
|
||||
type: el.type,
|
||||
...(el.type === SecretType.Personal ? { userId } : {})
|
||||
@@ -89,7 +89,7 @@ export const secretDALFactory = (db: TDbClient) => {
|
||||
const secs = await (tx || db)(TableName.Secret)
|
||||
.where({ folderId })
|
||||
.where((bd) => {
|
||||
bd.whereNull("userId").orWhere({ userId: userId || null });
|
||||
void bd.whereNull("userId").orWhere({ userId: userId || null });
|
||||
})
|
||||
.leftJoin(
|
||||
TableName.JnSecretTag,
|
||||
@@ -144,7 +144,7 @@ export const secretDALFactory = (db: TDbClient) => {
|
||||
if (el.type === SecretType.Personal && !userId) {
|
||||
throw new BadRequestError({ message: "Missing personal user id" });
|
||||
}
|
||||
bd.orWhere({
|
||||
void bd.orWhere({
|
||||
secretBlindIndex: el.blindIndex,
|
||||
type: el.type,
|
||||
userId: el.type === SecretType.Personal ? userId : null
|
||||
|
||||
@@ -318,7 +318,7 @@ export const secretQueueFactory = ({
|
||||
botKey
|
||||
);
|
||||
const suffixedSecrets: typeof secrets = {};
|
||||
const metadata = integration.metadata as Record<string, any>;
|
||||
const metadata = integration.metadata as Record<string, string>;
|
||||
if (metadata) {
|
||||
Object.keys(secrets).forEach((key) => {
|
||||
const prefix = metadata?.secretPrefix || "";
|
||||
|
||||
@@ -41,8 +41,8 @@ export const getTlsOption = (host?: SmtpHost | string, secure?: boolean) => {
|
||||
if (!secure) return { secure: false };
|
||||
if (!host) return { secure: true };
|
||||
|
||||
if (host === SmtpHost.Sendgrid) {
|
||||
return { secure: true, port: 465}; // more details here https://nodemailer.com/smtp/
|
||||
if ((host as SmtpHost) === SmtpHost.Sendgrid) {
|
||||
return { secure: true, port: 465 }; // more details here https://nodemailer.com/smtp/
|
||||
}
|
||||
if (host.includes("amazonaws.com")) {
|
||||
return { tls: { ciphers: "TLSv1.2" } };
|
||||
|
||||
@@ -51,7 +51,7 @@ To opt into telemetry, you can set "TELEMETRY_ENABLED=true" within the environme
|
||||
}
|
||||
};
|
||||
|
||||
const sendPostHogEvents = async (event: TPostHogEvent) => {
|
||||
const sendPostHogEvents = (event: TPostHogEvent) => {
|
||||
if (postHog) {
|
||||
postHog.capture({
|
||||
event: event.event,
|
||||
|
||||
@@ -74,10 +74,10 @@ export const webhookDALFactory = (db: TDbClient) => {
|
||||
.where(`${TableName.Environment}.projectId`, projectId)
|
||||
.where((qb) => {
|
||||
if (environment) {
|
||||
qb.where("slug", environment);
|
||||
void qb.where("slug", environment);
|
||||
}
|
||||
if (secretPath) {
|
||||
qb.where("secretPath", secretPath);
|
||||
void qb.where("secretPath", secretPath);
|
||||
}
|
||||
})
|
||||
.join(TableName.Environment, `${TableName.Webhook}.envId`, `${TableName.Environment}.id`)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import crypto from "node:crypto";
|
||||
|
||||
import { AxiosError } from "axios";
|
||||
import picomatch from "picomatch";
|
||||
|
||||
import { SecretKeyEncoding, TWebhooks } from "@app/db/schemas";
|
||||
@@ -111,7 +112,7 @@ export const fnTriggerWebhook = async ({
|
||||
.filter(({ status }) => status === "rejected")
|
||||
.map((data, i) => ({
|
||||
id: toBeTriggeredHooks[i].id,
|
||||
error: data.status === "rejected" && data.reason.message
|
||||
error: data.status === "rejected" ? (data.reason as AxiosError).message : ""
|
||||
}));
|
||||
|
||||
await webhookDAL.transaction(async (tx) => {
|
||||
|
||||
Reference in New Issue
Block a user