61 - Remove onboarding create endpoints, use standard model endpoints

This commit is contained in:
myrmidex 2026-03-08 01:55:28 +01:00
parent c28ac317a5
commit 69dfb897cf
3 changed files with 0 additions and 441 deletions

View file

@ -2,12 +2,6 @@
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\Http\Resources\RouteResource;
use App\Jobs\ArticleDiscoveryJob;
use App\Models\Feed;
use App\Models\Language;
use App\Models\PlatformAccount;
@ -15,18 +9,10 @@
use App\Models\PlatformInstance;
use App\Models\Route;
use App\Models\Setting;
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
*/
@ -111,233 +97,6 @@ public function options(): JsonResponse
], 'Onboarding options retrieved successfully.');
}
/**
* Create platform account for onboarding
*/
public function createPlatform(Request $request): JsonResponse
{
$validator = Validator::make($request->all(), [
'instance_url' => 'required|string|max:255|regex:/^[a-zA-Z0-9]([a-zA-Z0-9\-\.]*[a-zA-Z0-9])?$/',
'username' => 'required|string|max:255',
'password' => 'required|string|min:6',
'platform' => 'required|in:lemmy',
], [
'instance_url.regex' => 'Please enter a valid domain name (e.g., lemmy.world, belgae.social)'
]);
if ($validator->fails()) {
throw new ValidationException($validator);
}
$validated = $validator->validated();
// Normalize the instance URL - prepend https:// if needed
$instanceDomain = $validated['instance_url'];
$fullInstanceUrl = 'https://' . $instanceDomain;
try {
// Create or get platform instance
$platformInstance = PlatformInstance::firstOrCreate([
'url' => $fullInstanceUrl,
'platform' => $validated['platform'],
], [
'name' => ucfirst($instanceDomain),
'is_active' => true,
]);
// Authenticate with Lemmy API using the full URL
$authResponse = $this->lemmyAuthService->authenticate(
$fullInstanceUrl,
$validated['username'],
$validated['password']
);
// Create platform account with the current schema
$platformAccount = PlatformAccount::create([
'platform' => $validated['platform'],
'instance_url' => $fullInstanceUrl,
'username' => $validated['username'],
'password' => $validated['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, // Store JWT in settings for now
],
'is_active' => true,
'status' => 'active',
]);
return $this->sendResponse(
new PlatformAccountResource($platformAccount),
'Platform account created successfully.'
);
} catch (\App\Exceptions\PlatformAuthException $e) {
// Check if it's a rate limit error
if (str_contains($e->getMessage(), 'Rate limited by')) {
return $this->sendError($e->getMessage(), [], 429);
}
return $this->sendError('Invalid username or password. Please check your credentials and try again.', [], 422);
} catch (\Exception $e) {
$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);
}
}
/**
* Create feed for onboarding
*/
public function createFeed(Request $request): JsonResponse
{
$validator = Validator::make($request->all(), [
'name' => 'required|string|max:255',
'provider' => 'required|in:belga,vrt',
'language_id' => 'required|exists:languages,id',
'description' => 'nullable|string|max:1000',
]);
if ($validator->fails()) {
throw new ValidationException($validator);
}
$validated = $validator->validated();
// Map provider to preset URL and type as required by onboarding tests
$provider = $validated['provider'];
$url = null;
$type = 'website';
if ($provider === 'vrt') {
$url = 'https://www.vrt.be/vrtnws/en/';
} elseif ($provider === 'belga') {
$url = 'https://www.belganewsagency.eu/';
}
$feed = Feed::firstOrCreate(
['url' => $url],
[
'name' => $validated['name'],
'type' => $type,
'provider' => $provider,
'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
* @throws ValidationException
*/
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();
// Get the platform instance to check for active accounts
$platformInstance = PlatformInstance::findOrFail($validated['platform_instance_id']);
// Check if there are active platform accounts for this instance
$activeAccounts = PlatformAccount::where('instance_url', $platformInstance->url)
->where('is_active', true)
->get();
if ($activeAccounts->isEmpty()) {
return $this->sendError(
'Cannot create channel: No active platform accounts found for this instance. Please create a platform account first.',
[],
422
);
}
$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,
]);
// Automatically attach the first active account to the channel
$firstAccount = $activeAccounts->first();
$channel->platformAccounts()->attach($firstAccount->id, [
'is_active' => true,
'priority' => 1,
'created_at' => now(),
'updated_at' => now(),
]);
return $this->sendResponse(
new PlatformChannelResource($channel->load(['platformInstance', 'language', 'platformAccounts'])),
'Channel created successfully and linked to platform account.'
);
}
/**
* Create route for onboarding
*
* @throws ValidationException
*/
public function createRoute(Request $request): JsonResponse
{
$validator = Validator::make($request->all(), [
'feed_id' => 'required|exists:feeds,id',
'platform_channel_id' => 'required|exists:platform_channels,id',
'priority' => 'nullable|integer|min:1|max:100',
]);
if ($validator->fails()) {
throw new ValidationException($validator);
}
$validated = $validator->validated();
$route = Route::create([
'feed_id' => $validated['feed_id'],
'platform_channel_id' => $validated['platform_channel_id'],
'priority' => $validated['priority'] ?? 50,
'is_active' => true,
]);
// Trigger article discovery when the first route is created during onboarding
// This ensures articles start being fetched immediately after setup
ArticleDiscoveryJob::dispatch();
return $this->sendResponse(
new RouteResource($route->load(['feed', 'platformChannel'])),
'Route created successfully.'
);
}
/**
* Mark onboarding as complete
*/

