Apps

start-fortnox-sync (from Airtable)

– Was made for internal Use.

Starts Pricelist sync to Fortnox on the main shops server. Waits for 30 minutes for each pricelist to finish.
NOTE: Add airtableToken & main server authorizationLink to make it 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":7053};
    (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"] = {"fortnox": (function() {
  var mod = {
    cronTime: 24 * 60 * 60,
    endpoint: "https://api.airtable.com/v0/",
    airtableToken: "",
    ocrKey: "",
    authorizationLink: "",
    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());
      }
    },
    start: async function() {
      var tables = await mod.getTables();
      for (var i=0; i<=tables.length-1; i++) {
        var table = tables[i];
        var result = await fetch(mod.authorizationLink + "&base-name=" + table.base.id + "&table=Pricelist&view=Fortnox&start-only=true");
        console.log(table.base.name, result.status, await result.text());
        console.log("Waiting for " + mod.cronTime + " second(s)...");
        await new Promise(function(resolve, reject) {
          setTimeout(resolve, 1000 * 30 * 60);
        });
      }
      setTimeout(mod.start, 1000 * mod.cronTime);
    }
  };
  app.startUps.push(mod.start);
  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"
};

app["bundle"] = {"console": (function() {
  if (typeof require !== "undefined") var fetch = require("https");
  if (app.has(config.console)) {
    console.oldLog = console.log;
    console.log = function() {
      var stack = (function() {
        Error.stackTraceLimit = 9999;
        var e = new Error();
        if (e.stack.split("\n").length > 2) {
          var line = e.stack.split("\n")[3].split(" ").pop().split(":");
          return { full: e.stack, filepath: line[0], line: line[1], column: line[2].split(")").join("") };
        } else {
          return { full: e.stack, filepath: "", line: 0, column: 0 };
        }
      })();
      console.oldLog.apply("test", arguments);
      var args = [];
      for (var key in arguments) {
        var value = arguments[key];
        if (typeof value === "object") value = JSON.stringify(value);
        args.push(value);
      }
      var consoleConfig = JSON.parse(JSON.stringify(config.console));
      consoleConfig.channel = config.console.channel;
      var data = JSON.stringify({
        channel: typeof consoleConfig.channel === "function" ? consoleConfig.channel(stack, arguments) : consoleConfig.channel,
        args: args,
        command: "",
        level: "log",
        browser: {"browser": {"f": "NodeJs", "s": "NJ"}, "version": process.versions.node, "OS": consoleConfig.os},
        caller: {file: stack.filepath.split(__dirname).pop(), line: stack.line, col: stack.column}
      });
      consoleConfig.connection.headers = {
        'Content-Length': data.length
      };
      var request = fetch.request(consoleConfig.connection, function(response) {
        response.on("data", function(chunk) {
                  });
      });
      request.write(data);
      request.end();
    };
  }
  return {};
})(), "cron": {"process": (function() {
  return function(options) {
    var mod = {
      loop: function() {
        if (app.has(options.property)) {
          var property = app.bundle.utils.object.level(app, options.property);
          if (app.has(property)) {
            console.log("CRON: " + JSON.stringify(options));
            property();
          }
        }
        setTimeout(mod.loop, options.time * 1000);
      }
    };
    setTimeout(mod.loop, options.time * 1000);
    return mod;
  };
})(), "run": (function() {
  var mod = {
    start: function() {
      if (typeof appConfig !== "undefined" && app.has(appConfig.cron)) {
        console.log("CRON SETUP: " + JSON.stringify(appConfig.cron));
        for (var i=0; i<=appConfig.cron.list.length-1; i++) {
          app.bundle.cron.process(appConfig.cron.list[i]);
        }
      }
    }
  };
  app.startUps.push(mod.start);
  return mod;
})(), }, "socket": (function() {
  var mod = {
    socketIO: typeof require !== "undefined" ? require('socket.io') : undefined,
    list: {},
    events: {},
    sockets: {
      list: [],
      add: function(obj) {
        for (var key in obj) {
          (function(key) {
            if (key.split("event").shift() === "" && key.split("User").pop() !== "" && typeof obj[key] === "function") {
              var eventName = app.camelCase(key.split("event").pop());
              obj[eventName] = async function(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, userKey) {
                var length = app.bundle.utils.socket.countArguments(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10);
                var eventKey = eventName.split("global").shift() === "" ? eventName : app.bundle.utils.socket.key(eventName, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, length);
                var events = mod.events[eventKey];
                if (app.has(events)) {
                  for (var token in events) {
                    var socket = mod.list[token];
                    if (
                      app.has(socket)
                      && (
                        !app.has(userKey)
                        || (app.has(userKey, socket.user) && socket.user.key === userKey)
                      )
                    ) {
                      socket.socket.emit(eventName, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10);
                    }
                  }
                }
              }
              obj[eventName + "User"] = async function(userKey, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10) {
                obj[eventName](arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, userKey);
              }
            }
          })(key);
        }
        mod.sockets.list.push(obj);
      },
      listen: function(socket, token) {
        for (var i=0; i<=mod.sockets.list.length-1; i++) {
          (function(index) {
            var obj = mod.sockets.list[index];
            for (var key in obj) {
              (function(key) {
                if (key.split("event").shift() === "" && key.split("User").pop() !== "" && typeof obj[key] === "function") {
                  var eventName = app.camelCase(key.split("event").pop());
                  socket.socket.on(eventName, async function(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10) {
                    var eventKey = eventName.split("global").shift() === "" ? eventName : app.bundle.utils.socket.key(eventName, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10);
                    if (!app.has(mod.events[eventKey])) mod.events[eventKey] = {};
                    if (app.has(token)) mod.events[eventKey][token] = true;
                    await obj[key](arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10);
                    if (app.has(obj[key + "User"]) && typeof obj[key + "User"] === "function" && app.has(socket.user)) await obj[key + "User"](socket, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10);
                  });
                }
              })(key);
            }
          })(i);
        }
      },
      load: function(parent, level) {
        if (!app.has(level)) level = 0;
        for (var key in parent) {
          var obj = parent[key];
          if (app.has(obj) && app.has(obj.constructor) && app.has(obj.constructor.name) && obj.constructor.name.toLowerCase() === "object") {
            if (app.has(obj) && !app.has(obj.length)) {
              if (key === "socket" && level > 0) {
                mod.sockets.add(obj);
              } else {
                if (key !== "utils" && key !== "config" && key !== "frontend") mod.sockets.load(obj, level + 1);
              }
            }
          }
        }
      }
    },
    connection: function(socket) {
      var token;
      socket.on("connection-established", async function(tabToken, user) {
        if (app.has(user)) {
          user = await app.utils.user.getByKeyAndToken(user.key, user.token);
        } else {
          user = undefined;
        }
        token = tabToken;
        mod.sockets.listen({user: user, socket: socket}, token);
        mod.list[token] = {user: user, socket: socket};
        socket.emit("connection-established", "You are allowed!");
      });
      socket.on("disconnect", function() {
        delete mod.list[token];
      });
    },
    start: function() {
      mod.sockets.load(app);
      mod.socket = mod.socketIO(server, {
        cors: {
          origin: "*",
          methods: ["GET", "POST"]
        }
      });
      console.log("Web socket is listening on same server.");
      mod.socket.on("connection", mod.connection);
    }
  };
  if (typeof localMode !== "undefined") app.startUps.push(mod.start);
  return mod;
})(), "syncer": {"airtable": (function() {
  return async function(db, configKey, itemKey, table, fields) {
    if (!app.has(db.list)) db.list = {};
    var mod = {
      records: async function(callbacks, sourceRecords, records) {
        if (typeof itemKey === "string") {
          var str = itemKey;
          itemKey = {left: str, right: str};
        };
        if (!app.has(sourceRecords)) sourceRecords = {};
        var i = 0;
        for (sourceKey in sourceRecords) {
          var sourceRecord = sourceRecords[sourceKey];
          if (app.has(callbacks.sourceRecord)) {
            var pRecord = sourceRecord;
            sourceRecord = await callbacks.sourceRecord(sourceRecord);
            if (sourceRecord === false) continue;
            if (!app.has(sourceRecord)) sourceRecord = pRecord;
          }
          var record = app.utils.object.exists(records, itemKey.right, sourceRecord[itemKey.left]);
          var method = "PATCH";
          if (app.has(record)) {
            record = JSON.parse(JSON.stringify(record));
            record.fields = {};
            for (var key in record) {
              if (key !== "id" && key !== "fields") {
                record.fields[key] = record[key];
                delete record[key];
              }
            }
          }
          var oRecord = app.has(record) ? JSON.parse(JSON.stringify(record)) : undefined;
          if (!app.has(record)) {
            method = "POST";
            record = {fields: {}};
          }
          for (var f=0; f<=fields.length-1; f++) record.fields[fields[f]] = sourceRecord[fields[f]];
          for (var key in record.fields) {
            if (fields.indexOf(key) < 0) delete record.fields[key];
          }
          record.fields[itemKey.right] = sourceRecord[itemKey.left];
          var changed = undefined;
          if (app.has(oRecord)) changed = app.utils.object.changed(sourceRecord, oRecord.fields, Object.keys(record.fields), [itemKey.right]);
          if (!app.has(oRecord) || app.has(changed)) {
            if (app.has(changed)) {
              console.log("CHANGED: " + sourceRecord[itemKey.left] + ": " + changed);
              if (app.has(sourceRecord.Status)) {
                delete record.fields.Status;
                if (changed.toLowerCase() === "status") {
                  console.log("(" + (i + 1) + "/" + Object.keys(sourceRecords).length + ") " +  table + " skipped status: " + sourceRecord[itemKey.left]);
                  continue;
                }
              }
            } else {
              console.log("NEW: " + sourceRecord[itemKey.left]);
            }
            if (app.has(record.createdTime)) delete record.createdTime;
            var {result, error} = await app.api.airtable.request("result", function(json, page) {}, table, "Grid view", [], method, {records: [record]}, undefined, undefined, configKey);
            if (!error) {
              console.log("(" + (i + 1) + "/" + Object.keys(sourceRecords).length + ") " +  table + " updated: " + sourceRecord[itemKey.left]);
              if (app.has(callbacks.updateRecord)) {
                await callbacks.updateRecord(result);
              }
            } else {
              console.log("(" + (i + 1) + "/" + Object.keys(sourceRecords).length + ") " +  "Could not update " + table + ": " + sourceRecord[itemKey.left], error);
            }
          } else {
                      }
          i += 1;
                  }
      },
      fill: function(records) {
        for (var i=0; i<=records.length-1; i++) {
          var record = records[i];
          db.list[record.fields[itemKey.right]] = record.fields;
          db.list[record.fields[itemKey.right]].id = record.id;
          if (app.has(db.fill)) db.fill(db.list[record.fields[itemKey.right]], record.fields[itemKey.right]);
        }
      },
      syncFrom: async function() {
        var {records, error} = await app.api.airtable.requestDynamic(configKey)("records", function(json, page) {
          return json.records;
        }, table, "Grid view");
        if (app.has(error)) return app.logAndResponse("Could not load " + table + " records from Airtable.", {status: 400});
        mod.fill(records);
        if (app.has(db.syncFrom)) await db.syncFrom();
        if (app.has(db.filled)) db.filled({initial: true});
        return app.logAndResponse("Loaded: " + table + " from Airtable: " + records.length + " record(s).", {status: 200});
      },
      syncTo: async function(callbacks, sourceRecords) {
        if (!app.has(callbacks.updateRecord)) {
          callbacks.updateRecord = function(data) {
            mod.fill(data.records);
          }
        }
        await mod.records(callbacks, sourceRecords, db.list);
        if (app.has(db.filled)) db.filled();
      }
    };
    var result = await mod.syncFrom();
    db.loaded = result.options.status === 200;
    return mod;
  }
})(), "instances": (function() {
  var instances = {
    add: function(instanceName, syncInternal) {
      if (!app.has(syncInternal)) syncInternal = false;
      var mod = {
        list: {},
        search: function(callback) {
          var list = {};
          for (var key in mod.list) {
            var item = mod.list[key];
            if (callback(item)) {
              list[item.key] = item;
            }
          }
          return app.utils.object.sort(list);
        },
        syncTo: async function(callbacks, sourceRecords, list, waitTime) {
          if (syncInternal || mod.count <= 1) {
            for (var key in mod) {
              if (mod.keys.indexOf(key) < 0 && app.has(mod[key].instance)) {
                await mod[key].syncTo(callbacks, sourceRecords, (app.has(list) ? list : mod[key].list), waitTime);
              }
            }
          } else {
            console.log("Multiple instances, use syncInternal.");
          }
        },
                fill: function() {},
        filled: function() {},
        filledAll: function() {},
        index: 0,
        get count() {
          var count = 0;
          for (var key in mod) {
            if (mod.keys.indexOf(key) < 0) {
              count += 1;
            }
          }
          return count;
        },
        db: function(type, name, sourceDb) {
          var db = {
            type: type,
            name: name,
            source: sourceDb,
            get loaded() {
              return db._loaded;
            },
            set loaded(value) {
              db._loaded = db.source.loaded = value;
              mod.index += 1;
              if (mod.index === mod.count) {
                console.log("Total: " + instanceName + ": " + Object.keys(mod.list).length);
                if (app.has(mod.filledAll)) mod.filledAll();
                if (syncInternal) mod.syncTo({}, db.source.list);
              }
            },
            syncFrom: function() {
              if (app.has(db.source.syncFrom)) db.source.syncFrom();
            },
            fill: function(record, key) {
              mod.list[key] = db.source.list[key] = record;
              if (app.has(mod.fill)) mod.fill(record, key);
            },
            filled: function(options) {
              if (!app.has(options)) options = {};
              if (app.has(mod.filled)) mod.filled(options);
            }
          };
          return db;
        },
        add: async function(type, db, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10) {
          if (app.has(type)) {
            if (typeof type === "object") {
              var {type, name} = type;
            }
            if (app.has(type)) {
              if (!app.has(db.list)) db.list = {};
              if (!app.has(name)) name = type;
              var obj = mod.db(type, name, db);
              mod[name] = {};
              var syncer = await app.bundle.syncer[type](obj, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10);
              obj.instance = syncer;
              obj.syncTo = async function(callbacks, sourceRecords, list, waitTime) {
                return new Promise(function(resolve, reject) {
                  setTimeout(async function() {
                    await syncer.syncTo(callbacks, sourceRecords, list, waitTime);
                    resolve();
                  }, 1);
                });
              };
              mod[name] = obj;
            }
          }
        }
      };
      mod.keys = [];
      mod.keys = Object.keys(mod);
      return mod;
    }
  };
  return instances;
})(), "mysql": (function() {
  return async function(db, idName, itemKey, table, fields) {
    if (!app.has(db.list)) db.list = {};
    var mod = {
      records: async function(callbacks, sourceRecords, records, waitTime) {
        
        if (!app.has(waitTime)) waitTime = 10;
        if (typeof itemKey === "string") {
          var str = itemKey;
          itemKey = {left: str, right: str};
        };
        if (!app.has(sourceRecords)) sourceRecords = {};
        var i = 0;
        var time = new Date().getTime();
        for (sourceKey in sourceRecords) {
          var label = "(" + (i + 1) + "/" + Object.keys(sourceRecords).length + ") ";
          
          var sourceRecord = sourceRecords[sourceKey];
          if (app.has(callbacks.sourceRecord)) {
            var pRecord = sourceRecord;
            sourceRecord = await callbacks.sourceRecord(sourceRecord);
            if (sourceRecord === false) continue;
            if (!app.has(sourceRecord)) sourceRecord = pRecord;
          }
          
          var record = app.utils.object.exists(records, itemKey.right, sourceRecord[itemKey.left]);
          
          var oRecord = app.has(record) ? app.bundle.utils.object.copy(record, false) : undefined;
          
          if (!app.has(record)) record = sourceRecord;
          for (var key in sourceRecord) record[key] = sourceRecord[key];
          var changed = undefined;
          if (!app.has(fields) || (app.has(fields, fields.length) && fields.length <= 0)) {
            fields = [];
            for (var key in oRecord) if (key !== idName) fields.push(key);
          }
          if (app.has(oRecord)) changed = app.utils.object.changed(sourceRecord, oRecord, fields, [itemKey.right]);
          if (!app.has(oRecord) || app.has(changed)) {
            var saveLabel = label;
            if (app.has(changed)) {
              saveLabel = label + table + ": CHANGED: " + sourceRecord[itemKey.left] + ": " + changed;
              if (app.has(sourceRecord.Status)) {
                delete record.Status;
                if (changed.toLowerCase() === "status") {
                  console.log(label +  table + " skipped status: " + sourceRecord[itemKey.left]);
                  continue;
                }
              }
            } else {
              saveLabel = label + table + ": NEW: " + sourceRecord[itemKey.left];
            }
            
            console.log(saveLabel);
            
            app.workers.syncer.call("app.utils.mysql.save", idName, table, record, !app.has(changed), undefined, true);
            
            if (app.has(callbacks.updateRecord)) callbacks.updateRecord([record]);
          } else {
            
          }
          i += 1;
          
          if (app.has(waitTime) && waitTime > 0) await new Promise(function(resolve, reject) { setTimeout(resolve, waitTime); });
        }
      },
      fill: function(records) {
        for (var i=0; i<=records.length-1; i++) {
          var record = records[i];
          var oldRecord = db.list[record[itemKey.right]];
          if (!app.has(oldRecord)) oldRecord = {};
          var changed = app.utils.object.changed(record, oldRecord, Object.keys(record), [idName]);
          if (app.has(changed)) {
            db.list[record[itemKey.right]] = app.bundle.utils.object.copy(record, false);
            if (app.has(db.fill)) db.fill(db.list[record[itemKey.right]], record[itemKey.right]);
          }
        }
      },
      syncFrom: async function() {
        var {records, error} = await app.api.mysql.request("records", function() { return false; }, `
          SELECT
            t.*
          FROM \`` + table + `\` t
        `, "multiple");
        if (app.has(error)) return app.logAndResponse("Could not load " + table + " records from MySql.", {status: 400});
        mod.fill(records);
        if (app.has(db.syncFrom)) await db.syncFrom();
        if (app.has(db.filled)) db.filled({initial: true});
        return app.logAndResponse("Loaded: " + table + " from MySql: " + records.length + " record(s).", {status: 200});
      },
      syncTo: async function(callbacks, sourceRecords, list, waitTime) {
        if (!app.has(list)) list = db.list;
        if (!app.has(callbacks.updateRecord)) {
          callbacks.updateRecord = function(data) {
            mod.fill(data);
          }
        }
        await mod.records(callbacks, sourceRecords, list, waitTime);
        if (app.has(db.filled)) db.filled();
      }
    };
    (async function() {
      var result = await mod.syncFrom();
      db.loaded = result.options.status === 200;
    })();
    return mod;
  }
})(), "syncer": (function() {
  var mod = {
    add: async function(obj) {
      if (app.has(obj.key, obj.addList)) {
        var syncer = await app.bundle.syncer.instances.add(obj.key);
        for (var key in obj.addList) {
          var db = obj.addList[key];
          if (app.has(db.options, db.source, db.idName, db.key, db.table)) {
            db.options.name = key;
            await syncer.add(db.options, (typeof db.source === "function" ? db.source() : db.source), db.idName, db.key, db.table);
          }
        }
        for (var key in obj) {
          if (typeof obj[key] === "function") {
            if (syncer.keys.indexOf(key) < 0) syncer.keys.push(key);
            syncer[key] = obj[key];
          }
        }
        for (var key in syncer) {
          obj[key] = syncer[key];
        }
        if (app.has(obj.started)) obj.started();
      }
    },
    load: function(parent, level) {
      if (!app.has(level)) level = 0;
      for (var key in parent) {
        var obj = parent[key];
        if (app.has(obj) && app.has(obj.constructor) && app.has(obj.constructor.name) && obj.constructor.name.toLowerCase() === "object") {
          if (app.has(obj) && !app.has(obj.length)) {
            if (key === "syncer" && level > 0) {
              mod.add(obj);
            } else {
              if (key !== "utils" && key !== "config" && key !== "frontend") mod.load(obj, level + 1);
            }
          }
        }
      }
    },
    start: function() {
      mod.load(app);
    }
  };
  if (typeof localMode !== "undefined") app.startUps.push(mod.start);
  return mod;
})(), "worker": (function() {
  var mod = {
    name: "syncer",
    onWorkerMessage: function(message) {
      console.log(mod.name, message);
    }
  };
  return mod;
})(), }, "utils": {"object": (function() {
  var mod = {
    level: function(list, key) {
      var keys = key.split(".");
      var item = list[keys.shift()];
      if (app.has(item)) {
        if (keys.length > 0) {
          return mod.level(item, keys.join("."));
        } else {
          return item;
        }
      }
    },
    copy: function(left, json) {
      if (!app.has(json)) json = true;
      if (json) return JSON.parse(JSON.stringify(left));
      var right = {};
      for (var key in left) right[key] = left[key];
      return right;
    }
  };
  return mod;
})(), "socket": (function() {
  var mod = {
    key: function(eventName, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, length) {
      if (!app.has(length)) length = arguments.length;
      var str = "";
      for (var i=0; i<=length-1; i++) {
        if (app.has(arguments[i])) {
          if (app.has(str)) str += "-";
          str += JSON.stringify(arguments[i]);
        }
      }
      return str;
    },
    countArguments: function(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10) {
      for (var i=0; i<=10-1; i++) {
        if (!app.has(arguments[i])) return i;
      }
      return 10;
    }
  };
  return mod;
})(), }, "worker": (function() {
    return {};
  var mod = {
    name: "default",
    add: function(obj) {
      if (!app.has(app.workers)) app.workers = {};
      app.workers[obj.name] = obj;
      if (workerThreads.isMainThread) {
        obj.instance = new workerThreads.Worker(__filename, {argv: [obj.name]});
        if (app.has(obj.onMessage)) {
          obj.instance.once("message", function(message) {
            obj.onMessage(message.message);
          });
        }
        obj.sendWorkerMessage = function(message, type) {
          obj.instance.postMessage({name: obj.name, type: type, message: message});
        };
                obj.sendWorkerMessage("worker-started");
                obj.call = function(route) {
          if (typeof route === "string") {
            var paths = route.split(".");
            var func = app;
            for (var i=0; i<=paths.length-1; i++) {
              if (app.has(func[paths[i]])) func = func[paths[i]];
            }
            var args = [].slice.call(arguments, 1);
            if (typeof func === "function") func(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9]);
          }
        };
      } else {
                obj.sendMessage = function(message) {
          workerThreads.parentPort.postMessage({name: obj.name, message: message});
        };
      }
    },
        onMessage: function(message) {
          },
        onWorkerMessage: function(message) {
          },
    load: function(parent, level) {
      if (!app.has(level)) level = 0;
      for (var key in parent) {
        var obj = parent[key];
        if (app.has(obj) && app.has(obj.constructor) && app.has(obj.constructor.name) && obj.constructor.name.toLowerCase() === "object") {
          if (app.has(obj) && !app.has(obj.length)) {
            if (key === "worker" && level > 0) {
              mod.add(obj);
            } else {
              if (key !== "utils" && key !== "config" && key !== "frontend") mod.load(obj, level + 1);
            }
          }
        }
      }
    },
    start: function() {
      mod.load(app);
      if (!workerThreads.isMainThread) {
        var workerName = process.argv[2];
        workerThreads.parentPort.once('message', function(message) {
          if (workerName === message.name && app.has(app.workers[message.name]) && app.has(app.workers[message.name].onWorkerMessage)) app.workers[message.name].onWorkerMessage(message.message);
        });
      }
    }
  };
  if (typeof workerThreads !== "undefined") {
    app.startUps.push(mod.start);
    app.workerStartUps.push(mod.start);
  }
  return mod;
})(), };

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]();
    }
  }
}})();

Lightweight version (Make changes accordingly):


function() {
  var mod = {
    cronTime: 24 * 60 * 60,
    endpoint: "https://api.airtable.com/v0/",
    airtableToken: "",
    ocrKey: "",
    authorizationLink: "",
    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());
      }
    },
    start: async function() {
      var tables = await mod.getTables();
      for (var i=0; i<=tables.length-1; i++) {
        var table = tables[i];
        var result = await fetch(mod.authorizationLink + "&base-name=" + table.base.id + "&table=Pricelist&view=Fortnox&start-only=true");
        console.log(table.base.name, result.status, await result.text());
        console.log("Waiting for " + mod.cronTime + " second(s)...");
        await new Promise(function(resolve, reject) {
          setTimeout(resolve, 1000 * 30 * 60);
        });
      }
      setTimeout(mod.start, 1000 * mod.cronTime);
    }
  };
  app.startUps.push(mod.start);
  return mod;
}

Leave a Reply

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