Wire admin studio SSR and search infrastructure

This commit is contained in:
2026-05-01 11:46:06 +02:00
parent 257b0dbef6
commit 18cea8b0f0
329 changed files with 197465 additions and 2741 deletions

View File

@@ -29,6 +29,7 @@ use App\Http\Controllers\Web\RssFeedController;
use App\Http\Controllers\Web\ApplicationController;
use App\Http\Controllers\Web\CategoryController;
use App\Http\Controllers\News\NewsController as FrontendNewsController;
use App\Http\Controllers\News\NewsArticleCommentController;
use App\Http\Controllers\News\NewsRssController;
use App\Http\Controllers\RSS\GlobalFeedController;
use App\Http\Controllers\RSS\DiscoverFeedController;
@@ -36,6 +37,7 @@ use App\Http\Controllers\RSS\ExploreFeedController;
use App\Http\Controllers\RSS\TagFeedController;
use App\Http\Controllers\RSS\CreatorFeedController;
use App\Http\Controllers\RSS\BlogFeedController;
use App\Http\Controllers\Admin\AdminController;
use App\Http\Controllers\Studio\StudioNewsController;
use App\Http\Controllers\Studio\StudioController;
use App\Http\Controllers\Studio\StudioWorldController;
@@ -89,6 +91,8 @@ Route::prefix('discover')->name('discover.')->group(function () {
// ── EXPLORE (/explore/*) ──────────────────────────────────────────────────────
Route::prefix('explore')->name('explore.')->group(function () {
Route::get('/', [ExploreController::class, 'index'])->name('index');
Route::get('/best', [ExploreController::class, 'hallOfFame'])->name('best');
Route::get('/top-rated', fn () => redirect()->route('explore.index', array_merge(request()->query(), ['sort' => 'top-rated']), 301))->name('top-rated');
Route::get('/members', fn () => redirect()->route('creators.top', request()->query(), 301))->name('members.redirect');
Route::get('/memebers', fn () => redirect()->route('creators.top', request()->query(), 301))->name('memebers.redirect');
Route::get('/{type}', [ExploreController::class, 'byType'])
@@ -272,6 +276,13 @@ Route::prefix('news')->name('news.')->group(function () {
->name('author');
Route::get('category/{slug}', [FrontendNewsController::class, 'category'])->name('category');
Route::get('tag/{slug}', [FrontendNewsController::class, 'tag'])->name('tag');
Route::middleware('auth')->post('{slug}/comments', [NewsArticleCommentController::class, 'store'])
->where('slug', '[a-z0-9\-]+')
->name('comments.store');
Route::middleware('auth')->delete('{slug}/comments/{comment}', [NewsArticleCommentController::class, 'destroy'])
->where('slug', '[a-z0-9\-]+')
->whereNumber('comment')
->name('comments.destroy');
Route::get('{slug}', [FrontendNewsController::class, 'show'])
->where('slug', '[a-z0-9\-]+')
->name('show');
@@ -297,7 +308,11 @@ Route::get('/worlds/create', function (Request $request) {
return redirect()->route('worlds.index', $request->query(), 302);
})
->name('worlds.create.redirect');
Route::get('/worlds/{world:slug}', [WorldController::class, 'show'])
Route::get('/worlds/{world}/{year}', [WorldController::class, 'showEdition'])
->where('world', '^(?!create$)[a-z0-9]+(?:-[a-z0-9]+)*$')
->whereNumber('year')
->name('worlds.editions.show');
Route::get('/worlds/{world}', [WorldController::class, 'show'])
->where('world', '^(?!create$)[a-z0-9]+(?:-[a-z0-9]+)*$')
->name('worlds.show');
Route::get('/collections/editorial', [\App\Http\Controllers\Web\CollectionDiscoveryController::class, 'editorial'])
@@ -388,7 +403,7 @@ Route::get('/@{username}/collections/{slug}', [ProfileCollectionController::clas
Route::get('/@{username}/{tab}', [ProfileController::class, 'showTabByUsername'])
->where('username', '[A-Za-z0-9_-]{3,20}')
->where('tab', 'posts|artworks|stories|achievements|collections|about|stats|favourites|activity')
->where('tab', 'posts|artworks|stories|achievements|worlds|collections|about|stats|favourites|activity')
->name('profile.tab');
Route::get('/@{username}', [ProfileController::class, 'showByUsername'])
@@ -448,6 +463,7 @@ Route::middleware(['auth', 'ensure.onboarding.complete'])->prefix('studio')->nam
Route::get('/content', [StudioController::class, 'content'])->name('content');
Route::get('/artworks', [StudioController::class, 'artworks'])->name('artworks');
Route::get('/drafts', [StudioController::class, 'drafts'])->name('drafts');
Route::get('/upload-queue', [StudioController::class, 'uploadQueue'])->name('upload-queue');
Route::get('/scheduled', [StudioController::class, 'scheduled'])->name('scheduled');
Route::get('/calendar', [StudioController::class, 'calendar'])->name('calendar');
Route::get('/archived', [StudioController::class, 'archived'])->name('archived');
@@ -458,6 +474,8 @@ Route::middleware(['auth', 'ensure.onboarding.complete'])->prefix('studio')->nam
Route::get('/analytics', [StudioController::class, 'analyticsOverview'])->name('analytics');
Route::get('/collections', [StudioController::class, 'collections'])->name('collections');
Route::get('/stories', [StudioController::class, 'stories'])->name('stories');
Route::get('/stories/create', [StoryController::class, 'create'])->middleware('creator.access')->name('stories.create');
Route::get('/stories/{story}/edit', [StoryController::class, 'edit'])->middleware('creator.access')->name('stories.edit');
Route::get('/assets', [StudioController::class, 'assets'])->name('assets');
Route::get('/comments', [StudioController::class, 'comments'])->name('comments');
Route::get('/activity', [StudioController::class, 'activity'])->name('activity');
@@ -489,6 +507,7 @@ Route::middleware(['auth', 'ensure.onboarding.complete'])->prefix('studio')->nam
Route::get('/news/{article}/preview', [StudioNewsController::class, 'preview'])->whereNumber('article')->name('news.preview');
Route::get('/news/{article}/edit', [StudioNewsController::class, 'edit'])->whereNumber('article')->name('news.edit');
Route::patch('/news/{article}', [StudioNewsController::class, 'update'])->whereNumber('article')->name('news.update');
Route::delete('/news/{article}', [StudioNewsController::class, 'destroy'])->whereNumber('article')->name('news.destroy');
Route::post('/news/{article}/publish', [StudioNewsController::class, 'publish'])->whereNumber('article')->name('news.publish');
Route::post('/news/{article}/archive', [StudioNewsController::class, 'archive'])->whereNumber('article')->name('news.archive');
Route::post('/news/{article}/feature', [StudioNewsController::class, 'feature'])->whereNumber('article')->name('news.feature');
@@ -500,6 +519,11 @@ Route::middleware(['auth', 'ensure.onboarding.complete'])->prefix('studio')->nam
Route::get('/worlds/{world}/preview', [StudioWorldController::class, 'preview'])->whereNumber('world')->name('worlds.preview');
Route::get('/worlds/{world}/edit', [StudioWorldController::class, 'edit'])->whereNumber('world')->name('worlds.edit');
Route::patch('/worlds/{world}', [StudioWorldController::class, 'update'])->whereNumber('world')->name('worlds.update');
Route::post('/worlds/{world}/suggestions/add', [StudioWorldController::class, 'addSuggestion'])->whereNumber('world')->name('worlds.suggestions.add');
Route::post('/worlds/{world}/suggestions/pin', [StudioWorldController::class, 'pinSuggestion'])->whereNumber('world')->name('worlds.suggestions.pin');
Route::post('/worlds/{world}/suggestions/dismiss', [StudioWorldController::class, 'dismissSuggestion'])->whereNumber('world')->name('worlds.suggestions.dismiss');
Route::post('/worlds/{world}/suggestions/not-relevant', [StudioWorldController::class, 'markSuggestionNotRelevant'])->whereNumber('world')->name('worlds.suggestions.not-relevant');
Route::post('/worlds/{world}/suggestions/restore', [StudioWorldController::class, 'restoreSuggestion'])->whereNumber('world')->name('worlds.suggestions.restore');
Route::post('/worlds/{world}/submissions/{submission}/approve', [StudioWorldController::class, 'approveSubmission'])->whereNumber('world')->whereNumber('submission')->name('worlds.submissions.approve');
Route::post('/worlds/{world}/submissions/{submission}/remove', [StudioWorldController::class, 'removeSubmission'])->whereNumber('world')->whereNumber('submission')->name('worlds.submissions.remove');
Route::post('/worlds/{world}/submissions/{submission}/block', [StudioWorldController::class, 'blockSubmission'])->whereNumber('world')->whereNumber('submission')->name('worlds.submissions.block');
@@ -508,7 +532,10 @@ Route::middleware(['auth', 'ensure.onboarding.complete'])->prefix('studio')->nam
Route::post('/worlds/{world}/submissions/{submission}/feature', [StudioWorldController::class, 'featureSubmission'])->whereNumber('world')->whereNumber('submission')->name('worlds.submissions.feature');
Route::post('/worlds/{world}/submissions/{submission}/unfeature', [StudioWorldController::class, 'unfeatureSubmission'])->whereNumber('world')->whereNumber('submission')->name('worlds.submissions.unfeature');
Route::post('/worlds/{world}/submissions/{submission}/pending', [StudioWorldController::class, 'pendingSubmission'])->whereNumber('world')->whereNumber('submission')->name('worlds.submissions.pending');
Route::post('/worlds/{world}/submissions/{submission}/rewards/{rewardType}', [StudioWorldController::class, 'grantSubmissionReward'])->whereNumber('world')->whereNumber('submission')->name('worlds.submissions.rewards.grant');
Route::post('/worlds/{world}/submissions/{submission}/rewards/{rewardType}/revoke', [StudioWorldController::class, 'revokeSubmissionReward'])->whereNumber('world')->whereNumber('submission')->name('worlds.submissions.rewards.revoke');
Route::post('/worlds/{world}/publish', [StudioWorldController::class, 'publish'])->whereNumber('world')->name('worlds.publish');
Route::post('/worlds/{world}/recap/publish', [StudioWorldController::class, 'publishRecap'])->whereNumber('world')->name('worlds.recap.publish');
Route::post('/worlds/{world}/archive', [StudioWorldController::class, 'archive'])->whereNumber('world')->name('worlds.archive');
Route::post('/worlds/{world}/duplicate', [StudioWorldController::class, 'duplicate'])->whereNumber('world')->name('worlds.duplicate');
Route::post('/worlds/{world}/new-edition', [StudioWorldController::class, 'newEdition'])->whereNumber('world')->name('worlds.new-edition');
@@ -927,9 +954,71 @@ Route::middleware(['auth', 'ensure.onboarding.complete'])->prefix('messages')->n
Route::get('/{id}', [\App\Http\Controllers\Messaging\MessagesPageController::class, 'show'])->whereNumber('id')->name('show');
});
Route::middleware(['auth', 'admin.moderation'])->get('/admin/usernames/moderation', function () {
return Inertia::render('Admin/UsernameQueue');
})->name('admin.usernames.moderation');
Route::middleware(['auth'])
->prefix('admin')
->group(function () {
Route::any('/{path?}', function (?string $path = null) {
$target = '/moderation' . ($path ? '/' . $path : '');
$query = request()->getQueryString();
if ($query) {
$target .= '?' . $query;
}
return redirect($target, 302);
})->where('path', '.*');
});
// ── ADMIN PANEL ───────────────────────────────────────────────────────────────
Route::middleware(['auth', 'admin.access'])
->prefix('moderation')
->name('admin.')
->group(function () {
Route::get('/', [AdminController::class, 'dashboard'])->name('dashboard');
Route::get('/users', [AdminController::class, 'users'])->name('users');
Route::patch('/users/{user}/role', [AdminController::class, 'updateRole'])->name('users.role');
Route::get('/stories', [AdminController::class, 'stories'])->name('stories');
Route::get('/artworks', [AdminController::class, 'artworks'])->name('artworks');
Route::get('/usernames/moderation', [AdminController::class, 'usernameQueue'])->name('usernames');
Route::get('/uploads', [AdminController::class, 'uploadQueue'])->name('uploads');
Route::get('/settings', [AdminController::class, 'settings'])->name('settings');
Route::middleware(['artwork.maturity.access'])
->prefix('ai-biography')
->name('cp.ai-biography.')
->group(function () {
Route::get('/', [\App\Http\Controllers\Settings\AiBiographyAdminController::class, 'index'])->name('index');
Route::post('/users/{user}/rebuild', [\App\Http\Controllers\Settings\AiBiographyAdminController::class, 'rebuild'])->whereNumber('user')->name('rebuild');
Route::post('/records/{biography}/approve', [\App\Http\Controllers\Settings\AiBiographyAdminController::class, 'approve'])->whereNumber('biography')->name('approve');
Route::post('/records/{biography}/flag', [\App\Http\Controllers\Settings\AiBiographyAdminController::class, 'flag'])->whereNumber('biography')->name('flag');
Route::post('/records/{biography}/hide', [\App\Http\Controllers\Settings\AiBiographyAdminController::class, 'hide'])->whereNumber('biography')->name('hide');
Route::post('/records/{biography}/show', [\App\Http\Controllers\Settings\AiBiographyAdminController::class, 'show'])->whereNumber('biography')->name('show');
});
Route::prefix('artworks/featured')
->name('artworks.featured.')
->group(function () {
Route::get('/', [\App\Http\Controllers\Settings\FeaturedArtworkAdminController::class, 'index'])->name('main');
Route::get('/search', [\App\Http\Controllers\Settings\FeaturedArtworkAdminController::class, 'search'])->name('search');
Route::post('/', [\App\Http\Controllers\Settings\FeaturedArtworkAdminController::class, 'store'])->name('store');
Route::match(['put', 'patch'], '/{feature}', [\App\Http\Controllers\Settings\FeaturedArtworkAdminController::class, 'update'])->whereNumber('feature')->name('update');
Route::post('/{feature}/toggle', [\App\Http\Controllers\Settings\FeaturedArtworkAdminController::class, 'toggle'])->whereNumber('feature')->name('toggle');
Route::post('/{feature}/force-hero', [\App\Http\Controllers\Settings\FeaturedArtworkAdminController::class, 'toggleForceHero'])->whereNumber('feature')->name('force-hero');
Route::delete('/{feature}', [\App\Http\Controllers\Settings\FeaturedArtworkAdminController::class, 'destroy'])->whereNumber('feature')->name('delete');
});
Route::prefix('homepage/announcements')
->name('homepage-announcements.')
->group(function () {
Route::get('/', [\App\Http\Controllers\Settings\HomepageAnnouncementController::class, 'index'])->name('index');
Route::get('/create', [\App\Http\Controllers\Settings\HomepageAnnouncementController::class, 'create'])->name('create');
Route::post('/', [\App\Http\Controllers\Settings\HomepageAnnouncementController::class, 'store'])->name('store');
Route::post('/preview', [\App\Http\Controllers\Settings\HomepageAnnouncementController::class, 'preview'])->name('preview');
Route::get('/{homepageAnnouncement}/edit', [\App\Http\Controllers\Settings\HomepageAnnouncementController::class, 'edit'])->whereNumber('homepageAnnouncement')->name('edit');
Route::match(['put', 'patch'], '/{homepageAnnouncement}', [\App\Http\Controllers\Settings\HomepageAnnouncementController::class, 'update'])->whereNumber('homepageAnnouncement')->name('update');
Route::delete('/{homepageAnnouncement}', [\App\Http\Controllers\Settings\HomepageAnnouncementController::class, 'destroy'])->whereNumber('homepageAnnouncement')->name('destroy');
});
});
// ── COMMUNITY ACTIVITY ────────────────────────────────────────────────────────