Bump version and auto-generate sing-box HWIDs

This commit is contained in:
2026-07-08 21:03:14 +03:00
parent 42b85cc8fa
commit f6b722b22a
13 changed files with 101 additions and 281 deletions

View File

@@ -1,6 +1,6 @@
import { useEffect, useMemo, useRef, useState, type CSSProperties } from 'react';
import { open } from '@tauri-apps/plugin-dialog';
import { Cpu, FileCode2, Fingerprint, FolderOpen, Gauge, Link2, ShieldAlert, Trash2, Wand2 } from 'lucide-react';
import { Cpu, FileCode2, FolderOpen, Gauge, Link2, ShieldAlert, Trash2, Wand2 } from 'lucide-react';
import {
applyProfiles,
fetchSingBoxSubscription,
@@ -19,7 +19,6 @@ import {
pingSingBoxServer,
restartAsAdmin,
saveProfile,
saveSingBoxDeviceHwid,
saveSingBoxSubscription,
saveTarget,
selectSingBoxServer,
@@ -45,7 +44,7 @@ import { serviceControlState } from './viewModel';
type DraftItemType = Extract<ProfileItemType, 'process' | 'folder' | 'exe'>;
type ProxiFyreAction = 'start' | 'stop' | 'restart' | 'install' | 'uninstall';
type SingBoxAction = 'start' | 'stop' | 'install' | 'uninstall' | 'fetch' | 'forget' | 'identity' | 'generate' | 'ping';
type SingBoxAction = 'start' | 'stop' | 'install' | 'uninstall' | 'fetch' | 'forget' | 'generate' | 'ping';
type RouteMode = 'external' | 'local-singbox';
type ServiceVisualState = 'active' | 'settling' | null;
type PanelId = 'summary' | 'proxifyre' | 'proxy';
@@ -185,7 +184,6 @@ export function App() {
const [singBoxStatus, setSingBoxStatus] = useState<LocalSingBoxStatusResponse | null>(null);
const [singBoxSetupStatus, setSingBoxSetupStatus] = useState<SingBoxSetupStatus | null>(null);
const [subscriptionInput, setSubscriptionInput] = useState('');
const [deviceHwidInput, setDeviceHwidInput] = useState('');
const [serverPings, setServerPings] = useState<Record<string, PingServerResponse>>({});
const [proxyCheck, setProxyCheck] = useState<ProxyTargetCheckResponse | null>(null);
const [adminStatus, setAdminStatus] = useState<AdminStatusResponse | null>(null);
@@ -713,7 +711,6 @@ export function App() {
async function syncSingBoxSubscription() {
const subscriptionUrl = subscriptionInput.trim();
const deviceHwid = deviceHwidInput.trim();
if (!subscriptionUrl && !singBoxStatus?.config.hasSubscription) {
showNotice({
kind: 'error',
@@ -726,15 +723,12 @@ export function App() {
setSingBoxAction('fetch');
try {
if (subscriptionUrl) {
await saveSingBoxSubscription(subscriptionUrl, deviceHwid || undefined);
} else if (deviceHwid) {
await saveSingBoxDeviceHwid(deviceHwid);
await saveSingBoxSubscription(subscriptionUrl);
}
const status = await fetchSingBoxSubscription();
setSingBoxStatus(status);
setComponents((current) => upsertComponent(current, status.component));
setSubscriptionInput('');
setDeviceHwidInput('');
setServerPings({});
setProxyCheck(null);
showNotice({
@@ -753,64 +747,6 @@ export function App() {
}
}
async function saveSingBoxDeviceHwidData() {
const deviceHwid = deviceHwidInput.trim();
if (!deviceHwid) {
showNotice({
kind: 'error',
title: 'HWID не указан',
text: 'Вставь hard ID устройства для заголовка X-Hwid.',
});
return;
}
setSingBoxAction('identity');
try {
const status = await saveSingBoxDeviceHwid(deviceHwid);
setSingBoxStatus(status);
setComponents((current) => upsertComponent(current, status.component));
setDeviceHwidInput('');
showNotice({
kind: 'success',
title: 'HWID сохранен',
text: status.config.deviceHwidDisplay
? `Для подписки будет отправляться X-Hwid: ${status.config.deviceHwidDisplay}`
: 'Для подписки будет отправляться X-Hwid.',
});
} catch (error) {
showNotice({
kind: 'error',
title: 'HWID не сохранен',
text: errorMessage(error),
});
} finally {
setSingBoxAction(null);
}
}
async function forgetSingBoxDeviceHwidData() {
setSingBoxAction('identity');
try {
const status = await saveSingBoxDeviceHwid(undefined);
setSingBoxStatus(status);
setComponents((current) => upsertComponent(current, status.component));
setDeviceHwidInput('');
showNotice({
kind: 'info',
title: 'HWID очищен',
text: 'Заголовок X-Hwid больше не будет отправляться при загрузке подписки.',
});
} catch (error) {
showNotice({
kind: 'error',
title: 'HWID не очищен',
text: errorMessage(error),
});
} finally {
setSingBoxAction(null);
}
}
async function forgetSingBoxSubscriptionData() {
setSingBoxAction('forget');
setIsSingBoxMenuOpen(false);
@@ -823,7 +759,7 @@ export function App() {
showNotice({
kind: 'info',
title: 'Подписка очищена',
text: 'Ссылка, HWID, cache и выбранный сервер Local sing-box удалены.',
text: 'Ссылка, cache и выбранный сервер Local sing-box удалены.',
});
} catch (error) {
showNotice({
@@ -1492,38 +1428,6 @@ export function App() {
/>
</div>
<div className="subscription-line identity-line">
<span className="subscription-icon" aria-hidden="true">
<Fingerprint size={18} strokeWidth={1.9} />
</span>
<input
value={deviceHwidInput}
onChange={(event) => setDeviceHwidInput(event.target.value)}
placeholder={singBoxStatus?.config.deviceHwidDisplay ?? 'X-Hwid hard ID'}
spellCheck={false}
aria-label="Hard ID устройства для X-Hwid"
/>
<Button
type="button"
onClick={() => void saveSingBoxDeviceHwidData()}
loading={singBoxAction === 'identity'}
loadingLabel="Сохраняю"
variant="neutral"
disabled={Boolean(singBoxAction) || !deviceHwidInput.trim()}
>
{singBoxStatus?.config.hasDeviceHwid ? 'Заменить' : 'Сохранить'}
</Button>
<IconButton
type="button"
variant="danger"
onClick={() => void forgetSingBoxDeviceHwidData()}
disabled={Boolean(singBoxAction) || !singBoxStatus?.config.hasDeviceHwid}
label="Очистить hard ID устройства"
tooltip="Очистить HWID"
icon={<Trash2 size={18} strokeWidth={1.9} />}
/>
</div>
<div className="singbox-workspace-head">
<span>Серверы подписки</span>
<div className="singbox-workspace-actions">
@@ -2662,7 +2566,6 @@ function singBoxDetailLines(
`Файл: ${component?.path ?? 'не найден'}`,
`Конфиг: ${status?.generatedConfigPath ?? 'не создан'}`,
`Подписка: ${status?.config.subscriptionDisplayUrl ?? (status?.config.hasSubscription ? 'сохранена' : 'не загружена')}`,
`X-Hwid: ${status?.config.deviceHwidDisplay ?? (status?.config.hasDeviceHwid ? 'сохранен' : 'не задан')}`,
`Состав: ${setupDetails}`,
];
}