feat: auth strategies over GraphQL + svg loading

This commit is contained in:
NGPixel 2017-09-30 23:47:14 -04:00
parent 51f5c236d6
commit 501f0a9a53
13 changed files with 165 additions and 28 deletions

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -0,0 +1,8 @@
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="64px" height="64px" viewBox="0 0 64 64" enable-background="new 0 0 64 64" xml:space="preserve">
<path d="M32,19c13.089,0,27-3.154,27-9S45.089,1,32,1S5,4.154,5,10S18.911,19,32,19z"/>
<path d="M32,41c13.089,0,27-3.154,27-9V14.436c-1.481,1.493-3.963,2.968-8.022,4.174C45.864,20.128,38.946,21,32,21
s-13.864-0.872-18.978-2.391C8.963,17.403,6.481,15.929,5,14.436V32C5,37.846,18.911,41,32,41z"/>
<path d="M32,63c13.089,0,27-3.154,27-9V36.436c-1.481,1.493-3.963,2.968-8.022,4.174C45.864,42.128,38.946,43,32,43
s-13.864-0.872-18.978-2.391C8.963,39.403,6.481,37.929,5,36.436V54C5,59.846,18.911,63,32,63z"/>
</svg>

After

Width:  |  Height:  |  Size: 729 B

View File

