148 lines
4.0 KiB
Dart
148 lines
4.0 KiB
Dart
import 'dart:convert';
|
|
|
|
import 'package:file_picker/file_picker.dart';
|
|
import 'package:flutter/services.dart';
|
|
|
|
import '../../about/internal/application/ServerProfile.dart';
|
|
import '../../about/internal/application/ServiceConfig.dart';
|
|
import '../../comms/Comms.dart' show LogLine;
|
|
import '../parent/ParentViewModel.dart';
|
|
import 'ConnectLogs.dart';
|
|
|
|
class ViewLogs extends ParentViewModel {
|
|
ConnectLogs connection;
|
|
|
|
ViewLogs(super.context, this.connection);
|
|
|
|
/// Log lines as they arrive from the server. The screen subscribes to this
|
|
/// directly — a stream of remote output has no request/response shape to
|
|
/// hand back through the Connect interface one call at a time.
|
|
Stream<LogLine> get logs => getDataManager().logs;
|
|
|
|
/// Pulls the journal for [service], optionally bounded by a date range.
|
|
void fetchLogs(
|
|
ServerProfile server,
|
|
ServiceConfig service, {
|
|
DateTime? since,
|
|
DateTime? until,
|
|
}) async {
|
|
if (!await canReach(
|
|
server,
|
|
() => fetchLogs(server, service, since: since, until: until),
|
|
)) {
|
|
return;
|
|
}
|
|
getDataManager().stopLogs();
|
|
try {
|
|
await getDataManager().fetchLogs(
|
|
server,
|
|
service,
|
|
since: since,
|
|
until: until,
|
|
);
|
|
connection.onLogsFetched();
|
|
} catch (error) {
|
|
connection.onLogsFetched();
|
|
handleError(
|
|
error,
|
|
() => fetchLogs(server, service, since: since, until: until),
|
|
() {},
|
|
"Retry",
|
|
);
|
|
}
|
|
}
|
|
|
|
/// Reads the unit's systemd state. Failures are reported as `unknown`
|
|
/// rather than raised — the status pill is ambient, not an operation the
|
|
/// user asked for.
|
|
void loadStatus(ServerProfile server, ServiceConfig service) async {
|
|
try {
|
|
final status = await getDataManager().serviceStatus(server, service);
|
|
connection.onStatusLoaded(status);
|
|
} catch (_) {
|
|
connection.onStatusLoaded('unknown');
|
|
}
|
|
}
|
|
|
|
/// start | stop | restart on the service's unit.
|
|
void control(
|
|
ServerProfile server,
|
|
ServiceConfig service,
|
|
String action,
|
|
) async {
|
|
if (!await canReach(server, () => control(server, service, action))) return;
|
|
getDataManager().stopLogs();
|
|
try {
|
|
final result = await getDataManager().serviceControl(
|
|
server,
|
|
service,
|
|
action,
|
|
);
|
|
connection.onControlFinished(result.message);
|
|
loadStatus(server, service);
|
|
} catch (error) {
|
|
handleError(
|
|
error,
|
|
() => control(server, service, action),
|
|
() {},
|
|
"Retry",
|
|
);
|
|
}
|
|
}
|
|
|
|
/// Starts or stops the live tail.
|
|
void toggleFollow(
|
|
ServerProfile server,
|
|
ServiceConfig service,
|
|
bool currentlyFollowing,
|
|
) async {
|
|
if (currentlyFollowing) {
|
|
getDataManager().stopLogs();
|
|
connection.onFollowChanged(false);
|
|
return;
|
|
}
|
|
if (!await canReach(
|
|
server,
|
|
() => toggleFollow(server, service, currentlyFollowing),
|
|
)) {
|
|
return;
|
|
}
|
|
connection.onFollowChanged(true);
|
|
try {
|
|
await getDataManager().followLogs(server, service);
|
|
} catch (error) {
|
|
connection.onFollowChanged(false);
|
|
handleError(
|
|
error,
|
|
() => toggleFollow(server, service, false),
|
|
() {},
|
|
"Retry",
|
|
);
|
|
}
|
|
}
|
|
|
|
/// Writes [lines] to a file the user picks.
|
|
void saveLogs(String serviceName, List<String> lines) async {
|
|
try {
|
|
final body = lines.join('\n');
|
|
final stamp = DateTime.now()
|
|
.toIso8601String()
|
|
.replaceAll(':', '-')
|
|
.split('.')
|
|
.first;
|
|
final safeName = serviceName.replaceAll(RegExp(r'[^A-Za-z0-9_-]'), '_');
|
|
final path = await FilePicker.saveFile(
|
|
dialogTitle: 'Save logs',
|
|
fileName: '$safeName-logs-$stamp.txt',
|
|
bytes: Uint8List.fromList(utf8.encode(body)),
|
|
);
|
|
connection.onLogsSaved(path);
|
|
} catch (error) {
|
|
handleError(error, () => saveLogs(serviceName, lines), () {}, "Retry");
|
|
}
|
|
}
|
|
|
|
/// Stops any tail still running when the screen goes away.
|
|
void stopLogs() => getDataManager().stopLogs();
|
|
}
|