public cloud widget

This commit is contained in:
SPRINX0\prochazka
2025-05-19 16:33:04 +02:00
parent 281de5196e
commit 310f8bf6f7
10 changed files with 133 additions and 31 deletions

View 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;
},
};

View File

@@ -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) {

View File

@@ -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',
}, }
: {},
}; };
} }

View File

@@ -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,27 +112,26 @@ 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;
@@ -138,24 +139,31 @@ async function updateCloudFiles() {
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();
try {
await updateCloudFiles(); 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,
}; };

View 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} />

View File

@@ -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',

View File

@@ -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);
}

View 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>

View File

@@ -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}

View File

@@ -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}