Merge branch 'devel' of https://github.com/dbeaver/cloudbeaver into devel

This commit is contained in:
Alexander Skoblikov
2022-05-31 13:31:33 +03:00
44 changed files with 362 additions and 44 deletions
@@ -39,6 +39,8 @@
anonymousUserRole: "user",
supportsCustomConnections: false,
forwardProxy: false,
publicCredentialsSaveEnabled: true,
adminCredentialsSaveEnabled: true,
@@ -39,6 +39,8 @@
anonymousUserRole: "user",
supportsCustomConnections: false,
forwardProxy: false,
publicCredentialsSaveEnabled: true,
adminCredentialsSaveEnabled: true,
@@ -49,6 +49,8 @@ public class LocalResourceController implements RMController {
private static final Log log = Log.getLog(LocalResourceController.class);
private static final String FILE_REGEX = "(?U)[\\w.$() -]+";
private static final String PROJECT_PREFIX_GLOBAL = "g_";
private static final String PROJECT_PREFIX_SHARED = "s_";
private static final String PROJECT_PREFIX_USER = "u_";
@@ -182,6 +184,7 @@ public class LocalResourceController implements RMController {
@NotNull String resourcePath,
boolean isFolder) throws DBException
{
validateResourcePath(resourcePath);
Path targetPath = getTargetPath(projectId, resourcePath);
if (Files.exists(targetPath)) {
throw new DBException("Resource '" + resourcePath + "' already exists");
@@ -205,6 +208,7 @@ public class LocalResourceController implements RMController {
@Override
public void deleteResource(@NotNull String projectId, @NotNull String resourcePath, boolean recursive) throws DBException {
validateResourcePath(resourcePath);
Path targetPath = getTargetPath(projectId, resourcePath);
if (!Files.exists(targetPath)) {
throw new DBException("Resource '" + resourcePath + "' doesn't exists");
@@ -227,10 +231,11 @@ public class LocalResourceController implements RMController {
public RMResource[] getResourcePath(@NotNull String projectId, @NotNull String resourcePath) throws DBException {
return makeResourcePath(projectId, getTargetPath(projectId, resourcePath)).toArray(RMResource[]::new);
}
@NotNull
@Override
public byte[] getResourceContents(@NotNull String projectId, @NotNull String resourcePath) throws DBException {
validateResourcePath(resourcePath);
Path targetPath = getTargetPath(projectId, resourcePath);
if (!Files.exists(targetPath)) {
throw new DBException("Resource '" + resourcePath + "' doesn't exists");
@@ -249,6 +254,7 @@ public class LocalResourceController implements RMController {
@NotNull String resourcePath,
@NotNull byte[] data) throws DBException
{
validateResourcePath(resourcePath);
Number fileSizeLimit = WebAppUtils.getWebApplication()
.getAppConfiguration()
.getResourceQuota(WebSQLConstants.QUOTA_PROP_RM_FILE_SIZE_LIMIT);
@@ -272,6 +278,16 @@ public class LocalResourceController implements RMController {
return DEFAULT_CHANGE_ID;
}
private void validateResourcePath(String resourcePath) throws DBException {
if (resourcePath.startsWith(".")) {
throw new DBException("Resource path '" + resourcePath + "' can't start with dot");
}
if (!resourcePath.matches(FILE_REGEX)) {
String illegalCharacters = resourcePath.replaceAll(FILE_REGEX, " ").strip();
throw new DBException("Resource path '" + resourcePath + "' contains illegal characters: " + illegalCharacters);
}
}
@Override
public void addRMEventListener(RMEventListener listener) {
this.listeners.add(listener);
@@ -307,7 +323,7 @@ public class LocalResourceController implements RMController {
if (!targetPath.startsWith(projectPath)) {
throw new DBException("Invalid resource path");
}
return targetPath;
return WebAppUtils.getWebApplication().getHomeDirectory().relativize(targetPath);
} catch (InvalidPathException e) {
throw new DBException("Resource path contains invalid characters");
}
@@ -546,7 +546,7 @@ public class WebSession extends AbstractSessionPersistent implements SMSession,
}
clearAuthTokens();
this.sessionAuthContext.close();
this.user = null;
setUser(null);
if (this.sessionProject != null) {
this.sessionProject.dispose();
@@ -760,7 +760,7 @@ public class WebSession extends AbstractSessionPersistent implements SMSession,
}
if (application.isConfigurationMode() && this.user == null && newUser != null) {
//FIXME hotfix to avoid exception after external auth provider login in easy config
this.user = newUser;
setUser(newUser);
refreshUserData();
} else if (!CommonUtils.equalObjects(this.user, newUser)) {
throw new DBException("Can't authorize different users in the single session");
@@ -912,10 +912,14 @@ public class WebSession extends AbstractSessionPersistent implements SMSession,
this.adminSecurityController = application.getAdminSecurityController(this);
this.rmController = application.getResourceController(this);
this.user = tokenInfo.getUserId() == null ? null : new WebUser(securityController.getUserById(tokenInfo.getUserId()));
setUser(tokenInfo.getUserId() == null ? null : new WebUser(securityController.getUserById(tokenInfo.getUserId())));
refreshUserData();
}
private synchronized void setUser(@Nullable WebUser user) {
this.user = user;
}
@Override
public SMCredentials getActiveUserCredentials() {
return smCredentials;
@@ -26,7 +26,6 @@ Require-Bundle: org.eclipse.core.runtime;visibility:=reexport,
io.cloudbeaver.model;visibility:=reexport,
io.cloudbeaver.service.security;visibility:=reexport
Export-Package: io.cloudbeaver,
io.cloudbeaver.auth.provider.fa,
io.cloudbeaver.model,
io.cloudbeaver.model.resources,
io.cloudbeaver.model.user,
@@ -33,6 +33,7 @@ import org.jkiss.dbeaver.model.access.DBAAuthCredentials;
import org.jkiss.dbeaver.model.app.DBPDataSourceRegistry;
import org.jkiss.dbeaver.model.connection.DBPConnectionConfiguration;
import org.jkiss.dbeaver.model.connection.DBPDriver;
import org.jkiss.dbeaver.model.impl.auth.AuthModelDatabaseNativeCredentials;
import org.jkiss.dbeaver.model.navigator.DBNBrowseSettings;
import org.jkiss.dbeaver.model.navigator.DBNModel;
import org.jkiss.dbeaver.model.net.DBWHandlerConfiguration;
@@ -245,6 +246,7 @@ public class WebServiceUtils extends WebCommonUtils {
if (authProperties == null) {
authProperties = new LinkedHashMap<>();
}
authProperties.replace(AuthModelDatabaseNativeCredentials.PROP_USER_PASSWORD, null);
} else {
if (authProperties == null) {
// No changes
@@ -46,6 +46,7 @@ public class CBAppConfig extends BaseWebAppConfiguration {
private boolean supportsConnectionBrowser;
private boolean supportsUserWorkspaces;
private boolean enableReverseProxyAuth;
private boolean forwardProxy;
private boolean publicCredentialsSaveEnabled;
private boolean adminCredentialsSaveEnabled;
@@ -79,6 +80,7 @@ public class CBAppConfig extends BaseWebAppConfiguration {
this.authConfiguration = new LinkedHashMap<>();
this.resourceQuotas = new LinkedHashMap<>();
this.enableReverseProxyAuth = false;
this.forwardProxy = false;
}
public CBAppConfig(CBAppConfig src) {
@@ -98,6 +100,7 @@ public class CBAppConfig extends BaseWebAppConfiguration {
this.authConfiguration = new LinkedHashMap<>(src.authConfiguration);
this.resourceQuotas = new LinkedHashMap<>(src.resourceQuotas);
this.enableReverseProxyAuth = src.enableReverseProxyAuth;
this.forwardProxy = src.forwardProxy;
}
@@ -310,4 +313,11 @@ public class CBAppConfig extends BaseWebAppConfiguration {
return enableReverseProxyAuth;
}
////////////////////////////////////////////
// Forward proxy
public boolean isEnabledForwardProxy() {
return forwardProxy;
}
}
@@ -778,6 +778,7 @@ public class CBApplication extends BaseWebApplication {
JSONUtils.field(json, "publicCredentialsSaveEnabled", appConfig.isPublicCredentialsSaveEnabled());
JSONUtils.field(json, "adminCredentialsSaveEnabled", appConfig.isAdminCredentialsSaveEnabled());
JSONUtils.field(json, "enableReverseProxyAuth", appConfig.isEnabledReverseProxyAuth());
JSONUtils.field(json, "forwardProxy", appConfig.isEnabledForwardProxy());
JSONUtils.field(json, CBConstants.PARAM_RESOURCE_MANAGER_ENABLED, appConfig.isResourceManagerEnabled());
Map<String, Object> resourceQuotas = appConfig.getResourceQuotas();
@@ -93,12 +93,17 @@ public class CBJettyServer {
}
boolean forwardProxy = application.getAppConfiguration().isEnabledForwardProxy();
{
// HTTP config
for(Connector y : server.getConnectors()) {
for(ConnectionFactory x : y.getConnectionFactories()) {
if(x instanceof HttpConnectionFactory) {
((HttpConnectionFactory)x).getHttpConfiguration().setSendServerVersion(false);
HttpConfiguration httpConfiguration = ((HttpConnectionFactory)x).getHttpConfiguration();
httpConfiguration.setSendServerVersion(false);
if (forwardProxy) {
httpConfiguration.addCustomizer(new ForwardedRequestCustomizer());
}
}
}
}
@@ -168,6 +168,7 @@ public class WebDatabaseObjectInfo {
}
private static void getObjectFeatures(DBSObject object, List<String> features) {
boolean isDiagramSupported = true;
if (object instanceof DBPScriptObject) features.add(OBJECT_FEATURE_SCRIPT);
if (object instanceof DBPScriptObjectExt) features.add(OBJECT_FEATURE_SCRIPT_EXTENDED);
if (object instanceof DBSDataContainer) {
@@ -175,9 +176,14 @@ public class WebDatabaseObjectInfo {
if (((DBSDataContainer) object).isFeatureSupported(DBSDataContainer.FEATURE_DATA_FILTER)) {
features.add(OBJECT_FEATURE_DATA_CONTAINER_SUPPORTS_FILTERS);
}
if (((DBSDataContainer) object).isFeatureSupported(DBSDataContainer.FEATURE_KEY_VALUE)) {
isDiagramSupported = false;
}
}
if (object instanceof DBSDataManipulator) features.add(OBJECT_FEATURE_DATA_MANIPULATOR);
if (object instanceof DBSEntity && !(object instanceof DBSDataType)) features.add(OBJECT_FEATURE_ENTITY);
if (object instanceof DBSEntity && !(object instanceof DBSDataType) && isDiagramSupported) {
features.add(OBJECT_FEATURE_ENTITY);
}
if (object instanceof DBSSchema) features.add(OBJECT_FEATURE_SCHEMA);
if (object instanceof DBSCatalog) features.add(OBJECT_FEATURE_CATALOG);
if (object instanceof DBSObjectContainer) {
@@ -154,7 +154,9 @@ public class WebNavigatorNodeInfo {
features.add("canDelete");
}
if (objectManager instanceof DBEObjectRenamer && ((DBEObjectRenamer) objectManager).canRenameObject(object)) {
features.add("canRename");
if (!object.getDataSource().getContainer().getNavigatorSettings().isShowOnlyEntities()) {
features.add("canRename");
}
}
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

@@ -84,7 +84,7 @@ export const NavigationNode: NavigationNodeComponent = observer(function Navigat
onOpen={handleOpen}
onSelect={handleSelect}
>
<DNDPreview data={dndData} src="/icons/empty.svg" />
{/* <DNDPreview data={dndData} src="/icons/empty.svg" /> */}
<Control ref={setRef} dragging={dndData.state.isDragging} node={node} />
{(expanded || expandedExternal) && <NavigationNodeNested nodeId={node.id} path={path} component={component} />}
</TreeNode>
@@ -327,13 +327,7 @@ export class NavNodeManagerService extends Bootstrap {
const nodeInfo = await contexts.getContext(this.navigationNavNodeContext);
if (NodeManagerUtils.isDatabaseObject(nodeInfo.nodeId) && nodeInfo.connection) {
let connection: Connection | null;
try {
connection = await this.connectionsManagerService.requireConnection(nodeInfo.connection.id);
} catch (exception: any) {
this.notificationService.logException(exception);
throw exception;
}
const connection = await this.connectionsManagerService.requireConnection(nodeInfo.connection.id);
if (!connection?.connected) {
throw new Error('Connection not established');
@@ -1,8 +1,12 @@
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta content="text/html; charset=utf-8" />
<link rel="alternate icon" type="image/png" href="favicon.png" sizes="16х16">
<link rel="icon" type="image/png" sizes="32x32" href="favicon-32x32.png">
<link rel="icon" type="image/svg+xml" href="favicon.svg">
<link rel="apple-touch-icon" sizes="180x180" href="apple-touch-icon.png">
<link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500" rel="preload" as="font"
crossorigin="anonymous" />
<link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700" rel="stylesheet" crossorigin="anonymous" />
@@ -35,10 +35,14 @@ export class ConnectionsManagerService {
}
async requireConnection(connectionId: string | null = null): Promise<Connection | null> {
const context = await this.connectionExecutor.execute(connectionId);
const connection = context.getContext(this.connectionContext);
try {
const context = await this.connectionExecutor.execute(connectionId);
const connection = context.getContext(this.connectionContext);
return connection.connection;
return connection.connection;
} catch {
return null;
}
}
async addOpenedConnection(connection: Connection): Promise<void> {
@@ -50,7 +54,7 @@ export class ConnectionsManagerService {
objectCatalogId?: string,
objectSchemaId?: string
): ObjectContainer | undefined {
if (objectCatalogId){
if (objectCatalogId) {
const objectContainers = this.containerContainers.getCatalogData(connectionId, objectCatalogId);
if (!objectContainers) {
@@ -60,7 +64,7 @@ export class ConnectionsManagerService {
if (!objectSchemaId) {
return objectContainers.catalog;
}
return objectContainers.schemaList.find(
objectContainer => objectContainer.name === objectSchemaId
);
@@ -94,6 +94,10 @@ export class ServerConfigResource extends CachedDataResource<ServerConfig | null
return this.data?.resourceQuotas ?? {};
}
get resourceManagerEnabled() {
return this.update.resourceManagerEnabled ?? this.data?.resourceManagerEnabled ?? false;
}
isFeatureEnabled(feature: string, serverSide = false): boolean {
if (serverSide) {
return this.data?.enabledFeatures.includes(feature) || false;
@@ -117,6 +121,8 @@ export class ServerConfigResource extends CachedDataResource<ServerConfig | null
|| this.update.anonymousAccessEnabled !== this.data.anonymousAccessEnabled
|| this.update.resourceManagerEnabled !== this.data.resourceManagerEnabled
|| this.update.adminCredentialsSaveEnabled !== this.data.adminCredentialsSaveEnabled
|| this.update.publicCredentialsSaveEnabled !== this.data.publicCredentialsSaveEnabled
@@ -219,6 +225,8 @@ export class ServerConfigResource extends CachedDataResource<ServerConfig | null
this.update.adminCredentialsSaveEnabled = serverConfig.adminCredentialsSaveEnabled;
this.update.publicCredentialsSaveEnabled = serverConfig.publicCredentialsSaveEnabled;
this.update.resourceManagerEnabled = serverConfig.resourceManagerEnabled;
this.update.customConnectionsEnabled = serverConfig.supportsCustomConnections;
this.update.enabledAuthProviders = [...serverConfig.enabledAuthProviders];
this.update.enabledFeatures = [...serverConfig.enabledFeatures];
@@ -17,6 +17,8 @@ query serverConfig {
adminCredentialsSaveEnabled
publicCredentialsSaveEnabled
resourceManagerEnabled
licenseRequired
licenseValid
+4 -1
View File
@@ -1341,6 +1341,7 @@ export interface ServerConfig {
productInfo: ProductInfo;
publicCredentialsSaveEnabled?: Maybe<Scalars['Boolean']>;
redirectOnFederatedAuth?: Maybe<Scalars['Boolean']>;
resourceManagerEnabled?: Maybe<Scalars['Boolean']>;
resourceQuotas: Scalars['Object'];
rootURI: Scalars['String'];
serverURL: Scalars['String'];
@@ -1365,6 +1366,7 @@ export interface ServerConfigInput {
enabledAuthProviders?: InputMaybe<Array<Scalars['ID']>>;
enabledFeatures?: InputMaybe<Array<Scalars['ID']>>;
publicCredentialsSaveEnabled?: InputMaybe<Scalars['Boolean']>;
resourceManagerEnabled?: InputMaybe<Scalars['Boolean']>;
serverName?: InputMaybe<Scalars['String']>;
serverURL?: InputMaybe<Scalars['String']>;
sessionExpireTime?: InputMaybe<Scalars['Int']>;
@@ -2292,7 +2294,7 @@ export type ReadSessionLogQuery = { log: Array<{ time?: any; type: string; messa
export type ServerConfigQueryVariables = Exact<{ [key: string]: never }>;
export type ServerConfigQuery = { serverConfig: { name: string; version: string; workspaceId: string; serverURL: string; rootURI: string; hostName: string; productConfiguration: any; supportsCustomConnections?: boolean; supportsConnectionBrowser?: boolean; supportsWorkspaces?: boolean; sessionExpireTime?: number; anonymousAccessEnabled?: boolean; adminCredentialsSaveEnabled?: boolean; publicCredentialsSaveEnabled?: boolean; licenseRequired: boolean; licenseValid: boolean; configurationMode?: boolean; developmentMode?: boolean; redirectOnFederatedAuth?: boolean; enabledFeatures: Array<string>; enabledAuthProviders: Array<string>; resourceQuotas: any; disabledDrivers: Array<string>; supportedLanguages: Array<{ isoCode: string; displayName?: string; nativeName?: string }>; defaultNavigatorSettings: { showSystemObjects: boolean; showUtilityObjects: boolean; showOnlyEntities: boolean; mergeEntities: boolean; hideFolders: boolean; hideSchemas: boolean; hideVirtualModel: boolean }; productInfo: { id: string; version: string; latestVersionInfo?: string; name: string; description?: string; buildTime: string; releaseTime: string; licenseInfo?: string } } };
export type ServerConfigQuery = { serverConfig: { name: string; version: string; workspaceId: string; serverURL: string; rootURI: string; hostName: string; productConfiguration: any; supportsCustomConnections?: boolean; supportsConnectionBrowser?: boolean; supportsWorkspaces?: boolean; sessionExpireTime?: number; anonymousAccessEnabled?: boolean; adminCredentialsSaveEnabled?: boolean; publicCredentialsSaveEnabled?: boolean; resourceManagerEnabled?: boolean; licenseRequired: boolean; licenseValid: boolean; configurationMode?: boolean; developmentMode?: boolean; redirectOnFederatedAuth?: boolean; enabledFeatures: Array<string>; enabledAuthProviders: Array<string>; resourceQuotas: any; disabledDrivers: Array<string>; supportedLanguages: Array<{ isoCode: string; displayName?: string; nativeName?: string }>; defaultNavigatorSettings: { showSystemObjects: boolean; showUtilityObjects: boolean; showOnlyEntities: boolean; mergeEntities: boolean; hideFolders: boolean; hideSchemas: boolean; hideVirtualModel: boolean }; productInfo: { id: string; version: string; latestVersionInfo?: string; name: string; description?: string; buildTime: string; releaseTime: string; licenseInfo?: string } } };
export type SessionPermissionsQueryVariables = Exact<{ [key: string]: never }>;
@@ -3627,6 +3629,7 @@ export const ServerConfigDocument = `
anonymousAccessEnabled
adminCredentialsSaveEnabled
publicCredentialsSaveEnabled
resourceManagerEnabled
licenseRequired
licenseValid
configurationMode
@@ -126,7 +126,11 @@ export const ServerConfigurationPage: AdministrationItemContentComponent = obser
<ServerConfigurationConfigurationForm serverConfig={service.state.serverConfig} />
<ServerConfigurationNavigatorViewForm configs={service.state} />
<ServerConfigurationFeaturesForm state={service.state} configurationWizard={configurationWizard} />
<Placeholder container={service.pluginsContainer} />
<Placeholder
container={service.pluginsContainer}
configurationWizard={configurationWizard}
state={service.state}
/>
</Group>
<Placeholder
container={service.configurationContainer}
@@ -45,7 +45,7 @@ export class ServerConfigurationService {
readonly saveTask: IExecutor<IServerConfigSaveData>;
readonly validationTask: IExecutor<IServerConfigSaveData>;
readonly configurationContainer: PlaceholderContainer<IConfigurationPlaceholderProps>;
readonly pluginsContainer: PlaceholderContainer;
readonly pluginsContainer: PlaceholderContainer<IConfigurationPlaceholderProps>;
private done: boolean;
private stateLinked: boolean;
@@ -77,11 +77,6 @@ export const ObjectFolders = observer<IProps>(function ObjectFolders({ tab }) {
});
const folders = navNodeViewService.getFolders(nodeId) || [];
function openFolder(tabData: ITabData) {
navNodeManagerService.navToNode(nodeId, parentId, tabData.tabId);
}
const wrongFolder = (
!folders.includes(folderId)
&& folders.length > 0
@@ -94,6 +89,15 @@ export const ObjectFolders = observer<IProps>(function ObjectFolders({ tab }) {
folderId = folders[0];
}
function openFolder(tabData: ITabData) {
if (tabData.tabId === folderId) {
return;
}
navNodeManagerService.navToNode(nodeId, parentId, tabData.tabId);
}
useEffect(() => {
if (wrongFolder) {
navNodeManagerService.navToNode(nodeId, parentId, folderId);
@@ -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*
@@ -0,0 +1,45 @@
{
"name": "@cloudbeaver/plugin-resource-manager-administration",
"sideEffects": false,
"version": "0.1.0",
"description": "",
"license": "Apache-2.0",
"main": "lib/index.js",
"module": "lib/index.js",
"types": "src/index.ts",
"scripts": {
"prebuild": "rimraf lib/*",
"build": "webpack --config ../../configs/webpack.plugin.config.js",
"postbuild": "tsc",
"lint": "eslint ./src/ --ext .ts,.tsx",
"lint-fix": "eslint ./src/ --ext .ts,.tsx --fix"
},
"peerDependencies": {
"@cloudbeaver/core-administration": "~0.1.0",
"@cloudbeaver/core-app": "~0.1.0",
"@cloudbeaver/core-authentication": "~0.1.0",
"@cloudbeaver/core-blocks": "~0.1.0",
"@cloudbeaver/core-connections": "~0.1.0",
"@cloudbeaver/core-di": "~0.1.0",
"@cloudbeaver/core-dialogs": "~0.1.0",
"@cloudbeaver/core-events": "~0.1.0",
"@cloudbeaver/core-executor": "~0.1.0",
"@cloudbeaver/core-extensions": "~0.1.0",
"@cloudbeaver/core-localization": "~0.1.0",
"@cloudbeaver/core-notifications": "~0.1.0",
"@cloudbeaver/core-plugin": "~0.1.0",
"@cloudbeaver/core-product": "~0.1.0",
"@cloudbeaver/core-root": "~0.1.0",
"@cloudbeaver/core-routing": "~0.1.0",
"@cloudbeaver/core-sdk": "~0.1.0",
"@cloudbeaver/core-settings": "~0.1.0",
"@cloudbeaver/core-theming": "~0.1.0",
"@cloudbeaver/core-ui": "~0.1.0",
"@cloudbeaver/core-utils": "~0.1.0",
"@cloudbeaver/core-view": "~0.1.0",
"react": "~17.x.x",
"mobx": "~6.x.x",
"mobx-react-lite": "~3.x.x",
"reshadow": "~0.x.x"
}
}
@@ -0,0 +1,36 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2022 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();
}
register(): void | Promise<void> {
this.localizationService.addProvider(this.provider.bind(this));
}
load(): void | Promise<void> { }
private async provider(locale: string) {
switch (locale) {
case 'ru':
return (await import('./locales/ru')).default;
case 'it':
return (await import('./locales/it')).default;
case 'zh':
return (await import('./locales/zh')).default;
default:
return (await import('./locales/en')).default;
}
}
}
@@ -0,0 +1,54 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2022 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 { NotificationService } from '@cloudbeaver/core-events';
import { ExecutorInterrupter, IExecutionContextProvider } from '@cloudbeaver/core-executor';
import { ServerConfigResource } from '@cloudbeaver/core-root';
import { ILoadConfigData, ServerConfigurationService } from '@cloudbeaver/plugin-administration';
import { ResourceManagerSettings } from './ResourceManagerSettings';
@injectable()
export class PluginBootstrap extends Bootstrap {
constructor(
private readonly serverConfigurationService: ServerConfigurationService,
private readonly serverConfigResource: ServerConfigResource,
private readonly notificationService: NotificationService
) {
super();
this.loadConfigHandler = this.loadConfigHandler.bind(this);
}
register(): void | Promise<void> {
this.serverConfigurationService.pluginsContainer.add(ResourceManagerSettings, 0);
this.serverConfigurationService.loadConfigTask.addHandler(this.loadConfigHandler);
}
async load(): Promise<void> { }
private async loadConfigHandler(data: ILoadConfigData, contexts: IExecutionContextProvider<ILoadConfigData>) {
if (!data.reset) {
return;
}
try {
const config = await this.serverConfigResource.load();
if (!config) {
return;
}
data.state.serverConfig.resourceManagerEnabled = config.resourceManagerEnabled;
} catch (exception) {
ExecutorInterrupter.interrupt(contexts);
this.notificationService.logException(exception as any, 'Can\'t load server configuration');
}
}
}
@@ -0,0 +1,35 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2022 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 styled from 'reshadow';
import { BASE_CONTAINERS_STYLES, GroupTitle, PlaceholderComponent, Switch } from '@cloudbeaver/core-blocks';
import { useTranslate } from '@cloudbeaver/core-localization';
import type { IConfigurationPlaceholderProps } from '@cloudbeaver/plugin-administration';
export const ResourceManagerSettings: PlaceholderComponent<IConfigurationPlaceholderProps> = observer(
function ResourceManagerSettings({ state }) {
const translate = useTranslate();
return styled(BASE_CONTAINERS_STYLES)(
<>
<GroupTitle>{translate('plugin_resource_manager_title')}</GroupTitle>
<Switch
name='resourceManagerEnabled'
state={state.serverConfig}
description={translate('plugin_resource_manager_administration_settings_enable_description')}
mod={['primary']}
small
autoHide
>
{translate('plugin_resource_manager_administration_settings_enable')}
</Switch>
</>
);
});
@@ -0,0 +1,3 @@
import { resourceManagerAdministrationPlugin } from './manifest';
export default resourceManagerAdministrationPlugin;
@@ -0,0 +1,4 @@
export default [
['plugin_resource_manager_administration_settings_enable', 'Enable Resource manager'],
['plugin_resource_manager_administration_settings_enable_description', 'Enable Resource manager functionality'],
];
@@ -0,0 +1,4 @@
export default [
['plugin_resource_manager_administration_settings_enable', 'Enable Resource manager'],
['plugin_resource_manager_administration_settings_enable_description', 'Enable Resource manager functionality'],
];
@@ -0,0 +1,4 @@
export default [
['plugin_resource_manager_administration_settings_enable', 'Включить Менеджер ресурсов'],
['plugin_resource_manager_administration_settings_enable_description', 'Включает функционал Менеджера ресурсов'],
];
@@ -0,0 +1,4 @@
export default [
['plugin_resource_manager_administration_settings_enable', 'Enable Resource manager'],
['plugin_resource_manager_administration_settings_enable_description', 'Enable Resource manager functionality'],
];
@@ -0,0 +1,20 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2022 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';
import { LocaleService } from './LocaleService';
import { PluginBootstrap } from './PluginBootstrap';
export const resourceManagerAdministrationPlugin: PluginManifest = {
info: { name: 'Resource manager administration plugin' },
providers: [
PluginBootstrap,
LocaleService,
],
};
@@ -0,0 +1,20 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"rootDir": "src",
"outDir": "lib",
"composite": true
},
"include": [
"src"
],
"exclude": [
"**/node_modules",
"**/.*/"
],
"references": [
{
"path": "../*"
}
]
}
@@ -10,20 +10,19 @@ import { computed, makeObservable, observable } from 'mobx';
import { AuthInfoService } from '@cloudbeaver/core-authentication';
import { injectable } from '@cloudbeaver/core-di';
import { ResourceManagerSettingsService } from './ResourceManagerSettingsService';
import { ServerConfigResource } from '@cloudbeaver/core-root';
@injectable()
export class ResourceManagerService {
get enabled() {
return !this.resourceManagerSettingsService.settings.getValue('disabled') && !!this.authInfoService.userInfo;
return this.serverConfigResource.resourceManagerEnabled && !!this.authInfoService.userInfo;
}
panelEnabled: boolean;
constructor(
private readonly resourceManagerSettingsService: ResourceManagerSettingsService,
private readonly authInfoService: AuthInfoService,
private readonly serverConfigResource: ServerConfigResource
) {
this.togglePanel = this.togglePanel.bind(this);
@@ -32,7 +32,7 @@ interface State {
submit: () => Promise<void>;
}
const regex = /^(?!\.)[\p{L}\w\-$.\s]+$/u;
const regex = /^(?!\.)[\p{L}\w\-$.\s()]+$/u;
export const SaveScriptDialog: DialogComponent<Payload, string> = observer(function SaveScriptDialog({
payload,
@@ -14,5 +14,5 @@ export default [
['plugin_resource_manager_sync_script_error', "Failed to sync editor's query"],
['plugin_resource_manager_script_not_found_title', 'Failed to find the associated script'],
['plugin_resource_manager_script_not_found_message', 'The script was probably removed or renamed. The last known value will be used'],
['plugin_resource_manager_script_name_invalid_characters_message', 'Script\'s name may contain the following symbols "_-$." and can\'t start with a dot'],
['plugin_resource_manager_script_name_invalid_characters_message', 'Script\'s name may contain the following symbols "_-$.()" and can\'t start with a dot'],
];
@@ -14,5 +14,5 @@ export default [
['plugin_resource_manager_sync_script_error', "Failed to sync editor's query"],
['plugin_resource_manager_script_not_found_title', 'Failed to find the associated script'],
['plugin_resource_manager_script_not_found_message', 'The script was probably removed or renamed. The last known value will be used'],
['plugin_resource_manager_script_name_invalid_characters_message', 'Script\'s name may contain the following symbols "_-$." and can\'t start with a dot'],
['plugin_resource_manager_script_name_invalid_characters_message', 'Script\'s name may contain the following symbols "_-$.()" and can\'t start with a dot'],
];
@@ -14,5 +14,5 @@ export default [
['plugin_resource_manager_sync_script_error', 'Не удалось синхронизировать данные редактора'],
['plugin_resource_manager_script_not_found_title', 'Не удалось найти привязанный скрипт'],
['plugin_resource_manager_script_not_found_message', 'Возможно скрипт был удален или переименован. Будет использовано последнее известное значение'],
['plugin_resource_manager_script_name_invalid_characters_message', 'Имя скрипта может содержать следующие символы "_-$." и не может начинаться с точки'],
['plugin_resource_manager_script_name_invalid_characters_message', 'Имя скрипта может содержать следующие символы "_-$.()" и не может начинаться с точки'],
];
@@ -14,5 +14,5 @@ export default [
['plugin_resource_manager_sync_script_error', '同步查询失败'],
['plugin_resource_manager_script_not_found_title', '找不到关联的脚本'],
['plugin_resource_manager_script_not_found_message', '脚本可能被删除或重命名,将使用最后的有效值'],
['plugin_resource_manager_script_name_invalid_characters_message', 'Script\'s name may contain the following symbols "_-$." and can\'t start with a dot'],
['plugin_resource_manager_script_name_invalid_characters_message', 'Script\'s name may contain the following symbols "_-$.()" and can\'t start with a dot'],
];
@@ -14,8 +14,8 @@ import { SessionExpireService } from '@cloudbeaver/core-root';
import { createPath } from '@cloudbeaver/core-utils';
import { ActionService, ACTION_SAVE, DATA_CONTEXT_MENU, MenuService } from '@cloudbeaver/core-view';
import { NavResourceNodeService, RESOURCE_NODE_TYPE, SaveScriptDialog, ResourceManagerService, ProjectsResource, RESOURCES_NODE_PATH } from '@cloudbeaver/plugin-resource-manager';
import { DATA_CONTEXT_SQL_EDITOR_STATE, SqlEditorService, SQL_EDITOR_ACTIONS_MENU } from '@cloudbeaver/plugin-sql-editor';
import { SqlEditorNavigatorService, SqlEditorTabService } from '@cloudbeaver/plugin-sql-editor-navigation-tab';
import { DATA_CONTEXT_SQL_EDITOR_STATE, getSqlEditorName, SqlEditorService, SQL_EDITOR_ACTIONS_MENU } from '@cloudbeaver/plugin-sql-editor';
import { SqlEditorNavigatorService } from '@cloudbeaver/plugin-sql-editor-navigation-tab';
import { isScript } from './isScript';
import { SCRIPT_EXTENSION } from './SCRIPT_EXTENSION';
@@ -27,7 +27,6 @@ export class PluginBootstrap extends Bootstrap {
private readonly navNodeManagerService: NavNodeManagerService,
private readonly navTreeResource: NavTreeResource,
private readonly navResourceNodeService: NavResourceNodeService,
private readonly sqlEditorTabService: SqlEditorTabService,
private readonly sqlEditorService: SqlEditorService,
private readonly navNodeInfoResource: NavNodeInfoResource,
private readonly navigationTabsService: NavigationTabsService,
@@ -64,9 +63,9 @@ export class PluginBootstrap extends Bootstrap {
const tab = this.navigationTabsService.currentTab;
if (action === ACTION_SAVE) {
const tabName = this.sqlEditorTabService.getName(state);
const name = getSqlEditorName(state);
const result = await this.commonDialogService.open(SaveScriptDialog, {
defaultScriptName: tabName,
defaultScriptName: name,
});
if (result !== DialogueStateResult.Rejected && result !== DialogueStateResult.Resolved) {
@@ -19,6 +19,7 @@ import localization from '@cloudbeaver/plugin-localization';
import objectViewer from '@cloudbeaver/plugin-object-viewer';
import productPlugin from '@cloudbeaver/plugin-product';
import resourceManager from '@cloudbeaver/plugin-resource-manager';
import resourceManagerAdministration from '@cloudbeaver/plugin-resource-manager-administration';
import settingsMenu from '@cloudbeaver/plugin-settings-menu';
import settingsMenuAdministration from '@cloudbeaver/plugin-settings-menu-administration';
import sqlEditor from '@cloudbeaver/plugin-sql-editor';
@@ -60,6 +61,7 @@ const PLUGINS: PluginManifest[] = [
settingsMenuAdministration,
help,
resourceManager,
resourceManagerAdministration,
sqlEditorResource,
];