mirror of
https://github.com/Equicord/Equicord.git
synced 2025-06-07 05:42:55 -04:00
Updater: fix network errors triggering popups (#3436)
This commit is contained in:
parent
9430803f36
commit
47856a26f1
6 changed files with 98 additions and 55 deletions
|
@ -134,7 +134,11 @@ async function init() {
|
|||
|
||||
if (!IS_WEB && !IS_UPDATER_DISABLED) {
|
||||
runUpdateCheck();
|
||||
setInterval(runUpdateCheck, 1000 * 60 * 30); // 30 minutes
|
||||
|
||||
// this tends to get really annoying, so only do this if the user has auto-update without notification enabled
|
||||
if (Settings.autoUpdate && !Settings.autoUpdateNotification) {
|
||||
setInterval(runUpdateCheck, 1000 * 60 * 30); // 30 minutes
|
||||
}
|
||||
}
|
||||
|
||||
if (IS_DEV) {
|
||||
|
|
|
@ -35,7 +35,10 @@ export function serializeErrors(func: (...args: any[]) => any) {
|
|||
ok: false,
|
||||
error: e instanceof Error ? {
|
||||
// prototypes get lost, so turn error into plain object
|
||||
...e
|
||||
...e,
|
||||
message: e.message,
|
||||
name: e.name,
|
||||
stack: e.stack
|
||||
} : e
|
||||
};
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { get } from "@main/utils/simpleGet";
|
||||
import { fetchBuffer, fetchJson } from "@main/utils/http";
|
||||
import { IpcEvents } from "@shared/IpcEvents";
|
||||
import { VENCORD_USER_AGENT } from "@shared/vencordUserAgent";
|
||||
import { ipcMain } from "electron";
|
||||
|
@ -31,8 +31,8 @@ import { serializeErrors, VENCORD_FILES } from "./common";
|
|||
const API_BASE = `https://api.github.com/repos/${gitRemote}`;
|
||||
let PendingUpdates = [] as [string, string][];
|
||||
|
||||
async function githubGet(endpoint: string) {
|
||||
return get(API_BASE + endpoint, {
|
||||
async function githubGet<T = any>(endpoint: string) {
|
||||
return fetchJson<T>(API_BASE + endpoint, {
|
||||
headers: {
|
||||
Accept: "application/vnd.github+json",
|
||||
// "All API requests MUST include a valid User-Agent header.
|
||||
|
@ -46,9 +46,8 @@ async function calculateGitChanges() {
|
|||
const isOutdated = await fetchUpdates();
|
||||
if (!isOutdated) return [];
|
||||
|
||||
const res = await githubGet(`/compare/${gitHash}...HEAD`);
|
||||
const data = await githubGet(`/compare/${gitHash}...HEAD`);
|
||||
|
||||
const data = JSON.parse(res.toString("utf-8"));
|
||||
return data.commits.map((c: any) => ({
|
||||
// github api only sends the long sha
|
||||
hash: c.sha.slice(0, 7),
|
||||
|
@ -58,9 +57,8 @@ async function calculateGitChanges() {
|
|||
}
|
||||
|
||||
async function fetchUpdates() {
|
||||
const release = await githubGet("/releases/latest");
|
||||
const data = await githubGet("/releases/latest");
|
||||
|
||||
const data = JSON.parse(release.toString());
|
||||
const hash = data.name.slice(data.name.lastIndexOf(" ") + 1);
|
||||
if (hash === gitHash)
|
||||
return false;
|
||||
|
@ -70,16 +68,20 @@ async function fetchUpdates() {
|
|||
PendingUpdates.push([name, browser_download_url]);
|
||||
}
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
async function applyUpdates() {
|
||||
await Promise.all(PendingUpdates.map(
|
||||
async ([name, data]) => writeFile(
|
||||
join(__dirname, name),
|
||||
await get(data)
|
||||
)
|
||||
));
|
||||
const fileContents = await Promise.all(PendingUpdates.map(async ([name, url]) => {
|
||||
const contents = await fetchBuffer(url);
|
||||
return [join(__dirname, name), contents] as const;
|
||||
}));
|
||||
|
||||
await Promise.all(fileContents.map(async ([filename, contents]) =>
|
||||
writeFile(filename, contents))
|
||||
);
|
||||
|
||||
PendingUpdates = [];
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ import { join } from "path";
|
|||
|
||||
import { DATA_DIR } from "./constants";
|
||||
import { crxToZip } from "./crxToZip";
|
||||
import { get } from "./simpleGet";
|
||||
import { fetchBuffer } from "./http";
|
||||
|
||||
const extensionCacheDir = join(DATA_DIR, "ExtensionCache");
|
||||
|
||||
|
@ -69,13 +69,14 @@ export async function installExt(id: string) {
|
|||
} catch (err) {
|
||||
const url = `https://clients2.google.com/service/update2/crx?response=redirect&acceptformat=crx2,crx3&x=id%3D${id}%26uc&prodversion=${process.versions.chrome}`;
|
||||
|
||||
const buf = await get(url, {
|
||||
const buf = await fetchBuffer(url, {
|
||||
headers: {
|
||||
"User-Agent": `Electron ${process.versions.electron} ~ Vencord (https://github.com/Vendicated/Vencord)`
|
||||
}
|
||||
});
|
||||
|
||||
await extract(crxToZip(buf), extDir).catch(console.error);
|
||||
await extract(crxToZip(buf), extDir)
|
||||
.catch(err => console.error(`Failed to extract extension ${id}`, err));
|
||||
}
|
||||
|
||||
session.defaultSession.loadExtension(extDir);
|
||||
|
|
70
src/main/utils/http.ts
Normal file
70
src/main/utils/http.ts
Normal file
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* Vencord, a modification for Discord's desktop app
|
||||
* Copyright (c) 2022 Vendicated and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { createWriteStream } from "original-fs";
|
||||
import { Readable } from "stream";
|
||||
import { finished } from "stream/promises";
|
||||
|
||||
type Url = string | URL;
|
||||
|
||||
export async function checkedFetch(url: Url, options?: RequestInit) {
|
||||
try {
|
||||
var res = await fetch(url, options);
|
||||
} catch (err) {
|
||||
if (err instanceof Error && err.cause) {
|
||||
err = err.cause;
|
||||
}
|
||||
|
||||
throw new Error(`${options?.method ?? "GET"} ${url} failed: ${err}`);
|
||||
}
|
||||
|
||||
if (res.ok) {
|
||||
return res;
|
||||
}
|
||||
|
||||
let message = `${options?.method ?? "GET"} ${url}: ${res.status} ${res.statusText}`;
|
||||
try {
|
||||
const reason = await res.text();
|
||||
message += `\n${reason}`;
|
||||
} catch { }
|
||||
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
export async function fetchJson<T = any>(url: Url, options?: RequestInit) {
|
||||
const res = await checkedFetch(url, options);
|
||||
return res.json() as Promise<T>;
|
||||
}
|
||||
|
||||
export async function fetchBuffer(url: Url, options?: RequestInit) {
|
||||
const res = await checkedFetch(url, options);
|
||||
const buf = await res.arrayBuffer();
|
||||
|
||||
return Buffer.from(buf);
|
||||
}
|
||||
|
||||
export async function downloadToFile(url: Url, path: string, options?: RequestInit) {
|
||||
const res = await checkedFetch(url, options);
|
||||
if (!res.body) {
|
||||
throw new Error(`Download ${url}: response body is empty`);
|
||||
}
|
||||
|
||||
// @ts-expect-error weird type conflict
|
||||
const body = Readable.fromWeb(res.body);
|
||||
await finished(body.pipe(createWriteStream(path)));
|
||||
}
|
|
@ -1,37 +0,0 @@
|
|||
/*
|
||||
* Vencord, a modification for Discord's desktop app
|
||||
* Copyright (c) 2022 Vendicated and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import https from "https";
|
||||
|
||||
export function get(url: string, options: https.RequestOptions = {}) {
|
||||
return new Promise<Buffer>((resolve, reject) => {
|
||||
https.get(url, options, res => {
|
||||
const { statusCode, statusMessage, headers } = res;
|
||||
if (statusCode! >= 400)
|
||||
return void reject(`${statusCode}: ${statusMessage} - ${url}`);
|
||||
if (statusCode! >= 300)
|
||||
return void resolve(get(headers.location!, options));
|
||||
|
||||
const chunks = [] as Buffer[];
|
||||
res.on("error", reject);
|
||||
|
||||
res.on("data", chunk => chunks.push(chunk));
|
||||
res.once("end", () => resolve(Buffer.concat(chunks)));
|
||||
});
|
||||
});
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue