瀏覽代碼

feat: admin group edit / assign / unassign

NGPixel 7 年之前
父節點
當前提交
dc09d00875

+ 9 - 4
client/components/admin/admin-general.vue

@@ -21,15 +21,20 @@
                     v-text-field(label='Site Description', :counter='255', prepend-icon='public')
                     v-text-field(label='Site Keywords', :counter='255', prepend-icon='public')
                     v-select(label='Meta Robots', chips, tags, :items='metaRobots', v-model='metaRobotsSelection', prepend-icon='public')
-                  v-divider
-                  .px-3.pb-3
-                    v-btn(color='primary') Save
+                  v-divider.my-0
+                  v-card-actions.grey.lighten-4
+                    v-spacer
+                    v-btn(color='primary')
+                      v-icon(left) chevron_right
+                      span Save
             v-flex(lg6 xs12)
               v-card
                 v-toolbar(color='primary', dark, dense, flat)
                   v-toolbar-title
                     .subheading Site Branding
-                v-card-text ---
+                v-card-text
+                  v-text-field(label='Logo', prepend-icon='image')
+                  v-divider
 
 </template>
 

+ 94 - 14
client/components/admin/admin-groups-edit.vue

@@ -2,7 +2,7 @@
   v-card
     v-card(flat, color='grey lighten-5').pa-3.pt-4
       .headline.blue--text.text--darken-2 Edit Group
-      .subheading.grey--text {{group.name}}
+      .subheading.grey--text {{name}}
       v-btn(color='primary', fab, absolute, bottom, right, small, to='/groups'): v-icon arrow_upward
     v-tabs(v-model='tab', color='grey lighten-4', fixed-tabs, slider-color='primary', show-arrows)
       v-tab(key='properties') Properties
@@ -12,9 +12,9 @@
       v-tab-item(key='properties', :transition='false', :reverse-transition='false')
         v-card
           v-card-text
-            v-text-field(v-model='group.name', label='Group Name', counter='255', prepend-icon='people')
+            v-text-field(v-model='name', label='Group Name', counter='255', prepend-icon='people')
           v-card-actions.pa-3
-            v-btn(color='primary', @click='')
+            v-btn(color='primary', @click='updateGroup')
               v-icon(left) check
               | Save Changes
             .caption.ml-4.grey--text ID: {{group.id}}
@@ -25,7 +25,7 @@
                 | Delete Group
               v-card
                 .dialog-header.is-red Delete Group?
-                v-card-text Are you sure you want to delete group #[strong {{ group.name }}]? All users will be unassigned from this group.
+                v-card-text Are you sure you want to delete group #[strong {{ name }}]? All users will be unassigned from this group.
                 v-card-actions
                   v-spacer
                   v-btn(flat, @click='deleteGroupDialog = false') Cancel
@@ -78,7 +78,7 @@
       v-tab-item(key='users', :transition='false', :reverse-transition='false')
         v-card
           v-card-title.pb-0
