feat: rebuild page tree worker

pull/1098/head
NGPixel 5 years ago
parent 2883437a0f
commit 38c33c58bb

@ -3,7 +3,7 @@
v-card.page-selector
.dialog-header.is-dark
v-icon.mr-3(color='white') mdi-page-next-outline
span Select Page Location
.body-1 Select Page Location
v-spacer
v-progress-circular(
indeterminate
@ -12,44 +12,44 @@
:width='2'
v-show='searchLoading'
)
//- .d-flex(style='min-height:400px;')
//- v-flex(xs4).grey(:class='darkMode ? `darken-4` : `lighten-3`')
//- v-toolbar(color='grey darken-3', dark, dense, flat)
//- .body-2 Folders
//- v-spacer
//- v-btn(icon): v-icon create_new_folder
//- v-treeview(
//- v-model='tree'
//- :items='treeFolders'
//- :load-children='fetchFolders'
//- activatable
//- open-on-click
//- hoverable
//- )
//- template(slot='prepend', slot-scope='{ item, open, leaf }')
//- v-icon {{ open ? 'folder_open' : 'folder' }}
//- v-flex(xs8)
//- v-toolbar(color='grey darken-2', dark, dense, flat)
//- .body-2 Pages
//- v-spacer
//- v-btn(icon): v-icon forward
//- v-btn(icon): v-icon delete
//- v-list(dense)
//- v-list-item
//- v-list-item-avatar: v-icon insert_drive_file
//- v-list-item-title File A
//- v-divider
//- v-list-item
//- v-list-item-avatar: v-icon insert_drive_file
//- v-list-item-title File B
//- v-divider
//- v-list-item
//- v-list-item-avatar: v-icon insert_drive_file
//- v-list-item-title File C
//- v-divider
//- v-list-item
//- v-list-item-avatar: v-icon insert_drive_file
//- v-list-item-title File D
.d-flex(style='min-height:400px;')
v-flex.grey(xs4, :class='darkMode ? `darken-4` : `lighten-3`')
v-toolbar(color='grey darken-3', dark, dense, flat)
.body-2 Folders
//- v-spacer
//- v-btn(icon): v-icon create_new_folder
v-treeview(
v-model='tree'
:items='treeFolders'
:load-children='fetchFolders'
activatable
open-on-click
hoverable
)
template(slot='prepend', slot-scope='{ item, open, leaf }')
v-icon mdi-{{ open ? 'folder-open' : 'folder' }}
v-flex(xs8)
v-toolbar(color='grey darken-2', dark, dense, flat)
.body-2 Pages
v-spacer
v-btn(icon): v-icon mdi-forward
v-btn(icon): v-icon mdi-delete
v-list(dense)
v-list-item
v-list-item-icon: v-icon mdi-file-document-box
v-list-item-title File A
v-divider
v-list-item
v-list-item-icon: v-icon mdi-file-document-box
v-list-item-title File B
v-divider
v-list-item
v-list-item-icon: v-icon mdi-file-document-box
v-list-item-title File C
v-divider
v-list-item
v-list-item-icon: v-icon mdi-file-document-box
v-list-item-title File D
v-card-actions.grey.pa-2(:class='darkMode ? `darken-3-d5` : `lighten-1`')
v-select(
solo
@ -58,7 +58,7 @@
hide-details
single-line
:items='namespaces'
style='flex: 0 0 100px;'
style='flex: 0 0 100px; border-radius: 4px 0 0 4px;'
v-model='currentLocale'
)
v-text-field(
@ -68,6 +68,7 @@
v-model='currentPath'
flat
clearable
style='border-radius: 0 4px 4px 0;'
)
v-card-chin
v-spacer

@ -75,14 +75,18 @@ jobs:
onInit: true
schedule: PT15M
offlineSkip: false
repeat: true
syncGraphLocales:
onInit: true
schedule: P1D
offlineSkip: true
syncGraphUpdates:
repeat: true
rebuildTree:
onInit: true
schedule: P1D
offlineSkip: true
offlineSkip: false
repeat: false
immediate: true
worker: true
groups:
defaultPermissions:
- 'read:pages'

@ -99,12 +99,13 @@ module.exports = {
return
}
const schedule = (configHelper.isValidDurationString(queueParams.schedule)) ? queueParams.schedule : _.get(WIKI.config, queueParams.schedule)
const schedule = (configHelper.isValidDurationString(queueParams.schedule)) ? queueParams.schedule : 'P1D'
this.registerJob({
name: _.kebabCase(queueName),
immediate: queueParams.onInit,
immediate: _.get(queueParams, 'onInit', false),
schedule: schedule,
repeat: true
repeat: _.get(queueParams, 'repeat', false),
worker: _.get(queueParams, 'worker', false)
})
})
},

