From cd8a285e67f606802f474b24a1dd5160f94fa8c3 Mon Sep 17 00:00:00 2001 From: sergeyteleshev Date: Wed, 10 Jun 2026 00:28:59 +0200 Subject: [PATCH 01/12] dbeaver/pro#9526 adds profile form base functionality --- webapp/packages/plugin-project-info/package.json | 1 - .../src/ProjectInfoForm/ProjectInfoFormPanel.tsx | 2 +- webapp/packages/plugin-project-info/tsconfig.json | 3 +-- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/webapp/packages/plugin-project-info/package.json b/webapp/packages/plugin-project-info/package.json index 81fb76968d..695966b3e5 100644 --- a/webapp/packages/plugin-project-info/package.json +++ b/webapp/packages/plugin-project-info/package.json @@ -5,7 +5,6 @@ "./lib/module.js", "./lib/index.js", "src/**/*.css", - "src/**/*.scss", "public/**/*" ], "version": "0.1.0", diff --git a/webapp/packages/plugin-project-info/src/ProjectInfoForm/ProjectInfoFormPanel.tsx b/webapp/packages/plugin-project-info/src/ProjectInfoForm/ProjectInfoFormPanel.tsx index 278333bf92..fe03e9c9f2 100644 --- a/webapp/packages/plugin-project-info/src/ProjectInfoForm/ProjectInfoFormPanel.tsx +++ b/webapp/packages/plugin-project-info/src/ProjectInfoForm/ProjectInfoFormPanel.tsx @@ -20,7 +20,7 @@ export const ProjectInfoFormPanel = observer(function Pro
-
+
diff --git a/webapp/packages/plugin-project-info/tsconfig.json b/webapp/packages/plugin-project-info/tsconfig.json index df01b57d39..667cb149f8 100644 --- a/webapp/packages/plugin-project-info/tsconfig.json +++ b/webapp/packages/plugin-project-info/tsconfig.json @@ -54,8 +54,7 @@ "__custom_mocks__/**/*", "src/**/*", "src/**/*.json", - "src/**/*.css", - "src/**/*.scss" + "src/**/*.css" ], "exclude": [ "**/node_modules", From 3277a78ac1d9a34532178ff19b9e80d497c5ef46 Mon Sep 17 00:00:00 2001 From: sergeyteleshev Date: Fri, 12 Jun 2026 14:38:13 +0200 Subject: [PATCH 02/12] dbeaver/pro#9526 adds profiles form --- .../src/getNetworkHandlerDefaultProperties.ts | 18 ++ webapp/packages/core-connections/src/index.ts | 2 + .../core-connections/src/validateSSHConfig.ts | 40 +++++ .../SSH/ConnectionFormSSHPart.ts | 44 ++--- .../src/ConnectionForm/SSH/SSH.tsx | 158 +++--------------- .../src/ConnectionForm/SSH/SSHForm.tsx | 151 +++++++++++++++++ .../packages/plugin-connections/src/index.ts | 4 + 7 files changed, 245 insertions(+), 172 deletions(-) create mode 100644 webapp/packages/core-connections/src/getNetworkHandlerDefaultProperties.ts create mode 100644 webapp/packages/core-connections/src/validateSSHConfig.ts create mode 100644 webapp/packages/plugin-connections/src/ConnectionForm/SSH/SSHForm.tsx diff --git a/webapp/packages/core-connections/src/getNetworkHandlerDefaultProperties.ts b/webapp/packages/core-connections/src/getNetworkHandlerDefaultProperties.ts new file mode 100644 index 0000000000..a5e74b7907 --- /dev/null +++ b/webapp/packages/core-connections/src/getNetworkHandlerDefaultProperties.ts @@ -0,0 +1,18 @@ +/* + * CloudBeaver - Cloud Database Manager + * Copyright (C) 2020-2026 DBeaver Corp and others + * + * Licensed under the Apache License, Version 2.0. + * you may not use this file except in compliance with the License. + */ +import type { NetworkHandlerDescriptor } from '@cloudbeaver/core-sdk'; + +export function getNetworkHandlerDefaultProperties(handler: NetworkHandlerDescriptor): Record { + const properties: Record = {}; + for (const property of handler.properties) { + if (!property.features.includes('password')) { + properties[property.id!] = property.value; + } + } + return properties; +} diff --git a/webapp/packages/core-connections/src/index.ts b/webapp/packages/core-connections/src/index.ts index 537beabf84..c270a6436b 100644 --- a/webapp/packages/core-connections/src/index.ts +++ b/webapp/packages/core-connections/src/index.ts @@ -59,6 +59,8 @@ export * from './DBDriverResource.js'; export * from './CONNECTION_INFO_PARAM_SCHEMA.js'; export * from './isJDBCConnection.js'; export * from './NetworkHandlerResource.js'; +export * from './getNetworkHandlerDefaultProperties.js'; +export * from './validateSSHConfig.js'; export * from './useConnectionInfo.js'; export * from './useDBDriver.js'; export * from './USER_NAME_PROPERTY_ID.js'; diff --git a/webapp/packages/core-connections/src/validateSSHConfig.ts b/webapp/packages/core-connections/src/validateSSHConfig.ts new file mode 100644 index 0000000000..31ea3fccfd --- /dev/null +++ b/webapp/packages/core-connections/src/validateSSHConfig.ts @@ -0,0 +1,40 @@ +/* + * CloudBeaver - Cloud Database Manager + * Copyright (C) 2020-2026 DBeaver Corp and others + * + * Licensed under the Apache License, Version 2.0. + * you may not use this file except in compliance with the License. + */ +import { NetworkHandlerAuthType, type NetworkHandlerConfigInput } from '@cloudbeaver/core-sdk'; + +export function validateSSHConfig(state: NetworkHandlerConfigInput, initialState?: NetworkHandlerConfigInput | null): string[] { + const errors: string[] = []; + + if (!state.properties?.['host']?.length) { + errors.push("Field SSH 'Host' can't be empty"); + } + + const port = Number(state.properties?.['port']); + if (Number.isNaN(port) || port < 1) { + errors.push("Field SSH 'Port' can't be empty"); + } + + if (state.savePassword && !state.userName?.length) { + errors.push("Field SSH 'User' can't be empty"); + } + + const keyAuth = state.authType === NetworkHandlerAuthType.PublicKey; + const keySaved = initialState?.key === ''; + + if (keyAuth && state.savePassword && !keySaved && !state.key?.length) { + errors.push("Field SSH 'Private key' can't be empty"); + } + + const passwordSaved = initialState?.password === '' && initialState?.authType === state.authType; + + if (!keyAuth && state.savePassword && !passwordSaved && !state.password?.length) { + errors.push("Field SSH 'Password' can't be empty"); + } + + return errors; +} diff --git a/webapp/packages/plugin-connections/src/ConnectionForm/SSH/ConnectionFormSSHPart.ts b/webapp/packages/plugin-connections/src/ConnectionForm/SSH/ConnectionFormSSHPart.ts index 324f310686..fe390b3cd8 100644 --- a/webapp/packages/plugin-connections/src/ConnectionForm/SSH/ConnectionFormSSHPart.ts +++ b/webapp/packages/plugin-connections/src/ConnectionForm/SSH/ConnectionFormSSHPart.ts @@ -14,7 +14,13 @@ import { type NetworkHandlerConfigInput, type NetworkHandlerDescriptor, } from '@cloudbeaver/core-sdk'; -import { ConnectionInfoNetworkHandlersResource, NetworkHandlerResource, SSH_TUNNEL_ID } from '@cloudbeaver/core-connections'; +import { + ConnectionInfoNetworkHandlersResource, + getNetworkHandlerDefaultProperties, + NetworkHandlerResource, + SSH_TUNNEL_ID, + validateSSHConfig, +} from '@cloudbeaver/core-connections'; import { toJS } from 'mobx'; import type { IConnectionFormState } from '../IConnectionFormState.js'; import type { INetworkHandlerConfig } from '../Options/IConnectionNetworkHanler.js'; @@ -94,17 +100,8 @@ export class ConnectionFormSSHPart extends FormPart = {}; - if (handlerDescriptor) { - for (const property of handlerDescriptor.properties) { - if (!property.features.includes('password')) { - properties[property.id!] = property.value; - } - } - } - state.properties = { - ...properties, + ...getNetworkHandlerDefaultProperties(handlerDescriptor), ...state.properties, }; } @@ -145,29 +142,8 @@ export class ConnectionFormSSHPart extends FormPart = observer(function SSH({ formState, handlerState, tabId }) { const { selected } = useTab(tabId); const [loading, setLoading] = useState(false); - const { credentialsSavingEnabled } = useAdministrationSettings(); const networkHandlerResource = useService(NetworkHandlerResource); - const serverConfigResource = useResource(SSH, ServerConfigResource, undefined, { - active: selected, - }); + const SSHPart = getConnectionFormSSHPart(formState); + const optionsPart = getConnectionFormOptionsPart(formState); async function testConnection() { setLoading(true); const config = SSHPart.getConfig(); - await networkHandlerResource.test(config, formState.state.projectId, formState.state.connectionId); - setLoading(false); + try { + await networkHandlerResource.test(config, formState.state.projectId, formState.state.connectionId); + } finally { + setLoading(false); + } } const style = useS(styles); @@ -70,19 +50,6 @@ export const SSH: TabContainerPanelComponent = observer(function SSH({ fo const keyAuth = handlerState.authType === NetworkHandlerAuthType.PublicKey; const passwordFilled = (SSHPart.initialState?.password === null && handlerState.password !== '') || !!handlerState.password?.length; const testAvailable = keyAuth ? !!handlerState.key?.length : passwordFilled; - const passwordLabel = keyAuth ? 'Passphrase' : translate('connections_network_handler_ssh_tunnel_password'); - const passwordSaved = SSHPart.initialState?.password === '' && SSHPart.initialState.authType === handlerState.authType; - const keySaved = SSHPart.initialState?.key === ''; - const projectInfoResource = useService(ProjectInfoResource); - const isSharedProject = projectInfoResource.isProjectShared(formState.state.projectId); - const optionsPart = getConnectionFormOptionsPart(formState); - - const aliveIntervalLabel = translate('connections_network_handler_ssh_tunnel_advanced_settings_alive_interval'); - const connectTimeoutLabel = translate('connections_network_handler_ssh_tunnel_advanced_settings_connect_timeout'); - - const authTypeChangeHandler = useCallback(() => { - handlerState.password = ''; - }, []); useAutoLoad(SSH, [SSHPart, optionsPart], selected); @@ -93,99 +60,14 @@ export const SSH: TabContainerPanelComponent = observer(function SSH({ fo {translate('connections_network_handler_ssh_tunnel_enable')} - - - - {translate('connections_network_handler_ssh_tunnel_host')} - - - {translate('connections_network_handler_ssh_tunnel_port')} - - - - - {translate('connections_network_handler_ssh_tunnel_user')} - - - {passwordLabel} - - {keyAuth && } - - {credentialsSavingEnabled && !optionsPart.state.sharedCredentials && ( - - {translate( - !isSharedProject || serverConfigResource.data?.distributed - ? 'connections_connection_authentication_save_credentials_for_user' - : 'connections_connection_edit_save_credentials_shared', - )} - - )} - - - - - {aliveIntervalLabel} - - - {connectTimeoutLabel} - - - - + readonly={formState.isReadOnly} + sharedCredentials={optionsPart.state.sharedCredentials} + projectId={formState.state.projectId} + />