feat(config): check for frontend version update

This commit is contained in:
kunish 2024-10-10 23:42:40 +08:00
parent 7c310e7c66
commit 657c180e05
No known key found for this signature in database
GPG Key ID: 647A12B4F782C430
3 changed files with 67 additions and 47 deletions

View File

@ -248,37 +248,42 @@ export const updateRuleProviderAPI = (providerName: string) => {
} }
type ReleaseAPIResponse = { type ReleaseAPIResponse = {
tag_name: string
assets: { name: string }[] assets: { name: string }[]
} }
export const isUpdateAvailableAPI = async (versionResponse: string) => { export const isFrontendUpdateAvailableAPI = async (currentVersion: string) => {
const repositoryURL = 'https://api.github.com/repos/MetaCubeX/metacubexd'
const { tag_name } = await ky
.get(`${repositoryURL}/releases/latest`)
.json<ReleaseAPIResponse>()
return tag_name !== currentVersion
}
export const isBackendUpdateAvailableAPI = async (currentVersion: string) => {
const repositoryURL = 'https://api.github.com/repos/MetaCubeX/mihomo' const repositoryURL = 'https://api.github.com/repos/MetaCubeX/mihomo'
const match = /(alpha|beta|meta)-?(\w+)/.exec(versionResponse) const match = /(alpha|beta|meta)-?(\w+)/.exec(currentVersion)
if (!match) return false if (!match) return false
const check = async (url: string) => {
const { assets } = await ky
.get(`${repositoryURL}${url}`)
.json<ReleaseAPIResponse>()
const alreadyLatest = assets.some(({ name }) => name.includes(version))
return !alreadyLatest
}
const channel = match[1], const channel = match[1],
version = match[2] version = match[2]
if (channel === 'meta') { if (channel === 'meta') return await check('/releases/latest')
const { assets } = await ky
.get(`${repositoryURL}/releases/latest`)
.json<ReleaseAPIResponse>()
const alreadyLatest = assets.some(({ name }) => name.includes(version)) if (channel === 'alpha') return await check('/releases/tags/Prerelease-Alpha')
return !alreadyLatest
}
if (channel === 'alpha') {
const { assets } = await ky
.get(`${repositoryURL}/releases/tags/Prerelease-Alpha`)
.json<ReleaseAPIResponse>()
const alreadyLatest = assets.some(({ name }) => name.includes(version))
return !alreadyLatest
}
return false return false
} }

View File

@ -8,7 +8,8 @@ import {
fetchBackendVersionAPI, fetchBackendVersionAPI,
flushFakeIPDataAPI, flushFakeIPDataAPI,
flushingFakeIPData, flushingFakeIPData,
isUpdateAvailableAPI, isBackendUpdateAvailableAPI,
isFrontendUpdateAvailableAPI,
reloadConfigFileAPI, reloadConfigFileAPI,
reloadingConfigFile, reloadingConfigFile,
restartBackendAPI, restartBackendAPI,
@ -494,29 +495,37 @@ const ConfigForXd = () => {
) )
} }
const Versions: Component<{ backendVersion: Accessor<string> }> = ({ const Versions: Component<{
backendVersion, frontendVersion: string
}) => { backendVersion: Accessor<string>
const [isUpdateAvailable, setIsUpdateAvailable] = createSignal(false) }> = ({ frontendVersion, backendVersion }) => {
const [isFrontendUpdateAvailable] = createResource(() =>
isFrontendUpdateAvailableAPI(frontendVersion),
)
const [isBackendUpdateAvailable] = createResource(() =>
isBackendUpdateAvailableAPI(backendVersion()),
)
createEffect(async () => { const UpdateAvailableIndicator = () => (
const version = backendVersion() <span class="absolute -right-1 -top-1 flex h-3 w-3">
<span class="absolute inline-flex h-full w-full animate-ping rounded-full bg-info opacity-75" />
if (!version) return <span class="inline-flex h-3 w-3 rounded-full bg-info" />
</span>
setIsUpdateAvailable(await isUpdateAvailableAPI(version)) )
})
return ( return (
<div class="grid grid-cols-2 gap-4"> <div class="grid grid-cols-2 gap-4">
<kbd class="kbd">{import.meta.env.APP_VERSION}</kbd> <div class="relative">
<Show when={isFrontendUpdateAvailable()}>
<UpdateAvailableIndicator />
</Show>
<kbd class="kbd w-full">{import.meta.env.APP_VERSION}</kbd>
</div>
<div class="relative"> <div class="relative">
<Show when={isUpdateAvailable()}> <Show when={isBackendUpdateAvailable()}>
<span class="absolute -right-1 -top-1 flex h-3 w-3"> <UpdateAvailableIndicator />
<span class="absolute inline-flex h-full w-full animate-ping rounded-full bg-info opacity-75" />
<span class="inline-flex h-3 w-3 rounded-full bg-info" />
</span>
</Show> </Show>
<kbd class="kbd w-full">{backendVersion()}</kbd> <kbd class="kbd w-full">{backendVersion()}</kbd>
@ -536,13 +545,15 @@ export default () => {
const [t] = useI18n() const [t] = useI18n()
const [backendVersion, setBackendVersion] = createSignal('') const frontendVersion = `v${import.meta.env.APP_VERSION}`
const isSingBox = createMemo(() => backendVersion().includes('sing-box')) const [backendVersion] = createResource(fetchBackendVersionAPI, {
initialValue: '',
onMount(() => {
fetchBackendVersionAPI().then(setBackendVersion)
}) })
const isSingBox = createMemo(
() => backendVersion()?.includes('sing-box') || false,
)
return ( return (
<> <>
<DocumentTitle>{t('config')}</DocumentTitle> <DocumentTitle>{t('config')}</DocumentTitle>
@ -564,7 +575,12 @@ export default () => {
<ConfigTitle withDivider>{t('version')}</ConfigTitle> <ConfigTitle withDivider>{t('version')}</ConfigTitle>
<Versions backendVersion={backendVersion} /> <Show when={!backendVersion.loading}>
<Versions
frontendVersion={frontendVersion}
backendVersion={backendVersion}
/>
</Show>
</div> </div>
</> </>
) )

View File

@ -2,6 +2,7 @@ import AutoImport from 'unplugin-auto-import/vite'
import { defineConfig } from 'vite' import { defineConfig } from 'vite'
import { VitePWA } from 'vite-plugin-pwa' import { VitePWA } from 'vite-plugin-pwa'
import solidPlugin from 'vite-plugin-solid' import solidPlugin from 'vite-plugin-solid'
import { version } from './package.json'
export default defineConfig({ export default defineConfig({
base: './', base: './',
@ -9,9 +10,7 @@ export default defineConfig({
resolve: { alias: { '~': '/src' } }, resolve: { alias: { '~': '/src' } },
define: { define: {
'import.meta.env.APP_VERSION': JSON.stringify( 'import.meta.env.APP_VERSION': JSON.stringify(version),
process.env.npm_package_version,
),
}, },
plugins: [ plugins: [