mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-09-22 13:39:35 +00:00
feat(folders): added support for snapshot by env and folder
This commit is contained in:
178
backend/src/services/FolderService.ts
Normal file
178
backend/src/services/FolderService.ts
Normal file
@@ -0,0 +1,178 @@
|
||||
import { nanoid } from "nanoid";
|
||||
import { TFolderSchema } from "../models/folder";
|
||||
|
||||
type TAppendFolderDTO = {
|
||||
folderName: string;
|
||||
parentFolderId?: string;
|
||||
};
|
||||
|
||||
type TRenameFolderDTO = {
|
||||
folderName: string;
|
||||
folderId: string;
|
||||
};
|
||||
|
||||
export const validateFolderName = (folderName: string) => {
|
||||
const validNameRegex = /^[a-zA-Z0-9-_]+$/;
|
||||
return validNameRegex.test(folderName);
|
||||
};
|
||||
|
||||
export const generateFolderId = (): string => nanoid(12);
|
||||
|
||||
// simple bfs search
|
||||
export const searchByFolderId = (
|
||||
root: TFolderSchema,
|
||||
folderId: string
|
||||
): TFolderSchema | undefined => {
|
||||
const queue = [root];
|
||||
while (queue.length) {
|
||||
const folder = queue.pop() as TFolderSchema;
|
||||
if (folder.id === folderId) {
|
||||
return folder;
|
||||
}
|
||||
queue.push(...folder.children);
|
||||
}
|
||||
};
|
||||
|
||||
// bfs and then append to the folder
|
||||
const appendChild = (folders: TFolderSchema, folderName: string) => {
|
||||
const folder = folders.children.find(({ name }) => name === folderName);
|
||||
if (folder) {
|
||||
throw new Error("Folder already exists");
|
||||
}
|
||||
const id = generateFolderId();
|
||||
folders.version += 1;
|
||||
folders.children.push({
|
||||
id,
|
||||
name: folderName,
|
||||
children: [],
|
||||
version: 1,
|
||||
});
|
||||
return { id, name: folderName };
|
||||
};
|
||||
|
||||
// root of append child wrapper
|
||||
export const appendFolder = (
|
||||
folders: TFolderSchema,
|
||||
{ folderName, parentFolderId }: TAppendFolderDTO
|
||||
) => {
|
||||
const isRoot = !parentFolderId;
|
||||
|
||||
if (isRoot) {
|
||||
return appendChild(folders, folderName);
|
||||
}
|
||||
const folder = searchByFolderId(folders, parentFolderId);
|
||||
if (!folder) {
|
||||
throw new Error("Parent Folder not found");
|
||||
}
|
||||
return appendChild(folder, folderName);
|
||||
};
|
||||
|
||||
export const renameFolder = (
|
||||
folders: TFolderSchema,
|
||||
{ folderName, folderId }: TRenameFolderDTO
|
||||
) => {
|
||||
const folder = searchByFolderId(folders, folderId);
|
||||
if (!folder) {
|
||||
throw new Error("Folder doesn't exist");
|
||||
}
|
||||
|
||||
folder.name = folderName;
|
||||
};
|
||||
|
||||
// bfs but stops on parent folder
|
||||
// Then unmount the required child and then return both
|
||||
export const deleteFolderById = (folders: TFolderSchema, folderId: string) => {
|
||||
const queue = [folders];
|
||||
while (queue.length) {
|
||||
const folder = queue.pop() as TFolderSchema;
|
||||
folder.version += 1;
|
||||
const index = folder.children.findIndex(({ id }) => folderId === id);
|
||||
if (index !== -1) {
|
||||
const deletedFolder = folder.children.splice(index, 1);
|
||||
return { deletedNode: deletedFolder[0], parent: folder };
|
||||
}
|
||||
queue.push(...folder.children);
|
||||
}
|
||||
};
|
||||
|
||||
// bfs but return parent of the folderID
|
||||
export const getParentFromFolderId = (
|
||||
folders: TFolderSchema,
|
||||
folderId: string
|
||||
) => {
|
||||
const queue = [folders];
|
||||
while (queue.length) {
|
||||
const folder = queue.pop() as TFolderSchema;
|
||||
const index = folder.children.findIndex(({ id }) => folderId === id);
|
||||
if (index !== -1) return folder;
|
||||
|
||||
queue.push(...folder.children);
|
||||
}
|
||||
};
|
||||
|
||||
// to get all folders ids from everything from below nodes
|
||||
export const getAllFolderIds = (folders: TFolderSchema) => {
|
||||
const folderIds: Array<{ id: string; name: string }> = [];
|
||||
const queue = [folders];
|
||||
while (queue.length) {
|
||||
const folder = queue.pop() as TFolderSchema;
|
||||
folderIds.push({ id: folder.id, name: folder.name });
|
||||
queue.push(...folder.children);
|
||||
}
|
||||
return folderIds;
|
||||
};
|
||||
|
||||
// To get the path of a folder from the root. Used for breadcrumbs
|
||||
// LOGIC: We do dfs instead if bfs
|
||||
// Each time we go down we record the current node
|
||||
// We then record the number of childs of each root node
|
||||
// When we reach leaf node or when all childs of a root node are visited
|
||||
// We remove it from path recorded by using the total child record
|
||||
export const searchByFolderIdWithDir = (
|
||||
folders: TFolderSchema,
|
||||
folderId: string
|
||||
) => {
|
||||
const stack = [folders];
|
||||
const dir: Array<{ id: string; name: string }> = [];
|
||||
const hits: Record<string, number> = {};
|
||||
|
||||
while (stack.length) {
|
||||
const folder = stack.shift() as TFolderSchema;
|
||||
// score the hit
|
||||
hits[folder.id] = folder.children.length;
|
||||
const parent = dir[dir.length - 1];
|
||||
if (parent) hits[parent.id] -= 1;
|
||||
|
||||
if (folder.id === folderId) {
|
||||
dir.push({ name: folder.name, id: folder.id });
|
||||
return { folder, dir };
|
||||
}
|
||||
|
||||
if (folder.children.length) {
|
||||
dir.push({ name: folder.name, id: folder.id });
|
||||
stack.unshift(...folder.children);
|
||||
} else {
|
||||
if (!hits[parent.id]) {
|
||||
dir.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
};
|
||||
|
||||
// to get folder of a path given
|
||||
// Like /frontend/folder#1
|
||||
export const getFolderByPath = (folders: TFolderSchema, searchPath: string) => {
|
||||
const path = searchPath.split("/").filter(Boolean);
|
||||
const queue = [folders];
|
||||
let segment: TFolderSchema | undefined;
|
||||
while (queue.length && path.length) {
|
||||
const folder = queue.pop();
|
||||
const segmentPath = path.shift();
|
||||
segment = folder?.children.find(({ name }) => name === segmentPath);
|
||||
if (!segment) return;
|
||||
|
||||
queue.push(segment);
|
||||
}
|
||||
return segment;
|
||||
};
|
||||
Reference in New Issue
Block a user