feat: storage modes + git improvements
This commit is contained in:
parent
d5028b1bce
commit
f74c0caac2
@ -19,7 +19,7 @@
|
|||||||
) This is a system group. Some permissions cannot be modified.
|
) This is a system group. Some permissions cannot be modified.
|
||||||
v-container.px-3.pb-3.pt-0(fluid, grid-list-md)
|
v-container.px-3.pb-3.pt-0(fluid, grid-list-md)
|
||||||
v-layout(row, wrap)
|
v-layout(row, wrap)
|
||||||
v-flex(xs12, md6, lg4, v-for='pmGroup in permissions')
|
v-flex(xs12, md6, lg4, v-for='pmGroup in permissions', :key='pmGroup.category')
|
||||||
v-card.md2(flat, :class='$vuetify.dark ? "grey darken-3-d5" : "white"')
|
v-card.md2(flat, :class='$vuetify.dark ? "grey darken-3-d5" : "white"')
|
||||||
v-subheader {{pmGroup.category}}
|
v-subheader {{pmGroup.category}}
|
||||||
v-card-text.pt-0
|
v-card-text.pt-0
|
||||||
|
@ -20,11 +20,14 @@
|
|||||||
v-tab(v-for='tgt in activeTargets', :key='tgt.key') {{ tgt.title }}
|
v-tab(v-for='tgt in activeTargets', :key='tgt.key') {{ tgt.title }}
|
||||||
|
|
||||||
v-tab-item(key='settings', :transition='false', :reverse-transition='false')
|
v-tab-item(key='settings', :transition='false', :reverse-transition='false')
|
||||||
v-card.pa-3(flat, tile)
|
v-container.pa-3(fluid, grid-list-md)
|
||||||
|
v-layout(row, wrap)
|
||||||
|
v-flex(xs12, md6)
|
||||||
.body-2.grey--text.text--darken-1 Select which storage targets to enable:
|
.body-2.grey--text.text--darken-1 Select which storage targets to enable:
|
||||||
.caption.grey--text.pb-2 Some storage targets require additional configuration in their dedicated tab (when selected).
|
.caption.grey--text.pb-2 Some storage targets require additional configuration in their dedicated tab (when selected).
|
||||||
v-form
|
v-form
|
||||||
v-checkbox.my-0(
|
v-checkbox.my-0(
|
||||||
|
:disabled='!tgt.isAvailable'
|
||||||
v-for='tgt in targets'
|
v-for='tgt in targets'
|
||||||
v-model='tgt.isEnabled'
|
v-model='tgt.isEnabled'
|
||||||
:key='tgt.key'
|
:key='tgt.key'
|
||||||
@ -32,6 +35,18 @@
|
|||||||
color='primary'
|
color='primary'
|
||||||
hide-details
|
hide-details
|
||||||
)
|
)
|
||||||
|
v-flex(xs12, md6)
|
||||||
|
.pa-3.grey.radius-7(:class='$vuetify.dark ? "darken-4" : "lighten-5"')
|
||||||
|
.body-2.grey--text.text--darken-1 Advanced Settings
|
||||||
|
v-text-field.mt-3.md2(
|
||||||
|
v-model='syncInterval'
|
||||||
|
outline
|
||||||
|
background-color='grey lighten-2'
|
||||||
|
prepend-icon='schedule'
|
||||||
|
label='Synchronization Interval'
|
||||||
|
hint='For performance reasons, some storage targets synchronize changes on an interval-based schedule, instead of on every change. Define at which interval should the synchronization occur for all storage targets.'
|
||||||
|
persistent-hint
|
||||||
|
)
|
||||||
|
|
||||||
v-tab-item(v-for='(tgt, n) in activeTargets', :key='tgt.key', :transition='false', :reverse-transition='false')
|
v-tab-item(v-for='(tgt, n) in activeTargets', :key='tgt.key', :transition='false', :reverse-transition='false')
|
||||||
v-card.pa-3(flat, tile)
|
v-card.pa-3(flat, tile)
|
||||||
@ -58,7 +73,7 @@
|
|||||||
persistent-hint
|
persistent-hint
|
||||||
:class='cfg.value.hint ? "mb-2" : ""'
|
:class='cfg.value.hint ? "mb-2" : ""'
|
||||||
)
|
)
|
||||||
v-switch(
|
v-switch.mb-3(
|
||||||
v-else-if='cfg.value.type === "boolean"'
|
v-else-if='cfg.value.type === "boolean"'
|
||||||
:key='cfg.key'
|
:key='cfg.key'
|
||||||
:label='cfg.value.title'
|
:label='cfg.value.title'
|
||||||
@ -89,23 +104,26 @@
|
|||||||
label='Bi-directional'
|
label='Bi-directional'
|
||||||
color='primary'
|
color='primary'
|
||||||
value='sync'
|
value='sync'
|
||||||
|
:disabled='tgt.supportedModes.indexOf(`sync`) < 0'
|
||||||
)
|
)
|
||||||
v-radio(
|
v-radio(
|
||||||
label='Push to target'
|
label='Push to target'
|
||||||
color='primary'
|
color='primary'
|
||||||
value='push'
|
value='push'
|
||||||
|
:disabled='tgt.supportedModes.indexOf(`push`) < 0'
|
||||||
)
|
)
|
||||||
v-radio(
|
v-radio(
|
||||||
label='Pull from target'
|
label='Pull from target'
|
||||||
color='primary'
|
color='primary'
|
||||||
value='pull'
|
value='pull'
|
||||||
|
:disabled='tgt.supportedModes.indexOf(`pull`) < 0'
|
||||||
)
|
)
|
||||||
.body-1.ml-3
|
.body-1.ml-3
|
||||||
strong Bi-directional
|
strong Bi-directional #[em.red--text.text--lighten-2(v-if='tgt.supportedModes.indexOf(`sync`) < 0') Unsupported]
|
||||||
.pb-3 In bi-directional mode, content is first pulled from the storage target. Any newer content overwrites local content. New content since last sync is then pushed to the storage target, overwriting any content on target if present.
|
.pb-3 In bi-directional mode, content is first pulled from the storage target. Any newer content overwrites local content. New content since last sync is then pushed to the storage target, overwriting any content on target if present.
|
||||||
strong Push to target
|
strong Push to target #[em.red--text.text--lighten-2(v-if='tgt.supportedModes.indexOf(`push`) < 0') Unsupported]
|
||||||
.pb-3 Content is always pushed to the storage target, overwriting any existing content. This is the default and safest choice for backup scenarios.
|
.pb-3 Content is always pushed to the storage target, overwriting any existing content. This is safest choice for backup scenarios.
|
||||||
strong Pull from target
|
strong Pull from target #[em.red--text.text--lighten-2(v-if='tgt.supportedModes.indexOf(`pull`) < 0') Unsupported]
|
||||||
.pb-3 Content is always pulled from the storage target, overwriting any local content which already exists. This choice is usually reserved for single-use content import. Caution with this option as any local content will always be overwritten!
|
.pb-3 Content is always pulled from the storage target, overwriting any local content which already exists. This choice is usually reserved for single-use content import. Caution with this option as any local content will always be overwritten!
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -121,6 +139,7 @@ export default {
|
|||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
syncInterval: '5m',
|
||||||
targets: []
|
targets: []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -163,7 +182,13 @@ export default {
|
|||||||
targets: {
|
targets: {
|
||||||
query: targetsQuery,
|
query: targetsQuery,
|
||||||
fetchPolicy: 'network-only',
|
fetchPolicy: 'network-only',
|
||||||
update: (data) => _.cloneDeep(data.storage.targets).map(str => ({...str, config: str.config.map(cfg => ({...cfg, value: JSON.parse(cfg.value)}))})),
|
update: (data) => _.cloneDeep(data.storage.targets).map(str => ({
|
||||||
|
...str,
|
||||||
|
config: _.sortBy(str.config.map(cfg => ({
|
||||||
|
...cfg,
|
||||||
|
value: JSON.parse(cfg.value)
|
||||||
|
})), [t => t.value.order])
|
||||||
|
})),
|
||||||
watchLoading (isLoading) {
|
watchLoading (isLoading) {
|
||||||
this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-storage-refresh')
|
this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-storage-refresh')
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,14 @@
|
|||||||
query {
|
query {
|
||||||
storage {
|
storage {
|
||||||
targets(orderBy: "title ASC") {
|
targets(orderBy: "title ASC") {
|
||||||
|
isAvailable
|
||||||
isEnabled
|
isEnabled
|
||||||
key
|
key
|
||||||
title
|
title
|
||||||
description
|
description
|
||||||
logo
|
logo
|
||||||
website
|
website
|
||||||
|
supportedModes
|
||||||
mode
|
mode
|
||||||
config {
|
config {
|
||||||
key
|
key
|
||||||
|
@ -83,7 +83,7 @@
|
|||||||
"i18next-localstorage-cache": "1.1.1",
|
"i18next-localstorage-cache": "1.1.1",
|
||||||
"i18next-node-fs-backend": "2.1.1",
|
"i18next-node-fs-backend": "2.1.1",
|
||||||
"image-size": "0.7.1",
|
"image-size": "0.7.1",
|
||||||
"ioredis": "4.5.1",
|
"ioredis": "4.6.2",
|
||||||
"js-base64": "2.5.1",
|
"js-base64": "2.5.1",
|
||||||
"js-binary": "1.2.0",
|
"js-binary": "1.2.0",
|
||||||
"js-yaml": "3.12.1",
|
"js-yaml": "3.12.1",
|
||||||
@ -266,7 +266,7 @@
|
|||||||
"vue-codemirror": "4.0.6",
|
"vue-codemirror": "4.0.6",
|
||||||
"vue-hot-reload-api": "2.3.1",
|
"vue-hot-reload-api": "2.3.1",
|
||||||
"vue-loader": "15.6.2",
|
"vue-loader": "15.6.2",
|
||||||
"vue-material-design-icons": "2.6.0",
|
"vue-material-design-icons": "3.0.0",
|
||||||
"vue-moment": "4.0.0",
|
"vue-moment": "4.0.0",
|
||||||
"vue-router": "3.0.2",
|
"vue-router": "3.0.2",
|
||||||
"vue-simple-breakpoints": "1.0.3",
|
"vue-simple-breakpoints": "1.0.3",
|
||||||
|
@ -61,6 +61,7 @@ module.exports = {
|
|||||||
await WIKI.models.storage.refreshTargetsFromDisk()
|
await WIKI.models.storage.refreshTargetsFromDisk()
|
||||||
|
|
||||||
await WIKI.auth.activateStrategies()
|
await WIKI.auth.activateStrategies()
|
||||||
|
await WIKI.models.storage.initTargets()
|
||||||
await WIKI.queue.start()
|
await WIKI.queue.start()
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
|
@ -36,12 +36,14 @@ type StorageMutation {
|
|||||||
# -----------------------------------------------
|
# -----------------------------------------------
|
||||||
|
|
||||||
type StorageTarget {
|
type StorageTarget {
|
||||||
|
isAvailable: Boolean!
|
||||||
isEnabled: Boolean!
|
isEnabled: Boolean!
|
||||||
key: String!
|
key: String!
|
||||||
title: String!
|
title: String!
|
||||||
description: String
|
description: String
|
||||||
logo: String
|
logo: String
|
||||||
website: String
|
website: String
|
||||||
|
supportedModes: [String]
|
||||||
mode: String
|
mode: String
|
||||||
config: [KeyValuePair]
|
config: [KeyValuePair]
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,8 @@ module.exports = {
|
|||||||
type: (value.type || value).toLowerCase(),
|
type: (value.type || value).toLowerCase(),
|
||||||
title: value.title || _.startCase(key),
|
title: value.title || _.startCase(key),
|
||||||
hint: value.hint || false,
|
hint: value.hint || false,
|
||||||
enum: value.enum || false
|
enum: value.enum || false,
|
||||||
|
order: value.order || 100
|
||||||
})
|
})
|
||||||
return result
|
return result
|
||||||
}, {})
|
}, {})
|
||||||
|
@ -224,7 +224,9 @@ module.exports = class Page extends Model {
|
|||||||
'pages.*',
|
'pages.*',
|
||||||
{
|
{
|
||||||
authorName: 'author.name',
|
authorName: 'author.name',
|
||||||
creatorName: 'creator.name'
|
authorEmail: 'author.email',
|
||||||
|
creatorName: 'creator.name',
|
||||||
|
creatorEmail: 'creator.email'
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
.joinRelation('author')
|
.joinRelation('author')
|
||||||
|
@ -48,6 +48,7 @@ module.exports = class Storage extends Model {
|
|||||||
}
|
}
|
||||||
WIKI.data.storage = diskTargets.map(target => ({
|
WIKI.data.storage = diskTargets.map(target => ({
|
||||||
...target,
|
...target,
|
||||||
|
isAvailable: _.get(target, 'isAvailable', false),
|
||||||
props: commonHelper.parseModuleProps(target.props)
|
props: commonHelper.parseModuleProps(target.props)
|
||||||
}))
|
}))
|
||||||
|
|
||||||
@ -58,7 +59,7 @@ module.exports = class Storage extends Model {
|
|||||||
newTargets.push({
|
newTargets.push({
|
||||||
key: target.key,
|
key: target.key,
|
||||||
isEnabled: false,
|
isEnabled: false,
|
||||||
mode: 'push',
|
mode: target.defaultMode || 'push',
|
||||||
config: _.transform(target.props, (result, value, key) => {
|
config: _.transform(target.props, (result, value, key) => {
|
||||||
_.set(result, key, value.default)
|
_.set(result, key, value.default)
|
||||||
return result
|
return result
|
||||||
@ -100,10 +101,9 @@ module.exports = class Storage extends Model {
|
|||||||
try {
|
try {
|
||||||
for(let target of targets) {
|
for(let target of targets) {
|
||||||
target.fn = require(`../modules/storage/${target.key}/storage`)
|
target.fn = require(`../modules/storage/${target.key}/storage`)
|
||||||
await target.fn.init.call({
|
target.fn.config = target.config
|
||||||
config: target.config,
|
target.fn.mode = target.mode
|
||||||
mode: target.mode
|
await target.fn.init()
|
||||||
})
|
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
WIKI.logger.warn(err)
|
WIKI.logger.warn(err)
|
||||||
@ -114,11 +114,7 @@ module.exports = class Storage extends Model {
|
|||||||
static async pageEvent({ event, page }) {
|
static async pageEvent({ event, page }) {
|
||||||
try {
|
try {
|
||||||
for(let target of targets) {
|
for(let target of targets) {
|
||||||
await target.fn[event].call({
|
await target.fn[event](page)
|
||||||
config: target.config,
|
|
||||||
mode: target.mode,
|
|
||||||
page
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
WIKI.logger.warn(err)
|
WIKI.logger.warn(err)
|
||||||
|
@ -4,10 +4,19 @@ description: Local storage on disk or network shares.
|
|||||||
author: requarks.io
|
author: requarks.io
|
||||||
logo: https://static.requarks.io/logo/local-fs.svg
|
logo: https://static.requarks.io/logo/local-fs.svg
|
||||||
website: https://wiki.js.org
|
website: https://wiki.js.org
|
||||||
|
isAvailable: true
|
||||||
supportedModes:
|
supportedModes:
|
||||||
- push
|
- push
|
||||||
|
defaultMode: push
|
||||||
props:
|
props:
|
||||||
path:
|
path:
|
||||||
type: String
|
type: String
|
||||||
title: Path
|
title: Path
|
||||||
hint: Absolute path without a trailing slash (e.g. /home/wiki/backup, C:\wiki\backup)
|
hint: Absolute path without a trailing slash (e.g. /home/wiki/backup, C:\wiki\backup)
|
||||||
|
order: 1
|
||||||
|
createDailyBackups:
|
||||||
|
type: Boolean
|
||||||
|
default: false
|
||||||
|
title: Create Daily Backups
|
||||||
|
hint: A tar.gz archive containing all content will be created daily in subfolder named _daily. Archives are kept for a month.
|
||||||
|
order: 2
|
||||||
|
@ -46,23 +46,32 @@ module.exports = {
|
|||||||
// not used
|
// not used
|
||||||
},
|
},
|
||||||
async init() {
|
async init() {
|
||||||
|
WIKI.logger.info('(STORAGE/DISK) Initializing...')
|
||||||
await fs.ensureDir(this.config.path)
|
await fs.ensureDir(this.config.path)
|
||||||
|
WIKI.logger.info('(STORAGE/DISK) Initialization completed.')
|
||||||
},
|
},
|
||||||
async created() {
|
async sync() {
|
||||||
const filePath = path.join(this.config.path, `${this.page.path}.${getFileExtension(this.page.contentType)}`)
|
// not used
|
||||||
await fs.outputFile(filePath, injectMetadata(this.page), 'utf8')
|
|
||||||
},
|
},
|
||||||
async updated() {
|
async created(page) {
|
||||||
const filePath = path.join(this.config.path, `${this.page.path}.${getFileExtension(this.page.contentType)}`)
|
WIKI.logger.info(`(STORAGE/DISK) Creating file ${page.path}...`)
|
||||||
await fs.outputFile(filePath, injectMetadata(this.page), 'utf8')
|
const filePath = path.join(this.config.path, `${page.path}.${getFileExtension(page.contentType)}`)
|
||||||
|
await fs.outputFile(filePath, injectMetadata(page), 'utf8')
|
||||||
},
|
},
|
||||||
async deleted() {
|
async updated(page) {
|
||||||
const filePath = path.join(this.config.path, `${this.page.path}.${getFileExtension(this.page.contentType)}`)
|
WIKI.logger.info(`(STORAGE/DISK) Updating file ${page.path}...`)
|
||||||
|
const filePath = path.join(this.config.path, `${page.path}.${getFileExtension(page.contentType)}`)
|
||||||
|
await fs.outputFile(filePath, injectMetadata(page), 'utf8')
|
||||||
|
},
|
||||||
|
async deleted(page) {
|
||||||
|
WIKI.logger.info(`(STORAGE/DISK) Deleting file ${page.path}...`)
|
||||||
|
const filePath = path.join(this.config.path, `${page.path}.${getFileExtension(page.contentType)}`)
|
||||||
await fs.unlink(filePath)
|
await fs.unlink(filePath)
|
||||||
},
|
},
|
||||||
async renamed() {
|
async renamed(page) {
|
||||||
const sourceFilePath = path.join(this.config.path, `${this.page.sourcePath}.${getFileExtension(this.page.contentType)}`)
|
WIKI.logger.info(`(STORAGE/DISK) Renaming file ${page.sourcePath} to ${page.destinationPath}...`)
|
||||||
const destinationFilePath = path.join(this.config.path, `${this.page.destinationPath}.${getFileExtension(this.page.contentType)}`)
|
const sourceFilePath = path.join(this.config.path, `${page.sourcePath}.${getFileExtension(page.contentType)}`)
|
||||||
|
const destinationFilePath = path.join(this.config.path, `${page.destinationPath}.${getFileExtension(page.contentType)}`)
|
||||||
await fs.move(sourceFilePath, destinationFilePath, { overwrite: true })
|
await fs.move(sourceFilePath, destinationFilePath, { overwrite: true })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,10 +4,12 @@ description: Git is a version control system for tracking changes in computer fi
|
|||||||
author: requarks.io
|
author: requarks.io
|
||||||
logo: https://static.requarks.io/logo/git-alt.svg
|
logo: https://static.requarks.io/logo/git-alt.svg
|
||||||
website: https://git-scm.com/
|
website: https://git-scm.com/
|
||||||
|
isAvailable: true
|
||||||
supportedModes:
|
supportedModes:
|
||||||
- sync
|
- sync
|
||||||
- push
|
- push
|
||||||
- pull
|
- pull
|
||||||
|
defaultMode: sync
|
||||||
props:
|
props:
|
||||||
authType:
|
authType:
|
||||||
type: String
|
type: String
|
||||||
@ -17,42 +19,52 @@ props:
|
|||||||
enum:
|
enum:
|
||||||
- 'basic'
|
- 'basic'
|
||||||
- 'ssh'
|
- 'ssh'
|
||||||
|
order: 1
|
||||||
repoUrl:
|
repoUrl:
|
||||||
type: String
|
type: String
|
||||||
title: Repository URI
|
title: Repository URI
|
||||||
hint: Git-compliant URI (e.g. git@github.com:org/repo.git for ssh, https://github.com/org/repo.git for basic)
|
hint: Git-compliant URI (e.g. git@github.com:org/repo.git for ssh, https://github.com/org/repo.git for basic)
|
||||||
|
order: 2
|
||||||
branch:
|
branch:
|
||||||
type: String
|
type: String
|
||||||
default: 'master'
|
default: 'master'
|
||||||
|
order: 3
|
||||||
verifySSL:
|
verifySSL:
|
||||||
type: Boolean
|
type: Boolean
|
||||||
default: true
|
default: true
|
||||||
title: Verify SSL Certificate
|
title: Verify SSL Certificate
|
||||||
hint: Some hosts requires SSL certificate checking to be disabled. Leave enabled for proper security.
|
hint: Some hosts requires SSL certificate checking to be disabled. Leave enabled for proper security.
|
||||||
|
order: 31
|
||||||
sshPrivateKeyPath:
|
sshPrivateKeyPath:
|
||||||
type: String
|
type: String
|
||||||
title: SSH Private Key Path
|
title: SSH Private Key Path
|
||||||
hint: SSH Authentication Only - Absolute path to the key. The key must NOT be passphrase-protected.
|
hint: SSH Authentication Only - Absolute path to the key. The key must NOT be passphrase-protected.
|
||||||
|
order: 10
|
||||||
basicUsername:
|
basicUsername:
|
||||||
type: String
|
type: String
|
||||||
title: Username
|
title: Username
|
||||||
hint: Basic Authentication Only
|
hint: Basic Authentication Only
|
||||||
|
order: 11
|
||||||
basicPassword:
|
basicPassword:
|
||||||
type: String
|
type: String
|
||||||
title: Password / PAT
|
title: Password / PAT
|
||||||
hint: Basic Authentication Only
|
hint: Basic Authentication Only
|
||||||
|
order: 12
|
||||||
localRepoPath:
|
localRepoPath:
|
||||||
type: String
|
type: String
|
||||||
title: Local Repository Path
|
title: Local Repository Path
|
||||||
default: './data/repo'
|
default: './data/repo'
|
||||||
hint: 'Path where the local git repository will be created.'
|
hint: 'Path where the local git repository will be created.'
|
||||||
|
order: 30
|
||||||
defaultEmail:
|
defaultEmail:
|
||||||
type: String
|
type: String
|
||||||
title: Default Author Email
|
title: Default Author Email
|
||||||
default: 'name@company.com'
|
default: 'name@company.com'
|
||||||
hint: 'Used as fallback in case the author of the change is not present.'
|
hint: 'Used as fallback in case the author of the change is not present.'
|
||||||
|
order: 20
|
||||||
defaultName:
|
defaultName:
|
||||||
type: String
|
type: String
|
||||||
title: Default Author Name
|
title: Default Author Name
|
||||||
default: 'John Smith'
|
default: 'John Smith'
|
||||||
hint: 'Used as fallback in case the author of the change is not present.'
|
hint: 'Used as fallback in case the author of the change is not present.'
|
||||||
|
order: 21
|
||||||
|
@ -3,8 +3,6 @@ const sgit = require('simple-git/promise')
|
|||||||
const fs = require('fs-extra')
|
const fs = require('fs-extra')
|
||||||
const _ = require('lodash')
|
const _ = require('lodash')
|
||||||
|
|
||||||
let repoPath = path.join(process.cwd(), 'data/repo')
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get file extension based on content type
|
* Get file extension based on content type
|
||||||
*/
|
*/
|
||||||
@ -43,72 +41,80 @@ const injectMetadata = (page) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
git: null,
|
||||||
|
repoPath: path.join(process.cwd(), 'data/repo'),
|
||||||
async activated() {
|
async activated() {
|
||||||
|
// not used
|
||||||
},
|
},
|
||||||
async deactivated() {
|
async deactivated() {
|
||||||
|
// not used
|
||||||
},
|
},
|
||||||
async init() {
|
async init() {
|
||||||
WIKI.logger.info('(STORAGE/GIT) Initializing...')
|
WIKI.logger.info('(STORAGE/GIT) Initializing...')
|
||||||
repoPath = path.resolve(WIKI.ROOTPATH, this.config.localRepoPath)
|
this.repoPath = path.resolve(WIKI.ROOTPATH, this.config.localRepoPath)
|
||||||
await fs.ensureDir(repoPath)
|
await fs.ensureDir(this.repoPath)
|
||||||
const git = sgit(repoPath)
|
this.git = sgit(this.repoPath)
|
||||||
|
|
||||||
// Initialize repo (if needed)
|
// Initialize repo (if needed)
|
||||||
WIKI.logger.info('(STORAGE/GIT) Checking repository state...')
|
WIKI.logger.info('(STORAGE/GIT) Checking repository state...')
|
||||||
const isRepo = await git.checkIsRepo()
|
const isRepo = await this.git.checkIsRepo()
|
||||||
if (!isRepo) {
|
if (!isRepo) {
|
||||||
WIKI.logger.info('(STORAGE/GIT) Initializing local repository...')
|
WIKI.logger.info('(STORAGE/GIT) Initializing local repository...')
|
||||||
await git.init()
|
await this.git.init()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set default author
|
// Set default author
|
||||||
await git.raw(['config', '--local', 'user.email', this.config.defaultEmail])
|
await this.git.raw(['config', '--local', 'user.email', this.config.defaultEmail])
|
||||||
await git.raw(['config', '--local', 'user.name', this.config.defaultName])
|
await this.git.raw(['config', '--local', 'user.name', this.config.defaultName])
|
||||||
|
|
||||||
// Purge existing remotes
|
// Purge existing remotes
|
||||||
WIKI.logger.info('(STORAGE/GIT) Listing existing remotes...')
|
WIKI.logger.info('(STORAGE/GIT) Listing existing remotes...')
|
||||||
const remotes = await git.getRemotes()
|
const remotes = await this.git.getRemotes()
|
||||||
if (remotes.length > 0) {
|
if (remotes.length > 0) {
|
||||||
WIKI.logger.info('(STORAGE/GIT) Purging existing remotes...')
|
WIKI.logger.info('(STORAGE/GIT) Purging existing remotes...')
|
||||||
for(let remote of remotes) {
|
for(let remote of remotes) {
|
||||||
await git.removeRemote(remote.name)
|
await this.git.removeRemote(remote.name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add remote
|
// Add remote
|
||||||
WIKI.logger.info('(STORAGE/GIT) Setting SSL Verification config...')
|
WIKI.logger.info('(STORAGE/GIT) Setting SSL Verification config...')
|
||||||
await git.raw(['config', '--local', '--bool', 'http.sslVerify', _.toString(this.config.verifySSL)])
|
await this.git.raw(['config', '--local', '--bool', 'http.sslVerify', _.toString(this.config.verifySSL)])
|
||||||
switch (this.config.authType) {
|
switch (this.config.authType) {
|
||||||
case 'ssh':
|
case 'ssh':
|
||||||
WIKI.logger.info('(STORAGE/GIT) Setting SSH Command config...')
|
WIKI.logger.info('(STORAGE/GIT) Setting SSH Command config...')
|
||||||
await git.addConfig('core.sshCommand', `ssh -i "${this.config.sshPrivateKeyPath}" -o StrictHostKeyChecking=no`)
|
await this.git.addConfig('core.sshCommand', `ssh -i "${this.config.sshPrivateKeyPath}" -o StrictHostKeyChecking=no`)
|
||||||
WIKI.logger.info('(STORAGE/GIT) Adding origin remote via SSH...')
|
WIKI.logger.info('(STORAGE/GIT) Adding origin remote via SSH...')
|
||||||
await git.addRemote('origin', this.config.repoUrl)
|
await this.git.addRemote('origin', this.config.repoUrl)
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
WIKI.logger.info('(STORAGE/GIT) Adding origin remote via HTTPS...')
|
WIKI.logger.info('(STORAGE/GIT) Adding origin remote via HTTPS...')
|
||||||
await git.addRemote('origin', `https://${this.config.basicUsername}:${this.config.basicPassword}@${this.config.repoUrl}`)
|
await this.git.addRemote('origin', `https://${this.config.basicUsername}:${this.config.basicPassword}@${this.config.repoUrl}`)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch updates for remote
|
// Fetch updates for remote
|
||||||
WIKI.logger.info('(STORAGE/GIT) Fetch updates from remote...')
|
WIKI.logger.info('(STORAGE/GIT) Fetch updates from remote...')
|
||||||
await git.raw(['remote', 'update', 'origin'])
|
await this.git.raw(['remote', 'update', 'origin'])
|
||||||
|
|
||||||
// Checkout branch
|
// Checkout branch
|
||||||
const branches = await git.branch()
|
const branches = await this.git.branch()
|
||||||
if (!_.includes(branches.all, this.config.branch) && !_.includes(branches.all, `remotes/origin/${this.config.branch}`)) {
|
if (!_.includes(branches.all, this.config.branch) && !_.includes(branches.all, `remotes/origin/${this.config.branch}`)) {
|
||||||
throw new Error('Invalid branch! Make sure it exists on the remote first.')
|
throw new Error('Invalid branch! Make sure it exists on the remote first.')
|
||||||
}
|
}
|
||||||
WIKI.logger.info(`(STORAGE/GIT) Checking out branch ${this.config.branch}...`)
|
WIKI.logger.info(`(STORAGE/GIT) Checking out branch ${this.config.branch}...`)
|
||||||
await git.checkout(this.config.branch)
|
await this.git.checkout(this.config.branch)
|
||||||
|
|
||||||
|
// Perform initial sync
|
||||||
|
await this.sync()
|
||||||
|
|
||||||
|
WIKI.logger.info('(STORAGE/GIT) Initialization completed.')
|
||||||
|
},
|
||||||
|
async sync() {
|
||||||
// Pull rebase
|
// Pull rebase
|
||||||
if (_.includes(['sync', 'pull'], this.mode)) {
|
if (_.includes(['sync', 'pull'], this.mode)) {
|
||||||
WIKI.logger.info(`(STORAGE/GIT) Performing pull rebase from origin on branch ${this.config.branch}...`)
|
WIKI.logger.info(`(STORAGE/GIT) Performing pull rebase from origin on branch ${this.config.branch}...`)
|
||||||
await git.pull('origin', this.config.branch, ['--rebase'])
|
await this.git.pull('origin', this.config.branch, ['--rebase'])
|
||||||
}
|
}
|
||||||
|
|
||||||
// Push
|
// Push
|
||||||
@ -118,50 +124,48 @@ module.exports = {
|
|||||||
if (this.mode === 'push') {
|
if (this.mode === 'push') {
|
||||||
pushOpts.push('--force')
|
pushOpts.push('--force')
|
||||||
}
|
}
|
||||||
await git.push('origin', this.config.branch, pushOpts)
|
await this.git.push('origin', this.config.branch, pushOpts)
|
||||||
}
|
}
|
||||||
|
|
||||||
WIKI.logger.info('(STORAGE/GIT) Initialization completed.')
|
|
||||||
},
|
},
|
||||||
async created() {
|
async created(page) {
|
||||||
const fileName = `${this.page.path}.${getFileExtension(this.page.contentType)}`
|
WIKI.logger.info(`(STORAGE/GIT) Committing new file ${page.path}...`)
|
||||||
const filePath = path.join(repoPath, fileName)
|
const fileName = `${page.path}.${getFileExtension(page.contentType)}`
|
||||||
await fs.outputFile(filePath, injectMetadata(this.page), 'utf8')
|
const filePath = path.join(this.repoPath, fileName)
|
||||||
|
await fs.outputFile(filePath, injectMetadata(page), 'utf8')
|
||||||
|
|
||||||
const git = sgit(repoPath)
|
await this.git.add(`./${fileName}`)
|
||||||
await git.add(`./${fileName}`)
|
await this.git.commit(`docs: create ${page.path}`, fileName, {
|
||||||
await git.commit(`docs: create ${this.page.path}`, fileName, {
|
'--author': `"${page.authorName} <${page.authorEmail}>"`
|
||||||
'--author': `"${this.page.authorName} <${this.page.authorEmail}>"`
|
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
async updated() {
|
async updated(page) {
|
||||||
const fileName = `${this.page.path}.${getFileExtension(this.page.contentType)}`
|
WIKI.logger.info(`(STORAGE/GIT) Committing updated file ${page.path}...`)
|
||||||
const filePath = path.join(repoPath, fileName)
|
const fileName = `${page.path}.${getFileExtension(page.contentType)}`
|
||||||
await fs.outputFile(filePath, injectMetadata(this.page), 'utf8')
|
const filePath = path.join(this.repoPath, fileName)
|
||||||
|
await fs.outputFile(filePath, injectMetadata(page), 'utf8')
|
||||||
|
|
||||||
const git = sgit(repoPath)
|
await this.git.add(`./${fileName}`)
|
||||||
await git.add(`./${fileName}`)
|
await this.git.commit(`docs: update ${page.path}`, fileName, {
|
||||||
await git.commit(`docs: update ${this.page.path}`, fileName, {
|
'--author': `"${page.authorName} <${page.authorEmail}>"`
|
||||||
'--author': `"${this.page.authorName} <${this.page.authorEmail}>"`
|
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
async deleted() {
|
async deleted(page) {
|
||||||
const fileName = `${this.page.path}.${getFileExtension(this.page.contentType)}`
|
WIKI.logger.info(`(STORAGE/GIT) Committing removed file ${page.path}...`)
|
||||||
|
const fileName = `${page.path}.${getFileExtension(page.contentType)}`
|
||||||
|
|
||||||
const git = sgit(repoPath)
|
await this.git.rm(`./${fileName}`)
|
||||||
await git.rm(`./${fileName}`)
|
await this.git.commit(`docs: delete ${page.path}`, fileName, {
|
||||||
await git.commit(`docs: delete ${this.page.path}`, fileName, {
|
'--author': `"${page.authorName} <${page.authorEmail}>"`
|
||||||
'--author': `"${this.page.authorName} <${this.page.authorEmail}>"`
|
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
async renamed() {
|
async renamed(page) {
|
||||||
const sourceFilePath = `${this.page.sourcePath}.${getFileExtension(this.page.contentType)}`
|
WIKI.logger.info(`(STORAGE/GIT) Committing file move from ${page.sourcePath} to ${page.destinationPath}...`)
|
||||||
const destinationFilePath = `${this.page.destinationPath}.${getFileExtension(this.page.contentType)}`
|
const sourceFilePath = `${page.sourcePath}.${getFileExtension(page.contentType)}`
|
||||||
|
const destinationFilePath = `${page.destinationPath}.${getFileExtension(page.contentType)}`
|
||||||
|
|
||||||
const git = sgit(repoPath)
|
await this.git.mv(`./${sourceFilePath}`, `./${destinationFilePath}`)
|
||||||
await git.mv(`./${sourceFilePath}`, `./${destinationFilePath}`)
|
await this.git.commit(`docs: rename ${page.sourcePath} to ${destinationFilePath}`, destinationFilePath, {
|
||||||
await git.commit(`docs: rename ${this.page.sourcePath} to ${destinationFilePath}`, destinationFilePath, {
|
'--author': `"${page.authorName} <${page.authorEmail}>"`
|
||||||
'--author': `"${this.page.authorName} <${this.page.authorEmail}>"`
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
35
yarn.lock
35
yarn.lock
@ -3431,16 +3431,21 @@ copy-webpack-plugin@4.6.0:
|
|||||||
p-limit "^1.0.0"
|
p-limit "^1.0.0"
|
||||||
serialize-javascript "^1.4.0"
|
serialize-javascript "^1.4.0"
|
||||||
|
|
||||||
core-js@2.6.3:
|
core-js@2.6.3, core-js@^2.4.0, core-js@^2.5.7:
|
||||||
version "2.6.3"
|
version "2.6.3"
|
||||||
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.3.tgz#4b70938bdffdaf64931e66e2db158f0892289c49"
|
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.3.tgz#4b70938bdffdaf64931e66e2db158f0892289c49"
|
||||||
integrity sha512-l00tmFFZOBHtYhN4Cz7k32VM7vTn3rE2ANjQDxdEN6zmXZ/xq1jQuutnmHvMG1ZJ7xd72+TA5YpUK8wz3rWsfQ==
|
integrity sha512-l00tmFFZOBHtYhN4Cz7k32VM7vTn3rE2ANjQDxdEN6zmXZ/xq1jQuutnmHvMG1ZJ7xd72+TA5YpUK8wz3rWsfQ==
|
||||||
|
|
||||||
core-js@3.0.0-beta.11, core-js@3.0.0-beta.3, core-js@^1.0.0, core-js@^2.4.0, core-js@^2.5.7:
|
core-js@3.0.0-beta.11, core-js@3.0.0-beta.3:
|
||||||
version "3.0.0-beta.11"
|
version "3.0.0-beta.11"
|
||||||
resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.0.0-beta.11.tgz#dac9d000f562194cc8bc7fe142be0d70c8c910f8"
|
resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.0.0-beta.11.tgz#dac9d000f562194cc8bc7fe142be0d70c8c910f8"
|
||||||
integrity sha512-Q1gGAIqiFfR8ZqjrJw4gzjDrP2JsLacNQzUKUfqvcpg974bCQrPaT4a+HNbznQm5DabCIKw9fGQotj0dgdsMRg==
|
integrity sha512-Q1gGAIqiFfR8ZqjrJw4gzjDrP2JsLacNQzUKUfqvcpg974bCQrPaT4a+HNbznQm5DabCIKw9fGQotj0dgdsMRg==
|
||||||
|
|
||||||
|
core-js@^1.0.0:
|
||||||
|
version "1.2.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636"
|
||||||
|
integrity sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY=
|
||||||
|
|
||||||
core-util-is@1.0.2, core-util-is@~1.0.0:
|
core-util-is@1.0.2, core-util-is@~1.0.0:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
|
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
|
||||||
@ -6290,23 +6295,7 @@ invert-kv@^2.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-2.0.0.tgz#7393f5afa59ec9ff5f67a27620d11c226e3eec02"
|
resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-2.0.0.tgz#7393f5afa59ec9ff5f67a27620d11c226e3eec02"
|
||||||
integrity sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==
|
integrity sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==
|
||||||
|
|
||||||
ioredis@4.5.1:
|
ioredis@4.6.2, ioredis@^4.5.1:
|
||||||
version "4.5.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/ioredis/-/ioredis-4.5.1.tgz#b1c1c1657697caa3a617acb9370e3c0694edb775"
|
|
||||||
integrity sha512-p1BblrFZdb5Oc5EBsEb4EoycDqn7xi/NTNT4bDvo/w6B08eMNO1E7RAOOEA1GAb65+8Hbs2LgUyz3cZOTiP3xg==
|
|
||||||
dependencies:
|
|
||||||
cluster-key-slot "^1.0.6"
|
|
||||||
debug "^3.1.0"
|
|
||||||
denque "^1.1.0"
|
|
||||||
flexbuffer "0.0.6"
|
|
||||||
lodash.defaults "^4.2.0"
|
|
||||||
lodash.flatten "^4.4.0"
|
|
||||||
redis-commands "1.4.0"
|
|
||||||
redis-errors "^1.2.0"
|
|
||||||
redis-parser "^3.0.0"
|
|
||||||
standard-as-callback "^1.0.0"
|
|
||||||
|
|
||||||
ioredis@^4.5.1:
|
|
||||||
version "4.6.2"
|
version "4.6.2"
|
||||||
resolved "https://registry.yarnpkg.com/ioredis/-/ioredis-4.6.2.tgz#840847117fe0190a9309085847311a07183fc385"
|
resolved "https://registry.yarnpkg.com/ioredis/-/ioredis-4.6.2.tgz#840847117fe0190a9309085847311a07183fc385"
|
||||||
integrity sha512-zlc/LeoeriHTXm5z3rakPcfRcUV9x+xr0E+7/L7KH0D5z7sI5ngEQWR2RUxnwFcxUcCkvrXMztRIdBP3DhqMAQ==
|
integrity sha512-zlc/LeoeriHTXm5z3rakPcfRcUV9x+xr0E+7/L7KH0D5z7sI5ngEQWR2RUxnwFcxUcCkvrXMztRIdBP3DhqMAQ==
|
||||||
@ -13159,10 +13148,10 @@ vue-loader@15.6.2:
|
|||||||
vue-hot-reload-api "^2.3.0"
|
vue-hot-reload-api "^2.3.0"
|
||||||
vue-style-loader "^4.1.0"
|
vue-style-loader "^4.1.0"
|
||||||
|
|
||||||
vue-material-design-icons@2.6.0:
|
vue-material-design-icons@3.0.0:
|
||||||
version "2.6.0"
|
version "3.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/vue-material-design-icons/-/vue-material-design-icons-2.6.0.tgz#44de179ea85a31424cea1b1da5415f79cfce2743"
|
resolved "https://registry.yarnpkg.com/vue-material-design-icons/-/vue-material-design-icons-3.0.0.tgz#db387aa604e12523180d17db315126f6311d3e13"
|
||||||
integrity sha512-ueylNTZJk9jXGFSlSZ6iVIaw+r/uTMJDerG2X3ZAMz4yK7O/BAS1I/THVLA+mINyb4I7SPHmBt/msSYcdKSv0A==
|
integrity sha512-Y9dLUDuU0X3BjdeYjaHaWQ1UX4LMKT1kPIGApVhJmIs+HKY6xf57JLuba82YK+8iRlkJ/QYXbSV9oLrugn6M3w==
|
||||||
|
|
||||||
vue-moment@4.0.0:
|
vue-moment@4.0.0:
|
||||||
version "4.0.0"
|
version "4.0.0"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user