Commit d0dc300a authored by Nick's avatar Nick

feat: docker auto upgrade

parent 766eebbe
...@@ -27,6 +27,16 @@ ...@@ -27,6 +27,16 @@
v-list-item-subtitle {{ info.latestVersion }} v-list-item-subtitle {{ info.latestVersion }}
v-list-item-action v-list-item-action
v-list-item-action-text {{ $t('admin:system.published') }} {{ info.latestVersionReleaseDate | moment('from') }} v-list-item-action-text {{ $t('admin:system.published') }} {{ info.latestVersionReleaseDate | moment('from') }}
v-card-actions(v-if='upgradeCapable && !isLatestVersion && info.platform === `docker`', :class='$vuetify.theme.dark ? `grey darken-3-d5` : `indigo lighten-5`')
.caption.indigo--text.pl-3(:class='$vuetify.theme.dark ? `text--lighten-4` : ``') Wiki.js can perform the upgrade to the latest version for you.
v-spacer
v-btn.px-3(
color='indigo'
dark
@click='performUpgrade'
)
v-icon(left) mdi-upload
span Perform Upgrade
v-card.mt-4.animated.fadeInUp.wait-p2s v-card.mt-4.animated.fadeInUp.wait-p2s
v-subheader {{ $t('admin:system.hostInfo') }} v-subheader {{ $t('admin:system.hostInfo') }}
...@@ -92,24 +102,58 @@ ...@@ -92,24 +102,58 @@
v-list-item-subtitle {{ info.dbHost }} v-list-item-subtitle {{ info.dbHost }}
v-alert.mt-3.mx-4(:value='isDbLimited', color='deep-orange darken-2', icon='mdi-alert', dark) {{ $t('admin:system.dbPartialSupport') }} v-alert.mt-3.mx-4(:value='isDbLimited', color='deep-orange darken-2', icon='mdi-alert', dark) {{ $t('admin:system.dbPartialSupport') }}
v-dialog(
v-model='isUpgrading'
persistent
width='450'
)
v-card.blue.darken-5(dark)
v-card-text.text-center.pa-10
self-building-square-spinner(
:animation-duration='4000'
:size='40'
color='#FFF'
style='margin: 0 auto;'
)
.body-2.mt-5.blue--text.text--lighten-4 Your Wiki.js container is being upgraded...
.caption.blue--text.text--lighten-2 Please wait
v-progress-linear.mt-5(
color='blue lighten-2'
:value='upgradeProgress'
:buffer-value='upgradeProgress'
rounded
:stream='isUpgradingStarted'
query
:indeterminate='!isUpgradingStarted'
)
</template> </template>
<script> <script>
import _ from 'lodash' import _ from 'lodash'
import { SelfBuildingSquareSpinner } from 'epic-spinners'
import systemInfoQuery from 'gql/admin/system/system-query-info.gql' import systemInfoQuery from 'gql/admin/system/system-query-info.gql'
import performUpgradeMutation from 'gql/admin/system/system-mutation-upgrade.gql'
export default { export default {
data() { components: {
SelfBuildingSquareSpinner
},
data () {
return { return {
isUpgrading: false,
isUpgradingStarted: false,
upgradeProgress: 0,
info: {} info: {}
} }
}, },
computed: { computed: {
dbVersion() { dbVersion () {
return _.get(this.info, 'dbVersion', '').replace(/(?:\r\n|\r|\n)/g, '<br />') return _.get(this.info, 'dbVersion', '').replace(/(?:\r\n|\r|\n)/g, '<br />')
}, },
platformLogo() { platformLogo () {
switch (this.info.platform) { switch (this.info.platform) {
case 'docker': case 'docker':
return 'mdi-docker' return 'mdi-docker'
...@@ -127,18 +171,49 @@ export default { ...@@ -127,18 +171,49 @@ export default {
return '' return ''
} }
}, },
isDbLimited() { isDbLimited () {
return this.info.dbType === 'MySQL' && this.dbVersion.indexOf('5.') === 0 return this.info.dbType === 'MySQL' && this.dbVersion.indexOf('5.') === 0
},
isLatestVersion () {
return this.info.currentVersion === this.info.latestVersion
} }
}, },
methods: { methods: {
async refresh() { async refresh () {
await this.$apollo.queries.info.refetch() await this.$apollo.queries.info.refetch()
this.$store.commit('showNotification', { this.$store.commit('showNotification', {
message: this.$t('admin:system.refreshSuccess'), message: this.$t('admin:system.refreshSuccess'),
style: 'success', style: 'success',
icon: 'cached' icon: 'cached'
}) })
},
async performUpgrade () {
this.isUpgrading = true
this.isUpgradingStarted = false
this.upgradeProgress = 0
this.$store.commit(`loadingStart`, 'admin-system-upgrade')
try {
const respRaw = await this.$apollo.mutate({
mutation: performUpgradeMutation
})
const resp = _.get(respRaw, 'data.system.performUpgrade.responseResult', {})
if (resp.succeeded) {
this.isUpgradingStarted = true
let progressInterval = setInterval(() => {
this.upgradeProgress += 0.83
}, 500)
_.delay(() => {
clearInterval(progressInterval)
window.location.reload(true)
}, 60000)
} else {
throw new Error(resp.message)
}
} catch (err) {
this.$store.commit('pushGraphError', err)
this.$store.commit(`loadingStop`, 'admin-system-upgrade')
this.isUpgrading = false
}
} }
}, },
apollo: { apollo: {
......
...@@ -169,16 +169,16 @@ ...@@ -169,16 +169,16 @@
v-toolbar(dense, flat, color='light-green darken-3') v-toolbar(dense, flat, color='light-green darken-3')
v-spacer v-spacer
.caption.mr-1 or convert from .caption.mr-1 or convert from
v-btn.mx-1(depressed, color='light-green darken-2', @click='', disabled) v-btn.mx-1.animated.fadeInUp(depressed, color='light-green darken-2', @click='', disabled)
v-icon(left) mdi-alpha-a-circle v-icon(left) mdi-alpha-a-circle
.body-2.text-none AsciiDoc .body-2.text-none AsciiDoc
v-btn.mx-1(depressed, color='light-green darken-2', @click='', disabled) v-btn.mx-1.animated.fadeInUp.wait-p1s(depressed, color='light-green darken-2', @click='', disabled)
v-icon(left) mdi-alpha-c-circle v-icon(left) mdi-alpha-c-circle
.body-2.text-none CREOLE .body-2.text-none CREOLE
v-btn.mx-1(depressed, color='light-green darken-2', @click='', disabled) v-btn.mx-1.animated.fadeInUp.wait-p2s(depressed, color='light-green darken-2', @click='', disabled)
v-icon(left) mdi-alpha-t-circle v-icon(left) mdi-alpha-t-circle
.body-2.text-none Textile .body-2.text-none Textile
v-btn.mx-1(depressed, color='light-green darken-2', @click='', disabled) v-btn.mx-1.animated.fadeInUp.wait-p3s(depressed, color='light-green darken-2', @click='', disabled)
v-icon(left) mdi-alpha-w-circle v-icon(left) mdi-alpha-w-circle
.body-2.text-none WikiText .body-2.text-none WikiText
v-spacer v-spacer
......
...@@ -168,10 +168,12 @@ export default { ...@@ -168,10 +168,12 @@ export default {
}).then(res => res.json()) }).then(res => res.json())
if (resp.ok === true) { if (resp.ok === true) {
_.delay(() => {
this.success = true this.success = true
_.delay(() => { _.delay(() => {
window.location.assign('/login') window.location.assign('/login')
}, 3000) }, 3000)
}, 10000)
} else { } else {
this.error = true this.error = true
this.errorMessage = resp.error this.errorMessage = resp.error
......
mutation {
system {
performUpgrade {
responseResult {
succeeded
errorCode
slug
message
}
}
}
}
...@@ -2,19 +2,20 @@ query { ...@@ -2,19 +2,20 @@ query {
system { system {
info { info {
configFile configFile
cpuCores
currentVersion currentVersion
dbHost
dbType dbType
dbVersion dbVersion
dbHost hostname
latestVersion latestVersion
latestVersionReleaseDate latestVersionReleaseDate
nodeVersion
operatingSystem operatingSystem
platform platform
hostname
cpuCores
ramTotal ramTotal
upgradeCapable
workingDirectory workingDirectory
nodeVersion
} }
} }
} }
...@@ -60,6 +60,7 @@ ...@@ -60,6 +60,7 @@
"dependency-graph": "0.8.0", "dependency-graph": "0.8.0",
"diff": "4.0.1", "diff": "4.0.1",
"diff2html": "2.11.3", "diff2html": "2.11.3",
"dockerode": "2.5.8",
"dotize": "0.3.0", "dotize": "0.3.0",
"elasticsearch6": "npm:@elastic/elasticsearch@6", "elasticsearch6": "npm:@elastic/elasticsearch@6",
"elasticsearch7": "npm:@elastic/elasticsearch@7", "elasticsearch7": "npm:@elastic/elasticsearch@7",
......
...@@ -7,6 +7,7 @@ const path = require('path') ...@@ -7,6 +7,7 @@ const path = require('path')
const fs = require('fs-extra') const fs = require('fs-extra')
const moment = require('moment') const moment = require('moment')
const graphHelper = require('../../helpers/graph') const graphHelper = require('../../helpers/graph')
const Docker = require('dockerode')
/* global WIKI */ /* global WIKI */
...@@ -20,13 +21,13 @@ const dbTypes = { ...@@ -20,13 +21,13 @@ const dbTypes = {
module.exports = { module.exports = {
Query: { Query: {
async system() { return {} } async system () { return {} }
}, },
Mutation: { Mutation: {
async system() { return {} } async system () { return {} }
}, },
SystemQuery: { SystemQuery: {
flags() { flags () {
return _.transform(WIKI.config.flags, (result, value, key) => { return _.transform(WIKI.config.flags, (result, value, key) => {
result.push({ key, value }) result.push({ key, value })
}, []) }, [])
...@@ -34,7 +35,7 @@ module.exports = { ...@@ -34,7 +35,7 @@ module.exports = {
async info() { return {} } async info() { return {} }
}, },
SystemMutation: { SystemMutation: {
async updateFlags(obj, args, context) { async updateFlags (obj, args, context) {
WIKI.config.flags = _.transform(args.flags, (result, row) => { WIKI.config.flags = _.transform(args.flags, (result, row) => {
_.set(result, row.key, row.value) _.set(result, row.key, row.value)
}, {}) }, {})
...@@ -44,7 +45,7 @@ module.exports = { ...@@ -44,7 +45,7 @@ module.exports = {
responseResult: graphHelper.generateSuccess('System Flags applied successfully') responseResult: graphHelper.generateSuccess('System Flags applied successfully')
} }
}, },
async resetTelemetryClientId(obj, args, context) { async resetTelemetryClientId (obj, args, context) {
try { try {
WIKI.telemetry.generateClientId() WIKI.telemetry.generateClientId()
await WIKI.configSvc.saveToDb(['telemetry']) await WIKI.configSvc.saveToDb(['telemetry'])
...@@ -55,7 +56,7 @@ module.exports = { ...@@ -55,7 +56,7 @@ module.exports = {
return graphHelper.generateError(err) return graphHelper.generateError(err)
} }
}, },
async setTelemetry(obj, args, context) { async setTelemetry (obj, args, context) {
try { try {
_.set(WIKI.config, 'telemetry.isEnabled', args.enabled) _.set(WIKI.config, 'telemetry.isEnabled', args.enabled)
WIKI.telemetry.enabled = args.enabled WIKI.telemetry.enabled = args.enabled
...@@ -66,22 +67,44 @@ module.exports = { ...@@ -66,22 +67,44 @@ module.exports = {
} catch (err) { } catch (err) {
return graphHelper.generateError(err) return graphHelper.generateError(err)
} }
},
async performUpgrade (obj, args, context) {
try {
const dockerEngine = new Docker({ socketPath: '/var/run/docker.sock' })
await dockerEngine.run('containrrr/watchtower', ['--cleanup', '--run-once', 'wiki'], process.stdout, {
HostConfig: {
AutoRemove: true,
Mounts: [
{
Target: '/var/run/docker.sock',
Source: '/var/run/docker.sock',
Type: 'bind'
}
]
}
})
return {
responseResult: graphHelper.generateSuccess('Upgrade has started.')
}
} catch (err) {
return graphHelper.generateError(err)
}
} }
}, },
SystemInfo: { SystemInfo: {
configFile() { configFile () {
return path.join(process.cwd(), 'config.yml') return path.join(process.cwd(), 'config.yml')
}, },
cpuCores() { cpuCores () {
return os.cpus().length return os.cpus().length
}, },
currentVersion() { currentVersion () {
return WIKI.version return WIKI.version
}, },
dbType() { dbType () {
return _.get(dbTypes, WIKI.config.db.type, 'Unknown DB') return _.get(dbTypes, WIKI.config.db.type, 'Unknown DB')
}, },
async dbVersion() { async dbVersion () {
let version = 'Unknown Version' let version = 'Unknown Version'
switch (WIKI.config.db.type) { switch (WIKI.config.db.type) {
case 'mariadb': case 'mariadb':
...@@ -102,26 +125,26 @@ module.exports = { ...@@ -102,26 +125,26 @@ module.exports = {
} }
return version return version
}, },
dbHost() { dbHost () {
if (WIKI.config.db.type === 'sqlite') { if (WIKI.config.db.type === 'sqlite') {
return WIKI.config.db.storage return WIKI.config.db.storage
} else { } else {
return WIKI.config.db.host return WIKI.config.db.host
} }
}, },
hostname() { hostname () {
return os.hostname() return os.hostname()
}, },
latestVersion() { latestVersion () {
return WIKI.system.updates.version return WIKI.system.updates.version
}, },
latestVersionReleaseDate() { latestVersionReleaseDate () {
return moment.utc(WIKI.system.updates.releaseDate) return moment.utc(WIKI.system.updates.releaseDate)
}, },
nodeVersion() { nodeVersion () {
return process.version.substr(1) return process.version.substr(1)
}, },
async operatingSystem() { async operatingSystem () {
let osLabel = `${os.type()} (${os.platform()}) ${os.release()} ${os.arch()}` let osLabel = `${os.type()} (${os.platform()}) ${os.release()} ${os.arch()}`
if (os.platform() === 'linux') { if (os.platform() === 'linux') {
const osInfo = await getos() const osInfo = await getos()
...@@ -136,27 +159,30 @@ module.exports = { ...@@ -136,27 +159,30 @@ module.exports = {
} }
return os.platform() return os.platform()
}, },
ramTotal() { ramTotal () {
return filesize(os.totalmem()) return filesize(os.totalmem())
}, },
telemetry() { telemetry () {
return WIKI.telemetry.enabled return WIKI.telemetry.enabled
}, },
telemetryClientId() { telemetryClientId () {
return WIKI.config.telemetry.clientId return WIKI.config.telemetry.clientId
}, },
workingDirectory() { async upgradeCapable () {
return fs.pathExists('/var/run/docker.sock')
},
workingDirectory () {
return process.cwd() return process.cwd()
}, },
async groupsTotal() { async groupsTotal () {
const total = await WIKI.models.groups.query().count('* as total').first().pluck('total') const total = await WIKI.models.groups.query().count('* as total').first().pluck('total')
return _.toSafeInteger(total) return _.toSafeInteger(total)
}, },
async pagesTotal() { async pagesTotal () {
const total = await WIKI.models.pages.query().count('* as total').first().pluck('total') const total = await WIKI.models.pages.query().count('* as total').first().pluck('total')
return _.toSafeInteger(total) return _.toSafeInteger(total)
}, },
async usersTotal() { async usersTotal () {
const total = await WIKI.models.users.query().count('* as total').first().pluck('total') const total = await WIKI.models.users.query().count('* as total').first().pluck('total')
return _.toSafeInteger(total) return _.toSafeInteger(total)
} }
......
...@@ -33,6 +33,8 @@ type SystemMutation { ...@@ -33,6 +33,8 @@ type SystemMutation {
setTelemetry( setTelemetry(
enabled: Boolean! enabled: Boolean!
): DefaultResponse @auth(requires: ["manage:system"]) ): DefaultResponse @auth(requires: ["manage:system"])
performUpgrade: DefaultResponse @auth(requires: ["manage:system"])
} }
# ----------------------------------------------- # -----------------------------------------------
...@@ -67,6 +69,7 @@ type SystemInfo { ...@@ -67,6 +69,7 @@ type SystemInfo {
ramTotal: String @auth(requires: ["manage:system"]) ramTotal: String @auth(requires: ["manage:system"])
telemetry: Boolean @auth(requires: ["manage:system"]) telemetry: Boolean @auth(requires: ["manage:system"])
telemetryClientId: String @auth(requires: ["manage:system"]) telemetryClientId: String @auth(requires: ["manage:system"])
upgradeCapable: Boolean @auth(requires: ["manage:system"])
usersTotal: Int @auth(requires: ["manage:system", "manage:navigation", "manage:groups", "write:groups", "manage:users", "write:users"]) usersTotal: Int @auth(requires: ["manage:system", "manage:navigation", "manage:groups", "write:groups", "manage:users", "write:users"])
workingDirectory: String @auth(requires: ["manage:system"]) workingDirectory: String @auth(requires: ["manage:system"])
} }
This diff was suppressed by a .gitattributes entry.
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