UNPKG

nyx_server

Version:

Node内容发布

725 lines (654 loc) 29.3 kB
/* 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); }