Sfoglia il codice sorgente

feat: admin ssl - renew cert + toggle redirection btn

NGPixel 5 anni fa
parent
commit
ae53484abd

+ 213 - 117
client/components/admin/admin-ssl.vue

@@ -8,163 +8,259 @@
             .headline.primary--text.animated.fadeInLeft {{ $t('admin:ssl.title') }}
             .subtitle-1.grey--text.animated.fadeInLeft {{ $t('admin:ssl.subtitle') }}
           v-spacer
-          v-btn.animated.fadeInDown(color='success', depressed, @click='save', large)
-            v-icon(left) mdi-check
-            span {{$t('common:actions.apply')}}
+          v-btn.animated.fadeInDown(
+            v-if='info.sslProvider === `letsencrypt`'
+            color='black'
+            dark
+            depressed
+            @click='renewCertificate'
+            large
+            :loading='loadingRenew'
+            )
+            v-icon(left) mdi-cached
+            span {{$t('admin:ssl.renewCertificate')}}
         v-form.pt-3
           v-layout(row wrap)
             v-flex(lg6 xs12)
-              v-form
-                v-card.animated.fadeInUp
-                  v-toolbar(color='primary', dark, dense, flat)
-                    v-toolbar-title.subtitle-1 {{ $t('admin:ssl.provider') }}
-                  v-card-text
-                    v-select(
-                      :items='providers'
-                      outlined
-                      :label='$t(`admin:ssl.provider`)'
-                      required
-                      :counter='255'
-                      v-model='config.provider'
-                      prepend-icon='mdi-handshake'
-                      :hint='$t(`admin:ssl.providerHint`)'
-                      persistent-hint
-                      )
-                    v-text-field.mt-3(
-                      outlined
-                      :label='$t(`admin:ssl.domain`)'
-                      required
-                      :counter='255'
-                      v-model='config.domain'
-                      prepend-icon='mdi-earth'
-                      :hint='$t(`admin:ssl.domainHint`)'
-                      persistent-hint
-                      :disabled='config.provider === ``'
-                      )
-
-              v-card.animated.fadeInUp.wait-p2s.mt-3(v-if='config.provider !== ``')
-                v-toolbar(color='primary', dark, dense, flat)
-                  v-toolbar-title.subtitle-1 {{$t('admin:ssl.providerOptions')}}
-                v-card-text ---
+              v-card.animated.fadeInUp
+                v-subheader {{ $t('admin:ssl.currentState') }}
+                v-list(two-line, dense)
+                  v-list-item
+                    v-list-item-avatar
+                      v-icon.indigo.white--text mdi-handshake
+                    v-list-item-content
+                      v-list-item-title {{ $t(`admin:ssl.provider`) }}
+                      v-list-item-subtitle {{ providerTitle }}
+                  template(v-if='info.sslProvider === `letsencrypt`')
+                    v-list-item
+                      v-list-item-avatar
+                        v-icon.indigo.white--text mdi-application
+                      v-list-item-content
+                        v-list-item-title {{ $t(`admin:ssl.domain`) }}
+                        v-list-item-subtitle {{ info.sslDomain }}
+                    v-list-item
+                      v-list-item-avatar
+                        v-icon.indigo.white--text mdi-at
+                      v-list-item-content
+                        v-list-item-title {{ $t('admin:ssl.subscriberEmail') }}
+                        v-list-item-subtitle {{ info.sslSubscriberEmail }}
+                    v-list-item
+                      v-list-item-avatar
+                        v-icon.indigo.white--text mdi-calendar-remove-outline
+                      v-list-item-content
+                        v-list-item-title {{ $t('admin:ssl.expiration') }}
+                        v-list-item-subtitle {{ info.sslExpirationDate | moment('calendar') }}
+                    v-list-item
+                      v-list-item-avatar
+                        v-icon.indigo.white--text mdi-traffic-light
+                      v-list-item-content
+                        v-list-item-title {{ $t(`admin:ssl.status`) }}
+                        v-list-item-subtitle {{ info.sslStatus }}
 
             v-flex(lg6 xs12)
               v-card.animated.fadeInUp.wait-p2s
