source = new Source($pdo); $this->syncLog = new SyncLog($pdo); // Initialize sync services $this->syncServices = [ 'jellyfin' => new JellyfinSyncService($pdo), 'local' => new LocalSyncService($pdo), 'samba' => new SambaSyncService($pdo), 'nfs' => new NfsSyncService($pdo) ]; } // Show sync dashboard public function index(Request $request, Response $response, array $args): Response { // Get recent sync logs $recentLogs = $this->syncLog->orderBy('created_at', 'desc')->limit(10)->get(); // Get sync statistics $stats = [ 'total_syncs' => $this->syncLog->count(), 'successful_syncs' => $this->syncLog->where('status', 'completed')->count(), 'failed_syncs' => $this->syncLog->where('status', 'failed')->count(), 'pending_syncs' => $this->syncLog->where('status', 'pending')->count(), ]; return $this->render($response, 'admin/sync/index.twig', [ 'recent_logs' => $recentLogs, 'stats' => $stats, 'current_route' => 'sync' ]); } // Start a new sync public function start(Request $request, Response $response, array $args): Response { $data = $request->getParsedBody(); $type = $data['type'] ?? 'full'; // full, scan, update $sourceId = $data['source_id'] ?? null; try { // Create a new sync log $logData = [ 'source_id' => $sourceId, 'sync_type' => $type, 'status' => 'pending', 'started_at' => date('Y-m-d H:i:s'), 'created_at' => date('Y-m-d H:i:s') ]; $logId = $this->syncLog->create($logData); // Start sync in background $this->startBackgroundSync($sourceId, $logId, $type); return $this->json($response, [ 'success' => true, 'message' => 'Sync started', 'log_id' => $logId ]); } catch (\Exception $e) { return $this->json($response, [ 'success' => false, 'message' => 'Error starting sync: ' . $e->getMessage() ], 500); } } // Get sync status public function status(Request $request, Response $response, array $args): Response { $logId = $args['log_id'] ?? null; if (!$logId) { return $this->json($response, [ 'success' => false, 'message' => 'Log ID is required' ], 400); } $log = $this->syncLog->find($logId); if (!$log) { return $this->json($response, [ 'success' => false, 'message' => 'Sync log not found' ], 404); } // Get detailed status from the sync service if available $status = [ 'status' => $log['status'], 'progress' => (int)($log['progress'] ?? 0), 'message' => $log['message'] ?? '', 'started_at' => $log['started_at'], 'completed_at' => $log['completed_at'] ?? null, 'total_items' => (int)($log['total_items'] ?? 0), 'processed_items' => (int)($log['processed_items'] ?? 0), 'new_items' => (int)($log['new_items'] ?? 0), 'updated_items' => (int)($log['updated_items'] ?? 0), 'deleted_items' => (int)($log['deleted_items'] ?? 0) ]; return $this->json($response, [ 'success' => true, 'log' => $status ]); } // Cancel a running sync public function cancel(Request $request, Response $response, array $args): Response { $logId = $args['log_id'] ?? null; if (!$logId) { return $this->json($response, [ 'success' => false, 'message' => 'Log ID is required' ], 400); } try { // Update the log status to cancelled $this->syncLog->update($logId, [ 'status' => 'cancelled', 'completed_at' => date('Y-m-d H:i:s'), 'message' => 'Sync cancelled by user' ]); // TODO: Send a signal to the running process to cancel return $this->json($response, [ 'success' => true, 'message' => 'Sync cancelled' ]); } catch (\Exception $e) { return $this->json($response, [ 'success' => false, 'message' => 'Error cancelling sync: ' . $e->getMessage() ], 500); } } // Clear sync logs public function clearLogs(Request $request, Response $response, array $args): Response { $type = $request->getParsedBody()['type'] ?? 'completed'; // completed, all try { if ($type === 'all') { $this->syncLog->query('TRUNCATE TABLE sync_logs'); } else { $this->syncLog->where('status', 'completed')->delete(); $this->syncLog->where('status', 'failed')->delete(); $this->syncLog->where('status', 'cancelled')->delete(); } return $this->json($response, [ 'success' => true, 'message' => 'Logs cleared successfully' ]); } catch (\Exception $e) { return $this->json($response, [ 'success' => false, 'message' => 'Error clearing logs: ' . $e->getMessage() ], 500); } } // Start background sync process private function startBackgroundSync($sourceId, $logId, $type = 'full') { // This is a simplified example - you'll need to implement this based on your needs $command = sprintf( 'php %s/console.php sync:start --log=%d --type=%s %s > /dev/null 2>&1 &', dirname(__DIR__, 3), // Path to your project root $logId, escapeshellarg($type), $sourceId ? '--source=' . $sourceId : '' ); exec($command); } // Process sync (called from CLI) public function processSync($sourceId, $logId, $type = 'full') { try { $log = $this->syncLog->find($logId); if (!$log) { throw new \Exception('Sync log not found'); } // Update log status to started $this->syncLog->update($logId, [ 'status' => 'in_progress', 'started_at' => date('Y-m-d H:i:s'), 'message' => 'Sync started' ]); $source = null; if ($sourceId) { $source = $this->source->find($sourceId); if (!$source) { throw new \Exception('Source not found'); } } // Get the appropriate sync service $service = $this->getSyncService($source ? $source['type'] : 'local'); // Start sync $result = $service->sync($source, $type, function($progress, $message) use ($logId) { // Update progress callback $this->updateSyncProgress($logId, $progress, $message); }); // Update log with final status $this->syncLog->update($logId, [ 'status' => $result['success'] ? 'completed' : 'failed', 'completed_at' => date('Y-m-d H:i:s'), 'message' => $result['message'] ?? 'Sync completed', 'total_items' => $result['total_items'] ?? 0, 'processed_items' => $result['processed_items'] ?? 0, 'new_items' => $result['new_items'] ?? 0, 'updated_items' => $result['updated_items'] ?? 0, 'deleted_items' => $result['deleted_items'] ?? 0, 'errors' => !empty($result['errors']) ? json_encode($result['errors']) : null ]); return $result; } catch (\Exception $e) { // Update log with error if (isset($logId) && $this->syncLog) { $this->syncLog->update($logId, [ 'status' => 'failed', 'completed_at' => date('Y-m-d H:i:s'), 'message' => 'Error: ' . $e->getMessage() ]); } throw $e; } } // Update sync progress private function updateSyncProgress($logId, $progress, $message = '') { $this->syncLog->update($logId, [ 'progress' => $progress, 'message' => $message, 'updated_at' => date('Y-m-d H:i:s') ]); } // Get the appropriate sync service private function getSyncService($type) { $type = strtolower($type); if (!isset($this->syncServices[$type])) { throw new \Exception("Unsupported source type: $type"); } return $this->syncServices[$type]; } }