feat: enrich AgentInstanceResponse with version/capabilities, add password reset endpoint

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
hsiegeln
2026-03-23 18:13:37 +01:00
parent 2051572ee2
commit f4d2693561
4 changed files with 39 additions and 0 deletions

View File

@@ -1,5 +1,6 @@
package com.cameleer3.server.app.controller;
import com.cameleer3.server.app.dto.SetPasswordRequest;
import com.cameleer3.server.core.admin.AuditCategory;
import com.cameleer3.server.core.admin.AuditResult;
import com.cameleer3.server.core.admin.AuditService;
@@ -12,6 +13,7 @@ import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.Valid;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.DeleteMapping;
@@ -172,6 +174,18 @@ public class UserAdminController {
return ResponseEntity.noContent().build();
}
@PostMapping("/{userId}/password")
@Operation(summary = "Reset user password")
@ApiResponse(responseCode = "204", description = "Password reset")
public ResponseEntity<Void> resetPassword(
@PathVariable String userId,
@Valid @RequestBody SetPasswordRequest request,
HttpServletRequest httpRequest) {
userRepository.setPassword(userId, passwordEncoder.encode(request.password()));
auditService.log("reset_password", AuditCategory.USER_MGMT, userId, null, AuditResult.SUCCESS, httpRequest);
return ResponseEntity.noContent().build();
}
public record CreateUserRequest(String username, String displayName, String email, String password) {}
public record UpdateUserRequest(String displayName, String email) {}
}

View File

@@ -7,6 +7,7 @@ import jakarta.validation.constraints.NotNull;
import java.time.Duration;
import java.time.Instant;
import java.util.List;
import java.util.Map;
@Schema(description = "Agent instance summary with runtime metrics")
public record AgentInstanceResponse(
@@ -17,6 +18,8 @@ public record AgentInstanceResponse(
@NotNull List<String> routeIds,
@NotNull Instant registeredAt,
@NotNull Instant lastHeartbeat,
String version,
Map<String, Object> capabilities,
double tps,
double errorRate,
int activeRoutes,
@@ -29,6 +32,7 @@ public record AgentInstanceResponse(
info.id(), info.name(), info.group(),
info.state().name(), info.routeIds(),
info.registeredAt(), info.lastHeartbeat(),
info.version(), info.capabilities(),
0.0, 0.0,
0, info.routeIds() != null ? info.routeIds().size() : 0,
uptime
@@ -38,6 +42,7 @@ public record AgentInstanceResponse(
public AgentInstanceResponse withMetrics(double tps, double errorRate, int activeRoutes) {
return new AgentInstanceResponse(
id, name, group, status, routeIds, registeredAt, lastHeartbeat,
version, capabilities,
tps, errorRate, activeRoutes, totalRoutes, uptimeSeconds
);
}

View File

@@ -0,0 +1,7 @@
package com.cameleer3.server.app.dto;
import jakarta.validation.constraints.NotBlank;
public record SetPasswordRequest(
@NotBlank String password
) {}

View File

@@ -198,6 +198,19 @@ export function useDeleteUser() {
});
}
export function useSetPassword() {
const qc = useQueryClient();
return useMutation({
mutationFn: async ({ userId, password }: { userId: string; password: string }) => {
await adminFetch(`/users/${userId}/password`, {
method: 'POST',
body: JSON.stringify({ password }),
});
},
onSuccess: () => qc.invalidateQueries({ queryKey: ['admin', 'users'] }),
});
}
export function useAssignRoleToUser() {
const qc = useQueryClient();
return useMutation({