mirror of
https://github.com/dbeaver/cloudbeaver.git
synced 2026-09-24 16:04:36 +08:00
CB-4436 update session only in touchSession (#2377)
* CB-4436 update session only in touchSession
* CB-4346 adds session touch for user actions
* CB-4346 adds core-activity package
* CB-4346 fix: mobx version core-activity package
* CB-4346 ClientActivityService cleanup
* CB-4346 ClientActivityService cleanup [2]
* CB-4346 fix: client activity function contexts bind
* CB-4346 useClientActivity cleanup
* CB-4346 removed unneeded deps
* CB-4346 session warning dialog props renaming
* CB-4364 adds client activity service setsActivity method
* CB-4346 activity timers refactor
* CB-4346 adds min time for session expire setting
* CB-4346 adds update client activity each 300ms
* CB-4346 touchSession logic moved to SessionExpiredService
* СB-4346 session expire service uses session resource to touchSession
* CB-4346 code cleanup
* CB-4346 removed unused activity package from plugin session expiration
* CB-4346 SessionExpireService - removed cycle dependency
* CB-4346 code cleanup
* CB-4346 update session state if it is invalid
* CB-4346 removed arrow function from useClientActivity
* Revert "CB-4346 SessionExpireService - removed cycle dependency"
This reverts commit adba2d5eed.
* CB-4346 touchSession logic moved from SessionExpiredService to SessionResource because of cycle dependency
* CB-4346 chore: core-activity -> core-client-activity renaming
* CB-4346 chore: vars renaming
* CB-4346 refactor: removed force from sessionResource.touchSession()
* CB-4346 adds SessionTouchService
* CB-4346 chore: SESSION_EXPIRE_MIN_TIME const new name
* CB-4346 sessionTouchService license
* CB-4346 uses SessionTouchService as Dependency
* CB-4346 pr fixes
* CB-4346 adds core-localization to plugin-session-expiration
* CB-4346 adds core coreClientActivityManifest to unit tests
* CB-4346 updates ts references
* CB-4436 new update session gql api
* CB-4346 adds updateSession API call
* CB-4346 code cleanup sessionResource
* CB-4346 do not send state event for not authorized session
* CB-4346 chore: update session info with event
* CB-4346 adds update activity on touch (tablets, phones support)
* CB-4346 add debug log
* CB-4346 force update session on create
---------
Co-authored-by: s.teleshev <s.teleshev@mu.se>
Co-authored-by: Aleksei Potsetsuev <wrouds@gmail.com>
Co-authored-by: Evgenia Bezborodova <139753579+EvgeniaBzzz@users.noreply.github.com>
This commit is contained in:
co-authored by
s.teleshev
Aleksei Potsetsuev
Evgenia Bezborodova
parent
38987293a3
commit
c0c4e1870a
@@ -521,6 +521,7 @@ public class WebSession extends BaseWebSession
|
||||
HttpServletRequest request,
|
||||
HttpServletResponse response
|
||||
) throws DBWebException {
|
||||
log.debug("Update session lifetime " + getSessionId() + " for user " + getUserId());
|
||||
touchSession();
|
||||
HttpSession httpSession = request.getSession();
|
||||
this.lastRemoteAddr = request.getRemoteAddr();
|
||||
|
||||
@@ -566,7 +566,9 @@ extend type Mutation {
|
||||
closeSession: Boolean
|
||||
|
||||
# Refreshes session on server and returns its state
|
||||
touchSession: Boolean
|
||||
touchSession: Boolean @deprecated(reason: "use updateSession instead")
|
||||
# Refreshes session on server and returns session state
|
||||
updateSession: SessionInfo! @since(version: "24.0.0")
|
||||
|
||||
# Refresh session connection list
|
||||
refreshSessionConnections: Boolean
|
||||
|
||||
+5
@@ -90,9 +90,14 @@ public interface DBWServiceCore extends DBWService {
|
||||
@WebAction(authRequired = false)
|
||||
boolean closeSession(HttpServletRequest request) throws DBWebException;
|
||||
|
||||
@Deprecated
|
||||
@WebAction(authRequired = false)
|
||||
boolean touchSession(@NotNull HttpServletRequest request, @NotNull HttpServletResponse servletResponse) throws DBWebException;
|
||||
|
||||
@WebAction(authRequired = false)
|
||||
WebSession updateSession(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response)
|
||||
throws DBWebException;
|
||||
|
||||
@WebAction(authRequired = false)
|
||||
boolean refreshSessionConnections(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response) throws DBWebException;
|
||||
|
||||
|
||||
+2
@@ -99,6 +99,8 @@ public class WebServiceBindingCore extends WebServiceBindingBase<DBWServiceCore>
|
||||
.dataFetcher("closeSession", env -> getService(env).closeSession(GraphQLEndpoint.getServletRequest(env)))
|
||||
.dataFetcher("touchSession", env -> getService(env).touchSession(
|
||||
GraphQLEndpoint.getServletRequest(env), GraphQLEndpoint.getServletResponse(env)))
|
||||
.dataFetcher("updateSession", env -> getService(env).updateSession(
|
||||
GraphQLEndpoint.getServletRequest(env), GraphQLEndpoint.getServletResponse(env)))
|
||||
.dataFetcher("refreshSessionConnections", env -> getService(env).refreshSessionConnections(
|
||||
GraphQLEndpoint.getServletRequest(env), GraphQLEndpoint.getServletResponse(env)))
|
||||
.dataFetcher("changeSessionLanguage", env -> getService(env).changeSessionLanguage(getWebSession(env), env.getArgument("locale")))
|
||||
|
||||
+9
@@ -29,6 +29,7 @@ import io.cloudbeaver.server.CBApplication;
|
||||
import io.cloudbeaver.server.CBPlatform;
|
||||
import io.cloudbeaver.service.core.DBWServiceCore;
|
||||
import io.cloudbeaver.service.security.SMUtils;
|
||||
import io.cloudbeaver.service.session.WebSessionManager;
|
||||
import io.cloudbeaver.utils.WebConnectionFolderUtils;
|
||||
import io.cloudbeaver.utils.WebDataSourceUtils;
|
||||
import io.cloudbeaver.utils.WebEventUtils;
|
||||
@@ -285,6 +286,14 @@ public class WebServiceCore implements DBWServiceCore {
|
||||
return CBPlatform.getInstance().getSessionManager().touchSession(request, response);
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebSession updateSession(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response)
|
||||
throws DBWebException {
|
||||
WebSessionManager sessionManager = CBPlatform.getInstance().getSessionManager();
|
||||
sessionManager.touchSession(request, response);
|
||||
return sessionManager.getWebSession(request, response, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean refreshSessionConnections(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response) throws DBWebException {
|
||||
WebSession session = CBPlatform.getInstance().getSessionManager().getWebSession(request, response);
|
||||
|
||||
+21
-23
@@ -93,12 +93,11 @@ public class WebSessionManager {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public WebSession getWebSession(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response, boolean errorOnNoFound) throws DBWebException {
|
||||
return getWebSession(request, response, true, errorOnNoFound);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public WebSession getWebSession(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response, boolean updateInfo, boolean errorOnNoFound) throws DBWebException {
|
||||
public WebSession getWebSession(
|
||||
@NotNull HttpServletRequest request,
|
||||
@NotNull HttpServletResponse response,
|
||||
boolean errorOnNoFound
|
||||
) throws DBWebException {
|
||||
HttpSession httpSession = request.getSession(true);
|
||||
String sessionId = httpSession.getId();
|
||||
WebSession webSession;
|
||||
@@ -132,6 +131,7 @@ public class WebSessionManager {
|
||||
log.debug((restored ? "Restored " : "New ") + "web session '" + webSession.getSessionId() + "'");
|
||||
|
||||
webSession.setCacheExpired(!httpSession.isNew());
|
||||
webSession.updateInfo(request, response);
|
||||
|
||||
sessionMap.put(sessionId, webSession);
|
||||
} else {
|
||||
@@ -139,13 +139,6 @@ public class WebSessionManager {
|
||||
throw new DBWebException("Unexpected session type: " + baseWebSession.getClass().getName());
|
||||
}
|
||||
webSession = (WebSession) baseWebSession;
|
||||
if (updateInfo) {
|
||||
// Update only once per request
|
||||
if (!CommonUtils.toBoolean(request.getAttribute("sessionUpdated"))) {
|
||||
webSession.updateInfo(request, response);
|
||||
request.setAttribute("sessionUpdated", true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -328,16 +321,21 @@ public class WebSessionManager {
|
||||
*/
|
||||
public void sendSessionsStates() {
|
||||
synchronized (sessionMap) {
|
||||
for (var session : sessionMap.values()) {
|
||||
if (session instanceof WebHeadlessSession) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
session.addSessionEvent(new WSSessionStateEvent(session.getRemainingTime(), session.isValid()));
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to refresh session state: " + session.getSessionId(), e);
|
||||
}
|
||||
}
|
||||
sessionMap.values()
|
||||
.parallelStream()
|
||||
.filter(session -> {
|
||||
if (session instanceof WebSession webSession) {
|
||||
return webSession.isAuthorizedInSecurityManager();
|
||||
}
|
||||
return false;
|
||||
})
|
||||
.forEach(session -> {
|
||||
try {
|
||||
session.addSessionEvent(new WSSessionStateEvent(session.getRemainingTime(), session.isValid()));
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to refresh session state: " + session.getSessionId(), e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
"peerDependencies": {},
|
||||
"devDependencies": {
|
||||
"@cloudbeaver/core-browser": "~0.1.0",
|
||||
"@cloudbeaver/core-client-activity": "~0.1.0",
|
||||
"@cloudbeaver/core-events": "~0.1.0",
|
||||
"@cloudbeaver/core-localization": "~0.1.0",
|
||||
"@cloudbeaver/core-plugin": "~0.1.0",
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
import '@testing-library/jest-dom';
|
||||
|
||||
import { coreBrowserManifest } from '@cloudbeaver/core-browser';
|
||||
import { coreClientActivityManifest } from '@cloudbeaver/core-client-activity';
|
||||
import { coreEventsManifest } from '@cloudbeaver/core-events';
|
||||
import { coreLocalizationManifest } from '@cloudbeaver/core-localization';
|
||||
import { corePluginManifest } from '@cloudbeaver/core-plugin';
|
||||
@@ -37,6 +38,7 @@ const app = createApp(
|
||||
// coreRoutingManifest,
|
||||
// coreThemingManifest,
|
||||
coreLocalizationManifest,
|
||||
coreClientActivityManifest,
|
||||
);
|
||||
|
||||
const server = mockGraphQL(...mockAppInit(endpoint));
|
||||
|
||||
@@ -9,6 +9,9 @@
|
||||
{
|
||||
"path": "../core-browser/tsconfig.json"
|
||||
},
|
||||
{
|
||||
"path": "../core-client-activity/tsconfig.json"
|
||||
},
|
||||
{
|
||||
"path": "../core-data-context/tsconfig.json"
|
||||
},
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@cloudbeaver/core-blocks": "~0.1.0",
|
||||
"@cloudbeaver/core-client-activity": "~0.1.0",
|
||||
"@cloudbeaver/core-di": "~0.1.0",
|
||||
"@cloudbeaver/core-executor": "~0.1.0",
|
||||
"@cloudbeaver/core-localization": "~0.1.0",
|
||||
|
||||
@@ -21,6 +21,7 @@ import { useAppVersion } from '@cloudbeaver/core-version';
|
||||
|
||||
import style from './Body.m.css';
|
||||
import { useAppHeight } from './useAppHeight';
|
||||
import { useClientActivity } from './useClientActivity';
|
||||
|
||||
export const Body = observer(function Body() {
|
||||
// const serverConfigLoader = useResource(Body, ServerConfigResource, undefined);
|
||||
@@ -44,6 +45,7 @@ export const Body = observer(function Body() {
|
||||
});
|
||||
|
||||
useAppHeight();
|
||||
useClientActivity();
|
||||
|
||||
return (
|
||||
<DNDProvider>
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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 { useEffect } from 'react';
|
||||
|
||||
import { ClientActivityService } from '@cloudbeaver/core-client-activity';
|
||||
import { useService } from '@cloudbeaver/core-di';
|
||||
import { throttle } from '@cloudbeaver/core-utils';
|
||||
|
||||
const UPDATE_THROTTLE = 300;
|
||||
|
||||
export function useClientActivity() {
|
||||
const clientActivityService = useService(ClientActivityService);
|
||||
|
||||
const updateActivity = throttle(function updateActivity() {
|
||||
clientActivityService.updateActivity();
|
||||
}, UPDATE_THROTTLE);
|
||||
|
||||
function subscribeEvents() {
|
||||
document.addEventListener('mousemove', updateActivity);
|
||||
document.addEventListener('click', updateActivity);
|
||||
document.addEventListener('keydown', updateActivity);
|
||||
document.addEventListener('scroll', updateActivity);
|
||||
document.addEventListener('touchstart', updateActivity);
|
||||
}
|
||||
|
||||
function unsubscribeEvents() {
|
||||
document.removeEventListener('mousemove', updateActivity);
|
||||
document.removeEventListener('click', updateActivity);
|
||||
document.removeEventListener('keydown', updateActivity);
|
||||
document.removeEventListener('scroll', updateActivity);
|
||||
document.removeEventListener('touchstart', updateActivity);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
subscribeEvents();
|
||||
|
||||
return () => {
|
||||
unsubscribeEvents();
|
||||
};
|
||||
}, []);
|
||||
}
|
||||
@@ -9,6 +9,9 @@
|
||||
{
|
||||
"path": "../core-blocks/tsconfig.json"
|
||||
},
|
||||
{
|
||||
"path": "../core-client-activity/tsconfig.json"
|
||||
},
|
||||
{
|
||||
"path": "../core-di/tsconfig.json"
|
||||
},
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
"peerDependencies": {},
|
||||
"devDependencies": {
|
||||
"@cloudbeaver/core-browser": "~0.1.0",
|
||||
"@cloudbeaver/core-client-activity": "~0.1.0",
|
||||
"@cloudbeaver/core-events": "~0.1.0",
|
||||
"@cloudbeaver/core-localization": "~0.1.0",
|
||||
"@cloudbeaver/core-plugin": "~0.1.0",
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
import '@testing-library/jest-dom';
|
||||
|
||||
import { coreBrowserManifest } from '@cloudbeaver/core-browser';
|
||||
import { coreClientActivityManifest } from '@cloudbeaver/core-client-activity';
|
||||
import { coreEventsManifest } from '@cloudbeaver/core-events';
|
||||
import { coreLocalizationManifest } from '@cloudbeaver/core-localization';
|
||||
import { corePluginManifest } from '@cloudbeaver/core-plugin';
|
||||
@@ -40,6 +41,7 @@ const app = createApp(
|
||||
coreRoutingManifest,
|
||||
coreThemingManifest,
|
||||
coreLocalizationManifest,
|
||||
coreClientActivityManifest,
|
||||
);
|
||||
|
||||
const server = mockGraphQL(...mockAppInit(endpoint), ...mockAuthentication(endpoint));
|
||||
|
||||
@@ -14,6 +14,9 @@
|
||||
{
|
||||
"path": "../core-browser/tsconfig.json"
|
||||
},
|
||||
{
|
||||
"path": "../core-client-activity/tsconfig.json"
|
||||
},
|
||||
{
|
||||
"path": "../core-data-context/tsconfig.json"
|
||||
},
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
"peerDependencies": {},
|
||||
"devDependencies": {
|
||||
"@cloudbeaver/core-browser": "~0.1.0",
|
||||
"@cloudbeaver/core-client-activity": "~0.1.0",
|
||||
"@cloudbeaver/core-events": "~0.1.0",
|
||||
"@cloudbeaver/core-localization": "~0.1.0",
|
||||
"@cloudbeaver/core-plugin": "~0.1.0",
|
||||
|
||||
@@ -9,6 +9,7 @@ import '@testing-library/jest-dom';
|
||||
import { screen } from '@testing-library/react';
|
||||
|
||||
import { coreBrowserManifest } from '@cloudbeaver/core-browser';
|
||||
import { coreClientActivityManifest } from '@cloudbeaver/core-client-activity';
|
||||
import { coreEventsManifest } from '@cloudbeaver/core-events';
|
||||
import { coreLocalizationManifest } from '@cloudbeaver/core-localization';
|
||||
import { corePluginManifest } from '@cloudbeaver/core-plugin';
|
||||
@@ -35,6 +36,7 @@ const app = createApp(
|
||||
coreBrowserManifest,
|
||||
coreThemingManifest,
|
||||
coreLocalizationManifest,
|
||||
coreClientActivityManifest,
|
||||
);
|
||||
|
||||
mockGraphQL(...mockAppInit(endpoint));
|
||||
|
||||
@@ -12,6 +12,9 @@
|
||||
{
|
||||
"path": "../core-browser/tsconfig.json"
|
||||
},
|
||||
{
|
||||
"path": "../core-client-activity/tsconfig.json"
|
||||
},
|
||||
{
|
||||
"path": "../core-di/tsconfig.json"
|
||||
},
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
"@cloudbeaver/core-browser": "~0.1.0",
|
||||
"@cloudbeaver/core-browser-cookies": "~0.1.0",
|
||||
"@cloudbeaver/core-browser-settings": "~0.1.0",
|
||||
"@cloudbeaver/core-client-activity": "~0.1.0",
|
||||
"@cloudbeaver/core-connections": "~0.1.0",
|
||||
"@cloudbeaver/core-di": "~0.1.0",
|
||||
"@cloudbeaver/core-dialogs": "~0.1.0",
|
||||
|
||||
@@ -12,6 +12,7 @@ import { coreBlocksManifest } from '@cloudbeaver/core-blocks';
|
||||
import { coreBrowserManifest } from '@cloudbeaver/core-browser';
|
||||
import { coreBrowserCookiesManifest } from '@cloudbeaver/core-browser-cookies';
|
||||
import { coreBrowserSettingsManifest } from '@cloudbeaver/core-browser-settings';
|
||||
import { coreClientActivityManifest } from '@cloudbeaver/core-client-activity';
|
||||
import { coreConnectionsManifest } from '@cloudbeaver/core-connections';
|
||||
import { coreDIManifest, PluginManifest } from '@cloudbeaver/core-di';
|
||||
import { coreDialogsManifest } from '@cloudbeaver/core-dialogs';
|
||||
@@ -74,5 +75,6 @@ export const coreManifests: PluginManifest[] = [
|
||||
coreDialogsManifest,
|
||||
resourceManagerManifest,
|
||||
coreAppManifest,
|
||||
coreClientActivityManifest,
|
||||
coreNavigationTree,
|
||||
];
|
||||
|
||||
@@ -27,6 +27,9 @@
|
||||
{
|
||||
"path": "../core-browser/tsconfig.json"
|
||||
},
|
||||
{
|
||||
"path": "../core-client-activity/tsconfig.json"
|
||||
},
|
||||
{
|
||||
"path": "../core-connections/tsconfig.json"
|
||||
},
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
"peerDependencies": {},
|
||||
"devDependencies": {
|
||||
"@cloudbeaver/core-browser": "~0.1.0",
|
||||
"@cloudbeaver/core-client-activity": "~0.1.0",
|
||||
"@cloudbeaver/core-localization": "~0.1.0",
|
||||
"@cloudbeaver/core-plugin": "~0.1.0",
|
||||
"@cloudbeaver/core-product": "~0.1.0",
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
import '@testing-library/jest-dom';
|
||||
|
||||
import { coreBrowserManifest } from '@cloudbeaver/core-browser';
|
||||
import { coreClientActivityManifest } from '@cloudbeaver/core-client-activity';
|
||||
import { coreLocalizationManifest } from '@cloudbeaver/core-localization';
|
||||
import { corePluginManifest } from '@cloudbeaver/core-plugin';
|
||||
import { coreProductManifest } from '@cloudbeaver/core-product';
|
||||
@@ -33,6 +34,7 @@ const app = createApp(
|
||||
coreSDKManifest,
|
||||
coreSettingsManifest,
|
||||
coreLocalizationManifest,
|
||||
coreClientActivityManifest,
|
||||
);
|
||||
|
||||
const server = mockGraphQL(...mockAppInit(endpoint));
|
||||
|
||||
@@ -14,6 +14,9 @@
|
||||
{
|
||||
"path": "../core-browser/tsconfig.json"
|
||||
},
|
||||
{
|
||||
"path": "../core-client-activity/tsconfig.json"
|
||||
},
|
||||
{
|
||||
"path": "../core-di/tsconfig.json"
|
||||
},
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# production
|
||||
/lib
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
.env*
|
||||
|
||||
# debug
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"name": "@cloudbeaver/core-client-activity",
|
||||
"sideEffects": [
|
||||
"src/**/*.css",
|
||||
"src/**/*.scss",
|
||||
"public/**/*"
|
||||
],
|
||||
"version": "0.1.0",
|
||||
"description": "",
|
||||
"license": "Apache-2.0",
|
||||
"main": "dist/index.js",
|
||||
"scripts": {
|
||||
"build": "tsc -b",
|
||||
"lint": "eslint ./src/ --ext .ts,.tsx",
|
||||
"lint-fix": "eslint ./src/ --ext .ts,.tsx --fix",
|
||||
"validate-dependencies": "core-cli-validate-dependencies",
|
||||
"update-ts-references": "rimraf --glob dist && typescript-resolve-references"
|
||||
},
|
||||
"dependencies": {
|
||||
"@cloudbeaver/core-di": "~0.1.0",
|
||||
"@cloudbeaver/core-executor": "~0.1.0",
|
||||
"mobx": "^6.12.0"
|
||||
},
|
||||
"peerDependencies": {},
|
||||
"devDependencies": {
|
||||
"typescript": "^5.3.2"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* 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 { makeObservable, observable } from 'mobx';
|
||||
|
||||
import { injectable } from '@cloudbeaver/core-di';
|
||||
import { Executor, IExecutor } from '@cloudbeaver/core-executor';
|
||||
|
||||
const INACTIVE_PERIOD_TIME = 1000 * 60;
|
||||
|
||||
@injectable()
|
||||
export class ClientActivityService {
|
||||
private timer: ReturnType<typeof setTimeout> | null;
|
||||
public isActive: boolean;
|
||||
public onActiveStateChange: IExecutor<boolean>;
|
||||
|
||||
constructor() {
|
||||
this.setActivity = this.setActivity.bind(this);
|
||||
this.updateActivity = this.updateActivity.bind(this);
|
||||
this.resetActivity = this.resetActivity.bind(this);
|
||||
|
||||
this.timer = null;
|
||||
this.isActive = false;
|
||||
this.onActiveStateChange = new Executor();
|
||||
|
||||
makeObservable(this, {
|
||||
isActive: observable,
|
||||
});
|
||||
}
|
||||
|
||||
private setActivity(value: boolean) {
|
||||
this.isActive = value;
|
||||
this.onActiveStateChange.execute(value);
|
||||
}
|
||||
|
||||
resetActivity() {
|
||||
this.setActivity(false);
|
||||
|
||||
if (this.timer) {
|
||||
clearTimeout(this.timer);
|
||||
this.timer = null;
|
||||
}
|
||||
}
|
||||
|
||||
updateActivity() {
|
||||
this.setActivity(true);
|
||||
|
||||
if (this.timer) {
|
||||
clearTimeout(this.timer);
|
||||
}
|
||||
|
||||
this.timer = setTimeout(this.resetActivity, INACTIVE_PERIOD_TIME);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from './ClientActivityService';
|
||||
export * from './manifest';
|
||||
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* 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';
|
||||
|
||||
import { ClientActivityService } from './ClientActivityService';
|
||||
|
||||
export const coreClientActivityManifest: PluginManifest = {
|
||||
info: {
|
||||
name: 'Core Client Activity',
|
||||
},
|
||||
|
||||
providers: [ClientActivityService],
|
||||
};
|
||||
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"rootDir": "src",
|
||||
"outDir": "dist",
|
||||
"tsBuildInfoFile": "dist/tsconfig.tsbuildinfo"
|
||||
},
|
||||
"references": [
|
||||
{
|
||||
"path": "../core-di/tsconfig.json"
|
||||
},
|
||||
{
|
||||
"path": "../core-executor/tsconfig.json"
|
||||
}
|
||||
],
|
||||
"include": [
|
||||
"__custom_mocks__/**/*",
|
||||
"src/**/*",
|
||||
"src/**/*.json",
|
||||
"src/**/*.css",
|
||||
"src/**/*.scss"
|
||||
],
|
||||
"exclude": [
|
||||
"**/node_modules",
|
||||
"lib/**/*",
|
||||
"dist/**/*"
|
||||
]
|
||||
}
|
||||
@@ -29,6 +29,7 @@
|
||||
"peerDependencies": {},
|
||||
"devDependencies": {
|
||||
"@cloudbeaver/core-browser": "~0.1.0",
|
||||
"@cloudbeaver/core-client-activity": "~0.1.0",
|
||||
"@cloudbeaver/core-localization": "~0.1.0",
|
||||
"@cloudbeaver/core-plugin": "~0.1.0",
|
||||
"@cloudbeaver/core-product": "~0.1.0",
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
import '@testing-library/jest-dom';
|
||||
|
||||
import { coreBrowserManifest } from '@cloudbeaver/core-browser';
|
||||
import { coreClientActivityManifest } from '@cloudbeaver/core-client-activity';
|
||||
import { coreLocalizationManifest } from '@cloudbeaver/core-localization';
|
||||
import { corePluginManifest } from '@cloudbeaver/core-plugin';
|
||||
import { coreProductManifest } from '@cloudbeaver/core-product';
|
||||
@@ -33,6 +34,7 @@ const app = createApp(
|
||||
coreProductManifest,
|
||||
coreRootManifest,
|
||||
coreSDKManifest,
|
||||
coreClientActivityManifest,
|
||||
);
|
||||
|
||||
const server = mockGraphQL(...mockAppInit(endpoint));
|
||||
|
||||
@@ -9,6 +9,9 @@
|
||||
{
|
||||
"path": "../core-browser/tsconfig.json"
|
||||
},
|
||||
{
|
||||
"path": "../core-client-activity/tsconfig.json"
|
||||
},
|
||||
{
|
||||
"path": "../core-di/tsconfig.json"
|
||||
},
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
"@cloudbeaver/core-app": "~0.1.0",
|
||||
"@cloudbeaver/core-authentication": "~0.1.0",
|
||||
"@cloudbeaver/core-browser": "~0.1.0",
|
||||
"@cloudbeaver/core-client-activity": "~0.1.0",
|
||||
"@cloudbeaver/core-events": "~0.1.0",
|
||||
"@cloudbeaver/core-localization": "~0.1.0",
|
||||
"@cloudbeaver/core-plugin": "~0.1.0",
|
||||
|
||||
@@ -11,6 +11,7 @@ import { coreAppManifest } from '@cloudbeaver/core-app';
|
||||
import { coreAuthenticationManifest } from '@cloudbeaver/core-authentication';
|
||||
import { mockAuthentication } from '@cloudbeaver/core-authentication/dist/__custom_mocks__/mockAuthentication';
|
||||
import { coreBrowserManifest } from '@cloudbeaver/core-browser';
|
||||
import { coreClientActivityManifest } from '@cloudbeaver/core-client-activity';
|
||||
import { coreEventsManifest } from '@cloudbeaver/core-events';
|
||||
import { coreLocalizationManifest } from '@cloudbeaver/core-localization';
|
||||
import { corePluginManifest } from '@cloudbeaver/core-plugin';
|
||||
@@ -50,6 +51,7 @@ const app = createApp(
|
||||
coreProjectsManifest,
|
||||
coreUIManifest,
|
||||
coreViewManifest,
|
||||
coreClientActivityManifest,
|
||||
);
|
||||
|
||||
const server = mockGraphQL(...mockAppInit(endpoint), ...mockAuthentication(endpoint));
|
||||
|
||||
@@ -18,6 +18,9 @@
|
||||
{
|
||||
"path": "../core-browser/tsconfig.json"
|
||||
},
|
||||
{
|
||||
"path": "../core-client-activity/tsconfig.json"
|
||||
},
|
||||
{
|
||||
"path": "../core-data-context/tsconfig.json"
|
||||
},
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
"update-ts-references": "rimraf --glob dist && typescript-resolve-references"
|
||||
},
|
||||
"dependencies": {
|
||||
"@cloudbeaver/core-client-activity": "~0.1.0",
|
||||
"@cloudbeaver/core-di": "~0.1.0",
|
||||
"@cloudbeaver/core-executor": "~0.1.0",
|
||||
"@cloudbeaver/core-resource": "~0.1.0",
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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 { ClientActivityService } from '@cloudbeaver/core-client-activity';
|
||||
import { Dependency, injectable } from '@cloudbeaver/core-di';
|
||||
|
||||
import { SessionResource } from './SessionResource';
|
||||
|
||||
export const SESSION_TOUCH_TIME_PERIOD = 1000 * 60;
|
||||
|
||||
@injectable()
|
||||
export class SessionActivityService extends Dependency {
|
||||
private touchSessionTimer: ReturnType<typeof setTimeout> | null = null;
|
||||
|
||||
constructor(private readonly clientActivityService: ClientActivityService, private readonly sessionResource: SessionResource) {
|
||||
super();
|
||||
this.notifyClientActivity = this.notifyClientActivity.bind(this);
|
||||
this.clientActivityService.onActiveStateChange.addHandler(this.notifyClientActivity);
|
||||
}
|
||||
|
||||
private async notifyClientActivity() {
|
||||
if (this.touchSessionTimer || !this.clientActivityService.isActive) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await this.sessionResource.updateSession();
|
||||
} catch (e) {
|
||||
console.error('Session update error', e);
|
||||
}
|
||||
|
||||
this.touchSessionTimer = setTimeout(() => {
|
||||
if (this.touchSessionTimer) {
|
||||
clearTimeout(this.touchSessionTimer);
|
||||
this.touchSessionTimer = null;
|
||||
}
|
||||
}, SESSION_TOUCH_TIME_PERIOD);
|
||||
}
|
||||
}
|
||||
@@ -9,16 +9,26 @@ import { Bootstrap, injectable } from '@cloudbeaver/core-di';
|
||||
import { Executor, IExecutor } from '@cloudbeaver/core-executor';
|
||||
import { EServerErrorCode, GQLError, GraphQLService, SessionError } from '@cloudbeaver/core-sdk';
|
||||
import { errorOf } from '@cloudbeaver/core-utils';
|
||||
|
||||
export const SESSION_EXPIRE_MIN_TIME = 5 * 1000 * 60;
|
||||
|
||||
@injectable()
|
||||
export class SessionExpireService extends Bootstrap {
|
||||
expired = false;
|
||||
private isExpired = false;
|
||||
|
||||
onSessionExpire: IExecutor;
|
||||
constructor(private readonly graphQLService: GraphQLService) {
|
||||
super();
|
||||
|
||||
this.sessionExpired = this.sessionExpired.bind(this);
|
||||
|
||||
this.onSessionExpire = new Executor();
|
||||
}
|
||||
|
||||
get expired() {
|
||||
return this.isExpired;
|
||||
}
|
||||
|
||||
register(): void {
|
||||
this.graphQLService.registerInterceptor(this.sessionExpiredInterceptor.bind(this));
|
||||
}
|
||||
@@ -32,7 +42,7 @@ export class SessionExpireService extends Bootstrap {
|
||||
|
||||
const e = new SessionError('Session expired');
|
||||
this.graphQLService.blockRequests(e);
|
||||
this.expired = true;
|
||||
this.isExpired = true;
|
||||
this.onSessionExpire.execute();
|
||||
}
|
||||
|
||||
|
||||
@@ -42,6 +42,11 @@ export class SessionResource extends CachedDataResource<SessionState | null> {
|
||||
sessionInfoEventHandler.onEvent(
|
||||
ServerEventId.CbSessionState,
|
||||
event => {
|
||||
if (this.data) {
|
||||
this.data.valid = event.isValid ?? this.data.valid;
|
||||
this.data.remainingTime = event.remainingTime;
|
||||
// TODO: probably we want to call here this.dataUpdate
|
||||
}
|
||||
this.onStatusUpdate.execute(event);
|
||||
},
|
||||
undefined,
|
||||
@@ -68,13 +73,6 @@ export class SessionResource extends CachedDataResource<SessionState | null> {
|
||||
this.defaultLocale = defaultLocale;
|
||||
}
|
||||
|
||||
//! this method results in onDataUpdate handler skipping
|
||||
async refreshSilent(): Promise<void> {
|
||||
const session = await this.loader();
|
||||
|
||||
this.setData(session);
|
||||
}
|
||||
|
||||
async changeLanguage(locale: string): Promise<void> {
|
||||
if (this.data?.locale === locale) {
|
||||
return;
|
||||
@@ -95,6 +93,18 @@ export class SessionResource extends CachedDataResource<SessionState | null> {
|
||||
return session;
|
||||
}
|
||||
|
||||
async updateSession() {
|
||||
if (!this.data?.valid) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { updateSession } = await this.graphQLService.sdk.updateSession();
|
||||
|
||||
this.setData(updateSession);
|
||||
|
||||
return updateSession;
|
||||
}
|
||||
|
||||
protected setData(data: SessionState | null) {
|
||||
if (!this.action) {
|
||||
this.action = data?.actionParameters;
|
||||
|
||||
@@ -22,6 +22,7 @@ export * from './SessionDataResource';
|
||||
export * from './SessionSettingsService';
|
||||
export * from './SessionExpireService';
|
||||
export * from './SessionExpireEventService';
|
||||
export * from './SessionActivityService';
|
||||
export * from './ServerNodeService';
|
||||
export * from './WindowEventsService';
|
||||
export * from './manifest';
|
||||
|
||||
@@ -18,6 +18,7 @@ import { ServerNodeService } from './ServerNodeService';
|
||||
import { ServerSettingsResolverService } from './ServerSettingsResolverService';
|
||||
import { ServerSettingsService } from './ServerSettingsService';
|
||||
import { SessionActionService } from './SessionActionService';
|
||||
import { SessionActivityService } from './SessionActivityService';
|
||||
import { SessionDataResource } from './SessionDataResource';
|
||||
import { SessionEventSource } from './SessionEventSource';
|
||||
import { SessionExpireEventService } from './SessionExpireEventService';
|
||||
@@ -53,6 +54,7 @@ export const coreRootManifest: PluginManifest = {
|
||||
ServerConfigEventHandler,
|
||||
SessionEventSource,
|
||||
SessionInfoEventHandler,
|
||||
SessionActivityService,
|
||||
DataSynchronizationService,
|
||||
SessionPermissionEventHandler,
|
||||
ServerSettingsResolverService,
|
||||
|
||||
@@ -11,6 +11,9 @@
|
||||
"tsBuildInfoFile": "dist/tsconfig.tsbuildinfo"
|
||||
},
|
||||
"references": [
|
||||
{
|
||||
"path": "../core-client-activity/tsconfig.json"
|
||||
},
|
||||
{
|
||||
"path": "../core-di/tsconfig.json"
|
||||
},
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
mutation updateSession {
|
||||
updateSession {
|
||||
...SessionState
|
||||
}
|
||||
}
|
||||
@@ -28,6 +28,7 @@
|
||||
"peerDependencies": {},
|
||||
"devDependencies": {
|
||||
"@cloudbeaver/core-browser": "~0.1.0",
|
||||
"@cloudbeaver/core-client-activity": "~0.1.0",
|
||||
"@cloudbeaver/core-localization": "~0.1.0",
|
||||
"@cloudbeaver/core-plugin": "~0.1.0",
|
||||
"@cloudbeaver/core-product": "~0.1.0",
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
import '@testing-library/jest-dom';
|
||||
|
||||
import { coreBrowserManifest } from '@cloudbeaver/core-browser';
|
||||
import { coreClientActivityManifest } from '@cloudbeaver/core-client-activity';
|
||||
import { coreLocalizationManifest } from '@cloudbeaver/core-localization';
|
||||
import { corePluginManifest } from '@cloudbeaver/core-plugin';
|
||||
import { coreProductManifest } from '@cloudbeaver/core-product';
|
||||
@@ -33,6 +34,7 @@ const app = createApp(
|
||||
coreRootManifest,
|
||||
coreSDKManifest,
|
||||
coreBrowserManifest,
|
||||
coreClientActivityManifest,
|
||||
);
|
||||
|
||||
const server = mockGraphQL(...mockAppInit(endpoint));
|
||||
|
||||
@@ -9,6 +9,9 @@
|
||||
{
|
||||
"path": "../core-browser/tsconfig.json"
|
||||
},
|
||||
{
|
||||
"path": "../core-client-activity/tsconfig.json"
|
||||
},
|
||||
{
|
||||
"path": "../core-di/tsconfig.json"
|
||||
},
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
"peerDependencies": {},
|
||||
"devDependencies": {
|
||||
"@cloudbeaver/core-browser": "~0.1.0",
|
||||
"@cloudbeaver/core-client-activity": "~0.1.0",
|
||||
"@cloudbeaver/core-events": "~0.1.0",
|
||||
"@cloudbeaver/core-localization": "~0.1.0",
|
||||
"@cloudbeaver/core-plugin": "~0.1.0",
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
import '@testing-library/jest-dom';
|
||||
|
||||
import { coreBrowserManifest } from '@cloudbeaver/core-browser';
|
||||
import { coreClientActivityManifest } from '@cloudbeaver/core-client-activity';
|
||||
import { coreEventsManifest } from '@cloudbeaver/core-events';
|
||||
import { coreLocalizationManifest } from '@cloudbeaver/core-localization';
|
||||
import { corePluginManifest } from '@cloudbeaver/core-plugin';
|
||||
@@ -35,6 +36,7 @@ const app = createApp(
|
||||
coreSettingsManifest,
|
||||
coreBrowserManifest,
|
||||
coreLocalizationManifest,
|
||||
coreClientActivityManifest,
|
||||
);
|
||||
|
||||
const server = mockGraphQL(...mockAppInit(endpoint));
|
||||
|
||||
@@ -9,6 +9,9 @@
|
||||
{
|
||||
"path": "../core-browser/tsconfig.json"
|
||||
},
|
||||
{
|
||||
"path": "../core-client-activity/tsconfig.json"
|
||||
},
|
||||
{
|
||||
"path": "../core-di/tsconfig.json"
|
||||
},
|
||||
|
||||
+4
-2
@@ -8,7 +8,7 @@
|
||||
import { observer } from 'mobx-react-lite';
|
||||
|
||||
import { Group, GroupTitle, InputField, useResource, useTranslate } from '@cloudbeaver/core-blocks';
|
||||
import { ServerConfigResource } from '@cloudbeaver/core-root';
|
||||
import { ServerConfigResource, SESSION_EXPIRE_MIN_TIME, SESSION_TOUCH_TIME_PERIOD } from '@cloudbeaver/core-root';
|
||||
|
||||
import type { IServerConfigurationPageState } from '../IServerConfigurationPageState';
|
||||
|
||||
@@ -16,6 +16,8 @@ interface Props {
|
||||
state: IServerConfigurationPageState;
|
||||
}
|
||||
|
||||
const INPUT_MIN_SESSION_EXPIRE_TIME = Math.ceil((SESSION_EXPIRE_MIN_TIME + SESSION_TOUCH_TIME_PERIOD) / 60000) + 1;
|
||||
|
||||
export const ServerConfigurationInfoForm = observer<Props>(function ServerConfigurationInfoForm({ state }) {
|
||||
const serverConfigLoader = useResource(ServerConfigurationInfoForm, ServerConfigResource, undefined);
|
||||
const translate = useTranslate();
|
||||
@@ -41,7 +43,7 @@ export const ServerConfigurationInfoForm = observer<Props>(function ServerConfig
|
||||
type="number"
|
||||
name="sessionExpireTime"
|
||||
state={state.serverConfig}
|
||||
min={1}
|
||||
min={INPUT_MIN_SESSION_EXPIRE_TIME}
|
||||
mapState={(v: number | undefined) => String((v === 0 ? 60000 : v ?? 1800000) / 1000 / 60)}
|
||||
mapValue={(v?: string) => (v === undefined ? 30 : Number(v) || 1) * 1000 * 60}
|
||||
required
|
||||
|
||||
@@ -45,6 +45,7 @@
|
||||
"@cloudbeaver/core-app": "~0.1.0",
|
||||
"@cloudbeaver/core-authentication": "~0.1.0",
|
||||
"@cloudbeaver/core-browser": "~0.1.0",
|
||||
"@cloudbeaver/core-client-activity": "~0.1.0",
|
||||
"@cloudbeaver/core-connections": "~0.1.0",
|
||||
"@cloudbeaver/core-dialogs": "~0.1.0",
|
||||
"@cloudbeaver/core-events": "~0.1.0",
|
||||
|
||||
@@ -12,6 +12,7 @@ import { coreAppManifest } from '@cloudbeaver/core-app';
|
||||
import { coreAuthenticationManifest } from '@cloudbeaver/core-authentication';
|
||||
import { mockAuthentication } from '@cloudbeaver/core-authentication/dist/__custom_mocks__/mockAuthentication';
|
||||
import { coreBrowserManifest } from '@cloudbeaver/core-browser';
|
||||
import { coreClientActivityManifest } from '@cloudbeaver/core-client-activity';
|
||||
import { coreConnectionsManifest } from '@cloudbeaver/core-connections';
|
||||
import { coreDialogsManifest } from '@cloudbeaver/core-dialogs';
|
||||
import { coreEventsManifest } from '@cloudbeaver/core-events';
|
||||
@@ -68,6 +69,7 @@ const app = createApp(
|
||||
navigationTabsPlugin,
|
||||
objectViewerManifest,
|
||||
dataViewerManifest,
|
||||
coreClientActivityManifest,
|
||||
);
|
||||
|
||||
const server = mockGraphQL(...mockAppInit(endpoint), ...mockAuthentication(endpoint));
|
||||
|
||||
@@ -21,6 +21,9 @@
|
||||
{
|
||||
"path": "../core-browser/tsconfig.json"
|
||||
},
|
||||
{
|
||||
"path": "../core-client-activity/tsconfig.json"
|
||||
},
|
||||
{
|
||||
"path": "../core-connections/tsconfig.json"
|
||||
},
|
||||
|
||||
@@ -48,6 +48,7 @@
|
||||
"@cloudbeaver/core-app": "~0.1.0",
|
||||
"@cloudbeaver/core-authentication": "~0.1.0",
|
||||
"@cloudbeaver/core-browser": "~0.1.0",
|
||||
"@cloudbeaver/core-client-activity": "~0.1.0",
|
||||
"@cloudbeaver/core-connections": "~0.1.0",
|
||||
"@cloudbeaver/core-dialogs": "~0.1.0",
|
||||
"@cloudbeaver/core-events": "~0.1.0",
|
||||
|
||||
@@ -12,6 +12,7 @@ import { coreAppManifest } from '@cloudbeaver/core-app';
|
||||
import { coreAuthenticationManifest } from '@cloudbeaver/core-authentication';
|
||||
import { mockAuthentication } from '@cloudbeaver/core-authentication/dist/__custom_mocks__/mockAuthentication';
|
||||
import { coreBrowserManifest } from '@cloudbeaver/core-browser';
|
||||
import { coreClientActivityManifest } from '@cloudbeaver/core-client-activity';
|
||||
import { coreConnectionsManifest } from '@cloudbeaver/core-connections';
|
||||
import { coreDialogsManifest } from '@cloudbeaver/core-dialogs';
|
||||
import { coreEventsManifest } from '@cloudbeaver/core-events';
|
||||
@@ -68,6 +69,7 @@ const app = createApp(
|
||||
navigationTabsPlugin,
|
||||
objectViewerManifest,
|
||||
dataViewerManifest,
|
||||
coreClientActivityManifest,
|
||||
);
|
||||
|
||||
const server = mockGraphQL(...mockAppInit(endpoint), ...mockAuthentication(endpoint));
|
||||
|
||||
@@ -24,6 +24,9 @@
|
||||
{
|
||||
"path": "../core-browser/tsconfig.json"
|
||||
},
|
||||
{
|
||||
"path": "../core-client-activity/tsconfig.json"
|
||||
},
|
||||
{
|
||||
"path": "../core-connections/tsconfig.json"
|
||||
},
|
||||
|
||||
@@ -50,6 +50,7 @@
|
||||
"@cloudbeaver/core-app": "~0.1.0",
|
||||
"@cloudbeaver/core-authentication": "~0.1.0",
|
||||
"@cloudbeaver/core-browser": "~0.1.0",
|
||||
"@cloudbeaver/core-client-activity": "~0.1.0",
|
||||
"@cloudbeaver/core-connections": "~0.1.0",
|
||||
"@cloudbeaver/core-dialogs": "~0.1.0",
|
||||
"@cloudbeaver/core-events": "~0.1.0",
|
||||
|
||||
@@ -12,6 +12,7 @@ import { coreAppManifest } from '@cloudbeaver/core-app';
|
||||
import { coreAuthenticationManifest } from '@cloudbeaver/core-authentication';
|
||||
import { mockAuthentication } from '@cloudbeaver/core-authentication/dist/__custom_mocks__/mockAuthentication';
|
||||
import { coreBrowserManifest } from '@cloudbeaver/core-browser';
|
||||
import { coreClientActivityManifest } from '@cloudbeaver/core-client-activity';
|
||||
import { coreConnectionsManifest } from '@cloudbeaver/core-connections';
|
||||
import { coreDialogsManifest } from '@cloudbeaver/core-dialogs';
|
||||
import { coreEventsManifest } from '@cloudbeaver/core-events';
|
||||
@@ -66,6 +67,7 @@ const app = createApp(
|
||||
navigationTreePlugin,
|
||||
navigationTabsPlugin,
|
||||
objectViewerManifest,
|
||||
coreClientActivityManifest,
|
||||
);
|
||||
|
||||
const server = mockGraphQL(...mockAppInit(endpoint), ...mockAuthentication(endpoint));
|
||||
|
||||
@@ -24,6 +24,9 @@
|
||||
{
|
||||
"path": "../core-browser/tsconfig.json"
|
||||
},
|
||||
{
|
||||
"path": "../core-client-activity/tsconfig.json"
|
||||
},
|
||||
{
|
||||
"path": "../core-connections/tsconfig.json"
|
||||
},
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
"@cloudbeaver/core-app": "~0.1.0",
|
||||
"@cloudbeaver/core-authentication": "~0.1.0",
|
||||
"@cloudbeaver/core-browser": "~0.1.0",
|
||||
"@cloudbeaver/core-client-activity": "~0.1.0",
|
||||
"@cloudbeaver/core-events": "~0.1.0",
|
||||
"@cloudbeaver/core-localization": "~0.1.0",
|
||||
"@cloudbeaver/core-plugin": "~0.1.0",
|
||||
|
||||
@@ -11,6 +11,7 @@ import { coreAppManifest } from '@cloudbeaver/core-app';
|
||||
import { coreAuthenticationManifest } from '@cloudbeaver/core-authentication';
|
||||
import { mockAuthentication } from '@cloudbeaver/core-authentication/dist/__custom_mocks__/mockAuthentication';
|
||||
import { coreBrowserManifest } from '@cloudbeaver/core-browser';
|
||||
import { coreClientActivityManifest } from '@cloudbeaver/core-client-activity';
|
||||
import { coreEventsManifest } from '@cloudbeaver/core-events';
|
||||
import { coreLocalizationManifest } from '@cloudbeaver/core-localization';
|
||||
import { corePluginManifest } from '@cloudbeaver/core-plugin';
|
||||
@@ -48,6 +49,7 @@ const app = createApp(
|
||||
coreAppManifest,
|
||||
coreRoutingManifest,
|
||||
coreThemingManifest,
|
||||
coreClientActivityManifest,
|
||||
);
|
||||
|
||||
const server = mockGraphQL(...mockAppInit(endpoint), ...mockAuthentication(endpoint));
|
||||
|
||||
@@ -21,6 +21,9 @@
|
||||
{
|
||||
"path": "../core-browser/tsconfig.json"
|
||||
},
|
||||
{
|
||||
"path": "../core-client-activity/tsconfig.json"
|
||||
},
|
||||
{
|
||||
"path": "../core-di/tsconfig.json"
|
||||
},
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
"@cloudbeaver/core-app": "~0.1.0",
|
||||
"@cloudbeaver/core-authentication": "~0.1.0",
|
||||
"@cloudbeaver/core-browser": "~0.1.0",
|
||||
"@cloudbeaver/core-client-activity": "~0.1.0",
|
||||
"@cloudbeaver/core-connections": "~0.1.0",
|
||||
"@cloudbeaver/core-dialogs": "~0.1.0",
|
||||
"@cloudbeaver/core-events": "~0.1.0",
|
||||
|
||||
+2
@@ -12,6 +12,7 @@ import { coreAppManifest } from '@cloudbeaver/core-app';
|
||||
import { coreAuthenticationManifest } from '@cloudbeaver/core-authentication';
|
||||
import { mockAuthentication } from '@cloudbeaver/core-authentication/dist/__custom_mocks__/mockAuthentication';
|
||||
import { coreBrowserManifest } from '@cloudbeaver/core-browser';
|
||||
import { coreClientActivityManifest } from '@cloudbeaver/core-client-activity';
|
||||
import { coreConnectionsManifest } from '@cloudbeaver/core-connections';
|
||||
import { coreDialogsManifest } from '@cloudbeaver/core-dialogs';
|
||||
import { coreEventsManifest } from '@cloudbeaver/core-events';
|
||||
@@ -61,6 +62,7 @@ const app = createApp(
|
||||
coreNavigationTree,
|
||||
coreAppManifest,
|
||||
coreThemingManifest,
|
||||
coreClientActivityManifest,
|
||||
);
|
||||
|
||||
mockGraphQL(...mockAppInit(endpoint), ...mockAuthentication(endpoint));
|
||||
|
||||
@@ -24,6 +24,9 @@
|
||||
{
|
||||
"path": "../core-browser/tsconfig.json"
|
||||
},
|
||||
{
|
||||
"path": "../core-client-activity/tsconfig.json"
|
||||
},
|
||||
{
|
||||
"path": "../core-connections/tsconfig.json"
|
||||
},
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
"peerDependencies": {},
|
||||
"devDependencies": {
|
||||
"@cloudbeaver/core-browser": "~0.1.0",
|
||||
"@cloudbeaver/core-client-activity": "~0.1.0",
|
||||
"@cloudbeaver/core-events": "~0.1.0",
|
||||
"@cloudbeaver/core-localization": "~0.1.0",
|
||||
"@cloudbeaver/core-plugin": "~0.1.0",
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
import '@testing-library/jest-dom';
|
||||
|
||||
import { coreBrowserManifest } from '@cloudbeaver/core-browser';
|
||||
import { coreClientActivityManifest } from '@cloudbeaver/core-client-activity';
|
||||
import { coreEventsManifest } from '@cloudbeaver/core-events';
|
||||
import { coreLocalizationManifest } from '@cloudbeaver/core-localization';
|
||||
import { corePluginManifest } from '@cloudbeaver/core-plugin';
|
||||
@@ -35,6 +36,7 @@ const app = createApp(
|
||||
coreSettingsManifest,
|
||||
coreBrowserManifest,
|
||||
coreLocalizationManifest,
|
||||
coreClientActivityManifest,
|
||||
);
|
||||
|
||||
const server = mockGraphQL(...mockAppInit(endpoint));
|
||||
|
||||
@@ -12,6 +12,9 @@
|
||||
{
|
||||
"path": "../core-browser/tsconfig.json"
|
||||
},
|
||||
{
|
||||
"path": "../core-client-activity/tsconfig.json"
|
||||
},
|
||||
{
|
||||
"path": "../core-di/tsconfig.json"
|
||||
},
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
"@cloudbeaver/core-di": "~0.1.0",
|
||||
"@cloudbeaver/core-dialogs": "~0.1.0",
|
||||
"@cloudbeaver/core-events": "~0.1.0",
|
||||
"@cloudbeaver/core-localization": "~0.1.0",
|
||||
"@cloudbeaver/core-root": "~0.1.0",
|
||||
"@cloudbeaver/core-routing": "~0.1.0",
|
||||
"@cloudbeaver/core-sdk": "~0.1.0",
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
-8
@@ -6,15 +6,14 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
import { UserInfoResource } from '@cloudbeaver/core-authentication';
|
||||
import { importLazyComponent } from '@cloudbeaver/core-blocks';
|
||||
import { Bootstrap, injectable } from '@cloudbeaver/core-di';
|
||||
import { CommonDialogService, DialogueStateResult } from '@cloudbeaver/core-dialogs';
|
||||
import { ServerConfigResource, SessionExpireService, SessionResource } from '@cloudbeaver/core-root';
|
||||
import { NotificationService } from '@cloudbeaver/core-events';
|
||||
import { ServerConfigResource, SESSION_EXPIRE_MIN_TIME, SessionExpireService, SessionResource } from '@cloudbeaver/core-root';
|
||||
import { GraphQLService } from '@cloudbeaver/core-sdk';
|
||||
|
||||
import { SessionExpireWarningDialog } from './SessionExpireWarningDialog';
|
||||
|
||||
const WARN_IN = 5 * 1000 * 60;
|
||||
|
||||
const SessionExpireWarningDialog = importLazyComponent(() => import('./SessionExpireWarningDialog').then(m => m.SessionExpireWarningDialog));
|
||||
@injectable()
|
||||
export class SessionExpireWarningDialogBootstrap extends Bootstrap {
|
||||
private dialogInternalPromise: Promise<DialogueStateResult | null> | null;
|
||||
@@ -25,6 +24,7 @@ export class SessionExpireWarningDialogBootstrap extends Bootstrap {
|
||||
private readonly sessionResource: SessionResource,
|
||||
private readonly userInfoResource: UserInfoResource,
|
||||
private readonly graphQLService: GraphQLService,
|
||||
private readonly notificationService: NotificationService,
|
||||
) {
|
||||
super();
|
||||
this.dialogInternalPromise = null;
|
||||
@@ -52,12 +52,12 @@ export class SessionExpireWarningDialogBootstrap extends Bootstrap {
|
||||
|
||||
const sessionDuration = this.serverConfigResource.data?.sessionExpireTime;
|
||||
|
||||
if (this.sessionExpireService.expired || !sessionDuration || sessionDuration < WARN_IN) {
|
||||
if (this.sessionExpireService.expired || !sessionDuration || sessionDuration < SESSION_EXPIRE_MIN_TIME) {
|
||||
this.close();
|
||||
return;
|
||||
}
|
||||
|
||||
if (remainingTime !== undefined && remainingTime < WARN_IN) {
|
||||
if (remainingTime !== undefined && remainingTime <= SESSION_EXPIRE_MIN_TIME) {
|
||||
this.open();
|
||||
} else {
|
||||
this.close();
|
||||
@@ -74,7 +74,11 @@ export class SessionExpireWarningDialogBootstrap extends Bootstrap {
|
||||
const { sessionState } = await this.graphQLService.sdk.sessionState();
|
||||
|
||||
if (sessionState.valid) {
|
||||
await this.sessionResource.refreshSilent();
|
||||
try {
|
||||
await this.sessionResource.updateSession();
|
||||
} catch (e: any) {
|
||||
this.notificationService.logException(e, 'plugin_session_expiration_session_update_error');
|
||||
}
|
||||
} else {
|
||||
this.sessionExpireService.sessionExpired();
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export default [['plugin_session_expiration_session_update_error', 'Session update failed']];
|
||||
@@ -0,0 +1 @@
|
||||
export default [['plugin_session_expiration_session_update_error', 'Session update failed']];
|
||||
@@ -0,0 +1 @@
|
||||
export default [['plugin_session_expiration_session_update_error', 'Не удалось обновить сессию']];
|
||||
@@ -0,0 +1 @@
|
||||
export default [['plugin_session_expiration_session_update_error', 'Session update failed']];
|
||||
@@ -7,11 +7,12 @@
|
||||
*/
|
||||
import type { PluginManifest } from '@cloudbeaver/core-di';
|
||||
|
||||
import { LocaleService } from './LocaleService';
|
||||
import { PluginBootstrap } from './PluginBootstrap';
|
||||
import { SessionExpiredDialogBootstrap } from './SessionExpireDialog/SessionExpiredDialogBootstrap';
|
||||
import { SessionExpireWarningDialogBootstrap } from './SessionExpireWarningDialog/SessionExpireWarningDialogBootstrap';
|
||||
|
||||
export const sessionExpirationPlugin: PluginManifest = {
|
||||
info: { name: 'Session Expiration plugin' },
|
||||
providers: [PluginBootstrap, SessionExpiredDialogBootstrap, SessionExpireWarningDialogBootstrap],
|
||||
providers: [PluginBootstrap, SessionExpiredDialogBootstrap, SessionExpireWarningDialogBootstrap, LocaleService],
|
||||
};
|
||||
|
||||
@@ -21,6 +21,9 @@
|
||||
{
|
||||
"path": "../core-events/tsconfig.json"
|
||||
},
|
||||
{
|
||||
"path": "../core-localization/tsconfig.json"
|
||||
},
|
||||
{
|
||||
"path": "../core-root/tsconfig.json"
|
||||
},
|
||||
|
||||
@@ -51,6 +51,7 @@
|
||||
"@cloudbeaver/core-app": "~0.1.0",
|
||||
"@cloudbeaver/core-authentication": "~0.1.0",
|
||||
"@cloudbeaver/core-browser": "~0.1.0",
|
||||
"@cloudbeaver/core-client-activity": "~0.1.0",
|
||||
"@cloudbeaver/core-connections": "~0.1.0",
|
||||
"@cloudbeaver/core-dialogs": "~0.1.0",
|
||||
"@cloudbeaver/core-events": "~0.1.0",
|
||||
|
||||
@@ -12,6 +12,7 @@ import { coreAppManifest } from '@cloudbeaver/core-app';
|
||||
import { coreAuthenticationManifest } from '@cloudbeaver/core-authentication';
|
||||
import { mockAuthentication } from '@cloudbeaver/core-authentication/dist/__custom_mocks__/mockAuthentication';
|
||||
import { coreBrowserManifest } from '@cloudbeaver/core-browser';
|
||||
import { coreClientActivityManifest } from '@cloudbeaver/core-client-activity';
|
||||
import { coreConnectionsManifest } from '@cloudbeaver/core-connections';
|
||||
import { coreDialogsManifest } from '@cloudbeaver/core-dialogs';
|
||||
import { coreEventsManifest } from '@cloudbeaver/core-events';
|
||||
@@ -68,6 +69,7 @@ const app = createApp(
|
||||
navigationTabsPlugin,
|
||||
objectViewerManifest,
|
||||
dataViewerManifest,
|
||||
coreClientActivityManifest,
|
||||
);
|
||||
|
||||
const server = mockGraphQL(...mockAppInit(endpoint), ...mockAuthentication(endpoint));
|
||||
|
||||
@@ -24,6 +24,9 @@
|
||||
{
|
||||
"path": "../core-browser/tsconfig.json"
|
||||
},
|
||||
{
|
||||
"path": "../core-client-activity/tsconfig.json"
|
||||
},
|
||||
{
|
||||
"path": "../core-connections/tsconfig.json"
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user