feat: Add logging stack configuration and related localization

This commit is contained in:
2025-01-03 03:10:23 +00:00
committed by GitHub
parent 053184d769
commit 7ed8f4d529
9 changed files with 373 additions and 4 deletions

View File

@@ -11,6 +11,7 @@ import 'config.dart';
import 'eh.dart';
import 'file.dart';
import 'gallery.dart';
import 'log.dart';
import 'status.dart';
import 'tags.dart';
import 'task.dart';
@@ -379,6 +380,32 @@ abstract class _EHApi {
@GET('/task/import_cfg')
Future<ApiResult<DefaultImportConfig>> getDefaultImportConfig(
{@CancelRequest() CancelToken? cancel});
@GET('/log')
Future<ApiResult<LogEntries>> queryLog(
{@Query("page") int? page,
@Query("limit") int? limit,
@Query("offset") int? offset,
@Query("type") String? type,
@Query("min_level") int? minLevel,
@Query("allowed_level") String? allowedLevel,
@CancelRequest() CancelToken? cancel});
@DELETE('/log')
@MultiPart()
Future<ApiResult<bool>> clearLog(
{@Part(name: "type") String? type,
@Part(name: "min_level") int? minLevel,
@Part(name: "max_level") int? maxLevel,
@Part(name: "deleted_level") String? deletedLevel,
@Part(name: "end_time") String? endTime,
@CancelRequest() CancelToken? cancel});
@GET('/log/{id}')
Future<ApiResult<LogEntry>> getLog(@Path("id") int id,
{@CancelRequest() CancelToken? cancel});
@DELETE('/log/{id}')
@MultiPart()
Future<ApiResult<bool>> deleteLog(@Path("id") int id,
{@CancelRequest() CancelToken? cancel});
}
class EHApi extends __EHApi {

View File

@@ -2283,6 +2283,218 @@ class __EHApi implements _EHApi {
return _value;
}
@override
Future<ApiResult<LogEntries>> queryLog({
int? page,
int? limit,
int? offset,
String? type,
int? minLevel,
String? allowedLevel,
CancelToken? cancel,
}) async {
final _extra = <String, dynamic>{};
final queryParameters = <String, dynamic>{
r'page': page,
r'limit': limit,
r'offset': offset,
r'type': type,
r'min_level': minLevel,
r'allowed_level': allowedLevel,
};
queryParameters.removeWhere((k, v) => v == null);
final _headers = <String, dynamic>{};
const Map<String, dynamic>? _data = null;
final _options = _setStreamType<ApiResult<LogEntries>>(Options(
method: 'GET',
headers: _headers,
extra: _extra,
)
.compose(
_dio.options,
'/log',
queryParameters: queryParameters,
data: _data,
cancelToken: cancel,
)
.copyWith(
baseUrl: _combineBaseUrls(
_dio.options.baseUrl,
baseUrl,
)));
final _result = await _dio.fetch<Map<String, dynamic>>(_options);
late ApiResult<LogEntries> _value;
try {
_value = ApiResult<LogEntries>.fromJson(
_result.data!,
(json) => LogEntries.fromJson(json as Map<String, dynamic>),
);
} on Object catch (e, s) {
errorLogger?.logError(e, s, _options);
rethrow;
}
return _value;
}
@override
Future<ApiResult<bool>> clearLog({
String? type,
int? minLevel,
int? maxLevel,
String? deletedLevel,
String? endTime,
CancelToken? cancel,
}) async {
final _extra = <String, dynamic>{};
final queryParameters = <String, dynamic>{};
queryParameters.removeWhere((k, v) => v == null);
final _headers = <String, dynamic>{};
final _data = FormData();
if (type != null) {
_data.fields.add(MapEntry(
'type',
type,
));
}
if (minLevel != null) {
_data.fields.add(MapEntry(
'min_level',
minLevel.toString(),
));
}
if (maxLevel != null) {
_data.fields.add(MapEntry(
'max_level',
maxLevel.toString(),
));
}
if (deletedLevel != null) {
_data.fields.add(MapEntry(
'deleted_level',
deletedLevel,
));
}
if (endTime != null) {
_data.fields.add(MapEntry(
'end_time',
endTime,
));
}
final _options = _setStreamType<ApiResult<bool>>(Options(
method: 'DELETE',
headers: _headers,
extra: _extra,
contentType: 'multipart/form-data',
)
.compose(
_dio.options,
'/log',
queryParameters: queryParameters,
data: _data,
cancelToken: cancel,
)
.copyWith(
baseUrl: _combineBaseUrls(
_dio.options.baseUrl,
baseUrl,
)));
final _result = await _dio.fetch<Map<String, dynamic>>(_options);
late ApiResult<bool> _value;
try {
_value = ApiResult<bool>.fromJson(
_result.data!,
(json) => json as bool,
);
} on Object catch (e, s) {
errorLogger?.logError(e, s, _options);
rethrow;
}
return _value;
}
@override
Future<ApiResult<LogEntry>> getLog(
int id, {
CancelToken? cancel,
}) async {
final _extra = <String, dynamic>{};
final queryParameters = <String, dynamic>{};
queryParameters.removeWhere((k, v) => v == null);
final _headers = <String, dynamic>{};
const Map<String, dynamic>? _data = null;
final _options = _setStreamType<ApiResult<LogEntry>>(Options(
method: 'GET',
headers: _headers,
extra: _extra,
)
.compose(
_dio.options,
'/log/${id}',
queryParameters: queryParameters,
data: _data,
cancelToken: cancel,
)
.copyWith(
baseUrl: _combineBaseUrls(
_dio.options.baseUrl,
baseUrl,
)));
final _result = await _dio.fetch<Map<String, dynamic>>(_options);
late ApiResult<LogEntry> _value;
try {
_value = ApiResult<LogEntry>.fromJson(
_result.data!,
(json) => LogEntry.fromJson(json as Map<String, dynamic>),
);
} on Object catch (e, s) {
errorLogger?.logError(e, s, _options);
rethrow;
}
return _value;
}
@override
Future<ApiResult<bool>> deleteLog(
int id, {
CancelToken? cancel,
}) async {
final _extra = <String, dynamic>{};
final queryParameters = <String, dynamic>{};
queryParameters.removeWhere((k, v) => v == null);
final _headers = <String, dynamic>{};
const Map<String, dynamic>? _data = null;
final _options = _setStreamType<ApiResult<bool>>(Options(
method: 'DELETE',
headers: _headers,
extra: _extra,
contentType: 'multipart/form-data',
)
.compose(
_dio.options,
'/log/${id}',
queryParameters: queryParameters,
data: _data,
cancelToken: cancel,
)
.copyWith(
baseUrl: _combineBaseUrls(
_dio.options.baseUrl,
baseUrl,
)));
final _result = await _dio.fetch<Map<String, dynamic>>(_options);
late ApiResult<bool> _value;
try {
_value = ApiResult<bool>.fromJson(
_result.data!,
(json) => json as bool,
);
} on Object catch (e, s) {
errorLogger?.logError(e, s, _options);
rethrow;
}
return _value;
}
RequestOptions _setStreamType<T>(RequestOptions requestOptions) {
if (T != dynamic &&
!(requestOptions.responseType == ResponseType.bytes ||

View File

@@ -83,6 +83,7 @@ class Config {
required this.maxImportImgCount,
required this.enableServerTiming,
required this.thumbnailFormat,
required this.loggingStack,
});
bool cookies;
@JsonKey(name: 'db_path')
@@ -151,6 +152,8 @@ class Config {
bool enableServerTiming;
@JsonKey(name: 'thumbnail_format')
ThumbnailFormat thumbnailFormat;
@JsonKey(name: 'logging_stack')
bool loggingStack;
factory Config.fromJson(Map<String, dynamic> json) => _$ConfigFromJson(json);
Map<String, dynamic> toJson() => _$ConfigToJson(this);
}
@@ -206,6 +209,8 @@ class ConfigOptional {
this.importMethod,
this.maxImportImgCount,
this.enableServerTiming,
this.thumbnailFormat,
this.loggingStack,
});
String? cookies;
@JsonKey(name: 'db_path')
@@ -274,6 +279,8 @@ class ConfigOptional {
bool? enableServerTiming;
@JsonKey(name: 'thumbnail_format')
ThumbnailFormat? thumbnailFormat;
@JsonKey(name: 'logging_stack')
bool? loggingStack;
factory ConfigOptional.fromJson(Map<String, dynamic> json) =>
_$ConfigOptionalFromJson(json);
Map<String, dynamic> toJson() => _$ConfigOptionalToJson(this);

View File

@@ -51,6 +51,7 @@ Config _$ConfigFromJson(Map<String, dynamic> json) => Config(
enableServerTiming: json['enable_server_timing'] as bool,
thumbnailFormat:
$enumDecode(_$ThumbnailFormatEnumMap, json['thumbnail_format']),
loggingStack: json['logging_stack'] as bool,
);
Map<String, dynamic> _$ConfigToJson(Config instance) => <String, dynamic>{
@@ -91,6 +92,7 @@ Map<String, dynamic> _$ConfigToJson(Config instance) => <String, dynamic>{
'max_import_img_count': instance.maxImportImgCount,
'enable_server_timing': instance.enableServerTiming,
'thumbnail_format': _$ThumbnailFormatEnumMap[instance.thumbnailFormat]!,
'logging_stack': instance.loggingStack,
};
const _$ThumbnailMethodEnumMap = {
@@ -165,8 +167,10 @@ ConfigOptional _$ConfigOptionalFromJson(Map<String, dynamic> json) =>
$enumDecodeNullable(_$ImportMethodEnumMap, json['import_method']),
maxImportImgCount: (json['max_import_img_count'] as num?)?.toInt(),
enableServerTiming: json['enable_server_timing'] as bool?,
)..thumbnailFormat =
$enumDecodeNullable(_$ThumbnailFormatEnumMap, json['thumbnail_format']);
thumbnailFormat: $enumDecodeNullable(
_$ThumbnailFormatEnumMap, json['thumbnail_format']),
loggingStack: json['logging_stack'] as bool?,
);
Map<String, dynamic> _$ConfigOptionalToJson(ConfigOptional instance) =>
<String, dynamic>{
@@ -207,4 +211,5 @@ Map<String, dynamic> _$ConfigOptionalToJson(ConfigOptional instance) =>
'max_import_img_count': instance.maxImportImgCount,
'enable_server_timing': instance.enableServerTiming,
'thumbnail_format': _$ThumbnailFormatEnumMap[instance.thumbnailFormat],
'logging_stack': instance.loggingStack,
};

57
lib/api/log.dart Normal file
View File

@@ -0,0 +1,57 @@
import 'package:json_annotation/json_annotation.dart';
part 'log.g.dart';
enum LogLevel {
@JsonValue(1)
trace,
@JsonValue(2)
debug,
@JsonValue(3)
log,
@JsonValue(4)
info,
@JsonValue(5)
warn,
@JsonValue(6)
error;
}
@JsonSerializable()
class LogEntry {
LogEntry({
required this.id,
required this.time,
required this.message,
required this.level,
required this.type,
this.stack,
});
int id;
@JsonKey(fromJson: _fromJson, toJson: _toJson)
DateTime time;
String message;
LogLevel level;
String type;
String? stack;
static DateTime _fromJson(String d) => DateTime.parse(d);
static String _toJson(DateTime d) => d.toIso8601String();
factory LogEntry.fromJson(Map<String, dynamic> json) =>
_$LogEntryFromJson(json);
Map<String, dynamic> toJson() => _$LogEntryToJson(this);
}
@JsonSerializable()
class LogEntries {
LogEntries({
required this.datas,
this.count,
});
List<LogEntry> datas;
int? count;
factory LogEntries.fromJson(Map<String, dynamic> json) =>
_$LogEntriesFromJson(json);
Map<String, dynamic> toJson() => _$LogEntriesToJson(this);
}

47
lib/api/log.g.dart Normal file
View File

@@ -0,0 +1,47 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'log.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
LogEntry _$LogEntryFromJson(Map<String, dynamic> json) => LogEntry(
id: (json['id'] as num).toInt(),
time: LogEntry._fromJson(json['time'] as String),
message: json['message'] as String,
level: $enumDecode(_$LogLevelEnumMap, json['level']),
type: json['type'] as String,
stack: json['stack'] as String?,
);
Map<String, dynamic> _$LogEntryToJson(LogEntry instance) => <String, dynamic>{
'id': instance.id,
'time': LogEntry._toJson(instance.time),
'message': instance.message,
'level': _$LogLevelEnumMap[instance.level]!,
'type': instance.type,
'stack': instance.stack,
};
const _$LogLevelEnumMap = {
LogLevel.trace: 1,
LogLevel.debug: 2,
LogLevel.log: 3,
LogLevel.info: 4,
LogLevel.warn: 5,
LogLevel.error: 6,
};
LogEntries _$LogEntriesFromJson(Map<String, dynamic> json) => LogEntries(
datas: (json['datas'] as List<dynamic>)
.map((e) => LogEntry.fromJson(e as Map<String, dynamic>))
.toList(),
count: (json['count'] as num?)?.toInt(),
);
Map<String, dynamic> _$LogEntriesToJson(LogEntries instance) =>
<String, dynamic>{
'datas': instance.datas,
'count': instance.count,
};