-            v-btn(color='primary', @click='assignUser')
+            v-btn(color='primary', @click='searchUserDialog = true')
               v-icon(left) assignment_ind
               | Assign User
           v-data-table(
@@ -98,7 +98,7 @@
                   v-menu(bottom, right, min-width='200')
                     v-btn(icon, slot='activator'): v-icon.grey--text.text--darken-1 more_horiz
                     v-list
-                      v-list-tile(@click='deleteGroupConfirm(props.item)')
+                      v-list-tile(@click='unassignUser(props.item.id)')
                         v-list-tile-action: v-icon(color='orange') highlight_off
                         v-list-tile-content
                           v-list-tile-title Unassign
@@ -107,7 +107,7 @@
           .text-xs-center.py-2(v-if='users.length > 15')
             v-pagination(v-model='pagination.page', :length='pages')
 
-    user-search(v-model='searchUserDialog')
+    user-search(v-model='searchUserDialog', @select='assignUser')
 </template>
 
 <script>
@@ -115,7 +115,10 @@ import Criterias from '../common/criterias.vue'
 import UserSearch from '../common/user-search.vue'
 
 import groupQuery from 'gql/admin-groups-query-single.gql'
+import assignUserMutation from 'gql/admin-groups-mutation-assign.gql'
 import deleteGroupMutation from 'gql/admin-groups-mutation-delete.gql'
+import unassignUserMutation from 'gql/admin-groups-mutation-unassign.gql'
+import updateGroupMutation from 'gql/admin-groups-mutation-update.gql'
 
 export default {
   components: {
@@ -125,10 +128,11 @@ export default {
   data() {
     return {
       group: {
-        id: 7,
-        name: 'Editors',
+        id: 0,
+        name: '',
         users: []
       },
+      name: '',
       deleteGroupDialog: false,
       searchUserDialog: false,
       pagination: {},
@@ -152,10 +156,36 @@ export default {
       return Math.ceil(this.pagination.totalItems / this.pagination.rowsPerPage)
     }
   },
+  watch: {
+    group(newValue, oldValue) {
+      this.name = newValue.name
+    }
+  },
   methods: {
-    async deleteGroupConfirm(group) {
-      this.deleteGroupDialog = true
-      this.selectedGroup = group
+    async updateGroup() {
+      try {
+        await this.$apollo.mutate({
+          mutation: updateGroupMutation,
+          variables: {
+            id: this.group.id,
+            name: this.name
+          },
+          watchLoading (isLoading) {
+            this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-groups-update')
+          }
+        })
+        this.$store.commit('showNotification', {
+          style: 'success',
+          message: `Group changes have been saved.`,
+          icon: 'check'
+        })
+      } catch (err) {
+        this.$store.commit('showNotification', {
+          style: 'red',
+          message: err.message,
+          icon: 'warning'
+        })
+      }
     },
     async deleteGroup() {
       this.deleteGroupDialog = false
@@ -183,8 +213,57 @@ export default {
         })
       }
     },
-    assignUser() {
-      this.searchUserDialog = true
+    async assignUser(id) {
+      try {
+        await this.$apollo.mutate({
+          mutation: assignUserMutation,
+          variables: {
+            groupId: this.group.id,
+            userId: id
+          },
+          watchLoading (isLoading) {
+            this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-groups-assign')
+          }
+        })
+        this.$store.commit('showNotification', {
+          style: 'success',
+          message: `User has been assigned to ${this.group.name}.`,
+          icon: 'assignment_ind'
+        })
+        this.$apollo.queries.group.refetch()
+      } catch (err) {
+        this.$store.commit('showNotification', {
+          style: 'red',
+          message: err.message,
+          icon: 'warning'
+        })
+      }
+    },
+    async unassignUser(id) {
+      try {
+        await this.$apollo.mutate({
+          mutation: unassignUserMutation,
+          variables: {
+            groupId: this.group.id,
+            userId: id
+          },
+          watchLoading (isLoading) {
+            this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-groups-unassign')
+          }
+        })
+        this.$store.commit('showNotification', {
+          style: 'success',
+          message: `User has been unassigned from ${this.group.name}.`,
+          icon: 'assignment_ind'
+        })
+        this.$apollo.queries.group.refetch()
+      } catch (err) {
+        this.$store.commit('showNotification', {
+          style: 'red',
+          message: err.message,
+          icon: 'warning'
+        })
+      }
     }
   },
   apollo: {
@@ -195,6 +274,7 @@ export default {
           id: this.$route.params.id
         }
       },
+      fetchPolicy: 'network-only',
       update: (data) => data.groups.single,
       watchLoading (isLoading) {
         this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-groups-refresh')

+ 1 - 0
client/components/admin/admin-groups.vue

@@ -156,6 +156,7 @@ export default {
   apollo: {
     groups: {
       query: groupsQuery,
+      fetchPolicy: 'network-only',
       update: (data) => data.groups.list,
       watchLoading (isLoading) {
         this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-groups-refresh')

+ 5 - 8
client/components/admin/admin-locale.vue

@@ -8,7 +8,7 @@
           v-layout(row wrap)
             v-flex(lg6 xs12)
               v-card
-                v-toolbar(color='grey darken-3', dark, dense, flat)
+                v-toolbar(color='primary', dark, dense, flat)
                   v-toolbar-title
                     .subheading Locale Settings
                 v-card-text
@@ -38,8 +38,9 @@
                     persistent-hint
                     hint='Automatically download updates to this locale as they become available.'
                   )
-                v-divider
-                .px-3.pb-3
+                v-divider.my-0
+                v-card-actions.grey.lighten-4
+                  v-spacer
                   v-btn(color='primary', :loading='loading', @click='save')
                     v-icon(left) chevron_right
                     span Save
@@ -49,7 +50,7 @@
                   v-toolbar-title
                     .subheading Download Locale
                 v-list
-                  v-list-tile(v-for='lc in locales')
+                  v-list-tile(v-for='lc in locales', :key='lc.code')
                     v-list-tile-avatar
                       v-avatar.teal.white--text(tile, size='40') {{lc.code.toUpperCase()}}
                     v-list-tile-content
@@ -159,7 +160,3 @@ export default {
   }
 }
 </script>
-
-<style lang='scss'>
-
-</style>

+ 7 - 4
client/components/admin/admin-theme.vue

@@ -8,7 +8,7 @@
           v-layout(row wrap)
             v-flex(lg6 xs12)
               v-card
-                v-toolbar(color='grey darken-3', dark, dense, flat)
+                v-toolbar(color='primary', dark, dense, flat)
                   v-toolbar-title
                     .subheading Theme
                 v-card-text
@@ -21,9 +21,12 @@
                         v-list-tile-sub-title(v-html='data.item.author')
                   v-divider
                   v-switch(v-model='darkMode', label='Dark Mode', color='primary', persistent-hint, hint='Not recommended for accessibility.')
-                v-divider
-                .px-3.pb-3
-                  v-btn(color='primary') Save
+                v-divider.my-0
+                v-card-actions.grey.lighten-4
+                  v-spacer
+                  v-btn(color='primary', :loading='loading', @click='save')
+                    v-icon(left) chevron_right
+                    span Save
             v-flex(lg6 xs12)
               v-card
                 v-toolbar(color='teal', dark, dense, flat)

+ 70 - 30
client/components/common/user-search.vue

@@ -1,38 +1,66 @@
 <template lang="pug">
-  v-dialog(v-model='dialogOpen', max-width='650')
+  v-dialog(
+    v-model='dialogOpen'
+    max-width='650'
+    )
     v-card
-      .dialog-header Search User
+      .dialog-header
+        span Search User
+        v-spacer
+        v-progress-circular(
+          indeterminate
+          color='white'
+          :size='20'
+          :width='2'
+          v-show='searchLoading'
+          )
       v-card-text
-        v-select(
-          :items='items'
-          :loading='searchLoading'
-          :search-input.sync='search'
-          autocomplete
-          autofocus
-          cache-items
-          chips
-          clearable
-          hide-details
-          item-text='name',
-          item-value='id',
-          label='Search users...'
-          light
-          multiple
-          v-model='selectedItems'
-        )
-      v-card-actions
+        v-text-field.blue.lighten-5(
+          solo
+          flat
+          label='Search Users...'
+          v-model='search'
+          prepend-icon='search'
+          color='primary'
+          ref='searchIpt'
+          )
+        v-list(two-line)
+          template(v-for='(usr, idx) in items')
+            v-list-tile(:key='usr.id', @click='setUser(usr.id)')
+              v-list-tile-avatar(size='40', color='primary')
+                span.body-1.white--text {{usr.name | initials}}
+              v-list-tile-content
+                v-list-tile-title {{usr.name}}
+                v-list-tile-sub-title {{usr.email}}
+              v-list-tile-action
+                v-icon(color='primary') arrow_forward
+            v-divider.my-0(v-if='idx < items.length - 1')
+      v-divider.my-0
+      v-card-actions.grey.lighten-4
         v-spacer
-        v-btn(flat, @click='close', :disabled='loading') Cancel
-        v-btn(color='primary', dark, @click='setUser', :loading='loading', :disabled='loading')
-          v-icon(left) assignment_ind
-          span Select User
+        v-btn(
+          flat
+          @click='close'
+          :disabled='loading'
+          ) Cancel
 </template>
 
 <script>
+import _ from 'lodash'
+
 import searchUsersQuery from 'gql/common-users-query-search.gql'
 
 export default {
+  filters: {
+    initials(val) {
+      return val.split(' ').map(v => v.substring(0, 1)).join()
+    }
+  },
   props: {
+    multiple: {
+      type: Boolean,
+      default: false
+    },
     value: {
       type: Boolean,
       default: false
@@ -43,8 +71,7 @@ export default {
       loading: false,
       searchLoading: false,
       search: '',
-      items: [],
-      selectedItems: []
+      items: []
     }
   },
   computed: {
@@ -53,12 +80,25 @@ export default {
       set(value) { this.$emit('input', value) }
     }
   },
+  watch: {
+    value(newValue, oldValue) {
+      if (newValue && !oldValue) {
+        this.search = ''
+        this.selectedItems = null
+        _.delay(() => { this.$refs.searchIpt.focus() }, 100)
+      }
+    }
+  },
   methods: {
     close() {
       this.$emit('input', false)
     },
-    setUser() {
-
+    setUser(id) {
+      this.$emit('select', id)
+      this.close()
+    },
+    searchFilter(item, queryText, itemText) {
+      return _.includes(_.toLower(item.email), _.toLower(queryText)) || _.includes(_.toLower(item.name), _.toLower(queryText))
     }
   },
   apollo: {
@@ -66,13 +106,13 @@ export default {
       query: searchUsersQuery,
       variables() {
         return {
-          search: this.search
+          query: this.search
         }
       },
       skip() {
         return !this.search || this.search.length < 2
       },
-      update: (data) => data.upsells.segments,
+      update: (data) => data.users.search,
       watchLoading (isLoading) {
         this.searchLoading = isLoading
       }

+ 12 - 0
client/graph/admin-groups-mutation-assign.gql

@@ -0,0 +1,12 @@
+mutation ($groupId: Int!, $userId: Int!) {
+  groups {
+    assignUser(groupId: $groupId, userId: $userId) {
+      responseResult {
+        succeeded
+        errorCode
+        slug
+        message
+      }
+    }
+  }
+}

+ 12 - 0
client/graph/admin-groups-mutation-unassign.gql

@@ -0,0 +1,12 @@
+mutation ($groupId: Int!, $userId: Int!) {
+  groups {
+    unassignUser(groupId: $groupId, userId: $userId) {
+      responseResult {
+        succeeded
+        errorCode
+        slug
+        message
+      }
+    }
+  }
+}

+ 12 - 0
client/graph/admin-groups-mutation-update.gql

@@ -0,0 +1,12 @@
+mutation ($id: Int!, $name: String!) {
+  groups {
+    update(id: $id, name: $name) {
+      responseResult {
+        succeeded
+        errorCode
+        slug
+        message
+      }
+    }
+  }
+}

+ 1 - 0
client/graph/common-users-query-search.gql

@@ -4,6 +4,7 @@ query ($query: String!) {
       id
       name
       email
+      provider
     }
   }
 }

+ 24 - 24
package.json

@@ -61,15 +61,15 @@
     "express-brute": "1.0.1",
     "express-brute-redis": "0.0.1",
     "express-session": "1.15.6",
-    "file-type": "7.6.0",
+    "file-type": "7.7.1",
     "filesize.js": "1.0.2",
     "follow-redirects": "1.4.1",
-    "fs-extra": "5.0.0",
+    "fs-extra": "6.0.1",
     "getos": "3.1.0",
     "graphql": "0.13.2",
     "graphql-list-fields": "2.0.2",
-    "graphql-tools": "3.0.0",
-    "i18next": "11.2.3",
+    "graphql-tools": "3.0.1",
+    "i18next": "11.3.1",
     "i18next-express-middleware": "1.1.1",
     "i18next-localstorage-cache": "1.1.1",
     "i18next-node-fs-backend": "1.0.0",
@@ -82,7 +82,7 @@
     "markdown-it": "8.4.1",
     "markdown-it-abbr": "1.0.4",
     "markdown-it-anchor": "4.0.0",
-    "markdown-it-attrs": "1.2.1",
+    "markdown-it-attrs": "2.0.0",
     "markdown-it-emoji": "1.4.0",
     "markdown-it-expand-tabs": "1.0.13",
     "markdown-it-external-links": "0.0.6",
@@ -102,7 +102,7 @@
     "mysql2": "1.5.3",
     "node-2fa": "1.1.2",
     "oauth2orize": "1.11.0",
-    "ora": "2.0.0",
+    "ora": "2.1.0",
     "passport": "0.4.0",
     "passport-auth0": "0.6.1",
     "passport-azure-ad-oauth2": "0.0.4",
@@ -123,45 +123,45 @@
     "pm2": "2.10.3",
     "pug": "2.0.3",
     "qr-image": "3.2.0",
-    "raven": "2.6.0",
+    "raven": "2.6.1",
     "read-chunk": "2.1.0",
     "remove-markdown": "0.2.2",
     "request": "2.85.0",
     "request-promise": "4.2.2",
     "scim-query-filter-parser": "1.1.0",
     "semver": "5.5.0",
-    "sequelize": "4.37.6",
+    "sequelize": "4.37.7",
     "serve-favicon": "2.5.0",
     "sqlite3": "4.0.0",
     "uuid": "3.2.1",
-    "validator": "9.4.1",
+    "validator": "10.2.0",
     "validator-as-promised": "1.0.2",
     "winston": "3.0.0-rc2",
     "yargs": "11.0.0"
   },
   "devDependencies": {
     "@panter/vue-i18next": "0.9.1",
-    "@vue/cli": "3.0.0-beta.9",
+    "@vue/cli": "3.0.0-beta.10",
     "apollo-client-preset": "1.0.8",
     "apollo-fetch": "0.7.0",
     "apollo-link-batch-http": "1.2.2",
-    "autoprefixer": "8.3.0",
+    "autoprefixer": "8.4.1",
     "babel-cli": "6.26.0",
     "babel-core": "6.26.3",
     "babel-eslint": "8.2.3",
     "babel-jest": "23.0.0-alpha.0",
     "babel-loader": "7.1.4",
-    "babel-plugin-graphql-tag": "1.5.0",
+    "babel-plugin-graphql-tag": "1.6.0",
     "babel-plugin-lodash": "3.3.2",
     "babel-plugin-transform-imports": "1.5.0",
     "babel-polyfill": "6.26.0",
-    "babel-preset-env": "1.6.1",
+    "babel-preset-env": "1.7.0",
     "babel-preset-es2015": "6.24.1",
     "babel-preset-stage-2": "6.24.1",
     "brace": "0.11.1",
     "cache-loader": "1.2.2",
     "clean-webpack-plugin": "0.1.19",
-    "colors": "1.2.1",
+    "colors": "1.2.5",
     "copy-webpack-plugin": "4.5.1",
     "css-loader": "0.28.11",
     "cssnano": "4.0.0-rc.2",
@@ -176,7 +176,7 @@
     "eslint-plugin-vue": "4.5.0",
     "file-loader": "1.1.11",
     "graphiql": "0.11.11",
-    "graphql-tag": "^2.9.1",
+    "graphql-tag": "^2.9.2",
     "graphql-voyager": "1.0.0-rc.15",
     "hammerjs": "2.0.8",
     "html-webpack-plugin": "3.2.0",
@@ -189,13 +189,13 @@
     "lodash-webpack-plugin": "0.11.5",
     "mini-css-extract-plugin": "0.4.0",
     "node-sass": "4.9.0",
-    "offline-plugin": "5.0.1",
-    "optimize-css-assets-webpack-plugin": "4.0.0",
+    "offline-plugin": "5.0.3",
+    "optimize-css-assets-webpack-plugin": "4.0.1",
     "postcss-cssnext": "3.1.0",
-    "postcss-flexbugs-fixes": "3.3.0",
+    "postcss-flexbugs-fixes": "3.3.1",
     "postcss-flexibility": "2.0.0",
     "postcss-import": "11.1.0",
-    "postcss-loader": "2.1.4",
+    "postcss-loader": "2.1.5",
     "postcss-selector-parser": "4.0.0",
     "pug-lint": "2.5.0",
     "pug-loader": "2.4.0",
@@ -211,14 +211,14 @@
     "stylus": "0.54.5",
     "stylus-loader": "3.0.2",
     "twemoji-awesome": "1.0.6",
-    "vee-validate": "2.0.8",
+    "vee-validate": "2.0.9",
     "velocity-animate": "1.5.1",
     "vue": "2.5.16",
     "vue-apollo": "3.0.0-beta.5",
     "vue-clipboards": "1.2.4",
     "vue-codemirror": "4.0.5",
     "vue-hot-reload-api": "2.3.0",
-    "vue-loader": "15.0.4",
+    "vue-loader": "15.0.10",
     "vue-material-design-icons": "1.4.0",
     "vue-moment": "3.2.0",
     "vue-router": "3.0.1",
@@ -227,9 +227,9 @@
     "vuetify": "1.0.17",
     "vuex": "3.0.1",
     "vuex-persistedstate": "2.5.2",
-    "webpack": "4.6.0",
-    "webpack-bundle-analyzer": "2.11.1",
-    "webpack-cli": "2.0.15",
+    "webpack": "4.8.2",
+    "webpack-bundle-analyzer": "2.11.2",
+    "webpack-cli": "2.1.3",
     "webpack-dev-middleware": "3.1.3",
     "webpack-hot-middleware": "2.22.1",
     "webpack-merge": "4.1.2",

+ 31 - 26
server/graph/resolvers/group.js

@@ -34,18 +34,19 @@ module.exports = {
     }
   },
   GroupMutation: {
-    assignUser(obj, args) {
-      return WIKI.db.Group.findById(args.groupId).then(grp => {
-        if (!grp) {
-          throw new gql.GraphQLError('Invalid Group ID')
-        }
-        return WIKI.db.User.findById(args.userId).then(usr => {
-          if (!usr) {
-            throw new gql.GraphQLError('Invalid User ID')
-          }
-          return grp.addUser(usr)
-        })
-      })
+    async assignUser(obj, args) {
+      const grp = await WIKI.db.Group.findById(args.groupId)
+      if (!grp) {
+        throw new gql.GraphQLError('Invalid Group ID')
+      }
+      const usr = await WIKI.db.User.findById(args.userId)
+      if (!usr) {
+        throw new gql.GraphQLError('Invalid User ID')
+      }
+      await grp.addUser(usr)
+      return {
+        responseResult: graphHelper.generateSuccess('User has been assigned to group.')
+      }
     },
     async create(obj, args) {
       const group = await WIKI.db.Group.create({
@@ -67,25 +68,29 @@ module.exports = {
         responseResult: graphHelper.generateSuccess('Group has been deleted.')
       }
     },
-    unassignUser(obj, args) {
-      return WIKI.db.Group.findById(args.groupId).then(grp => {
-        if (!grp) {
-          throw new gql.GraphQLError('Invalid Group ID')
-        }
-        return WIKI.db.User.findById(args.userId).then(usr => {
-          if (!usr) {
-            throw new gql.GraphQLError('Invalid User ID')
-          }
-          return grp.removeUser(usr)
-        })
-      })
+    async unassignUser(obj, args) {
+      const grp = await WIKI.db.Group.findById(args.groupId)
+      if (!grp) {
+        throw new gql.GraphQLError('Invalid Group ID')
+      }
+      const usr = await WIKI.db.User.findById(args.userId)
+      if (!usr) {
+        throw new gql.GraphQLError('Invalid User ID')
+      }
+      await grp.removeUser(usr)
+      return {
+        responseResult: graphHelper.generateSuccess('User has been unassigned from group.')
+      }
     },
-    update(obj, args) {
-      return WIKI.db.Group.update({
+    async update(obj, args) {
+      await WIKI.db.Group.update({
         name: args.name
       }, {
         where: { id: args.id }
       })
+      return {
+        responseResult: graphHelper.generateSuccess('Group has been updated.')
+      }
     }
   },
   Group: {

+ 35 - 8
server/graph/resolvers/user.js

@@ -3,15 +3,42 @@
 
 module.exports = {
   Query: {
-    users(obj, args, context, info) {
-      return WIKI.db.User.findAll({ where: args })
-    }
+    async users() { return {} }
   },
   Mutation: {
-    createUser(obj, args) {
+    async users() { return {} }
+  },
+  UserQuery: {
+    async list(obj, args, context, info) {
+      return WIKI.db.User.findAll({
+        attributes: {
+          exclude: ['password', 'tfaSecret']
+        },
+        raw: true
+      })
+    },
+    async search(obj, args, context, info) {
+      return WIKI.db.User.findAll({
+        where: {
+          $or: [
+            { email: { $like: `%${args.query}%` } },
+            { name: { $like: `%${args.query}%` } }
+          ]
+        },
+        limit: 10,
+        attributes: ['id', 'email', 'name', 'provider', 'role', 'createdAt', 'updatedAt'],
+        raw: true
+      })
+    },
+    async single(obj, args, context, info) {
+      return WIKI.db.User.findById(args.id)
+    }
+  },
+  UserMutation: {
+    create(obj, args) {
       return WIKI.db.User.create(args)
     },
-    deleteUser(obj, args) {
+    delete(obj, args) {
       return WIKI.db.User.destroy({
         where: {
           id: args.id
@@ -19,7 +46,7 @@ module.exports = {
         limit: 1
       })
     },
-    modifyUser(obj, args) {
+    update(obj, args) {
       return WIKI.db.User.update({
         email: args.email,
         name: args.name,
@@ -30,10 +57,10 @@ module.exports = {
         where: { id: args.id }
       })
     },
-    resetUserPassword(obj, args) {
+    resetPassword(obj, args) {
       return false
     },
-    setUserPassword(obj, args) {
+    setPassword(obj, args) {
       return false
     }
   },

+ 0 - 51
server/graph/schemas/common.graphql

@@ -2,12 +2,6 @@
 
 # ENUMS
 
-enum UserRole {
-  guest
-  user
-  admin
-}
-
 enum FileType {
   binary
   image
@@ -134,19 +128,6 @@ type Translation {
   value: String!
 }
 
-# A User
-type User implements Base {
-  id: Int!
-  createdAt: Date
-  updatedAt: Date
-  email: String!
-  provider: String!
-  providerId: String
-  name: String
-  role: UserRole!
-  groups: [Group]
-}
-
 type OperationResult {
   succeeded: Boolean!
   message: String
@@ -164,7 +145,6 @@ type Query {
   settings(key: String): [Setting]
   tags(key: String): [Tag]
   translations(locale: String!, namespace: String!): [Translation]
-  users(id: Int, email: String, provider: String, providerId: String, role: UserRole): [User]
 }
 
 # Mutations (Create, Update, Delete)
@@ -202,15 +182,6 @@ type Mutation {
     name: String!
   ): Tag
 
-  createUser(
-    email: String!
-    name: String
-    passwordRaw: String
-    provider: String!
-    providerId: String
-    role: UserRole!
-  ): User
-
   deleteComment(
     id: Int!
   ): OperationResult
@@ -231,10 +202,6 @@ type Mutation {
     id: Int!
   ): OperationResult
 
-  deleteUser(
-    id: Int!
-  ): OperationResult
-
   modifyComment(
     id: Int!
     content: String!
@@ -246,15 +213,6 @@ type Mutation {
     subtitle: String
   ): Document
 
-  modifyUser(
-    id: Int!
-    email: String
-    name: String
-    provider: String
-    providerId: String
-    role: UserRole
-  ): User
-
   modifyRight(
     id: Int!
     path: String
@@ -297,20 +255,11 @@ type Mutation {
     rightId: Int!
   ): OperationResult
 
-  resetUserPassword(
-    id: Int!
-  ): OperationResult
-
   setConfigEntry(
     key: String!
     value: String!
   ): OperationResult
 
-  setUserPassword(
-    id: Int!
-    passwordRaw: String!
-  ): OperationResult
-
   uploadFile(
     category: FileType!
     filename: String!

+ 1 - 1
server/graph/schemas/group.graphql

@@ -37,7 +37,7 @@ type GroupMutation {
   update(
     id: Int!
     name: String!
-  ): GroupResponse
+  ): DefaultResponse
 
   delete(
     id: Int!

+ 101 - 0
server/graph/schemas/user.graphql

@@ -0,0 +1,101 @@
+# ===============================================
+# USERS
+# ===============================================
+
+extend type Query {
+  users: UserQuery
+}
+
+extend type Mutation {
+  users: UserMutation
+}
+
+# -----------------------------------------------
+# QUERIES
+# -----------------------------------------------
+
+type UserQuery {
+  list(
+    filter: String
+    orderBy: String
+  ): [UserMinimal]
+
+  search(
+    query: String!
+  ): [UserMinimal]
+
+  single(
+    id: Int!
+  ): User
+}
+
+# -----------------------------------------------
+# MUTATIONS
+# -----------------------------------------------
+
+type UserMutation {
+  create(
+    email: String!
+    name: String
+    passwordRaw: String
+    provider: String!
+    providerId: String
+    role: UserRole!
+  ): UserResponse
+
+  update(
+    id: Int!
+    email: String
+    name: String
+    provider: String
+    providerId: String
+    role: UserRole
+  ): UserResponse
+
+  delete(
+    id: Int!
+  ): DefaultResponse
+
+  resetPassword(
+    id: Int!
+  ): DefaultResponse
+
+  setPassword(
+    id: Int!
+    passwordRaw: String!
+  ): DefaultResponse
+}
+
+# -----------------------------------------------
+# TYPES
+# -----------------------------------------------
+
+enum UserRole {
+  guest
+  user
+  admin
+}
+
+type UserResponse {
+  responseResult: ResponseStatus!
+  user: User
+}
+
+type UserMinimal {
+  id: Int!
+  name: String!
+  email: String!
+  provider: String!
+}
+
+type User {
+  id: Int!
+  name: String!
+  email: String!
+  provider: String!
+  providerId: String
+  role: UserRole!
+  createdAt: Date!
+  updatedAt: Date!
+  groups: [Group]
+}

+ 281 - 129
yarn.lock

@@ -211,9 +211,9 @@
   version "0.5.3"
   resolved "https://registry.yarnpkg.com/@types/zen-observable/-/zen-observable-0.5.3.tgz#91b728599544efbb7386d8b6633693a3c2e7ade5"
 
-"@vue/cli-shared-utils@^3.0.0-beta.9":
-  version "3.0.0-beta.9"
-  resolved "https://registry.yarnpkg.com/@vue/cli-shared-utils/-/cli-shared-utils-3.0.0-beta.9.tgz#7f7142a9395cfae3af1ce03820c81fabc93923ab"
+"@vue/cli-shared-utils@^3.0.0-beta.10":
+  version "3.0.0-beta.10"
+  resolved "https://registry.yarnpkg.com/@vue/cli-shared-utils/-/cli-shared-utils-3.0.0-beta.10.tgz#3705277843bd2390769a711571b2463878fb0940"
   dependencies:
     chalk "^2.3.0"
     cmd-shim "^2.0.2"
@@ -223,11 +223,11 @@
     ora "^1.3.0"
     string.prototype.padstart "^3.0.0"
 
-"@vue/cli@3.0.0-beta.9":
-  version "3.0.0-beta.9"
-  resolved "https://registry.yarnpkg.com/@vue/cli/-/cli-3.0.0-beta.9.tgz#a765cfb4ae9b9f492053df0968dfd830b7a01960"
+"@vue/cli@3.0.0-beta.10":
+  version "3.0.0-beta.10"
+  resolved "https://registry.yarnpkg.com/@vue/cli/-/cli-3.0.0-beta.10.tgz#b919364be15d64ad039fed7768a560b6ea35c2b7"
   dependencies:
-    "@vue/cli-shared-utils" "^3.0.0-beta.9"
+    "@vue/cli-shared-utils" "^3.0.0-beta.10"
     chalk "^2.3.0"
     commander "^2.12.2"
     debug "^3.1.0"
@@ -235,6 +235,7 @@
     download-git-repo "^1.0.2"
     ejs "^2.5.7"
     execa "^0.8.0"
+    fs-extra "^6.0.0"
     globby "^8.0.1"
     import-global "^0.1.0"
     inquirer "^4.0.1"
@@ -244,12 +245,10 @@
     klaw-sync "^3.0.2"
     lodash.clonedeep "^4.5.0"
     minimist "^1.2.0"
-    mkdirp "^0.5.1"
     recast "^0.13.0"
     request "^2.83.0"
     request-promise-native "^1.0.5"
     resolve "^1.5.0"
-    rimraf "^2.6.2"
     semver "^5.4.1"
     slash "^1.0.0"
     validate-npm-package-name "^3.0.0"
@@ -269,6 +268,118 @@
     source-map "^0.5.6"
     vue-template-es2015-compiler "^1.6.0"
 
+"@webassemblyjs/ast@1.4.2":
+  version "1.4.2"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.4.2.tgz#ab715aa1fec9dd23c025204dba39690c119418ea"
+  dependencies:
+    "@webassemblyjs/helper-wasm-bytecode" "1.4.2"
+    "@webassemblyjs/wast-parser" "1.4.2"
+    debug "^3.1.0"
+    webassemblyjs "1.4.2"
+
+"@webassemblyjs/floating-point-hex-parser@1.4.2":
+  version "1.4.2"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.4.2.tgz#9296fb64caa37bf98c8064aa329680e3e2bfacc7"
+
+"@webassemblyjs/helper-buffer@1.4.2":
+  version "1.4.2"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.4.2.tgz#3cacecd5a6bfcb67932ed8219f81f92d8b2dafbb"
+
+"@webassemblyjs/helper-code-frame@1.4.2":
+  version "1.4.2"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.4.2.tgz#20526637c3849f12b08f8661248477eef9642329"
+  dependencies:
+    "@webassemblyjs/wast-printer" "1.4.2"
+
+"@webassemblyjs/helper-fsm@1.4.2":
+  version "1.4.2"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-fsm/-/helper-fsm-1.4.2.tgz#e41050282994b5be077b95b65b66ecd5a92c5e88"
+
+"@webassemblyjs/helper-wasm-bytecode@1.4.2":
+  version "1.4.2"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.4.2.tgz#b48c289c7921056aa12d71e78a17070ffe90c49c"
+
+"@webassemblyjs/helper-wasm-section@1.4.2":
+  version "1.4.2"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.4.2.tgz#520e02c0cc3e5e9b5f44f58abc04ba5eda6e5476"
+  dependencies:
+    "@webassemblyjs/ast" "1.4.2"
+    "@webassemblyjs/helper-buffer" "1.4.2"
+    "@webassemblyjs/helper-wasm-bytecode" "1.4.2"
+    "@webassemblyjs/wasm-gen" "1.4.2"
+
+"@webassemblyjs/leb128@1.4.2":
+  version "1.4.2"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.4.2.tgz#d13f368abdcefc54428f55a265a993de610f8893"
+  dependencies:
+    leb "^0.3.0"
+
+"@webassemblyjs/validation@1.4.2":
+  version "1.4.2"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/validation/-/validation-1.4.2.tgz#55cf5b219e25900c85773fc35beb9d12ae0ede53"
+  dependencies:
+    "@webassemblyjs/ast" "1.4.2"
+
+"@webassemblyjs/wasm-edit@1.4.2":
+  version "1.4.2"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.4.2.tgz#bde9a581065f63f257ed511d7d9cf04f8cd04524"
+  dependencies:
+    "@webassemblyjs/ast" "1.4.2"
+    "@webassemblyjs/helper-buffer" "1.4.2"
+    "@webassemblyjs/helper-wasm-bytecode" "1.4.2"
+    "@webassemblyjs/helper-wasm-section" "1.4.2"
+    "@webassemblyjs/wasm-gen" "1.4.2"
+    "@webassemblyjs/wasm-opt" "1.4.2"
+    "@webassemblyjs/wasm-parser" "1.4.2"
+    "@webassemblyjs/wast-printer" "1.4.2"
+    debug "^3.1.0"
+
+"@webassemblyjs/wasm-gen@1.4.2":
+  version "1.4.2"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.4.2.tgz#0899297f9426073736df799287845a73c597cf90"
+  dependencies:
+    "@webassemblyjs/ast" "1.4.2"
+    "@webassemblyjs/helper-wasm-bytecode" "1.4.2"
+    "@webassemblyjs/leb128" "1.4.2"
+
+"@webassemblyjs/wasm-opt@1.4.2":
+  version "1.4.2"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.4.2.tgz#c44ad48e109aec197e3bf69875c54537d76ba2e9"
+  dependencies:
+    "@webassemblyjs/ast" "1.4.2"
+    "@webassemblyjs/helper-buffer" "1.4.2"
+    "@webassemblyjs/wasm-gen" "1.4.2"
+    "@webassemblyjs/wasm-parser" "1.4.2"
+
+"@webassemblyjs/wasm-parser@1.4.2":
+  version "1.4.2"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.4.2.tgz#3bf7e10cfe336db0ecdea0a5d7ed8a63b7a7754a"
+  dependencies:
+    "@webassemblyjs/ast" "1.4.2"
+    "@webassemblyjs/helper-wasm-bytecode" "1.4.2"
+    "@webassemblyjs/leb128" "1.4.2"
+    "@webassemblyjs/wasm-parser" "1.4.2"
+    webassemblyjs "1.4.2"
+
+"@webassemblyjs/wast-parser@1.4.2":
+  version "1.4.2"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-parser/-/wast-parser-1.4.2.tgz#6499c38cf8895a81394f7e40d4681a85aaa84498"
+  dependencies:
+    "@webassemblyjs/ast" "1.4.2"
+    "@webassemblyjs/floating-point-hex-parser" "1.4.2"
+    "@webassemblyjs/helper-code-frame" "1.4.2"
+    "@webassemblyjs/helper-fsm" "1.4.2"
+    long "^3.2.0"
+    webassemblyjs "1.4.2"
+
+"@webassemblyjs/wast-printer@1.4.2":
+  version "1.4.2"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.4.2.tgz#ee70a828f0d9730b55b9a5c3ed694094ba68ba57"
+  dependencies:
+    "@webassemblyjs/ast" "1.4.2"
+    "@webassemblyjs/wast-parser" "1.4.2"
+    long "^3.2.0"
+
 abab@^1.0.3:
   version "1.0.3"
   resolved "https://registry.yarnpkg.com/abab/-/abab-1.0.3.tgz#b81de5f7274ec4e756d797cd834f303642724e5d"
@@ -832,15 +943,15 @@ auto-load@3.0.0:
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/auto-load/-/auto-load-3.0.0.tgz#f3c91d15120a32c89524b420d7283584c72088aa"
 
-autoprefixer@8.3.0:
-  version "8.3.0"
-  resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-8.3.0.tgz#22ac5151c3c8946bb8f75f337d5c5042c0ec6404"
+autoprefixer@8.4.1:
+  version "8.4.1"
+  resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-8.4.1.tgz#c6b30001ea4b3daa6b611e50071f62dd24beb564"
   dependencies:
-    browserslist "^3.2.4"
-    caniuse-lite "^1.0.30000830"
+    browserslist "^3.2.6"
+    caniuse-lite "^1.0.30000832"
     normalize-range "^0.1.2"
     num2fraction "^1.2.2"
-    postcss "^6.0.21"
+    postcss "^6.0.22"
     postcss-value-parser "^3.2.3"
 
 autoprefixer@^6.3.1:
@@ -1197,9 +1308,9 @@ babel-plugin-check-es2015-constants@^6.22.0:
   dependencies:
     babel-runtime "^6.22.0"
 
-babel-plugin-graphql-tag@1.5.0:
-  version "1.5.0"
-  resolved "https://registry.yarnpkg.com/babel-plugin-graphql-tag/-/babel-plugin-graphql-tag-1.5.0.tgz#ea0f9f68b39e60244d61a433103e4b266789e654"
+babel-plugin-graphql-tag@1.6.0:
+  version "1.6.0"
+  resolved "https://registry.yarnpkg.com/babel-plugin-graphql-tag/-/babel-plugin-graphql-tag-1.6.0.tgz#bfdc83e9a8d0eeea6d93508d6b5261368aa6064f"
   dependencies:
     babel-literal-to-ast "^0.1.2"
     babel-types "^6.24.1"
@@ -1573,9 +1684,9 @@ babel-polyfill@6.26.0, babel-polyfill@^6.26.0:
     core-js "^2.5.0"
     regenerator-runtime "^0.10.5"
 
-babel-preset-env@1.6.1:
-  version "1.6.1"
-  resolved "https://registry.yarnpkg.com/babel-preset-env/-/babel-preset-env-1.6.1.tgz#a18b564cc9b9afdf4aae57ae3c1b0d99188e6f48"
+babel-preset-env@1.7.0:
+  version "1.7.0"
+  resolved "https://registry.yarnpkg.com/babel-preset-env/-/babel-preset-env-1.7.0.tgz#dea79fa4ebeb883cd35dab07e260c1c9c04df77a"
   dependencies:
     babel-plugin-check-es2015-constants "^6.22.0"
     babel-plugin-syntax-trailing-function-commas "^6.22.0"
@@ -1604,7 +1715,7 @@ babel-preset-env@1.6.1:
     babel-plugin-transform-es2015-unicode-regex "^6.22.0"
     babel-plugin-transform-exponentiation-operator "^6.22.0"
     babel-plugin-transform-regenerator "^6.22.0"
-    browserslist "^2.1.2"
+    browserslist "^3.2.6"
     invariant "^2.2.2"
     semver "^5.3.0"
 
@@ -2094,19 +2205,12 @@ browserslist@^2.0.0, browserslist@^2.11.3:
     caniuse-lite "^1.0.30000792"
     electron-to-chromium "^1.3.30"
 
-browserslist@^2.1.2:
-  version "2.5.0"
-  resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-2.5.0.tgz#0ea00d22813a4dfae5786485225a9c584b3ef37c"
-  dependencies:
-    caniuse-lite "^1.0.30000744"
-    electron-to-chromium "^1.3.24"
-
-browserslist@^3.2.4:
-  version "3.2.6"
-  resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-3.2.6.tgz#138a44d04a9af64443679191d041f28ce5b965d5"
+browserslist@^3.2.6:
+  version "3.2.7"
+  resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-3.2.7.tgz#aa488634d320b55e88bab0256184dbbcca1e6de9"
   dependencies:
-    caniuse-lite "^1.0.30000830"
-    electron-to-chromium "^1.3.42"
+    caniuse-lite "^1.0.30000835"
+    electron-to-chromium "^1.3.45"
 
 bser@^2.0.0:
   version "2.0.0"
@@ -2332,17 +2436,13 @@ caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000792:
   version "1.0.30000792"
   resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000792.tgz#d0cea981f8118f3961471afbb43c9a1e5bbf0332"
 
-caniuse-lite@^1.0.30000744:
-  version "1.0.30000744"
-  resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000744.tgz#860fa5c83ba34fe619397d607f30bb474821671b"
-
 caniuse-lite@^1.0.30000805:
   version "1.0.30000815"
   resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000815.tgz#3a4258e6850362185adb11b0d754a48402d35bf6"
 
-caniuse-lite@^1.0.30000830:
-  version "1.0.30000831"
-  resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000831.tgz#ae109aa23218f721ca87cf45cd2ce56de3d2980d"
+caniuse-lite@^1.0.30000832, caniuse-lite@^1.0.30000835:
+  version "1.0.30000839"
+  resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000839.tgz#41fcc036cf1cb77a0e0be041210f77f1ced44a7b"
 
 capture-stack-trace@^1.0.0:
   version "1.0.0"
@@ -2433,6 +2533,14 @@ chalk@^2.3.1:
     escape-string-regexp "^1.0.5"
     supports-color "^5.2.0"
 
+chalk@^2.4.1:
+  version "2.4.1"
+  resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.1.tgz#18c49ab16a037b6eb0152cc83e3471338215b66e"
+  dependencies:
+    ansi-styles "^3.2.1"
+    escape-string-regexp "^1.0.5"
+    supports-color "^5.3.0"
+
 chalk@~0.4.0:
   version "0.4.0"
   resolved "https://registry.yarnpkg.com/chalk/-/chalk-0.4.0.tgz#5199a3ddcd0c1efe23bc08c1b027b06176e0c64f"
@@ -2837,9 +2945,9 @@ colors@1.0.3:
   version "1.0.3"
   resolved "https://registry.yarnpkg.com/colors/-/colors-1.0.3.tgz#0433f44d809680fdeb60ed260f1b0c262e82a40b"
 
-colors@1.2.1:
-  version "1.2.1"
-  resolved "https://registry.yarnpkg.com/colors/-/colors-1.2.1.tgz#f4a3d302976aaf042356ba1ade3b1a2c62d9d794"
+colors@1.2.5:
+  version "1.2.5"
+  resolved "https://registry.yarnpkg.com/colors/-/colors-1.2.5.tgz#89c7ad9a374bc030df8013241f68136ed8835afc"
 
 colors@^1.1.2:
   version "1.2.0"
@@ -3471,7 +3579,7 @@ date-now@^0.1.4:
   version "0.1.4"
   resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b"
 
-dateformat@^3.0.2:
+dateformat@^3.0.3:
   version "3.0.3"
   resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae"
 
@@ -3567,6 +3675,10 @@ deep-extend@^0.4.0, deep-extend@~0.4.0:
   version "0.4.2"
   resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.4.2.tgz#48b699c27e334bf89f10892be432f6e4c7d34a7f"
 
+deep-extend@^0.5.1:
+  version "0.5.1"
+  resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.5.1.tgz#b894a9dd90d3023fbf1c55a394fb858eb2066f1f"
+
 deep-is@~0.1.3:
   version "0.1.3"
   resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34"
@@ -3942,21 +4054,21 @@ ee-first@1.1.1:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
 
-ejs@^2.3.1, ejs@^2.3.4, ejs@^2.5.7:
+ejs@^2.3.4, ejs@^2.5.7:
   version "2.5.7"
   resolved "https://registry.yarnpkg.com/ejs/-/ejs-2.5.7.tgz#cc872c168880ae3c7189762fd5ffc00896c9518a"
 
+ejs@^2.5.9:
+  version "2.6.1"
+  resolved "https://registry.yarnpkg.com/ejs/-/ejs-2.6.1.tgz#498ec0d495655abc6f23cd61868d926464071aa0"
+
 electron-to-chromium@^1.2.7, electron-to-chromium@^1.3.30:
   version "1.3.31"
   resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.31.tgz#00d832cba9fe2358652b0c48a8816c8e3a037e9f"
 
-electron-to-chromium@^1.3.24:
-  version "1.3.24"
-  resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.24.tgz#9b7b88bb05ceb9fa016a177833cc2dde388f21b6"
-
-electron-to-chromium@^1.3.42:
-  version "1.3.44"
-  resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.44.tgz#ef6b150a60d523082388cadad88085ecd2fd4684"
+electron-to-chromium@^1.3.45:
+  version "1.3.45"
+  resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.45.tgz#458ac1b1c5c760ce8811a16d2bfbd97ec30bafb8"
 
 elegant-spinner@^1.0.1:
   version "1.0.1"
@@ -4640,9 +4752,9 @@ file-loader@1.1.11:
     loader-utils "^1.0.2"
     schema-utils "^0.4.5"
 
-file-type@7.6.0:
-  version "7.6.0"
-  resolved "https://registry.yarnpkg.com/file-type/-/file-type-7.6.0.tgz#b3dbfc8029148e86f30761b21253562943d21f06"
+file-type@7.7.1:
+  version "7.7.1"
+  resolved "https://registry.yarnpkg.com/file-type/-/file-type-7.7.1.tgz#91c2f5edb8ce70688b9b68a90d931bbb6cb21f65"
 
 file-type@^3.8.0:
   version "3.9.0"
@@ -4868,9 +4980,9 @@ from2@^2.1.0, from2@^2.1.1:
     inherits "^2.0.1"
     readable-stream "^2.0.0"
 
-fs-extra@5.0.0:
-  version "5.0.0"
-  resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-5.0.0.tgz#414d0110cdd06705734d055652c5411260c31abd"
+fs-extra@6.0.1, fs-extra@^6.0.0:
+  version "6.0.1"
+  resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-6.0.1.tgz#8abc128f7946e310135ddc93b98bddb410e7a34b"
   dependencies:
     graceful-fs "^4.1.2"
     jsonfile "^4.0.0"
@@ -5172,7 +5284,7 @@ globby@^7.1.1:
     pify "^3.0.0"
     slash "^1.0.0"
 
-globby@^8.0.1:
+globby@^8.0.0, globby@^8.0.1:
   version "8.0.1"
   resolved "https://registry.yarnpkg.com/globby/-/globby-8.0.1.tgz#b5ad48b8aa80b35b814fc1281ecc851f1d2b5b50"
   dependencies:
@@ -5357,13 +5469,13 @@ graphql-tag@^2.4.2:
   version "2.5.0"
   resolved "https://registry.yarnpkg.com/graphql-tag/-/graphql-tag-2.5.0.tgz#b43bfd8b5babcd2c205ad680c03e98b238934e0f"
 
-graphql-tag@^2.9.1:
-  version "2.9.1"
-  resolved "https://registry.yarnpkg.com/graphql-tag/-/graphql-tag-2.9.1.tgz#1ab090ef7d3518b06d8c97d1393672145fe91587"
+graphql-tag@^2.9.2:
+  version "2.9.2"
+  resolved "https://registry.yarnpkg.com/graphql-tag/-/graphql-tag-2.9.2.tgz#2f60a5a981375f430bf1e6e95992427dc18af686"
 
-graphql-tools@3.0.0:
-  version "3.0.0"
-  resolved "https://registry.yarnpkg.com/graphql-tools/-/graphql-tools-3.0.0.tgz#ff22ad15315fc268de8639d03936b911d78b9e9b"
+graphql-tools@3.0.1:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/graphql-tools/-/graphql-tools-3.0.1.tgz#456c27061fb2f1040f6bb7bf8a68459651090462"
   dependencies:
     apollo-link "1.2.1"
     apollo-utilities "^1.0.1"
@@ -5769,9 +5881,9 @@ i18next-xhr-backend@1.5.1:
   version "1.5.1"
   resolved "https://registry.yarnpkg.com/i18next-xhr-backend/-/i18next-xhr-backend-1.5.1.tgz#50282610780c6a696d880dfa7f4ac1d01e8c3ad5"
 
-i18next@11.2.3:
-  version "11.2.3"
-  resolved "https://registry.yarnpkg.com/i18next/-/i18next-11.2.3.tgz#cc29400460ede53ecc0a3a399e3497e82a1d830e"
+i18next@11.3.1:
+  version "11.3.1"
+  resolved "https://registry.yarnpkg.com/i18next/-/i18next-11.3.1.tgz#567f0e78350a30de32d2cee8667ce91b3d00d504"
 
 iconv-lite@0.4.13:
   version "0.4.13"
@@ -6541,7 +6653,7 @@ istanbul-reports@^1.1.3:
   dependencies:
     handlebars "^4.0.3"
 
-istextorbinary@^2.1.0:
+istextorbinary@^2.2.1:
   version "2.2.1"
   resolved "https://registry.yarnpkg.com/istextorbinary/-/istextorbinary-2.2.1.tgz#a5231a08ef6dd22b268d0895084cf8d58b5bec53"
   dependencies:
@@ -7246,6 +7358,10 @@ ldapjs@^1.0.1:
   optionalDependencies:
     dtrace-provider "^0.7.0"
 
+leb@^0.3.0:
+  version "0.3.0"
+  resolved "https://registry.yarnpkg.com/leb/-/leb-0.3.0.tgz#32bee9fad168328d6aea8522d833f4180eed1da3"
+
 left-pad@^1.2.0:
   version "1.2.0"
   resolved "https://registry.yarnpkg.com/left-pad/-/left-pad-1.2.0.tgz#d30a73c6b8201d8f7d8e7956ba9616087a68e0ee"
@@ -7566,7 +7682,7 @@ lodash.values@^4.3.0:
   version "4.3.0"
   resolved "https://registry.yarnpkg.com/lodash.values/-/lodash.values-4.3.0.tgz#a3a6c2b0ebecc5c2cba1c17e6e620fe81b53d347"
 
-lodash@4.17.10:
+lodash@4.17.10, lodash@^4.17.10:
   version "4.17.10"
   resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.10.tgz#1b7793cf7259ea38fb3661d4d38b3260af8ae4e7"
 
@@ -7620,6 +7736,10 @@ long-timeout@~0.1.1:
   version "0.1.1"
   resolved "https://registry.yarnpkg.com/long-timeout/-/long-timeout-0.1.1.tgz#9721d788b47e0bcb5a24c2e2bee1a0da55dab514"
 
+long@^3.2.0:
+  version "3.2.0"
+  resolved "https://registry.yarnpkg.com/long/-/long-3.2.0.tgz#d821b7138ca1cb581c172990ef14db200b5c474b"
+
 long@^4.0.0:
   version "4.0.0"
   resolved "https://registry.yarnpkg.com/long/-/long-4.0.0.tgz#9a7b71cfb7d361a194ea555241c92f7468d5bf28"
@@ -7717,9 +7837,9 @@ markdown-it-anchor@4.0.0:
   dependencies:
     string "^3.3.3"
 
-markdown-it-attrs@1.2.1:
-  version "1.2.1"
-  resolved "https://registry.yarnpkg.com/markdown-it-attrs/-/markdown-it-attrs-1.2.1.tgz#5e1560f47e896f6c21650e3abc1b12ce9f967d09"
+markdown-it-attrs@2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/markdown-it-attrs/-/markdown-it-attrs-2.0.0.tgz#be9493483b20da76eea710590c39f18049b6cca1"
 
 markdown-it-emoji@1.4.0:
   version "1.4.0"
@@ -7812,15 +7932,16 @@ media-typer@0.3.0:
   version "0.3.0"
   resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
 
-mem-fs-editor@^3.0.2:
-  version "3.0.2"
-  resolved "https://registry.yarnpkg.com/mem-fs-editor/-/mem-fs-editor-3.0.2.tgz#dd0a6eaf2bb8a6b37740067aa549eb530105af9f"
+mem-fs-editor@^4.0.0:
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/mem-fs-editor/-/mem-fs-editor-4.0.2.tgz#55a79b1e824da631254c4c95ba6366602c77af90"
   dependencies:
     commondir "^1.0.1"
-    deep-extend "^0.4.0"
-    ejs "^2.3.1"
+    deep-extend "^0.5.1"
+    ejs "^2.5.9"
     glob "^7.0.3"
-    globby "^6.1.0"
+    globby "^8.0.0"
+    isbinaryfile "^3.0.2"
     mkdirp "^0.5.0"
     multimatch "^2.0.0"
     rimraf "^2.2.8"
@@ -8663,9 +8784,9 @@ object.pick@^1.3.0:
   dependencies:
     isobject "^3.0.1"
 
-offline-plugin@5.0.1:
-  version "5.0.1"
-  resolved "https://registry.yarnpkg.com/offline-plugin/-/offline-plugin-5.0.1.tgz#c795ce98f7835cd6ad0327554130527dea65e362"
+offline-plugin@5.0.3:
+  version "5.0.3"
+  resolved "https://registry.yarnpkg.com/offline-plugin/-/offline-plugin-5.0.3.tgz#61488c5c5842d8576aa677384b5bbd3e60949289"
   dependencies:
     deep-extend "^0.4.0"
     ejs "^2.3.4"
@@ -8724,9 +8845,9 @@ optimist@^0.6.1:
     minimist "~0.0.1"
     wordwrap "~0.0.2"
 
-optimize-css-assets-webpack-plugin@4.0.0:
-  version "4.0.0"
-  resolved "https://registry.yarnpkg.com/optimize-css-assets-webpack-plugin/-/optimize-css-assets-webpack-plugin-4.0.0.tgz#d5f80041fb1391b358a1f35273c3b53de814e8fe"
+optimize-css-assets-webpack-plugin@4.0.1:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/optimize-css-assets-webpack-plugin/-/optimize-css-assets-webpack-plugin-4.0.1.tgz#48f016766752c7648b92cc1e795b999732bd87a2"
   dependencies:
     cssnano "^3.4.0"
     last-call-webpack-plugin "^3.0.0"
@@ -8742,9 +8863,9 @@ optionator@^0.8.1, optionator@^0.8.2:
     type-check "~0.3.2"
     wordwrap "~1.0.0"
 
-ora@2.0.0:
-  version "2.0.0"
-  resolved "https://registry.yarnpkg.com/ora/-/ora-2.0.0.tgz#8ec3a37fa7bffb54a3a0c188a1f6798e7e1827cd"
+ora@2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/ora/-/ora-2.1.0.tgz#6caf2830eb924941861ec53a173799e008b51e5b"
   dependencies:
     chalk "^2.3.1"
     cli-cursor "^2.1.0"
@@ -9652,9 +9773,9 @@ postcss-filter-plugins@^2.0.0:
     postcss "^5.0.4"
     uniqid "^4.0.0"
 
-postcss-flexbugs-fixes@3.3.0:
-  version "3.3.0"
-  resolved "https://registry.yarnpkg.com/postcss-flexbugs-fixes/-/postcss-flexbugs-fixes-3.3.0.tgz#e00849b536063749da50a0d410ba5d9ee65e27b8"
+postcss-flexbugs-fixes@3.3.1:
+  version "3.3.1"
+  resolved "https://registry.yarnpkg.com/postcss-flexbugs-fixes/-/postcss-flexbugs-fixes-3.3.1.tgz#0783cc7212850ef707f97f8bc8b6fb624e00c75d"
   dependencies:
     postcss "^6.0.1"
 
@@ -9722,9 +9843,9 @@ postcss-load-plugins@^2.3.0:
     cosmiconfig "^2.1.1"
     object-assign "^4.1.0"
 
-postcss-loader@2.1.4:
-  version "2.1.4"
-  resolved "https://registry.yarnpkg.com/postcss-loader/-/postcss-loader-2.1.4.tgz#f44a6390e03c84108b2b2063182d1a1011b2ce76"
+postcss-loader@2.1.5:
+  version "2.1.5"
+  resolved "https://registry.yarnpkg.com/postcss-loader/-/postcss-loader-2.1.5.tgz#3c6336ee641c8f95138172533ae461a83595e788"
   dependencies:
     loader-utils "^1.1.0"
     postcss "^6.0.0"
@@ -10165,7 +10286,7 @@ postcss@^6.0.1:
     source-map "^0.5.6"
     supports-color "^4.2.0"
 
-postcss@^6.0.20, postcss@^6.0.21:
+postcss@^6.0.20:
   version "6.0.21"
   resolved "https://registry.yarnpkg.com/postcss/-/postcss-6.0.21.tgz#8265662694eddf9e9a5960db6da33c39e4cd069d"
   dependencies:
@@ -10173,6 +10294,14 @@ postcss@^6.0.20, postcss@^6.0.21:
     source-map "^0.6.1"
     supports-color "^5.3.0"
 
+postcss@^6.0.22:
+  version "6.0.22"
+  resolved "https://registry.yarnpkg.com/postcss/-/postcss-6.0.22.tgz#e23b78314905c3b90cbd61702121e7a78848f2a3"
+  dependencies:
+    chalk "^2.4.1"
+    source-map "^0.6.1"
+    supports-color "^5.4.0"
+
 postgres-array@~1.0.0:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/postgres-array/-/postgres-array-1.0.2.tgz#8e0b32eb03bf77a5c0a7851e0441c169a256a238"
@@ -10579,9 +10708,9 @@ range-parser@^1.0.3, range-parser@~1.2.0:
   version "1.2.0"
   resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e"
 
-raven@2.6.0:
-  version "2.6.0"
-  resolved "https://registry.yarnpkg.com/raven/-/raven-2.6.0.tgz#3806a82c9ee8cd3e75c3b7ea7bb1935aad092d0c"
+raven@2.6.1:
+  version "2.6.1"
+  resolved "https://registry.yarnpkg.com/raven/-/raven-2.6.1.tgz#9b1945c02b93ef84e92e0dd2843095c1294530b3"
   dependencies:
     cookie "0.3.1"
     md5 "^2.2.1"
@@ -11507,9 +11636,9 @@ seq-queue@0.0.5:
   version "0.0.5"
   resolved "https://registry.yarnpkg.com/seq-queue/-/seq-queue-0.0.5.tgz#d56812e1c017a6e4e7c3e3a37a1da6d78dd3c93e"
 
-sequelize@4.37.6:
-  version "4.37.6"
-  resolved "https://registry.yarnpkg.com/sequelize/-/sequelize-4.37.6.tgz#f37eec55a285cfdeab27f8a85c5db38034f87126"
+sequelize@4.37.7:
+  version "4.37.7"
+  resolved "https://registry.yarnpkg.com/sequelize/-/sequelize-4.37.7.tgz#a66bfb486f438c0584c1ccc8107388311ba34780"
   dependencies:
     bluebird "^3.5.0"
     cls-bluebird "^2.1.0"
@@ -12138,6 +12267,12 @@ supports-color@^5.3.0:
   dependencies:
     has-flag "^3.0.0"
 
+supports-color@^5.4.0:
+  version "5.4.0"
+  resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.4.0.tgz#1c6b337402c2137605efe19f10fec390f6faab54"
+  dependencies:
+    has-flag "^3.0.0"
+
 svg-pan-zoom@^3.5.2:
   version "3.5.3"
   resolved "https://registry.yarnpkg.com/svg-pan-zoom/-/svg-pan-zoom-3.5.3.tgz#e6b52a1b349716c78e7b5f92d0ed1cc1b860f5d8"
@@ -12762,14 +12897,18 @@ validator-as-promised@1.0.2:
     lodash "^4.14.0"
     validator "^5.5.0"
 
-validator@9.4.1, validator@^9.4.1:
-  version "9.4.1"
-  resolved "https://registry.yarnpkg.com/validator/-/validator-9.4.1.tgz#abf466d398b561cd243050112c6ff1de6cc12663"
+validator@10.2.0:
+  version "10.2.0"
+  resolved "https://registry.yarnpkg.com/validator/-/validator-10.2.0.tgz#61d6b10c3d5c9f368c75c2ce8ca2b792522eaafa"
 
 validator@^5.5.0:
   version "5.7.0"
   resolved "https://registry.yarnpkg.com/validator/-/validator-5.7.0.tgz#7a87a58146b695ac486071141c0c49d67da05e5c"
 
+validator@^9.4.1:
+  version "9.4.1"
+  resolved "https://registry.yarnpkg.com/validator/-/validator-9.4.1.tgz#abf466d398b561cd243050112c6ff1de6cc12663"
+
 vary@^1:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.1.tgz#67535ebb694c1d52257457984665323f587e8d37"
@@ -12784,9 +12923,9 @@ vasync@^1.6.4:
   dependencies:
     verror "1.6.0"
 
-vee-validate@2.0.8:
-  version "2.0.8"
-  resolved "https://registry.yarnpkg.com/vee-validate/-/vee-validate-2.0.8.tgz#5fe52e1158d418736fe65807517ef5a0998d9c87"
+vee-validate@2.0.9:
+  version "2.0.9"
+  resolved "https://registry.yarnpkg.com/vee-validate/-/vee-validate-2.0.9.tgz#948a96572d9e2369d5cb217a84269ecf6c1f9a11"
 
 velocity-animate@1.5.1:
   version "1.5.1"
@@ -12902,9 +13041,9 @@ vue-hot-reload-api@2.3.0, vue-hot-reload-api@^2.3.0:
   version "2.3.0"
   resolved "https://registry.yarnpkg.com/vue-hot-reload-api/-/vue-hot-reload-api-2.3.0.tgz#97976142405d13d8efae154749e88c4e358cf926"
 
-vue-loader@15.0.4:
-  version "15.0.4"
-  resolved "https://registry.yarnpkg.com/vue-loader/-/vue-loader-15.0.4.tgz#309cc1ee6a438cabbbc79f8639890ce6cd9ba14c"
+vue-loader@15.0.10:
+  version "15.0.10"
+  resolved "https://registry.yarnpkg.com/vue-loader/-/vue-loader-15.0.10.tgz#cbbb43d63492c24bfb1963fb7997d5349ef42e72"
   dependencies:
     "@vue/component-compiler-utils" "^1.2.1"
     hash-sum "^1.0.2"
@@ -13021,6 +13160,16 @@ wcwidth@^1.0.1:
   dependencies:
     defaults "^1.0.3"
 
+webassemblyjs@1.4.2:
+  version "1.4.2"
+  resolved "https://registry.yarnpkg.com/webassemblyjs/-/webassemblyjs-1.4.2.tgz#3b07b506917c97153d83441d8a88ffa2d25cc07d"
+  dependencies:
+    "@webassemblyjs/ast" "1.4.2"
+    "@webassemblyjs/validation" "1.4.2"
+    "@webassemblyjs/wasm-parser" "1.4.2"
+    "@webassemblyjs/wast-parser" "1.4.2"
+    long "^3.2.0"
+
 webidl-conversions@^4.0.1, webidl-conversions@^4.0.2:
   version "4.0.2"
   resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad"
@@ -13031,9 +13180,9 @@ webpack-addons@^1.1.5:
   dependencies:
     jscodeshift "^0.4.0"
 
-webpack-bundle-analyzer@2.11.1:
-  version "2.11.1"
-  resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-2.11.1.tgz#b9fbfb6a32c0a8c1c3237223e90890796b950ab9"
+webpack-bundle-analyzer@2.11.2:
+  version "2.11.2"
+  resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-2.11.2.tgz#5ae05bfccffb16683977237bf319323653e44f9f"
   dependencies:
     acorn "^5.3.0"
     bfj-node4 "^5.2.0"
@@ -13048,9 +13197,9 @@ webpack-bundle-analyzer@2.11.1:
     opener "^1.4.3"
     ws "^4.0.0"
 
-webpack-cli@2.0.15:
-  version "2.0.15"
-  resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-2.0.15.tgz#7532066556b03bd3292285ac08537e28844616c2"
+webpack-cli@2.1.3:
+  version "2.1.3"
+  resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-2.1.3.tgz#65d166851abaa56067ef3f716b02a97ba6bbe84d"
   dependencies:
     chalk "^2.3.2"
     cross-spawn "^6.0.5"
@@ -13077,7 +13226,7 @@ webpack-cli@2.0.15:
     webpack-addons "^1.1.5"
     yargs "^11.1.0"
     yeoman-environment "^2.0.0"
-    yeoman-generator "^2.0.3"
+    yeoman-generator "^2.0.4"
 
 webpack-dev-middleware@3.1.3:
   version "3.1.3"
@@ -13122,10 +13271,13 @@ webpack-sources@^1.0.1, webpack-sources@^1.1.0:
     source-list-map "^2.0.0"
     source-map "~0.6.1"
 
-webpack@4.6.0:
-  version "4.6.0"
-  resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.6.0.tgz#363eafa733710eb0ed28c512b2b9b9f5fb01e69b"
+webpack@4.8.2:
+  version "4.8.2"
+  resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.8.2.tgz#41aa00fd32a8f253a2f12a2da11c8ad4d52fde1c"
   dependencies:
+    "@webassemblyjs/ast" "1.4.2"
+    "@webassemblyjs/wasm-edit" "1.4.2"
+    "@webassemblyjs/wasm-parser" "1.4.2"
     acorn "^5.0.0"
     acorn-dynamic-import "^3.0.0"
     ajv "^6.1.0"
@@ -13513,25 +13665,25 @@ yeoman-environment@^2.0.0, yeoman-environment@^2.0.5:
     text-table "^0.2.0"
     untildify "^3.0.2"
 
-yeoman-generator@^2.0.3:
-  version "2.0.3"
-  resolved "https://registry.yarnpkg.com/yeoman-generator/-/yeoman-generator-2.0.3.tgz#19426ed22687ffe05d31526c3f1c2cf67ba768f3"
+yeoman-generator@^2.0.4:
+  version "2.0.5"
+  resolved "https://registry.yarnpkg.com/yeoman-generator/-/yeoman-generator-2.0.5.tgz#57b0b3474701293cc9ec965288f3400b00887c81"
   dependencies:
     async "^2.6.0"
     chalk "^2.3.0"
     cli-table "^0.3.1"
-    cross-spawn "^5.1.0"
+    cross-spawn "^6.0.5"
     dargs "^5.1.0"
-    dateformat "^3.0.2"
+    dateformat "^3.0.3"
     debug "^3.1.0"
     detect-conflict "^1.0.0"
     error "^7.0.2"
     find-up "^2.1.0"
     github-username "^4.0.0"
-    istextorbinary "^2.1.0"
-    lodash "^4.17.4"
+    istextorbinary "^2.2.1"
+    lodash "^4.17.10"
     make-dir "^1.1.0"
-    mem-fs-editor "^3.0.2"
+    mem-fs-editor "^4.0.0"
     minimist "^1.2.0"
     pretty-bytes "^4.0.2"
     read-chunk "^2.1.0"