Apps

update-currency-rates (Airtable)

Finds pricelists on Airtable on all bases by checking 2 tables: Pricelist & (*).
(*) tables contains all the currency rates in this format:

(Please note that SEK is master currency)
Code. Rate
—- —-
SEK 1.0000000
USD 0.0972745
AED 0.3572467
AFN 8.4328730
ALL 10.0878786
AMD 37.8046774
ANG 0.1755555
AOA 49.2695078
ARS 20.5039556
AUD 0.1440093
AWG 0.1753372
AZN 0.1653666
BAM 0.1744463
BBD 0.1945489
BDT 10.2787552
BGN 0.1737087
BHD 0.0366749
BIF 202.5049957
BMD 0.0972745
BND 0.1291695
BOB 0.6730080
BRL 0.4934148
BSD 0.0972745
BTC 0.0000034
BTN 8.0083671
BWP 1.2691345
BYN 0.2458608
BZD 0.1963417
CAD 0.1307763
CDF 199.0343488
CHF 0.0880890
CLF 0.0028475
CLP 78.4471353
CNH 0.6683367
CNY 0.6691996
COP 446.2572805
CRC 52.8025693
CUC 0.0972745
CUP 2.5048170
CVE 9.8345643
CZK 2.0848492
DJF 17.3014645
DKK 0.6613413
DOP 5.3407418
DZD 13.1941608
EGP 3.0057902
ERN 1.4591167
ETB 5.2673778
EUR 0.0887559
FJD 0.2135126
FKP 0.0778768
GBP 0.0778768
GEL 0.2475635
GGP 0.0778768
GHS 1.0591830
GIP 0.0778768
GMD 6.0504707
GNF 835.2002281
GTQ 0.7598604
GYD 20.6000821
HKD 0.7635733
HNL 2.3920638
HRK 0.6688079
HTG 15.0497585
HUF 33.5033140
IDR 1451.5049914
ILS 0.3478009
IMP 0.0778768
INR 7.9869619
IQD 142.0206938
IRR 4112.2772804
ISK 13.2507253
JEP 0.0778768
JMD 14.7267398
JOD 0.0690065
JPY 12.8089372
KES 12.9423653
KGS 8.5037322
KHR 394.4036839
KMF 43.7005883
KPW 87.5470030
KRW 127.7350794
KWD 0.0298180
KYD 0.0811750
KZT 43.5906326
LAK 1667.1171376
LBP 1474.6429703
LKR 31.2678655
LRD 15.8022336
LSL 1.7388864
LYD 0.4650268
MAD 0.9945853
MDL 1.7770354
MGA 423.1478466
MKD 5.4706031
MMK 204.5568865
MNT 342.3087818
MOP 0.7877549
MRO
MRU 3.3248406
MUR 4.4162599
MVR 1.4931628
MWK 99.8270776
MXN 1.7624934
MYR 0.4276185
MZN 6.2158363
NAD 1.7431581
NGN 44.7783466
NIO 3.5587857
NOK 1.0021881
NPR 12.8134062
NZD 0.1531816
OMR 0.0374542
PAB 0.0972745
PEN 0.3673063
PGK 0.3428924
PHP 5.2940160
PKR 27.9550383
PLN 0.4152345
PYG 699.8093301
QAR 0.3579726
RON 0.4376698
RSD 10.4081887
RUB 7.7324680
RWF 107.4342575
SAR 0.3648546
SBD 0.8054728
SCR 1.3659748
SDG 58.2673942
SGD 0.1289742
SHP 0.0778768
SLL 1718.3531203
SOS 55.3428859
SRD 3.4945845
SSP 12.6709696
STD 2220.1910727
STN 2.2037495
SVC 0.8523411
SYP 244.4049683
SZL 1.7352759
THB 3.3052609
TJS 1.0641882
TMT 0.3404606
TND 0.2959575
TOP 0.2280113
TRY 1.8709378
TTD 0.6579504
TWD 2.9640010
TZS 227.6222078
UAH 3.5798085
UGX 367.9279694
UYU 3.7527751
UZS 1108.7357660
VEF
VES 2.3792455
VND 2282.0985133
VUV 11.5736165
WST 0.2651225
XAF 58.2200587
XAG 0.0038793
XAU 0.0000481
XCD 0.2628891
XDR 0.0724069
XOF 58.2200587
XPD 0.0000673
XPF 10.5913970
XPT 0.0000951
YER 24.3477994
ZAR 1.7422847
ZMW 1.9845085
ZWL 31.3223722

