nyx_server
Version:
Node内容发布
725 lines (654 loc) • 29.3 kB
JavaScript
/* global Buffer */
/* global global */
/*****************************************************************
模板
******************************************************************/
var _ = require('lodash');
var fs = require("fs");
var path = require('path');
var Promise = require('bluebird');
var ejs = require("ejs");
var uriresource = require("./utils/UriResource");
var TemplateContent = require("./TemplateContent");
var publishUtil = require("./utils/PublishUtil"); //内容分发工具
var chipLoader = require("./ChipLoader");
var config = require("../config/default-config");
var templateLoader = require("./TemplateLoader");
var EmptyContext = require("./EmptyContext");
var chipFn = require("./Chip");
var DBTemplate = require("../db/DBTemplate");
var dbtemplate = new DBTemplate(config.NYX_DB_CONFIG_NAME);
var PermissionError = require("./exception/PermissionError");
var PermissionConstant = require("./PermissionConstant");
var jwt = require('jsonwebtoken');
var cert = "1OTzi54b7C549J4roOPsKuNUzNW93dMI1Vt2m5MYRYsEQuwIKXRYyMNy1ArKOAYoKqbZkc0jENKjJ4ZSCYom2bmeS2qYWQ7A2KeAp3DoOfNeKfxcaKbaoq2bmZUsMVD3jwXPWwjEFjuZr4aorbKhg4nK5XMquGUH5ij98cUSFHVD9xrf9WEykssdJlL7wK5sZXxJjC3D6r8iDIiUz2j1qW5qigxPT7tFVfPdZZF20V95Gdd7g9m0ZastvwBe8jXW";
var certuser = 'yhstyg';
function escapeRegExpChars(string) {
if (!string) {
return '';
}
return String(string).replace(/[|\\{}()[\]^$+*?.]/g, '\\$&');
};
function chipRegbuild() {
return new RegExp("<%=\s*chip\\(([^%>]*)\\)\s*%>", "ig");
}
function chipBlockbuild(content) {
var blocks = content.match(chipRegbuild());
return _.map(blocks, function (block) {
var arr = chipRegbuild().exec(block);
return {
original: arr[0],
params: arr[1]
};
});
}
function Template(basePath, ownerProject) {
this.basePath = basePath;
this.config = null;
this._ownerProject = ownerProject;
this._template = this.initConfig();
}
Template.prototype.initConfig = function () {
var configPath = path.join(this.basePath, "package.json");
this.config = require(configPath);
var main = this.config.main;
this.templateFile = path.join(this.basePath, this.config.template); //模板文件
this.distTemplateFile = path.join(this.basePath, config.DIST_DIR, this.config.template); //build后模板文件
this.templateContent = new TemplateContent(this.templateFile);
this.name = this.config.name;
this.publishUrlPatter = this.config.name; //发布地址匹配模式
this._ui = this.config.ui; //ui配置
var mainPath = path.join(this.basePath, main);
return require(mainPath);
};
/**
* 模板支持的设备
*/
Template.prototype.getSupportDevices = function () {
return this.config.devices;
};
/**
* 得到匹配路径
*/
Template.prototype.getMatchPath = function () {
return this.config.matchPath;
};
/**
* 得到文件类型
*/
Template.prototype.getFileType = function () {
return this.config.fileType;
};
Template.prototype.getFilePath = function () {
return this.templateFile;
};
Template.prototype.getName = function () {
return this.name;
};
module.exports.Template = Template;
/*************************************
* 模板函数
**************************************/
function _render(template_content, template_data) {
var template = ejs.compile(template_content);
var retContent = template(template_data);
return retContent;
}
function getTempalteContent(template, requestInfo) {
var mode = requestInfo.mode;
if (mode == "publish") {
var distTemplateContent = new TemplateContent(template.distTemplateFile);
return distTemplateContent.getContent(requestInfo);
} else {
return template.templateContent.getContent(requestInfo);
}
}
function hasPermission(template , permissionType , requestInfo){
var context = this.log ? this : EmptyContext;
var apiFun = template._template["hasPermission"];
if (apiFun && _.isFunction(apiFun)) {
return apiFun.bind(context)( permissionType , requestInfo);
}else{
return true;
}
}
/**
* 得到模板内容和和模板数据
* @params {Object}requestInfo
*/
function getTempalteContentAndData(template, requestInfo) {
var context = this.log ? this : EmptyContext;
var log = context.log;
log.info("开始处理模板内容");
var _requestInfo = _.clone(requestInfo, true);
_requestInfo.onwerTemplate = template.name;
var mode = _requestInfo.mode;
var templateDataId = _requestInfo.params.dataId;
return Promise.all([template._template.getData.bind(context)(_requestInfo.params.dataId), getTempalteContent(template, _requestInfo)]).then(function (values) {
var template_data = values[0];
var template_content = values[1];
if (mode != "publish") { //发布模式不需要转换为本地路径
template_content = uriresource.transformUrl(template_content, template.basePath); //替换资源url,需要区分preview 和 publish
}
var chip_block_infos = chipBlockbuild(template_content);
var newblock = _.map(chip_block_infos, function (chip_block_info) {
return processChip(chip_block_info);
});
return Promise.resolve(newblock).map(function (block) {
var originalContent = block.original; //原始数据,需要被替换数据
var params = block.params; //chipprocess chip方法参数
var chipType = params[0];
if (chipType.split(".").length == 1) {
chipType = template._ownerProject.name + "." + chipType;
}
return loadChip.bind(context)(template, chipType).then(function (chip) {
var title = (params.length == 6) ? params[4] : chipType;
var nodeId = (params.length == 6) ? params[5] : params[4];
var chipParams = {'chipType':params[0],'id':params[1],'ssi':params[2],'bindTemplateId':params[3],'title':title , nodeId:nodeId};
if(params[3] == true){
chipParams['templateDataId']=templateDataId;
}
return chipFn.preview.bind(context)(chip, chipParams, mode).then(function (retContent) {
if (mode == 'edit') {
return wrapComment(retContent, chipParams, _requestInfo , template._ownerProject.name);
} else {
return retContent;
}
});
}).then(function (content) {
return { 'original': originalContent, 'content': content };
});
}).then(function (chipInfos) {
chipInfos.forEach(function (chipInfo) {
var original = chipInfo.original;
var content = chipInfo.content;
template_content = template_content.replace(original, content);
});
return [template_content, template_data];
});
}).then(function (result) {
log.info("完成模板内容处理");
return result;
}).catch(function (err) {
log.error(err);
throw err;
});
}
/**
* 模板预览
* @param requestInfo 数据参数
* @param retData 是否同时返回数据
* @return 模板解析后的html内容
*/
function _preview(template, requestInfo) {
var context = this.log ? this : EmptyContext;
var _requestInfo = _.clone(requestInfo, true);
_requestInfo.onwerTemplate = template.name;
return getTempalteContentAndData.bind(context)(template, requestInfo).spread(function (content, data) {
var pindex = _requestInfo.params.pindex; //分页号
var pagerInfo = null;
var template_data = data;
if (_.isArray(template_data)) { //如果是数组则是分页
var pageSize = template_data.length;
template_data = template_data[pindex];
//加入分页信息
pagerInfo = {
'pageSize': pageSize, //分页长度
'index': pindex //当前页号
};
}
var renderData = _.cloneDeep(template_data||{});
renderData.requestInfo = requestInfo; //请求信息
renderData.config = template.config; //模板基本信息
renderData.project = template._ownerProject.config; //项目配置信息
renderData.data = template_data; //模板实例数据
renderData.pagerInfo = pagerInfo; //分页信息
return _render(content, renderData);
});
}
function preview(requestInfo) {
var context = this.log ? this : EmptyContext;
var log = context.log;
log.info("预览模板");
var promise = templateLoader.loaderTemplate.bind(context)(requestInfo); //根据路径得到匹配的路径
return promise.then(function (template) {
log.info("选择模板 , 模板名称", template.getName());
var _hasPermission = hasPermission.bind(context)(template , PermissionConstant.TEMPLATE_PREVIEW ,requestInfo);
if(!_hasPermission){
throw new PermissionError("没有权限预览");
};
return _preview.bind(context)(template, requestInfo);
}).then(function (result) {
return result;
}).catch(function(err){
throw err;
});
}
module.exports.preview = preview;
function ui(requestInfo, uiname) {
var context = this.log ? this : EmptyContext;
var promise = templateLoader.loaderTemplate.bind(context)(requestInfo); //根据路径得到匹配的路径
return promise.then(function (template) {
var _hasPermission = hasPermission.bind(context)(template , PermissionConstant.TEMPLATE_UI , requestInfo);
if(!_hasPermission){
throw new PermissionError("没有权限执行UI操作");
}
return _ui.bind(context)(template, uiname, requestInfo);
}).catch(function (err) {
throw err;
});
}
function _ui(template, uiname, requestInfo) {
var context = this.log ? this : EmptyContext;
var data = {};
var funName = "init" + uiname.replace(/(\w)/, function (v) { return v.toUpperCase(); }) + "Data";
var dataFun = template._template[funName];
if (dataFun && _.isFunction(dataFun)) {
data = dataFun.bind(context)(requestInfo);
}
return Promise.resolve(data).then(function (data) {
var uibasePath = template._ui.base || ".";
var uitemplate = path.join(template.basePath, uibasePath, uiname + ".ejs");
return new Promise(function (resolve, reject) {
fs.readFile(uitemplate, { encoding: 'utf8', flag: 'r' }, function (err, content) {
if (err) {
reject(err);
} else {
var template = ejs.compile(content);
var retContent = template(data);
retContent = uriresource.transformUrl(retContent, template.basePath + "/" + uibasePath);
resolve(retContent);
}
});
});
});
}
module.exports.ui = ui;
function api(requestInfo, apiNmae) {
var context = this || EmptyContext;
var promise = templateLoader.loaderTemplate.bind(context)(requestInfo); //根据路径得到匹配的路径
return promise.then(function (template) {
var _hasPermission = hasPermission.bind(context)(template , PermissionConstant.TEMPLATE_API , requestInfo);
if(!_hasPermission){
throw new PermissionError("没有授权调用API");
}
return _api.bind(context)(template, apiNmae, requestInfo);
}).catch(function (err) {
throw err;
});
}
function _api(template, apiname, requestInfo) {
var context = this.log ? this : EmptyContext;
var data = {};
var config = template.config;
var apiList = config.exportApi;
if (typeof apiList == "undefined" || apiList.indexOf(apiname) < 0) { //如果没有暴漏相应的api则不许调用
return Promise.reject({ message: "非法调用" });
}
var apiFun = template._template[apiname];
if (apiFun && _.isFunction(apiFun)) {
data = apiFun.bind(context)(requestInfo);
}
return Promise.resolve(data);
}
module.exports.api = api;
/**
* 产生chipid
*/
function genChipId(template) {
return template.templateContent.getOriginContent().then(function (content) {
var chip_block_infos = chipBlockbuild(content);
var newblock = _.map(chip_block_infos, function (chip_block_info) {
return processChip(chip_block_info);
});
return Promise.resolve(newblock).map(function (block) {
var originalContent = block.original; //原始数据,需要被替换数据
var params = block.params; //chipprocess chip方法参数
var chipType = params[0];
return loadChip(template, chipType).then(function (chip) {
return chip.config.static || false;
}).then(function (isStatic) {
if (!isStatic) { //如果是静态模板则不生成id
var id = params[1];
if (id=='__') { //如果id没有则生成新的id
var splitType = chipType.split(".");
var _chipType = splitType[0];
var projectName = splitType[1] || template._ownerProject.name;
return dbtemplate.query("insert into chip_data_t(chipType , createdTime) values(?, now())", [projectName+"."+_chipType])
.then(function (result) {
return result.insertId;
}).then(function (id) {
//替换id占位符
var replacedContent = originalContent.replace(/['"]__['"]/ , id);
content = content.replace(originalContent, replacedContent);
return template.templateContent.syncContent(content);
}).catch(function (err) {
throw err;
});
} else {
return true;
}
}
});
}).then(function () {
return true;
}).catch(function (err) {
throw err;
});
});
}
module.exports.genChipId = genChipId;
function wrapComment(content, params, requestInfo , projectName) {
var id = params.id;
var chipType = params.chipType;
var ssi = params.ssi;
var bindTemplateId = params.bindTemplateId;
var title = params.title;
var nodeid = params.nodeId;
var comment = "<!--chip:start;id=" + id + ";projectName="+projectName+";name="+title+";type=" + chipType + ";ssi="+ssi+";bindTemplateId="+bindTemplateId+";nodeid=" + nodeid + ";editable=true;requestInfo=" + JSON.stringify(requestInfo) + "-->\n";
comment += content + "\n";
comment += "<!--chip:end;id=" + id + "-->\n";
return comment;
}
function loadChip(template, chipType) {
var context = this.log ? this : EmptyContext;
if (chipType.split(".").length == 1) {
chipType = template._ownerProject.name + "." + chipType;
}
return chipLoader.loadChip.bind(context)(chipType);
}
function edit(requestInfo, nodeid, chipInfo) {
var context = this.log ? this : EmptyContext;
var promise = templateLoader.loaderTemplate.bind(context)(requestInfo); //根据路径得到匹配的路径
return promise.then(function (template) {
var _hasPermission = hasPermission.bind(context)(template , PermissionConstant.TEMPLATE_EDIT , requestInfo);
if(!_hasPermission){
throw new PermissionError("没有授权调用API");
}
return _edit(template, requestInfo, nodeid, chipInfo);
}).catch(function (err) {
throw err;
});
}
module.exports.edit = edit;
/**
* 模板编辑
* @param {Object}requestInfo 当前模板的请求信息
* @param {String}nodeid 编辑的节点id
*/
function _edit(template, requestInfo, nodeid, chipInfo) {
var context = this.log ? this : EmptyContext;
var oldChipType = chipInfo.chipName;
var chipType = oldChipType;
var oldChipDataId = chipInfo.chipDataId;
var params = chipInfo.params;
return loadChip.bind(context)(template, chipType).then(function (chip) {
return chipFn.edit.bind(context)(chip, oldChipDataId, params);
}).then(function (chipDataId) {
if (oldChipDataId != chipDataId) { //新创建数据
var chipcode = "chip(\"" + oldChipType + "\" , " + chipDataId + ")";
return template.templateContent.replace(nodeid, chipcode).then(function (result) {
if (!result) {
throw new Error(result);
}
return result;
}).catch(function (err) {
throw new Error(err);
});
} else {
return chipLoader.loadChip.bind(context)(chipType).then(function (chip) {
return chipFn.preview.bind(context)(chip, chipDataId, params);
});
}
});
}
function processChip(chip_block_info) {
var context = this.log ? this : EmptyContext;
var log = context.log;
var params = chip_block_info.params;
var p = "([" + params + "])";
try {
var param = eval(p);
return { 'params': param, 'original': chip_block_info.original };
} catch (err) {
//没有上下文的变量
log.error("参数" + param + " , 没有在上下文环境");
throw new Error(err);
}
}
function expandRequestInfo(template, requestInfo, callback) {
var extra = requestInfo.params.extra;
if (!extra) {
extra = template.config.extra;
if (extra && _.isString(extra)) {
extra = extra.split(",");
extra = _.map(extra, function (s) {
return _.trim(s);
});
}
} else {
extra = _.trim(extra);
}
if (_.isArray(extra)) {
var allPromise = [];
extra.forEach(function (extraStr) {
var p = _.clone(requestInfo, true);
p.params.extra = extraStr;
var r = callback(template, p);
allPromise.push(r);
});
return Promise.all(allPromise).then(function () {
return true;
}).catch(function (err) {
throw err;
});
} else {
var p = _.clone(requestInfo, true);
var r = callback(template, p);
return Promise.resolve(r).catch(function (err) {
throw err;
});
}
}
/**
* 将数据内容发布到分发服务器上
* @return
*/
function publish(requestInfo) {
var context = this || EmptyContext;
var promise = templateLoader.loaderTemplate.bind(context)(requestInfo); //根据路径得到匹配的路径
return promise.then(function (template) {
var _hasPermission = hasPermission.bind(context)(template , PermissionConstant.TEMPLATE_PUBLISH , requestInfo);
if(!_hasPermission){
throw new PermissionError("没有授权发布");
};
return expandRequestInfo(template, requestInfo, _publish.bind(context));
}).catch(function (err) {
throw err;
});
}
module.exports.publish = publish;
function _publish(template, requestInfo) {
var context = this || EmptyContext;
var log = context.log;
return getTempalteContentAndData.bind(context)(template, requestInfo).spread(function (content, templateData) {
function publish(pubcontent, filename, fields) {
//得到需要发布的地址
return publishUtil.sendFile(pubcontent, filename, fields).then(function (result) {
return true;
}).catch(function (e) {
throw new Error("分发失败," + e ? e.message : "");
});
}
var allPromise = [];
if (_.isArray(templateData)) {
var count = templateData.length;
var retPublishUrl = "";
templateData.forEach(function (data, index) {
//加入分页信息
var pagerInfo = {
pageSize: count, //分页长度
index: index, //当前页号
urlPatter: "" //地址表达式
}; //分页地址表达式,预览和发布模式是不一样的
var renderData = _.cloneDeep(data||{}); //兼容上个版本的结构
renderData.data = data; //模板实例数据
renderData.pagerInfo = pagerInfo; //分页信息
renderData.requestInfo = requestInfo; //请求信息
renderData.config = template.config; //模板基本信息
renderData.project = template._ownerProject.config; //项目配置信息
var renderContent = _render(content, renderData);
var domain = template._template.getDomain.bind(context)(requestInfo.params.dataId, renderData);
var publishUrl = template._template.getPublishUrl.bind(context)(requestInfo.params.dataId, renderData);
log.info("分发地址 : " + domain + publishUrl);
if(index == 0){
retPublishUrl = domain + publishUrl;
}
var promise = publish(new Buffer(renderContent), path.basename(publishUrl),
[
{ fname: 'fe.cid', fvalue: domain }
, { fname: 'fe.filepath', fvalue: publishUrl }
, { fname: 'fe.syncflag', fvalue: '1' }
, { fname :'username' ,fvalue: certuser}
, { fname :'token' ,fvalue: getWebToken(domain , publishUrl , cert)}
]);
allPromise.push(promise);
});
return Promise.all(allPromise).then(function () {
return Promise.resolve(template._template.publishComplete.bind(context)(requestInfo.params.dataId ,retPublishUrl , renderData)).then(function () {
//返回首页
return retPublishUrl;
});
});
} else {
var renderData = _.cloneDeep(templateData||{}); //兼容上个版本的结构
renderData.data = templateData; //模板实例数据
renderData.pagerInfo = null; //分页信息
renderData.requestInfo = requestInfo; //请求信息
renderData.config = template.config; //模板基本信息
renderData.project = template._ownerProject.config; //项目配置信息
var renderContent = _render(content, renderData);
var domain = template._template.getDomain.bind(context)(requestInfo.params.dataId, renderData);
var publishUrl = template._template.getPublishUrl.bind(context)(requestInfo.params.dataId, renderData);
return publish(new Buffer(renderContent)
, path.basename(publishUrl)
, [ { fname: 'fe.cid', fvalue: domain }
, { fname: 'fe.filepath', fvalue: publishUrl }
, { fname: 'fe.syncflag', fvalue: '1' }
, { fname :'username' ,fvalue: certuser}
, { fname : 'token' ,fvalue: getWebToken(domain , publishUrl , cert)}]
).then(function () {
log.info("publish url : " + domain + publishUrl);
return true;
}).then(function () {
return Promise.resolve(template._template.publishComplete.bind(context)(requestInfo.params.dataId , domain+publishUrl)).then(function () {
return domain+publishUrl;
});
});
}
}).catch(function (err) {
throw err;
});
}
/**
* 模板下线
*/
function offline(requestInfo) {
var context = this || EmptyContext;
var promise = templateLoader.loaderTemplate.bind(context)(requestInfo); //根据路径得到匹配的路径
return promise.then(function (template) {
var _hasPermission = hasPermission.bind(context)(template , PermissionConstant.TEMPLATE_OFFLINE , requestInfo);
if(!_hasPermission){
throw new PermissionError("没有授权下线操作");
}
return expandRequestInfo(template, requestInfo, _offline.bind(context));
}).catch(function (err) {
throw err;
});
}
module.exports.offline = offline;
function _offline(template, requestInfo) {
var context = this || EmptyContext;
var log = context.log;
return Promise.resolve(template._template.getData(requestInfo.params.dataId)).then(function (templateData) {
function offline(filename, fields) {
//得到需要发布的地址
var offlineContent = '<!DOCTYPE><html><head><meta http-equiv="refresh" content="0;url=http://' + domain + '"> </head><body></body></html>';
return publishUtil.sendFile(offlineContent, filename, fields).then(function (result) {
return true;
}).catch(function (e) {
throw e;
});
}
var allPromise = [];
if (_.isArray(templateData)) {
var count = templateData.length;
templateData.forEach(function (data, index) {
var pagerInfo = {
pageSize: count, //分页长度
index: index, //当前页号
urlPatter: "" //地址表达式
}; //分页地址表达式,预览和发布模式是不一样的
var _data = _.cloneDeep(data||{});;
_data.data = data;
_data.pagerInfo = pagerInfo; //分页信息
_data.requestInfo = requestInfo; //请求信息
_data.config = template.config; //模板基本信息
_data.project = template._ownerProject.config; //项目配置信息
var domain = template._template.getDomain.bind(context)(requestInfo.params.dataId, _data);
var publishUrl = template._template.getPublishUrl.bind(context)(requestInfo.params.dataId, _data);
log.info("分发地址 : " + domain + publishUrl);
var promise = offline(path.basename(publishUrl),
[
{ fname: 'fe.cid', fvalue: domain }
, { fname: 'fe.filepath', fvalue: publishUrl }
, { fname: 'fe.syncflag', fvalue: '1' }
, { fname :'username' ,fvalue: certuser}
, { fname : 'token' ,fvalue: getWebToken(domain , publishUrl , cert)}
]);
allPromise.push(promise);
});
return Promise.all(allPromise).then(function () {
//下线成功后,回调模板下线接口
return Promise.resolve(template._template.offlineComplete.bind(context)(requestInfo.params.dataId)).then(function () {
return true;
});
});
} else {
var _data = _.cloneDeep(templateData||{});;;
_data.data = templateData;
_data.pagerInfo = null; //分页信息
_data.requestInfo = requestInfo; //请求信息
_data.config = template.config; //模板基本信息
_data.project = template._ownerProject.config; //项目配置信息
var domain = template._template.getDomain.bind(context)(requestInfo.params.dataId, _data);
var publishUrl = template._template.getPublishUrl.bind(context)(requestInfo.params.dataId, _data);
var time = (new Date()).getTime();
var token = jwt.sign({ 'fe.cid': domain , 'fe.filepath': publishUrl , 'time':time}, cert , { algorithm: 'HS256' });
return offline(path.basename(publishUrl)
, [ { fname: 'fe.cid', fvalue: domain }
, { fname: 'fe.filepath', fvalue: publishUrl }
, { fname: 'fe.syncflag', fvalue: '1' }
, { fname :'username' ,fvalue: certuser}
, { fname : 'token' ,fvalue: getWebToken(domain , publishUrl , cert)}]
).then(function () {
log.info("publish url : " + domain + publishUrl);
return true;
}).then(function () {
//下线成功后,回调模板下线接口
return Promise.resolve(template._template.offlineComplete.bind(context)(requestInfo.params.dataId)).then(function () {
return true;
});
});
}
});
}
function getWebToken(cid , filepath , cert){
var time = (new Date()).getTime();
var token = jwt.sign({ 'fe.cid': cid , 'fe.filepath': filepath , 'time':time}, cert , { algorithm: 'HS256' });
return token;
}
function resolve(expr, object) {
expr = expr.substring(1);
return _.get(object, expr);
}