dev-sidecar/packages/core/src/config.js

301 lines
8.9 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

const fs = require('fs')
const Shell = require('./shell')
const lodash = require('lodash')
const defConfig = require('./config/index.js')
const jsonApi = require('@docmirror/mitmproxy/src/json')
const request = require('request')
const path = require('path')
const log = require('./utils/util.log')
const mergeApi = require('./merge.js')
let configTarget = lodash.cloneDeep(defConfig)
function get () {
return configTarget
}
const getDefaultConfigBasePath = function () {
return get().server.setting.userBasePath
}
function _getRemoteSavePath (prefix = '') {
const dir = getDefaultConfigBasePath()
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir)
}
return path.join(dir, prefix + 'remote_config.json5')
}
function _getConfigPath () {
const dir = getDefaultConfigBasePath()
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir)
}
return path.join(dir, 'config.json')
}
let timer
const configApi = {
async startAutoDownloadRemoteConfig () {
if (timer != null) {
clearInterval(timer)
}
const download = async () => {
try {
await configApi.downloadRemoteConfig()
configApi.reload()
} catch (e) {
log.error('定时下载远程配置并重载配置失败', e)
}
}
await download()
timer = setInterval(download, 24 * 60 * 60 * 1000) // 1天
},
downloadRemoteConfig () {
if (get().app.remoteConfig.enabled !== true) {
return
}
const remoteConfigUrl = get().app.remoteConfig.url
// eslint-disable-next-line handle-callback-err
return new Promise((resolve, reject) => {
log.info('开始下载远程配置:', remoteConfigUrl)
request(remoteConfigUrl, (error, response, body) => {
if (error) {
log.error('下载远程配置失败, error:', error, ', response:', response, ', body:', body)
reject(error)
return
}
if (response && response.statusCode === 200) {
if (body == null || body.length < 2) {
log.warn('下载远程配置成功,但内容为空:', remoteConfigUrl)
resolve()
return
} else {
log.info('下载远程配置成功:', remoteConfigUrl)
}
// 尝试解析远程配置,如果解析失败,则不保存它
let remoteConfig
try {
remoteConfig = jsonApi.parse(body)
} catch (e) {
log.error(`远程配置内容格式不正确, url: ${remoteConfigUrl}, body: ${body}`)
remoteConfig = null
}
if (remoteConfig != null) {
const remoteSavePath = _getRemoteSavePath()
fs.writeFileSync(remoteSavePath, body)
log.info('保存远程配置文件成功:', remoteSavePath)
} else {
log.warn('远程配置对象为空:', remoteConfigUrl)
}
resolve()
} else {
log.error('下载远程配置失败, response:', response, ', body:', body)
let message
if (response) {
message = '下载远程配置失败: ' + response.message + ', code: ' + response.statusCode
} else {
message = '下载远程配置失败: response: ' + response
}
reject(new Error(message))
}
})
})
},
readRemoteConfig () {
if (get().app.remoteConfig.enabled !== true) {
return {}
}
const path = _getRemoteSavePath()
try {
if (fs.existsSync(path)) {
const file = fs.readFileSync(path)
log.info('读取远程配置文件成功:', path)
return jsonApi.parse(file.toString())
} else {
log.warn('远程配置文件不存在:', path)
}
} catch (e) {
log.error('读取远程配置文件失败:', path, ', error:', e)
}
return {}
},
readRemoteConfigStr () {
if (get().app.remoteConfig.enabled !== true) {
return '{}'
}
try {
const path = _getRemoteSavePath()
if (fs.existsSync(path)) {
const file = fs.readFileSync(path)
log.info('读取远程配置文件内容成功:', path)
return file.toString()
} else {
log.warn('远程配置文件不存在:', path)
}
} catch (e) {
log.error('读取远程配置文件内容失败:', e)
}
return '{}'
},
/**
* 保存自定义的 config
* @param newConfig
*/
save (newConfig) {
// 对比默认config的异同
let defConfig = configApi.getDefault()
// 如果开启了远程配置,则读取远程配置,合并到默认配置中
if (get().app.remoteConfig.enabled === true) {
defConfig = mergeApi.doMerge(defConfig, configApi.readRemoteConfig())
}
// 计算新配置与默认配置(启用远程配置时,含远程配置)的差异,并保存到 config.json 中
const diffConfig = mergeApi.doDiff(defConfig, newConfig)
const configPath = _getConfigPath()
fs.writeFileSync(configPath, jsonApi.stringify(diffConfig))
log.info('保存 config.json 自定义配置文件成功:', configPath)
// 重载配置
const allConfig = configApi.set(diffConfig)
return {
diffConfig,
allConfig
}
},
doMerge: mergeApi.doMerge,
doDiff: mergeApi.doDiff,
/**
* 读取 config.json 后,合并配置
* @returns {*}
*/
reload () {
const configPath = _getConfigPath()
let userConfig
if (!fs.existsSync(configPath)) {
userConfig = {}
log.info('config.json 文件不存在:', configPath)
} else {
const file = fs.readFileSync(configPath)
log.info('读取 config.json 成功:', configPath)
const fileStr = file.toString()
userConfig = fileStr && fileStr.length > 2 ? jsonApi.parse(fileStr) : {}
}
const config = configApi.set(userConfig)
return config || {}
},
update (partConfig) {
const newConfig = lodash.merge(configApi.get(), partConfig)
configApi.save(newConfig)
},
get,
set (newConfig) {
if (newConfig == null) {
log.warn('newConfig 为空,不做任何操作')
return configTarget
}
return configApi.load(newConfig)
},
load (newConfig) {
// 以用户配置作为基准配置,是为了保证用户配置的顺序在前
const merged = newConfig != null ? lodash.cloneDeep(newConfig) : {}
mergeApi.doMerge(merged, defConfig) // 合并默认配置
mergeApi.doMerge(merged, configApi.readRemoteConfig()) // 合并远程配置
if (newConfig != null) {
mergeApi.doMerge(merged, newConfig) // 再合并一次用户配置,使用户配置重新生效
}
mergeApi.deleteNullItems(merged) // 删除为null及[delete]的项
configTarget = merged
log.info('加载及合并远程配置完成')
return configTarget
},
getDefault () {
return lodash.cloneDeep(defConfig)
},
addDefault (key, defValue) {
lodash.set(defConfig, key, defValue)
},
// 移除用户配置,用于恢复出厂设置功能
async removeUserConfig () {
const configPath = _getConfigPath()
if (fs.existsSync(configPath)) {
// 读取 config.json 文件内容
const fileStr = fs.readFileSync(configPath).toString().replace(/\s/g, '')
// 判断文件内容是否为空或空配置
if (fileStr === '' || fileStr === '{}') {
fs.rmSync(configPath)
return false // config.json 内容为空或为空json
}
// 备份用户自定义配置文件
fs.renameSync(configPath, configPath + '.bak' + new Date().getTime() + '.json')
// 重新加载配置
configApi.load(null)
return true // 删除并重新加载配置成功
} else {
return false // config.json 文件不存在或内容为配置
}
},
resetDefault (key) {
if (key) {
let value = lodash.get(defConfig, key)
value = lodash.cloneDeep(value)
lodash.set(configTarget, key, value)
} else {
configTarget = lodash.cloneDeep(defConfig)
}
return configTarget
},
async getVariables (type) {
const method = type === 'npm' ? Shell.getNpmEnv : Shell.getSystemEnv
const currentMap = await method()
const list = []
const map = configTarget.variables[type]
for (const key in map) {
const exists = currentMap[key] != null
list.push({
key,
value: map[key],
exists
})
}
return list
},
async setVariables (type) {
const list = await configApi.getVariables(type)
const noSetList = list.filter(item => {
return !item.exists
})
if (list.length > 0) {
const context = {
root_ca_cert_path: configApi.get().server.setting.rootCaFile.certPath
}
for (const item of noSetList) {
if (item.value.indexOf('${') >= 0) {
for (const key in context) {
item.value = item.value.replcace(new RegExp('${' + key + '}', 'g'), context[key])
}
}
}
const method = type === 'npm' ? Shell.setNpmEnv : Shell.setSystemEnv
return method({ list: noSetList })
}
}
}
module.exports = configApi