import 'package:riverpod_annotation/riverpod_annotation.dart'; import '../../core/models/models.dart'; import '../../core/network/api_client_provider.dart'; import '../../core/network/node_master_api_client.dart'; import '../../core/polling/polling_async_notifier.dart'; part 'nodes_repository.g.dart'; /// Wraps the `/nodes/*` endpoints — the registry of sibling NodeMaster /// hosts and the server-side fan-out status view. class NodesRepository { const NodesRepository(this._client); final NodeMasterApiClient _client; Future> getAll() => _client.getAllRemoteNodes(); Future get(String id) => _client.getRemoteNode(id); Future add(RemoteNode node) => _client.addRemoteNode(node); Future update(String id, RemoteNode node) => _client.updateRemoteNode(id, node); Future delete(String id) => _client.deleteRemoteNode(id); Future> getAggregated() => _client.getAggregated(); } /// Null when no connection is active, mirroring [apiClientProvider]. @riverpod NodesRepository? nodesRepository(Ref ref) { final client = ref.watch(apiClientProvider); if (client == null) return null; return NodesRepository(client); } /// The registered-nodes list. Manual-refresh only, same reasoning as /// `servicesListProvider` — this backs Fleet's registry table (M6). @riverpod Future> remoteNodesList(Ref ref) async { final repo = ref.watch(nodesRepositoryProvider); if (repo == null) return []; return repo.getAll(); } /// The live `/nodes/aggregated` fan-out result, polled every 25s. Shared /// by the Dashboard's "Fleet" stat tile and the Fleet screen's card grid /// (M6) — one poll, cached by Riverpod, rather than each screen running /// its own duplicate timer against the same endpoint. @riverpod class AggregatedNodes extends _$AggregatedNodes with PollingMixin> { @override Duration get interval => const Duration(seconds: 25); @override Future> fetch() async { final repo = ref.watch(nodesRepositoryProvider); if (repo == null) return []; return repo.getAggregated(); } @override Future> build() => startPolling(); }