feat: refactor GitHub releases fetching and update handling with improved error notifications

pull/12372/head
twwu 2 years ago
parent d3fe6fd303
commit 87b23a1fac

@ -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 { uploadGitHub } from '@/service/plugins'
import { Octokit } from '@octokit/core'
import { GITHUB_ACCESS_TOKEN } from '@/config'
import { compareVersion, getLatestVersion } from '@/utils/semver' import { compareVersion, getLatestVersion } from '@/utils/semver'
import type { GitHubRepoReleaseResponse } from '../types' import type { GitHubRepoReleaseResponse } from '../types'
export const useGitHubReleases = () => { export const useGitHubReleases = () => {
const fetchReleases = async (owner: string, repo: string) => { const fetchReleases = async (owner: string, repo: string) => {
try { try {
const octokit = new Octokit({ const res = await fetch(`/repos/${owner}/${repo}/releases`)
auth: GITHUB_ACCESS_TOKEN, const bodyJson = await res.json()
}) if (bodyJson.status !== 200) throw new Error(bodyJson.data.message)
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 formattedReleases = res.data.map((release: any) => ({ const formattedReleases = bodyJson.data.map((release: any) => ({
tag_name: release.tag_name, tag_name: release.tag_name,
assets: release.assets.map((asset: any) => ({ assets: release.assets.map((asset: any) => ({
browser_download_url: asset.browser_download_url, browser_download_url: asset.browser_download_url,
@ -31,26 +21,46 @@ export const useGitHubReleases = () => {
return formattedReleases return formattedReleases
} }
catch (error) { catch (error) {
if (error instanceof Error) {
Toast.notify({
type: 'error',
message: error.message,
})
}
else {
Toast.notify({ Toast.notify({
type: 'error', type: 'error',
message: 'Failed to fetch repository releases', message: 'Failed to fetch repository releases',
}) })
}
return [] return []
} }
} }
const checkForUpdates = (fetchedReleases: GitHubRepoReleaseResponse[], currentVersion: string) => { 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 versions = fetchedReleases.map(release => release.tag_name)
const latestVersion = getLatestVersion(versions) const latestVersion = getLatestVersion(versions)
let res = false
try { try {
res = compareVersion(latestVersion, currentVersion) === 1 needUpdate = compareVersion(latestVersion, currentVersion) === 1
if (needUpdate)
toastProps.message = `New version available: ${latestVersion}`
} }
catch { 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 } return { fetchReleases, checkForUpdates }

@ -88,9 +88,11 @@ const DetailHeader = ({
return return
} }
try {
const fetchedReleases = await fetchReleases(author, name) const fetchedReleases = await fetchReleases(author, name)
if (checkForUpdates(fetchedReleases, meta!.version)) { if (fetchedReleases.length === 0) return
const { needUpdate, toastProps } = checkForUpdates(fetchedReleases, meta!.version)
Toast.notify(toastProps)
if (needUpdate) {
setShowUpdatePluginModal({ setShowUpdatePluginModal({
onSaveCallback: () => { onSaveCallback: () => {
onUpdate() onUpdate()
@ -109,27 +111,6 @@ const DetailHeader = ({
}, },
}) })
} }
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',
})
}
}
} }
const handleUpdatedFromMarketplace = () => { const handleUpdatedFromMarketplace = () => {

@ -54,9 +54,11 @@ const Action: FC<Props> = ({
const invalidateInstalledPluginList = useInvalidateInstalledPluginList() const invalidateInstalledPluginList = useInvalidateInstalledPluginList()
const handleFetchNewVersion = async () => { const handleFetchNewVersion = async () => {
try {
const fetchedReleases = await fetchReleases(author, pluginName) const fetchedReleases = await fetchReleases(author, pluginName)
if (checkForUpdates(fetchedReleases, meta!.version)) { if (fetchReleases.length === 0) return
const { needUpdate, toastProps } = checkForUpdates(fetchedReleases, meta!.version)
Toast.notify(toastProps)
if (needUpdate) {
setShowUpdatePluginModal({ setShowUpdatePluginModal({
onSaveCallback: () => { onSaveCallback: () => {
invalidateInstalledPluginList() invalidateInstalledPluginList()
@ -75,27 +77,6 @@ const Action: FC<Props> = ({
}, },
}) })
} }
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',
})
}
}
} }
const [isShowDeleteConfirm, { const [isShowDeleteConfirm, {

@ -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<Params> },
) {
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
}
}

@ -38,6 +38,7 @@
"@monaco-editor/react": "^4.6.0", "@monaco-editor/react": "^4.6.0",
"@next/mdx": "^14.0.4", "@next/mdx": "^14.0.4",
"@octokit/core": "^6.1.2", "@octokit/core": "^6.1.2",
"@octokit/request-error": "^6.1.5",
"@remixicon/react": "^4.3.0", "@remixicon/react": "^4.3.0",
"@sentry/react": "^7.54.0", "@sentry/react": "^7.54.0",
"@sentry/utils": "^7.54.0", "@sentry/utils": "^7.54.0",

@ -55,6 +55,9 @@ importers:
'@octokit/core': '@octokit/core':
specifier: ^6.1.2 specifier: ^6.1.2
version: 6.1.2 version: 6.1.2
'@octokit/request-error':
specifier: ^6.1.5
version: 6.1.5
'@remixicon/react': '@remixicon/react':
specifier: ^4.3.0 specifier: ^4.3.0
version: 4.3.0(react@18.2.0) version: 4.3.0(react@18.2.0)

Loading…
Cancel
Save