Fix tests
+ remove old routes
This commit is contained in:
parent
ae07aa80e0
commit
bac0ae12d9
14 changed files with 61 additions and 1083 deletions
|
|
@ -1,37 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Controllers;
|
|
||||||
|
|
||||||
use App\Models\Article;
|
|
||||||
use App\Models\Setting;
|
|
||||||
use Illuminate\Contracts\View\View;
|
|
||||||
use Illuminate\Http\RedirectResponse;
|
|
||||||
use Illuminate\Http\Request;
|
|
||||||
|
|
||||||
class ArticlesController extends Controller
|
|
||||||
{
|
|
||||||
public function __invoke(Request $request): View
|
|
||||||
{
|
|
||||||
$articles = Article::with('articlePublication')
|
|
||||||
->orderBy('created_at', 'desc')
|
|
||||||
->paginate(15);
|
|
||||||
|
|
||||||
$publishingApprovalsEnabled = Setting::isPublishingApprovalsEnabled();
|
|
||||||
|
|
||||||
return view('pages.articles.index', compact('articles', 'publishingApprovalsEnabled'));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function approve(Article $article): RedirectResponse
|
|
||||||
{
|
|
||||||
$article->approve('manual');
|
|
||||||
|
|
||||||
return redirect()->back()->with('success', 'Article approved and queued for publishing.');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function reject(Article $article): RedirectResponse
|
|
||||||
{
|
|
||||||
$article->reject('manual');
|
|
||||||
|
|
||||||
return redirect()->back()->with('success', 'Article rejected.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,81 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Controllers;
|
|
||||||
|
|
||||||
use App\Models\Feed;
|
|
||||||
use App\Http\Requests\StoreFeedRequest;
|
|
||||||
use App\Http\Requests\UpdateFeedRequest;
|
|
||||||
use App\Services\OnboardingRedirectService;
|
|
||||||
use Illuminate\Contracts\View\View;
|
|
||||||
use Illuminate\Http\RedirectResponse;
|
|
||||||
|
|
||||||
class FeedsController extends Controller
|
|
||||||
{
|
|
||||||
public function index(): View
|
|
||||||
{
|
|
||||||
$feeds = Feed::orderBy('is_active', 'desc')
|
|
||||||
->orderBy('name')
|
|
||||||
->get();
|
|
||||||
|
|
||||||
return view('pages.feeds.index', compact('feeds'));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function create(): View
|
|
||||||
{
|
|
||||||
return view('pages.feeds.create');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function store(StoreFeedRequest $request): RedirectResponse
|
|
||||||
{
|
|
||||||
$validated = $request->validated();
|
|
||||||
$validated['is_active'] = $validated['is_active'] ?? true;
|
|
||||||
|
|
||||||
Feed::create($validated);
|
|
||||||
|
|
||||||
return OnboardingRedirectService::handleRedirect(
|
|
||||||
$request,
|
|
||||||
'feeds.index',
|
|
||||||
'Feed created successfully!'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function show(Feed $feed): View
|
|
||||||
{
|
|
||||||
return view('pages.feeds.show', compact('feed'));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function edit(Feed $feed): View
|
|
||||||
{
|
|
||||||
return view('pages.feeds.edit', compact('feed'));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function update(UpdateFeedRequest $request, Feed $feed): RedirectResponse
|
|
||||||
{
|
|
||||||
$validated = $request->validated();
|
|
||||||
$validated['is_active'] = $validated['is_active'] ?? $feed->is_active;
|
|
||||||
|
|
||||||
$feed->update($validated);
|
|
||||||
|
|
||||||
return redirect()->route('feeds.index')
|
|
||||||
->with('success', 'Feed updated successfully!');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function destroy(Feed $feed): RedirectResponse
|
|
||||||
{
|
|
||||||
$feed->delete();
|
|
||||||
|
|
||||||
return redirect()->route('feeds.index')
|
|
||||||
->with('success', 'Feed deleted successfully!');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function toggle(Feed $feed): RedirectResponse
|
|
||||||
{
|
|
||||||
$newStatus = !$feed->is_active;
|
|
||||||
$feed->update(['is_active' => $newStatus]);
|
|
||||||
|
|
||||||
$status = $newStatus ? 'activated' : 'deactivated';
|
|
||||||
|
|
||||||
return redirect()->route('feeds.index')
|
|
||||||
->with('success', "Feed {$status} successfully!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Controllers;
|
|
||||||
|
|
||||||
use App\Models\Log;
|
|
||||||
use Illuminate\Contracts\View\View;
|
|
||||||
use Illuminate\Http\Request;
|
|
||||||
|
|
||||||
class LogsController extends Controller
|
|
||||||
{
|
|
||||||
public function __invoke(Request $request): View
|
|
||||||
{
|
|
||||||
$logs = Log::orderBy('created_at', 'desc')->paginate(50);
|
|
||||||
|
|
||||||
return view('pages.logs.index', compact('logs'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,114 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Controllers;
|
|
||||||
|
|
||||||
use App\Models\Feed;
|
|
||||||
use App\Models\PlatformAccount;
|
|
||||||
use App\Models\PlatformChannel;
|
|
||||||
use App\Models\PlatformInstance;
|
|
||||||
use App\Services\SystemStatusService;
|
|
||||||
use App\Services\DashboardStatsService;
|
|
||||||
use Illuminate\Http\Request;
|
|
||||||
use Illuminate\View\View;
|
|
||||||
use Illuminate\Http\RedirectResponse;
|
|
||||||
|
|
||||||
class OnboardingController extends Controller
|
|
||||||
{
|
|
||||||
public function index(Request $request): View|RedirectResponse
|
|
||||||
{
|
|
||||||
// Check if user needs onboarding
|
|
||||||
if (!$this->needsOnboarding()) {
|
|
||||||
$systemStatus = resolve(SystemStatusService::class)->getSystemStatus();
|
|
||||||
$statsService = resolve(DashboardStatsService::class);
|
|
||||||
|
|
||||||
$period = $request->get('period', 'today');
|
|
||||||
$stats = $statsService->getStats($period);
|
|
||||||
$systemStats = $statsService->getSystemStats();
|
|
||||||
$availablePeriods = $statsService->getAvailablePeriods();
|
|
||||||
|
|
||||||
return view('pages.dashboard', compact(
|
|
||||||
'systemStatus',
|
|
||||||
'stats',
|
|
||||||
'systemStats',
|
|
||||||
'availablePeriods',
|
|
||||||
'period'
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
return view('onboarding.welcome');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function platform(): View|RedirectResponse
|
|
||||||
{
|
|
||||||
if (!$this->needsOnboarding()) {
|
|
||||||
return redirect()->route('feeds.index');
|
|
||||||
}
|
|
||||||
|
|
||||||
return view('onboarding.platform');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function feed(): View|RedirectResponse
|
|
||||||
{
|
|
||||||
if (!$this->needsOnboarding()) {
|
|
||||||
return redirect()->route('feeds.index');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$this->hasPlatformAccount()) {
|
|
||||||
return redirect()->route('onboarding.platform');
|
|
||||||
}
|
|
||||||
|
|
||||||
return view('onboarding.feed');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function channel(): View|RedirectResponse
|
|
||||||
{
|
|
||||||
if (!$this->needsOnboarding()) {
|
|
||||||
return redirect()->route('feeds.index');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$this->hasPlatformAccount()) {
|
|
||||||
return redirect()->route('onboarding.platform');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$this->hasFeed()) {
|
|
||||||
return redirect()->route('onboarding.feed');
|
|
||||||
}
|
|
||||||
|
|
||||||
return view('onboarding.channel');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function complete(): View|RedirectResponse
|
|
||||||
{
|
|
||||||
if (!$this->needsOnboarding()) {
|
|
||||||
return redirect()->route('feeds.index');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$this->hasPlatformAccount() || !$this->hasFeed() || !$this->hasChannel()) {
|
|
||||||
return redirect()->route('onboarding.index');
|
|
||||||
}
|
|
||||||
|
|
||||||
$systemStatus = resolve(SystemStatusService::class)->getSystemStatus();
|
|
||||||
|
|
||||||
return view('onboarding.complete', compact('systemStatus'));
|
|
||||||
}
|
|
||||||
|
|
||||||
private function needsOnboarding(): bool
|
|
||||||
{
|
|
||||||
return !$this->hasPlatformAccount() || !$this->hasFeed() || !$this->hasChannel();
|
|
||||||
}
|
|
||||||
|
|
||||||
private function hasPlatformAccount(): bool
|
|
||||||
{
|
|
||||||
return PlatformAccount::where('is_active', true)->exists();
|
|
||||||
}
|
|
||||||
|
|
||||||
private function hasFeed(): bool
|
|
||||||
{
|
|
||||||
return Feed::where('is_active', true)->exists();
|
|
||||||
}
|
|
||||||
|
|
||||||
private function hasChannel(): bool
|
|
||||||
{
|
|
||||||
return PlatformChannel::where('is_active', true)->exists();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,106 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Controllers;
|
|
||||||
|
|
||||||
use App\Models\PlatformAccount;
|
|
||||||
use App\Models\PlatformInstance;
|
|
||||||
use App\Enums\PlatformEnum;
|
|
||||||
use Illuminate\Http\Request;
|
|
||||||
use Illuminate\Contracts\View\View;
|
|
||||||
use Illuminate\Http\RedirectResponse;
|
|
||||||
use Illuminate\Support\Facades\View as ViewFacade;
|
|
||||||
|
|
||||||
class PlatformAccountsController extends Controller
|
|
||||||
{
|
|
||||||
public function index(): View
|
|
||||||
{
|
|
||||||
$accounts = PlatformAccount::orderBy('platform')->orderBy('created_at', 'desc')->get();
|
|
||||||
|
|
||||||
return view('pages.platforms.index', compact('accounts'));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function create(): View
|
|
||||||
{
|
|
||||||
return ViewFacade::make('pages.platforms.create');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function store(Request $request): RedirectResponse
|
|
||||||
{
|
|
||||||
$validated = $request->validate([
|
|
||||||
'platform' => 'required|in:lemmy,mastodon,reddit',
|
|
||||||
'instance_url' => 'required|url',
|
|
||||||
'username' => 'required|string|max:255',
|
|
||||||
'password' => 'required|string',
|
|
||||||
'settings' => 'nullable|array',
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Create or find platform instance
|
|
||||||
$platformEnum = PlatformEnum::from($validated['platform']);
|
|
||||||
$instance = PlatformInstance::firstOrCreate([
|
|
||||||
'platform' => $platformEnum,
|
|
||||||
'url' => $validated['instance_url'],
|
|
||||||
], [
|
|
||||||
'name' => parse_url($validated['instance_url'], PHP_URL_HOST),
|
|
||||||
'description' => ucfirst($validated['platform']) . ' instance',
|
|
||||||
'is_active' => true,
|
|
||||||
]);
|
|
||||||
|
|
||||||
$account = PlatformAccount::create($validated);
|
|
||||||
|
|
||||||
// If this is the first account for this platform, make it active
|
|
||||||
if (! PlatformAccount::where('platform', $validated['platform'])->where('is_active', true)->exists()) {
|
|
||||||
$account->setAsActive();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if there's a redirect_to parameter for onboarding flow
|
|
||||||
$redirectTo = $request->input('redirect_to');
|
|
||||||
if ($redirectTo) {
|
|
||||||
return redirect($redirectTo)
|
|
||||||
->with('success', 'Platform account created successfully!');
|
|
||||||
}
|
|
||||||
|
|
||||||
return redirect()->route('platforms.index')
|
|
||||||
->with('success', 'Platform account created successfully!');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function edit(PlatformAccount $platformAccount): View
|
|
||||||
{
|
|
||||||
return ViewFacade::make('pages.platforms.edit', compact('platformAccount'));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function update(Request $request, PlatformAccount $platformAccount): RedirectResponse
|
|
||||||
{
|
|
||||||
$validated = $request->validate([
|
|
||||||
'instance_url' => 'required|url',
|
|
||||||
'username' => 'required|string|max:255',
|
|
||||||
'password' => 'nullable|string',
|
|
||||||
'settings' => 'nullable|array',
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Don't update password if not provided
|
|
||||||
if (empty($validated['password'])) {
|
|
||||||
unset($validated['password']);
|
|
||||||
}
|
|
||||||
|
|
||||||
$platformAccount->update($validated);
|
|
||||||
|
|
||||||
return redirect()->route('platforms.index')
|
|
||||||
->with('success', 'Platform account updated successfully!');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function destroy(PlatformAccount $platformAccount): RedirectResponse
|
|
||||||
{
|
|
||||||
$platformAccount->delete();
|
|
||||||
|
|
||||||
return redirect()->route('platforms.index')
|
|
||||||
->with('success', 'Platform account deleted successfully!');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setActive(PlatformAccount $platformAccount): RedirectResponse
|
|
||||||
{
|
|
||||||
$platformAccount->setAsActive();
|
|
||||||
|
|
||||||
return redirect()->route('platforms.index')
|
|
||||||
->with('success', "Set $platformAccount->username@$platformAccount->instance_url as active for {$platformAccount->platform->value}!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,116 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Controllers;
|
|
||||||
|
|
||||||
use App\Models\PlatformChannel;
|
|
||||||
use App\Models\PlatformInstance;
|
|
||||||
use Illuminate\Http\Request;
|
|
||||||
use Illuminate\Contracts\View\View;
|
|
||||||
use Illuminate\Http\RedirectResponse;
|
|
||||||
use Illuminate\Support\Facades\View as ViewFacade;
|
|
||||||
|
|
||||||
class PlatformChannelsController extends Controller
|
|
||||||
{
|
|
||||||
public function index(): View
|
|
||||||
{
|
|
||||||
$channels = PlatformChannel::with('platformInstance')
|
|
||||||
->orderBy('platform_instance_id')
|
|
||||||
->orderBy('name')
|
|
||||||
->get();
|
|
||||||
|
|
||||||
return ViewFacade::make('pages.channels.index', compact('channels'));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function create(): View
|
|
||||||
{
|
|
||||||
$instances = PlatformInstance::where('is_active', true)
|
|
||||||
->orderBy('name')
|
|
||||||
->get();
|
|
||||||
|
|
||||||
return ViewFacade::make('pages.channels.create', compact('instances'));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function store(Request $request): RedirectResponse
|
|
||||||
{
|
|
||||||
$validated = $request->validate([
|
|
||||||
'platform_instance_id' => 'required|exists:platform_instances,id',
|
|
||||||
'name' => 'required|string|max:255',
|
|
||||||
'display_name' => 'nullable|string|max:255',
|
|
||||||
'channel_id' => 'nullable|string|max:255',
|
|
||||||
'description' => 'nullable|string',
|
|
||||||
'language_id' => 'required|exists:languages,id',
|
|
||||||
'is_active' => 'boolean',
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Default is_active to true if not provided
|
|
||||||
$validated['is_active'] = $validated['is_active'] ?? true;
|
|
||||||
|
|
||||||
// Set display_name to name if not provided
|
|
||||||
$validated['display_name'] = $validated['display_name'] ?? $validated['name'];
|
|
||||||
|
|
||||||
PlatformChannel::create($validated);
|
|
||||||
|
|
||||||
// Check if there's a redirect_to parameter for onboarding flow
|
|
||||||
$redirectTo = $request->input('redirect_to');
|
|
||||||
if ($redirectTo) {
|
|
||||||
return redirect($redirectTo)
|
|
||||||
->with('success', 'Channel created successfully!');
|
|
||||||
}
|
|
||||||
|
|
||||||
return redirect()->route('channels.index')
|
|
||||||
->with('success', 'Channel created successfully!');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function show(PlatformChannel $channel): View
|
|
||||||
{
|
|
||||||
$channel->load(['platformInstance', 'feeds']);
|
|
||||||
|
|
||||||
return ViewFacade::make('pages.channels.show', compact('channel'));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function edit(PlatformChannel $channel): View
|
|
||||||
{
|
|
||||||
$instances = PlatformInstance::where('is_active', true)
|
|
||||||
->orderBy('name')
|
|
||||||
->get();
|
|
||||||
|
|
||||||
return ViewFacade::make('pages.channels.edit', compact('channel', 'instances'));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function update(Request $request, PlatformChannel $channel): RedirectResponse
|
|
||||||
{
|
|
||||||
$validated = $request->validate([
|
|
||||||
'platform_instance_id' => 'required|exists:platform_instances,id',
|
|
||||||
'name' => 'required|string|max:255',
|
|
||||||
'display_name' => 'nullable|string|max:255',
|
|
||||||
'channel_id' => 'nullable|string|max:255',
|
|
||||||
'description' => 'nullable|string',
|
|
||||||
'language_id' => 'required|exists:languages,id',
|
|
||||||
'is_active' => 'boolean',
|
|
||||||
]);
|
|
||||||
|
|
||||||
$channel->update($validated);
|
|
||||||
|
|
||||||
return redirect()->route('channels.index')
|
|
||||||
->with('success', 'Channel updated successfully!');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function destroy(PlatformChannel $channel): RedirectResponse
|
|
||||||
{
|
|
||||||
$channel->delete();
|
|
||||||
|
|
||||||
return redirect()->route('channels.index')
|
|
||||||
->with('success', 'Channel deleted successfully!');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function toggle(PlatformChannel $channel): RedirectResponse
|
|
||||||
{
|
|
||||||
$newStatus = !$channel->is_active;
|
|
||||||
$channel->update(['is_active' => $newStatus]);
|
|
||||||
|
|
||||||
$status = $newStatus ? 'activated' : 'deactivated';
|
|
||||||
|
|
||||||
return redirect()->route('channels.index')
|
|
||||||
->with('success', "Channel {$status} successfully!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,173 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Controllers;
|
|
||||||
|
|
||||||
use App\Models\Feed;
|
|
||||||
use App\Models\Route;
|
|
||||||
use App\Models\PlatformChannel;
|
|
||||||
use App\Services\RoutingValidationService;
|
|
||||||
use App\Exceptions\RoutingMismatchException;
|
|
||||||
use Illuminate\Database\Eloquent\Collection;
|
|
||||||
use Illuminate\Http\Request;
|
|
||||||
use Illuminate\Contracts\View\View;
|
|
||||||
use Illuminate\Http\RedirectResponse;
|
|
||||||
|
|
||||||
class RoutingController extends Controller
|
|
||||||
{
|
|
||||||
public function index(): View
|
|
||||||
{
|
|
||||||
$feeds = Feed::with(['channels.platformInstance'])
|
|
||||||
->where('is_active', true)
|
|
||||||
->orderBy('name')
|
|
||||||
->get();
|
|
||||||
|
|
||||||
$channels = PlatformChannel::with(['platformInstance', 'feeds'])
|
|
||||||
->where('is_active', true)
|
|
||||||
->orderBy('name')
|
|
||||||
->get();
|
|
||||||
|
|
||||||
return view('pages.routing.index', compact('feeds', 'channels'));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function create(): View
|
|
||||||
{
|
|
||||||
$feeds = Feed::where('is_active', true)
|
|
||||||
->orderBy('name')
|
|
||||||
->get();
|
|
||||||
|
|
||||||
$channels = PlatformChannel::with('platformInstance')
|
|
||||||
->where('is_active', true)
|
|
||||||
->orderBy('name')
|
|
||||||
->get();
|
|
||||||
|
|
||||||
return view('pages.routing.create', compact('feeds', 'channels'));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function store(Request $request): RedirectResponse
|
|
||||||
{
|
|
||||||
$validated = $request->validate([
|
|
||||||
'feed_id' => 'required|exists:feeds,id',
|
|
||||||
'channel_ids' => 'required|array|min:1',
|
|
||||||
'channel_ids.*' => 'exists:platform_channels,id',
|
|
||||||
'priority' => 'integer|min:0|max:100',
|
|
||||||
'filters' => 'nullable|string'
|
|
||||||
]);
|
|
||||||
|
|
||||||
/** @var Feed $feed */
|
|
||||||
$feed = Feed::findOrFail($validated['feed_id']);
|
|
||||||
|
|
||||||
/** @var Collection<int, PlatformChannel> $channels */
|
|
||||||
$channels = PlatformChannel::findMany($validated['channel_ids']);
|
|
||||||
$priority = $validated['priority'] ?? 0;
|
|
||||||
|
|
||||||
try {
|
|
||||||
app(RoutingValidationService::class)->validateLanguageCompatibility($feed, $channels);
|
|
||||||
} catch (RoutingMismatchException $e) {
|
|
||||||
return redirect()->back()
|
|
||||||
->withInput()
|
|
||||||
->withErrors(['language' => $e->getMessage()]);
|
|
||||||
}
|
|
||||||
|
|
||||||
$filters = $this->parseJsonFilters($validated['filters'] ?? null);
|
|
||||||
|
|
||||||
// Attach channels to feed
|
|
||||||
$syncData = [];
|
|
||||||
foreach ($validated['channel_ids'] as $channelId) {
|
|
||||||
$syncData[$channelId] = [
|
|
||||||
'is_active' => true,
|
|
||||||
'priority' => $priority,
|
|
||||||
'filters' => $filters,
|
|
||||||
'created_at' => now(),
|
|
||||||
'updated_at' => now()
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
$feed->channels()->syncWithoutDetaching($syncData);
|
|
||||||
|
|
||||||
return redirect()->route('routing.index')
|
|
||||||
->with('success', 'Feed routing created successfully!');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function edit(Feed $feed, PlatformChannel $channel): View
|
|
||||||
{
|
|
||||||
$routing = $feed->channels()
|
|
||||||
->wherePivot('platform_channel_id', $channel->id)
|
|
||||||
->first();
|
|
||||||
|
|
||||||
if (! $routing) {
|
|
||||||
abort(404, 'Routing not found');
|
|
||||||
}
|
|
||||||
|
|
||||||
return view('pages.routing.edit', compact('feed', 'channel', 'routing'));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function update(Request $request, Feed $feed, PlatformChannel $channel): RedirectResponse
|
|
||||||
{
|
|
||||||
$validated = $request->validate([
|
|
||||||
'is_active' => 'boolean',
|
|
||||||
'priority' => 'integer|min:0|max:100',
|
|
||||||
'filters' => 'nullable|string'
|
|
||||||
]);
|
|
||||||
|
|
||||||
$filters = $this->parseJsonFilters($validated['filters'] ?? null);
|
|
||||||
|
|
||||||
$feed->channels()->updateExistingPivot($channel->id, [
|
|
||||||
'is_active' => $validated['is_active'] ?? true,
|
|
||||||
'priority' => $validated['priority'] ?? 0,
|
|
||||||
'filters' => $filters,
|
|
||||||
'updated_at' => now()
|
|
||||||
]);
|
|
||||||
|
|
||||||
return redirect()->route('routing.index')
|
|
||||||
->with('success', 'Routing updated successfully!');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function destroy(Feed $feed, PlatformChannel $channel): RedirectResponse
|
|
||||||
{
|
|
||||||
$feed->channels()->detach($channel->id);
|
|
||||||
|
|
||||||
return redirect()->route('routing.index')
|
|
||||||
->with('success', 'Routing deleted successfully!');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function toggle(Request $request, Feed $feed, PlatformChannel $channel): RedirectResponse
|
|
||||||
{
|
|
||||||
$routing = Route::where('feed_id', $feed->id)
|
|
||||||
->where('platform_channel_id', $channel->id)
|
|
||||||
->first();
|
|
||||||
|
|
||||||
if (! $routing) {
|
|
||||||
abort(404, 'Routing not found');
|
|
||||||
}
|
|
||||||
|
|
||||||
$newStatus = ! $routing->is_active;
|
|
||||||
|
|
||||||
$feed->channels()->updateExistingPivot($channel->id, [
|
|
||||||
'is_active' => $newStatus,
|
|
||||||
'updated_at' => now()
|
|
||||||
]);
|
|
||||||
|
|
||||||
$status = $newStatus ? 'activated' : 'deactivated';
|
|
||||||
|
|
||||||
return redirect()->route('routing.index')
|
|
||||||
->with('success', "Routing {$status} successfully!");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return array<string, mixed>|null
|
|
||||||
*/
|
|
||||||
private function parseJsonFilters(?string $json): ?array
|
|
||||||
{
|
|
||||||
if (empty($json)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
$decoded = json_decode($json, true);
|
|
||||||
|
|
||||||
if (json_last_error() === JSON_ERROR_NONE) {
|
|
||||||
return $decoded;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,39 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Controllers;
|
|
||||||
|
|
||||||
use App\Models\Setting;
|
|
||||||
use Illuminate\Contracts\View\View;
|
|
||||||
use Illuminate\Http\RedirectResponse;
|
|
||||||
use Illuminate\Http\Request;
|
|
||||||
|
|
||||||
class SettingsController extends Controller
|
|
||||||
{
|
|
||||||
public function index(): View
|
|
||||||
{
|
|
||||||
$articleProcessingEnabled = Setting::isArticleProcessingEnabled();
|
|
||||||
$publishingApprovalsEnabled = Setting::isPublishingApprovalsEnabled();
|
|
||||||
|
|
||||||
return view('pages.settings.index', compact('articleProcessingEnabled', 'publishingApprovalsEnabled'));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function update(Request $request): RedirectResponse
|
|
||||||
{
|
|
||||||
$request->validate([
|
|
||||||
'article_processing_enabled' => 'boolean',
|
|
||||||
'enable_publishing_approvals' => 'boolean',
|
|
||||||
]);
|
|
||||||
|
|
||||||
Setting::setArticleProcessingEnabled($request->boolean('article_processing_enabled'));
|
|
||||||
Setting::setPublishingApprovalsEnabled($request->boolean('enable_publishing_approvals'));
|
|
||||||
|
|
||||||
// If redirected from onboarding, go to dashboard
|
|
||||||
if ($request->get('from') === 'onboarding') {
|
|
||||||
return redirect()->route('onboarding.index')
|
|
||||||
->with('success', 'System activated successfully! Welcome to Lemmy Poster.');
|
|
||||||
}
|
|
||||||
|
|
||||||
return redirect()->route('settings.index')
|
|
||||||
->with('success', 'Settings updated successfully.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
use Illuminate\Support\Carbon;
|
use Illuminate\Support\Carbon;
|
||||||
|
|
@ -19,6 +20,8 @@
|
||||||
*/
|
*/
|
||||||
class Keyword extends Model
|
class Keyword extends Model
|
||||||
{
|
{
|
||||||
|
use HasFactory;
|
||||||
|
|
||||||
protected $fillable = [
|
protected $fillable = [
|
||||||
'feed_id',
|
'feed_id',
|
||||||
'platform_channel_id',
|
'platform_channel_id',
|
||||||
|
|
|
||||||
|
|
@ -12,24 +12,40 @@ class KeywordFactory extends Factory
|
||||||
public function definition(): array
|
public function definition(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'keyword' => $this->faker->word(),
|
'feed_id' => null,
|
||||||
'is_blocked' => $this->faker->boolean(30), // 30% chance of being blocked
|
'platform_channel_id' => null,
|
||||||
|
'keyword' => 'test keyword',
|
||||||
|
'is_active' => $this->faker->boolean(70), // 70% chance of being active
|
||||||
'created_at' => $this->faker->dateTimeBetween('-1 year', 'now'),
|
'created_at' => $this->faker->dateTimeBetween('-1 year', 'now'),
|
||||||
'updated_at' => now(),
|
'updated_at' => now(),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function blocked(): static
|
public function forFeed(\App\Models\Feed $feed): static
|
||||||
{
|
{
|
||||||
return $this->state(fn (array $attributes) => [
|
return $this->state(fn (array $attributes) => [
|
||||||
'is_blocked' => true,
|
'feed_id' => $feed->id,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function allowed(): static
|
public function forChannel(\App\Models\PlatformChannel $channel): static
|
||||||
{
|
{
|
||||||
return $this->state(fn (array $attributes) => [
|
return $this->state(fn (array $attributes) => [
|
||||||
'is_blocked' => false,
|
'platform_channel_id' => $channel->id,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function active(): static
|
||||||
|
{
|
||||||
|
return $this->state(fn (array $attributes) => [
|
||||||
|
'is_active' => true,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function inactive(): static
|
||||||
|
{
|
||||||
|
return $this->state(fn (array $attributes) => [
|
||||||
|
'is_active' => false,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,45 +1,26 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
use App\Http\Controllers\ArticlesController;
|
|
||||||
use App\Http\Controllers\LogsController;
|
|
||||||
use App\Http\Controllers\OnboardingController;
|
|
||||||
use App\Http\Controllers\SettingsController;
|
|
||||||
use Illuminate\Support\Facades\Route;
|
use Illuminate\Support\Facades\Route;
|
||||||
|
|
||||||
// React SPA - catch all routes and serve the React app
|
|
||||||
Route::get('/{path?}', function () {
|
|
||||||
return view('app');
|
|
||||||
})->where('path', '.*')->name('spa');
|
|
||||||
|
|
||||||
// Legacy routes (can be removed once fully migrated to React)
|
|
||||||
/*
|
/*
|
||||||
// Onboarding routes
|
|--------------------------------------------------------------------------
|
||||||
Route::get('/', [OnboardingController::class, 'index'])->name('onboarding.index');
|
| Web Routes
|
||||||
Route::get('/onboarding/platform', [OnboardingController::class, 'platform'])->name('onboarding.platform');
|
|--------------------------------------------------------------------------
|
||||||
Route::get('/onboarding/feed', [OnboardingController::class, 'feed'])->name('onboarding.feed');
|
|
|
||||||
Route::get('/onboarding/channel', [OnboardingController::class, 'channel'])->name('onboarding.channel');
|
| Note: This Laravel backend now serves as an API for a separate React frontend.
|
||||||
Route::get('/onboarding/complete', [OnboardingController::class, 'complete'])->name('onboarding.complete');
|
| All UI routes are handled by the React application.
|
||||||
|
|
|
||||||
Route::get('/articles', ArticlesController::class)->name('articles');
|
|
||||||
Route::post('/articles/{article}/approve', [ArticlesController::class, 'approve'])->name('articles.approve');
|
|
||||||
Route::post('/articles/{article}/reject', [ArticlesController::class, 'reject'])->name('articles.reject');
|
|
||||||
Route::get('/logs', LogsController::class)->name('logs');
|
|
||||||
Route::get('/settings', [SettingsController::class, 'index'])->name('settings.index');
|
|
||||||
Route::put('/settings', [SettingsController::class, 'update'])->name('settings.update');
|
|
||||||
|
|
||||||
Route::resource('platforms', App\Http\Controllers\PlatformAccountsController::class)->names('platforms');
|
|
||||||
Route::post('/platforms/{platformAccount}/set-active', [App\Http\Controllers\PlatformAccountsController::class, 'setActive'])->name('platforms.set-active');
|
|
||||||
|
|
||||||
Route::resource('channels', App\Http\Controllers\PlatformChannelsController::class)->names('channels');
|
|
||||||
Route::post('/channels/{channel}/toggle', [App\Http\Controllers\PlatformChannelsController::class, 'toggle'])->name('channels.toggle');
|
|
||||||
Route::resource('feeds', App\Http\Controllers\FeedsController::class)->names('feeds');
|
|
||||||
Route::post('/feeds/{feed}/toggle', [App\Http\Controllers\FeedsController::class, 'toggle'])->name('feeds.toggle');
|
|
||||||
|
|
||||||
Route::get('/routing', [App\Http\Controllers\RoutingController::class, 'index'])->name('routing.index');
|
|
||||||
Route::get('/routing/create', [App\Http\Controllers\RoutingController::class, 'create'])->name('routing.create');
|
|
||||||
Route::post('/routing', [App\Http\Controllers\RoutingController::class, 'store'])->name('routing.store');
|
|
||||||
Route::get('/routing/{feed}/{channel}/edit', [App\Http\Controllers\RoutingController::class, 'edit'])->name('routing.edit');
|
|
||||||
Route::put('/routing/{feed}/{channel}', [App\Http\Controllers\RoutingController::class, 'update'])->name('routing.update');
|
|
||||||
Route::delete('/routing/{feed}/{channel}', [App\Http\Controllers\RoutingController::class, 'destroy'])->name('routing.destroy');
|
|
||||||
Route::post('/routing/{feed}/{channel}/toggle', [App\Http\Controllers\RoutingController::class, 'toggle'])->name('routing.toggle');
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// Simple API health check route
|
||||||
|
Route::get('/health', function () {
|
||||||
|
return response()->json(['status' => 'ok', 'service' => 'FFR API Backend']);
|
||||||
|
});
|
||||||
|
|
||||||
|
// For any unmatched routes, return a JSON response indicating this is an API backend
|
||||||
|
Route::fallback(function () {
|
||||||
|
return response()->json([
|
||||||
|
'message' => 'This is the FFR API backend. Use /api/v1/* endpoints or check the React frontend.',
|
||||||
|
'api_base' => '/api/v1'
|
||||||
|
], 404);
|
||||||
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,348 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace Tests\Feature;
|
|
||||||
|
|
||||||
use App\Models\Article;
|
|
||||||
use App\Models\Feed;
|
|
||||||
use App\Models\PlatformAccount;
|
|
||||||
use App\Models\PlatformChannel;
|
|
||||||
use App\Models\Route;
|
|
||||||
use App\Models\Setting;
|
|
||||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
||||||
use Illuminate\Support\Facades\Event;
|
|
||||||
use Tests\TestCase;
|
|
||||||
|
|
||||||
class ApiEndpointRegressionTest extends TestCase
|
|
||||||
{
|
|
||||||
use RefreshDatabase;
|
|
||||||
|
|
||||||
public function test_homepage_loads_successfully(): void
|
|
||||||
{
|
|
||||||
$response = $this->get('/');
|
|
||||||
$response->assertSuccessful();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function test_onboarding_platform_page_loads(): void
|
|
||||||
{
|
|
||||||
$response = $this->get('/onboarding/platform');
|
|
||||||
$response->assertSuccessful();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function test_onboarding_feed_page_loads(): void
|
|
||||||
{
|
|
||||||
$response = $this->get('/onboarding/feed');
|
|
||||||
|
|
||||||
// Accept both successful response and redirects for onboarding flow
|
|
||||||
$this->assertTrue(
|
|
||||||
$response->isSuccessful() || $response->isRedirect(),
|
|
||||||
"Expected successful response or redirect, got status: " . $response->getStatusCode()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function test_onboarding_channel_page_loads(): void
|
|
||||||
{
|
|
||||||
$response = $this->get('/onboarding/channel');
|
|
||||||
|
|
||||||
// Accept both successful response and redirects for onboarding flow
|
|
||||||
$this->assertTrue(
|
|
||||||
$response->isSuccessful() || $response->isRedirect(),
|
|
||||||
"Expected successful response or redirect, got status: " . $response->getStatusCode()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function test_onboarding_complete_page_loads(): void
|
|
||||||
{
|
|
||||||
$response = $this->get('/onboarding/complete');
|
|
||||||
$response->assertSuccessful();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function test_articles_page_loads_successfully(): void
|
|
||||||
{
|
|
||||||
$response = $this->get('/articles');
|
|
||||||
$response->assertSuccessful();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function test_articles_approve_endpoint_works(): void
|
|
||||||
{
|
|
||||||
Event::fake(); // Disable events to prevent side effects
|
|
||||||
|
|
||||||
$feed = Feed::factory()->create();
|
|
||||||
$article = Article::factory()->create([
|
|
||||||
'feed_id' => $feed->id,
|
|
||||||
'approval_status' => 'pending'
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Ensure article exists in database
|
|
||||||
$this->assertDatabaseHas('articles', [
|
|
||||||
'id' => $article->id,
|
|
||||||
'approval_status' => 'pending'
|
|
||||||
]);
|
|
||||||
|
|
||||||
$response = $this->withoutMiddleware()
|
|
||||||
->post("/articles/{$article->id}/approve");
|
|
||||||
|
|
||||||
$response->assertRedirect();
|
|
||||||
|
|
||||||
// Check database directly
|
|
||||||
$this->assertDatabaseHas('articles', [
|
|
||||||
'id' => $article->id,
|
|
||||||
'approval_status' => 'approved'
|
|
||||||
]);
|
|
||||||
|
|
||||||
$article->refresh();
|
|
||||||
$this->assertEquals('approved', $article->approval_status, 'Article status should be approved after calling approve endpoint');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function test_articles_reject_endpoint_works(): void
|
|
||||||
{
|
|
||||||
$feed = Feed::factory()->create();
|
|
||||||
$article = Article::factory()->create([
|
|
||||||
'feed_id' => $feed->id,
|
|
||||||
'approval_status' => 'pending'
|
|
||||||
]);
|
|
||||||
|
|
||||||
$response = $this->withoutMiddleware()
|
|
||||||
->post("/articles/{$article->id}/reject");
|
|
||||||
$response->assertRedirect();
|
|
||||||
|
|
||||||
$article->refresh();
|
|
||||||
$this->assertEquals('rejected', $article->approval_status);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function test_logs_page_loads_successfully(): void
|
|
||||||
{
|
|
||||||
$response = $this->get('/logs');
|
|
||||||
$response->assertSuccessful();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function test_settings_page_loads_successfully(): void
|
|
||||||
{
|
|
||||||
$response = $this->get('/settings');
|
|
||||||
$response->assertSuccessful();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function test_settings_update_endpoint_works(): void
|
|
||||||
{
|
|
||||||
Setting::factory()->create([
|
|
||||||
'key' => 'test_setting',
|
|
||||||
'value' => 'old_value'
|
|
||||||
]);
|
|
||||||
|
|
||||||
$response = $this->withoutMiddleware()
|
|
||||||
->put('/settings', [
|
|
||||||
'test_setting' => 'new_value'
|
|
||||||
]);
|
|
||||||
|
|
||||||
$response->assertRedirect();
|
|
||||||
$this->assertEquals('new_value', Setting::where('key', 'test_setting')->first()->value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function test_platforms_index_loads_successfully(): void
|
|
||||||
{
|
|
||||||
$response = $this->get('/platforms');
|
|
||||||
$response->assertSuccessful();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function test_platforms_create_loads_successfully(): void
|
|
||||||
{
|
|
||||||
$response = $this->get('/platforms/create');
|
|
||||||
$response->assertSuccessful();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function test_platforms_show_loads_successfully(): void
|
|
||||||
{
|
|
||||||
$platform = PlatformAccount::factory()->create();
|
|
||||||
|
|
||||||
$response = $this->get("/platforms/{$platform->id}");
|
|
||||||
$response->assertSuccessful();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function test_platforms_edit_loads_successfully(): void
|
|
||||||
{
|
|
||||||
$platform = PlatformAccount::factory()->create();
|
|
||||||
|
|
||||||
$response = $this->get("/platforms/{$platform->id}/edit");
|
|
||||||
$response->assertSuccessful();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function test_platforms_set_active_endpoint_works(): void
|
|
||||||
{
|
|
||||||
$platform = PlatformAccount::factory()->create(['is_active' => false]);
|
|
||||||
|
|
||||||
$response = $this->withoutMiddleware()
|
|
||||||
->post("/platforms/{$platform->id}/set-active");
|
|
||||||
$response->assertRedirect();
|
|
||||||
|
|
||||||
$platform->refresh();
|
|
||||||
$this->assertTrue($platform->is_active);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function test_channels_index_loads_successfully(): void
|
|
||||||
{
|
|
||||||
$response = $this->get('/channels');
|
|
||||||
$response->assertSuccessful();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function test_channels_create_loads_successfully(): void
|
|
||||||
{
|
|
||||||
$response = $this->get('/channels/create');
|
|
||||||
$response->assertSuccessful();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function test_channels_show_loads_successfully(): void
|
|
||||||
{
|
|
||||||
$channel = PlatformChannel::factory()->create();
|
|
||||||
|
|
||||||
$response = $this->get("/channels/{$channel->id}");
|
|
||||||
$response->assertSuccessful();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function test_channels_edit_loads_successfully(): void
|
|
||||||
{
|
|
||||||
$channel = PlatformChannel::factory()->create();
|
|
||||||
|
|
||||||
$response = $this->get("/channels/{$channel->id}/edit");
|
|
||||||
$response->assertSuccessful();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function test_channels_toggle_endpoint_works(): void
|
|
||||||
{
|
|
||||||
$channel = PlatformChannel::factory()->create(['is_active' => false]);
|
|
||||||
|
|
||||||
$response = $this->withoutMiddleware()
|
|
||||||
->post("/channels/{$channel->id}/toggle");
|
|
||||||
$response->assertRedirect();
|
|
||||||
|
|
||||||
$channel->refresh();
|
|
||||||
$this->assertTrue($channel->is_active);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function test_feeds_index_loads_successfully(): void
|
|
||||||
{
|
|
||||||
$response = $this->get('/feeds');
|
|
||||||
$response->assertSuccessful();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function test_feeds_create_loads_successfully(): void
|
|
||||||
{
|
|
||||||
$response = $this->get('/feeds/create');
|
|
||||||
$response->assertSuccessful();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function test_feeds_show_loads_successfully(): void
|
|
||||||
{
|
|
||||||
$feed = Feed::factory()->create();
|
|
||||||
|
|
||||||
$response = $this->get("/feeds/{$feed->id}");
|
|
||||||
$response->assertSuccessful();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function test_feeds_edit_loads_successfully(): void
|
|
||||||
{
|
|
||||||
$feed = Feed::factory()->create();
|
|
||||||
|
|
||||||
$response = $this->get("/feeds/{$feed->id}/edit");
|
|
||||||
$response->assertSuccessful();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function test_feeds_toggle_endpoint_works(): void
|
|
||||||
{
|
|
||||||
$feed = Feed::factory()->create(['is_active' => false]);
|
|
||||||
|
|
||||||
$response = $this->withoutMiddleware()
|
|
||||||
->post("/feeds/{$feed->id}/toggle");
|
|
||||||
$response->assertRedirect();
|
|
||||||
|
|
||||||
$feed->refresh();
|
|
||||||
$this->assertTrue($feed->is_active);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function test_routing_index_loads_successfully(): void
|
|
||||||
{
|
|
||||||
$response = $this->get('/routing');
|
|
||||||
$response->assertSuccessful();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function test_routing_create_loads_successfully(): void
|
|
||||||
{
|
|
||||||
$response = $this->get('/routing/create');
|
|
||||||
$response->assertSuccessful();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function test_routing_edit_loads_successfully(): void
|
|
||||||
{
|
|
||||||
$feed = Feed::factory()->create();
|
|
||||||
$channel = PlatformChannel::factory()->create();
|
|
||||||
Route::factory()->create([
|
|
||||||
'feed_id' => $feed->id,
|
|
||||||
'platform_channel_id' => $channel->id
|
|
||||||
]);
|
|
||||||
|
|
||||||
$response = $this->get("/routing/{$feed->id}/{$channel->id}/edit");
|
|
||||||
$response->assertSuccessful();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function test_routing_toggle_endpoint_works(): void
|
|
||||||
{
|
|
||||||
$feed = Feed::factory()->create();
|
|
||||||
$channel = PlatformChannel::factory()->create();
|
|
||||||
$route = Route::factory()->create([
|
|
||||||
'feed_id' => $feed->id,
|
|
||||||
'platform_channel_id' => $channel->id,
|
|
||||||
'is_active' => false
|
|
||||||
]);
|
|
||||||
|
|
||||||
$response = $this->withoutMiddleware()
|
|
||||||
->post("/routing/{$feed->id}/{$channel->id}/toggle");
|
|
||||||
$response->assertRedirect();
|
|
||||||
|
|
||||||
$route->refresh();
|
|
||||||
$this->assertTrue($route->is_active);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function test_all_get_routes_return_successful_status(): void
|
|
||||||
{
|
|
||||||
// Create necessary test data
|
|
||||||
$feed = Feed::factory()->create();
|
|
||||||
$channel = PlatformChannel::factory()->create();
|
|
||||||
$platform = PlatformAccount::factory()->create();
|
|
||||||
Route::factory()->create([
|
|
||||||
'feed_id' => $feed->id,
|
|
||||||
'platform_channel_id' => $channel->id
|
|
||||||
]);
|
|
||||||
|
|
||||||
$routes = [
|
|
||||||
'/',
|
|
||||||
'/onboarding/platform',
|
|
||||||
'/onboarding/feed',
|
|
||||||
'/onboarding/channel',
|
|
||||||
'/onboarding/complete',
|
|
||||||
'/articles',
|
|
||||||
'/logs',
|
|
||||||
'/settings',
|
|
||||||
'/platforms',
|
|
||||||
'/platforms/create',
|
|
||||||
"/platforms/{$platform->id}",
|
|
||||||
"/platforms/{$platform->id}/edit",
|
|
||||||
'/channels',
|
|
||||||
'/channels/create',
|
|
||||||
"/channels/{$channel->id}",
|
|
||||||
"/channels/{$channel->id}/edit",
|
|
||||||
'/feeds',
|
|
||||||
'/feeds/create',
|
|
||||||
"/feeds/{$feed->id}",
|
|
||||||
"/feeds/{$feed->id}/edit",
|
|
||||||
'/routing',
|
|
||||||
'/routing/create',
|
|
||||||
"/routing/{$feed->id}/{$channel->id}/edit",
|
|
||||||
];
|
|
||||||
|
|
||||||
foreach ($routes as $route) {
|
|
||||||
$response = $this->get($route);
|
|
||||||
$this->assertTrue(
|
|
||||||
$response->isSuccessful(),
|
|
||||||
"Route {$route} failed with status {$response->getStatusCode()}"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -218,14 +218,22 @@ public function test_platform_channel_post_model_creates_successfully(): void
|
||||||
|
|
||||||
public function test_keyword_model_creates_successfully(): void
|
public function test_keyword_model_creates_successfully(): void
|
||||||
{
|
{
|
||||||
$keyword = Keyword::factory()->create([
|
$feed = Feed::factory()->create();
|
||||||
|
$channel = PlatformChannel::factory()->create();
|
||||||
|
|
||||||
|
$keyword = Keyword::factory()
|
||||||
|
->forFeed($feed)
|
||||||
|
->forChannel($channel)
|
||||||
|
->create([
|
||||||
'keyword' => 'test keyword',
|
'keyword' => 'test keyword',
|
||||||
'is_blocked' => false
|
'is_active' => true
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$this->assertDatabaseHas('keywords', [
|
$this->assertDatabaseHas('keywords', [
|
||||||
'keyword' => 'test keyword',
|
'keyword' => 'test keyword',
|
||||||
'is_blocked' => false
|
'is_active' => true,
|
||||||
|
'feed_id' => $feed->id,
|
||||||
|
'platform_channel_id' => $channel->id
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,7 @@ public function test_article_discovery_for_feed_job_processes_feed(): void
|
||||||
$job = new ArticleDiscoveryForFeedJob($feed);
|
$job = new ArticleDiscoveryForFeedJob($feed);
|
||||||
|
|
||||||
// Mock the ArticleFetcher to return created articles
|
// Mock the ArticleFetcher to return created articles
|
||||||
$mockFetcher = \Mockery::mock('alias:' . \App\Services\Article\ArticleFetcher::class);
|
$mockFetcher = \Mockery::mock('overload:' . \App\Services\Article\ArticleFetcher::class);
|
||||||
$article1 = Article::factory()->create(['url' => 'https://example.com/article1', 'feed_id' => $feed->id]);
|
$article1 = Article::factory()->create(['url' => 'https://example.com/article1', 'feed_id' => $feed->id]);
|
||||||
$article2 = Article::factory()->create(['url' => 'https://example.com/article2', 'feed_id' => $feed->id]);
|
$article2 = Article::factory()->create(['url' => 'https://example.com/article2', 'feed_id' => $feed->id]);
|
||||||
$mockFetcher->shouldReceive('getArticlesFromFeed')
|
$mockFetcher->shouldReceive('getArticlesFromFeed')
|
||||||
|
|
@ -210,7 +210,8 @@ public function test_validate_article_listener_processes_new_article(): void
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Mock ArticleFetcher to return valid article data
|
// Mock ArticleFetcher to return valid article data
|
||||||
$mockFetcher = \Mockery::mock('alias:' . \App\Services\Article\ArticleFetcher::class);
|
$mockFetcher = \Mockery::mock('alias:ArticleFetcher2');
|
||||||
|
$this->app->instance(\App\Services\Article\ArticleFetcher::class, $mockFetcher);
|
||||||
$mockFetcher->shouldReceive('fetchArticleData')
|
$mockFetcher->shouldReceive('fetchArticleData')
|
||||||
->with($article)
|
->with($article)
|
||||||
->andReturn([
|
->andReturn([
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue