feat: GraphQL translations + login form
This commit is contained in:
+23
-20
@@ -6,10 +6,14 @@
|
|||||||
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'
|
||||||
|
import { ApolloClient, createBatchingNetworkInterface } from 'apollo-client'
|
||||||
import store from './store'
|
import store from './store'
|
||||||
import i18next from 'i18next'
|
|
||||||
import i18nextXHR from 'i18next-xhr-backend'
|
// ====================================
|
||||||
import VueI18Next from '@panter/vue-i18next'
|
// Load Modules
|
||||||
|
// ====================================
|
||||||
|
|
||||||
|
import localization from './modules/localization'
|
||||||
|
|
||||||
// ====================================
|
// ====================================
|
||||||
// Load Helpers
|
// Load Helpers
|
||||||
@@ -29,6 +33,7 @@ import editorFileComponent from './components/editor-file.vue'
|
|||||||
import editorVideoComponent from './components/editor-video.vue'
|
import editorVideoComponent from './components/editor-video.vue'
|
||||||
import historyComponent from './components/history.vue'
|
import historyComponent from './components/history.vue'
|
||||||
import loadingSpinnerComponent from './components/loading-spinner.vue'
|
import loadingSpinnerComponent from './components/loading-spinner.vue'
|
||||||
|
import loginComponent from './components/login.vue'
|
||||||
import modalCreatePageComponent from './components/modal-create-page.vue'
|
import modalCreatePageComponent from './components/modal-create-page.vue'
|
||||||
import modalCreateUserComponent from './components/modal-create-user.vue'
|
import modalCreateUserComponent from './components/modal-create-user.vue'
|
||||||
import modalDeleteUserComponent from './components/modal-delete-user.vue'
|
import modalDeleteUserComponent from './components/modal-delete-user.vue'
|
||||||
@@ -49,13 +54,24 @@ 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 Apollo Client (GraphQL)
|
||||||
|
// ====================================
|
||||||
|
|
||||||
|
window.apollo = new ApolloClient({
|
||||||
|
networkInterface: createBatchingNetworkInterface({
|
||||||
|
uri: window.location.protocol + '//' + window.location.host + siteConfig.path + '/graphql'
|
||||||
|
}),
|
||||||
|
connectToDevTools: true
|
||||||
|
})
|
||||||
|
|
||||||
// ====================================
|
// ====================================
|
||||||
// Initialize Vue Modules
|
// Initialize Vue Modules
|
||||||
// ====================================
|
// ====================================
|
||||||
|
|
||||||
Vue.use(VueResource)
|
Vue.use(VueResource)
|
||||||
Vue.use(VueClipboards)
|
Vue.use(VueClipboards)
|
||||||
Vue.use(VueI18Next)
|
Vue.use(localization.VueI18Next)
|
||||||
Vue.use(helpers)
|
Vue.use(helpers)
|
||||||
|
|
||||||
// ====================================
|
// ====================================
|
||||||
@@ -76,6 +92,7 @@ Vue.component('editorFile', editorFileComponent)
|
|||||||
Vue.component('editorVideo', editorVideoComponent)
|
Vue.component('editorVideo', editorVideoComponent)
|
||||||
Vue.component('history', historyComponent)
|
Vue.component('history', historyComponent)
|
||||||
Vue.component('loadingSpinner', loadingSpinnerComponent)
|
Vue.component('loadingSpinner', loadingSpinnerComponent)
|
||||||
|
Vue.component('login', loginComponent)
|
||||||
Vue.component('modalCreatePage', modalCreatePageComponent)
|
Vue.component('modalCreatePage', modalCreatePageComponent)
|
||||||
Vue.component('modalCreateUser', modalCreateUserComponent)
|
Vue.component('modalCreateUser', modalCreateUserComponent)
|
||||||
Vue.component('modalDeleteUser', modalDeleteUserComponent)
|
Vue.component('modalDeleteUser', modalDeleteUserComponent)
|
||||||
@@ -89,20 +106,6 @@ Vue.component('sourceView', sourceViewComponent)
|
|||||||
Vue.component('toggle', toggleComponent)
|
Vue.component('toggle', toggleComponent)
|
||||||
Vue.component('tree', treeComponent)
|
Vue.component('tree', treeComponent)
|
||||||
|
|
||||||
// ====================================
|
|
||||||
// Load Localization strings
|
|
||||||
// ====================================
|
|
||||||
|
|
||||||
i18next
|
|
||||||
.use(i18nextXHR)
|
|
||||||
.init({
|
|
||||||
backend: {
|
|
||||||
loadPath: siteConfig.path + '/js/i18n/{{lng}}.json'
|
|
||||||
},
|
|
||||||
lng: siteConfig.lang,
|
|
||||||
fallbackLng: siteConfig.lang
|
|
||||||
})
|
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', ev => {
|
document.addEventListener('DOMContentLoaded', ev => {
|
||||||
// ====================================
|
// ====================================
|
||||||
// Notifications
|
// Notifications
|
||||||
@@ -116,13 +119,13 @@ document.addEventListener('DOMContentLoaded', ev => {
|
|||||||
// Bootstrap Vue
|
// Bootstrap Vue
|
||||||
// ====================================
|
// ====================================
|
||||||
|
|
||||||
const i18n = new VueI18Next(i18next)
|
const i18n = localization.init()
|
||||||
window.wiki = new Vue({
|
window.wiki = new Vue({
|
||||||
mixins: [helpers],
|
mixins: [helpers],
|
||||||
components: {},
|
components: {},
|
||||||
store,
|
store,
|
||||||
i18n,
|
i18n,
|
||||||
el: '#root',
|
el: '#app',
|
||||||
methods: {
|
methods: {
|
||||||
changeTheme(opts) {
|
changeTheme(opts) {
|
||||||
this.$el.className = `has-stickynav is-primary-${opts.primary} is-alternate-${opts.alt}`
|
this.$el.className = `has-stickynav is-primary-${opts.primary} is-alternate-${opts.alt}`
|
||||||
|
|||||||
@@ -0,0 +1,50 @@
|
|||||||
|
<template lang="pug">
|
||||||
|
.login(:class='{ "is-error": error }')
|
||||||
|
.login-container(:class='{ "is-expanded": strategies.length > 1 }')
|
||||||
|
.login-error(v-if='error')
|
||||||
|
strong
|
||||||
|
i.icon-warning-outline
|
||||||
|
| {{ error.title }}
|
||||||
|
span {{ error.message }}
|
||||||
|
.login-providers(v-show='strategies.length > 1')
|
||||||
|
button.is-active(:title='$t("auth:providers.local")')
|
||||||
|
i.nc-icon-outline.ui-1_database
|
||||||
|
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 }}
|
||||||
|
.login-frame
|
||||||
|
h1 {{ siteTitle }}
|
||||||
|
h2 {{ $t('auth:loginrequired') }}
|
||||||
|
form(method='post', action='/login')
|
||||||
|
input#login-user(type='text', name='email', :placeholder='$t("auth:fields.emailuser")')
|
||||||
|
input#login-pass(type='password', name='password', :placeholder='$t("auth:fields.password")')
|
||||||
|
button.button.is-light-blue.is-fullwidth(type='submit')
|
||||||
|
span {{ $t('auth:actions.login') }}
|
||||||
|
.login-copyright
|
||||||
|
span {{ $t('footer.poweredby') }}
|
||||||
|
a(href='https://wiki.js.org', rel='external', title='Wiki.js') Wiki.js
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'login',
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
error: false,
|
||||||
|
strategies: []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
siteTitle() {
|
||||||
|
return siteConfig.title
|
||||||
|
}
|
||||||
|
}
|
||||||
|
methods: {
|
||||||
|
selectProvider(key, useForm) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
import i18next from 'i18next'
|
||||||
|
import i18nextXHR from 'i18next-xhr-backend'
|
||||||
|
import i18nextCache from 'i18next-localstorage-cache'
|
||||||
|
import gql from 'graphql-tag'
|
||||||
|
import VueI18Next from '@panter/vue-i18next'
|
||||||
|
import loSet from 'lodash/set'
|
||||||
|
|
||||||
|
/* global siteConfig */
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
VueI18Next,
|
||||||
|
init() {
|
||||||
|
i18next
|
||||||
|
.use(i18nextXHR)
|
||||||
|
.use(i18nextCache)
|
||||||
|
.init({
|
||||||
|
backend: {
|
||||||
|
loadPath: '{{lng}}/{{ns}}',
|
||||||
|
parse: (data) => data,
|
||||||
|
ajax: (url, opts, cb, data) => {
|
||||||
|
let langParams = url.split('/')
|
||||||
|
console.info(langParams)
|
||||||
|
window.apollo.query({
|
||||||
|
query: gql`
|
||||||
|
{
|
||||||
|
translations(locale:"${langParams[0]}", namespace:"${langParams[1]}") {
|
||||||
|
key
|
||||||
|
value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
}).then(resp => {
|
||||||
|
let ns = {}
|
||||||
|
if (resp.data.translations.length > 0) {
|
||||||
|
resp.data.translations.forEach(entry => {
|
||||||
|
loSet(ns, entry.key, entry.value)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return cb(ns, {status: '200'})
|
||||||
|
}).catch(err => {
|
||||||
|
console.error(err)
|
||||||
|
return cb(null, {status: '404'})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
cache: {
|
||||||
|
enabled: true,
|
||||||
|
expiration: 60 * 60 * 1000
|
||||||
|
},
|
||||||
|
defaultNS: 'common',
|
||||||
|
lng: siteConfig.lang,
|
||||||
|
fallbackLng: siteConfig.lang,
|
||||||
|
ns: ['common', 'admin', 'auth']
|
||||||
|
})
|
||||||
|
return new VueI18Next(i18next)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,10 +11,15 @@ [v-cloak], .is-hidden {
|
|||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
#root {
|
#app {
|
||||||
padding-bottom: 67px;
|
padding-bottom: 67px;
|
||||||
position: relative;
|
position: relative;
|
||||||
min-height: 100%;
|
min-height: 100%;
|
||||||
|
|
||||||
|
&.is-fullscreen {
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
|
|||||||
@@ -81,7 +81,6 @@ .login {
|
|||||||
|
|
||||||
border-right: none;
|
border-right: none;
|
||||||
border-radius: 6px 0 0 6px;
|
border-radius: 6px 0 0 6px;
|
||||||
background-color: mc('grey', '900');
|
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
@@ -94,8 +93,8 @@ .login {
|
|||||||
padding: 5px 15px;
|
padding: 5px 15px;
|
||||||
border: none;
|
border: none;
|
||||||
color: #FFF;
|
color: #FFF;
|
||||||
background-color: mc('light-blue', '800');
|
background: linear-gradient(to right, rgba(mc('light-blue', '800'), .7), rgba(mc('light-blue', '800'), 1));
|
||||||
border-top: 1px solid mc('light-blue', '900');
|
border-top: 1px solid rgba(mc('light-blue', '900'), .5);
|
||||||
font-family: $core-font-standard;
|
font-family: $core-font-standard;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
@@ -110,7 +109,7 @@ .login {
|
|||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: mc('light-blue', '600');
|
background-color: mc('light-blue', '900');
|
||||||
}
|
}
|
||||||
|
|
||||||
&:first-child {
|
&:first-child {
|
||||||
|
|||||||
+5
-2
@@ -53,6 +53,7 @@
|
|||||||
"connect-redis": "3.3.0",
|
"connect-redis": "3.3.0",
|
||||||
"cookie-parser": "1.4.3",
|
"cookie-parser": "1.4.3",
|
||||||
"diff2html": "2.3.0",
|
"diff2html": "2.3.0",
|
||||||
|
"dotize": "^0.2.0",
|
||||||
"execa": "0.8.0",
|
"execa": "0.8.0",
|
||||||
"express": "4.15.4",
|
"express": "4.15.4",
|
||||||
"express-brute": "1.0.1",
|
"express-brute": "1.0.1",
|
||||||
@@ -68,6 +69,7 @@
|
|||||||
"highlight.js": "9.12.0",
|
"highlight.js": "9.12.0",
|
||||||
"i18next": "9.0.0",
|
"i18next": "9.0.0",
|
||||||
"i18next-express-middleware": "1.0.5",
|
"i18next-express-middleware": "1.0.5",
|
||||||
|
"i18next-localstorage-cache": "^1.1.1",
|
||||||
"i18next-node-fs-backend": "1.0.0",
|
"i18next-node-fs-backend": "1.0.0",
|
||||||
"image-size": "0.6.1",
|
"image-size": "0.6.1",
|
||||||
"ioredis": "3.1.4",
|
"ioredis": "3.1.4",
|
||||||
@@ -105,7 +107,6 @@
|
|||||||
"passport-local": "1.0.0",
|
"passport-local": "1.0.0",
|
||||||
"passport-slack": "0.0.7",
|
"passport-slack": "0.0.7",
|
||||||
"passport-windowslive": "1.0.2",
|
"passport-windowslive": "1.0.2",
|
||||||
"passport.socketio": "3.7.0",
|
|
||||||
"pg": "7.3.0",
|
"pg": "7.3.0",
|
||||||
"pg-hstore": "2.3.2",
|
"pg-hstore": "2.3.2",
|
||||||
"pg-promise": "6.5.2",
|
"pg-promise": "6.5.2",
|
||||||
@@ -120,7 +121,6 @@
|
|||||||
"sequelize": "4.8.2",
|
"sequelize": "4.8.2",
|
||||||
"serve-favicon": "2.4.3",
|
"serve-favicon": "2.4.3",
|
||||||
"simplemde": "1.11.2",
|
"simplemde": "1.11.2",
|
||||||
"socket.io": "2.0.3",
|
|
||||||
"stopword": "0.1.7",
|
"stopword": "0.1.7",
|
||||||
"stream-to-promise": "2.2.0",
|
"stream-to-promise": "2.2.0",
|
||||||
"tar": "4.0.1",
|
"tar": "4.0.1",
|
||||||
@@ -133,6 +133,7 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@glimpse/glimpse": "0.22.15",
|
"@glimpse/glimpse": "0.22.15",
|
||||||
"@panter/vue-i18next": "0.5.1",
|
"@panter/vue-i18next": "0.5.1",
|
||||||
|
"apollo-client": "^1.9.3",
|
||||||
"babel-cli": "6.26.0",
|
"babel-cli": "6.26.0",
|
||||||
"babel-jest": "21.0.2",
|
"babel-jest": "21.0.2",
|
||||||
"babel-plugin-transform-object-assign": "6.22.0",
|
"babel-plugin-transform-object-assign": "6.22.0",
|
||||||
@@ -147,6 +148,7 @@
|
|||||||
"eslint-plugin-promise": "3.5.0",
|
"eslint-plugin-promise": "3.5.0",
|
||||||
"eslint-plugin-standard": "3.0.1",
|
"eslint-plugin-standard": "3.0.1",
|
||||||
"fuse-box": "^2.2.31",
|
"fuse-box": "^2.2.31",
|
||||||
|
"graphql-tag": "^2.4.2",
|
||||||
"i18next-xhr-backend": "1.4.2",
|
"i18next-xhr-backend": "1.4.2",
|
||||||
"jest": "21.0.2",
|
"jest": "21.0.2",
|
||||||
"jquery": "3.2.1",
|
"jquery": "3.2.1",
|
||||||
@@ -166,6 +168,7 @@
|
|||||||
"vue": "2.4.2",
|
"vue": "2.4.2",
|
||||||
"vue-clipboards": "1.1.0",
|
"vue-clipboards": "1.1.0",
|
||||||
"vue-lodash": "1.0.3",
|
"vue-lodash": "1.0.3",
|
||||||
|
"vue-material": "^0.7.5",
|
||||||
"vue-resource": "1.3.4",
|
"vue-resource": "1.3.4",
|
||||||
"vue-template-compiler": "2.4.2",
|
"vue-template-compiler": "2.4.2",
|
||||||
"vue-template-es2015-compiler": "1.5.3",
|
"vue-template-es2015-compiler": "1.5.3",
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ const AzureAdOAuth2Strategy = require('passport-azure-ad-oauth2').Strategy
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
key: 'azure',
|
key: 'azure',
|
||||||
title: 'Azure Active Directory',
|
title: 'Azure Active Directory',
|
||||||
|
useForm: false,
|
||||||
props: ['clientId', 'clientSecret', 'callbackURL', 'resource', 'tenant'],
|
props: ['clientId', 'clientSecret', 'callbackURL', 'resource', 'tenant'],
|
||||||
init (passport, conf) {
|
init (passport, conf) {
|
||||||
const jwt = require('jsonwebtoken')
|
const jwt = require('jsonwebtoken')
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ const FacebookStrategy = require('passport-facebook').Strategy
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
key: 'facebook',
|
key: 'facebook',
|
||||||
title: 'Facebook',
|
title: 'Facebook',
|
||||||
|
useForm: false,
|
||||||
props: ['clientId', 'clientSecret', 'callbackURL'],
|
props: ['clientId', 'clientSecret', 'callbackURL'],
|
||||||
init (passport, conf) {
|
init (passport, conf) {
|
||||||
passport.use('facebook',
|
passport.use('facebook',
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ const GitHubStrategy = require('passport-github2').Strategy
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
key: 'github',
|
key: 'github',
|
||||||
title: 'GitHub',
|
title: 'GitHub',
|
||||||
|
useForm: false,
|
||||||
props: ['clientId', 'clientSecret', 'callbackURL'],
|
props: ['clientId', 'clientSecret', 'callbackURL'],
|
||||||
init (passport, conf) {
|
init (passport, conf) {
|
||||||
passport.use('github',
|
passport.use('github',
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ const GoogleStrategy = require('passport-google-oauth20').Strategy
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
key: 'google',
|
key: 'google',
|
||||||
title: 'Google ID',
|
title: 'Google ID',
|
||||||
|
useForm: false,
|
||||||
props: ['clientId', 'clientSecret', 'callbackURL'],
|
props: ['clientId', 'clientSecret', 'callbackURL'],
|
||||||
init (passport, conf) {
|
init (passport, conf) {
|
||||||
passport.use('google',
|
passport.use('google',
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ const fs = require('fs')
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
key: 'ldap',
|
key: 'ldap',
|
||||||
title: 'LDAP / Active Directory',
|
title: 'LDAP / Active Directory',
|
||||||
|
useForm: true,
|
||||||
props: ['url', 'bindDn', 'bindCredentials', 'searchBase', 'searchFilter', 'tlsEnabled', 'tlsCertPath'],
|
props: ['url', 'bindDn', 'bindCredentials', 'searchBase', 'searchFilter', 'tlsEnabled', 'tlsCertPath'],
|
||||||
init (passport, conf) {
|
init (passport, conf) {
|
||||||
passport.use('ldapauth',
|
passport.use('ldapauth',
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ const LocalStrategy = require('passport-local').Strategy
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
key: 'local',
|
key: 'local',
|
||||||
title: 'Local',
|
title: 'Local',
|
||||||
|
useForm: true,
|
||||||
props: [],
|
props: [],
|
||||||
init (passport, conf) {
|
init (passport, conf) {
|
||||||
passport.use('local',
|
passport.use('local',
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ const WindowsLiveStrategy = require('passport-windowslive').Strategy
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
key: 'microsoft',
|
key: 'microsoft',
|
||||||
title: 'Microsoft Account',
|
title: 'Microsoft Account',
|
||||||
|
useForm: false,
|
||||||
props: ['clientId', 'clientSecret', 'callbackURL'],
|
props: ['clientId', 'clientSecret', 'callbackURL'],
|
||||||
init (passport, conf) {
|
init (passport, conf) {
|
||||||
passport.use('windowslive',
|
passport.use('windowslive',
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ const SlackStrategy = require('passport-slack').Strategy
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
key: 'slack',
|
key: 'slack',
|
||||||
title: 'Slack',
|
title: 'Slack',
|
||||||
|
useForm: false,
|
||||||
props: ['clientId', 'clientSecret', 'callbackURL'],
|
props: ['clientId', 'clientSecret', 'callbackURL'],
|
||||||
init (passport, conf) {
|
init (passport, conf) {
|
||||||
passport.use('slack',
|
passport.use('slack',
|
||||||
|
|||||||
+5
-47
@@ -20,7 +20,7 @@ module.exports = Promise.join(
|
|||||||
wiki.disk = require('./modules/disk').init()
|
wiki.disk = require('./modules/disk').init()
|
||||||
wiki.docs = require('./modules/documents').init()
|
wiki.docs = require('./modules/documents').init()
|
||||||
wiki.git = require('./modules/git').init(false)
|
wiki.git = require('./modules/git').init(false)
|
||||||
wiki.lang = require('i18next')
|
wiki.lang = require('./modules/localization').init()
|
||||||
wiki.mark = require('./modules/markdown')
|
wiki.mark = require('./modules/markdown')
|
||||||
wiki.search = require('./modules/search').init()
|
wiki.search = require('./modules/search').init()
|
||||||
wiki.upl = require('./modules/uploads').init()
|
wiki.upl = require('./modules/uploads').init()
|
||||||
@@ -37,13 +37,10 @@ module.exports = Promise.join(
|
|||||||
const favicon = require('serve-favicon')
|
const favicon = require('serve-favicon')
|
||||||
const flash = require('connect-flash')
|
const flash = require('connect-flash')
|
||||||
const http = require('http')
|
const http = require('http')
|
||||||
const i18nBackend = require('i18next-node-fs-backend')
|
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
const passportSocketIo = require('passport.socketio')
|
|
||||||
const session = require('express-session')
|
const session = require('express-session')
|
||||||
const SessionRedisStore = require('connect-redis')(session)
|
const SessionRedisStore = require('connect-redis')(session)
|
||||||
const graceful = require('node-graceful')
|
const graceful = require('node-graceful')
|
||||||
const socketio = require('socket.io')
|
|
||||||
const graphqlApollo = require('apollo-server-express')
|
const graphqlApollo = require('apollo-server-express')
|
||||||
const graphqlSchema = require('./modules/graphql')
|
const graphqlSchema = require('./modules/graphql')
|
||||||
|
|
||||||
@@ -100,23 +97,6 @@ module.exports = Promise.join(
|
|||||||
|
|
||||||
app.use(mw.seo)
|
app.use(mw.seo)
|
||||||
|
|
||||||
// ----------------------------------------
|
|
||||||
// Localization Engine
|
|
||||||
// ----------------------------------------
|
|
||||||
|
|
||||||
wiki.lang.use(i18nBackend).init({
|
|
||||||
load: 'languageOnly',
|
|
||||||
ns: ['common', 'admin', 'auth', 'errors', 'git'],
|
|
||||||
defaultNS: 'common',
|
|
||||||
saveMissing: false,
|
|
||||||
preload: [wiki.config.site.lang],
|
|
||||||
lng: wiki.config.site.lang,
|
|
||||||
fallbackLng: 'en',
|
|
||||||
backend: {
|
|
||||||
loadPath: path.join(wiki.SERVERPATH, 'locales/{{lng}}/{{ns}}.json')
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// ----------------------------------------
|
// ----------------------------------------
|
||||||
// View Engine Setup
|
// View Engine Setup
|
||||||
// ----------------------------------------
|
// ----------------------------------------
|
||||||
@@ -133,7 +113,7 @@ module.exports = Promise.join(
|
|||||||
|
|
||||||
app.locals.basedir = wiki.ROOTPATH
|
app.locals.basedir = wiki.ROOTPATH
|
||||||
app.locals._ = require('lodash')
|
app.locals._ = require('lodash')
|
||||||
app.locals.t = wiki.lang.t.bind(wiki.lang)
|
app.locals.t = wiki.lang.engine.t.bind(wiki.lang)
|
||||||
app.locals.moment = require('moment')
|
app.locals.moment = require('moment')
|
||||||
app.locals.moment.locale(wiki.config.site.lang)
|
app.locals.moment.locale(wiki.config.site.lang)
|
||||||
app.locals.config = wiki.config
|
app.locals.config = wiki.config
|
||||||
@@ -173,11 +153,10 @@ module.exports = Promise.join(
|
|||||||
// Start HTTP server
|
// Start HTTP server
|
||||||
// ----------------------------------------
|
// ----------------------------------------
|
||||||
|
|
||||||
wiki.logger.info(`HTTP/WS Server on port: ${wiki.config.port}`)
|
wiki.logger.info(`HTTP Server on port: ${wiki.config.port}`)
|
||||||
|
|
||||||
app.set('port', wiki.config.port)
|
app.set('port', wiki.config.port)
|
||||||
var server = http.createServer(app)
|
let server = http.createServer(app)
|
||||||
var io = socketio(server)
|
|
||||||
|
|
||||||
server.listen(wiki.config.port)
|
server.listen(wiki.config.port)
|
||||||
server.on('error', (error) => {
|
server.on('error', (error) => {
|
||||||
@@ -199,35 +178,14 @@ module.exports = Promise.join(
|
|||||||
})
|
})
|
||||||
|
|
||||||
server.on('listening', () => {
|
server.on('listening', () => {
|
||||||
wiki.logger.info('HTTP/WS Server: RUNNING')
|
wiki.logger.info('HTTP Server: RUNNING')
|
||||||
})
|
})
|
||||||
|
|
||||||
// ----------------------------------------
|
|
||||||
// WebSocket
|
|
||||||
// ----------------------------------------
|
|
||||||
|
|
||||||
io.use(passportSocketIo.authorize({
|
|
||||||
key: 'wikijs.sid',
|
|
||||||
store: sessionStore,
|
|
||||||
secret: wiki.config.site.sessionSecret,
|
|
||||||
cookieParser,
|
|
||||||
success: (data, accept) => {
|
|
||||||
accept()
|
|
||||||
},
|
|
||||||
fail: (data, message, error, accept) => {
|
|
||||||
accept()
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
|
|
||||||
io.on('connection', ctrl.ws)
|
|
||||||
|
|
||||||
// ----------------------------------------
|
// ----------------------------------------
|
||||||
// Graceful shutdown
|
// Graceful shutdown
|
||||||
// ----------------------------------------
|
// ----------------------------------------
|
||||||
|
|
||||||
graceful.on('exit', () => {
|
graceful.on('exit', () => {
|
||||||
// wiki.logger.info('- SHUTTING DOWN - Terminating Background Agent...')
|
|
||||||
// bgAgent.kill()
|
|
||||||
wiki.logger.info('- SHUTTING DOWN - Performing git sync...')
|
wiki.logger.info('- SHUTTING DOWN - Performing git sync...')
|
||||||
return global.git.resync().then(() => {
|
return global.git.resync().then(() => {
|
||||||
wiki.logger.info('- SHUTTING DOWN - Git sync successful. Now safe to exit.')
|
wiki.logger.info('- SHUTTING DOWN - Git sync successful. Now safe to exit.')
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ const FolderResolvers = require('../schemas/resolvers-folder')
|
|||||||
const GroupResolvers = require('../schemas/resolvers-group')
|
const GroupResolvers = require('../schemas/resolvers-group')
|
||||||
const SettingResolvers = require('../schemas/resolvers-setting')
|
const SettingResolvers = require('../schemas/resolvers-setting')
|
||||||
const TagResolvers = require('../schemas/resolvers-tag')
|
const TagResolvers = require('../schemas/resolvers-tag')
|
||||||
|
const TranslationResolvers = require('../schemas/resolvers-translation')
|
||||||
const UserResolvers = require('../schemas/resolvers-user')
|
const UserResolvers = require('../schemas/resolvers-user')
|
||||||
|
|
||||||
const resolvers = _.merge(
|
const resolvers = _.merge(
|
||||||
@@ -27,6 +28,7 @@ const resolvers = _.merge(
|
|||||||
GroupResolvers,
|
GroupResolvers,
|
||||||
SettingResolvers,
|
SettingResolvers,
|
||||||
TagResolvers,
|
TagResolvers,
|
||||||
|
TranslationResolvers,
|
||||||
UserResolvers,
|
UserResolvers,
|
||||||
DateScalar
|
DateScalar
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
const _ = require('lodash')
|
||||||
|
const dotize = require('dotize')
|
||||||
|
const i18nBackend = require('i18next-node-fs-backend')
|
||||||
|
const i18next = require('i18next')
|
||||||
|
const path = require('path')
|
||||||
|
const Promise = require('bluebird')
|
||||||
|
|
||||||
|
/* global wiki */
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
engine: null,
|
||||||
|
namespaces: ['common', 'admin', 'auth', 'errors', 'git'],
|
||||||
|
init() {
|
||||||
|
this.engine = i18next
|
||||||
|
this.engine.use(i18nBackend).init({
|
||||||
|
load: 'languageOnly',
|
||||||
|
ns: this.namespaces,
|
||||||
|
defaultNS: 'common',
|
||||||
|
saveMissing: false,
|
||||||
|
preload: [wiki.config.site.lang],
|
||||||
|
lng: wiki.config.site.lang,
|
||||||
|
fallbackLng: 'en',
|
||||||
|
backend: {
|
||||||
|
loadPath: path.join(wiki.SERVERPATH, 'locales/{{lng}}/{{ns}}.json')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return this
|
||||||
|
},
|
||||||
|
getByNamespace(locale, namespace) {
|
||||||
|
if (this.engine.hasResourceBundle(locale, namespace)) {
|
||||||
|
let data = this.engine.getResourceBundle(locale, namespace)
|
||||||
|
return _.map(dotize.convert(data), (value, key) => {
|
||||||
|
return {
|
||||||
|
key,
|
||||||
|
value
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
throw new Error('Invalid locale or namespace')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
loadLocale(locale) {
|
||||||
|
return Promise.fromCallback(cb => {
|
||||||
|
return this.engine.loadLanguages(locale, cb)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
setCurrentLocale(locale) {
|
||||||
|
return Promise.fromCallback(cb => {
|
||||||
|
return this.engine.changeLanguage(locale, cb)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
'use strict'
|
|
||||||
|
|
||||||
/* global wiki */
|
/* global wiki */
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
'use strict'
|
|
||||||
|
|
||||||
/* global wiki */
|
/* global wiki */
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
'use strict'
|
|
||||||
|
|
||||||
/* global wiki */
|
/* global wiki */
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
'use strict'
|
|
||||||
|
|
||||||
/* global wiki */
|
/* global wiki */
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
'use strict'
|
|
||||||
|
|
||||||
/* global wiki */
|
/* global wiki */
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
'use strict'
|
|
||||||
|
|
||||||
/* global wiki */
|
/* global wiki */
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
'use strict'
|
|
||||||
|
|
||||||
/* global wiki */
|
/* global wiki */
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
'use strict'
|
|
||||||
|
|
||||||
/* global wiki */
|
/* global wiki */
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
|
||||||
|
/* global wiki */
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
Query: {
|
||||||
|
translations (obj, args, context, info) {
|
||||||
|
return wiki.lang.getByNamespace(args.locale, args.namespace)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Mutation: {},
|
||||||
|
Translation: {}
|
||||||
|
}
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
'use strict'
|
|
||||||
|
|
||||||
/* global wiki */
|
/* global wiki */
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
'use strict'
|
|
||||||
|
|
||||||
const gql = require('graphql')
|
const gql = require('graphql')
|
||||||
|
|
||||||
|
|||||||
@@ -114,6 +114,11 @@ type Tag implements Base {
|
|||||||
documents: [Document]
|
documents: [Document]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Translation {
|
||||||
|
key: String!
|
||||||
|
value: String!
|
||||||
|
}
|
||||||
|
|
||||||
# A User
|
# A User
|
||||||
type User implements Base {
|
type User implements Base {
|
||||||
id: Int!
|
id: Int!
|
||||||
@@ -142,6 +147,7 @@ type Query {
|
|||||||
rights(id: Int): [Right]
|
rights(id: Int): [Right]
|
||||||
settings(key: String): [Setting]
|
settings(key: String): [Setting]
|
||||||
tags(key: String): [Tag]
|
tags(key: String): [Tag]
|
||||||
|
translations(locale: String!, namespace: String!): [Translation]
|
||||||
users(id: Int, email: String, provider: String, providerId: String, role: UserRole): [User]
|
users(id: Int, email: String, provider: String, providerId: String, role: UserRole): [User]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,31 +2,5 @@ extends ../master.pug
|
|||||||
|
|
||||||
block body
|
block body
|
||||||
body
|
body
|
||||||
.login#root(class={ "is-error": flash.length > 0 })
|
#app.is-fullscreen
|
||||||
.login-container(class={ "is-expanded": hasMultipleStrategies })
|
login
|
||||||
if flash.length > 0
|
|
||||||
.login-error
|
|
||||||
strong
|
|
||||||
i.icon-warning-outline
|
|
||||||
= flash[0].title
|
|
||||||
span= flash[0].message
|
|
||||||
if hasMultipleStrategies
|
|
||||||
.login-providers
|
|
||||||
button.is-active(title=t('auth:providers.local'))
|
|
||||||
i.nc-icon-outline.ui-1_database
|
|
||||||
span= t('auth:providers.local')
|
|
||||||
each strategy in authStrategies
|
|
||||||
button(onclick='window.location.assign("/login/' + strategy.key + '")', title=strategy.title)
|
|
||||||
!= strategy.icon
|
|
||||||
span= strategy.title
|
|
||||||
.login-frame
|
|
||||||
h1= config.site.title
|
|
||||||
h2= t('auth:loginrequired')
|
|
||||||
form(method='post', action='/login')
|
|
||||||
input#login-user(type='text', name='email', placeholder=t('auth:fields.emailuser'))
|
|
||||||
input#login-pass(type='password', name='password', placeholder=t('auth:fields.password'))
|
|
||||||
button.button.is-light-blue.is-fullwidth(type='submit')
|
|
||||||
span= t('auth:actions.login')
|
|
||||||
.login-copyright
|
|
||||||
= t('footer.poweredby')
|
|
||||||
a(href='https://wiki.js.org', rel='external', title='Wiki.js') Wiki.js
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ extends ./master.pug
|
|||||||
|
|
||||||
block body
|
block body
|
||||||
body
|
body
|
||||||
#root.has-stickynav(class=['is-primary-' + appconfig.theme.primary, 'is-alternate-' + appconfig.theme.alt])
|
#app.has-stickynav(class=['is-primary-' + appconfig.theme.primary, 'is-alternate-' + appconfig.theme.alt])
|
||||||
include ./common/header.pug
|
include ./common/header.pug
|
||||||
alert
|
alert
|
||||||
main
|
main
|
||||||
|
|||||||
Reference in New Issue
Block a user