diff --git a/app/src/main/java/io/xpipe/app/prefs/AppPrefs.java b/app/src/main/java/io/xpipe/app/prefs/AppPrefs.java index 7d74d7ed4..58e790a6a 100644 --- a/app/src/main/java/io/xpipe/app/prefs/AppPrefs.java +++ b/app/src/main/java/io/xpipe/app/prefs/AppPrefs.java @@ -97,6 +97,8 @@ public class AppPrefs { map(new SimpleBooleanProperty(true), "automaticallyCheckForUpdates", Boolean.class); final BooleanProperty encryptAllVaultData = mapVaultSpecific(new SimpleBooleanProperty(false), "encryptAllVaultData", Boolean.class); + final BooleanProperty enableTerminalLogging = + map(new SimpleBooleanProperty(false), "enableTerminalLogging", Boolean.class); final BooleanProperty enforceWindowModality = map(new SimpleBooleanProperty(false), "enforceWindowModality", Boolean.class); final BooleanProperty condenseConnectionDisplay = @@ -145,6 +147,10 @@ public class AppPrefs { final BooleanProperty disableApiAuthentication = map(new SimpleBooleanProperty(false), "disableApiAuthentication", Boolean.class); + public ObservableBooleanValue enableTerminalLogging() { + return enableTerminalLogging; + } + public ObservableStringValue apiKey() { return apiKey; } @@ -192,6 +198,7 @@ public class AppPrefs { new RdpCategory(), new SshCategory(), new LocalShellCategory(), + new LoggingCategory(), new SecurityCategory(), new HttpApiCategory(), new WorkflowCategory(), diff --git a/app/src/main/java/io/xpipe/app/prefs/LoggingCategory.java b/app/src/main/java/io/xpipe/app/prefs/LoggingCategory.java new file mode 100644 index 000000000..c887d806d --- /dev/null +++ b/app/src/main/java/io/xpipe/app/prefs/LoggingCategory.java @@ -0,0 +1,42 @@ +package io.xpipe.app.prefs; + +import io.xpipe.app.comp.base.ButtonComp; +import io.xpipe.app.core.AppI18n; +import io.xpipe.app.core.AppProperties; +import io.xpipe.app.fxcomps.Comp; +import io.xpipe.app.issue.ErrorEvent; +import io.xpipe.app.storage.DataStorage; +import io.xpipe.app.util.DesktopHelper; +import io.xpipe.app.util.OptionsBuilder; + +import java.io.IOException; +import java.nio.file.Files; + +public class LoggingCategory extends AppPrefsCategory { + + @Override + protected String getId() { + return "logging"; + } + + @Override + protected Comp create() { + var prefs = AppPrefs.get(); + return new OptionsBuilder() + .addTitle("sessionLogging") + .sub(new OptionsBuilder() + .nameAndDescription("enableTerminalLogging") + .addToggle(prefs.enableTerminalLogging) + .nameAndDescription("terminalLoggingDirectory") + .addComp(new ButtonComp(AppI18n.observable("openSessionLogs"), () -> { + var dir = AppProperties.get().getDataDir().resolve("sessions"); + try { + Files.createDirectories(dir); + DesktopHelper.browsePathLocal(dir); + } catch (IOException e) { + ErrorEvent.fromThrowable(e).handle(); + } + }).disable(prefs.enableTerminalLogging.not()))) + .buildComp(); + } +} diff --git a/app/src/main/java/io/xpipe/app/terminal/WindowsTerminalType.java b/app/src/main/java/io/xpipe/app/terminal/WindowsTerminalType.java index ac6580800..edb1ad374 100644 --- a/app/src/main/java/io/xpipe/app/terminal/WindowsTerminalType.java +++ b/app/src/main/java/io/xpipe/app/terminal/WindowsTerminalType.java @@ -20,17 +20,11 @@ public interface WindowsTerminalType extends ExternalTerminalType { // So just remove that slash var fixedName = FileNames.removeTrailingSlash(configuration.getColoredTitle()); - var toExec = !ShellDialects.isPowershell(LocalShell.getShell()) - ? CommandBuilder.of().addFile(configuration.getScriptFile()) - : CommandBuilder.of() - .add("powershell", "-ExecutionPolicy", "Bypass", "-File") - .addFile(configuration.getScriptFile()); var cmd = CommandBuilder.of().add("-w", "1", "nt"); - if (configuration.getColor() != null) { cmd.add("--tabColor").addQuoted(configuration.getColor().toHexString()); } - return cmd.add("--title").addQuoted(fixedName).add(toExec); + return cmd.add("--title").addQuoted(fixedName).add(configuration.getDialectLaunchCommand()); } @Override diff --git a/app/src/main/java/io/xpipe/app/util/TerminalLauncher.java b/app/src/main/java/io/xpipe/app/util/TerminalLauncher.java index 354df065d..031c938f8 100644 --- a/app/src/main/java/io/xpipe/app/util/TerminalLauncher.java +++ b/app/src/main/java/io/xpipe/app/util/TerminalLauncher.java @@ -1,16 +1,23 @@ package io.xpipe.app.util; import io.xpipe.app.core.AppI18n; +import io.xpipe.app.core.AppProperties; import io.xpipe.app.ext.ProcessControlProvider; import io.xpipe.app.issue.ErrorEvent; import io.xpipe.app.prefs.AppPrefs; +import io.xpipe.app.resources.FileAutoSystemIcon; import io.xpipe.app.storage.DataStorage; import io.xpipe.app.storage.DataStoreEntry; import io.xpipe.app.terminal.ExternalTerminalType; import io.xpipe.core.process.*; +import io.xpipe.core.store.FilePath; import io.xpipe.core.util.FailableFunction; import java.io.IOException; +import java.nio.file.Files; +import java.time.Instant; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; import java.util.List; import java.util.UUID; @@ -27,8 +34,8 @@ public class TerminalLauncher { public static void openDirect( String title, FailableFunction command, ExternalTerminalType type) - throws Exception { - try (var sc = LocalShell.getShell().start()) { + throws Exception { + try (var sc = LocalShell.getShell().start()) { var script = ScriptHelper.constructTerminalInitFile( sc.getShellDialect(), sc, @@ -64,13 +71,8 @@ public class TerminalLauncher { adjustedTitle, type.shouldClear() && AppPrefs.get().clearTerminalOnInit().get(), cc instanceof ShellControl ? type.additionalInitCommands() : TerminalInitFunction.none()); - var request = UUID.randomUUID(); - var d = ProcessControlProvider.get().getEffectiveLocalDialect(); - var launcherScript = d.terminalLauncherScript(request, adjustedTitle); - var preparationScript = ScriptHelper.createLocalExecScript(launcherScript); - var config = new ExternalTerminalType.LaunchConfiguration( - entry != null ? color : null, adjustedTitle, cleanTitle, preparationScript, d); + var config = createConfig(request, entry, cleanTitle, adjustedTitle); var latch = TerminalLauncherManager.submitAsync(request, cc, terminalConfig, directory); try { type.launch(config); @@ -80,4 +82,50 @@ public class TerminalLauncher { throw ErrorEvent.expected(new IOException("Unable to launch terminal " + type.toTranslatedString().getValue() + ": " + modMsg, ex)); } } + + + private static final DateTimeFormatter DATE_FORMATTER = + DateTimeFormatter.ofPattern("yyyy-MM-dd_HH-mm-ss").withZone(ZoneId.systemDefault()); + + private static ExternalTerminalType.LaunchConfiguration createConfig(UUID request, DataStoreEntry entry, String cleanTitle, String adjustedTitle) throws + Exception { + var color = entry != null ? DataStorage.get().getEffectiveColor(entry) : null; + var d = ProcessControlProvider.get().getEffectiveLocalDialect(); + var launcherScript = d.terminalLauncherScript(request, adjustedTitle); + var preparationScript = ScriptHelper.createLocalExecScript(launcherScript); + + if (!AppPrefs.get().enableTerminalLogging().get()) { + var config = new ExternalTerminalType.LaunchConfiguration( + entry != null ? color : null, adjustedTitle, cleanTitle, preparationScript, d); + return config; + } + + var logDir = AppProperties.get().getDataDir().resolve("sessions"); + Files.createDirectories(logDir); + var logFile = logDir.resolve(new FilePath(DataStorage.get().getStoreEntryDisplayName(entry) + " (" + + DATE_FORMATTER.format(Instant.now()) + ").log").fileSystemCompatible(OsType.getLocal()).toString()); + try (var sc = LocalShell.getShell().start()) { + if (OsType.getLocal() == OsType.WINDOWS) { + var content = """ + echo 'Transcript started, output file is "sessions\\%s"' + Start-Transcript -Force -LiteralPath "%s" > $Out-Null + & %s + Stop-Transcript > $Out-Null + echo 'Transcript stopped, output file is "sessions\\%s"' + """.formatted(logFile.getFileName().toString(), logFile, preparationScript, logFile.getFileName().toString()); + var ps = ScriptHelper.createExecScript(ShellDialects.POWERSHELL,sc, content); + var config = new ExternalTerminalType.LaunchConfiguration( + entry != null ? color : null, adjustedTitle, cleanTitle, ps, ShellDialects.POWERSHELL); + return config; + } else { + var content = """ + script -Command "%s" "%s" + """.formatted(preparationScript, logFile); + var ps = ScriptHelper.createExecScript(sc.getShellDialect(), sc, content); + var config = new ExternalTerminalType.LaunchConfiguration( + entry != null ? color : null, adjustedTitle, cleanTitle, ps, sc.getShellDialect()); + return config; + } + } + } } diff --git a/lang/app/strings/translations_da.properties b/lang/app/strings/translations_da.properties index 9911e5a50..26b0098b7 100644 --- a/lang/app/strings/translations_da.properties +++ b/lang/app/strings/translations_da.properties @@ -531,3 +531,10 @@ dontAllowTerminalRestartDescription=Som standard kan terminalsessioner genstarte openDocumentation=Åben dokumentation openDocumentationDescription=Besøg XPipes dokumentationsside for dette problem renameAll=Omdøb alle +logging=Logning +enableTerminalLogging=Aktiver terminal-logning +enableTerminalLoggingDescription=Aktiverer logning på klientsiden for alle terminalsessioner. Alle inputs og outputs fra terminalsessionen skrives ind i en sessionslogfil. Bemærk, at følsomme oplysninger som f.eks. adgangskodeprompter ikke registreres. +terminalLoggingDirectory=Logfiler for terminalsessioner +terminalLoggingDirectoryDescription=Alle logfiler gemmes i XPipe-databiblioteket på dit lokale system. +openSessionLogs=Åbne sessionslogfiler +sessionLogging=Logning af sessioner diff --git a/lang/app/strings/translations_de.properties b/lang/app/strings/translations_de.properties index 43c4947c3..f6ff93f56 100644 --- a/lang/app/strings/translations_de.properties +++ b/lang/app/strings/translations_de.properties @@ -525,3 +525,10 @@ dontAllowTerminalRestartDescription=Standardmäßig können Terminalsitzungen ne openDocumentation=Offene Dokumentation openDocumentationDescription=Besuche die XPipe-Dokumentationsseite zu diesem Thema renameAll=Alle umbenennen +logging=Protokollierung +enableTerminalLogging=Terminalprotokollierung einschalten +enableTerminalLoggingDescription=Aktiviert die clientseitige Protokollierung für alle Terminalsitzungen. Alle Eingaben und Ausgaben der Terminalsitzung werden in eine Sitzungsprotokolldatei geschrieben. Beachte, dass sensible Informationen wie Passwortabfragen nicht aufgezeichnet werden. +terminalLoggingDirectory=Terminal-Sitzungsprotokolle +terminalLoggingDirectoryDescription=Alle Protokolle werden in dem XPipe-Datenverzeichnis auf deinem lokalen System gespeichert. +openSessionLogs=Sitzungsprotokolle öffnen +sessionLogging=Sitzungsprotokollierung diff --git a/lang/app/strings/translations_en.properties b/lang/app/strings/translations_en.properties index 2ef94ec4a..a6eaa24af 100644 --- a/lang/app/strings/translations_en.properties +++ b/lang/app/strings/translations_en.properties @@ -531,3 +531,10 @@ dontAllowTerminalRestartDescription=By default, terminal sessions can be restart openDocumentation=Open documentation openDocumentationDescription=Visit the XPipe docs page for this issue renameAll=Rename all +logging=Logging +enableTerminalLogging=Enable terminal logging +enableTerminalLoggingDescription=Enables client-side logging for all terminal sessions. All inputs and outputs of the terminal session are written into a session log file. Note that any sensitive information like password prompts are not recorded. +terminalLoggingDirectory=Terminal session logs +terminalLoggingDirectoryDescription=All logs are stored in the XPipe data directory on your local system. +openSessionLogs=Open session logs +sessionLogging=Session logging diff --git a/lang/app/strings/translations_es.properties b/lang/app/strings/translations_es.properties index e9ae8f3d1..174c2fbe9 100644 --- a/lang/app/strings/translations_es.properties +++ b/lang/app/strings/translations_es.properties @@ -512,3 +512,10 @@ dontAllowTerminalRestartDescription=Por defecto, las sesiones de terminal pueden openDocumentation=Documentación abierta openDocumentationDescription=Visita la página de documentación de XPipe sobre este tema renameAll=Renombrar todo +logging=Registro +enableTerminalLogging=Activar el registro de terminal +enableTerminalLoggingDescription=Activa el registro del lado del cliente para todas las sesiones de terminal. Todas las entradas y salidas de la sesión de terminal se escriben en un archivo de registro de sesión. Ten en cuenta que no se registra ninguna información sensible, como las solicitudes de contraseña. +terminalLoggingDirectory=Registros de sesión de terminal +terminalLoggingDirectoryDescription=Todos los registros se almacenan en el directorio de datos de XPipe en tu sistema local. +openSessionLogs=Registros de sesión abiertos +sessionLogging=Registro de sesión diff --git a/lang/app/strings/translations_fr.properties b/lang/app/strings/translations_fr.properties index fdcacf96c..a3d26a85f 100644 --- a/lang/app/strings/translations_fr.properties +++ b/lang/app/strings/translations_fr.properties @@ -512,3 +512,10 @@ dontAllowTerminalRestartDescription=Par défaut, les sessions de terminal peuven openDocumentation=Documentation ouverte openDocumentationDescription=Visite la page de documentation de XPipe pour ce problème renameAll=Renommer tout +logging=Enregistrement +enableTerminalLogging=Activer la journalisation du terminal +enableTerminalLoggingDescription=Active la journalisation côté client pour toutes les sessions de terminal. Toutes les entrées et sorties de la session du terminal sont écrites dans un fichier journal de la session. Note que les informations sensibles telles que les invites de mot de passe ne sont pas enregistrées. +terminalLoggingDirectory=Journaux de session de terminal +terminalLoggingDirectoryDescription=Tous les journaux sont stockés dans le répertoire de données de XPipe sur ton système local. +openSessionLogs=Ouvrir les journaux de session +sessionLogging=Enregistrement de session diff --git a/lang/app/strings/translations_it.properties b/lang/app/strings/translations_it.properties index 0739361da..6b763121c 100644 --- a/lang/app/strings/translations_it.properties +++ b/lang/app/strings/translations_it.properties @@ -512,3 +512,10 @@ dontAllowTerminalRestartDescription=Per impostazione predefinita, le sessioni de openDocumentation=Documentazione aperta openDocumentationDescription=Visita la pagina dei documenti di XPipe per questo problema renameAll=Rinomina tutti +logging=Registrazione +enableTerminalLogging=Abilita la registrazione del terminale +enableTerminalLoggingDescription=Abilita la registrazione lato client per tutte le sessioni del terminale. Tutti gli input e gli output della sessione del terminale vengono scritti in un file di log della sessione. Le informazioni sensibili, come le richieste di password, non vengono registrate. +terminalLoggingDirectory=Registri di sessione del terminale +terminalLoggingDirectoryDescription=Tutti i registri vengono memorizzati nella directory dei dati di XPipe sul tuo sistema locale. +openSessionLogs=Registri di sessione aperti +sessionLogging=Registrazione della sessione diff --git a/lang/app/strings/translations_ja.properties b/lang/app/strings/translations_ja.properties index ed762e96c..04d86e9cc 100644 --- a/lang/app/strings/translations_ja.properties +++ b/lang/app/strings/translations_ja.properties @@ -512,3 +512,10 @@ dontAllowTerminalRestartDescription=デフォルトでは、ターミナル・ openDocumentation=ドキュメントを開く openDocumentationDescription=この問題のXPipeドキュメントページを見る renameAll=すべての名前を変更する +logging=ロギング +enableTerminalLogging=ターミナルロギングを有効にする +enableTerminalLoggingDescription=すべての端末セッションのクライアント側ログを有効にする。端末セッションのすべての入力と出力がセッションログファイルに書き込まれる。パスワードプロンプトのような機密情報は記録されないことに注意。 +terminalLoggingDirectory=端末のセッションログ +terminalLoggingDirectoryDescription=すべてのログは、ローカルシステムのXPipeデータディレクトリに保存される。 +openSessionLogs=セッションログを開く +sessionLogging=セッションロギング diff --git a/lang/app/strings/translations_nl.properties b/lang/app/strings/translations_nl.properties index b25cafaed..fa0c5b67a 100644 --- a/lang/app/strings/translations_nl.properties +++ b/lang/app/strings/translations_nl.properties @@ -512,3 +512,10 @@ dontAllowTerminalRestartDescription=Standaard kunnen terminalsessies opnieuw wor openDocumentation=Open documentatie openDocumentationDescription=Bezoek de XPipe docs pagina voor dit probleem renameAll=Hernoem alle +logging=Loggen +enableTerminalLogging=Terminal logging inschakelen +enableTerminalLoggingDescription=Schakelt client-side logging in voor alle terminalsessies. Alle inputs en outputs van de terminalsessie worden in een sessie logbestand geschreven. Merk op dat gevoelige informatie zoals wachtwoordaanvragen niet worden geregistreerd. +terminalLoggingDirectory=Terminal sessie logs +terminalLoggingDirectoryDescription=Alle logs worden opgeslagen in de XPipe datamap op je lokale systeem. +openSessionLogs=Open sessie logs +sessionLogging=Sessie loggen diff --git a/lang/app/strings/translations_pt.properties b/lang/app/strings/translations_pt.properties index 00a657ed2..98124cb59 100644 --- a/lang/app/strings/translations_pt.properties +++ b/lang/app/strings/translations_pt.properties @@ -512,3 +512,10 @@ dontAllowTerminalRestartDescription=Por defeito, as sessões de terminal podem s openDocumentation=Abre a documentação openDocumentationDescription=Visita a página de documentação do XPipe para esta questão renameAll=Renomeia tudo +logging=Registo +enableTerminalLogging=Ativar o registo de terminal +enableTerminalLoggingDescription=Ativa o registo do lado do cliente para todas as sessões de terminal. Todas as entradas e saídas da sessão de terminal são gravadas em um arquivo de log de sessão. Nota que qualquer informação sensível, como pedidos de palavra-passe, não é registada. +terminalLoggingDirectory=Registos de sessões de terminal +terminalLoggingDirectoryDescription=Todos os registos são armazenados no diretório de dados do XPipe no teu sistema local. +openSessionLogs=Abre os registos da sessão +sessionLogging=Registo de sessões diff --git a/lang/app/strings/translations_ru.properties b/lang/app/strings/translations_ru.properties index dc2cac910..c0ca6ab43 100644 --- a/lang/app/strings/translations_ru.properties +++ b/lang/app/strings/translations_ru.properties @@ -512,3 +512,10 @@ dontAllowTerminalRestartDescription=По умолчанию терминальн openDocumentation=Открытая документация openDocumentationDescription=Посетите страницу документации XPipe по этому вопросу renameAll=Переименовать все +logging=Ведение журнала +enableTerminalLogging=Включить ведение журнала терминала +enableTerminalLoggingDescription=Включает ведение журнала на стороне клиента для всех терминальных сессий. Все входы и выходы терминальной сессии записываются в файл журнала сессии. Обрати внимание, что любая конфиденциальная информация вроде запросов пароля не записывается. +terminalLoggingDirectory=Журналы терминальных сессий +terminalLoggingDirectoryDescription=Все журналы хранятся в каталоге данных XPipe в твоей локальной системе. +openSessionLogs=Открытые журналы сеансов +sessionLogging=Ведение журнала сеансов diff --git a/lang/app/strings/translations_tr.properties b/lang/app/strings/translations_tr.properties index be51fd1a5..7cd9fe0aa 100644 --- a/lang/app/strings/translations_tr.properties +++ b/lang/app/strings/translations_tr.properties @@ -513,3 +513,10 @@ dontAllowTerminalRestartDescription=Varsayılan olarak, terminal oturumları ter openDocumentation=Açık dokümantasyon openDocumentationDescription=Bu sorun için XPipe dokümanlar sayfasını ziyaret edin renameAll=Tümünü yeniden adlandır +logging=Günlük kaydı +enableTerminalLogging=Terminal günlüğünü etkinleştirme +enableTerminalLoggingDescription=Tüm terminal oturumları için istemci tarafı günlüğünü etkinleştirir. Terminal oturumunun tüm girdileri ve çıktıları bir oturum günlüğü dosyasına yazılır. Parola istemleri gibi hassas bilgilerin kaydedilmediğini unutmayın. +terminalLoggingDirectory=Terminal oturum günlükleri +terminalLoggingDirectoryDescription=Tüm günlükler yerel sisteminizdeki XPipe veri dizininde saklanır. +openSessionLogs=Oturum günlüklerini açın +sessionLogging=Oturum kaydı diff --git a/lang/app/strings/translations_zh.properties b/lang/app/strings/translations_zh.properties index f4bb880f0..4cb7e7232 100644 --- a/lang/app/strings/translations_zh.properties +++ b/lang/app/strings/translations_zh.properties @@ -512,3 +512,10 @@ dontAllowTerminalRestartDescription=默认情况下,终端会话可以在终 openDocumentation=开放文档 openDocumentationDescription=访问 XPipe 文档页面了解这一问题 renameAll=重命名所有 +logging=记录 +enableTerminalLogging=启用终端日志记录 +enableTerminalLoggingDescription=为所有终端会话启用客户端日志。终端会话的所有输入和输出都会写入会话日志文件。请注意,任何敏感信息(如密码提示)都不会被记录。 +terminalLoggingDirectory=终端会话日志 +terminalLoggingDirectoryDescription=所有日志都存储在本地系统的 XPipe 数据目录中。 +openSessionLogs=打开会话日志 +sessionLogging=会话记录