Please provide the specific file changes or a description of the modifications you have made so I can generate the commit message for you.

This commit is contained in:
liumangmang
2026-05-07 10:09:40 +08:00
parent 165cc0e35b
commit f24d0f69ed
19 changed files with 1757 additions and 367 deletions
@@ -6,12 +6,15 @@ import com.sshmanager.dto.BackupImportResponseDto;
import com.sshmanager.dto.BackupPackageDto;
import com.sshmanager.dto.BatchCommandRequest;
import com.sshmanager.dto.BatchCommandResponseDto;
import com.sshmanager.dto.ConnectionStatusCheckRequest;
import com.sshmanager.dto.ConnectionStatusResponseDto;
import com.sshmanager.entity.Connection;
import com.sshmanager.entity.User;
import com.sshmanager.repository.UserRepository;
import com.sshmanager.service.BackupService;
import com.sshmanager.service.BatchCommandService;
import com.sshmanager.service.ConnectionService;
import com.sshmanager.service.ConnectionStatusService;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.*;
@@ -27,15 +30,18 @@ public class ConnectionController {
private final ConnectionService connectionService;
private final BackupService backupService;
private final BatchCommandService batchCommandService;
private final ConnectionStatusService connectionStatusService;
private final UserRepository userRepository;
public ConnectionController(ConnectionService connectionService,
BackupService backupService,
BatchCommandService batchCommandService,
ConnectionStatusService connectionStatusService,
UserRepository userRepository) {
this.connectionService = connectionService;
this.backupService = backupService;
this.batchCommandService = batchCommandService;
this.connectionStatusService = connectionStatusService;
this.userRepository = userRepository;
}
@@ -101,9 +107,16 @@ public class ConnectionController {
return ResponseEntity.ok(batchCommandService.execute(userId, request));
}
@PostMapping("/status")
public ResponseEntity<ConnectionStatusResponseDto> checkStatuses(@RequestBody ConnectionStatusCheckRequest request,
Authentication authentication) {
Long userId = getCurrentUserId(authentication);
return ResponseEntity.ok(connectionStatusService.checkStatuses(userId, request));
}
@PostMapping("/test")
public ResponseEntity<Map<String, Object>> connectivity(@RequestBody Connection connection,
Authentication authentication) {
public ResponseEntity<Map<String, Object>> connectivity(@RequestBody Connection connection,
Authentication authentication) {
try {
Long userId = getCurrentUserId(authentication);
Connection fullConn = connectionService.getConnectionForSsh(connection.getId(), userId);
@@ -0,0 +1,12 @@
package com.sshmanager.dto;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
@Data
public class ConnectionStatusCheckRequest {
private List<Long> connectionIds = new ArrayList<Long>();
}
@@ -0,0 +1,15 @@
package com.sshmanager.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
@Data
@AllArgsConstructor
public class ConnectionStatusItemDto {
private Long connectionId;
private String connectionName;
private String status;
private String message;
private long durationMs;
}
@@ -0,0 +1,15 @@
package com.sshmanager.dto;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
@Data
public class ConnectionStatusResponseDto {
private int total;
private int onlineCount;
private int offlineCount;
private List<ConnectionStatusItemDto> results = new ArrayList<ConnectionStatusItemDto>();
}
@@ -0,0 +1,69 @@
package com.sshmanager.service;
import com.sshmanager.dto.ConnectionStatusCheckRequest;
import com.sshmanager.dto.ConnectionStatusItemDto;
import com.sshmanager.dto.ConnectionStatusResponseDto;
import com.sshmanager.entity.Connection;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
@Service
public class ConnectionStatusService {
private final ConnectionService connectionService;
public ConnectionStatusService(ConnectionService connectionService) {
this.connectionService = connectionService;
}
public ConnectionStatusResponseDto checkStatuses(Long userId, ConnectionStatusCheckRequest request) {
if (request == null || request.getConnectionIds() == null || request.getConnectionIds().isEmpty()) {
throw new IllegalArgumentException("At least one connection is required");
}
List<ConnectionStatusItemDto> results = new ArrayList<ConnectionStatusItemDto>();
int onlineCount = 0;
int offlineCount = 0;
for (Long connectionId : request.getConnectionIds()) {
Connection connection = connectionService.getConnectionForSsh(connectionId, userId);
long startedAt = System.currentTimeMillis();
try {
connectionService.testConnection(
connection,
connectionService.getDecryptedPassword(connection),
connectionService.getDecryptedPrivateKey(connection),
connectionService.getDecryptedPassphrase(connection)
);
long durationMs = System.currentTimeMillis() - startedAt;
results.add(new ConnectionStatusItemDto(
connection.getId(),
connection.getName(),
"online",
"SSH connection available",
durationMs
));
onlineCount += 1;
} catch (Exception error) {
long durationMs = System.currentTimeMillis() - startedAt;
results.add(new ConnectionStatusItemDto(
connection.getId(),
connection.getName(),
"offline",
error.getMessage(),
durationMs
));
offlineCount += 1;
}
}
ConnectionStatusResponseDto response = new ConnectionStatusResponseDto();
response.setTotal(results.size());
response.setOnlineCount(onlineCount);
response.setOfflineCount(offlineCount);
response.setResults(results);
return response;
}
}
@@ -6,10 +6,13 @@ import com.sshmanager.dto.BackupImportResponseDto;
import com.sshmanager.dto.BackupPackageDto;
import com.sshmanager.dto.BatchCommandRequest;
import com.sshmanager.dto.BatchCommandResponseDto;
import com.sshmanager.dto.ConnectionStatusCheckRequest;
import com.sshmanager.dto.ConnectionStatusResponseDto;
import com.sshmanager.repository.UserRepository;
import com.sshmanager.service.BackupService;
import com.sshmanager.service.BatchCommandService;
import com.sshmanager.service.ConnectionService;
import com.sshmanager.service.ConnectionStatusService;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
@@ -47,6 +50,9 @@ class ConnectionControllerTest {
@Mock
private BatchCommandService batchCommandService;
@Mock
private ConnectionStatusService connectionStatusService;
@Mock
private UserRepository userRepository;
@@ -149,4 +155,18 @@ class ConnectionControllerTest {
assertEquals(expected, response.getBody());
verify(batchCommandService).execute(1L, request);
}
@Test
void checkStatusesUsesCurrentUserId() {
ConnectionStatusCheckRequest request = new ConnectionStatusCheckRequest();
ConnectionStatusResponseDto expected = new ConnectionStatusResponseDto();
expected.setTotal(2);
when(connectionStatusService.checkStatuses(1L, request)).thenReturn(expected);
ResponseEntity<ConnectionStatusResponseDto> response = connectionController.checkStatuses(request, authentication);
assertEquals(200, response.getStatusCode().value());
assertEquals(expected, response.getBody());
verify(connectionStatusService).checkStatuses(1L, request);
}
}
@@ -0,0 +1,72 @@
package com.sshmanager.service;
import com.sshmanager.dto.ConnectionStatusCheckRequest;
import com.sshmanager.dto.ConnectionStatusResponseDto;
import com.sshmanager.entity.Connection;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import java.util.Arrays;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.doReturn;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
class ConnectionStatusServiceTest {
@Mock
private ConnectionService connectionService;
@InjectMocks
private ConnectionStatusService connectionStatusService;
@Test
void checkStatusesAggregatesOnlineAndOfflineResults() {
Connection onlineConnection = new Connection();
onlineConnection.setId(1L);
onlineConnection.setUserId(99L);
onlineConnection.setName("prod");
Connection offlineConnection = new Connection();
offlineConnection.setId(2L);
offlineConnection.setUserId(99L);
offlineConnection.setName("test");
ConnectionStatusCheckRequest request = new ConnectionStatusCheckRequest();
request.setConnectionIds(Arrays.asList(1L, 2L));
when(connectionService.getConnectionForSsh(1L, 99L)).thenReturn(onlineConnection);
when(connectionService.getConnectionForSsh(2L, 99L)).thenReturn(offlineConnection);
doReturn(onlineConnection).when(connectionService).testConnection(eq(onlineConnection), eq(null), eq(null), eq(null));
doThrow(new RuntimeException("Connection refused")).when(connectionService).testConnection(eq(offlineConnection), eq(null), eq(null), eq(null));
ConnectionStatusResponseDto response = connectionStatusService.checkStatuses(99L, request);
assertEquals(2, response.getTotal());
assertEquals(1, response.getOnlineCount());
assertEquals(1, response.getOfflineCount());
assertEquals("online", response.getResults().get(0).getStatus());
assertEquals("offline", response.getResults().get(1).getStatus());
assertEquals("prod", response.getResults().get(0).getConnectionName());
assertEquals("Connection refused", response.getResults().get(1).getMessage());
}
@Test
void checkStatusesRejectsEmptyConnectionIds() {
ConnectionStatusCheckRequest request = new ConnectionStatusCheckRequest();
IllegalArgumentException error = assertThrows(
IllegalArgumentException.class,
() -> connectionStatusService.checkStatuses(1L, request)
);
assertEquals("At least one connection is required", error.getMessage());
}
}