This commit is contained in:
MHSanaei 2026-04-19 22:43:31 +02:00
parent 8f74ccdda6
commit 0e6d54917e
No known key found for this signature in database
GPG key ID: 7E4060F2FBE5AB7A
17 changed files with 170 additions and 24 deletions

View file

@ -160,10 +160,10 @@ func (s *SubClashService) getProxies(inbound *model.Inbound, client model.Client
func (s *SubClashService) buildProxy(inbound *model.Inbound, client model.Client, stream map[string]any, extraRemark string) map[string]any {
proxy := map[string]any{
"name": s.SubService.genRemark(inbound, client.Email, extraRemark),
"name": s.SubService.genRemark(inbound, client.Email, extraRemark),
"server": inbound.Listen,
"port": inbound.Port,
"udp": true,
"port": inbound.Port,
"udp": true,
}
network, _ := stream["network"].(string)

View file

@ -76,9 +76,9 @@ type AllSetting struct {
SubURI string `json:"subURI" form:"subURI"` // Subscription server URI
SubJsonPath string `json:"subJsonPath" form:"subJsonPath"` // Path for JSON subscription endpoint
SubJsonURI string `json:"subJsonURI" form:"subJsonURI"` // JSON subscription server URI
SubClashEnable bool `json:"subClashEnable" form:"subClashEnable"` // Enable Clash/Mihomo subscription endpoint
SubClashPath string `json:"subClashPath" form:"subClashPath"` // Path for Clash/Mihomo subscription endpoint
SubClashURI string `json:"subClashURI" form:"subClashURI"` // Clash/Mihomo subscription server URI
SubClashEnable bool `json:"subClashEnable" form:"subClashEnable"` // Enable Clash/Mihomo subscription endpoint
SubClashPath string `json:"subClashPath" form:"subClashPath"` // Path for Clash/Mihomo subscription endpoint
SubClashURI string `json:"subClashURI" form:"subClashURI"` // Clash/Mihomo subscription server URI
SubJsonFragment string `json:"subJsonFragment" form:"subJsonFragment"` // JSON subscription fragment configuration
SubJsonNoises string `json:"subJsonNoises" form:"subJsonNoises"` // JSON subscription noise configuration
SubJsonMux string `json:"subJsonMux" form:"subJsonMux"` // JSON subscription mux configuration

View file

@ -1059,6 +1059,7 @@
},
showNord() {
nordModal.show();
},
async loadCustomGeoAliases() {
try {
const msg = await HttpUtil.get('/panel/api/custom-geo/aliases');

View file

@ -14,13 +14,18 @@ type NordService struct {
SettingService
}
var nordHTTPClient = &http.Client{Timeout: 15 * time.Second}
// maxResponseSize limits the maximum size of NordVPN API responses (10 MB).
const maxResponseSize = 10 << 20
func (s *NordService) GetCountries() (string, error) {
resp, err := http.Get("https://api.nordvpn.com/v1/countries")
resp, err := nordHTTPClient.Get("https://api.nordvpn.com/v1/countries")
if err != nil {
return "", err
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
body, err := io.ReadAll(io.LimitReader(resp.Body, maxResponseSize))
if err != nil {
return "", err
}
@ -28,13 +33,19 @@ func (s *NordService) GetCountries() (string, error) {
}
func (s *NordService) GetServers(countryId string) (string, error) {
// Validate countryId is numeric to prevent URL injection
for _, c := range countryId {
if c < '0' || c > '9' {
return "", common.NewError("invalid country ID")
}
}
url := fmt.Sprintf("https://api.nordvpn.com/v2/servers?limit=0&filters[servers_technologies][id]=35&filters[country_id]=%s", countryId)
resp, err := http.Get(url)
resp, err := nordHTTPClient.Get(url)
if err != nil {
return "", err
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
body, err := io.ReadAll(io.LimitReader(resp.Body, maxResponseSize))
if err != nil {
return "", err
}
@ -63,12 +74,18 @@ func (s *NordService) GetServers(countryId string) (string, error) {
}
func (s *NordService) SetKey(privateKey string) (string, error) {
if privateKey == "" {
return "", common.NewError("private key cannot be empty")
}
nordData := map[string]string{
"private_key": privateKey,
"token": "", // No token for manual key
"token": "",
}
data, _ := json.Marshal(nordData)
s.SettingService.SetNord(string(data))
err := s.SettingService.SetNord(string(data))
if err != nil {
return "", err
}
return string(data), nil
}
@ -91,7 +108,7 @@ func (s *NordService) GetCredentials(token string) (string, error) {
return "", common.NewErrorf("NordVPN API error: %s", resp.Status)
}
body, err := io.ReadAll(resp.Body)
body, err := io.ReadAll(io.LimitReader(resp.Body, maxResponseSize))
if err != nil {
return "", err
}
@ -111,7 +128,10 @@ func (s *NordService) GetCredentials(token string) (string, error) {
"token": token,
}
data, _ := json.Marshal(nordData)
s.SettingService.SetNord(string(data))
err = s.SettingService.SetNord(string(data))
if err != nil {
return "", err
}
return string(data), nil
}

View file

@ -767,13 +767,13 @@ func extractHostname(host string) string {
func (s *SettingService) GetDefaultSettings(host string) (any, error) {
type settingFunc func() (any, error)
settings := map[string]settingFunc{
"expireDiff": func() (any, error) { return s.GetExpireDiff() },
"trafficDiff": func() (any, error) { return s.GetTrafficDiff() },
"pageSize": func() (any, error) { return s.GetPageSize() },
"defaultCert": func() (any, error) { return s.GetCertFile() },
"defaultKey": func() (any, error) { return s.GetKeyFile() },
"tgBotEnable": func() (any, error) { return s.GetTgbotEnabled() },
"subEnable": func() (any, error) { return s.GetSubEnable() },
"expireDiff": func() (any, error) { return s.GetExpireDiff() },
"trafficDiff": func() (any, error) { return s.GetTrafficDiff() },
"pageSize": func() (any, error) { return s.GetPageSize() },
"defaultCert": func() (any, error) { return s.GetCertFile() },
"defaultKey": func() (any, error) { return s.GetKeyFile() },
"tgBotEnable": func() (any, error) { return s.GetTgbotEnabled() },
"subEnable": func() (any, error) { return s.GetSubEnable() },
"subJsonEnable": func() (any, error) { return s.GetSubJsonEnable() },
"subClashEnable": func() (any, error) { return s.GetSubClashEnable() },
"subTitle": func() (any, error) { return s.GetSubTitle() },
@ -781,8 +781,8 @@ func (s *SettingService) GetDefaultSettings(host string) (any, error) {
"subJsonURI": func() (any, error) { return s.GetSubJsonURI() },
"subClashURI": func() (any, error) { return s.GetSubClashURI() },
"remarkModel": func() (any, error) { return s.GetRemarkModel() },
"datepicker": func() (any, error) { return s.GetDatepicker() },
"ipLimitEnable": func() (any, error) { return s.GetIpLimitEnable() },
"datepicker": func() (any, error) { return s.GetDatepicker() },
"ipLimitEnable": func() (any, error) { return s.GetIpLimitEnable() },
}
result := make(map[string]any)

View file

@ -4,6 +4,8 @@
"confirm" = "تأكيد"
"cancel" = "إلغاء"
"close" = "إغلاق"
"save" = "حفظ"
"logout" = "تسجيل خروج"
"create" = "إنشاء"
"update" = "تحديث"
"copy" = "نسخ"
@ -496,6 +498,8 @@
"ipv4RoutingDesc" = "الخيارات دي هتوجه الترافيك بناءً على وجهة معينة عبر IPv4."
"warpRouting" = "توجيه WARP"
"warpRoutingDesc" = "الخيارات دي هتوجه الترافيك بناءً على وجهة معينة عبر WARP."
"nordRouting" = "توجيه NordVPN"
"nordRoutingDesc" = "الخيارات دي هتوجه الترافيك بناءً على وجهة معينة عبر NordVPN."
"Template" = "قالب إعدادات Xray المتقدم"
"TemplateDesc" = "ملف إعدادات Xray النهائي هيتولد بناءً على القالب ده."
"FreedomStrategy" = "استراتيجية بروتوكول الحرية"
@ -573,6 +577,14 @@
"testSuccess" = "الاختبار ناجح"
"testFailed" = "فشل الاختبار"
"testError" = "فشل اختبار المخرج"
"nordvpn" = "NordVPN"
"accessToken" = "رمز الوصول"
"country" = "الدولة"
"server" = "الخادم"
"city" = "المدينة"
"allCities" = "كل المدن"
"privateKey" = "المفتاح الخاص"
"load" = "الحمل"
[pages.xray.balancer]
"addBalancer" = "أضف موازن تحميل"

View file

@ -4,6 +4,8 @@
"confirm" = "Confirmar"
"cancel" = "Cancelar"
"close" = "Cerrar"
"save" = "Guardar"
"logout" = "Cerrar Sesión"
"create" = "Crear"
"update" = "Actualizar"
"copy" = "Copiar"
@ -496,6 +498,8 @@
"ipv4RoutingDesc" = "Estas opciones solo enrutarán a los dominios objetivo a través de IPv4."
"warpRouting" = "Enrutamiento WARP"
"warpRoutingDesc" = "Precaución: Antes de usar estas opciones, instale WARP en modo de proxy socks5 en su servidor siguiendo los pasos en el GitHub del panel. WARP enrutará el tráfico a los sitios web a través de los servidores de Cloudflare."
"nordRouting" = "Enrutamiento NordVPN"
"nordRoutingDesc" = "Estas opciones enrutarán el tráfico basado en un destino específico a través de NordVPN."
"Template" = "Plantilla de Configuración de Xray"
"TemplateDesc" = "Genera el archivo de configuración final de Xray basado en esta plantilla."
"FreedomStrategy" = "Configurar Estrategia para el Protocolo Freedom"
@ -573,6 +577,14 @@
"testSuccess" = "Prueba exitosa"
"testFailed" = "Prueba fallida"
"testError" = "Error al probar la salida"
"nordvpn" = "NordVPN"
"accessToken" = "Token de acceso"
"country" = "País"
"server" = "Servidor"
"city" = "Ciudad"
"allCities" = "Todas las ciudades"
"privateKey" = "Clave privada"
"load" = "Carga"
[pages.xray.balancer]
"addBalancer" = "Agregar equilibrador"

View file

@ -582,6 +582,8 @@
"country" = "کشور"
"server" = "سرور"
"privateKey" = "کلید خصوصی"
"city" = "شهر"
"allCities" = "همه شهرها"
"load" = "فشار سرور"
[pages.xray.balancer]

View file

@ -4,6 +4,8 @@
"confirm" = "Konfirmasi"
"cancel" = "Batal"
"close" = "Tutup"
"save" = "Simpan"
"logout" = "Keluar"
"create" = "Buat"
"update" = "Perbarui"
"copy" = "Salin"
@ -496,6 +498,8 @@
"ipv4RoutingDesc" = "Opsi ini akan mengalihkan lalu lintas berdasarkan tujuan tertentu melalui IPv4."
"warpRouting" = "Perutean WARP"
"warpRoutingDesc" = "Opsi ini akan mengalihkan lalu lintas berdasarkan tujuan tertentu melalui WARP."
"nordRouting" = "Routing NordVPN"
"nordRoutingDesc" = "Opsi ini akan mengalihkan lalu lintas berdasarkan tujuan tertentu melalui NordVPN."
"Template" = "Template Konfigurasi Xray Lanjutan"
"TemplateDesc" = "File konfigurasi Xray akhir akan dibuat berdasarkan template ini."
"FreedomStrategy" = "Strategi Protokol Freedom"
@ -573,6 +577,14 @@
"testSuccess" = "Tes berhasil"
"testFailed" = "Tes gagal"
"testError" = "Gagal menguji outbound"
"nordvpn" = "NordVPN"
"accessToken" = "Token Akses"
"country" = "Negara"
"server" = "Server"
"city" = "Kota"
"allCities" = "Semua Kota"
"privateKey" = "Kunci Privat"
"load" = "Beban"
[pages.xray.balancer]
"addBalancer" = "Tambahkan Penyeimbang"

View file

@ -4,6 +4,8 @@
"confirm" = "確認"
"cancel" = "キャンセル"
"close" = "閉じる"
"save" = "保存"
"logout" = "ログアウト"
"create" = "作成"
"update" = "更新"
"copy" = "コピー"
@ -496,6 +498,8 @@
"ipv4RoutingDesc" = "このオプションはIPv4のみを介してターゲットドメインへルーティングします"
"warpRouting" = "WARP ルーティング"
"warpRoutingDesc" = "注意これらのオプションを使用する前に、パネルのGitHubの手順に従って、サーバーにsocks5プロキシモードでWARPをインストールしてください。WARPはCloudflareサーバー経由でトラフィックをウェブサイトにルーティングします。"
"nordRouting" = "NordVPN ルーティング"
"nordRoutingDesc" = "これらのオプションはNordVPN経由で特定の宛先にトラフィックをルーティングします。"
"Template" = "高度なXray設定テンプレート"
"TemplateDesc" = "最終的なXray設定ファイルはこのテンプレートに基づいて生成されます"
"FreedomStrategy" = "Freedom プロトコル戦略"
@ -573,6 +577,14 @@
"testSuccess" = "テスト成功"
"testFailed" = "テスト失敗"
"testError" = "アウトバウンドのテストに失敗しました"
"nordvpn" = "NordVPN"
"accessToken" = "アクセストークン"
"country" = "国"
"server" = "サーバー"
"city" = "都市"
"allCities" = "すべての都市"
"privateKey" = "秘密鍵"
"load" = "負荷"
[pages.xray.balancer]
"addBalancer" = "負荷分散追加"

View file

@ -4,6 +4,8 @@
"confirm" = "Confirmar"
"cancel" = "Cancelar"
"close" = "Fechar"
"save" = "Salvar"
"logout" = "Sair"
"create" = "Criar"
"update" = "Atualizar"
"copy" = "Copiar"
@ -496,6 +498,8 @@
"ipv4RoutingDesc" = "Essas opções roteam o tráfego para um destino específico via IPv4."
"warpRouting" = "Roteamento WARP"
"warpRoutingDesc" = "Essas opções roteam o tráfego para um destino específico via WARP."
"nordRouting" = "Roteamento NordVPN"
"nordRoutingDesc" = "Essas opções roteiam o tráfego para um destino específico via NordVPN."
"Template" = "Modelo de Configuração Avançada do Xray"
"TemplateDesc" = "O arquivo final de configuração do Xray será gerado com base neste modelo."
"FreedomStrategy" = "Estratégia do Protocolo Freedom"
@ -573,6 +577,14 @@
"testSuccess" = "Teste bem-sucedido"
"testFailed" = "Teste falhou"
"testError" = "Falha ao testar saída"
"nordvpn" = "NordVPN"
"accessToken" = "Token de Acesso"
"country" = "País"
"server" = "Servidor"
"city" = "Cidade"
"allCities" = "Todas as Cidades"
"privateKey" = "Chave Privada"
"load" = "Carga"
[pages.xray.balancer]
"addBalancer" = "Adicionar Balanceador"

View file

@ -4,6 +4,8 @@
"confirm" = "Подтвердить"
"cancel" = "Отмена"
"close" = "Закрыть"
"save" = "Сохранить"
"logout" = "Выход"
"create" = "Создать"
"update" = "Обновить"
"copy" = "Копировать"
@ -495,7 +497,9 @@
"ipv4Routing" = "Правила IPv4"
"ipv4RoutingDesc" = "Эти параметры позволят клиентам маршрутизироваться к целевым доменам только через IPv4"
"warpRouting" = "Правила WARP"
"warpRoutingDesc" = " Эти опции будут направлять трафик в зависимости от конкретного пункта назначения через WARP."
"warpRoutingDesc" = " Эти опции будут направлять трафик в зависимости от конкретного пункта назначения через WARP."
"nordRouting" = "Маршрутизация NordVPN"
"nordRoutingDesc" = "Эти опции будут направлять трафик в зависимости от конкретного пункта назначения через NordVPN."
"Template" = "Шаблон конфигурации Xray"
"TemplateDesc" = "На основе шаблона создаётся конфигурационный файл Xray."
"FreedomStrategy" = "Настройка стратегии протокола Freedom"
@ -573,6 +577,14 @@
"testSuccess" = "Тест успешен"
"testFailed" = "Тест не пройден"
"testError" = "Не удалось протестировать исходящее подключение"
"nordvpn" = "NordVPN"
"accessToken" = "Токен доступа"
"country" = "Страна"
"server" = "Сервер"
"city" = "Город"
"allCities" = "Все города"
"privateKey" = "Приватный ключ"
"load" = "Нагрузка"
[pages.xray.balancer]
"addBalancer" = "Создать балансировщик"

View file

@ -4,6 +4,8 @@
"confirm" = "Onayla"
"cancel" = "İptal"
"close" = "Kapat"
"save" = "Kaydet"
"logout" = ıkış Yap"
"create" = "Oluştur"
"update" = "Güncelle"
"copy" = "Kopyala"
@ -496,6 +498,8 @@
"ipv4RoutingDesc" = "Bu seçenekler belirli bir varış yerine IPv4 üzerinden trafiği yönlendirir."
"warpRouting" = "WARP Yönlendirme"
"warpRoutingDesc" = "Bu seçenekler belirli bir varış yerine WARP üzerinden trafiği yönlendirir."
"nordRouting" = "NordVPN Yönlendirme"
"nordRoutingDesc" = "Bu seçenekler belirli bir varış yerine NordVPN üzerinden trafiği yönlendirir."
"Template" = "Gelişmiş Xray Yapılandırma Şablonu"
"TemplateDesc" = "Nihai Xray yapılandırma dosyası bu şablona göre oluşturulacaktır."
"FreedomStrategy" = "Freedom Protokol Stratejisi"
@ -573,6 +577,14 @@
"testSuccess" = "Test başarılı"
"testFailed" = "Test başarısız"
"testError" = "Giden test edilemedi"
"nordvpn" = "NordVPN"
"accessToken" = "Erişim Jetonu"
"country" = "Ülke"
"server" = "Sunucu"
"city" = "Şehir"
"allCities" = "Tüm Şehirler"
"privateKey" = "Özel Anahtar"
"load" = "Yük"
[pages.xray.balancer]
"addBalancer" = "Dengeleyici Ekle"

View file

@ -4,6 +4,8 @@
"confirm" = "Підтвердити"
"cancel" = "Скасувати"
"close" = "Закрити"
"save" = "Зберегти"
"logout" = "Вийти"
"create" = "Створити"
"update" = "Оновити"
"copy" = "Копіювати"
@ -496,6 +498,8 @@
"ipv4RoutingDesc" = "Ці параметри спрямовуватимуть трафік на основі певного призначення через IPv4."
"warpRouting" = "WARP Маршрутизація"
"warpRoutingDesc" = "Ці параметри маршрутизуватимуть трафік на основі певного пункту призначення через WARP."
"nordRouting" = "Маршрутизація NordVPN"
"nordRoutingDesc" = "Ці параметри маршрутизуватимуть трафік на основі певного пункту призначення через NordVPN."
"Template" = "Шаблон розширеної конфігурації Xray"
"TemplateDesc" = "Остаточний конфігураційний файл Xray буде створено на основі цього шаблону."
"FreedomStrategy" = "Стратегія протоколу свободи"
@ -573,6 +577,14 @@
"testSuccess" = "Тест успішний"
"testFailed" = "Тест не пройдено"
"testError" = "Не вдалося протестувати вихідне з'єднання"
"nordvpn" = "NordVPN"
"accessToken" = "Токен доступу"
"country" = "Країна"
"server" = "Сервер"
"city" = "Місто"
"allCities" = "Усі міста"
"privateKey" = "Приватний ключ"
"load" = "Навантаження"
[pages.xray.balancer]
"addBalancer" = "Додати балансир"

View file

@ -4,6 +4,8 @@
"confirm" = "Xác nhận"
"cancel" = "Hủy bỏ"
"close" = "Đóng"
"save" = "Lưu"
"logout" = "Đăng xuất"
"create" = "Tạo"
"update" = "Cập nhật"
"copy" = "Sao chép"
@ -496,6 +498,8 @@
"ipv4RoutingDesc" = "Những tùy chọn này sẽ chỉ định kết nối đến các tên miền mục tiêu qua IPv4."
"warpRouting" = "Định tuyến WARP"
"warpRoutingDesc" = "Cảnh báo: Trước khi sử dụng những tùy chọn này, hãy cài đặt WARP ở chế độ proxy socks5 trên máy chủ của bạn bằng cách làm theo các bước trên GitHub của bảng điều khiển. WARP sẽ định tuyến lưu lượng đến các trang web qua máy chủ Cloudflare."
"nordRouting" = "Định tuyến NordVPN"
"nordRoutingDesc" = "Các tùy chọn này sẽ định tuyến lưu lượng dựa trên đích cụ thể qua NordVPN."
"Template" = "Mẫu Cấu hình Xray"
"TemplateDesc" = "Tạo tệp cấu hình Xray cuối cùng dựa trên mẫu này."
"FreedomStrategy" = "Cấu hình Chiến lược cho Giao thức Freedom"
@ -573,6 +577,14 @@
"testSuccess" = "Kiểm tra thành công"
"testFailed" = "Kiểm tra thất bại"
"testError" = "Không thể kiểm tra đầu ra"
"nordvpn" = "NordVPN"
"accessToken" = "Mã truy cập"
"country" = "Quốc gia"
"server" = "Máy chủ"
"city" = "Thành phố"
"allCities" = "Tất cả thành phố"
"privateKey" = "Khóa riêng"
"load" = "Tải"
[pages.xray.balancer]
"addBalancer" = "Thêm cân bằng"

View file

@ -574,6 +574,9 @@
"test" = "测试"
"testResult" = "测试结果"
"testing" = "正在测试连接..."
"testSuccess" = "测试成功"
"testFailed" = "测试失败"
"testError" = "测试出站失败"
"nordvpn" = "NordVPN"
"accessToken" = "访问令牌"
"country" = "国家"

View file

@ -4,6 +4,8 @@
"confirm" = "確定"
"cancel" = "取消"
"close" = "關閉"
"save" = "儲存"
"logout" = "登出"
"create" = "建立"
"update" = "更新"
"copy" = "複製"
@ -496,6 +498,8 @@
"ipv4RoutingDesc" = "此選項將僅通過 IPv4 路由到目標域"
"warpRouting" = "WARP 路由"
"warpRoutingDesc" = "注意:在使用這些選項之前,請按照面板 GitHub 上的步驟在你的伺服器上以 socks5 代理模式安裝 WARP。WARP 將通過 Cloudflare 伺服器將流量路由到網站。"
"nordRouting" = "NordVPN 路由"
"nordRoutingDesc" = "這些選項將根據特定目的地通過 NordVPN 路由流量。"
"Template" = "高階 Xray 配置模板"
"TemplateDesc" = "最終的 Xray 配置檔案將基於此模板生成"
"FreedomStrategy" = "Freedom 協議策略"
@ -573,6 +577,14 @@
"testSuccess" = "測試成功"
"testFailed" = "測試失敗"
"testError" = "測試出站失敗"
"nordvpn" = "NordVPN"
"accessToken" = "訪問令牌"
"country" = "國家"
"server" = "伺服器"
"city" = "城市"
"allCities" = "所有城市"
"privateKey" = "私密金鑰"
"load" = "負載"
[pages.xray.balancer]
"addBalancer" = "新增負載均衡"