Upload beautify
This commit is contained in:
@@ -32,4 +32,98 @@ Route::prefix('v1')->name('api.v1.')->group(function () {
|
||||
// Category artworks (Category route-model binding uses slug)
|
||||
Route::get('categories/{category}/artworks', [\App\Http\Controllers\Api\ArtworkController::class, 'categoryArtworks'])
|
||||
->name('categories.artworks');
|
||||
|
||||
// Personalized feed (auth required)
|
||||
Route::middleware(['web', 'auth'])->get('feed', [\App\Http\Controllers\Api\FeedController::class, 'index'])
|
||||
->name('feed');
|
||||
});
|
||||
|
||||
Route::middleware(['web', 'auth'])->prefix('artworks')->name('api.artworks.')->group(function () {
|
||||
Route::post('/', [\App\Http\Controllers\Api\ArtworkController::class, 'store'])
|
||||
->name('store');
|
||||
});
|
||||
|
||||
Route::middleware(['web', 'auth'])->prefix('uploads')->name('api.uploads.')->group(function () {
|
||||
Route::post('init', [\App\Http\Controllers\Api\UploadController::class, 'init'])
|
||||
->middleware('throttle:uploads-init')
|
||||
->name('init');
|
||||
|
||||
Route::post('preload', [\App\Http\Controllers\Api\UploadController::class, 'preload'])
|
||||
->middleware('throttle:uploads-init')
|
||||
->name('preload');
|
||||
|
||||
Route::post('{id}/autosave', [\App\Http\Controllers\Api\UploadController::class, 'autosave'])
|
||||
->middleware('throttle:uploads-finish')
|
||||
->name('autosave');
|
||||
|
||||
Route::post('{id}/publish', [\App\Http\Controllers\Api\UploadController::class, 'publish'])
|
||||
->middleware('throttle:uploads-finish')
|
||||
->name('publish');
|
||||
|
||||
Route::get('{id}/status', [\App\Http\Controllers\Api\UploadController::class, 'processingStatus'])
|
||||
->middleware('throttle:uploads-status')
|
||||
->name('processing-status');
|
||||
|
||||
Route::post('chunk', [\App\Http\Controllers\Api\UploadController::class, 'chunk'])
|
||||
->middleware('throttle:uploads-init')
|
||||
->name('chunk');
|
||||
|
||||
Route::post('finish', [\App\Http\Controllers\Api\UploadController::class, 'finish'])
|
||||
->middleware('throttle:uploads-finish')
|
||||
->name('finish');
|
||||
|
||||
Route::post('cancel', [\App\Http\Controllers\Api\UploadController::class, 'cancel'])
|
||||
->middleware('throttle:uploads-finish')
|
||||
->name('cancel');
|
||||
|
||||
Route::get('status/{id}', [\App\Http\Controllers\Api\UploadController::class, 'status'])
|
||||
->middleware('throttle:uploads-status')
|
||||
->name('status');
|
||||
});
|
||||
|
||||
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');
|
||||
|
||||
Route::post('{id}/approve', [\App\Http\Controllers\Api\Admin\UploadModerationController::class, 'approve'])
|
||||
->whereUuid('id')
|
||||
->name('approve');
|
||||
|
||||
Route::post('{id}/reject', [\App\Http\Controllers\Api\Admin\UploadModerationController::class, 'reject'])
|
||||
->whereUuid('id')
|
||||
->name('reject');
|
||||
});
|
||||
|
||||
Route::middleware(['web', 'auth', 'admin.moderation'])->prefix('admin/reports')->name('api.admin.reports.')->group(function () {
|
||||
Route::get('similar-artworks', [\App\Http\Controllers\Api\Admin\SimilarArtworkReportController::class, 'index'])
|
||||
->name('similar-artworks');
|
||||
|
||||
Route::get('feed-performance', [\App\Http\Controllers\Api\Admin\FeedPerformanceReportController::class, 'index'])
|
||||
->name('feed-performance');
|
||||
});
|
||||
|
||||
Route::post('analytics/similar-artworks', [\App\Http\Controllers\Api\SimilarArtworkAnalyticsController::class, 'store'])
|
||||
->middleware('throttle:uploads-status')
|
||||
->name('api.analytics.similar-artworks.store');
|
||||
|
||||
Route::middleware(['web', 'auth'])->post('analytics/feed', [\App\Http\Controllers\Api\FeedAnalyticsController::class, 'store'])
|
||||
->middleware('throttle:uploads-status')
|
||||
->name('api.analytics.feed.store');
|
||||
|
||||
Route::middleware(['web', 'auth'])->prefix('discovery')->name('api.discovery.')->group(function () {
|
||||
Route::post('events', [\App\Http\Controllers\Api\DiscoveryEventController::class, 'store'])
|
||||
->middleware('throttle:uploads-status')
|
||||
->name('events.store');
|
||||
});
|
||||
|
||||
// Tag system (auth-protected; 404-only ownership checks handled in requests/controllers)
|
||||
Route::middleware(['web', 'auth'])->prefix('tags')->name('api.tags.')->group(function () {
|
||||
Route::get('search', [\App\Http\Controllers\Api\TagController::class, 'search'])->name('search');
|
||||
Route::get('popular', [\App\Http\Controllers\Api\TagController::class, 'popular'])->name('popular');
|
||||
});
|
||||
|
||||
Route::middleware(['web', 'auth'])->prefix('artworks')->name('api.artworks.tags.')->group(function () {
|
||||
Route::post('{id}/tags', [\App\Http\Controllers\Api\ArtworkTagController::class, 'store'])->whereNumber('id')->name('store');
|
||||
Route::put('{id}/tags', [\App\Http\Controllers\Api\ArtworkTagController::class, 'update'])->whereNumber('id')->name('update');
|
||||
Route::delete('{id}/tags/{tag}', [\App\Http\Controllers\Api\ArtworkTagController::class, 'destroy'])->whereNumber('id')->name('destroy');
|
||||
});
|
||||
|
||||
@@ -2,7 +2,15 @@
|
||||
|
||||
use Illuminate\Foundation\Inspiring;
|
||||
use Illuminate\Support\Facades\Artisan;
|
||||
use App\Uploads\Services\CleanupService;
|
||||
|
||||
Artisan::command('inspire', function () {
|
||||
$this->comment(Inspiring::quote());
|
||||
})->purpose('Display an inspiring quote');
|
||||
|
||||
Artisan::command('uploads:cleanup {--limit=100 : Maximum drafts to clean in one run}', function (): void {
|
||||
$limit = (int) $this->option('limit');
|
||||
$deleted = app(CleanupService::class)->cleanupStaleDrafts($limit);
|
||||
|
||||
$this->info("Uploads cleanup deleted {$deleted} draft(s).");
|
||||
})->purpose('Delete stale draft uploads and temporary files');
|
||||
|
||||
@@ -46,25 +46,6 @@ Route::get('/news/{id}/{slug?}', [NewsController::class, 'show'])->where('id', '
|
||||
Route::get('/categories', [CategoryController::class, 'index'])->name('legacy.categories');
|
||||
Route::get('/category/{group}/{slug?}/{id?}', [CategoryController::class, 'show'])->name('legacy.category');
|
||||
|
||||
// Short legacy routes for top-level category URLs like /Photography/3
|
||||
// Short legacy routes for top-level category URLs (mapped to CategoryController@show)
|
||||
/*
|
||||
Route::get('/Photography/{id}', [CategoryController::class, 'show'])
|
||||
->defaults('group', 'Photography')
|
||||
->where('id', '\\d+');
|
||||
|
||||
Route::get('/Wallpapers/{id}', [CategoryController::class, 'show'])
|
||||
->defaults('group', 'Wallpapers')
|
||||
->where('id', '\\d+');
|
||||
|
||||
Route::get('/Skins/{id}', [CategoryController::class, 'show'])
|
||||
->defaults('group', 'Skins')
|
||||
->where('id', '\\d+');
|
||||
|
||||
Route::get('/Other/{id}', [CategoryController::class, 'show'])
|
||||
->defaults('group', 'Other')
|
||||
->where('id', '\\d+');
|
||||
*/
|
||||
Route::get('/browse', [BrowseController::class, 'index'])->name('legacy.browse');
|
||||
Route::get('/featured', [FeaturedArtworksController::class, 'index'])->name('legacy.featured');
|
||||
Route::get('/featured-artworks', [FeaturedArtworksController::class, 'index'])->name('legacy.featured_artworks');
|
||||
@@ -104,9 +85,6 @@ Route::middleware('auth')->get('/recieved-comments', [ReceivedCommentsController
|
||||
// User account settings (legacy /user)
|
||||
Route::middleware('auth')->match(['get','post'], '/user', [LegacyUserController::class, 'index'])->name('legacy.user');
|
||||
|
||||
// Content-type landing pages (legacy look)
|
||||
Route::get('/photography', [PhotographyController::class, 'index'])->name('legacy.photography');
|
||||
|
||||
Route::get('/today-in-history', [TodayInHistoryController::class, 'index'])->name('legacy.today_in_history');
|
||||
Route::get('/today-downloads', [TodayDownloadsController::class, 'index'])->name('legacy.today_downloads');
|
||||
|
||||
|
||||
112
routes/web.php
112
routes/web.php
@@ -1,8 +1,12 @@
|
||||
<?php
|
||||
|
||||
use App\Http\Controllers\ProfileController;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use App\Http\Controllers\ProfileController;
|
||||
use App\Models\ContentType;
|
||||
use App\Http\Controllers\AvatarController;
|
||||
use App\Http\Controllers\ManageController;
|
||||
use App\Http\Controllers\Dashboard\ArtworkController as DashboardArtworkController;
|
||||
use Inertia\Inertia;
|
||||
|
||||
// Legacy routes are defined in routes/legacy.php and provide the site's
|
||||
// legacy pages (art, forum, news, profile, etc.). We keep the auth routes
|
||||
@@ -22,33 +26,107 @@ Route::middleware(['auth'])->prefix('dashboard')->name('dashboard.')->group(func
|
||||
|
||||
Route::middleware('auth')->group(function () {
|
||||
Route::get('/profile', [ProfileController::class, 'edit'])->name('profile.edit');
|
||||
Route::patch('/profile', [ProfileController::class, 'update'])->name('profile.update');
|
||||
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');
|
||||
|
||||
// Artwork public show (slug-based). This must come before the category route so the artwork
|
||||
// slug (last segment) is matched correctly while allowing multi-segment category paths.
|
||||
Route::get('/{contentTypeSlug}/{categoryPath}/{artwork}', [\App\Http\Controllers\ArtworkController::class, 'show'])
|
||||
->where([
|
||||
'contentTypeSlug' => 'photography|wallpapers|skins|other',
|
||||
'categoryPath' => '.*',
|
||||
'artwork' => '[A-Za-z0-9\-]+'
|
||||
])
|
||||
// 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');
|
||||
|
||||
// New slug-based category routes (e.g., /photography/audio/winamp)
|
||||
Route::get('/{contentTypeSlug}/{categoryPath?}', [\App\Http\Controllers\CategoryPageController::class, 'show'])
|
||||
Route::get('/{contentTypeSlug}/{path?}', [\App\Http\Controllers\ContentRouterController::class, 'handle'])
|
||||
->where('contentTypeSlug', 'photography|wallpapers|skins|other')
|
||||
->where('categoryPath', '.*')
|
||||
->name('category.slug');
|
||||
->where('path', '.*')
|
||||
->name('content.route');
|
||||
|
||||
|
||||
|
||||
use App\Http\Controllers\ManageController;
|
||||
|
||||
Route::middleware(['auth'])->group(function () {
|
||||
Route::get('/manage', [ManageController::class, 'index'])->name('manage');
|
||||
@@ -59,6 +137,10 @@ Route::middleware(['auth'])->group(function () {
|
||||
|
||||
// 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']);
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user