feat: admin storage actions + status light component

parent c9fc4a16
...@@ -92,8 +92,7 @@ module.exports = { ...@@ -92,8 +92,7 @@ module.exports = {
} }
} }
}, {}), }, {}),
actions: {} actions: md.actions
// actions: md.actions
} }
}), ['title']) }), ['title'])
} }
......
...@@ -94,6 +94,9 @@ module.exports = { ...@@ -94,6 +94,9 @@ module.exports = {
latestVersionReleaseDate () { latestVersionReleaseDate () {
return DateTime.fromISO(WIKI.system.updates.releaseDate).toJSDate() return DateTime.fromISO(WIKI.system.updates.releaseDate).toJSDate()
}, },
mailConfigured () {
return false // TODO: return true if mail is setup
},
nodeVersion () { nodeVersion () {
return process.version.substr(1) return process.version.substr(1)
}, },
......
...@@ -68,6 +68,7 @@ type SystemInfo { ...@@ -68,6 +68,7 @@ type SystemInfo {
httpsPort: Int httpsPort: Int
latestVersion: String latestVersion: String
latestVersionReleaseDate: Date latestVersionReleaseDate: Date
mailConfigured: Boolean
nodeVersion: String nodeVersion: String
operatingSystem: String operatingSystem: String
pagesTotal: Int pagesTotal: Int
......
...@@ -50,7 +50,7 @@ props: ...@@ -50,7 +50,7 @@ props:
- hot|Hot - hot|Hot
- cool|Cool - cool|Cool
actions: actions:
- handler: exportAll exportAll:
label: Export All DB Assets to Azure label: Export All DB Assets to Azure
hint: Output all content from the DB to Azure Blog Storage, overwriting any existing data. If you enabled Azure Blog Storage after content was created or you temporarily disabled it, you'll want to execute this action to add the missing content. hint: Output all content from the DB to Azure Blog Storage, overwriting any existing data. If you enabled Azure Blog Storage after content was created or you temporarily disabled it, you'll want to execute this action to add the missing content.
icon: this-way-up icon: this-way-up
...@@ -18,7 +18,7 @@ versioning: ...@@ -18,7 +18,7 @@ versioning:
sync: false sync: false
props: {} props: {}
actions: actions:
- handler: purge purge:
label: Purge All Assets label: Purge All Assets
hint: Delete all asset data from the database (not the metadata). Useful if you moved assets to another storage target and want to reduce the size of the database. hint: Delete all asset data from the database (not the metadata). Useful if you moved assets to another storage target and want to reduce the size of the database.
warn: This is a destructive action! Make sure all asset files are properly stored on another storage module! This action cannot be undone! warn: This is a destructive action! Make sure all asset files are properly stored on another storage module! This action cannot be undone!
......
...@@ -32,15 +32,15 @@ props: ...@@ -32,15 +32,15 @@ props:
icon: archive-folder icon: archive-folder
order: 2 order: 2
actions: actions:
- handler: dump dump:
label: Dump all content to disk label: Dump all content to disk
hint: Output all content from the DB to the local disk. If you enabled this module after content was created or you temporarily disabled this module, you'll want to execute this action to add the missing files. hint: Output all content from the DB to the local disk. If you enabled this module after content was created or you temporarily disabled this module, you'll want to execute this action to add the missing files.
icon: downloads icon: downloads
- handler: backup backup:
label: Create Backup label: Create Backup
hint: Will create a manual backup archive at this point in time, in a subfolder named _manual, from the contents currently on disk. hint: Will create a manual backup archive at this point in time, in a subfolder named _manual, from the contents currently on disk.
icon: archive-folder icon: archive-folder
- handler: importAll importAll:
label: Import Everything label: Import Everything
hint: Will import all content currently in the local disk folder. hint: Will import all content currently in the local disk folder.
icon: database-daily-import icon: database-daily-import
...@@ -59,7 +59,7 @@ props: ...@@ -59,7 +59,7 @@ props:
default: storage.google.com default: storage.google.com
order: 5 order: 5
actions: actions:
- handler: exportAll exportAll:
label: Export All DB Assets to GCS label: Export All DB Assets to GCS
hint: Output all content from the DB to Google Cloud Storage, overwriting any existing data. If you enabled Google Cloud Storage after content was created or you temporarily disabled it, you'll want to execute this action to add the missing content. hint: Output all content from the DB to Google Cloud Storage, overwriting any existing data. If you enabled Google Cloud Storage after content was created or you temporarily disabled it, you'll want to execute this action to add the missing content.
icon: this-way-up icon: this-way-up
...@@ -134,19 +134,19 @@ props: ...@@ -134,19 +134,19 @@ props:
icon: run-command icon: run-command
order: 50 order: 50
actions: actions:
- handler: syncUntracked syncUntracked:
label: Add Untracked Changes label: Add Untracked Changes
hint: Output all content from the DB to the local Git repository to ensure all untracked content is saved. If you enabled Git after content was created or you temporarily disabled Git, you'll want to execute this action to add the missing untracked changes. hint: Output all content from the DB to the local Git repository to ensure all untracked content is saved. If you enabled Git after content was created or you temporarily disabled Git, you'll want to execute this action to add the missing untracked changes.
icon: database-daily-export icon: database-daily-export
- handler: sync sync:
label: Force Sync label: Force Sync
hint: Will trigger an immediate sync operation, regardless of the current sync schedule. The sync direction is respected. hint: Will trigger an immediate sync operation, regardless of the current sync schedule. The sync direction is respected.
icon: synchronize icon: synchronize
- handler: importAll importAll:
label: Import Everything label: Import Everything
hint: Will import all content currently in the local Git repository, regardless of the latest commit state. Useful for importing content from the remote repository created before git was enabled. hint: Will import all content currently in the local Git repository, regardless of the latest commit state. Useful for importing content from the remote repository created before git was enabled.
icon: database-daily-import icon: database-daily-import
- handler: purge purge:
label: Purge Local Repository label: Purge Local Repository
hint: If you have unrelated merge histories, clearing the local repository can resolve this issue. This will not affect the remote repository or perform any commit. hint: If you have unrelated merge histories, clearing the local repository can resolve this issue. This will not affect the remote repository or perform any commit.
icon: trash icon: trash
...@@ -43,7 +43,7 @@ props: ...@@ -43,7 +43,7 @@ props:
hint: The repository default branch. hint: The repository default branch.
icon: code-fork icon: code-fork
actions: actions:
- handler: exportAll exportAll:
label: Export All DB Assets to GitHub label: Export All DB Assets to GitHub
hint: Output all content from the DB to GitHub, overwriting any existing data. If you enabled GitHub after content was created or you temporarily disabled it, you'll want to execute this action to add the missing content. hint: Output all content from the DB to GitHub, overwriting any existing data. If you enabled GitHub after content was created or you temporarily disabled it, you'll want to execute this action to add the missing content.
icon: this-way-up icon: this-way-up
...@@ -153,7 +153,7 @@ props: ...@@ -153,7 +153,7 @@ props:
if: if:
- { key: 'mode', eq: 'custom' } - { key: 'mode', eq: 'custom' }
actions: actions:
- handler: exportAll exportAll:
label: Export All DB Assets to S3 label: Export All DB Assets to S3
hint: Output all content from the DB to S3, overwriting any existing data. If you enabled S3 after content was created or you temporarily disabled it, you'll want to execute this action to add the missing content. hint: Output all content from the DB to S3, overwriting any existing data. If you enabled S3 after content was created or you temporarily disabled it, you'll want to execute this action to add the missing content.
icon: this-way-up icon: this-way-up
...@@ -87,7 +87,7 @@ props: ...@@ -87,7 +87,7 @@ props:
hint: Base directory where files will be transferred to. The path must already exists and be writable by the user. hint: Base directory where files will be transferred to. The path must already exists and be writable by the user.
icon: symlink-directory icon: symlink-directory
actions: actions:
- handler: exportAll exportAll:
label: Export All DB Assets to Remote label: Export All DB Assets to Remote
hint: Output all content from the DB to the remote SSH server, overwriting any existing data. If you enabled SFTP after content was created or you temporarily disabled it, you'll want to execute this action to add the missing content. hint: Output all content from the DB to the remote SSH server, overwriting any existing data. If you enabled SFTP after content was created or you temporarily disabled it, you'll want to execute this action to add the missing content.
icon: this-way-up icon: this-way-up
......
import { boot } from 'quasar/wrappers' import { boot } from 'quasar/wrappers'
import BlueprintIcon from '../components/BlueprintIcon.vue' import BlueprintIcon from '../components/BlueprintIcon.vue'
import StatusLight from '../components/StatusLight.vue'
import VNetworkGraph from 'v-network-graph' import VNetworkGraph from 'v-network-graph'
export default boot(({ app }) => { export default boot(({ app }) => {
app.component('BlueprintIcon', BlueprintIcon) app.component('BlueprintIcon', BlueprintIcon)
app.component('StatusLight', StatusLight)
app.use(VNetworkGraph) app.use(VNetworkGraph)
}) })
<template lang='pug'>
.status-light(:class='cssClasses')
</template>
<script setup>
import { computed } from 'vue'
// PROPS
const props = defineProps({
pulse: {
type: Boolean,
default: false
},
color: {
type: String,
default: ''
}
})
// COMPUTED
const cssClasses = computed(() => {
return `${props.color} ${props.pulse && 'pulsate'}`
})
</script>
<style lang="scss">
.status-light {
display: block;
width: 5px;
height: 100%;
min-height: 5px;
border-radius: 5px;
color: $grey-5;
background-color: currentColor;
background-image: linear-gradient(to bottom, transparent, rgba(255,255,255,.4));
&.negative {
color: $negative;
}
&.positive {
color: $positive;
}
&.warning {
color: $warning;
}
&.pulsate {
animation: status-light-pulsate 2s ease infinite;
}
}
@keyframes status-light-pulsate {
0% {
box-shadow: 0 0 5px 0 currentColor;
}
50% {
box-shadow: 0 0 5px 2px currentColor;
}
100% {
box-shadow: 0 0 5px 0 currentColor;
}
}
</style>
...@@ -1466,5 +1466,6 @@ ...@@ -1466,5 +1466,6 @@
"admin.api.key": "API Key", "admin.api.key": "API Key",
"admin.api.createSuccess": "API Key created successfully.", "admin.api.createSuccess": "API Key created successfully.",
"admin.api.revoked": "Revoked", "admin.api.revoked": "Revoked",
"admin.api.revokedHint": "This key has been revoked and can no longer be used." "admin.api.revokedHint": "This key has been revoked and can no longer be used.",
"admin.storage.setupRequired": "Setup required"
} }
...@@ -132,6 +132,8 @@ q-layout.admin(view='hHh Lpr lff') ...@@ -132,6 +132,8 @@ q-layout.admin(view='hHh Lpr lff')
q-item-section(avatar) q-item-section(avatar)
q-icon(name='img:/_assets/icons/fluent-rest-api.svg') q-icon(name='img:/_assets/icons/fluent-rest-api.svg')
q-item-section {{ t('admin.api.title') }} q-item-section {{ t('admin.api.title') }}
q-item-section(side)
status-light(:color='adminStore.info.isApiEnabled ? `positive` : `negative`')
q-item(to='/_admin/audit', v-ripple, active-class='bg-primary text-white', disabled) q-item(to='/_admin/audit', v-ripple, active-class='bg-primary text-white', disabled)
q-item-section(avatar) q-item-section(avatar)
q-icon(name='img:/_assets/icons/fluent-event-log.svg') q-icon(name='img:/_assets/icons/fluent-event-log.svg')
...@@ -144,6 +146,8 @@ q-layout.admin(view='hHh Lpr lff') ...@@ -144,6 +146,8 @@ q-layout.admin(view='hHh Lpr lff')
q-item-section(avatar) q-item-section(avatar)
q-icon(name='img:/_assets/icons/fluent-message-settings.svg') q-icon(name='img:/_assets/icons/fluent-message-settings.svg')
q-item-section {{ t('admin.mail.title') }} q-item-section {{ t('admin.mail.title') }}
q-item-section(side)
status-light(:color='adminStore.info.isMailConfigured ? `positive` : `warning`')
q-item(to='/_admin/security', v-ripple, active-class='bg-primary text-white') q-item(to='/_admin/security', v-ripple, active-class='bg-primary text-white')
q-item-section(avatar) q-item-section(avatar)
q-icon(name='img:/_assets/icons/fluent-protect.svg') q-icon(name='img:/_assets/icons/fluent-protect.svg')
...@@ -156,6 +160,8 @@ q-layout.admin(view='hHh Lpr lff') ...@@ -156,6 +160,8 @@ q-layout.admin(view='hHh Lpr lff')
q-item-section(avatar) q-item-section(avatar)
q-icon(name='img:/_assets/icons/fluent-processor.svg') q-icon(name='img:/_assets/icons/fluent-processor.svg')
q-item-section {{ t('admin.system.title') }} q-item-section {{ t('admin.system.title') }}
q-item-section(side)
status-light(:color='adminStore.isVersionLatest ? `positive` : `warning`')
q-item(to='/_admin/utilities', v-ripple, active-class='bg-primary text-white') q-item(to='/_admin/utilities', v-ripple, active-class='bg-primary text-white')
q-item-section(avatar) q-item-section(avatar)
q-icon(name='img:/_assets/icons/fluent-swiss-army-knife.svg') q-icon(name='img:/_assets/icons/fluent-swiss-army-knife.svg')
......
...@@ -41,14 +41,12 @@ q-page.admin-mail ...@@ -41,14 +41,12 @@ q-page.admin-mail
clickable clickable
) )
q-item-section(side) q-item-section(side)
q-icon( q-icon(:name='`img:` + str.strategy.icon')
:name='`img:` + str.strategy.icon'
)
q-item-section q-item-section
q-item-label {{str.displayName}} q-item-label {{str.displayName}}
q-item-label(caption) {{str.strategy.title}} q-item-label(caption) {{str.strategy.title}}
q-item-section(side) q-item-section(side)
q-spinner-rings(:color='str.isEnabled ? `positive` : `negative`', size='sm') status-light(:color='str.isEnabled ? `positive` : `negative`', :pulse='str.isEnabled')
q-btn.q-mt-sm.full-width( q-btn.q-mt-sm.full-width(
color='primary' color='primary'
icon='las la-plus' icon='las la-plus'
...@@ -499,7 +497,7 @@ function addStrategy (str) { ...@@ -499,7 +497,7 @@ function addStrategy (str) {
config: transform(str.props, (cfg, v, k) => { config: transform(str.props, (cfg, v, k) => {
cfg[k] = v.default cfg[k] = v.default
}, {}), }, {}),
isEnabled: true, isEnabled: false,
displayName: str.title, displayName: str.title,
selfRegistration: false, selfRegistration: false,
domainWhitelist: [], domainWhitelist: [],
......
...@@ -89,11 +89,13 @@ q-page.admin-dashboard ...@@ -89,11 +89,13 @@ q-page.admin-dashboard
) )
.col-12 .col-12
q-banner.bg-positive.text-white( q-banner.bg-positive.text-white(
:class='adminStore.isVersionLatest ? `bg-positive` : `bg-warning`'
inline-actions inline-actions
rounded rounded
) )
i.las.la-check.q-mr-sm i.las.la-check.q-mr-sm
span.text-weight-medium Your Wiki.js server is running the latest version! span.text-weight-medium(v-if='adminStore.isVersionLatest') Your Wiki.js server is running the latest version!
span.text-weight-medium(v-else) A new version of Wiki.js is available. Please update to the latest version.
template(#action) template(#action)
q-btn( q-btn(
flat flat
......
import { defineStore } from 'pinia' import { defineStore } from 'pinia'
import gql from 'graphql-tag' import gql from 'graphql-tag'
import { cloneDeep } from 'lodash-es' import { clone, cloneDeep } from 'lodash-es'
import semverGte from 'semver/functions/gte'
/* global APOLLO_CLIENT */ /* global APOLLO_CLIENT */
...@@ -13,7 +14,9 @@ export const useAdminStore = defineStore('admin', { ...@@ -13,7 +14,9 @@ export const useAdminStore = defineStore('admin', {
groupsTotal: 0, groupsTotal: 0,
pagesTotal: 0, pagesTotal: 0,
usersTotal: 0, usersTotal: 0,
loginsPastDay: 0 loginsPastDay: 0,
isApiEnabled: false,
isMailConfigured: false
}, },
overlay: null, overlay: null,
overlayOpts: {}, overlayOpts: {},
...@@ -22,7 +25,14 @@ export const useAdminStore = defineStore('admin', { ...@@ -22,7 +25,14 @@ export const useAdminStore = defineStore('admin', {
{ code: 'en', name: 'English' } { code: 'en', name: 'English' }
] ]
}), }),
getters: {}, getters: {
isVersionLatest: (state) => {
if (!state.info.currentVersion || !state.info.latestVersion || state.info.currentVersion === 'n/a' || state.info.latestVersion === 'n/a') {
return false
}
return semverGte(state.info.currentVersion, state.info.latestVersion)
}
},
actions: { actions: {
async fetchSites () { async fetchSites () {
const resp = await APOLLO_CLIENT.query({ const resp = await APOLLO_CLIENT.query({
...@@ -47,16 +57,24 @@ export const useAdminStore = defineStore('admin', { ...@@ -47,16 +57,24 @@ export const useAdminStore = defineStore('admin', {
const resp = await APOLLO_CLIENT.query({ const resp = await APOLLO_CLIENT.query({
query: gql` query: gql`
query getAdminInfo { query getAdminInfo {
apiState
systemInfo { systemInfo {
groupsTotal groupsTotal
usersTotal usersTotal
currentVersion
latestVersion
mailConfigured
} }
} }
`, `,
fetchPolicy: 'network-only' fetchPolicy: 'network-only'
}) })
this.info.groupsTotal = cloneDeep(resp?.data?.systemInfo.groupsTotal ?? 0) this.info.groupsTotal = clone(resp?.data?.systemInfo?.groupsTotal ?? 0)
this.info.usersTotal = cloneDeep(resp?.data?.systemInfo.usersTotal ?? 0) this.info.usersTotal = clone(resp?.data?.systemInfo?.usersTotal ?? 0)
this.info.currentVersion = clone(resp?.data?.systemInfo?.currentVersion ?? 'n/a')
this.info.latestVersion = clone(resp?.data?.systemInfo?.latestVersion ?? 'n/a')
this.info.isApiEnabled = clone(resp?.data?.apiState ?? false)
this.info.isMailConfigured = clone(resp?.data?.systemInfo?.mailConfigured ?? false)
} }
} }
}) })
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment