|
|
@@ -20,6 +20,7 @@ import Heading from "components/form-components/Heading";
|
|
|
import Loading from "components/Loading";
|
|
|
import api from "shared/api";
|
|
|
import { Context } from "shared/Context";
|
|
|
+import { dotenv_parse } from "shared/string_utils";
|
|
|
|
|
|
interface Props extends KeyValueArrayField {
|
|
|
id: string;
|
|
|
@@ -102,51 +103,7 @@ const KeyValueArray: React.FC<Props> = (props) => {
|
|
|
}
|
|
|
|
|
|
const parseEnv = (src: any, options: any) => {
|
|
|
- const debug = Boolean(options && options.debug);
|
|
|
- const obj = {} as Record<string, string>;
|
|
|
- const NEWLINE = "\n";
|
|
|
- const RE_INI_KEY_VAL = /^\s*([\w.-]+)\s*=\s*(.*)?\s*$/;
|
|
|
- const RE_NEWLINES = /\\n/g;
|
|
|
- const NEWLINES_MATCH = /\n|\r|\r\n/;
|
|
|
-
|
|
|
- // convert Buffers before splitting into lines and processing
|
|
|
- src
|
|
|
- .toString()
|
|
|
- .split(NEWLINES_MATCH)
|
|
|
- .forEach(function (line: any, idx: any) {
|
|
|
- // matching "KEY' and 'VAL' in 'KEY=VAL'
|
|
|
- const keyValueArr = line.match(RE_INI_KEY_VAL);
|
|
|
- // matched?
|
|
|
- if (keyValueArr != null) {
|
|
|
- const key = keyValueArr[1];
|
|
|
- // default undefined or missing values to empty string
|
|
|
- let val = keyValueArr[2] || "";
|
|
|
- const end = val.length - 1;
|
|
|
- const isDoubleQuoted = val[0] === '"' && val[end] === '"';
|
|
|
- const isSingleQuoted = val[0] === "'" && val[end] === "'";
|
|
|
-
|
|
|
- // if single or double quoted, remove quotes
|
|
|
- if (isSingleQuoted || isDoubleQuoted) {
|
|
|
- val = val.substring(1, end);
|
|
|
-
|
|
|
- // if double quoted, expand newlines
|
|
|
- if (isDoubleQuoted) {
|
|
|
- val = val.replace(RE_NEWLINES, NEWLINE);
|
|
|
- }
|
|
|
- } else {
|
|
|
- // remove surrounding whitespace
|
|
|
- val = val.trim();
|
|
|
- }
|
|
|
-
|
|
|
- obj[key] = val;
|
|
|
- } else if (debug) {
|
|
|
- console.log(
|
|
|
- `did not match key and value when parsing line ${idx + 1}: ${line}`
|
|
|
- );
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
- return obj;
|
|
|
+ return dotenv_parse(src);
|
|
|
};
|
|
|
|
|
|
const readFile = (env: string) => {
|
|
|
@@ -333,7 +290,7 @@ const KeyValueArray: React.FC<Props> = (props) => {
|
|
|
|
|
|
return (
|
|
|
<InputWrapper key={i}>
|
|
|
- <Input
|
|
|
+ <KeyInput
|
|
|
placeholder="ex: key"
|
|
|
width="270px"
|
|
|
value={entry.key}
|
|
|
@@ -360,30 +317,41 @@ const KeyValueArray: React.FC<Props> = (props) => {
|
|
|
}
|
|
|
/>
|
|
|
<Spacer />
|
|
|
- <Input
|
|
|
- placeholder="ex: value"
|
|
|
- width="270px"
|
|
|
- value={value}
|
|
|
- onChange={(e: any) => {
|
|
|
- e.persist();
|
|
|
- setState((prev) => {
|
|
|
- return {
|
|
|
- values: prev.values?.map((t, j) => {
|
|
|
- if (j == i) {
|
|
|
- return {
|
|
|
- ...t,
|
|
|
- value: e.target.value,
|
|
|
- };
|
|
|
- }
|
|
|
- return t;
|
|
|
- }),
|
|
|
- };
|
|
|
- });
|
|
|
- }}
|
|
|
- disabled={props.isReadOnly || value.includes("PORTERSECRET")}
|
|
|
- type={value.includes("PORTERSECRET") ? "password" : "text"}
|
|
|
- spellCheck={false}
|
|
|
- />
|
|
|
+ {value?.includes("PORTERSECRET") ? (
|
|
|
+ <KeyInput
|
|
|
+ placeholder="ex: value"
|
|
|
+ width="270px"
|
|
|
+ disabled
|
|
|
+ type={"password"}
|
|
|
+ spellCheck={false}
|
|
|
+ value={value}
|
|
|
+ />
|
|
|
+ ) : (
|
|
|
+ <MultiLineInput
|
|
|
+ placeholder="ex: value"
|
|
|
+ width="270px"
|
|
|
+ value={value}
|
|
|
+ onChange={(e: any) => {
|
|
|
+ e.persist();
|
|
|
+ setState((prev) => {
|
|
|
+ return {
|
|
|
+ values: prev.values?.map((t, j) => {
|
|
|
+ if (j == i) {
|
|
|
+ return {
|
|
|
+ ...t,
|
|
|
+ value: e.target.value,
|
|
|
+ };
|
|
|
+ }
|
|
|
+ return t;
|
|
|
+ }),
|
|
|
+ };
|
|
|
+ });
|
|
|
+ }}
|
|
|
+ disabled={props.isReadOnly}
|
|
|
+ spellCheck={false}
|
|
|
+ rows={value?.split("\n").length}
|
|
|
+ />
|
|
|
+ )}
|
|
|
{renderDeleteButton(i)}
|
|
|
{renderHiddenOption(value.includes("PORTERSECRET"), i)}
|
|
|
{checkOverridedKey(entry.key)}
|
|
|
@@ -486,24 +454,27 @@ export const getFinalVariablesForKeyValueArray: GetFinalVariablesFunction = (
|
|
|
};
|
|
|
}
|
|
|
|
|
|
+ const isNumber = (s: string) => {
|
|
|
+ return !isNaN(!s ? NaN : Number(String(s).trim()));
|
|
|
+ };
|
|
|
+
|
|
|
+ const rg = /(?:^|[^\\])(\\n)/g;
|
|
|
+ const fixNewlines = (s: string) => {
|
|
|
+ while (rg.test(s)) {
|
|
|
+ s = s.replace(rg, (str) => {
|
|
|
+ if (str.length == 2) return "\n";
|
|
|
+ if (str[0] != "\\") return str[0] + "\n";
|
|
|
+ return "\\n";
|
|
|
+ });
|
|
|
+ }
|
|
|
+ return s;
|
|
|
+ };
|
|
|
+
|
|
|
if (props.variable.includes("env")) {
|
|
|
let obj = {
|
|
|
normal: {},
|
|
|
} as any;
|
|
|
- const rg = /(?:^|[^\\])(\\n)/g;
|
|
|
- const fixNewlines = (s: string) => {
|
|
|
- while (rg.test(s)) {
|
|
|
- s = s.replace(rg, (str) => {
|
|
|
- if (str.length == 2) return "\n";
|
|
|
- if (str[0] != "\\") return str[0] + "\n";
|
|
|
- return "\\n";
|
|
|
- });
|
|
|
- }
|
|
|
- return s;
|
|
|
- };
|
|
|
- const isNumber = (s: string) => {
|
|
|
- return !isNaN(!s ? NaN : Number(String(s).trim()));
|
|
|
- };
|
|
|
+
|
|
|
state.values.forEach((entry: any, i: number) => {
|
|
|
if (isNumber(entry.value)) {
|
|
|
obj.normal[entry.key] = entry.value;
|
|
|
@@ -539,20 +510,7 @@ export const getFinalVariablesForKeyValueArray: GetFinalVariablesFunction = (
|
|
|
};
|
|
|
} else {
|
|
|
let obj = {} as any;
|
|
|
- const rg = /(?:^|[^\\])(\\n)/g;
|
|
|
- const fixNewlines = (s: string) => {
|
|
|
- while (rg.test(s)) {
|
|
|
- s = s.replace(rg, (str) => {
|
|
|
- if (str.length == 2) return "\n";
|
|
|
- if (str[0] != "\\") return str[0] + "\n";
|
|
|
- return "\\n";
|
|
|
- });
|
|
|
- }
|
|
|
- return s;
|
|
|
- };
|
|
|
- const isNumber = (s: string) => {
|
|
|
- return !isNaN(!s ? NaN : Number(String(s).trim()));
|
|
|
- };
|
|
|
+
|
|
|
state.values.forEach((entry: any, i: number) => {
|
|
|
if (isNumber(entry.value)) {
|
|
|
obj[entry.key] = entry.value;
|
|
|
@@ -579,7 +537,7 @@ export const getMetadata: GetMetadataFunction<KeyValueArrayMetadata> = (
|
|
|
state: KeyValueArrayFieldState
|
|
|
) => {
|
|
|
// We don't need any metadata for other key-value-array fields yet so we return null for that variable
|
|
|
- if (!props?.variable?.includes("env")) {
|
|
|
+ if (!state || !props?.variable?.includes("env")) {
|
|
|
return {
|
|
|
[props.variable]: null,
|
|
|
};
|
|
|
@@ -654,22 +612,35 @@ const ExpandableEnvGroup: React.FC<{
|
|
|
|
|
|
return (
|
|
|
<InputWrapper key={i}>
|
|
|
- <Input
|
|
|
+ <KeyInput
|
|
|
placeholder="ex: key"
|
|
|
width="270px"
|
|
|
value={key}
|
|
|
disabled
|
|
|
/>
|
|
|
<Spacer />
|
|
|
- <Input
|
|
|
- placeholder="ex: value"
|
|
|
- width="270px"
|
|
|
- value={value}
|
|
|
- disabled
|
|
|
- type={
|
|
|
- value.includes("PORTERSECRET") ? "password" : "text"
|
|
|
- }
|
|
|
- />
|
|
|
+ {value?.includes("PORTERSECRET") ? (
|
|
|
+ <KeyInput
|
|
|
+ placeholder="ex: value"
|
|
|
+ width="270px"
|
|
|
+ value={value}
|
|
|
+ disabled
|
|
|
+ type={
|
|
|
+ value.includes("PORTERSECRET")
|
|
|
+ ? "password"
|
|
|
+ : "text"
|
|
|
+ }
|
|
|
+ />
|
|
|
+ ) : (
|
|
|
+ <MultiLineInput
|
|
|
+ placeholder="ex: value"
|
|
|
+ width="270px"
|
|
|
+ value={value}
|
|
|
+ disabled
|
|
|
+ rows={value?.split("\n").length}
|
|
|
+ spellCheck={false}
|
|
|
+ ></MultiLineInput>
|
|
|
+ )}
|
|
|
</InputWrapper>
|
|
|
);
|
|
|
}
|
|
|
@@ -822,7 +793,7 @@ type InputProps = {
|
|
|
borderColor?: string;
|
|
|
};
|
|
|
|
|
|
-const Input = styled.input<InputProps>`
|
|
|
+const KeyInput = styled.input<InputProps>`
|
|
|
outline: none;
|
|
|
border: none;
|
|
|
margin-bottom: 5px;
|
|
|
@@ -837,6 +808,48 @@ const Input = styled.input<InputProps>`
|
|
|
height: 35px;
|
|
|
`;
|
|
|
|
|
|
+export const MultiLineInput = styled.textarea<InputProps>`
|
|
|
+ outline: none;
|
|
|
+ border: none;
|
|
|
+ margin-bottom: 5px;
|
|
|
+ font-size: 13px;
|
|
|
+ background: #ffffff11;
|
|
|
+ border: 1px solid
|
|
|
+ ${(props) => (props.borderColor ? props.borderColor : "#ffffff55")};
|
|
|
+ border-radius: 3px;
|
|
|
+ min-width: ${(props) => (props.width ? props.width : "270px")};
|
|
|
+ max-width: ${(props) => (props.width ? props.width : "270px")};
|
|
|
+ color: ${(props) => (props.disabled ? "#ffffff44" : "white")};
|
|
|
+ padding: 8px 10px 5px 10px;
|
|
|
+ min-height: 35px;
|
|
|
+ max-height: 100px;
|
|
|
+ white-space: nowrap;
|
|
|
+
|
|
|
+ ::-webkit-scrollbar {
|
|
|
+ width: 8px;
|
|
|
+ :horizontal {
|
|
|
+ height: 8px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ ::-webkit-scrollbar-corner {
|
|
|
+ width: 10px;
|
|
|
+ background: #ffffff11;
|
|
|
+ color: white;
|
|
|
+ }
|
|
|
+
|
|
|
+ ::-webkit-scrollbar-track {
|
|
|
+ width: 10px;
|
|
|
+ -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
|
|
|
+ box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
|
|
|
+ }
|
|
|
+
|
|
|
+ ::-webkit-scrollbar-thumb {
|
|
|
+ background-color: darkgrey;
|
|
|
+ outline: 1px solid slategrey;
|
|
|
+ }
|
|
|
+`;
|
|
|
+
|
|
|
const Label = styled.div`
|
|
|
color: #ffffff;
|
|
|
margin-bottom: 10px;
|