546 lines
24 KiB
Dart
546 lines
24 KiB
Dart
// lib/services/retry_service.dart
|
|
|
|
import 'dart:convert';
|
|
import 'dart:io';
|
|
import 'package:flutter/foundation.dart';
|
|
import 'package:path/path.dart' as p;
|
|
|
|
import 'package:environment_monitoring_app/models/in_situ_sampling_data.dart';
|
|
import 'package:environment_monitoring_app/services/marine_in_situ_sampling_service.dart';
|
|
import 'package:environment_monitoring_app/models/river_in_situ_sampling_data.dart';
|
|
import 'package:environment_monitoring_app/services/river_in_situ_sampling_service.dart';
|
|
// *** ADDED: Import River Investigative Model and Service ***
|
|
import 'package:environment_monitoring_app/models/river_inves_manual_sampling_data.dart';
|
|
import 'package:environment_monitoring_app/services/river_investigative_sampling_service.dart';
|
|
// *** END ADDED ***
|
|
import 'package:environment_monitoring_app/models/marine_inves_manual_sampling_data.dart';
|
|
import 'package:environment_monitoring_app/services/marine_investigative_sampling_service.dart';
|
|
import 'package:environment_monitoring_app/models/tarball_data.dart';
|
|
import 'package:environment_monitoring_app/services/marine_tarball_sampling_service.dart';
|
|
//import 'package:environment_monitoring_app/services/api_service.dart';
|
|
import 'package:environment_monitoring_app/services/database_helper.dart';
|
|
|
|
import 'package:environment_monitoring_app/services/base_api_service.dart';
|
|
import 'package:environment_monitoring_app/services/ftp_service.dart';
|
|
import 'package:environment_monitoring_app/services/server_config_service.dart';
|
|
import 'package:environment_monitoring_app/auth_provider.dart';
|
|
|
|
/// A dedicated service to manage the queue of failed API, FTP, and complex submission tasks.
|
|
class RetryService {
|
|
final DatabaseHelper _dbHelper = DatabaseHelper();
|
|
final BaseApiService _baseApiService = BaseApiService();
|
|
final FtpService _ftpService = FtpService();
|
|
final ServerConfigService _serverConfigService = ServerConfigService();
|
|
bool _isProcessing = false;
|
|
|
|
MarineInSituSamplingService? _marineInSituService;
|
|
RiverInSituSamplingService? _riverInSituService;
|
|
MarineInvestigativeSamplingService? _marineInvestigativeService;
|
|
MarineTarballSamplingService? _marineTarballService;
|
|
// *** ADDED: River Investigative Service member ***
|
|
RiverInvestigativeSamplingService? _riverInvestigativeService;
|
|
// *** END ADDED ***
|
|
AuthProvider? _authProvider;
|
|
|
|
// *** MODIFIED: Added riverInvestigativeService to initialize ***
|
|
void initialize({
|
|
required MarineInSituSamplingService marineInSituService,
|
|
required RiverInSituSamplingService riverInSituService,
|
|
required MarineInvestigativeSamplingService marineInvestigativeService,
|
|
required RiverInvestigativeSamplingService riverInvestigativeService, // <-- Added parameter
|
|
required MarineTarballSamplingService marineTarballService,
|
|
required AuthProvider authProvider,
|
|
}) {
|
|
_marineInSituService = marineInSituService;
|
|
_riverInSituService = riverInSituService;
|
|
_marineInvestigativeService = marineInvestigativeService;
|
|
_riverInvestigativeService = riverInvestigativeService; // <-- Assign parameter
|
|
_marineTarballService = marineTarballService;
|
|
_authProvider = authProvider;
|
|
}
|
|
// *** END MODIFIED ***
|
|
|
|
|
|
/// Adds a generic, complex task to the queue, to be handled by a background processor.
|
|
Future<void> queueTask({
|
|
required String type,
|
|
required Map<String, dynamic> payload,
|
|
}) async {
|
|
await _dbHelper.queueFailedRequest({
|
|
'type': type,
|
|
'endpoint_or_path': 'N/A', // Not applicable for complex tasks initially
|
|
'payload': jsonEncode(payload),
|
|
'timestamp': DateTime.now().toIso8601String(),
|
|
'status': 'pending',
|
|
});
|
|
debugPrint("Task of type '$type' has been queued for background processing.");
|
|
}
|
|
|
|
/// Adds a failed API request to the local database queue.
|
|
Future<void> addApiToQueue({
|
|
required String endpoint,
|
|
required String method,
|
|
Map<String, dynamic>? body,
|
|
Map<String, String>? fields,
|
|
Map<String, File>? files,
|
|
}) async {
|
|
// Convert File objects to paths for JSON serialization
|
|
final serializableFiles = files?.map((key, value) => MapEntry(key, value.path));
|
|
final payload = {
|
|
'method': method,
|
|
'body': body,
|
|
'fields': fields,
|
|
'files': serializableFiles, // Store paths instead of File objects
|
|
};
|
|
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,
|
|
required int ftpConfigId, // Added to specify which destination failed
|
|
}) async {
|
|
final payload = {
|
|
'localFilePath': localFilePath,
|
|
'ftpConfigId': ftpConfigId, // Store the specific config ID
|
|
};
|
|
await _dbHelper.queueFailedRequest({
|
|
'type': 'ftp',
|
|
'endpoint_or_path': remotePath,
|
|
'payload': jsonEncode(payload),
|
|
'timestamp': DateTime.now().toIso8601String(),
|
|
'status': 'pending',
|
|
});
|
|
debugPrint("FTP upload for file '$localFilePath' to config ID $ftpConfigId 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();
|
|
}
|
|
|
|
/// Processes the entire queue of pending tasks.
|
|
Future<void> processRetryQueue() async {
|
|
if (_isProcessing) {
|
|
debugPrint("[RetryService] ⏳ Queue is already being processed. Skipping.");
|
|
return;
|
|
}
|
|
_isProcessing = true;
|
|
debugPrint("[RetryService] ▶️ Starting to process main retry queue...");
|
|
|
|
final pendingTasks = await getPendingTasks();
|
|
if (pendingTasks.isEmpty) {
|
|
debugPrint("[RetryService] ⏹️ Queue is empty. Nothing to process.");
|
|
_isProcessing = false;
|
|
return;
|
|
}
|
|
|
|
// Check internet connection *before* processing
|
|
if (_authProvider == null || !await _authProvider!.isConnected()) {
|
|
debugPrint("[RetryService] ❌ No internet connection. Aborting queue processing.");
|
|
_isProcessing = false;
|
|
return;
|
|
}
|
|
|
|
debugPrint("[RetryService] 🔎 Found ${pendingTasks.length} pending tasks.");
|
|
// Process tasks one by one
|
|
for (final task in pendingTasks) {
|
|
// Add safety check in case a task is deleted mid-processing by another call
|
|
if (await _dbHelper.getRequestById(task['id'] as int) != null) {
|
|
await retryTask(task['id'] as int);
|
|
}
|
|
// Optional: Add a small delay between tasks if needed
|
|
// await Future.delayed(Duration(seconds: 1));
|
|
}
|
|
|
|
debugPrint("[RetryService] ⏹️ Finished processing retry queue.");
|
|
_isProcessing = false;
|
|
}
|
|
|
|
/// 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 (might have been processed already).");
|
|
return false; // Task doesn't exist or was processed elsewhere
|
|
}
|
|
|
|
bool success = false;
|
|
Map<String, dynamic> payload; // Declare outside try-catch
|
|
final String taskType = task['type'] as String; // Get type early for logging
|
|
|
|
try {
|
|
payload = jsonDecode(task['payload'] as String); // Decode payload inside try
|
|
} catch (e) {
|
|
debugPrint("Error decoding payload for task $taskId (Type: $taskType): $e. Removing invalid task.");
|
|
await _dbHelper.deleteRequestFromQueue(taskId);
|
|
return false; // Cannot process without valid payload
|
|
}
|
|
|
|
|
|
try {
|
|
// Ensure AuthProvider is initialized and we are online (checked in processRetryQueue)
|
|
if (_authProvider == null) {
|
|
debugPrint("RetryService has not been initialized. Cannot process task $taskId.");
|
|
return false;
|
|
}
|
|
|
|
// --- Complex Task Handlers ---
|
|
if (taskType == 'insitu_submission') {
|
|
debugPrint("Retrying complex task 'insitu_submission' with ID $taskId.");
|
|
if (_marineInSituService == null) {
|
|
debugPrint("Retry failed: MarineInSituSamplingService not initialized.");
|
|
return false;
|
|
}
|
|
|
|
final String logDirectoryPath = payload['localLogPath']; // Path to the directory
|
|
final jsonFilePath = p.join(logDirectoryPath, 'data.json');
|
|
final file = File(jsonFilePath);
|
|
|
|
if (!await file.exists()) {
|
|
debugPrint("Retry failed: Source log file no longer exists at $jsonFilePath");
|
|
await _dbHelper.deleteRequestFromQueue(taskId); // Remove invalid task
|
|
return false;
|
|
}
|
|
|
|
final content = await file.readAsString();
|
|
final jsonData = jsonDecode(content) as Map<String, dynamic>;
|
|
final InSituSamplingData dataToResubmit = InSituSamplingData.fromJson(jsonData);
|
|
|
|
// Re-run the original submission logic, passing the log directory
|
|
final result = await _marineInSituService!.submitInSituSample(
|
|
data: dataToResubmit,
|
|
appSettings: _authProvider!.appSettings, // Get current settings
|
|
authProvider: _authProvider!,
|
|
logDirectory: logDirectoryPath, // Pass directory to update log
|
|
);
|
|
success = result['success'];
|
|
|
|
} else if (taskType == 'river_insitu_submission') {
|
|
debugPrint("Retrying complex task 'river_insitu_submission' with ID $taskId.");
|
|
if (_riverInSituService == null) {
|
|
debugPrint("Retry failed: RiverInSituSamplingService not initialized.");
|
|
return false;
|
|
}
|
|
|
|
final String jsonFilePath = payload['localLogPath']; // Path to the JSON file
|
|
final file = File(jsonFilePath);
|
|
|
|
if (!await file.exists()) {
|
|
debugPrint("Retry failed: Source log file no longer exists at $jsonFilePath");
|
|
await _dbHelper.deleteRequestFromQueue(taskId);
|
|
return false;
|
|
}
|
|
final String logDirectoryPath = p.dirname(jsonFilePath); // Get directory from file path
|
|
|
|
|
|
final content = await file.readAsString();
|
|
final jsonData = jsonDecode(content) as Map<String, dynamic>;
|
|
final RiverInSituSamplingData dataToResubmit = RiverInSituSamplingData.fromJson(jsonData);
|
|
|
|
final result = await _riverInSituService!.submitData(
|
|
data: dataToResubmit,
|
|
appSettings: _authProvider!.appSettings,
|
|
authProvider: _authProvider!,
|
|
logDirectory: logDirectoryPath,
|
|
);
|
|
success = result['success'];
|
|
|
|
// *** ADDED: Handler for river_investigative_submission ***
|
|
} else if (taskType == 'river_investigative_submission') {
|
|
debugPrint("Retrying complex task 'river_investigative_submission' with ID $taskId.");
|
|
if (_riverInvestigativeService == null) {
|
|
debugPrint("Retry failed: RiverInvestigativeSamplingService not initialized.");
|
|
return false;
|
|
}
|
|
|
|
final String jsonFilePath = payload['localLogPath']; // Path to the JSON file
|
|
final file = File(jsonFilePath);
|
|
|
|
if (!await file.exists()) {
|
|
debugPrint("Retry failed: Source log file no longer exists at $jsonFilePath");
|
|
await _dbHelper.deleteRequestFromQueue(taskId);
|
|
return false;
|
|
}
|
|
final String logDirectoryPath = p.dirname(jsonFilePath); // Get directory from file path
|
|
|
|
final content = await file.readAsString();
|
|
final jsonData = jsonDecode(content) as Map<String, dynamic>;
|
|
// Use the correct Investigative data model
|
|
final RiverInvesManualSamplingData dataToResubmit = RiverInvesManualSamplingData.fromJson(jsonData);
|
|
|
|
// Call the submitData method from the Investigative service
|
|
final result = await _riverInvestigativeService!.submitData(
|
|
data: dataToResubmit,
|
|
appSettings: _authProvider!.appSettings,
|
|
authProvider: _authProvider!,
|
|
logDirectory: logDirectoryPath,
|
|
);
|
|
success = result['success'];
|
|
// *** END ADDED ***
|
|
|
|
} else if (taskType == 'investigative_submission') {
|
|
debugPrint("Retrying complex task 'investigative_submission' with ID $taskId.");
|
|
if (_marineInvestigativeService == null) {
|
|
debugPrint("Retry failed: MarineInvestigativeSamplingService not initialized.");
|
|
return false;
|
|
}
|
|
|
|
final String logDirectoryPath = payload['localLogPath']; // Path to the directory
|
|
final jsonFilePath = p.join(logDirectoryPath, 'data.json');
|
|
final file = File(jsonFilePath);
|
|
|
|
if (!await file.exists()) {
|
|
debugPrint("Retry failed: Source log file no longer exists at $jsonFilePath");
|
|
await _dbHelper.deleteRequestFromQueue(taskId);
|
|
return false;
|
|
}
|
|
|
|
final content = await file.readAsString();
|
|
final jsonData = jsonDecode(content) as Map<String, dynamic>;
|
|
final MarineInvesManualSamplingData dataToResubmit = MarineInvesManualSamplingData.fromJson(jsonData);
|
|
|
|
final result = await _marineInvestigativeService!.submitInvestigativeSample(
|
|
data: dataToResubmit,
|
|
appSettings: _authProvider!.appSettings,
|
|
authProvider: _authProvider!,
|
|
logDirectory: logDirectoryPath,
|
|
);
|
|
success = result['success'];
|
|
|
|
} else if (taskType == 'tarball_submission') {
|
|
debugPrint("Retrying complex task 'tarball_submission' with ID $taskId.");
|
|
if (_marineTarballService == null) {
|
|
debugPrint("Retry failed: MarineTarballSamplingService not initialized.");
|
|
return false;
|
|
}
|
|
|
|
final String logDirectoryPath = payload['localLogPath']; // Path to the directory
|
|
final jsonFilePath = p.join(logDirectoryPath, 'data.json');
|
|
final file = File(jsonFilePath);
|
|
|
|
if (!await file.exists()) {
|
|
debugPrint("Retry failed: Source log file no longer exists at $jsonFilePath");
|
|
await _dbHelper.deleteRequestFromQueue(taskId);
|
|
return false;
|
|
}
|
|
|
|
final content = await file.readAsString();
|
|
final jsonData = jsonDecode(content) as Map<String, dynamic>;
|
|
|
|
// Recreate File objects from paths
|
|
File? fileFromJson(dynamic path) => (path is String && path.isNotEmpty) ? File(path) : null;
|
|
|
|
final TarballSamplingData dataToResubmit = TarballSamplingData()
|
|
// Reconstruct the object from JSON data
|
|
..firstSampler = jsonData['firstSampler']
|
|
..firstSamplerUserId = jsonData['firstSamplerUserId']
|
|
..secondSampler = jsonData['secondSampler']
|
|
..samplingDate = jsonData['samplingDate']
|
|
..samplingTime = jsonData['samplingTime']
|
|
..selectedStateName = jsonData['selectedStateName']
|
|
..selectedCategoryName = jsonData['selectedCategoryName']
|
|
..selectedStation = jsonData['selectedStation']
|
|
..stationLatitude = jsonData['stationLatitude']
|
|
..stationLongitude = jsonData['stationLongitude']
|
|
..currentLatitude = jsonData['currentLatitude']
|
|
..currentLongitude = jsonData['currentLongitude']
|
|
..distanceDifference = jsonData['distanceDifference'] is num ? (jsonData['distanceDifference'] as num).toDouble() : null // Safe cast
|
|
..distanceDifferenceRemarks = jsonData['distanceDifferenceRemarks']
|
|
..classificationId = jsonData['classificationId'] is num ? (jsonData['classificationId'] as num).toInt() : null // Safe cast
|
|
..selectedClassification = jsonData['selectedClassification']
|
|
..leftCoastalViewImage = fileFromJson(jsonData['leftCoastalViewImage'])
|
|
..rightCoastalViewImage = fileFromJson(jsonData['rightCoastalViewImage'])
|
|
..verticalLinesImage = fileFromJson(jsonData['verticalLinesImage'])
|
|
..horizontalLineImage = fileFromJson(jsonData['horizontalLineImage'])
|
|
..optionalImage1 = fileFromJson(jsonData['optionalImage1'])
|
|
..optionalRemark1 = jsonData['optionalRemark1']
|
|
..optionalImage2 = fileFromJson(jsonData['optionalImage2'])
|
|
..optionalRemark2 = jsonData['optionalRemark2']
|
|
..optionalImage3 = fileFromJson(jsonData['optionalImage3'])
|
|
..optionalRemark3 = jsonData['optionalRemark3']
|
|
..optionalImage4 = fileFromJson(jsonData['optionalImage4'])
|
|
..optionalRemark4 = jsonData['optionalRemark4']
|
|
..reportId = jsonData['reportId'] // Preserve reportId if it exists
|
|
..submissionStatus = jsonData['submissionStatus'] // Preserve status info
|
|
..submissionMessage = jsonData['submissionMessage'];
|
|
|
|
|
|
debugPrint("Retrying Tarball submission...");
|
|
// Pass null for BuildContext, and the logDirectory path
|
|
final result = await _marineTarballService!.submitTarballSample(
|
|
data: dataToResubmit,
|
|
appSettings: _authProvider!.appSettings,
|
|
context: null, // Pass null for BuildContext during retry
|
|
logDirectory: logDirectoryPath, // Pass the directory path for potential update
|
|
);
|
|
success = result['success'];
|
|
|
|
// --- Simple Task Handlers ---
|
|
} else if (taskType == 'api') {
|
|
final endpoint = task['endpoint_or_path'] as String;
|
|
final method = payload['method'] as String;
|
|
final baseUrl = await _serverConfigService.getActiveApiUrl(); // Get current active URL
|
|
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'] ?? {});
|
|
// Recreate File objects from paths stored in the payload
|
|
final Map<String, File> files = (payload['files'] as Map<String, dynamic>?)
|
|
?.map((key, value) => MapEntry(key, File(value as String))) ?? {};
|
|
|
|
// Check if files still exist before attempting upload
|
|
bool allFilesExist = true;
|
|
List<String> missingFiles = []; // Keep track of missing files
|
|
for (var entry in files.entries) {
|
|
File file = entry.value;
|
|
if (!await file.exists()) {
|
|
debugPrint("Retry failed for API task $taskId: File ${file.path} (key: ${entry.key}) no longer exists.");
|
|
allFilesExist = false;
|
|
missingFiles.add(entry.key);
|
|
// break; // Stop checking further if one is missing
|
|
}
|
|
}
|
|
|
|
// If some files are missing, fail the entire task.
|
|
if (!allFilesExist) {
|
|
debugPrint("API Multipart retry failed for task $taskId because files are missing: ${missingFiles.join(', ')}. Removing task.");
|
|
await _dbHelper.deleteRequestFromQueue(taskId); // Remove invalid task
|
|
return false;
|
|
}
|
|
|
|
result = await _baseApiService.postMultipart(baseUrl: baseUrl, endpoint: endpoint, fields: fields, files: files);
|
|
} else { // Standard POST
|
|
final Map<String, dynamic> body = Map<String, dynamic>.from(payload['body'] ?? {});
|
|
result = await _baseApiService.post(baseUrl, endpoint, body);
|
|
}
|
|
success = result['success'];
|
|
|
|
} else if (taskType == 'ftp') {
|
|
final remotePath = task['endpoint_or_path'] as String;
|
|
final localFile = File(payload['localFilePath'] as String);
|
|
final int? ftpConfigId = payload['ftpConfigId'] as int?;
|
|
|
|
debugPrint("Retrying FTP task $taskId: Uploading ${localFile.path} to $remotePath using config ID $ftpConfigId");
|
|
|
|
if (ftpConfigId == null) {
|
|
debugPrint("Retry failed for FTP task $taskId: Missing FTP configuration ID in payload.");
|
|
await _dbHelper.deleteRequestFromQueue(taskId); // Remove invalid task
|
|
return false;
|
|
}
|
|
|
|
if (await localFile.exists()) {
|
|
final ftpConfigs = await _dbHelper.loadFtpConfigs() ?? [];
|
|
final config = ftpConfigs.firstWhere((c) => c['ftp_config_id'] == ftpConfigId, orElse: () => <String, dynamic>{}); // Use explicit type
|
|
|
|
|
|
if (config.isEmpty) {
|
|
debugPrint("Retry failed for FTP task $taskId: FTP configuration with ID $ftpConfigId not found.");
|
|
return false; // Fail the retry attempt, keep in queue
|
|
}
|
|
|
|
// Attempt upload using the specific config
|
|
final result = await _ftpService.uploadFile(config: config, fileToUpload: localFile, remotePath: remotePath);
|
|
success = result['success'];
|
|
|
|
} else {
|
|
debugPrint("Retry failed for FTP task $taskId: Source file no longer exists at ${localFile.path}");
|
|
await _dbHelper.deleteRequestFromQueue(taskId); // Remove task if file is gone
|
|
return false; // Explicitly return false as success is false
|
|
}
|
|
} else {
|
|
debugPrint("Unknown task type '$taskType' for task ID $taskId. Cannot retry. Removing task.");
|
|
await _dbHelper.deleteRequestFromQueue(taskId);
|
|
}
|
|
|
|
} on SessionExpiredException catch (e) {
|
|
debugPrint("Session expired during retry attempt for task $taskId (Type: $taskType): $e. Task remains in queue.");
|
|
success = false; // Session expiry during retry means failure for this attempt
|
|
} catch (e, stacktrace) { // Catch potential exceptions during processing
|
|
debugPrint("A critical error occurred while retrying task $taskId (Type: $taskType): $e");
|
|
debugPrint("Stacktrace: $stacktrace"); // Log stacktrace for detailed debugging
|
|
success = false; // Ensure success is false on exception
|
|
}
|
|
|
|
// Post-processing: Remove successful tasks from queue
|
|
if (success) {
|
|
debugPrint("Task $taskId (Type: $taskType) completed successfully. Removing from queue.");
|
|
await _dbHelper.deleteRequestFromQueue(taskId);
|
|
// If it was a complex task involving temporary ZIP files, attempt to delete them
|
|
if (taskType.endsWith('_submission') && payload['localLogPath'] != null) {
|
|
// Assume localLogPath points to the JSON file, get directory for cleanup
|
|
String pathToCheck = payload['localLogPath'];
|
|
// Check if it's a directory path already (for older marine insitu logs)
|
|
bool isDirectory = await Directory(pathToCheck).exists();
|
|
if (!isDirectory && pathToCheck.endsWith('.json')) {
|
|
pathToCheck = p.dirname(pathToCheck); // Get directory if it's a file path
|
|
isDirectory = true; // Now we are checking the directory
|
|
}
|
|
_cleanUpTemporaryZipFiles(pathToCheck, isDirectory: isDirectory);
|
|
}
|
|
// If it was an FTP task, attempt to delete the temporary ZIP file
|
|
if (taskType == 'ftp' && payload['localFilePath'] != null && (payload['localFilePath'] as String).endsWith('.zip')) {
|
|
_cleanUpTemporaryZipFiles(payload['localFilePath'], isDirectory: false);
|
|
}
|
|
|
|
} else {
|
|
debugPrint("Retry attempt for task $taskId (Type: $taskType) failed. It will remain in the queue.");
|
|
// Optional: Implement a retry limit here. If retries > X, mark task as 'failed' instead of 'pending'.
|
|
// e.g., await _dbHelper.updateTaskStatus(taskId, 'failed');
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
/// Helper function to delete temporary zip files after successful retry.
|
|
void _cleanUpTemporaryZipFiles(String path, {required bool isDirectory}) async {
|
|
try {
|
|
if (isDirectory) {
|
|
final dir = Directory(path);
|
|
if (await dir.exists()) {
|
|
final filesInDir = dir.listSync();
|
|
for (var entity in filesInDir) {
|
|
// Delete only ZIP files within the log directory
|
|
if (entity is File && entity.path.endsWith('.zip')) {
|
|
debugPrint("Deleting temporary zip file from directory: ${entity.path}");
|
|
await entity.delete();
|
|
}
|
|
}
|
|
// Optional: Delete the directory itself if now empty, ONLY if safe.
|
|
// Be cautious as data.json might still be needed or other files exist.
|
|
// if (await dir.listSync().isEmpty) {
|
|
// await dir.delete();
|
|
// debugPrint("Deleted empty log directory: ${dir.path}");
|
|
// }
|
|
} else {
|
|
debugPrint("Log directory not found for cleanup: $path");
|
|
}
|
|
} else {
|
|
// If it's a specific file path (like from FTP task)
|
|
final file = File(path);
|
|
if (await file.exists() && path.endsWith('.zip')) { // Ensure it's a zip file
|
|
debugPrint("Deleting temporary zip file: ${file.path}");
|
|
await file.delete();
|
|
} else if (!path.endsWith('.zip')) {
|
|
debugPrint("Skipping cleanup for non-zip file path: $path");
|
|
} else {
|
|
debugPrint("Temporary zip file not found for cleanup: $path");
|
|
}
|
|
}
|
|
} catch (e) {
|
|
debugPrint("Error cleaning up temporary zip file(s) for path $path: $e");
|
|
}
|
|
}
|
|
|
|
} // End of RetryService class |