environment_monitoring_app/lib/services/retry_service.dart

154 lines
5.7 KiB
Dart

// lib/services/retry_service.dart
import 'dart:convert';
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:environment_monitoring_app/services/api_service.dart';
import 'package:environment_monitoring_app/services/base_api_service.dart';
import 'package:environment_monitoring_app/services/ftp_service.dart';
// START CHANGE: Added imports to get server configurations
import 'package:environment_monitoring_app/services/server_config_service.dart';
// END CHANGE
/// A dedicated service to manage the queue of failed API and FTP requests
/// for manual resubmission.
class RetryService {
// Use singleton instances to avoid re-creating services unnecessarily.
final DatabaseHelper _dbHelper = DatabaseHelper();
final BaseApiService _baseApiService = BaseApiService();
final FtpService _ftpService = FtpService();
// START CHANGE: Add instance of ServerConfigService
final ServerConfigService _serverConfigService = ServerConfigService();
// END CHANGE
/// Adds a failed API request to the local database queue.
Future<void> addApiToQueue({
required String endpoint,
required String method, // e.g., 'POST' or 'POST_MULTIPART'
Map<String, dynamic>? body,
Map<String, String>? fields,
Map<String, File>? files,
}) async {
// We must convert File objects to their string paths before saving to JSON.
final serializableFiles = files?.map((key, value) => MapEntry(key, value.path));
final payload = {
'method': method,
'body': body,
'fields': fields,
'files': serializableFiles,
};
await _dbHelper.queueFailedRequest({
'type': 'api',
'endpoint_or_path': endpoint,
'payload': jsonEncode(payload),
'timestamp': DateTime.now().toIso8601String(),
'status': 'pending',
});
debugPrint("API request for endpoint '$endpoint' has been queued for retry.");
}
/// Adds a failed FTP upload to the local database queue.
Future<void> addFtpToQueue({
required String localFilePath,
required String remotePath,
}) async {
final payload = {'localFilePath': localFilePath};
await _dbHelper.queueFailedRequest({
'type': 'ftp',
'endpoint_or_path': remotePath,
'payload': jsonEncode(payload),
'timestamp': DateTime.now().toIso8601String(),
'status': 'pending',
});
debugPrint("FTP upload for file '$localFilePath' has been queued for retry.");
}
/// Retrieves all tasks currently in the 'pending' state from the queue.
Future<List<Map<String, dynamic>>> getPendingTasks() {
return _dbHelper.getPendingRequests();
}
/// Attempts to re-execute a single failed task from the queue.
/// Returns `true` on success, `false` on failure.
Future<bool> retryTask(int taskId) async {
final task = await _dbHelper.getRequestById(taskId);
if (task == null) {
debugPrint("Retry failed: Task with ID $taskId not found in the queue.");
return false;
}
bool success = false;
final payload = jsonDecode(task['payload'] as String);
try {
if (task['type'] == 'api') {
final endpoint = task['endpoint_or_path'] as String;
final method = payload['method'] as String;
// START CHANGE: Fetch the current active base URL to perform the retry
final baseUrl = await _serverConfigService.getActiveApiUrl();
debugPrint("Retrying API task $taskId: $method to $baseUrl/$endpoint");
Map<String, dynamic> result;
if (method == 'POST_MULTIPART') {
final Map<String, String> fields = Map<String, String>.from(payload['fields'] ?? {});
final Map<String, File> files = (payload['files'] as Map<String, dynamic>?)
?.map((key, value) => MapEntry(key, File(value as String))) ?? {};
result = await _baseApiService.postMultipart(baseUrl: baseUrl, endpoint: endpoint, fields: fields, files: files);
} else { // Assume 'POST'
final Map<String, dynamic> body = Map<String, dynamic>.from(payload['body'] ?? {});
result = await _baseApiService.post(baseUrl, endpoint, body);
}
// END CHANGE
success = result['success'];
} else if (task['type'] == 'ftp') {
final remotePath = task['endpoint_or_path'] as String;
final localFile = File(payload['localFilePath'] as String);
debugPrint("Retrying FTP task $taskId: Uploading ${localFile.path} to $remotePath");
if (await localFile.exists()) {
// START CHANGE: On retry, attempt to upload to ALL available FTP servers.
final ftpConfigs = await _dbHelper.loadFtpConfigs() ?? [];
if (ftpConfigs.isEmpty) {
debugPrint("Retry failed for FTP task $taskId: No FTP configurations found.");
return false;
}
for (final config in ftpConfigs) {
final result = await _ftpService.uploadFile(
config: config,
fileToUpload: localFile,
remotePath: remotePath
);
if (result['success']) {
success = true;
break; // Stop on the first successful upload
}
}
// END CHANGE
} else {
debugPrint("Retry failed for FTP task $taskId: Source file no longer exists at ${localFile.path}");
success = false;
}
}
} catch (e) {
debugPrint("A critical error occurred while retrying task $taskId: $e");
success = false;
}
if (success) {
debugPrint("Task $taskId completed successfully. Removing from queue.");
await _dbHelper.deleteRequestFromQueue(taskId);
} else {
debugPrint("Retry attempt for task $taskId failed. It will remain in the queue.");
}
return success;
}
}