diff --git a/.eslintrc.js b/.eslintrc.js index 57f5f4738..fa67d3031 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -6,7 +6,7 @@ module.exports = { }, "extends": "eslint:recommended", "rules": { - "indent": "off", + "indent": ["error", 4], "no-console": "off", "quotes": ["error", "double"], "no-trailing-spaces": "error", diff --git a/.travis.yml b/.travis.yml index 2c71bc4cc..b03397fab 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,16 +1,5 @@ language: node_js node_js: + - "7" - "6" - "4" - -#Build settings for node-fibers -env: - - CXX=g++-4.8 - -#Build settings for node-fibers -addons: - apt: - sources: - - ubuntu-toolchain-r-test - packages: - - g++-4.8 diff --git a/package.json b/package.json index 30371a195..fed58b0e0 100644 --- a/package.json +++ b/package.json @@ -42,13 +42,15 @@ }, "dependencies": { "body-parser": "^1.15.2", + "contra": "^1.9.4", "express": "^4.14.0", "home-dir": "^1.0.0", - "krl-compiler": "^0.20.0", + "krl-compiler": "^0.26.3", "leveldown": "^1.4.6", "lodash": "^4.11.2", "minimist": "^1.2.0", "mkdirp": "^0.5.1", - "pico-engine-core": "^0.15.6" + "pico-engine-core": "^0.20.1", + "request": "^2.79.0" } } diff --git a/public/index.html b/public/index.html index dc16e66c0..fda26db8f 100644 --- a/public/index.html +++ b/public/index.html @@ -4,55 +4,17 @@ Pico Bootstrap - -

Pico Bootstrap

-
-

Operations log

-

-
-
-

Review operations log

-

-Check the operations log, above. -Any problems will appear as -"*Problem..." -Please report any problems. -

-
-
-

Done

-

-The bootstrap is now complete. -

Managing Picos

Visit the My Picos @@ -84,6 +46,5 @@

Reset database

each time you clear the database.

-
diff --git a/public/js/index.js b/public/js/index.js deleted file mode 100644 index 98b4f9c87..000000000 --- a/public/js/index.js +++ /dev/null @@ -1,115 +0,0 @@ -$(document).ready(function() { - var $pre = $('#operations pre'); - var log = function(m) { - $pre.append(m).append("\r\n"); - } - var logProblem = function(m) { - log("*Problem "+m); - $("#review").addClass("problem"); - } - var get = // adapted from lodash.get, with thanks - function(o,p,v) { - var i=0, l=p.length; - while(o && i 0) { - log(rid+".krl length: "+k.length); - log("Registering "+rid); - $.getJSON("/api/ruleset/register-and-enable",{"src":k},function(rr){ - if (rr && rr.ok) { - log(rid+" registered and enabled"); - log("Installing "+rid); - $.getJSON("/api/ruleset/install/"+rid,function(ri){ - if (ri && ri.ok) { - log(rid+" installed"); - log("Adding "+rid+" to pico "+id); - $.getJSON("/api/pico/"+id+"/add-ruleset?rid="+rid,function(ra){ - if (ra && ra.ok) { - log(rid+" added to pico "+id); - callback(); - } else { - logProblem("adding "+rid); - } - }); - } else { - logProblem("installing "+rid); - } - }).fail(function() { - logProblem("installing "+rid+": failed to compile"); - }); - } else { - logProblem("registering "+rid); - } - }); - } else { - logProblem("getting "+rid); - } - },"text"); - }; - log("Loading database"); - $.getJSON("/api/db-dump", function(db_dump){ - log("Database loaded"); - if (db_dump.pico) { - log("Database has an owner pico"); - if (db_dump.rulesets) { - log("Database has rulesets"); - $("#done").addClass("okay"); - } else { - logProblem("finding rulesets"); - } - } else { - log("Creating owner pico"); - createOwnerPico(function(id,eci){ - log("Registering rulesets"); - installAndAddRuleset("io.picolabs.pico",id,function(){ - log("Sending event pico/root_created"); - $.getJSON("/sky/event/"+eci+"/19/pico/root_created", - {"id":id,"eci":eci},function(d){ - if (d && d.directives) { - log("Event pico/root_created processed"); - } else { - logProblem("with event pico/root_created"); - } - }); - installAndAddRuleset("io.picolabs.visual_params",id,function(){ - log("Sending event visual/update"); - $.getJSON("/sky/event/"+eci+"/31/visual/update", - {"dname":"Owner Pico","color":"#87cefa"},function(d){ - if (d && d.directives) { - log("Event visual/update processed"); - $("#done").addClass("okay"); - } else { - logProblem("with event visual/update"); - } - }); - }); - }); - }); - } - }); -}); diff --git a/public/js/mypicos.js b/public/js/mypicos.js index 521649ddc..40e8b19cf 100644 --- a/public/js/mypicos.js +++ b/public/js/mypicos.js @@ -224,53 +224,25 @@ $.getJSON("/api/db-dump", function(db_dump){ } log("Loading ruleset source code"); log("URL: "+url); - $.get(url,function(k){ - if (k && k.length > 0) { - log("Length: "+k.length); - $.getJSON("/api/ruleset/compile",{"src":k},function(rc){ - if (rc.code) { - var rid = rc.code.split(/"/)[3]; - log("Registering: "+rid); - $.getJSON("/api/ruleset/register-and-enable",{"src":k},function(rr){ - if (rr && rr.ok) { - log(rid+" registered and enabled"); - log("Installing: "+rid); - $.getJSON("/api/ruleset/install/"+rid,function(ri){ - if (ri && ri.ok) { - log(rid+" installed"); - log("Adding "+rid+" to pico: "+eci); - $.getJSON( - "/sky/event/"+eci+"/add-ruleset/pico/new_ruleset" - +"?rid="+rid, - function(ra){ - if (ra && ra.directives) { - log(rid+" added to pico"); - callback(); - } else { - logProblem("adding "+rid); - } - }); - } else { - logProblem("installing "+rid); - } - }).fail(function() { - logProblem("installing "+rid+": failed to compile"); - }); - } else { - logProblem("registering "+rid); - } - }); - } else { - logProblem("failed to validate"); - if (rc.error) { - log(rc.error); + $.getJSON("/api/ruleset/register",{"url": url},function(rr){ + if (rr && rr.ok) { + log(rr.rid+" registered"); + log("Adding "+rr.rid+" to pico: "+eci); + $.getJSON( + "/sky/event/"+eci+"/add-ruleset/pico/new_ruleset" + +"?rid="+rr.rid, + function(ra){ + if (ra && ra.directives) { + log(rr.rid+" added to pico"); + callback(); + } else { + logProblem("adding "+rr.rid); } - } }); } else { - logProblem("getting "+rid); + logProblem("registering"); } - },"text"); + }); }; e.preventDefault(); var args = formToJSON(this); diff --git a/public/js/ruleset.js b/public/js/ruleset.js index 6451ba209..f9b92b094 100644 --- a/public/js/ruleset.js +++ b/public/js/ruleset.js @@ -140,17 +140,9 @@ $.getJSON("/api/db-dump", function(db_dump){ return; } var rid = rc.code.split(/"/)[3]; - $.getJSON("/api/ruleset/register-and-enable",{"src":src},function(rr){ + $.getJSON("/api/ruleset/register",{"src":src},function(rr){ if (rr && rr.ok) { - $.getJSON("/api/ruleset/install/"+rid,function(ri){ - if (ri && ri.ok) { - location.reload(); - } else { - $feedback.html("Problem installing "+rid); - } - }).fail(function() { - $feedback.html("Problem installing "+rid+": failed to compile"); - }); + location.reload(); } else { $feedback.html("Problem registering "+rid); } diff --git a/public/js/student.js b/public/js/student.js index ae6a116de..ea437eb50 100644 --- a/public/js/student.js +++ b/public/js/student.js @@ -39,10 +39,9 @@ $(document).ready(function() { callback(own_eci); } else { log("Finding owner pico"); - $.getJSON("/api/owner-channel", function(owner){ - if (owner.channel) { - for (var k in owner.channel) { own_eci = k; break; } - log("Owner pico id is "+get(owner.channel,[own_eci,"pico_id"])); + $.getJSON("/api/owner-eci", function(owner){ + if (owner.ok && owner.eci) { + own_eci = owner.eci; $("#own_eci").val(own_eci); callback(own_eci); } else { diff --git a/public/old.html b/public/old.html deleted file mode 100644 index 880b27365..000000000 --- a/public/old.html +++ /dev/null @@ -1,121 +0,0 @@ - - - - Pico Engine - - - - - -
- - - - - - diff --git a/src/RulesetLoader.js b/src/RulesetLoader.js index d8a591e54..a080dd228 100644 --- a/src/RulesetLoader.js +++ b/src/RulesetLoader.js @@ -3,65 +3,65 @@ var path = require("path"); var mkdirp = require("mkdirp"); var compiler = require("krl-compiler"); var version_key = [ - require("pico-engine-core/package.json").version, - require("krl-compiler/package.json").version + require("pico-engine-core/package.json").version, + require("krl-compiler/package.json").version ].join("-"); var fsExist = function(file_path, callback){ - fs.stat(file_path, function(err, stats){ - if(err){ - if(err.code === "ENOENT"){ - return callback(undefined, false); - }else{ - return callback(err); - } - } - callback(undefined, true); - }); + fs.stat(file_path, function(err, stats){ + if(err){ + if(err.code === "ENOENT"){ + return callback(undefined, false); + }else{ + return callback(err); + } + } + callback(undefined, true); + }); }; var storeFile = function(file_path, src, callback){ - mkdirp(path.dirname(file_path), function(err){ - if(err) return callback(err); - fs.writeFile(file_path, src, { - encoding: "utf8" - }, callback); - }); + mkdirp(path.dirname(file_path), function(err){ + if(err) return callback(err); + fs.writeFile(file_path, src, { + encoding: "utf8" + }, callback); + }); }; module.exports = function(conf){ - var rulesets_dir = conf.rulesets_dir; + var rulesets_dir = conf.rulesets_dir; - return function(rs_info, callback){ - var hash = rs_info.hash; - var krl_src = rs_info.src; + return function(rs_info, callback){ + var hash = rs_info.hash; + var krl_src = rs_info.src; - var file = path.resolve( - rulesets_dir, - version_key, - hash.substr(0, 2), - hash.substr(2, 2), - hash + ".js" - ); - fsExist(file, function(err, does_exist){ - if(err) return callback(err); - if(does_exist){ - callback(undefined, require(file)); - return; - } - var js_src; - try{ - js_src = compiler(krl_src, { - inline_source_map: true - }).code; - }catch(err){ - return callback(err); - } - storeFile(file, js_src, function(err){ - if(err) return callback(err); - callback(undefined, require(file)); - }); - }); - }; + var file = path.resolve( + rulesets_dir, + version_key, + hash.substr(0, 2), + hash.substr(2, 2), + hash + ".js" + ); + fsExist(file, function(err, does_exist){ + if(err) return callback(err); + if(does_exist){ + callback(undefined, require(file)); + return; + } + var js_src; + try{ + js_src = compiler(krl_src, { + inline_source_map: true + }).code; + }catch(err){ + return callback(err); + } + storeFile(file, js_src, function(err){ + if(err) return callback(err); + callback(undefined, require(file)); + }); + }); + }; }; diff --git a/src/index.js b/src/index.js index a0916679c..29c3d2e06 100644 --- a/src/index.js +++ b/src/index.js @@ -1,6 +1,9 @@ var _ = require("lodash"); +var λ = require("contra"); +var fs = require("fs"); var path = require("path"); var express = require("express"); +var request = require("request"); var leveldown = require("leveldown"); var bodyParser = require("body-parser"); var PicoEngine = require("pico-engine-core"); @@ -12,332 +15,310 @@ var port = process.env.PORT || 8080; var pico_engine_home = process.env.PICO_ENGINE_HOME || path.resolve(__dirname, ".."); //////////////////////////////////////////////////////////////////////////////// -PicoEngine({ - compileAndLoadRuleset: RulesetLoader({ - rulesets_dir: path.resolve(pico_engine_home, "rulesets") - }), - db: { - db: leveldown, - location: path.join(pico_engine_home, "db") - } -}, function(err, pe){ - if(err){ - throw err; - } +var httpGetKRL = function(url, callback){ + request(url, function(err, resp, body){ + if(err) + return callback(err); + if(resp.statusCode !== 200) + return callback(new Error("Got a statusCode=" + resp.statusCode + " for: " + url)); - var logs = {}; - var logRID = "io.picolabs.logging"; - var logEntry = function(context,message){ - var eci = context.eci; - var timestamp = (new Date()).toISOString(); - var episode = logs[eci]; - if (episode) { - episode.logs.push(timestamp+" "+message); - } else { - console.log("[ERROR]","no episode found for",eci); - } - }; - var logEpisode = function(pico_id,context,callback){ - var eci = context.eci; - var episode = logs[eci]; - if (!episode) { - console.log("[ERROR]","no episode found for",eci); - return; - } - pe.db.getEntVar(pico_id,logRID,"status",function(e,status){ - if (status) { - pe.db.getEntVar(pico_id,logRID,"logs",function(e,data){ - data[episode.key] = episode.logs; - pe.db.putEntVar(pico_id,logRID,"logs",data,function(e){ - callback(delete logs[eci]); - }); - }); - } else { - callback(delete logs[eci]); - } - }); - }; - pe.emitter.on("episode_start", function(context){ - console.log("EPISODE_START",context); - var eci = context.eci; - var timestamp = (new Date()).toISOString(); - var episode = logs[eci]; - if (episode) { - console.log("[ERROR]","episode already exists for",eci); - } else { - episode = {}; - episode.key = ( - timestamp + " - " + eci - + " - " + ((context.event) ? context.event.eid : "query") - ).replace(/[.]/g, "-"); - episode.logs = []; - logs[eci] = episode; - } - }); - pe.emitter.on("klog", function(context, val, message){ - console.log("[KLOG]", message, val); - logEntry(context,"[KLOG] "+message+" "+JSON.stringify(val)); - }); - pe.emitter.on("debug", function(context, message){ - console.log("[DEBUG]", context, message); - logEntry(context,message); - }); - pe.emitter.on("episode_stop", function(context){ - console.log("EPISODE_STOP",context); - var callback = function(outcome){ - console.log("[EPISODE_REMOVED]",outcome); - }; - logEpisode(context.pico_id,context,callback); - }); - - var app = express(); - app.use(function(req, res, next) { - res.header("Access-Control-Allow-Origin", "*"); - res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept"); - next(); - }); - app.use(express.static(path.resolve(__dirname, "..", "public"))); - app.use(bodyParser.json({type: "application/json"})); - app.use(bodyParser.urlencoded({type: "application/x-www-form-urlencoded", extended: false})); - - var errResp = function(res, err){ - res.statusCode = err.statusCode || 500; - res.end(err.message); - }; - - - app.all("/sky/event/:eci/:eid/:domain/:type", function(req, res){ - var event = { - eci: req.params.eci, - eid: req.params.eid, - domain: req.params.domain, - type: req.params.type, - attrs: _.assign({}, req.query, req.body) - }; - pe.signalEvent(event, function(err, response){ - if(err) return errResp(res, err); - res.json(response); + callback(null, body); }); - }); +}; - app.all("/sky/cloud/:eci/:rid/:function", function(req, res){ - var query = { - eci: req.params.eci, - rid: req.params.rid, - name: req.params["function"], - args: _.assign({}, req.query, req.body) - }; - pe.runQuery(query, function(err, data){ - if(err) return errResp(res, err); - if(_.isFunction(data)){ - data(res); - }else{ - res.json(data); - } - }); - }); - - app.all("/api/db-dump", function(req, res){ - pe.db.toObj(function(err, db_data){ - if(err) return errResp(res, err); - res.json(db_data); +var registerBuiltInRulesets = function(pe, callback){ + var krl_dir = path.resolve(__dirname, "../krl"); + fs.readdir(krl_dir, function(err, files){ + if(err) return callback(err); + λ.each(files, function(filename, next){ + var file = path.resolve(krl_dir, filename); + fs.readFile(file, "utf8", function(err, src){ + if(err) return next(err); + pe.registerRuleset(src, {}, function(err){ + if(err) return next(err); + next(); + }); + }); + }, callback); }); - }); +}; - app.all("/old", function(req, res){ - pe.db.toObj(function(err, db_data){ - if(err) return errResp(res, err); - - var html = ""; - html += "\n\n"; - html += "

http://localhost:" + port + "

\n"; - html += "

Picos

\n"; - _.each(db_data.pico, function(pico){ - html += "
\n"; - html += "

"+pico.id+"

\n"; - html += "
\n"; - - html += "

Channels

\n"; - html += "
    \n"; - _.each(pico.channel, function(chan){ - var rm_link = "/api/pico/"+pico.id+"/rm-channel/"+chan.id; - html += "
  • "+JSON.stringify(chan)+" del
  • \n"; +var setupOwnerPico = function(pe, callback){ + pe.db.getOwnerECI(function(err, eci){ + if(err) return callback(err); + if(eci){//already setup + return callback(); + } + λ.waterfall([ + λ.curry(pe.db.newPico, {}), + function(pico, callback){ + pe.db.newChannel({ + pico_id: pico.id, + name: "main", + type: "secret" + }, function(err, channel){ + if(err) return callback(err); + callback(null, { + pico_id: pico.id, + eci: channel.id + }); + }); + }, + function(info, callback){ + pe.db.addRuleset({ + pico_id: info.pico_id, + rid: "io.picolabs.pico" + }, function(err){ + callback(err, info); + }); + }, + function(info, callback){ + pe.db.addRuleset({ + pico_id: info.pico_id, + rid: "io.picolabs.visual_params" + }, function(err){ + callback(err, info); + }); + }, + function(info, callback){ + pe.signalEvent({ + eci: info.eci, + eid: "19", + domain: "pico", + type: "root_created", + attrs: { + id: info.pico_id, + eci: info.eci + } + }, function(err){ + callback(err, info); + }); + }, + function(info, callback){ + pe.signalEvent({ + eci: info.eci, + eid: "31", + domain: "visual", + type: "update", + attrs: { + dname: "Owner Pico", + color: "#87cefa" + } + }, function(err){ + callback(err, info); + }); + } + ], function(err){ + callback(err, pe); }); - html += "
\n"; - - html += "
\n"; - html += "\n"; - html += "\n"; - html += "\n"; - html += "
\n"; + }); +}; - html += "

Rulesets

\n"; - html += "
    \n"; - _.each(pico.ruleset, function(d, rid){ - var rm_link = "/api/pico/"+pico.id+"/rm-ruleset/"+rid; - html += "
  • "+rid+" del
  • \n"; - html += "
      \n"; - _.each(_.get(db_data, ["pico", pico.id, rid, "vars"]), - function(v,k){ - html += "
    • "+k+"="+JSON.stringify(v)+"
    • \n"; - }); - html += "
    \n"; +var startPicoEngine = function(callback){ + PicoEngine({ + compileAndLoadRuleset: RulesetLoader({ + rulesets_dir: path.resolve(pico_engine_home, "rulesets") + }), + db: { + db: leveldown, + location: path.join(pico_engine_home, "db") + } + }, function(err, pe){ + if(err) return callback(err); + registerBuiltInRulesets(pe, function(err){ + if(err) return callback(err); + setupOwnerPico(pe, function(err){ + if(err) return callback(err); + callback(null, pe); + }); }); - html += "
\n"; + }); +}; - html += "
\n"; - html += "\n"; - html += "\n"; - html += "
\n"; +startPicoEngine(function(err, pe){ + if(err){ + throw err; + } - html += "
\n"; - html += "
\n"; - }); - html += "
\n"; - html += "add pico\n"; - html += "
\n"; - html += "

Rulesets

\n"; - _.each(_.get(db_data, ["rulesets", "versions"]), function(versions, rid){ - var enabled_hash = _.get(db_data, ["rulesets", "enabled", rid, "hash"]); - html += "
\n"; - html += "

"+rid+"

\n"; - _.each(versions, function(hashes, timestamp){ - _.each(hashes, function(is_there, hash){ - if(!is_there){ - return; - } - html += "
\n"; - html += timestamp + " | " + hash + " | "; - if(hash === enabled_hash){ - html += "disable"; - html += " | "; - if(pe.isInstalled(rid)){ - html += "uninstall"; - }else{ - html += "install"; - } - var the_krl_src = _.get(db_data, ["rulesets", "krl", enabled_hash, "src"]); - html += "
" + the_krl_src + "
\n"; - }else{ - html += "enable"; + var logs = {}; + var logRID = "io.picolabs.logging"; + var logEntry = function(context,message){ + var eci = context.eci; + var timestamp = (new Date()).toISOString(); + var episode = logs[eci]; + if (episode) { + episode.logs.push(timestamp+" "+message); + } else { + console.log("[ERROR]","no episode found for",eci); + } + }; + var logEpisode = function(pico_id,context,callback){ + var eci = context.eci; + var episode = logs[eci]; + if (!episode) { + console.log("[ERROR]","no episode found for",eci); + return; + } + pe.db.getEntVar(pico_id,logRID,"status",function(e,status){ + if (status) { + pe.db.getEntVar(pico_id,logRID,"logs",function(e,data){ + data[episode.key] = episode.logs; + pe.db.putEntVar(pico_id,logRID,"logs",data,function(e){ + callback(delete logs[eci]); + }); + }); + } else { + callback(delete logs[eci]); } - html += "
\n"; - }); }); - html += "
\n"; - }); - html += "
\n"; - html += "\n"; - html += "\n"; - html += "
\n"; - html += "
\n"; - html += "raw database dump\n"; - // html += "
\n" + JSON.stringify(db_data, undefined, 2) + "\n
\n"; - html += "\n\n"; - res.end(html); + }; + pe.emitter.on("episode_start", function(context){ + console.log("EPISODE_START",context); + var eci = context.eci; + var timestamp = (new Date()).toISOString(); + var episode = logs[eci]; + if (episode) { + console.log("[ERROR]","episode already exists for",eci); + } else { + episode = {}; + episode.key = ( + timestamp + " - " + eci + + " - " + ((context.event) ? context.event.eid : "query") + ).replace(/[.]/g, "-"); + episode.logs = []; + logs[eci] = episode; + } }); - }); - - app.all("/api/owner-channel", function(req, res){ - pe.db.getFirstChannel(function(err, first_channel){ - if(err) return errResp(res, err); - res.end(JSON.stringify(first_channel, undefined, 2)); + pe.emitter.on("klog", function(context, val, message){ + console.log("[KLOG]", message, val); + logEntry(context,"[KLOG] "+message+" "+JSON.stringify(val)); }); - }); - - app.all("/api/new-pico", function(req, res){ - pe.db.newPico({}, function(err, new_pico){ - if(err) return errResp(res, err); - res.end(JSON.stringify(new_pico, undefined, 2)); + pe.emitter.on("debug", function(context, message){ + console.log("[DEBUG]", context, message); + logEntry(context,message); }); - }); - - app.all("/api/rm-pico/:id", function(req, res){ - pe.db.removePico(req.params.id, function(err){ - if(err) return errResp(res, err); - res.json({ok: true}); + pe.emitter.on("episode_stop", function(context){ + console.log("EPISODE_STOP",context); + var callback = function(outcome){ + console.log("[EPISODE_REMOVED]",outcome); + }; + logEpisode(context.pico_id,context,callback); }); - }); - app.all("/api/pico/:id/new-channel", function(req, res){ - pe.db.newChannel({ - pico_id: req.params.id, - name: req.query.name, - type: req.query.type - }, function(err, new_channel){ - if(err) return errResp(res, err); - res.json(new_channel); + var app = express(); + app.use(function(req, res, next) { + res.header("Access-Control-Allow-Origin", "*"); + res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept"); + next(); }); - }); + app.use(express.static(path.resolve(__dirname, "..", "public"))); + app.use(bodyParser.json({type: "application/json"})); + app.use(bodyParser.urlencoded({type: "application/x-www-form-urlencoded", extended: false})); - app.all("/api/pico/:id/rm-channel/:eci", function(req, res){ - pe.db.removeChannel(req.params.id, req.params.eci, function(err){ - if(err) return errResp(res, err); - res.json({ok: true}); + var errResp = function(res, err){ + res.statusCode = err.statusCode || 500; + res.end(err.message); + }; + + + app.all("/sky/event/:eci/:eid/:domain/:type", function(req, res){ + var event = { + eci: req.params.eci, + eid: req.params.eid, + domain: req.params.domain, + type: req.params.type, + attrs: _.assign({}, req.query, req.body) + }; + pe.signalEvent(event, function(err, response){ + if(err) return errResp(res, err); + res.json(response); + }); }); - }); - app.all("/api/pico/:id/rm-ruleset/:rid", function(req, res){ - pe.db.removeRuleset(req.params.id, req.params.rid, function(err){ - if(err) return errResp(res, err); - res.json({ok: true}); + app.all("/sky/cloud/:eci/:rid/:function", function(req, res){ + var query = { + eci: req.params.eci, + rid: req.params.rid, + name: req.params["function"], + args: _.assign({}, req.query, req.body) + }; + pe.runQuery(query, function(err, data){ + if(err) return errResp(res, err); + if(_.isFunction(data)){ + data(res); + }else{ + res.json(data); + } + }); }); - }); - app.all("/api/pico/:id/add-ruleset", function(req, res){ - pe.db.addRuleset({pico_id: req.params.id, rid: req.query.rid}, function(err){ - if(err) return errResp(res, err); - res.json({ok: true}); + app.all("/api/db-dump", function(req, res){ + pe.db.toObj(function(err, db_data){ + if(err) return errResp(res, err); + res.json(db_data); + }); }); - }); - app.all("/api/ruleset/compile", function(req, res){ - try{ - res.json({ code: compiler(req.query.src).code}); - }catch(err){ - res.json({ error: err.toString() }); - } - }); + app.all("/api/owner-eci", function(req, res){ + pe.db.getOwnerECI(function(err, eci){ + if(err) return errResp(res, err); + res.json({ok: true, eci: eci}); + }); + }); - app.all("/api/ruleset/register-and-enable", function(req, res){ - pe.db.registerRuleset(req.query.src, function(err, hash){ - if(err) return errResp(res, err); - pe.db.enableRuleset(hash, function(err){ - if(err) return errResp(res, err); - res.json({ok: true}); - }); + app.all("/api/pico/:id/new-channel", function(req, res){ + pe.db.newChannel({ + pico_id: req.params.id, + name: req.query.name, + type: req.query.type + }, function(err, new_channel){ + if(err) return errResp(res, err); + res.json(new_channel); + }); }); - }); - app.all("/api/ruleset/register", function(req, res){ - pe.db.registerRuleset(req.query.src, function(err){ - if(err) return errResp(res, err); - res.json({ok: true}); + app.all("/api/pico/:id/rm-channel/:eci", function(req, res){ + pe.db.removeChannel(req.params.id, req.params.eci, function(err){ + if(err) return errResp(res, err); + res.json({ok: true}); + }); }); - }); - app.all("/api/ruleset/enable/:hash", function(req, res){ - pe.db.enableRuleset(req.params.hash, function(err){ - if(err) return errResp(res, err); - res.json({ok: true}); + app.all("/api/pico/:id/rm-ruleset/:rid", function(req, res){ + pe.db.removeRuleset(req.params.id, req.params.rid, function(err){ + if(err) return errResp(res, err); + res.json({ok: true}); + }); }); - }); - app.all("/api/ruleset/install/:rid", function(req, res){ - pe.installRID(req.params.rid, function(err){ - if(err) return errResp(res, err); - res.json({ok: true}); + app.all("/api/ruleset/compile", function(req, res){ + try{ + res.json({ code: compiler(req.query.src).code}); + }catch(err){ + res.json({ error: err.toString() }); + } }); - }); - app.all("/api/ruleset/disable/:rid", function(req, res){ - pe.db.disableRuleset(req.params.rid, function(err){ - if(err) return errResp(res, err); - res.json({ok: true}); + app.all("/api/ruleset/register", function(req, res){ + var register = function(src, meta){ + pe.registerRuleset(src, meta || {}, function(err, data){ + if(err) return errResp(res, err); + res.json({ok: true, rid: data.rid, hash: data.hash}); + }); + }; + if(_.isString(req.query.src)){ + register(req.query.src); + }else if(_.isString(req.query.url)){ + httpGetKRL(req.query.url, function(err, src){ + if(err) return errResp(res, err); + register(src, {url: req.query.url}); + }); + }else{ + errResp(res, new Error("expected `src` or `url`")); + } }); - }); - app.listen(port, function () { - console.log("http://localhost:" + port); - }); + app.listen(port, function () { + console.log("http://localhost:" + port); + }); });