A:基础提交

This commit is contained in:
jimmy committed 2025-07-30 18:59:58 +08:00
1 parent 0ab7a2bca4
commit 795acf9b5f
7 files changed
+480 -666

No files matched your search

+4 -2
View File
@@ -6,7 +6,7 @@ import type { subRoute, subRouteList } from '@/types/devIndex'
import app from '@/constants/app'
import { ref, onMounted, provide } from 'vue'
import { NButton } from 'naive-ui'
import { NConfigProvider, NGlobalStyle } from 'naive-ui'
import { NConfigProvider, NGlobalStyle, NMessageProvider, NLoadingBarProvider } from 'naive-ui'
import { useThemeStore } from '@/stores/theme'
import NetworkConnectionPanel1 from '@/components/NetworkConnectionPanel1.vue'
import MainLayout from './layouts/MainLayout.vue'
@@ -146,7 +146,8 @@ const getLayout = (route) => {
:theme="themeStore.theme"
>
<n-message-provider>
<n-global-style />
<n-loading-bar-provider>
<n-global-style />
<!-- 可拖动的导航栏 -->
<div
@@ -187,6 +188,7 @@ const getLayout = (route) => {
<!-- <UnifiedConnectionsList4 />-->
<!-- </div>-->
<!-- </div>-->
</n-loading-bar-provider>
</n-message-provider>
</n-config-provider>
</template>
-208
View File
@@ -1,208 +0,0 @@
/**
* 防火墙API服务
*/
import { httpClient } from '@/utils/http'
import type { ApiResponse } from '@/types/http'
import type {
FirewallRule,
CreateRuleRequest,
UpdateRuleRequest,
RuleFilter,
FirewallStatus,
FirewallStatistics,
FirewallRulesResponse,
DisplayRule,
} from '@/types/firewall'
import { RuleDirection, RuleAction, ProtocolType } from '@/types/firewall'
class FirewallApiService {
private readonly baseUrl = '/firewall'
/**
* 获取防火墙规则列表(支持分页和筛选)
*/
async getRules(filter?: RuleFilter): Promise<FirewallRulesResponse> {
const params: Record<string, string> = {}
if (filter) {
if (filter.name) params.name = filter.name
if (filter.direction) params.direction = filter.direction.toString()
if (typeof filter.enabled === 'boolean') params.enabled = filter.enabled.toString()
if (filter.protocol) params.protocol = filter.protocol.toString()
if (filter.action) params.action = filter.action.toString()
if (filter.application) params.application = filter.application
if (filter.port) params.port = filter.port
if (typeof filter.start === 'number') params.start = filter.start.toString()
if (typeof filter.limit === 'number') params.limit = filter.limit.toString()
}
const response = await httpClient.get<FirewallRulesResponse>(`${this.baseUrl}/rules`, params)
return response.data!
}
/**
* 创建防火墙规则
*/
async createRule(rule: CreateRuleRequest): Promise<void> {
await httpClient.post(`${this.baseUrl}/rules`, rule)
}
/**
* 更新防火墙规则
*/
async updateRule(rule: UpdateRuleRequest): Promise<void> {
await httpClient.put(`${this.baseUrl}/rules`, rule)
}
/**
* 删除防火墙规则
*/
async deleteRule(ruleName: string): Promise<void> {
await httpClient.delete(`${this.baseUrl}/rules`, { name: ruleName })
}
/**
* 获取防火墙状态
*/
async getStatus(): Promise<FirewallStatus> {
const response = await httpClient.get<FirewallStatus>(`${this.baseUrl}/status`)
return response.data!
}
/**
* 获取防火墙统计信息
*/
async getStatistics(): Promise<FirewallStatistics> {
const response = await httpClient.get<FirewallStatistics>(`${this.baseUrl}/statistics`)
return response.data!
}
/**
* 转换后端规则数据为前端显示格式
*/
convertToDisplayRule(rule: FirewallRule): DisplayRule {
// 将Profile枚举转换为字符串数组
const profiles: string[] = []
if ((rule.profiles & 1) === 1) profiles.push('域') // Domain
if ((rule.profiles & 2) === 2) profiles.push('专用') // Private
if ((rule.profiles & 4) === 4) profiles.push('公用') // Public
let direction
if (rule.direction === null || rule.direction === undefined) {
direction = RuleDirection.None
} else {
direction = rule.direction === RuleDirection.Inbound ? 'inbound' : 'outbound'
}
return {
id: rule.uniqueId || rule.name, // 优先使用后端提供的uniqueId,降级为name
name: rule.name,
description: rule.description,
enabled: rule.enabled,
direction: direction,
action: rule.action === RuleAction.Allow ? 'allow' : 'block',
program: rule.applicationName || rule.serviceName || '任意',
protocol: this.getProtocolDisplayName(rule.protocol),
port: rule.localPorts || rule.remotePorts || '任意',
profiles,
}
}
/**
* 转换前端显示规则为后端创建请求格式
*/
convertToCreateRequest(displayRule: DisplayRule): CreateRuleRequest {
// 将配置文件字符串数组转换为枚举值
let profiles = 0
displayRule.profiles.forEach((profile) => {
switch (profile) {
case '域':
profiles |= 1
break // Domain
case '专用':
profiles |= 2
break // Private
case '公用':
profiles |= 4
break // Public
}
})
return {
name: displayRule.name,
description: displayRule.description,
direction:
displayRule.direction === 'inbound' ? RuleDirection.Inbound : RuleDirection.Outbound,
action: displayRule.action === 'allow' ? RuleAction.Allow : RuleAction.Block,
enabled: displayRule.enabled,
applicationName: displayRule.program !== '任意' ? displayRule.program : '',
protocol: this.getProtocolTypeFromName(displayRule.protocol),
localPorts: displayRule.port !== '任意' ? displayRule.port : '',
profiles: profiles || 7, // 默认All (Domain | Private | Public)
localAddresses: '*',
remoteAddresses: '*',
}
}
/**
* 获取协议显示名称
*/
private getProtocolDisplayName(protocol: ProtocolType): string {
switch (protocol) {
case ProtocolType.TCP:
return 'TCP'
case ProtocolType.UDP:
return 'UDP'
case ProtocolType.ICMPV4:
return 'ICMP'
case ProtocolType.ICMPv6:
return 'ICMPv6'
case ProtocolType.Any:
return '任意'
default:
return '其他'
}
}
/**
* 从显示名称获取协议类型枚举
*/
private getProtocolTypeFromName(protocolName: string): ProtocolType {
switch (protocolName.toUpperCase()) {
case 'TCP':
return ProtocolType.TCP
case 'UDP':
return ProtocolType.UDP
case 'ICMP':
return ProtocolType.ICMPV4
case 'ICMPV6':
return ProtocolType.ICMPv6
case '任意':
return ProtocolType.Any
default:
return ProtocolType.Any
}
}
/**
* 批量操作规则(启用/禁用/删除)
*/
async batchUpdateRules(
ruleNames: string[],
operation: 'enable' | 'disable' | 'delete',
): Promise<void> {
const promises = ruleNames.map(async (name) => {
switch (operation) {
case 'enable':
return this.updateRule({ currentName: name, enabled: true })
case 'disable':
return this.updateRule({ currentName: name, enabled: false })
case 'delete':
return this.deleteRule(name)
}
})
await Promise.all(promises)
}
}
export const firewallApi = new FirewallApiService()
@@ -76,16 +76,16 @@
<n-form-item path="direction" label="规则方向" required>
<n-radio-group v-model:value="formData.direction">
<n-radio :value="1">入站 (Inbound)</n-radio>
<n-radio :value="2">出站 (Outbound)</n-radio>
<n-radio value="inbound">入站 (Inbound)</n-radio>
<n-radio value="outbound">出站 (Outbound)</n-radio>
</n-radio-group>
</n-form-item>
</div>
<n-form-item path="action" label="规则动作" required>
<n-radio-group v-model:value="formData.action">
<n-radio :value="1">允许 (Allow)</n-radio>
<n-radio :value="0">阻止 (Block)</n-radio>
<n-radio value="allow">允许 (Allow)</n-radio>
<n-radio value="block">阻止 (Block)</n-radio>
</n-radio-group>
</n-form-item>
</div>
@@ -102,10 +102,10 @@
应用程序路径
</div>
<div class="form-grid">
<n-form-item path="applicationName" label="应用程序">
<n-form-item path="program" label="应用程序">
<n-input
v-model:value="formData.applicationName"
placeholder="例: C:\Program Files\Example\app.exe (留空表示所有程序)"
v-model:value="formData.program"
placeholder="例: C:\Program Files\Example\app.exe (留空或'任意'表示所有程序)"
>
<template #suffix>
<n-button text @click="browseApplication">
@@ -114,16 +114,6 @@
</template>
</n-input>
</n-form-item>
<n-form-item path="serviceName" label="系统服务">
<n-select
v-model:value="formData.serviceName"
:options="serviceOptions"
placeholder="选择系统服务 (可选)"
filterable
clearable
/>
</n-form-item>
</div>
</div>
</div>
@@ -146,30 +136,10 @@
/>
</n-form-item>
<div class="form-row">
<n-form-item path="localPorts" label="本地端口">
<n-input
v-model:value="formData.localPorts"
placeholder="例: 80, 443, 8080-8090 (留空表示所有端口)"
/>
</n-form-item>
<n-form-item path="remotePorts" label="远程端口">
<n-input
v-model:value="formData.remotePorts"
placeholder="例: 80, 443, 8080-8090 (留空表示所有端口)"
/>
</n-form-item>
</div>
<n-form-item
v-if="formData.protocol === 1 || formData.protocol === 58"
path="icmpTypesAndCodes"
label="ICMP 类型和代码"
>
<n-form-item path="port" label="端口">
<n-input
v-model:value="formData.icmpTypesAndCodes"
placeholder="例: 8:0 (留空表示所有ICMP类型)"
v-model:value="formData.port"
placeholder="例: 80, 443, 8080-8090 (留空或'任意'表示所有端口)"
/>
</n-form-item>
</div>
@@ -177,36 +147,7 @@
</div>
</n-tab-pane>
<!-- 地址设置 -->
<n-tab-pane name="address" tab="地址设置">
<div class="tab-content">
<div class="form-section">
<div class="section-title">
<n-icon :component="GlobeOutline" />
IP 地址范围
</div>
<div class="form-grid">
<n-form-item path="localAddresses" label="本地地址">
<n-input
v-model:value="formData.localAddresses"
placeholder="例: 192.168.1.0/24, 10.0.0.1 (* 表示任何地址)"
/>
<template #feedback> 支持单个IP、CIDR格式或逗号分隔的多个地址</template>
</n-form-item>
<n-form-item path="remoteAddresses" label="远程地址">
<n-input
v-model:value="formData.remoteAddresses"
placeholder="例: 192.168.1.0/24, 10.0.0.1 (* 表示任何地址)"
/>
<template #feedback> 支持单个IP、CIDR格式或逗号分隔的多个地址</template>
</n-form-item>
</div>
</div>
</div>
</n-tab-pane>
<!-- 配置文件和接口 -->
<!-- 配置文件 -->
<n-tab-pane name="profile" tab="配置文件">
<div class="tab-content">
<div class="form-section">
@@ -218,19 +159,19 @@
<n-form-item path="profiles" label="应用于配置文件" required>
<n-checkbox-group v-model:value="formData.profiles">
<n-space vertical>
<n-checkbox :value="1">
<n-checkbox value="域">
<div class="profile-option">
<div class="profile-name">域网络 (Domain)</div>
<div class="profile-desc">计算机连接到域时</div>
</div>
</n-checkbox>
<n-checkbox :value="2">
<n-checkbox value="专用">
<div class="profile-option">
<div class="profile-name">专用网络 (Private)</div>
<div class="profile-desc">家庭或工作网络</div>
</div>
</n-checkbox>
<n-checkbox :value="4">
<n-checkbox value="公用">
<div class="profile-option">
<div class="profile-name">公用网络 (Public)</div>
<div class="profile-desc">公共场所的网络</div>
@@ -239,79 +180,6 @@
</n-space>
</n-checkbox-group>
</n-form-item>
<n-form-item path="interfaceTypes" label="接口类型">
<n-input
v-model:value="formData.interfaceTypes"
placeholder="例: Wireless, Lan (留空或'All'表示所有接口)"
/>
</n-form-item>
<n-form-item path="interfaces" label="特定接口">
<n-dynamic-tags
v-model:value="formData.interfaces"
placeholder="添加网络接口名称"
/>
<template #feedback> 可以指定特定的网络接口,留空表示所有接口</template>
</n-form-item>
</div>
</div>
</div>
</n-tab-pane>
<!-- 高级选项 -->
<n-tab-pane name="advanced" tab="高级选项">
<div class="tab-content">
<div class="form-section">
<div class="section-title">
<n-icon :component="CogOutline" />
边缘遍历设置
</div>
<div class="form-grid">
<n-form-item path="edgeTraversal" label="边缘遍历">
<n-checkbox v-model:checked="formData.edgeTraversal">
允许边缘遍历 (适用于需要穿越NAT的应用)
</n-checkbox>
</n-form-item>
<n-form-item path="edgeTraversalAllowed" label="边缘遍历策略">
<n-checkbox v-model:checked="formData.edgeTraversalAllowed">
允许延迟边缘遍历
</n-checkbox>
</n-form-item>
</div>
</div>
<div class="form-section">
<div class="section-title">
<n-icon :component="LockClosedOutline" />
授权设置
</div>
<div class="form-grid">
<n-form-item path="remoteMachineAuthorizationList" label="远程计算机授权">
<n-input
v-model:value="formData.remoteMachineAuthorizationList"
placeholder="输入安全描述符 (高级用户)"
type="textarea"
:rows="2"
/>
</n-form-item>
<n-form-item path="remoteUserAuthorizationList" label="远程用户授权">
<n-input
v-model:value="formData.remoteUserAuthorizationList"
placeholder="输入安全描述符 (高级用户)"
type="textarea"
:rows="2"
/>
</n-form-item>
<n-form-item path="embeddedContext" label="嵌入式上下文">
<n-input
v-model:value="formData.embeddedContext"
placeholder="可选的嵌入式上下文信息"
/>
</n-form-item>
</div>
</div>
</div>
@@ -371,87 +239,17 @@ import {
LockClosedOutline,
FolderOpenOutline,
} from '@vicons/ionicons5'
import {RuleDirection} from '@/types/firewall'
// 枚举定义 - 对应后端模型
// enum RuleDirection {
// Inbound = 1,
// Outbound = 2,
// }
enum RuleAction {
Block = 0,
Allow = 1,
}
enum ProtocolType {
Any = 256,
ICMPV4 = 1,
IGMP = 2,
TCP = 6,
UDP = 17,
IPv6 = 41,
IPv6Route = 43,
IPv6Frag = 44,
GRE = 47,
ICMPv6 = 58,
IPv6NoNxt = 59,
IPv6Opts = 60,
VRRP = 112,
PGM = 113,
L2TP = 115,
}
enum FirewallProfile {
Domain = 1,
Private = 2,
Public = 4,
}
// 防火墙规则接口 - 对应后端 FirewallRule
interface FirewallRule {
name: string
description: string
applicationName: string
serviceName: string
protocol: ProtocolType
localPorts: string
remotePorts: string
localAddresses: string
remoteAddresses: string
icmpTypesAndCodes: string
direction: RuleDirection
enabled: boolean
profiles: FirewallProfile[]
edgeTraversal: boolean
action: RuleAction
grouping: string
interfaceTypes: string
interfaces: string[]
// 高级属性
edgeTraversalAllowed: boolean
looseSourceMapping: boolean
localOnlyMapping: boolean
remoteMachineAuthorizationList: string
remoteUserAuthorizationList: string
embeddedContext: string
flags: number
secureFlags: boolean
}
// 创建规则请求接口 - 对应后端 CreateRuleRequest
interface CreateRuleRequest extends Omit<FirewallRule, 'profiles'> {
profiles: number // 使用位标志表示多个配置文件
}
import { RuleDirection, RuleAction, ProtocolType, FirewallProfile } from '@/types/firewall'
import type { DisplayRule } from '@/types/firewall'
const props = defineProps<{
modelValue: boolean
editRule?: FirewallRule | null
editRule?: DisplayRule | null
}>()
const emit = defineEmits<{
'update:modelValue': [value: boolean]
save: [rule: CreateRuleRequest]
save: [rule: DisplayRule]
}>()
// 响应式数据
@@ -467,38 +265,20 @@ const activeTab = ref('basic')
const saving = ref(false)
// 表单数据
const defaultFormData: FirewallRule = {
const defaultFormData: DisplayRule = {
id: '',
name: '',
description: '',
applicationName: '',
serviceName: '',
protocol: ProtocolType.Any,
localPorts: '',
remotePorts: '',
localAddresses: '*',
remoteAddresses: '*',
icmpTypesAndCodes: '',
direction: RuleDirection.Inbound,
enabled: true,
profiles: [FirewallProfile.Domain, FirewallProfile.Private],
edgeTraversal: false,
action: RuleAction.Allow,
grouping: '',
interfaceTypes: 'All',
interfaces: [],
// 高级属性
edgeTraversalAllowed: false,
looseSourceMapping: false,
localOnlyMapping: false,
remoteMachineAuthorizationList: '',
remoteUserAuthorizationList: '',
embeddedContext: '',
flags: 0,
secureFlags: false,
direction: 'inbound',
action: 'allow',
program: '任意',
protocol: '任意',
port: '任意',
profiles: ['域', '专用'],
}
const formData = ref<FirewallRule>({ ...defaultFormData })
const formData = ref<DisplayRule>({ ...defaultFormData })
// 表单验证规则
const formRules: FormRules = {
@@ -506,15 +286,14 @@ const formRules: FormRules = {
{ required: true, message: '请输入规则名称', trigger: 'blur' },
{ min: 1, max: 255, message: '规则名称长度应在1-255字符之间', trigger: 'blur' },
],
direction: [{ required: true, message: '请选择规则方向', trigger: 'change', type: 'number' }],
action: [{ required: true, message: '请选择规则动作', trigger: 'change', type: 'number' }],
protocol: [{ required: true, message: '请选择协议类型', trigger: 'change', type: 'number' }],
direction: [{ required: true, message: '请选择规则方向', trigger: 'change' }],
action: [{ required: true, message: '请选择规则动作', trigger: 'change' }],
profiles: [
{
required: true,
message: '请至少选择一个配置文件',
trigger: 'change',
validator: (rule, value: FirewallProfile[]) => {
validator: (rule, value: string[]) => {
return value && value.length > 0
},
},
@@ -523,29 +302,11 @@ const formRules: FormRules = {
// 选项数据
const protocolOptions = [
{ label: '任意协议', value: ProtocolType.Any },
{ label: 'TCP', value: ProtocolType.TCP },
{ label: 'UDP', value: ProtocolType.UDP },
{ label: 'ICMPv4', value: ProtocolType.ICMPV4 },
{ label: 'ICMPv6', value: ProtocolType.ICMPv6 },
{ label: 'IGMP', value: ProtocolType.IGMP },
{ label: 'IPv6', value: ProtocolType.IPv6 },
{ label: 'GRE', value: ProtocolType.GRE },
{ label: 'VRRP', value: ProtocolType.VRRP },
{ label: 'PGM', value: ProtocolType.PGM },
{ label: 'L2TP', value: ProtocolType.L2TP },
]
const serviceOptions = [
{ label: 'Windows Update (wuauserv)', value: 'wuauserv' },
{ label: '远程桌面服务 (TermService)', value: 'TermService' },
{ label: 'Windows Defender (WinDefend)', value: 'WinDefend' },
{ label: '打印后台处理程序 (Spooler)', value: 'Spooler' },
{ label: 'Windows 防火墙 (MpsSvc)', value: 'MpsSvc' },
{ label: 'DNS 客户端 (Dnscache)', value: 'Dnscache' },
{ label: 'DHCP 客户端 (Dhcp)', value: 'Dhcp' },
{ label: 'Workstation (LanmanWorkstation)', value: 'LanmanWorkstation' },
{ label: 'Server (LanmanServer)', value: 'LanmanServer' },
{ label: '任意', value: '任意' },
{ label: 'TCP', value: 'TCP' },
{ label: 'UDP', value: 'UDP' },
{ label: 'ICMP', value: 'ICMP' },
{ label: 'ICMPv6', value: 'ICMPv6' },
]
// 监听编辑规则变化
@@ -586,17 +347,15 @@ const handleSave = async () => {
await formRef.value?.validate()
saving.value = true
// 转换数据格式以匹配后端要求
const saveData: CreateRuleRequest = {
...formData.value,
// 将配置文件数组转换为位标志
profiles: formData.value.profiles.reduce((acc, profile) => acc | profile, 0),
// 生成ID(如果是新规则)
if (!formData.value.id) {
formData.value.id = Date.now().toString()
}
// 模拟保存延迟
await new Promise((resolve) => setTimeout(resolve, 1000))
emit('save', saveData)
emit('save', { ...formData.value })
message.success(isEdit.value ? '规则更新成功' : '规则创建成功')
handleClose()
} catch (errors) {
@@ -13,7 +13,7 @@ import type { HttpConfig } from '@/types/http.ts'
// 2. 配置管理器 (config/httpConfig.ts)
class HttpConfigManager {
private config: HttpConfig = {
baseURL: import.meta.env.VITE_APP_API_URL || 'http://localhost:8726',
baseURL: import.meta.env.VITE_APP_API_URL || 'http://localhost:8268/api',
timeout: 10000,
headers: {
'Content-Type': 'application/json'
+60 -88
View File
@@ -1,8 +1,6 @@
/**
* 防火墙相关类型定义
*/
// 所有 DateTime 在前端统一采用 string
// 防火墙配置文件类型
// FirewallProfile 枚举(使用 bit flags)
export enum FirewallProfile {
Domain = 1,
Private = 2,
@@ -10,20 +8,19 @@ export enum FirewallProfile {
All = Domain | Private | Public,
}
// 防火墙规则方向
// RuleDirection 枚举
export enum RuleDirection {
Inbound = 1,
Outbound = 2,
None = -1,
}
// 防火墙规则动作
// RuleAction 枚举
export enum RuleAction {
Allow = 1,
Block = 0,
}
// 协议类型
// ProtocolType 枚举
export enum ProtocolType {
Any = 256,
ICMPV4 = 1,
@@ -42,10 +39,31 @@ export enum ProtocolType {
L2TP = 115,
}
// 配置文件状态
export interface ProfileStatus {
profile: FirewallProfile
isEnabled: boolean
blockAllInboundTraffic: boolean
notifyOnListen: boolean
unicastResponsesDisabled: boolean
defaultInboundAction: RuleAction
defaultOutboundAction: RuleAction
}
// 防火墙状态信息
export interface FirewallStatus {
isEnabled: boolean
profileStatuses: Record<FirewallProfile, ProfileStatus>
totalRules: number
enabledRules: number
inboundRules: number
outboundRules: number
lastModified: string // ISO 格式字符串或 Date 类型
}
// 防火墙规则详细信息
export interface FirewallRule {
name: string
uniqueId?: string // 添加唯一标识符字段,用于解决重复规则显示问题
description: string
applicationName: string
serviceName: string
@@ -65,6 +83,8 @@ export interface FirewallRule {
interfaces: string[]
creationDate?: string
modificationDate?: string
// 高级属性
edgeTraversalAllowed: boolean
looseSourceMapping: boolean
localOnlyMapping: boolean
@@ -75,32 +95,32 @@ export interface FirewallRule {
secureFlags: boolean
}
// 创建防火墙规则的请求
// 创建规则请求
export interface CreateRuleRequest {
name: string
description?: string
applicationName?: string
serviceName?: string
protocol?: ProtocolType
localPorts?: string
remotePorts?: string
localAddresses?: string
remoteAddresses?: string
icmpTypesAndCodes?: string
description: string
applicationName: string
serviceName: string
protocol: ProtocolType
localPorts: string
remotePorts: string
localAddresses: string
remoteAddresses: string
icmpTypesAndCodes: string
direction: RuleDirection
enabled?: boolean
profiles?: FirewallProfile
action?: RuleAction
grouping?: string
interfaceTypes?: string
interfaces?: string[]
edgeTraversal?: boolean
edgeTraversalAllowed?: boolean
remoteMachineAuthorizationList?: string
remoteUserAuthorizationList?: string
enabled: boolean
profiles: FirewallProfile
action: RuleAction
grouping: string
interfaceTypes: string
interfaces: string[]
edgeTraversal: boolean
edgeTraversalAllowed: boolean
remoteMachineAuthorizationList: string
remoteUserAuthorizationList: string
}
// 更新防火墙规则的请求
// 更新规则请求
export interface UpdateRuleRequest {
currentName: string
newName?: string
@@ -119,41 +139,17 @@ export interface UpdateRuleRequest {
edgeTraversal?: boolean
}
// 防火墙规则查询过滤器
// 规则过滤器
export interface RuleFilter {
name?: string
direction?: RuleDirection | string
namePattern?: string
direction?: RuleDirection
enabled?: boolean
profile?: FirewallProfile
protocol?: ProtocolType | string
action?: RuleAction | string
application?: string
protocol?: ProtocolType
action?: RuleAction
applicationName?: string
grouping?: string
port?: string
start?: number
limit?: number
}
// 防火墙状态信息
export interface FirewallStatus {
isEnabled: boolean
profileStatuses: Record<string, ProfileStatus>
totalRules: number
enabledRules: number
inboundRules: number
outboundRules: number
lastModified: string
}
// 配置文件状态
export interface ProfileStatus {
profile: FirewallProfile
isEnabled: boolean
blockAllInboundTraffic: boolean
notifyOnListen: boolean
unicastResponsesDisabled: boolean
defaultInboundAction: RuleAction
defaultOutboundAction: RuleAction
}
// 防火墙统计信息
@@ -161,34 +157,10 @@ export interface FirewallStatistics {
totalRules: number
enabledRules: number
disabledRules: number
rulesByDirection: Record<string, number>
rulesByProtocol: Record<string, number>
rulesByProfile: Record<string, number>
rulesByAction: Record<string, number>
rulesByDirection: Record<RuleDirection, number>
rulesByProtocol: Record<ProtocolType, number>
rulesByProfile: Record<FirewallProfile, number>
rulesByAction: Record<RuleAction, number>
topApplications: string[]
topPorts: string[]
}
// 防火墙规则查询响应
export interface FirewallRulesResponse {
rules: FirewallRule[]
totalCount: number
startIndex: number
limit: number
hasMore: boolean
}
// 前端显示用的规则接口(兼容现有组件)
export interface DisplayRule {
id: string
name: string
description: string
enabled: boolean
direction: 'inbound' | 'outbound'
action: 'allow' | 'block'
program: string
protocol: string
port: string
profiles: string[]
priority?: number
}
+25
View File
@@ -0,0 +1,25 @@
/**
* HTTP相关类型定义
*/
import type { AxiosRequestConfig } from 'axios'
// HTTP配置接口
export interface HttpConfig {
baseURL: string
timeout?: number
headers?: Record<string, string>
}
// 请求配置接口
export interface RequestConfig extends AxiosRequestConfig {
skipErrorMessage?: boolean
skipLoading?: boolean
}
// API响应接口
export interface ApiResponse<T = unknown> {
success?: boolean
code?: number
data?: T
message?: string
}
+351 -87
View File
@@ -29,7 +29,7 @@
<n-icon :component="ListOutline" size="24" />
</div>
<div class="stat-content">
<div class="stat-number">{{ stats.total }}</div>
<div class="stat-number">{{ stats.disableRules }}</div>
<div class="stat-label">禁用规则</div>
</div>
</div>
@@ -49,7 +49,7 @@
<n-icon :component="PauseCircleOutline" size="24" />
</div>
<div class="stat-content">
<div class="stat-number">{{ stats.inactive }}</div>
<div class="stat-number">{{ stats.outboundRules }}</div>
<div class="stat-label">出站规则</div>
</div>
</div>
@@ -59,7 +59,7 @@
<n-icon :component="CloseCircleOutline" size="24" />
</div>
<div class="stat-content">
<div class="stat-number">{{ stats.blocked }}</div>
<div class="stat-number">{{ stats.inboundRules }}</div>
<div class="stat-label">入站规则</div>
</div>
</div>
@@ -268,7 +268,7 @@ import {
NSelect,
useMessage,
} from 'naive-ui'
import { RecycleScroller } from 'vue-virtual-scroller'
import { RecycleScroller, DynamicScroller } from 'vue-virtual-scroller'
import FirewallRuleForm from '@/components/FirewallRuleForm.vue'
import {
AddOutline,
@@ -284,6 +284,10 @@ import {
ArrowDownOutline,
RefreshOutline,
} from '@vicons/ionicons5'
import { httpClient } from '@/utils/http'
import type { FirewallStatus } from '@/types/firewall'
import { ApiResponse } from '@/types/http'
import StringUtils from '@/utils/stringUtils'
const message = useMessage()
@@ -293,7 +297,6 @@ const currentEditRule = ref(null)
const firewallEnabled = ref(true)
const checkedRowKeys = ref<string[]>([])
// 搜索和过滤
const searchQuery = ref('')
const filters = ref({
direction: null as string | null,
@@ -345,10 +348,30 @@ const batchOptions = [
// 统计数据
const stats = ref({
total: 127,
active: 89,
inactive: 38,
blocked: 1247,
//禁用规则
disableRules: 0,
//启动规则
active: 0,
// 出战规则
outboundRules: 0,
// 进站规则
inboundRules: 0,
})
// 请求状态数据
httpClient.get('/firewall/status').then((res: ApiResponse<FirewallStatus>) => {
if (!res.success) {
message.error(StringUtils.isBlank(res.message) ? '错误信息为空!' : res.message)
return
}
if (res.data === null || res.data === undefined ){
message.error("获取数据为空!")
return
}
stats.value.inboundRules = res.data!.inboundRules
stats.value.outboundRules = res.data!.outboundRules
stats.value.active = res.data!.enabledRules
stats.value.disableRules = res.data!.totalRules - res.data!.enabledRules
})
// 模拟规则数据
@@ -817,102 +840,320 @@ const handleSaveRule = (rule: unknown) => {
text-overflow: ellipsis;
white-space: nowrap;
}
/* 超超大屏幕 1801px-2000px */
@media (min-width: 1801px) and (max-width: 2000px) {
.checkbox-cell { width: 60px; }
.name-cell { width: 250px; }
.status-cell { width: 100px; }
.direction-cell { width: 100px; }
.action-cell { width: 100px; }
.program-cell { width: 320px; flex-shrink: 1; }
.protocol-cell { width: 100px; }
.port-cell { width: 120px; }
.profiles-cell { width: 180px; flex-shrink: 1; }
.actions-cell { width: 120px; }
.checkbox-cell {
width: 60px;
}
.name-cell {
width: 250px;
}
.status-cell {
width: 100px;
}
.direction-cell {
width: 100px;
}
.action-cell {
width: 100px;
}
.program-cell {
width: 320px;
flex-shrink: 1;
}
.protocol-cell {
width: 100px;
}
.port-cell {
width: 120px;
}
.profiles-cell {
width: 180px;
flex-shrink: 1;
}
.actions-cell {
width: 120px;
}
}
/* 极大屏幕 >2000px */
@media (min-width: 2001px) {
.checkbox-cell { width: 70px; }
.name-cell { width: 280px; }
.status-cell { width: 110px; }
.direction-cell { width: 110px; }
.action-cell { width: 110px; }
.program-cell { width: 380px; flex-shrink: 1; }
.protocol-cell { width: 110px; }
.port-cell { width: 140px; }
.profiles-cell { width: 200px; flex-shrink: 1; }
.actions-cell { width: 140px; }
.checkbox-cell {
width: 70px;
}
.name-cell {
width: 280px;
}
.status-cell {
width: 110px;
}
.direction-cell {
width: 110px;
}
.action-cell {
width: 110px;
}
.program-cell {
width: 380px;
flex-shrink: 1;
}
.protocol-cell {
width: 110px;
}
.port-cell {
width: 140px;
}
.profiles-cell {
width: 200px;
flex-shrink: 1;
}
.actions-cell {
width: 140px;
}
}
/* 超大屏幕 1601px-1800px */
@media (min-width: 1601px) and (max-width: 1800px) {
.checkbox-cell { width: 55px; }
.name-cell { width: 230px; }
.status-cell { width: 90px; }
.direction-cell { width: 90px; }
.action-cell { width: 90px; }
.program-cell { width: 280px; flex-shrink: 1; }
.protocol-cell { width: 90px; }
.port-cell { width: 110px; }
.profiles-cell { width: 160px; flex-shrink: 1; }
.actions-cell { width: 110px; }
.checkbox-cell {
width: 55px;
}
.name-cell {
width: 230px;
}
.status-cell {
width: 90px;
}
.direction-cell {
width: 90px;
}
.action-cell {
width: 90px;
}
.program-cell {
width: 280px;
flex-shrink: 1;
}
.protocol-cell {
width: 90px;
}
.port-cell {
width: 110px;
}
.profiles-cell {
width: 160px;
flex-shrink: 1;
}
.actions-cell {
width: 110px;
}
}
/* 基础列宽 - 针对超大屏幕(1401px-1600px) */
@media (min-width: 1401px) and (max-width: 1600px) {
.checkbox-cell { width: 50px; }
.name-cell { width: 200px; }
.status-cell { width: 80px; }
.direction-cell { width: 80px; }
.action-cell { width: 80px; }
.program-cell { width: 250px; flex-shrink: 1; }
.protocol-cell { width: 80px; }
.port-cell { width: 100px; }
.profiles-cell { width: 150px; flex-shrink: 1; }
.actions-cell { width: 100px; }
.checkbox-cell {
width: 50px;
}
.name-cell {
width: 200px;
}
.status-cell {
width: 80px;
}
.direction-cell {
width: 80px;
}
.action-cell {
width: 80px;
}
.program-cell {
width: 250px;
flex-shrink: 1;
}
.protocol-cell {
width: 80px;
}
.port-cell {
width: 100px;
}
.profiles-cell {
width: 150px;
flex-shrink: 1;
}
.actions-cell {
width: 100px;
}
}
/* 大屏幕 1201px-1400px */
@media (min-width: 1201px) and (max-width: 1400px) {
.checkbox-cell { width: 45px; }
.name-cell { width: 180px; }
.status-cell { width: 70px; }
.direction-cell { width: 70px; }
.action-cell { width: 70px; }
.program-cell { width: 220px; flex-shrink: 1; }
.protocol-cell { width: 70px; }
.port-cell { width: 90px; }
.profiles-cell { width: 130px; flex-shrink: 1; }
.actions-cell { width: 90px; }
.checkbox-cell {
width: 45px;
}
.name-cell {
width: 180px;
}
.status-cell {
width: 70px;
}
.direction-cell {
width: 70px;
}
.action-cell {
width: 70px;
}
.program-cell {
width: 220px;
flex-shrink: 1;
}
.protocol-cell {
width: 70px;
}
.port-cell {
width: 90px;
}
.profiles-cell {
width: 130px;
flex-shrink: 1;
}
.actions-cell {
width: 90px;
}
}
/* 中等屏幕 1025px-1200px */
@media (min-width: 1025px) and (max-width: 1200px) {
.checkbox-cell { width: 40px; }
.name-cell { width: 160px; }
.status-cell { width: 65px; }
.direction-cell { width: 65px; }
.action-cell { width: 65px; }
.program-cell { width: 200px; flex-shrink: 1; }
.protocol-cell { width: 65px; }
.port-cell { width: 80px; }
.profiles-cell { width: 120px; flex-shrink: 1; }
.actions-cell { width: 85px; }
.checkbox-cell {
width: 40px;
}
.name-cell {
width: 160px;
}
.status-cell {
width: 65px;
}
.direction-cell {
width: 65px;
}
.action-cell {
width: 65px;
}
.program-cell {
width: 200px;
flex-shrink: 1;
}
.protocol-cell {
width: 65px;
}
.port-cell {
width: 80px;
}
.profiles-cell {
width: 120px;
flex-shrink: 1;
}
.actions-cell {
width: 85px;
}
}
/* 小屏幕 769px-1024px */
@media (min-width: 769px) and (max-width: 1024px) {
.checkbox-cell { width: 40px; }
.name-cell { width: 140px; }
.status-cell { width: 60px; }
.direction-cell { width: 60px; }
.action-cell { width: 60px; }
.program-cell { width: 180px; flex-shrink: 1; }
.protocol-cell { width: 60px; }
.port-cell { width: 80px; }
.profiles-cell { width: 100px; flex-shrink: 1; }
.actions-cell { width: 80px; }
.checkbox-cell {
width: 40px;
}
.name-cell {
width: 140px;
}
.status-cell {
width: 60px;
}
.direction-cell {
width: 60px;
}
.action-cell {
width: 60px;
}
.program-cell {
width: 180px;
flex-shrink: 1;
}
.protocol-cell {
width: 60px;
}
.port-cell {
width: 80px;
}
.profiles-cell {
width: 100px;
flex-shrink: 1;
}
.actions-cell {
width: 80px;
}
}
/* 虚拟滚动列表 */
@@ -1119,14 +1360,37 @@ const handleSaveRule = (rule: unknown) => {
display: none;
}
.checkbox-cell { width: 35px; }
.name-cell { width: 120px; }
.status-cell { width: 50px; }
.direction-cell { width: 50px; }
.action-cell { width: 50px; }
.protocol-cell { width: 50px; }
.port-cell { width: 60px; }
.actions-cell { width: 70px; }
.checkbox-cell {
width: 35px;
}
.name-cell {
width: 120px;
}
.status-cell {
width: 50px;
}
.direction-cell {
width: 50px;
}
.action-cell {
width: 50px;
}
.protocol-cell {
width: 50px;
}
.port-cell {
width: 60px;
}
.actions-cell {
width: 70px;
}
}
/* 高度不足时的优化 */