diff --git a/frontend/src/pages/clients/ClientFormModal.tsx b/frontend/src/pages/clients/ClientFormModal.tsx
index a76c61c47..bb0c5a297 100644
--- a/frontend/src/pages/clients/ClientFormModal.tsx
+++ b/frontend/src/pages/clients/ClientFormModal.tsx
@@ -791,7 +791,7 @@ export default function ClientFormModal({
-
+
update('password', e.target.value)} />
} onClick={regeneratePassword} />
@@ -805,7 +805,7 @@ export default function ClientFormModal({
-
+
update('auth', e.target.value)} />
} onClick={() => update('auth', RandomUtil.randomLowerAndNum(16))} />
diff --git a/frontend/src/test/client-form-modal.test.tsx b/frontend/src/test/client-form-modal.test.tsx
new file mode 100644
index 000000000..8fe7d406e
--- /dev/null
+++ b/frontend/src/test/client-form-modal.test.tsx
@@ -0,0 +1,71 @@
+import { describe, it, expect, vi } from 'vitest';
+import { fireEvent, waitFor } from '@testing-library/react';
+import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
+
+import ClientFormModal from '@/pages/clients/ClientFormModal';
+import { renderWithProviders } from './test-utils';
+
+// ClientFormModal reads server state via react-query (useFail2banStatusQuery),
+// so it needs a QueryClientProvider on top of the shared ThemeProvider wrapper.
+function renderModal() {
+ const queryClient = new QueryClient({ defaultOptions: { queries: { retry: false } } });
+ renderWithProviders(
+
+ {}}
+ />
+ ,
+ );
+}
+
+function openCredentialsTab() {
+ const tab = Array.from(document.querySelectorAll('.ant-tabs-tab'))
+ .find((t) => (t.textContent ?? '').trim() === 'Credentials');
+ if (!tab) throw new Error('Credentials tab not found');
+ fireEvent.click(tab);
+}
+
+function tooltipIconForLabel(label: string): HTMLElement {
+ const labelEl = Array.from(document.querySelectorAll('.ant-form-item-label label'))
+ .find((l) => (l.textContent ?? '').trim() === label);
+ const item = labelEl?.closest('.ant-form-item') as HTMLElement | null;
+ if (!item) throw new Error(`Form item not found for label: ${label}`);
+ const tip = item.querySelector('.ant-form-item-tooltip') as HTMLElement | null;
+ if (!tip) throw new Error(`No tooltip on form item: ${label}`);
+ return tip;
+}
+
+describe('ClientFormModal credential tooltips', () => {
+ it('explains that the Password field is only consumed by Trojan/Shadowsocks', async () => {
+ renderModal();
+ openCredentialsTab();
+
+ const tip = tooltipIconForLabel('Password');
+ fireEvent.mouseEnter(tip);
+
+ await waitFor(() => {
+ expect(document.body.textContent).toContain(
+ 'Only used by Trojan and Shadowsocks clients; ignored for VLESS, VMess, Hysteria, and WireGuard.',
+ );
+ });
+ });
+
+ it('explains that Hysteria Auth is the credential Hysteria actually uses', async () => {
+ renderModal();
+ openCredentialsTab();
+
+ const tip = tooltipIconForLabel('Hysteria Auth');
+ fireEvent.mouseEnter(tip);
+
+ await waitFor(() => {
+ expect(document.body.textContent).toContain(
+ 'Credential used only by Hysteria clients. Trojan and Shadowsocks use the Password field instead.',
+ );
+ });
+ });
+});
diff --git a/internal/web/translation/ar-EG.json b/internal/web/translation/ar-EG.json
index 8c2dd2ce2..5ec5bc24b 100644
--- a/internal/web/translation/ar-EG.json
+++ b/internal/web/translation/ar-EG.json
@@ -806,6 +806,7 @@
"limitIpFail2banWindows": "Fail2ban غير متوفّر على نظام Windows، لذا لا يمكن تطبيق حد عناوين IP.",
"limitIpDisabled": "ميزة حد عناوين IP معطّلة على هذا الخادم.",
"password": "كلمة المرور",
+ "passwordDesc": "تُستخدم فقط من قبل عملاء Trojan و Shadowsocks؛ ويتم تجاهلها لـ VLESS و VMess و Hysteria و WireGuard.",
"subId": "معرّف الاشتراك",
"online": "متصل",
"email": "البريد",
@@ -906,6 +907,7 @@
"delOrphansConfirmContent": "يزيل كل عميل غير مرتبط بأي اتصال وارد مع سجل حركة مروره. لا يمكن التراجع.",
"auth": "Auth",
"hysteriaAuth": "Hysteria Auth",
+ "hysteriaAuthDesc": "بيانات اعتماد يستخدمها عملاء Hysteria فقط. أما Trojan و Shadowsocks فيستخدمان حقل «كلمة المرور» بدلاً منها.",
"uuid": "UUID",
"flow": "Flow",
"vmessSecurity": "أمان VMess",
diff --git a/internal/web/translation/en-US.json b/internal/web/translation/en-US.json
index c9ed12e46..b1458d00c 100644
--- a/internal/web/translation/en-US.json
+++ b/internal/web/translation/en-US.json
@@ -806,6 +806,7 @@
"limitIpFail2banWindows": "Fail2ban is not available on Windows, so the IP limit cannot be enforced.",
"limitIpDisabled": "The IP limit feature is disabled on this server.",
"password": "Password",
+ "passwordDesc": "Only used by Trojan and Shadowsocks clients; ignored for VLESS, VMess, Hysteria, and WireGuard.",
"subId": "Subscription ID",
"online": "Online",
"email": "Email",
@@ -906,6 +907,7 @@
"delOrphansConfirmContent": "Removes every client that is not attached to any inbound, along with its traffic record. This cannot be undone.",
"auth": "Auth",
"hysteriaAuth": "Hysteria Auth",
+ "hysteriaAuthDesc": "Credential used only by Hysteria clients. Trojan and Shadowsocks use the Password field instead.",
"uuid": "UUID",
"flow": "Flow",
"vmessSecurity": "VMess Security",
diff --git a/internal/web/translation/es-ES.json b/internal/web/translation/es-ES.json
index 2a73b5019..958212288 100644
--- a/internal/web/translation/es-ES.json
+++ b/internal/web/translation/es-ES.json
@@ -806,6 +806,7 @@
"limitIpFail2banWindows": "Fail2ban no está disponible en Windows, por lo que no se puede aplicar el límite de IP.",
"limitIpDisabled": "La función de límite de IP está deshabilitada en este servidor.",
"password": "Contraseña",
+ "passwordDesc": "Solo la usan los clientes Trojan y Shadowsocks; se ignora para VLESS, VMess, Hysteria y WireGuard.",
"subId": "ID de suscripción",
"online": "En línea",
"email": "Email",
@@ -906,6 +907,7 @@
"delOrphansConfirmContent": "Elimina todos los clientes que no están vinculados a ningún entrante, junto con su registro de tráfico. No se puede deshacer.",
"auth": "Auth",
"hysteriaAuth": "Hysteria Auth",
+ "hysteriaAuthDesc": "Credencial usada únicamente por los clientes Hysteria. Trojan y Shadowsocks usan el campo «Contraseña» en su lugar.",
"uuid": "UUID",
"flow": "Flow",
"vmessSecurity": "Seguridad VMess",
diff --git a/internal/web/translation/fa-IR.json b/internal/web/translation/fa-IR.json
index 8e4cce666..2ce8f7380 100644
--- a/internal/web/translation/fa-IR.json
+++ b/internal/web/translation/fa-IR.json
@@ -806,6 +806,7 @@
"limitIpFail2banWindows": "Fail2ban روی ویندوز در دسترس نیست، بنابراین محدودیت IP قابل اعمال نیست.",
"limitIpDisabled": "قابلیت محدودیت IP روی این سرور غیرفعال است.",
"password": "رمز عبور",
+ "passwordDesc": "فقط توسط کلاینتهای Trojan و Shadowsocks استفاده میشود؛ برای VLESS، VMess، Hysteria و WireGuard نادیده گرفته میشود.",
"subId": "شناسه اشتراک",
"online": "آنلاین",
"email": "ایمیل",
@@ -906,6 +907,7 @@
"delOrphansConfirmContent": "هر کلاینتی که به هیچ اینباندی متصل نیست، همراه با رکورد ترافیکاش حذف میشود. این عمل غیرقابل بازگشت است.",
"auth": "احراز",
"hysteriaAuth": "احراز Hysteria",
+ "hysteriaAuthDesc": "اعتباری که فقط کلاینتهای Hysteria از آن استفاده میکنند. Trojan و Shadowsocks بهجای آن از فیلد «رمز عبور» استفاده میکنند.",
"uuid": "UUID",
"flow": "Flow",
"vmessSecurity": "امنیت VMess",
diff --git a/internal/web/translation/id-ID.json b/internal/web/translation/id-ID.json
index c0144b56f..e6ff9f6dc 100644
--- a/internal/web/translation/id-ID.json
+++ b/internal/web/translation/id-ID.json
@@ -806,6 +806,7 @@
"limitIpFail2banWindows": "Fail2ban tidak tersedia di Windows, sehingga batas IP tidak dapat diterapkan.",
"limitIpDisabled": "Fitur batas IP dinonaktifkan di server ini.",
"password": "Kata sandi",
+ "passwordDesc": "Hanya digunakan oleh klien Trojan dan Shadowsocks; diabaikan untuk VLESS, VMess, Hysteria, dan WireGuard.",
"subId": "ID Langganan",
"online": "Online",
"email": "Email",
@@ -906,6 +907,7 @@
"delOrphansConfirmContent": "Menghapus setiap klien yang tidak terhubung ke inbound mana pun, beserta catatan lalu lintasnya. Tidak dapat dibatalkan.",
"auth": "Auth",
"hysteriaAuth": "Hysteria Auth",
+ "hysteriaAuthDesc": "Kredensial yang hanya digunakan oleh klien Hysteria. Trojan dan Shadowsocks menggunakan kolom \"Kata sandi\" sebagai gantinya.",
"uuid": "UUID",
"flow": "Flow",
"vmessSecurity": "Keamanan VMess",
diff --git a/internal/web/translation/ja-JP.json b/internal/web/translation/ja-JP.json
index dcef10f3f..174c7cf5c 100644
--- a/internal/web/translation/ja-JP.json
+++ b/internal/web/translation/ja-JP.json
@@ -806,6 +806,7 @@
"limitIpFail2banWindows": "Windows では Fail2ban を利用できないため、IP 制限を適用できません。",
"limitIpDisabled": "このサーバーでは IP 制限機能が無効になっています。",
"password": "パスワード",
+ "passwordDesc": "Trojan と Shadowsocks のクライアントのみが使用します。VLESS、VMess、Hysteria、WireGuard では無視されます。",
"subId": "サブスクリプション ID",
"online": "オンライン",
"email": "メール",
@@ -906,6 +907,7 @@
"delOrphansConfirmContent": "どのインバウンドにもアタッチされていないクライアントを、そのトラフィック記録とともにすべて削除します。元に戻せません。",
"auth": "Auth",
"hysteriaAuth": "Hysteria Auth",
+ "hysteriaAuthDesc": "Hysteria クライアントのみが使用する認証情報です。Trojan と Shadowsocks は代わりに「パスワード」フィールドを使用します。",
"uuid": "UUID",
"flow": "Flow",
"vmessSecurity": "VMess セキュリティ",
diff --git a/internal/web/translation/pt-BR.json b/internal/web/translation/pt-BR.json
index 8b9a40c2c..f68153f59 100644
--- a/internal/web/translation/pt-BR.json
+++ b/internal/web/translation/pt-BR.json
@@ -806,6 +806,7 @@
"limitIpFail2banWindows": "O Fail2ban não está disponível no Windows, portanto o limite de IP não pode ser aplicado.",
"limitIpDisabled": "O recurso de limite de IP está desativado neste servidor.",
"password": "Senha",
+ "passwordDesc": "Usada apenas pelos clientes Trojan e Shadowsocks; ignorada para VLESS, VMess, Hysteria e WireGuard.",
"subId": "ID da assinatura",
"online": "Online",
"email": "Email",
@@ -906,6 +907,7 @@
"delOrphansConfirmContent": "Remove todos os clientes que não estão vinculados a nenhum inbound, junto com seu registro de tráfego. Não é possível desfazer.",
"auth": "Auth",
"hysteriaAuth": "Hysteria Auth",
+ "hysteriaAuthDesc": "Credencial usada apenas pelos clientes Hysteria. Trojan e Shadowsocks usam o campo \"Senha\" em vez disso.",
"uuid": "UUID",
"flow": "Flow",
"vmessSecurity": "Segurança VMess",
diff --git a/internal/web/translation/ru-RU.json b/internal/web/translation/ru-RU.json
index 28e693c39..d65d38d29 100644
--- a/internal/web/translation/ru-RU.json
+++ b/internal/web/translation/ru-RU.json
@@ -806,6 +806,7 @@
"limitIpFail2banWindows": "Fail2ban недоступен в Windows, поэтому ограничение по IP не может быть применено.",
"limitIpDisabled": "Функция ограничения по IP отключена на этом сервере.",
"password": "Пароль",
+ "passwordDesc": "Используется только клиентами Trojan и Shadowsocks; игнорируется для VLESS, VMess, Hysteria и WireGuard.",
"subId": "ID подписки",
"online": "В сети",
"email": "Email",
@@ -906,6 +907,7 @@
"delOrphansConfirmContent": "Удаляются все клиенты, не привязанные ни к одному входящему, вместе с их записями трафика. Это действие нельзя отменить.",
"auth": "Авторизация",
"hysteriaAuth": "Hysteria Auth",
+ "hysteriaAuthDesc": "Учётные данные, используемые только клиентами Hysteria. Для Trojan и Shadowsocks используйте поле «Пароль».",
"uuid": "UUID",
"flow": "Flow",
"vmessSecurity": "VMess Security",
diff --git a/internal/web/translation/tr-TR.json b/internal/web/translation/tr-TR.json
index bdd48f593..be5a76e93 100644
--- a/internal/web/translation/tr-TR.json
+++ b/internal/web/translation/tr-TR.json
@@ -806,6 +806,7 @@
"limitIpFail2banWindows": "Fail2ban Windows'ta kullanılamadığından IP sınırı uygulanamaz.",
"limitIpDisabled": "IP sınırı özelliği bu sunucuda devre dışı.",
"password": "Şifre",
+ "passwordDesc": "Yalnızca Trojan ve Shadowsocks istemcileri tarafından kullanılır; VLESS, VMess, Hysteria ve WireGuard için yok sayılır.",
"subId": "Abonelik ID'si",
"online": "Çevrimiçi",
"email": "E-posta",
@@ -906,6 +907,7 @@
"delOrphansConfirmContent": "Hiçbir gelen bağlantıya bağlı olmayan her kullanıcı, trafik kaydıyla birlikte silinir. Geri alınamaz.",
"auth": "Auth",
"hysteriaAuth": "Hysteria Auth",
+ "hysteriaAuthDesc": "Yalnızca Hysteria istemcilerinin kullandığı kimlik bilgisi. Trojan ve Shadowsocks bunun yerine \"Şifre\" alanını kullanır.",
"uuid": "UUID",
"flow": "Flow",
"vmessSecurity": "VMess Güvenlik",
diff --git a/internal/web/translation/uk-UA.json b/internal/web/translation/uk-UA.json
index 9aaddb0ea..9d988e4f9 100644
--- a/internal/web/translation/uk-UA.json
+++ b/internal/web/translation/uk-UA.json
@@ -806,6 +806,7 @@
"limitIpFail2banWindows": "Fail2ban недоступний у Windows, тому обмеження за IP не може бути застосоване.",
"limitIpDisabled": "Функцію обмеження за IP вимкнено на цьому сервері.",
"password": "Пароль",
+ "passwordDesc": "Використовується лише клієнтами Trojan і Shadowsocks; ігнорується для VLESS, VMess, Hysteria та WireGuard.",
"subId": "ID підписки",
"online": "У мережі",
"email": "Email",
@@ -906,6 +907,7 @@
"delOrphansConfirmContent": "Видаляється кожен клієнт, не прив'язаний до жодного вхідного, разом із його записом трафіку. Цю дію неможливо скасувати.",
"auth": "Авторизація",
"hysteriaAuth": "Hysteria Auth",
+ "hysteriaAuthDesc": "Облікові дані, які використовують лише клієнти Hysteria. Для Trojan і Shadowsocks використовуйте поле «Пароль».",
"uuid": "UUID",
"flow": "Flow",
"vmessSecurity": "Безпека VMess",
diff --git a/internal/web/translation/vi-VN.json b/internal/web/translation/vi-VN.json
index 319e10c75..5adaf6ff0 100644
--- a/internal/web/translation/vi-VN.json
+++ b/internal/web/translation/vi-VN.json
@@ -806,6 +806,7 @@
"limitIpFail2banWindows": "Fail2ban không khả dụng trên Windows nên không thể áp dụng giới hạn IP.",
"limitIpDisabled": "Tính năng giới hạn IP đã bị tắt trên máy chủ này.",
"password": "Mật khẩu",
+ "passwordDesc": "Chỉ được dùng bởi các client Trojan và Shadowsocks; bị bỏ qua đối với VLESS, VMess, Hysteria và WireGuard.",
"subId": "ID đăng ký",
"online": "Trực tuyến",
"email": "Email",
@@ -906,6 +907,7 @@
"delOrphansConfirmContent": "Gỡ tất cả khách hàng không được gắn vào bất kỳ inbound nào, cùng với bản ghi lưu lượng của họ. Không thể hoàn tác.",
"auth": "Auth",
"hysteriaAuth": "Hysteria Auth",
+ "hysteriaAuthDesc": "Thông tin xác thực chỉ dùng cho client Hysteria. Trojan và Shadowsocks dùng trường \"Mật khẩu\" thay thế.",
"uuid": "UUID",
"flow": "Flow",
"vmessSecurity": "Bảo mật VMess",
diff --git a/internal/web/translation/zh-CN.json b/internal/web/translation/zh-CN.json
index 2940b7f10..b4921b555 100644
--- a/internal/web/translation/zh-CN.json
+++ b/internal/web/translation/zh-CN.json
@@ -806,6 +806,7 @@
"limitIpFail2banWindows": "Windows 上不支持 Fail2ban,无法实施 IP 限制。",
"limitIpDisabled": "此服务器已禁用 IP 限制功能。",
"password": "密码",
+ "passwordDesc": "仅 Trojan 和 Shadowsocks 客户端使用;VLESS、VMess、Hysteria 和 WireGuard 会忽略此项。",
"subId": "订阅 ID",
"online": "在线",
"email": "邮箱",
@@ -906,6 +907,7 @@
"delOrphansConfirmContent": "删除所有未关联到任何入站的客户端及其流量记录。该操作不可撤销。",
"auth": "认证",
"hysteriaAuth": "Hysteria 认证",
+ "hysteriaAuthDesc": "仅 Hysteria 客户端使用的凭据。Trojan 和 Shadowsocks 请改用上方的“密码”字段。",
"uuid": "UUID",
"flow": "Flow",
"vmessSecurity": "VMess 加密",
diff --git a/internal/web/translation/zh-TW.json b/internal/web/translation/zh-TW.json
index 42973b77d..64dfe43f1 100644
--- a/internal/web/translation/zh-TW.json
+++ b/internal/web/translation/zh-TW.json
@@ -806,6 +806,7 @@
"limitIpFail2banWindows": "Windows 上不支援 Fail2ban,無法實施 IP 限制。",
"limitIpDisabled": "此伺服器已停用 IP 限制功能。",
"password": "密碼",
+ "passwordDesc": "僅 Trojan 與 Shadowsocks 用戶端使用;VLESS、VMess、Hysteria 和 WireGuard 會忽略此項。",
"subId": "訂閱 ID",
"online": "上線",
"email": "電子郵件",
@@ -906,6 +907,7 @@
"delOrphansConfirmContent": "移除所有未關聯任何入站的客戶端,連同其流量紀錄一併刪除。此操作無法復原。",
"auth": "認證",
"hysteriaAuth": "Hysteria 認證",
+ "hysteriaAuthDesc": "僅 Hysteria 用戶端使用的憑證。Trojan 與 Shadowsocks 請改用上方的「密碼」欄位。",
"uuid": "UUID",
"flow": "Flow",
"vmessSecurity": "VMess 加密",