Release v1.1.0 #79
1 changed files with 51 additions and 105 deletions
|
|
@ -2,6 +2,11 @@
|
||||||
|
|
||||||
namespace App\Livewire;
|
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\ArticleDiscoveryJob;
|
||||||
use App\Jobs\SyncChannelPostsJob;
|
use App\Jobs\SyncChannelPostsJob;
|
||||||
use App\Models\Feed;
|
use App\Models\Feed;
|
||||||
|
|
@ -11,10 +16,11 @@
|
||||||
use App\Models\PlatformInstance;
|
use App\Models\PlatformInstance;
|
||||||
use App\Models\Route;
|
use App\Models\Route;
|
||||||
use App\Models\Setting;
|
use App\Models\Setting;
|
||||||
use App\Services\Auth\LemmyAuthService;
|
|
||||||
use App\Services\OnboardingService;
|
use App\Services\OnboardingService;
|
||||||
use Illuminate\Support\Facades\Crypt;
|
use Exception;
|
||||||
|
use InvalidArgumentException;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
use RuntimeException;
|
||||||
|
|
||||||
class Onboarding extends Component
|
class Onboarding extends Component
|
||||||
{
|
{
|
||||||
|
|
@ -49,11 +55,21 @@ class Onboarding extends Component
|
||||||
public bool $isLoading = false;
|
public bool $isLoading = false;
|
||||||
private ?int $previousChannelLanguageId = null;
|
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
|
public function boot(
|
||||||
{
|
CreatePlatformAccountAction $createPlatformAccountAction,
|
||||||
$this->lemmyAuthService = $lemmyAuthService;
|
CreateFeedAction $createFeedAction,
|
||||||
|
CreateChannelAction $createChannelAction,
|
||||||
|
CreateRouteAction $createRouteAction,
|
||||||
|
): void {
|
||||||
|
$this->createPlatformAccountAction = $createPlatformAccountAction;
|
||||||
|
$this->createFeedAction = $createFeedAction;
|
||||||
|
$this->createChannelAction = $createChannelAction;
|
||||||
|
$this->createRouteAction = $createRouteAction;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function mount(): void
|
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)',
|
'instanceUrl.regex' => 'Please enter a valid domain name (e.g., lemmy.world, belgae.social)',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$fullInstanceUrl = 'https://' . $this->instanceUrl;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Authenticate with Lemmy API first (before creating any records)
|
$platformAccount = $this->createPlatformAccountAction->execute(
|
||||||
$authResponse = $this->lemmyAuthService->authenticate(
|
$this->instanceUrl,
|
||||||
$fullInstanceUrl,
|
|
||||||
$this->username,
|
$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 = [
|
$this->existingAccount = [
|
||||||
'id' => $platformAccount->id,
|
'id' => $platformAccount->id,
|
||||||
'username' => $platformAccount->username,
|
'username' => $platformAccount->username,
|
||||||
|
|
@ -189,7 +176,7 @@ public function createPlatformAccount(): void
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->nextStep();
|
$this->nextStep();
|
||||||
} catch (\App\Exceptions\PlatformAuthException $e) {
|
} catch (PlatformAuthException $e) {
|
||||||
$message = $e->getMessage();
|
$message = $e->getMessage();
|
||||||
if (str_contains($message, 'Rate limited by')) {
|
if (str_contains($message, 'Rate limited by')) {
|
||||||
$this->formErrors['general'] = $message;
|
$this->formErrors['general'] = $message;
|
||||||
|
|
@ -198,9 +185,9 @@ public function createPlatformAccount(): void
|
||||||
} else {
|
} else {
|
||||||
$this->formErrors['general'] = 'Invalid username or password. Please check your credentials and try again.';
|
$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', [
|
logger()->error('Lemmy platform account creation failed', [
|
||||||
'instance_url' => $fullInstanceUrl,
|
'instance_url' => 'https://' . $this->instanceUrl,
|
||||||
'username' => $this->username,
|
'username' => $this->username,
|
||||||
'error' => $e->getMessage(),
|
'error' => $e->getMessage(),
|
||||||
'class' => get_class($e),
|
'class' => get_class($e),
|
||||||
|
|
@ -227,35 +214,17 @@ public function createFeed(): void
|
||||||
]);
|
]);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Get language short code
|
$this->createFeedAction->execute(
|
||||||
$language = Language::find($this->feedLanguageId);
|
$this->feedName,
|
||||||
$langCode = $language->short_code;
|
$this->feedProvider,
|
||||||
|
$this->feedLanguageId,
|
||||||
// Look up URL from config
|
$this->feedDescription ?: null,
|
||||||
$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->nextStep();
|
$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.';
|
$this->formErrors['general'] = 'Failed to create feed. Please try again.';
|
||||||
} finally {
|
} finally {
|
||||||
$this->isLoading = false;
|
$this->isLoading = false;
|
||||||
|
|
@ -285,42 +254,20 @@ public function createChannel(): void
|
||||||
$this->previousChannelLanguageId = $this->channelLanguageId;
|
$this->previousChannelLanguageId = $this->channelLanguageId;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$platformInstance = PlatformInstance::findOrFail($this->platformInstanceId);
|
$channel = $this->createChannelAction->execute(
|
||||||
|
$this->channelName,
|
||||||
// Check for active platform accounts
|
$this->platformInstanceId,
|
||||||
$activeAccounts = PlatformAccount::where('instance_url', $platformInstance->url)
|
$this->channelLanguageId,
|
||||||
->where('is_active', true)
|
$this->channelDescription ?: null,
|
||||||
->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(),
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Sync existing posts from this channel for duplicate detection
|
// Sync existing posts from this channel for duplicate detection
|
||||||
SyncChannelPostsJob::dispatch($channel);
|
SyncChannelPostsJob::dispatch($channel);
|
||||||
|
|
||||||
$this->nextStep();
|
$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.';
|
$this->formErrors['general'] = 'Failed to create channel. Please try again.';
|
||||||
} finally {
|
} finally {
|
||||||
$this->isLoading = false;
|
$this->isLoading = false;
|
||||||
|
|
@ -339,18 +286,17 @@ public function createRoute(): void
|
||||||
]);
|
]);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Route::create([
|
$this->createRouteAction->execute(
|
||||||
'feed_id' => $this->routeFeedId,
|
$this->routeFeedId,
|
||||||
'platform_channel_id' => $this->routeChannelId,
|
$this->routeChannelId,
|
||||||
'priority' => $this->routePriority,
|
$this->routePriority,
|
||||||
'is_active' => true,
|
);
|
||||||
]);
|
|
||||||
|
|
||||||
// Trigger article discovery
|
// Trigger article discovery
|
||||||
ArticleDiscoveryJob::dispatch();
|
ArticleDiscoveryJob::dispatch();
|
||||||
|
|
||||||
$this->nextStep();
|
$this->nextStep();
|
||||||
} catch (\Exception $e) {
|
} catch (Exception $e) {
|
||||||
$this->formErrors['general'] = 'Failed to create route. Please try again.';
|
$this->formErrors['general'] = 'Failed to create route. Please try again.';
|
||||||
} finally {
|
} finally {
|
||||||
$this->isLoading = false;
|
$this->isLoading = false;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue