mirror of
https://github.com/TermoraDev/termora.git
synced 2026-01-16 10:22:58 +08:00
feat: supports importing hosts from electerm (#296)
This commit is contained in:
@@ -7,10 +7,8 @@ import app.termora.transport.SFTPAction
|
|||||||
import com.formdev.flatlaf.extras.components.FlatPopupMenu
|
import com.formdev.flatlaf.extras.components.FlatPopupMenu
|
||||||
import com.formdev.flatlaf.icons.FlatTreeClosedIcon
|
import com.formdev.flatlaf.icons.FlatTreeClosedIcon
|
||||||
import com.formdev.flatlaf.icons.FlatTreeOpenIcon
|
import com.formdev.flatlaf.icons.FlatTreeOpenIcon
|
||||||
import kotlinx.serialization.json.intOrNull
|
import kotlinx.serialization.Serializable
|
||||||
import kotlinx.serialization.json.jsonArray
|
import kotlinx.serialization.json.*
|
||||||
import kotlinx.serialization.json.jsonObject
|
|
||||||
import kotlinx.serialization.json.jsonPrimitive
|
|
||||||
import org.apache.commons.csv.CSVFormat
|
import org.apache.commons.csv.CSVFormat
|
||||||
import org.apache.commons.csv.CSVParser
|
import org.apache.commons.csv.CSVParser
|
||||||
import org.apache.commons.csv.CSVPrinter
|
import org.apache.commons.csv.CSVPrinter
|
||||||
@@ -366,6 +364,7 @@ class NewHostTree : JXTree() {
|
|||||||
val importMenu = JMenu(I18n.getString("termora.welcome.contextmenu.import"))
|
val importMenu = JMenu(I18n.getString("termora.welcome.contextmenu.import"))
|
||||||
val csvMenu = importMenu.add("CSV")
|
val csvMenu = importMenu.add("CSV")
|
||||||
val xShellMenu = importMenu.add("Xshell")
|
val xShellMenu = importMenu.add("Xshell")
|
||||||
|
val electermMenu = importMenu.add("electerm")
|
||||||
val finalShellMenu = importMenu.add("FinalShell")
|
val finalShellMenu = importMenu.add("FinalShell")
|
||||||
val windTermMenu = importMenu.add("WindTerm")
|
val windTermMenu = importMenu.add("WindTerm")
|
||||||
val secureCRTMenu = importMenu.add("SecureCRT")
|
val secureCRTMenu = importMenu.add("SecureCRT")
|
||||||
@@ -399,6 +398,7 @@ class NewHostTree : JXTree() {
|
|||||||
|
|
||||||
xShellMenu.addActionListener { importHosts(lastNode, ImportType.Xshell) }
|
xShellMenu.addActionListener { importHosts(lastNode, ImportType.Xshell) }
|
||||||
secureCRTMenu.addActionListener { importHosts(lastNode, ImportType.SecureCRT) }
|
secureCRTMenu.addActionListener { importHosts(lastNode, ImportType.SecureCRT) }
|
||||||
|
electermMenu.addActionListener { importHosts(lastNode, ImportType.electerm) }
|
||||||
mobaXtermMenu.addActionListener { importHosts(lastNode, ImportType.MobaXterm) }
|
mobaXtermMenu.addActionListener { importHosts(lastNode, ImportType.MobaXterm) }
|
||||||
finalShellMenu.addActionListener { importHosts(lastNode, ImportType.FinalShell) }
|
finalShellMenu.addActionListener { importHosts(lastNode, ImportType.FinalShell) }
|
||||||
csvMenu.addActionListener { importHosts(lastNode, ImportType.CSV) }
|
csvMenu.addActionListener { importHosts(lastNode, ImportType.CSV) }
|
||||||
@@ -651,6 +651,7 @@ class NewHostTree : JXTree() {
|
|||||||
ImportType.WindTerm -> chooser.fileFilter = FileNameExtensionFilter("WindTerm (*.sessions)", "sessions")
|
ImportType.WindTerm -> chooser.fileFilter = FileNameExtensionFilter("WindTerm (*.sessions)", "sessions")
|
||||||
ImportType.CSV -> chooser.fileFilter = FileNameExtensionFilter("CSV (*.csv)", "csv")
|
ImportType.CSV -> chooser.fileFilter = FileNameExtensionFilter("CSV (*.csv)", "csv")
|
||||||
ImportType.SecureCRT -> chooser.fileFilter = FileNameExtensionFilter("SecureCRT (*.xml)", "xml")
|
ImportType.SecureCRT -> chooser.fileFilter = FileNameExtensionFilter("SecureCRT (*.xml)", "xml")
|
||||||
|
ImportType.electerm -> chooser.fileFilter = FileNameExtensionFilter("electerm (*.json)", "json")
|
||||||
ImportType.MobaXterm -> chooser.fileFilter =
|
ImportType.MobaXterm -> chooser.fileFilter =
|
||||||
FileNameExtensionFilter("MobaXterm (*.mobaconf,*.ini)", "ini", "mobaconf")
|
FileNameExtensionFilter("MobaXterm (*.mobaconf,*.ini)", "ini", "mobaconf")
|
||||||
|
|
||||||
@@ -730,6 +731,7 @@ class NewHostTree : JXTree() {
|
|||||||
ImportType.MobaXterm -> parseFromMobaXterm(folder, file)
|
ImportType.MobaXterm -> parseFromMobaXterm(folder, file)
|
||||||
ImportType.Xshell -> parseFromXshell(folder, file)
|
ImportType.Xshell -> parseFromXshell(folder, file)
|
||||||
ImportType.FinalShell -> parseFromFinalShell(folder, file)
|
ImportType.FinalShell -> parseFromFinalShell(folder, file)
|
||||||
|
ImportType.electerm -> parseFromElecterm(folder, file)
|
||||||
ImportType.CSV -> file.bufferedReader().use { parseFromCSV(folder, it) }
|
ImportType.CSV -> file.bufferedReader().use { parseFromCSV(folder, it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -923,6 +925,57 @@ class NewHostTree : JXTree() {
|
|||||||
return parseFromCSV(folder, StringReader(sw.toString()))
|
return parseFromCSV(folder, StringReader(sw.toString()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
private data class ElectermGroup(
|
||||||
|
val id: String = StringUtils.EMPTY,
|
||||||
|
val title: String = StringUtils.EMPTY,
|
||||||
|
val bookmarkIds: Set<String> = emptySet(),
|
||||||
|
val bookmarkGroupIds: Set<String> = emptySet(),
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun parseFromElecterm(folder: HostTreeNode, file: File): List<HostTreeNode> {
|
||||||
|
val json = ohMyJson.parseToJsonElement(file.readText()).jsonObject
|
||||||
|
val bookmarks = json["bookmarks"]?.jsonArray ?: return emptyList()
|
||||||
|
val bookmarkGroups = ohMyJson.decodeFromJsonElement<List<ElectermGroup>>(
|
||||||
|
json["bookmarkGroups"]?.jsonArray ?: JsonArray(emptyList())
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
val sw = StringWriter()
|
||||||
|
CSVPrinter(sw, CSVFormat.EXCEL.builder().setHeader(*CSV_HEADERS).get()).use { printer ->
|
||||||
|
for (i in 0 until bookmarks.size) {
|
||||||
|
val host = bookmarks[i].jsonObject
|
||||||
|
val type = host["type"]?.jsonPrimitive?.content ?: "SSH"
|
||||||
|
if (!StringUtils.equalsIgnoreCase(type, "SSH")) continue
|
||||||
|
val hostname = host["host"]?.jsonPrimitive?.content ?: StringUtils.EMPTY
|
||||||
|
val id = host["id"]?.jsonPrimitive?.content ?: continue
|
||||||
|
val title = host["title"]?.jsonPrimitive?.content ?: StringUtils.EMPTY
|
||||||
|
if (StringUtils.isAllBlank(title, hostname)) continue
|
||||||
|
val username = host["username"]?.jsonPrimitive?.content ?: StringUtils.EMPTY
|
||||||
|
val port = host["port"]?.jsonPrimitive?.intOrNull ?: 22
|
||||||
|
|
||||||
|
val folderNames = mutableListOf<String>()
|
||||||
|
var group = bookmarkGroups.find { it.bookmarkIds.contains(id) }
|
||||||
|
while (group != null && group.id != "default") {
|
||||||
|
folderNames.addFirst(group.title)
|
||||||
|
group = bookmarkGroups.find { it.bookmarkGroupIds.contains(group?.id ?: StringUtils.EMPTY) }
|
||||||
|
}
|
||||||
|
|
||||||
|
printer.printRecord(
|
||||||
|
folderNames.joinToString("/"),
|
||||||
|
StringUtils.defaultIfBlank(title, hostname),
|
||||||
|
hostname,
|
||||||
|
port,
|
||||||
|
username,
|
||||||
|
"SSH"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return parseFromCSV(folder, StringReader(sw.toString()))
|
||||||
|
}
|
||||||
|
|
||||||
private fun parseFromCSV(folderNode: HostTreeNode, sr: Reader): List<HostTreeNode> {
|
private fun parseFromCSV(folderNode: HostTreeNode, sr: Reader): List<HostTreeNode> {
|
||||||
val records = CSVParser.builder()
|
val records = CSVParser.builder()
|
||||||
.setFormat(CSVFormat.EXCEL.builder().setHeader(*CSV_HEADERS).setSkipHeaderRecord(true).get())
|
.setFormat(CSVFormat.EXCEL.builder().setHeader(*CSV_HEADERS).setSkipHeaderRecord(true).get())
|
||||||
@@ -1005,6 +1058,7 @@ class NewHostTree : JXTree() {
|
|||||||
SecureCRT,
|
SecureCRT,
|
||||||
MobaXterm,
|
MobaXterm,
|
||||||
FinalShell,
|
FinalShell,
|
||||||
|
electerm,
|
||||||
}
|
}
|
||||||
|
|
||||||
private class MoveHostTransferable(val nodes: List<HostTreeNode>) : Transferable {
|
private class MoveHostTransferable(val nodes: List<HostTreeNode>) : Transferable {
|
||||||
|
|||||||
Reference in New Issue
Block a user