添加屏蔽协议到面板,免去手动操作

This commit is contained in:
qaq
2025-10-31 15:58:49 +08:00
parent b587193e74
commit d6feb17b86
14 changed files with 336 additions and 27 deletions
+1 -1
View File
@@ -4,7 +4,7 @@ export type SiteConfig = typeof siteConfig;
// 缓存相关常量
const CACHE_PREFIX = 'vite_config_';
const VERSION = "1.4.0";
const VERSION = "1.4.1";
const APP_VERSION = "1.0.3";
const getInitialConfig = () => {
+109 -5
View File
@@ -5,6 +5,7 @@ import { Input } from "@heroui/input";
import { Textarea } from "@heroui/input";
import { Modal, ModalContent, ModalHeader, ModalBody, ModalFooter } from "@heroui/modal";
import { Chip } from "@heroui/chip";
import { Switch } from "@heroui/switch";
import { Spinner } from "@heroui/spinner";
import { Alert } from "@heroui/alert";
import { Progress } from "@heroui/progress";
@@ -28,6 +29,9 @@ interface Node {
portSta: number;
portEnd: number;
version?: string;
http?: number; // 0 关 1 开
tls?: number; // 0 关 1 开
socks?: number; // 0 关 1 开
status: number; // 1: 在线, 0: 离线
connectionStatus: 'online' | 'offline';
systemInfo?: {
@@ -49,6 +53,9 @@ interface NodeForm {
serverIp: string;
portSta: number;
portEnd: number;
http: number; // 0 关 1 开
tls: number; // 0 关 1 开
socks: number; // 0 关 1 开
}
export default function NodePage() {
@@ -61,13 +68,18 @@ export default function NodePage() {
const [deleteModalOpen, setDeleteModalOpen] = useState(false);
const [deleteLoading, setDeleteLoading] = useState(false);
const [nodeToDelete, setNodeToDelete] = useState<Node | null>(null);
const [protocolDisabled, setProtocolDisabled] = useState(false);
const [protocolDisabledReason, setProtocolDisabledReason] = useState('');
const [form, setForm] = useState<NodeForm>({
id: null,
name: '',
ipString: '',
serverIp: '',
portSta: 1000,
portEnd: 65535
portEnd: 65535,
http: 0,
tls: 0,
socks: 0
});
const [errors, setErrors] = useState<Record<string, string>>({});
@@ -404,6 +416,8 @@ export default function NodePage() {
setIsEdit(false);
setDialogVisible(true);
resetForm();
setProtocolDisabled(true);
setProtocolDisabledReason('节点未在线,等待节点上线后再设置');
};
// 编辑节点
@@ -416,8 +430,14 @@ export default function NodePage() {
ipString: node.ip ? node.ip.split(',').map(ip => ip.trim()).join('\n') : '',
serverIp: node.serverIp || '',
portSta: node.portSta,
portEnd: node.portEnd
portEnd: node.portEnd,
http: typeof node.http === 'number' ? node.http : 1,
tls: typeof node.tls === 'number' ? node.tls : 1,
socks: typeof node.socks === 'number' ? node.socks : 1
});
const offline = node.connectionStatus !== 'online';
setProtocolDisabled(offline);
setProtocolDisabledReason(offline ? '节点未在线,等待节点上线后再设置' : '');
setDialogVisible(true);
};
@@ -514,7 +534,10 @@ export default function NodePage() {
ip: ipString,
serverIp: form.serverIp,
portSta: form.portSta,
portEnd: form.portEnd
portEnd: form.portEnd,
http: form.http,
tls: form.tls,
socks: form.socks
};
const res = await apiCall(data);
@@ -530,7 +553,10 @@ export default function NodePage() {
ip: ipString,
serverIp: form.serverIp,
portSta: form.portSta,
portEnd: form.portEnd
portEnd: form.portEnd,
http: form.http,
tls: form.tls,
socks: form.socks
} : n
));
} else {
@@ -554,7 +580,10 @@ export default function NodePage() {
ipString: '',
serverIp: '',
portSta: 1000,
portEnd: 65535
portEnd: 65535,
http: 0,
tls: 0,
socks: 0
});
setErrors({});
};
@@ -869,8 +898,83 @@ export default function NodePage() {
/>
</div>
{/* 屏蔽协议 */}
<div className="mt-1">
<div className="text-sm font-medium text-default-700">屏蔽协议</div>
<div className="text-xs text-default-500 mb-2">开启开关以屏蔽对应协议</div>
{protocolDisabled && (
<Alert
color="warning"
variant="flat"
description={protocolDisabledReason || '等待节点上线后再设置'}
className="mb-2"
/>
)}
<div className={`grid grid-cols-1 sm:grid-cols-3 gap-3 bg-default-50 dark:bg-default-100 p-3 rounded-md border border-default-200 dark:border-default-100/30 ${protocolDisabled ? 'opacity-70' : ''}`}>
{/* HTTP tile */}
<div className="px-3 py-3 rounded-lg bg-white dark:bg-default-50 border border-default-200 dark:border-default-100/30 hover:border-primary-200 transition-colors">
<div className="flex items-center gap-2 mb-2">
<svg className="w-4 h-4 text-default-500" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><rect x="2" y="4" width="20" height="16" rx="2"/><path d="M2 10h20"/></svg>
<div className="text-sm font-medium text-default-700">HTTP</div>
</div>
<div className="flex items-center justify-between">
<div className="text-xs text-default-500">禁用/启用</div>
<Switch
size="sm"
isSelected={form.http === 1}
isDisabled={protocolDisabled}
onValueChange={(v) => setForm(prev => ({ ...prev, http: v ? 1 : 0 }))}
/>
</div>
<div className="mt-1 text-xs text-default-400">{form.http === 1 ? '已开启' : '已关闭'}</div>
</div>
{/* TLS tile */}
<div className="px-3 py-3 rounded-lg bg-white dark:bg-default-50 border border-default-200 dark:border-default-100/30 hover:border-primary-200 transition-colors">
<div className="flex items-center gap-2 mb-2">
<svg className="w-4 h-4 text-default-500" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M6 10V7a6 6 0 1 1 12 0v3"/><rect x="4" y="10" width="16" height="10" rx="2"/></svg>
<div className="text-sm font-medium text-default-700">TLS</div>
</div>
<div className="flex items-center justify-between">
<div className="text-xs text-default-500">禁用/启用</div>
<Switch
size="sm"
isSelected={form.tls === 1}
isDisabled={protocolDisabled}
onValueChange={(v) => setForm(prev => ({ ...prev, tls: v ? 1 : 0 }))}
/>
</div>
<div className="mt-1 text-xs text-default-400">{form.tls === 1 ? '已开启' : '已关闭'}</div>
</div>
{/* SOCKS tile */}
<div className="px-3 py-3 rounded-lg bg-white dark:bg-default-50 border border-default-200 dark:border-default-100/30 hover:border-primary-200 transition-colors">
<div className="flex items-center gap-2 mb-2">
<svg className="w-4 h-4 text-default-500" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>
<div className="text-sm font-medium text-default-700">SOCKS</div>
</div>
<div className="flex items-center justify-between">
<div className="text-xs text-default-500">禁用/启用</div>
<Switch
size="sm"
isSelected={form.socks === 1}
isDisabled={protocolDisabled}
onValueChange={(v) => setForm(prev => ({ ...prev, socks: v ? 1 : 0 }))}
/>
</div>
<div className="mt-1 text-xs text-default-400">{form.socks === 1 ? '已开启' : '已关闭'}</div>
</div>
</div>
</div>
<Alert
color="danger"
variant="flat"
description="请不要在出口节点执行屏蔽协议,否则可能影响转发;屏蔽协议仅需在入口节点执行。"
className="mt-3"
/>
<Alert
color="primary"