Add tests for featured thumbnail generation; apply Pint formatting and related edits

This commit is contained in:
2026-05-06 18:55:40 +02:00
parent 7a8bc8e22a
commit 82f2b1f660
65 changed files with 11325 additions and 49545 deletions

View File

@@ -35,13 +35,13 @@ Route::middleware(['web', 'throttle:60,1'])->prefix('leaderboard')->name('api.le
Route::get('worlds', [\App\Http\Controllers\Api\LeaderboardController::class, 'worlds'])->name('worlds');
});
Route::middleware(['web', 'auth', 'creator.access'])->prefix('stories')->name('api.stories.')->group(function () {
Route::middleware(['web', 'auth', 'verified', 'creator.access'])->prefix('stories')->name('api.stories.')->group(function () {
Route::post('create', [\App\Http\Controllers\StoryController::class, 'apiCreate'])->middleware('forum.bot.protection:api_write')->name('create');
Route::put('update', [\App\Http\Controllers\StoryController::class, 'apiUpdate'])->middleware('forum.bot.protection:api_write')->name('update');
Route::post('autosave', [\App\Http\Controllers\StoryController::class, 'apiAutosave'])->middleware('forum.bot.protection:api_write')->name('autosave');
});
Route::middleware(['web', 'auth', 'creator.access'])->prefix('story')->name('api.story.')->group(function () {
Route::middleware(['web', 'auth', 'verified', 'creator.access'])->prefix('story')->name('api.story.')->group(function () {
Route::post('upload-image', [\App\Http\Controllers\StoryController::class, 'apiUploadImage'])->middleware('forum.bot.protection:api_write')->name('upload-image');
Route::get('artworks', [\App\Http\Controllers\StoryController::class, 'apiArtworks'])->name('artworks');
});
@@ -141,7 +141,7 @@ Route::prefix('rank')->name('api.rank.')->middleware(['throttle:60,1'])->group(f
*/
// ── Studio Pro API (authenticated) ─────────────────────────────────────────────
Route::middleware(['web', 'auth'])->prefix('studio')->name('api.studio.')->group(function () {
Route::middleware(['web', 'auth', 'verified'])->prefix('studio')->name('api.studio.')->group(function () {
Route::post('events', [\App\Http\Controllers\Studio\StudioEventsApiController::class, 'store'])->name('events.store');
Route::get('upload-queue', [\App\Http\Controllers\Studio\StudioUploadQueueApiController::class, 'index'])->name('upload-queue.index');
Route::post('upload-queue/batches', [\App\Http\Controllers\Studio\StudioUploadQueueApiController::class, 'store'])->name('upload-queue.store');
@@ -150,6 +150,9 @@ Route::middleware(['web', 'auth'])->prefix('studio')->name('api.studio.')->group
Route::post('upload-queue/items/{id}/retry', [\App\Http\Controllers\Studio\StudioUploadQueueApiController::class, 'retry'])->whereNumber('id')->name('upload-queue.items.retry');
Route::post('news/media/upload', [\App\Http\Controllers\Studio\StudioNewsMediaApiController::class, 'store'])->middleware(['throttle:20,1', 'forum.bot.protection:api_write'])->name('news.media.upload');
Route::delete('news/media', [\App\Http\Controllers\Studio\StudioNewsMediaApiController::class, 'destroy'])->middleware(['throttle:20,1', 'forum.bot.protection:api_write'])->name('news.media.destroy');
Route::post('academy/lessons/media/upload', [\App\Http\Controllers\Settings\AcademyLessonMediaApiController::class, 'store'])->middleware(['throttle:20,1', 'forum.bot.protection:api_write'])->name('academy.lessons.media.upload');
Route::get('academy/lessons/media/assets', [\App\Http\Controllers\Settings\AcademyLessonMediaApiController::class, 'assets'])->middleware(['throttle:20,1', 'forum.bot.protection:api_write'])->name('academy.lessons.media.assets');
Route::delete('academy/lessons/media', [\App\Http\Controllers\Settings\AcademyLessonMediaApiController::class, 'destroy'])->middleware(['throttle:20,1', 'forum.bot.protection:api_write'])->name('academy.lessons.media.destroy');
Route::post('worlds/media/upload', [\App\Http\Controllers\Studio\StudioWorldMediaApiController::class, 'store'])->middleware(['throttle:20,1', 'forum.bot.protection:api_write'])->name('worlds.media.upload');
Route::delete('worlds/media', [\App\Http\Controllers\Studio\StudioWorldMediaApiController::class, 'destroy'])->middleware(['throttle:20,1', 'forum.bot.protection:api_write'])->name('worlds.media.destroy');
Route::put('preferences', [\App\Http\Controllers\Studio\StudioPreferencesApiController::class, 'updatePreferences'])->name('preferences.settings');
@@ -180,7 +183,11 @@ Route::middleware(['web', 'auth'])->prefix('studio')->name('api.studio.')->group
Route::get('tags/search', [\App\Http\Controllers\Studio\StudioArtworksApiController::class, 'searchTags'])->name('tags.search');
});
Route::middleware(['web', 'auth'])->prefix('cards')->name('api.cards.')->group(function () {
Route::middleware(['web', 'auth', 'verified'])->prefix('academy')->name('api.academy.')->group(function () {
Route::post('categories', [\App\Http\Controllers\Settings\AcademyAdminController::class, 'categoriesStoreJson'])->middleware(['throttle:20,1', 'forum.bot.protection:api_write'])->name('categories.store');
});
Route::middleware(['web', 'auth', 'verified'])->prefix('cards')->name('api.cards.')->group(function () {
Route::post('{id}/like', [\App\Http\Controllers\Api\NovaCards\NovaCardInteractionController::class, 'like'])
->middleware(['throttle:nova-cards-render', 'forum.bot.protection:api_write'])
->whereNumber('id')
@@ -650,7 +657,7 @@ Route::middleware(['web'])
Route::get('{id}/medal', [\App\Http\Controllers\Api\ArtworkAwardController::class, 'show'])->whereNumber('id')->name('show');
});
Route::middleware(['web', 'auth', 'normalize.username'])->group(function () {
Route::middleware(['web', 'auth', 'verified', 'normalize.username'])->group(function () {
Route::match(['post', 'delete'], 'like', [\App\Http\Controllers\Api\SocialCompatibilityController::class, 'like'])
->name('api.social.like');
@@ -706,7 +713,7 @@ Route::middleware(['web', 'throttle:60,1'])
->whereNumber('id')
->name('api.artworks.comments.index');
Route::middleware(['web', 'auth', 'normalize.username', 'throttle:20,1'])->group(function () {
Route::middleware(['web', 'auth', 'verified', 'normalize.username', 'throttle:20,1'])->group(function () {
Route::post('artworks/{id}/comments', [\App\Http\Controllers\Api\ArtworkCommentController::class, 'store'])
->whereNumber('id')
->name('api.artworks.comments.store');
@@ -725,7 +732,7 @@ Route::middleware(['web', 'throttle:60,1'])
->whereNumber('id')
->name('api.news.comments.index');
Route::middleware(['web', 'auth', 'normalize.username', 'throttle:20,1'])->group(function () {
Route::middleware(['web', 'auth', 'verified', 'normalize.username', 'throttle:20,1'])->group(function () {
Route::post('news/articles/{id}/comments', [\App\Http\Controllers\Api\NewsArticleCommentController::class, 'store'])
->whereNumber('id')
->name('api.news.comments.store');
@@ -755,7 +762,7 @@ Route::middleware(['web', 'throttle:reactions-read'])->group(function () {
->name('api.news.comments.reactions.index');
});
Route::middleware(['web', 'auth', 'normalize.username', 'throttle:reactions-write'])->group(function () {
Route::middleware(['web', 'auth', 'verified', 'normalize.username', 'throttle:reactions-write'])->group(function () {
Route::post('artworks/{id}/reactions', [\App\Http\Controllers\Api\ReactionController::class, 'toggleArtworkReaction'])
->whereNumber('id')
->name('api.artworks.reactions.toggle');
@@ -808,7 +815,7 @@ Route::middleware(['web'])
->name('following');
// Auth-required: follow / unfollow
Route::middleware(['auth', 'normalize.username', 'throttle:follow-write', 'forum.security.firewall:follow', 'forum.bot.protection:follow'])->group(function () {
Route::middleware(['auth', 'verified', 'normalize.username', 'throttle:follow-write', 'forum.security.firewall:follow', 'forum.bot.protection:follow'])->group(function () {
Route::post('{username}/follow', [\App\Http\Controllers\Api\FollowController::class, 'follow'])
->where('username', '[A-Za-z0-9_-]{3,20}')
->name('follow');
@@ -835,7 +842,7 @@ Route::middleware(['web'])
// POST /api/messages/{conversation_id}/{message_id}/react → add reaction
// DELETE /api/messages/{conversation_id}/{message_id}/react → remove reaction
// DELETE /api/messages/message/{message_id} → soft-delete message
Route::middleware(['web', 'auth', 'normalize.username', 'throttle:60,1'])
Route::middleware(['web', 'auth', 'verified', 'normalize.username', 'throttle:60,1'])
->prefix('messages')
->name('api.messages.')
->group(function () {
@@ -931,7 +938,7 @@ Route::middleware(['web', 'throttle:60,1'])
->name('comments.index');
});
Route::middleware(['web', 'auth', 'normalize.username'])
Route::middleware(['web', 'auth', 'verified', 'normalize.username'])
->prefix('posts')
->name('api.posts.')
->group(function () {
@@ -1021,7 +1028,7 @@ Route::middleware(['web', 'throttle:social-read'])
->name('index');
});
Route::middleware(['web', 'auth'])
Route::middleware(['web', 'auth', 'verified'])
->prefix('stories')
->name('api.stories.social.')
->group(function () {

View File

@@ -60,6 +60,7 @@ use App\Http\Controllers\User\MonthlyCommentatorsController;
use App\Http\Controllers\User\ProfileCollectionController;
use App\Http\Controllers\User\SavedCollectionController;
use App\Http\Controllers\User\CollectionSavedLibraryController;
use App\Http\Controllers\Moderation\Traffic\OnlineVisitorsController;
use App\Http\Controllers\Settings\CollectionAiController;
use App\Http\Controllers\Settings\CollectionInsightsController;
use App\Http\Controllers\Settings\CollectionManageController;
@@ -325,10 +326,10 @@ 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'])
Route::middleware(['auth', 'verified'])->post('{slug}/comments', [NewsArticleCommentController::class, 'store'])
->where('slug', '[a-z0-9\-]+')
->name('comments.store');
Route::middleware('auth')->delete('{slug}/comments/{comment}', [NewsArticleCommentController::class, 'destroy'])
Route::middleware(['auth', 'verified'])->delete('{slug}/comments/{comment}', [NewsArticleCommentController::class, 'destroy'])
->where('slug', '[a-z0-9\-]+')
->whereNumber('comment')
->name('comments.destroy');
@@ -400,7 +401,7 @@ Route::get('/groups/{group}/{section}', [\App\Http\Controllers\GroupController::
Route::get('/groups/{group}/posts/{post}', [\App\Http\Controllers\GroupPostController::class, 'show'])
->name('groups.posts.show');
Route::middleware('auth')->group(function () {
Route::middleware(['auth', 'verified'])->group(function () {
Route::post('/me/saved/collections/lists', [CollectionSavedLibraryController::class, 'storeList'])
->name('me.saved.collections.lists.store');
Route::get('/me/saved/collections/lists/{listSlug}', [\App\Http\Controllers\User\SavedCollectionController::class, 'showList'])
@@ -459,11 +460,11 @@ Route::get('/@{username}', [ProfileController::class, 'showByUsername'])
->where('username', '[A-Za-z0-9_-]{3,20}')
->name('profile.show');
Route::middleware('auth')->post('/@{username}/follow', [ProfileController::class, 'toggleFollow'])
Route::middleware(['auth', 'verified'])->post('/@{username}/follow', [ProfileController::class, 'toggleFollow'])
->where('username', '[A-Za-z0-9_-]{3,20}')
->name('profile.follow');
Route::middleware('auth')->post('/@{username}/comment', [ProfileController::class, 'storeComment'])
Route::middleware(['auth', 'verified'])->post('/@{username}/comment', [ProfileController::class, 'storeComment'])
->where('username', '[A-Za-z0-9_-]{3,20}')
->name('profile.comment');
@@ -507,7 +508,7 @@ Route::middleware(['auth'])->get('/dashboard/profile', [ProfileController::class
Route::middleware(['auth'])->get('/settings/profile', [ProfileController::class, 'editSettings'])->name('settings.profile');
// ── STUDIO Pro (/studio/*) ────────────────────────────────────────────────────
Route::middleware(['auth', 'ensure.onboarding.complete'])->prefix('studio')->name('studio.')->group(function () {
Route::middleware(['auth', 'verified', 'ensure.onboarding.complete'])->prefix('studio')->name('studio.')->group(function () {
Route::get('/', [StudioController::class, 'index'])->name('index');
Route::get('/content', [StudioController::class, 'content'])->name('content');
Route::get('/artworks', [StudioController::class, 'artworks'])->name('artworks');
@@ -708,7 +709,7 @@ Route::get('/internal/nova-cards/render-frame/{uuid}', [\App\Http\Controllers\In
->where('uuid', '[0-9a-f\-]{36}')
->name('nova-cards.render-frame');
Route::middleware('auth')->group(function () {
Route::middleware(['auth', 'verified'])->group(function () {
Route::post('/cards/{card}/comments', [\App\Http\Controllers\NovaCardCommentController::class, 'store'])
->whereNumber('card')
->name('cards.comments.store');
@@ -756,7 +757,7 @@ Route::middleware(['artwork.maturity.access'])->prefix('cp/ai-biography')->name(
});
// ── SETTINGS / PROFILE EDIT ───────────────────────────────────────────────────
Route::middleware(['auth', 'normalize.username', 'ensure.onboarding.complete'])->group(function () {
Route::middleware(['auth', 'verified', 'normalize.username', 'ensure.onboarding.complete'])->group(function () {
Route::get('/profile', fn () => redirect()->route('dashboard.profile', [], 301))->name('legacy.profile.redirect');
Route::get('/settings', fn () => redirect()->route('dashboard.profile', [], 302))->name('settings');
Route::get('/profile/edit', fn () => redirect()->route('dashboard.profile', [], 302))->name('profile.edit');
@@ -993,12 +994,12 @@ Route::view('/data-deletion', 'privacy.data-deletion')->name('privacy.data_delet
Route::view('/blank', 'blank')->name('blank');
// ── MESSAGES ──────────────────────────────────────────────────────────────────
Route::middleware(['auth', 'ensure.onboarding.complete'])
Route::middleware(['auth', 'verified', 'ensure.onboarding.complete'])
->get('/messages/attachments/{id}', [\App\Http\Controllers\Api\Messaging\AttachmentController::class, 'show'])
->whereNumber('id')
->name('messages.attachments.show');
Route::middleware(['auth', 'ensure.onboarding.complete'])->prefix('messages')->name('messages.')->group(function () {
Route::middleware(['auth', 'verified', 'ensure.onboarding.complete'])->prefix('messages')->name('messages.')->group(function () {
Route::get('/', [\App\Http\Controllers\Messaging\MessagesPageController::class, 'index'])->name('index');
Route::get('/{id}', [\App\Http\Controllers\Messaging\MessagesPageController::class, 'show'])->whereNumber('id')->name('show');
});
@@ -1018,6 +1019,17 @@ Route::middleware(['auth'])
})->where('path', '.*');
});
Route::middleware(['auth', 'admin.access'])
->prefix('moderation')
->name('moderation.')
->group(function (): void {
Route::get('/traffic/online', [OnlineVisitorsController::class, 'index'])
->name('traffic.online');
Route::get('/traffic/online/data', [OnlineVisitorsController::class, 'data'])
->name('traffic.online.data');
});
// ── ADMIN PANEL ───────────────────────────────────────────────────────────────
Route::middleware(['auth', 'admin.access'])
->prefix('moderation')