The spartan miniplatform
The previous one was much too stiff, but now I have a real minimal platform ready, based on ideas I was sketching out before. It consists of three files. First, called configure.js (I've got it in the main dir of the platform), is used to set up the initial image (read: storage). The server should be run with it as the main file and (at least) one page requested.
import("storage");
storage.pwd = "undefined";
storage["worker.js.bak"] =
"""var _code = request.params.code;
if (_code) {
appjet._boot.saveWorkerSource(_code);
response.redirect(request.path);
}
print(FORM({method:'post'},
TEXTAREA({name:'code'},
html(appjet._boot.workerCode)
),
INPUT({type:'submit'})
));
""";
printp("Configured. MD5 of password is:", BR(), md5(storage.pwd));
Before running it, you may fill the password (it's used to authenticate to file saving plugin, so only you can save files, not any malicious app calling filesaving webservice). It creates the initial storage and prints the hash of a password for you.
Then, there is, slightly changed, filesaving local webservice, fs.rb (I've got it in www subdirectory, where static files are served from and ruby scripts can be called as well):
#!/usr/local/bin/ruby
require "cgi"
require "digest/md5"
cgi = CGI.new
hashpwd = Digest::MD5.hexdigest(cgi["pwd"]);
cgi.params.delete("pwd");
if ("5e543256c480ac577d30f76f9120eb74" != hashpwd) then
raise "password does not match";
end
cgi.params.each do |name, code|
File.open("../apps/"+name, "wb") do |f| f.syswrite(code) end
end
cgi.out do "" end
If you edited password in configure.js, edit also its hash in this file.
And last, but not least, is the bootstrapper, boot.js (I've got it in apps subdirectory, where all the apps and libs in the platform should reside). A bit longer than the previous one:
(appjet._boot = {
rawSaveSource: function (file, code) {
var info = { pwd: this.getString("pwd") };
info[file+".js"] = code;
wpost("http://staticdomain.net/fs.rb", info);
},
saveWorkerSource: function (code) {
if (this.workerCode) {
this.putString("worker.js.bak", this.workerCode);
}
this.rawSaveSource("lib-! worker",
"/* appjet:version 0.1 */\n" +
"/* appjet:library */\n" + code
);
this.putString("worker.js", code);
},
getString: function (key) {
return appjet._native.storage_get("obj-root", key).value;
},
putString: function (key, value) {
return appjet._native.storage_put("obj-root", key, "string", value).status == 0;
},
run: function (amd) {
appjet.mainDomain = amd;
if (request.headers.Host == "rescue."+amd) {
var rescue = this.getString("worker.js.bak");
this.saveWorkerSource(rescue);
printp("Last known good code restored.", BR(), CODE(rescue));
response.stop();
}
this.workerCode = this.getString("worker.js");
import("lib-! worker");
},
}).run("appdomain.net");
You should edit url to fs.rb (top) and the domain of your platform itself (bottom), then run the server with -c apps boot.js. First page you should request is rescue.appdomain.net. The rescue function restores last code that worked. If you enter code that breaks, simply load rescue.appdomain.net again.
The miniplatform itself resides in any other subdomain (like foo.appdomain.net). It starts with really spartan interface, the lone TEXTAREA tag with worker code to edit and the submit button. You can immediately begin to improve and evoive your platform (first change I did was adding style:'width:100%;height:50ex' to the textarea, so I can see what I edit). And it is a platform - it really saves your changes into a file and allows you to save another files beyond the worker (including boot.js itself, when you're ready) using appjet._boot.rawSaveSource call.