feat: utilities section (wip) + auth utilities
This commit is contained in:
parent
9cd8657ce6
commit
604941fe6a
@ -83,8 +83,8 @@
|
|||||||
v-list-tile(to='/system', v-if='hasPermission(`manage:system`)')
|
v-list-tile(to='/system', v-if='hasPermission(`manage:system`)')
|
||||||
v-list-tile-avatar: v-icon tune
|
v-list-tile-avatar: v-icon tune
|
||||||
v-list-tile-title {{ $t('admin:system.title') }}
|
v-list-tile-title {{ $t('admin:system.title') }}
|
||||||
v-list-tile(to='/utilities', v-if='hasPermission(`manage:system`)', disabled)
|
v-list-tile(to='/utilities', v-if='hasPermission(`manage:system`)')
|
||||||
v-list-tile-avatar: v-icon(color='grey lighten-2') build
|
v-list-tile-avatar: v-icon build
|
||||||
v-list-tile-title {{ $t('admin:utilities.title') }}
|
v-list-tile-title {{ $t('admin:utilities.title') }}
|
||||||
v-list-tile(to='/webhooks', v-if='hasPermission(`manage:system`)', disabled)
|
v-list-tile(to='/webhooks', v-if='hasPermission(`manage:system`)', disabled)
|
||||||
v-list-tile-avatar: v-icon(color='grey lighten-2') ac_unit
|
v-list-tile-avatar: v-icon(color='grey lighten-2') ac_unit
|
||||||
|
93
client/components/admin/admin-utilities-auth.vue
Normal file
93
client/components/admin/admin-utilities-auth.vue
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
<template lang='pug'>
|
||||||
|
v-card
|
||||||
|
v-toolbar(flat, color='primary', dark, dense)
|
||||||
|
.subheading {{ $t('admin:utilities.authTitle') }}
|
||||||
|
v-card-text
|
||||||
|
v-subheader.pl-0 Generate New Authentication Public / Private Key Certificates
|
||||||
|
.body-1 This will invalidate all current session tokens and cause all users to be logged out.
|
||||||
|
.body-1.red--text You will need to log back in after the operation.
|
||||||
|
v-btn(outline, color='primary', @click='regenCerts', :disabled='loading').ml-0.mt-3
|
||||||
|
v-icon(left) build
|
||||||
|
span Proceed
|
||||||
|
v-divider.my-3
|
||||||
|
v-subheader.pl-0 Reset Guest User
|
||||||
|
.body-1 This will reset the guest user to its default parameters and permissions.
|
||||||
|
v-btn(outline, color='primary', @click='resetGuest', :disabled='loading').ml-0.mt-3
|
||||||
|
v-icon(left) build
|
||||||
|
span Proceed
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import _ from 'lodash'
|
||||||
|
import Cookies from 'js-cookie'
|
||||||
|
import utilityAuthRegencertsMutation from 'gql/admin/utilities/utilities-mutation-auth-regencerts.gql'
|
||||||
|
import utilityAuthResetguestMutation from 'gql/admin/utilities/utilities-mutation-auth-resetguest.gql'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data: () => {
|
||||||
|
return {
|
||||||
|
loading: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async regenCerts() {
|
||||||
|
this.loading = true
|
||||||
|
this.$store.commit(`loadingStart`, 'admin-utilities-auth-regencerts')
|
||||||
|
|
||||||
|
try {
|
||||||
|
const respRaw = await this.$apollo.mutate({
|
||||||
|
mutation: utilityAuthRegencertsMutation
|
||||||
|
})
|
||||||
|
const resp = _.get(respRaw, 'data.authentication.regenerateCertificates.responseResult', {})
|
||||||
|
if (resp.succeeded) {
|
||||||
|
this.$store.commit('showNotification', {
|
||||||
|
message: 'New Certificates generated successfully.',
|
||||||
|
style: 'success',
|
||||||
|
icon: 'check'
|
||||||
|
})
|
||||||
|
Cookies.remove('jwt')
|
||||||
|
_.delay(() => {
|
||||||
|
window.location.assign('/login')
|
||||||
|
}, 1000)
|
||||||
|
} else {
|
||||||
|
throw new Error(resp.message)
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
this.$store.commit('pushGraphError', err)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$store.commit(`loadingStop`, 'admin-utilities-auth-regencerts')
|
||||||
|
this.loading = false
|
||||||
|
},
|
||||||
|
async resetGuest() {
|
||||||
|
this.loading = true
|
||||||
|
this.$store.commit(`loadingStart`, 'admin-utilities-auth-resetguest')
|
||||||
|
|
||||||
|
try {
|
||||||
|
const respRaw = await this.$apollo.mutate({
|
||||||
|
mutation: utilityAuthResetguestMutation
|
||||||
|
})
|
||||||
|
const resp = _.get(respRaw, 'data.authentication.resetGuestUser.responseResult', {})
|
||||||
|
if (resp.succeeded) {
|
||||||
|
this.$store.commit('showNotification', {
|
||||||
|
message: 'Guest user was reset successfully.',
|
||||||
|
style: 'success',
|
||||||
|
icon: 'check'
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
throw new Error(resp.message)
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
this.$store.commit('pushGraphError', err)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$store.commit(`loadingStop`, 'admin-utilities-auth-resetguest')
|
||||||
|
this.loading = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang='scss'>
|
||||||
|
|
||||||
|
</style>
|
32
client/components/admin/admin-utilities-cache.vue
Normal file
32
client/components/admin/admin-utilities-cache.vue
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
<template lang='pug'>
|
||||||
|
v-card
|
||||||
|
v-toolbar(flat, color='primary', dark, dense)
|
||||||
|
.subheading {{ $t('admin:utilities.cacheTitle') }}
|
||||||
|
v-card-text
|
||||||
|
v-subheader.pl-0 Flush Pages Cache
|
||||||
|
.body-1 Pages are cached to disk for better performance. You can flush the cache to force all content to be fetched from the DB again.
|
||||||
|
v-btn(outline, color='primary').ml-0.mt-3
|
||||||
|
v-icon(left) build
|
||||||
|
span Proceed
|
||||||
|
v-divider.my-3
|
||||||
|
v-subheader.pl-0 Flush Assets Cache
|
||||||
|
.body-1 Assets such as images and other files are cached to disk for better performance. You can flush the cache to force all assets to be fetched from the DB again.
|
||||||
|
v-btn(outline, color='primary').ml-0.mt-3
|
||||||
|
v-icon(left) build
|
||||||
|
span Proceed
|
||||||
|
v-divider.my-3
|
||||||
|
v-subheader.pl-0 Flush Temporary Uploads
|
||||||
|
.body-1 New uploads are temporarily saved to disk while they are being processed. They are automatically deleted after processing, but you can force an immediate cleanup using this tool.
|
||||||
|
v-btn(outline, color='primary').ml-0.mt-3
|
||||||
|
v-icon(left) build
|
||||||
|
span Proceed
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang='scss'>
|
||||||
|
|
||||||
|
</style>
|
77
client/components/admin/admin-utilities-importv1.vue
Normal file
77
client/components/admin/admin-utilities-importv1.vue
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
<template lang='pug'>
|
||||||
|
v-card.wiki-form
|
||||||
|
v-toolbar(flat, color='primary', dark, dense)
|
||||||
|
.subheading {{ $t('admin:utilities.importv1Title') }}
|
||||||
|
v-card-text
|
||||||
|
.text-xs-center
|
||||||
|
img.animated.fadeInUp.wait-p1s(src='/svg/icon-software.svg')
|
||||||
|
.body-2 Import from Wiki.js 1.x
|
||||||
|
v-divider.my-4
|
||||||
|
.body-1 Data from a Wiki.js 1.x installation can be imported easily using this tool. What do you want to import?
|
||||||
|
v-checkbox(
|
||||||
|
label='Content'
|
||||||
|
value='content'
|
||||||
|
color='deep-orange darken-2'
|
||||||
|
v-model='importFilters'
|
||||||
|
hide-details
|
||||||
|
)
|
||||||
|
v-checkbox(
|
||||||
|
label='Uploads'
|
||||||
|
value='uploads'
|
||||||
|
color='deep-orange darken-2'
|
||||||
|
v-model='importFilters'
|
||||||
|
hide-details
|
||||||
|
)
|
||||||
|
v-checkbox(
|
||||||
|
label='Users'
|
||||||
|
value='users'
|
||||||
|
color='deep-orange darken-2'
|
||||||
|
v-model='importFilters'
|
||||||
|
hide-details
|
||||||
|
)
|
||||||
|
v-divider.my-3
|
||||||
|
v-text-field.mt-3(
|
||||||
|
outline
|
||||||
|
label='MongoDB Connection String'
|
||||||
|
hint='The connection string to connect to the Wiki.js 1.x MongoDB database.'
|
||||||
|
persistent-hint
|
||||||
|
v-model='dbConnStr'
|
||||||
|
v-if='needDB'
|
||||||
|
)
|
||||||
|
v-text-field.mt-3(
|
||||||
|
outline
|
||||||
|
label='Content Repo Path'
|
||||||
|
hint='The full path to where the Wiki.js 1.x content is stored on disk.'
|
||||||
|
persistent-hint
|
||||||
|
v-model='contentPath'
|
||||||
|
v-if='needDisk'
|
||||||
|
)
|
||||||
|
v-card-chin
|
||||||
|
v-btn(depressed, color='deep-orange darken-2', :disabled='!needDB && !needDisk').ml-0
|
||||||
|
v-icon(left, color='white') label_important
|
||||||
|
span.white--text Start Import
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
importFilters: ['content', 'uploads'],
|
||||||
|
dbConnStr: 'mongodb://',
|
||||||
|
contentPath: '/wiki-v1/repo'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
needDB() {
|
||||||
|
return this.importFilters.indexOf('users') >= 0
|
||||||
|
},
|
||||||
|
needDisk() {
|
||||||
|
return this.importFilters.indexOf('content') >= 0 || this.importFilters.indexOf('uploads') >= 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang='scss'>
|
||||||
|
|
||||||
|
</style>
|
80
client/components/admin/admin-utilities-telemetry.vue
Normal file
80
client/components/admin/admin-utilities-telemetry.vue
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
<template lang='pug'>
|
||||||
|
v-card
|
||||||
|
v-toolbar(flat, color='primary', dark, dense)
|
||||||
|
.subheading {{ $t('admin:utilities.telemetryTitle') }}
|
||||||
|
v-form
|
||||||
|
v-card-text
|
||||||
|
v-subheader What is telemetry?
|
||||||
|
.body-1.pl-3 Telemetry allows the developers of Wiki.js to improve the software by collecting basic anonymized data about its usage and the host info. #[br] This is entirely optional and #[strong absolutely no] private data (such as content or personal data) is collected.
|
||||||
|
.body-1.pt-3.pl-3 For maximum privacy, a random client ID is generated during setup. This ID is used to group requests together while keeping complete anonymity. You can reset and generate a new one below at any time.
|
||||||
|
v-divider.my-3
|
||||||
|
v-subheader What is collected?
|
||||||
|
.body-1.pl-3 When telemetry is enabled, only the following data is transmitted:
|
||||||
|
v-list
|
||||||
|
v-list-tile
|
||||||
|
v-list-tile-avatar: v-icon info_outline
|
||||||
|
v-list-tile-content
|
||||||
|
v-list-tile-title.body-1 Version of Wiki.js installed
|
||||||
|
v-list-tile-subtitle.caption: em e.g. v2.0.123
|
||||||
|
v-list-tile
|
||||||
|
v-list-tile-avatar: v-icon info_outline
|
||||||
|
v-list-tile-content
|
||||||
|
v-list-tile-title.body-1 Basic OS information
|
||||||
|
v-list-tile-subtitle.caption: em Platform (Linux, macOS or Windows), Total CPU cores and DB type (PostgreSQL, MySQL, MariaDB, SQLite or SQL Server)
|
||||||
|
v-list-tile
|
||||||
|
v-list-tile-avatar: v-icon info_outline
|
||||||
|
v-list-tile-content
|
||||||
|
v-list-tile-title.body-1 Crash debug data
|
||||||
|
v-list-tile-subtitle.caption: em Stack trace of the error
|
||||||
|
v-list-tile
|
||||||
|
v-list-tile-avatar: v-icon info_outline
|
||||||
|
v-list-tile-content
|
||||||
|
v-list-tile-title.body-1 Setup analytics
|
||||||
|
v-list-tile-subtitle.caption: em Installation checkpoint reached
|
||||||
|
.body-1.pl-3 Note that any data collected is stored for a maximum of 180 days, after which it is permanently deleted.
|
||||||
|
v-divider.my-3
|
||||||
|
v-subheader What is it used for?
|
||||||
|
.body-1.pl-3 Telemetry is used by developers to improve Wiki.js, mostly for the following reasons:
|
||||||
|
v-list(dense)
|
||||||
|
v-list-tile
|
||||||
|
v-list-tile-avatar: v-icon info_outline
|
||||||
|
v-list-tile-content: v-list-tile-title.body-1 Identify critical bugs more easily and fix them in a timely manner.
|
||||||
|
v-list-tile
|
||||||
|
v-list-tile-avatar: v-icon info_outline
|
||||||
|
v-list-tile-content: v-list-tile-title.body-1 Understand the upgrade rate of current installations.
|
||||||
|
v-list-tile
|
||||||
|
v-list-tile-avatar: v-icon info_outline
|
||||||
|
v-list-tile-content: v-list-tile-title.body-1 Optimize performance and testing scenarios based on most popular environments.
|
||||||
|
.body-1.pl-3 Only authorized developers have access to the data. It is not shared to any 3rd party nor is it used for any other application than improving Wiki.js.
|
||||||
|
v-divider.my-3
|
||||||
|
v-subheader Settings
|
||||||
|
.pl-3
|
||||||
|
v-switch.mt-0(
|
||||||
|
v-model='telemetry',
|
||||||
|
label='Enable Telemetry',
|
||||||
|
:value='true',
|
||||||
|
color='primary',
|
||||||
|
hint='Allow Wiki.js to transmit telemetry data.',
|
||||||
|
persistent-hint
|
||||||
|
)
|
||||||
|
.subheading.mt-3.grey--text.text--darken-1 Client ID
|
||||||
|
.body-1 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||||
|
v-card-chin
|
||||||
|
v-btn(depressed, color='success', @click='updateTelemetry')
|
||||||
|
v-icon(left) chevron_right
|
||||||
|
| Save Changes
|
||||||
|
v-spacer
|
||||||
|
v-btn(outline, color='grey', @click='resetClientId')
|
||||||
|
v-icon(left) autorenew
|
||||||
|
span Reset Client ID
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang='scss'>
|
||||||
|
|
||||||
|
</style>
|
@ -5,196 +5,75 @@
|
|||||||
.admin-header
|
.admin-header
|
||||||
img(src='/svg/icon-maintenance.svg', alt='Utilities', style='width: 80px;')
|
img(src='/svg/icon-maintenance.svg', alt='Utilities', style='width: 80px;')
|
||||||
.admin-header-title
|
.admin-header-title
|
||||||
.headline.primary--text Utilities
|
.headline.primary--text {{$t('admin:utilities.title')}}
|
||||||
.subheading.grey--text Maintenance and troubleshooting tools
|
.subheading.grey--text {{$t('admin:utilities.subtitle')}}
|
||||||
|
|
||||||
v-card.mt-3
|
v-flex(lg3, xs12)
|
||||||
v-tabs(color='grey darken-2', fixed-tabs, slider-color='white', show-arrows, dark)
|
v-card.animated.fadeInUp
|
||||||
v-tab(key='tools') Tools
|
v-toolbar(flat, color='primary', dark, dense)
|
||||||
v-tab(key='cache') Cache
|
.subheading {{$t('admin:utilities.tools')}}
|
||||||
v-tab(key='telemetry') Telemetry
|
v-list(two-line, dense).py-0
|
||||||
v-tab(key='support') Support
|
template(v-for='(tool, idx) in tools')
|
||||||
|
v-list-tile(:key='tool.key', @click='selectedTool = tool.key', :disabled='!tool.isAvailable')
|
||||||
|
v-list-tile-avatar
|
||||||
|
v-icon(:color='!tool.isAvailable ? `grey lighten-1` : (selectedTool === tool.key ? `blue ` : `grey darken-1`)') {{ tool.icon }}
|
||||||
|
v-list-tile-content
|
||||||
|
v-list-tile-title.body-2(:class='!tool.isAvailable ? `grey--text` : (selectedTool === tool.key ? `primary--text` : ``)') {{ $t('admin:utilities.' + tool.i18nKey + 'Title') }}
|
||||||
|
v-list-tile-sub-title.caption(:class='!tool.isAvailable ? `grey--text text--lighten-1` : (selectedTool === tool.key ? `blue--text ` : ``)') {{ $t('admin:utilities.' + tool.i18nKey + 'Subtitle') }}
|
||||||
|
v-list-tile-avatar(v-if='selectedTool === tool.key')
|
||||||
|
v-icon.animated.fadeInLeft(color='primary') arrow_forward_ios
|
||||||
|
v-divider(v-if='idx < tools.length - 1')
|
||||||
|
|
||||||
v-tab-item(key='tools', :transition='false', :reverse-transition='false')
|
v-flex.animated.fadeInUp.wait-p2s(xs12, lg9)
|
||||||
v-container.pa-2(fluid, grid-list-sm, :class='$vuetify.dark ? "" : "grey lighten-5"')
|
transition(name='admin-router')
|
||||||
v-layout(row, wrap)
|
component(:is='selectedTool')
|
||||||
v-flex(xs12, sm6)
|
|
||||||
v-card
|
|
||||||
v-toolbar(:color='$vuetify.dark ? "" : "grey darken-3"', dark, dense, flat)
|
|
||||||
v-toolbar-title
|
|
||||||
.subheading Authentication
|
|
||||||
v-spacer
|
|
||||||
v-chip(label, color='white', small).grey--text.text--darken-2 coming soon
|
|
||||||
v-subheader Flush User Sessions
|
|
||||||
v-card-text.pt-0.pl-4
|
|
||||||
.body-1 This will cause all users to be logged out. You will need to log back in after the operation.
|
|
||||||
v-btn(depressed).ml-0
|
|
||||||
v-icon(left, color='grey') build
|
|
||||||
span Proceed
|
|
||||||
v-divider.my-0
|
|
||||||
v-subheader Reset Guest User
|
|
||||||
v-card-text.pt-0.pl-4
|
|
||||||
.body-1 This will reset the guest user to its default parameters and permissions.
|
|
||||||
v-btn(depressed).ml-0
|
|
||||||
v-icon(left, color='grey') build
|
|
||||||
span Proceed
|
|
||||||
v-card.mt-3
|
|
||||||
v-toolbar(:color='$vuetify.dark ? "" : "grey darken-3"', dark, dense, flat)
|
|
||||||
v-toolbar-title
|
|
||||||
.subheading Modules
|
|
||||||
v-spacer
|
|
||||||
v-chip(label, color='white', small).grey--text.text--darken-2 coming soon
|
|
||||||
v-subheader Rescan Modules
|
|
||||||
v-card-text.pt-0.pl-4
|
|
||||||
.body-1 Look for new modules on disk. Existing configurations will be merged.
|
|
||||||
v-select.mt-3(
|
|
||||||
v-model='rescanModuleType'
|
|
||||||
:items='moduleTypes'
|
|
||||||
label='Modules Type'
|
|
||||||
outline
|
|
||||||
background-color='grey lighten-1'
|
|
||||||
hide-details
|
|
||||||
dense
|
|
||||||
)
|
|
||||||
v-btn.ml-0(color='primary', depressed, dark)
|
|
||||||
v-icon(left) chevron_right
|
|
||||||
span Rescan
|
|
||||||
v-flex(xs12, sm6)
|
|
||||||
v-card
|
|
||||||
v-toolbar(:color='$vuetify.dark ? "" : "grey darken-3"', dark, dense, flat)
|
|
||||||
v-toolbar-title
|
|
||||||
.subheading Maintenance Mode
|
|
||||||
v-spacer
|
|
||||||
v-chip(label, color='white', small).grey--text.text--darken-2 coming soon
|
|
||||||
v-card-text
|
|
||||||
.body-1 Maintenance mode restrict access to the site to administrators only, regarless of current permissions.
|
|
||||||
v-btn.mt-3.ml-0(color='orange darken-2', depressed, dark)
|
|
||||||
icon-home-alert.mr-2(fillColor='#FFFFFF')
|
|
||||||
| Turn On Maintenance Mode
|
|
||||||
v-card.mt-3
|
|
||||||
v-toolbar(:color='$vuetify.dark ? "" : "grey darken-3"', dark, dense, flat)
|
|
||||||
v-toolbar-title
|
|
||||||
.subheading Graph Endpoint
|
|
||||||
v-spacer
|
|
||||||
v-chip(label, color='white', small).grey--text.text--darken-2 coming soon
|
|
||||||
v-card-text
|
|
||||||
.body-1 The Graph API Endpoint from which remote resources like locales, themes and plugins are fetched.
|
|
||||||
.caption.red--text Do not change unless you know what you're doing!
|
|
||||||
v-text-field.my-2(outline, hide-details, background-color='grey lighten-1', label='Graph Endpoint', value='https://graph.requarks.io')
|
|
||||||
v-btn.ml-0(color='primary', depressed, dark)
|
|
||||||
v-icon(left) chevron_right
|
|
||||||
span Save
|
|
||||||
|
|
||||||
v-tab-item(key='cache', :transition='false', :reverse-transition='false')
|
|
||||||
v-card
|
|
||||||
v-card-text Coming soon
|
|
||||||
|
|
||||||
v-tab-item(key='telemetry', :transition='false', :reverse-transition='false')
|
|
||||||
v-card
|
|
||||||
v-form
|
|
||||||
v-card-text
|
|
||||||
v-subheader What is telemetry?
|
|
||||||
.body-1.pl-3 Telemetry allows the developers of Wiki.js to improve the software by collecting basic anonymized data about its usage and the host info. #[br] This is entirely optional and #[strong absolutely no] private data (such as content or personal data) is collected.
|
|
||||||
.body-1.pt-3.pl-3 For maximum privacy, a random client ID is generated during setup. This ID is used to group requests together while keeping complete anonymity. You can reset and generate a new one below at any time.
|
|
||||||
v-divider.my-3
|
|
||||||
v-subheader What is collected?
|
|
||||||
.body-1.pl-3 When telemetry is enabled, only the following data is transmitted:
|
|
||||||
v-list(dense)
|
|
||||||
v-list-tile
|
|
||||||
v-list-tile-avatar: v-icon info_outline
|
|
||||||
v-list-tile-content: v-list-tile-title.caption Version of Wiki.js installed
|
|
||||||
v-list-tile
|
|
||||||
v-list-tile-avatar: v-icon info_outline
|
|
||||||
v-list-tile-content: v-list-tile-title.caption Basic OS information (platform, CPU cores count, DB type)
|
|
||||||
v-list-tile
|
|
||||||
v-list-tile-avatar: v-icon info_outline
|
|
||||||
v-list-tile-content: v-list-tile-title.caption Crash debug data
|
|
||||||
v-list-tile
|
|
||||||
v-list-tile-avatar: v-icon info_outline
|
|
||||||
v-list-tile-content: v-list-tile-title.caption Setup analytics (installation checkpoint reached)
|
|
||||||
.body-2.pl-3
|
|
||||||
v-divider.my-3
|
|
||||||
v-subheader Settings
|
|
||||||
.pl-3
|
|
||||||
v-switch.mt-0(
|
|
||||||
v-model='telemetry',
|
|
||||||
label='Enable Telemetry',
|
|
||||||
:value='true',
|
|
||||||
color='primary',
|
|
||||||
hint='Allow Wiki.js to transmit telemetry data.',
|
|
||||||
persistent-hint
|
|
||||||
)
|
|
||||||
.subheading.mt-3.grey--text.text--darken-1 Client ID
|
|
||||||
.body-1 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
|
||||||
v-card-chin
|
|
||||||
v-btn(color='primary', @click='updateTelemetry')
|
|
||||||
v-icon(left) chevron_right
|
|
||||||
| Save Changes
|
|
||||||
v-spacer
|
|
||||||
v-btn(outline, color='grey', @click='resetClientId')
|
|
||||||
v-icon(left) autorenew
|
|
||||||
span Reset Client ID
|
|
||||||
|
|
||||||
v-tab-item(key='support', :transition='false', :reverse-transition='false')
|
|
||||||
v-card.pa-3
|
|
||||||
v-subheader Report a bug
|
|
||||||
.body-1.pl-3 Bugs can be reported using GitHub issues on the project repository page.
|
|
||||||
v-btn.ml-3.mt-3(depressed, dark, color='grey darken-2', href='https://github.com/Requarks/wiki/issues', target='_blank')
|
|
||||||
icon-github-circle.mr-3(fillColor='#FFFFFF')
|
|
||||||
span Submit an issue
|
|
||||||
v-divider.my-3
|
|
||||||
v-subheader Suggest a New Feature / Enhancement
|
|
||||||
.body-1.pl-3 Have an idea for a new feature or something that could be improved?
|
|
||||||
v-btn.ml-3.mt-3(depressed, dark, color='indigo', href='https://requests.requarks.io/wiki', target='_blank')
|
|
||||||
v-icon(left) lightbulb_outline
|
|
||||||
span Submit an idea
|
|
||||||
v-divider.my-3
|
|
||||||
v-subheader Questions / Comments
|
|
||||||
.body-1.pl-3 Join our gitter channel. We are very active and friendly!
|
|
||||||
v-btn.ml-3.mt-3(depressed, dark, color='pink', href='https://gitter.im/Requarks/wiki', target='_blank')
|
|
||||||
v-icon(left) chat
|
|
||||||
span Launch Gitter
|
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import IconGithubCircle from 'mdi/GithubCircle'
|
|
||||||
import IconHomeAlert from 'mdi/HomeAlert'
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
IconGithubCircle,
|
UtilityAuth: () => import(/* webpackChunkName: "admin" */ './admin-utilities-auth.vue'),
|
||||||
IconHomeAlert
|
UtilityCache: () => import(/* webpackChunkName: "admin" */ './admin-utilities-cache.vue'),
|
||||||
|
UtilityImportv1: () => import(/* webpackChunkName: "admin" */ './admin-utilities-importv1.vue'),
|
||||||
|
UtilityTelemetry: () => import(/* webpackChunkName: "admin" */ './admin-utilities-telemetry.vue')
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
tab: '0',
|
selectedTool: 'UtilityAuth',
|
||||||
moduleTypes: [
|
tools: [
|
||||||
{ text: 'Authentication', value: 'authentication' },
|
{
|
||||||
{ text: 'Editor', value: 'editor' },
|
key: 'UtilityAuth',
|
||||||
{ text: 'Logging', value: 'logging' },
|
icon: 'lock_outline',
|
||||||
{ text: 'Rendering', value: 'renderer' },
|
i18nKey: 'auth',
|
||||||
{ text: 'Search Engine', value: 'search' },
|
isAvailable: true
|
||||||
{ text: 'Storage', value: 'storage' }
|
},
|
||||||
],
|
{
|
||||||
rescanModuleType: 'authentication',
|
key: 'UtilityCache',
|
||||||
telemetry: true
|
icon: 'invert_colors',
|
||||||
}
|
i18nKey: 'cache',
|
||||||
},
|
isAvailable: true
|
||||||
methods: {
|
},
|
||||||
updateTelemetry() {
|
{
|
||||||
this.$store.commit('showNotification', {
|
key: 'UtilityGraphEndpoint',
|
||||||
style: 'indigo',
|
icon: 'settings_ethernet',
|
||||||
message: `Coming soon...`,
|
i18nKey: 'graphEndpoint',
|
||||||
icon: 'directions_boat'
|
isAvailable: false
|
||||||
})
|
},
|
||||||
},
|
{
|
||||||
resetClientId() {
|
key: 'UtilityImportv1',
|
||||||
this.$store.commit('showNotification', {
|
icon: 'present_to_all',
|
||||||
style: 'indigo',
|
i18nKey: 'importv1',
|
||||||
message: `Coming soon...`,
|
isAvailable: true
|
||||||
icon: 'directions_boat'
|
},
|
||||||
})
|
{
|
||||||
|
key: 'UtilityTelemetry',
|
||||||
|
icon: 'wifi_tethering',
|
||||||
|
i18nKey: 'telemetry',
|
||||||
|
isAvailable: true
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,12 @@
|
|||||||
|
mutation {
|
||||||
|
authentication {
|
||||||
|
regenerateCertificates {
|
||||||
|
responseResult {
|
||||||
|
succeeded
|
||||||
|
errorCode
|
||||||
|
slug
|
||||||
|
message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
mutation {
|
||||||
|
authentication {
|
||||||
|
resetGuestUser {
|
||||||
|
responseResult {
|
||||||
|
succeeded
|
||||||
|
errorCode
|
||||||
|
slug
|
||||||
|
message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
13
client/static/svg/icon-software.svg
Normal file
13
client/static/svg/icon-software.svg
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 22.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="Layer_1" x="0px" y="0px" viewBox="0 0 48 48" style="enable-background:new 0 0 48 48;" xml:space="preserve" width="96px" height="96px">
|
||||||
|
<path style="fill:#891212;" d="M24,28L6,35V20l18-7V28z"/>
|
||||||
|
<path style="fill:#891212;" d="M42,20l-18-7v15l18,7V20z"/>
|
||||||
|
<path style="fill:#891212;" d="M26,18.5c0,1.379-1.121,2.5-2.5,2.5S21,19.879,21,18.5s1.121-2.5,2.5-2.5S26,17.121,26,18.5z"/>
|
||||||
|
<path style="fill:#90CAF9;" d="M23.5,6C16.598,6,11,11.598,11,18.5S16.598,31,23.5,31S36,25.402,36,18.5S30.402,6,23.5,6z M23.5,20.805c-1.273,0-2.305-1.031-2.305-2.305s1.031-2.305,2.305-2.305s2.305,1.031,2.305,2.305S24.77,20.805,23.5,20.805z"/>
|
||||||
|
<path style="fill:#CFE8F9;" d="M20.676,19.844L13,25.344c0.879,1.316,2.516,2.938,4.188,4l5.43-7.531 C21.684,21.516,20.957,20.781,20.676,19.844z"/>
|
||||||
|
<path style="fill:#CFE8F9;" d="M30.27,8l-5.965,8.164c0.938,0.266,1.68,0.977,1.988,1.895l8.117-5.648 C33.41,10.621,31.984,9.109,30.27,8z"/>
|
||||||
|
<path style="fill:#F44336;" d="M24,27L6,20v15l18,7V27z"/>
|
||||||
|
<path style="fill:#C62828;" d="M42,35l-18,7V27l18-7V35z"/>
|
||||||
|
<path style="fill:#1E88E5;" d="M23.5,15c-1.934,0-3.5,1.566-3.5,3.5s1.566,3.5,3.5,3.5s3.5-1.566,3.5-3.5S25.434,15,23.5,15z M23.5,20c-0.828,0-1.5-0.672-1.5-1.5s0.672-1.5,1.5-1.5s1.5,0.672,1.5,1.5S24.328,20,23.5,20z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.5 KiB |
@ -46,12 +46,12 @@ const init = {
|
|||||||
atomic: 400
|
atomic: 400
|
||||||
})
|
})
|
||||||
devWatcher.on('ready', () => {
|
devWatcher.on('ready', () => {
|
||||||
devWatcher.on('all', () => {
|
devWatcher.on('all', _.debounce(() => {
|
||||||
console.warn(chalk.yellow.bold('--- >>>>>>>>>>>>>>>>>>>>>>>>>>>> ---'))
|
console.warn(chalk.yellow.bold('--- >>>>>>>>>>>>>>>>>>>>>>>>>>>> ---'))
|
||||||
console.warn(chalk.yellow.bold('--- Changes detected: Restarting ---'))
|
console.warn(chalk.yellow.bold('--- Changes detected: Restarting ---'))
|
||||||
console.warn(chalk.yellow.bold('--- <<<<<<<<<<<<<<<<<<<<<<<<<<<< ---'))
|
console.warn(chalk.yellow.bold('--- <<<<<<<<<<<<<<<<<<<<<<<<<<<< ---'))
|
||||||
this.reload()
|
this.reload()
|
||||||
})
|
}, 500))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
@ -4,6 +4,9 @@ const _ = require('lodash')
|
|||||||
const path = require('path')
|
const path = require('path')
|
||||||
const jwt = require('jsonwebtoken')
|
const jwt = require('jsonwebtoken')
|
||||||
const moment = require('moment')
|
const moment = require('moment')
|
||||||
|
const Promise = require('bluebird')
|
||||||
|
const crypto = Promise.promisifyAll(require('crypto'))
|
||||||
|
const pem2jwk = require('pem-jwk').pem2jwk
|
||||||
|
|
||||||
const securityHelper = require('../helpers/security')
|
const securityHelper = require('../helpers/security')
|
||||||
|
|
||||||
@ -236,5 +239,72 @@ module.exports = {
|
|||||||
async reloadGroups() {
|
async reloadGroups() {
|
||||||
const groupsArray = await WIKI.models.groups.query()
|
const groupsArray = await WIKI.models.groups.query()
|
||||||
this.groups = _.keyBy(groupsArray, 'id')
|
this.groups = _.keyBy(groupsArray, 'id')
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate New Authentication Public / Private Key Certificates
|
||||||
|
*/
|
||||||
|
async regenerateCertificates() {
|
||||||
|
WIKI.logger.info('Regenerating certificates...')
|
||||||
|
|
||||||
|
_.set(WIKI.config, 'sessionSecret', (await crypto.randomBytesAsync(32)).toString('hex'))
|
||||||
|
const certs = crypto.generateKeyPairSync('rsa', {
|
||||||
|
modulusLength: 2048,
|
||||||
|
publicKeyEncoding: {
|
||||||
|
type: 'pkcs1',
|
||||||
|
format: 'pem'
|
||||||
|
},
|
||||||
|
privateKeyEncoding: {
|
||||||
|
type: 'pkcs1',
|
||||||
|
format: 'pem',
|
||||||
|
cipher: 'aes-256-cbc',
|
||||||
|
passphrase: WIKI.config.sessionSecret
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
_.set(WIKI.config, 'certs', {
|
||||||
|
jwk: pem2jwk(certs.publicKey),
|
||||||
|
public: certs.publicKey,
|
||||||
|
private: certs.privateKey
|
||||||
|
})
|
||||||
|
|
||||||
|
await WIKI.configSvc.saveToDb([
|
||||||
|
'certs',
|
||||||
|
'sessionSecret'
|
||||||
|
])
|
||||||
|
|
||||||
|
await WIKI.auth.activateStrategies()
|
||||||
|
|
||||||
|
WIKI.logger.info('Regenerated certificates: [ COMPLETED ]')
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset Guest User
|
||||||
|
*/
|
||||||
|
async resetGuestUser() {
|
||||||
|
WIKI.logger.info('Resetting guest account...')
|
||||||
|
const guestGroup = await WIKI.models.groups.query().where('id', 2).first()
|
||||||
|
|
||||||
|
await WIKI.models.users.query().delete().where({
|
||||||
|
providerKey: 'local',
|
||||||
|
email: 'guest@example.com'
|
||||||
|
}).orWhere('id', 2)
|
||||||
|
|
||||||
|
const guestUser = await WIKI.models.users.query().insert({
|
||||||
|
id: 2,
|
||||||
|
provider: 'local',
|
||||||
|
email: 'guest@example.com',
|
||||||
|
name: 'Guest',
|
||||||
|
password: '',
|
||||||
|
locale: 'en',
|
||||||
|
defaultEditor: 'markdown',
|
||||||
|
tfaIsActive: false,
|
||||||
|
isSystem: true,
|
||||||
|
isActive: true,
|
||||||
|
isVerified: true
|
||||||
|
})
|
||||||
|
await guestUser.$relatedQuery('groups').relate(guestGroup.id)
|
||||||
|
|
||||||
|
WIKI.logger.info('Guest user has been reset: [ COMPLETED ]')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,9 @@ module.exports = {
|
|||||||
async authentication() { return {} }
|
async authentication() { return {} }
|
||||||
},
|
},
|
||||||
AuthenticationQuery: {
|
AuthenticationQuery: {
|
||||||
|
/**
|
||||||
|
* Fetch active authentication strategies
|
||||||
|
*/
|
||||||
async strategies(obj, args, context, info) {
|
async strategies(obj, args, context, info) {
|
||||||
let strategies = await WIKI.models.authentication.getStrategies(args.isEnabled)
|
let strategies = await WIKI.models.authentication.getStrategies(args.isEnabled)
|
||||||
strategies = strategies.map(stg => {
|
strategies = strategies.map(stg => {
|
||||||
@ -38,6 +41,9 @@ module.exports = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
AuthenticationMutation: {
|
AuthenticationMutation: {
|
||||||
|
/**
|
||||||
|
* Perform Login
|
||||||
|
*/
|
||||||
async login(obj, args, context) {
|
async login(obj, args, context) {
|
||||||
try {
|
try {
|
||||||
const authResult = await WIKI.models.users.login(args, context)
|
const authResult = await WIKI.models.users.login(args, context)
|
||||||
@ -54,6 +60,9 @@ module.exports = {
|
|||||||
return graphHelper.generateError(err)
|
return graphHelper.generateError(err)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* Perform 2FA Login
|
||||||
|
*/
|
||||||
async loginTFA(obj, args, context) {
|
async loginTFA(obj, args, context) {
|
||||||
try {
|
try {
|
||||||
const authResult = await WIKI.models.users.loginTFA(args, context)
|
const authResult = await WIKI.models.users.loginTFA(args, context)
|
||||||
@ -65,6 +74,9 @@ module.exports = {
|
|||||||
return graphHelper.generateError(err)
|
return graphHelper.generateError(err)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* Register a new account
|
||||||
|
*/
|
||||||
async register(obj, args, context) {
|
async register(obj, args, context) {
|
||||||
try {
|
try {
|
||||||
await WIKI.models.users.register({ ...args, verify: true }, context)
|
await WIKI.models.users.register({ ...args, verify: true }, context)
|
||||||
@ -75,6 +87,9 @@ module.exports = {
|
|||||||
return graphHelper.generateError(err)
|
return graphHelper.generateError(err)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* Update Authentication Strategies
|
||||||
|
*/
|
||||||
async updateStrategies(obj, args, context) {
|
async updateStrategies(obj, args, context) {
|
||||||
try {
|
try {
|
||||||
WIKI.config.auth = {
|
WIKI.config.auth = {
|
||||||
@ -103,6 +118,32 @@ module.exports = {
|
|||||||
} catch (err) {
|
} catch (err) {
|
||||||
return graphHelper.generateError(err)
|
return graphHelper.generateError(err)
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Generate New Authentication Public / Private Key Certificates
|
||||||
|
*/
|
||||||
|
async regenerateCertificates(obj, args, context) {
|
||||||
|
try {
|
||||||
|
await WIKI.auth.regenerateCertificates()
|
||||||
|
return {
|
||||||
|
responseResult: graphHelper.generateSuccess('Certificates have been regenerated successfully.')
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
return graphHelper.generateError(err)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Reset Guest User
|
||||||
|
*/
|
||||||
|
async resetGuestUser(obj, args, context) {
|
||||||
|
try {
|
||||||
|
await WIKI.auth.resetGuestUser()
|
||||||
|
return {
|
||||||
|
responseResult: graphHelper.generateSuccess('Guest user has been reset successfully.')
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
return graphHelper.generateError(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
AuthenticationStrategy: {
|
AuthenticationStrategy: {
|
||||||
|
@ -46,6 +46,9 @@ type AuthenticationMutation {
|
|||||||
strategies: [AuthenticationStrategyInput]!
|
strategies: [AuthenticationStrategyInput]!
|
||||||
config: AuthenticationConfigInput
|
config: AuthenticationConfigInput
|
||||||
): DefaultResponse @auth(requires: ["manage:system"])
|
): DefaultResponse @auth(requires: ["manage:system"])
|
||||||
|
|
||||||
|
regenerateCertificates: DefaultResponse @auth(requires: ["manage:system"])
|
||||||
|
resetGuestUser: DefaultResponse @auth(requires: ["manage:system"])
|
||||||
}
|
}
|
||||||
|
|
||||||
# -----------------------------------------------
|
# -----------------------------------------------
|
||||||
|
@ -336,7 +336,7 @@ module.exports = class User extends Model {
|
|||||||
|
|
||||||
static async loginTFA(opts, context) {
|
static async loginTFA(opts, context) {
|
||||||
if (opts.securityCode.length === 6 && opts.loginToken.length === 64) {
|
if (opts.securityCode.length === 6 && opts.loginToken.length === 64) {
|
||||||
let result = null // await WIKI.redis.get(`tfa:${opts.loginToken}`)
|
let result = await WIKI.redis.get(`tfa:${opts.loginToken}`)
|
||||||
if (result) {
|
if (result) {
|
||||||
let userId = _.toSafeInteger(result)
|
let userId = _.toSafeInteger(result)
|
||||||
if (userId && userId > 0) {
|
if (userId && userId > 0) {
|
||||||
|
@ -351,7 +351,7 @@ module.exports = {
|
|||||||
new stream.Transform({
|
new stream.Transform({
|
||||||
objectMode: true,
|
objectMode: true,
|
||||||
transform: async (page, enc, cb) => {
|
transform: async (page, enc, cb) => {
|
||||||
const fileName = `${page.path}.${getFileExtension(page.contentType)}`
|
let fileName = `${page.path}.${getFileExtension(page.contentType)}`
|
||||||
if (WIKI.config.lang.namespacing && WIKI.config.lang.code !== page.localeCode) {
|
if (WIKI.config.lang.namespacing && WIKI.config.lang.code !== page.localeCode) {
|
||||||
fileName = `${page.localeCode}/${fileName}`
|
fileName = `${page.localeCode}/${fileName}`
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user