2025-08-09 00:03:45 +02:00
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
namespace App\Http\Controllers\Api\V1;
|
|
|
|
|
|
|
|
|
|
use App\Http\Requests\StoreFeedRequest;
|
|
|
|
|
use App\Http\Resources\FeedResource;
|
|
|
|
|
use App\Http\Resources\PlatformAccountResource;
|
|
|
|
|
use App\Http\Resources\PlatformChannelResource;
|
|
|
|
|
use App\Models\Feed;
|
|
|
|
|
use App\Models\Language;
|
|
|
|
|
use App\Models\PlatformAccount;
|
|
|
|
|
use App\Models\PlatformChannel;
|
|
|
|
|
use App\Models\PlatformInstance;
|
|
|
|
|
use App\Services\Auth\LemmyAuthService;
|
|
|
|
|
use Illuminate\Http\JsonResponse;
|
|
|
|
|
use Illuminate\Http\Request;
|
|
|
|
|
use Illuminate\Support\Facades\Validator;
|
|
|
|
|
use Illuminate\Validation\ValidationException;
|
|
|
|
|
|
|
|
|
|
class OnboardingController extends BaseController
|
|
|
|
|
{
|
|
|
|
|
public function __construct(
|
|
|
|
|
private readonly LemmyAuthService $lemmyAuthService
|
|
|
|
|
) {}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get onboarding status - whether user needs onboarding
|
|
|
|
|
*/
|
|
|
|
|
public function status(): JsonResponse
|
|
|
|
|
{
|
|
|
|
|
$hasPlatformAccount = PlatformAccount::where('is_active', true)->exists();
|
|
|
|
|
$hasFeed = Feed::where('is_active', true)->exists();
|
|
|
|
|
$hasChannel = PlatformChannel::where('is_active', true)->exists();
|
|
|
|
|
|
|
|
|
|
$needsOnboarding = !$hasPlatformAccount || !$hasFeed || !$hasChannel;
|
|
|
|
|
|
|
|
|
|
// Determine current step
|
|
|
|
|
$currentStep = null;
|
|
|
|
|
if ($needsOnboarding) {
|
|
|
|
|
if (!$hasPlatformAccount) {
|
|
|
|
|
$currentStep = 'platform';
|
|
|
|
|
} elseif (!$hasFeed) {
|
|
|
|
|
$currentStep = 'feed';
|
|
|
|
|
} elseif (!$hasChannel) {
|
|
|
|
|
$currentStep = 'channel';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $this->sendResponse([
|
|
|
|
|
'needs_onboarding' => $needsOnboarding,
|
|
|
|
|
'current_step' => $currentStep,
|
|
|
|
|
'has_platform_account' => $hasPlatformAccount,
|
|
|
|
|
'has_feed' => $hasFeed,
|
|
|
|
|
'has_channel' => $hasChannel,
|
|
|
|
|
], 'Onboarding status retrieved successfully.');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get onboarding options (languages, platform instances)
|
|
|
|
|
*/
|
|
|
|
|
public function options(): JsonResponse
|
|
|
|
|
{
|
|
|
|
|
$languages = Language::where('is_active', true)
|
|
|
|
|
->orderBy('name')
|
|
|
|
|
->get(['id', 'short_code', 'name', 'native_name', 'is_active']);
|
|
|
|
|
|
|
|
|
|
$platformInstances = PlatformInstance::where('is_active', true)
|
|
|
|
|
->orderBy('name')
|
|
|
|
|
->get(['id', 'platform', 'url', 'name', 'description', 'is_active']);
|
|
|
|
|
|
|
|
|
|
return $this->sendResponse([
|
|
|
|
|
'languages' => $languages,
|
|
|
|
|
'platform_instances' => $platformInstances,
|
|
|
|
|
], 'Onboarding options retrieved successfully.');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Create platform account for onboarding
|
|
|
|
|
*/
|
|
|
|
|
public function createPlatform(Request $request): JsonResponse
|
|
|
|
|
{
|
|
|
|
|
$validator = Validator::make($request->all(), [
|
|
|
|
|
'instance_url' => 'required|url|max:255',
|
|
|
|
|
'username' => 'required|string|max:255',
|
|
|
|
|
'password' => 'required|string|min:6',
|
|
|
|
|
'platform' => 'required|in:lemmy',
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
if ($validator->fails()) {
|
|
|
|
|
throw new ValidationException($validator);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$validated = $validator->validated();
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
// Create or get platform instance
|
|
|
|
|
$platformInstance = PlatformInstance::firstOrCreate([
|
|
|
|
|
'url' => $validated['instance_url'],
|
|
|
|
|
'platform' => $validated['platform'],
|
|
|
|
|
], [
|
|
|
|
|
'name' => parse_url($validated['instance_url'], PHP_URL_HOST) ?? 'Lemmy Instance',
|
|
|
|
|
'is_active' => true,
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
// Authenticate with Lemmy API
|
|
|
|
|
$authResponse = $this->lemmyAuthService->authenticate(
|
|
|
|
|
$validated['instance_url'],
|
|
|
|
|
$validated['username'],
|
|
|
|
|
$validated['password']
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Create platform account with the current schema
|
|
|
|
|
$platformAccount = PlatformAccount::create([
|
|
|
|
|
'platform' => $validated['platform'],
|
|
|
|
|
'instance_url' => $validated['instance_url'],
|
|
|
|
|
'username' => $validated['username'],
|
|
|
|
|
'password' => $validated['password'],
|
|
|
|
|
'api_token' => $authResponse['jwt'] ?? null,
|
|
|
|
|
'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,
|
|
|
|
|
],
|
|
|
|
|
'is_active' => true,
|
|
|
|
|
'status' => 'active',
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
return $this->sendResponse(
|
|
|
|
|
new PlatformAccountResource($platformAccount),
|
|
|
|
|
'Platform account created successfully.'
|
|
|
|
|
);
|
|
|
|
|
|
2025-08-09 00:52:14 +02:00
|
|
|
} catch (\App\Exceptions\PlatformAuthException $e) {
|
|
|
|
|
// Handle authentication-specific errors with cleaner messages
|
|
|
|
|
return $this->sendError('Invalid username or password. Please check your credentials and try again.', [], 422);
|
2025-08-09 00:03:45 +02:00
|
|
|
} catch (\Exception $e) {
|
2025-08-09 00:52:14 +02:00
|
|
|
// Handle other errors (network, instance not found, etc.)
|
|
|
|
|
$message = 'Unable to connect to the Lemmy instance. Please check the URL and try again.';
|
|
|
|
|
|
|
|
|
|
// If it's a network/connection issue, provide a more specific message
|
|
|
|
|
if (str_contains(strtolower($e->getMessage()), 'connection') ||
|
|
|
|
|
str_contains(strtolower($e->getMessage()), 'network') ||
|
|
|
|
|
str_contains(strtolower($e->getMessage()), 'timeout')) {
|
|
|
|
|
$message = 'Connection failed. Please check the instance URL and your internet connection.';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $this->sendError($message, [], 422);
|
2025-08-09 00:03:45 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Create feed for onboarding
|
|
|
|
|
*/
|
|
|
|
|
public function createFeed(Request $request): JsonResponse
|
|
|
|
|
{
|
|
|
|
|
$validator = Validator::make($request->all(), [
|
|
|
|
|
'name' => 'required|string|max:255',
|
|
|
|
|
'url' => 'required|url|max:500',
|
|
|
|
|
'type' => 'required|in:website,rss',
|
|
|
|
|
'language_id' => 'required|exists:languages,id',
|
|
|
|
|
'description' => 'nullable|string|max:1000',
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
if ($validator->fails()) {
|
|
|
|
|
throw new ValidationException($validator);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$validated = $validator->validated();
|
|
|
|
|
|
|
|
|
|
$feed = Feed::create([
|
|
|
|
|
'name' => $validated['name'],
|
|
|
|
|
'url' => $validated['url'],
|
|
|
|
|
'type' => $validated['type'],
|
|
|
|
|
'language_id' => $validated['language_id'],
|
|
|
|
|
'description' => $validated['description'] ?? null,
|
|
|
|
|
'is_active' => true,
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
return $this->sendResponse(
|
|
|
|
|
new FeedResource($feed->load('language')),
|
|
|
|
|
'Feed created successfully.'
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Create channel for onboarding
|
|
|
|
|
*/
|
|
|
|
|
public function createChannel(Request $request): JsonResponse
|
|
|
|
|
{
|
|
|
|
|
$validator = Validator::make($request->all(), [
|
|
|
|
|
'name' => 'required|string|max:255',
|
|
|
|
|
'platform_instance_id' => 'required|exists:platform_instances,id',
|
|
|
|
|
'language_id' => 'required|exists:languages,id',
|
|
|
|
|
'description' => 'nullable|string|max:1000',
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
if ($validator->fails()) {
|
|
|
|
|
throw new ValidationException($validator);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$validated = $validator->validated();
|
|
|
|
|
|
|
|
|
|
$channel = PlatformChannel::create([
|
|
|
|
|
'platform_instance_id' => $validated['platform_instance_id'],
|
|
|
|
|
'channel_id' => $validated['name'], // For Lemmy, this is the community name
|
|
|
|
|
'name' => $validated['name'],
|
|
|
|
|
'display_name' => ucfirst($validated['name']),
|
|
|
|
|
'description' => $validated['description'] ?? null,
|
|
|
|
|
'language_id' => $validated['language_id'],
|
|
|
|
|
'is_active' => true,
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
return $this->sendResponse(
|
|
|
|
|
new PlatformChannelResource($channel->load(['platformInstance', 'language'])),
|
|
|
|
|
'Channel created successfully.'
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Mark onboarding as complete
|
|
|
|
|
*/
|
|
|
|
|
public function complete(): JsonResponse
|
|
|
|
|
{
|
|
|
|
|
// In a real implementation, you might want to update a user preference
|
|
|
|
|
// or create a setting that tracks onboarding completion
|
|
|
|
|
// For now, we'll just return success since the onboarding status
|
|
|
|
|
// is determined by the existence of platform accounts, feeds, and channels
|
|
|
|
|
|
|
|
|
|
return $this->sendResponse(
|
|
|
|
|
['completed' => true],
|
|
|
|
|
'Onboarding completed successfully.'
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|