mirror of https://github.com/msgbyte/tailchat
refactor: 调整桌面版依赖
parent
385efc6b6e
commit
fc8915430f
@ -0,0 +1,140 @@
|
||||
/**
|
||||
* Fork from https://github.com/ankurk91/electron-update-notifier/blob/master/src/index.ts
|
||||
*/
|
||||
|
||||
import path from 'path';
|
||||
import axios from 'axios';
|
||||
import electron from 'electron';
|
||||
import compareVersions from 'compare-versions';
|
||||
import gh from 'github-url-to-object';
|
||||
|
||||
interface Options {
|
||||
repository?: string;
|
||||
token?: string;
|
||||
debug?: boolean;
|
||||
silent?: boolean;
|
||||
}
|
||||
|
||||
interface GithubReleaseObject {
|
||||
tag_name: string;
|
||||
body: string;
|
||||
html_url: string;
|
||||
}
|
||||
|
||||
export const defaultOptions: Options = {
|
||||
debug: false, // force run in development
|
||||
silent: true,
|
||||
};
|
||||
|
||||
export function setUpdateNotification(options: Options = defaultOptions) {
|
||||
const withDefaults = Object.assign(defaultOptions, options);
|
||||
|
||||
if (electron.app.isReady()) {
|
||||
checkForUpdates(withDefaults);
|
||||
} else {
|
||||
electron.app.on('ready', () => {
|
||||
checkForUpdates(withDefaults);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export async function checkForUpdates({
|
||||
repository,
|
||||
token,
|
||||
debug,
|
||||
silent,
|
||||
}: Options = defaultOptions) {
|
||||
if (!electron.app.isPackaged && !debug) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!repository) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const pkg = require(path.join(electron.app.getAppPath(), 'package.json'));
|
||||
const ghObj = gh(pkg.repository);
|
||||
|
||||
if (!ghObj) {
|
||||
throw new Error('Repository URL not found in package.json file.');
|
||||
}
|
||||
|
||||
repository = ghObj.user + '/' + ghObj.repo;
|
||||
}
|
||||
|
||||
let latestRelease: null | GithubReleaseObject = null;
|
||||
|
||||
try {
|
||||
const { data: releases } = await axios.get(
|
||||
`https://api.github.com/repos/${repository}/releases`,
|
||||
{
|
||||
headers: token ? { authorization: `token ${token}` } : {},
|
||||
}
|
||||
);
|
||||
|
||||
latestRelease = releases[0] as GithubReleaseObject;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
|
||||
if (!silent) {
|
||||
showDialog(
|
||||
'Unable to check for updates at this moment. Try again.',
|
||||
'error'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (!latestRelease) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
compareVersions.compare(
|
||||
latestRelease.tag_name,
|
||||
electron.app.getVersion(),
|
||||
'>'
|
||||
)
|
||||
) {
|
||||
showUpdateDialog(latestRelease);
|
||||
} else {
|
||||
if (!silent) {
|
||||
showDialog(`You are already running the latest version.`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function showUpdateDialog(release: GithubReleaseObject) {
|
||||
electron.dialog
|
||||
.showMessageBox({
|
||||
title: electron.app.getName(),
|
||||
type: 'info',
|
||||
message: `New release available`,
|
||||
detail:
|
||||
`Installed Version: ${electron.app.getVersion()}\nLatest Version: ${
|
||||
release.tag_name
|
||||
}\n\n${release.body}`.trim(),
|
||||
buttons: ['Download', 'Later'],
|
||||
defaultId: 0,
|
||||
cancelId: 1,
|
||||
})
|
||||
.then(({ response }) => {
|
||||
if (response === 0) {
|
||||
setImmediate(() => {
|
||||
electron.shell.openExternal(release.html_url);
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
throw new Error(error);
|
||||
});
|
||||
}
|
||||
|
||||
const showDialog = (detail: string, type = 'info') => {
|
||||
electron.dialog.showMessageBox({
|
||||
title: electron.app.getName(),
|
||||
message: 'Update checker',
|
||||
buttons: ['Close'],
|
||||
defaultId: 0,
|
||||
cancelId: 0,
|
||||
type,
|
||||
detail,
|
||||
});
|
||||
};
|
@ -0,0 +1,217 @@
|
||||
import assert from 'assert';
|
||||
import isURL from 'is-url';
|
||||
import isDev from 'electron-is-dev';
|
||||
import ms from 'ms';
|
||||
import gh from 'github-url-to-object';
|
||||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
import os from 'os';
|
||||
import { format } from 'util';
|
||||
import pkg from '../../package.json';
|
||||
const userAgent = format(
|
||||
'%s/%s (%s: %s)',
|
||||
pkg.name,
|
||||
pkg.version,
|
||||
os.platform(),
|
||||
os.arch()
|
||||
);
|
||||
const supportedPlatforms = ['darwin', 'win32'];
|
||||
|
||||
interface ILogger {
|
||||
log(...args: any[]): void;
|
||||
info(...args: any[]): void;
|
||||
error(...args: any[]): void;
|
||||
warn(...args: any[]): void;
|
||||
}
|
||||
|
||||
interface IUpdateElectronAppOptions<L = ILogger> {
|
||||
/**
|
||||
* @param {String} repo A GitHub repository in the format `owner/repo`.
|
||||
* Defaults to your `package.json`'s `"repository"` field
|
||||
*/
|
||||
readonly repo?: string;
|
||||
/**
|
||||
* @param {String} host Defaults to `https://update.electronjs.org`
|
||||
*/
|
||||
readonly host?: string;
|
||||
/**
|
||||
* @param {String} updateInterval How frequently to check for updates. Defaults to `10 minutes`.
|
||||
* Minimum allowed interval is `5 minutes`.
|
||||
*/
|
||||
readonly updateInterval?: string;
|
||||
/**
|
||||
* @param {Object} logger A custom logger object that defines a `log` function.
|
||||
* Defaults to `console`. See electron-log, a module
|
||||
* that aggregates logs from main and renderer processes into a single file.
|
||||
*/
|
||||
readonly logger?: L;
|
||||
/**
|
||||
* @param {Boolean} notifyUser Defaults to `true`. When enabled the user will be
|
||||
* prompted to apply the update immediately after download.
|
||||
*/
|
||||
readonly notifyUser?: boolean;
|
||||
|
||||
electron?: typeof Electron.CrossProcessExports;
|
||||
}
|
||||
|
||||
export function updateElectronApp<L extends ILogger = ILogger>(
|
||||
opts: IUpdateElectronAppOptions<L> = {}
|
||||
) {
|
||||
// check for bad input early, so it will be logged during development
|
||||
opts = validateInput(opts);
|
||||
|
||||
// don't attempt to update during development
|
||||
if (isDev) {
|
||||
const message =
|
||||
'update-electron-app config looks good; aborting updates since app is in development mode';
|
||||
opts.logger ? opts.logger.log(message) : console.log(message);
|
||||
return;
|
||||
}
|
||||
|
||||
opts.electron.app.isReady()
|
||||
? initUpdater(opts)
|
||||
: opts.electron.app.on('ready', () => initUpdater(opts));
|
||||
}
|
||||
|
||||
function initUpdater<L extends ILogger = ILogger>(
|
||||
opts: IUpdateElectronAppOptions<L>
|
||||
) {
|
||||
const { host, repo, updateInterval, logger, electron } = opts;
|
||||
const { app, autoUpdater, dialog } = electron;
|
||||
const feedURL = `${host}/${repo}/${process.platform}-${
|
||||
process.arch
|
||||
}/${app.getVersion()}`;
|
||||
const requestHeaders = { 'User-Agent': userAgent };
|
||||
|
||||
function log(...args: any[]) {
|
||||
logger.log(...args);
|
||||
}
|
||||
|
||||
// exit early on unsupported platforms, e.g. `linux`
|
||||
if (
|
||||
typeof process !== 'undefined' &&
|
||||
process.platform &&
|
||||
!supportedPlatforms.includes(process.platform)
|
||||
) {
|
||||
log(
|
||||
`Electron's autoUpdater does not support the '${process.platform}' platform`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
log('feedURL', feedURL);
|
||||
log('requestHeaders', requestHeaders);
|
||||
autoUpdater.setFeedURL({
|
||||
url: feedURL,
|
||||
headers: requestHeaders,
|
||||
});
|
||||
|
||||
autoUpdater.on('error', (err) => {
|
||||
log('updater error');
|
||||
log(err);
|
||||
});
|
||||
|
||||
autoUpdater.on('checking-for-update', () => {
|
||||
log('checking-for-update');
|
||||
});
|
||||
|
||||
autoUpdater.on('update-available', () => {
|
||||
log('update-available; downloading...');
|
||||
});
|
||||
|
||||
autoUpdater.on('update-not-available', () => {
|
||||
log('update-not-available');
|
||||
});
|
||||
|
||||
if (opts.notifyUser) {
|
||||
autoUpdater.on(
|
||||
'update-downloaded',
|
||||
(event, releaseNotes, releaseName, releaseDate, updateURL) => {
|
||||
log('update-downloaded', [
|
||||
event,
|
||||
releaseNotes,
|
||||
releaseName,
|
||||
releaseDate,
|
||||
updateURL,
|
||||
]);
|
||||
|
||||
const dialogOpts = {
|
||||
type: 'info',
|
||||
buttons: ['Restart', 'Later'],
|
||||
title: 'Application Update',
|
||||
message: process.platform === 'win32' ? releaseNotes : releaseName,
|
||||
detail:
|
||||
'A new version has been downloaded. Restart the application to apply the updates.',
|
||||
};
|
||||
|
||||
dialog.showMessageBox(dialogOpts).then(({ response }) => {
|
||||
if (response === 0) autoUpdater.quitAndInstall();
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// check for updates right away and keep checking later
|
||||
autoUpdater.checkForUpdates();
|
||||
setInterval(() => {
|
||||
autoUpdater.checkForUpdates();
|
||||
}, ms(updateInterval));
|
||||
}
|
||||
|
||||
function validateInput<L extends ILogger = ILogger>(
|
||||
opts: IUpdateElectronAppOptions<L>
|
||||
): IUpdateElectronAppOptions<L> {
|
||||
const defaults = {
|
||||
host: 'https://update.electronjs.org',
|
||||
updateInterval: '10 minutes',
|
||||
logger: console,
|
||||
notifyUser: true,
|
||||
};
|
||||
const { host, updateInterval, logger, notifyUser } = Object.assign(
|
||||
{},
|
||||
defaults,
|
||||
opts
|
||||
);
|
||||
|
||||
// allows electron to be mocked in tests
|
||||
const electron = opts.electron || require('electron');
|
||||
|
||||
let repo = opts.repo;
|
||||
if (!repo) {
|
||||
const pkgBuf = fs.readFileSync(
|
||||
path.join(electron.app.getAppPath(), 'package.json')
|
||||
);
|
||||
const pkg = JSON.parse(pkgBuf.toString());
|
||||
const repoString = (pkg.repository && pkg.repository.url) || pkg.repository;
|
||||
const repoObject = gh(repoString);
|
||||
assert(
|
||||
repoObject,
|
||||
"repo not found. Add repository string to your app's package.json file"
|
||||
);
|
||||
repo = `${repoObject.user}/${repoObject.repo}`;
|
||||
}
|
||||
|
||||
assert(
|
||||
repo && repo.length && repo.includes('/'),
|
||||
'repo is required and should be in the format `owner/repo`'
|
||||
);
|
||||
|
||||
assert(
|
||||
isURL(host) && host.startsWith('https'),
|
||||
'host must be a valid HTTPS URL'
|
||||
);
|
||||
|
||||
assert(
|
||||
typeof updateInterval === 'string' && updateInterval.match(/^\d+/),
|
||||
'updateInterval must be a human-friendly string interval like `20 minutes`'
|
||||
);
|
||||
|
||||
assert(
|
||||
ms(updateInterval) >= 5 * 60 * 1000,
|
||||
'updateInterval must be `5 minutes` or more'
|
||||
);
|
||||
|
||||
assert(logger && typeof logger.log, 'function');
|
||||
|
||||
return { host, repo, updateInterval, logger, electron, notifyUser };
|
||||
}
|
Loading…
Reference in New Issue