diff --git a/packages/api/package.json b/packages/api/package.json index 88e76bc04..bdbe00e78 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -46,6 +46,7 @@ "mssql": "^6.0.1", "mysql": "^2.17.1", "nedb-promises": "^4.0.1", + "node-fetch": "^2.6.1", "pg": "^7.17.0", "pg-query-stream": "^3.1.1", "xlsx": "^0.16.8" diff --git a/packages/api/src/controllers/plugins.js b/packages/api/src/controllers/plugins.js new file mode 100644 index 000000000..c24b453bb --- /dev/null +++ b/packages/api/src/controllers/plugins.js @@ -0,0 +1,22 @@ +const fs = require('fs-extra'); +const fetch = require('node-fetch'); + +module.exports = { + script_meta: 'get', + async script({ plugin }) { + const data = await fs.readFile('/home/jena/jenasoft/dbgate-plugin-csv/lib/frontend.js', { + encoding: 'utf-8', + }); + return data; + }, + + search_meta: 'get', + async search({ filter }) { + console.log(`https://api.npms.io/v2/search?q=keywords:dbgate ${encodeURIComponent(filter)}`); + const response = await fetch(`https://api.npms.io/v2/search?q=keywords:dbgate ${encodeURIComponent(filter)}`); + const json = await response.json(); + console.log(json); + const { results } = json || {}; + return results || []; + }, +}; diff --git a/packages/api/src/main.js b/packages/api/src/main.js index bafaf0c59..fa741841f 100644 --- a/packages/api/src/main.js +++ b/packages/api/src/main.js @@ -23,6 +23,7 @@ const config = require('./controllers/config'); const files = require('./controllers/files'); const archive = require('./controllers/archive'); const uploads = require('./controllers/uploads'); +const plugins = require('./controllers/plugins'); const { rundir } = require('./utility/directories'); @@ -67,6 +68,7 @@ function start(argument = null) { useController(app, '/files', files); useController(app, '/archive', archive); useController(app, '/uploads', uploads); + useController(app, '/plugins', plugins); if (process.env.PAGES_DIRECTORY) { app.use('/pages', express.static(process.env.PAGES_DIRECTORY)); diff --git a/packages/api/src/utility/directories.js b/packages/api/src/utility/directories.js index d001c0920..fbb42d921 100644 --- a/packages/api/src/utility/directories.js +++ b/packages/api/src/utility/directories.js @@ -35,6 +35,8 @@ const dirFunc = (dirname, clean = false) => () => { const jsldir = dirFunc('jsl', true); const rundir = dirFunc('run', true); const uploadsdir = dirFunc('uploads', true); +const pluginstmpdir = dirFunc('plugins-tmp', true); +const pluginsdir = dirFunc('plugins'); const archivedir = dirFunc('archive'); module.exports = { @@ -44,4 +46,6 @@ module.exports = { uploadsdir, archivedir, ensureDirectory, + pluginstmpdir, + pluginsdir, }; diff --git a/packages/web/src/App.js b/packages/web/src/App.js index a02f8210c..b8bd715de 100644 --- a/packages/web/src/App.js +++ b/packages/web/src/App.js @@ -16,6 +16,7 @@ import ConnectionsPinger from './utility/ConnectionsPinger'; import { ModalLayerProvider } from './modals/showModal'; import UploadsProvider from './utility/UploadsProvider'; import ThemeHelmet from './themes/ThemeHelmet'; +import PluginsProvider from './plugins/PluginsProvider'; function App() { return ( @@ -31,7 +32,9 @@ function App() { - + + + diff --git a/packages/web/src/icons.js b/packages/web/src/icons.js index 790950042..038e5ab34 100644 --- a/packages/web/src/icons.js +++ b/packages/web/src/icons.js @@ -42,6 +42,7 @@ const iconNames = { 'icon run': 'mdi mdi-play', 'icon chevron-down': 'mdi mdi-chevron-down', + 'icon plugin': 'mdi mdi-toy-brick', 'img green-ok': 'mdi mdi-check-circle color-green-8', 'img alert': 'mdi mdi-alert-circle color-blue-6', diff --git a/packages/web/src/plugins/PluginsList.js b/packages/web/src/plugins/PluginsList.js new file mode 100644 index 000000000..469d7bbb4 --- /dev/null +++ b/packages/web/src/plugins/PluginsList.js @@ -0,0 +1,86 @@ +import React from 'react'; +import styled from 'styled-components'; +import useTheme from '../theme/useTheme'; +import { openNewTab } from '../utility/common'; +import { useSetOpenedTabs } from '../utility/globalState'; + +const Wrapper = styled.div` + margin: 1px 3px 10px 5px; + display: flex; + align-items: center; + &:hover { + background-color: ${(props) => props.theme.left_background_blue[1]}; + } +`; + +const Texts = styled.div` + margin-left: 10px; +`; + +const Name = styled.div` + font-weight: bold; +`; + +const Line = styled.div` + display: flex; +`; + +const Icon = styled.img` + width: 50px; + height: 50px; +`; + +const Description = styled.div` + font-style: italic; +`; + +const Author = styled.div` + font-weight: bold; +`; + +const Version = styled.div` + margin-left: 5px; +`; + +function openPlugin(setOpenedTabs, plugin) { + openNewTab(setOpenedTabs, { + title: plugin.package.name, + icon: 'icon plugin', + tabComponent: 'PluginTab', + props: { + plugin, + }, + }); +} + +function PluginsListItem({ plugin }) { + const setOpenedTabs = useSetOpenedTabs(); + const theme = useTheme(); + return ( + openPlugin(setOpenedTabs, plugin)} theme={theme}> + + + + {plugin.package.name} + {plugin.package.version} + + + {plugin.package.description} + + + {plugin.package.author && plugin.package.author.name} + + + + ); +} + +export default function PluginsList({ plugins }) { + return ( + <> + {plugins.map((plugin) => ( + + ))} + + ); +} diff --git a/packages/web/src/plugins/PluginsProvider.js b/packages/web/src/plugins/PluginsProvider.js new file mode 100644 index 000000000..cac3487db --- /dev/null +++ b/packages/web/src/plugins/PluginsProvider.js @@ -0,0 +1,23 @@ +import React from 'react'; +import axios from '../utility/axios'; + +const PluginsContext = React.createContext(null); + +export default function PluginsProvider({ children }) { + const [plugins, setPlugins] = React.useState(null); + const handleLoadPlugin = async () => { + const resp = await axios.request({ + method: 'get', + url: 'plugins/script', + params: { + plugin: 'csv', + }, + }); + const module = eval(resp.data); + console.log('MODULE', module); + }; + React.useEffect(() => { + handleLoadPlugin(); + }, []); + return {children}; +} diff --git a/packages/web/src/tabs/PluginTab.js b/packages/web/src/tabs/PluginTab.js new file mode 100644 index 000000000..2fba68b31 --- /dev/null +++ b/packages/web/src/tabs/PluginTab.js @@ -0,0 +1,28 @@ +import React from 'react'; +import styled from 'styled-components'; +import _ from 'lodash'; +import ObjectListControl from '../utility/ObjectListControl'; +import { TableColumn } from '../utility/TableControl'; +import columnAppObject from '../appobj/columnAppObject'; +import constraintAppObject from '../appobj/constraintAppObject'; +import { useTableInfo, useDbCore } from '../utility/metadataLoaders'; +import useTheme from '../theme/useTheme'; + +const WhitePage = styled.div` + position: absolute; + left: 0; + top: 0; + right: 0; + bottom: 0; + background-color: ${(props) => props.theme.main_background}; + overflow: auto; +`; + +export default function PluginTab({ plugin }) { + const theme = useTheme(); + return ( + +
{plugin.package.name}
+
+ ); +} diff --git a/packages/web/src/tabs/index.js b/packages/web/src/tabs/index.js index 97d46acd6..e77d0bc3e 100644 --- a/packages/web/src/tabs/index.js +++ b/packages/web/src/tabs/index.js @@ -6,6 +6,7 @@ import ShellTab from './ShellTab'; import InfoPageTab from './InfoPageTab'; import ArchiveFileTab from './ArchiveFileTab'; import FreeTableTab from './FreeTableTab'; +import PluginTab from './PluginTab'; export default { TableDataTab, @@ -16,4 +17,5 @@ export default { ShellTab, ArchiveFileTab, FreeTableTab, + PluginTab, }; diff --git a/packages/web/src/widgets/ArchiveWidget.js b/packages/web/src/widgets/ArchiveWidget.js index c628a37a7..e73f1c0be 100644 --- a/packages/web/src/widgets/ArchiveWidget.js +++ b/packages/web/src/widgets/ArchiveWidget.js @@ -1,19 +1,10 @@ import React from 'react'; -import styled from 'styled-components'; import _ from 'lodash'; import { AppObjectList } from '../appobj/AppObjectList'; -import { useCurrentArchive, useOpenedTabs, useSavedSqlFiles, useSetCurrentArchive } from '../utility/globalState'; -import closedTabAppObject from '../appobj/closedTabAppObject'; -import { - SearchBoxWrapper, - WidgetsInnerContainer, - WidgetsMainContainer, - WidgetsOuterContainer, - WidgetTitle, -} from './WidgetStyles'; +import { useCurrentArchive, useSetCurrentArchive } from '../utility/globalState'; +import { SearchBoxWrapper, WidgetsInnerContainer } from './WidgetStyles'; import WidgetColumnBar, { WidgetColumnBarItem } from './WidgetColumnBar'; -import savedSqlFileAppObject from '../appobj/savedSqlFileAppObject'; import { useArchiveFiles, useArchiveFolders } from '../utility/metadataLoaders'; import archiveFolderAppObject from '../appobj/archiveFolderAppObject'; import archiveFileAppObject from '../appobj/archiveFileAppObject'; diff --git a/packages/web/src/widgets/PluginsWidget.js b/packages/web/src/widgets/PluginsWidget.js new file mode 100644 index 000000000..9796fdf05 --- /dev/null +++ b/packages/web/src/widgets/PluginsWidget.js @@ -0,0 +1,73 @@ +import React from 'react'; +import _ from 'lodash'; + +import { AppObjectList } from '../appobj/AppObjectList'; +import { useCurrentArchive, useSetCurrentArchive } from '../utility/globalState'; +import { SearchBoxWrapper, WidgetsInnerContainer } from './WidgetStyles'; +import WidgetColumnBar, { WidgetColumnBarItem } from './WidgetColumnBar'; +import { useArchiveFiles, useArchiveFolders } from '../utility/metadataLoaders'; +import archiveFolderAppObject from '../appobj/archiveFolderAppObject'; +import archiveFileAppObject from '../appobj/archiveFileAppObject'; +import SearchInput from './SearchInput'; +import InlineButton from './InlineButton'; +import axios from '../utility/axios'; +import useFetch from '../utility/useFetch'; +import PluginsList from '../plugins/PluginsList'; + +function InstalledPluginsList() { + // const folders = useArchiveFolders(); + // const [filter, setFilter] = React.useState(''); + + // const setArchive = useSetCurrentArchive(); + + // const handleRefreshFolders = () => { + // axios.post('archive/refresh-folders', {}); + // }; + + return ( + + {/* setArchive(archive.name)} + filter={filter} + /> */} + + ); +} + +function AvailablePluginsList() { + const [filter, setFilter] = React.useState(''); + + const plugins = useFetch({ + url: 'plugins/search', + params: { + filter, + }, + defaultValue: [], + }); + + return ( + <> + + + + + + + + ); +} + +export default function PluginsWidget() { + return ( + + + + + + + + + ); +} diff --git a/packages/web/src/widgets/WidgetContainer.js b/packages/web/src/widgets/WidgetContainer.js index 4ef38c466..9fa204db5 100644 --- a/packages/web/src/widgets/WidgetContainer.js +++ b/packages/web/src/widgets/WidgetContainer.js @@ -3,11 +3,13 @@ import { useCurrentWidget } from '../utility/globalState'; import ArchiveWidget from './ArchiveWidget'; import DatabaseWidget from './DatabaseWidget'; import FilesWidget from './FilesWidget'; +import PluginsWidget from './PluginsWidget'; export default function WidgetContainer() { const currentWidget = useCurrentWidget(); if (currentWidget === 'database') return ; if (currentWidget === 'file') return ; if (currentWidget === 'archive') return ; + if (currentWidget === 'plugins') return ; return null; } diff --git a/packages/web/src/widgets/WidgetIconPanel.js b/packages/web/src/widgets/WidgetIconPanel.js index ce78fd5c5..e00cbcd37 100644 --- a/packages/web/src/widgets/WidgetIconPanel.js +++ b/packages/web/src/widgets/WidgetIconPanel.js @@ -51,6 +51,11 @@ export default function WidgetIconPanel() { name: 'archive', title: 'Archive (saved tabular data)', }, + { + icon: 'icon plugin', + name: 'plugins', + title: 'Extensions & Plugins', + }, // { // icon: 'fa-cog', // name: 'settings', diff --git a/yarn.lock b/yarn.lock index 505cc1a98..161b25e1b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7960,6 +7960,11 @@ node-fetch@^1.0.1: encoding "^0.1.11" is-stream "^1.0.1" +node-fetch@^2.6.1: + version "2.6.1" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" + integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== + node-forge@0.9.0: version "0.9.0" resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.9.0.tgz#d624050edbb44874adca12bb9a52ec63cb782579"