grid-apps-cmms/app.js

935 lines
26 KiB
JavaScript
Raw Normal View History

2020-06-08 12:17:56 -04:00
/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
2022-02-10 22:59:29 -05:00
// ensure each app gets a local version-specific copy, not shared
function require_fresh(path) {
const rpa = require.resolve(path);
delete require.cache[rpa];
return require(rpa);
}
const fs = require('fs');
const uglify = require('uglify-js');
const moment = require('moment');
const agent = require('express-useragent');
2021-12-27 10:14:21 -05:00
const license = require_fresh('./src/moto/license.js');
2020-05-30 20:39:56 -04:00
const version = license.VERSION || "rogue";
const netdb = require('@gridspace/net-level-client');
const PATH = require('path');
const fileCache = {};
const code_src = {};
const code = {};
const mods = {};
const load = [];
2020-06-03 22:39:30 -04:00
const synth = {};
2020-06-04 00:15:48 -04:00
const api = {};
const wrap = {};
let forceUseCache = false;
let serviceWorker = true;
let crossOrigin = false;
let setupFn;
2020-05-30 18:16:40 -04:00
let cacheDir;
let startTime;
2021-01-26 09:03:02 -05:00
let oversion;
let dversion;
let lastmod;
let logger;
let debug;
let over;
2020-05-30 18:16:40 -04:00
let http;
let util;
let dir;
2020-05-30 18:16:40 -04:00
let log;
2022-02-10 22:59:29 -05:00
const EventEmitter = require('events');
class AppEmitter extends EventEmitter {}
const events = new AppEmitter();
netdb.create = async function(map = {}) {
if (util.isfile(map.conf)) {
Object.assign(map, JSON.parse(fs.readFileSync(map.conf)));
}
const client = new netdb();
if (map.host && map.port) await client.open(map.host, map.port);
if (map.user && map.pass) await client.auth(map.user, map.pass);
if (map.base) await client.use(map.base);
logger.log({ netdb: map.host, user: map.user, base: map.base });
return client;
};
// experimental wrapping of esm modules using embedded data urls
function wrap_module(buf, as) {
const base64 = Buffer.from(buf).toString('base64');
const path = as.split('.');
const exp = path.pop();
return [
`gapp.register("${path.join('.')}", [], (root, exports) => {`,
`const url = "data:text/javascript;base64,${base64}";`,
`exports({ ["${exp}"]: import(url) });`,
"});"
].join('\n');
}
function addonce(array, v) {
if (array.indexOf(v) < 0) {
array.push(v);
}
};
function init(mod) {
2024-06-16 21:48:41 -04:00
const ENV = mod.env;
startTime = time();
lastmod = mod.util.lastmod;
logger = mod.log;
2024-06-16 21:48:41 -04:00
debug = ENV.debug || mod.meta.debug;
oversion = ENV.over || mod.meta.over;
crossOrigin = ENV.xorigin || mod.meta.xorigin || false;
serviceWorker = (ENV.service || mod.meta.service) !== false;
2020-05-30 18:16:40 -04:00
http = mod.http;
util = mod.util;
dir = mod.dir;
2020-05-30 18:16:40 -04:00
log = mod.log;
2024-06-16 21:48:41 -04:00
if (ENV.single) console.log({ cwd: process.cwd(), env: ENV });
dversion = debug ? `_${version}` : version;
2024-06-16 21:48:41 -04:00
cacheDir = ENV.cache || mod.util.datadir("cache");
if (ENV.single) logger.log({ cacheDir });
forceUseCache = ENV.cache ? true : false;
const approot = PATH.join("main","gapp");
const refcache = {};
const callstack = [];
let xxxx = false;
2021-12-29 10:37:17 -05:00
function find_refs(cache, path, ismod) {
let rec = refcache[path];
if (rec) {
let crec = cache[path];
if (!crec) {
cache[path] = rec;
for (let d of rec.deps) find_refs(cache, d);
2022-02-22 11:32:35 -05:00
for (let u of rec.uses) find_refs(cache, u);
2021-12-30 22:59:46 -05:00
}
return;
2021-12-30 22:59:46 -05:00
}
callstack.push(path);
rec = cache[path] = refcache[path] = {
uses: [],
deps: [ approot ]
};
let full = PATH.join(dir,"src",`${path}.js`);
try {
fs.lstatSync(full);
} catch (e) {
console.log({missing: full, callstack});
throw e;
}
// skip interrogating file if it's a module (external compacted)
if (ismod) {
wrap[`src/${path}.js`] = path.replaceAll('/','.');
return;
}
let lines = fs.readFileSync(full)
2021-12-29 10:37:17 -05:00
.toString()
.split('\n');
for (let line of lines) {
let arr, pos;
let upos = line.indexOf('// use:');
if (upos >= 0) {
arr = rec.uses;
pos = upos + 7;
}
2021-12-29 10:37:17 -05:00
let dpos = line.indexOf('// dep:');
if (dpos >= 0) {
arr = rec.deps;
pos = dpos + 7;
}
let mpos = line.indexOf('// mod:');
if (mpos >= 0) {
arr = rec.deps;
pos = mpos + 7;
}
if (upos >= 0 && dpos >= 0) {
console.log(`invalid line: ${line}`);
process.exit();
}
if (arr && pos >= 0) {
let path = line.substring(pos).trim().replace(/\./g,'/').trim();
addonce(arr, path);
find_refs(cache, path, mpos >= 0);
}
}
// if (xxxx) console.log({path, ...rec});
2022-02-22 11:32:35 -05:00
if (false) {
let seek = 'mesh/api';
if (rec.uses.indexOf(seek) >= 0 || rec.deps.indexOf(seek) >= 0) {
console.log({PULLS:seek, path});
}
}
callstack.pop();
}
// return record position indicated by path
function pos(path, list) {
for (let i=0; i<list.length; i++) {
if (list[i].path === path) {
return i;
}
}
console.trace(`not found: ${path}`);
process.exit();
}
function order_refs(cache) {
const recs = Object.entries(cache).map(entry => {
return { path: entry[0], deps: entry[1].deps }
}).sort((a,b) => {
return a.path === b.path ? 0 : a.path < b.path ? -1 : 1;
});
if (xxxx) console.log({ordering: recs});
// for each rec, ensure that dependencies are inserted before it
let lrec = recs.slice();
for (let rec of lrec) {
let { path, deps } = rec;
for (let dep of deps) {
let rpos = pos(path, recs);
let dpos = pos(dep, recs);
if (dpos > rpos) {
let drec = recs[dpos];
// remove old dep record
recs.splice(dpos, 1);
// insert dep before
recs.splice(rpos, 0, drec);
let nrpos = pos(path, recs);
let ndpos = pos(dep, recs);
let fail = nrpos != ndpos + 1;
// if (xxxx) { console.log({move: dep, dpos, before: path, rpos}); }
if (fail) {
console.log({move: dep, dpos, before: path, rpos, ndpos, nrpos, recs: recs.slice(0,10)});
process.exit();
}
2021-12-29 10:37:17 -05:00
}
}
}
if (xxxx) console.log({recs});
return recs.map(rec => rec.path);
2021-12-29 10:37:17 -05:00
}
// process script dependencies, expand paths
for (let [ key, val ] of Object.entries(script)) {
if (val.indexOf(approot) < 0) {
val = [ approot, ...val ];
}
const list = val.map(p => p.charAt(0) === '&' ? p.substring(1) : p);
const cache = {};
const roots = [];
// xxxx = key === "kiri_work";
// for each path in the list, find deps and add to list
for (let path of val) {
2021-12-29 10:37:17 -05:00
let fc = path.charAt(0);
if (fc === '@') {
continue;
}
if (fc === '#') {
continue;
}
2021-12-29 10:37:17 -05:00
if (fc === '&') {
path = path.substring(1);
addonce(roots, path);
2021-12-29 10:37:17 -05:00
}
find_refs(cache, path);
}
if (xxxx) console.log({ processing: key, val });
let refs = order_refs(cache).filter(p => roots.indexOf(p) < 0);
// remove paths that are in refs
let paths = list.filter(p => {
if (p.charAt(0) === '&') {
p = p.substring(1);
}
return refs.indexOf(p) < 0 && roots.indexOf(p) < 0;
});
// when dependency roots exist, re-write val array
if (roots.length) {
val = [...refs, ...paths, ...roots];
}
// val.splice(1, 0, ...roots);
if (xxxx) console.log({key, cache, refs, paths, roots, val});
script[key] = val.map(p => {
let fc = p.charAt(0);
if (fc === '@') return p;
if (fc === '#') {
fc = p.split('#');
let nupath = `src/${fc[1]}.js`;
wrap[nupath] = fc[1].replaceAll('/','.');
return nupath;
}
return `src/${p}.js`;
});
// console.log({script: key, files: script[key]});
}
2020-05-30 18:16:40 -04:00
mod.on.test((req) => {
2020-06-12 11:51:02 -04:00
let cookie = cookieValue(req.headers.cookie, "version") || undefined;
let vmatch = mod.meta.version || "*";
if (!Array.isArray(vmatch)) {
vmatch = [ vmatch ];
}
if (vmatch.indexOf("*") >= 0) {
return true;
2020-05-30 18:16:40 -04:00
}
return vmatch.indexOf(cookie) >= 0;
2020-05-30 18:16:40 -04:00
});
mod.add(handleSetup);
mod.add(handleOptions);
mod.add(handleWasm);
mod.add(fullpath({
2020-05-30 21:26:01 -04:00
"/kiri" : redir("/kiri/", 301),
2021-12-16 19:11:52 -05:00
"/mesh" : redir("/mesh/", 301),
2020-05-30 21:26:01 -04:00
"/meta" : redir("/meta/", 301),
"/kiri/index.html" : redir("/kiri/", 301),
2021-12-16 19:11:52 -05:00
"/mesh/index.html" : redir("/mesh/", 301),
2020-05-30 21:26:01 -04:00
"/meta/index.html" : redir("/meta/", 301)
}));
2020-05-30 20:39:56 -04:00
mod.add(handleVersion);
mod.add(prepath([
[ "/code/", handleCode ],
// [ "/wasm/", handleWasm ]
]));
mod.add(fixedmap("/api/", api));
if (debug) {
mod.static("/mod/", "mod");
2025-01-25 18:03:56 -05:00
mod.static("/mods/", "mods");
2020-05-30 18:16:40 -04:00
mod.sync("/reload", () => {
mod.reload();
return "reload";
});
}
2020-05-30 21:26:01 -04:00
mod.add(rewriteHtmlVersion);
mod.add((req, res, next) => {
const path = req.gs.path.substring(1);
if (wrap[path]) {
const data = getCachedFile(path, file => {
console.log({ hot_wrap: file });
return fs.readFileSync(file);
});
res.setHeader('Content-Type', 'application/javascript; charset=UTF-8');
return res.end(data);
}
next();
});
mod.static("/src/", "src");
mod.static("/obj/", "web/obj");
2021-12-16 19:11:52 -05:00
mod.static("/font/", "web/font");
2024-07-28 15:02:35 -04:00
mod.static("/fon2/", "web/fon2");
2021-12-16 19:11:52 -05:00
mod.static("/mesh/", "web/mesh");
mod.static("/moto/", "web/moto");
2020-05-30 21:26:01 -04:00
mod.static("/meta/", "web/meta");
mod.static("/kiri/", "web/kiri");
2025-01-25 18:03:56 -05:00
function load_modules(root, force) {
// load modules
lastmod(`${dir}/${root}`) && fs.readdirSync(`${dir}/${root}`).forEach(mdir => {
const modpath = `${root}/${mdir}`;
if (dir.charAt(0) === '.') return;
const stats = fs.lstatSync(`${mod.dir}/${modpath}`);
if (!(stats.isDirectory() || stats.isSymbolicLink())) return;
if (util.isfile(PATH.join(mod.dir,modpath,".disable"))) return;
const isDebugMod = util.isfile(PATH.join(mod.dir,modpath,".debug"));
const isElectronMod = util.isfile(PATH.join(mod.dir,modpath,".electron"));
if (force || (ENV.electron && !isElectronMod)) return;
if (force || (!ENV.electron && isElectronMod && !isDebugMod)) return;
try {
loadModule(mod, modpath);
} catch (error) {
console.log({ module: mdir, error });
}
});
}
// load development and 3rd party modules
load_modules('mod');
// load optional local modules
load_modules('mods');
// run loads injected by modules
while (load.length) {
try {
load.shift()();
} catch (e) {
logger.log({on_load_fail: e});
}
}
// runs after module loads / injects
prepareScripts();
};
// either add module assets to path or require(init.js)
function loadModule(mod, dir) {
if (dir.indexOf('node_modules') >= 0) {
return;
}
if (lastmod(`${mod.dir}/${dir}/.ignore`)) {
return;
}
lastmod(`${mod.dir}/${dir}/init.js`) ?
initModule(mod, `./${dir}/init.js`, dir) :
mod.static("/", `${mod.dir}/${dir}`);
}
// load module and call returned function with helper object
function initModule(mod, file, dir) {
logger.log({module: file});
require_fresh(file)({
2022-02-10 22:59:29 -05:00
// express functions added here show up at "/api/" url root
api: api,
adm: {
2021-01-26 09:03:02 -05:00
reload: prepareScripts,
setver: (ver) => { oversion = ver },
crossOrigin: (bool) => { crossOrigin = bool }
},
2022-02-10 22:59:29 -05:00
events,
const: {
args: {},
2020-06-04 00:31:59 -04:00
meta: mod.meta,
debug: debug,
script: script,
moddir: dir,
rootdir: mod.dir,
version: oversion || version
},
2020-07-06 18:36:57 -04:00
env: mod.env,
pkg: {
agent,
moment,
netdb,
},
mod: mods,
util: {
log: logger.log,
time: time,
guid: guid,
2020-05-30 18:16:40 -04:00
mkdirs: util.mkdir,
isfile: util.isfile,
confdir: util.confdir,
2020-05-30 18:16:40 -04:00
datadir: util.datadir,
lastmod: lastmod,
obj2string: obj2string,
string2obj: string2obj,
getCookieValue: cookieValue,
2020-05-30 18:16:40 -04:00
logger: log.new
},
inject: (code, file, opt = {}) => {
let codelist = script[code];
if (!codelist) {
return logger.log(`inject missing target "${code}"`);
}
const path = `${dir}/${file}`;
codelist.push(path);
2020-11-17 15:49:41 -05:00
if (opt.cachever) {
cachever[path] = opt.cachever;
}
},
path: {
any: arg => { mod.add(arg) },
pre: arg => { mod.add(prepath(arg)) },
map: arg => { mod.add(fixedmap(arg)) },
full: arg => { mod.add(fullpath(arg)) },
static: (root, pre) => {
mod.static(pre || "/", root);
},
code: (endpoint, path) => {
let fpath = PATH.join(mod.dir, path);
if (debug) {
code[endpoint] = fs.readFileSync(fpath);
} else {
code[endpoint] = minify(fpath);
}
code_src[endpoint] = {
endpoint,
path: path,
mod: lastmod(fpath)
};
},
redir: redir,
remap: remap,
setup: fn => { setupFn = fn }
},
handler: {
addCORS: addCorsHeaders,
2020-05-30 18:16:40 -04:00
redirect: http.redirect,
reply404: http.reply404,
2022-02-24 19:53:22 -05:00
decodePost: http.decodePost,
reply: quickReply
},
ws: {
register: mod.wss
},
onload: (fn) => {
load.push(fn);
},
onexit: (fn) => {
mod.on.exit(fn);
}
});
}
const script = {
kiri : [
2020-06-04 21:29:40 -04:00
"@devices",
2022-02-22 11:32:35 -05:00
"kiri/ui",
"&main/kiri",
"&kiri/lang-en"
2021-12-26 19:43:48 -05:00
],
kiri_work : [
"kiri-run/worker",
2022-02-22 11:32:35 -05:00
"&main/kiri",
2021-12-26 19:43:48 -05:00
],
kiri_pool : [
"&kiri-run/minion",
"&main/kiri",
2021-12-26 19:43:48 -05:00
],
2020-12-31 23:22:39 -05:00
engine : [
"&kiri-run/engine",
"&main/kiri",
2021-12-26 19:43:48 -05:00
],
frame : [
"kiri-run/frame"
2021-12-26 19:43:48 -05:00
],
2020-05-30 21:26:01 -04:00
meta : [
2021-12-29 10:37:17 -05:00
"main/meta",
2021-12-26 19:43:48 -05:00
],
2021-12-16 19:11:52 -05:00
mesh : [
2021-12-29 10:37:17 -05:00
"&main/mesh"
2021-12-26 19:43:48 -05:00
],
mesh_work : [
2021-12-29 10:37:17 -05:00
"&mesh/work"
2021-12-27 18:13:11 -05:00
],
mesh_pool : [
2021-12-29 10:37:17 -05:00
"&mesh/pool"
2022-08-31 16:35:25 -04:00
],
cache : [
"moto/license",
2022-08-31 16:35:25 -04:00
"main/service",
],
service : [
"moto/license",
2022-08-31 16:35:25 -04:00
"moto/service"
2021-12-26 19:43:48 -05:00
]
};
// prevent caching of specified modules
2020-11-17 15:49:41 -05:00
const cachever = {};
function promise(resolve, reject) {
return new Promise(resolve, reject);
}
function rval() {
return (Math.round(Math.random()*0xffffffff)).toString(36);
}
function guid() {
return time().toString(36)+rval()+rval()+rval();
}
function time() {
return Date.now();
}
function obj2string(o) {
return JSON.stringify(o);
}
function string2obj(s) {
return JSON.parse(s);
}
function handleSetup(req, res, next) {
2020-12-13 19:31:35 -05:00
if (setupFn) {
setupFn(req, res, next);
} else {
next();
}
}
2020-05-30 20:39:56 -04:00
function handleVersion(req, res, next) {
let vstr = oversion || dversion || version;
if (["/kiri/","/mesh/","/meta/"].indexOf(req.app.path) >= 0 && req.url.indexOf(vstr) < 0) {
2020-05-30 20:39:56 -04:00
if (req.url.indexOf("?") > 0) {
2021-01-26 09:03:02 -05:00
return http.redirect(res, `${req.url},ver:${vstr}`);
2020-05-30 20:39:56 -04:00
} else {
2021-01-26 09:03:02 -05:00
return http.redirect(res, `${req.url}?ver:${vstr}`);
2020-05-30 20:39:56 -04:00
}
} else {
next();
}
}
function handleOptions(req, res, next) {
2020-06-02 20:28:48 -04:00
try {
req.app.ua = agent.parse(req.headers['user-agent'] || '');
} catch (e) {
logger.log("ua parse error on : "+req.headers['user-agent']);
}
2022-08-31 16:35:25 -04:00
res.setHeader("Service-Worker-Allowed", "/");
if (req.method === 'OPTIONS') {
addCorsHeaders(req, res);
res.end();
} else {
next();
}
}
function handleWasm(req, res, next) {
let file = req.app.path.split('/').pop();
let ext = (file || '').split('.')[1];
let path = PATH.join(dir,"src","wasm",file);
let mod = lastmod(path);
if (ext === 'wasm' && mod) {
let imd = ifModifiedDate(req);
if (imd && mod <= imd) {
res.writeHead(304, "Not Modified");
return res.end();
}
res.writeHead(200, {
'Content-Type': 'application/wasm',
'Cache-Control': 'public, max-age=600',
'Last-Modified': new Date(mod).toGMTString(),
});
res.end(fs.readFileSync(path));
} else {
next();
}
}
function handleCode(req, res, next) {
let key = req.app.path.split('/')[2].split('.')[0],
ck = code_src[key],
js = code[key];
if (!js) {
2020-05-30 18:16:40 -04:00
return http.reply404(req, res);
}
if (ck) {
2020-12-11 15:31:22 -05:00
let mpath = `${dir}/${ck.path}`;
let mod = lastmod(mpath);
if (mod > ck.mod) {
if (debug) {
2020-12-11 15:31:22 -05:00
js = code[ck.endpoint] = fs.readFileSync(mpath);
} else {
2020-12-11 15:31:22 -05:00
js = code[ck.endpoint] = minify(mpath);
}
ck.mod = mod;
}
}
addCorsHeaders(req, res);
serveCode(req, res, {
code: js,
mtime: startTime
});
}
function serveCode(req, res, code) {
if (code.deny) {
2020-05-30 18:16:40 -04:00
return http.reply404(req, res);
}
let imd = ifModifiedDate(req);
if (imd && code.mtime <= imd && !code.nocache) {
res.writeHead(304, "Not Modified");
res.end();
return;
}
let cacheControl = code.nocache ?
'private, max-age=0' :
'public, max-age=600';
res.writeHead(200, {
'Content-Type': 'application/javascript',
'Cache-Control': cacheControl,
'Last-Modified': new Date(code.mtime).toGMTString(),
});
res.end(code.code);
}
// pack/concat device script strings to inject into /code/ scripts
2020-06-04 21:29:40 -04:00
function generateDevices() {
let root = PATH.join(dir,"src","kiri-dev");
2020-06-03 22:39:30 -04:00
let devs = {};
fs.readdirSync(root).forEach(type => {
let map = devs[type] = devs[type] || {};
fs.readdirSync(PATH.join(root,type)).forEach(device => {
map[device] = JSON.parse(fs.readFileSync(PATH.join(root,type,device)));
2020-06-03 22:39:30 -04:00
});
});
2020-06-04 21:29:40 -04:00
synth.devices = `self.devices = ${JSON.stringify(devs)};`;
}
// pack/concat code modules served under "/code/"
function prepareScripts() {
2020-06-04 21:29:40 -04:00
generateDevices();
2021-12-26 19:43:48 -05:00
for (let key of Object.keys(script)) {
code[key] = concatCode(script[key]);
}
}
function concatCode(array) {
let code = [];
2020-06-03 22:44:05 -04:00
let direct = array.filter(f => f.charAt(0) !== '@');
let inject = array.filter(f => f.charAt(0) === '@').map(f => f.substring(1));
synth.inject = "/* injection point */";
// in debug mode, the script should load dependent
// scripts instead of serving a complete bundle
if (debug) {
const code = [
oversion ? `self.debug_version='${oversion}';self.enable_service=${serviceWorker};` : '',
'self.debug=true;',
'(function() { let load = [ '
];
2020-06-03 22:39:30 -04:00
direct.forEach(file => {
const vers = cachever[file] || oversion || dversion || version;
code.push(`"/${file.replace(/\\/g,'/')}?${vers}",`);
});
code.push([
']; function load_next() {',
'let file = load.shift();',
'if (!file) return;',
'if (!self.document) { importScripts(file); return load_next() }',
'let s = document.createElement("script");',
's.type = "text/javascript";',
's.src = file;',
's.onload = load_next;',
'document.head.appendChild(s);',
'} load_next(); })();'
].join('\n'));
2020-06-03 22:39:30 -04:00
inject.forEach(key => {
code.push(synth[key]);
});
return code.join('\n');
}
2020-06-03 22:39:30 -04:00
direct.forEach(file => {
let cached = getCachedFile(file, path => {
return minify(PATH.join(dir,file));
});
if (oversion) {
cached = `self.debug_version='${oversion}';self.enable_service=${serviceWorker};` + cached;
}
code.push(cached);
});
2020-06-03 22:39:30 -04:00
inject.forEach(key => {
code.push(synth[key]);
});
return code.join('');
}
2020-05-30 18:16:40 -04:00
function getCachedFile(file, fn) {
let filePath = PATH.join(dir,file);
let cachePath = cacheDir + PATH.sep + file
.replace(/\//g,'_')
.replace(/\\/g,'_')
.replace(/:/g,'_'),
cached = fileCache[filePath],
now = time();
if (cached) {
if (now - cached.lastcheck > 60000) {
let smod = lastmod(filePath),
cmod = cached.mtime;
2021-02-14 16:21:51 -05:00
if (!smod) {
throw "missing source file";
}
if (smod > cmod) {
cached = null;
} else {
cached.lastcheck = now;
}
}
}
if (!cached) {
let smod = lastmod(filePath),
cmod = lastmod(cachePath),
cacheData;
if (cmod >= smod || (forceUseCache && cmod)) {
cacheData = fs.readFileSync(cachePath);
} else {
logger.log({update_cache:filePath});
cacheData = fn(filePath);
fs.writeFileSync(cachePath, cacheData);
}
if (wrap[file]) {
// console.log('WRAP', filePath, cacheData.length);
cacheData = wrap_module(cacheData, wrap[file]);
}
cached = {
data: cacheData,
mtime: cmod || now,
lastcheck: now
};
fileCache[filePath] = cached;
}
// console.log('[*]', filePath, cached.data.length);
return cached.data;
}
function minify(path) {
let code = fs.readFileSync(path);
let mini = uglify.minify(code.toString(), {
compress: {
merge_vars: false,
unused: false
}
});
if (mini.error) {
console.trace(mini.error);
throw mini.error;
}
return mini.code;
}
function quickReply(res, code, msg) {
res.writeHead(code);
res.end(msg+"\n");
}
function ifModifiedDate(req) {
let ims = req.headers['if-modified-since'];
if (ims) {
// because sys time has a higher resolution than
// seconds converted from IMS header. so give it
// an extra second
return new Date(ims).getTime() + 1000;
}
return 0;
}
function addCorsHeaders(req, res) {
res.setHeader('Access-Control-Allow-Credentials', 'true');
res.setHeader('Access-Control-Allow-Headers', 'X-Moto-Ajax, Content-Type');
res.setHeader('Access-Control-Allow-Origin', req.headers['origin'] || '*');
if (!crossOrigin) {
res.setHeader("Cross-Origin-Opener-Policy", 'same-origin');
res.setHeader("Cross-Origin-Embedder-Policy", 'require-corp');
}
res.setHeader("Allow", "GET,POST,OPTIONS");
}
// dispatch for path prefixs
function prepath(pre) {
function handle(req, res, next) {
pre.uid = pre.uid || guid();
req.ppi = req.ppi || {};
let path = req.app.path,
key, fn, i = req.ppi[pre.uid] || 0;
while (i < pre.length) {
key = pre[i][0];
fn = pre[i++][1];
if (path.indexOf(key) === 0) {
return fn(req, res, () => {
req.ppi[pre.uid] = i;
handle(req, res, next);
});
}
}
next();
}
return handle;
}
// dispatch full fixed paths
function fullpath(map) {
return (req, res, next) => {
let fn = map[req.app.path];
if (fn) fn(req, res, next);
else next();
};
}
// dispatch full paths based on a prefix and a function map
function fixedmap(prefix, map) {
return (req, res, next) => {
let path = req.app.path;
if (path.indexOf(prefix) != 0) return next();
let fn = map[path.substring(prefix.length)];
if (fn) fn(req, res, next);
else next();
};
}
// HTTP 307 redirect
2020-05-30 20:39:56 -04:00
function redir(path, type) {
return (req, res, next) => http.redirect(res, path, type);
}
// mangle request path
function remap(path) {
return (req, res, next) => {
req.url = req.app.path = path;
next();
}
}
function cookieValue(cookie,key) {
if (!cookie) return null;
key = (key || "key") + "=";
let kpos = cookie.lastIndexOf(key);
if (kpos >= 0) {
return cookie.substring(kpos+key.length).split(';')[0];
} else {
return null;
}
}
function rewriteHtmlVersion(req, res, next) {
if (["/kiri/","/mesh/","/meta/","/kiri/engine.html","/kiri/frame.html"].indexOf(req.app.path) >= 0) {
addCorsHeaders(req, res);
let real_write = res.write;
let real_end = res.end;
let mlen = '{{version}}'.length;
let vstr = oversion || dversion || version;
if (vstr.length < mlen) {
vstr = vstr.padStart(mlen,0);
} else if (vstr.length > mlen) {
vstr = vstr.substring(0,mlen);
}
res.write = function() {
arguments[0] = arguments[0].toString().replace(/{{version}}/g,vstr);
real_write.apply(res, arguments);
};
res.end = function() {
if (arguments[0]) {
arguments[0] = arguments[0].toString().replace(/{{version}}/g,vstr);
}
real_end.apply(res, arguments);
};
}
next();
}
module.exports = init;