From 731015e893a7b7044066ed7cead424f1c2ea5798 Mon Sep 17 00:00:00 2001 From: Baobhan Sith <80159437+Heavrnl@users.noreply.github.com> Date: Tue, 29 Apr 2025 16:03:41 +0800 Subject: [PATCH] update --- .../src/auth/ipBlacklistCheck.middleware.ts | 8 ++ .../src/services/ip-blacklist.service.ts | 20 ++++- .../backend/src/services/settings.service.ts | 19 +++++ .../src/settings/settings.controller.ts | 3 +- .../frontend/src/stores/settings.store.ts | 18 ++++- packages/frontend/src/views/SettingsView.vue | 80 ++++++++++++++++--- 6 files changed, 129 insertions(+), 19 deletions(-) diff --git a/packages/backend/src/auth/ipBlacklistCheck.middleware.ts b/packages/backend/src/auth/ipBlacklistCheck.middleware.ts index 720b746..f0ce580 100644 --- a/packages/backend/src/auth/ipBlacklistCheck.middleware.ts +++ b/packages/backend/src/auth/ipBlacklistCheck.middleware.ts @@ -1,5 +1,6 @@ import { Request, Response, NextFunction } from 'express'; import { ipBlacklistService } from '../services/ip-blacklist.service'; +import { settingsService } from '../services/settings.service'; // <-- Import settingsService /** * IP 黑名单检查中间件 @@ -17,6 +18,13 @@ export const ipBlacklistCheckMiddleware = async (req: Request, res: Response, ne } try { + // 首先检查 IP 黑名单功能是否启用 + const isEnabled = await settingsService.isIpBlacklistEnabled(); + if (!isEnabled) { + // console.log('[IP Blacklist Check] 功能已禁用,跳过检查。'); + return next(); // 功能禁用,直接放行 + } + const isBlocked = await ipBlacklistService.isBlocked(clientIp); if (isBlocked) { console.warn(`[IP Blacklist Check] 已阻止来自被封禁 IP ${clientIp} 的访问。`); diff --git a/packages/backend/src/services/ip-blacklist.service.ts b/packages/backend/src/services/ip-blacklist.service.ts index fa92a32..9bae825 100644 --- a/packages/backend/src/services/ip-blacklist.service.ts +++ b/packages/backend/src/services/ip-blacklist.service.ts @@ -51,10 +51,16 @@ export class IpBlacklistService { * @param ip IP 地址 * @returns 如果被封禁则返回 true,否则返回 false */ - async isBlocked(ip: string): Promise { - try { - const entry = await this.getEntry(ip); - if (!entry) { + async isBlocked(ip: string): Promise { + // 首先检查功能是否启用 + if (!(await settingsService.isIpBlacklistEnabled())) { + // console.log('[IP Blacklist] 功能已禁用,跳过 isBlocked 检查。'); + return false; // 如果禁用,则认为 IP 未被阻止 + } + + try { + const entry = await this.getEntry(ip); + if (!entry) { return false; // 不在黑名单中 } // 检查封禁时间是否已过 @@ -75,6 +81,12 @@ export class IpBlacklistService { * @param ip IP 地址 */ async recordFailedAttempt(ip: string): Promise { + // 首先检查功能是否启用 + if (!(await settingsService.isIpBlacklistEnabled())) { + // console.log('[IP Blacklist] 功能已禁用,跳过 recordFailedAttempt。'); + return; // 如果禁用,则不记录失败尝试 + } + if (LOCAL_IPS.includes(ip)) { console.log(`[IP Blacklist] 检测到本地 IP ${ip} 登录失败,跳过黑名单处理。`); return; diff --git a/packages/backend/src/services/settings.service.ts b/packages/backend/src/services/settings.service.ts index ff403b4..a5fae56 100644 --- a/packages/backend/src/services/settings.service.ts +++ b/packages/backend/src/services/settings.service.ts @@ -30,6 +30,7 @@ const LAYOUT_TREE_KEY = 'layoutTree'; // 布局树设置键 const AUTO_COPY_ON_SELECT_KEY = 'autoCopyOnSelect'; // 终端选中自动复制设置键 const STATUS_MONITOR_INTERVAL_SECONDS_KEY = 'statusMonitorIntervalSeconds'; // 状态监控间隔设置键 const DEFAULT_STATUS_MONITOR_INTERVAL_SECONDS = 3; // 默认状态监控间隔 +const IP_BLACKLIST_ENABLED_KEY = 'ipBlacklistEnabled'; // IP 黑名单启用设置键 export const settingsService = { /** @@ -108,6 +109,24 @@ export const settingsService = { ]); }, + /** + * 检查 IP 黑名单功能是否已启用 + * @returns 返回是否启用 (boolean),如果未设置则默认为 true + */ + async isIpBlacklistEnabled(): Promise { + console.log(`[Service] Attempting to get setting for key: ${IP_BLACKLIST_ENABLED_KEY}`); + try { + const enabledStr = await settingsRepository.getSetting(IP_BLACKLIST_ENABLED_KEY); + console.log(`[Service] Raw value from repository for ${IP_BLACKLIST_ENABLED_KEY}:`, enabledStr); + // 如果设置存在且值为 'false',则返回 false,否则都返回 true (包括未设置的情况) + return enabledStr !== 'false'; + } catch (error) { + console.error(`[Service] Error getting IP blacklist enabled setting (key: ${IP_BLACKLIST_ENABLED_KEY}):`, error); + // 出错时返回默认值 true (安全起见,默认启用) + return true; + } + }, // *** 确保这里有逗号 *** + /** * 获取焦点切换顺序 * @returns 返回存储的完整焦点切换配置对象,如果未设置或无效则返回默认空配置 diff --git a/packages/backend/src/settings/settings.controller.ts b/packages/backend/src/settings/settings.controller.ts index e835e94..f1fa468 100644 --- a/packages/backend/src/settings/settings.controller.ts +++ b/packages/backend/src/settings/settings.controller.ts @@ -46,7 +46,8 @@ export const settingsController = { 'commandInputSyncTarget', // +++ 添加命令输入同步目标键 +++ 'timezone', // NEW: 添加时区键 'rdpModalWidth', // NEW: 添加 RDP 模态框宽度键 - 'rdpModalHeight' // NEW: 添加 RDP 模态框高度键 + 'rdpModalHeight', // NEW: 添加 RDP 模态框高度键 + 'ipBlacklistEnabled' // <-- 添加 IP 黑名单启用键 ]; const filteredSettings: Record = {}; for (const key in settingsToUpdate) { diff --git a/packages/frontend/src/stores/settings.store.ts b/packages/frontend/src/stores/settings.store.ts index 7af27b7..d789a9f 100644 --- a/packages/frontend/src/stores/settings.store.ts +++ b/packages/frontend/src/stores/settings.store.ts @@ -47,6 +47,7 @@ interface SettingsState { timezone?: string; // NEW: 时区设置 (e.g., 'Asia/Shanghai', 'UTC') rdpModalWidth?: string; // NEW: RDP 模态框宽度 rdpModalHeight?: string; // NEW: RDP 模态框高度 + ipBlacklistEnabled?: string; // <-- NEW: IP 黑名单启用状态 'true' or 'false' // Add other general settings keys here as needed [key: string]: string | undefined; // Allow other string settings } @@ -100,6 +101,11 @@ export const useSettingsStore = defineStore('settings', () => { } + // NEW: IP Blacklist enabled default + if (settings.value.ipBlacklistEnabled === undefined) { + settings.value.ipBlacklistEnabled = 'true'; // 默认启用 IP 黑名单 + } + if (settings.value.autoCopyOnSelect === undefined) { settings.value.autoCopyOnSelect = 'false'; // 默认禁用选中即复制 } @@ -299,7 +305,8 @@ export const useSettingsStore = defineStore('settings', () => { 'commandInputSyncTarget', // +++ 添加命令输入同步目标键 +++ 'timezone', // NEW: 添加时区键 'rdpModalWidth', // NEW: 添加 RDP 模态框宽度键 - 'rdpModalHeight' // NEW: 添加 RDP 模态框高度键 + 'rdpModalHeight', // NEW: 添加 RDP 模态框高度键 + 'ipBlacklistEnabled' // <-- NEW: 添加 IP 黑名单启用键 ]; if (!allowedKeys.includes(key)) { console.error(`[SettingsStore] 尝试更新不允许的设置键: ${key}`); @@ -358,7 +365,8 @@ export const useSettingsStore = defineStore('settings', () => { 'commandInputSyncTarget', // +++ 添加命令输入同步目标键 +++ 'timezone', // NEW: 添加时区键 'rdpModalWidth', // NEW: 添加 RDP 模态框宽度键 - 'rdpModalHeight' // NEW: 添加 RDP 模态框高度键 + 'rdpModalHeight', // NEW: 添加 RDP 模态框高度键 + 'ipBlacklistEnabled' // <-- NEW: 添加 IP 黑名单启用键 ]; const filteredUpdates: Partial = {}; let languageUpdate: string | undefined = undefined; // Use string type @@ -530,6 +538,11 @@ export const useSettingsStore = defineStore('settings', () => { // Getter for IP Whitelist enabled status const ipWhitelistEnabled = computed(() => settings.value.ipWhitelistEnabled === 'true'); + // <-- NEW: Getter for IP Blacklist enabled status --> + const ipBlacklistEnabledBoolean = computed(() => { + // Default to true if the setting is missing or not 'false' + return settings.value.ipBlacklistEnabled !== 'false'; + }); // Getter for auto copy on select setting, returning boolean const autoCopyOnSelectBoolean = computed(() => { @@ -600,6 +613,7 @@ export const useSettingsStore = defineStore('settings', () => { showPopupFileEditorBoolean, shareFileEditorTabsBoolean, ipWhitelistEnabled, // 暴露 IP 白名单启用状态 + ipBlacklistEnabledBoolean, // <-- NEW: 暴露 IP 黑名单启用状态 getter autoCopyOnSelectBoolean, dockerDefaultExpandBoolean, // +++ 暴露 Docker 默认展开 getter +++ statusMonitorIntervalSecondsNumber, // +++ 暴露状态监控间隔 getter +++ diff --git a/packages/frontend/src/views/SettingsView.vue b/packages/frontend/src/views/SettingsView.vue index a226009..9415346 100644 --- a/packages/frontend/src/views/SettingsView.vue +++ b/packages/frontend/src/views/SettingsView.vue @@ -199,13 +199,38 @@
-

