mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-07 04:27:29 +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 { useTranslation } from "next-i18next";
|
||||||
import { faUpload } from "@fortawesome/free-solid-svg-icons";
|
import { faUpload } from "@fortawesome/free-solid-svg-icons";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
|
import { parseDocument, Scalar, YAMLMap } from 'yaml';
|
||||||
|
|
||||||
import Button from "../basic/buttons/Button";
|
import Button from "../basic/buttons/Button";
|
||||||
import Error from "../basic/Error";
|
import Error from "../basic/Error";
|
||||||
import parse from "../utilities/file";
|
import { parseDotEnv } from '../utilities/parseDotEnv';
|
||||||
import guidGenerator from "../utilities/randomId";
|
import guidGenerator from "../utilities/randomId";
|
||||||
|
|
||||||
interface DropZoneProps {
|
interface DropZoneProps {
|
||||||
@@ -51,6 +52,53 @@ const DropZone = ({
|
|||||||
|
|
||||||
const [loading, setLoading] = useState(false);
|
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
|
// This function function immediately parses the file after it is dropped
|
||||||
const handleDrop = async (e: DragEvent) => {
|
const handleDrop = async (e: DragEvent) => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
@@ -61,20 +109,12 @@ const DropZone = ({
|
|||||||
|
|
||||||
const file = e.dataTransfer.files[0];
|
const file = e.dataTransfer.files[0];
|
||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
|
const fileType = file.name.split('.')[1];
|
||||||
|
|
||||||
reader.onload = (event) => {
|
reader.onload = (event) => {
|
||||||
if (event.target === null || event.target.result === null) return;
|
if (event.target === null || event.target.result === null) return;
|
||||||
// parse function's argument looks like to be ArrayBuffer
|
// parse function's argument looks like to be ArrayBuffer
|
||||||
const keyPairs = parse(event.target.result as Buffer);
|
const newData = getSecrets(event.target.result as ArrayBuffer, fileType);
|
||||||
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",
|
|
||||||
};
|
|
||||||
});
|
|
||||||
setData(newData);
|
setData(newData);
|
||||||
setButtonReady(true);
|
setButtonReady(true);
|
||||||
};
|
};
|
||||||
@@ -95,25 +135,14 @@ const DropZone = ({
|
|||||||
setTimeout(() => setLoading(false), 5000);
|
setTimeout(() => setLoading(false), 5000);
|
||||||
if (e.currentTarget.files === null) return;
|
if (e.currentTarget.files === null) return;
|
||||||
const file = e.currentTarget.files[0];
|
const file = e.currentTarget.files[0];
|
||||||
|
const fileType = file.name.split('.')[1];
|
||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
reader.onload = (event) => {
|
reader.onload = (event) => {
|
||||||
if (event.target === null || event.target.result === null) return;
|
if (event.target === null || event.target.result === null) return;
|
||||||
const { result } = event.target;
|
const { result } = event.target;
|
||||||
if (typeof result === "string") {
|
const newData = getSecrets(result as ArrayBuffer, fileType);
|
||||||
const newData = result
|
setData(newData);
|
||||||
.split("\n")
|
setButtonReady(true);
|
||||||
.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);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
reader.readAsText(file);
|
reader.readAsText(file);
|
||||||
};
|
};
|
||||||
@@ -139,7 +168,7 @@ const DropZone = ({
|
|||||||
id="fileSelect"
|
id="fileSelect"
|
||||||
type="file"
|
type="file"
|
||||||
className="opacity-0 absolute w-full h-full"
|
className="opacity-0 absolute w-full h-full"
|
||||||
accept=".txt,.env"
|
accept=".txt,.env,.yml"
|
||||||
onChange={handleFileSelect}
|
onChange={handleFileSelect}
|
||||||
/>
|
/>
|
||||||
{errorDragAndDrop ? (
|
{errorDragAndDrop ? (
|
||||||
@@ -176,7 +205,7 @@ const DropZone = ({
|
|||||||
id="fileSelect"
|
id="fileSelect"
|
||||||
type="file"
|
type="file"
|
||||||
className="opacity-0 absolute w-full h-full"
|
className="opacity-0 absolute w-full h-full"
|
||||||
accept=".txt,.env"
|
accept=".txt,.env,.yml"
|
||||||
onChange={handleFileSelect}
|
onChange={handleFileSelect}
|
||||||
/>
|
/>
|
||||||
<div className="flex flex-row w-full items-center justify-center mb-6 mt-5">
|
<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;
|
|
||||||
@@ -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