Updates the currency rates for all pricelists every 24 hours.
* NOTE: Add ocrKey & AirtableToken to make the script work.


(async function() {
  var localMode = typeof addEventListener === "undefined" && typeof input === "undefined" ? true : false;
  if (localMode === true) {
    console.log(`
      Global base packages required:
      form-data
      node-fetch@2.6.7
    `);
    var workerThreads = require('worker_threads');
    
    var fs = require("fs");
    var https = require("https");
    var http = require("http");
        
        var FormData = require("form-data");
    var fetch = require("node-fetch");
    var appConfig = {"mainFile":"main.js","port":7052,"bundle":false};
    (function() {
      var parts = __filename.split("/");
      var fileName = parts.pop();
      var file = parts.join("/") + "/.bundle." + fileName + "on";
      if (fs.existsSync(file)) {
        var options = JSON.parse((function() {
          var contents = fs.readFileSync(file, "utf8");
          contents = contents.replace(/\/\*[\s\S]*?\*\/|\/\/.*/g,'').trim();
          return contents;
        })());
        for (var key in options) appConfig[key] = options[key];
      }
    })();
    if (typeof appConfig.reverseProxy !== "undefined" && typeof appConfig.reverseProxy.domains !== "undefined") {
      (function() {
        var redbird = require('redbird')({
          port: 80,
          letsencrypt: {
            path: __dirname + '/certs'
          },
          ssl: {
            port: 443,
            key: "certs/key.pem",
            cert: "certs/cert.pem"
          }
        });
        for (var domain in appConfig.reverseProxy.domains) {
          redbird.register(domain, appConfig.reverseProxy.domains[domain]);
        }
      })();
    }
    if (typeof appConfig.port === "number") {
      if (typeof appConfig.host !== "string") appConfig.host = "127.0.0.1";
      var httpOrHttps = appConfig.noCertificate === true ? http : https;
        if (workerThreads.isMainThread) {
        var server = httpOrHttps.createServer({
          key: `-----BEGIN RSA PRIVATE KEY-----
MIIEogIBAAKCAQEAr68IoGyAC1HCh6N8djdeFYewWKbqywedHCVOpF/bFGP47KJP
W26Yng81OgK/4RkXp07My1B16jeFN5NXqmfdtQENTckxtmJA2j6/4d2y0m9aacLl
dx1a4mNzbM78Ln9smEk0+SWp6fypN0kg9OER3ljQpCb8YspgwHbnJLlky2c6q84S
cC8Oc2hhPXo/CJit2xoI3m8SAaqfDsHAmyqd2bsBBaH9ssgpaIxGQ2QWFfHZJOSE
D5AUEO+HuJQverSNzxAsLItuKbUfvGvm/tUvXymlStmDyWIwNvfdneuozZ5WPIEP
d43s++QKmI5qlA1Z+j+wcY3zFyqUM1NcWBUhrQIDAQABAoIBACYBtdoO3vyT6YOy
iKCCheYefrYPFkhqE0EdiQ/idODKZ/W5f3WGTZoULC2qnpwx834MfB2YAIp1DGrj
g1moMryPx7MGTazpQKJ2ZMiWT7Nax7KNqVrFjP3hCf2GIeRlSLcBT2Z/EW0/bdQ6
C9kuP9FcYXbBwGQW6Ct7DbJSMU4XYUErl4sXb7H9vwARlWxJ4Gq8fldagy7T+oX2
OwObO9/e8GdkJBlr5PM2ZZOES3avGjJnPGFwQ/wCreNaqt8ir+fFmd6odLjxh2mH
sn7WwIC/Eyp/TcM/O7bZT3SfSCWbKsk2NGQCX3cV49Zr37+4slujtAnK6sJI2Jr6
riPPvEECgYEA2DgXHphkZwTGuhMMCnaDuKbv3y7iJ15oggTo8CAMfCb7sE1FEPOs
0gnYG7p8xa1HPNGx5PkQCpIIz8pMC9+0Dqxu7bX5t0D0JQDDB+8UhtGbEgXPrAF1
NHpWHil6Or/KmfF5kXe15gKkkohVFrJw0HyMnBAvSbzd/L2mwa1xOQkCgYEA0AG6
n4Im1aFOnOnHaXD6w4CP1ehysHkFoxad+EeUu9d1dfIrPlUCTwtL0tHRDR0uehA4
PaUPThFllTCuqGTXawxuAdNxsa8KttvjTPEI29dqB/4IgIYTNXqZnfrgNWC3mNLw
InyrlR8RsrRAS1R6AyWxWh9Nm0iKAKhsxgdOgIUCgYB4mUB79i/6LfXSD5GlvFjY
A3TDnVjS8JuF+csbNCUCkpPL0C13uRJpzMfXH3s8ntufFq8Msca6vp1fmMw1yz6Y
+KCeweNYzUff477ki/t8/yhpMwiUPfPro1ipViUw44zTtJZEButUMaEtghFDqZ+3
CeE7ouNdU5TVxcpfOKhwUQKBgApmm7tQGbsC5thnxCXclV1jN0394oY6dvKxtdJt
Wd2Op3vvUQQ74fKr4O24uhhKxkEqQHWspDhGHGs6VPFsoWzj4ThMJ1o4I3QDSLlX
MBc2DUI7DJfInHtHFxlUKxPgMy38Fi/TRg0d0Ze69aAOqE8x+k1EVXAXT3c69L1u
Lhm1AoGAcOJy7MewUUegFUcbg+hYTd8zXZFX5Tzl5eiExqyWxBnp2/Nuasw4950X
Xr0lSnFx2cXrqiLjgYxFQdCwaJmL0uAuYjALzKvsQaCpbNJ+kMQ28mlDp3lYHmXp
s8HGa0SNzjRLQWRMBw0dx7IAAsuyFmv/6e6jvg2ksxrUQ8ViidE=
-----END RSA PRIVATE KEY-----
`,
          cert: `-----BEGIN CERTIFICATE-----
MIICmjCCAYICCQCiHtY6daqa3zANBgkqhkiG9w0BAQUFADAPMQ0wCwYDVQQKDARj
ZXJ0MB4XDTIxMDQyNjE0NDQ0MFoXDTQ4MDkxMDE0NDQ0MFowDzENMAsGA1UECgwE
Y2VydDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK+vCKBsgAtRwoej
fHY3XhWHsFim6ssHnRwlTqRf2xRj+OyiT1tumJ4PNToCv+EZF6dOzMtQdeo3hTeT
V6pn3bUBDU3JMbZiQNo+v+HdstJvWmnC5XcdWuJjc2zO/C5/bJhJNPklqen8qTdJ
IPThEd5Y0KQm/GLKYMB25yS5ZMtnOqvOEnAvDnNoYT16PwiYrdsaCN5vEgGqnw7B
wJsqndm7AQWh/bLIKWiMRkNkFhXx2STkhA+QFBDvh7iUL3q0jc8QLCyLbim1H7xr
5v7VL18ppUrZg8liMDb33Z3rqM2eVjyBD3eN7PvkCpiOapQNWfo/sHGN8xcqlDNT
XFgVIa0CAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAM6yKAW3NJi4unaxH5kUaqAwb
HKfeZANu8UP1n6GwpD4n/unC2EGRo5gkUVKZonErPEIv6wL6KDX1CKFT46AtSnMi
QIpNriYPdSsBsADvnwRDTlCiv667Jzjb0jnI38aBZ7vw0z+QHbplfy4Ss6Q3NkEN
HGXibAidrQrj/zptLxAsp+MYfg380uQhJHIcXbqynCozKtORFL3FcR+SDb6J3h1W
0atSlKPkAsRDTCSMQT700d3w5UaQXyx+Ac4xEuEqvZXJLKAOETBfcgsaIIVyc6H2
1khfWxkMvEEbkHQfSX8ps1G5An3jUdq2nofwgphxBCEriG1neVfbTP3ZS6mE4A==
-----END CERTIFICATE-----
`
        }).listen(appConfig.port, appConfig.host !== "127.0.0.1" ? appConfig.host : undefined);
        server.on("request", async function(req, res) {
          req.text = async function() {
            return new Promise(function(resolve, reject) {
              var text = "";
              req.on("data", function(chunk) {
                text += chunk.toString();
              });
              req.on("end", function(chunk) {
                resolve(text);
              });
            });
          }
          req.arrayBuffer = async function() {
            return new Promise(function(resolve, reject) {
              var chunkedData = Buffer.from("");
              req.on("data", function(chunk) {
                chunkedData = Buffer.concat([chunkedData, chunk]);
              });
              req.on("end", function(chunk) {
                resolve(chunkedData);
              });
            });
          }
          req.headers.get = function(match) {
            for (var key in req.headers) {
              if (key.toLowerCase() === match.toLowerCase()) return req.headers[key];
            }
          };
          req.url = (appConfig.noCertificate === true ? "http" : "https") + "://" + appConfig.host + ":" + appConfig.port + req.url;
          console.log(req.url);
          var result = await addEventListener("fetch", {request: req});
          if (
            app.has(result) === true
            && app.has(result.data) === true
            && app.has(result.options) === true
            && app.has(result.options.status) === true
            && app.has(result.options.headers) === true
          ) {
            for (var key in result.options.headers) res.setHeader(key, result.options.headers[key]);
            res.statusCode = result.options.status;
            if (typeof result.data === "object" && app.has(result.data.byteLength) === true) {
              res.end(Buffer.from(result.data, "binary"));
            } else {
              res.end(result.data);
            }
          } else {
            if (app.has(app.utils) && app.has(app.utils) && app.has(app.utils.frontend) && app.has(app.utils.frontend.static)) {
              app.utils.frontend.static(req, res);
            } else {
              res.statusCode = 400;
              res.end("Could not process in local mode.");
            }
          }
        });
        console.log("Server is listening on " + appConfig.host + ":" + appConfig.port);
        addEventListener = async function(action, event) {
          return await app.handleRequest(event.request);
        };
      }
    }
  }
  
  var app = {
  start: function() {
    
  }
};app.startUps = [];
app.workerStartUps= [];
app.callbacks = {static: []};app["build"] = {};app["config"] = {};app["cron"] = {"update": (function() {
  var mod = {
    cronTime: 24 * 60 * 60,
    endpoint: "https://api.airtable.com/v0/",
    airtableToken: "",
    ocrKey: "",
    get headers() {
      return {
        "Content-Type": "application/json",
        "Authorization": "Bearer " + mod.airtableToken
      };
    },
    bases: async function() {
      var result = await fetch(mod.endpoint + "meta/bases", {
        headers: mod.headers
      });
      if (result.status === 200) {
        return (await result.json()).bases;
      } else {
        console.log(result.status, await result.text());
      }
    },
    getTables: async function() {
      var bases = await mod.bases();
      if (app.has(bases)) {
        var list = [];
        for (var i=0; i<=bases.length-1; i++) {
          var base = bases[i];
          console.log("(" + (i + 1) + "/" + bases.length + ") Checking base " + base.name);
          var result = await fetch(mod.endpoint + "meta/bases/" + base.id + "/tables", {
            headers: mod.headers
          });
          if (result.status === 200) {
            var found = {base: base};
            var tables = (await result.json()).tables;
            for (var t=0; t<=tables.length-1; t++) {
              var table = tables[t];
              if (table.name.toLowerCase().trim() === "pricelist") found.pricelist = table;
              if (table.name.toLowerCase().trim() === "*") found.currency = table;
            }
            if (app.has(found.pricelist) && app.has(found.currency)) {
              list.push(found);
              console.log(app.consoleColors.fgCyan, "Found: " + base.name);
                          }
          } else {
            console.log(result.status, await result.text());
          }
        }
        return list;
      }
    },
    getRecords: async function(table, type, offset) {
      var fetchTable = table[type];
      var result = await fetch(mod.endpoint + table.base.id + "/" + fetchTable.name + "?pageSize=100" + (app.has(offset) ? "&offset=" + offset : ""), {
        headers: mod.headers
      });
      if (result.status === 200) {
        var data = await result.json();
        if (app.has(data.offset)) {
          var moreRecords = await mod.getRecords(table, type, data.offset);
          if (app.has(moreRecords)) {
            for (var i=0; i<=moreRecords.length-1; i++) data.records.push(moreRecords[i]);
              return data.records;
          }
        } else {
          return data.records;
        }
      } else {
        console.log(result.status, await result.text());
      }
    },
    updateRecords: async function(table, type, updates) {
      var fetchTable = table[type];
      while (updates.length > 0) {
        console.log("..." + updates.length);
        var newUpdates = updates.slice(0, 10);
        var result = await fetch(mod.endpoint + table.base.id + "/" + fetchTable.name, {
          method: "PATCH",
          headers: mod.headers,
          body: JSON.stringify({
            records: newUpdates
          })
        });
        if (result.status === 200) {
          updates = updates.slice(10);
        } else {
          console.log(result.status, await result.text());
          break;
        }
      }
    },
    number: function(decimals) {
      if (!decimals) return 1;
      var num = 10;
      for (var i=0; i<=decimals-2; i++) num *= 10;
      return num;
    },
    getRates: async function() {
      var result = await fetch("https://openexchangerates.org/api/latest.json?app_id=" + mod.ocrKey + "&base=USD");
      if (result.status === 200) {
        var json = await result.json();
        var rate = 1;
        var newRates = {};
        for (var cBase in json.rates) {
          if (cBase === "SEK") rate = json.rates[cBase];
        }
        for (var cBase in json.rates) {
          newRates[cBase] = Math.round(json.rates[cBase] / rate * mod.number(8)) / mod.number(8);
        }
        return newRates;
      } else {
        console.log(result.status, await result.text());
      }
    },
    currency: async function() {
      var rates = await mod.getRates();
      if (app.has(rates)) {
        var tables = await mod.getTables();
        for (var i=0; i<=tables.length-1; i++) {
          var table = tables[i];
          console.log(app.consoleColors.fgCyan, table.base.name);
          var updates = [];
          var records = await mod.getRecords(table, "currency");
          for (var r=0; r<=records.length-1; r++) {
            var record = records[r];
            if (app.has(rates[record.fields["Code"]])) {
              updates.push({
                id: record.id,
                fields: {
                  "Rate": rates[record.fields["Code"]]
                }
              });
            }
          }
          console.log("Updating " + updates.length + " record(s).");
          await mod.updateRecords(table, "currency", updates);
                  }
      }
      setTimeout(mod.currency, 1000 * mod.cronTime);
    }
  };
  app.startUps.push(mod.currency);
  return mod;
})(), };app["publish"] = {};
var config = app.config;
var modules = app.modules;
app.has = function(value) {
  var found = true;
  for (var i=0; i<=arguments.length-1; i++) {
    var value = arguments[i];
    if (!(typeof value !== "undefined" && value !== null && value !== "")) found = false;
  }
  return found;
};
(function(left, right) {
  var copyConfig = function(left, right) {
    if (app.has(left) && app.has(right)) {
      for (var key in left) {
        var value = left[key];
        if (typeof value !== "object") {
          right[key] = value;
        } else {
          copyConfig(value, right[key]);
        }
      }
    }
  };
  copyConfig(left, right);
})(appConfig.config, config);
app.response = function(data, options) {
  if (typeof options === "undefined") options = {};
  if (typeof options.headers === "undefined") options.headers = {};
  if (!app.has(options.headers["Access-Control-Allow-Origin"])) options.headers["Access-Control-Allow-Origin"] = "*";
  if (!app.has(options.headers["Access-Control-Allow-Headers"])) options.headers["Access-Control-Allow-Headers"] = "content-type, content";
  if (!app.has(options.headers["Access-Control-Allow-Methods"])) options.headers["Access-Control-Allow-Methods"] = "HEAD, GET, PUT, DELETE, POST, OPTIONS";
  if (
    typeof Response !== "undefined"
    && !(
      typeof __dirname !== "undefined"
      && __dirname.toLowerCase().split("/users/").length > 1
    )
    && app.has(appConfig.workerName)
  ) {
    return new Response(data, options);
  } else {
    return {data: data, options: options};
  }
};
app.logAndResponse = function(data, options) {
  console.log(data);
  return app.response(data, options);
},
app.camelCase = function camelize(str, capitalFirst) {
  if (!app.has(capitalFirst)) capitalFirst = false;
  var result = str.replace(/(?:^\w|[A-Z]|\b\w)/g, function(word, index) {
    return index === 0 ? word.toLowerCase() : word.toUpperCase();
  }).replace(/\s+/g, '');
  if (capitalFirst) result = result.substr(0, 1).toUpperCase() + result.substr(1, 999);
  return result;
};
app.properCase = function(str) {
  return str.replace(
    /\w\S*/g,
    function(txt) { return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase(); }
  );
};
if (app.has(app.api) === true) {   (function() {
    var callbackLevel = function(apiLevel) {
      if (app.has(apiLevel) && !app.has(apiLevel.length)) {
        for (var moduleName in apiLevel) {
          if (app.has(apiLevel[moduleName]) === true) {
            callbackLevel(apiLevel[moduleName]);
            for (var key in apiLevel[moduleName]) {
              (function(moduleName, key) {
                var func = apiLevel[moduleName][key];
                if (key.split("Callback").length > 1 && typeof func === "function") {
                  apiLevel[moduleName][key.split("Callback").shift() + "Multi"] = async function(count, name, callback, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15) {
                    return new Promise(function(resolve, reject) {
                      if (!app.has(count)) count = 1;
                      var rCount = 0;
                      var resolveCount;
                      for (var i=0; i<=count-1; i++) {
                        (async function(index) {
                          var countResult = await apiLevel[moduleName][key.split("Callback").shift()](name, async function(arg1, arg2, arg3, arg4, arg5) {
                            if (typeof callback === "function") {
                              var result = await callback(arg1, arg2, arg3, arg4, arg5);
                              if (result === true && !app.has(resolveCount)) {
                                console.log("MULTI INDEX:", index);
                                resolveCount = index;
                              }
                              return result;
                            }
                          }, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15);
                          rCount += 1;
                          if (resolveCount === index || rCount >= count) resolve(countResult);
                        })(i);
                      }
                    });
                  };
                  apiLevel[moduleName][key.split("Callback").shift()] = async function(name, callback, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15) {
                    if (typeof callback !== "function") {
                      arg15 = arg13; arg14 = arg12; arg13 = arg11; arg12 = arg10; arg11 = arg9; arg10 = arg8;arg9 = arg7; arg8 = arg6; arg7 = arg5; arg6 = arg4; arg5 = arg3; arg4 = arg2; arg3 = arg1; arg2 = callback; arg1 = name;
                    }
                    var output, error;
                    await apiLevel[moduleName][key](async function(data, page) {
                      var result = typeof callback === "function" ? await callback(data, page) : undefined;
                      if (app.has(result) && app.has(result.length)) {
                        if (!app.has(output)) output = [];
                        output = output.concat(result);
                      } else {
                        output = data;
                      }
                      return result;
                    }, function(err, errorText) {
                      error = {error: err, errorText};
                    }, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15);
                    var obj = {};
                    if (typeof callback !== "function") return output;
                    obj[name] = output;
                    obj.error = error;
                    return obj;
                  };
                }
              })(moduleName, key);
            }
          }
        }
      }
    };
    callbackLevel(app.api);
  })();
}
app.handleRequest = async function(request) {
  if (typeof request === "undefined") return false;
  var url = new URL(request.url);
  if (request.method === "OPTIONS") return app.response("Ok", {status: 200});
  if (app.has(config) && app.has(config.api) && app.has(config.api.authorization)) {
    var authorization = app.has(request.headers) ? (app.has(request.headers.get) ? request.headers.get("authorization") : request.headers.authorization) : "";
    if (!app.has(authorization)) authorization = url.searchParams.get("authorization");
    if (typeof config.api.authorization === "string") {
      if (authorization !== config.api.authorization) return app.response("Unauthorized.", {status: 400});
    } else {
      if (typeof config.api.authorization === "object" && config.api.authorization !== null) {
        if (app.has(config.api.authorization.length)) {
          if (config.api.authorization.indexOf(authorization) < 0) return app.response("Unauthorized.", {status: 400});
        } else {
          if (!app.has(config.api.authorization[authorization])) return app.response("Unauthorized.", {status: 400});
          request.condition = config.api.authorization[authorization];
        }
      }
    }
  }
  var parts = url.pathname.split("/");
  parts.shift();
  if (parts.length === 1 && parts[0].trim() === "") parts[0] = "index";
  if (app.indexMode === true) parts = ["index"];
  if (parts.length > 0 && parts[0].trim() !== "") {
    var level = app;
    for (var i=0; i<=parts.length-1; i++) {
      var part = app.camelCase(parts[i].trim().split("-").join(" "));
      if (typeof level[part] !== "undefined" || typeof level["indexDynamic"] !== "undefined") {
        level = !(level === app && typeof level["indexDynamic"] !== "undefined") ? level[part] : level;
        if (i === parts.length-1) {
          if (typeof level === "object" && level !== null && typeof level["index"] === "function") {
            var caller = {};
            caller[part] = async function() { return await level["index"](request); }
            if (!app.has(request.headers.get("start-only"))) {
              return await caller[part]();
            } else {
              caller[part]();
              return app.response("Started.", {status: 200});
            }
          }
          if (typeof level === "function") {
            var caller = {};
            caller[part] = async function() { return await level(request); }
            if (!app.has(request.headers.get("start-only"))) {
              return await caller[part]();
            } else {
              caller[part]();
              return app.response("Started.", {status: 200});
            }
          }
        }
        if (typeof level === "object" && level !== null && typeof level["indexDynamic"] === "function") {
          var caller = {};
          caller[part] = async function() { return await level["indexDynamic"](request); }
          if (!app.has(request.headers.get("start-only"))) {
            return await caller[part]();
          } else {
            caller[part]();
            return app.response("Started.", {status: 200});
          }
        }
      }
    }
  }
  if (typeof localMode === "undefined" || localMode !== true) return app.response("Unknown request: " + url.pathname, {status: 400});
};
if (typeof localMode === "undefined" && typeof addEventListener !== "undefined") {
  addEventListener('fetch', event => {
    event.respondWith(app.handleRequest(event.request));
  });
}
app.consoleColors = {
  reset: "\x1b[0m%s\x1b[0m",
  bright: "\x1b[1m%s\x1b[0m",
  dim: "\x1b[2m%s\x1b[0m",
  underscore: "\x1b[4m%s\x1b[0m",
  blink: "\x1b[5m%s\x1b[0m",
  reverse: "\x1b[7m%s\x1b[0m",
  hidden: "\x1b[8m%s\x1b[0m",
  fgBlack: "\x1b[30m%s\x1b[0m",
  fgRed: "\x1b[31m%s\x1b[0m",
  fgGreen: "\x1b[32m%s\x1b[0m",
  fgYellow: "\x1b[33m%s\x1b[0m",
  fgBlue: "\x1b[34m%s\x1b[0m",
  fgMagenta: "\x1b[35m%s\x1b[0m",
  fgCyan: "\x1b[36m%s\x1b[0m",
  fgWhite: "\x1b[37m%s\x1b[0m",
  fgGray: "\x1b[90m%s\x1b[0m",
  bgBlack: "\x1b[40m%s\x1b[0m",
  bgRed: "\x1b[41m%s\x1b[0m",
  bgGreen: "\x1b[42m%s\x1b[0m",
  bgYellow: "\x1b[43m%s\x1b[0m",
  bgBlue: "\x1b[44m%s\x1b[0m",
  bgMagenta: "\x1b[45m%s\x1b[0m",
  bgCyan: "\x1b[46m%s\x1b[0m",
  bgWhite: "\x1b[47m%s\x1b[0m",
  bgGray: "\x1b[100m%s\x1b[0m"
};

if (typeof app.start === "function") {
  app.start();
}


if ((typeof localMode !== "undefined" && workerThreads.isMainThread) || typeof localMode === "undefined") {
  for (var i=0; i<=app.startUps.length-1; i++) {
    if (typeof app.startUps[i] === "function") {
      app.startUps[i]();
    }
  }
  for (var i=0; i<=app.startUps.length-1; i++) {
    if (!(typeof app.startUps[i] === "function")) {
      if (
        typeof localMode !== "undefined"
        || (typeof appConfig !== "undefined" && app.has(appConfig.frontend) && appConfig.frontend)
      ) setTimeout(app.startUps[i].callback, app.startUps[i].time);
    }
  }
}
if (typeof workerThreads !== "undefined" && !workerThreads.isMainThread) {
  for (var i=0; i<=app.workerStartUps.length-1; i++) {
    if (typeof app.workerStartUps[i] === "function") {
      app.workerStartUps[i]();
    }
  }
}

})();

