user(); if (!$user) { return response()->json(['message' => 'Unauthorized'], 401); } $this->ensurePublicStorageReady(); $data = $request->validate([ 'file' => [ 'required', 'image', 'mimes:jpg,jpeg,png,gif,webp', 'max:2048', 'dimensions:max_width=150,max_height=150', ], ]); if ($user->avatar_path) { Storage::disk('public')->delete($user->avatar_path); } $path = $data['file']->store('avatars', 'public'); $user->avatar_path = $path; $user->save(); return response()->json([ 'path' => $path, 'url' => Storage::url($path), ]); } public function storeLogo(Request $request): JsonResponse { $user = $request->user(); if (!$user || !$user->roles()->where('name', 'ROLE_ADMIN')->exists()) { return response()->json(['message' => 'Forbidden'], 403); } $this->ensurePublicStorageReady(); $data = $request->validate([ 'file' => ['required', 'file', 'mimes:jpg,jpeg,png,gif,webp,svg,ico', 'max:5120'], ]); $path = $data['file']->store('logos', 'public'); return response()->json([ 'path' => $path, 'url' => Storage::url($path), ]); } public function storeFavicon(Request $request): JsonResponse { $user = $request->user(); if (!$user || !$user->roles()->where('name', 'ROLE_ADMIN')->exists()) { return response()->json(['message' => 'Forbidden'], 403); } $this->ensurePublicStorageReady(); $data = $request->validate([ 'file' => ['required', 'file', 'mimes:png,ico', 'max:2048'], ]); $path = $data['file']->store('favicons', 'public'); return response()->json([ 'path' => $path, 'url' => Storage::url($path), ]); } private function ensurePublicStorageReady(): void { $storagePublic = storage_path('app/public'); $publicStorage = public_path('storage'); if (file_exists($storagePublic) && !is_dir($storagePublic)) { @rename($storagePublic, $storagePublic.'.bak.'.date('Ymd_His')); } if (!is_dir($storagePublic) && !@mkdir($storagePublic, 0775, true) && !is_dir($storagePublic)) { throw new RuntimeException('Failed to create storage/app/public directory.'); } if (is_link($publicStorage)) { $target = readlink($publicStorage); $resolved = $target !== false ? realpath(dirname($publicStorage).DIRECTORY_SEPARATOR.$target) : false; $expected = realpath($storagePublic); if ($resolved === $expected) { $this->ensureUploadSubdirs($storagePublic); return; } @unlink($publicStorage); } elseif (is_dir($publicStorage)) { File::copyDirectory($publicStorage, $storagePublic); File::deleteDirectory($publicStorage); } elseif (file_exists($publicStorage)) { @rename($publicStorage, $publicStorage.'.bak.'.date('Ymd_His')); } if (!@symlink($storagePublic, $publicStorage) && !is_link($publicStorage)) { throw new RuntimeException('Failed to create public/storage symlink.'); } $this->ensureUploadSubdirs($storagePublic); } private function ensureUploadSubdirs(string $storagePublic): void { foreach (['avatars', 'favicons', 'logos', 'rank-badges'] as $dir) { $path = $storagePublic.DIRECTORY_SEPARATOR.$dir; if (!is_dir($path)) { @mkdir($path, 0775, true); } } } }