//const { argv } = require("process"); const fs = require("fs-extra"); const bodyParser = require("body-parser"); const glob = require("glob"); const path = require("path"); const Mustache = require("mustache"); const cors = require("cors"); const express = require("express"); const process = require("process"); /******************************************* SEE README.md to start ********************************************/ const apxtri = {}; apxtri.main = async () => { if (!fs.existsSync("/etc/nginx/nginx.conf")) { console.log( "\x1b[31m Check documentation, nginx have to be installed on this server first, no /etc/nginx/nginx.conf available, install then rerun yarn command." ); process.exit(0); } //Check prerequest data if ( fs.existsSync("../adminapi/objects/tribes/idx/tribes_dns.json") && fs.existsSync("../adminapi/objects/tribes/itm/adminapi.json") ) { // check all tribes are in tribes_dns.json const conf = fs.readJSONSync( "../adminapi/objects/tribes/itm/adminapi.json" ); const tribesdns = fs.readJsonSync( `../adminapi/objects/tribes/idx/tribes_dns.json` ); //check if new tribe was add glob .sync("../*") .filter((f) => fs.lstatSync(f).isDirectory()) .forEach(async (t) => { const tribe = path.basename(t); //console.log(tribe); if (![".",".."].includes(tribe) && !tribesdns[tribe]) { await apxtri.setuptribe(tribe, conf); } }); } else { const initconf = fs.readJsonSync( "../adminapi/apxtri/setup/initadminapi.json" ); await apxtri.setuptribe("adminapi", initconf); } // run api with update conf apxtri.runexpress( fs.readJsonSync(`../adminapi/objects/tribes/idx/tribes_dns.json`), fs.readJSONSync("../adminapi/objects/tribes/itm/adminapi.json") ); }; apxtri.getip = async () => { /** * Return json with public IP and network interfaces with their local IP * {WANIP:"public IP","eth0":"localIPoneth0",...} */ const network = {}; const urlgetip = `https://api.ipify.org?format=json`; const getdata = await fetch(urlgetip); if (getdata.ok) { const data = await getdata.json(); network.WANIP = data.ip; } const os = await import("os"); const interfaces = os.networkInterfaces(); for (const name in interfaces) { for (const iface of interfaces[name]) { // Check for IPv4 and make sure it's not an internal (loopback) address if (iface.family === "IPv4" && !iface.internal) { network[name] = iface.address; //localIP = iface.address; //console.log(`Local network IP (${name}): ${localIP}`); } } } return network; }; apxtri.setuptribe = async (tribe, conf) => { let inittribe; if (tribe == "adminapi") { console.log( "Nice to meet you, this is a first install hope you'll enjoy, if any issues please request on discord https://discord.gg/jF7cAkZn" ); try { inittribe = conf; inittribe.townpath = __dirname.replace("/adminapi/apxtri", ""); const townnation = inittribe.townpath.split("/").slice(-1)[0].split("-"); inittribe.townId = townnation[0]; inittribe.nationId = townnation[1]; inittribe.dns.push( `admin.adminapi.${inittribe.townId}.${inittribe.nationId}` ); } catch (err) { console.log("Your town folder must be something townid-nation"); } } else { console.log(`a new tribe called ${tribe} was detected`); inittribe = { tribeId: tribe, townpath: __dirname.replace("/adminapi/apxtri", ""), dns: [`admin.${tribe}.${conf.townId}.${conf.nationId}`], status: "unchain", nationId: conf.nationId, townId: conf.townId, activelog: [], }; } inittribe.sudoUser = process.env.USER; //check nation exist and town does not exist if ( !fs.existsSync("../adminapi/objects/nations/idx/lst_nations.json") || !fs.existsSync("../adminapi/objects/towns/idx/lst_towns.json") ) { console.log( `Sorry, check setup.sh process that was not able to init your adminapi/objects ` ); process.exit(0); } fs.outputJSONSync(`../adminapi/objects/tribes/itm/${tribe}.json`, inittribe, { space: 2, }); if (!fs.existsSync("../adminapi/objects/tribes/conf.json")) { fs.outputJSONSync("../adminapi/objects/tribes/conf.json", { name: "tribes", schema: "adminapi/schema/tribes.json", lastupdate: 0, }); } const lst_tribeIdpath = "../adminapi/objects/tribes/idx/lst_tribeId.json"; let lsttribe = fs.existsSync(lst_tribeIdpath) ? fs.readJsonSync(lst_tribeIdpath) : []; lsttribe.push(tribe); fs.outputJSONSync(lst_tribeIdpath, lsttribe); const tribes_dnspath = "../adminapi/objects/tribes/idx/tribes_dns.json"; const tribedns = fs.existsSync(tribes_dnspath) ? fs.readJsonSync(tribes_dnspath) : {}; tribedns[tribe] = inittribe.dns; fs.outputJSONSync(tribes_dnspath, tribedns); const tribespath = "../adminapi/objects/tribes/idx/tribes.json"; const tribes = fs.existsSync(tribespath) ? fs.readJSONSync(tribespath) : {}; tribes[tribe] = inittribe; fs.outputJSONSync(tribespath, tribes, { space: 2 }); // check nginx conf and eventually change if not starting by user "current user"; let etcnginx = fs.readFileSync("/etc/nginx/nginx.conf", "utf8"); if (etcnginx.split("\n")[0] !== `user ${inittribe.sudoUser};`) { inittribe.mainpath = inittribe.townpath.replace( `/${inittribe.townId}-${inittribe.nationId}`, "" ); const nginxmain = fs.readFileSync( "../adminapi/apxtri/setup/nginx.maincf", "utf8" ); console.log("Modify /etc/nginx/nginx.conf"); fs.outputFileSync( "/etc/nginx/nginx.conf", Mustache.render(nginxmain, inittribe), { adAdmin: true } ); } // add conf for http://adminapx.adminapi const nginxapx = fs.readFileSync( "../adminapi/apxtri/setup/nginx.wwwscf", "utf8" ); inittribe.website = "admin"; fs.outputFileSync( `../${tribe}/nginx/${inittribe.website}.${tribe}.${inittribe.townId}.${inittribe.nationId}.conf`, Mustache.render(nginxapx, inittribe) ); fs.outputFileSync( `../${tribe}/objects/wwws/admin/dist/index_en.html`, `

