mirror of
https://github.com/Infisical/infisical.git
synced 2025-03-25 14:05:03 +00:00
add ability to import secrets with comments
This commit is contained in:
@ -3,10 +3,11 @@ import Image from "next/image";
|
||||
import { useTranslation } from "next-i18next";
|
||||
import { faUpload } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { parseDocument, Scalar, YAMLMap } from 'yaml';
|
||||
|
||||
import Button from "../basic/buttons/Button";
|
||||
import Error from "../basic/Error";
|
||||
import parse from "../utilities/file";
|
||||
import { parseDotEnv } from '../utilities/parseDotEnv';
|
||||
import guidGenerator from "../utilities/randomId";
|
||||
|
||||
interface DropZoneProps {
|
||||
@ -51,6 +52,53 @@ const DropZone = ({
|
||||
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
const getSecrets = (file: ArrayBuffer, fileType: string) => {
|
||||
let secrets;
|
||||
switch (fileType) {
|
||||
case 'env': {
|
||||
const keyPairs = parseDotEnv(file);
|
||||
secrets = Object.keys(keyPairs).map((key, index) => {
|
||||
return {
|
||||
id: guidGenerator(),
|
||||
pos: numCurrentRows + index,
|
||||
key: key,
|
||||
value: keyPairs[key as keyof typeof keyPairs].value,
|
||||
comment: keyPairs[key as keyof typeof keyPairs].comments.join('\n'),
|
||||
type: 'shared',
|
||||
};
|
||||
});
|
||||
break;
|
||||
}
|
||||
case 'yml': {
|
||||
const parsedFile = parseDocument(file.toString());
|
||||
const keyPairs = parsedFile.contents!.toJSON();
|
||||
|
||||
secrets = Object.keys(keyPairs).map((key, index) => {
|
||||
const fileContent = parsedFile.contents as YAMLMap<Scalar, Scalar>;
|
||||
const comment =
|
||||
fileContent!.items
|
||||
.find((item) => item.key.value === key)
|
||||
?.key?.commentBefore?.split('\n')
|
||||
.map((comment) => comment.trim())
|
||||
.join('\n') ?? '';
|
||||
return {
|
||||
id: guidGenerator(),
|
||||
pos: numCurrentRows + index,
|
||||
key: key,
|
||||
value: keyPairs[key as keyof typeof keyPairs]?.toString() ?? '',
|
||||
comment,
|
||||
type: 'shared',
|
||||
};
|
||||
});
|
||||
break;
|
||||
}
|
||||
default:
|
||||
secrets = '';
|
||||
break;
|
||||
}
|
||||
return secrets;
|
||||
};
|
||||
|
||||
// This function function immediately parses the file after it is dropped
|
||||
const handleDrop = async (e: DragEvent) => {
|
||||
setLoading(true);
|
||||
@ -61,20 +109,12 @@ const DropZone = ({
|
||||
|
||||
const file = e.dataTransfer.files[0];
|
||||
const reader = new FileReader();
|
||||
const fileType = file.name.split('.')[1];
|
||||
|
||||
reader.onload = (event) => {
|
||||
if (event.target === null || event.target.result === null) return;
|
||||
// parse function's argument looks like to be ArrayBuffer
|
||||
const keyPairs = parse(event.target.result as Buffer);
|
||||
const newData = Object.keys(keyPairs).map((key, index) => {
|
||||
return {
|
||||
id: guidGenerator(),
|
||||
pos: numCurrentRows + index,
|
||||
key: key,
|
||||
value: keyPairs[key as keyof typeof keyPairs],
|
||||
type: "shared",
|
||||
};
|
||||
});
|
||||
const newData = getSecrets(event.target.result as ArrayBuffer, fileType);
|
||||
setData(newData);
|
||||
setButtonReady(true);
|
||||
};
|
||||
@ -95,25 +135,14 @@ const DropZone = ({
|
||||
setTimeout(() => setLoading(false), 5000);
|
||||
if (e.currentTarget.files === null) return;
|
||||
const file = e.currentTarget.files[0];
|
||||
const fileType = file.name.split('.')[1];
|
||||
const reader = new FileReader();
|
||||
reader.onload = (event) => {
|
||||
if (event.target === null || event.target.result === null) return;
|
||||
const { result } = event.target;
|
||||
if (typeof result === "string") {
|
||||
const newData = result
|
||||
.split("\n")
|
||||
.map((line: string, index: number) => {
|
||||
return {
|
||||
id: guidGenerator(),
|
||||
pos: numCurrentRows + index,
|
||||
key: line.split("=")[0],
|
||||
value: line.split("=").slice(1, line.split("=").length).join("="),
|
||||
type: "shared",
|
||||
};
|
||||
});
|
||||
setData(newData);
|
||||
setButtonReady(true);
|
||||
}
|
||||
const newData = getSecrets(result as ArrayBuffer, fileType);
|
||||
setData(newData);
|
||||
setButtonReady(true);
|
||||
};
|
||||
reader.readAsText(file);
|
||||
};
|
||||
@ -139,7 +168,7 @@ const DropZone = ({
|
||||
id="fileSelect"
|
||||
type="file"
|
||||
className="opacity-0 absolute w-full h-full"
|
||||
accept=".txt,.env"
|
||||
accept=".txt,.env,.yml"
|
||||
onChange={handleFileSelect}
|
||||
/>
|
||||
{errorDragAndDrop ? (
|
||||
@ -176,7 +205,7 @@ const DropZone = ({
|
||||
id="fileSelect"
|
||||
type="file"
|
||||
className="opacity-0 absolute w-full h-full"
|
||||
accept=".txt,.env"
|
||||
accept=".txt,.env,.yml"
|
||||
onChange={handleFileSelect}
|
||||
/>
|
||||
<div className="flex flex-row w-full items-center justify-center mb-6 mt-5">
|
||||
|
@ -1,47 +0,0 @@
|
||||
const LINE =
|
||||
/(?:^|^)\s*(?:export\s+)?([\w.-]+)(?:\s*=\s*?|:\s+?)(\s*'(?:\\'|[^'])*'|\s*"(?:\\"|[^"])*"|\s*`(?:\\`|[^`])*`|[^#\r\n]+)?\s*(?:#.*)?(?:$|$)/gm;
|
||||
|
||||
/**
|
||||
* Return text that is the buffer parsed
|
||||
* @param {Buffer} src - source buffer
|
||||
* @returns {String} text - text of buffer
|
||||
*/
|
||||
function parse(src: Buffer) {
|
||||
const obj: Record<string, string> = {};
|
||||
|
||||
// Convert buffer to string
|
||||
let lines = src.toString();
|
||||
|
||||
// Convert line breaks to same format
|
||||
lines = lines.replace(/\r\n?/gm, '\n');
|
||||
|
||||
let match;
|
||||
while ((match = LINE.exec(lines)) != null) {
|
||||
const key = match[1];
|
||||
|
||||
// Default undefined or null to empty string
|
||||
let value = match[2] || '';
|
||||
|
||||
// Remove whitespace
|
||||
value = value.trim();
|
||||
|
||||
// Check if double quoted
|
||||
const maybeQuote = value[0];
|
||||
|
||||
// Remove surrounding quotes
|
||||
value = value.replace(/^(['"`])([\s\S]*)\1$/gm, '$2');
|
||||
|
||||
// Expand newlines if double quoted
|
||||
if (maybeQuote === '"') {
|
||||
value = value.replace(/\\n/g, '\n');
|
||||
value = value.replace(/\\r/g, '\r');
|
||||
}
|
||||
|
||||
// Add to object
|
||||
obj[key] = value;
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
export default parse;
|
66
frontend/components/utilities/parseDotEnv.ts
Normal file
66
frontend/components/utilities/parseDotEnv.ts
Normal file
@ -0,0 +1,66 @@
|
||||
const LINE =
|
||||
/(?:^|^)\s*(?:export\s+)?([\w.-]+)(?:\s*=\s*?|:\s+?)(\s*'(?:\\'|[^'])*'|\s*"(?:\\"|[^"])*"|\s*`(?:\\`|[^`])*`|[^#\r\n]+)?\s*(?:#.*)?(?:$|$)/gm;
|
||||
|
||||
/**
|
||||
* Return text that is the buffer parsed
|
||||
* @param {ArrayBuffer} src - source buffer
|
||||
* @returns {String} text - text of buffer
|
||||
*/
|
||||
export function parseDotEnv(src: ArrayBuffer) {
|
||||
const object: {
|
||||
[key: string]: { value: string; comments: string[] };
|
||||
} = {};
|
||||
|
||||
// Convert buffer to string
|
||||
let lines = src.toString();
|
||||
|
||||
// Convert line breaks to same format
|
||||
lines = lines.replace(/\r\n?/gm, '\n');
|
||||
|
||||
let comments: string[] = [];
|
||||
|
||||
lines
|
||||
.split('\n')
|
||||
.map((line) => {
|
||||
// collect comments of each env variable
|
||||
if (line.startsWith('#')) {
|
||||
comments.push(line.replace('#', '').trim());
|
||||
} else if (line) {
|
||||
let match;
|
||||
let item: [string, string, string[]] | [] = [];
|
||||
|
||||
while ((match = LINE.exec(line)) !== null) {
|
||||
const key = match[1];
|
||||
|
||||
// Default undefined or null to empty string
|
||||
let value = match[2] || '';
|
||||
|
||||
// Remove whitespace
|
||||
value = value.trim();
|
||||
|
||||
// Check if double quoted
|
||||
const maybeQuote = value[0];
|
||||
|
||||
// Remove surrounding quotes
|
||||
value = value.replace(/^(['"`])([\s\S]*)\1$/gm, '$2');
|
||||
|
||||
// Expand newlines if double quoted
|
||||
if (maybeQuote === '"') {
|
||||
value = value.replace(/\\n/g, '\n');
|
||||
value = value.replace(/\\r/g, '\r');
|
||||
}
|
||||
item = [key, value, comments];
|
||||
}
|
||||
comments = [];
|
||||
return item;
|
||||
}
|
||||
return [];
|
||||
})
|
||||
.filter((line) => line.length > 1)
|
||||
.forEach((line) => {
|
||||
const [key, value, comments] = line;
|
||||
object[key as string] = { value, comments };
|
||||
});
|
||||
|
||||
return object;
|
||||
}
|
Reference in New Issue
Block a user