mirror of
https://github.com/chaos-zhu/easynode.git
synced 2026-10-07 16:18:31 +08:00
271 lines
7.4 KiB
Dart
271 lines
7.4 KiB
Dart
import 'dart:async';
|
|
|
|
import 'package:flutter/foundation.dart' show ChangeNotifier;
|
|
|
|
import '../../core/api/cookie_store.dart';
|
|
import '../../core/security/server_certificate_trust.dart';
|
|
import '../auth/auth_session.dart';
|
|
import '../servers/server_model.dart';
|
|
import 'docker_container.dart';
|
|
import 'docker_socket_client.dart';
|
|
|
|
enum DockerConnectionStatus { connecting, connected, disconnected, error }
|
|
|
|
class DockerSessionState {
|
|
DockerSessionState({
|
|
required this.server,
|
|
required this.client,
|
|
this.status = DockerConnectionStatus.connecting,
|
|
this.containers = const [],
|
|
this.logs = '',
|
|
this.errorMessage,
|
|
this.loading = true,
|
|
this.refreshing = false,
|
|
});
|
|
|
|
final ServerModel server;
|
|
final DockerSocketClient client;
|
|
DockerConnectionStatus status;
|
|
List<DockerContainer> containers;
|
|
String logs;
|
|
String? errorMessage;
|
|
bool loading;
|
|
bool refreshing;
|
|
final Set<String> operatingIds = {};
|
|
}
|
|
|
|
class DockerSessionManager extends ChangeNotifier {
|
|
final Map<String, DockerSessionState> _sessions = {};
|
|
final Map<String, List<StreamSubscription<dynamic>>> _subscriptions = {};
|
|
String? _activeHostId;
|
|
|
|
List<DockerSessionState> get sessions =>
|
|
_sessions.values.toList(growable: false);
|
|
|
|
DockerSessionState? get activeSession {
|
|
final id = _activeHostId;
|
|
if (id == null) return null;
|
|
return _sessions[id];
|
|
}
|
|
|
|
DockerSessionState? sessionFor(String? hostId) {
|
|
if (hostId == null || hostId.isEmpty) return null;
|
|
return _sessions[hostId];
|
|
}
|
|
|
|
String? get activeHostId => _activeHostId;
|
|
|
|
bool isConnected(String hostId) =>
|
|
_sessions[hostId]?.status == DockerConnectionStatus.connected;
|
|
|
|
Future<void> connect({
|
|
required ServerModel server,
|
|
required AuthSession authSession,
|
|
required SessionCookieStore cookieStore,
|
|
required ServerCertificateTrustStore certificateTrust,
|
|
}) async {
|
|
final existing = _sessions[server.id];
|
|
if (existing != null) {
|
|
_activeHostId = server.id;
|
|
if (existing.status == DockerConnectionStatus.disconnected ||
|
|
existing.status == DockerConnectionStatus.error) {
|
|
_markConnecting(existing);
|
|
await existing.client.connect();
|
|
} else {
|
|
notifyListeners();
|
|
}
|
|
return;
|
|
}
|
|
|
|
final client = DockerSocketClient(
|
|
authSession: authSession,
|
|
cookieStore: cookieStore,
|
|
certificateTrust: certificateTrust,
|
|
hostId: server.id,
|
|
);
|
|
final session = DockerSessionState(server: server, client: client);
|
|
_sessions[server.id] = session;
|
|
_activeHostId = server.id;
|
|
_subscriptions[server.id] = [
|
|
client.connectedStream.listen((connected) {
|
|
if (connected) {
|
|
session.status = DockerConnectionStatus.connected;
|
|
} else {
|
|
if (session.status == DockerConnectionStatus.connecting) return;
|
|
session.status = DockerConnectionStatus.disconnected;
|
|
session.loading = false;
|
|
}
|
|
notifyListeners();
|
|
}),
|
|
client.containersStream.listen((containers) {
|
|
session.containers = containers;
|
|
session.loading = false;
|
|
session.refreshing = false;
|
|
session.operatingIds.clear();
|
|
session.errorMessage = null;
|
|
session.status = DockerConnectionStatus.connected;
|
|
notifyListeners();
|
|
}),
|
|
client.logsStream.listen((logs) {
|
|
session.logs = logs;
|
|
notifyListeners();
|
|
}),
|
|
client.operationStream.listen((result) {
|
|
if (!result.success) {
|
|
session.operatingIds.clear();
|
|
session.errorMessage = result.message;
|
|
}
|
|
session.loading = false;
|
|
notifyListeners();
|
|
if (result.success) client.refresh();
|
|
}),
|
|
client.errorStream.listen((message) {
|
|
session.status = DockerConnectionStatus.error;
|
|
session.loading = false;
|
|
session.refreshing = false;
|
|
session.errorMessage = message;
|
|
notifyListeners();
|
|
}),
|
|
];
|
|
notifyListeners();
|
|
await client.connect();
|
|
}
|
|
|
|
void activate(String hostId) {
|
|
if (!_sessions.containsKey(hostId)) return;
|
|
_activeHostId = hostId;
|
|
notifyListeners();
|
|
}
|
|
|
|
void _markConnecting(DockerSessionState session) {
|
|
session.status = DockerConnectionStatus.connecting;
|
|
session.loading = true;
|
|
session.refreshing = false;
|
|
session.containers = const [];
|
|
session.logs = '';
|
|
session.errorMessage = null;
|
|
session.operatingIds.clear();
|
|
notifyListeners();
|
|
}
|
|
|
|
void refreshActive() {
|
|
final session = activeSession;
|
|
_refreshSession(session);
|
|
}
|
|
|
|
void refresh(String hostId) {
|
|
_refreshSession(sessionFor(hostId));
|
|
}
|
|
|
|
void _refreshSession(DockerSessionState? session) {
|
|
if (session == null) return;
|
|
session.refreshing = true;
|
|
session.errorMessage = null;
|
|
notifyListeners();
|
|
session.client.refresh();
|
|
}
|
|
|
|
void reconnectActive() {
|
|
final session = activeSession;
|
|
_reconnectSession(session);
|
|
}
|
|
|
|
void reconnect(String hostId) {
|
|
_reconnectSession(sessionFor(hostId));
|
|
}
|
|
|
|
void _reconnectSession(DockerSessionState? session) {
|
|
if (session == null) return;
|
|
_markConnecting(session);
|
|
unawaited(session.client.connect());
|
|
}
|
|
|
|
void getLogs(DockerContainer container, {String? hostId}) {
|
|
final session = hostId == null ? activeSession : sessionFor(hostId);
|
|
if (session == null) return;
|
|
session.logs = '';
|
|
notifyListeners();
|
|
session.client.getLogs(container.id);
|
|
}
|
|
|
|
void refreshLogs(DockerContainer container, {String? hostId}) {
|
|
final session = hostId == null ? activeSession : sessionFor(hostId);
|
|
session?.client.getLogs(container.id);
|
|
}
|
|
|
|
void start(DockerContainer container, {String? hostId}) =>
|
|
_operate(container, (client) {
|
|
client.start(container.id);
|
|
}, hostId: hostId);
|
|
|
|
void stop(DockerContainer container, {String? hostId}) =>
|
|
_operate(container, (client) {
|
|
client.stop(container.id);
|
|
}, hostId: hostId);
|
|
|
|
void restart(DockerContainer container, {String? hostId}) =>
|
|
_operate(container, (client) {
|
|
client.restart(container.id);
|
|
}, hostId: hostId);
|
|
|
|
void delete(DockerContainer container, {String? hostId}) =>
|
|
_operate(container, (client) {
|
|
client.delete(container.id);
|
|
}, hostId: hostId);
|
|
|
|
void _operate(
|
|
DockerContainer container,
|
|
void Function(DockerSocketClient client) send, {
|
|
String? hostId,
|
|
}) {
|
|
final session = hostId == null ? activeSession : sessionFor(hostId);
|
|
if (session == null) return;
|
|
session.operatingIds.add(container.id);
|
|
session.errorMessage = null;
|
|
notifyListeners();
|
|
send(session.client);
|
|
}
|
|
|
|
void disconnectActive() {
|
|
final id = _activeHostId;
|
|
if (id == null) return;
|
|
disconnect(id);
|
|
}
|
|
|
|
void disconnect(String hostId) {
|
|
final wasActive = _activeHostId == hostId;
|
|
_disconnect(hostId);
|
|
if (wasActive) {
|
|
_activeHostId = _sessions.keys.isEmpty ? null : _sessions.keys.first;
|
|
}
|
|
notifyListeners();
|
|
}
|
|
|
|
void disconnectAll() {
|
|
_disconnectAll();
|
|
notifyListeners();
|
|
}
|
|
|
|
void _disconnect(String hostId) {
|
|
final session = _sessions.remove(hostId);
|
|
if (session == null) return;
|
|
for (final sub in _subscriptions.remove(hostId) ?? const []) {
|
|
sub.cancel();
|
|
}
|
|
session.client.disconnect();
|
|
}
|
|
|
|
void _disconnectAll() {
|
|
for (final hostId in _sessions.keys.toList(growable: false)) {
|
|
_disconnect(hostId);
|
|
}
|
|
_activeHostId = null;
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
_disconnectAll();
|
|
super.dispose();
|
|
}
|
|
}
|