From 87b23a1fac901a5678f1a55f28a7f9a5263cb962 Mon Sep 17 00:00:00 2001 From: twwu Date: Tue, 19 Nov 2024 11:20:17 +0800 Subject: [PATCH] feat: refactor GitHub releases fetching and update handling with improved error notifications --- .../plugins/install-plugin/hooks.ts | 58 ++++++++++-------- .../plugin-detail-panel/detail-header.tsx | 59 +++++++------------ .../components/plugins/plugin-item/action.tsx | 59 +++++++------------ .../repos/[owner]/[repo]/releases/route.ts | 36 +++++++++++ web/package.json | 1 + web/pnpm-lock.yaml | 3 + 6 files changed, 114 insertions(+), 102 deletions(-) create mode 100644 web/app/repos/[owner]/[repo]/releases/route.ts diff --git a/web/app/components/plugins/install-plugin/hooks.ts b/web/app/components/plugins/install-plugin/hooks.ts index 50ed9ab085..b2a5af4a2f 100644 --- a/web/app/components/plugins/install-plugin/hooks.ts +++ b/web/app/components/plugins/install-plugin/hooks.ts @@ -1,26 +1,16 @@ -import Toast from '@/app/components/base/toast' +import Toast, { type IToastProps } from '@/app/components/base/toast' import { uploadGitHub } from '@/service/plugins' -import { Octokit } from '@octokit/core' -import { GITHUB_ACCESS_TOKEN } from '@/config' import { compareVersion, getLatestVersion } from '@/utils/semver' import type { GitHubRepoReleaseResponse } from '../types' export const useGitHubReleases = () => { const fetchReleases = async (owner: string, repo: string) => { try { - const octokit = new Octokit({ - auth: GITHUB_ACCESS_TOKEN, - }) - const res = await octokit.request('GET /repos/{owner}/{repo}/releases', { - owner, - repo, - headers: { - 'X-GitHub-Api-Version': '2022-11-28', - }, - }) - if (res.status !== 200) throw new Error('Failed to fetch releases') + const res = await fetch(`/repos/${owner}/${repo}/releases`) + const bodyJson = await res.json() + if (bodyJson.status !== 200) throw new Error(bodyJson.data.message) - const formattedReleases = res.data.map((release: any) => ({ + const formattedReleases = bodyJson.data.map((release: any) => ({ tag_name: release.tag_name, assets: release.assets.map((asset: any) => ({ browser_download_url: asset.browser_download_url, @@ -31,26 +21,46 @@ export const useGitHubReleases = () => { return formattedReleases } catch (error) { - Toast.notify({ - type: 'error', - message: 'Failed to fetch repository releases', - }) + if (error instanceof Error) { + Toast.notify({ + type: 'error', + message: error.message, + }) + } + else { + Toast.notify({ + type: 'error', + message: 'Failed to fetch repository releases', + }) + } return [] } } const checkForUpdates = (fetchedReleases: GitHubRepoReleaseResponse[], currentVersion: string) => { - if (fetchedReleases.length === 0) throw new Error('No releases found') + let needUpdate = false + const toastProps: IToastProps = { + type: 'info', + message: 'No new version available', + } + if (fetchedReleases.length === 0) { + toastProps.type = 'error' + toastProps.message = 'Input releases is empty' + return { needUpdate, toastProps } + } const versions = fetchedReleases.map(release => release.tag_name) const latestVersion = getLatestVersion(versions) - let res = false try { - res = compareVersion(latestVersion, currentVersion) === 1 + needUpdate = compareVersion(latestVersion, currentVersion) === 1 + if (needUpdate) + toastProps.message = `New version available: ${latestVersion}` } catch { - throw new Error('Failed to compare versions, please check the version format.') + needUpdate = false + toastProps.type = 'error' + toastProps.message = 'Fail to compare versions, please check the version format' } - return res + return { needUpdate, toastProps } } return { fetchReleases, checkForUpdates } diff --git a/web/app/components/plugins/plugin-detail-panel/detail-header.tsx b/web/app/components/plugins/plugin-detail-panel/detail-header.tsx index 810c815bad..5e1b0770f7 100644 --- a/web/app/components/plugins/plugin-detail-panel/detail-header.tsx +++ b/web/app/components/plugins/plugin-detail-panel/detail-header.tsx @@ -88,47 +88,28 @@ const DetailHeader = ({ return } - try { - const fetchedReleases = await fetchReleases(author, name) - if (checkForUpdates(fetchedReleases, meta!.version)) { - setShowUpdatePluginModal({ - onSaveCallback: () => { - onUpdate() - }, - payload: { - type: PluginSource.github, - github: { - originalPackageInfo: { - id: detail.plugin_unique_identifier, - repo: meta!.repo, - version: meta!.version, - package: meta!.package, - releases: fetchedReleases, - }, + const fetchedReleases = await fetchReleases(author, name) + if (fetchedReleases.length === 0) return + const { needUpdate, toastProps } = checkForUpdates(fetchedReleases, meta!.version) + Toast.notify(toastProps) + if (needUpdate) { + setShowUpdatePluginModal({ + onSaveCallback: () => { + onUpdate() + }, + payload: { + type: PluginSource.github, + github: { + originalPackageInfo: { + id: detail.plugin_unique_identifier, + repo: meta!.repo, + version: meta!.version, + package: meta!.package, + releases: fetchedReleases, }, }, - }) - } - else { - Toast.notify({ - type: 'info', - message: 'No new version available', - }) - } - } - catch (error) { - if (error instanceof Error) { - Toast.notify({ - type: 'error', - message: error.message, - }) - } - else { - Toast.notify({ - type: 'error', - message: 'Failed to compare versions', - }) - } + }, + }) } } diff --git a/web/app/components/plugins/plugin-item/action.tsx b/web/app/components/plugins/plugin-item/action.tsx index 773e42fdc9..a387727b4f 100644 --- a/web/app/components/plugins/plugin-item/action.tsx +++ b/web/app/components/plugins/plugin-item/action.tsx @@ -54,47 +54,28 @@ const Action: FC = ({ const invalidateInstalledPluginList = useInvalidateInstalledPluginList() const handleFetchNewVersion = async () => { - try { - const fetchedReleases = await fetchReleases(author, pluginName) - if (checkForUpdates(fetchedReleases, meta!.version)) { - setShowUpdatePluginModal({ - onSaveCallback: () => { - invalidateInstalledPluginList() - }, - payload: { - type: PluginSource.github, - github: { - originalPackageInfo: { - id: pluginUniqueIdentifier, - repo: meta!.repo, - version: meta!.version, - package: meta!.package, - releases: fetchedReleases, - }, + const fetchedReleases = await fetchReleases(author, pluginName) + if (fetchReleases.length === 0) return + const { needUpdate, toastProps } = checkForUpdates(fetchedReleases, meta!.version) + Toast.notify(toastProps) + if (needUpdate) { + setShowUpdatePluginModal({ + onSaveCallback: () => { + invalidateInstalledPluginList() + }, + payload: { + type: PluginSource.github, + github: { + originalPackageInfo: { + id: pluginUniqueIdentifier, + repo: meta!.repo, + version: meta!.version, + package: meta!.package, + releases: fetchedReleases, }, }, - }) - } - else { - Toast.notify({ - type: 'info', - message: 'No new version available', - }) - } - } - catch (error) { - if (error instanceof Error) { - Toast.notify({ - type: 'error', - message: error.message, - }) - } - else { - Toast.notify({ - type: 'error', - message: 'Failed to compare versions', - }) - } + }, + }) } } diff --git a/web/app/repos/[owner]/[repo]/releases/route.ts b/web/app/repos/[owner]/[repo]/releases/route.ts new file mode 100644 index 0000000000..29b604d94b --- /dev/null +++ b/web/app/repos/[owner]/[repo]/releases/route.ts @@ -0,0 +1,36 @@ +import { type NextRequest, NextResponse } from 'next/server' +import { Octokit } from '@octokit/core' +import { RequestError } from '@octokit/request-error' +import { GITHUB_ACCESS_TOKEN } from '@/config' + +type Params = { + owner: string, + repo: string, +} + +const octokit = new Octokit({ + auth: GITHUB_ACCESS_TOKEN, +}) + +export async function GET( + request: NextRequest, + { params }: { params: Promise }, +) { + const { owner, repo } = (await params) + try { + const releasesRes = await octokit.request('GET /repos/{owner}/{repo}/releases', { + owner, + repo, + headers: { + 'X-GitHub-Api-Version': '2022-11-28', + }, + }) + return NextResponse.json(releasesRes) + } + catch (error) { + if (error instanceof RequestError) + return NextResponse.json(error.response) + else + throw error + } +} diff --git a/web/package.json b/web/package.json index bb60bedd5c..2d415f7f10 100644 --- a/web/package.json +++ b/web/package.json @@ -38,6 +38,7 @@ "@monaco-editor/react": "^4.6.0", "@next/mdx": "^14.0.4", "@octokit/core": "^6.1.2", + "@octokit/request-error": "^6.1.5", "@remixicon/react": "^4.3.0", "@sentry/react": "^7.54.0", "@sentry/utils": "^7.54.0", diff --git a/web/pnpm-lock.yaml b/web/pnpm-lock.yaml index 2c43d1fe44..68f9d2e3d6 100644 --- a/web/pnpm-lock.yaml +++ b/web/pnpm-lock.yaml @@ -55,6 +55,9 @@ importers: '@octokit/core': specifier: ^6.1.2 version: 6.1.2 + '@octokit/request-error': + specifier: ^6.1.5 + version: 6.1.5 '@remixicon/react': specifier: ^4.3.0 version: 4.3.0(react@18.2.0)