Browse Source

feat: uploads config + security admin page

NGPixel 5 years ago
parent
commit
134f057bb8

+ 4 - 0
client/components/admin.vue

@@ -89,6 +89,9 @@
             v-list-item(to='/mail', color='primary', v-if='hasPermission(`manage:system`)')
               v-list-item-avatar(size='24', tile): v-icon mdi-email-multiple-outline
               v-list-item-title {{ $t('admin:mail.title') }}
+            v-list-item(to='/security', v-if='hasPermission(`manage:system`)')
+              v-list-item-avatar(size='24', tile): v-icon mdi-lock-check
+              v-list-item-title {{ $t('admin:security.title') }}
             v-list-item(to='/ssl', v-if='hasPermission(`manage:system`)')
               v-list-item-avatar(size='24', tile): v-icon mdi-cloud-lock-outline
               v-list-item-title {{ $t('admin:ssl.title') }}
@@ -172,6 +175,7 @@ const router = new VueRouter({
     { path: '/storage', component: () => import(/* webpackChunkName: "admin" */ './admin/admin-storage.vue') },
     { path: '/api', component: () => import(/* webpackChunkName: "admin" */ './admin/admin-api.vue') },
     { path: '/mail', component: () => import(/* webpackChunkName: "admin" */ './admin/admin-mail.vue') },
+    { path: '/security', component: () => import(/* webpackChunkName: "admin" */ './admin/admin-security.vue') },
     { path: '/ssl', component: () => import(/* webpackChunkName: "admin" */ './admin/admin-ssl.vue') },
     { path: '/system', component: () => import(/* webpackChunkName: "admin" */ './admin/admin-system.vue') },
     { path: '/utilities', component: () => import(/* webpackChunkName: "admin" */ './admin/admin-utilities.vue') },

+ 3 - 138
client/components/admin/admin-general.vue

@@ -167,93 +167,6 @@
                     disabled
                     )
 
-              v-card.mt-5.animated.fadeInUp.wait-p5s
-                v-toolbar(color='red darken-2', dark, dense, flat)
-                  v-toolbar-title.subtitle-1 Security
-                v-card-text
-                  v-alert(outlined, color='red darken-2', icon='mdi-information-outline').body-2 Make sure to understand the implications before turning on / off a security feature.
-                  v-switch.mt-3(
-                    inset
-                    label='Block IFrame Embedding'
-                    color='red darken-2'
-                    v-model='config.securityIframe'
-                    persistent-hint
-                    hint='Prevents other websites from embedding your wiki in an iframe. This provides clickjacking protection.'
-                    )
-
-                  v-divider.mt-3
-                  v-switch(
-                    inset
-                    label='Same Origin Referrer Policy'
-                    color='red darken-2'
-                    v-model='config.securityReferrerPolicy'
-                    persistent-hint
-                    hint='Limits the referrer header to same origin.'
-                    )
-
-                  v-divider.mt-3
-                  v-switch(
-                    inset
-                    label='Trust X-Forwarded-* Proxy Headers'
-                    color='red darken-2'
-                    v-model='config.securityTrustProxy'
-                    persistent-hint
-                    hint='Should be enabled when using a reverse-proxy like nginx, apache, CloudFlare, etc in front of Wiki.js. Turn off otherwise.'
-                    )
-
-                  v-divider.mt-3
-                  v-switch(
-                    inset
-                    label='Subresource Integrity (SRI)'
-                    color='red darken-2'
-                    v-model='config.securitySRI'
-                    persistent-hint
-                    hint='This ensure that resources such as CSS and JS files are not altered during delivery.'
-                    disabled
-                    )
-
-                  v-divider.mt-3
-                  v-switch(
-                    inset
-                    label='Enforce HSTS'
-                    color='red darken-2'
-                    v-model='config.securityHSTS'
-                    persistent-hint
-                    hint='This ensures the connection cannot be established through an insecure HTTP connection.'
-                    )
-                  v-select.mt-5(
-                    outlined
-                    label='HSTS Max Age'
-                    :items='hstsDurations'
-                    v-model='config.securityHSTSDuration'
-                    prepend-icon='mdi-subdirectory-arrow-right'
-                    :disabled='!config.securityHSTS'
-                    hide-details
-                    style='max-width: 450px;'
-                    )
-                  .pl-11.mt-3
-                    .caption Defines the duration for which the server should only deliver content through HTTPS.
-                    .caption It's a good idea to start with small values and make sure that nothing breaks on your wiki before moving to longer values.
-
-                  v-divider.mt-3
-                  v-switch(
-                    inset
-                    label='Enforce CSP'
-                    color='red darken-2'
-                    v-model='config.securityCSP'
-                    persistent-hint
-                    hint='Restricts scripts to pre-approved content sources.'
-                    disabled
-                    )
-                  v-textarea.mt-5(
-                    label='CSP Directives'
-                    outlined
-                    v-model='config.securityCSPDirectives'
-                    prepend-icon='mdi-subdirectory-arrow-right'
-                    persistent-hint
-                    hint='One directive per line.'
-                    disabled
-                  )
     component(:is='activeModal')
 
 </template>
@@ -296,24 +209,8 @@ export default {
         featurePageRatings: false,
         featurePageComments: false,
         featurePersonalWikis: false,
-        featureTinyPNG: false,
-        securityIframe: true,
-        securityReferrerPolicy: true,
-        securityTrustProxy: true,
-        securitySRI: true,
-        securityHSTS: false,
-        securityHSTSDuration: 0,
-        securityCSP: false,
-        securityCSPDirectives: ''
+        featureTinyPNG: false
       },
-      hstsDurations: [
-        { value: 300, text: '5 minutes' },
-        { value: 86400, text: '1 day' },
-        { value: 604800, text: '1 week' },
-        { value: 2592000, text: '1 month' },
-        { value: 31536000, text: '1 year' },
-        { value: 63072000, text: '2 years' }
-      ],
       metaRobots: [
         { text: 'Index', value: 'index' },
         { text: 'Follow', value: 'follow' },
@@ -360,14 +257,6 @@ export default {
               $featurePageRatings: Boolean!
               $featurePageComments: Boolean!
               $featurePersonalWikis: Boolean!
-              $securityIframe: Boolean!
-              $securityReferrerPolicy: Boolean!
-              $securityTrustProxy: Boolean!
-              $securitySRI: Boolean!
-              $securityHSTS: Boolean!
-              $securityHSTSDuration: Int!
-              $securityCSP: Boolean!
-              $securityCSPDirectives: String!
             ) {
               site {
                 updateConfig(
@@ -382,15 +271,7 @@ export default {
                   logoUrl: $logoUrl,
                   featurePageRatings: $featurePageRatings,
                   featurePageComments: $featurePageComments,
-                  featurePersonalWikis: $featurePersonalWikis,
-                  securityIframe: $securityIframe,
-                  securityReferrerPolicy: $securityReferrerPolicy,
-                  securityTrustProxy: $securityTrustProxy,
-                  securitySRI: $securitySRI,
-                  securityHSTS: $securityHSTS,
-                  securityHSTSDuration: $securityHSTSDuration,
-                  securityCSP: $securityCSP,
-                  securityCSPDirectives: $securityCSPDirectives
+                  featurePersonalWikis: $featurePersonalWikis
                 ) {
                   responseResult {
                     succeeded
@@ -414,15 +295,7 @@ export default {
             logoUrl: _.get(this.config, 'logoUrl', ''),
             featurePageRatings: _.get(this.config, 'featurePageRatings', false),
             featurePageComments: _.get(this.config, 'featurePageComments', false),
-            featurePersonalWikis: _.get(this.config, 'featurePersonalWikis', false),
-            securityIframe: _.get(this.config, 'securityIframe', false),
-            securityReferrerPolicy: _.get(this.config, 'securityReferrerPolicy', false),
-            securityTrustProxy: _.get(this.config, 'securityTrustProxy', false),
-            securitySRI: _.get(this.config, 'securitySRI', false),
-            securityHSTS: _.get(this.config, 'securityHSTS', false),
-            securityHSTSDuration: _.get(this.config, 'securityHSTSDuration', 0),
-            securityCSP: _.get(this.config, 'securityCSP', false),
-            securityCSPDirectives: _.get(this.config, 'securityCSPDirectives', '')
+            featurePersonalWikis: _.get(this.config, 'featurePersonalWikis', false)
           },
           watchLoading (isLoading) {
             this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-site-update')
@@ -475,14 +348,6 @@ export default {
               featurePageRatings
               featurePageComments
               featurePersonalWikis
-              securityIframe
-              securityReferrerPolicy
-              securityTrustProxy
-              securitySRI
-              securityHSTS
-              securityHSTSDuration
-              securityCSP
-              securityCSPDirectives
             }
           }
         }

+ 265 - 0
client/components/admin/admin-security.vue

@@ -0,0 +1,265 @@
+<template lang='pug'>
+  v-container(fluid, grid-list-lg)
+    v-layout(row wrap)
+      v-flex(xs12)
+        .admin-header
+          img.animated.fadeInUp(src='/svg/icon-private.svg', alt='Security', style='width: 80px;')
+          .admin-header-title
+            .headline.primary--text.animated.fadeInLeft {{ $t('admin:security.title') }}
+            .subtitle-1.grey--text.animated.fadeInLeft {{ $t('admin:security.subtitle') }}
+          v-spacer
+          v-btn.animated.fadeInDown(color='success', depressed, @click='save', large)
+            v-icon(left) mdi-check
+            span {{$t('common:actions.apply')}}
+        v-form.pt-3
+          v-layout(row wrap)
+            v-flex(lg6 xs12)
+              v-card.animated.fadeInUp
+                v-toolbar(color='red darken-2', dark, dense, flat)
+                  v-toolbar-title.subtitle-1 Security
+                v-card-text
+                  v-alert(outlined, color='red darken-2', icon='mdi-information-outline').body-2 Make sure to understand the implications before turning on / off a security feature.
+                  v-switch.mt-3(
+                    inset
+                    label='Block IFrame Embedding'
+                    color='red darken-2'
+                    v-model='config.securityIframe'
+                    persistent-hint
+                    hint='Prevents other websites from embedding your wiki in an iframe. This provides clickjacking protection.'
+                    )
+
+                  v-divider.mt-3
+                  v-switch(
+                    inset
+                    label='Same Origin Referrer Policy'
+                    color='red darken-2'
+                    v-model='config.securityReferrerPolicy'
+                    persistent-hint
+                    hint='Limits the referrer header to same origin.'
+                    )
+
+                  v-divider.mt-3
+                  v-switch(
+                    inset
+                    label='Trust X-Forwarded-* Proxy Headers'
+                    color='red darken-2'
+                    v-model='config.securityTrustProxy'
+                    persistent-hint
+                    hint='Should be enabled when using a reverse-proxy like nginx, apache, CloudFlare, etc in front of Wiki.js. Turn off otherwise.'
+                    )
+
+                  //- v-divider.mt-3
+                  //- v-switch(
+                  //-   inset
+                  //-   label='Subresource Integrity (SRI)'
+                  //-   color='red darken-2'
+                  //-   v-model='config.securitySRI'
+                  //-   persistent-hint
+                  //-   hint='This ensure that resources such as CSS and JS files are not altered during delivery.'
+                  //-   disabled
+                  //-   )
+
+                  v-divider.mt-3
+                  v-switch(
+                    inset
+                    label='Enforce HSTS'
+                    color='red darken-2'
+                    v-model='config.securityHSTS'
+                    persistent-hint
+                    hint='This ensures the connection cannot be established through an insecure HTTP connection.'
+                    )
+                  v-select.mt-5(
+                    outlined
+                    label='HSTS Max Age'
+                    :items='hstsDurations'
+                    v-model='config.securityHSTSDuration'
+                    prepend-icon='mdi-subdirectory-arrow-right'
+                    :disabled='!config.securityHSTS'
+                    hide-details
+                    style='max-width: 450px;'
+                    )
+                  .pl-11.mt-3
+                    .caption Defines the duration for which the server should only deliver content through HTTPS.
+                    .caption It's a good idea to start with small values and make sure that nothing breaks on your wiki before moving to longer values.
+
+                  v-divider.mt-3
+                  v-switch(
+                    inset
+                    label='Enforce CSP'
+                    color='red darken-2'
+                    v-model='config.securityCSP'
+                    persistent-hint
+                    hint='Restricts scripts to pre-approved content sources.'
+                    disabled
+                    )
+                  v-textarea.mt-5(
+                    label='CSP Directives'
+                    outlined
+                    v-model='config.securityCSPDirectives'
+                    prepend-icon='mdi-subdirectory-arrow-right'
+                    persistent-hint
+                    hint='One directive per line.'
+                    disabled
+                  )
+
+            v-flex(lg6 xs12)
+              v-card.animated.fadeInUp.wait-p2s
+                v-toolbar(color='primary', dark, dense, flat)
+                  v-toolbar-title.subtitle-1 {{ $t('admin:security.uploads') }}
+                v-card-text
+                  v-text-field(
+                    outlined
+                    :label='$t(`admin:security.maxUploadSize`)'
+                    required
+                    v-model='config.uploadMaxFileSize'
+                    prepend-icon='mdi-progress-upload'
+                    :hint='$t(`admin:security.maxUploadSizeHint`)'
+                    persistent-hint
+                    :suffix='$t(`admin:security.maxUploadSizeSuffix`)'
+                    style='max-width: 450px;'
+                    )
+                  v-text-field.mt-3(
+                    outlined
+                    :label='$t(`admin:security.maxUploadBatch`)'
+                    required
+                    v-model='config.uploadMaxFiles'
+                    prepend-icon='mdi-upload-lock'
+                    :hint='$t(`admin:security.maxUploadBatchHint`)'
+                    persistent-hint
+                    :suffix='$t(`admin:security.maxUploadBatchSuffix`)'
+                    style='max-width: 450px;'
+                    )
+</template>
+
+<script>
+import _ from 'lodash'
+import { sync } from 'vuex-pathify'
+import gql from 'graphql-tag'
+
+export default {
+  data() {
+    return {
+      config: {
+        uploadMaxFileSize: 0,
+        uploadMaxFiles: 0,
+        securityIframe: true,
+        securityReferrerPolicy: true,
+        securityTrustProxy: true,
+        securitySRI: true,
+        securityHSTS: false,
+        securityHSTSDuration: 0,
+        securityCSP: false,
+        securityCSPDirectives: ''
+      },
+      hstsDurations: [
+        { value: 300, text: '5 minutes' },
+        { value: 86400, text: '1 day' },
+        { value: 604800, text: '1 week' },
+        { value: 2592000, text: '1 month' },
+        { value: 31536000, text: '1 year' },
+        { value: 63072000, text: '2 years' }
+      ]
+    }
+  },
+  computed: {
+    activeModal: sync('editor/activeModal')
+  },
+  methods: {
+    async save () {
+      try {
+        await this.$apollo.mutate({
+          mutation: gql`
+            mutation (
+              $uploadMaxFileSize: Int
+              $uploadMaxFiles: Int
+              $securityIframe: Boolean
+              $securityReferrerPolicy: Boolean
+              $securityTrustProxy: Boolean
+              $securitySRI: Boolean
+              $securityHSTS: Boolean
+              $securityHSTSDuration: Int
+              $securityCSP: Boolean
+              $securityCSPDirectives: String
+            ) {
+              site {
+                updateConfig(
+                  uploadMaxFileSize: $uploadMaxFileSize,
+                  uploadMaxFiles: $uploadMaxFiles,
+                  securityIframe: $securityIframe,
+                  securityReferrerPolicy: $securityReferrerPolicy,
+                  securityTrustProxy: $securityTrustProxy,
+                  securitySRI: $securitySRI,
+                  securityHSTS: $securityHSTS,
+                  securityHSTSDuration: $securityHSTSDuration,
+                  securityCSP: $securityCSP,
+                  securityCSPDirectives: $securityCSPDirectives
+                ) {
+                  responseResult {
+                    succeeded
+                    errorCode
+                    slug
+                    message
+                  }
+                }
+              }
+            }
+          `,
+          variables: {
+            uploadMaxFileSize: _.toSafeInteger(_.get(this.config, 'uploadMaxFileSize', 0)),
+            uploadMaxFiles: _.toSafeInteger(_.get(this.config, 'uploadMaxFiles', 0)),
+            securityIframe: _.get(this.config, 'securityIframe', false),
+            securityReferrerPolicy: _.get(this.config, 'securityReferrerPolicy', false),
+            securityTrustProxy: _.get(this.config, 'securityTrustProxy', false),
+            securitySRI: _.get(this.config, 'securitySRI', false),
+            securityHSTS: _.get(this.config, 'securityHSTS', false),
+            securityHSTSDuration: _.get(this.config, 'securityHSTSDuration', 0),
+            securityCSP: _.get(this.config, 'securityCSP', false),
+            securityCSPDirectives: _.get(this.config, 'securityCSPDirectives', '')
+          },
+          watchLoading (isLoading) {
+            this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-site-update')
+          }
+        })
+        this.$store.commit('showNotification', {
+          style: 'success',
+          message: 'Configuration saved successfully.',
+          icon: 'check'
+        })
+      } catch (err) {
+        this.$store.commit('pushGraphError', err)
+      }
+    }
+  },
+  apollo: {
+    config: {
+      query: gql`
+        {
+          site {
+            config {
+              uploadMaxFileSize
+              uploadMaxFiles
+              securityIframe
+              securityReferrerPolicy
+              securityTrustProxy
+              securitySRI
+              securityHSTS
+              securityHSTSDuration
+              securityCSP
+              securityCSPDirectives
+            }
+          }
+        }
+      `,
+      fetchPolicy: 'network-only',
+      update: (data) => _.cloneDeep(data.site.config),
+      watchLoading (isLoading) {
+        this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-security-refresh')
+      }
+    }
+  }
+}
+</script>
+
+<style lang='scss'>
+
+</style>

File diff suppressed because it is too large
+ 0 - 0
client/static/svg/icon-private.svg


+ 0 - 12
config.sample.yml

@@ -105,18 +105,6 @@ bindIP: 0.0.0.0
 
 logLevel: info
 
-# ---------------------------------------------------------------------
-# Upload Limits
-# ---------------------------------------------------------------------
-# If you're using a reverse-proxy in front of Wiki.js, you must also
-# change your proxy upload limits!
-
-uploads:
-  # Maximum upload size in bytes per file (default: 5242880 (5 MB))
-  maxFileSize: 5242880
-  # Maximum file uploads per request (default: 10)
-  maxFiles: 10
-
 # ---------------------------------------------------------------------
 # Offline Mode
 # ---------------------------------------------------------------------

+ 3 - 3
server/app/data.yml

@@ -24,9 +24,6 @@ defaults:
       min: 1
     bindIP: 0.0.0.0
     logLevel: info
-    uploads:
-      maxFileSize: 5242880
-      maxFiles: 10
     offline: false
     ha: false
     # DB defaults
@@ -67,6 +64,9 @@ defaults:
       securityCSPDirectives: ''
     server:
       sslRedir: false
+    uploads:
+      maxFileSize: 5242880
+      maxFiles: 10
     flags:
       ldapdebug: false
       sqllog: false

+ 9 - 7
server/controllers/upload.js

@@ -10,13 +10,15 @@ const sanitize = require('sanitize-filename')
 /**
  * Upload files
  */
-router.post('/u', multer({
-  dest: path.resolve(WIKI.ROOTPATH, WIKI.config.dataPath, 'uploads'),
-  limits: {
-    fileSize: WIKI.config.uploads.maxFileSize,
-    files: WIKI.config.uploads.maxFiles
-  }
-}).array('mediaUpload'), async (req, res, next) => {
+router.post('/u', (req, res, next) => {
+  multer({
+    dest: path.resolve(WIKI.ROOTPATH, WIKI.config.dataPath, 'uploads'),
+    limits: {
+      fileSize: WIKI.config.uploads.maxFileSize,
+      files: WIKI.config.uploads.maxFiles
+    }
+  }).array('mediaUpload')(req, res, next)
+}, async (req, res, next) => {
   if (!_.some(req.user.permissions, pm => _.includes(['write:assets', 'manage:system'], pm))) {
     return res.status(403).json({
       succeeded: false,

+ 51 - 26
server/graph/resolvers/site.js

@@ -20,44 +20,69 @@ module.exports = {
         logoUrl: WIKI.config.logoUrl,
         ...WIKI.config.seo,
         ...WIKI.config.features,
-        ...WIKI.config.security
+        ...WIKI.config.security,
+        uploadMaxFileSize: WIKI.config.uploads.maxFileSize,
+        uploadMaxFiles: WIKI.config.uploads.maxFiles
       }
     }
   },
   SiteMutation: {
     async updateConfig(obj, args, context) {
-      let siteHost = _.trim(args.host)
-      if (siteHost.endsWith('/')) {
-        siteHost = siteHost.splice(0, -1)
-      }
       try {
-        WIKI.config.host = siteHost
-        WIKI.config.title = _.trim(args.title)
-        WIKI.config.company = _.trim(args.company)
-        WIKI.config.contentLicense = args.contentLicense
+        if (args.host) {
+          let siteHost = _.trim(args.host)
+          if (siteHost.endsWith('/')) {
+            siteHost = siteHost.splice(0, -1)
+          }
+          WIKI.config.host = siteHost
+        }
+
+        if (args.title) {
+          WIKI.config.title = _.trim(args.title)
+        }
+
+        if (args.company) {
+          WIKI.config.company = _.trim(args.company)
+        }
+
+        if (args.contentLicense) {
+          WIKI.config.contentLicense = args.contentLicense
+        }
+
+        if (args.logoUrl) {
+          WIKI.config.logoUrl = _.trim(args.logoUrl)
+        }
+
         WIKI.config.seo = {
-          description: args.description,
-          robots: args.robots,
-          analyticsService: args.analyticsService,
-          analyticsId: args.analyticsId
+          description: _.get(args, 'description', WIKI.config.seo.description),
+          robots: _.get(args, 'robots', WIKI.config.seo.robots),
+          analyticsService: _.get(args, 'analyticsService', WIKI.config.seo.analyticsService),
+          analyticsId: _.get(args, 'analyticsId', WIKI.config.seo.analyticsId)
         }
-        WIKI.config.logoUrl = _.trim(args.logoUrl)
+
         WIKI.config.features = {
-          featurePageRatings: args.featurePageRatings,
-          featurePageComments: args.featurePageComments,
-          featurePersonalWikis: args.featurePersonalWikis
+          featurePageRatings: _.get(args, 'featurePageRatings', WIKI.config.features.featurePageRatings),
+          featurePageComments: _.get(args, 'featurePageComments', WIKI.config.features.featurePageComments),
+          featurePersonalWikis: _.get(args, 'featurePersonalWikis', WIKI.config.features.featurePersonalWikis)
         }
+
         WIKI.config.security = {
-          securityIframe: args.securityIframe,
-          securityReferrerPolicy: args.securityReferrerPolicy,
-          securityTrustProxy: args.securityTrustProxy,
-          securitySRI: args.securitySRI,
-          securityHSTS: args.securityHSTS,
-          securityHSTSDuration: args.securityHSTSDuration,
-          securityCSP: args.securityCSP,
-          securityCSPDirectives: args.securityCSPDirectives
+          securityIframe: _.get(args, 'securityIframe', WIKI.config.security.securityIframe),
+          securityReferrerPolicy: _.get(args, 'securityReferrerPolicy', WIKI.config.security.securityReferrerPolicy),
+          securityTrustProxy: _.get(args, 'securityTrustProxy', WIKI.config.security.securityTrustProxy),
+          securitySRI: _.get(args, 'securitySRI', WIKI.config.security.securitySRI),
+          securityHSTS: _.get(args, 'securityHSTS', WIKI.config.security.securityHSTS),
+          securityHSTSDuration: _.get(args, 'securityHSTSDuration', WIKI.config.security.securityHSTSDuration),
+          securityCSP: _.get(args, 'securityCSP', WIKI.config.security.securityCSP),
+          securityCSPDirectives: _.get(args, 'securityCSPDirectives', WIKI.config.security.securityCSPDirectives)
+        }
+
+        WIKI.config.uploads = {
+          maxFileSize: _.get(args, 'uploadMaxFileSize', WIKI.config.uploads.maxFileSize),
+          maxFiles: _.get(args, 'uploadMaxFiles', WIKI.config.uploads.maxFiles)
         }
-        await WIKI.configSvc.saveToDb(['host', 'title', 'company', 'contentLicense', 'seo', 'logoUrl', 'features', 'security'])
+
+        await WIKI.configSvc.saveToDb(['host', 'title', 'company', 'contentLicense', 'seo', 'logoUrl', 'features', 'security', 'uploads'])
 
         if (WIKI.config.security.securityTrustProxy) {
           WIKI.app.enable('trust proxy')

+ 25 - 20
server/graph/schemas/site.graphql

@@ -24,26 +24,29 @@ type SiteQuery {
 
 type SiteMutation {
   updateConfig(
-    host: String!
-    title: String!
-    description: String!
-    robots: [String]!
-    analyticsService: String!
-    analyticsId: String!
-    company: String!
-    contentLicense: String!
-    logoUrl: String!
-    featurePageRatings: Boolean!
-    featurePageComments: Boolean!
-    featurePersonalWikis: Boolean!
-    securityIframe: Boolean!
-    securityReferrerPolicy: Boolean!
-    securityTrustProxy: Boolean!
-    securitySRI: Boolean!
-    securityHSTS: Boolean!
-    securityHSTSDuration: Int!
-    securityCSP: Boolean!
-    securityCSPDirectives: String!
+    host: String
+    title: String
+    description: String
+    robots: [String]
+    analyticsService: String
+    analyticsId: String
+    company: String
+    contentLicense: String
+    logoUrl: String
+    featurePageRatings: Boolean
+    featurePageComments: Boolean
+    featurePersonalWikis: Boolean
+    securityIframe: Boolean
+    securityReferrerPolicy: Boolean
+    securityTrustProxy: Boolean
+    securitySRI: Boolean
+    securityHSTS: Boolean
+    securityHSTSDuration: Int
+    securityCSP: Boolean
+    securityCSPDirectives: String
+    uploadMaxFileSize: Int
+    uploadMaxFiles: Int
+
   ): DefaultResponse @auth(requires: ["manage:system"])
 }
 
@@ -72,4 +75,6 @@ type SiteConfig {
   securityHSTSDuration: Int!
   securityCSP: Boolean!
   securityCSPDirectives: String!
+  uploadMaxFileSize: Int!
+  uploadMaxFiles: Int!
 }

+ 1 - 0
server/setup.js

@@ -186,6 +186,7 @@ module.exports = () => {
         'sessionSecret',
         'telemetry',
         'theming',
+        'uploads',
         'title'
       ], false)
 

Some files were not shown because too many files changed in this diff