diff --git a/lib/Command/SyncMissingUsersToCommon.php b/lib/Command/SyncMissingUsersToCommon.php
index 2ceba7209c19a959cfbf614d148777fb53bbeb09..c4828dba780854654784081ea4bbd5411e0ab928 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,10 +112,17 @@ 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) {
- $this->processSingleUser($username, $ipAddress, $isDryRun, $output, $stats);
+ try {
+ $this->processSingleUser($username, $ipAddress, $isDryRun, $output, $stats);
+ } catch (Exception $e) {
+ $errorMessage = sprintf('Unexpected error processing user %s: %s', $username, $e->getMessage());
+ $output->writeln(sprintf('%s', $errorMessage));
+ $stats['errors']++;
+ $stats['failed_reasons'][] = $errorMessage;
+ }
}
}
@@ -118,52 +130,92 @@ 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);
+
+ // 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) {
+ $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'];
}
/**
* 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) {
+ $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
- }
+ // Strip legacy domain from username before checking in common DB
+ $usernameWithoutDomain = $this->userService->stripLegacyDomainFromUsername($username);
+ if ($this->userService->isUsernameTaken($usernameWithoutDomain)) {
+ // This is not an error, just a skip - user already exists
+ return;
+ }
- try {
$this->syncUserToCommon($username, $ipAddress, $isDryRun, $output);
$stats['success']++;
} catch (Exception $e) {
- $output->writeln(sprintf('Error syncing 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;
}
- $stats['processed']++;
}
/**
* 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.', $remaining));
+ }
+ }
+
+ $output->writeln('');
+ $output->writeln(sprintf('Summary: %d/%d users processed successfully',
+ $stats['success'], $stats['total']));
}
/**
@@ -176,48 +228,64 @@ 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));
-
- // Check if user exists and is on LDAP backend
- $user = $this->userManager->get($username);
- if (!$user) {
- throw new Exception("User not found in NextCloud");
+ // Only show processing message in dry run mode for performance
+ if ($isDryRun) {
+ $output->writeln(sprintf('Processing user: %s', $username));
}
- if (!$this->ldapConnectionService->isUserOnLDAPBackend($user)) {
- throw new Exception("User is not on LDAP backend");
- }
+ try {
+ // Get user from userManager (already verified to exist in processSingleUser)
+ $user = $this->userManager->get($username);
+
+ // Check if user is on LDAP backend - skip if not
+ if (!$this->ldapConnectionService->isUserOnLDAPBackend($user)) {
+ // Skip users that are not on LDAP backend
+ if ($isDryRun) {
+ $output->writeln(sprintf(' Skipping user %s: not on LDAP backend', $username));
+ }
+ return; // Skip this user gracefully
+ }
- // 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));
+ // 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
- $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));
+ if (!$isDryRun) {
+ // Add user to common database
+ $this->userService->addUsernameToCommonDataStore(
+ $usernameWithoutDomain,
+ $ipAddress,
+ $userMetadata['recoveryMailAddress'],
+ $createdAt
+ );
+ // 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));
+ }
+ } 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);
}
}
}