package com.example.plugin; import com.intellij.credentialStore.CredentialAttributes; import com.intellij.ide.passwordSafe.PasswordSafe; import com.intellij.openapi.components.PersistentStateComponent; import com.intellij.openapi.components.State; import com.intellij.openapi.components.Storage; import com.intellij.util.xmlb.XmlSerializerUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; import java.util.List; import java.util.Optional; @State( name = "SshServerManager", storages = { @Storage("ssh-servers.xml") } ) public class SshServerManager implements PersistentStateComponent { public static class State { public List servers = new ArrayList<>(); public String currentServerId; } private State myState = new State(); @Nullable @Override public State getState() { return myState; } @Override public void loadState(@NotNull State state) { XmlSerializerUtil.copyBean(state, myState); } // ---------- CRUD ---------- public List getServers() { return myState.servers; } public Optional findServer(String id) { return myState.servers.stream().filter(s -> s.id.equals(id)).findFirst(); } public void addServer(SshServer server) { myState.servers.add(server); } public void removeServer(String id) { myState.servers.removeIf(s -> s.id.equals(id)); if (id.equals(myState.currentServerId)) { myState.currentServerId = null; } PasswordSafe.getInstance().setPassword(createCredentialAttributes(id), null); } public void updateServer(SshServer updated) { for (int i = 0; i < myState.servers.size(); i++) { if (myState.servers.get(i).id.equals(updated.id)) { myState.servers.set(i, updated); break; } } } // ---------- Текущий сервер ---------- public String getCurrentServerId() { return myState.currentServerId; } public void setCurrentServerId(String id) { myState.currentServerId = id; } public Optional getCurrentServer() { return Optional.ofNullable(myState.currentServerId).flatMap(this::findServer); } // ---------- Пароль ---------- public String getPassword(String serverId) { return PasswordSafe.getInstance().getPassword(createCredentialAttributes(serverId)); } public void setPassword(String serverId, String password) { PasswordSafe.getInstance().setPassword(createCredentialAttributes(serverId), password); } private CredentialAttributes createCredentialAttributes(String serverId) { return new CredentialAttributes("SshRemotePlugin.server." + serverId); } // ---------- Singleton ---------- public static SshServerManager getInstance() { return com.intellij.openapi.application.ApplicationManager.getApplication() .getService(SshServerManager.class); } // ---------- Вспомогательные статические методы ---------- /** * Возвращает локальный путь к папке проекта на основе хоста, имени сервера и удалённой рабочей директории. */ public static String getLocalProjectPath(SshServer server) { Path path = Paths.get(System.getProperty("user.home"), "ssh-remote-projects", sanitize(server.host), sanitize(server.name)); if (server.remoteProjectPath != null && !server.remoteProjectPath.isBlank()) { String normalized = server.remoteProjectPath.replace('\\', '/').replaceAll("/+", "/"); for (String segment : normalized.split("/")) { if (!segment.isEmpty()) { path = path.resolve(sanitize(segment)); } } } return path.toString().replace('\\', '/'); } /** * Заменяет в строке все символы, кроме букв, цифр, точки и дефиса, на '_'. */ public static String sanitize(String input) { return input.replaceAll("[^a-zA-Z0-9.-]", "_"); } }