Configuration List Forms (#83)
Created Lists & forms for configurations in mcs
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -0,0 +1,151 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2019 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
import React, {
|
||||
useState,
|
||||
useEffect,
|
||||
createRef,
|
||||
ChangeEvent,
|
||||
useCallback
|
||||
} from "react";
|
||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||
import Grid from "@material-ui/core/Grid";
|
||||
import get from "lodash/get";
|
||||
import InputBoxWrapper from "../InputBoxWrapper/InputBoxWrapper";
|
||||
import { InputLabel, Tooltip } from "@material-ui/core";
|
||||
import { fieldBasic } from "../common/styleLibrary";
|
||||
import HelpIcon from "@material-ui/icons/Help";
|
||||
|
||||
interface ICSVMultiSelector {
|
||||
elements: string;
|
||||
name: string;
|
||||
label: string;
|
||||
tooltip?: string;
|
||||
classes: any;
|
||||
onChange: (elements: string) => void;
|
||||
}
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
...fieldBasic,
|
||||
inputLabel: {
|
||||
...fieldBasic.inputLabel,
|
||||
marginBottom: 10
|
||||
},
|
||||
inputContainer: {
|
||||
height: 150,
|
||||
overflowY: "auto",
|
||||
padding: 15,
|
||||
position: "relative",
|
||||
border: "1px solid #c4c4c4",
|
||||
marginBottom: 10
|
||||
},
|
||||
labelContainer: {
|
||||
display: "flex"
|
||||
}
|
||||
});
|
||||
|
||||
const CSVMultiSelector = ({
|
||||
elements,
|
||||
name,
|
||||
label,
|
||||
tooltip,
|
||||
onChange,
|
||||
classes
|
||||
}: ICSVMultiSelector) => {
|
||||
const [currentElements, setCurrentElements] = useState<string[]>([""]);
|
||||
const bottomList = createRef<HTMLDivElement>();
|
||||
|
||||
// Use effect to get the initial values from props
|
||||
useCallback(() => {
|
||||
if (currentElements.length === 1 && currentElements[0] === "") {
|
||||
const elementsSplitted = elements.split(",");
|
||||
if (elementsSplitted[elementsSplitted.length - 1].trim() !== "") {
|
||||
elementsSplitted.push("");
|
||||
}
|
||||
setCurrentElements(elementsSplitted);
|
||||
}
|
||||
}, [elements, setCurrentElements, currentElements]);
|
||||
|
||||
// Use effect to send new values to onChange
|
||||
useEffect(() => {
|
||||
const elementsString = currentElements
|
||||
.filter(element => element.trim() !== "")
|
||||
.join(",");
|
||||
onChange(elementsString);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [currentElements]);
|
||||
|
||||
// If the last input is not empty, we add a new one
|
||||
const addEmptyLine = (elementsUp: string[]) => {
|
||||
if (elementsUp[elementsUp.length - 1].trim() !== "") {
|
||||
elementsUp.push("");
|
||||
const refScroll = bottomList.current;
|
||||
|
||||
if (refScroll) {
|
||||
refScroll.scrollIntoView(false);
|
||||
}
|
||||
}
|
||||
|
||||
return elementsUp;
|
||||
};
|
||||
|
||||
// Onchange function for input box, we get the dataset-index & only update that value in the array
|
||||
const onChangeElement = (e: ChangeEvent<HTMLInputElement>) => {
|
||||
e.persist();
|
||||
|
||||
let updatedElement = [...currentElements];
|
||||
const index = get(e.target, "dataset.index", 0);
|
||||
updatedElement[index] = e.target.value;
|
||||
|
||||
updatedElement = addEmptyLine(updatedElement);
|
||||
setCurrentElements(updatedElement);
|
||||
};
|
||||
|
||||
const inputs = currentElements.map((element, index) => {
|
||||
return (
|
||||
<InputBoxWrapper
|
||||
id={`${name}-${index.toString()}`}
|
||||
label={""}
|
||||
name={`${name}-${index.toString()}`}
|
||||
value={currentElements[index]}
|
||||
onChange={onChangeElement}
|
||||
index={index}
|
||||
key={`csv-${name}-${index.toString()}`}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Grid item xs={12} className={classes.fieldContainer}>
|
||||
<InputLabel className={classes.inputLabel}>{label}</InputLabel>
|
||||
<Grid item xs={12} className={classes.inputContainer}>
|
||||
{inputs}
|
||||
<div ref={bottomList} />
|
||||
</Grid>
|
||||
{tooltip !== "" && (
|
||||
<div className={classes.tooltipContainer}>
|
||||
<Tooltip title={tooltip} placement="left">
|
||||
<HelpIcon />
|
||||
</Tooltip>
|
||||
</div>
|
||||
)}
|
||||
</Grid>
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
export default withStyles(styles)(CSVMultiSelector);
|
||||
@@ -43,6 +43,7 @@ interface InputBoxProps {
|
||||
type?: string;
|
||||
tooltip?: string;
|
||||
autoComplete?: string;
|
||||
index?: number;
|
||||
}
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
@@ -89,14 +90,17 @@ const InputBoxWrapper = ({
|
||||
disabled = false,
|
||||
multiline = false,
|
||||
tooltip = "",
|
||||
index = 0,
|
||||
classes
|
||||
}: InputBoxProps) => {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Grid item xs={12} className={classes.fieldContainer}>
|
||||
<InputLabel htmlFor={id} className={classes.inputLabel}>
|
||||
{label}
|
||||
</InputLabel>
|
||||
{label !== "" && (
|
||||
<InputLabel htmlFor={id} className={classes.inputLabel}>
|
||||
{label}
|
||||
</InputLabel>
|
||||
)}
|
||||
<div className={classes.textBoxContainer}>
|
||||
<InputField
|
||||
className={classes.boxDesign}
|
||||
@@ -110,10 +114,11 @@ const InputBoxWrapper = ({
|
||||
type={type}
|
||||
multiline={multiline}
|
||||
autoComplete={autoComplete}
|
||||
inputProps={{ "data-index": index }}
|
||||
/>
|
||||
</div>
|
||||
{tooltip !== "" && (
|
||||
<div>
|
||||
<div className={classes.tooltipContainer}>
|
||||
<Tooltip title={tooltip} placement="left">
|
||||
<HelpIcon />
|
||||
</Tooltip>
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// This object contains variables that will be used across form components.
|
||||
|
||||
export const fieldBasic = {
|
||||
inputLabel: {
|
||||
fontWeight: 500,
|
||||
@@ -27,5 +28,8 @@ export const fieldBasic = {
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
marginBottom: 10
|
||||
},
|
||||
tooltipContainer: {
|
||||
marginLeft: 5
|
||||
}
|
||||
};
|
||||
|
||||
@@ -19,10 +19,11 @@ import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||
import Grid from "@material-ui/core/Grid";
|
||||
import InputBoxWrapper from "../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
|
||||
import RadioGroupSelector from "../Common/FormComponents/RadioGroupSelector/RadioGroupSelector";
|
||||
import { KVField } from "./types";
|
||||
import { IElementValue, KVField } from "./types";
|
||||
import CSVMultiSelector from "../Common/FormComponents/CSVMultiSelector/CSVMultiSelector";
|
||||
|
||||
interface IConfGenericProps {
|
||||
onChange: (newValue: Map<string, string>) => void;
|
||||
onChange: (newValue: IElementValue[]) => void;
|
||||
fields: KVField[];
|
||||
classes: any;
|
||||
}
|
||||
@@ -34,100 +35,92 @@ const ConfTargetGeneric = ({
|
||||
fields,
|
||||
classes
|
||||
}: IConfGenericProps) => {
|
||||
//Local States
|
||||
const [valueHolder, setValueHolder] = useState<IElementValue[]>([]);
|
||||
const fieldsElements = !fields ? [] : fields;
|
||||
|
||||
const [keyValues, setKeyValues] = useState<Map<string, string>>(new Map());
|
||||
// Effect to create all the values to hold
|
||||
useEffect(() => {
|
||||
const values: IElementValue[] = [];
|
||||
fields.forEach(field => {
|
||||
const stateInsert: IElementValue = {
|
||||
key: field.name,
|
||||
value: field.type === "on|off" ? "false" : ""
|
||||
};
|
||||
values.push(stateInsert);
|
||||
});
|
||||
|
||||
setValueHolder(values);
|
||||
}, [fields]);
|
||||
|
||||
useEffect(() => {
|
||||
if (keyValues.size > 0) {
|
||||
onChange(keyValues);
|
||||
}
|
||||
}, [keyValues, onChange]);
|
||||
onChange(valueHolder);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [valueHolder]);
|
||||
|
||||
const val = (key: string): string => {
|
||||
return keyValues.get(key) === undefined ? "" : keyValues.get(key) + "";
|
||||
const setValueElement = (key: string, value: string, index: number) => {
|
||||
const valuesDup = [...valueHolder];
|
||||
valuesDup[index] = { key, value };
|
||||
|
||||
setValueHolder(valuesDup);
|
||||
};
|
||||
const valFall = (key: string, fallback: string): string => {
|
||||
return keyValues.get(key) === undefined
|
||||
? fallback
|
||||
: keyValues.get(key) + "";
|
||||
|
||||
const fieldDefinition = (field: KVField, item: number) => {
|
||||
switch (field.type) {
|
||||
case "on|off":
|
||||
return (
|
||||
<RadioGroupSelector
|
||||
currentSelection={valueHolder[item] ? valueHolder[item].value : ""}
|
||||
id={field.name}
|
||||
name={field.name}
|
||||
label={field.label}
|
||||
tooltip={field.tooltip}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
|
||||
setValueElement(field.name, e.target.value, item)
|
||||
}
|
||||
selectorOptions={[
|
||||
{ label: "On", value: "true" },
|
||||
{ label: "Off", value: "false" }
|
||||
]}
|
||||
/>
|
||||
);
|
||||
case "csv":
|
||||
return (
|
||||
<CSVMultiSelector
|
||||
elements={valueHolder[item] ? valueHolder[item].value : ""}
|
||||
label={field.label}
|
||||
name={field.name}
|
||||
onChange={(value: string) =>
|
||||
setValueElement(field.name, value, item)
|
||||
}
|
||||
tooltip={field.tooltip}
|
||||
/>
|
||||
);
|
||||
default:
|
||||
return (
|
||||
<InputBoxWrapper
|
||||
id={field.name}
|
||||
name={field.name}
|
||||
label={field.label}
|
||||
tooltip={field.tooltip}
|
||||
value={valueHolder[item] ? valueHolder[item].value : ""}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
|
||||
setValueElement(field.name, e.target.value, item)
|
||||
}
|
||||
multiline={!!field.multiline}
|
||||
/>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Grid container>
|
||||
{fields.map(field => (
|
||||
{fieldsElements.map((field, item) => (
|
||||
<React.Fragment key={field.name}>
|
||||
{field.type === "on|off" ? (
|
||||
<Grid item xs={12}>
|
||||
<RadioGroupSelector
|
||||
currentSelection={valFall(field.name, "false")}
|
||||
id={field.name}
|
||||
name={field.name}
|
||||
label={field.label}
|
||||
tooltip={field.tooltip}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setKeyValues(keyValues.set(field.name, e.target.value));
|
||||
}}
|
||||
selectorOptions={[
|
||||
{ label: "On", value: "true" },
|
||||
{ label: "Off", value: "false" }
|
||||
]}
|
||||
/>
|
||||
</Grid>
|
||||
) : (
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
id={field.name}
|
||||
name={field.name}
|
||||
label={field.label}
|
||||
tooltip={field.tooltip}
|
||||
value={val(field.name)}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setKeyValues(keyValues.set(field.name, e.target.value));
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
)}
|
||||
<Grid item xs={12}>
|
||||
{fieldDefinition(field, item)}
|
||||
</Grid>
|
||||
</React.Fragment>
|
||||
))}
|
||||
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
id="queue-dir"
|
||||
name="queue_dir"
|
||||
label="Queue Dir"
|
||||
value={val("queue_dir")}
|
||||
tooltip="staging dir for undelivered messages e.g. '/home/events'"
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setKeyValues(keyValues.set("queue_dir", e.target.value));
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
id="queue-limit"
|
||||
name="queue_limit"
|
||||
label="Queue Limit"
|
||||
type="number"
|
||||
value={val("queue_limit")}
|
||||
tooltip="maximum limit for undelivered messages, defaults to '10000'"
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setKeyValues(keyValues.set("queue_limit", e.target.value));
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
id="comment"
|
||||
name="comment"
|
||||
label="Comment"
|
||||
multiline={true}
|
||||
value={val("comment")}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setKeyValues(keyValues.set("comment", e.target.value));
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<br />
|
||||
</Grid>
|
||||
|
||||
@@ -0,0 +1,155 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2020 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React, { useState } from "react";
|
||||
import get from "lodash/get";
|
||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||
import Grid from "@material-ui/core/Grid";
|
||||
import Typography from "@material-ui/core/Typography";
|
||||
import { TextField } from "@material-ui/core";
|
||||
import InputAdornment from "@material-ui/core/InputAdornment";
|
||||
import SearchIcon from "@material-ui/icons/Search";
|
||||
import history from "../../../../history";
|
||||
import TableWrapper from "../../Common/TableWrapper/TableWrapper";
|
||||
import { configurationElements } from "../utils";
|
||||
import { IConfigurationElement } from "../types";
|
||||
import EditConfiguration from "../CustomForms/EditConfiguration";
|
||||
|
||||
interface IListConfiguration {
|
||||
classes: any;
|
||||
}
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
errorBlock: {
|
||||
color: "red"
|
||||
},
|
||||
strongText: {
|
||||
fontWeight: 700
|
||||
},
|
||||
keyName: {
|
||||
marginLeft: 5
|
||||
},
|
||||
actionsTray: {
|
||||
textAlign: "right",
|
||||
"& button": {
|
||||
marginLeft: 10
|
||||
}
|
||||
},
|
||||
searchField: {
|
||||
background: "#FFFFFF",
|
||||
padding: 12,
|
||||
borderRadius: 5,
|
||||
boxShadow: "0px 3px 6px #00000012"
|
||||
},
|
||||
iconText: {
|
||||
lineHeight: "24px"
|
||||
}
|
||||
});
|
||||
|
||||
const ConfigurationsList = ({ classes }: IListConfiguration) => {
|
||||
const [editScreenOpen, setEditScreenOpen] = useState(false);
|
||||
const [selectedConfiguration, setSelectedConfiguration] = useState({
|
||||
configuration_id: "",
|
||||
configuration_label: ""
|
||||
});
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [error, setError] = useState("");
|
||||
const [filter, setFilter] = useState("");
|
||||
|
||||
const tableActions = [
|
||||
{
|
||||
type: "edit",
|
||||
onClick: (element: IConfigurationElement) => {
|
||||
const url = get(element, "url", "");
|
||||
if (url !== "") {
|
||||
// We redirect Browser
|
||||
history.push(url);
|
||||
} else {
|
||||
setSelectedConfiguration(element);
|
||||
setEditScreenOpen(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
const filteredRecords: IConfigurationElement[] = configurationElements.filter(
|
||||
elementItem =>
|
||||
elementItem.configuration_id
|
||||
.toLocaleLowerCase()
|
||||
.includes(filter.toLocaleLowerCase())
|
||||
);
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
{editScreenOpen && (
|
||||
<EditConfiguration
|
||||
open={editScreenOpen}
|
||||
closeModalAndRefresh={() => {
|
||||
setIsLoading(true);
|
||||
setEditScreenOpen(false);
|
||||
}}
|
||||
selectedConfiguration={selectedConfiguration}
|
||||
/>
|
||||
)}
|
||||
<Grid container>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="h6">Configurations List</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<br />
|
||||
</Grid>
|
||||
{error !== "" && <Grid container>{error}</Grid>}
|
||||
<Grid item xs={12} className={classes.actionsTray}>
|
||||
<TextField
|
||||
placeholder="Filter"
|
||||
className={classes.searchField}
|
||||
id="search-resource"
|
||||
label=""
|
||||
onChange={event => {
|
||||
setFilter(event.target.value);
|
||||
}}
|
||||
InputProps={{
|
||||
disableUnderline: true,
|
||||
startAdornment: (
|
||||
<InputAdornment position="start">
|
||||
<SearchIcon />
|
||||
</InputAdornment>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<br />
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<TableWrapper
|
||||
itemActions={tableActions}
|
||||
columns={[
|
||||
{ label: "Configuration", elementKey: "configuration_id" }
|
||||
]}
|
||||
isLoading={isLoading}
|
||||
records={filteredRecords}
|
||||
entityName="Configurations"
|
||||
idField="configuration_id"
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
export default withStyles(styles)(ConfigurationsList);
|
||||
@@ -0,0 +1,188 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2020 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React, { useState } from "react";
|
||||
import get from "lodash/get";
|
||||
import Grid from "@material-ui/core/Grid";
|
||||
import Typography from "@material-ui/core/Typography";
|
||||
import { Button, TextField } from "@material-ui/core";
|
||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||
import InputAdornment from "@material-ui/core/InputAdornment";
|
||||
import SearchIcon from "@material-ui/icons/Search";
|
||||
import AddIcon from "@material-ui/icons/Add";
|
||||
import TableWrapper from "../../Common/TableWrapper/TableWrapper";
|
||||
import EditConfiguration from "../CustomForms/EditConfiguration";
|
||||
|
||||
interface IMatchParams {
|
||||
isExact: boolean;
|
||||
params: any;
|
||||
path: string;
|
||||
}
|
||||
|
||||
interface IWebhookPanel {
|
||||
match: IMatchParams;
|
||||
classes: any;
|
||||
}
|
||||
|
||||
interface IWebhook {
|
||||
name: string;
|
||||
}
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
errorBlock: {
|
||||
color: "red"
|
||||
},
|
||||
strongText: {
|
||||
fontWeight: 700
|
||||
},
|
||||
keyName: {
|
||||
marginLeft: 5
|
||||
},
|
||||
actionsTray: {
|
||||
textAlign: "right",
|
||||
"& button": {
|
||||
marginLeft: 10
|
||||
}
|
||||
},
|
||||
searchField: {
|
||||
background: "#FFFFFF",
|
||||
padding: 12,
|
||||
borderRadius: 5,
|
||||
boxShadow: "0px 3px 6px #00000012"
|
||||
},
|
||||
iconText: {
|
||||
lineHeight: "24px"
|
||||
}
|
||||
});
|
||||
|
||||
const panels = {
|
||||
logger: {
|
||||
main: "logger",
|
||||
title: "Logger Webhook Configuration",
|
||||
modalTitle: "Logger Webhook",
|
||||
apiURL: "",
|
||||
configuration: {
|
||||
configuration_id: "logger_webhook",
|
||||
configuration_label: "Logger Webhook"
|
||||
}
|
||||
},
|
||||
audit: {
|
||||
main: "audit",
|
||||
title: "Audit Webhook Configuration",
|
||||
modalTitle: "Audit Webhook",
|
||||
apiURL: "",
|
||||
configuration: {
|
||||
configuration_id: "audit_webhook",
|
||||
configuration_label: "Audit Webhook"
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const WebhookPanel = ({ match, classes }: IWebhookPanel) => {
|
||||
const [addWebhookOpen, setAddWebhookOpen] = useState<boolean>(false);
|
||||
const [error, setError] = useState<string>("");
|
||||
const [filter, setFilter] = useState<string>("");
|
||||
const [isLoading, setIsLoading] = useState<boolean>(false);
|
||||
const [webhooks, setWebhooks] = useState<IWebhook[]>([]);
|
||||
|
||||
const pathIn = get(match, "path", "");
|
||||
const panelToDisplay = pathIn.split("/");
|
||||
const panelData = get(panels, panelToDisplay[2], false);
|
||||
|
||||
if (!panelData) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const filteredRecords: IWebhook[] = webhooks.filter(elementItem =>
|
||||
elementItem.name.toLocaleLowerCase().includes(filter.toLocaleLowerCase())
|
||||
);
|
||||
|
||||
const tableActions = [
|
||||
{
|
||||
type: "edit",
|
||||
onClick: () => {}
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
{addWebhookOpen && (
|
||||
<EditConfiguration
|
||||
open={addWebhookOpen}
|
||||
closeModalAndRefresh={() => {
|
||||
setIsLoading(true);
|
||||
setAddWebhookOpen(false);
|
||||
}}
|
||||
selectedConfiguration={panelData.configuration}
|
||||
/>
|
||||
)}
|
||||
<Grid container>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="h6">{panelData.title}</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<br />
|
||||
</Grid>
|
||||
{error !== "" && <Grid container>{error}</Grid>}
|
||||
<Grid item xs={12} className={classes.actionsTray}>
|
||||
<TextField
|
||||
placeholder="Filter"
|
||||
className={classes.searchField}
|
||||
id="search-resource"
|
||||
label=""
|
||||
onChange={event => {
|
||||
setFilter(event.target.value);
|
||||
}}
|
||||
InputProps={{
|
||||
disableUnderline: true,
|
||||
startAdornment: (
|
||||
<InputAdornment position="start">
|
||||
<SearchIcon />
|
||||
</InputAdornment>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
startIcon={<AddIcon />}
|
||||
onClick={() => {
|
||||
setAddWebhookOpen(true);
|
||||
}}
|
||||
>
|
||||
Add Webhook Configuration
|
||||
</Button>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<br />
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<TableWrapper
|
||||
itemActions={tableActions}
|
||||
columns={[{ label: "Name", elementKey: "name" }]}
|
||||
isLoading={isLoading}
|
||||
records={filteredRecords}
|
||||
entityName="Webhook Configurations"
|
||||
idField="name"
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
export default withStyles(styles)(WebhookPanel);
|
||||
@@ -18,11 +18,12 @@ import React, { useCallback, useEffect, useState } from "react";
|
||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||
import { FormControlLabel, Switch } from "@material-ui/core";
|
||||
import Grid from "@material-ui/core/Grid";
|
||||
import InputBoxWrapper from "../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
|
||||
import RadioGroupSelector from "../Common/FormComponents/RadioGroupSelector/RadioGroupSelector";
|
||||
import InputBoxWrapper from "../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
|
||||
import RadioGroupSelector from "../../Common/FormComponents/RadioGroupSelector/RadioGroupSelector";
|
||||
import { IElementValue } from "../types";
|
||||
|
||||
interface IConfMySqlProps {
|
||||
onChange: (newValue: Map<string, string>) => void;
|
||||
onChange: (newValue: IElementValue[]) => void;
|
||||
classes: any;
|
||||
}
|
||||
|
||||
@@ -81,32 +82,16 @@ const ConfMySql = ({ onChange, classes }: IConfMySqlProps) => {
|
||||
|
||||
useEffect(() => {
|
||||
if (dsnString !== "") {
|
||||
let values: Map<string, string> = new Map();
|
||||
const formValues = [
|
||||
{ key: "dsn_string", value: dsnString },
|
||||
{ key: "table", value: table },
|
||||
{ key: "format", value: format },
|
||||
{ key: "queue_dir", value: queueDir },
|
||||
{ key: "queue_limit", value: queueLimit },
|
||||
{ key: "comment", value: comment }
|
||||
];
|
||||
|
||||
if (dsnString !== "") {
|
||||
values.set("dsn_string", dsnString);
|
||||
}
|
||||
if (table !== "") {
|
||||
values.set("table", table);
|
||||
}
|
||||
|
||||
if (format !== "") {
|
||||
values.set("format", format);
|
||||
}
|
||||
|
||||
if (queueDir !== "") {
|
||||
values.set("queue_dir", queueDir);
|
||||
}
|
||||
|
||||
if (queueLimit !== "") {
|
||||
values.set("queue_limit", queueLimit);
|
||||
}
|
||||
|
||||
if (comment !== "") {
|
||||
values.set("comment", comment);
|
||||
}
|
||||
|
||||
onChange(values);
|
||||
onChange(formValues);
|
||||
}
|
||||
}, [dsnString, table, format, queueDir, queueLimit, comment, onChange]);
|
||||
|
||||
@@ -18,12 +18,13 @@ import React, { useCallback, useEffect, useState } from "react";
|
||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||
import { FormControlLabel, Switch } from "@material-ui/core";
|
||||
import Grid from "@material-ui/core/Grid";
|
||||
import InputBoxWrapper from "../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
|
||||
import RadioGroupSelector from "../Common/FormComponents/RadioGroupSelector/RadioGroupSelector";
|
||||
import SelectWrapper from "../Common/FormComponents/SelectWrapper/SelectWrapper";
|
||||
import InputBoxWrapper from "../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
|
||||
import RadioGroupSelector from "../../Common/FormComponents/RadioGroupSelector/RadioGroupSelector";
|
||||
import SelectWrapper from "../../Common/FormComponents/SelectWrapper/SelectWrapper";
|
||||
import { IElementValue } from "../types";
|
||||
|
||||
interface IConfPostgresProps {
|
||||
onChange: (newValue: Map<string, string>) => void;
|
||||
onChange: (newValue: IElementValue[]) => void;
|
||||
classes: any;
|
||||
}
|
||||
|
||||
@@ -129,32 +130,16 @@ const ConfPostgres = ({ onChange, classes }: IConfPostgresProps) => {
|
||||
|
||||
useEffect(() => {
|
||||
if (connectionString !== "") {
|
||||
let values: Map<string, string> = new Map();
|
||||
const formValues = [
|
||||
{ key: "connection_string", value: connectionString },
|
||||
{ key: "table", value: table },
|
||||
{ key: "format", value: format },
|
||||
{ key: "queue_dir", value: queueDir },
|
||||
{ key: "queue_limit", value: queueLimit },
|
||||
{ key: "comment", value: comment }
|
||||
];
|
||||
|
||||
if (connectionString !== "") {
|
||||
values.set("connection_string", connectionString);
|
||||
}
|
||||
if (table !== "") {
|
||||
values.set("table", table);
|
||||
}
|
||||
|
||||
if (format !== "") {
|
||||
values.set("format", format);
|
||||
}
|
||||
|
||||
if (queueDir !== "") {
|
||||
values.set("queue_dir", queueDir);
|
||||
}
|
||||
|
||||
if (queueLimit !== "") {
|
||||
values.set("queue_limit", queueLimit);
|
||||
}
|
||||
|
||||
if (comment !== "") {
|
||||
values.set("comment", comment);
|
||||
}
|
||||
|
||||
onChange(values);
|
||||
onChange(formValues);
|
||||
}
|
||||
}, [
|
||||
connectionString,
|
||||
@@ -0,0 +1,160 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2019 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React, { useCallback, useEffect, useState } from "react";
|
||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||
import { Button } from "@material-ui/core";
|
||||
import Grid from "@material-ui/core/Grid";
|
||||
import Typography from "@material-ui/core/Typography";
|
||||
import ModalWrapper from "../../Common/ModalWrapper/ModalWrapper";
|
||||
import api from "../../../../common/api";
|
||||
import { serverNeedsRestart } from "../../../../actions";
|
||||
import { connect } from "react-redux";
|
||||
import ConfTargetGeneric from "../ConfTargetGeneric";
|
||||
import { fieldsConfigurations, removeEmptyFields } from "../utils";
|
||||
import { IConfigurationElement, IElementValue } from "../types";
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
errorBlock: {
|
||||
color: "red"
|
||||
},
|
||||
strongText: {
|
||||
fontWeight: 700
|
||||
},
|
||||
keyName: {
|
||||
marginLeft: 5
|
||||
},
|
||||
buttonContainer: {
|
||||
textAlign: "right"
|
||||
},
|
||||
logoButton: {
|
||||
height: "80px"
|
||||
}
|
||||
});
|
||||
|
||||
interface IAddNotificationEndpointProps {
|
||||
open: boolean;
|
||||
closeModalAndRefresh: any;
|
||||
serverNeedsRestart: typeof serverNeedsRestart;
|
||||
selectedConfiguration: IConfigurationElement;
|
||||
classes: any;
|
||||
}
|
||||
|
||||
const EditConfiguration = ({
|
||||
open,
|
||||
closeModalAndRefresh,
|
||||
serverNeedsRestart,
|
||||
selectedConfiguration,
|
||||
classes
|
||||
}: IAddNotificationEndpointProps) => {
|
||||
//Local States
|
||||
const [valuesObj, setValueObj] = useState<IElementValue[]>([]);
|
||||
const [saving, setSaving] = useState<boolean>(false);
|
||||
const [addError, setError] = useState<string>("");
|
||||
|
||||
//Effects
|
||||
|
||||
useEffect(() => {
|
||||
if (saving) {
|
||||
const payload = {
|
||||
key_values: removeEmptyFields(valuesObj)
|
||||
};
|
||||
api
|
||||
.invoke(
|
||||
"PUT",
|
||||
`/api/v1/configs/${selectedConfiguration.configuration_id}`,
|
||||
payload
|
||||
)
|
||||
.then(res => {
|
||||
setSaving(false);
|
||||
setError("");
|
||||
serverNeedsRestart(true);
|
||||
|
||||
closeModalAndRefresh();
|
||||
})
|
||||
.catch(err => {
|
||||
setSaving(false);
|
||||
setError(err);
|
||||
});
|
||||
}
|
||||
}, [
|
||||
saving,
|
||||
serverNeedsRestart,
|
||||
selectedConfiguration,
|
||||
valuesObj,
|
||||
closeModalAndRefresh
|
||||
]);
|
||||
|
||||
//Fetch Actions
|
||||
const submitForm = (event: React.FormEvent) => {
|
||||
event.preventDefault();
|
||||
setSaving(true);
|
||||
};
|
||||
|
||||
const onValueChange = useCallback(
|
||||
newValue => {
|
||||
setValueObj(newValue);
|
||||
},
|
||||
[setValueObj]
|
||||
);
|
||||
|
||||
return (
|
||||
<ModalWrapper
|
||||
modalOpen={open}
|
||||
onClose={closeModalAndRefresh}
|
||||
title={selectedConfiguration.configuration_label}
|
||||
>
|
||||
<React.Fragment>
|
||||
{addError !== "" && (
|
||||
<Grid item xs={12}>
|
||||
<Typography
|
||||
component="p"
|
||||
variant="body1"
|
||||
className={classes.errorBlock}
|
||||
>
|
||||
{addError}
|
||||
</Typography>
|
||||
</Grid>
|
||||
)}
|
||||
<form noValidate onSubmit={submitForm}>
|
||||
<ConfTargetGeneric
|
||||
fields={
|
||||
fieldsConfigurations[selectedConfiguration.configuration_id]
|
||||
}
|
||||
onChange={onValueChange}
|
||||
/>
|
||||
<Grid item xs={3} className={classes.buttonContainer}>
|
||||
<Button
|
||||
type="submit"
|
||||
variant="contained"
|
||||
color="primary"
|
||||
fullWidth
|
||||
disabled={saving}
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
</Grid>
|
||||
<Grid item xs={9} />
|
||||
</form>
|
||||
</React.Fragment>
|
||||
</ModalWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
const connector = connect(null, { serverNeedsRestart });
|
||||
|
||||
export default connector(withStyles(styles)(EditConfiguration));
|
||||
@@ -26,7 +26,8 @@ export type KVFieldType =
|
||||
| "address"
|
||||
| "duration"
|
||||
| "uri"
|
||||
| "sentence";
|
||||
| "sentence"
|
||||
| "csv";
|
||||
|
||||
export interface KVField {
|
||||
name: string;
|
||||
@@ -35,4 +36,16 @@ export interface KVField {
|
||||
required?: boolean;
|
||||
type: KVFieldType;
|
||||
options?: SelectorTypes[];
|
||||
multiline?: boolean;
|
||||
}
|
||||
|
||||
export interface IConfigurationElement {
|
||||
configuration_id: string;
|
||||
configuration_label: string;
|
||||
url?: string;
|
||||
}
|
||||
|
||||
export interface IElementValue {
|
||||
key: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
771
portal-ui/src/screens/Console/Configurations/utils.ts
Normal file
771
portal-ui/src/screens/Console/Configurations/utils.ts
Normal file
@@ -0,0 +1,771 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2020 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
import { IConfigurationElement, IElementValue } from "./types";
|
||||
|
||||
export const notifyPostgres = "notify_postgres";
|
||||
export const notifyMysql = "notify_mysql";
|
||||
export const notifyKafka = "notify_kafka";
|
||||
export const notifyAmqp = "notify_amqp";
|
||||
export const notifyMqtt = "notify_mqtt";
|
||||
export const notifyRedis = "notify_redis";
|
||||
export const notifyNats = "notify_nats";
|
||||
export const notifyElasticsearch = "notify_elasticsearch";
|
||||
export const notifyWebhooks = "notify_webhooks";
|
||||
export const notifyNsq = "notify_nsq";
|
||||
|
||||
export const configurationElements: IConfigurationElement[] = [
|
||||
{ configuration_id: "region", configuration_label: "Region Configuration" },
|
||||
{ configuration_id: "cache", configuration_label: "Cache Configuration" },
|
||||
{
|
||||
configuration_id: "compression",
|
||||
configuration_label: "Compression Configuration"
|
||||
},
|
||||
{ configuration_id: "etcd", configuration_label: "Etcd Configuration" },
|
||||
{
|
||||
configuration_id: "identity_openid",
|
||||
configuration_label: "Identity Openid Configuration"
|
||||
},
|
||||
{
|
||||
configuration_id: "identity_ldap",
|
||||
configuration_label: "Identity LDAP Configuration"
|
||||
},
|
||||
{
|
||||
configuration_id: "policy_opa",
|
||||
configuration_label: "Policy OPA Configuration"
|
||||
},
|
||||
{
|
||||
configuration_id: "kms_vault",
|
||||
configuration_label: "KMS Vault Configuration"
|
||||
},
|
||||
{ configuration_id: "kms_kes", configuration_label: "KMS KES Configuration" },
|
||||
{
|
||||
configuration_id: "logger_webhook",
|
||||
configuration_label: "Logger Webhook Configuration",
|
||||
url: "/webhook/logger"
|
||||
},
|
||||
{
|
||||
configuration_id: "audit_webhook",
|
||||
configuration_label: "Audit Webhook Configuration",
|
||||
url: "/webhook/audit"
|
||||
}
|
||||
];
|
||||
|
||||
export const fieldsConfigurations: any = {
|
||||
region: [
|
||||
{
|
||||
name: "name",
|
||||
required: true,
|
||||
label: "name",
|
||||
tooltip: 'Name of the location of the server e.g. "us-west-rack2"',
|
||||
type: "string"
|
||||
},
|
||||
{
|
||||
name: "comment",
|
||||
required: false,
|
||||
label: "comment",
|
||||
tooltip: "You can add a comment to this setting",
|
||||
type: "string",
|
||||
multiline: true
|
||||
}
|
||||
],
|
||||
cache: [
|
||||
{
|
||||
name: "drives",
|
||||
required: true,
|
||||
label: "Drives",
|
||||
tooltip:
|
||||
'Mountpoints e.g. "/optane1" or "/optane2", you can write one per field',
|
||||
type: "csv"
|
||||
},
|
||||
{
|
||||
name: "expiry",
|
||||
required: false,
|
||||
label: "Expiry",
|
||||
tooltip: 'Cache expiry duration in days e.g. "90"',
|
||||
type: "number"
|
||||
},
|
||||
{
|
||||
name: "quota",
|
||||
required: false,
|
||||
label: "Quota",
|
||||
tooltip: 'Limit cache drive usage in percentage e.g. "90"',
|
||||
type: "number"
|
||||
},
|
||||
{
|
||||
name: "exclude",
|
||||
required: false,
|
||||
label: "Exclude",
|
||||
tooltip:
|
||||
'Wildcard exclusion patterns e.g. "bucket/*.tmp" or "*.exe", you can write one per field',
|
||||
type: "csv"
|
||||
},
|
||||
{
|
||||
name: "after",
|
||||
required: false,
|
||||
label: "After",
|
||||
tooltip: "Minimum number of access before caching an object",
|
||||
type: "number"
|
||||
},
|
||||
{
|
||||
name: "comment",
|
||||
required: false,
|
||||
label: "Comment",
|
||||
tooltip: "You can add a comment to this setting",
|
||||
type: "string",
|
||||
multiline: true
|
||||
}
|
||||
],
|
||||
compression: [
|
||||
{
|
||||
name: "minio_compress",
|
||||
required: true,
|
||||
label: "MinIO Compress",
|
||||
type: "on|off"
|
||||
},
|
||||
{
|
||||
name: "extensions",
|
||||
required: false,
|
||||
label: "Extensions",
|
||||
tooltip:
|
||||
'Extensions to compress e.g. ".txt",".log" or ".csv", you can write one per field',
|
||||
type: "csv"
|
||||
},
|
||||
{
|
||||
name: "mime_types",
|
||||
required: false,
|
||||
label: "Mime Types",
|
||||
tooltip:
|
||||
'Mime types e.g. "text/*","application/json" or "application/xml", you can write one per field',
|
||||
type: "csv"
|
||||
}
|
||||
],
|
||||
etcd: [
|
||||
{
|
||||
name: "endpoints",
|
||||
required: true,
|
||||
label: "Endpoints",
|
||||
tooltip:
|
||||
'List of etcd endpoints e.g. "http://localhost:2379", you can write one per field',
|
||||
type: "csv"
|
||||
},
|
||||
{
|
||||
name: "path_prefix",
|
||||
required: false,
|
||||
label: "Path Prefix",
|
||||
tooltip: 'namespace prefix to isolate tenants e.g. "customer1/"',
|
||||
type: "string"
|
||||
},
|
||||
{
|
||||
name: "coredns_path",
|
||||
required: false,
|
||||
label: "Coredns Path",
|
||||
tooltip: 'Shared bucket DNS records, default is "/skydns"',
|
||||
type: "string"
|
||||
},
|
||||
{
|
||||
name: "client_cert",
|
||||
required: false,
|
||||
label: "Client Cert",
|
||||
tooltip: "Client cert for mTLS authentication",
|
||||
type: "string"
|
||||
},
|
||||
{
|
||||
name: "client_cert_key",
|
||||
required: false,
|
||||
label: "Client Cert Key",
|
||||
tooltip: "Client cert key for mTLS authentication",
|
||||
type: "string"
|
||||
},
|
||||
{
|
||||
name: "comment",
|
||||
required: false,
|
||||
label: "Comment",
|
||||
tooltip: "You can add a comment to this setting",
|
||||
type: "string",
|
||||
multiline: true
|
||||
}
|
||||
],
|
||||
identity_openid: [
|
||||
{
|
||||
name: "client_id",
|
||||
required: false,
|
||||
label: "Client ID",
|
||||
type: "string"
|
||||
},
|
||||
{
|
||||
name: "config_url",
|
||||
required: false,
|
||||
label: "Config URL",
|
||||
tooltip: "Config URL for Client ID configuration",
|
||||
type: "string"
|
||||
}
|
||||
],
|
||||
identity_ldap: [
|
||||
{
|
||||
name: "server_addr",
|
||||
required: true,
|
||||
label: "Server ADDR",
|
||||
tooltip: 'AD/LDAP server address e.g. "myldapserver.com:636"',
|
||||
type: "string"
|
||||
},
|
||||
{
|
||||
name: "username_format",
|
||||
required: true,
|
||||
label: "Username Format",
|
||||
tooltip:
|
||||
'List of username bind DNs e.g. "uid=%s","cn=accounts","dc=myldapserver" or "dc=com", you can write one per field',
|
||||
type: "csv"
|
||||
},
|
||||
{
|
||||
name: "username_search_filter",
|
||||
required: true,
|
||||
label: "Username Search Filter",
|
||||
tooltip:
|
||||
'User search filter, for example "(cn=%s)" or "(sAMAccountName=%s)" or "(uid=%s)"',
|
||||
type: "string"
|
||||
},
|
||||
{
|
||||
name: "group_search_filter",
|
||||
required: true,
|
||||
label: "Group Search Filter",
|
||||
tooltip:
|
||||
'Search filter for groups e.g. "(&(objectclass=groupOfNames)(memberUid=%s))"',
|
||||
type: "string"
|
||||
},
|
||||
{
|
||||
name: "username_search_base_dn",
|
||||
required: false,
|
||||
label: "Username Search Base DN",
|
||||
tooltip: "List of username search DNs, you can write one per field",
|
||||
type: "csv"
|
||||
},
|
||||
{
|
||||
name: "group_name_attribute",
|
||||
required: false,
|
||||
label: "Group Name Attribute",
|
||||
tooltip: 'Search attribute for group name e.g. "cn"',
|
||||
type: "string"
|
||||
},
|
||||
{
|
||||
name: "sts_expiry",
|
||||
required: false,
|
||||
label: "STS Expiry",
|
||||
tooltip:
|
||||
'temporary credentials validity duration in s,m,h,d. Default is "1h"',
|
||||
type: "string"
|
||||
},
|
||||
{
|
||||
name: "tls_skip_verify",
|
||||
required: false,
|
||||
label: "TLS Skip Verify",
|
||||
tooltip:
|
||||
'Trust server TLS without verification, defaults to "off" (verify)',
|
||||
type: "on|off"
|
||||
},
|
||||
{
|
||||
name: "server_insecure",
|
||||
required: false,
|
||||
label: "Server Insecure",
|
||||
tooltip:
|
||||
'Allow plain text connection to AD/LDAP server, defaults to "off"',
|
||||
type: "on|off"
|
||||
},
|
||||
{
|
||||
name: "comment",
|
||||
required: false,
|
||||
label: "Comment",
|
||||
tooltip: "Optionally add a comment to this setting",
|
||||
type: "string",
|
||||
multiline: true
|
||||
}
|
||||
],
|
||||
policy_opa: [
|
||||
{
|
||||
name: "opa_url",
|
||||
required: true,
|
||||
label: "OPA URL",
|
||||
type: "string"
|
||||
}
|
||||
],
|
||||
kms_vault: [],
|
||||
kms_kes: [],
|
||||
logger_webhook: [
|
||||
{
|
||||
name: "name",
|
||||
required: true,
|
||||
label: "Name",
|
||||
tooltip: "Name of the webhook",
|
||||
type: "string"
|
||||
},
|
||||
{
|
||||
name: "auth_token",
|
||||
required: true,
|
||||
label: "Auth Token",
|
||||
type: "string"
|
||||
},
|
||||
{
|
||||
name: "endpoint",
|
||||
required: true,
|
||||
label: "Endpoint",
|
||||
type: "string"
|
||||
}
|
||||
],
|
||||
audit_webhook: [
|
||||
{
|
||||
name: "name",
|
||||
required: true,
|
||||
label: "Name",
|
||||
tooltip: "Name of the webhook",
|
||||
type: "string"
|
||||
},
|
||||
{
|
||||
name: "auth_token",
|
||||
required: true,
|
||||
label: "Auth Token",
|
||||
type: "string"
|
||||
},
|
||||
{
|
||||
name: "endpoint",
|
||||
required: true,
|
||||
label: "Endpoint",
|
||||
type: "string"
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
const commonFields = [
|
||||
{
|
||||
name: "queue-dir",
|
||||
label: "Queue Dir",
|
||||
required: true,
|
||||
|
||||
tooltip: "staging dir for undelivered messages e.g. '/home/events'",
|
||||
type: "string"
|
||||
},
|
||||
{
|
||||
name: "queue-limit",
|
||||
label: "Queue Limit",
|
||||
required: false,
|
||||
|
||||
tooltip: "maximum limit for undelivered messages, defaults to '10000'",
|
||||
type: "number"
|
||||
},
|
||||
{
|
||||
name: "comment",
|
||||
label: "Comment",
|
||||
required: false,
|
||||
type: "string",
|
||||
multiline: true
|
||||
}
|
||||
];
|
||||
|
||||
export const notificationEndpointsFields: any = {
|
||||
[notifyKafka]: [
|
||||
{
|
||||
name: "brokers",
|
||||
label: "Brokers",
|
||||
required: true,
|
||||
|
||||
tooltip: "comma separated list of Kafka broker addresses",
|
||||
type: "string"
|
||||
},
|
||||
{
|
||||
name: "topic",
|
||||
label: "Topic",
|
||||
tooltip: "Kafka topic used for bucket notifications",
|
||||
type: "string"
|
||||
},
|
||||
{
|
||||
name: "sasl_username",
|
||||
label: "SASL Username",
|
||||
tooltip: "username for SASL/PLAIN or SASL/SCRAM authentication",
|
||||
type: "string"
|
||||
},
|
||||
{
|
||||
name: "sasl_password",
|
||||
label: "SASL Password",
|
||||
tooltip: "password for SASL/PLAIN or SASL/SCRAM authentication",
|
||||
type: "string"
|
||||
},
|
||||
{
|
||||
name: "sasl_mechanism",
|
||||
label: "SASL Mechanism",
|
||||
tooltip: "sasl authentication mechanism, default 'PLAIN'",
|
||||
type: "string"
|
||||
},
|
||||
{
|
||||
name: "tls_client_auth",
|
||||
label: "TLS Client Auth",
|
||||
tooltip:
|
||||
"clientAuth determines the Kafka server's policy for TLS client auth",
|
||||
type: "string"
|
||||
},
|
||||
{
|
||||
name: "sasl",
|
||||
label: "SASL",
|
||||
tooltip: "set to 'on' to enable SASL authentication",
|
||||
type: "on|off"
|
||||
},
|
||||
{
|
||||
name: "tls",
|
||||
label: "TLS",
|
||||
tooltip: "set to 'on' to enable TLS",
|
||||
type: "on|off"
|
||||
},
|
||||
{
|
||||
name: "tls_skip_verify",
|
||||
label: "TLS skip verify",
|
||||
tooltip:
|
||||
'trust server TLS without verification, defaults to "on" (verify)',
|
||||
type: "on|off"
|
||||
},
|
||||
{
|
||||
name: "client_tls_cert",
|
||||
label: "client TLS cert",
|
||||
tooltip: "path to client certificate for mTLS auth",
|
||||
type: "path"
|
||||
},
|
||||
{
|
||||
name: "client_tls_key",
|
||||
label: "client TLS key",
|
||||
tooltip: "path to client key for mTLS auth",
|
||||
type: "path"
|
||||
},
|
||||
{
|
||||
name: "version",
|
||||
label: "Version",
|
||||
tooltip: "specify the version of the Kafka cluster e.g '2.2.0'",
|
||||
type: "string"
|
||||
},
|
||||
...commonFields
|
||||
],
|
||||
[notifyAmqp]: [
|
||||
{
|
||||
name: "url",
|
||||
required: true,
|
||||
label: "url",
|
||||
tooltip:
|
||||
"AMQP server endpoint e.g. `amqp://myuser:mypassword@localhost:5672`",
|
||||
type: "url"
|
||||
},
|
||||
{
|
||||
name: "exchange",
|
||||
label: "exchange",
|
||||
tooltip: "name of the AMQP exchange",
|
||||
type: "string"
|
||||
},
|
||||
{
|
||||
name: "exchange_type",
|
||||
label: "exchange_type",
|
||||
tooltip: "AMQP exchange type",
|
||||
type: "string"
|
||||
},
|
||||
{
|
||||
name: "routing_key",
|
||||
label: "routing_key",
|
||||
tooltip: "routing key for publishing",
|
||||
type: "string"
|
||||
},
|
||||
{
|
||||
name: "mandatory",
|
||||
label: "mandatory",
|
||||
tooltip:
|
||||
"quietly ignore undelivered messages when set to 'off', default is 'on'",
|
||||
type: "on|off"
|
||||
},
|
||||
{
|
||||
name: "durable",
|
||||
label: "durable",
|
||||
tooltip:
|
||||
"persist queue across broker restarts when set to 'on', default is 'off'",
|
||||
type: "on|off"
|
||||
},
|
||||
{
|
||||
name: "no_wait",
|
||||
label: "no_wait",
|
||||
tooltip:
|
||||
"non-blocking message delivery when set to 'on', default is 'off'",
|
||||
type: "on|off"
|
||||
},
|
||||
{
|
||||
name: "internal",
|
||||
label: "internal",
|
||||
tooltip:
|
||||
"set to 'on' for exchange to be not used directly by publishers, but only when bound to other exchanges",
|
||||
type: "on|off"
|
||||
},
|
||||
{
|
||||
name: "auto_deleted",
|
||||
label: "auto_deleted",
|
||||
tooltip:
|
||||
"auto delete queue when set to 'on', when there are no consumers",
|
||||
type: "on|off"
|
||||
},
|
||||
{
|
||||
name: "delivery_mode",
|
||||
label: "delivery_mode",
|
||||
tooltip: "set to '1' for non-persistent or '2' for persistent queue",
|
||||
type: "number"
|
||||
},
|
||||
...commonFields
|
||||
],
|
||||
[notifyRedis]: [
|
||||
{
|
||||
name: "address",
|
||||
required: true,
|
||||
label: "address",
|
||||
tooltip: "Redis server's address. For example: `localhost:6379`",
|
||||
type: "address"
|
||||
},
|
||||
{
|
||||
name: "key",
|
||||
required: true,
|
||||
label: "key",
|
||||
tooltip: "Redis key to store/update events, key is auto-created",
|
||||
type: "string"
|
||||
},
|
||||
{
|
||||
name: "password",
|
||||
label: "password",
|
||||
tooltip: "Redis server password",
|
||||
type: "string"
|
||||
},
|
||||
...commonFields
|
||||
],
|
||||
[notifyMqtt]: [
|
||||
{
|
||||
name: "broker",
|
||||
required: true,
|
||||
label: "broker",
|
||||
tooltip: "MQTT server endpoint e.g. `tcp://localhost:1883`",
|
||||
type: "uri"
|
||||
},
|
||||
{
|
||||
name: "topic",
|
||||
required: true,
|
||||
label: "topic",
|
||||
tooltip: "name of the MQTT topic to publish",
|
||||
type: "string"
|
||||
},
|
||||
{
|
||||
name: "username",
|
||||
label: "username",
|
||||
tooltip: "MQTT username",
|
||||
type: "string"
|
||||
},
|
||||
{
|
||||
name: "password",
|
||||
label: "password",
|
||||
tooltip: "MQTT password",
|
||||
type: "string"
|
||||
},
|
||||
{
|
||||
name: "qos",
|
||||
label: "qos",
|
||||
tooltip: "set the quality of service priority, defaults to '0'",
|
||||
type: "number"
|
||||
},
|
||||
{
|
||||
name: "keep_alive_interval",
|
||||
label: "keep_alive_interval",
|
||||
tooltip: "keep-alive interval for MQTT connections in s,m,h,d",
|
||||
type: "duration"
|
||||
},
|
||||
{
|
||||
name: "reconnect_interval",
|
||||
label: "reconnect_interval",
|
||||
tooltip: "reconnect interval for MQTT connections in s,m,h,d",
|
||||
type: "duration"
|
||||
},
|
||||
...commonFields
|
||||
],
|
||||
[notifyNats]: [
|
||||
{
|
||||
name: "address",
|
||||
required: true,
|
||||
label: "address",
|
||||
tooltip: "NATS server address e.g. '0.0.0.0:4222'",
|
||||
type: "address"
|
||||
},
|
||||
{
|
||||
name: "subject",
|
||||
required: true,
|
||||
label: "subject",
|
||||
tooltip: "NATS subscription subject",
|
||||
type: "string"
|
||||
},
|
||||
{
|
||||
name: "username",
|
||||
label: "username",
|
||||
tooltip: "NATS username",
|
||||
type: "string"
|
||||
},
|
||||
{
|
||||
name: "password",
|
||||
label: "password",
|
||||
tooltip: "NATS password",
|
||||
type: "string"
|
||||
},
|
||||
{
|
||||
name: "token",
|
||||
label: "token",
|
||||
tooltip: "NATS token",
|
||||
type: "string"
|
||||
},
|
||||
{
|
||||
name: "tls",
|
||||
label: "tls",
|
||||
tooltip: "set to 'on' to enable TLS",
|
||||
type: "on|off"
|
||||
},
|
||||
{
|
||||
name: "tls_skip_verify",
|
||||
label: "tls_skip_verify",
|
||||
tooltip:
|
||||
'trust server TLS without verification, defaults to "on" (verify)',
|
||||
type: "on|off"
|
||||
},
|
||||
{
|
||||
name: "ping_interval",
|
||||
label: "ping_interval",
|
||||
tooltip: "client ping commands interval in s,m,h,d. Disabled by default",
|
||||
type: "duration"
|
||||
},
|
||||
{
|
||||
name: "streaming",
|
||||
label: "streaming",
|
||||
tooltip: "set to 'on', to use streaming NATS server",
|
||||
type: "on|off"
|
||||
},
|
||||
{
|
||||
name: "streaming_async",
|
||||
label: "streaming_async",
|
||||
tooltip: "set to 'on', to enable asynchronous publish",
|
||||
type: "on|off"
|
||||
},
|
||||
{
|
||||
name: "streaming_max_pub_acks_in_flight",
|
||||
label: "streaming_max_pub_acks_in_flight",
|
||||
tooltip: "number of messages to publish without waiting for ACKs",
|
||||
type: "number"
|
||||
},
|
||||
{
|
||||
name: "streaming_cluster_id",
|
||||
label: "streaming_cluster_id",
|
||||
tooltip: "unique ID for NATS streaming cluster",
|
||||
type: "string"
|
||||
},
|
||||
{
|
||||
name: "cert_authority",
|
||||
label: "cert_authority",
|
||||
tooltip: "path to certificate chain of the target NATS server",
|
||||
type: "string"
|
||||
},
|
||||
{
|
||||
name: "client_cert",
|
||||
label: "client_cert",
|
||||
tooltip: "client cert for NATS mTLS auth",
|
||||
type: "string"
|
||||
},
|
||||
{
|
||||
name: "client_key",
|
||||
label: "client_key",
|
||||
tooltip: "client cert key for NATS mTLS auth",
|
||||
type: "string"
|
||||
},
|
||||
...commonFields
|
||||
],
|
||||
[notifyElasticsearch]: [
|
||||
{
|
||||
name: "url",
|
||||
required: true,
|
||||
label: "url",
|
||||
tooltip:
|
||||
"Elasticsearch server's address, with optional authentication info",
|
||||
type: "url"
|
||||
},
|
||||
{
|
||||
name: "index",
|
||||
required: true,
|
||||
label: "index",
|
||||
tooltip:
|
||||
"Elasticsearch index to store/update events, index is auto-created",
|
||||
type: "string"
|
||||
},
|
||||
{
|
||||
name: "format",
|
||||
required: true,
|
||||
label: "format",
|
||||
tooltip:
|
||||
"'namespace' reflects current bucket/object list and 'access' reflects a journal of object operations, defaults to 'namespace'",
|
||||
type: "enum"
|
||||
},
|
||||
...commonFields
|
||||
],
|
||||
[notifyWebhooks]: [
|
||||
{
|
||||
name: "endpoint",
|
||||
required: true,
|
||||
label: "endpoint",
|
||||
tooltip:
|
||||
"webhook server endpoint e.g. http://localhost:8080/minio/events",
|
||||
type: "url"
|
||||
},
|
||||
{
|
||||
name: "auth_token",
|
||||
label: "auth_token",
|
||||
tooltip: "opaque string or JWT authorization token",
|
||||
type: "string"
|
||||
},
|
||||
...commonFields
|
||||
],
|
||||
[notifyNsq]: [
|
||||
{
|
||||
name: "nsqd_address",
|
||||
required: true,
|
||||
label: "nsqd_address",
|
||||
tooltip: "NSQ server address e.g. '127.0.0.1:4150'",
|
||||
type: "address"
|
||||
},
|
||||
{
|
||||
name: "topic",
|
||||
required: true,
|
||||
label: "topic",
|
||||
tooltip: "NSQ topic",
|
||||
type: "string"
|
||||
},
|
||||
{
|
||||
name: "tls",
|
||||
label: "tls",
|
||||
tooltip: "set to 'on' to enable TLS",
|
||||
type: "on|off"
|
||||
},
|
||||
{
|
||||
name: "tls_skip_verify",
|
||||
label: "tls_skip_verify",
|
||||
tooltip:
|
||||
'trust server TLS without verification, defaults to "on" (verify)',
|
||||
type: "on|off"
|
||||
},
|
||||
...commonFields
|
||||
]
|
||||
};
|
||||
|
||||
export const removeEmptyFields = (formFields: IElementValue[]) => {
|
||||
const nonEmptyFields = formFields.filter(field => field.value !== "");
|
||||
|
||||
return nonEmptyFields;
|
||||
};
|
||||
@@ -58,7 +58,9 @@ import ServiceAccounts from "./ServiceAccounts/ServiceAccounts";
|
||||
import Users from "./Users/Users";
|
||||
import Groups from "./Groups/Groups";
|
||||
import ListNotificationEndpoints from "./NotificationEndopoints/ListNotificationEndpoints";
|
||||
import ConfigurationsList from "./Configurations/ConfigurationPanels/ConfigurationsList";
|
||||
import { Button, LinearProgress } from "@material-ui/core";
|
||||
import WebhookPanel from "./Configurations/ConfigurationPanels/WebhookPanel";
|
||||
|
||||
function Copyright() {
|
||||
return (
|
||||
@@ -289,10 +291,17 @@ class Console extends React.Component<
|
||||
<Route exact path="/dashboard" component={Dashboard} />
|
||||
<Route exct path="/groups" component={Groups} />
|
||||
<Route
|
||||
exct
|
||||
exact
|
||||
path="/notification-endpoints"
|
||||
component={ListNotificationEndpoints}
|
||||
/>
|
||||
<Route
|
||||
exact
|
||||
path="/configurations-list"
|
||||
component={ConfigurationsList}
|
||||
/>
|
||||
<Route exact path="/webhook/logger" component={WebhookPanel} />
|
||||
<Route exact path="/webhook/audit" component={WebhookPanel} />
|
||||
<Route exact path="/">
|
||||
<Redirect to="/dashboard" />
|
||||
</Route>
|
||||
|
||||
@@ -38,6 +38,7 @@ import { createStyles, Theme } from "@material-ui/core/styles";
|
||||
import PersonIcon from "@material-ui/icons/Person";
|
||||
import api from "../../common/api";
|
||||
import NotificationsIcon from "@material-ui/icons/Notifications";
|
||||
import ListAltIcon from "@material-ui/icons/ListAlt";
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
@@ -156,6 +157,12 @@ class Menu extends React.Component<MenuProps> {
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="Lambda Notifications" />
|
||||
</ListItem>
|
||||
<ListItem button component={NavLink} to="/configurations-list">
|
||||
<ListItemIcon>
|
||||
<ListAltIcon />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="Configurations List" />
|
||||
</ListItem>
|
||||
<Divider />
|
||||
<ListItem
|
||||
button
|
||||
|
||||
@@ -15,18 +15,33 @@
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React, { useCallback, useEffect, useState } from "react";
|
||||
import get from "lodash/get";
|
||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||
import { Button, LinearProgress } from "@material-ui/core";
|
||||
import Grid from "@material-ui/core/Grid";
|
||||
import Typography from "@material-ui/core/Typography";
|
||||
import ModalWrapper from "../Common/ModalWrapper/ModalWrapper";
|
||||
import ConfPostgres from "../Configurations/ConfPostgres";
|
||||
import ConfPostgres from "../Configurations/CustomForms/ConfPostgres";
|
||||
import api from "../../../common/api";
|
||||
import { serverNeedsRestart } from "../../../actions";
|
||||
import { connect } from "react-redux";
|
||||
import ConfMySql from "../Configurations/ConfMySql";
|
||||
import ConfMySql from "../Configurations/CustomForms/ConfMySql";
|
||||
import ConfTargetGeneric from "../Configurations/ConfTargetGeneric";
|
||||
import { KVField } from "../Configurations/types";
|
||||
import {
|
||||
notificationEndpointsFields,
|
||||
notifyPostgres,
|
||||
notifyMysql,
|
||||
notifyKafka,
|
||||
notifyAmqp,
|
||||
notifyMqtt,
|
||||
notifyRedis,
|
||||
notifyNats,
|
||||
notifyElasticsearch,
|
||||
notifyWebhooks,
|
||||
notifyNsq,
|
||||
removeEmptyFields
|
||||
} from "../Configurations/utils";
|
||||
import { IElementValue } from "../Configurations/types";
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
@@ -47,17 +62,6 @@ const styles = (theme: Theme) =>
|
||||
}
|
||||
});
|
||||
|
||||
const notifyPostgres = "notify_postgres";
|
||||
const notifyMysql = "notify_mysql";
|
||||
const notifyKafka = "notify_kafka";
|
||||
const notifyAmqp = "notify_amqp";
|
||||
const notifyMqtt = "notify_mqtt";
|
||||
const notifyRedis = "notify_redis";
|
||||
const notifyNats = "notify_nats";
|
||||
const notifyElasticsearch = "notify_elasticsearch";
|
||||
const notifyWebhooks = "notify_webhooks";
|
||||
const notifyNsq = "notify_nsq";
|
||||
|
||||
interface IAddNotificationEndpointProps {
|
||||
open: boolean;
|
||||
closeModalAndRefresh: any;
|
||||
@@ -73,7 +77,7 @@ const AddNotificationEndpoint = ({
|
||||
}: IAddNotificationEndpointProps) => {
|
||||
//Local States
|
||||
const [service, setService] = useState<string>("");
|
||||
const [valuesObj, setValueObj] = useState<Map<string, string>>(new Map());
|
||||
const [valuesArr, setValueArr] = useState<IElementValue[]>([]);
|
||||
const [saving, setSaving] = useState<boolean>(false);
|
||||
const [addError, setError] = useState<string>("");
|
||||
|
||||
@@ -81,13 +85,8 @@ const AddNotificationEndpoint = ({
|
||||
|
||||
useEffect(() => {
|
||||
if (saving) {
|
||||
let keyValues: Array<object> = new Array<object>();
|
||||
valuesObj.forEach((value: string, key: string) => {
|
||||
keyValues.push({ key: key, value: value });
|
||||
});
|
||||
|
||||
let payload = {
|
||||
key_values: keyValues
|
||||
const payload = {
|
||||
key_values: removeEmptyFields(valuesArr)
|
||||
};
|
||||
api
|
||||
.invoke("PUT", `/api/v1/configs/${service}`, payload)
|
||||
@@ -103,7 +102,7 @@ const AddNotificationEndpoint = ({
|
||||
setError(err);
|
||||
});
|
||||
}
|
||||
}, [saving, serverNeedsRestart, service, valuesObj, closeModalAndRefresh]);
|
||||
}, [saving, serverNeedsRestart, service, valuesArr, closeModalAndRefresh]);
|
||||
|
||||
//Fetch Actions
|
||||
const submitForm = (event: React.FormEvent) => {
|
||||
@@ -113,9 +112,9 @@ const AddNotificationEndpoint = ({
|
||||
|
||||
const onValueChange = useCallback(
|
||||
newValue => {
|
||||
setValueObj(newValue);
|
||||
setValueArr(newValue);
|
||||
},
|
||||
[setValueObj]
|
||||
[setValueArr]
|
||||
);
|
||||
|
||||
let srvComponent = <React.Fragment />;
|
||||
@@ -128,444 +127,12 @@ const AddNotificationEndpoint = ({
|
||||
srvComponent = <ConfMySql onChange={onValueChange} />;
|
||||
break;
|
||||
}
|
||||
case notifyKafka: {
|
||||
const fields: KVField[] = [
|
||||
{
|
||||
name: "brokers",
|
||||
label: "Brokers",
|
||||
required: true,
|
||||
|
||||
tooltip: "comma separated list of Kafka broker addresses",
|
||||
type: "string"
|
||||
},
|
||||
{
|
||||
name: "topic",
|
||||
label: "Topic",
|
||||
tooltip: "Kafka topic used for bucket notifications",
|
||||
type: "string"
|
||||
},
|
||||
{
|
||||
name: "sasl_username",
|
||||
label: "SASL Username",
|
||||
tooltip: "username for SASL/PLAIN or SASL/SCRAM authentication",
|
||||
type: "string"
|
||||
},
|
||||
{
|
||||
name: "sasl_password",
|
||||
label: "SASL Password",
|
||||
tooltip: "password for SASL/PLAIN or SASL/SCRAM authentication",
|
||||
type: "string"
|
||||
},
|
||||
{
|
||||
name: "sasl_mechanism",
|
||||
label: "SASL Mechanism",
|
||||
tooltip: "sasl authentication mechanism, default 'PLAIN'",
|
||||
type: "string"
|
||||
},
|
||||
{
|
||||
name: "tls_client_auth",
|
||||
label: "TLS Client Auth",
|
||||
tooltip:
|
||||
"clientAuth determines the Kafka server's policy for TLS client auth",
|
||||
type: "string"
|
||||
},
|
||||
{
|
||||
name: "sasl",
|
||||
label: "SASL",
|
||||
tooltip: "set to 'on' to enable SASL authentication",
|
||||
type: "on|off"
|
||||
},
|
||||
{
|
||||
name: "tls",
|
||||
label: "TLS",
|
||||
tooltip: "set to 'on' to enable TLS",
|
||||
type: "on|off"
|
||||
},
|
||||
{
|
||||
name: "tls_skip_verify",
|
||||
label: "TLS skip verify",
|
||||
tooltip:
|
||||
'trust server TLS without verification, defaults to "on" (verify)',
|
||||
type: "on|off"
|
||||
},
|
||||
{
|
||||
name: "client_tls_cert",
|
||||
label: "client TLS cert",
|
||||
tooltip: "path to client certificate for mTLS auth",
|
||||
type: "path"
|
||||
},
|
||||
{
|
||||
name: "client_tls_key",
|
||||
label: "client TLS key",
|
||||
tooltip: "path to client key for mTLS auth",
|
||||
type: "path"
|
||||
},
|
||||
{
|
||||
name: "version",
|
||||
label: "Version",
|
||||
tooltip: "specify the version of the Kafka cluster e.g '2.2.0'",
|
||||
type: "string"
|
||||
}
|
||||
];
|
||||
default: {
|
||||
const fields = get(notificationEndpointsFields, service, []);
|
||||
|
||||
srvComponent = (
|
||||
<ConfTargetGeneric fields={fields} onChange={onValueChange} />
|
||||
);
|
||||
break;
|
||||
}
|
||||
case notifyAmqp: {
|
||||
const fields: KVField[] = [
|
||||
{
|
||||
name: "url",
|
||||
required: true,
|
||||
label: "url",
|
||||
tooltip:
|
||||
"AMQP server endpoint e.g. `amqp://myuser:mypassword@localhost:5672`",
|
||||
type: "url"
|
||||
},
|
||||
{
|
||||
name: "exchange",
|
||||
label: "exchange",
|
||||
tooltip: "name of the AMQP exchange",
|
||||
type: "string"
|
||||
},
|
||||
{
|
||||
name: "exchange_type",
|
||||
label: "exchange_type",
|
||||
tooltip: "AMQP exchange type",
|
||||
type: "string"
|
||||
},
|
||||
{
|
||||
name: "routing_key",
|
||||
label: "routing_key",
|
||||
tooltip: "routing key for publishing",
|
||||
type: "string"
|
||||
},
|
||||
{
|
||||
name: "mandatory",
|
||||
label: "mandatory",
|
||||
tooltip:
|
||||
"quietly ignore undelivered messages when set to 'off', default is 'on'",
|
||||
type: "on|off"
|
||||
},
|
||||
{
|
||||
name: "durable",
|
||||
label: "durable",
|
||||
tooltip:
|
||||
"persist queue across broker restarts when set to 'on', default is 'off'",
|
||||
type: "on|off"
|
||||
},
|
||||
{
|
||||
name: "no_wait",
|
||||
label: "no_wait",
|
||||
tooltip:
|
||||
"non-blocking message delivery when set to 'on', default is 'off'",
|
||||
type: "on|off"
|
||||
},
|
||||
{
|
||||
name: "internal",
|
||||
label: "internal",
|
||||
tooltip:
|
||||
"set to 'on' for exchange to be not used directly by publishers, but only when bound to other exchanges",
|
||||
type: "on|off"
|
||||
},
|
||||
{
|
||||
name: "auto_deleted",
|
||||
label: "auto_deleted",
|
||||
tooltip:
|
||||
"auto delete queue when set to 'on', when there are no consumers",
|
||||
type: "on|off"
|
||||
},
|
||||
{
|
||||
name: "delivery_mode",
|
||||
label: "delivery_mode",
|
||||
tooltip: "set to '1' for non-persistent or '2' for persistent queue",
|
||||
type: "number"
|
||||
}
|
||||
];
|
||||
|
||||
srvComponent = (
|
||||
<ConfTargetGeneric fields={fields} onChange={onValueChange} />
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
case notifyRedis: {
|
||||
const fields: KVField[] = [
|
||||
{
|
||||
name: "address",
|
||||
required: true,
|
||||
label: "address",
|
||||
tooltip: "Redis server's address. For example: `localhost:6379`",
|
||||
type: "address"
|
||||
},
|
||||
{
|
||||
name: "key",
|
||||
required: true,
|
||||
label: "key",
|
||||
tooltip: "Redis key to store/update events, key is auto-created",
|
||||
type: "string"
|
||||
},
|
||||
{
|
||||
name: "password",
|
||||
label: "password",
|
||||
tooltip: "Redis server password",
|
||||
type: "string"
|
||||
}
|
||||
];
|
||||
|
||||
srvComponent = (
|
||||
<ConfTargetGeneric fields={fields} onChange={onValueChange} />
|
||||
);
|
||||
break;
|
||||
}
|
||||
case notifyMqtt: {
|
||||
const fields: KVField[] = [
|
||||
{
|
||||
name: "broker",
|
||||
required: true,
|
||||
label: "broker",
|
||||
tooltip: "MQTT server endpoint e.g. `tcp://localhost:1883`",
|
||||
type: "uri"
|
||||
},
|
||||
{
|
||||
name: "topic",
|
||||
required: true,
|
||||
label: "topic",
|
||||
tooltip: "name of the MQTT topic to publish",
|
||||
type: "string"
|
||||
},
|
||||
{
|
||||
name: "username",
|
||||
label: "username",
|
||||
tooltip: "MQTT username",
|
||||
type: "string"
|
||||
},
|
||||
{
|
||||
name: "password",
|
||||
label: "password",
|
||||
tooltip: "MQTT password",
|
||||
type: "string"
|
||||
},
|
||||
{
|
||||
name: "qos",
|
||||
label: "qos",
|
||||
tooltip: "set the quality of service priority, defaults to '0'",
|
||||
type: "number"
|
||||
},
|
||||
{
|
||||
name: "keep_alive_interval",
|
||||
label: "keep_alive_interval",
|
||||
tooltip: "keep-alive interval for MQTT connections in s,m,h,d",
|
||||
type: "duration"
|
||||
},
|
||||
{
|
||||
name: "reconnect_interval",
|
||||
label: "reconnect_interval",
|
||||
tooltip: "reconnect interval for MQTT connections in s,m,h,d",
|
||||
type: "duration"
|
||||
}
|
||||
];
|
||||
|
||||
srvComponent = (
|
||||
<ConfTargetGeneric fields={fields} onChange={onValueChange} />
|
||||
);
|
||||
break;
|
||||
}
|
||||
case notifyNats: {
|
||||
const fields: KVField[] = [
|
||||
{
|
||||
name: "address",
|
||||
required: true,
|
||||
label: "address",
|
||||
tooltip: "NATS server address e.g. '0.0.0.0:4222'",
|
||||
type: "address"
|
||||
},
|
||||
{
|
||||
name: "subject",
|
||||
required: true,
|
||||
label: "subject",
|
||||
tooltip: "NATS subscription subject",
|
||||
type: "string"
|
||||
},
|
||||
{
|
||||
name: "username",
|
||||
label: "username",
|
||||
tooltip: "NATS username",
|
||||
type: "string"
|
||||
},
|
||||
{
|
||||
name: "password",
|
||||
label: "password",
|
||||
tooltip: "NATS password",
|
||||
type: "string"
|
||||
},
|
||||
{
|
||||
name: "token",
|
||||
label: "token",
|
||||
tooltip: "NATS token",
|
||||
type: "string"
|
||||
},
|
||||
{
|
||||
name: "tls",
|
||||
label: "tls",
|
||||
tooltip: "set to 'on' to enable TLS",
|
||||
type: "on|off"
|
||||
},
|
||||
{
|
||||
name: "tls_skip_verify",
|
||||
label: "tls_skip_verify",
|
||||
tooltip:
|
||||
'trust server TLS without verification, defaults to "on" (verify)',
|
||||
type: "on|off"
|
||||
},
|
||||
{
|
||||
name: "ping_interval",
|
||||
label: "ping_interval",
|
||||
tooltip:
|
||||
"client ping commands interval in s,m,h,d. Disabled by default",
|
||||
type: "duration"
|
||||
},
|
||||
{
|
||||
name: "streaming",
|
||||
label: "streaming",
|
||||
tooltip: "set to 'on', to use streaming NATS server",
|
||||
type: "on|off"
|
||||
},
|
||||
{
|
||||
name: "streaming_async",
|
||||
label: "streaming_async",
|
||||
tooltip: "set to 'on', to enable asynchronous publish",
|
||||
type: "on|off"
|
||||
},
|
||||
{
|
||||
name: "streaming_max_pub_acks_in_flight",
|
||||
label: "streaming_max_pub_acks_in_flight",
|
||||
tooltip: "number of messages to publish without waiting for ACKs",
|
||||
type: "number"
|
||||
},
|
||||
{
|
||||
name: "streaming_cluster_id",
|
||||
label: "streaming_cluster_id",
|
||||
tooltip: "unique ID for NATS streaming cluster",
|
||||
type: "string"
|
||||
},
|
||||
{
|
||||
name: "cert_authority",
|
||||
label: "cert_authority",
|
||||
tooltip: "path to certificate chain of the target NATS server",
|
||||
type: "string"
|
||||
},
|
||||
{
|
||||
name: "client_cert",
|
||||
label: "client_cert",
|
||||
tooltip: "client cert for NATS mTLS auth",
|
||||
type: "string"
|
||||
},
|
||||
{
|
||||
name: "client_key",
|
||||
label: "client_key",
|
||||
tooltip: "client cert key for NATS mTLS auth",
|
||||
type: "string"
|
||||
}
|
||||
];
|
||||
|
||||
srvComponent = (
|
||||
<ConfTargetGeneric fields={fields} onChange={onValueChange} />
|
||||
);
|
||||
break;
|
||||
}
|
||||
case notifyElasticsearch: {
|
||||
const fields: KVField[] = [
|
||||
{
|
||||
name: "url",
|
||||
required: true,
|
||||
label: "url",
|
||||
tooltip:
|
||||
"Elasticsearch server's address, with optional authentication info",
|
||||
type: "url"
|
||||
},
|
||||
{
|
||||
name: "index",
|
||||
required: true,
|
||||
label: "index",
|
||||
tooltip:
|
||||
"Elasticsearch index to store/update events, index is auto-created",
|
||||
type: "string"
|
||||
},
|
||||
{
|
||||
name: "format",
|
||||
required: true,
|
||||
label: "format",
|
||||
tooltip:
|
||||
"'namespace' reflects current bucket/object list and 'access' reflects a journal of object operations, defaults to 'namespace'",
|
||||
type: "enum"
|
||||
}
|
||||
];
|
||||
|
||||
srvComponent = (
|
||||
<ConfTargetGeneric fields={fields} onChange={onValueChange} />
|
||||
);
|
||||
break;
|
||||
}
|
||||
case notifyWebhooks: {
|
||||
const fields: KVField[] = [
|
||||
{
|
||||
name: "endpoint",
|
||||
required: true,
|
||||
label: "endpoint",
|
||||
tooltip:
|
||||
"webhook server endpoint e.g. http://localhost:8080/minio/events",
|
||||
type: "url"
|
||||
},
|
||||
{
|
||||
name: "auth_token",
|
||||
label: "auth_token",
|
||||
tooltip: "opaque string or JWT authorization token",
|
||||
type: "string"
|
||||
}
|
||||
];
|
||||
|
||||
srvComponent = (
|
||||
<ConfTargetGeneric fields={fields} onChange={onValueChange} />
|
||||
);
|
||||
break;
|
||||
}
|
||||
case notifyNsq: {
|
||||
const fields: KVField[] = [
|
||||
{
|
||||
name: "nsqd_address",
|
||||
required: true,
|
||||
label: "nsqd_address",
|
||||
tooltip: "NSQ server address e.g. '127.0.0.1:4150'",
|
||||
type: "address"
|
||||
},
|
||||
{
|
||||
name: "topic",
|
||||
required: true,
|
||||
label: "topic",
|
||||
tooltip: "NSQ topic",
|
||||
type: "string"
|
||||
},
|
||||
{
|
||||
name: "tls",
|
||||
label: "tls",
|
||||
tooltip: "set to 'on' to enable TLS",
|
||||
type: "on|off"
|
||||
},
|
||||
{
|
||||
name: "tls_skip_verify",
|
||||
label: "tls_skip_verify",
|
||||
tooltip:
|
||||
'trust server TLS without verification, defaults to "on" (verify)',
|
||||
type: "on|off"
|
||||
}
|
||||
];
|
||||
|
||||
srvComponent = (
|
||||
<ConfTargetGeneric fields={fields} onChange={onValueChange} />
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -65,15 +65,15 @@ const styles = (theme: Theme) =>
|
||||
borderTopLeftRadius: "3px",
|
||||
borderBottomLeftRadius: "3px",
|
||||
background:
|
||||
"transparent linear-gradient(333deg, #281B6F 1%, #271260 13%, #120D53 83%) 0% 0% no-repeat padding-box;",
|
||||
"transparent linear-gradient(333deg, #281B6F 1%, #271260 13%, #120D53 83%) 0% 0% no-repeat padding-box;"
|
||||
},
|
||||
oceanBg: {
|
||||
backgroundImage: "url(/images/BG_Illustration.svg)",
|
||||
backgroundRepeat: "no-repeat",
|
||||
backgroundPosition: "bottom left",
|
||||
height: "100%",
|
||||
width: "100%"
|
||||
},
|
||||
oceanBg:{
|
||||
backgroundImage:'url(/images/BG_Illustration.svg)',
|
||||
backgroundRepeat:'no-repeat',
|
||||
backgroundPosition:'bottom left',
|
||||
height:'100%',
|
||||
width:'100%',
|
||||
},
|
||||
theLogin: {
|
||||
padding: "76px 62px 20px 62px"
|
||||
}
|
||||
@@ -140,7 +140,7 @@ class Login extends React.Component<LoginProps> {
|
||||
<Paper className={classes.paper}>
|
||||
<Grid container className={classes.mainContainer}>
|
||||
<Grid item xs={7} className={classes.theOcean}>
|
||||
<div className={classes.oceanBg}></div>
|
||||
<div className={classes.oceanBg}></div>
|
||||
</Grid>
|
||||
<Grid item xs={5} className={classes.theLogin}>
|
||||
<Typography component="h1" variant="h6">
|
||||
|
||||
Reference in New Issue
Block a user