import 'package:riverpod_annotation/riverpod_annotation.dart'; import '../../../containers/data/datasources/containers_datasource.dart'; import '../../../containers/domain/entities/container.dart'; import '../../domain/entities/stack.dart'; part 'stacks_datasource.g.dart'; /// Data source for stack operations. /// /// Stacks are derived from container labels (Docker Compose convention). /// The Core API doesn't have a dedicated stacks endpoint, so we aggregate /// from containers. class StacksDatasource { StacksDatasource(this._containersDatasource); final ContainersDatasource _containersDatasource; /// Gets all stacks by aggregating container data. Future> getStacks() async { final containers = await _containersDatasource.getContainers(); final containerEntities = containers.map((c) => c.toEntity()).toList(); // Group containers by stack name final stackMap = >{}; for (final container in containerEntities) { final stackName = container.stackName; if (stackName != null) { stackMap.putIfAbsent(stackName, () => []).add(container); } } // Convert to Stack entities return stackMap.entries.map((entry) { final name = entry.key; final stackContainers = entry.value; final runningCount = stackContainers.where((c) => c.isRunning).length; return Stack( id: name, // Use name as ID for compose stacks name: name, type: StackType.compose, status: runningCount == stackContainers.length ? StackStatus.active : runningCount > 0 ? StackStatus.active : StackStatus.inactive, containerCount: stackContainers.length, runningCount: runningCount, ); }).toList() ..sort((a, b) => a.name.compareTo(b.name)); } /// Gets a single stack by ID. Future getStack(String id) async { final stacks = await getStacks(); return stacks.firstWhere( (s) => s.id == id, orElse: () => throw Exception('Stack not found: $id'), ); } /// Performs an action on all containers in a stack. Future stackAction(String stackId, String action) async { final containers = await _containersDatasource.getContainers(); final stackContainers = containers .map((c) => c.toEntity()) .where((c) => c.stackId == stackId) .toList(); for (final container in stackContainers) { await _containersDatasource.containerAction(container.fullId, action); } } } /// Provides the stacks datasource. @riverpod StacksDatasource stacksDatasource(StacksDatasourceRef ref) { final containersDatasource = ref.watch(containersDatasourceProvider); return StacksDatasource(containersDatasource); }