25ed765a03
6 Wochen uncommittete Arbeit (Voice-Channels, LiveKit-Manager, Settings-Modal u.v.m.) als ein WIP-Commit gesichert, damit nichts verloren geht und der Pi den aktuellen Stand klonen kann. Thematische Aufarbeitung: siehe ROADMAP M0. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CPrAGBxBT6GfPXzeWQ4AXb
84 lines
2.3 KiB
Dart
84 lines
2.3 KiB
Dart
import 'dart:async';
|
|
import 'dart:convert';
|
|
import 'dart:io';
|
|
|
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
|
|
class E2eeDiagEntry {
|
|
final DateTime timestamp;
|
|
final String roomId;
|
|
final String eventId;
|
|
final String sessionId;
|
|
final String error;
|
|
|
|
const E2eeDiagEntry({
|
|
required this.timestamp,
|
|
required this.roomId,
|
|
required this.eventId,
|
|
required this.sessionId,
|
|
required this.error,
|
|
});
|
|
|
|
Map<String, dynamic> toJson() => {
|
|
'ts': timestamp.toIso8601String(),
|
|
'room': roomId,
|
|
'event': eventId,
|
|
'session': sessionId,
|
|
'error': error,
|
|
};
|
|
}
|
|
|
|
class E2eeDiagnosticsNotifier extends Notifier<List<E2eeDiagEntry>> {
|
|
static const int _maxEntries = 500;
|
|
|
|
@override
|
|
List<E2eeDiagEntry> build() => [];
|
|
|
|
void add(E2eeDiagEntry entry) {
|
|
final next = [...state, entry];
|
|
state = next.length > _maxEntries ? next.sublist(next.length - _maxEntries) : next;
|
|
}
|
|
|
|
void clear() => state = [];
|
|
|
|
Future<String> upload({required String serverUrl, required String token}) async {
|
|
if (state.isEmpty) return 'Keine Diagnosedaten vorhanden.';
|
|
|
|
final payload = utf8.encode(jsonEncode({
|
|
'client': 'Pyramid',
|
|
'uploaded_at': DateTime.now().toIso8601String(),
|
|
'entry_count': state.length,
|
|
'entries': state.map((e) => e.toJson()).toList(),
|
|
}));
|
|
|
|
final client = HttpClient();
|
|
try {
|
|
final uri = Uri.parse('$serverUrl/api/e2ee-diagnostics');
|
|
final request = await client.postUrl(uri);
|
|
request.headers.set('Content-Type', 'application/json; charset=utf-8');
|
|
request.headers.set('Authorization', 'Bearer $token');
|
|
request.headers.contentLength = payload.length;
|
|
request.add(payload);
|
|
final response = await request.close();
|
|
await response.drain<void>();
|
|
if (response.statusCode == 200) {
|
|
final count = state.length;
|
|
clear();
|
|
return '$count Einträge hochgeladen und lokal gelöscht.';
|
|
}
|
|
return 'Server-Fehler: HTTP ${response.statusCode}';
|
|
} on SocketException catch (e) {
|
|
return 'Netzwerkfehler: ${e.message}';
|
|
} catch (e) {
|
|
return 'Fehler: $e';
|
|
} finally {
|
|
client.close();
|
|
}
|
|
}
|
|
}
|
|
|
|
final e2eeDiagnosticsProvider =
|
|
NotifierProvider<E2eeDiagnosticsNotifier, List<E2eeDiagEntry>>(
|
|
E2eeDiagnosticsNotifier.new,
|
|
);
|