From c28ac317a59cfd6dc5c850ebc2fd3ef071e59a12 Mon Sep 17 00:00:00 2001 From: myrmidex Date: Sun, 8 Mar 2026 01:48:09 +0100 Subject: [PATCH] 61 - Refactor Livewire Onboarding to use Actions, fix double-encryption bug --- app/Livewire/Onboarding.php | 156 ++++++++++++------------------------ 1 file changed, 51 insertions(+), 105 deletions(-) diff --git a/app/Livewire/Onboarding.php b/app/Livewire/Onboarding.php index dc5eda3..f21e062 100644 --- a/app/Livewire/Onboarding.php +++ b/app/Livewire/Onboarding.php @@ -2,6 +2,11 @@ namespace App\Livewire; +use App\Actions\CreateChannelAction; +use App\Actions\CreateFeedAction; +use App\Actions\CreatePlatformAccountAction; +use App\Actions\CreateRouteAction; +use App\Exceptions\PlatformAuthException; use App\Jobs\ArticleDiscoveryJob; use App\Jobs\SyncChannelPostsJob; use App\Models\Feed; @@ -11,10 +16,11 @@ use App\Models\PlatformInstance; use App\Models\Route; use App\Models\Setting; -use App\Services\Auth\LemmyAuthService; use App\Services\OnboardingService; -use Illuminate\Support\Facades\Crypt; +use Exception; +use InvalidArgumentException; use Livewire\Component; +use RuntimeException; class Onboarding extends Component { @@ -49,11 +55,21 @@ class Onboarding extends Component public bool $isLoading = false; private ?int $previousChannelLanguageId = null; - protected LemmyAuthService $lemmyAuthService; + protected CreatePlatformAccountAction $createPlatformAccountAction; + protected CreateFeedAction $createFeedAction; + protected CreateChannelAction $createChannelAction; + protected CreateRouteAction $createRouteAction; - public function boot(LemmyAuthService $lemmyAuthService): void - { - $this->lemmyAuthService = $lemmyAuthService; + public function boot( + CreatePlatformAccountAction $createPlatformAccountAction, + CreateFeedAction $createFeedAction, + CreateChannelAction $createChannelAction, + CreateRouteAction $createRouteAction, + ): void { + $this->createPlatformAccountAction = $createPlatformAccountAction; + $this->createFeedAction = $createFeedAction; + $this->createChannelAction = $createChannelAction; + $this->createRouteAction = $createRouteAction; } public function mount(): void @@ -146,42 +162,13 @@ public function createPlatformAccount(): void 'instanceUrl.regex' => 'Please enter a valid domain name (e.g., lemmy.world, belgae.social)', ]); - $fullInstanceUrl = 'https://' . $this->instanceUrl; - try { - // Authenticate with Lemmy API first (before creating any records) - $authResponse = $this->lemmyAuthService->authenticate( - $fullInstanceUrl, + $platformAccount = $this->createPlatformAccountAction->execute( + $this->instanceUrl, $this->username, - $this->password + $this->password, ); - // Only create platform instance after successful authentication - $platformInstance = PlatformInstance::firstOrCreate([ - 'url' => $fullInstanceUrl, - 'platform' => 'lemmy', - ], [ - 'name' => ucfirst($this->instanceUrl), - 'is_active' => true, - ]); - - // Create platform account - $platformAccount = PlatformAccount::create([ - 'platform' => 'lemmy', - 'instance_url' => $fullInstanceUrl, - 'username' => $this->username, - 'password' => Crypt::encryptString($this->password), - 'settings' => [ - 'display_name' => $authResponse['person_view']['person']['display_name'] ?? null, - 'description' => $authResponse['person_view']['person']['bio'] ?? null, - 'person_id' => $authResponse['person_view']['person']['id'] ?? null, - 'platform_instance_id' => $platformInstance->id, - 'api_token' => $authResponse['jwt'] ?? null, - ], - 'is_active' => true, - 'status' => 'active', - ]); - $this->existingAccount = [ 'id' => $platformAccount->id, 'username' => $platformAccount->username, @@ -189,7 +176,7 @@ public function createPlatformAccount(): void ]; $this->nextStep(); - } catch (\App\Exceptions\PlatformAuthException $e) { + } catch (PlatformAuthException $e) { $message = $e->getMessage(); if (str_contains($message, 'Rate limited by')) { $this->formErrors['general'] = $message; @@ -198,9 +185,9 @@ public function createPlatformAccount(): void } else { $this->formErrors['general'] = 'Invalid username or password. Please check your credentials and try again.'; } - } catch (\Exception $e) { + } catch (Exception $e) { logger()->error('Lemmy platform account creation failed', [ - 'instance_url' => $fullInstanceUrl, + 'instance_url' => 'https://' . $this->instanceUrl, 'username' => $this->username, 'error' => $e->getMessage(), 'class' => get_class($e), @@ -227,35 +214,17 @@ public function createFeed(): void ]); try { - // Get language short code - $language = Language::find($this->feedLanguageId); - $langCode = $language->short_code; - - // Look up URL from config - $url = config("feed.providers.{$this->feedProvider}.languages.{$langCode}.url"); - - if (!$url) { - $this->formErrors['general'] = 'Invalid provider and language combination.'; - $this->isLoading = false; - return; - } - - $providerConfig = config("feed.providers.{$this->feedProvider}"); - - Feed::firstOrCreate( - ['url' => $url], - [ - 'name' => $this->feedName, - 'type' => $providerConfig['type'] ?? 'website', - 'provider' => $this->feedProvider, - 'language_id' => $this->feedLanguageId, - 'description' => $this->feedDescription ?: null, - 'is_active' => true, - ] + $this->createFeedAction->execute( + $this->feedName, + $this->feedProvider, + $this->feedLanguageId, + $this->feedDescription ?: null, ); $this->nextStep(); - } catch (\Exception $e) { + } catch (InvalidArgumentException $e) { + $this->formErrors['general'] = 'Invalid provider and language combination.'; + } catch (Exception $e) { $this->formErrors['general'] = 'Failed to create feed. Please try again.'; } finally { $this->isLoading = false; @@ -285,42 +254,20 @@ public function createChannel(): void $this->previousChannelLanguageId = $this->channelLanguageId; try { - $platformInstance = PlatformInstance::findOrFail($this->platformInstanceId); - - // Check for active platform accounts - $activeAccounts = PlatformAccount::where('instance_url', $platformInstance->url) - ->where('is_active', true) - ->get(); - - if ($activeAccounts->isEmpty()) { - $this->formErrors['general'] = 'No active platform accounts found for this instance. Please create a platform account first.'; - $this->isLoading = false; - return; - } - - $channel = PlatformChannel::create([ - 'platform_instance_id' => $this->platformInstanceId, - 'channel_id' => $this->channelName, - 'name' => $this->channelName, - 'display_name' => ucfirst($this->channelName), - 'description' => $this->channelDescription ?: null, - 'language_id' => $this->channelLanguageId, - 'is_active' => true, - ]); - - // Attach first active account - $channel->platformAccounts()->attach($activeAccounts->first()->id, [ - 'is_active' => true, - 'priority' => 1, - 'created_at' => now(), - 'updated_at' => now(), - ]); + $channel = $this->createChannelAction->execute( + $this->channelName, + $this->platformInstanceId, + $this->channelLanguageId, + $this->channelDescription ?: null, + ); // Sync existing posts from this channel for duplicate detection SyncChannelPostsJob::dispatch($channel); $this->nextStep(); - } catch (\Exception $e) { + } catch (RuntimeException $e) { + $this->formErrors['general'] = $e->getMessage(); + } catch (Exception $e) { $this->formErrors['general'] = 'Failed to create channel. Please try again.'; } finally { $this->isLoading = false; @@ -339,18 +286,17 @@ public function createRoute(): void ]); try { - Route::create([ - 'feed_id' => $this->routeFeedId, - 'platform_channel_id' => $this->routeChannelId, - 'priority' => $this->routePriority, - 'is_active' => true, - ]); + $this->createRouteAction->execute( + $this->routeFeedId, + $this->routeChannelId, + $this->routePriority, + ); // Trigger article discovery ArticleDiscoveryJob::dispatch(); $this->nextStep(); - } catch (\Exception $e) { + } catch (Exception $e) { $this->formErrors['general'] = 'Failed to create route. Please try again.'; } finally { $this->isLoading = false;