mirror of
https://github.com/DeNNiiInc/dbgate.git
synced 2026-04-17 22:36:01 +00:00
SYNC: Merge pull request #12 from dbgate/feature/team-files
This commit is contained in:
@@ -10,7 +10,13 @@ function getTokenSecret() {
|
||||
return tokenSecret;
|
||||
}
|
||||
|
||||
function getStaticTokenSecret() {
|
||||
// TODO static not fixed
|
||||
return '14813c43-a91b-4ad1-9dcd-a81bd7dbb05f';
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getTokenLifetime,
|
||||
getTokenSecret,
|
||||
getStaticTokenSecret,
|
||||
};
|
||||
|
||||
@@ -21,7 +21,7 @@ module.exports = {
|
||||
const filePermissions = await loadFilePermissionsFromRequest(req);
|
||||
|
||||
for (const file of await fs.readdir(dir)) {
|
||||
if (!hasPermission(`all-files`, loadedPermissions)) {
|
||||
if (!hasPermission(`all-disk-files`, loadedPermissions)) {
|
||||
const role = getFilePermissionRole('apps', file, filePermissions);
|
||||
if (role == 'deny') continue;
|
||||
}
|
||||
|
||||
@@ -8,11 +8,13 @@ const path = require('path');
|
||||
const { handleProcessCommunication } = require('../utility/processComm');
|
||||
const processArgs = require('../utility/processArgs');
|
||||
const { appdir } = require('../utility/directories');
|
||||
const { getLogger, extractErrorLogData } = require('dbgate-tools');
|
||||
const { getLogger, extractErrorLogData, removeSqlFrontMatter } = require('dbgate-tools');
|
||||
const pipeForkLogs = require('../utility/pipeForkLogs');
|
||||
const config = require('./config');
|
||||
const { sendToAuditLog } = require('../utility/auditlog');
|
||||
const { testStandardPermission, testDatabaseRolePermission } = require('../utility/hasPermission');
|
||||
const { getStaticTokenSecret } = require('../auth/authCommon');
|
||||
const jwt = require('jsonwebtoken');
|
||||
|
||||
const logger = getLogger('sessions');
|
||||
|
||||
@@ -95,7 +97,7 @@ module.exports = {
|
||||
socket.emit(`session-initialize-file-${jslid}`);
|
||||
},
|
||||
|
||||
handle_ping() { },
|
||||
handle_ping() {},
|
||||
|
||||
create_meta: true,
|
||||
async create({ conid, database }) {
|
||||
@@ -149,12 +151,23 @@ module.exports = {
|
||||
|
||||
executeQuery_meta: true,
|
||||
async executeQuery({ sesid, sql, autoCommit, autoDetectCharts, limitRows, frontMatter }, req) {
|
||||
await testStandardPermission('dbops/query', req);
|
||||
let useTokenIsOk = false;
|
||||
if (frontMatter?.useToken) {
|
||||
const decoded = jwt.verify(frontMatter.useToken, getStaticTokenSecret());
|
||||
if (decoded?.['contentHash'] == crypto.createHash('md5').update(removeSqlFrontMatter(sql)).digest('hex')) {
|
||||
useTokenIsOk = true;
|
||||
}
|
||||
}
|
||||
if (!useTokenIsOk) {
|
||||
await testStandardPermission('dbops/query', req);
|
||||
}
|
||||
const session = this.opened.find(x => x.sesid == sesid);
|
||||
if (!session) {
|
||||
throw new Error('Invalid session');
|
||||
}
|
||||
await testDatabaseRolePermission(session.conid, session.database, 'run_script', req);
|
||||
if (!useTokenIsOk) {
|
||||
await testDatabaseRolePermission(session.conid, session.database, 'run_script', req);
|
||||
}
|
||||
|
||||
sendToAuditLog(req, {
|
||||
category: 'dbop',
|
||||
|
||||
@@ -29,6 +29,8 @@ const files = require('./controllers/files');
|
||||
const scheduler = require('./controllers/scheduler');
|
||||
const queryHistory = require('./controllers/queryHistory');
|
||||
const cloud = require('./controllers/cloud');
|
||||
const teamFiles = require('./controllers/teamFiles');
|
||||
|
||||
const onFinished = require('on-finished');
|
||||
const processArgs = require('./utility/processArgs');
|
||||
|
||||
@@ -264,6 +266,7 @@ function useAllControllers(app, electron) {
|
||||
useController(app, electron, '/apps', apps);
|
||||
useController(app, electron, '/auth', auth);
|
||||
useController(app, electron, '/cloud', cloud);
|
||||
useController(app, electron, '/team-files', teamFiles);
|
||||
}
|
||||
|
||||
function setElectronSender(electronSender) {
|
||||
|
||||
@@ -784,49 +784,6 @@ module.exports = {
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"pureName": "roles",
|
||||
"columns": [
|
||||
{
|
||||
"pureName": "roles",
|
||||
"columnName": "id",
|
||||
"dataType": "int",
|
||||
"autoIncrement": true,
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"pureName": "roles",
|
||||
"columnName": "name",
|
||||
"dataType": "varchar(250)",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"foreignKeys": [],
|
||||
"primaryKey": {
|
||||
"pureName": "roles",
|
||||
"constraintType": "primaryKey",
|
||||
"constraintName": "PK_roles",
|
||||
"columns": [
|
||||
{
|
||||
"columnName": "id"
|
||||
}
|
||||
]
|
||||
},
|
||||
"preloadedRows": [
|
||||
{
|
||||
"id": -1,
|
||||
"name": "anonymous-user"
|
||||
},
|
||||
{
|
||||
"id": -2,
|
||||
"name": "logged-user"
|
||||
},
|
||||
{
|
||||
"id": -3,
|
||||
"name": "superadmin"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"pureName": "role_connections",
|
||||
"columns": [
|
||||
@@ -1243,6 +1200,129 @@ module.exports = {
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"pureName": "role_team_files",
|
||||
"columns": [
|
||||
{
|
||||
"pureName": "role_team_files",
|
||||
"columnName": "id",
|
||||
"dataType": "int",
|
||||
"autoIncrement": true,
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"pureName": "role_team_files",
|
||||
"columnName": "role_id",
|
||||
"dataType": "int",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"pureName": "role_team_files",
|
||||
"columnName": "team_file_id",
|
||||
"dataType": "int",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"pureName": "role_team_files",
|
||||
"columnName": "allow_read",
|
||||
"dataType": "int",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"pureName": "role_team_files",
|
||||
"columnName": "allow_write",
|
||||
"dataType": "int",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"pureName": "role_team_files",
|
||||
"columnName": "allow_use",
|
||||
"dataType": "int",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"foreignKeys": [
|
||||
{
|
||||
"constraintType": "foreignKey",
|
||||
"constraintName": "FK_role_team_files_role_id",
|
||||
"pureName": "role_team_files",
|
||||
"refTableName": "roles",
|
||||
"deleteAction": "CASCADE",
|
||||
"columns": [
|
||||
{
|
||||
"columnName": "role_id",
|
||||
"refColumnName": "id"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"constraintType": "foreignKey",
|
||||
"constraintName": "FK_role_team_files_team_file_id",
|
||||
"pureName": "role_team_files",
|
||||
"refTableName": "team_files",
|
||||
"deleteAction": "CASCADE",
|
||||
"columns": [
|
||||
{
|
||||
"columnName": "team_file_id",
|
||||
"refColumnName": "id"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"pureName": "role_team_files",
|
||||
"constraintType": "primaryKey",
|
||||
"constraintName": "PK_role_team_files",
|
||||
"columns": [
|
||||
{
|
||||
"columnName": "id"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"pureName": "roles",
|
||||
"columns": [
|
||||
{
|
||||
"pureName": "roles",
|
||||
"columnName": "id",
|
||||
"dataType": "int",
|
||||
"autoIncrement": true,
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"pureName": "roles",
|
||||
"columnName": "name",
|
||||
"dataType": "varchar(250)",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"foreignKeys": [],
|
||||
"primaryKey": {
|
||||
"pureName": "roles",
|
||||
"constraintType": "primaryKey",
|
||||
"constraintName": "PK_roles",
|
||||
"columns": [
|
||||
{
|
||||
"columnName": "id"
|
||||
}
|
||||
]
|
||||
},
|
||||
"preloadedRows": [
|
||||
{
|
||||
"id": -1,
|
||||
"name": "anonymous-user"
|
||||
},
|
||||
{
|
||||
"id": -2,
|
||||
"name": "logged-user"
|
||||
},
|
||||
{
|
||||
"id": -3,
|
||||
"name": "superadmin"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"pureName": "table_permission_roles",
|
||||
"columns": [
|
||||
@@ -1362,39 +1442,111 @@ module.exports = {
|
||||
]
|
||||
},
|
||||
{
|
||||
"pureName": "users",
|
||||
"pureName": "team_file_types",
|
||||
"columns": [
|
||||
{
|
||||
"pureName": "users",
|
||||
"pureName": "team_file_types",
|
||||
"columnName": "id",
|
||||
"dataType": "int",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"pureName": "team_file_types",
|
||||
"columnName": "name",
|
||||
"dataType": "varchar(250)",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"foreignKeys": [],
|
||||
"primaryKey": {
|
||||
"pureName": "team_file_types",
|
||||
"constraintType": "primaryKey",
|
||||
"constraintName": "PK_team_file_types",
|
||||
"columns": [
|
||||
{
|
||||
"columnName": "id"
|
||||
}
|
||||
]
|
||||
},
|
||||
"preloadedRows": [
|
||||
{
|
||||
"id": -1,
|
||||
"name": "sql"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"pureName": "team_files",
|
||||
"columns": [
|
||||
{
|
||||
"pureName": "team_files",
|
||||
"columnName": "id",
|
||||
"dataType": "int",
|
||||
"autoIncrement": true,
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"pureName": "users",
|
||||
"columnName": "login",
|
||||
"pureName": "team_files",
|
||||
"columnName": "file_name",
|
||||
"dataType": "varchar(250)",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"pureName": "users",
|
||||
"columnName": "password",
|
||||
"dataType": "varchar(250)",
|
||||
"pureName": "team_files",
|
||||
"columnName": "file_content",
|
||||
"dataType": "text",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"pureName": "users",
|
||||
"columnName": "email",
|
||||
"dataType": "varchar(250)",
|
||||
"pureName": "team_files",
|
||||
"columnName": "file_type_id",
|
||||
"dataType": "int",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"pureName": "team_files",
|
||||
"columnName": "owner_user_id",
|
||||
"dataType": "int",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"pureName": "team_files",
|
||||
"columnName": "metadata",
|
||||
"dataType": "varchar(1000)",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"foreignKeys": [],
|
||||
"foreignKeys": [
|
||||
{
|
||||
"constraintType": "foreignKey",
|
||||
"constraintName": "FK_team_files_file_type_id",
|
||||
"pureName": "team_files",
|
||||
"refTableName": "team_file_types",
|
||||
"columns": [
|
||||
{
|
||||
"columnName": "file_type_id",
|
||||
"refColumnName": "id"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"constraintType": "foreignKey",
|
||||
"constraintName": "FK_team_files_owner_user_id",
|
||||
"pureName": "team_files",
|
||||
"refTableName": "users",
|
||||
"deleteAction": "CASCADE",
|
||||
"columns": [
|
||||
{
|
||||
"columnName": "owner_user_id",
|
||||
"refColumnName": "id"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"pureName": "users",
|
||||
"pureName": "team_files",
|
||||
"constraintType": "primaryKey",
|
||||
"constraintName": "PK_users",
|
||||
"constraintName": "PK_team_files",
|
||||
"columns": [
|
||||
{
|
||||
"columnName": "id"
|
||||
@@ -1879,6 +2031,127 @@ module.exports = {
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"pureName": "user_team_files",
|
||||
"columns": [
|
||||
{
|
||||
"pureName": "user_team_files",
|
||||
"columnName": "id",
|
||||
"dataType": "int",
|
||||
"autoIncrement": true,
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"pureName": "user_team_files",
|
||||
"columnName": "user_id",
|
||||
"dataType": "int",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"pureName": "user_team_files",
|
||||
"columnName": "team_file_id",
|
||||
"dataType": "int",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"pureName": "user_team_files",
|
||||
"columnName": "allow_read",
|
||||
"dataType": "int",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"pureName": "user_team_files",
|
||||
"columnName": "allow_write",
|
||||
"dataType": "int",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"pureName": "user_team_files",
|
||||
"columnName": "allow_use",
|
||||
"dataType": "int",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"foreignKeys": [
|
||||
{
|
||||
"constraintType": "foreignKey",
|
||||
"constraintName": "FK_user_team_files_user_id",
|
||||
"pureName": "user_team_files",
|
||||
"refTableName": "users",
|
||||
"deleteAction": "CASCADE",
|
||||
"columns": [
|
||||
{
|
||||
"columnName": "user_id",
|
||||
"refColumnName": "id"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"constraintType": "foreignKey",
|
||||
"constraintName": "FK_user_team_files_team_file_id",
|
||||
"pureName": "user_team_files",
|
||||
"refTableName": "team_files",
|
||||
"deleteAction": "CASCADE",
|
||||
"columns": [
|
||||
{
|
||||
"columnName": "team_file_id",
|
||||
"refColumnName": "id"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"pureName": "user_team_files",
|
||||
"constraintType": "primaryKey",
|
||||
"constraintName": "PK_user_team_files",
|
||||
"columns": [
|
||||
{
|
||||
"columnName": "id"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"pureName": "users",
|
||||
"columns": [
|
||||
{
|
||||
"pureName": "users",
|
||||
"columnName": "id",
|
||||
"dataType": "int",
|
||||
"autoIncrement": true,
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"pureName": "users",
|
||||
"columnName": "login",
|
||||
"dataType": "varchar(250)",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"pureName": "users",
|
||||
"columnName": "password",
|
||||
"dataType": "varchar(250)",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"pureName": "users",
|
||||
"columnName": "email",
|
||||
"dataType": "varchar(250)",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"foreignKeys": [],
|
||||
"primaryKey": {
|
||||
"pureName": "users",
|
||||
"constraintType": "primaryKey",
|
||||
"constraintName": "PK_users",
|
||||
"columns": [
|
||||
{
|
||||
"columnName": "id"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"collections": [],
|
||||
|
||||
@@ -327,7 +327,7 @@ async function testStandardPermission(permission, req, loadedPermissions) {
|
||||
loadedPermissions = await loadPermissionsFromRequest(req);
|
||||
}
|
||||
if (!hasPermission(permission, loadedPermissions)) {
|
||||
throw new Error('DBGM-00265 Permission not granted');
|
||||
throw new Error(`DBGM-00265 Permission ${permission} not granted`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -344,7 +344,7 @@ async function testDatabaseRolePermission(conid, database, requiredRole, req) {
|
||||
const requiredIndex = getDatabaseRoleLevelIndex(requiredRole);
|
||||
const roleIndex = getDatabaseRoleLevelIndex(role);
|
||||
if (roleIndex < requiredIndex) {
|
||||
throw new Error('DBGM-00266 Permission not granted');
|
||||
throw new Error(`DBGM-00266 Permission ${requiredRole} not granted`);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -107,11 +107,27 @@ export function testSubPermission(
|
||||
export function getPredefinedPermissions(predefinedRoleName: string) {
|
||||
switch (predefinedRoleName) {
|
||||
case 'superadmin':
|
||||
return ['*', '~widgets/*', 'widgets/admin', 'widgets/database', '~all-connections'];
|
||||
return ['*', '~widgets/*', 'widgets/admin', 'widgets/database', '~all-connections', '~all-team-files/*'];
|
||||
case 'logged-user':
|
||||
return ['*', '~widgets/admin', '~admin/*', '~internal-storage', '~all-connections', '~run-shell-script'];
|
||||
return [
|
||||
'*',
|
||||
'~widgets/admin',
|
||||
'~admin/*',
|
||||
'~internal-storage',
|
||||
'~all-connections',
|
||||
'~run-shell-script',
|
||||
'~all-team-files/*',
|
||||
];
|
||||
case 'anonymous-user':
|
||||
return ['*', '~widgets/admin', '~admin/*', '~internal-storage', '~all-connections', '~run-shell-script'];
|
||||
return [
|
||||
'*',
|
||||
'~widgets/admin',
|
||||
'~admin/*',
|
||||
'~internal-storage',
|
||||
'~all-connections',
|
||||
'~run-shell-script',
|
||||
'~all-team-files/*',
|
||||
];
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -192,6 +192,7 @@
|
||||
import { isProApp } from '../utility/proTools';
|
||||
import { saveFileToDisk } from '../utility/exportFileTools';
|
||||
import { getConnectionInfo } from '../utility/metadataLoaders';
|
||||
import { showSnackbarError } from '../utility/snackbar';
|
||||
|
||||
export let data;
|
||||
|
||||
@@ -214,11 +215,20 @@
|
||||
function createMenu() {
|
||||
return [
|
||||
handler?.tabComponent && { text: 'Open', onClick: openTab },
|
||||
hasPermission(`files/${data.folder}/write`) && { text: 'Rename', onClick: handleRename },
|
||||
hasPermission(`files/${data.folder}/write`) && { text: 'Create copy', onClick: handleCopy },
|
||||
hasPermission(`files/${data.folder}/write`) && { text: 'Delete', onClick: handleDelete },
|
||||
|
||||
!data.teamFileId && hasPermission(`files/${data.folder}/write`) && { text: 'Rename', onClick: handleRename },
|
||||
!data.teamFileId && hasPermission(`files/${data.folder}/write`) && { text: 'Create copy', onClick: handleCopy },
|
||||
!data.teamFileId && hasPermission(`files/${data.folder}/write`) && { text: 'Delete', onClick: handleDelete },
|
||||
|
||||
data.teamFileId && data.allowWrite && { text: 'Rename', onClick: handleRename },
|
||||
data.teamFileId &&
|
||||
data.allowRead &&
|
||||
hasPermission('all-team-files/create') && { text: 'Create copy', onClick: handleCopy },
|
||||
data.teamFileId && data.allowWrite && { text: 'Delete', onClick: handleDelete },
|
||||
|
||||
folder == 'markdown' && { text: 'Show page', onClick: showMarkdownPage },
|
||||
{ text: 'Download', onClick: handleDownload },
|
||||
!data.teamFileId && { text: 'Download', onClick: handleDownload },
|
||||
data.teamFileId && data.allowRead && { text: 'Download', onClick: handleDownload },
|
||||
];
|
||||
}
|
||||
|
||||
@@ -226,7 +236,9 @@
|
||||
showModal(ConfirmModal, {
|
||||
message: `Really delete file ${data.file}?`,
|
||||
onConfirm: () => {
|
||||
if (data.folid && data.cntid) {
|
||||
if (data.teamFileId) {
|
||||
apiCall('team-files/delete', { teamFileId: data.teamFileId });
|
||||
} else if (data.folid && data.cntid) {
|
||||
apiCall('cloud/delete-content', {
|
||||
folid: data.folid,
|
||||
cntid: data.cntid,
|
||||
@@ -244,7 +256,9 @@
|
||||
label: 'New file name',
|
||||
header: 'Rename file',
|
||||
onConfirm: newFile => {
|
||||
if (data.folid && data.cntid) {
|
||||
if (data.teamFileId) {
|
||||
apiCall('team-files/update', { teamFileId: data.teamFileId, name: newFile });
|
||||
} else if (data.folid && data.cntid) {
|
||||
apiCall('cloud/rename-content', {
|
||||
folid: data.folid,
|
||||
cntid: data.cntid,
|
||||
@@ -263,7 +277,9 @@
|
||||
label: 'New file name',
|
||||
header: 'Copy file',
|
||||
onConfirm: newFile => {
|
||||
if (data.folid && data.cntid) {
|
||||
if (data.teamFileId) {
|
||||
apiCall('team-files/copy', { teamFileId: data.teamFileId, newName: newFile });
|
||||
} else if (data.folid && data.cntid) {
|
||||
apiCall('cloud/copy-file', {
|
||||
folid: data.folid,
|
||||
cntid: data.cntid,
|
||||
@@ -279,7 +295,12 @@
|
||||
const handleDownload = () => {
|
||||
saveFileToDisk(
|
||||
async filePath => {
|
||||
if (data.folid && data.cntid) {
|
||||
if (data.teamFileId) {
|
||||
await apiCall('team-files/export-file', {
|
||||
teamFileId: data.teamFileId,
|
||||
filePath,
|
||||
});
|
||||
} else if (data.folid && data.cntid) {
|
||||
await apiCall('cloud/export-file', {
|
||||
folid: data.folid,
|
||||
cntid: data.cntid,
|
||||
@@ -299,7 +320,23 @@
|
||||
|
||||
async function openTab() {
|
||||
let dataContent;
|
||||
if (data.folid && data.cntid) {
|
||||
if (data.teamFileId) {
|
||||
if (data?.metadata?.autoExecute) {
|
||||
if (!data.allowUse) {
|
||||
showSnackbarError('You do not have permission to use this team file');
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (!data.allowRead) {
|
||||
showSnackbarError('You do not have permission to read this team file');
|
||||
return;
|
||||
}
|
||||
}
|
||||
const resp = await apiCall('team-files/get-content', {
|
||||
teamFileId: data.teamFileId,
|
||||
});
|
||||
dataContent = resp.content;
|
||||
} else if (data.folid && data.cntid) {
|
||||
const resp = await apiCall('cloud/get-content', {
|
||||
folid: data.folid,
|
||||
cntid: data.cntid,
|
||||
@@ -324,6 +361,11 @@
|
||||
tooltip = `${getConnectionLabel(connection)}\n${database}`;
|
||||
}
|
||||
|
||||
if (data?.metadata?.connectionId) {
|
||||
connProps.conid = data.metadata.connectionId;
|
||||
connProps.database = data.metadata.databaseName;
|
||||
}
|
||||
|
||||
openNewTab(
|
||||
{
|
||||
title: data.file,
|
||||
@@ -336,6 +378,8 @@
|
||||
savedFormat: handler.format,
|
||||
savedCloudFolderId: data.folid,
|
||||
savedCloudContentId: data.cntid,
|
||||
savedTeamFileId: data.teamFileId,
|
||||
hideEditor: data.teamFileId && data?.metadata?.autoExecute && !data.allowRead,
|
||||
...connProps,
|
||||
},
|
||||
},
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
export let isSplitter = true;
|
||||
export let initialValue = undefined;
|
||||
export let hideFirst = false;
|
||||
|
||||
export let allowCollapseChild1 = false;
|
||||
export let allowCollapseChild2 = false;
|
||||
@@ -22,28 +23,32 @@
|
||||
</script>
|
||||
|
||||
<div class="container" bind:clientHeight>
|
||||
<div
|
||||
class="child1"
|
||||
style={isSplitter
|
||||
? collapsed1
|
||||
? 'display:none'
|
||||
: collapsed2
|
||||
? 'flex:1'
|
||||
: `height:${size}px; min-height:${size}px; max-height:${size}px}`
|
||||
: `flex:1`}
|
||||
>
|
||||
<slot name="1" />
|
||||
</div>
|
||||
{#if isSplitter}
|
||||
{#if !hideFirst}
|
||||
<div
|
||||
class={'vertical-split-handle'}
|
||||
style={collapsed1 || collapsed2 ? 'display:none' : ''}
|
||||
use:splitterDrag={'clientY'}
|
||||
on:resizeSplitter={e => {
|
||||
size += e.detail;
|
||||
if (clientHeight > 0) customRatio = size / clientHeight;
|
||||
}}
|
||||
/>
|
||||
class="child1"
|
||||
style={isSplitter
|
||||
? collapsed1
|
||||
? 'display:none'
|
||||
: collapsed2
|
||||
? 'flex:1'
|
||||
: `height:${size}px; min-height:${size}px; max-height:${size}px}`
|
||||
: `flex:1`}
|
||||
>
|
||||
<slot name="1" />
|
||||
</div>
|
||||
{/if}
|
||||
{#if isSplitter}
|
||||
{#if !hideFirst}
|
||||
<div
|
||||
class={'vertical-split-handle'}
|
||||
style={collapsed1 || collapsed2 ? 'display:none' : ''}
|
||||
use:splitterDrag={'clientY'}
|
||||
on:resizeSplitter={e => {
|
||||
size += e.detail;
|
||||
if (clientHeight > 0) customRatio = size / clientHeight;
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
<div
|
||||
class={collapsed1 ? 'child1' : 'child2'}
|
||||
style={collapsed2 ? 'display:none' : collapsed1 ? 'flex:1' : 'child2'}
|
||||
|
||||
@@ -120,6 +120,7 @@
|
||||
'icon structure': 'mdi mdi-tools',
|
||||
'icon square': 'mdi mdi-square',
|
||||
'icon data-deploy': 'mdi mdi-database-settings',
|
||||
'icon team-file': 'mdi mdi-account-file',
|
||||
|
||||
'icon cloud-account': 'mdi mdi-account-remove-outline',
|
||||
'icon cloud-account-connected': 'mdi mdi-account-check-outline',
|
||||
@@ -351,6 +352,7 @@
|
||||
'img settings': 'mdi mdi-cog color-icon-blue',
|
||||
'img data-deploy': 'mdi mdi-database-settings color-icon-green',
|
||||
'img arrow-start-here': 'mdi mdi-arrow-down-bold-circle color-icon-green',
|
||||
'img team-file': 'mdi mdi-account-file color-icon-red',
|
||||
};
|
||||
</script>
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
import FormProviderCore from '../forms/FormProviderCore.svelte';
|
||||
import FormSubmit from '../forms/FormSubmit.svelte';
|
||||
import FormTextField from '../forms/FormTextField.svelte';
|
||||
import { cloudSigninTokenHolder } from '../stores';
|
||||
import { cloudSigninTokenHolder, getCurrentConfig } from '../stores';
|
||||
import { _t } from '../translations';
|
||||
import { apiCall } from '../utility/api';
|
||||
import { writable } from 'svelte/store';
|
||||
@@ -13,6 +13,7 @@
|
||||
import ModalBase from './ModalBase.svelte';
|
||||
import { closeCurrentModal, showModal } from './modalTools';
|
||||
import FormCloudFolderSelect from '../forms/FormCloudFolderSelect.svelte';
|
||||
import FormCheckboxField from '../forms/FormCheckboxField.svelte';
|
||||
|
||||
export let data;
|
||||
export let name;
|
||||
@@ -31,7 +32,20 @@
|
||||
|
||||
const handleSubmit = async e => {
|
||||
const { name, cloudFolder } = e.detail;
|
||||
if (cloudFolder === '__local') {
|
||||
if ($values['saveToTeamFolder']) {
|
||||
const { teamFileId } = await apiCall('team-files/create-new', { fileType: folder, file: name, data });
|
||||
closeCurrentModal();
|
||||
if (onSave) {
|
||||
onSave(name, {
|
||||
savedFile: name,
|
||||
savedFolder: folder,
|
||||
savedFilePath: null,
|
||||
savedCloudFolderId: null,
|
||||
savedCloudContentId: null,
|
||||
savedTeamFileId: teamFileId,
|
||||
});
|
||||
}
|
||||
} else if (cloudFolder === '__local') {
|
||||
await apiCall('files/save', { folder, file: name, data, format });
|
||||
closeCurrentModal();
|
||||
if (onSave) {
|
||||
@@ -41,6 +55,7 @@
|
||||
savedFilePath: null,
|
||||
savedCloudFolderId: null,
|
||||
savedCloudContentId: null,
|
||||
savedTeamFileId: null,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
@@ -61,6 +76,7 @@
|
||||
savedFilePath: null,
|
||||
savedCloudFolderId: cloudFolder,
|
||||
savedCloudContentId: resp.cntid,
|
||||
savedTeamFileId: null,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -82,6 +98,7 @@
|
||||
savedFilePath: filePath,
|
||||
savedCloudFolderId: null,
|
||||
savedCloudContentId: null,
|
||||
savedTeamFileId: null,
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -91,7 +108,7 @@
|
||||
<ModalBase {...$$restProps}>
|
||||
<svelte:fragment slot="header">Save file</svelte:fragment>
|
||||
<FormTextField label="File name" name="name" focused />
|
||||
{#if $cloudSigninTokenHolder}
|
||||
{#if $cloudSigninTokenHolder && !$values['saveToTeamFolder']}
|
||||
<FormCloudFolderSelect
|
||||
label="Choose cloud folder"
|
||||
name="cloudFolder"
|
||||
@@ -107,6 +124,9 @@
|
||||
]}
|
||||
/>
|
||||
{/if}
|
||||
{#if getCurrentConfig().storageDatabase}
|
||||
<FormCheckboxField label="Save to team folder" name="saveToTeamFolder" />
|
||||
{/if}
|
||||
|
||||
<svelte:fragment slot="footer">
|
||||
<FormSubmit value={_t('common.save', { defaultMessage: 'Save' })} on:click={handleSubmit} />
|
||||
|
||||
@@ -160,6 +160,7 @@
|
||||
export let conid;
|
||||
export let database;
|
||||
export let initialArgs;
|
||||
export let hideEditor;
|
||||
|
||||
export const activator = createActivator('QueryTab', false);
|
||||
|
||||
@@ -653,7 +654,7 @@
|
||||
</script>
|
||||
|
||||
<ToolStripContainer bind:this={domToolStrip}>
|
||||
<VerticalSplitter isSplitter={visibleResultTabs} initialValue={splitterInitialValue}>
|
||||
<VerticalSplitter isSplitter={visibleResultTabs} initialValue={splitterInitialValue} hideFirst={hideEditor}>
|
||||
<svelte:fragment slot="1">
|
||||
{#if driver?.databaseEngineTypes?.includes('sql')}
|
||||
<SqlEditor
|
||||
|
||||
@@ -184,6 +184,12 @@ const cloudContentListLoader = () => ({
|
||||
reloadTrigger: { key: `cloud-content-changed` },
|
||||
});
|
||||
|
||||
const teamFilesLoader = () => ({
|
||||
url: 'team-files/list',
|
||||
params: {},
|
||||
reloadTrigger: { key: `team-files-changed` },
|
||||
});
|
||||
|
||||
async function getCore(loader, args) {
|
||||
const { url, params, reloadTrigger, transform, onLoaded, errorValue } = loader(args);
|
||||
const key = stableStringify({ url, ...params });
|
||||
@@ -523,3 +529,10 @@ export function getCloudContentList(args) {
|
||||
export function useCloudContentList(args = {}) {
|
||||
return useCore(cloudContentListLoader, args);
|
||||
}
|
||||
|
||||
export function getTeamFiles(args) {
|
||||
return getCore(teamFilesLoader, args);
|
||||
}
|
||||
export function useTeamFiles(args) {
|
||||
return useCore(teamFilesLoader, args);
|
||||
}
|
||||
|
||||
@@ -15,11 +15,14 @@ export default async function saveTabFile(editor, saveMode, folder, format, file
|
||||
const tabs = get(openedTabs);
|
||||
const tabid = editor.activator.tabid;
|
||||
const data = editor.getData();
|
||||
const { savedFile, savedFilePath, savedFolder, savedCloudFolderId, savedCloudContentId } =
|
||||
const { savedFile, savedFilePath, savedFolder, savedCloudFolderId, savedCloudContentId, savedTeamFileId } =
|
||||
tabs.find(x => x.tabid == tabid).props || {};
|
||||
|
||||
const handleSave = async () => {
|
||||
if (savedCloudFolderId && savedCloudContentId) {
|
||||
if (savedTeamFileId) {
|
||||
const resp = await apiCall('team-files/update', { teamFileId: savedTeamFileId, data });
|
||||
markTabSaved(tabid);
|
||||
} else if (savedCloudFolderId && savedCloudContentId) {
|
||||
const resp = await apiCall('cloud/save-file', {
|
||||
folid: savedCloudFolderId,
|
||||
fileName: savedFile,
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
import SearchInput from '../elements/SearchInput.svelte';
|
||||
import FontIcon from '../icons/FontIcon.svelte';
|
||||
import { apiCall } from '../utility/api';
|
||||
import { useFiles } from '../utility/metadataLoaders';
|
||||
import { useFiles, useTeamFiles } from '../utility/metadataLoaders';
|
||||
import WidgetsInnerContainer from './WidgetsInnerContainer.svelte';
|
||||
import { isProApp } from '../utility/proTools';
|
||||
import InlineUploadButton from '../buttons/InlineUploadButton.svelte';
|
||||
@@ -29,6 +29,7 @@
|
||||
const perspectiveFiles = useFiles({ folder: 'perspectives' });
|
||||
const modelTransformFiles = useFiles({ folder: 'modtrans' });
|
||||
const appFiles = useFiles({ folder: 'apps' });
|
||||
const teamFiles = useTeamFiles({});
|
||||
|
||||
$: files = [
|
||||
...($sqlFiles || []),
|
||||
@@ -44,6 +45,7 @@
|
||||
...((isProApp() && $dataDeployJobFiles) || []),
|
||||
...((isProApp() && $dbCompareJobFiles) || []),
|
||||
...((isProApp() && $appFiles) || []),
|
||||
...($teamFiles || []),
|
||||
];
|
||||
|
||||
function handleRefreshFiles() {
|
||||
@@ -81,5 +83,10 @@
|
||||
</SearchBoxWrapper>
|
||||
|
||||
<WidgetsInnerContainer>
|
||||
<AppObjectList list={files} module={savedFileAppObject} groupFunc={data => dataFolderTitle(data.folder)} {filter} />
|
||||
<AppObjectList
|
||||
list={files}
|
||||
module={savedFileAppObject}
|
||||
groupFunc={data => (data.teamFileId ? 'Team files' : dataFolderTitle(data.folder))}
|
||||
{filter}
|
||||
/>
|
||||
</WidgetsInnerContainer>
|
||||
|
||||
Reference in New Issue
Block a user