feat: rendering modules + admin rendering
This commit is contained in:
parent
db8e598e9a
commit
4cb9f3d9c6
@ -6,141 +6,45 @@
|
||||
.subheading.grey--text Configure how content is rendered
|
||||
v-layout.mt-3(row wrap)
|
||||
v-flex(lg3 xs12)
|
||||
v-card
|
||||
v-toolbar(
|
||||
color='primary'
|
||||
dense
|
||||
flat
|
||||
dark
|
||||
v-toolbar(
|
||||
color='primary'
|
||||
dense
|
||||
flat
|
||||
dark
|
||||
)
|
||||
v-icon.mr-2 line_weight
|
||||
.subheading Pipeline
|
||||
v-expansion-panel.adm-rendering-pipeline
|
||||
v-expansion-panel-content(
|
||||
hide-actions
|
||||
v-for='core in cores'
|
||||
:key='core.key'
|
||||
)
|
||||
v-icon.mr-2 line_weight
|
||||
.subheading Pipeline
|
||||
v-toolbar(
|
||||
color='blue'
|
||||
dense
|
||||
dark
|
||||
)
|
||||
v-icon.mr-2 arrow_downward
|
||||
.body-2 Markdown
|
||||
v-spacer
|
||||
v-btn(
|
||||
icon
|
||||
@click=''
|
||||
v-toolbar(
|
||||
slot='header'
|
||||
color='blue'
|
||||
dense
|
||||
dark
|
||||
flat
|
||||
)
|
||||
v-icon add
|
||||
v-list(two-line, dense)
|
||||
v-list-tile(avatar)
|
||||
v-list-tile-avatar
|
||||
v-icon(color='grey') crop_free
|
||||
v-list-tile-content
|
||||
v-list-tile-title Core
|
||||
v-list-tile-sub-title Basic Markdown parser
|
||||
v-list-tile-avatar
|
||||
v-icon(color='green', small) lens
|
||||
v-divider.my-0
|
||||
v-list-tile(avatar)
|
||||
v-list-tile-avatar
|
||||
v-icon(color='grey') tag_faces
|
||||
v-list-tile-content
|
||||
v-list-tile-title Emoji
|
||||
v-list-tile-sub-title Convert tags to emojis
|
||||
v-list-tile-avatar
|
||||
v-icon(color='green', small) lens
|
||||
v-divider.my-0
|
||||
v-list-tile(avatar)
|
||||
v-list-tile-avatar
|
||||
v-icon(color='grey') list
|
||||
v-list-tile-content
|
||||
v-list-tile-title Task Lists
|
||||
v-list-tile-sub-title Parse task lists to checkboxes
|
||||
v-list-tile-avatar
|
||||
v-icon(color='green', small) lens
|
||||
v-divider.my-0
|
||||
v-list-tile(avatar)
|
||||
v-list-tile-avatar
|
||||
v-icon(color='grey') multiline_chart
|
||||
v-list-tile-content
|
||||
v-list-tile-title PlantUML
|
||||
v-list-tile-sub-title Generate diagrams from PlantUML syntax
|
||||
v-list-tile-avatar
|
||||
v-icon(color='green', small) lens
|
||||
v-divider.my-0
|
||||
v-list-tile(avatar)
|
||||
v-list-tile-avatar
|
||||
v-icon(color='grey') merge_type
|
||||
v-list-tile-content
|
||||
v-list-tile-title Mermaid
|
||||
v-list-tile-sub-title Generate flowcharts from Mermaid syntax
|
||||
v-list-tile-avatar
|
||||
v-icon(color='green', small) lens
|
||||
v-divider.my-0
|
||||
v-list-tile(avatar)
|
||||
v-list-tile-avatar
|
||||
v-icon(color='grey') functions
|
||||
v-list-tile-content
|
||||
v-list-tile-title Mathjax Pre-Processor
|
||||
v-list-tile-sub-title Parse Mathjax content from Markdown
|
||||
v-list-tile-avatar
|
||||
v-icon(color='red', small) trip_origin
|
||||
|
||||
v-toolbar(
|
||||
color='blue'
|
||||
dense
|
||||
dark
|
||||
)
|
||||
v-icon.mr-2 arrow_downward
|
||||
.body-2 HTML
|
||||
v-spacer
|
||||
v-btn(
|
||||
icon
|
||||
@click=''
|
||||
)
|
||||
v-icon add
|
||||
v-list(two-line, dense)
|
||||
v-list-tile(avatar)
|
||||
v-list-tile-avatar
|
||||
v-icon(color='grey') subscriptions
|
||||
v-list-tile-content
|
||||
v-list-tile-title Video Players
|
||||
v-list-tile-sub-title Embed video players such as Youtube, Vimeo, etc.
|
||||
v-list-tile-avatar
|
||||
v-icon(color='green', small) lens
|
||||
v-divider.my-0
|
||||
v-list-tile(avatar)
|
||||
v-list-tile-avatar
|
||||
v-icon(color='grey') subtitles
|
||||
v-list-tile-content
|
||||
v-list-tile-title Asciinema
|
||||
v-list-tile-sub-title Embed asciinema players from compatible links
|
||||
v-list-tile-avatar
|
||||
v-icon(color='green', small) lens
|
||||
v-divider.my-0
|
||||
v-list-tile(avatar)
|
||||
v-list-tile-avatar
|
||||
v-icon(color='grey') volume_up
|
||||
v-list-tile-content
|
||||
v-list-tile-title Audio Players
|
||||
v-list-tile-sub-title Embed audio players for audio content
|
||||
v-list-tile-avatar
|
||||
v-icon(color='green', small) lens
|
||||
v-divider.my-0
|
||||
v-list-tile(avatar)
|
||||
v-list-tile-avatar
|
||||
v-icon(color='grey') insert_comment
|
||||
v-list-tile-content
|
||||
v-list-tile-title Blockquotes
|
||||
v-list-tile-sub-title Process styled blockquotes
|
||||
v-list-tile-avatar
|
||||
v-icon(color='green', small) lens
|
||||
v-divider.my-0
|
||||
v-list-tile(avatar)
|
||||
v-list-tile-avatar
|
||||
v-icon(color='grey') functions
|
||||
v-list-tile-content
|
||||
v-list-tile-title Mathjax Processor
|
||||
v-list-tile-sub-title TeX/MathML Math Equations Parser
|
||||
v-list-tile-avatar
|
||||
v-icon(color='red', small) trip_origin
|
||||
.body-2 {{core.input}}
|
||||
v-icon.mx-2 arrow_forward
|
||||
.caption {{core.output}}
|
||||
v-list(two-line, dense)
|
||||
v-list-tile(
|
||||
avatar
|
||||
v-for='rdr in core.children'
|
||||
:key='rdr.key'
|
||||
)
|
||||
v-list-tile-avatar
|
||||
v-icon(color='grey') {{rdr.icon}}
|
||||
v-list-tile-content
|
||||
v-list-tile-title {{rdr.title}}
|
||||
v-list-tile-sub-title {{rdr.description}}
|
||||
v-list-tile-avatar
|
||||
v-icon(color='green', small, v-if='rdr.isEnabled') lens
|
||||
v-icon(color='red', small, v-else) trip_origin
|
||||
v-divider.my-0
|
||||
|
||||
v-flex(lg9 xs12)
|
||||
v-card
|
||||
@ -201,16 +105,45 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import _ from 'lodash'
|
||||
|
||||
import renderersQuery from 'gql/admin/rendering/rendering-query-renderers.gql'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
linkify: true,
|
||||
codeTheme: 'Light'
|
||||
codeTheme: 'Light',
|
||||
renderers: []
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
cores() {
|
||||
return _.filter(this.renderers, ['dependsOn', null]).map(core => {
|
||||
core.children = _.concat([_.cloneDeep(core)], _.filter(this.renderers, ['dependsOn', core.key]))
|
||||
return core
|
||||
})
|
||||
}
|
||||
},
|
||||
apollo: {
|
||||
renderers: {
|
||||
query: renderersQuery,
|
||||
fetchPolicy: 'network-only',
|
||||
update: (data) => _.cloneDeep(data.rendering.renderers).map(str => ({...str, config: str.config.map(cfg => ({...cfg, value: JSON.parse(cfg.value)}))})),
|
||||
watchLoading (isLoading) {
|
||||
this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-rendering-refresh')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang='scss'>
|
||||
.adm-rendering-pipeline {
|
||||
border-top: 1px solid #FFF;
|
||||
|
||||
.v-expansion-panel__header {
|
||||
padding: 0 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -14,22 +14,14 @@
|
||||
small Wiki.js Installation Wizard
|
||||
v-divider
|
||||
v-stepper-step(step='2' :complete='state > 2')
|
||||
| System Check
|
||||
small Checking your system for compatibility
|
||||
v-divider
|
||||
v-stepper-step(step='3' :complete='state > 3')
|
||||
| General Information
|
||||
small Site Title, Language and Access
|
||||
v-divider
|
||||
v-stepper-step(step='4' :complete='state > 4')
|
||||
| Administration Account
|
||||
small Create the admin account
|
||||
v-divider(v-if='this.conf.upgrade')
|
||||
v-stepper-step(step='5' :complete='state > 5', v-if='this.conf.upgrade')
|
||||
v-stepper-step(step='3' :complete='state > 3', v-if='this.conf.upgrade')
|
||||
| Upgrade from Wiki.js 1.x
|
||||
small Migrate your existing installation
|
||||
v-divider
|
||||
v-stepper-step(:step='this.conf.upgrade ? 6 : 5' :complete='this.conf.upgrade ? state > 5 : state > 6')
|
||||
v-stepper-step(:step='this.conf.upgrade ? 4 : 3' :complete='this.conf.upgrade ? state > 3 : state > 4')
|
||||
| Final Steps
|
||||
small Finalizing your installation
|
||||
|
||||
@ -68,126 +60,13 @@
|
||||
)
|
||||
v-divider
|
||||
.pt-3.text-xs-center
|
||||
v-btn(color='primary', @click='proceedToSyscheck', :disabled='loading') Start
|
||||
|
||||
//- ==============================================
|
||||
//- SYSTEM CHECK
|
||||
//- ==============================================
|
||||
|
||||
v-stepper-content(step='2')
|
||||
v-card.text-xs-center(flat)
|
||||
svg.icons.is-64: use(xlink:href='#nc-metrics')
|
||||
.subheading System Check
|
||||
v-container
|
||||
v-layout(row, align-center, v-if='loading')
|
||||
v-progress-circular(v-if='loading', indeterminate, color='blue')
|
||||
.body-2.blue--text.ml-3 Checking your system for compatibility...
|
||||
v-alert(type='success', outline, :value='!loading && syscheck.ok') Looks good! No issues so far.
|
||||
v-alert(type='error', outline, :value='!loading && !syscheck.ok') {{ syscheck.error }}
|
||||
v-list.mt-3(two-line, v-if='!loading && syscheck.ok', dense)
|
||||
template(v-for='(rs, n) in syscheck.results')
|
||||
v-divider(v-if='n > 0', inset)
|
||||
v-list-tile
|
||||
v-list-tile-avatar(color='green lighten-5')
|
||||
v-icon(color='green') check
|
||||
v-list-tile-content
|
||||
v-list-tile-title {{rs.title}}
|
||||
v-list-tile-sub-title {{rs.subtitle}}
|
||||
v-divider
|
||||
.pt-3.text-xs-center
|
||||
v-btn(@click='proceedToWelcome', :disabled='loading') Back
|
||||
v-btn(color='primary', @click='proceedToSyscheck', v-if='!loading && !syscheck.ok') Check Again
|
||||
v-btn(color='red', dark, @click='proceedToGeneral', v-if='!loading && !syscheck.ok') Continue Anyway
|
||||
v-btn(color='primary', @click='proceedToGeneral', v-if='loading || syscheck.ok', :disabled='loading') Continue
|
||||
|
||||
//- ==============================================
|
||||
//- GENERAL
|
||||
//- ==============================================
|
||||
|
||||
v-stepper-content(step='3')
|
||||
v-card.text-xs-center(flat)
|
||||
svg.icons.is-64: use(xlink:href='#nc-webpage')
|
||||
.subheading General Information
|
||||
v-form
|
||||
v-container
|
||||
v-layout(row, wrap)
|
||||
v-flex(xs12, sm6).pr-3
|
||||
v-text-field(
|
||||
outline
|
||||
background-color='grey lighten-2'
|
||||
v-model='conf.title',
|
||||
label='Site Title',
|
||||
:counter='255',
|
||||
persistent-hint,
|
||||
hint='The site title will appear in the top left corner on every page and within the window title bar.',
|
||||
v-validate='{ required: true, min: 2 }',
|
||||
data-vv-name='siteTitle',
|
||||
data-vv-as='Site Title',
|
||||
data-vv-scope='general',
|
||||
:error-messages='errors.collect(`siteTitle`)'
|
||||
)
|
||||
v-flex.pr-3(xs12, sm6)
|
||||
v-text-field(
|
||||
outline
|
||||
background-color='grey lighten-2'
|
||||
v-model='conf.port',
|
||||
label='Server Port',
|
||||
persistent-hint,
|
||||
hint='The port on which Wiki.js will listen to. Usually port 80 if connecting directly, or a random port (e.g. 3000) if using a web server in front of it. Set $(PORT) to use the PORT environment variable.',
|
||||
v-validate='{ required: true }',
|
||||
data-vv-name='port',
|
||||
data-vv-as='Port',
|
||||
data-vv-scope='general',
|
||||
:error-messages='errors.collect(`port`)'
|
||||
)
|
||||
v-layout(row, wrap).mt-3
|
||||
v-flex(xs12, sm6).pr-3
|
||||
v-text-field(
|
||||
outline
|
||||
background-color='grey lighten-2'
|
||||
v-model='conf.pathContent',
|
||||
label='Content Data Path',
|
||||
persistent-hint,
|
||||
hint='The path where content is stored (markdown files, uploads, etc.)',
|
||||
v-validate='{ required: true, min: 2 }',
|
||||
data-vv-name='pathContent',
|
||||
data-vv-as='Content Data Path',
|
||||
data-vv-scope='general',
|
||||
:error-messages='errors.collect(`pathContent`)'
|
||||
)
|
||||
v-flex(xs12, sm6)
|
||||
v-text-field(
|
||||
outline
|
||||
background-color='grey lighten-2'
|
||||
v-model='conf.pathData',
|
||||
label='Temporary Data Path',
|
||||
persistent-hint,
|
||||
hint='The path where temporary data is stored (cache, thumbnails, temporary upload files, etc.)',
|
||||
v-validate='{ required: true, min: 2 }',
|
||||
data-vv-name='pathData',
|
||||
data-vv-as='Temporary Data Path',
|
||||
data-vv-scope='general',
|
||||
:error-messages='errors.collect(`pathData`)'
|
||||
)
|
||||
v-layout(row, wrap).mt-3
|
||||
v-flex(xs12)
|
||||
v-checkbox(
|
||||
color='primary',
|
||||
v-model='conf.public',
|
||||
label='Public Access',
|
||||
persistent-hint,
|
||||
hint='Should the site be accessible (read only) without login.'
|
||||
)
|
||||
v-divider
|
||||
.pt-3.text-xs-center
|
||||
v-btn(@click='proceedToSyscheck', :disabled='loading') Back
|
||||
v-btn(color='primary', @click='proceedToAdmin', :disabled='loading') Continue
|
||||
v-btn(color='primary', @click='proceedToAdmin', :disabled='loading') Start
|
||||
|
||||
//- ==============================================
|
||||
//- ADMINISTRATOR ACCOUNT
|
||||
//- ==============================================
|
||||
|
||||
v-stepper-content(step='4')
|
||||
v-stepper-content(step='2')
|
||||
v-card.text-xs-center(flat)
|
||||
svg.icons.is-64: use(xlink:href='#nc-man-black')
|
||||
.subheading Administrator Account
|
||||
@ -245,14 +124,14 @@
|
||||
:error-messages='errors.collect(`adminPasswordConfirm`)'
|
||||
)
|
||||
.pt-3.text-xs-center
|
||||
v-btn(@click='proceedToGeneral', :disabled='loading') Back
|
||||
v-btn(@click='proceedToWelcome', :disabled='loading') Back
|
||||
v-btn(color='primary', @click='proceedToUpgrade', :disabled='loading') Continue
|
||||
|
||||
//- ==============================================
|
||||
//- UPGRADE FROM 1.x
|
||||
//- ==============================================
|
||||
|
||||
v-stepper-content(step='5', v-if='conf.upgrade')
|
||||
v-stepper-content(step='3', v-if='conf.upgrade')
|
||||
v-card.text-xs-center(flat)
|
||||
svg.icons.is-64: use(xlink:href='#nc-spaceship')
|
||||
.subheading Upgrade from Wiki.js 1.x
|
||||
@ -283,7 +162,7 @@
|
||||
//- FINAL
|
||||
//- ==============================================
|
||||
|
||||
v-stepper-content(:step='conf.upgrade ? 6 : 5')
|
||||
v-stepper-content(:step='conf.upgrade ? 4 : 3')
|
||||
v-card.text-xs-center(flat)
|
||||
template(v-if='loading')
|
||||
.mt-3(style='width: 64px; display:inline-block;')
|
||||
@ -340,11 +219,6 @@ export default {
|
||||
return {
|
||||
loading: false,
|
||||
state: 1,
|
||||
syscheck: {
|
||||
ok: false,
|
||||
error: '',
|
||||
results: []
|
||||
},
|
||||
final: {
|
||||
ok: false,
|
||||
error: '',
|
||||
@ -354,13 +228,7 @@ export default {
|
||||
adminEmail: '',
|
||||
adminPassword: '',
|
||||
adminPasswordConfirm: '',
|
||||
lang: siteConfig.lang || 'en',
|
||||
pathData: './data',
|
||||
pathContent: './content',
|
||||
port: siteConfig.port || 80,
|
||||
public: (siteConfig.public === true),
|
||||
telemetry: true,
|
||||
title: siteConfig.title || 'Wiki',
|
||||
upgrade: false,
|
||||
upgMongo: 'mongodb://'
|
||||
},
|
||||
@ -373,73 +241,43 @@ export default {
|
||||
this.state = 1
|
||||
this.loading = false
|
||||
},
|
||||
proceedToSyscheck () {
|
||||
let self = this
|
||||
this.state = 2
|
||||
this.loading = true
|
||||
this.syscheck = {
|
||||
ok: false,
|
||||
error: '',
|
||||
results: []
|
||||
}
|
||||
|
||||
_.delay(() => {
|
||||
axios.post('/syscheck', self.conf).then(resp => {
|
||||
if (resp.data.ok === true) {
|
||||
self.syscheck.ok = true
|
||||
self.syscheck.results = resp.data.results
|
||||
} else {
|
||||
self.syscheck.ok = false
|
||||
self.syscheck.error = resp.data.error
|
||||
}
|
||||
self.loading = false
|
||||
self.$nextTick()
|
||||
}).catch(err => {
|
||||
window.alert(err.message)
|
||||
})
|
||||
}, 1000)
|
||||
},
|
||||
proceedToGeneral () {
|
||||
this.state = 3
|
||||
this.loading = false
|
||||
},
|
||||
async proceedToAdmin () {
|
||||
if (this.state < 4) {
|
||||
if (this.state < 2) {
|
||||
const validationSuccess = await this.$validator.validateAll('general')
|
||||
if (!validationSuccess) {
|
||||
this.state = 3
|
||||
this.state = 1
|
||||
return
|
||||
}
|
||||
}
|
||||
this.state = 4
|
||||
this.state = 2
|
||||
this.loading = false
|
||||
},
|
||||
async proceedToUpgrade () {
|
||||
if (this.state < 5) {
|
||||
if (this.state < 3) {
|
||||
const validationSuccess = await this.$validator.validateAll('admin')
|
||||
if (!validationSuccess || this.conf.adminPassword !== this.conf.adminPasswordConfirm) {
|
||||
this.state = 4
|
||||
this.state = 2
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if (this.conf.upgrade) {
|
||||
this.state = 5
|
||||
this.state = 3
|
||||
this.loading = false
|
||||
} else {
|
||||
this.proceedToFinal()
|
||||
}
|
||||
},
|
||||
async proceedToFinal () {
|
||||
if (this.conf.upgrade && this.state < 6) {
|
||||
if (this.conf.upgrade && this.state < 4) {
|
||||
const validationSuccess = await this.$validator.validateAll('upgrade')
|
||||
if (!validationSuccess) {
|
||||
this.state = 5
|
||||
this.state = 3
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
this.state = this.conf.upgrade ? 6 : 5
|
||||
this.state = this.conf.upgrade ? 4 : 3
|
||||
this.loading = true
|
||||
this.final = {
|
||||
ok: false,
|
||||
|
@ -0,0 +1,12 @@
|
||||
mutation($renderers: [RendererInput]) {
|
||||
rendering {
|
||||
updateRenderers(renderers: $renderers) {
|
||||
responseResult {
|
||||
succeeded
|
||||
errorCode
|
||||
slug
|
||||
message
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
18
client/graph/admin/rendering/rendering-query-renderers.gql
Normal file
18
client/graph/admin/rendering/rendering-query-renderers.gql
Normal file
@ -0,0 +1,18 @@
|
||||
{
|
||||
rendering {
|
||||
renderers {
|
||||
isEnabled
|
||||
key
|
||||
title
|
||||
description
|
||||
icon
|
||||
dependsOn
|
||||
input
|
||||
output
|
||||
config {
|
||||
key
|
||||
value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -51,6 +51,7 @@ module.exports = {
|
||||
async postBootMaster() {
|
||||
await WIKI.models.authentication.refreshStrategiesFromDisk()
|
||||
await WIKI.models.editors.refreshEditorsFromDisk()
|
||||
await WIKI.models.renderers.refreshRenderersFromDisk()
|
||||
await WIKI.models.storage.refreshTargetsFromDisk()
|
||||
|
||||
await WIKI.auth.activateStrategies()
|
||||
|
@ -96,7 +96,7 @@ exports.up = knex => {
|
||||
table.string('createdAt').notNullable()
|
||||
table.string('updatedAt').notNullable()
|
||||
})
|
||||
// STORAGE -----------------------------
|
||||
// RENDERERS ---------------------------
|
||||
.createTable('renderers', table => {
|
||||
table.increments('id').primary()
|
||||
table.string('key').notNullable().unique()
|
||||
@ -199,14 +199,19 @@ exports.up = knex => {
|
||||
exports.down = knex => {
|
||||
return knex.schema
|
||||
.dropTableIfExists('userGroups')
|
||||
.dropTableIfExists('pageHistoryTags')
|
||||
.dropTableIfExists('pageHistory')
|
||||
.dropTableIfExists('pageTags')
|
||||
.dropTableIfExists('assets')
|
||||
.dropTableIfExists('assetFolders')
|
||||
.dropTableIfExists('comments')
|
||||
.dropTableIfExists('editors')
|
||||
.dropTableIfExists('groups')
|
||||
.dropTableIfExists('locales')
|
||||
.dropTableIfExists('pages')
|
||||
.dropTableIfExists('renderers')
|
||||
.dropTableIfExists('settings')
|
||||
.dropTableIfExists('storage')
|
||||
.dropTableIfExists('tags')
|
||||
.dropTableIfExists('users')
|
||||
}
|
||||
|
58
server/graph/resolvers/rendering.js
Normal file
58
server/graph/resolvers/rendering.js
Normal file
@ -0,0 +1,58 @@
|
||||
const _ = require('lodash')
|
||||
const graphHelper = require('../../helpers/graph')
|
||||
|
||||
/* global WIKI */
|
||||
|
||||
module.exports = {
|
||||
Query: {
|
||||
async rendering() { return {} }
|
||||
},
|
||||
Mutation: {
|
||||
async rendering() { return {} }
|
||||
},
|
||||
RenderingQuery: {
|
||||
async renderers(obj, args, context, info) {
|
||||
let renderers = await WIKI.models.renderers.getRenderers()
|
||||
renderers = renderers.map(rdr => {
|
||||
const rendererInfo = _.find(WIKI.data.renderers, ['key', rdr.key]) || {}
|
||||
return {
|
||||
...rendererInfo,
|
||||
...rdr,
|
||||
config: _.sortBy(_.transform(rdr.config, (res, value, key) => {
|
||||
const configData = _.get(rendererInfo.props, key, {})
|
||||
res.push({
|
||||
key,
|
||||
value: JSON.stringify({
|
||||
...configData,
|
||||
value
|
||||
})
|
||||
})
|
||||
}, []), 'key')
|
||||
}
|
||||
})
|
||||
if (args.filter) { renderers = graphHelper.filter(renderers, args.filter) }
|
||||
if (args.orderBy) { renderers = graphHelper.orderBy(renderers, args.orderBy) }
|
||||
return renderers
|
||||
}
|
||||
},
|
||||
RenderingMutation: {
|
||||
async updateRenderers(obj, args, context) {
|
||||
try {
|
||||
for (let rdr of args.renderers) {
|
||||
await WIKI.models.storage.query().patch({
|
||||
isEnabled: rdr.isEnabled,
|
||||
config: _.reduce(rdr.config, (result, value, key) => {
|
||||
_.set(result, `${value.key}`, value.value)
|
||||
return result
|
||||
}, {})
|
||||
}).where('key', rdr.key)
|
||||
}
|
||||
return {
|
||||
responseResult: graphHelper.generateSuccess('Renderers updated successfully')
|
||||
}
|
||||
} catch (err) {
|
||||
return graphHelper.generateError(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
55
server/graph/schemas/rendering.graphql
Normal file
55
server/graph/schemas/rendering.graphql
Normal file
@ -0,0 +1,55 @@
|
||||
# ===============================================
|
||||
# RENDERING
|
||||
# ===============================================
|
||||
|
||||
extend type Query {
|
||||
rendering: RenderingQuery
|
||||
}
|
||||
|
||||
extend type Mutation {
|
||||
rendering: RenderingMutation
|
||||
}
|
||||
|
||||
# -----------------------------------------------
|
||||
# QUERIES
|
||||
# -----------------------------------------------
|
||||
|
||||
type RenderingQuery {
|
||||
renderers(
|
||||
filter: String
|
||||
orderBy: String
|
||||
): [Renderer]
|
||||
}
|
||||
|
||||
# -----------------------------------------------
|
||||
# MUTATIONS
|
||||
# -----------------------------------------------
|
||||
|
||||
type RenderingMutation {
|
||||
updateRenderers(
|
||||
renderers: [RendererInput]
|
||||
): DefaultResponse
|
||||
}
|
||||
|
||||
# -----------------------------------------------
|
||||
# TYPES
|
||||
# -----------------------------------------------
|
||||
|
||||
type Renderer {
|
||||
isEnabled: Boolean!
|
||||
key: String!
|
||||
title: String!
|
||||
description: String
|
||||
icon: String
|
||||
dependsOn: String
|
||||
input: String
|
||||
output: String
|
||||
config: [KeyValuePair]
|
||||
}
|
||||
|
||||
input RendererInput {
|
||||
isEnabled: Boolean!
|
||||
key: String!
|
||||
mode: String!
|
||||
config: [KeyValuePairInput]
|
||||
}
|
108
server/models/renderers.js
Normal file
108
server/models/renderers.js
Normal file
@ -0,0 +1,108 @@
|
||||
const Model = require('objection').Model
|
||||
const path = require('path')
|
||||
const fs = require('fs-extra')
|
||||
const _ = require('lodash')
|
||||
const yaml = require('js-yaml')
|
||||
const commonHelper = require('../helpers/common')
|
||||
|
||||
/* global WIKI */
|
||||
|
||||
/**
|
||||
* Renderer model
|
||||
*/
|
||||
module.exports = class Renderer extends Model {
|
||||
static get tableName() { return 'renderers' }
|
||||
|
||||
static get jsonSchema () {
|
||||
return {
|
||||
type: 'object',
|
||||
required: ['key', 'isEnabled'],
|
||||
|
||||
properties: {
|
||||
id: {type: 'integer'},
|
||||
key: {type: 'string'},
|
||||
isEnabled: {type: 'boolean'},
|
||||
config: {type: 'object'}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static async getRenderers() {
|
||||
return WIKI.models.renderers.query()
|
||||
}
|
||||
|
||||
static async refreshRenderersFromDisk() {
|
||||
let trx
|
||||
try {
|
||||
const dbRenderers = await WIKI.models.renderers.query()
|
||||
|
||||
// -> Fetch definitions from disk
|
||||
const rendererDirs = await fs.readdir(path.join(WIKI.SERVERPATH, 'modules/renderer'))
|
||||
let diskRenderers = []
|
||||
for (let dir of rendererDirs) {
|
||||
const def = await fs.readFile(path.join(WIKI.SERVERPATH, 'modules/renderer', dir, 'definition.yml'), 'utf8')
|
||||
diskRenderers.push(yaml.safeLoad(def))
|
||||
}
|
||||
WIKI.data.renderers = diskRenderers.map(renderer => ({
|
||||
...renderer,
|
||||
props: commonHelper.parseModuleProps(renderer.props)
|
||||
}))
|
||||
|
||||
// -> Insert new Renderers
|
||||
let newRenderers = []
|
||||
for (let renderer of WIKI.data.renderers) {
|
||||
if (!_.some(dbRenderers, ['key', renderer.key])) {
|
||||
newRenderers.push({
|
||||
key: renderer.key,
|
||||
isEnabled: _.get(renderer, 'enabledDefault', true),
|
||||
config: _.transform(renderer.props, (result, value, key) => {
|
||||
_.set(result, key, value.default)
|
||||
return result
|
||||
}, {})
|
||||
})
|
||||
} else {
|
||||
const rendererConfig = _.get(_.find(dbRenderers, ['key', renderer.key]), 'config', {})
|
||||
await WIKI.models.renderers.query().patch({
|
||||
config: _.transform(renderer.props, (result, value, key) => {
|
||||
if (!_.has(result, key)) {
|
||||
_.set(result, key, value.default)
|
||||
}
|
||||
return result
|
||||
}, rendererConfig)
|
||||
}).where('key', renderer.key)
|
||||
}
|
||||
}
|
||||
if (newRenderers.length > 0) {
|
||||
trx = await WIKI.models.Objection.transaction.start(WIKI.models.knex)
|
||||
for (let renderer of newRenderers) {
|
||||
await WIKI.models.renderers.query(trx).insert(renderer)
|
||||
}
|
||||
await trx.commit()
|
||||
WIKI.logger.info(`Loaded ${newRenderers.length} new renderers: [ OK ]`)
|
||||
} else {
|
||||
WIKI.logger.info(`No new renderers found: [ SKIPPED ]`)
|
||||
}
|
||||
} catch (err) {
|
||||
WIKI.logger.error(`Failed to scan or load new renderers: [ FAILED ]`)
|
||||
WIKI.logger.error(err)
|
||||
if (trx) {
|
||||
trx.rollback()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static async pageEvent({ event, page }) {
|
||||
const targets = await WIKI.models.storage.query().where('isEnabled', true)
|
||||
if (targets && targets.length > 0) {
|
||||
_.forEach(targets, target => {
|
||||
WIKI.queue.job.syncStorage.add({
|
||||
event,
|
||||
target,
|
||||
page
|
||||
}, {
|
||||
removeOnComplete: true
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
@ -1,7 +1,8 @@
|
||||
key: markdownAbbr
|
||||
title: Abbreviations
|
||||
description: Parse abbreviations into abbr tags
|
||||
key: htmlAsciinema
|
||||
title: Asciinema
|
||||
description: Embed asciinema players from compatible links
|
||||
author: requarks.io
|
||||
dependsOn:
|
||||
- markdownCore
|
||||
icon: subtitles
|
||||
enabledDefault: false
|
||||
dependsOn: htmlCore
|
||||
props: {}
|
||||
|
@ -1,7 +1,8 @@
|
||||
key: markdownAbbr
|
||||
title: Abbreviations
|
||||
description: Parse abbreviations into abbr tags
|
||||
key: htmlBlockquotes
|
||||
title: Blockquotes
|
||||
description: Embed audio players for audio content
|
||||
author: requarks.io
|
||||
dependsOn:
|
||||
- markdownCore
|
||||
icon: insert_comment
|
||||
enabledDefault: true
|
||||
dependsOn: htmlCore
|
||||
props: {}
|
||||
|
8
server/modules/renderer/html-core/definition.yml
Normal file
8
server/modules/renderer/html-core/definition.yml
Normal file
@ -0,0 +1,8 @@
|
||||
key: htmlCore
|
||||
title: Core
|
||||
description: Basic HTML Parser
|
||||
author: requarks.io
|
||||
input: html
|
||||
output: html
|
||||
icon: crop_free
|
||||
props: {}
|
1
server/modules/renderer/html-core/renderer.js
Normal file
1
server/modules/renderer/html-core/renderer.js
Normal file
@ -0,0 +1 @@
|
||||
module.exports = {}
|
@ -1,7 +1,8 @@
|
||||
key: markdownAbbr
|
||||
title: Abbreviations
|
||||
description: Parse abbreviations into abbr tags
|
||||
key: htmlMathjax
|
||||
title: Mathjax Processor
|
||||
description: TeX/MathML Math Equations Parser
|
||||
author: requarks.io
|
||||
dependsOn:
|
||||
- markdownCore
|
||||
icon: functions
|
||||
enabledDefault: false
|
||||
dependsOn: htmlCore
|
||||
props: {}
|
||||
|
@ -1,7 +1,8 @@
|
||||
key: markdownAbbr
|
||||
title: Abbreviations
|
||||
description: Parse abbreviations into abbr tags
|
||||
key: htmlMedia
|
||||
title: Media Players
|
||||
description: Embed players such as Youtube, Vimeo, Soundcloud, etc.
|
||||
author: requarks.io
|
||||
dependsOn:
|
||||
- markdownCore
|
||||
icon: subscriptions
|
||||
enabledDefault: false
|
||||
dependsOn: htmlCore
|
||||
props: {}
|
||||
|
@ -1,7 +1,8 @@
|
||||
key: markdownAbbr
|
||||
title: Abbreviations
|
||||
description: Parse abbreviations into abbr tags
|
||||
key: htmlSecurity
|
||||
title: Security
|
||||
description: Filter and strips potentially dangerous content
|
||||
author: requarks.io
|
||||
dependsOn:
|
||||
- markdownCore
|
||||
icon: whatshot
|
||||
enabledDefault: true
|
||||
dependsOn: htmlCore
|
||||
props: {}
|
||||
|
@ -2,6 +2,7 @@ key: markdownAbbr
|
||||
title: Abbreviations
|
||||
description: Parse abbreviations into abbr tags
|
||||
author: requarks.io
|
||||
dependsOn:
|
||||
- markdownCore
|
||||
icon: text_format
|
||||
enabledDefault: true
|
||||
dependsOn: markdownCore
|
||||
props: {}
|
||||
|
@ -2,7 +2,9 @@ key: markdownCore
|
||||
title: Core
|
||||
description: Basic Markdown Parser
|
||||
author: requarks.io
|
||||
dependsOn: []
|
||||
input: markdown
|
||||
output: html
|
||||
icon: crop_free
|
||||
props:
|
||||
linkify:
|
||||
type: Boolean
|
||||
|
@ -2,6 +2,7 @@ key: markdownEmoji
|
||||
title: Emoji
|
||||
description: Convert tags to emojis
|
||||
author: requarks.io
|
||||
dependsOn:
|
||||
- markdownCore
|
||||
icon: tag_faces
|
||||
enabledDefault: true
|
||||
dependsOn: markdownCore
|
||||
props: {}
|
||||
|
@ -2,6 +2,7 @@ key: markdownExpandtabs
|
||||
title: Expand Tabs
|
||||
description: Replace tabs with spaces in code blocks
|
||||
author: requarks.io
|
||||
dependsOn:
|
||||
- markdownCore
|
||||
icon: space_bar
|
||||
enabledDefault: true
|
||||
dependsOn: markdownCore
|
||||
props: {}
|
||||
|
@ -2,6 +2,7 @@ key: markdownFootnotes
|
||||
title: Footnotes
|
||||
description: Parse footnotes references
|
||||
author: requarks.io
|
||||
dependsOn:
|
||||
- markdownCore
|
||||
icon: low_priority
|
||||
enabledDefault: true
|
||||
dependsOn: markdownCore
|
||||
props: {}
|
||||
|
@ -2,6 +2,7 @@ key: markdownMathjax
|
||||
title: Mathjax Pre-Processor
|
||||
description: Pre-parse TeX blocks for Mathjax
|
||||
author: requarks.io
|
||||
dependsOn:
|
||||
- markdownCore
|
||||
icon: functions
|
||||
enabledDefault: false
|
||||
dependsOn: markdownCore
|
||||
props: {}
|
||||
|
@ -2,6 +2,7 @@ key: markdownMermaid
|
||||
title: Mermaid
|
||||
description: Generate flowcharts from Mermaid syntax
|
||||
author: requarks.io
|
||||
dependsOn:
|
||||
- markdownCore
|
||||
icon: merge_type
|
||||
enabledDefault: false
|
||||
dependsOn: markdownCore
|
||||
props: {}
|
||||
|
@ -2,6 +2,7 @@ key: markdownPlantuml
|
||||
title: PlantUML
|
||||
description: Generate diagrams from PlantUML syntax
|
||||
author: requarks.io
|
||||
dependsOn:
|
||||
- markdownCore
|
||||
icon: multiline_chart
|
||||
enabledDefault: false
|
||||
dependsOn: markdownCore
|
||||
props: {}
|
||||
|
@ -2,6 +2,7 @@ key: markdownTasklists
|
||||
title: Task Lists
|
||||
description: Parse task lists to checkboxes
|
||||
author: requarks.io
|
||||
dependsOn:
|
||||
- markdownCore
|
||||
icon: list
|
||||
enabledDefault: true
|
||||
dependsOn: markdownCore
|
||||
props: {}
|
||||
|
188
server/setup.js
188
server/setup.js
@ -24,7 +24,6 @@ module.exports = () => {
|
||||
const yaml = require('js-yaml')
|
||||
const _ = require('lodash')
|
||||
const cfgHelper = require('./helpers/config')
|
||||
const filesize = require('filesize.js')
|
||||
const crypto = Promise.promisifyAll(require('crypto'))
|
||||
|
||||
// ----------------------------------------
|
||||
@ -76,174 +75,6 @@ module.exports = () => {
|
||||
})
|
||||
})
|
||||
|
||||
/**
|
||||
* Perform basic system checks
|
||||
*/
|
||||
app.post('/syscheck', (req, res) => {
|
||||
WIKI.telemetry.enabled = (req.body.telemetry === true)
|
||||
WIKI.telemetry.sendEvent('setup', 'start')
|
||||
|
||||
Promise.mapSeries([
|
||||
() => {
|
||||
const semver = require('semver')
|
||||
if (!semver.satisfies(semver.clean(process.version), '>=8.9.0')) {
|
||||
throw new Error('Node.js version is too old. Minimum is 8.9.0.')
|
||||
}
|
||||
return {
|
||||
title: 'Node.js ' + process.version + ' detected.',
|
||||
subtitle: ' Minimum is 8.9.0.'
|
||||
}
|
||||
},
|
||||
() => {
|
||||
return Promise.try(() => {
|
||||
require('crypto')
|
||||
}).catch(err => {
|
||||
throw new Error('Crypto Node.js module is not available.')
|
||||
}).return({
|
||||
title: 'Node.js Crypto module is available.',
|
||||
subtitle: 'Crypto module is required.'
|
||||
})
|
||||
},
|
||||
() => {
|
||||
const exec = require('child_process').exec
|
||||
const semver = require('semver')
|
||||
return new Promise((resolve, reject) => {
|
||||
exec('git --version', (err, stdout, stderr) => {
|
||||
if (err || stdout.length < 3) {
|
||||
reject(new Error('Git is not installed or not reachable from PATH.'))
|
||||
}
|
||||
let gitver = _.head(stdout.match(/[\d]+\.[\d]+(\.[\d]+)?/gi))
|
||||
if (!gitver || !semver.satisfies(semver.clean(gitver), '>=2.7.4')) {
|
||||
reject(new Error('Git version is too old. Minimum is 2.7.4.'))
|
||||
}
|
||||
resolve({
|
||||
title: 'Git ' + gitver + ' detected.',
|
||||
subtitle: 'Minimum is 2.7.4.'
|
||||
})
|
||||
})
|
||||
})
|
||||
},
|
||||
() => {
|
||||
const os = require('os')
|
||||
if (os.totalmem() < 1000 * 1000 * 768) {
|
||||
throw new Error('Not enough memory. Minimum is 768 MB.')
|
||||
}
|
||||
return {
|
||||
title: filesize(os.totalmem()) + ' of system memory available.',
|
||||
subtitle: 'Minimum is 768 MB.'
|
||||
}
|
||||
},
|
||||
() => {
|
||||
let fs = require('fs')
|
||||
return Promise.try(() => {
|
||||
fs.accessSync(path.join(WIKI.ROOTPATH, 'config.yml'), (fs.constants || fs).W_OK)
|
||||
}).catch(err => {
|
||||
throw new Error('config.yml file is not writable by Node.js process or was not created properly.')
|
||||
}).return({
|
||||
title: 'config.yml is writable by the setup process.',
|
||||
subtitle: 'Setup will write to this file.'
|
||||
})
|
||||
}
|
||||
], test => test()).then(results => {
|
||||
res.json({ ok: true, results })
|
||||
}).catch(err => {
|
||||
res.json({ ok: false, error: err.message })
|
||||
})
|
||||
})
|
||||
|
||||
/**
|
||||
* Check the Git connection
|
||||
*/
|
||||
app.post('/gitcheck', (req, res) => {
|
||||
WIKI.telemetry.sendEvent('setup', 'gitcheck')
|
||||
|
||||
const exec = require('execa')
|
||||
const url = require('url')
|
||||
|
||||
const dataDir = path.resolve(WIKI.ROOTPATH, cfgHelper.parseConfigValue(req.body.pathData))
|
||||
const gitDir = path.resolve(WIKI.ROOTPATH, cfgHelper.parseConfigValue(req.body.pathRepo))
|
||||
|
||||
let gitRemoteUrl = ''
|
||||
|
||||
if (req.body.gitUseRemote === true) {
|
||||
let urlObj = url.parse(cfgHelper.parseConfigValue(req.body.gitUrl))
|
||||
if (req.body.gitAuthType === 'basic') {
|
||||
urlObj.auth = req.body.gitAuthUser + ':' + req.body.gitAuthPass
|
||||
}
|
||||
gitRemoteUrl = url.format(urlObj)
|
||||
}
|
||||
|
||||
Promise.mapSeries([
|
||||
() => {
|
||||
return fs.ensureDir(dataDir).then(() => 'Data directory path is valid.')
|
||||
},
|
||||
() => {
|
||||
return fs.ensureDir(gitDir).then(() => 'Git directory path is valid.')
|
||||
},
|
||||
() => {
|
||||
return exec.stdout('git', ['init'], { cwd: gitDir }).then(result => {
|
||||
return 'Local git repository has been initialized.'
|
||||
})
|
||||
},
|
||||
() => {
|
||||
if (req.body.gitUseRemote === false) { return false }
|
||||
return exec.stdout('git', ['config', '--local', 'user.name', 'Wiki'], { cwd: gitDir }).then(result => {
|
||||
return 'Git Signature Name has been set successfully.'
|
||||
})
|
||||
},
|
||||
() => {
|
||||
if (req.body.gitUseRemote === false) { return false }
|
||||
return exec.stdout('git', ['config', '--local', 'user.email', req.body.gitServerEmail], { cwd: gitDir }).then(result => {
|
||||
return 'Git Signature Name has been set successfully.'
|
||||
})
|
||||
},
|
||||
() => {
|
||||
if (req.body.gitUseRemote === false) { return false }
|
||||
return exec.stdout('git', ['config', '--local', '--bool', 'http.sslVerify', req.body.gitAuthSSL], { cwd: gitDir }).then(result => {
|
||||
return 'Git SSL Verify flag has been set successfully.'
|
||||
})
|
||||
},
|
||||
() => {
|
||||
if (req.body.gitUseRemote === false) { return false }
|
||||
if (_.includes(['sshenv', 'sshdb'], req.body.gitAuthType)) {
|
||||
req.body.gitAuthSSHKey = path.join(dataDir, 'ssh/key.pem')
|
||||
}
|
||||
if (_.startsWith(req.body.gitAuthType, 'ssh')) {
|
||||
return exec.stdout('git', ['config', '--local', 'core.sshCommand', 'ssh -i "' + req.body.gitAuthSSHKey + '" -o StrictHostKeyChecking=no'], { cwd: gitDir }).then(result => {
|
||||
return 'Git SSH Private Key path has been set successfully.'
|
||||
})
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
},
|
||||
() => {
|
||||
if (req.body.gitUseRemote === false) { return false }
|
||||
return exec.stdout('git', ['remote', 'rm', 'origin'], { cwd: gitDir }).catch(err => {
|
||||
if (_.includes(err.message, 'No such remote') || _.includes(err.message, 'Could not remove')) {
|
||||
return true
|
||||
} else {
|
||||
throw err
|
||||
}
|
||||
}).then(() => {
|
||||
return exec.stdout('git', ['remote', 'add', 'origin', gitRemoteUrl], { cwd: gitDir }).then(result => {
|
||||
return 'Git Remote was added successfully.'
|
||||
})
|
||||
})
|
||||
},
|
||||
() => {
|
||||
if (req.body.gitUseRemote === false) { return false }
|
||||
return exec.stdout('git', ['pull', 'origin', req.body.gitBranch], { cwd: gitDir }).then(result => {
|
||||
return 'Git Pull operation successful.'
|
||||
})
|
||||
}
|
||||
], step => { return step() }).then(results => {
|
||||
return res.json({ ok: true, results: _.without(results, false) })
|
||||
}).catch(err => {
|
||||
let errMsg = (err.stderr) ? err.stderr.replace(/(error:|warning:|fatal:)/gi, '').replace(/ \s+/g, ' ') : err.message
|
||||
res.json({ ok: false, error: errMsg })
|
||||
})
|
||||
})
|
||||
|
||||
/**
|
||||
* Finalize
|
||||
*/
|
||||
@ -263,13 +94,6 @@ module.exports = () => {
|
||||
let confRaw = await fs.readFileAsync(path.join(WIKI.ROOTPATH, 'config.yml'), 'utf8')
|
||||
let conf = yaml.safeLoad(confRaw)
|
||||
|
||||
conf.port = req.body.port
|
||||
conf.paths.data = req.body.pathData
|
||||
conf.paths.content = req.body.pathContent
|
||||
|
||||
confRaw = yaml.safeDump(conf)
|
||||
await fs.writeFileAsync(path.join(WIKI.ROOTPATH, 'config.yml'), confRaw)
|
||||
|
||||
// Create directory structure
|
||||
await fs.ensureDir(conf.paths.data)
|
||||
await fs.ensureDir(path.join(conf.paths.data, 'cache'))
|
||||
@ -283,16 +107,13 @@ module.exports = () => {
|
||||
_.set(WIKI.config, 'lang.autoUpdate', true)
|
||||
_.set(WIKI.config, 'lang.namespacing', false)
|
||||
_.set(WIKI.config, 'lang.namespaces', [])
|
||||
_.set(WIKI.config, 'paths.content', req.body.pathContent)
|
||||
_.set(WIKI.config, 'paths.data', req.body.pathData)
|
||||
_.set(WIKI.config, 'port', req.body.port)
|
||||
_.set(WIKI.config, 'public', req.body.public === 'true')
|
||||
_.set(WIKI.config, 'public', false)
|
||||
_.set(WIKI.config, 'sessionSecret', (await crypto.randomBytesAsync(32)).toString('hex'))
|
||||
_.set(WIKI.config, 'telemetry.isEnabled', req.body.telemetry === 'true')
|
||||
_.set(WIKI.config, 'telemetry.clientId', WIKI.telemetry.cid)
|
||||
_.set(WIKI.config, 'theming.theme', 'default')
|
||||
_.set(WIKI.config, 'theming.darkMode', false)
|
||||
_.set(WIKI.config, 'title', req.body.title)
|
||||
_.set(WIKI.config, 'title', 'Wiki.js')
|
||||
|
||||
// Save config to DB
|
||||
WIKI.logger.info('Persisting config to DB...')
|
||||
@ -325,6 +146,9 @@ module.exports = () => {
|
||||
await WIKI.models.editors.refreshEditorsFromDisk()
|
||||
await WIKI.models.editors.query().patch({ isEnabled: true }).where('key', 'markdown')
|
||||
|
||||
// Load renderers
|
||||
await WIKI.models.renderers.refreshRenderersFromDisk()
|
||||
|
||||
// Load storage targets
|
||||
await WIKI.models.storage.refreshTargetsFromDisk()
|
||||
|
||||
@ -367,7 +191,7 @@ module.exports = () => {
|
||||
WIKI.logger.info('Setup is complete!')
|
||||
res.json({
|
||||
ok: true,
|
||||
redirectPath: WIKI.config.site.path,
|
||||
redirectPath: '/',
|
||||
redirectPort: WIKI.config.port
|
||||
}).end()
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user