mirror of
https://github.com/TermoraDev/termora.git
synced 2026-01-16 02:12:58 +08:00
Compare commits
43 Commits
dependabot
...
2.x
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7c6d144a89 | ||
|
|
a5434c5cdf | ||
|
|
68bd883305 | ||
|
|
3fa2659d59 | ||
|
|
3ece32e427 | ||
|
|
dad4d26fd8 | ||
|
|
9b387c71fc | ||
|
|
7ac833b53b | ||
|
|
2327c5fd48 | ||
|
|
bfc63a3983 | ||
|
|
c727925791 | ||
|
|
cae1173180 | ||
|
|
10d2736232 | ||
|
|
97f01b7e3f | ||
|
|
21c7dd7a42 | ||
|
|
bbc64043ed | ||
|
|
79842f4625 | ||
|
|
626b344088 | ||
|
|
5b165ed587 | ||
|
|
d73b3b706e | ||
|
|
2928b35585 | ||
|
|
04bece21ff | ||
|
|
9e2e104baa | ||
|
|
0615378a17 | ||
|
|
013b03f9ef | ||
|
|
026b13ba05 | ||
|
|
6ec526eeeb | ||
|
|
e064bb9bb5 | ||
|
|
1f3fb5e2c0 | ||
|
|
5984f3e856 | ||
|
|
572c381e90 | ||
|
|
7a8ecb06bf | ||
|
|
4c928ac826 | ||
|
|
d07f9ede8c | ||
|
|
21a015bf8c | ||
|
|
71a1f5db4b | ||
|
|
96fd07a6ff | ||
|
|
733e062a7b | ||
|
|
e87a779adc | ||
|
|
9c6aa4dcb6 | ||
|
|
566b087eb1 | ||
|
|
2235e4c2a4 | ||
|
|
3b9d1f277b |
2
.github/workflows/linux.yml
vendored
2
.github/workflows/linux.yml
vendored
@@ -4,7 +4,7 @@ on: [ push, pull_request ]
|
|||||||
|
|
||||||
env:
|
env:
|
||||||
JBR_MAJOR: 21.0.8
|
JBR_MAJOR: 21.0.8
|
||||||
JBR_PATCH: b1138.52
|
JBR_PATCH: b1163.69
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
|
|||||||
4
.github/workflows/osx.yml
vendored
4
.github/workflows/osx.yml
vendored
@@ -9,14 +9,14 @@ env:
|
|||||||
TERMORA_MAC_NOTARY: "${{ startsWith(github.event.head_commit.message, 'release: ') && github.repository == 'TermoraDev/termora' }}"
|
TERMORA_MAC_NOTARY: "${{ startsWith(github.event.head_commit.message, 'release: ') && github.repository == 'TermoraDev/termora' }}"
|
||||||
TERMORA_MAC_NOTARY_KEYCHAIN_PROFILE: ${{ secrets.TERMORA_MAC_NOTARY_KEYCHAIN_PROFILE }}
|
TERMORA_MAC_NOTARY_KEYCHAIN_PROFILE: ${{ secrets.TERMORA_MAC_NOTARY_KEYCHAIN_PROFILE }}
|
||||||
JBR_MAJOR: 21.0.8
|
JBR_MAJOR: 21.0.8
|
||||||
JBR_PATCH: b1138.52
|
JBR_PATCH: b1163.69
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
os: [ macos-15, macos-13 ]
|
os: [ macos-15-intel, macos-latest ]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
|
|||||||
2
.github/workflows/windows.yml
vendored
2
.github/workflows/windows.yml
vendored
@@ -4,7 +4,7 @@ on: [ push, pull_request ]
|
|||||||
|
|
||||||
env:
|
env:
|
||||||
JBR_MAJOR: 21.0.8
|
JBR_MAJOR: 21.0.8
|
||||||
JBR_PATCH: b1138.52
|
JBR_PATCH: b1163.69
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
|
|||||||
@@ -1,50 +1,50 @@
|
|||||||
[versions]
|
[versions]
|
||||||
kotlin = "2.2.20"
|
kotlin = "2.3.0"
|
||||||
slf4j = "2.0.17"
|
slf4j = "2.0.17"
|
||||||
pty4j = "0.13.10"
|
pty4j = "0.13.10"
|
||||||
tinylog = "2.7.0"
|
tinylog = "2.7.0"
|
||||||
kotlinx-coroutines = "1.10.2"
|
kotlinx-coroutines = "1.10.2"
|
||||||
flatlaf = "3.6.2"
|
flatlaf = "3.7"
|
||||||
kotlinx-serialization-json = "1.9.0"
|
kotlinx-serialization-json = "1.9.0"
|
||||||
commons-codec = "1.19.0"
|
commons-codec = "1.20.0"
|
||||||
commons-lang3 = "3.19.0"
|
commons-lang3 = "3.20.0"
|
||||||
commons-csv = "1.14.1"
|
commons-csv = "1.14.1"
|
||||||
commons-net = "3.12.0"
|
commons-net = "3.12.0"
|
||||||
commons-text = "1.14.0"
|
commons-text = "1.15.0"
|
||||||
commons-compress = "1.28.0"
|
commons-compress = "1.28.0"
|
||||||
commons-vfs2 = "2.10.0"
|
commons-vfs2 = "2.10.0"
|
||||||
swingx = "1.6.5-1"
|
swingx = "1.6.5-1"
|
||||||
jgoodies-forms = "1.9.0"
|
jgoodies-forms = "1.9.0"
|
||||||
jfa = "1.2.0"
|
jfa = "1.2.0"
|
||||||
oshi = "6.9.0"
|
oshi = "6.9.1"
|
||||||
versioncompare = "1.4.1"
|
versioncompare = "1.4.1"
|
||||||
jna = "5.18.1"
|
jna = "5.18.1"
|
||||||
jSystemThemeDetector = "3.9.1"
|
jSystemThemeDetector = "3.9.1"
|
||||||
commons-io = "2.20.0"
|
commons-io = "2.21.0"
|
||||||
jbr-api = "17.1.10.1"
|
jbr-api = "17.1.10.1"
|
||||||
hutool = "5.8.40"
|
hutool = "5.8.40"
|
||||||
jsch = "2.27.3"
|
jsch = "2.27.3"
|
||||||
okhttp = "5.2.1"
|
okhttp = "5.3.0"
|
||||||
sshj = "0.39.0"
|
sshj = "0.39.0"
|
||||||
sshd-core = "2.15.0"
|
sshd-core = "2.15.0"
|
||||||
jgit = "7.4.0.202509020913-r"
|
jgit = "7.4.0.202509020913-r"
|
||||||
commonmark = "0.26.0"
|
commonmark = "0.27.0"
|
||||||
jnafilechooser = "1.1.2"
|
jnafilechooser = "1.1.2"
|
||||||
xodus = "2.0.1"
|
xodus = "2.0.1"
|
||||||
bip39 = "1.0.9"
|
bip39 = "1.0.9"
|
||||||
colorpicker = "2.0.1"
|
colorpicker = "2.0.1"
|
||||||
rhino = "1.8.0"
|
rhino = "1.9.0"
|
||||||
delight-rhino-sandbox = "0.0.17"
|
delight-rhino-sandbox = "0.2.1"
|
||||||
testcontainers = "2.0.0"
|
testcontainers = "2.0.3"
|
||||||
mixpanel = "1.5.3"
|
mixpanel = "1.7.0"
|
||||||
jSerialComm = "2.11.2"
|
jSerialComm = "2.11.4"
|
||||||
ini4j = "0.5.5-2"
|
ini4j = "0.5.5-2"
|
||||||
restart4j = "0.0.1"
|
restart4j = "0.0.1"
|
||||||
eddsa = "0.3.0"
|
eddsa = "0.3.0"
|
||||||
exposed = "1.0.0-rc-2"
|
exposed = "1.0.0-rc-4"
|
||||||
h2 = "2.3.232"
|
h2 = "2.3.232"
|
||||||
sqlite = "3.50.3.0"
|
sqlite = "3.50.3.0"
|
||||||
jug = "5.1.1"
|
jug = "5.2.0"
|
||||||
semver4j = "6.0.0"
|
semver4j = "6.0.0"
|
||||||
jsvg = "2.0.0"
|
jsvg = "2.0.0"
|
||||||
dom4j = "2.2.0"
|
dom4j = "2.2.0"
|
||||||
@@ -70,7 +70,7 @@ flatlafextras = { group = "com.formdev", name = "flatlaf-extras", version.ref =
|
|||||||
flatlafswingx = { module = "com.formdev:flatlaf-swingx", version.ref = "flatlaf" }
|
flatlafswingx = { module = "com.formdev:flatlaf-swingx", version.ref = "flatlaf" }
|
||||||
testcontainers-bom = { module = "org.testcontainers:testcontainers-bom", version.ref = "testcontainers" }
|
testcontainers-bom = { module = "org.testcontainers:testcontainers-bom", version.ref = "testcontainers" }
|
||||||
testcontainers = { module = "org.testcontainers:testcontainers" }
|
testcontainers = { module = "org.testcontainers:testcontainers" }
|
||||||
testcontainers-junit-jupiter = { module = "org.testcontainers:junit-jupiter" }
|
testcontainers-junit-jupiter = { module = "org.testcontainers:testcontainers-junit-jupiter" }
|
||||||
swingx = { module = "org.swinglabs.swingx:swingx-all", version.ref = "swingx" }
|
swingx = { module = "org.swinglabs.swingx:swingx-all", version.ref = "swingx" }
|
||||||
jgoodies-forms = { module = "com.jgoodies:jgoodies-forms", version.ref = "jgoodies-forms" }
|
jgoodies-forms = { module = "com.jgoodies:jgoodies-forms", version.ref = "jgoodies-forms" }
|
||||||
jna = { module = "net.java.dev.jna:jna", version.ref = "jna" }
|
jna = { module = "net.java.dev.jna:jna", version.ref = "jna" }
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ project.version = "0.0.4"
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
testImplementation(kotlin("test"))
|
testImplementation(kotlin("test"))
|
||||||
implementation("com.qcloud:cos_api:5.6.257")
|
implementation("com.qcloud:cos_api:5.6.260.1")
|
||||||
compileOnly(project(":"))
|
compileOnly(project(":"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ project.version = "0.0.8"
|
|||||||
dependencies {
|
dependencies {
|
||||||
testImplementation(kotlin("test"))
|
testImplementation(kotlin("test"))
|
||||||
compileOnly(project(":"))
|
compileOnly(project(":"))
|
||||||
implementation("com.fifesoft:rsyntaxtextarea:3.6.0")
|
implementation("com.fifesoft:rsyntaxtextarea:3.6.1")
|
||||||
implementation("com.fifesoft:languagesupport:3.4.0")
|
implementation("com.fifesoft:languagesupport:3.4.0")
|
||||||
implementation("com.fifesoft:autocomplete:3.3.2")
|
implementation("com.fifesoft:autocomplete:3.3.2")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ project.version = "0.0.2"
|
|||||||
dependencies {
|
dependencies {
|
||||||
testImplementation(kotlin("test"))
|
testImplementation(kotlin("test"))
|
||||||
compileOnly(project(":"))
|
compileOnly(project(":"))
|
||||||
implementation("org.apache.commons:commons-pool2:2.12.1")
|
implementation("org.apache.commons:commons-pool2:2.13.0")
|
||||||
testImplementation(project(":"))
|
testImplementation(project(":"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,9 +7,9 @@ project.version = "0.0.8"
|
|||||||
dependencies {
|
dependencies {
|
||||||
testImplementation(kotlin("test"))
|
testImplementation(kotlin("test"))
|
||||||
compileOnly(project(":"))
|
compileOnly(project(":"))
|
||||||
implementation("com.maxmind.geoip2:geoip2:4.4.0")
|
implementation("com.maxmind.geoip2:geoip2:5.0.0")
|
||||||
// https://github.com/hstyi/geolite2
|
// https://github.com/hstyi/geolite2
|
||||||
implementation("com.github.hstyi:geolite2:v1.0-202510200054")
|
implementation("com.github.hstyi:geolite2:v1.0-202510270056")
|
||||||
}
|
}
|
||||||
|
|
||||||
apply(from = "$rootDir/plugins/common.gradle.kts")
|
apply(from = "$rootDir/plugins/common.gradle.kts")
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ project.version = "0.0.5"
|
|||||||
dependencies {
|
dependencies {
|
||||||
testImplementation(kotlin("test"))
|
testImplementation(kotlin("test"))
|
||||||
compileOnly(project(":"))
|
compileOnly(project(":"))
|
||||||
implementation("com.fazecast:jSerialComm:2.11.2")
|
implementation("com.fazecast:jSerialComm:2.11.4")
|
||||||
}
|
}
|
||||||
|
|
||||||
apply(from = "$rootDir/plugins/common.gradle.kts")
|
apply(from = "$rootDir/plugins/common.gradle.kts")
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ plugins {
|
|||||||
alias(libs.plugins.kotlin.jvm)
|
alias(libs.plugins.kotlin.jvm)
|
||||||
}
|
}
|
||||||
|
|
||||||
project.version = "0.0.3"
|
project.version = "0.0.4"
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
testImplementation(kotlin("test"))
|
testImplementation(kotlin("test"))
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ class SMBHostOptionsPane : OptionsPane() {
|
|||||||
sftpDefaultDirectory = sftpOption.defaultDirectoryField.text,
|
sftpDefaultDirectory = sftpOption.defaultDirectoryField.text,
|
||||||
extras = mutableMapOf(
|
extras = mutableMapOf(
|
||||||
"smb.share" to generalOption.shareTextField.text,
|
"smb.share" to generalOption.shareTextField.text,
|
||||||
|
"smb.domain" to generalOption.domainTextField.text,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -66,6 +67,7 @@ class SMBHostOptionsPane : OptionsPane() {
|
|||||||
generalOption.remarkTextArea.text = host.remark
|
generalOption.remarkTextArea.text = host.remark
|
||||||
generalOption.passwordTextField.text = host.authentication.password
|
generalOption.passwordTextField.text = host.authentication.password
|
||||||
generalOption.shareTextField.text = host.options.extras["smb.share"] ?: StringUtils.EMPTY
|
generalOption.shareTextField.text = host.options.extras["smb.share"] ?: StringUtils.EMPTY
|
||||||
|
generalOption.domainTextField.text = host.options.extras["smb.domain"] ?: StringUtils.EMPTY
|
||||||
|
|
||||||
sftpOption.defaultDirectoryField.text = host.options.sftpDefaultDirectory
|
sftpOption.defaultDirectoryField.text = host.options.sftpDefaultDirectory
|
||||||
}
|
}
|
||||||
@@ -114,6 +116,7 @@ class SMBHostOptionsPane : OptionsPane() {
|
|||||||
val nameTextField = OutlineTextField(128)
|
val nameTextField = OutlineTextField(128)
|
||||||
val shareTextField = OutlineTextField(256)
|
val shareTextField = OutlineTextField(256)
|
||||||
val usernameTextField = OutlineComboBox<String>()
|
val usernameTextField = OutlineComboBox<String>()
|
||||||
|
val domainTextField = OutlineTextField(128)
|
||||||
val hostTextField = OutlineTextField(255)
|
val hostTextField = OutlineTextField(255)
|
||||||
val passwordTextField = OutlinePasswordField(255)
|
val passwordTextField = OutlinePasswordField(255)
|
||||||
val remarkTextArea = FixedLengthTextArea(512)
|
val remarkTextArea = FixedLengthTextArea(512)
|
||||||
@@ -188,7 +191,9 @@ class SMBHostOptionsPane : OptionsPane() {
|
|||||||
.add(portTextField).xy(7, rows).apply { rows += step }
|
.add(portTextField).xy(7, rows).apply { rows += step }
|
||||||
|
|
||||||
.add("${I18n.getString("termora.new-host.general.username")}:").xy(1, rows)
|
.add("${I18n.getString("termora.new-host.general.username")}:").xy(1, rows)
|
||||||
.add(usernameTextField).xyw(3, rows, 5).apply { rows += step }
|
.add(usernameTextField).xy(3, rows)
|
||||||
|
.add("${SMBI18n.getString("termora.plugins.smb.domain")}:").xy(5, rows)
|
||||||
|
.add(domainTextField).xy(7, rows).apply { rows += step }
|
||||||
|
|
||||||
.add("${I18n.getString("termora.new-host.general.password")}:").xy(1, rows)
|
.add("${I18n.getString("termora.new-host.general.password")}:").xy(1, rows)
|
||||||
.add(passwordTextField).xyw(3, rows, 5).apply { rows += step }
|
.add(passwordTextField).xyw(3, rows, 5).apply { rows += step }
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ class SMBProtocolProvider private constructor() : TransferProtocolProvider {
|
|||||||
val client = SMBClient()
|
val client = SMBClient()
|
||||||
val host = requester.host
|
val host = requester.host
|
||||||
val connection = client.connect(host.host, host.port)
|
val connection = client.connect(host.host, host.port)
|
||||||
|
val domain = host.options.extras["smb.domain"] ?: StringUtils.EMPTY
|
||||||
val session = when (host.username) {
|
val session = when (host.username) {
|
||||||
"Guest" -> connection.authenticate(AuthenticationContext.guest())
|
"Guest" -> connection.authenticate(AuthenticationContext.guest())
|
||||||
"Anonymous" -> connection.authenticate(AuthenticationContext.anonymous())
|
"Anonymous" -> connection.authenticate(AuthenticationContext.anonymous())
|
||||||
@@ -37,7 +38,7 @@ class SMBProtocolProvider private constructor() : TransferProtocolProvider {
|
|||||||
AuthenticationContext(
|
AuthenticationContext(
|
||||||
host.username,
|
host.username,
|
||||||
host.authentication.password.toCharArray(),
|
host.authentication.password.toCharArray(),
|
||||||
null
|
domain.ifBlank { null }
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +1,2 @@
|
|||||||
termora.plugins.smb.share=Share name
|
termora.plugins.smb.share=Share name
|
||||||
|
termora.plugins.smb.domain=Domain
|
||||||
|
|||||||
@@ -1 +1,3 @@
|
|||||||
termora.plugins.smb.share=共享名称
|
termora.plugins.smb.share=共享名称
|
||||||
|
termora.plugins.smb.domain=域名
|
||||||
|
|
||||||
|
|||||||
@@ -1 +1,3 @@
|
|||||||
termora.plugins.smb.share=共享名稱
|
termora.plugins.smb.share=共享名稱
|
||||||
|
termora.plugins.smb.domain=網域
|
||||||
|
|
||||||
|
|||||||
@@ -50,6 +50,17 @@ class ApplicationInitializr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://github.com/TermoraDev/termora/issues/1254
|
||||||
|
if (System.getProperty(FlatSystemProperties.UI_SCALE).isNullOrBlank()) {
|
||||||
|
val scale = System.getenv("TERMORA_SCALE")
|
||||||
|
if (scale.isNullOrBlank().not()) {
|
||||||
|
if (NumberUtils.toDouble(scale, -1.0) > 0) {
|
||||||
|
System.setProperty(FlatSystemProperties.UI_SCALE_ENABLED, "true")
|
||||||
|
System.setProperty(FlatSystemProperties.UI_SCALE, scale)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 启动
|
// 启动
|
||||||
val runtime = measureTimeMillis { ApplicationRunner().run() }
|
val runtime = measureTimeMillis { ApplicationRunner().run() }
|
||||||
val log = LoggerFactory.getLogger(javaClass)
|
val log = LoggerFactory.getLogger(javaClass)
|
||||||
|
|||||||
@@ -10,15 +10,11 @@ import com.formdev.flatlaf.extras.FlatInspector
|
|||||||
import com.formdev.flatlaf.ui.FlatTableCellBorder
|
import com.formdev.flatlaf.ui.FlatTableCellBorder
|
||||||
import com.formdev.flatlaf.util.SystemInfo
|
import com.formdev.flatlaf.util.SystemInfo
|
||||||
import com.jthemedetecor.OsThemeDetector
|
import com.jthemedetecor.OsThemeDetector
|
||||||
import com.mixpanel.mixpanelapi.ClientDelivery
|
|
||||||
import com.mixpanel.mixpanelapi.MessageBuilder
|
|
||||||
import com.mixpanel.mixpanelapi.MixpanelAPI
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.apache.commons.io.FileUtils
|
import org.apache.commons.io.FileUtils
|
||||||
import org.apache.commons.lang3.LocaleUtils
|
import org.apache.commons.lang3.LocaleUtils
|
||||||
import org.apache.commons.lang3.SystemUtils
|
import org.apache.commons.lang3.SystemUtils
|
||||||
import org.json.JSONObject
|
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
import java.awt.*
|
import java.awt.*
|
||||||
import java.awt.desktop.AppReopenedEvent
|
import java.awt.desktop.AppReopenedEvent
|
||||||
@@ -369,61 +365,8 @@ class ApplicationRunner {
|
|||||||
if (Application.isUnknownVersion()) {
|
if (Application.isUnknownVersion()) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
MixpanelService.getInstance().push("launch")
|
||||||
swingCoroutineScope.launch(Dispatchers.IO) {
|
|
||||||
try {
|
|
||||||
val properties = JSONObject()
|
|
||||||
properties.put("os", SystemUtils.OS_NAME)
|
|
||||||
if (SystemInfo.isLinux) {
|
|
||||||
properties.put("platform", "Linux")
|
|
||||||
} else if (SystemInfo.isWindows) {
|
|
||||||
properties.put("platform", "Windows")
|
|
||||||
} else if (SystemInfo.isMacOS) {
|
|
||||||
properties.put("platform", "macOS")
|
|
||||||
}
|
|
||||||
properties.put("version", Application.getVersion())
|
|
||||||
properties.put("language", Locale.getDefault().toString())
|
|
||||||
val message = MessageBuilder("0871335f59ee6d0eb246b008a20f9d1c")
|
|
||||||
.event(getAnalyticsUserID(), "launch", properties)
|
|
||||||
val delivery = ClientDelivery()
|
|
||||||
delivery.addMessage(message)
|
|
||||||
val endpoints = listOf(
|
|
||||||
"https://api-eu.mixpanel.com",
|
|
||||||
"https://api-in.mixpanel.com",
|
|
||||||
"https://api.mixpanel.com",
|
|
||||||
"http://api.mixpanel.com",
|
|
||||||
)
|
|
||||||
for (endpoint in endpoints) {
|
|
||||||
try {
|
|
||||||
MixpanelAPI(
|
|
||||||
"$endpoint/track",
|
|
||||||
"$endpoint/engage",
|
|
||||||
"$endpoint/groups"
|
|
||||||
).deliver(delivery, true)
|
|
||||||
break
|
|
||||||
} catch (e: Exception) {
|
|
||||||
if (log.isErrorEnabled) {
|
|
||||||
log.error(e.message, e)
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e: Exception) {
|
|
||||||
if (log.isErrorEnabled) {
|
|
||||||
log.error(e.message, e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getAnalyticsUserID(): String {
|
|
||||||
val properties = DatabaseManager.getInstance().properties
|
|
||||||
var id = properties.getString("AnalyticsUserID")
|
|
||||||
if (id.isNullOrBlank()) {
|
|
||||||
id = randomUUID()
|
|
||||||
properties.putString("AnalyticsUserID", id)
|
|
||||||
}
|
|
||||||
return id
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
85
src/main/kotlin/app/termora/MixpanelService.kt
Normal file
85
src/main/kotlin/app/termora/MixpanelService.kt
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
package app.termora
|
||||||
|
|
||||||
|
import app.termora.database.DatabaseManager
|
||||||
|
import com.formdev.flatlaf.util.SystemInfo
|
||||||
|
import com.mixpanel.mixpanelapi.ClientDelivery
|
||||||
|
import com.mixpanel.mixpanelapi.MessageBuilder
|
||||||
|
import com.mixpanel.mixpanelapi.MixpanelAPI
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import org.apache.commons.lang3.SystemUtils
|
||||||
|
import org.json.JSONObject
|
||||||
|
import org.slf4j.LoggerFactory
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
internal class MixpanelService private constructor() {
|
||||||
|
companion object {
|
||||||
|
private val log = LoggerFactory.getLogger(MixpanelService::class.java)
|
||||||
|
|
||||||
|
fun getInstance(): MixpanelService {
|
||||||
|
return ApplicationScope.forApplicationScope().getOrCreate(MixpanelService::class) { MixpanelService() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun push(event: String, extras: Map<String, String> = emptyMap()) {
|
||||||
|
swingCoroutineScope.launch(Dispatchers.IO) {
|
||||||
|
try {
|
||||||
|
val properties = JSONObject()
|
||||||
|
for (entry in extras) {
|
||||||
|
properties.put(entry.key, entry.value)
|
||||||
|
}
|
||||||
|
properties.put("os", SystemUtils.OS_NAME)
|
||||||
|
if (SystemInfo.isLinux) {
|
||||||
|
properties.put("platform", "Linux")
|
||||||
|
} else if (SystemInfo.isWindows) {
|
||||||
|
properties.put("platform", "Windows")
|
||||||
|
} else if (SystemInfo.isMacOS) {
|
||||||
|
properties.put("platform", "macOS")
|
||||||
|
}
|
||||||
|
properties.put("version", Application.getVersion())
|
||||||
|
properties.put("language", Locale.getDefault().toString())
|
||||||
|
val message = MessageBuilder("0871335f59ee6d0eb246b008a20f9d1c")
|
||||||
|
.event(getAnalyticsUserID(), event, properties)
|
||||||
|
val delivery = ClientDelivery()
|
||||||
|
delivery.addMessage(message)
|
||||||
|
val endpoints = listOf(
|
||||||
|
"https://api-eu.mixpanel.com",
|
||||||
|
"https://api-in.mixpanel.com",
|
||||||
|
"https://api.mixpanel.com",
|
||||||
|
"http://api.mixpanel.com",
|
||||||
|
)
|
||||||
|
for (endpoint in endpoints) {
|
||||||
|
try {
|
||||||
|
MixpanelAPI(
|
||||||
|
"$endpoint/track",
|
||||||
|
"$endpoint/engage",
|
||||||
|
"$endpoint/groups"
|
||||||
|
).deliver(delivery, true)
|
||||||
|
break
|
||||||
|
} catch (e: Exception) {
|
||||||
|
if (log.isErrorEnabled) {
|
||||||
|
log.error(e.message, e)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
if (log.isErrorEnabled) {
|
||||||
|
log.error(e.message, e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun getAnalyticsUserID(): String {
|
||||||
|
val properties = DatabaseManager.getInstance().properties
|
||||||
|
var id = properties.getString("AnalyticsUserID")
|
||||||
|
if (id.isNullOrBlank()) {
|
||||||
|
id = randomUUID()
|
||||||
|
properties.putString("AnalyticsUserID", id)
|
||||||
|
}
|
||||||
|
return id
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -3,5 +3,5 @@ package app.termora
|
|||||||
import app.termora.actions.AnActionEvent
|
import app.termora.actions.AnActionEvent
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
class OpenHostActionEvent(source: Any, val host: Host, event: EventObject) :
|
class OpenHostActionEvent(source: Any, val host: Host, event: EventObject, val tabIndex: Int = -1) :
|
||||||
AnActionEvent(source, String(), event)
|
AnActionEvent(source, String(), event)
|
||||||
@@ -179,7 +179,7 @@ abstract class PtyHostTerminalTab(
|
|||||||
|
|
||||||
val tab = createReconnectTerminalTab()
|
val tab = createReconnectTerminalTab()
|
||||||
manager.addTerminalTab(index, tab, true)
|
manager.addTerminalTab(index, tab, true)
|
||||||
manager.closeTerminalTab(this, true)
|
manager.closeTerminalTab(this, disposable = true, reconnect = true)
|
||||||
|
|
||||||
if (tab is HostTerminalTab) {
|
if (tab is HostTerminalTab) {
|
||||||
tab.start()
|
tab.start()
|
||||||
|
|||||||
@@ -547,6 +547,7 @@ class SettingsOptionsPane : OptionsPane() {
|
|||||||
|
|
||||||
rightClickComboBox.addItem("Copy")
|
rightClickComboBox.addItem("Copy")
|
||||||
rightClickComboBox.addItem("CopyAndPaste")
|
rightClickComboBox.addItem("CopyAndPaste")
|
||||||
|
rightClickComboBox.addItem("Nothing")
|
||||||
|
|
||||||
rightClickComboBox.selectedItem = terminalSetting.rightClick
|
rightClickComboBox.selectedItem = terminalSetting.rightClick
|
||||||
|
|
||||||
@@ -576,6 +577,8 @@ class SettingsOptionsPane : OptionsPane() {
|
|||||||
text = I18n.getString("termora.settings.terminal.right-click.copy")
|
text = I18n.getString("termora.settings.terminal.right-click.copy")
|
||||||
} else if (value == "CopyAndPaste") {
|
} else if (value == "CopyAndPaste") {
|
||||||
text = I18n.getString("termora.settings.terminal.right-click.copy-and-paste")
|
text = I18n.getString("termora.settings.terminal.right-click.copy-and-paste")
|
||||||
|
}else if (value == "Nothing") {
|
||||||
|
text = I18n.getString("termora.settings.terminal.right-click.nothing")
|
||||||
}
|
}
|
||||||
return super.getListCellRendererComponent(list, text, index, isSelected, cellHasFocus)
|
return super.getListCellRendererComponent(list, text, index, isSelected, cellHasFocus)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -141,25 +141,28 @@ class TerminalTabbed(
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun removeTabAt(index: Int, disposable: Boolean = true) {
|
private fun removeTabAt(index: Int, disposable: Boolean = true, reconnect: Boolean = false) {
|
||||||
if (tabbedPane.isTabClosable(index)) {
|
if (tabbedPane.isTabClosable(index)) {
|
||||||
val tab = tabs[index]
|
val tab = tabs[index]
|
||||||
|
|
||||||
// 询问是否可以关闭
|
// 询问是否可以关闭
|
||||||
if (disposable) {
|
if (disposable) {
|
||||||
// 如果开启了关闭确认,那么直接询问用户
|
// 如果是重连接,那么直接关闭不进行任何形式的询问
|
||||||
if (appearance.confirmTabClose) {
|
if (reconnect.not()) {
|
||||||
if (OptionPane.showConfirmDialog(
|
// 如果开启了关闭确认,那么直接询问用户
|
||||||
windowScope.window,
|
if (appearance.confirmTabClose) {
|
||||||
I18n.getString("termora.tabbed.tab.close-prompt"),
|
if (OptionPane.showConfirmDialog(
|
||||||
messageType = JOptionPane.QUESTION_MESSAGE,
|
windowScope.window,
|
||||||
optionType = JOptionPane.OK_CANCEL_OPTION
|
I18n.getString("termora.tabbed.tab.close-prompt"),
|
||||||
) != JOptionPane.OK_OPTION
|
messageType = JOptionPane.QUESTION_MESSAGE,
|
||||||
) {
|
optionType = JOptionPane.OK_CANCEL_OPTION
|
||||||
|
) != JOptionPane.OK_OPTION
|
||||||
|
) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else if (!tab.willBeClose()) { // 如果没有开启则询问用户
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
} else if (!tab.willBeClose()) { // 如果没有开启则询问用户
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -233,7 +236,7 @@ class TerminalTabbed(
|
|||||||
if (tab is HostTerminalTab) {
|
if (tab is HostTerminalTab) {
|
||||||
actionManager
|
actionManager
|
||||||
.getAction(OpenHostAction.OPEN_HOST)
|
.getAction(OpenHostAction.OPEN_HOST)
|
||||||
.actionPerformed(OpenHostActionEvent(this, tab.host, evt))
|
.actionPerformed(OpenHostActionEvent(this, tab.host, evt, tabIndex + 1))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -361,7 +364,7 @@ class TerminalTabbed(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun indexOfTerminalTab(tab: TerminalTab):Int {
|
override fun indexOfTerminalTab(tab: TerminalTab): Int {
|
||||||
return tabbedPane.indexOfComponent(tab.getJComponent())
|
return tabbedPane.indexOfComponent(tab.getJComponent())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -451,10 +454,10 @@ class TerminalTabbed(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun closeTerminalTab(tab: TerminalTab, disposable: Boolean) {
|
override fun closeTerminalTab(tab: TerminalTab, disposable: Boolean, reconnect: Boolean) {
|
||||||
for (i in 0 until tabs.size) {
|
for (i in 0 until tabs.size) {
|
||||||
if (tabs[i] == tab) {
|
if (tabs[i] == tab) {
|
||||||
removeTabAt(i, disposable)
|
removeTabAt(i, disposable, reconnect)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ interface TerminalTabbedManager {
|
|||||||
fun getSelectedTerminalTab(): TerminalTab?
|
fun getSelectedTerminalTab(): TerminalTab?
|
||||||
fun getTerminalTabs(): List<TerminalTab>
|
fun getTerminalTabs(): List<TerminalTab>
|
||||||
fun setSelectedTerminalTab(tab: TerminalTab)
|
fun setSelectedTerminalTab(tab: TerminalTab)
|
||||||
fun closeTerminalTab(tab: TerminalTab, disposable: Boolean = true)
|
fun closeTerminalTab(tab: TerminalTab, disposable: Boolean = true, reconnect: Boolean = false)
|
||||||
fun refreshTerminalTabs()
|
fun refreshTerminalTabs()
|
||||||
fun indexOfTerminalTab(tab: TerminalTab): Int
|
fun indexOfTerminalTab(tab: TerminalTab): Int
|
||||||
}
|
}
|
||||||
@@ -67,6 +67,11 @@ class PullService private constructor() : SyncService(), Disposable, Application
|
|||||||
private var lastChangeHash = StringUtils.EMPTY
|
private var lastChangeHash = StringUtils.EMPTY
|
||||||
|
|
||||||
private fun pullChanges() {
|
private fun pullChanges() {
|
||||||
|
|
||||||
|
if (accountManager.isLocally()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
val hash: String
|
val hash: String
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -56,7 +56,12 @@ class OpenHostAction : AnAction() {
|
|||||||
|
|
||||||
if (tab == null) return
|
if (tab == null) return
|
||||||
|
|
||||||
terminalTabbedManager.addTerminalTab(tab)
|
if (evt.tabIndex >= 0) {
|
||||||
|
terminalTabbedManager.addTerminalTab(evt.tabIndex, tab)
|
||||||
|
} else {
|
||||||
|
terminalTabbedManager.addTerminalTab(tab)
|
||||||
|
}
|
||||||
|
|
||||||
if (tab is PtyHostTerminalTab) {
|
if (tab is PtyHostTerminalTab) {
|
||||||
tab.start()
|
tab.start()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,10 @@ class TerminalCopyAction : AnAction() {
|
|||||||
|
|
||||||
override fun actionPerformed(evt: AnActionEvent) {
|
override fun actionPerformed(evt: AnActionEvent) {
|
||||||
val terminalPanel = evt.getData(DataProviders.TerminalPanel) ?: return
|
val terminalPanel = evt.getData(DataProviders.TerminalPanel) ?: return
|
||||||
|
val selectionModel = terminalPanel.terminal.getSelectionModel()
|
||||||
|
if (!selectionModel.hasSelection()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
val text = terminalPanel.copy()
|
val text = terminalPanel.copy()
|
||||||
val systemClipboard = terminalPanel.toolkit.systemClipboard
|
val systemClipboard = terminalPanel.toolkit.systemClipboard
|
||||||
|
|
||||||
@@ -53,4 +57,4 @@ class TerminalCopyAction : AnAction() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,12 +27,14 @@ class KeyboardInteractiveDialog(
|
|||||||
isModal = true
|
isModal = true
|
||||||
isResizable = true
|
isResizable = true
|
||||||
controlsVisible = false
|
controlsVisible = false
|
||||||
title = I18n.getString("termora.new-host.title")
|
|
||||||
|
|
||||||
init()
|
init()
|
||||||
pack()
|
pack()
|
||||||
size = Dimension(max(300, size.width), size.height)
|
size = Dimension(max(300, size.width), size.height)
|
||||||
|
|
||||||
|
// fix https://github.com/TermoraDev/termora/issues/1311
|
||||||
|
pack()
|
||||||
|
|
||||||
setLocationRelativeTo(null)
|
setLocationRelativeTo(null)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ class TerminalUserInteraction(
|
|||||||
)
|
)
|
||||||
dialog.setLocationRelativeTo(owner)
|
dialog.setLocationRelativeTo(owner)
|
||||||
dialog.title = instruction ?: name ?: "OTP"
|
dialog.title = instruction ?: name ?: "OTP"
|
||||||
|
dialog.title = StringUtils.defaultIfBlank(dialog.title, "OTP")
|
||||||
passwords[i] = dialog.getText()
|
passwords[i] = dialog.getText()
|
||||||
if (passwords[i].isBlank()) {
|
if (passwords[i].isBlank()) {
|
||||||
break
|
break
|
||||||
|
|||||||
@@ -29,8 +29,10 @@ class KeymapPanel : JPanel(BorderLayout()) {
|
|||||||
private val copyBtn = JButton(Icons.copy)
|
private val copyBtn = JButton(Icons.copy)
|
||||||
private val renameBtn = JButton(Icons.edit)
|
private val renameBtn = JButton(Icons.edit)
|
||||||
private val deleteBtn = JButton(Icons.delete)
|
private val deleteBtn = JButton(Icons.delete)
|
||||||
|
private val infoBtn = JButton(Icons.questionMark)
|
||||||
private val database get() = DatabaseManager.getInstance()
|
private val database get() = DatabaseManager.getInstance()
|
||||||
private val allowKeyCodes = mutableSetOf<Int>()
|
private val allowKeyCodes = mutableSetOf<Int>()
|
||||||
|
private val owner get() = SwingUtilities.getWindowAncestor(this)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
initView()
|
initView()
|
||||||
@@ -89,8 +91,8 @@ class KeymapPanel : JPanel(BorderLayout()) {
|
|||||||
box.add(copyBtn)
|
box.add(copyBtn)
|
||||||
box.add(renameBtn)
|
box.add(renameBtn)
|
||||||
box.add(deleteBtn)
|
box.add(deleteBtn)
|
||||||
|
box.add(infoBtn)
|
||||||
box.add(Box.createHorizontalGlue())
|
box.add(Box.createHorizontalGlue())
|
||||||
box.border = BorderFactory.createEmptyBorder(0, 0, 6, 0)
|
|
||||||
|
|
||||||
add(box, BorderLayout.NORTH)
|
add(box, BorderLayout.NORTH)
|
||||||
add(scrollPane, BorderLayout.CENTER)
|
add(scrollPane, BorderLayout.CENTER)
|
||||||
@@ -105,6 +107,12 @@ class KeymapPanel : JPanel(BorderLayout()) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
infoBtn.addActionListener {
|
||||||
|
val color = UIManager.getColor("TextField.placeholderForeground")
|
||||||
|
val msg = I18n.getString("termora.settings.keymap.question", color.red, color.green, color.blue)
|
||||||
|
OptionPane.showMessageDialog(owner, msg)
|
||||||
|
}
|
||||||
|
|
||||||
copyBtn.addActionListener {
|
copyBtn.addActionListener {
|
||||||
val keymap = getCurrentKeymap()
|
val keymap = getCurrentKeymap()
|
||||||
if (keymap != null) {
|
if (keymap != null) {
|
||||||
|
|||||||
@@ -185,6 +185,13 @@ class PluginPanel(val descriptor: PluginPluginDescriptor) : JPanel(), Disposable
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MixpanelService.getInstance().push(
|
||||||
|
"uninstall-plugin", mapOf(
|
||||||
|
"pluginName" to descriptor.plugin.getName(),
|
||||||
|
"pluginVersion" to descriptor.version.toString(),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
// 询问是否重启
|
// 询问是否重启
|
||||||
TermoraRestarter.getInstance().scheduleRestart(owner)
|
TermoraRestarter.getInstance().scheduleRestart(owner)
|
||||||
} else {
|
} else {
|
||||||
@@ -227,6 +234,13 @@ class PluginPanel(val descriptor: PluginPluginDescriptor) : JPanel(), Disposable
|
|||||||
}
|
}
|
||||||
}, button == updateButton)
|
}, button == updateButton)
|
||||||
|
|
||||||
|
MixpanelService.getInstance().push(
|
||||||
|
"${if (button == installButton) "install" else "update"}-plugin", mapOf(
|
||||||
|
"pluginName" to descriptor.plugin.getName(),
|
||||||
|
"pluginVersion" to descriptor.version.toString(),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
withContext(Dispatchers.Swing) {
|
withContext(Dispatchers.Swing) {
|
||||||
installed.add(descriptor.id)
|
installed.add(descriptor.id)
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ import kotlinx.coroutines.launch
|
|||||||
import org.apache.commons.io.FileUtils
|
import org.apache.commons.io.FileUtils
|
||||||
import org.apache.commons.io.IOUtils
|
import org.apache.commons.io.IOUtils
|
||||||
import org.apache.commons.lang3.StringUtils
|
import org.apache.commons.lang3.StringUtils
|
||||||
|
import org.apache.commons.lang3.Strings
|
||||||
|
import org.apache.commons.lang3.SystemUtils
|
||||||
import java.awt.datatransfer.DataFlavor
|
import java.awt.datatransfer.DataFlavor
|
||||||
import java.awt.datatransfer.StringSelection
|
import java.awt.datatransfer.StringSelection
|
||||||
import java.net.URI
|
import java.net.URI
|
||||||
@@ -64,7 +66,15 @@ internal class RDPProtocolProvider private constructor() : GenericProtocolProvid
|
|||||||
}
|
}
|
||||||
|
|
||||||
val sb = StringBuilder()
|
val sb = StringBuilder()
|
||||||
sb.append("full address:s:").append(host.host).append(':').append(host.port).appendLine()
|
sb.append("full address:s:")
|
||||||
|
if (SystemUtils.IS_OS_WINDOWS && Strings.CI.contains(host.host, ":")) {
|
||||||
|
var newHost = Strings.CI.removeStart(host.host, "[")
|
||||||
|
newHost = Strings.CI.removeEnd(newHost, "]")
|
||||||
|
sb.append('[').append(newHost).append(']')
|
||||||
|
} else {
|
||||||
|
sb.append(host.host)
|
||||||
|
}
|
||||||
|
sb.append(':').append(host.port).appendLine()
|
||||||
sb.append("username:s:").append(host.username).appendLine()
|
sb.append("username:s:").append(host.username).appendLine()
|
||||||
val desktop = host.options.extras["desktop"]
|
val desktop = host.options.extras["desktop"]
|
||||||
if (desktop.isNullOrBlank().not()) {
|
if (desktop.isNullOrBlank().not()) {
|
||||||
|
|||||||
@@ -27,9 +27,14 @@ class CloneSessionTerminalTabbedContextMenuExtension private constructor() : Ter
|
|||||||
cloneSession.addActionListener(object : AnAction() {
|
cloneSession.addActionListener(object : AnAction() {
|
||||||
override fun actionPerformed(evt: AnActionEvent) {
|
override fun actionPerformed(evt: AnActionEvent) {
|
||||||
val terminalTabbedManager = evt.getData(DataProviders.TerminalTabbedManager) ?: return
|
val terminalTabbedManager = evt.getData(DataProviders.TerminalTabbedManager) ?: return
|
||||||
|
val index = terminalTabbedManager.indexOfTerminalTab(tab)
|
||||||
val handler = c.copy(channel = null)
|
val handler = c.copy(channel = null)
|
||||||
val newTab = SSHTerminalTab(windowScope, tab.host, handler)
|
val newTab = SSHTerminalTab(windowScope, tab.host, handler)
|
||||||
terminalTabbedManager.addTerminalTab(newTab)
|
if (index >= 0) {
|
||||||
|
terminalTabbedManager.addTerminalTab(index + 1, newTab)
|
||||||
|
} else {
|
||||||
|
terminalTabbedManager.addTerminalTab(newTab)
|
||||||
|
}
|
||||||
newTab.start()
|
newTab.start()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -332,6 +332,12 @@ class ControlSequenceIntroducerProcessor(terminal: Terminal, reader: TerminalRea
|
|||||||
var top = sr.getOrElse(0) { 1 }
|
var top = sr.getOrElse(0) { 1 }
|
||||||
var bottom = sr.getOrElse(1) { terminalModel.getRows() }
|
var bottom = sr.getOrElse(1) { terminalModel.getRows() }
|
||||||
|
|
||||||
|
// ";r" https://vt100.net/docs/vt510-rm/DECSTBM.html
|
||||||
|
if (sr.size == 1 && args.startsWith(';')) {
|
||||||
|
bottom = top
|
||||||
|
top = 1
|
||||||
|
}
|
||||||
|
|
||||||
if (bottom <= top) {
|
if (bottom <= top) {
|
||||||
if (log.isWarnEnabled) {
|
if (log.isWarnEnabled) {
|
||||||
log.warn("Set Scrolling Region Error. top: $top , bottom: $bottom")
|
log.warn("Set Scrolling Region Error. top: $top , bottom: $bottom")
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package app.termora.terminal.panel
|
package app.termora.terminal.panel
|
||||||
|
|
||||||
|
import app.termora.actions.TerminalCopyAction
|
||||||
import app.termora.keymap.KeyShortcut
|
import app.termora.keymap.KeyShortcut
|
||||||
import app.termora.keymap.KeymapManager
|
import app.termora.keymap.KeymapManager
|
||||||
import app.termora.plugin.internal.AltKeyModifier
|
import app.termora.plugin.internal.AltKeyModifier
|
||||||
@@ -71,6 +72,7 @@ class TerminalPanelKeyAdapter(
|
|||||||
}
|
}
|
||||||
|
|
||||||
val keyStroke = KeyStroke.getKeyStrokeForEvent(e)
|
val keyStroke = KeyStroke.getKeyStrokeForEvent(e)
|
||||||
|
val keymapActions = activeKeymap.getActionIds(KeyShortcut(keyStroke))
|
||||||
for (action in terminalPanel.getTerminalActions()) {
|
for (action in terminalPanel.getTerminalActions()) {
|
||||||
if (action.test(keyStroke, e)) {
|
if (action.test(keyStroke, e)) {
|
||||||
action.actionPerformed(e)
|
action.actionPerformed(e)
|
||||||
@@ -104,7 +106,9 @@ class TerminalPanelKeyAdapter(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 如果命中了全局快捷键,那么不处理
|
// 如果命中了全局快捷键,那么不处理
|
||||||
if (keyStroke.modifiers != 0 && activeKeymap.getActionIds(KeyShortcut(keyStroke)).isNotEmpty()) {
|
val copyShortcutWithoutSelection =
|
||||||
|
keymapActions.contains(TerminalCopyAction.COPY) && terminal.getSelectionModel().hasSelection().not()
|
||||||
|
if (keyStroke.modifiers != 0 && keymapActions.isNotEmpty() && !copyShortcutWithoutSelection) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,4 +163,4 @@ class TerminalPanelKeyAdapter(
|
|||||||
return Character.toLowerCase(e.keyCode.toChar())
|
return Character.toLowerCase(e.keyCode.toChar())
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,30 +53,31 @@ class TerminalPanelMouseSelectionAdapter(private val terminalPanel: TerminalPane
|
|||||||
if (SwingUtilities.isRightMouseButton(e)) {
|
if (SwingUtilities.isRightMouseButton(e)) {
|
||||||
// 如果有选中并且开启了选中复制,那么右键直接是粘贴
|
// 如果有选中并且开启了选中复制,那么右键直接是粘贴
|
||||||
if (selectionModel.hasSelection() && isSelectCopy.not()) {
|
if (selectionModel.hasSelection() && isSelectCopy.not()) {
|
||||||
triggerCopyAction(
|
if (rightClickMode != "Nothing") {
|
||||||
KeyEvent(
|
triggerCopyAction(
|
||||||
e.component,
|
|
||||||
KeyEvent.KEY_PRESSED,
|
|
||||||
e.`when`,
|
|
||||||
e.modifiersEx,
|
|
||||||
KeyEvent.VK_C,
|
|
||||||
'C'
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
if (rightClickMode == "CopyAndPaste") {
|
|
||||||
triggerPasteAction(
|
|
||||||
KeyEvent(
|
KeyEvent(
|
||||||
e.component,
|
e.component,
|
||||||
KeyEvent.KEY_PRESSED,
|
KeyEvent.KEY_PRESSED,
|
||||||
e.`when`,
|
e.`when`,
|
||||||
e.modifiersEx,
|
e.modifiersEx,
|
||||||
KeyEvent.VK_V,
|
KeyEvent.VK_C,
|
||||||
'V'
|
'C'
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
|
||||||
|
|
||||||
|
if (rightClickMode == "CopyAndPaste") {
|
||||||
|
triggerPasteAction(
|
||||||
|
KeyEvent(
|
||||||
|
e.component,
|
||||||
|
KeyEvent.KEY_PRESSED,
|
||||||
|
e.`when`,
|
||||||
|
e.modifiersEx,
|
||||||
|
KeyEvent.VK_V,
|
||||||
|
'V'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// paste
|
// paste
|
||||||
triggerPasteAction(
|
triggerPasteAction(
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ class TransferAnAction : AnAction(I18n.getString("termora.transport.sftp"), Icon
|
|||||||
val panel = tabbed.getTransportPanel(i) ?: continue
|
val panel = tabbed.getTransportPanel(i) ?: continue
|
||||||
if (panel.host.id == host.id) {
|
if (panel.host.id == host.id) {
|
||||||
tabbed.selectedIndex = i
|
tabbed.selectedIndex = i
|
||||||
|
terminalTabbedManager.setSelectedTerminalTab(sftpTab)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package app.termora.transfer
|
package app.termora.transfer
|
||||||
|
|
||||||
import app.termora.WindowScope
|
|
||||||
import app.termora.plugin.Extension
|
import app.termora.plugin.Extension
|
||||||
|
import java.awt.Window
|
||||||
import java.nio.file.FileSystem
|
import java.nio.file.FileSystem
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import javax.swing.JMenuItem
|
import javax.swing.JMenuItem
|
||||||
@@ -14,7 +14,7 @@ internal interface TransportContextMenuExtension : Extension {
|
|||||||
* @param fileSystem 为 null 表示可能已经断线,处于不可用状态
|
* @param fileSystem 为 null 表示可能已经断线,处于不可用状态
|
||||||
*/
|
*/
|
||||||
fun createJMenuItem(
|
fun createJMenuItem(
|
||||||
windowScope: WindowScope,
|
window: Window,
|
||||||
fileSystem: FileSystem?,
|
fileSystem: FileSystem?,
|
||||||
popupMenu: TransportPopupMenu,
|
popupMenu: TransportPopupMenu,
|
||||||
files: List<Pair<Path, TransportTableModel.Attributes>>
|
files: List<Pair<Path, TransportTableModel.Attributes>>
|
||||||
|
|||||||
@@ -108,7 +108,7 @@ internal class TransportPopupMenu(
|
|||||||
for (extension in extensionManager.getExtensions(TransportContextMenuExtension::class.java)) {
|
for (extension in extensionManager.getExtensions(TransportContextMenuExtension::class.java)) {
|
||||||
try {
|
try {
|
||||||
val menu = extension.createJMenuItem(
|
val menu = extension.createJMenuItem(
|
||||||
ApplicationScope.forWindowScope(owner),
|
owner,
|
||||||
fileSystem,
|
fileSystem,
|
||||||
this,
|
this,
|
||||||
files
|
files
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package app.termora.transfer.internal.sftp
|
package app.termora.transfer.internal.sftp
|
||||||
|
|
||||||
import app.termora.I18n
|
import app.termora.I18n
|
||||||
import app.termora.WindowScope
|
|
||||||
import app.termora.actions.AnAction
|
import app.termora.actions.AnAction
|
||||||
import app.termora.actions.AnActionEvent
|
import app.termora.actions.AnActionEvent
|
||||||
import app.termora.randomUUID
|
import app.termora.randomUUID
|
||||||
@@ -9,6 +8,7 @@ import app.termora.transfer.*
|
|||||||
import org.apache.commons.lang3.StringUtils
|
import org.apache.commons.lang3.StringUtils
|
||||||
import org.apache.sshd.common.file.util.MockPath
|
import org.apache.sshd.common.file.util.MockPath
|
||||||
import org.apache.sshd.sftp.client.fs.SftpFileSystem
|
import org.apache.sshd.sftp.client.fs.SftpFileSystem
|
||||||
|
import java.awt.Window
|
||||||
import java.nio.file.FileSystem
|
import java.nio.file.FileSystem
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import javax.swing.JMenu
|
import javax.swing.JMenu
|
||||||
@@ -23,7 +23,7 @@ internal class CompressTransportContextMenuExtension private constructor() : Tra
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun createJMenuItem(
|
override fun createJMenuItem(
|
||||||
windowScope: WindowScope,
|
window: Window,
|
||||||
fileSystem: FileSystem?,
|
fileSystem: FileSystem?,
|
||||||
popupMenu: TransportPopupMenu,
|
popupMenu: TransportPopupMenu,
|
||||||
files: List<Pair<Path, TransportTableModel.Attributes>>
|
files: List<Pair<Path, TransportTableModel.Attributes>>
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
package app.termora.transfer.internal.sftp
|
package app.termora.transfer.internal.sftp
|
||||||
|
|
||||||
import app.termora.I18n
|
import app.termora.I18n
|
||||||
import app.termora.WindowScope
|
|
||||||
import app.termora.actions.AnAction
|
import app.termora.actions.AnAction
|
||||||
import app.termora.actions.AnActionEvent
|
import app.termora.actions.AnActionEvent
|
||||||
import app.termora.randomUUID
|
import app.termora.randomUUID
|
||||||
import app.termora.transfer.*
|
import app.termora.transfer.*
|
||||||
import org.apache.commons.lang3.StringUtils
|
import org.apache.commons.lang3.StringUtils
|
||||||
import org.apache.sshd.sftp.client.fs.SftpFileSystem
|
import org.apache.sshd.sftp.client.fs.SftpFileSystem
|
||||||
|
import java.awt.Window
|
||||||
import java.nio.file.FileSystem
|
import java.nio.file.FileSystem
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import javax.swing.JMenu
|
import javax.swing.JMenu
|
||||||
@@ -21,7 +21,7 @@ internal class ExtractTransportContextMenuExtension private constructor() : Tran
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun createJMenuItem(
|
override fun createJMenuItem(
|
||||||
windowScope: WindowScope,
|
window: Window,
|
||||||
fileSystem: FileSystem?,
|
fileSystem: FileSystem?,
|
||||||
popupMenu: TransportPopupMenu,
|
popupMenu: TransportPopupMenu,
|
||||||
files: List<Pair<Path, TransportTableModel.Attributes>>
|
files: List<Pair<Path, TransportTableModel.Attributes>>
|
||||||
|
|||||||
@@ -3,11 +3,11 @@ package app.termora.transfer.internal.sftp
|
|||||||
import app.termora.I18n
|
import app.termora.I18n
|
||||||
import app.termora.Icons
|
import app.termora.Icons
|
||||||
import app.termora.OptionPane
|
import app.termora.OptionPane
|
||||||
import app.termora.WindowScope
|
|
||||||
import app.termora.transfer.TransportContextMenuExtension
|
import app.termora.transfer.TransportContextMenuExtension
|
||||||
import app.termora.transfer.TransportPopupMenu
|
import app.termora.transfer.TransportPopupMenu
|
||||||
import app.termora.transfer.TransportTableModel
|
import app.termora.transfer.TransportTableModel
|
||||||
import org.apache.sshd.sftp.client.fs.SftpFileSystem
|
import org.apache.sshd.sftp.client.fs.SftpFileSystem
|
||||||
|
import java.awt.Window
|
||||||
import java.nio.file.FileSystem
|
import java.nio.file.FileSystem
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import javax.swing.JMenuItem
|
import javax.swing.JMenuItem
|
||||||
@@ -19,7 +19,7 @@ internal class RmrfTransportContextMenuExtension private constructor() : Transpo
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun createJMenuItem(
|
override fun createJMenuItem(
|
||||||
windowScope: WindowScope,
|
window: Window,
|
||||||
fileSystem: FileSystem?,
|
fileSystem: FileSystem?,
|
||||||
popupMenu: TransportPopupMenu,
|
popupMenu: TransportPopupMenu,
|
||||||
files: List<Pair<Path, TransportTableModel.Attributes>>
|
files: List<Pair<Path, TransportTableModel.Attributes>>
|
||||||
@@ -31,7 +31,7 @@ internal class RmrfTransportContextMenuExtension private constructor() : Transpo
|
|||||||
val rmrfMenu = JMenuItem("rm -rf", Icons.warningIntroduction)
|
val rmrfMenu = JMenuItem("rm -rf", Icons.warningIntroduction)
|
||||||
rmrfMenu.addActionListener {
|
rmrfMenu.addActionListener {
|
||||||
if (OptionPane.showConfirmDialog(
|
if (OptionPane.showConfirmDialog(
|
||||||
windowScope.window,
|
window,
|
||||||
I18n.getString("termora.transport.table.contextmenu.rm-warning"),
|
I18n.getString("termora.transport.table.contextmenu.rm-warning"),
|
||||||
messageType = JOptionPane.ERROR_MESSAGE
|
messageType = JOptionPane.ERROR_MESSAGE
|
||||||
) == JOptionPane.YES_OPTION
|
) == JOptionPane.YES_OPTION
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ termora.settings.terminal.hyperlink=Hyperlink
|
|||||||
termora.settings.terminal.select-copy=Select copy
|
termora.settings.terminal.select-copy=Select copy
|
||||||
termora.settings.terminal.right-click=Right click
|
termora.settings.terminal.right-click=Right click
|
||||||
termora.settings.terminal.right-click.copy-and-paste=Copy and Paste
|
termora.settings.terminal.right-click.copy-and-paste=Copy and Paste
|
||||||
|
termora.settings.terminal.right-click.nothing=Nothing
|
||||||
termora.settings.terminal.right-click.copy=${termora.copy}
|
termora.settings.terminal.right-click.copy=${termora.copy}
|
||||||
termora.settings.terminal.cursor-style=Cursor type
|
termora.settings.terminal.cursor-style=Cursor type
|
||||||
termora.settings.terminal.cursor-blink=Cursor blink
|
termora.settings.terminal.cursor-blink=Cursor blink
|
||||||
@@ -114,7 +115,7 @@ termora.settings.keymap=Keymap
|
|||||||
termora.settings.keymap.shortcut=Shortcut
|
termora.settings.keymap.shortcut=Shortcut
|
||||||
termora.settings.keymap.action=Action
|
termora.settings.keymap.action=Action
|
||||||
termora.settings.keymap.already-exists=The shortcut [{0}] is already in use by [{1}]
|
termora.settings.keymap.already-exists=The shortcut [{0}] is already in use by [{1}]
|
||||||
|
termora.settings.keymap.question=Select a line, press the <font color=rgb({0},{1},{2})> ⌫ (Backspace)</font> key to remove the shortcut
|
||||||
|
|
||||||
termora.settings.sftp.edit-command=Edit Command
|
termora.settings.sftp.edit-command=Edit Command
|
||||||
termora.settings.sftp.db-click-behavior=Double-click
|
termora.settings.sftp.db-click-behavior=Double-click
|
||||||
|
|||||||
@@ -81,6 +81,7 @@ termora.settings.terminal.hyperlink=Ссылки
|
|||||||
termora.settings.terminal.select-copy=Копировать выделенное
|
termora.settings.terminal.select-copy=Копировать выделенное
|
||||||
termora.settings.terminal.right-click=правой кнопкой мыши
|
termora.settings.terminal.right-click=правой кнопкой мыши
|
||||||
termora.settings.terminal.right-click.copy-and-paste=Копировать и вставить
|
termora.settings.terminal.right-click.copy-and-paste=Копировать и вставить
|
||||||
|
termora.settings.terminal.right-click.nothing=никто
|
||||||
termora.settings.terminal.cursor-style=Вид курсора
|
termora.settings.terminal.cursor-style=Вид курсора
|
||||||
termora.settings.terminal.cursor-blink=Мигать курсором
|
termora.settings.terminal.cursor-blink=Мигать курсором
|
||||||
termora.settings.terminal.local-shell=Локальный терминал
|
termora.settings.terminal.local-shell=Локальный терминал
|
||||||
@@ -99,7 +100,7 @@ termora.settings.keymap=Горяиче клавиши
|
|||||||
termora.settings.keymap.shortcut=Комбинация
|
termora.settings.keymap.shortcut=Комбинация
|
||||||
termora.settings.keymap.action=Команда
|
termora.settings.keymap.action=Команда
|
||||||
termora.settings.keymap.already-exists=Комбинация [{0}] уже используется [{1}]
|
termora.settings.keymap.already-exists=Комбинация [{0}] уже используется [{1}]
|
||||||
|
termora.settings.keymap.question=Выберите строку и нажмите <font color=rgb({0},{1},{2})> ⌫ (клавишу Backspace)</font>, чтобы удалить сочетание клавиш
|
||||||
|
|
||||||
termora.settings.sftp.edit-command=Редактировать команду
|
termora.settings.sftp.edit-command=Редактировать команду
|
||||||
termora.settings.sftp.db-click-behavior=двойной щелчок
|
termora.settings.sftp.db-click-behavior=двойной щелчок
|
||||||
|
|||||||
@@ -73,6 +73,7 @@ termora.settings.terminal.hyperlink=超链接
|
|||||||
termora.settings.terminal.select-copy=选中复制
|
termora.settings.terminal.select-copy=选中复制
|
||||||
termora.settings.terminal.right-click=右键点击
|
termora.settings.terminal.right-click=右键点击
|
||||||
termora.settings.terminal.right-click.copy-and-paste=复制 & 粘贴
|
termora.settings.terminal.right-click.copy-and-paste=复制 & 粘贴
|
||||||
|
termora.settings.terminal.right-click.nothing=无操作
|
||||||
termora.settings.terminal.cursor-style=光标样式
|
termora.settings.terminal.cursor-style=光标样式
|
||||||
termora.settings.terminal.cursor-blink=光标闪烁
|
termora.settings.terminal.cursor-blink=光标闪烁
|
||||||
termora.settings.terminal.local-shell=本地终端
|
termora.settings.terminal.local-shell=本地终端
|
||||||
@@ -125,7 +126,7 @@ termora.settings.keymap=键盘
|
|||||||
termora.settings.keymap.shortcut=快捷键
|
termora.settings.keymap.shortcut=快捷键
|
||||||
termora.settings.keymap.action=操作
|
termora.settings.keymap.action=操作
|
||||||
termora.settings.keymap.already-exists=快捷键 [{0}] 已经被 [{1}] 占用
|
termora.settings.keymap.already-exists=快捷键 [{0}] 已经被 [{1}] 占用
|
||||||
|
termora.settings.keymap.question=选中一行,按下 <font color=rgb({0},{1},{2})> ⌫ (退格键)</font> 移除快捷键
|
||||||
|
|
||||||
termora.settings.sftp.edit-command=编辑命令
|
termora.settings.sftp.edit-command=编辑命令
|
||||||
termora.settings.sftp.db-click-behavior=双击行为
|
termora.settings.sftp.db-click-behavior=双击行为
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ termora.settings.keymap=鍵盤
|
|||||||
termora.settings.keymap.shortcut=快捷鍵
|
termora.settings.keymap.shortcut=快捷鍵
|
||||||
termora.settings.keymap.action=操作
|
termora.settings.keymap.action=操作
|
||||||
termora.settings.keymap.already-exists=快捷鍵 [{0}] 已經被 [{1}] 占用
|
termora.settings.keymap.already-exists=快捷鍵 [{0}] 已經被 [{1}] 占用
|
||||||
|
termora.settings.keymap.question=選取一行,按下 <font color=rgb({0},{1},{2})> ⌫ (退格鍵)</font> 移除快速鍵
|
||||||
|
|
||||||
termora.settings.sftp.edit-command=編輯命令
|
termora.settings.sftp.edit-command=編輯命令
|
||||||
termora.settings.sftp.db-click-behavior=按兩下行為
|
termora.settings.sftp.db-click-behavior=按兩下行為
|
||||||
@@ -84,6 +85,7 @@ termora.settings.terminal.hyperlink=超連結
|
|||||||
termora.settings.terminal.select-copy=選取複製
|
termora.settings.terminal.select-copy=選取複製
|
||||||
termora.settings.terminal.right-click=右鍵點擊
|
termora.settings.terminal.right-click=右鍵點擊
|
||||||
termora.settings.terminal.right-click.copy-and-paste=複製 & 貼上
|
termora.settings.terminal.right-click.copy-and-paste=複製 & 貼上
|
||||||
|
termora.settings.terminal.right-click.nothing=無操作
|
||||||
termora.settings.terminal.cursor-style=遊標風格
|
termora.settings.terminal.cursor-style=遊標風格
|
||||||
termora.settings.terminal.cursor-blink=遊標閃爍
|
termora.settings.terminal.cursor-blink=遊標閃爍
|
||||||
termora.settings.terminal.local-shell=本地端
|
termora.settings.terminal.local-shell=本地端
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
FROM linuxserver/openssh-server:9.3_p2-r1-ls147
|
FROM linuxserver/openssh-server:9.3_p2-r1-ls147
|
||||||
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories \
|
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.tuna.tsinghua.edu.cn/g' /etc/apk/repositories \
|
||||||
&& apk update && apk add wget tmux gcc zip p7zip g++ git make zsh htop stress-ng inetutils-telnet xclock xcalc xorg-server xinit && wget https://ohse.de/uwe/releases/lrzsz-0.12.20.tar.gz \
|
&& apk update && apk add wget tmux gcc zip p7zip g++ git make zsh htop stress-ng inetutils-telnet xclock xcalc xorg-server xinit && wget https://ohse.de/uwe/releases/lrzsz-0.12.20.tar.gz \
|
||||||
&& tar -xf lrzsz-0.12.20.tar.gz && cd lrzsz-0.12.20 && ./configure && make && make install \
|
&& tar -xf lrzsz-0.12.20.tar.gz && cd lrzsz-0.12.20 && ./configure && make && make install \
|
||||||
&& ln -s /usr/local/bin/lrz /usr/local/bin/rz && ln -s /usr/local/bin/lsz /usr/local/bin/sz
|
&& ln -s /usr/local/bin/lrz /usr/local/bin/rz && ln -s /usr/local/bin/lsz /usr/local/bin/sz
|
||||||
@@ -7,3 +7,6 @@ RUN sed -i 's/#AllowAgentForwarding yes/AllowAgentForwarding yes/g' /etc/ssh/ssh
|
|||||||
RUN sed -i 's/AllowTcpForwarding no/AllowTcpForwarding yes/g' /etc/ssh/sshd_config
|
RUN sed -i 's/AllowTcpForwarding no/AllowTcpForwarding yes/g' /etc/ssh/sshd_config
|
||||||
RUN sed -i 's/GatewayPorts no/GatewayPorts yes/g' /etc/ssh/sshd_config
|
RUN sed -i 's/GatewayPorts no/GatewayPorts yes/g' /etc/ssh/sshd_config
|
||||||
RUN sed -i 's/X11Forwarding no/X11Forwarding yes/g' /etc/ssh/sshd_config
|
RUN sed -i 's/X11Forwarding no/X11Forwarding yes/g' /etc/ssh/sshd_config
|
||||||
|
|
||||||
|
# docker build -t sshd .
|
||||||
|
# docker run --rm -it -e SUDO_ACCESS=true -e PASSWORD_ACCESS=true -e USER_PASSWORD=123456 -p 2222:2222 sshd
|
||||||
Reference in New Issue
Block a user