mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-07-10 14:46:08 +00:00
feat(panel): surface dev-build version in UI, bot, and CLI
A dev build now shows its `dev+<commit>` identity instead of a misleading stable-looking version in the sidebar badge, dashboard card, update modal, Telegram status report, startup log, and `x-ui -v`. Adds a shared formatPanelVersion helper (single v prefix; dev labels shown verbatim) and fixes the mobile-tag double-v. Renames the version getters for clarity: config.GetVersion to GetBaseVersion (raw embedded version), config.GetReportedVersion to GetPanelVersion (advertised/displayed), and the xray process GetVersion to GetXrayVersion.
This commit is contained in:
@@ -33,6 +33,7 @@ import {
|
||||
} from '@ant-design/icons';
|
||||
|
||||
import { HttpUtil } from '@/utils';
|
||||
import { formatPanelVersion } from '@/lib/panel-version';
|
||||
import { pauseAnimationsUntilLeave, useTheme } from '@/hooks/useTheme';
|
||||
import { useAllSettings } from '@/api/queries/useAllSettings';
|
||||
import './AppSidebar.css';
|
||||
@@ -84,7 +85,7 @@ function DonateButton({ ariaLabel }: { ariaLabel: string }) {
|
||||
|
||||
function VersionBadge({ version, collapsed }: { version: string; collapsed?: boolean }) {
|
||||
if (!version) return null;
|
||||
const label = `v${version}`;
|
||||
const label = formatPanelVersion(version);
|
||||
return (
|
||||
<a
|
||||
href={REPO_URL}
|
||||
|
||||
@@ -14,6 +14,18 @@ function parseVersionParts(version: string): [number, number, number] | null {
|
||||
return [out[0], out[1], out[2]];
|
||||
}
|
||||
|
||||
// Format a panel version for display. Dev builds report a "dev+<commit>"
|
||||
// identity (see config.GetPanelVersion); show those — and any other
|
||||
// non-numeric label — verbatim. Semantic versions get a single normalized "v"
|
||||
// prefix, so a raw "v3.4.0" tag and a bare "3.4.0" both render as "v3.4.0"
|
||||
// instead of doubling up to "vv3.4.0".
|
||||
export function formatPanelVersion(version: string | undefined | null): string {
|
||||
const v = (version || '').trim();
|
||||
if (!v) return '';
|
||||
const normalized = v.replace(/^v/i, '');
|
||||
return /^\d/.test(normalized) ? `v${normalized}` : v;
|
||||
}
|
||||
|
||||
export function isPanelUpdateAvailable(latest: string, current: string): boolean {
|
||||
if (!latest || !current) return false;
|
||||
const a = parseVersionParts(latest);
|
||||
|
||||
@@ -37,6 +37,7 @@ import {
|
||||
} from '@ant-design/icons';
|
||||
|
||||
import { HttpUtil, SizeFormatter, TimeFormatter, ClipboardManager, FileManager } from '@/utils';
|
||||
import { formatPanelVersion } from '@/lib/panel-version';
|
||||
import { useTheme } from '@/hooks/useTheme';
|
||||
import { useStatusQuery } from '@/api/queries/useStatusQuery';
|
||||
import { useMediaQuery } from '@/hooks/useMediaQuery';
|
||||
@@ -104,7 +105,7 @@ export default function IndexPage() {
|
||||
}, []);
|
||||
|
||||
const displayVersion = useMemo(
|
||||
() => panelUpdateInfo.currentVersion || window.X_UI_CUR_VER || '?',
|
||||
() => window.X_UI_CUR_VER || panelUpdateInfo.currentVersion || '?',
|
||||
[panelUpdateInfo.currentVersion],
|
||||
);
|
||||
|
||||
@@ -240,10 +241,8 @@ export default function IndexPage() {
|
||||
{isMobile && displayVersion && (
|
||||
<Tag color={panelUpdateInfo.updateAvailable ? 'orange' : 'green'}>
|
||||
{panelUpdateInfo.updateAvailable
|
||||
? panelUpdateInfo.channel === 'dev'
|
||||
? panelUpdateInfo.latestVersion
|
||||
: `v${panelUpdateInfo.latestVersion}`
|
||||
: `v${displayVersion}`}
|
||||
? formatPanelVersion(panelUpdateInfo.latestVersion)
|
||||
: formatPanelVersion(displayVersion)}
|
||||
</Tag>
|
||||
)}
|
||||
</Space>
|
||||
@@ -272,8 +271,8 @@ export default function IndexPage() {
|
||||
{!isMobile && (
|
||||
<span>
|
||||
{panelUpdateInfo.updateAvailable
|
||||
? `${t('update')} ${panelUpdateInfo.latestVersion}`
|
||||
: `v${displayVersion}`}
|
||||
? `${t('update')} ${formatPanelVersion(panelUpdateInfo.latestVersion)}`
|
||||
: formatPanelVersion(displayVersion)}
|
||||
</span>
|
||||
)}
|
||||
</Space>,
|
||||
|
||||
@@ -5,6 +5,7 @@ import { CloudDownloadOutlined } from '@ant-design/icons';
|
||||
import axios from 'axios';
|
||||
|
||||
import { HttpUtil, PromiseUtil } from '@/utils';
|
||||
import { formatPanelVersion } from '@/lib/panel-version';
|
||||
import './PanelUpdateModal.css';
|
||||
|
||||
export interface PanelUpdateInfo {
|
||||
@@ -140,7 +141,7 @@ export default function PanelUpdateModal({
|
||||
{isDev ? (
|
||||
<Tag color="green">{info.currentCommit || '?'}</Tag>
|
||||
) : (
|
||||
<Tag color="green">v{info.currentVersion || '?'}</Tag>
|
||||
<Tag color="green">{formatPanelVersion(window.X_UI_CUR_VER || info.currentVersion) || '?'}</Tag>
|
||||
)}
|
||||
</div>
|
||||
{info.updateAvailable ? (
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
|
||||
import { isPanelUpdateAvailable } from '@/lib/panel-version';
|
||||
import { formatPanelVersion, isPanelUpdateAvailable } from '@/lib/panel-version';
|
||||
|
||||
// Parity with web/service/panel.go isNewerVersion.
|
||||
describe('isPanelUpdateAvailable', () => {
|
||||
@@ -31,3 +31,26 @@ describe('isPanelUpdateAvailable', () => {
|
||||
expect(isPanelUpdateAvailable('nightly-1', 'nightly-1')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('formatPanelVersion', () => {
|
||||
it('adds a single v prefix to bare semantic versions', () => {
|
||||
expect(formatPanelVersion('3.4.0')).toBe('v3.4.0');
|
||||
expect(formatPanelVersion('2.6.5')).toBe('v2.6.5');
|
||||
});
|
||||
|
||||
it('does not double up the v on already-prefixed tags', () => {
|
||||
expect(formatPanelVersion('v3.4.0')).toBe('v3.4.0');
|
||||
expect(formatPanelVersion('V3.4.0')).toBe('v3.4.0');
|
||||
});
|
||||
|
||||
it('shows dev builds verbatim without a v prefix', () => {
|
||||
expect(formatPanelVersion('dev+1a2b3c4d')).toBe('dev+1a2b3c4d');
|
||||
expect(formatPanelVersion('dev')).toBe('dev');
|
||||
});
|
||||
|
||||
it('returns empty for blank input and leaves unknown markers untouched', () => {
|
||||
expect(formatPanelVersion('')).toBe('');
|
||||
expect(formatPanelVersion(undefined)).toBe('');
|
||||
expect(formatPanelVersion('?')).toBe('?');
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user