mirror of
https://github.com/DeNNiiInc/dbgate.git
synced 2026-04-29 21:33:58 +00:00
public cloud widget
This commit is contained in:
15
packages/api/src/controllers/cloud.js
Normal file
15
packages/api/src/controllers/cloud.js
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
const fs = require('fs-extra');
|
||||||
|
const _ = require('lodash');
|
||||||
|
const path = require('path');
|
||||||
|
const { appdir } = require('../utility/directories');
|
||||||
|
const socket = require('../utility/socket');
|
||||||
|
const connections = require('./connections');
|
||||||
|
const { getPublicCloudFiles } = require('../utility/cloudIntf');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
publicFiles_meta: true,
|
||||||
|
async publicFiles() {
|
||||||
|
const res = await getPublicCloudFiles();
|
||||||
|
return res;
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -27,6 +27,7 @@ const plugins = require('./controllers/plugins');
|
|||||||
const files = require('./controllers/files');
|
const files = require('./controllers/files');
|
||||||
const scheduler = require('./controllers/scheduler');
|
const scheduler = require('./controllers/scheduler');
|
||||||
const queryHistory = require('./controllers/queryHistory');
|
const queryHistory = require('./controllers/queryHistory');
|
||||||
|
const cloud = require('./controllers/cloud');
|
||||||
const onFinished = require('on-finished');
|
const onFinished = require('on-finished');
|
||||||
const processArgs = require('./utility/processArgs');
|
const processArgs = require('./utility/processArgs');
|
||||||
|
|
||||||
@@ -223,6 +224,7 @@ function useAllControllers(app, electron) {
|
|||||||
useController(app, electron, '/query-history', queryHistory);
|
useController(app, electron, '/query-history', queryHistory);
|
||||||
useController(app, electron, '/apps', apps);
|
useController(app, electron, '/apps', apps);
|
||||||
useController(app, electron, '/auth', auth);
|
useController(app, electron, '/auth', auth);
|
||||||
|
useController(app, electron, '/cloud', cloud);
|
||||||
}
|
}
|
||||||
|
|
||||||
function setElectronSender(electronSender) {
|
function setElectronSender(electronSender) {
|
||||||
|
|||||||
@@ -36,11 +36,13 @@ async function callRefactorSqlQueryApi(query, task, structure, dialect) {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getExternalParamsWithLicense() {
|
function getExternalParamsWithLicense(isPost = false) {
|
||||||
return {
|
return {
|
||||||
headers: {
|
headers: isPost
|
||||||
'Content-Type': 'application/json',
|
? {
|
||||||
},
|
'Content-Type': 'application/json',
|
||||||
|
}
|
||||||
|
: {},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,8 +8,10 @@ const { datadir } = require('./directories');
|
|||||||
const platformInfo = require('./platformInfo');
|
const platformInfo = require('./platformInfo');
|
||||||
const connections = require('../controllers/connections');
|
const connections = require('../controllers/connections');
|
||||||
const { isProApp } = require('./checkLicense');
|
const { isProApp } = require('./checkLicense');
|
||||||
|
const socket = require('./socket');
|
||||||
|
|
||||||
const logger = getLogger('cloudIntf');
|
const logger = getLogger('cloudIntf');
|
||||||
|
|
||||||
let cloudFiles = null;
|
let cloudFiles = null;
|
||||||
|
|
||||||
const DBGATE_IDENTITY_URL = process.env.LOCAL_DBGATE_IDENTITY
|
const DBGATE_IDENTITY_URL = process.env.LOCAL_DBGATE_IDENTITY
|
||||||
@@ -30,7 +32,7 @@ async function createDbGateIdentitySession(client) {
|
|||||||
{
|
{
|
||||||
client,
|
client,
|
||||||
},
|
},
|
||||||
getExternalParamsWithLicense()
|
getExternalParamsWithLicense(true)
|
||||||
);
|
);
|
||||||
return {
|
return {
|
||||||
sid: resp.data.sid,
|
sid: resp.data.sid,
|
||||||
@@ -49,7 +51,7 @@ function startCloudTokenChecking(sid, callback) {
|
|||||||
try {
|
try {
|
||||||
const resp = await axios.default.get(
|
const resp = await axios.default.get(
|
||||||
`${DBGATE_IDENTITY_URL}/api/get-token/${sid}`,
|
`${DBGATE_IDENTITY_URL}/api/get-token/${sid}`,
|
||||||
getExternalParamsWithLicense()
|
getExternalParamsWithLicense(false)
|
||||||
);
|
);
|
||||||
|
|
||||||
if (resp.data.status == 'ok') {
|
if (resp.data.status == 'ok') {
|
||||||
@@ -110,52 +112,58 @@ async function collectCloudFilesSearchTags() {
|
|||||||
async function updateCloudFiles() {
|
async function updateCloudFiles() {
|
||||||
let lastCloudFilesTags;
|
let lastCloudFilesTags;
|
||||||
try {
|
try {
|
||||||
const fileContent = await fs.readFile(path.join(datadir(), 'cloud-files-tags.json'), 'utf-8');
|
lastCloudFilesTags = await fs.readFile(path.join(datadir(), 'cloud-files-tags.txt'), 'utf-8');
|
||||||
cloudFiles = JSON.parse(fileContent);
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
lastCloudFilesTags = [];
|
lastCloudFilesTags = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const tags = (await collectCloudFilesSearchTags()).join(',');
|
||||||
let lastCheckedTm = 0;
|
let lastCheckedTm = 0;
|
||||||
if (_.isEqual(cloudFiles, lastCloudFilesTags) && cloudFiles.length > 0) {
|
if (tags == lastCloudFilesTags && cloudFiles.length > 0) {
|
||||||
lastCheckedTm = _.max(cloudFiles.map(x => x.modifiedTm));
|
lastCheckedTm = _.max(cloudFiles.map(x => parseInt(x.modifiedTm)));
|
||||||
}
|
}
|
||||||
const tags = await collectCloudFilesSearchTags();
|
|
||||||
|
|
||||||
const resp = await axios.default.post(
|
logger.info({ tags, lastCheckedTm }, 'Downloading cloud files');
|
||||||
`${DBGATE_CLOUD_URL}/public-cloud-updates`,
|
|
||||||
{
|
const resp = await axios.default.get(
|
||||||
lastCheckedTm,
|
`${DBGATE_CLOUD_URL}/public-cloud-updates?lastCheckedTm=${lastCheckedTm}&tags=${tags}`,
|
||||||
tags,
|
getExternalParamsWithLicense(false)
|
||||||
},
|
|
||||||
getExternalParamsWithLicense()
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
logger.info(`Downloaded ${resp.data.length} cloud files`);
|
||||||
|
|
||||||
const filesByPath = _.keyBy(cloudFiles, 'path');
|
const filesByPath = _.keyBy(cloudFiles, 'path');
|
||||||
for(const file of resp.data) {
|
for (const file of resp.data) {
|
||||||
filesByPath[file.path] = file;
|
filesByPath[file.path] = file;
|
||||||
}
|
}
|
||||||
|
|
||||||
cloudFiles = Object.values(filesByPath);
|
cloudFiles = Object.values(filesByPath);
|
||||||
|
|
||||||
await fs.writeFile(
|
await fs.writeFile(path.join(datadir(), 'cloud-files.jsonl'), cloudFiles.map(x => JSON.stringify(x)).join('\n'));
|
||||||
path.join(datadir(), 'cloud-files.jsonl'),
|
await fs.writeFile(path.join(datadir(), 'cloud-files-tags.txt'), tags);
|
||||||
cloudFiles.map(x => JSON.stringify(x)).join('\n')
|
|
||||||
);
|
|
||||||
|
|
||||||
await fs.writeFile(
|
socket.emitChanged(`public-cloud-changed`);
|
||||||
path.join(datadir(), 'cloud-files-tags.json'),
|
|
||||||
JSON.stringify(tags)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function startCloudFiles() {
|
async function startCloudFiles() {
|
||||||
await loadCloudFiles();
|
await loadCloudFiles();
|
||||||
await updateCloudFiles();
|
try {
|
||||||
|
await updateCloudFiles();
|
||||||
|
} catch (err) {
|
||||||
|
logger.error(extractErrorLogData(err), 'Error updating cloud files');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getPublicCloudFiles() {
|
||||||
|
if (!loadCloudFiles) {
|
||||||
|
await loadCloudFiles();
|
||||||
|
}
|
||||||
|
return cloudFiles;
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
createDbGateIdentitySession,
|
createDbGateIdentitySession,
|
||||||
startCloudTokenChecking,
|
startCloudTokenChecking,
|
||||||
startCloudFiles,
|
startCloudFiles,
|
||||||
|
getPublicCloudFiles,
|
||||||
};
|
};
|
||||||
|
|||||||
29
packages/web/src/appobj/CloudFileAppObject.svelte
Normal file
29
packages/web/src/appobj/CloudFileAppObject.svelte
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
<script lang="ts" context="module">
|
||||||
|
import AppObjectCore from './AppObjectCore.svelte';
|
||||||
|
|
||||||
|
export const extractKey = data => data.path;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import openNewTab from '../utility/openNewTab';
|
||||||
|
import { copyTextToClipboard } from '../utility/clipboard';
|
||||||
|
import { showModal } from '../modals/modalTools';
|
||||||
|
import ConfirmModal from '../modals/ConfirmModal.svelte';
|
||||||
|
import getElectron from '../utility/getElectron';
|
||||||
|
import FavoriteModal from '../modals/FavoriteModal.svelte';
|
||||||
|
import { apiCall } from '../utility/api';
|
||||||
|
|
||||||
|
export let data;
|
||||||
|
|
||||||
|
function createMenu() {
|
||||||
|
return [];
|
||||||
|
// return [
|
||||||
|
// { text: 'Delete', onClick: handleDelete },
|
||||||
|
// { text: 'Edit', onClick: editFavorite },
|
||||||
|
// { text: 'Edit JSON definition', onClick: editFavoriteJson },
|
||||||
|
// !electron && data.urlPath && { text: 'Copy link', onClick: copyLink },
|
||||||
|
// ];
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<AppObjectCore {...$$restProps} {data} icon={'img sql-file'} title={data.title} menu={createMenu} />
|
||||||
@@ -40,6 +40,8 @@
|
|||||||
'icon invisible-box': 'mdi mdi-minus-box-outline icon-invisible',
|
'icon invisible-box': 'mdi mdi-minus-box-outline icon-invisible',
|
||||||
'icon cloud-upload': 'mdi mdi-cloud-upload',
|
'icon cloud-upload': 'mdi mdi-cloud-upload',
|
||||||
'icon cloud': 'mdi mdi-cloud',
|
'icon cloud': 'mdi mdi-cloud',
|
||||||
|
'icon cloud-public': 'mdi mdi-cloud-search',
|
||||||
|
'icon cloud-logged': 'mdi mdi-cloud-key',
|
||||||
'icon import': 'mdi mdi-application-import',
|
'icon import': 'mdi mdi-application-import',
|
||||||
'icon export': 'mdi mdi-application-export',
|
'icon export': 'mdi mdi-application-export',
|
||||||
'icon new-connection': 'mdi mdi-database-plus',
|
'icon new-connection': 'mdi mdi-database-plus',
|
||||||
|
|||||||
@@ -166,6 +166,12 @@ const authTypesLoader = ({ engine }) => ({
|
|||||||
errorValue: null,
|
errorValue: null,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const publicCloudFilesLoader = () => ({
|
||||||
|
url: 'cloud/public-files',
|
||||||
|
params: {},
|
||||||
|
reloadTrigger: { key: `public-cloud-changed` },
|
||||||
|
});
|
||||||
|
|
||||||
async function getCore(loader, args) {
|
async function getCore(loader, args) {
|
||||||
const { url, params, reloadTrigger, transform, onLoaded, errorValue } = loader(args);
|
const { url, params, reloadTrigger, transform, onLoaded, errorValue } = loader(args);
|
||||||
const key = stableStringify({ url, ...params });
|
const key = stableStringify({ url, ...params });
|
||||||
@@ -456,3 +462,10 @@ export function getSchemaList(args) {
|
|||||||
export function useSchemaList(args) {
|
export function useSchemaList(args) {
|
||||||
return useCore(schemaListLoader, args);
|
return useCore(schemaListLoader, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getPublicCloudFiles(args) {
|
||||||
|
return getCore(publicCloudFilesLoader, args);
|
||||||
|
}
|
||||||
|
export function usePublicCloudFiles(args = {}) {
|
||||||
|
return useCore(publicCloudFilesLoader, args);
|
||||||
|
}
|
||||||
|
|||||||
25
packages/web/src/widgets/CloudItemsWidget.svelte
Normal file
25
packages/web/src/widgets/CloudItemsWidget.svelte
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import SavedFilesList from './SavedFilesList.svelte';
|
||||||
|
|
||||||
|
import WidgetColumnBar from './WidgetColumnBar.svelte';
|
||||||
|
import WidgetColumnBarItem from './WidgetColumnBarItem.svelte';
|
||||||
|
|
||||||
|
import AppObjectList from '../appobj/AppObjectList.svelte';
|
||||||
|
import * as cloudFileAppObject from '../appobj/CloudFileAppObject.svelte';
|
||||||
|
import { usePublicCloudFiles } from '../utility/metadataLoaders';
|
||||||
|
import { _t } from '../translations';
|
||||||
|
|
||||||
|
import WidgetsInnerContainer from './WidgetsInnerContainer.svelte';
|
||||||
|
|
||||||
|
$: publicFiles = usePublicCloudFiles();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<WidgetColumnBar>
|
||||||
|
<WidgetColumnBarItem title="Public cloud" name="cloud" height="70%" storageName="publicCloudItems">
|
||||||
|
<WidgetsInnerContainer>
|
||||||
|
<AppObjectList list={$publicFiles || []} module={cloudFileAppObject} groupFunc={data => data.folder} />
|
||||||
|
</WidgetsInnerContainer>
|
||||||
|
</WidgetColumnBarItem>
|
||||||
|
|
||||||
|
<WidgetColumnBarItem title="Favorites" name="favorites" storageName="favoritesWidget"></WidgetColumnBarItem>
|
||||||
|
</WidgetColumnBar>
|
||||||
@@ -9,6 +9,7 @@
|
|||||||
import AppWidget from './AppWidget.svelte';
|
import AppWidget from './AppWidget.svelte';
|
||||||
import AdminMenuWidget from './AdminMenuWidget.svelte';
|
import AdminMenuWidget from './AdminMenuWidget.svelte';
|
||||||
import AdminPremiumPromoWidget from './AdminPremiumPromoWidget.svelte';
|
import AdminPremiumPromoWidget from './AdminPremiumPromoWidget.svelte';
|
||||||
|
import CloudItemsWidget from './CloudItemsWidget.svelte';
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<DatabaseWidget hidden={$visibleSelectedWidget != 'database'} />
|
<DatabaseWidget hidden={$visibleSelectedWidget != 'database'} />
|
||||||
@@ -37,3 +38,6 @@
|
|||||||
{#if $visibleSelectedWidget == 'premium'}
|
{#if $visibleSelectedWidget == 'premium'}
|
||||||
<AdminPremiumPromoWidget />
|
<AdminPremiumPromoWidget />
|
||||||
{/if}
|
{/if}
|
||||||
|
{#if $visibleSelectedWidget == 'cloud'}
|
||||||
|
<CloudItemsWidget />
|
||||||
|
{/if}
|
||||||
|
|||||||
@@ -67,6 +67,8 @@
|
|||||||
name: 'cloud',
|
name: 'cloud',
|
||||||
title: 'DbGate Cloud',
|
title: 'DbGate Cloud',
|
||||||
isCloud: true,
|
isCloud: true,
|
||||||
|
iconSignedIn: 'icon cloud-logged',
|
||||||
|
iconPublic: 'icon cloud-public',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: 'icon premium',
|
icon: 'icon premium',
|
||||||
@@ -143,8 +145,8 @@
|
|||||||
{/if}
|
{/if}
|
||||||
{#each widgets
|
{#each widgets
|
||||||
.filter(x => x && hasPermission(`widgets/${x.name}`))
|
.filter(x => x && hasPermission(`widgets/${x.name}`))
|
||||||
.filter(x => !x.isCloud || $cloudSigninToken)
|
.filter(x => !x.isPremiumPromo || !isProApp())
|
||||||
.filter(x => !x.isPremiumPromo || !isProApp()) as item}
|
.map(x => (x.isCloud ? { ...x, icon: $cloudSigninToken ? x.iconSignedIn : x.iconPublic } : x)) as item}
|
||||||
<div
|
<div
|
||||||
class="wrapper"
|
class="wrapper"
|
||||||
class:selected={item.name == $visibleSelectedWidget}
|
class:selected={item.name == $visibleSelectedWidget}
|
||||||
|
|||||||
Reference in New Issue
Block a user