middleware(['auth', 'verified'])->name('dashboard'); Route::middleware(['auth'])->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'); Route::put('/artworks/{id}', [DashboardArtworkController::class, 'update'])->whereNumber('id')->name('artworks.update'); Route::delete('/artworks/{id}', [DashboardArtworkController::class, 'destroy'])->whereNumber('id')->name('artworks.destroy'); }); Route::middleware('auth')->group(function () { Route::get('/profile', [ProfileController::class, 'edit'])->name('profile.edit'); Route::match(['post','put','patch'], '/profile', [ProfileController::class, 'update'])->name('profile.update'); Route::delete('/profile', [ProfileController::class, 'destroy'])->name('profile.destroy'); // Password change endpoint (accepts POST or PUT from legacy and new forms) Route::match(['post', 'put'], '/profile/password', [ProfileController::class, 'password'])->name('profile.password'); // Avatar upload (backend only) - processes and stores avatars Route::post('/avatar/upload', [AvatarController::class, 'upload'])->name('avatar.upload'); }); Route::middleware(['auth'])->group(function () { Route::get('/upload', function () { $contentTypes = ContentType::with(['rootCategories.children'])->get()->map(function ($ct) { return [ 'id' => $ct->id, 'name' => $ct->name, 'categories' => $ct->rootCategories->map(function ($c) { return [ 'id' => $c->id, 'name' => $c->name, 'children' => $c->children->map(function ($ch) { return ['id' => $ch->id, 'name' => $ch->name]; })->values()->all(), ]; })->values()->all(), ]; })->values()->all(); return Inertia::render('Upload/Index', [ 'draftId' => null, 'content_types' => $contentTypes, 'suggested_tags' => [], 'filesCdnUrl' => config('cdn.files_url'), 'chunkSize' => (int) config('uploads.chunk.max_bytes', 5242880), 'feature_flags' => [ 'uploads_v2' => (bool) config('features.uploads_v2', false), ], ]); })->name('upload'); Route::get('/upload/draft/{id}', function (string $id) { $contentTypes = ContentType::with(['rootCategories.children'])->get()->map(function ($ct) { return [ 'id' => $ct->id, 'name' => $ct->name, 'categories' => $ct->rootCategories->map(function ($c) { return [ 'id' => $c->id, 'name' => $c->name, 'children' => $c->children->map(function ($ch) { return ['id' => $ch->id, 'name' => $ch->name]; })->values()->all(), ]; })->values()->all(), ]; })->values()->all(); return Inertia::render('Upload/Index', [ 'draftId' => $id, 'content_types' => $contentTypes, 'suggested_tags' => [], 'filesCdnUrl' => config('cdn.files_url'), 'chunkSize' => (int) config('uploads.chunk.max_bytes', 5242880), 'feature_flags' => [ 'uploads_v2' => (bool) config('features.uploads_v2', false), ], ]); })->whereUuid('id')->name('upload.draft'); }); require __DIR__.'/auth.php'; Route::get('/tag/{tag:slug}', [\App\Http\Controllers\Web\TagController::class, 'show']) ->where('tag', '[a-z0-9\-]+') ->name('tags.show'); Route::view('/blank', 'blank')->name('blank'); // Bind the artwork route parameter to a model if it exists, otherwise return null use App\Models\Artwork; Route::bind('artwork', function ($value) { return Artwork::where('slug', $value)->first(); }); // Universal content router: handles content-type roots, nested categories and artwork slugs. // Keep the explicit /photography route above (if present) so the legacy controller can continue // to serve photography's root page. This catch-all route delegates to a controller that // will forward to the appropriate existing controller (artwork or category handlers). // Provide a named route alias for legacy artwork URL generation used in tests. Route::get('/{contentTypeSlug}/{categoryPath}/{artwork}', [\App\Http\Controllers\ContentRouterController::class, 'handle']) ->where('contentTypeSlug', 'photography|wallpapers|skins|other') ->where('categoryPath', '[^/]+(?:/[^/]+)*') ->name('artworks.show'); Route::get('/{contentTypeSlug}/{path?}', [\App\Http\Controllers\ContentRouterController::class, 'handle']) ->where('contentTypeSlug', 'photography|wallpapers|skins|other') ->where('path', '.*') ->name('content.route'); Route::middleware(['auth'])->group(function () { Route::get('/manage', [ManageController::class, 'index'])->name('manage'); Route::get('/manage/edit/{id}', [ManageController::class, 'edit'])->name('manage.edit'); Route::post('/manage/update/{id}', [ManageController::class, 'update'])->name('manage.update'); Route::post('/manage/delete/{id}', [ManageController::class, 'destroy'])->name('manage.destroy'); }); // Admin routes for artworks (separated from public routes) Route::middleware(['auth'])->prefix('admin')->name('admin.')->group(function () { Route::get('uploads/moderation', function () { return Inertia::render('Admin/UploadQueue'); })->middleware('admin.moderation')->name('uploads.moderation'); Route::resource('artworks', \App\Http\Controllers\Admin\ArtworkController::class)->except(['show']); });