middleware('throttle:public'); Route::get('/f/{path}', function($path){ // Sanitize: strip directory traversal, only allow basename $path = basename($path); $storpath = storage_path('app/public/uploads/' . $path); if (!file_exists($storpath)){ abort(404, 'File not found.'); } $mime = Storage::mimeType('public/uploads/' . $path); return response()->file($storpath, [ 'Content-Type' => $mime, 'Content-Disposition' => 'inline; filename="'.$path.'"' ]); })->middleware('throttle:public'); Route::get('/mu/{path}', function($path){ // Sanitize: strip directory traversal, only allow basename $path = basename($path); $storpath = storage_path('app/public/band/' . $path); if (!file_exists($storpath)){ abort(404, 'File not found.'); } $mime = Storage::mimeType('public/band/' . $path); return response()->file($storpath, [ 'Content-Type' => $mime, 'Content-Disposition' => 'inline; filename="'.$path.'"' ]); })->middleware('throttle:public'); // Presence - Public routes Route::get('/me', [PresenceController::class, 'index']); Route::get('/me/post/{id}', [PresenceController::class, 'show']); Route::post('/me/post/{id}/comment', [PresenceController::class, 'storeComment']); Route::post('/me/contact', [PresenceController::class, 'contact']); // Presence - Admin routes (auth required) Route::middleware('auth')->prefix('me')->group(function () { Route::get('/admin', [PresenceController::class, 'admin']); Route::post('/status', [PresenceController::class, 'updateStatus']); Route::post('/post', [PresenceController::class, 'storePost']); Route::put('/post/{id}', [PresenceController::class, 'updatePost']); Route::delete('/post/{id}', [PresenceController::class, 'deletePost']); Route::delete('/comment/{id}', [PresenceController::class, 'deleteComment']); }); Route::get('/test', [SiteController::class, 'test']); // Auth-protected routes Route::middleware('auth')->group(function () { Route::get('/profile', [ProfileController::class, 'edit'])->name('profile.edit'); Route::patch('/profile', [ProfileController::class, 'update'])->name('profile.update'); Route::delete('/profile', [ProfileController::class, 'destroy'])->name('profile.destroy'); Route::get('/dashboard', [DashboardController::class, 'index'])->name('dashboard'); Route::get('/dashboard/stats', [DashboardController::class, 'statistics'])->name('dashboard.stats'); Route::resource('l', LinkController::class)->only(['create', 'store', 'edit', 'update', 'destroy']); Route::resource('w', WritingController::class)->only(['create', 'store', 'edit', 'update', 'destroy']); Route::resource('f', \App\Http\Controllers\FileController::class)->only(['create', 'store', 'edit', 'update', 'destroy']); }); // Admin-only routes Route::middleware(['auth', 'throttle:admin', \App\Http\Middleware\Admin::class])->group(function () { Route::get('/admin', [AdminController::class, 'index'])->name('admin'); Route::post('/l/import', [LinkController::class, 'import'])->name('l.import'); Route::get('/env', [SiteController::class, 'env']); }); Route::post('/4', [SiteController::class, 'search4chan']); // Public read-only routes Route::resource('w', WritingController::class)->only(['index', 'show']); Route::resource('l', LinkController::class)->only(['index', 'show']); Route::resource('f', \App\Http\Controllers\FileController::class)->only(['index', 'show']); // Session updates - auth required, whitelisted keys only Route::post('/update-session', function(Request $req){ if ($req->input('key') && $req->input('value')){ $allowed = ['theme', 'locale', 'sidebar']; if (!in_array($req->input('key'), $allowed)) { abort(403, 'Session key not allowed.'); } session()->put($req->input('key'), $req->input('value')); return redirect()->back(); } })->middleware('auth'); require __DIR__.'/auth.php'; /*Route::get('/toy/{v?}', function($v = null){ if (is_null($v)){ return view('toys.index'); } // Only allow alphanumeric and hyphens if (!preg_match('/^[a-zA-Z0-9\-]+$/', $v)) { abort(404); } if (!view()->exists('toys.'.$v)) { abort(404); } return view('toys.'.$v); });*/ // Audio stream - requires auth to prevent abuse Route::get('/stream/audio', function() { $command = 'ffmpeg -re -i http://somafm.com/vaporwaves.pls -f mp3 -'; return response()->stream(function() use ($command) { $handle = popen($command, 'r'); if ($handle) { while (!feof($handle)) { $chunk = fread($handle, 8192); if ($chunk !== false) { echo $chunk; flush(); } } pclose($handle); } }, 200, [ 'Content-Type' => 'audio/mpeg', 'Cache-Control' => 'no-cache', 'X-Accel-Buffering' => 'no' ]); })->middleware(['auth', 'throttle:expensive']); Route::get('/newtab', function() { return view('newtab'); }); Route::get('/do', function(){ $data = []; $file = storage_path('app/todo.json'); $data['items'] = file_exists($file) ? json_decode(file_get_contents($file), true) ?? [] : []; return view('do', $data); }); Route::post('/do', [SiteController::class, 'updateTodo'])->middleware('auth'); // API-style todo endpoint — token auth, no CSRF needed Route::post('/api/do', [SiteController::class, 'updateTodoWithToken'])->middleware('throttle:public'); // Catch-all: only allow specific whitelisted views Route::get('/{v}', function($v){ $allowed = ['notes', 'kyanite', 'marked', 'v', '4', 'matrix', 'psql']; if (!in_array($v, $allowed)) { abort(404); } return view($v); })->where('v', '[a-zA-Z0-9\-]+');