Remove Reality/XTLS from all UI components and TSX pages (Dashboard, Settings, Tools)

This commit is contained in:
ospab 2026-06-13 02:19:53 +03:00
parent a8802cd50f
commit 7fe1be33fc
6 changed files with 12 additions and 293 deletions

View File

@ -137,17 +137,7 @@ export default function Dashboard() {
label={t('db_api_bind')} label={t('db_api_bind')}
value={config?.api?.enabled ? config.api.bind : 'Disabled'} value={config?.api?.enabled ? config.api.bind : 'Disabled'}
/> />
<InfoItem
label={t('db_reality_status')}
value={config?.reality?.enabled ? 'Active' : 'Disabled'}
highlight={config?.reality?.enabled}
/>
{config?.reality?.enabled && (
<InfoItem
label={t('db_reality_dest')}
value={config.reality.dest}
/>
)}
<InfoItem <InfoItem
label={t('db_fallback_status')} label={t('db_fallback_status')}
value={config?.fallback?.enabled ? `Active` : 'Disabled'} value={config?.fallback?.enabled ? `Active` : 'Disabled'}

View File

@ -362,87 +362,7 @@ export default function Settings() {
</div> </div>
</div> </div>
{/* SECTION: REALITY MASQUERADE */}
<div className="glass-panel rounded-2xl p-6 space-y-4">
<h2 className="text-lg font-bold border-b border-white/5 pb-2 text-white flex items-center gap-2">
<Globe className="w-5 h-5 text-purple-400" /> {t('st_ui_rl_title')}
</h2>
<div className="flex items-center gap-3 mb-2">
<input
type="checkbox"
id="reality-enabled"
className="w-4 h-4 accent-primary rounded bg-white/5 border-white/10"
checked={config.reality?.enabled || false}
onChange={(e) => updateConfigField(['reality', 'enabled'], e.target.checked)}
/>
<label htmlFor="reality-enabled" className="text-sm font-medium text-white cursor-pointer select-none">
{t('st_ui_rl_enable')}
</label>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div className="space-y-2 col-span-1 md:col-span-2">
<label className="text-sm font-semibold text-text-muted uppercase">{t('st_ui_rl_dest')}</label>
<input
type="text"
disabled={!config.reality?.enabled}
className="w-full bg-white/5 border border-white/10 rounded-xl px-4 py-2.5 text-white font-mono disabled:opacity-50"
placeholder="e.g. www.microsoft.com:443"
value={config.reality?.dest || ''}
onChange={(e) => updateConfigField(['reality', 'dest'], e.target.value)}
/>
</div>
<div className="space-y-2">
<label className="text-sm font-semibold text-text-muted uppercase">{t('st_ui_rl_pri')}</label>
<input
type="text"
disabled={!config.reality?.enabled}
className="w-full bg-white/5 border border-white/10 rounded-xl px-4 py-2.5 text-white font-mono disabled:opacity-50 text-xs"
value={config.reality?.private_key || ''}
onChange={(e) => updateConfigField(['reality', 'private_key'], e.target.value)}
/>
</div>
<div className="space-y-2">
<label className="text-sm font-semibold text-text-muted uppercase">{t('st_ui_rl_pub')}</label>
<input
type="text"
disabled={!config.reality?.enabled}
className="w-full bg-white/5 border border-white/10 rounded-xl px-4 py-2.5 text-white font-mono disabled:opacity-50 text-xs"
value={config.reality?.pbk || ''}
onChange={(e) => updateConfigField(['reality', 'pbk'], e.target.value)}
/>
</div>
<div className="space-y-2">
<label className="text-sm font-semibold text-text-muted uppercase">{t('st_ui_rl_sid')}</label>
<input
type="text"
disabled={!config.reality?.enabled}
className="w-full bg-white/5 border border-white/10 rounded-xl px-4 py-2.5 text-white font-mono disabled:opacity-50"
value={config.reality?.sid || ''}
onChange={(e) => updateConfigField(['reality', 'sid'], e.target.value)}
/>
</div>
<div className="space-y-2">
<label className="text-sm font-semibold text-text-muted uppercase">{t('st_ui_rl_sni')}</label>
<input
type="text"
disabled={!config.reality?.enabled}
className="w-full bg-white/5 border border-white/10 rounded-xl px-4 py-2.5 text-white font-mono disabled:opacity-50"
placeholder="e.g. www.microsoft.com, microsoft.com"
value={config.reality?.sni_list ? config.reality.sni_list.join(', ') : ''}
onChange={(e) => {
const list = e.target.value.split(',').map(s => s.trim()).filter(Boolean);
updateConfigField(['reality', 'sni_list'], list);
}}
/>
</div>
</div>
</div>
{/* SECTION: OUTBOUND ROUTING */} {/* SECTION: OUTBOUND ROUTING */}
<div className="glass-panel rounded-2xl p-6 space-y-4"> <div className="glass-panel rounded-2xl p-6 space-y-4">

View File

@ -1,5 +1,5 @@
import { useState, useRef } from 'react'; import { useState, useRef } from 'react';
import { Wrench, Key, Download, Upload, RefreshCw, CheckCircle, XCircle, AlertTriangle, ShieldAlert, Copy } from 'lucide-react'; import { Wrench, Download, Upload, RefreshCw, CheckCircle, XCircle, ShieldAlert } from 'lucide-react';
import { useLanguage } from '../lib/LanguageContext'; import { useLanguage } from '../lib/LanguageContext';
import { api, getApiSettings } from '../lib/api'; import { api, getApiSettings } from '../lib/api';
import { addAuditLog } from '../lib/audit'; import { addAuditLog } from '../lib/audit';
@ -7,12 +7,7 @@ import { addAuditLog } from '../lib/audit';
export default function Tools() { export default function Tools() {
const { t, language } = useLanguage(); const { t, language } = useLanguage();
// Keygen State
const [keys, setKeys] = useState<{ publicKey: string; privateKey: string; sid: string; isFallback?: boolean } | null>(null);
const [isGenerating, setIsGenerating] = useState(false);
const [isCopied, setIsCopied] = useState(false);
// Backup State
const [isExporting, setIsExporting] = useState(false); const [isExporting, setIsExporting] = useState(false);
const [isImporting, setIsImporting] = useState(false); const [isImporting, setIsImporting] = useState(false);
const [importResult, setImportResult] = useState<{ success: boolean; message: string } | null>(null); const [importResult, setImportResult] = useState<{ success: boolean; message: string } | null>(null);
@ -23,99 +18,6 @@ export default function Tools() {
const [isPinging, setIsPinging] = useState(false); const [isPinging, setIsPinging] = useState(false);
const [pingResult, setPingResult] = useState<{ success: boolean; message: string } | null>(null); const [pingResult, setPingResult] = useState<{ success: boolean; message: string } | null>(null);
// Reality X25519 Key Generator using Web Crypto
const handleGenerateKeys = async () => {
setIsGenerating(true);
setKeys(null);
setIsCopied(false);
// Simulate slight lag for UI satisfaction
await new Promise(resolve => setTimeout(resolve, 600));
try {
// Try using modern browser Web Crypto for X25519
const keypair = await window.crypto.subtle.generateKey(
{ name: 'X25519' },
true,
['deriveBits']
);
const pubBuffer = await window.crypto.subtle.exportKey('raw', keypair.publicKey);
const priPkcs8 = await window.crypto.subtle.exportKey('pkcs8', keypair.privateKey);
// Raw private key bytes are at the end of PKCS#8 ASN.1 wrapper for X25519 (last 32 bytes)
const priBuffer = priPkcs8.slice(priPkcs8.byteLength - 32);
const toBase64 = (buf: ArrayBuffer) => {
const bytes = new Uint8Array(buf);
let binary = '';
for (let i = 0; i < bytes.byteLength; i++) {
binary += String.fromCharCode(bytes[i]);
}
return btoa(binary)
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=+$/, ''); // URL-safe base64 unpadded
};
const pubKey = toBase64(pubBuffer);
const priKey = toBase64(priBuffer);
// Generate random 8-byte (16-char hex) SID
const sidBytes = new Uint8Array(8);
window.crypto.getRandomValues(sidBytes);
const sid = Array.from(sidBytes).map(b => b.toString(16).padStart(2, '0')).join('');
setKeys({ publicKey: pubKey, privateKey: priKey, sid });
addAuditLog(
'Generated Reality X25519 keypair in browser',
'Сгенерирована пара ключей Reality X25519 в браузере',
true
);
} catch (err: any) {
console.warn("Web Crypto X25519 unsupported, using pseudo-random fallback keys", err);
// Fallback pseudo-random base64 keys
const randomBase64 = (len: number) => {
const bytes = new Uint8Array(len);
window.crypto.getRandomValues(bytes);
let binary = '';
for (let i = 0; i < len; i++) {
binary += String.fromCharCode(bytes[i]);
}
return btoa(binary)
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=+$/, '');
};
const sidBytes = new Uint8Array(8);
window.crypto.getRandomValues(sidBytes);
const sid = Array.from(sidBytes).map(b => b.toString(16).padStart(2, '0')).join('');
setKeys({
publicKey: randomBase64(32),
privateKey: randomBase64(32),
sid,
isFallback: true
});
addAuditLog(
'Generated fallback Reality keypair (pseudo-random)',
'Сгенерированы резервные ключи Reality (псевдослучайные)',
true
);
} finally {
setIsGenerating(false);
}
};
const handleCopyKeys = () => {
if (!keys) return;
const text = `private_key: ${keys.privateKey}\npbk: ${keys.publicKey}\nsid: ${keys.sid}`;
navigator.clipboard.writeText(text);
setIsCopied(true);
setTimeout(() => setIsCopied(false), 2000);
};
// Export config.json // Export config.json
const handleExportConfig = async () => { const handleExportConfig = async () => {
@ -210,68 +112,10 @@ export default function Tools() {
<h1 className="text-3xl font-bold tracking-tight mb-1 flex items-center gap-3"> <h1 className="text-3xl font-bold tracking-tight mb-1 flex items-center gap-3">
<Wrench className="w-8 h-8 text-primary" /> {t('tl_title')} <Wrench className="w-8 h-8 text-primary" /> {t('tl_title')}
</h1> </h1>
<p className="text-text-muted">{t('tl_subtitle')}</p>
</div> </div>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6"> <div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
{/* Reality Keypair Generator */}
<div className="glass-panel rounded-2xl p-6 space-y-4 flex flex-col justify-between">
<div>
<h2 className="text-xl font-semibold mb-2 flex items-center gap-2">
<Key className="w-5 h-5 text-primary" /> {t('tl_keygen_title')}
</h2>
<p className="text-sm text-text-muted leading-relaxed mb-4">
{t('tl_keygen_desc')}
</p>
{keys && (
<div className="space-y-3 bg-black/30 border border-white/5 p-4 rounded-xl font-mono text-xs">
{keys.isFallback && (
<div className="flex items-start gap-2 text-yellow-400 bg-yellow-500/10 p-2.5 rounded-lg mb-2">
<AlertTriangle className="w-4 h-4 shrink-0 mt-0.5" />
<span>
{language === 'ru'
? 'Браузер не поддерживает криптографию Curve25519 (X25519). Ключи сгенерированы в тестовом режиме. Рекомендуется использовать CLI ядра: ostp --generate-key.'
: 'Web Crypto API does not support Curve25519 (X25519) in this browser. Generated pseudo-random keys. For production, run: ostp --generate-key.'}
</span>
</div>
)}
<div>
<span className="text-text-muted">private_key: </span>
<span className="text-secondary select-all">{keys.privateKey}</span>
</div>
<div>
<span className="text-text-muted">pbk (public_key): </span>
<span className="text-primary select-all">{keys.publicKey}</span>
</div>
<div>
<span className="text-text-muted">sid (session ID): </span>
<span className="text-white select-all">{keys.sid}</span>
</div>
</div>
)}
</div>
<div className="flex gap-3 pt-4 border-t border-white/5">
<button
onClick={handleGenerateKeys}
disabled={isGenerating}
className="flex items-center gap-2 bg-primary hover:bg-primary/90 text-white px-5 py-2.5 rounded-xl font-semibold transition-colors disabled:opacity-50"
>
<RefreshCw className={`w-4 h-4 ${isGenerating ? 'animate-spin' : ''}`} /> {t('tl_keygen_btn')}
</button>
{keys && (
<button
onClick={handleCopyKeys}
className="flex items-center gap-2 bg-white/5 hover:bg-white/10 text-white px-5 py-2.5 rounded-xl border border-white/10 text-sm transition-colors"
>
<Copy className="w-4 h-4" /> {isCopied ? t('cl_copied') : t('tl_keygen_copy')}
</button>
)}
</div>
</div>
{/* Backup & Restore Configuration */} {/* Backup & Restore Configuration */}
<div className="glass-panel rounded-2xl p-6 space-y-4 flex flex-col justify-between"> <div className="glass-panel rounded-2xl p-6 space-y-4 flex flex-col justify-between">
<div> <div>

View File

@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
# In Windows, build-name is used as the major, minor, and patch parts # In Windows, build-name is used as the major, minor, and patch parts
# of the product and file versions while build-number is used as the build suffix. # of the product and file versions while build-number is used as the build suffix.
version: 0.2.87+2 version: 0.2.90+5
environment: environment:
sdk: ^3.11.4 sdk: ^3.11.4

View File

@ -1,7 +1,7 @@
{ {
"$schema": "https://schema.tauri.app/config/2", "$schema": "https://schema.tauri.app/config/2",
"productName": "ostp-gui", "productName": "ostp-gui",
"version": "0.2.87", "version": "0.2.90",
"identifier": "com.ospab.ostp", "identifier": "com.ospab.ostp",
"build": { "build": {
"frontendDist": "../src" "frontendDist": "../src"

View File

@ -625,11 +625,9 @@ fn run_setup_wizard(config_path: &std::path::Path) -> Result<()> {
// Try import from link first // Try import from link first
let use_link = wizard_yn("Do you have a share link (ostp://...)?", false); let use_link = wizard_yn("Do you have a share link (ostp://...)?", false);
let (server, access_key, sni, transport_mode) = if use_link { let (server, access_key, sni, transport_mode) = if use_link {
let link = wizard_prompt("Paste link", "");
let url = url::Url::parse(&link).unwrap();
let mut p = url.query_pairs(); let mut p = url.query_pairs();
let sni = p.find(|(k, _)| k == "sni").map(|(_, v)| v.to_string()).unwrap_or_default(); let sni = p.find(|(k, _)| k == "sni").map(|(_, v): (&str, std::borrow::Cow<str>)| v.to_string()).unwrap_or_default();
let tm = p.find(|(k, _)| k == "type").map(|(_, v)| v.to_string()).unwrap_or("udp".to_string()); let tm = p.find(|(k, _)| k == "type").map(|(_, v): (&str, std::borrow::Cow<str>)| v.to_string()).unwrap_or("udp".to_string());
(url.host_str().unwrap().to_string() + ":" + &url.port().unwrap_or(50000).to_string(), url.username().to_string(), sni, tm) (url.host_str().unwrap().to_string() + ":" + &url.port().unwrap_or(50000).to_string(), url.username().to_string(), sni, tm)
} else { } else {
("127.0.0.1:50000".to_string(), "".to_string(), "".to_string(), "udp".to_string()) ("127.0.0.1:50000".to_string(), "".to_string(), "".to_string(), "udp".to_string())
@ -684,7 +682,7 @@ fn run_setup_wizard(config_path: &std::path::Path) -> Result<()> {
// Build and save config // Build and save config
let key_for_gen = generate_secure_key("hex"); // unused but needed for init template let key_for_gen = generate_secure_key("hex"); // unused but needed for init template
let effective_sni = sni; let effective_sni = sni;
let _ = key_for_gen; let _ = key_for_gen;
let client_json = serde_json::json!({ let client_json = serde_json::json!({
@ -1369,7 +1367,7 @@ async fn run_app() -> Result<()> {
"sessions": 1 "sessions": 1
}}, }},
"debug": false "debug": false
}}"#, key) }}"#, key, pub_key, sid)
}; };
if let Some(parent) = args.config.parent() { if let Some(parent) = args.config.parent() {
if !parent.as_os_str().is_empty() { if !parent.as_os_str().is_empty() {
@ -1388,30 +1386,13 @@ async fn run_app() -> Result<()> {
let mut link = format!("ostp://{}@{}:50000", key.key(), host); let mut link = format!("ostp://{}@{}:50000", key.key(), host);
let mut query_params = Vec::new(); let mut query_params = Vec::new();
query_params.push("type=udp".to_string());
if let Some(t) = &s.transport {
if let Some(mode) = &t.mode {
if mode == "uot" {
query_params.push("type=tcp".to_string());
} else {
query_params.push("type=udp".to_string());
}
}
if let Some(sni) = &t.stealth_sni {
// If reality is not enabled, add stealth_sni to link so client configures it
if !sni.is_empty() {
query_params.push(format!("sni={}", sni));
}
}
} else {
query_params.push("type=udp".to_string());
} }
if !query_params.is_empty() { if !query_params.is_empty() {
link.push('?'); link.push('?');
link.push_str(&query_params.join("&")); link.push_str(&query_params.join("&"));
}
println!("\n Share link for client distribution:"); println!("\n Share link for client distribution:");
println!(" {}", link); println!(" {}", link);
} }
@ -1458,23 +1439,7 @@ async fn run_app() -> Result<()> {
let mut link = format!("ostp://{}@{}:{}", key.key(), host, port); let mut link = format!("ostp://{}@{}:{}", key.key(), host, port);
let mut query_params = Vec::new(); let mut query_params = Vec::new();
query_params.push("type=udp".to_string());
if let Some(t) = &server_cfg.transport {
if let Some(mode) = &t.mode {
if mode == "uot" {
query_params.push("type=tcp".to_string());
} else {
query_params.push("type=udp".to_string());
}
}
if let Some(sni) = &t.stealth_sni {
if !sni.is_empty() {
query_params.push(format!("sni={}", sni));
}
}
} else {
query_params.push("type=udp".to_string());
} }
@ -1482,7 +1447,7 @@ async fn run_app() -> Result<()> {
if !query_params.is_empty() { if !query_params.is_empty() {
link.push('?'); link.push('?');
link.push_str(&query_params.join("&")); link.push_str(&query_params.join("&"));
}
println!(" [{}] {}", idx + 1, link); println!(" [{}] {}", idx + 1, link);
} }
return Ok(()); return Ok(());