Compare commits

..

8 Commits

Author SHA1 Message Date
dranik
0b51b10bbf fixed qml 2026-05-26 01:03:27 +03:00
dranik
016e7cd0ba fixed dd secret 2026-05-26 00:45:04 +03:00
dranik
d8fefe74f5 fixed reload loader 2026-05-26 00:21:04 +03:00
dranik
fdf3bd53cf fixed loader 2026-05-26 00:10:46 +03:00
dranik
7796748be4 fixed button back 2026-05-25 14:20:55 +03:00
dranik
356e6e2872 fix mtproxy/telemt 'base secret' 2026-05-25 13:40:05 +03:00
dranik
74cd8f3a36 fixed remove base secret 2026-05-25 13:17:19 +03:00
dranik
fc6f7dd83d fix color & fix enabled 2026-05-25 12:49:42 +03:00
9 changed files with 407 additions and 258 deletions

View File

@@ -22,7 +22,6 @@ jobs:
recipes:
- 'recipes/**'
- 'conanfile.py'
- '.github/workflows/deploy.yml'
Bake-Prebuilts-Linux:
runs-on: ubuntu-latest
@@ -540,17 +539,13 @@ jobs:
# ------------------------------------------------------
Bake-Prebuilts-MacOS-NE:
runs-on: macos-latest
needs: Detect-Changes
if: needs.Detect-Changes.outputs.recipes_changed == 'true'
strategy:
matrix:
xcode-version: [16.2, 16.4, 26.4]
include:
- xcode-version: 26.4
os: macos-26
runs-on: ${{ matrix.os || 'macos-latest' }}
xcode-version: [16.2, 16.4]
steps:
- uses: actions/checkout@v4

View File

