diff --git a/packages/api/src/controllers/auth.js b/packages/api/src/controllers/auth.js index 0d3b81a00..fbaa74f3d 100644 --- a/packages/api/src/controllers/auth.js +++ b/packages/api/src/controllers/auth.js @@ -13,7 +13,8 @@ const { } = require('../auth/authProvider'); const storage = require('./storage'); const { decryptPasswordString } = require('../utility/crypting'); -const { createDbGateIdentitySession, getIdentitySigninUrl } = require('../utility/cloudIntf'); +const { createDbGateIdentitySession, startCloudTokenChecking } = require('../utility/cloudIntf'); +const socket = require('../utility/socket'); const logger = getLogger('auth'); @@ -138,10 +139,11 @@ module.exports = { createCloudLoginSession_meta: true, async createCloudLoginSession({ client }) { - const sid = await createDbGateIdentitySession(client); - return { - url: getIdentitySigninUrl(sid), - }; + const res = await createDbGateIdentitySession(client); + startCloudTokenChecking(res.sid, token => { + socket.emit('got-cloud-token', { token }); + }); + return res; }, authMiddleware, diff --git a/packages/api/src/utility/cloudIntf.js b/packages/api/src/utility/cloudIntf.js index 3258167e3..ee2b8301f 100644 --- a/packages/api/src/utility/cloudIntf.js +++ b/packages/api/src/utility/cloudIntf.js @@ -1,8 +1,11 @@ const axios = require('axios'); const { getExternalParamsWithLicense } = require('./authProxy'); +const { getLogger, extractErrorLogData } = require('dbgate-tools'); + +const logger = getLogger('cloudIntf'); const DBGATE_IDENTITY_URL = process.env.LOCAL_DBGATE_IDENTITY - ? 'http://localhost:3001' + ? 'http://localhost:3103' : process.env.DEVWEB || process.env.DEVMODE ? 'https://identity.dbgate.udolni.net' : 'https://identity.dbgate.io'; @@ -21,14 +24,37 @@ async function createDbGateIdentitySession(client) { }, getExternalParamsWithLicense() ); - return resp.data.sid; + return { + sid: resp.data.sid, + url: `${DBGATE_IDENTITY_URL}/api/signin/${resp.data.sid}`, + }; } -function getIdentitySigninUrl(sid) { - return `${DBGATE_IDENTITY_URL}/signin/${sid}`; +function startCloudTokenChecking(sid, callback) { + const started = Date.now(); + const interval = setInterval(async () => { + if (Date.now() - started > 60 * 1000) { + clearInterval(interval); + return; + } + + try { + const resp = await axios.default.get( + `${DBGATE_IDENTITY_URL}/api/get-token/${sid}`, + getExternalParamsWithLicense() + ); + + if (resp.data.status == 'ok') { + clearInterval(interval); + callback(resp.data.token); + } + } catch (err) { + logger.error(extractErrorLogData(err), 'Error checking cloud token'); + } + }, 500); } module.exports = { createDbGateIdentitySession, - getIdentitySigninUrl, + startCloudTokenChecking, }; diff --git a/packages/web/src/App.svelte b/packages/web/src/App.svelte index 487880df7..a0787efa0 100644 --- a/packages/web/src/App.svelte +++ b/packages/web/src/App.svelte @@ -14,7 +14,7 @@ // import { shouldWaitForElectronInitialize } from './utility/getElectron'; import { subscribeConnectionPingers } from './utility/connectionsPinger'; import { subscribePermissionCompiler } from './utility/hasPermission'; - import { apiCall, installNewVolatileConnectionListener } from './utility/api'; + import { apiCall, installNewCloudTokenListener, installNewVolatileConnectionListener } from './utility/api'; import { getConfig, getSettings, getUsedApps } from './utility/metadataLoaders'; import AppTitleProvider from './utility/AppTitleProvider.svelte'; import getElectron from './utility/getElectron'; @@ -51,6 +51,7 @@ subscribeConnectionPingers(); subscribePermissionCompiler(); installNewVolatileConnectionListener(); + installNewCloudTokenListener(); initializeAppUpdates(); } diff --git a/packages/web/src/commands/stdCommands.ts b/packages/web/src/commands/stdCommands.ts index 85c97b632..8b3418422 100644 --- a/packages/web/src/commands/stdCommands.ts +++ b/packages/web/src/commands/stdCommands.ts @@ -1,4 +1,5 @@ import { + cloudSigninToken, currentDatabase, currentTheme, emptyConnectionGroupNames, @@ -662,6 +663,15 @@ if (hasPermission('settings/change')) { }); } +registerCommand({ + id: 'cloud.logout', + category: 'Cloud', + name: 'Logout', + onClick: () => { + cloudSigninToken.set(null); + }, +}); + registerCommand({ id: 'file.exit', category: 'File', diff --git a/packages/web/src/icons/FontIcon.svelte b/packages/web/src/icons/FontIcon.svelte index 1dc0f9e98..22dc64d7b 100644 --- a/packages/web/src/icons/FontIcon.svelte +++ b/packages/web/src/icons/FontIcon.svelte @@ -39,6 +39,7 @@ 'icon minus-thick': 'mdi mdi-minus-thick', 'icon invisible-box': 'mdi mdi-minus-box-outline icon-invisible', 'icon cloud-upload': 'mdi mdi-cloud-upload', + 'icon cloud': 'mdi mdi-cloud', 'icon import': 'mdi mdi-application-import', 'icon export': 'mdi mdi-application-export', 'icon new-connection': 'mdi mdi-database-plus', diff --git a/packages/web/src/stores.ts b/packages/web/src/stores.ts index dac8c4f02..c79e0de7f 100644 --- a/packages/web/src/stores.ts +++ b/packages/web/src/stores.ts @@ -182,6 +182,8 @@ export const focusedConnectionOrDatabase = writable<{ conid: string; database?: export const focusedTreeDbKey = writable<{ key: string; root: string; type: string; text: string }>(null); +export const cloudSigninToken = writableWithStorage(null, 'cloudSigninToken'); + export const DEFAULT_OBJECT_SEARCH_SETTINGS = { pureName: true, schemaName: false, diff --git a/packages/web/src/utility/api.ts b/packages/web/src/utility/api.ts index bfe827cfe..0314cb047 100644 --- a/packages/web/src/utility/api.ts +++ b/packages/web/src/utility/api.ts @@ -14,6 +14,7 @@ import { batchDispatchCacheTriggers, dispatchCacheChange } from './cache'; import { isAdminPage, isOneOfPage } from './pageDefs'; import { openWebLink } from './simpleTools'; import { serializeJsTypesReplacer } from 'dbgate-tools'; +import { cloudSigninToken } from '../stores'; export const strmid = uuidv1(); @@ -279,6 +280,12 @@ export function installNewVolatileConnectionListener() { }); } +export function installNewCloudTokenListener() { + apiOn('got-cloud-token', async ({ token }) => { + cloudSigninToken.set(token); + }); +} + export function getAuthCategory(config) { if (config.isBasicAuth) { return 'basic'; diff --git a/packages/web/src/widgets/WidgetIconPanel.svelte b/packages/web/src/widgets/WidgetIconPanel.svelte index 0245a5f28..778f5eb8f 100644 --- a/packages/web/src/widgets/WidgetIconPanel.svelte +++ b/packages/web/src/widgets/WidgetIconPanel.svelte @@ -9,6 +9,7 @@ visibleHamburgerMenuWidget, lockedDatabaseMode, getCurrentConfig, + cloudSigninToken, } from '../stores'; import mainMenuDefinition from '../../../../app/src/mainMenuDefinition'; import hasPermission from '../utility/hasPermission'; @@ -18,6 +19,7 @@ import getElectron from '../utility/getElectron'; let domSettings; + let domCloudAccount; let domMainMenu; const widgets = [ @@ -61,9 +63,10 @@ title: 'Selected cell data detail view', }, { - icon: 'icon app', - name: 'app', - title: 'Application layers', + icon: 'icon cloud', + name: 'cloud', + title: 'DbGate Cloud', + isCloud: true, }, { icon: 'icon premium', @@ -95,7 +98,26 @@ const rect = domSettings.getBoundingClientRect(); const left = rect.right; const top = rect.bottom; - const items = [{ command: 'settings.show' }, { command: 'theme.changeTheme' }, { command: 'settings.commands' }]; + const items = [ + { command: 'settings.show' }, + { command: 'theme.changeTheme' }, + { command: 'settings.commands' }, + { + text: 'View applications', + onClick: () => { + $selectedWidget = 'app'; + $visibleWidgetSideBar = true; + }, + }, + ]; + currentDropDownMenu.set({ left, top, items }); + } + + function handleCloudAccountMenu() { + const rect = domCloudAccount.getBoundingClientRect(); + const left = rect.right; + const top = rect.bottom; + const items = [{ command: 'cloud.logout' }]; currentDropDownMenu.set({ left, top, items }); } @@ -121,6 +143,7 @@ {/if} {#each widgets .filter(x => x && hasPermission(`widgets/${x.name}`)) + .filter(x => !x.isCloud || $cloudSigninToken) .filter(x => !x.isPremiumPromo || !isProApp()) as item}
--> -