Full Code of requarks/wiki for AI

main f0f71536ab2a cached
695 files
2.3 MB
632.0k tokens
832 symbols
1 requests
Download .txt
Showing preview only (2,511K chars total). Download the full file or copy to clipboard to get everything.
Repository: requarks/wiki
Branch: main
Commit: f0f71536ab2a
Files: 695
Total size: 2.3 MB

Directory structure:
gitextract_3rhs9jgy/

├── .babelrc
├── .devcontainer/
│   └── devcontainer.json
├── .editorconfig
├── .eslintignore
├── .eslintrc.yml
├── .gitattributes
├── .github/
│   ├── CODE_OF_CONDUCT.md
│   ├── CONTRIBUTING.md
│   ├── FUNDING.yml
│   ├── ISSUE_TEMPLATE/
│   │   └── config.yml
│   ├── PULL_REQUEST_TEMPLATE.md
│   ├── auto_assign.yml
│   ├── issuecomplete.yml
│   └── workflows/
│       ├── build.yml
│       ├── helm.yml
│       └── packer.yml
├── .gitignore
├── .npmrc
├── .nvmrc
├── .vscode/
│   ├── extensions.json
│   ├── launch.json
│   └── settings.json
├── LICENSE
├── README.md
├── SECURITY.md
├── client/
│   ├── .modernizrrc.js
│   ├── client-app.js
│   ├── client-setup.js
│   ├── components/
│   │   ├── admin/
│   │   │   ├── admin-analytics.vue
│   │   │   ├── admin-api-create.vue
│   │   │   ├── admin-api.vue
│   │   │   ├── admin-auth.vue
│   │   │   ├── admin-comments.vue
│   │   │   ├── admin-contribute.vue
│   │   │   ├── admin-dashboard.vue
│   │   │   ├── admin-dev-flags.vue
│   │   │   ├── admin-editor.vue
│   │   │   ├── admin-extensions.vue
│   │   │   ├── admin-general.vue
│   │   │   ├── admin-groups-edit-permissions.vue
│   │   │   ├── admin-groups-edit-rules.vue
│   │   │   ├── admin-groups-edit-users.vue
│   │   │   ├── admin-groups-edit.vue
│   │   │   ├── admin-groups.vue
│   │   │   ├── admin-locale.vue
│   │   │   ├── admin-logging-console.vue
│   │   │   ├── admin-logging.vue
│   │   │   ├── admin-mail.vue
│   │   │   ├── admin-navigation.vue
│   │   │   ├── admin-pages-edit.vue
│   │   │   ├── admin-pages-visualize.vue
│   │   │   ├── admin-pages.vue
│   │   │   ├── admin-rendering.vue
│   │   │   ├── admin-search.vue
│   │   │   ├── admin-security.vue
│   │   │   ├── admin-ssl.vue
│   │   │   ├── admin-stats.vue
│   │   │   ├── admin-storage.vue
│   │   │   ├── admin-system.vue
│   │   │   ├── admin-tags.vue
│   │   │   ├── admin-theme.vue
│   │   │   ├── admin-users-create.vue
│   │   │   ├── admin-users-edit.vue
│   │   │   ├── admin-users.vue
│   │   │   ├── admin-utilities-auth.vue
│   │   │   ├── admin-utilities-cache.vue
│   │   │   ├── admin-utilities-content.vue
│   │   │   ├── admin-utilities-export.vue
│   │   │   ├── admin-utilities-importv1.vue
│   │   │   ├── admin-utilities-telemetry.vue
│   │   │   ├── admin-utilities.vue
│   │   │   └── admin-webhooks.vue
│   │   ├── admin.vue
│   │   ├── comments.vue
│   │   ├── common/
│   │   │   ├── duration-picker.vue
│   │   │   ├── loader.vue
│   │   │   ├── nav-header.vue
│   │   │   ├── notify.vue
│   │   │   ├── page-convert.vue
│   │   │   ├── page-delete.vue
│   │   │   ├── page-selector.vue
│   │   │   ├── password-strength.vue
│   │   │   ├── search-results.vue
│   │   │   ├── social-sharing.vue
│   │   │   ├── user-search.vue
│   │   │   ├── v-card-chin.vue
│   │   │   └── v-card-info.vue
│   │   ├── editor/
│   │   │   ├── api/
│   │   │   │   └── server-selector.vue
│   │   │   ├── ckeditor/
│   │   │   │   └── conflict.vue
│   │   │   ├── common/
│   │   │   │   ├── cmFold.js
│   │   │   │   └── katex.js
│   │   │   ├── editor-api.vue
│   │   │   ├── editor-asciidoc.vue
│   │   │   ├── editor-ckeditor.vue
│   │   │   ├── editor-code.vue
│   │   │   ├── editor-markdown.vue
│   │   │   ├── editor-modal-blocks.vue
│   │   │   ├── editor-modal-conflict.vue
│   │   │   ├── editor-modal-drawio.vue
│   │   │   ├── editor-modal-editorselect.vue
│   │   │   ├── editor-modal-media.vue
│   │   │   ├── editor-modal-properties.vue
│   │   │   ├── editor-modal-unsaved.vue
│   │   │   ├── editor-redirect.vue
│   │   │   └── markdown/
│   │   │       ├── help.vue
│   │   │       ├── plantuml.js
│   │   │       └── tabset.js
│   │   ├── editor.vue
│   │   ├── history.vue
│   │   ├── login.vue
│   │   ├── new-page.vue
│   │   ├── not-found.vue
│   │   ├── profile/
│   │   │   ├── comments.vue
│   │   │   ├── pages.vue
│   │   │   └── profile.vue
│   │   ├── profile.vue
│   │   ├── register.vue
│   │   ├── setup.vue
│   │   ├── source.vue
│   │   ├── tags.vue
│   │   ├── unauthorized.vue
│   │   └── welcome.vue
│   ├── graph/
│   │   ├── admin/
│   │   │   ├── analytics/
│   │   │   │   ├── analytics-mutation-save-providers.gql
│   │   │   │   └── analytics-query-providers.gql
│   │   │   ├── auth/
│   │   │   │   ├── auth-query-groups.gql
│   │   │   │   ├── auth-query-host.gql
│   │   │   │   └── auth-query-strategies.gql
│   │   │   ├── contribute/
│   │   │   │   └── contribute-query-contributors.gql
│   │   │   ├── dashboard/
│   │   │   │   └── dashboard-query-stats.gql
│   │   │   ├── dev/
│   │   │   │   ├── dev-mutation-save-flags.gql
│   │   │   │   └── dev-query-flags.gql
│   │   │   ├── groups/
│   │   │   │   ├── groups-mutation-assign.gql
│   │   │   │   ├── groups-mutation-create.gql
│   │   │   │   ├── groups-mutation-unassign.gql
│   │   │   │   └── groups-query-list.gql
│   │   │   ├── locale/
│   │   │   │   ├── locale-mutation-download.gql
│   │   │   │   ├── locale-mutation-save.gql
│   │   │   │   └── locale-query-list.gql
│   │   │   ├── logging/
│   │   │   │   ├── logging-mutation-save-loggers.gql
│   │   │   │   ├── logging-query-loggers.gql
│   │   │   │   └── logging-subscription-livetrail.gql
│   │   │   ├── mail/
│   │   │   │   ├── mail-mutation-save-config.gql
│   │   │   │   ├── mail-mutation-sendtest.gql
│   │   │   │   └── mail-query-config.gql
│   │   │   ├── pages/
│   │   │   │   ├── pages-query-list.gql
│   │   │   │   └── pages-query-single.gql
│   │   │   ├── rendering/
│   │   │   │   ├── rendering-mutation-save-renderers.gql
│   │   │   │   └── rendering-query-renderers.gql
│   │   │   ├── search/
│   │   │   │   ├── search-mutation-rebuild-index.gql
│   │   │   │   ├── search-mutation-save-engines.gql
│   │   │   │   └── search-query-engines.gql
│   │   │   ├── storage/
│   │   │   │   ├── storage-mutation-executeaction.gql
│   │   │   │   ├── storage-mutation-save-targets.gql
│   │   │   │   ├── storage-query-status.gql
│   │   │   │   └── storage-query-targets.gql
│   │   │   ├── system/
│   │   │   │   ├── system-mutation-upgrade.gql
│   │   │   │   └── system-query-info.gql
│   │   │   ├── theme/
│   │   │   │   ├── theme-mutation-save.gql
│   │   │   │   └── theme-query-config.gql
│   │   │   ├── users/
│   │   │   │   ├── users-mutation-create.gql
│   │   │   │   └── users-query-groups.gql
│   │   │   └── utilities/
│   │   │       ├── utilities-mutation-auth-regencerts.gql
│   │   │       ├── utilities-mutation-auth-resetguest.gql
│   │   │       ├── utilities-mutation-cache-flushcache.gql
│   │   │       ├── utilities-mutation-cache-flushuploads.gql
│   │   │       ├── utilities-mutation-content-migratelocale.gql
│   │   │       ├── utilities-mutation-content-rebuildtree.gql
│   │   │       ├── utilities-mutation-importv1-users.gql
│   │   │       ├── utilities-mutation-telemetry-resetid.gql
│   │   │       ├── utilities-mutation-telemetry-set.gql
│   │   │       └── utilities-query-telemetry.gql
│   │   ├── common/
│   │   │   ├── common-localization-query-translations.gql
│   │   │   ├── common-pages-mutation-delete.gql
│   │   │   ├── common-pages-mutation-move.gql
│   │   │   ├── common-pages-query-list.gql
│   │   │   ├── common-pages-query-search.gql
│   │   │   ├── common-pages-query-tags.gql
│   │   │   └── common-pages-query-tree.gql
│   │   ├── editor/
│   │   │   ├── editor-media-mutation-asset-delete.gql
│   │   │   ├── editor-media-mutation-asset-rename.gql
│   │   │   ├── editor-media-mutation-folder-create.gql
│   │   │   ├── editor-media-query-folder-list.gql
│   │   │   └── editor-media-query-list.gql
│   │   ├── login/
│   │   │   ├── login-mutation-changepassword.gql
│   │   │   ├── login-mutation-login.gql
│   │   │   ├── login-mutation-tfa.gql
│   │   │   └── login-query-strategies.gql
│   │   └── register/
│   │       └── register-mutation-create.gql
│   ├── helpers/
│   │   ├── compatibility.js
│   │   └── index.js
│   ├── index-app.js
│   ├── index-legacy.js
│   ├── index-setup.js
│   ├── libs/
│   │   ├── animate/
│   │   │   └── animate.scss
│   │   ├── codemirror-merge/
│   │   │   └── diff-match-patch.js
│   │   ├── markdown-it-underline/
│   │   │   └── index.js
│   │   ├── modernizr/
│   │   │   └── modernizr.js
│   │   ├── prism/
│   │   │   ├── prism.css
│   │   │   └── prism.js
│   │   └── twemoji/
│   │       └── twemoji-awesome.scss
│   ├── modules/
│   │   ├── boot.js
│   │   └── localization.js
│   ├── polyfills/
│   │   └── array-from.js
│   ├── scss/
│   │   ├── app.scss
│   │   ├── base/
│   │   │   ├── animation.scss
│   │   │   ├── base.scss
│   │   │   ├── icons.scss
│   │   │   ├── material.scss
│   │   │   └── mixins.scss
│   │   ├── components/
│   │   │   ├── codemirror.scss
│   │   │   ├── katex.scss
│   │   │   ├── v-btn.scss
│   │   │   ├── v-data-table.scss
│   │   │   ├── v-dialog.scss
│   │   │   ├── v-form.scss
│   │   │   └── v-tabs.scss
│   │   ├── fonts/
│   │   │   ├── arabic.scss
│   │   │   └── default.scss
│   │   ├── global.scss
│   │   ├── layout/
│   │   │   └── _rtl.scss
│   │   ├── legacy.scss
│   │   └── pages/
│   │       ├── _error.scss
│   │       ├── _new.scss
│   │       ├── _notfound.scss
│   │       ├── _unauthorized.scss
│   │       └── _welcome.scss
│   ├── static/
│   │   ├── browserconfig.xml
│   │   ├── favicons/
│   │   │   └── browserconfig.xml
│   │   ├── manifest.json
│   │   └── svg/
│   │       └── twemoji.asar
│   ├── store/
│   │   ├── admin.js
│   │   ├── editor.js
│   │   ├── index.js
│   │   ├── page.js
│   │   ├── site.js
│   │   └── user.js
│   └── themes/
│       └── default/
│           ├── components/
│           │   ├── nav-footer.vue
│           │   ├── nav-sidebar.vue
│           │   ├── page.vue
│           │   └── tabset.vue
│           ├── js/
│           │   └── app.js
│           ├── scss/
│           │   └── app.scss
│           └── theme.yml
├── config.sample.yml
├── cypress.json
├── dev/
│   ├── build/
│   │   ├── Dockerfile
│   │   └── config.yml
│   ├── build-arm/
│   │   └── Dockerfile
│   ├── containers/
│   │   ├── Dockerfile
│   │   ├── config.yml
│   │   └── docker-compose.yml
│   ├── cypress/
│   │   ├── ci-setup.sh
│   │   ├── integration/
│   │   │   └── setup.spec.js
│   │   ├── plugins/
│   │   │   └── index.js
│   │   └── support/
│   │       ├── commands.js
│   │       └── index.js
│   ├── examples/
│   │   └── docker-compose.yml
│   ├── helm/
│   │   ├── .helmignore
│   │   ├── Chart.yaml
│   │   ├── README.md
│   │   ├── templates/
│   │   │   ├── NOTES.txt
│   │   │   ├── _helpers.tpl
│   │   │   ├── deployment.yaml
│   │   │   ├── ingress.yaml
│   │   │   ├── postgresql-pvc.yaml
│   │   │   ├── postgresql-secret.yaml
│   │   │   ├── postgresql-service.yaml
│   │   │   ├── postgresql-statefulset.yaml
│   │   │   ├── service.yaml
│   │   │   ├── serviceaccount.yaml
│   │   │   └── tests/
│   │   │       └── test-connection.yaml
│   │   └── values.yaml
│   ├── index.js
│   ├── installer/
│   │   ├── main.go
│   │   └── syscheck.go
│   ├── openshift/
│   │   └── Dockerfile
│   ├── packer/
│   │   ├── digitalocean.json
│   │   └── scripts/
│   │       ├── 001-onboot.sh
│   │       ├── 010-docker.sh
│   │       ├── 011-ufw-docker.sh
│   │       ├── 020-force-ssh-logout.sh
│   │       ├── 099-one-click
│   │       ├── 900-cleanup.sh
│   │       └── 999-img-check.sh
│   ├── search-engines/
│   │   └── solr/
│   │       └── solrconfig.xml
│   ├── templates/
│   │   ├── legacy.pug
│   │   ├── master.pug
│   │   └── setup.pug
│   └── webpack/
│       ├── webpack.dev.js
│       └── webpack.prod.js
├── package.json
├── patches/
│   └── extract-files+9.0.0.patch
└── server/
    ├── app/
    │   ├── content/
    │   │   └── create.md
    │   ├── data.yml
    │   └── regex.js
    ├── controllers/
    │   ├── auth.js
    │   ├── common.js
    │   ├── ssl.js
    │   └── upload.js
    ├── core/
    │   ├── asar.js
    │   ├── auth.js
    │   ├── cache.js
    │   ├── config.js
    │   ├── db.js
    │   ├── extensions.js
    │   ├── kernel.js
    │   ├── letsencrypt.js
    │   ├── localization.js
    │   ├── logger.js
    │   ├── mail.js
    │   ├── scheduler.js
    │   ├── servers.js
    │   ├── sideloader.js
    │   ├── system.js
    │   ├── telemetry.js
    │   └── worker.js
    ├── db/
    │   ├── beta/
    │   │   ├── index.js
    │   │   ├── migrations/
    │   │   │   ├── 2.0.0-beta.1.js
    │   │   │   ├── 2.0.0-beta.11.js
    │   │   │   ├── 2.0.0-beta.127.js
    │   │   │   ├── 2.0.0-beta.148.js
    │   │   │   ├── 2.0.0-beta.205.js
    │   │   │   ├── 2.0.0-beta.217.js
    │   │   │   ├── 2.0.0-beta.242.js
    │   │   │   ├── 2.0.0-beta.293.js
    │   │   │   ├── 2.0.0-beta.38.js
    │   │   │   ├── 2.0.0-beta.99.js
    │   │   │   ├── 2.0.0-rc.2.js
    │   │   │   └── 2.0.0-rc.29.js
    │   │   └── migrations-sqlite/
    │   │       ├── 2.0.0-beta.1.js
    │   │       ├── 2.0.0-beta.11.js
    │   │       ├── 2.0.0-beta.127.js
    │   │       ├── 2.0.0-beta.205.js
    │   │       ├── 2.0.0-beta.217.js
    │   │       ├── 2.0.0-beta.242.js
    │   │       ├── 2.0.0-beta.293.js
    │   │       ├── 2.0.0-beta.38.js
    │   │       ├── 2.0.0-beta.99.js
    │   │       └── 2.0.0-rc.2.js
    │   ├── migrations/
    │   │   ├── 2.0.0.js
    │   │   ├── 2.1.85.js
    │   │   ├── 2.2.17.js
    │   │   ├── 2.2.3.js
    │   │   ├── 2.3.10.js
    │   │   ├── 2.3.23.js
    │   │   ├── 2.4.13.js
    │   │   ├── 2.4.14.js
    │   │   ├── 2.4.36.js
    │   │   ├── 2.4.61.js
    │   │   ├── 2.5.1.js
    │   │   ├── 2.5.108.js
    │   │   ├── 2.5.118.js
    │   │   ├── 2.5.12.js
    │   │   ├── 2.5.122.js
    │   │   └── 2.5.128.js
    │   ├── migrations-sqlite/
    │   │   ├── 2.0.0.js
    │   │   ├── 2.2.17.js
    │   │   ├── 2.2.3.js
    │   │   ├── 2.3.10.js
    │   │   ├── 2.3.14.js
    │   │   ├── 2.3.23.js
    │   │   ├── 2.4.13.js
    │   │   ├── 2.4.36.js
    │   │   ├── 2.4.61.js
    │   │   ├── 2.5.1.js
    │   │   ├── 2.5.108.js
    │   │   ├── 2.5.118.js
    │   │   ├── 2.5.12.js
    │   │   ├── 2.5.122.js
    │   │   └── 2.5.128.js
    │   └── migrator-source.js
    ├── graph/
    │   ├── directives/
    │   │   ├── auth.js
    │   │   └── rate-limit.js
    │   ├── index.js
    │   ├── resolvers/
    │   │   ├── analytics.js
    │   │   ├── asset.js
    │   │   ├── authentication.js
    │   │   ├── comment.js
    │   │   ├── contribute.js
    │   │   ├── folder.js
    │   │   ├── group.js
    │   │   ├── localization.js
    │   │   ├── logging.js
    │   │   ├── mail.js
    │   │   ├── navigation.js
    │   │   ├── page.js
    │   │   ├── rendering.js
    │   │   ├── search.js
    │   │   ├── site.js
    │   │   ├── storage.js
    │   │   ├── system.js
    │   │   ├── tag.js
    │   │   ├── theming.js
    │   │   └── user.js
    │   ├── scalars/
    │   │   └── date.js
    │   └── schemas/
    │       ├── analytics.graphql
    │       ├── asset.graphql
    │       ├── authentication.graphql
    │       ├── comment.graphql
    │       ├── common.graphql
    │       ├── contribute.graphql
    │       ├── group.graphql
    │       ├── localization.graphql
    │       ├── logging.graphql
    │       ├── mail.graphql
    │       ├── navigation.graphql
    │       ├── page.graphql
    │       ├── rendering.graphql
    │       ├── scalars.graphql
    │       ├── search.graphql
    │       ├── site.graphql
    │       ├── storage.graphql
    │       ├── system.graphql
    │       ├── theming.graphql
    │       └── user.graphql
    ├── helpers/
    │   ├── asset.js
    │   ├── brute-knex.js
    │   ├── common.js
    │   ├── config.js
    │   ├── error.js
    │   ├── graph.js
    │   ├── page.js
    │   └── security.js
    ├── index.js
    ├── jobs/
    │   ├── fetch-graph-locale.js
    │   ├── purge-uploads.js
    │   ├── rebuild-tree.js
    │   ├── render-page.js
    │   ├── sanitize-svg.js
    │   ├── sync-graph-locales.js
    │   ├── sync-graph-updates.js
    │   └── sync-storage.js
    ├── locales/
    │   └── README.md
    ├── master.js
    ├── middlewares/
    │   ├── security.js
    │   └── seo.js
    ├── models/
    │   ├── analytics.js
    │   ├── apiKeys.js
    │   ├── assetFolders.js
    │   ├── assets.js
    │   ├── authentication.js
    │   ├── commentProviders.js
    │   ├── comments.js
    │   ├── editors.js
    │   ├── groups.js
    │   ├── locales.js
    │   ├── loggers.js
    │   ├── navigation.js
    │   ├── pageHistory.js
    │   ├── pageLinks.js
    │   ├── pages.js
    │   ├── renderers.js
    │   ├── searchEngines.js
    │   ├── settings.js
    │   ├── storage.js
    │   ├── tags.js
    │   ├── userKeys.js
    │   └── users.js
    ├── modules/
    │   ├── analytics/
    │   │   ├── azureinsights/
    │   │   │   ├── code.yml
    │   │   │   └── definition.yml
    │   │   ├── baidutongji/
    │   │   │   ├── code.yml
    │   │   │   └── definition.yml
    │   │   ├── countly/
    │   │   │   ├── code.yml
    │   │   │   └── definition.yml
    │   │   ├── elasticapm/
    │   │   │   ├── code.yml
    │   │   │   └── definition.yml
    │   │   ├── fathom/
    │   │   │   ├── code.yml
    │   │   │   └── definition.yml
    │   │   ├── fullstory/
    │   │   │   ├── code.yml
    │   │   │   └── definition.yml
    │   │   ├── google/
    │   │   │   ├── code.yml
    │   │   │   └── definition.yml
    │   │   ├── gtm/
    │   │   │   ├── code.yml
    │   │   │   └── definition.yml
    │   │   ├── hotjar/
    │   │   │   ├── code.yml
    │   │   │   └── definition.yml
    │   │   ├── matomo/
    │   │   │   ├── code.yml
    │   │   │   └── definition.yml
    │   │   ├── newrelic/
    │   │   │   ├── code.yml
    │   │   │   └── definition.yml
    │   │   ├── plausible/
    │   │   │   ├── code.yml
    │   │   │   └── definition.yml
    │   │   ├── statcounter/
    │   │   │   ├── code.yml
    │   │   │   └── definition.yml
    │   │   ├── umami/
    │   │   │   ├── code.yml
    │   │   │   └── definition.yml
    │   │   ├── umami2/
    │   │   │   ├── code.yml
    │   │   │   └── definition.yml
    │   │   └── yandex/
    │   │       ├── code.yml
    │   │       └── definition.yml
    │   ├── authentication/
    │   │   ├── auth0/
    │   │   │   ├── authentication.js
    │   │   │   └── definition.yml
    │   │   ├── azure/
    │   │   │   ├── authentication.js
    │   │   │   └── definition.yml
    │   │   ├── cas/
    │   │   │   ├── authentication.js
    │   │   │   └── definition.yml
    │   │   ├── discord/
    │   │   │   ├── authentication.js
    │   │   │   └── definition.yml
    │   │   ├── dropbox/
    │   │   │   ├── authentication.js
    │   │   │   └── definition.yml
    │   │   ├── facebook/
    │   │   │   ├── authentication.js
    │   │   │   └── definition.yml
    │   │   ├── firebase/
    │   │   │   ├── authentication.js
    │   │   │   └── definition.yml
    │   │   ├── github/
    │   │   │   ├── authentication.js
    │   │   │   └── definition.yml
    │   │   ├── gitlab/
    │   │   │   ├── authentication.js
    │   │   │   └── definition.yml
    │   │   ├── google/
    │   │   │   ├── authentication.js
    │   │   │   └── definition.yml
    │   │   ├── keycloak/
    │   │   │   ├── authentication.js
    │   │   │   └── definition.yml
    │   │   ├── ldap/
    │   │   │   ├── authentication.js
    │   │   │   └── definition.yml
    │   │   ├── local/
    │   │   │   ├── authentication.js
    │   │   │   └── definition.yml
    │   │   ├── microsoft/
    │   │   │   ├── authentication.js
    │   │   │   └── definition.yml
    │   │   ├── oauth2/
    │   │   │   ├── authentication.js
    │   │   │   └── definition.yml
    │   │   ├── oidc/
    │   │   │   ├── authentication.js
    │   │   │   └── definition.yml
    │   │   ├── okta/
    │   │   │   ├── authentication.js
    │   │   │   └── definition.yml
    │   │   ├── rocketchat/
    │   │   │   ├── authentication.js
    │   │   │   └── definition.yml
    │   │   ├── saml/
    │   │   │   ├── authentication.js
    │   │   │   └── definition.yml
    │   │   ├── slack/
    │   │   │   ├── authentication.js
    │   │   │   └── definition.yml
    │   │   └── twitch/
    │   │       ├── authentication.js
    │   │       └── definition.yml
    │   ├── comments/
    │   │   ├── artalk/
    │   │   │   ├── code.yml
    │   │   │   └── definition.yml
    │   │   ├── commento/
    │   │   │   ├── code.yml
    │   │   │   └── definition.yml
    │   │   ├── default/
    │   │   │   ├── comment.js
    │   │   │   └── definition.yml
    │   │   └── disqus/
    │   │       ├── code.yml
    │   │       └── definition.yml
    │   ├── editor/
    │   │   ├── api/
    │   │   │   └── definition.yml
    │   │   ├── asciidoc/
    │   │   │   └── definition.yml
    │   │   ├── ckeditor/
    │   │   │   └── definition.yml
    │   │   ├── code/
    │   │   │   └── definition.yml
    │   │   ├── markdown/
    │   │   │   └── definition.yml
    │   │   ├── redirect/
    │   │   │   └── definition.yml
    │   │   └── wysiwyg/
    │   │       └── definition.yml
    │   ├── extensions/
    │   │   ├── git/
    │   │   │   └── ext.js
    │   │   ├── pandoc/
    │   │   │   └── ext.js
    │   │   ├── puppeteer/
    │   │   │   └── ext.js
    │   │   └── sharp/
    │   │       └── ext.js
    │   ├── logging/
    │   │   ├── airbrake/
    │   │   │   ├── definition.yml
    │   │   │   └── logger.js
    │   │   ├── bugsnag/
    │   │   │   ├── definition.yml
    │   │   │   └── logger.js
    │   │   ├── disk/
    │   │   │   ├── definition.yml
    │   │   │   └── logger.js
    │   │   ├── eventlog/
    │   │   │   ├── definition.yml
    │   │   │   └── logger.js
    │   │   ├── loggly/
    │   │   │   ├── definition.yml
    │   │   │   └── logger.js
    │   │   ├── logstash/
    │   │   │   ├── definition.yml
    │   │   │   └── logger.js
    │   │   ├── newrelic/
    │   │   │   ├── definition.yml
    │   │   │   └── logger.js
    │   │   ├── papertrail/
    │   │   │   ├── definition.yml
    │   │   │   └── logger.js
    │   │   ├── raygun/
    │   │   │   ├── definition.yml
    │   │   │   └── logger.js
    │   │   ├── rollbar/
    │   │   │   ├── definition.yml
    │   │   │   └── logger.js
    │   │   ├── sentry/
    │   │   │   ├── definition.yml
    │   │   │   └── logger.js
    │   │   └── syslog/
    │   │       ├── definition.yml
    │   │       └── logger.js
    │   ├── rendering/
    │   │   ├── asciidoc-core/
    │   │   │   ├── definition.yml
    │   │   │   └── renderer.js
    │   │   ├── html-asciinema/
    │   │   │   ├── definition.yml
    │   │   │   └── renderer.js
    │   │   ├── html-blockquotes/
    │   │   │   ├── definition.yml
    │   │   │   └── renderer.js
    │   │   ├── html-codehighlighter/
    │   │   │   ├── definition.yml
    │   │   │   └── renderer.js
    │   │   ├── html-core/
    │   │   │   ├── definition.yml
    │   │   │   └── renderer.js
    │   │   ├── html-diagram/
    │   │   │   ├── definition.yml
    │   │   │   └── renderer.js
    │   │   ├── html-image-prefetch/
    │   │   │   ├── definition.yml
    │   │   │   └── renderer.js
    │   │   ├── html-mediaplayers/
    │   │   │   ├── definition.yml
    │   │   │   └── renderer.js
    │   │   ├── html-mermaid/
    │   │   │   ├── definition.yml
    │   │   │   └── renderer.js
    │   │   ├── html-security/
    │   │   │   ├── definition.yml
    │   │   │   └── renderer.js
    │   │   ├── html-tabset/
    │   │   │   ├── definition.yml
    │   │   │   └── renderer.js
    │   │   ├── html-twemoji/
    │   │   │   ├── definition.yml
    │   │   │   └── renderer.js
    │   │   ├── markdown-abbr/
    │   │   │   ├── definition.yml
    │   │   │   └── renderer.js
    │   │   ├── markdown-core/
    │   │   │   ├── definition.yml
    │   │   │   ├── renderer.js
    │   │   │   └── underline.js
    │   │   ├── markdown-emoji/
    │   │   │   ├── definition.yml
    │   │   │   └── renderer.js
    │   │   ├── markdown-expandtabs/
    │   │   │   ├── definition.yml
    │   │   │   └── renderer.js
    │   │   ├── markdown-footnotes/
    │   │   │   ├── definition.yml
    │   │   │   └── renderer.js
    │   │   ├── markdown-imsize/
    │   │   │   ├── definition.yml
    │   │   │   └── renderer.js
    │   │   ├── markdown-katex/
    │   │   │   ├── definition.yml
    │   │   │   ├── mhchem.js
    │   │   │   └── renderer.js
    │   │   ├── markdown-kroki/
    │   │   │   ├── definition.yml
    │   │   │   └── renderer.js
    │   │   ├── markdown-mathjax/
    │   │   │   ├── definition.yml
    │   │   │   └── renderer.js
    │   │   ├── markdown-multi-table/
    │   │   │   ├── definition.yml
    │   │   │   └── renderer.js
    │   │   ├── markdown-pivot-table/
    │   │   │   ├── definition.yml
    │   │   │   └── renderer.js
    │   │   ├── markdown-plantuml/
    │   │   │   ├── definition.yml
    │   │   │   └── renderer.js
    │   │   ├── markdown-supsub/
    │   │   │   ├── definition.yml
    │   │   │   └── renderer.js
    │   │   ├── markdown-tasklists/
    │   │   │   ├── definition.yml
    │   │   │   └── renderer.js
    │   │   └── openapi-core/
    │   │       ├── definition.yml
    │   │       └── renderer.js
    │   ├── search/
    │   │   ├── algolia/
    │   │   │   ├── definition.yml
    │   │   │   └── engine.js
    │   │   ├── aws/
    │   │   │   ├── definition.yml
    │   │   │   └── engine.js
    │   │   ├── azure/
    │   │   │   ├── definition.yml
    │   │   │   └── engine.js
    │   │   ├── db/
    │   │   │   ├── definition.yml
    │   │   │   └── engine.js
    │   │   ├── elasticsearch/
    │   │   │   ├── definition.yml
    │   │   │   └── engine.js
    │   │   ├── manticore/
    │   │   │   ├── definition.yml
    │   │   │   └── engine.js
    │   │   ├── postgres/
    │   │   │   ├── definition.yml
    │   │   │   └── engine.js
    │   │   ├── solr/
    │   │   │   ├── definition.yml
    │   │   │   └── engine.js
    │   │   └── sphinx/
    │   │       ├── definition.yml
    │   │       └── engine.js
    │   └── storage/
    │       ├── azure/
    │       │   ├── definition.yml
    │       │   └── storage.js
    │       ├── box/
    │       │   ├── definition.yml
    │       │   └── storage.js
    │       ├── digitalocean/
    │       │   ├── definition.yml
    │       │   └── storage.js
    │       ├── disk/
    │       │   ├── common.js
    │       │   ├── definition.yml
    │       │   └── storage.js
    │       ├── dropbox/
    │       │   ├── definition.yml
    │       │   └── storage.js
    │       ├── gdrive/
    │       │   ├── definition.yml
    │       │   └── storage.js
    │       ├── git/
    │       │   ├── definition.yml
    │       │   └── storage.js
    │       ├── onedrive/
    │       │   ├── definition.yml
    │       │   └── storage.js
    │       ├── s3/
    │       │   ├── common.js
    │       │   ├── definition.yml
    │       │   └── storage.js
    │       ├── s3generic/
    │       │   ├── definition.yml
    │       │   └── storage.js
    │       └── sftp/
    │           ├── definition.yml
    │           └── storage.js
    ├── setup.js
    ├── templates/
    │   ├── account-reset-pwd.html
    │   ├── account-verify.html
    │   └── test.html
    ├── test/
    │   └── helpers/
    │       └── page.test.js
    ├── themes/
    │   └── default/
    │       └── theme.yml
    └── views/
        ├── admin.pug
        ├── editor.pug
        ├── error.pug
        ├── history.pug
        ├── legacy/
        │   ├── login.pug
        │   └── page.pug
        ├── login.pug
        ├── new.pug
        ├── notfound.pug
        ├── page.pug
        ├── profile.pug
        ├── register.pug
        ├── source.pug
        ├── tags.pug
        ├── unauthorized.pug
        └── welcome.pug

================================================
FILE CONTENTS
================================================

================================================
FILE: .babelrc
================================================
{
  "comments": true,
  "plugins": [
    "lodash",
    "graphql-tag",
    "@babel/plugin-syntax-dynamic-import",
    "@babel/plugin-syntax-import-meta",
    "@babel/plugin-proposal-class-properties",
    "@babel/plugin-proposal-json-strings",
    [
      "@babel/plugin-proposal-decorators",
      {
        "legacy": true
      }
    ],
    "@babel/plugin-proposal-function-sent",
    "@babel/plugin-proposal-export-namespace-from",
    "@babel/plugin-proposal-numeric-separator",
    "@babel/plugin-proposal-throw-expressions",
    [
      "prismjs", {
        "languages": ["clike", "markup"],
        "plugins": ["line-numbers", "autoloader", "normalize-whitespace", "copy-to-clipboard", "toolbar"],
        "theme": "twilight",
        "css": true
      }
    ]
  ],
  "presets": [
    [
      "@babel/preset-env", {
        "useBuiltIns": "entry",
        "corejs": 3,
        "debug": false
      }
    ]
  ]
}


================================================
FILE: .devcontainer/devcontainer.json
================================================
// How to get remote container development working with VSCode:
// 1. Install "Remote Development" extension pack (ms-vscode-remote.vscode-remote-extensionpack)
// 2. Select "Remote Containers - Reopen in container"

{
  "name": "Wiki.js",
  "dockerComposeFile": [
    "../dev/containers/docker-compose.yml"
  ],
  "forwardPorts": [3000, 3001],
  "service": "wiki",
  "workspaceFolder": "/wiki",
  "settings": {
    "terminal.integrated.shell.linux": "/bin/bash"
  },
  "extensions": [
	"EditorConfig.editorconfig",
	"dbaeumer.vscode-eslint",
	"christian-kohler.path-intellisense",
	"mrmlnc.vscode-puglint",
	"octref.vetur",
	"dzannotti.vscode-babel-coloring",
	"wayou.vscode-todo-highlight",
	"visualstudioexptteam.vscodeintellicode",
	"lukas-tr.materialdesignicons-intellisense",
	"codezombiech.gitignore",
	"kumar-harsh.graphql-for-vscode",
	"mrmlnc.vscode-duplicate",
	"oderwat.indent-rainbow",
	"christian-kohler.npm-intellisense"
],
  "postCreateCommand": ["yarn", "install"]
}


================================================
FILE: .editorconfig
================================================
root = true

[*]
indent_style = space
indent_size = 2
charset = utf-8
trim_trailing_whitespace = true
end_of_line = lf
insert_final_newline = true

[*.{jade,pug,md}]
trim_trailing_whitespace = false

[Makefile]
indent_style = tab
indent_size = 4


================================================
FILE: .eslintignore
================================================
**/node_modules/**
**/*.min.js
assets/**
client/libs/**
coverage/**
repo/**
data/**
logs/**


================================================
FILE: .eslintrc.yml
================================================
extends:
  - requarks
  - plugin:vue/strongly-recommended
  - plugin:cypress/recommended
env:
  node: true
  jest: true
parserOptions:
  parser: babel-eslint
  ecmaVersion: 2017
  allowImportExportEverywhere: true
globals:
  document: false
  navigator: false
  window: false


================================================
FILE: .gitattributes
================================================
# Common settings that generally should always be used with your language specific settings

# Auto detect text files and perform LF normalization
# https://www.davidlaing.com/2012/09/19/customise-your-gitattributes-to-become-a-git-ninja/
*          text=auto

#
# The above will handle all files NOT found below
#

# Documents
*.bibtex   text diff=bibtex
*.doc	        diff=astextplain
*.DOC	        diff=astextplain
*.docx          diff=astextplain
*.DOCX          diff=astextplain
*.dot           diff=astextplain
*.DOT           diff=astextplain
*.pdf           diff=astextplain
*.PDF           diff=astextplain
*.rtf           diff=astextplain
*.RTF	        diff=astextplain
*.md       text
*.tex      text diff=tex
*.adoc     text
*.textile  text
*.mustache text
*.csv      text
*.tab      text
*.tsv      text
*.txt      text
*.sql      text

# Graphics
*.png      binary
*.jpg      binary
*.jpeg     binary
*.gif      binary
*.tif      binary
*.tiff     binary
*.ico      binary
# SVG treated as an asset (binary) by default.
*.svg      text
# If you want to treat it as binary,
# use the following line instead.
# *.svg    binary
*.eps      binary

# Scripts
*.bash     text eol=lf
*.sh       text eol=lf
# These are explicitly windows files and should use crlf
*.bat      text eol=crlf
*.cmd      text eol=crlf
*.ps1      text eol=crlf

# Serialisation
*.json     text
*.toml     text
*.xml      text
*.yaml     text
*.yml      text

# Archives
*.7z       binary
*.gz       binary
*.tar      binary
*.zip      binary

#
# Exclude files from exporting
#

.gitattributes export-ignore
.gitignore     export-ignore

# Auto detect text files and perform LF normalization
# https://www.davidlaing.com/2012/09/19/customise-your-gitattributes-to-become-a-git-ninja/
*    text=auto

*.cs text diff=csharp

# Treat all Go files in this repo as binary, with no git magic updating
# line endings. Windows users contributing to Go will need to use a
# modern version of git and editors capable of LF line endings.

*.go -text diff=golang

## GITATTRIBUTES FOR WEB PROJECTS
#
# These settings are for any web project.
#
# Details per file setting:
#   text    These files should be normalized (i.e. convert CRLF to LF).
#   binary  These files are binary and should be left untouched.
#
# Note that binary is a macro for -text -diff.
######################################################################

# Auto detect
##   Handle line endings automatically for files detected as
##   text and leave all files detected as binary untouched.
##   This will handle all files NOT defined below.
*                 text=auto

# Source code
*.bash            text eol=lf
*.bat             text eol=crlf
*.cmd             text eol=crlf
*.coffee          text
*.css             text
*.htm             text diff=html
*.html            text diff=html
*.inc             text
*.ini             text
*.js              text
*.json            text
*.jsx             text
*.less            text
*.ls              text
*.map             text -diff
*.od              text
*.onlydata        text
*.php             text diff=php
*.pl              text
*.ps1             text eol=crlf
*.py              text diff=python
*.rb              text diff=ruby
*.sass            text
*.scm             text
*.scss            text diff=css
*.sh              text eol=lf
*.sql             text
*.styl            text
*.tag             text
*.ts              text
*.tsx             text
*.xml             text
*.xhtml           text diff=html

# Docker
*.dockerignore    text
Dockerfile        text

# Documentation
*.ipynb           text
*.markdown        text
*.md              text
*.mdwn            text
*.mdown           text
*.mkd             text
*.mkdn            text
*.mdtxt           text
*.mdtext          text
*.txt             text
AUTHORS           text
CHANGELOG         text
CHANGES           text
CONTRIBUTING      text
COPYING           text
copyright         text
*COPYRIGHT*       text
INSTALL           text
license           text
LICENSE           text
NEWS              text
readme            text
*README*          text
TODO              text

# Templates
*.dot             text
*.ejs             text
*.haml            text
*.handlebars      text
*.hbs             text
*.hbt             text
*.jade            text
*.latte           text
*.mustache        text
*.njk             text
*.phtml           text
*.tmpl            text
*.tpl             text
*.twig            text
*.vue             text

# Linters
.csslintrc        text
.eslintrc         text
.htmlhintrc       text
.jscsrc           text
.jshintrc         text
.jshintignore     text
.stylelintrc      text

# Configs
*.bowerrc         text
*.cnf             text
*.conf            text
*.config          text
.babelrc          text
.browserslistrc   text
.editorconfig     text
.env              text
.gitattributes    text
.gitconfig        text
.htaccess         text
*.lock            text -diff
package-lock.json text -diff
*.npmignore       text
*.yaml            text
*.yml             text
browserslist      text
Makefile          text
makefile          text

# Heroku
Procfile          text
.slugignore       text

# Graphics
*.ai              binary
*.bmp             binary
*.eps             binary
*.gif             binary
*.gifv            binary
*.ico             binary
*.jng             binary
*.jp2             binary
*.jpg             binary
*.jpeg            binary
*.jpx             binary
*.jxr             binary
*.pdf             binary
*.png             binary
*.psb             binary
*.psd             binary
# SVG treated as an asset (binary) by default.
*.svg             text
# If you want to treat it as binary,
# use the following line instead.
# *.svg           binary
*.svgz            binary
*.tif             binary
*.tiff            binary
*.wbmp            binary
*.webp            binary

# Audio
*.kar             binary
*.m4a             binary
*.mid             binary
*.midi            binary
*.mp3             binary
*.ogg             binary
*.ra              binary

# Video
*.3gpp            binary
*.3gp             binary
*.as              binary
*.asf             binary
*.asx             binary
*.fla             binary
*.flv             binary
*.m4v             binary
*.mng             binary
*.mov             binary
*.mp4             binary
*.mpeg            binary
*.mpg             binary
*.ogv             binary
*.swc             binary
*.swf             binary
*.webm            binary

# Archives
*.7z              binary
*.gz              binary
*.jar             binary
*.rar             binary
*.tar             binary
*.zip             binary

# Fonts
*.ttf             binary
*.eot             binary
*.otf             binary
*.woff            binary
*.woff2           binary

# Executables
*.exe             binary
*.pyc             binary


================================================
FILE: .github/CODE_OF_CONDUCT.md
================================================
# Contributor Covenant Code of Conduct

## Our Pledge

In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.

## Our Standards

Examples of behavior that contributes to creating a positive environment include:

* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members

Examples of unacceptable behavior by participants include:

* The use of sexualized language or imagery and unwelcome sexual attention or advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a professional setting

## Our Responsibilities

Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.

Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.

## Scope

This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.

## Enforcement

Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at abuse@requarks.io. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.

Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.

## Attribution

This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]

[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/


================================================
FILE: .github/CONTRIBUTING.md
================================================
# Contribute

## Introduction

First, thank you for considering contributing to Wiki.js! It's people like you that make the open source community such a great community! 😊

We welcome any type of contribution, not only code. You can help with
- **QA**: file bug reports, the more details you can give the better (e.g. screenshots with the console open)
- **Marketing**: writing blog posts, howto's, printing stickers, ...
- **Community**: presenting the project at meetups, organizing a dedicated meetup for the local community, ...
- **Code**: take a look at the [open issues](https://github.com/Requarks/wiki/issues). Even if you can't write code, commenting on them, showing that you care about a given issue matters. It helps us triage them.
- **Money**: we welcome financial contributions in full transparency on our [open collective](https://opencollective.com/wikijs).

## Your First Contribution

Working on your first Pull Request? You can learn how from this *free* course, [How to Contribute to an Open Source Project on GitHub](https://egghead.io/courses/how-to-contribute-to-an-open-source-project-on-github).

## Submitting code

Any code change should be submitted as a pull request. The description should explain what the code does and give steps to execute it. The pull request should also contain tests.

## Code review process

The bigger the pull request, the longer it will take to review and merge. Try to break down large pull requests in smaller chunks that are easier to review and merge.
It is also always helpful to have some context for your pull request. What was the purpose? Why does it matter to you?

## Requesting new features / enhancements

Use the feature request board to submit new ideas and vote on which ideas should be integrated first.

:triangular_flag_on_post: [https://js.wiki/feedback/](https://js.wiki/feedback/)

*Do not use GitHub issues to submit new feature ideas, as it will closed and you'll be asked to use the feature request board above. GitHub Issues are limited to bugs / issues / help*.

## Financial contributions

We also welcome financial contributions in full transparency on our [open collective](https://opencollective.com/wikijs).
Anyone can file an expense. If the expense makes sense for the development of the community, it will be "merged" in the ledger of our open collective by the core contributors and the person who filed the expense will be reimbursed.

## Questions

If you have any questions, create an [issue](https://github.com/Requarks/wiki/issues/new/choose) (protip: do a quick search first to see if someone else didn't ask the same question before!).
You can also reach us at <hello@wikijs.opencollective.com>.

## Credits

### Contributors

Thank you to all the people who have already contributed to Wiki.js!
<a href="https://github.com/Requarks/wiki/graphs/contributors"><img src="https://opencollective.com/wikijs/contributors.svg?width=890" /></a>


### Backers

Thank you to all our backers! [[Become a backer](https://opencollective.com/wikijs#backer)]

<a href="https://opencollective.com/wikijs#backers" target="_blank"><img src="https://opencollective.com/wikijs/backers.svg?width=890"></a>


### Sponsors

Thank you to all our sponsors! (please ask your company to also support this open source project by [becoming a sponsor](https://opencollective.com/wikijs#sponsor))

<a href="https://opencollective.com/wikijs/sponsor/0/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/0/avatar.svg"></a>
<a href="https://opencollective.com/wikijs/sponsor/1/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/1/avatar.svg"></a>
<a href="https://opencollective.com/wikijs/sponsor/2/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/2/avatar.svg"></a>
<a href="https://opencollective.com/wikijs/sponsor/3/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/3/avatar.svg"></a>
<a href="https://opencollective.com/wikijs/sponsor/4/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/4/avatar.svg"></a>
<a href="https://opencollective.com/wikijs/sponsor/5/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/5/avatar.svg"></a>
<a href="https://opencollective.com/wikijs/sponsor/6/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/6/avatar.svg"></a>
<a href="https://opencollective.com/wikijs/sponsor/7/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/7/avatar.svg"></a>
<a href="https://opencollective.com/wikijs/sponsor/8/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/8/avatar.svg"></a>
<a href="https://opencollective.com/wikijs/sponsor/9/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/9/avatar.svg"></a>

<!-- This `CONTRIBUTING.md` is based on @nayafia's template https://github.com/nayafia/contributing-template -->


================================================
FILE: .github/FUNDING.yml
================================================
# These are supported funding model platforms

github: [NGPixel]
patreon: requarks
open_collective: wikijs
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
custom: # Replace with a single custom sponsorship URL


================================================
FILE: .github/ISSUE_TEMPLATE/config.yml
================================================
blank_issues_enabled: false
contact_links:
  - name: Help / Questions
    url: https://github.com/Requarks/wiki/discussions/categories/help-questions
    about: Ask the community for help on using or setting up Wiki.js
  - name: Errors / Bug Reports
    url: https://github.com/Requarks/wiki/discussions/categories/error-bug-report
    about: Create a discussion around the bug / error you're getting. If validated, a proper GitHub issue will be created so that it can be worked on.
  - name: Request a new feature / improvement
    url: https://feedback.js.wiki/wiki
    about: Submit ideas for new features or improvements.


================================================
FILE: .github/PULL_REQUEST_TEMPLATE.md
================================================


================================================
FILE: .github/auto_assign.yml
================================================
# Set to true to add reviewers to pull requests
addReviewers: true

# Set to true to add assignees to pull requests
addAssignees: true

# A list of reviewers to be added to pull requests (GitHub user name)
reviewers: 
  - NGPixel

# A list of keywords to be skipped the process that add reviewers if pull requests include it 
skipKeywords:
  - wip

# A number of reviewers added to the pull request
# Set 0 to add all the reviewers (default: 0)
numberOfReviewers: 0


================================================
FILE: .github/issuecomplete.yml
================================================
# The name of the label to apply when an issue does not have all tasks checked
labelName: invalid

# The color of the label in hex format (without #)
labelColor:

# The text of the comment to add to the issue in addition to the label
commentText: >
  You haven't provided the required info about your host! (OS, Wiki.js version, Database engine)

# Whether or not to ensure all checkboxes are checked
checkCheckboxes: false

# Keywords to look for in the body of the issue
keywords:
  - Wiki.js version
  - Database engine


================================================
FILE: .github/workflows/build.yml
================================================
name: Build + Publish

on:
  push:
    branches:
      - main
    tags:
      - 'v*'

env:
  BASE_DEV_VERSION: 2.5.0

jobs:

  build:
    name: Build
    runs-on: ubuntu-latest
    permissions:
      packages: write

    steps:
    - uses: actions/checkout@v4

    - name: Set Build Variables
      run: |
        if [[ "$GITHUB_REF" =~ ^refs/tags/v* ]]; then
          echo "Using TAG mode: $GITHUB_REF_NAME"
          echo "REL_VERSION=$GITHUB_REF_NAME" >> $GITHUB_ENV
          echo "REL_VERSION_STRICT=${GITHUB_REF_NAME#?}" >> $GITHUB_ENV
        else
          echo "Using BRANCH mode: v$BASE_DEV_VERSION-dev.$GITHUB_RUN_NUMBER"
          echo "REL_VERSION=v$BASE_DEV_VERSION-dev.$GITHUB_RUN_NUMBER" >> $GITHUB_ENV
          echo "REL_VERSION_STRICT=$BASE_DEV_VERSION-dev.$GITHUB_RUN_NUMBER" >> $GITHUB_ENV
        fi

    - name: Disable DEV Flag + Set Version
      run: |
        sudo apt-get install jq -y
        mv package.json pkg-temp.json
        jq --arg vs "$REL_VERSION_STRICT" -r '. + {dev:false, version:$vs}' pkg-temp.json > package.json
        rm pkg-temp.json
        cat package.json

    - name: Login to DockerHub
      uses: docker/login-action@v3
      with:
        username: ${{ secrets.DOCKERHUB_USERNAME }}
        password: ${{ secrets.DOCKERHUB_TOKEN }}

    - name: Login to GitHub Container Registry
      uses: docker/login-action@v3
      with:
        registry: ghcr.io
        username: ${{ github.repository_owner }}
        password: ${{ secrets.GITHUB_TOKEN }}

    - name: Build and push Docker images
      uses: docker/build-push-action@v5
      with:
        context: .
        file: dev/build/Dockerfile
        push: true
        tags: |
          requarks/wiki:canary
          requarks/wiki:canary-${{ env.REL_VERSION_STRICT }}
          ghcr.io/requarks/wiki:canary
          ghcr.io/requarks/wiki:canary-${{ env.REL_VERSION_STRICT }}

    - name: Extract compiled files
      run: |
        mkdir -p _dist
        docker create --name wiki ghcr.io/requarks/wiki:canary-$REL_VERSION_STRICT
        docker cp wiki:/wiki _dist
        docker rm wiki
        rm _dist/wiki/config.yml
        cp ./config.sample.yml _dist/wiki/config.sample.yml
        find _dist/wiki/ -printf "%P\n" | tar -czf wiki-js.tar.gz --no-recursion -C _dist/wiki/ -T -

    - name: Upload a Build Artifact
      uses: actions/upload-artifact@v4
      with:
        name: drop
        path: wiki-js.tar.gz

  cypress:
    name: Run Cypress Tests
    runs-on: ubuntu-latest
    needs: [build]

    strategy:
      matrix:
        dbtype: [postgres, mysql, mariadb, sqlite]

    steps:
    - uses: actions/checkout@v4

    - name: Set Test Variables
      run: |
        if [[ "$GITHUB_REF" =~ ^refs/tags/v* ]]; then
          echo "Using TAG mode: $GITHUB_REF_NAME"
          echo "REL_VERSION_STRICT=${GITHUB_REF_NAME#?}" >> $GITHUB_ENV
        else
          echo "Using BRANCH mode: v$BASE_DEV_VERSION-dev.$GITHUB_RUN_NUMBER"
          echo "REL_VERSION_STRICT=$BASE_DEV_VERSION-dev.$GITHUB_RUN_NUMBER" >> $GITHUB_ENV
        fi

    - name: Run Tests
      env:
        MATRIXENV: ${{ matrix.dbtype }}
        CYPRESS_KEY: ${{ secrets.CYPRESS_KEY }}
      run: |
        chmod u+x dev/cypress/ci-setup.sh
        dev/cypress/ci-setup.sh
        docker run --name cypress --ipc=host --shm-size 1G -v $GITHUB_WORKSPACE:/e2e -w /e2e cypress/included:4.9.0 --record --key "$CYPRESS_KEY" --headless --group "$MATRIXENV" --ci-build-id "$REL_VERSION_STRICT-run$GITHUB_RUN_NUMBER.$GITHUB_RUN_ATTEMPT" --tag "$REL_VERSION_STRICT" --config baseUrl=http://172.17.0.1:3000

  arm:
    name: ARM Build
    runs-on: ubuntu-latest
    needs: [cypress]
    permissions:
      packages: write

    strategy:
      matrix:
        include:
          - platform: linux/arm64
            docker: arm64
          # - platform: linux/arm/v7
          #   docker: armv7

    steps:
    - uses: actions/checkout@v4

    - name: Set Version Variables
      run: |
        if [[ "$GITHUB_REF" =~ ^refs/tags/v* ]]; then
          echo "Using TAG mode: $GITHUB_REF_NAME"
          echo "REL_VERSION_STRICT=${GITHUB_REF_NAME#?}" >> $GITHUB_ENV
        else
          echo "Using BRANCH mode: v$BASE_DEV_VERSION-dev.$GITHUB_RUN_NUMBER"
          echo "REL_VERSION_STRICT=$BASE_DEV_VERSION-dev.$GITHUB_RUN_NUMBER" >> $GITHUB_ENV
        fi

    - name: Set up QEMU
      uses: docker/setup-qemu-action@v3

    - name: Set up Docker Buildx
      uses: docker/setup-buildx-action@v3

    - name: Login to DockerHub
      uses: docker/login-action@v3
      with:
        username: ${{ secrets.DOCKERHUB_USERNAME }}
        password: ${{ secrets.DOCKERHUB_TOKEN }}

    - name: Login to GitHub Container Registry
      uses: docker/login-action@v3
      with:
        registry: ghcr.io
        username: ${{ github.repository_owner }}
        password: ${{ secrets.GITHUB_TOKEN }}

    - name: Download a Build Artifact
      uses: actions/download-artifact@v4
      with:
        name: drop
        path: drop

    - name: Extract Build
      run: |
        mkdir -p build
        tar -xzf $GITHUB_WORKSPACE/drop/wiki-js.tar.gz -C $GITHUB_WORKSPACE/build --exclude=node_modules

    - name: Build and push Docker images
      uses: docker/build-push-action@v5
      with:
        context: .
        file: dev/build-arm/Dockerfile
        platforms: ${{ matrix.platform }}
        provenance: false
        push: true
        tags: |
          requarks/wiki:canary-${{ matrix.docker }}-${{ env.REL_VERSION_STRICT }}
          ghcr.io/requarks/wiki:canary-${{ matrix.docker }}-${{ env.REL_VERSION_STRICT }}

  windows:
    name: Windows Build
    runs-on: windows-latest
    needs: [cypress]

    steps:
    - name: Setup Node.js environment
      uses: actions/setup-node@v4
      with:
        node-version: 20.x

    - name: Download a Build Artifact
      uses: actions/download-artifact@v4
      with:
        name: drop
        path: drop

    - name: Extract Build
      run: |
        mkdir -p win
        tar -xzf $env:GITHUB_WORKSPACE\drop\wiki-js.tar.gz -C $env:GITHUB_WORKSPACE\win
        Copy-Item win\node_modules\extract-files\package.json patch-extractfile.json -Force
        Remove-Item -Path win\node_modules -Force -Recurse

    - name: Install Dependencies
      run: |
        yarn --production --frozen-lockfile --non-interactive
        yarn patch-package
      working-directory: win

    - name: Fix patched packages
      run: |
        Copy-Item patch-extractfile.json win\node_modules\extract-files\package.json -Force

    - name: Create Bundle
      run: tar -czf wiki-js-windows.tar.gz -C $env:GITHUB_WORKSPACE\win .

    - name: Upload a Build Artifact
      uses: actions/upload-artifact@v4
      with:
        name: drop-win
        path: wiki-js-windows.tar.gz

  beta:
    name: Publish Beta Images
    runs-on: ubuntu-latest
    if: startsWith(github.ref, 'refs/tags/v')
    needs: [build, arm, windows]
    permissions:
      packages: write

    steps:
    - name: Set Version Variables
      run: |
        echo "Using TAG mode: $GITHUB_REF_NAME"
        echo "REL_VERSION_STRICT=${GITHUB_REF_NAME#?}" >> $GITHUB_ENV

    - name: Login to DockerHub
      uses: docker/login-action@v3
      with:
        username: ${{ secrets.DOCKERHUB_USERNAME }}
        password: ${{ secrets.DOCKERHUB_TOKEN }}

    - name: Login to GitHub Container Registry
      uses: docker/login-action@v3
      with:
        registry: ghcr.io
        username: ${{ github.repository_owner }}
        password: ${{ secrets.GITHUB_TOKEN }}

    - name: Create and Push Manifests
      run: |
        echo "Creating the manifests..."

        docker manifest create requarks/wiki:beta-$REL_VERSION_STRICT requarks/wiki:canary-$REL_VERSION_STRICT requarks/wiki:canary-arm64-$REL_VERSION_STRICT
        docker manifest create ghcr.io/requarks/wiki:beta-$REL_VERSION_STRICT ghcr.io/requarks/wiki:canary-$REL_VERSION_STRICT ghcr.io/requarks/wiki:canary-arm64-$REL_VERSION_STRICT

        echo "Pushing the manifests..."

        docker manifest push -p requarks/wiki:beta-$REL_VERSION_STRICT
        docker manifest push -p ghcr.io/requarks/wiki:beta-$REL_VERSION_STRICT

  release:
    name: Publish Release Images
    runs-on: ubuntu-latest
    if: startsWith(github.ref, 'refs/tags/v')
    environment: prod
    needs: [beta]
    permissions:
      packages: write
      contents: write

    steps:
    - name: Set Version Variables
      run: |
        echo "Using TAG mode: $GITHUB_REF_NAME"
        echo "REL_VERSION_STRICT=${GITHUB_REF_NAME#?}" >> $GITHUB_ENV

    - name: Login to DockerHub
      uses: docker/login-action@v3
      with:
        username: ${{ secrets.DOCKERHUB_USERNAME }}
        password: ${{ secrets.DOCKERHUB_TOKEN }}

    - name: Login to GitHub Container Registry
      uses: docker/login-action@v3
      with:
        registry: ghcr.io
        username: ${{ github.repository_owner }}
        password: ${{ secrets.GITHUB_TOKEN }}

    - name: Create and Push Manifests
      run: |
        echo "Fetching semver tool..."
        curl -LJO https://static.requarks.io/semver
        chmod +x semver

        MAJOR=`./semver get major $REL_VERSION_STRICT`
        MINOR=`./semver get minor $REL_VERSION_STRICT`
        MAJORMINOR="$MAJOR.$MINOR"

        echo "Using major $MAJOR and minor $MINOR..."
        echo "Creating the manifests..."

        docker manifest create requarks/wiki:$REL_VERSION_STRICT requarks/wiki:canary-$REL_VERSION_STRICT requarks/wiki:canary-arm64-$REL_VERSION_STRICT
        docker manifest create requarks/wiki:$MAJOR requarks/wiki:canary-$REL_VERSION_STRICT requarks/wiki:canary-arm64-$REL_VERSION_STRICT
        docker manifest create requarks/wiki:$MAJORMINOR requarks/wiki:canary-$REL_VERSION_STRICT requarks/wiki:canary-arm64-$REL_VERSION_STRICT
        docker manifest create requarks/wiki:latest requarks/wiki:canary-$REL_VERSION_STRICT requarks/wiki:canary-arm64-$REL_VERSION_STRICT
        docker manifest create ghcr.io/requarks/wiki:$REL_VERSION_STRICT ghcr.io/requarks/wiki:canary-$REL_VERSION_STRICT ghcr.io/requarks/wiki:canary-arm64-$REL_VERSION_STRICT
        docker manifest create ghcr.io/requarks/wiki:$MAJOR ghcr.io/requarks/wiki:canary-$REL_VERSION_STRICT ghcr.io/requarks/wiki:canary-arm64-$REL_VERSION_STRICT
        docker manifest create ghcr.io/requarks/wiki:$MAJORMINOR ghcr.io/requarks/wiki:canary-$REL_VERSION_STRICT ghcr.io/requarks/wiki:canary-arm64-$REL_VERSION_STRICT
        docker manifest create ghcr.io/requarks/wiki:latest ghcr.io/requarks/wiki:canary-$REL_VERSION_STRICT ghcr.io/requarks/wiki:canary-arm64-$REL_VERSION_STRICT

        echo "Pushing the manifests..."

        docker manifest push -p requarks/wiki:$REL_VERSION_STRICT
        docker manifest push -p requarks/wiki:$MAJOR
        docker manifest push -p requarks/wiki:$MAJORMINOR
        docker manifest push -p requarks/wiki:latest
        docker manifest push -p ghcr.io/requarks/wiki:$REL_VERSION_STRICT
        docker manifest push -p ghcr.io/requarks/wiki:$MAJOR
        docker manifest push -p ghcr.io/requarks/wiki:$MAJORMINOR
        docker manifest push -p ghcr.io/requarks/wiki:latest

    - name: Download Linux Build
      uses: actions/download-artifact@v4
      with:
        name: drop
        path: drop

    - name: Download Windows Build
      uses: actions/download-artifact@v4
      with:
        name: drop-win
        path: drop-win

    - name: Generate Changelog
      id: changelog
      uses: Requarks/changelog-action@v1
      with:
        token: ${{ github.token }}
        tag: ${{ github.ref_name }}
        writeToFile: false

    - name: Update GitHub Release
      uses: ncipollo/release-action@v1.12.0
      with:
        allowUpdates: true
        draft: false
        makeLatest: true
        name: ${{ github.ref_name }}
        body: ${{ steps.changelog.outputs.changes }}
        token: ${{ github.token }}
        artifacts: 'drop/wiki-js.tar.gz,drop-win/wiki-js-windows.tar.gz'

    # - name: Notify Slack Releases Channel
    #   uses: slackapi/slack-github-action@v1.26.0
    #   with:
    #     payload: |
    #       {
    #         "text": "Wiki.js ${{ github.ref_name }} has been released."
    #       }
    #   env:
    #     SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
    #     SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK

    - name: Notify Telegram Channel
      uses: appleboy/telegram-action@v0.1.1
      with:
        to: ${{ secrets.TELEGRAM_TO }}
        token: ${{ secrets.TELEGRAM_TOKEN }}
        format: markdown
        disable_web_page_preview: true
        message: |
          Wiki.js *${{ github.ref_name }}* has been released!
          See [release notes](https://github.com/requarks/wiki/releases) for details.

    - name: Notify Discord Channel
      uses: sebastianpopp/discord-action@v2.0
      with:
        webhook: ${{ secrets.DISCORD_WEBHOOK }}
        message: Wiki.js ${{ github.ref_name }} has been released! See https://github.com/requarks/wiki/releases for details.

  # build-do-image:
  #   name: Build DigitalOcean Image
  #   runs-on: ubuntu-latest
  #   needs: [release]

  #   steps:
  #   - uses: actions/checkout@v4

  #   - name: Set Version Variables
  #     run: |
  #       echo "Using TAG mode: $GITHUB_REF_NAME"
  #       echo "REL_VERSION_STRICT=${GITHUB_REF_NAME#?}" >> $GITHUB_ENV

  #   - name: Install Packer
  #     run: |
  #       curl -fsSL https://apt.releases.hashicorp.com/gpg | sudo apt-key add -
  #       sudo apt-add-repository "deb [arch=amd64] https://apt.releases.hashicorp.com $(lsb_release -cs) main"
  #       sudo apt-get update && sudo apt-get install packer

  #   - name: Build Droplet Image
  #     env:
  #       DIGITALOCEAN_API_TOKEN: ${{ secrets.DO_TOKEN }}
  #       WIKI_APP_VERSION: ${{ env.REL_VERSION_STRICT }}
  #     working-directory: dev/packer
  #     run: |
  #       packer build digitalocean.json


================================================
FILE: .github/workflows/helm.yml
================================================
name: Helm Chart CI

on:
  # Triggers the workflow on push or pull request events but only for the dev branch
  push:
    branches: [ main ]
    paths: [ dev/helm/** ]

  # Allows you to run this workflow manually from the Actions tab
  workflow_dispatch:

jobs:
  build:
    name: Update Chart
    runs-on: ubuntu-latest

    steps:
      # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
      - uses: actions/checkout@v6
      
      - name: Package and Push Chart
        run: |
          export CHARTVER=$(yq '.version' dev/helm/Chart.yaml)
          helm plugin install https://github.com/chartmuseum/helm-push.git
          helm repo add chartmuseum https://charts.js.wiki
          helm cm-push --version="$CHARTVER" --username="${{secrets.HELM_REPO_USERNAME}}" --password="${{secrets.HELM_REPO_PASSWORD}}" dev/helm/ chartmuseum
          helm repo remove chartmuseum


================================================
FILE: .github/workflows/packer.yml
================================================
name: Build DigitalOcean Image

on:
  workflow_dispatch:
    inputs:
      version:
        description: 'App Version'
        required: true
        type: string

jobs:
  build-do-image:
    name: Build DigitalOcean Image
    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v6

    - name: Install Packer
      run: |
        wget -O - https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
        echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(grep -oP '(?<=UBUNTU_CODENAME=).*' /etc/os-release || lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list
        sudo apt update && sudo apt install packer

    - name: Build Droplet Image
      env:
        DIGITALOCEAN_API_TOKEN: ${{ secrets.DO_TOKEN }}
        WIKI_APP_VERSION: ${{ github.event.inputs.version }}
      working-directory: dev/packer
      run: |
        packer plugins install github.com/digitalocean/digitalocean
        packer build digitalocean.json


================================================
FILE: .gitignore
================================================
# Logs
logs
*.log
/logs

# Deployment builds
dist

# Dependency directories
node_modules
npm/node_modules

# NPM / Yarn
.npm
.node_repl_history
npm-debug.log*
.yarn

# Generated assets
/assets
server/views/master.pug
server/views/legacy/master.pug
server/views/setup.pug

# Webpack
.webpack-cache
.fusebox

# Config Files
/config.yml

# Data directories
/repo
/data
/uploads
/content
/temp
*.sqlite

# IDE exclude
.idea
*.sublime-*

# Test results
test-results/
.scannerwork

# Localization Resources
/server/locales/**/*.yml


================================================
FILE: .npmrc
================================================
save-exact = true
save-prefix = ""


================================================
FILE: .nvmrc
================================================
v24.12.0


================================================
FILE: .vscode/extensions.json
================================================
{
  "recommendations": [
    "EditorConfig.editorconfig",
    "dbaeumer.vscode-eslint",
    "christian-kohler.path-intellisense",
    "mrmlnc.vscode-puglint",
    "octref.vetur"
  ]
}


================================================
FILE: .vscode/launch.json
================================================
{
    // Use IntelliSense to learn about possible Node.js debug attributes.
    // Hover to view descriptions of existing attributes.
    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
    {
      "type": "node",
      "request": "attach",
      "name": "Attach (Inspector Protocol)",
      "port": 9229,
      "protocol": "inspector"
    },
        {
            "type": "node",
            "request": "launch",
            "name": "Launch Program",
            "program": "${workspaceRoot}\\server.js"
        },
        {
            "type": "node",
            "request": "attach",
            "name": "Attach to Port",
            "address": "localhost",
            "port": 9222
        }
    ]
}


================================================
FILE: .vscode/settings.json
================================================
{
  "eslint.enable": true,
  "puglint.enable": true,
  "editor.formatOnSave": false,
  "editor.tabSize": 2,
  "eslint.validate": [
    "javascript",
    "vue"
  ],
  "editor.codeActionsOnSave": {
    "source.fixAll.eslint": "explicit"
  },
  "i18n-ally.localesPaths": [
    "server/locales"
  ]
}


================================================
FILE: LICENSE
================================================
                    GNU AFFERO GENERAL PUBLIC LICENSE
                       Version 3, 19 November 2007

 Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
 Everyone is permitted to copy and distribute verbatim copies
 of this license document, but changing it is not allowed.

                            Preamble

  The GNU Affero General Public License is a free, copyleft license for
software and other kinds of works, specifically designed to ensure
cooperation with the community in the case of network server software.

  The licenses for most software and other practical works are designed
to take away your freedom to share and change the works.  By contrast,
our General Public Licenses are intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users.

  When we speak of free software, we are referring to freedom, not
price.  Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.

  Developers that use our General Public Licenses protect your rights
with two steps: (1) assert copyright on the software, and (2) offer
you this License which gives you legal permission to copy, distribute
and/or modify the software.

  A secondary benefit of defending all users' freedom is that
improvements made in alternate versions of the program, if they
receive widespread use, become available for other developers to
incorporate.  Many developers of free software are heartened and
encouraged by the resulting cooperation.  However, in the case of
software used on network servers, this result may fail to come about.
The GNU General Public License permits making a modified version and
letting the public access it on a server without ever releasing its
source code to the public.

  The GNU Affero General Public License is designed specifically to
ensure that, in such cases, the modified source code becomes available
to the community.  It requires the operator of a network server to
provide the source code of the modified version running there to the
users of that server.  Therefore, public use of a modified version, on
a publicly accessible server, gives the public access to the source
code of the modified version.

  An older license, called the Affero General Public License and
published by Affero, was designed to accomplish similar goals.  This is
a different license, not a version of the Affero GPL, but Affero has
released a new version of the Affero GPL which permits relicensing under
this license.

  The precise terms and conditions for copying, distribution and
modification follow.

                       TERMS AND CONDITIONS

  0. Definitions.

  "This License" refers to version 3 of the GNU Affero General Public License.

  "Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.

  "The Program" refers to any copyrightable work licensed under this
License.  Each licensee is addressed as "you".  "Licensees" and
"recipients" may be individuals or organizations.

  To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy.  The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.

  A "covered work" means either the unmodified Program or a work based
on the Program.

  To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy.  Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.

  To "convey" a work means any kind of propagation that enables other
parties to make or receive copies.  Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.

  An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License.  If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.

  1. Source Code.

  The "source code" for a work means the preferred form of the work
for making modifications to it.  "Object code" means any non-source
form of a work.

  A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.

  The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form.  A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.

  The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities.  However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work.  For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.

  The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.

  The Corresponding Source for a work in source code form is that
same work.

  2. Basic Permissions.

  All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met.  This License explicitly affirms your unlimited
permission to run the unmodified Program.  The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work.  This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.

  You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force.  You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright.  Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.

  Conveying under any other circumstances is permitted solely under
the conditions stated below.  Sublicensing is not allowed; section 10
makes it unnecessary.

  3. Protecting Users' Legal Rights From Anti-Circumvention Law.

  No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.

  When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.

  4. Conveying Verbatim Copies.

  You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.

  You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.

  5. Conveying Modified Source Versions.

  You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:

    a) The work must carry prominent notices stating that you modified
    it, and giving a relevant date.

    b) The work must carry prominent notices stating that it is
    released under this License and any conditions added under section
    7.  This requirement modifies the requirement in section 4 to
    "keep intact all notices".

    c) You must license the entire work, as a whole, under this
    License to anyone who comes into possession of a copy.  This
    License will therefore apply, along with any applicable section 7
    additional terms, to the whole of the work, and all its parts,
    regardless of how they are packaged.  This License gives no
    permission to license the work in any other way, but it does not
    invalidate such permission if you have separately received it.

    d) If the work has interactive user interfaces, each must display
    Appropriate Legal Notices; however, if the Program has interactive
    interfaces that do not display Appropriate Legal Notices, your
    work need not make them do so.

  A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit.  Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.

  6. Conveying Non-Source Forms.

  You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:

    a) Convey the object code in, or embodied in, a physical product
    (including a physical distribution medium), accompanied by the
    Corresponding Source fixed on a durable physical medium
    customarily used for software interchange.

    b) Convey the object code in, or embodied in, a physical product
    (including a physical distribution medium), accompanied by a
    written offer, valid for at least three years and valid for as
    long as you offer spare parts or customer support for that product
    model, to give anyone who possesses the object code either (1) a
    copy of the Corresponding Source for all the software in the
    product that is covered by this License, on a durable physical
    medium customarily used for software interchange, for a price no
    more than your reasonable cost of physically performing this
    conveying of source, or (2) access to copy the
    Corresponding Source from a network server at no charge.

    c) Convey individual copies of the object code with a copy of the
    written offer to provide the Corresponding Source.  This
    alternative is allowed only occasionally and noncommercially, and
    only if you received the object code with such an offer, in accord
    with subsection 6b.

    d) Convey the object code by offering access from a designated
    place (gratis or for a charge), and offer equivalent access to the
    Corresponding Source in the same way through the same place at no
    further charge.  You need not require recipients to copy the
    Corresponding Source along with the object code.  If the place to
    copy the object code is a network server, the Corresponding Source
    may be on a different server (operated by you or a third party)
    that supports equivalent copying facilities, provided you maintain
    clear directions next to the object code saying where to find the
    Corresponding Source.  Regardless of what server hosts the
    Corresponding Source, you remain obligated to ensure that it is
    available for as long as needed to satisfy these requirements.

    e) Convey the object code using peer-to-peer transmission, provided
    you inform other peers where the object code and Corresponding
    Source of the work are being offered to the general public at no
    charge under subsection 6d.

  A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.

  A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling.  In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage.  For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product.  A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.

  "Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source.  The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.

  If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information.  But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).

  The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed.  Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.

  Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.

  7. Additional Terms.

  "Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law.  If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.

  When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it.  (Additional permissions may be written to require their own
removal in certain cases when you modify the work.)  You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.

  Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:

    a) Disclaiming warranty or limiting liability differently from the
    terms of sections 15 and 16 of this License; or

    b) Requiring preservation of specified reasonable legal notices or
    author attributions in that material or in the Appropriate Legal
    Notices displayed by works containing it; or

    c) Prohibiting misrepresentation of the origin of that material, or
    requiring that modified versions of such material be marked in
    reasonable ways as different from the original version; or

    d) Limiting the use for publicity purposes of names of licensors or
    authors of the material; or

    e) Declining to grant rights under trademark law for use of some
    trade names, trademarks, or service marks; or

    f) Requiring indemnification of licensors and authors of that
    material by anyone who conveys the material (or modified versions of
    it) with contractual assumptions of liability to the recipient, for
    any liability that these contractual assumptions directly impose on
    those licensors and authors.

  All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10.  If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term.  If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.

  If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.

  Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.

  8. Termination.

  You may not propagate or modify a covered work except as expressly
provided under this License.  Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).

  However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.

  Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.

  Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License.  If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.

  9. Acceptance Not Required for Having Copies.

  You are not required to accept this License in order to receive or
run a copy of the Program.  Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance.  However,
nothing other than this License grants you permission to propagate or
modify any covered work.  These actions infringe copyright if you do
not accept this License.  Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.

  10. Automatic Licensing of Downstream Recipients.

  Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License.  You are not responsible
for enforcing compliance by third parties with this License.

  An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations.  If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.

  You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License.  For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.

  11. Patents.

  A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based.  The
work thus licensed is called the contributor's "contributor version".

  A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version.  For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.

  Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.

  In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement).  To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.

  If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients.  "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.

  If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.

  A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License.  You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.

  Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.

  12. No Surrender of Others' Freedom.

  If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License.  If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all.  For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.

  13. Remote Network Interaction; Use with the GNU General Public License.

  Notwithstanding any other provision of this License, if you modify the
Program, your modified version must prominently offer all users
interacting with it remotely through a computer network (if your version
supports such interaction) an opportunity to receive the Corresponding
Source of your version by providing access to the Corresponding Source
from a network server at no charge, through some standard or customary
means of facilitating copying of software.  This Corresponding Source
shall include the Corresponding Source for any work covered by version 3
of the GNU General Public License that is incorporated pursuant to the
following paragraph.

  Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU General Public License into a single
combined work, and to convey the resulting work.  The terms of this
License will continue to apply to the part which is the covered work,
but the work with which it is combined will remain governed by version
3 of the GNU General Public License.

  14. Revised Versions of this License.

  The Free Software Foundation may publish revised and/or new versions of
the GNU Affero General Public License from time to time.  Such new versions
will be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.

  Each version is given a distinguishing version number.  If the
Program specifies that a certain numbered version of the GNU Affero General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation.  If the Program does not specify a version number of the
GNU Affero General Public License, you may choose any version ever published
by the Free Software Foundation.

  If the Program specifies that a proxy can decide which future
versions of the GNU Affero General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.

  Later license versions may give you additional or different
permissions.  However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.

  15. Disclaimer of Warranty.

  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

  16. Limitation of Liability.

  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.

  17. Interpretation of Sections 15 and 16.

  If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.

                     END OF TERMS AND CONDITIONS

            How to Apply These Terms to Your New Programs

  If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.

  To do so, attach the following notices to the program.  It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.

    <one line to give the program's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as published
    by the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

Also add information on how to contact you by electronic and paper mail.

  If your software can interact with users remotely through a computer
network, you should also make sure that it provides a way for users to
get its source.  For example, if your program is a web application, its
interface could display a "Source" link that leads users to an archive
of the code.  There are many ways you could offer source, and different
solutions will be better for different programs; see section 13 for the
specific requirements.

  You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU AGPL, see
<http://www.gnu.org/licenses/>.


================================================
FILE: README.md
================================================
<div align="center">

<picture>
  <source media="(prefers-color-scheme: dark)" srcset="https://static.requarks.io/logo/wikijs-full-darktheme.svg">
  <img alt="Wiki.js" src="https://static.requarks.io/logo/wikijs-full.svg" width="600">
</picture>

[![Release](https://img.shields.io/github/release/Requarks/wiki.svg?style=flat&maxAge=3600)](https://github.com/Requarks/wiki/releases)
[![License](https://img.shields.io/badge/license-AGPLv3-blue.svg?style=flat)](https://github.com/requarks/wiki/blob/master/LICENSE)
[![Standard - JavaScript Style Guide](https://img.shields.io/badge/code%20style-standard-green.svg?style=flat&logo=javascript&logoColor=white)](http://standardjs.com/)
[![Build + Publish](https://github.com/Requarks/wiki/actions/workflows/build.yml/badge.svg)](https://github.com/Requarks/wiki/actions/workflows/build.yml)  
[![GitHub Sponsors](https://img.shields.io/github/sponsors/ngpixel?logo=github&color=ea4aaa)](https://github.com/users/NGPixel/sponsorship)
[![Open Collective backers and sponsors](https://img.shields.io/opencollective/all/wikijs?label=backers&color=218bff&logo=opencollective&logoColor=white)](https://opencollective.com/wikijs)
[![Downloads](https://img.shields.io/github/downloads/Requarks/wiki/total.svg?style=flat&logo=github)](https://github.com/Requarks/wiki/releases)
[![Docker Pulls](https://img.shields.io/docker/pulls/requarks/wiki.svg?logo=docker&logoColor=white)](https://hub.docker.com/r/requarks/wiki/)  
[![Chat on Discord](https://img.shields.io/badge/discord-join-8D96F6.svg?style=flat&logo=discord&logoColor=white)](https://discord.gg/rcxt9QS2jd)
[![Follow on Bluesky](https://img.shields.io/badge/bluesky-%40js.wiki-blue.svg?style=flat&logo=bluesky&logoColor=white)](https://bsky.app/profile/js.wiki)
[![Follow on Telegram](https://img.shields.io/badge/telegram-%40wiki__js-blue.svg?style=flat&logo=telegram)](https://t.me/wiki_js)
[![Reddit](https://img.shields.io/badge/reddit-%2Fr%2Fwikijs-orange?logo=reddit&logoColor=white)](https://www.reddit.com/r/wikijs/)

##### A modern, lightweight and powerful wiki app built on NodeJS

</div>

- **[Official Website](https://js.wiki/)**
- **[Documentation](https://docs.requarks.io/)**
- [Requirements](https://docs.requarks.io/install/requirements)
- [Installation](https://docs.requarks.io/install)
- [Demo](https://docs.requarks.io/demo)
- [Changelog](https://github.com/requarks/wiki/releases)
- [Feature Requests](https://feedback.js.wiki/wiki)
- Chat with us on [Discord](https://discord.gg/rcxt9QS2jd)
- [Translations](https://docs.requarks.io/dev/translations) *(We need your help!)*
- [E2E Testing Results](https://dashboard.cypress.io/projects/r7qxah/runs)
- [Special Thanks](#special-thanks)
- [Contribute](#contributors)

[Follow our Twitter feed](https://twitter.com/requarks) to learn about upcoming updates and new releases!

<h2 align="center">Donate</h2>

<div align="center">

Wiki.js is an open source project that has been made possible due to the generous contributions by community [backers](https://js.wiki/about). If you are interested in supporting this project, please consider [becoming a sponsor](https://github.com/users/NGPixel/sponsorship), [becoming a patron](https://www.patreon.com/requarks), donating to our [OpenCollective](https://opencollective.com/wikijs), via [Paypal](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=FLV5X255Z9CJU&source=url) or via Ethereum (`0xe1d55c19ae86f6bcbfb17e7f06ace96bdbb22cb5`).
  
  [![Become a Sponsor](https://img.shields.io/badge/donate-github-ea4aaa.svg?style=popout&logo=github)](https://github.com/users/NGPixel/sponsorship)
  [![Become a Patron](https://img.shields.io/badge/donate-patreon-orange.svg?style=popout&logo=patreon)](https://www.patreon.com/requarks)
  [![Donate on OpenCollective](https://img.shields.io/badge/donate-open%20collective-blue.svg?style=popout&logo=data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48c3ZnIHdpZHRoPSIyNTZweCIgaGVpZ2h0PSIyNTZweCIgdmlld0JveD0iMCAwIDI1NiAyNTYiIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgcHJlc2VydmVBc3BlY3RSYXRpbz0ieE1pZFlNaWQiPjxnPjxwYXRoIGQ9Ik0yMDkuNzY1MTQ0LDEyOC4xNDk5NzkgQzIwOS43NjUxNDQsMTQ0LjE2MzMgMjA0Ljg2NDM4MSwxNTkuNDg5ODkgMTk2LjQ5ODc0NywxNzIuNzI1MDcyIEwyMjkuOTQ1Njc1LDIwNi4xNzE5OTkgQzI0Ni42ODIxMDUsMTgzLjg1Njc1OSAyNTUuNzI5MzA3LDE1Ni43MTUxNTIgMjU1LjcyOTMwNywxMjguODIxMTAyIEMyNTUuNzI5MzA3LDk5LjU1Njk5MTcgMjQ1Ljk3NDYwMyw3My4wNzEwMjA3IDIyOS4yNTg5NDQsNTEuNDg1ODEyOCBMMTk2LjQ4MzE0LDg0LjIxNDc5NCBDMjA1LjEyMjU2MSw5Ny4yMjI0NjgzIDIwOS43MzY5MDcsMTEyLjQ4NzgxIDIwOS43NDk1MzcsMTI4LjEwMzE1NiBMMjA5Ljc2NTE0NCwxMjguMTQ5OTc5IFoiIGZpbGw9IiNCOEQzRjQiPjwvcGF0aD48cGF0aCBkPSJNMTI3LjUxMzQ4NCwyMTAuMzU0ODE2IEM4Mi4xNDYwODcyLDIxMC4yNjg5NTggNDUuMzg3NTA5NCwxNzMuNTE3MzU4IDQ1LjI5MzAzOTMsMTI4LjE0OTk3OSBDNDUuMzYxNzUwMiw4Mi43NjQzMTM4IDgyLjEyNzg0ODcsNDUuOTg0MjU3IDEyNy41MTM0ODQsNDUuODk4MzE4NiBDMTQ0LjI0NDc1Miw0NS44OTgzMTg2IDE1OS41NzEzNDIsNTAuNzk5MDgxNyAxNzIuMTE5NzkyLDU5LjE2NDcxNTQgTDIwNC44NjQzODEsMjYuMzg4OTExNiBDMTgyLjU0MzY1LDkuNjY2NjUxMjkgMTU1LjQwMzQyOSwwLjYzMDg2MzI5OCAxMjcuNTEzNDg0LDAuNjM2NDk0NDAzIEM1Ny4xMjM1NDM3LDAuNjM2NDk0NDAzIDAsNTcuNzYwMDM4MSAwLDEyOC4xNDk5NzkgQzAsMTk4LjUwODcwNCA1Ny4xMjM1NDM3LDI1NS42NjM0NjMgMTI3LjUxMzQ4NCwyNTUuNjYzNDYzIEMxNTUuNTM3MzUyLDI1NS43NDA4NzYgMTgyLjc3NTk4OSwyNDYuNDA4NTEgMjA0Ljg2NDM4MSwyMjkuMTYxODg0IEwxNzEuNDE3NDU0LDE5NS43MzA1NjQgQzE1OS41NTU3MzQsMjA1LjQ4NTI2OCAxNDQuMjYwMzU5LDIxMC4zNTQ4MTYgMTI3LjUxMzQ4NCwyMTAuMzU0ODE2IEwxMjcuNTEzNDg0LDIxMC4zNTQ4MTYgWiIgZmlsbD0iIzdGQURGMiI+PC9wYXRoPjwvZz48L3N2Zz4=)](https://opencollective.com/wikijs)
  [![Donate via Paypal](https://img.shields.io/badge/donate-paypal-blue.svg?style=popout&logo=paypal)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=FLV5X255Z9CJU&source=url)  
  [![Donate via Ethereum](https://img.shields.io/badge/donate-ethereum-999.svg?style=popout&logo=ethereum&logoColor=CCC)](https://etherscan.io/address/0xe1d55c19ae86f6bcbfb17e7f06ace96bdbb22cb5)
  [![Donate via Bitcoin](https://img.shields.io/badge/donate-bitcoin-ff9900.svg?style=popout&logo=bitcoin&logoColor=CCC)](https://checkout.opennode.com/p/2553c612-f863-4407-82b3-1a7685268747)
  [![Buy a T-Shirt](https://img.shields.io/badge/buy-t--shirts-teal.svg?style=popout&logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHg9IjBweCIgeT0iMHB4Igp3aWR0aD0iMjQiIGhlaWdodD0iMjQiCnZpZXdCb3g9IjAgMCAxOTIgMTkyIgpzdHlsZT0iIGZpbGw6IzAwMDAwMDsiPjxnIGZpbGw9Im5vbmUiIGZpbGwtcnVsZT0ibm9uemVybyIgc3Ryb2tlPSJub25lIiBzdHJva2Utd2lkdGg9IjEiIHN0cm9rZS1saW5lY2FwPSJidXR0IiBzdHJva2UtbGluZWpvaW49Im1pdGVyIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiIHN0cm9rZS1kYXNoYXJyYXk9IiIgc3Ryb2tlLWRhc2hvZmZzZXQ9IjAiIGZvbnQtZmFtaWx5PSJub25lIiBmb250LXdlaWdodD0ibm9uZSIgZm9udC1zaXplPSJub25lIiB0ZXh0LWFuY2hvcj0ibm9uZSIgc3R5bGU9Im1peC1ibGVuZC1tb2RlOiBub3JtYWwiPjxwYXRoIGQ9Ik0wLDE5MnYtMTkyaDE5MnYxOTJ6IiBmaWxsPSJub25lIj48L3BhdGg+PGcgZmlsbD0iIzFhYmM5YyI+PGcgaWQ9InN1cmZhY2UxIj48cGF0aCBkPSJNOTYsMGMtMTUuMjE4NzUsMCAtMjQuNjg3NSwzLjY1NjI1IC0yNS41LDRsLTIyLjUsNy4yNWMtMTAuNDA2MjUsMy4xODc1IC0xOS4wOTM3NSw5LjQzNzUgLTI1LjUsMTguMjVsLTIyLjUsNDIuNWwyNy4yNSwxNi43NWwxMi43NSwtMjR2MTE5LjI1YzAsNC40MDYyNSAyNS4wNjI1LDggNTYsOGMzMC45Mzc1LDAgNTYsLTMuNTkzNzUgNTYsLTh2LTExOS4yNWwxMi43NSwyNGwyNy4yNSwtMTYuNzVsLTIyLjUsLTQyLjVjLTYuNDA2MjUsLTguODEyNSAtMTUuMTU2MjUsLTE1LjA2MjUgLTI0Ljc1LC0xOC4yNWwtMjIuMjUsLTcuMjVjLTAuMTg3NSwwIC0xLjAzMTI1LDEuMzEyNSAtMiwyLjc1bDEuMjUsLTIuNWMwLDAgLTkuODQzNzUsLTQuMjUgLTI1Ljc1LC00LjI1ek05Niw4YzExLjQwNjI1LDAgMTguNDM3NSwyLjI1IDIxLDMuMjVjLTQuNDY4NzUsNS43NSAtMTEuNDA2MjUsMTIuNzUgLTIxLDEyLjc1Yy05LjQwNjI1LDAgLTE2LjQwNjI1LC03LjA2MjUgLTIwLjc1LC0xMi43NWMyLjg3NSwtMS4wNjI1IDkuODc1LC0zLjI1IDIwLjc1LC0zLjI1eiI+PC9wYXRoPjwvZz48L2c+PC9nPjwvc3ZnPg==)](https://wikijs.threadless.com)

</div>

<h2 align="center">Gold Tier Sponsors</h2>

<div align="center">
<table>
  <tbody>
    <tr>
      <td align="center" valign="middle" width="444">
        <a href="https://trans-zero.com/" target="_blank">
          <img src="https://cdn.js.wiki/images/sponsors/transzero.png">
        </a>
      </td>
    </tr>
  </tbody>
</table>
</div>

<h2 align="center">GitHub Sponsors</h2>

Support this project by becoming a sponsor. Your name will show up in the Contribute page of all Wiki.js installations as well as here with a link to your website! [[Become a sponsor](https://github.com/users/NGPixel/sponsorship)]

<div align="center">
<table>
  <tbody>
    <tr>
      <td align="center" valign="middle" width="444">
        <a href="https://www.stellarhosted.com/" target="_blank">
          <img src="https://cdn.js.wiki/images/sponsors/stellarhosted.png">
        </a>
      </td>
    </tr>
  </tbody>
</table>
</div>

<div align="center">
<table>
  <tbody>
    <tr>
      <td align="center" valign="middle" width="130">
        <a href="https://acceleanation.com/" target="_blank">
          <img src="https://avatars.githubusercontent.com/u/41210718?s=200&v=4">
        </a>
      </td>
      <td align="center" valign="middle" width="130">
        <a href="https://github.com/alexksso" target="_blank">
          Alexander Casassovici<br />(@alexksso)
        </a>
      </td>
      <td align="center" valign="middle" width="130">
        <a href="https://github.com/broxen" target="_blank">
          Broxen<br />(@broxen)
        </a>
      </td>
      <td align="center" valign="middle" width="130">
        <a href="https://github.com/xDacon" target="_blank">
          Dacon<br />(@xDacon)
        </a>
      </td>
      <td align="center" valign="middle" width="130">
        <a href="https://github.com/DonNabla" target="_blank">
          Maxime Pierre<br />(@DonNabla)
        </a>
      </td>
      <td align="center" valign="middle" width="130">
        <a href="https://github.com/GigabiteLabs" target="_blank">
          <img src="https://static.requarks.io/sponsors/gigabitelabs-148x129.png">
        </a>
      </td>
      <td align="center" valign="middle" width="130">
        <a href="https://www.hostwiki.com/" target="_blank">
          <img src="https://cdn.js.wiki/images/sponsors/hostwiki.png">
        </a>
      </td>
    </tr>
    <tr>
      <td align="center" valign="middle" width="130">
        <a href="https://github.com/JayDaley" target="_blank">
          Jay Daley<br />(@JayDaley)
        </a>
      </td>
      <td align="center" valign="middle" width="130">
        <a href="https://github.com/idokka" target="_blank">
          Oleksii<br />(@idokka)
        </a>
      </td>
      <td align="center" valign="middle" width="130">
        <a href="https://www.openhost-network.com/" target="_blank">
          <img src="https://avatars.githubusercontent.com/u/114218287?s=200&v=4">
        </a>
      </td>
      <td align="center" valign="middle" width="130">
        <a href="https://www.prevo.ch/" target="_blank">
          <img src="https://avatars.githubusercontent.com/u/114394792?v=4">
        </a>
      </td>
      <td align="center" valign="middle" width="130">
        <a href="https://github.com/shanekearney" target="_blank">
          Shane Kearney<br />(@shanekearney)
        </a>
      </td>
      <td align="center" valign="middle" width="130">
        <a href="http://www.taicep.org/" target="_blank">
          <img src="https://avatars.githubusercontent.com/u/160072306?v=4">
        </a>
      </td>
      <td align="center" valign="middle" width="130"></td>
    </tr>
  </tbody>
</table>

<table><tbody><tr><td>
<img width="441" height="1" />

- Akira Suenami ([@a-suenami](https://github.com/a-suenami))
- Armin Reiter ([@arminreiter](https://github.com/arminreiter))
- Arnaud Marchand ([@snuids](https://github.com/snuids))
- Brian Douglass ([@bhdouglass](https://github.com/bhdouglass))
- Bryon Vandiver ([@asterick](https://github.com/asterick))
- Cameron Steele ([@ATechAdventurer](https://github.com/ATechAdventurer))
- Charlie Schliesser ([@charlie-s](https://github.com/charlie-s))
- Cloud Data Hosting LLC ([@CloudDataHostingLLC](https://github.com/CloudDataHostingLLC))
- Cole Manning ([@RVRX](https://github.com/RVRX))
- CrazyMarvin ([@CrazyMarvin](https://github.com/CrazyMarvin))
- Daniel Horner ([@danhorner](https://github.com/danhorner))
- David Christian Holin ([@SirGibihm](https://github.com/SirGibihm))
- Dragan Espenschied ([@despens](https://github.com/despens))
- Elijah Zobenko ([@he110](https://github.com/he110))
- Emerson-Perna ([@Emerson-Perna](https://github.com/Emerson-Perna))
- Ernie ([@iamernie](https://github.com/iamernie))
- Fabio Ferrari ([@devxops](https://github.com/devxops))
- Finsa S.p.A. ([@finsaspa](https://github.com/finsaspa))
- Florian Moss ([@florianmoss](https://github.com/florianmoss))
- GoodCorporateCitizen ([@GoodCorporateCitizen](https://github.com/GoodCorporateCitizen))
- HeavenBay ([@HeavenBay](https://github.com/heavenbay))
- HikaruEgashira ([@HikaruEgashira](https://github.com/HikaruEgashira))
- Ian Hyzy ([@ianhyzy](https://github.com/ianhyzy))
- Jaimyn Mayer ([@jabelone](https://github.com/jabelone))
- Jay Lee ([@polyglotm](https://github.com/polyglotm))
- Kelly Wardrop ([@dropcoded](https://github.com/dropcoded))
- Loki ([@binaryloki](https://github.com/binaryloki))
- MaFarine ([@MaFarine](https://github.com/MaFarine))
- Marcilio Leite Neto ([@marclneto](https://github.com/marclneto))
- Mattias Johnson ([@mattiasJohnson](https://github.com/mattiasJohnson))
- Max Ricketts-Uy ([@MaxRickettsUy](https://github.com/MaxRickettsUy))
- Mickael Asseline ([@PAPAMICA](https://github.com/PAPAMICA))
- Mitchell Rowton ([@mrowton](https://github.com/mrowton))
        
</td><td>
<img width="441" height="1" />

- M. Scott Ford ([@mscottford](https://github.com/mscottford))
- Nick Halase ([@nhalase](https://github.com/nhalase))
- Nick Price ([@DominoTree](https://github.com/DominoTree))
- Nina Reynolds ([@cutecycle](https://github.com/cutecycle))
- Noel Cower ([@nilium](https://github.com/nilium))
- Oleksandr Koltsov ([@crambo](https://github.com/crambo))
- Phi Zeroth ([@phizeroth](https://github.com/phizeroth))
- Philipp Schmitt ([@pschmitt](https://github.com/pschmitt))
- Robert Lanzke ([@winkelement](https://github.com/winkelement))
- Ruizhe Li ([@liruizhe1995](https://github.com/liruizhe1995))
- Sam Martin ([@ABitMoreDepth](https://github.com/ABitMoreDepth))
- Sean Coffey ([@seanecoffey](https://github.com/seanecoffey))
- Simon Ott ([@ottsimon](https://github.com/ottsimon))
- Stephan Kristyn ([@stevek-pro](https://github.com/stevek-pro))
- Theodore Chu ([@TheodoreChu](https://github.com/TheodoreChu))
- Tim Elmer ([@tim-elmer](https://github.com/tim-elmer))
- Tyler Denman ([@tylerguy](https://github.com/tylerguy))
- Victor Bilgin ([@vbilgin](https://github.com/vbilgin))
- VMO Solutions ([@vmosolutions](https://github.com/vmosolutions))
- YazMogg35 ([@YazMogg35](https://github.com/YazMogg35))
- Yu Yongwoo ([@uyu423](https://github.com/uyu423))
- ameyrakheja ([@ameyrakheja](https://github.com/ameyrakheja))
- aniketpanjwani ([@aniketpanjwani](https://github.com/aniketpanjwani))
- aytaa ([@aytaa](https://github.com/aytaa))
- cesar ([@cesarnr21](https://github.com/cesarnr21))
- chaee ([@chaee](https://github.com/chaee))
- lwileczek ([@lwileczek](https://github.com/lwileczek))
- magicpotato ([@fortheday](https://github.com/fortheday))
- motoacs ([@motoacs](https://github.com/motoacs))
- muzian666 ([@muzian666](https://github.com/muzian666))
- rburckner ([@rburckner](https://github.com/rburckner))
- scorpion ([@scorpion](https://github.com/scorpion))
- valantien ([@valantien](https://github.com/valantien))
        
</td></tr></tbody></table>
</div>

<h2 align="center">OpenCollective Sponsors</h2>

Support this project by becoming a sponsor. Your logo will show up in the Contribute page of all Wiki.js installations as well as here with a link to your website! [[Become a sponsor](https://opencollective.com/wikijs#sponsor)]

<div align="center">
<table>
  <tbody>
    <tr>
      <td align="center" valign="middle">
        <a href="https://opencollective.com/wikijs/sponsor/0/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/0/avatar.svg"></a>
      </td>
      <td align="center" valign="middle">
        <a href="https://opencollective.com/wikijs/sponsor/1/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/1/avatar.svg"></a>
      </td>
      <td align="center" valign="middle">
        <a href="https://opencollective.com/wikijs/sponsor/2/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/2/avatar.svg"></a>
      </td>
      <td align="center" valign="middle">
        <a href="https://opencollective.com/wikijs/sponsor/3/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/3/avatar.svg"></a>
      </td>
      <td align="center" valign="middle">
        <a href="https://opencollective.com/wikijs/sponsor/4/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/4/avatar.svg"></a>
      </td>
    </tr>
    <tr>
      <td align="center" valign="middle">
        <a href="https://opencollective.com/wikijs/sponsor/5/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/5/avatar.svg"></a>
      </td>
      <td align="center" valign="middle">
        <a href="https://opencollective.com/wikijs/sponsor/6/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/6/avatar.svg"></a>
      </td>
      <td align="center" valign="middle">
        <a href="https://opencollective.com/wikijs/sponsor/7/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/7/avatar.svg"></a>
      </td>
      <td align="center" valign="middle">
        <a href="https://opencollective.com/wikijs/sponsor/8/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/8/avatar.svg"></a>
      </td>
      <td align="center" valign="middle">
        <a href="https://opencollective.com/wikijs/sponsor/9/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/9/avatar.svg"></a>
      </td>
    </tr>
    <tr>
      <td align="center" valign="middle">
        <a href="https://opencollective.com/wikijs/sponsor/10/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/10/avatar.svg"></a>
      </td>
      <td align="center" valign="middle">
        <a href="https://opencollective.com/wikijs/sponsor/11/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/11/avatar.svg"></a>
      </td>
      <td align="center" valign="middle">
        <a href="https://opencollective.com/wikijs/sponsor/12/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/12/avatar.svg"></a>
      </td>
      <td align="center" valign="middle">
        <a href="https://opencollective.com/wikijs/sponsor/13/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/13/avatar.svg"></a>
      </td>
      <td align="center" valign="middle">
        <a href="https://opencollective.com/wikijs/sponsor/14/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/14/avatar.svg"></a>
      </td>
    </tr>
    <tr>
      <td align="center" valign="middle">
        <a href="https://opencollective.com/wikijs/sponsor/15/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/15/avatar.svg"></a>
      </td>
      <td align="center" valign="middle">
        <a href="https://opencollective.com/wikijs/sponsor/16/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/16/avatar.svg"></a>
      </td>
      <td align="center" valign="middle">
        <a href="https://opencollective.com/wikijs/sponsor/17/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/17/avatar.svg"></a>
      </td>
      <td align="center" valign="middle">
        <a href="https://opencollective.com/wikijs/sponsor/18/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/18/avatar.svg"></a>
      </td>
      <td align="center" valign="middle">
        <a href="https://opencollective.com/wikijs/sponsor/19/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/19/avatar.svg"></a>
      </td>
    </tr>
    <tr>
      <td align="center" valign="middle">
        <a href="https://opencollective.com/wikijs/sponsor/20/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/20/avatar.svg"></a>
      </td>
      <td align="center" valign="middle">
        <a href="https://opencollective.com/wikijs/sponsor/21/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/21/avatar.svg"></a>
      </td>
      <td align="center" valign="middle">
        <a href="https://opencollective.com/wikijs/sponsor/22/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/22/avatar.svg"></a>
      </td>
      <td align="center" valign="middle">
        <a href="https://opencollective.com/wikijs/sponsor/23/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/23/avatar.svg"></a>
      </td>
      <td align="center" valign="middle">
        <a href="https://opencollective.com/wikijs/sponsor/24/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/24/avatar.svg"></a>
      </td>
    </tr>
    <tr>
      <td align="center" valign="middle">
        <a href="https://opencollective.com/wikijs/sponsor/25/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/25/avatar.svg"></a>
      </td>
      <td align="center" valign="middle">
        <a href="https://opencollective.com/wikijs/sponsor/26/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/26/avatar.svg"></a>
      </td>
      <td align="center" valign="middle">
        <a href="https://opencollective.com/wikijs/sponsor/27/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/27/avatar.svg"></a>
      </td>
      <td align="center" valign="middle">
        <a href="https://opencollective.com/wikijs/sponsor/28/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/28/avatar.svg"></a>
      </td>
      <td align="center" valign="middle">
        <a href="https://opencollective.com/wikijs/sponsor/29/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/29/avatar.svg"></a>
      </td>
    </tr>
    <tr>
      <td align="center" valign="middle">
        <a href="https://opencollective.com/wikijs/sponsor/30/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/30/avatar.svg"></a>
      </td>
      <td align="center" valign="middle">
        <a href="https://opencollective.com/wikijs/sponsor/31/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/31/avatar.svg"></a>
      </td>
      <td align="center" valign="middle">
        <a href="https://opencollective.com/wikijs/sponsor/32/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/32/avatar.svg"></a>
      </td>
      <td align="center" valign="middle">
        <a href="https://opencollective.com/wikijs/sponsor/33/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/33/avatar.svg"></a>
      </td>
      <td align="center" valign="middle">
        <a href="https://opencollective.com/wikijs/sponsor/34/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/34/avatar.svg"></a>
      </td>
    </tr>
    <tr>
      <td align="center" valign="middle">
        <a href="https://opencollective.com/wikijs/sponsor/35/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/35/avatar.svg"></a>
      </td>
      <td align="center" valign="middle">
        <a href="https://opencollective.com/wikijs/sponsor/36/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/36/avatar.svg"></a>
      </td>
      <td align="center" valign="middle">
        <a href="https://opencollective.com/wikijs/sponsor/37/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/37/avatar.svg"></a>
      </td>
      <td align="center" valign="middle">
        <a href="https://opencollective.com/wikijs/sponsor/38/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/38/avatar.svg"></a>
      </td>
      <td align="center" valign="middle">
        <a href="https://opencollective.com/wikijs/sponsor/39/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/39/avatar.svg"></a>
      </td>
    </tr>
    <tr>
      <td align="center" valign="middle">
        <a href="https://opencollective.com/wikijs/sponsor/40/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/40/avatar.svg"></a>
      </td>
      <td align="center" valign="middle">
        <a href="https://opencollective.com/wikijs/sponsor/41/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/41/avatar.svg"></a>
      </td>
      <td align="center" valign="middle">
        <a href="https://opencollective.com/wikijs/sponsor/42/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/42/avatar.svg"></a>
      </td>
      <td align="center" valign="middle">
        <a href="https://opencollective.com/wikijs/sponsor/43/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/43/avatar.svg"></a>
      </td>
      <td align="center" valign="middle">
        <a href="https://opencollective.com/wikijs/sponsor/44/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/44/avatar.svg"></a>
      </td>
    </tr>
    <tr>
      <td align="center" valign="middle">
        <a href="https://opencollective.com/wikijs/sponsor/40/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/45/avatar.svg"></a>
      </td>
      <td align="center" valign="middle">
        <a href="https://opencollective.com/wikijs/sponsor/41/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/46/avatar.svg"></a>
      </td>
      <td align="center" valign="middle">
        <a href="https://opencollective.com/wikijs/sponsor/42/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/47/avatar.svg"></a>
      </td>
      <td align="center" valign="middle">
        <a href="https://opencollective.com/wikijs/sponsor/43/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/48/avatar.svg"></a>
      </td>
      <td align="center" valign="middle">
        <a href="https://opencollective.com/wikijs/sponsor/44/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/49/avatar.svg"></a>
      </td>
    </tr>
  </tbody>
</table>
</div>

<h2 align="center">Patreon Backers</h2>

Thank you to all our patrons! 🙏 [[Become a patron](https://www.patreon.com/requarks)]

<div align="center">
<table><tbody><tr><td>
<img width="441" height="1" />

- Aeternum
- Al Romano
- Alex Balabanov
- Alex Milanov
- Alex Zen
- Arti Zirk
- Ave
- Brandon Curtis
- Damien Hottelier
- Daniel T. Holtzclaw
- Dave 'Sri' Seah
- djagoo
- dz
- Douglas Lassance
- Ergoflix
- Ernie Reid
- Etienne
- Flemis Jurgenheimer
- Florent
- Günter Pavlas
- hong
- Hope
- Ian
- Imari Childress
- Iskander Callos
  
</td><td>
<img width="441" height="1" />

- Josh Stewart
- Justin Dunsworth
- Keir
- Loïc CRAMPON
- Ludgeir Ibanez
- Lyn Matten
- Mads Rosendahl
- Mark Mansur
- Matt Gedigian
- Mike Ditton
- Nate Figz
- Patryk
- Paul O'Fallon
- Philipp Schürch
- Tracey Duffy
- Quaxim
- Richeir
- Sergio Navarro Fernández
- Shad Narcher
- ShadowVoyd
- SmartNET.works
- Stepan Sokolovskyi
- Zach Crawford
- Zach Maynard
- 张白驹

</td></tr></tbody></table>
</div>

<h2 align="center">OpenCollective Backers</h2>

Thank you to all our backers! 🙏 [[Become a backer](https://opencollective.com/wikijs#backer)]

<a href="https://opencollective.com/wikijs#backers" target="_blank"><img src="https://opencollective.com/wikijs/backers.svg?width=890"></a>

<h2 align="center">Contributors</h2>

This project exists thanks to all the people who contribute. [[Contribute]](https://github.com/Requarks/wiki/blob/master/.github/CONTRIBUTING.md).
<a href="https://github.com/Requarks/wiki/graphs/contributors"><img src="https://opencollective.com/wikijs/contributors.svg?width=890" /></a>

<h2 align="center">Special Thanks</h2>

![Browserstack](https://js.wiki/legacy/logo_browserstack.png)  
[Browserstack](https://www.browserstack.com/) for providing access to their great cross-browser testing tools.

![Cloudflare](https://js.wiki/legacy/logo_cloudflare.png)  
[Cloudflare](https://www.cloudflare.com/) for providing their great CDN, SSL and advanced networking services.

![DigitalOcean](https://js.wiki/legacy/logo_digitalocean.png)  
[DigitalOcean](https://m.do.co/c/5f7445bfa4d0) for providing hosting of the Wiki.js documentation site and APIs.

![Icons8](https://static.requarks.io/logo/icons8-text-h40.png)  
[Icons8](https://icons8.com/) for providing access to their beautiful icon sets.

![Localazy](https://static.requarks.io/logo/localazy-h40.png)  
[Localazy](https://localazy.com/) for providing access to their great localization service.

![Lokalise](https://static.requarks.io/logo/lokalise-text-h40.png)  
[Lokalise](https://lokalise.com/) for providing access to their great localization tool.

![MacStadium](https://static.requarks.io/logo/macstadium-h40.png)  
[MacStadium](https://www.macstadium.com) for providing access to their Mac hardware in the cloud.

![Netlify](https://js.wiki/legacy/logo_netlify.png)  
[Netlify](https://www.netlify.com) for providing hosting for our website.

![ngrok](https://static.requarks.io/logo/ngrok-h40.png)  
[ngrok](https://ngrok.com) for providing access to their great HTTP tunneling services.

![Porkbun](https://static.requarks.io/logo/porkbun.png)  
[Porkbun](https://www.porkbun.com) for providing domain registration services.


================================================
FILE: SECURITY.md
================================================
# Security Policy

Wiki.js is built with security in mind. We try our absolute best to deliver secure and robust applications. However, like any software, there can be security bugs, either introduced by an update or by using an attack vector that wasn't considered when designing the software.

If you find such vulnerability, it's important to disclose it in a quick and secure manner to the developers. Follow the instructions below to report a vulnerability.

## Supported Versions

| Version | Supported          |
| ------- | ------------------ |
| 2.x.x   | :white_check_mark: |
| 1.x.x   | :x:                |

## Reporting a Vulnerability

> [!CAUTION]
> **DO NOT CREATE A GITHUB ISSUE / DISCUSSION** to report a potential vulnerability / security problem. Instead, use the process below:

Submit a Vulnerability Report by filling in the form on https://github.com/requarks/wiki/security/advisories/new

Include as much details as possible, such as:
- The version(s) of Wiki.js that are impacted
- How to reproduce the vulnerability (step-by-step, screenshots or a video)
- The platform / environment it occurs on (e.g. OS version, DB type + version, etc.)
- Any potential fixes or reference code you think might be helpful in resolving the issue
- Your GitHub username if you'd like to be included as a collaborator on the private fix branch

The vulnerability will be investigated ASAP. If deemed valid, a draft security advisory will be created on GitHub and you will be included as a collaborator. A fix will be worked on in a private branch to resolves the issue. Once a fix is available, the advisory will be published.

> [!NOTE]
> There's no reward for submitting a report. As this is open source project and not corporate owned, we are not able to provide monetary rewards. You will however be credited as the bug reporter in the release notes.


================================================
FILE: client/.modernizrrc.js
================================================
module.exports = {
  classPrefix: 'mdz-',
  options: ['setClasses'],
  'feature-detects': [
    'css/backdropfilter'
  ]
}


================================================
FILE: client/client-app.js
================================================
/* global siteConfig */

import Vue from 'vue'
import VueRouter from 'vue-router'
import VueClipboards from 'vue-clipboards'
import { ApolloClient } from 'apollo-client'
import { BatchHttpLink } from 'apollo-link-batch-http'
import { ApolloLink, split } from 'apollo-link'
import { WebSocketLink } from 'apollo-link-ws'
import { ErrorLink } from 'apollo-link-error'
import { InMemoryCache } from 'apollo-cache-inmemory'
import { getMainDefinition } from 'apollo-utilities'
import VueApollo from 'vue-apollo'
import Vuetify from 'vuetify/lib'
import Velocity from 'velocity-animate'
import Vuescroll from 'vuescroll/dist/vuescroll-native'
import Hammer from 'hammerjs'
import moment from 'moment-timezone'
import VueMoment from 'vue-moment'
import store from './store'
import Cookies from 'js-cookie'

// ====================================
// Load Modules
// ====================================

import boot from './modules/boot'
import localization from './modules/localization'

// ====================================
// Load Helpers
// ====================================

import helpers from './helpers'

// ====================================
// Initialize Global Vars
// ====================================

window.WIKI = null
window.boot = boot
window.Hammer = Hammer

moment.locale(siteConfig.lang)

store.commit('user/REFRESH_AUTH')

// ====================================
// Initialize Apollo Client (GraphQL)
// ====================================

const graphQLEndpoint = window.location.protocol + '//' + window.location.host + '/graphql'
const graphQLWSEndpoint = ((window.location.protocol === 'https:') ? 'wss:' : 'ws:') + '//' + window.location.host + '/graphql-subscriptions'

const graphQLLink = ApolloLink.from([
  new ErrorLink(({ graphQLErrors, networkError }) => {
    if (graphQLErrors) {
      let isAuthError = false
      graphQLErrors.map(({ message, locations, path }) => {
        if (message === `Forbidden`) {
          isAuthError = true
        }
        console.error(`[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`)
      })
      store.commit('showNotification', {
        style: 'red',
        message: isAuthError ? `You are not authorized to access this resource.` : `An unexpected error occurred.`,
        icon: 'alert'
      })
    }
    if (networkError) {
      console.error(networkError)
      store.commit('showNotification', {
        style: 'red',
        message: `Network Error: ${networkError.message}`,
        icon: 'alert'
      })
    }
  }),
  new BatchHttpLink({
    includeExtensions: true,
    uri: graphQLEndpoint,
    credentials: 'include',
    fetch: async (uri, options) => {
      // Strip __typename fields from variables
      let body = JSON.parse(options.body)
      body = body.map(bd => {
        return ({
          ...bd,
          variables: JSON.parse(JSON.stringify(bd.variables), (key, value) => { return key === '__typename' ? undefined : value })
        })
      })
      options.body = JSON.stringify(body)

      // Inject authentication token
      const jwtToken = Cookies.get('jwt')
      if (jwtToken) {
        options.headers.Authorization = `Bearer ${jwtToken}`
      }

      const resp = await fetch(uri, options)

      // Handle renewed JWT
      const newJWT = resp.headers.get('new-jwt')
      if (newJWT) {
        Cookies.set('jwt', newJWT, { expires: 365, secure: window.location.protocol === 'https:' })
      }
      return resp
    }
  })
])

const graphQLWSLink = new WebSocketLink({
  uri: graphQLWSEndpoint,
  options: {
    reconnect: true,
    lazy: true,
    connectionParams: () => {
      const token = Cookies.get('jwt')
      return token ? { token } : {}
    }
  }
})

window.graphQL = new ApolloClient({
  link: split(({ query }) => {
    const { kind, operation } = getMainDefinition(query)
    return kind === 'OperationDefinition' && operation === 'subscription'
  }, graphQLWSLink, graphQLLink),
  cache: new InMemoryCache(),
  connectToDevTools: (process.env.node_env === 'development')
})

// ====================================
// Initialize Vue Modules
// ====================================

Vue.config.productionTip = false

Vue.use(VueRouter)
Vue.use(VueApollo)
Vue.use(VueClipboards)
Vue.use(localization.VueI18Next)
Vue.use(helpers)
Vue.use(Vuetify)
Vue.use(VueMoment, { moment })
Vue.use(Vuescroll)

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'))

let bootstrap = () => {
  // ====================================
  // Notifications
  // ====================================

  window.addEventListener('beforeunload', () => {
    store.dispatch('startLoading')
  })

  const apolloProvider = new VueApollo({
    defaultClient: window.graphQL
  })

  // ====================================
  // Bootstrap Vue
  // ====================================

  const i18n = localization.init()

  let darkModeEnabled = siteConfig.darkMode
  if ((store.get('user/appearance') || '').length > 0) {
    darkModeEnabled = (store.get('user/appearance') === 'dark')
  }

  window.WIKI = new Vue({
    el: '#root',
    components: {},
    mixins: [helpers],
    apolloProvider,
    store,
    i18n,
    vuetify: new Vuetify({
      rtl: siteConfig.rtl,
      theme: {
        dark: darkModeEnabled
      }
    }),
    mounted () {
      this.$moment.locale(siteConfig.lang)
      if ((store.get('user/dateFormat') || '').length > 0) {
        this.$moment.updateLocale(this.$moment.locale(), {
          longDateFormat: {
            'L': store.get('user/dateFormat')
          }
        })
      }
      if ((store.get('user/timezone') || '').length > 0) {
        this.$moment.tz.setDefault(store.get('user/timezone'))
      }
    }
  })

  // ----------------------------------
  // Dispatch boot ready
  // ----------------------------------

  window.boot.notify('vue')
}

window.boot.onDOMReady(bootstrap)


================================================
FILE: client/client-setup.js
================================================
/* eslint-disable import/first */
import Vue from 'vue'
import Vuetify from 'vuetify/lib'
import boot from './modules/boot'
/* eslint-enable import/first */

window.WIKI = null
window.boot = boot

Vue.use(Vuetify)

Vue.component('setup', () => import(/* webpackMode: "eager" */ './components/setup.vue'))

let bootstrap = () => {
  window.WIKI = new Vue({
    el: '#root',
    vuetify: new Vuetify()
  })
}

window.boot.onDOMReady(bootstrap)


================================================
FILE: client/components/admin/admin-analytics.vue
================================================
<template lang='pug'>
  v-container(fluid, grid-list-lg)
    v-layout(row, wrap)
      v-flex(xs12)
        .admin-header
          img.animated.fadeInUp(src='/_assets/svg/icon-line-chart.svg', alt='Analytics', style='width: 80px;')
          .admin-header-title
            .headline.primary--text.animated.fadeInLeft {{ $t('admin:analytics.title') }}
            .subtitle-1.grey--text.animated.fadeInLeft.wait-p4s {{ $t('admin:analytics.subtitle') }}
          v-spacer
          v-btn.animated.fadeInDown.wait-p2s.mr-3(icon, outlined, color='grey', @click='refresh')
            v-icon mdi-refresh
          v-btn.animated.fadeInDown(color='success', @click='save', depressed, large)
            v-icon(left) mdi-check
            span {{$t('common:actions.apply')}}

      v-flex(lg3, xs12)
        v-card.animated.fadeInUp
          v-toolbar(flat, color='primary', dark, dense)
            .subtitle-1 {{$t('admin:analytics.providers')}}
          v-list(two-line, dense).py-0
            template(v-for='(str, idx) in providers')
              v-list-item(:key='str.key', @click='selectedProvider = str.key', :disabled='!str.isAvailable')
                v-list-item-avatar(size='24')
                  v-icon(color='grey', v-if='!str.isAvailable') mdi-minus-box-outline
                  v-icon(color='primary', v-else-if='str.isEnabled', v-ripple, @click='str.isEnabled = false') mdi-checkbox-marked-outline
                  v-icon(color='grey', v-else, v-ripple, @click='str.isEnabled = true') mdi-checkbox-blank-outline
                v-list-item-content
                  v-list-item-title.body-2(:class='!str.isAvailable ? `grey--text` : (selectedProvider === str.key ? `primary--text` : ``)') {{ str.title }}
                  v-list-item-subtitle: .caption(:class='!str.isAvailable ? `grey--text text--lighten-1` : (selectedProvider === str.key ? `blue--text ` : ``)') {{ str.description }}
                v-list-item-avatar(v-if='selectedProvider === str.key', size='24')
                  v-icon.animated.fadeInLeft(color='primary', large) mdi-chevron-right
              v-divider(v-if='idx < providers.length - 1')

      v-flex(xs12, lg9)

        v-card.animated.fadeInUp.wait-p2s
          v-toolbar(color='primary', dense, flat, dark)
            .subtitle-1 {{provider.title}}
            v-spacer
            v-switch(
              dark
              color='blue lighten-5'
              label='Active'
              v-model='provider.isEnabled'
              hide-details
              inset
              )
          v-card-info(color='blue')
            div
              div {{provider.description}}
              span.caption: a(:href='provider.website') {{provider.website}}
            v-spacer
            .admin-providerlogo
              img(:src='provider.logo', :alt='provider.title')
          v-card-text
            v-form
              .overline.pb-5 {{$t('admin:analytics.providerConfiguration')}}
              .body-1.ml-3(v-if='!provider.config || provider.config.length < 1'): em {{$t('admin:analytics.providerNoConfiguration')}}
              template(v-else, v-for='cfg in provider.config')
                v-select(
                  v-if='cfg.value.type === "string" && cfg.value.enum'
                  outlined
                  :items='cfg.value.enum'
                  :key='cfg.key'
                  :label='cfg.value.title'
                  v-model='cfg.value.value'
                  prepend-icon='mdi-cog-box'
                  :hint='cfg.value.hint ? cfg.value.hint : ""'
                  persistent-hint
                  :class='cfg.value.hint ? "mb-2" : ""'
                )
                v-switch.mb-3(
                  v-else-if='cfg.value.type === "boolean"'
                  :key='cfg.key'
                  :label='cfg.value.title'
                  v-model='cfg.value.value'
                  color='primary'
                  prepend-icon='mdi-cog-box'
                  :hint='cfg.value.hint ? cfg.value.hint : ""'
                  persistent-hint
                  inset
                  )
                v-textarea(
                  v-else-if='cfg.value.type === "string" && cfg.value.multiline'
                  outlined
                  :key='cfg.key'
                  :label='cfg.value.title'
                  v-model='cfg.value.value'
                  prepend-icon='mdi-cog-box'
                  :hint='cfg.value.hint ? cfg.value.hint : ""'
                  persistent-hint
                  :class='cfg.value.hint ? "mb-2" : ""'
                  )
                v-text-field(
                  v-else
                  outlined
                  :key='cfg.key'
                  :label='cfg.value.title'
                  v-model='cfg.value.value'
                  prepend-icon='mdi-cog-box'
                  :hint='cfg.value.hint ? cfg.value.hint : ""'
                  persistent-hint
                  :class='cfg.value.hint ? "mb-2" : ""'
                  )

</template>

<script>
import _ from 'lodash'

import providersQuery from 'gql/admin/analytics/analytics-query-providers.gql'
import providersSaveMutation from 'gql/admin/analytics/analytics-mutation-save-providers.gql'

export default {
  data() {
    return {
      providers: [],
      selectedProvider: '',
      provider: {}
    }
  },
  watch: {
    selectedProvider(newValue, oldValue) {
      this.provider = _.find(this.providers, ['key', newValue]) || {}
    },
    providers(newValue, oldValue) {
      this.selectedProvider = 'google'
    }
  },
  methods: {
    async refresh() {
      await this.$apollo.queries.providers.refetch()
      this.$store.commit('showNotification', {
        message: this.$t('admin:analytics.refreshSuccess'),
        style: 'success',
        icon: 'cached'
      })
    },
    async save() {
      this.$store.commit(`loadingStart`, 'admin-analytics-saveproviders')
      try {
        await this.$apollo.mutate({
          mutation: providersSaveMutation,
          variables: {
            providers: this.providers.map(str => _.pick(str, [
              'isEnabled',
              'key',
              'config'
            ])).map(str => ({...str, config: str.config.map(cfg => ({...cfg, value: JSON.stringify({ v: cfg.value.value })}))}))
          }
        })
        this.$store.commit('showNotification', {
          message: this.$t('admin:analytics.saveSuccess'),
          style: 'success',
          icon: 'check'
        })
      } catch (err) {
        this.$store.commit('pushGraphError', err)
      }
      this.$store.commit(`loadingStop`, 'admin-analytics-saveproviders')
    }
  },
  apollo: {
    providers: {
      query: providersQuery,
      fetchPolicy: 'network-only',
      update: (data) => _.cloneDeep(data.analytics.providers).map(str => ({
        ...str,
        config: _.sortBy(str.config.map(cfg => ({
          ...cfg,
          value: JSON.parse(cfg.value)
        })), [t => t.value.order])
      })),
      watchLoading (isLoading) {
        this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-analytics-refresh')
      }
    }
  }
}
</script>


================================================
FILE: client/components/admin/admin-api-create.vue
================================================
<template lang="pug">
  div
    v-dialog(v-model='isShown', max-width='650', persistent)
      v-card
        .dialog-header.is-short
          v-icon.mr-3(color='white') mdi-plus
          span {{$t('admin:api.newKeyTitle')}}
        v-card-text.pt-5
          v-text-field(
            outlined
            prepend-icon='mdi-format-title'
            v-model='name'
            :label='$t(`admin:api.newKeyName`)'
            persistent-hint
            ref='keyNameInput'
            :hint='$t(`admin:api.newKeyNameHint`)'
            counter='255'
            )
          v-select.mt-3(
            :items='expirations'
            outlined
            prepend-icon='mdi-clock'
            v-model='expiration'
            :label='$t(`admin:api.newKeyExpiration`)'
            :hint='$t(`admin:api.newKeyExpirationHint`)'
            persistent-hint
            )
          v-divider.mt-4
          v-subheader.pl-2: strong.indigo--text {{$t('admin:api.newKeyPermissionScopes')}}
          v-list.pl-8(nav)
            v-list-item-group(v-model='fullAccess')
              v-list-item(
                :value='true'
                active-class='indigo--text'
                )
                template(v-slot:default='{ active, toggle }')
                  v-list-item-action
                    v-checkbox(
                      :input-value='active'
                      :true-value='true'
                      color='indigo'
                      @click='toggle'
                    )
                  v-list-item-content
                    v-list-item-title {{$t('admin:api.newKeyFullAccess')}}
            v-divider.mt-3
            v-subheader.caption.indigo--text {{$t('admin:api.newKeyGroupPermissions')}}
            v-list-item
              v-select(
                :disabled='fullAccess'
                :items='groups'
                item-text='name'
                item-value='id'
                outlined
                color='indigo'
                v-model='group'
                :label='$t(`admin:api.newKeyGroup`)'
                :hint='$t(`admin:api.newKeyGroupHint`)'
                persistent-hint
                )
        v-card-chin
          v-spacer
          v-btn(text, @click='isShown = false', :disabled='loading') {{$t('common:actions.cancel')}}
          v-btn.px-3(depressed, color='primary', @click='generate', :loading='loading')
            v-icon(left) mdi-chevron-right
            span {{$t('common:actions.generate')}}

    v-dialog(
      v-model='isCopyKeyDialogShown'
      max-width='750'
      persistent
      overlay-color='blue darken-5'
      overlay-opacity='.9'
      )
      v-card
        v-toolbar(dense, flat, color='primary', dark) {{$t('admin:api.newKeyTitle')}}
        v-card-text.pt-5
          .body-2.text-center
            i18next(tag='span', path='admin:api.newKeyCopyWarn')
              strong(place='bold') {{$t('admin:api.newKeyCopyWarnBold')}}
          v-textarea.mt-3(
            ref='keyContentsIpt'
            filled
            no-resize
            readonly
            v-model='key'
            :rows='10'
            hide-details
          )
        v-card-chin
          v-spacer
          v-btn.px-3(depressed, dark, color='primary', @click='isCopyKeyDialogShown = false') {{$t('common:actions.close')}}
</template>

<script>
import _ from 'lodash'
import gql from 'graphql-tag'

import groupsQuery from 'gql/admin/users/users-query-groups.gql'

export default {
  props: {
    value: {
      type: Boolean,
      default: false
    }
  },
  data() {
    return {
      loading: false,
      name: '',
      expiration: '1y',
      fullAccess: true,
      groups: [],
      group: null,
      isCopyKeyDialogShown: false,
      key: ''
    }
  },
  computed: {
    isShown: {
      get() { return this.value },
      set(val) { this.$emit('input', val) }
    },
    expirations() {
      return [
        { value: '30d', text: this.$t('admin:api.expiration30d') },
        { value: '90d', text: this.$t('admin:api.expiration90d') },
        { value: '180d', text: this.$t('admin:api.expiration180d') },
        { value: '1y', text: this.$t('admin:api.expiration1y') },
        { value: '3y', text: this.$t('admin:api.expiration3y') }
      ]
    }
  },
  watch: {
    value (newValue, oldValue) {
      if (newValue) {
        setTimeout(() => {
          this.$refs.keyNameInput.focus()
        }, 400)
      }
    }
  },
  methods: {
    async generate () {
      try {
        if (_.trim(this.name).length < 2 || this.name.length > 255) {
          throw new Error(this.$t('admin:api.newKeyNameError'))
        } else if (!this.fullAccess && !this.group) {
          throw new Error(this.$t('admin:api.newKeyGroupError'))
        } else if (!this.fullAccess && this.group === 2) {
          throw new Error(this.$t('admin:api.newKeyGuestGroupError'))
        }
      } catch (err) {
        return this.$store.commit('showNotification', {
          style: 'red',
          message: err,
          icon: 'alert'
        })
      }

      this.loading = true

      try {
        const resp = await this.$apollo.mutate({
          mutation: gql`
            mutation ($name: String!, $expiration: String!, $fullAccess: Boolean!, $group: Int) {
              authentication {
                createApiKey (name: $name, expiration: $expiration, fullAccess: $fullAccess, group: $group) {
                  key
                  responseResult {
                    succeeded
                    errorCode
                    slug
                    message
                  }
                }
              }
            }
          `,
          variables: {
            name: this.name,
            expiration: this.expiration,
            fullAccess: (this.fullAccess === true),
            group: this.group
          },
          watchLoading (isLoading) {
            this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-api-create')
          }
        })
        if (_.get(resp, 'data.authentication.createApiKey.responseResult.succeeded', false)) {
          this.$store.commit('showNotification', {
            style: 'success',
            message: this.$t('admin:api.newKeySuccess'),
            icon: 'check'
          })

          this.name = ''
          this.expiration = '1y'
          this.fullAccess = true
          this.group = null
          this.isShown = false
          this.$emit('refresh')

          this.key = _.get(resp, 'data.authentication.createApiKey.key', '???')
          this.isCopyKeyDialogShown = true

          setTimeout(() => {
            this.$refs.keyContentsIpt.$refs.input.select()
          }, 400)
        } else {
          this.$store.commit('showNotification', {
            style: 'red',
            message: _.get(resp, 'data.authentication.createApiKey.responseResult.message', 'An unexpected error occurred.'),
            icon: 'alert'
          })
        }
      } catch (err) {
        this.$store.commit('pushGraphError', err)
      }

      this.loading = false
    }
  },
  apollo: {
    groups: {
      query: groupsQuery,
      fetchPolicy: 'network-only',
      update: (data) => data.groups.list,
      watchLoading (isLoading) {
        this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-api-groups-refresh')
      }
    }
  }
}
</script>


================================================
FILE: client/components/admin/admin-api.vue
================================================
<template lang='pug'>
  v-container(fluid, grid-list-lg)
    v-layout(row, wrap)
      v-flex(xs12)
        .admin-header
          img.animated.fadeInUp(src='/_assets/svg/icon-rest-api.svg', alt='API', style='width: 80px;')
          .admin-header-title
            .headline.primary--text.animated.fadeInLeft {{$t('admin:api.title')}}
            .subtitle-1.grey--text.animated.fadeInLeft {{$t('admin:api.subtitle')}}
          v-spacer
          template(v-if='enabled')
            status-indicator.mr-3(positive, pulse)
            .caption.green--text.animated.fadeInLeft {{$t('admin:api.enabled')}}
          template(v-else)
            status-indicator.mr-3(negative, pulse)
            .caption.red--text.animated.fadeInLeft {{$t('admin:api.disabled')}}
          v-spacer
          v-btn.mr-3.animated.fadeInDown.wait-p2s(outlined, color='grey', icon, @click='refresh')
            v-icon mdi-refresh
          v-btn.mr-3.animated.fadeInDown.wait-p1s(:color='enabled ? `red` : `green`', depressed, @click='globalSwitch', dark, :loading='isToggleLoading')
            v-icon(left) mdi-power
            span(v-if='!enabled') {{$t('admin:api.enableButton')}}
            span(v-else) {{$t('admin:api.disableButton')}}
          v-btn.animated.fadeInDown(color='primary', depressed, large, @click='newKey', dark)
            v-icon(left) mdi-plus
            span {{$t('admin:api.newKeyButton')}}
        v-card.mt-3.animated.fadeInUp
          v-simple-table(v-if='keys && keys.length > 0')
            template(v-slot:default)
              thead
                tr.grey(:class='$vuetify.theme.dark ? `darken-4-d5` : `lighten-5`')
                  th {{$t('admin:api.headerName')}}
                  th {{$t('admin:api.headerKeyEnding')}}
                  th {{$t('admin:api.headerExpiration')}}
                  th {{$t('admin:api.headerCreated')}}
                  th {{$t('admin:api.headerLastUpdated')}}
                  th(width='100') {{$t('admin:api.headerRevoke')}}
              tbody
                tr(v-for='key of keys', :key='`key-` + key.id')
                  td
                    strong(:class='key.isRevoked ? `red--text` : ``') {{ key.name }}
                    em.caption.ml-1.red--text(v-if='key.isRevoked') (revoked)
                  td.caption {{ key.keyShort }}
                  td(:style='key.isRevoked ? `text-decoration: line-through;` : ``') {{ key.expiration | moment('LL') }}
                  td {{ key.createdAt | moment('calendar') }}
                  td {{ key.updatedAt | moment('calendar') }}
                  td: v-btn(icon, @click='revoke(key)', :disabled='key.isRevoked'): v-icon(color='error') mdi-cancel
          v-card-text(v-else)
            v-alert.mb-0(icon='mdi-information', :value='true', outlined, color='info') {{$t('admin:api.noKeyInfo')}}

    create-api-key(v-model='isCreateDialogShown', @refresh='refresh(false)')

    v-dialog(v-model='isRevokeConfirmDialogShown', max-width='500', persistent)
      v-card
        .dialog-header.is-red {{$t('admin:api.revokeConfirm')}}
        v-card-text.pa-4
          i18next(tag='span', path='admin:api.revokeConfirmText')
            strong(place='name') {{ current.name }}
        v-card-actions
          v-spacer
          v-btn(text, @click='isRevokeConfirmDialogShown = false', :disabled='revokeLoading') {{$t('common:actions.cancel')}}
          v-btn(color='red', dark, @click='revokeConfirm', :loading='revokeLoading') {{$t('admin:api.revoke')}}
</template>

<script>
import _ from 'lodash'
import gql from 'graphql-tag'
import { StatusIndicator } from 'vue-status-indicator'

import CreateApiKey from './admin-api-create.vue'

export default {
  components: {
    StatusIndicator,
    CreateApiKey
  },
  data() {
    return {
      enabled: false,
      isToggleLoading: false,
      keys: [],
      isCreateDialogShown: false,
      isRevokeConfirmDialogShown: false,
      revokeLoading: false,
      current: {}
    }
  },
  methods: {
    async refresh (notify = true) {
      this.$apollo.queries.keys.refetch()
      if (notify) {
        this.$store.commit('showNotification', {
          message: this.$t('admin:api.refreshSuccess'),
          style: 'success',
          icon: 'cached'
        })
      }
    },
    async globalSwitch () {
      this.isToggleLoading = true
      try {
        const resp = await this.$apollo.mutate({
          mutation: gql`
            mutation ($enabled: Boolean!) {
              authentication {
                setApiState (enabled: $enabled) {
                  responseResult {
                    succeeded
                    errorCode
                    slug
                    message
                  }
                }
              }
            }
          `,
          variables: {
            enabled: !this.enabled
          },
          watchLoading (isLoading) {
            this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-api-toggle')
          }
        })
        if (_.get(resp, 'data.authentication.setApiState.responseResult.succeeded', false)) {
          this.$store.commit('showNotification', {
            style: 'success',
            message: this.enabled ? this.$t('admin:api.toggleStateDisabledSuccess') : this.$t('admin:api.toggleStateEnabledSuccess'),
            icon: 'check'
          })
          await this.$apollo.queries.enabled.refetch()
        } else {
          this.$store.commit('showNotification', {
            style: 'red',
            message: _.get(resp, 'data.authentication.setApiState.responseResult.message', 'An unexpected error occurred.'),
            icon: 'alert'
          })
        }
      } catch (err) {
        this.$store.commit('pushGraphError', err)
      }
      this.isToggleLoading = false
    },
    async newKey () {
      this.isCreateDialogShown = true
    },
    revoke (key) {
      this.current = key
      this.isRevokeConfirmDialogShown = true
    },
    async revokeConfirm () {
      this.revokeLoading = true
      try {
        const resp = await this.$apollo.mutate({
          mutation: gql`
            mutation ($id: Int!) {
              authentication {
                revokeApiKey (id: $id) {
                  responseResult {
                    succeeded
                    errorCode
                    slug
                    message
                  }
                }
              }
            }
          `,
          variables: {
            id: this.current.id
          },
          watchLoading (isLoading) {
            this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-api-revoke')
          }
        })
        if (_.get(resp, 'data.authentication.revokeApiKey.responseResult.succeeded', false)) {
          this.$store.commit('showNotification', {
            style: 'success',
            message: this.$t('admin:api.revokeSuccess'),
            icon: 'check'
          })
          this.refresh(false)
        } else {
          this.$store.commit('showNotification', {
            style: 'red',
            message: _.get(resp, 'data.authentication.revokeApiKey.responseResult.message', 'An unexpected error occurred.'),
            icon: 'alert'
          })
        }
      } catch (err) {
        this.$store.commit('pushGraphError', err)
      }
      this.isRevokeConfirmDialogShown = false
      this.revokeLoading = false
    }
  },
  apollo: {
    enabled: {
      query: gql`
        {
          authentication {
            apiState
          }
        }
      `,
      fetchPolicy: 'network-only',
      update: (data) => data.authentication.apiState,
      watchLoading (isLoading) {
        this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-api-state-refresh')
      }
    },
    keys: {
      query: gql`
        {
          authentication {
            apiKeys {
              id
              name
              keyShort
              expiration
              isRevoked
              createdAt
              updatedAt
            }
          }
        }
      `,
      fetchPolicy: 'network-only',
      update: (data) => data.authentication.apiKeys,
      watchLoading (isLoading) {
        this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-api-keys-refresh')
      }
    }
  }
}
</script>

<style lang='scss'>

</style>


================================================
FILE: client/components/admin/admin-auth.vue
================================================
<template lang='pug'>
  v-container(fluid, grid-list-lg)
    v-layout(row, wrap)
      v-flex(xs12)
        .admin-header
          img.animated.fadeInUp(src='/_assets/svg/icon-unlock.svg', alt='Authentication', style='width: 80px;')
          .admin-header-title
            .headline.primary--text.animated.fadeInLeft {{ $t('admin:auth.title') }}
            .subtitle-1.grey--text.animated.fadeInLeft.wait-p4s {{ $t('admin:auth.subtitle') }}
          v-spacer
          v-btn.animated.fadeInDown.wait-p3s(icon, outlined, color='grey', href='https://docs.requarks.io/auth', target='_blank')
            v-icon mdi-help-circle
          v-btn.animated.fadeInDown.wait-p2s.mx-3(icon, outlined, color='grey', @click='refresh')
            v-icon mdi-refresh
          v-btn.animated.fadeInDown(color='success', @click='save', depressed, large)
            v-icon(left) mdi-check
            span {{$t('common:actions.apply')}}

      v-flex(lg3, xs12)
        v-card.animated.fadeInUp
          v-toolbar(flat, color='teal', dark, dense)
            .subtitle-1 {{$t('admin:auth.activeStrategies')}}
          v-list(two-line, dense).py-0
            draggable(
              v-model='activeStrategies'
              handle='.is-handle'
              direction='vertical'
              )
              transition-group
                v-list-item(
                  v-for='(str, idx) in activeStrategies'
                  :key='str.key'
                  @click='selectedStrategy = str.key'
                  :class='selectedStrategy === str.key ? ($vuetify.theme.dark ? `grey darken-5` : `teal lighten-5`) : ``'
                  )
                  v-list-item-avatar.is-handle(size='24')
                    v-icon(:color='selectedStrategy === str.key ? `teal` : `grey`') mdi-drag-horizontal
                  v-list-item-content
                    v-list-item-title.body-2(:class='selectedStrategy === str.key ? `teal--text` : ``') {{ str.displayName }}
                    v-list-item-subtitle: .caption(:class='selectedStrategy === str.key ? `teal--text ` : ``') {{ str.strategy.title }}
                  v-list-item-avatar(v-if='selectedStrategy === str.key', size='24')
                    v-icon.animated.fadeInLeft(color='teal', large) mdi-chevron-right
          v-card-chin
            v-menu(offset-y, bottom, min-width='250px', max-width='550px', max-height='50vh', style='flex: 1 1;', center)
              template(v-slot:activator='{ on }')
                v-btn(v-on='on', color='primary', depressed, block)
                  v-icon(left) mdi-plus
                  span {{$t('admin:auth.addStrategy')}}
              v-list(dense)
                template(v-for='(str, idx) of strategies')
                  v-list-item(
                    :key='str.key'
                    :disabled='str.isDisabled'
                    @click='addStrategy(str)'
                    )
                    v-list-item-avatar(height='24', width='48', tile)
                      v-img(:src='str.logo', width='48px', height='24px', contain, :style='str.isDisabled ? `opacity: .25;` : ``')
                    v-list-item-content
                      v-list-item-title {{str.title}}
                      v-list-item-subtitle: .caption(:style='str.isDisabled ? `opacity: .4;` : ``') {{str.description}}
                  v-divider(v-if='idx < strategies.length - 1')

      v-flex(xs12, lg9)
        v-card.animated.fadeInUp.wait-p2s
          v-toolbar(color='primary', dense, flat, dark)
            .subtitle-1 {{strategy.displayName}} #[em ({{strategy.strategy.title}})]
            v-spacer
            v-btn(small, outlined, dark, color='white', :disabled='strategy.key === `local`', @click='deleteStrategy()')
              v-icon(left) mdi-close
              span {{$t('common:actions.delete')}}
          v-card-info(color='blue')
            div
              span {{strategy.strategy.description}}
              .caption: a(:href='strategy.strategy.website') {{strategy.strategy.website}}
            v-spacer
            .admin-providerlogo
              img(:src='strategy.strategy.logo', :alt='strategy.strategy.title')
          v-card-text
            .row
              .col-8
                v-text-field(
                  outlined
                  :label='$t(`admin:auth.displayName`)'
                  v-model='strategy.displayName'
                  prepend-icon='mdi-format-title'
                  :hint='$t(`admin:auth.displayNameHint`)'
                  persistent-hint
                  )
              .col-4
                v-switch.mt-1(
                  :label='$t(`admin:auth.strategyIsEnabled`)'
                  v-model='strategy.isEnabled'
                  color='primary'
                  prepend-icon='mdi-power'
                  :hint='$t(`admin:auth.strategyIsEnabledHint`)'
                  persistent-hint
                  inset
                  :disabled='strategy.key === `local`'
                  )
            template(v-if='strategy.config && Object.keys(strategy.config).length > 0')
              v-divider
              .overline.my-5 {{$t('admin:auth.strategyConfiguration')}}
              .pr-3
                template(v-for='cfg in strategy.config')
                  v-select.mb-3(
                    v-if='cfg.value.type === "string" && cfg.value.enum'
                    outlined
                    :items='cfg.value.enum'
                    :key='cfg.key'
                    :label='cfg.value.title'
                    v-model='cfg.value.value'
                    prepend-icon='mdi-cog-box'
                    :hint='cfg.value.hint ? cfg.value.hint : ""'
                    persistent-hint
                    :class='cfg.value.hint ? "mb-2" : ""'
                    :style='cfg.value.maxWidth > 0 ? `max-width:` + cfg.value.maxWidth + `px;` : ``'
                  )
                  v-switch.mb-6(
                    v-else-if='cfg.value.type === "boolean"'
                    :key='cfg.key'
                    :label='cfg.value.title'
                    v-model='cfg.value.value'
                    color='primary'
                    prepend-icon='mdi-cog-box'
                    :hint='cfg.value.hint ? cfg.value.hint : ""'
                    persistent-hint
                    inset
                    )
                  v-textarea.mb-3(
                    v-else-if='cfg.value.type === "string" && cfg.value.multiline'
                    outlined
                    :key='cfg.key'
                    :label='cfg.value.title'
                    v-model='cfg.value.value'
                    prepend-icon='mdi-cog-box'
                    :hint='cfg.value.hint ? cfg.value.hint : ""'
                    persistent-hint
                    :class='cfg.value.hint ? "mb-2" : ""'
                    )
                  v-text-field.mb-3(
                    v-else
                    outlined
                    :key='cfg.key'
                    :label='cfg.value.title'
                    v-model='cfg.value.value'
                    prepend-icon='mdi-cog-box'
                    :hint='cfg.value.hint ? cfg.value.hint : ""'
                    persistent-hint
                    :class='cfg.value.hint ? "mb-2" : ""'
                    :style='cfg.value.maxWidth > 0 ? `max-width:` + cfg.value.maxWidth + `px;` : ``'
                    )
            v-divider
            .overline.my-5 {{$t('admin:auth.registration')}}
            .pr-3
              v-switch.ml-3(
                v-model='strategy.selfRegistration'
                :label='$t(`admin:auth.selfRegistration`)'
                color='primary'
                :hint='$t(`admin:auth.selfRegistrationHint`)'
                persistent-hint
                inset
              )
              v-combobox.ml-3.mt-5(
                :label='$t(`admin:auth.domainsWhitelist`)'
                v-model='strategy.domainWhitelist'
                prepend-icon='mdi-email-check-outline'
                outlined
                :disabled='!strategy.selfRegistration'
                :hint='$t(`admin:auth.domainsWhitelistHint`)'
                persistent-hint
                small-chips
                deletable-chips
                clearable
                multiple
                chips
                )
              v-autocomplete.mt-3.ml-3(
                outlined
                :disabled='!strategy.selfRegistration'
                :items='groups'
                item-text='name'
                item-value='id'
                :label='$t(`admin:auth.autoEnrollGroups`)'
                v-model='strategy.autoEnrollGroups'
                prepend-icon='mdi-account-group'
                :hint='$t(`admin:auth.autoEnrollGroupsHint`)'
                small-chips
                persistent-hint
                deletable-chips
                clearable
                multiple
                chips
                )

        v-card.mt-4.wiki-form.animated.fadeInUp.wait-p4s(v-if='selectedStrategy !== `local`')
          v-toolbar(color='primary', dense, flat, dark)
            .subtitle-1 {{$t('admin:auth.configReference')}}
          v-card-text
            .body-2 {{$t('admin:auth.configReferenceSubtitle')}}
            v-alert.mt-3.radius-7(v-if='host.length < 8', color='red', outlined, :value='true', icon='mdi-alert')
              i18next(path='admin:auth.siteUrlNotSetup', tag='span')
                strong(place='siteUrl') {{$t('admin:general.siteUrl')}}
                strong(place='general') {{$t('admin:general.title')}}
            .pa-3.mt-3.radius-7.grey(v-else, :class='$vuetify.theme.dark ? `darken-3-d5` : `lighten-3`')
              .body-2: strong {{$t('admin:auth.allowedWebOrigins')}}
              .body-2 {{host}}
              v-divider.my-3
              .body-2: strong {{$t('admin:auth.callbackUrl')}}
              .body-2 {{host}}/login/{{strategy.key}}/callback
              v-divider.my-3
              .body-2: strong {{$t('admin:auth.loginUrl')}}
              .body-2 {{host}}/login
              v-divider.my-3
              .body-2: strong {{$t('admin:auth.logoutUrl')}}
              .body-2 {{host}}
              v-divider.my-3
              .body-2: strong {{$t('admin:auth.tokenEndpointAuthMethod')}}
              .body-2 HTTP-POST
</template>

<script>
import _ from 'lodash'
import gql from 'graphql-tag'
import { v4 as uuid } from 'uuid'

import groupsQuery from 'gql/admin/auth/auth-query-groups.gql'
import hostQuery from 'gql/admin/auth/auth-query-host.gql'

import draggable from 'vuedraggable'

export default {
  components: {
    draggable
  },
  filters: {
    startCase(val) { return _.startCase(val) }
  },
  data() {
    return {
      groups: [],
      strategies: [],
      activeStrategies: [],
      selectedStrategy: '',
      host: '',
      strategy: {
        strategy: {}
      }
    }
  },
  watch: {
    selectedStrategy(newValue, oldValue) {
      this.strategy = _.find(this.activeStrategies, ['key', newValue]) || {}
    },
    activeStrategies(newValue, oldValue) {
      this.selectedStrategy = 'local'
    }
  },
  methods: {
    async refresh() {
      await this.$apollo.queries.strategies.refetch()
      await this.$apollo.queries.activeStrategies.refetch()
      this.$store.commit('showNotification', {
        message: this.$t('admin:auth.refreshSuccess'),
        style: 'success',
        icon: 'cached'
      })
    },
    addStrategy (str) {
      const newStr = {
        key: uuid(),
        strategy: str,
        config: str.props.map(c => ({
          key: c.key,
          value: {
            ...c,
            value: c.default
          }
        })),
        order: this.activeStrategies.length,
        isEnabled: true,
        displayName: str.title,
        selfRegistration: false,
        domainWhitelist: [],
        autoEnrollGroups: []
      }
      this.activeStrategies = [...this.activeStrategies, newStr]
      this.$nextTick(() => {
        this.selectedStrategy = newStr.key
      })
    },
    deleteStrategy () {
      this.activeStrategies = _.reject(this.activeStrategies, ['key', this.strategy.key])
    },
    async save() {
      this.$store.commit(`loadingStart`, 'admin-auth-savestrategies')
      try {
        const resp = await this.$apollo.mutate({
          mutation: gql`
            mutation($strategies: [AuthenticationStrategyInput]!) {
              authentication {
                updateStrategies(strategies: $strategies) {
                  responseResult {
                    succeeded
                    errorCode
                    slug
                    message
                  }
                }
              }
            }
          `,
          variables: {
            strategies: this.activeStrategies.map((str, idx) => ({
              key: str.key,
              strategyKey: str.strategy.key,
              displayName: str.displayName,
              order: idx,
              isEnabled: str.isEnabled,
              config: str.config.map(cfg => ({...cfg, value: JSON.stringify({ v: cfg.value.value })})),
              selfRegistration: str.selfRegistration,
              domainWhitelist: str.domainWhitelist,
              autoEnrollGroups: str.autoEnrollGroups
            }))
          }
        })
        if (_.get(resp, 'data.authentication.updateStrategies.responseResult.succeeded', false)) {
          this.$store.commit('showNotification', {
            message: this.$t('admin:auth.saveSuccess'),
            style: 'success',
            icon: 'check'
          })
        } else {
          throw new Error(_.get(resp, 'data.authentication.updateStrategies.responseResult.message', this.$t('common:error.unexpected')))
        }
      } catch (err) {
        this.$store.commit('pushGraphError', err)
      }
      this.$store.commit(`loadingStop`, 'admin-auth-savestrategies')
    }
  },
  apollo: {
    strategies: {
      query: gql`
        query {
          authentication {
            strategies {
              key
              title
              description
              isAvailable
              useForm
              logo
              website
              props {
                key
                value
              }
            }
          }
        }
      `,
      fetchPolicy: 'network-only',
      update: (data) => _.get(data, 'authentication.strategies', []).map(str => ({
        ...str,
        isDisabled: !str.isAvailable || str.key === `local`,
        props: _.sortBy(str.props.map(cfg => ({
          key: cfg.key,
          ...JSON.parse(cfg.value)
        })), [t => t.order])
      })),
      watchLoading (isLoading) {
        this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-auth-strategies-refresh')
      }
    },
    activeStrategies: {
      query: gql`
        query {
          authentication {
            activeStrategies {
              key
              strategy {
                key
                title
                description
                useForm
                logo
                website
              }
              config {
                key
                value
              }
              order
              isEnabled
              displayName
              selfRegistration
              domainWhitelist
              autoEnrollGroups
            }
          }
        }
      `,
      fetchPolicy: 'network-only',
      update: (data) => _.sortBy(_.get(data, 'authentication.activeStrategies', []).map(str => ({
        ...str,
        config: _.sortBy(str.config.map(cfg => ({
          ...cfg,
          value: JSON.parse(cfg.value)
        })), [t => t.value.order])
      })), ['order']),
      watchLoading (isLoading) {
        this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-auth-activestrategies-refresh')
      }
    },
    groups: {
      query: groupsQuery,
      fetchPolicy: 'network-only',
      update: (data) => data.groups.list,
      watchLoading (isLoading) {
        this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-auth-groups-refresh')
      }
    },
    host: {
      query: hostQuery,
      fetchPolicy: 'network-only',
      update: (data) => _.cloneDeep(data.site.config.host),
      watchLoading (isLoading) {
        this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-auth-host-refresh')
      }
    }
  }
}
</script>


================================================
FILE: client/components/admin/admin-comments.vue
================================================
<template lang='pug'>
  v-container(fluid, grid-list-lg)
    v-layout(row, wrap)
      v-flex(xs12)
        .admin-header
          img.animated.fadeInUp(src='/_assets/svg/icon-chat-bubble.svg', alt='Comments', style='width: 80px;')
          .admin-header-title
            .headline.primary--text.animated.fadeInLeft {{$t('admin:comments.title')}}
            .subtitle-1.grey--text.animated.fadeInLeft.wait-p2s {{$t('admin:comments.subtitle')}}
          v-spacer
          v-btn.animated.fadeInDown.wait-p3s(icon, outlined, color='grey', href='https://docs.requarks.io/comments', target='_blank')
            v-icon mdi-help-circle
          v-btn.mx-3.animated.fadeInDown.wait-p2s(icon, outlined, color='grey', @click='refresh')
            v-icon mdi-refresh
          v-btn.animated.fadeInDown(color='success', @click='save', depressed, large)
            v-icon(left) mdi-check
            span {{$t('common:actions.apply')}}

      v-flex(lg3, xs12)
        v-card.animated.fadeInUp
          v-toolbar(flat, color='primary', dark, dense)
            .subtitle-1 {{$t('admin:comments.provider')}}
          v-list.py-0(two-line, dense)
            template(v-for='(provider, idx) in providers')
              v-list-item(:key='provider.key', @click='selectedProvider = provider.key', :disabled='!provider.isAvailable')
                v-list-item-avatar(size='24')
                  v-icon(color='grey', v-if='!provider.isAvailable') mdi-minus-box-outline
                  v-icon(color='primary', v-else-if='provider.key === selectedProvider') mdi-checkbox-marked-circle-outline
                  v-icon(color='grey', v-else) mdi-checkbox-blank-circle-outline
                v-list-item-content
                  v-list-item-title.body-2(:class='!provider.isAvailable ? `grey--text` : (selectedProvider === provider.key ? `primary--text` : ``)') {{ provider.title }}
                  v-list-item-subtitle: .caption(:class='!provider.isAvailable ? `grey--text text--lighten-1` : (selectedProvider === provider.key ? `blue--text ` : ``)') {{ provider.description }}
                v-list-item-avatar(v-if='selectedProvider === provider.key', size='24')
                  v-icon.animated.fadeInLeft(color='primary', large) mdi-chevron-right
              v-divider(v-if='idx < providers.length - 1')

      v-flex(lg9, xs12)
        v-card.animated.fadeInUp.wait-p2s
          v-toolbar(color='primary', dense, flat, dark)
            .subtitle-1 {{provider.title}}
          v-card-info(color='blue')
            div
              div {{provider.description}}
              span.caption: a(:href='provider.website') {{provider.website}}
            v-spacer
            .admin-providerlogo
              img(:src='provider.logo', :alt='provider.title')
          v-card-text
            .overline.my-5 {{$t('admin:comments.providerConfig')}}
            .body-2.ml-3(v-if='!provider.config || provider.config.length < 1'): em {{$t('admin:comments.providerNoConfig')}}
            template(v-else, v-for='cfg in provider.config')
              v-select.mb-3(
                v-if='cfg.value.type === "string" && cfg.value.enum'
                outlined
                :items='cfg.value.enum'
                :key='cfg.key'
                :label='cfg.value.title'
                v-model='cfg.value.value'
                prepend-icon='mdi-cog-box'
                :hint='cfg.value.hint ? cfg.value.hint : ""'
                persistent-hint
                :class='cfg.value.hint ? "mb-2" : ""'
                :style='cfg.value.maxWidth > 0 ? `max-width:` + cfg.value.maxWidth + `px;` : ``'
              )
              v-switch.mb-6(
                v-else-if='cfg.value.type === "boolean"'
                :key='cfg.key'
                :label='cfg.value.title'
                v-model='cfg.value.value'
                color='primary'
                prepend-icon='mdi-cog-box'
                :hint='cfg.value.hint ? cfg.value.hint : ""'
                persistent-hint
                inset
                )
              v-textarea.mb-3(
                v-else-if='cfg.value.type === "string" && cfg.value.multiline'
                outlined
                :key='cfg.key'
                :label='cfg.value.title'
                v-model='cfg.value.value'
                prepend-icon='mdi-cog-box'
                :hint='cfg.value.hint ? cfg.value.hint : ""'
                persistent-hint
                :class='cfg.value.hint ? "mb-2" : ""'
                )
              v-text-field.mb-3(
                v-else
                outlined
                :key='cfg.key'
                :label='cfg.value.title'
                v-model='cfg.value.value'
                prepend-icon='mdi-cog-box'
                :hint='cfg.value.hint ? cfg.value.hint : ""'
                persistent-hint
                :class='cfg.value.hint ? "mb-2" : ""'
                :style='cfg.value.maxWidth > 0 ? `max-width:` + cfg.value.maxWidth + `px;` : ``'
                )
</template>

<script>
import _ from 'lodash'
import gql from 'graphql-tag'

export default {
  data() {
    return {
      providers: [],
      selectedProvider: '',
      provider: {}
    }
  },
  watch: {
    selectedProvider(newValue, oldValue) {
      this.provider = _.find(this.providers, ['key', newValue]) || {}
    },
    providers(newValue, oldValue) {
      this.selectedProvider = _.get(_.find(this.providers, 'isEnabled'), 'key', 'db')
    }
  },
  methods: {
    async refresh() {
      await this.$apollo.queries.providers.refetch()
      this.$store.commit('showNotification', {
        message: this.$t('admin:comments.listRefreshSuccess'),
        style: 'success',
        icon: 'cached'
      })
    },
    async save() {
      this.$store.commit(`loadingStart`, 'admin-comments-saveproviders')
      try {
        const resp = await this.$apollo.mutate({
          mutation: gql`
            mutation($providers: [CommentProviderInput]!) {
              comments {
                updateProviders(providers: $providers) {
                  responseResult {
                    succeeded
                    errorCode
                    slug
                    message
                  }
                }
              }
            }
          `,
          variables: {
            providers: this.providers.map(tgt => ({
              isEnabled: tgt.key === this.selectedProvider,
              key: tgt.key,
              config: tgt.config.map(cfg => ({...cfg, value: JSON.stringify({ v: cfg.value.value })}))
            }))
          }
        })
        if (_.get(resp, 'data.comments.updateProviders.responseResult.succeeded', false)) {
          this.$store.commit('showNotification', {
            message: this.$t('admin:comments.configSaveSuccess'),
            style: 'success',
            icon: 'check'
          })
        } else {
          throw new Error(_.get(resp, 'data.comments.updateProviders.responseResult.message', this.$t('common:error.unexpected')))
        }
      } catch (err) {
        this.$store.commit('pushGraphError', err)
      }
      this.$store.commit(`loadingStop`, 'admin-comments-saveproviders')
    }
  },
  apollo: {
    providers: {
      query: gql`
        query {
          comments {
            providers {
              isEnabled
              key
              title
              description
              logo
              website
              isAvailable
              config {
                key
                value
              }
            }
          }
        }
      `,
      fetchPolicy: 'network-only',
      update: (data) => _.cloneDeep(data.comments.providers).map(str => ({
        ...str,
        config: _.sortBy(str.config.map(cfg => ({
          ...cfg,
          value: JSON.parse(cfg.value)
        })), [t => t.value.order])
      })),
      watchLoading (isLoading) {
        this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-comments-refresh')
      }
    }
  }
}
</script>


================================================
FILE: client/components/admin/admin-contribute.vue
================================================
<template lang='pug'>
  v-container.admin-contribute(fluid, grid-list-lg)
    v-layout(row, wrap)
      v-flex(xs12)
        .admin-header
          img.animated.fadeInUp(src='/_assets/svg/icon-heart-health.svg', alt='Contribute', style='width: 80px;')
          .admin-header-title
            .headline.primary--text.animated.fadeInLeft {{ $t('admin:contribute.title') }}
            .subtitle-1.grey--text.animated.fadeInLeft.wait-p4s {{ $t('admin:contribute.subtitle') }}
        v-card.mt-3.animated.fadeInUp
          v-card-text
            i18next.body-2.pl-3(path='admin:contribute.openSource', tag='div')
              v-icon(color='red') mdi-heart
              a(href='https://requarks.io', target='_blank') requarks.io
              a(href='https://github.com/Requarks/wiki/graphs/contributors', target='_blank') {{ $t('admin:contribute.openSourceContributors') }}
            .body-2.pt-3.pl-3 {{ $t('admin:contribute.needYourHelp') }}
            v-divider.mt-3
            v-subheader.subtitle-2 {{ $t('admin:contribute.fundOurWork') }}
            v-tabs.mx-3.radius-7.admin-contribute-tabs(
              centered
              fixed-tabs
              background-color='primary'
              color='white'
              dark
              slider-color='#FFF'
              icons-and-text
              )
              v-tab
                span GitHub
                v-icon.my-1(size='24') mdi-github
              v-tab
                span Patreon
                img.my-1(src='/_assets/svg/icon-patreon.svg', style='height: 24px;')
              v-tab
                span OpenCollective
                img.my-1(src='/_assets/svg/icon-opencollective.svg', style='height: 24px;')
              v-tab
                span PayPal
                img.my-1(src='/_assets/svg/icon-paypal.svg', style='height: 24px;')
              v-tab
                span Ethereum
                img.my-1(src='/_assets/svg/icon-ethereum.svg', style='height: 24px;')
              v-tab
                span T-Shirts
                img.my-1(src='/_assets/svg/icon-t-shirt.svg', style='height: 24px;')
              v-tab-item(:transition='false', :reverse-transition='false')
                .body-2.pa-3 {{ $t('admin:contribute.github') }}
                a.ml-3(href='https://github.com/users/NGPixel/sponsorship', :title='$t(`admin:contribute.becomeASponsor`)')
                  img(src='/_assets/img/donate_github.svg', :alt='$t(`admin:contribute.becomeASponsor`)' style='width:200px;')
              v-tab-item(:transition='false', :reverse-transition='false')
                .body-2.pa-3 {{ $t('admin:contribute.patreon') }}
                a.ml-3(href='https://www.patreon.com/bePatron?u=16744039', :title='$t(`admin:contribute.becomeAPatron`)')
                  img(src='/_assets/img/donate_patreon.png', :alt='$t(`admin:contribute.becomeAPatron`)' style='width:200px;')
              v-tab-item(:transition='false', :reverse-transition='false')
                .body-2.pa-3 {{ $t('admin:contribute.openCollective') }}
                a.ml-3(href='https://opencollective.com/wikijs/donate', :title='$t(`admin:contribute.makeADonation`)')
                  img(src='/_assets/img/donate_opencollective.png', :alt='$t(`admin:contribute.makeADonation`)' style='width:300px;')
              v-tab-item(:transition='false', :reverse-transition='false')
                .body-2.pa-3 {{ $t('admin:contribute.paypal') }}
                .ml-3
                  form(action='https://www.paypal.com/cgi-bin/webscr', method='post', target='_top')
                    input(type='hidden', name='cmd', value='_s-xclick')
                    input(type='hidden', name='hosted_button_id', value='FLV5X255Z9CJU')
                    input(type='image', src='/_assets/img/donate_paypal.png', border='0', name='submit', title='PayPal - The safer, easier way to pay online!', alt='Donate with PayPal button')
                    img(alt='', border='0', src='https://www.paypal.com/en_CA/i/scr/pixel.gif', width='1', height='1')
              v-tab-item(:transition='false', :reverse-transition='false')
                .body-2.pa-3 {{ $t('admin:contribute.ethereum') }}
                .ml-3
                  .admin-contribute-ethaddress
                    strong Ethereum Address
                    span 0xE1d55C19aE86f6Bcbfb17e7f06aCe96BdBb22Cb5
                  div: img(src='/_assets/img/donate_eth_qr.png')
              v-tab-item(:transition='false', :reverse-transition='false')
                .body-2.pa-3 {{ $t('admin:contribute.tshirts') }}
                v-card-actions.ml-2
                  v-btn(outlined, :color='$vuetify.theme.dark ? `blue lighten-1` : `primary`', href='https://wikijs.threadless.com', large)
                    v-icon(left) mdi-tshirt-crew
                    span {{ $t('admin:contribute.shop') }}
            v-divider.mt-3
            v-subheader.subtitle-2  {{ $t('admin:contribute.contribute') }}
            .body-2.pl-3
              ul
                i18next(path='admin:contribute.submitAnIdea', tag='li')
                  a(href='https://requests.requarks.io/wiki', target='_blank') {{ $t('admin:contribute.submitAnIdeaLink') }}
                i18next(path='admin:contribute.foundABug', tag='li')
                  a(href='https://github.com/Requarks/wiki/issues', target='_blank') Github
                i18next(path='admin:contribute.helpTranslate', tag='li')
                  a(href='https://wiki.requarks.io/slack', target='_blank') Slack
            v-divider.mt-3
            v-subheader.subtitle-2  {{ $t('admin:contribute.spreadTheWord') }}
            .body-2.pl-3
              ul
                li {{ $t('admin:contribute.talkToFriends') }}
                i18next(path='admin:contribute.followUsOnTwitter', tag='li')
                  a(href='https://twitter.com/requarks', target='_blank') Twitter
          v-toolbar(color='indigo', dense, dark)
            .subtitle-1 Sponsors &amp; Backers
          v-container.pa-5.grey(fluid, :class='$vuetify.theme.dark ? `darken-3` : `lighten-4`')
            v-progress-circular(indeterminate, color='indigo', size='24', width='2', v-if='backers.length < 1')
            v-row(dense)
              v-col(cols='12', lg='6', xl='4', v-for='(backer, idx) in backers', :key='backer.id')
                v-card.grey(flat, :class='$vuetify.theme.dark ? `darken-4` : `lighten-2`')
                  v-list-item
                    v-list-item-avatar
                      img(v-if='backer.avatar', :src='backer.avatar')
                      v-avatar(v-else, color='blue-grey', size='40')
                        span.white--text.subtitle-1 {{backer.name[0].toUpperCase()}}
                    v-list-item-content
                      v-list-item-title {{backer.name}}
                      v-list-item-subtitle: .caption Since {{backer.joined | moment('MMMM DD, YYYY')}} on {{backer.source}}
                    v-list-item-action(v-if='backer.twitter')
                      v-btn(icon, :href='backer.twitter', target='_blank')
                        v-icon(color='grey') mdi-twitter
                    v-list-item-action(v-if='backer.website')
                      v-btn(icon, :href='backer.website', target='_blank')
                        v-icon(color='grey') mdi-earth
          v-toolbar(color='primary', dense, dark)
            .subtitle-1 Special Thanks
          v-list(two-line)
            v-list-item
              v-list-item-avatar
                img(src='https://static.requarks.io/logo/algolia.svg', alt='Algolia')
              v-list-item-content
                v-list-item-title Algolia
                v-list-item-subtitle Algolia is a powerful search-as-a-service solution, made easy to use with API clients, UI libraries, and pre-built integrations.
              v-list-item-action
                v-btn(icon, href='https://www.algolia.com/', target='_blank')
                  v-icon(color='grey') mdi-earth
            v-divider
            v-list-item
              v-list-item-avatar
                img(src='https://static.requarks.io/logo/browserstack.svg', alt='Browserstack')
              v-list-item-content
                v-list-item-title BrowserStack
                v-list-item-subtitle BrowserStack is a cloud web and mobile testing platform that enables developers to test their websites and mobile applications.
              v-list-item-action
                v-btn(icon, href='https://www.browserstack.com/', target='_blank')
                  v-icon(color='grey') mdi-earth
            v-divider
            v-list-item
              v-list-item-avatar
                img(src='https://static.requarks.io/logo/cloudflare.svg', alt='Cloudflare')
              v-list-item-content
                v-list-item-title Cloudflare
                v-list-item-subtitle Providing content delivery network services, DDoS mitigation, Internet security and distributed domain name server services.
              v-list-item-action
                v-btn(icon, href='https://www.cloudflare.com/', target='_blank')
                  v-icon(color='grey') mdi-earth
            v-divider
            v-list-item
              v-list-item-avatar
                img(src='https://static.requarks.io/logo/digitalocean.svg', alt='DigitalOcean')
              v-list-item-content
                v-list-item-title DigitalOcean
                v-list-item-subtitle Providing developers and businesses a reliable, easy-to-use cloud computing platform of virtual servers (Droplets), object storage (Spaces), and more.
              v-list-item-action
                v-btn(icon, href='https://m.do.co/c/5f7445bfa4d0', target='_blank')
                  v-icon(color='grey') mdi-earth
            v-divider
            v-list-item
              v-list-item-avatar(tile)
                img(src='/_assets/svg/logo-icons8.svg', alt='Icons8')
              v-list-item-content
                v-list-item-title Icons8
                v-list-item-subtitle All the Icons You Need. Guaranteed.
              v-list-item-action
                v-btn(icon, href='https://icons8.com', target='_blank')
                  v-icon(color='grey') mdi-earth
            v-divider
            v-list-item
              v-list-item-avatar(tile)
                img(src='https://static.requarks.io/logo/lokalise.png', alt='Lokalise')
              v-list-item-content
                v-list-item-title Lokalise
                v-list-item-subtitle Lokalise is a translation management system built for agile teams who want to automate their localization process.
              v-list-item-action
                v-btn(icon, href='https://lokalise.co', target='_blank')
                  v-icon(color='grey') mdi-earth
            v-divider
            v-list-item
              v-list-item-avatar(tile)
                img(src='https://static.requarks.io/logo/netlify.svg', alt='Netlify')
              v-list-item-content
                v-list-item-title Netlify
                v-list-item-subtitle Deploy modern static websites with Netlify. Get CDN, Continuous deployment, 1-click HTTPS, and all the services you need.
              v-list-item-action
                v-btn(icon, href='https://www.netlify.com', target='_blank')
                  v-icon(color='grey') mdi-earth

</template>

<script>
import gql from 'graphql-tag'

export default {
  data() {
    return {
      backers: []
    }
  },
  apollo: {
    backers: {
      query: gql`
        {
          contribute {
            contributors {
              id
              source
              name
              joined
              website
              twitter
              avatar
            }
          }
        }
      `,
      fetchPolicy: 'network-only',
      update: (data) => data.contribute.contributors,
      watchLoading (isLoading) {
        this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-contribute-refresh')
      }
    }
  }
}
</script>

<style lang='scss'>
.admin-contribute {

  &-tabs {
    .v-tabs__item img {
      height: 24px;
      margin-bottom: 5px;
    }
  }

  &-ethaddress {
    display: inline-block;
    margin-bottom: 12px;
    border-radius: 7px;
    background-color: mc('grey', '100');
    color: mc('grey', '700');
    padding: 12px;

    strong {
      display: block;
    }
  }

  ul {
    margin-left: 1rem;
    list-style-type: square;
  }
}
</style>


================================================
FILE: client/components/admin/admin-dashboard.vue
================================================
<template lang='pug'>
  v-container(fluid, grid-list-lg)
    v-layout(row, wrap)
      v-flex(xs12)
        .admin-header
          img.animated.fadeInUp(src='/_assets/svg/icon-browse-page.svg', alt='Dashboard', style='width: 80px;')
          .admin-header-title
            .headline.primary--text.animated.fadeInLeft {{ $t('admin:dashboard.title') }}
            .subtitle-1.grey--text.animated.fadeInLeft.wait-p2s {{ $t('admin:dashboard.subtitle') }}
      v-flex(xs12 md6 lg4 xl3 d-flex)
        v-card.primary.dashboard-card.animated.fadeInUp(dark)
          v-card-text
            v-icon.dashboard-icon mdi-file-document-outline
            .overline {{$t('admin:dashboard.pages')}}
            animated-number.display-1(
              :value='info.pagesTotal'
              :duration='2000'
              :formatValue='round'
              easing='easeOutQuint'
              )
      v-flex(xs12 md6 lg4 xl3 d-flex)
        v-card.blue.darken-3.dashboard-card.animated.fadeInUp.wait-p2s(dark)
          v-card-text
            v-icon.dashboard-icon mdi-account
            .overline {{$t('admin:dashboard.users')}}
            animated-number.display-1(
              :value='info.usersTotal'
              :duration='2000'
              :formatValue='round'
              easing='easeOutQuint'
              )
      v-flex(xs12 md6 lg4 xl3 d-flex)
        v-card.blue.darken-4.dashboard-card.animated.fadeInUp.wait-p4s(dark)
          v-card-text
            v-icon.dashboard-icon mdi-account-group
            .overline {{$t('admin:dashboard.groups')}}
            animated-number.display-1(
              :value='info.groupsTotal'
              :duration='2000'
              :formatValue='round'
              easing='easeOutQuint'
              )
      v-flex(xs12 md6 lg12 xl3 d-flex)
        v-card.dashboard-card.animated.fadeInUp.wait-p6s(
          :class='isLatestVersion ? "green" : "red lighten-2"'
          dark
          )
          v-btn.btn-animate-wrench(fab, absolute, :right='!$vuetify.rtl', :left='$vuetify.rtl', top, small, light, to='system', v-if='hasPermission(`manage:system`)')
            v-icon(:color='isLatestVersion ? `green` : `red darken-4`', small) mdi-wrench
          v-card-text
            v-icon.dashboard-icon mdi-blur
            .subtitle-1 Wiki.js {{info.currentVersion}}
            .body-2(v-if='isLatestVersion') {{$t('admin:dashboard.versionLatest')}}
            .body-2(v-else) {{$t('admin:dashboard.versionNew', { version: info.latestVersion })}}
      v-flex(xs12, xl6)
        v-card.radius-7.animated.fadeInUp.wait-p2s
          v-toolbar(:color='$vuetify.theme.dark ? `grey darken-2` : `grey lighten-5`', dense, flat)
            v-spacer
            .overline {{$t('admin:dashboard.recentPages')}}
            v-spacer
          v-data-table.pb-2(
            :items='recentPages'
            :headers='recentPagesHeaders'
            :loading='recentPagesLoading'
            hide-default-footer
            hide-default-header
            )
            template(slot='item', slot-scope='props')
              tr.is-clickable(:active='props.selected', @click='$router.push(`/pages/` + props.item.id)')
                td
                  .body-2: strong {{ props.item.title }}
                td.admin-pages-path
                  v-chip(label, small, :color='$vuetify.theme.dark ? `grey darken-4` : `grey lighten-4`') {{ props.item.locale }}
                  span.ml-2.grey--text(:class='$vuetify.theme.dark ? `text--lighten-1` : `text--darken-2`') / {{ props.item.path }}
                td.text-right.caption(width='250') {{ props.item.updatedAt | moment('calendar') }}
      v-flex(xs12, xl6)
        v-card.radius-7.animated.fadeInUp.wait-p4s
          v-toolbar(:color='$vuetify.theme.dark ? `grey darken-2` : `grey lighten-5`', dense, flat)
            v-spacer
            .overline {{$t('admin:dashboard.lastLogins')}}
            v-spacer
          v-data-table.pb-2(
            :items='lastLogins'
            :headers='lastLoginsHeaders'
            :loading='lastLoginsLoading'
            hide-default-footer
            hide-default-header
            )
            template(slot='item', slot-scope='props')
              tr.is-clickable(:active='props.selected', @click='$router.push(`/users/` + props.item.id)')
                td
                  .body-2: strong {{ props.item.name }}
                td.text-right.caption(width='250') {{ props.item.lastLoginAt | moment('calendar') }}

      v-flex(xs12)
        v-card.dashboard-contribute.animated.fadeInUp.wait-p4s
          v-card-text
            img(src='/_assets/svg/icon-heart-health.svg', alt='Contribute', style='height: 80px;')
            .pl-5
              .subtitle-1 {{$t('admin:contribute.title')}}
              .body-2.mt-3: strong {{$t('admin:dashboard.contributeSubtitle')}}
              .body-2 {{$t('admin:dashboard.contributeHelp')}}
              v-btn.mx-0.mt-4(:color='$vuetify.theme.dark ? `indigo lighten-3` : `indigo`', outlined, small, to='/contribute')
                .caption: strong {{$t('admin:dashboard.contributeLearnMore')}}

</template>

<script>
import _ from 'lodash'
import AnimatedNumber from 'animated-number-vue'
import { get } from 'vuex-pathify'
import gql from 'graphql-tag'
import semverLte from 'semver/functions/lte'

export default {
  components: {
    AnimatedNumber
  },
  data() {
    return {
      recentPages: [],
      recentPagesLoading: false,
      recentPagesHeaders: [
        { text: 'Title', value: 'title' },
        { text: 'Path', value: 'path' },
        { text: 'Last Updated', value: 'updatedAt', width: 250 }
      ],
      lastLogins: [],
      lastLoginsLoading: false,
      lastLoginsHeaders: [
        { text: 'User', value: 'displayName' },
        { text: 'Last Login', value: 'lastLoginAt', width: 250 }
      ]
    }
  },
  computed: {
    isLatestVersion() {
      if (this.info.latestVersion === 'n/a' || this.info.currentVersion === 'n/a') {
        return true
      } else {
        return semverLte(this.info.latestVersion, this.info.currentVersion)
      }
    },
    info: get('admin/info'),
    permissions: get('user/permissions')
  },
  methods: {
    round(val) { return Math.round(val) },
    hasPermission(prm) {
      if (_.isArray(prm)) {
        return _.some(prm, p => {
          return _.includes(this.permissions, p)
        })
      } else {
        return _.includes(this.permissions, prm)
      }
    }
  },
  apollo: {
    recentPages: {
      query: gql`
        query {
          pages {
            list(limit: 10, orderBy: UPDATED, orderByDirection: DESC) {
              id
              locale
              path
              title
              description
              contentType
              isPublished
              isPrivate
              privateNS
              createdAt
              updatedAt
            }
          }
        }
      `,
      update: (data) => data.pages.list,
      watchLoading (isLoading) {
        this.recentPagesLoading = isLoading
        this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-dashboard-recentpages')
      }
    },
    lastLogins: {
      query: gql`
        query {
          users {
            lastLogins {
              id
              name
              lastLoginAt
            }
          }
        }
      `,
      fetchPolicy: 'network-only',
      update: (data) => data.users.lastLogins,
      watchLoading (isLoading) {
        this.lastLoginsLoading = isLoading
        this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-dashboard-lastlogins')
      }
    }
  }
}
</script>

<style lang='scss'>

.dashboard-card {
  display: flex;
  width: 100%;
  border-radius: 7px;

  .v-card__text {
    overflow: hidden;
    position: relative;
  }
}

.dashboard-contribute {
  background-color: #FFF;
  background-image: linear-gradient(to bottom, #FFF 0%, lighten(mc('indigo', '50'), 3%) 100%);
  border-radius: 7px;

  @at-root .theme--dark & {
    background-color: mc('grey', '800');
    background-image: linear-gradient(to bottom, mc('grey', '800') 0%, darken(mc('grey', '800'), 6%) 100%);
  }

  .v-card__text {
    display: flex;
    align-items: center;
    color: mc('indigo', '500') !important;

    @at-root .theme--dark & {
      color: mc('grey', '300') !important;
    }
  }
}

.v-icon.dashboard-icon {
  position: absolute !important;
  right: 0;
  top: 12px;
  font-size: 100px !important;
  opacity: .25;

  @at-root .v-application--is-rtl & {
    left: 0;
    right: initial;
  }
}

</style>


================================================
FILE: client/components/admin/admin-dev-flags.vue
================================================
<template lang='pug'>
  v-container(fluid, grid-list-lg)
    v-layout(row, wrap)
      v-flex(xs12)
        .admin-header
          img(src='/_assets/svg/icon-console.svg', alt='Developer Tools', style='width: 80px;')
          .admin-header-title
            .headline.primary--text Developer Tools
            .subtitle-1.grey--text Flags
          v-spacer
          v-btn(color='success', depressed, @click='save', large)
            v-icon(left) mdi-check
            span {{$t('common:actions.apply')}}

        v-card.mt-3(:class='$vuetify.theme.dark ? `grey darken-3-d5` : `white grey--text text--darken-3`')
          v-alert(color='red', :value='true', icon='mdi-alert', dark, prominent)
            span Do NOT enable these flags unless you know what you're doing!
            .caption Doing so may result in data loss or broken installation!
          v-card-text
            v-switch.mt-3(
              color='primary'
              hint='Log detailed debug info on LDAP/AD login attempts.'
              persistent-hint
              label='LDAP Debug'
              v-model='flags.ldapdebug'
              inset
            )
            v-divider.mt-3
            v-switch.mt-3(
              color='red'
              hint='Log all queries made to the database to console.'
              persistent-hint
              label='SQL Query Logging'
              v-model='flags.sqllog'
              inset
            )
</template>

<script>
import _ from 'lodash'

import flagsQuery from 'gql/admin/dev/dev-query-flags.gql'
import flagsMutation from 'gql/admin/dev/dev-mutation-save-flags.gql'

export default {
  data() {
    return {
      flags: {
        sqllog: false
      }
    }
  },
  methods: {
    async save() {
      try {
        await this.$apollo.mutate({
          mutation: flagsMutation,
          variables: {
            flags: _.transform(this.flags, (result, value, key) => {
              result.push({ key, value })
            }, [])
          },
          watchLoading (isLoading) {
            this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-dev-flags-update')
          }
        })
        this.$store.commit('showNotification', {
          style: 'success',
          message: 'Flags applied successfully.',
          icon: 'check'
        })
      } catch (err) {
        this.$store.commit('pushGraphError', err)
      }
    }
  },
  apollo: {
    flags: {
      query: flagsQuery,
      fetchPolicy: 'network-only',
      update: (data) => _.transform(data.system.flags, (result, row) => {
        _.set(result, row.key, row.value)
      }, {}),
      watchLoading (isLoading) {
        this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-dev-flags-refresh')
      }
    }
  }
}
</script>

<style lang='scss'>

</style>


================================================
FILE: client/components/admin/admin-editor.vue
================================================
<template lang='pug'>
  v-container(fluid, grid-list-lg)
    v-layout(row, wrap)
      v-flex(xs12)
        .admin-header
          img(src='/_assets/svg/icon-web-design.svg', alt='Editor', style='width: 80px;')
          .admin-header-title
            .headline.primary--text Editor
            .subtitle-1.grey--text Configure the content editors #[v-chip(label, color='primary', small).white--text coming soon]
          v-spacer
          v-btn(outline, color='grey', @click='refresh', large)
            v-icon refresh
          v-btn(color='success', @click='save', depressed, large)
            v-icon(left) check
            span {{$t('common:actions.apply')}}

        v-card.mt-3
          v-tabs(color='grey darken-2', fixed-tabs, slider-color='white', show-arrows, dark)
            v-tab(key='settings'): v-icon settings
            v-tab(key='code') Markdown

            v-tab-item(key='settings', :transition='false', :reverse-transition='false')
              v-card.pa-3(flat, tile)
                .body-2.grey--text.text--darken-1 Select which editors to enable:
                .caption.grey--text.pb-2 Some editors require additional configuration in their dedicated tab (when selected).
                v-form
                  v-checkbox.my-0(
                    v-for='editor in editors'
                    v-model='editor.isEnabled'
                    :key='editor.key'
                    :label='editor.title'
                    color='primary'
                    disabled
                    hide-details
                  )
            v-tab-item(key='code', :transition='false', :reverse-transition='false')
              v-card.wiki-form.pa-3(flat, tile)
                v-form
                  v-subheader Editor Configuration
                  .body-1.ml-3 This editor has no configuration options you can modify.
</template>

<script>
export default {
  data() {
    return {
      editors: [
        { title: 'API Docs', key: 'api', isEnabled: false },
        { title: 'Code', key: 'code', isEnabled: true },
        { title: 'Markdown', key: 'markdown', isEnabled: true },
        { title: 'Tabular', key: 'tabular', isEnabled: false },
        { title: 'Visual Builder', key: 'visual', isEnabled: false },
        { title: 'WikiText', key: 'wikitext', isEnabled: false }
      ]
    }
  },
  methods: {
    save() {},
    refresh() {}
  }
}
</script>

<style lang='scss'>

</style>


================================================
FILE: client/components/admin/admin-extensions.vue
================================================
<template lang='pug'>
  v-container(fluid, grid-list-lg)
    v-layout(row wrap)
      v-flex(xs12)
        .admin-header
          img.animated.fadeInUp(src='/_assets/svg/icon-installing-updates.svg', alt='Extensions', style='width: 80px;')
          .admin-header-title
            .headline.primary--text.animated.fadeInLeft {{ $t('admin:extensions.title') }}
            .subtitle-1.grey--text.animated.fadeInLeft {{ $t('admin:extensions.subtitle') }}
        v-form.pt-3
          v-layout(row wrap)
            v-flex(xl6 lg8 xs12)
              v-alert.mb-4(outlined, color='error', icon='mdi-alert')
                span New extensions cannot be installed at the moment. This feature is coming in a future release.
              v-expansion-panels.admin-extensions-exp(hover, popout)
                v-expansion-panel(v-for='ext of extensions', :key='`ext-` + ext.key')
                  v-expansion-panel-header(disable-icon-rotate)
                    span {{ext.title}}
                    template(v-slot:actions)
                      v-chip(label, color='success', small, v-if='ext.isInstalled') Installed
                      v-chip(label, color='warning', small, v-else) Not Installed
                  v-expansion-panel-content.pa-0
                    v-card(flat, :class='$vuetify.theme.dark ? `grey darken-3` : `grey lighten-5`', tile)
                      v-card-text
                        .body-2 {{ext.description}}
                        v-divider.my-4
                        .body-2
                          strong.mr-2 This extension is
                          v-chip.mr-2(v-if='ext.isCompatible', label, outlined, small, color='success') compatible
                          v-chip.mr-2(v-else, label, small, color='error') not compatible
                          strong with your host.
                      v-card-chin
                        v-spacer
                        v-btn(disabled)
                          v-icon(left) mdi-plus
                          span Install
</template>

<script>
import _ from 'lodash'
import gql from 'graphql-tag'

export default {
  data() {
    return {
      extensions: []
    }
  },
  methods: {
    async save () {
      // try {
      //   await this.$apollo.mutate({
      //     mutation: gql`
      //       mutation (
      //         $host: String!
      //       ) {
      //         site {
      //           updateConfig(
      //             host: $host
      //           ) {
      //             responseResult {
      //               succeeded
      //               errorCode
      //               slug
      //               message
      //             }
      //           }
      //         }
      //       }
      //     `,
      //     variables: {
      //       host: _.get(this.config, 'host', '')
      //     },
      //     watchLoading (isLoading) {
      //       this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-extensions-update')
      //     }
      //   })
      //   this.$store.commit('showNotification', {
      //     style: 'success',
      //     message: 'Configuration saved successfully.',
      //     icon: 'check'
      //   })
      // } catch (err) {
      //   this.$store.commit('pushGraphError', err)
      // }
    }
  },
  apollo: {
    extensions: {
      query: gql`
        {
          system {
            extensions {
              key
              title
              description
              isInstalled
              isCompatible
            }
          }
        }
      `,
      fetchPolicy: 'network-only',
      update: (data) => _.cloneDeep(data.system.extensions),
      watchLoading (isLoading) {
        this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-extensions-refresh')
      }
    }
  }
}
</script>

<style lang='scss'>
.admin-extensions-exp {
  .v-expansion-panel-content__wrap {
    padding: 0;
  }
}
</style>


================================================
FILE: client/components/admin/admin-general.vue
================================================
<template lang='pug'>
  v-container(fluid, grid-list-lg)
    v-layout(row wrap)
      v-flex(xs12)
        .admin-header
          img.animated.fadeInUp(src='/_assets/svg/icon-categorize.svg', alt='General', style='width: 80px;')
          .admin-header-title
            .headline.primary--text.animated.fadeInLeft {{ $t('admin:general.title') }}
            .subtitle-1.grey--text.animated.fadeInLeft {{ $t('admin:general.subtitle') }}
          v-spacer
          v-btn.animated.fadeInDown(color='success', depressed, @click='save', large)
            v-icon(left) mdi-check
            span {{$t('common:actions.apply')}}
        v-form.pt-3
          v-layout(row wrap)
            v-flex(lg6 xs12)
              v-form
                v-card.animated.fadeInUp
                  v-toolbar(color='primary', dark, dense, flat)
                    v-toolbar-title.subtitle-1 {{ $t('admin:general.siteInfo') }}
                  .overline.grey--text.pa-4 {{$t('admin:general.general')}}
                  .px-3.pb-3
                    v-text-field(
                      outlined
                      :label='$t(`admin:general.siteUrl`)'
                      required
                      :counter='255'
                      v-model='config.host'
                      prepend-icon='mdi-label-variant-outline'
                      :hint='$t(`admin:general.siteUrlHint`)'
                      persistent-hint
                      )
                    v-text-field.mt-3(
                      outlined
                      :label='$t(`admin:general.siteTitle`)'
                      required
                      :counter='50'
                      v-model='config.title'
                      prepend-icon='mdi-earth'
                      :hint='$t(`admin:general.siteTitleHint`)'
                      persistent-hint
                      )
                  v-divider
                  .overline.grey--text.pa-4 {{$t('admin:general.logo')}}
                  .pt-2.pb-7.pl-10.pr-3
                    .d-flex.align-center
                      v-avatar(size='100', tile)
                        v-img(
                          :src='config.logoUrl'
                          lazy-src='data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNcWQ8AAdcBKrJda2oAAAAASUVORK5CYII='
                          aspect-ratio='1'
                          )
                      .ml-4(style='flex: 1 1 auto;')
                        v-text-field(
                          outlined
                          :label='$t(`admin:general.logoUrl`)'
                          v-model='config.logoUrl'
                          :hint='$t(`admin:general.logoUrlHint`)'
                          persistent-hint
                          append-icon='mdi-folder-image'
                          @click:append='browseLogo'
                          @keyup.enter='refreshLogo'
                        )
                  v-divider
                  .overline.grey--text.pa-4 {{$t('admin:general.footerCopyright')}}
                  .px-3.pb-3
                    v-text-field(
                      outlined
                      :label='$t(`admin:general.companyName`)'
                      v-model='config.company'
                      :counter='255'
                      prepend-icon='mdi-domain'
                      persistent-hint
                      :hint='$t(`admin:general.companyNameHint`)'
                      )
                    v-select.mt-3(
                      outlined
                      :label='$t(`admin:general.contentLicense`)'
                      :items='contentLicenses'
                      v-model='config.contentLicense'
                      prepend-icon='mdi-creative-commons'
                      :return-object='false'
                      :hint='$t(`admin:general.contentLicenseHint`)'
                      persistent-hint
                    )
                    v-text-field.mt-3(
                      outlined
                      :label='$t(`admin:general.footerOverride`)'
                      v-model='config.footerOverride'
                      prepend-icon='mdi-page-layout-footer'
                      append-icon='mdi-language-markdown'
                      persistent-hint
                      :hint='$t(`admin:general.footerOverrideHint`)'
                      )
                  v-divider
                  .overline.grey--text.pa-4 SEO
                  .px-3.pb-3
                    v-text-field(
                      outlined
                      :label='$t(`admin:general.siteDescription`)'
                      :counter='255'
                      v-model='config.description'
                      prepend-icon='mdi-compass'
                      :hint='$t(`admin:general.siteDescriptionHint`)'
                      persistent-hint
           
Download .txt
gitextract_3rhs9jgy/

├── .babelrc
├── .devcontainer/
│   └── devcontainer.json
├── .editorconfig
├── .eslintignore
├── .eslintrc.yml
├── .gitattributes
├── .github/
│   ├── CODE_OF_CONDUCT.md
│   ├── CONTRIBUTING.md
│   ├── FUNDING.yml
│   ├── ISSUE_TEMPLATE/
│   │   └── config.yml
│   ├── PULL_REQUEST_TEMPLATE.md
│   ├── auto_assign.yml
│   ├── issuecomplete.yml
│   └── workflows/
│       ├── build.yml
│       ├── helm.yml
│       └── packer.yml
├── .gitignore
├── .npmrc
├── .nvmrc
├── .vscode/
│   ├── extensions.json
│   ├── launch.json
│   └── settings.json
├── LICENSE
├── README.md
├── SECURITY.md
├── client/
│   ├── .modernizrrc.js
│   ├── client-app.js
│   ├── client-setup.js
│   ├── components/
│   │   ├── admin/
│   │   │   ├── admin-analytics.vue
│   │   │   ├── admin-api-create.vue
│   │   │   ├── admin-api.vue
│   │   │   ├── admin-auth.vue
│   │   │   ├── admin-comments.vue
│   │   │   ├── admin-contribute.vue
│   │   │   ├── admin-dashboard.vue
│   │   │   ├── admin-dev-flags.vue
│   │   │   ├── admin-editor.vue
│   │   │   ├── admin-extensions.vue
│   │   │   ├── admin-general.vue
│   │   │   ├── admin-groups-edit-permissions.vue
│   │   │   ├── admin-groups-edit-rules.vue
│   │   │   ├── admin-groups-edit-users.vue
│   │   │   ├── admin-groups-edit.vue
│   │   │   ├── admin-groups.vue
│   │   │   ├── admin-locale.vue
│   │   │   ├── admin-logging-console.vue
│   │   │   ├── admin-logging.vue
│   │   │   ├── admin-mail.vue
│   │   │   ├── admin-navigation.vue
│   │   │   ├── admin-pages-edit.vue
│   │   │   ├── admin-pages-visualize.vue
│   │   │   ├── admin-pages.vue
│   │   │   ├── admin-rendering.vue
│   │   │   ├── admin-search.vue
│   │   │   ├── admin-security.vue
│   │   │   ├── admin-ssl.vue
│   │   │   ├── admin-stats.vue
│   │   │   ├── admin-storage.vue
│   │   │   ├── admin-system.vue
│   │   │   ├── admin-tags.vue
│   │   │   ├── admin-theme.vue
│   │   │   ├── admin-users-create.vue
│   │   │   ├── admin-users-edit.vue
│   │   │   ├── admin-users.vue
│   │   │   ├── admin-utilities-auth.vue
│   │   │   ├── admin-utilities-cache.vue
│   │   │   ├── admin-utilities-content.vue
│   │   │   ├── admin-utilities-export.vue
│   │   │   ├── admin-utilities-importv1.vue
│   │   │   ├── admin-utilities-telemetry.vue
│   │   │   ├── admin-utilities.vue
│   │   │   └── admin-webhooks.vue
│   │   ├── admin.vue
│   │   ├── comments.vue
│   │   ├── common/
│   │   │   ├── duration-picker.vue
│   │   │   ├── loader.vue
│   │   │   ├── nav-header.vue
│   │   │   ├── notify.vue
│   │   │   ├── page-convert.vue
│   │   │   ├── page-delete.vue
│   │   │   ├── page-selector.vue
│   │   │   ├── password-strength.vue
│   │   │   ├── search-results.vue
│   │   │   ├── social-sharing.vue
│   │   │   ├── user-search.vue
│   │   │   ├── v-card-chin.vue
│   │   │   └── v-card-info.vue
│   │   ├── editor/
│   │   │   ├── api/
│   │   │   │   └── server-selector.vue
│   │   │   ├── ckeditor/
│   │   │   │   └── conflict.vue
│   │   │   ├── common/
│   │   │   │   ├── cmFold.js
│   │   │   │   └── katex.js
│   │   │   ├── editor-api.vue
│   │   │   ├── editor-asciidoc.vue
│   │   │   ├── editor-ckeditor.vue
│   │   │   ├── editor-code.vue
│   │   │   ├── editor-markdown.vue
│   │   │   ├── editor-modal-blocks.vue
│   │   │   ├── editor-modal-conflict.vue
│   │   │   ├── editor-modal-drawio.vue
│   │   │   ├── editor-modal-editorselect.vue
│   │   │   ├── editor-modal-media.vue
│   │   │   ├── editor-modal-properties.vue
│   │   │   ├── editor-modal-unsaved.vue
│   │   │   ├── editor-redirect.vue
│   │   │   └── markdown/
│   │   │       ├── help.vue
│   │   │       ├── plantuml.js
│   │   │       └── tabset.js
│   │   ├── editor.vue
│   │   ├── history.vue
│   │   ├── login.vue
│   │   ├── new-page.vue
│   │   ├── not-found.vue
│   │   ├── profile/
│   │   │   ├── comments.vue
│   │   │   ├── pages.vue
│   │   │   └── profile.vue
│   │   ├── profile.vue
│   │   ├── register.vue
│   │   ├── setup.vue
│   │   ├── source.vue
│   │   ├── tags.vue
│   │   ├── unauthorized.vue
│   │   └── welcome.vue
│   ├── graph/
│   │   ├── admin/
│   │   │   ├── analytics/
│   │   │   │   ├── analytics-mutation-save-providers.gql
│   │   │   │   └── analytics-query-providers.gql
│   │   │   ├── auth/
│   │   │   │   ├── auth-query-groups.gql
│   │   │   │   ├── auth-query-host.gql
│   │   │   │   └── auth-query-strategies.gql
│   │   │   ├── contribute/
│   │   │   │   └── contribute-query-contributors.gql
│   │   │   ├── dashboard/
│   │   │   │   └── dashboard-query-stats.gql
│   │   │   ├── dev/
│   │   │   │   ├── dev-mutation-save-flags.gql
│   │   │   │   └── dev-query-flags.gql
│   │   │   ├── groups/
│   │   │   │   ├── groups-mutation-assign.gql
│   │   │   │   ├── groups-mutation-create.gql
│   │   │   │   ├── groups-mutation-unassign.gql
│   │   │   │   └── groups-query-list.gql
│   │   │   ├── locale/
│   │   │   │   ├── locale-mutation-download.gql
│   │   │   │   ├── locale-mutation-save.gql
│   │   │   │   └── locale-query-list.gql
│   │   │   ├── logging/
│   │   │   │   ├── logging-mutation-save-loggers.gql
│   │   │   │   ├── logging-query-loggers.gql
│   │   │   │   └── logging-subscription-livetrail.gql
│   │   │   ├── mail/
│   │   │   │   ├── mail-mutation-save-config.gql
│   │   │   │   ├── mail-mutation-sendtest.gql
│   │   │   │   └── mail-query-config.gql
│   │   │   ├── pages/
│   │   │   │   ├── pages-query-list.gql
│   │   │   │   └── pages-query-single.gql
│   │   │   ├── rendering/
│   │   │   │   ├── rendering-mutation-save-renderers.gql
│   │   │   │   └── rendering-query-renderers.gql
│   │   │   ├── search/
│   │   │   │   ├── search-mutation-rebuild-index.gql
│   │   │   │   ├── search-mutation-save-engines.gql
│   │   │   │   └── search-query-engines.gql
│   │   │   ├── storage/
│   │   │   │   ├── storage-mutation-executeaction.gql
│   │   │   │   ├── storage-mutation-save-targets.gql
│   │   │   │   ├── storage-query-status.gql
│   │   │   │   └── storage-query-targets.gql
│   │   │   ├── system/
│   │   │   │   ├── system-mutation-upgrade.gql
│   │   │   │   └── system-query-info.gql
│   │   │   ├── theme/
│   │   │   │   ├── theme-mutation-save.gql
│   │   │   │   └── theme-query-config.gql
│   │   │   ├── users/
│   │   │   │   ├── users-mutation-create.gql
│   │   │   │   └── users-query-groups.gql
│   │   │   └── utilities/
│   │   │       ├── utilities-mutation-auth-regencerts.gql
│   │   │       ├── utilities-mutation-auth-resetguest.gql
│   │   │       ├── utilities-mutation-cache-flushcache.gql
│   │   │       ├── utilities-mutation-cache-flushuploads.gql
│   │   │       ├── utilities-mutation-content-migratelocale.gql
│   │   │       ├── utilities-mutation-content-rebuildtree.gql
│   │   │       ├── utilities-mutation-importv1-users.gql
│   │   │       ├── utilities-mutation-telemetry-resetid.gql
│   │   │       ├── utilities-mutation-telemetry-set.gql
│   │   │       └── utilities-query-telemetry.gql
│   │   ├── common/
│   │   │   ├── common-localization-query-translations.gql
│   │   │   ├── common-pages-mutation-delete.gql
│   │   │   ├── common-pages-mutation-move.gql
│   │   │   ├── common-pages-query-list.gql
│   │   │   ├── common-pages-query-search.gql
│   │   │   ├── common-pages-query-tags.gql
│   │   │   └── common-pages-query-tree.gql
│   │   ├── editor/
│   │   │   ├── editor-media-mutation-asset-delete.gql
│   │   │   ├── editor-media-mutation-asset-rename.gql
│   │   │   ├── editor-media-mutation-folder-create.gql
│   │   │   ├── editor-media-query-folder-list.gql
│   │   │   └── editor-media-query-list.gql
│   │   ├── login/
│   │   │   ├── login-mutation-changepassword.gql
│   │   │   ├── login-mutation-login.gql
│   │   │   ├── login-mutation-tfa.gql
│   │   │   └── login-query-strategies.gql
│   │   └── register/
│   │       └── register-mutation-create.gql
│   ├── helpers/
│   │   ├── compatibility.js
│   │   └── index.js
│   ├── index-app.js
│   ├── index-legacy.js
│   ├── index-setup.js
│   ├── libs/
│   │   ├── animate/
│   │   │   └── animate.scss
│   │   ├── codemirror-merge/
│   │   │   └── diff-match-patch.js
│   │   ├── markdown-it-underline/
│   │   │   └── index.js
│   │   ├── modernizr/
│   │   │   └── modernizr.js
│   │   ├── prism/
│   │   │   ├── prism.css
│   │   │   └── prism.js
│   │   └── twemoji/
│   │       └── twemoji-awesome.scss
│   ├── modules/
│   │   ├── boot.js
│   │   └── localization.js
│   ├── polyfills/
│   │   └── array-from.js
│   ├── scss/
│   │   ├── app.scss
│   │   ├── base/
│   │   │   ├── animation.scss
│   │   │   ├── base.scss
│   │   │   ├── icons.scss
│   │   │   ├── material.scss
│   │   │   └── mixins.scss
│   │   ├── components/
│   │   │   ├── codemirror.scss
│   │   │   ├── katex.scss
│   │   │   ├── v-btn.scss
│   │   │   ├── v-data-table.scss
│   │   │   ├── v-dialog.scss
│   │   │   ├── v-form.scss
│   │   │   └── v-tabs.scss
│   │   ├── fonts/
│   │   │   ├── arabic.scss
│   │   │   └── default.scss
│   │   ├── global.scss
│   │   ├── layout/
│   │   │   └── _rtl.scss
│   │   ├── legacy.scss
│   │   └── pages/
│   │       ├── _error.scss
│   │       ├── _new.scss
│   │       ├── _notfound.scss
│   │       ├── _unauthorized.scss
│   │       └── _welcome.scss
│   ├── static/
│   │   ├── browserconfig.xml
│   │   ├── favicons/
│   │   │   └── browserconfig.xml
│   │   ├── manifest.json
│   │   └── svg/
│   │       └── twemoji.asar
│   ├── store/
│   │   ├── admin.js
│   │   ├── editor.js
│   │   ├── index.js
│   │   ├── page.js
│   │   ├── site.js
│   │   └── user.js
│   └── themes/
│       └── default/
│           ├── components/
│           │   ├── nav-footer.vue
│           │   ├── nav-sidebar.vue
│           │   ├── page.vue
│           │   └── tabset.vue
│           ├── js/
│           │   └── app.js
│           ├── scss/
│           │   └── app.scss
│           └── theme.yml
├── config.sample.yml
├── cypress.json
├── dev/
│   ├── build/
│   │   ├── Dockerfile
│   │   └── config.yml
│   ├── build-arm/
│   │   └── Dockerfile
│   ├── containers/
│   │   ├── Dockerfile
│   │   ├── config.yml
│   │   └── docker-compose.yml
│   ├── cypress/
│   │   ├── ci-setup.sh
│   │   ├── integration/
│   │   │   └── setup.spec.js
│   │   ├── plugins/
│   │   │   └── index.js
│   │   └── support/
│   │       ├── commands.js
│   │       └── index.js
│   ├── examples/
│   │   └── docker-compose.yml
│   ├── helm/
│   │   ├── .helmignore
│   │   ├── Chart.yaml
│   │   ├── README.md
│   │   ├── templates/
│   │   │   ├── NOTES.txt
│   │   │   ├── _helpers.tpl
│   │   │   ├── deployment.yaml
│   │   │   ├── ingress.yaml
│   │   │   ├── postgresql-pvc.yaml
│   │   │   ├── postgresql-secret.yaml
│   │   │   ├── postgresql-service.yaml
│   │   │   ├── postgresql-statefulset.yaml
│   │   │   ├── service.yaml
│   │   │   ├── serviceaccount.yaml
│   │   │   └── tests/
│   │   │       └── test-connection.yaml
│   │   └── values.yaml
│   ├── index.js
│   ├── installer/
│   │   ├── main.go
│   │   └── syscheck.go
│   ├── openshift/
│   │   └── Dockerfile
│   ├── packer/
│   │   ├── digitalocean.json
│   │   └── scripts/
│   │       ├── 001-onboot.sh
│   │       ├── 010-docker.sh
│   │       ├── 011-ufw-docker.sh
│   │       ├── 020-force-ssh-logout.sh
│   │       ├── 099-one-click
│   │       ├── 900-cleanup.sh
│   │       └── 999-img-check.sh
│   ├── search-engines/
│   │   └── solr/
│   │       └── solrconfig.xml
│   ├── templates/
│   │   ├── legacy.pug
│   │   ├── master.pug
│   │   └── setup.pug
│   └── webpack/
│       ├── webpack.dev.js
│       └── webpack.prod.js
├── package.json
├── patches/
│   └── extract-files+9.0.0.patch
└── server/
    ├── app/
    │   ├── content/
    │   │   └── create.md
    │   ├── data.yml
    │   └── regex.js
    ├── controllers/
    │   ├── auth.js
    │   ├── common.js
    │   ├── ssl.js
    │   └── upload.js
    ├── core/
    │   ├── asar.js
    │   ├── auth.js
    │   ├── cache.js
    │   ├── config.js
    │   ├── db.js
    │   ├── extensions.js
    │   ├── kernel.js
    │   ├── letsencrypt.js
    │   ├── localization.js
    │   ├── logger.js
    │   ├── mail.js
    │   ├── scheduler.js
    │   ├── servers.js
    │   ├── sideloader.js
    │   ├── system.js
    │   ├── telemetry.js
    │   └── worker.js
    ├── db/
    │   ├── beta/
    │   │   ├── index.js
    │   │   ├── migrations/
    │   │   │   ├── 2.0.0-beta.1.js
    │   │   │   ├── 2.0.0-beta.11.js
    │   │   │   ├── 2.0.0-beta.127.js
    │   │   │   ├── 2.0.0-beta.148.js
    │   │   │   ├── 2.0.0-beta.205.js
    │   │   │   ├── 2.0.0-beta.217.js
    │   │   │   ├── 2.0.0-beta.242.js
    │   │   │   ├── 2.0.0-beta.293.js
    │   │   │   ├── 2.0.0-beta.38.js
    │   │   │   ├── 2.0.0-beta.99.js
    │   │   │   ├── 2.0.0-rc.2.js
    │   │   │   └── 2.0.0-rc.29.js
    │   │   └── migrations-sqlite/
    │   │       ├── 2.0.0-beta.1.js
    │   │       ├── 2.0.0-beta.11.js
    │   │       ├── 2.0.0-beta.127.js
    │   │       ├── 2.0.0-beta.205.js
    │   │       ├── 2.0.0-beta.217.js
    │   │       ├── 2.0.0-beta.242.js
    │   │       ├── 2.0.0-beta.293.js
    │   │       ├── 2.0.0-beta.38.js
    │   │       ├── 2.0.0-beta.99.js
    │   │       └── 2.0.0-rc.2.js
    │   ├── migrations/
    │   │   ├── 2.0.0.js
    │   │   ├── 2.1.85.js
    │   │   ├── 2.2.17.js
    │   │   ├── 2.2.3.js
    │   │   ├── 2.3.10.js
    │   │   ├── 2.3.23.js
    │   │   ├── 2.4.13.js
    │   │   ├── 2.4.14.js
    │   │   ├── 2.4.36.js
    │   │   ├── 2.4.61.js
    │   │   ├── 2.5.1.js
    │   │   ├── 2.5.108.js
    │   │   ├── 2.5.118.js
    │   │   ├── 2.5.12.js
    │   │   ├── 2.5.122.js
    │   │   └── 2.5.128.js
    │   ├── migrations-sqlite/
    │   │   ├── 2.0.0.js
    │   │   ├── 2.2.17.js
    │   │   ├── 2.2.3.js
    │   │   ├── 2.3.10.js
    │   │   ├── 2.3.14.js
    │   │   ├── 2.3.23.js
    │   │   ├── 2.4.13.js
    │   │   ├── 2.4.36.js
    │   │   ├── 2.4.61.js
    │   │   ├── 2.5.1.js
    │   │   ├── 2.5.108.js
    │   │   ├── 2.5.118.js
    │   │   ├── 2.5.12.js
    │   │   ├── 2.5.122.js
    │   │   └── 2.5.128.js
    │   └── migrator-source.js
    ├── graph/
    │   ├── directives/
    │   │   ├── auth.js
    │   │   └── rate-limit.js
    │   ├── index.js
    │   ├── resolvers/
    │   │   ├── analytics.js
    │   │   ├── asset.js
    │   │   ├── authentication.js
    │   │   ├── comment.js
    │   │   ├── contribute.js
    │   │   ├── folder.js
    │   │   ├── group.js
    │   │   ├── localization.js
    │   │   ├── logging.js
    │   │   ├── mail.js
    │   │   ├── navigation.js
    │   │   ├── page.js
    │   │   ├── rendering.js
    │   │   ├── search.js
    │   │   ├── site.js
    │   │   ├── storage.js
    │   │   ├── system.js
    │   │   ├── tag.js
    │   │   ├── theming.js
    │   │   └── user.js
    │   ├── scalars/
    │   │   └── date.js
    │   └── schemas/
    │       ├── analytics.graphql
    │       ├── asset.graphql
    │       ├── authentication.graphql
    │       ├── comment.graphql
    │       ├── common.graphql
    │       ├── contribute.graphql
    │       ├── group.graphql
    │       ├── localization.graphql
    │       ├── logging.graphql
    │       ├── mail.graphql
    │       ├── navigation.graphql
    │       ├── page.graphql
    │       ├── rendering.graphql
    │       ├── scalars.graphql
    │       ├── search.graphql
    │       ├── site.graphql
    │       ├── storage.graphql
    │       ├── system.graphql
    │       ├── theming.graphql
    │       └── user.graphql
    ├── helpers/
    │   ├── asset.js
    │   ├── brute-knex.js
    │   ├── common.js
    │   ├── config.js
    │   ├── error.js
    │   ├── graph.js
    │   ├── page.js
    │   └── security.js
    ├── index.js
    ├── jobs/
    │   ├── fetch-graph-locale.js
    │   ├── purge-uploads.js
    │   ├── rebuild-tree.js
    │   ├── render-page.js
    │   ├── sanitize-svg.js
    │   ├── sync-graph-locales.js
    │   ├── sync-graph-updates.js
    │   └── sync-storage.js
    ├── locales/
    │   └── README.md
    ├── master.js
    ├── middlewares/
    │   ├── security.js
    │   └── seo.js
    ├── models/
    │   ├── analytics.js
    │   ├── apiKeys.js
    │   ├── assetFolders.js
    │   ├── assets.js
    │   ├── authentication.js
    │   ├── commentProviders.js
    │   ├── comments.js
    │   ├── editors.js
    │   ├── groups.js
    │   ├── locales.js
    │   ├── loggers.js
    │   ├── navigation.js
    │   ├── pageHistory.js
    │   ├── pageLinks.js
    │   ├── pages.js
    │   ├── renderers.js
    │   ├── searchEngines.js
    │   ├── settings.js
    │   ├── storage.js
    │   ├── tags.js
    │   ├── userKeys.js
    │   └── users.js
    ├── modules/
    │   ├── analytics/
    │   │   ├── azureinsights/
    │   │   │   ├── code.yml
    │   │   │   └── definition.yml
    │   │   ├── baidutongji/
    │   │   │   ├── code.yml
    │   │   │   └── definition.yml
    │   │   ├── countly/
    │   │   │   ├── code.yml
    │   │   │   └── definition.yml
    │   │   ├── elasticapm/
    │   │   │   ├── code.yml
    │   │   │   └── definition.yml
    │   │   ├── fathom/
    │   │   │   ├── code.yml
    │   │   │   └── definition.yml
    │   │   ├── fullstory/
    │   │   │   ├── code.yml
    │   │   │   └── definition.yml
    │   │   ├── google/
    │   │   │   ├── code.yml
    │   │   │   └── definition.yml
    │   │   ├── gtm/
    │   │   │   ├── code.yml
    │   │   │   └── definition.yml
    │   │   ├── hotjar/
    │   │   │   ├── code.yml
    │   │   │   └── definition.yml
    │   │   ├── matomo/
    │   │   │   ├── code.yml
    │   │   │   └── definition.yml
    │   │   ├── newrelic/
    │   │   │   ├── code.yml
    │   │   │   └── definition.yml
    │   │   ├── plausible/
    │   │   │   ├── code.yml
    │   │   │   └── definition.yml
    │   │   ├── statcounter/
    │   │   │   ├── code.yml
    │   │   │   └── definition.yml
    │   │   ├── umami/
    │   │   │   ├── code.yml
    │   │   │   └── definition.yml
    │   │   ├── umami2/
    │   │   │   ├── code.yml
    │   │   │   └── definition.yml
    │   │   └── yandex/
    │   │       ├── code.yml
    │   │       └── definition.yml
    │   ├── authentication/
    │   │   ├── auth0/
    │   │   │   ├── authentication.js
    │   │   │   └── definition.yml
    │   │   ├── azure/
    │   │   │   ├── authentication.js
    │   │   │   └── definition.yml
    │   │   ├── cas/
    │   │   │   ├── authentication.js
    │   │   │   └── definition.yml
    │   │   ├── discord/
    │   │   │   ├── authentication.js
    │   │   │   └── definition.yml
    │   │   ├── dropbox/
    │   │   │   ├── authentication.js
    │   │   │   └── definition.yml
    │   │   ├── facebook/
    │   │   │   ├── authentication.js
    │   │   │   └── definition.yml
    │   │   ├── firebase/
    │   │   │   ├── authentication.js
    │   │   │   └── definition.yml
    │   │   ├── github/
    │   │   │   ├── authentication.js
    │   │   │   └── definition.yml
    │   │   ├── gitlab/
    │   │   │   ├── authentication.js
    │   │   │   └── definition.yml
    │   │   ├── google/
    │   │   │   ├── authentication.js
    │   │   │   └── definition.yml
    │   │   ├── keycloak/
    │   │   │   ├── authentication.js
    │   │   │   └── definition.yml
    │   │   ├── ldap/
    │   │   │   ├── authentication.js
    │   │   │   └── definition.yml
    │   │   ├── local/
    │   │   │   ├── authentication.js
    │   │   │   └── definition.yml
    │   │   ├── microsoft/
    │   │   │   ├── authentication.js
    │   │   │   └── definition.yml
    │   │   ├── oauth2/
    │   │   │   ├── authentication.js
    │   │   │   └── definition.yml
    │   │   ├── oidc/
    │   │   │   ├── authentication.js
    │   │   │   └── definition.yml
    │   │   ├── okta/
    │   │   │   ├── authentication.js
    │   │   │   └── definition.yml
    │   │   ├── rocketchat/
    │   │   │   ├── authentication.js
    │   │   │   └── definition.yml
    │   │   ├── saml/
    │   │   │   ├── authentication.js
    │   │   │   └── definition.yml
    │   │   ├── slack/
    │   │   │   ├── authentication.js
    │   │   │   └── definition.yml
    │   │   └── twitch/
    │   │       ├── authentication.js
    │   │       └── definition.yml
    │   ├── comments/
    │   │   ├── artalk/
    │   │   │   ├── code.yml
    │   │   │   └── definition.yml
    │   │   ├── commento/
    │   │   │   ├── code.yml
    │   │   │   └── definition.yml
    │   │   ├── default/
    │   │   │   ├── comment.js
    │   │   │   └── definition.yml
    │   │   └── disqus/
    │   │       ├── code.yml
    │   │       └── definition.yml
    │   ├── editor/
    │   │   ├── api/
    │   │   │   └── definition.yml
    │   │   ├── asciidoc/
    │   │   │   └── definition.yml
    │   │   ├── ckeditor/
    │   │   │   └── definition.yml
    │   │   ├── code/
    │   │   │   └── definition.yml
    │   │   ├── markdown/
    │   │   │   └── definition.yml
    │   │   ├── redirect/
    │   │   │   └── definition.yml
    │   │   └── wysiwyg/
    │   │       └── definition.yml
    │   ├── extensions/
    │   │   ├── git/
    │   │   │   └── ext.js
    │   │   ├── pandoc/
    │   │   │   └── ext.js
    │   │   ├── puppeteer/
    │   │   │   └── ext.js
    │   │   └── sharp/
    │   │       └── ext.js
    │   ├── logging/
    │   │   ├── airbrake/
    │   │   │   ├── definition.yml
    │   │   │   └── logger.js
    │   │   ├── bugsnag/
    │   │   │   ├── definition.yml
    │   │   │   └── logger.js
    │   │   ├── disk/
    │   │   │   ├── definition.yml
    │   │   │   └── logger.js
    │   │   ├── eventlog/
    │   │   │   ├── definition.yml
    │   │   │   └── logger.js
    │   │   ├── loggly/
    │   │   │   ├── definition.yml
    │   │   │   └── logger.js
    │   │   ├── logstash/
    │   │   │   ├── definition.yml
    │   │   │   └── logger.js
    │   │   ├── newrelic/
    │   │   │   ├── definition.yml
    │   │   │   └── logger.js
    │   │   ├── papertrail/
    │   │   │   ├── definition.yml
    │   │   │   └── logger.js
    │   │   ├── raygun/
    │   │   │   ├── definition.yml
    │   │   │   └── logger.js
    │   │   ├── rollbar/
    │   │   │   ├── definition.yml
    │   │   │   └── logger.js
    │   │   ├── sentry/
    │   │   │   ├── definition.yml
    │   │   │   └── logger.js
    │   │   └── syslog/
    │   │       ├── definition.yml
    │   │       └── logger.js
    │   ├── rendering/
    │   │   ├── asciidoc-core/
    │   │   │   ├── definition.yml
    │   │   │   └── renderer.js
    │   │   ├── html-asciinema/
    │   │   │   ├── definition.yml
    │   │   │   └── renderer.js
    │   │   ├── html-blockquotes/
    │   │   │   ├── definition.yml
    │   │   │   └── renderer.js
    │   │   ├── html-codehighlighter/
    │   │   │   ├── definition.yml
    │   │   │   └── renderer.js
    │   │   ├── html-core/
    │   │   │   ├── definition.yml
    │   │   │   └── renderer.js
    │   │   ├── html-diagram/
    │   │   │   ├── definition.yml
    │   │   │   └── renderer.js
    │   │   ├── html-image-prefetch/
    │   │   │   ├── definition.yml
    │   │   │   └── renderer.js
    │   │   ├── html-mediaplayers/
    │   │   │   ├── definition.yml
    │   │   │   └── renderer.js
    │   │   ├── html-mermaid/
    │   │   │   ├── definition.yml
    │   │   │   └── renderer.js
    │   │   ├── html-security/
    │   │   │   ├── definition.yml
    │   │   │   └── renderer.js
    │   │   ├── html-tabset/
    │   │   │   ├── definition.yml
    │   │   │   └── renderer.js
    │   │   ├── html-twemoji/
    │   │   │   ├── definition.yml
    │   │   │   └── renderer.js
    │   │   ├── markdown-abbr/
    │   │   │   ├── definition.yml
    │   │   │   └── renderer.js
    │   │   ├── markdown-core/
    │   │   │   ├── definition.yml
    │   │   │   ├── renderer.js
    │   │   │   └── underline.js
    │   │   ├── markdown-emoji/
    │   │   │   ├── definition.yml
    │   │   │   └── renderer.js
    │   │   ├── markdown-expandtabs/
    │   │   │   ├── definition.yml
    │   │   │   └── renderer.js
    │   │   ├── markdown-footnotes/
    │   │   │   ├── definition.yml
    │   │   │   └── renderer.js
    │   │   ├── markdown-imsize/
    │   │   │   ├── definition.yml
    │   │   │   └── renderer.js
    │   │   ├── markdown-katex/
    │   │   │   ├── definition.yml
    │   │   │   ├── mhchem.js
    │   │   │   └── renderer.js
    │   │   ├── markdown-kroki/
    │   │   │   ├── definition.yml
    │   │   │   └── renderer.js
    │   │   ├── markdown-mathjax/
    │   │   │   ├── definition.yml
    │   │   │   └── renderer.js
    │   │   ├── markdown-multi-table/
    │   │   │   ├── definition.yml
    │   │   │   └── renderer.js
    │   │   ├── markdown-pivot-table/
    │   │   │   ├── definition.yml
    │   │   │   └── renderer.js
    │   │   ├── markdown-plantuml/
    │   │   │   ├── definition.yml
    │   │   │   └── renderer.js
    │   │   ├── markdown-supsub/
    │   │   │   ├── definition.yml
    │   │   │   └── renderer.js
    │   │   ├── markdown-tasklists/
    │   │   │   ├── definition.yml
    │   │   │   └── renderer.js
    │   │   └── openapi-core/
    │   │       ├── definition.yml
    │   │       └── renderer.js
    │   ├── search/
    │   │   ├── algolia/
    │   │   │   ├── definition.yml
    │   │   │   └── engine.js
    │   │   ├── aws/
    │   │   │   ├── definition.yml
    │   │   │   └── engine.js
    │   │   ├── azure/
    │   │   │   ├── definition.yml
    │   │   │   └── engine.js
    │   │   ├── db/
    │   │   │   ├── definition.yml
    │   │   │   └── engine.js
    │   │   ├── elasticsearch/
    │   │   │   ├── definition.yml
    │   │   │   └── engine.js
    │   │   ├── manticore/
    │   │   │   ├── definition.yml
    │   │   │   └── engine.js
    │   │   ├── postgres/
    │   │   │   ├── definition.yml
    │   │   │   └── engine.js
    │   │   ├── solr/
    │   │   │   ├── definition.yml
    │   │   │   └── engine.js
    │   │   └── sphinx/
    │   │       ├── definition.yml
    │   │       └── engine.js
    │   └── storage/
    │       ├── azure/
    │       │   ├── definition.yml
    │       │   └── storage.js
    │       ├── box/
    │       │   ├── definition.yml
    │       │   └── storage.js
    │       ├── digitalocean/
    │       │   ├── definition.yml
    │       │   └── storage.js
    │       ├── disk/
    │       │   ├── common.js
    │       │   ├── definition.yml
    │       │   └── storage.js
    │       ├── dropbox/
    │       │   ├── definition.yml
    │       │   └── storage.js
    │       ├── gdrive/
    │       │   ├── definition.yml
    │       │   └── storage.js
    │       ├── git/
    │       │   ├── definition.yml
    │       │   └── storage.js
    │       ├── onedrive/
    │       │   ├── definition.yml
    │       │   └── storage.js
    │       ├── s3/
    │       │   ├── common.js
    │       │   ├── definition.yml
    │       │   └── storage.js
    │       ├── s3generic/
    │       │   ├── definition.yml
    │       │   └── storage.js
    │       └── sftp/
    │           ├── definition.yml
    │           └── storage.js
    ├── setup.js
    ├── templates/
    │   ├── account-reset-pwd.html
    │   ├── account-verify.html
    │   └── test.html
    ├── test/
    │   └── helpers/
    │       └── page.test.js
    ├── themes/
    │   └── default/
    │       └── theme.yml
    └── views/
        ├── admin.pug
        ├── editor.pug
        ├── error.pug
        ├── history.pug
        ├── legacy/
        │   ├── login.pug
        │   └── page.pug
        ├── login.pug
        ├── new.pug
        ├── notfound.pug
        ├── page.pug
        ├── profile.pug
        ├── register.pug
        ├── source.pug
        ├── tags.pug
        ├── unauthorized.pug
        └── welcome.pug
Download .txt
SYMBOL INDEX (832 symbols across 169 files)

FILE: client/client-app.js
  method mounted (line 217) | mounted () {

FILE: client/components/editor/common/cmFold.js
  method register (line 11) | register(lang) {
  function foldHandler (line 16) | function foldHandler (cm, start) {

FILE: client/components/editor/common/katex.js
  function isValidDelim (line 3) | function isValidDelim (state, pos) {
  method katexInline (line 30) | katexInline (state, silent) {
  method katexBlock (line 98) | katexBlock (state, start, end, silent) {

FILE: client/components/editor/markdown/plantuml.js
  method init (line 8) | init (mdinst, conf) {
  function encode64 (line 142) | function encode64 (data) {
  function append3bytes (line 156) | function append3bytes (b1, b2, b3) {
  function encode6bit (line 169) | function encode6bit(raw) {

FILE: client/components/editor/markdown/tabset.js
  method format (line 5) | format () {

FILE: client/helpers/index.js
  method filesize (line 12) | filesize (rawSize) {
  method makeSafePath (line 20) | makeSafePath (rawPath) {
  method resolvePath (line 28) | resolvePath (path) {
  method setInputSelection (line 38) | setInputSelection (input, startPos, endPos) {
  method install (line 56) | install(Vue) {

FILE: client/libs/codemirror-merge/diff-match-patch.js
  function c (line 11) | function c(a){for(var b="",c=0,g=-1,h=d.length;g<a.length-1;){g=a.indexO...
  function c (line 15) | function c(a,b,c){for(var d=a.substring(c,c+Math.floor(a.length/4)),e=-1...
  function b (line 20) | function b(a,b){if(!a||!b)return 6;var c=a.charAt(a.length-1),d=b.charAt...
  function d (line 34) | function d(a,d){var e=a/b.length,g=Math.abs(c-d);return f.Match_Distance...

FILE: client/libs/modernizr/modernizr.js
  function o (line 3) | function o(n){var e=r.className,s=Modernizr._config.classPrefix||"";if(c...
  function a (line 3) | function a(n,e){return typeof n===e}
  function i (line 3) | function i(){var n,e,s,o,i,l,r;for(var c in f)if(f.hasOwnProperty(c)){if...

FILE: client/modules/boot.js
  method isReady (line 10) | isReady (evt) {
  method register (line 20) | register (evt, clb, once) {
  method registerOnce (line 38) | registerOnce (evt, clb) {
  method notify (line 44) | notify (evt) {
  method onDOMReady (line 61) | onDOMReady (clb) {

FILE: client/modules/localization.js
  method init (line 14) | init() {

FILE: client/store/index.js
  method loadingStart (line 36) | loadingStart (st, stackName) {
  method loadingStop (line 39) | loadingStop (st, stackName) {
  method showNotification (line 42) | showNotification (st, opts) {
  method updateNotificationState (line 50) | updateNotificationState (st, newState) {
  method pushGraphError (line 53) | pushGraphError (st, err) {

FILE: client/store/user.js
  method REFRESH_AUTH (line 26) | REFRESH_AUTH(st) {

FILE: dev/index.js
  method dev (line 12) | dev() {
  method reload (line 61) | async reload() {

FILE: dev/installer/main.go
  function main (line 38) | func main() {

FILE: dev/installer/syscheck.go
  constant nodejsSemverRange (line 13) | nodejsSemverRange = ">=8.11.4 <11.0.0"
  constant ramMin (line 14) | ramMin = 768
  function CheckNodeJs (line 17) | func CheckNodeJs() bool {
  function CheckRAM (line 36) | func CheckRAM() bool {
  function CheckNetworkAccess (line 48) | func CheckNetworkAccess() bool {

FILE: server/core/asar.js
  constant UINT64 (line 3) | const UINT64 = require('cuint').UINT64
  method serve (line 19) | async serve (pkgName, req, res, next) {
  method unload (line 42) | async unload () {
  method readArchiveHeaderSync (line 51) | readArchiveHeaderSync (fd) {
  method readFilesystemSync (line 71) | readFilesystemSync (archive) {
  class Filesystem (line 88) | class Filesystem {
    method constructor (line 89) | constructor (src) {
    method searchNodeFromDirectory (line 95) | searchNodeFromDirectory (p) {
    method getNode (line 106) | getNode (p) {
    method getFile (line 116) | getFile (p, followLinks) {

FILE: server/core/auth.js
  method init (line 28) | init() {
  method activateStrategies (line 59) | async activateStrategies () {
  method authenticate (line 113) | authenticate (req, res, next) {
  method checkAccess (line 221) | checkAccess(user, permissions = [], page = false) {
  method checkExclusiveAccess (line 304) | checkExclusiveAccess(user, includePermissions = [], excludePermissions =...
  method _applyPageRuleSpecificity (line 325) | _applyPageRuleSpecificity ({ rule, checkState, higherPriority = [] }) {
  method reloadGroups (line 350) | async reloadGroups () {
  method reloadApiKeys (line 359) | async reloadApiKeys () {
  method regenerateCertificates (line 367) | async regenerateCertificates () {
  method resetGuestUser (line 405) | async resetGuestUser() {
  method subscribeToEvents (line 435) | subscribeToEvents() {
  method getEffectivePermissions (line 453) | getEffectivePermissions (req, page) {
  method revokeUserTokens (line 483) | revokeUserTokens ({ id, kind = 'u' }) {

FILE: server/core/cache.js
  method init (line 4) | init() {

FILE: server/core/config.js
  method init (line 14) | init() {
  method loadFromDb (line 83) | async loadFromDb() {
  method saveToDb (line 98) | async saveToDb(keys, propagate = true) {
  method applyFlags (line 123) | async applyFlags() {
  method subscribeToEvents (line 130) | subscribeToEvents() {

FILE: server/core/db.js
  method init (line 26) | init() {
  method subscribeToNotifications (line 231) | async subscribeToNotifications () {
  method unsubscribeToNotifications (line 269) | async unsubscribeToNotifications () {
  method notifyViaDB (line 282) | notifyViaDB (event, value) {

FILE: server/core/extensions.js
  method init (line 8) | async init () {

FILE: server/core/kernel.js
  method init (line 7) | async init() {
  method preBootMaster (line 32) | async preBootMaster() {
  method bootMaster (line 53) | async bootMaster() {
  method postBootMaster (line 71) | async postBootMaster() {
  method initTelemetry (line 94) | async initTelemetry() {
  method shutdown (line 109) | async shutdown (devMode = false) {

FILE: server/core/letsencrypt.js
  constant ACME (line 1) | const ACME = require('acme')
  constant CSR (line 5) | const CSR = require('@root/csr')
  constant PEM (line 6) | const PEM = require('@root/pem')
  method init (line 15) | async init () {
  method requestCertificate (line 34) | async requestCertificate () {

FILE: server/core/localization.js
  method init (line 15) | init() {
  method attachMiddleware (line 37) | attachMiddleware (app) {
  method getByNamespace (line 46) | async getByNamespace(locale, namespace) {
  method loadLocale (line 65) | async loadLocale(locale, opts = { silent: false }) {
  method refreshNamespaces (line 100) | async refreshNamespaces (silent = false) {
  method setCurrentLocale (line 113) | async setCurrentLocale(locale) {

FILE: server/core/logger.js
  method init (line 8) | init(uid) {

FILE: server/core/mail.js
  method init (line 11) | init() {
  method send (line 48) | async send(opts) {
  method loadTemplate (line 70) | async loadTemplate(key) {

FILE: server/core/scheduler.js
  class Job (line 8) | class Job {
    method constructor (line 9) | constructor({
    method start (line 30) | start(data) {
    method enqueue (line 44) | enqueue(data) {
    method invoke (line 53) | async invoke(data) {
    method stop (line 97) | async stop() {
  method init (line 106) | init() {
  method start (line 109) | start() {
  method registerJob (line 126) | registerJob(opts, data) {
  method stop (line 131) | async stop() {

FILE: server/core/servers.js
  method startHTTP (line 23) | async startHTTP () {
  method startHTTPS (line 61) | async startHTTPS () {
  method startGraphQL (line 123) | async startGraphQL () {
  method closeConnections (line 168) | closeConnections (mode = 'all') {
  method stopServers (line 183) | async stopServers () {
  method restartServer (line 198) | async restartServer (srv = 'https') {

FILE: server/core/sideloader.js
  method init (line 8) | async init () {
  method importLocales (line 27) | async importLocales() {

FILE: server/core/system.js
  method init (line 26) | init() {
  method upgradeFromMongo (line 37) | async upgradeFromMongo (opts) {
  method export (line 93) | async export (opts) {

FILE: server/core/telemetry.js
  method init (line 11) | init() {
  method sendError (line 19) | sendError(err) {
  method sendEvent (line 22) | sendEvent(eventCategory, eventAction, eventLabel) {
  method sendInstanceEvent (line 25) | async sendInstanceEvent(eventType) {
  method generateClientId (line 140) | generateClientId() {

FILE: server/core/worker.js
  constant WIKI (line 3) | let WIKI = {

FILE: server/db/beta/index.js
  method migrate (line 9) | async migrate (knex) {

FILE: server/db/migrator-source.js
  method getMigrations (line 14) | async getMigrations() {
  method getMigrationName (line 22) | getMigrationName(migration) {
  method getMigration (line 26) | getMigration(migration) {

FILE: server/graph/directives/auth.js
  class AuthDirective (line 5) | class AuthDirective extends SchemaDirectiveVisitor {
    method visitObject (line 6) | visitObject(type) {
    method visitFieldDefinition (line 13) | visitFieldDefinition(field, details) {
    method visitArgumentDefinition (line 18) | visitArgumentDefinition(argument, details) {
    method ensureFieldsWrapped (line 23) | ensureFieldsWrapped(objectType) {

FILE: server/graph/index.js
  class LiveTrailLogger (line 46) | class LiveTrailLogger extends Transport {
    method constructor (line 47) | constructor(opts) {
    method log (line 54) | log (info, callback = () => {}) {

FILE: server/graph/resolvers/analytics.js
  method analytics (line 8) | async analytics() { return {} }
  method analytics (line 11) | async analytics() { return {} }
  method providers (line 14) | async providers(obj, args, context, info) {
  method updateProviders (line 37) | async updateProviders(obj, args, context) {

FILE: server/graph/resolvers/asset.js
  method assets (line 10) | async assets() { return {} }
  method assets (line 13) | async assets() { return {} }
  method list (line 16) | async list(obj, args, context) {
  method folders (line 34) | async folders(obj, args, context) {
  method createFolder (line 50) | async createFolder(obj, args, context) {
  method renameAsset (line 77) | async renameAsset(obj, args, context) {
  method deleteAsset (line 156) | async deleteAsset(obj, args, context) {
  method flushTempUploads (line 195) | async flushTempUploads(obj, args, context) {

FILE: server/graph/resolvers/authentication.js
  method authentication (line 10) | async authentication () { return {} }
  method authentication (line 13) | async authentication () { return {} }
  method apiKeys (line 19) | async apiKeys (obj, args, context) {
  method apiState (line 34) | apiState () {
  method strategies (line 37) | async strategies () {
  method activeStrategies (line 52) | async activeStrategies (obj, args, context, info) {
  method createApiKey (line 80) | async createApiKey (obj, args, context) {
  method login (line 96) | async login (obj, args, context) {
  method loginTFA (line 115) | async loginTFA (obj, args, context) {
  method loginChangePassword (line 129) | async loginChangePassword (obj, args, context) {
  method forgotPassword (line 143) | async forgotPassword (obj, args, context) {
  method register (line 156) | async register (obj, args, context) {
  method setApiState (line 169) | async setApiState (obj, args, context) {
  method revokeApiKey (line 183) | async revokeApiKey (obj, args, context) {
  method updateStrategies (line 200) | async updateStrategies (obj, args, context) {
  method regenerateCertificates (line 253) | async regenerateCertificates (obj, args, context) {
  method resetGuestUser (line 266) | async resetGuestUser (obj, args, context) {
  method icon (line 278) | icon (ap, args) {

FILE: server/graph/resolvers/comment.js
  method comments (line 8) | async comments() { return {} }
  method comments (line 11) | async comments() { return {} }
  method providers (line 17) | async providers(obj, args, context, info) {
  method list (line 42) | async list (obj, args, context) {
  method single (line 67) | async single (obj, args, context) {
  method create (line 102) | async create (obj, args, context) {
  method update (line 120) | async update (obj, args, context) {
  method delete (line 138) | async delete (obj, args, context) {
  method updateProviders (line 155) | async updateProviders(obj, args, context) {

FILE: server/graph/resolvers/contribute.js
  method contribute (line 8) | async contribute() { return {} }
  method contributors (line 11) | async contributors(obj, args, context, info) {

FILE: server/graph/resolvers/group.js
  method groups (line 10) | async groups () { return {} }
  method groups (line 13) | async groups () { return {} }
  method list (line 19) | async list () {
  method single (line 28) | async single(obj, args) {
  method assignUser (line 36) | async assignUser (obj, args, { req }) {
  method create (line 88) | async create (obj, args, { req }) {
  method delete (line 105) | async delete (obj, args) {
  method unassignUser (line 125) | async unassignUser (obj, args) {
  method update (line 152) | async update (obj, args, { req }) {
  method users (line 206) | users (grp) {

FILE: server/graph/resolvers/localization.js
  method localization (line 8) | async localization() { return {} }
  method localization (line 11) | async localization() { return {} }
  method locales (line 14) | async locales(obj, args, context, info) {
  method config (line 27) | async config(obj, args, context, info) {
  method translations (line 35) | translations (obj, args, context, info) {
  method downloadLocale (line 40) | async downloadLocale(obj, args, context) {
  method updateLocale (line 54) | async updateLocale(obj, args, context) {

FILE: server/graph/resolvers/logging.js
  method logging (line 8) | async logging() { return {} }
  method logging (line 11) | async logging() { return {} }
  method loggers (line 19) | async loggers(obj, args, context, info) {
  method updateLoggers (line 44) | async updateLoggers(obj, args, context) {

FILE: server/graph/resolvers/mail.js
  method mail (line 8) | async mail() { return {} }
  method mail (line 11) | async mail() { return {} }
  method config (line 14) | async config(obj, args, context, info) {
  method sendTest (line 22) | async sendTest(obj, args, context) {
  method updateConfig (line 45) | async updateConfig(obj, args, context) {

FILE: server/graph/resolvers/navigation.js
  method navigation (line 7) | async navigation () { return {} }
  method navigation (line 10) | async navigation () { return {} }
  method tree (line 13) | async tree (obj, args, context, info) {
  method config (line 16) | config (obj, args, context, info) {
  method updateTree (line 21) | async updateTree (obj, args, context) {
  method updateConfig (line 37) | async updateConfig (obj, args, context) {

FILE: server/graph/resolvers/page.js
  method pages (line 8) | async pages() { return {} }
  method pages (line 11) | async pages() { return {} }
  method history (line 17) | async history(obj, args, context, info) {
  method version (line 35) | async version(obj, args, context, info) {
  method search (line 52) | async search (obj, args, context) {
  method list (line 76) | async list (obj, args, context, info) {
  method single (line 152) | async single (obj, args, context, info) {
  method singleByPath (line 173) | async singleByPath(obj, args, context, info) {
  method tags (line 200) | async tags (obj, args, context, info) {
  method searchTags (line 218) | async searchTags (obj, args, context, info) {
  method tree (line 249) | async tree (obj, args, context, info) {
  method links (line 299) | async links (obj, args, context, info) {
  method checkConflicts (line 354) | async checkConflicts (obj, args, context, info) {
  method conflictLatest (line 372) | async conflictLatest (obj, args, context, info) {
  method create (line 396) | async create(obj, args, context) {
  method update (line 413) | async update(obj, args, context) {
  method convert (line 430) | async convert(obj, args, context) {
  method move (line 446) | async move(obj, args, context) {
  method delete (line 462) | async delete(obj, args, context) {
  method deleteTag (line 478) | async deleteTag (obj, args, context) {
  method updateTag (line 497) | async updateTag (obj, args, context) {
  method flushCache (line 518) | async flushCache(obj, args, context) {
  method migrateToLocale (line 532) | async migrateToLocale(obj, args, context) {
  method rebuildTree (line 546) | async rebuildTree(obj, args, context) {
  method render (line 559) | async render (obj, args, context) {
  method restore (line 576) | async restore (obj, args, context) {
  method purgeHistory (line 612) | async purgeHistory (obj, args, context) {
  method tags (line 624) | async tags (obj) {

FILE: server/graph/resolvers/rendering.js
  method rendering (line 8) | async rendering() { return {} }
  method rendering (line 11) | async rendering() { return {} }
  method renderers (line 14) | async renderers(obj, args, context, info) {
  method updateRenderers (line 41) | async updateRenderers(obj, args, context) {

FILE: server/graph/resolvers/search.js
  method search (line 8) | async search() { return {} }
  method search (line 11) | async search() { return {} }
  method searchEngines (line 14) | async searchEngines(obj, args, context, info) {
  method updateSearchEngines (line 41) | async updateSearchEngines(obj, args, context) {
  method rebuildIndex (line 71) | async rebuildIndex (obj, args, context) {

FILE: server/graph/resolvers/site.js
  method site (line 8) | async site() { return {} }
  method site (line 11) | async site() { return {} }
  method config (line 14) | async config(obj, args, context, info) {
  method updateConfig (line 42) | async updateConfig(obj, args, context) {

FILE: server/graph/resolvers/storage.js
  method storage (line 8) | async storage() { return {} }
  method storage (line 11) | async storage() { return {} }
  method targets (line 14) | async targets(obj, args, context, info) {
  method status (line 40) | async status(obj, args, context, info) {
  method updateTargets (line 55) | async updateTargets(obj, args, context) {
  method executeAction (line 90) | async executeAction(obj, args, context) {

FILE: server/graph/resolvers/system.js
  method system (line 27) | async system () { return {} }
  method system (line 30) | async system () { return {} }
  method flags (line 33) | flags () {
  method info (line 38) | async info () { return {} }
  method extensions (line 39) | async extensions () {
  method exportStatus (line 46) | async exportStatus () {
  method updateFlags (line 56) | async updateFlags (obj, args, context) {
  method resetTelemetryClientId (line 66) | async resetTelemetryClientId (obj, args, context) {
  method setTelemetry (line 77) | async setTelemetry (obj, args, context) {
  method performUpgrade (line 89) | async performUpgrade (obj, args, context) {
  method importUsersFromV1 (line 112) | async importUsersFromV1(obj, args, context) {
  method setHTTPSRedirection (line 247) | async setHTTPSRedirection (obj, args, context) {
  method renewHTTPSCertificate (line 257) | async renewHTTPSCertificate (obj, args, context) {
  method export (line 280) | async export (obj, args, context) {
  method configFile (line 311) | configFile () {
  method cpuCores (line 314) | cpuCores () {
  method currentVersion (line 317) | currentVersion () {
  method dbType (line 320) | dbType () {
  method dbVersion (line 323) | async dbVersion () {
  method dbHost (line 344) | dbHost () {
  method hostname (line 351) | hostname () {
  method httpPort (line 354) | httpPort () {
  method httpRedirection (line 357) | httpRedirection () {
  method httpsPort (line 360) | httpsPort () {
  method latestVersion (line 363) | latestVersion () {
  method latestVersionReleaseDate (line 366) | latestVersionReleaseDate () {
  method nodeVersion (line 369) | nodeVersion () {
  method operatingSystem (line 372) | async operatingSystem () {
  method platform (line 380) | async platform () {
  method ramTotal (line 387) | ramTotal () {
  method sslDomain (line 390) | sslDomain () {
  method sslExpirationDate (line 393) | sslExpirationDate () {
  method sslProvider (line 396) | sslProvider () {
  method sslStatus (line 399) | sslStatus () {
  method sslSubscriberEmail (line 402) | sslSubscriberEmail () {
  method telemetry (line 405) | telemetry () {
  method telemetryClientId (line 408) | telemetryClientId () {
  method upgradeCapable (line 411) | async upgradeCapable () {
  method workingDirectory (line 414) | workingDirectory () {
  method groupsTotal (line 417) | async groupsTotal () {
  method pagesTotal (line 421) | async pagesTotal () {
  method usersTotal (line 425) | async usersTotal () {
  method tagsTotal (line 429) | async tagsTotal () {

FILE: server/graph/resolvers/theming.js
  method theming (line 9) | async theming() { return {} }
  method theming (line 12) | async theming() { return {} }
  method themes (line 15) | async themes(obj, args, context, info) {
  method config (line 22) | async config(obj, args, context, info) {
  method setConfig (line 35) | async setConfig(obj, args, context, info) {

FILE: server/graph/resolvers/user.js
  method users (line 8) | async users() { return {} }
  method users (line 11) | async users() { return {} }
  method list (line 14) | async list(obj, args, context, info) {
  method search (line 18) | async search(obj, args, context, info) {
  method single (line 25) | async single(obj, args, context, info) {
  method profile (line 37) | async profile (obj, args, context, info) {
  method lastLogins (line 56) | async lastLogins (obj, args, context, info) {
  method create (line 65) | async create (obj, args) {
  method delete (line 76) | async delete (obj, args) {
  method update (line 97) | async update (obj, args) {
  method verify (line 108) | async verify (obj, args) {
  method activate (line 119) | async activate (obj, args) {
  method deactivate (line 130) | async deactivate (obj, args) {
  method enableTFA (line 147) | async enableTFA (obj, args) {
  method disableTFA (line 158) | async disableTFA (obj, args) {
  method resetPassword (line 169) | resetPassword (obj, args) {
  method updateProfile (line 172) | async updateProfile (obj, args, context) {
  method changePassword (line 213) | async changePassword (obj, args, context) {
  method groups (line 251) | groups (usr) {
  method groups (line 256) | async groups (usr) {
  method pagesTotal (line 260) | async pagesTotal (usr) {

FILE: server/graph/scalars/date.js
  method parseValue (line 8) | parseValue(value) {
  method serialize (line 11) | serialize(value) {
  method parseLiteral (line 14) | parseLiteral(ast) {

FILE: server/helpers/asset.js
  method generateHash (line 8) | generateHash(assetPath) {
  method getPathInfo (line 12) | getPathInfo(assetPath) {

FILE: server/helpers/common.js
  method getTypeDefaultValue (line 13) | getTypeDefaultValue (type) {
  method parseModuleProps (line 23) | parseModuleProps (props) {
  method getCookieOpts (line 45) | getCookieOpts () {

FILE: server/helpers/config.js
  method parseConfigValue (line 18) | parseConfigValue (cfg) {
  method isValidDurationString (line 26) | isValidDurationString (val) {

FILE: server/helpers/graph.js
  method generateSuccess (line 4) | generateSuccess (msg) {
  method generateError (line 12) | generateError (err, complete = true) {

FILE: server/helpers/page.js
  method parsePath (line 24) | parsePath (rawPath, opts = {}) {
  method generateHash (line 71) | generateHash(opts) {
  method injectPageMetadata (line 77) | injectPageMetadata(page) {
  method isReservedPath (line 104) | isReservedPath(rawPath) {
  method getFileExtension (line 122) | getFileExtension(contentType) {
  method getContentType (line 128) | getContentType (filePath) {
  method getPagePath (line 135) | getPagePath (filePath) {

FILE: server/helpers/security.js
  method sanitizeCommitUser (line 6) | sanitizeCommitUser (user) {
  method generateToken (line 19) | async generateToken (length) {

FILE: server/index.js
  constant WIKI (line 15) | let WIKI = {

FILE: server/models/analytics.js
  method tableName (line 14) | static get tableName() { return 'analytics' }
  method idColumn (line 15) | static get idColumn() { return 'key' }
  method jsonSchema (line 17) | static get jsonSchema () {
  method jsonAttributes (line 29) | static get jsonAttributes() {
  method getProviders (line 33) | static async getProviders(isEnabled) {
  method refreshProvidersFromDisk (line 38) | static async refreshProvidersFromDisk() {
  method getCode (line 97) | static async getCode ({ cache = false } = {}) {

FILE: server/models/apiKeys.js
  method tableName (line 12) | static get tableName() { return 'apiKeys' }
  method jsonSchema (line 14) | static get jsonSchema () {
  method $beforeUpdate (line 31) | async $beforeUpdate(opt, context) {
  method $beforeInsert (line 36) | async $beforeInsert(context) {
  method createNewKey (line 43) | static async createNewKey ({ name, expiration, fullAccess, group }) {

FILE: server/models/assetFolders.js
  method tableName (line 10) | static get tableName() { return 'assetFolders' }
  method jsonSchema (line 12) | static get jsonSchema () {
  method relationMappings (line 24) | static get relationMappings() {
  method getHierarchy (line 42) | static async getHierarchy (folderId) {
  method getAllPaths (line 64) | static async getAllPaths () {

FILE: server/models/assets.js
  method tableName (line 15) | static get tableName() { return 'assets' }
  method jsonSchema (line 17) | static get jsonSchema () {
  method relationMappings (line 36) | static get relationMappings() {
  method $beforeUpdate (line 57) | async $beforeUpdate(opt, context) {
  method $beforeInsert (line 62) | async $beforeInsert(context) {
  method getAssetPath (line 69) | async getAssetPath() {
  method deleteAssetCache (line 77) | async deleteAssetCache() {
  method upload (line 81) | static async upload(opts) {
  method getAsset (line 169) | static async getAsset(assetPath, res) {
  method getAssetFromCache (line 196) | static async getAssetFromCache(assetPath, cachePath, res) {
  method getAssetFromStorage (line 208) | static async getAssetFromStorage(assetPath, res) {
  method getAssetFromDb (line 223) | static async getAssetFromDb(assetPath, fileHash, cachePath, res) {
  method flushTempUploads (line 235) | static async flushTempUploads() {

FILE: server/models/authentication.js
  method tableName (line 14) | static get tableName() { return 'authentication' }
  method idColumn (line 15) | static get idColumn() { return 'key' }
  method jsonSchema (line 17) | static get jsonSchema () {
  method jsonAttributes (line 29) | static get jsonAttributes() {
  method getStrategy (line 33) | static async getStrategy(key) {
  method getStrategies (line 37) | static async getStrategies() {
  method getStrategiesForLegacyClient (line 46) | static async getStrategiesForLegacyClient() {
  method refreshStrategiesFromDisk (line 78) | static async refreshStrategiesFromDisk() {

FILE: server/models/commentProviders.js
  method tableName (line 14) | static get tableName() { return 'commentProviders' }
  method idColumn (line 15) | static get idColumn() { return 'key' }
  method jsonSchema (line 17) | static get jsonSchema () {
  method jsonAttributes (line 29) | static get jsonAttributes() {
  method getProvider (line 33) | static async getProvider(key) {
  method getProviders (line 37) | static async getProviders(isEnabled) {
  method refreshProvidersFromDisk (line 42) | static async refreshProvidersFromDisk() {
  method initProvider (line 101) | static async initProvider() {

FILE: server/models/comments.js
  method tableName (line 11) | static get tableName() { return 'comments' }
  method jsonSchema (line 13) | static get jsonSchema () {
  method relationMappings (line 31) | static get relationMappings() {
  method $beforeUpdate (line 52) | $beforeUpdate() {
  method $beforeInsert (line 55) | $beforeInsert() {
  method postNewComment (line 63) | static async postNewComment ({ pageId, replyTo, content, guestName, gues...
  method updateComment (line 130) | static async updateComment ({ id, content, user, ip }) {
  method deleteComment (line 164) | static async deleteComment ({ id, user, ip }) {

FILE: server/models/editors.js
  method tableName (line 14) | static get tableName() { return 'editors' }
  method idColumn (line 15) | static get idColumn() { return 'key' }
  method jsonSchema (line 17) | static get jsonSchema () {
  method jsonAttributes (line 29) | static get jsonAttributes() {
  method getEditors (line 33) | static async getEditors() {
  method refreshEditorsFromDisk (line 37) | static async refreshEditorsFromDisk() {
  method getDefaultEditor (line 97) | static async getDefaultEditor(contentType) {

FILE: server/models/groups.js
  method tableName (line 7) | static get tableName() { return 'groups' }
  method jsonSchema (line 9) | static get jsonSchema () {
  method jsonAttributes (line 25) | static get jsonAttributes() {
  method relationMappings (line 29) | static get relationMappings() {
  method $beforeUpdate (line 46) | $beforeUpdate() {
  method $beforeInsert (line 49) | $beforeInsert() {

FILE: server/models/locales.js
  method tableName (line 9) | static get tableName() { return 'locales' }
  method idColumn (line 10) | static get idColumn() { return 'code' }
  method jsonSchema (line 12) | static get jsonSchema () {
  method jsonAttributes (line 29) | static get jsonAttributes() {
  method $beforeUpdate (line 33) | $beforeUpdate() {
  method $beforeInsert (line 36) | $beforeInsert() {
  method getNavLocales (line 41) | static async getNavLocales({ cache = false } = {}) {

FILE: server/models/loggers.js
  method tableName (line 14) | static get tableName() { return 'loggers' }
  method idColumn (line 15) | static get idColumn() { return 'key' }
  method jsonSchema (line 17) | static get jsonSchema () {
  method jsonAttributes (line 30) | static get jsonAttributes() {
  method getLoggers (line 34) | static async getLoggers() {
  method refreshLoggersFromDisk (line 38) | static async refreshLoggersFromDisk() {
  method pageEvent (line 99) | static async pageEvent({ event, page }) {

FILE: server/models/navigation.js
  method tableName (line 10) | static get tableName() { return 'navigation' }
  method idColumn (line 11) | static get idColumn() { return 'key' }
  method jsonSchema (line 13) | static get jsonSchema () {
  method getTree (line 25) | static async getTree({ cache = false, locale = 'en', groups = [], bypass...
  method getAuthorizedItems (line 62) | static getAuthorizedItems(tree = [], groups = []) {

FILE: server/models/pageHistory.js
  method tableName (line 11) | static get tableName() { return 'pageHistory' }
  method jsonSchema (line 13) | static get jsonSchema () {
  method relationMappings (line 35) | static get relationMappings() {
  method $beforeInsert (line 84) | $beforeInsert() {
  method addVersion (line 91) | static async addVersion(opts) {
  method getVersion (line 115) | static async getVersion({ pageId, versionId }) {
  method getHistory (line 158) | static async getHistory({ pageId, offsetPage = 0, offsetSize = 100 }) {
  method purge (line 238) | static async purge (olderThan) {

FILE: server/models/pageLinks.js
  method tableName (line 7) | static get tableName() { return 'pageLinks' }
  method jsonSchema (line 9) | static get jsonSchema () {
  method relationMappings (line 22) | static get relationMappings() {

FILE: server/models/pages.js
  method tableName (line 31) | static get tableName() { return 'pages' }
  method jsonSchema (line 33) | static get jsonSchema () {
  method jsonAttributes (line 57) | static get jsonAttributes() {
  method relationMappings (line 61) | static get relationMappings() {
  method $beforeUpdate (line 118) | $beforeUpdate() {
  method $beforeInsert (line 121) | $beforeInsert() {
  method beforeDelete (line 130) | static async beforeDelete({ asFindQuery }) {
  method cacheSchema (line 137) | static get cacheSchema() {
  method injectMetadata (line 174) | injectMetadata () {
  method getFileExtension (line 183) | getFileExtension() {
  method parseMetadata (line 194) | static parseMetadata (raw, contentType) {
  method createPage (line 241) | static async createPage(opts) {
  method updatePage (line 370) | static async updatePage(opts) {
  method convertPage (line 497) | static async convertPage(opts) {
  method movePage (line 665) | static async movePage(opts) {
  method deletePage (line 791) | static async deletePage(opts) {
  method reconnectLinks (line 850) | static async reconnectLinks (opts) {
  method rebuildTree (line 922) | static async rebuildTree() {
  method renderPage (line 937) | static async renderPage(page) {
  method getPage (line 952) | static async getPage(opts) {
  method getPageFromDb (line 977) | static async getPageFromDb(opts) {
  method savePageToCache (line 1052) | static async savePageToCache(page) {
  method getPageFromCache (line 1086) | static async getPageFromCache(opts) {
  method deletePageFromCache (line 1114) | static async deletePageFromCache(hash) {
  method flushCache (line 1121) | static async flushCache() {
  method migrateToLocale (line 1133) | static async migrateToLocale({ sourceLocale, targetLocale }) {
  method cleanHTML (line 1152) | static cleanHTML(rawHTML = '') {
  method subscribeToEvents (line 1166) | static subscribeToEvents() {

FILE: server/models/renderers.js
  method tableName (line 15) | static get tableName() { return 'renderers' }
  method idColumn (line 16) | static get idColumn() { return 'key' }
  method jsonSchema (line 18) | static get jsonSchema () {
  method jsonAttributes (line 30) | static get jsonAttributes() {
  method getRenderers (line 34) | static async getRenderers() {
  method fetchDefinitions (line 38) | static async fetchDefinitions() {
  method refreshRenderersFromDisk (line 51) | static async refreshRenderersFromDisk() {
  method getRenderingPipeline (line 110) | static async getRenderingPipeline(contentType) {

FILE: server/models/searchEngines.js
  method tableName (line 14) | static get tableName() { return 'searchEngines' }
  method idColumn (line 15) | static get idColumn() { return 'key' }
  method jsonSchema (line 17) | static get jsonSchema () {
  method jsonAttributes (line 30) | static get jsonAttributes() {
  method getSearchEngines (line 34) | static async getSearchEngines() {
  method refreshSearchEnginesFromDisk (line 38) | static async refreshSearchEnginesFromDisk() {
  method initEngine (line 98) | static async initEngine({ activate = false } = {}) {

FILE: server/models/settings.js
  method tableName (line 10) | static get tableName() { return 'settings' }
  method idColumn (line 11) | static get idColumn() { return 'key' }
  method jsonSchema (line 13) | static get jsonSchema () {
  method jsonAttributes (line 26) | static get jsonAttributes() {
  method $beforeUpdate (line 30) | $beforeUpdate() {
  method $beforeInsert (line 33) | $beforeInsert() {
  method getConfig (line 37) | static async getConfig() {

FILE: server/models/storage.js
  method tableName (line 14) | static get tableName() { return 'storage' }
  method idColumn (line 15) | static get idColumn() { return 'key' }
  method jsonSchema (line 17) | static get jsonSchema () {
  method jsonAttributes (line 30) | static get jsonAttributes() {
  method getTargets (line 34) | static async getTargets() {
  method refreshTargetsFromDisk (line 38) | static async refreshTargetsFromDisk() {
  method initTargets (line 117) | static async initTargets() {
  method pageEvent (line 180) | static async pageEvent({ event, page }) {
  method assetEvent (line 191) | static async assetEvent({ event, asset }) {
  method getLocalLocations (line 202) | static async getLocalLocations({ asset }) {
  method executeAction (line 219) | static async executeAction(targetKey, handler) {

FILE: server/models/tags.js
  method tableName (line 10) | static get tableName() { return 'tags' }
  method jsonSchema (line 12) | static get jsonSchema () {
  method relationMappings (line 28) | static get relationMappings() {
  method $beforeUpdate (line 45) | $beforeUpdate() {
  method $beforeInsert (line 48) | $beforeInsert() {
  method associateTags (line 53) | static async associateTags ({ tags, page }) {

FILE: server/models/userKeys.js
  method tableName (line 11) | static get tableName() { return 'userKeys' }
  method jsonSchema (line 13) | static get jsonSchema () {
  method relationMappings (line 28) | static get relationMappings() {
  method $beforeInsert (line 41) | async $beforeInsert(context) {
  method generateToken (line 47) | static async generateToken ({ userId, kind }, context) {
  method validateToken (line 58) | static async validateToken ({ kind, token, skipDelete }, context) {
  method destroyToken (line 73) | static async destroyToken ({ token }) {

FILE: server/models/users.js
  method tableName (line 17) | static get tableName() { return 'users' }
  method jsonSchema (line 19) | static get jsonSchema () {
  method relationMappings (line 44) | static get relationMappings() {
  method $beforeUpdate (line 85) | async $beforeUpdate(opt, context) {
  method $beforeInsert (line 94) | async $beforeInsert(context) {
  method generateHash (line 107) | async generateHash() {
  method verifyPassword (line 114) | async verifyPassword(pwd) {
  method generateTFA (line 122) | async generateTFA() {
  method enableTFA (line 135) | async enableTFA() {
  method disableTFA (line 141) | async disableTFA() {
  method verifyTFA (line 148) | verifyTFA(code) {
  method getGlobalPermissions (line 153) | getGlobalPermissions() {
  method getGroups (line 157) | getGroups() {
  method processProfile (line 165) | static async processProfile({ profile, providerKey }) {
  method login (line 293) | static async login (opts, context) {
  method afterLoginChecks (line 337) | static async afterLoginChecks (user, context, { skipTFA, skipChangePwd }...
  method refreshToken (line 418) | static async refreshToken(user) {
  method loginTFA (line 468) | static async loginTFA ({ securityCode, continuationToken, setup }, conte...
  method loginChangePassword (line 492) | static async loginChangePassword ({ continuationToken, newPassword }, co...
  method loginForgotPassword (line 526) | static async loginForgotPassword ({ email }, context) {
  method createNewUser (line 563) | static async createNewUser ({ providerKey, email, passwordRaw, name, gro...
  method updateUser (line 681) | static async updateUser ({ id, email, name, newPassword, groups, locatio...
  method deleteUser (line 744) | static async deleteUser (id, replaceId) {
  method register (line 766) | static async register ({ email, password, name, verify = false, bypassCh...
  method logout (line 870) | static async logout (context) {
  method getGuestUser (line 879) | static async getGuestUser () {
  method getRootUser (line 891) | static async getRootUser () {
  method updateUserAvatarData (line 904) | static async updateUserAvatarData (userId, data) {
  method getUserAvatarData (line 928) | static async getUserAvatarData (userId) {

FILE: server/modules/authentication/auth0/authentication.js
  method init (line 10) | init (passport, conf) {
  method logout (line 31) | logout (conf) {

FILE: server/modules/authentication/azure/authentication.js
  method init (line 12) | init (passport, conf) {

FILE: server/modules/authentication/cas/authentication.js
  method init (line 11) | init (passport, conf) {

FILE: server/modules/authentication/discord/authentication.js
  method init (line 11) | init (passport, conf) {

FILE: server/modules/authentication/dropbox/authentication.js
  method init (line 11) | init (passport, conf) {

FILE: server/modules/authentication/facebook/authentication.js
  method init (line 11) | init (passport, conf) {

FILE: server/modules/authentication/firebase/authentication.js
  method init (line 13) | init (passport, conf) {

FILE: server/modules/authentication/github/authentication.js
  method init (line 11) | init (passport, conf) {

FILE: server/modules/authentication/gitlab/authentication.js
  method init (line 11) | init (passport, conf) {

FILE: server/modules/authentication/google/authentication.js
  method init (line 11) | init (passport, conf) {
  method logout (line 61) | logout (conf) {

FILE: server/modules/authentication/keycloak/authentication.js
  method init (line 12) | init (passport, conf) {
  method logout (line 47) | logout (conf, context) {

FILE: server/modules/authentication/ldap/authentication.js
  method init (line 12) | init (passport, conf) {
  function getTlsOptions (line 77) | function getTlsOptions(conf) {

FILE: server/modules/authentication/local/authentication.js
  method init (line 12) | init (passport, conf) {

FILE: server/modules/authentication/microsoft/authentication.js
  method init (line 11) | init (passport, conf) {

FILE: server/modules/authentication/oauth2/authentication.js
  method init (line 12) | init (passport, conf) {
  method logout (line 71) | logout (conf) {

FILE: server/modules/authentication/oidc/authentication.js
  method init (line 12) | init (passport, conf) {
  method logout (line 59) | logout (conf) {

FILE: server/modules/authentication/okta/authentication.js
  method init (line 11) | init (passport, conf) {

FILE: server/modules/authentication/rocketchat/authentication.js
  method init (line 12) | init (passport, conf) {
  method logout (line 57) | logout (conf) {

FILE: server/modules/authentication/saml/authentication.js
  method init (line 12) | init (passport, conf) {

FILE: server/modules/authentication/slack/authentication.js
  method init (line 11) | init (passport, conf) {

FILE: server/modules/authentication/twitch/authentication.js
  method init (line 11) | init (passport, conf) {

FILE: server/modules/comments/default/comment.js
  method highlight (line 20) | highlight(str, lang) {
  method init (line 35) | async init (config) {
  method create (line 64) | async create ({ page, replyTo, content, user }) {
  method update (line 126) | async update ({ id, content, user }) {
  method remove (line 137) | async remove ({ id, user }) {
  method getPageIdFromCommentId (line 143) | async getPageIdFromCommentId (id) {
  method getCommentById (line 150) | async getCommentById (id) {
  method count (line 156) | async count (pageId) {

FILE: server/modules/extensions/git/ext.js
  method isCompatible (line 8) | async isCompatible () {
  method check (line 11) | async check () {

FILE: server/modules/extensions/pandoc/ext.js
  method isCompatible (line 8) | async isCompatible () {
  method check (line 12) | async check () {

FILE: server/modules/extensions/puppeteer/ext.js
  method isCompatible (line 8) | async isCompatible () {
  method check (line 12) | async check () {

FILE: server/modules/extensions/sharp/ext.js
  method isCompatible (line 11) | async isCompatible () {
  method check (line 15) | async check () {

FILE: server/modules/logging/airbrake/logger.js
  method init (line 6) | init (logger, conf) {

FILE: server/modules/logging/bugsnag/logger.js
  method init (line 10) | init (logger, conf) {

FILE: server/modules/logging/disk/logger.js
  method init (line 6) | init (logger, conf) {

FILE: server/modules/logging/eventlog/logger.js
  method init (line 6) | init (logger, conf) {

FILE: server/modules/logging/loggly/logger.js
  method init (line 8) | init (logger, conf) {

FILE: server/modules/logging/logstash/logger.js
  method init (line 6) | init (logger, conf) {

FILE: server/modules/logging/newrelic/logger.js
  method init (line 6) | init (logger, conf) {

FILE: server/modules/logging/papertrail/logger.js
  method init (line 8) | init (logger, conf) {

FILE: server/modules/logging/raygun/logger.js
  method init (line 6) | init (logger, conf) {

FILE: server/modules/logging/rollbar/logger.js
  method init (line 10) | init (logger, conf) {

FILE: server/modules/logging/sentry/logger.js
  method init (line 9) | init (logger, conf) {

FILE: server/modules/logging/syslog/logger.js
  method init (line 6) | init (logger, conf) {

FILE: server/modules/rendering/asciidoc-core/renderer.js
  method render (line 5) | async render() {

FILE: server/modules/rendering/html-asciinema/renderer.js
  method init (line 2) | init($, config) {

FILE: server/modules/rendering/html-blockquotes/renderer.js
  method init (line 2) | init($, config) {

FILE: server/modules/rendering/html-codehighlighter/renderer.js
  method init (line 4) | async init($, config) {

FILE: server/modules/rendering/html-core/renderer.js
  constant URL (line 5) | const URL = require('url').URL
  method render (line 12) | async render() {
  function decodeEscape (line 301) | function decodeEscape (string) {

FILE: server/modules/rendering/html-diagram/renderer.js
  method init (line 2) | async init($, config) {

FILE: server/modules/rendering/html-image-prefetch/renderer.js
  method init (line 24) | async init($) {

FILE: server/modules/rendering/html-mediaplayers/renderer.js
  method init (line 2) | init($, config) {

FILE: server/modules/rendering/html-mermaid/renderer.js
  method init (line 2) | init($, config) {

FILE: server/modules/rendering/html-security/renderer.js
  method init (line 5) | async init(input, config) {

FILE: server/modules/rendering/html-tabset/renderer.js
  method init (line 4) | async init($, config) {

FILE: server/modules/rendering/html-twemoji/renderer.js
  method init (line 8) | init (input, conf) {

FILE: server/modules/rendering/markdown-abbr/renderer.js
  method init (line 8) | init (md, conf) {

FILE: server/modules/rendering/markdown-core/renderer.js
  method render (line 23) | async render() {

FILE: server/modules/rendering/markdown-emoji/renderer.js
  method init (line 9) | init (md, conf) {

FILE: server/modules/rendering/markdown-expandtabs/renderer.js
  method init (line 9) | init (md, conf) {

FILE: server/modules/rendering/markdown-footnotes/renderer.js
  method init (line 8) | init (md, conf) {

FILE: server/modules/rendering/markdown-imsize/renderer.js
  method init (line 8) | init (md, conf) {

FILE: server/modules/rendering/markdown-katex/mhchem.js
  function assertNever (line 1675) | function assertNever(a) {}
  function assertString (line 1677) | function assertString(a) {}

FILE: server/modules/rendering/markdown-katex/renderer.js
  method init (line 26) | init (mdinst, conf) {
  function isValidDelim (line 61) | function isValidDelim (state, pos) {
  function katexInline (line 87) | function katexInline (state, silent) {
  function katexBlock (line 148) | function katexBlock (state, start, end, silent) {

FILE: server/modules/rendering/markdown-kroki/renderer.js
  method init (line 8) | init (mdinst, conf) {

FILE: server/modules/rendering/markdown-mathjax/renderer.js
  method init (line 22) | async init (mdinst, conf) {
  function isValidDelim (line 72) | function isValidDelim (state, pos) {
  function mathjaxInline (line 98) | function mathjaxInline (state, silent) {
  function mathjaxBlock (line 159) | function mathjaxBlock (state, start, end, silent) {

FILE: server/modules/rendering/markdown-multi-table/renderer.js
  method init (line 4) | init (md, conf) {

FILE: server/modules/rendering/markdown-pivot-table/renderer.js
  method init (line 4) | init (md) {

FILE: server/modules/rendering/markdown-plantuml/renderer.js
  method init (line 8) | init (mdinst, conf) {
  function encode64 (line 142) | function encode64 (data) {
  function append3bytes (line 156) | function append3bytes (b1, b2, b3) {
  function encode6bit (line 169) | function encode6bit(raw) {

FILE: server/modules/rendering/markdown-supsub/renderer.js
  method init (line 9) | init (md, conf) {

FILE: server/modules/rendering/markdown-tasklists/renderer.js
  method init (line 8) | init (md, conf) {

FILE: server/modules/rendering/openapi-core/renderer.js
  method render (line 4) | async render() {

FILE: server/modules/search/algolia/engine.js
  method activate (line 9) | async activate() {
  method deactivate (line 12) | async deactivate() {
  method init (line 18) | async init() {
  method query (line 47) | async query(q, opts) {
  method created (line 73) | async created(page) {
  method updated (line 88) | async updated(page) {
  method deleted (line 101) | async deleted(page) {
  method renamed (line 109) | async renamed(page) {
  method rebuild (line 123) | async rebuild() {

FILE: server/modules/search/aws/engine.js
  constant AWS (line 2) | const AWS = require('aws-sdk')
  method activate (line 9) | async activate() {
  method deactivate (line 12) | async deactivate() {
  method init (line 18) | async init() {
  method query (line 152) | async query(q, opts) {
  method created (line 189) | async created(page) {
  method updated (line 212) | async updated(page) {
  method deleted (line 235) | async deleted(page) {
  method renamed (line 251) | async renamed(page) {
  method rebuild (line 281) | async rebuild() {

FILE: server/modules/search/azure/engine.js
  method activate (line 10) | async activate() {
  method deactivate (line 13) | async deactivate() {
  method init (line 19) | async init() {
  method query (line 91) | async query(q, opts) {
  method created (line 142) | async created(page) {
  method updated (line 159) | async updated(page) {
  method deleted (line 176) | async deleted(page) {
  method renamed (line 189) | async renamed(page) {
  method rebuild (line 210) | async rebuild() {

FILE: server/modules/search/db/engine.js
  method activate (line 4) | activate() {
  method deactivate (line 7) | deactivate() {
  method init (line 13) | init() {
  method query (line 22) | async query(q, opts) {
  method created (line 61) | async created(page) {
  method updated (line 69) | async updated(page) {
  method deleted (line 77) | async deleted(page) {
  method renamed (line 85) | async renamed(page) {
  method rebuild (line 91) | async rebuild() {

FILE: server/modules/search/elasticsearch/engine.js
  method activate (line 9) | async activate() {
  method deactivate (line 12) | async deactivate() {
  method init (line 18) | async init() {
  method createIndex (line 63) | async createIndex() {
  method query (line 148) | async query(q, opts) {
  method buildTags (line 198) | async buildTags(id) {
  method buildSuggest (line 207) | buildSuggest(page) {
  method created (line 228) | async created(page) {
  method updated (line 250) | async updated(page) {
  method deleted (line 272) | async deleted(page) {
  method renamed (line 285) | async renamed(page) {
  method rebuild (line 311) | async rebuild() {
  function getTlsOptions (line 404) | function getTlsOptions(conf) {

FILE: server/modules/search/manticore/engine.js
  method activate (line 2) | activate() {
  method deactivate (line 5) | deactivate() {
  method query (line 8) | query() {
  method created (line 11) | created() {
  method updated (line 14) | updated() {
  method deleted (line 17) | deleted() {
  method renamed (line 20) | renamed() {
  method rebuild (line 23) | rebuild() {

FILE: server/modules/search/postgres/engine.js
  method activate (line 8) | async activate() {
  method deactivate (line 13) | async deactivate() {
  method init (line 22) | async init() {
  method query (line 61) | async query(q, opts) {
  method created (line 107) | async created(page) {
  method updated (line 119) | async updated(page) {
  method deleted (line 135) | async deleted(page) {
  method renamed (line 146) | async renamed(page) {
  method rebuild (line 158) | async rebuild() {

FILE: server/modules/search/solr/engine.js
  method activate (line 2) | activate() {
  method deactivate (line 5) | deactivate() {
  method query (line 8) | query() {
  method created (line 11) | created() {
  method updated (line 14) | updated() {
  method deleted (line 17) | deleted() {
  method renamed (line 20) | renamed() {
  method rebuild (line 23) | rebuild() {

FILE: server/modules/search/sphinx/engine.js
  method activate (line 2) | activate() {
  method deactivate (line 5) | deactivate() {
  method query (line 8) | query() {
  method created (line 11) | created() {
  method updated (line 14) | updated() {
  method deleted (line 17) | deleted() {
  method renamed (line 20) | renamed() {
  method rebuild (line 23) | rebuild() {

FILE: server/modules/storage/azure/storage.js
  method activated (line 16) | async activated() {
  method deactivated (line 19) | async deactivated() {
  method init (line 22) | async init() {
  method created (line 40) | async created (page) {
  method updated (line 47) | async updated (page) {
  method deleted (line 54) | async deleted (page) {
  method renamed (line 62) | async renamed(page) {
  method assetUploaded (line 86) | async assetUploaded (asset) {
  method assetDeleted (line 96) | async assetDeleted (asset) {
  method assetRenamed (line 108) | async assetRenamed (asset) {
  method getLocalLocation (line 117) | async getLocalLocation () {
  method exportAll (line 123) | async exportAll() {

FILE: server/modules/storage/box/storage.js
  method activated (line 2) | async activated() {
  method deactivated (line 5) | async deactivated() {
  method init (line 8) | async init() {
  method created (line 11) | async created() {
  method updated (line 14) | async updated() {
  method deleted (line 17) | async deleted() {
  method renamed (line 20) | async renamed() {
  method getLocalLocation (line 23) | async getLocalLocation () {

FILE: server/modules/storage/disk/common.js
  method importFromDisk (line 15) | async importFromDisk ({ fullPath, moduleName }) {
  method processPage (line 73) | async processPage ({ user, fullPath, relPath, contentType, moduleName }) {
  method processAsset (line 117) | async processAsset ({ user, relPath, file, moduleName }) {
  method clearFolderCache (line 168) | clearFolderCache () {

FILE: server/modules/storage/disk/storage.js
  method activated (line 16) | async activated() {
  method deactivated (line 19) | async deactivated() {
  method init (line 22) | async init() {
  method sync (line 27) | async sync({ manual } = { manual: false }) {
  method created (line 47) | async created(page) {
  method updated (line 56) | async updated(page) {
  method deleted (line 65) | async deleted(page) {
  method renamed (line 74) | async renamed(page) {
  method assetUploaded (line 96) | async assetUploaded (asset) {
  method assetDeleted (line 105) | async assetDeleted (asset) {
  method assetRenamed (line 114) | async assetRenamed (asset) {
  method getLocalLocation (line 118) | async getLocalLocation (asset) {
  method dump (line 124) | async dump() {
  method backup (line 168) | async backup() {
  method importAll (line 171) | async importAll() {

FILE: server/modules/storage/dropbox/storage.js
  method activated (line 2) | async activated() {
  method deactivated (line 5) | async deactivated() {
  method init (line 8) | async init() {
  method created (line 11) | async created() {
  method updated (line 14) | async updated() {
  method deleted (line 17) | async deleted() {
  method renamed (line 20) | async renamed() {
  method getLocalLocation (line 23) | async getLocalLocation () {

FILE: server/modules/storage/gdrive/storage.js
  method activated (line 2) | async activated() {
  method deactivated (line 5) | async deactivated() {
  method init (line 8) | async init() {
  method created (line 11) | async created() {
  method updated (line 14) | async updated() {
  method deleted (line 17) | async deleted() {
  method renamed (line 20) | async renamed() {
  method getLocalLocation (line 23) | async getLocalLocation () {

FILE: server/modules/storage/git/storage.js
  method activated (line 19) | async activated() {
  method deactivated (line 22) | async deactivated() {
  method init (line 28) | async init() {
  method sync (line 120) | async sync() {
  method processFiles (line 195) | async processFiles(files, user) {
  method created (line 297) | async created(page) {
  method updated (line 319) | async updated(page) {
  method deleted (line 341) | async deleted(page) {
  method renamed (line 361) | async renamed(page) {
  method assetUploaded (line 390) | async assetUploaded (asset) {
  method assetDeleted (line 405) | async assetDeleted (asset) {
  method assetRenamed (line 418) | async assetRenamed (asset) {
  method getLocalLocation (line 426) | async getLocalLocation (asset) {
  method importAll (line 432) | async importAll() {
  method syncUntracked (line 470) | async syncUntracked() {
  method purge (line 517) | async purge() {

FILE: server/modules/storage/onedrive/storage.js
  method activated (line 2) | async activated() {
  method deactivated (line 5) | async deactivated() {
  method init (line 8) | async init() {
  method created (line 11) | async created() {
  method updated (line 14) | async updated() {
  method deleted (line 17) | async deleted() {
  method renamed (line 20) | async renamed() {
  method getLocalLocation (line 23) | async getLocalLocation () {

FILE: server/modules/storage/s3/common.js
  method constructor (line 22) | constructor(storageName) {
  method activated (line 26) | async activated() {
  method deactivated (line 29) | async deactivated() {
  method init (line 32) | async init() {
  method created (line 66) | async created(page) {
  method updated (line 71) | async updated(page) {
  method deleted (line 76) | async deleted(page) {
  method renamed (line 81) | async renamed(page) {
  method assetUploaded (line 101) | async assetUploaded (asset) {
  method assetDeleted (line 110) | async assetDeleted (asset) {
  method assetRenamed (line 119) | async assetRenamed (asset) {
  method getLocalLocation (line 124) | async getLocalLocation () {
  method exportAll (line 130) | async exportAll() {

FILE: server/modules/storage/sftp/storage.js
  method activated (line 19) | async activated() {
  method deactivated (line 22) | async deactivated() {
  method init (line 25) | async init() {
  method created (line 45) | async created(page) {
  method updated (line 51) | async updated(page) {
  method deleted (line 57) | async deleted(page) {
  method renamed (line 62) | async renamed(page) {
  method assetUploaded (line 82) | async assetUploaded (asset) {
  method assetDeleted (line 92) | async assetDeleted (asset) {
  method assetRenamed (line 101) | async assetRenamed (asset) {
  method getLocalLocation (line 106) | async getLocalLocation () {
  method exportAll (line 112) | async exportAll() {
  method ensureDirectory (line 151) | async ensureDirectory(filePath) {
Condensed preview — 695 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (2,519K chars).
[
  {
    "path": ".babelrc",
    "chars": 918,
    "preview": "{\n  \"comments\": true,\n  \"plugins\": [\n    \"lodash\",\n    \"graphql-tag\",\n    \"@babel/plugin-syntax-dynamic-import\",\n    \"@b"
  },
  {
    "path": ".devcontainer/devcontainer.json",
    "chars": 984,
    "preview": "// How to get remote container development working with VSCode:\n// 1. Install \"Remote Development\" extension pack (ms-vs"
  },
  {
    "path": ".editorconfig",
    "chars": 246,
    "preview": "root = true\n\n[*]\nindent_style = space\nindent_size = 2\ncharset = utf-8\ntrim_trailing_whitespace = true\nend_of_line = lf\ni"
  },
  {
    "path": ".eslintignore",
    "chars": 92,
    "preview": "**/node_modules/**\n**/*.min.js\nassets/**\nclient/libs/**\ncoverage/**\nrepo/**\ndata/**\nlogs/**\n"
  },
  {
    "path": ".eslintrc.yml",
    "chars": 276,
    "preview": "extends:\n  - requarks\n  - plugin:vue/strongly-recommended\n  - plugin:cypress/recommended\nenv:\n  node: true\n  jest: true\n"
  },
  {
    "path": ".gitattributes",
    "chars": 6861,
    "preview": "# Common settings that generally should always be used with your language specific settings\n\n# Auto detect text files an"
  },
  {
    "path": ".github/CODE_OF_CONDUCT.md",
    "chars": 3214,
    "preview": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nIn the interest of fostering an open and welcoming environment, w"
  },
  {
    "path": ".github/CONTRIBUTING.md",
    "chars": 4974,
    "preview": "# Contribute\n\n## Introduction\n\nFirst, thank you for considering contributing to Wiki.js! It's people like you that make "
  },
  {
    "path": ".github/FUNDING.yml",
    "chars": 294,
    "preview": "# These are supported funding model platforms\n\ngithub: [NGPixel]\npatreon: requarks\nopen_collective: wikijs\nko_fi: # Repl"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "chars": 626,
    "preview": "blank_issues_enabled: false\ncontact_links:\n  - name: Help / Questions\n    url: https://github.com/Requarks/wiki/discussi"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "chars": 0,
    "preview": ""
  },
  {
    "path": ".github/auto_assign.yml",
    "chars": 466,
    "preview": "# Set to true to add reviewers to pull requests\naddReviewers: true\n\n# Set to true to add assignees to pull requests\naddA"
  },
  {
    "path": ".github/issuecomplete.yml",
    "chars": 523,
    "preview": "# The name of the label to apply when an issue does not have all tasks checked\nlabelName: invalid\n\n# The color of the la"
  },
  {
    "path": ".github/workflows/build.yml",
    "chars": 13905,
    "preview": "name: Build + Publish\n\non:\n  push:\n    branches:\n      - main\n    tags:\n      - 'v*'\n\nenv:\n  BASE_DEV_VERSION: 2.5.0\n\njo"
  },
  {
    "path": ".github/workflows/helm.yml",
    "chars": 906,
    "preview": "name: Helm Chart CI\n\non:\n  # Triggers the workflow on push or pull request events but only for the dev branch\n  push:\n  "
  },
  {
    "path": ".github/workflows/packer.yml",
    "chars": 1112,
    "preview": "name: Build DigitalOcean Image\n\non:\n  workflow_dispatch:\n    inputs:\n      version:\n        description: 'App Version'\n "
  },
  {
    "path": ".gitignore",
    "chars": 526,
    "preview": "# Logs\nlogs\n*.log\n/logs\n\n# Deployment builds\ndist\n\n# Dependency directories\nnode_modules\nnpm/node_modules\n\n# NPM / Yarn\n"
  },
  {
    "path": ".npmrc",
    "chars": 35,
    "preview": "save-exact = true\nsave-prefix = \"\"\n"
  },
  {
    "path": ".nvmrc",
    "chars": 9,
    "preview": "v24.12.0\n"
  },
  {
    "path": ".vscode/extensions.json",
    "chars": 184,
    "preview": "{\n  \"recommendations\": [\n    \"EditorConfig.editorconfig\",\n    \"dbaeumer.vscode-eslint\",\n    \"christian-kohler.path-intel"
  },
  {
    "path": ".vscode/launch.json",
    "chars": 783,
    "preview": "{\n    // Use IntelliSense to learn about possible Node.js debug attributes.\n    // Hover to view descriptions of existin"
  },
  {
    "path": ".vscode/settings.json",
    "chars": 297,
    "preview": "{\n  \"eslint.enable\": true,\n  \"puglint.enable\": true,\n  \"editor.formatOnSave\": false,\n  \"editor.tabSize\": 2,\n  \"eslint.va"
  },
  {
    "path": "LICENSE",
    "chars": 34520,
    "preview": "                    GNU AFFERO GENERAL PUBLIC LICENSE\n                       Version 3, 19 November 2007\n\n Copyright (C)"
  },
  {
    "path": "README.md",
    "chars": 29955,
    "preview": "<div align=\"center\">\n\n<picture>\n  <source media=\"(prefers-color-scheme: dark)\" srcset=\"https://static.requarks.io/logo/w"
  },
  {
    "path": "SECURITY.md",
    "chars": 1864,
    "preview": "# Security Policy\n\nWiki.js is built with security in mind. We try our absolute best to deliver secure and robust applica"
  },
  {
    "path": "client/.modernizrrc.js",
    "chars": 123,
    "preview": "module.exports = {\n  classPrefix: 'mdz-',\n  options: ['setClasses'],\n  'feature-detects': [\n    'css/backdropfilter'\n  ]"
  },
  {
    "path": "client/client-app.js",
    "chars": 8633,
    "preview": "/* global siteConfig */\n\nimport Vue from 'vue'\nimport VueRouter from 'vue-router'\nimport VueClipboards from 'vue-clipboa"
  },
  {
    "path": "client/client-setup.js",
    "chars": 442,
    "preview": "/* eslint-disable import/first */\nimport Vue from 'vue'\nimport Vuetify from 'vuetify/lib'\nimport boot from './modules/bo"
  },
  {
    "path": "client/components/admin/admin-analytics.vue",
    "chars": 7113,
    "preview": "<template lang='pug'>\n  v-container(fluid, grid-list-lg)\n    v-layout(row, wrap)\n      v-flex(xs12)\n        .admin-heade"
  },
  {
    "path": "client/components/admin/admin-api-create.vue",
    "chars": 7340,
    "preview": "<template lang=\"pug\">\n  div\n    v-dialog(v-model='isShown', max-width='650', persistent)\n      v-card\n        .dialog-he"
  },
  {
    "path": "client/components/admin/admin-api.vue",
    "chars": 8313,
    "preview": "<template lang='pug'>\n  v-container(fluid, grid-list-lg)\n    v-layout(row, wrap)\n      v-flex(xs12)\n        .admin-heade"
  },
  {
    "path": "client/components/admin/admin-auth.vue",
    "chars": 16369,
    "preview": "<template lang='pug'>\n  v-container(fluid, grid-list-lg)\n    v-layout(row, wrap)\n      v-flex(xs12)\n        .admin-heade"
  },
  {
    "path": "client/components/admin/admin-comments.vue",
    "chars": 7987,
    "preview": "<template lang='pug'>\n  v-container(fluid, grid-list-lg)\n    v-layout(row, wrap)\n      v-flex(xs12)\n        .admin-heade"
  },
  {
    "path": "client/components/admin/admin-contribute.vue",
    "chars": 12398,
    "preview": "<template lang='pug'>\n  v-container.admin-contribute(fluid, grid-list-lg)\n    v-layout(row, wrap)\n      v-flex(xs12)\n   "
  },
  {
    "path": "client/components/admin/admin-dashboard.vue",
    "chars": 8556,
    "preview": "<template lang='pug'>\n  v-container(fluid, grid-list-lg)\n    v-layout(row, wrap)\n      v-flex(xs12)\n        .admin-heade"
  },
  {
    "path": "client/components/admin/admin-dev-flags.vue",
    "chars": 2804,
    "preview": "<template lang='pug'>\n  v-container(fluid, grid-list-lg)\n    v-layout(row, wrap)\n      v-flex(xs12)\n        .admin-heade"
  },
  {
    "path": "client/components/admin/admin-editor.vue",
    "chars": 2428,
    "preview": "<template lang='pug'>\n  v-container(fluid, grid-list-lg)\n    v-layout(row, wrap)\n      v-flex(xs12)\n        .admin-heade"
  },
  {
    "path": "client/components/admin/admin-extensions.vue",
    "chars": 3912,
    "preview": "<template lang='pug'>\n  v-container(fluid, grid-list-lg)\n    v-layout(row wrap)\n      v-flex(xs12)\n        .admin-header"
  },
  {
    "path": "client/components/admin/admin-general.vue",
    "chars": 19725,
    "preview": "<template lang='pug'>\n  v-container(fluid, grid-list-lg)\n    v-layout(row wrap)\n      v-flex(xs12)\n        .admin-header"
  },
  {
    "path": "client/components/admin/admin-groups-edit-permissions.vue",
    "chars": 7610,
    "preview": "<template lang=\"pug\">\n  v-card(flat)\n    v-container.px-3.pb-3.pt-3(fluid, grid-list-md)\n      v-layout(row, wrap)\n     "
  },
  {
    "path": "client/components/admin/admin-groups-edit-rules.vue",
    "chars": 12543,
    "preview": "<template lang=\"pug\">\n  v-card(flat)\n    v-card-text(v-if='group.id === 1')\n      v-alert.radius-7.mb-0(\n        :class="
  },
  {
    "path": "client/components/admin/admin-groups-edit-users.vue",
    "chars": 4465,
    "preview": "<template lang=\"pug\">\n  v-card(flat)\n    v-card-title.pb-4(:class='$vuetify.theme.dark ? `grey darken-3-d3` : `grey ligh"
  },
  {
    "path": "client/components/admin/admin-groups-edit.vue",
    "chars": 8570,
    "preview": "<template lang='pug'>\n  v-container(fluid, grid-list-lg)\n    v-layout(row wrap)\n      v-flex(xs12)\n        .admin-header"
  },
  {
    "path": "client/components/admin/admin-groups.vue",
    "chars": 5934,
    "preview": "<template lang='pug'>\n  v-container(fluid, grid-list-lg)\n    v-layout(row wrap)\n      v-flex(xs12)\n        .admin-header"
  },
  {
    "path": "client/components/admin/admin-locale.vue",
    "chars": 11799,
    "preview": "<template lang='pug'>\n  v-container(fluid, grid-list-lg)\n    v-layout(row, wrap)\n      v-flex(xs12)\n        .admin-heade"
  },
  {
    "path": "client/components/admin/admin-logging-console.vue",
    "chars": 2301,
    "preview": "<template lang='pug'>\n  v-dialog(v-model='isShown', width='90vw', max-width='1200')\n    .dialog-header\n      span Live C"
  },
  {
    "path": "client/components/admin/admin-logging.vue",
    "chars": 7143,
    "preview": "<template lang='pug'>\n  v-container(fluid, grid-list-lg)\n    v-layout(row, wrap)\n      v-flex(xs12)\n        .admin-heade"
  },
  {
    "path": "client/components/admin/admin-mail.vue",
    "chars": 10050,
    "preview": "<template lang='pug'>\n  v-container(fluid, grid-list-lg)\n    v-layout(row, wrap)\n      v-flex(xs12)\n        .admin-heade"
  },
  {
    "path": "client/components/admin/admin-navigation.vue",
    "chars": 23629,
    "preview": "<template lang='pug'>\n  v-container(fluid, grid-list-lg)\n    v-layout(row wrap)\n      v-flex(xs12)\n        .admin-header"
  },
  {
    "path": "client/components/admin/admin-pages-edit.vue",
    "chars": 10593,
    "preview": "<template lang='pug'>\n  v-container(fluid, grid-list-lg)\n    v-layout(row, wrap, v-if='page.id')\n      v-flex(xs12)\n    "
  },
  {
    "path": "client/components/admin/admin-pages-visualize.vue",
    "chars": 13144,
    "preview": "<template lang='pug'>\n  v-container(fluid, grid-list-lg)\n    v-layout(row wrap)\n      v-flex(xs12)\n        .admin-header"
  },
  {
    "path": "client/components/admin/admin-pages.vue",
    "chars": 5513,
    "preview": "<template lang='pug'>\n  v-container(fluid, grid-list-lg)\n    v-layout(row wrap)\n      v-flex(xs12)\n        .admin-header"
  },
  {
    "path": "client/components/admin/admin-rendering.vue",
    "chars": 8966,
    "preview": "<template lang='pug'>\n  v-container(fluid, grid-list-lg)\n    v-layout(row, wrap)\n      v-flex(xs12)\n        .admin-heade"
  },
  {
    "path": "client/components/admin/admin-search.vue",
    "chars": 8363,
    "preview": "<template lang='pug'>\n  v-container(fluid, grid-list-lg)\n    v-layout(row, wrap)\n      v-flex(xs12)\n        .admin-heade"
  },
  {
    "path": "client/components/admin/admin-security.vue",
    "chars": 17864,
    "preview": "<template lang='pug'>\n  v-container(fluid, grid-list-lg)\n    v-layout(row wrap)\n      v-flex(xs12)\n        .admin-header"
  },
  {
    "path": "client/components/admin/admin-ssl.vue",
    "chars": 9447,
    "preview": "<template lang='pug'>\n  v-container(fluid, grid-list-lg)\n    v-layout(row wrap)\n      v-flex(xs12)\n        .admin-header"
  },
  {
    "path": "client/components/admin/admin-stats.vue",
    "chars": 705,
    "preview": "<template lang='pug'>\n  v-container(fluid, fill-height)\n    v-layout(row wrap)\n      v-flex(xs12)\n        .admin-header-"
  },
  {
    "path": "client/components/admin/admin-storage.vue",
    "chars": 15887,
    "preview": "<template lang='pug'>\n  v-container(fluid, grid-list-lg)\n    v-layout(row, wrap)\n      v-flex(xs12)\n        .admin-heade"
  },
  {
    "path": "client/components/admin/admin-system.vue",
    "chars": 8969,
    "preview": "<template lang='pug'>\n  v-container.admin-system(fluid, grid-list-lg)\n    v-layout(row, wrap)\n      v-flex(xs12)\n       "
  },
  {
    "path": "client/components/admin/admin-tags.vue",
    "chars": 8647,
    "preview": "<template lang='pug'>\n  v-container(fluid, grid-list-lg)\n    v-layout(row wrap)\n      v-flex(xs12)\n        .admin-header"
  },
  {
    "path": "client/components/admin/admin-theme.vue",
    "chars": 9483,
    "preview": "<template lang='pug'>\n  v-container(fluid, grid-list-lg)\n    v-layout(row wrap)\n      v-flex(xs12)\n        .admin-header"
  },
  {
    "path": "client/components/admin/admin-users-create.vue",
    "chars": 6917,
    "preview": "<template lang=\"pug\">\n  v-dialog(v-model='isShown', max-width='650', persistent)\n    v-card\n      .dialog-header.is-shor"
  },
  {
    "path": "client/components/admin/admin-users-edit.vue",
    "chars": 47289,
    "preview": "<template lang='pug'>\n  v-container(fluid, grid-list-lg)\n    v-layout(row, wrap)\n      v-flex(xs12)\n        .admin-heade"
  },
  {
    "path": "client/components/admin/admin-users.vue",
    "chars": 6105,
    "preview": "<template lang='pug'>\n  v-container(fluid, grid-list-lg)\n    v-layout(row, wrap)\n      v-flex(xs12)\n        .admin-heade"
  },
  {
    "path": "client/components/admin/admin-utilities-auth.vue",
    "chars": 3082,
    "preview": "<template lang='pug'>\n  v-card\n    v-toolbar(flat, color='primary', dark, dense)\n      .subtitle-1 {{ $t('admin:utilitie"
  },
  {
    "path": "client/components/admin/admin-utilities-cache.vue",
    "chars": 4048,
    "preview": "<template lang='pug'>\n  v-card\n    v-toolbar(flat, color='primary', dark, dense)\n      .subtitle-1 {{ $t('admin:utilitie"
  },
  {
    "path": "client/components/admin/admin-utilities-content.vue",
    "chars": 10704,
    "preview": "<template lang='pug'>\n  v-card\n    v-toolbar(flat, color='primary', dark, dense)\n      .subtitle-1 {{ $t('admin:utilitie"
  },
  {
    "path": "client/components/admin/admin-utilities-export.vue",
    "chars": 7767,
    "preview": "<template lang='pug'>\n  v-card\n    v-toolbar(flat, color='primary', dark, dense)\n      .subtitle-1 {{ $t('admin:utilitie"
  },
  {
    "path": "client/components/admin/admin-utilities-importv1.vue",
    "chars": 18250,
    "preview": "<template lang='pug'>\n  v-card\n    v-toolbar(flat, color='primary', dark, dense)\n      .subtitle-1 {{ $t('admin:utilitie"
  },
  {
    "path": "client/components/admin/admin-utilities-telemetry.vue",
    "chars": 6531,
    "preview": "<template lang='pug'>\n  v-card\n    v-toolbar(flat, color='primary', dark, dense)\n      .subtitle-1 {{ $t('admin:utilitie"
  },
  {
    "path": "client/components/admin/admin-utilities.vue",
    "chars": 3580,
    "preview": "<template lang='pug'>\n  v-container(fluid, grid-list-lg)\n    v-layout(row, wrap)\n      v-flex(xs12)\n        .admin-heade"
  },
  {
    "path": "client/components/admin/admin-webhooks.vue",
    "chars": 4299,
    "preview": "<template lang='pug'>\n  v-container(fluid, grid-list-lg)\n    v-layout(row, wrap)\n      v-flex(xs12)\n        .admin-heade"
  },
  {
    "path": "client/components/admin.vue",
    "chars": 14612,
    "preview": "<template lang='pug'>\n  v-app.admin\n    nav-header(hide-search)\n      template(slot='mid')\n        v-spacer\n        .ove"
  },
  {
    "path": "client/components/comments.vue",
    "chars": 15997,
    "preview": "<template lang=\"pug\">\n  div(v-intersect.once='onIntersect')\n    v-textarea#discussion-new(\n      outlined\n      flat\n   "
  },
  {
    "path": "client/components/common/duration-picker.vue",
    "chars": 2720,
    "preview": "<template lang='pug'>\n  v-toolbar.radius-7(flat, :color='$vuetify.theme.dark ? \"grey darken-4-l3\" : \"grey lighten-3\"')\n "
  },
  {
    "path": "client/components/common/loader.vue",
    "chars": 1343,
    "preview": "<template lang='pug'>\n  v-dialog(v-model='value', persistent, max-width='350', :overlay-color='color', overlay-opacity='"
  },
  {
    "path": "client/components/common/nav-header.vue",
    "chars": 21754,
    "preview": "<template lang='pug'>\n  v-app-bar.nav-header(color='black', dark, app, :clipped-left='!$vuetify.rtl', :clipped-right='$v"
  },
  {
    "path": "client/components/common/notify.vue",
    "chars": 1063,
    "preview": "<template lang='pug'>\n  v-snackbar.nav-notify(\n    :color='notification.style'\n    top\n    multi-line\n    v-model='notif"
  },
  {
    "path": "client/components/common/page-convert.vue",
    "chars": 3240,
    "preview": "<template lang='pug'>\n  v-dialog(\n    v-model='isShown'\n    max-width='550'\n    persistent\n    overlay-color='blue-grey "
  },
  {
    "path": "client/components/common/page-delete.vue",
    "chars": 3420,
    "preview": "<template lang='pug'>\n  v-dialog(\n    v-model='isShown'\n    max-width='550'\n    persistent\n    overlay-color='red darken"
  },
  {
    "path": "client/components/common/page-selector.vue",
    "chars": 9290,
    "preview": "<template lang=\"pug\">\n  v-dialog(\n    v-model='isShown'\n    max-width='850px'\n    overlay-color='blue darken-4'\n    over"
  },
  {
    "path": "client/components/common/password-strength.vue",
    "chars": 2019,
    "preview": "<template lang=\"pug\">\n  .password-strength\n    v-progress-linear(\n      :color='passwordStrengthColor'\n      v-model='pa"
  },
  {
    "path": "client/components/common/search-results.vue",
    "chars": 7352,
    "preview": "<template lang=\"pug\">\n  .search-results(v-if='searchIsFocused || (search && search.length > 1)')\n    .search-results-con"
  },
  {
    "path": "client/components/common/social-sharing.vue",
    "chars": 4419,
    "preview": "<template lang=\"pug\">\n  v-list(nav, dense)\n    v-list-item(@click='', ref='copyUrlButton')\n      v-icon(color='grey', sm"
  },
  {
    "path": "client/components/common/user-search.vue",
    "chars": 3211,
    "preview": "<template lang=\"pug\">\n  v-dialog(\n    v-model='dialogOpen'\n    max-width='650'\n    )\n    v-card\n      .dialog-header\n   "
  },
  {
    "path": "client/components/common/v-card-chin.vue",
    "chars": 198,
    "preview": "<template lang='pug'>\n  div\n    v-divider.my-0\n    v-card-actions(:class='$vuetify.theme.dark ? \"grey darken-4-l5\" : \"gr"
  },
  {
    "path": "client/components/common/v-card-info.vue",
    "chars": 1458,
    "preview": "<template lang='pug'>\n  .v-card-info(:class='`is-` + color')\n    v-card-text.d-flex.align-center(:class='colors.cls')\n  "
  },
  {
    "path": "client/components/editor/api/server-selector.vue",
    "chars": 75,
    "preview": "<template lang=\"pug\">\n\n</template>\n\n<script>\nexport default {\n\n}\n</script>\n"
  },
  {
    "path": "client/components/editor/ckeditor/conflict.vue",
    "chars": 4114,
    "preview": "<template lang=\"pug\">\n  v-dialog(\n    v-model='isShown'\n    max-width='700'\n    )\n    v-card\n      .dialog-header.is-sho"
  },
  {
    "path": "client/components/editor/common/cmFold.js",
    "chars": 1863,
    "preview": "// Header matching code by CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license:"
  },
  {
    "path": "client/components/editor/common/katex.js",
    "chars": 4288,
    "preview": "// Test if potential opening or closing delimieter\n// Assumes that there is a \"$\" at state.src[pos]\nfunction isValidDeli"
  },
  {
    "path": "client/components/editor/editor-api.vue",
    "chars": 16484,
    "preview": "<template lang='pug'>\n  .editor-api\n    .editor-api-main\n      v-list.editor-api-sidebar.radius-0(nav, :class='$vuetify."
  },
  {
    "path": "client/components/editor/editor-asciidoc.vue",
    "chars": 22167,
    "preview": "<template lang='pug'>\n  .editor-asciidoc\n    v-toolbar.editor-asciidoc-toolbar(dense, color='primary', dark, flat, style"
  },
  {
    "path": "client/components/editor/editor-ckeditor.vue",
    "chars": 6396,
    "preview": "<template lang='pug'>\n  .editor-ckeditor\n    div(ref='toolbarContainer')\n    div.contents(ref='editor')\n    v-system-bar"
  },
  {
    "path": "client/components/editor/editor-code.vue",
    "chars": 13115,
    "preview": "<template lang='pug'>\n  .editor-code\n    .editor-code-main\n      .editor-code-sidebar\n        v-tooltip(right, color='te"
  },
  {
    "path": "client/components/editor/editor-markdown.vue",
    "chars": 36057,
    "preview": "<template lang='pug'>\n  .editor-markdown\n    v-toolbar.editor-markdown-toolbar(dense, color='primary', dark, flat, style"
  },
  {
    "path": "client/components/editor/editor-modal-blocks.vue",
    "chars": 1878,
    "preview": "<template lang='pug'>\n  v-card.editor-modal-blocks.animated.fadeInLeft(flat, tile)\n    v-container.pa-3(grid-list-lg, fl"
  },
  {
    "path": "client/components/editor/editor-modal-conflict.vue",
    "chars": 7283,
    "preview": "<template lang='pug'>\n  v-card.editor-modal-conflict.animated.fadeIn(flat, tile)\n    .pa-4\n      v-toolbar.radius-7(flat"
  },
  {
    "path": "client/components/editor/editor-modal-drawio.vue",
    "chars": 3434,
    "preview": "<template lang='pug'>\n  v-card.editor-modal-drawio.animated.fadeIn(flat, tile)\n    iframe(\n      ref='drawio'\n      src="
  },
  {
    "path": "client/components/editor/editor-modal-editorselect.vue",
    "chars": 3882,
    "preview": "<template lang='pug'>\n  v-dialog(v-model='isShown', persistent, max-width='700', no-click-animation)\n    v-btn(fab, fixe"
  },
  {
    "path": "client/components/editor/editor-modal-media.vue",
    "chars": 25369,
    "preview": "<template lang='pug'>\n  v-card.editor-modal-media.animated.fadeInLeft(flat, tile, :class='`is-editor-` + editorKey')\n   "
  },
  {
    "path": "client/components/editor/editor-modal-properties.vue",
    "chars": 14951,
    "preview": "<template lang='pug'>\n  v-dialog(\n    v-model='isShown'\n    persistent\n    width='1000'\n    :fullscreen='$vuetify.breakp"
  },
  {
    "path": "client/components/editor/editor-modal-unsaved.vue",
    "chars": 869,
    "preview": "<template lang=\"pug\">\n  v-dialog(v-model='isShown', max-width='550')\n    v-card\n      .dialog-header.is-short.is-red\n   "
  },
  {
    "path": "client/components/editor/editor-redirect.vue",
    "chars": 7261,
    "preview": "<template lang='pug'>\n  .editor-redirect\n    .editor-redirect-main\n      .editor-redirect-editor\n        v-container.px-"
  },
  {
    "path": "client/components/editor/markdown/help.vue",
    "chars": 13976,
    "preview": "<template lang='pug'>\n  v-card.editor-markdown-help.animated.fadeInLeft(flat, tile)\n    v-container.pa-3(grid-list-lg, f"
  },
  {
    "path": "client/components/editor/markdown/plantuml.js",
    "chars": 5380,
    "preview": "const pako = require('pako')\n\n// ------------------------------------\n// Markdown - PlantUML Preprocessor\n// -----------"
  },
  {
    "path": "client/components/editor/markdown/tabset.js",
    "chars": 555,
    "preview": "import cash from 'cash-dom'\nimport _ from 'lodash'\n\nexport default {\n  format () {\n    for (let i = 1; i < 6; i++) {\n   "
  },
  {
    "path": "client/components/editor.vue",
    "chars": 20525,
    "preview": "<template lang=\"pug\">\n  v-app.editor(:dark='$vuetify.theme.dark')\n    nav-header(dense)\n      template(slot='mid')\n     "
  },
  {
    "path": "client/components/history.vue",
    "chars": 18728,
    "preview": "<template lang='pug'>\n  v-app(:dark='$vuetify.theme.dark').history\n    nav-header\n    v-content\n      v-toolbar(color='p"
  },
  {
    "path": "client/components/login.vue",
    "chars": 25232,
    "preview": "<template lang=\"pug\">\n  v-app\n    .login(:style='`background-image: url(` + bgUrl + `);`')\n      .login-sd\n        .d-fl"
  },
  {
    "path": "client/components/new-page.vue",
    "chars": 816,
    "preview": "<template lang='pug'>\n  v-app\n    .newpage\n      .newpage-content\n        img.animated.fadeIn(src='/_assets/svg/icon-del"
  },
  {
    "path": "client/components/not-found.vue",
    "chars": 505,
    "preview": "<template lang='pug'>\n  v-app\n    .notfound\n      .notfound-content\n        img.animated.fadeIn(src='/_assets/svg/icon-d"
  },
  {
    "path": "client/components/profile/comments.vue",
    "chars": 323,
    "preview": "<template lang='pug'>\n  v-container(fluid, fill-height, grid-list-lg)\n    v-layout(row wrap)\n      v-flex(xs12)\n        "
  },
  {
    "path": "client/components/profile/pages.vue",
    "chars": 3870,
    "preview": "<template lang='pug'>\n  v-container(fluid, grid-list-lg)\n    v-layout(row wrap)\n      v-flex(xs12)\n        .profile-head"
  },
  {
    "path": "client/components/profile/profile.vue",
    "chars": 42053,
    "preview": "<template lang='pug'>\n  v-container(fluid, grid-list-lg)\n    v-layout(row wrap)\n      v-flex(xs12)\n        .profile-head"
  },
  {
    "path": "client/components/profile.vue",
    "chars": 2705,
    "preview": "<template lang='pug'>\n  v-app(:dark='$vuetify.theme.dark').profile\n    nav-header\n    v-navigation-drawer.pb-0(v-model='"
  },
  {
    "path": "client/components/register.vue",
    "chars": 9517,
    "preview": "<template lang=\"pug\">\n  v-app\n    .register\n      v-container(grid-list-lg)\n        v-layout(row, wrap)\n          v-flex"
  },
  {
    "path": "client/components/setup.vue",
    "chars": 8767,
    "preview": "<template lang=\"pug\">\n  v-app.setup\n    v-content\n      v-container\n        v-layout\n          v-flex(xs12, lg6, offset-"
  },
  {
    "path": "client/components/source.vue",
    "chars": 2741,
    "preview": "<template lang='pug'>\n  v-app(:dark='$vuetify.theme.dark').source\n    nav-header\n    v-content\n      v-toolbar(color='pr"
  },
  {
    "path": "client/components/tags.vue",
    "chars": 10852,
    "preview": "<template lang='pug'>\n  v-app(:dark='$vuetify.theme.dark').tags\n    nav-header\n    v-navigation-drawer.pb-0.elevation-1("
  },
  {
    "path": "client/components/unauthorized.vue",
    "chars": 767,
    "preview": "<template lang='pug'>\n  v-app\n    .unauthorized\n      .unauthorized-content\n        img.animated.fadeIn(src='/_assets/sv"
  },
  {
    "path": "client/components/welcome.vue",
    "chars": 870,
    "preview": "<template lang='pug'>\n  v-app\n    .onboarding\n      .onboarding-content\n        img.animated.fadeIn(src='/_assets/svg/lo"
  },
  {
    "path": "client/graph/admin/analytics/analytics-mutation-save-providers.gql",
    "chars": 217,
    "preview": "mutation($providers: [AnalyticsProviderInput]!) {\n  analytics {\n    updateProviders(providers: $providers) {\n      respo"
  },
  {
    "path": "client/graph/admin/analytics/analytics-query-providers.gql",
    "chars": 198,
    "preview": "query {\n  analytics {\n    providers {\n      isEnabled\n      key\n      title\n      description\n      isAvailable\n      lo"
  },
  {
    "path": "client/graph/admin/auth/auth-query-groups.gql",
    "chars": 62,
    "preview": "query {\n  groups {\n    list {\n      id\n      name\n    }\n  }\n}\n"
  },
  {
    "path": "client/graph/admin/auth/auth-query-host.gql",
    "chars": 47,
    "preview": "{\n  site {\n    config {\n      host\n    }\n  }\n}\n"
  },
  {
    "path": "client/graph/admin/auth/auth-query-strategies.gql",
    "chars": 286,
    "preview": "query {\n  authentication {\n    strategies {\n      isEnabled\n      key\n      title\n      description\n      isAvailable\n  "
  },
  {
    "path": "client/graph/admin/contribute/contribute-query-contributors.gql",
    "chars": 205,
    "preview": "query {\n  contribute {\n    contributors {\n      company\n      currency\n      description\n      id\n      image\n      name"
  },
  {
    "path": "client/graph/admin/dashboard/dashboard-query-stats.gql",
    "chars": 151,
    "preview": "query {\n  system {\n    info {\n      currentVersion\n      latestVersion\n      groupsTotal\n      pagesTotal\n      usersTot"
  },
  {
    "path": "client/graph/admin/dev/dev-mutation-save-flags.gql",
    "chars": 208,
    "preview": "mutation (\n  $flags: [SystemFlagInput]!\n) {\n  system {\n    updateFlags(\n      flags: $flags\n    ) {\n      responseResult"
  },
  {
    "path": "client/graph/admin/dev/dev-query-flags.gql",
    "chars": 59,
    "preview": "{\n  system {\n    flags {\n      key\n      value\n    }\n  }\n}\n"
  },
  {
    "path": "client/graph/admin/groups/groups-mutation-assign.gql",
    "chars": 215,
    "preview": "mutation ($groupId: Int!, $userId: Int!) {\n  groups {\n    assignUser(groupId: $groupId, userId: $userId) {\n      respons"
  },
  {
    "path": "client/graph/admin/groups/groups-mutation-create.gql",
    "chars": 255,
    "preview": "mutation ($name: String!) {\n  groups {\n    create(name: $name) {\n      responseResult {\n        succeeded\n        errorC"
  },
  {
    "path": "client/graph/admin/groups/groups-mutation-unassign.gql",
    "chars": 217,
    "preview": "mutation ($groupId: Int!, $userId: Int!) {\n  groups {\n    unassignUser(groupId: $groupId, userId: $userId) {\n      respo"
  },
  {
    "path": "client/graph/admin/groups/groups-query-list.gql",
    "chars": 125,
    "preview": "query {\n  groups {\n    list {\n      id\n      name\n      isSystem\n      userCount\n      createdAt\n      updatedAt\n    }\n "
  },
  {
    "path": "client/graph/admin/locale/locale-mutation-download.gql",
    "chars": 192,
    "preview": "mutation($locale: String!) {\n  localization {\n    downloadLocale(locale: $locale) {\n      responseResult {\n        succe"
  },
  {
    "path": "client/graph/admin/locale/locale-mutation-save.gql",
    "chars": 338,
    "preview": "mutation($locale: String!, $autoUpdate: Boolean!, $namespacing: Boolean!, $namespaces: [String]!) {\n  localization {\n   "
  },
  {
    "path": "client/graph/admin/locale/locale-query-list.gql",
    "chars": 267,
    "preview": "{\n  localization {\n    locales {\n      availability\n      code\n      createdAt\n      isInstalled\n      installDate\n     "
  },
  {
    "path": "client/graph/admin/logging/logging-mutation-save-loggers.gql",
    "chars": 195,
    "preview": "mutation($loggers: [LoggerInput]) {\n  logging {\n    updateLoggers(loggers: $loggers) {\n      responseResult {\n        su"
  },
  {
    "path": "client/graph/admin/logging/logging-query-loggers.gql",
    "chars": 210,
    "preview": "query {\n  logging {\n    loggers(orderBy: \"title ASC\") {\n      isEnabled\n      key\n      title\n      description\n      lo"
  },
  {
    "path": "client/graph/admin/logging/logging-subscription-livetrail.gql",
    "chars": 77,
    "preview": "subscription {\n  loggingLiveTrail {\n    level\n    output\n    timestamp\n  }\n}\n"
  },
  {
    "path": "client/graph/admin/mail/mail-mutation-save-config.gql",
    "chars": 775,
    "preview": "mutation (\n  $senderName: String!\n  $senderEmail: String!\n  $host: String!\n  $port: Int!\n  $name: String!\n  $secure: Boo"
  },
  {
    "path": "client/graph/admin/mail/mail-mutation-sendtest.gql",
    "chars": 203,
    "preview": "mutation ($recipientEmail: String!) {\n  mail {\n    sendTest(recipientEmail: $recipientEmail) {\n      responseResult {\n  "
  },
  {
    "path": "client/graph/admin/mail/mail-query-config.gql",
    "chars": 233,
    "preview": "{\n  mail {\n    config {\n      senderName\n      senderEmail\n      host\n      port\n      name\n      secure\n      verifySSL"
  },
  {
    "path": "client/graph/admin/pages/pages-query-list.gql",
    "chars": 204,
    "preview": "query {\n  pages {\n    list {\n      id\n      locale\n      path\n      title\n      description\n      contentType\n      isPu"
  },
  {
    "path": "client/graph/admin/pages/pages-query-single.gql",
    "chars": 396,
    "preview": "query($id: Int!) {\n  pages {\n    single(id:$id) {\n      id\n      path\n      hash\n      title\n      description\n      isP"
  },
  {
    "path": "client/graph/admin/rendering/rendering-mutation-save-renderers.gql",
    "chars": 207,
    "preview": "mutation($renderers: [RendererInput]) {\n  rendering {\n    updateRenderers(renderers: $renderers) {\n      responseResult "
  },
  {
    "path": "client/graph/admin/rendering/rendering-query-renderers.gql",
    "chars": 201,
    "preview": "{\n  rendering {\n    renderers {\n      isEnabled\n      key\n      title\n      description\n      icon\n      dependsOn\n     "
  },
  {
    "path": "client/graph/admin/search/search-mutation-rebuild-index.gql",
    "chars": 149,
    "preview": "mutation {\n  search {\n    rebuildIndex {\n      responseResult {\n        succeeded\n        errorCode\n        slug\n       "
  },
  {
    "path": "client/graph/admin/search/search-mutation-save-engines.gql",
    "chars": 206,
    "preview": "mutation($engines: [SearchEngineInput]) {\n  search {\n    updateSearchEngines(engines: $engines) {\n      responseResult {"
  },
  {
    "path": "client/graph/admin/search/search-query-engines.gql",
    "chars": 217,
    "preview": "query {\n  search {\n    searchEngines(orderBy: \"title\") {\n      isEnabled\n      key\n      title\n      description\n      l"
  },
  {
    "path": "client/graph/admin/storage/storage-mutation-executeaction.gql",
    "chars": 233,
    "preview": "mutation($targetKey: String!, $handler: String!) {\n  storage {\n    executeAction(targetKey: $targetKey, handler: $handle"
  },
  {
    "path": "client/graph/admin/storage/storage-mutation-save-targets.gql",
    "chars": 203,
    "preview": "mutation($targets: [StorageTargetInput]!) {\n  storage {\n    updateTargets(targets: $targets) {\n      responseResult {\n  "
  },
  {
    "path": "client/graph/admin/storage/storage-query-status.gql",
    "chars": 112,
    "preview": "query {\n  storage {\n    status {\n      key\n      title\n      status\n      message\n      lastAttempt\n    }\n  }\n}\n"
  },
  {
    "path": "client/graph/admin/storage/storage-query-targets.gql",
    "chars": 356,
    "preview": "query {\n  storage {\n    targets {\n      isAvailable\n      isEnabled\n      key\n      title\n      description\n      logo\n "
  },
  {
    "path": "client/graph/admin/system/system-mutation-upgrade.gql",
    "chars": 151,
    "preview": "mutation {\n  system {\n    performUpgrade {\n      responseResult {\n        succeeded\n        errorCode\n        slug\n     "
  },
  {
    "path": "client/graph/admin/system/system-query-info.gql",
    "chars": 317,
    "preview": "query {\n  system {\n    info {\n      configFile\n      cpuCores\n      currentVersion\n      dbHost\n      dbType\n      dbVer"
  },
  {
    "path": "client/graph/admin/theme/theme-mutation-save.gql",
    "chars": 443,
    "preview": "mutation($theme: String!, $iconset: String!, $darkMode: Boolean!, $tocPosition: String, $injectCSS: String, $injectHead:"
  },
  {
    "path": "client/graph/admin/theme/theme-query-config.gql",
    "chars": 154,
    "preview": "query {\n  theming {\n    config {\n      theme\n      iconset\n      darkMode\n      tocPosition\n      injectCSS\n      inject"
  },
  {
    "path": "client/graph/admin/users/users-mutation-create.gql",
    "chars": 473,
    "preview": "mutation ($providerKey: String!, $email: String!, $name: String!, $passwordRaw: String, $groups: [Int]!, $mustChangePass"
  },
  {
    "path": "client/graph/admin/users/users-query-groups.gql",
    "chars": 77,
    "preview": "query {\n  groups {\n    list {\n      id\n      name\n      isSystem\n    }\n  }\n}\n"
  },
  {
    "path": "client/graph/admin/utilities/utilities-mutation-auth-regencerts.gql",
    "chars": 167,
    "preview": "mutation {\n  authentication {\n    regenerateCertificates {\n      responseResult {\n        succeeded\n        errorCode\n  "
  },
  {
    "path": "client/graph/admin/utilities/utilities-mutation-auth-resetguest.gql",
    "chars": 159,
    "preview": "mutation {\n  authentication {\n    resetGuestUser {\n      responseResult {\n        succeeded\n        errorCode\n        sl"
  },
  {
    "path": "client/graph/admin/utilities/utilities-mutation-cache-flushcache.gql",
    "chars": 146,
    "preview": "mutation {\n  pages {\n    flushCache {\n      responseResult {\n        succeeded\n        errorCode\n        slug\n        me"
  },
  {
    "path": "client/graph/admin/utilities/utilities-mutation-cache-flushuploads.gql",
    "chars": 153,
    "preview": "mutation {\n  assets {\n    flushTempUploads {\n      responseResult {\n        succeeded\n        errorCode\n        slug\n   "
  },
  {
    "path": "client/graph/admin/utilities/utilities-mutation-content-migratelocale.gql",
    "chars": 269,
    "preview": "mutation($sourceLocale: String!, $targetLocale: String!) {\n  pages {\n    migrateToLocale(sourceLocale: $sourceLocale, ta"
  },
  {
    "path": "client/graph/admin/utilities/utilities-mutation-content-rebuildtree.gql",
    "chars": 147,
    "preview": "mutation {\n  pages {\n    rebuildTree {\n      responseResult {\n        succeeded\n        errorCode\n        slug\n        m"
  },
  {
    "path": "client/graph/admin/utilities/utilities-mutation-importv1-users.gql",
    "chars": 389,
    "preview": "mutation($mongoDbConnString: String!, $groupMode: SystemImportUsersGroupMode!) {\n  system {\n    importUsersFromV1(mongoD"
  },
  {
    "path": "client/graph/admin/utilities/utilities-mutation-telemetry-resetid.gql",
    "chars": 159,
    "preview": "mutation {\n  system {\n    resetTelemetryClientId {\n      responseResult {\n        succeeded\n        errorCode\n        sl"
  },
  {
    "path": "client/graph/admin/utilities/utilities-mutation-telemetry-set.gql",
    "chars": 188,
    "preview": "mutation($enabled: Boolean!) {\n  system {\n    setTelemetry(enabled: $enabled) {\n      responseResult {\n        succeeded"
  },
  {
    "path": "client/graph/admin/utilities/utilities-query-telemetry.gql",
    "chars": 82,
    "preview": "query {\n  system {\n    info {\n      telemetry\n      telemetryClientId\n    }\n  }\n}\n"
  },
  {
    "path": "client/graph/common/common-localization-query-translations.gql",
    "chars": 155,
    "preview": "query($locale: String!, $namespace: String!) {\n  localization {\n    translations(locale:$locale, namespace:$namespace) {"
  },
  {
    "path": "client/graph/common/common-pages-mutation-delete.gql",
    "chars": 162,
    "preview": "mutation($id: Int!) {\n  pages {\n    delete(id: $id) {\n      responseResult {\n        succeeded\n        errorCode\n       "
  },
  {
    "path": "client/graph/common/common-pages-mutation-move.gql",
    "chars": 290,
    "preview": "mutation($id: Int!, $destinationPath: String!, $destinationLocale: String!) {\n  pages {\n    move(id: $id, destinationPat"
  },
  {
    "path": "client/graph/common/common-pages-query-list.gql",
    "chars": 361,
    "preview": "query ($limit: Int, $orderBy: PageOrderBy, $orderByDirection: PageOrderByDirection, $tags: [String!], $locale: String) {"
  },
  {
    "path": "client/graph/common/common-pages-query-search.gql",
    "chars": 206,
    "preview": "query ($query: String!) {\n  pages {\n    search(query:$query) {\n      results {\n        id\n        title\n        descript"
  },
  {
    "path": "client/graph/common/common-pages-query-tags.gql",
    "chars": 63,
    "preview": "query {\n  pages {\n    tags {\n      tag\n      title\n    }\n  }\n}\n"
  },
  {
    "path": "client/graph/common/common-pages-query-tree.gql",
    "chars": 217,
    "preview": "query ($parent: Int!, $mode: PageTreeMode!, $locale: String!) {\n  pages {\n    tree(parent: $parent, mode: $mode, locale:"
  },
  {
    "path": "client/graph/editor/editor-media-mutation-asset-delete.gql",
    "chars": 169,
    "preview": "mutation ($id: Int!) {\n  assets {\n    deleteAsset(id: $id) {\n      responseResult {\n        succeeded\n        errorCode\n"
  },
  {
    "path": "client/graph/editor/editor-media-mutation-asset-rename.gql",
    "chars": 209,
    "preview": "mutation ($id: Int!, $filename: String!) {\n  assets {\n    renameAsset(id:$id, filename: $filename) {\n      responseResul"
  },
  {
    "path": "client/graph/editor/editor-media-mutation-folder-create.gql",
    "chars": 234,
    "preview": "mutation ($parentFolderId: Int!, $slug: String!) {\n  assets {\n    createFolder(parentFolderId:$parentFolderId, slug: $sl"
  },
  {
    "path": "client/graph/editor/editor-media-query-folder-list.gql",
    "chars": 132,
    "preview": "query ($parentFolderId: Int!) {\n  assets {\n    folders(parentFolderId:$parentFolderId) {\n      id\n      name\n      slug\n"
  },
  {
    "path": "client/graph/editor/editor-media-query-list.gql",
    "chars": 215,
    "preview": "query ($folderId: Int!, $kind: AssetKind!) {\n  assets {\n    list(folderId:$folderId, kind: $kind) {\n      id\n      filen"
  },
  {
    "path": "client/graph/login/login-mutation-changepassword.gql",
    "chars": 292,
    "preview": "mutation($continuationToken: String!, $newPassword: String!) {\n  authentication {\n    loginChangePassword(continuationTo"
  },
  {
    "path": "client/graph/login/login-mutation-login.gql",
    "chars": 348,
    "preview": "mutation($username: String!, $password: String!, $strategy: String!) {\n  authentication {\n    login(username: $username,"
  },
  {
    "path": "client/graph/login/login-mutation-tfa.gql",
    "chars": 284,
    "preview": "mutation($continuationToken: String!, $securityCode: String!) {\n  authentication {\n    loginTFA(continuationToken: $cont"
  },
  {
    "path": "client/graph/login/login-query-strategies.gql",
    "chars": 167,
    "preview": "query {\n  authentication {\n    strategies(\n      isEnabled: true\n    ) {\n      key\n      title\n      useForm\n      icon\n"
  },
  {
    "path": "client/graph/register/register-mutation-create.gql",
    "chars": 265,
    "preview": "mutation($email: String!, $password: String!, $name: String!) {\n  authentication {\n    register(email: $email, password:"
  },
  {
    "path": "client/helpers/compatibility.js",
    "chars": 206,
    "preview": "// =======================================\n// Fetch polyfill\n// =======================================\n// Requirement: "
  },
  {
    "path": "client/helpers/index.js",
    "chars": 1687,
    "preview": "import filesize from 'filesize.js'\nimport _ from 'lodash'\n\n/* global siteConfig */\n\nconst helpers = {\n  /**\n   * Convert"
  },
  {
    "path": "client/index-app.js",
    "chars": 770,
    "preview": "require('core-js/stable')\nrequire('regenerator-runtime/runtime')\n\n/* global siteConfig */\n/* eslint-disable no-unused-ex"
  },
  {
    "path": "client/index-legacy.js",
    "chars": 87,
    "preview": "require('./scss/legacy.scss')\nrequire('./scss/fonts/default.scss')\n\nwindow.WIKI = null\n"
  },
  {
    "path": "client/index-setup.js",
    "chars": 284,
    "preview": "require('core-js/stable')\nrequire('regenerator-runtime/runtime')\n\n/* eslint-disable no-unused-expressions */\n\nrequire('."
  },
  {
    "path": "client/libs/animate/animate.scss",
    "chars": 72853,
    "preview": "@charset \"UTF-8\";\n\n/*!\n * animate.css -http://daneden.me/animate\n * Version - 3.5.1\n * Licensed under the MIT license - "
  },
  {
    "path": "client/libs/codemirror-merge/diff-match-patch.js",
    "chars": 21415,
    "preview": "var diff_match_patch=function(){this.Diff_Timeout=1;this.Diff_EditCost=4;this.Match_Threshold=.5;this.Match_Distance=1E3"
  },
  {
    "path": "client/libs/markdown-it-underline/index.js",
    "chars": 294,
    "preview": "const renderEm = (tokens, idx, opts, env, slf) => {\n  const token = tokens[idx];\n  if (token.markup === '_') {\n    token"
  },
  {
    "path": "client/libs/modernizr/modernizr.js",
    "chars": 1526,
    "preview": "/*! modernizr 3.6.0 (Custom Build) | MIT *\n * https://modernizr.com/download/?-setclasses !*/\n!function(n,e,s){function "
  },
  {
    "path": "client/libs/prism/prism.css",
    "chars": 3195,
    "preview": "/* PrismJS 1.11.0\nhttp://prismjs.com/download.html?themes=prism-dark&languages=markup+css+clike+javascript+c+bash+basic+"
  },
  {
    "path": "client/libs/prism/prism.js",
    "chars": 76266,
    "preview": "/* PrismJS 1.11.0\nhttp://prismjs.com/download.html?themes=prism-dark&languages=markup+css+clike+javascript+c+bash+basic+"
  },
  {
    "path": "client/libs/twemoji/twemoji-awesome.scss",
    "chars": 27328,
    "preview": ".twa {\n  display: inline-block;\n  height: 1em;\n  width: 1em;\n  margin: 0 .05em 0 .1em;\n  vertical-align: -0.1em;\n  backg"
  }
]

// ... and 495 more files (download for full content)

About this extraction

This page contains the full source code of the requarks/wiki GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 695 files (2.3 MB), approximately 632.0k tokens, and a symbol index with 832 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!