summaryrefslogtreecommitdiff
path: root/routes
diff options
context:
space:
mode:
authorThomas Grothe <grothe.tr@gmail.com>2026-04-11 17:49:42 -0400
committerThomas Grothe <grothe.tr@gmail.com>2026-04-11 17:49:42 -0400
commitd4f97aa956be051dd5b9a184557106dc7de112ac (patch)
treee4c73c93408b5fbc4cc1b92cabcaceaaebbb1833 /routes
parentbcac54576d7309ac0471a7be5664c5a4e8d7349e (diff)
parent054c19bf65beb43d0dd6137f9bf16cf8ca9f6190 (diff)
Merge remote-tracking branch 'origin/main'
Diffstat (limited to 'routes')
-rwxr-xr-xroutes/web.php104
1 files changed, 53 insertions, 51 deletions
diff --git a/routes/web.php b/routes/web.php
index e32edfa..e2cf097 100755
--- a/routes/web.php
+++ b/routes/web.php
@@ -1,71 +1,49 @@
<?php
use Illuminate\Support\Facades\Route;
+use Illuminate\Support\Facades\Storage;
use App\Http\Controllers\SiteController;
use App\Http\Controllers\ProfileController;
use App\Http\Controllers\WritingController;
use App\Http\Controllers\LinkController;
use App\Http\Controllers\DashboardController;
use App\Http\Controllers\PresenceController;
+use App\Http\Controllers\AdminController;
use Illuminate\Http\Request;
Route::get('/', function () {
return view('home');
});
-Route::get('/env', [SiteController::class, 'env']);
+Route::get('/dq', [SiteController::class, 'duneQuote'])->middleware('throttle:public');
-Route::get('/dq', [SiteController::class, 'duneQuote']);
-
-//temporarily desabled for security
-//Route::post('/f', [SiteController::class, 'uploadFiles']); //TODO later use file resource or FPR service
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)){
- return response()->json(['error' => 'file not found ' . $storpath]);
+ abort(404, 'File not found.');
}
- $mime = Storage::mimeType($storpath);
-
- //return response()->json([
- // 'success' => true,
- // 'message' => 'file uploaded',
- // 'url' => $storpath,
- //]);
-
-// this is how you would return the file which would download the file in browser
+ $mime = Storage::mimeType('public/uploads/' . $path);
return response()->file($storpath, [
'Content-Type' => $mime,
'Content-Disposition' => 'inline; filename="'.$path.'"'
]);
-});
-
-/*Route::get('/yt', function () {
- return view('youtube');
-});*/
-
-// Get video info and direct stream URL (recommended approach)
-//Route::post('/yt/info', [YouTubeController::class, 'getVideoInfo']);
-// Stream video through Laravel proxy (with range support)
-//Route::get('/yt/stream', [YouTubeController::class, 'streamVideo']);
-// Stream directly via yt-dlp (not recommended - no seeking support)
-//Route::get('/yt/stream-direct', [YouTubeController::class, 'streamDirect']);
-// Get available formats for a video
-//Route::post('/yt/formats', [YouTubeController::class, 'getFormats']);
+})->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)){
- return response()->json(['error' => 'file not found ' . $storpath]);
+ abort(404, 'File not found.');
}
- $mime = Storage::mimeType($storpath);
+ $mime = Storage::mimeType('public/band/' . $path);
return response()->file($storpath, [
'Content-Type' => $mime,
'Content-Disposition' => 'inline; filename="'.$path.'"'
]);
-});
-
-Route::resource('w', WritingController::class);
-Route::resource('l', LinkController::class);
+})->middleware('throttle:public');
// Presence - Public routes
Route::get('/me', [PresenceController::class, 'index']);
@@ -85,43 +63,62 @@ Route::middleware('auth')->prefix('me')->group(function () {
Route::get('/test', [SiteController::class, 'test']);
-Route::get('/4', [SiteController::class, 'search4chan']);
-
-Route::get('/dashboard', function () {
- return view('dashboard');
-})->middleware(['auth', 'verified'])->name('dashboard');
-
+// 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::get('/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){
+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 endpoint - streams from ffmpeg
+// Audio stream - requires auth to prevent abuse
Route::get('/stream/audio', function() {
- // Configure ffmpeg command to output mp3 stream
- // Example: stream from microphone or other source
- //$command = 'ffmpeg -re -i /dev/null -f mp3 -';
- //$command = 'ffmpeg -re -f pulse -i defaul -f mp3 -';
- $command = 'ffmpeg -re -i http://somafm.com/vaporwaves.pls :-f mp3 -';
+ $command = 'ffmpeg -re -i http://somafm.com/vaporwaves.pls -f mp3 -';
return response()->stream(function() use ($command) {
$handle = popen($command, 'r');
@@ -141,12 +138,17 @@ Route::get('/stream/audio', function() {
'Cache-Control' => 'no-cache',
'X-Accel-Buffering' => 'no'
]);
-});
+})->middleware(['auth', 'throttle:expensive']);
Route::get('/newtab', function() {
return view('newtab');
});
+// Catch-all: only allow specific whitelisted views
Route::get('/{v}', function($v){
+ $allowed = ['notes', 'kyanite', 'marked', 'v'];
+ if (!in_array($v, $allowed)) {
+ abort(404);
+ }
return view($v);
-});
+})->where('v', '[a-zA-Z0-9\-]+');