Browse Source

feat: vue-apollo + auth providers resolver (wip)

NGPixel 7 years ago
parent
commit
f5fb21aaba

+ 13 - 28
client/app.js

@@ -10,10 +10,9 @@ import VueClipboards from 'vue-clipboards'
 import VueSimpleBreakpoints from 'vue-simple-breakpoints'
 import VeeValidate from 'vee-validate'
 import { ApolloClient } from 'apollo-client'
-import { ApolloLink } from 'apollo-link'
-import { createApolloFetch } from 'apollo-fetch'
 import { BatchHttpLink } from 'apollo-link-batch-http'
 import { InMemoryCache } from 'apollo-cache-inmemory'
+import VueApollo from 'vue-apollo'
 import Vuetify from 'vuetify'
 import Velocity from 'velocity-animate'
 import Hammer from 'hammerjs'
@@ -36,7 +35,7 @@ import helpers from './helpers'
 // Initialize Global Vars
 // ====================================
 
-window.wiki = null
+window.WIKI = null
 window.boot = boot
 window.CONSTANTS = CONSTANTS
 window.Hammer = Hammer
@@ -47,31 +46,11 @@ window.Hammer = Hammer
 
 const graphQLEndpoint = window.location.protocol + '//' + window.location.host + siteConfig.path + 'graphql'
 
-const apolloFetch = createApolloFetch({
-  uri: graphQLEndpoint,
-  constructOptions: (requestOrRequests, options) => ({
-    ...options,
-    method: 'POST',
-    body: JSON.stringify(requestOrRequests),
-    credentials: 'include'
-  })
-})
-
 window.graphQL = new ApolloClient({
-  link: ApolloLink.from([
-    new ApolloLink((operation, forward) => {
-      operation.setContext({
-        headers: {
-          'Content-Type': 'application/json'
-        }
-      })
-
-      return forward(operation)
-    }),
-    new BatchHttpLink({
-      fetch: apolloFetch
-    })
-  ]),
+  link: new BatchHttpLink({
+    uri: graphQLEndpoint,
+    credentials: 'include'
+  }),
   cache: new InMemoryCache(),
   connectToDevTools: (process.env.node_env === 'development')
 })
