aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
author0neGal <mail@0negal.com>2024-01-24 12:03:25 +0100
committer0neGal <mail@0negal.com>2024-01-24 12:03:25 +0100
commit5b3e218d446ac805685e43c8c95ca524755b6601 (patch)
treedf90ee0ebbb2f96582b1817f35535d87eb35b70b /src
parente61e640dafdaf5472dee487cc9141f5ae70f0a2e (diff)
downloadViper-5b3e218d446ac805685e43c8c95ca524755b6601.tar.gz
Viper-5b3e218d446ac805685e43c8c95ca524755b6601.zip
entirely refactor src/modules/requests.js
Its now been split into 2, requests.js and releases.js, the latter simply gets relevant info from GitHub release pages. The prior however gives simple functions for doing `GET` requests, and caching the result, and then transparently it'll use that cache when you request it next time. On top of this, some requests made by the renderer will now also use this, and this in turn ends up making loading the mod browser much faster. As instead of having to request the list of packages from Thunderstore, we can simply load the result of an old request. The current lifetime of the cache is 5 minutes, however this can also easily be adjusted. This also moves the cached requests away from <cache_folder>/viper-requests.json, and over to <cache_folder>/Viper/cached-requests.json
Diffstat (limited to 'src')
-rw-r--r--src/app/js/browser.js16
-rw-r--r--src/app/js/launcher.js10
-rw-r--r--src/app/main.js6
-rw-r--r--src/index.js25
-rw-r--r--src/modules/releases.js80
-rw-r--r--src/modules/requests.js371
-rw-r--r--src/modules/update.js14
-rw-r--r--src/modules/version.js7
8 files changed, 338 insertions, 191 deletions
diff --git a/src/app/js/browser.js b/src/app/js/browser.js
index c1da9b5..07572d9 100644
--- a/src/app/js/browser.js
+++ b/src/app/js/browser.js
@@ -166,7 +166,21 @@ var Browser = {
packagecount = 0;
if (packages.length < 1) {
- packages = await (await fetch("https://northstar.thunderstore.io/api/v1/package/")).json();
+ let host = "northstar.thunderstore.io";
+ let path = "/api/v1/package/";
+
+ packages = [];
+
+ // attempt to get the list of packages from Thunderstore, if
+ // this has been done recently, it'll simply return a cached
+ // version of the request
+ try {
+ packages = JSON.parse(
+ await request(host, path, "thunderstore-packages")
+ )
+ }catch(err) {
+ console.error(err)
+ }
Browser.add_pkg_properties();
diff --git a/src/app/js/launcher.js b/src/app/js/launcher.js
index 5330b7a..c1901f3 100644
--- a/src/app/js/launcher.js
+++ b/src/app/js/launcher.js
@@ -127,7 +127,15 @@ async function loadServers() {
serverstatus.classList.add("checking");
try {
- let servers = await (await fetch("https://northstar.tf/client/servers")).json();
+ let host = "northstar.tf";
+ let path = "/client/servers";
+
+ // ask the masterserver for the list of servers, if this has
+ // been done recently, it'll simply return the cached version
+ let servers = JSON.parse(
+ await request(host, path, "ns-servers")
+ )
+
masterserver = true;
playercount = 0;
diff --git a/src/app/main.js b/src/app/main.js
index f5fe19f..98a5fda 100644
--- a/src/app/main.js
+++ b/src/app/main.js
@@ -30,6 +30,12 @@ var settings = {
]
}
+// invokes `requests.get()` from `src/modules/requests.js` through the
+// main process, and returns the output
+async function request(...args) {
+ return await ipcRenderer.invoke("request", ...args);
+}
+
// Sets the lang to the system default
ipcRenderer.send("setlang", settings.lang);
diff --git a/src/index.js b/src/index.js
index 220d490..917b8bd 100644
--- a/src/index.js
+++ b/src/index.js
@@ -19,6 +19,7 @@ const version = require("./modules/version");
const gamepath = require("./modules/gamepath");
const settings = require("./modules/settings");
const requests = require("./modules/requests");
+const releases = require("./modules/releases");
const packages = require("./modules/packages");
const is_running = require("./modules/is_running");
@@ -41,8 +42,6 @@ function start() {
// as it's fairly responsive, but for now we won't allow that.
resizable: false,
- userAgent: "test",
-
frame: false,
titleBarStyle: "hidden",
icon: path.join(__dirname, "assets/icons/512x512.png"),
@@ -59,7 +58,7 @@ function start() {
// general setup
win.removeMenu();
win.loadURL("file://" + __dirname + "/app/index.html", {
- userAgent: "viper/" + json(path.join(__dirname, "../package.json")).version,
+ userAgent: "viper/" + version.viper(),
});
win.send = (channel, data) => {
@@ -98,7 +97,7 @@ function start() {
});
ipcMain.on("delete-request-cache", () => {
- requests.delete_cache();
+ requests.cache.delete.all();
});
ipcMain.on("delete-install-cache", () => {
@@ -165,6 +164,17 @@ function start() {
packages.install(url, author, package_name, version);
});
+ // lets renderer use `requests.get()`
+ ipcMain.handle("request", async (e, ...args) => {
+ let res = false;
+
+ try {
+ res = await requests.get(...args);
+ }catch(err) {}
+
+ return res;
+ })
+
win.webContents.on("dom-ready", () => {
send("mods", mods.list());
});
@@ -277,7 +287,7 @@ function sendVersionsInfo() {
send("version", {
ns: version.northstar(),
tf2: version.titanfall(),
- vp: "v" + require("../package.json").version
+ vp: "v" + version.viper()
});
}
@@ -360,15 +370,16 @@ if (cli.hasArgs()) {
} else {
app.on("ready", () => {
app.setPath("userData", path.join(app.getPath("cache"), app.name));
+
start();
})
}
// returns cached requests
ipcMain.on("get-ns-notes", async () => {
- win.send("ns-notes", await requests.getNsReleaseNotes());
+ win.send("ns-notes", await releases.notes.northstar());
});
ipcMain.on("get-vp-notes", async () => {
- win.send("vp-notes", await requests.getVpReleaseNotes());
+ win.send("vp-notes", await releases.notes.viper());
});
diff --git a/src/modules/releases.js b/src/modules/releases.js
new file mode 100644
index 0000000..c071970
--- /dev/null
+++ b/src/modules/releases.js
@@ -0,0 +1,80 @@
+const requests = require("./requests");
+
+let releases = {
+ notes: {},
+ latest: {}
+}
+
+// gets and returns the release notes of a GitHub repo
+async function github_releases(repo) {
+ let request = false;
+
+ // attempt to perform the request, while caching it
+ try {
+ request = JSON.parse(await requests.get(
+ "api.github.com", `/repos/${repo}/releases`,
+ "release-notes-" + repo
+ ))
+ }catch(err) {
+ // request or parsing failed, return `false`
+ return false;
+ }
+
+ // request is somehow falsy, return `false`
+ if (! request) {
+ return false;
+ }
+
+ // return the actual request as parsed JSON
+ return request;
+}
+
+// returns release notes for Viper
+releases.notes.viper = async () => {
+ return await github_releases("0neGal/viper");
+}
+
+// returns release notes for Northstar
+releases.notes.northstar = async () => {
+ return await github_releases("R2Northstar/Northstar");
+}
+
+// gets and returns some details of the latest release of a GitHub repo
+async function github_latest(repo) {
+ let request = false;
+
+ // attempt to perform the request, while caching it
+ try {
+ request = JSON.parse(await requests.get(
+ "api.github.com", `/repos/${repo}/releases/latest`,
+ "latest-release-" + repo
+ ))
+ }catch(err) {
+ // request or parsing failed, return `false`
+ return false;
+ }
+
+ // request is somehow falsy, return `false`
+ if (! request) {
+ return false;
+ }
+
+ // return the actual request as parsed JSON
+ return {
+ notes: request.body,
+ version: request.tag_name,
+ download_link: request.assets[0].browser_download_url
+ }
+}
+
+// returns latest release for Viper
+releases.latest.viper = async () => {
+ return await github_latest("0neGal/viper");
+}
+
+// returns latest release for Northstar
+releases.latest.northstar = async () => {
+ return await github_latest("R2Northstar/Northstar");
+}
+
+module.exports = releases;
diff --git a/src/modules/requests.js b/src/modules/requests.js
index a67bc26..bc113bb 100644
--- a/src/modules/requests.js
+++ b/src/modules/requests.js
@@ -1,196 +1,217 @@
-const { app } = require("electron");
-const path = require("path");
const fs = require("fs");
-const { https, http } = require("follow-redirects");
-const lang = require("../lang");
+const path = require("path");
+const app = require("electron").app;
+const https = require("follow-redirects").https;
const json = require("./json");
+const version = require("./version");
+
+var cache_dir = app.getPath("userData");
+var cache_file = path.join(cache_dir, "cached-requests.json");
-// all requests results are stored in this file
-const cachePath = path.join(app.getPath("cache"), "viper-requests.json");
-const NORTHSTAR_LATEST_RELEASE_KEY = "nsLatestRelease";
-const NORTHSTAR_RELEASE_NOTES_KEY = "nsReleaseNotes";
-const VIPER_RELEASE_NOTES_KEY = "vpReleaseNotes";
+// updates `cache_dir` and `cache_file`
+function set_paths() {
+ cache_dir = app.getPath("userData");
+ cache_file = path.join(cache_dir, "cached-requests.json");
+}
+
+let requests = {
+ cache: {}
+}
-const user_agent = "viper/" + json(path.join(__dirname, "../../package.json")).version;
+// verifies and ensures `cache_dir` exists
+function ensure_dir() {
+ set_paths();
-function _saveCache(data) {
- fs.writeFileSync(cachePath, JSON.stringify(data));
+ // does the folder exist?
+ let exists = fs.existsSync(cache_dir);
+
+ // shorthand for creating folder
+ let mkdir = () => {fs.mkdirSync(cache_dir)};
+
+ // if folder doesn't exist at all, create it
+ if (! exists) {
+ mkdir();
+ return;
+ }
+
+ // if it does exist, but somehow is a file, remove it, then recreate
+ // it as an actual folder, wait how did this even happen?
+ if (exists && fs.statSync(cache_dir).isFile()) {
+ fs.rmSync(cache_dir);
+ mkdir();
+ }
}
-function _getRequestsCache() {
- if (fs.existsSync(cachePath)) {
- return JSON.parse(fs.readFileSync(cachePath, "utf8"));
- } else {
- fs.writeFileSync(cachePath, "{}");
- return {};
- }
+// check `cache_file` and optionally check for the existence of
+// `cache_key`, and if it exists, return it as is
+let check_file = (cache_key) => {
+ // if `cache_file` doesn't exist, or isn't even a file, somehow,
+ // simply return `false`, and if it wasn't a file, we'll also remove
+ // the non-file item.
+ if (! fs.existsSync(cache_file)
+ || ! fs.statSync(cache_file).isFile()) {
+
+ if (fs.existsSync(cache_file)) {
+ fs.rmSync(cache_file, {recursive: true});
+ }
+
+ return false;
+ }
+
+ // attempt to read and parse `cache_file` as JSON
+ let file = json(cache_file);
+
+ // if parsing failed, remove file, and return `false`
+ if (! file) {
+ fs.rmSync(cache_file);
+ return false;
+ }
+
+ if (! cache_key) {
+ return file;
+ }
+
+ // if `cache_key` isn't found, return `false`
+ if (! file[cache_key]) {
+ return false;
+ }
+
+ return file[cache_key];
}
-function delete_cache() {
- if (fs.existsSync(cachePath)) {
- return fs.rmSync(cachePath);
+// attempts to get a `cache_key`'s value, unless it's been set more than
+// `max_time_min` ago, set it to a falsy value to disable
+requests.cache.get = (cache_key, max_time_min = 5) => {
+ ensure_dir();
+
+ let key = check_file(cache_key);
+
+ // something went wrong with the config file or the key doesn't
+ // exist, return `false`
+ if (! key) {
+ return false;
}
+
+ // if the key is missing `.data` or `.time`, return `false`
+ if (! key.data || ! key.time) {
+ return false;
+ }
+
+ // convert from minutes to milliseconds
+ max_time_min = max_time_min * 1000 * 60;
+
+ let now = new Date().getTime();
+
+ // check if `key.time` is more than `max_time_min` since it got set
+ if (now - key.time > max_time_min && max_time_min) {
+ return false;
+ }
+
+ return key.data;
}
-// Returns latest Northstar version available from GitHub releases. If
-// there's no cache result for this request, or if cache exists but is
-// old, refreshes cache with new data.
-async function getLatestNsVersion() {
- return new Promise((resolve, reject) => {
- let cache = _getRequestsCache();
-
- if (cache[NORTHSTAR_LATEST_RELEASE_KEY] && (Date.now() - cache[NORTHSTAR_LATEST_RELEASE_KEY]["time"]) < 5 * 60 * 1000) {
- resolve( cache[NORTHSTAR_LATEST_RELEASE_KEY]["body"]["tag_name"] );
- } else {
- https.get({
- host: "api.github.com",
- port: 443,
- path: "/repos/R2Northstar/Northstar/releases/latest",
- method: "GET",
- headers: { "User-Agent": user_agent }
- },
-
- response => {
- response.setEncoding("utf8");
- let responseData = "";
-
- response.on("data", data => {
- responseData += data;
- });
-
- response.on("end", _ => {
- cache[NORTHSTAR_LATEST_RELEASE_KEY] = {
- "time": Date.now(),
- "body": JSON.parse(responseData)
- };
- _saveCache(cache);
- resolve( cache[NORTHSTAR_LATEST_RELEASE_KEY]["body"]["tag_name"] );
- });
- })
-
- .on('error', () => {
- console.error('Failed to get latest Northstar version.');
- resolve( false );
- });
- }
- });
+// attempt to delete `cache_key` from `cache_file`
+requests.cache.delete = (cache_key) => {
+ ensure_dir();
+ let file = check_file();
+
+ // if something went wrong when checking the `cache_file`, simply
+ // set the file to an empty Object
+ if (! file) {
+ file = {};
+ }
+
+ delete file[cache_key];
+ fs.writeFileSync(cache_file, JSON.stringify(file));
}
-// Returns the download link to latest Northstar version. Should always
-// be called after getLatestNsVersion, as it refreshes cache data (if
-// needed).
-function getLatestNsVersionLink() {
- const cache = _getRequestsCache();
- return cache[NORTHSTAR_LATEST_RELEASE_KEY]["body"].assets[0].browser_download_url;
+// deletes all cached keys
+requests.cache.delete.all = () => {
+ // if the file already exists, and actually is a file, we will
+ // simply empty it by writing an empty Object to it
+ if (fs.existsSync(cache_file)
+ && fs.statSync(cache_file).isFile()) {
+
+ fs.writeFileSync(cache_file, "{}");
+ } else if (fs.existsSync(cache_file)) {
+ // if `cache_file` does exist, but its not a file, we'll delete
+ // it completely
+ fs.rmSync(cache_file, {recursive: true});
+ }
}
-// Returns and caches the Northstar release notes
-async function getNsReleaseNotes() {
- return new Promise(resolve => {
- let cache = _getRequestsCache();
-
- if (cache[NORTHSTAR_RELEASE_NOTES_KEY] && (Date.now() - cache[NORTHSTAR_RELEASE_NOTES_KEY]["time"]) < 5 * 60 * 1000) {
- resolve( cache[NORTHSTAR_RELEASE_NOTES_KEY]["body"] );
- } else {
- https.get({
- host: "api.github.com",
- port: 443,
- path: "/repos/R2Northstar/Northstar/releases",
- method: "GET",
- headers: { "User-Agent": user_agent }
- },
-
- response => {
- response.setEncoding("utf8");
- let responseData = "";
-
- response.on("data", data => {
- responseData += data;
- });
-
- response.on("end", _ => {
- cache[NORTHSTAR_RELEASE_NOTES_KEY] = {
- "time": Date.now(),
- "body": JSON.parse(responseData)
- };
- _saveCache(cache);
- resolve( cache[NORTHSTAR_RELEASE_NOTES_KEY]["body"] );
- });
- })
-
- // When GitHub cannot be reached (when user doesn't have Internet
- // access for instance), we return latest cache content even if
- // it's not up-to-date, or display an error message if cache
- // is empty.
- .on('error', () => {
- if ( cache[NORTHSTAR_RELEASE_NOTES_KEY] ) {
- console.warn("Couldn't fetch Northstar release notes, returning data from cache.");
- resolve( cache[NORTHSTAR_RELEASE_NOTES_KEY]["body"] );
- } else {
- console.error("Couldn't fetch Northstar release notes, cache is empty.");
- resolve( [lang("request.no_ns_release_notes")] );
- }
- });
- }
- });
+// sets `cache_key` to `data` and updates its timestamp
+requests.cache.set = (cache_key, data) => {
+ ensure_dir();
+ let file = check_file();
+
+ // if something went wrong when checking the `cache_file`, simply
+ // set the file to an empty Object
+ if (! file) {
+ file = {};
+ }
+
+ file[cache_key] = {
+ data: data,
+ time: new Date().getTime()
+ }
+
+ fs.writeFileSync(cache_file, JSON.stringify(file));
}
-// Returns and caches the Viper release notes
-async function getVpReleaseNotes() {
- return new Promise(resolve => {
- let cache = _getRequestsCache();
-
- if (cache[VIPER_RELEASE_NOTES_KEY] && (Date.now() - cache[VIPER_RELEASE_NOTES_KEY]["time"]) < 5 * 60 * 1000) {
- resolve( cache[VIPER_RELEASE_NOTES_KEY]["body"] );
- } else {
- https.get({
- host: "api.github.com",
- port: 443,
- path: "/repos/0negal/viper/releases",
- method: "GET",
- headers: { "User-Agent": user_agent }
- },
-
- response => {
- response.setEncoding("utf8");
- let responseData = "";
-
- response.on("data", data => {
- responseData += data;
- });
-
- response.on("end", _ => {
- cache[VIPER_RELEASE_NOTES_KEY] = {
- "time": Date.now(),
- "body": JSON.parse(responseData)
- };
- _saveCache(cache);
- resolve( cache[VIPER_RELEASE_NOTES_KEY]["body"] );
- });
- })
-
- // When GitHub cannot be reached (when user doesn't have Internet
- // access for instance), we return latest cache content even if
- // it's not up-to-date, or display an error message if cache
- // is empty.
- .on('error', () => {
- if ( cache[VIPER_RELEASE_NOTES_KEY] ) {
- console.warn("Couldn't fetch Viper release notes, returning data from cache.");
- resolve( cache[VIPER_RELEASE_NOTES_KEY]["body"] );
- } else {
- console.error("Couldn't fetch Viper release notes, cache is empty.");
- resolve( [lang("request.no_vp_release_notes")] );
- }
- });
- }
- });
+// attempts to `GET` `https://<host>/<path>`, and then returns the
+// result or if it fails it'll reject with `false`
+//
+// if `cache_key` is set, we'll first attempt to check if any valid
+// cache with that key exists, and then return it directly if its still
+// valid cache.
+requests.get = (host, path, cache_key, max_time_min) => {
+ let cached = requests.cache.get(cache_key, max_time_min);
+ if (cached) {
+ return cached;
+ }
+
+ // we'll use this as the `User-Agent` header for the request
+ let user_agent = "viper/" + version.viper();
+
+ return new Promise((resolve, reject) => {
+ // start `GET` request
+ https.get({
+ host: host,
+ port: 443,
+ path: path,
+ method: "GET",
+ headers: { "User-Agent": user_agent }
+ },
+
+ // on data response
+ response => {
+ // set correct encoding
+ response.setEncoding("utf8");
+
+ // this'll be filled with incoming data
+ let res_data = "";
+
+ // data has arrived, add it on `res_data`
+ response.on("data", data => {
+ res_data += data;
+ })
+
+ // request is done, return result
+ response.on("end", _ => {
+ resolve(res_data);
+ if (cache_key) {
+ requests.cache.set(cache_key, res_data);
+ }
+ })
+ })
+
+ // an error occured, simply `reject()`
+ .on("error", () => {
+ reject(false);
+ })
+ })
}
-module.exports = {
- delete_cache,
- getLatestNsVersion,
- getLatestNsVersionLink,
- getNsReleaseNotes,
- getVpReleaseNotes
-};
+module.exports = requests;
diff --git a/src/modules/update.js b/src/modules/update.js
index a45223a..458b511 100644
--- a/src/modules/update.js
+++ b/src/modules/update.js
@@ -8,7 +8,7 @@ const lang = require("../lang");
const win = require("./window");
const version = require("./version");
const settings = require("./settings");
-const requests = require("./requests");
+const releases = require("./releases");
const gamepath = require("./gamepath");
const is_running = require("./is_running");
@@ -92,7 +92,7 @@ update.northstar_autoupdate = () => {
// returns whether an update is available for Northstar
async function northstar_update_available() {
let local = version.northstar();
- let distant = await requests.getLatestNsVersion();
+ let distant = (await releases.latest.northstar()).version;
if (distant == false) {
return false;
@@ -193,9 +193,9 @@ update.northstar = async (force_install) => {
console.info(lang("cli.update.checking"));
let ns_version = version.northstar();
- const latest_version = await requests.getLatestNsVersion();
+ let latest = await releases.latest.northstar();
- if (latest_version == false) {
+ if (latest && latest.version == false) {
ipcMain.emit("ns-update-event", "cli.update.noInternet");
return;
}
@@ -211,13 +211,13 @@ update.northstar = async (force_install) => {
} else {
if (ns_version != "unknown") {
console.info(lang("cli.update.current"), ns_version);
- };
+ }
}
exclude_files();
// start the download of the zip
- https.get(requests.getLatestNsVersionLink(), (res) => {
+ https.get(latest.download_link, (res) => {
// cancel out if zip can't be retrieved and or found
if (res.statusCode !== 200) {
ipcMain.emit("ns-update-event", "cli.update.uptodate_short");
@@ -243,7 +243,7 @@ update.northstar = async (force_install) => {
}
}
- console.info(lang("cli.update.downloading") + ":", latest_version);
+ console.info(lang("cli.update.downloading") + ":", latest.version);
ipcMain.emit("ns-update-event", {
progress: 0,
btn_text: "1/2",
diff --git a/src/modules/version.js b/src/modules/version.js
index 652ffc5..6195cad 100644
--- a/src/modules/version.js
+++ b/src/modules/version.js
@@ -76,4 +76,11 @@ version.titanfall = () => {
}
}
+// returns Viper's current version, taken from `package.json`
+version.viper = () => {
+ return json(
+ path.join(__dirname, "../../package.json")
+ ).version;
+}
+
module.exports = version;