mirror of
https://github.com/foss42/apidash.git
synced 2025-12-01 18:28:25 +08:00
feat: add terminal state provider
This commit is contained in:
@@ -2,3 +2,4 @@ export 'history_meta_model.dart';
|
||||
export 'history_request_model.dart';
|
||||
export 'request_model.dart';
|
||||
export 'settings_model.dart';
|
||||
export 'terminal_models.dart';
|
||||
|
||||
@@ -2,5 +2,6 @@ export 'ai_providers.dart';
|
||||
export 'collection_providers.dart';
|
||||
export 'environment_providers.dart';
|
||||
export 'history_providers.dart';
|
||||
export 'terminal_providers.dart';
|
||||
export 'settings_providers.dart';
|
||||
export 'ui_providers.dart';
|
||||
|
||||
179
lib/providers/terminal_providers.dart
Normal file
179
lib/providers/terminal_providers.dart
Normal file
@@ -0,0 +1,179 @@
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
import 'package:apidash_core/apidash_core.dart';
|
||||
import '../models/terminal_models.dart';
|
||||
|
||||
final terminalStateProvider =
|
||||
StateNotifierProvider<TerminalController, TerminalState>((ref) {
|
||||
return TerminalController();
|
||||
});
|
||||
|
||||
class TerminalState {
|
||||
TerminalState({required this.entries})
|
||||
: index = {
|
||||
for (var i = 0; i < entries.length; i++) entries[i].id: i,
|
||||
};
|
||||
|
||||
final List<TerminalEntry> entries; // newest first
|
||||
final Map<String, int> index;
|
||||
|
||||
TerminalState copyWith({List<TerminalEntry>? entries}) {
|
||||
return TerminalState(entries: entries ?? this.entries);
|
||||
}
|
||||
}
|
||||
|
||||
class TerminalController extends StateNotifier<TerminalState> {
|
||||
TerminalController() : super(TerminalState(entries: <TerminalEntry>[]));
|
||||
|
||||
static const _uuid = Uuid();
|
||||
|
||||
String _newId() => _uuid.v4();
|
||||
|
||||
void clear() {
|
||||
state = TerminalState(entries: <TerminalEntry>[]);
|
||||
}
|
||||
|
||||
String append(TerminalEntry entry) {
|
||||
final list = [entry, ...state.entries];
|
||||
state = TerminalState(entries: list);
|
||||
return entry.id;
|
||||
}
|
||||
|
||||
void _updateById(String id, TerminalEntry Function(TerminalEntry) updater) {
|
||||
final idx = state.index[id];
|
||||
if (idx == null) return;
|
||||
final current = state.entries[idx];
|
||||
final updated = updater(current);
|
||||
final list = [...state.entries];
|
||||
list[idx] = updated;
|
||||
state = TerminalState(entries: list);
|
||||
}
|
||||
|
||||
// Convenience builders
|
||||
String startNetwork({
|
||||
required APIType apiType,
|
||||
required HTTPVerb method,
|
||||
required String url,
|
||||
String? requestId,
|
||||
Map<String, String>? requestHeaders,
|
||||
String? requestBodyPreview,
|
||||
bool isStreaming = false,
|
||||
}) {
|
||||
final id = _newId();
|
||||
final entry = TerminalEntry(
|
||||
id: id,
|
||||
ts: DateTime.now(),
|
||||
source: TerminalSource.network,
|
||||
level: TerminalLevel.info,
|
||||
requestId: requestId,
|
||||
network: NetworkLogData(
|
||||
phase: NetworkPhase.started,
|
||||
apiType: apiType,
|
||||
method: method,
|
||||
url: url,
|
||||
requestHeaders: requestHeaders,
|
||||
requestBodyPreview: requestBodyPreview,
|
||||
isStreaming: isStreaming,
|
||||
sentAt: DateTime.now(),
|
||||
),
|
||||
);
|
||||
append(entry);
|
||||
return id;
|
||||
}
|
||||
|
||||
void addNetworkChunk(String logId, BodyChunk chunk) {
|
||||
_updateById(logId, (e) {
|
||||
final n = e.network;
|
||||
if (n == null) return e;
|
||||
n.chunks.add(chunk);
|
||||
return e.copyWith(
|
||||
network: n.copyWith(phase: NetworkPhase.progress),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
void completeNetwork(
|
||||
String logId, {
|
||||
int? statusCode,
|
||||
Map<String, String>? responseHeaders,
|
||||
String? responseBodyPreview,
|
||||
Duration? duration,
|
||||
}) {
|
||||
_updateById(logId, (e) {
|
||||
final n = e.network;
|
||||
if (n == null) return e;
|
||||
return e.copyWith(
|
||||
level: (statusCode != null && statusCode >= 400)
|
||||
? TerminalLevel.error
|
||||
: TerminalLevel.info,
|
||||
network: n.copyWith(
|
||||
phase: NetworkPhase.completed,
|
||||
responseStatus: statusCode,
|
||||
responseHeaders: responseHeaders,
|
||||
responseBodyPreview: responseBodyPreview,
|
||||
duration: duration,
|
||||
completedAt: DateTime.now(),
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
void failNetwork(String logId, String message) {
|
||||
_updateById(logId, (e) {
|
||||
final n = e.network;
|
||||
if (n == null) return e;
|
||||
return e.copyWith(
|
||||
level: TerminalLevel.error,
|
||||
network: n.copyWith(
|
||||
phase: NetworkPhase.failed,
|
||||
errorMessage: message,
|
||||
completedAt: DateTime.now(),
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
void logJs({
|
||||
required String level,
|
||||
required List<String> args,
|
||||
String? stack,
|
||||
String? context,
|
||||
String? contextRequestId,
|
||||
}) {
|
||||
append(TerminalEntry(
|
||||
id: _newId(),
|
||||
ts: DateTime.now(),
|
||||
source: TerminalSource.js,
|
||||
level: switch (level) {
|
||||
'warn' => TerminalLevel.warn,
|
||||
'error' || 'fatal' => TerminalLevel.error,
|
||||
_ => TerminalLevel.info,
|
||||
},
|
||||
requestId: contextRequestId,
|
||||
js: JsLogData(
|
||||
level: level,
|
||||
args: args,
|
||||
stack: stack,
|
||||
context: context,
|
||||
contextRequestId: contextRequestId,
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
void logSystem({
|
||||
required String category,
|
||||
required String message,
|
||||
String? stack,
|
||||
TerminalLevel level = TerminalLevel.info,
|
||||
List<String> tags = const <String>[],
|
||||
}) {
|
||||
append(TerminalEntry(
|
||||
id: _newId(),
|
||||
ts: DateTime.now(),
|
||||
source: TerminalSource.system,
|
||||
level: level,
|
||||
tags: tags,
|
||||
system: SystemLogData(category: category, message: message, stack: stack),
|
||||
));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user