{{ $t('settings.ipBlacklist.title') }}

+
+

{{ $t('settings.ipBlacklist.title') }}

+ + +
-

{{ $t('settings.ipBlacklist.description') }}

- -
-
- + + + +
+

{{ $t('settings.ipBlacklist.description') }}

+ + +
+
@@ -220,11 +245,11 @@ {{ $t('settings.ipBlacklist.saveConfigButton') }}
-

{{ blacklistSettingsMessage }}

- -
- -

{{ $t('settings.ipBlacklist.currentBannedTitle') }}

+

{{ blacklistSettingsMessage }}

+ +
+ +

{{ $t('settings.ipBlacklist.currentBannedTitle') }}

{{ ipBlacklist.error }}
@@ -263,7 +288,12 @@
-

{{ blacklistDeleteError }}

+

{{ blacklistDeleteError }}

+
+ +
+ {{ $t('settings.ipBlacklist.disabledMessage', 'IP 黑名单功能当前已禁用。') }} +
@@ -547,6 +577,7 @@ const { workspaceSidebarPersistentBoolean, captchaSettings, // <-- Import CAPTCHA settings state commandInputSyncTarget, // NEW: Import command input sync target getter + ipBlacklistEnabledBoolean, // <-- Import IP Blacklist enabled getter } = storeToRefs(settingsStore); // Removed Passkey state import from authStore @@ -570,6 +601,7 @@ const blacklistSettingsForm = reactive({ // Renamed to avoid conflict with store const popupEditorEnabled = ref(true); // 本地状态,用于 v-model const workspaceSidebarPersistentEnabled = ref(false); // 新增:侧边栏固定设置的本地状态 const commandInputSyncTargetLocal = ref<'none' | 'quickCommands' | 'commandHistory'>('none'); // NEW: Local state for command input sync target +const ipBlacklistEnabled = ref(true); // <-- Local state for IP Blacklist switch // --- Local UI feedback state --- const ipWhitelistLoading = ref(false); @@ -581,6 +613,9 @@ const languageSuccess = ref(false); const blacklistSettingsLoading = ref(false); const blacklistSettingsMessage = ref(''); const blacklistSettingsSuccess = ref(false); +// Removed ipBlacklistEnabledLoading, ipBlacklistEnabledMessage, ipBlacklistEnabledSuccess refs + + const popupEditorLoading = ref(false); const popupEditorMessage = ref(''); const popupEditorSuccess = ref(false); @@ -658,6 +693,7 @@ watch(settings, (newSettings, oldSettings) => { workspaceSidebarPersistentEnabled.value = workspaceSidebarPersistentBoolean.value; // 新增:同步侧边栏固定设置 commandInputSyncTargetLocal.value = commandInputSyncTarget.value; // NEW: Sync command input sync target selectedTimezone.value = newSettings.timezone || 'UTC'; // 同步时区设置 + ipBlacklistEnabled.value = ipBlacklistEnabledBoolean.value; // <-- Sync IP Blacklist enabled state }, { deep: true, immediate: true }); // immediate: true to run on initial load @@ -1069,6 +1105,26 @@ const handleUpdateBlacklistSettings = async () => { } }; +// --- IP Blacklist Enable/Disable Method (Button Style) --- +const handleUpdateIpBlacklistEnabled = async () => { + // Toggle local state immediately for instant UI feedback + const originalValue = ipBlacklistEnabled.value; + ipBlacklistEnabled.value = !ipBlacklistEnabled.value; + + try { + const valueToSave = ipBlacklistEnabled.value ? 'true' : 'false'; + await settingsStore.updateSetting('ipBlacklistEnabled', valueToSave); + // Save successful, no message needed for toggle switch + console.log('IP Blacklist enabled status saved:', valueToSave); + } catch (error: any) { + console.error('更新 IP 黑名单启用状态失败:', error); + // Optionally show error notification to user here + // Revert button state on failure + ipBlacklistEnabled.value = originalValue; // Revert to original value + } + // No loading/message state management needed +}; + // --- CAPTCHA Settings Method --- const handleUpdateCaptchaSettings = async () => { captchaLoading.value = true;