174 lines
8.0 KiB
Java
174 lines
8.0 KiB
Java
package com.example.plugin;
|
||
|
||
import com.intellij.openapi.application.ApplicationManager;
|
||
import com.intellij.openapi.progress.ProgressIndicator;
|
||
import com.intellij.openapi.progress.ProgressManager;
|
||
import com.intellij.openapi.progress.Task;
|
||
import com.intellij.openapi.project.Project;
|
||
import com.intellij.openapi.project.ProjectManager;
|
||
import com.intellij.openapi.project.ProjectManagerListener;
|
||
import com.intellij.openapi.startup.StartupActivity;
|
||
import com.intellij.openapi.ui.Messages;
|
||
import com.intellij.openapi.vfs.*;
|
||
import com.jcraft.jsch.*;
|
||
import org.jetbrains.annotations.NotNull;
|
||
|
||
import java.nio.file.Paths;
|
||
import java.util.Optional;
|
||
import java.util.concurrent.ExecutorService;
|
||
import java.util.concurrent.Executors;
|
||
|
||
@SuppressWarnings("deprecation")
|
||
public class SshSyncStartupActivity implements StartupActivity {
|
||
|
||
private static final ExecutorService uploadExecutor = Executors.newSingleThreadExecutor();
|
||
|
||
@Override
|
||
public void runActivity(@NotNull Project project) {
|
||
// При закрытии проекта разрываем SFTP-подключение
|
||
project.getMessageBus().connect().subscribe(ProjectManager.TOPIC, new ProjectManagerListener() {
|
||
@Override
|
||
public void projectClosed(@NotNull Project closedProject) {
|
||
if (closedProject.equals(project)) {
|
||
SftpSessionManager.getInstance(project).disconnect();
|
||
}
|
||
}
|
||
});
|
||
|
||
// Слушатель изменений файлов для синхронизации
|
||
VirtualFile baseDir = project.getBaseDir();
|
||
if (baseDir != null) {
|
||
VirtualFileManager.getInstance().addVirtualFileListener(new VirtualFileListener() {
|
||
@Override
|
||
public void contentsChanged(@NotNull VirtualFileEvent event) {
|
||
syncFile(event.getFile(), project);
|
||
}
|
||
|
||
@Override
|
||
public void fileCreated(@NotNull VirtualFileEvent event) {
|
||
syncFile(event.getFile(), project);
|
||
}
|
||
|
||
private void syncFile(VirtualFile file, Project project) {
|
||
if (!file.isDirectory()) {
|
||
uploadFile(file, project);
|
||
}
|
||
}
|
||
}, project);
|
||
}
|
||
|
||
// === Автосинхронизация при открытии проекта ===
|
||
String projectPath = project.getBasePath();
|
||
if (projectPath != null) {
|
||
String serverId = ProjectServerMapping.getInstance().getServerIdForProject(projectPath);
|
||
if (serverId != null) {
|
||
Optional<SshServer> maybeServer = SshServerManager.getInstance().findServer(serverId);
|
||
if (maybeServer.isPresent()) {
|
||
SshServer server = maybeServer.get();
|
||
// Модальный диалог – блокирует IDE до завершения синхронизации
|
||
ApplicationManager.getApplication().invokeLater(() -> {
|
||
ProgressManager.getInstance().run(new Task.Modal(project, "Synchronizing with " + server.name, true) {
|
||
@Override
|
||
public void run(@NotNull ProgressIndicator indicator) {
|
||
try {
|
||
String localRoot = Paths.get(System.getProperty("user.home"), "ssh-remote-projects",
|
||
sanitize(server.host), sanitize(server.remoteProjectPath))
|
||
.toString().replace('\\', '/');
|
||
ConnectToRemoteAction.syncProject(server, localRoot, indicator);
|
||
|
||
// Обновляем виртуальную файловую систему, чтобы IDE увидела изменения
|
||
ApplicationManager.getApplication().invokeLater(() -> {
|
||
VirtualFile rootDir = LocalFileSystem.getInstance().findFileByPath(localRoot);
|
||
if (rootDir != null) {
|
||
VfsUtil.markDirtyAndRefresh(true, true, true, rootDir);
|
||
}
|
||
});
|
||
} catch (Exception e) {
|
||
ApplicationManager.getApplication().invokeLater(() ->
|
||
Messages.showErrorDialog("Sync failed: " + e.getMessage(), "Sync Error"));
|
||
}
|
||
}
|
||
});
|
||
});
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
private void uploadFile(VirtualFile localFile, Project project) {
|
||
String projectPath = project.getBasePath();
|
||
if (projectPath == null) return;
|
||
|
||
// Определяем привязанный сервер
|
||
String serverId = ProjectServerMapping.getInstance().getServerIdForProject(projectPath);
|
||
if (serverId == null) return;
|
||
|
||
Optional<SshServer> maybeServer = SshServerManager.getInstance().findServer(serverId);
|
||
if (maybeServer.isEmpty()) return;
|
||
SshServer server = maybeServer.get();
|
||
|
||
// Вычисляем локальный корень проекта (как он был сохранен при создании)
|
||
String localRoot = Paths.get(System.getProperty("user.home"), "ssh-remote-projects",
|
||
sanitize(server.host), sanitize(server.remoteProjectPath))
|
||
.toString().replace('\\', '/');
|
||
|
||
String localFilePath = localFile.getPath(); // всегда с прямыми слешами
|
||
|
||
// Игнорируем служебные файлы IDE
|
||
if (localFilePath.contains("/.idea/") || localFile.getName().equals("remote.iml")) {
|
||
return;
|
||
}
|
||
|
||
// Проверяем, что файл принадлежит проекту
|
||
if (!localFilePath.startsWith(localRoot)) {
|
||
return;
|
||
}
|
||
|
||
// Относительный путь и удалённый путь
|
||
String relativePath = localFilePath.substring(localRoot.length());
|
||
if (relativePath.startsWith("/")) {
|
||
relativePath = relativePath.substring(1);
|
||
}
|
||
String remoteFilePath = server.remoteProjectPath + "/" + relativePath;
|
||
|
||
// Отправляем файл в отдельном потоке через общее SFTP-соединение
|
||
uploadExecutor.submit(() -> {
|
||
try {
|
||
ChannelSftp sftp = SftpSessionManager.getInstance(project).getChannel(server);
|
||
String remoteParent = remoteFilePath.substring(0, remoteFilePath.lastIndexOf('/'));
|
||
createRemoteDirs(sftp, remoteParent);
|
||
sftp.put(localFilePath, remoteFilePath);
|
||
} catch (Exception e) {
|
||
ApplicationManager.getApplication().invokeLater(() ->
|
||
com.intellij.openapi.ui.Messages.showErrorDialog(
|
||
"Sync error " + localFile.getName() + ": " + e.getMessage(),
|
||
"SFTP Error"
|
||
)
|
||
);
|
||
}
|
||
});
|
||
}
|
||
|
||
private void createRemoteDirs(ChannelSftp sftp, String dir) throws SftpException {
|
||
if (dir.isEmpty() || dir.equals("/")) return;
|
||
String[] parts = dir.split("/");
|
||
StringBuilder path = new StringBuilder();
|
||
for (String part : parts) {
|
||
if (part.isEmpty()) continue;
|
||
path.append("/").append(part);
|
||
try {
|
||
sftp.stat(path.toString());
|
||
} catch (SftpException e) {
|
||
if (e.id == ChannelSftp.SSH_FX_NO_SUCH_FILE) {
|
||
sftp.mkdir(path.toString());
|
||
} else {
|
||
throw e;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
private String sanitize(String input) {
|
||
return input.replaceAll("[^a-zA-Z0-9.-]", "_");
|
||
}
|
||
} |