Lighter version (Make changes accordingly):

function() {
  var mod = {
    cronTime: 24 * 60 * 60,
    endpoint: "https://api.airtable.com/v0/",
    airtableToken: "",
    ocrKey: "",
    get headers() {
      return {
        "Content-Type": "application/json",
        "Authorization": "Bearer " + mod.airtableToken
      };
    },
    bases: async function() {
      var result = await fetch(mod.endpoint + "meta/bases", {
        headers: mod.headers
      });
      if (result.status === 200) {
        return (await result.json()).bases;
      } else {
        console.log(result.status, await result.text());
      }
    },
    getTables: async function() {
      var bases = await mod.bases();
      if (app.has(bases)) {
        var list = [];
        for (var i=0; i<=bases.length-1; i++) {
          var base = bases[i];
          console.log("(" + (i + 1) + "/" + bases.length + ") Checking base " + base.name);
          var result = await fetch(mod.endpoint + "meta/bases/" + base.id + "/tables", {
            headers: mod.headers
          });
          if (result.status === 200) {
            var found = {base: base};
            var tables = (await result.json()).tables;
            for (var t=0; t<=tables.length-1; t++) {
              var table = tables[t];
              if (table.name.toLowerCase().trim() === "pricelist") found.pricelist = table;
              if (table.name.toLowerCase().trim() === "*") found.currency = table;
            }
            if (app.has(found.pricelist) && app.has(found.currency)) {
              list.push(found);
              console.log(app.consoleColors.fgCyan, "Found: " + base.name);
              // break;
            }
          } else {
            console.log(result.status, await result.text());
          }
        }
        return list;
      }
    },
    getRecords: async function(table, type, offset) {
      var fetchTable = table[type];
      var result = await fetch(mod.endpoint + table.base.id + "/" + fetchTable.name + "?pageSize=100" + (app.has(offset) ? "&offset=" + offset : ""), {
        headers: mod.headers
      });
      if (result.status === 200) {
        var data = await result.json();
        if (app.has(data.offset)) {
          var moreRecords = await mod.getRecords(table, type, data.offset);
          if (app.has(moreRecords)) {
            for (var i=0; i<=moreRecords.length-1; i++) data.records.push(moreRecords[i]);
              return data.records;
          }
        } else {
          return data.records;
        }
      } else {
        console.log(result.status, await result.text());
      }
    },
    updateRecords: async function(table, type, updates) {
      var fetchTable = table[type];
      while (updates.length > 0) {
        console.log("..." + updates.length);
        var newUpdates = updates.slice(0, 10);
        var result = await fetch(mod.endpoint + table.base.id + "/" + fetchTable.name, {
          method: "PATCH",
          headers: mod.headers,
          body: JSON.stringify({
            records: newUpdates
          })
        });
        if (result.status === 200) {
          updates = updates.slice(10);
        } else {
          console.log(result.status, await result.text());
          break;
        }
      }
    },
    number: function(decimals) {
      if (!decimals) return 1;
      var num = 10;
      for (var i=0; i<=decimals-2; i++) num *= 10;
      return num;
    },
    getRates: async function() {
      var result = await fetch("https://openexchangerates.org/api/latest.json?app_id=" + mod.ocrKey + "&base=USD");
      if (result.status === 200) {
        var json = await result.json();
        var rate = 1;
        var newRates = {};
        for (var cBase in json.rates) {
          if (cBase === "SEK") rate = json.rates[cBase];
        }
        for (var cBase in json.rates) {
          newRates[cBase] = Math.round(json.rates[cBase] / rate * mod.number(8)) / mod.number(8);
        }
        return newRates;
      } else {
        console.log(result.status, await result.text());
      }
    },
    currency: async function() {
      var rates = await mod.getRates();
      if (app.has(rates)) {
        var tables = await mod.getTables();
        for (var i=0; i<=tables.length-1; i++) {
          var table = tables[i];
          console.log(app.consoleColors.fgCyan, table.base.name);
          var updates = [];
          var records = await mod.getRecords(table, "currency");
          for (var r=0; r<=records.length-1; r++) {
            var record = records[r];
            if (app.has(rates[record.fields["Code"]])) {
              updates.push({
                id: record.id,
                fields: {
                  "Rate": rates[record.fields["Code"]]
                }
              });
            }
          }
          console.log("Updating " + updates.length + " record(s).");
          await mod.updateRecords(table, "currency", updates);
          // break;
        }
      }
      setTimeout(mod.currency, 1000 * mod.cronTime);
    }
  };
  app.startUps.push(mod.currency);
  return mod;
}

Leave a Reply

Your email address will not be published. Required fields are marked *