fix(aria): IOC Feeds — restore app, fix API shapes, canonical UI
CD - Develop / build-and-deploy (push) Failing after 10m58s
CD - Develop / build-and-deploy (push) Failing after 10m58s
Root cause: IocFeeds.svelte called feeds.find() on a Map (backend returned
Map<String,Object> not List) → reactive statement crash → entire Svelte tree
broken, all sidebar nav dead.
Backend:
- /feeds/status now returns List<Map> with feedName/configured/enabled/
lastRunAt/lastRunStatus/lastIocsAdded/lastIocsUpdated fields
- /feeds/runs now returns full Page<IocFeedRun> (not stripped .getContent())
- IocFeedService.isConfigured(feedName) added for per-feed key check
- Feed order fixed to [OTX, MALWARE_BAZAAR, THREAT_FOX] (deterministic)
Frontend:
- loadFeeds: Array.isArray guard prevents crash if shape is ever wrong
- loadRuns: safe fallback for both Page and plain-array responses
- api.ts refreshFeed URL fixed: /feeds/refresh/{name} not /feeds/{name}/refresh
- IocFeedStatus type: added lastIocsUpdated
- IocFeeds.svelte: Refresh All moved from header to toolbar; all buttons
use canonical btn/btn-primary/btn-secondary/btn-sm classes; pollInterval
now calls pollStatus (status-only poll) not full refresh trigger
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -5,14 +5,13 @@ import com.hiveops.aria.repository.IocFeedRunRepository;
|
||||
import com.hiveops.aria.service.IocFeedService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.*;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/aria/feeds")
|
||||
@@ -20,6 +19,9 @@ import java.util.Optional;
|
||||
@Slf4j
|
||||
public class IocFeedController {
|
||||
|
||||
private final IocFeedService feedService;
|
||||
private final IocFeedRunRepository feedRunRepository;
|
||||
|
||||
private Thread feedThread(String name, Runnable task) {
|
||||
Thread t = new Thread(task, name);
|
||||
t.setUncaughtExceptionHandler((thread, ex) ->
|
||||
@@ -27,30 +29,36 @@ public class IocFeedController {
|
||||
return t;
|
||||
}
|
||||
|
||||
private final IocFeedService feedService;
|
||||
private final IocFeedRunRepository feedRunRepository;
|
||||
|
||||
/** Latest run per feed — used by the status cards in the UI */
|
||||
/** One status object per feed — array, matches IocFeedStatus[] in the frontend */
|
||||
@GetMapping("/status")
|
||||
@PreAuthorize("hasRole('BCOS_ADMIN')")
|
||||
public ResponseEntity<Map<String, Object>> status() {
|
||||
List<String> names = feedService.feedNames();
|
||||
Map<String, Object> result = new java.util.LinkedHashMap<>();
|
||||
for (String name : names) {
|
||||
public ResponseEntity<List<Map<String, Object>>> status() {
|
||||
List<Map<String, Object>> result = new ArrayList<>();
|
||||
for (String name : feedService.feedNames()) {
|
||||
Optional<IocFeedRun> last = feedRunRepository.findTopByFeedNameOrderByStartedAtDesc(name);
|
||||
result.put(name, last.orElse(null));
|
||||
Map<String, Object> entry = new LinkedHashMap<>();
|
||||
entry.put("feedName", name);
|
||||
entry.put("configured", feedService.isConfigured(name));
|
||||
entry.put("enabled", true);
|
||||
last.ifPresent(r -> {
|
||||
entry.put("lastRunAt", r.getCompletedAt() != null ? r.getCompletedAt() : r.getStartedAt());
|
||||
entry.put("lastRunStatus", r.getStatus());
|
||||
entry.put("lastIocsAdded", r.getIocsAdded());
|
||||
entry.put("lastIocsUpdated", r.getIocsUpdated());
|
||||
});
|
||||
result.add(entry);
|
||||
}
|
||||
return ResponseEntity.ok(result);
|
||||
}
|
||||
|
||||
/** Recent run history — default 50 rows */
|
||||
/** Paginated run history — returns Spring Page so the frontend gets content + page metadata */
|
||||
@GetMapping("/runs")
|
||||
@PreAuthorize("hasRole('BCOS_ADMIN')")
|
||||
public ResponseEntity<List<IocFeedRun>> runs(
|
||||
public ResponseEntity<Page<IocFeedRun>> runs(
|
||||
@RequestParam(defaultValue = "0") int page,
|
||||
@RequestParam(defaultValue = "50") int size) {
|
||||
@RequestParam(defaultValue = "20") int size) {
|
||||
return ResponseEntity.ok(
|
||||
feedRunRepository.findAllByOrderByStartedAtDesc(PageRequest.of(page, size)).getContent()
|
||||
feedRunRepository.findAllByOrderByStartedAtDesc(PageRequest.of(page, size))
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -114,6 +114,15 @@ public class IocFeedService {
|
||||
}
|
||||
|
||||
public List<String> feedNames() {
|
||||
return List.copyOf(FEED_SOURCES.keySet());
|
||||
return List.of("OTX", "MALWARE_BAZAAR", "THREAT_FOX");
|
||||
}
|
||||
|
||||
public boolean isConfigured(String feedName) {
|
||||
return switch (feedName) {
|
||||
case "OTX" -> otxClient.isConfigured();
|
||||
case "MALWARE_BAZAAR" -> malwareBazaarClient.isConfigured();
|
||||
case "THREAT_FOX" -> threatFoxClient.isConfigured();
|
||||
default -> false;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user