refactor: remove config namespaces

pull/621/head
NGPixel 7 years ago
parent b1499d1d64
commit 416755f17a

@ -58,7 +58,7 @@
color='blue' color='blue'
) )
v-spacer v-spacer
v-progress-circular.mr-3(indeterminate, color='blue', v-show='isLoading') v-progress-circular.mr-3(indeterminate, color='blue', :size='22', :width='2' v-show='isLoading')
slot(name='actions') slot(name='actions')
transition(name='navHeaderSearch') transition(name='navHeaderSearch')
v-btn(icon, @click='searchToggle', v-if='!searchIsShown') v-btn(icon, @click='searchToggle', v-if='!searchIsShown')

@ -41,6 +41,8 @@
import VueRouter from 'vue-router' import VueRouter from 'vue-router'
import { mapState } from 'vuex' import { mapState } from 'vuex'
/* global WIKI */
const router = new VueRouter({ const router = new VueRouter({
mode: 'history', mode: 'history',
base: '/p', base: '/p',
@ -53,6 +55,15 @@ const router = new VueRouter({
] ]
}) })
router.beforeEach((to, from, next) => {
WIKI.$store.commit('loadingStart', 'profile')
next()
})
router.afterEach((to, from) => {
WIKI.$store.commit('loadingStop', 'profile')
})
export default { export default {
data() { data() {
return { return {

@ -12,13 +12,32 @@
v-toolbar(color='primary', dark, dense, flat) v-toolbar(color='primary', dark, dense, flat)
v-toolbar-title v-toolbar-title
.subheading Display .subheading Display
v-subheader Locale v-card-text
v-subheader.pl-0 Locale
v-select.grey.lighten-5(solo, flat)
v-divider
v-subheader.pl-0 Timezone
v-select.grey.lighten-5(solo, flat)
v-divider.my-0
v-card-actions.grey.lighten-4
v-spacer
v-btn(color='primary')
v-icon(left) chevron_right
span Save
v-flex(lg6 xs12) v-flex(lg6 xs12)
v-card v-card
v-toolbar(color='primary', dark, dense, flat) v-toolbar(color='primary', dark, dense, flat)
v-toolbar-title v-toolbar-title
.subheading --- .subheading Editing
v-card-text --- v-card-text
v-subheader.pl-0 Default Editor
v-select.grey.lighten-5(solo, flat)
v-divider.my-0
v-card-actions.grey.lighten-4
v-spacer
v-btn(color='primary')
v-icon(left) chevron_right
span Save
</template> </template>

@ -1,40 +1,74 @@
<template lang='pug'> <template lang='pug'>
v-container(fluid, fill-height, grid-list-lg) v-container(fluid, grid-list-lg)
v-layout(row wrap) v-layout(row wrap)
v-flex(xs12) v-flex(xs6)
.headline.primary--text Profile .headline.primary--text Profile
.subheading.grey--text Personal profile .subheading.grey--text Personal profile
v-form.pt-3 v-flex(xs6).text-xs-right
v-layout(row wrap) v-btn(outline, color='primary').mr-0
v-flex(lg6 xs12) v-icon(left) public
v-form span View Public Profile
v-card v-flex(lg6 xs12)
v-toolbar(color='primary', dark, dense, flat) v-card
v-toolbar-title v-toolbar(color='primary', dark, dense, flat)
.subheading User Details v-toolbar-title
v-card-text .subheading User Details
v-text-field(label='Name', :counter='255', v-model='name', prepend-icon='person') v-card-text
v-divider v-text-field(label='Name', :counter='255', v-model='name', prepend-icon='person')
v-text-field(label='Email', :counter='255', prepend-icon='email') v-text-field(label='Job Title', :counter='255', prepend-icon='accessibility')
v-divider.my-0 v-text-field(label='Location / Office', :counter='255', prepend-icon='location_on')
v-card-actions.grey.lighten-4 v-divider.my-0
v-spacer v-card-actions.grey.lighten-4
v-btn(color='primary') v-spacer
v-icon(left) chevron_right v-btn(color='primary')
span Save v-icon(left) chevron_right
v-flex(lg6 xs12) span Save
v-card v-card.mt-3
v-toolbar(color='primary', dark, dense, flat) v-toolbar(color='purple darken-4', dark, dense, flat)
v-toolbar-title v-toolbar-title
.subheading Picture .subheading Authentication
v-card-text --- v-card-text
v-card.mt-3 v-subheader.pl-0 Provider
v-toolbar(color='teal', dark, dense, flat) v-toolbar(flat, color='purple lighten-5', dense).purple--text.text--darken-4
v-toolbar-title v-icon(color='purple darken-4') supervised_user_circle
.subheading Activity .subheading.ml-3 Local
v-card-text.grey--text.text--darken-2 v-divider
.body-1 Joined #[strong January 1st, 2010] v-subheader.pl-0 Two-Factor Authentication (2FA)
.body-1 Last login on #[strong January 2nd, 2010] .caption.mb-2 2FA adds an extra layer of security by requiring a unique code generated on your smartphone when signing in.
v-btn(color='purple darken-4', dark, depressed).ml-0 Enable 2FA
v-btn(color='purple darken-4', dark, depressed, disabled).ml-0 Disable 2FA
v-divider
v-subheader.pl-0 Change Password
v-text-field(label='Current Password', prepend-icon='last_page')
v-text-field(label='New Password', prepend-icon='last_page')
v-text-field(label='Confirm New Password', prepend-icon='last_page')
v-btn(color='purple darken-4', dark, depressed).ml-0 Change Password
v-flex(lg6 xs12)
v-card
v-toolbar(color='primary', dark, dense, flat)
v-toolbar-title
.subheading Picture
v-card-title
v-avatar(size='64', color='grey')
v-icon(size='64', color='grey lighten-2') account_circle
v-btn(depressed).ml-4.elevation-1 Upload Picture
v-btn(depressed, disabled).elevation-1 Remove Picture
v-card.mt-3
v-toolbar(color='teal', dark, dense, flat)
v-toolbar-title
.subheading Activity
v-card-text.grey--text.text--darken-2
.body-2.grey--text Joined on
.body-1: strong January 1st, 2018 at 12:00 AM
.body-2.grey--text.mt-3 Profile last updated on
.body-1: strong January 1st, 2018 at 12:00 AM
.body-2.grey--text.mt-3 Last login on
.body-1: strong January 1st, 2018 at 12:00 AM
v-divider
.body-2.grey--text.mt-3 Pages created
.body-1: strong 0
.body-2.grey--text.mt-3 Comments posted
.body-1: strong 0
</template> </template>
<script> <script>

@ -209,8 +209,12 @@
v-flex.pr-3(xs6) v-flex.pr-3(xs6)
v-text-field( v-text-field(
ref='adminPassword', ref='adminPassword',
counter='255'
v-model='conf.adminPassword', v-model='conf.adminPassword',
label='Password', label='Password',
:append-icon="pwdMode ? 'visibility' : 'visibility_off'"
:append-icon-cb="() => (pwdMode = !pwdMode)"
:type="pwdMode ? 'password' : 'text'"
hint='At least 8 characters long.', hint='At least 8 characters long.',
v-validate='{ required: true, min: 8 }', v-validate='{ required: true, min: 8 }',
data-vv-name='adminPassword', data-vv-name='adminPassword',
@ -221,8 +225,12 @@
v-flex(xs6) v-flex(xs6)
v-text-field( v-text-field(
ref='adminPasswordConfirm', ref='adminPasswordConfirm',
counter='255'
v-model='conf.adminPasswordConfirm', v-model='conf.adminPasswordConfirm',
label='Confirm Password', label='Confirm Password',
:append-icon="pwdConfirmMode ? 'visibility' : 'visibility_off'"
:append-icon-cb="() => (pwdConfirmMode = !pwdConfirmMode)"
:type="pwdConfirmMode ? 'password' : 'text'"
hint='Verify your password again.', hint='Verify your password again.',
v-validate='{ required: true, min: 8 }', v-validate='{ required: true, min: 8 }',
data-vv-name='adminPasswordConfirm', data-vv-name='adminPasswordConfirm',
@ -339,7 +347,9 @@ export default {
title: siteConfig.title || 'Wiki', title: siteConfig.title || 'Wiki',
upgrade: false, upgrade: false,
upgMongo: 'mongodb://' upgMongo: 'mongodb://'
} },
pwdMode: true,
pwdConfirmMode: true
} }
}, },
methods: { methods: {

@ -15,6 +15,7 @@
// @import 'node_modules/diff2html/dist/diff2html.min'; // @import 'node_modules/diff2html/dist/diff2html.min';
@import 'pages/welcome'; @import 'pages/welcome';
@import 'pages/error';
@import 'layout/_rtl'; @import 'layout/_rtl';

@ -0,0 +1,64 @@
.app-error {
background: linear-gradient(to bottom, mc('grey', '900') 0%, mc('grey', '800') 100%);
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
color: mc('grey', '50');
img {
width: 250px;
filter: grayscale(50%) brightness(120%);
animation: errorlogo 5s linear infinite;
margin-bottom: 3rem;
@include until($tablet) {
width: 200px;
}
}
@keyframes errorlogo {
0% {
filter: blur(0) grayscale(50%) brightness(200%) hue-rotate(110deg);
}
10% {
filter: blur(0) grayscale(50%) brightness(200%) hue-rotate(110deg) invert(100%);
}
15% {
filter: blur(0) grayscale(50%) brightness(200%) hue-rotate(110deg) invert(0%);
}
30% {
filter: blur(0) grayscale(50%) brightness(200%) hue-rotate(110deg);
}
32% {
filter: blur(0) grayscale(50%) brightness(200%) hue-rotate(2700deg) invert(100%);
}
34% {
filter: blur(0) grayscale(100%) brightness(50%) hue-rotate(110deg);
}
50% {
filter: blur(0) grayscale(100%) brightness(200%) hue-rotate(110deg) sepia(0%);
}
55% {
filter: blur(0) grayscale(100%) brightness(100%) hue-rotate(110deg) sepia(100%);
}
60% {
filter: blur(0) grayscale(50%) brightness(200%) hue-rotate(110deg) sepia(0%);
}
90% {
filter: blur(0) grayscale(50%) brightness(200%) hue-rotate(110deg);
}
95% {
filter: blur(5px) grayscale(50%) brightness(200%) hue-rotate(720deg);
}
100% {
filter: blur(0) grayscale(50%) brightness(200%) hue-rotate(110deg) invert(100%);
}
}
code {
color: mc('grey', '500');
font-size: .8rem;
}
}

@ -5,7 +5,7 @@
# https://docs.requarks.io/wiki/install # https://docs.requarks.io/wiki/install
# --------------------------------------------------------------------- # ---------------------------------------------------------------------
# Port the main server should listen to # Port the server should listen to
# --------------------------------------------------------------------- # ---------------------------------------------------------------------
port: 80 port: 80
@ -55,27 +55,3 @@ redis:
# Possible values: error, warn, info (default), verbose, debug, silly # Possible values: error, warn, info (default), verbose, debug, silly
logLevel: info logLevel: info
# ---------------------------------------------------------------------
# Configuration Mode
# ---------------------------------------------------------------------
# Possible values: auto (default), file, setup
configMode: auto
# ---------------------------------------------------------------------
# Background Workers
# ---------------------------------------------------------------------
# Leave 0 for auto based on CPU cores
workers: 0
# ---------------------------------------------------------------------
# High Availability
# ---------------------------------------------------------------------
# Read the docs BEFORE changing these settings!
ha:
node: primary
uid: master
readonly: false

@ -23,12 +23,6 @@ defaults:
port: 6379 port: 6379
db: 0 db: 0
password: null password: null
configMode: auto
workers: 0
ha:
node: primary
uid: master
readonly: false
# DB defaults # DB defaults
auth: auth:
public: false public: false
@ -36,8 +30,6 @@ defaults:
local: local:
isEnabled: true isEnabled: true
allowSelfRegister: false allowSelfRegister: false
git:
enabled: false
logging: logging:
telemetry: false telemetry: false
loggers: loggers:
@ -48,6 +40,7 @@ defaults:
rtl: false rtl: false
title: Wiki.js title: Wiki.js
# System defaults # System defaults
setup: false
cors: cors:
credentials: true credentials: true
maxAge: 600 maxAge: 600

@ -1,10 +1,9 @@
/* global WIKI */
const _ = require('lodash')
const passport = require('passport') const passport = require('passport')
const fs = require('fs-extra') const fs = require('fs-extra')
const _ = require('lodash')
const path = require('path') const path = require('path')
const autoload = require('auto-load')
/* global WIKI */
module.exports = { module.exports = {
strategies: {}, strategies: {},
@ -30,34 +29,39 @@ module.exports = {
}) })
}) })
// Load authentication strategies
const modules = _.values(autoload(path.join(WIKI.SERVERPATH, 'modules/authentication')))
_.forEach(modules, (strategy) => {
const strategyConfig = _.get(WIKI.config.auth.strategies, strategy.key, { isEnabled: false })
strategyConfig.callbackURL = `${WIKI.config.site.host}${WIKI.config.site.path}login/${strategy.key}/callback`
strategy.config = strategyConfig
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-${strategy.key}.svg`), 'utf8').then(iconData => {
strategy.icon = iconData
}).catch(err => {
if (err.code === 'ENOENT') {
strategy.icon = '[missing icon]'
} else {
WIKI.logger.warn(err)
}
})
this.strategies[strategy.key] = strategy
WIKI.logger.info(`Authentication Provider ${strategy.title}: [ OK ]`)
})
return this return this
},
async activateStrategies() {
try {
// Unload any active strategies
WIKI.auth.strategies = []
const currentStrategies = _.keys(passport._strategies)
_.pull(currentStrategies, 'session')
_.forEach(currentStrategies, stg => { passport.unuse(stg) })
// Load enable strategies
const enabledStrategies = await WIKI.db.authentication.getEnabledStrategies()
for (let idx in enabledStrategies) {
const stg = enabledStrategies[idx]
const strategy = require(`../modules/authentication/${stg.key}`)
stg.config.callbackURL = `${WIKI.config.site.host}/login/${stg.key}/callback`
strategy.init(passport, stg.config)
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.warn(err)
}
})
WIKI.auth.strategies[stg.key] = strategy
WIKI.logger.info(`Authentication Strategy ${stg.title}: [ OK ]`)
}
} catch (err) {
WIKI.logger.error(`Authentication Strategy: [ FAILED ]`)
WIKI.logger.error(err)
}
} }
} }

@ -50,45 +50,32 @@ module.exports = {
/** /**
* Load config from DB * Load config from DB
*
* @param {Array} subsets Array of subsets to load
* @returns Promise
*/ */
async loadFromDb(subsets) { async loadFromDb() {
if (!_.isArray(subsets) || subsets.length === 0) { let conf = await WIKI.db.settings.getConfig()
subsets = WIKI.data.configNamespaces if (conf) {
} WIKI.config = _.defaultsDeep(conf, WIKI.config)
let results = await WIKI.db.settings.query().select(['key', 'value']).whereIn('key', subsets)
if (_.isArray(results) && results.length === subsets.length) {
results.forEach(result => {
WIKI.config[result.key] = result.value
})
return true
} else { } else {
WIKI.logger.warn('DB Configuration is empty or incomplete.') WIKI.logger.warn('DB Configuration is empty or incomplete. Switching to Setup mode...')
return false WIKI.config.setup = true
} }
}, },
/** /**
* Save config to DB * Save config to DB
* *
* @param {Array} subsets Array of subsets to save * @param {Array} keys Array of keys to save
* @returns Promise * @returns Promise
*/ */
async saveToDb(subsets) { async saveToDb(keys) {
if (!_.isArray(subsets) || subsets.length === 0) {
subsets = WIKI.data.configNamespaces
}
let trx = await WIKI.db.Objection.transaction.start(WIKI.db.knex) let trx = await WIKI.db.Objection.transaction.start(WIKI.db.knex)
try { try {
for (let set of subsets) { for (let key of keys) {
console.info(set) const value = _.get(WIKI.config, key, null)
await WIKI.db.settings.query(trx).patch({ let affectedRows = await WIKI.db.settings.query(trx).patch({ value }).where('key', key)
value: _.get(WIKI.config, set, {}) if (affectedRows === 0 && value) {
}).where('key', set) await WIKI.db.settings.query(trx).insert({ key, value })
}
} }
await trx.commit() await trx.commit()
} catch (err) { } catch (err) {

@ -53,6 +53,20 @@ module.exports = {
client: dbClient, client: dbClient,
useNullAsDefault: true, useNullAsDefault: true,
connection: dbConfig, connection: dbConfig,
pool: {
async afterCreate(conn, done) {
// -> Set Connection App Name
switch (WIKI.config.db.type) {
case 'postgres':
await conn.query(`set application_name = 'Wiki.js'`)
done()
break
default:
done()
break
}
}
},
debug: WIKI.IS_DEBUG debug: WIKI.IS_DEBUG
}) })
@ -71,21 +85,13 @@ module.exports = {
directory: path.join(WIKI.SERVERPATH, 'db/migrations'), directory: path.join(WIKI.SERVERPATH, 'db/migrations'),
tableName: 'migrations' tableName: 'migrations'
}) })
},
// -> Set Connection App Name
async setAppName() {
switch (WIKI.config.db.type) {
case 'postgres':
return self.knex.raw(`set application_name = 'Wiki.js'`)
}
} }
} }
let initTasksQueue = (WIKI.IS_MASTER) ? [ let initTasksQueue = (WIKI.IS_MASTER) ? [
initTasks.syncSchemas, initTasks.syncSchemas
initTasks.setAppName
] : [ ] : [
initTasks.setAppName () => { return Promise.resolve() }
] ]
// Perform init tasks // Perform init tasks

@ -1,89 +1,53 @@
const _ = require('lodash')
const cluster = require('cluster')
const Promise = require('bluebird')
/* global WIKI */ /* global WIKI */
module.exports = { module.exports = {
numWorkers: 1, async init() {
workers: [], WIKI.logger.info('=======================================')
init() { WIKI.logger.info('= Wiki.js =============================')
if (cluster.isMaster) { WIKI.logger.info('=======================================')
WIKI.logger.info('=======================================')
WIKI.logger.info('= Wiki.js =============================')
WIKI.logger.info('=======================================')
WIKI.redis = require('./redis').init() WIKI.db = require('./db').init()
WIKI.queue = require('./queue').init() WIKI.redis = require('./redis').init()
WIKI.queue = require('./queue').init()
this.setWorkerLimit() await this.preBootMaster()
this.bootMaster() this.bootMaster()
} else {
this.bootWorker()
}
}, },
/** /**
* Pre-Master Boot Sequence * Pre-Master Boot Sequence
*/ */
preBootMaster() { async preBootMaster() {
return Promise.mapSeries([ try {
() => { return WIKI.db.onReady }, await WIKI.db.onReady
() => { return WIKI.configSvc.loadFromDb() }, await WIKI.configSvc.loadFromDb()
() => { return WIKI.queue.clean() } await WIKI.queue.clean()
], fn => { return fn() }) } catch (err) {
WIKI.logger.error(err)
process.exit(1)
}
}, },
/** /**
* Boot Master Process * Boot Master Process
*/ */
bootMaster() { async bootMaster() {
this.preBootMaster().then(sequenceResults => { try {
if (_.every(sequenceResults, rs => rs === true) && WIKI.config.configMode !== 'setup') { if (WIKI.config.setup) {
this.postBootMaster() WIKI.logger.info('Starting setup wizard...')
} else {
WIKI.logger.info('Starting configuration manager...')
require('../setup')() require('../setup')()
} else {
await require('../master')()
this.postBootMaster()
} }
return true } catch (err) {
}).catch(err => {
WIKI.logger.error(err) WIKI.logger.error(err)
process.exit(1) process.exit(1)
}) }
}, },
/** /**
* Post-Master Boot Sequence * Post-Master Boot Sequence
*/ */
async postBootMaster() { async postBootMaster() {
await require('../master')() await WIKI.auth.activateStrategies()
await WIKI.queue.start()
WIKI.queue.start()
cluster.on('exit', (worker, code, signal) => {
if (!global.DEV) {
WIKI.logger.info(`Background Worker #${worker.id} was terminated.`)
}
})
},
/**
* Boot Worker Process
*/
bootWorker() {
WIKI.logger.info(`Background Worker #${cluster.worker.id} is initializing...`)
require('../worker')
},
/**
* Spawn new Worker process
*/
spawnWorker() {
this.workers.push(cluster.fork())
},
/**
* Set Worker count based on config + system capabilities
*/
setWorkerLimit() {
const numCPUs = require('os').cpus().length
this.numWorkers = (WIKI.config.workers > 0) ? WIKI.config.workers : numCPUs
if (this.numWorkers > numCPUs) {
this.numWorkers = numCPUs
}
} }
} }

