diff --git a/server/bundles/io.cloudbeaver.model/OSGI-INF/l10n/bundle.properties b/server/bundles/io.cloudbeaver.model/OSGI-INF/l10n/bundle.properties new file mode 100644 index 0000000000..f4e51c2924 --- /dev/null +++ b/server/bundles/io.cloudbeaver.model/OSGI-INF/l10n/bundle.properties @@ -0,0 +1,13 @@ +meta.io.cloudbeaver.model.app.ServletSystemInformationCollector.osInfo.name=OS +meta.io.cloudbeaver.model.app.ServletSystemInformationCollector.memoryAvailable.name=Memory available +meta.io.cloudbeaver.model.app.ServletSystemInformationCollector.javaVersion.name=Java version +meta.io.cloudbeaver.model.app.ServletSystemInformationCollector.javaParameters.name=JVM parameters +meta.io.cloudbeaver.model.app.ServletSystemInformationCollector.productName.name=Product name +meta.io.cloudbeaver.model.app.ServletSystemInformationCollector.productVersion.name=Product version +meta.io.cloudbeaver.model.app.ServletSystemInformationCollector.workspacePath.name=Workspace path +meta.io.cloudbeaver.model.app.ServletSystemInformationCollector.installPath.name=Install path +meta.io.cloudbeaver.model.app.ServletSystemInformationCollector.deploymentType.name=Deployment type +meta.io.cloudbeaver.model.app.ServletSystemInformationCollector.smDbUrl.name=Security manager database URL +meta.io.cloudbeaver.model.app.ServletSystemInformationCollector.smDbDriverName.name=Security manager database driver name +meta.io.cloudbeaver.model.app.ServletSystemInformationCollector.smDbProductName.name=Security manager database product name +meta.io.cloudbeaver.model.app.ServletSystemInformationCollector.smDbProductVersion.name=Security manager database product version \ No newline at end of file diff --git a/server/bundles/io.cloudbeaver.model/build.properties b/server/bundles/io.cloudbeaver.model/build.properties index d9cc939477..436dfbcddc 100644 --- a/server/bundles/io.cloudbeaver.model/build.properties +++ b/server/bundles/io.cloudbeaver.model/build.properties @@ -2,5 +2,6 @@ source.. = src/ output.. = target/classes/ bin.includes = .,\ META-INF/,\ + OSGI-INF/,\ plugin.xml,\ schema/ diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/app/ServletApplication.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/app/ServletApplication.java index 8fd3bae376..fd8298db22 100644 --- a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/app/ServletApplication.java +++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/app/ServletApplication.java @@ -91,6 +91,12 @@ public interface ServletApplication extends DBPApplication { boolean isLicenseRequired(); + /** + * Collector that contains information about system. + */ + @NotNull + ServletSystemInformationCollector getSystemInformationCollector(); + default void getStatusInfo(Map infoMap) { } diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/app/ServletSystemInformationCollector.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/app/ServletSystemInformationCollector.java new file mode 100644 index 0000000000..ade5aae22c --- /dev/null +++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/app/ServletSystemInformationCollector.java @@ -0,0 +1,170 @@ +/* + * DBeaver - Universal Database Manager + * Copyright (C) 2010-2024 DBeaver Corp and others + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.cloudbeaver.model.app; + +import io.cloudbeaver.auth.NoAuthCredentialsProvider; +import org.jkiss.code.NotNull; +import org.jkiss.dbeaver.DBException; +import org.jkiss.dbeaver.model.DBPConnectionInformation; +import org.jkiss.dbeaver.model.DBPObject; +import org.jkiss.dbeaver.model.meta.Property; +import org.jkiss.dbeaver.model.meta.PropertyGroup; +import org.jkiss.dbeaver.model.meta.PropertyLength; +import org.jkiss.dbeaver.utils.GeneralUtils; +import org.jkiss.dbeaver.utils.SystemVariablesResolver; +import org.jkiss.utils.StandardConstants; + +import java.nio.file.Files; +import java.nio.file.Path; + +/** + * Web system information collector. + */ +public class ServletSystemInformationCollector implements DBPObject { + + private enum DeploymentType { + DEFAULT, + DOCKER, + KUBERNETES + } + + @NotNull + protected final T application; + + @NotNull + private final String osInfo; + @NotNull + private final String javaVersion; + @NotNull + private final String javaParameters; + @NotNull + private final String productName; + @NotNull + private final String productVersion; + @NotNull + private final String memoryAvailable; + @NotNull + private final String installPath; + private DBPConnectionInformation smDatabaseInfo; + private String workspacePath; + private final DeploymentType deploymentType; + + public ServletSystemInformationCollector(@NotNull T application) { + this.application = application; + this.osInfo = System.getProperty(StandardConstants.ENV_OS_NAME) + " " + System.getProperty( + StandardConstants.ENV_OS_VERSION) + " (" + System.getProperty(StandardConstants.ENV_OS_ARCH) + ")"; + this.javaVersion = System.getProperty(StandardConstants.ENV_JAVA_VERSION) + " by " + System.getProperty( + StandardConstants.ENV_JAVA_VENDOR) + " (" + System.getProperty(StandardConstants.ENV_JAVA_ARCH) + "bit)"; + this.javaParameters = System.getProperty("sun.java.command"); + this.productName = GeneralUtils.getProductName(); + this.productVersion = GeneralUtils.getProductVersion().toString(); + this.installPath = SystemVariablesResolver.getInstallPath(); + this.memoryAvailable = "%dMb/%dMb".formatted( + Runtime.getRuntime().totalMemory() / (1024 * 1024), + Runtime.getRuntime().maxMemory() / (1024 * 1024) + ); + deploymentType = checkDeploymentType(); + } + + private DeploymentType checkDeploymentType() { + if (System.getenv("KUBERNETES_SERVICE_HOST") != null) { + return DeploymentType.KUBERNETES; + } + if (isRunningInDocker()) { + return DeploymentType.DOCKER; + } + + return DeploymentType.DEFAULT; + } + + @NotNull + @Property(order = 1) + public String getProductName() { + return productName; + } + + @NotNull + @Property(order = 2) + public String getProductVersion() { + return productVersion; + } + + @NotNull + @Property(order = 11) + public String getOsInfo() { + return osInfo; + } + + @NotNull + @Property(order = 12) + public String getMemoryAvailable() { + return memoryAvailable; + } + + @NotNull + @Property(order = 21, length = PropertyLength.MULTILINE) + public String getJavaVersion() { + return javaVersion; + } + + @NotNull + @Property(order = 22, length = PropertyLength.MULTILINE) + public String getJavaParameters() { + return javaParameters; + } + + @NotNull + @Property(order = 23) + public String getDeploymentType() { + return deploymentType.name(); + } + + @NotNull + @PropertyGroup(order = 31, category = "Security manager database", id = "sm") + public DBPConnectionInformation getSmDatabaseInfo() { + return smDatabaseInfo; + } + + @Property(order = Integer.MAX_VALUE - 10, length = PropertyLength.MULTILINE) + public String getWorkspacePath() { + return workspacePath; + } + + public void setWorkspacePath(String workspacePath) { + this.workspacePath = workspacePath; + } + + @NotNull + @Property(order = Integer.MAX_VALUE - 10, length = PropertyLength.MULTILINE) + public String getInstallPath() { + return installPath; + } + + + /** + * Collects info about internal databases. + */ + public void collectInternalDatabaseUseInformation() throws DBException { + this.smDatabaseInfo = application.getAdminSecurityController(new NoAuthCredentialsProvider()) + .getInternalDatabaseInformation(); + } + + private static boolean isRunningInDocker() { + Path cgroupPath = Path.of("/.dockerenv"); + return Files.exists(cgroupPath); + } +} diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/utils/WebCommonUtils.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/utils/WebCommonUtils.java index 6601be8372..1a819bbea2 100644 --- a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/utils/WebCommonUtils.java +++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/utils/WebCommonUtils.java @@ -43,7 +43,7 @@ public class WebCommonUtils { PropertyCollector propertyCollector = new PropertyCollector(details, false); propertyCollector.collectProperties(); return Arrays.stream(propertyCollector.getProperties()) - .filter(p -> !(p instanceof ObjectPropertyDescriptor && ((ObjectPropertyDescriptor) p).isHidden())) + .filter(p -> !(p instanceof ObjectPropertyDescriptor objProp && objProp.isHidden())) .map(p -> new WebPropertyInfo(session, p, propertyCollector)).toArray(WebPropertyInfo[]::new); } diff --git a/server/bundles/io.cloudbeaver.server.ce/src/io/cloudbeaver/server/CBApplication.java b/server/bundles/io.cloudbeaver.server.ce/src/io/cloudbeaver/server/CBApplication.java index 1966bc8f26..7708dca801 100644 --- a/server/bundles/io.cloudbeaver.server.ce/src/io/cloudbeaver/server/CBApplication.java +++ b/server/bundles/io.cloudbeaver.server.ce/src/io/cloudbeaver/server/CBApplication.java @@ -23,6 +23,7 @@ import io.cloudbeaver.model.WebServerConfig; import io.cloudbeaver.model.app.BaseServletApplication; import io.cloudbeaver.model.app.ServletAuthApplication; import io.cloudbeaver.model.app.ServletAuthConfiguration; +import io.cloudbeaver.model.app.ServletSystemInformationCollector; import io.cloudbeaver.model.config.CBAppConfig; import io.cloudbeaver.model.config.CBServerConfig; import io.cloudbeaver.model.config.SMControllerConfiguration; @@ -56,11 +57,8 @@ import org.jkiss.dbeaver.model.websocket.event.WSEventController; import org.jkiss.dbeaver.model.websocket.event.WSServerConfigurationChangedEvent; import org.jkiss.dbeaver.runtime.DBWorkbench; import org.jkiss.dbeaver.runtime.ui.DBPPlatformUI; -import org.jkiss.dbeaver.utils.GeneralUtils; -import org.jkiss.dbeaver.utils.SystemVariablesResolver; import org.jkiss.utils.ArrayUtils; import org.jkiss.utils.CommonUtils; -import org.jkiss.utils.StandardConstants; import java.io.File; import java.io.IOException; @@ -112,6 +110,7 @@ public abstract class CBApplication extends private CBSessionManager sessionManager; private final Map initActions = new ConcurrentHashMap<>(); + private ServletSystemInformationCollector systemInformationCollector; private CBJettyServer jettyServer; @@ -247,15 +246,18 @@ public abstract class CBApplication extends log.error("Error setting workspace location to " + getWorkspaceDirectory().toAbsolutePath(), e); return; } - - log.debug(GeneralUtils.getProductName() + " " + GeneralUtils.getProductVersion() + " is starting"); //$NON-NLS-1$ - log.debug("\tOS: " + System.getProperty(StandardConstants.ENV_OS_NAME) + " " + System.getProperty( - StandardConstants.ENV_OS_VERSION) + " (" + System.getProperty(StandardConstants.ENV_OS_ARCH) + ")"); - log.debug("\tJava version: " + System.getProperty(StandardConstants.ENV_JAVA_VERSION) + " by " + System.getProperty( - StandardConstants.ENV_JAVA_VENDOR) + " (" + System.getProperty(StandardConstants.ENV_JAVA_ARCH) + "bit)"); - log.debug("\tInstall path: '" + SystemVariablesResolver.getInstallPath() + "'"); //$NON-NLS-1$ //$NON-NLS-2$ - log.debug("\tGlobal workspace: '" + instanceLoc.getURL() + "'"); //$NON-NLS-1$ //$NON-NLS-2$ - log.debug("\tMemory available " + (runtime.totalMemory() / (1024 * 1024)) + "Mb/" + (runtime.maxMemory() / (1024 * 1024)) + "Mb"); + this.systemInformationCollector = createSystemInformationCollector(); + this.systemInformationCollector.setWorkspacePath(instanceLoc.getURL().toString()); + + log.debug("%s %s is starting".formatted( + systemInformationCollector.getProductName(), + systemInformationCollector.getProductVersion()) + ); //$NON-NLS-1$ + log.debug("\tOS: " + systemInformationCollector.getOsInfo()); + log.debug("\tJava version: " + systemInformationCollector.getJavaVersion()); + log.debug("\tInstall path: '" + systemInformationCollector.getInstallPath() + "'"); //$NON-NLS-1$ //$NON-NLS-2$ + log.debug("\tGlobal workspace: '" + systemInformationCollector.getWorkspacePath() + "'"); //$NON-NLS-1$ //$NON-NLS-2$ + log.debug("\tMemory available " + systemInformationCollector.getMemoryAvailable()); DBWorkbench.getPlatform().getApplication(); @@ -321,6 +323,11 @@ public abstract class CBApplication extends } grantPermissionsToConnections(); } + try { + this.systemInformationCollector.collectInternalDatabaseUseInformation(); + } catch (DBException e) { + log.error("Error collecting system information", e); + } eventController.scheduleCheckJob(); @@ -331,6 +338,10 @@ public abstract class CBApplication extends return; } + protected ServletSystemInformationCollector createSystemInformationCollector() { + return new ServletSystemInformationCollector<>(this); + } + protected void initializeAdditionalConfiguration() { } @@ -785,4 +796,10 @@ public abstract class CBApplication extends public ConnectionController getConnectionController() { return new ConnectionControllerCE(); } + + @NotNull + @Override + public ServletSystemInformationCollector getSystemInformationCollector() { + return systemInformationCollector; + } } diff --git a/server/bundles/io.cloudbeaver.server/schema/service.core.graphqls b/server/bundles/io.cloudbeaver.server/schema/service.core.graphqls index 2022abe368..8db9a458e4 100644 --- a/server/bundles/io.cloudbeaver.server/schema/service.core.graphqls +++ b/server/bundles/io.cloudbeaver.server/schema/service.core.graphqls @@ -570,6 +570,7 @@ input ConnectionConfig { extend type Query { # Return server config serverConfig: ServerConfig! + systemInfo: [ObjectPropertyInfo!]! @since(version: "24.3.5") # Return product settings productSettings: ProductSettings! @since(version: "24.0.1") diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/DBWServiceCore.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/DBWServiceCore.java index 2549fa64c5..803e62836b 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/DBWServiceCore.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/DBWServiceCore.java @@ -41,6 +41,12 @@ public interface DBWServiceCore extends DBWService { @WebAction(authRequired = false, initializationRequired = false) WebServerConfig getServerConfig() throws DBWebException; + /** + * Returns information of system. + */ + @WebAction(authRequired = false) + WebPropertyInfo[] getSystemInformationProperties(@NotNull WebSession webSession); + @WebAction(authRequired = false) WebProductSettings getProductSettings(@NotNull WebSession webSession); diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/WebServiceBindingCore.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/WebServiceBindingCore.java index c310b95d33..d231799c0f 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/WebServiceBindingCore.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/WebServiceBindingCore.java @@ -53,6 +53,7 @@ public class WebServiceBindingCore extends WebServiceBindingBase WebAppSessionManager sessionManager = WebAppUtils.getWebApplication().getSessionManager(); model.getQueryType() .dataFetcher("serverConfig", env -> getService(env).getServerConfig()) + .dataFetcher("systemInfo", env -> getService(env).getSystemInformationProperties(getWebSession(env))) .dataFetcher("productSettings", env -> getService(env).getProductSettings(getWebSession(env))) .dataFetcher("driverList", env -> getService(env).getDriverList(getWebSession(env), env.getArgument("id"))) diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/impl/WebServiceCore.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/impl/WebServiceCore.java index 746460111a..5b94594b12 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/impl/WebServiceCore.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/impl/WebServiceCore.java @@ -27,10 +27,7 @@ import io.cloudbeaver.server.WebAppUtils; import io.cloudbeaver.server.WebApplication; import io.cloudbeaver.service.core.DBWServiceCore; import io.cloudbeaver.service.security.SMUtils; -import io.cloudbeaver.utils.ServletAppUtils; -import io.cloudbeaver.utils.WebConnectionFolderUtils; -import io.cloudbeaver.utils.WebDataSourceUtils; -import io.cloudbeaver.utils.WebEventUtils; +import io.cloudbeaver.utils.*; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import org.jkiss.code.NotNull; @@ -44,18 +41,13 @@ import org.jkiss.dbeaver.model.app.DBPDataSourceRegistry; import org.jkiss.dbeaver.model.app.DBPProject; import org.jkiss.dbeaver.model.connection.DBPConnectionConfiguration; import org.jkiss.dbeaver.model.connection.DBPDriver; -import org.jkiss.dbeaver.model.exec.DBCConnectException; import org.jkiss.dbeaver.model.navigator.*; import org.jkiss.dbeaver.model.net.DBWHandlerConfiguration; -import org.jkiss.dbeaver.model.net.DBWHandlerType; import org.jkiss.dbeaver.model.net.DBWNetworkHandler; import org.jkiss.dbeaver.model.net.DBWTunnel; import org.jkiss.dbeaver.model.net.ssh.SSHSession; import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor; -import org.jkiss.dbeaver.model.secret.DBSSecretController; -import org.jkiss.dbeaver.model.secret.DBSSecretValue; import org.jkiss.dbeaver.model.websocket.WSConstants; -import org.jkiss.dbeaver.model.websocket.event.datasource.WSDataSourceConnectEvent; import org.jkiss.dbeaver.model.websocket.event.datasource.WSDataSourceProperty; import org.jkiss.dbeaver.registry.DataSourceDescriptor; import org.jkiss.dbeaver.registry.DataSourceProviderRegistry; @@ -63,8 +55,6 @@ import org.jkiss.dbeaver.registry.network.NetworkHandlerDescriptor; import org.jkiss.dbeaver.registry.network.NetworkHandlerRegistry; import org.jkiss.dbeaver.registry.settings.ProductSettingsRegistry; import org.jkiss.dbeaver.runtime.DBWorkbench; -import org.jkiss.dbeaver.runtime.jobs.ConnectionTestJob; -import org.jkiss.dbeaver.utils.RuntimeUtils; import org.jkiss.utils.CommonUtils; import java.util.*; @@ -82,6 +72,14 @@ public class WebServiceCore implements DBWServiceCore { return WebAppUtils.getWebApplication().getWebServerConfig(); } + @Override + public WebPropertyInfo[] getSystemInformationProperties(@NotNull WebSession webSession) { + return WebCommonUtils.getObjectProperties( + webSession, + WebAppUtils.getWebApplication().getSystemInformationCollector() + ); + } + @Override public List getDriverList(@NotNull WebSession webSession, String driverId) { List result = new ArrayList<>(); diff --git a/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/service/security/CBEmbeddedSecurityController.java b/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/service/security/CBEmbeddedSecurityController.java index 3b24ae5f28..246b108ff5 100644 --- a/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/service/security/CBEmbeddedSecurityController.java +++ b/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/service/security/CBEmbeddedSecurityController.java @@ -36,6 +36,7 @@ import org.jkiss.code.NotNull; import org.jkiss.code.Nullable; import org.jkiss.dbeaver.DBException; import org.jkiss.dbeaver.Log; +import org.jkiss.dbeaver.model.DBPConnectionInformation; import org.jkiss.dbeaver.model.DBPPage; import org.jkiss.dbeaver.model.auth.*; import org.jkiss.dbeaver.model.exec.DBCException; @@ -3322,4 +3323,10 @@ public class CBEmbeddedSecurityController private String getDefaultUserTeam() { return application.getAppConfiguration().getDefaultUserTeam(); } + + @NotNull + @Override + public DBPConnectionInformation getInternalDatabaseInformation() { + return database.getMetaDataInfo(); + } } diff --git a/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/service/security/db/CBDatabase.java b/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/service/security/db/CBDatabase.java index fb12e0b8b8..a362d566af 100644 --- a/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/service/security/db/CBDatabase.java +++ b/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/service/security/db/CBDatabase.java @@ -33,6 +33,7 @@ import org.jkiss.code.Nullable; import org.jkiss.dbeaver.DBException; import org.jkiss.dbeaver.Log; import org.jkiss.dbeaver.model.DBConstants; +import org.jkiss.dbeaver.model.DBPConnectionInformation; import org.jkiss.dbeaver.model.auth.AuthInfo; import org.jkiss.dbeaver.model.connection.DBPDriver; import org.jkiss.dbeaver.model.impl.app.ApplicationRegistry; @@ -86,6 +87,8 @@ public class CBDatabase { private final ServletApplication application; private final WebDatabaseConfig databaseConfiguration; private PoolingDataSource cbDataSource; + private DBPConnectionInformation cbConnectionInformation; + private transient volatile Connection exclusiveConnection; private String instanceId; @@ -200,7 +203,16 @@ public class CBDatabase { try (Connection connection = cbDataSource.getConnection()) { DatabaseMetaData metaData = connection.getMetaData(); - log.debug("\tConnected to " + metaData.getDatabaseProductName() + " " + metaData.getDatabaseProductVersion()); + final String dbName = metaData.getDatabaseProductName(); + final String dbVersion = metaData.getDatabaseProductVersion(); + log.debug("\tConnected to " + dbName + " " + dbVersion); + + cbConnectionInformation = new DBPConnectionInformation( + databaseConfiguration.getUrl(), + databaseConfiguration.getDriver(), + dbName, + dbVersion + ); if (dialect instanceof SQLDialectSchemaController && CommonUtils.isNotEmpty(schemaName)) { var dialectSchemaController = (SQLDialectSchemaController) dialect; @@ -600,6 +612,14 @@ public class CBDatabase { || v2DefaultUrl.equals(databaseConfiguration.getUrl()); } + /** + * Returns internal database metadata. + */ + @NotNull + public DBPConnectionInformation getMetaDataInfo() { + return cbConnectionInformation; + } + protected WebDatabaseConfig getDatabaseConfiguration() { return databaseConfiguration; } diff --git a/webapp/packages/core-sdk/src/queries/administration/getSystemInfo.gql b/webapp/packages/core-sdk/src/queries/administration/getSystemInfo.gql new file mode 100644 index 0000000000..300ce2d11e --- /dev/null +++ b/webapp/packages/core-sdk/src/queries/administration/getSystemInfo.gql @@ -0,0 +1,13 @@ +query getSystemInfo { + info: systemInfo { + id + displayName + category + value + length + features + dataType + order + required + } +} diff --git a/webapp/packages/plugin-system-information-administration/.gitignore b/webapp/packages/plugin-system-information-administration/.gitignore new file mode 100644 index 0000000000..15bc16c7c3 --- /dev/null +++ b/webapp/packages/plugin-system-information-administration/.gitignore @@ -0,0 +1,17 @@ +# dependencies +/node_modules + +# testing +/coverage + +# production +/lib + +# misc +.DS_Store +.env* + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* diff --git a/webapp/packages/plugin-system-information-administration/package.json b/webapp/packages/plugin-system-information-administration/package.json new file mode 100644 index 0000000000..5a4ba0e5d5 --- /dev/null +++ b/webapp/packages/plugin-system-information-administration/package.json @@ -0,0 +1,45 @@ +{ + "name": "@cloudbeaver/plugin-system-information-administration", + "type": "module", + "sideEffects": [ + "src/**/*.css", + "src/**/*.scss", + "public/**/*" + ], + "version": "0.1.0", + "description": "", + "license": "Apache-2.0", + "exports": { + ".": "./dist/index.js" + }, + "scripts": { + "build": "tsc -b", + "clean": "rimraf --glob dist", + "lint": "eslint ./src/ --ext .ts,.tsx", + "test": "core-cli-test", + "validate-dependencies": "core-cli-validate-dependencies" + }, + "dependencies": { + "@cloudbeaver/core-administration": "workspace:*", + "@cloudbeaver/core-blocks": "workspace:*", + "@cloudbeaver/core-di": "workspace:*", + "@cloudbeaver/core-localization": "workspace:*", + "@cloudbeaver/core-resource": "workspace:*", + "@cloudbeaver/core-root": "workspace:*", + "@cloudbeaver/core-sdk": "workspace:*", + "@cloudbeaver/core-ui": "workspace:*", + "@cloudbeaver/plugin-product-information-administration": "workspace:*", + "mobx": "^6", + "mobx-react-lite": "^4", + "react": "^19", + "react-dom": "^19", + "tslib": "^2" + }, + "devDependencies": { + "@cloudbeaver/core-cli": "workspace:*", + "@cloudbeaver/tsconfig": "workspace:*", + "@types/react": "^19", + "typescript": "^5", + "typescript-plugin-css-modules": "^5" + } +} diff --git a/webapp/packages/plugin-system-information-administration/src/LocaleService.ts b/webapp/packages/plugin-system-information-administration/src/LocaleService.ts new file mode 100644 index 0000000000..a55cfec20e --- /dev/null +++ b/webapp/packages/plugin-system-information-administration/src/LocaleService.ts @@ -0,0 +1,35 @@ +/* + * CloudBeaver - Cloud Database Manager + * Copyright (C) 2020-2024 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 { Bootstrap, injectable } from '@cloudbeaver/core-di'; +import { LocalizationService } from '@cloudbeaver/core-localization'; + +@injectable() +export class LocaleService extends Bootstrap { + constructor(private readonly localizationService: LocalizationService) { + super(); + } + + override register(): void { + this.localizationService.addProvider(this.provider.bind(this)); + } + + private async provider(locale: string) { + switch (locale) { + case 'ru': + return (await import('./locales/ru.js')).default; + case 'it': + return (await import('./locales/it.js')).default; + case 'zh': + return (await import('./locales/zh.js')).default; + case 'fr': + return (await import('./locales/fr.js')).default; + default: + return (await import('./locales/en.js')).default; + } + } +} diff --git a/webapp/packages/plugin-system-information-administration/src/SystemInformation.tsx b/webapp/packages/plugin-system-information-administration/src/SystemInformation.tsx new file mode 100644 index 0000000000..c984f80603 --- /dev/null +++ b/webapp/packages/plugin-system-information-administration/src/SystemInformation.tsx @@ -0,0 +1,71 @@ +/* + * CloudBeaver - Cloud Database Manager + * Copyright (C) 2020-2024 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 { observer } from 'mobx-react-lite'; + +import type { AdministrationItemContentProps } from '@cloudbeaver/core-administration'; +import { + Button, + ColoredContainer, + Flex, + Fill, + Group, + ObjectPropertyInfoForm, + useClipboard, + useResource, + useTranslate, + useObjectPropertyCategories, + GroupTitle, + Container, +} from '@cloudbeaver/core-blocks'; +import type { TabContainerPanelComponent } from '@cloudbeaver/core-ui'; +import { SystemInformationResource } from './SystemInformationResource.js'; + +export const SystemInformation: TabContainerPanelComponent = observer(function SystemInformation() { + const translate = useTranslate(); + const copy = useClipboard(); + const systemInformationResource = useResource(SystemInformation, SystemInformationResource, undefined); + const properties = systemInformationResource.data ?? []; + const { categories, isUncategorizedExists } = useObjectPropertyCategories(properties); + + function copyToClipboard() { + if (systemInformationResource.data) { + copy( + systemInformationResource.data + .map(property => `${property.category ? property.category + '/' : ''}${property.displayName}: ${property.value}`) + .join('\n'), + true, + ); + } + } + + return ( + + + {isUncategorizedExists && ( + + + + )} + {categories.map(category => ( + + {category} + + + ))} + + + + + + + + ); +}); diff --git a/webapp/packages/plugin-system-information-administration/src/SystemInformationBootstrap.ts b/webapp/packages/plugin-system-information-administration/src/SystemInformationBootstrap.ts new file mode 100644 index 0000000000..ec890dc318 --- /dev/null +++ b/webapp/packages/plugin-system-information-administration/src/SystemInformationBootstrap.ts @@ -0,0 +1,29 @@ +/* + * CloudBeaver - Cloud Database Manager + * Copyright (C) 2020-2024 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 { importLazyComponent } from '@cloudbeaver/core-blocks'; +import { injectable, Bootstrap } from '@cloudbeaver/core-di'; +import { ProductInfoService } from '@cloudbeaver/plugin-product-information-administration'; + +const SystemInformation = importLazyComponent(() => import('./SystemInformation.js').then(m => m.SystemInformation)); + +@injectable() +export class SystemInformationBootstrap extends Bootstrap { + constructor(private readonly productInfoService: ProductInfoService) { + super(); + } + + override register(): void { + this.productInfoService.addSubItem({ + key: 'system-information', + name: 'plugin_system_information_administration_tab_title', + panel: () => SystemInformation, + order: 2, + }); + } +} diff --git a/webapp/packages/plugin-system-information-administration/src/SystemInformationResource.ts b/webapp/packages/plugin-system-information-administration/src/SystemInformationResource.ts new file mode 100644 index 0000000000..7746984928 --- /dev/null +++ b/webapp/packages/plugin-system-information-administration/src/SystemInformationResource.ts @@ -0,0 +1,28 @@ +/* + * CloudBeaver - Cloud Database Manager + * Copyright (C) 2020-2024 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 { injectable } from '@cloudbeaver/core-di'; +import { CachedDataResource } from '@cloudbeaver/core-resource'; +import { GraphQLService, type ObjectPropertyInfo } from '@cloudbeaver/core-sdk'; +import { SessionPermissionsResource, EAdminPermission } from '@cloudbeaver/core-root'; + +@injectable() +export class SystemInformationResource extends CachedDataResource { + constructor( + private readonly graphQLService: GraphQLService, + private readonly sessionPermissionsResource: SessionPermissionsResource, + ) { + super(() => []); + this.sessionPermissionsResource.require(this, EAdminPermission.admin).outdateResource(this); + } + + protected async loader(): Promise { + const { info } = await this.graphQLService.sdk.getSystemInfo(); + return info; + } +} diff --git a/webapp/packages/plugin-system-information-administration/src/index.ts b/webapp/packages/plugin-system-information-administration/src/index.ts new file mode 100644 index 0000000000..3c3f378e09 --- /dev/null +++ b/webapp/packages/plugin-system-information-administration/src/index.ts @@ -0,0 +1,11 @@ +/* + * CloudBeaver - Cloud Database Manager + * Copyright (C) 2020-2024 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 { pluginSystemInformationAdministrationManifest } from './manifest.js'; + +export { pluginSystemInformationAdministrationManifest }; +export default pluginSystemInformationAdministrationManifest; diff --git a/webapp/packages/plugin-system-information-administration/src/locales/en.ts b/webapp/packages/plugin-system-information-administration/src/locales/en.ts new file mode 100644 index 0000000000..d843a54fdf --- /dev/null +++ b/webapp/packages/plugin-system-information-administration/src/locales/en.ts @@ -0,0 +1,8 @@ +/* + * CloudBeaver - Cloud Database Manager + * Copyright (C) 2020-2024 DBeaver Corp and others + * + * Licensed under the Apache License, Version 2.0. + * you may not use this file except in compliance with the License. + */ +export default [['plugin_system_information_administration_tab_title', 'System Information']]; diff --git a/webapp/packages/plugin-system-information-administration/src/locales/fr.ts b/webapp/packages/plugin-system-information-administration/src/locales/fr.ts new file mode 100644 index 0000000000..d843a54fdf --- /dev/null +++ b/webapp/packages/plugin-system-information-administration/src/locales/fr.ts @@ -0,0 +1,8 @@ +/* + * CloudBeaver - Cloud Database Manager + * Copyright (C) 2020-2024 DBeaver Corp and others + * + * Licensed under the Apache License, Version 2.0. + * you may not use this file except in compliance with the License. + */ +export default [['plugin_system_information_administration_tab_title', 'System Information']]; diff --git a/webapp/packages/plugin-system-information-administration/src/locales/it.ts b/webapp/packages/plugin-system-information-administration/src/locales/it.ts new file mode 100644 index 0000000000..d843a54fdf --- /dev/null +++ b/webapp/packages/plugin-system-information-administration/src/locales/it.ts @@ -0,0 +1,8 @@ +/* + * CloudBeaver - Cloud Database Manager + * Copyright (C) 2020-2024 DBeaver Corp and others + * + * Licensed under the Apache License, Version 2.0. + * you may not use this file except in compliance with the License. + */ +export default [['plugin_system_information_administration_tab_title', 'System Information']]; diff --git a/webapp/packages/plugin-system-information-administration/src/locales/ru.ts b/webapp/packages/plugin-system-information-administration/src/locales/ru.ts new file mode 100644 index 0000000000..93eb5483df --- /dev/null +++ b/webapp/packages/plugin-system-information-administration/src/locales/ru.ts @@ -0,0 +1,8 @@ +/* + * CloudBeaver - Cloud Database Manager + * Copyright (C) 2020-2024 DBeaver Corp and others + * + * Licensed under the Apache License, Version 2.0. + * you may not use this file except in compliance with the License. + */ +export default [['plugin_system_information_administration_tab_title', 'Информация о системе']]; diff --git a/webapp/packages/plugin-system-information-administration/src/locales/zh.ts b/webapp/packages/plugin-system-information-administration/src/locales/zh.ts new file mode 100644 index 0000000000..d843a54fdf --- /dev/null +++ b/webapp/packages/plugin-system-information-administration/src/locales/zh.ts @@ -0,0 +1,8 @@ +/* + * CloudBeaver - Cloud Database Manager + * Copyright (C) 2020-2024 DBeaver Corp and others + * + * Licensed under the Apache License, Version 2.0. + * you may not use this file except in compliance with the License. + */ +export default [['plugin_system_information_administration_tab_title', 'System Information']]; diff --git a/webapp/packages/plugin-system-information-administration/src/manifest.ts b/webapp/packages/plugin-system-information-administration/src/manifest.ts new file mode 100644 index 0000000000..11047b3099 --- /dev/null +++ b/webapp/packages/plugin-system-information-administration/src/manifest.ts @@ -0,0 +1,20 @@ +/* + * CloudBeaver - Cloud Database Manager + * Copyright (C) 2020-2024 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 { PluginManifest } from '@cloudbeaver/core-di'; + +export const pluginSystemInformationAdministrationManifest: PluginManifest = { + info: { + name: 'System Information Administration plugin', + }, + + providers: [ + () => import('./LocaleService.js').then(m => m.LocaleService), + () => import('./SystemInformationBootstrap.js').then(m => m.SystemInformationBootstrap), + () => import('./SystemInformationResource.js').then(m => m.SystemInformationResource), + ], +}; diff --git a/webapp/packages/plugin-system-information-administration/tsconfig.json b/webapp/packages/plugin-system-information-administration/tsconfig.json new file mode 100644 index 0000000000..23c409d260 --- /dev/null +++ b/webapp/packages/plugin-system-information-administration/tsconfig.json @@ -0,0 +1,53 @@ +{ + "extends": "@cloudbeaver/tsconfig/tsconfig.json", + "compilerOptions": { + "rootDir": "src", + "outDir": "dist", + "tsBuildInfoFile": "dist/tsconfig.tsbuildinfo", + "composite": true + }, + "references": [ + { + "path": "../core-administration" + }, + { + "path": "../core-blocks" + }, + { + "path": "../core-cli" + }, + { + "path": "../core-di" + }, + { + "path": "../core-localization" + }, + { + "path": "../core-resource" + }, + { + "path": "../core-root" + }, + { + "path": "../core-sdk" + }, + { + "path": "../core-ui" + }, + { + "path": "../plugin-product-information-administration" + } + ], + "include": [ + "__custom_mocks__/**/*", + "src/**/*", + "src/**/*.json", + "src/**/*.css", + "src/**/*.scss" + ], + "exclude": [ + "**/node_modules", + "lib/**/*", + "dist/**/*" + ] +} diff --git a/webapp/packages/plugin-version-update-administration/src/PluginBootstrap.ts b/webapp/packages/plugin-version-update-administration/src/PluginBootstrap.ts index 4eac40d15d..7a16d73200 100644 --- a/webapp/packages/plugin-version-update-administration/src/PluginBootstrap.ts +++ b/webapp/packages/plugin-version-update-administration/src/PluginBootstrap.ts @@ -27,7 +27,7 @@ export class PluginBootstrap extends Bootstrap { key: 'version-update', name: 'plugin_version_update_administration_tab_title', panel: () => VersionUpdate, - order: 2, + order: 3, }); this.versionUpdateService.registerGeneralInstruction(() => DockerUpdateInstructions); diff --git a/webapp/packages/product-default-impl/package.json b/webapp/packages/product-default-impl/package.json index 98681235c3..d6794633ac 100644 --- a/webapp/packages/product-default-impl/package.json +++ b/webapp/packages/product-default-impl/package.json @@ -78,6 +78,7 @@ "@cloudbeaver/plugin-sql-editor-screen": "workspace:*", "@cloudbeaver/plugin-sql-generator": "workspace:*", "@cloudbeaver/plugin-sso": "workspace:*", + "@cloudbeaver/plugin-system-information-administration": "workspace:*", "@cloudbeaver/plugin-task-manager": "workspace:*", "@cloudbeaver/plugin-theme": "workspace:*", "@cloudbeaver/plugin-tools-panel": "workspace:*", diff --git a/webapp/packages/product-default-impl/src/index.ts b/webapp/packages/product-default-impl/src/index.ts index dfa4ed41f3..ea43180499 100644 --- a/webapp/packages/product-default-impl/src/index.ts +++ b/webapp/packages/product-default-impl/src/index.ts @@ -72,6 +72,7 @@ import userProfileAdministration from '@cloudbeaver/plugin-user-profile-administ import { userProfileSettingsPlugin } from '@cloudbeaver/plugin-user-profile-settings'; import version from '@cloudbeaver/plugin-version'; import versionUpdate from '@cloudbeaver/plugin-version-update-administration'; +import { pluginSystemInformationAdministrationManifest } from '@cloudbeaver/plugin-system-information-administration'; import { defaultProductManifest } from './manifest.js'; @@ -139,6 +140,7 @@ const PLUGINS: PluginManifest[] = [ holidaysPluginAdministration, appLogoPlugin, appLogoPluginAdministration, + pluginSystemInformationAdministrationManifest, // must be las one to override all defaultProductManifest, ]; diff --git a/webapp/packages/product-default-impl/tsconfig.json b/webapp/packages/product-default-impl/tsconfig.json index ab50e95aa3..287d74c854 100644 --- a/webapp/packages/product-default-impl/tsconfig.json +++ b/webapp/packages/product-default-impl/tsconfig.json @@ -184,6 +184,9 @@ { "path": "../plugin-sso" }, + { + "path": "../plugin-system-information-administration" + }, { "path": "../plugin-task-manager" }, diff --git a/webapp/yarn.lock b/webapp/yarn.lock index 3025aff62d..88fab40177 100644 --- a/webapp/yarn.lock +++ b/webapp/yarn.lock @@ -4055,6 +4055,32 @@ __metadata: languageName: unknown linkType: soft +"@cloudbeaver/plugin-system-information-administration@workspace:*, @cloudbeaver/plugin-system-information-administration@workspace:packages/plugin-system-information-administration": + version: 0.0.0-use.local + resolution: "@cloudbeaver/plugin-system-information-administration@workspace:packages/plugin-system-information-administration" + dependencies: + "@cloudbeaver/core-administration": "workspace:*" + "@cloudbeaver/core-blocks": "workspace:*" + "@cloudbeaver/core-cli": "workspace:*" + "@cloudbeaver/core-di": "workspace:*" + "@cloudbeaver/core-localization": "workspace:*" + "@cloudbeaver/core-resource": "workspace:*" + "@cloudbeaver/core-root": "workspace:*" + "@cloudbeaver/core-sdk": "workspace:*" + "@cloudbeaver/core-ui": "workspace:*" + "@cloudbeaver/plugin-product-information-administration": "workspace:*" + "@cloudbeaver/tsconfig": "workspace:*" + "@types/react": "npm:^19" + mobx: "npm:^6" + mobx-react-lite: "npm:^4" + react: "npm:^19" + react-dom: "npm:^19" + tslib: "npm:^2" + typescript: "npm:^5" + typescript-plugin-css-modules: "npm:^5" + languageName: unknown + linkType: soft + "@cloudbeaver/plugin-task-manager@workspace:*, @cloudbeaver/plugin-task-manager@workspace:packages/plugin-task-manager": version: 0.0.0-use.local resolution: "@cloudbeaver/plugin-task-manager@workspace:packages/plugin-task-manager" @@ -4344,6 +4370,7 @@ __metadata: "@cloudbeaver/plugin-sql-editor-screen": "workspace:*" "@cloudbeaver/plugin-sql-generator": "workspace:*" "@cloudbeaver/plugin-sso": "workspace:*" + "@cloudbeaver/plugin-system-information-administration": "workspace:*" "@cloudbeaver/plugin-task-manager": "workspace:*" "@cloudbeaver/plugin-theme": "workspace:*" "@cloudbeaver/plugin-tools-panel": "workspace:*"