feat(aria): add Svelte SPA frontend and threat events API
CD - Develop / build-and-deploy (push) Failing after 54s

- frontend/: full aria.bcos.dev SPA (port 5187) with Dashboard, Threat Events,
  and IOC Management views; Phase 2-3 nav items disabled as placeholders
- ThreatEventController: GET /api/aria/events (paginated, severity/device filter)
  and GET /api/aria/stats (total/critical/high/activeIoc counts)
- Deployed to bcos.dev: hiveiq-aria-frontend container + aria.bcos.dev NPM proxy

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-15 10:00:07 -04:00
parent 8c877ddd31
commit fe9a252dcd
26 changed files with 4338 additions and 0 deletions
@@ -0,0 +1,67 @@
package com.hiveops.aria.controller;
import com.hiveops.aria.entity.ThreatEvent;
import com.hiveops.aria.repository.ThreatEventRepository;
import com.hiveops.aria.repository.ThreatIocRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Map;
@RestController
@RequestMapping("/api/aria")
@RequiredArgsConstructor
public class ThreatEventController {
private final ThreatEventRepository eventRepository;
private final ThreatIocRepository iocRepository;
@GetMapping("/events")
@PreAuthorize("hasAnyRole('MSP_ADMIN','BCOS_ADMIN')")
public Page<ThreatEvent> getEvents(
@RequestParam(required = false) ThreatEvent.ThreatSeverity severity,
@RequestParam(required = false) String deviceAgentId,
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "50") int size) {
PageRequest pageable = PageRequest.of(page, size, Sort.by("detectedAt").descending());
if (severity != null) {
return eventRepository.findBySeverityOrderByDetectedAtDesc(severity, pageable);
}
if (deviceAgentId != null && !deviceAgentId.isBlank()) {
return eventRepository.findByDeviceAgentIdOrderByDetectedAtDesc(deviceAgentId, pageable);
}
return eventRepository.findAllByOrderByDetectedAtDesc(pageable);
}
@GetMapping("/stats")
@PreAuthorize("hasAnyRole('MSP_ADMIN','BCOS_ADMIN')")
public ResponseEntity<Map<String, Object>> getStats() {
Instant since24h = Instant.now().minus(24, ChronoUnit.HOURS);
Instant since7d = Instant.now().minus(7, ChronoUnit.DAYS);
long totalEvents = eventRepository.count();
long critical24h = eventRepository.findBySeverityOrderByDetectedAtDesc(
ThreatEvent.ThreatSeverity.CRITICAL,
PageRequest.of(0, Integer.MAX_VALUE)).getTotalElements();
long high24h = eventRepository.findBySeverityOrderByDetectedAtDesc(
ThreatEvent.ThreatSeverity.HIGH,
PageRequest.of(0, Integer.MAX_VALUE)).getTotalElements();
long activeIocs = iocRepository.findByActiveTrue().size();
return ResponseEntity.ok(Map.of(
"totalEvents", totalEvents,
"criticalCount", critical24h,
"highCount", high24h,
"activeIocCount", activeIocs
));
}
}