diff --git a/.gitignore b/.gitignore index 41183d2b..2db728f8 100644 --- a/.gitignore +++ b/.gitignore @@ -19,9 +19,7 @@ npm-debug.log* # Generated assets /assets /assets-legacy -server/views/master.pug -server/views/legacy/master.pug -server/views/setup.pug +server/views/base.pug # Webpack .webpack-cache diff --git a/client/client-app.js b/client/client-app.js index 6d4c4340..213e509a 100644 --- a/client/client-app.js +++ b/client/client-app.js @@ -134,27 +134,22 @@ Vue.prototype.Velocity = Velocity // Register Vue Components // ==================================== -Vue.component('Admin', () => import(/* webpackChunkName: "admin" */ './components/admin.vue')) Vue.component('Comments', () => import(/* webpackChunkName: "comments" */ './components/comments.vue')) Vue.component('Editor', () => import(/* webpackPrefetch: -100, webpackChunkName: "editor" */ './components/editor.vue')) Vue.component('History', () => import(/* webpackChunkName: "history" */ './components/history.vue')) Vue.component('Loader', () => import(/* webpackPrefetch: true, webpackChunkName: "ui-extra" */ './components/common/loader.vue')) -Vue.component('Login', () => import(/* webpackPrefetch: true, webpackChunkName: "login" */ './components/login.vue')) Vue.component('NavHeader', () => import(/* webpackMode: "eager" */ './components/common/nav-header.vue')) Vue.component('NewPage', () => import(/* webpackChunkName: "new-page" */ './components/new-page.vue')) Vue.component('Notify', () => import(/* webpackMode: "eager" */ './components/common/notify.vue')) Vue.component('NotFound', () => import(/* webpackChunkName: "not-found" */ './components/not-found.vue')) Vue.component('PageSelector', () => import(/* webpackPrefetch: true, webpackChunkName: "ui-extra" */ './components/common/page-selector.vue')) Vue.component('PageSource', () => import(/* webpackChunkName: "source" */ './components/source.vue')) -Vue.component('Profile', () => import(/* webpackChunkName: "profile" */ './components/profile.vue')) -Vue.component('Register', () => import(/* webpackChunkName: "register" */ './components/register.vue')) Vue.component('SearchResults', () => import(/* webpackPrefetch: true, webpackChunkName: "ui-extra" */ './components/common/search-results.vue')) Vue.component('SocialSharing', () => import(/* webpackPrefetch: true, webpackChunkName: "ui-extra" */ './components/common/social-sharing.vue')) Vue.component('Tags', () => import(/* webpackChunkName: "tags" */ './components/tags.vue')) Vue.component('Unauthorized', () => import(/* webpackChunkName: "unauthorized" */ './components/unauthorized.vue')) Vue.component('VCardChin', () => import(/* webpackPrefetch: true, webpackChunkName: "ui-extra" */ './components/common/v-card-chin.vue')) Vue.component('VCardInfo', () => import(/* webpackPrefetch: true, webpackChunkName: "ui-extra" */ './components/common/v-card-info.vue')) -Vue.component('Welcome', () => import(/* webpackChunkName: "welcome" */ './components/welcome.vue')) Vue.component('NavFooter', () => import(/* webpackChunkName: "theme" */ './themes/' + siteConfig.theme + '/components/nav-footer.vue')) Vue.component('Page', () => import(/* webpackChunkName: "theme" */ './themes/' + siteConfig.theme + '/components/page.vue')) diff --git a/client/components/admin.vue b/client/components/admin.vue deleted file mode 100644 index f6e3722a..00000000 --- a/client/components/admin.vue +++ /dev/null @@ -1,335 +0,0 @@ - - - - - diff --git a/client/components/admin/admin-analytics.vue b/client/components/admin/admin-analytics.vue deleted file mode 100644 index dd760502..00000000 --- a/client/components/admin/admin-analytics.vue +++ /dev/null @@ -1,181 +0,0 @@ - - - diff --git a/client/components/admin/admin-api-create.vue b/client/components/admin/admin-api-create.vue deleted file mode 100644 index 22712bf1..00000000 --- a/client/components/admin/admin-api-create.vue +++ /dev/null @@ -1,236 +0,0 @@ - - - diff --git a/client/components/admin/admin-api.vue b/client/components/admin/admin-api.vue deleted file mode 100644 index 3e1f94f2..00000000 --- a/client/components/admin/admin-api.vue +++ /dev/null @@ -1,239 +0,0 @@ - - - - - diff --git a/client/components/admin/admin-auth.vue b/client/components/admin/admin-auth.vue deleted file mode 100644 index d30f8e87..00000000 --- a/client/components/admin/admin-auth.vue +++ /dev/null @@ -1,433 +0,0 @@ - - - diff --git a/client/components/admin/admin-comments.vue b/client/components/admin/admin-comments.vue deleted file mode 100644 index af960eb8..00000000 --- a/client/components/admin/admin-comments.vue +++ /dev/null @@ -1,206 +0,0 @@ - - - diff --git a/client/components/admin/admin-contribute.vue b/client/components/admin/admin-contribute.vue deleted file mode 100644 index e13507fa..00000000 --- a/client/components/admin/admin-contribute.vue +++ /dev/null @@ -1,256 +0,0 @@ - - - - - diff --git a/client/components/admin/admin-dashboard.vue b/client/components/admin/admin-dashboard.vue deleted file mode 100644 index 9dfa9a97..00000000 --- a/client/components/admin/admin-dashboard.vue +++ /dev/null @@ -1,256 +0,0 @@ - - - - - diff --git a/client/components/admin/admin-dev-flags.vue b/client/components/admin/admin-dev-flags.vue deleted file mode 100644 index f345ea7d..00000000 --- a/client/components/admin/admin-dev-flags.vue +++ /dev/null @@ -1,94 +0,0 @@ - - - - - diff --git a/client/components/admin/admin-editor.vue b/client/components/admin/admin-editor.vue deleted file mode 100644 index c9e59713..00000000 --- a/client/components/admin/admin-editor.vue +++ /dev/null @@ -1,66 +0,0 @@ - - - - - diff --git a/client/components/admin/admin-extensions.vue b/client/components/admin/admin-extensions.vue deleted file mode 100644 index 1e0fdfa2..00000000 --- a/client/components/admin/admin-extensions.vue +++ /dev/null @@ -1,119 +0,0 @@ - - - - - diff --git a/client/components/admin/admin-general.vue b/client/components/admin/admin-general.vue deleted file mode 100644 index 24829c8c..00000000 --- a/client/components/admin/admin-general.vue +++ /dev/null @@ -1,369 +0,0 @@ - - - - - diff --git a/client/components/admin/admin-groups-edit-permissions.vue b/client/components/admin/admin-groups-edit-permissions.vue deleted file mode 100644 index 91b1b684..00000000 --- a/client/components/admin/admin-groups-edit-permissions.vue +++ /dev/null @@ -1,224 +0,0 @@ - - - diff --git a/client/components/admin/admin-groups-edit-rules.vue b/client/components/admin/admin-groups-edit-rules.vue deleted file mode 100644 index 89d97db4..00000000 --- a/client/components/admin/admin-groups-edit-rules.vue +++ /dev/null @@ -1,336 +0,0 @@ - - - - - diff --git a/client/components/admin/admin-groups-edit-users.vue b/client/components/admin/admin-groups-edit-users.vue deleted file mode 100644 index 17368825..00000000 --- a/client/components/admin/admin-groups-edit-users.vue +++ /dev/null @@ -1,149 +0,0 @@ - - - diff --git a/client/components/admin/admin-groups-edit.vue b/client/components/admin/admin-groups-edit.vue deleted file mode 100644 index 4f50c8f6..00000000 --- a/client/components/admin/admin-groups-edit.vue +++ /dev/null @@ -1,271 +0,0 @@ - - - - - diff --git a/client/components/admin/admin-groups.vue b/client/components/admin/admin-groups.vue deleted file mode 100644 index 6f1134cc..00000000 --- a/client/components/admin/admin-groups.vue +++ /dev/null @@ -1,170 +0,0 @@ - - - - - diff --git a/client/components/admin/admin-locale.vue b/client/components/admin/admin-locale.vue deleted file mode 100644 index 10271942..00000000 --- a/client/components/admin/admin-locale.vue +++ /dev/null @@ -1,302 +0,0 @@ - - - diff --git a/client/components/admin/admin-logging-console.vue b/client/components/admin/admin-logging-console.vue deleted file mode 100644 index 5e14553c..00000000 --- a/client/components/admin/admin-logging-console.vue +++ /dev/null @@ -1,106 +0,0 @@ - - - - - diff --git a/client/components/admin/admin-logging.vue b/client/components/admin/admin-logging.vue deleted file mode 100644 index 25d157e5..00000000 --- a/client/components/admin/admin-logging.vue +++ /dev/null @@ -1,194 +0,0 @@ - - - - - diff --git a/client/components/admin/admin-mail.vue b/client/components/admin/admin-mail.vue deleted file mode 100644 index 660660f9..00000000 --- a/client/components/admin/admin-mail.vue +++ /dev/null @@ -1,258 +0,0 @@ - - - - - diff --git a/client/components/admin/admin-navigation.vue b/client/components/admin/admin-navigation.vue deleted file mode 100644 index e4796c2f..00000000 --- a/client/components/admin/admin-navigation.vue +++ /dev/null @@ -1,526 +0,0 @@ - - - - - diff --git a/client/components/admin/admin-pages-edit.vue b/client/components/admin/admin-pages-edit.vue deleted file mode 100644 index cd43533b..00000000 --- a/client/components/admin/admin-pages-edit.vue +++ /dev/null @@ -1,235 +0,0 @@ - - - - diff --git a/client/components/admin/admin-pages-visualize.vue b/client/components/admin/admin-pages-visualize.vue deleted file mode 100644 index 116b38a9..00000000 --- a/client/components/admin/admin-pages-visualize.vue +++ /dev/null @@ -1,405 +0,0 @@ - - - - - diff --git a/client/components/admin/admin-pages.vue b/client/components/admin/admin-pages.vue deleted file mode 100644 index 0ca26de1..00000000 --- a/client/components/admin/admin-pages.vue +++ /dev/null @@ -1,169 +0,0 @@ - - - - - diff --git a/client/components/admin/admin-rendering.vue b/client/components/admin/admin-rendering.vue deleted file mode 100644 index 5a77cd3c..00000000 --- a/client/components/admin/admin-rendering.vue +++ /dev/null @@ -1,261 +0,0 @@ - - - - - diff --git a/client/components/admin/admin-search.vue b/client/components/admin/admin-search.vue deleted file mode 100644 index cf58b5db..00000000 --- a/client/components/admin/admin-search.vue +++ /dev/null @@ -1,217 +0,0 @@ - - - - - diff --git a/client/components/admin/admin-security.vue b/client/components/admin/admin-security.vue deleted file mode 100644 index 7a8d305b..00000000 --- a/client/components/admin/admin-security.vue +++ /dev/null @@ -1,444 +0,0 @@ - - - - - diff --git a/client/components/admin/admin-ssl.vue b/client/components/admin/admin-ssl.vue deleted file mode 100644 index 71991b5c..00000000 --- a/client/components/admin/admin-ssl.vue +++ /dev/null @@ -1,269 +0,0 @@ - - - - - diff --git a/client/components/admin/admin-stats.vue b/client/components/admin/admin-stats.vue deleted file mode 100644 index cbfd09a3..00000000 --- a/client/components/admin/admin-stats.vue +++ /dev/null @@ -1,32 +0,0 @@ - - - - - diff --git a/client/components/admin/admin-storage.vue b/client/components/admin/admin-storage.vue deleted file mode 100644 index fae1d2bd..00000000 --- a/client/components/admin/admin-storage.vue +++ /dev/null @@ -1,372 +0,0 @@ - - - - - diff --git a/client/components/admin/admin-system.vue b/client/components/admin/admin-system.vue deleted file mode 100644 index 4f0c8256..00000000 --- a/client/components/admin/admin-system.vue +++ /dev/null @@ -1,238 +0,0 @@ - - - - - diff --git a/client/components/admin/admin-tags.vue b/client/components/admin/admin-tags.vue deleted file mode 100644 index 0bd000e8..00000000 --- a/client/components/admin/admin-tags.vue +++ /dev/null @@ -1,248 +0,0 @@ - - - - - diff --git a/client/components/admin/admin-theme.vue b/client/components/admin/admin-theme.vue deleted file mode 100644 index 83ec5fac..00000000 --- a/client/components/admin/admin-theme.vue +++ /dev/null @@ -1,255 +0,0 @@ - - - - - diff --git a/client/components/admin/admin-users-create.vue b/client/components/admin/admin-users-create.vue deleted file mode 100644 index 7128149e..00000000 --- a/client/components/admin/admin-users-create.vue +++ /dev/null @@ -1,256 +0,0 @@ - - - diff --git a/client/components/admin/admin-users-edit.vue b/client/components/admin/admin-users-edit.vue deleted file mode 100644 index 7b5acae6..00000000 --- a/client/components/admin/admin-users-edit.vue +++ /dev/null @@ -1,1077 +0,0 @@ - - - - diff --git a/client/components/admin/admin-users.vue b/client/components/admin/admin-users.vue deleted file mode 100644 index 52985f5e..00000000 --- a/client/components/admin/admin-users.vue +++ /dev/null @@ -1,192 +0,0 @@ - - - - - diff --git a/client/components/admin/admin-utilities-auth.vue b/client/components/admin/admin-utilities-auth.vue deleted file mode 100644 index 25b72f79..00000000 --- a/client/components/admin/admin-utilities-auth.vue +++ /dev/null @@ -1,93 +0,0 @@ - - - - - diff --git a/client/components/admin/admin-utilities-cache.vue b/client/components/admin/admin-utilities-cache.vue deleted file mode 100644 index ac7251b9..00000000 --- a/client/components/admin/admin-utilities-cache.vue +++ /dev/null @@ -1,108 +0,0 @@ - - - - - diff --git a/client/components/admin/admin-utilities-content.vue b/client/components/admin/admin-utilities-content.vue deleted file mode 100644 index ac2b64ca..00000000 --- a/client/components/admin/admin-utilities-content.vue +++ /dev/null @@ -1,318 +0,0 @@ - - - - - diff --git a/client/components/admin/admin-utilities-importv1.vue b/client/components/admin/admin-utilities-importv1.vue deleted file mode 100644 index 78dd3a54..00000000 --- a/client/components/admin/admin-utilities-importv1.vue +++ /dev/null @@ -1,507 +0,0 @@ - - - - - diff --git a/client/components/admin/admin-utilities-telemetry.vue b/client/components/admin/admin-utilities-telemetry.vue deleted file mode 100644 index e36fecef..00000000 --- a/client/components/admin/admin-utilities-telemetry.vue +++ /dev/null @@ -1,162 +0,0 @@ - - - - - diff --git a/client/components/admin/admin-utilities.vue b/client/components/admin/admin-utilities.vue deleted file mode 100644 index 815177b9..00000000 --- a/client/components/admin/admin-utilities.vue +++ /dev/null @@ -1,91 +0,0 @@ - - - - - diff --git a/client/components/admin/admin-webhooks.vue b/client/components/admin/admin-webhooks.vue deleted file mode 100644 index 84179100..00000000 --- a/client/components/admin/admin-webhooks.vue +++ /dev/null @@ -1,116 +0,0 @@ - - - - - diff --git a/client/components/common/loader.vue b/client/components/common/loader.vue index bf6ca0da..dab216f9 100644 --- a/client/components/common/loader.vue +++ b/client/components/common/loader.vue @@ -8,7 +8,7 @@ :size='60' color='#FFF' ) - img(v-else-if='mode === `icon`', :src='`/_assets/svg/icon-` + icon + `.svg`', :alt='icon') + img(v-else-if='mode === `icon`', :src='`/_assets-legacy/svg/icon-` + icon + `.svg`', :alt='icon') .subtitle-1.white--text {{ title }} .caption {{ subtitle }} diff --git a/client/components/common/nav-header.vue b/client/components/common/nav-header.vue index d8fc3c80..9dd3addc 100644 --- a/client/components/common/nav-header.vue +++ b/client/components/common/nav-header.vue @@ -222,11 +222,11 @@ //- v-list-item-content //- v-list-item-title {{$t('common:header.myWiki')}} //- v-list-item-subtitle.overline Coming soon - v-list-item(href='/p') + v-list-item(href='/_profile') v-list-item-action: v-icon(color='blue-grey') mdi-face-profile v-list-item-content v-list-item-title(:class='$vuetify.theme.dark ? `blue-grey--text text--lighten-3` : `blue-grey--text`') {{$t('common:header.profile')}} - v-list-item(@click='logout') + v-list-item(href='/logout') v-list-item-action: v-icon(color='red') mdi-logout v-list-item-title.red--text {{$t('common:header.logout')}} @@ -314,7 +314,7 @@ export default { url: (this.pictureUrl === 'internal') ? `/_userav/${this.$store.get('user/id')}` : this.pictureUrl } } else { - const nameParts = this.name.toUpperCase().split(' ') + const nameParts = ['X', 'X'] // this.name.toUpperCase().split(' ') let initials = _.head(nameParts).charAt(0) if (nameParts.length > 1) { initials += _.last(nameParts).charAt(0) diff --git a/client/components/common/search-results.vue b/client/components/common/search-results.vue index 64c57f41..ff9c3e54 100644 --- a/client/components/common/search-results.vue +++ b/client/components/common/search-results.vue @@ -2,7 +2,7 @@ .search-results(v-if='searchIsFocused || (search && search.length > 1)') .search-results-container .search-results-help(v-if='!search || (search && search.length < 2)') - img(src='/_assets/svg/icon-search-alt.svg') + img(src='/_assets-legacy/svg/icon-search-alt.svg') .mt-4 {{$t('common:header.searchHint')}} .search-results-loader(v-else-if='searchIsLoading && (!results || results.length < 1)') orbit-spinner( @@ -12,7 +12,7 @@ ) .headline.mt-5 {{$t('common:header.searchLoading')}} .search-results-none(v-else-if='!searchIsLoading && (!results || results.length < 1)') - img(src='/_assets/svg/icon-no-results.svg', alt='No Results') + img(src='/_assets-legacy/svg/icon-no-results.svg', alt='No Results') .subheading {{$t('common:header.searchNoResult')}} template(v-if='search && search.length >= 2 && results && results.length > 0') v-subheader.white--text {{$t('common:header.searchResultsCount', { total: response.totalHits })}} @@ -20,7 +20,7 @@ template(v-for='(item, idx) of results') v-list-item(@click='goToPage(item)', @click.middle="goToPageInNewTab(item)", :key='item.id', :class='idx === cursor ? `highlighted` : ``') v-list-item-avatar(tile) - img(src='/_assets/svg/icon-selective-highlighting.svg') + img(src='/_assets-legacy/svg/icon-selective-highlighting.svg') v-list-item-content v-list-item-title(v-text='item.title') v-list-item-subtitle.caption(v-text='item.description') diff --git a/client/components/editor.vue b/client/components/editor.vue index 15311e9b..170eb1f3 100644 --- a/client/components/editor.vue +++ b/client/components/editor.vue @@ -295,7 +295,6 @@ export default { $content: String! $description: String! $editor: String! - $isPrivate: Boolean! $isPublished: Boolean! $locale: String! $path: String! @@ -303,35 +302,32 @@ export default { $publishStartDate: Date $scriptCss: String $scriptJs: String + $siteId: UUID! $tags: [String]! $title: String! ) { - pages { - create( - content: $content - description: $description - editor: $editor - isPrivate: $isPrivate - isPublished: $isPublished - locale: $locale - path: $path - publishEndDate: $publishEndDate - publishStartDate: $publishStartDate - scriptCss: $scriptCss - scriptJs: $scriptJs - tags: $tags - title: $title - ) { - responseResult { - succeeded - errorCode - slug - message - } - page { - id - updatedAt - } + createPage( + content: $content + description: $description + editor: $editor + isPublished: $isPublished + locale: $locale + path: $path + publishEndDate: $publishEndDate + publishStartDate: $publishStartDate + scriptCss: $scriptCss + scriptJs: $scriptJs + siteId: $siteId + tags: $tags + title: $title + ) { + operation { + succeeded + message + } + page { + id + updatedAt } } } @@ -341,32 +337,32 @@ export default { description: this.$store.get('page/description'), editor: this.$store.get('editor/editorKey'), locale: this.$store.get('page/locale'), - isPrivate: false, isPublished: this.$store.get('page/isPublished'), path: this.$store.get('page/path'), publishEndDate: this.$store.get('page/publishEndDate') || '', publishStartDate: this.$store.get('page/publishStartDate') || '', scriptCss: this.$store.get('page/scriptCss'), scriptJs: this.$store.get('page/scriptJs'), + siteId: this.$store.get('site/id'), tags: this.$store.get('page/tags'), title: this.$store.get('page/title') } }) - resp = _.get(resp, 'data.pages.create', {}) - if (_.get(resp, 'responseResult.succeeded')) { - this.checkoutDateActive = _.get(resp, 'page.updatedAt', this.checkoutDateActive) + resp = resp?.data?.createPage || {} + if (resp?.operation?.succeeded) { + this.checkoutDateActive = resp?.page?.updatedAt ?? this.checkoutDateActive this.isConflict = false this.$store.commit('showNotification', { message: this.$t('editor:save.createSuccess'), style: 'success', icon: 'check' }) - this.$store.set('editor/id', _.get(resp, 'page.id')) + this.$store.set('editor/id', resp?.page?.id) this.$store.set('editor/mode', 'update') this.exitConfirmed = true window.location.assign(`/${this.$store.get('page/locale')}/${this.$store.get('page/path')}`) } else { - throw new Error(_.get(resp, 'responseResult.message')) + throw new Error(resp?.operation?.message) } } else { // -------------------------------------------- @@ -427,7 +423,7 @@ export default { tags: $tags title: $title ) { - responseResult { + operation { succeeded errorCode slug @@ -458,7 +454,7 @@ export default { } }) resp = _.get(resp, 'data.pages.update', {}) - if (_.get(resp, 'responseResult.succeeded')) { + if (_.get(resp, 'operation.succeeded')) { this.checkoutDateActive = _.get(resp, 'page.updatedAt', this.checkoutDateActive) this.isConflict = false this.$store.commit('showNotification', { @@ -472,7 +468,7 @@ export default { }, 1000) } } else { - throw new Error(_.get(resp, 'responseResult.message')) + throw new Error(_.get(resp, 'operation.message')) } } diff --git a/client/components/editor/editor-api.vue b/client/components/editor/editor-api.vue index 14451d53..bf9f6b9a 100644 --- a/client/components/editor/editor-api.vue +++ b/client/components/editor/editor-api.vue @@ -59,7 +59,7 @@ v-list-item-group(v-model='kind', mandatory, color='primary') v-list-item(value='rest') v-list-item-avatar - img(src='/_assets/svg/icon-transaction-list.svg', alt='REST') + img(src='/_assets-legacy/svg/icon-transaction-list.svg', alt='REST') v-list-item-content v-list-item-title REST API v-list-item-subtitle Classic REST Endpoints @@ -67,7 +67,7 @@ v-icon(:color='kind === `rest` ? `primary` : `grey lighten-3`') mdi-check-circle v-list-item(value='graphql', disabled) v-list-item-avatar - img(src='/_assets/svg/icon-graphql.svg', alt='GraphQL') + img(src='/_assets-legacy/svg/icon-graphql.svg', alt='GraphQL') v-list-item-content v-list-item-title GraphQL v-list-item-subtitle.grey--text.text--lighten-1 Schema-based API diff --git a/client/components/editor/editor-markdown.vue b/client/components/editor/editor-markdown.vue index 6134524d..cb8bae3b 100644 --- a/client/components/editor/editor-markdown.vue +++ b/client/components/editor/editor-markdown.vue @@ -259,7 +259,7 @@ import tabsetHelper from './markdown/tabset' const CtrlKey = /Mac/.test(navigator.platform) ? 'Cmd' : 'Ctrl' // Prism Config -Prism.plugins.autoloader.languages_path = '/_assets/js/prism/' +Prism.plugins.autoloader.languages_path = '/_assets-legacy/js/prism/' Prism.plugins.NormalizeWhitespace.setDefaults({ 'remove-trailing': true, 'remove-indent': true, @@ -378,7 +378,7 @@ md.renderer.rules.katex_block = (tokens, idx) => { md.renderer.rules.emoji = (token, idx) => { return twemoji.parse(token[idx].content, { callback (icon, opts) { - return `/_assets/svg/twemoji/${icon}.svg` + return `/_assets-legacy/svg/twemoji/${icon}.svg` } }) } diff --git a/client/components/editor/editor-modal-editorselect.vue b/client/components/editor/editor-modal-editorselect.vue index b5ce35f5..9f7e22e0 100644 --- a/client/components/editor/editor-modal-editorselect.vue +++ b/client/components/editor/editor-modal-editorselect.vue @@ -6,46 +6,6 @@ .subtitle-1.white--text {{$t('editor:select.title')}} v-container(grid-list-lg, fluid) v-layout(row, wrap, justify-center) - v-flex(xs4) - v-hover - template(v-slot:default='{ hover }') - v-card.radius-7.primary.animated.fadeInUp( - hover - light - ripple - ) - v-card-text.text-center(@click='') - img(src='/_assets/svg/editor-icon-api.svg', alt='API', style='width: 36px; opacity: .5;') - .body-2.blue--text.mt-2.text--lighten-2 API Docs - .caption.blue--text.text--lighten-1 REST / GraphQL - v-fade-transition - v-overlay( - v-if='hover' - absolute - color='primary' - opacity='.8' - ) - .body-2.mt-7 Coming Soon - v-flex(xs4) - v-hover - template(v-slot:default='{ hover }') - v-card.radius-7.primary.animated.fadeInUp.wait-p1s( - hover - light - ripple - ) - v-card-text.text-center(@click='') - img(src='/_assets/svg/editor-icon-wikitext.svg', alt='WikiText', style='width: 36px; opacity: .5;') - .body-2.blue--text.mt-2.text--lighten-2 Blog - .caption.blue--text.text--lighten-1 Timeline of Posts - v-fade-transition - v-overlay( - v-if='hover' - absolute - color='primary' - opacity='.8' - ) - .body-2.mt-7 Coming Soon v-flex(xs4) v-card.radius-7.animated.fadeInUp.wait-p2s( hover @@ -53,7 +13,7 @@ ripple ) v-card-text.text-center(@click='selectEditor("code")') - img(src='/_assets/svg/editor-icon-code.svg', alt='Code', style='width: 36px;') + img(src='/_assets-legacy/svg/editor-icon-code.svg', alt='Code', style='width: 36px;') .body-2.primary--text.mt-2 Code .caption.grey--text Raw HTML v-flex(xs4) @@ -63,29 +23,9 @@ ripple ) v-card-text.text-center(@click='selectEditor("markdown")') - img(src='/_assets/svg/editor-icon-markdown.svg', alt='Markdown', style='width: 36px;') + img(src='/_assets-legacy/svg/editor-icon-markdown.svg', alt='Markdown', style='width: 36px;') .body-2.primary--text.mt-2 Markdown .caption.grey--text Plain Text Formatting - v-flex(xs4) - v-hover - template(v-slot:default='{ hover }') - v-card.radius-7.primary.animated.fadeInUp.wait-p2s( - hover - light - ripple - ) - v-card-text.text-center(@click='') - img(src='/_assets/svg/editor-icon-tabular.svg', alt='Tabular', style='width: 36px; opacity: .5;') - .body-2.blue--text.mt-2.text--lighten-2 Tabular - .caption.blue--text.text--lighten-1 Excel-like - v-fade-transition - v-overlay( - v-if='hover' - absolute - color='primary' - opacity='.8' - ) - .body-2.mt-7 Coming Soon v-flex(xs4) v-card.radius-7.animated.fadeInUp.wait-p3s( hover @@ -93,10 +33,9 @@ ripple ) v-card-text.text-center(@click='selectEditor("ckeditor")') - img(src='/_assets/svg/editor-icon-ckeditor.svg', alt='Visual Editor', style='width: 36px;') + img(src='/_assets-legacy/svg/editor-icon-ckeditor.svg', alt='Visual Editor', style='width: 36px;') .body-2.mt-2.primary--text Visual Editor .caption.grey--text Rich-text WYSIWYG - //- .caption.blue--text.text--lighten-2 {{$t('editor:select.cannotChange')}} v-card.radius-7.mt-2(color='teal darken-3', dark) v-card-text.text-center.py-4 @@ -112,69 +51,9 @@ ripple ) v-card-text.text-center(@click='fromTemplate') - img(src='/_assets/svg/icon-cube.svg', alt='From Template', style='width: 42px; opacity: .5;') + img(src='/_assets-legacy/svg/icon-cube.svg', alt='From Template', style='width: 42px; opacity: .5;') .body-2.mt-1.teal--text From Template .caption.grey--text Use an existing page... - v-flex(xs4) - v-hover - template(v-slot:default='{ hover }') - v-card.radius-7.teal.animated.fadeInUp.wait-p1s( - hover - light - ripple - ) - //- v-card-text.text-center(@click='selectEditor("redirect")') - v-card-text.text-center(@click='') - img(src='/_assets/svg/icon-route.svg', alt='Redirection', style='width: 42px; opacity: .5;') - .body-2.mt-1.teal--text.text--lighten-2 Redirection - .caption.teal--text.text--lighten-1 Redirect the user to... - v-flex(xs4) - v-hover - template(v-slot:default='{ hover }') - v-card.radius-7.teal.animated.fadeInUp.wait-p2s( - hover - light - ripple - ) - v-card-text.text-center(@click='') - img(src='/_assets/svg/icon-sewing-patch.svg', alt='Code', style='width: 42px; opacity: .5;') - .body-2.mt-1.teal--text.text--lighten-2 Embed - .caption.teal--text.text--lighten-1 Include external pages - v-fade-transition - v-overlay( - v-if='hover' - absolute - color='teal' - opacity='.8' - ) - .body-2.mt-7 Coming Soon - v-hover - template(v-slot:default='{ hover }') - v-card.radius-7.mt-2(color='indigo darken-3', dark) - v-toolbar(dense, flat, color='light-green darken-3') - v-spacer - .caption.mr-1 or convert from - v-btn.mx-1.animated.fadeInUp(depressed, color='light-green darken-2', @click='', disabled) - v-icon(left) mdi-alpha-a-circle - .body-2.text-none AsciiDoc - v-btn.mx-1.animated.fadeInUp.wait-p1s(depressed, color='light-green darken-2', @click='', disabled) - v-icon(left) mdi-alpha-c-circle - .body-2.text-none CREOLE - v-btn.mx-1.animated.fadeInUp.wait-p2s(depressed, color='light-green darken-2', @click='', disabled) - v-icon(left) mdi-alpha-t-circle - .body-2.text-none Textile - v-btn.mx-1.animated.fadeInUp.wait-p3s(depressed, color='light-green darken-2', @click='', disabled) - v-icon(left) mdi-alpha-w-circle - .body-2.text-none WikiText - v-spacer - v-fade-transition - v-overlay( - v-if='hover' - absolute - color='light-green darken-3' - opacity='.8' - ) - .body-2 Coming Soon page-selector(mode='select', v-model='templateDialogIsShown', :open-handler='fromTemplateHandle', :path='path', :locale='locale', must-exist) diff --git a/client/components/login.vue b/client/components/login.vue deleted file mode 100644 index e7875ccf..00000000 --- a/client/components/login.vue +++ /dev/null @@ -1,801 +0,0 @@ - - - - - diff --git a/client/components/new-page.vue b/client/components/new-page.vue index 71ed92fd..62a8ba63 100644 --- a/client/components/new-page.vue +++ b/client/components/new-page.vue @@ -2,7 +2,7 @@ v-app .newpage .newpage-content - img.animated.fadeIn(src='/_assets/svg/icon-delete-file.svg', alt='Not Found') + img.animated.fadeIn(src='/_assets-legacy/svg/icon-delete-file.svg', alt='Not Found') .headline {{ $t('newpage.title') }} .subtitle-1.mt-3 {{ $t('newpage.subtitle') }} v-btn.mt-5(:href='`/e/` + locale + `/` + path', x-large) diff --git a/client/components/profile.vue b/client/components/profile.vue deleted file mode 100644 index b1206e9c..00000000 --- a/client/components/profile.vue +++ /dev/null @@ -1,98 +0,0 @@ - - - - - diff --git a/client/components/profile/comments.vue b/client/components/profile/comments.vue deleted file mode 100644 index 1d5bbfbf..00000000 --- a/client/components/profile/comments.vue +++ /dev/null @@ -1,20 +0,0 @@ - - - - - diff --git a/client/components/profile/pages.vue b/client/components/profile/pages.vue deleted file mode 100644 index 6e37baa7..00000000 --- a/client/components/profile/pages.vue +++ /dev/null @@ -1,121 +0,0 @@ - - - - - diff --git a/client/components/profile/profile.vue b/client/components/profile/profile.vue deleted file mode 100644 index 918cb6d3..00000000 --- a/client/components/profile/profile.vue +++ /dev/null @@ -1,923 +0,0 @@ - - - - - diff --git a/client/components/register.vue b/client/components/register.vue deleted file mode 100644 index d64cc15e..00000000 --- a/client/components/register.vue +++ /dev/null @@ -1,306 +0,0 @@ - - - - - diff --git a/client/components/setup.vue b/client/components/setup.vue deleted file mode 100644 index 012e4667..00000000 --- a/client/components/setup.vue +++ /dev/null @@ -1,273 +0,0 @@ - - - - - diff --git a/client/components/tags.vue b/client/components/tags.vue index 3d717b38..6865d0ce 100644 --- a/client/components/tags.vue +++ b/client/components/tags.vue @@ -89,7 +89,7 @@ v-btn(text, height='40'): v-icon(size='20') mdi-chevron-double-down v-divider .text-center.pt-10(v-if='selection.length < 1') - img(src='/_assets/svg/icon-price-tag.svg') + img(src='/_assets-legacy/svg/icon-price-tag.svg') .subtitle-2.grey--text {{$t('tags:selectOneMoreTagsHint')}} .px-5.py-2(v-else) v-data-iterator( @@ -112,11 +112,11 @@ .subtitle-2.grey--text.mt-5 {{$t('tags:retrievingResultsLoading')}} template(v-slot:no-data) .text-center.pt-10 - img(src='/_assets/svg/icon-info.svg') + img(src='/_assets-legacy/svg/icon-info.svg') .subtitle-2.grey--text {{$t('tags:noResults')}} template(v-slot:no-results) .text-center.pt-10 - img(src='/_assets/svg/icon-info.svg') + img(src='/_assets-legacy/svg/icon-info.svg') .subtitle-2.grey--text {{$t('tags:noResultsWithFilter')}} template(v-slot:default='props') v-row(align='stretch') diff --git a/client/components/welcome.vue b/client/components/welcome.vue deleted file mode 100644 index c182efdd..00000000 --- a/client/components/welcome.vue +++ /dev/null @@ -1,35 +0,0 @@ - - - - - diff --git a/client/graph/admin/analytics/analytics-mutation-save-providers.gql b/client/graph/admin/analytics/analytics-mutation-save-providers.gql deleted file mode 100644 index 09cc9a49..00000000 --- a/client/graph/admin/analytics/analytics-mutation-save-providers.gql +++ /dev/null @@ -1,12 +0,0 @@ -mutation($providers: [AnalyticsProviderInput]!) { - analytics { - updateProviders(providers: $providers) { - responseResult { - succeeded - errorCode - slug - message - } - } - } -} diff --git a/client/graph/admin/analytics/analytics-query-providers.gql b/client/graph/admin/analytics/analytics-query-providers.gql deleted file mode 100644 index 4402fc9c..00000000 --- a/client/graph/admin/analytics/analytics-query-providers.gql +++ /dev/null @@ -1,17 +0,0 @@ -query { - analytics { - providers { - isEnabled - key - title - description - isAvailable - logo - website - config { - key - value - } - } - } -} diff --git a/client/graph/admin/auth/auth-query-groups.gql b/client/graph/admin/auth/auth-query-groups.gql deleted file mode 100644 index c4bd890f..00000000 --- a/client/graph/admin/auth/auth-query-groups.gql +++ /dev/null @@ -1,8 +0,0 @@ -query { - groups { - list { - id - name - } - } -} diff --git a/client/graph/admin/auth/auth-query-host.gql b/client/graph/admin/auth/auth-query-host.gql deleted file mode 100644 index b9c7e31d..00000000 --- a/client/graph/admin/auth/auth-query-host.gql +++ /dev/null @@ -1,7 +0,0 @@ -{ - site { - config { - host - } - } -} diff --git a/client/graph/admin/auth/auth-query-strategies.gql b/client/graph/admin/auth/auth-query-strategies.gql deleted file mode 100644 index ce2ed90a..00000000 --- a/client/graph/admin/auth/auth-query-strategies.gql +++ /dev/null @@ -1,21 +0,0 @@ -query { - authentication { - strategies { - isEnabled - key - title - description - isAvailable - useForm - logo - website - config { - key - value - } - selfRegistration - domainWhitelist - autoEnrollGroups - } - } -} diff --git a/client/graph/admin/contribute/contribute-query-contributors.gql b/client/graph/admin/contribute/contribute-query-contributors.gql deleted file mode 100644 index d5c4b4ec..00000000 --- a/client/graph/admin/contribute/contribute-query-contributors.gql +++ /dev/null @@ -1,17 +0,0 @@ -query { - contribute { - contributors { - company - currency - description - id - image - name - profile - tier - totalDonated - twitter - website - } - } -} diff --git a/client/graph/admin/dashboard/dashboard-query-stats.gql b/client/graph/admin/dashboard/dashboard-query-stats.gql deleted file mode 100644 index 75bf2096..00000000 --- a/client/graph/admin/dashboard/dashboard-query-stats.gql +++ /dev/null @@ -1,12 +0,0 @@ -query { - system { - info { - currentVersion - latestVersion - groupsTotal - pagesTotal - usersTotal - tagsTotal - } - } -} diff --git a/client/graph/admin/dev/dev-mutation-save-flags.gql b/client/graph/admin/dev/dev-mutation-save-flags.gql deleted file mode 100644 index be9a9ffd..00000000 --- a/client/graph/admin/dev/dev-mutation-save-flags.gql +++ /dev/null @@ -1,16 +0,0 @@ -mutation ( - $flags: [SystemFlagInput]! -) { - system { - updateFlags( - flags: $flags - ) { - responseResult { - succeeded - errorCode - slug - message - } - } - } -} diff --git a/client/graph/admin/dev/dev-query-flags.gql b/client/graph/admin/dev/dev-query-flags.gql deleted file mode 100644 index 3650b06f..00000000 --- a/client/graph/admin/dev/dev-query-flags.gql +++ /dev/null @@ -1,8 +0,0 @@ -{ - system { - flags { - key - value - } - } -} diff --git a/client/graph/admin/groups/groups-mutation-assign.gql b/client/graph/admin/groups/groups-mutation-assign.gql deleted file mode 100644 index af76cb5e..00000000 --- a/client/graph/admin/groups/groups-mutation-assign.gql +++ /dev/null @@ -1,12 +0,0 @@ -mutation ($groupId: Int!, $userId: Int!) { - groups { - assignUser(groupId: $groupId, userId: $userId) { - responseResult { - succeeded - errorCode - slug - message - } - } - } -} diff --git a/client/graph/admin/groups/groups-mutation-create.gql b/client/graph/admin/groups/groups-mutation-create.gql deleted file mode 100644 index 281a3fe8..00000000 --- a/client/graph/admin/groups/groups-mutation-create.gql +++ /dev/null @@ -1,18 +0,0 @@ -mutation ($name: String!) { - groups { - create(name: $name) { - responseResult { - succeeded - errorCode - slug - message - } - group { - id - name - createdAt - updatedAt - } - } - } -} diff --git a/client/graph/admin/groups/groups-mutation-unassign.gql b/client/graph/admin/groups/groups-mutation-unassign.gql deleted file mode 100644 index 7cc2cb70..00000000 --- a/client/graph/admin/groups/groups-mutation-unassign.gql +++ /dev/null @@ -1,12 +0,0 @@ -mutation ($groupId: Int!, $userId: Int!) { - groups { - unassignUser(groupId: $groupId, userId: $userId) { - responseResult { - succeeded - errorCode - slug - message - } - } - } -} diff --git a/client/graph/admin/groups/groups-query-list.gql b/client/graph/admin/groups/groups-query-list.gql deleted file mode 100644 index c2453f10..00000000 --- a/client/graph/admin/groups/groups-query-list.gql +++ /dev/null @@ -1,12 +0,0 @@ -query { - groups { - list { - id - name - isSystem - userCount - createdAt - updatedAt - } - } -} diff --git a/client/graph/admin/locale/locale-mutation-download.gql b/client/graph/admin/locale/locale-mutation-download.gql deleted file mode 100644 index e11c0613..00000000 --- a/client/graph/admin/locale/locale-mutation-download.gql +++ /dev/null @@ -1,12 +0,0 @@ -mutation($locale: String!) { - localization { - downloadLocale(locale: $locale) { - responseResult { - succeeded - errorCode - slug - message - } - } - } -} diff --git a/client/graph/admin/locale/locale-mutation-save.gql b/client/graph/admin/locale/locale-mutation-save.gql deleted file mode 100644 index df38c1a2..00000000 --- a/client/graph/admin/locale/locale-mutation-save.gql +++ /dev/null @@ -1,12 +0,0 @@ -mutation($locale: String!, $autoUpdate: Boolean!, $namespacing: Boolean!, $namespaces: [String]!) { - localization { - updateLocale(locale: $locale, autoUpdate: $autoUpdate, namespacing: $namespacing, namespaces: $namespaces) { - responseResult { - succeeded - errorCode - slug - message - } - } - } -} diff --git a/client/graph/admin/locale/locale-query-list.gql b/client/graph/admin/locale/locale-query-list.gql deleted file mode 100644 index c91e2d90..00000000 --- a/client/graph/admin/locale/locale-query-list.gql +++ /dev/null @@ -1,21 +0,0 @@ -{ - localization { - locales { - availability - code - createdAt - isInstalled - installDate - isRTL - name - nativeName - updatedAt - } - config { - locale - autoUpdate - namespacing - namespaces - } - } -} diff --git a/client/graph/admin/logging/logging-mutation-save-loggers.gql b/client/graph/admin/logging/logging-mutation-save-loggers.gql deleted file mode 100644 index 2d86ba14..00000000 --- a/client/graph/admin/logging/logging-mutation-save-loggers.gql +++ /dev/null @@ -1,12 +0,0 @@ -mutation($loggers: [LoggerInput]) { - logging { - updateLoggers(loggers: $loggers) { - responseResult { - succeeded - errorCode - slug - message - } - } - } -} diff --git a/client/graph/admin/logging/logging-query-loggers.gql b/client/graph/admin/logging/logging-query-loggers.gql deleted file mode 100644 index 3c244c32..00000000 --- a/client/graph/admin/logging/logging-query-loggers.gql +++ /dev/null @@ -1,17 +0,0 @@ -query { - logging { - loggers(orderBy: "title ASC") { - isEnabled - key - title - description - logo - website - level - config { - key - value - } - } - } -} diff --git a/client/graph/admin/logging/logging-subscription-livetrail.gql b/client/graph/admin/logging/logging-subscription-livetrail.gql deleted file mode 100644 index 80e1fda4..00000000 --- a/client/graph/admin/logging/logging-subscription-livetrail.gql +++ /dev/null @@ -1,7 +0,0 @@ -subscription { - loggingLiveTrail { - level - output - timestamp - } -} diff --git a/client/graph/admin/mail/mail-mutation-save-config.gql b/client/graph/admin/mail/mail-mutation-save-config.gql deleted file mode 100644 index 3b8f6999..00000000 --- a/client/graph/admin/mail/mail-mutation-save-config.gql +++ /dev/null @@ -1,38 +0,0 @@ -mutation ( - $senderName: String!, - $senderEmail: String!, - $host: String!, - $port: Int!, - $secure: Boolean!, - $verifySSL: Boolean!, - $user: String!, - $pass: String!, - $useDKIM: Boolean!, - $dkimDomainName: String!, - $dkimKeySelector: String!, - $dkimPrivateKey: String! -) { - mail { - updateConfig( - senderName: $senderName, - senderEmail: $senderEmail, - host: $host, - port: $port, - secure: $secure, - verifySSL: $verifySSL, - user: $user, - pass: $pass, - useDKIM: $useDKIM, - dkimDomainName: $dkimDomainName, - dkimKeySelector: $dkimKeySelector, - dkimPrivateKey: $dkimPrivateKey - ) { - responseResult { - succeeded - errorCode - slug - message - } - } - } -} diff --git a/client/graph/admin/mail/mail-mutation-sendtest.gql b/client/graph/admin/mail/mail-mutation-sendtest.gql deleted file mode 100644 index 66157947..00000000 --- a/client/graph/admin/mail/mail-mutation-sendtest.gql +++ /dev/null @@ -1,12 +0,0 @@ -mutation ($recipientEmail: String!) { - mail { - sendTest(recipientEmail: $recipientEmail) { - responseResult { - succeeded - errorCode - slug - message - } - } - } -} diff --git a/client/graph/admin/mail/mail-query-config.gql b/client/graph/admin/mail/mail-query-config.gql deleted file mode 100644 index 66232acb..00000000 --- a/client/graph/admin/mail/mail-query-config.gql +++ /dev/null @@ -1,18 +0,0 @@ -{ - mail { - config { - senderName - senderEmail - host - port - secure - verifySSL - user - pass - useDKIM - dkimDomainName - dkimKeySelector - dkimPrivateKey - } - } -} diff --git a/client/graph/admin/pages/pages-query-list.gql b/client/graph/admin/pages/pages-query-list.gql deleted file mode 100644 index 0faf0785..00000000 --- a/client/graph/admin/pages/pages-query-list.gql +++ /dev/null @@ -1,17 +0,0 @@ -query { - pages { - list { - id - locale - path - title - description - contentType - isPublished - isPrivate - privateNS - createdAt - updatedAt - } - } -} diff --git a/client/graph/admin/pages/pages-query-single.gql b/client/graph/admin/pages/pages-query-single.gql deleted file mode 100644 index f245772a..00000000 --- a/client/graph/admin/pages/pages-query-single.gql +++ /dev/null @@ -1,27 +0,0 @@ -query($id: Int!) { - pages { - single(id:$id) { - id - path - hash - title - description - isPrivate - isPublished - privateNS - publishStartDate - publishEndDate - contentType - createdAt - updatedAt - editor - locale - authorId - authorName - authorEmail - creatorId - creatorName - creatorEmail - } - } -} diff --git a/client/graph/admin/rendering/rendering-mutation-save-renderers.gql b/client/graph/admin/rendering/rendering-mutation-save-renderers.gql deleted file mode 100644 index 8a717785..00000000 --- a/client/graph/admin/rendering/rendering-mutation-save-renderers.gql +++ /dev/null @@ -1,12 +0,0 @@ -mutation($renderers: [RendererInput]) { - rendering { - updateRenderers(renderers: $renderers) { - responseResult { - succeeded - errorCode - slug - message - } - } - } -} diff --git a/client/graph/admin/rendering/rendering-query-renderers.gql b/client/graph/admin/rendering/rendering-query-renderers.gql deleted file mode 100644 index b117b7ab..00000000 --- a/client/graph/admin/rendering/rendering-query-renderers.gql +++ /dev/null @@ -1,18 +0,0 @@ -{ - rendering { - renderers { - isEnabled - key - title - description - icon - dependsOn - input - output - config { - key - value - } - } - } -} diff --git a/client/graph/admin/search/search-mutation-rebuild-index.gql b/client/graph/admin/search/search-mutation-rebuild-index.gql deleted file mode 100644 index 9f4a46ee..00000000 --- a/client/graph/admin/search/search-mutation-rebuild-index.gql +++ /dev/null @@ -1,12 +0,0 @@ -mutation { - search { - rebuildIndex { - responseResult { - succeeded - errorCode - slug - message - } - } - } -} diff --git a/client/graph/admin/search/search-mutation-save-engines.gql b/client/graph/admin/search/search-mutation-save-engines.gql deleted file mode 100644 index 7ab64db6..00000000 --- a/client/graph/admin/search/search-mutation-save-engines.gql +++ /dev/null @@ -1,12 +0,0 @@ -mutation($engines: [SearchEngineInput]) { - search { - updateSearchEngines(engines: $engines) { - responseResult { - succeeded - errorCode - slug - message - } - } - } -} diff --git a/client/graph/admin/search/search-query-engines.gql b/client/graph/admin/search/search-query-engines.gql deleted file mode 100644 index bc305a09..00000000 --- a/client/graph/admin/search/search-query-engines.gql +++ /dev/null @@ -1,17 +0,0 @@ -query { - search { - searchEngines(orderBy: "title") { - isEnabled - key - title - description - logo - website - isAvailable - config { - key - value - } - } - } -} diff --git a/client/graph/admin/storage/storage-mutation-executeaction.gql b/client/graph/admin/storage/storage-mutation-executeaction.gql deleted file mode 100644 index aa0fb07c..00000000 --- a/client/graph/admin/storage/storage-mutation-executeaction.gql +++ /dev/null @@ -1,12 +0,0 @@ -mutation($targetKey: String!, $handler: String!) { - storage { - executeAction(targetKey: $targetKey, handler: $handler) { - responseResult { - succeeded - errorCode - slug - message - } - } - } -} diff --git a/client/graph/admin/storage/storage-mutation-save-targets.gql b/client/graph/admin/storage/storage-mutation-save-targets.gql deleted file mode 100644 index 153ec199..00000000 --- a/client/graph/admin/storage/storage-mutation-save-targets.gql +++ /dev/null @@ -1,12 +0,0 @@ -mutation($targets: [StorageTargetInput]!) { - storage { - updateTargets(targets: $targets) { - responseResult { - succeeded - errorCode - slug - message - } - } - } -} diff --git a/client/graph/admin/storage/storage-query-status.gql b/client/graph/admin/storage/storage-query-status.gql deleted file mode 100644 index 9a9e1f44..00000000 --- a/client/graph/admin/storage/storage-query-status.gql +++ /dev/null @@ -1,11 +0,0 @@ -query { - storage { - status { - key - title - status - message - lastAttempt - } - } -} diff --git a/client/graph/admin/storage/storage-query-targets.gql b/client/graph/admin/storage/storage-query-targets.gql deleted file mode 100644 index 71e6e619..00000000 --- a/client/graph/admin/storage/storage-query-targets.gql +++ /dev/null @@ -1,27 +0,0 @@ -query { - storage { - targets { - isAvailable - isEnabled - key - title - description - logo - website - supportedModes - mode - hasSchedule - syncInterval - syncIntervalDefault - config { - key - value - } - actions { - handler - label - hint - } - } - } -} diff --git a/client/graph/admin/system/system-mutation-upgrade.gql b/client/graph/admin/system/system-mutation-upgrade.gql deleted file mode 100644 index 030a4449..00000000 --- a/client/graph/admin/system/system-mutation-upgrade.gql +++ /dev/null @@ -1,12 +0,0 @@ -mutation { - system { - performUpgrade { - responseResult { - succeeded - errorCode - slug - message - } - } - } -} diff --git a/client/graph/admin/system/system-query-info.gql b/client/graph/admin/system/system-query-info.gql deleted file mode 100644 index e14cf680..00000000 --- a/client/graph/admin/system/system-query-info.gql +++ /dev/null @@ -1,21 +0,0 @@ -query { - system { - info { - configFile - cpuCores - currentVersion - dbHost - dbType - dbVersion - hostname - latestVersion - latestVersionReleaseDate - nodeVersion - operatingSystem - platform - ramTotal - upgradeCapable - workingDirectory - } - } -} diff --git a/client/graph/admin/theme/theme-mutation-save.gql b/client/graph/admin/theme/theme-mutation-save.gql deleted file mode 100644 index 856442ce..00000000 --- a/client/graph/admin/theme/theme-mutation-save.gql +++ /dev/null @@ -1,12 +0,0 @@ -mutation($theme: String!, $iconset: String!, $darkMode: Boolean!, $injectCSS: String, $injectHead: String, $injectBody: String) { - theming { - setConfig(theme: $theme, iconset: $iconset, darkMode: $darkMode, injectCSS: $injectCSS, injectHead: $injectHead, injectBody: $injectBody) { - responseResult { - succeeded - errorCode - slug - message - } - } - } -} diff --git a/client/graph/admin/theme/theme-query-config.gql b/client/graph/admin/theme/theme-query-config.gql deleted file mode 100644 index 360cb2fa..00000000 --- a/client/graph/admin/theme/theme-query-config.gql +++ /dev/null @@ -1,12 +0,0 @@ -query { - theming { - config { - theme - iconset - darkMode - injectCSS - injectHead - injectBody - } - } -} diff --git a/client/graph/admin/users/users-mutation-create.gql b/client/graph/admin/users/users-mutation-create.gql deleted file mode 100644 index 90861007..00000000 --- a/client/graph/admin/users/users-mutation-create.gql +++ /dev/null @@ -1,12 +0,0 @@ -mutation ($providerKey: String!, $email: String!, $name: String!, $passwordRaw: String, $groups: [Int]!, $mustChangePassword: Boolean, $sendWelcomeEmail: Boolean) { - users { - create(providerKey: $providerKey, email: $email, name: $name, passwordRaw: $passwordRaw, groups: $groups, mustChangePassword: $mustChangePassword, sendWelcomeEmail: $sendWelcomeEmail) { - responseResult { - succeeded - errorCode - slug - message - } - } - } -} diff --git a/client/graph/admin/users/users-query-groups.gql b/client/graph/admin/users/users-query-groups.gql deleted file mode 100644 index 4bc1b91f..00000000 --- a/client/graph/admin/users/users-query-groups.gql +++ /dev/null @@ -1,9 +0,0 @@ -query { - groups { - list { - id - name - isSystem - } - } -} diff --git a/client/graph/admin/utilities/utilities-mutation-auth-regencerts.gql b/client/graph/admin/utilities/utilities-mutation-auth-regencerts.gql deleted file mode 100644 index d45e971a..00000000 --- a/client/graph/admin/utilities/utilities-mutation-auth-regencerts.gql +++ /dev/null @@ -1,12 +0,0 @@ -mutation { - authentication { - regenerateCertificates { - responseResult { - succeeded - errorCode - slug - message - } - } - } -} diff --git a/client/graph/admin/utilities/utilities-mutation-auth-resetguest.gql b/client/graph/admin/utilities/utilities-mutation-auth-resetguest.gql deleted file mode 100644 index 078fe26f..00000000 --- a/client/graph/admin/utilities/utilities-mutation-auth-resetguest.gql +++ /dev/null @@ -1,12 +0,0 @@ -mutation { - authentication { - resetGuestUser { - responseResult { - succeeded - errorCode - slug - message - } - } - } -} diff --git a/client/graph/admin/utilities/utilities-mutation-cache-flushcache.gql b/client/graph/admin/utilities/utilities-mutation-cache-flushcache.gql deleted file mode 100644 index 92a5bd2e..00000000 --- a/client/graph/admin/utilities/utilities-mutation-cache-flushcache.gql +++ /dev/null @@ -1,12 +0,0 @@ -mutation { - pages { - flushCache { - responseResult { - succeeded - errorCode - slug - message - } - } - } -} diff --git a/client/graph/admin/utilities/utilities-mutation-cache-flushuploads.gql b/client/graph/admin/utilities/utilities-mutation-cache-flushuploads.gql deleted file mode 100644 index c72d0605..00000000 --- a/client/graph/admin/utilities/utilities-mutation-cache-flushuploads.gql +++ /dev/null @@ -1,12 +0,0 @@ -mutation { - assets { - flushTempUploads { - responseResult { - succeeded - errorCode - slug - message - } - } - } -} diff --git a/client/graph/admin/utilities/utilities-mutation-content-migratelocale.gql b/client/graph/admin/utilities/utilities-mutation-content-migratelocale.gql deleted file mode 100644 index 3b759361..00000000 --- a/client/graph/admin/utilities/utilities-mutation-content-migratelocale.gql +++ /dev/null @@ -1,13 +0,0 @@ -mutation($sourceLocale: String!, $targetLocale: String!) { - pages { - migrateToLocale(sourceLocale: $sourceLocale, targetLocale: $targetLocale) { - responseResult { - succeeded - errorCode - slug - message - } - count - } - } -} diff --git a/client/graph/admin/utilities/utilities-mutation-content-rebuildtree.gql b/client/graph/admin/utilities/utilities-mutation-content-rebuildtree.gql deleted file mode 100644 index 4f307fde..00000000 --- a/client/graph/admin/utilities/utilities-mutation-content-rebuildtree.gql +++ /dev/null @@ -1,12 +0,0 @@ -mutation { - pages { - rebuildTree { - responseResult { - succeeded - errorCode - slug - message - } - } - } -} diff --git a/client/graph/admin/utilities/utilities-mutation-importv1-users.gql b/client/graph/admin/utilities/utilities-mutation-importv1-users.gql deleted file mode 100644 index 54319baf..00000000 --- a/client/graph/admin/utilities/utilities-mutation-importv1-users.gql +++ /dev/null @@ -1,19 +0,0 @@ -mutation($mongoDbConnString: String!, $groupMode: SystemImportUsersGroupMode!) { - system { - importUsersFromV1(mongoDbConnString: $mongoDbConnString, groupMode: $groupMode) { - responseResult { - succeeded - errorCode - slug - message - } - usersCount - groupsCount - failed { - provider - email - error - } - } - } -} diff --git a/client/graph/admin/utilities/utilities-mutation-telemetry-resetid.gql b/client/graph/admin/utilities/utilities-mutation-telemetry-resetid.gql deleted file mode 100644 index d0e20005..00000000 --- a/client/graph/admin/utilities/utilities-mutation-telemetry-resetid.gql +++ /dev/null @@ -1,12 +0,0 @@ -mutation { - system { - resetTelemetryClientId { - responseResult { - succeeded - errorCode - slug - message - } - } - } -} diff --git a/client/graph/admin/utilities/utilities-mutation-telemetry-set.gql b/client/graph/admin/utilities/utilities-mutation-telemetry-set.gql deleted file mode 100644 index c26a4e2d..00000000 --- a/client/graph/admin/utilities/utilities-mutation-telemetry-set.gql +++ /dev/null @@ -1,12 +0,0 @@ -mutation($enabled: Boolean!) { - system { - setTelemetry(enabled: $enabled) { - responseResult { - succeeded - errorCode - slug - message - } - } - } -} diff --git a/client/graph/admin/utilities/utilities-query-telemetry.gql b/client/graph/admin/utilities/utilities-query-telemetry.gql deleted file mode 100644 index cbfe83ce..00000000 --- a/client/graph/admin/utilities/utilities-query-telemetry.gql +++ /dev/null @@ -1,8 +0,0 @@ -query { - system { - info { - telemetry - telemetryClientId - } - } -} diff --git a/client/graph/login/login-mutation-changepassword.gql b/client/graph/login/login-mutation-changepassword.gql deleted file mode 100644 index 127c5644..00000000 --- a/client/graph/login/login-mutation-changepassword.gql +++ /dev/null @@ -1,13 +0,0 @@ -mutation($continuationToken: String!, $newPassword: String!) { - authentication { - loginChangePassword(continuationToken: $continuationToken, newPassword: $newPassword) { - responseResult { - succeeded - errorCode - slug - message - } - jwt - } - } -} diff --git a/client/graph/login/login-mutation-login.gql b/client/graph/login/login-mutation-login.gql deleted file mode 100644 index 11e90a8c..00000000 --- a/client/graph/login/login-mutation-login.gql +++ /dev/null @@ -1,16 +0,0 @@ -mutation($username: String!, $password: String!, $strategy: String!) { - authentication { - login(username: $username, password: $password, strategy: $strategy) { - responseResult { - succeeded - errorCode - slug - message - } - jwt - mustChangePwd - mustProvideTFA - continuationToken - } - } -} diff --git a/client/graph/login/login-mutation-tfa.gql b/client/graph/login/login-mutation-tfa.gql deleted file mode 100644 index 116362e5..00000000 --- a/client/graph/login/login-mutation-tfa.gql +++ /dev/null @@ -1,13 +0,0 @@ -mutation($continuationToken: String!, $securityCode: String!) { - authentication { - loginTFA(continuationToken: $continuationToken, securityCode: $securityCode) { - responseResult { - succeeded - errorCode - slug - message - } - jwt - } - } -} diff --git a/client/graph/login/login-query-strategies.gql b/client/graph/login/login-query-strategies.gql deleted file mode 100644 index 5f8914d2..00000000 --- a/client/graph/login/login-query-strategies.gql +++ /dev/null @@ -1,14 +0,0 @@ -query { - authentication { - strategies( - isEnabled: true - ) { - key - title - useForm - icon - color - selfRegistration - } - } -} diff --git a/client/graph/register/register-mutation-create.gql b/client/graph/register/register-mutation-create.gql deleted file mode 100644 index 16cc9836..00000000 --- a/client/graph/register/register-mutation-create.gql +++ /dev/null @@ -1,13 +0,0 @@ -mutation($email: String!, $password: String!, $name: String!) { - authentication { - register(email: $email, password: $password, name: $name) { - responseResult { - succeeded - errorCode - slug - message - } - jwt - } - } -} diff --git a/client/index-app.js b/client/index-app.js index 51d4d5de..0fa44f7e 100644 --- a/client/index-app.js +++ b/client/index-app.js @@ -1,7 +1,6 @@ require('core-js/stable') require('regenerator-runtime/runtime') -/* global siteConfig */ /* eslint-disable no-unused-expressions */ switch (window.document.documentElement.lang) { @@ -17,10 +16,10 @@ switch (window.document.documentElement.lang) { require('modernizr') require('./scss/app.scss') -import(/* webpackChunkName: "theme" */ './themes/' + siteConfig.theme + '/scss/app.scss') +import(/* webpackChunkName: "theme" */ './themes/default/scss/app.scss') import(/* webpackChunkName: "mdi" */ '@mdi/font/css/materialdesignicons.css') require('./helpers/compatibility.js') require('./client-app.js') -import(/* webpackChunkName: "theme" */ './themes/' + siteConfig.theme + '/js/app.js') +import(/* webpackChunkName: "theme" */ './themes/default/js/app.js') diff --git a/client/modules/localization.js b/client/modules/localization.js index 2fa865ac..72fc8c64 100644 --- a/client/modules/localization.js +++ b/client/modules/localization.js @@ -28,25 +28,27 @@ export default { loadPath: '{{lng}}/{{ns}}', parse: (data) => data, ajax: (url, opts, cb, data) => { - let langParams = url.split('/') - graphQL.query({ - query: localeQuery, - variables: { - locale: langParams[0], - namespace: langParams[1] - } - }).then(resp => { - let ns = {} - if (_.get(resp, 'data.localization.translations', []).length > 0) { - resp.data.localization.translations.forEach(entry => { - _.set(ns, entry.key, entry.value) - }) - } - return cb(ns, {status: '200'}) - }).catch(err => { - console.error(err) - return cb(null, {status: '404'}) - }) + let ns = {} + return cb(ns, {status: '200'}) + // let langParams = url.split('/') + // graphQL.query({ + // query: localeQuery, + // variables: { + // locale: langParams[0], + // namespace: langParams[1] + // } + // }).then(resp => { + // let ns = {} + // if (_.get(resp, 'data.localization.translations', []).length > 0) { + // resp.data.localization.translations.forEach(entry => { + // _.set(ns, entry.key, entry.value) + // }) + // } + // return cb(ns, {status: '200'}) + // }).catch(err => { + // console.error(err) + // return cb(null, {status: '404'}) + // }) } } ] diff --git a/client/static/browserconfig.xml b/client/static/browserconfig.xml index 86c3f303..eaba6662 100644 --- a/client/static/browserconfig.xml +++ b/client/static/browserconfig.xml @@ -2,7 +2,7 @@ - + #1976d2 diff --git a/client/static/manifest.json b/client/static/manifest.json index 47fc6f33..b28a23a2 100644 --- a/client/static/manifest.json +++ b/client/static/manifest.json @@ -4,12 +4,12 @@ "start_url": "/", "icons": [ { - "src": "/_assets/favicons/android-chrome-192x192.png", + "src": "/_assets-legacy/favicons/android-chrome-192x192.png", "sizes": "192x192", "type": "image/png" }, { - "src": "/_assets/favicons/android-chrome-256x256.png", + "src": "/_assets-legacy/favicons/android-chrome-256x256.png", "sizes": "256x256", "type": "image/png" } diff --git a/client/store/admin.js b/client/store/admin.js deleted file mode 100644 index cb9d236e..00000000 --- a/client/store/admin.js +++ /dev/null @@ -1,17 +0,0 @@ -import { make } from 'vuex-pathify' - -const state = { - info: { - currentVersion: 'n/a', - latestVersion: 'n/a', - groupsTotal: 0, - pagesTotal: 0, - usersTotal: 0 - } -} - -export default { - namespaced: true, - state, - mutations: make.mutations(state) -} diff --git a/client/store/site.js b/client/store/site.js index e26830e3..9f8fff46 100644 --- a/client/store/site.js +++ b/client/store/site.js @@ -3,10 +3,10 @@ import { make } from 'vuex-pathify' /* global siteConfig */ const state = { + id: siteConfig.id, company: siteConfig.company, contentLicense: siteConfig.contentLicense, dark: siteConfig.darkMode, - mascot: true, title: siteConfig.title, logoUrl: siteConfig.logoUrl, search: '', diff --git a/config.sample.yml b/config.sample.yml index b98f38d1..f4ba837e 100644 --- a/config.sample.yml +++ b/config.sample.yml @@ -93,7 +93,7 @@ bindIP: 0.0.0.0 # --------------------------------------------------------------------- # Logging # --------------------------------------------------------------------- -# Possible values: error, warn, info (default), verbose, debug, silly +# Possible values: error, warn, info (default), debug logLevel: info diff --git a/dev/templates/base.pug b/dev/templates/base.pug index fa90ce42..ff019ecf 100644 --- a/dev/templates/base.pug +++ b/dev/templates/base.pug @@ -6,7 +6,7 @@ html(lang=siteConfig.lang) meta(name='viewport', content='user-scalable=yes, width=device-width, initial-scale=1, maximum-scale=5') meta(name='theme-color', content='#1976d2') meta(name='msapplication-TileColor', content='#1976d2') - meta(name='msapplication-TileImage', content='/_assets/favicons/mstile-150x150.png') + meta(name='msapplication-TileImage', content='/_assets-legacy/favicons/mstile-150x150.png') title= pageMeta.title + ' | ' + config.title @@ -20,12 +20,12 @@ html(lang=siteConfig.lang) meta(property='og:site_name', content=config.title) //- Favicon - link(rel='apple-touch-icon', sizes='180x180', href='/_assets/favicons/apple-touch-icon.png') - link(rel='icon', type='image/png', sizes='192x192', href='/_assets/favicons/android-chrome-192x192.png') - link(rel='icon', type='image/png', sizes='32x32', href='/_assets/favicons/favicon-32x32.png') - link(rel='icon', type='image/png', sizes='16x16', href='/_assets/favicons/favicon-16x16.png') - link(rel='mask-icon', href='/_assets/favicons/safari-pinned-tab.svg', color='#1976d2') - link(rel='manifest', href='/_assets/manifest.json') + link(rel='apple-touch-icon', sizes='180x180', href='/_assets-legacy/favicons/apple-touch-icon.png') + link(rel='icon', type='image/png', sizes='192x192', href='/_assets-legacy/favicons/android-chrome-192x192.png') + link(rel='icon', type='image/png', sizes='32x32', href='/_assets-legacy/favicons/favicon-32x32.png') + link(rel='icon', type='image/png', sizes='16x16', href='/_assets-legacy/favicons/favicon-16x16.png') + link(rel='mask-icon', href='/_assets-legacy/favicons/safari-pinned-tab.svg', color='#1976d2') + link(rel='manifest', href='/_assets-legacy/manifest.json') //- Site Properties script. diff --git a/package.json b/package.json index 9c73d22e..e1725b2e 100644 --- a/package.json +++ b/package.json @@ -165,6 +165,7 @@ "semver": "7.3.7", "serve-favicon": "2.5.0", "simple-git": "2.21.0", + "socket.io": "4.5.2", "ssh2": "1.9.0", "ssh2-promise": "1.0.2", "striptags": "3.2.0", diff --git a/server/controllers/auth.js b/server/controllers/auth.js index a63df757..03092eed 100644 --- a/server/controllers/auth.js +++ b/server/controllers/auth.js @@ -81,11 +81,13 @@ router.all('/login/:strategy/callback', async (req, res, next) => { /** * Logout */ -router.get('/logout', async (req, res) => { +router.get('/logout', async (req, res, next) => { const redirURL = await WIKI.models.users.logout({ req, res }) - req.logout() - res.clearCookie('jwt') - res.redirect(redirURL) + req.logout((err) => { + if (err) { return next(err) } + res.clearCookie('jwt') + res.redirect(redirURL) + }) }) /** diff --git a/server/controllers/common.js b/server/controllers/common.js index f0b985d0..d55ff351 100644 --- a/server/controllers/common.js +++ b/server/controllers/common.js @@ -113,11 +113,15 @@ router.get(['/d', '/d/*'], async (req, res, next) => { /** * Create/Edit document */ -router.get(['/e', '/e/*'], async (req, res, next) => { +router.get(['/_edit', '/_edit/*'], async (req, res, next) => { const pageArgs = pageHelper.parsePath(req.path, { stripExt: true }) + if (pageArgs.path === '') { + return res.redirect(`/_edit/home`) + } + if (WIKI.config.lang.namespacing && !pageArgs.explicitLocale) { - return res.redirect(`/e/${pageArgs.locale}/${pageArgs.path}`) + return res.redirect(`/_edit/${pageArgs.locale}/${pageArgs.path}`) } req.i18n.changeLanguage(pageArgs.locale) diff --git a/server/controllers/ws.js b/server/controllers/ws.js new file mode 100644 index 00000000..51151c93 --- /dev/null +++ b/server/controllers/ws.js @@ -0,0 +1,31 @@ +const chalk = require('chalk') +const os = require('node:os') + +/* global WIKI */ + +module.exports = () => { + WIKI.servers.ws.on('connection', (socket) => { + // TODO: Validate token + permissions + const token = socket.handshake.auth.token + console.info(token) + + const listeners = {} + + socket.on('server:logs', () => { + socket.emit('server:log', chalk`{greenBright Streaming logs from {bold Wiki.js} instance {yellowBright.bold ${WIKI.INSTANCE_ID}} on host {yellowBright.bold ${os.hostname()}}...}`) + listeners.serverLogs = (msg) => { + socket.emit('server:log', msg) + } + WIKI.logger.ws.addListener('log', listeners.serverLogs) + WIKI.logger.warn(`User XYZ is streaming server logs. ( Listeners: ${WIKI.logger.ws.listenerCount('log')} )`) + }) + + socket.on('disconnect', () => { + if (listeners.serverLogs) { + WIKI.logger.ws.removeListener('log', listeners.serverLogs) + delete listeners.serverLogs + } + WIKI.logger.warn(`User XYZ has stopped streaming server logs. ( Listeners: ${WIKI.logger.ws.listenerCount('log')} )`) + }) + }) +} diff --git a/server/core/auth.js b/server/core/auth.js index 09eca7e7..848fb7a6 100644 --- a/server/core/auth.js +++ b/server/core/auth.js @@ -112,6 +112,7 @@ module.exports = { WIKI.auth.passport.authenticate('jwt', {session: false}, async (err, user, info) => { if (err) { return next() } let mustRevalidate = false + const strategyId = user.pvd // Expired but still valid within N days, just renew if (info instanceof Error && info.name === 'TokenExpiredError') { @@ -143,10 +144,11 @@ module.exports = { if (mustRevalidate) { const jwtPayload = jwt.decode(securityHelper.extractJWT(req)) try { - const newToken = await WIKI.models.users.refreshToken(jwtPayload.id) + const newToken = await WIKI.models.users.refreshToken(jwtPayload.id, jwtPayload.pvd) user = newToken.user user.permissions = user.getPermissions() user.groups = user.getGroups() + user.strategyId = strategyId req.user = user // Try headers, otherwise cookies for response @@ -163,6 +165,7 @@ module.exports = { user = await WIKI.models.users.getById(user.id) user.permissions = user.getPermissions() user.groups = user.getGroups() + user.strategyId = strategyId req.user = user } else { // JWT is NOT valid, set as guest diff --git a/server/core/logger.js b/server/core/logger.js index 912f2171..664296fa 100644 --- a/server/core/logger.js +++ b/server/core/logger.js @@ -17,6 +17,8 @@ const primaryLogger = new Logger() let ignoreNextLevels = false +primaryLogger.ws = new EventEmitter() + LEVELS.forEach(lvl => { primaryLogger[lvl] = (...args) => { primaryLogger.emit(lvl, ...args) @@ -24,16 +26,20 @@ LEVELS.forEach(lvl => { if (!ignoreNextLevels) { primaryLogger.on(lvl, (msg) => { + let formatted = '' if (WIKI.config.logFormat === 'json') { - console.log(JSON.stringify({ + formatted = JSON.stringify({ timestamp: new Date().toISOString(), instance: WIKI.INSTANCE_ID, level: lvl, message: msg - })) + }) } else { - console.log(chalk`${new Date().toISOString()} {dim [${WIKI.INSTANCE_ID}]} {${LEVELCOLORS[lvl]}.bold ${lvl}}: ${msg}`) + formatted = chalk`${new Date().toISOString()} {dim [${WIKI.INSTANCE_ID}]} {${LEVELCOLORS[lvl]}.bold ${lvl}}: ${msg}` } + + console.log(formatted) + primaryLogger.ws.emit('log', formatted) }) } if (lvl === WIKI.config.logLevel) { diff --git a/server/core/servers.js b/server/core/servers.js index 2bc30ffa..5ba59982 100644 --- a/server/core/servers.js +++ b/server/core/servers.js @@ -4,27 +4,26 @@ const https = require('https') const { ApolloServer } = require('apollo-server-express') const Promise = require('bluebird') const _ = require('lodash') +const io = require('socket.io') const { ApolloServerPluginLandingPageGraphQLPlayground, ApolloServerPluginLandingPageProductionDefault } = require('apollo-server-core') /* global WIKI */ module.exports = { - servers: { - graph: null, - http: null, - https: null - }, + graph: null, + http: null, + https: null, + ws: null, connections: new Map(), le: null, /** - * Start HTTP Server + * Initialize HTTP Server */ - async startHTTP () { + async initHTTP () { WIKI.logger.info(`HTTP Server on port: [ ${WIKI.config.port} ]`) - this.servers.http = http.createServer(WIKI.app) + this.http = http.createServer(WIKI.app) - this.servers.http.listen(WIKI.config.port, WIKI.config.bindIP) - this.servers.http.on('error', (error) => { + this.http.on('error', (error) => { if (error.syscall !== 'listen') { throw error } @@ -41,11 +40,11 @@ module.exports = { } }) - this.servers.http.on('listening', () => { + this.http.on('listening', () => { WIKI.logger.info('HTTP Server: [ RUNNING ]') }) - this.servers.http.on('connection', conn => { + this.http.on('connection', conn => { let connKey = `http:${conn.remoteAddress}:${conn.remotePort}` this.connections.set(connKey, conn) conn.on('close', () => { @@ -54,9 +53,15 @@ module.exports = { }) }, /** - * Start HTTPS Server + * Start HTTP Server */ - async startHTTPS () { + async startHTTP () { + this.http.listen(WIKI.config.port, WIKI.config.bindIP) + }, + /** + * Initialize HTTPS Server + */ + async initHTTPS () { if (WIKI.config.ssl.provider === 'letsencrypt') { this.le = require('./letsencrypt') await this.le.init() @@ -82,10 +87,10 @@ module.exports = { WIKI.logger.error(err) return process.exit(1) } - this.servers.https = https.createServer(tlsOpts, WIKI.app) + this.https = https.createServer(tlsOpts, WIKI.app) - this.servers.https.listen(WIKI.config.ssl.port, WIKI.config.bindIP) - this.servers.https.on('error', (error) => { + this.https.listen(WIKI.config.ssl.port, WIKI.config.bindIP) + this.https.on('error', (error) => { if (error.syscall !== 'listen') { throw error } @@ -102,11 +107,11 @@ module.exports = { } }) - this.servers.https.on('listening', () => { + this.https.on('listening', () => { WIKI.logger.info('HTTPS Server: [ RUNNING ]') }) - this.servers.https.on('connection', conn => { + this.https.on('connection', conn => { let connKey = `https:${conn.remoteAddress}:${conn.remotePort}` this.connections.set(connKey, conn) conn.on('close', () => { @@ -114,12 +119,18 @@ module.exports = { }) }) }, + /** + * Start HTTPS Server + */ + async startHTTPS () { + this.https.listen(WIKI.config.ssl.port, WIKI.config.bindIP) + }, /** * Start GraphQL Server */ async startGraphQL () { const graphqlSchema = require('../graph') - this.servers.graph = new ApolloServer({ + this.graph = new ApolloServer({ schema: graphqlSchema, uploads: false, context: ({ req, res }) => ({ req, res }), @@ -129,12 +140,31 @@ module.exports = { }) : ApolloServerPluginLandingPageProductionDefault({ footer: false }) - // ApolloServerPluginDrainHttpServer({ httpServer: this.servers.http }) - // ...(this.servers.https && ApolloServerPluginDrainHttpServer({ httpServer: this.servers.https })) + // ApolloServerPluginDrainHttpServer({ httpServer: this.http }) + // ...(this.https && ApolloServerPluginDrainHttpServer({ httpServer: this.https })) ] }) - await this.servers.graph.start() - this.servers.graph.applyMiddleware({ app: WIKI.app, cors: false, path: '/_graphql' }) + await this.graph.start() + this.graph.applyMiddleware({ app: WIKI.app, cors: false, path: '/_graphql' }) + }, + /** + * Start Socket.io WebSocket Server + */ + async initWebSocket() { + if (this.https) { + this.ws = new io.Server(this.https, { + path: '/_ws/', + serveClient: false + }) + WIKI.logger.info(`WebSocket Server attached to HTTPS Server [ OK ]`) + } else { + this.ws = new io.Server(this.http, { + path: '/_ws/', + serveClient: false, + cors: true // TODO: dev only, replace with app settings once stable + }) + WIKI.logger.info(`WebSocket Server attached to HTTP Server [ OK ]`) + } }, /** * Close all active connections @@ -156,15 +186,15 @@ module.exports = { */ async stopServers () { this.closeConnections() - if (this.servers.http) { - await Promise.fromCallback(cb => { this.servers.http.close(cb) }) - this.servers.http = null + if (this.http) { + await Promise.fromCallback(cb => { this.http.close(cb) }) + this.http = null } - if (this.servers.https) { - await Promise.fromCallback(cb => { this.servers.https.close(cb) }) - this.servers.https = null + if (this.https) { + await Promise.fromCallback(cb => { this.https.close(cb) }) + this.https = null } - this.servers.graph = null + this.graph = null }, /** * Restart Server @@ -173,17 +203,19 @@ module.exports = { this.closeConnections(srv) switch (srv) { case 'http': - if (this.servers.http) { - await Promise.fromCallback(cb => { this.servers.http.close(cb) }) - this.servers.http = null + if (this.http) { + await Promise.fromCallback(cb => { this.http.close(cb) }) + this.http = null } + this.initHTTP() this.startHTTP() break case 'https': - if (this.servers.https) { - await Promise.fromCallback(cb => { this.servers.https.close(cb) }) - this.servers.https = null + if (this.https) { + await Promise.fromCallback(cb => { this.https.close(cb) }) + this.https = null } + this.initHTTPS() this.startHTTPS() break default: diff --git a/server/graph/resolvers/hooks.js b/server/graph/resolvers/hooks.js index 9c5e8e7a..cf30dd6a 100644 --- a/server/graph/resolvers/hooks.js +++ b/server/graph/resolvers/hooks.js @@ -6,6 +6,7 @@ const _ = require('lodash') module.exports = { Query: { async hooks () { + WIKI.logger.warn('Seriously man') return WIKI.models.hooks.query().orderBy('name') }, async hookById (obj, args) { @@ -20,16 +21,18 @@ module.exports = { try { // -> Validate inputs if (!args.name || args.name.length < 1) { - throw WIKI.ERROR(new Error('Invalid Hook Name'), 'HookCreateInvalidName') + throw new WIKI.Error.Custom('HookCreateInvalidName', 'Invalid Hook Name') } if (!args.events || args.events.length < 1) { - throw WIKI.ERROR(new Error('Invalid Hook Events'), 'HookCreateInvalidEvents') + throw new WIKI.Error.Custom('HookCreateInvalidEvents', 'Invalid Hook Events') } if (!args.url || args.url.length < 8 || !args.url.startsWith('http')) { - throw WIKI.ERROR(new Error('Invalid Hook URL'), 'HookCreateInvalidURL') + throw new WIKI.Error.Custom('HookCreateInvalidURL', 'Invalid Hook URL') } // -> Create hook const newHook = await WIKI.models.hooks.createHook(args) + WIKI.logger.debug(`New Hook ${newHook.id} created successfully.`) + return { operation: graphHelper.generateSuccess('Hook created successfully'), hook: newHook @@ -46,20 +49,21 @@ module.exports = { // -> Load hook const hook = await WIKI.models.hooks.query().findById(args.id) if (!hook) { - throw WIKI.ERROR(new Error('Invalid Hook ID'), 'HookInvalidId') + throw new WIKI.Error.Custom('HookInvalidId', 'Invalid Hook ID') } // -> Check for bad input if (_.has(args.patch, 'name') && args.patch.name.length < 1) { - throw WIKI.ERROR(new Error('Invalid Hook Name'), 'HookCreateInvalidName') + throw new WIKI.Error.Custom('HookCreateInvalidName', 'Invalid Hook Name') } if (_.has(args.patch, 'events') && args.patch.events.length < 1) { - throw WIKI.ERROR(new Error('Invalid Hook Events'), 'HookCreateInvalidEvents') + throw new WIKI.Error.Custom('HookCreateInvalidEvents', 'Invalid Hook Events') } if (_.has(args.patch, 'url') && (_.trim(args.patch.url).length < 8 || !args.patch.url.startsWith('http'))) { - throw WIKI.ERROR(new Error('URL is invalid.'), 'HookInvalidURL') + throw new WIKI.Error.Custom('HookInvalidURL', 'URL is invalid.') } // -> Update hook await WIKI.models.hooks.query().findById(args.id).patch(args.patch) + WIKI.logger.debug(`Hook ${args.id} updated successfully.`) return { operation: graphHelper.generateSuccess('Hook updated successfully') @@ -74,6 +78,7 @@ module.exports = { async deleteHook (obj, args) { try { await WIKI.models.hooks.deleteHook(args.id) + WIKI.logger.debug(`Hook ${args.id} deleted successfully.`) return { operation: graphHelper.generateSuccess('Hook deleted successfully') } diff --git a/server/graph/resolvers/mail.js b/server/graph/resolvers/mail.js index a86c443b..611c2376 100644 --- a/server/graph/resolvers/mail.js +++ b/server/graph/resolvers/mail.js @@ -30,7 +30,7 @@ module.exports = { }) return { - responseResult: graphHelper.generateSuccess('Test email sent successfully.') + operation: graphHelper.generateSuccess('Test email sent successfully.') } } catch (err) { return graphHelper.generateError(err) @@ -58,7 +58,7 @@ module.exports = { WIKI.mail.init() return { - responseResult: graphHelper.generateSuccess('Mail configuration updated successfully.') + operation: graphHelper.generateSuccess('Mail configuration updated successfully.') } } catch (err) { return graphHelper.generateError(err) diff --git a/server/graph/resolvers/site.js b/server/graph/resolvers/site.js index 5fec0acf..9dd1b24e 100644 --- a/server/graph/resolvers/site.js +++ b/server/graph/resolvers/site.js @@ -53,10 +53,10 @@ module.exports = { try { // -> Validate inputs if (!args.hostname || args.hostname.length < 1 || !/^(\\*)|([a-z0-9\-.:]+)$/.test(args.hostname)) { - throw WIKI.ERROR(new Error('Invalid Site Hostname'), 'SiteCreateInvalidHostname') + throw new WIKI.Error.Custom('SiteCreateInvalidHostname', 'Invalid Site Hostname') } if (!args.title || args.title.length < 1 || !/^[^<>"]+$/.test(args.title)) { - throw WIKI.ERROR(new Error('Invalid Site Title'), 'SiteCreateInvalidTitle') + throw new WIKI.Error.Custom('SiteCreateInvalidTitle', 'Invalid Site Title') } // -> Check for duplicate catch-all if (args.hostname === '*') { @@ -64,7 +64,7 @@ module.exports = { hostname: args.hostname }).first() if (site) { - throw WIKI.ERROR(new Error('A site with a catch-all hostname already exists! Cannot have 2 catch-all hostnames.'), 'SiteCreateDuplicateCatchAll') + throw new WIKI.Error.Custom('SiteCreateDuplicateCatchAll', 'A site with a catch-all hostname already exists! Cannot have 2 catch-all hostnames.') } } // -> Create site @@ -88,17 +88,17 @@ module.exports = { // -> Load site const site = await WIKI.models.sites.query().findById(args.id) if (!site) { - throw WIKI.ERROR(new Error('Invalid Site ID'), 'SiteInvalidId') + throw new WIKI.Error.Custom('SiteInvalidId', 'Invalid Site ID') } // -> Check for bad input if (_.has(args.patch, 'hostname') && _.trim(args.patch.hostname).length < 1) { - throw WIKI.ERROR(new Error('Hostname is invalid.'), 'SiteInvalidHostname') + throw new WIKI.Error.Custom('SiteInvalidHostname', 'Hostname is invalid.') } // -> Check for duplicate catch-all if (args.patch.hostname === '*' && site.hostname !== '*') { const dupSite = await WIKI.models.sites.query().where({ hostname: '*' }).first() if (dupSite) { - throw WIKI.ERROR(new Error(`Site ${dupSite.config.title} with a catch-all hostname already exists! Cannot have 2 catch-all hostnames.`), 'SiteUpdateDuplicateCatchAll') + throw new WIKI.Error.Custom('SiteUpdateDuplicateCatchAll', `Site ${dupSite.config.title} with a catch-all hostname already exists! Cannot have 2 catch-all hostnames.`) } } // -> Format Code @@ -132,7 +132,7 @@ module.exports = { // -> Ensure site isn't last one const sitesCount = await WIKI.models.sites.query().count('id').first() if (sitesCount?.count && _.toNumber(sitesCount?.count) <= 1) { - throw WIKI.ERROR(new Error('Cannot delete the last site. At least 1 site must exists at all times.'), 'SiteDeleteLastSite') + throw new WIKI.Error.Custom('SiteDeleteLastSite', 'Cannot delete the last site. At least 1 site must exists at all times.') } // -> Delete site await WIKI.models.sites.deleteSite(args.id) diff --git a/server/graph/resolvers/system.js b/server/graph/resolvers/system.js index c73b737d..50ac3f11 100644 --- a/server/graph/resolvers/system.js +++ b/server/graph/resolvers/system.js @@ -30,6 +30,24 @@ module.exports = { } }, Mutation: { + async disconnectWS (obj, args, context) { + WIKI.servers.ws.disconnectSockets(true) + WIKI.logger.info('All active websocket connections have been terminated.') + return { + operation: graphHelper.generateSuccess('All websocket connections closed successfully.') + } + }, + async installExtension (obj, args, context) { + try { + await WIKI.extensions.ext[args.key].install() + // TODO: broadcast ext install + return { + status: graphHelper.generateSuccess('Extension installed successfully') + } + } catch (err) { + return graphHelper.generateError(err) + } + }, async updateSystemFlags (obj, args, context) { WIKI.config.flags = _.transform(args.flags, (result, row) => { _.set(result, row.key, row.value) @@ -47,17 +65,6 @@ module.exports = { return { status: graphHelper.generateSuccess('System Security configuration applied successfully') } - }, - async installExtension (obj, args, context) { - try { - await WIKI.extensions.ext[args.key].install() - // TODO: broadcast ext install - return { - status: graphHelper.generateSuccess('Extension installed successfully') - } - } catch (err) { - return graphHelper.generateError(err) - } } }, SystemInfo: { diff --git a/server/graph/scalars/date.js b/server/graph/scalars/date.js index 4a20a6ff..f2939711 100644 --- a/server/graph/scalars/date.js +++ b/server/graph/scalars/date.js @@ -1,10 +1,19 @@ const gql = require('graphql') +const { DateTime } = require('luxon') + +function parseDateTime (value) { + const nDate = DateTime.fromISO(value) + return nDate.isValid ? nDate : null +} module.exports = new gql.GraphQLScalarType({ name: 'Date', description: 'ISO date-time string at UTC', parseValue(value) { - return new Date(value) + if (typeof value !== 'string') { + throw new TypeError('Date value must be an string!') + } + return parseDateTime(value) }, serialize(value) { return value.toISOString() @@ -13,6 +22,6 @@ module.exports = new gql.GraphQLScalarType({ if (ast.kind !== gql.Kind.STRING) { throw new TypeError('Date value must be an string!') } - return new Date(ast.value) + return parseDateTime(ast.value) } }) diff --git a/server/graph/schemas/page.graphql b/server/graph/schemas/page.graphql index c87106c4..5c48ee83 100644 --- a/server/graph/schemas/page.graphql +++ b/server/graph/schemas/page.graphql @@ -64,11 +64,11 @@ extend type Query { extend type Mutation { createPage( + siteId: UUID! content: String! description: String! editor: String! isPublished: Boolean! - isPrivate: Boolean! locale: String! path: String! publishEndDate: Date @@ -84,7 +84,6 @@ extend type Mutation { content: String description: String editor: String - isPrivate: Boolean isPublished: Boolean locale: String path: String @@ -164,9 +163,7 @@ type Page { hash: String title: String description: String - isPrivate: Boolean isPublished: Boolean - privateNS: String publishStartDate: Date publishEndDate: Date tags: [PageTag] diff --git a/server/graph/schemas/system.graphql b/server/graph/schemas/system.graphql index 5fcf229f..f7301ad7 100644 --- a/server/graph/schemas/system.graphql +++ b/server/graph/schemas/system.graphql @@ -10,6 +10,12 @@ extend type Query { } extend type Mutation { + disconnectWS: DefaultResponse + + installExtension( + key: String! + ): DefaultResponse + updateSystemFlags( flags: [SystemFlagInput]! ): DefaultResponse @@ -34,10 +40,6 @@ extend type Mutation { uploadMaxFileSize: Int uploadScanSVG: Boolean ): DefaultResponse - - installExtension( - key: String! - ): DefaultResponse } # ----------------------------------------------- diff --git a/server/helpers/error.js b/server/helpers/error.js index 6bff807a..72a3b326 100644 --- a/server/helpers/error.js +++ b/server/helpers/error.js @@ -1,6 +1,9 @@ const CustomError = require('custom-error-instance') module.exports = { + Custom (slug, message) { + return CustomError(slug, { message }) + }, AssetDeleteForbidden: CustomError('AssetDeleteForbidden', { message: 'You are not authorized to delete this asset.' }), diff --git a/server/helpers/page.js b/server/helpers/page.js index 8049edc1..57714daf 100644 --- a/server/helpers/page.js +++ b/server/helpers/page.js @@ -42,7 +42,7 @@ module.exports = { p = _.trim(p) return !_.isEmpty(p) && p !== '..' && p !== '.' }) - if (pathParts[0].length === 1) { + if (pathParts[0].startsWith('_')) { pathParts.shift() } if (localeSegmentRegex.test(pathParts[0])) { @@ -102,7 +102,7 @@ module.exports = { */ isReservedPath(rawPath) { const firstSection = _.head(rawPath.split('/')) - if (firstSection.length <= 1) { + if (firstSection.length < 1) { return true } else if (localeSegmentRegex.test(firstSection)) { return true diff --git a/server/models/pages.js b/server/models/pages.js index 897927b0..b7d839a7 100644 --- a/server/models/pages.js +++ b/server/models/pages.js @@ -227,6 +227,11 @@ module.exports = class Page extends Model { * @returns {Promise} Promise of the Page Model Instance */ static async createPage(opts) { + // -> Validate site + if (!WIKI.sites[opts.siteId]) { + throw new WIKI.Error.Custom('InvalidSiteId', 'Site ID is invalid.') + } + // -> Validate path if (opts.path.includes('.') || opts.path.includes(' ') || opts.path.includes('\\') || opts.path.includes('//')) { throw new WIKI.Error.PageIllegalPath() @@ -295,8 +300,9 @@ module.exports = class Page extends Model { publishState: opts.publishState, localeCode: opts.locale, path: opts.path, - publishEndDate: opts.publishEndDate || '', - publishStartDate: opts.publishStartDate || '', + publishEndDate: opts.publishEndDate?.toISO(), + publishStartDate: opts.publishStartDate?.toISO(), + siteId: opts.siteId, title: opts.title, toc: '[]', extra: JSON.stringify({ diff --git a/server/models/users.js b/server/models/users.js index f6cf793d..706aaefb 100644 --- a/server/models/users.js +++ b/server/models/users.js @@ -381,7 +381,7 @@ module.exports = class User extends Model { return new Promise((resolve, reject) => { context.req.login(user, { session: false }, async errc => { if (errc) { return reject(errc) } - const jwtToken = await WIKI.models.users.refreshToken(user) + const jwtToken = await WIKI.models.users.refreshToken(user, strategyId) resolve({ jwt: jwtToken.token, redirect }) }) }) @@ -390,7 +390,7 @@ module.exports = class User extends Model { /** * Generate a new token for a user */ - static async refreshToken(user) { + static async refreshToken(user, provider) { if (_.isString(user)) { user = await WIKI.models.users.query().findById(user).withGraphFetched('groups').modifyGraph('groups', builder => { builder.select('groups.id', 'permissions') @@ -415,7 +415,8 @@ module.exports = class User extends Model { token: jwt.sign({ id: user.id, email: user.email, - groups: user.getGroups() + groups: user.getGroups(), + ...provider && { pvd: provider } }, { key: WIKI.config.auth.certs.private, passphrase: WIKI.config.auth.secret @@ -831,9 +832,17 @@ module.exports = class User extends Model { if (!context.req.user || context.req.user.id === WIKI.config.auth.guestUserId) { return '/' } - const usr = await WIKI.models.users.query().findById(context.req.user.id).select('providerKey') - const provider = _.find(WIKI.auth.strategies, ['key', usr.providerKey]) - return provider.logout ? provider.logout(provider.config) : '/' + if (context.req.user.strategyId && _.has(WIKI.auth.strategies, context.req.user.strategyId)) { + const selStrategy = WIKI.auth.strategies[context.req.user.strategyId] + if (!selStrategy.isEnabled) { + throw new WIKI.Error.AuthProviderInvalid() + } + const provider = _.find(WIKI.data.authentication, ['key', selStrategy.module]) + if (provider.logout) { + return provider.logout(provider.config) + } + } + return '/' } static async getGuestUser () { diff --git a/server/web.js b/server/web.js index ba88ef9d..a2b61ba0 100644 --- a/server/web.js +++ b/server/web.js @@ -37,6 +37,26 @@ module.exports = async () => { WIKI.app = app app.use(compression()) + // ---------------------------------------- + // Initialize HTTP/HTTPS Server + // ---------------------------------------- + + const useHTTPS = WIKI.config.ssl.enabled === true || WIKI.config.ssl.enabled === 'true' || WIKI.config.ssl.enabled === 1 || WIKI.config.ssl.enabled === '1' + + await WIKI.servers.initHTTP() + + if (useHTTPS) { + await WIKI.servers.initHTTPS() + } + + await WIKI.servers.initWebSocket() + + // ---------------------------------------- + // Attach WebSocket Server + // ---------------------------------------- + + ctrl.ws() + // ---------------------------------------- // Security // ---------------------------------------- @@ -208,7 +228,7 @@ module.exports = async () => { await WIKI.servers.startHTTP() - if (WIKI.config.ssl.enabled === true || WIKI.config.ssl.enabled === 'true' || WIKI.config.ssl.enabled === 1 || WIKI.config.ssl.enabled === '1') { + if (useHTTPS) { await WIKI.servers.startHTTPS() } diff --git a/ux/package.json b/ux/package.json index cf2c4c2d..181f4215 100644 --- a/ux/package.json +++ b/ux/package.json @@ -72,6 +72,7 @@ "pinia": "2.0.20", "pug": "3.0.2", "quasar": "2.7.7", + "socket.io-client": "4.5.2", "tippy.js": "6.3.7", "uuid": "8.3.2", "v-network-graph": "0.6.6", @@ -81,6 +82,7 @@ "vue-router": "4.1.3", "vue3-otp-input": "0.3.6", "vuedraggable": "4.1.0", + "xterm": "4.19.0", "zxcvbn": "4.4.2" }, "devDependencies": { diff --git a/ux/public/_assets/icons/fluent-linux-terminal-animated.svg b/ux/public/_assets/icons/fluent-linux-terminal-animated.svg new file mode 100644 index 00000000..770515ee --- /dev/null +++ b/ux/public/_assets/icons/fluent-linux-terminal-animated.svg @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ux/public/_assets/icons/fluent-linux-terminal.svg b/ux/public/_assets/icons/fluent-linux-terminal.svg new file mode 100644 index 00000000..fc1a6a65 --- /dev/null +++ b/ux/public/_assets/icons/fluent-linux-terminal.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ux/public/_assets/icons/ultraviolet-disconnected.svg b/ux/public/_assets/icons/ultraviolet-disconnected.svg new file mode 100644 index 00000000..47ed81fc --- /dev/null +++ b/ux/public/_assets/icons/ultraviolet-disconnected.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ux/src/i18n/locales/en.json b/ux/src/i18n/locales/en.json index f3a02bdd..76991e51 100644 --- a/ux/src/i18n/locales/en.json +++ b/ux/src/i18n/locales/en.json @@ -1482,5 +1482,19 @@ "welcome.title": "Welcome to Wiki.js!", "welcome.subtitle": "Let's get started...", "welcome.createHome": "Create the homepage", - "welcome.admin": "Administration Area" + "welcome.admin": "Administration Area", + "admin.terminal.title": "Terminal", + "admin.terminal.subtitle": "View process logs in real-time", + "admin.terminal.command": "Command", + "admin.terminal.logs": "Logs", + "admin.terminal.connect": "Connect", + "admin.terminal.disconnect": "Disconnect", + "admin.terminal.clear": "Clear", + "admin.terminal.connecting": "Connecting to server...", + "admin.terminal.connected": "Connected.", + "admin.terminal.disconnected": "Disconnected.", + "admin.terminal.connectError": "Connection Error:", + "admin.utilities.disconnectWS": "Disconnect WebSocket Sessions", + "admin.utilities.disconnectWSHint": "Force all active websocket connections to be closed.", + "admin.utilities.disconnectWSSuccess": "All active websocket connections have been terminated." } diff --git a/ux/src/layouts/AdminLayout.vue b/ux/src/layouts/AdminLayout.vue index e7e176d7..33474067 100644 --- a/ux/src/layouts/AdminLayout.vue +++ b/ux/src/layouts/AdminLayout.vue @@ -166,6 +166,10 @@ q-layout.admin(view='hHh Lpr lff') q-item-section {{ t('admin.system.title') }} q-item-section(side) status-light(:color='adminStore.isVersionLatest ? `positive` : `warning`') + q-item(to='/_admin/terminal', v-ripple, active-class='bg-primary text-white') + q-item-section(avatar) + q-icon(name='img:/_assets/icons/fluent-linux-terminal.svg') + q-item-section {{ t('admin.terminal.title') }} q-item(to='/_admin/utilities', v-ripple, active-class='bg-primary text-white') q-item-section(avatar) q-icon(name='img:/_assets/icons/fluent-swiss-army-knife.svg') diff --git a/ux/src/pages/AdminTerminal.vue b/ux/src/pages/AdminTerminal.vue new file mode 100644 index 00000000..5e224896 --- /dev/null +++ b/ux/src/pages/AdminTerminal.vue @@ -0,0 +1,169 @@ + + + + + diff --git a/ux/src/pages/AdminUtilities.vue b/ux/src/pages/AdminUtilities.vue index d3967a3c..64d7785a 100644 --- a/ux/src/pages/AdminUtilities.vue +++ b/ux/src/pages/AdminUtilities.vue @@ -19,6 +19,19 @@ q-page.admin-utilities .q-pa-md.q-gutter-md q-card.shadow-1 q-list(separator) + q-item + blueprint-icon(icon='disconnected', :hue-rotate='45') + q-item-section + q-item-label {{t(`admin.utilities.disconnectWS`)}} + q-item-label(caption) {{t(`admin.utilities.disconnectWSHint`)}} + q-item-section(side) + q-btn.acrylic-btn( + flat + icon='las la-arrow-circle-right' + color='primary' + @click='disconnectWS' + :label='t(`common.actions.proceed`)' + ) q-item blueprint-icon(icon='database-export', :hue-rotate='45') q-item-section @@ -103,6 +116,7 @@ q-page.admin-utilities import { computed, reactive } from 'vue' import { useMeta, useQuasar } from 'quasar' import { useI18n } from 'vue-i18n' +import gql from 'graphql-tag' import { useSiteStore } from 'src/stores/site' @@ -140,6 +154,42 @@ const purgeHistoryTimeframes = computed(() => ([ { value: '1y', label: t('admin.utitilies.purgeHistoryYear', 1, { count: 1 }) }, { value: '2y', label: t('admin.utitilies.purgeHistoryYear', 2, { count: 2 }) } ])) + +// METHODS + +async function disconnectWS () { + $q.loading.show() + try { + const resp = await APOLLO_CLIENT.mutate({ + mutation: gql` + mutation disconnectWS { + disconnectWS { + operation { + succeeded + message + } + } + } + `, + fetchPolicy: 'network-only' + }) + if (resp?.data?.disconnectWS?.operation?.succeeded) { + $q.notify({ + type: 'positive', + message: t('admin.utilities.disconnectWSSuccess') + }) + } else { + throw new Error(resp?.data?.disconnectWS?.operation?.succeeded) + } + } catch (err) { + $q.notify({ + type: 'negative', + message: 'Failed to disconnect WS connections.', + caption: err.message + }) + } + $q.loading.hide() +}