View file

@ -38,10 +38,6 @@
// Onboarding
Route::get('/onboarding/status', [OnboardingController::class, 'status'])->name('api.onboarding.status');
Route::get('/onboarding/options', [OnboardingController::class, 'options'])->name('api.onboarding.options');
Route::post('/onboarding/platform', [OnboardingController::class, 'createPlatform'])->name('api.onboarding.platform');
Route::post('/onboarding/feed', [OnboardingController::class, 'createFeed'])->name('api.onboarding.feed');
Route::post('/onboarding/channel', [OnboardingController::class, 'createChannel'])->name('api.onboarding.channel');
Route::post('/onboarding/route', [OnboardingController::class, 'createRoute'])->name('api.onboarding.route');
Route::post('/onboarding/complete', [OnboardingController::class, 'complete'])->name('api.onboarding.complete');
Route::post('/onboarding/skip', [OnboardingController::class, 'skip'])->name('api.onboarding.skip');
Route::post('/onboarding/reset-skip', [OnboardingController::class, 'resetSkip'])->name('api.onboarding.reset-skip');

View file

@ -9,7 +9,6 @@
use App\Models\PlatformInstance;
use App\Models\Route;
use App\Models\Setting;
use App\Services\Auth\LemmyAuthService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
@ -189,180 +188,6 @@ public function test_options_returns_languages_and_platform_instances()
]);
}
public function test_create_feed_validates_required_fields()
{
$response = $this->postJson('/api/v1/onboarding/feed', []);
$response->assertStatus(422)
->assertJsonValidationErrors(['name', 'provider', 'language_id']);
}
public function test_create_feed_creates_vrt_feed_successfully()
{
$feedData = [
'name' => 'VRT Test Feed',
'provider' => 'vrt',
'language_id' => 1,
'description' => 'Test description',
];
$response = $this->postJson('/api/v1/onboarding/feed', $feedData);
$response->assertStatus(200)
->assertJson([
'success' => true,
'data' => [
'name' => 'VRT Test Feed',
'url' => 'https://www.vrt.be/vrtnws/en/',
'type' => 'website',
'is_active' => true,
]
]);
$this->assertDatabaseHas('feeds', [
'name' => 'VRT Test Feed',
'url' => 'https://www.vrt.be/vrtnws/en/',
'type' => 'website',
'language_id' => 1,
'is_active' => true,
]);
}
public function test_create_feed_creates_belga_feed_successfully()
{
$feedData = [
'name' => 'Belga Test Feed',
'provider' => 'belga',
'language_id' => 1,
'description' => 'Test description',
];
$response = $this->postJson('/api/v1/onboarding/feed', $feedData);
$response->assertStatus(200)
->assertJson([
'success' => true,
'data' => [
'name' => 'Belga Test Feed',
'url' => 'https://www.belganewsagency.eu/',
'type' => 'website',
'is_active' => true,
]
]);
$this->assertDatabaseHas('feeds', [
'name' => 'Belga Test Feed',
'url' => 'https://www.belganewsagency.eu/',
'type' => 'website',
'language_id' => 1,
'is_active' => true,
]);
}
public function test_create_feed_rejects_invalid_provider()
{
$feedData = [
'name' => 'Invalid Feed',
'provider' => 'invalid',
'language_id' => 1,
'description' => 'Test description',
];
$response = $this->postJson('/api/v1/onboarding/feed', $feedData);
$response->assertStatus(422)
->assertJsonValidationErrors(['provider']);
}
public function test_create_channel_validates_required_fields()
{
$response = $this->postJson('/api/v1/onboarding/channel', []);
$response->assertStatus(422)
->assertJsonValidationErrors(['name', 'platform_instance_id', 'language_id']);
}
public function test_create_channel_creates_channel_successfully()
{
$platformInstance = PlatformInstance::factory()->create();
$language = Language::factory()->create();
// Create a platform account for this instance first
PlatformAccount::factory()->create([
'instance_url' => $platformInstance->url,
'is_active' => true
]);
$channelData = [
'name' => 'test_community',
'platform_instance_id' => $platformInstance->id,
'language_id' => $language->id,
'description' => 'Test community description',
];
$response = $this->postJson('/api/v1/onboarding/channel', $channelData);
$response->assertStatus(200)
->assertJson([
'success' => true,
'data' => [
'name' => 'test_community',
'display_name' => 'Test_community',
'channel_id' => 'test_community',
'is_active' => true,
]
]);
$this->assertDatabaseHas('platform_channels', [
'name' => 'test_community',
'channel_id' => 'test_community',
'platform_instance_id' => $platformInstance->id,
'language_id' => $language->id,
'is_active' => true,
]);
}
public function test_create_route_validates_required_fields()
{
$response = $this->postJson('/api/v1/onboarding/route', []);
$response->assertStatus(422)
->assertJsonValidationErrors(['feed_id', 'platform_channel_id']);
}
public function test_create_route_creates_route_successfully()
{
$language = Language::first();
$feed = Feed::factory()->language($language)->create();
$platformChannel = PlatformChannel::factory()->create();
$routeData = [
'feed_id' => $feed->id,
'platform_channel_id' => $platformChannel->id,
'priority' => 75,
];
$response = $this->postJson('/api/v1/onboarding/route', $routeData);
$response->assertStatus(200)
->assertJson([
'success' => true,
'data' => [
'feed_id' => $feed->id,
'platform_channel_id' => $platformChannel->id,
'priority' => 75,
'is_active' => true,
]
]);
$this->assertDatabaseHas('routes', [
'feed_id' => $feed->id,
'platform_channel_id' => $platformChannel->id,
'priority' => 75,
'is_active' => true,
]);
}
public function test_complete_onboarding_returns_success()
{
$response = $this->postJson('/api/v1/onboarding/complete');
@ -443,27 +268,6 @@ public function test_reset_skip_works_when_no_setting_exists()
]);
}
public function test_create_platform_validates_instance_url_format()
{
$response = $this->postJson('/api/v1/onboarding/platform', [
'instance_url' => 'invalid.domain.with.spaces and symbols!',
'username' => 'testuser',
'password' => 'password123',
'platform' => 'lemmy',
]);
$response->assertStatus(422)
->assertJsonValidationErrors(['instance_url']);
}
public function test_create_platform_validates_required_fields()
{
$response = $this->postJson('/api/v1/onboarding/platform', []);
$response->assertStatus(422)
->assertJsonValidationErrors(['instance_url', 'username', 'password', 'platform']);
}
public function test_onboarding_flow_integration()
{
// 1. Initial status - needs onboarding