sails-hook-blacksails
Version:
A Sails Micro-app architecture framework
446 lines (416 loc) • 12.5 kB
JavaScript
/** @module ConfigHelper
* version: 20180301
* lastUpdater: Kent
*/
import _ from 'lodash';
import fs from 'fs';
// import appRootPath from 'app-root-path';
const appRootPath = sails.config.blacksails.sailsRoot;
const configProdPath = 'config/env/production';
const configDevPath = 'config/env/development';
const configTestPath = 'config/env/test';
const configLocalPath = 'config/local';
const configLocalDefaultPath = 'config/local.default.js';
const currentConfigPath = 'config/local.current.js';
module.exports = {
getAuthConfig() {
return sails.config.authorization;
},
getProjectConfig(projectName) {
try {
const config = sails.config.project;
if (projectName) {
return config[projectName];
}
return config;
} catch (e) {
throw e;
}
},
isDropMode() {
return (sails.config.models.migrate === 'drop');
},
isSafeMode() {
return (sails.config.models.migrate === 'safe');
},
isInitTestData() {
return (sails.config.models.initTestData == true);
},
isProduction() {
return (sails.config.environment === 'production');
},
isDevelopment() {
return (sails.config.environment === undefined)
|| (typeof sails.config.environment === 'undefined')
|| (sails.config.environment === 'development');
},
isTesting() {
return (sails.config.environment === 'test');
},
getBaseUrl() {
return sails.config.appUrl;
},
getDefualtVuePath() {
return '../../../../sails-hook-admin/views/admin/default/vue.ejs';
},
getDefaultLayoutPath() {
return '../../sails-hook-admin/views/admin/default';
},
async update(formatConfig) {
try {
for (let data of formatConfig) {
data = {
...data,
key: data.key || '',
};
const modelConfig = await Config.findOne({
where: {
name: data.name,
key: data.key,
},
});
if (modelConfig) {
modelConfig.value = data.value;
modelConfig.type = data.type;
if (data.desc) {
modelConfig.description = data.desc;
}
await modelConfig.save();
} else {
await Config.create(data);
}
}
return true;
} catch (e) {
throw e;
}
},
async sync(extraConfig) {
sails.log.info('Syncing model config & local config');
try {
// sails.log.debug('!!! appRootPath=>', appRootPath)
if (extraConfig) {
const formatExtraConfig = ConfigHelper.jsonTOPath(JSON.parse(JSON.stringify(extraConfig)));
await this.update(formatExtraConfig);
}
const localConfig = require(`${appRootPath}/${configLocalPath}`);
const { project } = sails.config;
const dbConfig = await ConfigHelper.getModelJSONConfig();
let envConfig = {};
if (ConfigHelper.isProduction()) {
envConfig = require(`${appRootPath}/${configProdPath}`);
} else if (ConfigHelper.isDevelopment()) {
envConfig = require(`${appRootPath}/${configDevPath}`);
} else {
delete localConfig.port;
delete localConfig.appUrl;
delete localConfig.environment;
envConfig = require(`${appRootPath}/${configTestPath}`);
}
const allConfig = _.merge(
{},
// 最後載入環境 config,確保以 env config 為主
{ project },
envConfig,
localConfig,
dbConfig,
);
delete allConfig.passport;
delete allConfig.bootstrapTimeout;
const pureJSONConfig = JSON.parse(JSON.stringify(allConfig));
await this.update(ConfigHelper.jsonTOPath(pureJSONConfig));
return await ConfigHelper.load();
} catch (e) {
throw e;
}
},
load: async () => {
sails.log.info('Updating sails configs');
try {
const modelConfig = await ConfigHelper.getModelJSONConfig();
sails.config = _.merge(
{},
sails.config,
modelConfig,
);
// sails.emit('hook:admin-config:reloaded');
// 把 config 寫入到 local.current.js
const data = `module.exports = ${JSON.stringify({
aws: sails.config.aws,
...modelConfig,
}, null, 2)};`;
await fs.writeFileSync(`${appRootPath}/${currentConfigPath}`, data, 'utf8');
return sails.config;
} catch (e) {
throw e;
}
},
init: () => {
},
jsonTOPath: (data) => {
// sails.log.debug(data);
try {
let result = [];
for (const key of Object.keys(data)) {
if (_.isArray(data[key])) {
result.push({
name: key,
value: JSON.stringify(data[key]),
type: 'array',
});
} else if (_.isObject(data[key])) {
let formatObject = ConfigHelper.getPath(data[key], '', []);
formatObject = formatObject.map(e => ({
name: key,
...e,
}));
result = result.concat(formatObject);
} else {
const item = {
name: key,
value: data[key],
type: 'text',
};
if (!Number.isNaN(parseInt(data[key], 10))) {
item.type = 'number';
}
if (!_.isEmpty(data[key]) && (data[key] == true || data[key] == false)) {
item.type = 'boolean';
}
result.push(item);
}
}
return result;
} catch (e) {
throw e;
}
},
pathTOJSON: (data) => {
try {
const result = {};
data.forEach((info) => {
let { name } = info;
// if (name.startsWith('project-')) {
// // eslint-disable-next-line no-param-reassign
// info.key = `${name}.${info.key}`;
// name = 'project';
// }
result[name] = result[name] || {};
if (info.key) {
const pathArray = info.key.split('.');
if (pathArray.length > 0) {
const value = info.type === 'array' ? JSON.parse(info.value) : info.value;
result[name] = ConfigHelper.arrayTOObject(result[name], pathArray, value);
} else {
let { value } = info;
if (info.type === 'boolean') {
value = !!info.value;
} else if (info.type === 'number') {
value = parseInt(info.value, 10);
}
result[name] = value;
}
} else {
let { value } = info;
if (info.type === 'boolean') {
value = !!info.value;
} else if (info.type === 'number') {
value = parseInt(info.value, 10);
}
result[name] = value;
}
});
// sails.log.info(result);
return result;
} catch (e) {
throw e;
}
},
getPath: (curData, path, result, allData, nowPointer) => {
const data = curData;
try {
if (_.isEmpty(data)) {
// 判斷該物件內是否還有其他的物件
for (const item in allData) {
if (!_.isEmpty(allData[item]) && _.isObject(allData[item])) {
ConfigHelper.getPath(allData[item], item, result, allData, item);
break;
}
}
return result;
}
for (const key of Object.keys(data)) {
if (_.isArray(data[key])) {
const value = data[key];
result.push({
key: `${path}${path ? '.' : ''}${key}`,
value: JSON.stringify(value),
type: 'array',
});
delete data[key];
return ConfigHelper.getPath(data, path, result, allData, nowPointer);
}
if (_.isObject(data[key])) {
return ConfigHelper.getPath(data[key], `${path}${path ? '.' : ''}${key}`, result, data, key);
}
const item = {
key: `${path}${path ? '.' : ''}${key}`,
value: data[key],
type: 'text',
};
if (!Number.isNaN(parseInt(data[key], 10))) {
item.type = 'number';
}
if (!_.isEmpty(data[key]) && (data[key] == true || data[key] == false)) {
item.type = 'boolean';
}
result.push(item);
delete data[key];
return ConfigHelper.getPath(data, path, result, allData, nowPointer);
}
} catch (e) {
throw e;
}
},
arrayTOObject: (obj, keys, value) => {
try {
const lastKey = keys.pop();
const lastObj = keys.reduce((obj, key) =>
obj[key] = obj[key] || {},
obj);
lastObj[lastKey] = value;
return obj;
} catch (e) {
throw e;
}
},
getModelJSONConfig: async () => {
try {
let modelConfig = await Config.findAll();
modelConfig = modelConfig.map(data => data.toJSON());
modelConfig = ConfigHelper.pathTOJSON(modelConfig);
return modelConfig;
} catch (e) {
throw e;
}
},
getApiKey(key) {
try {
const apiKeyObject = sails.config['api-key'];
if (apiKeyObject) {
const targetKey = _.get(apiKeyObject, key);
sails.log(`[!] Get API KEY '${key}', value: '${targetKey}'.`);
return targetKey || null;
}
return null;
} catch (e) {
sails.log.error(e);
throw e;
}
},
async insertMenuItem(itemArray) {
// sails.log.debug('Inserting new menuItems...');
try {
if (!this.isDevelopment()) {
return false;
}
const defaultRole = await Role.findOne({
where: { authority: 'admin' },
});
/* eslint no-await-in-loop: 0 */
for (const item of itemArray) {
if (!item.title) {
sails.log.warn(`Missing Menuitem Title founded. menu href '${item.title}' has no title('${item.title}').`);
}
let ParentMenuItemId = item.ParentMenuItemId;
if (item.parentKey) {
const parentItem = await MenuItem.findOne({
where: {
key: item.parentKey,
},
});
if (parentItem) ParentMenuItemId = parentItem.id;
}
const menuItem = await MenuItem.create({
icon: item.icon,
iconType: item.iconType,
model: item.model,
href: item.href,
title: item.title,
key: item.key || null,
order: item.order || 100,
isActive: true,
ParentMenuItemId,
});
if (typeof item.role === 'string' && item.role !== 'admin') {
sails.log.info(`Menuitem's extra authority founded. menu '${item.title}' needs role '${item.role}'.`);
const targetRole = await Role.findOne({
where: { authority: item.role },
});
if (!targetRole) {
throw Error(`Can not find target authority '${item.role}'.`);
}
await RoleMenuItem.create({
name: 'READ_WRITE',
RoleId: targetRole.id,
MenuItemId: menuItem.id,
});
} else if (_.isArray(item.roles)) {
sails.log.info('Menuitem\'s extra authority authority ARRAY founded.');
for (const role of item.roles) {
sails.log.info(`menu '${item.title}' needs role '${role}'.`);
const targetRole = await Role.findOne({
where: { authority: role },
});
if (!targetRole) {
throw Error(`Can not find target authority '${role}'.`);
}
await RoleMenuItem.create({
name: 'READ_WRITE',
RoleId: targetRole.id,
MenuItemId: menuItem.id,
});
}
} else {
await RoleMenuItem.create({
name: 'READ_WRITE',
RoleId: defaultRole.id,
MenuItemId: menuItem.id,
});
}
}
return true;
} catch (e) {
sails.log.error(e);
throw e;
}
},
// support TEXT only
async updateByKey(key, value) {
let keyPath = key.split('.');
const dbName = keyPath[0];
keyPath = keyPath.splice(1);
const dbKey = keyPath.join('.');
const configEntry = await Config.findOne({
where: {
name: dbName,
key: dbKey,
},
});
sails.log(key, dbName, dbKey);
sails.log(configEntry);
if (configEntry == null) {
await Config.create({
name: dbName,
key: dbKey,
value,
type: 'text',
});
} else {
configEntry.value = value;
configEntry.save();
}
await ConfigHelper.sync();
await ConfigHelper.load();
},
};