From 2594c1edb12dd4657b6bd6ee650a5a928d772781 Mon Sep 17 00:00:00 2001 From: Alice <53339016+AliceHincu@users.noreply.github.com> Date: Tue, 17 Sep 2024 00:13:24 +0300 Subject: [PATCH] Desktop: Seamless-Updates: used download function from tool-utils (#11066) --- .../app-desktop/tools/githubReleasesUtils.ts | 22 +++----- .../app-desktop/tools/modifyReleaseAssets.ts | 6 +-- packages/tools/tool-utils.ts | 52 ++++++++++++++----- 3 files changed, 48 insertions(+), 32 deletions(-) diff --git a/packages/app-desktop/tools/githubReleasesUtils.ts b/packages/app-desktop/tools/githubReleasesUtils.ts index 4db186ee4..2d7811269 100644 --- a/packages/app-desktop/tools/githubReleasesUtils.ts +++ b/packages/app-desktop/tools/githubReleasesUtils.ts @@ -1,10 +1,8 @@ import * as fs from 'fs'; -import { createWriteStream } from 'fs'; import * as path from 'path'; -import { promisify } from 'util'; import { GitHubRelease, GitHubReleaseAsset } from '../utils/checkForUpdatesUtils'; +import { downloadFile } from '../../tools/tool-utils'; -const pipeline = promisify(require('stream').pipeline); export interface Context { repo: string; // {owner}/{repo} @@ -14,6 +12,7 @@ export interface Context { const apiBaseUrl = 'https://api.github.com/repos/'; const defaultApiHeaders = (context: Context) => ({ + 'User-Agent': 'Joplin', 'Authorization': `token ${context.githubToken}`, 'X-GitHub-Api-Version': '2022-11-28', 'Accept': 'application/vnd.github+json', @@ -45,7 +44,7 @@ export const getTargetRelease = async (context: Context, targetTag: string): Pro }; // Download a file from Joplin Desktop releases -export const downloadFile = async (context: Context, asset: GitHubReleaseAsset, destinationDir: string): Promise => { +export const downloadFileFromGitHub = async (context: Context, asset: GitHubReleaseAsset, destinationDir: string) => { const downloadPath = path.join(destinationDir, asset.name); if (!fs.existsSync(destinationDir)) { fs.mkdirSync(destinationDir); @@ -53,19 +52,10 @@ export const downloadFile = async (context: Context, asset: GitHubReleaseAsset, /* eslint-disable no-console */ console.log(`Downloading ${asset.name} from ${asset.url} to ${downloadPath}`); - const response = await fetch(asset.url, { - headers: { - ...defaultApiHeaders(context), - 'Accept': 'application/octet-stream', - }, + await downloadFile(asset.url, downloadPath, { + ...defaultApiHeaders(context), + 'Accept': 'application/octet-stream', }); - if (!response.ok) { - throw new Error(`Failed to download file: Status Code ${response.status}`); - } - const fileStream = createWriteStream(downloadPath); - await pipeline(response.body, fileStream); - console.log('Download successful!'); - /* eslint-enable no-console */ return downloadPath; }; diff --git a/packages/app-desktop/tools/modifyReleaseAssets.ts b/packages/app-desktop/tools/modifyReleaseAssets.ts index 82d1246f9..35d3b05b9 100644 --- a/packages/app-desktop/tools/modifyReleaseAssets.ts +++ b/packages/app-desktop/tools/modifyReleaseAssets.ts @@ -1,7 +1,7 @@ import path = require('path'); import { parseArgs } from 'util'; -import { Context, downloadFile, getTargetRelease, updateReleaseAsset, uploadReleaseAsset } from './githubReleasesUtils'; +import { Context, downloadFileFromGitHub, getTargetRelease, updateReleaseAsset, uploadReleaseAsset } from './githubReleasesUtils'; import { GitHubRelease } from '../utils/checkForUpdatesUtils'; import { GenerateInfo, generateLatestArm64Yml } from './generateLatestArm64Yml'; @@ -36,9 +36,9 @@ const createReleaseAssets = async (context: Context, release: GitHubRelease) => let zipPath; for (const asset of release.assets) { if (asset.name.endsWith('arm64.zip')) { - zipPath = await downloadFile(context, asset, downloadDir); + zipPath = await downloadFileFromGitHub(context, asset, downloadDir); } else if (asset.name.endsWith('arm64.DMG')) { - dmgPath = await downloadFile(context, asset, downloadDir); + dmgPath = await downloadFileFromGitHub(context, asset, downloadDir); } } diff --git a/packages/tools/tool-utils.ts b/packages/tools/tool-utils.ts index da5aee029..912a509f8 100644 --- a/packages/tools/tool-utils.ts +++ b/packages/tools/tool-utils.ts @@ -190,23 +190,49 @@ export async function setPackagePrivateField(filePath: string, value: any) { await writeFile(filePath, JSON.stringify(obj, null, 2), 'utf8'); } -export async function downloadFile(url: string, targetPath: string) { +export async function downloadFile(url: string, targetPath: string, headers: { [key: string]: string }) { const https = require('https'); return new Promise((resolve, reject) => { - const file = createWriteStream(targetPath); - // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied - https.get(url, (response: any) => { - if (response.statusCode !== 200) reject(new Error(`HTTP error ${response.statusCode}`)); - response.pipe(file); - file.on('finish', () => { - // file.close(); - resolve(null); + const makeDownloadRequest = (url: string) => { + const file = createWriteStream(targetPath); + // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied + https.get(url, headers, (response: any) => { + if (response.statusCode === 403) { + let data = ''; + response.on('data', (chunk: string) => { + data += chunk; + }); + response.on('end', () => { + console.log(`Body: ${data}`); + reject(new Error('Access forbidden. Possibly due to rate limiting or lack of permission.')); + }); + } else if (response.statusCode === 302 || response.statusCode === 301) { + const newUrl = response.headers.location; + if (newUrl) { + console.log(`Redirecting download request to ${newUrl}`); + file.close(); + makeDownloadRequest(url); + } else { + reject(new Error('Redirection failed, url undefined')); + } + } else if (response.statusCode !== 200) { + reject(new Error(`HTTP error ${response.statusCode}`)); + } else { + response.pipe(file); + file.on('finish', () => { + file.close(); + resolve(null); + }); + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied + }).on('error', (error: any) => { + reject(error); }); - // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied - }).on('error', (error: any) => { - reject(error); - }); + }; + + makeDownloadRequest(url); }); }