@ -97,7 +97,7 @@ module.exports = {
}
// Check source asset permissions
const assetSourcePath = (asset.folderId) ? hierarchy.map(h => h.slug).join('/') + `/${filename}` : filename
const assetSourcePath = (asset.folderId) ? hierarchy.map(h => h.slug).join('/') + `/${asset.filename}` : asset.filename
if (!WIKI.auth.checkAccess(context.req.user, ['manage:assets'], { path: assetSourcePath })) {
throw new WIKI.Error.AssetRenameForbidden()
}
@ -118,6 +118,16 @@ module.exports = {
// Delete old asset cache
await asset.deleteAssetCache()
// Rename in Storage
await WIKI.models.storage.assetEvent({
event: 'renamed',
asset: {
...asset,
sourcePath: assetSourcePath,
destinationPath: assetTargetPath
}
})
return {
responseResult: graphHelper.generateSuccess('Asset has been renamed successfully.')
}
@ -145,6 +155,12 @@ module.exports = {
await WIKI.models.assets.query().deleteById(args.id)
await asset.deleteAssetCache()
// Delete from Storage
await WIKI.models.storage.assetEvent({
event: 'deleted',
asset
})
return {
responseResult: graphHelper.generateSuccess('Asset has been deleted successfully.')
}

@ -0,0 +1,63 @@
const _ = require('lodash')
/* global WIKI */
module.exports = async (pageId) => {
WIKI.logger.info(`Rebuilding page tree...`)
try {
WIKI.models = require('../core/db').init()
await WIKI.configSvc.loadFromDb()
await WIKI.configSvc.applyFlags()
await WIKI.models.knex.table('pageTree').truncate()
const pages = await WIKI.models.pages.query().select('id', 'path', 'localeCode', 'title', 'isPrivate', 'privateNS').orderBy(['localeCode', 'path'])
let tree = []
let pik = 0
for (const page of pages) {
const pagePaths = page.path.split('/')
let currentPath = ''
let depth = 0
let parentId = null
for (const part of pagePaths) {
depth++
const isFolder = (depth < pagePaths.length)
currentPath = currentPath ? `${currentPath}/${part}` : part
const found = _.find(tree, {
localeCode: page.localeCode,
path: currentPath
})
if (!found) {
pik++
tree.push({
id: pik,
localeCode: page.localeCode,
path: currentPath,
depth: depth,
title: isFolder ? part : page.title,
isFolder: isFolder,
isPrivate: !isFolder && page.isPrivate,
privateNS: !isFolder ? page.privateNS : null,
parent: parentId,
pageId: isFolder ? null : page.id
})
parentId = pik
} else {
parentId = found.id
}
}
}
if (tree.length > 0) {
await WIKI.models.knex.table('pageTree').insert(tree)
}
await WIKI.models.knex.destroy()
WIKI.logger.info(`Rebuilding page tree: [ COMPLETED ]`)
} catch (err) {
WIKI.logger.error(`Rebuilding page tree: [ FAILED ]`)
WIKI.logger.error(err.message)
}
}

@ -1,69 +0,0 @@
'use strict'
// /* global WIKI */
// const Promise = require('bluebird')
// const fs = Promise.promisifyAll(require('fs-extra'))
// const klaw = require('klaw')
// const moment = require('moment')
// const path = require('path')
// const entryHelper = require('../helpers/entry')
module.exports = (job) => {
return true
// return WIKI.git.resync().then(() => {
// // -> Stream all documents
// let cacheJobs = []
// let jobCbStreamDocsResolve = null
// let jobCbStreamDocs = new Promise((resolve, reject) => {
// jobCbStreamDocsResolve = resolve
// })
// klaw(WIKI.REPOPATH).on('data', function (item) {
// if (path.extname(item.path) === '.md' && path.basename(item.path) !== 'README.md') {
// let entryPath = entryHelper.parsePath(entryHelper.getEntryPathFromFullPath(item.path))
// let cachePath = entryHelper.getCachePath(entryPath)
// // -> Purge outdated cache
// cacheJobs.push(
// fs.statAsync(cachePath).then((st) => {
// return moment(st.mtime).isBefore(item.stats.mtime) ? 'expired' : 'active'
// }).catch((err) => {
// return (err.code !== 'EEXIST') ? err : 'new'
// }).then((fileStatus) => {
// // -> Delete expired cache file
// if (fileStatus === 'expired') {
// return fs.unlinkAsync(cachePath).return(fileStatus)
// }
// return fileStatus
// }).then((fileStatus) => {
// // -> Update cache and search index
// if (fileStatus !== 'active') {
// return global.entries.updateCache(entryPath).then(entry => {
// process.send({
// action: 'searchAdd',
// content: entry
// })
// return true
// })
// }
// return true
// })
// )
// }
// }).on('end', () => {
// jobCbStreamDocsResolve(Promise.all(cacheJobs))
// })
// return jobCbStreamDocs
// }).then(() => {
// WIKI.logger.info('Git remote repository sync: DONE')
// return true
// })
}

@ -125,6 +125,14 @@ module.exports = class Asset extends Model {
// Move temp upload to cache
await fs.move(opts.path, path.join(process.cwd(), `data/cache/${fileHash}.dat`), { overwrite: true })
// Add to Storage
if (!opts.skipStorage) {
await WIKI.models.storage.assetEvent({
event: 'uploaded',
asset
})
}
}
static async getAsset(assetPath, res) {

@ -251,7 +251,7 @@ module.exports = class Page extends Model {
})
// -> Save Tags
if (opts.tags.length > 0) {
if (opts.tags && opts.tags.length > 0) {
await WIKI.models.tags.associateTags({ tags: opts.tags, page })
}

@ -180,6 +180,17 @@ module.exports = class Storage extends Model {
}
}
static async assetEvent({ event, asset }) {
try {
for (let target of this.targets) {
await target.fn[`asset${_.capitalize(event)}`](asset)
}
} catch (err) {
WIKI.logger.warn(err)
throw err
}
}
static async executeAction(targetKey, handler) {
try {
const target = _.find(this.targets, ['key', targetKey])

@ -25,6 +25,9 @@ const getContenType = (filePath) => {
}
const getPagePath = (filePath) => {
if (process.platform === 'win32') {
filePath = filePath.replace(/\\/g, '/')
}
let meta = {
locale: 'en',
path: _.initial(filePath.split('.')).join('')
@ -194,7 +197,7 @@ module.exports = {
await WIKI.models.pages.updatePage({
id: currentPage.id,
title: _.get(pageData, 'title', currentPage.title),
description: _.get(pageData, 'description', currentPage.description),
description: _.get(pageData, 'description', currentPage.description) || '',
isPublished: _.get(pageData, 'isPublished', currentPage.isPublished),
isPrivate: false,
content: pageData.content,
@ -209,7 +212,7 @@ module.exports = {
path: contentPath.path,
locale: contentPath.locale,
title: _.get(pageData, 'title', _.last(contentPath.path.split('/'))),
description: _.get(pageData, 'description', ''),
description: _.get(pageData, 'description', '') || '',
isPublished: _.get(pageData, 'isPublished', true),
isPrivate: false,
content: pageData.content,
@ -230,6 +233,7 @@ module.exports = {
})
} else {
WIKI.logger.warn(`(STORAGE/GIT) Failed to open ${item.file}`)
console.error(err)
WIKI.logger.warn(err)
}
}
@ -310,7 +314,47 @@ module.exports = {
'--author': `"${page.authorName} <${page.authorEmail}>"`
})
},
/**
* ASSET UPLOAD
*
* @param {Object} asset Asset to upload
*/
async assetUploaded (asset) {
WIKI.logger.info(`(STORAGE/GIT) Committing new file ${asset.path}...`)
const filePath = path.join(this.repoPath, asset.path)
await fs.outputFile(filePath, asset, 'utf8')
await this.git.add(`./${asset.path}`)
await this.git.commit(`docs: upload ${asset.path}`, asset.path, {
'--author': `"${asset.authorName} <${asset.authorEmail}>"`
})
},
/**
* ASSET DELETE
*
* @param {Object} asset Asset to upload
*/
async assetDeleted (asset) {
WIKI.logger.info(`(STORAGE/GIT) Committing removed file ${asset.path}...`)
await this.git.rm(`./${asset.path}`)
await this.git.commit(`docs: delete ${asset.path}`, asset.path, {
'--author': `"${asset.authorName} <${asset.authorEmail}>"`
})
},
/**
* ASSET RENAME
*
* @param {Object} asset Asset to upload
*/
async assetRenamed (asset) {
WIKI.logger.info(`(STORAGE/GIT) Committing file move from ${asset.sourcePath} to ${asset.destinationPath}...`)
await this.git.mv(`./${asset.sourcePath}`, `./${asset.destinationPath}`)
await this.git.commit(`docs: rename ${asset.sourcePath} to ${asset.destinationPath}`, asset.destinationPath, {
'--author': `"${asset.authorName} <${asset.authorEmail}>"`
})
},
/**
* HANDLERS
*/

Loading…
Cancel
Save