feat: config wizard save
This commit is contained in:
@@ -237,6 +237,7 @@ module.exports = () => {
|
||||
}
|
||||
|
||||
// Update config file
|
||||
wiki.logger.info('Writing config file to disk...')
|
||||
let confRaw = await fs.readFileAsync(path.join(wiki.ROOTPATH, 'config.yml'), 'utf8')
|
||||
let conf = yaml.safeLoad(confRaw)
|
||||
|
||||
@@ -256,18 +257,60 @@ module.exports = () => {
|
||||
wiki.config.uploads = wiki.config.uploads || {}
|
||||
|
||||
// Site namespace
|
||||
wiki.config.site.title = req.body.title
|
||||
wiki.config.site.path = req.body.path
|
||||
wiki.config.site.lang = req.body.lang
|
||||
wiki.config.site.rtl = _.includes(wiki.data.rtlLangs, req.body.lang)
|
||||
wiki.config.site.sessionSecret = (await crypto.randomBytesAsync(32)).toString('hex')
|
||||
_.set(wiki.config.site, 'title', req.body.title)
|
||||
_.set(wiki.config.site, 'path', req.body.path)
|
||||
_.set(wiki.config.site, 'lang', req.body.lang)
|
||||
_.set(wiki.config.site, 'rtl', _.includes(wiki.data.rtlLangs, req.body.lang))
|
||||
_.set(wiki.config.site, 'sessionSecret', (await crypto.randomBytesAsync(32)).toString('hex'))
|
||||
|
||||
// Auth namespace
|
||||
wiki.config.auth.public = (req.body.public === 'true')
|
||||
_.set(wiki.config.auth, 'public', req.body.public === 'true')
|
||||
_.set(wiki.config.auth, 'strategies.local.allowSelfRegister', req.body.selfRegister === 'true')
|
||||
|
||||
// Git namespace
|
||||
_.set(wiki.config.git, 'enabled', req.body.gitUseRemote === 'true')
|
||||
if (wiki.config.git.enabled) {
|
||||
_.set(wiki.config.git, 'url', req.body.gitUrl)
|
||||
_.set(wiki.config.git, 'branch', req.body.gitBranch)
|
||||
_.set(wiki.config.git, 'author.defaultEmail', req.body.gitServerEmail)
|
||||
_.set(wiki.config.git, 'author.useUserEmail', req.body.gitShowUserEmail)
|
||||
_.set(wiki.config.git, 'sslVerify', req.body.gitAuthSSL === 'true')
|
||||
_.set(wiki.config.git, 'auth.type', req.body.gitAuthType)
|
||||
switch (wiki.config.git.auth.type) {
|
||||
case 'basic':
|
||||
_.set(wiki.config.git, 'auth.user', req.body.gitAuthUser)
|
||||
_.set(wiki.config.git, 'auth.pass', req.body.gitAuthPass)
|
||||
break
|
||||
case 'ssh':
|
||||
_.set(wiki.config.git, 'auth.keyPath', req.body.gitAuthSSHKey)
|
||||
break
|
||||
case 'sshenv':
|
||||
_.set(wiki.config.git, 'auth.keyEnv', req.body.gitAuthSSHKeyEnv)
|
||||
break
|
||||
case 'sshdb':
|
||||
_.set(wiki.config.git, 'auth.keyContents', req.body.gitAuthSSHKeyDB)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Logging namespace
|
||||
wiki.config.logging.telemetry = (req.body.telemetry === 'true')
|
||||
|
||||
// Save config to DB
|
||||
wiki.logger.info('Persisting config to DB...')
|
||||
await wiki.configSvc.saveToDb()
|
||||
|
||||
// Create root administrator
|
||||
wiki.logger.info('Creating root administrator...')
|
||||
await wiki.db.User.upsert({
|
||||
email: req.body.adminEmail,
|
||||
provider: 'local',
|
||||
password: await wiki.db.User.hashPassword(req.body.adminPassword),
|
||||
name: 'Administrator',
|
||||
role: 'admin',
|
||||
tfaIsActive: false
|
||||
})
|
||||
|
||||
res.json({ ok: true })
|
||||
} catch (err) {
|
||||
res.json({ ok: false, error: err.message })
|
||||
|
@@ -37,126 +37,126 @@ router.get('/t/*', (req, res, next) => {
|
||||
})
|
||||
})
|
||||
|
||||
router.post('/img', wiki.disk.uploadImgHandler, (req, res, next) => {
|
||||
let destFolder = _.chain(req.body.folder).trim().toLower().value()
|
||||
// router.post('/img', wiki.disk.uploadImgHandler, (req, res, next) => {
|
||||
// let destFolder = _.chain(req.body.folder).trim().toLower().value()
|
||||
|
||||
wiki.upl.validateUploadsFolder(destFolder).then((destFolderPath) => {
|
||||
if (!destFolderPath) {
|
||||
res.json({ ok: false, msg: wiki.lang.t('errors:invalidfolder') })
|
||||
return true
|
||||
}
|
||||
// wiki.upl.validateUploadsFolder(destFolder).then((destFolderPath) => {
|
||||
// if (!destFolderPath) {
|
||||
// res.json({ ok: false, msg: wiki.lang.t('errors:invalidfolder') })
|
||||
// return true
|
||||
// }
|
||||
|
||||
Promise.map(req.files, (f) => {
|
||||
let destFilename = ''
|
||||
let destFilePath = ''
|
||||
// Promise.map(req.files, (f) => {
|
||||
// let destFilename = ''
|
||||
// let destFilePath = ''
|
||||
|
||||
return wiki.disk.validateUploadsFilename(f.originalname, destFolder, true).then((fname) => {
|
||||
destFilename = fname
|
||||
destFilePath = path.resolve(destFolderPath, destFilename)
|
||||
// return wiki.disk.validateUploadsFilename(f.originalname, destFolder, true).then((fname) => {
|
||||
// destFilename = fname
|
||||
// destFilePath = path.resolve(destFolderPath, destFilename)
|
||||
|
||||
return readChunk(f.path, 0, 262)
|
||||
}).then((buf) => {
|
||||
// -> Check MIME type by magic number
|
||||
// return readChunk(f.path, 0, 262)
|
||||
// }).then((buf) => {
|
||||
// // -> Check MIME type by magic number
|
||||
|
||||
let mimeInfo = fileType(buf)
|
||||
if (!_.includes(['image/png', 'image/jpeg', 'image/gif', 'image/webp'], mimeInfo.mime)) {
|
||||
return Promise.reject(new Error(wiki.lang.t('errors:invalidfiletype')))
|
||||
}
|
||||
return true
|
||||
}).then(() => {
|
||||
// -> Move file to final destination
|
||||
// let mimeInfo = fileType(buf)
|
||||
// if (!_.includes(['image/png', 'image/jpeg', 'image/gif', 'image/webp'], mimeInfo.mime)) {
|
||||
// return Promise.reject(new Error(wiki.lang.t('errors:invalidfiletype')))
|
||||
// }
|
||||
// return true
|
||||
// }).then(() => {
|
||||
// // -> Move file to final destination
|
||||
|
||||
return fs.moveAsync(f.path, destFilePath, { clobber: false })
|
||||
}).then(() => {
|
||||
return {
|
||||
ok: true,
|
||||
filename: destFilename,
|
||||
filesize: f.size
|
||||
}
|
||||
}).reflect()
|
||||
}, {concurrency: 3}).then((results) => {
|
||||
let uplResults = _.map(results, (r) => {
|
||||
if (r.isFulfilled()) {
|
||||
return r.value()
|
||||
} else {
|
||||
return {
|
||||
ok: false,
|
||||
msg: r.reason().message
|
||||
}
|
||||
}
|
||||
})
|
||||
res.json({ ok: true, results: uplResults })
|
||||
return true
|
||||
}).catch((err) => {
|
||||
res.json({ ok: false, msg: err.message })
|
||||
return true
|
||||
})
|
||||
})
|
||||
})
|
||||
// return fs.moveAsync(f.path, destFilePath, { clobber: false })
|
||||
// }).then(() => {
|
||||
// return {
|
||||
// ok: true,
|
||||
// filename: destFilename,
|
||||
// filesize: f.size
|
||||
// }
|
||||
// }).reflect()
|
||||
// }, {concurrency: 3}).then((results) => {
|
||||
// let uplResults = _.map(results, (r) => {
|
||||
// if (r.isFulfilled()) {
|
||||
// return r.value()
|
||||
// } else {
|
||||
// return {
|
||||
// ok: false,
|
||||
// msg: r.reason().message
|
||||
// }
|
||||
// }
|
||||
// })
|
||||
// res.json({ ok: true, results: uplResults })
|
||||
// return true
|
||||
// }).catch((err) => {
|
||||
// res.json({ ok: false, msg: err.message })
|
||||
// return true
|
||||
// })
|
||||
// })
|
||||
// })
|
||||
|
||||
router.post('/file', wiki.disk.uploadFileHandler, (req, res, next) => {
|
||||
let destFolder = _.chain(req.body.folder).trim().toLower().value()
|
||||
// router.post('/file', wiki.disk.uploadFileHandler, (req, res, next) => {
|
||||
// let destFolder = _.chain(req.body.folder).trim().toLower().value()
|
||||
|
||||
wiki.upl.validateUploadsFolder(destFolder).then((destFolderPath) => {
|
||||
if (!destFolderPath) {
|
||||
res.json({ ok: false, msg: wiki.lang.t('errors:invalidfolder') })
|
||||
return true
|
||||
}
|
||||
// wiki.upl.validateUploadsFolder(destFolder).then((destFolderPath) => {
|
||||
// if (!destFolderPath) {
|
||||
// res.json({ ok: false, msg: wiki.lang.t('errors:invalidfolder') })
|
||||
// return true
|
||||
// }
|
||||
|
||||
Promise.map(req.files, (f) => {
|
||||
let destFilename = ''
|
||||
let destFilePath = ''
|
||||
// Promise.map(req.files, (f) => {
|
||||
// let destFilename = ''
|
||||
// let destFilePath = ''
|
||||
|
||||
return wiki.disk.validateUploadsFilename(f.originalname, destFolder, false).then((fname) => {
|
||||
destFilename = fname
|
||||
destFilePath = path.resolve(destFolderPath, destFilename)
|
||||
// return wiki.disk.validateUploadsFilename(f.originalname, destFolder, false).then((fname) => {
|
||||
// destFilename = fname
|
||||
// destFilePath = path.resolve(destFolderPath, destFilename)
|
||||
|
||||
// -> Move file to final destination
|
||||
// // -> Move file to final destination
|
||||
|
||||
return fs.moveAsync(f.path, destFilePath, { clobber: false })
|
||||
}).then(() => {
|
||||
return {
|
||||
ok: true,
|
||||
filename: destFilename,
|
||||
filesize: f.size
|
||||
}
|
||||
}).reflect()
|
||||
}, {concurrency: 3}).then((results) => {
|
||||
let uplResults = _.map(results, (r) => {
|
||||
if (r.isFulfilled()) {
|
||||
return r.value()
|
||||
} else {
|
||||
return {
|
||||
ok: false,
|
||||
msg: r.reason().message
|
||||
}
|
||||
}
|
||||
})
|
||||
res.json({ ok: true, results: uplResults })
|
||||
return true
|
||||
}).catch((err) => {
|
||||
res.json({ ok: false, msg: err.message })
|
||||
return true
|
||||
})
|
||||
})
|
||||
})
|
||||
// return fs.moveAsync(f.path, destFilePath, { clobber: false })
|
||||
// }).then(() => {
|
||||
// return {
|
||||
// ok: true,
|
||||
// filename: destFilename,
|
||||
// filesize: f.size
|
||||
// }
|
||||
// }).reflect()
|
||||
// }, {concurrency: 3}).then((results) => {
|
||||
// let uplResults = _.map(results, (r) => {
|
||||
// if (r.isFulfilled()) {
|
||||
// return r.value()
|
||||
// } else {
|
||||
// return {
|
||||
// ok: false,
|
||||
// msg: r.reason().message
|
||||
// }
|
||||
// }
|
||||
// })
|
||||
// res.json({ ok: true, results: uplResults })
|
||||
// return true
|
||||
// }).catch((err) => {
|
||||
// res.json({ ok: false, msg: err.message })
|
||||
// return true
|
||||
// })
|
||||
// })
|
||||
// })
|
||||
|
||||
router.get('/*', (req, res, next) => {
|
||||
let fileName = req.params[0]
|
||||
if (!validPathRe.test(fileName)) {
|
||||
return res.sendStatus(404).end()
|
||||
}
|
||||
// router.get('/*', (req, res, next) => {
|
||||
// let fileName = req.params[0]
|
||||
// if (!validPathRe.test(fileName)) {
|
||||
// return res.sendStatus(404).end()
|
||||
// }
|
||||
|
||||
// todo: Authentication-based access
|
||||
// // todo: Authentication-based access
|
||||
|
||||
res.sendFile(fileName, {
|
||||
root: wiki.git.getRepoPath() + '/uploads/',
|
||||
dotfiles: 'deny'
|
||||
}, (err) => {
|
||||
if (err) {
|
||||
res.status(err.status).end()
|
||||
}
|
||||
})
|
||||
})
|
||||
// res.sendFile(fileName, {
|
||||
// root: wiki.git.getRepoPath() + '/uploads/',
|
||||
// dotfiles: 'deny'
|
||||
// }, (err) => {
|
||||
// if (err) {
|
||||
// res.status(err.status).end()
|
||||
// }
|
||||
// })
|
||||
// })
|
||||
|
||||
module.exports = router
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/* global wiki */
|
||||
|
||||
module.exports = () => {
|
||||
module.exports = async () => {
|
||||
// ----------------------------------------
|
||||
// Load global modules
|
||||
// ----------------------------------------
|
||||
|
@@ -54,28 +54,52 @@ module.exports = {
|
||||
* @param {Array} subsets Array of subsets to load
|
||||
* @returns Promise
|
||||
*/
|
||||
loadFromDb(subsets) {
|
||||
async loadFromDb(subsets) {
|
||||
if (!_.isArray(subsets) || subsets.length === 0) {
|
||||
subsets = wiki.data.configNamespaces
|
||||
}
|
||||
|
||||
return wiki.db.Setting.findAll({
|
||||
let results = await wiki.db.Setting.findAll({
|
||||
attributes: ['key', 'config'],
|
||||
where: {
|
||||
key: {
|
||||
$in: subsets
|
||||
}
|
||||
}
|
||||
}).then(results => {
|
||||
if (_.isArray(results) && results.length === subsets.length) {
|
||||
results.forEach(result => {
|
||||
wiki.config[result.key] = result.config
|
||||
})
|
||||
return true
|
||||
} else {
|
||||
wiki.logger.warn('DB Configuration is empty or incomplete.')
|
||||
return false
|
||||
}
|
||||
})
|
||||
if (_.isArray(results) && results.length === subsets.length) {
|
||||
results.forEach(result => {
|
||||
wiki.config[result.key] = result.config
|
||||
})
|
||||
return true
|
||||
} else {
|
||||
wiki.logger.warn('DB Configuration is empty or incomplete.')
|
||||
return false
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Save config to DB
|
||||
*
|
||||
* @param {Array} subsets Array of subsets to save
|
||||
* @returns Promise
|
||||
*/
|
||||
async saveToDb(subsets) {
|
||||
if (!_.isArray(subsets) || subsets.length === 0) {
|
||||
subsets = wiki.data.configNamespaces
|
||||
}
|
||||
|
||||
try {
|
||||
for (let set of subsets) {
|
||||
await wiki.db.Setting.upsert({
|
||||
key: set,
|
||||
config: _.get(wiki.config, set, {})
|
||||
})
|
||||
}
|
||||
} catch (err) {
|
||||
wiki.logger.error(`Failed to save configuration to DB: ${err.message}`)
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
@@ -73,7 +73,7 @@ module.exports = {
|
||||
min: 0,
|
||||
idle: 10000
|
||||
},
|
||||
logging: log => { wiki.logger.log('verbose', log) },
|
||||
logging: log => { wiki.logger.log('debug', log) },
|
||||
operatorsAliases
|
||||
})
|
||||
|
||||
@@ -110,7 +110,7 @@ module.exports = {
|
||||
syncSchemas() {
|
||||
return self.inst.sync({
|
||||
force: false,
|
||||
logging: log => { wiki.logger.log('verbose', log) }
|
||||
logging: log => { wiki.logger.log('debug', log) }
|
||||
})
|
||||
},
|
||||
// -> Set Connection App Name
|
||||
|
@@ -11,10 +11,10 @@ module.exports = {
|
||||
// Console
|
||||
|
||||
let logger = new (winston.Logger)({
|
||||
level: (wiki.IS_DEBUG) ? 'debug' : 'info',
|
||||
level: wiki.config.logLevel,
|
||||
transports: [
|
||||
new (winston.transports.Console)({
|
||||
level: (wiki.IS_DEBUG) ? 'debug' : 'info',
|
||||
level: wiki.config.logLevel,
|
||||
prettyPrint: true,
|
||||
colorize: true,
|
||||
silent: false,
|
||||
|
@@ -14,6 +14,8 @@ module.exports = {
|
||||
async upgradeFromMongo (opts) {
|
||||
wiki.telemetry.sendEvent('setup', 'upgradeFromMongo')
|
||||
|
||||
wiki.logger.info('Upgrading from MongoDB...')
|
||||
|
||||
let mongo = require('mongodb').MongoClient
|
||||
let parsedMongoConStr = cfgHelper.parseConfigValue(opts.mongoCnStr)
|
||||
|
||||
|
@@ -112,7 +112,7 @@ block body
|
||||
label.label(for='ipt-public') Public Access
|
||||
span.desc Should the site be accessible (read only) without login.
|
||||
p.control.is-fullwidth
|
||||
input#ipt-selfregister(type='checkbox', v-model='conf.selfregister', data-vv-scope='general', name='ipt-selfregister')
|
||||
input#ipt-selfregister(type='checkbox', v-model='conf.selfRegister', data-vv-scope='general', name='ipt-selfregister')
|
||||
label.label(for='ipt-selfregister') Allow Self-Registration
|
||||
span.desc Can users create their own account to gain access?
|
||||
section
|
||||
@@ -208,7 +208,7 @@ block body
|
||||
p.control.is-fullwidth
|
||||
label.label Private Key location
|
||||
input(type='text', placeholder='e.g. /etc/wiki/keys/git.pem', v-model='conf.gitAuthSSHKey')
|
||||
span.desc The full path to the private key on disk.
|
||||
span.desc The full path to the #[strong unencrypted] private key on disk.
|
||||
.column(v-show='conf.gitAuthType === "sshenv"')
|
||||
p.control.is-fullwidth
|
||||
label.label Private Key Environment Variable
|
||||
|
Reference in New Issue
Block a user