feat: fixed eslint and resolved all eslint issues

This commit is contained in:
Akhil Mohan
2024-01-30 22:30:42 +05:30
parent 7c4dd5000f
commit 5855cc660a
147 changed files with 1165 additions and 938 deletions
@@ -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) => {
+5
View File
@@ -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);
+10 -8
View File
@@ -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 }))
);
}
+1 -1
View File
@@ -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;
};
+3 -3
View File
@@ -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
+1 -1
View File
@@ -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 || "";
+2 -2
View File
@@ -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,
+2 -2
View File
@@ -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`)
+2 -1
View File
@@ -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) => {