aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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;