-                  v-toolbar(color='primary', dark, dense, flat)
-                    v-toolbar-title.subtitle-1 {{ $t('admin:ssl.ports') }}
-                  v-card-text
-                    v-row
-                      v-col(cols='6')
-                        v-text-field(
-                          outlined
-                          :label='$t(`admin:ssl.httpPort`)'
-                          v-model='config.httpPort'
-                          prepend-icon='mdi-lock-open-variant-outline'
-                          :hint='$t(`admin:ssl.httpPortHint`)'
-                          persistent-hint
-                          )
-                      v-col(cols='6')
-                        v-checkbox(
-                          :label='$t(`admin:ssl.httpPortRedirect`)'
-                          v-model='config.httpRedirect'
-                          :hint='$t(`admin:ssl.httpPortRedirectHint`)'
-                          :disabled='config.provider === ``'
-                          persistent-hint
-                          color='primary'
+                v-subheader {{ $t('admin:ssl.ports') }}
+                v-list(two-line, dense)
+                  v-list-item
+                    v-list-item-avatar
+                      v-icon.blue.white--text mdi-lock-open-variant
+                    v-list-item-content
+                      v-list-item-title {{ $t(`admin:ssl.httpPort`) }}
+                      v-list-item-subtitle {{ info.httpPort }}
+                  template(v-if='info.httpsPort > 0')
+                    v-divider
+                    v-list-item
+                      v-list-item-avatar
+                        v-icon.green.white--text mdi-lock
+                      v-list-item-content
+                        v-list-item-title {{ $t(`admin:ssl.httpsPort`) }}
+                        v-list-item-subtitle {{ info.httpsPort }}
+                    v-divider
+                    v-list-item
+                      v-list-item-avatar
+                        v-icon.indigo.white--text mdi-sign-direction
+                      v-list-item-content
+                        v-list-item-title {{ $t(`admin:ssl.httpPortRedirect`) }}
+                        v-list-item-subtitle {{ info.httpRedirection }}
+                      v-list-item-action
+                        v-btn.red--text(
+                          v-if='info.httpRedirection'
+                          depressed
+                          :color='$vuetify.theme.dark ? `red darken-4` : `red lighten-5`'
+                          :class='$vuetify.theme.dark ? `text--lighten-5` : `text--darken-2`'
+                          @click='toggleRedir'
+                          :loading='loadingRedir'
                           )
-                      v-col(cols='6')
-                        v-text-field(
-                          outlined
-                          :label='$t(`admin:ssl.httpsPort`)'
-                          v-model='config.httpsPort'
-                          prepend-icon='mdi-lock'
-                          :hint='$t(`admin:ssl.httpsPortHint`)'
-                          persistent-hint
-                          :disabled='config.provider === ``'
+                          v-icon(left) mdi-power
+                          span {{$t('admin:ssl.httpPortRedirectTurnOff')}}
+                        v-btn.green--text(
+                          v-else
+                          depressed
+                          :color='$vuetify.theme.dark ? `green darken-4` : `green lighten-5`'
+                          :class='$vuetify.theme.dark ? `text--lighten-5` : `text--darken-2`'
+                          @click='toggleRedir'
+                          :loading='loadingRedir'
                           )
-                  v-card-text.grey(:class='$vuetify.theme.dark ? `darken-4-l5` : `lighten-4`')
-                    .caption {{$t(`admin:ssl.writableConfigFileWarning`)}}
+                          v-icon(left) mdi-power
+                          span {{$t('admin:ssl.httpPortRedirectTurnOn')}}
+
+    v-dialog(
+      v-model='loadingRenew'
+      persistent
+      max-width='450'
+      )
+      v-card(color='black', dark)
+        v-card-text.pa-10.text-center
+          semipolar-spinner.animated.fadeIn(
+            :animation-duration='1500'
+            :size='65'
+            color='#FFF'
+            style='margin: 0 auto;'
+          )
+          .mt-5.body-1.white--text {{$t('admin:ssl.renewCertificateLoadingTitle')}}
+          .caption.mt-4 {{$t('admin:ssl.renewCertificateLoadingSubtitle')}}
 
 </template>
 
 <script>
 import _ from 'lodash'
-import siteConfigQuery from 'gql/admin/site/site-query-config.gql'
-import siteUpdateConfigMutation from 'gql/admin/site/site-mutation-save-config.gql'
+import gql from 'graphql-tag'
+
+import { SemipolarSpinner } from 'epic-spinners'
 
 export default {
+  components: {
+    SemipolarSpinner
+  },
   data() {
     return {
-      config: {
-        provider: '',
-        domain: '',
-        httpPort: 3000,
-        httpPortRedirect: true,
-        httpsPort: 443
+      loadingRenew: false,
+      loadingRedir: false,
+      info: {
+        sslDomain: '',
+        sslProvider: '',
+        sslSubscriberEmail: '',
+        sslExpirationDate: false,
+        sslStatus: '',
+        httpPort: 0,
+        httpRedirection: false,
+        httpsPort: 0
       }
     }
   },
   computed: {
-    providers () {
-      return [
-        { text: this.$t('admin:ssl.providerDisabled'), value: '' },
-        { text: this.$t('admin:ssl.providerLetsEncrypt'), value: 'letsencrypt' },
-        { text: this.$t('admin:ssl.providerCustomCertificate'), value: 'custom' }
-      ]
+    providerTitle () {
+      switch (this.info.sslProvider) {
+        case 'custom':
+          return this.$t('admin:ssl.providerCustomCertificate')
+        case 'letsencrypt':
+          return this.$t('admin:ssl.providerLetsEncrypt')
+        default:
+          return this.$t('admin:ssl.providerDisabled')
+      }
     }
   },
   methods: {
-    async save () {
+    async toggleRedir () {
+      this.loadingRedir = true
       try {
+        this.info.httpRedirection = !this.info.httpRedirection
         await this.$apollo.mutate({
-          mutation: siteUpdateConfigMutation,
+          mutation: gql`
+            mutation ($enabled: Boolean!) {
+              system {
+                setHTTPSRedirection(enabled: $enabled) {
+                  responseResult {
+                    succeeded
+                    errorCode
+                    slug
+                    message
+                  }
+                }
+              }
+            }
+          `,
           variables: {
-            host: _.get(this.config, 'host', ''),
-            title: _.get(this.config, 'title', ''),
-            description: _.get(this.config, 'description', ''),
-            robots: _.get(this.config, 'robots', []),
-            analyticsService: _.get(this.config, 'analyticsService', ''),
-            analyticsId: _.get(this.config, 'analyticsId', ''),
-            company: _.get(this.config, 'company', ''),
-            hasLogo: _.get(this.config, 'hasLogo', false),
-            logoIsSquare: _.get(this.config, 'logoIsSquare', false),
-            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', '')
+            enabled: _.get(this.info, 'httpRedirection', false)
           },
           watchLoading (isLoading) {
-            this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-site-update')
+            this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-ssl-toggleRedirection')
           }
         })
         this.$store.commit('showNotification', {
           style: 'success',
-          message: 'Configuration saved successfully.',
+          message: this.$t('admin:ssl.httpPortRedirectSaveSuccess'),
           icon: 'check'
         })
-        this.siteTitle = this.config.title
-        this.company = this.config.company
       } catch (err) {
+        this.info.httpRedirection = !this.info.httpRedirection
         this.$store.commit('pushGraphError', err)
       }
+      this.loadingRedir = false
+    },
+    async renewCertificate () {
+      this.loadingRenew = true
+      try {
+        const respRaw = await this.$apollo.mutate({
+          mutation: gql`
+            mutation {
+              system {
+                renewHTTPSCertificate {
+                  responseResult {
+                    succeeded
+                    errorCode
+                    slug
+                    message
+                  }
+                }
+              }
+            }
+          `,
+          watchLoading (isLoading) {
+            this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-ssl-renew')
+          }
+        })
+        const resp = _.get(respRaw, 'data.system.renewHTTPSCertificate.responseResult', {})
+        if (resp.succeeded) {
+          this.$store.commit('showNotification', {
+            style: 'success',
+            message: this.$t('admin:ssl.renewCertificateSuccess'),
+            icon: 'check'
+          })
+        } else {
+          throw new Error(resp.message)
+        }
+      } catch (err) {
+        this.$store.commit('pushGraphError', err)
+      }
+      this.loadingRenew = false
+    }
+  },
+  apollo: {
+    info: {
+      query: gql`
+        {
+          system {
+            info {
+              httpPort
+              httpRedirection
+              httpsPort
+              sslDomain
+              sslExpirationDate
+              sslProvider
+              sslStatus
+              sslSubscriberEmail
+            }
+          }
+        }
+      `,
+      fetchPolicy: 'network-only',
+      update: (data) => _.cloneDeep(data.system.info),
+      watchLoading (isLoading) {
+        this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-ssl-refresh')
+      }
     }
   }
-  // apollo: {
-  //   config: {
-  //     query: siteConfigQuery,
-  //     fetchPolicy: 'network-only',
-  //     update: (data) => _.cloneDeep(data.site.config),
-  //     watchLoading (isLoading) {
-  //       this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-site-refresh')
-  //     }
-  //   }
-  // }
 }
 </script>
 

+ 4 - 4
package.json

@@ -73,7 +73,7 @@
     "express": "4.17.1",
     "express-brute": "1.0.1",
     "express-session": "1.17.0",
-    "file-type": "13.1.0",
+    "file-type": "13.1.1",
     "filesize": "6.0.1",
     "fs-extra": "8.1.0",
     "getos": "3.1.1",
@@ -121,7 +121,7 @@
     "node-2fa": "1.1.2",
     "node-cache": "5.1.0",
     "nodemailer": "6.4.2",
-    "objection": "1.6.11",
+    "objection": "2.1.2",
     "passport": "0.4.1",
     "passport-auth0": "1.3.1",
     "passport-azure-ad": "4.2.1",
@@ -158,7 +158,7 @@
     "scim-query-filter-parser": "2.0.4",
     "semver": "7.1.1",
     "serve-favicon": "2.5.0",
-    "simple-git": "1.129.0",
+    "simple-git": "1.130.0",
     "solr-node": "1.2.1",
     "sqlite3": "4.1.1",
     "ssh2": "0.8.7",
@@ -188,7 +188,7 @@
     "@babel/plugin-syntax-import-meta": "^7.8.3",
     "@babel/polyfill": "^7.8.3",
     "@babel/preset-env": "^7.8.3",
-    "@mdi/font": "4.7.95",
+    "@mdi/font": "4.8.95",
     "@panter/vue-i18next": "0.15.1",
     "@requarks/ckeditor5": "12.4.0-wiki.14",
     "@vue/babel-preset-app": "4.1.2",

+ 2 - 0
server/app/data.yml

@@ -54,6 +54,8 @@ defaults:
       securityHSTSDuration: 300
       securityCSP: false
       securityCSPDirectives: ''
+    server:
+      sslRedir: false
     flags:
       ldapdebug: false
       sqllog: false

+ 13 - 0
server/controllers/letsencrypt.js → server/controllers/ssl.js

@@ -1,6 +1,7 @@
 const express = require('express')
 const router = express.Router()
 const _ = require('lodash')
+const qs = require('querystring')
 
 /* global WIKI */
 
@@ -22,4 +23,16 @@ router.get('/.well-known/acme-challenge/:token', (req, res, next) => {
   }
 })
 
+/**
+ * Redirect to HTTPS if HTTP Redirection is enabled
+ */
+router.all('/*', (req, res, next) => {
+  if (WIKI.config.server.sslRedir && !req.secure && WIKI.servers.servers.https) {
+    let query = (!_.isEmpty(req.query)) ? `?${qs.stringify(req.query)}` : ``
+    return res.redirect(`https://${req.hostname}${req.originalUrl}${query}`)
+  } else {
+    next()
+  }
+})
+
 module.exports = router

+ 35 - 5
server/core/servers.js

@@ -46,7 +46,7 @@ module.exports = {
     })
 
     this.servers.http.on('connection', conn => {
-      let connKey = `${conn.remoteAddress}:${conn.remotePort}`
+      let connKey = `http:${conn.remoteAddress}:${conn.remotePort}`
       this.connections.set(connKey, conn)
       conn.on('close', () => {
         this.connections.delete(connKey)
@@ -108,7 +108,7 @@ module.exports = {
     })
 
     this.servers.https.on('connection', conn => {
-      let connKey = `${conn.remoteAddress}:${conn.remotePort}`
+      let connKey = `https:${conn.remoteAddress}:${conn.remotePort}`
       this.connections.set(connKey, conn)
       conn.on('close', () => {
         this.connections.delete(connKey)
@@ -135,11 +135,17 @@ module.exports = {
   /**
    * Close all active connections
    */
-  closeConnections () {
-    for (const conn of this.connections.values()) {
+  closeConnections (mode = 'all') {
+    for (const [key, conn] of this.connections) {
+      if (mode !== `all` && key.indexOf(`${mode}:`) !== 0) {
+        continue
+      }
       conn.destroy()
+      this.connections.delete(key)
+    }
+    if (mode === 'all') {
+      this.connections.clear()
     }
-    this.connections.clear()
   },
   /**
    * Stop all servers
@@ -155,5 +161,29 @@ module.exports = {
       this.servers.https = null
     }
     this.servers.graph = null
+  },
+  /**
+   * Restart Server
+   */
+  async restartServer (srv = 'https') {
+    this.closeConnections(srv)
+    switch (srv) {
+      case 'http':
+        if (this.servers.http) {
+          await Promise.fromCallback(cb => { this.servers.http.close(cb) })
+          this.servers.http = null
+        }
+        this.startHTTP()
+        break
+      case 'https':
+        if (this.servers.https) {
+          await Promise.fromCallback(cb => { this.servers.https.close(cb) })
+          this.servers.https = null
+        }
+        this.startHTTPS()
+        break
+      default:
+        throw new Error('Cannot restart server: Invalid designation')
+    }
   }
 }

+ 56 - 0
server/graph/resolvers/system.js

@@ -220,6 +220,38 @@ module.exports = {
       } catch (err) {
         return graphHelper.generateError(err)
       }
+    },
+    /**
+     * Set HTTPS Redirection State
+     */
+    async setHTTPSRedirection (obj, args, context) {
+      _.set(WIKI.config, 'server.sslRedir', args.enabled)
+      await WIKI.configSvc.saveToDb(['server'])
+      return {
+        responseResult: graphHelper.generateSuccess('HTTP Redirection state set successfully.')
+      }
+    },
+    /**
+     * Renew SSL Certificate
+     */
+    async renewHTTPSCertificate (obj, args, context) {
+      try {
+        if (!WIKI.config.ssl.enabled) {
+          throw new WIKI.Error.SystemSSLDisabled()
+        } else if (WIKI.config.ssl.provider !== `letsencrypt`) {
+          throw new WIKI.Error.SystemSSLRenewInvalidProvider()
+        } else if (!WIKI.servers.le) {
+          throw new WIKI.Error.SystemSSLLEUnavailable()
+        } else {
+          await WIKI.servers.le.requestCertificate()
+          await WIKI.servers.restartServer('https')
+          return {
+            responseResult: graphHelper.generateSuccess('SSL Certificate renewed successfully.')
+          }
+        }
+      } catch (err) {
+        return graphHelper.generateError(err)
+      }
     }
   },
   SystemInfo: {
@@ -266,6 +298,15 @@ module.exports = {
     hostname () {
       return os.hostname()
     },
+    httpPort () {
+      return WIKI.servers.servers.http ? _.get(WIKI.servers.servers.http.address(), 'port', 0) : 0
+    },
+    httpRedirection () {
+      return _.get(WIKI.config, 'server.sslRedir', false)
+    },
+    httpsPort () {
+      return WIKI.servers.servers.https ? _.get(WIKI.servers.servers.https.address(), 'port', 0) : 0
+    },
     latestVersion () {
       return WIKI.system.updates.version
     },
@@ -293,6 +334,21 @@ module.exports = {
     ramTotal () {
       return filesize(os.totalmem())
     },
+    sslDomain () {
+      return WIKI.config.ssl.enabled && WIKI.config.ssl.provider === `letsencrypt` ? WIKI.config.ssl.domain : null
+    },
+    sslExpirationDate () {
+      return WIKI.config.ssl.enabled && WIKI.config.ssl.provider === `letsencrypt` ? _.get(WIKI.config.letsencrypt, 'payload.expires', null) : null
+    },
+    sslProvider () {
+      return WIKI.config.ssl.enabled ? WIKI.config.ssl.provider : null
+    },
+    sslStatus () {
+      return 'OK'
+    },
+    sslSubscriberEmail () {
+      return WIKI.config.ssl.enabled && WIKI.config.ssl.provider === `letsencrypt` ? WIKI.config.ssl.subscriberEmail : null
+    },
     telemetry () {
       return WIKI.telemetry.enabled
     },

+ 14 - 0
server/graph/schemas/system.graphql

@@ -40,6 +40,12 @@ type SystemMutation {
     mongoDbConnString: String!
     groupMode: SystemImportUsersGroupMode!
   ): SystemImportUsersResponse @auth(requires:  ["manage:system"])
+
+  setHTTPSRedirection(
+    enabled: Boolean!
+  ): DefaultResponse @auth(requires: ["manage:system"])
+
+  renewHTTPSCertificate: DefaultResponse @auth(requires: ["manage:system"])
 }
 
 # -----------------------------------------------
@@ -65,6 +71,9 @@ type SystemInfo {
   dbVersion: String @auth(requires: ["manage:system"])
   groupsTotal: Int @auth(requires: ["manage:system", "manage:navigation", "manage:groups", "write:groups", "manage:users", "write:users"])
   hostname: String @auth(requires: ["manage:system"])
+  httpPort: Int @auth(requires: ["manage:system"])
+  httpRedirection: Boolean @auth(requires: ["manage:system"])
+  httpsPort: Int @auth(requires: ["manage:system"])
   latestVersion: String @auth(requires: ["manage:system"])
   latestVersionReleaseDate: Date @auth(requires: ["manage:system"])
   nodeVersion: String @auth(requires: ["manage:system"])
@@ -72,6 +81,11 @@ type SystemInfo {
   pagesTotal: Int @auth(requires: ["manage:system", "manage:navigation", "manage:pages", "delete:pages"])
   platform: String @auth(requires: ["manage:system"])
   ramTotal: String @auth(requires: ["manage:system"])
+  sslDomain: String @auth(requires: ["manage:system"])
+  sslExpirationDate: Date @auth(requires: ["manage:system"])
+  sslProvider: String @auth(requires: ["manage:system"])
+  sslStatus: String @auth(requires: ["manage:system"])
+  sslSubscriberEmail: String @auth(requires: ["manage:system"])
   telemetry: Boolean @auth(requires: ["manage:system"])
   telemetryClientId: String @auth(requires: ["manage:system"])
   upgradeCapable: Boolean @auth(requires: ["manage:system"])

+ 16 - 0
server/helpers/error.js

@@ -165,6 +165,22 @@ module.exports = {
     message: 'An unexpected error occured during search operation.',
     code: 4001
   }),
+  SystemGenericError: CustomError('SystemGenericError', {
+    message: 'An unexpected error occured.',
+    code: 7001
+  }),
+  SystemSSLDisabled: CustomError('SystemSSLDisabled', {
+    message: 'SSL is not enabled.',
+    code: 7002
+  }),
+  SystemSSLLEUnavailable: CustomError('SystemSSLLEUnavailable', {
+    message: 'Let\'s Encrypt is not initialized.',
+    code: 7004
+  }),
+  SystemSSLRenewInvalidProvider: CustomError('SystemSSLRenewInvalidProvider', {
+    message: 'Current provider does not support SSL certificate renewal.',
+    code: 7003
+  }),
   UserCreationFailed: CustomError('UserCreationFailed', {
     message: 'An unexpected error occured during user creation.',
     code: 1009

+ 2 - 2
server/master.js

@@ -59,10 +59,10 @@ module.exports = async () => {
   }))
 
   // ----------------------------------------
-  // Let's Encrypt Challenge
+  // SSL Handlers
   // ----------------------------------------
 
-  app.use('/', ctrl.letsencrypt)
+  app.use('/', ctrl.ssl)
 
   // ----------------------------------------
   // Passport Authentication

+ 25 - 21
yarn.lock

@@ -2052,10 +2052,10 @@
   resolved "https://registry.yarnpkg.com/@log4js-node/log4js-api/-/log4js-api-1.0.2.tgz#7a8143fb33f077df3e579dca7f18fea74a02ec8b"
   integrity sha512-6SJfx949YEWooh/CUPpJ+F491y4BYJmknz4hUN1+RHvKoUEynKbRmhnwbk/VLmh4OthLLDNCyWXfbh4DG1cTXA==
 
-"@mdi/font@4.7.95":
-  version "4.7.95"
-  resolved "https://registry.yarnpkg.com/@mdi/font/-/font-4.7.95.tgz#46fddf35aad64dd623a8b1837f78ca4ed7bc48b1"
-  integrity sha512-/SWooHIFz2dXkQJk3VhEXSbBplOU1lIkGSELAmw0peFEgR8KPqyM//M3vD8WDZETuEOSRVhVqLevP3okrsM5dw==
+"@mdi/font@4.8.95":
+  version "4.8.95"
+  resolved "https://registry.yarnpkg.com/@mdi/font/-/font-4.8.95.tgz#e026255815fbac2d5155fdf5cd3069b8dc599900"
+  integrity sha512-mfEjd6kkuheZ15CBU7g/q+De9+dah/SEgVH0uZsgCJTSYa+CkXIen35aNyHoixgcEfPV4Or0NLJvyYM5CXUnbQ==
 
 "@opencensus/web-types@0.0.7":
   version "0.0.7"
@@ -5821,6 +5821,11 @@ date-utils@*:
   resolved "https://registry.yarnpkg.com/date-utils/-/date-utils-1.2.21.tgz#61fb16cdc1274b3c9acaaffe9fc69df8720a2b64"
   integrity sha1-YfsWzcEnSzyayq/+n8ad+HIKK2Q=
 
+db-errors@^0.2.3:
+  version "0.2.3"
+  resolved "https://registry.yarnpkg.com/db-errors/-/db-errors-0.2.3.tgz#a6a38952e00b20e790f2695a6446b3c65497ffa2"
+  integrity sha512-OOgqgDuCavHXjYSJoV2yGhv6SeG8nk42aoCSoyXLZUH7VwFG27rxbavU1z+VrZbZjphw5UkDQwUlD21MwZpUng==
+
 de-indent@^1.0.2:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/de-indent/-/de-indent-1.0.2.tgz#b2038e846dc33baa5796128d0804b455b8c1e21d"
@@ -6970,13 +6975,13 @@ file-loader@5.0.2:
     loader-utils "^1.2.3"
     schema-utils "^2.5.0"
 
-file-type@13.1.0:
-  version "13.1.0"
-  resolved "https://registry.yarnpkg.com/file-type/-/file-type-13.1.0.tgz#5cfeade4745fad9504bb1435b5b5003af272b5a8"
-  integrity sha512-nr4fSvwYSlQl7YmaWS8rsvDrAm6VgCeb2ysHh18+YBSH4RxewhPKUQrj2XRuEMBNnH6E4xw+yWTL7+jiMrh6GA==
+file-type@13.1.1:
+  version "13.1.1"
+  resolved "https://registry.yarnpkg.com/file-type/-/file-type-13.1.1.tgz#439ceea3a50a3929b21d18d9c1c77fd7f2a63aa8"
+  integrity sha512-HEb3tepyq8KzKSFEGMJSIxqn8uC1n3AM8OKME5+BIgq0bErRzcDPOdmnyPKtfjStSpIvuk0Rle8mvuG4824caQ==
   dependencies:
     readable-web-to-node-stream "^2.0.0"
-    strtok3 "^5.0.1"
+    strtok3 "^5.0.2"
     token-types "^2.0.0"
     typedarray-to-buffer "^3.1.5"
 
@@ -10648,14 +10653,13 @@ object.values@^1.1.0:
     function-bind "^1.1.1"
     has "^1.0.3"
 
-objection@1.6.11:
-  version "1.6.11"
-  resolved "https://registry.yarnpkg.com/objection/-/objection-1.6.11.tgz#6755c15300277eee76c44faf4295704d8e2e02e2"
-  integrity sha512-/W6iR6+YvFg1U4k5DyX1MrY+xqodDM8AAOU1J0b3HlptsNw8V3uDHjZgTi1cFPPe5+ZeTTMvhIFhNiUP6+nqYQ==
+objection@2.1.2:
+  version "2.1.2"
+  resolved "https://registry.yarnpkg.com/objection/-/objection-2.1.2.tgz#da42e743ef69eabbb29bbf50efbe6f49acc854ba"
+  integrity sha512-55tkg1C8iFfsEz07N3peKXU4COve+jRTaUlSXAGlcC2Kk/7ONntREnwErQp+0483g7eexEbc8EJ7CDPu/RmYoQ==
   dependencies:
-    ajv "^6.10.0"
-    bluebird "^3.5.5"
-    lodash "^4.17.11"
+    ajv "^6.10.2"
+    db-errors "^0.2.3"
 
 offline-plugin@5.0.7:
   version "5.0.7"
@@ -13813,10 +13817,10 @@ signal-exit@^3.0.0, signal-exit@^3.0.2:
   resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d"
   integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=
 
-simple-git@1.129.0:
-  version "1.129.0"
-  resolved "https://registry.yarnpkg.com/simple-git/-/simple-git-1.129.0.tgz#eddd2611d2bf41c77e1d08cd70c0b7f3af785040"
-  integrity sha512-XbzNmugMTeV2crZnPl+b1ZJn+nqXCUNyrZxDXpLM0kHL3B85sbPlpd8q9I4qtAHI9D2FxTB6w4BuiAGKYtyzKw==
+simple-git@1.130.0:
+  version "1.130.0"
+  resolved "https://registry.yarnpkg.com/simple-git/-/simple-git-1.130.0.tgz#b689c4163bc021df563a81f256de54482005195d"
+  integrity sha512-gQsPA1uuAkGUa6S+yG4NRknKHVEV+Vnp437w8dJpDpzjtEH566WRSz5z6DoIxlBFaLC7Xwypznsuf1S/J0gtFg==
   dependencies:
     debug "^4.0.1"
 
@@ -14313,7 +14317,7 @@ striptags@3.1.1:
   resolved "https://registry.yarnpkg.com/striptags/-/striptags-3.1.1.tgz#c8c3e7fdd6fb4bb3a32a3b752e5b5e3e38093ebd"
   integrity sha1-yMPn/db7S7OjKjt1LltePjgJPr0=
 
-strtok3@^5.0.1:
+strtok3@^5.0.2:
   version "5.0.2"
   resolved "https://registry.yarnpkg.com/strtok3/-/strtok3-5.0.2.tgz#bb81f1f56742e16f1a30ccce5dc3d9498aa5475a"
   integrity sha512-EFeVpFC5qDsqPEJSrIYyS/ueFBknGhgSK9cW+YAJF/cgJG/KSjoK7X6rK5xnpcLe7y1LVkVFCXWbAb+ClNKzKQ==