فهرست منبع

Use zip for multiple endpoints download

Sergiu Miclea 6 سال پیش
والد
کامیت
0233621597

+ 1 - 0
package.json

@@ -58,6 +58,7 @@
   },
   "dependencies": {
     "@webpack-blocks/webpack2": "^0.4.0",
+    "adm-zip": "^0.4.13",
     "autobind-decorator": "^2.1.0",
     "axios": "^0.18.0",
     "babel-core": "^6.26.0",

+ 3 - 9
server/main.js

@@ -17,12 +17,12 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 import express from 'express'
 import fs from 'fs'
 import path from 'path'
-import requireWithoutCache from 'require-without-cache'
 
-import packageJson from '../package.json'
+import router from './router'
 
 // Create our app
 const app = express()
+
 const PORT = process.env.PORT || 3000
 const isDev = process.argv.find(a => a === '--dev')
 const CORIOLIS_URL = process.env.CORIOLIS_URL || '/'
@@ -47,13 +47,7 @@ app.use(express.static('dist'))
 
 require('./proxy')(app)
 
-// $FlowIgnore
-app.get('/version', (req, res) => { res.send({ version: packageJson.version }) })
-
-// $FlowIgnore
-app.get('/config', (req, res) => {
-  res.send(requireWithoutCache('../config.js', require).config)
-})
+app.use('/api', router)
 
 if (isDev) {
   // $FlowIgnore

+ 64 - 0
server/router.js

@@ -0,0 +1,64 @@
+/*
+Copyright (C) 2019  Cloudbase Solutions SRL
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Affero General Public License for more details.
+You should have received a copy of the GNU Affero General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+// @flow
+
+import requireWithoutCache from 'require-without-cache'
+import express from 'express'
+import bodyParser from 'body-parser'
+import AdmZip from 'adm-zip'
+import stream from 'stream'
+
+import packageJson from '../package.json'
+
+import type { ZipContent } from '../src/types/ZipContent'
+
+const router = express.Router()
+
+router.use(bodyParser.json())
+
+// $FlowIgnore
+router.get('/version', (req, res) => {
+  res.json({ version: packageJson.version })
+})
+
+// $FlowIgnore
+router.get('/config', (req, res) => {
+  res.send(requireWithoutCache('../config.js', require).config)
+})
+
+// $FlowIgnore
+router.post('/download-zip', (req, res) => {
+  try {
+    let contents: ZipContent[] = req.body.contents
+    if (!contents || !contents.length || !contents[0].filename || typeof contents[0].content !== 'string') {
+      throw new Error()
+    }
+    let zip = new AdmZip()
+    contents.forEach(content => {
+      zip.addFile(content.filename, Buffer.alloc(content.content.length, content.content))
+    })
+    let zipBuffer = zip.toBuffer()
+    let readStream = new stream.PassThrough()
+    readStream.end(zipBuffer)
+    res.set('Content-Disposition', 'attachment; filename=contents.zip')
+    res.set('Content-Type', 'text/plain')
+    readStream.pipe(res)
+  } catch (err) {
+    console.error(err)
+    res.status(500).json({ error: { message: 'Invalid request body for download zip API' } })
+  }
+})
+
+export default router

+ 1 - 3
src/components/pages/EndpointsPage/EndpointsPage.jsx

@@ -194,9 +194,7 @@ class EndpointsPage extends React.Component<{ history: any }, State> {
   }
 
   handleExportToJson() {
-    this.state.selectedEndpoints.forEach(endpoint => {
-      endpointStore.exportToJson(endpoint)
-    })
+    endpointStore.exportToZip(this.state.selectedEndpoints)
   }
 
   handleDeleteAction() {

+ 28 - 0
src/stores/EndpointStore.js

@@ -17,7 +17,9 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 import { observable, runInAction, action } from 'mobx'
 
 import type { Endpoint, Validation, StorageBackend, Storage } from '../types/Endpoint'
+import type { ZipContent } from '../types/ZipContent'
 
+import apiCaller from '../utils/ApiCaller'
 import notificationStore from './NotificationStore'
 import EndpointSource from '../sources/EndpointSource'
 
@@ -148,6 +150,32 @@ class EndpointStore {
     DomUtils.download(JSON.stringify(endpoint), `${endpoint.name}.endpoint`)
   }
 
+  @action async exportToZip(endpoints: Endpoint[]): Promise<void> {
+    await Promise.all(endpoints.map(async endpoint => {
+      let connectionInfo = await EndpointSource.getConnectionInfo(endpoint)
+      endpoint.connection_info = connectionInfo
+    }))
+    let zipContents: ZipContent[] = endpoints.map(endpoint => ({
+      filename: `${endpoint.name}.endpoint`,
+      content: JSON.stringify(endpoint),
+    }))
+    let response = await apiCaller.send({
+      url: '/api/download-zip',
+      data: { contents: zipContents },
+      method: 'POST',
+      responseType: 'blob',
+    })
+    const url = window.URL.createObjectURL(new Blob([response.data]))
+    const link = document.createElement('a')
+    link.href = url
+    link.setAttribute('download', 'coriolis-endpoints.zip')
+    if (document.body) {
+      document.body.appendChild(link)
+    }
+    link.click()
+    link.remove()
+  }
+
   @action setConnectionInfo(connectionInfo: $PropertyType<Endpoint, 'connection_info'>) {
     this.connectionInfo = connectionInfo
     this.connectionInfoLoading = false

+ 1 - 1
src/stores/LicenceStore.js

@@ -32,7 +32,7 @@ class LicenceStore {
       return this.version
     }
 
-    let response = await apiCaller.get('/version')
+    let response = await apiCaller.get('/api/version')
     runInAction(() => {
       this.version = response.data.version
     })

+ 20 - 0
src/types/ZipContent.js

@@ -0,0 +1,20 @@
+/*
+Copyright (C) 2019  Cloudbase Solutions SRL
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Affero General Public License for more details.
+You should have received a copy of the GNU Affero General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+// @flow
+
+export type ZipContent = {
+  filename: string,
+  content: string,
+}

+ 1 - 1
src/utils/Config.js

@@ -8,7 +8,7 @@ class ConfigLoader {
   config: Config
 
   async load() {
-    let res = await apiCaller.get('/config')
+    let res = await apiCaller.get('/api/config')
     this.config = res.data
   }
 }

+ 5 - 0
yarn.lock

@@ -317,6 +317,11 @@ adal-node@^0.1.25:
     xmldom ">= 0.1.x"
     xpath.js "~1.0.5"
 
+adm-zip@^0.4.13:
+  version "0.4.13"
+  resolved "https://registry.yarnpkg.com/adm-zip/-/adm-zip-0.4.13.tgz#597e2f8cc3672151e1307d3e95cddbc75672314a"
+  integrity sha512-fERNJX8sOXfel6qCBCMPvZLzENBEhZTzKqg6vrOW5pvoEaQuJhRU4ndTAh6lHOxn1I6jnz2NHra56ZODM751uw==
+
 agent-base@4, agent-base@^4.1.0:
   version "4.2.1"
   resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.2.1.tgz#d89e5999f797875674c07d87f260fc41e83e8ca9"