@ -17,7 +17,7 @@ module.exports = {
ns: this.namespaces, ns: this.namespaces,
defaultNS: 'common', defaultNS: 'common',
saveMissing: false, saveMissing: false,
lng: WIKI.config.site.lang, lng: WIKI.config.lang,
fallbackLng: 'en' fallbackLng: 'en'
}) })
@ -31,7 +31,7 @@ module.exports = {
} }
// Load current language // Load current language
this.loadLocale(WIKI.config.site.lang, { silent: true }) this.loadLocale(WIKI.config.lang, { silent: true })
return this return this
}, },
@ -55,6 +55,7 @@ module.exports = {
const res = await WIKI.db.locales.query().findOne('code', locale) const res = await WIKI.db.locales.query().findOne('code', locale)
if (res) { if (res) {
if (_.isPlainObject(res.strings)) { if (_.isPlainObject(res.strings)) {
console.info(res.strings)
_.forOwn(res.strings, (data, ns) => { _.forOwn(res.strings, (data, ns) => {
this.namespaces.push(ns) this.namespaces.push(ns)
this.engine.addResourceBundle(locale, ns, data, true, true) this.engine.addResourceBundle(locale, ns, data, true, true)

@ -23,6 +23,15 @@ exports.up = knex => {
table.string('slug').notNullable() table.string('slug').notNullable()
table.integer('parentId').unsigned().references('id').inTable('assetFolders') table.integer('parentId').unsigned().references('id').inTable('assetFolders')
}) })
// AUTHENTICATION ----------------------
.createTable('authentication', table => {
table.increments('id').primary()
table.string('key').notNullable().unique()
table.string('title').notNullable()
table.boolean('isEnabled').notNullable().defaultTo(false)
table.boolean('useForm').notNullable().defaultTo(false)
table.jsonb('config').notNullable()
})
// COMMENTS ---------------------------- // COMMENTS ----------------------------
.createTable('comments', table => { .createTable('comments', table => {
table.increments('id').primary() table.increments('id').primary()
@ -30,6 +39,14 @@ exports.up = knex => {
table.string('createdAt').notNullable() table.string('createdAt').notNullable()
table.string('updatedAt').notNullable() table.string('updatedAt').notNullable()
}) })
// EDITORS -----------------------------
.createTable('editors', table => {
table.increments('id').primary()
table.string('key').notNullable().unique()
table.string('title').notNullable()
table.boolean('isEnabled').notNullable().defaultTo(false)
table.jsonb('config')
})
// GROUPS ------------------------------ // GROUPS ------------------------------
.createTable('groups', table => { .createTable('groups', table => {
table.increments('id').primary() table.increments('id').primary()
@ -54,6 +71,7 @@ exports.up = knex => {
table.string('path').notNullable() table.string('path').notNullable()
table.string('title').notNullable() table.string('title').notNullable()
table.string('description') table.string('description')
table.boolean('isPrivate').notNullable().defaultTo(false)
table.boolean('isPublished').notNullable().defaultTo(false) table.boolean('isPublished').notNullable().defaultTo(false)
table.string('publishStartDate') table.string('publishStartDate')
table.string('publishEndDate') table.string('publishEndDate')
@ -66,9 +84,16 @@ exports.up = knex => {
table.increments('id').primary() table.increments('id').primary()
table.string('key').notNullable().unique() table.string('key').notNullable().unique()
table.jsonb('value') table.jsonb('value')
table.string('createdAt').notNullable()
table.string('updatedAt').notNullable() table.string('updatedAt').notNullable()
}) })
// STORAGE -----------------------------
.createTable('storage', table => {
table.increments('id').primary()
table.string('key').notNullable().unique()
table.string('title').notNullable()
table.boolean('isEnabled').notNullable().defaultTo(false)
table.jsonb('config')
})
// TAGS -------------------------------- // TAGS --------------------------------
.createTable('tags', table => { .createTable('tags', table => {
table.increments('id').primary() table.increments('id').primary()
@ -82,16 +107,17 @@ exports.up = knex => {
table.increments('id').primary() table.increments('id').primary()
table.string('email').notNullable() table.string('email').notNullable()
table.string('name').notNullable() table.string('name').notNullable()
table.string('provider').notNullable().defaultTo('local')
table.string('providerId') table.string('providerId')
table.string('password') table.string('password')
table.boolean('tfaIsActive').notNullable().defaultTo(false) table.boolean('tfaIsActive').notNullable().defaultTo(false)
table.string('tfaSecret') table.string('tfaSecret')
table.enum('role', ['admin', 'guest', 'user']).notNullable().defaultTo('guest') table.enum('role', ['admin', 'guest', 'user']).notNullable().defaultTo('guest')
table.string('jobTitle').defaultTo('')
table.string('location').defaultTo('')
table.string('pictureUrl')
table.string('timezone').notNullable().defaultTo('America/New_York')
table.string('createdAt').notNullable() table.string('createdAt').notNullable()
table.string('updatedAt').notNullable() table.string('updatedAt').notNullable()
table.unique(['provider', 'email'])
}) })
// ===================================== // =====================================
// RELATION TABLES // RELATION TABLES
@ -120,11 +146,16 @@ exports.up = knex => {
table.integer('authorId').unsigned().references('id').inTable('users') table.integer('authorId').unsigned().references('id').inTable('users')
}) })
.table('pages', table => { .table('pages', table => {
table.string('editor').references('key').inTable('editors')
table.string('locale', 2).references('code').inTable('locales') table.string('locale', 2).references('code').inTable('locales')
table.integer('authorId').unsigned().references('id').inTable('users') table.integer('authorId').unsigned().references('id').inTable('users')
}) })
.table('users', table => { .table('users', table => {
table.string('locale', 2).references('code').inTable('locales') table.string('provider').references('key').inTable('authentication').notNullable().defaultTo('local')
table.string('locale', 2).references('code').inTable('locales').notNullable().defaultTo('en')
table.string('defaultEditor').references('key').inTable('editors').notNullable().defaultTo('markdown')
table.unique(['provider', 'email'])
}) })
} }

@ -0,0 +1,64 @@
const Model = require('objection').Model
const autoload = require('auto-load')
const path = require('path')
const _ = require('lodash')
/* global WIKI */
/**
* Authentication model
*/
module.exports = class Authentication extends Model {
static get tableName() { return 'authentication' }
static get jsonSchema () {
return {
type: 'object',
required: ['key', 'title', 'isEnabled', 'useForm'],
properties: {
id: {type: 'integer'},
key: {type: 'string'},
title: {type: 'string'},
isEnabled: {type: 'boolean'},
useForm: {type: 'boolean'},
config: {type: 'object'}
}
}
}
static async getEnabledStrategies() {
return WIKI.db.authentication.query().where({ isEnabled: true })
}
static async refreshStrategiesFromDisk() {
try {
const dbStrategies = await WIKI.db.authentication.query()
const diskStrategies = autoload(path.join(WIKI.SERVERPATH, 'modules/authentication'))
let newStrategies = []
_.forOwn(diskStrategies, (strategy, strategyKey) => {
if (!_.some(dbStrategies, ['key', strategy.key])) {
newStrategies.push({
key: strategy.key,
title: strategy.title,
isEnabled: false,
useForm: strategy.useForm,
config: _.reduce(strategy.props, (result, value, key) => {
_.set(result, value, '')
return result
}, {})
})
}
})
if (newStrategies.length > 0) {
await WIKI.db.authentication.query().insert(newStrategies)
WIKI.logger.info(`Loaded ${newStrategies.length} new authentication strategies: [ OK ]`)
} else {
WIKI.logger.info(`No new authentication strategies found: [ SKIPPED ]`)
}
} catch (err) {
WIKI.logger.error(`Failed to scan or load new authentication providers: [ FAILED ]`)
WIKI.logger.error(err)
}
}
}

@ -0,0 +1,62 @@
const Model = require('objection').Model
const autoload = require('auto-load')
const path = require('path')
const _ = require('lodash')
/* global WIKI */
/**
* Editor model
*/
module.exports = class Editor extends Model {
static get tableName() { return 'editors' }
static get jsonSchema () {
return {
type: 'object',
required: ['key', 'title', 'isEnabled'],
properties: {
id: {type: 'integer'},
key: {type: 'string'},
title: {type: 'string'},
isEnabled: {type: 'boolean'},
config: {type: 'object'}
}
}
}
static async getEnabledEditors() {
return WIKI.db.editors.query().where({ isEnabled: true })
}
static async refreshEditorsFromDisk() {
try {
const dbEditors = await WIKI.db.editors.query()
const diskEditors = autoload(path.join(WIKI.SERVERPATH, 'modules/editor'))
let newEditors = []
_.forOwn(diskEditors, (strategy, strategyKey) => {
if (!_.some(dbEditors, ['key', strategy.key])) {
newEditors.push({
key: strategy.key,
title: strategy.title,
isEnabled: false,
config: _.reduce(strategy.props, (result, value, key) => {
_.set(result, value, '')
return result
}, {})
})
}
})
if (newEditors.length > 0) {
await WIKI.db.editors.query().insert(newEditors)
WIKI.logger.info(`Loaded ${newEditors.length} new editors: [ OK ]`)
} else {
WIKI.logger.info(`No new editors found: [ SKIPPED ]`)
}
} catch (err) {
WIKI.logger.error(`Failed to scan or load new editors: [ FAILED ]`)
WIKI.logger.error(err)
}
}
}

@ -1,9 +1,12 @@
const Model = require('objection').Model const Model = require('objection').Model
const _ = require('lodash')
/* global WIKI */
/** /**
* Settings model * Settings model
*/ */
module.exports = class User extends Model { module.exports = class Setting extends Model {
static get tableName() { return 'settings' } static get tableName() { return 'settings' }
static get jsonSchema () { static get jsonSchema () {
@ -25,7 +28,18 @@ module.exports = class User extends Model {
this.updatedAt = new Date().toISOString() this.updatedAt = new Date().toISOString()
} }
$beforeInsert() { $beforeInsert() {
this.createdAt = new Date().toISOString()
this.updatedAt = new Date().toISOString() this.updatedAt = new Date().toISOString()
} }
static async getConfig() {
const settings = await WIKI.db.settings.query()
if (settings.length > 0) {
return _.reduce(settings, (res, val, key) => {
_.set(res, val.key, (val.value.v) ? val.value.v : val.value)
return res
}, {})
} else {
return false
}
}
} }

@ -30,6 +30,9 @@ module.exports = class User extends Model {
tfaIsActive: {type: 'boolean', default: false}, tfaIsActive: {type: 'boolean', default: false},
tfaSecret: {type: 'string'}, tfaSecret: {type: 'string'},
locale: {type: 'string'}, locale: {type: 'string'},
jobTitle: {type: 'string'},
location: {type: 'string'},
pictureUrl: {type: 'string'},
createdAt: {type: 'string'}, createdAt: {type: 'string'},
updatedAt: {type: 'string'} updatedAt: {type: 'string'}
} }

@ -4,11 +4,10 @@
// =========================================== // ===========================================
const path = require('path') const path = require('path')
const cluster = require('cluster')
let WIKI = { let WIKI = {
IS_DEBUG: process.env.NODE_ENV === 'development', IS_DEBUG: process.env.NODE_ENV === 'development',
IS_MASTER: cluster.isMaster, IS_MASTER: true,
ROOTPATH: process.cwd(), ROOTPATH: process.cwd(),
SERVERPATH: path.join(process.cwd(), 'server'), SERVERPATH: path.join(process.cwd(), 'server'),
Error: require('./helpers/error'), Error: require('./helpers/error'),
@ -32,18 +31,14 @@ WIKI.logger = require('./core/logger').init('MASTER')
WIKI.telemetry = require('./core/telemetry').init() WIKI.telemetry = require('./core/telemetry').init()
process.on('unhandledRejection', (err) => { process.on('unhandledRejection', (err) => {
WIKI.logger.warn(err)
WIKI.telemetry.sendError(err) WIKI.telemetry.sendError(err)
}) })
process.on('uncaughtException', (err) => { process.on('uncaughtException', (err) => {
WIKI.logger.warn(err)
WIKI.telemetry.sendError(err) WIKI.telemetry.sendError(err)
}) })
// ----------------------------------------
// Init DB
// ----------------------------------------
WIKI.db = require('./core/db').init()
// ---------------------------------------- // ----------------------------------------
// Start Kernel // Start Kernel
// ---------------------------------------- // ----------------------------------------

@ -7,14 +7,15 @@ const { createApolloFetch } = require('apollo-fetch')
WIKI.redis = require('../core/redis').init() WIKI.redis = require('../core/redis').init()
WIKI.db = require('../core/db').init() WIKI.db = require('../core/db').init()
const apollo = createApolloFetch({
uri: 'https://graph.requarks.io'
})
module.exports = async (job) => { module.exports = async (job) => {
WIKI.logger.info(`Fetching locale ${job.data.locale} from Graph endpoint...`) WIKI.logger.info(`Fetching locale ${job.data.locale} from Graph endpoint...`)
try { try {
await WIKI.configSvc.loadFromDb()
const apollo = createApolloFetch({
uri: WIKI.config.graphEndpoint
})
const respStrings = await apollo({ const respStrings = await apollo({
query: `query ($code: String!) { query: `query ($code: String!) {
localization { localization {

@ -7,15 +7,14 @@ const { createApolloFetch } = require('apollo-fetch')
WIKI.redis = require('../core/redis').init() WIKI.redis = require('../core/redis').init()
WIKI.db = require('../core/db').init() WIKI.db = require('../core/db').init()
const apollo = createApolloFetch({
uri: 'https://graph.requarks.io'
})
module.exports = async (job) => { module.exports = async (job) => {
WIKI.logger.info('Syncing locales with Graph endpoint...') WIKI.logger.info('Syncing locales with Graph endpoint...')
try { try {
await WIKI.configSvc.loadFromDb(['site']) await WIKI.configSvc.loadFromDb()
const apollo = createApolloFetch({
uri: WIKI.config.graphEndpoint
})
// -> Fetch locales list // -> Fetch locales list

@ -75,7 +75,7 @@ module.exports = async () => {
app.use(session({ app.use(session({
name: 'wikijs.sid', name: 'wikijs.sid',
store: sessionStore, store: sessionStore,
secret: WIKI.config.site.sessionSecret, secret: WIKI.config.sessionSecret,
resave: false, resave: false,
saveUninitialized: false saveUninitialized: false
})) }))

@ -0,0 +1,10 @@
// ------------------------------------
// Markdown Editor (default)
// ------------------------------------
module.exports = {
key: 'markdown',
title: 'Markdown (default)',
props: [],
init (conf) {}
}

@ -250,8 +250,6 @@ module.exports = () => {
app.post('/finalize', async (req, res) => { app.post('/finalize', async (req, res) => {
WIKI.telemetry.sendEvent('setup', 'finalize') WIKI.telemetry.sendEvent('setup', 'finalize')
console.error('DUDE')
try { try {
// Upgrade from WIKI.js 1.x? // Upgrade from WIKI.js 1.x?
if (req.body.upgrade) { if (req.body.upgrade) {
@ -272,41 +270,31 @@ module.exports = () => {
confRaw = yaml.safeDump(conf) confRaw = yaml.safeDump(conf)
await fs.writeFileAsync(path.join(WIKI.ROOTPATH, 'config.yml'), confRaw) await fs.writeFileAsync(path.join(WIKI.ROOTPATH, 'config.yml'), confRaw)
_.set(WIKI.config, 'port', req.body.port) // Set config
_.set(WIKI.config, 'defaultEditor', true)
_.set(WIKI.config, 'graphEndpoint', 'https://graph.requarks.io')
_.set(WIKI.config, 'lang', 'en')
_.set(WIKI.config, 'langAutoUpdate', true)
_.set(WIKI.config, 'langRTL', false)
_.set(WIKI.config, 'paths.content', req.body.pathContent) _.set(WIKI.config, 'paths.content', req.body.pathContent)
_.set(WIKI.config, 'port', req.body.port)
// Populate config namespaces _.set(WIKI.config, 'public', req.body.public === 'true')
WIKI.config.auth = WIKI.config.auth || {} _.set(WIKI.config, 'sessionSecret', (await crypto.randomBytesAsync(32)).toString('hex'))
WIKI.config.features = WIKI.config.features || {} _.set(WIKI.config, 'telemetry', req.body.telemetry === 'true')
WIKI.config.logging = WIKI.config.logging || {} _.set(WIKI.config, 'title', req.body.title)
WIKI.config.site = WIKI.config.site || {}
WIKI.config.theme = WIKI.config.theme || {}
WIKI.config.uploads = WIKI.config.uploads || {}
// Site namespace
_.set(WIKI.config.site, 'title', req.body.title)
_.set(WIKI.config.site, 'lang', 'en')
_.set(WIKI.config.site, 'langAutoUpdate', true)
_.set(WIKI.config.site, 'rtl', false)
_.set(WIKI.config.site, 'sessionSecret', (await crypto.randomBytesAsync(32)).toString('hex'))
// Auth namespace
_.set(WIKI.config.auth, 'public', req.body.public === 'true')
_.set(WIKI.config.auth, 'strategies.local.isEnabled', true)
_.set(WIKI.config.auth, 'strategies.local.allowSelfRegister', req.body.selfRegister === 'true')
// Logging namespace
WIKI.config.logging.telemetry = (req.body.telemetry === 'true')
// Save config to DB // Save config to DB
WIKI.logger.info('Persisting config to DB...') WIKI.logger.info('Persisting config to DB...')
await WIKI.db.settings.query().insert([ await WIKI.db.settings.query().insert([
{ key: 'auth', value: WIKI.config.auth }, { key: 'defaultEditor', value: { v: WIKI.config.defaultEditor } },
{ key: 'features', value: WIKI.config.features }, { key: 'graphEndpoint', value: { v: WIKI.config.graphEndpoint } },
{ key: 'logging', value: WIKI.config.logging }, { key: 'lang', value: { v: WIKI.config.lang } },
{ key: 'site', value: WIKI.config.site }, { key: 'langAutoUpdate', value: { v: WIKI.config.langAutoUpdate } },
{ key: 'theme', value: WIKI.config.theme }, { key: 'langRTL', value: { v: WIKI.config.langRTL } },
{ key: 'uploads', value: WIKI.config.uploads } { key: 'public', value: { v: WIKI.config.public } },
{ key: 'sessionSecret', value: { v: WIKI.config.sessionSecret } },
{ key: 'telemetry', value: { v: WIKI.config.telemetry } },
{ key: 'title', value: { v: WIKI.config.title } }
]) ])
// Create default locale // Create default locale
@ -319,8 +307,20 @@ module.exports = () => {
nativeName: 'English' nativeName: 'English'
}) })
// Load authentication strategies + enable local
await WIKI.db.authentication.refreshStrategiesFromDisk()
await WIKI.db.authentication.query().patch({ isEnabled: true }).where('key', 'local')
// Load editors + enable default
await WIKI.db.editors.refreshEditorsFromDisk()
await WIKI.db.editors.query().patch({ isEnabled: true }).where('key', 'markdown')
// Create root administrator // Create root administrator
WIKI.logger.info('Creating root administrator...') WIKI.logger.info('Creating root administrator...')
await WIKI.db.users.query().delete().where({
provider: 'local',
email: req.body.adminEmail
})
await WIKI.db.users.query().insert({ await WIKI.db.users.query().insert({
email: req.body.adminEmail, email: req.body.adminEmail,
provider: 'local', provider: 'local',
@ -328,11 +328,12 @@ module.exports = () => {
name: 'Administrator', name: 'Administrator',
role: 'admin', role: 'admin',
locale: 'en', locale: 'en',
defaultEditor: 'markdown',
tfaIsActive: false tfaIsActive: false
}) })
// Create Guest account // Create Guest account
WIKI.logger.info('Creating root administrator...') WIKI.logger.info('Creating guest account...')
const guestUsr = await WIKI.db.users.query().findOne({ const guestUsr = await WIKI.db.users.query().findOne({
provider: 'local', provider: 'local',
email: 'guest@example.com' email: 'guest@example.com'
@ -345,6 +346,7 @@ module.exports = () => {
password: '', password: '',
role: 'guest', role: 'guest',
locale: 'en', locale: 'en',
defaultEditor: 'markdown',
tfaIsActive: false tfaIsActive: false
}) })
} }
@ -356,6 +358,8 @@ module.exports = () => {
redirectPort: WIKI.config.port redirectPort: WIKI.config.port
}).end() }).end()
WIKI.config.setup = false
WIKI.logger.info('Stopping Setup...') WIKI.logger.info('Stopping Setup...')
WIKI.server.destroy(() => { WIKI.server.destroy(() => {
WIKI.logger.info('Setup stopped. Starting Wiki.js...') WIKI.logger.info('Setup stopped. Starting Wiki.js...')
@ -392,7 +396,7 @@ module.exports = () => {
// Start HTTP server // Start HTTP server
// ---------------------------------------- // ----------------------------------------
WIKI.logger.info(`HTTP 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)
WIKI.server = http.createServer(app) WIKI.server = http.createServer(app)
@ -433,6 +437,6 @@ module.exports = () => {
}) })
WIKI.server.on('listening', () => { WIKI.server.on('listening', () => {
WIKI.logger.info('HTTP Server: RUNNING') WIKI.logger.info('HTTP Server: [ RUNNING ]')
}) })
} }

@ -1,13 +1,26 @@
extends ./master.pug extends ./master.pug
block body block body
body(class='is-error') #app.is-fullscreen
.container v-app(dark)
a(href='/'): img(src=config.site.path + '/images/logo.png') .app-error
h1= message v-container
h2= t('errors:generic') .pt-5
a.button.is-amber.is-inverted.is-featured(href=config.site.path+ '/')= t('errors:actions.gohome') v-layout(row)
v-flex(xs10)
a(href='/'): img(src='/svg/logo-wikijs.svg')
v-flex(xs2).text-xs-right
v-btn(href='/', depressed, color='red darken-3')
v-icon(left) home
span Home
v-alert(color='grey', outline, :value='true', icon='error')
strong.red--text.text--lighten-3 Oops, something went wrong...
.body-1.red--text.text--lighten-2= message
if error.stack if error.stack
h3= t('errors:debugmsg') v-expansion-panel.mt-5
pre: code #{error.stack} v-expansion-panel-content.red.darken-3(:value='true')
div(slot='header') View Debug Trace
v-card(color='grey darken-4')
v-card-text
pre: code #{error.stack}

@ -87,19 +87,26 @@ const init = {
const devWatcher = chokidar.watch([ const devWatcher = chokidar.watch([
'./server', './server',
'!./server/views/master.pug' '!./server/views/master.pug'
]) ], {
ignoreInitial: true,
atomic: 400
})
devWatcher.on('ready', () => { devWatcher.on('ready', () => {
devWatcher.on('all', () => { devWatcher.on('all', async () => {
console.warn('--- >>>>>>>>>>>>>>>>>>>>>>>>>>>> ---') console.warn('--- >>>>>>>>>>>>>>>>>>>>>>>>>>>> ---')
console.warn('--- Changes detected: Restarting ---') console.warn('--- Changes detected: Restarting ---')
console.warn('--- <<<<<<<<<<<<<<<<<<<<<<<<<<<< ---') console.warn('--- <<<<<<<<<<<<<<<<<<<<<<<<<<<< ---')
console.warn('--- Closing DB connections...')
await global.WIKI.db.knex.destroy()
console.warn('--- Closing Redis connections...')
await global.WIKI.redis.quit()
console.warn('--- Closing Server connections...')
global.WIKI.server.destroy(() => { global.WIKI.server.destroy(() => {
global.WIKI = {} global.WIKI = {}
for (const workerId in cluster.workers) {
cluster.workers[workerId].kill()
}
Object.keys(require.cache).forEach(function(id) { Object.keys(require.cache).forEach(function(id) {
if (/[/\\]server[/\\]/.test(id)) delete require.cache[id] if (/[/\\]server[/\\]/.test(id)) {
delete require.cache[id]
}
}) })
require('./server') require('./server')
}) })

Loading…
Cancel
Save