mirror of
https://github.com/MatsuriDayo/nekoray.git
synced 2026-01-02 16:09:00 +08:00
Compare commits
23 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
adef6cd4af | ||
|
|
399b171adf | ||
|
|
6e4c180428 | ||
|
|
75c6496151 | ||
|
|
62c59f6fd3 | ||
|
|
12d6fc24e7 | ||
|
|
6de7c588b6 | ||
|
|
99c8d50943 | ||
|
|
2a177256ce | ||
|
|
b1d1674912 | ||
|
|
2dd9cf45eb | ||
|
|
96a6586c5e | ||
|
|
375ffec58d | ||
|
|
7aa863b881 | ||
|
|
79838d8679 | ||
|
|
61e27f4014 | ||
|
|
e868de0209 | ||
|
|
6f81ba7773 | ||
|
|
be0eae662b | ||
|
|
f6cf1414bf | ||
|
|
7c783308a7 | ||
|
|
cd9bb8f72d | ||
|
|
df141f9079 |
16
.github/workflows/build-nekoray-cmake.yml
vendored
16
.github/workflows/build-nekoray-cmake.yml
vendored
@ -4,20 +4,20 @@ on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
tag:
|
||||
description: 'Release Tag'
|
||||
description: "Release Tag"
|
||||
required: true
|
||||
publish:
|
||||
description: 'Publish: If want ignore'
|
||||
description: "Publish: If want ignore"
|
||||
required: false
|
||||
artifact-pack:
|
||||
description: 'artifact-pack: If want ignore'
|
||||
description: "artifact-pack: If want ignore"
|
||||
required: false
|
||||
jobs:
|
||||
build-go:
|
||||
strategy:
|
||||
matrix:
|
||||
cross_os: [ windows, linux ]
|
||||
cross_arch: [ amd64 ]
|
||||
cross_os: [windows, linux]
|
||||
cross_arch: [amd64]
|
||||
include:
|
||||
- cross_os: public_res
|
||||
cross_arch: public_res
|
||||
@ -38,7 +38,7 @@ jobs:
|
||||
if: steps.cache-common.outputs.cache-hit != 'true'
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: ^1.21
|
||||
go-version: ^1.22
|
||||
- name: Build golang parts
|
||||
if: steps.cache-common.outputs.cache-hit != 'true'
|
||||
shell: bash
|
||||
@ -61,7 +61,7 @@ jobs:
|
||||
include:
|
||||
- platform: windows-2022
|
||||
arch: x64
|
||||
qt_version: "6.5"
|
||||
qt_version: "6.7"
|
||||
- platform: ubuntu-20.04
|
||||
arch: x64
|
||||
qt_version: "5.12"
|
||||
@ -196,7 +196,7 @@ jobs:
|
||||
cp -r public_res/* linux64
|
||||
####
|
||||
bash ../libs/package_appimage.sh
|
||||
mv nekoray-x86_64.AppImage $version_standalone-linux-x64.AppImage
|
||||
mv nekobox-x86_64.AppImage $version_standalone-linux-x64.AppImage
|
||||
- name: Clean Up
|
||||
run: |
|
||||
cd deployment
|
||||
|
||||
18
.github/workflows/update-pkgbuild.yml
vendored
Normal file
18
.github/workflows/update-pkgbuild.yml
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
name: AUR CI
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
- 'LICENSE'
|
||||
- '!.github/workflows/**'
|
||||
|
||||
jobs:
|
||||
update:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: chitang233/aur-pkgbuild-builder@main
|
||||
with:
|
||||
deploy_key: ${{ secrets.DEPLOY_KEY }}
|
||||
package_name: 'nekoray-git'
|
||||
@ -353,7 +353,7 @@ namespace Qv2ray::components::proxy {
|
||||
// execute and get the code
|
||||
const auto returnCode = QProcess::execute(action.first, action.second);
|
||||
// print out the commands and result codes
|
||||
DEBUG(QString("[%1] Program: %2, Args: %3").arg(returnCode).arg(action.first).arg(action.second.join(";")));
|
||||
DEBUG(QStringLiteral("[%1] Program: %2, Args: %3").arg(returnCode).arg(action.first).arg(action.second.join(";")));
|
||||
// give the code back
|
||||
results << (returnCode == QProcess::NormalExit);
|
||||
}
|
||||
@ -423,7 +423,7 @@ namespace Qv2ray::components::proxy {
|
||||
// execute and get the code
|
||||
const auto returnCode = QProcess::execute(action.first, action.second);
|
||||
// print out the commands and result codes
|
||||
DEBUG(QString("[%1] Program: %2, Args: %3").arg(returnCode).arg(action.first).arg(action.second.join(";")));
|
||||
DEBUG(QStringLiteral("[%1] Program: %2, Args: %3").arg(returnCode).arg(action.first).arg(action.second.join(";")));
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
132
3rdparty/qv2ray/v2/ui/LogHighlighter.cpp
vendored
132
3rdparty/qv2ray/v2/ui/LogHighlighter.cpp
vendored
@ -1,132 +0,0 @@
|
||||
#include "LogHighlighter.hpp"
|
||||
|
||||
#define TO_EOL "(([\\s\\S]*)|([\\d\\D]*)|([\\w\\W]*))$"
|
||||
#define REGEX_IPV6_ADDR \
|
||||
R"(\[\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*\])"
|
||||
#define REGEX_IPV4_ADDR \
|
||||
R"((\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5]))"
|
||||
#define REGEX_PORT_NUMBER R"(([0-9]|[1-9]\d{1,3}|[1-5]\d{4}|6[0-5]{2}[0-3][0-5])*)"
|
||||
|
||||
namespace Qv2ray::ui {
|
||||
SyntaxHighlighter::SyntaxHighlighter(bool darkMode, QTextDocument *parent) : QSyntaxHighlighter(parent) {
|
||||
HighlightingRule rule;
|
||||
|
||||
if (darkMode) {
|
||||
tcpudpFormat.setForeground(QColor(0, 200, 230));
|
||||
ipHostFormat.setForeground(Qt::yellow);
|
||||
warningFormat.setForeground(QColor(255, 160, 15));
|
||||
warningFormat2.setForeground(Qt::cyan);
|
||||
} else {
|
||||
ipHostFormat.setForeground(QColor(0, 52, 130));
|
||||
tcpudpFormat.setForeground(QColor(0, 52, 130));
|
||||
warningFormat.setBackground(QColor(255, 160, 15));
|
||||
warningFormat.setForeground(Qt::white);
|
||||
warningFormat2.setForeground(Qt::darkCyan);
|
||||
}
|
||||
const static QColor darkGreenColor(10, 180, 0);
|
||||
|
||||
acceptedFormat.setForeground(darkGreenColor);
|
||||
acceptedFormat.setFontItalic(true);
|
||||
acceptedFormat.setFontWeight(QFont::Bold);
|
||||
rule.pattern = QRegularExpression("\\saccepted\\s");
|
||||
rule.format = acceptedFormat;
|
||||
highlightingRules.append(rule);
|
||||
//
|
||||
rejectedFormat.setFontWeight(QFont::Bold);
|
||||
rejectedFormat.setBackground(Qt::red);
|
||||
rejectedFormat.setForeground(Qt::white);
|
||||
rejectedFormat.setFontWeight(QFont::Bold);
|
||||
rule.pattern = QRegularExpression("\\srejected\\s" TO_EOL);
|
||||
rule.format = rejectedFormat;
|
||||
highlightingRules.append(rule);
|
||||
//
|
||||
dateFormat.setForeground(darkMode ? Qt::cyan : Qt::darkCyan);
|
||||
rule.pattern = QRegularExpression("\\d\\d\\d\\d/\\d\\d/\\d\\d");
|
||||
rule.format = dateFormat;
|
||||
highlightingRules.append(rule);
|
||||
//
|
||||
timeFormat.setForeground(darkMode ? Qt::cyan : Qt::darkCyan);
|
||||
rule.pattern = QRegularExpression("\\d\\d:\\d\\d:\\d\\d");
|
||||
rule.format = timeFormat;
|
||||
highlightingRules.append(rule);
|
||||
//
|
||||
debugFormat.setForeground(Qt::darkGray);
|
||||
rule.pattern = QRegularExpression("\\[D[Ee][Bb][Uu].*?\\]");
|
||||
rule.format = debugFormat;
|
||||
highlightingRules.append(rule);
|
||||
//
|
||||
infoFormat.setForeground(darkMode ? Qt::lightGray : Qt::darkCyan);
|
||||
rule.pattern = QRegularExpression("\\[I[Nn][Ff][Oo].*?\\]");
|
||||
rule.format = infoFormat;
|
||||
highlightingRules.append(rule);
|
||||
//
|
||||
warningFormat.setFontWeight(QFont::Bold);
|
||||
warningFormat2.setFontWeight(QFont::Bold);
|
||||
rule.pattern = QRegularExpression("\\[W[Aa][Rr][Nn].*?\\]");
|
||||
rule.format = warningFormat2;
|
||||
highlightingRules.append(rule);
|
||||
//
|
||||
rule.pattern = QRegularExpression("\\[E[Rr][Rr][Oo].*?\\]");
|
||||
rule.format = rejectedFormat;
|
||||
highlightingRules.append(rule);
|
||||
|
||||
//
|
||||
v2rayComponentFormat.setForeground(darkMode ? darkGreenColor : Qt::darkYellow);
|
||||
rule.pattern = QRegularExpression(R"( (\w+\/)+\w+: )");
|
||||
rule.format = v2rayComponentFormat;
|
||||
highlightingRules.append(rule);
|
||||
//
|
||||
failedFormat.setFontWeight(QFont::Bold);
|
||||
failedFormat.setBackground(Qt::red);
|
||||
failedFormat.setForeground(Qt::white);
|
||||
rule.pattern = QRegularExpression("failed");
|
||||
rule.format = failedFormat;
|
||||
highlightingRules.append(rule);
|
||||
//
|
||||
rule.pattern = QRegularExpression(">>>>+");
|
||||
rule.format = warningFormat;
|
||||
highlightingRules.append(rule);
|
||||
//
|
||||
rule.pattern = QRegularExpression("<<<<+");
|
||||
rule.format = warningFormat;
|
||||
highlightingRules.append(rule);
|
||||
|
||||
{
|
||||
// IP IPv6 Host;
|
||||
rule.pattern = QRegularExpression(REGEX_IPV4_ADDR ":" REGEX_PORT_NUMBER);
|
||||
rule.pattern.setPatternOptions(QRegularExpression::ExtendedPatternSyntaxOption);
|
||||
rule.format = ipHostFormat;
|
||||
highlightingRules.append(rule);
|
||||
//
|
||||
rule.pattern = QRegularExpression(REGEX_IPV6_ADDR ":" REGEX_PORT_NUMBER);
|
||||
rule.pattern.setPatternOptions(QRegularExpression::ExtendedPatternSyntaxOption);
|
||||
rule.format = ipHostFormat;
|
||||
highlightingRules.append(rule);
|
||||
//
|
||||
rule.pattern = QRegularExpression("([a-zA-Z0-9]([a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9])?\\.)+[a-zA-Z]{2,6}(/|):" REGEX_PORT_NUMBER);
|
||||
rule.pattern.setPatternOptions(QRegularExpression::PatternOption::ExtendedPatternSyntaxOption);
|
||||
rule.format = ipHostFormat;
|
||||
highlightingRules.append(rule);
|
||||
}
|
||||
|
||||
for (const auto &pattern: {"tcp:", "udp:"}) {
|
||||
tcpudpFormat.setFontWeight(QFont::Bold);
|
||||
rule.pattern = QRegularExpression(pattern);
|
||||
rule.format = tcpudpFormat;
|
||||
highlightingRules.append(rule);
|
||||
}
|
||||
}
|
||||
|
||||
void SyntaxHighlighter::highlightBlock(const QString &text) {
|
||||
for (const HighlightingRule &rule: highlightingRules) {
|
||||
QRegularExpressionMatchIterator matchIterator = rule.pattern.globalMatch(text);
|
||||
|
||||
while (matchIterator.hasNext()) {
|
||||
QRegularExpressionMatch match = matchIterator.next();
|
||||
setFormat(match.capturedStart(), match.capturedLength(), rule.format);
|
||||
}
|
||||
}
|
||||
|
||||
setCurrentBlockState(0);
|
||||
}
|
||||
} // namespace Qv2ray::ui
|
||||
92
3rdparty/qv2ray/v2/ui/LogHighlighter.hpp
vendored
92
3rdparty/qv2ray/v2/ui/LogHighlighter.hpp
vendored
@ -1,92 +0,0 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the examples of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:BSD$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** BSD License Usage
|
||||
** Alternatively, you may use this file under the terms of the BSD license
|
||||
** as follows:
|
||||
**
|
||||
** "Redistribution and use in source and binary forms, with or without
|
||||
** modification, are permitted provided that the following conditions are
|
||||
** met:
|
||||
** * Redistributions of source code must retain the above copyright
|
||||
** notice, this list of conditions and the following disclaimer.
|
||||
** * Redistributions in binary form must reproduce the above copyright
|
||||
** notice, this list of conditions and the following disclaimer in
|
||||
** the documentation and/or other materials provided with the
|
||||
** distribution.
|
||||
** * Neither the name of The Qt Company Ltd nor the names of its
|
||||
** contributors may be used to endorse or promote products derived
|
||||
** from this software without specific prior written permission.
|
||||
**
|
||||
**
|
||||
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QRegularExpression>
|
||||
#include <QSyntaxHighlighter>
|
||||
#include <QTextCharFormat>
|
||||
#include <QTextDocument>
|
||||
|
||||
namespace Qv2ray {
|
||||
namespace ui {
|
||||
class SyntaxHighlighter : public QSyntaxHighlighter {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit SyntaxHighlighter(bool darkMode, QTextDocument *parent = nullptr);
|
||||
|
||||
protected:
|
||||
void highlightBlock(const QString &text) override;
|
||||
|
||||
private:
|
||||
struct HighlightingRule {
|
||||
QRegularExpression pattern;
|
||||
QTextCharFormat format;
|
||||
};
|
||||
QVector<HighlightingRule> highlightingRules;
|
||||
|
||||
QTextCharFormat tcpudpFormat;
|
||||
QTextCharFormat dateFormat;
|
||||
QTextCharFormat acceptedFormat;
|
||||
QTextCharFormat rejectedFormat;
|
||||
QTextCharFormat failedFormat;
|
||||
QTextCharFormat warningFormat;
|
||||
QTextCharFormat warningFormat2;
|
||||
QTextCharFormat infoFormat;
|
||||
QTextCharFormat debugFormat;
|
||||
QTextCharFormat timeFormat;
|
||||
QTextCharFormat ipHostFormat;
|
||||
QTextCharFormat v2rayComponentFormat;
|
||||
};
|
||||
} // namespace ui
|
||||
} // namespace Qv2ray
|
||||
|
||||
using namespace Qv2ray::ui;
|
||||
@ -191,13 +191,13 @@ QVariant QJsonModel::data(const QModelIndex &index, int role) const {
|
||||
|
||||
if (role == Qt::DisplayRole) {
|
||||
if (index.column() == 0)
|
||||
return QString("%1").arg(item->key());
|
||||
return QStringLiteral("%1").arg(item->key());
|
||||
|
||||
if (index.column() == 1)
|
||||
return QString("%1").arg(item->value());
|
||||
return QStringLiteral("%1").arg(item->value());
|
||||
} else if (Qt::EditRole == role) {
|
||||
if (index.column() == 1) {
|
||||
return QString("%1").arg(item->value());
|
||||
return QStringLiteral("%1").arg(item->value());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
project(nekoray VERSION 0.1 LANGUAGES CXX)
|
||||
project(nekobox VERSION 0.1 LANGUAGES CXX)
|
||||
|
||||
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||
|
||||
@ -131,7 +131,6 @@ set(PROJECT_SOURCES
|
||||
3rdparty/qrcodegen.cpp
|
||||
3rdparty/QtExtKeySequenceEdit.cpp
|
||||
|
||||
3rdparty/qv2ray/v2/ui/LogHighlighter.cpp
|
||||
3rdparty/qv2ray/v2/ui/QvAutoCompleteTextEdit.cpp
|
||||
3rdparty/qv2ray/v2/components/proxy/QvProxyConfigurator.cpp
|
||||
3rdparty/qv2ray/v2/ui/widgets/common/QJsonModel.cpp
|
||||
@ -150,7 +149,6 @@ set(PROJECT_SOURCES
|
||||
db/ConfigBuilder.cpp
|
||||
|
||||
fmt/AbstractBean.cpp
|
||||
fmt/Bean2CoreObj_ray.cpp
|
||||
fmt/Bean2CoreObj_box.cpp
|
||||
fmt/Bean2External.cpp
|
||||
fmt/Bean2Link.cpp
|
||||
@ -239,23 +237,23 @@ set(PROJECT_SOURCES
|
||||
|
||||
# Qt exe
|
||||
if (${QT_VERSION_MAJOR} GREATER_EQUAL 6)
|
||||
qt_add_executable(nekoray
|
||||
qt_add_executable(nekobox
|
||||
MANUAL_FINALIZATION
|
||||
${PROJECT_SOURCES}
|
||||
)
|
||||
# Define target properties for Android with Qt 6 as:
|
||||
# set_property(TARGET nekoray APPEND PROPERTY QT_ANDROID_PACKAGE_SOURCE_DIR
|
||||
# set_property(TARGET nekobox APPEND PROPERTY QT_ANDROID_PACKAGE_SOURCE_DIR
|
||||
# ${CMAKE_CURRENT_SOURCE_DIR}/android)
|
||||
# For more information, see https://doc.qt.io/qt-6/qt-add-executable.html#target-creation
|
||||
else ()
|
||||
if (ANDROID)
|
||||
add_library(nekoray SHARED
|
||||
add_library(nekobox SHARED
|
||||
${PROJECT_SOURCES}
|
||||
)
|
||||
# Define properties for Android with Qt 5 after find_package() calls as:
|
||||
# set(ANDROID_PACKAGE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/android")
|
||||
else ()
|
||||
add_executable(nekoray
|
||||
add_executable(nekobox
|
||||
${PROJECT_SOURCES}
|
||||
)
|
||||
endif ()
|
||||
@ -263,9 +261,13 @@ endif ()
|
||||
|
||||
# Target
|
||||
|
||||
set_property(TARGET nekoray PROPERTY AUTOUIC ON)
|
||||
set_property(TARGET nekoray PROPERTY AUTOMOC ON)
|
||||
set_property(TARGET nekoray PROPERTY AUTORCC ON)
|
||||
set_property(TARGET nekobox PROPERTY AUTOUIC ON)
|
||||
set_property(TARGET nekobox PROPERTY AUTOMOC ON)
|
||||
set_property(TARGET nekobox PROPERTY AUTORCC ON)
|
||||
|
||||
set_target_properties(nekobox PROPERTIES
|
||||
WIN32_EXECUTABLE TRUE
|
||||
)
|
||||
|
||||
# Target Source Translations
|
||||
|
||||
@ -278,17 +280,17 @@ set(LUPDATE_OPTIONS
|
||||
-locations none -no-obsolete
|
||||
)
|
||||
if (${QT_VERSION_MAJOR} GREATER_EQUAL 6)
|
||||
qt_add_lupdate(nekoray TS_FILES ${TS_FILES} OPTIONS ${LUPDATE_OPTIONS})
|
||||
qt_add_lrelease(nekoray TS_FILES ${TS_FILES} QM_FILES_OUTPUT_VARIABLE QM_FILES)
|
||||
qt_add_lupdate(nekobox TS_FILES ${TS_FILES} OPTIONS ${LUPDATE_OPTIONS})
|
||||
qt_add_lrelease(nekobox TS_FILES ${TS_FILES} QM_FILES_OUTPUT_VARIABLE QM_FILES)
|
||||
else ()
|
||||
qt5_create_translation(QM_FILES ${PROJECT_SOURCES} ${TS_FILES} OPTIONS ${LUPDATE_OPTIONS})
|
||||
endif ()
|
||||
configure_file(translations/translations.qrc ${CMAKE_BINARY_DIR} COPYONLY)
|
||||
target_sources(nekoray PRIVATE ${CMAKE_BINARY_DIR}/translations.qrc)
|
||||
target_sources(nekobox PRIVATE ${CMAKE_BINARY_DIR}/translations.qrc)
|
||||
|
||||
# Target Link
|
||||
|
||||
target_link_libraries(nekoray PRIVATE
|
||||
target_link_libraries(nekobox PRIVATE
|
||||
Qt${QT_VERSION_MAJOR}::Widgets Qt${QT_VERSION_MAJOR}::Network Qt${QT_VERSION_MAJOR}::Svg
|
||||
Threads::Threads
|
||||
${NKR_EXTERNAL_TARGETS}
|
||||
@ -296,5 +298,5 @@ target_link_libraries(nekoray PRIVATE
|
||||
)
|
||||
|
||||
if (QT_VERSION_MAJOR EQUAL 6)
|
||||
qt_finalize_executable(nekoray)
|
||||
qt_finalize_executable(nekobox)
|
||||
endif ()
|
||||
|
||||
11
README.md
11
README.md
@ -8,16 +8,6 @@ Support Windows / Linux out of the box now.
|
||||
|
||||
目前支持 Windows / Linux 开箱即用
|
||||
|
||||
## 4.x 开发计划
|
||||
|
||||
软件定位:电脑端节点调试软件。更新频率随机,可用性无保证。机场订阅用户建议使用 Clash Verge Rev 等。
|
||||
|
||||
1. 移除 Xray 核心,更新 sing-box。
|
||||
2. 移除一些没用的功能。
|
||||
3. 更新文档。
|
||||
4. 更新部分依赖
|
||||
5. 移除 macos 遗留
|
||||
|
||||
## 下载 / Download
|
||||
|
||||
### GitHub Releases (Portable ZIP)
|
||||
@ -64,7 +54,6 @@ https://matsuridayo.github.io
|
||||
- Trojan
|
||||
- TUIC ( sing-box )
|
||||
- NaïveProxy ( Custom Core )
|
||||
- Hysteria ( Custom Core or sing-box )
|
||||
- Hysteria2 ( Custom Core or sing-box )
|
||||
- Custom Outbound
|
||||
- Custom Config
|
||||
|
||||
18
appdmg.json
18
appdmg.json
@ -1,18 +0,0 @@
|
||||
{
|
||||
"title": "nekoray",
|
||||
"icon": "res/nekoray.icns",
|
||||
"contents": [
|
||||
{
|
||||
"x": 448,
|
||||
"y": 344,
|
||||
"type": "link",
|
||||
"path": "/Applications"
|
||||
},
|
||||
{
|
||||
"x": 192,
|
||||
"y": 344,
|
||||
"type": "file",
|
||||
"path": "build/nekoray.app"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -4,12 +4,12 @@ set(PLATFORM_LIBRARIES wininet wsock32 ws2_32 user32 rasapi32 iphlpapi)
|
||||
include(cmake/windows/generate_product_version.cmake)
|
||||
generate_product_version(
|
||||
QV2RAY_RC
|
||||
ICON "${CMAKE_SOURCE_DIR}/res/nekoray.ico"
|
||||
NAME "nekoray"
|
||||
BUNDLE "nekoray"
|
||||
COMPANY_NAME "nekoray"
|
||||
COMPANY_COPYRIGHT "nekoray"
|
||||
FILE_DESCRIPTION "nekoray"
|
||||
ICON "${CMAKE_SOURCE_DIR}/res/nekobox.ico"
|
||||
NAME "nekobox"
|
||||
BUNDLE "nekobox"
|
||||
COMPANY_NAME "nekobox"
|
||||
COMPANY_COPYRIGHT "nekobox"
|
||||
FILE_DESCRIPTION "nekobox"
|
||||
)
|
||||
add_definitions(-DUNICODE -D_UNICODE -DNOMINMAX)
|
||||
set(GUI_TYPE WIN32)
|
||||
|
||||
@ -22,30 +22,48 @@ namespace NekoGui {
|
||||
}
|
||||
|
||||
QString genTunName() {
|
||||
auto tun_name = "nekoray-tun";
|
||||
auto tun_name = "neko-tun";
|
||||
#ifdef Q_OS_MACOS
|
||||
tun_name = "utun9";
|
||||
#endif
|
||||
return tun_name;
|
||||
}
|
||||
|
||||
void MergeJson(const QJsonObject &custom, QJsonObject &outbound) {
|
||||
void MergeJson(QJsonObject &dst, const QJsonObject &src) {
|
||||
// 合并
|
||||
if (custom.isEmpty()) return;
|
||||
for (const auto &key: custom.keys()) {
|
||||
if (outbound.contains(key)) {
|
||||
auto v = custom[key];
|
||||
auto v_orig = outbound[key];
|
||||
if (v.isObject() && v_orig.isObject()) { // isObject 则合并?
|
||||
auto vo = v.toObject();
|
||||
QJsonObject vo_orig = v_orig.toObject();
|
||||
MergeJson(vo, vo_orig);
|
||||
outbound[key] = vo_orig;
|
||||
if (src.isEmpty()) return;
|
||||
for (const auto &key: src.keys()) {
|
||||
auto v_src = src[key];
|
||||
if (dst.contains(key)) {
|
||||
auto v_dst = dst[key];
|
||||
if (v_src.isObject() && v_dst.isObject()) { // isObject 则合并?
|
||||
auto v_src_obj = v_src.toObject();
|
||||
auto v_dst_obj = v_dst.toObject();
|
||||
MergeJson(v_dst_obj, v_src_obj);
|
||||
dst[key] = v_dst_obj;
|
||||
} else {
|
||||
outbound[key] = v;
|
||||
dst[key] = v_src;
|
||||
}
|
||||
} else if (v_src.isArray()) {
|
||||
if (key.startsWith("+")) {
|
||||
auto key2 = SubStrAfter(key, "+");
|
||||
auto v_dst = dst[key2];
|
||||
auto v_src_arr = v_src.toArray();
|
||||
auto v_dst_arr = v_dst.toArray();
|
||||
QJSONARRAY_ADD(v_src_arr, v_dst_arr)
|
||||
dst[key2] = v_src_arr;
|
||||
} else if (key.endsWith("+")) {
|
||||
auto key2 = SubStrBefore(key, "+");
|
||||
auto v_dst = dst[key2];
|
||||
auto v_src_arr = v_src.toArray();
|
||||
auto v_dst_arr = v_dst.toArray();
|
||||
QJSONARRAY_ADD(v_dst_arr, v_src_arr)
|
||||
dst[key2] = v_dst_arr;
|
||||
} else {
|
||||
dst[key] = v_src;
|
||||
}
|
||||
} else {
|
||||
outbound[key] = custom[key];
|
||||
dst[key] = v_src;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -64,15 +82,11 @@ namespace NekoGui {
|
||||
if (customBean != nullptr && customBean->core == "internal-full") {
|
||||
result->coreConfig = QString2QJsonObject(customBean->config_simple);
|
||||
} else {
|
||||
if (IS_NEKO_BOX) {
|
||||
BuildConfigSingBox(status);
|
||||
} else {
|
||||
BuildConfigV2Ray(status);
|
||||
}
|
||||
BuildConfigSingBox(status);
|
||||
}
|
||||
|
||||
// apply custom config
|
||||
MergeJson(QString2QJsonObject(ent->bean->custom_config), result->coreConfig);
|
||||
MergeJson(result->coreConfig, QString2QJsonObject(ent->bean->custom_config));
|
||||
|
||||
return result;
|
||||
}
|
||||
@ -80,7 +94,7 @@ namespace NekoGui {
|
||||
QString BuildChain(int chainId, const std::shared_ptr<BuildConfigStatus> &status) {
|
||||
auto group = profileManager->GetGroup(status->ent->gid);
|
||||
if (group == nullptr) {
|
||||
status->result->error = QString("This profile is not in any group, your data may be corrupted.");
|
||||
status->result->error = QStringLiteral("This profile is not in any group, your data may be corrupted.");
|
||||
return {};
|
||||
}
|
||||
|
||||
@ -92,11 +106,11 @@ namespace NekoGui {
|
||||
for (auto id: list) {
|
||||
resolved += profileManager->GetProfile(id);
|
||||
if (resolved.last() == nullptr) {
|
||||
status->result->error = QString("chain missing ent: %1").arg(id);
|
||||
status->result->error = QStringLiteral("chain missing ent: %1").arg(id);
|
||||
break;
|
||||
}
|
||||
if (resolved.last()->type == "chain") {
|
||||
status->result->error = QString("chain in chain is not allowed: %1").arg(id);
|
||||
status->result->error = QStringLiteral("chain in chain is not allowed: %1").arg(id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -113,7 +127,7 @@ namespace NekoGui {
|
||||
if (group->front_proxy_id >= 0) {
|
||||
auto fEnt = profileManager->GetProfile(group->front_proxy_id);
|
||||
if (fEnt == nullptr) {
|
||||
status->result->error = QString("front proxy ent not found.");
|
||||
status->result->error = QStringLiteral("front proxy ent not found.");
|
||||
return {};
|
||||
}
|
||||
ents += resolveChain(fEnt);
|
||||
@ -157,254 +171,6 @@ namespace NekoGui {
|
||||
status->ipListDirect += line; \
|
||||
}
|
||||
|
||||
// V2Ray
|
||||
|
||||
void BuildConfigV2Ray(const std::shared_ptr<BuildConfigStatus> &status) {
|
||||
// Log
|
||||
auto logObj = QJsonObject{{"loglevel", dataStore->log_level}};
|
||||
status->result->coreConfig.insert("log", logObj);
|
||||
|
||||
// Inbounds
|
||||
QJsonObject sniffing{
|
||||
{"destOverride", QJsonArray{"http", "tls", "quic"}},
|
||||
{"enabled", true},
|
||||
{"metadataOnly", false},
|
||||
{"routeOnly", dataStore->routing->sniffing_mode == SniffingMode::FOR_ROUTING},
|
||||
};
|
||||
|
||||
// socks-in
|
||||
if (IsValidPort(dataStore->inbound_socks_port) && !status->forTest) {
|
||||
QJsonObject inboundObj;
|
||||
inboundObj["tag"] = "socks-in";
|
||||
inboundObj["protocol"] = "socks";
|
||||
inboundObj["listen"] = dataStore->inbound_address;
|
||||
inboundObj["port"] = dataStore->inbound_socks_port;
|
||||
QJsonObject socksSettings = {{"udp", true}};
|
||||
if (dataStore->routing->sniffing_mode != SniffingMode::DISABLE) {
|
||||
inboundObj["sniffing"] = sniffing;
|
||||
}
|
||||
if (dataStore->inbound_auth->NeedAuth()) {
|
||||
socksSettings["auth"] = "password";
|
||||
socksSettings["accounts"] = QJsonArray{
|
||||
QJsonObject{
|
||||
{"user", dataStore->inbound_auth->username},
|
||||
{"pass", dataStore->inbound_auth->password},
|
||||
},
|
||||
};
|
||||
}
|
||||
inboundObj["settings"] = socksSettings;
|
||||
status->inbounds += inboundObj;
|
||||
}
|
||||
// http-in
|
||||
if (IsValidPort(dataStore->inbound_http_port) && !status->forTest) {
|
||||
QJsonObject inboundObj;
|
||||
inboundObj["tag"] = "http-in";
|
||||
inboundObj["protocol"] = "http";
|
||||
inboundObj["listen"] = dataStore->inbound_address;
|
||||
inboundObj["port"] = dataStore->inbound_http_port;
|
||||
if (dataStore->routing->sniffing_mode != SniffingMode::DISABLE) {
|
||||
inboundObj["sniffing"] = sniffing;
|
||||
}
|
||||
if (dataStore->inbound_auth->NeedAuth()) {
|
||||
inboundObj["settings"] = QJsonObject{
|
||||
{"accounts", QJsonArray{
|
||||
QJsonObject{
|
||||
{"user", dataStore->inbound_auth->username},
|
||||
{"pass", dataStore->inbound_auth->password},
|
||||
},
|
||||
}},
|
||||
};
|
||||
}
|
||||
status->inbounds += inboundObj;
|
||||
}
|
||||
|
||||
// Outbounds
|
||||
auto tagProxy = BuildChain(0, status);
|
||||
if (!status->result->error.isEmpty()) return;
|
||||
|
||||
// direct & bypass & block
|
||||
status->outbounds += QJsonObject{
|
||||
{"protocol", "freedom"},
|
||||
{"domainStrategy", dataStore->core_ray_freedom_domainStrategy},
|
||||
{"tag", "direct"},
|
||||
};
|
||||
status->outbounds += QJsonObject{
|
||||
{"protocol", "freedom"},
|
||||
{"domainStrategy", dataStore->core_ray_freedom_domainStrategy},
|
||||
{"tag", "bypass"},
|
||||
};
|
||||
status->outbounds += QJsonObject{
|
||||
{"protocol", "blackhole"},
|
||||
{"tag", "block"},
|
||||
};
|
||||
|
||||
// DNS out
|
||||
if (!status->forTest) {
|
||||
QJsonObject dnsOut;
|
||||
dnsOut["protocol"] = "dns";
|
||||
dnsOut["tag"] = "dns-out";
|
||||
QJsonObject dnsOut_settings;
|
||||
dnsOut_settings["network"] = "tcp";
|
||||
dnsOut_settings["port"] = 53;
|
||||
dnsOut_settings["address"] = "8.8.8.8";
|
||||
dnsOut_settings["userLevel"] = 1;
|
||||
dnsOut["settings"] = dnsOut_settings;
|
||||
dnsOut["proxySettings"] = QJsonObject{{"tag", tagProxy},
|
||||
{"transportLayer", true}};
|
||||
|
||||
status->outbounds += dnsOut;
|
||||
status->routingRules += QJsonObject{
|
||||
{"type", "field"},
|
||||
{"port", "53"},
|
||||
{"inboundTag", QJsonArray{"socks-in", "http-in"}},
|
||||
{"outboundTag", "dns-out"},
|
||||
};
|
||||
}
|
||||
|
||||
// custom inbound
|
||||
if (!status->forTest) QJSONARRAY_ADD(status->inbounds, QString2QJsonObject(dataStore->custom_inbound)["inbounds"].toArray())
|
||||
|
||||
status->result->coreConfig.insert("inbounds", status->inbounds);
|
||||
status->result->coreConfig.insert("outbounds", status->outbounds);
|
||||
|
||||
// user rule
|
||||
if (!status->forTest) {
|
||||
DOMAIN_USER_RULE
|
||||
IP_USER_RULE
|
||||
}
|
||||
|
||||
// final add DNS
|
||||
QJsonObject dns;
|
||||
QJsonArray dnsServers;
|
||||
|
||||
// Remote or FakeDNS
|
||||
QJsonObject dnsServerRemote;
|
||||
dnsServerRemote["address"] = dataStore->routing->remote_dns;
|
||||
dnsServerRemote["domains"] = QList2QJsonArray<QString>(status->domainListDNSRemote);
|
||||
dnsServerRemote["queryStrategy"] = dataStore->routing->remote_dns_strategy;
|
||||
if (!status->forTest) dnsServers += dnsServerRemote;
|
||||
|
||||
// Direct
|
||||
auto directDnsAddress = dataStore->routing->direct_dns;
|
||||
if (directDnsAddress.contains("://")) {
|
||||
auto directDnsIp = SubStrBefore(SubStrAfter(directDnsAddress, "://"), "/");
|
||||
if (IsIpAddress(directDnsIp)) {
|
||||
status->routingRules.push_front(QJsonObject{
|
||||
{"type", "field"},
|
||||
{"ip", QJsonArray{directDnsIp}},
|
||||
{"outboundTag", "direct"},
|
||||
});
|
||||
} else {
|
||||
status->routingRules.push_front(QJsonObject{
|
||||
{"type", "field"},
|
||||
{"domain", QJsonArray{directDnsIp}},
|
||||
{"outboundTag", "direct"},
|
||||
});
|
||||
}
|
||||
} else if (directDnsAddress != "localhost") {
|
||||
status->routingRules.push_front(QJsonObject{
|
||||
{"type", "field"},
|
||||
{"ip", QJsonArray{directDnsAddress}},
|
||||
{"outboundTag", "direct"},
|
||||
});
|
||||
}
|
||||
QJsonObject directObj{
|
||||
{"address", directDnsAddress.replace("https://", "https+local://")},
|
||||
{"queryStrategy", dataStore->routing->direct_dns_strategy},
|
||||
{"domains", QList2QJsonArray<QString>(status->domainListDNSDirect)},
|
||||
};
|
||||
if (dataStore->routing->dns_final_out == "bypass") {
|
||||
dnsServers.prepend(directObj);
|
||||
} else {
|
||||
dnsServers.append(directObj);
|
||||
}
|
||||
|
||||
dns["disableFallback"] = true;
|
||||
dns["servers"] = dnsServers;
|
||||
dns["tag"] = "dns";
|
||||
|
||||
if (dataStore->routing->use_dns_object) {
|
||||
dns = QString2QJsonObject(dataStore->routing->dns_object);
|
||||
}
|
||||
status->result->coreConfig.insert("dns", dns);
|
||||
|
||||
// Routing
|
||||
QJsonObject routing;
|
||||
routing["domainStrategy"] = dataStore->routing->domain_strategy;
|
||||
if (status->forTest) routing["domainStrategy"] = "AsIs";
|
||||
|
||||
// final add user rule (block)
|
||||
QJsonObject routingRule_tmp;
|
||||
routingRule_tmp["type"] = "field";
|
||||
routingRule_tmp["outboundTag"] = "block";
|
||||
if (!status->ipListBlock.isEmpty()) {
|
||||
auto tmp = routingRule_tmp;
|
||||
tmp["ip"] = QList2QJsonArray<QString>(status->ipListBlock);
|
||||
status->routingRules += tmp;
|
||||
}
|
||||
if (!status->domainListBlock.isEmpty()) {
|
||||
auto tmp = routingRule_tmp;
|
||||
tmp["domain"] = QList2QJsonArray<QString>(status->domainListBlock);
|
||||
status->routingRules += tmp;
|
||||
}
|
||||
|
||||
// final add user rule (proxy)
|
||||
routingRule_tmp["outboundTag"] = "proxy";
|
||||
if (!status->ipListRemote.isEmpty()) {
|
||||
auto tmp = routingRule_tmp;
|
||||
tmp["ip"] = QList2QJsonArray<QString>(status->ipListRemote);
|
||||
status->routingRules += tmp;
|
||||
}
|
||||
if (!status->domainListRemote.isEmpty()) {
|
||||
auto tmp = routingRule_tmp;
|
||||
tmp["domain"] = QList2QJsonArray<QString>(status->domainListRemote);
|
||||
status->routingRules += tmp;
|
||||
}
|
||||
|
||||
// final add user rule (bypass)
|
||||
routingRule_tmp["outboundTag"] = "bypass";
|
||||
if (!status->ipListDirect.isEmpty()) {
|
||||
auto tmp = routingRule_tmp;
|
||||
tmp["ip"] = QList2QJsonArray<QString>(status->ipListDirect);
|
||||
status->routingRules += tmp;
|
||||
}
|
||||
if (!status->domainListDirect.isEmpty()) {
|
||||
auto tmp = routingRule_tmp;
|
||||
tmp["domain"] = QList2QJsonArray<QString>(status->domainListDirect);
|
||||
status->routingRules += tmp;
|
||||
}
|
||||
|
||||
// def_outbound
|
||||
if (!status->forTest) status->routingRules += QJsonObject{
|
||||
{"type", "field"},
|
||||
{"port", "0-65535"},
|
||||
{"outboundTag", dataStore->routing->def_outbound},
|
||||
};
|
||||
|
||||
// final add routing rule
|
||||
auto routingRules = QString2QJsonObject(dataStore->routing->custom)["rules"].toArray();
|
||||
if (status->forTest) routingRules = {};
|
||||
if (!status->forTest) QJSONARRAY_ADD(routingRules, QString2QJsonObject(dataStore->custom_route_global)["rules"].toArray())
|
||||
QJSONARRAY_ADD(routingRules, status->routingRules)
|
||||
routing["rules"] = routingRules;
|
||||
status->result->coreConfig.insert("routing", routing);
|
||||
|
||||
// Policy & stats
|
||||
QJsonObject policy;
|
||||
QJsonObject levels;
|
||||
QJsonObject level1;
|
||||
level1["connIdle"] = 30;
|
||||
levels["1"] = level1;
|
||||
policy["levels"] = levels;
|
||||
|
||||
QJsonObject policySystem;
|
||||
policySystem["statsOutboundDownlink"] = true;
|
||||
policySystem["statsOutboundUplink"] = true;
|
||||
policy["system"] = policySystem;
|
||||
status->result->coreConfig.insert("policy", policy);
|
||||
status->result->coreConfig.insert("stats", QJsonObject());
|
||||
}
|
||||
|
||||
QString BuildChainInternal(int chainId, const QList<std::shared_ptr<ProxyEntity>> &ents,
|
||||
const std::shared_ptr<BuildConfigStatus> &status) {
|
||||
QString chainTag = "c-" + Int2String(chainId);
|
||||
@ -454,29 +220,14 @@ namespace NekoGui {
|
||||
// chain rules: past
|
||||
if (pastExternalStat == 0) {
|
||||
auto replaced = status->outbounds.last().toObject();
|
||||
if (IS_NEKO_BOX) {
|
||||
replaced["detour"] = tagOut;
|
||||
} else {
|
||||
replaced["proxySettings"] = QJsonObject{
|
||||
{"tag", tagOut},
|
||||
{"transportLayer", true},
|
||||
};
|
||||
}
|
||||
replaced["detour"] = tagOut;
|
||||
status->outbounds.removeLast();
|
||||
status->outbounds += replaced;
|
||||
} else {
|
||||
if (IS_NEKO_BOX) {
|
||||
status->routingRules += QJsonObject{
|
||||
{"inbound", QJsonArray{pastTag + "-mapping"}},
|
||||
{"outbound", tagOut},
|
||||
};
|
||||
} else {
|
||||
status->routingRules += QJsonObject{
|
||||
{"type", "field"},
|
||||
{"inboundTag", QJsonArray{pastTag + "-mapping"}},
|
||||
{"outboundTag", tagOut},
|
||||
};
|
||||
}
|
||||
status->routingRules += QJsonObject{
|
||||
{"inbound", QJsonArray{pastTag + "-mapping"}},
|
||||
{"outbound", tagOut},
|
||||
};
|
||||
}
|
||||
} else {
|
||||
// index == 0 means last profile in chain / not chain
|
||||
@ -515,43 +266,20 @@ namespace NekoGui {
|
||||
if (thisExternalStat == 2) dataStore->need_keep_vpn_off = true;
|
||||
if (thisExternalStat == 1) {
|
||||
// mapping
|
||||
if (IS_NEKO_BOX) {
|
||||
status->inbounds += QJsonObject{
|
||||
{"type", "direct"},
|
||||
{"tag", tagOut + "-mapping"},
|
||||
{"listen", "127.0.0.1"},
|
||||
{"listen_port", ext_mapping_port},
|
||||
{"override_address", ent->bean->serverAddress},
|
||||
{"override_port", ent->bean->serverPort},
|
||||
};
|
||||
} else {
|
||||
status->inbounds += QJsonObject{
|
||||
{"protocol", "dokodemo-door"},
|
||||
{"tag", tagOut + "-mapping"},
|
||||
{"listen", "127.0.0.1"},
|
||||
{"port", ext_mapping_port},
|
||||
{"settings", QJsonObject{
|
||||
// to
|
||||
{"address", ent->bean->serverAddress},
|
||||
{"port", ent->bean->serverPort},
|
||||
{"network", "tcp,udp"},
|
||||
}},
|
||||
};
|
||||
}
|
||||
status->inbounds += QJsonObject{
|
||||
{"type", "direct"},
|
||||
{"tag", tagOut + "-mapping"},
|
||||
{"listen", "127.0.0.1"},
|
||||
{"listen_port", ext_mapping_port},
|
||||
{"override_address", ent->bean->serverAddress},
|
||||
{"override_port", ent->bean->serverPort},
|
||||
};
|
||||
// no chain rule and not outbound, so need to set to direct
|
||||
if (isFirstProfile) {
|
||||
if (IS_NEKO_BOX) {
|
||||
status->routingRules += QJsonObject{
|
||||
{"inbound", QJsonArray{tagOut + "-mapping"}},
|
||||
{"outbound", "direct"},
|
||||
};
|
||||
} else {
|
||||
status->routingRules += QJsonObject{
|
||||
{"type", "field"},
|
||||
{"inboundTag", QJsonArray{tagOut + "-mapping"}},
|
||||
{"outboundTag", "direct"},
|
||||
};
|
||||
}
|
||||
status->routingRules += QJsonObject{
|
||||
{"inbound", QJsonArray{tagOut + "-mapping"}},
|
||||
{"outbound", "direct"},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@ -574,23 +302,11 @@ namespace NekoGui {
|
||||
status->result->extRs.emplace_back(std::make_shared<NekoGui_fmt::ExternalBuildResult>(extR));
|
||||
|
||||
// SOCKS OUTBOUND
|
||||
if (IS_NEKO_BOX) {
|
||||
outbound["type"] = "socks";
|
||||
outbound["server"] = "127.0.0.1";
|
||||
outbound["server_port"] = ext_socks_port;
|
||||
} else {
|
||||
outbound["protocol"] = "socks";
|
||||
QJsonObject settings;
|
||||
QJsonArray servers;
|
||||
QJsonObject server;
|
||||
server["address"] = "127.0.0.1";
|
||||
server["port"] = ext_socks_port;
|
||||
servers.push_back(server);
|
||||
settings["servers"] = servers;
|
||||
outbound["settings"] = settings;
|
||||
}
|
||||
outbound["type"] = "socks";
|
||||
outbound["server"] = "127.0.0.1";
|
||||
outbound["server_port"] = ext_socks_port;
|
||||
} else {
|
||||
const auto coreR = IS_NEKO_BOX ? ent->bean->BuildCoreObjSingBox() : ent->bean->BuildCoreObjV2Ray();
|
||||
const auto coreR = ent->bean->BuildCoreObjSingBox();
|
||||
if (coreR.outbound.isEmpty()) {
|
||||
status->result->error = "unsupported outbound";
|
||||
return {};
|
||||
@ -613,14 +329,8 @@ namespace NekoGui {
|
||||
needMux &= dataStore->mux_concurrency > 0;
|
||||
|
||||
if (stream != nullptr) {
|
||||
if (IS_NEKO_BOX) {
|
||||
if (stream->network == "grpc" || stream->network == "quic" || (stream->network == "http" && stream->security == "tls")) {
|
||||
needMux = false;
|
||||
}
|
||||
} else {
|
||||
if (stream->network == "grpc" || stream->network == "quic") {
|
||||
needMux = false;
|
||||
}
|
||||
if (stream->network == "grpc" || stream->network == "quic" || (stream->network == "http" && stream->security == "tls")) {
|
||||
needMux = false;
|
||||
}
|
||||
if (stream->multiplex_status == 0) {
|
||||
if (!dataStore->mux_default_on) needMux = false;
|
||||
@ -635,36 +345,22 @@ namespace NekoGui {
|
||||
}
|
||||
|
||||
// common
|
||||
if (IS_NEKO_BOX) {
|
||||
// apply domain_strategy
|
||||
outbound["domain_strategy"] = dataStore->routing->outbound_domain_strategy;
|
||||
// apply mux
|
||||
if (!muxApplied && needMux) {
|
||||
auto muxObj = QJsonObject{
|
||||
{"enabled", true},
|
||||
{"protocol", dataStore->mux_protocol},
|
||||
{"padding", dataStore->mux_padding},
|
||||
{"max_streams", dataStore->mux_concurrency},
|
||||
};
|
||||
outbound["multiplex"] = muxObj;
|
||||
muxApplied = true;
|
||||
}
|
||||
} else {
|
||||
// apply domain_strategy
|
||||
if (!status->forTest) outbound["domainStrategy"] = dataStore->routing->outbound_domain_strategy;
|
||||
// apply mux
|
||||
if (!muxApplied && needMux) {
|
||||
auto muxObj = QJsonObject{
|
||||
{"enabled", true},
|
||||
{"concurrency", dataStore->mux_concurrency},
|
||||
};
|
||||
outbound["mux"] = muxObj;
|
||||
muxApplied = true;
|
||||
}
|
||||
// apply domain_strategy
|
||||
outbound["domain_strategy"] = dataStore->routing->outbound_domain_strategy;
|
||||
// apply mux
|
||||
if (!muxApplied && needMux) {
|
||||
auto muxObj = QJsonObject{
|
||||
{"enabled", true},
|
||||
{"protocol", dataStore->mux_protocol},
|
||||
{"padding", dataStore->mux_padding},
|
||||
{"max_streams", dataStore->mux_concurrency},
|
||||
};
|
||||
outbound["multiplex"] = muxObj;
|
||||
muxApplied = true;
|
||||
}
|
||||
|
||||
// apply custom outbound settings
|
||||
MergeJson(QString2QJsonObject(ent->bean->custom_outbound), outbound);
|
||||
MergeJson(outbound, QString2QJsonObject(ent->bean->custom_outbound));
|
||||
|
||||
// Bypass Lookup for the first profile
|
||||
auto serverAddress = ent->bean->serverAddress;
|
||||
@ -720,7 +416,7 @@ namespace NekoGui {
|
||||
}
|
||||
|
||||
// tun-in
|
||||
if (IS_NEKO_BOX_INTERNAL_TUN && dataStore->spmode_vpn && !status->forTest) {
|
||||
if (dataStore->vpn_internal_tun && dataStore->spmode_vpn && !status->forTest) {
|
||||
QJsonObject inboundObj;
|
||||
inboundObj["tag"] = "tun-in";
|
||||
inboundObj["type"] = "tun";
|
||||
@ -808,7 +504,7 @@ namespace NekoGui {
|
||||
} else if (item.startsWith("keyword:")) {
|
||||
domain_keyword += item.replace("keyword:", "").toLower();
|
||||
} else {
|
||||
domain_full += item.toLower();
|
||||
domain_subdomain += item.toLower();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -845,21 +541,22 @@ namespace NekoGui {
|
||||
};
|
||||
|
||||
// Direct
|
||||
auto directDNSAddress = dataStore->routing->direct_dns;
|
||||
if (!status->forTest) {
|
||||
QJsonObject directObj{
|
||||
{"tag", "dns-direct"},
|
||||
{"address_resolver", "dns-local"},
|
||||
{"strategy", dataStore->routing->direct_dns_strategy},
|
||||
{"address", directDNSAddress.replace("+local://", "://")},
|
||||
{"detour", "direct"},
|
||||
};
|
||||
if (dataStore->routing->dns_final_out == "bypass") {
|
||||
dnsServers.prepend(directObj);
|
||||
} else {
|
||||
dnsServers.append(directObj);
|
||||
}
|
||||
QJsonObject directObj{
|
||||
{"tag", "dns-direct"},
|
||||
{"address_resolver", "dns-local"},
|
||||
{"strategy", dataStore->routing->direct_dns_strategy},
|
||||
{"address", dataStore->routing->direct_dns},
|
||||
{"detour", "direct"},
|
||||
};
|
||||
if (dataStore->routing->dns_final_out == "bypass") {
|
||||
dnsServers.prepend(directObj);
|
||||
} else {
|
||||
dnsServers.append(directObj);
|
||||
}
|
||||
dnsRules.append(QJsonObject{
|
||||
{"outbound", "any"},
|
||||
{"server", "dns-direct"},
|
||||
});
|
||||
|
||||
// block
|
||||
if (!status->forTest)
|
||||
@ -869,7 +566,7 @@ namespace NekoGui {
|
||||
};
|
||||
|
||||
// Fakedns
|
||||
if (dataStore->fake_dns && IS_NEKO_BOX_INTERNAL_TUN && dataStore->spmode_vpn && !status->forTest) {
|
||||
if (dataStore->fake_dns && dataStore->vpn_internal_tun && dataStore->spmode_vpn && !status->forTest) {
|
||||
dnsServers += QJsonObject{
|
||||
{"tag", "dns-fake"},
|
||||
{"address", "fakeip"},
|
||||
@ -911,7 +608,7 @@ namespace NekoGui {
|
||||
}
|
||||
|
||||
// fakedns rule
|
||||
if (dataStore->fake_dns && IS_NEKO_BOX_INTERNAL_TUN && dataStore->spmode_vpn && !status->forTest) {
|
||||
if (dataStore->fake_dns && dataStore->vpn_internal_tun && dataStore->spmode_vpn && !status->forTest) {
|
||||
dnsRules += QJsonObject{
|
||||
{"inbound", "tun-in"},
|
||||
{"server", "dns-fake"},
|
||||
@ -969,7 +666,7 @@ namespace NekoGui {
|
||||
};
|
||||
|
||||
// tun user rule
|
||||
if (IS_NEKO_BOX_INTERNAL_TUN && dataStore->spmode_vpn && !status->forTest) {
|
||||
if (dataStore->vpn_internal_tun && dataStore->spmode_vpn && !status->forTest) {
|
||||
auto match_out = dataStore->vpn_rule_white ? "proxy" : "bypass";
|
||||
|
||||
QString process_name_rule = dataStore->vpn_rule_process.trimmed();
|
||||
@ -1009,7 +706,7 @@ namespace NekoGui {
|
||||
QJSONARRAY_ADD(routingRules, status->routingRules)
|
||||
auto routeObj = QJsonObject{
|
||||
{"rules", routingRules},
|
||||
{"auto_detect_interface", dataStore->spmode_vpn},
|
||||
{"auto_detect_interface", dataStore->spmode_vpn}, // TODO force enable?
|
||||
{
|
||||
"geoip",
|
||||
QJsonObject{
|
||||
@ -1047,8 +744,8 @@ namespace NekoGui {
|
||||
|
||||
QString WriteVPNSingBoxConfig() {
|
||||
// tun user rule
|
||||
auto match_out = dataStore->vpn_rule_white ? "nekoray-socks" : "direct";
|
||||
auto no_match_out = dataStore->vpn_rule_white ? "direct" : "nekoray-socks";
|
||||
auto match_out = dataStore->vpn_rule_white ? "neko-socks" : "direct";
|
||||
auto no_match_out = dataStore->vpn_rule_white ? "direct" : "neko-socks";
|
||||
|
||||
QString process_name_rule = dataStore->vpn_rule_process.trimmed();
|
||||
if (!process_name_rule.isEmpty()) {
|
||||
@ -1099,7 +796,7 @@ namespace NekoGui {
|
||||
return QFileInfo(file).absoluteFilePath();
|
||||
}
|
||||
|
||||
QString WriteVPNLinuxScript(const QString &protectPath, const QString &configPath) {
|
||||
QString WriteVPNLinuxScript(const QString &configPath) {
|
||||
#ifdef Q_OS_WIN
|
||||
return {};
|
||||
#endif
|
||||
@ -1108,9 +805,7 @@ namespace NekoGui {
|
||||
if (QFile::exists("vpn/vpn-run-root.sh")) scriptFn = "vpn/vpn-run-root.sh";
|
||||
auto script = ReadFileText(scriptFn)
|
||||
.replace("./nekobox_core", QApplication::applicationDirPath() + "/nekobox_core")
|
||||
.replace("$PROTECT_LISTEN_PATH", protectPath)
|
||||
.replace("$CONFIG_PATH", configPath)
|
||||
.replace("$TABLE_FWMARK", "514");
|
||||
.replace("$CONFIG_PATH", configPath);
|
||||
// write script
|
||||
QFile file2;
|
||||
file2.setFileName(QFileInfo(scriptFn).fileName());
|
||||
@ -1120,4 +815,4 @@ namespace NekoGui {
|
||||
return QFileInfo(file2).absoluteFilePath();
|
||||
}
|
||||
|
||||
} // namespace NekoGui
|
||||
} // namespace NekoGui
|
||||
|
||||
@ -46,8 +46,6 @@ namespace NekoGui {
|
||||
|
||||
std::shared_ptr<BuildConfigResult> BuildConfig(const std::shared_ptr<ProxyEntity> &ent, bool forTest, bool forExport);
|
||||
|
||||
void BuildConfigV2Ray(const std::shared_ptr<BuildConfigStatus> &status);
|
||||
|
||||
void BuildConfigSingBox(const std::shared_ptr<BuildConfigStatus> &status);
|
||||
|
||||
QString BuildChain(int chainId, const std::shared_ptr<BuildConfigStatus> &status);
|
||||
@ -57,5 +55,5 @@ namespace NekoGui {
|
||||
|
||||
QString WriteVPNSingBoxConfig();
|
||||
|
||||
QString WriteVPNLinuxScript(const QString &protectPath, const QString &configPath);
|
||||
QString WriteVPNLinuxScript(const QString &configPath);
|
||||
} // namespace NekoGui
|
||||
|
||||
@ -42,7 +42,7 @@ namespace NekoGui {
|
||||
// Load Proxys
|
||||
QList<int> delProfile;
|
||||
for (auto id: profilesIdOrder) {
|
||||
auto ent = LoadProxyEntity(QString("profiles/%1.json").arg(id));
|
||||
auto ent = LoadProxyEntity(QStringLiteral("profiles/%1.json").arg(id));
|
||||
// Corrupted profile?
|
||||
if (ent == nullptr || ent->bean == nullptr || ent->bean->version == -114514) {
|
||||
delProfile << id;
|
||||
@ -58,7 +58,7 @@ namespace NekoGui {
|
||||
auto loadedOrder = groupsTabOrder;
|
||||
groupsTabOrder = {};
|
||||
for (auto id: groupsIdOrder) {
|
||||
auto ent = LoadGroup(QString("groups/%1.json").arg(id));
|
||||
auto ent = LoadGroup(QStringLiteral("groups/%1.json").arg(id));
|
||||
// Corrupted group?
|
||||
if (ent->id != id) {
|
||||
continue;
|
||||
@ -103,7 +103,7 @@ namespace NekoGui {
|
||||
auto newId = i++;
|
||||
profile->id = newId;
|
||||
profile->gid = gidOld2New[gid];
|
||||
profile->fn = QString("profiles/%1.json").arg(newId);
|
||||
profile->fn = QStringLiteral("profiles/%1.json").arg(newId);
|
||||
profile->Save();
|
||||
newProfiles[newId] = profile;
|
||||
newProfilesIdOrder << newId;
|
||||
@ -122,7 +122,7 @@ namespace NekoGui {
|
||||
auto group = groups[oldGid];
|
||||
QFile::remove(group->fn);
|
||||
group->id = newId;
|
||||
group->fn = QString("groups/%1.json").arg(newId);
|
||||
group->fn = QStringLiteral("groups/%1.json").arg(newId);
|
||||
group->Save();
|
||||
newGroups[newId] = group;
|
||||
newGroupsIdOrder << newId;
|
||||
@ -184,8 +184,6 @@ namespace NekoGui {
|
||||
bean = new NekoGui_fmt::TrojanVLESSBean(NekoGui_fmt::TrojanVLESSBean::proxy_VLESS);
|
||||
} else if (type == "naive") {
|
||||
bean = new NekoGui_fmt::NaiveBean();
|
||||
} else if (type == "hysteria") {
|
||||
bean = new NekoGui_fmt::QUICBean(NekoGui_fmt::QUICBean::proxy_Hysteria);
|
||||
} else if (type == "hysteria2") {
|
||||
bean = new NekoGui_fmt::QUICBean(NekoGui_fmt::QUICBean::proxy_Hysteria2);
|
||||
} else if (type == "tuic") {
|
||||
@ -229,7 +227,7 @@ namespace NekoGui {
|
||||
if (latency < 0) {
|
||||
return QObject::tr("Unavailable");
|
||||
} else if (latency > 0) {
|
||||
return UNICODE_LRO + QString("%1 ms").arg(latency);
|
||||
return UNICODE_LRO + QStringLiteral("%1 ms").arg(latency);
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
@ -270,7 +268,7 @@ namespace NekoGui {
|
||||
profiles[ent->id] = ent;
|
||||
profilesIdOrder.push_back(ent->id);
|
||||
|
||||
ent->fn = QString("profiles/%1.json").arg(ent->id);
|
||||
ent->fn = QStringLiteral("profiles/%1.json").arg(ent->id);
|
||||
ent->Save();
|
||||
return true;
|
||||
}
|
||||
@ -280,7 +278,7 @@ namespace NekoGui {
|
||||
if (dataStore->started_id == id) return;
|
||||
profiles.erase(id);
|
||||
profilesIdOrder.removeAll(id);
|
||||
QFile(QString("profiles/%1.json").arg(id)).remove();
|
||||
QFile(QStringLiteral("profiles/%1.json").arg(id)).remove();
|
||||
}
|
||||
|
||||
void ProfileManager::MoveProfile(const std::shared_ptr<ProxyEntity> &ent, int gid) {
|
||||
@ -344,7 +342,7 @@ namespace NekoGui {
|
||||
groupsIdOrder.push_back(ent->id);
|
||||
groupsTabOrder.push_back(ent->id);
|
||||
|
||||
ent->fn = QString("groups/%1.json").arg(ent->id);
|
||||
ent->fn = QStringLiteral("groups/%1.json").arg(ent->id);
|
||||
ent->Save();
|
||||
return true;
|
||||
}
|
||||
@ -361,7 +359,7 @@ namespace NekoGui {
|
||||
groups.erase(gid);
|
||||
groupsIdOrder.removeAll(gid);
|
||||
groupsTabOrder.removeAll(gid);
|
||||
QFile(QString("groups/%1.json").arg(gid)).remove();
|
||||
QFile(QStringLiteral("groups/%1.json").arg(gid)).remove();
|
||||
}
|
||||
|
||||
std::shared_ptr<Group> ProfileManager::GetGroup(int id) {
|
||||
@ -393,4 +391,4 @@ namespace NekoGui {
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace NekoGui
|
||||
} // namespace NekoGui
|
||||
|
||||
@ -29,12 +29,12 @@ namespace NekoGui_traffic {
|
||||
}
|
||||
|
||||
[[nodiscard]] QString DisplaySpeed() const {
|
||||
return UNICODE_LRO + QString("%1↑ %2↓").arg(ReadableSize(uplink_rate), ReadableSize(downlink_rate));
|
||||
return UNICODE_LRO + QStringLiteral("%1↑ %2↓").arg(ReadableSize(uplink_rate), ReadableSize(downlink_rate));
|
||||
}
|
||||
|
||||
[[nodiscard]] QString DisplayTraffic() const {
|
||||
if (downlink + uplink == 0) return "";
|
||||
return UNICODE_LRO + QString("%1↑ %2↓").arg(ReadableSize(uplink), ReadableSize(downlink));
|
||||
return UNICODE_LRO + QStringLiteral("%1↑ %2↓").arg(ReadableSize(uplink), ReadableSize(downlink));
|
||||
}
|
||||
};
|
||||
} // namespace NekoGui_traffic
|
||||
|
||||
@ -22,9 +22,9 @@ cmake -GNinja ..
|
||||
ninja
|
||||
```
|
||||
|
||||
编译完成后得到 `nekoray`
|
||||
编译完成后得到 `nekobox`
|
||||
|
||||
解压 Release 的压缩包,替换其中的 `nekoray`,删除 `launcher` 即可使用。
|
||||
解压 Release 的压缩包,替换其中的 `nekobox`,删除 `launcher` 即可使用。
|
||||
|
||||
## 复杂编译法
|
||||
|
||||
@ -69,7 +69,7 @@ cmake -GNinja ..
|
||||
ninja
|
||||
```
|
||||
|
||||
编译完成后得到 `nekoray`
|
||||
编译完成后得到 `nekobox`
|
||||
|
||||
### Go 部分编译
|
||||
|
||||
|
||||
@ -55,9 +55,9 @@ cmake -GNinja -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH=D:/path/to/qt/5.15.
|
||||
ninja
|
||||
```
|
||||
|
||||
编译完成后得到 `nekoray.exe`
|
||||
编译完成后得到 `nekobox.exe`
|
||||
|
||||
最后运行 `windeployqt nekoray.exe` 自动复制所需 DLL 等文件到当前目录
|
||||
最后运行 `windeployqt nekobox.exe` 自动复制所需 DLL 等文件到当前目录
|
||||
|
||||
### Go 部分编译
|
||||
|
||||
|
||||
@ -38,7 +38,7 @@ namespace NekoGui_fmt {
|
||||
}
|
||||
|
||||
QString AbstractBean::DisplayTypeAndName() {
|
||||
return QString("[%1] %2").arg(DisplayType(), DisplayName());
|
||||
return QStringLiteral("[%1] %2").arg(DisplayType(), DisplayName());
|
||||
}
|
||||
|
||||
void AbstractBean::ResolveDomainToIP(const std::function<void()> &onFinished) {
|
||||
|
||||
@ -59,8 +59,6 @@ namespace NekoGui_fmt {
|
||||
|
||||
virtual int NeedExternal(bool isFirstProfile) { return 0; };
|
||||
|
||||
virtual CoreObjOutboundBuildResult BuildCoreObjV2Ray() { return {}; };
|
||||
|
||||
virtual CoreObjOutboundBuildResult BuildCoreObjSingBox() { return {}; };
|
||||
|
||||
virtual ExternalBuildResult BuildExternal(int mapping_port, int socks_port, int external_stat) { return {}; };
|
||||
|
||||
@ -191,27 +191,12 @@ namespace NekoGui_fmt {
|
||||
{"tls", coreTlsObj},
|
||||
};
|
||||
|
||||
if (proxy_type == proxy_Hysteria) {
|
||||
outbound["type"] = "hysteria";
|
||||
outbound["obfs"] = obfsPassword;
|
||||
outbound["disable_mtu_discovery"] = disableMtuDiscovery;
|
||||
outbound["recv_window"] = streamReceiveWindow;
|
||||
outbound["recv_window_conn"] = connectionReceiveWindow;
|
||||
outbound["up_mbps"] = uploadMbps;
|
||||
outbound["down_mbps"] = downloadMbps;
|
||||
|
||||
if (!hopPort.trimmed().isEmpty()) {
|
||||
outbound["hop_ports"] = hopPort;
|
||||
outbound["hop_interval"] = hopInterval;
|
||||
}
|
||||
if (authPayloadType == hysteria_auth_base64) outbound["auth"] = authPayload;
|
||||
if (authPayloadType == hysteria_auth_string) outbound["auth_str"] = authPayload;
|
||||
} else if (proxy_type == proxy_Hysteria2) {
|
||||
if (proxy_type == proxy_Hysteria2) {
|
||||
outbound["type"] = "hysteria2";
|
||||
outbound["password"] = password;
|
||||
outbound["up_mbps"] = uploadMbps;
|
||||
outbound["down_mbps"] = downloadMbps;
|
||||
|
||||
|
||||
if (!hopPort.trimmed().isEmpty()) {
|
||||
outbound["hop_ports"] = hopPort;
|
||||
outbound["hop_interval"] = hopInterval;
|
||||
|
||||
@ -1,211 +0,0 @@
|
||||
#include "db/ProxyEntity.hpp"
|
||||
#include "fmt/includes.h"
|
||||
|
||||
#define MAKE_SETTINGS_STREAM_SETTINGS \
|
||||
outbound["settings"] = settings; \
|
||||
auto streamSettings = stream->BuildStreamSettingsV2Ray(); \
|
||||
outbound["streamSettings"] = streamSettings;
|
||||
|
||||
namespace NekoGui_fmt {
|
||||
QJsonObject V2rayStreamSettings::BuildStreamSettingsV2Ray() {
|
||||
QJsonObject streamSettings{{"network", network}};
|
||||
|
||||
if (network == "ws") {
|
||||
QJsonObject ws;
|
||||
if (!host.isEmpty()) ws["headers"] = QJsonObject{{"Host", host}};
|
||||
// ws path & ed
|
||||
if (!path.isEmpty()) ws["path"] = path;
|
||||
streamSettings["wsSettings"] = ws;
|
||||
} else if (network == "http") {
|
||||
QJsonObject http;
|
||||
if (!path.isEmpty()) http["path"] = path;
|
||||
if (!host.isEmpty()) http["host"] = QList2QJsonArray(host.split(","));
|
||||
streamSettings["httpSettings"] = http;
|
||||
} else if (network == "grpc") {
|
||||
QJsonObject grpc;
|
||||
if (!path.isEmpty()) grpc["serviceName"] = path;
|
||||
streamSettings["grpcSettings"] = grpc;
|
||||
} else if (network == "quic") {
|
||||
QJsonObject quic;
|
||||
if (!header_type.isEmpty()) quic["header"] = QJsonObject{{"type", header_type}};
|
||||
if (!path.isEmpty()) quic["key"] = path;
|
||||
if (!host.isEmpty()) quic["security"] = host;
|
||||
streamSettings["quicSettings"] = quic;
|
||||
} else if (network == "tcp" && !header_type.isEmpty()) {
|
||||
QJsonObject header{{"type", header_type}};
|
||||
if (header_type == "http") {
|
||||
header["request"] = QJsonObject{
|
||||
{"path", QList2QJsonArray(path.split(","))},
|
||||
{"headers", QJsonObject{{"Host", QList2QJsonArray(host.split(","))}}},
|
||||
};
|
||||
}
|
||||
streamSettings["tcpSettings"] = QJsonObject{{"header", header}};
|
||||
}
|
||||
|
||||
if (security == "tls") {
|
||||
QJsonObject tls;
|
||||
if (!utlsFingerprint.isEmpty()) tls["fingerprint"] = utlsFingerprint;
|
||||
if (!sni.trimmed().isEmpty()) tls["serverName"] = sni;
|
||||
if (reality_pbk.trimmed().isEmpty()) {
|
||||
if (allow_insecure || NekoGui::dataStore->skip_cert) tls["allowInsecure"] = true;
|
||||
if (!alpn.trimmed().isEmpty()) tls["alpn"] = QList2QJsonArray(alpn.split(","));
|
||||
if (!certificate.trimmed().isEmpty()) {
|
||||
tls["disableSystemRoot"] = true;
|
||||
tls["certificates"] = QJsonArray{
|
||||
QJsonObject{
|
||||
{"usage", "verify"},
|
||||
{"certificate", QList2QJsonArray(SplitLines(certificate.trimmed()))},
|
||||
},
|
||||
};
|
||||
}
|
||||
streamSettings["tlsSettings"] = tls;
|
||||
streamSettings["security"] = "tls";
|
||||
} else {
|
||||
tls["publicKey"] = reality_pbk;
|
||||
tls["shortId"] = reality_sid;
|
||||
tls["spiderX"] = reality_spx;
|
||||
if (utlsFingerprint.isEmpty()) tls["fingerprint"] = "chrome";
|
||||
streamSettings["realitySettings"] = tls;
|
||||
streamSettings["security"] = "reality";
|
||||
}
|
||||
}
|
||||
|
||||
return streamSettings;
|
||||
}
|
||||
|
||||
CoreObjOutboundBuildResult SocksHttpBean::BuildCoreObjV2Ray() {
|
||||
CoreObjOutboundBuildResult result;
|
||||
|
||||
QJsonObject outbound;
|
||||
outbound["protocol"] = socks_http_type == type_HTTP ? "http" : "socks";
|
||||
|
||||
QJsonObject settings;
|
||||
QJsonArray servers;
|
||||
QJsonObject server;
|
||||
|
||||
server["address"] = serverAddress;
|
||||
server["port"] = serverPort;
|
||||
|
||||
QJsonArray users;
|
||||
QJsonObject user;
|
||||
user["user"] = username;
|
||||
user["pass"] = password;
|
||||
users.push_back(user);
|
||||
if (!username.isEmpty() && !password.isEmpty()) server["users"] = users;
|
||||
|
||||
servers.push_back(server);
|
||||
settings["servers"] = servers;
|
||||
|
||||
MAKE_SETTINGS_STREAM_SETTINGS
|
||||
|
||||
result.outbound = outbound;
|
||||
return result;
|
||||
}
|
||||
|
||||
CoreObjOutboundBuildResult ShadowSocksBean::BuildCoreObjV2Ray() {
|
||||
CoreObjOutboundBuildResult result;
|
||||
|
||||
QJsonObject outbound{{"protocol", "shadowsocks"}};
|
||||
|
||||
QJsonObject settings;
|
||||
QJsonArray servers;
|
||||
QJsonObject server;
|
||||
|
||||
server["address"] = serverAddress;
|
||||
server["port"] = serverPort;
|
||||
server["method"] = method;
|
||||
server["password"] = password;
|
||||
|
||||
if (uot != 0) {
|
||||
server["uot"] = true;
|
||||
server["UoTVersion"] = uot;
|
||||
} else {
|
||||
server["uot"] = false;
|
||||
}
|
||||
|
||||
servers.push_back(server);
|
||||
settings["servers"] = servers;
|
||||
|
||||
if (!plugin.trimmed().isEmpty()) {
|
||||
settings["plugin"] = SubStrBefore(plugin, ";");
|
||||
settings["pluginOpts"] = SubStrAfter(plugin, ";");
|
||||
}
|
||||
|
||||
MAKE_SETTINGS_STREAM_SETTINGS
|
||||
|
||||
result.outbound = outbound;
|
||||
return result;
|
||||
}
|
||||
|
||||
CoreObjOutboundBuildResult VMessBean::BuildCoreObjV2Ray() {
|
||||
CoreObjOutboundBuildResult result;
|
||||
QJsonObject outbound{{"protocol", "vmess"}};
|
||||
|
||||
QJsonObject settings{
|
||||
{"vnext", QJsonArray{
|
||||
QJsonObject{
|
||||
{"address", serverAddress},
|
||||
{"port", serverPort},
|
||||
{"users", QJsonArray{
|
||||
QJsonObject{
|
||||
{"id", uuid.trimmed()},
|
||||
{"alterId", aid},
|
||||
{"security", security},
|
||||
}}},
|
||||
}}}};
|
||||
|
||||
MAKE_SETTINGS_STREAM_SETTINGS
|
||||
|
||||
result.outbound = outbound;
|
||||
return result;
|
||||
}
|
||||
|
||||
CoreObjOutboundBuildResult TrojanVLESSBean::BuildCoreObjV2Ray() {
|
||||
CoreObjOutboundBuildResult result;
|
||||
QJsonObject outbound{
|
||||
{"protocol", proxy_type == proxy_VLESS ? "vless" : "trojan"},
|
||||
};
|
||||
|
||||
QJsonObject settings;
|
||||
if (proxy_type == proxy_VLESS) {
|
||||
if (flow == "none") {
|
||||
flow = "";
|
||||
}
|
||||
settings = QJsonObject{
|
||||
{"vnext", QJsonArray{
|
||||
QJsonObject{
|
||||
{"address", serverAddress},
|
||||
{"port", serverPort},
|
||||
{"users", QJsonArray{
|
||||
QJsonObject{
|
||||
{"id", password.trimmed()},
|
||||
{"encryption", "none"},
|
||||
{"flow", flow},
|
||||
}}},
|
||||
}}}};
|
||||
} else {
|
||||
settings = QJsonObject{
|
||||
{"servers", QJsonArray{
|
||||
QJsonObject{
|
||||
{"address", serverAddress},
|
||||
{"port", serverPort},
|
||||
{"password", password},
|
||||
}}}};
|
||||
}
|
||||
|
||||
MAKE_SETTINGS_STREAM_SETTINGS
|
||||
|
||||
result.outbound = outbound;
|
||||
return result;
|
||||
}
|
||||
|
||||
CoreObjOutboundBuildResult CustomBean::BuildCoreObjV2Ray() {
|
||||
CoreObjOutboundBuildResult result;
|
||||
|
||||
if (core == "internal") {
|
||||
result.outbound = QString2QJsonObject(config_simple);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
} // namespace NekoGui_fmt
|
||||
@ -9,7 +9,7 @@
|
||||
#define WriteTempFile(fn, data) \
|
||||
QDir dir; \
|
||||
if (!dir.exists("temp")) dir.mkdir("temp"); \
|
||||
QFile f(QString("temp/") + fn); \
|
||||
QFile f(QStringLiteral("temp/") + fn); \
|
||||
bool ok = f.open(QIODevice::WriteOnly | QIODevice::Truncate); \
|
||||
if (ok) { \
|
||||
f.write(data); \
|
||||
@ -36,54 +36,26 @@ namespace NekoGui_fmt {
|
||||
}
|
||||
|
||||
int QUICBean::NeedExternal(bool isFirstProfile) {
|
||||
auto hysteriaCore = [=] {
|
||||
auto extCore = [=] {
|
||||
if (isFirstProfile) {
|
||||
if (NekoGui::dataStore->spmode_vpn && hyProtocol != hysteria_protocol_facktcp && hopPort.trimmed().isEmpty()) {
|
||||
if (NekoGui::dataStore->spmode_vpn && hopPort.trimmed().isEmpty()) {
|
||||
return 1;
|
||||
}
|
||||
return 2;
|
||||
} else {
|
||||
if (hyProtocol == hysteria_protocol_facktcp || !hopPort.trimmed().isEmpty()) {
|
||||
if (!hopPort.trimmed().isEmpty()) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
};
|
||||
|
||||
auto hysteria2Core = [=] {
|
||||
if (isFirstProfile) {
|
||||
if (NekoGui::dataStore->spmode_vpn) {
|
||||
return 1;
|
||||
}
|
||||
return 2;
|
||||
}
|
||||
return 1;
|
||||
};
|
||||
|
||||
auto tuicCore = [=] {
|
||||
if (isFirstProfile) {
|
||||
if (NekoGui::dataStore->spmode_vpn) {
|
||||
return 1;
|
||||
}
|
||||
return 2;
|
||||
}
|
||||
return 1;
|
||||
};
|
||||
|
||||
if (IS_NEKO_BOX) {
|
||||
if (!forceExternal && (proxy_type == proxy_TUIC || hyProtocol == hysteria_protocol_udp)) {
|
||||
// sing-box support
|
||||
return 0;
|
||||
} else {
|
||||
// hysteria core support
|
||||
return hysteriaCore();
|
||||
}
|
||||
} else if (proxy_type == proxy_TUIC) {
|
||||
return tuicCore();
|
||||
} else if (proxy_type == proxy_Hysteria2) {
|
||||
return hysteria2Core();
|
||||
if (!forceExternal) {
|
||||
// sing-box support
|
||||
return 0;
|
||||
} else {
|
||||
return hysteriaCore();
|
||||
// external core support
|
||||
return extCore();
|
||||
}
|
||||
}
|
||||
|
||||
@ -241,68 +213,11 @@ namespace NekoGui_fmt {
|
||||
WriteTempFile("hysteria2_" + GetRandomString(10) + ".json", result.config_export.toUtf8());
|
||||
result.arguments = QStringList{"-c", TempFile};
|
||||
|
||||
return result;
|
||||
|
||||
} else { // Hysteria
|
||||
ExternalBuildResult result{NekoGui::dataStore->extraCore->Get("hysteria")};
|
||||
|
||||
QJsonObject config;
|
||||
|
||||
// determine server format
|
||||
auto is_direct = external_stat == 2;
|
||||
auto sniGen = sni;
|
||||
if (sni.isEmpty() && !IsIpAddress(serverAddress)) sniGen = serverAddress;
|
||||
|
||||
auto server = serverAddress;
|
||||
if (!hopPort.trimmed().isEmpty()) {
|
||||
server = WrapIPV6Host(server) + ":" + hopPort;
|
||||
} else {
|
||||
server = WrapIPV6Host(server) + ":" + Int2String(serverPort);
|
||||
}
|
||||
config["server"] = is_direct ? server : "127.0.0.1:" + Int2String(mapping_port);
|
||||
|
||||
// listen
|
||||
config["socks5"] = QJsonObject{
|
||||
{"listen", "127.0.0.1:" + Int2String(socks_port)},
|
||||
};
|
||||
|
||||
// misc
|
||||
|
||||
config["retry"] = 5;
|
||||
config["fast_open"] = true;
|
||||
config["lazy_start"] = true;
|
||||
config["obfs"] = obfsPassword;
|
||||
config["up_mbps"] = uploadMbps;
|
||||
config["down_mbps"] = downloadMbps;
|
||||
|
||||
if (authPayloadType == hysteria_auth_base64) config["auth"] = authPayload;
|
||||
if (authPayloadType == hysteria_auth_string) config["auth_str"] = authPayload;
|
||||
|
||||
if (hyProtocol == hysteria_protocol_facktcp) config["protocol"] = "faketcp";
|
||||
if (hyProtocol == hysteria_protocol_wechat_video) config["protocol"] = "wechat-video";
|
||||
|
||||
if (!sniGen.isEmpty()) config["server_name"] = sniGen;
|
||||
if (!alpn.isEmpty()) config["alpn"] = alpn;
|
||||
|
||||
if (!caText.trimmed().isEmpty()) {
|
||||
WriteTempFile("hysteria_" + GetRandomString(10) + ".crt", caText.toUtf8());
|
||||
config["ca"] = TempFile;
|
||||
}
|
||||
|
||||
if (allowInsecure) config["insecure"] = true;
|
||||
if (streamReceiveWindow > 0) config["recv_window_conn"] = streamReceiveWindow;
|
||||
if (connectionReceiveWindow > 0) config["recv_window"] = connectionReceiveWindow;
|
||||
if (disableMtuDiscovery) config["disable_mtu_discovery"] = true;
|
||||
config["hop_interval"] = hopInterval;
|
||||
|
||||
//
|
||||
|
||||
result.config_export = QJsonObject2QString(config, false);
|
||||
WriteTempFile("hysteria_" + GetRandomString(10) + ".json", result.config_export.toUtf8());
|
||||
result.arguments = QStringList{"--no-check", "-c", TempFile};
|
||||
|
||||
return result;
|
||||
}
|
||||
ExternalBuildResult e;
|
||||
e.error = "unknown type";
|
||||
return e;
|
||||
}
|
||||
|
||||
ExternalBuildResult CustomBean::BuildExternal(int mapping_port, int socks_port, int external_stat) {
|
||||
|
||||
@ -13,7 +13,7 @@ namespace NekoGui_fmt {
|
||||
url.setScheme("http");
|
||||
}
|
||||
} else {
|
||||
url.setScheme(QString("socks%1").arg(socks_http_type));
|
||||
url.setScheme(QStringLiteral("socks%1").arg(socks_http_type));
|
||||
}
|
||||
if (!name.isEmpty()) url.setFragment(name);
|
||||
if (!username.isEmpty()) url.setUserName(username);
|
||||
@ -182,29 +182,7 @@ namespace NekoGui_fmt {
|
||||
|
||||
QString QUICBean::ToShareLink() {
|
||||
QUrl url;
|
||||
if (proxy_type == proxy_Hysteria) {
|
||||
url.setScheme("hysteria");
|
||||
url.setHost(serverAddress);
|
||||
url.setPort(serverPort);
|
||||
QUrlQuery q;
|
||||
q.addQueryItem("upmbps", Int2String(uploadMbps));
|
||||
q.addQueryItem("downmbps", Int2String(downloadMbps));
|
||||
if (!obfsPassword.isEmpty()) {
|
||||
q.addQueryItem("obfs", "xplus");
|
||||
q.addQueryItem("obfsParam", obfsPassword);
|
||||
}
|
||||
if (authPayloadType == hysteria_auth_string) q.addQueryItem("auth", authPayload);
|
||||
if (hyProtocol == hysteria_protocol_facktcp) q.addQueryItem("protocol", "faketcp");
|
||||
if (hyProtocol == hysteria_protocol_wechat_video) q.addQueryItem("protocol", "wechat-video");
|
||||
if (!hopPort.trimmed().isEmpty()) q.addQueryItem("mport", hopPort);
|
||||
if (allowInsecure) q.addQueryItem("insecure", "1");
|
||||
if (!sni.isEmpty()) q.addQueryItem("peer", sni);
|
||||
if (!alpn.isEmpty()) q.addQueryItem("alpn", alpn);
|
||||
if (connectionReceiveWindow > 0) q.addQueryItem("recv_window", Int2String(connectionReceiveWindow));
|
||||
if (streamReceiveWindow > 0) q.addQueryItem("recv_window_conn", Int2String(streamReceiveWindow));
|
||||
if (!q.isEmpty()) url.setQuery(q);
|
||||
if (!name.isEmpty()) url.setFragment(name);
|
||||
} else if (proxy_type == proxy_TUIC) {
|
||||
if (proxy_type == proxy_TUIC) {
|
||||
url.setScheme("tuic");
|
||||
url.setUserName(uuid);
|
||||
url.setPassword(password);
|
||||
@ -244,4 +222,4 @@ namespace NekoGui_fmt {
|
||||
return url.toString(QUrl::FullyEncoded);
|
||||
}
|
||||
|
||||
} // namespace NekoGui_fmt
|
||||
} // namespace NekoGui_fmt
|
||||
|
||||
@ -24,7 +24,7 @@ namespace NekoGui_fmt {
|
||||
QString DisplayType() override {
|
||||
if (core == "internal") {
|
||||
auto obj = QString2QJsonObject(config_simple);
|
||||
return obj[IS_NEKO_BOX ? "type" : "protocol"].toString();
|
||||
return obj["type"].toString();
|
||||
} else if (core == "internal-full") {
|
||||
return software_core_name + " config";
|
||||
}
|
||||
@ -36,11 +36,7 @@ namespace NekoGui_fmt {
|
||||
QString DisplayAddress() override {
|
||||
if (core == "internal") {
|
||||
auto obj = QString2QJsonObject(config_simple);
|
||||
if (IS_NEKO_BOX) {
|
||||
return ::DisplayAddress(obj["server"].toString(), obj["server_port"].toInt());
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
return ::DisplayAddress(obj["server"].toString(), obj["server_port"].toInt());
|
||||
} else if (core == "internal-full") {
|
||||
return {};
|
||||
}
|
||||
@ -52,7 +48,5 @@ namespace NekoGui_fmt {
|
||||
ExternalBuildResult BuildExternal(int mapping_port, int socks_port, int external_stat) override;
|
||||
|
||||
CoreObjOutboundBuildResult BuildCoreObjSingBox() override;
|
||||
|
||||
CoreObjOutboundBuildResult BuildCoreObjV2Ray() override;
|
||||
};
|
||||
} // namespace NekoGui_fmt
|
||||
@ -55,7 +55,7 @@ namespace NekoGui_fmt {
|
||||
|
||||
// security
|
||||
|
||||
auto type = GetQueryValue(query, "type", "tcp");
|
||||
auto type = GetQueryValue(query, "type", "tcp");
|
||||
if (type == "h2") {
|
||||
type = "http";
|
||||
}
|
||||
@ -256,37 +256,7 @@ namespace NekoGui_fmt {
|
||||
auto query = QUrlQuery(url.query());
|
||||
if (url.host().isEmpty() || url.port() == -1) return false;
|
||||
|
||||
if (url.scheme() == "hysteria") {
|
||||
// https://hysteria.network/docs/uri-scheme/
|
||||
if (!query.hasQueryItem("upmbps") || !query.hasQueryItem("downmbps")) return false;
|
||||
|
||||
name = url.fragment(QUrl::FullyDecoded);
|
||||
serverAddress = url.host();
|
||||
serverPort = url.port();
|
||||
hopPort = query.queryItemValue("mport");
|
||||
obfsPassword = query.queryItemValue("obfsParam");
|
||||
allowInsecure = QStringList{"1", "true"}.contains(query.queryItemValue("insecure"));
|
||||
uploadMbps = query.queryItemValue("upmbps").toInt();
|
||||
downloadMbps = query.queryItemValue("downmbps").toInt();
|
||||
|
||||
auto protocolStr = (query.hasQueryItem("protocol") ? query.queryItemValue("protocol") : "udp").toLower();
|
||||
if (protocolStr == "faketcp") {
|
||||
hyProtocol = NekoGui_fmt::QUICBean::hysteria_protocol_facktcp;
|
||||
} else if (protocolStr.startsWith("wechat")) {
|
||||
hyProtocol = NekoGui_fmt::QUICBean::hysteria_protocol_wechat_video;
|
||||
}
|
||||
|
||||
if (query.hasQueryItem("auth")) {
|
||||
authPayload = query.queryItemValue("auth");
|
||||
authPayloadType = NekoGui_fmt::QUICBean::hysteria_auth_string;
|
||||
}
|
||||
|
||||
alpn = query.queryItemValue("alpn");
|
||||
sni = FIRST_OR_SECOND(query.queryItemValue("peer"), query.queryItemValue("sni"));
|
||||
|
||||
connectionReceiveWindow = query.queryItemValue("recv_window").toInt();
|
||||
streamReceiveWindow = query.queryItemValue("recv_window_conn").toInt();
|
||||
} else if (url.scheme() == "tuic") {
|
||||
if (url.scheme() == "tuic") {
|
||||
// by daeuniverse
|
||||
// https://github.com/daeuniverse/dae/discussions/182
|
||||
|
||||
|
||||
@ -1,15 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
namespace Preset {
|
||||
namespace Xray {
|
||||
inline QStringList UtlsFingerPrint = {"", "chrome", "firefox", "edge", "safari", "360", "qq", "ios", "android", "random", "randomized"};
|
||||
inline QStringList ShadowsocksMethods = {"aes-128-gcm", "aes-256-gcm", "aes-192-gcm", "chacha20-ietf-poly1305", "xchacha20-ietf-poly1305",
|
||||
"2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm", "2022-blake3-chacha20-poly1305",
|
||||
"aes-128-ctr", "aes-192-ctr", "aes-256-ctr", "aes-128-cfb", "aes-192-cfb", "aes-256-cfb",
|
||||
"rc4", "rc4-md5", "bf-cfb", "chacha20", "chacha20-ietf", "xchacha20", "none"};
|
||||
inline QStringList Flows = {"xtls-rprx-vision", "xtls-rprx-vision-udp443"};
|
||||
} // namespace Xray
|
||||
|
||||
namespace SingBox {
|
||||
inline QStringList VpnImplementation = {"gvisor", "system", "mixed"};
|
||||
inline QStringList DomainStrategy = {"", "ipv4_only", "ipv6_only", "prefer_ipv4", "prefer_ipv6"};
|
||||
|
||||
@ -5,32 +5,19 @@
|
||||
namespace NekoGui_fmt {
|
||||
class QUICBean : public AbstractBean {
|
||||
public:
|
||||
static constexpr int proxy_Hysteria = 0;
|
||||
// static constexpr int proxy_Hysteria = 0;
|
||||
static constexpr int proxy_TUIC = 1;
|
||||
static constexpr int proxy_Hysteria2 = 3;
|
||||
int proxy_type = proxy_Hysteria;
|
||||
int proxy_type = proxy_Hysteria2;
|
||||
|
||||
bool forceExternal = false;
|
||||
|
||||
// Hysteria 1
|
||||
|
||||
static constexpr int hysteria_protocol_udp = 0;
|
||||
static constexpr int hysteria_protocol_facktcp = 1;
|
||||
static constexpr int hysteria_protocol_wechat_video = 2;
|
||||
int hyProtocol = 0;
|
||||
|
||||
static constexpr int hysteria_auth_none = 0;
|
||||
static constexpr int hysteria_auth_string = 1;
|
||||
static constexpr int hysteria_auth_base64 = 2;
|
||||
int authPayloadType = 0;
|
||||
QString authPayload = "";
|
||||
|
||||
// Hysteria 1&2
|
||||
// Hysteria 2
|
||||
|
||||
QString obfsPassword = "";
|
||||
|
||||
int uploadMbps = 100;
|
||||
int downloadMbps = 100;
|
||||
int uploadMbps = 0;
|
||||
int downloadMbps = 0;
|
||||
|
||||
qint64 streamReceiveWindow = 0;
|
||||
qint64 connectionReceiveWindow = 0;
|
||||
@ -62,8 +49,7 @@ namespace NekoGui_fmt {
|
||||
|
||||
explicit QUICBean(int _proxy_type) : AbstractBean(0) {
|
||||
proxy_type = _proxy_type;
|
||||
if (proxy_type == proxy_Hysteria || proxy_type == proxy_Hysteria2) {
|
||||
_add(new configItem("authPayload", &authPayload, itemType::string));
|
||||
if (proxy_type == proxy_Hysteria2) {
|
||||
_add(new configItem("obfsPassword", &obfsPassword, itemType::string));
|
||||
_add(new configItem("uploadMbps", &uploadMbps, itemType::integer));
|
||||
_add(new configItem("downloadMbps", &downloadMbps, itemType::integer));
|
||||
@ -72,14 +58,7 @@ namespace NekoGui_fmt {
|
||||
_add(new configItem("disableMtuDiscovery", &disableMtuDiscovery, itemType::boolean));
|
||||
_add(new configItem("hopInterval", &hopInterval, itemType::integer));
|
||||
_add(new configItem("hopPort", &hopPort, itemType::string));
|
||||
if (proxy_type == proxy_Hysteria) { // hy1
|
||||
_add(new configItem("authPayloadType", &authPayloadType, itemType::integer));
|
||||
_add(new configItem("protocol", &hyProtocol, itemType::integer));
|
||||
} else { // hy2
|
||||
uploadMbps = 0;
|
||||
downloadMbps = 0;
|
||||
_add(new configItem("password", &password, itemType::string));
|
||||
}
|
||||
_add(new configItem("password", &password, itemType::string));
|
||||
} else if (proxy_type == proxy_TUIC) {
|
||||
_add(new configItem("uuid", &uuid, itemType::string));
|
||||
_add(new configItem("password", &password, itemType::string));
|
||||
@ -108,8 +87,6 @@ namespace NekoGui_fmt {
|
||||
return software_core_name;
|
||||
} else if (proxy_type == proxy_TUIC) {
|
||||
return "tuic";
|
||||
} else if (proxy_type == proxy_Hysteria) {
|
||||
return "hysteria";
|
||||
} else {
|
||||
return "hysteria2";
|
||||
}
|
||||
@ -118,8 +95,6 @@ namespace NekoGui_fmt {
|
||||
QString DisplayType() override {
|
||||
if (proxy_type == proxy_TUIC) {
|
||||
return "TUIC";
|
||||
} else if (proxy_type == proxy_Hysteria) {
|
||||
return "Hysteria1";
|
||||
} else {
|
||||
return "Hysteria2";
|
||||
}
|
||||
|
||||
@ -23,8 +23,6 @@ namespace NekoGui_fmt {
|
||||
|
||||
QString DisplayType() override { return "Shadowsocks"; };
|
||||
|
||||
CoreObjOutboundBuildResult BuildCoreObjV2Ray() override;
|
||||
|
||||
CoreObjOutboundBuildResult BuildCoreObjSingBox() override;
|
||||
|
||||
bool TryParseLink(const QString &link);
|
||||
|
||||
@ -26,8 +26,6 @@ namespace NekoGui_fmt {
|
||||
|
||||
QString DisplayType() override { return socks_http_type == type_HTTP ? "HTTP" : "Socks"; };
|
||||
|
||||
CoreObjOutboundBuildResult BuildCoreObjV2Ray() override;
|
||||
|
||||
CoreObjOutboundBuildResult BuildCoreObjSingBox() override;
|
||||
|
||||
bool TryParseLink(const QString &link);
|
||||
|
||||
@ -24,8 +24,6 @@ namespace NekoGui_fmt {
|
||||
|
||||
QString DisplayType() override { return proxy_type == proxy_VLESS ? "VLESS" : "Trojan"; };
|
||||
|
||||
CoreObjOutboundBuildResult BuildCoreObjV2Ray() override;
|
||||
|
||||
CoreObjOutboundBuildResult BuildCoreObjSingBox() override;
|
||||
|
||||
bool TryParseLink(const QString &link);
|
||||
|
||||
@ -49,8 +49,6 @@ namespace NekoGui_fmt {
|
||||
_add(new configItem("mux_s", &multiplex_status, itemType::integer));
|
||||
}
|
||||
|
||||
QJsonObject BuildStreamSettingsV2Ray();
|
||||
|
||||
void BuildStreamSettingsSingBox(QJsonObject *outbound);
|
||||
};
|
||||
|
||||
|
||||
@ -21,8 +21,6 @@ namespace NekoGui_fmt {
|
||||
|
||||
QString DisplayType() override { return "VMess"; };
|
||||
|
||||
CoreObjOutboundBuildResult BuildCoreObjV2Ray() override;
|
||||
|
||||
CoreObjOutboundBuildResult BuildCoreObjSingBox() override;
|
||||
|
||||
bool TryParseLink(const QString &link);
|
||||
|
||||
@ -40,6 +40,7 @@ require (
|
||||
github.com/libdns/cloudflare v0.1.1 // indirect
|
||||
github.com/libdns/libdns v0.2.2 // indirect
|
||||
github.com/logrusorgru/aurora v2.0.3+incompatible // indirect
|
||||
github.com/metacubex/tfo-go v0.0.0-20240821025650-e9be0afd5e7d // indirect
|
||||
github.com/mholt/acmez v1.2.0 // indirect
|
||||
github.com/miekg/dns v1.1.59 // indirect
|
||||
github.com/onsi/ginkgo/v2 v2.9.7 // indirect
|
||||
@ -52,19 +53,18 @@ require (
|
||||
github.com/sagernet/cloudflare-tls v0.0.0-20231208171750-a4483c1b7cd1 // indirect
|
||||
github.com/sagernet/gvisor v0.0.0-20240428053021-e691de28565f // indirect
|
||||
github.com/sagernet/netlink v0.0.0-20240523065131-45e60152f9ba // indirect
|
||||
github.com/sagernet/quic-go v0.45.1-beta.2 // indirect
|
||||
github.com/sagernet/quic-go v0.47.0-beta.2 // indirect
|
||||
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 // indirect
|
||||
github.com/sagernet/sing v0.4.1 // indirect
|
||||
github.com/sagernet/sing-dns v0.2.1-0.20240624030536-ca4a5f7afb65 // indirect
|
||||
github.com/sagernet/sing v0.4.3 // indirect
|
||||
github.com/sagernet/sing-dns v0.2.3 // indirect
|
||||
github.com/sagernet/sing-mux v0.2.0 // indirect
|
||||
github.com/sagernet/sing-quic v0.2.0-beta.12 // indirect
|
||||
github.com/sagernet/sing-shadowsocks v0.2.6 // indirect
|
||||
github.com/sagernet/sing-quic v0.2.2 // indirect
|
||||
github.com/sagernet/sing-shadowsocks v0.2.7 // indirect
|
||||
github.com/sagernet/sing-shadowsocks2 v0.2.0 // indirect
|
||||
github.com/sagernet/sing-shadowtls v0.1.4 // indirect
|
||||
github.com/sagernet/sing-tun v0.3.2 // indirect
|
||||
github.com/sagernet/sing-vmess v0.1.8 // indirect
|
||||
github.com/sagernet/sing-tun v0.3.3 // indirect
|
||||
github.com/sagernet/sing-vmess v0.1.12 // indirect
|
||||
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 // indirect
|
||||
github.com/sagernet/tfo-go v0.0.0-20231209031829-7b5343ac1dc6 // indirect
|
||||
github.com/sagernet/utls v1.5.4 // indirect
|
||||
github.com/sagernet/wireguard-go v0.0.0-20231215174105-89dec3b2f3e8 // indirect
|
||||
github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854 // indirect
|
||||
@ -80,16 +80,16 @@ require (
|
||||
golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f // indirect
|
||||
golang.org/x/mod v0.18.0 // indirect
|
||||
golang.org/x/net v0.25.0 // indirect
|
||||
golang.org/x/sync v0.7.0 // indirect
|
||||
golang.org/x/sys v0.21.0 // indirect
|
||||
golang.org/x/text v0.16.0 // indirect
|
||||
golang.org/x/sync v0.8.0 // indirect
|
||||
golang.org/x/sys v0.25.0 // indirect
|
||||
golang.org/x/text v0.18.0 // indirect
|
||||
golang.org/x/time v0.5.0 // indirect
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
|
||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de // indirect
|
||||
google.golang.org/grpc v1.63.2 // indirect
|
||||
google.golang.org/protobuf v1.33.0 // indirect
|
||||
lukechampine.com/blake3 v1.2.1 // indirect
|
||||
lukechampine.com/blake3 v1.3.0 // indirect
|
||||
)
|
||||
|
||||
replace grpc_server => ../../grpc_server
|
||||
@ -98,7 +98,7 @@ replace github.com/matsuridayo/libneko => ../../../../libneko
|
||||
|
||||
replace github.com/sagernet/sing-box => ../../../../sing-box
|
||||
|
||||
// replace github.com/sagernet/sing-quic => ../../../../sing-quic
|
||||
replace github.com/sagernet/sing-quic => ../../../../sing-quic
|
||||
|
||||
// replace github.com/sagernet/sing => ../../../../sing
|
||||
|
||||
|
||||
@ -94,6 +94,8 @@ github.com/libdns/libdns v0.2.2 h1:O6ws7bAfRPaBsgAYt8MDe2HcNBGC29hkZ9MX2eUSX3s=
|
||||
github.com/libdns/libdns v0.2.2/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ=
|
||||
github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8=
|
||||
github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
|
||||
github.com/metacubex/tfo-go v0.0.0-20240821025650-e9be0afd5e7d h1:j9LtzkYstLFoNvXW824QQeN7Y26uPL5249kzWKbzO9U=
|
||||
github.com/metacubex/tfo-go v0.0.0-20240821025650-e9be0afd5e7d/go.mod h1:c7bVFM9f5+VzeZ/6Kg77T/jrg1Xp8QpqlSHvG/aXVts=
|
||||
github.com/mholt/acmez v1.2.0 h1:1hhLxSgY5FvH5HCnGUuwbKY2VQVo8IU7rxXKSnZ7F30=
|
||||
github.com/mholt/acmez v1.2.0/go.mod h1:VT9YwH1xgNX1kmYY89gY8xPJC84BFAisjo8Egigt4kE=
|
||||
github.com/miekg/dns v1.1.59 h1:C9EXc/UToRwKLhK5wKU/I4QVsBUc8kE6MkHBkeypWZs=
|
||||
@ -125,33 +127,29 @@ github.com/sagernet/gvisor v0.0.0-20240428053021-e691de28565f h1:NkhuupzH5ch7b/Y
|
||||
github.com/sagernet/gvisor v0.0.0-20240428053021-e691de28565f/go.mod h1:KXmw+ouSJNOsuRpg4wgwwCQuunrGz4yoAqQjsLjc6N0=
|
||||
github.com/sagernet/netlink v0.0.0-20240523065131-45e60152f9ba h1:EY5AS7CCtfmARNv2zXUOrsEMPFDGYxaw65JzA2p51Vk=
|
||||
github.com/sagernet/netlink v0.0.0-20240523065131-45e60152f9ba/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
|
||||
github.com/sagernet/quic-go v0.45.1-beta.2 h1:zkEeCbhdFFkrxKcuIRBtXNKci/1t2J/39QSG/sPvlmc=
|
||||
github.com/sagernet/quic-go v0.45.1-beta.2/go.mod h1:+N3FqM9DAzOWfe64uxXuBejVJwX7DeW7BslzLO6N/xI=
|
||||
github.com/sagernet/quic-go v0.47.0-beta.2 h1:1tCGWFOSaXIeuQaHrwOMJIYvlupjTcaVInGQw5ArULU=
|
||||
github.com/sagernet/quic-go v0.47.0-beta.2/go.mod h1:bLVKvElSEMNv7pu7SZHscW02TYigzQ5lQu3Nh4wNh8Q=
|
||||
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 h1:5Th31OC6yj8byLGkEnIYp6grlXfo1QYUfiYFGjewIdc=
|
||||
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU=
|
||||
github.com/sagernet/sing v0.2.18/go.mod h1:OL6k2F0vHmEzXz2KW19qQzu172FDgSbUSODylighuVo=
|
||||
github.com/sagernet/sing v0.4.1 h1:zVlpE+7k7AFoC2pv6ReqLf0PIHjihL/jsBl5k05PQFk=
|
||||
github.com/sagernet/sing v0.4.1/go.mod h1:ieZHA/+Y9YZfXs2I3WtuwgyCZ6GPsIR7HdKb1SdEnls=
|
||||
github.com/sagernet/sing-dns v0.2.1-0.20240624030536-ca4a5f7afb65 h1:lcCe7E1csuyUA3RCvpFcIYOy6FIifDthKaCrUjLG4xA=
|
||||
github.com/sagernet/sing-dns v0.2.1-0.20240624030536-ca4a5f7afb65/go.mod h1:dArgyPZmK8+zDBVRMjV3r12zHgnTara0ahrWwSe/eQE=
|
||||
github.com/sagernet/sing v0.4.3 h1:Ty/NAiNnVd6844k7ujlL5lkzydhcTH5Psc432jXA4Y8=
|
||||
github.com/sagernet/sing v0.4.3/go.mod h1:ieZHA/+Y9YZfXs2I3WtuwgyCZ6GPsIR7HdKb1SdEnls=
|
||||
github.com/sagernet/sing-dns v0.2.3 h1:YzeBUn2tR38F7HtvGEQ0kLRLmZWMEgi/+7wqa4Twb1k=
|
||||
github.com/sagernet/sing-dns v0.2.3/go.mod h1:BJpJv6XLnrUbSyIntOT6DG9FW0f4fETmPAHvNjOprLg=
|
||||
github.com/sagernet/sing-mux v0.2.0 h1:4C+vd8HztJCWNYfufvgL49xaOoOHXty2+EAjnzN3IYo=
|
||||
github.com/sagernet/sing-mux v0.2.0/go.mod h1:khzr9AOPocLa+g53dBplwNDz4gdsyx/YM3swtAhlkHQ=
|
||||
github.com/sagernet/sing-quic v0.2.0-beta.12 h1:BhvA5mmrDFEyDUQB5eeu+9UhF+ieyuNJ5Rsb0dAG3QY=
|
||||
github.com/sagernet/sing-quic v0.2.0-beta.12/go.mod h1:YVpLfVi8BvYM7NMrjmnvcRm3E8iMETf1gFQmTQDN9jI=
|
||||
github.com/sagernet/sing-shadowsocks v0.2.6 h1:xr7ylAS/q1cQYS8oxKKajhuQcchd5VJJ4K4UZrrpp0s=
|
||||
github.com/sagernet/sing-shadowsocks v0.2.6/go.mod h1:j2YZBIpWIuElPFL/5sJAj470bcn/3QQ5lxZUNKLDNAM=
|
||||
github.com/sagernet/sing-shadowsocks v0.2.7 h1:zaopR1tbHEw5Nk6FAkM05wCslV6ahVegEZaKMv9ipx8=
|
||||
github.com/sagernet/sing-shadowsocks v0.2.7/go.mod h1:0rIKJZBR65Qi0zwdKezt4s57y/Tl1ofkaq6NlkzVuyE=
|
||||
github.com/sagernet/sing-shadowsocks2 v0.2.0 h1:wpZNs6wKnR7mh1wV9OHwOyUr21VkS3wKFHi+8XwgADg=
|
||||
github.com/sagernet/sing-shadowsocks2 v0.2.0/go.mod h1:RnXS0lExcDAovvDeniJ4IKa2IuChrdipolPYWBv9hWQ=
|
||||
github.com/sagernet/sing-shadowtls v0.1.4 h1:aTgBSJEgnumzFenPvc+kbD9/W0PywzWevnVpEx6Tw3k=
|
||||
github.com/sagernet/sing-shadowtls v0.1.4/go.mod h1:F8NBgsY5YN2beQavdgdm1DPlhaKQlaL6lpDdcBglGK4=
|
||||
github.com/sagernet/sing-tun v0.3.2 h1:z0bLUT/YXH9RrJS9DsIpB0Bb9afl2hVJOmHd0zA3HJY=
|
||||
github.com/sagernet/sing-tun v0.3.2/go.mod h1:DxLIyhjWU/HwGYoX0vNGg2c5QgTQIakphU1MuERR5tQ=
|
||||
github.com/sagernet/sing-vmess v0.1.8 h1:XVWad1RpTy9b5tPxdm5MCU8cGfrTGdR8qCq6HV2aCNc=
|
||||
github.com/sagernet/sing-vmess v0.1.8/go.mod h1:vhx32UNzTDUkNwOyIjcZQohre1CaytquC5mPplId8uA=
|
||||
github.com/sagernet/sing-tun v0.3.3 h1:LZnQNmfGcNG2KPTPkLgc+Lo7k606QJVkPp2DnjriwUk=
|
||||
github.com/sagernet/sing-tun v0.3.3/go.mod h1:DxLIyhjWU/HwGYoX0vNGg2c5QgTQIakphU1MuERR5tQ=
|
||||
github.com/sagernet/sing-vmess v0.1.12 h1:2gFD8JJb+eTFMoa8FIVMnknEi+vCSfaiTXTfEYAYAPg=
|
||||
github.com/sagernet/sing-vmess v0.1.12/go.mod h1:luTSsfyBGAc9VhtCqwjR+dt1QgqBhuYBCONB/POhF8I=
|
||||
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 h1:DImB4lELfQhplLTxeq2z31Fpv8CQqqrUwTbrIRumZqQ=
|
||||
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7/go.mod h1:FP9X2xjT/Az1EsG/orYYoC+5MojWnuI7hrffz8fGwwo=
|
||||
github.com/sagernet/tfo-go v0.0.0-20231209031829-7b5343ac1dc6 h1:z3SJQhVyU63FT26Wn/UByW6b7q8QKB0ZkPqsyqcz2PI=
|
||||
github.com/sagernet/tfo-go v0.0.0-20231209031829-7b5343ac1dc6/go.mod h1:73xRZuxwkFk4aiLw28hG8W6o9cr2UPrGL9pdY2UTbvY=
|
||||
github.com/sagernet/utls v1.5.4 h1:KmsEGbB2dKUtCNC+44NwAdNAqnqQ6GA4pTO0Yik56co=
|
||||
github.com/sagernet/utls v1.5.4/go.mod h1:CTGxPWExIloRipK3XFpYv0OVyhO8kk3XCGW/ieyTh1s=
|
||||
github.com/sagernet/wireguard-go v0.0.0-20231215174105-89dec3b2f3e8 h1:R0OMYAScomNAVpTfbHFpxqJpvwuhxSRi+g6z7gZhABs=
|
||||
@ -229,8 +227,8 @@ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
|
||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
|
||||
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@ -245,15 +243,15 @@ golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
|
||||
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
|
||||
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
|
||||
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
||||
golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
|
||||
golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
||||
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
@ -296,5 +294,5 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
lukechampine.com/blake3 v1.2.1 h1:YuqqRuaqsGV71BV/nm9xlI0MKUv4QC54jQnBChWbGnI=
|
||||
lukechampine.com/blake3 v1.2.1/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k=
|
||||
lukechampine.com/blake3 v1.3.0 h1:sJ3XhFINmHSrYCgl958hscfIa3bw8x4DqMP3u1YvoYE=
|
||||
lukechampine.com/blake3 v1.3.0/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k=
|
||||
|
||||
@ -17,7 +17,7 @@ func Launcher() {
|
||||
_debug := flag.Bool("debug", false, "Debug mode")
|
||||
flag.Parse()
|
||||
|
||||
cmd := exec.Command("./nekoray", flag.Args()...)
|
||||
cmd := exec.Command("./nekobox", flag.Args()...)
|
||||
|
||||
ld_env := "LD_LIBRARY_PATH=" + filepath.Join(wd, "./usr/lib")
|
||||
qt_plugin_env := "QT_PLUGIN_PATH=" + filepath.Join(wd, "./usr/plugins")
|
||||
|
||||
@ -30,9 +30,9 @@ func main() {
|
||||
time.Sleep(time.Second)
|
||||
Updater()
|
||||
// 3. start
|
||||
exec.Command("./nekoray.exe").Start()
|
||||
exec.Command("./nekobox.exe").Start()
|
||||
} else {
|
||||
// 1. nekoray stop it self and run "updater.exe"
|
||||
// 1. main prog quit and run "updater.exe"
|
||||
Copy("./updater.exe", "./updater.old")
|
||||
exec.Command("./updater.old", os.Args[1:]...).Start()
|
||||
}
|
||||
@ -43,7 +43,7 @@ func main() {
|
||||
if os.Getenv("NKR_FROM_LAUNCHER") == "1" {
|
||||
Launcher()
|
||||
} else {
|
||||
exec.Command("./nekoray").Start()
|
||||
exec.Command("./nekobox").Start()
|
||||
}
|
||||
}
|
||||
return
|
||||
|
||||
@ -71,6 +71,11 @@ func Updater() {
|
||||
os.RemoveAll("./nekoray_update")
|
||||
os.RemoveAll("./nekoray.zip")
|
||||
os.RemoveAll("./nekoray.tar.gz")
|
||||
|
||||
// nekoray -> nekobox
|
||||
os.Remove("./nekoray.exe")
|
||||
os.Remove("./nekoray.png")
|
||||
os.Remove("./nekoray_core.exe")
|
||||
}
|
||||
|
||||
func Exist(path string) bool {
|
||||
|
||||
@ -53,10 +53,6 @@ func (s *BaseServer) Update(ctx context.Context, in *gen.UpdateReq) (*gen.Update
|
||||
var search string
|
||||
if runtime.GOOS == "windows" && runtime.GOARCH == "amd64" {
|
||||
search = "windows64"
|
||||
// check Qt5 update after nekoray v3.3
|
||||
if _, err := os.Stat("../Qt5Core.dll"); err == nil {
|
||||
search = "windows7-x64"
|
||||
}
|
||||
} else if runtime.GOOS == "linux" && runtime.GOARCH == "amd64" {
|
||||
search = "linux64"
|
||||
} else if runtime.GOOS == "darwin" {
|
||||
|
||||
@ -23,5 +23,5 @@ popd
|
||||
|
||||
#### Go: nekobox_core ####
|
||||
pushd go/cmd/nekobox_core
|
||||
go build -v -o $DEST -trimpath -ldflags "-w -s -X $neko_common.Version_neko=$version_standalone" -tags "with_clash_api,with_gvisor,with_quic,with_wireguard,with_utls,with_ech"
|
||||
go build -v -o $DEST -trimpath -ldflags "-w -s -X github.com/matsuridayo/libneko/neko_common.Version_neko=$version_standalone" -tags "with_clash_api,with_gvisor,with_quic,with_wireguard,with_utls,with_ech"
|
||||
popd
|
||||
|
||||
@ -7,7 +7,7 @@ rm -rf $DEST
|
||||
mkdir -p $DEST
|
||||
|
||||
#### copy binary ####
|
||||
cp $BUILD/nekoray $DEST
|
||||
cp $BUILD/nekobox $DEST
|
||||
|
||||
#### Download: prebuilt runtime ####
|
||||
curl -Lso usr.zip https://github.com/MatsuriDayo/nekoray_qt_runtime/releases/download/20220503/20230202-5.12.8-ubuntu20.04-linux64.zip
|
||||
|
||||
@ -2,20 +2,16 @@
|
||||
set -e
|
||||
|
||||
source libs/env_deploy.sh
|
||||
if [ "$DL_QT_VER" == "5.15" ]; then
|
||||
DEST=$DEPLOYMENT/windows7-x64
|
||||
else
|
||||
DEST=$DEPLOYMENT/windows64
|
||||
fi
|
||||
DEST=$DEPLOYMENT/windows64
|
||||
rm -rf $DEST
|
||||
mkdir -p $DEST
|
||||
|
||||
#### copy exe ####
|
||||
cp $BUILD/nekoray.exe $DEST
|
||||
cp $BUILD/nekobox.exe $DEST
|
||||
|
||||
#### deploy qt & DLL runtime ####
|
||||
pushd $DEST
|
||||
windeployqt nekoray.exe --no-compiler-runtime --no-system-d3d-compiler --no-opengl-sw --verbose 2
|
||||
windeployqt nekobox.exe --no-compiler-runtime --no-system-d3d-compiler --no-opengl-sw --verbose 2
|
||||
rm -rf translations
|
||||
rm -rf libEGL.dll libGLESv2.dll Qt6Pdf.dll
|
||||
|
||||
|
||||
@ -3,7 +3,7 @@ cd qtsdk
|
||||
if [ "$DL_QT_VER" == "5.15" ]; then
|
||||
curl -LSO https://github.com/MatsuriDayo/nekoray_qt_runtime/releases/download/20220503/Qt5.15.7-Windows-x86_64-VS2019-16.11.20-20221103.7z
|
||||
else
|
||||
curl -LSO https://github.com/MatsuriDayo/nekoray_qt_runtime/releases/download/20220503/Qt6.5.2-Windows-x86_64-VS2022-17.6.5-20230803.7z
|
||||
curl -LSO https://github.com/MatsuriDayo/nekoray_qt_runtime/releases/download/20220503/Qt6.7.2-Windows-x86_64-VS2022-17.10.3-20240621.7z
|
||||
fi
|
||||
7z x *.7z
|
||||
rm *.7z
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
SRC_ROOT="$PWD"
|
||||
DEPLOYMENT="$SRC_ROOT/deployment"
|
||||
BUILD="$SRC_ROOT/build"
|
||||
version_standalone="nekoray-"$(cat nekoray_version.txt)
|
||||
version_standalone="nekoray-"$(cat nekoray_version.txt) # 下次改
|
||||
|
||||
@ -18,6 +18,16 @@ popd
|
||||
|
||||
####
|
||||
|
||||
if [ ! -d "sing-quic" ]; then
|
||||
git clone --no-checkout https://github.com/MatsuriDayo/sing-quic.git
|
||||
fi
|
||||
pushd sing-quic
|
||||
git checkout "$COMMIT_SING_QUIC"
|
||||
|
||||
popd
|
||||
|
||||
####
|
||||
|
||||
if [ ! -d "libneko" ]; then
|
||||
git clone --no-checkout https://github.com/MatsuriDayo/libneko.git
|
||||
fi
|
||||
|
||||
@ -1,2 +1,3 @@
|
||||
export COMMIT_SING_BOX="cf36758f11b7c144e1211801753cc91f06ff2906"
|
||||
export COMMIT_SING_BOX="06557f6cef23160668122a17a818b378b5a216b5"
|
||||
export COMMIT_SING_QUIC="b49ce60d9b3622d5238fee96bfd3c5f6e3915b42"
|
||||
export COMMIT_LIBNEKO="1c47a3af71990a7b2192e03292b4d246c308ef0b"
|
||||
|
||||
@ -2,37 +2,37 @@
|
||||
|
||||
sudo apt-get install fuse -y
|
||||
|
||||
cp -r linux64 nekoray.AppDir
|
||||
cp -r linux64 nekobox.AppDir
|
||||
|
||||
# The file for Appimage
|
||||
|
||||
rm nekoray.AppDir/launcher
|
||||
rm nekobox.AppDir/launcher
|
||||
|
||||
cat >nekoray.AppDir/nekoray.desktop <<-EOF
|
||||
cat >nekobox.AppDir/nekobox.desktop <<-EOF
|
||||
[Desktop Entry]
|
||||
Name=nekoray
|
||||
Exec=echo "NekoRay started"
|
||||
Icon=nekoray
|
||||
Name=nekobox
|
||||
Exec=echo "nekobox started"
|
||||
Icon=nekobox
|
||||
Type=Application
|
||||
Categories=Network
|
||||
EOF
|
||||
|
||||
cat >nekoray.AppDir/AppRun <<-EOF
|
||||
cat >nekobox.AppDir/AppRun <<-EOF
|
||||
#!/bin/bash
|
||||
echo "PATH: \${PATH}"
|
||||
echo "NekoRay runing on: \$APPDIR"
|
||||
LD_LIBRARY_PATH=\${APPDIR}/usr/lib QT_PLUGIN_PATH=\${APPDIR}/usr/plugins \${APPDIR}/nekoray -appdata "\$@"
|
||||
echo "nekobox runing on: \$APPDIR"
|
||||
LD_LIBRARY_PATH=\${APPDIR}/usr/lib QT_PLUGIN_PATH=\${APPDIR}/usr/plugins \${APPDIR}/nekobox -appdata "\$@"
|
||||
EOF
|
||||
|
||||
chmod +x nekoray.AppDir/AppRun
|
||||
chmod +x nekobox.AppDir/AppRun
|
||||
|
||||
# build
|
||||
|
||||
curl -fLSO https://github.com/AppImage/AppImageKit/releases/latest/download/appimagetool-x86_64.AppImage
|
||||
chmod +x appimagetool-x86_64.AppImage
|
||||
./appimagetool-x86_64.AppImage nekoray.AppDir
|
||||
./appimagetool-x86_64.AppImage nekobox.AppDir
|
||||
|
||||
# clean
|
||||
|
||||
rm appimagetool-x86_64.AppImage
|
||||
rm -rf nekoray.AppDir
|
||||
rm -rf nekobox.AppDir
|
||||
|
||||
@ -25,8 +25,8 @@ if [ ! -s /usr/share/applications/nekoray.desktop ]; then
|
||||
[Desktop Entry]
|
||||
Name=nekoray
|
||||
Comment=Qt based cross-platform GUI proxy configuration manager (backend: sing-box)
|
||||
Exec=sh -c "PATH=/opt/nekoray:\$PATH /opt/nekoray/nekoray -appdata"
|
||||
Icon=/opt/nekoray/nekoray.png
|
||||
Exec=sh -c "PATH=/opt/nekoray:\$PATH /opt/nekoray/nekobox -appdata"
|
||||
Icon=/opt/nekoray/nekobox.png
|
||||
Terminal=false
|
||||
Type=Application
|
||||
Categories=Network;Application;
|
||||
|
||||
@ -18,7 +18,7 @@ namespace NekoGui {
|
||||
|
||||
namespace CoreType {
|
||||
enum CoreType {
|
||||
V2RAY,
|
||||
V2RAY, // DO NOT USE
|
||||
SING_BOX,
|
||||
};
|
||||
}
|
||||
|
||||
@ -18,7 +18,7 @@ namespace NekoGui_network {
|
||||
if (NekoGui::dataStore->sub_use_proxy) {
|
||||
QNetworkProxy p;
|
||||
// Note: sing-box mixed socks5 protocol error
|
||||
p.setType(IS_NEKO_BOX ? QNetworkProxy::HttpProxy : QNetworkProxy::Socks5Proxy);
|
||||
p.setType(QNetworkProxy::HttpProxy);
|
||||
p.setHostName("127.0.0.1");
|
||||
p.setPort(NekoGui::dataStore->inbound_socks_port);
|
||||
if (NekoGui::dataStore->inbound_auth->NeedAuth()) {
|
||||
@ -51,7 +51,7 @@ namespace NekoGui_network {
|
||||
for (const auto &err: errors) {
|
||||
error_str << err.errorString();
|
||||
}
|
||||
MW_show_log(QString("SSL Errors: %1 %2").arg(error_str.join(","), NekoGui::dataStore->sub_insecure ? "(Ignored)" : ""));
|
||||
MW_show_log(QStringLiteral("SSL Errors: %1 %2").arg(error_str.join(","), NekoGui::dataStore->sub_insecure ? "(Ignored)" : ""));
|
||||
});
|
||||
// Wait for response
|
||||
auto abortTimer = new QTimer;
|
||||
|
||||
@ -237,7 +237,6 @@ namespace NekoGui {
|
||||
_add(new configItem("current_group", ¤t_group, itemType::integer));
|
||||
_add(new configItem("inbound_address", &inbound_address, itemType::string));
|
||||
_add(new configItem("inbound_socks_port", &inbound_socks_port, itemType::integer));
|
||||
_add(new configItem("inbound_http_port", &inbound_http_port, itemType::integer));
|
||||
_add(new configItem("log_level", &log_level, itemType::string));
|
||||
_add(new configItem("mux_protocol", &mux_protocol, itemType::string));
|
||||
_add(new configItem("mux_concurrency", &mux_concurrency, itemType::integer));
|
||||
@ -248,7 +247,6 @@ namespace NekoGui {
|
||||
_add(new configItem("theme", &theme, itemType::string));
|
||||
_add(new configItem("custom_inbound", &custom_inbound, itemType::string));
|
||||
_add(new configItem("custom_route", &custom_route_global, itemType::string));
|
||||
_add(new configItem("v2ray_asset_dir", &v2ray_asset_dir, itemType::string));
|
||||
_add(new configItem("sub_use_proxy", &sub_use_proxy, itemType::boolean));
|
||||
_add(new configItem("remember_id", &remember_id, itemType::integer));
|
||||
_add(new configItem("remember_enable", &remember_enable, itemType::boolean));
|
||||
@ -284,12 +282,7 @@ namespace NekoGui {
|
||||
_add(new configItem("core_box_clash_api", &core_box_clash_api, itemType::integer));
|
||||
_add(new configItem("core_box_clash_api_secret", &core_box_clash_api_secret, itemType::string));
|
||||
_add(new configItem("core_box_underlying_dns", &core_box_underlying_dns, itemType::string));
|
||||
_add(new configItem("core_ray_direct_dns", &core_ray_direct_dns, itemType::boolean));
|
||||
_add(new configItem("core_ray_freedom_domainStrategy", &core_ray_freedom_domainStrategy, itemType::string));
|
||||
_add(new configItem("vpn_internal_tun", &vpn_internal_tun, itemType::boolean));
|
||||
#ifdef Q_OS_WIN
|
||||
_add(new configItem("core_ray_windows_disable_auto_interface", &core_ray_windows_disable_auto_interface, itemType::boolean));
|
||||
#endif
|
||||
}
|
||||
|
||||
void DataStore::UpdateStartedId(int id) {
|
||||
@ -310,11 +303,7 @@ namespace NekoGui {
|
||||
if (isDefault) {
|
||||
QString version = SubStrBefore(NKR_VERSION, "-");
|
||||
if (!version.contains(".")) version = "2.0";
|
||||
if (IS_NEKO_BOX) {
|
||||
return "NekoBox/PC/" + version + " (Prefer ClashMeta Format)";
|
||||
} else {
|
||||
return "NekoRay/PC/" + version + " (Prefer ClashMeta Format)";
|
||||
}
|
||||
return "NekoBox/PC/" + version + " (Prefer ClashMeta Format)";
|
||||
}
|
||||
return user_agent;
|
||||
}
|
||||
@ -335,10 +324,8 @@ namespace NekoGui {
|
||||
"domain:firebase.io\n"
|
||||
"domain:crashlytics.com\n";
|
||||
}
|
||||
if (IS_NEKO_BOX) {
|
||||
if (!Preset::SingBox::DomainStrategy.contains(domain_strategy)) domain_strategy = "";
|
||||
if (!Preset::SingBox::DomainStrategy.contains(outbound_domain_strategy)) outbound_domain_strategy = "";
|
||||
}
|
||||
if (!Preset::SingBox::DomainStrategy.contains(domain_strategy)) domain_strategy = "";
|
||||
if (!Preset::SingBox::DomainStrategy.contains(outbound_domain_strategy)) outbound_domain_strategy = "";
|
||||
_add(new configItem("direct_ip", &this->direct_ip, itemType::string));
|
||||
_add(new configItem("direct_domain", &this->direct_domain, itemType::string));
|
||||
_add(new configItem("proxy_ip", &this->proxy_ip, itemType::string));
|
||||
@ -362,7 +349,7 @@ namespace NekoGui {
|
||||
}
|
||||
|
||||
QString Routing::DisplayRouting() const {
|
||||
return QString("[Proxy] %1\n[Proxy] %2\n[Direct] %3\n[Direct] %4\n[Block] %5\n[Block] %6\n[Default Outbound] %7\n[DNS] %8")
|
||||
return QStringLiteral("[Proxy] %1\n[Proxy] %2\n[Direct] %3\n[Direct] %4\n[Block] %5\n[Block] %6\n[Default Outbound] %7\n[DNS] %8")
|
||||
.arg(SplitLinesSkipSharp(proxy_domain).join(","), 10)
|
||||
.arg(SplitLinesSkipSharp(proxy_ip).join(","), 10)
|
||||
.arg(SplitLinesSkipSharp(direct_domain).join(","), 10)
|
||||
@ -428,16 +415,13 @@ namespace NekoGui {
|
||||
// System Utils
|
||||
|
||||
QString FindCoreAsset(const QString &name) {
|
||||
QStringList search{NekoGui::dataStore->v2ray_asset_dir};
|
||||
QStringList search{};
|
||||
search << QApplication::applicationDirPath();
|
||||
search << "/usr/share/sing-geoip";
|
||||
search << "/usr/share/sing-geosite";
|
||||
search << "/usr/share/xray";
|
||||
search << "/usr/local/share/xray";
|
||||
search << "/opt/xray";
|
||||
search << "/usr/share/v2ray";
|
||||
search << "/usr/local/share/v2ray";
|
||||
search << "/opt/v2ray";
|
||||
search << "/usr/share/sing-box";
|
||||
search << "/usr/lib/nekobox";
|
||||
search << "/usr/share/nekobox";
|
||||
for (const auto &dir: search) {
|
||||
if (dir.isEmpty()) continue;
|
||||
QFileInfo asset(dir + "/" + name);
|
||||
|
||||
@ -17,7 +17,5 @@ namespace NekoGui {
|
||||
bool IsAdmin();
|
||||
} // namespace NekoGui
|
||||
|
||||
#define IS_NEKO_BOX (NekoGui::coreType == NekoGui::CoreType::SING_BOX)
|
||||
#define IS_NEKO_BOX_INTERNAL_TUN (IS_NEKO_BOX && NekoGui::dataStore->vpn_internal_tun)
|
||||
#define ROUTES_PREFIX_NAME QString(IS_NEKO_BOX ? "routes_box" : "routes")
|
||||
#define ROUTES_PREFIX_NAME QStringLiteral("routes_box")
|
||||
#define ROUTES_PREFIX QString(ROUTES_PREFIX_NAME + "/")
|
||||
|
||||
@ -93,7 +93,7 @@ namespace NekoGui {
|
||||
// Saved
|
||||
|
||||
// Misc
|
||||
QString log_level = "warning";
|
||||
QString log_level = "info";
|
||||
QString test_latency_url = "http://cp.cloudflare.com/";
|
||||
QString test_download_url = "http://cachefly.cachefly.net/10mb.test";
|
||||
int test_download_timeout = 30;
|
||||
@ -102,12 +102,11 @@ namespace NekoGui {
|
||||
int traffic_loop_interval = 1000;
|
||||
bool connection_statistics = false;
|
||||
int current_group = 0; // group id
|
||||
QString mux_protocol = "";
|
||||
QString mux_protocol = "h2mux";
|
||||
bool mux_padding = false;
|
||||
int mux_concurrency = 8;
|
||||
bool mux_default_on = false;
|
||||
QString theme = "0";
|
||||
QString v2ray_asset_dir = "";
|
||||
int language = 0;
|
||||
QString mw_size = "";
|
||||
bool check_include_pre = false;
|
||||
@ -136,7 +135,6 @@ namespace NekoGui {
|
||||
// Socks & HTTP Inbound
|
||||
QString inbound_address = "127.0.0.1";
|
||||
int inbound_socks_port = 2080; // or Mixed
|
||||
int inbound_http_port = 2081;
|
||||
InboundAuthorization *inbound_auth = new InboundAuthorization;
|
||||
QString custom_inbound = "{\"inbounds\": []}";
|
||||
|
||||
@ -166,9 +164,6 @@ namespace NekoGui {
|
||||
int core_box_clash_api = -9090;
|
||||
QString core_box_clash_api_secret = "";
|
||||
QString core_box_underlying_dns = "";
|
||||
bool core_ray_direct_dns = false;
|
||||
bool core_ray_windows_disable_auto_interface = false;
|
||||
QString core_ray_freedom_domainStrategy = "";
|
||||
|
||||
// Other Core
|
||||
ExtraCore *extraCore = new ExtraCore;
|
||||
|
||||
@ -8,8 +8,8 @@
|
||||
|
||||
//
|
||||
|
||||
inline QString software_name = "NekoRay";
|
||||
inline QString software_core_name = "Xray";
|
||||
inline QString software_name = "NekoBox";
|
||||
inline QString software_core_name = "sing-box";
|
||||
|
||||
// Main Functions
|
||||
|
||||
|
||||
@ -140,7 +140,7 @@ int main(int argc, char* argv[]) {
|
||||
return 0;
|
||||
}
|
||||
// Some Bad System
|
||||
QMessageBox::warning(nullptr, "NekoRay", "RunGuard disallow to run, use -many to force start.");
|
||||
QMessageBox::warning(nullptr, "NekoGui", "RunGuard disallow to run, use -many to force start.");
|
||||
return 0;
|
||||
}
|
||||
MF_release_runguard = [&] { guard.release(); };
|
||||
@ -148,7 +148,7 @@ int main(int argc, char* argv[]) {
|
||||
// icons
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0))
|
||||
QIcon::setFallbackSearchPaths(QStringList{
|
||||
":/nekoray",
|
||||
":/neko",
|
||||
":/icon",
|
||||
});
|
||||
#endif
|
||||
@ -177,9 +177,6 @@ int main(int argc, char* argv[]) {
|
||||
|
||||
// Load dataStore
|
||||
switch (NekoGui::coreType) {
|
||||
case NekoGui::CoreType::V2RAY:
|
||||
NekoGui::dataStore->fn = "groups/nekoray.json";
|
||||
break;
|
||||
case NekoGui::CoreType::SING_BOX:
|
||||
NekoGui::dataStore->fn = "groups/nekobox.json";
|
||||
break;
|
||||
|
||||
@ -1 +1 @@
|
||||
4.0-beta1-2024-07-02
|
||||
4.0.1-2024-12-12
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
|
||||
<body>
|
||||
<h3>
|
||||
<p>Please put your clash dashboard files to "nekoray/config/dashboard" dir.</p>
|
||||
<p>Please put your clash dashboard files to "./config/dashboard" dir.</p>
|
||||
<p>For example, you can download from the following URL.</p>
|
||||
<p>
|
||||
<a href="https://github.com/MetaCubeX/Yacd-meta/archive/refs/heads/gh-pages.zip">Download Yacd-meta</a>
|
||||
|
||||
@ -16,7 +16,6 @@
|
||||
</qresource>
|
||||
<qresource prefix="/neko">
|
||||
<file alias="nekobox.png">public/nekobox.png</file>
|
||||
<file alias="nekoray.png">public/nekoray.png</file>
|
||||
<file>neko.css</file>
|
||||
<file>vpn/vpn-run-root.sh</file>
|
||||
<file>vpn/sing-box-vpn.json</file>
|
||||
|
||||
|
Before Width: | Height: | Size: 66 KiB After Width: | Height: | Size: 66 KiB |
BIN
res/nekoray.icns
BIN
res/nekoray.icns
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 60 KiB |
@ -12,7 +12,7 @@
|
||||
{
|
||||
"tag": "dns-remote",
|
||||
"address": "8.8.8.8",
|
||||
"detour": "nekoray-socks"
|
||||
"detour": "neko-socks"
|
||||
},
|
||||
{
|
||||
"tag": "dns-direct",
|
||||
@ -75,7 +75,7 @@
|
||||
"outbounds": [
|
||||
{
|
||||
"type": "socks",
|
||||
"tag": "nekoray-socks",
|
||||
"tag": "neko-socks",
|
||||
"udp_fragment": true,
|
||||
//%SOCKS_USER_PASS%
|
||||
"server": "127.0.0.1",
|
||||
|
||||
@ -6,40 +6,25 @@ if [ "$EUID" -ne 0 ]; then
|
||||
echo "[Warning] Tun script not running as root"
|
||||
fi
|
||||
|
||||
if [ "$(uname)" == "Darwin" ]; then
|
||||
IS_MACOS=1
|
||||
fi
|
||||
|
||||
[ -z $TABLE_FWMARK ] && echo "Please set env TABLE_FWMARK" && exit
|
||||
command -v pkill >/dev/null 2>&1 || echo "[Warning] pkill not found"
|
||||
|
||||
BASEDIR=$(dirname "$0")
|
||||
cd $BASEDIR
|
||||
|
||||
pre_start_linux() {
|
||||
# set bypass: fwmark
|
||||
ip rule add pref 8999 fwmark $TABLE_FWMARK table main || return
|
||||
ip -6 rule add pref 8999 fwmark $TABLE_FWMARK table main || return
|
||||
|
||||
# for Tun2Socket
|
||||
iptables -I INPUT -s 172.19.0.2 -d 172.19.0.1 -p tcp -j ACCEPT
|
||||
ip6tables -I INPUT -s fdfe:dcba:9876::2 -d fdfe:dcba:9876::1 -p tcp -j ACCEPT
|
||||
}
|
||||
|
||||
start() {
|
||||
[ -z $IS_MACOS ] && pre_start_linux
|
||||
"./nekobox_core" run -c "$CONFIG_PATH" --protect-listen-path "$PROTECT_LISTEN_PATH" --protect-fwmark $TABLE_FWMARK
|
||||
pre_start_linux
|
||||
"./nekobox_core" run -c "$CONFIG_PATH"
|
||||
}
|
||||
|
||||
stop() {
|
||||
[ -z $IS_MACOS ] || return
|
||||
for local in $BYPASS_IPS; do
|
||||
ip rule del to $local table main
|
||||
done
|
||||
iptables -D INPUT -s 172.19.0.2 -d 172.19.0.1 -p tcp -j ACCEPT
|
||||
ip6tables -D INPUT -s fdfe:dcba:9876::2 -d fdfe:dcba:9876::1 -p tcp -j ACCEPT
|
||||
ip rule del fwmark $TABLE_FWMARK
|
||||
ip -6 rule del fwmark $TABLE_FWMARK
|
||||
}
|
||||
|
||||
if [ "$1" != "stop" ]; then
|
||||
|
||||
@ -205,7 +205,7 @@ namespace NekoGui_rpc {
|
||||
|
||||
#define NOT_OK \
|
||||
*rpcOK = false; \
|
||||
onError(QString("QNetworkReply::NetworkError code: %1\n").arg(status));
|
||||
onError(QStringLiteral("QNetworkReply::NetworkError code: %1\n").arg(status));
|
||||
|
||||
void Client::Exit() {
|
||||
libcore::EmptyReq request;
|
||||
|
||||
@ -122,14 +122,6 @@ namespace NekoGui_sub {
|
||||
if (!ok) return;
|
||||
}
|
||||
|
||||
// Hysteria1
|
||||
if (str.startsWith("hysteria://")) {
|
||||
needFix = false;
|
||||
ent = NekoGui::ProfileManager::NewProxyEntity("hysteria");
|
||||
auto ok = ent->QUICBean()->TryParseLink(str);
|
||||
if (!ok) return;
|
||||
}
|
||||
|
||||
// Hysteria2
|
||||
if (str.startsWith("hysteria2://") || str.startsWith("hy2://")) {
|
||||
needFix = false;
|
||||
@ -200,7 +192,7 @@ namespace NekoGui_sub {
|
||||
try {
|
||||
return n.as<int>();
|
||||
} catch (const YAML::Exception &ex2) {
|
||||
ex2.what();
|
||||
qDebug() << ex2.what();
|
||||
}
|
||||
qDebug() << ex.what();
|
||||
return def;
|
||||
@ -413,37 +405,6 @@ namespace NekoGui_sub {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (type == "hysteria") {
|
||||
auto bean = ent->QUICBean();
|
||||
|
||||
bean->hopPort = Node2QString(proxy["ports"]);
|
||||
|
||||
bean->allowInsecure = Node2Bool(proxy["skip-cert-verify"]);
|
||||
auto alpn = Node2QStringList(proxy["alpn"]);
|
||||
bean->caText = Node2QString(proxy["ca-str"]);
|
||||
if (!alpn.isEmpty()) bean->alpn = alpn[0];
|
||||
bean->sni = Node2QString(proxy["sni"]);
|
||||
|
||||
auto auth_str = FIRST_OR_SECOND(Node2QString(proxy["auth_str"]), Node2QString(proxy["auth-str"]));
|
||||
auto auth = Node2QString(proxy["auth"]);
|
||||
if (!auth_str.isEmpty()) {
|
||||
bean->authPayloadType = NekoGui_fmt::QUICBean::hysteria_auth_string;
|
||||
bean->authPayload = auth_str;
|
||||
}
|
||||
if (!auth.isEmpty()) {
|
||||
bean->authPayloadType = NekoGui_fmt::QUICBean::hysteria_auth_base64;
|
||||
bean->authPayload = auth;
|
||||
}
|
||||
bean->obfsPassword = Node2QString(proxy["obfs"]);
|
||||
|
||||
if (Node2Bool(proxy["disable_mtu_discovery"]) || Node2Bool(proxy["disable-mtu-discovery"])) bean->disableMtuDiscovery = true;
|
||||
bean->streamReceiveWindow = Node2Int(proxy["recv-window"]);
|
||||
bean->connectionReceiveWindow = Node2Int(proxy["recv-window-conn"]);
|
||||
|
||||
auto upMbps = Node2QString(proxy["up"]).split(" ")[0].toInt();
|
||||
auto downMbps = Node2QString(proxy["down"]).split(" ")[0].toInt();
|
||||
if (upMbps > 0) bean->uploadMbps = upMbps;
|
||||
if (downMbps > 0) bean->downloadMbps = downMbps;
|
||||
} else if (type == "hysteria2") {
|
||||
auto bean = ent->QUICBean();
|
||||
|
||||
|
||||
@ -141,15 +141,7 @@ namespace NekoGui_sys {
|
||||
|
||||
void CoreProcess::Start() {
|
||||
show_stderr = false;
|
||||
// set extra env
|
||||
auto v2ray_asset_dir = NekoGui::FindCoreAsset("geoip.dat");
|
||||
if (!v2ray_asset_dir.isEmpty()) {
|
||||
v2ray_asset_dir = QFileInfo(v2ray_asset_dir).absolutePath();
|
||||
env << "XRAY_LOCATION_ASSET=" + v2ray_asset_dir;
|
||||
}
|
||||
if (NekoGui::dataStore->core_ray_direct_dns) env << "NKR_CORE_RAY_DIRECT_DNS=1";
|
||||
if (NekoGui::dataStore->core_ray_windows_disable_auto_interface) env << "NKR_CORE_RAY_WINDOWS_DISABLE_AUTO_INTERFACE=1";
|
||||
//
|
||||
// cwd: same as GUI, at ./config
|
||||
ExternalProcess::Start();
|
||||
write((NekoGui::dataStore->core_token + "\n").toUtf8());
|
||||
}
|
||||
|
||||
@ -56,7 +56,7 @@ LONG __stdcall CreateCrashHandler(EXCEPTION_POINTERS *pException) {
|
||||
}
|
||||
// 创建消息提示
|
||||
QMessageBox::warning(NULL, "Application crashed",
|
||||
QString("ErrorCode: %1 ErrorAddr:%2 ErrorFlag: %3 ErrorPara: %4\nVersion: %5\nDump file at %6")
|
||||
QStringLiteral("ErrorCode: %1 ErrorAddr:%2 ErrorFlag: %3 ErrorPara: %4\nVersion: %5\nDump file at %6")
|
||||
.arg(errCode)
|
||||
.arg(errAddr)
|
||||
.arg(errFlag)
|
||||
|
||||
@ -11,18 +11,10 @@
|
||||
<source>Enable</source>
|
||||
<translation>فعال کردن</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>HTTP Listen Port</source>
|
||||
<translation>پورت HTTP درحال شنود</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Listen Address</source>
|
||||
<translation>آدرس درحال شنود</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Socks Listen Port</source>
|
||||
<translation>پورت ساکس درحال شنود</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>concurrency</source>
|
||||
<translation>همزمانی</translation>
|
||||
@ -71,14 +63,6 @@
|
||||
<source>Custom Inbound</source>
|
||||
<translation>ورودی سفارشی</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Asset Location</source>
|
||||
<translation>مکان دارایی</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Default: dir of "nekoray"</source>
|
||||
<translation type="unfinished">مسیر پیش فرض "nekoray"</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Concurrent</source>
|
||||
<translation>هم زمان</translation>
|
||||
@ -197,16 +181,6 @@
|
||||
<source>Override underlying DNS</source>
|
||||
<translation type="unfinished">لغو دی ان اس زیربنایی</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>It is recommended to leave it blank, but it sometimes does not work, at this time you can set this option.
|
||||
For NekoRay, this rewrites the underlying(localhost) DNS in Tun Mode.
|
||||
For NekoBox, this rewrites the underlying(localhost) DNS in Tun Mode, normal mode, and also URL Test.</source>
|
||||
<translation type="unfinished">پیشنهاد میشود که این گزینه را انتخاب نشده باقی بگذارید ، اما گاهی اوقات کار نمیکند در این مواقع شما میتوانید این گزینه را انتخاب کنید ، برای Nekoray این گزینه دی ان اس زیربنایی را در حالت تونل بازنویسی میکند ، و برای NekoBox این گزینه دی ان اس زیربنایی را هم در حالت تونل و هم در حالت معمولی و تست آدرس بازنویسی میکند</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>If you Tun Mode is not working, try to change this option.</source>
|
||||
<translation type="unfinished">اگر حالت تونل برای شما کار نمیکند ، این گزینه را تغییر دهید</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Default On</source>
|
||||
<translation type="unfinished">به صورت پیشفرض فعال</translation>
|
||||
@ -243,6 +217,10 @@ For NekoBox, this rewrites the underlying(localhost) DNS in Tun Mode, normal mod
|
||||
<source>Old Share Link Format</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Mixed (SOCKS+HTTP) Listen Port</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>DialogEditGroup</name>
|
||||
@ -871,14 +849,6 @@ This needs to be run NekoBox with administrator privileges.</source>
|
||||
<source>Certificate</source>
|
||||
<translation type="unfinished">گواهی</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Auth Type</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Protocol</source>
|
||||
<translation type="unfinished">پروتکل</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Download (Mbps)</source>
|
||||
<translation type="unfinished">دانلود (مگابیت بر ثانیه)</translation>
|
||||
@ -911,10 +881,6 @@ This needs to be run NekoBox with administrator privileges.</source>
|
||||
<source>SNI</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Auth Payload</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Disable SNI</source>
|
||||
<translation type="unfinished"></translation>
|
||||
@ -1368,14 +1334,6 @@ This needs to be run NekoBox with administrator privileges.</source>
|
||||
<source>Imported %1 profile(s)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Http inbound is not enabled, can't set system proxy.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Settings</source>
|
||||
<translation>تنظیمات</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Current server is incompatible with Tun. Please stop the server first, enable Tun Mode, and then restart.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
@ -1384,11 +1342,6 @@ This needs to be run NekoBox with administrator privileges.</source>
|
||||
<source>Not Running</source>
|
||||
<translation>در حال اجرا نیست</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>None</source>
|
||||
<translatorcomment>هیچ یک</translatorcomment>
|
||||
<translation>هیچ یک</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Select</source>
|
||||
<translation>انتخاب</translation>
|
||||
@ -1539,6 +1492,10 @@ End: %2</source>
|
||||
<source>Stop Testing</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>URL Test</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ProxyItem</name>
|
||||
@ -1679,6 +1636,10 @@ Direct: %2</source>
|
||||
<source>Default</source>
|
||||
<translation type="unfinished">پیش فرض</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>The last speed test did not exit completely, please wait. If it persists, please restart the program.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>Qv2ray::ui::widgets::AutoCompleteTextEdit</name>
|
||||
|
||||
@ -23,14 +23,6 @@
|
||||
<source>Edit</source>
|
||||
<translation>Изменить</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Socks Listen Port</source>
|
||||
<translation>Адрес входящих SOCKS</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>HTTP Listen Port</source>
|
||||
<translation>Адрес входящих HTTP</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Enable</source>
|
||||
<translation>Вкл</translation>
|
||||
@ -115,14 +107,6 @@
|
||||
<source>Core</source>
|
||||
<translation>Ядро</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Asset Location</source>
|
||||
<translation>Расположение файлов</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Default: dir of "nekoray"</source>
|
||||
<translation>По умолчанию: текущая папка nekoray</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Select</source>
|
||||
<translation>Выбрать</translation>
|
||||
@ -211,18 +195,6 @@
|
||||
<source>Override underlying DNS</source>
|
||||
<translation>Переопределить нижестоящий DNS</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>It is recommended to leave it blank, but it sometimes does not work, at this time you can set this option.
|
||||
For NekoRay, this rewrites the underlying(localhost) DNS in Tun Mode.
|
||||
For NekoBox, this rewrites the underlying(localhost) DNS in Tun Mode, normal mode, and also URL Test.</source>
|
||||
<translation>Рекомендуется оставить параметр пустым, но иногда это не срабатывает как надо, и в таком случае можно использовать эту опцию.
|
||||
Для NekoRay это переопределяет нижестоящий (localhost) DNS в Tun режиме.
|
||||
Для NekoBox это переопределяет нижестоящий (localhost) DNS в Tun режиме, нормальном режиме, а также при URL тесте.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>If you Tun Mode is not working, try to change this option.</source>
|
||||
<translation>Если TUN-режим не работает, попробуйте изменить эту опцию.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Timeout (s)</source>
|
||||
<translation>Таймаут (с)</translation>
|
||||
@ -243,6 +215,10 @@ For NekoBox, this rewrites the underlying(localhost) DNS in Tun Mode, normal mod
|
||||
<source>Old Share Link Format</source>
|
||||
<translation>Поделиться ссылкой в старом формате</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Mixed (SOCKS+HTTP) Listen Port</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>DialogEditGroup</name>
|
||||
@ -872,14 +848,6 @@ https://matsuridayo.github.io/n-configuration/#vpn-tun</translation>
|
||||
</context>
|
||||
<context>
|
||||
<name>EditQUIC</name>
|
||||
<message>
|
||||
<source>Auth Type</source>
|
||||
<translation>Тип авторизации</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Protocol</source>
|
||||
<translation>Протокол</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Download (Mbps)</source>
|
||||
<translation>Скорость приема (Mbps)</translation>
|
||||
@ -916,10 +884,6 @@ https://matsuridayo.github.io/n-configuration/#vpn-tun</translation>
|
||||
<source>SNI</source>
|
||||
<translation>SNI</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Auth Payload</source>
|
||||
<translation>Полезная нагрузка в режиме авторизации</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Disable SNI</source>
|
||||
<translation>Отключить SNI</translation>
|
||||
@ -1394,14 +1358,6 @@ https://matsuridayo.github.io/n-configuration/#vpn-tun</translation>
|
||||
<source>Imported %1 profile(s)</source>
|
||||
<translation>Импортирован(ы) %1 профиль(ей)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Http inbound is not enabled, can't set system proxy.</source>
|
||||
<translation>HTTP inbound не включен в настройках, невозможно установить системный прокси.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Settings</source>
|
||||
<translation>Настройки</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Please run NekoBox as admin</source>
|
||||
<translation>Пожалуйста, запустите NekoBox с правами администратора</translation>
|
||||
@ -1414,10 +1370,6 @@ https://matsuridayo.github.io/n-configuration/#vpn-tun</translation>
|
||||
<source>Not Running</source>
|
||||
<translation>Не запущен</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>None</source>
|
||||
<translation>Нет</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Select</source>
|
||||
<translation>Выбор</translation>
|
||||
@ -1546,6 +1498,10 @@ End: %2</source>
|
||||
<source>Stop Testing</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>URL Test</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ProxyItem</name>
|
||||
@ -1693,6 +1649,10 @@ Release note:
|
||||
<source>Default</source>
|
||||
<translation>По умолчанию</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>The last speed test did not exit completely, please wait. If it persists, please restart the program.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>Qv2ray::ui::widgets::AutoCompleteTextEdit</name>
|
||||
|
||||
@ -11,18 +11,10 @@
|
||||
<source>Enable</source>
|
||||
<translation>启用</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>HTTP Listen Port</source>
|
||||
<translation>HTTP 监听端口</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Listen Address</source>
|
||||
<translation>监听地址</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Socks Listen Port</source>
|
||||
<translation>Socks 监听端口</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>concurrency</source>
|
||||
<translation>并发</translation>
|
||||
@ -71,14 +63,6 @@
|
||||
<source>Custom Inbound</source>
|
||||
<translation>自定义入站</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Asset Location</source>
|
||||
<translation>资源文件路径</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Default: dir of "nekoray"</source>
|
||||
<translation>默认值:和 nekoray 同路径</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Concurrent</source>
|
||||
<translation>并发</translation>
|
||||
@ -203,18 +187,6 @@
|
||||
<source>Override underlying DNS</source>
|
||||
<translation>覆盖底层 DNS</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>It is recommended to leave it blank, but it sometimes does not work, at this time you can set this option.
|
||||
For NekoRay, this rewrites the underlying(localhost) DNS in Tun Mode.
|
||||
For NekoBox, this rewrites the underlying(localhost) DNS in Tun Mode, normal mode, and also URL Test.</source>
|
||||
<translation>建议留空,但有时会出现问题,这时可以设置这个选项。
|
||||
对于 NekoRay 来说,在 Tun 模式下会重写 underlying(localhost) DNS。
|
||||
对于 NekoBox 来说,在 Tun 模式、正常模式和 URL 测试中会重写 underlying(localhost) DNS。</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>If you Tun Mode is not working, try to change this option.</source>
|
||||
<translation>如果您的 Tun 模式有问题,请尝试更改此选项。</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Default On</source>
|
||||
<translation>默认开启</translation>
|
||||
@ -243,6 +215,10 @@ For NekoBox, this rewrites the underlying(localhost) DNS in Tun Mode, normal mod
|
||||
<source>Interval (minute, invalid if less than 30)</source>
|
||||
<translation>时间间隔(分钟,少于 30 分钟无效)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Mixed (SOCKS+HTTP) Listen Port</source>
|
||||
<translation>Mixed (SOCKS+HTTP) 监听端口</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>DialogEditGroup</name>
|
||||
@ -877,14 +853,6 @@ This needs to be run NekoBox with administrator privileges.</source>
|
||||
<source>Certificate</source>
|
||||
<translation>证书</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Auth Type</source>
|
||||
<translation>认证类型</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Protocol</source>
|
||||
<translation>协议</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Download (Mbps)</source>
|
||||
<translation>下载速度 (Mbps)</translation>
|
||||
@ -917,10 +885,6 @@ This needs to be run NekoBox with administrator privileges.</source>
|
||||
<source>SNI</source>
|
||||
<translation>SNI</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Auth Payload</source>
|
||||
<translation>认证有效载荷</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Generate UUID</source>
|
||||
<translation>生成 UUID</translation>
|
||||
@ -1210,10 +1174,6 @@ This needs to be run NekoBox with administrator privileges.</source>
|
||||
<source>Imported %1 profile(s)</source>
|
||||
<translation>导入了 %1 个配置</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>None</source>
|
||||
<translation>无</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Unavailable</source>
|
||||
<translation>不可用</translation>
|
||||
@ -1242,10 +1202,6 @@ This needs to be run NekoBox with administrator privileges.</source>
|
||||
<source>Testing</source>
|
||||
<translation>正在测试</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Http inbound is not enabled, can't set system proxy.</source>
|
||||
<translation>HTTP 入站未启用,无法设置系统代理。</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Update</source>
|
||||
<translation>更新</translation>
|
||||
@ -1316,10 +1272,6 @@ End: %2</source>
|
||||
<source>Remove Unavailable</source>
|
||||
<translation>删除不可用的配置</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Settings</source>
|
||||
<translation>设置</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>New profile</source>
|
||||
<translation>手动输入配置</translation>
|
||||
@ -1536,7 +1488,7 @@ Split by line.</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>In and Out IP</source>
|
||||
<translation>入口出口IP</translation>
|
||||
<translation>入口出口 IP</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Test Options</source>
|
||||
@ -1546,6 +1498,10 @@ Split by line.</source>
|
||||
<source>Stop Testing</source>
|
||||
<translation>停止测试</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>URL Test</source>
|
||||
<translation>URL 测试</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ProxyItem</name>
|
||||
@ -1693,6 +1649,10 @@ Release note:
|
||||
<source>Default</source>
|
||||
<translation>默认</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>The last speed test did not exit completely, please wait. If it persists, please restart the program.</source>
|
||||
<translation>上次速度测试未完全退出,请等待。如果问题仍然存在,请重新启动程序。</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>Qv2ray::ui::widgets::AutoCompleteTextEdit</name>
|
||||
|
||||
@ -59,16 +59,8 @@ DialogBasicSettings::DialogBasicSettings(QWidget *parent)
|
||||
|
||||
// Common
|
||||
|
||||
if (IS_NEKO_BOX) {
|
||||
ui->groupBox_http->hide();
|
||||
ui->inbound_socks_port_l->setText(ui->inbound_socks_port_l->text().replace("Socks", "Mixed (SOCKS+HTTP)"));
|
||||
ui->log_level->addItems(QString("trace debug info warn error fatal panic").split(" "));
|
||||
ui->mux_protocol->addItems({"h2mux", "smux", "yamux"});
|
||||
} else {
|
||||
ui->log_level->addItems({"debug", "info", "warning", "none"});
|
||||
ui->mux_protocol->hide();
|
||||
ui->mux_padding->hide();
|
||||
}
|
||||
ui->log_level->addItems(QStringLiteral("trace debug info warn error fatal panic").split(" "));
|
||||
ui->mux_protocol->addItems({"h2mux", "smux", "yamux"});
|
||||
|
||||
refresh_auth();
|
||||
|
||||
@ -76,7 +68,6 @@ DialogBasicSettings::DialogBasicSettings(QWidget *parent)
|
||||
D_LOAD_COMBO_STRING(log_level)
|
||||
CACHE.custom_inbound = NekoGui::dataStore->custom_inbound;
|
||||
D_LOAD_INT(inbound_socks_port)
|
||||
D_LOAD_INT_ENABLE(inbound_http_port, http_enable)
|
||||
D_LOAD_INT(test_concurrent)
|
||||
D_LOAD_INT(test_download_timeout)
|
||||
D_LOAD_STRING(test_latency_url)
|
||||
@ -102,9 +93,7 @@ DialogBasicSettings::DialogBasicSettings(QWidget *parent)
|
||||
#endif
|
||||
|
||||
// Style
|
||||
if (IS_NEKO_BOX) {
|
||||
ui->connection_statistics_box->setDisabled(true);
|
||||
}
|
||||
ui->connection_statistics_box->setDisabled(true);
|
||||
//
|
||||
D_LOAD_BOOL(check_include_pre)
|
||||
D_LOAD_BOOL(connection_statistics)
|
||||
@ -166,11 +155,9 @@ DialogBasicSettings::DialogBasicSettings(QWidget *parent)
|
||||
// Core
|
||||
|
||||
ui->groupBox_core->setTitle(software_core_name);
|
||||
ui->core_v2ray_asset->setText(NekoGui::dataStore->v2ray_asset_dir);
|
||||
//
|
||||
CACHE.extraCore = QString2QJsonObject(NekoGui::dataStore->extraCore->core_map);
|
||||
if (!CACHE.extraCore.contains("naive")) CACHE.extraCore.insert("naive", "");
|
||||
if (!CACHE.extraCore.contains("hysteria")) CACHE.extraCore.insert("hysteria", "");
|
||||
if (!CACHE.extraCore.contains("hysteria2")) CACHE.extraCore.insert("hysteria2", "");
|
||||
if (!CACHE.extraCore.contains("tuic")) CACHE.extraCore.insert("tuic", "");
|
||||
//
|
||||
@ -179,16 +166,6 @@ DialogBasicSettings::DialogBasicSettings(QWidget *parent)
|
||||
extra_core_layout->addWidget(new ExtraCoreWidget(&CACHE.extraCore, s));
|
||||
}
|
||||
//
|
||||
connect(ui->core_v2ray_asset, &QLineEdit::textChanged, this, [=] {
|
||||
CACHE.needRestart = true;
|
||||
});
|
||||
connect(ui->core_v2ray_asset_pick, &QPushButton::clicked, this, [=] {
|
||||
auto fn = QFileDialog::getExistingDirectory(this, tr("Select"), QDir::currentPath(),
|
||||
QFileDialog::Option::ShowDirsOnly | QFileDialog::Option::ReadOnly);
|
||||
if (!fn.isEmpty()) {
|
||||
ui->core_v2ray_asset->setText(fn);
|
||||
}
|
||||
});
|
||||
connect(ui->extra_core_add, &QPushButton::clicked, this, [=] {
|
||||
bool ok;
|
||||
auto s = QInputDialog::getText(nullptr, tr("Add"),
|
||||
@ -225,7 +202,7 @@ DialogBasicSettings::DialogBasicSettings(QWidget *parent)
|
||||
|
||||
// Security
|
||||
|
||||
ui->utlsFingerprint->addItems(IS_NEKO_BOX ? Preset::SingBox::UtlsFingerPrint : Preset::Xray::UtlsFingerPrint);
|
||||
ui->utlsFingerprint->addItems(Preset::SingBox::UtlsFingerPrint);
|
||||
|
||||
D_LOAD_BOOL(skip_cert)
|
||||
ui->utlsFingerprint->setCurrentText(NekoGui::dataStore->utlsFingerprint);
|
||||
@ -242,7 +219,6 @@ void DialogBasicSettings::accept() {
|
||||
D_SAVE_COMBO_STRING(log_level)
|
||||
NekoGui::dataStore->custom_inbound = CACHE.custom_inbound;
|
||||
D_SAVE_INT(inbound_socks_port)
|
||||
D_SAVE_INT_ENABLE(inbound_http_port, http_enable)
|
||||
D_SAVE_INT(test_concurrent)
|
||||
D_SAVE_INT(test_download_timeout)
|
||||
D_SAVE_STRING(test_latency_url)
|
||||
@ -291,7 +267,6 @@ void DialogBasicSettings::accept() {
|
||||
|
||||
// Core
|
||||
|
||||
NekoGui::dataStore->v2ray_asset_dir = ui->core_v2ray_asset->text();
|
||||
NekoGui::dataStore->extraCore->core_map = QJsonObject2QString(CACHE.extraCore, true);
|
||||
|
||||
// Mux
|
||||
@ -306,7 +281,7 @@ void DialogBasicSettings::accept() {
|
||||
NekoGui::dataStore->utlsFingerprint = ui->utlsFingerprint->currentText();
|
||||
|
||||
// 关闭连接统计,停止刷新前清空记录。
|
||||
if (NekoGui::dataStore->traffic_loop_interval == 0 || NekoGui::dataStore->connection_statistics == false) {
|
||||
if (NekoGui::dataStore->traffic_loop_interval == 0 || !NekoGui::dataStore->connection_statistics) {
|
||||
MW_dialog_message("", "ClearConnectionList");
|
||||
}
|
||||
|
||||
@ -395,80 +370,39 @@ void DialogBasicSettings::on_core_settings_clicked() {
|
||||
MyLineEdit *core_box_clash_api;
|
||||
MyLineEdit *core_box_clash_api_secret;
|
||||
MyLineEdit *core_box_underlying_dns;
|
||||
QCheckBox *core_ray_direct_dns;
|
||||
QCheckBox *core_ray_windows_disable_auto_interface;
|
||||
QComboBox *core_ray_freedom_domainStrategy;
|
||||
//
|
||||
auto core_box_underlying_dns_l = new QLabel(tr("Override underlying DNS"));
|
||||
core_box_underlying_dns_l->setToolTip(tr(
|
||||
"It is recommended to leave it blank, but it sometimes does not work, at this time you can set this option.\n"
|
||||
"For NekoRay, this rewrites the underlying(localhost) DNS in Tun Mode.\n"
|
||||
"For NekoBox, this rewrites the underlying(localhost) DNS in Tun Mode, normal mode, and also URL Test."));
|
||||
core_box_underlying_dns = new MyLineEdit;
|
||||
core_box_underlying_dns->setText(NekoGui::dataStore->core_box_underlying_dns);
|
||||
core_box_underlying_dns->setMinimumWidth(300);
|
||||
layout->addWidget(core_box_underlying_dns_l, ++line, 0);
|
||||
layout->addWidget(core_box_underlying_dns, line, 1);
|
||||
//
|
||||
if (IS_NEKO_BOX) {
|
||||
auto core_box_enable_clash_api_l = new QLabel("Enable Clash API");
|
||||
core_box_enable_clash_api = new QCheckBox;
|
||||
core_box_enable_clash_api->setChecked(NekoGui::dataStore->core_box_clash_api > 0);
|
||||
layout->addWidget(core_box_enable_clash_api_l, ++line, 0);
|
||||
layout->addWidget(core_box_enable_clash_api, line, 1);
|
||||
//
|
||||
auto core_box_clash_api_l = new QLabel("Clash API Listen Port");
|
||||
core_box_clash_api = new MyLineEdit;
|
||||
core_box_clash_api->setText(Int2String(std::abs(NekoGui::dataStore->core_box_clash_api)));
|
||||
layout->addWidget(core_box_clash_api_l, ++line, 0);
|
||||
layout->addWidget(core_box_clash_api, line, 1);
|
||||
//
|
||||
auto core_box_clash_api_secret_l = new QLabel("Clash API Secret");
|
||||
core_box_clash_api_secret = new MyLineEdit;
|
||||
core_box_clash_api_secret->setText(NekoGui::dataStore->core_box_clash_api_secret);
|
||||
layout->addWidget(core_box_clash_api_secret_l, ++line, 0);
|
||||
layout->addWidget(core_box_clash_api_secret, line, 1);
|
||||
} else {
|
||||
auto core_ray_direct_dns_l = new QLabel("NKR_CORE_RAY_DIRECT_DNS");
|
||||
core_ray_direct_dns_l->setToolTip(tr("If you Tun Mode is not working, try to change this option."));
|
||||
core_ray_direct_dns = new QCheckBox;
|
||||
core_ray_direct_dns->setChecked(NekoGui::dataStore->core_ray_direct_dns);
|
||||
connect(core_ray_direct_dns, &QCheckBox::clicked, this, [&] { CACHE.needRestart = true; });
|
||||
layout->addWidget(core_ray_direct_dns_l, ++line, 0);
|
||||
layout->addWidget(core_ray_direct_dns, line, 1);
|
||||
//
|
||||
auto core_ray_freedom_domainStrategy_l = new QLabel("Freedom Strategy");
|
||||
core_ray_freedom_domainStrategy = new QComboBox;
|
||||
core_ray_freedom_domainStrategy->addItems({"", "AsIs", "UseIP", "UseIPv4", "UseIPv6"});
|
||||
core_ray_freedom_domainStrategy->setCurrentText(NekoGui::dataStore->core_ray_freedom_domainStrategy);
|
||||
layout->addWidget(core_ray_freedom_domainStrategy_l, ++line, 0);
|
||||
layout->addWidget(core_ray_freedom_domainStrategy, line, 1);
|
||||
#ifdef Q_OS_WIN
|
||||
auto core_ray_windows_disable_auto_interface_l = new QLabel("NKR_CORE_RAY_WINDOWS_DISABLE_AUTO_INTERFACE");
|
||||
core_ray_windows_disable_auto_interface_l->setToolTip(tr("If you Tun Mode is not working, try to change this option."));
|
||||
core_ray_windows_disable_auto_interface = new QCheckBox;
|
||||
core_ray_windows_disable_auto_interface->setChecked(NekoGui::dataStore->core_ray_windows_disable_auto_interface);
|
||||
connect(core_ray_windows_disable_auto_interface, &QCheckBox::clicked, this, [&] { CACHE.needRestart = true; });
|
||||
layout->addWidget(core_ray_windows_disable_auto_interface_l, ++line, 0);
|
||||
layout->addWidget(core_ray_windows_disable_auto_interface, line, 1);
|
||||
#endif
|
||||
}
|
||||
auto core_box_enable_clash_api_l = new QLabel("Enable Clash API");
|
||||
core_box_enable_clash_api = new QCheckBox;
|
||||
core_box_enable_clash_api->setChecked(NekoGui::dataStore->core_box_clash_api > 0);
|
||||
layout->addWidget(core_box_enable_clash_api_l, ++line, 0);
|
||||
layout->addWidget(core_box_enable_clash_api, line, 1);
|
||||
//
|
||||
auto core_box_clash_api_l = new QLabel("Clash API Listen Port");
|
||||
core_box_clash_api = new MyLineEdit;
|
||||
core_box_clash_api->setText(Int2String(std::abs(NekoGui::dataStore->core_box_clash_api)));
|
||||
layout->addWidget(core_box_clash_api_l, ++line, 0);
|
||||
layout->addWidget(core_box_clash_api, line, 1);
|
||||
//
|
||||
auto core_box_clash_api_secret_l = new QLabel("Clash API Secret");
|
||||
core_box_clash_api_secret = new MyLineEdit;
|
||||
core_box_clash_api_secret->setText(NekoGui::dataStore->core_box_clash_api_secret);
|
||||
layout->addWidget(core_box_clash_api_secret_l, ++line, 0);
|
||||
layout->addWidget(core_box_clash_api_secret, line, 1);
|
||||
//
|
||||
auto box = new QDialogButtonBox;
|
||||
box->setOrientation(Qt::Horizontal);
|
||||
box->setStandardButtons(QDialogButtonBox::Cancel | QDialogButtonBox::Ok);
|
||||
connect(box, &QDialogButtonBox::accepted, w, [=] {
|
||||
NekoGui::dataStore->core_box_underlying_dns = core_box_underlying_dns->text();
|
||||
if (IS_NEKO_BOX) {
|
||||
NekoGui::dataStore->core_box_clash_api = core_box_clash_api->text().toInt() * (core_box_enable_clash_api->isChecked() ? 1 : -1);
|
||||
NekoGui::dataStore->core_box_clash_api_secret = core_box_clash_api_secret->text();
|
||||
} else {
|
||||
NekoGui::dataStore->core_ray_direct_dns = core_ray_direct_dns->isChecked();
|
||||
NekoGui::dataStore->core_ray_freedom_domainStrategy = core_ray_freedom_domainStrategy->currentText();
|
||||
#ifdef Q_OS_WIN
|
||||
NekoGui::dataStore->core_ray_windows_disable_auto_interface = core_ray_windows_disable_auto_interface->isChecked();
|
||||
#endif
|
||||
}
|
||||
NekoGui::dataStore->core_box_clash_api = core_box_clash_api->text().toInt() * (core_box_enable_clash_api->isChecked() ? 1 : -1);
|
||||
NekoGui::dataStore->core_box_clash_api_secret = core_box_clash_api_secret->text();
|
||||
MW_dialog_message(Dialog_DialogBasicSettings, "UpdateDataStore");
|
||||
w->accept();
|
||||
});
|
||||
|
||||
@ -95,7 +95,7 @@
|
||||
<item>
|
||||
<widget class="QLabel" name="inbound_socks_port_l">
|
||||
<property name="text">
|
||||
<string>Socks Listen Port</string>
|
||||
<string>Mixed (SOCKS+HTTP) Listen Port</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@ -112,36 +112,6 @@
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_http">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_16">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="text">
|
||||
<string>HTTP Listen Port</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="inbound_http_port">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="http_enable">
|
||||
<property name="text">
|
||||
<string>Enable</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
@ -596,39 +566,7 @@
|
||||
<item>
|
||||
<widget class="QWidget" name="assest_group" native="true">
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_12">
|
||||
<property name="text">
|
||||
<string>Asset Location</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<layout class="QGridLayout" name="gridLayout_4">
|
||||
<item row="0" column="0">
|
||||
<widget class="MyLineEdit" name="core_v2ray_asset">
|
||||
<property name="placeholderText">
|
||||
<string>Default: dir of "nekoray"</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QPushButton" name="core_v2ray_asset_pick">
|
||||
<property name="text">
|
||||
<string>Select</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string notr="true">Loglevel</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QComboBox" name="log_level">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
|
||||
@ -638,14 +576,21 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_6">
|
||||
<property name="text">
|
||||
<string>Multiplex (mux)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string notr="true">Loglevel</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QComboBox" name="mux_protocol"/>
|
||||
@ -709,8 +654,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>198</width>
|
||||
<height>58</height>
|
||||
<width>632</width>
|
||||
<height>299</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_6">
|
||||
|
||||
@ -22,19 +22,13 @@ DialogManageRoutes::DialogManageRoutes(QWidget *parent) : QDialog(parent), ui(ne
|
||||
|
||||
QStringList qsValue = {""};
|
||||
QString dnsHelpDocumentUrl;
|
||||
if (IS_NEKO_BOX) {
|
||||
ui->outbound_domain_strategy->addItems(Preset::SingBox::DomainStrategy);
|
||||
ui->domainStrategyCombo->addItems(Preset::SingBox::DomainStrategy);
|
||||
qsValue += QString("prefer_ipv4 prefer_ipv6 ipv4_only ipv6_only").split(" ");
|
||||
ui->dns_object->setPlaceholderText(DecodeB64IfValid("ewogICJzZXJ2ZXJzIjogW10sCiAgInJ1bGVzIjogW10sCiAgImZpbmFsIjogIiIsCiAgInN0cmF0ZWd5IjogIiIsCiAgImRpc2FibGVfY2FjaGUiOiBmYWxzZSwKICAiZGlzYWJsZV9leHBpcmUiOiBmYWxzZSwKICAiaW5kZXBlbmRlbnRfY2FjaGUiOiBmYWxzZSwKICAicmV2ZXJzZV9tYXBwaW5nIjogZmFsc2UsCiAgImZha2VpcCI6IHt9Cn0="));
|
||||
dnsHelpDocumentUrl = "https://sing-box.sagernet.org/configuration/dns/";
|
||||
} else {
|
||||
ui->outbound_domain_strategy->addItems({"AsIs", "UseIPv4", "UseIPv6", "PreferIPv4", "PreferIPv6"});
|
||||
ui->domainStrategyCombo->addItems({"AsIs", "IPIfNonMatch", "IPOnDemand"});
|
||||
qsValue += QString("use_ip use_ip4 use_ip6").split(" ");
|
||||
ui->dns_object->setPlaceholderText(DecodeB64IfValid("ewogICJzZXJ2ZXJzIjogW10KfQ=="));
|
||||
dnsHelpDocumentUrl = "https://www.v2fly.org/config/dns.html";
|
||||
}
|
||||
//
|
||||
ui->outbound_domain_strategy->addItems(Preset::SingBox::DomainStrategy);
|
||||
ui->domainStrategyCombo->addItems(Preset::SingBox::DomainStrategy);
|
||||
qsValue += QStringLiteral("prefer_ipv4 prefer_ipv6 ipv4_only ipv6_only").split(" ");
|
||||
ui->dns_object->setPlaceholderText(DecodeB64IfValid("ewogICJzZXJ2ZXJzIjogW10sCiAgInJ1bGVzIjogW10sCiAgImZpbmFsIjogIiIsCiAgInN0cmF0ZWd5IjogIiIsCiAgImRpc2FibGVfY2FjaGUiOiBmYWxzZSwKICAiZGlzYWJsZV9leHBpcmUiOiBmYWxzZSwKICAiaW5kZXBlbmRlbnRfY2FjaGUiOiBmYWxzZSwKICAicmV2ZXJzZV9tYXBwaW5nIjogZmFsc2UsCiAgImZha2VpcCI6IHt9Cn0="));
|
||||
dnsHelpDocumentUrl = "https://sing-box.sagernet.org/configuration/dns/";
|
||||
//
|
||||
ui->direct_dns_strategy->addItems(qsValue);
|
||||
ui->remote_dns_strategy->addItems(qsValue);
|
||||
//
|
||||
|
||||
@ -20,7 +20,6 @@ DialogVPNSettings::DialogVPNSettings(QWidget *parent) : QDialog(parent), ui(new
|
||||
ui->hide_console->setVisible(false);
|
||||
#endif
|
||||
ui->strict_route->setChecked(NekoGui::dataStore->vpn_strict_route);
|
||||
ui->single_core->setVisible(IS_NEKO_BOX);
|
||||
ui->single_core->setChecked(NekoGui::dataStore->vpn_internal_tun);
|
||||
//
|
||||
D_LOAD_STRING_PLAIN(vpn_rule_cidr)
|
||||
|
||||
@ -62,7 +62,7 @@ DialogEditProfile::DialogEditProfile(const QString &_type, int profileOrGroupId,
|
||||
ui->host_l->setVisible(false);
|
||||
}
|
||||
// 传输设置 ED
|
||||
if (txt == "ws" && IS_NEKO_BOX) {
|
||||
if (txt == "ws") {
|
||||
ui->ws_early_data_length->setVisible(true);
|
||||
ui->ws_early_data_length_l->setVisible(true);
|
||||
ui->ws_early_data_name->setVisible(true);
|
||||
@ -74,11 +74,7 @@ DialogEditProfile::DialogEditProfile(const QString &_type, int profileOrGroupId,
|
||||
ui->ws_early_data_name_l->setVisible(false);
|
||||
}
|
||||
// 传输设置 for NekoBox
|
||||
if (IS_NEKO_BOX) {
|
||||
if (!ui->utlsFingerprint->count()) ui->utlsFingerprint->addItems(Preset::SingBox::UtlsFingerPrint);
|
||||
} else {
|
||||
if (!ui->utlsFingerprint->count()) ui->utlsFingerprint->addItems(Preset::Xray::UtlsFingerPrint);
|
||||
}
|
||||
if (!ui->utlsFingerprint->count()) ui->utlsFingerprint->addItems(Preset::SingBox::UtlsFingerPrint);
|
||||
// 传输设置 是否可见
|
||||
int networkBoxVisible = 0;
|
||||
for (auto label: ui->network_box->findChildren<QLabel *>()) {
|
||||
@ -89,19 +85,13 @@ DialogEditProfile::DialogEditProfile(const QString &_type, int profileOrGroupId,
|
||||
});
|
||||
ui->network->removeItem(0);
|
||||
|
||||
// if (IS_NEKO_BOX) {
|
||||
// ui->network->addItem("httpupgrade");
|
||||
// }
|
||||
|
||||
// security changed
|
||||
connect(ui->security, &QComboBox::currentTextChanged, this, [=](const QString &txt) {
|
||||
if (txt == "tls") {
|
||||
ui->security_box->setVisible(true);
|
||||
ui->tls_camouflage_box->setVisible(true);
|
||||
if (IS_NEKO_BOX) {
|
||||
ui->reality_spx->hide();
|
||||
ui->reality_spx_l->hide();
|
||||
}
|
||||
ui->reality_spx->hide();
|
||||
ui->reality_spx_l->hide();
|
||||
} else {
|
||||
ui->security_box->setVisible(false);
|
||||
ui->tls_camouflage_box->setVisible(false);
|
||||
@ -124,7 +114,6 @@ DialogEditProfile::DialogEditProfile(const QString &_type, int profileOrGroupId,
|
||||
LOAD_TYPE("vmess")
|
||||
LOAD_TYPE("vless")
|
||||
LOAD_TYPE("naive")
|
||||
LOAD_TYPE("hysteria")
|
||||
LOAD_TYPE("hysteria2")
|
||||
LOAD_TYPE("tuic")
|
||||
ui->type->addItem(tr("Custom (%1 outbound)").arg(software_core_name), "internal");
|
||||
@ -182,7 +171,7 @@ void DialogEditProfile::typeSelected(const QString &newType) {
|
||||
auto _innerWidget = new EditNaive(this);
|
||||
innerWidget = _innerWidget;
|
||||
innerEditor = _innerWidget;
|
||||
} else if (type == "hysteria" || type == "hysteria2" || type == "tuic") {
|
||||
} else if (type == "hysteria2" || type == "tuic") {
|
||||
auto _innerWidget = new EditQUIC(this);
|
||||
innerWidget = _innerWidget;
|
||||
innerEditor = _innerWidget;
|
||||
@ -286,47 +275,43 @@ void DialogEditProfile::typeSelected(const QString &newType) {
|
||||
ADD_ASTERISK(this)
|
||||
|
||||
// 设置 for NekoBox
|
||||
if (IS_NEKO_BOX) {
|
||||
if (type == "vmess" || type == "vless") {
|
||||
ui->packet_encoding->setVisible(true);
|
||||
ui->packet_encoding_l->setVisible(true);
|
||||
} else {
|
||||
ui->packet_encoding->setVisible(false);
|
||||
ui->packet_encoding_l->setVisible(false);
|
||||
}
|
||||
if (type == "vmess" || type == "vless" || type == "trojan") {
|
||||
ui->network_l->setVisible(true);
|
||||
ui->network->setVisible(true);
|
||||
ui->network_box->setVisible(true);
|
||||
} else {
|
||||
ui->network_l->setVisible(false);
|
||||
ui->network->setVisible(false);
|
||||
ui->network_box->setVisible(false);
|
||||
}
|
||||
if (type == "vmess" || type == "vless" || type == "trojan" || type == "http") {
|
||||
ui->security->setVisible(true);
|
||||
ui->security_l->setVisible(true);
|
||||
} else {
|
||||
ui->security->setVisible(false);
|
||||
ui->security_l->setVisible(false);
|
||||
}
|
||||
if (type == "vmess" || type == "vless" || type == "trojan" || type == "shadowsocks") {
|
||||
ui->multiplex->setVisible(true);
|
||||
ui->multiplex_l->setVisible(true);
|
||||
} else {
|
||||
ui->multiplex->setVisible(false);
|
||||
ui->multiplex_l->setVisible(false);
|
||||
}
|
||||
// 设置 是否可见
|
||||
int streamBoxVisible = 0;
|
||||
for (auto label: ui->stream_box->findChildren<QLabel *>()) {
|
||||
if (!label->isHidden()) streamBoxVisible++;
|
||||
}
|
||||
ui->stream_box->setVisible(streamBoxVisible);
|
||||
if (type == "vmess" || type == "vless") {
|
||||
ui->packet_encoding->setVisible(true);
|
||||
ui->packet_encoding_l->setVisible(true);
|
||||
} else {
|
||||
ui->packet_encoding->setVisible(false);
|
||||
ui->packet_encoding_l->setVisible(false);
|
||||
}
|
||||
if (type == "vmess" || type == "vless" || type == "trojan") {
|
||||
ui->network_l->setVisible(true);
|
||||
ui->network->setVisible(true);
|
||||
ui->network_box->setVisible(true);
|
||||
} else {
|
||||
ui->network_l->setVisible(false);
|
||||
ui->network->setVisible(false);
|
||||
ui->network_box->setVisible(false);
|
||||
}
|
||||
if (type == "vmess" || type == "vless" || type == "trojan" || type == "http") {
|
||||
ui->security->setVisible(true);
|
||||
ui->security_l->setVisible(true);
|
||||
} else {
|
||||
ui->security->setVisible(false);
|
||||
ui->security_l->setVisible(false);
|
||||
}
|
||||
if (type == "vmess" || type == "vless" || type == "trojan" || type == "shadowsocks") {
|
||||
ui->multiplex->setVisible(true);
|
||||
ui->multiplex_l->setVisible(true);
|
||||
} else {
|
||||
ui->multiplex->setVisible(false);
|
||||
ui->multiplex_l->setVisible(false);
|
||||
}
|
||||
|
||||
// 设置 是否可见
|
||||
int streamBoxVisible = 0;
|
||||
for (auto label: ui->stream_box->findChildren<QLabel *>()) {
|
||||
if (!label->isHidden()) streamBoxVisible++;
|
||||
}
|
||||
ui->stream_box->setVisible(streamBoxVisible);
|
||||
|
||||
// 载入 type 之后,有些类型没有右边的设置
|
||||
auto rightNoBox = (ui->stream_box->isHidden() && ui->network_box->isHidden() && ui->security_box->isHidden());
|
||||
|
||||
@ -245,6 +245,11 @@
|
||||
<string notr="true">ws</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string notr="true">httpupgrade</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string notr="true">http</string>
|
||||
|
||||
@ -40,7 +40,6 @@ void EditCustom::onStart(std::shared_ptr<NekoGui::ProxyEntity> _ent) {
|
||||
// load known core
|
||||
auto core_map = QString2QJsonObject(NekoGui::dataStore->extraCore->core_map);
|
||||
for (const auto &key: core_map.keys()) {
|
||||
if (key == "naive" || key == "hysteria") continue;
|
||||
ui->core->addItem(key);
|
||||
}
|
||||
if (preset_core == "internal") {
|
||||
@ -119,7 +118,7 @@ void EditCustom::onStart(std::shared_ptr<NekoGui::ProxyEntity> _ent) {
|
||||
auto command = QStringList{extR->program};
|
||||
command += extR->arguments;
|
||||
auto btn = QMessageBox::information(this, tr("Preview config"),
|
||||
QString("Command: %1\n\n%2").arg(QStringList2Command(command), extR->config_export),
|
||||
QStringLiteral("Command: %1\n\n%2").arg(QStringList2Command(command), extR->config_export),
|
||||
"OK", "Copy", "", 0, 0);
|
||||
if (btn == 1) {
|
||||
QApplication::clipboard()->setText(extR->config_export);
|
||||
|
||||
@ -25,13 +25,10 @@ void EditQUIC::onStart(std::shared_ptr<NekoGui::ProxyEntity> _ent) {
|
||||
P_LOAD_INT(downloadMbps);
|
||||
P_LOAD_BOOL(disableMtuDiscovery)
|
||||
P_LOAD_STRING(obfsPassword);
|
||||
P_LOAD_STRING(authPayload);
|
||||
P_LOAD_INT(streamReceiveWindow);
|
||||
P_LOAD_INT(connectionReceiveWindow);
|
||||
|
||||
P_LOAD_BOOL(forceExternal);
|
||||
P_LOAD_COMBO_INT(hyProtocol);
|
||||
P_LOAD_COMBO_INT(authPayloadType);
|
||||
P_LOAD_STRING(uuid);
|
||||
P_LOAD_STRING(password);
|
||||
|
||||
@ -48,7 +45,7 @@ void EditQUIC::onStart(std::shared_ptr<NekoGui::ProxyEntity> _ent) {
|
||||
P_LOAD_BOOL(allowInsecure);
|
||||
P_LOAD_BOOL(disableSni);
|
||||
|
||||
if (bean->proxy_type == NekoGui_fmt::QUICBean::proxy_Hysteria || bean->proxy_type == NekoGui_fmt::QUICBean::proxy_Hysteria2) {
|
||||
if (bean->proxy_type == NekoGui_fmt::QUICBean::proxy_Hysteria2) {
|
||||
ui->uuid->hide();
|
||||
ui->uuid_l->hide();
|
||||
ui->uuidgen->hide();
|
||||
@ -60,31 +57,15 @@ void EditQUIC::onStart(std::shared_ptr<NekoGui::ProxyEntity> _ent) {
|
||||
ui->heartbeat->hide();
|
||||
ui->heartbeat_l->hide();
|
||||
ui->uos->hide();
|
||||
if (!IS_NEKO_BOX) ui->forceExternal->hide();
|
||||
|
||||
if (bean->proxy_type == NekoGui_fmt::QUICBean::proxy_Hysteria) { // hy1
|
||||
ui->password->hide();
|
||||
ui->password_l->hide();
|
||||
} else { // hy2
|
||||
ui->hyProtocol->hide();
|
||||
ui->hyProtocol_l->hide();
|
||||
ui->hyProtocol->hide();
|
||||
ui->hyProtocol_l->hide();
|
||||
ui->authPayload->hide();
|
||||
ui->authPayload_l->hide();
|
||||
ui->authPayloadType->hide();
|
||||
ui->authPayloadType_l->hide();
|
||||
ui->alpn->hide();
|
||||
ui->alpn_l->hide();
|
||||
ui->TLS->removeItem(ui->alpn_sp);
|
||||
if (IS_NEKO_BOX) {
|
||||
ui->disableMtuDiscovery->hide();
|
||||
ui->connectionReceiveWindow->hide();
|
||||
ui->connectionReceiveWindow_l->hide();
|
||||
ui->streamReceiveWindow->hide();
|
||||
ui->streamReceiveWindow_l->hide();
|
||||
}
|
||||
}
|
||||
ui->alpn->hide();
|
||||
ui->alpn_l->hide();
|
||||
ui->TLS->removeItem(ui->alpn_sp);
|
||||
ui->disableMtuDiscovery->hide();
|
||||
ui->connectionReceiveWindow->hide();
|
||||
ui->connectionReceiveWindow_l->hide();
|
||||
ui->streamReceiveWindow->hide();
|
||||
ui->streamReceiveWindow_l->hide();
|
||||
} else if (bean->proxy_type == NekoGui_fmt::QUICBean::proxy_TUIC) {
|
||||
ui->hopPort->hide();
|
||||
ui->hopPort_l->hide();
|
||||
@ -94,22 +75,14 @@ void EditQUIC::onStart(std::shared_ptr<NekoGui::ProxyEntity> _ent) {
|
||||
ui->uploadMbps_l->hide();
|
||||
ui->downloadMbps->hide();
|
||||
ui->downloadMbps_l->hide();
|
||||
ui->hyProtocol->hide();
|
||||
ui->hyProtocol_l->hide();
|
||||
ui->disableMtuDiscovery->hide();
|
||||
ui->obfsPassword->hide();
|
||||
ui->obfsPassword_l->hide();
|
||||
ui->authPayload->hide();
|
||||
ui->authPayload_l->hide();
|
||||
ui->authPayloadType->hide();
|
||||
ui->authPayloadType_l->hide();
|
||||
ui->streamReceiveWindow->hide();
|
||||
ui->streamReceiveWindow_l->hide();
|
||||
ui->connectionReceiveWindow->hide();
|
||||
ui->connectionReceiveWindow_l->hide();
|
||||
if (!IS_NEKO_BOX) {
|
||||
ui->uos->hide();
|
||||
}
|
||||
ui->uos->hide();
|
||||
}
|
||||
}
|
||||
|
||||
@ -118,16 +91,13 @@ bool EditQUIC::onEnd() {
|
||||
|
||||
P_SAVE_BOOL(forceExternal);
|
||||
|
||||
// Hysteria
|
||||
// Hysteria 2
|
||||
P_SAVE_STRING(hopPort);
|
||||
P_SAVE_INT(hopInterval);
|
||||
P_SAVE_INT(uploadMbps);
|
||||
P_SAVE_INT(downloadMbps);
|
||||
P_SAVE_COMBO_INT(hyProtocol);
|
||||
P_SAVE_BOOL(disableMtuDiscovery)
|
||||
P_SAVE_STRING(obfsPassword);
|
||||
P_SAVE_COMBO_INT(authPayloadType);
|
||||
P_SAVE_STRING(authPayload);
|
||||
P_SAVE_INT(streamReceiveWindow);
|
||||
P_SAVE_INT(connectionReceiveWindow);
|
||||
|
||||
|
||||
@ -16,6 +16,48 @@
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<layout class="QGridLayout" name="upBox">
|
||||
<item row="1" column="1">
|
||||
<layout class="QHBoxLayout" name="downloadMbpsLay">
|
||||
<item>
|
||||
<widget class="QLabel" name="downloadMbps_l">
|
||||
<property name="text">
|
||||
<string>Download (Mbps)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="MyLineEdit" name="downloadMbps"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<layout class="QHBoxLayout" name="hopPortLay">
|
||||
<item>
|
||||
<widget class="QLabel" name="hopPort_l">
|
||||
<property name="text">
|
||||
<string>Hop Port</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="MyLineEdit" name="hopPort"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<layout class="QHBoxLayout" name="hopIntervalLay">
|
||||
<item>
|
||||
<widget class="QLabel" name="hopInterval_l">
|
||||
<property name="text">
|
||||
<string>Hop Interval (s)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="MyLineEdit" name="hopInterval"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<layout class="QHBoxLayout" name="heartbeatLay">
|
||||
<item>
|
||||
@ -37,20 +79,27 @@
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<layout class="QHBoxLayout" name="hopPortLay">
|
||||
<item row="1" column="0">
|
||||
<layout class="QHBoxLayout" name="uploadMbpsLay">
|
||||
<item>
|
||||
<widget class="QLabel" name="hopPort_l">
|
||||
<widget class="QLabel" name="uploadMbps_l">
|
||||
<property name="text">
|
||||
<string>Hop Port</string>
|
||||
<string>Upload (Mbps)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="MyLineEdit" name="hopPort"/>
|
||||
<widget class="MyLineEdit" name="uploadMbps"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QCheckBox" name="zeroRttHandshake">
|
||||
<property name="text">
|
||||
<string>Zero Rtt Handshake</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<layout class="QHBoxLayout" name="congestionControlLay">
|
||||
<item>
|
||||
@ -81,48 +130,6 @@
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<layout class="QHBoxLayout" name="downloadMbpsLay">
|
||||
<item>
|
||||
<widget class="QLabel" name="downloadMbps_l">
|
||||
<property name="text">
|
||||
<string>Download (Mbps)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="MyLineEdit" name="downloadMbps"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<layout class="QHBoxLayout" name="hopIntervalLay">
|
||||
<item>
|
||||
<widget class="QLabel" name="hopInterval_l">
|
||||
<property name="text">
|
||||
<string>Hop Interval (s)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="MyLineEdit" name="hopInterval"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<layout class="QHBoxLayout" name="uploadMbpsLay">
|
||||
<item>
|
||||
<widget class="QLabel" name="uploadMbps_l">
|
||||
<property name="text">
|
||||
<string>Upload (Mbps)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="MyLineEdit" name="uploadMbps"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<layout class="QHBoxLayout" name="udpRelayModeLay">
|
||||
<item>
|
||||
@ -148,14 +155,18 @@
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QCheckBox" name="zeroRttHandshake">
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="forceExternal">
|
||||
<property name="text">
|
||||
<string>Zero Rtt Handshake</string>
|
||||
<string>Force use external core</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="uos">
|
||||
<property name="toolTip">
|
||||
<string notr="true">Requires sing-box server</string>
|
||||
@ -165,10 +176,16 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QCheckBox" name="forceExternal">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="disableMtuDiscovery">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Force use external core</string>
|
||||
<string>Disable MTU Discovery</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@ -176,93 +193,38 @@
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QGridLayout" name="obfuscation">
|
||||
<item row="1" column="0">
|
||||
<item row="0" column="1">
|
||||
<widget class="MyLineEdit" name="obfsPassword"/>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="obfsPassword_l">
|
||||
<property name="text">
|
||||
<string>Obfs Password</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="hyProtocol_l">
|
||||
<property name="text">
|
||||
<string>Protocol</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="QComboBox" name="hyProtocol">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string notr="true">QUIC</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string notr="true">FakeTCP</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string notr="true">wechat-video</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="disableMtuDiscovery">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Disable MTU Discovery</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="MyLineEdit" name="obfsPassword"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QGridLayout" name="authentication">
|
||||
<item row="2" column="1">
|
||||
<widget class="MyLineEdit" name="uuid"/>
|
||||
<item row="0" column="2">
|
||||
<widget class="QPushButton" name="uuidgen">
|
||||
<property name="text">
|
||||
<string>Generate UUID</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="authPayloadType_l">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<widget class="QLabel" name="uuid_l">
|
||||
<property name="text">
|
||||
<string>Auth Type</string>
|
||||
<string notr="true">UUID</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="MyLineEdit" name="uuid"/>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="authPayload_l">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Auth Payload</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="password_l">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
|
||||
@ -275,44 +237,8 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="uuid_l">
|
||||
<property name="text">
|
||||
<string notr="true">UUID</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="2">
|
||||
<widget class="QPushButton" name="uuidgen">
|
||||
<property name="text">
|
||||
<string>Generate UUID</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1" colspan="2">
|
||||
<widget class="MyLineEdit" name="password"/>
|
||||
</item>
|
||||
<item row="1" column="1" colspan="2">
|
||||
<widget class="MyLineEdit" name="authPayload"/>
|
||||
</item>
|
||||
<item row="0" column="1" colspan="2">
|
||||
<widget class="QComboBox" name="authPayloadType">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string notr="true">NONE</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string notr="true">STRING</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string notr="true">BASE64</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
<widget class="MyLineEdit" name="password"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
@ -428,11 +354,8 @@
|
||||
<tabstop>zeroRttHandshake</tabstop>
|
||||
<tabstop>forceExternal</tabstop>
|
||||
<tabstop>uos</tabstop>
|
||||
<tabstop>hyProtocol</tabstop>
|
||||
<tabstop>disableMtuDiscovery</tabstop>
|
||||
<tabstop>obfsPassword</tabstop>
|
||||
<tabstop>authPayloadType</tabstop>
|
||||
<tabstop>authPayload</tabstop>
|
||||
<tabstop>uuid</tabstop>
|
||||
<tabstop>uuidgen</tabstop>
|
||||
<tabstop>password</tabstop>
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
EditShadowSocks::EditShadowSocks(QWidget *parent) : QWidget(parent),
|
||||
ui(new Ui::EditShadowSocks) {
|
||||
ui->setupUi(this);
|
||||
ui->method->addItems(IS_NEKO_BOX ? Preset::SingBox::ShadowsocksMethods : Preset::Xray::ShadowsocksMethods);
|
||||
ui->method->addItems(Preset::SingBox::ShadowsocksMethods);
|
||||
}
|
||||
|
||||
EditShadowSocks::~EditShadowSocks() {
|
||||
|
||||
@ -23,7 +23,7 @@ void EditTrojanVLESS::onStart(std::shared_ptr<NekoGui::ProxyEntity> _ent) {
|
||||
ui->flow_l->hide();
|
||||
}
|
||||
ui->password->setText(bean->password);
|
||||
ui->flow->addItems(IS_NEKO_BOX ? Preset::SingBox::Flows : Preset::Xray::Flows);
|
||||
ui->flow->addItems(Preset::SingBox::Flows);
|
||||
ui->flow->setCurrentText(bean->flow);
|
||||
}
|
||||
|
||||
|
||||
@ -21,7 +21,6 @@
|
||||
#include "3rdparty/qrcodegen.hpp"
|
||||
#include "3rdparty/VT100Parser.hpp"
|
||||
#include "3rdparty/qv2ray/v2/components/proxy/QvProxyConfigurator.hpp"
|
||||
#include "3rdparty/qv2ray/v2/ui/LogHighlighter.hpp"
|
||||
|
||||
#ifndef NKR_NO_ZXING
|
||||
#include "3rdparty/ZxingQtReader.hpp"
|
||||
@ -91,18 +90,9 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi
|
||||
}
|
||||
}
|
||||
|
||||
// software_name
|
||||
if (IS_NEKO_BOX) {
|
||||
software_name = "NekoBox";
|
||||
software_core_name = "sing-box";
|
||||
// replace default values
|
||||
if (NekoGui::dataStore->log_level == "warning") NekoGui::dataStore->log_level = "info";
|
||||
if (NekoGui::dataStore->mux_protocol.isEmpty()) NekoGui::dataStore->mux_protocol = "h2mux";
|
||||
//
|
||||
if (QDir("dashboard").count() == 0) {
|
||||
QDir().mkdir("dashboard");
|
||||
QFile::copy(":/neko/dashboard-notice.html", "dashboard/index.html");
|
||||
}
|
||||
if (QDir("dashboard").count() == 0) {
|
||||
QDir().mkdir("dashboard");
|
||||
QFile::copy(":/neko/dashboard-notice.html", "dashboard/index.html");
|
||||
}
|
||||
|
||||
// top bar
|
||||
@ -111,12 +101,12 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi
|
||||
ui->toolButton_server->setMenu(ui->menu_server);
|
||||
ui->menubar->setVisible(false);
|
||||
connect(ui->toolButton_document, &QToolButton::clicked, this, [=] { QDesktopServices::openUrl(QUrl("https://matsuridayo.github.io/")); });
|
||||
connect(ui->toolButton_ads, &QToolButton::clicked, this, [=] { QDesktopServices::openUrl(QUrl("https://matsuricom.pages.dev/")); });
|
||||
connect(ui->toolButton_ads, &QToolButton::clicked, this, [=] { QDesktopServices::openUrl(QUrl("https://neko-box.pages.dev/喵")); });
|
||||
connect(ui->toolButton_update, &QToolButton::clicked, this, [=] { runOnNewThread([=] { CheckUpdate(); }); });
|
||||
connect(ui->toolButton_url_test, &QToolButton::clicked, this, [=] { speedtest_current_group(1, true); });
|
||||
|
||||
// Setup log UI
|
||||
ui->splitter->restoreState(DecodeB64IfValid(NekoGui::dataStore->splitter_state));
|
||||
new SyntaxHighlighter(false, qvLogDocument);
|
||||
qvLogDocument->setUndoRedoEnabled(false);
|
||||
ui->masterLogBrowser->setUndoRedoEnabled(false);
|
||||
ui->masterLogBrowser->setDocument(qvLogDocument);
|
||||
@ -344,10 +334,10 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi
|
||||
neko_set_spmode_vpn(false);
|
||||
});
|
||||
connect(ui->menu_qr, &QAction::triggered, this, [=]() { display_qr_link(false); });
|
||||
connect(ui->menu_tcp_ping, &QAction::triggered, this, [=]() { speedtest_current_group(0); });
|
||||
connect(ui->menu_url_test, &QAction::triggered, this, [=]() { speedtest_current_group(1); });
|
||||
connect(ui->menu_full_test, &QAction::triggered, this, [=]() { speedtest_current_group(2); });
|
||||
connect(ui->menu_stop_testing, &QAction::triggered, this, [=]() { speedtest_current_group(114514); });
|
||||
connect(ui->menu_tcp_ping, &QAction::triggered, this, [=]() { speedtest_current_group(0, false); });
|
||||
connect(ui->menu_url_test, &QAction::triggered, this, [=]() { speedtest_current_group(1, false); });
|
||||
connect(ui->menu_full_test, &QAction::triggered, this, [=]() { speedtest_current_group(2, false); });
|
||||
connect(ui->menu_stop_testing, &QAction::triggered, this, [=]() { speedtest_current_group(114514, false); });
|
||||
//
|
||||
auto set_selected_or_group = [=](int mode) {
|
||||
// 0=group 1=select 2=unknown(menu is hide)
|
||||
@ -398,10 +388,10 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi
|
||||
if (NekoGui::dataStore->core_port <= 0) NekoGui::dataStore->core_port = 19810;
|
||||
|
||||
auto core_path = QApplication::applicationDirPath() + "/";
|
||||
core_path += IS_NEKO_BOX ? "nekobox_core" : "nekoray_core";
|
||||
core_path += "nekobox_core";
|
||||
|
||||
QStringList args;
|
||||
args.push_back(IS_NEKO_BOX ? "nekobox" : "nekoray");
|
||||
args.push_back("nekobox");
|
||||
args.push_back("-port");
|
||||
args.push_back(Int2String(NekoGui::dataStore->core_port));
|
||||
if (NekoGui::dataStore->flag_debug) args.push_back("-debug");
|
||||
@ -488,7 +478,7 @@ void MainWindow::show_group(int gid) {
|
||||
|
||||
auto group = NekoGui::profileManager->GetGroup(gid);
|
||||
if (group == nullptr) {
|
||||
MessageBoxWarning(tr("Error"), QString("No such group: %1").arg(gid));
|
||||
MessageBoxWarning(tr("Error"), QStringLiteral("No such group: %1").arg(gid));
|
||||
NekoGui::dataStore->refreshing_group = false;
|
||||
return;
|
||||
}
|
||||
@ -639,7 +629,7 @@ void MainWindow::on_commitDataRequest() {
|
||||
//
|
||||
if (!isMaximized()) {
|
||||
auto olds = NekoGui::dataStore->mw_size;
|
||||
auto news = QString("%1x%2").arg(size().width()).arg(size().height());
|
||||
auto news = QStringLiteral("%1x%2").arg(size().width()).arg(size().height());
|
||||
if (olds != news) {
|
||||
NekoGui::dataStore->mw_size = news;
|
||||
}
|
||||
@ -726,20 +716,8 @@ void MainWindow::on_menu_exit_triggered() {
|
||||
void MainWindow::neko_set_spmode_system_proxy(bool enable, bool save) {
|
||||
if (enable != NekoGui::dataStore->spmode_system_proxy) {
|
||||
if (enable) {
|
||||
#if defined(Q_OS_WIN)
|
||||
if (!IS_NEKO_BOX && !IsValidPort(NekoGui::dataStore->inbound_http_port)) {
|
||||
auto btn = QMessageBox::warning(this, software_name,
|
||||
tr("Http inbound is not enabled, can't set system proxy."),
|
||||
"OK", tr("Settings"), "", 0, 0);
|
||||
if (btn == 1) {
|
||||
on_menu_basic_settings_triggered();
|
||||
}
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
auto socks_port = NekoGui::dataStore->inbound_socks_port;
|
||||
auto http_port = NekoGui::dataStore->inbound_http_port;
|
||||
if (IS_NEKO_BOX) http_port = socks_port;
|
||||
auto http_port = NekoGui::dataStore->inbound_socks_port;
|
||||
SetSystemProxy(http_port, socks_port);
|
||||
} else {
|
||||
ClearSystemProxy();
|
||||
@ -761,7 +739,7 @@ void MainWindow::neko_set_spmode_system_proxy(bool enable, bool save) {
|
||||
void MainWindow::neko_set_spmode_vpn(bool enable, bool save) {
|
||||
if (enable != NekoGui::dataStore->spmode_vpn) {
|
||||
if (enable) {
|
||||
if (IS_NEKO_BOX_INTERNAL_TUN) {
|
||||
if (NekoGui::dataStore->vpn_internal_tun) {
|
||||
bool requestPermission = !NekoGui::IsAdmin();
|
||||
if (requestPermission) {
|
||||
#ifdef Q_OS_LINUX
|
||||
@ -799,7 +777,7 @@ void MainWindow::neko_set_spmode_vpn(bool enable, bool save) {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (IS_NEKO_BOX_INTERNAL_TUN) {
|
||||
if (NekoGui::dataStore->vpn_internal_tun) {
|
||||
// current core is sing-box
|
||||
} else {
|
||||
if (!StopVPNProcess()) {
|
||||
@ -820,7 +798,7 @@ void MainWindow::neko_set_spmode_vpn(bool enable, bool save) {
|
||||
NekoGui::dataStore->spmode_vpn = enable;
|
||||
refresh_status();
|
||||
|
||||
if (IS_NEKO_BOX_INTERNAL_TUN && NekoGui::dataStore->started_id >= 0) neko_start(NekoGui::dataStore->started_id);
|
||||
if (NekoGui::dataStore->vpn_internal_tun && NekoGui::dataStore->started_id >= 0) neko_start(NekoGui::dataStore->started_id);
|
||||
}
|
||||
|
||||
void MainWindow::refresh_status(const QString &traffic_update) {
|
||||
@ -854,17 +832,12 @@ void MainWindow::refresh_status(const QString &traffic_update) {
|
||||
|
||||
if (last_test_time.addSecs(2) < QTime::currentTime()) {
|
||||
auto txt = running == nullptr ? tr("Not Running")
|
||||
: QString("[%1] %2").arg(group_name, running->bean->DisplayName()).left(30);
|
||||
: QStringLiteral("[%1] %2").arg(group_name, running->bean->DisplayName()).left(30);
|
||||
ui->label_running->setText(txt);
|
||||
}
|
||||
//
|
||||
auto display_http = tr("None");
|
||||
if (IsValidPort(NekoGui::dataStore->inbound_http_port)) {
|
||||
display_http = DisplayAddress(NekoGui::dataStore->inbound_address, NekoGui::dataStore->inbound_http_port);
|
||||
}
|
||||
auto display_socks = DisplayAddress(NekoGui::dataStore->inbound_address, NekoGui::dataStore->inbound_socks_port);
|
||||
auto inbound_txt = QString("Socks: %1\nHTTP: %2").arg(display_socks, display_http);
|
||||
if (IS_NEKO_BOX) inbound_txt = QString("Mixed: %1").arg(display_socks);
|
||||
auto inbound_txt = QStringLiteral("Mixed: %1").arg(display_socks);
|
||||
ui->label_inbound->setText(inbound_txt);
|
||||
//
|
||||
ui->checkBox_VPN->setChecked(NekoGui::dataStore->spmode_vpn);
|
||||
@ -1194,7 +1167,7 @@ void MainWindow::on_menu_profile_debug_info_triggered() {
|
||||
if (ents.count() != 1) return;
|
||||
auto btn = QMessageBox::information(this, software_name, ents.first()->ToJsonBytes(), "OK", "Edit", "Reload", 0, 0);
|
||||
if (btn == 1) {
|
||||
QDesktopServices::openUrl(QUrl::fromLocalFile(QFileInfo(QString("profiles/%1.json").arg(ents.first()->id)).absoluteFilePath()));
|
||||
QDesktopServices::openUrl(QUrl::fromLocalFile(QFileInfo(QStringLiteral("profiles/%1.json").arg(ents.first()->id)).absoluteFilePath()));
|
||||
} else if (btn == 2) {
|
||||
NekoGui::dataStore->Load();
|
||||
NekoGui::profileManager->LoadManager();
|
||||
@ -1235,23 +1208,23 @@ void MainWindow::on_menu_export_config_triggered() {
|
||||
if (ent->bean->DisplayCoreType() != software_core_name) return;
|
||||
|
||||
auto result = BuildConfig(ent, false, true);
|
||||
QString config_core = QJsonObject2QString(result->coreConfig, true);
|
||||
QString config_core = QJsonObject2QString(result->coreConfig, false);
|
||||
QApplication::clipboard()->setText(config_core);
|
||||
|
||||
QMessageBox msg(QMessageBox::Information, tr("Config copied"), config_core);
|
||||
QMessageBox msg(QMessageBox::Information, tr("Config copied"), tr("Config copied"));
|
||||
msg.addButton("Copy core config", QMessageBox::YesRole);
|
||||
msg.addButton("Copy test config", QMessageBox::YesRole);
|
||||
msg.addButton("Copy test config", QMessageBox::NoRole);
|
||||
msg.addButton(QMessageBox::Ok);
|
||||
msg.setEscapeButton(QMessageBox::Ok);
|
||||
msg.setDefaultButton(QMessageBox::Ok);
|
||||
auto ret = msg.exec();
|
||||
if (ret == 0) {
|
||||
if (ret == 2) {
|
||||
result = BuildConfig(ent, false, false);
|
||||
config_core = QJsonObject2QString(result->coreConfig, true);
|
||||
config_core = QJsonObject2QString(result->coreConfig, false);
|
||||
QApplication::clipboard()->setText(config_core);
|
||||
} else if (ret == 1) {
|
||||
} else if (ret == 3) {
|
||||
result = BuildConfig(ent, true, false);
|
||||
config_core = QJsonObject2QString(result->coreConfig, true);
|
||||
config_core = QJsonObject2QString(result->coreConfig, false);
|
||||
QApplication::clipboard()->setText(config_core);
|
||||
}
|
||||
}
|
||||
@ -1802,9 +1775,8 @@ bool MainWindow::StartVPNProcess() {
|
||||
return true;
|
||||
}
|
||||
//
|
||||
auto protectPath = QDir::currentPath() + "/protect";
|
||||
auto configPath = NekoGui::WriteVPNSingBoxConfig();
|
||||
auto scriptPath = NekoGui::WriteVPNLinuxScript(protectPath, configPath);
|
||||
auto scriptPath = NekoGui::WriteVPNLinuxScript(configPath);
|
||||
//
|
||||
#ifdef Q_OS_WIN
|
||||
runOnNewThread([=] {
|
||||
@ -1816,11 +1788,6 @@ bool MainWindow::StartVPNProcess() {
|
||||
runOnUiThread([=] { neko_set_spmode_vpn(false); });
|
||||
});
|
||||
#else
|
||||
QFile::remove(protectPath);
|
||||
if (QFile::exists(protectPath)) {
|
||||
MessageBoxWarning("Error", "protect cannot be removed");
|
||||
return false;
|
||||
}
|
||||
//
|
||||
auto vpn_process = new QProcess;
|
||||
QProcess::connect(vpn_process, &QProcess::stateChanged, this, [=](QProcess::ProcessState state) {
|
||||
@ -1833,7 +1800,7 @@ bool MainWindow::StartVPNProcess() {
|
||||
//
|
||||
vpn_process->setProcessChannelMode(QProcess::ForwardedChannels);
|
||||
#ifdef Q_OS_MACOS
|
||||
vpn_process->start("osascript", {"-e", QString("do shell script \"%1\" with administrator privileges")
|
||||
vpn_process->start("osascript", {"-e", QStringLiteral("do shell script \"%1\" with administrator privileges")
|
||||
.arg("bash " + scriptPath)});
|
||||
#else
|
||||
vpn_process->start("pkexec", {"bash", scriptPath});
|
||||
@ -1856,7 +1823,7 @@ bool MainWindow::StopVPNProcess(bool unconditional) {
|
||||
#else
|
||||
QProcess p;
|
||||
#ifdef Q_OS_MACOS
|
||||
p.start("osascript", {"-e", QString("do shell script \"%1\" with administrator privileges")
|
||||
p.start("osascript", {"-e", QStringLiteral("do shell script \"%1\" with administrator privileges")
|
||||
.arg("pkill -2 -U 0 nekobox_core")});
|
||||
#else
|
||||
if (unconditional) {
|
||||
|
||||
@ -185,7 +185,7 @@ private:
|
||||
|
||||
static void setup_grpc();
|
||||
|
||||
void speedtest_current_group(int mode);
|
||||
void speedtest_current_group(int mode, bool test_group);
|
||||
|
||||
void speedtest_current();
|
||||
|
||||
|
||||
@ -205,6 +205,19 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="toolButton_url_test">
|
||||
<property name="text">
|
||||
<string>URL Test</string>
|
||||
</property>
|
||||
<property name="popupMode">
|
||||
<enum>QToolButton::InstantPopup</enum>
|
||||
</property>
|
||||
<property name="toolButtonStyle">
|
||||
<enum>Qt::ToolButtonTextUnderIcon</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
@ -665,7 +678,7 @@
|
||||
<string notr="true">Tcp Ping</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string notr="true">Ctrl+Shift+T</string>
|
||||
<string notr="true">Ctrl+Alt+T</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="menu_url_test">
|
||||
@ -673,7 +686,7 @@
|
||||
<string notr="true">Url Test</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string notr="true">Ctrl+Shift+U</string>
|
||||
<string notr="true">Ctrl+Alt+U</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="menu_clear_test_result">
|
||||
@ -681,7 +694,7 @@
|
||||
<string>Clear Test Result</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string notr="true">Ctrl+Shift+C</string>
|
||||
<string notr="true">Ctrl+Alt+C</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="menu_export_config">
|
||||
@ -726,7 +739,7 @@
|
||||
<string>Remove Duplicates</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string notr="true">Ctrl+Shift+D</string>
|
||||
<string notr="true">Ctrl+Alt+D</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionfake">
|
||||
@ -774,7 +787,7 @@
|
||||
<string>Remove Unavailable</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string notr="true">Ctrl+Shift+R</string>
|
||||
<string notr="true">Ctrl+Alt+R</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="menu_full_test">
|
||||
@ -782,7 +795,7 @@
|
||||
<string>Full Test</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string notr="true">Ctrl+Shift+F</string>
|
||||
<string notr="true">Ctrl+Alt+F</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="menu_hotkey_settings">
|
||||
@ -803,7 +816,7 @@
|
||||
<string>Copy links of selected (Neko Links)</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string notr="true">Ctrl+Alt+C</string>
|
||||
<string notr="true">Ctrl+N</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionfake_2">
|
||||
@ -859,7 +872,7 @@
|
||||
<string>Resolve domain</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string notr="true">Ctrl+Shift+I</string>
|
||||
<string notr="true">Ctrl+Alt+I</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="menu_vpn_settings">
|
||||
@ -902,6 +915,9 @@
|
||||
<property name="text">
|
||||
<string>Stop Testing</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string notr="true">Ctrl+Alt+S</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
|
||||
@ -58,8 +58,14 @@ void MainWindow::setup_grpc() {
|
||||
inline bool speedtesting = false;
|
||||
inline QList<QThread *> speedtesting_threads = {};
|
||||
|
||||
void MainWindow::speedtest_current_group(int mode) {
|
||||
void MainWindow::speedtest_current_group(int mode, bool test_group) {
|
||||
if (speedtesting) {
|
||||
MessageBoxWarning(software_name, QObject::tr("The last speed test did not exit completely, please wait. If it persists, please restart the program."));
|
||||
return;
|
||||
}
|
||||
|
||||
auto profiles = get_selected_or_group();
|
||||
if (test_group) profiles = NekoGui::profileManager->CurrentGroup()->ProfilesWithOrder();
|
||||
if (profiles.isEmpty()) return;
|
||||
auto group = NekoGui::profileManager->CurrentGroup();
|
||||
if (group->archive) return;
|
||||
@ -75,11 +81,6 @@ void MainWindow::speedtest_current_group(int mode) {
|
||||
}
|
||||
|
||||
#ifndef NKR_NO_GRPC
|
||||
if (speedtesting) {
|
||||
MessageBoxWarning(software_name, "The last speed test did not exit completely, please wait. If it persists, please restart the program.");
|
||||
return;
|
||||
}
|
||||
|
||||
QStringList full_test_flags;
|
||||
if (mode == libcore::FullTest) {
|
||||
auto w = new QDialog(this);
|
||||
@ -150,7 +151,7 @@ void MainWindow::speedtest_current_group(int mode) {
|
||||
//
|
||||
libcore::TestReq req;
|
||||
req.set_mode((libcore::TestMode) mode);
|
||||
req.set_timeout(5000);
|
||||
req.set_timeout(10 * 1000);
|
||||
req.set_url(NekoGui::dataStore->test_latency_url.toStdString());
|
||||
|
||||
//
|
||||
@ -181,7 +182,7 @@ void MainWindow::speedtest_current_group(int mode) {
|
||||
}
|
||||
//
|
||||
auto config = new libcore::LoadConfigReq;
|
||||
config->set_core_config(QJsonObject2QString(c->coreConfig, true).toStdString());
|
||||
config->set_core_config(QJsonObject2QString(c->coreConfig, false).toStdString());
|
||||
req.set_allocated_config(config);
|
||||
req.set_in_address(profile->bean->serverAddress.toStdString());
|
||||
|
||||
@ -238,6 +239,7 @@ void MainWindow::speedtest_current_group(int mode) {
|
||||
lock_return.lock();
|
||||
lock_return.unlock();
|
||||
speedtesting = false;
|
||||
MW_show_log(QObject::tr("Speedtest finished."));
|
||||
});
|
||||
#endif
|
||||
}
|
||||
@ -250,7 +252,7 @@ void MainWindow::speedtest_current() {
|
||||
runOnNewThread([=] {
|
||||
libcore::TestReq req;
|
||||
req.set_mode(libcore::UrlTest);
|
||||
req.set_timeout(5000);
|
||||
req.set_timeout(10 * 1000);
|
||||
req.set_url(NekoGui::dataStore->test_latency_url.toStdString());
|
||||
|
||||
bool rpcOK;
|
||||
@ -262,12 +264,12 @@ void MainWindow::speedtest_current() {
|
||||
|
||||
runOnUiThread([=] {
|
||||
if (!result.error().empty()) {
|
||||
MW_show_log(QString("UrlTest error: %1").arg(result.error().c_str()));
|
||||
MW_show_log(QStringLiteral("UrlTest error: %1").arg(result.error().c_str()));
|
||||
}
|
||||
if (latency <= 0) {
|
||||
ui->label_running->setText(tr("Test Result") + ": " + tr("Unavailable"));
|
||||
} else if (latency > 0) {
|
||||
ui->label_running->setText(tr("Test Result") + ": " + QString("%1 ms").arg(latency));
|
||||
ui->label_running->setText(tr("Test Result") + ": " + QStringLiteral("%1 ms").arg(latency));
|
||||
}
|
||||
});
|
||||
});
|
||||
@ -306,7 +308,7 @@ void MainWindow::neko_start(int _id) {
|
||||
auto neko_start_stage2 = [=] {
|
||||
#ifndef NKR_NO_GRPC
|
||||
libcore::LoadConfigReq req;
|
||||
req.set_core_config(QJsonObject2QString(result->coreConfig, true).toStdString());
|
||||
req.set_core_config(QJsonObject2QString(result->coreConfig, false).toStdString());
|
||||
req.set_enable_nekoray_connections(NekoGui::dataStore->connection_statistics);
|
||||
if (NekoGui::dataStore->traffic_loop_interval > 0) {
|
||||
req.add_stats_outbounds("proxy");
|
||||
|
||||
Loading…
Reference in New Issue
Block a user