Files
tatlock-ui/lib/features/control_room/stacks/data/datasources/stacks_datasource.dart
T
Jeroen SchweitzerandClaude Opus 4.5 6989241ae7 chore: upgrade to riverpod 3.x and freezed 3.x
- Upgrade flutter_riverpod to 3.1.0, riverpod_annotation to 4.0.0
- Upgrade freezed to 3.2.3, freezed_annotation to 3.1.0
- Migrate freezed classes to use sealed keyword (freezed 3.x)
- Update provider naming (*NotifierProvider → *Provider)
- Add legacy.dart import for StateNotifierProvider compatibility
- Fix valueOrNull → value for AsyncValue
- Remove unused imports and fields
- Add sync from Authentik button to users/groups pages
- Suppress invalid_annotation_target warning in analysis_options

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-02 00:07:01 +01:00

133 lines
4.4 KiB
Dart

import 'package:dio/dio.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:tatlock_ui/core/api/api_client.dart';
import 'package:tatlock_ui/features/control_room/containers/data/datasources/containers_datasource.dart';
import 'package:tatlock_ui/features/control_room/containers/domain/entities/container.dart';
import 'package:tatlock_ui/features/control_room/stacks/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 provides endpoints for stack YAML and environment variables.
class StacksDatasource {
StacksDatasource(this._dio, this._containersDatasource);
final Dio _dio;
final ContainersDatasource _containersDatasource;
/// Gets all stacks by aggregating container data.
Future<List<Stack>> getStacks() async {
final containers = await _containersDatasource.getContainers();
final containerEntities = containers.map((c) => c.toEntity()).toList();
// Group containers by stack name
final stackMap = <String, List<Container>>{};
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<Stack> 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<void> 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);
}
}
/// Gets the compose YAML for a stack.
Future<String> getStackYaml(String stackId) async {
final response = await _dio.get<String>(
'/infrastructure/stacks/$stackId/compose',
);
return response.data ?? '';
}
/// Updates the compose YAML for a stack.
Future<void> updateStackYaml(String stackId, String yaml) async {
await _dio.put<void>(
'/infrastructure/stacks/$stackId/compose',
data: yaml,
options: Options(contentType: 'text/yaml'),
);
}
/// Gets the environment variables for a stack.
Future<Map<String, String>> getStackEnvVars(String stackId) async {
final response = await _dio.get<Map<String, dynamic>>(
'/infrastructure/stacks/$stackId/env',
);
return response.data?.map((k, v) => MapEntry(k, v.toString())) ?? {};
}
/// Updates the environment variables for a stack.
Future<void> updateStackEnvVars(
String stackId,
Map<String, String> envVars,
) async {
await _dio.put<void>(
'/infrastructure/stacks/$stackId/env',
data: envVars,
);
}
/// Deploys/redeploys a stack with current configuration.
Future<void> deployStack(String stackId) async {
await _dio.post<void>('/infrastructure/stacks/$stackId/deploy');
}
/// Rebuilds a stack (pulls fresh images and recreates containers).
Future<void> rebuildStack(String stackId) async {
await _dio.post<void>('/infrastructure/stacks/$stackId/rebuild');
}
}
/// Provides the stacks datasource.
@riverpod
StacksDatasource stacksDatasource(Ref ref) {
final dio = ref.watch(coreApiClientProvider);
final containersDatasource = ref.watch(containersDatasourceProvider);
return StacksDatasource(dio, containersDatasource);
}