From 7656f57a11c43ee973890192e6c0de266c7384eb Mon Sep 17 00:00:00 2001 From: philc Date: Thu, 7 Dec 2023 12:04:19 +0100 Subject: [PATCH] full code --- apxtri.js | 245 ++ middlewares/checkHeaders.js | 102 + middlewares/footer.md | 1 + middlewares/header.md | 127 + middlewares/isAuthenticated.js | 207 ++ models/Checkjson.js | 364 +++ models/Contracts.js | 113 + models/Nations.js | 260 ++ models/Notifications.js | 223 ++ models/Odmdb.js | 863 +++++ models/Pagans.js | 187 ++ models/Toolsbox.js | 646 ++++ models/Towns.js | 208 ++ models/Trackings.js | 29 + models/Tribes.js | 377 +++ models/Wwws.js | 111 + models/lg/Checkjson_en.json | 6 + models/lg/Checkjson_fr.json | 7 + models/lg/Nations_fr.json | 7 + models/lg/Notifications_fr.json | 9 + models/lg/Odmdb_en.json | 5 + models/lg/Odmdb_fr.json | 19 + models/lg/Pagans_en.json | 8 + models/lg/Pagans_fr.json | 14 + models/lg/Tribes_fr.json | 3 + models/lg/middlewares_en.json | 8 + models/lg/middlewares_fr.json | 10 + models/unittest/Checkjson.js | 158 + models/unittest/Odmdb.js | 43 + package.json | 112 + routes/nations.js | 35 + routes/notifications.js | 30 + routes/odmdb.js | 600 ++++ routes/pagans.js | 327 ++ routes/towns.js | 32 + routes/trackings.js | 82 + routes/tribes.js | 413 +++ routes/wwws.js | 19 + setup/conf/initconf.json | 34 + setup/conf/nginx.conf.mustache | 43 + setup/conf/nginxmodelwebsite.conf.mustache | 97 + setup/conf/nginxproxyparams.mustache | 11 + setup/conf/setup_xx.json | 1 + unittest.js | 118 + yarn.lock | 3345 ++++++++++++++++++++ 45 files changed, 9659 insertions(+) create mode 100755 apxtri.js create mode 100755 middlewares/checkHeaders.js create mode 100644 middlewares/footer.md create mode 100644 middlewares/header.md create mode 100755 middlewares/isAuthenticated.js create mode 100755 models/Checkjson.js create mode 100755 models/Contracts.js create mode 100755 models/Nations.js create mode 100644 models/Notifications.js create mode 100644 models/Odmdb.js create mode 100644 models/Pagans.js create mode 100755 models/Toolsbox.js create mode 100644 models/Towns.js create mode 100644 models/Trackings.js create mode 100755 models/Tribes.js create mode 100644 models/Wwws.js create mode 100644 models/lg/Checkjson_en.json create mode 100644 models/lg/Checkjson_fr.json create mode 100644 models/lg/Nations_fr.json create mode 100644 models/lg/Notifications_fr.json create mode 100644 models/lg/Odmdb_en.json create mode 100644 models/lg/Odmdb_fr.json create mode 100644 models/lg/Pagans_en.json create mode 100644 models/lg/Pagans_fr.json create mode 100644 models/lg/Tribes_fr.json create mode 100644 models/lg/middlewares_en.json create mode 100644 models/lg/middlewares_fr.json create mode 100644 models/unittest/Checkjson.js create mode 100644 models/unittest/Odmdb.js create mode 100755 package.json create mode 100755 routes/nations.js create mode 100644 routes/notifications.js create mode 100644 routes/odmdb.js create mode 100755 routes/pagans.js create mode 100755 routes/towns.js create mode 100644 routes/trackings.js create mode 100755 routes/tribes.js create mode 100644 routes/wwws.js create mode 100644 setup/conf/initconf.json create mode 100755 setup/conf/nginx.conf.mustache create mode 100755 setup/conf/nginxmodelwebsite.conf.mustache create mode 100644 setup/conf/nginxproxyparams.mustache create mode 100644 setup/conf/setup_xx.json create mode 100644 unittest.js create mode 100644 yarn.lock diff --git a/apxtri.js b/apxtri.js new file mode 100755 index 0000000..f252088 --- /dev/null +++ b/apxtri.js @@ -0,0 +1,245 @@ +const { argv } = require("process"); +const fs = require("fs-extra"); +const mustache = require("mustache"); +const bodyParser = require("body-parser"); +const glob = require("glob"); +const path = require("path"); +const cors = require("cors"); +const express = require("express"); +const process = require("process"); + +/******************************************* + +SEE https://gitea.ndda.fr/apxtri/apxtri/wiki/Devrules +To have a quick understanding and convention before doing deeply in source code + +To share configuration : + process.env.dirtown is folder where town folder name /townId-nationId is accessible + const conf = require(`../../conf/townconf.json`); + app.locals.tribeids is defined later in apixtrib.js and allow express app to always have in memory a dynamic of tribeId available in req.app.locals.tribeids +*/ +/** + * 1st install for dev + * run $ node apxtri.js nationId:ants townId:devfarm dns:devfarm-ants + * then just yarn dev + * it create a folder outside ../townId-nationId/ + * To convert a dev into a chain town run again with relevant param: + * run $ node apxtri.js nationId:ants townId:devfarm dns:devfarm-ants + * check the web interface http://dns + * then just yarn startpm2 your town is under PM2 control + * + * + * @param {args} args key:value example node apxtri nationId:ants townId:devfarm dns:devfarm-ants + * if no parameter from adminapi/www/adminapx/conf/setup_xx.json + * + * Keyword townId = "devfarm" then this is unchain town to dev + * else this is a production town ready to chain to the nationId + * + * @returns listen onto http:/dns (80 and 443) for admin webapp and http://localhost:initconf.api.port + * by setting the nginx parameter + * A folder for town data is created at the same level than apxtri as /townId-nationId/conf.json ... + */ + +// check nginx exist +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(); +} +const param = {}; +argv.slice(2).forEach((arg) => { + const kv = arg.split(":"); + if (kv.length == 2) { + param[kv[0]] = kv[1]; + } +}); +const log=(info,mode)=>{ + // with yarn dev => mode=dev show only log for dev + // to check other => node apxtrie mode:bug for example + // if mode is not define then it log it + if (!mode || param.mode==mode) console.log(info) +} +if (!fs.existsSync('..conf/townconf.json')){ + // This is a first install + const nam= path.resolve('..').split('/').slice(-1)[0].split('-'); + const town=nam[0] + const nation=nam[1] + // doit exister param.dns pour mettre à jour le conf.json pour la conf adminapi + //run setup process +} + + +// setup_xx.json is gitignore so at first install we are in dev configuration +/* +let infotown = { + nationId: "ants", + townId: "devfarm", + dns: "devfarm-ants", + comment: + "Auto generate setup from apxtri after node apxtri nationId:value townId:value dns:domaine_to_access", +}; +if (fs.existsSync(`${__dirname}/adminapi/www/adminapx/conf/setup_xx.json`)) { + infotown = fs.readJsonSync( + `${__dirname}/adminapi/www/adminapx/conf/setup_xx.json` + ); +} +if ( + Object.keys(param).length > 0 && + param.nationId && + param.townId && + param.dns +) { + infotown.nationId = param.nationId; + infotown.townId = param.townId; + infotown.dns = param.dns; +} +fs.outputJsonSync( + `${__dirname}/adminapi/www/adminapx/conf/setup_xx.json`, + infotown +); +infotown.dirapi = __dirname; +infotown.dirtown = path.resolve( + `${__dirname}/../${infotown.townId}-${infotown.nationId}` +); +process.env.dirtown = infotown.dirtown; +infotown.dirapxwebapp = path.resolve(`${infotown.dirapi}/..`); //same level as apxtri +if ( + !fs.existsSync(`${infotown.dirtown}/conf.json`) || + !fs.existsSync(`${__dirname}/adminapi/www/nginx_adminapx.conf`) +) { + // Case of new town or request a reset of dns to access adminapx + // genere a minimum conf with nationId, townId, dns, dirapi, dirtown + fs.outputJsonSync(`${infotown.dirtown}/conf.json`, infotown, { space: 2 }); + const Towns = require("./api/models/Towns"); + const rescreate = Towns.create(); + if (rescreate.status != 200) { + console.log("Sorry error "); + process.exit(); + } +} +*/ +const conf = require(path.resolve(`../conf/townconf.json`)); + +// Create and update ./nationchains plutot que cette fonction +// A creuser et voir comment synchro +/*const { updateobjectsfromfreshesttown } = require("./models/Nations.js"); +updateobjectsfromfreshesttown(conf.towns, { + pagans: "alias_all.json", + towns: "townId_all.json", + nations: "nationId_all.json", +}); +*/ +// Run main express process for a /towId-nationId/tribes + +let tribelist = {}; +if (fs.existsSync(`../nationchains/tribes/idx/tribeId_all.json`)) { + tribelist = fs.readJsonSync(`../nationchains/tribes/idx/tribeId_all.json`); +} +let doms = conf.dns; // only dns of town during the init process +let tribeIds = []; +let routes = glob.sync(`./routes/*.js`).map((f) => { + return { url: `/${path.basename(f, ".js")}`, route: f }; +}); + +glob.sync(`../nationchains/tribes/*/api/routes/*.js`).forEach((f) => { + const ids = f.indexOf(`../nationchains/tribes/`); + const trib = f.slice( + ids + `../nationchains/tribes/`.length, + f.lastIndexOf("/api/routes") + ); + routes.push({ url: `/${trib}/${path.basename(f, ".js")}`, route: f }); +}); +//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) +Object.keys(tribelist).forEach((t) => { + tribelist[t].dns.forEach((d) => { + const dm = d.split(".").slice(-2).join("."); + if (!doms.includes(dm)) doms.push(dm); + }); + tribeIds.push(t); +}); +console.log("Allowed DOMs to access to this apxtri server: ", doms); + +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)); +// To set depending of post put json data size to send +app.use(express.json()); +app.use(bodyParser.json(conf.api.bodyparse.json)); +app.disable('x-powered-by');// for security +app.locals.tribeids = tribeIds; +console.log("app.locals.tribeids", app.locals.tribeids); +// Cors management +const corsOptions = { + origin: (origin, callback) => { + //before modif only origin == undefined + // ajout d'une condition en dev à voir || (infotown.townId == "devfarm" && (origin == undefined || origin == null)) + if ( + (origin == undefined || + origin.indexOf("chrome-extension") > -1) + ) { + callback(null, true); + } else { + const rematch = /^https?:\/\/(.*):.*/g.exec(origin); + //console.log( rematch ) + let tmp = origin.replace(/http.?:\/\//g, "").split("."); + + if (rematch && rematch.length > 1) tmp = rematch[1].split("."); + //console.log( 'tmp', tmp ) + let dom = tmp[tmp.length - 1]; + if (tmp.length > 1) { + dom = `${tmp[tmp.length - 2]}.${tmp[tmp.length - 1]}`; + } + console.log( + `origin: ${origin}, dom:${dom}, CORS allowed? : ${doms.includes(dom)}` + ); + if (doms.includes(dom)) { + callback(null, true); + } else { + callback(false); + } + } + }, + exposedHeaders: Object.keys(conf.api.exposedHeaders), +}; +// CORS +app.use(cors(corsOptions)); +// Static Routes // try to use nginx route instead in comments +/*app.use( express.static( `${__dirname}/nationchains/tribes/${conf.mayorId}/www/cdn/public`, { + dotfiles: 'allow' +} ) ); +*/ +// 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); + } +}); +console.log(logroute); +if (param.mode && param.mode=='dev') { + console.log( + `\x1b[42m############################################################################################\x1b[0m\n\x1b[42mThis is dev conf accessible in http://devfarm-ants to switch this as production, you must run:\n 1 - 'yarn dev nationId:ants townId:usbfarm dns:usbfarm-ants ' to conf your town and check it.\n 2 - 'yarn startpm2'\n Where:\n\x1b[42m * nationId have to exist in the nationchains\n * townId new or if exist must have the smae current dns,\n * dns domaine that has to redirect 80/443 into this server (example wall-ants.ndda.fr redirect to 213.32.65.213 ).\n Check README's project to learn more.\x1b[0m\n To work with apxweb for the front use http://defarm-ants/apxweb/www/tplname/src/index.html to use the api during dev process\n\x1b[42m############################################################################################\x1b[0m` + ); +} +app.listen(conf.api.port, () => { + let webaccess = `api waits request on `; + conf.dns.forEach((u) => { + webaccess += `http://${u}:${conf.api.port}`; + }); + console.log(webaccess); +}); +console.log( + "\x1b[42m\x1b[37m", + "Made with love for people's freedom, enjoy !!!", + "\x1b[0m" +); diff --git a/middlewares/checkHeaders.js b/middlewares/checkHeaders.js new file mode 100755 index 0000000..f332005 --- /dev/null +++ b/middlewares/checkHeaders.js @@ -0,0 +1,102 @@ +const conf = require(`../../conf/townconf.json`); + /** + * @api {get} http://header/CheckHeaders - CheckHeaders + * @apiGroup Middlewares + * @apiName CheckHeaders + * @apiDescription a list of headers are mandatory to access apxtri see in your space town /conf.json.exposedHeaders + * + * @apiHeader {string} xalias 'anonymous' or unique alias + * @apiHeader {string} xapp name of the webapp store in tribe/tribeid/www/{xapp} + * @apiHeader {string} xlang the 2 letter request langage (if does not exist then return en = english). + * @apiHeader {string} xtribe unique tribe name where xapp exist + * @apiHeader {string} xdays a timestamp 0 or generate during the authentifyme process + * @apiHeader {string} xhash anonymous or signature of message: xalias_xdays created by alias private key during authentifyme process + * @apiHeader {array[]} xprofils list of string profil apply into xtribe for xapp + * @apiHeader {string} xuuid a unique number uuid.v4 created the fisrt time a domain is visited on a device + * @apiHeader {integer} xtrkversion a version number link to tracking system + * + * @apiHeaderExample {json} Header-Example: + * { + * Cache-Control: "no-cache", + * Expires: 0, Pragma:"no-cache", + * xalias:"jojo", + * xapp:"presentation", + * xdays:1700733068298 + * xhash:"LS0tLS1CRUdJTiBQR1AgU0lHTkVEIE1FU1NBR0UtLS0tLQpIYXNoOiBTSEE1MTIKCmpvam9fMTcwMDczMzA2ODI5OAotLS0tLUJFR0lOIFBHUCBTSUdOQVRVUkUtLS0tLQoKd25VRUFSWUtBQ2NGZ21WZklJd0prTmFVQ0daRHVUYnBGaUVFTjZlc1ZMSWdURmtPRGFVaDFwUUlaa081Ck51a0FBR09MQVA5OS96c21YeEd0b0VuYnpnekppZDJMcDA3YlBNZ1gwNUdhOUFVWjlCQm91Z0VBOVlYVworYjZIM2JHWHVhbEVOc3BrdUk1alNlTFNUWGNkSStjTExTZk5OQTg9Cj1uVjhNCi0tLS0tRU5EIFBHUCBTSUdOQVRVUkUtLS0tLQo=", + * xlang:"fr", + * xprofils:["anonymous", "pagans"], + * xtribe:"smatchit", + * xtrkversion:1, + * xuuid:"ea1cf73f-27f5-4c69-ab53-197a0feab9b2" + * } + * @apiErrorExample {json} Error-Response: + * HTTP/1/1 400 Not Found + * { + * status:400, + * ref:"middlewares", + * msg:"missingheaders", + * data:["headermissing1"] + * } + * @apiErrorExample {json} Error-Response: + * HTTP/1/1 404 Not Found + * { + * status:404, + * ref:"middlewares" + * msg:"tribeiddoesnotexist", + * data: {xalias} + * } + */ +const checkHeaders = (req, res, next) => { + req.session = {}; + const header = {}; + if (!req.header("xlang") && req.header("Content-Language")) + req.params.xlang = req.header("Content-Language"); + let missingheader = []; + //console.log("req.headers", req.headers); + for (const h of conf.api.exposedHeaders) { + //console.log( h, req.header( h ) ) + if (req.params[h]) { + header[h] = req.params[h]; + } else if (req.header(h)) { + header[h] = req.header(h); + } else { + missingheader.push(h); + } + } + // console.log( 'pass header', header ) + // store in session the header information + req.session.header = header; + // Each header have to be declared + if (missingheader != "") { + // bad request + return res.status(400).json({ + status:400, + ref: "middlewares", + msg: "missingheader", + data: missingheader, + }); + } + //console.log( req.app.locals.tribeids ) + // xtribe == "town" is used during the setup process + // xtribe == "adminapi" is used to access /adminapi + if ( + !( + ["town","adminapi"].includes(header.xtribe) || req.app.locals.tribeids.includes(header.xtribe) + ) + ) { + return res.status(404).json({ + status:404, + ref: "middlewares", + msg: "tribeiddoesnotexist", + data: { xtribe: header.xtribe }, + }); + } + if (!conf.api.languages.includes(header.xlang)) { + console.log("warning language requested does not exist force to english"); + header.xlang = "en"; + } + //set anonymous profil + req.session.header.xprofils=["anonymous"] + next(); +}; +module.exports = checkHeaders; diff --git a/middlewares/footer.md b/middlewares/footer.md new file mode 100644 index 0000000..b219490 --- /dev/null +++ b/middlewares/footer.md @@ -0,0 +1 @@ +Documentation Best practices \ No newline at end of file diff --git a/middlewares/header.md b/middlewares/header.md new file mode 100644 index 0000000..74aec47 --- /dev/null +++ b/middlewares/header.md @@ -0,0 +1,127 @@ +## api users and backend developers + +api documentation for routes and middleware has to respect apidoc's rules [https://apidocjs.com/](https://apidocjs.com)  + +To update this doc accessible in [https://wal-ants.ndda.fr/cdn/apidoc](https://wal-ants.ndda.fr/cdn/apidoc) : + +`yarn apidoc`  + +For api tribe's doc  accessible in [https://smatchit.io/cdn/apidoc](https://smatchit.io/cdn/apidoc) [:](https://smatchit.io/cdn/apidoc:)  + +`yarn apidoctribename` + +Objects manage in apxtri: pagans, notifications, nations, towns, tribes, wwws  + +All others objects are manage in town/tribe  + +```plaintext +/townName_nationName/ + /apxtri/ # core process + /nationchains/conf.json # town settings contain all glabl parameter +``` + +url: **/api/routeName** For core api apxtri in /apxtri : + +```plaintext +/apxtri/middlewares/ +/apxtri/routes/ +/apxtri/models/ +/apxtri/models/lg/ lauage accessible by https://wall-ants.ndda.fr/nationchains/models/Checkjson_fr.json +/apxtri/models/unitest/ +``` + +url: **/api/smatchit/routeName** for tribe smatchit example api in /town\_nation/tribes/smatchit(tribeid)  + +```plaintext +/nationchains/tribes/smatchit/api/routes/ +/nationchains/tribes/smatchit/api/models/ +/nationchains/tribes/smatchit/api/models/lg/ language customization accessible https://smatchit.io/smatchit/models/model_lg.json +``` + +**static files** are served by nginx, each tribe nginx conf are store and can be customize in /nationchains/tribes/{tribe}/www/nginx\_{xtribe}\_{xapp}.conf + +## Object management (Odmdb) + +An object has a name and is defined by a schema that contain properties key. + +A propertie has a name and a list of caracteristics (type, pattern,format,...) that have to be validate to be accepted. +All properties respect the rules [https://json-schema.org/draft/2020-12/schema,](https://json-schema.org/draft/2020-12/schema,) some extra"format" can be add to mutualise recurrent regex pattern + +To access a schema [https://wall-ants.ndda.fr/nationchains/schema/nations.json](https://wall-ants.ndda.fr/nationchains/schema/nations.json) and language specifique [https//:wall-ants.ndda.fr/nationchains/schema/lg/nations\_fr.json](https//:wall-ants.ndda.fr/nationchains/schema/lg/nations_fr.json) + +A checkjson.js is available to manage all specific format [https://wall-ants.ndda.fr/Checkjson.js](https://wall-ants.ndda.fr/Checkjson.js) see **Odmdb - schema Checkjson** + +**Additional properties that not exist in 2020-12/schema :** + +**required**: an array of required properties + +**apxid**: the propertie used as an unique id + +**apxuniquekey**: array of unique properties + +**apxidx** : array of index + +**apxaccessrights**: object with key profilname and accessrights on properties {profilname:{C:\[properties array\],R:\[properties array\],U:\[\],D:\[\]}} + +Items of an object are store in files into :   + +```plaintext +/objectnames/idx/keyval_objkey.json +/objectnames/itm/uniqueid.json +``` + +## api pre-request + +**Valid header see Middlewares** + +App use openpgp.js lib to sign xdays\_xalias with a privatekey and store it in xhash. + +/middlewares/isAuthenticated.js check if (xhash) is a valid signature of the public key a xhash is valid for 24 hours + +See Pagans models that contain authentification process + +**api Return in 3 data structure:** + +A - data file from a classical get  [https://wall-ants.ndda.fr/Checkjson.js](https://smatchit.io/Checkjson.js) + +B -  a json single answer {status, ref,msg,data}: + +* status: http code return +* ref: model/route name reference where message come from +* msg: a message template key store into models/lg/name\_lg.json (where lg is 2 letters language) +* data: an object data use to render the value of the message key. + +C - a json multi answer {status,multimsg:\[{ref,msg,data}\]} + +         Each {ref,msg,data\] work the same way than B + +To show feedback context message in a language lg => get /nationchains/models/{{ref}}\_{{lg}}.json +This contain a json {msg:"mustache template string to render with data"}   + +## Accessrights: + +An alias is just an identity, to access a tribe, a person must exist with an authenticated alias into /nationchains/tribes/{tribename}/persons/itm/{alias}.json + +A person has a property profils with a list of profilename, common profiles are : anonymous (no identity) / pagan (an identity)  / person (an identity with access right into a tribe) / druid (the administrator of a tribe) / major (administrator of a town/server) + +Each object has an apxaccessrights that is a list of profil and CRUD access per object key . + +## Add tribe's api: + +Accessible with https://dns/api/tribename/routes + +```plaintext +/nationchains/tribes/tribename/api/routes +/nationchains/tribes/tribename/api/middlewares +/nationchains/tribes/tribename/api/models +/nationchains/tribes/tribename/schema +/nationchains/tribes/tribename/schema/lg +``` + +```plaintext +// Example of a route +const conf = require(`../../../../../conf/townconf.json`); +const express = require(`../../../../../apxtri/node_modules/express`); +const fs = require(`../../../../../apxtri/node_modules/fs-extra`); +const Nofications = require(`../../../../../apxtri/models/Notifications.js`); +``` \ No newline at end of file diff --git a/middlewares/isAuthenticated.js b/middlewares/isAuthenticated.js new file mode 100755 index 0000000..cf48e90 --- /dev/null +++ b/middlewares/isAuthenticated.js @@ -0,0 +1,207 @@ +const fs = require("fs-extra"); +const dayjs = require("dayjs"); +//const path=require('path'); +const glob = require("glob"); +// To debug it could be easier with source code: +// const openpgp = require("/media/phil/usbfarm/apxtri/node_modules/openpgp/dist/node/openpgp.js"); +const openpgp = require("openpgp"); + +/** + * @api {get} http://header/istauthenticated - isAuthenticated + * @apiGroup Middlewares + * @apiName isAuthenticated + * @apiDescription - valid if exist xalias_xdays_xhash.substr(20,200) in town/tmp/tokens/ + * - if not, + * - valid if xhash signature sign xalias_xdays with alias's publickey. + * - if not valid => not allowed + * - If valid => + * - store a xalias_xdays_xhash.substr (20,200) into /tmp/tokens with xprofils array from person. + * - update header.xprofils from this token + * + * apxtri profils are anonymous, pagans, mayor (on a node server), druid (on a tribe like smatchit). + * + * pagan identity is independant of domain (tribe), by default profils are :['anonymous','pagans']. if this alias exist in a tribe domain as a person then his profils come from /tribes/{tribeId}/objects/person/itm/{alias}.json profils:['anonymous','pagans','person','seeker'] any profils allowed to act on tribe objects. + * + * Each profil have CRUD accessright on object managed in schema in apxaccessrights:{owner,profil:{"C":[],"R":[properties],"U":[properties],"D":[]}}, see Odmdb for details. + * + * A process run once each day to clean up all xhash tmp/tokens oldest than 24 hours. + * + **/ +const isAuthenticated = async (req, res, next) => { + /*console.log('PASSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS') + console.log(__dirname) + console.log(path.resolve('../tmp/tokens')) + if (fs.existsSync('../tmp/tokens')) console.log('pass A') + if (fs.existsSync('../tmp/tokens')) console.log('pass B') + */ + const withlog = true; + const currentday = dayjs().date(); + fs.ensureDirSync(`../tmp/tokens`); + let menagedone = fs.existsSync( + `../tmp/tokens/menagedone${currentday}` + ); + + if (withlog) + console.log(`menagedone${currentday} was it done today?:${menagedone}`); + if (!menagedone) { + // clean oldest + const tsday = dayjs().valueOf(); // now in timestamp format + glob.sync(`../tmp/tokens/menagedone*`).forEach((f) => { + fs.removeSync(f); + }); + glob.sync(`../tmp/tokens/*.json`).forEach((f) => { + const fsplit = f.split("_"); + const elapse = tsday - parseInt(fsplit[2]); + //24h 86400000 milliseconde 15mn 900000 + if (elapse && elapse > 86400000) { + fs.remove(f); + } + }); + fs.outputFile( + `../tmp/tokens/menagedone${currentday}`, + "done by middleware/isAUthenticated" + ); + } + //Check register in tmp/tokens/ + if (withlog) console.log("isAuthenticate?", req.session.header, req.body); + + const resnotauth = { + ref: "middlewares", + msg: "notauthenticated", + data: { + xalias: req.session.header.xalias, + xaliasexists: true, + }, + }; + if ( + req.session.header.xalias == "anonymous" || + req.session.header.xhash == "anonymous" + ) { + if (withlog) console.log("alias anonymous means not auth"); + resnotauth.status = 401; + return res.status(resnotauth.status).json(resnotauth); + } + + let tmpfs = `../tmp/tokens/${req.session.header.xalias}_${req.session.header.xtribe}_${req.session.header.xdays}`; + //max filename in ext4: 255 characters + tmpfs += `_${req.session.header.xhash.substring( + 150, + 150 + tmpfs.length - 249 + )}.json`; + + const bruteforcepenalty = async (alias, action) => { + const sleep = (ms) => { + return new Promise((resolve) => setTimeout(resolve, ms)); + }; + const failstamp = `../tmp/tokens/${alias}.json`; + if (action == "clean") { + //to reinit bruteforce checker + if (withlog) console.log("try to clean penalty file ", failstamp); + fs.remove(failstamp, (err) => { + if (err) console.log("Check forcebrut ", err); + }); + } else if (action == "penalty") { + const stamp = fs.existsSync(failstamp) + ? fs.readJSONSync(failstamp) + : { lastfail: dayjs().format(), numberfail: 0 }; + stamp.lastfail = dayjs().format(); + stamp.numberfail += 1; + fs.outputJSON(failstamp, stamp); + if (withlog) console.log("penalty:", stamp); + await sleep(stamp.numberfail * 100); //increase of 0,1 second the answer time per fail + if (withlog) console.log("time out penalty"); + } + }; + if (!fs.existsSync(tmpfs)) { + // need to check detached sign + let publickey = ""; + console.log(process.cwd()); + console.log(process.env.PWD); + console.log(__dirname); + const aliasinfo = `../nationchains/pagans/itm/${req.session.header.xalias}.json`; + if (fs.existsSync(aliasinfo)) { + publickey = fs.readJsonSync(aliasinfo).publickey; + } else if (req.body.publickey) { + resnotauth.data.xaliasexists = false; + publickey = req.body.publickey; + } + if (publickey == "") { + if (withlog) console.log("alias unknown"); + resnotauth.status = 404; + resnotauth.data.xaliasexists = false; + return res.status(resnotauth.status).send(resnotauth); + } + if (withlog) console.log("publickey", publickey); + if (publickey.substring(0, 31) !== "-----BEGIN PGP PUBLIC KEY BLOCK") { + if (withlog) + console.log("Publickey is not valid as armored key:", publickey); + await bruteforcepenalty(req.session.header.xalias, "penalty"); + resnotauth.status = 404; + return res.status(resnotauth.status).send(resnotauth); + } + const clearmsg = Buffer.from(req.session.header.xhash, "base64").toString(); + if (clearmsg.substring(0, 10) !== "-----BEGIN") { + if (withlog) + console.log("xhash conv is not valid as armored key:", clearmsg); + await bruteforcepenalty(req.session.header.xalias, "penalty"); + resnotauth.status = 404; + return res.status(resnotauth.status).send(resnotauth); + } + if (withlog) console.log("clearmsg", clearmsg); + const pubkey = await openpgp.readKey({ armoredKey: publickey }); + const signedMessage = await openpgp.readCleartextMessage({ + cleartextMessage: clearmsg, + }); + const verificationResult = await openpgp.verify({ + message: signedMessage, + verificationKeys: pubkey, + }); + if (withlog) console.log(verificationResult); + if (withlog) console.log(verificationResult.signatures[0].keyID.toHex()); + try { + await verificationResult.signatures[0].verified; + if ( + verificationResult.data != + `${req.session.header.xalias}_${req.session.header.xdays}` + ) { + resnotauth.msg = "signaturefailled"; + if (withlog) + console.log( + `message recu:${verificationResult.data} , message attendu:${req.session.header.xalias}_${req.session.header.xdays}` + ); + await bruteforcepenalty(req.session.header.xalias, "penalty"); + resnotauth.status = 401; + return res.status(resnotauth.status).send(resnotauth); + } + } catch (e) { + resnotauth.msg = "signaturefailled"; + if (withlog) console.log("erreur", e); + await bruteforcepenalty(req.session.header.xalias, "penalty"); + resnotauth.status = 401; + return res.status(resnotauth.status).send(resnotauth); + } + // authenticated then get person profils (person = pagan for a xtrib) + const person = `${process.env.dirtown}/tribes/${req.session.header.xtribe}/objects/persons/itm/${req.session.header.xalias}.json`; + if (withlog) { + console.log("Profils tribe/app management"); + console.log("person", person); + } + if (fs.existsSync(person)) { + const infoperson = fs.readJSONSync(person); + console.log(infoperson); + infoperson.profils.forEach((p) => { + if (!req.session.header.xprofils.includes(p)) req.session.header.xprofils.push(p); + }) + }else{ + if (!req.session.header.xprofils.includes('pagans')) req.session.header.xprofils.push("pagans"); + } + fs.outputJSONSync(tmpfs, req.session.header.xprofils); + } else { + //tmpfs exist get profils from identification process + req.session.header.xprofils = fs.readJSONSync(tmpfs); + } + bruteforcepenalty(req.session.header.xalias, "clean"); + console.log(`${req.session.header.xalias} Authenticated`); + next(); +}; +module.exports = isAuthenticated; diff --git a/models/Checkjson.js b/models/Checkjson.js new file mode 100755 index 0000000..b651a61 --- /dev/null +++ b/models/Checkjson.js @@ -0,0 +1,364 @@ +/* +This module have to be use in back as well front +can be include in project with + - >into a browser : + - into a node.js : const Checkjson = require( `../nationchains/socialworld/contracts/Checkjson.js`); +*/ +// --## + +const Checkjson = {}; +Checkjson.schema = {}; +Checkjson.schema.properties = {}; +Checkjson.schema.properties.type = {}; +Checkjson.schema.properties.type.string = (str) => typeof str === "string"; +Checkjson.schema.properties.type.array = (val) => Array.isArray(val); +Checkjson.schema.properties.type.object = (val) => typeof val === 'object' && val !== null && !Array.isArray(val); +Checkjson.schema.properties.type.number = (n) => typeof n === "number"; +Checkjson.schema.properties.type.boolean = (n) => typeof n === "boolean"; +Checkjson.schema.properties.type.integer = (n) => + n != "" && !isNaN(n) && Math.round(n) == n; +Checkjson.schema.properties.type.float = (n) => + n != "" && !isNaN(n) && Math.round(n) != n; //not yet in json schema +Checkjson.schema.properties.minLength = (str, min) => + typeof str === "string" && str.length > parseInt(min); +Checkjson.schema.properties.maxLength = (str, max) => + typeof str === "string" && str.length < parseInt(max); +Checkjson.schema.properties.multipleOf = (n, val) => + typeof n === "number" && + typeof val === "number" && + parseFloat(n) / parseFloat(val) - + Math.round(parseFloat(n) / parseFloat(val)) < + 0.0000001; +Checkjson.schema.properties.range = ( + n, + minimum, + exclusiveMinimum, + maximum, + exclusiveMaximum +) => { + //console.log(minimum,exclusiveMinimum,maximum, exclusiveMaximum,n) + if (typeof n !== "number") return false; + if (minimum && parseFloat(n) < parseFloat(minimum)) return false; + if (exclusiveMinimum && parseFloat(n) <= parseFloat(exclusiveMinimum)) + return false; + if (maximum && parseFloat(n) > parseFloat(maximum)) return false; + if (exclusiveMaximum && parseFloat(n) >= parseFloat(exclusiveMaximum)) + return false; + return true; +}; +Checkjson.schema.properties.pattern = (str, pattern) => { + try { + pattern = new RegExp(pattern); + } catch (e) { + console.log("err pattern in checkjon", pattern); + return false; + } + return pattern.test(str); +}; +Checkjson.schema.properties.enum = (str, enumvalues) => { + if (Array.isArray(enumvalues)) { + return typeof str === "string" && enumvalues.includes(str); + } else if (tribeId) { + //enumvalues is a reference of objectname.key + const { tribeId, obj, keyid } = enumvalues.split("."); + return fs.existsSync( + `../../nationchains/tribes/${tribeId}/schema/${obj}/itm/${keyid}.json` + ); + } else { + return true; + } +}; +// to check a value for a pattern +// Checkjson.schema.properties.pattern(value, properties[p].pattern) +/** + * + * @param {string} str to test + * @param {string} format keyworkd existing in Checkjson.schema.properties.format + * @return null if format does not exist, true or false + */ +Checkjson.testformat=(str, format)=>{ + if (!Checkjson.schema.properties.format[format]) { return null} + return Checkjson.schema.properties.pattern(str, Checkjson.schema.properties.format[format]) + +} +// see format https://json-schema.org/understanding-json-schema/reference/string.html#format +// to check a just value with a format use Checkjson.testformat=(value, format) +Checkjson.schema.properties.format = { + "date-time": /\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d{1,3}/, + stringalphaonly: /^[A-Za-z0-9]{3,}$/, + time: /[0-2]\d:[0-5]\d:[0-5]\d\.\d{1,3}/, + date: /\d{4}-[01]\d-[0-3]\d/, + duration: / /, + email: + /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/, + "idn-email": / /, + uuid: /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/, + uri: / /, + "uri-reference": / /, + iri: / /, + hostname: / /, + "idn-hostname": / /, + ipv4: /^([0–9]{1,3}.){3}.([0–9]{1,3})$/, + ipv6: /^((([0–9A-Fa-f]{1,4}:){7}[0–9A-Fa-f]{1,4})|(([0–9A-Fa-f]{1,4}:){6}:[0–9A-Fa-f]{1,4})|(([0–9A-Fa-f]{1,4}:){5}:([0–9A-Fa-f]{1,4}:)?[0–9A-Fa-f]{1,4})|(([0–9A-Fa-f]{1,4}:){4}:([0–9A-Fa-f]{1,4}:){0,2}[0–9A-Fa-f]{1,4})|(([0–9A-Fa-f]{1,4}:){3}:([0–9A-Fa-f]{1,4}:){0,3}[0–9A-Fa-f]{1,4})|(([0–9A-Fa-f]{1,4}:){2}:([0–9A-Fa-f]{1,4}:){0,4}[0–9A-Fa-f]{1,4})|(([0–9A-Fa-f]{1,4}:){6}((b((25[0–5])|(1d{2})|(2[0–4]d)|(d{1,2}))b).){3}(b((25[0–5])|(1d{2})|(2[0–4]d)|(d{1,2}))b))|(([0–9A-Fa-f]{1,4}:){0,5}:((b((25[0–5])|(1d{2})|(2[0–4]d)|(d{1,2}))b).){3}(b((25[0–5])|(1d{2})|(2[0–4]d)|(d{1,2}))b))|(::([0–9A-Fa-f]{1,4}:){0,5}((b((25[0–5])|(1d{2})|(2[0–4]d)|(d{1,2}))b).){3}(b((25[0–5])|(1d{2})|(2[0–4]d)|(d{1,2}))b))|([0–9A-Fa-f]{1,4}::([0–9A-Fa-f]{1,4}:){0,5}[0–9A-Fa-f]{1,4})|(::([0–9A-Fa-f]{1,4}:){0,6}[0–9A-Fa-f]{1,4})|(([0–9A-Fa-f]{1,4}:){1,7}:))$/, + telephonefr: /^0[1-9][0-9]{9}$/, + telephoneinter: /^\+*(\d{3})*[0-9,\-]{8,}/, + password: + /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[$@$!%*?&.])[A-Za-z\d$@$!%*?&.{}:|\s]{8,}/, + postalcodefr: /(^\d{5}$)|(^\d{5}-\d{4}$)/, + pgppublickey: + /^-----BEGIN PGP PUBLIC KEY BLOCK-----(\n|\r|\r\n)(\n|\r|\r\n)([0-9a-zA-Z\+\/=]*(\n|\r|\r\n))*-----END PGP PUBLIC KEY BLOCK-----(\n|\r|\r\n)?$/gm, + pgpprivatekey: + /^-----BEGIN PGP PRIVATE KEY BLOCK-----(\n|\r|\r\n)(\n|\r|\r\n)([0-9a-zA-Z\+\/=]*(\n|\r|\r\n))*-----END PGP PRIVATE KEY BLOCK-----(\n|\r|\r\n)?$/gm, +}; +Checkjson.schema.properties.default; +Checkjson.schema.validation = (schema) => { + /*validate a schema structure*/ + const multimsg = []; + const res = {}; + if (schema.properties) { + Object.keys(schema.properties).forEach((p) => { + const properties = schema.properties; + if ( + properties[p].type && + typeof properties[p].type === "string" && + !Checkjson.schema.properties.type[properties[p].type] + ) { + multimsg.push({ + ref: "Checkjson", + msg: "schemaerrtypedoesnotexist", + data: { propertie: p, type: properties[p].type }, + }); + } + if ( + properties[p].type && + typeof properties[p].type === "object"){ + if (properties[p]['$ref']){ + //This is manage by Odmdb.schema to load recursively complex schema + multimsg.push({ + ref: "Checkjson", + msg: "externalrefnotload", + data: { propertie: p, ref: properties[p]["$ref"]}, + }); + } + //case type=="object" with properties + if (properties[p].properties){ + const checksub = Checkjson.schema.validation(properties[p]) + if (checksub.status!=200){ + multimsg = multimsg.concat(checksub.multimsg) + } + } + // if not $ref or no properties then any object is accepted + } + + if ( + properties[p].format && + !Checkjson.schema.properties.format[properties[p].format] + ) { + multimsg.push({ + ref: "Checkjson", + msg: "schemaerrformatdoesnotexist", + data: { propertie: p, format: properties[p].format }, + }); + } + if (properties[p].enum && !Array.isArray(properties[p].enum)) { + multimsg.push({ + ref: "Checkjson", + msg: "schemaerrenumnotarray", + data: { propertie: p, enum: properties[p].enum }, + }); + } + }); + } + // 406 means not acceptable + if (multimsg.length > 0) { + res.status = 406; + res.multimsg = multimsg; + } else { + res.status = 200; + res.ref = "Checkjson"; + res.msg = "validcheck"; + } + return res; +}; +/** + * Check data with a schema + * + * @param {object} schema a json schema + * @param {*} data some data to check using schema + * @param {*} withschemacheck boolean that force a schema check (usefull on modification schema) + * @returns {status: 200, ref:"Checkjson", msg:"validcheck", data:{itm:object}} + * {status:417, multimsg:[{re,msg,data}],data:{itm:object}} + */ +Checkjson.schema.data = (schema, data, withschemacheck) => { + /* validate a data set with a schema in a context ctx */ + /* + console.log('#################') + console.log(schema); + console.log('---------') + console.log(data) + */ + const propertiescheck=(properties,subdata)=>{ + // properties ={prop1:{type,format},prop2:{type:object,...}} + // subdata={prop1,prop2} + // Return [] => no error, else 1 item per error {msg,ref:checkjson,data} + let multimsg=[] + Object.keys(properties).forEach((p) => { + //type is mandatory in a propertie + if (subdata[p]) { + if (properties[p].properties){ + //means it is a subobject + multimsg=multimsg.concat(propertiescheck(properties[p].properties,subdata[p])) + } + //type can be a list of string; number, array, boolean, object, null + const typlist = + properties[p].type && typeof properties[p].type === "string" + ? [properties[p].type] + : properties[p].type; + let valid = false; + typlist.forEach((typ) => { + // at least one test have to be valid + if (Checkjson.schema.properties.type[typ](subdata[p])) valid = true; + }); + if (!valid) + multimsg.push({ + ref: "Checkjson", + msg: "dataerrpropertie", + data: { key: p, value: subdata[p] }, + }); + if ( + properties[p].minLength && + !Checkjson.schema.properties.minLength( + subdata[p], + properties[p].minLength + ) + ) { + multimsg.push({ + ref: "Checkjson", + msg: "dataerrpropertie", + data: { + key: p, + value: subdata[p], + minLength: properties[p].minLength, + }, + }); + } + if ( + properties[p].maxLength && + !Checkjson.schema.properties.maxLength( + subdata[p], + properties[p].maxLength + ) + ) { + multimsg.push({ + ref: "Checkjson", + msg: "dataerrpropertie", + data: { + key: p, + value: subdata[p], + maxLength: properties[p].maxLength, + }, + }); + } + if ( + properties[p].multipleOf && + !Checkjson.schema.properties.multipleOf( + subdata[p], + properties[p].multipleOf + ) + ) { + multimsg.push({ + ref: "Checkjson", + msg: "dataerrpropertie", + data: { + key: p, + value: subdata[p], + multipleOf: properties[p].multipleOf, + }, + }); + } + if ( + properties[p].minimum || + properties[p].maximum || + properties[p].exclusiveMinimum || + properties[p].exclusiveMaximum + ) { + // test range + if ( + !Checkjson.schema.properties.range( + subdata[p], + properties[p].minimum, + properties[p].exclusiveMinimum, + properties[p].maximum, + properties[p].exclusiveMaximum + ) + ) { + multimsg.push({ + ref: "Checkjson", + msg: "dataerrpropertie", + data: { + key: p, + value: subdata[p], + minimum: properties[p].minimum, + maximum: properties[p].maximum, + exclusiveMinimum: properties[p].exclusiveMinimum, + exclusiveMaximum: properties[p].exclusiveMaximum, + }, + }); + } + } + if ( + properties[p].enum && + !Checkjson.schema.properties.enum(subdata[p], properties[p].enum) + ) { + multimsg.push({ + ref: "Checkjson", + msg: "dataerrpropertie", + data: { key: p, value: subdata[p], enumlst: properties[p].enum }, + }); + } + if (properties[p].format) { + properties[p].pattern = + Checkjson.schema.properties.format[properties[p].format]; + } + if ( + properties[p].pattern && + !Checkjson.schema.properties.pattern(subdata[p], properties[p].pattern) + ) { + multimsg.push({ + ref: "Checkjson", + msg: "dataerrpropertie", + data: { key: p, value: subdata[p], pattern: properties[p].pattern }, + }); + } + } else if (schema.required && schema.required.includes(p)) { + multimsg.push({ + ref: "Checkjson", + msg: "dataerrpropertierequired", + data: { key: p, required: true }, + }); + } + }); + return multimsg + };//end propertiescheck() + + if (withschemacheck) { + const validschema = Checkjson.schema.validation(schema); + if (validschema.status != 200) return validschema; + } + let multi=propertiescheck(schema.properties,data) + const res = {}; + + if (multi.length > 0) { + res.status = 417; + res.multimsg = multi; + } else { + res.status = 200; + res.ref = "Checkjson"; + res.msg = "validcheck"; + } + if (schema.apxid) { + res.data={apxid : data[schema.apxid],itm:data}; + } + return res; +}; +if (typeof module !== "undefined") module.exports = Checkjson; diff --git a/models/Contracts.js b/models/Contracts.js new file mode 100755 index 0000000..0d14f16 --- /dev/null +++ b/models/Contracts.js @@ -0,0 +1,113 @@ +const fs = require( 'fs-extra' ); +const glob = require( 'glob' ); +const moment = require( 'moment' ); +const axios = require( 'axios' ); + +const conf=require(`../conf.json`) + +/* +Model that will process actions plan for each client like sending email campain, or anything that +are plan in /tribes/tribeid/actions/todo +*/ +const Cards = {}; //require('../../models/Cards'); +const Contracts = {}; +/* +Send if envoicampain a liste of email in param.msg.destperso with param.headers +if not envoicampain, it just return a test about what to send +@param = {headers, msg:{destperso}} +*/ +Contracts.sendcampain = async ( param, envoicampain ) => { + if( envoicampain ) { + // Carefull w the action post outputs/msg just wait the feedback of the 1st message + const retcampain = await axios.post( 'https://mail.maildigit.fr/outputs/msg', param.msg, { + headers: param.headers + } ); + if( retcampain.status !== 200 ) { + console.log( "err", retcampain.payload.moreinfo ); + fs.appendFileSync( `${conf.tribes}/log_erreurglobal.txt`, moment( new Date() ) + .format( 'YYYYMMDD HH:mm:ss' ) + ' - IMPOSSIBLE TO SEND CAMPAIN TODO for :' + param.tribeid + ' -- ' + retcampain.payload.moreinfo + '\n', 'utf-8' ); + }; + return retcampain; + } else { + // permet de tester ce qu'il y a à envoyer + let premieremail = ""; + for( let i = 0; i < param.msg.destperso.length; i++ ) { + premieremail += param.msg.destperso[ 0 ].email + ","; + } + return { + status: 201, + payload: { + info: [ 'simplecomptage' ], + model: 'Contracts', + moreinfo: "#email: " + param.msg.destperso.length + " - 5 1st emails: " + premieremail + } + }; + } +} +Contracts.initActiontodo = async ( envoie ) => { + const datedeb = moment( new Date() ) + .format( 'YYYYMMDD HH:mm:ss' ); + let todo, actiondone; + let log = { + nbaction: 0, + nbactionexec: 0, + nbactionerr: 0, + actionlist: "" + }; + const listclient = fs.readJsonSync( `${conf.tribes}/tribeids.json` ); + for( let clid in listclient ) { + console.log( listclient[ clid ] ); + let listaction = glob.sync( `${conf.tribes}/${listclient[clid]}/actions/todo/*.json` ); + for( let action in listaction ) { + console.log( listaction[ action ] ) + log.nbaction++; + todo = fs.readJsonSync( listaction[ action ] ); + let passdate = true; + // currentdate doit etre après la startDate si existe et avant valideuntilDate si existe + // console.log('test now est avant date start ', moment() < moment(todo.startDate, 'YYYYMMDD HH:mm:ss').toDate()); + if( todo.startDate && ( moment() < moment( todo.startDate, 'YYYYMMDD HH:mm:ss' ) + .toDate() ) ) { + passdate = false; + }; + // currentdate ne doit pas depasser la date de validité de la tache + // console.log('test now est après la date de validite ', moment() > moment(todo.validuntilDate, 'YYYYMMDD HH:mm:ss').toDate()); + if( todo.valideuntilDate && ( moment() > moment( todo.validuntilDate, 'YYYYMMDD HH:mm:ss' ) + .toDate() ) ) { + passdate = false; + }; + // currentdate + if( passdate && todo.action && todo.error == "" ) { + log.nbactionexec++; + const actiondone = await Contracts[ todo.action ]( todo, envoie ); + todo.datesRun.push( moment( new Date() ) + .format( 'YYYYMMDD HH:mm:ss' ) ); + //console.log("actiondone" + log.actionlist += "STATUS:" + actiondone.status + " -- " + listaction[ action ] + "\n"; + if( actiondone.status == 200 ) { + todo.error = ""; + } else { + log.nbactionerr++; + todo.error += "status : " + actiondone.status + ' ' + actiondone.payload.moreinfo; + }; + if( parseInt( todo.maxnumberoftime ) && todo.maxnumberoftime != "999" && ( todo.datesRun.length >= parseInt( todo.maxnumberoftime ) ) ) { + //archive en done this triggeraction + fs.outputJsonSync( listaction[ action ].replace( '/todo/', '/done/' ), todo, { + spaces: 2 + } ); + fs.unlinkSync( listaction[ action ] ); + } else { + fs.outputJsonSync( listaction[ action ], todo, { + spaces: 2 + } ); + } + } else { + log.actionlist += "STATUS : not executed " + listaction[ action ] + "\n"; + }; + }; + }; + const trace = "###################### LOGS ####################\nSTART:" + datedeb + " END:" + moment( new Date() ) + .format( 'YYYYMMDD HH:mm:ss' ) + "\n nombre d'actions analysées : " + log.nbaction + " dont executées : " + log.nbactionexec + " dont en erreur: " + log.nbactionerr + "\n" + log.actionlist; + fs.appendFileSync( `${conf.tribes}/log.txt`, trace, 'utf-8' ); + return "done"; +} +module.exports = Contracts; diff --git a/models/Nations.js b/models/Nations.js new file mode 100755 index 0000000..bb63528 --- /dev/null +++ b/models/Nations.js @@ -0,0 +1,260 @@ +const bcrypt = require("bcrypt"); +const fs = require("fs-extra"); +const glob = require("glob"); +const jwt = require("jwt-simple"); +const axios = require("axios"); +const path = require("path"); +const conf = require(`../../conf/townconf.json`); +const Odmdb = require("./Odmdb.js"); +// lowercase 1st letter is normal +const towns = require("./Towns.js"); +const pagans = require("./Pagans.js"); +/* +Blockchain manager +* Manage network directory of nations and towns +* read Blockchain and search, +* submit a transaction (now) or contract (futur) to store from userA.pubkey to userB.pubkey a number of AXESS +* mine to be able to register a block and create AXESS +* manage APIXP rules 20 M APIXP 1AXESS = 1 block validation +* manage contract = action if something appened validate by a proof of work +*/ +const Nations = {}; +Nations.init = () => { + console.group("init Nations"); +}; + +Nations.chaintown = (nationId, townId) => { + /** + * if not already exist Add a requested town into conf.towns.push({ "townId": "wall", "nationId": "ants", "dns": "wall-ants.ndda.fr" }) + */ +}; + +Nations.updateobjectsfromfreshesttown = (dnstownlist, objectidx) => { + /** + * Get lasttime update per apxtri object then choose the latest source and update local town + * if an item exist localy and does not from the town requested + * @Param {array} dnstownlist list of dns to get latest data + * @Param {object} objectidx objectnme:idxfile {agans:"alias_all.json",...} + * @return create/update nationchains/pagans town nation + */ + const localversion = {}; + const objlist = Object.keys(objectidx); + objlist.forEach((o) => { + let objconf = { + name: o, + schema: `../nationchains/tribes/adminapi/schema/${o}.jsons`, + lastupdate: -1, + }; + if (fs.existsSync(`../nationchains/${o}/conf.json`)) { + objconf = fs.readJsonSync(`../nationchains/${o}/conf.json`); + } else { + fs.outputJsonSync(`../nationchains/${o}/conf.json`, objconf); + } + localversion[o] = [conf.dns[0], objconf.lastupdate]; + }); + //console.log(localversion); + for (let t = 0; t < dnstownlist.length; t++) { + if (conf.townId != dnstownlist[t].townId) { // to avoid update itself + let promiseconf = []; + let objecttotest = []; + objlist.forEach((o) => { + //console.log(`https://${dnstownlist[t].dns}/nationchains/${o}/conf.json`); + objecttotest.push(o); + promiseconf.push( + axios.get(`https://${dnstownlist[t].dns}/nationchains/${o}/conf.json`) + ); + }); + Promise.all(promiseconf) + .then((reps) => { + let promiseidx = []; + let objecttoupdate = []; + let objlastupdate = []; + for (let i = 0; i < objecttotest.length; i++) { + if ( + parseInt(reps[i].data.lastupdate) > + parseInt(localversion[reps[i].data.name][1]) + ) { + // add promise to get data + /*console.log( + `https://${dnstownlist[t].dns}/nationchains/${ + reps[i].data.name + }/idx/${objectidx[reps[i].data.name]}` + );*/ + objecttoupdate.push(objecttotest[i]); + objlastupdate.push(reps[i].data.lastupdate); + promiseidx.push( + axios.get( + `https://${dnstownlist[t].dns}/nationchains/${ + reps[i].data.name + }/idx/${objectidx[reps[i].data.name]}` + ) + ); + } + } + Promise.all(promiseidx) + .then((rets) => { + for (let j = 0; j < objecttoupdate.length; j++) { + Odmdb.updatefromidxall( + objecttoupdate[j], + objectidx[objecttoupdate[j]], + rets[j].data, + objlastupdate[j] + ); + } + }) + .catch((err) => { + console.log("ERR get idx data"); + console.log(err); + }); + }) + .catch((err) => { + console.log("ERR get conf lastupdate"); + console.log(err); + }); + } + } + return {status:200,ref:"Nations",msg:"updated",data:{}}; +}; +Nations.synchronizeold = () => { + /* + Run process to communicate with a list of towns to update network and transaction + */ + //update himself then send to other information + if (process.env.NODE_ENV != "prod") { + // Not concerned + return {}; + } + const initcurrentinstance = { + fixedIP: "", + lastblocknumber: 0, + firsttimeupdate: 0, + lastimeupdate: 0, + positifupdate: 0, + negatifupdate: 0, + pubkeyadmin: "", + tribeids: [], + logins: [], + knowninstance: [], + }; + let currentinstance = initcurrentinstance; + try { + currentinstance = fs.readFileSync( + `${conf.tribes}/${conf.mayorId}/nationchains/nodes/${conf.rootURL}`, + "utf-8" + ); + } catch (err) { + console.log("first init"); + } + const loginsglob = fs.readJsonSync(`${conf.tmp}/loginsglob.json`, "utf-8"); + currentinstance.logins = Object.keys(loginsglob); + currentinstance.tribeids = [...new Set(Object.values(loginsglob))]; + currentinstance.instanceknown = glob.Sync( + `${conf.tribes}/${conf.mayorId}/nationchains/nodes/*` + ); + //Save it + fs.outputJsonSync( + `${conf.tribes}/${conf.mayorId}/nationchains/nodes/${conf.rootURL}`, + currentinstance + ); + // proof of work + // try to find a key based on last block with difficulty + // if find then send to all for update and try to get token + // in any case rerun Nations.synchronize() + currentinstance.instanceknown.forEach((u) => { + if (u != conf.rootURL) { + //send currentinstance info and get back state of + axios + .post(`https://${u}/nationchains/push`, currentinstance) + .then((rep) => { + newdata = rep.payload.moreinfo; + //Available update info + fs.readJson( + `${conf.tribes}/${conf.mayorId}/nationchains/nodes/${u}`, + (err, data) => { + if (err) { + data.negatifupdate += 1; + data.lasttimeupdate = Date.now(); + } else { + data.positifupdate += 1; + data.lastimeupdate = Date.now(); + data.tribeids = newdata.tribeids; + data.logins = newdata.logins; + data.lastblocknumber = newdata.lastblocknumber; + newdata.knowninstance.forEach((k) => { + if (!data.knowninstance.includes(k)) { + data.knowninstance.push(k); + //init the domain for next update + initcurrentinstance.firsttimeupdate = Date.now(); + fs.outputJson( + `${conf.tribes}/${conf.mayorId}/nationchains/nodes/${k}`, + initcurrentinstance, + "utf-8" + ); + } + }); + } + //save with info + fs.outputJson( + `${conf.tribes}/${conf.mayorId}/nationchains/nodes/${u}`, + data + ); + } + ); + }) + .catch((err) => { + //Not available + data.negatifupdate += 1; + data.lasttimeupdate = Date.now(); + fs.outputJson( + `${conf.tribes}/${conf.mayorId}/nationchains/nodes/${u}`, + data + ); + }); + } + }); +}; + +Nations.create = (conf) => { + /* + @conf from a nationchains/socialworld/setup/townSetup {object, nationId, townId, dns} + @return + */ + const res = {}; + if (conf.object == "towns") { + Odmdb.create("nationchains/socialworld/objects", "towns", conf); + } + const nations = fs.readJsonSync( + "./nationchains/nations/idx/nationId_all.json" + ); + if (!ObjectKeys(nations).includes(conf.nationId)) { + res.status = 404; + res.info = `your nationId ${conf.nationId} does not exist you have to choose an existing one`; + return res; + } + const towns = fs.readJsonSync("./nationchains/towns/idx/townId_all.json"); + if (towns[conf.nationId].includes(conf.townId)) { + res.status = 409; + res.info = `This conf.townId already exist you have to find a unique town name`; + return res; + } + const towndata = { + uuid: conf.townId, + nationid: conf.nationId, + url: `${conf.townId}.${conf.nationId}.${conf.dns}`, + status: conf.dns == "unchain" ? "unchain" : "tochain", + }; + const metatown = fs.readJsonSync( + "./nationchains/socialworld/metaobject/towns.json" + ); + Odmdb.add(objectpath, towns, metatown, towndata); + + fs.outputJsonSync( + `./nationchains/socialworld/objects/towns/${townId}.json`, + towndata + ); + res.status = 200; + res.info = `${townId} create for ${nationId} nation`; + return res; +}; + +module.exports = Nations; diff --git a/models/Notifications.js b/models/Notifications.js new file mode 100644 index 0000000..ae79ff4 --- /dev/null +++ b/models/Notifications.js @@ -0,0 +1,223 @@ +const glob = require("glob"); +const path = require("path"); +const fs = require("fs-extra"); +const axios = require("axios"); +//const smtp = require("smtp-client"); +const nodemailer = require("nodemailer"); +const conf = require(`../../conf/townconf.json`); +/** + * To manage any communication between Pagan + * mayor druid emailing/sms/paper from tribe register smtp, simcard, mail api to Person(s) / Pagan(s) + * volatile notification message from tribe activities to Pagans / person () + * + */ +const Notifications = {}; + +Notifications.get = (alias, tribeId) => { + const notiffile = `../nationchains/tribes/${req.params.tribeId}/notifications/${req.params.alias}.json`; + const msg = fs.existsSync(notiffile) ? fs.readJSONSync(notiffile) : {}; + return { + status: 200, + ref: "Notification", + msg: "Messagelist", + data: { notif: [{ tribeId, msg }] }, + }; +}; + +Notifications.sendsms = async (data, tribeId) => { + /** + * Never use need wallet in mailjet to test + * To set up with mailjet see https://dev.mailjet.com/sms/guides/send-sms-api/#authentication + * + * @param {string} data.To a phone number with international +3360101010101 + * @param {string} data.Text text to send + * + * a conf.sms with {url:"smsurl", Token:"", From:""} + * + * + */ + + if (!conf.sms) { + return { + status: 412, + ref: "Notifications", + msg: "missingconf", + data: { tribe: tribeId }, + }; + } + let missingk = [][("To", "Text")].forEach((k) => { + if (!data[k]) { + missingk.push(k); + } + }); + if (missingk.lenght > 0) { + return { + status: 428, + ref: "Notifications", + msg: "missingdata", + data: { missingk: missingk }, + }; + } + let confsms = conf.sms; + if ( + fs.existsSync( + `../nationchains/tribes/itm/${req.session.header.xtribe}.json` + ) + ) { + const conftrib = fs.readJSONSync( + `../nationchains/tribes/itm/${req.session.header.xtribe}.json` + ); + if (conftrib.sms) confsms = conftrib.sms; + } + data.From = confsms.From; + const sendsms = await axios.post(confsms.url, { + headers: { + Authorization: `Bearer ${confsms.MJ_TOKEN}`, + "Content-Type": "application/json", + }, + body: JSON.stringify(data), + }); + if (sendsms.status == 200) { + return { + status: 200, + ref: "Notifications", + msg: "successfullsentsms", + data: {}, + }; + } else { + return { + status: sendsms.status, + ref: "Notifications", + msg: "errsendsms", + data: { err: sendsms.data }, + }; + } + + /* si tout se passe bien: + { + "From": "MJPilot", + "To": "+33600000000", + "Text": "Have a nice SMS flight with Mailjet !", + "MessageId": "2034075536371630429", + "SmsCount": 1, + "CreationTS": 1521626400, + "SentTS": 1521626402, + "Cost": { + "Value": 0.0012, + "Currency": "EUR" + }, + "Status": { + "Code": 2, + "Name": "sent", + "Description": "Message sent" + } +} +} +*/ +}; + +Notifications.sendmail = async (data, tribe) => { + /** + * See https://nodemailer.com/message/ for available fields to add + * @param {string} [data.from] an email authorized by smtp used priority from header xtribe + * @param {string} data.to list of email separate by , + * @param {string} data.subject + * @param {string} data.html + * @param {string} data.text + * @param {string} [data.Cc] list of email in copy + * @param {string} [data.Bcc] list of email in hidden copy + * @param {string} [data.attachments] array of + * {filename:'filename.txt',content:'txt'}, + * {filename:'img.svg',path:"https://....svg", contentType:'image/svg'} + * {filename:'img.svg',path:"https://....svg", contentType :'text/plain'} + * {filename:'img.png',path:"data:text/svg;base64.aGVsbG8gd29ybGQ="} + * + * @example data + * {"to":"wall-ants.ndda.fr", + * "subject":"Test", + * "html":"

test welcome

", + * "text":"test welcome", + * "attachments":[{filename:"text.txt",pathfile:"/media/phil/textA.txt","contenttype":"text/plain"}] + * } + * @return {object} + * { status: 200, ref:"pagans",msg:"",data: { } } + * + * + */ + if (!conf.smtp || !conf.emailcontact) { + return { + status: 412, + ref: "Notifications", + msg: "missingconf", + data: { tribe: tribe }, + }; + } + if (!data.from) { + data.from = conf.emailcontact; + } + let missingk = []; + ["from", "to", "subject", "html", "text"].forEach((k) => { + if (!data[k]) { + missingk.push(k); + } + }); + if (missingk.lenght > 0) { + return { + status: 428, + ref: "Notifications", + msg: "missingdata", + data: { missingk: missingk }, + }; + } + let confsmtp = conf.smtp; + const conftribfile = `../nationchains/tribes/itm/${tribe}.json`; + if (fs.existsSync(conftribfile)) { + const conftrib = fs.readJSONSync(conftribfile); + confsmtp = conftrib.smtp; + if (!data.from) data.from = conftrib.emailcontact; + } + + //const client = smtp.connect(confsmtp); + const transporter = await nodemailer.createTransport(confsmtp); + //@todo add attachments management + if (data.filelist) { + data.attachments=[]; + let missingfile = []; + data.filelist.forEach((fo) => { + if (fs.existsSync(fo.pathfile)){ + + }else{ missingfile.push(fo.pathfile);} + }); + if (missingfile.lenght > 0) + return { + status: 428, + ref: "Notifications", + msg: "missingfile", + data: { missingfile: missingfile }, + }; + } + //console.log("data:", data); + const res = await transporter.sendMail(data); + if ( + res.accepted && + data.to.split(",").reduce((acc, m) => acc && res.accepted.includes(m), true) + ) { + data.accepted = res.accepted; + data.rejected = res.rejected; + return { + status: 200, + ref: "Notifications", + msg: "successfullsentemail", + data, + }; + } else if (res.accepted && res.rejected) { + data.accepted = res.accepted; + data.rejected = res.rejected; + return { status: 410, ref: "Notifications", msg: "errsendmail", data }; + } else { + data.errmailer = res.err; + return { status: 417, ref: "Notifications", msg: "errsendmail", data }; + } +}; + +module.exports = Notifications; diff --git a/models/Odmdb.js b/models/Odmdb.js new file mode 100644 index 0000000..1c90cbb --- /dev/null +++ b/models/Odmdb.js @@ -0,0 +1,863 @@ +const glob = require("glob"); +const path = require("path"); +const fs = require("fs-extra"); +const dayjs = require("dayjs"); +const axios = require("axios"); +const conf = require(`../../conf/townconf.json`); +const Checkjson = require(`./Checkjson.js`); +const { promiseHooks } = require("v8"); + +/** + * This manage Objects for indexing, searching, checking and act to CRUD + * @objectPathName = objectpath/objectname + * objectpath/objectname/conf.json + * /idx/all_key1.json = {key1value:{object}} + * lst_key1.json = [key1valA,key1val2,...] + * key2_key1.json = {key2value:[key1value]} + * all_key1_filtername = {key1value:{object}} + * /itm/apxidvalue.json + * in conf.json: + * { + * "name": "object name ex:'nations'", + * "schema": "relativ schema from dirapi dirtown ex:'adminapi/schema/nations.json'"", + * "lastupdateschema": 0, time stamp last schema update + * "lastupdatedata":0 time stamp last itm update + * } + * + * Specifics key in schema to apxtri: + * apxid : the field value to use to store item + * apxuniquekey : list of field that has to be unique you cannot have 2 itm with same key value + * apxidx : list of index file /idx/ + * { "name":"lst_fieldA", "keyval": "alias" }, => lst_fieldA.json = [fieldAvalue1,...] + { "name":"all_fieldA", "keyval": "fieldA" }, => all_fieldA.json = + if fieldA in apxuniquekey = {fieldAvalue1:{object}} + not in apxuniquekey = {fieldAvalue1:[{object}]} + { "name":"word_fieldA", "keyval": "fieldA", "objkey": ["fieldB"]}, => word_fieldA.json + if fieldA in apxuniquekey = {fieldAvalue1:fieldBvalue,} + if fieldA not in uniquekey = {fieldAvalue1: [fieldBv1,fieldBv2,]} + { "name":"word_fieldA", "keyval": "fieldA", "objkey": ["fieldB","fieldC"]}, => word_fieldA.json + if fieldA in apxuniquekey = {fieldAvalue1:{fieldB:val,fieldC:val},} + if fieldA not in uniquekey = {fieldAvalue1: [{fieldB:val,fieldC:val},]} + * apxaccessrights : list of profil with CRUD accesrights + + + + **/ + +const Odmdb = {}; + +/** + * @api syncObject + * @param {string} url to an existing object conf (/objectname/conf.json) + * @param {timestamp} timestamp + * 0 => rebuild local object from all_{idapx}.json + * >0 => update itm and idx search by datetime + * @param + */ +Odmdb.syncObject = () => {}; + +/** + * @api createObject: create a space to host object + * + * @source {string} "new", url, + * @schemapath {string} path to create or replace a schema ${schemaPath}/schema/ + * @objectPath {string} path where object are store + * @objectName {string} name of the object + * @schema {object} the json schema for this object + * @lgjson {object} the json file for a specific language + * @lg {string} the 2 letters language + * + * Setup a new object localy => + * source + * + * - from scratch => + * Create + * - from a synchronization + * Download from source all_{apxid}.json + * + * + */ +Odmdb.createObject = ( + source, + schemaPath, + objectPath, + objectName, + schema, + lgjson, + lg +) => { + if (!fs.existsSync(schemaPath)) { + return { + status: 404, + ref: "Odmdb", + info: "pathnamedoesnotexist", + moreinfo: { fullpath: schemaPath }, + }; + } + if (!fs.existsSync(objectPath)) { + return { + status: 404, + ref: "Odmdb", + info: "pathnamedoesnotexist", + moreinfo: { fullpath: objectPath }, + }; + } + // store schema file if not empty undefined or {} + if ( + schema && + !(Object.keys(schema).length === 0 && schema.constructor === Object) + ) { + fs.outputJSONSync(`${schemaPath}/schema/${objectName}.json`, schema, { + spaces: 2, + }); + } + if ( + lgjson && + lg && + !(Object.keys(lgjson).length === 0 && lgjson.constructor === Object) + ) { + fs.outputJSONSync(`${schemaPath}/lg/${objectName}_${lg}.json`, lgjson, { + spaces: 2, + }); + } + //create environnement object with the new schema config + if (!fs.existsSync(`${objectPath}/${objectName}`)) { + fs.outputJsonSync( + `${objectPath}/${objectName}/idx/confjson`, + { schema: `${schemaPath}/schema/${objectName}.json` }, + { spaces: 2 } + ); + } + return { status: 200 }; +}; + +/** + * Update an object + * @param {string} objectPathname + * @param {object} meta update request + * lg: + * lgobj: object to store in /lg/objectname_lg.json + * schema: an update schema + * @return {status, ref:"Odmdb", msg:"", data} + * + * Create a tmp object env and check existing object to identify any issues + * If all is fine then apply change by replacing + */ +Odmdb.updateObject = (objectPathname, meta) => {}; + +/** + * Get a schema from objectPathname + * + * todo only local schema => plan a sync each 10minutes + * @schemaPath local path adminapi/schema/objectName.json or /tribename/schema/objectName + * @validschema boolean if necessary to check schema or not mainly use when change schema + * @return {status:200,data:{conf:"schemaconf",schema:"schemacontent"} } + */ +Odmdb.Schema = (objectPathname, validschema) => { + const getpath = (schemaPath) => { + if (schemaPath.slice(-5) != ".json") schemaPath += ".json"; + if (schemaPath.substring(0, 4) == "http") { + // lance requete http pour recuperer le schema avec un await axios + } else { + schemaPath = `../nationchains/tribes/${schemaPath}`; + /*if (schemaPath.substring(0, 9) == "adminapi/") { + schemaPath = `${conf.dirapi}/${schemaPath}`; + } else { + schemaPath = `${conf.dirtown}/tribes/${schemaPath}`; + }*/ + if (!fs.existsSync(schemaPath)) { + return {}; + } else { + return fs.readJsonSync(schemaPath); + } + } + }; + console.log(`${objectPathname}/conf.json`); + const confschema = fs.readJsonSync(`${objectPathname}/conf.json`); + console.log(confschema); + const res = { + status: 200, + ref: "Odmdb", + msg: "getschema", + data: { conf: confschema }, + }; + res.data.schema = getpath(confschema.schema); + + if (Object.keys(res.data.schema).length == 0) { + return { + status: 404, + ref: "Odmdb", + msg: "schemanotfound", + data: { objectPathname, schema: {} }, + }; + } + + //looking for type:object with $ref to load and replace by ref content (ref must be adminapi/ or tribeid/) + //@todo only 1 level $ref if multi level need to rewrite with recursive call + Object.keys(res.data.schema.properties).forEach((p) => { + if ( + res.data.schema.properties[p].type == "object" && + res.data.schema.properties[p]["$ref"] + ) { + const subschema = getpath(res.data.schema.properties[p]["$ref"]); + if (Object.keys(res.data.schema).length == 0) { + res.status = 404; + res.msg = "missingref"; + res.data.missingref = res.data.schema.properties[p]["$ref"]; + return res; + } else { + subschema.description += ` from external schema: ${res.data.schema.properties[p]["$ref"]}`; + res.data.schema.properties[p] = subschema; + } + } + }); + + if (!res.data.schema.apxid) { + return { + status: 406, + ref: "Odmdb", + msg: "missingprimarykey", + data: {}, + }; + } + if (res.data.schema.apxidx) { + //add apxidx to apxuniquekey in case not + if (!res.data.schema.apxuniquekey.includes(res.data.schema.apxid)) { + res.data.schema.apxuniquekey.push(res.data.schema.apxid); + } + res.data.schema.apxidx.forEach((idx) => { + if ( + idx.objkey && + !res.data.schema.apxuniquekey.includes(idx.keyval) && + !idx.objkey.includes(res.data.schema.apxid) + ) { + return { + status: 406, + ref: "Odmdb", + msg: "unconsistencyapxidx", + data: { + name: idx.name, + keyval: idx.keyval, + objkey: idx.objkey, + apxid: res.data.schema.apxid, + }, + }; + } + }); + } + if (validschema || 1 == 1) { + // return {status:200, ref, msg} or {status!:200,multimsg:[{ref,msg;data}]} + const check = Checkjson.schema.validation(res.data.schema); + if (check.status != 200) { + res.multimsg = check.multimsg; + res.status = check.status; + } + } + return res; +}; +Odmdb.search = (objectPath, objectName, search) => { + /* + @search= { + txt: string, + algo: match | pattern | fuzzy + fieldstring:[list of field], + indexfilter:{index1:[val1,val2 | ] } + } + Return data:[uuids] + + example: search exact match hill in townId + heavy search={txt:"hill",algo:"match",fieldstring:"toxnId"} + light search={txt:"hill", algo:"match", indexfilter:{"key":"townId","value":[]}} + light search={txt:"hill", algo:"match", indexfilter:{"key":"nationId","value":"ants"}} + + */ + const schema = Odmdb.schema(objectPath, objectName); + if (schema.status != 200) return schema; +}; + +Odmdb.r = (objectPathname, apxid, role) => { + const itmf = `${objectPathname}/itm/${apxid}.json`; + if (!fs.existsSync(itmf)) { + return { + status: 404, + ref: "Odmdb", + msg: "persondoesnotexist", + data: { person: apxid }, + }; + } + const getschema = Odmdb.Schema(objectPathname, true); + if (getschema.status != 200) return getschema; + const itm = fs.readJsonSync(itmf); + if (itm.owner && itm.owner == role.xalias) { + role.xprofils.push("owner"); + } + const accessright = Odmdb.accessright( + getschema.data.schema.apxaccessrights, + role + ); + if (!accessright.R) { + return { + status: 403, + ref: "Odmdb", + msg: "forbidden", + data: { person: apxid }, + }; + } + const data = {}; + accessright.R.forEach((p) => { + data[p] = itm[p]; + }); + return { status: 200, ref: "Odmdb", msg: "found", data }; +}; + +/** + * To get an array of item (itm) per primarykey with a list of field + * Object are store in objectPath/objectName/conf.json contain + * + * @objectPathname where object are store (where /object/conf.json indicate where the schema is) + * @uuidprimarykeyList list of uuid requested + * @role {xalias,xprofiles} allow to get accessright come from header + * @propertiesfilter (optionnal) key to return for each object (if undefined then return all) + * @Return {status:200; data:{uuid:{data filter by @propertiesfilter},uuid:"notfound"}} + +ASUP doit être gerer au niveau des view des index + + +*/ +Odmdb.ASUPreads = (objectPathname, apxidlist, role, propertiesfilter) => { + const res = { status: 200, data: {} }; + const getschema = Odmdb.Schema(objectPathname, true); + if (getschema.status != 200) return getschema; + // Test if readable at least if owner + role.xprofils.push("owner"); + const accessright = (Odmdb.accessright = + (getschema.data.schema.apxaccessright, role.xprofils)); + if (!accessright.R) { + return { + status: 403, + ref: "Odmdb", + msg: "accessforbidden", + data: { crud: "R", accessright }, + }; + } + apxidlist.forEach((id) => { + if (fs.existsSync(`${objectPathname}/itm/${id}.json`)) { + const objectdata = fs.readJsonSync(`${objectPathname}/itm/${id}.json`); + if (objectdata.owner && objectdata.owner == role.xalias) { + if (!role.xprofils.includes("owner")) role.xprofils.push("owner"); + } else { + if (role.xprofils.includes("owner")) + role.xprofils = role.xprofils.filter((e) => e !== "owner"); + } + const accessright = Odmdb.accessright( + getschema.data.schema.apxaccessright, + role + ); + if (!accessright.R) { + res.data[id] = "forbiden"; + } else { + let newpropertiesfilter = Object.keys(objectdata); + if (accessright.R.length > 0) { + const setaccess = new Set(accessright.R); + if (!propertiesfilter) propertiesfilter = Object.keys(objectdata); + newpropertiesfilter = propertiesfilter.filter((f) => + setaccess.has(f) + ); + } + const objinfo = {}; + newpropertiesfilter.forEach((k) => { + if (objectdata[k]) objinfo[k] = objectdata[k]; + }); + res.data[id] = objinfo; + } + } else { + res.data[id] = "notfound"; + } + }); + return res; +}; +/** + * Convert profils in accessright + * @param {*} apxaccessright from schema object {profilname:{R}} + * @param {*} role {xprofils,xalias} accessible after isAuthenticated + * @returns access right to C create if present, to read (properties list or all if empty), to Update properties list or all if empty, D elete + * example: {"C":[],"R":[properties list],"U":[properties ist],"D":[]} + */ +Odmdb.accessright = (apxaccessrights, role) => { + //console.log("apxaccessrights",apxaccessrights) + //console.log("role",role) + const accessright = {}; + role.xprofils.forEach((p) => { + if (apxaccessrights[p]) { + Object.keys(apxaccessrights[p]).forEach((act) => { + if (!accessright[act]) { + accessright[act] = apxaccessrights[p][act]; + } else { + accessright[act] = [ + ...new Set([...accessright[act], ...apxaccessrights[p][act]]), + ]; + } + //console.log(act,accessright[act]) + }); + } + }); + return accessright; +}; +/** + * CUD a data itm into objectPathname if checkJson is valid + * and update idx + * idx is upto date for unique properties but not for list + * @param {string} objectpathname folder name where object are stored + * @param {object} itm an object respecting the checkJson schema in objectPathname/conf.json + * @param {string} crud: C reate U pdate D elete + * @param {array} role {xprofils,xalias} xprofils list of profils like anonymous,pagans, person owner is deuce if object properties owner is alias + + * */ + +Odmdb.cud = (objectPathname, crud, itm, role, runindex = true) => { + const getschema = Odmdb.Schema(objectPathname, true); + if (getschema.status != 200) return getschema; + + if (!itm[getschema.data.schema.apxid]) { + return { + status: 406, + ref: "Odmdb", + msg: "apxidmissing", + data: { missingkey: getschema.data.schema.apxid }, + }; + } + fs.ensureDirSync(`${objectPathname}/itm/`); + fs.ensureDirSync(`${objectPathname}/idx/`); + const existid = fs.existsSync( + `${objectPathname}/itm/${itm[getschema.data.schema.apxid]}.json` + ); + + /*const pathindex = `${objectPathname}/idx/lst_${getschema.data.schema.apxid}.json`; + if (!fs.existsSync(pathindex)) { + fs.outputJSONSync(pathindex, []); + fs.ensureDirSync(`${objectPathname}/itm/`); + } + const existid = fs + .readJSONSync(pathindex) + .includes(itm[getschema.data.schema.apxid]); + */ + if (existid && crud == "C") { + return { + status: 406, + ref: "Odmdb", + msg: "alreadyexist", + data: { + objectname: path.basename(objectPathname), + key: getschema.data.schema.apxid, + val: itm[getschema.data.schema.apxid], + }, + }; + } + if (!existid && ["U", "D"].includes(crud)) { + return { + status: 406, + ref: "Odmdb", + msg: "doesnotexist", + data: { + objectname: path.basename(objectPathname), + key: getschema.data.schema.apxid, + val: itm[getschema.data.schema.apxid], + }, + }; + } + const itmold = existid + ? fs.readJSONSync( + `${objectPathname}/itm/${itm[getschema.data.schema.apxid]}.json` + ) + : {}; + if (existid && itmold.owner && itmold.owner == role.xalias) { + role.xprofils.push("owner"); + } + if (!existid && crud == "C" && !itm.owner) { + // set owner cause this is a Create + itm.owner = role.xalias; + role.xprofils.push("owner"); + } + //get accessright {C:[],R:[],U:[],D:[]} if exist means authorize, if array contain properties (for R and U) right is only allowed on properties + const accessright = Odmdb.accessright( + getschema.data.schema.apxaccessrights, + role + ); + console.log("accessright", accessright); + if ( + (crud == "C" && !accessright.C) || + (crud == "D" && !accessright.D) || + (crud == "U" && !accessright.U) + ) { + return { + status: 403, + ref: "Odmdb", + msg: "accessforbidden", + data: { crud, accessright }, + }; + } + //delete or save + if (crud == "D") { + itmold["dt_delete"] = dayjs().toISOString(); + fs.outputJSONSync( + `${objectPathname}/delitm/${itmold[getschema.data.schema.apxid]}.json`, + itmold + ); + fs.rmSync( + `${objectPathname}/itm/${itmold[getschema.data.schema.apxid]}.json` + ); + } else { + // if Create Update erase old version + let itmtostore = itm; + if (crud == "U" && accessright.U.length > 0) { + itmtostore = itmold; + accessright.U.forEach((p) => { + itmtostore[p] = itm[p]; + }); + itmtostore.dt_update = dayjs().toISOString(); + } + if (crud == "C") itmtostore.dt_create = dayjs().toISOString(); + // check consistency of datatostore + const chkdata = Checkjson.schema.data( + getschema.data.schema, + itmtostore, + false + ); + if (chkdata.status != 200) return chkdata; + if (!getschema.data.schema.apxuniquekey) + getschema.data.schema.apxuniquekey = []; + console.log(`${objectPathname}/itm/${chkdata.data.apxid}.json`); + console.log(chkdata.data.itm); + fs.outputJSONSync( + `${objectPathname}/itm/${chkdata.data.apxid}.json`, + chkdata.data.itm + ); + } + //console.log("getschema", getschema); + //rebuild index if requested + console.log("runidx", runindex); + console.log(objectPathname); + if (runindex) Odmdb.runidx(objectPathname, getschema.data.schema); + getschema.data.conf.lastupdatedata = dayjs().toISOString(); + fs.outputJSONSync(`${objectPathname}/conf.json`, getschema.data.conf); + return { + status: 200, + ref: "Odmdb", + msg: "cudsuccessfull", + data: {}, + }; +}; +/** + * apxidx:[list of index file + * { name: string, // filename in /idx/name.json + * type: array (list of unique value found in keyval), example: [val1, val2, val3,....] eventualy filter + * view (only unique key and object with objkey eventually filter by filter ) example: {idalias:{itm}} + * distribution (=> {val of keyval:[apxid itm value]} with ) + * keyval:string, // a propertie of type string or array + * objkeys:[] and array of properties if empty [] means all properties, if 1 element => an array of this properties + * filter : an eval function base on itm data (itm.key) that return true (return data item) or false (ignore item) + * + */ +Odmdb.runidx = (objectPathname, schema) => { + console.log(`idx for ${objectPathname}`); + if (!schema || !schema.apxid) { + const getschema = Odmdb.Schema(objectPathname, true); + if (getschema.status != 200) return getschema; + schema = getschema.data.schema; + } + const ventil = {}; + schema.apxidx.forEach((id) => { + ventil[id.name] = id; + if (id.keyval.includes(".")) { + // Means keyval is an object that can be only string for an idx + ventil[id.name].isobject = true; + ventil[id.name].isunique = false; + ventil[id.name].nbobjkey = 0; + ventil[id.name].keyvaltype = "string"; + ventil[id.name].filter = id.filter ? id.filter.replace(/;/g, "") : ""; // check integrity of string + ventil[id.name].data = {}; + } else { + ventil[id.name].isunique = schema.apxuniquekey.includes(id.keyval); + ventil[id.name].nbobjkey = id.objkey ? id.objkey.length : 0; + ventil[id.name].keyvaltype = schema.properties[id.keyval].type; + ventil[id.name].filter = id.filter ? id.filter.replace(/;/g, "") : ""; // check integrity of string + ventil[id.name].data = ventil[id.name].type == "array" ? [] : {}; + } + }); + glob.sync(`${objectPathname}/itm/*.json`).forEach((i) => { + const itm = fs.readJSONSync(i); + Object.keys(ventil).forEach((n) => { + let keep = true; + if (ventil[n].filter != "") { + try { + keep = eval(ventil[n].filter); + } catch (err) { + keep = false; + } + } + if (keep && ventil[n].type == "array" && itm[ventil[n].keyval]) { + if (ventil[n].keyvaltype == "array") { + itm[ventil[n].keyval].forEach((v) => { + if (!ventil[n].data.includes(v)) ventil[n].data.push(v); + }); + } else { + if (!ventil[n].data.includes(itm[ventil[n].keyval])) + ventil[n].data.push(itm[ventil[n].keyval]); + } + } + if ( + keep && + ventil[n].type == "view" && + ventil[n].isunique && + itm[ventil[n].keyval] + ) { + if (ventil[n].nbobjkey == 0) + ventil[n].data[itm[ventil[n].keyval]] = itm; + if (ventil[n].nbobjkey == 1) + ventil[n].data[itm[ventil[n].keyval]] = itm[ventil[n].objkey[0]]; + if (ventil[n].nbobjkey > 1) { + const objdata = {}; + Object.keys(itm).forEach((k) => { + if (ventil[n].objkey.includes(k)) objdata[k] = itm[k]; + }); + ventil[n].data[itm[ventil[n].keyval]] = objdata; + } + } + if (keep && ventil[n].type == "distribution" && itm[ventil[n].keyval]) { + const listval = + ventil[n].keyvaltype == "string" + ? [itm[ventil[n].keyval]] + : itm[ventil[n].keyval]; + // itm[ventil[n].keyval] is an array + listval.forEach((val) => { + if (!ventil[n].data[val]) ventil[n].data[val] = []; + ventil[n].data[val].push(itm[schema.apxid]); + }); + } + if (keep && ventil[n].type == "distribution" && ventil[n].isobject && itm[ventil[n].keyval.split('.')[0]]) { + let itmval = JSON.parse(JSON.stringify(itm)); + console.log( ventil[n].keyval) + console.log(itmval) + ventil[n].keyval + .split(".") + .forEach((i) => itmval = itmval[i] ? itmval[i] : null); + if (itmval) { + if (!ventil[n].data[itmval]) ventil[n].data[itmval] = []; + ventil[n].data[itmval].push(itm[schema.apxid]); + } + } + }); + }); + Object.keys(ventil).forEach((n) => { + //console.log(`${objectPathname}/idx/${ventil[n].name}.json`) + fs.outputJSON( + `${objectPathname}/idx/${ventil[n].name}.json`, + ventil[n].data + ); + }); +}; + +/** + * create/update idx from itm(s) + * + * @param {string} objectPathname + * @param {object} itm item to Create or to Update or {} if crud == I or crud == D + * @param {object} itmold (current item) if crud == U or D to get previous itm before change or {} if crud==I or C + * @param {letter} crud CUDI C add, U update, D delete I reindex + * @param {array} idx if specific request to rebuild list of idx only if [] then use schema one + * @param {object} schema if empty it use schema from Odmdb.Schema().data.schema + * + * example create alias 12 name fred: + * Odmdb.idxfromitm('.../tribes/ndda/persons',"C",{alias:'12',name:"fred"},{},[], {person schema}) + * example update alias 12 in name freddy: + * Odmdb.idxfromitm('.../tribes/ndda/persons',"U",{alias:'12',name:"freddy"},{alias:'12',name:"fred"},[], {person schema}) + * example delete alias 12: + * Odmdb.idxfromitm('.../tribes/ndda/persons',"D",{},{alias:'12',name:"fred"},[], {person schema}) + * example to rebuild all index from scratch + * Odmdb.idxfromitm('.../tribes/ndda/persons',"I",{},{},[], {person schema}) + * example to rebuild only publickey_alias index from scratch + * Odmdb.idxfromitm('.../tribes/ndda/pagans',"I",{},{},[{ name:"publickey_alias",keyval:"publickey",objkey:["alias"]}], {pagans schema}) + * + */ +Odmdb.ASUPidxfromitm = ( + objectPathname, + crud, + itm, + itmold, + idxs = [], + schema +) => { + console.log(`idxfromitem for ${objectPathname} action:${crud}`); + if (!schema || !schema.apxid) { + const getschema = Odmdb.Schema(objectPathname, true); + if (getschema.status != 200) return getschema; + schema = getschema.data.schema; + } + console.log(schema.apxuniquekey); + const itms = crud == "I" ? glob.sync(`${objectPathname}/itm/*.json`) : [itm]; + console.log(itms); + if (crud == "I") { + //reinit all idx + idxs.forEach((idx) => { + fs.remove(`${objectPathname}/idx/${idx.name}.json`); + }); + } + let idxtoreindex = []; //store index that has to be reprocessto get full context + idxs = idxs.length == 0 ? schema.apxidx : idxs; // get all index if none + itms.forEach((i) => { + if (crud == "I") { + itm = fs.readJSONSync(i); + } + //console.log(itm); + idxs.forEach((idx) => { + const keyvalisunique = schema.apxuniquekey.includes(idx.keyval); // check if keyval is unique mean store as an object (or string) else store as an array + const idxsrc = `${objectPathname}/idx/${idx.name}.json`; + const idxinit = idx.name.substring(0, 4) == "lst_" ? [] : {}; // select type of idx (array or object) + let idxfile = !fs.existsSync(idxsrc) ? idxinit : fs.readJSONSync(idxsrc); + if (idx.name.substring(0, 4) == "lst_") { + if (["D", "U"].includes(crud)) { + if (keyvalisunique) { + idxfile = idxfile.filter((e) => e !== itmold[idx.keyval]); + } else { + idxtoreindex.push(idx); //@todo + } + } + console.log(idx.keyval); + console.log(itm[idx.keyval]); + + if ( + ["C", "U", "I"].includes(crud) && + !idxfile.includes(itm[idx.keyval]) + ) { + idxfile.push(itm[idx.keyval]); + } + } else { + if (!idx.objkey) { + //mean all properties + idx.objkey = Object.keys(schema.properties); + } + if (keyvalisunique && idx.objkey.length == 1) { + if (["D", "U"].includes(crud)) { + delete idxfile[itmold[idx.keyval]]; + } else { + idxfile[itm[idx.keyval]] = itm[idx.objkey[0]]; + } + } + if (keyvalisunique && idx.objkey.length > 1) { + if (["D", "U"].includes(crud)) { + delete idxfile[itmold[idx.keyval]]; + } else { + const itmfilter = {}; + idx.objkey.forEach((i) => { + if (itm[i]) itmfilter[i] = itm[i]; + }); + idxfile[itm[idx.keyval]] = itmfilter; + } + } + if (!keyvalisunique && idx.objkey.length == 1) { + if ( + ["D", "U"].includes(crud) && + idxfile[itmold[idx.keyval]].IndexOf(itmold[idx.objkey[0]]) > -1 + ) { + // U because need to remove previous value before adding it + idxfile[itmold[idx.keyval]].splice( + idxfile[itmold[idx.keyval]].IndexOf(itmold[idx.objkey[0]]), + 1 + ); + } + if (["C", "U", "I"].includes(crud)) { + if (!idxfile[itm[idx.keyval]]) idxfile[itm[idx.keyval]] = []; + if (!idxfile[itm[idx.keyval]].includes(itm[idx.objkey[0]])) { + idxfile[itm[idx.keyval]].push(itm[idx.objkey[0]]); + } + } + } + if (!keyvalisunique && idx.objkey.length > 1) { + if (["D", "U"].includes(crud) && idxfile[itmold[idx.keyval]]) { + // U because need to remove previous value before adding it + let arrayofit = []; + idxfile[itmold[idx.keyval]].forEach((it) => { + if (it[schema.apxid] != itm[schema.apxid]) arrayofit.push(it); + }); + idxfile[itmold[idx.keyval]] = arrayofit; + } + if (["C", "U", "I"].includes(crud)) { + const itmfilter = {}; + idx.objkey.forEach((i) => { + if (itm[i]) itmfilter[i] = itm[i]; + }); + if (!idxfile[itm[idx.keyval]]) idxfile[itm[idx.keyval]] = []; + idxfile[itm[idx.keyval]].push(itmfilter); + } + } + } + fs.outputJSONSync(idxsrc, idxfile); + }); + }); + if (crud != "I") { + //update lastupdatedata to inform something change + const confschema = fs.readJSONSync(`${objectPathname}/conf.json`); + confschema.lastupdatedata = dayjs().toISOString(); + fs.outputJSONSync(`${objectPathname}/conf.json`, confschema); + } + return { status: 200, ref: "Odmdb", msg: "successreindex", data: {} }; +}; +Odmdb.updatefromidxall = (objectname, idxname, data, lastupdate) => { + /** + * Update all itm of objectname from index idx/idxname with data + * if itm exist in local and not in data then /ojectname/conf.json.lastupdate = now + * not then /ojectname/conf.json.lastupdate = lastupdate + * this way mean next time server A want to refresh from B its lastupdate < than on A + */ + let conflastupdate = 0; + let localidx = {}; + if (fs.existsSync(`../nationchains/${objectname}/idx/${idxname}`)) { + localidx = fs.readJsonSync(`../nationchains/${objectname}/idx/${idxname}`); + } + Object.keys(data).forEach((id) => { + if (localidx[id]) { + if ( + localidx[id].dt_update && + data[id].dt_update && + localidx[id].dt_update > data[id].dt_update + ) { + // means local information is fresher than the one in data for replacement + // .toISIString ex: 2019-01-25T02:00:00.000Z' + conflastupdate = dayjs().toISOString(); + } else { + // replace itm with data + localidx[id] = data[id]; + fs.outputJsonSync( + `../nationchains/${objectname}/itm/${id}.json`, + data[id] + ); + } + } else { + // add itm + localidx[id] = data[id]; + fs.outputJsonSync( + `../nationchains/${objectname}/itm/${id}.json`, + data[id] + ); + } + }); + //check if it miss id in fresher update means conf.lastupdate will be now to indicate + Object.keys(localidx).forEach((id) => { + if (!data[id]) { + conflastupdate = dayjs().toISOString(); + } + }); + // update the object files + if (conflastupdate == 0) conflastupdate = lastupdate; + fs.outputJSONSync(`../nationchains/${objectname}/idx/${idxname}`, localidx); + const objconf = fs.readJsonSync(`../nationchains/${objectname}/conf.json`); + objconf.lastupdate = conflastupdate; + fs.outputJsonSync(`../nationchains/${objectname}/conf.json`, objconf); + return { + status: 200, + ref: "Odmdb.js", + info: "Successfullupdate", + data: { objectname, idxname, lastupdate }, + }; +}; +module.exports = Odmdb; diff --git a/models/Pagans.js b/models/Pagans.js new file mode 100644 index 0000000..93084de --- /dev/null +++ b/models/Pagans.js @@ -0,0 +1,187 @@ +const glob = require("glob"); +const path = require("path"); +const dayjs = require("dayjs"); +const fs = require("fs-extra"); +const axios = require("axios"); +const Mustache = require('mustache'); +const openpgp = require("openpgp"); +const Notifications = require("./Notifications.js"); +const Odmdb = require("./Odmdb.js"); + +const conf = require(`../../conf/townconf.json`); + +/** + * Pagan Management numeric Identity and Person (Person = Pagan Id + tribe) + * + * + * + */ +const Pagans = {}; + +/** + * Remove authentification token after a logout + * @param {string} alias + * @param {string} tribe + * @param {integer} xdays + * @param {string} xhash + * @returns {status:200, ref:"Pagans",msg:"logout"} + * tmpfs name file has to be on line with the tmpfs create by isAuthenticated + * tmpfs contain profils name for a tribe/ + */ +Pagans.logout = (alias, tribe, xdays, xhash) => { + //console.log(alias, tribe, xdays, xhash); + // inline with middleware isAuthenticated.js + let tmpfs = `../tmp/tokens/${alias}_${tribe}_${xdays}`; + //max filename in ext4: 255 characters + tmpfs += `_${xhash.substring(150, 150 + tmpfs.length - 249)}.json`; + fs.remove(tmpfs); + console.log('logout token',tmpfs) + return { status: 200, ref: "Pagans", msg: "logout" }; +}; + +/** + * @param {string} alias a alias that exist or not + * @return {object} { status: 200, ref:"pagans",msg:"aliasexist",data: { alias, publicKey } } + * { status: 404, ref:"pagans",msg:"aliasdoesnotexist",data: { alias} } + * + **/ +Pagans.getalias = (alias) => { + //bypass Odmdb cause all is public save ressources + if (fs.existsSync(`../nationchains/pagans/itm/${alias}.json`)) { + return { + status: 200, + ref: "Pagans", + msg: "aliasexist", + data: fs.readJSONSync( + `../nationchains/pagans/itm/${alias}.json` + ), + }; + } else { + return { + status: 404, + ref: "Pagans", + msg: "aliasdoesnotexist", + data: { alias }, + }; + } +}; + + +/** + * Send email with alias's keys to email or person alias person.recovery.email + * + * If email or pubkey is undefined then get data from tribe/person(alias) + * Send email with keys + * @param {object} data + * @param {string} data.alias + * @param {pgpPrivate} [data.privatekey] + * @param {string} [data.passphrase] + * @param {string} data.tribe + * @param {pgpPublic} [data.publickey] + * @param {string} [data.email] + * @param {string} data.lg + */ +Pagans.sendmailkey = (data) => { + + console.log( + data.alias, + "-", + data.privatekey.substring(0,10), + "-", + data.tribe, + "-", + data.passphrase, + "-", + data.publickey.substring(0,10), + "-", + data.email + ); + const person = { alias:data.alias, privatekey:data.privatekey, tribe:data.tribe }; + + if (!data.publickey || !data.email || !data.privatekey) { + const personfile = `../../nationchains/tribes/${data.tribe}/objects/persons/itm/${data.alias}.json`; + if (!fs.existsSync(personfile)) { + return { + status: 404, + ref: "Pagans", + msg: "persondoesnotexist", + data: { alias:data.alias, tribe:data.tribe }, + }; + } + const persondata= fs.readJsonSync(personfile); + person.email = persondata.recoveryauth.email; + person.publickey = persondata.recoveryauth.publickey; + person.privatekey = persondata.recoveryauth.privatekey; + person.passphrase = persondata.recoveryauth.passphrase; + } else { + person.email = data.email; + person.passphrase = data.passphrase; + person.publickey = data.publickey; + } + person.avecpassphrase=(person.passphrase!="") + console.log("person:", person); + + const tplemail = require(`../../nationchains/tribes/adminapi/template/createidentity_${lg}.js`) + + person.message + const maildata = { + to: person.email, + subject: Mustache.render(tplemail.subject, person), + html: Mustache.render(tplemail.html, person), + text: Mustache.render(tplemail.text, person), + attachments: [ + { + filename:`${person.alias}_publickey.txt`, + content: person.publickey, + contentType:"text/plain" + }, + { + filename:`${person.alias}_privatekey.txt`, + content: person.privatekey, + contentType:"text/plain" + } + ] + }; + return Notifications.sendmail(maildata, data.tribe); +}; + +Pagans.authenticatedetachedSignature = async ( + alias, + pubK, + detachedSignature, + message +) => { + /** + * Check that a message was signed with a privateKey from a publicKey + * This is not necessary if isAuthenticated, but can be usefull to double check + * @TODO finish it and implement it also in /apxpagan.js for browser + * @alias {string} alias link to the publicKey + * @pubK {string} publiKey text format + * @detachedSignature {string} a detachedsignatured get from apx.detachedSignature + * @message {string} the message signed + * @return {boolean} true the message was signed by alias + * false the message was not signed by alias + */ + const publicKey = await openpgp.readKey({ armoredKey: pubK }); + const msg = await openpgp.createMessage({ text: message }); + const signature = await openpgp.readSignature({ + armoredSignature: detachedSignature, // parse detached signature + }); + const verificationResult = await openpgp.verify({ + msg, // Message object + signature, + verificationKeys: publicKey, + }); + const { verified, keyID } = verificationResult.signatures[0]; + try { + await verified; // throws on invalid signature + console.log("Signed by key id " + keyID.toHex()); + return KeyId.toHex().alias == alias; + } catch (e) { + console.log("Signature could not be verified: " + e.message); + return false; + } +}; + + +module.exports = Pagans; diff --git a/models/Toolsbox.js b/models/Toolsbox.js new file mode 100755 index 0000000..fe2800e --- /dev/null +++ b/models/Toolsbox.js @@ -0,0 +1,646 @@ +/* eslint-disable no-useless-escape */ +const fs = require("fs"); +const path = require("path"); +const bcrypt = require("bcrypt"); +const moment = require("moment"); +const utils = {}; + +console.log( + "Check in /utils/index.js to find usefull function for your dev.\n Feel free to send suggestion, code to maintainer of apxtri project (see /package.json to get email).\n We'll add to the roadmap to add it." +); + +/** + * EMAIL + */ +/* const validateEmail = email => { + const regExp = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; + return regExp.test(email); +}; + +const validatePassword = pwd => { + const regExp = new RegExp( + /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[$@$!%*?&.])[A-Za-z\d$@$!%*?&.{}:|\s]{8,}/ + ); + return regExp.test(pwd); +}; + +const filterInvalidInArray = (array, validate) => + array ? array.filter(el => !validate(el)) : undefined; // return undefined when every elements is valid + +/** + * POSTAL CODE + */ +/* +const validatePostalCode = postalCode => + /(^\d{5}$)|(^\d{5}-\d{4}$)/.test(postalCode); + +/** + * PHONE + */ +/* const validatePhoneNumber = phoneNumber => + /((^0[1-9]|\+[0-9]{3})([-. ]?[0-9]{2}){4}$)/.test(phoneNumber); + +const correctPhoneNumber = phone => + phone[0] === '0' ? '+33' + phone.substr(1) : phone; + +const Checkjson = (appProfil, referential, data) => { + // @TODO get a referentiel per object then check data validity and allowed access + // need to add referentiel manager + const invalidefor = []; + let updateDatabase = false; + Object.keys(data).forEach(field => { + switch (field) { + case 'token': + updateDatabase = true; + break; + case 'email': + if (!validateEmail(data.email)) { + invalidefor.push('ERREMAIL:' + field); + } else { + updateDatabase = true; + } + break; + case 'password': + if (!validatePassword(data.password)) { + invalidefor.push('ERRPWD:' + field); + } else { + data.password = bcrypt.hash(data.password, config.saltRounds); + updateDatabase = true; + } + break; + } + }); + return { invalidefor, data, updateDatabase }; +}; +*/ +//Permet d'attendre en milliseconde +// s'utilise avec async ()=>{ +// await sleep(2000) +//} +utils.sleep = (ms) => { + return new Promise((resolve) => setTimeout(resolve, ms)); +}; +utils.normalize = {}; +utils.normalize.telephonefr = (phone) => { + phone = phone.trim().replace(/[- .]/g, ""); + if ( + Checkjson.schema.properties.format.telephoenfr(phone) && + phone.length == 10 && + phone[0] == "0" + ) { + phone = "+33 " + phone.substring(1); + } + return phone; +}; +utils.normalize.zfill10 = (num) => { + let s = num + ""; + while (s.length < 10) s = "0" + s; + return s; +}; +utils.generemdp = (nbpos, fromchar) => { + if (!fromchar) { + const fromchar = "ABCDEFGHIJKLMNPQRSTUVWZY123456789"; + } + //const chaine = "ABCDEFGHIJKLMNPQRSTUVWZY123456789"; + let mdp = ""; + for (var i = 0; i < nbpos; i++) { + var pos = Math.floor(Math.random() * fromchar.length); + mdp += fromchar.substring(pos, pos + 1); + } + return mdp; +}; +utils.generecompteur = (filecpt, typeincrement) => { + let file = `${filecpt}/${typeincrement}.json`; + let prefix = ""; + if ((typeincrement = "ANNEESEMAINE")) { + file = `${filecpt}/${typeincrement}${moment().format( + "YYYY" + )}${moment().format("WW")}.json`; + prefix = `${moment().format("YYYY")}${moment().format("WW")}`; + } + let num = 1; + try { + num = parseInt(fs.readFileSync(file, "utf8")) + 1; + } catch (err) { + console.log("Nouveau compteur incrementale ", file); + } + fs.writeFileSync(file, num, "utf8"); + return prefix + num; +}; +/** + * CSV + */ +utils.json2csv = (jsondata, options, callback) => { + // uniquement json = [{niv1:val,niv1:[liste of val]}] + // console.log('_________________________'); + // console.log(jsondata) + // console.log('_________________________'); + if (jsondata.length == 0) { + return callback("Empty json", null); + } + if (!options.retln) options.retln = "\n"; + if (!options.sep) options.sep = ";"; + if (!options.arraysplitsep) options.arraysplitsep = ","; + if (!options.replacespecialcarJson2Csv) { + options.replacespecialcarJson2Csv = []; + } else { + if (typeof options.replacespecialcarJson2Csv == "string") { + //permet de passer des regex en string + options.replacespecialcarJson2Csv = eval( + options.replacespecialcarJson2Csv + ); + } + } + let etat = ""; + let csv = ""; + let entete = ""; + let prem = true; + for (const j in jsondata) { + // console.log(jsondata[j]) + for (const c in options.champs) { + if (prem) { + entete += options.champs[c] + options.sep; + } + if (jsondata[j][options.champs[c]]) { + if (options.array.indexOf(options.champs[c]) > -1) { + csv += + jsondata[j][options.champs[c]].join(options.arraysplitsep) + + options.sep; + } else { + let currentValue = ""; + if (jsondata[j][options.champs[c]]) + currentValue += jsondata[j][options.champs[c]]; + options.replacespecialcarJson2Csv.forEach((re) => { + //console.log(currentValue) + currentValue = currentValue.replace(re[1], re[0]); + }); + csv += currentValue + options.sep; + } + } else { + csv += options.sep; + } + } + csv = csv.substring(0, csv.length - 1) + options.retln; + if (prem) { + prem = false; + entete = entete.substring(0, entete.length - 1) + options.retln; + // console.log(entete) + } + } + // return entete + csv; + if (etat == "") { + return callback(null, entete + csv); + } else { + return callback(etat, null); + } +}; +/** + * Get headers from first line of CSV + * @param {array} lines array of string which contains each csv lines + * @return {array} string array of headers + */ +utils.getHeaders = (lines, sep) => + lines[0].split(sep).map((i) => i.replace(/"/g, "")); +/** + * [csv2json description] + * @param {object} csv object of csv file that has been read + * @param {object} options object containing csv options, headers, ... + {retln:'code de retour de ligne \n ou \n\r', + sep:'code to split cells', + champs:[ch1,ch2,...] catch only those field, + array:[ch1, ] can have more than one field champs with same name then data are push into an array } + * @param {Function} callback callback function + * @return {callback} - return an error if error, else return json + it convert a csv file into a json = [{field:value}] + +Usage example: +fiche.csv2article = (err, fiche) => { + if (!err) { + console.log(fiche) + } +} +utils.csv2json(fs.readFileSync('./devdata/tribee/aubergenville/infoexterne/localbusiness.csv', 'utf-8'), { + retln: "\n", + sep: ";", + champs: ["NOM", "OBJET", "ADRESSE_PRO", "CP_PRO", "VILLE_PRO", "ZONE", "PHONE_PRO", "HORAIRESDESC", "HORAIREDATA", "URL", "FACEBOOK", "INSTA", "EMAIL_PRO", "IMG", "TAG"], + array: ["TAG", "PHONE_PRO", "EMAIL_PRO"] +}, fiche.csv2article) + + */ +utils.replacecarbtweendblquote = (csv, car, carremplacant) => { + /* + return csv text with any car betwenn 2 " by CARSEPARATOR + */ + let newcsv = ""; + let txtencours = ""; + let flagouvert = false; + const sepreg = new RegExp(`${car}`, "gmi"); + for (let j = 0; j < csv.length; j++) { + //if((csv[j] == "\"" && csv[j + 1] && csv[j + 1] != "\"") || (csv[j] == "\"" && csv[j - 1] && csv[j - 1] != "\"") || (csv[j] == "\"" && csv[j - 1] && csv[j - 2] && csv[j - 1] != "\"" && csv[j - 2] != "\"")) { + if (csv[j] == '"') { + if (flagouvert) { + // on cherche à ferme une chaine de texte + if (csv[j + 1] == '"') { + //on a "" consecutif qu'on remplace par "" et on fait j+1 + txtencours += '""'; + j++; + } else { + // on a bien une fermeture + flagouvert = false; + newcsv += txtencours.replace(sepreg, carremplacant); + txtencours = '"'; + } + } else { + // on ouvre une chaine + flagouvert = true; + //on met le contenu précédent ds newcsv + newcsv += txtencours; + txtencours = '"'; + } + //} else if((csv[j] !== "\n") && (csv[j + 1] && csv[j] + csv[j + 1] !== "\n\r")) { + } else if (csv[j] !== "\n") { + txtencours += csv[j]; + // } else if((csv[j] == "\n") || (csv[j + 1] && csv[j] + csv[j + 1] == "\n\r")) { + } else if (csv[j] == "\n") { + if (!flagouvert) txtencours += "\n"; + } + } + return newcsv + txtencours; +}; +utils.analysestring = (string) => { + let buftxt = ""; + let bufcode = ""; + let i = 0; + let avecRL = false; + for (let p = 0; p < string.length; p++) { + if (string[p].charCodeAt() == 10) { + buftxt += "[RL]"; + avecRL = true; + } else { + buftxt += string[p]; + } + bufcode += "-" + string[p].charCodeAt(); + if (i == 20) { + if (avecRL) { + console.log(`${buftxt} - ${bufcode}`); + } else { + console.log(`${buftxt} ---- ${bufcode}`); + } + i = 0; + buftxt = ""; + bufcode = ""; + avecRL = false; + } + i++; + } +}; + +const txtstring = `32932,BK_F2F_B_COM_10x1H-09,"My Communication Workshop ""Session N°9 - 1H""","

 

+ + + + + + +
+

Learner who needs to develop their ability to communicate effectively at work, both in writing and speaking

+
",,english,2,0,,2,0,classroom,"0000-00-00 00:00:00","0000-00-00 00:00:00",0000-00-00,0000-00-00,https://www.yesnyoulearning.com/lms/index.php?r=player&course_id=32932,1101,,"BUSINESS KEYS",0, +32933,BK_F2F_B_COM_10x1H-10,"My Communication Workshop Session N°10 - 1H","

 

+ + + + + + +
+

Learner who needs to develop their ability to communicate effectively at work, both in writing and speaking

+
",,english,2,0,,2,0,classroom,"0000-00-00 00:00:00","0000-00-00 00:00:00",0000-00-00,0000-00-00,https://www.yesnyoulearning.com/lms/index.php?r=player&course_id=32933,1101,,"BUSINESS KEYS",0, +32934,BK_F2F_B_JOB_10x1H-01,"My Job Search Workshop Session N°1 - 1H","

PACK JOB SEARCH

",,english,2,0,,2,0,classroom,,,0000-00-00,0000-00-00,https://www.yesnyoulearning.com/lms/index.php?r=player&course_id=32934,1108,,,0, +32935,BK_F2F_B_JOB_10x1H-02,"My Job Search Workshop Session N°2 - 1H","

PACK JOB SEARCH

",,english,2,0,,2,0,classroom,,,0000-00-00,0000-00-00,https://www.yesnyoulearning.com/lms/index.php?r=player&course_id=32935,1108,,,0,`; +//utils.analysestring(txtstring) +//console.log(utils.replacecarbtweendblquote(txtstring, ",", 'CARSEPARATOR') +// .split("\n")[0].split(",")) +utils.csv2json = (csv, options, callback) => { + // EN CAS DE PB AVEC UN FICHIER EXCEL RECALCITRANT + // l'ouvrir dans calc linux et sauvegarder csv utf8, ; , " enregistrer le contenu de la cellule comme affiché + console.log("\n--------------- CSV2JSON ---------------\n"); + // Default CSV options + if (!options.retln) options.retln = "\n"; + if (csv.indexOf("\n\r") > -1) options.retln = "\n\r"; + if (!options.sep) options.sep = ";"; + //gestion d un separateur dans une chaine de texte + //const regseptext = new RegExp(`${options.sep}(?!(?:[^"]*"[^"]*")*[^"]*$)`, 'gm'); + //csv = csv.replace(regseptext, "CARACSEPAR"); + // csv = utils.replacecarbtweendblquote(csv, options.retln, "RETLIGNE") + csv = utils.replacecarbtweendblquote(csv, options.sep, "CARSEPARATOR"); + if (!options.replacespecialcarCsv2Json) { + options.replacespecialcarCsv2Json = []; + } else { + if (typeof options.replacespecialcarCsv2Json == "string") { + //permet de passer des regex en string + options.replacespecialcarCsv2Json = eval( + options.replacespecialcarCsv2Json + ); + } + } + const result = []; + const lines = csv.split(options.retln); + const headers = utils.getHeaders(lines, options.sep); + let unknownHeaders = ""; + //console.log('headers', headers) + //console.log('options.champs', options.champs) + headers.forEach((header) => { + // Si un header n'est pas présent dans la liste des champs prédéfinis + // on l'ajoute aux champs inconnus + if (options.champs.indexOf(header) === -1) { + unknownHeaders += `${header}, `; + } + }); + if (unknownHeaders !== "") { + const errorMsg = `CSV2JSON() - Champs inconnus : ${unknownHeaders}`; + return callback(errorMsg, null); + } + lines.forEach((line, index) => { + // Skip headers line or empty lines + if (index === 0 || line.replace(/\s/g, "").length === 0) { + return; + } + // pour debuguer on met origincsv pour voir la ligne d'origine + const currentLineData = { origincsv: line, linenumber: index }; + const currentLine = line.split(options.sep); // Current string in the line + for (let j = 0; j < headers.length; j++) { + // Si la ligne n'est pas vide + if (currentLine[j]) { + // On clean le champs + // ajout eventuel de modification de caracter reservé ; dans les libelléetc... + let currentValue = currentLine[j].trim(); + //on transforme le caractere separateur modifié entre double quote + currentValue = currentValue.replace("CARSEPARATOR", options.sep); + options.replacespecialcarCsv2Json.forEach((re) => { + currentValue = currentValue.replace(re[0], re[1]); + }); + // Si le header est un email + if (headers[j].includes("EMAIL")) { + // Supprimer tous les espaces + currentValue = currentLine[j].replace(/\s/g, ""); + } + // on check si le chamos doit être numerique + if (options.numericfield.includes(headers[j])) { + currentValue = currentLine[j].replace(/\,/g, "."); + try { + const test = parseFloat(currentValue); + } catch (er) { + return callback( + `${headers[j]} contiens la valeur -${currentValue}- et devrait être numerique`, + null + ); + } + } + if (currentValue) { + // Si le header actuel est de type array + // Cela signifie que le header apparaît plusieurs fois dans le CSV + // et que les valeurs correspondantes à ce header + // doivent être mis dans un array + if (options.array && options.array.indexOf(headers[j]) > -1) { + // Si le tableau pour ce header n'existe pas on le crée + if (!currentLineData[headers[j]]) { + currentLineData[headers[j]] = []; + } + if (options.arraysplitsep) { + currentValue.split(options.arraysplitsep).forEach((v) => { + currentLineData[headers[j]].push(v); + }); + } else { + currentLineData[headers[j]].push(currentValue); + } + } else { + // Si un header est déjà présent pour la ligne + // alors que il n'est pas spécifié comme étant un array + // on retourne une erreur + if (currentLineData[headers[j]]) { + const errorMsg = `Le champ ${headers[j]} est présent plusieurs fois alors qu'il n'est pas spécifié comme étant un array !`; + return callback(errorMsg, null); + } + currentLineData[headers[j]] = currentValue; + } + } + } + } + result.push(currentLineData); + }); + return callback(null, result); +}; +/** + * [csvparam2json description] + * @param {object} csv object of csv file that has been read + * @param {object} options object containing csv options, headers, ... + {retln:'code de retour de ligne \n ou \n\r', + sep:'code to split cells', + champs:[ch1,ch2,...] catch only those field, + array:[ch1, ] can have more than one field champs with same name then data are push into an array } + * @param {Function} callback callback function + * @return {callback} - return an error if error, else return json + it converts a csv with 3 column col1;col2;col3 in a json in a tree + if in col1 we have __ => then it splits a leaf + col1 = xxxx__yyyy ; col2 = value ; col3 = comment that is ignored + return data = {xxxx:{yyyy:value}} + col1 = xxxx; col2 = value; col3 = comment ignored +return data = {xxxx:value} + +Usage example: +fiche.csvparam2article = (err, fiche) => { + if (!err) { + console.log(fiche) + } +} +utils.csvparam2json(fs.readFileSync('./devdata/tribee/aubergenville/infoexterne/localbusiness.csv', 'utf-8'), { + retln: "\n", + sep: ";", + champs: ["NOM", "OBJET", "ADRESSE_PRO", "CP_PRO", "VILLE_PRO", "ZONE", "PHONE_PRO", "HORAIRESDESC", "HORAIREDATA", "URL", "FACEBOOK", "INSTA", "EMAIL_PRO", "IMG", "TAG"], + array: ["TAG", "PHONE_PRO", "EMAIL_PRO"] +}, fiche.csv2article) + + */ +utils.csvparam2json = (csv, options, callback) => { + console.log("\n--------------- CSVPARAM2JSON ---------------\n"); + let etat = ""; + const param = {}; + if (!options.retln) { + options.retln = "\n"; + } + if (csv.indexOf("\n\r") > -1) { + options.retln = "\n\r"; + } + if (!options.sep) { + options.sep = ";"; + } + if (!options.seplevel) { + options.seplevel = "__"; + } + if (!options.replacespecialcarCsv2Json) { + options.replacespecialcarCsv2Json = []; + } else { + if (typeof options.replacespecialcarCsv2Json == "string") { + //permet de passer des regex en string + options.replacespecialcarCsv2Json = eval( + options.replacespecialcarCsv2Json + ); + } + } + const lines = csv.split(options.retln); + for (let i = 0; i < lines.length; i++) { + const infol = lines[i].split(options.sep); + //console.log(infol) + if (infol[0].length > 4 && infol.length < 2) { + // si le 1er element à plus de 4 caractere et s'il y a moins de 3 colonnes c'est qu'il y a un pb + etat += `Erreur sur ${lines[i]} moins de 3 column separé par ${options.sep}`; + continue; + } + // On ajoute ici la gestion de tous les caracteres spéciaux + // reservées pour le csv ; ' etc..' + if (infol[1] && infol[1] + "" == infol[1]) { + options.replacespecialcarCsv2Json.forEach((re) => { + //console.log("gggggggggggggggggggg", infol[1]) + infol[1] = infol[1].replace(re[0], re[1]); + }); + // console.log(infol[1]) + infol[1] = infol[1].replace(/'|’/g, '"'); + //console.log(infol[1]) + if (infol[1].toLowerCase() === "true") { + infol[1] = true; + } else if (infol[1].toLowerCase() === "false") { + infol[1] = false; + } + } + console.log(infol[1]); + //supprime des lignes vides + if (infol[0] == "") continue; + if (infol[0].indexOf(options.seplevel) == -1) { + param[infol[0]] = infol[1]; + continue; + } else { + const arbre = infol[0].split(options.seplevel); + switch (arbre.length) { + case 1: + param[arbre[0]] = infol[1]; + break; + case 2: + if (arbre[1] != "ARRAY") { + if (!param[arbre[0]]) param[arbre[0]] = {}; + param[arbre[0]][arbre[1]] = infol[1]; + } else { + if (!param[arbre[0]]) param[arbre[0]] = []; + //console.log('aff', infol[1].substring(1, infol[1].length - 1).replace(/""/g, '"')) + eval("result=" + infol[1]); + //.substring(1, infol[1].length - 1).replace(/""/g, '"')) + param[arbre[0]].push(result); + } + break; + case 3: + if (arbre[2] != "ARRAY") { + if (!param[arbre[0]]) param[arbre[0]] = {}; + if (!param[arbre[0]][arbre[1]]) param[arbre[0]][arbre[1]] = {}; + param[arbre[0]][arbre[1]][arbre[2]] = infol[1]; + } else { + if (!param[arbre[0]]) param[arbre[0]] = {}; + if (!param[arbre[0]][arbre[1]]) param[arbre[0]][arbre[1]] = []; + //eval("result = \"test\""); + //console.log(result); + eval("result=" + infol[1]); + //.substring(1, infol[1].length - 1).replace(/""/g, '"')) + param[arbre[0]][arbre[1]].push(result); + } + break; + case 4: + if (arbre[3] != "ARRAY") { + if (!param[arbre[0]]) param[arbre[0]] = {}; + if (!param[arbre[0]][arbre[1]]) param[arbre[0]][arbre[1]] = {}; + if (!param[arbre[0]][arbre[1]][arbre[2]]) + param[arbre[0]][arbre[1]][arbre[2]] = {}; + param[arbre[0]][arbre[1]][arbre[2]][arbre[3]] = infol[1]; + } else { + if (!param[arbre[0]]) param[arbre[0]] = {}; + if (!param[arbre[0]][arbre[1]]) param[arbre[0]][arbre[1]] = {}; + if (!param[arbre[0]][arbre[1]][arbre[2]]) + param[arbre[0]][arbre[1]][arbre[2]] = []; + eval("result=" + infol[1]); + //.substring(1, infol[1].length - 1).replace(/""/g, '"')) + param[arbre[0]][arbre[1]][arbre[2]].push(result); + break; + } + default: + break; + } + } + } + // JSON.parse(JSON.stringify(param)) + console.log( + "kkkkkkkkkkkkkkkkkk", + param["catalogue"]["filtrecatalog"]["searchengine"] + ); + if (etat == "") { + return callback(null, JSON.parse(JSON.stringify(param))); + } else { + return callback(etat, null); + } +}; +utils.levenshtein = (a, b) => { + if (a.length === 0) return b.length; + if (b.length === 0) return a.length; + let tmp, i, j, prev, val, row; + // swap to save some memory O(min(a,b)) instead of O(a) + if (a.length > b.length) { + tmp = a; + a = b; + b = tmp; + } + row = Array(a.length + 1); + // init the row + for (i = 0; i <= a.length; i++) { + row[i] = i; + } + // fill in the rest + for (i = 1; i <= b.length; i++) { + prev = i; + for (j = 1; j <= a.length; j++) { + if (b[i - 1] === a[j - 1]) { + val = row[j - 1]; // match + } else { + val = Math.min( + row[j - 1] + 1, // substitution + Math.min( + prev + 1, // insertion + row[j] + 1 + ) + ); // deletion + } + row[j - 1] = prev; + prev = val; + } + row[a.length] = prev; + } + return row[a.length]; +}; +utils.testinarray = (array, arrayreferent) => { + // au moins un element de array existe dans arryreferent + let exist = false; + if (arrayreferent) { + //console.log('arrrrrrrrrrrrrrr', arrayreferent) + array.forEach((e) => { + //console.log(e) + if (arrayreferent.includes(e)) exist = true; + }); + } + return exist; +}; +/* +DIRECTORY +*/ +const isDirectory = (source) => fs.lstatSync(source).isDirectory(); +const getDirectories = (source) => + fs + .readdirSync(source) + .map((name) => path.join(source, name)) + .filter(isDirectory); + +module.exports = utils; diff --git a/models/Towns.js b/models/Towns.js new file mode 100644 index 0000000..d475e78 --- /dev/null +++ b/models/Towns.js @@ -0,0 +1,208 @@ +const bcrypt = require("bcrypt"); +const fs = require("fs-extra"); +const glob = require("glob"); +const moment = require("moment"); +const jwt = require("jwt-simple"); +const UUID = require("uuid"); +const conf = require(`../../conf/townconf.json`); +const Checkjson = require(`./Checkjson.js`); +const Odmdb = require("./Odmdb.js"); + +const Towns = {}; + +Towns.create = () => { + // Create a new town from conf (generate in apxtrie.js if town not already exist in the server) + console.log( + `RUNNING A NEW SETUP with nation ${conf.nationId} and town ${conf.townId} to be accessible in dns http://${conf.dns}` + ); + const initconf = fs.readJSONSync( + `${conf.dirapi}/adminapi/www/adminapx/initconf.json` + ); + // Synchronize nationchains/ + const { updateobjectsfromfreshesttown } = require("./api/models/Nations.js"); + updateobjectsfromfreshesttown(initconf.towns, { + pagans: "alias_all.json", + towns: "townId_all.json", + nations: "nationId_all.json", + }); + + initconf.dirapi = conf.dirapi; + initconf.dirtown = conf.dirtown; + initconf.nationId = conf.nationId; + initconf.townId = conf.townId; + initconf.sudoerUser = process.env.USER; + if (!initconf.dns.includes(conf.dns)) { + initconf.dns.push(conf.dns); + } + initconf.nginx.include.push(`${initconf.dirapi}/adminapi/www/nginx_*.conf`); + initconf.nginx.include.push(`${initconf.dirtown}/tribes/**/www/nginx_*.conf`); + initconf.nginx.logs = `${initconf.dirtown}/logs/nginx/adminapx`; + fs.ensureDirSync(`${initconf.dirtown}/logs/nginx`); + fs.ensureDirSync(`${initconf.dirtown}/tmp/tokens`); + + initconf.nginx.website = "adminapx"; + initconf.nginx.fswww = `${initconf.dirapi}/adminapi/www`; + initconf.nginx.pageindex = "index_en.html"; + const { exec } = require("child_process"); + exec( + `sudo chown -R ${process.env.USER}:${process.env.USER} /etc/nginx`, + (error, stdout, stderr) => { + if (error) { + console.log("\x1b[42m", error, stdout, stderr, "x1b[0m"); + console.log( + `impossible to change owner of /etc/nginx by ${initconf.sudoerUser}:${initconf.sudoerUser}` + ); + fs.removeSync(initconf.dirtown); + process.exit(); + } else { + console.log( + `successfull sudo chown -R ${process.env.USER}:${process.env.USER} /etc/nginx` + ); + } + } + ); + // create town env + fs.outputJsonSync(`${initconf.dirtown}/conf.json`, initconf, { space: 2 }); + const nginxconf = fs.readFileSync( + "./adminapi/www/adminapx/conf/nginx.conf.mustache", + "utf8" + ); + const proxyparams = fs.readFileSync( + "./adminapi/www/adminapx/conf/nginxproxyparams.mustache", + "utf8" + ); + const websiteconf = fs.readFileSync( + "./adminapi/www/adminapx/conf/nginxmodelwebsite.conf.mustache", + "utf8" + ); + + // saved and change nginx conf + if (!fs.existsSync("/etc/nginx/nginxconf.saved")) { + fs.moveSync("/etc/nginx/nginx.conf", "/etc/nginx/nginxconf.saved"); + console.log( + "your previous /etc/nginx/nginx.conf was backup in /etc/nginx/nginxconf.saved" + ); + } + fs.outputFileSync( + "/etc/nginx/nginx.conf", + mustache.render(nginxconf, initconf), + "utf8" + ); + fs.outputFileSync( + "/etc/nginx/proxy_params", + mustache.render(proxyparams, initconf), + "utf8" + ); + fs.outputFileSync( + `${initconf.dirapi}/adminapi/www/nginx_adminapx.conf`, + mustache.render(websiteconf, initconf), + "utf8" + ); + exec(initconf.nginx.restart, (error, stdout, stderr) => { + if (error) { + console.log("\x1b[42m", error, stdout, stderr, "x1b[0m"); + //@todo supprimer la derniere config nginx et relancer + fs.moveSync("/etc/nginx/nginxconf.saved", "/etc/nginx/nginx.conf"); + console.log("Restart yarn dev with correct parameter"); + // cleanup + fs.removeSync(initconf.dirtown); + } else { + //@TODO à finaliser en test sur machien pour creation de nouvelles villes + // add town in nationchains + const gettown = Odmdb.get(`${initconf.dirapi}/nationchains`, "towns", [ + initconf.townId, + ]); + if (gettown.data[initconf.townId] == "notfound") { + Odmdb.create( + `${initconf.dirapi}/nationschains`, + "towns", + { + townId: initconf.townId, + nationId: initconf.nationId, + dns: initconf.dns, + IP: "127.0.0.1", + status: "unchain", + tribes: [], + }, + false + ); + } else if (gettown.data[initconf.townId].dns !== initconf.dns) { + //reinstallation d'une town sur un autre serveur maj du dns , l'ip le status et les tribes se mettent à jour via l'interface + const updtown = Odmdb.update( + `${initconf.dirapi}/nationchains`, + "towns", + { dns: initconf.dns }, + initconf.townId + ); + } + console.log(`ready to use http://${initconf.dns}`); + } + }); +}; + +Towns.changeowner = async (newowner, requestby) => { + /** + * + */ + if (!fs.existsSync(`./nationchains/pagans/itm/${newowner}.json`)) { + return { + status: 404, + ref: "towns", + msg: "newownerdoesnotexist", + data: { alias: newowner }, + }; + } + if (!conf.mayorId || conf.mayorId == requestby) { + // update object town + town/conf.json + setup_xx.json + const gettown = Odmdb.get(`../nationchains`, "towns", [ + conf.townId, + ]); + console.log(`before town: ${conf.townId}`, gettown); + if (gettown.data[conf.townId] == "notfound") { + return { + status: 404, + ref: "towns", + msg: "townIdnotfound", + data: { townId: conf.townId }, + }; + } + gettown.data[conf.townId].mayorId = newowner; + const objup = await Odmdb.update( + `../nationchains`, + "towns", + gettown.data[conf.townId], + conf.townId + ); + //update the itm town + if (objup.status != 200) { + return objup; + } + console.log(`after town update: ${conf.townId}`, gettown); + + conf.mayorId = newowner; + fs.outputJsonSync(`../conf.json`, conf); + const setup = fs.readJSONSync( + `${conf.dirapi}/adminapi/www/adminapx/conf/setup_xx.json` + ); + conf.mayorId = newowner; + //update the setup file for webapp adminapi + fs.outputJsonSync( + `${conf.dirapi}/adminapi/www/adminapx/conf/setup_xx.json`, + setup + ); + return { + status: 200, + ref: "towns", + msg: "newownerchangesuccess", + data: { alias: newowner }, + }; + } + return { + status: 403, + ref: "towns", + msg: "notallow", + data: { newowner, currentowner: conf.mayorId }, + }; +}; + +module.exports = Towns; diff --git a/models/Trackings.js b/models/Trackings.js new file mode 100644 index 0000000..153b19b --- /dev/null +++ b/models/Trackings.js @@ -0,0 +1,29 @@ +const glob = require("glob"); +const path = require("path"); +const fs = require("fs-extra"); +const dayjs = require("dayjs"); +const axios = require("axios"); +const conf = require(`../../conf/townconf.json`); +const Checkjson = require(`./Checkjson.js`); + +const Trackings = {} + +/** + * Tracking system management + * + * Data collection is done from nginx log system see routes/trackings.js for doc + */ + + +/** + * Process plan to run each night or on demand to collect log data and cleanup + */ +Trackings.logcollection=()=>{ + +} + +Trackings.dashboard=(graphname)=>{ + console.log('Process data to provide a specific graph') +} + +module.export = Trackings; \ No newline at end of file diff --git a/models/Tribes.js b/models/Tribes.js new file mode 100755 index 0000000..f003671 --- /dev/null +++ b/models/Tribes.js @@ -0,0 +1,377 @@ +const bcrypt = require( 'bcrypt' ); +const fs = require( 'fs-extra' ); +const path = require( 'path' ); +const glob = require( 'glob' ); +const Mustache = require( 'mustache' ); +const execSync = require( 'child_process' ) + .execSync; +const dnsSync = require( 'dns-sync' ); +const jwt = require( 'jwt-simple' ); +const moment = require( 'moment' ); +const UUID = require( 'uuid' ); +const Pagans = require( './Pagans.js' ); +const conf = require(`../../conf/townconf.json`); +const Checkjson = require( `./Checkjson.js`); +/* +tribeid manager + +@TODO @STUDY + +To add a tribe in dirtown/tribes with a mayor phil +see man adduser and file reference call skelet directory to set an env for apxtri in /home/tribename/ +accessible by tribename/password +then add group group me to phil to allow phil to ate a symlink /dirtown/tribes/tribename => to /home/tribename + +At each reboot run a process to analyse /api/routes and api/models whre only js can be exexuted are safe (only write data into /home/tribename, never outside) + +1- Create a user in linux with $ sudo useradd smatchit +2 => this create a user:group and a folder smatchit in /home/phil/dirtown/tribes/ +2 => add group smatchit to phil to allow phil to access file with a group accessright +3 set a password if needed "$sudo passwd smatchit" (sm@tchit) to smatchit to make it available from ssh on port 22 +4 +4 to delete a user sudo userdel smatchit (this keep folder smatchit to remove folder smatchit => sudo userdel --remove smacthit) + + + + + + + + + + + + +/tribes/tribeid +Manage a tribeid space +* create +* update by managing option and contract +* delete a tribeid +* check accountability and + +*/ +const Tribes = {}; +Tribes.init = () => { + console.group( 'init Tribes' ); + let tribeids = []; + let routes = glob.sync( './routes/*.js' ) + .map( f => { + return { url: `/${path.basename(f,'.js')}`, route: f } + } ); + let DOMs = []; + let appname = {}; + TribesGlobalConfig = glob.sync( `${conf.tribes}/**/clientconf.json` ) + .map( f => { + const conf = fs.readJSONSync( f ); + // check if plugins exist and add it in .plugins of each tribeid conf + conf.plugins = glob.sync( `${conf.tribes}/${conf.tribeid}/plugins/**/package.json` ) + .map( p => { + const pack = fs.readJsonSync( p, 'utf8' ); + routes.push( { url: `/${pack.name}`, route: `${conf.tribes}/${conf.tribeid}/plugins/${pack.name}/route.js` } ); + return pack; + } ); + //Add here any other info to get a global view and init + //... + tribeids.push( conf.tribeid ); + DOMs = [ ...new Set( [ ...DOMs, ...conf.allowedDOMs ] ) ]; + if( conf.website ) appname[ conf.tribeid ] = Object.keys( conf.website ) + return conf; + } ); + // store global conf fofs.existsSync( `${conf.tmp}/clientconfglob.json` )r sharing to other api + fs.outputJsonSync( `${conf.tmp}/clientconfglob.json`, TribesGlobalConfig, { + spaces: 2 + } ); + return { tribeids, routes, DOMs, appname } +} +Tribes.create = ( data ) => { + /* data = clientconf.json + { + "tribeid": "apxtri", + "genericpsw": "Trze3aze!", + "website": { + "presentation":"https://www.apxtri.org", + "webapp": "https://webapp.apxtri.org" + }, + "allowedDOMs": ["local.fr", "localhost:9002", "ndda.fr", "apxtri.org"], + "clientname": "apxtri", + "clientlogo": "", + "geoloc": [], + "useradmin": {PUBKEY:"",EMAIL:"",LOGIN:"adminapxtri",UUID:"adminapxtri"}, + "smtp": { + "emailFrom": "support@apxtri.org", + "emailcc": [], + "service": "gmail", + "auth": { + "user": "antonin.ha@gmail.com", + "pass": "Ha06110" + } + }, + "accepted-language": "fr,en", + "langueReferential": ["fr"] + } + What about: + "tribeid": same than the folder where all the client's file are stored + "genericpsw": a generic password for new user need upper lowercase number ans special char + "dnsname": a domain name belonging to the client + "subdns": "www", a sub domain subdns.dnsname give a public web access to + "website": { keywebsite:url}, give access to conf.tribes/tribeid/www/keywebsite/index.html, + "allowedDOMs": ["local.fr", "localhost:9002", "nnda.fr"], //for CORS, @TODO generate from prévious URL this allow this apxtri instance to be accessible + "clientname": Name of the organisation if any, + "clientlogo": logo of the organisation if any, + "geoloc": [], if any + "useradmin": { this is the 1st user create automaticaly to make gui available for the 1st user + "PUBKEY":public key to be authentify without an email, + "EMAIL":user email, we need at least one of authentification set up after the user can use both or only one + "LOGIN": login to use for access admintribeid, + "UUID": unique id normaly UUID but a uuid admintribeid is the same person in any apxtri instance so we use it by convention. + "xlang": lang used by this user + }, + "smtp": { smtp used to send email by nodemailer lib basic example with a google account + "emailFrom": "support@xx.fr", + "emailcc": [], + "service": "gmail", + "auth": { + "user": "antonin.ha@gmail.com", + "pass": "Ha06110" + } + }, + "accepted-language": "fr,en", list of accepted-language in terme of http request. + "langueReferential": ["fr"], list of the text that have to be translate in referentials + } + */ + //update tmp/confglog.json + const dataclient = Tribes.init(); + //return in prod all instance apxinfo={tribeids:[],logins:[]} + // in dev return only local + //check tribeid name is unique + console.log( 'liste des tribeid', dataclient.tribeids ) + if( dataclient.tribeids.includes( data.tribeid ) ) { + return { status: 403, payload: { model: "client", info: [ 'tribeidalreadyexist' ] } } + } + //loginsglob = {login:tribeid} + let loginsglob = {}; + if( fs.existsSync( `${conf.tmp}/loginsglob.json`, 'utf-8' ) ) { + loginsglob = fs.readJsonSync( `${conf.tmp}/loginsglob.json`, 'utf-8' ); + } + const logins = Object.keys( loginsglob ); + if( logins.includes( data.useradmin.login ) ) { + return { status: 403, payload: { model: "client", info: [ 'loginalreadyexist' ] } } + } + fs.ensureDirSync( `${conf.tribes}/${data.tribeid}` ); + [ 'users', 'www', 'referentials', 'nationchains' ].forEach( r => { + fs.copySync( `${__dirapi}/setup/tribes/apxtri/${r}`, `${conf.tribes}/${data.tribeid}/${r}` ); + } ) + fs.outputJsonSync( `${conf.tribes}/${data.tribeid}/clientconf.json`, data ); + const confcli = JSON.parse( Mustache.render( fs.readFileSync( `${__dirapi}/setup/tribes/apxtri/clientconf.mustache`, 'utf8' ), data ) ); + fs.outputJsonSync( `${conf.tribes}/${data.tribeid}/clientconf.json`, confcli ); + + return Pagans.createUser( { + xpaganid: "setup", + xworkon: data.tribeid, + xlang: data.useradmin.xlang + }, data.useradmin ); +}; +Tribes.archive = ( tribeid ) => { + //A faire zip un repertoire tribeid dans + // remove tribeid de data ou devdata + try { + fs.moveSync( `${conf.tribes}/${tribeid}`, `${conf.archivefolder}/${tribeid}` ); + //update apxtrienv + Tribes.init(); + return { status: 200, payload: { info: [ 'deletetribeidsuccessfull' ], models: 'Tribes', moreinfo: "TODO see in Tribes.archive" } } + } catch ( err ) { + console.log( "Erreur d'archivage", err ) + return { status: 403, payload: { info: [ 'archiveerror' ], models: 'Tribes', moreinfo: err } } + } +} +////////////// Manage file for Tribes +Tribes.checkaccessfolder = ( folder, typeaccessrequested, useraccessrights, useruuid ) => { + // check folder right + + + +} + +Tribes.checkaccessfiles = ( listfile, typeaccessrequested, useraccessrights, useruuid ) => { + // @listfile to check accessright on file or folder + // @typeaccessrequested on files R read or download, U for pdate, D for delete , O for owned a Owner has all rights RUD on its files + // @useraccessright from its account /userd/uuid.json + // @useruuid public uuid user + // return {'ok':[file auhtorized],'ko':[files not authorized]} + + const checkauthlistfile = { 'ok': [], 'ko': [] } + let structf = [] + let inforep = { file: {}, dir: {} } + let done; + for( const f of listfile ) { + done = false; + if( !fs.existsSync( `${conf.tribes}/${f}` ) ) { + done = true; + checkauthlistfile.ko.push( f ) + console.log( `${f} file does not exist` ) + } else { + structf = f.split( '/' ); + } + //on ckeck tribeid existe / tribeid/object/ + if( !done && + useraccessrights.data[ structf[ 0 ] ] && + useraccessrights.data[ structf[ 0 ] ][ structf[ 1 ] ] && + useraccessrights.data[ structf[ 0 ] ][ structf[ 1 ] ].includes( typeaccessrequested ) ) { + done = true; + checkauthlistfile.ok.push( f ); + } else { + // check if in folder we have a.info.json .file[f].shared{useruuid:'CRUDO'} + console.log( 'structf', structf ) + if( fs.existsSync( `${conf.tribes}/${structf.slice(0,-1).join('/')}/.info.json` ) ) { + inforep = fs.readJsonSync( `${conf.tribes}/${structf.slice(0,-1).join('/')}/.info.json`, 'utf8' ) + } + console.log( `no accessrights for ${f} for ${useruuid} ` ) + } + if( !done && inforep.file[ f ] && inforep.file[ f ] && inforep.file[ f ].shared && inforep.file[ f ].shared[ useruuid ] && inforep.file[ f ].shared[ useruuid ].includes( typeaccessrequested ) ) { + done = true; + checkauthlistfile.ok.push( f ) + } + // If no authorization then ko + if( !done ) { + checkauthlistfile.ko.push( f ) + } + } // end loop for + //console.log( 'checkauthlistfile', checkauthlistfile ) + return checkauthlistfile; +} + +Tribes.dirls = ( tribeid, dir ) => { + /* + Return list of file into tribeid/dir + */ + let comment = { src: `${tribeid}/${dir}`, file: {}, dir: {} }; + if( fs.existsSync( `${conf.tribes}/${tribeid}/${dir}/.info.json` ) ) { + comment = fs.readJsonSync( `${conf.tribes}/${tribeid}/${dir}/.info.json`, 'utf-8' ); + } + const listfile = [] + const listdir = [] + glob.sync( `${conf.tribes}/${tribeid}/${dir}/*` ) + .forEach( f => { + //console.log( f ) + const stats = fs.statSync( f ); + // console.log( stats ) + if( stats.isFile() ) { + listfile.push( path.basename( f ) ) + if( !comment.file[ path.basename( f ) ] ) { + comment.file[ path.basename( f ) ] = { tags: [], info: "", thumbb64: "" }; + } + comment.file[ path.basename( f ) ].mtime = stats.mtime; + comment.file[ path.basename( f ) ].ctime = stats.ctime; + comment.file[ path.basename( f ) ].size = stats.size; + } + if( stats.isDirectory() ) { + listdir.push( path.basename( f ) ) + if( !comment.dir[ path.basename( f ) ] ) { + comment.dir[ path.basename( f ) ] = { tags: [], info: "", thumbb64: "" } + } + comment.dir[ path.basename( f ) ].nbfile = glob.sync( `${f}/*.*` ) + .length; + comment.dir[ path.basename( f ) ].mtime = stats.mtime; + comment.dir[ path.basename( f ) ].ctime = stats.mtime; + console.log( 'comment.dir', comment.dir ) + } + } ); + // on remove les file or dir that was deleted + Object.keys( comment.file ) + .forEach( f => { + if( !listfile.includes( f ) ) delete comment.file[ f ] + } ) + Object.keys( comment.dir ) + .forEach( d => { + if( !listdir.includes( d ) ) delete comment.dir[ d ] + } ) + //console.log( comment ) + fs.outputJson( `${conf.tribes}/${tribeid}/${dir}/.info.json`, comment, 'utf-8' ); + return { status: 200, payload: { info: [ 'succestogetls' ], models: 'Tribes', moreinfo: comment } } +}; +Tribes.addspaceweb = ( data ) => { + /* + To create a public spaceweb accessible from https://dnsname/pageindex + + input: + {dnsname:["archilinea.fr","www.archilinea.fr"], 1st is tha main dns other are just servername redirection + tribeid:"archilinea", from req.session.header.xworkon + website:"presentation", + pageindex:"app_index_fr.html" + mode:dev(local no ssl) | prod(IP + ssl) +} + output: + nginx conf and ssl to serve each https://dnsname to /{tribeid}/www/app/{website} + + + Carefull this action is executed with root and restart nginx + apxtri to work + */ + data.configdomain = conf.tribes; + data.porthttp = conf.porthttp; + console.assert( conf.loglevel == "quiet", 'data to create spaceweb:', data ); + // create spaceweb app for tribeid/www/app/website/pageindexname.html + if( !fs.existsSync( `${conf.tribes}/${data.tribeid}/www/app/${data.website}` ) ) { + fs.outputFileSync( `${conf.tribes}/${data.tribeid}/www/app/${data.website}/${data.pageindex}`, `

Hello ${data.tribeid} ${data.website} onto ${data.dnsname.join(',')}`, 'utf-8' ) + } + //create logs folder + fs.ensureDirSync( `${conf.tribes}/${data.tribeid}/logs/nginx` ); + // add nginx http config + const confnginx = fs.readFileSync( 'setup/nginx/modelwebsite.conf.mustache', 'utf-8' ); + fs.outputFileSync( `/etc/nginx/conf.d/${data.dnsname[0]}.conf`, Mustache.render( confnginx, data ), 'utf-8' ); + if( data.dns == "unchain" ) { + //add in /etc/hosts + let hosts = fs.readFileSync( '/etc/hosts', 'utf8' ); + let chg = false; + data.dnsname.forEach( d => { + if( !hosts.includes( `127.0.0.1 ${d}` ) ) { + hosts += `\n127.0.0.1 ${d}`; + chg = true; + } + if( chg ) { + fs.outputFileSync( '/etc/hosts', hosts, 'utf8' ) + } + } ); + }; + //Ckeck dns respond + data.dnsname.forEach( d => { + if( !dnsSync.resolve( `${d}` ) ) { + rep += `\nresolving ${d} will not responding valid IP, please setup domain redirection IP before runing this script` + } + } ) + //update clienconf.json + const clientconf = fs.readJsonSync( `${conf.tribes}/${data.tribeid}/clientconf.json` ); + clientconf.website[ data.website ] = data.dnsname[ 0 ]; + //merge allowedDOMs in unique concat + clientconf.allowedDOMs = [ ...new Set( ...clientconf.allowedDOMs, ...data.dnsname ) ]; + fs.outputJsonSync( `${conf.tribes}/${data.tribeid}/clientconf.json`, clientconf, 'utf-8' ); + if( !data.setup ) { + // in setup apxtri is not running and it will be start manually at the 1st run + // in other case need to plan a restart for CORS + setTimeout( Tribes.restartapxtri, 300000, data.clienId ); + } + const nginxrestart = execSync( `sudo systemctl restart nginx` ) + .toString(); + console.log( 'Restart nginx', nginxrestart ) + if( data.mode != "unchain" ) { + // get ssl certificate ATTENTION il faut ajouter -d devant chaque domain qui redirige vers l'espace web. + const certbot = execSync( `sudo certbot --nginx -d ${data.dnsname.join(' -d ')}` ) + .toString(); + console.log( 'certbot is running A CHECKER POUR UNE VRAIE PROD ????', certbot ) + } + //sh execution to update change requested + return { + status: 200, + payload: { + model: "Tribes", + info: [ 'webspacecreated' ], + moreinfo: "Space web well created" + } + }; +} +Tribes.restartapxtri = ( tribeid ) => { + console.log( 'A restarting was requested 5mn ago from a new spacedev for ' + tribeid ) + execSync( 'yarn restartpm2' ); +} + + +module.exports = Tribes; diff --git a/models/Wwws.js b/models/Wwws.js new file mode 100644 index 0000000..90cc98b --- /dev/null +++ b/models/Wwws.js @@ -0,0 +1,111 @@ +const fs = require("fs-extra"); +const path = require("path"); +const dnsSync = require("dns-sync"); +const mustache = require("mustache"); +const readlineSync = require("readline-sync"); +const conf = require(`../../conf/townconf.json`); +const Wwws = {}; + +/*Wwws.apxtriinstall = (paramconf) => { + if (fs.existsSync(`${conf.dirtown}/conf.json`)) { + console.log("You already have a conf on this town"); + process.exit(); + } + //first install + const nginxconf = fs.readFileSync( + `${conf.dirapi}/adminapi/www/adminapx/conf/nginx.conf.mustache`, + "utf8" + ); + const proxyparams = fs.readFileSync( + "../nationchains/www/adminapx/static/tpl/nginxproxy_params.mustache", + "utf8" + ); + // saved and change nginx conf + if (!fs.existsSync("/etc/nginx/nginxconf.saved")) { + fs.moveSync("/etc/nginx/nginx.conf", "/etc/nginx/nginxconf.saved"); + console.log( + "your previous /etc/nginx/nginx.conf was backup in /etc/nginx/nginxconf.saved" + ); + } + fs.outputFileSync( + "/etc/nginx/nginx.conf", + mustache.render(nginxconf, paramconf), + "utf8" + ); + fs.outputFileSync( + "/etc/nginx/proxy_params", + mustache.render(proxyparams, paramconf), + "utf8" + ); + if (!fs.existsSync(paramconf.nginx.logs)) fs.mkdirSync(paramconf.nginx.logs); + paramconf.nginx.firstinstall = true; + fs.outputJsonSync("../nationchains/tribes/conf.json", paramconf, { + space: 2, + }); + + return Www.create(paramconf.nginx); +}; +*/ +Wwws.create = (paramnginx) => { + /** + * Create an nginx conf to make available a spaceweb for a tribe /www/appname/ + * + */ + const res = { + status: 200, + ref: "Www", + msg: "successfulwww", + data: { website: paramnginx.website }, + }; + const nginxwebsite = fs.readFileSync( + `${conf.dirapi}/adminapi/www/adminapx/conf/nginxmodelwebsite.conf.mustache`, + "utf8" + ); + fs.outputFileSync( + `./${paramnginx.fswww}www/nginx_${paramnginx.website}.conf`, + mustache.render(nginxwebsite, paramnginx), + "utf8" + ); + if (!fs.existsSync(`./${paramnginx.fswww}www/${paramnginx.website}`)) { + //See later how to generate specific template of webapp + fs.mkdirSync(`./${paramnginx.fswww}www/${paramnginx.website}`); + } + if (!fs.existsSync(`./${paramnginx.fswww}www/cdn`)) { + //See later how to generate specific template of webapp + fs.mkdirSync(`./${paramnginx.fswww}www/cdn`); + } + //restart nginx + const { exec } = require("child_process"); + exec(paramnginx.restart, (error, stdout, stderr) => { + if (error) { + if (paramnginx.firstinstall) { + console.log("\x1b[42m", error, stdout, stderr, "x1b[0m"); + } + //@todo supprimer la derniere config et relancer + res.status = 500; + res.msg = "nginxError"; + res.data = { msg: `${error}
${stdout}
${stderr}` }; + } else { + if (paramnginx.firstinstall) { + // the tribes/conf.json is saved in apxtriinstall + console.log( + `\x1b[42m###########################################################################################\x1b[0m\n\x1b[42mWellcome into apxtri, you can now 'yarn dev' for dev or 'yarn startpm2' for prod or \n'yarn unittest' for testing purpose. Access to your town here \x1b[0m\x1b[32mhttp://adminapx\x1b[0m \x1b[42m \nto finish your town setup. Don't forget to set your localhost /etc/hosts by adding 127.0.0.1 adminapx or {LAN IP} adminapx . Check README's project to learn more. \x1b[0m\n\x1b[42m###########################################################################################\x1b[0m` + ); + } else { + // add website to tribe conf + } + } + }); + return res; +}; +Wwws.setssl = () => { + // Run process to change nginx conf to get a ssl +}; + +Wwws.configlist = (tribeId) => { + //if accessright R return list of conf parameter {webapp:{conf parameter}} + const res = { status: 200, data: {} }; + return res; +}; + +module.exports = Wwws; diff --git a/models/lg/Checkjson_en.json b/models/lg/Checkjson_en.json new file mode 100644 index 0000000..b55d69f --- /dev/null +++ b/models/lg/Checkjson_en.json @@ -0,0 +1,6 @@ +{ + "validcheck":"Your data are valid", + "typedoesnnotexistinschema":"This type in your propertie is not manage by Checkjson.js", + "dataerrpropertie":"Check your data that not fit your schema rules propertie", + "dataerrpropertiesrequired":"This propertie is required and not present in your data" +} \ No newline at end of file diff --git a/models/lg/Checkjson_fr.json b/models/lg/Checkjson_fr.json new file mode 100644 index 0000000..a76cb1c --- /dev/null +++ b/models/lg/Checkjson_fr.json @@ -0,0 +1,7 @@ +{ + "validcheck":"Your data are valid", + "typedoesnnotexistinschema":"This type in your propertie is not manage by Checkjson.js", + "dataerrpropertie":"Check your data that not fit your schema rules propertie", + "dataerrpropertiesrequired":"This propertie is required and not present in your data", + "externalrefnotload":"You have in your schema a relative sub schema, you have to load it before" +} \ No newline at end of file diff --git a/models/lg/Nations_fr.json b/models/lg/Nations_fr.json new file mode 100644 index 0000000..8b2739b --- /dev/null +++ b/models/lg/Nations_fr.json @@ -0,0 +1,7 @@ +{ + "nationnamedesc":"Nom unique d'une nation dans un monde social", + "nationnamedesclong":"Nom unique identifiant une nation dans le monde social créer par un maire d'une nouvelle ville avec un contrat sociale specifique", + "nationnameinfo":"

Une nation posséde un nom unique, un contrat sociale (contracts/name.js) signé et validé par tous les maires des villes associées à cette nation

", + "statusdesc":"Statut de la nation", + "statusactive":"Nation active" +} \ No newline at end of file diff --git a/models/lg/Notifications_fr.json b/models/lg/Notifications_fr.json new file mode 100644 index 0000000..83faab3 --- /dev/null +++ b/models/lg/Notifications_fr.json @@ -0,0 +1,9 @@ +{ + "missingconf":"Il manque un smtp/sms valide pour {{tribe}} ou sur le serveur /conf.json", + "missingdata":"Il manque des données obligatoire dans data {{#missingk}} {{.}} {{/missingk}}", + "missingfile":"Le ou les fichiers suivants n'existent pas {{#missingfile}} {{.}} {{/missingfile}}", + "errsendmail":"Une erreur s'est produite lors de l'envoie de l'email", + "successfullsentemail":"Email correctement envoyé", + "errsendsms":"Une erreur s'est produite lors de l'envoie du sms", + "successfullsentsms":"Sms bien envoyé à {{To}}" +} \ No newline at end of file diff --git a/models/lg/Odmdb_en.json b/models/lg/Odmdb_en.json new file mode 100644 index 0000000..0232fbf --- /dev/null +++ b/models/lg/Odmdb_en.json @@ -0,0 +1,5 @@ +{ + "schemanotfound":"Schema not found in {{{fullpath}}}", + "pathnamedoesnotexist":"ObjectPath or objectName does not exist {{{indexpath}}}", + "objectfiledoesnotexist":"Requested index does not exist here: {{{objectpath}}}" +} \ No newline at end of file diff --git a/models/lg/Odmdb_fr.json b/models/lg/Odmdb_fr.json new file mode 100644 index 0000000..587effb --- /dev/null +++ b/models/lg/Odmdb_fr.json @@ -0,0 +1,19 @@ +{ + "alreadyexist": "Un object {{objectname}} avec la clé {{key}} existe déjà avec {{val}}", + "doesnotexist": "L'object {{objectname}} n'existe pas avec {{key}}:{{val}}", + "getschema": "Schema {{{conf.name}}}", + "schemanotfound": "Schema introuvable dans {{{schemaPath}}}", + "pathnamedoesnotexist": "Le repertoire n'existe pas {{{indexpath}}}", + "objectfiledoesnotexist": "Le fichier n'existe pas {{{objectpath}}}", + "cudsuccessfull": "Mise à jour effectuée avec succés", + "successfulcreatewithoutemail":"Créer sans envoi d'email", + "successfulcreatewitemail":"Créer vous allez recevoir un email", + "missingprimarykey": "Il manque une clé primaire apxid pour stocker et identifier les objects", + "unconsistencyapxidx": "L'index {{name}} doit contenir en objkey au moins {{apxid}} car keyval n'est pas unique", + "profilnotallow": "Vous n'avez pas le profil de {{profils}}, cette action n'est pas authorisée", + "successreindex": "Objet reindexé à partir des items, vos index sont à jour", + "indexexist":"L'indexe existe", + "typenotavailable":"Le type: {{type}} pour la propertie : {{propertie}} de l'object :{{objectPathname}} n'est pas pris en compte pour l'indexation", + "objectslist":"Liste des objects d'apxtri et de {{tribe}}", + "errordelete":"Desolé impossible de supprimer ce compte" +} diff --git a/models/lg/Pagans_en.json b/models/lg/Pagans_en.json new file mode 100644 index 0000000..271234f --- /dev/null +++ b/models/lg/Pagans_en.json @@ -0,0 +1,8 @@ +{ + "aliasexist":"This alias {{alias]} exist", + "aliasdoesnotexist":"This alias {{alias}} does not exist ", + "personexist":"This person {{alias}} exist for {{tribeid}}", + "successfullcreate": "This identity {{alias}} creation was successfull. {{#withemail}} An email was sent to {{email}}, if you do not receive it, please download your keys before living this page.{{/withemail}}", + "successfulluppdate": "Your alias as a Person is now update into {{tribeid}}", + "tribedoesnotexist": "Your tribe {{tribeid}} does not exist in this town" +} diff --git a/models/lg/Pagans_fr.json b/models/lg/Pagans_fr.json new file mode 100644 index 0000000..e3e55f5 --- /dev/null +++ b/models/lg/Pagans_fr.json @@ -0,0 +1,14 @@ +{ + "aliasexist": "Cet alias {{alias}} existe", + "emailerr": "Verifier votre email {{email}}", + "aliasorprivkeytooshort": "Vérifiez votre alias et votre clé privée", + "aliasdoesnotexist": "Cet alias {{alias}} n'existe pas", + "personexist": "Cette personne {{alias}} existe pour {{tribeid}}", + "persondoesnotexist": "Cette personne {{alias}} n'existe pas pour {{tribeid}}", + "successfulcreate":"Votre identité {{alias}} a été créee à partir de vos clés.{{#emailsent}} Un email a été envoyé à {{email}}, si vous ne le recevez pas, veuillez télécharger vos clés avant de quitter cette page.{{/emailsent}} {{#emailerror}}Un problème lors de l'envoi sur {{email}} s'est produit. Veuillez télécharger vos clés avant de quitter cette page.{{/emailerror}}
{{#createperson}}Félicitation Votre compte est ouvert chez {{tribe}}. {{/createperson}} {{#errorperson}}Attention votre identité n'a pas été associée à {{tribe}}. Vous pourrez rejoindre {{tribe}} dans l'écran suivant. {{/errorperson}}", + "successfulluppdate": "Votre alias en tant que Personne est maintenant mis à jour dans {{tribeid}}", + "errcreate": "Desolé, un probléme inconnu empeche la creation", + "logout": "Votre token a été supprimé du server", + "emailnotfound":"Auncune identité n'a été enregistrée sur {{tribe}} avec l'email {{email}} ", + "recoveryemailsent":"Votre email: {{email}} dispose de {{nbalias}} identitées sur {{tribe}}, nous avons envoyés {{nbsent}} messages sans problèmes." +} diff --git a/models/lg/Tribes_fr.json b/models/lg/Tribes_fr.json new file mode 100644 index 0000000..9b8f806 --- /dev/null +++ b/models/lg/Tribes_fr.json @@ -0,0 +1,3 @@ +{ + "actionmissing":"L'action {{data.action}} n'existe pas pour la tribut {{data.tribe}}." +} \ No newline at end of file diff --git a/models/lg/middlewares_en.json b/models/lg/middlewares_en.json new file mode 100644 index 0000000..55a7802 --- /dev/null +++ b/models/lg/middlewares_en.json @@ -0,0 +1,8 @@ +{ + "errrequest": "Backend seems not available", + "missingheader": "Some header miss to have a valid request: {{#data}} {{.}} {{/data}}", + "tribeiddoesnotexist": "Header xtribe: {{data.xtribe}} does not exist in this town you cannot access", + "authenticated": "Your alias{{{data.xalias}}} is authenticated", + "notauthenticated": "Your alias: {{data.xalias}} is not authenticated {{^data.aliasexists}} and this alias does not exist !{{/data.aliasexists}}", + "forbiddenAccessright": "Alias {{data.xalias}} has not access right to act {{data.action}} onto object {{data.object}} for tribe {{mor.xworkon}}" +} diff --git a/models/lg/middlewares_fr.json b/models/lg/middlewares_fr.json new file mode 100644 index 0000000..a2bd98a --- /dev/null +++ b/models/lg/middlewares_fr.json @@ -0,0 +1,10 @@ +{ + "errrequest": "Le serveur ne semble pas répondre", + "unconsistentpgp": "Vos clés ne sont pas conforme {{err}}", + "missingheader": "Certains en-têtes manquent pour avoir une requête valide : {{#data}} {{.}} {{/data}}", + "tribeiddoesnotexist": "L'en-tête xtribe : {{data.xtribe}} n'existe pas dans cette ville, vous ne pouvez pas y accéder", + "authenticated": "Votre alias {{{data.xalias}}} est authentifié", + "notauthenticated": "Votre alias : {{data.xalias}} n'est pas authentifié {{^data.aliasexists}} et cet alias n'existe pas !{{/data.aliasexists}}", + "forbiddenAccessright": "L'alias {{data.xalias}} n'a pas le droit d'agir {{data.action}} sur l'objet {{data.object}} pour la tribu {{mor.xworkon}}", + "signaturefailled": "Desolé votre signature n'est pas valide pour cet alias." +} diff --git a/models/unittest/Checkjson.js b/models/unittest/Checkjson.js new file mode 100644 index 0000000..dfea249 --- /dev/null +++ b/models/unittest/Checkjson.js @@ -0,0 +1,158 @@ +/* +Unit testing +*/ +const assert = require("assert"); +const Checkjson = require("../Checkjson.js"); +const conf = require(`../../conf/townconf.json`); + +const ut = { name: "Checkjson" }; + +const schema = { + $schema: "http://json-schema.org/schema#", + title: "Dummy schema to test Checkjson.js", + description: "Checkjson is use on server as well as into a browser", + $comment: "We change schema type on the fly to simplify the test", + type: "object", + properties: { + totest: {}, + }, +}; +const testproperties = [ + { + name: "test0", + data: { totest: true }, + properties: { totest: { type: "boolean" } }, + status: 200, + }, + { + name: "test1", + data: { totest: "blabla" }, + properties: { totest: { type: "string" } }, + status: 200, + }, + { + name: "test2", + data: { totest: 123 }, + properties: { totest: { type: "string" } }, + status: 417, + }, + { + name: "test3", + data: { totest: 123.13 }, + properties: { totest: { type: "integer" } }, + status: 417, + }, + { + name: "test4", + data: { totest: 123 }, + properties: { totest: { type: "number" } }, + status: 200, + }, + { + name: "test5", + data: { totest: 12312 }, + properties: { totest: { type: "number" } }, + status: 200, + }, + { + name: "test6", + data: { totest: 12.313 }, + properties: { totest: { type: "float" } }, + status: 200, + }, + { + name: "test7", + data: { totest: "blablab sfde" }, + properties: { totest: { type: "string", minLength: 1111 } }, + status: 417, + }, + { + name: "test8", + data: { totest: "blablab sfde" }, + properties: { totest: { type: "string", minLength: 4, maxLength: 128 } }, + status: 200, + }, + { + name: "test9", + data: { totest: 12 }, + properties: { totest: { type: "integer", multipleOf: 3 } }, + status: 200, + }, + { + name: "test10", + data: { totest: 9 }, + properties: { + totest: { type: "number", minimum: -10, exclusiveMaximum: 10 }, + }, + status: 200, + }, + { + name: "test11", + data: { totest: 10 }, + properties: { + totest: { type: "number", minimum: -10, exclusiveMaximum: 10 }, + }, + status: 417, + }, + { + name: "test12", + data: { totest: "gfhrtabcdgfr" }, + properties: { totest: { type: "string", pattern: /.*abc.*/ } }, + status: 200, + }, + { + name: "test13", + data: { totest: "toto@google.com" }, + properties: { totest: { type: "string", format: "email" } }, + status: 200, + }, + { + name: "test14", + data: { totest: "Aze123@0" }, + properties: { totest: { type: "string", format: "password" } }, + status: 200, + }, + { + name: "test15", + data: { totest: "value1" }, + properties: { + totest: { type: "string", enum: ["value1", "value2", "value3"] }, + }, + status: 200, + }, + { + name: "test16", + data: { totest: ["t1", "t2"] }, + properties: { totest: { type: ["string", "number"] } }, + status: 417, + }, + { + name: "test17", + data: { totest: 12 }, + properties: { totest: { type: ["string", "number"] } }, + status: 200, + }, +]; + +ut.testproperties = (options) => { + let msg = ""; + testproperties.forEach((t) => { + schema.properties = t.properties; + const res = Checkjson.schema.data(schema, t.data); + if (res.status != t.status) { + msg = msg == "" ? "Unconsistent testproperties() name list: " : `${msg},`; + if (options.verbose) { + console.log(t); + console.log(res); + } + msg += res.err.map((e) => ` ${t.name} ${e.info}`); + } + }); + return assert.deepEqual(msg, "", msg); +}; + +ut.run = (options) => { + console.log("Test Checkjson properties"); + ut.testproperties(options); +}; +module.exports = ut; diff --git a/models/unittest/Odmdb.js b/models/unittest/Odmdb.js new file mode 100644 index 0000000..29a93eb --- /dev/null +++ b/models/unittest/Odmdb.js @@ -0,0 +1,43 @@ +/* +Unit testing +*/ +const assert = require("assert"); +const fs = require("fs-extra"); +const path = require("path"); +const Odmdb = require("../Odmdb.js"); +const { generemdp } = require("../toolsbox.js"); +const conf = require(`../../conf/townconf.json`); + +const ut = { name: "Odmdb" }; +/* +Test crud process for any object +*/ +ut.crud = (objectPathname, itm, profils) => { + // + // test if exist + // if not test create + // test to read + // test update + // test delete + const res = { status: 200, err: [] }; + return res; +}; + +const testvar={alias:"tutu", passphrase:"",privatekey:"", publickey:""} + +const testitms=[ + {objectPathname:`../nationchains/pagans`, + itm:{alias:'toutou', publickey:}} +] + + + +ut.run = (options) => { + let msg="" + testitms.forEach(i=>{ + ut.crud(i) + //si erreur add msg+++ + }) + assert.deepEqual(msg, "", msg); +}; +module.exports = ut; diff --git a/package.json b/package.json new file mode 100755 index 0000000..1491fd4 --- /dev/null +++ b/package.json @@ -0,0 +1,112 @@ +{ + "name": "apxtri", + "version": "0.0.1", + "logo": { + "file": "../nationchains/tribes/adminapi/www/cdn/share/logo/logocarredark.png" + }, + "description": "apxtri Decentralized Autonomous Organization", + "main": "apxtri.js", + "repository": { + "url": "https://gitea.ndda.fr/apxtri/apxtri", + "type": "git" + }, + "scripts": { + "stoppm2": "pm2 stop apxtri.js", + "startpm2": "pm2 start apxtri.js --log-date-format 'DD-MM HH:mm:ss.SSS'", + "deletepm2": "pm2 delete apxtri", + "restartpm2": "pm2 restart apxtri.js --log-date-format 'DD-MM HH:mm:ss.SSS'", + "startblockchain": "pm2 start models/Blockchains.js --log-date-format 'DD-MM HH:mm:ss:SSS'", + "logpm2": "pm2 logs apxtri.js --lines 200", + "dev": "node apxtri.js mode:dev", + "unittest": "node unittest.js", + "apidoc": "apidoc -i middlewares routes -o ../nationchains/tribes/adminapi/www/apidoc", + "apidocsmatchit": "apidoc -i ../nationchains/tribes/smatchit/api/middlewares ../nationchains/tribes/smatchit/api/routes -o ../nationchains/tribes/smatchit/www/apidoc" + }, + "apidoc": { + "name": "apxtri", + "version": "1.0.0", + "title": "apiDoc for apxtri", + "description": "Core api documentation", + "url": "https://town.dns/api", + "order": [ + "Middlewares", + "Odmdb", + "Nationchains" + ], + "template": { + "forceLanguage": "en", + "showRequiredLabels": true, + "withCompare": true, + "withGenerator": true, + "aloneDisplay": false + }, + "header": { + "title": "Introduction", + "filename": "header.md" + }, + "footer": { + "title": "Best practices", + "filename": "footer.md" + } + }, + "maintainers": [ + { + "name": "Philippe Colzy", + "email": "support@need-data.com", + "url": "https://need-data.com" + } + ], + "contributors": [ + { + "name": "Antonin Hattabe", + "email": "", + "url": "" + }, + { + "name": "Maxime Serrato", + "email": "", + "url": "" + }, + { + "name": "Charles Filuzeau", + "email": "", + "url": "" + } + ], + "author": { + "name": "patma", + "email": "patma@mail.be", + "url": "" + }, + "license": "MIT", + "dependencies": { + "@editorjs/editorjs": "^2.26.5", + "apidoc": "^0.54.0", + "async": "^3.2.0", + "axios": "^0.21.1", + "baunsu": "^0.2.3", + "bcrypt": "^5.0.0", + "cors": "^2.8.4", + "crypto-js": "^4.1.1", + "dayjs": "^1.11.7", + "dns-sync": "^0.2.1", + "express": "^4.16.3", + "formidable": "^2.1.1", + "fs-extra": "^11.1.0", + "glob": "^7.1.2", + "jszip": "^3.7.1", + "jwt-simple": "^0.5.1", + "luxon": "^2.1.1", + "moment": "^2.22.1", + "mustache": "^2.3.0", + "nodemailer": "^6.9.7", + "openpgp": "^5.10.1", + "path": "^0.12.7", + "pm2": "^5.3.0 ", + "readline-sync": "^1.4.10", + "smtp-client": "^0.4.0", + "stripe": "^14.4.0", + "uuid": "^9.0.0" + }, + "devDependencies": {} +} diff --git a/routes/nations.js b/routes/nations.js new file mode 100755 index 0000000..b7bb1f1 --- /dev/null +++ b/routes/nations.js @@ -0,0 +1,35 @@ +const express = require( 'express' ); +const fs=require('fs-extra'); +const conf = require(`../../conf/townconf.json`); + +// Classes +const Nations = require( '../models/Nations.js' ); +// Middlewares +const checkHeaders = require( '../middlewares/checkHeaders' ); +const isAuthenticated = require( '../middlewares/isAuthenticated' ); +const router = express.Router(); + +/* +Manage nation +A major create a nation with at least a town => nation:{name, towns:[]} contracts/nationname.js + contracts/townsname.js +Manage a new towns in a nation => update nation:[nationname:towns:[]} contracts/townname.js +*/ + + +/** + * @api {put} /nations/viewtown/:town - nationlist from a town major + * @apigroup Nation + * @apiName nationlist + * @apiDescription get list of nation from a town to help this instance to update network topology + * @apiParam {string} town fromwhich the data come from + * @apiSuccess {object} refreshnetworktopoly + * * HTTP/1.1 200 OK + * {status:200,ref:"Nations",msg:"updated",data:{}}; + * + */ +router.put( '/', checkHeaders, isAuthenticated, ( req, res ) => { + const uptown = Nations.updatetown() + res.status(uptown.status).send(uptown) +} ) + +module.exports = router; diff --git a/routes/notifications.js b/routes/notifications.js new file mode 100644 index 0000000..3fcc786 --- /dev/null +++ b/routes/notifications.js @@ -0,0 +1,30 @@ +const express = require("express"); +// Classes +const Notifications = require("../models/Notifications.js"); + +// Middlewares +const checkHeaders = require("../middlewares/checkHeaders"); +const isAuthenticated = require("../middlewares/isAuthenticated"); + +const router = express.Router(); + +/** + * @api {get} /notifications/:alias/:tribeId + * @apiName notiflist + * @apiDescription Get list of notifications for an alias and a tribe + * @apiGroup Notification + * + * @apiParam {string} alias + * @apiParam {string} tribeId + * @apiSuccess {object} notif content + * @apiSuccessExample {json} Success-Response: + * HTTP/1.1 200 OK + * {status:200,ref:"Notification",msg:"Messagelist",data:{notif:[{tribeId,msg:{from(email or uuid or alias):[{dt,msg}]}}]} + * + **/ +router.get("/:alias/:tribeId", (req, res) => { + const getnot = Notification.get(req.params.alias,req.params.tribeId); + res.status(getalias.status).send(getalias); +}); + +module.exports=router; \ No newline at end of file diff --git a/routes/odmdb.js b/routes/odmdb.js new file mode 100644 index 0000000..0a5165c --- /dev/null +++ b/routes/odmdb.js @@ -0,0 +1,600 @@ +const express = require("express"); +const glob = require("glob"); +const fs = require("fs-extra"); +const path = require("path"); +const conf = require(`../../conf/townconf.json`); +const Odmdb = require("../models/Odmdb.js"); +// Middlewares +const checkHeaders = require("../middlewares/checkHeaders.js"); +const isAuthenticated = require("../middlewares/isAuthenticated.js"); +const router = express.Router(); + +/** + * @api {get} /odmdb/objects - objects Get + * @apiGroup Odmdb + * @apiName getIndex + * @apiDescription Get objects available for header.xtribe + * + * @apiSuccess {object} contain data.indexname + * @apiSuccessExample {json} Success-Response: + * HTTP/1.1 200 OK + * {"status":200, "ref":"Odmdb", "msg":"objectslist", "data":{indexname,content:{index file}} + * + * + */ +router.get("/objects", checkHeaders, isAuthenticated, (req, res) => { + const data = { + tribe: req.session.headers.xtribe, + apx: { conf: {}, objectnames: [] }, + tribe: { conf: {}, objectnames: [] }, + }; + glob.sync(`../nationchains/tribes/adminapi/schema/*.json`).forEach((f) => { + const objectname = path.basename(f, ".json"); + if (objectname == "conf") { + data.apx.conf = fs.readJSONSync(f); + } else { + data.apx.objectnames.push(objectnames); + } + }); + glob + .sync(`../nationchains/tribes/${req.session.headers.xtribe}/schema/*.json`) + .forEach((f) => { + const objectname = path.basename(f, ".json"); + if (objectname == "conf") { + data.apx.conf = fs.readJSONSync(f); + } else { + data.apx.objectnames.push(objectnames); + } + }); + res.status(200).json({ status: 200, ref: "Odmdb", msg: "objectslist", data }); +}); +/** + * @api {get} /odmdb/idx/:tribe/:objectname/:indexname - index Get + * @apiGroup Odmdb + * @apiName getIndex + * @apiDescription Get index file for an object + * + * @apiParam {string} tribe if common adminapi or tribename + * @apiParam {string} objectname If in conf.nationObjects then object is into nationchains/ else in tribes/xtribe/objectname/idx/indexname indexname + * @apiParam {String} indexname name of index file in /idx/indexnamme.json + * + * + * @apiError {json} objectNotfound the file does not exist + * @apiErrorExample {json} + * HTTP/1.1 404 Not Found + {"status":404,"ref":"Odmdb","msg":"pathnamedoesnotexist","data":{indexpath}} + * + * @apiSuccess {object} indexfile content + * @apiSuccessExample {json} Success-Response: + * HTTP/1.1 200 OK + * {"status":200, "ref":"Odmdb", "msg":"indexexist", "data":{indexname,content:{index file}} + * + * + */ +router.get( + "/idx/:tribe/:objectname/:indexname", + checkHeaders, + isAuthenticated, + (req, res) => { + console.log("passe"); + // indexname = objectname_key_value.json + let objectLocation = "../nationchains/"; + if (!conf.api.nationObjects.includes(req.params.objectname)) { + objectLocation += `tribes/${req.session.headers.xtribe}/`; + // check if accessright + } + const indexpath = `${objectLocation}/${req.params.objectname}/idx/${req.params.indexname}`; + if (fs.existsSync(indexpath)) { + res.status(200).json({ + ref: "Odmdb", + msg: "indexexist", + data: { + indexname: req.params.indexname, + content: fs.readJsonSync(indexpath), + }, + }); + } else { + res.status(404).json({ + ref: "Odmdb", + msg: "pathnamedoesnotexist", + data: { indexpath }, + }); + } + } +); + +/** + * @api {get} /odmdb/rebuildidx/:objectname - index refresh all + * @apiGroup Odmdb + * @apiName refreshAllIndex + * @apiDescription Rebuild all index for an object, this can be usefull in case crash or any data conflict. + * + * @apiParam {string} objectname Mandatory + * + * @apiSuccess {object} indexfile content + * @apiSuccessExample {json} successreindex + * HTTP/1.1 200 OK + * {"status":200, "ref":"Odmdb", "msg":"successreindex", "data":{"indexlist":[]}} + * + * @apiError {json} objectNotfound the file does not exist + * @apiErrorExample {json} + * HTTP/1.1 404 Not Found + {"status":404,"ref":"Odmdb","msg":"see nationchains/model/lg/Odmdb_xx.json","data":"object to render with msg"} + * + */ +router.get( + "/rebuildidx/:objectname", + checkHeaders, + isAuthenticated, + (req, res) => { + console.log("reindex"); + // check validity and accessright + const objectPathname = conf.api.nationObjects.includes( + req.params.objectname + ) + ? `../nationchains/${req.params.objectname}` + : `${conf.dirtown}/tribes/${req.session.header.xtribe}/${req.params.objectname}`; + //console.log(objectPathname); + if (!fs.existsSync(objectPathname)) { + res.status(404).json({ + status: 404, + ref: "Odmdb", + msg: "pathnamedoesnotexist", + data: { indexpath: objectPathname }, + }); + return false; + } + if ( + conf.api.nationObjects.includes(req.params.objectname) && + !req.session.header.xprofils.includes("mayor") + ) { + res.status(403).json({ + status: 403, + ref: "Odmdb", + msg: "profilnotallow", + data: { profils: "mayor" }, + }); + return false; + } + if ( + !conf.api.nationObjects.includes(req.params.objectname) && + !req.session.header.xprofils.includes("druid") + ) { + res.status(403).json({ + status: 403, + ref: "Odmdb", + msg: "profilnotallow", + data: { profils: "druid" }, + }); + return false; + } + const reindex = Odmdb.idxfromitm(objectPathname, "I", {}, {}, [], {}); + res.status(reindex.status).json(reindex); + } +); + +/** + * @api {post} /odmdb/itm/:objectname - item Create + * @apiGroup Odmdb + * @apiName postItm + * @apiPermission none + * @apiDescription Add an new item (data) into a collection of objectname items. Before Check data integrity with the relevant schema. + * Then create a new primarykey. For multilanguage return see nationchains/model/lg/Odmdb_xx.json. + * + * @apiParam {string} objectname Place where to create new item, schema and version are available in /objectname/conf.json + * + * @apiBody {Object} data must pass Checkjson.js with schema + * + * @apiSuccess {json} data idxprimary Value of idxprimary into objectname collection + * @apiSuccessExample {json} Success-Response: + * HTTP/1.1 200 OK + * {"status":200, "ref":"Odmdb", "msg":"cudsuccessfull", "data":{"itm":{}}} + * + * @apiError {json} schemanotfound The objectname schema is not found + * @apiError {json} pathnamedoesnotexist The objectname does not exist for the tribe + * @apiError {json} missingprimarykey Body data must have primarykey to be created + * @apiError {json} unconsistencyapxidx some Body data get unique key that already exist + * @apiError {json} checkjsonfalse The body data are not consistent with the schema + * @apiErrorExample {json} + * HTTP/1.1 404 Not Found + * {"status":404,"ref":"Odmdb","msg":"see nationchains/model/lg/Odmdb_xx.json","data":"object to render with msg"} + * + */ +router.post("/itm/:objectname", checkHeaders, isAuthenticated, (req, res) => { + // Create an item of an object with no specificities + // if specificities then create a route / model that import odmdb + res.json({}); +}); +router.get( + "/searchitems/:objectname/:question", + checkHeaders, + isAuthenticated, + (req, res) => { + /** + * + * + */ + console.log( + "route referentials get all language" + + req.params.objectname + + "-" + + req.params.question + ); + const getref = Referentials.getref( + true, + req.params.source, + req.params.idref, + req.session.header.xworkon, + req.session.header.xlang + ); + // Return any status the data if any erreur return empty object + res.jsonp(getref.payload.data); + } +); + +/** + * @api {get} /odmdb/itm/:objectname/:primaryindex - item Get + * @apiGroup Odmdb + * @apiName getItemFromId + * @apiDescription Get itm for a primaryid of an object + * + * @apiParam {String} objectname name Mandatory if in conf.nationObjects then file is into nationchains/ else in /nationchains/tribes/xtribe/objectname + * @apiParam {String} primaryindex the unique id where item is store + * + * @apiError {json} objectNotfound the file item does not exist + * @apiErrorExample {json} + * HTTP/1.1 404 Not Found + * {"status":404,"ref":"Odmdb","msg":"doesnotexist","data":{"objectname":"objectname","key":"apxid","val":"primaryindex"}} + * + * @apiSuccess {object} indexfile content + * @apiSuccessExample {json} Success-Response: + * HTTP/1.1 200 OK + * {"status":200, "ref":"Odmdb", "msg":"indexexist", "data":{"indexname","content":{itm file}} + * + * + */ +// indexname = objectname_key_value.json +router.get( + "/itm/:objectname/:primaryindex", + checkHeaders, + isAuthenticated, + (req, res) => { + const objectName = req.params.objectname; + const objectId = req.params.primaryindex; + let objectLocation = "../nationchains/"; + if (!conf.api.nationObjects.includes(objectName)) { + objectLocation += `tribes/${req.session.headers.xtribe}/${objectName}`; + // check if accessright on object on item + // in case not res.status(403) + } + const objectpath = `${objectLocation}/${objectName}/itm/${objectId}`; + + if (fs.existsSync(objectpath)) { + res.status(200).json({ data: fs.readJsonSync(objectpath) }); + } else { + res.status(404).json({ + ref: "Odmdb", + msg: "objectfiledoesnotexist", + data: { objectpath }, + }); + } + } +); +/** + * @api {get} https://wall-ants.ndda.fr/Checkjson.js - schema Checkjson.js + * @apiGroup Odmdb + * @apiName checkjsonjs + * @apiDescription Public js lib to import in a browser by :
+ * `````` + * to import in a node.js:
+ * ```const Checkjson = require(`Checkjson.js`);``` + * + * with functions:
+ * Checkjson.schema.validation(schema) that return
+ * - {status:200, ref:"Checkjson", msg:"validcheck"} => all rules are correct
+ * - {status:406, multimsg:[{ref:"Checkjson",msg:"errorkey",data:{}}]}
+ * + * Checkjson.schema.data(schema{json},data{json},withschemacheck{boolean}) that return
+ * - {status:200, ref:"Checkjson", msg:"validcheck"} => all data keys respect schema rules
+ * - {status:417, multimsg:[{ref:"Checkjson",msg:"errorkey",data:{}}]}
+ * + * To identify issues, get the language errorkey list with a get + * https://wall-ants.ndda.fr/nationchains/models/Checkjson_lg.json + * + */ + +/** + * @api {get} https://wall-ants.ndda.fr/nationchains/schema/:objectname - schema Get public + * @apiGroup Odmdb + * @apiName getPublicSchema + * @apiDescription Get a Schema model from public apxtri (nations, pagans,persons,towns, tribes,wwws) + * @apiSuccess {json} contain json file + * @apiSuccessExample {json} Fichier direct + * HTTP/1.1 200 Success-response: + { + "$id": "https://smatchit.io/schema/pagan", + "$comment": "To create account bin apxtri", + "title": "Pagans identity", + "description": "A numeric id in the nationchains world", + "type": "object", + "properties": { + "publickey": { + "title": "Alias's publickey", + "description": "Public key generate with openpgp.js", + "type": "string", + "format": "pgppublickey" + }, + "alias": { + "title": "Alias", + "description": "text to remember easily a public key", + "type": "string", + "minLength": 4, + "pattern": "^[a-z0-9]*$" + }, + "dt_delete": { + "title": "Date of death", + "description": "Date of alias delete request, your will will be apply", + "type": "string", + "format": "date-time" + }, + "will": { + "title": "Will script after death", + "description": "This will script will be apply on your data 30 days after your death", + "type": "string" + } + }, + "required": ["publickey", "alias"], + "apxid": "alias", + "apxuniquekey": ["publickey"], + "apxidx": [ + { "name": "lst_alias", "keyval": "alias" }, + { "name": "alias", "keyval": "alias" } + ], + "apxaccessrights": { + "owner": { "R": [], "D": [] }, + "anonymous": { "C": [], "R": ["alias"] }, + "pagan": { "R": ["alias", "publickey"] } + } +} + + * @apiError {json} contain json file + * @apiErrorExample {string} nginx html not found message + * HTTP/1.1 404 Not Found + * ... +**/ + +/** + * @api {get} https://tribedns/:tribe/schema/:objectname.json - schema Get from tribe + * @apiGroup Odmdb + * @apiName getPrivateSchema + * @apiDescription Get a tribe schema or schema/lg/:objectname_xx.json this is served by nginx not express. tribedns is the domain name (ex:smatchit.io) + * + * @apiParam {string} tribe where schema is stored ex;smatchit + * @apiParam {String} objectname the json file name ex persons.json or for language /lg/persons_fr.json + * + * @apiError {object} ref objectname note found + * @apiErrorExample {json} Error-response: + * HTTP/1.1 404 Not Found + * ... + * @apiSuccess {json} data contains schema requested + * @apiSuccessExample {json} Success-Response for : https://smatchit.io/smatchit/schema/persons.json + * HTTP/1.1 200 Success-response: + * {{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "/smatchit/schema/sirets", + "title": "Siret is a legal french company", + "description": "A recruiter can active a jobad if a maxnumber is not reach for this siret. Maxnumber is set depending of an offer or can be set manualy", + "type": "object", + "properties": { + "siret": { + "title": "siret", + "description": "A unique string identifying a company ", + "type": "string", + "minLength": 14, + "pattern": "^[0-9]*$" + }, + "owner": { + "title": "Owner of this siret", + "description": "For accessright purpose this is set by the alias that pay the first time a subscription", + "type": "string" + }, + "dt_create": { + "type": "string", + "format": "date-time", + "default": "dayjs.now()" + }, + "dt_update": { + "type": "string", + "format": "date-time" + }, + "subscription": { + "title": "Offer subscribe", + "type": "array", + "items": { + "type": "object", + "properties": { + "offer": { "type": "string", "enum": ["A", "B", "C", "FREE"] }, + "dt_payment": { "type": "string", "format": "date-time" } + } + } + }, + "emailbilling":{ + "type":"string", + "format":"email" + }, + "namebilling":{ + "type":"string" + }, + "maxactivejobad": { + "title": "Number of active jobad at the same time", + "description": "Alloaw by subscription payment for a date inside the last dt_payment and offer", + "type": "integer" + }, + "activejobad": { + "title": "Current number of active jobadd", + "type": "integer" + }, + "frenchlocation": { + "title": "Location", + "description": "use franch gov refential to locaize", + "$ref": "nationchains/schema/frenchlocation" + }, + "denomination": { + "title": "Company name", + "type": "string" + } + }, + "required": ["siret"], + "additionalProperties": true, + "apxid": "siret", + "apxuniquekey": ["siret"], + "apxidx": [ + { + "name": "lst_siret", + "keyval": "siret" + } + ], + "apxaccessrights": { + "owner": { + "D": [], + "R": [ + "siret", + "dt_create", + "dt_update", + "subscription", + "maxactivejobadd", + "activejobadd", + "frenchlocation", + "demomination" + ], + "U": ["frenchlocation", "demomination"] + }, + "druid": { + "C": [], + "D": [], + "R": [ + "siret", + "dt_create", + "dt_update", + "subscription", + "maxactivejobadd", + "activejobadd", + "frenchlocation", + "demomination" + ], + "U": [ + "subscription", + "maxactivejobadd", + "activejobadd", + "frenchlocation", + "demomination" + ] + }, + "pagans": { "C": [] }, + "adminrecruiter": { + "C": [], + "R": [ + "siret", + "dt_create", + "dt_update", + "subscription", + "maxactivejobadd", + "activejobadd", + "frenchlocation", + "demomination" + ], + "U": ["frenchlocation", "demomination"] + }, + "recruiter": { + "R": [ + "siret", + "dt_create", + "dt_update", + "subscription", + "maxactivejobadd", + "activejobadd", + "frenchlocation", + "demomination" + ] + } + } +}} + */ + +/** + * @api {put} https://wall-ants.ndda.fr/odmdb/schema/:tribe/:objectname - schema Put + * @apiGroup Odmdb + * @apiName putSchema + * @apiDescription Replace a schema by another one need druid profil for a tribe + * + * @apiParam {String} objectname + * + * @apiBody {string} schemapath where to store schema .../schema + * @apiBody {string} objectpath where to store object ...objectname/idx/conf.json + * @apiBody {json} schema content + * @apiBody {json} schemalang content in lg + * @apiBody {string} lang define which schemalg is (2 letters) + * + * @apiError {object} ref objectmodel to get in the right language + * @apiErrorExample {json} Error-response: + * HTTP/1.1 404 Not Found + * {"status":404,"ref":"Odmdb","msg":"schemanotfound","data":{"fullpath"}} + * + * @apiSuccess {object} data contains schema requested + * HTTP/1.1 200 Success-response: + * {"status":200,"data":{schema}} + * + * + */ +router.put( + "schema/:tribe/:objectname", + checkHeaders, + isAuthenticated, + (req, res) => { + const fullpath = path.resolve( + `${__dirname}/tribes/${req.session.header.xworkon}/schema/${req.params.pathobjectname}.json` + ); + const set = Odmdb.setObject( + path.resolve(`${__dirname}/tribes/${req.session.header.xworkon}`) + ); + + if (fs.existsSync(fullpath)) { + res.status(200).json({ data: fs.readJsonSync(fullpath) }); + } else { + res + .status(404) + .json({ msg: "schemanotfound", ref: "odmdb", data: { fullpath } }); + } + } +); + +/** + * @api {get} https://wall-ants.ndda.fr/nationchains/models/:modelname_lg.json - translation notif Get public + * @apiGroup Odmdb + * @apiName getPublicModelmessagekey + * @apiDescription Get a public json object for the ref: modelname in language lg, to get a template description with key msg + * @apiParam {string} modelname Mandatory + * @apiSuccess {json} contain json file + * @apiSuccessExample {json} Fichier direct + * HTTP/1.1 200 Success-response: + * { + "alreadyexist": "Un object {{objectname}} avec la clé {{key}} existe déjà avec {{val}}", + "doesnotexist": "L'object {{objectname}} avec la clé {{key}} ,'existe pas avec {{val}}", + "getschema": "Schema {{{conf.name}}}", + "schemanotfound": "Schema introuvable dans {{{schemaPath}}}", + "pathnamedoesnotexist": "Le repertoire n'existe pas {{{indexpath}}}", + "objectfiledoesnotexist": "Le fichier n'existe pas {{{objectpath}}}", + "cudsuccessfull": "Mise à jour effectuée avec succés", + "missingprimarykey": "Il manque une clé primaire apxid pour stocker et identifier les objects", + "unconsistencyapxidx": "L'index {{name}} doit contenir en objkey au moins {{apxid}} car keyval n'est pas unique", + "profilnotallow": "Vous n'avez pas le profil de {{profils}}, cette action n'est pas authorisée", + "successreindex": "Objet reindexé à partir des items, vos index sont à jour", + "indexexist":"L'indexe existe" + * } + * @apiError {json} contain json file + * @apiErrorExample {string} nginx html not found message + * HTTP/1.1 404 Not Found + * ... +**/ + +module.exports = router; diff --git a/routes/pagans.js b/routes/pagans.js new file mode 100755 index 0000000..6a49f98 --- /dev/null +++ b/routes/pagans.js @@ -0,0 +1,327 @@ +const express = require("express"); +const fs = require("fs-extra"); +const path = require("path"); +// Classes +const Pagans = require("../models/Pagans.js"); +const Odmdb = require("../models/Odmdb.js"); +// Middlewares +const checkHeaders = require("../middlewares/checkHeaders.js"); +const isAuthenticated = require("../middlewares/isAuthenticated.js"); + +const conf = require(`../../conf/townconf.json`); + +const router = express.Router(); +/** + * /api/models/Pagans.js + * + * Managed: + +/** + * Alias exist then return public key or not + * @api {get} /pagans/alias/:alias - alias Get + * @apiName isalias + * @apiGroup Pagans + * @apiDescription If alias exist return its publickey + * + * @param {string} alias + * + * @apiError {json} aliasdoesnotexist + * @apiErrorExample {json} + * HTTP/1.1 404 Not Found + {"status":404,"ref":"pagans","msg":"aliasdoesnotexist","data": { alias}} + * + * @apiSuccess {object} indexfile content + * @apiSuccessExample {json} Success-Response: + * HTTP/1.1 200 OK + * {"status":200, ref:"pagans","msg":"aliasexist","data": { alias, publicKey }} + * * + **/ +router.get("/alias/:alias", (req, res) => { + const getalias = Pagans.getalias(req.params.alias); + res.status(getalias.status).send(getalias); +}); +/** + * Remove serveur token + * @api {get} /pagans/logout - pagan Logout + * @apiName Removetoken + * @apiGroup Pagans + * @apiDescription Remove token + * + * @apiSuccess {object} indexfile content + * @apiSuccessExample {json} Success-Response: + * HTTP/1.1 200 OK + * {status: 200, ref: "Pagans", msg: "logout" + * + */ +router.get("/logout", checkHeaders, isAuthenticated, (req, res) => { + console.log(req.session.header); + const logout = Pagans.logout( + req.session.header.xalias, + req.session.header.xtribe, + req.session.header.xdays, + req.session.header.xhash + ); + res.status(logout.status).json(logout); +}); +/** + * @api {get} /pagans/isauth - pagan isAuthenticated? + * @apiName isAuth + * @apiGroup Pagans + * @apiDescription Check if pagan's token is still valid + * + * @apiError (400) missingheaders + * @apiError (400) xaliasdoesnotexist + * @apiError (400) signaturefailled + * @apiError (401) aliasanonymous + * @apiError (404) tribedoesnotexist + * + * @apiSuccess (200) valid + * {object} data contains indexfile requested + * + */ +router.get("/isauth", checkHeaders, isAuthenticated, (req, res) => { + res.status(200).send({ + status: 200, + ref: "headers", + msg: "authenticated", + data: { + xalias: req.session.header.xalias, + xprofils: req.session.header.xprofils, + }, + }); +}); + +/** + * @api {post} /pagans - pagan Post + * @apiName addpagan + * @apiGroup Pagans + * @apiDescription + * Create a pagan account from alias, publickey, if trusted recovery => + * Create a person in xtribe/person/xalias.json with profil.auth={email,privatekey, passphrase} + * Middleware isAuthenticated check that: + * - xhash is well signed from private key linked to the publickey of alias + * - check that alias does not already exist (if yes then verifiedsigne would be false) + * Need to wait next block chain to be sure that alias is register in the blokchain + * @apiBody {string} alias available (that does not already exist check get /api/alias/:alias that must return 404). + * @apiBody {string} publickey + * @apiBody {string} [email] if specified then an email is sent to it with public and privatekey + * @apiBody {string} [privatekey] + * @apiBody {string} [passphrase] if not specidied => passphrase="" + * @apiBody {string} [trustedtribe] the tribename if not specified then the process will only create a pagan identity, else an item person is create for trustedtribe (that must exist with profil 'person'). To create a person with an existing pagan identity use put /api/person/:alias after authenticated you (headers). In case a person is created then we use all valid other apiBody respecting rules https://smatchit.io/api/odmdb/schema/persons.json + * + * @apiError {json} objectNotfound the file does not exist + * @apiErrorExample {json} + * HTTP/1.1 404 Not Found + {"status":404,"ref":"Odmdb","msg":"pathnamedoesnotexist","data":{indexpath}} + * + * @apiSuccess {object} indexfile content + * @apiSuccessExample {json} Success-Response: + * HTTP/1.1 200 OK + * {"status":200, "ref":"Odmdb", "msg":"indexexist", "data":{indexname,content:{index file}} + * + */ +router.post("/", checkHeaders, isAuthenticated, async (req, res) => { + console.log("pass ici", req.body); + const role = { + xalias: req.session.header.xalias, + xprofils: req.session.header.xprofils, + }; + const emailregex = + /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; + if (!(req.body.trustedtribe && req.body.email && emailregex.test(req.body.email) )) { + res.status(400).json({status:400,ref:"Pagans",msg:"emailerr", data:{email:req.body.email}}) + return + } + const objpagan = { alias: req.body.alias, publickey: req.body.publickey }; + const newpagan = Odmdb.cud(`../nationchains/pagans`, "C", objpagan, role); + const createprocess={status:200, ref:"Pagans", msg:"successfulcreate",data:{alias:req.body.alias}}; + if (newpagan.status == 200) { + if (req.body.email) { + const emailsent = await Pagans.sendmailkey({ + alias: req.body.alias, + privatekey: req.body.privatekey, + tribe: req.session.header.xtribe, + passprhase: req.body.passphrase, + publickey: req.body.publickey, + email: req.body.email, + lg: req.session.header.xlang + } + ); + createprocess.data.emailsent = (emailsent.status == 200); + createprocess.data.email=req.body.email + createprocess.data.tribe=req.session.header.xtribe; + if (emailsent.status!=200) { + console.log("err emailsent: ",emailsent) + createprocess.data.emailerror = emailsent.data.err; + } + } + if (req.body.trustedtribe) { + const persondata = { + alias: req.body.alias, + owner: req.body.alias, + profils: ["pagans", "persons"], + recoveryauth: { + email: req.body.email, + privatekey: req.body.privatekey, + publickey: req.body.publickey, + passphrase: req.body.passphrase, + }, + }; + const personup = Odmdb.cud(`../nationchains/tribes/${req.body.trustedtribe}/objects/persons`, "C", persondata, {xprofils:["pagan"],xalias:req.body.alias}); + console.log('personup',personup) + if (personup.status==200){ + createprocess.data.createperson=true; + }else{ + createprocess.data.createperson=false; + createprocess.data.errorperson=true; + createprocess.data.errpersonup=personup.data; + console.log("Warning pagan created but person not created and no recovery registration", personup); + } + res.status(createprocess.status).json(createprocess); + }else{ + res.status(newpagan.status).json(newpagan); + } + } else { + //error to create pagan certaily already exist + res.status(newpagan.status).json(newpagan); + } +}); +/** + * @api {delete} /pagans/alias/:alias - pagan Delete + * @apiName deletepagan + * @apiGroup Pagans + * @apiDescription + * Delete an alias and his publickey, this mean that publickey disapear as well as alias. All tribe will be inform and will delete person of this alias if they have. This alias will be availlable after 1 year. + * */ +router.delete("/alias/:alias", checkHeaders, isAuthenticated, (req, res) => { + console.log(`DELETE pagans nationchains/pagans/${req.params.alias}.json`); + const result = Pagans.deletealias(req.params.id, req.session.header); + res.status(result.status).send(result); +}); +/** + * @api {delete} /pagans/person/:alias - person Delete + * @apiName deleteperson + * @apiGroup Pagans + * @apiDescription + * Unsubscribe a person to a tribe => remove a person item and all data link to this alias + * */ +router.delete("/person/:alias", checkHeaders, isAuthenticated, (req, res) => { + const personpath=`../nationchains/tribes/${req.session.header.xtribe}/objects/persons`; + const role = { + xalias: req.session.header.xalias, + xprofils: req.session.header.xprofils, + }; + req.session.header.role + const delperson = Odmdb.cud(personpath,"D",{alias:req.params.alias},role,true); + console.log(`DELETE person ${personpath}/${req.params.alias}.json `); + console.log(delperson) + res.status(delperson.status).json(delperson); +}); + +/** + * @api {get} /pagans/person/:alias - person Get + * @apiName getpersondata + * @apiDescription Get person information from his alias for a xtribe (data and profils per apps) + * @apiGroup Pagans + * + * @apiParam {string} alias + * + * @apiSuccess (200) personExist + * @apiSuccessExample {json} + * {status:200, ref:"pagans",msg:"personexist",data: { person } } + * + * @apiError (404) Notfound + * @apiErrorExample {json} + * {status: 404, ref:"pagans",msg:"persondoesnotexist",data: { person } } + * + * @todo check accessright for req.session.header.xalias to see if jhe can get person data + * if req.param.alias == req.session.header.xalias => Owner + * else need accessright to on person set at R + * */ +router.get("/person/:alias", checkHeaders, isAuthenticated, (req, res) => { + const getperson=Odmdb.r( `../nationchains/tribes/${req.session.header.xtribe}/objects/persons`,req.params.alias,{ xprofils: req.session.header.xprofils, xalias: req.session.header.xalias }) + res.status(getperson.status).send(getperson); +}); + +/** + * @api {put} /pagans/person - person Put + * @apiName updateperson + * @apiGroup Pagans + * @apiDescription add or update a person = alias in a tribe. alias authenticated must have a profil with accessright into schema person. + * @apiHeader {string} xalias + * @apiParam {object} in line with schema in https://smatchit.io/api/odmdb/schema/persons + * + */ +router.put("/person", checkHeaders, isAuthenticated, (req, res) => { + //console.log(req.body); + const pathobj=`../nationchains/tribes/${req.session.header.xtribe}/objects/persons`; + const action = (fs.existsSync(`${pathobj}/itm/${req.body.alias}.json`))? "U":"C"; + //set req.body to be in line with schema + if (!req.body.profils){ + req.body.profils=["anonymous","pagans","persons"] + } + const personup = Odmdb.cud(pathobj, action, req.body, {xprofils:req.session.header.xprofils, xalias:req.session.header.xalias}); + console.log('personup',personup) + res.status(personup.status).json(personup); +}); + +/** + * @api {get} /pagans/keyrecovery/tribe/email - recovery keys by email + * @apiName recoveryKey + * @apiGroup Pagans + * @apiDescription Send mails with all registers identities (one per alias where recoveryauth.email is register) + * + * @apiSuccess {object} + * @apiSuccessExample {json} Success-Response: + * HTTP/1.1 200 OK + * {"status":200, "ref":"Pagans", "msg":"recoveryemailsent", "data":{email,tribe,nbalias:'number of alias with this email', nbqent:'number of eamil sent must = nbalias'}} + * + * @apiError (404) {string} emailnotfound email does not exist for this tribe + * @apiErrorExample {json} + * {status: 404, ref:"pagans",msg:"emailnotfound",data: { tribe,email } } + * + */ +router.get("/keyrecovery/:tribe/:email", checkHeaders, (req, res) => { + const idxreco=`../../nationchains/tribes/${req.params.tribe}/objects/persons/idx/emailrecovery_alias.json`; + if (fs.existsSync(idxreco)){ + const emailreco = fs.readJSONSync(idxreco); + if (emailreco[req.params.email]){ + let sentemails=0; + emailreco[req.params.email].forEach(alias=>{ + const ret = Pagans.sendmailkey({alias:alias,tribe:req.params.tribe,lg:req.session.header.xlang}) + sentemails = (ret.status==200)? sentemails+1:sentemails; + }) + return {status:200,ref:"Pagans",msg:"recoveryemailsent",data:{email:req.params.email,tribe:req.params.tribe,nbalias:emailreco[req.params.email].length, nbsent:sentemails}} + }else{ + return {status:404,ref:"Pagans",msg:"emailnotfound",data:{tribe:req.params.tribe,email:req.params.email}} + } + }else{ + return {status:404,ref:"Pagans",msg:"emailnotfound",data:{tribe:req.params.tribe,email:req.params.email}} + } +}); +/** + * @api {get} /pagans/keyrecovery/tribe/alias - recovery keys by alias + * @apiName recoveryKey + * @apiGroup Pagans + * @apiDescription Send to email recovery if exist key files + * + * @apiSuccess {object} + * @apiSuccessExample {json} Success-Response: + * HTTP/1.1 200 OK + * {"status":200, "ref":"Pagans", "msg":"recoveryemailsent", "data":{email,tribe,nbalias:1, nbqent:1 or 0}} + * + * @apiError (404) {string} emailnotfound email does not exist for this tribe + * @apiErrorExample {json} + * {status: 404, ref:"pagans",msg:"emailnotfound",data: { tribe,email } } + * + */ +router.get("/keyrecovery/:tribe/:alias", checkHeaders, (req, res) => { + // a tester et revoir + const ret = Pagans.sendmailkey({alias:req.params.alias,tribe:req.params.tribe,lg:req.session.header.xlang}) + if (ret.status==200){ + return {status:200,ref:"Pagans",msg:"recoveryemailsent",data:{email:"",tribe:req.params.tribe,nbalias:1, nbsent:1}} + }else{ + return {status:404,ref:"Pagans",msg:"emailnotfound",data:{tribe:req.params.tribe,email:req.params.email}} + } +}); +module.exports = router; diff --git a/routes/towns.js b/routes/towns.js new file mode 100755 index 0000000..c76ff8e --- /dev/null +++ b/routes/towns.js @@ -0,0 +1,32 @@ +const express = require("express"); +const path = require("path"); + +// Classes +const Towns = require("../models/Towns.js"); +const Notifications = require("../models/Notifications.js"); +// Middlewares +const checkHeaders = require("../middlewares/checkHeaders.js"); +const isAuthenticated = require("../middlewares/isAuthenticated.js"); +const router = express.Router(); + +/** + * @api {get} /towns/ownershipr/:alias - town owner change + * @apiName changeowner + * @apiGroup Towns + * @apiDescription Change owner (major role) of a town (server) after a first install or just to resale it. Only the current major can run this. + * @param {string} alias an alias that will become owner of a town + * @apiSuccess (object) ownerchangesuccess + * @apiSuccessExample {json} + * HTTP/1.1 200 OK + * {status:200, ref:"towns",msg:"ownerchangesuccess",data: { alias } } + * @apiError {object} aliasnotallow + * @apiErrorExample {json} + * HTTP/1.1 403 Forbidden + * {status:403,ref:"towns",msg:"aliasnotallow",data: { alias} } + * + **/ +router.get("/changeowner/:alias",checkHeaders, isAuthenticated, (req, res) => { + res.send(Towns.changeowner(req.params.alias, req.session.header.xalias)); +}); + +module.exports = router; diff --git a/routes/trackings.js b/routes/trackings.js new file mode 100644 index 0000000..e6f39d5 --- /dev/null +++ b/routes/trackings.js @@ -0,0 +1,82 @@ +const express = require("express"); +const glob = require("glob"); +const fs = require("fs-extra"); +const path = require("path"); +const conf = require(`../../conf/townconf.json`); +const Odmdb = require("../models/Odmdb.js"); +// Middlewares +const checkHeaders = require("../middlewares/checkHeaders"); +const isAuthenticated = require("../middlewares/isAuthenticated"); +const router = express.Router(); + + +/** + * @api {get} https://dns.xx/trk/pathtofile? - tracking system + * @apiGroup Trackings + * @apiName trackingsystem + * @apiDescription + * without header:
+ * https://dns.xx/trk/pathtofile?alias=anonymous&uuid=1b506f71-1bff-416c-9057-cb8b86296f60&src=btnregister&version=1&lg=fr + * + * with header
+ * https://dns.xx/trk/pathtofile?srckey=btnregister&version=1 + * + * where pathtofile is a ressource accessible from https://dns.xx/pathtofile +* html usage to track a loading page or email when a picture is load + * using apxwebapp in /src/ we got: + * < img src="static/img/photo.jpg" data-trksrckey="loadpage" data-version="1" > + * + * using html + apx.js (or at least with header {xalias,xuuid,xlang}) + * < img lazysrc="trk/static/img/photo.jpg data-trksrckey="loadpage" data-version="1" > + * + * in js action: + * + * + * + * + * + * + * #will hit an eventlistener
+ * axios.get("https://dns.xx/trk/cdn/empty.json?alias=anonymous&uuid=1b506f71-1bff-416c-9057-cb8b86296f60&srckey=btnregister&version=1"); + * + * + * #or if no js available (example:email or pdf document)
+ * < img src="https://dns.xx/trk/static/img/photo.jpg?alias=anonymous&uuid=1b506f71-1bff-416c-9057-cb8b86296f60&srckey=loadpage&version=1" + * + * + * + * + * will hit a tracker then redirect to url> * + * + * + * + * **if you use apx.js** : in html add in < button >, < img >, < a > tag data-trksrc="srckey" + * + * < img src="https://dns.xx/static/img/photo.jpg" data-trkversion="1" data-trksrckey="registerform"> + * < button data-trksrc="https://dns.xx/static/img/photo.jpg" data-trkversion="1" data-trksrckey="registerform"> + * + * + * Tracking log are store into tribe/logs/nginx/tribe_appname.trk.log + * Src have to be manage in tribe/api/models/lg/src_en.json + * {"srckey":{ + * "app":"presentation|app|apptest", + * "title":"", + * "description":"" + * } + * } + * + * + * @apiParam {String} alias=anonymous if authenticated we get from headers + * @apiParam {String} uuid a uuid v4 generate the first time a web page is open on a browser + * @apiParam {String} srckey source action that trig this get + * @apiParam {integer} version=1 can be an int, date or any version of the src + * @apiParam {integer} [tm] a timestamp of action when it is not immediate (for offline app) + * + */ + + + + +module.exports=router; \ No newline at end of file diff --git a/routes/tribes.js b/routes/tribes.js new file mode 100755 index 0000000..1143174 --- /dev/null +++ b/routes/tribes.js @@ -0,0 +1,413 @@ +const express = require( 'express' ); +const fs = require( 'fs-extra' ); +const path = require( 'path' ); +const glob = require('glob'); +const conf = require(`../../conf/townconf.json`); + +// Classes +const Tribes = require( '../models/Tribes.js' ); +// Middlewares +const checkHeaders = require( '../middlewares/checkHeaders' ); +const isAuthenticated = require( '../middlewares/isAuthenticated' ); +const router = express.Router(); + +/** +* @api {get} /tribes/www/:tribeId - tribe list +* @apiName getlisttrib +* @apiDescription Get list of www object (space web) +* @apiGroup Tribes +* +* @apiParam {String} tribeId it identify an existing tribe* +* @apiSuccess (object) listwww contains folder name in www for tribeId +* @apiSuccessExample {json} listwww +* HTTP/1.1 200 OK +* {status:200,ref:"Tribes",msg:"listwww",data:{listwww}} +*/ +router.get('www', checkHeaders,isAuthenticated,(req,res)=>{ + let listwww=[] + glob.sync(`${conf.dirtown}/tribes/${req.params.tribeId}/www/*`).forEach(d=>{ + listwww.push(d.split("/").pop()) + }) + res.status(200).json({status:200,ref:"Tribes",msg:"listwww",data:{listwww}}) +}) + +//router.post('www/') to create a webspace + +//router.put('www/:app') to update + +//router.delete('www/:tribeId/:app) + +router.post('/actionanonyme',checkHeaders,(req,res)=>{ + if (!fs.existsSync(`${conf.dirtown}/tribes/${req.session.header.xtribe}/actions/${req.body.action}.js`)){ + res.status(403).send({status:403,msg:"actionmissing",ref:"Tribes", data:{action:req.body.action,tribe:req.session.header.xtribe}}) + } + const action = require(`${conf.dirtown}/tribes/${req.session.header.xtribe}/actions/${req.body.action}.js`) + const resaction= action.run(req.body,req.session.header); + res.status(resaction.status).send(resaction); +}) + +router.post('/action',checkHeaders,isAuthenticated,(req,res)=>{ + +}) + + +router.get( '/clientconf/:tribeid', checkHeaders, isAuthenticated, ( req, res ) => { + /* + get a clientconf.json for a tribeid depending of user accessright + if tribeid == all and user is admin of apxtri => get /tmp/clientconfglob.json + req.session.header.accessrights, req.session.header.apixpaganid + */ + console.log( `Tribes/clientconf for tribeid:${req.params.tribeid}` ) + if( req.params.tribeid == "all" && req.session.header.accessrights.data.apxtri && req.session.header.accessrights.data.apxtri.tribeid && req.session.header.accessrights.data.apxtri.tribeid.includes( 'R' ) ) { + res.status( 200 ) + .send( { moreinfo: fs.readJsonSync( `${config.tmp}/clientconfglob.json`, 'utf-8' ) } ); + return; + } + if( req.session.header.accessrights.data[ req.params.tribeid ] && + req.session.header.accessrights.data[ req.params.tribeid ].tribeid && + req.session.header.accessrights.data[ req.params.tribeid ].tribeid.includes( 'R' ) && + fs.existsSync( `${config.tribes}/${req.params.tribeid}/clientconf.json` ) ) { + // const conftribeid = { moreinfo: {} } + // conftribeid.moreinfo[ req.params.tribeid ] = fs.readJsonSync( `${config.tribes}/${req.params.tribeid}/clientconf.json`, 'utf-8' ); + res.status( 200 ) + .send( { moreinfo: [ fs.readJsonSync( `${config.tribes}/${req.params.tribeid}/clientconf.json`, 'utf-8' ) ] } ); + return; + } + // if not authorized or dos not exist return empty + // no specific message is send for security reason (check only log) + res.status( 403 ) + .send( { info: [ 'forbidenAccess' ], models: 'Tribes' } ) + .end(); +} ) +router.put( '/', checkHeaders, isAuthenticated, ( req, res ) => { + console.log( 'Create a new tribeid, with a useradmin' ) + console.log( ' send data = clientconf.json with all parameter.' ) + // !!!!! check for security any ; \n or so because data can be used into shell + const add = Tribes.create( req.body ); + res.status( add.status ) + .send( add.payload ) +} ) +router.delete( '/archivetribeid/:tribeid', checkHeaders, isAuthenticated, ( req, res ) => { + console.log( "request archive tribeid" ) + const archive = Tribes.archive( req.params.tribeid ); + res.status( archive.status ) + .send( archive.payload ) +} ); +router.post( '/spaceweb', checkHeaders, isAuthenticated, ( req, res ) => { + // !!!!! check for security any ; \n or so because data can be used into shell + console.log( 'Create a new webapp for xworkon ' ) + req.body.tribeid = req.session.header.xworkon; + const add = Tribes.addspaceweb( req.body ) + res.status( add.status ) + .send( add.payload ) +} ) +router.get( '/spaceweb/components/:tribeid/:website/:key', checkHeaders, ( req, res ) => { + // check if key is valid before continue + // exemple: get Tribes/spaceweb/components/ndda/mesa/123?rep=appmesatable/appsimpletable.mustache + const file = `${config.tribes}/${req.params.tribeid}/spacedev/${req.params.website}/src/ctatic/components/${req.query.path}` + console.log( `Request components file from ${file}` ) + if( fs.existsSync( file ) ) { + res.sendFile( file ); + } else { + res.send( `console.error("Missing components file in ${req.params.tribeid}/spacedev/${req.params.website}/src/ctatic/components/${req.query.path}");` ); + } +} ) +router.get( '/plugins/:tribeid/:pluginname/:key/:filename', ( req, res ) => { + // No accessright possible cause it is load on the fly + // @todo Check key to authorize access to the plugin (key comme from user ACCESSRIGHTS[tribeid plugin owner:pluginname]).key + // return a file into /:tribeid owner of plugin/plugins/:pluginname/components/:filename + // if not exist or invalid key then return console.error + const file = `${config.tribes}/${req.params.tribeid}/plugins/${req.params.pluginname}/components/${req.params.filename}` + console.log( 'Tribes/plugins/ ', file ) + if( fs.existsSync( file ) ) { + res.sendFile( file ); + } else { + res.send( `console.error("Missing plugin file in ${req.params.tribeid}/plugins/${req.params.pluginname}/components/${req.params.filename}");` ); + } +} ); + +router.get( '/dirls', checkHeaders, isAuthenticated, ( req, res ) => { + // url /Tribes/dirls?rep=referentials/dataManagement + // request information about a req.query.rep from header xworkon/ + // return + // {file:[{}],dir:[{}]} + // @todo check if isAuthorized and exist + + console.log( 'request dirls', `${config.tribes}/${req.session.header.xworkon}/${req.query.rep}` ); + if( !fs.existsSync( `${config.tribes}/${req.session.header.xworkon}/${req.query.rep}` ) ) { + res.status( 404 ) + .send( { 'info': [ 'dirnotexist' ], model: 'Tribes' } ); + } + const info = Tribes.dirls( req.session.header.xworkon, req.query.rep ); + console.log( info ) + res.status( info.status ) + .send( info.payload ); +} ) +router.delete( '/ls', checkHeaders, isAuthenticated, ( req, res ) => { + // check Accessright with D or O on each + // url /Tribes/ls + // req.body.files=[listfiles file to delete ] + const authfiles = Tribes.checkaccessfiles( req.body, 'D', req.session.header.accessrights, req.session.header.apixpaganid ); + authfiles.ok.forEach( f => { fs.remove( `${config.tribes}/${f}` ); } ) + res.status( 200 ) + .send( { 'info': [ 'fileauthdeleted' ], models: 'Tribes', moreinfo: authfiles } ) +} ); +router.put( '/sendjson', checkHeaders, isAuthenticated, ( req, res ) => { + //req.body = {object:spacedev, path:website/src/data/tpldataname_lg.json, data:{...}} + //console.log( req.body ) + const dest = `${config.tribes}/${req.session.header.xworkon}/${req.body.object}/${req.body.path}`; + console.log( `Send json to saved to ${dest}` ); + if( !( req.body.object && fs.existsSync( `${config.tribes}/${req.session.header.xworkon}/${req.body.object}` ) ) ) { + res.status( '404' ) + .send( { info: [ 'objectmissiong' ], models: 'Tribes', moreinfo: `object: ${req.body.object} does not exist req.body must {object, data, path} into data ${req.session.header.xworkon}/${req.body.object}` } ) + } else { + if( fs.existsSync( `${config.tribes}/${req.session.header.xworkon}/${req.body.object}/${req.body.path}` ) ) { + // exist so can be update check accessright update on this + //A REVOIR hasAccessrighton( req.body.object, "U" ); + } else { + // AREVOIRhasAccessrighton( req.body.object, "C" ); + } + fs.outputJsonSync( dest, req.body.data ); + res.status( 200 ) + .send( { info: [ 'filesaved' ], models: 'Tribes' } ) + } +} ); +router.post( '/downloadls', checkHeaders, isAuthenticated, ( req, res ) => { + // midlleware hasAccessrighton.js is not apply here only to access/update/create information inside an object + // to get file a user need accessrights to data: object: R or to Own it + // or if exist a .info.json into folder get shared as R in uuid + + //req.body contain list of path file or folder if only 1 file then download it, otherwise zip list and send zip file + + const authfiles = Tribes.checkaccessfiles( req.body.files, 'R', req.session.header.accessrights, req.session.header.xpaganid ); + if( authfiles.ok.length == 1 ) { + // bidouille en attendnat de faire un .zip binaire propre + if( !authfiles.ok[ 0 ].includes( '.xml' ) ) { + res.status( 200 ) + .download( `${config.tribes}/${authfiles.ok[0]}`, authfiles.ok[ 0 ] ); + } else { + fs.copySync( `${config.tribes}/${authfiles.ok[0]}`, `${config.tribes}/${config.mayorId}/www/app/webapp/static/tmp/${authfiles.ok[ 0 ]}` ) + } + } else if( authfiles.ok.length > 1 ) { + // on zip et on envoie + //res.status( 200 ) + // .download( `${config.tribes}/${authfiles.ok[0]}`, authfiles.ok[ 0 ]) + res.status( 200 ) + .attachment( `${config.tribes}/${authfiles.ok[0]}` ); + + } else { + req.body.filepon + res.status( 403 ) + .send( 'Forbidden access' ) + } +} ); +router.post( '/upfilepond', checkHeaders, isAuthenticated, ( req, res ) => { + console.log( 'post /Tribes/uploadfilepond' ); + // Store file and return a unique id to save button + // that provide folder where to store it + const formidable = require( 'formidable' ); + const form = formidable( { multiples: false } ); + form.parse( req, ( err, fields, files ) => { + if( err ) { next( err ); return; } + //console.log( 'fields',fields); + // fileMetadaObject send + let context = JSON.parse( fields.filepond ); + let idfile = files.filepond.path; + let name = files.filepond.name; + let subfolder = context.subfolder; + name = name.replace( /[ ,'"’]/g, "_" ); + //console.log( 'files.filepond:', files.filepond ); + console.log( idfile, `${config.tribes}/${req.session.header.xworkon}/www/${subfolder}/${name}` ) + // On le supprime s'il existe deja + fs.removeSync( `${config.tribes}/${req.session.header.xworkon}/www/${subfolder}/${name}` ); + // mv tmp + fs.moveSync( idfile, `${config.tribes}/${req.session.header.xworkon}/www/${subfolder}/${name}` ); + //res.status(200).send({models:"Tribes",info:["Savedsuccess"],moreinfo:{id:file.filepond.path}}) + //return for filepond + res.writeHead( 200, { 'Content-Type': 'text/plain' } ); + res.end( idfile ); + } ) +} ); +router.delete( '/file', checkHeaders, isAuthenticated, ( req, res ) => { + //src = objectfolder with accessright/... + //hasAccessrighton( "www", "D" ), + if( !req.query.src ) { + res.status( 404 ) + .send( { info: [ 'deleteerror' ], models: "Tribes", moreinfo: "your del req need a src" } ) + return; + }; + // A REVOIR hasAccessrighton( req.query.src.split( '/' )[ 0 ], "D" ); + console.log( 'Remove file', `${config.tribes}/${req.session.header.xworkon}/${req.query.src}` ) + console.log( req.body ) + fs.removeSync( `${config.tribes}/${req.session.header.xworkon}/${req.query.src}` ); + res.status( 200 ) + .send( { info: [ 'Successfullremove' ], models: "Tribes" } ) +} ); +router.post( '/uploadfile', checkHeaders, isAuthenticated, ( req, res ) => { + console.log( 'upload a file ' ) + /* Authentification is needed to get a TOKEN + curl -X POST -H "xtribe: apxtri" -H "xworkon: pvmsaveurs" -H "xlang: fr" -H "xpaganid: 1" -H "xauth: 1" -H "xapp: pvmsaveurs:pvmsaveurs" -H "Content-Type: application/json" -d '{"LOGIN":"adminapxtri","PASSWORD":"Trze3aze!"}' http://pvmsaveurs.pvmsaveurs.fr/app/users/login + if exist replace xpaganidTOKEN with payload.TOKEN value + + curl -H "xtribe: pvmsaveurs" -H "xworkon: pvmsaveurs" -H "xlang: fr" -H "xpaganid: adminapxtri" -H "xauth: xpressuuisToken" -H "xapp: pvmsaveurs:pvmsaveurs" -F 'data=@filename.xx' http://pvmsaveurs.pvmsaveurs.fr/app/Tribes/uploadfile + */ + const formidable = require( 'formidable' ); + const form = formidable( { multiples: false } ); + form.parse( req, function ( err, fields, files ) { + //console.log( files.data ) + var oldPath = files.data.path; + var newPath = `${config.tribes}/${req.session.header.xworkon}/${clientconf.uploadzip[files.data.name].dest}`; + console.log( 'oldPath', oldPath ) + console.log( 'newPath', newPath ) + var rawData = fs.readFileSync( oldPath ) + fs.outputFile( newPath, rawData, function ( err ) { + if( err ) { + console.log( err ); + return res.status( 405 ) + .send( { info: [ 'savederror' ], models: "Tribes", moreinfo: "your file was not able to be saved into the server" } ) + } else { + return res.status( 200 ) + .send( { + info: [ "successfullsent" ], + models: "Tribes" + } ); + } + } ) + } ); +} ); +router.post( '/uploadzip', checkHeaders, ( req, res ) => { + console.log( 'uploadzip a file ' ) + + /* no authentification to upload a zip filename into /tribes/${xworkon}/${clientconf.uploadzip[filename].dest} + unzip it using the password ${clientconf.uploadzip[filename].psw + if no error then run the callback ${clientconf.uploadzip[filename].callback + but a password to unzip + in clientconf.json need to be set + "uploadzip": { + "articlesTribespvm.zip": { + "comment": "unzip with overwrite if same name", + "psw": "azPI1209qtrse", + "dest": "importexport/tmp", + "unzipoption": "-aoa", + "callback": "importexport/integrationitem.js" + } + }, + Example: + cd where zip file is stored + curl -H "xtribe: pvmsaveurs" -H "xworkon: pvmsaveurs" -H "xlang: fr" -H "xpaganid: adminapxtri" -H "xauth: 1" -H "xapp: pvmsaveurs:pvmsaveurs" -F 'data=@articlesTribespvm.zip' http://pvmsaveurs.pvmsaveurs.fr/app/Tribes/uploadzip + + */ + const clientconf = fs.readJSONSync( `${config.tribes}/${req.session.header.xworkon}/clientconf.json` ) + if( !clientconf.uploadzip ) { + return res.status( '404' ) + .send( { info: [ "missconf" ], models: "Tribes", moreinfo: `no uploadzip in clientconf for ${req.session.header.xworkon} please contact apxtri admin ` } ); + }; + const uploadzip = clientconf.uploadzip; + const formidable = require( 'formidable' ); + const form = formidable( { multiples: false } ); + form.parse( req, function ( err, fields, files ) { + //console.log( files.data ) + var oldPath = files.data.path; + if( !Object.keys( clientconf.uploadzip ) + .includes( files.data.name ) ) { + return res.status( 403 ) + .send( { info: [ "notAllowed" ], models: "Tribes", moreinfo: `file ${files.data.name} not allowed to be upload` } ) + } else { + console.log( "context:", clientconf.uploadzip[ files.data.name ] ) + var newPath = `${config.tribes}/${req.session.header.xworkon}/${clientconf.uploadzip[files.data.name].dest}`; + //console.log( 'oldPath', oldPath ) + //console.log( 'newPath', `${newPath}/${files.data.name}` ) + fs.moveSync( oldPath, `${newPath}/${files.data.name}`, { overwrite: true } ); + const cp = require( 'child_process' ); + //console.log( `7z e -p${clientconf.uploadzip[ files.data.name ].psw} ${newPath}/${files.data.name}` ); + console.log( '7z', [ 'e', `-p${clientconf.uploadzip[ files.data.name ].psw}`, `${newPath}/${files.data.name}`, `-o${config.tribes}/${req.session.header.xworkon}/${clientconf.uploadzip[ files.data.name ].dest}`, clientconf.uploadzip[ files.data.name ].unzipoption ] ); + var newFiles = cp.spawnSync( '7z', [ 'e', `-p${clientconf.uploadzip[ files.data.name ].psw}`, `${newPath}/${files.data.name}`, `-o${config.tribes}/${req.session.header.xworkon}/${clientconf.uploadzip[ files.data.name ].dest}`, clientconf.uploadzip[ files.data.name ].unzipoption ] ); + console.log( newFiles.output.toString() ) + if( newFiles.output.toString() + .includes( 'Everything is Ok' ) ) { + if( clientconf.uploadzip[ files.data.name ].callback ) { + const integ = require( `${config.tribes}/${req.session.header.xworkon}/${clientconf.uploadzip[files.data.name].callback}` ) + .run(); + console.log( 'integration', integ ) + return res.status( integ.status ) + .send( integ.payload ); + } else { + return res.status( 200 ) + .send( { + info: [ "successfullsent" ], + models: "Tribes" + } ); + } + } else { + return res.status( 400 ) + .send( { + info: [ "zipfileerror" ], + models: "Tribes", + moreinfo: newFiles.output.toString() + } ) + } + } + } ) +} ); + +router.post( '/upload', checkHeaders, isAuthenticated, ( req, res ) => { + 1 // ACHANGER VIA usage sendjson + // url /Tribes/upload?save=tmp&rep=referentials/dataManagement + // if save=tmp then store in a tmp file + // if save=ok then mv the tmp file to the folder + // midlleware hasAccessrighton.js is not apply here only to access/update/create information inside an object + // to upload a file a user need accessrights to data: object: C or to Own it + // or if dir.file exist a .info.json into folder get shared as C in uuid accessright + /* + to add in front +
+ + +
+ */ + console.log( 'Envoie image' ) + console.log( 'body', req.body ); + console.log( 'params', req.params ); + //const authfolder = Tribes.checkaccessfiles( req.params.rep, 'C', req.session.header.accessrights, req.session.header.xpaganid ); + // cheack autorisation to create or replace a file for this accessrights user + const authfolder = { ok: "tt" } + if( authfolder.ok ) { + if( req.params.save == 'file' ) { + if( fs.existsSync( req.body.filepond ) ) { + fs.mv( req.body.filepond, req.params.rep ); + } + }; + // voir si c'est toujours pertinent car upload est géré par filepond pour les image + if( req.params.save == 'upload' ) { + const form = formidable( { multiples: false } ); + form.parse( req, ( err, fields, files ) => { + if( err ) { next( err ); return; } + let thefile = files.filebond.path; + fs.outputFileSync() + console.log( 'thefile:' + thefile ); + res.writeHead( 200, { 'Content-Type': 'text/plain' } ); + res.end( theFile ); + } ) + } + } else { + res.status( 403 ) + .send( 'forbiden access' ); + } +} ); +/* +Manage tribeid into /data/tribee/tribeid +client space dedicated + +@Todo +clientconfglob copy cut from Referentials.clientconfglob +clientconf.json copy cut from Referentials.clientconf +list of tribeid copy cut from Referentials. +Add a tribeid +update clientconf + + +*/ + + + +module.exports = router; diff --git a/routes/wwws.js b/routes/wwws.js new file mode 100644 index 0000000..8e304e4 --- /dev/null +++ b/routes/wwws.js @@ -0,0 +1,19 @@ +const express = require("express"); +const path = require("path"); + +// Classes +const Wwws = require("../models/Wwws.js"); +// Middlewares +const checkHeaders = require("../middlewares/checkHeaders"); +const isAuthenticated = require("../middlewares/isAuthenticated"); + +const router = express.Router(); +// GET api/wwws/conf/:tribeId/:website +// if profils accessright return the nginx conf in ${conf.dirtown}/tribes/${req.param.tribeId}/www/nginx_${req.params.tribeId}_${req.params.website}.conf +router.get("/conf/:tribeId/:website", checkHeaders, isAuthenticated, (req, res) => { + res.send(Www.configlist(req.params.tribeId)); +}); +router.post("/conf/:tribeId/:website", checkHeaders, isAuthenticated, (req, res) => { + res.send(Wwws.create(req.params.tribeId)); +}); +module.exports = router; diff --git a/setup/conf/initconf.json b/setup/conf/initconf.json new file mode 100644 index 0000000..ba5e649 --- /dev/null +++ b/setup/conf/initconf.json @@ -0,0 +1,34 @@ +{ + "dns": ["devfarm-ants"], + "towns": [ + { "townId": "wall", "nationId": "ants", "dns": "wall-ants.ndda.fr" } + ], + "api": { + "port": 3020, + "languages": ["en", "fr"], + "exposedHeaders": ["xdays", "xhash", "xalias", "xlang", "xtribe", "xapp","xuuid"], + "nationObjects": [ + "schema", + "blocks", + "nations", + "towns", + "tribes", + "pagans" + ], + "unittesting": ["middlewares", "models", "routes", "nationchains"], + "appset": { "trust proxy": true }, + "bodyparse": { + "urlencoded": { + "limit": "50mb", + "extended": true + }, + "json": { "limit": "500mb" } + } + }, + "nginx": { + "restart": "sudo systemctl restart nginx", + "worker_connections": 1024, + "include": ["/etc/nginx/conf.d/*.conf"], + "pageindex": "index_en.html" + } +} diff --git a/setup/conf/nginx.conf.mustache b/setup/conf/nginx.conf.mustache new file mode 100755 index 0000000..54caaae --- /dev/null +++ b/setup/conf/nginx.conf.mustache @@ -0,0 +1,43 @@ +user {{{sudoerUser}}}; +worker_processes auto; +error_log /var/log/nginx/error.log notice; +pid /var/run/nginx.pid; +#include /etc/nginx/modules-enabled/*.conf; + +events { + worker_connections 1024; +} +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + log_format main '$time_iso8601###$status###$request'; + + log_format tracker escape=json + '{"time":"$time_iso8601","alias":"$arg_alias","uuid":"$arg_uuid",' + '"lg":"$arg_lg","consentcookie":"$arg_consentcookie","version":"$arg_version",' + '"srckey":"$arg_srckey","request_filename":"$request_filename",' + '"remoteaddr":"$remote_addr","httpxforwardedfor":"$http_x_forwarded_for",' + '"httpreferer":"$http_referer","httpuseragent":"$http_user_agent","request":"$request"}'; + + log_format mainold '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + + sendfile on; + keepalive_timeout 65; + gzip on; + gzip_vary on; + gzip_proxied any; + gzip_comp_level 6; + gzip_buffers 4 32k; + gzip_http_version 1.1; + gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript application/x-font-ttf application/javascript font/eot font/opentype image/svg+xml image/x-icon text/plain; + + ## + # Virtual Host Configs + ## + {{#nginx.include}} + include {{{.}}}; + {{/nginx.include}} +} diff --git a/setup/conf/nginxmodelwebsite.conf.mustache b/setup/conf/nginxmodelwebsite.conf.mustache new file mode 100755 index 0000000..0481808 --- /dev/null +++ b/setup/conf/nginxmodelwebsite.conf.mustache @@ -0,0 +1,97 @@ +server { +server_name {{#dns}} {{.}} {{/dns}}; +access_log {{{nginx.logs}}}.access.log main; + +set $trackme 0; +if ( $uri ~ ^/trk/ ){ + set $trackme 1; +} +access_log {{{nginx.logs}}}.trk.log tracker if=$trackme ; +location ~* /trk/ { + if ( $uri ~ ^/trk/redirect ){ + return 301 $arg_url; + } + rewrite ^/trk/(.*)$ /$1; +} + +location /Checkjson.js { + alias {{{dirapi}}}/api/models/Checkjson.js; +} +location ~* /nationchains/(blocks|pagans|towns|nations)/ { +# Warning: never add tribes for keeping it private +root {{{dirapi}}}/; +} +location ~* /nationchains/models/ { + rewrite /nationchains/models/(.*$) /$1 break; + root {{{dirapi}}}/api/models/lg/; +} +location ~* /nationchains/schema/ { +#outside of nationchains for git purpose +rewrite /nationchains/schema/(.*$) /$1 break; +root {{{dirapi}}}/adminapi/schema/; +} +location ~* /{{tribeId}}/schema/{ + rewrite /{{tribeId}}/schema/(.*$) /$1 break; + root {{dirtown}}/tribes/{{tribeId}}/schema/; +} + +location ~* /{{tribeId}}/models/{ + rewrite /{{tribeId}}/models/(.*$) /$1 break; + root {{dirtown}}/tribes/{{tribeId}}/api/models/lg/; +} + +# /plugins/pluginame/components/xxx?plugin=pluginname&pluginkey=key +# acess if exist pluginkey +location /plugins/ { +add_header X-debug "plugins local $arg_plugin/keys/$arg_pluginkey sent"; +root {{{nginx.fswww}}}/plugins/; +if (-f {{{nginx.fswww}}}/plugins/$arg_plugin/keys/$arg_pluginkey) { +rewrite /plugins/([^/]+)/components/([^\?]+) /$1/components/$2 break; +} +return 403 "No valid token access for plugin:$arg_plugin with token:$arg_pluginkey please ask your admin"; +} + +location /cdn/ { + expires 1y; + add_header Cache-Control "public"; + rewrite /cdn/(.*$) /$1 break; + root {{{nginx.fswww}}}/cdn/; +} + +location /spacedev/ { +rewrite /spacedev/(.*$) /$1 break; +root {{{nginx.fswww}}}spacedev/{{{nginx.website}}}/dist/; +} + +location /api/ { +rewrite /api/(.*$) /$1 break; +proxy_pass http://localhost:{{{api.port}}}; +proxy_redirect off; +include proxy_params; +} + +location /apxwebapp/ { +rewrite /apxwebapp/(.*$) /$1 break; +root {{{dirapxwebapp}}}/apxwebapp/; +index index.html index_en.html; +} + +#to add htpasswd install apache2-utils => sudo htpasswd -c dirtown/tribes/tribeId/.htpasswd loginname passwd see man for +option + +location / { +{{#nginx.private}} +auth_basic "Mot de passe {{nginx.privatelogin}}"; +auth_basic_user_file {{dirtown}}/tribes/{{tribeId}}/.htpasswd; +{{/nginx.private}} +root {{{nginx.fswww}}}/{{{nginx.website}}}; +index index.html {{{nginx.pageindex}}}; +} +error_page 404 /404.html; +# redirect server error pages to the static page /50x.html +# +error_page 500 502 503 504 /50x.html; +location = /50x.html { +root /usr/local/nginx/html; +} +} \ No newline at end of file diff --git a/setup/conf/nginxproxyparams.mustache b/setup/conf/nginxproxyparams.mustache new file mode 100644 index 0000000..1b73dde --- /dev/null +++ b/setup/conf/nginxproxyparams.mustache @@ -0,0 +1,11 @@ + proxy_set_header Host $http_host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + client_max_body_size 10m; + client_body_buffer_size 128k; + proxy_connect_timeout 90; + proxy_send_timeout 90; + proxy_read_timeout 90; + proxy_buffers 32 4k; + proxy_set_header X-NginX-Proxy true; \ No newline at end of file diff --git a/setup/conf/setup_xx.json b/setup/conf/setup_xx.json new file mode 100644 index 0000000..6c31518 --- /dev/null +++ b/setup/conf/setup_xx.json @@ -0,0 +1 @@ +{"nationId":"ants","townId":"devfarm","dns":"devfarm-ants","comment":"Auto generate setup from apxtri after node apxtri nationId:value townId:value dns:domaine_to_access","mayorId":"philc"} diff --git a/unittest.js b/unittest.js new file mode 100644 index 0000000..d0e4c05 --- /dev/null +++ b/unittest.js @@ -0,0 +1,118 @@ +const fs = require("fs-extra"); +const glob = require("glob"); +const path = require("path"); +const process = require("process"); +//const config = require( './tribes/townconf.js' ); +/*const config = { + unittesting: ["middlewares", "models", "routes", "nationchains"], +};*/ + +global.__base = __dirname + "/"; +const ut = {}; +ut.help = ` +\x1b[32m###################################\x1b[0m +\x1b[32m### Make it simple Unit Testing ###\x1b[0m +\x1b[32m###################################\x1b[0m +Any file into a folder **/unittest/*.js can be run as a unit test. +Each file have to be independant to test a full process in production as well than in dev. +After each test the data has to be clean up (don't forget that your running test is apply into production). +Please add explicit research track message to help understand your code and how to debug it. + +yarn unittest [-Options] [filtername ] + +Options available: + \x1b[1m-verbose :\x1b[0m add system error to debug. + \x1b[1m-publish :\x1b[0m if test valid then push to production server list for propagation. + \x1b[1m-help :\x1b[0m this help. + +filername: any test file name usefull when you just want to test your code under dev + +Each unit testing file is structured like +\x1b[100m\x1b[32m +const assert=require('assert'); +const any components you require +... +any testing process from create to delete based on assert lib that allow to test if your test expected what it has to be +see https://nodejs.org/api/assert.html +... +const ut={name:"testing name", run: ()=>{the function to run}}; +module.exports=ut; +\x1b[0m + +To request any update or change in the code add to your git branch a new file into where you test your modifications /inittest/ +We first run your test and check with git diff your code before merge your branch with your inittest + +You'll be inform when a new release including your change into apxtri will be available. +\x1b[7mThanks for improving our democracy tool.\x1b[0m +`; +ut.run = (options) => { + if (options.filetotest.length == 0) + console.log( + "Check your files because no test to run (for more: yarn unittest -help)" + ); + options.filetotest.forEach((f) => { + const unittest = require(f); + console.log("\x1b[34m", `Unittest ${f}`, "\x1b[0m"); + try { + unittest.run(options); + console.log("✅", "\x1b[32m", unittest.name, "\x1b[0m"); + } catch (e) { + console.log( + "❌", + "\x1b[41m", + unittest.name, + e.stack.split("\n")[0], + "\x1b[0m" + ); + if (options.verbose) console.log(e.stack); + } + }); +}; +//get town conf +const infotown = fs.readJsonSync( + `${__dirname}/adminapi/www/adminapx/conf/setup_xx.json` +); +infotown.dirtown = path.resolve( + `${__dirname}/../${infotown.townId}-${infotown.nationId}` +); +const conf = fs.readJSONSync(`${infotown.dirtown}/conf.json`); +process.env.dirtown = infotown.dirtown; +const options = { filetotest: [] }; +process.argv.slice(2).forEach((arg) => { + switch (arg.substring(0, 2)) { + case "-v": + options.verbose = true; + break; + case "-p": + options.publish = true; + break; + case "-h": + options.active = true; + options.help = true; + break; + default: + options.active = true; + conf.api.unittesting.forEach((codefolder) => { + glob + .sync(`${__dirname}/api/${codefolder}/**/unittest/${arg}.js`) + .forEach((f) => { + if (!options.filetotest.includes(f)) options.filetotest.push(f); + }); + }); + break; + } +}); +if (!options.active) { + conf.api.unittesting.forEach((codefolder) => { + glob + .sync(`./${codefolder}/**/unittest/*.js`) + .forEach((f) => { + if (!options.filetotest.includes(f)) options.filetotest.push(f); + }); + }); + console.log( + "You can test only some unittest by passing yarn unittest name where name is a /unittest/name.js file that exist, type 'yarn unittest -help' for more" + ); + console.log("Looking for unittest folder into ", conf.api.unittesting); +} +options.help ? console.log(ut.help) : ut.run(options); diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 0000000..61a2ccd --- /dev/null +++ b/yarn.lock @@ -0,0 +1,3345 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@colors/colors@1.6.0", "@colors/colors@^1.6.0": + version "1.6.0" + resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.6.0.tgz#ec6cd237440700bc23ca23087f513c75508958b0" + integrity sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA== + +"@dabh/diagnostics@^2.0.2": + version "2.0.3" + resolved "https://registry.yarnpkg.com/@dabh/diagnostics/-/diagnostics-2.0.3.tgz#7f7e97ee9a725dffc7808d93668cc984e1dc477a" + integrity sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA== + dependencies: + colorspace "1.1.x" + enabled "2.0.x" + kuler "^2.0.0" + +"@discoveryjs/json-ext@^0.5.0": + version "0.5.7" + resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz#1d572bfbbe14b7704e0ba0f39b74815b84870d70" + integrity sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw== + +"@editorjs/editorjs@^2.26.5": + version "2.28.2" + resolved "https://registry.yarnpkg.com/@editorjs/editorjs/-/editorjs-2.28.2.tgz#a265c7d10e83adef81813e4dc0f01fe3464dff50" + integrity sha512-g6V0Nd3W9IIWMpvxDNTssQ6e4kxBp1Y0W4GIf8cXRlmcBp3TUjrgCYJQmNy3l2a6ZzhyBAoVSe8krJEq4g7PQw== + +"@esbuild/android-arm64@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.16.17.tgz#cf91e86df127aa3d141744edafcba0abdc577d23" + integrity sha512-MIGl6p5sc3RDTLLkYL1MyL8BMRN4tLMRCn+yRJJmEDvYZ2M7tmAf80hx1kbNEUX2KJ50RRtxZ4JHLvCfuB6kBg== + +"@esbuild/android-arm@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.16.17.tgz#025b6246d3f68b7bbaa97069144fb5fb70f2fff2" + integrity sha512-N9x1CMXVhtWEAMS7pNNONyA14f71VPQN9Cnavj1XQh6T7bskqiLLrSca4O0Vr8Wdcga943eThxnVp3JLnBMYtw== + +"@esbuild/android-x64@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.16.17.tgz#c820e0fef982f99a85c4b8bfdd582835f04cd96e" + integrity sha512-a3kTv3m0Ghh4z1DaFEuEDfz3OLONKuFvI4Xqczqx4BqLyuFaFkuaG4j2MtA6fuWEFeC5x9IvqnX7drmRq/fyAQ== + +"@esbuild/darwin-arm64@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.16.17.tgz#edef4487af6b21afabba7be5132c26d22379b220" + integrity sha512-/2agbUEfmxWHi9ARTX6OQ/KgXnOWfsNlTeLcoV7HSuSTv63E4DqtAc+2XqGw1KHxKMHGZgbVCZge7HXWX9Vn+w== + +"@esbuild/darwin-x64@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.16.17.tgz#42829168730071c41ef0d028d8319eea0e2904b4" + integrity sha512-2By45OBHulkd9Svy5IOCZt376Aa2oOkiE9QWUK9fe6Tb+WDr8hXL3dpqi+DeLiMed8tVXspzsTAvd0jUl96wmg== + +"@esbuild/freebsd-arm64@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.16.17.tgz#1f4af488bfc7e9ced04207034d398e793b570a27" + integrity sha512-mt+cxZe1tVx489VTb4mBAOo2aKSnJ33L9fr25JXpqQqzbUIw/yzIzi+NHwAXK2qYV1lEFp4OoVeThGjUbmWmdw== + +"@esbuild/freebsd-x64@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.16.17.tgz#636306f19e9bc981e06aa1d777302dad8fddaf72" + integrity sha512-8ScTdNJl5idAKjH8zGAsN7RuWcyHG3BAvMNpKOBaqqR7EbUhhVHOqXRdL7oZvz8WNHL2pr5+eIT5c65kA6NHug== + +"@esbuild/linux-arm64@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.16.17.tgz#a003f7ff237c501e095d4f3a09e58fc7b25a4aca" + integrity sha512-7S8gJnSlqKGVJunnMCrXHU9Q8Q/tQIxk/xL8BqAP64wchPCTzuM6W3Ra8cIa1HIflAvDnNOt2jaL17vaW+1V0g== + +"@esbuild/linux-arm@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.16.17.tgz#b591e6a59d9c4fe0eeadd4874b157ab78cf5f196" + integrity sha512-iihzrWbD4gIT7j3caMzKb/RsFFHCwqqbrbH9SqUSRrdXkXaygSZCZg1FybsZz57Ju7N/SHEgPyaR0LZ8Zbe9gQ== + +"@esbuild/linux-ia32@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.16.17.tgz#24333a11027ef46a18f57019450a5188918e2a54" + integrity sha512-kiX69+wcPAdgl3Lonh1VI7MBr16nktEvOfViszBSxygRQqSpzv7BffMKRPMFwzeJGPxcio0pdD3kYQGpqQ2SSg== + +"@esbuild/linux-loong64@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.16.17.tgz#d5ad459d41ed42bbd4d005256b31882ec52227d8" + integrity sha512-dTzNnQwembNDhd654cA4QhbS9uDdXC3TKqMJjgOWsC0yNCbpzfWoXdZvp0mY7HU6nzk5E0zpRGGx3qoQg8T2DQ== + +"@esbuild/linux-mips64el@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.16.17.tgz#4e5967a665c38360b0a8205594377d4dcf9c3726" + integrity sha512-ezbDkp2nDl0PfIUn0CsQ30kxfcLTlcx4Foz2kYv8qdC6ia2oX5Q3E/8m6lq84Dj/6b0FrkgD582fJMIfHhJfSw== + +"@esbuild/linux-ppc64@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.16.17.tgz#206443a02eb568f9fdf0b438fbd47d26e735afc8" + integrity sha512-dzS678gYD1lJsW73zrFhDApLVdM3cUF2MvAa1D8K8KtcSKdLBPP4zZSLy6LFZ0jYqQdQ29bjAHJDgz0rVbLB3g== + +"@esbuild/linux-riscv64@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.16.17.tgz#c351e433d009bf256e798ad048152c8d76da2fc9" + integrity sha512-ylNlVsxuFjZK8DQtNUwiMskh6nT0vI7kYl/4fZgV1llP5d6+HIeL/vmmm3jpuoo8+NuXjQVZxmKuhDApK0/cKw== + +"@esbuild/linux-s390x@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.16.17.tgz#661f271e5d59615b84b6801d1c2123ad13d9bd87" + integrity sha512-gzy7nUTO4UA4oZ2wAMXPNBGTzZFP7mss3aKR2hH+/4UUkCOyqmjXiKpzGrY2TlEUhbbejzXVKKGazYcQTZWA/w== + +"@esbuild/linux-x64@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.16.17.tgz#e4ba18e8b149a89c982351443a377c723762b85f" + integrity sha512-mdPjPxfnmoqhgpiEArqi4egmBAMYvaObgn4poorpUaqmvzzbvqbowRllQ+ZgzGVMGKaPkqUmPDOOFQRUFDmeUw== + +"@esbuild/netbsd-x64@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.16.17.tgz#7d4f4041e30c5c07dd24ffa295c73f06038ec775" + integrity sha512-/PzmzD/zyAeTUsduZa32bn0ORug+Jd1EGGAUJvqfeixoEISYpGnAezN6lnJoskauoai0Jrs+XSyvDhppCPoKOA== + +"@esbuild/openbsd-x64@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.16.17.tgz#970fa7f8470681f3e6b1db0cc421a4af8060ec35" + integrity sha512-2yaWJhvxGEz2RiftSk0UObqJa/b+rIAjnODJgv2GbGGpRwAfpgzyrg1WLK8rqA24mfZa9GvpjLcBBg8JHkoodg== + +"@esbuild/sunos-x64@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.16.17.tgz#abc60e7c4abf8b89fb7a4fe69a1484132238022c" + integrity sha512-xtVUiev38tN0R3g8VhRfN7Zl42YCJvyBhRKw1RJjwE1d2emWTVToPLNEQj/5Qxc6lVFATDiy6LjVHYhIPrLxzw== + +"@esbuild/win32-arm64@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.16.17.tgz#7b0ff9e8c3265537a7a7b1fd9a24e7bd39fcd87a" + integrity sha512-ga8+JqBDHY4b6fQAmOgtJJue36scANy4l/rL97W+0wYmijhxKetzZdKOJI7olaBaMhWt8Pac2McJdZLxXWUEQw== + +"@esbuild/win32-ia32@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.16.17.tgz#e90fe5267d71a7b7567afdc403dfd198c292eb09" + integrity sha512-WnsKaf46uSSF/sZhwnqE4L/F89AYNMiD4YtEcYekBt9Q7nj0DiId2XH2Ng2PHM54qi5oPrQ8luuzGszqi/veig== + +"@esbuild/win32-x64@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.16.17.tgz#c5a1a4bfe1b57f0c3e61b29883525c6da3e5c091" + integrity sha512-y+EHuSchhL7FjHgvQL/0fnnFmO4T1bhvWANX6gcnqTjtnKWbTvUMCpGnv2+t+31d7RzyEAYAd4u2fnIhHL6N/Q== + +"@jridgewell/gen-mapping@^0.3.0": + version "0.3.3" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz#7e02e6eb5df901aaedb08514203b096614024098" + integrity sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ== + dependencies: + "@jridgewell/set-array" "^1.0.1" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping" "^0.3.9" + +"@jridgewell/resolve-uri@^3.1.0": + version "3.1.1" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz#c08679063f279615a3326583ba3a90d1d82cc721" + integrity sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA== + +"@jridgewell/set-array@^1.0.1": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" + integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== + +"@jridgewell/source-map@^0.3.3": + version "0.3.5" + resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.5.tgz#a3bb4d5c6825aab0d281268f47f6ad5853431e91" + integrity sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ== + dependencies: + "@jridgewell/gen-mapping" "^0.3.0" + "@jridgewell/trace-mapping" "^0.3.9" + +"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14": + version "1.4.15" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" + integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== + +"@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.9": + version "0.3.20" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz#72e45707cf240fa6b081d0366f8265b0cd10197f" + integrity sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q== + dependencies: + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" + +"@mapbox/node-pre-gyp@^1.0.11": + version "1.0.11" + resolved "https://registry.yarnpkg.com/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz#417db42b7f5323d79e93b34a6d7a2a12c0df43fa" + integrity sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ== + dependencies: + detect-libc "^2.0.0" + https-proxy-agent "^5.0.0" + make-dir "^3.1.0" + node-fetch "^2.6.7" + nopt "^5.0.0" + npmlog "^5.0.1" + rimraf "^3.0.2" + semver "^7.3.5" + tar "^6.1.11" + +"@opencensus/core@0.0.9": + version "0.0.9" + resolved "https://registry.yarnpkg.com/@opencensus/core/-/core-0.0.9.tgz#b16f775435ee309433e4126af194d37313fc93b3" + integrity sha512-31Q4VWtbzXpVUd2m9JS6HEaPjlKvNMOiF7lWKNmXF84yUcgfAFL5re7/hjDmdyQbOp32oGc+RFV78jXIldVz6Q== + dependencies: + continuation-local-storage "^3.2.1" + log-driver "^1.2.7" + semver "^5.5.0" + shimmer "^1.2.0" + uuid "^3.2.1" + +"@opencensus/core@^0.0.8": + version "0.0.8" + resolved "https://registry.yarnpkg.com/@opencensus/core/-/core-0.0.8.tgz#df01f200c2d2fbfe14dae129a1a86fb87286db92" + integrity sha512-yUFT59SFhGMYQgX0PhoTR0LBff2BEhPrD9io1jWfF/VDbakRfs6Pq60rjv0Z7iaTav5gQlttJCX2+VPxFWCuoQ== + dependencies: + continuation-local-storage "^3.2.1" + log-driver "^1.2.7" + semver "^5.5.0" + shimmer "^1.2.0" + uuid "^3.2.1" + +"@opencensus/propagation-b3@0.0.8": + version "0.0.8" + resolved "https://registry.yarnpkg.com/@opencensus/propagation-b3/-/propagation-b3-0.0.8.tgz#0751e6fd75f09400d9d3c419001e9e15a0df68e9" + integrity sha512-PffXX2AL8Sh0VHQ52jJC4u3T0H6wDK6N/4bg7xh4ngMYOIi13aR1kzVvX1sVDBgfGwDOkMbl4c54Xm3tlPx/+A== + dependencies: + "@opencensus/core" "^0.0.8" + uuid "^3.2.1" + +"@pm2/agent@~2.0.0": + version "2.0.3" + resolved "https://registry.yarnpkg.com/@pm2/agent/-/agent-2.0.3.tgz#6b47fda837f185864767fe1e048f61d1de31fc45" + integrity sha512-xkqqCoTf5VsciMqN0vb9jthW7olVAi4KRFNddCc7ZkeJZ3i8QwZANr4NSH2H5DvseRFHq7MiPspRY/EWAFWWTg== + dependencies: + async "~3.2.0" + chalk "~3.0.0" + dayjs "~1.8.24" + debug "~4.3.1" + eventemitter2 "~5.0.1" + fast-json-patch "^3.0.0-1" + fclone "~1.0.11" + nssocket "0.6.0" + pm2-axon "~4.0.1" + pm2-axon-rpc "~0.7.0" + proxy-agent "~6.3.0" + semver "~7.5.0" + ws "~7.4.0" + +"@pm2/io@~5.0.0": + version "5.0.2" + resolved "https://registry.yarnpkg.com/@pm2/io/-/io-5.0.2.tgz#5e4177281280082d7c490bb776fad7f8448c6bca" + integrity sha512-XAvrNoQPKOyO/jJyCu8jPhLzlyp35MEf7w/carHXmWKddPzeNOFSEpSEqMzPDawsvpxbE+i918cNN+MwgVsStA== + dependencies: + "@opencensus/core" "0.0.9" + "@opencensus/propagation-b3" "0.0.8" + async "~2.6.1" + debug "~4.3.1" + eventemitter2 "^6.3.1" + require-in-the-middle "^5.0.0" + semver "~7.5.4" + shimmer "^1.2.0" + signal-exit "^3.0.3" + tslib "1.9.3" + +"@pm2/js-api@~0.6.7": + version "0.6.7" + resolved "https://registry.yarnpkg.com/@pm2/js-api/-/js-api-0.6.7.tgz#ed28c3b7b6d26f03f826318754fdc5468afa589f" + integrity sha512-jiJUhbdsK+5C4zhPZNnyA3wRI01dEc6a2GhcQ9qI38DyIk+S+C8iC3fGjcjUbt/viLYKPjlAaE+hcT2/JMQPXw== + dependencies: + async "^2.6.3" + axios "^0.21.0" + debug "~4.3.1" + eventemitter2 "^6.3.1" + ws "^7.0.0" + +"@pm2/pm2-version-check@latest": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@pm2/pm2-version-check/-/pm2-version-check-1.0.4.tgz#cf97fbb14b0eca95430ca05eedccbd2683806e43" + integrity sha512-SXsM27SGH3yTWKc2fKR4SYNxsmnvuBQ9dd6QHtEWmiZ/VqaOYPAIlS8+vMcn27YLtAEBGvNRSh3TPNvtjZgfqA== + dependencies: + debug "^4.3.1" + +"@tootallnate/quickjs-emscripten@^0.23.0": + version "0.23.0" + resolved "https://registry.yarnpkg.com/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz#db4ecfd499a9765ab24002c3b696d02e6d32a12c" + integrity sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA== + +"@types/eslint-scope@^3.7.3": + version "3.7.7" + resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.7.tgz#3108bd5f18b0cdb277c867b3dd449c9ed7079ac5" + integrity sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg== + dependencies: + "@types/eslint" "*" + "@types/estree" "*" + +"@types/eslint@*": + version "8.44.8" + resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-8.44.8.tgz#f4fe1dab9b3d3dd98082d4b9f80e59ab40f1261c" + integrity sha512-4K8GavROwhrYl2QXDXm0Rv9epkA8GBFu0EI+XrrnnuCl7u8CWBRusX7fXJfanhZTDWSAL24gDI/UqXyUM0Injw== + dependencies: + "@types/estree" "*" + "@types/json-schema" "*" + +"@types/estree@*", "@types/estree@^1.0.0": + version "1.0.5" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.5.tgz#a6ce3e556e00fd9895dd872dd172ad0d4bd687f4" + integrity sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw== + +"@types/json-schema@*", "@types/json-schema@^7.0.8": + version "7.0.15" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" + integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== + +"@types/node@*", "@types/node@>=8.1.0": + version "20.10.3" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.10.3.tgz#4900adcc7fc189d5af5bb41da8f543cea6962030" + integrity sha512-XJavIpZqiXID5Yxnxv3RUDKTN5b81ddNC3ecsA0SoFXz/QU8OGBwZGMomiq0zw+uuqbL/krztv/DINAQ/EV4gg== + dependencies: + undici-types "~5.26.4" + +"@types/triple-beam@^1.3.2": + version "1.3.5" + resolved "https://registry.yarnpkg.com/@types/triple-beam/-/triple-beam-1.3.5.tgz#74fef9ffbaa198eb8b588be029f38b00299caa2c" + integrity sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw== + +"@webassemblyjs/ast@1.11.6", "@webassemblyjs/ast@^1.11.5": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.11.6.tgz#db046555d3c413f8966ca50a95176a0e2c642e24" + integrity sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q== + dependencies: + "@webassemblyjs/helper-numbers" "1.11.6" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + +"@webassemblyjs/floating-point-hex-parser@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz#dacbcb95aff135c8260f77fa3b4c5fea600a6431" + integrity sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw== + +"@webassemblyjs/helper-api-error@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz#6132f68c4acd59dcd141c44b18cbebbd9f2fa768" + integrity sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q== + +"@webassemblyjs/helper-buffer@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.6.tgz#b66d73c43e296fd5e88006f18524feb0f2c7c093" + integrity sha512-z3nFzdcp1mb8nEOFFk8DrYLpHvhKC3grJD2ardfKOzmbmJvEf/tPIqCY+sNcwZIY8ZD7IkB2l7/pqhUhqm7hLA== + +"@webassemblyjs/helper-numbers@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz#cbce5e7e0c1bd32cf4905ae444ef64cea919f1b5" + integrity sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g== + dependencies: + "@webassemblyjs/floating-point-hex-parser" "1.11.6" + "@webassemblyjs/helper-api-error" "1.11.6" + "@xtuc/long" "4.2.2" + +"@webassemblyjs/helper-wasm-bytecode@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz#bb2ebdb3b83aa26d9baad4c46d4315283acd51e9" + integrity sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA== + +"@webassemblyjs/helper-wasm-section@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.6.tgz#ff97f3863c55ee7f580fd5c41a381e9def4aa577" + integrity sha512-LPpZbSOwTpEC2cgn4hTydySy1Ke+XEu+ETXuoyvuyezHO3Kjdu90KK95Sh9xTbmjrCsUwvWwCOQQNta37VrS9g== + dependencies: + "@webassemblyjs/ast" "1.11.6" + "@webassemblyjs/helper-buffer" "1.11.6" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + "@webassemblyjs/wasm-gen" "1.11.6" + +"@webassemblyjs/ieee754@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz#bb665c91d0b14fffceb0e38298c329af043c6e3a" + integrity sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg== + dependencies: + "@xtuc/ieee754" "^1.2.0" + +"@webassemblyjs/leb128@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.11.6.tgz#70e60e5e82f9ac81118bc25381a0b283893240d7" + integrity sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ== + dependencies: + "@xtuc/long" "4.2.2" + +"@webassemblyjs/utf8@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.11.6.tgz#90f8bc34c561595fe156603be7253cdbcd0fab5a" + integrity sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA== + +"@webassemblyjs/wasm-edit@^1.11.5": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.6.tgz#c72fa8220524c9b416249f3d94c2958dfe70ceab" + integrity sha512-Ybn2I6fnfIGuCR+Faaz7YcvtBKxvoLV3Lebn1tM4o/IAJzmi9AWYIPWpyBfU8cC+JxAO57bk4+zdsTjJR+VTOw== + dependencies: + "@webassemblyjs/ast" "1.11.6" + "@webassemblyjs/helper-buffer" "1.11.6" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + "@webassemblyjs/helper-wasm-section" "1.11.6" + "@webassemblyjs/wasm-gen" "1.11.6" + "@webassemblyjs/wasm-opt" "1.11.6" + "@webassemblyjs/wasm-parser" "1.11.6" + "@webassemblyjs/wast-printer" "1.11.6" + +"@webassemblyjs/wasm-gen@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.6.tgz#fb5283e0e8b4551cc4e9c3c0d7184a65faf7c268" + integrity sha512-3XOqkZP/y6B4F0PBAXvI1/bky7GryoogUtfwExeP/v7Nzwo1QLcq5oQmpKlftZLbT+ERUOAZVQjuNVak6UXjPA== + dependencies: + "@webassemblyjs/ast" "1.11.6" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + "@webassemblyjs/ieee754" "1.11.6" + "@webassemblyjs/leb128" "1.11.6" + "@webassemblyjs/utf8" "1.11.6" + +"@webassemblyjs/wasm-opt@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.6.tgz#d9a22d651248422ca498b09aa3232a81041487c2" + integrity sha512-cOrKuLRE7PCe6AsOVl7WasYf3wbSo4CeOk6PkrjS7g57MFfVUF9u6ysQBBODX0LdgSvQqRiGz3CXvIDKcPNy4g== + dependencies: + "@webassemblyjs/ast" "1.11.6" + "@webassemblyjs/helper-buffer" "1.11.6" + "@webassemblyjs/wasm-gen" "1.11.6" + "@webassemblyjs/wasm-parser" "1.11.6" + +"@webassemblyjs/wasm-parser@1.11.6", "@webassemblyjs/wasm-parser@^1.11.5": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.6.tgz#bb85378c527df824004812bbdb784eea539174a1" + integrity sha512-6ZwPeGzMJM3Dqp3hCsLgESxBGtT/OeCvCZ4TA1JUPYgmhAx38tTPR9JaKy0S5H3evQpO/h2uWs2j6Yc/fjkpTQ== + dependencies: + "@webassemblyjs/ast" "1.11.6" + "@webassemblyjs/helper-api-error" "1.11.6" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + "@webassemblyjs/ieee754" "1.11.6" + "@webassemblyjs/leb128" "1.11.6" + "@webassemblyjs/utf8" "1.11.6" + +"@webassemblyjs/wast-printer@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.11.6.tgz#a7bf8dd7e362aeb1668ff43f35cb849f188eff20" + integrity sha512-JM7AhRcE+yW2GWYaKeHL5vt4xqee5N2WcezptmgyhNS+ScggqcT1OtXykhAb13Sn5Yas0j2uv9tHgrjwvzAP4A== + dependencies: + "@webassemblyjs/ast" "1.11.6" + "@xtuc/long" "4.2.2" + +"@webpack-cli/configtest@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@webpack-cli/configtest/-/configtest-1.2.0.tgz#7b20ce1c12533912c3b217ea68262365fa29a6f5" + integrity sha512-4FB8Tj6xyVkyqjj1OaTqCjXYULB9FMkqQ8yGrZjRDrYh0nOE+7Lhs45WioWQQMV+ceFlE368Ukhe6xdvJM9Egg== + +"@webpack-cli/info@^1.5.0": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@webpack-cli/info/-/info-1.5.0.tgz#6c78c13c5874852d6e2dd17f08a41f3fe4c261b1" + integrity sha512-e8tSXZpw2hPl2uMJY6fsMswaok5FdlGNRTktvFk2sD8RjH0hE2+XistawJx1vmKteh4NmGmNUrp+Tb2w+udPcQ== + dependencies: + envinfo "^7.7.3" + +"@webpack-cli/serve@^1.7.0": + version "1.7.0" + resolved "https://registry.yarnpkg.com/@webpack-cli/serve/-/serve-1.7.0.tgz#e1993689ac42d2b16e9194376cfb6753f6254db1" + integrity sha512-oxnCNGj88fL+xzV+dacXs44HcDwf1ovs3AuEzvP7mqXw7fQntqIhQ1BRmynh4qEKQSSSRSWVyXRjmTbZIX9V2Q== + +"@xtuc/ieee754@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790" + integrity sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA== + +"@xtuc/long@4.2.2": + version "4.2.2" + resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d" + integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ== + +abbrev@1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" + integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== + +accepts@~1.3.8: + version "1.3.8" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" + integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw== + dependencies: + mime-types "~2.1.34" + negotiator "0.6.3" + +acorn-import-assertions@^1.9.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz#507276249d684797c84e0734ef84860334cfb1ac" + integrity sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA== + +acorn@^8.7.1, acorn@^8.8.2: + version "8.11.2" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.11.2.tgz#ca0d78b51895be5390a5903c5b3bdcdaf78ae40b" + integrity sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w== + +agent-base@6: + version "6.0.2" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" + integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== + dependencies: + debug "4" + +agent-base@^7.0.2, agent-base@^7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.0.tgz#536802b76bc0b34aa50195eb2442276d613e3434" + integrity sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg== + dependencies: + debug "^4.3.4" + +ajv-keywords@^3.5.2: + version "3.5.2" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" + integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== + +ajv@^6.12.5: + version "6.12.6" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +amp-message@~0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/amp-message/-/amp-message-0.1.2.tgz#a78f1c98995087ad36192a41298e4db49e3dfc45" + integrity sha512-JqutcFwoU1+jhv7ArgW38bqrE+LQdcRv4NxNw0mp0JHQyB6tXesWRjtYKlDgHRY2o3JE5UTaBGUK8kSWUdxWUg== + dependencies: + amp "0.3.1" + +amp@0.3.1, amp@~0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/amp/-/amp-0.3.1.tgz#6adf8d58a74f361e82c1fa8d389c079e139fc47d" + integrity sha512-OwIuC4yZaRogHKiuU5WlMR5Xk/jAcpPtawWL05Gj8Lvm2F6mwoJt4O/bHI+DHwG79vWd+8OFYM4/BzYqyRd3qw== + +ansi-colors@^4.1.1: + version "4.1.3" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.3.tgz#37611340eb2243e70cc604cad35d63270d48781b" + integrity sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw== + +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + +ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +anymatch@~3.1.2: + version "3.1.3" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" + integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + +apidoc@^0.54.0: + version "0.54.0" + resolved "https://registry.yarnpkg.com/apidoc/-/apidoc-0.54.0.tgz#b30d3c46ef451ed4fe024c18a7ffbda978bf5fd5" + integrity sha512-VCOdwkAaFK7bDLbiAiFKqX8SVlAnk7sQCXDARwshBQpVRqRGDNY993kAMPkWmSD1RCVBDMFIOgOfOR9mcISNZQ== + dependencies: + bootstrap "3.4.1" + commander "^8.3.0" + diff-match-patch "^1.0.5" + esbuild-loader "^2.16.0" + expose-loader "^3.1.0" + fs-extra "^10.0.0" + glob "^7.2.0" + handlebars "^4.7.7" + iconv-lite "^0.6.3" + jquery "^3.6.0" + klaw-sync "^6.0.0" + lodash "^4.17.21" + markdown-it "^12.2.0" + nodemon "^2.0.15" + prismjs "^1.25.0" + semver "^7.3.5" + style-loader "^3.3.1" + webpack "^5.64.2" + webpack-cli "^4.9.1" + winston "^3.3.3" + +"aproba@^1.0.3 || ^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/aproba/-/aproba-2.0.0.tgz#52520b8ae5b569215b354efc0caa3fe1e45a8adc" + integrity sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ== + +are-we-there-yet@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz#372e0e7bd279d8e94c653aaa1f67200884bf3e1c" + integrity sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw== + dependencies: + delegates "^1.0.0" + readable-stream "^3.6.0" + +argparse@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== + dependencies: + sprintf-js "~1.0.2" + +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + +array-flatten@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" + integrity sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg== + +asap@^2.0.0: + version "2.0.6" + resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" + integrity sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA== + +asn1.js@^5.0.0: + version "5.4.1" + resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-5.4.1.tgz#11a980b84ebb91781ce35b0fdc2ee294e3783f07" + integrity sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA== + dependencies: + bn.js "^4.0.0" + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + safer-buffer "^2.1.0" + +ast-types@^0.13.4: + version "0.13.4" + resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.13.4.tgz#ee0d77b343263965ecc3fb62da16e7222b2b6782" + integrity sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w== + dependencies: + tslib "^2.0.1" + +async-listener@^0.6.0: + version "0.6.10" + resolved "https://registry.yarnpkg.com/async-listener/-/async-listener-0.6.10.tgz#a7c97abe570ba602d782273c0de60a51e3e17cbc" + integrity sha512-gpuo6xOyF4D5DE5WvyqZdPA3NGhiT6Qf07l7DCB0wwDEsLvDIbCr6j9S5aj5Ch96dLace5tXVzWBZkxU/c5ohw== + dependencies: + semver "^5.3.0" + shimmer "^1.1.0" + +async@^2.6.3, async@~2.6.1: + version "2.6.4" + resolved "https://registry.yarnpkg.com/async/-/async-2.6.4.tgz#706b7ff6084664cd7eae713f6f965433b5504221" + integrity sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA== + dependencies: + lodash "^4.17.14" + +async@^3.2.0, async@^3.2.3, async@~3.2.0: + version "3.2.5" + resolved "https://registry.yarnpkg.com/async/-/async-3.2.5.tgz#ebd52a8fdaf7a2289a24df399f8d8485c8a46b66" + integrity sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg== + +axios@^0.21.0, axios@^0.21.1: + version "0.21.4" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.4.tgz#c67b90dc0568e5c1cf2b0b858c43ba28e2eda575" + integrity sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg== + dependencies: + follow-redirects "^1.14.0" + +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + +basic-ftp@^5.0.2: + version "5.0.3" + resolved "https://registry.yarnpkg.com/basic-ftp/-/basic-ftp-5.0.3.tgz#b14c0fe8111ce001ec913686434fe0c2fb461228" + integrity sha512-QHX8HLlncOLpy54mh+k/sWIFd0ThmRqwe9ZjELybGZK+tZ8rUb9VO0saKJUROTbE+KhzDUT7xziGpGrW8Kmd+g== + +baunsu@^0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/baunsu/-/baunsu-0.2.3.tgz#03545ed2ea49382f3091707459265b6df5a15f60" + integrity sha512-GAfhGakOVMIeBaPH70Iu3MZycb/7aWWruJXMw+54HVlVi0hiYY2qMQrsui8Q1UiYIHw/SiDhaZ839ghhoJGAtQ== + dependencies: + encoding "0.1.x" + hasshu "0.2.x" + +bcrypt@^5.0.0: + version "5.1.1" + resolved "https://registry.yarnpkg.com/bcrypt/-/bcrypt-5.1.1.tgz#0f732c6dcb4e12e5b70a25e326a72965879ba6e2" + integrity sha512-AGBHOG5hPYZ5Xl9KXzU5iKq9516yEmvCKDg3ecP5kX2aB6UqTeXZxk2ELnDgDm6BQSMlLt9rDB4LoSMx0rYwww== + dependencies: + "@mapbox/node-pre-gyp" "^1.0.11" + node-addon-api "^5.0.0" + +big.js@^5.2.2: + version "5.2.2" + resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" + integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ== + +binary-extensions@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" + integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== + +blessed@0.1.81: + version "0.1.81" + resolved "https://registry.yarnpkg.com/blessed/-/blessed-0.1.81.tgz#f962d687ec2c369570ae71af843256e6d0ca1129" + integrity sha512-LoF5gae+hlmfORcG1M5+5XZi4LBmvlXTzwJWzUlPryN/SJdSflZvROM2TwkT0GMpq7oqT48NRd4GS7BiVBc5OQ== + +bn.js@^4.0.0: + version "4.12.0" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88" + integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA== + +bodec@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/bodec/-/bodec-0.1.0.tgz#bc851555430f23c9f7650a75ef64c6a94c3418cc" + integrity sha512-Ylo+MAo5BDUq1KA3f3R/MFhh+g8cnHmo8bz3YPGhI1znrMaf77ol1sfvYJzsw3nTE+Y2GryfDxBaR+AqpAkEHQ== + +body-parser@1.20.1: + version "1.20.1" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.1.tgz#b1812a8912c195cd371a3ee5e66faa2338a5c668" + integrity sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw== + dependencies: + bytes "3.1.2" + content-type "~1.0.4" + debug "2.6.9" + depd "2.0.0" + destroy "1.2.0" + http-errors "2.0.0" + iconv-lite "0.4.24" + on-finished "2.4.1" + qs "6.11.0" + raw-body "2.5.1" + type-is "~1.6.18" + unpipe "1.0.0" + +bootstrap@3.4.1: + version "3.4.1" + resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-3.4.1.tgz#c3a347d419e289ad11f4033e3c4132b87c081d72" + integrity sha512-yN5oZVmRCwe5aKwzRj6736nSmKDX7pLYwsXiCj/EYmo16hODaBiT4En5btW/jhBF/seV+XMx3aYwukYC3A49DA== + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +braces@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" + integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== + dependencies: + fill-range "^7.0.1" + +browserslist@^4.14.5: + version "4.22.2" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.22.2.tgz#704c4943072bd81ea18997f3bd2180e89c77874b" + integrity sha512-0UgcrvQmBDvZHFGdYUehrCNIazki7/lUP3kkoi/r3YB2amZbFM9J43ZRkJTXBUZK4gmx56+Sqk9+Vs9mwZx9+A== + dependencies: + caniuse-lite "^1.0.30001565" + electron-to-chromium "^1.4.601" + node-releases "^2.0.14" + update-browserslist-db "^1.0.13" + +buffer-from@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" + integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== + +bytes@3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" + integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== + +call-bind@^1.0.0: + version "1.0.5" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.5.tgz#6fa2b7845ce0ea49bf4d8b9ef64727a2c2e2e513" + integrity sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ== + dependencies: + function-bind "^1.1.2" + get-intrinsic "^1.2.1" + set-function-length "^1.1.1" + +caniuse-lite@^1.0.30001565: + version "1.0.30001566" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001566.tgz#61a8e17caf3752e3e426d4239c549ebbb37fef0d" + integrity sha512-ggIhCsTxmITBAMmK8yZjEhCO5/47jKXPu6Dha/wuCS4JePVL+3uiDEBuhu2aIoT+bqTOR8L76Ip1ARL9xYsEJA== + +chalk@3.0.0, chalk@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4" + integrity sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +charm@~0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/charm/-/charm-0.1.2.tgz#06c21eed1a1b06aeb67553cdc53e23274bac2296" + integrity sha512-syedaZ9cPe7r3hoQA9twWYKu5AIyCswN5+szkmPBe9ccdLrj4bYaCnLVPTLd2kgVRc7+zoX4tyPgRnFKCj5YjQ== + +chokidar@^3.5.2, chokidar@^3.5.3: + version "3.5.3" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" + integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== + dependencies: + anymatch "~3.1.2" + braces "~3.0.2" + glob-parent "~5.1.2" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.6.0" + optionalDependencies: + fsevents "~2.3.2" + +chownr@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece" + integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== + +chrome-trace-event@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz#1015eced4741e15d06664a957dbbf50d041e26ac" + integrity sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg== + +cli-tableau@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/cli-tableau/-/cli-tableau-2.0.1.tgz#baa78d83e08a2d7ab79b7dad9406f0254977053f" + integrity sha512-he+WTicka9cl0Fg/y+YyxcN6/bfQ/1O3QmgxRXDhABKqLzvoOSM4fMzp39uMyLBulAFuywD2N7UaoQE7WaADxQ== + dependencies: + chalk "3.0.0" + +clone-deep@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387" + integrity sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ== + dependencies: + is-plain-object "^2.0.4" + kind-of "^6.0.2" + shallow-clone "^3.0.0" + +color-convert@^1.9.3: + version "1.9.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== + dependencies: + color-name "1.1.3" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== + +color-name@^1.0.0, color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +color-string@^1.6.0: + version "1.9.1" + resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.9.1.tgz#4467f9146f036f855b764dfb5bf8582bf342c7a4" + integrity sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg== + dependencies: + color-name "^1.0.0" + simple-swizzle "^0.2.2" + +color-support@^1.1.2: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2" + integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg== + +color@^3.1.3: + version "3.2.1" + resolved "https://registry.yarnpkg.com/color/-/color-3.2.1.tgz#3544dc198caf4490c3ecc9a790b54fe9ff45e164" + integrity sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA== + dependencies: + color-convert "^1.9.3" + color-string "^1.6.0" + +colorette@^2.0.14: + version "2.0.20" + resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" + integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== + +colorspace@1.1.x: + version "1.1.4" + resolved "https://registry.yarnpkg.com/colorspace/-/colorspace-1.1.4.tgz#8d442d1186152f60453bf8070cd66eb364e59243" + integrity sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w== + dependencies: + color "^3.1.3" + text-hex "1.0.x" + +commander@2.15.1: + version "2.15.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.15.1.tgz#df46e867d0fc2aec66a34662b406a9ccafff5b0f" + integrity sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag== + +commander@^2.20.0: + version "2.20.3" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" + integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== + +commander@^7.0.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" + integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== + +commander@^8.3.0: + version "8.3.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-8.3.0.tgz#4837ea1b2da67b9c616a67afbb0fafee567bca66" + integrity sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww== + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== + +console-control-strings@^1.0.0, console-control-strings@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" + integrity sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ== + +content-disposition@0.5.4: + version "0.5.4" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" + integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ== + dependencies: + safe-buffer "5.2.1" + +content-type@~1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918" + integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== + +continuation-local-storage@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/continuation-local-storage/-/continuation-local-storage-3.2.1.tgz#11f613f74e914fe9b34c92ad2d28fe6ae1db7ffb" + integrity sha512-jx44cconVqkCEEyLSKWwkvUXwO561jXMa3LPjTPsm5QR22PA0/mhe33FT4Xb5y74JDvt/Cq+5lm8S8rskLv9ZA== + dependencies: + async-listener "^0.6.0" + emitter-listener "^1.1.1" + +cookie-signature@1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" + integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ== + +cookie@0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b" + integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw== + +core-util-is@~1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" + integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== + +cors@^2.8.4: + version "2.8.5" + resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29" + integrity sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g== + dependencies: + object-assign "^4" + vary "^1" + +croner@~4.1.92: + version "4.1.97" + resolved "https://registry.yarnpkg.com/croner/-/croner-4.1.97.tgz#6e373dc7bb3026fab2deb0d82685feef20796766" + integrity sha512-/f6gpQuxDaqXu+1kwQYSckUglPaOrHdbIlBAu0YuW8/Cdb45XwXYNUBXg3r/9Mo6n540Kn/smKcZWko5x99KrQ== + +cross-spawn@^7.0.3: + version "7.0.3" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" + integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +crypto-js@^4.1.1: + version "4.2.0" + resolved "https://registry.yarnpkg.com/crypto-js/-/crypto-js-4.2.0.tgz#4d931639ecdfd12ff80e8186dba6af2c2e856631" + integrity sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q== + +culvert@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/culvert/-/culvert-0.1.2.tgz#9502f5f0154a2d5a22a023e79f71cc936fa6ef6f" + integrity sha512-yi1x3EAWKjQTreYWeSd98431AV+IEE0qoDyOoaHJ7KJ21gv6HtBXHVLX74opVSGqcR8/AbjJBHAHpcOy2bj5Gg== + +data-uri-to-buffer@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-6.0.1.tgz#540bd4c8753a25ee129035aebdedf63b078703c7" + integrity sha512-MZd3VlchQkp8rdend6vrx7MmVDJzSNTBvghvKjirLkD+WTChA3KUf0jkE68Q4UyctNqI11zZO9/x2Yx+ub5Cvg== + +dayjs@^1.11.7, dayjs@~1.11.5: + version "1.11.10" + resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.10.tgz#68acea85317a6e164457d6d6947564029a6a16a0" + integrity sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ== + +dayjs@~1.8.24: + version "1.8.36" + resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.8.36.tgz#be36e248467afabf8f5a86bae0de0cdceecced50" + integrity sha512-3VmRXEtw7RZKAf+4Tv1Ym9AGeo8r8+CjDi26x+7SYQil1UqtqdaokhzoEJohqlzt0m5kacJSDhJQkG/LWhpRBw== + +debug@2.6.9: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + +debug@4, debug@^4, debug@^4.1.1, debug@^4.3.1, debug@^4.3.4, debug@~4.3.1: + version "4.3.4" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" + integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== + dependencies: + ms "2.1.2" + +debug@^3.2.6, debug@^3.2.7: + version "3.2.7" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" + integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== + dependencies: + ms "^2.1.1" + +define-data-property@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.1.tgz#c35f7cd0ab09883480d12ac5cb213715587800b3" + integrity sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ== + dependencies: + get-intrinsic "^1.2.1" + gopd "^1.0.1" + has-property-descriptors "^1.0.0" + +degenerator@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/degenerator/-/degenerator-5.0.1.tgz#9403bf297c6dad9a1ece409b37db27954f91f2f5" + integrity sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ== + dependencies: + ast-types "^0.13.4" + escodegen "^2.1.0" + esprima "^4.0.1" + +delegates@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" + integrity sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ== + +depd@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" + integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== + +destroy@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015" + integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== + +detect-libc@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.2.tgz#8ccf2ba9315350e1241b88d0ac3b0e1fbd99605d" + integrity sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw== + +dezalgo@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/dezalgo/-/dezalgo-1.0.4.tgz#751235260469084c132157dfa857f386d4c33d81" + integrity sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig== + dependencies: + asap "^2.0.0" + wrappy "1" + +diff-match-patch@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/diff-match-patch/-/diff-match-patch-1.0.5.tgz#abb584d5f10cd1196dfc55aa03701592ae3f7b37" + integrity sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw== + +dns-sync@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/dns-sync/-/dns-sync-0.2.1.tgz#c519da400b90fa2e4a30a70030a1573330c72fa9" + integrity sha512-VB1pDSVs82kFsZuoHQ5/Ysx62WiIfDGn9sx/x55EoVyk8pLwdqWGB2XCaDDOusBllb+1y3XRijscFPJJfpbFiw== + dependencies: + debug "^4" + shelljs "~0.8" + +ee-first@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== + +electron-to-chromium@^1.4.601: + version "1.4.603" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.603.tgz#446907c21d333b55d0beaba1cb5b48430775a8a7" + integrity sha512-Dvo5OGjnl7AZTU632dFJtWj0uJK835eeOVQIuRcmBmsFsTNn3cL05FqOyHAfGQDIoHfLhyJ1Tya3PJ0ceMz54g== + +emitter-listener@^1.1.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/emitter-listener/-/emitter-listener-1.1.2.tgz#56b140e8f6992375b3d7cb2cab1cc7432d9632e8" + integrity sha512-Bt1sBAGFHY9DKY+4/2cV6izcKJUf5T7/gkdmkxzX/qv9CcGH8xSwVRW5mtX03SWJtRTWSOpzCuWN9rBFYZepZQ== + dependencies: + shimmer "^1.2.0" + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +emojis-list@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78" + integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q== + +enabled@2.0.x: + version "2.0.0" + resolved "https://registry.yarnpkg.com/enabled/-/enabled-2.0.0.tgz#f9dd92ec2d6f4bbc0d5d1e64e21d61cd4665e7c2" + integrity sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ== + +encodeurl@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" + integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w== + +encoding@0.1.x: + version "0.1.13" + resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.13.tgz#56574afdd791f54a8e9b2785c0582a2d26210fa9" + integrity sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A== + dependencies: + iconv-lite "^0.6.2" + +enhanced-resolve@^5.15.0: + version "5.15.0" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz#1af946c7d93603eb88e9896cee4904dc012e9c35" + integrity sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg== + dependencies: + graceful-fs "^4.2.4" + tapable "^2.2.0" + +enquirer@2.3.6: + version "2.3.6" + resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d" + integrity sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg== + dependencies: + ansi-colors "^4.1.1" + +entities@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-2.1.0.tgz#992d3129cf7df6870b96c57858c249a120f8b8b5" + integrity sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w== + +envinfo@^7.7.3: + version "7.11.0" + resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.11.0.tgz#c3793f44284a55ff8c82faf1ffd91bc6478ea01f" + integrity sha512-G9/6xF1FPbIw0TtalAMaVPpiq2aDEuKLXM314jPVAO9r2fo2a4BLqMNkmRS7O/xPPZ+COAhGIz3ETvHEV3eUcg== + +es-module-lexer@^1.2.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-1.4.1.tgz#41ea21b43908fe6a287ffcbe4300f790555331f5" + integrity sha512-cXLGjP0c4T3flZJKQSuziYoq7MlT+rnvfZjfp7h+I7K9BNX54kP9nyWvdbwjQ4u1iWbOL4u96fgeZLToQlZC7w== + +esbuild-loader@^2.16.0: + version "2.21.0" + resolved "https://registry.yarnpkg.com/esbuild-loader/-/esbuild-loader-2.21.0.tgz#2698a3e565b0db2bb19a3dd91c2b6c9aad526c80" + integrity sha512-k7ijTkCT43YBSZ6+fBCW1Gin7s46RrJ0VQaM8qA7lq7W+OLsGgtLyFV8470FzYi/4TeDexniTBTPTwZUnXXR5g== + dependencies: + esbuild "^0.16.17" + joycon "^3.0.1" + json5 "^2.2.0" + loader-utils "^2.0.0" + tapable "^2.2.0" + webpack-sources "^1.4.3" + +esbuild@^0.16.17: + version "0.16.17" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.16.17.tgz#fc2c3914c57ee750635fee71b89f615f25065259" + integrity sha512-G8LEkV0XzDMNwXKgM0Jwu3nY3lSTwSGY6XbxM9cr9+s0T/qSV1q1JVPBGzm3dcjhCic9+emZDmMffkwgPeOeLg== + optionalDependencies: + "@esbuild/android-arm" "0.16.17" + "@esbuild/android-arm64" "0.16.17" + "@esbuild/android-x64" "0.16.17" + "@esbuild/darwin-arm64" "0.16.17" + "@esbuild/darwin-x64" "0.16.17" + "@esbuild/freebsd-arm64" "0.16.17" + "@esbuild/freebsd-x64" "0.16.17" + "@esbuild/linux-arm" "0.16.17" + "@esbuild/linux-arm64" "0.16.17" + "@esbuild/linux-ia32" "0.16.17" + "@esbuild/linux-loong64" "0.16.17" + "@esbuild/linux-mips64el" "0.16.17" + "@esbuild/linux-ppc64" "0.16.17" + "@esbuild/linux-riscv64" "0.16.17" + "@esbuild/linux-s390x" "0.16.17" + "@esbuild/linux-x64" "0.16.17" + "@esbuild/netbsd-x64" "0.16.17" + "@esbuild/openbsd-x64" "0.16.17" + "@esbuild/sunos-x64" "0.16.17" + "@esbuild/win32-arm64" "0.16.17" + "@esbuild/win32-ia32" "0.16.17" + "@esbuild/win32-x64" "0.16.17" + +escalade@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" + integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== + +escape-html@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" + integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow== + +escape-string-regexp@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + +escodegen@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-2.1.0.tgz#ba93bbb7a43986d29d6041f99f5262da773e2e17" + integrity sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w== + dependencies: + esprima "^4.0.1" + estraverse "^5.2.0" + esutils "^2.0.2" + optionalDependencies: + source-map "~0.6.1" + +eslint-scope@5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" + integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== + dependencies: + esrecurse "^4.3.0" + estraverse "^4.1.1" + +esprima@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== + +esrecurse@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== + dependencies: + estraverse "^5.2.0" + +estraverse@^4.1.1: + version "4.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" + integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== + +estraverse@^5.2.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" + integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== + +esutils@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== + +etag@~1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" + integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== + +eventemitter2@5.0.1, eventemitter2@~5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/eventemitter2/-/eventemitter2-5.0.1.tgz#6197a095d5fb6b57e8942f6fd7eaad63a09c9452" + integrity sha512-5EM1GHXycJBS6mauYAbVKT1cVs7POKWb2NXD4Vyt8dDqeZa7LaDK1/sjtL+Zb0lzTpSNil4596Dyu97hz37QLg== + +eventemitter2@^6.3.1: + version "6.4.9" + resolved "https://registry.yarnpkg.com/eventemitter2/-/eventemitter2-6.4.9.tgz#41f2750781b4230ed58827bc119d293471ecb125" + integrity sha512-JEPTiaOt9f04oa6NOkc4aH+nVp5I3wEjpHbIPqfgCdD5v5bUzy7xQqwcVO2aDQgOWhI28da57HksMrzK9HlRxg== + +eventemitter2@~0.4.14: + version "0.4.14" + resolved "https://registry.yarnpkg.com/eventemitter2/-/eventemitter2-0.4.14.tgz#8f61b75cde012b2e9eb284d4545583b5643b61ab" + integrity sha512-K7J4xq5xAD5jHsGM5ReWXRTFa3JRGofHiMcVgQ8PRwgWxzjHpMWCIzsmyf60+mh8KLsqYPcjUMa0AC4hd6lPyQ== + +events@^3.2.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" + integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== + +expose-loader@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/expose-loader/-/expose-loader-3.1.0.tgz#7a0bdecb345b921ca238a8c4715a4ea7e227213f" + integrity sha512-2RExSo0yJiqP+xiUue13jQa2IHE8kLDzTI7b6kn+vUlBVvlzNSiLDzo4e5Pp5J039usvTUnxZ8sUOhv0Kg15NA== + +express@^4.16.3: + version "4.18.2" + resolved "https://registry.yarnpkg.com/express/-/express-4.18.2.tgz#3fabe08296e930c796c19e3c516979386ba9fd59" + integrity sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ== + dependencies: + accepts "~1.3.8" + array-flatten "1.1.1" + body-parser "1.20.1" + content-disposition "0.5.4" + content-type "~1.0.4" + cookie "0.5.0" + cookie-signature "1.0.6" + debug "2.6.9" + depd "2.0.0" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + finalhandler "1.2.0" + fresh "0.5.2" + http-errors "2.0.0" + merge-descriptors "1.0.1" + methods "~1.1.2" + on-finished "2.4.1" + parseurl "~1.3.3" + path-to-regexp "0.1.7" + proxy-addr "~2.0.7" + qs "6.11.0" + range-parser "~1.2.1" + safe-buffer "5.2.1" + send "0.18.0" + serve-static "1.15.0" + setprototypeof "1.2.0" + statuses "2.0.1" + type-is "~1.6.18" + utils-merge "1.0.1" + vary "~1.1.2" + +fast-deep-equal@^3.1.1: + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +fast-json-patch@^3.0.0-1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/fast-json-patch/-/fast-json-patch-3.1.1.tgz#85064ea1b1ebf97a3f7ad01e23f9337e72c66947" + integrity sha512-vf6IHUX2SBcA+5/+4883dsIjpBTqmfBjmYiWK1savxQmFk4JfBMLa7ynTYOs1Rolp/T1betJxHiGD3g1Mn8lUQ== + +fast-json-stable-stringify@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== + +fastest-levenshtein@^1.0.12: + version "1.0.16" + resolved "https://registry.yarnpkg.com/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz#210e61b6ff181de91ea9b3d1b84fdedd47e034e5" + integrity sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg== + +fclone@1.0.11, fclone@~1.0.11: + version "1.0.11" + resolved "https://registry.yarnpkg.com/fclone/-/fclone-1.0.11.tgz#10e85da38bfea7fc599341c296ee1d77266ee640" + integrity sha512-GDqVQezKzRABdeqflsgMr7ktzgF9CyS+p2oe0jJqUY6izSSbhPIQJDpoU4PtGcD7VPM9xh/dVrTu6z1nwgmEGw== + +fecha@^4.2.0: + version "4.2.3" + resolved "https://registry.yarnpkg.com/fecha/-/fecha-4.2.3.tgz#4d9ccdbc61e8629b259fdca67e65891448d569fd" + integrity sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw== + +fill-range@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" + integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== + dependencies: + to-regex-range "^5.0.1" + +finalhandler@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.2.0.tgz#7d23fe5731b207b4640e4fcd00aec1f9207a7b32" + integrity sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg== + dependencies: + debug "2.6.9" + encodeurl "~1.0.2" + escape-html "~1.0.3" + on-finished "2.4.1" + parseurl "~1.3.3" + statuses "2.0.1" + unpipe "~1.0.0" + +find-up@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" + integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== + dependencies: + locate-path "^5.0.0" + path-exists "^4.0.0" + +flat@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241" + integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== + +fn.name@1.x.x: + version "1.1.0" + resolved "https://registry.yarnpkg.com/fn.name/-/fn.name-1.1.0.tgz#26cad8017967aea8731bc42961d04a3d5988accc" + integrity sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw== + +follow-redirects@^1.14.0: + version "1.15.3" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.3.tgz#fe2f3ef2690afce7e82ed0b44db08165b207123a" + integrity sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q== + +formidable@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/formidable/-/formidable-2.1.2.tgz#fa973a2bec150e4ce7cac15589d7a25fc30ebd89" + integrity sha512-CM3GuJ57US06mlpQ47YcunuUZ9jpm8Vx+P2CGt2j7HpgkKZO/DJYQ0Bobim8G6PFQmK5lOqOOdUXboU+h73A4g== + dependencies: + dezalgo "^1.0.4" + hexoid "^1.0.0" + once "^1.4.0" + qs "^6.11.0" + +forwarded@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" + integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== + +fresh@0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" + integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q== + +fs-extra@^10.0.0: + version "10.1.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.1.0.tgz#02873cfbc4084dde127eaa5f9905eef2325d1abf" + integrity sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + +fs-extra@^11.1.0: + version "11.2.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-11.2.0.tgz#e70e17dfad64232287d01929399e0ea7c86b0e5b" + integrity sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + +fs-extra@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" + integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^4.0.0" + universalify "^0.1.0" + +fs-minipass@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" + integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg== + dependencies: + minipass "^3.0.0" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== + +fsevents@~2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" + integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== + +function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== + +gauge@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/gauge/-/gauge-3.0.2.tgz#03bf4441c044383908bcfa0656ad91803259b395" + integrity sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q== + dependencies: + aproba "^1.0.3 || ^2.0.0" + color-support "^1.1.2" + console-control-strings "^1.0.0" + has-unicode "^2.0.1" + object-assign "^4.1.1" + signal-exit "^3.0.0" + string-width "^4.2.3" + strip-ansi "^6.0.1" + wide-align "^1.1.2" + +get-intrinsic@^1.0.2, get-intrinsic@^1.1.3, get-intrinsic@^1.2.1, get-intrinsic@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.2.tgz#281b7622971123e1ef4b3c90fd7539306da93f3b" + integrity sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA== + dependencies: + function-bind "^1.1.2" + has-proto "^1.0.1" + has-symbols "^1.0.3" + hasown "^2.0.0" + +get-uri@^6.0.1: + version "6.0.2" + resolved "https://registry.yarnpkg.com/get-uri/-/get-uri-6.0.2.tgz#e019521646f4a8ff6d291fbaea2c46da204bb75b" + integrity sha512-5KLucCJobh8vBY1K07EFV4+cPZH3mrV9YeAruUseCQKHB58SGjjT2l9/eA9LD082IiuMjSlFJEcdJ27TXvbZNw== + dependencies: + basic-ftp "^5.0.2" + data-uri-to-buffer "^6.0.0" + debug "^4.3.4" + fs-extra "^8.1.0" + +git-node-fs@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/git-node-fs/-/git-node-fs-1.0.0.tgz#49b215e242ebe43aa4c7561bbba499521752080f" + integrity sha512-bLQypt14llVXBg0S0u8q8HmU7g9p3ysH+NvVlae5vILuUvs759665HvmR5+wb04KjHyjFcDRxdYb4kyNnluMUQ== + +git-sha1@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/git-sha1/-/git-sha1-0.1.2.tgz#599ac192b71875825e13a445f3a6e05118c2f745" + integrity sha512-2e/nZezdVlyCopOCYHeW0onkbZg7xP1Ad6pndPy1rCygeRykefUS6r7oA5cJRGEFvseiaz5a/qUHFVX1dd6Isg== + +glob-parent@~5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + +glob-to-regexp@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" + integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== + +glob@^7.0.0, glob@^7.0.5, glob@^7.1.2, glob@^7.1.3, glob@^7.2.0: + version "7.2.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" + integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.1.1" + once "^1.3.0" + path-is-absolute "^1.0.0" + +gopd@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" + integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA== + dependencies: + get-intrinsic "^1.1.3" + +graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4, graceful-fs@^4.2.9: + version "4.2.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" + integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== + +handlebars@^4.7.7: + version "4.7.8" + resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.8.tgz#41c42c18b1be2365439188c77c6afae71c0cd9e9" + integrity sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ== + dependencies: + minimist "^1.2.5" + neo-async "^2.6.2" + source-map "^0.6.1" + wordwrap "^1.0.0" + optionalDependencies: + uglify-js "^3.1.4" + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +has-property-descriptors@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz#52ba30b6c5ec87fd89fa574bc1c39125c6f65340" + integrity sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg== + dependencies: + get-intrinsic "^1.2.2" + +has-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.1.tgz#1885c1305538958aff469fef37937c22795408e0" + integrity sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg== + +has-symbols@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" + integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== + +has-unicode@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" + integrity sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ== + +hasown@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.0.tgz#f4c513d454a57b7c7e1650778de226b11700546c" + integrity sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA== + dependencies: + function-bind "^1.1.2" + +hasshu@0.2.x: + version "0.2.5" + resolved "https://registry.yarnpkg.com/hasshu/-/hasshu-0.2.5.tgz#9cc6d4ced7ecb8332791dde3799cda22ef00edd4" + integrity sha512-x8oMri+KUJ5OSpJeHW+iyRJa/NzRPT9ijKySpGlJCDIJdJvxfZ+hiqwul6gHHat714t4SwtZT/r9uj41Hr1L3w== + +hexoid@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/hexoid/-/hexoid-1.0.0.tgz#ad10c6573fb907de23d9ec63a711267d9dc9bc18" + integrity sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g== + +http-errors@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3" + integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ== + dependencies: + depd "2.0.0" + inherits "2.0.4" + setprototypeof "1.2.0" + statuses "2.0.1" + toidentifier "1.0.1" + +http-proxy-agent@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-7.0.0.tgz#e9096c5afd071a3fce56e6252bb321583c124673" + integrity sha512-+ZT+iBxVUQ1asugqnD6oWoRiS25AkjNfG085dKJGtGxkdwLQrMKU5wJr2bOOFAXzKcTuqq+7fZlTMgG3SRfIYQ== + dependencies: + agent-base "^7.1.0" + debug "^4.3.4" + +https-proxy-agent@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" + integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== + dependencies: + agent-base "6" + debug "4" + +https-proxy-agent@^7.0.2: + version "7.0.2" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.2.tgz#e2645b846b90e96c6e6f347fb5b2e41f1590b09b" + integrity sha512-NmLNjm6ucYwtcUmL7JQC1ZQ57LmHP4lT15FQ8D61nak1rO6DH+fz5qNK2Ap5UN4ZapYICE3/0KodcLYSPsPbaA== + dependencies: + agent-base "^7.0.2" + debug "4" + +iconv-lite@0.4.24, iconv-lite@^0.4.4: + version "0.4.24" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + +iconv-lite@^0.6.2, iconv-lite@^0.6.3: + version "0.6.3" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" + integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== + dependencies: + safer-buffer ">= 2.1.2 < 3.0.0" + +ignore-by-default@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/ignore-by-default/-/ignore-by-default-1.0.1.tgz#48ca6d72f6c6a3af00a9ad4ae6876be3889e2b09" + integrity sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA== + +immediate@~3.0.5: + version "3.0.6" + resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b" + integrity sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ== + +import-local@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.1.0.tgz#b4479df8a5fd44f6cdce24070675676063c95cb4" + integrity sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg== + dependencies: + pkg-dir "^4.2.0" + resolve-cwd "^3.0.0" + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +inherits@2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + integrity sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw== + +ini@^1.3.5: + version "1.3.8" + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" + integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== + +interpret@^1.0.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e" + integrity sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA== + +interpret@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/interpret/-/interpret-2.2.0.tgz#1a78a0b5965c40a5416d007ad6f50ad27c417df9" + integrity sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw== + +ip@^1.1.8: + version "1.1.8" + resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.8.tgz#ae05948f6b075435ed3307acce04629da8cdbf48" + integrity sha512-PuExPYUiu6qMBQb4l06ecm6T6ujzhmh+MeJcW9wa89PoAz5pvd4zPgN5WJV104mb6S2T1AwNIAaB70JNrLQWhg== + +ip@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ip/-/ip-2.0.0.tgz#4cf4ab182fee2314c75ede1276f8c80b479936da" + integrity sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ== + +ipaddr.js@1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" + integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== + +is-arrayish@^0.3.1: + version "0.3.2" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03" + integrity sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ== + +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + dependencies: + binary-extensions "^2.0.0" + +is-core-module@^2.13.0: + version "2.13.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.1.tgz#ad0d7532c6fea9da1ebdc82742d74525c6273384" + integrity sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw== + dependencies: + hasown "^2.0.0" + +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== + +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + +is-glob@^4.0.1, is-glob@~4.0.1: + version "4.0.3" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-plain-object@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" + integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== + dependencies: + isobject "^3.0.1" + +is-stream@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" + integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== + +isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== + +isobject@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" + integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg== + +jest-worker@^27.4.5: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.5.1.tgz#8d146f0900e8973b106b6f73cc1e9a8cb86f8db0" + integrity sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg== + dependencies: + "@types/node" "*" + merge-stream "^2.0.0" + supports-color "^8.0.0" + +joycon@^3.0.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/joycon/-/joycon-3.1.1.tgz#bce8596d6ae808f8b68168f5fc69280996894f03" + integrity sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw== + +jquery@^3.6.0: + version "3.7.1" + resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.7.1.tgz#083ef98927c9a6a74d05a6af02806566d16274de" + integrity sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg== + +js-git@^0.7.8: + version "0.7.8" + resolved "https://registry.yarnpkg.com/js-git/-/js-git-0.7.8.tgz#52fa655ab61877d6f1079efc6534b554f31e5444" + integrity sha512-+E5ZH/HeRnoc/LW0AmAyhU+mNcWBzAKE+30+IDMLSLbbK+Tdt02AdkOKq9u15rlJsDEGFqtgckc8ZM59LhhiUA== + dependencies: + bodec "^0.1.0" + culvert "^0.1.2" + git-sha1 "^0.1.2" + pako "^0.2.5" + +json-parse-even-better-errors@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" + integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + +json-stringify-safe@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" + integrity sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA== + +json5@^2.1.2, json5@^2.2.0: + version "2.2.3" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" + integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== + +jsonfile@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" + integrity sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg== + optionalDependencies: + graceful-fs "^4.1.6" + +jsonfile@^6.0.1: + version "6.1.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" + integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ== + dependencies: + universalify "^2.0.0" + optionalDependencies: + graceful-fs "^4.1.6" + +jszip@^3.7.1: + version "3.10.1" + resolved "https://registry.yarnpkg.com/jszip/-/jszip-3.10.1.tgz#34aee70eb18ea1faec2f589208a157d1feb091c2" + integrity sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g== + dependencies: + lie "~3.3.0" + pako "~1.0.2" + readable-stream "~2.3.6" + setimmediate "^1.0.5" + +jwt-simple@^0.5.1: + version "0.5.6" + resolved "https://registry.yarnpkg.com/jwt-simple/-/jwt-simple-0.5.6.tgz#3357adec55b26547114157be66748995b75b333a" + integrity sha512-40aUybvhH9t2h71ncA1/1SbtTNCVZHgsTsTgqPUxGWDmUDrXyDf2wMNQKEbdBjbf4AI+fQhbECNTV6lWxQKUzg== + +kind-of@^6.0.2: + version "6.0.3" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" + integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== + +klaw-sync@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/klaw-sync/-/klaw-sync-6.0.0.tgz#1fd2cfd56ebb6250181114f0a581167099c2b28c" + integrity sha512-nIeuVSzdCCs6TDPTqI8w1Yre34sSq7AkZ4B3sfOBbI2CgVSB4Du4aLQijFU2+lhAFCwt9+42Hel6lQNIv6AntQ== + dependencies: + graceful-fs "^4.1.11" + +kuler@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/kuler/-/kuler-2.0.0.tgz#e2c570a3800388fb44407e851531c1d670b061b3" + integrity sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A== + +lazy@~1.0.11: + version "1.0.11" + resolved "https://registry.yarnpkg.com/lazy/-/lazy-1.0.11.tgz#daa068206282542c088288e975c297c1ae77b690" + integrity sha512-Y+CjUfLmIpoUCCRl0ub4smrYtGGr5AOa2AKOaWelGHOGz33X/Y/KizefGqbkwfz44+cnq/+9habclf8vOmu2LA== + +lie@~3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/lie/-/lie-3.3.0.tgz#dcf82dee545f46074daf200c7c1c5a08e0f40f6a" + integrity sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ== + dependencies: + immediate "~3.0.5" + +line-buffer@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/line-buffer/-/line-buffer-0.1.4.tgz#e07de183310b60122fde4b335cf6606543ffb1d3" + integrity sha512-JAklY0PFT9JTtIMS/tpTRwByIVUo8jJLT4mTK43gl9mUZRDxz1F9UsOWySLk80+Oxo9KpwEEuO/z/Nk5k2IC2w== + +linkify-it@^3.0.1: + version "3.0.3" + resolved "https://registry.yarnpkg.com/linkify-it/-/linkify-it-3.0.3.tgz#a98baf44ce45a550efb4d49c769d07524cc2fa2e" + integrity sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ== + dependencies: + uc.micro "^1.0.1" + +loader-runner@^4.2.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.3.0.tgz#c1b4a163b99f614830353b16755e7149ac2314e1" + integrity sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg== + +loader-utils@^2.0.0: + version "2.0.4" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.4.tgz#8b5cb38b5c34a9a018ee1fc0e6a066d1dfcc528c" + integrity sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw== + dependencies: + big.js "^5.2.2" + emojis-list "^3.0.0" + json5 "^2.1.2" + +locate-path@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" + integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== + dependencies: + p-locate "^4.1.0" + +lodash@^4.17.14, lodash@^4.17.21: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + +log-driver@^1.2.7: + version "1.2.7" + resolved "https://registry.yarnpkg.com/log-driver/-/log-driver-1.2.7.tgz#63b95021f0702fedfa2c9bb0a24e7797d71871d8" + integrity sha512-U7KCmLdqsGHBLeWqYlFA0V0Sl6P08EE1ZrmA9cxjUE0WVqT9qnyVDPz1kzpFEP0jdJuFnasWIfSd7fsaNXkpbg== + +logform@^2.3.2, logform@^2.4.0: + version "2.6.0" + resolved "https://registry.yarnpkg.com/logform/-/logform-2.6.0.tgz#8c82a983f05d6eaeb2d75e3decae7a768b2bf9b5" + integrity sha512-1ulHeNPp6k/LD8H91o7VYFBng5i1BDE7HoKxVbZiGFidS1Rj65qcywLxX+pVfAPoQJEjRdvKcusKwOupHCVOVQ== + dependencies: + "@colors/colors" "1.6.0" + "@types/triple-beam" "^1.3.2" + fecha "^4.2.0" + ms "^2.1.1" + safe-stable-stringify "^2.3.1" + triple-beam "^1.3.0" + +lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + dependencies: + yallist "^4.0.0" + +lru-cache@^7.14.1: + version "7.18.3" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.18.3.tgz#f793896e0fd0e954a59dfdd82f0773808df6aa89" + integrity sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA== + +luxon@^2.1.1: + version "2.5.2" + resolved "https://registry.yarnpkg.com/luxon/-/luxon-2.5.2.tgz#17ed497f0277e72d58a4756d6a9abee4681457b6" + integrity sha512-Yg7/RDp4nedqmLgyH0LwgGRvMEKVzKbUdkBYyCosbHgJ+kaOUx0qzSiSatVc3DFygnirTPYnMM2P5dg2uH1WvA== + +make-dir@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" + integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== + dependencies: + semver "^6.0.0" + +markdown-it@^12.2.0: + version "12.3.2" + resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-12.3.2.tgz#bf92ac92283fe983fe4de8ff8abfb5ad72cd0c90" + integrity sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg== + dependencies: + argparse "^2.0.1" + entities "~2.1.0" + linkify-it "^3.0.1" + mdurl "^1.0.1" + uc.micro "^1.0.5" + +mdurl@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e" + integrity sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g== + +media-typer@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" + integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ== + +merge-descriptors@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" + integrity sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w== + +merge-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" + integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== + +methods@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" + integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w== + +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +mime-types@^2.1.27, mime-types@~2.1.24, mime-types@~2.1.34: + version "2.1.35" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + +mime@1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" + integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== + +minimalistic-assert@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" + integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== + +minimatch@^3.1.1, minimatch@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + +minimist@^1.2.5: + version "1.2.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" + integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== + +minipass@^3.0.0: + version "3.3.6" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.3.6.tgz#7bba384db3a1520d18c9c0e5251c3444e95dd94a" + integrity sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw== + dependencies: + yallist "^4.0.0" + +minipass@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-5.0.0.tgz#3e9788ffb90b694a5d0ec94479a45b5d8738133d" + integrity sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ== + +minizlib@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" + integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg== + dependencies: + minipass "^3.0.0" + yallist "^4.0.0" + +mkdirp@1.0.4, mkdirp@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" + integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== + +module-details-from-path@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/module-details-from-path/-/module-details-from-path-1.0.3.tgz#114c949673e2a8a35e9d35788527aa37b679da2b" + integrity sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A== + +moment@^2.22.1: + version "2.29.4" + resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.4.tgz#3dbe052889fe7c1b2ed966fcb3a77328964ef108" + integrity sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w== + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== + +ms@2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +ms@2.1.3, ms@^2.1.1: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +mustache@^2.3.0: + version "2.3.2" + resolved "https://registry.yarnpkg.com/mustache/-/mustache-2.3.2.tgz#a6d4d9c3f91d13359ab889a812954f9230a3d0c5" + integrity sha512-KpMNwdQsYz3O/SBS1qJ/o3sqUJ5wSb8gb0pul8CO0S56b9Y2ALm8zCfsjPXsqGFfoNBkDwZuZIAjhsZI03gYVQ== + +mute-stream@~0.0.4: + version "0.0.8" + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" + integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== + +needle@2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/needle/-/needle-2.4.0.tgz#6833e74975c444642590e15a750288c5f939b57c" + integrity sha512-4Hnwzr3mi5L97hMYeNl8wRW/Onhy4nUKR/lVemJ8gJedxxUyBLm9kkrDColJvoSfwi0jCNhD+xCdOtiGDQiRZg== + dependencies: + debug "^3.2.6" + iconv-lite "^0.4.4" + sax "^1.2.4" + +negotiator@0.6.3: + version "0.6.3" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" + integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== + +neo-async@^2.6.2: + version "2.6.2" + resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" + integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== + +netmask@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/netmask/-/netmask-2.0.2.tgz#8b01a07644065d536383835823bc52004ebac5e7" + integrity sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg== + +node-addon-api@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-5.1.0.tgz#49da1ca055e109a23d537e9de43c09cca21eb762" + integrity sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA== + +node-fetch@^2.6.7: + version "2.7.0" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d" + integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A== + dependencies: + whatwg-url "^5.0.0" + +node-releases@^2.0.14: + version "2.0.14" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.14.tgz#2ffb053bceb8b2be8495ece1ab6ce600c4461b0b" + integrity sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw== + +nodemailer@^6.9.7: + version "6.9.7" + resolved "https://registry.yarnpkg.com/nodemailer/-/nodemailer-6.9.7.tgz#ec2f488f62ba1558e7b19239b62778df4a5c4397" + integrity sha512-rUtR77ksqex/eZRLmQ21LKVH5nAAsVicAtAYudK7JgwenEDZ0UIQ1adUGqErz7sMkWYxWTTU1aeP2Jga6WQyJw== + +nodemon@^2.0.15: + version "2.0.22" + resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-2.0.22.tgz#182c45c3a78da486f673d6c1702e00728daf5258" + integrity sha512-B8YqaKMmyuCO7BowF1Z1/mkPqLk6cs/l63Ojtd6otKjMx47Dq1utxfRxcavH1I7VSaL8n5BUaoutadnsX3AAVQ== + dependencies: + chokidar "^3.5.2" + debug "^3.2.7" + ignore-by-default "^1.0.1" + minimatch "^3.1.2" + pstree.remy "^1.1.8" + semver "^5.7.1" + simple-update-notifier "^1.0.7" + supports-color "^5.5.0" + touch "^3.1.0" + undefsafe "^2.0.5" + +nopt@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-5.0.0.tgz#530942bb58a512fccafe53fe210f13a25355dc88" + integrity sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ== + dependencies: + abbrev "1" + +nopt@~1.0.10: + version "1.0.10" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-1.0.10.tgz#6ddd21bd2a31417b92727dd585f8a6f37608ebee" + integrity sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg== + dependencies: + abbrev "1" + +normalize-path@^3.0.0, normalize-path@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + +npmlog@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-5.0.1.tgz#f06678e80e29419ad67ab964e0fa69959c1eb8b0" + integrity sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw== + dependencies: + are-we-there-yet "^2.0.0" + console-control-strings "^1.1.0" + gauge "^3.0.0" + set-blocking "^2.0.0" + +nssocket@0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/nssocket/-/nssocket-0.6.0.tgz#59f96f6ff321566f33c70f7dbeeecdfdc07154fa" + integrity sha512-a9GSOIql5IqgWJR3F/JXG4KpJTA3Z53Cj0MeMvGpglytB1nxE4PdFNC0jINe27CS7cGivoynwc054EzCcT3M3w== + dependencies: + eventemitter2 "~0.4.14" + lazy "~1.0.11" + +object-assign@^4, object-assign@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== + +object-inspect@^1.9.0: + version "1.13.1" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.1.tgz#b96c6109324ccfef6b12216a956ca4dc2ff94bc2" + integrity sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ== + +on-finished@2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" + integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== + dependencies: + ee-first "1.1.1" + +once@^1.3.0, once@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== + dependencies: + wrappy "1" + +one-time@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/one-time/-/one-time-1.0.0.tgz#e06bc174aed214ed58edede573b433bbf827cb45" + integrity sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g== + dependencies: + fn.name "1.x.x" + +openpgp@^5.10.1: + version "5.11.0" + resolved "https://registry.yarnpkg.com/openpgp/-/openpgp-5.11.0.tgz#cec5b285d188148f7b5201b9aceb53850cc286a2" + integrity sha512-hytHsxIPtRhuh6uAmoBUThHSwHSX3imLu7x4453T+xkVqIw49rl22MRD4KQIAQdCDoVdouejzYgcuLmMA/2OAA== + dependencies: + asn1.js "^5.0.0" + +p-limit@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" + integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== + dependencies: + p-try "^2.0.0" + +p-locate@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" + integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== + dependencies: + p-limit "^2.2.0" + +p-try@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== + +pac-proxy-agent@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/pac-proxy-agent/-/pac-proxy-agent-7.0.1.tgz#6b9ddc002ec3ff0ba5fdf4a8a21d363bcc612d75" + integrity sha512-ASV8yU4LLKBAjqIPMbrgtaKIvxQri/yh2OpI+S6hVa9JRkUI3Y3NPFbfngDtY7oFtSMD3w31Xns89mDa3Feo5A== + dependencies: + "@tootallnate/quickjs-emscripten" "^0.23.0" + agent-base "^7.0.2" + debug "^4.3.4" + get-uri "^6.0.1" + http-proxy-agent "^7.0.0" + https-proxy-agent "^7.0.2" + pac-resolver "^7.0.0" + socks-proxy-agent "^8.0.2" + +pac-resolver@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/pac-resolver/-/pac-resolver-7.0.0.tgz#79376f1ca26baf245b96b34c339d79bff25e900c" + integrity sha512-Fd9lT9vJbHYRACT8OhCbZBbxr6KRSawSovFpy8nDGshaK99S/EBhVIHp9+crhxrsZOuvLpgL1n23iyPg6Rl2hg== + dependencies: + degenerator "^5.0.0" + ip "^1.1.8" + netmask "^2.0.2" + +pako@^0.2.5: + version "0.2.9" + resolved "https://registry.yarnpkg.com/pako/-/pako-0.2.9.tgz#f3f7522f4ef782348da8161bad9ecfd51bf83a75" + integrity sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA== + +pako@~1.0.2: + version "1.0.11" + resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf" + integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw== + +parseurl@~1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" + integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== + +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== + +path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + +path-parse@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + +path-to-regexp@0.1.7: + version "0.1.7" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" + integrity sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ== + +path@^0.12.7: + version "0.12.7" + resolved "https://registry.yarnpkg.com/path/-/path-0.12.7.tgz#d4dc2a506c4ce2197eb481ebfcd5b36c0140b10f" + integrity sha512-aXXC6s+1w7otVF9UletFkFcDsJeO7lSZBPUQhtb5O0xJe8LtYhj/GxldoL09bBj9+ZmE2hNoHqQSFMN5fikh4Q== + dependencies: + process "^0.11.1" + util "^0.10.3" + +picocolors@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" + integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== + +picomatch@^2.0.4, picomatch@^2.2.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + +pidusage@^2.0.21: + version "2.0.21" + resolved "https://registry.yarnpkg.com/pidusage/-/pidusage-2.0.21.tgz#7068967b3d952baea73e57668c98b9eaa876894e" + integrity sha512-cv3xAQos+pugVX+BfXpHsbyz/dLzX+lr44zNMsYiGxUw+kV5sgQCIcLd1z+0vq+KyC7dJ+/ts2PsfgWfSC3WXA== + dependencies: + safe-buffer "^5.2.1" + +pidusage@~3.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/pidusage/-/pidusage-3.0.2.tgz#6faa5402b2530b3af2cf93d13bcf202889724a53" + integrity sha512-g0VU+y08pKw5M8EZ2rIGiEBaB8wrQMjYGFfW2QVIfyT8V+fq8YFLkvlz4bz5ljvFDJYNFCWT3PWqcRr2FKO81w== + dependencies: + safe-buffer "^5.2.1" + +pkg-dir@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" + integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== + dependencies: + find-up "^4.0.0" + +pm2-axon-rpc@~0.7.0, pm2-axon-rpc@~0.7.1: + version "0.7.1" + resolved "https://registry.yarnpkg.com/pm2-axon-rpc/-/pm2-axon-rpc-0.7.1.tgz#2daec5383a63135b3f18babb70266dacdcbc429a" + integrity sha512-FbLvW60w+vEyvMjP/xom2UPhUN/2bVpdtLfKJeYM3gwzYhoTEEChCOICfFzxkxuoEleOlnpjie+n1nue91bDQw== + dependencies: + debug "^4.3.1" + +pm2-axon@~4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/pm2-axon/-/pm2-axon-4.0.1.tgz#a7b4bb586e9aeb35b1042b488cde15b60cabafd2" + integrity sha512-kES/PeSLS8orT8dR5jMlNl+Yu4Ty3nbvZRmaAtROuVm9nYYGiaoXqqKQqQYzWQzMYWUKHMQTvBlirjE5GIIxqg== + dependencies: + amp "~0.3.1" + amp-message "~0.1.1" + debug "^4.3.1" + escape-string-regexp "^4.0.0" + +pm2-deploy@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/pm2-deploy/-/pm2-deploy-1.0.2.tgz#98d8385553a3a4dca11c7b3116deb519bc5961a7" + integrity sha512-YJx6RXKrVrWaphEYf++EdOOx9EH18vM8RSZN/P1Y+NokTKqYAca/ejXwVLyiEpNju4HPZEk3Y2uZouwMqUlcgg== + dependencies: + run-series "^1.1.8" + tv4 "^1.3.0" + +pm2-multimeter@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/pm2-multimeter/-/pm2-multimeter-0.1.2.tgz#1a1e55153d41a05534cea23cfe860abaa0eb4ace" + integrity sha512-S+wT6XfyKfd7SJIBqRgOctGxaBzUOmVQzTAS+cg04TsEUObJVreha7lvCfX8zzGVr871XwCSnHUU7DQQ5xEsfA== + dependencies: + charm "~0.1.1" + +pm2-sysmonit@^1.2.8: + version "1.2.8" + resolved "https://registry.yarnpkg.com/pm2-sysmonit/-/pm2-sysmonit-1.2.8.tgz#eddea34a53fd8c8d7c3efb73b97a3c548686e24d" + integrity sha512-ACOhlONEXdCTVwKieBIQLSi2tQZ8eKinhcr9JpZSUAL8Qy0ajIgRtsLxG/lwPOW3JEKqPyw/UaHmTWhUzpP4kA== + dependencies: + async "^3.2.0" + debug "^4.3.1" + pidusage "^2.0.21" + systeminformation "^5.7" + tx2 "~1.0.4" + +"pm2@^5.3.0 ": + version "5.3.0" + resolved "https://registry.yarnpkg.com/pm2/-/pm2-5.3.0.tgz#06850810f77cd98495ae1c66fbdd028a8fb5899e" + integrity sha512-xscmQiAAf6ArVmKhjKTeeN8+Td7ZKnuZFFPw1DGkdFPR/0Iyx+m+1+OpCdf9+HQopX3VPc9/wqPQHqVOfHum9w== + dependencies: + "@pm2/agent" "~2.0.0" + "@pm2/io" "~5.0.0" + "@pm2/js-api" "~0.6.7" + "@pm2/pm2-version-check" latest + async "~3.2.0" + blessed "0.1.81" + chalk "3.0.0" + chokidar "^3.5.3" + cli-tableau "^2.0.0" + commander "2.15.1" + croner "~4.1.92" + dayjs "~1.11.5" + debug "^4.3.1" + enquirer "2.3.6" + eventemitter2 "5.0.1" + fclone "1.0.11" + mkdirp "1.0.4" + needle "2.4.0" + pidusage "~3.0" + pm2-axon "~4.0.1" + pm2-axon-rpc "~0.7.1" + pm2-deploy "~1.0.2" + pm2-multimeter "^0.1.2" + promptly "^2" + semver "^7.2" + source-map-support "0.5.21" + sprintf-js "1.1.2" + vizion "~2.2.1" + yamljs "0.3.0" + optionalDependencies: + pm2-sysmonit "^1.2.8" + +prismjs@^1.25.0: + version "1.29.0" + resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.29.0.tgz#f113555a8fa9b57c35e637bba27509dcf802dd12" + integrity sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q== + +process-nextick-args@~2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" + integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== + +process@^0.11.1: + version "0.11.10" + resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" + integrity sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A== + +promised-timeout@0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/promised-timeout/-/promised-timeout-0.5.1.tgz#56bd98eb8c59e594dae85551bc54451cd4584c23" + integrity sha512-pil+5J1yOuH5jUp/Dk65OXGGOWLMSSQ00whNbQPbZXZTBlpr2a44hELj686WW6iB3cLiUALyeIQ2n3gpZiEK3w== + +promised-timeout@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/promised-timeout/-/promised-timeout-0.2.0.tgz#45eafcf5ed9457e2e60efa77c2c860d6ef1d816e" + integrity sha512-GIFMiDI53g37wjxeh510DgtMDnkawWem/RWBuJlCglaWgdCAUPYvkMX+zTj1nP3fDVmyrWGRthivWHX/TZ5IKw== + +promptly@^2: + version "2.2.0" + resolved "https://registry.yarnpkg.com/promptly/-/promptly-2.2.0.tgz#2a13fa063688a2a5983b161fff0108a07d26fc74" + integrity sha512-aC9j+BZsRSSzEsXBNBwDnAxujdx19HycZoKgRgzWnS8eOHg1asuf9heuLprfbe739zY3IdUQx+Egv6Jn135WHA== + dependencies: + read "^1.0.4" + +proxy-addr@~2.0.7: + version "2.0.7" + resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" + integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== + dependencies: + forwarded "0.2.0" + ipaddr.js "1.9.1" + +proxy-agent@~6.3.0: + version "6.3.1" + resolved "https://registry.yarnpkg.com/proxy-agent/-/proxy-agent-6.3.1.tgz#40e7b230552cf44fd23ffaf7c59024b692612687" + integrity sha512-Rb5RVBy1iyqOtNl15Cw/llpeLH8bsb37gM1FUfKQ+Wck6xHlbAhWGUFiTRHtkjqGTA5pSHz6+0hrPW/oECihPQ== + dependencies: + agent-base "^7.0.2" + debug "^4.3.4" + http-proxy-agent "^7.0.0" + https-proxy-agent "^7.0.2" + lru-cache "^7.14.1" + pac-proxy-agent "^7.0.1" + proxy-from-env "^1.1.0" + socks-proxy-agent "^8.0.2" + +proxy-from-env@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" + integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== + +pstree.remy@^1.1.8: + version "1.1.8" + resolved "https://registry.yarnpkg.com/pstree.remy/-/pstree.remy-1.1.8.tgz#c242224f4a67c21f686839bbdb4ac282b8373d3a" + integrity sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w== + +punycode@^2.1.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" + integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== + +qs@6.11.0: + version "6.11.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.0.tgz#fd0d963446f7a65e1367e01abd85429453f0c37a" + integrity sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q== + dependencies: + side-channel "^1.0.4" + +qs@^6.11.0: + version "6.11.2" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.2.tgz#64bea51f12c1f5da1bc01496f48ffcff7c69d7d9" + integrity sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA== + dependencies: + side-channel "^1.0.4" + +randombytes@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" + integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== + dependencies: + safe-buffer "^5.1.0" + +range-parser@~1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" + integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== + +raw-body@2.5.1: + version "2.5.1" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.1.tgz#fe1b1628b181b700215e5fd42389f98b71392857" + integrity sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig== + dependencies: + bytes "3.1.2" + http-errors "2.0.0" + iconv-lite "0.4.24" + unpipe "1.0.0" + +read@^1.0.4: + version "1.0.7" + resolved "https://registry.yarnpkg.com/read/-/read-1.0.7.tgz#b3da19bd052431a97671d44a42634adf710b40c4" + integrity sha512-rSOKNYUmaxy0om1BNjMN4ezNT6VKK+2xF4GBhc81mkH7L60i6dp8qPYrkndNLT3QPphoII3maL9PVC9XmhHwVQ== + dependencies: + mute-stream "~0.0.4" + +readable-stream@^3.4.0, readable-stream@^3.6.0: + version "3.6.2" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" + integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +readable-stream@~2.3.6: + version "2.3.8" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b" + integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA== + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + +readdirp@~3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" + integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== + dependencies: + picomatch "^2.2.1" + +readline-sync@^1.4.10: + version "1.4.10" + resolved "https://registry.yarnpkg.com/readline-sync/-/readline-sync-1.4.10.tgz#41df7fbb4b6312d673011594145705bf56d8873b" + integrity sha512-gNva8/6UAe8QYepIQH/jQ2qn91Qj0B9sYjMBBs3QOB8F2CXcKgLxQaJRP76sWVRQt+QU+8fAkCbCvjjMFu7Ycw== + +rechoir@^0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384" + integrity sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw== + dependencies: + resolve "^1.1.6" + +rechoir@^0.7.0: + version "0.7.1" + resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.7.1.tgz#9478a96a1ca135b5e88fc027f03ee92d6c645686" + integrity sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg== + dependencies: + resolve "^1.9.0" + +require-in-the-middle@^5.0.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/require-in-the-middle/-/require-in-the-middle-5.2.0.tgz#4b71e3cc7f59977100af9beb76bf2d056a5a6de2" + integrity sha512-efCx3b+0Z69/LGJmm9Yvi4cqEdxnoGnxYxGxBghkkTTFeXRtTCmmhO0AnAfHz59k957uTSuy8WaHqOs8wbYUWg== + dependencies: + debug "^4.1.1" + module-details-from-path "^1.0.3" + resolve "^1.22.1" + +resolve-cwd@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d" + integrity sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg== + dependencies: + resolve-from "^5.0.0" + +resolve-from@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" + integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== + +resolve@^1.1.6, resolve@^1.22.1, resolve@^1.9.0: + version "1.22.8" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d" + integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== + dependencies: + is-core-module "^2.13.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + +rimraf@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" + integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== + dependencies: + glob "^7.1.3" + +run-series@^1.1.8: + version "1.1.9" + resolved "https://registry.yarnpkg.com/run-series/-/run-series-1.1.9.tgz#15ba9cb90e6a6c054e67c98e1dc063df0ecc113a" + integrity sha512-Arc4hUN896vjkqCYrUXquBFtRZdv1PfLbTYP71efP6butxyQ0kWpiNJyAgsxscmQg1cqvHY32/UCBzXedTpU2g== + +safe-buffer@5.2.1, safe-buffer@^5.1.0, safe-buffer@^5.2.1, safe-buffer@~5.2.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + +safe-stable-stringify@^2.3.1: + version "2.4.3" + resolved "https://registry.yarnpkg.com/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz#138c84b6f6edb3db5f8ef3ef7115b8f55ccbf886" + integrity sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g== + +"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0", safer-buffer@^2.1.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +sax@^1.2.4: + version "1.3.0" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.3.0.tgz#a5dbe77db3be05c9d1ee7785dbd3ea9de51593d0" + integrity sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA== + +schema-utils@^3.1.1, schema-utils@^3.2.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.3.0.tgz#f50a88877c3c01652a15b622ae9e9795df7a60fe" + integrity sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg== + dependencies: + "@types/json-schema" "^7.0.8" + ajv "^6.12.5" + ajv-keywords "^3.5.2" + +semver@^5.3.0, semver@^5.5.0, semver@^5.7.1: + version "5.7.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" + integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== + +semver@^6.0.0: + version "6.3.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== + +semver@^7.2, semver@^7.3.5, semver@~7.5.0, semver@~7.5.4: + version "7.5.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" + integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== + dependencies: + lru-cache "^6.0.0" + +semver@~7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e" + integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A== + +send@0.18.0: + version "0.18.0" + resolved "https://registry.yarnpkg.com/send/-/send-0.18.0.tgz#670167cc654b05f5aa4a767f9113bb371bc706be" + integrity sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg== + dependencies: + debug "2.6.9" + depd "2.0.0" + destroy "1.2.0" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + fresh "0.5.2" + http-errors "2.0.0" + mime "1.6.0" + ms "2.1.3" + on-finished "2.4.1" + range-parser "~1.2.1" + statuses "2.0.1" + +serialize-javascript@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.1.tgz#b206efb27c3da0b0ab6b52f48d170b7996458e5c" + integrity sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w== + dependencies: + randombytes "^2.1.0" + +serve-static@1.15.0: + version "1.15.0" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.15.0.tgz#faaef08cffe0a1a62f60cad0c4e513cff0ac9540" + integrity sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g== + dependencies: + encodeurl "~1.0.2" + escape-html "~1.0.3" + parseurl "~1.3.3" + send "0.18.0" + +set-blocking@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw== + +set-function-length@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.1.1.tgz#4bc39fafb0307224a33e106a7d35ca1218d659ed" + integrity sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ== + dependencies: + define-data-property "^1.1.1" + get-intrinsic "^1.2.1" + gopd "^1.0.1" + has-property-descriptors "^1.0.0" + +setimmediate@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" + integrity sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA== + +setprototypeof@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" + integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== + +shallow-clone@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3" + integrity sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA== + dependencies: + kind-of "^6.0.2" + +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + +shelljs@~0.8: + version "0.8.5" + resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.8.5.tgz#de055408d8361bed66c669d2f000538ced8ee20c" + integrity sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow== + dependencies: + glob "^7.0.0" + interpret "^1.0.0" + rechoir "^0.6.2" + +shimmer@^1.1.0, shimmer@^1.2.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/shimmer/-/shimmer-1.2.1.tgz#610859f7de327b587efebf501fb43117f9aff337" + integrity sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw== + +side-channel@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" + integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw== + dependencies: + call-bind "^1.0.0" + get-intrinsic "^1.0.2" + object-inspect "^1.9.0" + +signal-exit@^3.0.0, signal-exit@^3.0.3: + version "3.0.7" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" + integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== + +simple-swizzle@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz#a4da6b635ffcccca33f70d17cb92592de95e557a" + integrity sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg== + dependencies: + is-arrayish "^0.3.1" + +simple-update-notifier@^1.0.7: + version "1.1.0" + resolved "https://registry.yarnpkg.com/simple-update-notifier/-/simple-update-notifier-1.1.0.tgz#67694c121de354af592b347cdba798463ed49c82" + integrity sha512-VpsrsJSUcJEseSbMHkrsrAVSdvVS5I96Qo1QAQ4FxQ9wXFcB+pjj7FB7/us9+GcgfW4ziHtYMc1J0PLczb55mg== + dependencies: + semver "~7.0.0" + +smart-buffer@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.2.0.tgz#6e1d71fa4f18c05f7d0ff216dd16a481d0e8d9ae" + integrity sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg== + +smtp-channel@0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/smtp-channel/-/smtp-channel-0.2.3.tgz#db660868ad3b5296775ec8078c38aa634c4dcda6" + integrity sha512-5PR7qW88Z6GcLfvgop6WpUMwNoJKKQY9CP8Gpk5INbSs3UtWqMgFYkTTMaynnbj+6e4D/L0JuagbuH3fjm4+Fw== + dependencies: + line-buffer "^0.1.4" + promised-timeout "^0.2.0" + +smtp-client@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/smtp-client/-/smtp-client-0.4.0.tgz#2015da1f4d9821e67bf22ced01568b027d305245" + integrity sha512-TvxdX02b96hN6732TGkXcXHsaTeXgVMXjdAhYzz/pLGE6V1p/5jh1XYJa/PfE+O21jHLmwHYWVP3c7JNFWwmOg== + dependencies: + promised-timeout "0.5.1" + smtp-channel "0.2.3" + +socks-proxy-agent@^8.0.2: + version "8.0.2" + resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-8.0.2.tgz#5acbd7be7baf18c46a3f293a840109a430a640ad" + integrity sha512-8zuqoLv1aP/66PHF5TqwJ7Czm3Yv32urJQHrVyhD7mmA6d61Zv8cIXQYPTWwmg6qlupnPvs/QKDmfa4P/qct2g== + dependencies: + agent-base "^7.0.2" + debug "^4.3.4" + socks "^2.7.1" + +socks@^2.7.1: + version "2.7.1" + resolved "https://registry.yarnpkg.com/socks/-/socks-2.7.1.tgz#d8e651247178fde79c0663043e07240196857d55" + integrity sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ== + dependencies: + ip "^2.0.0" + smart-buffer "^4.2.0" + +source-list-map@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34" + integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw== + +source-map-support@0.5.21, source-map-support@~0.5.20: + version "0.5.21" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" + integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +sprintf-js@1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.2.tgz#da1765262bf8c0f571749f2ad6c26300207ae673" + integrity sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug== + +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== + +stack-trace@0.0.x: + version "0.0.10" + resolved "https://registry.yarnpkg.com/stack-trace/-/stack-trace-0.0.10.tgz#547c70b347e8d32b4e108ea1a2a159e5fdde19c0" + integrity sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg== + +statuses@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" + integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== + +"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" + +strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +stripe@^14.4.0: + version "14.7.0" + resolved "https://registry.yarnpkg.com/stripe/-/stripe-14.7.0.tgz#11b24a1b5651a22db829aba8b2b8b495dbd3308f" + integrity sha512-A6XWH76K0K7IgEBz4odIVxjhkdPduSPafZju6ltAFU0oMbWJlfeaDyMTfRJ9vcYA9zqENfwRizExD/2nZ4rQ1A== + dependencies: + "@types/node" ">=8.1.0" + qs "^6.11.0" + +style-loader@^3.3.1: + version "3.3.3" + resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-3.3.3.tgz#bba8daac19930169c0c9c96706749a597ae3acff" + integrity sha512-53BiGLXAcll9maCYtZi2RCQZKa8NQQai5C4horqKyRmHj9H7QmcUyucrH+4KW/gBQbXM2AsB0axoEcFZPlfPcw== + +supports-color@^5.5.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + dependencies: + has-flag "^3.0.0" + +supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +supports-color@^8.0.0: + version "8.1.1" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" + integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== + dependencies: + has-flag "^4.0.0" + +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + +systeminformation@^5.7: + version "5.21.20" + resolved "https://registry.yarnpkg.com/systeminformation/-/systeminformation-5.21.20.tgz#16bd0d0c4118d00e670409a1f592ba7043159d2c" + integrity sha512-AyS1fNc+MDoAJtFknFbbo587H8h6yejJwM+H9rVusnOToIEkiMehMyD5JM7o3j55Cto20MawIZrcgNMgd4BfOQ== + +tapable@^2.1.1, tapable@^2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0" + integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== + +tar@^6.1.11: + version "6.2.0" + resolved "https://registry.yarnpkg.com/tar/-/tar-6.2.0.tgz#b14ce49a79cb1cd23bc9b016302dea5474493f73" + integrity sha512-/Wo7DcT0u5HUV486xg675HtjNd3BXZ6xDbzsCUZPt5iw8bTQ63bP0Raut3mvro9u+CUyq7YQd8Cx55fsZXxqLQ== + dependencies: + chownr "^2.0.0" + fs-minipass "^2.0.0" + minipass "^5.0.0" + minizlib "^2.1.1" + mkdirp "^1.0.3" + yallist "^4.0.0" + +terser-webpack-plugin@^5.3.7: + version "5.3.9" + resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.9.tgz#832536999c51b46d468067f9e37662a3b96adfe1" + integrity sha512-ZuXsqE07EcggTWQjXUj+Aot/OMcD0bMKGgF63f7UxYcu5/AJF53aIpK1YoP5xR9l6s/Hy2b+t1AM0bLNPRuhwA== + dependencies: + "@jridgewell/trace-mapping" "^0.3.17" + jest-worker "^27.4.5" + schema-utils "^3.1.1" + serialize-javascript "^6.0.1" + terser "^5.16.8" + +terser@^5.16.8: + version "5.25.0" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.25.0.tgz#6579b4cca45b08bf0fdaa1a04605fd5860dfb2ac" + integrity sha512-we0I9SIsfvNUMP77zC9HG+MylwYYsGFSBG8qm+13oud2Yh+O104y614FRbyjpxys16jZwot72Fpi827YvGzuqg== + dependencies: + "@jridgewell/source-map" "^0.3.3" + acorn "^8.8.2" + commander "^2.20.0" + source-map-support "~0.5.20" + +text-hex@1.0.x: + version "1.0.0" + resolved "https://registry.yarnpkg.com/text-hex/-/text-hex-1.0.0.tgz#69dc9c1b17446ee79a92bf5b884bb4b9127506f5" + integrity sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg== + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +toidentifier@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" + integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== + +touch@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/touch/-/touch-3.1.0.tgz#fe365f5f75ec9ed4e56825e0bb76d24ab74af83b" + integrity sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA== + dependencies: + nopt "~1.0.10" + +tr46@~0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" + integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== + +triple-beam@^1.3.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/triple-beam/-/triple-beam-1.4.1.tgz#6fde70271dc6e5d73ca0c3b24e2d92afb7441984" + integrity sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg== + +tslib@1.9.3: + version "1.9.3" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286" + integrity sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ== + +tslib@^2.0.1: + version "2.6.2" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" + integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== + +tv4@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/tv4/-/tv4-1.3.0.tgz#d020c846fadd50c855abb25ebaecc68fc10f7963" + integrity sha512-afizzfpJgvPr+eDkREK4MxJ/+r8nEEHcmitwgnPUqpaP+FpwQyadnxNoSACbgc/b1LsZYtODGoPiFxQrgJgjvw== + +tx2@~1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/tx2/-/tx2-1.0.5.tgz#ee0b0e5e2c351f8d23e54bdf46dd60afa3bbc73d" + integrity sha512-sJ24w0y03Md/bxzK4FU8J8JveYYUbSs2FViLJ2D/8bytSiyPRbuE3DyL/9UKYXTZlV3yXq0L8GLlhobTnekCVg== + dependencies: + json-stringify-safe "^5.0.1" + +type-is@~1.6.18: + version "1.6.18" + resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" + integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== + dependencies: + media-typer "0.3.0" + mime-types "~2.1.24" + +uc.micro@^1.0.1, uc.micro@^1.0.5: + version "1.0.6" + resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.6.tgz#9c411a802a409a91fc6cf74081baba34b24499ac" + integrity sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA== + +uglify-js@^3.1.4: + version "3.17.4" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.17.4.tgz#61678cf5fa3f5b7eb789bb345df29afb8257c22c" + integrity sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g== + +undefsafe@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/undefsafe/-/undefsafe-2.0.5.tgz#38733b9327bdcd226db889fb723a6efd162e6e2c" + integrity sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA== + +undici-types@~5.26.4: + version "5.26.5" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" + integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== + +universalify@^0.1.0: + version "0.1.2" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" + integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== + +universalify@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.1.tgz#168efc2180964e6386d061e094df61afe239b18d" + integrity sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw== + +unpipe@1.0.0, unpipe@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== + +update-browserslist-db@^1.0.13: + version "1.0.13" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz#3c5e4f5c083661bd38ef64b6328c26ed6c8248c4" + integrity sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg== + dependencies: + escalade "^3.1.1" + picocolors "^1.0.0" + +uri-js@^4.2.2: + version "4.4.1" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" + integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== + dependencies: + punycode "^2.1.0" + +util-deprecate@^1.0.1, util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== + +util@^0.10.3: + version "0.10.4" + resolved "https://registry.yarnpkg.com/util/-/util-0.10.4.tgz#3aa0125bfe668a4672de58857d3ace27ecb76901" + integrity sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A== + dependencies: + inherits "2.0.3" + +utils-merge@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" + integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA== + +uuid@^3.2.1: + version "3.4.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" + integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== + +uuid@^9.0.0: + version "9.0.1" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.1.tgz#e188d4c8853cc722220392c424cd637f32293f30" + integrity sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA== + +vary@^1, vary@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" + integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== + +vizion@~2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/vizion/-/vizion-2.2.1.tgz#04201ea45ffd145d5b5210e385a8f35170387fb2" + integrity sha512-sfAcO2yeSU0CSPFI/DmZp3FsFE9T+8913nv1xWBOyzODv13fwkn6Vl7HqxGpkr9F608M+8SuFId3s+BlZqfXww== + dependencies: + async "^2.6.3" + git-node-fs "^1.0.0" + ini "^1.3.5" + js-git "^0.7.8" + +watchpack@^2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.4.0.tgz#fa33032374962c78113f93c7f2fb4c54c9862a5d" + integrity sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg== + dependencies: + glob-to-regexp "^0.4.1" + graceful-fs "^4.1.2" + +webidl-conversions@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" + integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== + +webpack-cli@^4.9.1: + version "4.10.0" + resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-4.10.0.tgz#37c1d69c8d85214c5a65e589378f53aec64dab31" + integrity sha512-NLhDfH/h4O6UOy+0LSso42xvYypClINuMNBVVzX4vX98TmTaTUxwRbXdhucbFMd2qLaCTcLq/PdYrvi8onw90w== + dependencies: + "@discoveryjs/json-ext" "^0.5.0" + "@webpack-cli/configtest" "^1.2.0" + "@webpack-cli/info" "^1.5.0" + "@webpack-cli/serve" "^1.7.0" + colorette "^2.0.14" + commander "^7.0.0" + cross-spawn "^7.0.3" + fastest-levenshtein "^1.0.12" + import-local "^3.0.2" + interpret "^2.2.0" + rechoir "^0.7.0" + webpack-merge "^5.7.3" + +webpack-merge@^5.7.3: + version "5.10.0" + resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-5.10.0.tgz#a3ad5d773241e9c682803abf628d4cd62b8a4177" + integrity sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA== + dependencies: + clone-deep "^4.0.1" + flat "^5.0.2" + wildcard "^2.0.0" + +webpack-sources@^1.4.3: + version "1.4.3" + resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.4.3.tgz#eedd8ec0b928fbf1cbfe994e22d2d890f330a933" + integrity sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ== + dependencies: + source-list-map "^2.0.0" + source-map "~0.6.1" + +webpack-sources@^3.2.3: + version "3.2.3" + resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde" + integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w== + +webpack@^5.64.2: + version "5.89.0" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.89.0.tgz#56b8bf9a34356e93a6625770006490bf3a7f32dc" + integrity sha512-qyfIC10pOr70V+jkmud8tMfajraGCZMBWJtrmuBymQKCrLTRejBI8STDp1MCyZu/QTdZSeacCQYpYNQVOzX5kw== + dependencies: + "@types/eslint-scope" "^3.7.3" + "@types/estree" "^1.0.0" + "@webassemblyjs/ast" "^1.11.5" + "@webassemblyjs/wasm-edit" "^1.11.5" + "@webassemblyjs/wasm-parser" "^1.11.5" + acorn "^8.7.1" + acorn-import-assertions "^1.9.0" + browserslist "^4.14.5" + chrome-trace-event "^1.0.2" + enhanced-resolve "^5.15.0" + es-module-lexer "^1.2.1" + eslint-scope "5.1.1" + events "^3.2.0" + glob-to-regexp "^0.4.1" + graceful-fs "^4.2.9" + json-parse-even-better-errors "^2.3.1" + loader-runner "^4.2.0" + mime-types "^2.1.27" + neo-async "^2.6.2" + schema-utils "^3.2.0" + tapable "^2.1.1" + terser-webpack-plugin "^5.3.7" + watchpack "^2.4.0" + webpack-sources "^3.2.3" + +whatwg-url@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" + integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw== + dependencies: + tr46 "~0.0.3" + webidl-conversions "^3.0.0" + +which@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +wide-align@^1.1.2: + version "1.1.5" + resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.5.tgz#df1d4c206854369ecf3c9a4898f1b23fbd9d15d3" + integrity sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg== + dependencies: + string-width "^1.0.2 || 2 || 3 || 4" + +wildcard@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.1.tgz#5ab10d02487198954836b6349f74fff961e10f67" + integrity sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ== + +winston-transport@^4.5.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/winston-transport/-/winston-transport-4.6.0.tgz#f1c1a665ad1b366df72199e27892721832a19e1b" + integrity sha512-wbBA9PbPAHxKiygo7ub7BYRiKxms0tpfU2ljtWzb3SjRjv5yl6Ozuy/TkXf00HTAt+Uylo3gSkNwzc4ME0wiIg== + dependencies: + logform "^2.3.2" + readable-stream "^3.6.0" + triple-beam "^1.3.0" + +winston@^3.3.3: + version "3.11.0" + resolved "https://registry.yarnpkg.com/winston/-/winston-3.11.0.tgz#2d50b0a695a2758bb1c95279f0a88e858163ed91" + integrity sha512-L3yR6/MzZAOl0DsysUXHVjOwv8mKZ71TrA/41EIduGpOOV5LQVodqN+QdQ6BS6PJ/RdIshZhq84P/fStEZkk7g== + dependencies: + "@colors/colors" "^1.6.0" + "@dabh/diagnostics" "^2.0.2" + async "^3.2.3" + is-stream "^2.0.0" + logform "^2.4.0" + one-time "^1.0.0" + readable-stream "^3.4.0" + safe-stable-stringify "^2.3.1" + stack-trace "0.0.x" + triple-beam "^1.3.0" + winston-transport "^4.5.0" + +wordwrap@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" + integrity sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q== + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== + +ws@^7.0.0: + version "7.5.9" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.9.tgz#54fa7db29f4c7cec68b1ddd3a89de099942bb591" + integrity sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q== + +ws@~7.4.0: + version "7.4.6" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.6.tgz#5654ca8ecdeee47c33a9a4bf6d28e2be2980377c" + integrity sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A== + +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== + +yamljs@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/yamljs/-/yamljs-0.3.0.tgz#dc060bf267447b39f7304e9b2bfbe8b5a7ddb03b" + integrity sha512-C/FsVVhht4iPQYXOInoxUM/1ELSf9EsgKH34FofQOp6hwCPrW4vG4w5++TED3xRUo8gD7l0P1J1dLlDYzODsTQ== + dependencies: + argparse "^1.0.7" + glob "^7.0.5"