diff --git a/src/drivers/base/SQLLikeConnection.ts b/src/drivers/base/SQLLikeConnection.ts index 785f75e..82ea995 100644 --- a/src/drivers/base/SQLLikeConnection.ts +++ b/src/drivers/base/SQLLikeConnection.ts @@ -13,13 +13,21 @@ export interface MySqlConnectionConfig { port: number; } +export type ConnectionSslOption = + | boolean + | { + ca?: string; + cert?: string; + key?: string; + }; + export interface PgConnectionConfig { database: string; user: string; password: string; host: string; port: number; - ssl?: boolean; + ssl?: ConnectionSslOption; } export interface SqliteConnectionConfig { diff --git a/src/main/ipc/ipc_file_system.ts b/src/main/ipc/ipc_file_system.ts index f2313ff..6c2877f 100644 --- a/src/main/ipc/ipc_file_system.ts +++ b/src/main/ipc/ipc_file_system.ts @@ -3,7 +3,9 @@ import { dialog, SaveDialogSyncOptions, shell, + OpenDialogOptions, } from 'electron'; +import fs from 'fs'; import saveCsvFile from '../../libs/SaveCSVFile'; import saveExcelFile from '../../libs/SaveExcelFile'; import CommunicateHandler from './../CommunicateHandler'; @@ -15,15 +17,23 @@ CommunicateHandler.handle( return dialog.showMessageBoxSync(window, options); } return 0; - } + }, ) + .handle('show-open-dialog', ([options]: [OpenDialogOptions], { window }) => { + if (window) { + return dialog.showOpenDialog(window, options); + } + }) + .handle('read-file', ([fileName]: [string]) => { + return fs.readFileSync(fileName); + }) .handle( 'show-save-dialog', ([options]: [SaveDialogSyncOptions], { window }) => { if (window) { return dialog.showSaveDialogSync(window, options); } - } + }, ) .handle('save-csv-file', ([fileName, records]: [string, object[]]) => { saveCsvFile(fileName, records); diff --git a/src/main/preload.ts b/src/main/preload.ts index f5a8c46..cf4ca19 100644 --- a/src/main/preload.ts +++ b/src/main/preload.ts @@ -8,6 +8,8 @@ import { MessageBoxSyncOptions, MenuItemConstructorOptions, SaveDialogSyncOptions, + OpenDialogOptions, + OpenDialogReturnValue, } from 'electron'; import { ProgressInfo, @@ -61,6 +63,14 @@ const electronHandler = { ): Promise => ipcRenderer.invoke('show-save-dialog', [options]), + showOpenDialog: ( + options: OpenDialogOptions, + ): Promise => + ipcRenderer.invoke('show-open-dialog', [options]), + + readFile: (fileName: string): Promise => + ipcRenderer.invoke('read-file', [fileName]), + showFileInFolder: (fileName: string) => ipcRenderer.invoke('show-item-in-folder', [fileName]), diff --git a/src/renderer/components/ConnectionListTree/ConnectionConfigEditor/CertificatePicker.tsx b/src/renderer/components/ConnectionListTree/ConnectionConfigEditor/CertificatePicker.tsx new file mode 100644 index 0000000..bec8880 --- /dev/null +++ b/src/renderer/components/ConnectionListTree/ConnectionConfigEditor/CertificatePicker.tsx @@ -0,0 +1,151 @@ +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import styles from './certificate.module.scss'; +import { + faCheckCircle, + faCircleMinus, + faCircleXmark, + faFolder, +} from '@fortawesome/free-solid-svg-icons'; +import { useCallback } from 'react'; +import { ConnectionSslOption } from 'drivers/base/SQLLikeConnection'; + +interface PickerButtonProps { + label: string; + onChange: (v?: string) => void; + value?: string; +} + +interface CertificatePickerProps { + value?: ConnectionSslOption; + onChange: (v: ConnectionSslOption | undefined) => void; +} + +function CertificatePickerButton({ + label, + onChange, + value, +}: PickerButtonProps) { + const onClick = useCallback(() => { + window.electron.showOpenDialog({}).then((e) => { + if (!e.canceled) { + if (e.filePaths.length === 1) { + window.electron + .readFile(e.filePaths[0]) + .then((content) => onChange(new TextDecoder().decode(content))); + } + } + }); + }, [onChange]); + + const className = [styles.button, value ? styles.provided : undefined] + .filter(Boolean) + .join(' '); + + return ( +
+
+ + + {label} +
+ {value && ( +
{ + onChange(undefined); + }} + > + +
+ )} + {!value && ( +
+ +
+ )} +
+ ); +} + +export default function CertificatePicker({ + value, + onChange, +}: CertificatePickerProps) { + const onUpdate = useCallback( + (property: 'ca' | 'cert' | 'key', newValue?: string) => { + let tmp: ConnectionSslOption | undefined; + if (!value) tmp = { [property]: newValue }; + else if (value === true) { + tmp = { [property]: newValue }; + } else { + tmp = { ...value, [property]: newValue }; + } + + // Remove all the undefined property + if (typeof tmp === 'object') { + for (const tmpKey of Object.keys(tmp)) { + const key = tmpKey as keyof ConnectionSslOption; + if (!tmp[key]) { + delete tmp[key]; + } + } + + if (Object.entries(tmp).length === 0) { + onChange(value === true ? true : undefined); + } else onChange(tmp); + } else { + onChange(undefined); + } + }, + [value, onChange], + ); + + const ca = typeof value === 'object' ? value.ca : undefined; + const cert = typeof value === 'object' ? value.cert : undefined; + const key = typeof value === 'object' ? value.key : undefined; + const ssl = !!value; + + return ( +
+

+ +

+
+ onUpdate('ca', e)} + /> + onUpdate('cert', e)} + /> + onUpdate('key', e)} + /> +
+
+ ); +} diff --git a/src/renderer/components/ConnectionListTree/ConnectionConfigEditor/PostgreConnectionEditor copy.tsx b/src/renderer/components/ConnectionListTree/ConnectionConfigEditor/PostgreConnectionEditor.tsx similarity index 66% rename from src/renderer/components/ConnectionListTree/ConnectionConfigEditor/PostgreConnectionEditor copy.tsx rename to src/renderer/components/ConnectionListTree/ConnectionConfigEditor/PostgreConnectionEditor.tsx index dea084a..094a6c9 100644 --- a/src/renderer/components/ConnectionListTree/ConnectionConfigEditor/PostgreConnectionEditor copy.tsx +++ b/src/renderer/components/ConnectionListTree/ConnectionConfigEditor/PostgreConnectionEditor.tsx @@ -9,19 +9,11 @@ export default function PostgreConnectionEditor({ config: ConnectionStoreConfig; onChange: (value: ConnectionStoreConfig) => void; }) { + console.log(config); + return ( - ); } diff --git a/src/renderer/components/ConnectionListTree/ConnectionConfigEditor/RelationalDbConnectionEditor.tsx b/src/renderer/components/ConnectionListTree/ConnectionConfigEditor/RelationalDbConnectionEditor.tsx index cd23d33..248274b 100644 --- a/src/renderer/components/ConnectionListTree/ConnectionConfigEditor/RelationalDbConnectionEditor.tsx +++ b/src/renderer/components/ConnectionListTree/ConnectionConfigEditor/RelationalDbConnectionEditor.tsx @@ -3,6 +3,7 @@ import Stack from 'renderer/components/Stack'; import TextField from 'renderer/components/TextField'; import PasswordField from 'renderer/components/PasswordField'; import { useState } from 'react'; +import CertificatePicker from './CertificatePicker'; export default function RelationalDbConnectionEditor({ config, @@ -52,6 +53,11 @@ export default function RelationalDbConnectionEditor({ + onChange({ ...config, ssl: value })} + /> + div { + background: var(--color-input-border); + padding: 10px; + + &:hover { + opacity: 0.8; + } + } +} + +.icon { + border-left: 1px solid var(--color-surface); +} + +.button.provided { + > div { + background: #27ae60; + color: #fff; + } +} + +.close { + background: #e74c3c !important; + color: #fff !important; +} diff --git a/src/renderer/components/ConnectionListTree/ConnectionConfigEditor/index.tsx b/src/renderer/components/ConnectionListTree/ConnectionConfigEditor/index.tsx index 219f596..93b2b38 100644 --- a/src/renderer/components/ConnectionListTree/ConnectionConfigEditor/index.tsx +++ b/src/renderer/components/ConnectionListTree/ConnectionConfigEditor/index.tsx @@ -1,6 +1,6 @@ import { ConnectionStoreConfig } from 'drivers/base/SQLLikeConnection'; import MySQLConnectionEditor from './MySQLConnectionEditor'; -import PostgreConnectionEditor from './PostgreConnectionEditor copy'; +import PostgreConnectionEditor from './PostgreConnectionEditor'; export default function ConnectionConfigEditor({ type,