optimizations
This commit is contained in:
261
routes/api.php
261
routes/api.php
@@ -55,6 +55,11 @@ Route::middleware(['web', 'throttle:300,1'])
|
||||
->whereNumber('id')
|
||||
->name('api.art.similar');
|
||||
|
||||
Route::middleware(['web', 'throttle:120,1'])
|
||||
->get('art/{id}/similar-ai', \App\Http\Controllers\Api\SimilarAiArtworksController::class)
|
||||
->whereNumber('id')
|
||||
->name('api.art.similar-ai');
|
||||
|
||||
Route::middleware(['web', 'throttle:5,10'])
|
||||
->post('art/{id}/view', \App\Http\Controllers\Api\ArtworkViewController::class)
|
||||
->middleware('forum.bot.protection:api_write')
|
||||
@@ -75,6 +80,11 @@ Route::middleware(['web', 'throttle:social-read'])
|
||||
->get('activity', [\App\Http\Controllers\Api\SocialActivityController::class, 'index'])
|
||||
->name('api.activity');
|
||||
|
||||
Route::middleware(['web', 'throttle:social-read'])
|
||||
->get('profile/{username}/activity', \App\Http\Controllers\Api\ProfileActivityController::class)
|
||||
->where('username', '[A-Za-z0-9_-]{3,20}')
|
||||
->name('api.profile.activity');
|
||||
|
||||
Route::middleware(['web', 'throttle:social-read'])
|
||||
->get('comments', [\App\Http\Controllers\Api\SocialCompatibilityController::class, 'comments'])
|
||||
->name('api.social.comments.index');
|
||||
@@ -110,6 +120,11 @@ Route::middleware(['web', 'auth'])->prefix('studio')->name('api.studio.')->group
|
||||
Route::put('artworks/{id}', [\App\Http\Controllers\Studio\StudioArtworksApiController::class, 'update'])->whereNumber('id')->name('artworks.update');
|
||||
Route::post('artworks/{id}/toggle', [\App\Http\Controllers\Studio\StudioArtworksApiController::class, 'toggle'])->whereNumber('id')->name('artworks.toggle');
|
||||
Route::get('artworks/{id}/analytics', [\App\Http\Controllers\Studio\StudioArtworksApiController::class, 'analytics'])->whereNumber('id')->name('artworks.analytics');
|
||||
Route::get('artworks/{id}/ai', [\App\Http\Controllers\Studio\StudioArtworkAiAssistApiController::class, 'show'])->whereNumber('id')->name('artworks.ai.show');
|
||||
Route::post('artworks/{id}/ai/analyze', [\App\Http\Controllers\Studio\StudioArtworkAiAssistApiController::class, 'analyze'])->whereNumber('id')->name('artworks.ai.analyze');
|
||||
Route::post('artworks/{id}/ai/apply', [\App\Http\Controllers\Studio\StudioArtworkAiAssistApiController::class, 'apply'])->whereNumber('id')->name('artworks.ai.apply');
|
||||
Route::post('artworks/{id}/ai/events', [\App\Http\Controllers\Studio\StudioArtworkAiAssistApiController::class, 'event'])->whereNumber('id')->name('artworks.ai.events');
|
||||
Route::post('artworks/{id}/ai/regenerate', [\App\Http\Controllers\Studio\StudioArtworkAiAssistApiController::class, 'regenerate'])->whereNumber('id')->name('artworks.ai.regenerate');
|
||||
Route::post('artworks/{id}/replace-file', [\App\Http\Controllers\Studio\StudioArtworksApiController::class, 'replaceFile'])->whereNumber('id')->name('artworks.replaceFile');
|
||||
// Versioning
|
||||
Route::get('artworks/{id}/versions', [\App\Http\Controllers\Studio\StudioArtworksApiController::class, 'versions'])->whereNumber('id')->name('artworks.versions');
|
||||
@@ -117,6 +132,192 @@ 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::post('{id}/like', [\App\Http\Controllers\Api\NovaCards\NovaCardInteractionController::class, 'like'])
|
||||
->middleware(['throttle:nova-cards-render', 'forum.bot.protection:api_write'])
|
||||
->whereNumber('id')
|
||||
->name('like.alias.store');
|
||||
Route::delete('{id}/like', [\App\Http\Controllers\Api\NovaCards\NovaCardInteractionController::class, 'unlike'])
|
||||
->whereNumber('id')
|
||||
->name('like.alias.destroy');
|
||||
Route::post('{id}/save', [\App\Http\Controllers\Api\NovaCards\NovaCardInteractionController::class, 'save'])
|
||||
->middleware(['throttle:nova-cards-render', 'forum.bot.protection:api_write'])
|
||||
->whereNumber('id')
|
||||
->name('save.alias.store');
|
||||
Route::post('{id}/remix', [\App\Http\Controllers\Api\NovaCards\NovaCardInteractionController::class, 'remix'])
|
||||
->middleware(['throttle:nova-cards-publish', 'forum.bot.protection:api_write'])
|
||||
->whereNumber('id')
|
||||
->name('remix.alias.store');
|
||||
Route::post('drafts', [\App\Http\Controllers\Api\NovaCards\NovaCardDraftController::class, 'store'])
|
||||
->middleware(['throttle:nova-cards-drafts', 'forum.bot.protection:api_write'])
|
||||
->name('drafts.store');
|
||||
Route::get('drafts/{id}', [\App\Http\Controllers\Api\NovaCards\NovaCardDraftController::class, 'show'])
|
||||
->whereNumber('id')
|
||||
->name('drafts.show');
|
||||
Route::patch('drafts/{id}', [\App\Http\Controllers\Api\NovaCards\NovaCardDraftController::class, 'update'])
|
||||
->middleware(['throttle:nova-cards-autosave', 'forum.bot.protection:api_write'])
|
||||
->whereNumber('id')
|
||||
->name('drafts.update');
|
||||
Route::post('drafts/{id}/autosave', [\App\Http\Controllers\Api\NovaCards\NovaCardDraftController::class, 'autosave'])
|
||||
->middleware(['throttle:nova-cards-autosave', 'forum.bot.protection:api_write'])
|
||||
->whereNumber('id')
|
||||
->name('drafts.autosave');
|
||||
Route::post('drafts/{id}/background', [\App\Http\Controllers\Api\NovaCards\NovaCardDraftController::class, 'background'])
|
||||
->middleware(['throttle:nova-cards-background-upload', 'forum.bot.protection:api_write'])
|
||||
->whereNumber('id')
|
||||
->name('drafts.background');
|
||||
Route::post('drafts/{id}/render', [\App\Http\Controllers\Api\NovaCards\NovaCardDraftController::class, 'render'])
|
||||
->middleware(['throttle:nova-cards-render', 'forum.bot.protection:api_write'])
|
||||
->whereNumber('id')
|
||||
->name('drafts.render');
|
||||
Route::post('drafts/{id}/publish', [\App\Http\Controllers\Api\NovaCards\NovaCardDraftController::class, 'publish'])
|
||||
->middleware(['throttle:nova-cards-publish', 'forum.bot.protection:api_write'])
|
||||
->whereNumber('id')
|
||||
->name('drafts.publish');
|
||||
Route::delete('drafts/{id}', [\App\Http\Controllers\Api\NovaCards\NovaCardDraftController::class, 'destroy'])
|
||||
->whereNumber('id')
|
||||
->name('drafts.destroy');
|
||||
Route::get('collections', [\App\Http\Controllers\Api\NovaCards\NovaCardInteractionController::class, 'collections'])
|
||||
->name('collections.index');
|
||||
Route::post('collections', [\App\Http\Controllers\Api\NovaCards\NovaCardInteractionController::class, 'storeCollection'])
|
||||
->middleware(['throttle:nova-cards-drafts', 'forum.bot.protection:api_write'])
|
||||
->name('collections.store');
|
||||
Route::patch('collections/{id}', [\App\Http\Controllers\Api\NovaCards\NovaCardInteractionController::class, 'updateCollection'])
|
||||
->middleware(['throttle:nova-cards-drafts', 'forum.bot.protection:api_write'])
|
||||
->whereNumber('id')
|
||||
->name('collections.update');
|
||||
Route::post('collections/{id}/items', [\App\Http\Controllers\Api\NovaCards\NovaCardInteractionController::class, 'storeCollectionItem'])
|
||||
->middleware(['throttle:nova-cards-drafts', 'forum.bot.protection:api_write'])
|
||||
->whereNumber('id')
|
||||
->name('collections.items.store');
|
||||
Route::delete('collections/{id}/items/{cardId}', [\App\Http\Controllers\Api\NovaCards\NovaCardInteractionController::class, 'destroyCollectionItem'])
|
||||
->whereNumber('id')
|
||||
->whereNumber('cardId')
|
||||
->name('collections.items.destroy');
|
||||
Route::get('challenges', [\App\Http\Controllers\Api\NovaCards\NovaCardInteractionController::class, 'challenges'])
|
||||
->name('challenges.index');
|
||||
Route::get('assets', [\App\Http\Controllers\Api\NovaCards\NovaCardInteractionController::class, 'assets'])
|
||||
->name('assets.index');
|
||||
Route::get('templates', [\App\Http\Controllers\Api\NovaCards\NovaCardInteractionController::class, 'templates'])
|
||||
->name('templates.index');
|
||||
Route::post('like/{id}', [\App\Http\Controllers\Api\NovaCards\NovaCardInteractionController::class, 'like'])
|
||||
->middleware(['throttle:nova-cards-render', 'forum.bot.protection:api_write'])
|
||||
->whereNumber('id')
|
||||
->name('like');
|
||||
Route::delete('like/{id}', [\App\Http\Controllers\Api\NovaCards\NovaCardInteractionController::class, 'unlike'])
|
||||
->whereNumber('id')
|
||||
->name('unlike');
|
||||
Route::post('favorite/{id}', [\App\Http\Controllers\Api\NovaCards\NovaCardInteractionController::class, 'favorite'])
|
||||
->middleware(['throttle:nova-cards-render', 'forum.bot.protection:api_write'])
|
||||
->whereNumber('id')
|
||||
->name('favorite');
|
||||
Route::delete('favorite/{id}', [\App\Http\Controllers\Api\NovaCards\NovaCardInteractionController::class, 'unfavorite'])
|
||||
->whereNumber('id')
|
||||
->name('unfavorite');
|
||||
Route::post('save/{id}', [\App\Http\Controllers\Api\NovaCards\NovaCardInteractionController::class, 'save'])
|
||||
->middleware(['throttle:nova-cards-render', 'forum.bot.protection:api_write'])
|
||||
->whereNumber('id')
|
||||
->name('save');
|
||||
Route::delete('save/{id}', [\App\Http\Controllers\Api\NovaCards\NovaCardInteractionController::class, 'unsave'])
|
||||
->whereNumber('id')
|
||||
->name('unsave');
|
||||
Route::post('remix/{id}', [\App\Http\Controllers\Api\NovaCards\NovaCardInteractionController::class, 'remix'])
|
||||
->middleware(['throttle:nova-cards-publish', 'forum.bot.protection:api_write'])
|
||||
->whereNumber('id')
|
||||
->name('remix');
|
||||
Route::post('duplicate/{id}', [\App\Http\Controllers\Api\NovaCards\NovaCardInteractionController::class, 'duplicate'])
|
||||
->middleware(['throttle:nova-cards-drafts', 'forum.bot.protection:api_write'])
|
||||
->whereNumber('id')
|
||||
->name('duplicate');
|
||||
Route::get('drafts/{id}/versions', [\App\Http\Controllers\Api\NovaCards\NovaCardInteractionController::class, 'versions'])
|
||||
->whereNumber('id')
|
||||
->name('drafts.versions');
|
||||
Route::post('drafts/{id}/restore/{versionId}', [\App\Http\Controllers\Api\NovaCards\NovaCardInteractionController::class, 'restoreVersion'])
|
||||
->middleware(['throttle:nova-cards-autosave', 'forum.bot.protection:api_write'])
|
||||
->whereNumber('id')
|
||||
->whereNumber('versionId')
|
||||
->name('drafts.restore');
|
||||
Route::post('drafts/{id}/restore-version/{versionId}', [\App\Http\Controllers\Api\NovaCards\NovaCardInteractionController::class, 'restoreVersion'])
|
||||
->middleware(['throttle:nova-cards-autosave', 'forum.bot.protection:api_write'])
|
||||
->whereNumber('id')
|
||||
->whereNumber('versionId')
|
||||
->name('drafts.restore-version');
|
||||
Route::post('challenges/{challengeId}/submit/{id}', [\App\Http\Controllers\Api\NovaCards\NovaCardInteractionController::class, 'submitChallenge'])
|
||||
->middleware(['throttle:nova-cards-publish', 'forum.bot.protection:api_write'])
|
||||
->whereNumber('challengeId')
|
||||
->whereNumber('id')
|
||||
->name('challenges.submit');
|
||||
Route::post('challenges/{id}/submit', [\App\Http\Controllers\Api\NovaCards\NovaCardInteractionController::class, 'submitChallengeByChallenge'])
|
||||
->middleware(['throttle:nova-cards-publish', 'forum.bot.protection:api_write'])
|
||||
->whereNumber('id')
|
||||
->name('challenges.submit.alias');
|
||||
Route::post('challenges/{id}/submit-card', [\App\Http\Controllers\Api\NovaCards\NovaCardInteractionController::class, 'submitChallengeByChallenge'])
|
||||
->middleware(['throttle:nova-cards-publish', 'forum.bot.protection:api_write'])
|
||||
->whereNumber('id')
|
||||
->name('challenges.submit-card');
|
||||
|
||||
// v3: Creator presets
|
||||
Route::get('presets', [\App\Http\Controllers\Api\NovaCards\NovaCardPresetController::class, 'index'])
|
||||
->name('presets.index');
|
||||
Route::post('presets', [\App\Http\Controllers\Api\NovaCards\NovaCardPresetController::class, 'store'])
|
||||
->middleware(['throttle:nova-cards-drafts', 'forum.bot.protection:api_write'])
|
||||
->name('presets.store');
|
||||
Route::patch('presets/{id}', [\App\Http\Controllers\Api\NovaCards\NovaCardPresetController::class, 'update'])
|
||||
->middleware(['throttle:nova-cards-drafts', 'forum.bot.protection:api_write'])
|
||||
->whereNumber('id')
|
||||
->name('presets.update');
|
||||
Route::delete('presets/{id}', [\App\Http\Controllers\Api\NovaCards\NovaCardPresetController::class, 'destroy'])
|
||||
->whereNumber('id')
|
||||
->name('presets.destroy');
|
||||
Route::post('presets/capture/{cardId}', [\App\Http\Controllers\Api\NovaCards\NovaCardPresetController::class, 'captureFromCard'])
|
||||
->middleware(['throttle:nova-cards-drafts', 'forum.bot.protection:api_write'])
|
||||
->whereNumber('cardId')
|
||||
->name('presets.capture');
|
||||
Route::get('presets/{presetId}/apply/{cardId}', [\App\Http\Controllers\Api\NovaCards\NovaCardPresetController::class, 'applyToCard'])
|
||||
->whereNumber('presetId')
|
||||
->whereNumber('cardId')
|
||||
->name('presets.apply');
|
||||
|
||||
// v3: Export requests
|
||||
Route::post('{id}/export', [\App\Http\Controllers\Api\NovaCards\NovaCardExportController::class, 'store'])
|
||||
->middleware(['throttle:nova-cards-render', 'forum.bot.protection:api_write'])
|
||||
->whereNumber('id')
|
||||
->name('export.store');
|
||||
Route::get('exports/{exportId}', [\App\Http\Controllers\Api\NovaCards\NovaCardExportController::class, 'show'])
|
||||
->whereNumber('exportId')
|
||||
->name('exports.show');
|
||||
|
||||
// v3: AI-assist suggestions
|
||||
Route::get('{id}/ai-suggest', [\App\Http\Controllers\Api\NovaCards\NovaCardDiscoveryController::class, 'suggest'])
|
||||
->middleware(['throttle:nova-cards-render'])
|
||||
->whereNumber('id')
|
||||
->name('ai-suggest');
|
||||
});
|
||||
|
||||
Route::middleware(['web', 'throttle:60,1'])->prefix('cards')->name('api.cards.')->group(function () {
|
||||
Route::get('{id}/lineage', [\App\Http\Controllers\Api\NovaCards\NovaCardInteractionController::class, 'lineage'])
|
||||
->whereNumber('id')
|
||||
->name('lineage.alias');
|
||||
Route::get('lineage/{id}', [\App\Http\Controllers\Api\NovaCards\NovaCardInteractionController::class, 'lineage'])
|
||||
->whereNumber('id')
|
||||
->name('lineage');
|
||||
Route::post('share/{id}', [\App\Http\Controllers\Api\NovaCards\NovaCardEngagementController::class, 'share'])
|
||||
->middleware('forum.bot.protection:api_write')
|
||||
->whereNumber('id')
|
||||
->name('share');
|
||||
Route::post('download/{id}', [\App\Http\Controllers\Api\NovaCards\NovaCardEngagementController::class, 'download'])
|
||||
->middleware('forum.bot.protection:api_write')
|
||||
->whereNumber('id')
|
||||
->name('download');
|
||||
|
||||
// v3: Discovery feeds (public, cached)
|
||||
Route::get('rising', [\App\Http\Controllers\Api\NovaCards\NovaCardDiscoveryController::class, 'rising'])
|
||||
->name('rising');
|
||||
Route::get('{id}/related', [\App\Http\Controllers\Api\NovaCards\NovaCardDiscoveryController::class, 'related'])
|
||||
->whereNumber('id')
|
||||
->name('related');
|
||||
});
|
||||
|
||||
Route::prefix('v1')->name('api.v1.')->group(function () {
|
||||
// Public browse feed (authoritative tables only)
|
||||
Route::get('browse', [\App\Http\Controllers\Api\BrowseController::class, 'index'])
|
||||
@@ -216,6 +417,12 @@ Route::middleware(['web', 'auth', 'normalize.username'])->prefix('uploads')->nam
|
||||
->name('vision-suggest');
|
||||
});
|
||||
|
||||
Route::middleware(['web', 'auth'])->prefix('search')->name('api.search.')->group(function () {
|
||||
Route::post('image', \App\Http\Controllers\Api\ImageSearchController::class)
|
||||
->middleware(['throttle:20,1', 'forum.bot.protection:api_write'])
|
||||
->name('image');
|
||||
});
|
||||
|
||||
Route::middleware(['web', 'auth', 'admin.moderation'])->prefix('admin/uploads')->name('api.admin.uploads.')->group(function () {
|
||||
Route::get('pending', [\App\Http\Controllers\Api\Admin\UploadModerationController::class, 'pending'])
|
||||
->name('pending');
|
||||
@@ -233,9 +440,23 @@ Route::middleware(['web', 'auth', 'admin.moderation'])->prefix('admin/reports')-
|
||||
Route::get('queue', [\App\Http\Controllers\Api\Admin\ModerationReportQueueController::class, 'index'])
|
||||
->name('queue');
|
||||
|
||||
Route::patch('{report}', [\App\Http\Controllers\Api\Admin\ModerationReportQueueController::class, 'update'])
|
||||
->whereNumber('report')
|
||||
->name('update');
|
||||
|
||||
Route::post('{report}/moderate-target', [\App\Http\Controllers\Api\Admin\ModerationReportQueueController::class, 'moderateTarget'])
|
||||
->whereNumber('report')
|
||||
->name('moderate-target');
|
||||
|
||||
Route::get('feed-engine-decision', [\App\Http\Controllers\Api\Admin\FeedEngineDecisionController::class, 'index'])
|
||||
->name('feed-engine-decision');
|
||||
|
||||
Route::get('similar-artworks', [\App\Http\Controllers\Api\Admin\SimilarArtworkReportController::class, 'index'])
|
||||
->name('similar-artworks');
|
||||
|
||||
Route::get('discovery-feedback', [\App\Http\Controllers\Api\Admin\DiscoveryFeedbackReportController::class, 'index'])
|
||||
->name('discovery-feedback');
|
||||
|
||||
Route::get('feed-performance', [\App\Http\Controllers\Api\Admin\FeedPerformanceReportController::class, 'index'])
|
||||
->name('feed-performance');
|
||||
|
||||
@@ -256,6 +477,20 @@ Route::middleware(['web', 'auth', 'admin.moderation'])->prefix('admin/usernames'
|
||||
->name('reject');
|
||||
});
|
||||
|
||||
Route::middleware(['web', 'auth', 'admin.moderation'])->prefix('admin/collections')->name('api.admin.collections.')->group(function () {
|
||||
Route::patch('{collection}/moderation', [\App\Http\Controllers\Api\Admin\CollectionModerationController::class, 'updateModeration'])
|
||||
->name('moderation.update');
|
||||
|
||||
Route::patch('{collection}/interactions', [\App\Http\Controllers\Api\Admin\CollectionModerationController::class, 'updateInteractions'])
|
||||
->name('interactions.update');
|
||||
|
||||
Route::post('{collection}/unfeature', [\App\Http\Controllers\Api\Admin\CollectionModerationController::class, 'unfeature'])
|
||||
->name('unfeature');
|
||||
|
||||
Route::delete('{collection}/members/{member}', [\App\Http\Controllers\Api\Admin\CollectionModerationController::class, 'destroyMember'])
|
||||
->name('members.destroy');
|
||||
});
|
||||
|
||||
Route::post('analytics/similar-artworks', [\App\Http\Controllers\Api\SimilarArtworkAnalyticsController::class, 'store'])
|
||||
->middleware('throttle:uploads-status')
|
||||
->name('api.analytics.similar-artworks.store');
|
||||
@@ -272,6 +507,22 @@ Route::middleware(['web', 'auth', 'normalize.username'])->prefix('discovery')->n
|
||||
Route::post('events', [\App\Http\Controllers\Api\DiscoveryEventController::class, 'store'])
|
||||
->middleware(['throttle:uploads-status', 'forum.bot.protection:api_write'])
|
||||
->name('events.store');
|
||||
|
||||
Route::post('feedback/hide-artwork', [\App\Http\Controllers\Api\DiscoveryNegativeSignalController::class, 'hideArtwork'])
|
||||
->middleware(['throttle:uploads-status', 'forum.bot.protection:api_write'])
|
||||
->name('feedback.hide-artwork');
|
||||
|
||||
Route::delete('feedback/hide-artwork', [\App\Http\Controllers\Api\DiscoveryNegativeSignalController::class, 'unhideArtwork'])
|
||||
->middleware(['throttle:uploads-status', 'forum.bot.protection:api_write'])
|
||||
->name('feedback.unhide-artwork');
|
||||
|
||||
Route::post('feedback/dislike-tag', [\App\Http\Controllers\Api\DiscoveryNegativeSignalController::class, 'dislikeTag'])
|
||||
->middleware(['throttle:uploads-status', 'forum.bot.protection:api_write'])
|
||||
->name('feedback.dislike-tag');
|
||||
|
||||
Route::delete('feedback/dislike-tag', [\App\Http\Controllers\Api\DiscoveryNegativeSignalController::class, 'undislikeTag'])
|
||||
->middleware(['throttle:uploads-status', 'forum.bot.protection:api_write'])
|
||||
->name('feedback.undislike-tag');
|
||||
});
|
||||
|
||||
// ─── Artwork Search (Meilisearch-powered, public) ────────────────────────────
|
||||
@@ -440,26 +691,32 @@ Route::middleware(['web', 'auth', 'normalize.username', 'throttle:30,1'])
|
||||
->name('tags');
|
||||
});
|
||||
|
||||
Route::middleware(['web', 'auth', 'throttle:social-read'])
|
||||
->get('users/suggestions', \App\Http\Controllers\Api\UserSuggestionsController::class)
|
||||
->name('api.users.suggestions');
|
||||
|
||||
// ── Follow system ─────────────────────────────────────────────────────────────
|
||||
// POST /api/user/{username}/follow → follow a user
|
||||
// DELETE /api/user/{username}/follow → unfollow a user
|
||||
// GET /api/user/{username}/followers → paginated followers (public)
|
||||
// GET /api/user/{username}/following → paginated following (public)
|
||||
Route::middleware(['web', 'throttle:60,1'])
|
||||
Route::middleware(['web'])
|
||||
->prefix('user')
|
||||
->name('api.user.follow.')
|
||||
->group(function () {
|
||||
// Public: list followers / following
|
||||
Route::get('{username}/followers', [\App\Http\Controllers\Api\FollowController::class, 'followers'])
|
||||
->middleware('throttle:social-read')
|
||||
->where('username', '[A-Za-z0-9_-]{3,20}')
|
||||
->name('followers');
|
||||
|
||||
Route::get('{username}/following', [\App\Http\Controllers\Api\FollowController::class, 'following'])
|
||||
->middleware('throttle:social-read')
|
||||
->where('username', '[A-Za-z0-9_-]{3,20}')
|
||||
->name('following');
|
||||
|
||||
// Auth-required: follow / unfollow
|
||||
Route::middleware(['auth', 'normalize.username'])->group(function () {
|
||||
Route::middleware(['auth', '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');
|
||||
|
||||
@@ -47,6 +47,7 @@ Schedule::command('skinbase:reset-windowed-stats --period=7d')
|
||||
Schedule::command('uploads:cleanup')->dailyAt('03:00');
|
||||
Schedule::command('analytics:aggregate-similar-artworks')->dailyAt('03:10');
|
||||
Schedule::command('analytics:aggregate-feed')->dailyAt('03:20');
|
||||
Schedule::command('analytics:aggregate-discovery-feedback')->dailyAt('03:25');
|
||||
|
||||
// Drain Redis artwork-stat delta queue so MySQL counters stay fresh.
|
||||
// Run every 5 minutes with overlap protection.
|
||||
|
||||
@@ -14,8 +14,8 @@
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use App\Http\Controllers\Legacy\AvatarController;
|
||||
use App\Http\Controllers\Legacy\CategoryRedirectController;
|
||||
use App\Http\Controllers\Community\LatestCommentsController;
|
||||
use App\Http\Controllers\User\FavouritesController;
|
||||
use App\Http\Controllers\User\StatisticsController;
|
||||
use App\Http\Controllers\User\ProfileController;
|
||||
use App\Http\Controllers\Web\GalleryController;
|
||||
|
||||
@@ -57,8 +57,8 @@ Route::get('/latest', fn () => redirect('/uploads/latest', 301))->name
|
||||
Route::get('/authors/top', fn () => redirect('/creators/top', 301))->name('authors.top');
|
||||
Route::get('/latest-artworks', fn () => redirect()->route('discover.fresh', request()->query(), 301))->name('legacy.latest_artworks');
|
||||
|
||||
Route::get('/latest-comments', fn () => redirect()->route('community.activity', request()->query(), 301))->name('legacy.latest_comments');
|
||||
Route::get('/comments/latest', fn () => redirect()->route('community.activity', request()->query(), 301))->name('comments.latest');
|
||||
Route::get('/latest-comments', [LatestCommentsController::class, 'index'])->name('legacy.latest_comments');
|
||||
Route::get('/comments/latest', [LatestCommentsController::class, 'index'])->name('comments.latest');
|
||||
|
||||
Route::get('/today-in-history', fn () => redirect()->route('discover.on-this-day', request()->query(), 301))->name('legacy.today_in_history');
|
||||
|
||||
@@ -73,7 +73,6 @@ Route::get('/top-favourites', fn () => redirect()->route('discover.top-rated'
|
||||
Route::get('/top-authors', fn () => redirect('/creators/top', 301))->name('legacy.top_authors');
|
||||
Route::get('/interviews', fn () => redirect('/stories', 301))->name('legacy.interviews');
|
||||
Route::get('/apply', fn () => redirect('/contact', 301))->name('legacy.apply.redirect');
|
||||
Route::match(['get','post'], '/bug-report', fn () => redirect('/contact', 301))->name('bug-report.redirect');
|
||||
|
||||
// ── BUDDIES / MYBUDDIES ───────────────────────────────────────────────────────
|
||||
Route::middleware('auth')->get('/mybuddies.php', fn () => redirect()->route('dashboard.following', [], 301))->name('legacy.mybuddies.php');
|
||||
@@ -104,5 +103,5 @@ Route::middleware('auth')->get('/recieved-comments', fn () => redirect()->route(
|
||||
Route::middleware('auth')->get('/received-comments', fn () => redirect()->route('dashboard.comments.received', request()->query(), 301))->name('legacy.received_comments.corrected');
|
||||
|
||||
Route::middleware(['auth'])->group(function () {
|
||||
Route::get('/statistics', [StatisticsController::class, 'index'])->name('legacy.statistics');
|
||||
Route::get('/statistics', fn () => redirect()->route('leaderboard', [], 301))->name('legacy.statistics');
|
||||
});
|
||||
|
||||
232
routes/web.php
232
routes/web.php
@@ -17,6 +17,7 @@ use App\Http\Controllers\Web\PageController;
|
||||
use App\Http\Controllers\StoryController;
|
||||
use App\Http\Controllers\Web\HomeController;
|
||||
use App\Http\Controllers\Web\FooterController;
|
||||
use App\Http\Controllers\Web\BugReportController;
|
||||
use App\Http\Controllers\Web\StaffController;
|
||||
use App\Http\Controllers\Web\RssFeedController;
|
||||
use App\Http\Controllers\Web\ApplicationController;
|
||||
@@ -35,7 +36,15 @@ use App\Http\Controllers\Community\LatestController;
|
||||
use App\Http\Controllers\User\MembersController;
|
||||
use App\Http\Controllers\User\TodayDownloadsController;
|
||||
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\Models\Artwork;
|
||||
use App\Http\Controllers\Settings\CollectionAiController;
|
||||
use App\Http\Controllers\Settings\CollectionInsightsController;
|
||||
use App\Http\Controllers\Settings\CollectionManageController;
|
||||
use App\Http\Controllers\Settings\CollectionProgrammingController;
|
||||
use App\Http\Controllers\Settings\CollectionSurfaceController;
|
||||
use Inertia\Inertia;
|
||||
|
||||
Route::get('/', [HomeController::class, 'index'])->name('index');
|
||||
@@ -104,6 +113,8 @@ Route::get('/rules-and-guidelines', [FooterController::class, 'rules'])->name('r
|
||||
Route::get('/privacy-policy', [FooterController::class, 'privacyPolicy'])->name('privacy-policy');
|
||||
Route::get('/terms-of-service', [FooterController::class, 'termsOfService'])->name('terms-of-service');
|
||||
Route::get('/staff', [StaffController::class, 'index'])->name('staff');
|
||||
Route::get('/bug-report', [BugReportController::class, 'show'])->name('bug-report');
|
||||
Route::post('/bug-report', [BugReportController::class, 'submit'])->middleware('auth')->name('bug-report.submit');
|
||||
|
||||
Route::get('/contact', [ApplicationController::class, 'show'])->name('contact.show');
|
||||
Route::post('/contact', [ApplicationController::class, 'submit'])->middleware('throttle:6,1')->name('contact.submit');
|
||||
@@ -231,11 +242,72 @@ Route::prefix('news')->name('news.')->group(function () {
|
||||
});
|
||||
Route::get('/rss/news', [NewsRssController::class, 'feed'])->name('news.rss');
|
||||
|
||||
Route::get('/collections/featured', [\App\Http\Controllers\Web\CollectionDiscoveryController::class, 'featured'])
|
||||
->name('collections.featured');
|
||||
Route::get('/collections/trending', [\App\Http\Controllers\Web\CollectionDiscoveryController::class, 'trending'])
|
||||
->name('collections.trending');
|
||||
Route::get('/collections/editorial', [\App\Http\Controllers\Web\CollectionDiscoveryController::class, 'editorial'])
|
||||
->name('collections.editorial');
|
||||
Route::get('/collections/community', [\App\Http\Controllers\Web\CollectionDiscoveryController::class, 'community'])
|
||||
->name('collections.community');
|
||||
Route::get('/collections/seasonal', [\App\Http\Controllers\Web\CollectionDiscoveryController::class, 'seasonal'])
|
||||
->name('collections.seasonal');
|
||||
Route::get('/collections/campaigns/{campaignKey}', [\App\Http\Controllers\Web\CollectionDiscoveryController::class, 'campaign'])
|
||||
->where('campaignKey', '[A-Za-z0-9_-]{1,80}')
|
||||
->name('collections.campaign.show');
|
||||
Route::get('/collections/program/{programKey}', [\App\Http\Controllers\Web\CollectionDiscoveryController::class, 'program'])
|
||||
->where('programKey', '[A-Za-z0-9_-]{1,80}')
|
||||
->name('collections.program.show');
|
||||
Route::get('/collections/recommended', [\App\Http\Controllers\Web\CollectionDiscoveryController::class, 'recommended'])
|
||||
->name('collections.recommended');
|
||||
Route::get('/collections/search', [\App\Http\Controllers\Web\CollectionDiscoveryController::class, 'search'])
|
||||
->name('collections.search');
|
||||
|
||||
Route::middleware('auth')->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'])
|
||||
->name('me.saved.collections.lists.show');
|
||||
Route::post('/me/saved/collections/{collection}/lists', [CollectionSavedLibraryController::class, 'storeItem'])
|
||||
->name('me.saved.collections.lists.items.store');
|
||||
Route::patch('/me/saved/collections/{collection}/note', [CollectionSavedLibraryController::class, 'updateNote'])
|
||||
->name('me.saved.collections.notes.update');
|
||||
Route::post('/me/saved/collections/lists/{list}/items/reorder', [CollectionSavedLibraryController::class, 'reorderItems'])
|
||||
->name('me.saved.collections.lists.items.reorder');
|
||||
Route::delete('/me/saved/collections/lists/{list}/items/{collection}', [CollectionSavedLibraryController::class, 'destroyItem'])
|
||||
->name('me.saved.collections.lists.items.destroy');
|
||||
Route::post('/collections/{collection}/follow', [\App\Http\Controllers\CollectionEngagementController::class, 'follow'])->name('collections.follow');
|
||||
Route::delete('/collections/{collection}/follow', [\App\Http\Controllers\CollectionEngagementController::class, 'unfollow'])->name('collections.unfollow');
|
||||
Route::post('/collections/{collection}/like', [\App\Http\Controllers\CollectionEngagementController::class, 'like'])->name('collections.like');
|
||||
Route::delete('/collections/{collection}/like', [\App\Http\Controllers\CollectionEngagementController::class, 'unlike'])->name('collections.unlike');
|
||||
Route::post('/collections/{collection}/save', [\App\Http\Controllers\CollectionEngagementController::class, 'save'])->name('collections.save');
|
||||
Route::delete('/collections/{collection}/save', [\App\Http\Controllers\CollectionEngagementController::class, 'unsave'])->name('collections.unsave');
|
||||
Route::post('/collections/{collection}/submissions', [\App\Http\Controllers\CollectionSubmissionController::class, 'store'])->name('collections.submissions.store');
|
||||
Route::delete('/collections/submissions/{submission}', [\App\Http\Controllers\CollectionSubmissionController::class, 'destroy'])->name('collections.submissions.destroy');
|
||||
Route::post('/collections/submissions/{submission}/approve', [\App\Http\Controllers\CollectionSubmissionController::class, 'approve'])->name('collections.submissions.approve');
|
||||
Route::post('/collections/submissions/{submission}/reject', [\App\Http\Controllers\CollectionSubmissionController::class, 'reject'])->name('collections.submissions.reject');
|
||||
Route::post('/collections/{collection}/comments', [\App\Http\Controllers\CollectionCommentController::class, 'store'])->name('collections.comments.store');
|
||||
Route::delete('/collections/{collection}/comments/{comment}', [\App\Http\Controllers\CollectionCommentController::class, 'destroy'])->name('collections.comments.destroy');
|
||||
});
|
||||
|
||||
Route::post('/collections/{collection}/share', [\App\Http\Controllers\CollectionEngagementController::class, 'share'])
|
||||
->name('collections.share');
|
||||
Route::get('/collections/{collection}/comments', [\App\Http\Controllers\CollectionCommentController::class, 'index'])
|
||||
->name('collections.comments.index');
|
||||
Route::get('/collections/series/{seriesKey}', [ProfileCollectionController::class, 'showSeries'])
|
||||
->where('seriesKey', '[A-Za-z0-9_-]{1,80}')
|
||||
->name('collections.series.show');
|
||||
|
||||
// ── PROFILES (@username) ──────────────────────────────────────────────────────
|
||||
Route::get('/@{username}/gallery', [ProfileController::class, 'showGalleryByUsername'])
|
||||
->where('username', '[A-Za-z0-9_-]{3,20}')
|
||||
->name('profile.gallery');
|
||||
|
||||
Route::get('/@{username}/collections/{slug}', [ProfileCollectionController::class, 'show'])
|
||||
->where('username', '[A-Za-z0-9_-]{3,20}')
|
||||
->where('slug', '[a-z0-9\-]{2,140}')
|
||||
->name('profile.collections.show');
|
||||
|
||||
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')
|
||||
@@ -253,6 +325,9 @@ Route::middleware('auth')->post('/@{username}/comment', [ProfileController::clas
|
||||
->where('username', '[A-Za-z0-9_-]{3,20}')
|
||||
->name('profile.comment');
|
||||
|
||||
Route::middleware('auth')->get('/me/saved/collections', [SavedCollectionController::class, 'index'])
|
||||
->name('me.saved.collections');
|
||||
|
||||
// ── DASHBOARD ─────────────────────────────────────────────────────────────────
|
||||
Route::get('/dashboard', [DashboardController::class, 'index'])
|
||||
->middleware(['auth', 'verified'])
|
||||
@@ -263,6 +338,10 @@ Route::middleware(['auth', 'creator.access'])->prefix('creator')->name('creator.
|
||||
Route::get('/analytics', fn () => redirect()->route('studio.analytics'))->name('analytics');
|
||||
});
|
||||
|
||||
Route::middleware(['auth', \App\Http\Middleware\NoIndexDashboard::class])
|
||||
->get('/manage', [\App\Http\Controllers\Dashboard\ManageController::class, 'index'])
|
||||
->name('manage');
|
||||
|
||||
Route::middleware(['auth', \App\Http\Middleware\NoIndexDashboard::class])->prefix('dashboard')->name('dashboard.')->group(function () {
|
||||
Route::get('/artworks', [DashboardArtworkController::class, 'index'])->name('artworks.index');
|
||||
Route::get('/artworks/{id}/edit', [DashboardArtworkController::class, 'edit'])->whereNumber('id')->name('artworks.edit');
|
||||
@@ -294,6 +373,79 @@ Route::middleware(['auth', 'ensure.onboarding.complete'])->prefix('studio')->nam
|
||||
Route::get('/artworks/{id}/edit', [StudioController::class, 'edit'])->whereNumber('id')->name('artworks.edit');
|
||||
Route::get('/artworks/{id}/analytics', [StudioController::class, 'analytics'])->whereNumber('id')->name('artworks.analytics');
|
||||
Route::get('/analytics', [StudioController::class, 'analyticsOverview'])->name('analytics');
|
||||
Route::get('/cards', [\App\Http\Controllers\Studio\StudioNovaCardsController::class, 'index'])->name('cards.index');
|
||||
Route::get('/cards/create', [\App\Http\Controllers\Studio\StudioNovaCardsController::class, 'create'])->name('cards.create');
|
||||
Route::post('/cards/remix/{id}', [\App\Http\Controllers\Studio\StudioNovaCardsController::class, 'remix'])->whereNumber('id')->name('cards.remix');
|
||||
Route::get('/cards/{id}/edit', [\App\Http\Controllers\Studio\StudioNovaCardsController::class, 'edit'])->whereNumber('id')->name('cards.edit');
|
||||
Route::get('/cards/{id}/preview', [\App\Http\Controllers\Studio\StudioNovaCardsController::class, 'preview'])->whereNumber('id')->name('cards.preview');
|
||||
Route::get('/cards/{id}/analytics', [\App\Http\Controllers\Studio\StudioNovaCardsController::class, 'analytics'])->whereNumber('id')->name('cards.analytics');
|
||||
});
|
||||
|
||||
Route::get('/cards', [\App\Http\Controllers\Web\NovaCardsController::class, 'index'])->name('cards.index');
|
||||
Route::get('/cards/popular', [\App\Http\Controllers\Web\NovaCardsController::class, 'popular'])->name('cards.popular');
|
||||
Route::get('/cards/rising', [\App\Http\Controllers\Web\NovaCardsController::class, 'rising'])->name('cards.rising');
|
||||
Route::get('/cards/remixed', [\App\Http\Controllers\Web\NovaCardsController::class, 'remixed'])->name('cards.remixed');
|
||||
Route::get('/cards/remix-highlights', [\App\Http\Controllers\Web\NovaCardsController::class, 'remixHighlights'])->name('cards.remix-highlights');
|
||||
Route::get('/cards/editorial', [\App\Http\Controllers\Web\NovaCardsController::class, 'editorial'])->name('cards.editorial');
|
||||
Route::get('/cards/seasonal', [\App\Http\Controllers\Web\NovaCardsController::class, 'seasonal'])->name('cards.seasonal');
|
||||
Route::get('/cards/collections/{slug}-{id}', [\App\Http\Controllers\Web\NovaCardsController::class, 'collection'])
|
||||
->where('slug', '[a-z0-9\-]+')
|
||||
->whereNumber('id')
|
||||
->name('cards.collections.show');
|
||||
Route::get('/cards/{slug}-{id}/lineage', [\App\Http\Controllers\Web\NovaCardsController::class, 'lineage'])
|
||||
->where('slug', '[a-z0-9\-]+')
|
||||
->whereNumber('id')
|
||||
->name('cards.lineage');
|
||||
Route::get('/cards/challenges', [\App\Http\Controllers\Web\NovaCardsController::class, 'challenges'])->name('cards.challenges');
|
||||
Route::get('/cards/challenges/{slug}', [\App\Http\Controllers\Web\NovaCardsController::class, 'challenge'])->name('cards.challenges.show');
|
||||
Route::get('/cards/templates', [\App\Http\Controllers\Web\NovaCardsController::class, 'templates'])->name('cards.templates');
|
||||
Route::get('/cards/assets', [\App\Http\Controllers\Web\NovaCardsController::class, 'assets'])->name('cards.assets');
|
||||
Route::get('/cards/category/{categorySlug}', [\App\Http\Controllers\Web\NovaCardsController::class, 'category'])->name('cards.category');
|
||||
Route::get('/cards/mood/{moodSlug}', [\App\Http\Controllers\Web\NovaCardsController::class, 'mood'])->name('cards.mood');
|
||||
Route::get('/cards/style/{styleSlug}', [\App\Http\Controllers\Web\NovaCardsController::class, 'style'])->name('cards.style');
|
||||
Route::get('/cards/palette/{paletteSlug}', [\App\Http\Controllers\Web\NovaCardsController::class, 'palette'])->name('cards.palette');
|
||||
Route::get('/cards/tag/{tagSlug}', [\App\Http\Controllers\Web\NovaCardsController::class, 'tag'])->name('cards.tag');
|
||||
Route::get('/cards/creator/{username}/portfolio', [\App\Http\Controllers\Web\NovaCardsController::class, 'creatorPortfolio'])
|
||||
->where('username', '[A-Za-z0-9_-]{3,20}')
|
||||
->name('cards.creator.portfolio');
|
||||
Route::get('/cards/creator/{username}', [\App\Http\Controllers\Web\NovaCardsController::class, 'creator'])
|
||||
->where('username', '[A-Za-z0-9_-]{3,20}')
|
||||
->name('cards.creator');
|
||||
Route::get('/cards/{slug}-{id}', [\App\Http\Controllers\Web\NovaCardsController::class, 'show'])
|
||||
->where('slug', '[a-z0-9\-]+')
|
||||
->whereNumber('id')
|
||||
->name('cards.show');
|
||||
|
||||
Route::middleware('auth')->group(function () {
|
||||
Route::post('/cards/{card}/comments', [\App\Http\Controllers\NovaCardCommentController::class, 'store'])
|
||||
->whereNumber('card')
|
||||
->name('cards.comments.store');
|
||||
Route::delete('/cards/{card}/comments/{comment}', [\App\Http\Controllers\NovaCardCommentController::class, 'destroy'])
|
||||
->whereNumber('card')
|
||||
->whereNumber('comment')
|
||||
->name('cards.comments.destroy');
|
||||
});
|
||||
|
||||
Route::middleware(['auth', 'admin.moderation'])->prefix('cp/cards')->name('cp.cards.')->group(function () {
|
||||
Route::get('/', [\App\Http\Controllers\Settings\NovaCardAdminController::class, 'index'])->name('index');
|
||||
Route::patch('/{card}', [\App\Http\Controllers\Settings\NovaCardAdminController::class, 'updateCard'])->whereNumber('card')->name('update');
|
||||
Route::patch('/creators/{user}', [\App\Http\Controllers\Settings\NovaCardAdminController::class, 'updateCreator'])->whereNumber('user')->name('creators.update');
|
||||
Route::get('/templates', [\App\Http\Controllers\Settings\NovaCardAdminController::class, 'templates'])->name('templates.index');
|
||||
Route::post('/templates', [\App\Http\Controllers\Settings\NovaCardAdminController::class, 'storeTemplate'])->name('templates.store');
|
||||
Route::patch('/templates/{template}', [\App\Http\Controllers\Settings\NovaCardAdminController::class, 'updateTemplate'])->whereNumber('template')->name('templates.update');
|
||||
Route::get('/asset-packs', [\App\Http\Controllers\Settings\NovaCardAdminController::class, 'assetPacks'])->name('asset-packs.index');
|
||||
Route::post('/asset-packs', [\App\Http\Controllers\Settings\NovaCardAdminController::class, 'storeAssetPack'])->name('asset-packs.store');
|
||||
Route::patch('/asset-packs/{assetPack}', [\App\Http\Controllers\Settings\NovaCardAdminController::class, 'updateAssetPack'])->whereNumber('assetPack')->name('asset-packs.update');
|
||||
Route::get('/challenges', [\App\Http\Controllers\Settings\NovaCardAdminController::class, 'challenges'])->name('challenges.index');
|
||||
Route::post('/challenges', [\App\Http\Controllers\Settings\NovaCardAdminController::class, 'storeChallenge'])->name('challenges.store');
|
||||
Route::patch('/challenges/{challenge}', [\App\Http\Controllers\Settings\NovaCardAdminController::class, 'updateChallenge'])->whereNumber('challenge')->name('challenges.update');
|
||||
Route::get('/collections', [\App\Http\Controllers\Settings\NovaCardAdminController::class, 'collections'])->name('collections.index');
|
||||
Route::post('/collections', [\App\Http\Controllers\Settings\NovaCardAdminController::class, 'storeCollection'])->name('collections.store');
|
||||
Route::patch('/collections/{collection}', [\App\Http\Controllers\Settings\NovaCardAdminController::class, 'updateCollection'])->whereNumber('collection')->name('collections.update');
|
||||
Route::post('/collections/{collection}/cards', [\App\Http\Controllers\Settings\NovaCardAdminController::class, 'storeCollectionCard'])->whereNumber('collection')->name('collections.cards.store');
|
||||
Route::delete('/collections/{collection}/cards/{card}', [\App\Http\Controllers\Settings\NovaCardAdminController::class, 'destroyCollectionCard'])->whereNumber('collection')->whereNumber('card')->name('collections.cards.destroy');
|
||||
Route::post('/categories', [\App\Http\Controllers\Settings\NovaCardAdminController::class, 'storeCategory'])->name('categories.store');
|
||||
Route::patch('/categories/{category}', [\App\Http\Controllers\Settings\NovaCardAdminController::class, 'updateCategory'])->whereNumber('category')->name('categories.update');
|
||||
});
|
||||
|
||||
// ── SETTINGS / PROFILE EDIT ───────────────────────────────────────────────────
|
||||
@@ -318,6 +470,82 @@ Route::middleware(['auth', 'normalize.username', 'ensure.onboarding.complete'])-
|
||||
Route::post('/settings/personal/update', [ProfileController::class, 'updatePersonalSection'])->middleware('forum.bot.protection:profile_update')->name('settings.personal.update');
|
||||
Route::post('/settings/notifications/update', [ProfileController::class, 'updateNotificationsSection'])->middleware('forum.bot.protection:profile_update')->name('settings.notifications.update');
|
||||
Route::post('/settings/security/password', [ProfileController::class, 'updateSecurityPassword'])->middleware('forum.bot.protection:profile_update')->name('settings.security.password');
|
||||
|
||||
Route::get('/settings/collections/create', [CollectionManageController::class, 'create'])->name('settings.collections.create');
|
||||
Route::post('/settings/collections', [CollectionManageController::class, 'store'])->name('settings.collections.store');
|
||||
Route::post('/settings/collections/smart-preview', [CollectionManageController::class, 'smartPreview'])->name('settings.collections.smart.preview');
|
||||
Route::post('/settings/collections/reorder-profile', [CollectionManageController::class, 'reorderProfile'])->name('settings.collections.reorder-profile');
|
||||
Route::get('/settings/collections/dashboard', [CollectionInsightsController::class, 'dashboard'])->name('settings.collections.dashboard');
|
||||
Route::get('/settings/collections/search', [CollectionInsightsController::class, 'search'])->name('settings.collections.search');
|
||||
Route::post('/settings/collections/bulk-actions', [CollectionInsightsController::class, 'bulkActions'])->name('settings.collections.bulk-actions');
|
||||
Route::get('/settings/collections/{collection}', [CollectionManageController::class, 'show'])->name('settings.collections.show');
|
||||
Route::get('/settings/collections/{collection}/edit', [CollectionManageController::class, 'edit'])->name('settings.collections.edit');
|
||||
Route::get('/settings/collections/{collection}/analytics', [CollectionInsightsController::class, 'analytics'])->name('settings.collections.analytics');
|
||||
Route::get('/settings/collections/{collection}/health', [CollectionInsightsController::class, 'health'])->name('settings.collections.health');
|
||||
Route::get('/settings/collections/{collection}/history', [CollectionInsightsController::class, 'history'])->name('settings.collections.history');
|
||||
Route::post('/settings/collections/{collection}/history/{history}/restore', [CollectionInsightsController::class, 'restoreHistory'])->name('settings.collections.history.restore');
|
||||
Route::patch('/settings/collections/{collection}', [CollectionManageController::class, 'update'])->name('settings.collections.update');
|
||||
Route::post('/settings/collections/{collection}/presentation', [CollectionManageController::class, 'updatePresentation'])->name('settings.collections.presentation');
|
||||
Route::post('/settings/collections/{collection}/campaign', [CollectionManageController::class, 'updateCampaign'])->name('settings.collections.campaign');
|
||||
Route::post('/settings/collections/{collection}/series', [CollectionManageController::class, 'updateSeries'])->name('settings.collections.series');
|
||||
Route::post('/settings/collections/{collection}/lifecycle', [CollectionManageController::class, 'updateLifecycle'])->name('settings.collections.lifecycle');
|
||||
Route::post('/settings/collections/{collection}/linked-collections', [CollectionManageController::class, 'syncLinkedCollections'])->name('settings.collections.linked.sync');
|
||||
Route::post('/settings/collections/{collection}/entity-links', [CollectionManageController::class, 'syncEntityLinks'])->name('settings.collections.entity-links.sync');
|
||||
Route::delete('/settings/collections/{collection}', [CollectionManageController::class, 'destroy'])->name('settings.collections.destroy');
|
||||
Route::post('/settings/collections/{collection}/feature', [CollectionManageController::class, 'feature'])->name('settings.collections.feature');
|
||||
Route::delete('/settings/collections/{collection}/feature', [CollectionManageController::class, 'unfeature'])->name('settings.collections.unfeature');
|
||||
Route::patch('/settings/collections/{collection}/smart-rules', [CollectionManageController::class, 'updateSmartRules'])->name('settings.collections.smart.rules');
|
||||
Route::post('/settings/collections/{collection}/ai/suggest-title', [CollectionAiController::class, 'suggestTitle'])->name('settings.collections.ai.suggest-title');
|
||||
Route::post('/settings/collections/{collection}/ai/suggest-summary', [CollectionAiController::class, 'suggestSummary'])->name('settings.collections.ai.suggest-summary');
|
||||
Route::post('/settings/collections/{collection}/ai/suggest-cover', [CollectionAiController::class, 'suggestCover'])->name('settings.collections.ai.suggest-cover');
|
||||
Route::post('/settings/collections/{collection}/ai/suggest-grouping', [CollectionAiController::class, 'suggestGrouping'])->name('settings.collections.ai.suggest-grouping');
|
||||
Route::post('/settings/collections/{collection}/ai/suggest-related-artworks', [CollectionAiController::class, 'suggestRelatedArtworks'])->name('settings.collections.ai.suggest-related-artworks');
|
||||
Route::post('/settings/collections/{collection}/ai/suggest-tags', [CollectionAiController::class, 'suggestTags'])->name('settings.collections.ai.suggest-tags');
|
||||
Route::post('/settings/collections/{collection}/ai/suggest-seo-description', [CollectionAiController::class, 'suggestSeoDescription'])->name('settings.collections.ai.suggest-seo-description');
|
||||
Route::post('/settings/collections/{collection}/ai/explain-smart-rules', [CollectionAiController::class, 'explainSmartRules'])->name('settings.collections.ai.explain-smart-rules');
|
||||
Route::post('/settings/collections/{collection}/ai/suggest-split-themes', [CollectionAiController::class, 'suggestSplitThemes'])->name('settings.collections.ai.suggest-split-themes');
|
||||
Route::post('/settings/collections/{collection}/ai/suggest-merge-idea', [CollectionAiController::class, 'suggestMergeIdea'])->name('settings.collections.ai.suggest-merge-idea');
|
||||
Route::post('/settings/collections/{collection}/ai/detect-weak-metadata', [CollectionAiController::class, 'detectWeakMetadata'])->name('settings.collections.ai.detect-weak-metadata');
|
||||
Route::post('/settings/collections/{collection}/ai/suggest-stale-refresh', [CollectionAiController::class, 'suggestStaleRefresh'])->name('settings.collections.ai.suggest-stale-refresh');
|
||||
Route::post('/settings/collections/{collection}/ai/suggest-campaign-fit', [CollectionAiController::class, 'suggestCampaignFit'])->name('settings.collections.ai.suggest-campaign-fit');
|
||||
Route::post('/settings/collections/{collection}/ai/suggest-related-collections-to-link', [CollectionAiController::class, 'suggestRelatedCollectionsToLink'])->name('settings.collections.ai.suggest-related-collections-to-link');
|
||||
Route::post('/settings/collections/{collection}/ai/quality-review', [CollectionInsightsController::class, 'qualityReview'])->name('settings.collections.ai.quality-review');
|
||||
Route::post('/settings/collections/{collection}/workflow', [CollectionInsightsController::class, 'workflowUpdate'])->name('settings.collections.workflow');
|
||||
Route::post('/settings/collections/{collection}/quality-refresh', [CollectionInsightsController::class, 'qualityRefresh'])->name('settings.collections.quality-refresh');
|
||||
Route::post('/settings/collections/{collection}/canonicalize', [CollectionInsightsController::class, 'canonicalize'])->name('settings.collections.canonicalize');
|
||||
Route::post('/settings/collections/{collection}/merge', [CollectionInsightsController::class, 'merge'])->name('settings.collections.merge');
|
||||
Route::post('/settings/collections/{collection}/merge/reject', [CollectionInsightsController::class, 'rejectDuplicate'])->name('settings.collections.merge.reject');
|
||||
Route::post('/settings/collections/{collection}/artworks', [CollectionManageController::class, 'attachArtworks'])->name('settings.collections.artworks.attach');
|
||||
Route::get('/settings/collections/artworks/{artwork}/options', [CollectionManageController::class, 'artworkCollectionOptions'])->name('settings.collections.artworks.options');
|
||||
Route::get('/settings/collections/{collection}/artworks/available', [CollectionManageController::class, 'availableArtworks'])->name('settings.collections.artworks.available');
|
||||
Route::delete('/settings/collections/{collection}/artworks/{artwork}', [CollectionManageController::class, 'removeArtwork'])->name('settings.collections.artworks.remove');
|
||||
Route::post('/settings/collections/{collection}/reorder', [CollectionManageController::class, 'reorderArtworks'])->name('settings.collections.artworks.reorder');
|
||||
Route::post('/settings/collections/{collection}/members', [\App\Http\Controllers\CollectionCollaborationController::class, 'store'])->name('settings.collections.members.store');
|
||||
Route::patch('/settings/collections/{collection}/members/{member}', [\App\Http\Controllers\CollectionCollaborationController::class, 'update'])->name('settings.collections.members.update');
|
||||
Route::post('/settings/collections/{collection}/members/{member}/transfer', [\App\Http\Controllers\CollectionCollaborationController::class, 'transfer'])->name('settings.collections.members.transfer');
|
||||
Route::delete('/settings/collections/{collection}/members/{member}', [\App\Http\Controllers\CollectionCollaborationController::class, 'destroy'])->name('settings.collections.members.destroy');
|
||||
Route::post('/settings/collections/members/{member}/accept', [\App\Http\Controllers\CollectionCollaborationController::class, 'accept'])->name('settings.collections.members.accept');
|
||||
Route::post('/settings/collections/members/{member}/decline', [\App\Http\Controllers\CollectionCollaborationController::class, 'decline'])->name('settings.collections.members.decline');
|
||||
Route::get('/settings/collections/surfaces', [CollectionSurfaceController::class, 'index'])->name('settings.collections.surfaces.index');
|
||||
Route::post('/settings/collections/surfaces/definitions', [CollectionSurfaceController::class, 'storeDefinition'])->name('settings.collections.surfaces.definitions.store');
|
||||
Route::patch('/settings/collections/surfaces/definitions/{definition}', [CollectionSurfaceController::class, 'updateDefinition'])->name('settings.collections.surfaces.definitions.update');
|
||||
Route::delete('/settings/collections/surfaces/definitions/{definition}', [CollectionSurfaceController::class, 'destroyDefinition'])->name('settings.collections.surfaces.definitions.destroy');
|
||||
Route::post('/settings/collections/surfaces/placements', [CollectionSurfaceController::class, 'storePlacement'])->name('settings.collections.surfaces.placements.store');
|
||||
Route::patch('/settings/collections/surfaces/placements/{placement}', [CollectionSurfaceController::class, 'updatePlacement'])->name('settings.collections.surfaces.placements.update');
|
||||
Route::delete('/settings/collections/surfaces/placements/{placement}', [CollectionSurfaceController::class, 'destroyPlacement'])->name('settings.collections.surfaces.placements.destroy');
|
||||
Route::get('/settings/collections/surfaces/definitions/{definition}/preview', [CollectionSurfaceController::class, 'preview'])->name('settings.collections.surfaces.preview');
|
||||
Route::post('/settings/collections/surfaces/batch-editorial', [CollectionSurfaceController::class, 'batchEditorial'])->name('settings.collections.surfaces.batch-editorial');
|
||||
Route::get('/staff/collections/programming', [CollectionProgrammingController::class, 'index'])->name('staff.collections.programming');
|
||||
Route::post('/staff/collections/programs', [CollectionProgrammingController::class, 'storeProgram'])->name('staff.collections.programs.store');
|
||||
Route::patch('/staff/collections/programs/{program}', [CollectionProgrammingController::class, 'updateProgram'])->name('staff.collections.programs.update');
|
||||
Route::post('/staff/collections/surfaces/preview', [CollectionProgrammingController::class, 'preview'])->name('staff.collections.surfaces.preview');
|
||||
Route::post('/staff/collections/eligibility/refresh', [CollectionProgrammingController::class, 'refreshEligibility'])->name('staff.collections.eligibility.refresh');
|
||||
Route::post('/staff/collections/duplicate-scan', [CollectionProgrammingController::class, 'duplicateScan'])->name('staff.collections.duplicate-scan');
|
||||
Route::post('/staff/collections/recommendation-refresh', [CollectionProgrammingController::class, 'refreshRecommendations'])->name('staff.collections.recommendation-refresh');
|
||||
Route::post('/staff/collections/metadata', [CollectionProgrammingController::class, 'updateMetadata'])->name('staff.collections.metadata.update');
|
||||
Route::post('/staff/collections/merge-queue/canonicalize', [CollectionProgrammingController::class, 'canonicalizeCandidate'])->name('staff.collections.merge-queue.canonicalize');
|
||||
Route::post('/staff/collections/merge-queue/merge', [CollectionProgrammingController::class, 'mergeCandidate'])->name('staff.collections.merge-queue.merge');
|
||||
Route::post('/staff/collections/merge-queue/reject', [CollectionProgrammingController::class, 'rejectCandidate'])->name('staff.collections.merge-queue.reject');
|
||||
});
|
||||
|
||||
// ── UPLOAD ────────────────────────────────────────────────────────────────────
|
||||
@@ -405,6 +633,10 @@ 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');
|
||||
|
||||
// ── COMMUNITY ACTIVITY ────────────────────────────────────────────────────────
|
||||
|
||||
Route::get('/community/activity', [\App\Http\Controllers\Web\CommunityActivityController::class, 'index'])
|
||||
|
||||
Reference in New Issue
Block a user