Wellcome in ${tribe}

`, "utF8" ); // add hosts entry for local access // this command is ran by the setup.sh // grep -q '^127.0.0.1 adminapx.adminapi' /etc/hosts || echo '127.0.0.1 adminapx.adminapi' | sudo tee -a /etc/hosts > /dev/null const ips = await apxtri.getip(); const nginxrestart = (inittribe.nginx && inittribe.nginx.restart) ? inittribe.nginx.restart : fs.readJSONSync("../adminapi/apxtri/setup/initadminapi.json").nginx .restart; const { exec } = require("child_process"); exec(nginxrestart, (error, stdout, stderr) => { if (error) { console.log("\x1b[42m", error, stdout, stderr, "x1b[0m"); process.exit(0); } else { const etchosts = Object.values(ips) .map((ip) => `${ip} ${inittribe.dns.join(" ")}`) .join("\n "); console.log( `\x1b[42m###############################################################################################################\x1b[0m\n\x1b[42mWellcome into this fresh apxtri install, currently running as "$ yarn dev".\n To access and set up a public domain name, add lines in file /etc/hosts into the machine you want to use to access:\n\x1b[0m\x1b[32m ${etchosts} \x1b[0m \x1b[42m\n Then open in your local browser \x1b[0m\x1b[32m http:// ${inittribe.dns.join( " " )} \x1b[0m \x1b[42m \nFor local dev continue with 'yarn dev'\nTo run as a production run 'yarn startapx' or 'yarn restartapx' then pm2 monitor your production process.\n\x1b[0m\n\x1b[42m###############################################################################################################\x1b[0m` ); } }); }; apxtri.runexpress = async (tribesdns, conf) => { const Odmdb = require(path.resolve("./apxtri/models/Odmdb.js")); let tribeIds = Object.keys(tribesdns); // context is store in /itm/tribename.json ={contexte:{routes:[],models:[{model:,tplstringslg:[]}]} // routes={url,route} check how to add plugin tribe route later // keep only the 2 last part (.) of domain name to validate cors with it (generic domain) let routes = []; let doms = []; tribeIds.forEach((t) => { tribesdns[t].forEach((d) => { const dm = d.split(".").slice(-2).join("."); if (!doms.includes(dm)) doms.push(dm); //reindex database attention check dev-ants/.. a bug was fixed glob.sync(`../${t}/objects/*`).forEach((o) => { //console.log("reindex: ", o); Odmdb.runidx(o); }); }); const context = {}; const pathtr = path.resolve(`../${t}`); context.routes = []; tribroutes = glob.sync(`${pathtr}/apxtri/routes/*.js`).map((f) => { const rt = `/${t}/${path.basename(f, ".js")}`; context.routes.push(rt); return { url: rt, route: f }; }); context.models = glob.sync(`${pathtr}/apxtri/models/*.js`).map((f) => { const modname = `${path.basename(f, ".js")}`; return { model: modname, tplstrings: glob .sync(`${pathtr}/objects/tplstrings/${modname}_*.json`) .map((l) => path.basename(l, ".json").split("_")[1]), }; }); //console.log(context.routes); //console.log(context.models); //const conft = `../itm/${t}.json`; //const ctx = fs.readJsonSync(conft); //ctx.context = context; //fs.outputJSONSync(conft, ctx, { spaces: 2 }); routes = routes.concat(tribroutes); }); const app = express(); // load express parameter from conf Object.keys(conf.api.appset).forEach((p) => { app.set(p, conf.api.appset[p]); }); // To set depending of data form or get size to send app.use(bodyParser.urlencoded(conf.api.bodyparse.urlencoded)); app.use(bodyParser.json(conf.api.bodyparse.json)); // To set depending of post put json data size to send app.use(express.json(conf.api.json)); app.disable("x-powered-by"); // for security app.locals.tribeids = tribeIds; const currentmod = "apxtri"; const log = conf.api.activelog ? conf.api.activelog.includes(currentmod) : false; console.log( currentmod, " Allowed DOMs to access to this apxtri server:", JSON.stringify(doms) ); console.log(currentmod, " app.locals.tribeids", app.locals.tribeids); // Cors management let originlst = "test"; doms.forEach((d) => { originlst += `|${d.replace(/\./g, "\\.")}`; }); const regtxt = `^http.?:\/\/(${originlst})`; let cor = false; const regorigin = new RegExp(regtxt); app.use((req, res, next) => { if (req.headers.origin == undefined) { cor = true; } else { cor = regorigin.test(req.headers.origin); } if (!cor) console.log( `The domain name ${req.headers.origin} is not allow to access for CORS settings, add it in itm/tribename.json in dns` ); cors({ origin: cor, allowedHeaders: conf.api.exposedHeaders, exposedHeaders: conf.api.exposedHeaders, credentials: true, preflightContinue: false, optionsSuccessStatus: 204, }); next(); }); // Routers add any routes from /routes and /plugins let logroute = "Routes available on this apxtri instance: \n"; routes.forEach((r) => { try { logroute += r.url.padEnd(30, " ") + r.route + "\n"; app.use(r.url, require(r.route)); } catch (err) { logroute += " (err check it module.exports=router;? or ...)\n======\n "; console.log("raise err-:", err); } }); if (log) { console.log(currentmod, logroute); console.log(currentmod, conf.api); } //Listen event file for each tribe // @TODO à ajouter ici const ips = await apxtri.getip(); app.listen(conf.api.port, () => { let webaccess = `/api/ waits request on port:${conf.api.port} `; let localnet = "/etc/hosts for your local network:\n" let publicnet ="/etc/hosts for internet network:\n" conf.dns.forEach((u) => { //webaccess += `http://${u}/api/ `; Object.keys(ips).forEach(ik=>{ if (ik=="WANIP"){ publicnet+= `${ips.WANIP} ${u} \n`; }else{ localnet+= `${ips[ik]} ${u} \n` } }) }); console.log( `\x1b[42m\x1b[37m${webaccess} \nOpen in your browser http(s):// ${conf.dns.join(" ")} to manage this apXtri town. \nCheck your network conf \n${localnet} ${publicnet}\nMore in README's project.\nTo get support ask \x1b[0m\x1b[32m in discord https://discord.gg/jF7cAkZn ` ); console.log( "\x1b[42m\x1b[37m \n", " Made with love for people's freedom, enjoy !!! ", "\x1b[0m" ); }); }; apxtri.main();