mirror of
https://github.com/diced/zipline.git
synced 2025-05-10 18:05:54 +02:00
feat: version checking
This commit is contained in:
parent
485f106a65
commit
4a5d01c663
4 changed files with 252 additions and 18 deletions
|
@ -48,6 +48,7 @@ import Link from 'next/link';
|
|||
import { useRouter } from 'next/router';
|
||||
import { useState } from 'react';
|
||||
import ConfigProvider from './ConfigProvider';
|
||||
import VersionBadge from './VersionBadge';
|
||||
|
||||
type NavLinks = {
|
||||
label: string;
|
||||
|
@ -357,23 +358,27 @@ export default function Layout({ children, config }: { children: React.ReactNode
|
|||
}
|
||||
})}
|
||||
|
||||
<Divider mt='auto' />
|
||||
<div style={{ marginTop: 'auto' }}>
|
||||
<VersionBadge />
|
||||
|
||||
<ScrollArea mah='auto'>
|
||||
<Box>
|
||||
{config.website.externalLinks.map(({ name, url }, i) => (
|
||||
<NavLink
|
||||
key={i}
|
||||
label={name}
|
||||
leftSection={<IconExternalLink size='1rem' />}
|
||||
variant='light'
|
||||
component={Link}
|
||||
href={url}
|
||||
target='_blank'
|
||||
/>
|
||||
))}
|
||||
</Box>
|
||||
</ScrollArea>
|
||||
<Divider />
|
||||
|
||||
<ScrollArea mah='auto'>
|
||||
<Box>
|
||||
{config.website.externalLinks.map(({ name, url }, i) => (
|
||||
<NavLink
|
||||
key={i}
|
||||
label={name}
|
||||
leftSection={<IconExternalLink size='1rem' />}
|
||||
variant='light'
|
||||
component={Link}
|
||||
href={url}
|
||||
target='_blank'
|
||||
/>
|
||||
))}
|
||||
</Box>
|
||||
</ScrollArea>
|
||||
</div>
|
||||
</AppShell.Navbar>
|
||||
|
||||
<AppShell.Main>
|
||||
|
|
173
src/components/VersionBadge.tsx
Normal file
173
src/components/VersionBadge.tsx
Normal file
|
@ -0,0 +1,173 @@
|
|||
import useVersion from '@/lib/hooks/useVersion';
|
||||
import {
|
||||
Anchor,
|
||||
Badge,
|
||||
Button,
|
||||
Flex,
|
||||
Indicator,
|
||||
Modal,
|
||||
Paper,
|
||||
Stack,
|
||||
Text,
|
||||
Title,
|
||||
Tooltip,
|
||||
} from '@mantine/core';
|
||||
import { useDisclosure } from '@mantine/hooks';
|
||||
|
||||
function DataDisplay({ items }: { items: { label: string; value: string; href?: string }[] }) {
|
||||
return (
|
||||
<Paper withBorder p='sm'>
|
||||
<Stack gap='xs'>
|
||||
{items.map((item, index) => (
|
||||
<Flex justify='space-between' align='center' style={{ width: '100%' }} key={index}>
|
||||
<Text c='dimmed' fw='bolder' style={{ flex: 1 }}>
|
||||
{item.label}
|
||||
</Text>
|
||||
|
||||
{item.href ? (
|
||||
<Anchor href={item.href} target='_blank'>
|
||||
{item.value}
|
||||
</Anchor>
|
||||
) : (
|
||||
<Text>{item.value}</Text>
|
||||
)}
|
||||
</Flex>
|
||||
))}
|
||||
</Stack>
|
||||
</Paper>
|
||||
);
|
||||
}
|
||||
|
||||
function VersionButton({ text, children, href }: { href: string; text: string; children: React.ReactNode }) {
|
||||
return (
|
||||
<Button
|
||||
component='a'
|
||||
href={href}
|
||||
target='_blank'
|
||||
variant='filled'
|
||||
fullWidth
|
||||
color='blue'
|
||||
size='sm'
|
||||
mt='xs'
|
||||
leftSection={
|
||||
<Text size='sm' fw='bolder'>
|
||||
{text}
|
||||
</Text>
|
||||
}
|
||||
>
|
||||
{children}
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
export default function VersionBadge() {
|
||||
const { version, isLoading } = useVersion();
|
||||
const [opened, { open, close }] = useDisclosure(false);
|
||||
|
||||
if (isLoading) return null;
|
||||
if (!version) return null;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Modal title='Zipline Version' opened={opened} onClose={close} size='lg'>
|
||||
{version.isLatest && <Text>Running the latest version of Zipline.</Text>}
|
||||
{version.isUpstream && (
|
||||
<Text>
|
||||
You are running an <b>unstable</b> version of Zipline. Upstream versions are not fully tested and
|
||||
may contain bugs.
|
||||
</Text>
|
||||
)}
|
||||
{!version.isLatest && !version.isUpstream && version.isRelease && (
|
||||
<Text>
|
||||
You are running an <b>outdated</b> version of Zipline. It is recommended to update to the{' '}
|
||||
<Anchor href={version.latest?.url!}>latest version</Anchor>.
|
||||
</Text>
|
||||
)}
|
||||
|
||||
<Indicator
|
||||
processing
|
||||
position='middle-end'
|
||||
inline
|
||||
offset={-15}
|
||||
color='red'
|
||||
disabled={version.isLatest}
|
||||
>
|
||||
<Title order={3} my='sm'>
|
||||
Current Version
|
||||
</Title>
|
||||
</Indicator>
|
||||
<DataDisplay
|
||||
items={[
|
||||
{
|
||||
label: 'Version',
|
||||
value: version.version?.tag!,
|
||||
href: `https://github.com/diced/zipline/releases/${version.version?.tag}`,
|
||||
},
|
||||
{
|
||||
label: 'Commit',
|
||||
value: version.version?.sha!,
|
||||
href: `https://github.com/diced/zipline/commit/${version.version?.sha}`,
|
||||
},
|
||||
{ label: 'Upstream?', value: version.isUpstream ? 'Yes' : 'No' },
|
||||
]}
|
||||
/>
|
||||
|
||||
{!version.isLatest && version.isUpstream && (
|
||||
<>
|
||||
<Title order={3} mt='sm'>
|
||||
Latest Commit Available
|
||||
</Title>
|
||||
<Text c='dimmed' size='sm' mb='sm'>
|
||||
This is only visible when running an upstream version.
|
||||
</Text>
|
||||
|
||||
<DataDisplay
|
||||
items={[
|
||||
{
|
||||
label: 'Commit',
|
||||
value: version.latest?.commit?.sha!.slice(0, 7)!,
|
||||
href: `https://github.com/diced/zipline/commit/${version.latest?.commit?.sha}`,
|
||||
},
|
||||
{
|
||||
label: 'Available to update',
|
||||
value: version.latest?.commit?.pull ? 'Yes' : 'No',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
||||
{!version.isLatest && version.isRelease && (
|
||||
<>
|
||||
<Title order={3} mt='sm'>
|
||||
{version.latest?.tag} is available
|
||||
</Title>
|
||||
|
||||
<VersionButton text='Changelogs' href={version.latest?.url!}>
|
||||
{version.latest?.tag}
|
||||
</VersionButton>
|
||||
|
||||
<VersionButton text='Update' href='https://zipline.diced.sh/docs/get-started/docker#updating'>
|
||||
{version.latest?.tag}
|
||||
</VersionButton>
|
||||
</>
|
||||
)}
|
||||
</Modal>
|
||||
|
||||
<Tooltip label='Click to view more version information'>
|
||||
<Badge
|
||||
onClick={open}
|
||||
style={{ cursor: 'pointer', textTransform: 'unset' }}
|
||||
mx='sm'
|
||||
my='xs'
|
||||
color={version.isLatest ? 'green' : 'red'}
|
||||
variant='dot'
|
||||
size='lg'
|
||||
radius='md'
|
||||
>
|
||||
{version.version?.tag}
|
||||
</Badge>
|
||||
</Tooltip>
|
||||
</>
|
||||
);
|
||||
}
|
23
src/lib/hooks/useVersion.ts
Executable file
23
src/lib/hooks/useVersion.ts
Executable file
|
@ -0,0 +1,23 @@
|
|||
import useSWR from 'swr';
|
||||
import { Response } from '../api/response';
|
||||
const f = async () => {
|
||||
const res = await fetch('/api/version', {
|
||||
cache: 'force-cache',
|
||||
});
|
||||
if (!res.ok) throw new Error('Failed to fetch version');
|
||||
const r = await res.json();
|
||||
return r;
|
||||
};
|
||||
|
||||
export default function useVersion() {
|
||||
const { isLoading, data } = useSWR<Response['/api/version'], Error>('/api/version', f, {
|
||||
refreshInterval: undefined,
|
||||
revalidateOnFocus: false,
|
||||
revalidateIfStale: false,
|
||||
refreshWhenOffline: false,
|
||||
refreshWhenHidden: false,
|
||||
revalidateOnReconnect: false,
|
||||
});
|
||||
|
||||
return { version: data?.data, isLoading };
|
||||
}
|
|
@ -3,16 +3,49 @@ import fastifyPlugin from 'fastify-plugin';
|
|||
import { getVersion } from '@/lib/version';
|
||||
|
||||
export type ApiVersionResponse = {
|
||||
version: string;
|
||||
details: ReturnType<typeof getVersion>;
|
||||
data: VersionAPI;
|
||||
};
|
||||
|
||||
interface VersionAPI {
|
||||
isUpstream?: boolean;
|
||||
isRelease?: boolean;
|
||||
isLatest?: boolean;
|
||||
version?: {
|
||||
tag: string;
|
||||
sha: string;
|
||||
url: string;
|
||||
};
|
||||
latest?: {
|
||||
tag: string;
|
||||
url: string;
|
||||
commit?: {
|
||||
sha: string;
|
||||
url: string;
|
||||
pull: boolean;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export const PATH = '/api/version';
|
||||
export default fastifyPlugin(
|
||||
(server, _, done) => {
|
||||
server.get(PATH, { preHandler: [userMiddleware] }, async (_, res) => {
|
||||
const details = getVersion();
|
||||
const params = new URLSearchParams([['details', JSON.stringify(details)]]);
|
||||
|
||||
return res.send(details);
|
||||
const resp = await fetch(`https://zipline-version.diced.sh/?${params.toString()}`);
|
||||
|
||||
if (!resp.ok) {
|
||||
return res.internalServerError('failed to fetch version details: ' + await resp.text());
|
||||
}
|
||||
|
||||
const data: VersionAPI = await resp.json();
|
||||
|
||||
return res.send({
|
||||
data,
|
||||
details,
|
||||
});
|
||||
});
|
||||
|
||||
done();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue