{
+ if (!version) return null;
+ const cleaned = version.trim().replace(/^v/i, '');
+ if (!cleaned) return null;
+ const parts = cleaned
+ .split(/[^0-9]+/)
+ .filter(Boolean)
+ .map((segment) => Number.parseInt(segment, 10))
+ .filter(Number.isFinite);
+ return parts.length ? parts : null;
+};
+
+const compareVersions = (latest?: string | null, current?: string | null) => {
+ const latestParts = parseVersionSegments(latest);
+ const currentParts = parseVersionSegments(current);
+ if (!latestParts || !currentParts) return null;
+ const length = Math.max(latestParts.length, currentParts.length);
+ for (let i = 0; i < length; i++) {
+ const l = latestParts[i] || 0;
+ const c = currentParts[i] || 0;
+ if (l > c) return 1;
+ if (l < c) return -1;
+ }
+ return 0;
+};
+
export function SystemPage() {
const { t, i18n } = useTranslation();
const { showNotification, showConfirmation } = useNotificationStore();
@@ -51,11 +83,15 @@ export function SystemPage() {
const modelsError = useModelsStore((state) => state.error);
const fetchModelsFromStore = useModelsStore((state) => state.fetchModels);
- const [modelStatus, setModelStatus] = useState<{ type: 'success' | 'warning' | 'error' | 'muted'; message: string }>();
+ const [modelStatus, setModelStatus] = useState<{
+ type: 'success' | 'warning' | 'error' | 'muted';
+ message: string;
+ }>();
const [requestLogModalOpen, setRequestLogModalOpen] = useState(false);
const [requestLogDraft, setRequestLogDraft] = useState(false);
const [requestLogTouched, setRequestLogTouched] = useState(false);
const [requestLogSaving, setRequestLogSaving] = useState(false);
+ const [checkingVersion, setCheckingVersion] = useState(false);
const apiKeysCache = useRef
([]);
const versionTapCount = useRef(0);
@@ -136,7 +172,7 @@ export function SystemPage() {
if (auth.connectionStatus !== 'connected') {
setModelStatus({
type: 'warning',
- message: t('notification.connection_required')
+ message: t('notification.connection_required'),
});
return;
}
@@ -158,11 +194,12 @@ export function SystemPage() {
const hasModels = list.length > 0;
setModelStatus({
type: hasModels ? 'success' : 'warning',
- message: hasModels ? t('system_info.models_count', { count: list.length }) : t('system_info.models_empty')
+ message: hasModels
+ ? t('system_info.models_count', { count: list.length })
+ : t('system_info.models_empty'),
});
} catch (err: unknown) {
- const message =
- err instanceof Error ? err.message : typeof err === 'string' ? err : '';
+ const message = err instanceof Error ? err.message : typeof err === 'string' ? err : '';
const suffix = message ? `: ${message}` : '';
const text = `${t('system_info.models_error')}${suffix}`;
setModelStatus({ type: 'error', message: text });
@@ -244,6 +281,39 @@ export function SystemPage() {
}
};
+ const handleVersionCheck = useCallback(async () => {
+ setCheckingVersion(true);
+ try {
+ const data = await versionApi.checkLatest();
+ const latestRaw = data?.['latest-version'] ?? data?.latest_version ?? data?.latest ?? '';
+ const latest = typeof latestRaw === 'string' ? latestRaw : String(latestRaw ?? '');
+ const comparison = compareVersions(latest, auth.serverVersion);
+
+ if (!latest) {
+ showNotification(t('system_info.version_check_error'), 'error');
+ return;
+ }
+
+ if (comparison === null) {
+ showNotification(t('system_info.version_current_missing'), 'warning');
+ return;
+ }
+
+ if (comparison > 0) {
+ showNotification(t('system_info.version_update_available', { version: latest }), 'warning');
+ } else {
+ showNotification(t('system_info.version_is_latest'), 'success');
+ }
+ } catch (error: unknown) {
+ const message =
+ error instanceof Error ? error.message : typeof error === 'string' ? error : '';
+ const suffix = message ? `: ${message}` : '';
+ showNotification(`${t('system_info.version_check_error')}${suffix}`, 'error');
+ } finally {
+ setCheckingVersion(false);
+ }
+ }, [auth.serverVersion, showNotification, t]);
+
useEffect(() => {
fetchConfig().catch(() => {
// ignore
@@ -290,7 +360,21 @@ export function SystemPage() {
-
{t('footer.api_version')}
+
+
{t('footer.api_version')}
+
+
{apiVersion}
@@ -366,61 +450,70 @@ export function SystemPage() {
-