mirror of
https://github.com/dbeaver/cloudbeaver.git
synced 2026-09-24 16:04:36 +08:00
Merge branch 'devel' of https://github.com/dbeaver/cloudbeaver into devel
This commit is contained in:
@@ -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,
|
||||
|
||||
|
||||
+18
-2
@@ -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");
|
||||
}
|
||||
|
||||
+7
-3
@@ -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();
|
||||
|
||||
+6
-1
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+7
-1
@@ -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) {
|
||||
|
||||
+3
-1
@@ -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 |
+1
-1
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
+5
-1
@@ -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}
|
||||
|
||||
+1
-1
@@ -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');
|
||||
}
|
||||
}
|
||||
}
|
||||
+35
@@ -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,
|
||||
];
|
||||
|
||||
|
||||
Reference in New Issue
Block a user