Better audit logs

This commit is contained in:
LittleSheep 2024-10-14 22:05:49 +08:00
parent 77288713e1
commit 48e3b510cf
7 changed files with 126 additions and 46 deletions

View File

@ -485,5 +485,6 @@
"auditLog": "Audit log", "auditLog": "Audit log",
"shareImage": "Share as image", "shareImage": "Share as image",
"shareImageFooter": "Only on the Solar Network", "shareImageFooter": "Only on the Solar Network",
"fileSavedAt": "File saved at @path" "fileSavedAt": "File saved at @path",
"showIp": "Show IP Address"
} }

View File

@ -481,5 +481,6 @@
"auditLog": "活动日志", "auditLog": "活动日志",
"shareImage": "分享图片", "shareImage": "分享图片",
"shareImageFooter": "上 Solar Network 看更多有趣帖子", "shareImageFooter": "上 Solar Network 看更多有趣帖子",
"fileSavedAt": "文件保存于 @path" "fileSavedAt": "文件保存于 @path",
"showIp": "显示 IP 地址"
} }

View File

@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:gap/gap.dart'; import 'package:gap/gap.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:google_fonts/google_fonts.dart'; import 'package:google_fonts/google_fonts.dart';
import 'package:marquee/marquee.dart';
import 'package:solian/exceptions/request.dart'; import 'package:solian/exceptions/request.dart';
import 'package:solian/exts.dart'; import 'package:solian/exts.dart';
import 'package:solian/models/audit_log.dart'; import 'package:solian/models/audit_log.dart';
@ -29,7 +30,7 @@ class _AuditLogScreenState extends State<AuditLogScreen> {
final AuthProvider auth = Get.find(); final AuthProvider auth = Get.find();
final client = await auth.configureClient('id'); final client = await auth.configureClient('id');
final resp = final resp =
await client.get('/users/me/events?take=10&offset=${_events.length}'); await client.get('/users/me/events?take=15&offset=${_events.length}');
if (resp.statusCode != 200) { if (resp.statusCode != 200) {
context.showErrorDialog(RequestException(resp)); context.showErrorDialog(RequestException(resp));
} }
@ -45,6 +46,22 @@ class _AuditLogScreenState extends State<AuditLogScreen> {
}); });
} }
bool _showIp = false;
String _censorIpAddress(String ip) {
List<String> parts = ip.split('.');
if (parts.length == 4) {
String censoredPart1 = '*' * parts[1].length;
String censoredPart2 = '*' * parts[2].length;
String censoredPart3 = '*' * parts[3].length;
return '${parts[0]}.$censoredPart1.$censoredPart2.$censoredPart3';
} else {
return '***.***.***.***';
}
}
@override @override
void initState() { void initState() {
super.initState(); super.initState();
@ -53,7 +70,27 @@ class _AuditLogScreenState extends State<AuditLogScreen> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return InfiniteList( return Column(
children: [
CheckboxListTile(
value: _showIp,
title: Text('showIp'.tr),
contentPadding: const EdgeInsets.symmetric(horizontal: 24),
secondary: const Icon(Icons.alternate_email),
tileColor:
Theme.of(context).colorScheme.surfaceContainer.withOpacity(0.5),
onChanged: (val) {
setState(() => _showIp = val ?? false);
},
),
Expanded(
child: RefreshIndicator(
onRefresh: () {
_events.clear();
return _getEvents();
},
child: InfiniteList(
padding: const EdgeInsets.symmetric(vertical: 12),
itemCount: _events.length, itemCount: _events.length,
isLoading: _isBusy, isLoading: _isBusy,
onFetchData: () { onFetchData: () {
@ -65,6 +102,7 @@ class _AuditLogScreenState extends State<AuditLogScreen> {
isFirst: idx == 0, isFirst: idx == 0,
isLast: _events.length - 1 == idx, isLast: _events.length - 1 == idx,
alignment: TimelineAlign.start, alignment: TimelineAlign.start,
indicatorStyle: IndicatorStyle(width: 15),
endChild: Container( endChild: Container(
child: Card( child: Card(
child: Column( child: Column(
@ -74,6 +112,24 @@ class _AuditLogScreenState extends State<AuditLogScreen> {
element.type, element.type,
style: GoogleFonts.robotoMono(fontSize: 15), style: GoogleFonts.robotoMono(fontSize: 15),
), ),
Text(
_showIp
? element.ipAddress
: _censorIpAddress(element.ipAddress),
style: GoogleFonts.sourceCodePro(
fontWeight: FontWeight.bold,
),
),
SizedBox(
height: 20,
width: double.maxFinite,
child: Marquee(
text: element.userAgent,
velocity: 25,
startAfter: Duration(milliseconds: 500),
pauseAfterRound: Duration(milliseconds: 3000),
),
),
Row( Row(
children: [ children: [
RelativeDate(element.createdAt), RelativeDate(element.createdAt),
@ -89,6 +145,10 @@ class _AuditLogScreenState extends State<AuditLogScreen> {
), ),
).paddingSymmetric(horizontal: 18); ).paddingSymmetric(horizontal: 18);
}, },
),
),
),
],
); );
} }
} }

View File

@ -62,9 +62,8 @@ class _PostDetailScreenState extends State<PostDetailScreen> {
return CustomScrollView( return CustomScrollView(
slivers: [ slivers: [
if (_isBusy)
SliverToBoxAdapter( SliverToBoxAdapter(
child: LoadingIndicator(), child: LoadingIndicator(isActive: _isBusy),
), ),
SliverToBoxAdapter( SliverToBoxAdapter(
child: PostItem( child: PostItem(

View File

@ -38,12 +38,14 @@ class _ChannelListWidgetState extends State<ChannelListWidget> {
Future<void> _loadLastMessages() async { Future<void> _loadLastMessages() async {
final messages = await _eventController.src.getLastInAllChannels(); final messages = await _eventController.src.getLastInAllChannels();
if (mounted) {
setState(() { setState(() {
_lastMessages = messages _lastMessages = messages
.map((k, v) => MapEntry(k, v.firstOrNull)) .map((k, v) => MapEntry(k, v.firstOrNull))
.cast<int, LocalMessageEventTableData>(); .cast<int, LocalMessageEventTableData>();
}); });
} }
}
@override @override
void initState() { void initState() {

View File

@ -414,6 +414,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.0.5" version: "2.0.5"
fading_edge_scrollview:
dependency: transitive
description:
name: fading_edge_scrollview
sha256: "1f84fe3ea8e251d00d5735e27502a6a250e4aa3d3b330d3fdcb475af741464ef"
url: "https://pub.dev"
source: hosted
version: "4.1.1"
fake_async: fake_async:
dependency: transitive dependency: transitive
description: description:
@ -1277,6 +1285,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.5.0" version: "0.5.0"
marquee:
dependency: "direct main"
description:
name: marquee
sha256: a87e7e80c5d21434f90ad92add9f820cf68be374b226404fe881d2bba7be0862
url: "https://pub.dev"
source: hosted
version: "2.3.0"
matcher: matcher:
dependency: transitive dependency: transitive
description: description:

View File

@ -89,6 +89,7 @@ dependencies:
qr_flutter: ^4.1.0 qr_flutter: ^4.1.0
flutter_resizable_container: ^3.0.0 flutter_resizable_container: ^3.0.0
file_saver: ^0.2.14 file_saver: ^0.2.14
marquee: ^2.3.0
dev_dependencies: dev_dependencies:
flutter_test: flutter_test: