diff --git a/app/src/mainMenuDefinition.js b/app/src/mainMenuDefinition.js index 16f0b63d1..1548b23be 100644 --- a/app/src/mainMenuDefinition.js +++ b/app/src/mainMenuDefinition.js @@ -90,6 +90,7 @@ module.exports = ({ editMenu }) => [ { divider: true }, { command: 'folder.showLogs', hideDisabled: true }, { command: 'folder.showData', hideDisabled: true }, + { command: 'new.gist', hideDisabled: true }, ], }, { diff --git a/packages/api/src/controllers/serverConnections.js b/packages/api/src/controllers/serverConnections.js index afd3b12c9..a35bb3330 100644 --- a/packages/api/src/controllers/serverConnections.js +++ b/packages/api/src/controllers/serverConnections.js @@ -240,4 +240,20 @@ module.exports = { if (opened.connection.isReadOnly) return false; return this.loadDataCore('summaryCommand', { conid, command, row }); }, + + getOpenedConnectionReport() { + return this.opened.map(con => ({ + status: con.status, + versionText: con.version?.versionText, + databaseCount: con.databases.length, + connection: _.pick(con.connection, [ + 'engine', + 'useSshTunnel', + 'authType', + 'trustServerCertificate', + 'useSsl', + 'sshMode', + ]), + })); + }, }; diff --git a/packages/api/src/controllers/uploads.js b/packages/api/src/controllers/uploads.js index 059649462..8c5576f32 100644 --- a/packages/api/src/controllers/uploads.js +++ b/packages/api/src/controllers/uploads.js @@ -1,8 +1,15 @@ const crypto = require('crypto'); const path = require('path'); -const { uploadsdir } = require('../utility/directories'); +const { uploadsdir, getLogsFilePath } = require('../utility/directories'); const { getLogger } = require('dbgate-tools'); const logger = getLogger('uploads'); +const axios = require('axios'); +const os = require('os'); +const fs = require('fs/promises'); +const { read } = require('./queryHistory'); +const platformInfo = require('../utility/platformInfo'); +const _ = require('lodash'); +const serverConnections = require('./serverConnections'); module.exports = { upload_meta: { @@ -35,4 +42,80 @@ module.exports = { get(req, res) { res.sendFile(path.join(uploadsdir(), req.query.file)); }, + + uploadErrorToGist_meta: true, + async uploadErrorToGist() { + const logs = await fs.readFile(getLogsFilePath(), { encoding: 'utf-8' }); + const connections = await serverConnections.getOpenedConnectionReport(); + try { + const response = await axios.default.post( + 'https://api.github.com/gists', + { + description: 'DbGate error report', + public: false, + files: { + 'logs.jsonl': { + content: logs, + }, + 'os.json': { + content: JSON.stringify( + { + release: os.release(), + arch: os.arch(), + machine: os.machine(), + platform: os.platform(), + type: os.type(), + }, + null, + 2 + ), + }, + 'platform.json': { + content: JSON.stringify( + _.omit( + { + ...platformInfo, + }, + ['defaultKeyfile', 'sshAuthSock'] + ), + null, + 2 + ), + }, + 'connections.json': { + content: JSON.stringify(connections, null, 2), + }, + }, + }, + { + headers: { + Authorization: `token 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`, + 'Content-Type': 'application/json', + Accept: 'application/vnd.github.v3+json', + }, + } + ); + + return response.data; + } catch (err) { + logger.error({ err }, 'Error uploading gist'); + + return { + apiErrorMessage: err.message, + }; + // console.error('Error creating gist:', error.response ? error.response.data : error.message); + } + }, + + deleteGist_meta: true, + async deleteGist({ url }) { + const response = await axios.default.delete(url, { + headers: { + Authorization: `token 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`, + 'Content-Type': 'application/json', + Accept: 'application/vnd.github.v3+json', + }, + }); + return true; + }, }; diff --git a/packages/web/src/commands/stdCommands.ts b/packages/web/src/commands/stdCommands.ts index 84360afce..cd0e02e35 100644 --- a/packages/web/src/commands/stdCommands.ts +++ b/packages/web/src/commands/stdCommands.ts @@ -38,6 +38,7 @@ import { getSettings } from '../utility/metadataLoaders'; import { isMac } from '../utility/common'; import { doLogout, internalRedirectTo } from '../clientAuth'; import { disconnectServerConnection } from '../appobj/ConnectionAppObject.svelte'; +import UploadErrorModal from '../modals/UploadErrorModal.svelte'; // function themeCommand(theme: ThemeDefinition) { // return { @@ -865,6 +866,14 @@ registerCommand({ onClick: () => getElectron().send('window-action', 'selectAll'), }); +registerCommand({ + id: 'new.gist', + category: 'New', + name: 'Upload error to gist', + onClick: () => showModal(UploadErrorModal), +}); + + const electron = getElectron(); if (electron) { electron.addEventListener('run-command', (e, commandId) => runCommand(commandId)); diff --git a/packages/web/src/modals/UploadErrorModal.svelte b/packages/web/src/modals/UploadErrorModal.svelte new file mode 100644 index 000000000..ab9b94b27 --- /dev/null +++ b/packages/web/src/modals/UploadErrorModal.svelte @@ -0,0 +1,58 @@ + + + + Upload error + + {#if htmlUrl} + + Upload error to gist was successful. You could check uploaded data, if don't want to make them public, use Delete button to remove them from gist. + Open uploaded data + + {:else} + + {/if} + + + + + + +
Upload error to gist was successful. You could check uploaded data, if don't want to make them public, use Delete button to remove them from gist.
Open uploaded data