Rewrites
Simple rewrites are done in admin panel, and for more complicated cases you can always write a rewriter function.
Using admin panel
Navigate to Settings > Rewrites section, initially it should be empty.
Create a new rewrite rule.
Regexp is a mask itself, Fields are GET/POST fields to be replaced with matching brackets from Regexp.
You can set either Page (plus optionally Controller mode) or Controller + Controller mode.
If you set Page, it will apply rewrite and then execute page, returning data as if it was just a direct page invokation.
In below example, navigating to '/blog/by-id/1234/' is equivalent to navigating to '/blog/?id=1234&mode=view'
When Page is not set, an appropriate Controller is called and it's output is directly returned to client.
Below example shows how to add API mappings. When calling '/api/accounting/list_invoices/', system will execute controller ApiAccounting.list_invoices and return results which should (hopefully) be a correctly formatted JSON.
Making complicated rewrites using function
You must add a rewrite function in site configuration. Of course, that can be one-liner, calling a larger function from project folder itself.
Generally, you do whatever you want here.
- If, upon function call end, this.action is defined, a regular page invocation will be done
- If this.action is empty, a this.prerun_output will be returned directly (can be String or binnary Buffer). Can be helpful if you want to return some binary data, for example.
exports.site={
code:"mysite.com",
title: " - mysite",
adminpanel_title: "MYSITE",
names: ["mysite.com","www.mysite.com"],
database: { login:"", database:"", password:"" },
// CUSTOM REWRITER CALL
custom_rewriter: function(rr) { return rr.F("Rewriter","custom"); },
load: [
"functions/Rewriter.js",
],
paths: {
basepath: "/www/sites/mysite.com/",
},
previews: [ [24,24], [48,48], [128,128], [400,300], [640,480], [800,600], [220,150], [200,300], [75,75], [500,500], [990,400] ],
default_l10n_id:1,
language: "en",
show_errors: 1
};
var fs=require("fs");
// A complicated case, featuring REMOTE IP check, arguments sanitizing, language check and checking if site was payed
var ALLOWED_OFFICE_IPS={
"192.168.0.10":1,
"192.168.0.20":1,
};
exports.functions=[{
_type:"functions",
_section:"Rewriter",
sanitize_rec:function(a,obj,k) {
if (typeof a==="string") {
if (a.match(/<script[^>]*>/i)) {
if (obj && SANITIZE_CLEAR[rr.site.EXPORTNAME] && SANITIZE_CLEAR[rr.site.EXPORTNAME][k]) { obj[k]=undefined; return; }
if (obj && SANITIZE_CLEAR["*"] && SANITIZE_CLEAR["*"][k]) { obj[k]=undefined; return; }
throw new Error("Sanitizer error - <script> detected!");
}
var tmp=a.match(/(on(abort|after|animati|before|blur|canplay|change|click|context|copy|cut|dblclick|drag|drag|drop|error|focus|fullscreen|hashchange|key|load|message|mouse|offline|online|play|popstate|progress|reset|scroll|search|seek|select|show|stalled|storage|submit|suspend|timeupdate|toggle|touch|transition|unload|volumechange|waiting|wheel)\w{0,12})\s*=/i);
if (tmp) {
if (obj && SANITIZE_CLEAR[rr.site.EXPORTNAME] && SANITIZE_CLEAR[rr.site.EXPORTNAME][k]) { obj[k]=undefined; return; }
if (obj && SANITIZE_CLEAR["*"] && SANITIZE_CLEAR["*"][k]) { obj[k]=undefined; return; }
throw new Error("Sanitizer error - "+tmp[1]+" detected!");
}
if (a.match(/\s+href\s*=\s*"\s*javascript/i)) {
if (obj && SANITIZE_CLEAR[rr.site.EXPORTNAME] && SANITIZE_CLEAR[rr.site.EXPORTNAME][k]) { obj[k]=undefined; return; }
if (obj && SANITIZE_CLEAR["*"] && SANITIZE_CLEAR["*"][k]) { obj[k]=undefined; return; }
throw new Error("Sanitizer error - href=javascript detected");
}
return a.replace(/<script[^>]*>/gi,"");
}
if (typeof a!=="object") return a;
if (a.constructor===Array) {
for (var i=0;i<a.length;i++) {
a[i]=sanitize_rec(a[i],a,i);
}
} else {
for (var k in a) {
a[k]=sanitize_rec(a[k],a,k);
}
}
return a;
},
ip_allowed: function() {
return ALLOWED_OFFICE_IPS[system.env.REMOTE_ADDR];
},
set_language: function() {
// TODO check GET/POST arguments, cookies and Accept-Language to set this.use_language
this.use_language=this.fields._language||"en";
},
custom: function() {
// Generally, you do whatever you want here.
// If, upon function call end, this.action is defined, a regular page invokation will be done
// If this.action is empty, a this.prerun_output will be returned directly.
if (this.action=="/get/content_of_etc_passwd/") {
var f=new fs.File("/etc/passwd");
f.open("r");
this.prerun_output=f.read();
f.close();
this.action=undefined;
return;
}
if (!this.F("Rewriter","ip_allowed")) this.F("Rewriter","sanitize_rec",this.fields);
this.F("Rewriter","set_language");
if (this.site.not_payed && !this.F("Rewriter","ip_allowed")) return this.action="/site-disabled/";//
},
}];