mirror of
https://git.selfprivacy.org/kherel/selfprivacy.org.app.git
synced 2024-11-10 19:03:12 +00:00
feat: Allow viewing service logs from the service screen
This commit is contained in:
parent
74eb1135df
commit
894d23bb7c
|
@ -370,7 +370,8 @@
|
|||
"invalid_input": "Invalid input",
|
||||
"create_job": "Create job",
|
||||
"update_job": "Update job",
|
||||
"wait_for_jobs": "Server is busy with other jobs. Please wait until they are finished."
|
||||
"wait_for_jobs": "Server is busy with other jobs. Please wait until they are finished.",
|
||||
"logs": "Service logs"
|
||||
},
|
||||
"mail": {
|
||||
"login_info": "Use username and password from users tab. IMAP port is 143 with STARTTLS, SMTP port is 587 with STARTTLS."
|
||||
|
|
|
@ -7,9 +7,9 @@ fragment LogEntry on LogEntry {
|
|||
cursor
|
||||
}
|
||||
|
||||
query Logs($limit: Int!, $upCursor: String, $downCursor: String) {
|
||||
query Logs($limit: Int!, $upCursor: String, $downCursor: String, $filterBySlice: String) {
|
||||
logs {
|
||||
paginated(limit: $limit, upCursor: $upCursor, downCursor: $downCursor) {
|
||||
paginated(limit: $limit, upCursor: $upCursor, downCursor: $downCursor, filterBySlice: $filterBySlice) {
|
||||
pageMeta {
|
||||
upCursor
|
||||
downCursor
|
||||
|
|
|
@ -329,11 +329,13 @@ class Variables$Query$Logs {
|
|||
required int limit,
|
||||
String? upCursor,
|
||||
String? downCursor,
|
||||
String? filterBySlice,
|
||||
}) =>
|
||||
Variables$Query$Logs._({
|
||||
r'limit': limit,
|
||||
if (upCursor != null) r'upCursor': upCursor,
|
||||
if (downCursor != null) r'downCursor': downCursor,
|
||||
if (filterBySlice != null) r'filterBySlice': filterBySlice,
|
||||
});
|
||||
|
||||
Variables$Query$Logs._(this._$data);
|
||||
|
@ -350,6 +352,10 @@ class Variables$Query$Logs {
|
|||
final l$downCursor = data['downCursor'];
|
||||
result$data['downCursor'] = (l$downCursor as String?);
|
||||
}
|
||||
if (data.containsKey('filterBySlice')) {
|
||||
final l$filterBySlice = data['filterBySlice'];
|
||||
result$data['filterBySlice'] = (l$filterBySlice as String?);
|
||||
}
|
||||
return Variables$Query$Logs._(result$data);
|
||||
}
|
||||
|
||||
|
@ -361,6 +367,8 @@ class Variables$Query$Logs {
|
|||
|
||||
String? get downCursor => (_$data['downCursor'] as String?);
|
||||
|
||||
String? get filterBySlice => (_$data['filterBySlice'] as String?);
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final result$data = <String, dynamic>{};
|
||||
final l$limit = limit;
|
||||
|
@ -373,6 +381,10 @@ class Variables$Query$Logs {
|
|||
final l$downCursor = downCursor;
|
||||
result$data['downCursor'] = l$downCursor;
|
||||
}
|
||||
if (_$data.containsKey('filterBySlice')) {
|
||||
final l$filterBySlice = filterBySlice;
|
||||
result$data['filterBySlice'] = l$filterBySlice;
|
||||
}
|
||||
return result$data;
|
||||
}
|
||||
|
||||
|
@ -413,6 +425,15 @@ class Variables$Query$Logs {
|
|||
if (l$downCursor != lOther$downCursor) {
|
||||
return false;
|
||||
}
|
||||
final l$filterBySlice = filterBySlice;
|
||||
final lOther$filterBySlice = other.filterBySlice;
|
||||
if (_$data.containsKey('filterBySlice') !=
|
||||
other._$data.containsKey('filterBySlice')) {
|
||||
return false;
|
||||
}
|
||||
if (l$filterBySlice != lOther$filterBySlice) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -421,10 +442,12 @@ class Variables$Query$Logs {
|
|||
final l$limit = limit;
|
||||
final l$upCursor = upCursor;
|
||||
final l$downCursor = downCursor;
|
||||
final l$filterBySlice = filterBySlice;
|
||||
return Object.hashAll([
|
||||
l$limit,
|
||||
_$data.containsKey('upCursor') ? l$upCursor : const {},
|
||||
_$data.containsKey('downCursor') ? l$downCursor : const {},
|
||||
_$data.containsKey('filterBySlice') ? l$filterBySlice : const {},
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
@ -442,6 +465,7 @@ abstract class CopyWith$Variables$Query$Logs<TRes> {
|
|||
int? limit,
|
||||
String? upCursor,
|
||||
String? downCursor,
|
||||
String? filterBySlice,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -462,12 +486,15 @@ class _CopyWithImpl$Variables$Query$Logs<TRes>
|
|||
Object? limit = _undefined,
|
||||
Object? upCursor = _undefined,
|
||||
Object? downCursor = _undefined,
|
||||
Object? filterBySlice = _undefined,
|
||||
}) =>
|
||||
_then(Variables$Query$Logs._({
|
||||
..._instance._$data,
|
||||
if (limit != _undefined && limit != null) 'limit': (limit as int),
|
||||
if (upCursor != _undefined) 'upCursor': (upCursor as String?),
|
||||
if (downCursor != _undefined) 'downCursor': (downCursor as String?),
|
||||
if (filterBySlice != _undefined)
|
||||
'filterBySlice': (filterBySlice as String?),
|
||||
}));
|
||||
}
|
||||
|
||||
|
@ -481,6 +508,7 @@ class _CopyWithStubImpl$Variables$Query$Logs<TRes>
|
|||
int? limit,
|
||||
String? upCursor,
|
||||
String? downCursor,
|
||||
String? filterBySlice,
|
||||
}) =>
|
||||
_res;
|
||||
}
|
||||
|
@ -645,6 +673,15 @@ const documentNodeQueryLogs = DocumentNode(definitions: [
|
|||
defaultValue: DefaultValueNode(value: null),
|
||||
directives: [],
|
||||
),
|
||||
VariableDefinitionNode(
|
||||
variable: VariableNode(name: NameNode(value: 'filterBySlice')),
|
||||
type: NamedTypeNode(
|
||||
name: NameNode(value: 'String'),
|
||||
isNonNull: false,
|
||||
),
|
||||
defaultValue: DefaultValueNode(value: null),
|
||||
directives: [],
|
||||
),
|
||||
],
|
||||
directives: [],
|
||||
selectionSet: SelectionSetNode(selections: [
|
||||
|
@ -670,6 +707,10 @@ const documentNodeQueryLogs = DocumentNode(definitions: [
|
|||
name: NameNode(value: 'downCursor'),
|
||||
value: VariableNode(name: NameNode(value: 'downCursor')),
|
||||
),
|
||||
ArgumentNode(
|
||||
name: NameNode(value: 'filterBySlice'),
|
||||
value: VariableNode(name: NameNode(value: 'filterBySlice')),
|
||||
),
|
||||
],
|
||||
directives: [],
|
||||
selectionSet: SelectionSetNode(selections: [
|
||||
|
|
|
@ -146,6 +146,13 @@ interface ConfigItem {
|
|||
type: String!
|
||||
}
|
||||
|
||||
type CpuMonitoring {
|
||||
start: DateTime
|
||||
end: DateTime
|
||||
step: Int!
|
||||
overallUsage: MonitoringValuesResult!
|
||||
}
|
||||
|
||||
"""Date with time (isoformat)"""
|
||||
scalar DateTime
|
||||
|
||||
|
@ -156,6 +163,13 @@ type DeviceApiTokenMutationReturn implements MutationReturnInterface {
|
|||
token: String
|
||||
}
|
||||
|
||||
type DiskMonitoring {
|
||||
start: DateTime
|
||||
end: DateTime
|
||||
step: Int!
|
||||
overallUsage: MonitoringMetricsResult!
|
||||
}
|
||||
|
||||
enum DnsProvider {
|
||||
CLOUDFLARE
|
||||
DIGITALOCEAN
|
||||
|
@ -210,9 +224,9 @@ input InitializeRepositoryInput {
|
|||
}
|
||||
|
||||
"""
|
||||
The `JSON` scalar type represents JSON values as specified by [ECMA-404](http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf).
|
||||
The `JSON` scalar type represents JSON values as specified by [ECMA-404](https://ecma-international.org/wp-content/uploads/ECMA-404_2nd_edition_december_2017.pdf).
|
||||
"""
|
||||
scalar JSON @specifiedBy(url: "http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf")
|
||||
scalar JSON @specifiedBy(url: "https://ecma-international.org/wp-content/uploads/ECMA-404_2nd_edition_december_2017.pdf")
|
||||
|
||||
type Job {
|
||||
getJobs: [ApiJob!]!
|
||||
|
@ -233,7 +247,7 @@ type LogEntry {
|
|||
}
|
||||
|
||||
type Logs {
|
||||
paginated(limit: Int! = 20, upCursor: String = null, downCursor: String = null): PaginatedEntries!
|
||||
paginated(limit: Int! = 20, upCursor: String = null, downCursor: String = null, filterBySlice: String = null, filterByUnit: String = null): PaginatedEntries!
|
||||
}
|
||||
|
||||
type LogsPageMeta {
|
||||
|
@ -241,6 +255,15 @@ type LogsPageMeta {
|
|||
downCursor: String
|
||||
}
|
||||
|
||||
type MemoryMonitoring {
|
||||
start: DateTime
|
||||
end: DateTime
|
||||
step: Int!
|
||||
overallUsage: MonitoringValuesResult!
|
||||
averageUsageByService: MonitoringMetricsResult!
|
||||
maxUsageByService: MonitoringMetricsResult!
|
||||
}
|
||||
|
||||
input MigrateToBindsInput {
|
||||
emailBlockDevice: String!
|
||||
bitwardenBlockDevice: String!
|
||||
|
@ -249,6 +272,39 @@ input MigrateToBindsInput {
|
|||
pleromaBlockDevice: String!
|
||||
}
|
||||
|
||||
type Monitoring {
|
||||
cpuUsage(start: DateTime = null, end: DateTime = null, step: Int! = 60): CpuMonitoring!
|
||||
memoryUsage(start: DateTime = null, end: DateTime = null, step: Int! = 60): MemoryMonitoring!
|
||||
diskUsage(start: DateTime = null, end: DateTime = null, step: Int! = 60): DiskMonitoring!
|
||||
networkUsage(start: DateTime = null, end: DateTime = null, step: Int! = 60): NetworkMonitoring!
|
||||
}
|
||||
|
||||
type MonitoringMetric {
|
||||
id: String!
|
||||
values: [MonitoringValue!]!
|
||||
}
|
||||
|
||||
type MonitoringMetrics {
|
||||
metrics: [MonitoringMetric!]!
|
||||
}
|
||||
|
||||
union MonitoringMetricsResult = MonitoringMetrics | MonitoringQueryError
|
||||
|
||||
type MonitoringQueryError {
|
||||
error: String!
|
||||
}
|
||||
|
||||
type MonitoringValue {
|
||||
timestamp: DateTime!
|
||||
value: String!
|
||||
}
|
||||
|
||||
type MonitoringValues {
|
||||
values: [MonitoringValue!]!
|
||||
}
|
||||
|
||||
union MonitoringValuesResult = MonitoringValues | MonitoringQueryError
|
||||
|
||||
input MoveServiceInput {
|
||||
serviceId: String!
|
||||
location: String!
|
||||
|
@ -301,6 +357,13 @@ interface MutationReturnInterface {
|
|||
code: Int!
|
||||
}
|
||||
|
||||
type NetworkMonitoring {
|
||||
start: DateTime
|
||||
end: DateTime
|
||||
step: Int!
|
||||
overallUsage: MonitoringMetricsResult!
|
||||
}
|
||||
|
||||
type PaginatedEntries {
|
||||
"""Metadata to aid in pagination."""
|
||||
pageMeta: LogsPageMeta!
|
||||
|
@ -318,6 +381,7 @@ type Query {
|
|||
jobs: Job!
|
||||
services: Services!
|
||||
backup: Backup!
|
||||
monitoring: Monitoring!
|
||||
}
|
||||
|
||||
input RecoveryKeyLimitsInput {
|
||||
|
@ -357,6 +421,7 @@ type Service {
|
|||
isMovable: Boolean!
|
||||
isRequired: Boolean!
|
||||
isEnabled: Boolean!
|
||||
isInstalled: Boolean!
|
||||
canBeBackedUp: Boolean!
|
||||
backupDescription: String!
|
||||
status: ServiceStatusEnum!
|
||||
|
|
|
@ -2289,5 +2289,13 @@ const possibleTypesMap = <String, Set<String>>{
|
|||
'EnumConfigItem',
|
||||
'StringConfigItem',
|
||||
},
|
||||
'MonitoringMetricsResult': {
|
||||
'MonitoringMetrics',
|
||||
'MonitoringQueryError',
|
||||
},
|
||||
'MonitoringValuesResult': {
|
||||
'MonitoringValues',
|
||||
'MonitoringQueryError',
|
||||
},
|
||||
'StorageUsageInterface': {'ServiceStorageUsage'},
|
||||
};
|
||||
|
|
|
@ -5,6 +5,7 @@ mixin LogsApi on GraphQLApiMap {
|
|||
required final int limit,
|
||||
final String? upCursor,
|
||||
final String? downCursor,
|
||||
final String? slice,
|
||||
}) async {
|
||||
QueryResult<Query$Logs> response;
|
||||
List<ServerLogEntry> logsList = [];
|
||||
|
@ -17,6 +18,7 @@ mixin LogsApi on GraphQLApiMap {
|
|||
upCursor: upCursor,
|
||||
downCursor: downCursor,
|
||||
limit: limit,
|
||||
filterBySlice: slice,
|
||||
);
|
||||
final query = Options$Query$Logs(variables: variables);
|
||||
response = await client.query$Logs(query);
|
||||
|
|
|
@ -15,16 +15,20 @@ class ServerLogsBloc extends Bloc<ServerLogsEvent, ServerLogsState> {
|
|||
ServerLogsBloc() : super(ServerLogsInitial()) {
|
||||
on<ServerLogsFetch>((final event, final emit) async {
|
||||
emit(ServerLogsLoading());
|
||||
final String? slice = event.serviceId != null
|
||||
? '${event.serviceId?.replaceAll('-', '_')}.slice'
|
||||
: null;
|
||||
try {
|
||||
final (logsData, meta) = await _getLogs(limit: 50);
|
||||
final (logsData, meta) = await _getLogs(limit: 50, slice: slice);
|
||||
emit(
|
||||
ServerLogsLoaded(
|
||||
logsData.sorted(
|
||||
oldEntries: logsData.sorted(
|
||||
(final a, final b) => b.timestamp.compareTo(a.timestamp),
|
||||
),
|
||||
List<ServerLogEntry>.empty(growable: true),
|
||||
meta,
|
||||
false,
|
||||
newEntries: List<ServerLogEntry>.empty(growable: true),
|
||||
meta: meta,
|
||||
loadingMore: false,
|
||||
slice: slice,
|
||||
),
|
||||
);
|
||||
if (_apiLogsSubscription != null) {
|
||||
|
@ -49,17 +53,21 @@ class ServerLogsBloc extends Bloc<ServerLogsEvent, ServerLogsState> {
|
|||
!currentState.loadingMore &&
|
||||
currentState.meta.upCursor != null) {
|
||||
try {
|
||||
final (logsData, meta) =
|
||||
await _getLogs(limit: 50, downCursor: currentState.meta.upCursor);
|
||||
final (logsData, meta) = await _getLogs(
|
||||
limit: 50,
|
||||
downCursor: currentState.meta.upCursor,
|
||||
slice: currentState.slice,
|
||||
);
|
||||
final allEntries = currentState.oldEntries
|
||||
..addAll(logsData)
|
||||
..sort((final a, final b) => b.timestamp.compareTo(a.timestamp));
|
||||
emit(
|
||||
ServerLogsLoaded(
|
||||
allEntries.toSet().toList(),
|
||||
currentState.newEntries,
|
||||
meta,
|
||||
false,
|
||||
oldEntries: allEntries.toSet().toList(),
|
||||
newEntries: currentState.newEntries,
|
||||
meta: meta,
|
||||
loadingMore: false,
|
||||
slice: currentState.slice,
|
||||
),
|
||||
);
|
||||
} catch (e) {
|
||||
|
@ -71,6 +79,10 @@ class ServerLogsBloc extends Bloc<ServerLogsEvent, ServerLogsState> {
|
|||
on<ServerLogsGotNewEntry>((final event, final emit) {
|
||||
final currentState = state;
|
||||
if (currentState is ServerLogsLoaded) {
|
||||
if (currentState.slice != null &&
|
||||
event.entry.systemdSlice != currentState.slice) {
|
||||
return;
|
||||
}
|
||||
final allEntries = currentState.newEntries
|
||||
..add(event.entry)
|
||||
..sort(
|
||||
|
@ -78,10 +90,11 @@ class ServerLogsBloc extends Bloc<ServerLogsEvent, ServerLogsState> {
|
|||
);
|
||||
emit(
|
||||
ServerLogsLoaded(
|
||||
currentState.oldEntries,
|
||||
allEntries.toSet().toList(),
|
||||
currentState.meta,
|
||||
currentState.loadingMore,
|
||||
oldEntries: currentState.oldEntries,
|
||||
newEntries: allEntries.toSet().toList(),
|
||||
meta: currentState.meta,
|
||||
loadingMore: currentState.loadingMore,
|
||||
slice: currentState.slice,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -103,6 +116,7 @@ class ServerLogsBloc extends Bloc<ServerLogsEvent, ServerLogsState> {
|
|||
// All entries returned will be greater than this cursor. Sets lower bound on results.
|
||||
final String? downCursor,
|
||||
// Only one cursor can be set at a time.
|
||||
final String? slice,
|
||||
}) {
|
||||
final String? apiVersion =
|
||||
getIt<ApiConnectionRepository>().apiData.apiVersion.data;
|
||||
|
@ -124,6 +138,7 @@ class ServerLogsBloc extends Bloc<ServerLogsEvent, ServerLogsState> {
|
|||
limit: limit,
|
||||
upCursor: upCursor,
|
||||
downCursor: downCursor,
|
||||
slice: slice,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -5,6 +5,10 @@ sealed class ServerLogsEvent extends Equatable {
|
|||
}
|
||||
|
||||
final class ServerLogsFetch extends ServerLogsEvent {
|
||||
const ServerLogsFetch({this.serviceId});
|
||||
|
||||
final String? serviceId;
|
||||
|
||||
@override
|
||||
List<Object> get props => [];
|
||||
}
|
||||
|
|
|
@ -15,18 +15,20 @@ final class ServerLogsLoading extends ServerLogsState {
|
|||
}
|
||||
|
||||
final class ServerLogsLoaded extends ServerLogsState {
|
||||
ServerLogsLoaded(
|
||||
this.oldEntries,
|
||||
this.newEntries,
|
||||
this.meta,
|
||||
this.loadingMore,
|
||||
) : _lastCursor = newEntries.isEmpty ? '' : newEntries.first.cursor;
|
||||
ServerLogsLoaded({
|
||||
required this.oldEntries,
|
||||
required this.newEntries,
|
||||
required this.meta,
|
||||
required this.loadingMore,
|
||||
this.slice,
|
||||
}) : _lastCursor = newEntries.isEmpty ? '' : newEntries.first.cursor;
|
||||
|
||||
final List<ServerLogEntry> oldEntries;
|
||||
final List<ServerLogEntry> newEntries;
|
||||
final ServerLogsPageMeta meta;
|
||||
final bool loadingMore;
|
||||
final String _lastCursor;
|
||||
final String? slice;
|
||||
|
||||
List<String> get systemdUnits => oldEntries
|
||||
.map((final entry) => entry.systemdUnit ?? 'kernel')
|
||||
|
@ -56,7 +58,7 @@ final class ServerLogsLoaded extends ServerLogsState {
|
|||
}
|
||||
|
||||
@override
|
||||
List<Object> get props => [oldEntries, newEntries, meta, _lastCursor];
|
||||
List<Object?> get props => [oldEntries, newEntries, meta, _lastCursor, slice];
|
||||
}
|
||||
|
||||
final class ServerLogsError extends ServerLogsState {
|
||||
|
|
|
@ -6,7 +6,7 @@ class EmptyPagePlaceholder extends StatelessWidget {
|
|||
const EmptyPagePlaceholder({
|
||||
required this.title,
|
||||
required this.iconData,
|
||||
required this.description,
|
||||
this.description,
|
||||
this.showReadyCard = false,
|
||||
super.key,
|
||||
});
|
||||
|
@ -14,7 +14,7 @@ class EmptyPagePlaceholder extends StatelessWidget {
|
|||
final bool showReadyCard;
|
||||
final IconData iconData;
|
||||
final String title;
|
||||
final String description;
|
||||
final String? description;
|
||||
|
||||
@override
|
||||
Widget build(final BuildContext context) => showReadyCard
|
||||
|
@ -54,7 +54,7 @@ class _ContentWidget extends StatelessWidget {
|
|||
|
||||
final IconData iconData;
|
||||
final String title;
|
||||
final String description;
|
||||
final String? description;
|
||||
|
||||
@override
|
||||
Widget build(final BuildContext context) => Container(
|
||||
|
@ -76,14 +76,15 @@ class _ContentWidget extends StatelessWidget {
|
|||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const Gap(8),
|
||||
Text(
|
||||
description,
|
||||
textAlign: TextAlign.center,
|
||||
style: Theme.of(context).textTheme.titleSmall?.copyWith(
|
||||
color: Theme.of(context).colorScheme.onBackground,
|
||||
),
|
||||
),
|
||||
if (description != null) const Gap(8),
|
||||
if (description != null)
|
||||
Text(
|
||||
description!,
|
||||
textAlign: TextAlign.center,
|
||||
style: Theme.of(context).textTheme.titleSmall?.copyWith(
|
||||
color: Theme.of(context).colorScheme.onBackground,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
|
|
@ -10,7 +10,9 @@ import 'package:selfprivacy/utils/platform_adapter.dart';
|
|||
|
||||
@RoutePage()
|
||||
class ServerLogsScreen extends StatefulWidget {
|
||||
const ServerLogsScreen({super.key});
|
||||
const ServerLogsScreen({this.serviceId, super.key});
|
||||
|
||||
final String? serviceId;
|
||||
|
||||
@override
|
||||
State<ServerLogsScreen> createState() => _ServerLogsScreenState();
|
||||
|
@ -27,7 +29,7 @@ class _ServerLogsScreenState extends State<ServerLogsScreen> {
|
|||
super.initState();
|
||||
_serverLogsBloc = BlocProvider.of<ServerLogsBloc>(context);
|
||||
_scrollController.addListener(_onScroll);
|
||||
_serverLogsBloc.add(ServerLogsFetch());
|
||||
_serverLogsBloc.add(ServerLogsFetch(serviceId: widget.serviceId));
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -82,7 +84,7 @@ class _ServerLogsScreenState extends State<ServerLogsScreen> {
|
|||
const Key centerKey = ValueKey<String>('server-logs-center-key');
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text('server.logs'.tr()),
|
||||
title: Text(widget.serviceId == null ? 'server.logs'.tr() : 'service_page.logs'.tr()),
|
||||
),
|
||||
endDrawer: BlocBuilder<ServerLogsBloc, ServerLogsState>(
|
||||
builder: (final context, final state) {
|
||||
|
@ -107,6 +109,14 @@ class _ServerLogsScreenState extends State<ServerLogsScreen> {
|
|||
_selectedSystemdUnit == null
|
||||
? state.oldEntries
|
||||
: state.oldEntriesForUnit(_selectedSystemdUnit!);
|
||||
if (filteredOldLogs.isEmpty && filteredNewLogs.isEmpty) {
|
||||
return Center(
|
||||
child: EmptyPagePlaceholder(
|
||||
title: 'server.logs_empty'.tr(),
|
||||
iconData: Icons.info_outline,
|
||||
),
|
||||
);
|
||||
}
|
||||
return CustomScrollView(
|
||||
center: centerKey,
|
||||
controller: _scrollController,
|
||||
|
|
|
@ -83,7 +83,7 @@ class _ServerDetailsScreenState extends State<ServerDetailsScreen>
|
|||
ListTile(
|
||||
title: Text('server.logs'.tr()),
|
||||
leading: const Icon(Icons.manage_search_outlined),
|
||||
onTap: () => context.pushRoute(const ServerLogsRoute()),
|
||||
onTap: () => context.pushRoute(ServerLogsRoute()),
|
||||
),
|
||||
const Divider(height: 32),
|
||||
Text(
|
||||
|
|
|
@ -172,6 +172,17 @@ class _ServicePageState extends State<ServicePage> {
|
|||
style: Theme.of(context).textTheme.titleMedium,
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
iconColor: Theme.of(context).colorScheme.onBackground,
|
||||
onTap: () => context.pushRoute(
|
||||
ServerLogsRoute(serviceId: service.id),
|
||||
),
|
||||
leading: const Icon(Icons.manage_search_outlined),
|
||||
title: Text(
|
||||
'service_page.logs'.tr(),
|
||||
style: Theme.of(context).textTheme.titleMedium,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
|
|
@ -133,9 +133,14 @@ abstract class _$RootRouter extends RootStackRouter {
|
|||
);
|
||||
},
|
||||
ServerLogsRoute.name: (routeData) {
|
||||
final args = routeData.argsAs<ServerLogsRouteArgs>(
|
||||
orElse: () => const ServerLogsRouteArgs());
|
||||
return AutoRoutePage<dynamic>(
|
||||
routeData: routeData,
|
||||
child: const ServerLogsScreen(),
|
||||
child: ServerLogsScreen(
|
||||
serviceId: args.serviceId,
|
||||
key: args.key,
|
||||
),
|
||||
);
|
||||
},
|
||||
ServerSettingsRoute.name: (routeData) {
|
||||
|
@ -524,16 +529,40 @@ class ServerDetailsRoute extends PageRouteInfo<void> {
|
|||
|
||||
/// generated route for
|
||||
/// [ServerLogsScreen]
|
||||
class ServerLogsRoute extends PageRouteInfo<void> {
|
||||
const ServerLogsRoute({List<PageRouteInfo>? children})
|
||||
: super(
|
||||
class ServerLogsRoute extends PageRouteInfo<ServerLogsRouteArgs> {
|
||||
ServerLogsRoute({
|
||||
String? serviceId,
|
||||
Key? key,
|
||||
List<PageRouteInfo>? children,
|
||||
}) : super(
|
||||
ServerLogsRoute.name,
|
||||
args: ServerLogsRouteArgs(
|
||||
serviceId: serviceId,
|
||||
key: key,
|
||||
),
|
||||
initialChildren: children,
|
||||
);
|
||||
|
||||
static const String name = 'ServerLogsRoute';
|
||||
|
||||
static const PageInfo<void> page = PageInfo<void>(name);
|
||||
static const PageInfo<ServerLogsRouteArgs> page =
|
||||
PageInfo<ServerLogsRouteArgs>(name);
|
||||
}
|
||||
|
||||
class ServerLogsRouteArgs {
|
||||
const ServerLogsRouteArgs({
|
||||
this.serviceId,
|
||||
this.key,
|
||||
});
|
||||
|
||||
final String? serviceId;
|
||||
|
||||
final Key? key;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'ServerLogsRouteArgs{serviceId: $serviceId, key: $key}';
|
||||
}
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
|
|
Loading…
Reference in a new issue