github.com/minio/console@v1.4.1/web-app/src/screens/Console/EventDestinations/CustomForms/ConfPostgres.tsx (about)

     1  // This file is part of MinIO Console Server
     2  // Copyright (c) 2021 MinIO, Inc.
     3  //
     4  // This program is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Affero General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // This program is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    12  // GNU Affero General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Affero General Public License
    15  // along with this program.  If not, see <http://www.gnu.org/licenses/>.
    16  
    17  import React, { Fragment, useCallback, useEffect, useState } from "react";
    18  import {
    19    Box,
    20    CommentBox,
    21    FormLayout,
    22    Grid,
    23    InputBox,
    24    RadioGroup,
    25    ReadBox,
    26    Select,
    27    Switch,
    28  } from "mds";
    29  import { IElementValue } from "../../Configurations/types";
    30  
    31  interface IConfPostgresProps {
    32    onChange: (newValue: IElementValue[]) => void;
    33  }
    34  
    35  const ConfPostgres = ({ onChange }: IConfPostgresProps) => {
    36    //Local States
    37    const [useConnectionString, setUseConnectionString] =
    38      useState<boolean>(false);
    39    const [connectionString, setConnectionString] = useState<string>("");
    40    const [host, setHostname] = useState<string>("");
    41    const [dbName, setDbName] = useState<string>("");
    42    const [port, setPort] = useState<string>("");
    43    const [user, setUser] = useState<string>("");
    44    const [password, setPassword] = useState<string>("");
    45    const [sslMode, setSslMode] = useState<string>(" ");
    46  
    47    const [table, setTable] = useState<string>("");
    48    const [format, setFormat] = useState<string>("namespace");
    49    const [queueDir, setQueueDir] = useState<string>("");
    50    const [queueLimit, setQueueLimit] = useState<string>("");
    51    const [comment, setComment] = useState<string>("");
    52  
    53    // connection_string*  (string)             Postgres server connection-string e.g. "host=localhost port=5432 dbname=minio_events user=postgres password=password sslmode=disable"
    54  
    55    //  "host=localhost
    56    // port=5432
    57    //dbname=minio_events
    58    //user=postgres
    59    //password=password
    60    //sslmode=disable"
    61  
    62    // table*              (string)             DB table name to store/update events, table is auto-created
    63    // format*             (namespace*|access)  'namespace' reflects current bucket/object list and 'access' reflects a journal of object operations, defaults to 'namespace'
    64    // queue_dir           (path)               staging dir for undelivered messages e.g. '/home/events'
    65    // queue_limit         (number)             maximum limit for undelivered messages, defaults to '10000'
    66    // comment             (sentence)           optionally add a comment to this setting
    67  
    68    const KvSeparator = "=";
    69    const parseConnectionString = (
    70      input: string,
    71      keys: string[],
    72    ): Map<string, string> => {
    73      let valueIndexes: number[] = [];
    74  
    75      for (const key of keys) {
    76        const i = input.indexOf(key + KvSeparator);
    77        if (i === -1) {
    78          continue;
    79        }
    80        valueIndexes.push(i);
    81      }
    82      valueIndexes.sort((n1, n2) => n1 - n2);
    83  
    84      let kvFields = new Map<string, string>();
    85      let fields: string[] = new Array<string>(valueIndexes.length);
    86      for (let i = 0; i < valueIndexes.length; i++) {
    87        const j = i + 1;
    88        if (j < valueIndexes.length) {
    89          fields[i] = input.slice(valueIndexes[i], valueIndexes[j]);
    90        } else {
    91          fields[i] = input.slice(valueIndexes[i]);
    92        }
    93      }
    94  
    95      for (let field of fields) {
    96        if (field === undefined) {
    97          continue;
    98        }
    99        const key = field.slice(0, field.indexOf("="));
   100        const value = field.slice(field.indexOf("=") + 1).trim();
   101        kvFields.set(key, value);
   102      }
   103      return kvFields;
   104    };
   105  
   106    const configToString = useCallback((): string => {
   107      let strValue = "";
   108      if (host !== "") {
   109        strValue = `${strValue} host=${host}`;
   110      }
   111      if (dbName !== "") {
   112        strValue = `${strValue} dbname=${dbName}`;
   113      }
   114      if (user !== "") {
   115        strValue = `${strValue} user=${user}`;
   116      }
   117      if (password !== "") {
   118        strValue = `${strValue} password=${password}`;
   119      }
   120      if (port !== "") {
   121        strValue = `${strValue} port=${port}`;
   122      }
   123      if (sslMode !== " ") {
   124        strValue = `${strValue} sslmode=${sslMode}`;
   125      }
   126  
   127      strValue = `${strValue} `;
   128  
   129      return strValue.trim();
   130    }, [host, dbName, user, password, port, sslMode]);
   131  
   132    useEffect(() => {
   133      if (connectionString !== "") {
   134        const formValues = [
   135          { key: "connection_string", value: connectionString },
   136          { key: "table", value: table },
   137          { key: "format", value: format },
   138          { key: "queue_dir", value: queueDir },
   139          { key: "queue_limit", value: queueLimit },
   140          { key: "comment", value: comment },
   141        ];
   142  
   143        onChange(formValues);
   144      }
   145    }, [
   146      connectionString,
   147      table,
   148      format,
   149      queueDir,
   150      queueLimit,
   151      comment,
   152      onChange,
   153    ]);
   154  
   155    useEffect(() => {
   156      const cs = configToString();
   157      setConnectionString(cs);
   158    }, [
   159      user,
   160      dbName,
   161      password,
   162      port,
   163      sslMode,
   164      host,
   165      setConnectionString,
   166      configToString,
   167    ]);
   168  
   169    useEffect(() => {
   170      if (useConnectionString) {
   171        // build connection_string
   172        const cs = configToString();
   173        setConnectionString(cs);
   174  
   175        return;
   176      }
   177      // parse connection_string
   178      const kv = parseConnectionString(connectionString, [
   179        "host",
   180        "port",
   181        "dbname",
   182        "user",
   183        "password",
   184        "sslmode",
   185      ]);
   186      setHostname(kv.get("host") ? kv.get("host") + "" : "");
   187      setPort(kv.get("port") ? kv.get("port") + "" : "");
   188      setDbName(kv.get("dbname") ? kv.get("dbname") + "" : "");
   189      setUser(kv.get("user") ? kv.get("user") + "" : "");
   190      setPassword(kv.get("password") ? kv.get("password") + "" : "");
   191      setSslMode(kv.get("sslmode") ? kv.get("sslmode") + "" : " ");
   192  
   193      // eslint-disable-next-line react-hooks/exhaustive-deps
   194    }, [useConnectionString]);
   195  
   196    return (
   197      <FormLayout containerPadding={false} withBorders={false}>
   198        <Switch
   199          label={"Manually Configure String"}
   200          checked={useConnectionString}
   201          id="manualString"
   202          name="manualString"
   203          onChange={(e) => {
   204            setUseConnectionString(e.target.checked);
   205          }}
   206          value={"manualString"}
   207        />
   208        {useConnectionString ? (
   209          <Fragment>
   210            <InputBox
   211              id="connection-string"
   212              name="connection_string"
   213              label="Connection String"
   214              value={connectionString}
   215              onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
   216                setConnectionString(e.target.value);
   217              }}
   218            />
   219          </Fragment>
   220        ) : (
   221          <Fragment>
   222            <Grid item xs={12}>
   223              <Box
   224                withBorders
   225                useBackground
   226                sx={{
   227                  overflowY: "auto",
   228                  height: 170,
   229                  marginBottom: 12,
   230                }}
   231              >
   232                <InputBox
   233                  id="host"
   234                  name="host"
   235                  label=""
   236                  placeholder="Enter Host"
   237                  value={host}
   238                  onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
   239                    setHostname(e.target.value);
   240                  }}
   241                />
   242                <InputBox
   243                  id="db-name"
   244                  name="db-name"
   245                  label=""
   246                  placeholder="Enter DB Name"
   247                  value={dbName}
   248                  onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
   249                    setDbName(e.target.value);
   250                  }}
   251                />
   252                <InputBox
   253                  id="port"
   254                  name="port"
   255                  label=""
   256                  placeholder="Enter Port"
   257                  value={port}
   258                  onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
   259                    setPort(e.target.value);
   260                  }}
   261                />
   262                <Select
   263                  value={sslMode}
   264                  label=""
   265                  id="sslmode"
   266                  name="sslmode"
   267                  onChange={(value): void => {
   268                    if (value) {
   269                      setSslMode(value + "");
   270                    }
   271                  }}
   272                  options={[
   273                    { label: "Enter SSL Mode", value: " " },
   274                    { label: "Require", value: "require" },
   275                    { label: "Disable", value: "disable" },
   276                    { label: "Verify CA", value: "verify-ca" },
   277                    { label: "Verify Full", value: "verify-full" },
   278                  ]}
   279                />
   280                <InputBox
   281                  id="user"
   282                  name="user"
   283                  label=""
   284                  placeholder="Enter User"
   285                  value={user}
   286                  onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
   287                    setUser(e.target.value);
   288                  }}
   289                />
   290                <InputBox
   291                  id="password"
   292                  name="password"
   293                  label=""
   294                  type="password"
   295                  placeholder="Enter Password"
   296                  value={password}
   297                  onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
   298                    setPassword(e.target.value);
   299                  }}
   300                />
   301              </Box>
   302            </Grid>
   303            <ReadBox label={"Connection String"} multiLine>
   304              {connectionString}
   305            </ReadBox>
   306          </Fragment>
   307        )}
   308        <InputBox
   309          id="table"
   310          name="table"
   311          label="Table"
   312          placeholder={"Enter Table Name"}
   313          value={table}
   314          tooltip="DB table name to store/update events, table is auto-created"
   315          onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
   316            setTable(e.target.value);
   317          }}
   318        />
   319        <RadioGroup
   320          currentValue={format}
   321          id="format"
   322          name="format"
   323          label="Format"
   324          onChange={(e) => {
   325            setFormat(e.target.value);
   326          }}
   327          tooltip="'namespace' reflects current bucket/object list and 'access' reflects a journal of object operations, defaults to 'namespace'"
   328          selectorOptions={[
   329            { label: "Namespace", value: "namespace" },
   330            { label: "Access", value: "access" },
   331          ]}
   332        />
   333        <InputBox
   334          id="queue-dir"
   335          name="queue_dir"
   336          label="Queue Dir"
   337          placeholder="Enter Queue Directory"
   338          value={queueDir}
   339          tooltip="Staging directory for undelivered messages e.g. '/home/events'"
   340          onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
   341            setQueueDir(e.target.value);
   342          }}
   343        />
   344        <InputBox
   345          id="queue-limit"
   346          name="queue_limit"
   347          label="Queue Limit"
   348          placeholder="Enter Queue Limit"
   349          type="number"
   350          value={queueLimit}
   351          tooltip="Maximum limit for undelivered messages, defaults to '10000'"
   352          onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
   353            setQueueLimit(e.target.value);
   354          }}
   355        />
   356        <CommentBox
   357          id="comment"
   358          name="comment"
   359          label="Comment"
   360          placeholder="Enter custom notes if any"
   361          value={comment}
   362          onChange={(e) => {
   363            setComment(e.target.value);
   364          }}
   365        />
   366      </FormLayout>
   367    );
   368  };
   369  
   370  export default ConfPostgres;