@@ -81,6 +60,7 @@ window.graphQL = new ApolloClient({
 // ====================================
 
 Vue.use(VueRouter)
+Vue.use(VueApollo)
 Vue.use(VueClipboards)
 Vue.use(VueSimpleBreakpoints)
 Vue.use(localization.VueI18Next)
@@ -121,15 +101,20 @@ let bootstrap = () => {
     store.dispatch('startLoading')
   })
 
+  const apolloProvider = new VueApollo({
+    defaultClient: window.graphQL
+  })
+
   // ====================================
   // Bootstrap Vue
   // ====================================
 
   const i18n = localization.init()
-  window.wiki = new Vue({
+  window.WIKI = new Vue({
     el: '#app',
     components: {},
     mixins: [helpers],
+    provide: apolloProvider.provide(),
     store,
     i18n
   })

+ 3 - 0
client/components/admin-api.vue

@@ -5,6 +5,9 @@
       .subheading.grey--text Manage keys to access the API
     v-card
       v-card-title
+        v-btn(color='green', dark)
+          v-icon(left) power_settings_new
+          | Enable API
         v-btn(color='primary', dark)
           v-icon(left) add
           | New API Key

+ 11 - 21
client/components/admin-auth.vue

@@ -6,24 +6,12 @@
         .subheading.grey--text Configure the authentication settings of your wiki
       v-tabs(color='grey lighten-4', grow, slider-color='primary', show-arrows)
         v-tab(key='settings'): v-icon settings
-        v-tab(key='db') Local
-        v-tab(key='algolia') Auth0
-        v-tab(key='elasticsearch') Azure AD
-        v-tab(key='solr') Discord
-        v-tab(key='solr') Dropbox
-        v-tab(key='solr') Facebook
-        v-tab(key='solr') GitHub
-        v-tab(key='solr') Google
-        v-tab(key='solr') LDAP
-        v-tab(key='solr') Microsoft
-        v-tab(key='solr') OAuth2 Generic
-        v-tab(key='solr') Slack
-        v-tab(key='solr') Twitch
+        v-tab(v-for='provider in providers', :key='provider.key') {{ provider.title }}
 
         v-tab-item(key='settings')
           v-card.pa-3
             v-form
-              v-checkbox(v-for='(engine, n) in engines', v-model='auths', :key='n', :label='engine.text', :value='engine.value', color='primary')
+              v-checkbox(v-for='(provider, n) in providers', v-model='auths', :key='provider.key', :label='provider.title', :value='provider.key', color='primary')
               v-divider
               v-btn(color='primary')
                 v-icon(left) chevron_right
@@ -34,18 +22,20 @@
 </template>
 
 <script>
+/* global CONSTANTS */
+
 export default {
   data() {
     return {
-      engines: [
-        { text: 'Local', value: 'local' },
-        { text: 'Auth0', value: 'auth0' },
-        { text: 'Algolia', value: 'algolia' },
-        { text: 'Elasticsearch', value: 'elasticsearch' },
-        { text: 'Solr', value: 'solr' }
-      ],
+      providers: [],
       auths: ['local']
     }
+  },
+  apollo: {
+    providers: {
+      query: CONSTANTS.GRAPH.AUTHENTICATION.QUERY_PROVIDERS,
+      update: (data) => data.authentication.providers
+    }
   }
 }
 </script>

+ 1 - 1
client/components/nav-header.vue

@@ -4,7 +4,7 @@
     v-toolbar-title
       span.subheading Wiki.js
     v-spacer
-    v-progress-circular.mr-3(indeterminate, color='blue')
+    v-progress-circular.mr-3(indeterminate, color='blue', v-if='$apollo.loading')
     v-btn(icon)
       v-icon(color='grey') search
     v-btn(icon, @click.native='darkTheme = !darkTheme')

+ 19 - 9
client/constants/graphql.js

@@ -1,16 +1,26 @@
 import gql from 'graphql-tag'
 
 export default {
-  GQL_QUERY_AUTHENTICATION: gql`
-    query($mode: String!) {
-      authentication(mode:$mode) {
-        key
-        useForm
-        title
-        icon
+  AUTHENTICATION: {
+    QUERY_PROVIDERS: gql`
+      query {
+        authentication {
+          providers {
+            isEnabled
+            key
+            props
+            title
+            useForm
+            icon
+            config {
+              key
+              value
+            }
+          }
+        }
       }
-    }
-  `,
+    `
+  },
   GQL_QUERY_TRANSLATIONS: gql`
     query($locale: String!, $namespace: String!) {
       translations(locale:$locale, namespace:$namespace) {

+ 2 - 2
client/constants/index.js

@@ -1,5 +1,5 @@
-import GRAPHQL from './graphql'
+import GRAPH from './graphql'
 
 export default {
-  GRAPHQL
+  GRAPH
 }

+ 2 - 2
package.json

@@ -135,7 +135,6 @@
     "yargs": "11.0.0"
   },
   "devDependencies": {
-    "@glimpse/glimpse": "0.22.15",
     "@panter/vue-i18next": "0.9.1",
     "apollo-client-preset": "1.0.8",
     "apollo-fetch": "0.7.0",
@@ -198,6 +197,7 @@
     "vee-validate": "2.0.5",
     "velocity-animate": "1.5.1",
     "vue": "2.5.13",
+    "vue-apollo": "3.0.0-beta.4",
     "vue-clipboards": "1.2.2",
     "vue-codemirror": "4.0.3",
     "vue-hot-reload-api": "2.3.0",
@@ -211,7 +211,7 @@
     "vuex-persistedstate": "2.4.2",
     "webpack": "3.11.0",
     "webpack-bundle-analyzer": "2.11.1",
-    "webpack-dev-middleware": "3.0.0",
+    "webpack-dev-middleware": "2.0.3",
     "webpack-hot-middleware": "2.21.2",
     "webpack-merge": "4.1.2",
     "whatwg-fetch": "2.0.3",

+ 1 - 1
server/app/data.yml

@@ -31,7 +31,7 @@ defaults:
       public: false
       strategies:
         local:
-          enabled: true
+          isEnabled: true
           allowSelfRegister: false
     git:
       enabled: false

+ 15 - 11
server/core/auth.js

@@ -4,6 +4,7 @@ const _ = require('lodash')
 const passport = require('passport')
 const fs = require('fs-extra')
 const path = require('path')
+const autoload = require('auto-load')
 
 module.exports = {
   strategies: {},
@@ -31,26 +32,29 @@ module.exports = {
 
     // Load authentication strategies
 
-    _.forOwn(_.omitBy(WIKI.config.auth.strategies, s => s.enabled === false), (strategyConfig, strategyKey) => {
-      strategyConfig.callbackURL = `${WIKI.config.site.host}${WIKI.config.site.path}login/${strategyKey}/callback`
-      let strategy = require(`../modules/authentication/${strategyKey}`)
-      try {
-        strategy.init(passport, strategyConfig)
-      } catch (err) {
-        WIKI.logger.error(`Authentication Provider ${strategyKey}: [ FAILED ]`)
-        WIKI.logger.error(err)
+    const modules = _.values(autoload(path.join(WIKI.SERVERPATH, 'modules/authentication')))
+    _.forEach(modules, (strategy) => {
+      const strategyConfig = _.get(WIKI.config.auth.strategies, strategy.key, {})
+      strategyConfig.callbackURL = `${WIKI.config.site.host}${WIKI.config.site.path}login/${strategy.key}/callback`
+      if (strategyConfig.isEnabled) {
+        try {
+          strategy.init(passport, strategyConfig)
+        } catch (err) {
+          WIKI.logger.error(`Authentication Provider ${strategy.title}: [ FAILED ]`)
+          WIKI.logger.error(err)
+        }
       }
-      fs.readFile(path.join(WIKI.ROOTPATH, `assets/svg/auth-icon-${strategyKey}.svg`), 'utf8').then(iconData => {
+      fs.readFile(path.join(WIKI.ROOTPATH, `assets/svg/auth-icon-${strategy.key}.svg`), 'utf8').then(iconData => {
         strategy.icon = iconData
       }).catch(err => {
         if (err.code === 'ENOENT') {
           strategy.icon = '[missing icon]'
         } else {
-          WIKI.logger.error(err)
+          WIKI.logger.warn(err)
         }
       })
       this.strategies[strategy.key] = strategy
-      WIKI.logger.info(`Authentication Provider ${strategyKey}: [ OK ]`)
+      WIKI.logger.info(`Authentication Provider ${strategy.title}: [ OK ]`)
     })
 
     // Create Guest account for first-time

+ 9 - 18
server/graph/resolvers/authentication.js

@@ -13,28 +13,19 @@ module.exports = {
   },
   AuthenticationQuery: {
     providers(obj, args, context, info) {
-      switch (args.mode) {
-        case 'active':
-          let strategies = _.chain(WIKI.auth.strategies).map(str => {
-            return {
-              key: str.key,
-              title: str.title,
-              useForm: str.useForm
-            }
-          }).sortBy(['title']).value()
-          let localStrategy = _.remove(strategies, str => str.key === 'local')
-          return _.concat(localStrategy, strategies)
-        case 'all':
-
-          break
-        default:
-          return null
-      }
+      return _.chain(WIKI.auth.strategies).map(str => {
+        return {
+          isEnabled: true,
+          key: str.key,
+          title: str.title,
+          useForm: str.useForm
+        }
+      }).sortBy(['title']).value()
     }
   },
   AuthenticationProvider: {
     icon (ap, args) {
-      return fs.readFileAsync(path.join(WIKI.ROOTPATH, `assets/svg/auth-icon-${ap.key}.svg`), 'utf8').catch(err => {
+      return fs.readFile(path.join(WIKI.ROOTPATH, `assets/svg/auth-icon-${ap.key}.svg`), 'utf8').catch(err => {
         if (err.code === 'ENOENT') {
           return null
         }

+ 8 - 2
server/graph/schemas/authentication.graphql

@@ -10,7 +10,13 @@ type AuthenticationQuery {
   providers: [AuthenticationProvider]
 }
 
-type AuthenticationMutation
+type AuthenticationMutation {
+  updateProvider(
+    provider: String!
+    isEnabled: Boolean!
+    config: [KeyValuePairInput]
+  ): DefaultResponse
+}
 
 type AuthenticationProvider {
   isEnabled: Boolean!
@@ -19,5 +25,5 @@ type AuthenticationProvider {
   title: String!
   useForm: Boolean!
   icon: String
-  config: String
+  config: [KeyValuePair]
 }

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

@@ -29,6 +29,26 @@ interface Base {
 
 # TYPES
 
+type KeyValuePair {
+  key: String!
+  value: String!
+}
+input KeyValuePairInput {
+  key: String!
+  value: String!
+}
+
+type DefaultResponse {
+  operation: ResponseStatus
+}
+
+type ResponseStatus {
+  succeeded: Boolean!
+  code: Int!
+  slug: String!
+  message: String
+}
+
 type Comment implements Base {
   id: Int!
   createdAt: Date

+ 0 - 4
server/index.js

@@ -17,10 +17,6 @@ let WIKI = {
 }
 global.WIKI = WIKI
 
-// if (WIKI.IS_DEBUG) {
-//   require('@glimpse/glimpse').init()
-// }
-
 WIKI.configSvc.init()
 
 // ----------------------------------------

File diff suppressed because it is too large
+ 54 - 442
yarn.lock


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