From 5db64c7cc6d1b32ca7592754b0384d4dacd8592c Mon Sep 17 00:00:00 2001 From: theronakpatel Date: Wed, 23 Jul 2025 12:49:26 +0530 Subject: [PATCH 1/8] Try-catch added to handle erros --- lib/Command/SyncMissingUsersToCommon.php | 124 +++++++++++++---------- 1 file changed, 69 insertions(+), 55 deletions(-) diff --git a/lib/Command/SyncMissingUsersToCommon.php b/lib/Command/SyncMissingUsersToCommon.php index 2ceba720..bc4357a9 100644 --- a/lib/Command/SyncMissingUsersToCommon.php +++ b/lib/Command/SyncMissingUsersToCommon.php @@ -110,7 +110,12 @@ class SyncMissingUsersToCommon extends Command { $output->writeln(sprintf('Processing %d specific users', count($usernames))); foreach ($usernames as $username) { - $this->processSingleUser($username, $ipAddress, $isDryRun, $output, $stats); + try { + $this->processSingleUser($username, $ipAddress, $isDryRun, $output, $stats); + } catch (Exception $e) { + $output->writeln(sprintf('Unexpected error processing user %s: %s', $username, $e->getMessage())); + $stats['errors']++; + } } } @@ -118,15 +123,18 @@ class SyncMissingUsersToCommon extends Command { * Process all users from NextCloud */ private function processAllUsers(string $ipAddress, bool $isDryRun, OutputInterface $output, array &$stats): void { - $userCount = 0; $output->writeln('Processing all users from NextCloud'); - $this->userManager->callForAllUsers(function (IUser $user) use (&$userCount, $ipAddress, $isDryRun, $output, &$stats) { - $this->processSingleUser($user->getUID(), $ipAddress, $isDryRun, $output, $stats); - $userCount++; - if ($userCount > 0 && $userCount % 100 === 0) { - $output->writeln(sprintf('Progress: %d processed, %d success, %d errors', - $userCount, $stats['success'], $stats['errors'])); + $this->userManager->callForAllUsers(function (IUser $user) use ($ipAddress, $isDryRun, $output, &$stats) { + try { + $this->processSingleUser($user->getUID(), $ipAddress, $isDryRun, $output, $stats); + if ($stats['processed'] > 0 && $stats['processed'] % 100 === 0) { + $output->writeln(sprintf('Progress: %d processed, %d success, %d errors', + $stats['processed'], $stats['success'], $stats['errors'])); + } + } catch (Exception $e) { + $output->writeln(sprintf('Unexpected error processing user %s: %s', $user->getUID(), $e->getMessage())); + $stats['errors']++; } }); } @@ -135,27 +143,28 @@ class SyncMissingUsersToCommon extends Command { * Process a single user */ private function processSingleUser(string $username, string $ipAddress, bool $isDryRun, OutputInterface $output, array &$stats): void { - $user = $this->userManager->get($username); - if (!$user) { - $output->writeln(sprintf('User not found: %s', $username)); - $stats['errors']++; - return; - } + $stats['processed']++; // Increment processed count first for all users + + try { + $user = $this->userManager->get($username); + if (!$user) { + $output->writeln(sprintf('User not found: %s', $username)); + $stats['errors']++; + return; + } - // Strip legacy domain from username before checking in common DB - $usernameWithoutDomain = $this->userService->stripLegacyDomainFromUsername($username); - if ($this->userService->isUsernameTaken($usernameWithoutDomain)) { - return; // Skip if already exists - } + // Strip legacy domain from username before checking in common DB + $usernameWithoutDomain = $this->userService->stripLegacyDomainFromUsername($username); + if ($this->userService->isUsernameTaken($usernameWithoutDomain)) { + return; // Skip if already exists (processed count already incremented) + } - try { $this->syncUserToCommon($username, $ipAddress, $isDryRun, $output); $stats['success']++; } catch (Exception $e) { - $output->writeln(sprintf('Error syncing user %s: %s', $username, $e->getMessage())); + $output->writeln(sprintf('Error processing user %s: %s', $username, $e->getMessage())); $stats['errors']++; } - $stats['processed']++; } /** @@ -178,46 +187,51 @@ class SyncMissingUsersToCommon extends Command { private function syncUserToCommon(string $username, string $ipAddress, bool $isDryRun, OutputInterface $output): void { $output->writeln(sprintf('Processing user: %s', $username)); - // Check if user exists and is on LDAP backend - $user = $this->userManager->get($username); - if (!$user) { - throw new Exception("User not found in NextCloud"); - } + try { + // Check if user exists and is on LDAP backend + $user = $this->userManager->get($username); + if (!$user) { + throw new Exception("User not found in NextCloud"); + } - if (!$this->ldapConnectionService->isUserOnLDAPBackend($user)) { - throw new Exception("User is not on LDAP backend"); - } + if (!$this->ldapConnectionService->isUserOnLDAPBackend($user)) { + throw new Exception("User is not on LDAP backend"); + } - // Get user metadata from LDAP - $userMetadata = $this->ldapConnectionService->getUserMetadata($username); + // Get user metadata from LDAP + $userMetadata = $this->ldapConnectionService->getUserMetadata($username); - // Use usernameWithoutDomain from LDAP metadata - $usernameWithoutDomain = $userMetadata['usernameWithoutDomain']; + // Use usernameWithoutDomain from LDAP metadata + $usernameWithoutDomain = $userMetadata['usernameWithoutDomain']; - // Convert LDAP createTimestamp to MySQL datetime format - $ldapTimestamp = $userMetadata['createTimestamp'] ?? null; - $createdAt = $ldapTimestamp; + // Convert LDAP createTimestamp to MySQL datetime format + $ldapTimestamp = $userMetadata['createTimestamp'] ?? null; + $createdAt = $ldapTimestamp; - if ($ldapTimestamp) { - $dateTime = \DateTime::createFromFormat('YmdHis\Z', $ldapTimestamp, new \DateTimeZone('UTC')); - if ($dateTime !== false) { - $createdAt = $dateTime->format('Y-m-d H:i:s'); // Format compatible with MySQL + if ($ldapTimestamp) { + $dateTime = \DateTime::createFromFormat('YmdHis\Z', $ldapTimestamp, new \DateTimeZone('UTC')); + if ($dateTime !== false) { + $createdAt = $dateTime->format('Y-m-d H:i:s'); // Format compatible with MySQL + } } - } - $output->writeln(sprintf(' Found user in LDAP: %s (created: %s)', $usernameWithoutDomain, $createdAt)); - - if (!$isDryRun) { - // Add user to common database - $this->userService->addUsernameToCommonDataStore( - $usernameWithoutDomain, - $ipAddress, - $userMetadata['recoveryMailAddress'], - $createdAt - ); - $output->writeln(sprintf(' ✓ User synced successfully to common database: %s', $usernameWithoutDomain)); - } else { - $output->writeln(sprintf(' Would sync user to common database with recovery email: %s and created_at: %s', $userMetadata['recoveryMailAddress'], $createdAt)); + $output->writeln(sprintf(' Found user in LDAP: %s (created: %s)', $usernameWithoutDomain, $createdAt)); + + if (!$isDryRun) { + // Add user to common database + $this->userService->addUsernameToCommonDataStore( + $usernameWithoutDomain, + $ipAddress, + $userMetadata['recoveryMailAddress'], + $createdAt + ); + $output->writeln(sprintf(' ✓ User synced successfully to common database: %s', $usernameWithoutDomain)); + } else { + $output->writeln(sprintf(' Would sync user to common database with recovery email: %s and created_at: %s', $userMetadata['recoveryMailAddress'], $createdAt)); + } + } catch (Exception $e) { + // Re-throw the exception to be caught by the caller + throw new Exception(sprintf("Failed to sync user %s: %s", $username, $e->getMessage()), 0, $e); } } } -- GitLab From dcb38d7b8f592bf9e14e8dd4ca41b365440e6203 Mon Sep 17 00:00:00 2001 From: theronakpatel Date: Sat, 26 Jul 2025 08:49:57 +0530 Subject: [PATCH 2/8] removed comment --- lib/Command/SyncMissingUsersToCommon.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Command/SyncMissingUsersToCommon.php b/lib/Command/SyncMissingUsersToCommon.php index bc4357a9..7f8ab1b0 100644 --- a/lib/Command/SyncMissingUsersToCommon.php +++ b/lib/Command/SyncMissingUsersToCommon.php @@ -156,7 +156,7 @@ class SyncMissingUsersToCommon extends Command { // Strip legacy domain from username before checking in common DB $usernameWithoutDomain = $this->userService->stripLegacyDomainFromUsername($username); if ($this->userService->isUsernameTaken($usernameWithoutDomain)) { - return; // Skip if already exists (processed count already incremented) + return; // Skip if already exists } $this->syncUserToCommon($username, $ipAddress, $isDryRun, $output); -- GitLab From 1820c51ec6ea64c40e631ecc371efaf960e25ef5 Mon Sep 17 00:00:00 2001 From: theronakpatel Date: Sat, 26 Jul 2025 08:52:22 +0530 Subject: [PATCH 3/8] bumped version to 10.1.1 --- appinfo/info.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appinfo/info.xml b/appinfo/info.xml index e2145280..fede4817 100644 --- a/appinfo/info.xml +++ b/appinfo/info.xml @@ -10,7 +10,7 @@ - 10.1.0 + 10.1.1 agpl Murena SAS EcloudAccounts -- GitLab From 5d0de1cf31fd881a0a61cc71e90e89b68c7fd2ab Mon Sep 17 00:00:00 2001 From: theronakpatel Date: Sat, 26 Jul 2025 09:06:22 +0530 Subject: [PATCH 4/8] Optimize code for better logging --- lib/Command/SyncMissingUsersToCommon.php | 82 +++++++++++++++++++----- 1 file changed, 67 insertions(+), 15 deletions(-) diff --git a/lib/Command/SyncMissingUsersToCommon.php b/lib/Command/SyncMissingUsersToCommon.php index 7f8ab1b0..27f1959c 100644 --- a/lib/Command/SyncMissingUsersToCommon.php +++ b/lib/Command/SyncMissingUsersToCommon.php @@ -89,14 +89,19 @@ class SyncMissingUsersToCommon extends Command { */ private function processUsers(array $usernames, string $ipAddress, bool $isDryRun, OutputInterface $output): array { $stats = [ + 'total' => 0, 'processed' => 0, 'success' => 0, - 'errors' => 0 + 'errors' => 0, + 'failed_reasons' => [] ]; if (!empty($usernames)) { + $stats['total'] = count($usernames); $this->processSpecificUsers($usernames, $ipAddress, $isDryRun, $output, $stats); } else { + // Don't pre-count for all users - it's an expensive operation + $output->writeln('Processing all users from NextCloud (counting during processing)'); $this->processAllUsers($ipAddress, $isDryRun, $output, $stats); } @@ -107,14 +112,16 @@ class SyncMissingUsersToCommon extends Command { * Process a list of specific users */ private function processSpecificUsers(array $usernames, string $ipAddress, bool $isDryRun, OutputInterface $output, array &$stats): void { - $output->writeln(sprintf('Processing %d specific users', count($usernames))); + $output->writeln(sprintf('Processing %d specific users', $stats['total'])); foreach ($usernames as $username) { try { $this->processSingleUser($username, $ipAddress, $isDryRun, $output, $stats); } catch (Exception $e) { - $output->writeln(sprintf('Unexpected error processing user %s: %s', $username, $e->getMessage())); + $errorMessage = sprintf('Unexpected error processing user %s: %s', $username, $e->getMessage()); + $output->writeln(sprintf('%s', $errorMessage)); $stats['errors']++; + $stats['failed_reasons'][] = $errorMessage; } } } @@ -123,20 +130,25 @@ class SyncMissingUsersToCommon extends Command { * Process all users from NextCloud */ private function processAllUsers(string $ipAddress, bool $isDryRun, OutputInterface $output, array &$stats): void { - $output->writeln('Processing all users from NextCloud'); - $this->userManager->callForAllUsers(function (IUser $user) use ($ipAddress, $isDryRun, $output, &$stats) { try { $this->processSingleUser($user->getUID(), $ipAddress, $isDryRun, $output, $stats); - if ($stats['processed'] > 0 && $stats['processed'] % 100 === 0) { + + // Reduce output frequency for better performance + if ($stats['processed'] > 0 && $stats['processed'] % 1000 === 0) { $output->writeln(sprintf('Progress: %d processed, %d success, %d errors', $stats['processed'], $stats['success'], $stats['errors'])); } } catch (Exception $e) { - $output->writeln(sprintf('Unexpected error processing user %s: %s', $user->getUID(), $e->getMessage())); + $errorMessage = sprintf('Unexpected error processing user %s: %s', $user->getUID(), $e->getMessage()); + $output->writeln(sprintf('%s', $errorMessage)); $stats['errors']++; + $stats['failed_reasons'][] = $errorMessage; } }); + + // Set total after processing for all users + $stats['total'] = $stats['processed']; } /** @@ -148,22 +160,27 @@ class SyncMissingUsersToCommon extends Command { try { $user = $this->userManager->get($username); if (!$user) { - $output->writeln(sprintf('User not found: %s', $username)); + $errorMessage = sprintf('User not found: %s', $username); + $output->writeln(sprintf('%s', $errorMessage)); $stats['errors']++; + $stats['failed_reasons'][] = $errorMessage; return; } // Strip legacy domain from username before checking in common DB $usernameWithoutDomain = $this->userService->stripLegacyDomainFromUsername($username); if ($this->userService->isUsernameTaken($usernameWithoutDomain)) { - return; // Skip if already exists + // This is not an error, just a skip - user already exists + return; } $this->syncUserToCommon($username, $ipAddress, $isDryRun, $output); $stats['success']++; } catch (Exception $e) { - $output->writeln(sprintf('Error processing user %s: %s', $username, $e->getMessage())); + $errorMessage = sprintf('Error processing user %s: %s', $username, $e->getMessage()); + $output->writeln(sprintf('%s', $errorMessage)); $stats['errors']++; + $stats['failed_reasons'][] = $errorMessage; } } @@ -171,8 +188,34 @@ class SyncMissingUsersToCommon extends Command { * Display final results */ private function displayFinalResults(array $stats, OutputInterface $output): void { - $output->writeln(sprintf('Final result: %d users processed, %d success, %d errors.', - $stats['processed'], $stats['success'], $stats['errors'])); + $output->writeln(''); + $output->writeln('=== FINAL RESULTS ==='); + $output->writeln(sprintf('Total users to process: %d', $stats['total'])); + $output->writeln(sprintf('Users processed: %d', $stats['processed'])); + $output->writeln(sprintf('Successfully synced: %d', $stats['success'])); + $output->writeln(sprintf('Errors encountered: %d', $stats['errors'])); + + if (!empty($stats['failed_reasons'])) { + $output->writeln(''); + $output->writeln('=== FAILURE REASONS ==='); + + // Limit the number of reasons shown for performance + $maxReasonsToShow = 50; + $reasonsToShow = array_slice($stats['failed_reasons'], 0, $maxReasonsToShow); + + foreach ($reasonsToShow as $reason) { + $output->writeln(sprintf('• %s', $reason)); + } + + if (count($stats['failed_reasons']) > $maxReasonsToShow) { + $remaining = count($stats['failed_reasons']) - $maxReasonsToShow; + $output->writeln(sprintf('... and %d more errors (truncated for performance)', $remaining)); + } + } + + $output->writeln(''); + $output->writeln(sprintf('Summary: %d/%d users processed successfully', + $stats['success'], $stats['total'])); } /** @@ -185,7 +228,10 @@ class SyncMissingUsersToCommon extends Command { * @return void */ private function syncUserToCommon(string $username, string $ipAddress, bool $isDryRun, OutputInterface $output): void { - $output->writeln(sprintf('Processing user: %s', $username)); + // Only show processing message in dry run mode for performance + if ($isDryRun) { + $output->writeln(sprintf('Processing user: %s', $username)); + } try { // Check if user exists and is on LDAP backend @@ -215,7 +261,10 @@ class SyncMissingUsersToCommon extends Command { } } - $output->writeln(sprintf(' Found user in LDAP: %s (created: %s)', $usernameWithoutDomain, $createdAt)); + // Only show detailed output in dry run mode + if ($isDryRun) { + $output->writeln(sprintf(' Found user in LDAP: %s (created: %s)', $usernameWithoutDomain, $createdAt)); + } if (!$isDryRun) { // Add user to common database @@ -225,7 +274,10 @@ class SyncMissingUsersToCommon extends Command { $userMetadata['recoveryMailAddress'], $createdAt ); - $output->writeln(sprintf(' ✓ User synced successfully to common database: %s', $usernameWithoutDomain)); + // Only show success message in dry run mode for performance + if ($isDryRun) { + $output->writeln(sprintf(' ✓ User synced successfully to common database: %s', $usernameWithoutDomain)); + } } else { $output->writeln(sprintf(' Would sync user to common database with recovery email: %s and created_at: %s', $userMetadata['recoveryMailAddress'], $createdAt)); } -- GitLab From 2d03ea37207b66e0bf6c16fd7978882f48cd5498 Mon Sep 17 00:00:00 2001 From: theronakpatel Date: Sat, 26 Jul 2025 09:07:49 +0530 Subject: [PATCH 5/8] error handling added --- lib/Command/SyncMissingUsersToCommon.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Command/SyncMissingUsersToCommon.php b/lib/Command/SyncMissingUsersToCommon.php index 27f1959c..0743e24f 100644 --- a/lib/Command/SyncMissingUsersToCommon.php +++ b/lib/Command/SyncMissingUsersToCommon.php @@ -209,7 +209,7 @@ class SyncMissingUsersToCommon extends Command { if (count($stats['failed_reasons']) > $maxReasonsToShow) { $remaining = count($stats['failed_reasons']) - $maxReasonsToShow; - $output->writeln(sprintf('... and %d more errors (truncated for performance)', $remaining)); + $output->writeln(sprintf('... and %d more errors.', $remaining)); } } -- GitLab From a288fee99b4b8d4266426a88f9a112de886db7a4 Mon Sep 17 00:00:00 2001 From: theronakpatel Date: Wed, 30 Jul 2025 17:28:45 +0530 Subject: [PATCH 6/8] added check if user is at ldap backend or not --- lib/Command/SyncMissingUsersToCommon.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/Command/SyncMissingUsersToCommon.php b/lib/Command/SyncMissingUsersToCommon.php index 0743e24f..51363c56 100644 --- a/lib/Command/SyncMissingUsersToCommon.php +++ b/lib/Command/SyncMissingUsersToCommon.php @@ -240,8 +240,13 @@ class SyncMissingUsersToCommon extends Command { throw new Exception("User not found in NextCloud"); } + // Check if user is on LDAP backend - skip if not if (!$this->ldapConnectionService->isUserOnLDAPBackend($user)) { - throw new Exception("User is not on LDAP backend"); + // Skip users that are not on LDAP backend (like ncadmin) + if ($isDryRun) { + $output->writeln(sprintf(' Skipping user %s: not on LDAP backend', $username)); + } + return; // Skip this user gracefully } // Get user metadata from LDAP -- GitLab From 805ab83700f268285fdbb564ec76965446ca6b38 Mon Sep 17 00:00:00 2001 From: theronakpatel Date: Wed, 30 Jul 2025 17:30:04 +0530 Subject: [PATCH 7/8] comment changes --- lib/Command/SyncMissingUsersToCommon.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Command/SyncMissingUsersToCommon.php b/lib/Command/SyncMissingUsersToCommon.php index 51363c56..cf4ddd27 100644 --- a/lib/Command/SyncMissingUsersToCommon.php +++ b/lib/Command/SyncMissingUsersToCommon.php @@ -242,7 +242,7 @@ class SyncMissingUsersToCommon extends Command { // Check if user is on LDAP backend - skip if not if (!$this->ldapConnectionService->isUserOnLDAPBackend($user)) { - // Skip users that are not on LDAP backend (like ncadmin) + // Skip users that are not on LDAP backend if ($isDryRun) { $output->writeln(sprintf(' Skipping user %s: not on LDAP backend', $username)); } -- GitLab From cb0f911a8e3f31d46e88c162501bfdb5d42f4d95 Mon Sep 17 00:00:00 2001 From: theronakpatel Date: Mon, 4 Aug 2025 23:57:24 +0530 Subject: [PATCH 8/8] redundant removed --- lib/Command/SyncMissingUsersToCommon.php | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/lib/Command/SyncMissingUsersToCommon.php b/lib/Command/SyncMissingUsersToCommon.php index cf4ddd27..c4828dba 100644 --- a/lib/Command/SyncMissingUsersToCommon.php +++ b/lib/Command/SyncMissingUsersToCommon.php @@ -234,11 +234,8 @@ class SyncMissingUsersToCommon extends Command { } try { - // Check if user exists and is on LDAP backend + // Get user from userManager (already verified to exist in processSingleUser) $user = $this->userManager->get($username); - if (!$user) { - throw new Exception("User not found in NextCloud"); - } // Check if user is on LDAP backend - skip if not if (!$this->ldapConnectionService->isUserOnLDAPBackend($user)) { -- GitLab