UNPKG

flexbiz-server

Version:

Flexible Server

1,126 lines (1,056 loc) 36.6 kB
const controller = require('../controllers/controller'); const underscore =require('underscore'); const async = require("async"); const {evalute} = require("../libs/utils"); const utils = require("../libs/utils"); const {isSupperAdmin} = require("../libs/utils"); const PostBook = require('../libs/post-book'); const PostSocai = require('../libs/post-socai'); const moment = require("moment"); const numeral = require("numeral"); const fs = require("fs"); const _ = require("lodash") const joinModel2 = (arr,id_app, model, joinFields, fn,options={cache:true})=>{ return [...arr].joinModel2(id_app, model, joinFields, fn,options); } const fieldScheme = new global.Schema( { stt:{type:Number,default:0}, stt_col:{type:Number,default:0}, name:{type: String, required: "Yêu cầu nhập mã trường",index:true,lowercase:true,trim:true}, type:{type: String, default:'String',trim:true}, form:{type: String,trim:true},//user for type array or Mixed header:{type: String, required: "Yêu cầu nhập tên trường"}, header2:{type: String}, api_description:{type: String},//Miêu tả field trong tài liệu api sort:Number, unique:Boolean, default:global.Schema.Types.Mixed, lowercase:{type:Boolean,default:false}, uppercase:{type:Boolean,default:false}, required:global.Schema.Types.Mixed, index:{type:Boolean,default:false}, is_tmp:{type:Boolean,default:false}, is_title:{type:Boolean,default:false}, on_view:{type:Boolean,default:true}, maxlength:Number, min:global.Schema.Types.Mixed, max:global.Schema.Types.Mixed, ref_model:{type: String,trim:true}, ref_field:{type:String,trim:true}, ref_label:{type:String,trim:true}, ref_label_as:{type:String,trim:true}, ref_search_fields:{type:String,trim:true}, ref_data_fields:{type:String,trim:true}, ref_condition:{type:String,trim:true}, multiple:Boolean, not_display:global.Schema.Types.Mixed, not_input:global.Schema.Types.Mixed, min_width_display:{type:Number,default:80}, format:{type:String}, align:{type:String}, color:{type:String}, bold:Boolean, html_variant_display:{type:String}, html_component_display:{type:String}, html_component_input:{type:String}, render_on_list:String, render_on_view:String, render_title:String, grid_configs:{type:String,trim:true}, handle_value_changed:{type:String,trim:true}, handle_search_condition:{type:String,trim:true}, tab:{type:String,trim:true}, group:{type:String,trim:true}, help_text:{type:String,trim:true}, line:{type:Number,default:0} } ) const listinfoSchema = new global.Schema({ menu_position:String, code: {type: String, required: true,index:true,lowercase:true,trim:true}, title: {type: String, required: true}, title2: {type: String}, mother_code: {type: String,lowercase:true,trim:true}, api_code: {type: String,lowercase:true,trim:true}, model_code: {type: String,lowercase:true,trim:true}, fields:[fieldScheme], postinfos:[global.Schema.Types.Mixed], exfields:global.Schema.Types.Mixed, create_model: {type: Boolean, default: false}, breadcrumbs: {type: String,trim:true}, not_add:Boolean, not_edit:Boolean, not_delete:Boolean, not_copy:Boolean, //events handle_cell_clicked:{type: String,trim:true}, expression_check_save:{type:String,trim:true}, on_saved:{type:String,trim:true}, on_deleting:{type:String,trim:true}, on_deleted:{type:String,trim:true}, on_init:{type:String,trim:true}, on_init_update:{type:String,trim:true}, handle_view_server:{type:String,trim:true}, handle_oncreating_server:{type:String,trim:true}, handle_oncreated_server:{type:String,trim:true}, handle_onupdating_server:{type:String,trim:true}, handle_onupdated_server:{type:String,trim:true}, handle_ondeleting_server:{type:String,trim:true}, //condition search_required:Boolean, search_form:{type:String,trim:true}, default_condition:{type:String,trim:true}, require_condition:{type:String,trim:true}, // export_yn:Boolean, sort_by:String, not_need_right:{type: Boolean,default:false},//Có cần phân quyền hay không require_id_app:{type: Boolean,default:true},//Dữ liệu theo công ty hay dữ liệu chung private_data:Boolean, view_tabs:[], allow_users:{type: String,trim:true}, input_users:{type: String,trim:true}, create_menu:Boolean, show_view_tabs_on_grid:Boolean, form_size:{type:String,default:"md",trim:true}, // is_dashboard_item:{type: Boolean, default: false}, dashboard_default:{type: Boolean, default: true}, dashboard_ids:{type: String,trim:true}, dashboard_stt:{type: Number}, // get_other_fields:String, options:{}, status: {type: Boolean, default: true}, date_created: {type: Date, default: Date.now}, date_updated: {type: Date, default: Date.now}, user_created: {type: String, default: ''}, user_updated: {type: String, default: ''} }); if((global.configs||{}).createIndexes){ listinfoSchema.index({code: 1},{unique:true}); listinfoSchema.index({title: 1}); listinfoSchema.index({title2: 1}); listinfoSchema.index({mother_code: 1}); listinfoSchema.index({api_code: 1}); listinfoSchema.index({model_code: 1}); listinfoSchema.index({mother_code: 1}); listinfoSchema.index({is_dashboard_item: 1}); listinfoSchema.index({dashboard_ids: 1}); listinfoSchema.index({mother_code: "text",code:"text",title:"text"},{name:"listinfo_index2_text"}); listinfoSchema.index({status: 1}); listinfoSchema.index({user_created: 1,visible_to: 1,visible_to_users: 1}); } const model = global.mongoose.models.listinfo || global.mongoose.model('listinfo', listinfoSchema); const requireFields= model.requireFields ={ id_app:{type:String,required:true}, exfields:global.Schema.Types.Mixed, listinfo_code:String, status: {type: Boolean, default: true}, date_created: {type: Date, default: Date.now}, date_updated: {type: Date, default: Date.now}, user_created: {type: String, default: ''}, user_updated: {type: String, default: ''}, visible_to: {type: Number, default: 0}, visible_to_users: [String], } model.createSchema = (info)=>{ if(!info.create_model) return null; let api_code = info.api_code || info.code; const require_id_app = (info.require_id_app!=false && info.require_id_app!="false"); const _requireFields ={...requireFields} if(!require_id_app){ delete _requireFields.id_app; } let mySchema = new global.Schema(_requireFields,{strict: false}); let require_paths = Object.keys(mySchema.paths); let fields = {}; for(let f of info.fields.filter(f=>f.type!=="Link" && f.type!=="Files" && f.type!=="Action" && f.type!=="Group" && require_paths.indexOf(f.name)<0)){ if(f.unique){ f.index = true; } let _field = Object.assign({},f); if(_field.type=="DateTime" || _field.type=="Time") _field.type= "Date"; if(_field.type=="File" || _field.type=="OtherFile") _field.type= "Mixed"; if(_field.type=="Array") _field.type= "Mixed"; if(_field.type=="Pdf") _field.type= "String"; if(_field.type=="Image") _field.type= "String"; if(_field.type=="String") _field.trim = true; _field.required = false; delete _field.name; delete _field.header; delete _field.header2; delete _field.line; delete _field.sort; delete _field.unique; delete _field._id; delete _field.stt; delete _field.not_display; delete _field.not_search; delete _field.format; delete _field.align; delete _field.color; delete _field.form; delete _field.min_width_display; delete _field.html_component_display; delete _field.html_variant_display; delete _field.html_component_input; delete _field.handle_value_changed; delete _field.default; delete _field.multiple; delete _field.tab; delete _field.help_text; delete _field.grid_configs; delete _field.render_on_list; delete _field.render_on_view; delete _field.is_tmp; delete _field.is_title; delete _field.on_view; delete _field.handle_view_server; if(_field.type=="String" || _field.type=="Number" ){ _field.maxlength = _field.maxlength || 4000; }else{ delete _field.maxlength } fields[f.name] = _field } mySchema.add(fields); //create index for all fields if((global.configs||{}).createIndexes){ info.fields.filter(f=>!f.unique && f.type!="Array" && f.type!="Mixed" && require_paths.indexOf(f.name)<0).forEach(field=>{ mySchema.index({[field.name]:1}); }) //create unique index let uq = info.fields.filter(f=>f.unique); if(uq.length>0){ let indUq = {id_app:1}; uq.forEach((field) => { indUq[field.name] =field.sort || 1; }); mySchema.index(indUq,{unique: true }); } //create text index let textFields = info.fields.filter(f=>f.type==="String"); if(textFields.length>0){ let index_name = `${api_code}_index_text`; let indText = {}; textFields.forEach((field) => { indText[field.name] ="text"; }); mySchema.index(indText,{name:index_name}); //console.log("auto create index",index_name); } //create requiere index const requireIndex ={} require_paths.forEach(rq=>{ requireIndex[rq] =1; }) mySchema.index(requireIndex); } return mySchema; } model.updateModel = async (info,_newModel)=>{ if(info.toObject) info = info.toObject(); if(!info.create_model) return null; let model_code = info.api_code || info.model_code || info.code; if(!_newModel) _newModel = global.mongoose.models[model_code]; if(!_newModel) return; if(!_newModel.schema){ console.error("model does not have schema",_newModel); return; } // //console.log("update model...",model_code); const api_code = info.api_code || info.code; const _newList = global.controllers[api_code.toUpperCase()]; let require_paths = Object.keys(requireFields); let fields = [...info.fields]; //add ref fields let fields_have_ref =fields.filter(f=>f.ref_model && f.ref_model.indexOf("[")<0 && f.ref_field && f.type!=="Array" && f.type!=="Mixed" && f.ref_label) .map(f=>f.ref_label_as || f.ref_label) .filter(f=>!fields.find(gf=>gf.name===f)) .map(f=>{ return { name:f, type:"String" } }) //console.log("ref fields",fields_have_ref); fields = fields.concat(fields_have_ref); // fields.filter(f=>f.type!=="Link" && f.type!=="Files" && f.type!=="Action" && f.type!=="Group" && require_paths.indexOf(f.name)<0).forEach(field=>{ if(field.toObject) field = field.toObject(); const _field = {...field}; if(underscore.has(_newModel.schema.paths,field.name)){ if(_newModel.schema.paths[field.name].options && !_newModel.schema.paths[field.name].options.isDynamic){ return; } if(field.type==="Array" || field.type==="Mixed"){ return; } _newModel.schema.remove(field.name); } if(_field.type=="DateTime" || _field.type=="Time") _field.type= "Date"; if(_field.type=="File" || _field.type=="OtherFile") _field.type= "Mixed"; if(_field.type=="Array") _field.type= "Mixed"; if(_field.type=="Pdf") _field.type= "String"; if(_field.type=="Image") _field.type= "String"; if(_field.type=="String") _field.trim = true; if(model_code!=info.code){ _field.required = false;//phai luon dat la false neu khong se bi dung giua cac api khac dung chung model } _field.isDynamic = true; delete _field.name; delete _field.header; delete _field.header2; delete _field.line; delete _field.sort; delete _field.unique; delete _field._id; delete _field.stt; delete _field.not_display; delete _field.not_search; delete _field.format; delete _field.align; delete _field.color; delete _field.form; delete _field.min_width_display; delete _field.html_component_display; delete _field.html_variant_display; delete _field.html_component_input; delete _field.handle_value_changed; delete _field.default; delete _field.multiple; delete _field.tab; delete _field.help_text; delete _field.grid_configs; delete _field.render_on_list; delete _field.render_on_view; delete _field.is_tmp; delete _field.is_title; delete _field.on_view; delete _field.handle_view_server; if(_field.type=="String" || _field.type=="Number" ){ _field.maxlength = _field.maxlength || 4000; }else{ delete _field.maxlength } _newModel.schema.add({[field.name]:_field}); if(!field.unique && field.type!="Array" && field.type!="Mixed") _newModel.schema.index({[field.name]:1}); }) //create unique index if((global.configs||{}).createIndexes){ if(_newList && _newList.is_dynamic_list){ let uq = fields.filter(f=>f.unique); let indUq; if(uq.length>0){ indUq = {id_app:1}; uq.forEach((field) => { indUq[field.name] =field.sort || 1; }); } if(_newList.options.unique && _newList.options.unique.length>0){ let old_index_unique ={id_app:1}; _newList.options.unique.forEach(u=>{ let field = fields.find(f=>f.name===u); old_index_unique[u]= (field||{}).sort || 1; }); //console.log("drop current index unique...",old_index_unique); _newModel.collection.dropIndex(old_index_unique,()=>{ //if(e) return console.error("Can't drop index unique",old_index_unique,model_code,e); //create new unique if(indUq){ //console.log("create new index unique...",indUq); _newModel.schema.index(indUq,{unique: true }); } }) }else{ //create new unique if(indUq){ //console.log("create new index unique...",indUq) _newModel.schema.index(indUq,{unique: true }); } } } } } model.createModel = (info)=>{ if(!info.create_model) return null; let model_code = info.api_code || info.model_code || info.code; //models are not allowed to create new APIs if(global.secu_models.indexOf(model_code)>=0) return null; // let exModel = global.mongoose.models[model_code] if(!exModel){ let model_path = (((global.configs||{}).paths||{}).models || __dirname) + "/" + model_code + ".js" if(fs.existsSync(model_path)){ exModel = require(model_path); } } if(!exModel){ exModel = global.mongoose.model(model_code, model.createSchema(info)); }else{ model.updateModel(info,exModel); } return exModel; } const deletePost = (_listInfo,obj)=>{ return Promise.all((_listInfo.postinfos||[]).filter(info=>info.model && info.script && info.condition).map(postInfo=>{ let m = global.getModel(postInfo.model); let scriptCondition = postInfo.condition.indexOf("return ")<0?`return ${postInfo.condition}`:postInfo.condition; let condition = evalute(scriptCondition,{master:obj,data:obj,moment}); condition.id_app = obj.id_app; //console.log("delete post dynamic",postInfo.model,condition); return m.deleteMany(condition); })) } const post = (_listInfo,obj)=>{ if(obj.toObject) obj = obj.toObject(); return Promise.all((_listInfo.postinfos||[]).filter(info=>info.model && info.script && info.condition).map(postInfo=>{ return (async ()=>{ let script = `return (async ()=>{ try{ ${postInfo.script} }catch(e){ console.error("error post data",e) return {error:e} } })()` let data = await evalute(script,{master:{...obj},data:{...obj},moment,numeral}); if(data && data.error){ throw data.error } //console.log("post dynamic",postInfo.model,data); return new Promise((resolve,reject)=>{ if(postInfo.model==="socai"){ const postsocai = new PostSocai(obj, data); postsocai.run((e, rs)=>{ if(e) return reject(e); resolve(rs); }); }else{ let m = global.getModel(postInfo.model); const posttdttno = new PostBook(obj, data, m, function(detail, callback) { callback(detail); }); posttdttno.run(function(e, rs) { //console.log("posted dynamic",rs,e) if(e) return reject(e); resolve(rs); }) } }) })() })) } const dynamicFinding = (_listInfo,user,condition,next)=>{ if(_listInfo.api_code && _listInfo.api_code!=_listInfo.code){ //don't change condition of original api return next(null,condition); } if(_listInfo.private_data){ condition.user_created = user.email } if(_listInfo.require_condition){ try{ let script = _listInfo.require_condition; if(script.indexOf("return ")<0) script = `return ${script}`; const require_condition = evalute(script,{user,condition,moment,numeral}); if(require_condition && Object.keys(require_condition).length>0){ for(let key in require_condition){ condition[key] = require_condition[key]; } } }catch(e){ console.error(e); return next(`Error on require condition: ${e.message}`); } } next(null,condition); } const dynamicCreating = async (listInfo,user,obj,next)=>{ let _listInfo = await model.findOne({_id:listInfo._id}).lean(); if(!_listInfo) return next("Không tìm thấy danh mục này"); for(let key in _listInfo){ listInfo[key] = _listInfo[key]; } const require_id_app = (_listInfo.require_id_app!=false && _listInfo.require_id_app!="false"); const supportUsers = configs.supportUsers || [] if (!require_id_app && (_listInfo.input_users||"").toLowerCase().indexOf(user.email.toLowerCase())<0 && !supportUsers.includes(user.email.toLowerCase()) && !isSupperAdmin(user.email.toLowerCase())) { //console.log("support users",supportUsers,user.email.toLowerCase()) return next('Bạn không có quyền thực hiện thao tác này'); } if(_listInfo.handle_oncreating_server && _listInfo.handle_oncreating_server.trim()!==""){ if(_listInfo.handle_oncreating_server.indexOf("next")<0) return next("Script xử lý dữ liệu trước khi tạo yêu cầu gọi function next"); try{ await new Promise((resolve,reject)=>{ async.parallel([ (next)=>{ const func_body = `(async ()=>{ try{ ${_listInfo.handle_oncreating_server} }catch(e){ next(e); } })();` setImmediate(async ()=>{ try{ await evalute(func_body,{obj,user,getLib:global.getLib,getModel:global.getModel,utils,query:utils.query,async,controller,controllers:global.controllers,joinModel2,next,moment,numeral}) }catch(e){ console.error("error dynamic create",_listInfo.handle_oncreating_server,e) next(e); } }) } ],(e,rs)=>{ if(e){ console.error(e); return reject(e) } resolve(rs); }) }) }catch(e){ console.error(e); return next(e); } } next(null,obj); } const dynamicCreated = async (_listInfo,user,obj,next)=>{ if(_listInfo.handle_oncreated_server && _listInfo.handle_oncreated_server.trim()!==""){ if(_listInfo.handle_oncreated_server.indexOf("next")<0) return next("Script xử lý dữ liệu sau khi tạo yêu cầu gọi function next"); try{ await new Promise((resolve,reject)=>{ async.parallel([ (next)=>{ const func_body = `(async ()=>{ try{ ${_listInfo.handle_oncreated_server} }catch(e){ next(e); } })();` setImmediate(async ()=>{ try{ await evalute(func_body,{obj,user,getLib:global.getLib,getModel:global.getModel,controller,controllers:global.controllers,utils,query:utils.query,async,joinModel2,next,moment,numeral}) }catch(e){ console.error("error dynamic created",_listInfo.handle_oncreated_server,e) next(e) } }) } ],(e,rs)=>{ if(e){ console.error(e); return reject(e) } resolve(rs); }) }) }catch(e){ console.error(e); return next(e); } } next(null,obj); } const dynamicUpdating = async (listInfo,user,data,obj,next)=>{ let _listInfo = await model.findOne({_id:listInfo._id}).lean(); if(!_listInfo) return next("Không tìm thấy danh mục này"); for(let key in _listInfo){ listInfo[key] = _listInfo[key]; } const require_id_app = (_listInfo.require_id_app!=false && _listInfo.require_id_app!="false"); const supportUsers = configs.supportUsers || []; if (!require_id_app && (_listInfo.input_users||"").toLowerCase().indexOf(user.email.toLowerCase())<0 && !supportUsers.includes(user.email.toLowerCase()) && !isSupperAdmin(user.email.toLowerCase())) { //console.log("support users",supportUsers,user.email.toLowerCase()) return next('Bạn không có quyền thực hiện thao tác này'); } if(_listInfo.handle_onupdating_server && _listInfo.handle_onupdating_server.trim()!==""){ if(_listInfo.handle_onupdating_server.indexOf("next")<0) return next("Script xử lý dữ liệu trước khi update yêu cầu gọi function next"); try{ await new Promise((resolve,reject)=>{ async.series([ (next)=>{ const func_body = `(async ()=>{ try{ ${_listInfo.handle_onupdating_server} }catch(e){ next(e); } })();` setImmediate(async ()=>{ try{ await evalute(func_body,{data,obj,user,getLib:global.getLib,getModel:global.getModel,controller,controllers:global.controllers,utils,query:utils.query,async,joinModel2,next,moment,numeral}) }catch(e){ console.error("error dynamic update",_listInfo.handle_onupdating_server,e) next(e) } }) } ],(e,rs)=>{ if(e){ console.error(e); return reject(e) } resolve(rs); }) }) }catch(e){ console.error(e); return next(e); } } next(null,data,obj); } const dynamicUpdated = async (_listInfo,user,obj,next)=>{ if(_listInfo.handle_onupdated_server && _listInfo.handle_onupdated_server.trim()!==""){ if(_listInfo.handle_onupdated_server.indexOf("next")<0) return next("Script xử lý dữ liệu sau khi update yêu cầu gọi function next"); try{ await new Promise((resolve,reject)=>{ async.series([ (next)=>{ const func_body = `(async ()=>{ try{ ${_listInfo.handle_onupdated_server} }catch(e){ next(e) } })();` setImmediate(async ()=>{ try{ await evalute(func_body,{obj,user,getLib:global.getLib,getModel:global.getModel,controller,controllers:global.controllers,utils,query:utils.query,async,joinModel2,next,moment,numeral}) }catch(e){ console.error("error dynamic updated",_listInfo.handle_onupdated_server,e) next(e) } }) } ],(e,rs)=>{ if(e){ console.error(e); return reject(e) } resolve(rs); }) }) }catch(e){ console.error(e); return next(e); } } next(null,obj); } const dynamicDeleting = async (listInfo,user,obj,next)=>{ let _listInfo = await model.findOne({_id:listInfo._id}).lean(); if(!_listInfo) return next("Không tìm thấy danh mục này"); for(let key in _listInfo){ listInfo[key] = _listInfo[key]; } const require_id_app = (_listInfo.require_id_app!=false && _listInfo.require_id_app!="false"); const supportUsers = configs.supportUsers || []; if (!require_id_app && (_listInfo.input_users||"").toLowerCase().indexOf(user.email.toLowerCase())<0 && !supportUsers.includes(user.email) && !isSupperAdmin(user.email.toLowerCase())) { return next('Bạn không có quyền thực hiện thao tác này'); } if(_listInfo.handle_ondeleting_server && _listInfo.handle_ondeleting_server.trim()!==""){ if(_listInfo.handle_ondeleting_server.indexOf("next")<0) return next("Script xử lý dữ liệu trước khi update yêu cầu gọi function next"); try{ await new Promise((resolve,reject)=>{ async.series([ (next)=>{ const func_body = `(async ()=>{ try{ ${_listInfo.handle_ondeleting_server} }catch(e){ next(e); } })();` setImmediate(async ()=>{ try{ await evalute(func_body,{obj,user,getLib:global.getLib,getModel:global.getModel,controller,controllers:global.controllers,utils,query:utils.query,async,joinModel2,next,moment,numeral}) }catch(e){ console.error("error dynamic delete",_listInfo.handle_ondeleting_server,e) next(e) } }) } ],(e,rs)=>{ if(e){ console.error(e); return reject(e) } resolve(rs); }) }) }catch(e){ console.error(e); return next(e); } } next(null,obj); } const onView = async (obj,user,items,next)=>{ //private data if(obj.private_data){ items = items.filter(item=>item.user_created===user.email) } //onview if(obj.handle_view_server && obj.handle_view_server.trim()!==""){ if(obj.handle_view_server.indexOf("next")<0) return next("Script xử lý view yêu cầu gọi function next"); await new Promise(resolve=>{ async.series([ (next)=>{ setImmediate(async ()=>{ const func_body = `(async ()=>{ try{ ${obj.handle_view_server} }catch(e){ next(e); } })();` try{ await evalute(func_body,{items,user,getLib:global.getLib,utils,next,async,joinModel2,moment,numeral}) }catch(e){ console.error("error dynamic view",obj.handle_view_serve,e); next(e); } }) } ],(e,rs)=>{ if(e) console.error(e); resolve(rs); }) }) } // let ref_model,where,fields,ref_name_model; //let fields_have_ref = obj.fields.filter(f=>["Number","String"].indexOf(f.type)>0 && f.ref_model && f.ref_model.indexOf("[")<0 && f.ref_field && f.ref_label); let fields_have_ref = obj.fields.filter(f=>["Number","String"].indexOf(f.type)>0 && f.ref_model && f.ref_field && f.ref_label); if(fields_have_ref.length===0){ return next(null,items); } async.mapSeries(fields_have_ref, (f,cb)=>{ setImmediate(async ()=>{ try{ ref_name_model= f.ref_model; if(ref_name_model.indexOf("[")>=0 || ref_name_model.indexOf(",")>=0){ try{ if(ref_name_model.indexOf("[")>=0){ ref_model = utils.JSONParser(ref_name_model) }else{ ref_model = ref_name_model.split(","); } }catch(e){ console.error("ref_model is not valid",ref_name_model,e); return cb(e); } }else{ if(ref_name_model==="dmkh") ref_name_model = "customer"; if(ref_name_model==="dmtk") ref_name_model = "account"; if(ref_name_model==="dmnt") ref_name_model = "currency"; if(ref_name_model==="dmnhtask") ref_name_model = "group"; ref_model = global.mongoose.models[ref_name_model]; if(!ref_model){ let _listInofOfRef = await model.findOne({code:ref_name_model}); if(_listInofOfRef && (_listInofOfRef.api_code || _listInofOfRef.model_code)){ let model_code = _listInofOfRef.api_code || _listInofOfRef.model_code; ref_model = global.mongoose.models[model_code]; } if(!ref_model){ let ctrl = global.controllers[ref_name_model.trim().toUpperCase()]; if(ctrl) ref_model = ctrl.getProperty("model"); } } if(!ref_model) ref_model = global.getModel(ref_name_model); } }catch(e){ console.error(e); return cb(e); } let items_will_join = items.filter(item=>item[f.name] || item[f.name]==0) if(f.ref_label){ fields = {[f.ref_label_as || f.ref_label]:f.ref_label}; //items_will_join = items_will_join.filter(i=>!i[f.ref_label_as || f.ref_label]); }else{ fields ={} } if(f.ref_model==="trangthai"){ fields.color="color"; } if(!_.isArray(ref_model)){ //create where where = (item)=>{ let w = { [f.ref_field]:item[f.name] } //other condition let condition; if(f.ref_condition && f.ref_condition.indexOf("API")<0){ let func_string = f.ref_condition; if(func_string.indexOf("return ")<0){ func_string = `return ${func_string}`; } let userInfo = {...user}; try{ condition = evalute(func_string,{master:item,data:item,detail:item,userInfo}); }catch(e){ console.error(e,func_string); } } if(condition) w = {...w,...condition}; //check where of participant if(f.ref_model==="participant"){ delete w.groups; } return w; } //id app let id_app; if(underscore.has(ref_model.schema.paths,"id_app")){ id_app = user.current_id_app; } await items_will_join.asyncJoinModel2(id_app,ref_model,{where,fields}); }else{ if(f.ref_field && f.ref_label){ items_will_join.forEach(item=>{ let ref_item = ref_model.find(r=>r[f.ref_field]==item[f.name]); if(ref_item) item[f.ref_label_as || f.ref_label] = ref_item[f.ref_label] }) } } cb(); }) },(e)=>{ if(e) return next(e); next(null,items); }) } model.createController = (router,_listInfo)=>{ let api_code = _listInfo.api_code || _listInfo.code; if(!_listInfo.create_model) return null; let sort={},unique=[],_newModel,_newList; _listInfo.fields.filter(f=>f.sort).forEach(f=>{ sort[f.name] = f.sort; }) if(_listInfo.sort_by){ if(_listInfo.sort_by.indexOf("return ")>=0 || _listInfo.sort_by.indexOf("{")===0) { try{ let func_string = _listInfo.sort_by; if(func_string.indexOf("return ")<0){ func_string = "return " + func_string; } sort = evalute(func_string); }catch(e){ console.error("error create sort", _listInfo.sort_by,e); } }else{ sort = _listInfo.sort_by.split(",").reduce((sort,b)=>sort[b]=1,{}) } } _listInfo.fields.filter(f=>f.unique).forEach(f=>{ unique.push(f.name); }) _newList = global.controllers[api_code.toUpperCase()]; if(!_newList){ if(api_code=="moduleinfo") console.log("create new API",api_code); if(Object.keys(sort).length===0) sort.date_created = -1; //create new model const require_id_app = (_listInfo.require_id_app!=false && _listInfo.require_id_app!="false"); //console.log("create new api",api_code); _newModel = model.createModel(_listInfo); if(!_newModel) return; _newList = new controller(router,_newModel,api_code.toLowerCase(),{ sort: sort, unique:unique, notNeedRight:_listInfo.not_need_right, require_id_app, onView:(user,items,next)=>{ onView(_listInfo,user,items,next); }, onCreating:async (user,data,next)=>{ next(null,data); }, onUpdating:async (user,data,obj,next)=>{ next(null,data,obj); }, onDeleting:async (user,obj,next)=>{ next(null,obj); } }) _newList.is_dynamic_list = true; _newList.dynamicFinding = (user,condition,next)=>{ dynamicFinding(_listInfo,user,condition,next); } _newList.dynamicCreating = (user,obj,next)=>{ dynamicCreating(_listInfo,user,obj,next); } _newList.dynamicCreated = (user,obj,next)=>{ dynamicCreated(_listInfo,user,obj,next); } _newList.dynamicUpdating = (user,data,obj,next)=>{ dynamicUpdating(_listInfo,user,data,obj,next); } _newList.dynamicUpdated = (user,obj,next)=>{ dynamicUpdated(_listInfo,user,obj,next); } _newList.dynamicDeleting = (user,obj,next)=>{ dynamicDeleting(_listInfo,user,obj,next); } if(_listInfo.fields.find(f=>f.name==="ma_ct") && _listInfo.postinfos && _listInfo.postinfos.length>0){ //console.log("dynamic list",_listInfo.code,"has post") _newList.dynamicDeletePost = async (obj)=>{ return deletePost(_listInfo,obj) } _newList.dynamicPost = async (obj, fn)=>{ if(_newList.post){ _newList.post(obj, async function(e){ if(e) return fn(e); try{ let rs = await post(_listInfo,obj); fn(null,rs) }catch(e){ fn(e.message || e.error || e) } }) }else{ try{ let rs = await post(_listInfo,obj); fn(null,rs) }catch(e){ fn(e.message || e.error || e) } } } } _newList.route(); }else{ if(api_code=="moduleinfo") console.log("update API",api_code); //console.log("update API",api_code); //update options if(Object.keys(sort).length>0) _newList.sort = _newList.options.sort = sort; //update model _newModel = _newList.model; model.updateModel(_listInfo,_newModel); //set new unique and view if(_newList.is_dynamic_list || !_newList.unique || _newList.unique.length===0 ){ _newList.unique = _newList.options.unique=unique; } const current_dynamicView = _newList.dynamicView; _newList.dynamicView = (user,items,next)=>{ //console.log("handle dynamicView",_listInfo.code); if(current_dynamicView){ //console.log("handle current dynamicView",_listInfo.code); current_dynamicView(user,items,(e,items)=>{ if(e) return next(e); //console.log("handle dynamicView 2",_listInfo.code); onView(_listInfo,user,items,next); }) }else{ //console.log("handle new dynamicView",_listInfo.code); onView(_listInfo,user,items,next); } } const current_dynamicFinding = _newList.dynamicFinding; _newList.dynamicFinding = (user,condition,next)=>{ if(current_dynamicFinding){ current_dynamicFinding(user,condition,(e,condition)=>{ if(e) return next(e); dynamicFinding(_listInfo,user,condition,next); }) }else{ dynamicFinding(_listInfo,user,condition,next); } } const current_dynamicCreating = _newList.dynamicCreating; _newList.dynamicCreating = (user,obj,next)=>{ if(current_dynamicCreating){ current_dynamicCreating(user,obj,(e,obj)=>{ //console.log("handle current_dynamicCreating",_listInfo.code,e,obj); if(e) return next(e); dynamicCreating(_listInfo,user,obj,next); //console.log("handle dynamicCreating",_listInfo.code,obj); }) }else{ dynamicCreating(_listInfo,user,obj,next); //console.log("handle dynamicCreating",_listInfo.code,obj); } } const current_dynamicCreated = _newList.dynamicCreated; _newList.dynamicCreated = (user,obj,next)=>{ if(current_dynamicCreated){ current_dynamicCreated(user,obj,(e,obj)=>{ if(e) return next(e); dynamicCreated(_listInfo,user,obj,next); }) }else{ dynamicCreated(_listInfo,user,obj,next); } } const current_dynamicUpdating = _newList.dynamicUpdating; _newList.dynamicUpdating = (user,data,obj,next)=>{ if(current_dynamicUpdating){ current_dynamicUpdating(user,data,obj,(e,data,obj)=>{ if(e) return next(e); dynamicUpdating(_listInfo,user,data,obj,next); }) }else{ dynamicUpdating(_listInfo,user,data,obj,next); } } const current_dynamicUpdated = _newList.dynamicUpdated; _newList.dynamicUpdated = (user,obj,next)=>{ if(current_dynamicUpdated){ current_dynamicUpdated(user,obj,(e,obj)=>{ if(e) return next(e); dynamicUpdated(_listInfo,user,obj,next); }) }else{ dynamicUpdated(_listInfo,user,obj,next); } } const current_dynamicDeleting = _newList.dynamicDeleting; _newList.dynamicDeleting = (user,obj,next)=>{ if(current_dynamicDeleting){ current_dynamicDeleting(user,obj,(e,obj)=>{ if(e) return next(e); dynamicDeleting(_listInfo,user,obj,next); }) }else{ dynamicDeleting(_listInfo,user,obj,next); } } if(_listInfo.fields.find(f=>f.name==="ma_ct") && _listInfo.postinfos && _listInfo.postinfos.length>0){ //chi xu lydynamic post mot lan de tranh trung du lieu if(!_newList.dynamicDeletePost) _newList.dynamicDeletePost = async (obj)=>{ return deletePost(_listInfo,obj) } if(!_newList.dynamicPost) _newList.dynamicPost = async (obj, fn)=>{ if(_newList.post){ _newList.post(obj, async function(e){ if(e) return fn(e); try{ let rs = await post(_listInfo,obj); fn(null,rs) }catch(e){ fn(e.message || e.error || e) } }) }else{ try{ let rs = await post(_listInfo,obj); fn(null,rs) }catch(e){ fn(e.message || e.error || e) } } } }else{ _newList.dynamicPost = undefined _newList.dynamicDeletePost = undefined } } return _newList; } module.exports = model;