Skip to content

Commit a7bb896

Browse files
committed
fix no error handler for download update zip and add referer
1 parent 0b27667 commit a7bb896

File tree

8 files changed

+161
-31
lines changed

8 files changed

+161
-31
lines changed

src/renderer/components/batch-progress/use-batch-progress.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,16 @@ const useBatchProgress = (
2020
initState: BatchProgressState = defaultBatchProgressState
2121
): [BatchProgressState, typeof setBatchProgressState] => {
2222
const [batchProgressState, setState] = useState<BatchProgressState>(initState);
23-
function setBatchProgressState(state: Partial<BatchProgressState>) {
23+
function setBatchProgressState(
24+
state: Partial<BatchProgressState> | ((state: BatchProgressState) => Partial<BatchProgressState>)
25+
) {
26+
if (typeof state === "function") {
27+
setState(s => ({
28+
...s,
29+
...state(s)
30+
}));
31+
return;
32+
}
2433
setState(s => ({
2534
...s,
2635
...state,

src/renderer/components/modals/general/about/download-update.tsx

Lines changed: 58 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,13 @@ import Settings from "@renderer/modules/settings";
1212
import useDownloadUpdate from "./use-download-update";
1313

1414
const DownloadButton: React.FC<{
15+
downloadError: Error | null,
1516
downloadedFilePath: string | undefined,
1617
progressStatus: BatchTaskStatus,
1718
onClickStart: () => void,
1819
onClickPause: () => void,
1920
}> = ({
21+
downloadError,
2022
downloadedFilePath,
2123
progressStatus,
2224
onClickStart,
@@ -52,6 +54,13 @@ const DownloadButton: React.FC<{
5254
content = translate("modals.about.updateApp.operationButton.showItemInDir");
5355
}
5456

57+
if (downloadError) {
58+
variant = "info";
59+
iconClassName = "bi bi-arrow-repeat me-1";
60+
handleClick = onClickStart;
61+
content = translate("common.retry")
62+
}
63+
5564
return (
5665
<Button
5766
size="sm"
@@ -79,24 +88,40 @@ const DownloadUpdate: React.FC<DownloadUpgradeProps> = ({
7988
const {
8089
cachedFilePath,
8190
cachedProgressState,
91+
fetchUpdate,
8292
downloadLatestVersion,
8393
background,
8494
} = useDownloadUpdate();
95+
const [downloadError, setDownloadError] = useState<Error | null>(null)
8596
const [downloadedFilePath, setDownloadFilePath] = useState(cachedFilePath);
8697

8798
// download
8899
const handleProgress = (loaded: number, total: number): boolean => {
89100
setBatchProgressState({
90101
total: total,
91102
finished: loaded,
103+
errored: 0,
92104
});
93105
return isGoOn.current;
94106
}
95107

108+
const handleError = (err: Error) => {
109+
if (err.toString().includes("aborted")) {
110+
return;
111+
}
112+
setDownloadError(err);
113+
setBatchProgressState(s => ({
114+
status: BatchTaskStatus.Ended,
115+
finished: 0,
116+
errored: s.finished,
117+
}));
118+
};
119+
96120
const handleStart = () => {
97121
if (batchProgressState.status === BatchTaskStatus.Running) {
98122
return;
99123
}
124+
setDownloadError(null);
100125
setBatchProgressState({
101126
status: BatchTaskStatus.Running,
102127
});
@@ -109,7 +134,8 @@ const DownloadUpdate: React.FC<DownloadUpgradeProps> = ({
109134
status: BatchTaskStatus.Ended,
110135
});
111136
setDownloadFilePath(filePath);
112-
});
137+
})
138+
.catch(handleError);
113139
};
114140

115141
const handlePause = () => {
@@ -119,11 +145,16 @@ const DownloadUpdate: React.FC<DownloadUpgradeProps> = ({
119145
});
120146
};
121147

148+
const handleManuelDownload = async () => {
149+
const {downloadPageUrl} = await fetchUpdate();
150+
await shell.openExternal(downloadPageUrl);
151+
}
152+
122153
// store and restore state
123154
useMount(() => {
124155
if (cachedProgressState) {
125156
setBatchProgressState(cachedProgressState);
126-
if (cachedProgressState.status !== BatchTaskStatus.Standby) {
157+
if (cachedProgressState.status === BatchTaskStatus.Running) {
127158
handleStart();
128159
return;
129160
}
@@ -139,8 +170,24 @@ const DownloadUpdate: React.FC<DownloadUpgradeProps> = ({
139170
return (
140171
<div>
141172
<div className="d-flex justify-content-between align-items-center">
142-
<div>{translate("modals.about.updateApp.foundLatest")} <span className="text-primary">v{version}</span></div>
173+
<div className="me-auto">
174+
{translate("modals.about.updateApp.foundLatest")}
175+
<span className="text-primary ms-1">v{version}</span>
176+
</div>
177+
{
178+
downloadError &&
179+
<Button
180+
className="me-1"
181+
size="sm"
182+
variant="link"
183+
onClick={handleManuelDownload}
184+
>
185+
{translate("modals.about.updateApp.downloadManually")}
186+
<i className="bi bi-box-arrow-up-right ms-1"/>
187+
</Button>
188+
}
143189
<DownloadButton
190+
downloadError={downloadError}
144191
downloadedFilePath={downloadedFilePath}
145192
progressStatus={batchProgressState.status}
146193
onClickStart={handleStart}
@@ -164,13 +211,21 @@ const DownloadUpdate: React.FC<DownloadUpgradeProps> = ({
164211
downloadedFilePath &&
165212
<small className="text-success">{translate("common.downloaded")}</small>
166213
}
214+
{
215+
downloadError &&
216+
<small className="text-danger">{translate("common.failed")}</small>
217+
}
167218
<BatchProgress
168219
status={batchProgressState.status}
169220
total={batchProgressState.total}
170221
finished={batchProgressState.finished}
171222
errored={batchProgressState.errored}
172223
isPercent
173224
/>
225+
{
226+
downloadError &&
227+
<small className="text-danger">{downloadError.toString()}</small>
228+
}
174229
</>
175230
}
176231
</div>

src/renderer/components/modals/general/about/use-download-update.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// this file cached download state to keep job can run background.
2-
import {downloadLatestVersion} from "@renderer/modules/update-app";
2+
import {fetchUpdate, downloadLatestVersion} from "@renderer/modules/update-app";
33
import {BatchProgressState} from "@renderer/components/batch-progress";
44

55
let downloadPromise: ReturnType<typeof downloadLatestVersion> | undefined;
@@ -38,6 +38,7 @@ const useDownloadUpdate = () => {
3838
return {
3939
cachedFilePath,
4040
cachedProgressState,
41+
fetchUpdate,
4142
downloadLatestVersion: _downloadLatestVersion,
4243
background,
4344
};

src/renderer/modules/i18n/lang/dict.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ export default interface Dictionary {
1919
refreshing: string,
2020
refreshed: string,
2121
interrupt: string,
22+
retry: string,
23+
retrying: string,
2224
notFound: string,
2325
noObjectSelected: string,
2426
noDomainToGet: string,
@@ -477,6 +479,7 @@ export default interface Dictionary {
477479
alreadyLatest: string,
478480
foundLatest: string,
479481
changeLogsTitle: string,
482+
downloadManually: string,
480483
operationButton: {
481484
start: string,
482485
resume: string,

src/renderer/modules/i18n/lang/en-us.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ const dict: Dictionary = {
2020
refreshing: "Refreshing",
2121
refreshed: "Refreshed",
2222
interrupt: "Interrupt",
23+
retry: "Retry",
24+
retrying: "Retrying",
2325
notFound: "Page Not Found!",
2426
noObjectSelected: "No object selected",
2527
noDomainToGet: "No domain to get object",
@@ -475,6 +477,7 @@ const dict: Dictionary = {
475477
alreadyLatest: "You're up to date!",
476478
foundLatest: "A new version is available!",
477479
changeLogsTitle: "Changes:",
480+
downloadManually: "Download Manually",
478481
operationButton: {
479482
start: "Download",
480483
resume: "Resume",

src/renderer/modules/i18n/lang/ja-jp.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ const dict: Dictionary = {
2020
refreshing: "更新中",
2121
refreshed: "正常に更新",
2222
interrupt: "中断",
23+
retry: "リトライ",
24+
retrying: "リトライ中",
2325
notFound: "未発見",
2426
noObjectSelected: "操作可能なオブジェクトがない",
2527
noDomainToGet: "オブジェクトを取得するためのドメインがない",
@@ -474,6 +476,7 @@ const dict: Dictionary = {
474476
alreadyLatest: "既に最新バージョンです!",
475477
foundLatest: "新しいバージョンを見つけました",
476478
changeLogsTitle: "リリースノート:",
479+
downloadManually: "手動ダウンロード",
477480
operationButton: {
478481
start: "更新を開始",
479482
resume: "ダウンロードを続行",

src/renderer/modules/i18n/lang/zh-cn.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ const dict: Dictionary = {
2020
refreshing: "刷新中",
2121
refreshed: "已刷新",
2222
interrupt: "中断",
23+
retry: "重试",
24+
retrying: "重试中",
2325
notFound: "来到了无牛问津的地方",
2426
noObjectSelected: "未选择操作对象",
2527
noDomainToGet: "没有可用的域名获取对象",
@@ -474,6 +476,7 @@ const dict: Dictionary = {
474476
alreadyLatest: "已是最新版!",
475477
foundLatest: "发现新版本",
476478
changeLogsTitle: "主要更新:",
479+
downloadManually: "手动下载",
477480
operationButton: {
478481
start: "开始下载",
479482
resume: "继续下载",

src/renderer/modules/update-app/index.ts

Lines changed: 79 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,21 @@ import {upgrade} from "@renderer/customize";
99

1010
const CACHE_DURATION = Duration.Hour;
1111

12-
let latestVersion: string = "";
13-
let latestDownloadUrl: string = "";
14-
let lastCheckTimestamp: number = 0;
12+
interface UpdateInfo {
13+
referer: string,
14+
downloadPageUrl: string,
15+
latestVersion: string,
16+
latestDownloadUrl: string,
17+
lastCheckTimestamp: number,
18+
}
19+
20+
let cachedUpdateInfo: UpdateInfo = {
21+
referer: "",
22+
downloadPageUrl: "",
23+
latestVersion: "",
24+
latestDownloadUrl: "",
25+
lastCheckTimestamp: 0,
26+
}
1527

1628
function compareVersion(current: string, latest: string) {
1729
const arr = current.split(".");
@@ -33,27 +45,60 @@ function compareVersion(current: string, latest: string) {
3345
}
3446

3547
export async function fetchReleaseNote(version: string): Promise<string> {
36-
const resp = await fetch(`${upgrade.release_notes_url}${version}.md`);
37-
if (Math.floor(resp.status / 100) !== 2) {
48+
const resp = await new Promise<request.Response>((resolve, reject) => {
49+
request.get(
50+
{
51+
url: `${upgrade.release_notes_url}${version}.md`,
52+
},
53+
(error, response) => {
54+
if(error) {
55+
reject(error);
56+
return;
57+
}
58+
resolve(response);
59+
});
60+
});
61+
if (Math.floor(resp.statusCode / 100) !== 2) {
3862
return "Not Found";
3963
}
40-
return resp.text();
64+
return resp.body;
65+
}
66+
67+
68+
export async function fetchUpdate(): Promise<UpdateInfo> {
69+
if (Date.now() - cachedUpdateInfo.lastCheckTimestamp <= CACHE_DURATION) {
70+
return cachedUpdateInfo;
71+
}
72+
const resp = await new Promise<request.Response>((resolve, reject) => {
73+
request.get(
74+
{
75+
url: upgrade.check_url,
76+
},
77+
(error, response) => {
78+
if (error || Math.floor(response.statusCode / 100) !== 2) {
79+
reject(error);
80+
return;
81+
}
82+
resolve(response);
83+
}
84+
);
85+
});
86+
const respJson = JSON.parse(resp.body);
87+
cachedUpdateInfo = {
88+
referer: respJson.referer,
89+
downloadPageUrl: respJson["download_page"],
90+
latestVersion: respJson.version,
91+
latestDownloadUrl: respJson.downloads[process.platform][process.arch],
92+
lastCheckTimestamp: 0,
93+
};
94+
return cachedUpdateInfo;
4195
}
4296

4397
/**
4498
* return null if there isn't a new version
4599
*/
46100
export async function fetchLatestVersion(currentVersion: string): Promise<string | null> {
47-
if (Date.now() - lastCheckTimestamp <= CACHE_DURATION) {
48-
return compareVersion(currentVersion, latestVersion) < 0
49-
? latestVersion
50-
: null;
51-
}
52-
const resp = await fetch(upgrade.check_url);
53-
const respJson = await resp.json();
54-
lastCheckTimestamp = Date.now();
55-
latestVersion = respJson.version;
56-
latestDownloadUrl = respJson.downloads[process.platform][process.arch];
101+
const {latestVersion} = await fetchUpdate();
57102
return compareVersion(currentVersion, latestVersion) < 0
58103
? latestVersion
59104
: null;
@@ -102,9 +147,7 @@ export async function downloadLatestVersion({
102147
// if return false will stop download
103148
onProgress: (loaded: number, total: number) => boolean,
104149
}): Promise<string> {
105-
if (!latestDownloadUrl || Date.now() - lastCheckTimestamp <= CACHE_DURATION) {
106-
await fetchLatestVersion("");
107-
}
150+
const {latestDownloadUrl, referer} = await fetchUpdate();
108151

109152
const fileName = decodeURIComponent(path.basename(latestDownloadUrl));
110153
const to = path.join(toDirectory, fileName);
@@ -125,7 +168,12 @@ export async function downloadLatestVersion({
125168

126169
// get remote file info
127170
const response = await new Promise<Response>((resolve, reject) => {
128-
request.head(latestDownloadUrl)
171+
request.head({
172+
url: latestDownloadUrl,
173+
headers: {
174+
"Referer": referer,
175+
},
176+
})
129177
.on("error", reject)
130178
.on("response", resolve);
131179
});
@@ -146,16 +194,21 @@ export async function downloadLatestVersion({
146194

147195
// download file
148196
let downloadedSize = tempFileSize;
149-
const fileWriter = fs.createWriteStream(downloadedFilePath, {
150-
// old code is `flags: ...`. maybe it's a mistake
151-
// mode: fs.constants.O_CREAT | fs.constants.O_WRONLY | fs.constants.O_NONBLOCK,
152-
start: downloadedSize,
153-
});
154197
await new Promise((resolve, reject) => {
198+
const fileWriter = fs.createWriteStream(downloadedFilePath, {
199+
start: downloadedSize,
200+
});
201+
fileWriter.on("error", err => {
202+
if (req) {
203+
req.abort();
204+
}
205+
reject(err);
206+
});
155207
const req = request({
156208
url: latestDownloadUrl,
157209
headers: {
158-
"Range": `bytes=${downloadedSize}-`
210+
"Range": `bytes=${downloadedSize}-`,
211+
"Referer": referer,
159212
},
160213
});
161214
req.on("error", reject)

0 commit comments

Comments
 (0)