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;