@@ -119,9 +119,14 @@ ErrorCode InstallController::setupContainer(const ServerCredentials &credentials
return e;
qDebug().noquote() << "InstallController::setupContainer prepareHostWorker finished";
amnezia::ScriptVars removeContainerVars =
amnezia::genBaseVars(credentials, container, QString(), QString());
if (!isUpdate) {
removeContainerVars.append({ { "$REMOVE_CONTAINER_DATA", QStringLiteral("1") } });
}
sshSession.runScript(credentials,
sshSession.replaceVars(amnezia::scriptData(SharedScriptType::remove_container),
amnezia::genBaseVars(credentials, container, QString(), QString())));
sshSession.replaceVars(amnezia::scriptData(SharedScriptType::remove_container),
removeContainerVars));
qDebug().noquote() << "InstallController::setupContainer removeContainer finished";
qDebug().noquote() << "buildContainerWorker start";
@@ -942,10 +947,12 @@ ErrorCode InstallController::removeContainer(const QString &serverId, DockerCont
return ErrorCode::InternalError;
}
SshSession sshSession(this);
amnezia::ScriptVars removeContainerVars =
amnezia::genBaseVars(credentials, container, QString(), QString());
removeContainerVars.append({ { "$REMOVE_CONTAINER_DATA", QStringLiteral("1") } });
ErrorCode errorCode = sshSession.runScript(
credentials,
sshSession.replaceVars(amnezia::scriptData(SharedScriptType::remove_container),
amnezia::genBaseVars(credentials, container, QString(), QString())));
sshSession.replaceVars(amnezia::scriptData(SharedScriptType::remove_container), removeContainerVars));
if (errorCode == ErrorCode::NoError) {
QMap<DockerContainer, ContainerConfig> containers = adminConfig->containers;

View File

@@ -295,6 +295,8 @@ amnezia::ScriptVars amnezia::genMtProxyVars(const ContainerConfig &containerConf
vars.append({{"$MTPROXY_PORT", c.port.isEmpty() ? QString(protocols::mtProxy::defaultPort) : c.port}});
vars.append({{"$MTPROXY_SECRET", c.secret}});
vars.append({{"$MTPROXY_REGENERATE_SECRET",
c.secret.isEmpty() ? QStringLiteral("1") : QStringLiteral("0")}});
vars.append({{"$MTPROXY_TAG", c.tag}});
vars.append({{"$MTPROXY_TRANSPORT_MODE",
c.transportMode.isEmpty() ? QString(protocols::mtProxy::transportModeStandard)
@@ -350,6 +352,8 @@ amnezia::ScriptVars amnezia::genTelemtVars(const ContainerConfig &containerConfi
vars.append({ { "$TELEMT_TOML_TLS", faketls ? QLatin1String("true") : QLatin1String("false") } });
vars.append({ { "$TELEMT_PORT", c.port.isEmpty() ? QString(protocols::telemt::defaultPort) : c.port } });
vars.append({ { "$TELEMT_SECRET", c.secret } });
vars.append({ { "$TELEMT_REGENERATE_SECRET",
c.secret.isEmpty() ? QStringLiteral("1") : QStringLiteral("0") } });
vars.append({ { "$TELEMT_TAG", c.tag } });
QString tlsDomain = c.tlsDomain;
if (tlsDomain.isEmpty()) {

View File

@@ -4,8 +4,10 @@
curl -s https://core.telegram.org/getProxySecret -o /data/proxy-secret
curl -s https://core.telegram.org/getProxyConfig -o /data/proxy-multi.conf
# Determine secret: env var -> saved file -> generate new
if [ -n "$MTPROXY_SECRET" ]; then
# Determine secret: regenerate (fresh install) -> env var -> saved file -> generate new
if [ "$MTPROXY_REGENERATE_SECRET" = "1" ]; then
SECRET=$(openssl rand -hex 16)
elif [ -n "$MTPROXY_SECRET" ]; then
SECRET="$MTPROXY_SECRET"
elif [ -f /data/secret ]; then
SECRET=$(cat /data/secret)

View File

@@ -1,3 +1,4 @@
sudo docker stop $CONTAINER_NAME;\
sudo docker rm -fv $CONTAINER_NAME;\
sudo docker rmi $CONTAINER_NAME
sudo docker rmi $CONTAINER_NAME;\
test "$REMOVE_CONTAINER_DATA" = "1" && sudo docker volume rm -f ${CONTAINER_NAME}-data 2>/dev/null || true

View File

@@ -4,8 +4,10 @@
echo "[*] Amnezia Telemt: configure script start"
mkdir -p /data/tlsfront
# Secret: substituted $TELEMT_SECRET -> saved file -> openssl (same rules as MTProxy configure)
if [ -n "$TELEMT_SECRET" ]; then
# Secret: regenerate (fresh install) -> env var -> saved file -> openssl
if [ "$TELEMT_REGENERATE_SECRET" = "1" ]; then
SECRET=$(openssl rand -hex 16)
elif [ -n "$TELEMT_SECRET" ]; then
SECRET="$TELEMT_SECRET"
elif [ -f /data/secret ]; then
SECRET=$(cat /data/secret)

View File

@@ -47,10 +47,10 @@ ListViewType {
PageController.goToPage(PageEnum.PageServiceDnsSettings)
} else if (isMtProxy) {
MtProxyConfigModel.updateModel(config)
PageController.goToPage(PageEnum.PageServiceMtProxySettings)
PageController.goToPage(PageEnum.PageServiceMtProxySettings, false)
} else if (isTelemt) {
TelemtConfigModel.updateModel(config)
PageController.goToPage(PageEnum.PageServiceTelemtSettings)
PageController.goToPage(PageEnum.PageServiceTelemtSettings, false)
} else {
InstallController.updateProtocols(ServersUiController.getServerId(ServersUiController.processedServerIndex), containerIndex)
PageController.goToPage(PageEnum.PageSettingsServerProtocol)

View File

@@ -20,15 +20,10 @@ import "../Components"
PageType {
id: root
Rectangle {
anchors.fill: parent
z: -1
color: AmneziaStyle.color.onyxBlack
}
property int containerStatus: 1
property bool isUpdating: false
property bool isCheckingStatus: false
property bool isFetchingSecret: false
property bool previousEnabled: true
property int previousContainerStatus: 1
@@ -50,7 +45,7 @@ PageType {
onSavedTransportModeChanged: {
if (savedTransportMode === "faketls") {
root.syncedSecretTabIndex = 2
root.syncedSecretTabIndex = 1
} else if (savedTransportMode !== "") {
root.syncedSecretTabIndex = 0
}
@@ -68,9 +63,96 @@ PageType {
readonly property bool mtProxyNetworkBlocked: !NetworkReachabilityController.hasInternetAccess
readonly property bool navigationBlockedWhileBusy: isUpdating || diagLoading
property bool remoteOperationBusy: false
readonly property bool operationInProgress: isCheckingStatus || isFetchingSecret || isUpdating || diagLoading
readonly property bool pageBusy: operationInProgress || remoteOperationBusy
readonly property bool navigationBlockedWhileBusy: pageBusy
property bool pageOpenHandled: false
property bool busyIndicatorShown: false
function syncPageBusyIndicator() {
if (!root.pageOpenHandled) {
return
}
var wantBusy = root.pageBusy
if (wantBusy === root.busyIndicatorShown) {
return
}
root.busyIndicatorShown = wantBusy
PageController.showBusyIndicator(wantBusy)
}
onPageBusyChanged: syncPageBusyIndicator()
function mtProxyDomainToHex(domain) {
var hex = ""
for (var i = 0; i < domain.length; i++) {
var code = domain.charCodeAt(i).toString(16)
hex += (code.length < 2 ? "0" : "") + code
}
return hex
}
function mtProxyClientSecret(baseHex32, mode, tlsDomain) {
if (baseHex32 === "") {
return ""
}
if (mode === "faketls") {
return "ee" + baseHex32 + mtProxyDomainToHex(tlsDomain)
}
return "dd" + baseHex32
}
function mtProxyClientSecretForTabIndex(baseHex32, tabIndex, tlsDomain, defaultTlsDomain) {
var domain = tlsDomain !== "" ? tlsDomain : defaultTlsDomain
if (tabIndex === 1) {
return mtProxyClientSecret(baseHex32, "faketls", domain)
}
return mtProxyClientSecret(baseHex32, "standard", domain)
}
property bool containerStatusRefreshCallPending: false
function mtProxyRequestContainerStatusRefresh() {
if (!NetworkReachabilityController.hasInternetAccess) {
isCheckingStatus = false
syncPageBusyIndicator()
return
}
isCheckingStatus = true
syncPageBusyIndicator()
InstallController.refreshContainerStatus(ServersUiController.getServerId(ServersUiController.processedServerIndex), ServersUiController.processedContainerIndex)
}
function mtProxyScheduleContainerStatusRefresh() {
if (containerStatusRefreshCallPending) {
return
}
containerStatusRefreshCallPending = true
Qt.callLater(function () {
containerStatusRefreshCallPending = false
root.mtProxyRequestContainerStatusRefresh()
})
}
function mtProxyOnPageShown() {
if (root.pageOpenHandled) {
return
}
root.pageOpenHandled = true
PageController.disableControls(navigationBlockedWhileBusy)
if (!NetworkReachabilityController.hasInternetAccess) {
isCheckingStatus = false
} else {
isCheckingStatus = true
}
syncPageBusyIndicator()
root.mtProxyScheduleContainerStatusRefresh()
}
// Hex values that exist in last loaded / last successfully saved config — show link panel only for these.
property var mtProxyPersistedAdditionalHex: []
function mtProxyRefreshPersistedAdditionalSecrets() {
@@ -92,11 +174,8 @@ PageType {
return false
}
// Rejects garbage like "123123123123"; only dotted IPv4 shape (≤3 digits per octet, ≤4 octets).
readonly property var natIpv4InputFormat: /^(\d{1,3}\.){0,3}\d{0,3}$/
// Defer SSH/updateContainer so QML control handlers return before nested event loops run;
// avoids "Object destroyed while one of its QML signal handlers is in progress".
function mtProxyScheduleUpdate(closePage) {
var cp = closePage === undefined ? false : closePage
Qt.callLater(function () {
@@ -104,7 +183,6 @@ PageType {
})
}
// Optional IPv4: show invalid while typing only when the string looks complete (four octets), so partial entry is not nagged.
function natIpv4FieldShowInvalidError(text) {
var t = text ? String(text).replace(/^\s+|\s+$/g, '') : ""
if (t === "")
@@ -167,15 +245,9 @@ PageType {
root.mtProxyRefreshPersistedAdditionalSecrets()
})
if (!NetworkReachabilityController.hasInternetAccess) {
isCheckingStatus = false
return
}
isCheckingStatus = true
InstallController.refreshContainerStatus(ServersUiController.getServerId(ServersUiController.processedServerIndex), ServersUiController.processedContainerIndex)
Qt.callLater(root.mtProxyOnPageShown)
}
// Block back navigation and Escape (via PageStart.isControlsDisabled) while SSH/update or diagnostics refresh runs.
onNavigationBlockedWhileBusyChanged: {
if (root.visible) {
PageController.disableControls(navigationBlockedWhileBusy)
@@ -184,10 +256,16 @@ PageType {
onVisibleChanged: {
if (!visible) {
root.pageOpenHandled = false
containerStatusRefreshCallPending = false
isCheckingStatus = false
isFetchingSecret = false
busyIndicatorShown = false
PageController.disableControls(false)
PageController.showBusyIndicator(false)
diagLoading = false
} else {
PageController.disableControls(navigationBlockedWhileBusy)
root.mtProxyOnPageShown()
}
}
@@ -199,8 +277,7 @@ PageType {
return
}
if (NetworkReachabilityController.hasInternetAccess) {
isCheckingStatus = true
InstallController.refreshContainerStatus(ServersUiController.getServerId(ServersUiController.processedServerIndex), ServersUiController.processedContainerIndex)
root.mtProxyScheduleContainerStatusRefresh()
}
}
}
@@ -208,10 +285,15 @@ PageType {
Connections {
target: InstallController
function onServerIsBusy(busy) {
remoteOperationBusy = busy
}
function onUpdateContainerFinished(message, closePage) {
if (!root.visible) {
isUpdating = false
isCheckingStatus = false
isFetchingSecret = false
return
}
isUpdating = false
@@ -227,9 +309,11 @@ PageType {
if (!root.visible) {
isUpdating = false
isCheckingStatus = false
isFetchingSecret = false
return
}
isUpdating = false
isFetchingSecret = false
containerStatus = previousContainerStatus
MtProxyConfigModel.setEnabled(previousEnabled)
MtProxyConfigModel.setPort(previousPort)
@@ -254,6 +338,7 @@ PageType {
}
if (enabled && pendingUpdateAfterEnable) {
pendingUpdateAfterEnable = false
isUpdating = true
root.mtProxyScheduleUpdate(false)
return
}
@@ -266,9 +351,9 @@ PageType {
function onContainerStatusRefreshed(status) {
if (!root.visible) {
isCheckingStatus = false
isFetchingSecret = false
return
}
isCheckingStatus = false
containerStatus = status
root.savedTransportMode = MtProxyConfigModel.getTransportMode()
@@ -276,10 +361,17 @@ PageType {
root.savedPublicHost = MtProxyConfigModel.getPublicHost()
if (status === 1) {
MtProxyConfigModel.setEnabled(true)
isFetchingSecret = true
isCheckingStatus = false
InstallController.fetchContainerSecret(ServersUiController.getServerId(ServersUiController.processedServerIndex), ServersUiController.processedContainerIndex)
} else if (status === 2) {
MtProxyConfigModel.setEnabled(false)
} else {
isFetchingSecret = false
isCheckingStatus = false
if (status === 2) {
MtProxyConfigModel.setEnabled(false)
}
}
syncPageBusyIndicator()
}
function onContainerDiagnosticsRefreshed(portReachable, upstreamReachable, clientsConnected, lastConfigRefresh, statsEndpoint) {
@@ -296,20 +388,35 @@ PageType {
function onContainerSecretFetched(secret) {
if (!root.visible) {
isFetchingSecret = false
return
}
isFetchingSecret = false
syncPageBusyIndicator()
MtProxyConfigModel.validateAndSetSecret(secret)
}
}
Item {
id: contentLayer
anchors.fill: parent
enabled: !root.pageBusy
BackButtonType {
id: backButton
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: 20 + SettingsController.safeAreaTopMargin
anchors.topMargin: 20 + PageController.safeAreaTopMargin
onFocusChanged: {
if (this.activeFocus) connectionListView.positionViewAtBeginning()
if (this.activeFocus) {
if (mainTabBar.currentIndex === 0) {
connectionListView.positionViewAtBeginning()
} else {
settingsListView.positionViewAtBeginning()
}
}
}
}
@@ -318,20 +425,20 @@ PageType {
anchors.top: backButton.bottom
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: 8
spacing: 0
BaseHeaderType {
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
Layout.topMargin: 8
Layout.bottomMargin: 24
headerText: qsTr("MTProxy settings")
}
LabelWithButtonType {
Layout.fillWidth: true
Layout.leftMargin: 0
Layout.leftMargin: 16
Layout.rightMargin: 16
text: qsTr("Read more about this settings")
textColor: AmneziaStyle.color.goldenApricot
@@ -340,35 +447,49 @@ PageType {
}
}
TabBar {
id: mainTabBar
CaptionTextType {
Layout.fillWidth: true
Layout.topMargin: 4
Layout.leftMargin: 16
Layout.rightMargin: 16
Layout.topMargin: 8
visible: root.mtProxyNetworkBlocked
text: qsTr("No internet connection. Connect to the internet to change MTProxy settings.")
color: AmneziaStyle.color.mutedGray
wrapMode: Text.WordWrap
font.pixelSize: 14
}
}
background: Rectangle {
color: AmneziaStyle.color.transparent
Rectangle {
width: parent.width
height: 1
anchors.bottom: parent.bottom
color: AmneziaStyle.color.slateGray
}
}
TabBar {
id: mainTabBar
anchors.top: pageHeader.bottom
anchors.left: parent.left
anchors.right: parent.right
width: parent.width
TabButtonType {
text: qsTr("Connection")
isSelected: mainTabBar.currentIndex === 0
}
TabButtonType {
text: qsTr("Settings")
isSelected: mainTabBar.currentIndex === 1
background: Rectangle {
color: AmneziaStyle.color.transparent
Rectangle {
width: parent.width
height: 1
anchors.bottom: parent.bottom
color: AmneziaStyle.color.slateGray
}
}
TabButtonType {
text: qsTr("Connection")
isSelected: mainTabBar.currentIndex === 0
}
TabButtonType {
text: qsTr("Settings")
isSelected: mainTabBar.currentIndex === 1
}
}
StackLayout {
id: tabContent
anchors.top: pageHeader.bottom
anchors.top: mainTabBar.bottom
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
@@ -382,35 +503,11 @@ PageType {
width: connectionListView.width
spacing: 0
function domainToHex(domain) {
var hex = ""
for (var i = 0; i < domain.length; i++) {
var code = domain.charCodeAt(i).toString(16)
hex += (code.length < 2 ? "0" : "") + code
}
return hex
}
function secretForMode(mode) {
if (mode === "faketls") {
var domain = root.savedTlsDomain !== "" ? root.savedTlsDomain : MtProxyConfigModel.defaultTlsDomain()
return "ee" + secret + domainToHex(domain)
} else if (mode === "padded") {
return "dd" + secret
}
return secret
}
property int secretTabIndex: root.syncedSecretTabIndex
function activeSecret() {
if (root.syncedSecretTabIndex === 0) {
return secretForMode("standard")
}
if (root.syncedSecretTabIndex === 1) {
return secretForMode("padded")
}
return secretForMode("faketls")
return root.mtProxyClientSecretForTabIndex(secret, root.syncedSecretTabIndex,
root.savedTlsDomain, MtProxyConfigModel.defaultTlsDomain())
}
function effectiveSecret() {
@@ -754,33 +851,9 @@ PageType {
width: settingsListView.width
spacing: 0
function mtProxyDomainToHex(domain) {
var hex = ""
for (var i = 0; i < domain.length; i++) {
var code = domain.charCodeAt(i).toString(16)
hex += (code.length < 2 ? "0" : "") + code
}
return hex
}
function mtProxySecretForBaseHex(baseHex, mode) {
if (mode === "faketls") {
var domain = root.savedTlsDomain !== "" ? root.savedTlsDomain : MtProxyConfigModel.defaultTlsDomain()
return "ee" + baseHex + mtProxyDomainToHex(domain)
} else if (mode === "padded") {
return "dd" + baseHex
}
return baseHex
}
function mtProxyActiveSecretForBaseHex(baseHex) {
if (root.syncedSecretTabIndex === 0) {
return mtProxySecretForBaseHex(baseHex, "standard")
}
if (root.syncedSecretTabIndex === 1) {
return mtProxySecretForBaseHex(baseHex, "padded")
}
return mtProxySecretForBaseHex(baseHex, "faketls")
return root.mtProxyClientSecretForTabIndex(baseHex, root.syncedSecretTabIndex,
root.savedTlsDomain, MtProxyConfigModel.defaultTlsDomain())
}
function mtProxyEffectiveHostForLinks() {
@@ -804,7 +877,7 @@ PageType {
Layout.bottomMargin: 16
text: qsTr("Enable MTProxy")
checked: isEnabled
enabled: !isCheckingStatus && containerStatus !== 0 && containerStatus !== 3 && !isUpdating
enabled: containerStatus !== 0 && containerStatus !== 3 && !root.pageBusy
&& !root.mtProxyNetworkBlocked
onToggled: function () {
if (checked !== isEnabled) {
@@ -843,13 +916,14 @@ PageType {
CaptionTextType {
Layout.fillWidth: true
text: secret !== "" ? secret : qsTr("Not generated")
text: secret !== "" ? mtProxyActiveSecretForBaseHex(secret) : qsTr("Not generated")
color: secret !== "" ? AmneziaStyle.color.paleGray : AmneziaStyle.color.mutedGray
elide: Text.ElideMiddle
wrapMode: Text.WrapAnywhere
font.pixelSize: 14
}
ImageButtonType {
Layout.alignment: Qt.AlignTop
implicitWidth: 36
implicitHeight: 36
hoverEnabled: true
@@ -1098,6 +1172,7 @@ PageType {
clickedFunction: function () {
transportMode = (index === 0) ? "standard" : "faketls"
MtProxyConfigModel.setTransportMode(transportMode)
root.syncedSecretTabIndex = transportMode === "faketls" ? 1 : 0
transportModeDropDown.closeTriggered()
}
}
@@ -1286,6 +1361,9 @@ PageType {
imageColor: AmneziaStyle.color.vibrantRed
onClicked: {
MtProxyConfigModel.removeAdditionalSecret(index)
if (containerStatus === 1) {
root.mtProxyScheduleUpdate(false)
}
}
}
}
@@ -1852,34 +1930,5 @@ PageType {
}
}
Rectangle {
anchors.fill: parent
visible: isCheckingStatus || isUpdating || root.mtProxyNetworkBlocked
color: AmneziaStyle.color.midnightBlack
opacity: 0.6
z: 1
MouseArea {
anchors.fill: parent
}
BusyIndicator {
anchors.centerIn: parent
visible: isCheckingStatus || isUpdating
running: isCheckingStatus || isUpdating
width: 48
height: 48
}
CaptionTextType {
anchors.left: parent.left
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
anchors.leftMargin: 24
anchors.rightMargin: 24
visible: root.mtProxyNetworkBlocked && !isCheckingStatus && !isUpdating
horizontalAlignment: Text.AlignHCenter
text: qsTr("No internet connection. Connect to the internet to change MTProxy settings.")
color: AmneziaStyle.color.paleGray
wrapMode: Text.WordWrap
font.pixelSize: 14
}
}
}

View File

@@ -18,15 +18,10 @@ import "../Components"
PageType {
id: root
Rectangle {
anchors.fill: parent
z: -1
color: AmneziaStyle.color.onyxBlack
}
property int containerStatus: 1
property bool isUpdating: false
property bool isCheckingStatus: false
property bool isFetchingSecret: false
property bool previousEnabled: true
property int previousContainerStatus: 1
@@ -40,6 +35,7 @@ PageType {
property bool previousNatEnabled: false
property string previousNatInternalIp: ""
property string previousNatExternalIp: ""
property string previousSecret: ""
property string savedTransportMode: ""
property string savedTlsDomain: ""
@@ -47,7 +43,7 @@ PageType {
onSavedTransportModeChanged: {
if (savedTransportMode === "faketls") {
root.syncedSecretTabIndex = 2
root.syncedSecretTabIndex = 1
} else if (savedTransportMode !== "") {
root.syncedSecretTabIndex = 0
}
@@ -64,9 +60,97 @@ PageType {
property string diagStatsEndpoint: ""
readonly property bool telemtNetworkBlocked: !NetworkReachabilityController.hasInternetAccess
readonly property bool navigationBlockedWhileBusy: isUpdating || diagLoading
// Defer SSH/updateContainer so QML control handlers return before nested event loops run.
property bool remoteOperationBusy: false
readonly property bool operationInProgress: isCheckingStatus || isFetchingSecret || isUpdating || diagLoading
readonly property bool pageBusy: operationInProgress || remoteOperationBusy
readonly property bool navigationBlockedWhileBusy: pageBusy
property bool pageOpenHandled: false
property bool busyIndicatorShown: false
function syncPageBusyIndicator() {
if (!root.pageOpenHandled) {
return
}
var wantBusy = root.pageBusy
if (wantBusy === root.busyIndicatorShown) {
return
}
root.busyIndicatorShown = wantBusy
PageController.showBusyIndicator(wantBusy)
}
onPageBusyChanged: syncPageBusyIndicator()
function telemtDomainToHex(domain) {
var hex = ""
for (var i = 0; i < domain.length; i++) {
var code = domain.charCodeAt(i).toString(16)
hex += (code.length < 2 ? "0" : "") + code
}
return hex
}
function telemtClientSecret(baseHex32, mode, tlsDomain) {
if (baseHex32 === "") {
return ""
}
if (mode === "faketls") {
return "ee" + baseHex32 + telemtDomainToHex(tlsDomain)
}
return "dd" + baseHex32
}
function telemtClientSecretForTabIndex(baseHex32, tabIndex, tlsDomain, defaultTlsDomain) {
var domain = tlsDomain !== "" ? tlsDomain : defaultTlsDomain
if (tabIndex === 1) {
return telemtClientSecret(baseHex32, "faketls", domain)
}
return telemtClientSecret(baseHex32, "standard", domain)
}
property bool containerStatusRefreshCallPending: false
function telemtRequestContainerStatusRefresh() {
if (!NetworkReachabilityController.hasInternetAccess) {
isCheckingStatus = false
syncPageBusyIndicator()
return
}
isCheckingStatus = true
syncPageBusyIndicator()
InstallController.refreshContainerStatus(ServersUiController.getServerId(ServersUiController.processedServerIndex), ServersUiController.processedContainerIndex)
}
function telemtScheduleContainerStatusRefresh() {
if (containerStatusRefreshCallPending) {
return
}
containerStatusRefreshCallPending = true
Qt.callLater(function () {
containerStatusRefreshCallPending = false
root.telemtRequestContainerStatusRefresh()
})
}
function telemtOnPageShown() {
if (root.pageOpenHandled) {
return
}
root.pageOpenHandled = true
PageController.disableControls(navigationBlockedWhileBusy)
if (!NetworkReachabilityController.hasInternetAccess) {
isCheckingStatus = false
} else {
isCheckingStatus = true
}
syncPageBusyIndicator()
root.telemtScheduleContainerStatusRefresh()
}
function telemtScheduleUpdate(closePage) {
var cp = closePage === undefined ? false : closePage
Qt.callLater(function () {
@@ -105,12 +189,7 @@ PageType {
root.savedTlsDomain = TelemtConfigModel.getTlsDomain()
root.savedPublicHost = TelemtConfigModel.getPublicHost()
if (!NetworkReachabilityController.hasInternetAccess) {
isCheckingStatus = false
return
}
isCheckingStatus = true
InstallController.refreshContainerStatus(ServersUiController.getServerId(ServersUiController.processedServerIndex), ServersUiController.processedContainerIndex)
Qt.callLater(root.telemtOnPageShown)
}
onNavigationBlockedWhileBusyChanged: {
@@ -121,10 +200,16 @@ PageType {
onVisibleChanged: {
if (!visible) {
root.pageOpenHandled = false
containerStatusRefreshCallPending = false
isCheckingStatus = false
isFetchingSecret = false
busyIndicatorShown = false
PageController.disableControls(false)
PageController.showBusyIndicator(false)
diagLoading = false
} else {
PageController.disableControls(navigationBlockedWhileBusy)
root.telemtOnPageShown()
}
}
@@ -136,8 +221,7 @@ PageType {
return
}
if (NetworkReachabilityController.hasInternetAccess) {
isCheckingStatus = true
InstallController.refreshContainerStatus(ServersUiController.getServerId(ServersUiController.processedServerIndex), ServersUiController.processedContainerIndex)
root.telemtScheduleContainerStatusRefresh()
}
}
}
@@ -145,10 +229,15 @@ PageType {
Connections {
target: InstallController
function onServerIsBusy(busy) {
remoteOperationBusy = busy
}
function onUpdateContainerFinished(message, closePage) {
if (!root.visible) {
isUpdating = false
isCheckingStatus = false
isFetchingSecret = false
return
}
isUpdating = false
@@ -166,9 +255,11 @@ PageType {
if (!root.visible) {
isUpdating = false
isCheckingStatus = false
isFetchingSecret = false
return
}
isUpdating = false
isFetchingSecret = false
containerStatus = previousContainerStatus
TelemtConfigModel.setEnabled(previousEnabled)
TelemtConfigModel.setPort(previousPort)
@@ -181,6 +272,9 @@ PageType {
TelemtConfigModel.setNatEnabled(previousNatEnabled)
TelemtConfigModel.setNatInternalIp(previousNatInternalIp)
TelemtConfigModel.setNatExternalIp(previousNatExternalIp)
if (previousSecret !== "") {
TelemtConfigModel.setSecret(previousSecret)
}
}
function onSetContainerEnabledFinished(enabled) {
@@ -190,6 +284,7 @@ PageType {
}
if (enabled && pendingUpdateAfterEnable) {
pendingUpdateAfterEnable = false
isUpdating = true
root.telemtScheduleUpdate(false)
return
}
@@ -202,9 +297,9 @@ PageType {
function onContainerStatusRefreshed(status) {
if (!root.visible) {
isCheckingStatus = false
isFetchingSecret = false
return
}
isCheckingStatus = false
containerStatus = status
root.savedTransportMode = TelemtConfigModel.getTransportMode()
@@ -212,10 +307,17 @@ PageType {
root.savedPublicHost = TelemtConfigModel.getPublicHost()
if (status === 1) {
TelemtConfigModel.setEnabled(true)
isFetchingSecret = true
isCheckingStatus = false
InstallController.fetchContainerSecret(ServersUiController.getServerId(ServersUiController.processedServerIndex), ServersUiController.processedContainerIndex)
} else if (status === 2) {
TelemtConfigModel.setEnabled(false)
} else {
isFetchingSecret = false
isCheckingStatus = false
if (status === 2) {
TelemtConfigModel.setEnabled(false)
}
}
syncPageBusyIndicator()
}
function onContainerDiagnosticsRefreshed(portReachable, upstreamReachable, clientsConnected, lastConfigRefresh, statsEndpoint) {
@@ -232,20 +334,35 @@ PageType {
function onContainerSecretFetched(secret) {
if (!root.visible) {
isFetchingSecret = false
return
}
isFetchingSecret = false
syncPageBusyIndicator()
TelemtConfigModel.validateAndSetSecret(secret)
}
}
Item {
id: contentLayer
anchors.fill: parent
enabled: !root.pageBusy
BackButtonType {
id: backButton
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: 20 + SettingsController.safeAreaTopMargin
anchors.topMargin: 20 + PageController.safeAreaTopMargin
onFocusChanged: {
if (this.activeFocus) connectionListView.positionViewAtBeginning()
if (this.activeFocus) {
if (mainTabBar.currentIndex === 0) {
connectionListView.positionViewAtBeginning()
} else {
settingsListView.positionViewAtBeginning()
}
}
}
}
@@ -254,20 +371,20 @@ PageType {
anchors.top: backButton.bottom
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: 8
spacing: 0
BaseHeaderType {
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
Layout.topMargin: 8
Layout.bottomMargin: 24
headerText: qsTr("Telemt settings")
}
LabelWithButtonType {
Layout.fillWidth: true
Layout.leftMargin: 0
Layout.leftMargin: 16
Layout.rightMargin: 16
text: qsTr("Read more about this settings")
textColor: AmneziaStyle.color.goldenApricot
@@ -276,35 +393,49 @@ PageType {
}
}
TabBar {
id: mainTabBar
CaptionTextType {
Layout.fillWidth: true
Layout.topMargin: 4
Layout.leftMargin: 16
Layout.rightMargin: 16
Layout.topMargin: 8
visible: root.telemtNetworkBlocked
text: qsTr("No internet connection. Connect to the internet to change Telemt settings.")
color: AmneziaStyle.color.mutedGray
wrapMode: Text.WordWrap
font.pixelSize: 14
}
}
background: Rectangle {
color: AmneziaStyle.color.transparent
Rectangle {
width: parent.width
height: 1
anchors.bottom: parent.bottom
color: AmneziaStyle.color.slateGray
}
}
TabBar {
id: mainTabBar
anchors.top: pageHeader.bottom
anchors.left: parent.left
anchors.right: parent.right
width: parent.width
TabButtonType {
text: qsTr("Connection")
isSelected: mainTabBar.currentIndex === 0
}
TabButtonType {
text: qsTr("Settings")
isSelected: mainTabBar.currentIndex === 1
background: Rectangle {
color: AmneziaStyle.color.transparent
Rectangle {
width: parent.width
height: 1
anchors.bottom: parent.bottom
color: AmneziaStyle.color.slateGray
}
}
TabButtonType {
text: qsTr("Connection")
isSelected: mainTabBar.currentIndex === 0
}
TabButtonType {
text: qsTr("Settings")
isSelected: mainTabBar.currentIndex === 1
}
}
StackLayout {
id: tabContent
anchors.top: pageHeader.bottom
anchors.top: mainTabBar.bottom
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
@@ -318,36 +449,11 @@ PageType {
width: connectionListView.width
spacing: 0
function domainToHex(domain) {
var hex = ""
for (var i = 0; i < domain.length; i++) {
var code = domain.charCodeAt(i).toString(16)
hex += (code.length < 2 ? "0" : "") + code
}
return hex
}
function secretForMode(mode) {
if (mode === "faketls") {
var domain = root.savedTlsDomain !== "" ? root.savedTlsDomain : TelemtConfigModel.defaultTlsDomain()
return "ee" + secret + domainToHex(domain)
} else if (mode === "padded") {
return "dd" + secret
}
// Telemt default (secure MTProto, not FakeTLS): Telegram proxy links require dd + hex secret
return "dd" + secret
}
property int secretTabIndex: root.syncedSecretTabIndex
function activeSecret() {
if (root.syncedSecretTabIndex === 0) {
return secretForMode("standard")
}
if (root.syncedSecretTabIndex === 1) {
return secretForMode("padded")
}
return secretForMode("faketls")
return root.telemtClientSecretForTabIndex(secret, root.syncedSecretTabIndex,
root.savedTlsDomain, TelemtConfigModel.defaultTlsDomain())
}
function effectiveSecret() {
@@ -690,6 +796,11 @@ PageType {
width: settingsListView.width
spacing: 0
function telemtActiveSecretForBaseHex(baseHex) {
return root.telemtClientSecretForTabIndex(baseHex, root.syncedSecretTabIndex,
root.savedTlsDomain, TelemtConfigModel.defaultTlsDomain())
}
SwitcherType {
id: enableTelemtSwitch
Layout.fillWidth: true
@@ -699,11 +810,13 @@ PageType {
Layout.bottomMargin: 16
text: qsTr("Enable Telemt")
checked: isEnabled
enabled: !isCheckingStatus && containerStatus !== 0 && containerStatus !== 3 && !isUpdating
enabled: containerStatus !== 0 && containerStatus !== 3 && !root.pageBusy
&& !root.telemtNetworkBlocked
onToggled: function () {
if (checked !== isEnabled) {
previousEnabled = isEnabled
previousContainerStatus = containerStatus
root.previousSecret = secret
isEnabled = checked
isUpdating = true
if (checked) {
@@ -736,13 +849,14 @@ PageType {
CaptionTextType {
Layout.fillWidth: true
text: secret !== "" ? secret : qsTr("Not generated")
text: secret !== "" ? telemtActiveSecretForBaseHex(secret) : qsTr("Not generated")
color: secret !== "" ? AmneziaStyle.color.paleGray : AmneziaStyle.color.mutedGray
elide: Text.ElideMiddle
wrapMode: Text.WrapAnywhere
font.pixelSize: 14
}
ImageButtonType {
Layout.alignment: Qt.AlignTop
implicitWidth: 36
implicitHeight: 36
hoverEnabled: true
@@ -750,12 +864,14 @@ PageType {
imageColor: AmneziaStyle.color.paleGray
visible: ServersModel.isProcessedServerHasWriteAccess()
onClicked: {
var secretSnapshot = secret
showQuestionDrawer(
qsTr("Generate new secret?"),
qsTr("All existing connection links will stop working. Users will need new links."),
qsTr("Generate"),
qsTr("Cancel"),
function () {
root.previousSecret = secretSnapshot
if (containerStatus === 1) {
isUpdating = true
TelemtConfigModel.generateSecret()
@@ -926,6 +1042,7 @@ PageType {
clickedFunction: function () {
transportMode = (index === 0) ? "standard" : "faketls"
TelemtConfigModel.setTransportMode(transportMode)
root.syncedSecretTabIndex = transportMode === "faketls" ? 1 : 0
transportModeDropDown.closeTriggered()
}
}
@@ -1406,6 +1523,7 @@ PageType {
previousNatEnabled = natEnabled
previousNatInternalIp = natInternalIp
previousNatExternalIp = natExternalIp
root.previousSecret = secret
isUpdating = true
root.telemtScheduleUpdate(false)
}
@@ -1414,34 +1532,5 @@ PageType {
}
}
Rectangle {
anchors.fill: parent
visible: isCheckingStatus || isUpdating || root.telemtNetworkBlocked
color: AmneziaStyle.color.midnightBlack
opacity: 0.6
z: 1
MouseArea {
anchors.fill: parent
}
BusyIndicator {
anchors.centerIn: parent
visible: isCheckingStatus || isUpdating
running: isCheckingStatus || isUpdating
width: 48
height: 48
}
CaptionTextType {
anchors.left: parent.left
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
anchors.leftMargin: 24
anchors.rightMargin: 24
visible: root.telemtNetworkBlocked && !isCheckingStatus && !isUpdating
horizontalAlignment: Text.AlignHCenter
text: qsTr("No internet connection. Connect to the internet to change Telemt settings.")
color: AmneziaStyle.color.paleGray
wrapMode: Text.WordWrap
font.pixelSize: 14
}
}
}