UNPKG

json-object-editor

Version:

JOE the Json Object Editor | Platform Edition

599 lines (526 loc) 22.1 kB
console.time('Sites on '+JOE.webconfig.sitesPort); var compress = require('compression'); var modulename = JOE.Utils.color('[sites]','module'); var express = require('express'); var server; if(JOE.webconfig.sitesPort != JOE.webconfig.port){ server = express(); }else{ server = JOE.Server; } var http = require('http'); var https = require('https'), pem = require('pem'); var fs = require('fs'); var SitesServer = { }; JOE.filesDir = filesDir = JOE.appDir+'/'+JOE.webconfig.filesDir+'/'; JOE.Utils.setupFileFolder(filesDir,'files'); // function setupFileFolder(){ // if (!fs.existsSync(filesDir)){ // fs.mkdirSync(filesDir); // } // console.log('files stored in: '+filesDir); // } // setupFileFolder(); function getInclude(req,res,next){ var payload = { WEBCONFIG:JOE.webconfig, SETTING:JOE.Cache.settings }; var id = req.params.id; var file = JOE.Data.include.where({_id:id})[0]||{content:'file not found'}; var content = file.content; if(file.fillTemplate){ content = fillTemplate(content,payload); } switch(file.filetype) { case 'js': res.set('Content-Type', 'application/javascript'); break; case 'css': res.set('Content-Type', 'text/css'); break; } res.send(content); } server.use(compress()); server.use('/'+JOE.webconfig.filesDir+'/',express.static(JOE.filesDir)); server.use(JOE.webconfig.joepath,express.static(JOE.joedir)); server.get(['/_include/:id'],getInclude); function findDynamicPage(originalURL,site,req){//returns a page or false var sitepages = JOE.Data.page.where({site:site._id,dynamic:true}); console.log(modulename+' '+site.name,sitepages.length); var dynamic_pieces = originalURL.replace(site.url,'').split('/').condense(); var piecelength = dynamic_pieces.length; var testpath,testpagepieces,mixins,testpage,tpp,match; console.log(modulename+' dynamics',dynamic_pieces); for(var p = 0; p < sitepages.length; p++){ match = true; testpage = sitepages[p]; testpagepieces = testpage['path'].split('/').condense(); if(piecelength == testpagepieces.length){ logit(testpagepieces); testpath = []; mixins = {}; for(var i = 0;i < piecelength; i++){ var tpp = testpagepieces[i]; if(dynamic_pieces[i] != tpp){ if(tpp.indexOf(':') == -1){ match = false; break; } mixins[tpp.replace(':','')] = dynamic_pieces[i]; } } /* testpage['path'].split('/').condense().map(function(piece,i){ if(piece.indexOf(':') == -1){ testpath.push(piece); }else{ mixins[piece.replace(':','')] = dynamic_pieces[i]; } }) //console.log(testpath); testpath = testpath.join('/'); if(originalURL.indexOf(testpath) != -1){ return ({page:testpage,mixins:mixins}) }*/ if(match){ logit('dynamic page found: '+testpage.name); return ({page:testpage,mixins:mixins}); } } } //go through all dynamic pages, findt he one that matches the pattern minus any : /* for(var neg = piecelength-1;neg > 0; neg--){ testpath = dynamic_pieces.slice(0,neg).join('/'); console.log('testpath',testpath); foundpage = sitepages.where({path:testpath}); if(foundpage.length){ if(foundpage.length == 0){ return {page:foundpage[0],mixins:dynamic_pieces.slice(neg)} } } //get mixins } */ //go backwards through pieces until we've found the page' return false; } var Layouts = { } var Editor = { wrap:function(content,tag,data){ if(Editor[tag]){ tag = Editor[tag]; } var data = data ||{}; var h = '<'+tag+' '; for(var d in data){ h+= 'data-'+d+'="'+data[d]+'"'; } h+='>'+content+'</'+tag+'>'; //logit(tag, h); return h; }, block_tag:'jpe-block', layout_tag:'jpe-layout', section_tag:'jpe-section', gui:function(obj,specs){ var specs = $c.merge({},specs||{}); var bt =''; if(obj.use_template){ bt = JOE.Cache.findByID('block',obj.use_template); } let wrapper = '<jpe-info data-id="'+obj._id+'" ' +(specs.src && ` data-src="${specs.src}"`||'') +(specs.section && ` data-section="${specs.section}"`||'') +(bt && " data-block_template='"+bt._id+"' data-block_template_name='"+bt.name+"' ") +' data-schema="'+obj.itemtype+'" data-name="'+obj.name+'"></jpe-info>'; return wrapper; }, styles:'<link rel="stylesheet" href="/JsonObjectEditor/css/page-editor-styles.css">', scripts:'<script src="/JsonObjectEditor/js/page-editor-scripts.js"></script>', menu:'<joe-editor-menu onclick="window.jpe.toggleEditorMode();"></joe-editor-menu>' } var Sections = { get:function(template_str,editor){ var sects=[]; var sect_str; ((template_str||'').match(/\$\{this.SECTION.[a-zA-Z0-9\_\-]*}/g)||[]).map(function(mt){ sect_str = mt.replace('${this.SECTION.','').replace('}',''); sects.push(sect_str); }); return sects; } } var Blocks = { render:function(block,specs,includes,dataOverloads={}){ var specs = $c.merge({},specs||{}); var editor = specs.editor || false; var block_template; var inc = includes || []; //logit('editor-mode: '+specs.editor); //var wrapper = !!specs.wrapper || tr if($c.isCuid(block)){ block = JOE.Cache.findByID('block',block); block_template = block.template; dataOverloads.block = block; } if(block.template_type == "module"){ block_template = JOE.Utils.requireFromString(block_template,block._id)(dataOverloads); } if(block.use_template){ var templateblock = JOE.Cache.findByID('block',block.use_template); block_template = templateblock.template; if(templateblock.template_type == "module"){ // dataOverloads.block_template = block_template; block_template = JOE.Utils.requireFromString(block_template,templateblock._id)(dataOverloads); } inc = inc.concat(templateblock.includes || []); } inc = inc.concat(block.includes || []); //TODO: pass info in here to add to block on the fly block_template = block_template //replace foreach with non .replace(/\$\{foreach/g,'_!{foreach') .replace(/\$\{end\sforeach\}/g,'_!{end foreach}') .replace(/\$\{item/g,'_!{item') //replace blocks with non .replace(/\$\{this\.BLOCK\./g,'__{this.BLOCK.') .replace(/\$\{this\.BLOCK_TEMPLATE\./g,'__{this.BLOCK_TEMPLATE.') //replace all others .replace(/\$\{this\./g,'_!{this.') //.replace(/\_\!\{this\.DYNAMIC/g,'${this.DYNAMIC') //switch blocks back .replace(/\_\_\{this\.BLOCK\./g,'${this.BLOCK.') .replace(/\_\_\{this\.BLOCK\_TEMPLATE\./g,'${this.BLOCK_TEMPLATE.') //filltemplate block_template = block_template = fillTemplate(block_template, Object.assign({BLOCK:block,BLOCK_TEMPLATE:templateblock},dataOverloads) ) //switch others back .replace(/\_\!\{this\./g,'${this.') .replace(/\_\!\{/g,'${') //logit(block); var blockstr = '' +((editor && '<'+Editor.block_tag+' data-cuid="'+block._id+'">')||'') +block_template +((editor && Editor.gui(block,{src:specs.src,section:specs.section})+'</'+Editor.block_tag+'>')||''); //logit(block.name,blockstr,specs); //return blockstr; return {content:blockstr,includes:inc}; }, page:function(page,layout,sections,editor,includes,dataOverloads){ if($c.isCuid(page)){ page = JOE.Cache.findByID('page',page); } if($c.isCuid(layout)){ layout = JOE.Cache.findByID('layout',layout); } var sect_array = Sections.get(layout.template,editor); sect_array = sect_array.concat(Sections.get(page.content,editor)); sect_array.condense(true); var sections = sections || {}; sect_array.map(function(b){ sections[b] = sections[b] || ''; }); //console.log('blocks',$c.isArray(page.blocks)); //logit('sections:',sect_array,layout.template); //CHECK IF BLOCK IN PAGE + PAGE SET TO SYNC, OTHERWISE ADD TO BLOCKS function getSectionContentFromBlock(block){ var payload = Blocks.render(block.block,{editor:editor,src:block.src,section:block.section},includes,dataOverloads); sections[block.section] = sections[block.section] || ''; sections[block.section] += payload.content; return {includes:payload.includes || []} } (page.blocks || []).map(function(block){ //TODO:Remove uneccesary Setness here var p = getSectionContentFromBlock(block); includes = includes.concat(p.includes); includes = Array.from(new Set(includes)); }); if(page.syncLayoutBlocks){ (layout.blocks || []).map(function(block){ if(!page.blocks.where({block:block.block}).length){ var s = getSectionContentFromBlock(block); includes = includes.concat(s.includes); } }); } if(editor){ var sec; for(var s in sections){ sec = sections[s]; //logit(s); sec = Editor.wrap(sec,'section_tag',{section:s}); sections[s] = sec; } } return {sections:sections,includes:includes}; }, layout:function(layout,sections,editor){ if($c.isCuid(layout)){ layout = JOE.Cache.findByID('layout',layout); } var sect_array = Sections.get(layout.template); var sections = sections || {}; sect_array.map(function(b){ sections[b] = sections[b] || ''; }); //logit('sections:',sect_array,layout.template); (layout.blocks || []).map(function(block){ sections[block.section] = sections[block.section] || ''; sections[block.section] += Blocks.render(block.block,{editor:editor,src:block.src}); }) return sections; } } SitesServer.parseRoute = function(req,res,next){ const origin = req?.headers?.referer && new URL(req.headers.referer).origin; var routeStartTimer = new Date().getTime(); var mixins = {}; var DYNAMIC={}; var section_content; var editor = false; var includes = []; var payload = this.payload = { siteRoute:req.params.siteRoute, originalURL:req._parsedUrl.pathname,//req.originalUrl regURL:req.url }; if(req.query.editor){ //console.log('editor mode'); editor = true; } //logit('sites running',payload); var sites = JOE.Data.site || [],siteurl,site,route,stest; for(var s = 0, tot= sites.length; s<tot;s++){ stest = sites[s].url[0] == '/'?sites[s].url:'/'+sites[s].url; //logit('site url test: '+stest); //if the url matches the original URL and is not just a forward slash if(payload.originalURL.indexOf(stest) == 0 && stest != '/'){ siteurl = stest; site = payload.site = sites[s]; route = payload.siteRoute = payload.originalURL.replace(siteurl,''); } } var default_site; if(!payload.site){ default_site = JOE.Data.site.where({url:''})[0]||null; if(default_site){ site = payload.site = default_site; route = payload.siteRoute = payload.originalURL.replace(siteurl,''); } } logit('\n\r'+modulename+new Date()+' siteurl: '+siteurl+' site: '+(site && site.name)||'','route: '+route); if(!payload.site){ payload.site = 'no site found'; }else{ includes = includes.concat(payload.site.includes || []); //includes = payload.site.includes||[]; //get datasets var ds_source = (payload.site.datasets || []); var datasets = {}; ds_source.map(function(ds){ datasets[ds] = JOE.Data[ds]; }); var route = payload.siteRoute || ''; var routeWithSlash = route.startsWith('/') ? route : '/' + route; var routeWithoutSlash = route.startsWith('/') ? route.slice(1) : route; var page = JOE.Data.page.where({$and:[{site:site._id},{path:{$in:[routeWithSlash,routeWithoutSlash]}}]})[0] || findDynamicPage(payload.originalURL,site,req); if(!page){ logit('page not found: '+route); page = (payload.site.homepage && JOE.Data.page.where({_id:payload.site.homepage})[0]) || false; } if(page.mixins){ mixins = page.mixins; page = page.page; console.log(modulename+' mixins',mixins); } // //look for dynamic content // if(payload.originalURL.indexOf(':') != -1){ // dynamic = true; // var dynamic_pieces = payload.originalURL.split('/'); // console.log(dynamic_pieces); // } if(!page){ res.send(payload); return; } page = $c.merge({},page); includes = includes.concat(page.includes || []); payload.page = page; /*DYNAMIC PAGE HANDLER*/ var hash=""; if(page.dynamic && page.content_items && page.content_items.length){ //find mixins logit('content items: '+page.content_items); page.content_items.map(function(ci){ if(mixins[ci.reference]){ DYNAMIC[ci.reference] = $J.get(mixins[ci.reference]); } if(DYNAMIC[ci.reference] && DYNAMIC[ci.reference].itemtype != ci.itemtype){ delete DYNAMIC[ci.reference]; console.log(modulename+' dynamic itemtype mismatch',ci.itemtype,DYNAMIC[ci.reference].itemtype); } }) //use ":" for particular parameter //or throw 404 } var layout = JOE.Cache.findByID(page.layout)||false;//JOE.Data.layout.where({_id:page.layout})[0]||false; //layout sections //section_content = Blocks.layout(layout,section_content,editor); var block_data = Blocks.page(page,layout,section_content,editor,includes, {DYNAMIC}); section_content = block_data.sections; includes = includes.concat(block_data.includes); //logit('section_content',section_content); /*PLUGIN HANDLER*/ var pluginstr = ''; if(page.content_type && page.content_type =='plugin' && page.plugin){ var plugin = page.plugin; var data = {}; page.plugin_params.map(function(param){ data[param.param] = param.value; }) data = $c.merge(data,req.query); var method = page.plugin_method || 'default'; console.log(modulename+' running '+JOE.Utils.color('['+plugin+']','plugin')+' > '+method); var errors = []; var pluginClass = JOE.Apps.plugins[plugin]; if(!pluginClass){ errors.push('plugin "'+plugin+'" not initialized') } else if(!pluginClass[method]){ errors.push('method "'+method+'" not found') } if(errors.length){ if(!layout){ res.jsonp({errors:errors,failedat:'Server'}); return; }else{ pluginstr = {errors:errors,failedat:'Server'}; } } try{ var response = pluginClass[method](data,req); if(response && response.errors){ res.jsonp(response.errors); return; } if(!layout){ if(typeof response == "string"){ res.send(response); }else{ res.jsonp(response); } }else{ pluginstr = response; } }catch(e){ res.jsonp({errors:e}); } if(!layout){ return; } } /*MODULE HANDLER*/ if(layout){ logit(`has layout ${new Date().getTime() - routeStartTimer}`); //layout includes includes = includes.concat(layout.includes || []); logit('includes: '+includes.length); includes = Array.from(new Set(includes)); var INC = '',incobj; includes.map(function(i){ incobj = JOE.Data.include.where({_id:i})[0]||{filetype:'notfound'}; var url='/_include/'+incobj._id; switch(incobj.filetype){ case 'css': INC += '<link rel="Stylesheet" type="text/css" href="'+url+'"/>'; break; case 'js': INC += '<script src="'+url+'" type="application/javascript"></script>'; break; } }); var content = { DYNAMIC:DYNAMIC, INCLUDES:INC, PAGE:page, LAYOUT:layout, SITE:site, JOEPATH:JOE.webconfig.joepath, DATA:datasets, PLUGIN:{str:pluginstr}, WEBCONFIG:JOE.webconfig, SECTION:section_content||{}, REQUEST:req, ORIGIN:origin }; if (pluginstr){ content.PAGE.content = pluginstr; } if(page.content_type && page.content_type =='module'){ console.log(`modules > ${new Date().getTime() - routeStartTimer}`); //run function with data on module page.content = JOE.Utils.requireFromString(page.content,page._id)(content); } if(editor){ //TODO: do this for other page vars to? page.content = Editor.wrap(page.content,'jpe-content',{name:'page content'}) } var template = layout.template.replace('${this.PAGE.content}',content.PAGE.content); var html = fillTemplate(template,content); if(editor){ html = html.replace('</head>',Editor.styles+'</head>') .replace('</body>',Editor.gui(layout)+Editor.gui(page)+Editor.menu +'<script>var jsc_hostname = "'+JOE.webconfig.hostname+'"; ' +'var jsc_port = '+JOE.webconfig.port+';' +'var jsc_svgs={};' +'jsc_svgs.page = \''+JOE.Schemas.schema.page.menuicon+'\';' +'jsc_svgs.block = \''+JOE.Schemas.schema.block.menuicon+'\';' +'jsc_svgs.layout = \''+JOE.Schemas.schema.layout.menuicon+'\';' +'</script>' +Editor.scripts+'</body>'); } res.send(html); logit(`sent html ${new Date().getTime() - routeStartTimer}`); return; } } res.send(payload); var routeEndTimer = new Date().getTime() - routeStartTimer; logit(`route served in:${routeEndTimer}ms`); } server.get(['/*','/*/:siteRoute'],SitesServer.parseRoute); if(JOE.webconfig.sitesPort != JOE.webconfig.port){ http.Server(server); server.listen(JOE.webconfig.sitesPort,function(){ //console.log('sites listening on '+JOE.webconfig.sitesPort); console.log(modulename+' on '+JOE.webconfig.sitesPort); }); var httpsPort = JOE.webconfig.httpsSitesPort; if(httpsPort){ https.Server({key: JOE.Pem.serviceKey, cert: JOE.Pem.certificate}, server).listen(httpsPort); console.log(modulename+' https on '+httpsPort); // pem.createCertificate({selfSigned:true}, function(err, keys){ // if(err){ // console.log(modulename+' cert error',err); // } // https.Server({key: keys.serviceKey, cert: keys.certificate}, server).listen(httpsPort); // console.log(modulename+' https on '+httpsPort); // }); } } //else{ //} SitesServer.findDynamicPage = findDynamicPage; SitesServer.server = server; module.exports = SitesServer;