mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-19 07:33:18 +00:00
858 lines
24 KiB
TypeScript
858 lines
24 KiB
TypeScript
/* eslint-disable no-await-in-loop */
|
|
import { ForbiddenError } from "@casl/ability";
|
|
|
|
import { ActionProjectType } from "@app/db/schemas";
|
|
import { Event, EventType } from "@app/ee/services/audit-log/audit-log-types";
|
|
import { ProjectPermissionCommitsActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
|
import { BadRequestError, NotFoundError } from "@app/lib/errors";
|
|
import { logger } from "@app/lib/logger";
|
|
import { ActorAuthMethod, ActorType } from "@app/services/auth/auth-type";
|
|
import { TFolderCommitDALFactory } from "@app/services/folder-commit/folder-commit-dal";
|
|
import {
|
|
ResourceType,
|
|
TCommitResourceChangeDTO,
|
|
TFolderCommitServiceFactory
|
|
} from "@app/services/folder-commit/folder-commit-service";
|
|
import {
|
|
isFolderCommitChange,
|
|
isSecretCommitChange
|
|
} from "@app/services/folder-commit-changes/folder-commit-changes-dal";
|
|
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
|
import { TProjectEnvDALFactory } from "@app/services/project-env/project-env-dal";
|
|
import { TSecretServiceFactory } from "@app/services/secret/secret-service";
|
|
import { TProcessNewCommitRawDTO } from "@app/services/secret/secret-types";
|
|
import { TSecretFolderDALFactory } from "@app/services/secret-folder/secret-folder-dal";
|
|
import { TSecretFolderServiceFactory } from "@app/services/secret-folder/secret-folder-service";
|
|
import { TSecretV2BridgeServiceFactory } from "@app/services/secret-v2-bridge/secret-v2-bridge-service";
|
|
import { SecretOperations, SecretUpdateMode } from "@app/services/secret-v2-bridge/secret-v2-bridge-types";
|
|
|
|
import { TPermissionServiceFactory } from "../permission/permission-service-types";
|
|
import { TSecretApprovalPolicyServiceFactory } from "../secret-approval-policy/secret-approval-policy-service";
|
|
import { TSecretApprovalRequestServiceFactory } from "../secret-approval-request/secret-approval-request-service";
|
|
|
|
type TPitServiceFactoryDep = {
|
|
folderCommitService: TFolderCommitServiceFactory;
|
|
secretService: Pick<TSecretServiceFactory, "getSecretVersionsV2ByIds" | "getChangeVersions">;
|
|
folderService: Pick<
|
|
TSecretFolderServiceFactory,
|
|
"getFolderById" | "getFolderVersions" | "createManyFolders" | "updateManyFolders" | "deleteManyFolders"
|
|
>;
|
|
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission">;
|
|
folderDAL: Pick<TSecretFolderDALFactory, "findSecretPathByFolderIds" | "findBySecretPath">;
|
|
projectEnvDAL: Pick<TProjectEnvDALFactory, "findOne">;
|
|
secretApprovalRequestService: Pick<
|
|
TSecretApprovalRequestServiceFactory,
|
|
"generateSecretApprovalRequest" | "generateSecretApprovalRequestV2Bridge"
|
|
>;
|
|
secretApprovalPolicyService: Pick<TSecretApprovalPolicyServiceFactory, "getSecretApprovalPolicy">;
|
|
projectDAL: Pick<TProjectDALFactory, "checkProjectUpgradeStatus" | "findProjectBySlug" | "findById">;
|
|
secretV2BridgeService: TSecretV2BridgeServiceFactory;
|
|
folderCommitDAL: Pick<TFolderCommitDALFactory, "transaction">;
|
|
};
|
|
|
|
export type TPitServiceFactory = ReturnType<typeof pitServiceFactory>;
|
|
|
|
export const pitServiceFactory = ({
|
|
folderCommitService,
|
|
secretService,
|
|
folderService,
|
|
permissionService,
|
|
folderDAL,
|
|
projectEnvDAL,
|
|
secretApprovalRequestService,
|
|
secretApprovalPolicyService,
|
|
projectDAL,
|
|
secretV2BridgeService,
|
|
folderCommitDAL
|
|
}: TPitServiceFactoryDep) => {
|
|
const getCommitsCount = async ({
|
|
actor,
|
|
actorId,
|
|
actorOrgId,
|
|
actorAuthMethod,
|
|
projectId,
|
|
environment,
|
|
path
|
|
}: {
|
|
actor: ActorType;
|
|
actorId: string;
|
|
actorOrgId: string;
|
|
actorAuthMethod: ActorAuthMethod;
|
|
projectId: string;
|
|
environment: string;
|
|
path: string;
|
|
}) => {
|
|
const result = await folderCommitService.getCommitsCount({
|
|
actor,
|
|
actorId,
|
|
actorOrgId,
|
|
actorAuthMethod,
|
|
projectId,
|
|
environment,
|
|
path
|
|
});
|
|
|
|
return result;
|
|
};
|
|
|
|
const getCommitsForFolder = async ({
|
|
actor,
|
|
actorId,
|
|
actorOrgId,
|
|
actorAuthMethod,
|
|
projectId,
|
|
environment,
|
|
path,
|
|
offset,
|
|
limit,
|
|
search,
|
|
sort
|
|
}: {
|
|
actor: ActorType;
|
|
actorId: string;
|
|
actorOrgId: string;
|
|
actorAuthMethod: ActorAuthMethod;
|
|
projectId: string;
|
|
environment: string;
|
|
path: string;
|
|
offset: number;
|
|
limit: number;
|
|
search?: string;
|
|
sort: "asc" | "desc";
|
|
}) => {
|
|
const result = await folderCommitService.getCommitsForFolder({
|
|
actor,
|
|
actorId,
|
|
actorOrgId,
|
|
actorAuthMethod,
|
|
projectId,
|
|
environment,
|
|
path,
|
|
offset,
|
|
limit,
|
|
search,
|
|
sort
|
|
});
|
|
|
|
return {
|
|
commits: result.commits.map((commit) => ({
|
|
...commit,
|
|
commitId: commit.commitId.toString()
|
|
})),
|
|
total: result.total,
|
|
hasMore: result.hasMore
|
|
};
|
|
};
|
|
|
|
const getCommitChanges = async ({
|
|
actor,
|
|
actorId,
|
|
actorOrgId,
|
|
actorAuthMethod,
|
|
projectId,
|
|
commitId
|
|
}: {
|
|
actor: ActorType;
|
|
actorId: string;
|
|
actorOrgId: string;
|
|
actorAuthMethod: ActorAuthMethod;
|
|
projectId: string;
|
|
commitId: string;
|
|
}) => {
|
|
const changes = await folderCommitService.getCommitChanges({
|
|
actor,
|
|
actorId,
|
|
actorOrgId,
|
|
actorAuthMethod,
|
|
projectId,
|
|
commitId
|
|
});
|
|
|
|
const [folderWithPath] = await folderDAL.findSecretPathByFolderIds(projectId, [changes.folderId]);
|
|
|
|
for (const change of changes.changes) {
|
|
if (isSecretCommitChange(change)) {
|
|
change.versions = await secretService.getChangeVersions(
|
|
{
|
|
secretVersion: change.secretVersion,
|
|
secretId: change.secretId,
|
|
id: change.id,
|
|
isUpdate: change.isUpdate,
|
|
changeType: change.changeType
|
|
},
|
|
(Number.parseInt(change.secretVersion, 10) - 1).toString(),
|
|
actorId,
|
|
actor,
|
|
actorOrgId,
|
|
actorAuthMethod,
|
|
changes.envId,
|
|
projectId,
|
|
folderWithPath?.path || ""
|
|
);
|
|
} else if (isFolderCommitChange(change)) {
|
|
change.versions = await folderService.getFolderVersions(
|
|
change,
|
|
(Number.parseInt(change.folderVersion, 10) - 1).toString(),
|
|
change.folderChangeId
|
|
);
|
|
}
|
|
}
|
|
|
|
return {
|
|
changes: {
|
|
...changes,
|
|
commitId: changes.commitId.toString()
|
|
}
|
|
};
|
|
};
|
|
|
|
const compareCommitChanges = async ({
|
|
actor,
|
|
actorId,
|
|
actorOrgId,
|
|
actorAuthMethod,
|
|
projectId,
|
|
commitId,
|
|
folderId,
|
|
environment,
|
|
deepRollback,
|
|
secretPath
|
|
}: {
|
|
actor: ActorType;
|
|
actorId: string;
|
|
actorOrgId: string;
|
|
actorAuthMethod: ActorAuthMethod;
|
|
projectId: string;
|
|
commitId: string;
|
|
folderId: string;
|
|
environment: string;
|
|
deepRollback: boolean;
|
|
secretPath: string;
|
|
}) => {
|
|
const latestCommit = await folderCommitService.getLatestCommit({
|
|
folderId,
|
|
actor,
|
|
actorId,
|
|
actorOrgId,
|
|
actorAuthMethod,
|
|
projectId
|
|
});
|
|
|
|
const targetCommit = await folderCommitService.getCommitById({
|
|
commitId,
|
|
actor,
|
|
actorId,
|
|
actorOrgId,
|
|
actorAuthMethod,
|
|
projectId
|
|
});
|
|
|
|
const env = await projectEnvDAL.findOne({
|
|
projectId,
|
|
slug: environment
|
|
});
|
|
|
|
if (!latestCommit) {
|
|
throw new NotFoundError({ message: "Latest commit not found" });
|
|
}
|
|
|
|
let diffs;
|
|
if (deepRollback) {
|
|
diffs = await folderCommitService.deepCompareFolder({
|
|
targetCommitId: targetCommit.id,
|
|
envId: env.id,
|
|
projectId
|
|
});
|
|
} else {
|
|
const folderData = await folderService.getFolderById({
|
|
actor,
|
|
actorId,
|
|
actorOrgId,
|
|
actorAuthMethod,
|
|
id: folderId
|
|
});
|
|
|
|
diffs = [
|
|
{
|
|
folderId: folderData.id,
|
|
folderName: folderData.name,
|
|
folderPath: secretPath,
|
|
changes: await folderCommitService.compareFolderStates({
|
|
targetCommitId: commitId,
|
|
currentCommitId: latestCommit.id
|
|
})
|
|
}
|
|
];
|
|
}
|
|
|
|
for (const diff of diffs) {
|
|
for (const change of diff.changes) {
|
|
// Use discriminated union type checking
|
|
if (change.type === ResourceType.SECRET) {
|
|
// TypeScript now knows this is a SecretChange
|
|
if (change.secretKey && change.secretVersion && change.secretId) {
|
|
change.versions = await secretService.getChangeVersions(
|
|
{
|
|
secretVersion: change.secretVersion,
|
|
secretId: change.secretId,
|
|
id: change.id,
|
|
isUpdate: change.isUpdate,
|
|
changeType: change.changeType
|
|
},
|
|
change.fromVersion || "1",
|
|
actorId,
|
|
actor,
|
|
actorOrgId,
|
|
actorAuthMethod,
|
|
env.id,
|
|
projectId,
|
|
diff.folderPath || ""
|
|
);
|
|
}
|
|
} else if (change.type === ResourceType.FOLDER) {
|
|
// TypeScript now knows this is a FolderChange
|
|
if (change.folderVersion) {
|
|
change.versions = await folderService.getFolderVersions(change, change.fromVersion || "1", change.id);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return diffs;
|
|
};
|
|
|
|
const rollbackToCommit = async ({
|
|
actor,
|
|
actorId,
|
|
actorOrgId,
|
|
actorAuthMethod,
|
|
projectId,
|
|
commitId,
|
|
folderId,
|
|
deepRollback,
|
|
message,
|
|
environment
|
|
}: {
|
|
actor: ActorType;
|
|
actorId: string;
|
|
actorOrgId: string;
|
|
actorAuthMethod: ActorAuthMethod;
|
|
projectId: string;
|
|
commitId: string;
|
|
folderId: string;
|
|
deepRollback: boolean;
|
|
message?: string;
|
|
environment: string;
|
|
}) => {
|
|
const { permission: userPermission } = await permissionService.getProjectPermission({
|
|
actor,
|
|
actorId,
|
|
projectId,
|
|
actorAuthMethod,
|
|
actorOrgId,
|
|
actionProjectType: ActionProjectType.SecretManager
|
|
});
|
|
|
|
ForbiddenError.from(userPermission).throwUnlessCan(
|
|
ProjectPermissionCommitsActions.PerformRollback,
|
|
ProjectPermissionSub.Commits
|
|
);
|
|
|
|
const latestCommit = await folderCommitService.getLatestCommit({
|
|
folderId,
|
|
actor,
|
|
actorId,
|
|
actorOrgId,
|
|
actorAuthMethod,
|
|
projectId
|
|
});
|
|
|
|
if (!latestCommit) {
|
|
throw new NotFoundError({ message: "Latest commit not found" });
|
|
}
|
|
|
|
logger.info(`PIT - Attempting to rollback folder ${folderId} from commit ${latestCommit.id} to commit ${commitId}`);
|
|
|
|
const targetCommit = await folderCommitService.getCommitById({
|
|
commitId,
|
|
actor,
|
|
actorId,
|
|
actorAuthMethod,
|
|
actorOrgId,
|
|
projectId
|
|
});
|
|
|
|
const env = await projectEnvDAL.findOne({
|
|
projectId,
|
|
slug: environment
|
|
});
|
|
|
|
if (!targetCommit || targetCommit.folderId !== folderId || targetCommit.envId !== env.id) {
|
|
throw new NotFoundError({ message: "Target commit not found" });
|
|
}
|
|
|
|
if (!latestCommit || latestCommit.envId !== env.id) {
|
|
throw new NotFoundError({ message: "Latest commit not found" });
|
|
}
|
|
|
|
if (deepRollback) {
|
|
await folderCommitService.deepRollbackFolder(commitId, env.id, actorId, actor, projectId, message);
|
|
return { success: true };
|
|
}
|
|
|
|
const diff = await folderCommitService.compareFolderStates({
|
|
currentCommitId: latestCommit.id,
|
|
targetCommitId: commitId
|
|
});
|
|
|
|
const response = await folderCommitService.applyFolderStateDifferences({
|
|
differences: diff,
|
|
actorInfo: {
|
|
actorType: actor,
|
|
actorId,
|
|
message: message || "Rollback to previous commit"
|
|
},
|
|
folderId,
|
|
projectId,
|
|
reconstructNewFolders: deepRollback
|
|
});
|
|
|
|
return {
|
|
success: true,
|
|
secretChangesCount: response.secretChangesCount,
|
|
folderChangesCount: response.folderChangesCount,
|
|
totalChanges: response.totalChanges
|
|
};
|
|
};
|
|
|
|
const revertCommit = async ({
|
|
actor,
|
|
actorId,
|
|
actorOrgId,
|
|
actorAuthMethod,
|
|
projectId,
|
|
commitId
|
|
}: {
|
|
actor: ActorType;
|
|
actorId: string;
|
|
actorOrgId: string;
|
|
actorAuthMethod: ActorAuthMethod;
|
|
projectId: string;
|
|
commitId: string;
|
|
}) => {
|
|
const response = await folderCommitService.revertCommitChanges({
|
|
commitId,
|
|
actor,
|
|
actorId,
|
|
actorAuthMethod,
|
|
actorOrgId,
|
|
projectId
|
|
});
|
|
|
|
return response;
|
|
};
|
|
|
|
const getFolderStateAtCommit = async ({
|
|
actor,
|
|
actorId,
|
|
actorOrgId,
|
|
actorAuthMethod,
|
|
projectId,
|
|
commitId
|
|
}: {
|
|
actor: ActorType;
|
|
actorId: string;
|
|
actorOrgId: string;
|
|
actorAuthMethod: ActorAuthMethod;
|
|
projectId: string;
|
|
commitId: string;
|
|
}) => {
|
|
const commit = await folderCommitService.getCommitById({
|
|
commitId,
|
|
actor,
|
|
actorId,
|
|
actorOrgId,
|
|
actorAuthMethod,
|
|
projectId
|
|
});
|
|
|
|
if (!commit) {
|
|
throw new NotFoundError({ message: `Commit with ID ${commitId} not found` });
|
|
}
|
|
|
|
const response = await folderCommitService.reconstructFolderState(commitId);
|
|
|
|
return response.map((item) => {
|
|
if (item.type === ResourceType.SECRET) {
|
|
return {
|
|
...item,
|
|
secretVersion: Number(item.secretVersion)
|
|
};
|
|
}
|
|
|
|
if (item.type === ResourceType.FOLDER) {
|
|
return {
|
|
...item,
|
|
folderVersion: Number(item.folderVersion)
|
|
};
|
|
}
|
|
|
|
return item;
|
|
});
|
|
};
|
|
|
|
const processNewCommitRaw = async ({
|
|
actorId,
|
|
projectId,
|
|
environment,
|
|
actor,
|
|
actorOrgId,
|
|
actorAuthMethod,
|
|
secretPath,
|
|
message,
|
|
changes = {
|
|
secrets: {
|
|
create: [],
|
|
update: [],
|
|
delete: []
|
|
},
|
|
folders: {
|
|
create: [],
|
|
update: [],
|
|
delete: []
|
|
}
|
|
}
|
|
}: {
|
|
actorId: string;
|
|
projectId: string;
|
|
environment: string;
|
|
actor: ActorType;
|
|
actorOrgId: string;
|
|
actorAuthMethod: ActorAuthMethod;
|
|
secretPath: string;
|
|
message: string;
|
|
changes: TProcessNewCommitRawDTO;
|
|
}) => {
|
|
const policy =
|
|
actor === ActorType.USER
|
|
? await secretApprovalPolicyService.getSecretApprovalPolicy(projectId, environment, secretPath)
|
|
: undefined;
|
|
const secretMutationEvents: Event[] = [];
|
|
|
|
const project = await projectDAL.findById(projectId);
|
|
if (project.enforceCapitalization) {
|
|
const caseViolatingSecretKeys = [
|
|
// Check create operations
|
|
...(changes.secrets?.create
|
|
?.filter((sec) => sec.secretKey !== sec.secretKey.toUpperCase())
|
|
.map((sec) => sec.secretKey) ?? []),
|
|
|
|
// Check update operations
|
|
...(changes.secrets?.update
|
|
?.filter((sec) => sec.newSecretName && sec.newSecretName !== sec.newSecretName.toUpperCase())
|
|
.map((sec) => sec.secretKey) ?? [])
|
|
];
|
|
|
|
if (caseViolatingSecretKeys.length) {
|
|
throw new BadRequestError({
|
|
message: `Secret names must be in UPPERCASE per project requirements: ${caseViolatingSecretKeys.join(
|
|
", "
|
|
)}. You can disable this requirement in project settings`
|
|
});
|
|
}
|
|
}
|
|
|
|
const response = await folderCommitDAL.transaction(async (trx) => {
|
|
const targetFolder = await folderDAL.findBySecretPath(projectId, environment, secretPath, trx);
|
|
if (!targetFolder)
|
|
throw new NotFoundError({
|
|
message: `Folder with path '${secretPath}' in environment with slug '${environment}' not found`,
|
|
name: "CreateManySecret"
|
|
});
|
|
const commitChanges: TCommitResourceChangeDTO[] = [];
|
|
const folderChanges: { create: string[]; update: string[]; delete: string[] } = {
|
|
create: [],
|
|
update: [],
|
|
delete: []
|
|
};
|
|
|
|
if ((changes.folders?.create?.length ?? 0) > 0) {
|
|
const createdFolders = await folderService.createManyFolders({
|
|
projectId,
|
|
actor,
|
|
actorId,
|
|
actorOrgId,
|
|
actorAuthMethod,
|
|
folders:
|
|
changes.folders?.create?.map((folder) => ({
|
|
name: folder.folderName,
|
|
environment,
|
|
path: secretPath,
|
|
description: folder.description
|
|
})) ?? [],
|
|
tx: trx,
|
|
commitChanges
|
|
});
|
|
const newFolderEvents = createdFolders.folders.map(
|
|
(folder) =>
|
|
({
|
|
type: EventType.CREATE_FOLDER,
|
|
metadata: {
|
|
environment,
|
|
folderId: folder.id,
|
|
folderName: folder.name,
|
|
folderPath: secretPath,
|
|
...(folder.description ? { description: folder.description } : {})
|
|
}
|
|
}) as Event
|
|
);
|
|
secretMutationEvents.push(...newFolderEvents);
|
|
folderChanges.create.push(...createdFolders.folders.map((folder) => folder.id));
|
|
}
|
|
|
|
if ((changes.folders?.update?.length ?? 0) > 0) {
|
|
const updatedFolders = await folderService.updateManyFolders({
|
|
projectId,
|
|
actor,
|
|
actorId,
|
|
actorOrgId,
|
|
actorAuthMethod,
|
|
folders:
|
|
changes.folders?.update?.map((folder) => ({
|
|
environment,
|
|
path: secretPath,
|
|
id: folder.id,
|
|
name: folder.folderName,
|
|
description: folder.description
|
|
})) ?? [],
|
|
tx: trx,
|
|
commitChanges
|
|
});
|
|
|
|
const updatedFolderEvents = updatedFolders.newFolders.map(
|
|
(folder) =>
|
|
({
|
|
type: EventType.UPDATE_FOLDER,
|
|
metadata: {
|
|
environment,
|
|
folderId: folder.id,
|
|
folderPath: secretPath,
|
|
newFolderName: folder.name,
|
|
newFolderDescription: folder.description
|
|
}
|
|
}) as Event
|
|
);
|
|
secretMutationEvents.push(...updatedFolderEvents);
|
|
folderChanges.update.push(...updatedFolders.newFolders.map((folder) => folder.id));
|
|
}
|
|
|
|
if ((changes.folders?.delete?.length ?? 0) > 0) {
|
|
const deletedFolders = await folderService.deleteManyFolders({
|
|
projectId,
|
|
actor,
|
|
actorId,
|
|
actorOrgId,
|
|
actorAuthMethod,
|
|
folders:
|
|
changes.folders?.delete?.map((folder) => ({
|
|
environment,
|
|
path: secretPath,
|
|
idOrName: folder.id
|
|
})) ?? [],
|
|
tx: trx,
|
|
commitChanges
|
|
});
|
|
const deletedFolderEvents = deletedFolders.folders.map(
|
|
(folder) =>
|
|
({
|
|
type: EventType.DELETE_FOLDER,
|
|
metadata: {
|
|
environment,
|
|
folderId: folder.id,
|
|
folderPath: secretPath,
|
|
folderName: folder.name
|
|
}
|
|
}) as Event
|
|
);
|
|
secretMutationEvents.push(...deletedFolderEvents);
|
|
folderChanges.delete.push(...deletedFolders.folders.map((folder) => folder.id));
|
|
}
|
|
|
|
if (policy) {
|
|
if (
|
|
(changes.secrets?.create?.length ?? 0) > 0 ||
|
|
(changes.secrets?.update?.length ?? 0) > 0 ||
|
|
(changes.secrets?.delete?.length ?? 0) > 0
|
|
) {
|
|
const approval = await secretApprovalRequestService.generateSecretApprovalRequestV2Bridge({
|
|
policy,
|
|
secretPath,
|
|
environment,
|
|
projectId,
|
|
actor,
|
|
actorId,
|
|
actorOrgId,
|
|
actorAuthMethod,
|
|
data: {
|
|
[SecretOperations.Create]:
|
|
changes.secrets?.create?.map((el) => ({
|
|
tagIds: el.tagIds,
|
|
secretValue: el.secretValue,
|
|
secretComment: el.secretComment,
|
|
metadata: el.metadata,
|
|
skipMultilineEncoding: el.skipMultilineEncoding,
|
|
secretKey: el.secretKey,
|
|
secretMetadata: el.secretMetadata
|
|
})) ?? [],
|
|
[SecretOperations.Update]:
|
|
changes.secrets?.update?.map((el) => ({
|
|
tagIds: el.tagIds,
|
|
newSecretName: el.newSecretName,
|
|
secretValue: el.secretValue,
|
|
secretComment: el.secretComment,
|
|
metadata: el.metadata,
|
|
skipMultilineEncoding: el.skipMultilineEncoding,
|
|
secretKey: el.secretKey,
|
|
secretMetadata: el.secretMetadata
|
|
})) ?? [],
|
|
[SecretOperations.Delete]:
|
|
changes.secrets?.delete?.map((el) => ({
|
|
secretKey: el.secretKey
|
|
})) ?? []
|
|
}
|
|
});
|
|
return {
|
|
approvalId: approval.id,
|
|
folderChanges,
|
|
secretMutationEvents
|
|
};
|
|
}
|
|
return {
|
|
folderChanges,
|
|
secretMutationEvents
|
|
};
|
|
}
|
|
|
|
if ((changes.secrets?.create?.length ?? 0) > 0) {
|
|
const newSecrets = await secretV2BridgeService.createManySecret({
|
|
secretPath,
|
|
environment,
|
|
projectId,
|
|
actorAuthMethod,
|
|
actorOrgId,
|
|
actor,
|
|
actorId,
|
|
secrets: changes.secrets?.create ?? [],
|
|
tx: trx,
|
|
commitChanges
|
|
});
|
|
secretMutationEvents.push({
|
|
type: EventType.CREATE_SECRETS,
|
|
metadata: {
|
|
environment,
|
|
secretPath,
|
|
secrets: newSecrets.map((secret) => ({
|
|
secretId: secret.id,
|
|
secretKey: secret.secretKey,
|
|
secretVersion: secret.version,
|
|
secretTags: secret.tags?.map((tag) => tag.name)
|
|
}))
|
|
}
|
|
});
|
|
}
|
|
if ((changes.secrets?.update?.length ?? 0) > 0) {
|
|
const updatedSecrets = await secretV2BridgeService.updateManySecret({
|
|
secretPath,
|
|
environment,
|
|
projectId,
|
|
actorAuthMethod,
|
|
actorOrgId,
|
|
actor,
|
|
actorId,
|
|
secrets: changes.secrets?.update ?? [],
|
|
mode: SecretUpdateMode.FailOnNotFound,
|
|
tx: trx,
|
|
commitChanges
|
|
});
|
|
secretMutationEvents.push({
|
|
type: EventType.UPDATE_SECRETS,
|
|
metadata: {
|
|
environment,
|
|
secretPath,
|
|
secrets: updatedSecrets.map((secret) => ({
|
|
secretId: secret.id,
|
|
secretKey: secret.secretKey,
|
|
secretVersion: secret.version,
|
|
secretTags: secret.tags?.map((tag) => tag.name)
|
|
}))
|
|
}
|
|
});
|
|
}
|
|
if ((changes.secrets?.delete?.length ?? 0) > 0) {
|
|
const deletedSecrets = await secretV2BridgeService.deleteManySecret({
|
|
secretPath,
|
|
environment,
|
|
projectId,
|
|
actorAuthMethod,
|
|
actorOrgId,
|
|
actor,
|
|
actorId,
|
|
secrets: changes.secrets?.delete ?? [],
|
|
tx: trx,
|
|
commitChanges
|
|
});
|
|
secretMutationEvents.push({
|
|
type: EventType.DELETE_SECRETS,
|
|
metadata: {
|
|
environment,
|
|
secretPath,
|
|
secrets: deletedSecrets.map((secret) => ({
|
|
secretId: secret.id,
|
|
secretKey: secret.secretKey,
|
|
secretVersion: secret.version
|
|
}))
|
|
}
|
|
});
|
|
}
|
|
if (commitChanges?.length > 0) {
|
|
const commit = await folderCommitService.createCommit(
|
|
{
|
|
actor: {
|
|
type: actor || ActorType.PLATFORM,
|
|
metadata: {
|
|
id: actorId
|
|
}
|
|
},
|
|
message,
|
|
folderId: targetFolder.id,
|
|
changes: commitChanges
|
|
},
|
|
trx
|
|
);
|
|
return {
|
|
folderChanges,
|
|
commitId: commit?.id,
|
|
secretMutationEvents
|
|
};
|
|
}
|
|
return {
|
|
folderChanges,
|
|
secretMutationEvents
|
|
};
|
|
});
|
|
|
|
return response;
|
|
};
|
|
|
|
return {
|
|
getCommitsCount,
|
|
getCommitsForFolder,
|
|
getCommitChanges,
|
|
compareCommitChanges,
|
|
rollbackToCommit,
|
|
revertCommit,
|
|
getFolderStateAtCommit,
|
|
processNewCommitRaw
|
|
};
|
|
};
|