mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-06 19:28:16 +00:00
add folder controller
This commit is contained in:
@@ -0,0 +1,89 @@
|
|||||||
|
import { Request, Response } from 'express';
|
||||||
|
import { Secret } from '../../models';
|
||||||
|
import Folder from '../../models/folder';
|
||||||
|
import { BadRequestError } from '../../utils/errors';
|
||||||
|
import { ROOT_FOLDER_PATH, getFolderPath, getParentPath, normalizePath, validateFolderName } from '../../utils/folder';
|
||||||
|
import { ADMIN, MEMBER } from '../../variables';
|
||||||
|
import { validateMembership } from '../../helpers/membership';
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
// verify workspace id/environment
|
||||||
|
export const createFolder = async (req: Request, res: Response) => {
|
||||||
|
const { workspaceId, environment, folderName, parentFolderId } = req.body
|
||||||
|
if (!validateFolderName(folderName)) {
|
||||||
|
throw BadRequestError({ message: "Folder name cannot contain spaces. Only underscore and dashes" })
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parentFolderId) {
|
||||||
|
const parentFolder = await Folder.find({ environment: environment, workspace: workspaceId, id: parentFolderId });
|
||||||
|
if (!parentFolder) {
|
||||||
|
throw BadRequestError({ message: "The parent folder doesn't exist" })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let completePath = await getFolderPath(parentFolderId)
|
||||||
|
if (completePath == ROOT_FOLDER_PATH) {
|
||||||
|
completePath = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentFolderPath = completePath + "/" + folderName // construct new path with current folder to be created
|
||||||
|
const normalizedCurrentPath = normalizePath(currentFolderPath)
|
||||||
|
const normalizedParentPath = getParentPath(normalizedCurrentPath)
|
||||||
|
|
||||||
|
const existingFolder = await Folder.findOne({
|
||||||
|
name: folderName,
|
||||||
|
workspace: workspaceId,
|
||||||
|
environment: environment,
|
||||||
|
parent: parentFolderId,
|
||||||
|
path: normalizedCurrentPath
|
||||||
|
});
|
||||||
|
|
||||||
|
if (existingFolder) {
|
||||||
|
return res.json(existingFolder)
|
||||||
|
}
|
||||||
|
|
||||||
|
const newFolder = new Folder({
|
||||||
|
name: folderName,
|
||||||
|
workspace: workspaceId,
|
||||||
|
environment: environment,
|
||||||
|
parent: parentFolderId,
|
||||||
|
path: normalizedCurrentPath,
|
||||||
|
parentPath: normalizedParentPath
|
||||||
|
});
|
||||||
|
|
||||||
|
await newFolder.save();
|
||||||
|
|
||||||
|
return res.json(newFolder)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const deleteFolder = async (req: Request, res: Response) => {
|
||||||
|
const { folderId } = req.params
|
||||||
|
const queue: any[] = [folderId];
|
||||||
|
|
||||||
|
const folder = await Folder.findById(folderId);
|
||||||
|
if (!folder) {
|
||||||
|
throw BadRequestError({ message: "The folder doesn't exist" })
|
||||||
|
}
|
||||||
|
|
||||||
|
// check that user is a member of the workspace
|
||||||
|
await validateMembership({
|
||||||
|
userId: req.user._id.toString(),
|
||||||
|
workspaceId: folder.workspace as any,
|
||||||
|
acceptedRoles: [ADMIN, MEMBER]
|
||||||
|
});
|
||||||
|
|
||||||
|
while (queue.length > 0) {
|
||||||
|
const currentFolderId = queue.shift();
|
||||||
|
|
||||||
|
const childFolders = await Folder.find({ parent: currentFolderId });
|
||||||
|
for (const childFolder of childFolders) {
|
||||||
|
queue.push(childFolder._id);
|
||||||
|
}
|
||||||
|
|
||||||
|
await Secret.deleteMany({ folder: currentFolderId });
|
||||||
|
|
||||||
|
await Folder.deleteOne({ _id: currentFolderId });
|
||||||
|
}
|
||||||
|
|
||||||
|
res.send()
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
import { Schema, Types, model } from 'mongoose';
|
||||||
|
|
||||||
|
const folderSchema = new Schema({
|
||||||
|
name: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
workspace: {
|
||||||
|
type: Schema.Types.ObjectId,
|
||||||
|
ref: 'Workspace',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
environment: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
parent: {
|
||||||
|
type: Schema.Types.ObjectId,
|
||||||
|
ref: 'Folder',
|
||||||
|
required: false, // optional for root folders
|
||||||
|
},
|
||||||
|
path: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
parentPath: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
timestamps: true
|
||||||
|
});
|
||||||
|
|
||||||
|
const Folder = model('Folder', folderSchema);
|
||||||
|
|
||||||
|
export default Folder;
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
import Folder from "../models/folder";
|
||||||
|
|
||||||
|
export const ROOT_FOLDER_PATH = "/"
|
||||||
|
|
||||||
|
export const getFolderPath = async (folderId: string) => {
|
||||||
|
let currentFolder = await Folder.findById(folderId);
|
||||||
|
const pathSegments = [];
|
||||||
|
|
||||||
|
while (currentFolder) {
|
||||||
|
pathSegments.unshift(currentFolder.name);
|
||||||
|
currentFolder = currentFolder.parent ? await Folder.findById(currentFolder.parent) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return '/' + pathSegments.join('/');
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
Returns the folder ID associated with the specified secret path in the given workspace and environment.
|
||||||
|
@param workspaceId - The ID of the workspace to search in.
|
||||||
|
@param environment - The environment to search in.
|
||||||
|
@param secretPath - The secret path to search for.
|
||||||
|
@returns The folder ID associated with the specified secret path, or undefined if the path is at the root folder level.
|
||||||
|
@throws Error if the specified secret path is not found.
|
||||||
|
*/
|
||||||
|
export const getFolderIdFromPath = async (workspaceId: string, environment: string, secretPath: string) => {
|
||||||
|
const secretPathParts = secretPath.split("/").filter(path => path != "")
|
||||||
|
if (secretPathParts.length <= 1) {
|
||||||
|
return undefined // root folder, so no folder id
|
||||||
|
}
|
||||||
|
|
||||||
|
const folderId = await Folder.find({ path: secretPath, workspace: workspaceId, environment: environment })
|
||||||
|
if (!folderId) {
|
||||||
|
throw Error("Secret path not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
return folderId
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cleans up a path by removing empty parts, duplicate slashes,
|
||||||
|
* and ensuring it starts with ROOT_FOLDER_PATH.
|
||||||
|
* @param path - The input path to clean up.
|
||||||
|
* @returns The cleaned-up path string.
|
||||||
|
*/
|
||||||
|
export const normalizePath = (path: string) => {
|
||||||
|
if (path == undefined || path == "" || path == ROOT_FOLDER_PATH) {
|
||||||
|
return ROOT_FOLDER_PATH
|
||||||
|
}
|
||||||
|
|
||||||
|
const pathParts = path.split("/").filter(part => part != "")
|
||||||
|
const cleanPathString = ROOT_FOLDER_PATH + pathParts.join("/")
|
||||||
|
|
||||||
|
return cleanPathString
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getFoldersInDirectory = async (workspaceId: string, environment: string, pathString: string) => {
|
||||||
|
const normalizedPath = normalizePath(pathString)
|
||||||
|
const foldersInDirectory = await Folder.find({
|
||||||
|
workspace: workspaceId,
|
||||||
|
environment: environment,
|
||||||
|
parentPath: normalizedPath,
|
||||||
|
});
|
||||||
|
|
||||||
|
return foldersInDirectory;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the parent path of the given path.
|
||||||
|
* @param path - The input path.
|
||||||
|
* @returns The parent path string.
|
||||||
|
*/
|
||||||
|
export const getParentPath = (path: string) => {
|
||||||
|
const normalizedPath = normalizePath(path);
|
||||||
|
const folderParts = normalizedPath.split('/').filter(part => part !== '');
|
||||||
|
|
||||||
|
let folderParent = ROOT_FOLDER_PATH;
|
||||||
|
if (folderParts.length > 1) {
|
||||||
|
folderParent = ROOT_FOLDER_PATH + folderParts.slice(0, folderParts.length - 1).join('/');
|
||||||
|
}
|
||||||
|
|
||||||
|
return folderParent;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const validateFolderName = (folderName: string) => {
|
||||||
|
const validNameRegex = /^[a-zA-Z0-9-_]+$/;
|
||||||
|
return validNameRegex.test(folderName);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user