feat: changed backend-pg to backend

This commit is contained in:
Akhil Mohan
2024-01-27 19:14:33 +05:30
parent d13eafcef7
commit 66d258f02b
792 changed files with 40298 additions and 40607 deletions
@@ -0,0 +1,83 @@
import { Knex } from "knex";
import { TDbClient } from "@app/db";
import { TableName,TSecretImports } from "@app/db/schemas";
import { DatabaseError } from "@app/lib/errors";
import { ormify } from "@app/lib/knex";
export type TSecretImportDALFactory = ReturnType<typeof secretImportDALFactory>;
export const secretImportDALFactory = (db: TDbClient) => {
const secretImportOrm = ormify(db, TableName.SecretImport);
// we are using postion based sorting as its a small list
// this will return the last value of the position in a folder with secret imports
const findLastImportPosition = async (folderId: string, tx?: Knex) => {
const lastPos = await (tx || db)(TableName.SecretImport)
.where({ folderId })
.max({ position: "position" })
.first();
return lastPos?.position || 0;
};
const updateAllPosition = async (folderId: string, pos: number, targetPos: number, tx?: Knex) => {
try {
if (targetPos === -1) {
// this means delete
await (tx || db)(TableName.SecretImport)
.where({ folderId })
.andWhere("position", ">", pos)
.decrement("position", 1);
return;
}
if (targetPos > pos) {
await (tx || db)(TableName.SecretImport)
.where({ folderId })
.where("position", "<=", targetPos)
.andWhere("position", ">", pos)
.decrement("position", 1);
} else {
await (tx || db)(TableName.SecretImport)
.where({ folderId })
.where("position", ">=", targetPos)
.andWhere("position", "<", pos)
.increment("position", 1);
}
} catch (error) {
throw new DatabaseError({ error, name: "Update position" });
}
};
const find = async (filter: Partial<TSecretImports>, tx?: Knex) => {
try {
const docs = await (tx || db)(TableName.SecretImport)
.where(filter)
.join(
TableName.Environment,
`${TableName.SecretImport}.importEnv`,
`${TableName.Environment}.id`
)
.select(
db.ref("*").withSchema(TableName.SecretImport) as unknown as keyof TSecretImports,
db.ref("slug").withSchema(TableName.Environment),
db.ref("name").withSchema(TableName.Environment),
db.ref("id").withSchema(TableName.Environment).as("envId")
)
.orderBy("position", "asc");
return docs.map(({ envId, slug, name, ...el }) => ({
...el,
importEnv: { id: envId, slug, name }
}));
} catch (error) {
throw new DatabaseError({ error, name: "Find secret imports" });
}
};
return {
...secretImportOrm,
find,
findLastImportPosition,
updateAllPosition
};
};
@@ -0,0 +1,43 @@
import { SecretType, TSecretImports } from "@app/db/schemas";
import { groupBy } from "@app/lib/fn";
import { TSecretDALFactory } from "../secret/secret-dal";
import { TSecretFolderDALFactory } from "../secret-folder/secret-folder-dal";
export const fnSecretsFromImports = async ({
allowedImports,
folderDAL,
secretDAL
}: {
allowedImports: (Omit<TSecretImports, "importEnv"> & {
importEnv: { id: string; slug: string; name: string };
})[];
folderDAL: Pick<TSecretFolderDALFactory, "findByManySecretPath">;
secretDAL: Pick<TSecretDALFactory, "find">;
}) => {
const importedFolders = await folderDAL.findByManySecretPath(
allowedImports.map(({ importEnv, importPath }) => ({
envId: importEnv.id,
secretPath: importPath
}))
);
const folderIds = importedFolders.map((el) => el?.id).filter(Boolean) as string[];
if (!folderIds.length) {
return [];
}
const importedSecrets = await secretDAL.find({
$in: { folderId: folderIds },
type: SecretType.Shared
});
const importedSecsGroupByFolderId = groupBy(importedSecrets, (i) => i.folderId);
return allowedImports.map(({ importPath, importEnv }, i) => ({
secretPath: importPath,
environment: importEnv.slug,
environmentInfo: importEnv,
folderId: importedFolders?.[i]?.id,
secrets: importedFolders?.[i]?.id
? importedSecsGroupByFolderId[importedFolders?.[i]?.id as string]
: []
}));
};
@@ -0,0 +1,227 @@
import { ForbiddenError, subject } from "@casl/ability";
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
import {
ProjectPermissionActions,
ProjectPermissionSub
} from "@app/ee/services/permission/project-permission";
import { BadRequestError } from "@app/lib/errors";
import { TProjectEnvDALFactory } from "../project-env/project-env-dal";
import { TSecretDALFactory } from "../secret/secret-dal";
import { TSecretFolderDALFactory } from "../secret-folder/secret-folder-dal";
import { TSecretImportDALFactory } from "./secret-import-dal";
import { fnSecretsFromImports } from "./secret-import-fns";
import {
TCreateSecretImportDTO,
TDeleteSecretImportDTO,
TGetSecretImportsDTO,
TGetSecretsFromImportDTO,
TUpdateSecretImportDTO
} from "./secret-import-types";
type TSecretImportServiceFactoryDep = {
secretImportDAL: TSecretImportDALFactory;
folderDAL: TSecretFolderDALFactory;
secretDAL: Pick<TSecretDALFactory, "find">;
projectEnvDAL: TProjectEnvDALFactory;
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission">;
};
const ERR_SEC_IMP_NOT_FOUND = new BadRequestError({ message: "Secret import not found" });
export type TSecretImportServiceFactory = ReturnType<typeof secretImportServiceFactory>;
export const secretImportServiceFactory = ({
secretImportDAL,
projectEnvDAL,
permissionService,
folderDAL,
secretDAL
}: TSecretImportServiceFactoryDep) => {
const createImport = async ({
environment,
data,
actor,
actorId,
projectId,
path
}: TCreateSecretImportDTO) => {
const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId);
// check if user has permission to import into destination path
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionActions.Create,
subject(ProjectPermissionSub.Secrets, { environment, secretPath: path })
);
// check if user has permission to import from target path
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionActions.Create,
subject(ProjectPermissionSub.Secrets, {
environment: data.environment,
secretPath: data.path
})
);
const folder = await folderDAL.findBySecretPath(projectId, environment, path);
if (!folder) throw new BadRequestError({ message: "Folder not found", name: "Create import" });
// TODO(akhilmhdh-pg): updated permission check add here
const [importEnv] = await projectEnvDAL.findBySlugs(projectId, [data.environment]);
if (!importEnv)
throw new BadRequestError({ error: "Imported env not found", name: "Create import" });
const secImport = await secretImportDAL.transaction(async (tx) => {
const lastPos = await secretImportDAL.findLastImportPosition(folder.id, tx);
return secretImportDAL.create(
{
folderId: folder.id,
position: lastPos + 1,
importEnv: importEnv.id,
importPath: data.path
},
tx
);
});
return { ...secImport, importEnv };
};
const updateImport = async ({
path,
environment,
projectId,
actor,
actorId,
data,
id
}: TUpdateSecretImportDTO) => {
const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId);
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionActions.Edit,
subject(ProjectPermissionSub.Secrets, { environment, secretPath: path })
);
const folder = await folderDAL.findBySecretPath(projectId, environment, path);
if (!folder) throw new BadRequestError({ message: "Folder not found", name: "Update import" });
const secImpDoc = await secretImportDAL.findOne({ folderId: folder.id, id });
if (!secImpDoc) throw ERR_SEC_IMP_NOT_FOUND;
const importedEnv = data.environment // this is get env information of new one or old one
? (await projectEnvDAL.findBySlugs(projectId, [data.environment]))?.[0]
: await projectEnvDAL.findById(secImpDoc.importEnv);
if (!importedEnv)
throw new BadRequestError({ error: "Imported env not found", name: "Create import" });
const updatedSecImport = await secretImportDAL.transaction(async (tx) => {
const secImp = await secretImportDAL.findOne({ folderId: folder.id, id });
if (!secImp) throw ERR_SEC_IMP_NOT_FOUND;
if (data.position) {
await secretImportDAL.updateAllPosition(folder.id, secImp.position, data.position, tx);
}
const [doc] = await secretImportDAL.update(
{ id, folderId: folder.id },
{
position: data?.position,
importEnv: data?.environment ? importedEnv.id : undefined,
importPath: data?.path
},
tx
);
return doc;
});
return { ...updatedSecImport, importEnv: importedEnv };
};
const deleteImport = async ({
path,
environment,
projectId,
actor,
actorId,
id
}: TDeleteSecretImportDTO) => {
const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId);
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionActions.Delete,
subject(ProjectPermissionSub.Secrets, { environment, secretPath: path })
);
const folder = await folderDAL.findBySecretPath(projectId, environment, path);
if (!folder) throw new BadRequestError({ message: "Folder not found", name: "Delete import" });
const secImport = await secretImportDAL.transaction(async (tx) => {
const [doc] = await secretImportDAL.delete({ folderId: folder.id, id }, tx);
if (!doc)
throw new BadRequestError({ name: "Sec imp del", message: "Secret import doc not found" });
await secretImportDAL.updateAllPosition(folder.id, doc.position, -1, tx);
const importEnv = await projectEnvDAL.findById(doc.importEnv);
if (!importEnv)
throw new BadRequestError({ error: "Imported env not found", name: "Create import" });
return { ...doc, importEnv };
});
return secImport;
};
const getImports = async ({
path,
environment,
projectId,
actor,
actorId
}: TGetSecretImportsDTO) => {
const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId);
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionActions.Read,
subject(ProjectPermissionSub.Secrets, { environment, secretPath: path })
);
const folder = await folderDAL.findBySecretPath(projectId, environment, path);
if (!folder) throw new BadRequestError({ message: "Folder not found", name: "Get imports" });
const secImports = await secretImportDAL.find({ folderId: folder.id });
return secImports;
};
const getSecretsFromImports = async ({
path,
environment,
projectId,
actor,
actorId
}: TGetSecretsFromImportDTO) => {
const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId);
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionActions.Read,
subject(ProjectPermissionSub.Secrets, { environment, secretPath: path })
);
const folder = await folderDAL.findBySecretPath(projectId, environment, path);
if (!folder) return [];
// this will already order by position
// so anything based on this order will also be in right position
const secretImports = await secretImportDAL.find({ folderId: folder.id });
const allowedImports = secretImports.filter(({ importEnv, importPath }) =>
permission.can(
ProjectPermissionActions.Read,
subject(ProjectPermissionSub.Secrets, {
environment: importEnv.slug,
secretPath: importPath
})
)
);
return fnSecretsFromImports({ allowedImports, folderDAL, secretDAL });
};
return {
createImport,
updateImport,
deleteImport,
getImports,
getSecretsFromImports,
fnSecretsFromImports
};
};
@@ -0,0 +1,33 @@
import { TProjectPermission } from "@app/lib/types";
export type TCreateSecretImportDTO = {
environment: string;
path: string;
data: {
environment: string;
path: string;
};
} & TProjectPermission;
export type TUpdateSecretImportDTO = {
environment: string;
path: string;
id: string;
data: Partial<{ environment: string; path: string; position: number }>;
} & TProjectPermission;
export type TDeleteSecretImportDTO = {
environment: string;
path: string;
id: string;
} & TProjectPermission;
export type TGetSecretImportsDTO = {
environment: string;
path: string;
} & TProjectPermission;
export type TGetSecretsFromImportDTO = {
environment: string;
path: string;
} & TProjectPermission;