@ -3,6 +3,8 @@
/* global siteConfig */ /* global siteConfig */
/* eslint-disable no-new */ /* eslint-disable no-new */
import CONSTANTS from './constants'
import Vue from 'vue' import Vue from 'vue'
import VueResource from 'vue-resource' import VueResource from 'vue-resource'
import VueClipboards from 'vue-clipboards' import VueClipboards from 'vue-clipboards'
@ -54,11 +56,18 @@ import contentViewComponent from './pages/content-view.component.js'
import editorComponent from './components/editor.component.js' import editorComponent from './components/editor.component.js'
import sourceViewComponent from './pages/source-view.component.js' import sourceViewComponent from './pages/source-view.component.js'
// ====================================
// Initialize Global Vars
// ====================================
window.wiki = null
window.CONSTANTS = CONSTANTS
// ==================================== // ====================================
// Initialize Apollo Client (GraphQL) // Initialize Apollo Client (GraphQL)
// ==================================== // ====================================
window.apollo = new ApolloClient({ window.graphQL = new ApolloClient({
networkInterface: createBatchingNetworkInterface({ networkInterface: createBatchingNetworkInterface({
uri: window.location.protocol + '//' + window.location.host + siteConfig.path + '/graphql' uri: window.location.protocol + '//' + window.location.host + siteConfig.path + '/graphql'
}), }),

View File

@ -7,11 +7,8 @@
| {{ error.title }} | {{ error.title }}
span {{ error.message }} span {{ error.message }}
.login-providers(v-show='strategies.length > 1') .login-providers(v-show='strategies.length > 1')
button.is-active(:title='$t("auth:providers.local")') button(v-for='strategy in strategies', :class='{ "is-active": strategy.key === selectedStrategy }', @click='selectStrategy(strategy.key, strategy.useForm)', :title='strategy.title')
i.nc-icon-outline.ui-1_database em(v-html='strategy.icon')
span {{ $t('auth:providers.local') }}
button(v-for='strategy in strategies', @onclick='selectProvider(strategy.key, strategy.useForm)', :title='strategy.title')
//-!= strategy.icon
span {{ strategy.title }} span {{ strategy.title }}
.login-frame .login-frame
h1 {{ siteTitle }} h1 {{ siteTitle }}
@ -32,7 +29,8 @@ export default {
data() { data() {
return { return {
error: false, error: false,
strategies: [] strategies: [],
selectedStrategy: 'local'
} }
}, },
computed: { computed: {
@ -41,9 +39,31 @@ export default {
} }
}, },
methods: { methods: {
selectProvider(key, useForm) { selectStrategy(key, useForm) {
this.selectedStrategy = key
if (!useForm) {
window.location.assign(siteConfig.path + '/login/' + key)
}
},
refreshStrategies() {
graphQL.query({
query: CONSTANTS.GRAPHQL.GQL_QUERY_AUTHENTICATION,
variables: {
mode: 'active'
}
}).then(resp => {
if (resp.data.authentication) {
this.strategies = resp.data.authentication
} else {
throw new Error('No authentication providers available!')
}
}).catch(err => {
console.error(err)
})
} }
},
mounted() {
this.refreshStrategies()
} }
} }
</script> </script>

View File

@ -0,0 +1,22 @@
import gql from 'graphql-tag'
export default {
GQL_QUERY_AUTHENTICATION: gql`
query($mode: String!) {
authentication(mode:$mode) {
key
useForm
title
icon
}
}
`,
GQL_QUERY_TRANSLATIONS: gql`
query($locale: String!, $namespace: String!) {
translations(locale:$locale, namespace:$namespace) {
key
value
}
}
`
}

View File

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

View File

@ -1,11 +1,10 @@
import i18next from 'i18next' import i18next from 'i18next'
import i18nextXHR from 'i18next-xhr-backend' import i18nextXHR from 'i18next-xhr-backend'
import i18nextCache from 'i18next-localstorage-cache' import i18nextCache from 'i18next-localstorage-cache'
import gql from 'graphql-tag'
import VueI18Next from '@panter/vue-i18next' import VueI18Next from '@panter/vue-i18next'
import loSet from 'lodash/set' import loSet from 'lodash/set'
/* global siteConfig */ /* global siteConfig, graphQL, CONSTANTS */
module.exports = { module.exports = {
VueI18Next, VueI18Next,
@ -19,16 +18,12 @@ module.exports = {
parse: (data) => data, parse: (data) => data,
ajax: (url, opts, cb, data) => { ajax: (url, opts, cb, data) => {
let langParams = url.split('/') let langParams = url.split('/')
console.info(langParams) graphQL.query({
window.apollo.query({ query: CONSTANTS.GRAPHQL.GQL_QUERY_TRANSLATIONS,
query: gql` variables: {
{ locale: langParams[0],
translations(locale:"${langParams[0]}", namespace:"${langParams[1]}") { namespace: langParams[1]
key }
value
}
}
`
}).then(resp => { }).then(resp => {
let ns = {} let ns = {}
if (resp.data.translations.length > 0) { if (resp.data.translations.length > 0) {

View File

@ -61,7 +61,11 @@
background-color: mc($color,'800'); background-color: mc($color,'800');
color: #FFF; color: #FFF;
animation: none; animation: none;
} }
&:focus {
box-shadow: inset 0 0 0 3px rgba(255,255,255, .4);
}
} }
} }

View File

@ -35,6 +35,7 @@
.login-frame { .login-frame {
border-radius: 0 6px 6px 0; border-radius: 0 6px 6px 0;
border-left: none;
} }
} }
@ -104,6 +105,10 @@
align-items: center; align-items: center;
transition: all .4s ease; transition: all .4s ease;
&:focus {
outline: none;
}
@include until($tablet) { @include until($tablet) {
justify-content: center; justify-content: center;
} }
@ -114,12 +119,24 @@
&:first-child { &:first-child {
border-top: none; border-top: none;
&.is-active {
border-top: 1px solid rgba(255,255,255, .5);
}
} }
&.is-active { &.is-active {
background-color: mc('grey', '100'); background-image: linear-gradient(to right, rgba(255,255,255,1) 0%,rgba(255,255,255,.77) 100%);
background-image: radial-gradient(circle at top left, rgba(mc('grey', '200'),1) 0%,rgba(255,255,255,1) 100%);
color: mc('light-blue', '700'); color: mc('light-blue', '700');
cursor: default;
&:hover {
background-color: transparent;
}
svg path {
fill: mc('light-blue', '800');
}
} }
i { i {
@ -160,7 +177,8 @@
} }
&-frame { &-frame {
background-image: radial-gradient(circle at top left, rgba(255,255,255,1) 5%,rgba(240,240,240,.6) 100%); background-image: radial-gradient(circle at top center, rgba(255,255,255,1) 5%,rgba(255,255,255,.6) 100%);
border: 1px solid rgba(255,255,255, .5);
border-radius: 6px; border-radius: 6px;
width: 400px; width: 400px;
padding: 1rem; padding: 1rem;
@ -178,6 +196,7 @@
font-size: 2rem; font-size: 2rem;
font-weight: 600; font-weight: 600;
color: mc('light-blue', '700'); color: mc('light-blue', '700');
text-shadow: 1px 1px 0 #FFF;
padding: 0; padding: 0;
margin: 0; margin: 0;
} }
@ -186,6 +205,7 @@
font-size: 1.5rem; font-size: 1.5rem;
font-weight: 300; font-weight: 300;
color: mc('grey', '700'); color: mc('grey', '700');
text-shadow: 1px 1px 0 #FFF;
padding: 0; padding: 0;
margin: 0 0 25px 0; margin: 0 0 25px 0;
} }
@ -200,6 +220,7 @@
border: 1px solid #FFF; border: 1px solid #FFF;
border-radius: 3px; border-radius: 3px;
background-color: rgba(255,255,255,.9); background-color: rgba(255,255,255,.9);
box-shadow: inset 0 0 0 3px rgba(255,255,255, .25);
padding: 0 15px; padding: 0 15px;
height: 40px; height: 40px;
margin: 0 0 10px 0; margin: 0 0 10px 0;
@ -212,6 +233,9 @@
&:focus { &:focus {
outline: none; outline: none;
border-color: mc('light-blue','500'); border-color: mc('light-blue','500');
background-color: rgba(255,255,255,1);
box-shadow: inset 0 0 0 3px rgba(mc('light-blue','500'), .25);
color: mc('light-blue', '800');
} }
} }

View File

@ -14,7 +14,7 @@ module.exports = {
useForm: false, useForm: false,
props: ['clientId', 'clientSecret', 'callbackURL'], props: ['clientId', 'clientSecret', 'callbackURL'],
init (passport, conf) { init (passport, conf) {
passport.use('windowslive', passport.use('microsoft',
new WindowsLiveStrategy({ new WindowsLiveStrategy({
clientID: conf.clientId, clientID: conf.clientId,
clientSecret: conf.clientSecret, clientSecret: conf.clientSecret,

View File

@ -10,6 +10,7 @@ const _ = require('lodash')
const typeDefs = fs.readFileSync(path.join(wiki.SERVERPATH, 'schemas/types.graphql'), 'utf8') const typeDefs = fs.readFileSync(path.join(wiki.SERVERPATH, 'schemas/types.graphql'), 'utf8')
const DateScalar = require('../schemas/scalar-date') const DateScalar = require('../schemas/scalar-date')
const AuthenticationResolvers = require('../schemas/resolvers-authentication')
const CommentResolvers = require('../schemas/resolvers-comment') const CommentResolvers = require('../schemas/resolvers-comment')
const DocumentResolvers = require('../schemas/resolvers-document') const DocumentResolvers = require('../schemas/resolvers-document')
const FileResolvers = require('../schemas/resolvers-file') const FileResolvers = require('../schemas/resolvers-file')
@ -21,6 +22,7 @@ const TranslationResolvers = require('../schemas/resolvers-translation')
const UserResolvers = require('../schemas/resolvers-user') const UserResolvers = require('../schemas/resolvers-user')
const resolvers = _.merge( const resolvers = _.merge(
AuthenticationResolvers,
CommentResolvers, CommentResolvers,
DocumentResolvers, DocumentResolvers,
FileResolvers, FileResolvers,

View File

@ -0,0 +1,40 @@
const _ = require('lodash')
const fs = require('fs-extra')
const path = require('path')
/* global wiki */
module.exports = {
Query: {
authentication(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
}
}
},
Mutation: {},
AuthenticationProvider: {
icon (ap, args) {
return fs.readFileAsync(path.join(wiki.ROOTPATH, `assets/svg/auth-icon-${ap.key}.svg`), 'utf8').catch(err => {
if (err.code === 'ENOENT') {
return null
}
throw err
})
}
}
}

View File

@ -32,10 +32,11 @@ interface Base {
# TYPES # TYPES
type AuthenticationProvider { type AuthenticationProvider {
id: String! key: String!
useForm: Boolean! useForm: Boolean!
title: String! title: String!
props: [String] props: [String]
icon: String
config: String config: String
} }