Sfoglia il codice sorgente

启动器0.1.3版本 内测版

laiguoran 6 anni fa
parent
commit
22ca335ee3

BIN
data/fileInfo.dll


File diff suppressed because it is too large
+ 1 - 1
data/sc_software.json


+ 2 - 1
package.json

@@ -1,6 +1,6 @@
 {
   "name": "startup",
-  "version": "0.1.2",
+  "version": "0.1.3",
   "author": "珠海纵横软件有限公司",
   "description": "启动器",
   "license": null,
@@ -54,6 +54,7 @@
   },
   "dependencies": {
     "axios": "^0.18.0",
+    "compressing": "^1.2.4",
     "electron-dl": "^1.12.0",
     "electron-updater": "^2.21.10",
     "element-ui": "^2.4.1",

File diff suppressed because it is too large
+ 1 - 1
src/database/index.js


+ 2 - 0
src/main/index.js

@@ -5,6 +5,7 @@ import usbffi from './main-process/usb-ffi'
 import downloads from './main-process/downloads'
 import updateInstall from './main-process/updateInstall'
 import fileselect from './main-process/file-select'
+import exeinstall from './main-process/exe-install'
 import db from '../database'
 const path = require('path')
 // const glob = require('glob')
@@ -102,6 +103,7 @@ function initialize () {
     downloads(win)
     updateInstall(win)
     fileselect(win)
+    exeinstall(win)
     // require('./main-process/downloads').initialize(win)
     // let files = glob.sync(path.join(__dirname, './main-process/*.js'))
     // if (files !== []) {

+ 43 - 6
src/main/main-process/downloads.js

@@ -15,7 +15,10 @@ const folderpath = 'data/software'
 const download = require('electron-dl').download
 // const BrowserWindow = electron.BrowserWindow
 const app = electron.app
-const fs = require('fs-extra')
+const fs = require('fs')
+const fse = require('fs-extra')
+const compressing = require('compressing')
+const mkdirp = require('mkdirp')
 const __appPath = app.getPath('userData')
 
 const downloads = function (win) {
@@ -42,7 +45,7 @@ const downloads = function (win) {
 
   ipcMain.on('download', (event, args) => {
     let info = db.read().get('sc_download').getById(args.id).value()
-    let downloadpath = info.downloadpath
+    let downloadpath = info.down_url
     let options = {
       saveAs: false,
       directory: path.join(__appPath, folderpath),
@@ -61,16 +64,50 @@ const downloads = function (win) {
           info.pause = false
           info.resume = false
           db.read().get('sc_download').getById(args.id).assign(info).write()
-          fs.removeSync(path.join(__appPath, folderpath, info.savepath))
+          fse.removeSync(path.join(__appPath, folderpath, info.savepath))
         } catch (err) {
           console.log(err)
         }
       }
     }
     download(win, downloadpath, options)
-      .then(
-        dl => win.webContents.send('downloadsuccess', {id: args.id, savepath: path.join(__appPath, folderpath, info.savepath), index: args.index})
-      )
+      .then(function (dl) {
+        try {
+          let zippath = path.join(__appPath, folderpath)
+          let zipname = info.savepath
+          let exename = ''
+          fs.createReadStream(path.join(zippath, zipname))
+            .on('error', function (error) {
+              throw error
+            })
+            .pipe(new compressing.zip.UncompressStream())
+            .on('error', function (error) {
+              throw error
+            })
+            .on('finish', function () {
+              // 完成后删除zip压缩包
+              fs.unlinkSync(path.join(zippath, zipname))
+              console.log('ok')
+              win.webContents.send('downloadsuccess', {id: args.id, savepath: path.join(zippath, exename), index: args.index})
+            })
+            .on('entry', function (header, stream, next) {
+              stream.on('end', next)
+              // header.type => file | directory
+              // header.name => path name
+              exename = header.name
+              if (header.type === 'file') {
+                stream.pipe(fs.createWriteStream(path.join(zippath, header.name)))
+              } else { // directory
+                mkdirp(path.join(zippath, header.name), err => {
+                  if (err) throw err
+                  stream.resume()
+                })
+              }
+            })
+        } catch (err) {
+          console.log(err)
+        }
+      })
       .catch(console.error)
   })
 }

+ 127 - 0
src/main/main-process/exe-install.js

@@ -0,0 +1,127 @@
+'use strict'
+
+/**
+ * 软件安装检测
+ *
+ * @author EllisRan.
+ * @date 2018/8/9
+ * @version
+ */
+import db from '../../database'
+const path = require('path')
+const ffi = require('ffi')
+const fs = require('fs')
+const electron = require('electron')
+const ipcMain = electron.ipcMain
+const regeditPath64 = 'HKLM\\SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall'
+const regeditPath32 = 'HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall'
+const regeditPath = isOSWin64() ? regeditPath64 : regeditPath32
+const regedit = require('regedit')
+
+const exeinstall = function (win) {
+  ipcMain.on('checkExeInstall', (event, args) => {
+    let info = args.info
+    try {
+      // 先检查数据库中是否已存在该注册表软件的信息,有则退出,无则继续
+      let exesqlinfo = db.read().get('sc_exeData').find({ regeditName: info.regedit, fileVersion: info.version }).value()
+      if (exesqlinfo !== undefined) {
+        event.sender.send('InstallResult', { status: 400, msg: `启动器已存在相同版本的 ${info.fulltitle} 软件`, id: info.id, index: args.index })
+      } else {
+        let regeditpath = path.join(regeditPath, info.regedit)
+        regedit.list(regeditpath, async function (err, result) {
+          if (err === null && result !== undefined) {
+            let values = result[regeditpath].values
+            let regeditMsg = {
+              path: values['Inno Setup: App Path'].value,
+              name: values['DisplayName'].value,
+              regeditName: info.regedit,
+              fileName: values['Inno Setup: Icon Group'].value,
+              simpleName: info.product_title,
+              versionName: info.title,
+              keyNumber: info.key_number,
+              keytype: info.keytype,
+              isshow: true,
+              auto: false,
+              pid: info.pid,
+              product_id: info.product_id,
+              down_id: info.down_id,
+              product_version: info.version,
+              show_tip: false,
+              show_updateVersion: info.version
+            }
+            let data = await checkDirectory(regeditMsg.path)
+            for (let i = 0; i < data.length; i++) {
+              let exeinfo = regeditMsg
+              exeinfo.exeName = data[i].exeName
+              exeinfo.fileVersion = data[i].FileVersion
+              exeinfo.productName = data[i].ProductName
+              exeinfo.productVersion = data[i].ProductVersion
+              exeinfo.fileDescription = data[i].FileDescription
+              exeinfo.addtime = Date.parse(new Date()) / 1000
+              db.read().get('sc_exeData').insert(exeinfo).write()
+            }
+            db.read().get('sc_productData').updateById(info.pid, { isshow: true }).write()
+            await db.read().get('sc_productData').updateById(info.pid, { isshow: true }).write()
+            await db.read().set('sc_hadInstall.url', '/softwarestartup/' + info.pid).write()
+            event.sender.send('InstallResult', { status: 200, msg: data.length, id: info.id, index: args.index })
+            win.webContents.send('successUpdate', { id: info.pid, num: data.length, delnum: 0 })
+          } else {
+            event.sender.send('InstallResult', { status: 400, msg: `未能成功安装 ${info.fulltitle} 软件`, id: info.id, index: args.index })
+          }
+        })
+      }
+    } catch (err) {
+      event.sender.send('InstallResult', { status: 400, msg: err, id: info.id, index: args.index })
+    }
+  })
+
+  /**
+   * 判断文件名是否包含纵横软件exe的部分名称
+   * @param item
+   * @return {boolean}
+   */
+  function existSoftwareName (item) {
+    // 纵横软件exe所带包含的文件名
+    const smartcostSoftwareHeader = ['SmartCost', 'Measure', 'DrawingBuilder', 'BillsEditor']
+    let flag = false
+    for (let i = 0; i < smartcostSoftwareHeader.length; i++) {
+      if (item.indexOf(smartcostSoftwareHeader[i]) !== -1) {
+        flag = true
+        break
+      }
+    }
+    return flag
+  }
+
+  /**
+   * 检查文件夹并判断是否包含纵横软件(一个文件可能存在多个纵横软件的)
+   * @param files
+   * @return {Promise.<Array>}
+   */
+  async function checkDirectory (files) {
+    let data = []
+    let dirname = await fs.readdirSync(files)
+    dirname.forEach(async function (item, index) {
+      if (path.extname(item) === '.exe' && existSoftwareName(item)) {
+        let pathstr = path.join('data/fileInfo.dll')
+        let libm = ffi.Library(pathstr, {
+          'GetFileInfo': ['string', ['string']]
+        })
+        let fileinfo = JSON.parse(await libm.GetFileInfo(path.join(files, item)))
+        fileinfo.exeName = item
+        data.push(fileinfo)
+      }
+    })
+    return data
+  }
+}
+
+/**
+ * 判断系统位数
+ * @return {boolean}
+ */
+function isOSWin64 () {
+  return process.arch === 'x64' || process.env.hasOwnProperty('PROCESSOR_ARCHITEW6432')
+}
+
+export default exeinstall

+ 151 - 24
src/main/main-process/file-select.js

@@ -11,9 +11,14 @@ import db from '../../database'
 const path = require('path')
 const ffi = require('ffi')
 const fs = require('fs')
+const fse = require('fs-extra')
 const electron = require('electron')
 const ipcMain = electron.ipcMain
 const dialog = electron.dialog
+const regeditPath64 = 'HKLM\\SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall'
+const regeditPath32 = 'HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall'
+const regeditPath = isOSWin64() ? regeditPath64 : regeditPath32
+const regedit = require('regedit')
 
 const fileselect = function (win) {
   ipcMain.on('file-select', function (event) {
@@ -27,7 +32,10 @@ const fileselect = function (win) {
         } else {
           let addnum = await checkExeName(files[0], data)
           if (addnum !== 0) {
-            event.sender.send('successUpdate', { id: '1', num: addnum, delnum: 0 })
+            let info = await db.read().get('sc_exeData').last().value()
+            await db.read().get('sc_productData').updateById(info.pid, { isshow: true }).write()
+            await db.read().set('sc_hadInstall.url', '/softwarestartup/' + info.pid).write()
+            event.sender.send('successUpdate', { id: info.pid, num: addnum, delnum: 0 })
           } else {
             event.sender.send('failedDirectory', { msg: '该文件夹中的纵横软件已存在启动器中' })
           }
@@ -63,44 +71,65 @@ async function checkDirectory (files) {
 
 /**
  * 判断数据库是否已存在该文件信息,不存在则添加到数据库中
+ * @param Directory
  * @param files
- * @return {Promise.<Array>}
+ * @return {Promise.<number>}
  */
 async function checkExeName (Directory, files) {
   let index = Directory.lastIndexOf('\\')
   let filename = Directory.substring(index + 1, Directory.length)
   let addnum = 0
+  let addflag = false
   for (let i = 0; i < files.length; i++) {
     let exeinfo = await db.read().get('sc_exeData').find({ path: Directory, exeName: files[i].exeName, fileVersion: files[i].FileVersion }).value()
     if (!exeinfo) {
-      let exeData = {
-        path: Directory,
-        name: filename,
-        regeditName: '',
-        fileName: filename,
-        simpleName: '',
-        versionName: '',
-        fileVersion: files[i].FileVersion,
-        productName: files[i].ProductName,
-        productVersion: files[i].ProductVersion,
-        fileDescription: files[i].FileDescription,
-        exeName: files[i].exeName,
-        pid: '1',
-        keyNumber: '',
-        keytype: '',
-        addtime: Date.parse(new Date()) / 1000,
-        isshow: true,
-        auto: false
+      addflag = true
+      break
+    }
+  }
+  if (addflag) {
+    // 再和注册表内容对应,有则添加,无则为未分类
+    let regeditMsg = await checkRegeditbyDirectory(Directory)
+    await sleep(5000)
+    for (let i = 0; i < files.length; i++) {
+      let exeinfo = await db.read().get('sc_exeData').find({ path: Directory, exeName: files[i].exeName, fileVersion: files[i].FileVersion }).value()
+      if (!exeinfo) {
+        let exeData = {
+          path: Directory,
+          name: regeditMsg.name !== '' ? regeditMsg.name : filename,
+          regeditName: regeditMsg.regeditName,
+          fileName: regeditMsg.fileName !== '' ? regeditMsg.fileName : filename,
+          simpleName: regeditMsg.simpleName,
+          versionName: regeditMsg.versionName,
+          exeName: files[i].exeName,
+          fileVersion: files[i].FileVersion,
+          productName: files[i].ProductName,
+          productVersion: files[i].ProductVersion,
+          fileDescription: files[i].FileDescription,
+          pid: regeditMsg.pid,
+          keyNumber: regeditMsg.keyNumber,
+          keytype: regeditMsg.keytype,
+          product_id: regeditMsg.product_id,
+          down_id: regeditMsg.down_id,
+          product_version: regeditMsg.product_version,
+          show_tip: false,
+          show_updateVersion: regeditMsg.product_version,
+          addtime: Date.parse(new Date()) / 1000,
+          isshow: true,
+          auto: false
+        }
+        await db.read().get('sc_exeData').insert(exeData).write()
+        ++addnum
       }
-      await db.read().get('sc_exeData').insert(exeData).write()
-      await db.read().get('sc_productData').updateById(1, { isshow: true }).write()
-      await db.read().set('sc_hadInstall.url', '/softwarestartup/1').write()
-      ++addnum
     }
   }
   return addnum
 }
 
+const sleep = (timeout = 2000) => new Promise(resolve => {
+  setTimeout(resolve, timeout)
+})
+
 /**
  * 判断文件名是否包含纵横软件exe的部分名称
  * @param item
@@ -119,4 +148,102 @@ function existSoftwareName (item) {
   return flag
 }
 
+/**
+ * 纵横注册表信息中是否存在该文件夹,存在则返回该注册表信息,并加入到产品数据库中
+ * @param directory
+ * @return {{name: string, regeditName: string, simpleName: string, versionName: string, pid: string, keyNumber: string, keytype: string}}
+ */
+async function checkRegeditbyDirectory (directory) {
+  let regeditMsg = {
+    name: '',
+    fileName: '',
+    regeditName: '',
+    simpleName: '',
+    versionName: '',
+    pid: '1',
+    keyNumber: '',
+    keytype: '',
+    product_id: '',
+    down_id: '',
+    product_version: ''
+  }
+  regedit.list(regeditPath).on('data', async function (result) {
+    try {
+      let softwarelist = result.data.keys
+      for (let i in softwarelist) {
+        if (softwarelist[i].indexOf('SmartCost_') !== -1) {
+          regedit.list(regeditPath + '\\' + softwarelist[i]).on('data', async function (result2) {
+            let values = result2.data.values
+            let directoryPath = values['Inno Setup: App Path'].value
+            if (directoryPath === directory) {
+              let simpleVersion = values['DisplayVersion'] === undefined || values['DisplayVersion'] === null ? '' : values['DisplayVersion'].value.split('_')
+              regeditMsg.name = values['DisplayName'].value
+              regeditMsg.fileName = values['Inno Setup: Icon Group'].value
+              regeditMsg.regeditName = softwarelist[i]
+              regeditMsg.simpleName = simpleVersion === '' ? '' : simpleVersion[0]
+              regeditMsg.versionName = simpleVersion === '' ? '' : simpleVersion[1]
+              let softwarejson = path.join('data/sc_software.json')
+              let downlist = fse.readJsonSync(softwarejson).sc_down
+              let downinfo = downlist.find(function (item) {
+                return item.regedit === regeditMsg.regeditName
+              })
+              let ptitle = downinfo !== undefined ? downinfo.product_title : regeditMsg.simpleName
+              // 查找数据库是否存在该产品,有则添加,无则根据simplename 新增产品,simplename为空的话,则为未分类
+              // 新方法,先关联注册表名查找数据,然后再关联产品数据库生成
+              if (ptitle !== '') {
+                if (regeditMsg.simpleName === '') {
+                  regeditMsg.simpleName = downinfo.product_title
+                  regeditMsg.versionName = downinfo.title
+                }
+                let productInfo = db.read().get('sc_productData').find({ title: ptitle }).value()
+                if (productInfo !== undefined) {
+                  regeditMsg.pid = productInfo.id
+                  regeditMsg.product_version = productInfo.version
+                } else {
+                  // 查找sc_software.json文件,有则新增产品,无则为未分类(旧的也会归这类)
+                  let scproductlist = fse.readJsonSync(softwarejson).sc_product
+                  let scproductinfo = scproductlist.find(function (item) {
+                    return item.title === ptitle
+                  })
+                  if (scproductinfo !== undefined) {
+                    let addproduct = scproductinfo
+                    addproduct.addtime = Date.parse(new Date()) / 1000
+                    addproduct.isshow = false
+                    let info = db.read().get('sc_productData').insert(addproduct).write()
+                    regeditMsg.pid = info.id
+                    regeditMsg.product_version = info.version
+                  }
+                }
+                // 锁号和锁类型添加
+                // let downlist = fse.readJsonSync(softwarejson).sc_down
+                // let downinfo = downlist.find(function (item) {
+                //   return item.product_title === regeditMsg.simpleName && item.title === regeditMsg.versionName
+                // })
+                if (downinfo !== undefined) {
+                  regeditMsg.keyNumber = downinfo.key_number
+                  regeditMsg.keytype = downinfo.keytype
+                  regeditMsg.product_id = downinfo.product_id
+                  regeditMsg.down_id = downinfo.down_id
+                }
+              }
+              return regeditMsg
+            }
+          })
+        }
+      }
+    } catch (err) {
+      console.log(err)
+    }
+  })
+  return regeditMsg
+}
+
+/**
+ * 判断系统位数
+ * @return {boolean}
+ */
+function isOSWin64 () {
+  return process.arch === 'x64' || process.env.hasOwnProperty('PROCESSOR_ARCHITEW6432')
+}
+
 export default fileselect

+ 38 - 24
src/main/main-process/updateInstall.js

@@ -100,16 +100,16 @@ const updateInstall = function (win) {
     let num = await db.read().get('sc_exeData').filter({ auto: true, isshow: false }).size().value()
     // 先让默认的“未分类” isshow:true,免于被删除,当不存在手动添加的软件或自动检测软件的时候,暂时隐藏“未分类”,
     // 当启动器不存在任何软件时,默认显示“未分类”
-    let manualnum = db.read().get('sc_exeData').filter({ auto: false }).size().value()
-    let flag = db.read().get('sc_productData').getById('1').value().isshow
-    if (!flag) {
-      db.read().get('sc_productData').updateById('1', { isshow: true }).write()
-    }
-    db.read().get('sc_productData').removeWhere({ isshow: false }).write()
-    let allnum = db.read().get('sc_exeData').size().value()
-    if (manualnum === 0 && !flag && allnum !== 0) {
-      db.read().get('sc_productData').updateById('1', { isshow: false }).write()
-    }
+    // let manualnum = db.read().get('sc_exeData').filter({ auto: false }).size().value()
+    // let flag = db.read().get('sc_productData').getById('1').value().isshow
+    // if (!flag) {
+    //   db.read().get('sc_productData').updateById('1', { isshow: true }).write()
+    // }
+    // db.read().get('sc_productData').removeWhere({ isshow: false }).write()
+    // let allnum = db.read().get('sc_exeData').size().value()
+    // if (manualnum === 0 && !flag && allnum !== 0) {
+    //   db.read().get('sc_productData').updateById('1', { isshow: false }).write()
+    // }
     db.read().get('sc_exeData').removeWhere({ auto: true, isshow: false }).write()
     return num
   }
@@ -172,22 +172,26 @@ const updateInstall = function (win) {
       let dbExeData = db.read().get('sc_exeData').find({ exeName: exeData.exeName, path: exeData.path, fileVersion: exeData.fileVersion }).value()
       if (dbExeData === undefined || dbExeData === null) {
         // 先判断是否存在该产品数据库(同时关联到sc_sofeware.json文件数据),再加入安装包数据库
-        if (exeData.simpleName !== '') {
-          let softwarejson = path.join('data/sc_software.json')
-          let productInfo = db.read().get('sc_productData').find({ title: exeData.simpleName }).value()
+        // 新方法,先关联注册表名查找数据,然后再关联产品数据库生成
+        let softwarejson = path.join('data/sc_software.json')
+        let downlist = fse.readJsonSync(softwarejson).sc_down
+        let downinfo = downlist.find(function (item) {
+          return item.regedit === exeData.regeditName
+        })
+        let ptitle = downinfo !== undefined ? downinfo.product_title : exeData.simpleName
+        if (ptitle !== '') {
+          if (exeData.simpleName === '') {
+            exeData.simpleName = downinfo.product_title
+            exeData.versionName = downinfo.title
+          }
+          let productInfo = db.read().get('sc_productData').find({ title: ptitle }).value()
           if (productInfo === undefined || productInfo === null) {
             let productlist = fse.readJsonSync(softwarejson).sc_product
             for (let i in productlist) {
-              if (productlist[i].title === exeData.simpleName) {
-                let productInfo2 = {
-                  readid: productlist[i].product_id,
-                  title: productlist[i].title,
-                  productName: productlist[i].productName,
-                  src: productlist[i].src,
-                  content: productlist[i].content,
-                  addtime: Date.parse(new Date()) / 1000,
-                  isshow: true
-                }
+              if (productlist[i].title === ptitle) {
+                let productInfo2 = productlist[i]
+                productInfo2.addtime = Date.parse(new Date()) / 1000
+                productInfo2.isshow = true
                 let insertproudct = db.read().get('sc_productData').insert(productInfo2).write()
                 exeData.pid = insertproudct.id
                 productInfo = insertproudct
@@ -201,9 +205,14 @@ const updateInstall = function (win) {
           // 获取json文件中的锁号,更新到exe当中
           let downlist = fse.readJsonSync(softwarejson).sc_down
           for (let j in downlist) {
-            if (productInfo.readid === downlist[j].product_id && exeData.versionName === downlist[j].title) {
+            if (productInfo.product_id === downlist[j].product_id && exeData.versionName === downlist[j].title) {
               exeData.keyNumber = downlist[j].key_number
               exeData.keytype = downlist[j].keytype
+              exeData.product_id = downlist[j].product_id
+              exeData.down_id = downlist[j].down_id
+              exeData.product_version = productInfo.version
+              exeData.show_tip = false
+              exeData.show_updateVersion = productInfo.version
               break
             }
           }
@@ -211,6 +220,11 @@ const updateInstall = function (win) {
           exeData.pid = '1'
           exeData.keyNumber = ''
           exeData.keytype = ''
+          exeData.product_id = ''
+          exeData.down_id = ''
+          exeData.product_version = ''
+          exeData.show_tip = false
+          exeData.show_updateVersion = exeData.fileVersion
           db.read().get('sc_productData').updateById('1', { isshow: true }).write()
         }
         exeData.addtime = Date.parse(new Date()) / 1000

+ 53 - 4
src/renderer/components/StartUpPage.vue

@@ -27,11 +27,45 @@
   import DownloadHeader from './StartUpPage/DownloadHeader'
   const electron = require('electron').remote
   const BrowserWindow = electron.BrowserWindow
+  const fs = require('fs-extra')
+  const path = require('path')
 
   export default {
     name: 'element-page',
     mixins: [mixin],
     components: { SoftwareUpdate, UsbHeader, DownloadHeader },
+    created () {
+      // 更改为启动前获取最新官网产品数据
+      let softwarejson = path.join('data/sc_software.json')
+      if (this.checkOnline()) {
+        let version = fs.readJsonSync(softwarejson).startup_version
+        let self = this
+        this.$http({
+          url: 'https://smartcost.com.cn/startup/sc_version.json?' + this.RndNum(),
+          method: 'get',
+          timeout: 5000
+        }).then(function (response) {
+          if (version !== response.data.startup_version) {
+            // 加入随机数防止缓存
+            self.$http.get('https://smartcost.com.cn/startup/sc_software.json?' + self.RndNum())
+              .then(function (response2) {
+                fs.writeJsonSync(softwarejson, response2.data)
+              })
+              .catch(function (error2) {
+                throw error2
+              })
+          }
+        }).catch(function () {
+        })
+
+        // 启动前检测数据库一些异常情况,恢复到上一个状态,暂时包含下载列,安装列
+        let downinfo = this.$db.read().get('sc_download').find({ start: false, pause: true }).value()
+        if (downinfo !== undefined) {
+          this.$db.read().get('sc_download').updateById(downinfo.id, { start: true, pause: false }).write()
+        }
+        this.$db.read().get('sc_download').updateWhere({ status: 3 }, { status: 2 }).write()
+      }
+    },
     methods: {
       minimizeWindow () {
         const window = BrowserWindow.getFocusedWindow()
@@ -46,8 +80,23 @@
         let url = first ? { path: '/firstopen' } : { path: this.$db.read().get('sc_hadInstall.url').value() }
         this.$router.push(url)
       },
-      relationDownload (name, downloadpath, size) {
-        this.$refs.download.adddownload(name, downloadpath, size)
+      relationDownload (downID) {
+        this.$refs.download.adddownload(downID)
+      },
+      checkOnline () {
+        if (!navigator.onLine) {
+          return false
+        } else {
+          return true
+        }
+      },
+      // 产生随机数函数
+      RndNum (n = 6) {
+        let rnd = ''
+        for (let i = 0; i < n; i++) {
+          rnd += Math.floor(Math.random() * 10)
+        }
+        return rnd
       }
     }
   }
@@ -71,7 +120,7 @@
   }
 
   .header{
-    background-color: rgba(255,101,1,0.8);
+    background-color: rgba(0,153,204,1);
     color: #fff;
     -webkit-app-region: drag
   }
@@ -88,7 +137,7 @@
   }
 
   .border {
-    border-color: rgba(255,101,1,0.8) !important;
+    border-color: rgba(0,153,204,0.8) !important
   }
 
   .btn-block {

+ 113 - 38
src/renderer/components/StartUpPage/DownloadHeader.vue

@@ -15,17 +15,19 @@
             </span>
             &nbsp;&nbsp;{{ item.size }}
           </span>
-          {{ item.name }}
+          {{ item.fulltitle }}
         </div>
         <div class="pb-2" v-else>
           <span class="float-right">
             <i class="fas fa-times text-danger" @click="deleteDownload(item.id, index)"></i>
-          </span>{{ item.name }}
+          </span>{{ item.fulltitle }}
         </div>
-        <div class="pb-2" v-if="item.isfinish === false">
+        <div class="pb-2" v-if="item.status === 1">
           <el-progress :text-inside="true" :stroke-width="18" :percentage="item.percent" status="success"></el-progress>
         </div>
-        <el-button v-else @click="installbtn(item.id)" type="primary" size="medium" class="btn-sm btn-block btn-blue">安装软件</el-button>
+        <el-button v-else-if="item.status === 2" @click="installbtn(item.id, index)" type="primary" size="medium" class="btn-sm btn-block btn-blue">安装软件</el-button>
+        <el-button v-else-if="item.status === 3" type="primary" size="medium" class="btn-sm btn-block btn-blue">正在安装本软件...</el-button>
+        <el-button v-else-if="item.status === 4" type="primary" size="medium" class="btn-sm btn-block btn-blue">已安装</el-button>
       </div>
     </el-dropdown-menu>
   </el-dropdown>
@@ -33,9 +35,10 @@
 
 <script>
   import mixin from '../mixin'
+  const fs = require('fs-extra')
+  const path = require('path')
   const lodashId = require('lodash-id')
-  // const path = require('path')
-  // const fs = require('fs-extra')
+  const ffi = require('ffi')
   export default {
     mixins: [mixin],
     data () {
@@ -62,11 +65,12 @@
         info.show = false
         info.pause = false
         info.savepath = msg.savepath
+        info.status = 2
         this.$db.read().get('sc_download').getById(msg.id).assign(info).write()
         // 通知下载完成
         let notification = {
           title: '下载器',
-          body: info.name + '下载完成'
+          body: info.fulltitle + '下载完成'
         }
         let myNotification = new window.Notification(notification.title, notification)
         this.downloads.splice(msg.index, 1, info)
@@ -84,6 +88,20 @@
           return true
         }
       })
+
+      this.$electron.ipcRenderer.on('InstallResult', (event, item) => {
+        let info = this.$db.read().get('sc_download').getById(item.id).value()
+        if (item.status === 400) {
+          this.$message.error(item.msg)
+          this.$db.read().get('sc_download').updateById(item.id, { status: 2 }).write()
+          info.status = 2
+          this.downloads.splice(item.index, 1, info)
+        } else {
+          this.$db.read().get('sc_download').updateById(item.id, { status: 4 }).write()
+          info.status = 4
+          this.downloads.splice(item.index, 1, info)
+        }
+      })
     },
     beforeDestroy () {
     },
@@ -133,25 +151,39 @@
           console.log(err)
         }
       },
-      adddownload (name, downloadpath, size) {
+      adddownload (downID) {
         if (this.checkOnline()) {
           this.$refs.downloadmenu.show()
-          let downloadlist = {
-            'name': name,
-            'downloadpath': downloadpath,
-            'size': size,
-            'isfinish': false,
-            'percent': 0,
-            'savepath': downloadpath.substring(downloadpath.lastIndexOf('/') + 1),
-            'addtime': Date.parse(new Date()) / 1000,
-            'show': false,
-            'pause': false,
-            'resume': false,
-            'start': true,
-            'delete': false
+          let softwarejson = path.join('data/sc_software.json')
+          let downlist = fs.readJsonSync(softwarejson).sc_down
+          let downinfo = downlist.find(function (item) {
+            return item.down_id === downID
+          })
+          downinfo.isfinish = false
+          downinfo.percent = 0
+          downinfo.savepath = downinfo.down_url.substring(downinfo.down_url.lastIndexOf('/') + 1)
+          downinfo.addtime = Date.parse(new Date()) / 1000
+          downinfo.show = false
+          downinfo.pause = false
+          downinfo.resume = false
+          downinfo.start = true
+          downinfo.delete = false
+          downinfo.status = 1
+          // 若产品不存在则插入到数据库sc_productData中,并隐藏
+          let productinfo = this.$db.read().get('sc_productData').find({ product_id: downinfo.product_id }).value()
+          if (productinfo === undefined) {
+            let productlist = fs.readJsonSync(softwarejson).sc_product
+            let scproductinfo = productlist.find(function (item) {
+              return item.product_id === downinfo.product_id
+            })
+            scproductinfo.addtime = Date.parse(new Date()) / 1000
+            scproductinfo.isshow = false
+            productinfo = this.$db.read().get('sc_productData').insert(scproductinfo).write()
           }
-          let info = this.$db.read().get('sc_download').insert(downloadlist).write()
-          this.downloads.push(downloadlist)
+          downinfo.pid = productinfo.id
+          downinfo.version = productinfo.version
+          let info = this.$db.read().get('sc_download').insert(downinfo).write()
+          this.downloads.push(downinfo)
           this.downloadNum = this.$db.read().get('sc_download').filter({delete: false}).size().value()
           // 如果downloads中没有未下载的info,就让新增的info进入下载过程
           let info2 = this.$db.read().get('sc_download').find({isfinish: false}).value()
@@ -163,22 +195,65 @@
         }
       },
       testbtn () {
-        const path = require('path')
-        const ffi = require('ffi')
-        let exeData = {
-          path: 'C:\\Program Files (x86)\\纵横软件\\广东公路造价编审系统(标准化2011)专业版 910(营改增)',
-          exeName: 'SmartCost_GuangDong.exe'
-        }
-        let pathstr = path.join('data/fileInfo.dll')
-        let libm = ffi.Library(pathstr, {
-          'GetFileInfo': ['string', ['string']]
-        })
-        let fileinfo = JSON.parse(libm.GetFileInfo(path.join(exeData.path, exeData.exeName)))
-        console.log(fileinfo)
+        this.adddownload('326')
       },
-      installbtn (id) {
+      installbtn (id, index) {
+        // 安装exe监测,5s后开始监听安装状态
         let info = this.$db.read().get('sc_download').getById(id).value()
-        this.$electron.shell.openItem(info.savepath)
+        if (info.status === 2) {
+          // 判断启动器是否已安装本软件
+          let exesqlinfo = this.$db.read().get('sc_exeData').find({ regeditName: info.regedit, fileVersion: info.version }).value()
+          if (exesqlinfo !== undefined) {
+            this.$message({
+              message: `启动器已存在相同版本的 ${info.fulltitle} 软件`,
+              iconClass: '',
+              type: 'error'
+            })
+          } else {
+            this.$electron.shell.openItem(info.savepath)
+            let self = this
+            setTimeout(function () {
+              info.status = 3
+              self.$db.read().get('sc_download').getById(id).assign(info).write()
+              self.downloads.splice(index, 1, info)
+              self.watchExeInstall(index, info)
+            }, 5000)
+          }
+        } else {
+          this.$message({
+            message: info.fulltitle + ' 正在安装中,请稍后......',
+            iconClass: '',
+            type: 'warning'
+          })
+        }
+      },
+      watchExeInstall (index, info) {
+        // 监听安装程序目录,不存在则结束监听,进入判断安装是否完成or取消
+        let self = this
+        let interval = setInterval(function () {
+          let result = self.checkExeExist(info.savepath)
+          if (result === 0) {
+            clearInterval(interval)
+            // 判断是否完成还是取消了
+
+            self.$electron.ipcRenderer.send('checkExeInstall', { index: index, info: info })
+          }
+        }, 3000)
+      },
+      checkExeExist (exepath) {
+        try {
+          let ffifile = path.join('data/fileInfo.dll')
+          let libm = ffi.Library(ffifile, {
+            'FindProcess': ['int', ['string']]
+          })
+          let exerun = libm.FindProcess(exepath)
+          if (exerun === 0) {
+            return 0
+          }
+          return 1
+        } catch (err) {
+          console.error('ffi.Library', err)
+        }
       }
     }
   }
@@ -211,7 +286,7 @@
   }
   .badge-info {
     color: #fff;
-    background-color: #17a2b8;
+    background-color: #ff9900;
   }
   .badge-pill {
     padding-right: .6em;

+ 13 - 15
src/renderer/components/StartUpPage/SoftwareDetail.vue

@@ -18,7 +18,7 @@
             <h5 class="w-25 d-inline-block">{{ down.title }}</h5>
             <div class="w-50 d-inline-block"><span v-if="down.key_number !== ''">锁号:{{ down.key_number }}</span></div>
             <div class="float-right">
-              <el-button type="primary" @click="downloadSoftware(down.fulltitle, down.down_url, down.size)" class="btn btn-primary btn-blue btn-sm d-inline-block">下载</el-button>
+              <el-button type="primary" @click="downloadSoftware(down.down_id, down.version)" class="btn btn-primary btn-blue btn-sm d-inline-block">下载</el-button>
             </div>
           </div>
         </el-tab-pane>
@@ -61,35 +61,33 @@
         let softwarejson = path.join('data/sc_software.json')
         let softwarelist = fs.readJsonSync(softwarejson).sc_product
         let pid = this.$route.params.pid
-        let items = ''
         let loadings = true
-        softwarelist.forEach(function (value) {
-          if (value.product_id === pid) {
-            items = value
+        let items = softwarelist.find(function (item) {
+          if (item.product_id === pid) {
             loadings = false
-            return false
           }
+          return item.product_id === pid
         })
         this.items = items
         let downlist = fs.readJsonSync(softwarejson).sc_down
-        let newdownlist = []
-        downlist.forEach(function (v) {
-          if (v.product_id === pid) {
-            newdownlist.push(v)
-          }
+        let newdownlist = downlist.filter(function (item) {
+          return item.product_id === pid
         })
         this.downlist = newdownlist
         this.loading = loadings
       },
-      downloadSoftware (name, downloadpath, size) {
+      downloadSoftware (downID, version) {
         // 先判断是否已在下载列表中,再加入列表中
-        let downloaditem = this.$db.read().get('sc_download').find({name: name, delete: false}).value()
+        let downloaditem = this.$db.read().get('sc_download').find({ down_id: downID, version: version }).value()
         if (!downloaditem) {
-          this.$emit('softwareDownload', name, downloadpath, size)
+          this.$emit('softwareDownload', downID)
         } else {
+          let info = this.downlist.find(function (item) {
+            return item.down_id === downID
+          })
           this.$message({
             showClose: true,
-            message: name + '已存在下载列中',
+            message: info.fulltitle + '已存在下载列中',
             iconClass: '',
             type: 'warning'
           })

+ 4 - 43
src/renderer/components/StartUpPage/SoftwareList.vue

@@ -1,6 +1,6 @@
 <template>
-  <div class="software-list pt-4 pb-2" v-loading="loading" element-loading-text="数据同步中">
-    <el-row type="flex" justify="center"  v-for="software in softwarelist" :key="software.product_id">
+  <div class="software-list pt-4 pb-2">
+    <el-row type="flex" justify="center" v-for="software in softwarelist" :key="software.product_id">
       <el-col :span="22" class="software-item">
         <router-link :to="{ name: 'software-detail', params: { pid: software.product_id }}">
           <div class="media">
@@ -34,51 +34,12 @@
     mixins: [mixin],
     data () {
       return {
-        softwarelist: '',
-        loading: false
+        softwarelist: ''
       }
     },
     created () {
       let softwarejson = path.join('data/sc_software.json')
-      if (this.checkOnline()) {
-        let version = fs.readJsonSync(softwarejson).startup_version
-        let self = this
-        this.$http({
-          url: 'http://www.smartcost.com.cn/startup/sc_version.json',
-          method: 'get',
-          timeout: 5000
-        }).then(function (response) {
-          if (version === response.data.startup_version) {
-            self.softwarelist = fs.readJsonSync(softwarejson).sc_product
-          } else {
-            self.loading = true
-            self.$http.get('http://www.smartcost.com.cn/startup/sc_software.json')
-              .then(function (response2) {
-                fs.writeJsonSync(softwarejson, response2.data)
-                self.softwarelist = response2.data.sc_product
-                self.loading = false
-              })
-              .catch(function (error2) {
-                self.loading = false
-                throw error2
-              })
-          }
-        })
-          .catch(function () {
-            self.$message.error('网络不佳,无法获取最新列表')
-          })
-      } else {
-        this.softwarelist = fs.readJsonSync(softwarejson).sc_product
-      }
-    },
-    methods: {
-      checkOnline () {
-        if (!navigator.onLine) {
-          return false
-        } else {
-          return true
-        }
-      }
+      this.softwarelist = fs.readJsonSync(softwarejson).sc_product
     }
   }
 </script>

+ 104 - 7
src/renderer/components/StartUpPage/SoftwareStartupDetail.vue

@@ -19,6 +19,18 @@
             <div class="w-50 d-inline-block">{{ exe.fileDescription }}</div>
             <div class="float-right">
               <el-button type="primary" class="btn btn-primary btn-blue btn-sm d-inline-block" @click="openExebtn(exe.id)">启动软件</el-button>
+              <b></b>
+              <el-button type="success" v-if="exe.show_tip === true" class="btn btn-success btn-sm d-inline-block" @click="updateExebtn(exe.id)">新版本</el-button>
+              <el-dialog
+                      title="软件更新"
+                      :visible.sync="dialogVisible"
+                      width="60%">
+                <div class="modal-body" v-html="productVer"></div>
+                <span slot="footer" class="dialog-footer">
+                  <el-button class="btn btn-sm btn-primary btn-blue" type="primary" @click="downloadbtn(did)">下载更新</el-button>
+                  <el-button class="btn btn-sm btn-secondary" @click="ignorebtn(prid, did)">忽略该版本</el-button>
+                </span>
+              </el-dialog>
             </div>
             <div>{{ exe.path }}</div>
           </div>
@@ -34,13 +46,19 @@
   import mixin from '../mixin'
   const path = require('path')
   const fs = require('fs')
+  const fse = require('fs-extra')
+  const ffi = require('ffi')
   export default {
     mixins: [mixin],
     data () {
       return {
         activeName: 'first',
         products: '',
-        exelist: ''
+        exelist: '',
+        dialogVisible: false,
+        productVer: '',
+        did: '',
+        prid: ''
       }
     },
     created () {
@@ -61,8 +79,22 @@
       fetchData () {
         let productid = this.$route.params.productid === undefined ? this.$db.read().get('sc_productData').last().value().id : this.$route.params.productid
         this.$db.read().set('sc_hadInstall.url', '/softwarestartup/' + productid).write()
-        this.products = this.$db.read().get('sc_productData').getById(productid).value()
-        this.exelist = this.$db.read().get('sc_exeData').filter({ pid: productid }).orderBy('addtime', 'desc').value()
+        let products = this.$db.read().get('sc_productData').getById(productid).value()
+        this.products = products
+        let exelist = this.$db.read().get('sc_exeData').filter({ pid: productid }).orderBy('addtime', 'desc').value()
+        let softwarejson = path.join('data/sc_software.json')
+        let productlist = fse.readJsonSync(softwarejson).sc_product
+        let scproductInfo = productlist.find(function (item) {
+          return item.product_id === products.product_id
+        })
+        for (let i = 0; i < exelist.length; i++) {
+          if (scproductInfo !== undefined && scproductInfo.version !== exelist[i].show_updateVersion) {
+            exelist[i].show_updateVersion = scproductInfo.version
+            exelist[i].show_tip = true
+            this.$db.read().get('sc_exeData').updateById(exelist[i].id, { show_updateVersion: scproductInfo.version, show_tip: true }).write()
+          }
+        }
+        this.exelist = exelist
         if (productid === '1') {
           this.activeName = 'first'
         }
@@ -72,10 +104,23 @@
         let file = fs.existsSync(path.join(info.path, info.exeName))
         let self = this
         if (file) {
-          self.$message.success('正在启动软件...')
-          setTimeout(function () {
-            self.$electron.shell.openItem(path.join(info.path, info.exeName))
-          }, 500)
+          // 还要检测软件是否升级了,升级了则要替换exeData
+          let exeVersion = self.getExeVersion(path.join(info.path, info.exeName))
+          if (exeVersion !== info.fileVersion) {
+            self.$confirm('该软件版本已升级,是否更新启动器数据?', '提示', {
+              confirmButtonText: '确定',
+              cancelButtonText: '取消',
+              type: 'error'
+            }).then(() => {
+              self.$electron.ipcRenderer.send('updateInstall')
+            }).catch(() => {
+            })
+          } else {
+            self.$message.success('正在启动软件...')
+            setTimeout(function () {
+              self.$electron.shell.openItem(path.join(info.path, info.exeName))
+            }, 500)
+          }
         } else {
           self.$confirm('该软件已卸载或路径不对了,是否移除?', '提示', {
             confirmButtonText: '确定',
@@ -83,6 +128,11 @@
             type: 'error'
           }).then(() => {
             self.$db.read().get('sc_exeData').removeById(id).write()
+            // 查找是否还存在其他产品软件,否则隐藏左边产品栏产品
+            let info2 = self.$db.read().get('sc_exeData').find({ pid: info.pid }).value()
+            if (info2 === undefined) {
+              self.$db.read().get('sc_productData').updateById(info.pid, { isshow: false }).write()
+            }
             self.fetchData()
             self.$message({
               type: 'success',
@@ -92,12 +142,40 @@
           })
         }
       },
+      updateExebtn (id) {
+        let info = this.$db.read().get('sc_exeData').getById(id).value()
+        let productInfo = this.$db.read().get('sc_productData').getById(info.pid).value()
+        this.productVer = productInfo.versionDesc
+        this.dialogVisible = true
+        this.did = id
+        this.prid = info.pid
+      },
+      downloadbtn (id) {
+        this.dialogVisible = false
+      },
+      ignorebtn (pid, id) {
+        this.$db.read().get('sc_exeData').updateById(id, { show_tip: false }).write()
+        this.exelist = this.$db.read().get('sc_exeData').filter({ pid: pid }).orderBy('addtime', 'desc').value()
+        this.dialogVisible = false
+      },
       checkOnline () {
         if (!navigator.onLine) {
           return false
         } else {
           return true
         }
+      },
+      getExeVersion (exepath) {
+        try {
+          let ffifile = path.join('data/fileInfo.dll')
+          let libm = ffi.Library(ffifile, {
+            'GetFileInfo': ['string', ['string']]
+          })
+          let fileinfo = JSON.parse(libm.GetFileInfo(exepath))
+          return fileinfo.FileVersion
+        } catch (err) {
+          console.error('ffi.Library', err)
+        }
       }
     }
   }
@@ -197,4 +275,23 @@
   .software-content img{
     width: 100%;
   }
+  .btn-success {
+    color: #fff !important;
+    background-color: #28a745 !important;
+    border-color: #28a745 !important;
+  }
+  .modal-body {
+    max-height: 230px;
+    overflow-y: auto;
+  }
+  .btn-secondary {
+    color: #fff;
+    background-color: #6c757d;
+    border-color: #6c757d;
+  }
+  .btn-secondary:hover {
+    color: #fff;
+    background-color: #5a6268;
+    border-color: #545b62;
+  }
 </style>

+ 7 - 8
src/renderer/components/StartUpPage/UsbHeader.vue

@@ -12,7 +12,7 @@
         <div class="pt-3 pb-2" v-for="product in lock.prolist">
           <div class="float-right">
             <el-button type="primary" class="btn btn-primary btn-blue btn-sm" v-if="product.status === 1" @click="openProduct(product.startpath)">启动</el-button>
-            <el-button type="primary" class="btn btn-primary btn-blue btn-sm" v-else-if="product.status === 2" @click="downloadProduct(product.down_name, product.down_url, product.down_size)">下载</el-button>
+            <el-button type="primary" class="btn btn-primary btn-blue btn-sm" v-else-if="product.status === 2" @click="downloadProduct(product.down_id, product.version)">下载</el-button>
           </div>
           {{ product.title }}
         </div>
@@ -69,9 +69,8 @@
                   pdata = {
                     title: downlist[z].product_title + ' ' + downlist[z].title,
                     status: 2,
-                    down_url: downlist[z].down_url,
-                    down_size: downlist[z].size,
-                    down_name: downlist[z].fulltitle
+                    down_id: downlist[z].down_id,
+                    version: downlist[z].version
                   }
                   flag = false
                   prolist.push(pdata)
@@ -102,16 +101,16 @@
       openProduct (item) {
         this.$electron.shell.openItem(item)
       },
-      downloadProduct (name, downloadpath, size) {
+      downloadProduct (downID, version) {
         // 先判断是否已在下载列表中,再加入列表中
-        let downloaditem = this.$db.read().get('sc_download').find({name: name, delete: false}).value()
+        let downloaditem = this.$db.read().get('sc_download').find({ down_id: downID, version: version }).value()
         if (!downloaditem) {
           this.$refs.usbmenu.hide()
-          this.$emit('softwareDownload', name, downloadpath, size)
+          this.$emit('softwareDownload', downID)
         } else {
           this.$message({
             showClose: true,
-            message: name + '已存在下载列中',
+            message: downloaditem.fulltitle + '已存在下载列中',
             iconClass: '',
             type: 'warning'
           })

+ 53 - 2
yarn.lock

@@ -1513,7 +1513,7 @@ buffer-alloc@^1.1.0:
     buffer-alloc-unsafe "^1.1.0"
     buffer-fill "^1.0.0"
 
-buffer-crc32@^0.2.1:
+buffer-crc32@^0.2.1, buffer-crc32@~0.2.3:
   version "0.2.13"
   resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242"
 
@@ -2080,6 +2080,19 @@ compressible@~2.0.13:
   dependencies:
     mime-db ">= 1.34.0 < 2"
 
+compressing@^1.2.4:
+  version "1.2.4"
+  resolved "https://registry.yarnpkg.com/compressing/-/compressing-1.2.4.tgz#451b437638e8da9f83ef2c330c5c6e4a4052647f"
+  dependencies:
+    flushwritable "^1.0.0"
+    get-ready "^1.0.0"
+    mkdirp "^0.5.1"
+    pump "^3.0.0"
+    streamifier "^0.1.1"
+    tar-stream "^1.5.2"
+    yauzl "^2.7.0"
+    yazl "^2.4.2"
+
 compression@^1.5.2:
   version "1.7.2"
   resolved "http://registry.npmjs.org/compression/-/compression-1.7.2.tgz#aaffbcd6aaf854b44ebb280353d5ad1651f59a69"
@@ -3721,6 +3734,12 @@ fd-slicer@~1.0.1:
   dependencies:
     pend "~1.2.0"
 
+fd-slicer@~1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e"
+  dependencies:
+    pend "~1.2.0"
+
 ffi@^2.2.0:
   version "2.2.0"
   resolved "https://registry.yarnpkg.com/ffi/-/ffi-2.2.0.tgz#bf18b04666a29f71227ed56895d5430af47042fa"
@@ -3853,6 +3872,10 @@ flush-write-stream@^1.0.0:
     inherits "^2.0.1"
     readable-stream "^2.0.4"
 
+flushwritable@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/flushwritable/-/flushwritable-1.0.0.tgz#3e328d8fde412ad47e738e3be750b4d290043498"
+
 follow-redirects@^1.0.0, follow-redirects@^1.3.0:
   version "1.5.0"
   resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.5.0.tgz#234f49cf770b7f35b40e790f636ceba0c3a0ab77"
@@ -4046,6 +4069,10 @@ get-func-name@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.0.tgz#ead774abee72e20409433a066366023dd6887a41"
 
+get-ready@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/get-ready/-/get-ready-1.0.0.tgz#f91817f1e9adecfea13a562adfc8de883ab34782"
+
 get-stdin@^4.0.1:
   version "4.0.1"
   resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe"
@@ -6984,6 +7011,13 @@ pump@^2.0.0, pump@^2.0.1:
     end-of-stream "^1.1.0"
     once "^1.3.1"
 
+pump@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64"
+  dependencies:
+    end-of-stream "^1.1.0"
+    once "^1.3.1"
+
 pumpify@^1.3.3:
   version "1.5.1"
   resolved "https://registry.yarnpkg.com/pumpify/-/pumpify-1.5.1.tgz#36513be246ab27570b1a374a5ce278bfd74370ce"
@@ -8136,6 +8170,10 @@ stream-slicer@0.0.6:
   version "0.0.6"
   resolved "https://registry.yarnpkg.com/stream-slicer/-/stream-slicer-0.0.6.tgz#f86b2ac5c2440b7a0a87b71f33665c0788046138"
 
+streamifier@^0.1.1:
+  version "0.1.1"
+  resolved "https://registry.yarnpkg.com/streamifier/-/streamifier-0.1.1.tgz#97e98d8fa4d105d62a2691d1dc07e820db8dfc4f"
+
 strict-uri-encode@^1.0.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713"
@@ -8297,7 +8335,7 @@ tapable@^0.2.7:
   version "0.2.8"
   resolved "https://registry.yarnpkg.com/tapable/-/tapable-0.2.8.tgz#99372a5c999bf2df160afc0d74bed4f47948cd22"
 
-tar-stream@^1.5.0:
+tar-stream@^1.5.0, tar-stream@^1.5.2:
   version "1.6.1"
   resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-1.6.1.tgz#f84ef1696269d6223ca48f6e1eeede3f7e81f395"
   dependencies:
@@ -9257,6 +9295,19 @@ yauzl@2.4.1:
   dependencies:
     fd-slicer "~1.0.1"
 
+yauzl@^2.7.0:
+  version "2.10.0"
+  resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9"
+  dependencies:
+    buffer-crc32 "~0.2.3"
+    fd-slicer "~1.1.0"
+
+yazl@^2.4.2:
+  version "2.4.3"
+  resolved "https://registry.yarnpkg.com/yazl/-/yazl-2.4.3.tgz#ec26e5cc87d5601b9df8432dbdd3cd2e5173a071"
+  dependencies:
+    buffer-crc32 "~0.2.3"
+
 yeast@0.1.2:
   version "0.1.2"
   resolved "https://registry.yarnpkg.com/yeast/-/yeast-0.1.2.tgz#008e06d8094320c372dbc2f8ed76a0ca6c8ac419"