diff --git a/app/FileManagerFile.php b/app/FileManagerFile.php index 31ff26ad..6964bfd9 100644 --- a/app/FileManagerFile.php +++ b/app/FileManagerFile.php @@ -114,4 +114,14 @@ class FileManagerFile extends Model { return $this->hasOne('App\FileManagerFolder', 'unique_id', 'folder_id'); } + + /** + * Get sharing attributes + * + * @return \Illuminate\Database\Eloquent\Relations\HasOne + */ + public function shared() + { + return $this->hasOne('App\Share', 'item_id', 'unique_id'); + } } diff --git a/app/FileManagerFolder.php b/app/FileManagerFolder.php index 7a445acf..f2e00ba0 100644 --- a/app/FileManagerFolder.php +++ b/app/FileManagerFolder.php @@ -165,6 +165,16 @@ class FileManagerFolder extends Model return $this->hasMany('App\FileManagerFolder', 'parent_id', 'unique_id')->withTrashed(); } + /** + * Get sharing attributes + * + * @return \Illuminate\Database\Eloquent\Relations\HasOne + */ + public function shared() + { + return $this->hasOne('App\Share', 'item_id', 'unique_id'); + } + // Delete all folder childrens public static function boot() { diff --git a/app/Http/Controllers/AppFunctionsController.php b/app/Http/Controllers/AppFunctionsController.php index 083a7c33..fb20afed 100644 --- a/app/Http/Controllers/AppFunctionsController.php +++ b/app/Http/Controllers/AppFunctionsController.php @@ -2,8 +2,9 @@ namespace App\Http\Controllers; -use Illuminate\Http\Request; use Illuminate\Support\Facades\File; +use Illuminate\Support\Facades\Auth; +use Illuminate\Http\Request; use Response; class AppFunctionsController extends Controller @@ -17,28 +18,4 @@ class AppFunctionsController extends Controller { return view("index"); } - - /** - * Get file - * - * @param $filename - * @return mixed - */ - public function get_avatar($basename) - { - // Get file path - $path = storage_path() . '/app/avatars/' . $basename; - - // Check if file exist - if (!File::exists($path)) abort(404); - - $file = File::get($path); - $type = File::mimeType($path); - - // Create response - $response = Response::make($file, 200); - $response->header("Content-Type", $type); - - return $response; - } } diff --git a/app/Http/Controllers/Auth/AuthController.php b/app/Http/Controllers/Auth/AuthController.php index 98e6147a..6170a55c 100644 --- a/app/Http/Controllers/Auth/AuthController.php +++ b/app/Http/Controllers/Auth/AuthController.php @@ -136,7 +136,7 @@ class AuthController extends Controller 'client_secret' => config('services.passport.client_secret'), 'username' => $request->email, 'password' => $request->password, - 'scope' => '', + 'scope' => 'master', ]); return Request::create(url('/oauth/token'), 'POST', $request->all()); diff --git a/app/Http/Controllers/FileAccessController.php b/app/Http/Controllers/FileAccessController.php new file mode 100644 index 00000000..f81907a8 --- /dev/null +++ b/app/Http/Controllers/FileAccessController.php @@ -0,0 +1,125 @@ +where('user_id', $user_id) + ->where('basename', $filename) + ->firstOrFail(); + + // Get file path + $path = storage_path() . '/app/file-manager/' . $file->basename; + + // Check if file exist + if (!File::exists($path)) abort(404); + + $file = File::get($path); + $type = File::mimeType($path); + $size = File::size($path); + + // Create response + $response = Response::make($file, 200); + $response->header("Content-Type", $type); + $response->header("Content-Disposition", 'attachment; filename=' . $filename); + $response->header("Content-Length", $size); + $response->header("Accept-Ranges", "bytes"); + $response->header("Content-Range", "bytes 0-" . $size . "/" . $size); + + return $response; + } + + /** + * Get avatar + * + * @param $basename + * @return mixed + */ + public function get_avatar($basename) + { + // Get file path + $path = storage_path() . '/app/avatars/' . $basename; + + // Check if file exist + if (!File::exists($path)) abort(404); + + $file = File::get($path); + $type = File::mimeType($path); + + // Create response + $response = Response::make($file, 200); + $response->header("Content-Type", $type); + + return $response; + } + + /** + * Get image thumbnail + * + * @param $filename + * @return mixed + */ + public function get_thumbnail($filename) + { + // Get user id + $user_id = Auth::id(); + + // Get file record + $file = FileManagerFile::withTrashed() + ->where('user_id', $user_id) + ->where('thumbnail', $filename) + ->firstOrFail(); + + /* if ($request->has('token')) { + + // Get sharing record + $shared = Share::where('token', $request->token)->firstOrFail(); + + // Get all children folders + $foldersIds = FileManagerFolder::with('folders:id,parent_id,unique_id,name') + ->where('user_id', $user_id) + ->where('parent_id', $shared->item_id) + ->get(); + + // Get all authorized parent folders by shared folder as root of tree + $authorized_parent_folder_ids = Arr::flatten([filter_folders_ids($foldersIds), $shared->item_id]); + + // Check user access + if ( ! in_array($file->folder_id, $authorized_parent_folder_ids)) abort(401); + }*/ + + // Get file path + $path = storage_path() . '/app/file-manager/' . $file->getOriginal('thumbnail'); + + // Check if file exist + if (!File::exists($path)) abort(404); + + $file = File::get($path); + $type = File::mimeType($path); + + // Create response + $response = Response::make($file, 200); + $response->header("Content-Type", $type); + + return $response; + } +} diff --git a/app/Http/Controllers/FileBrowser/BrowseController.php b/app/Http/Controllers/FileBrowser/BrowseController.php new file mode 100644 index 00000000..ffbfa4dc --- /dev/null +++ b/app/Http/Controllers/FileBrowser/BrowseController.php @@ -0,0 +1,198 @@ +with(['trashed_folders']) + ->where('user_id', $user_id) + ->get(['parent_id', 'unique_id', 'name']); + + $folders = FileManagerFolder::onlyTrashed() + ->where('user_id', $user_id) + ->whereIn('unique_id', filter_folders_ids($folders_trashed)) + ->get(); + + // Get files trashed + $files_trashed = FileManagerFile::onlyTrashed() + ->where('user_id', $user_id) + ->whereNotIn('folder_id', array_values(array_unique(recursiveFind($folders_trashed->toArray(), 'unique_id')))) + ->get(); + + // Collect folders and files to single array + return collect([$folders, $files_trashed])->collapse(); + } + + /** + * Get user shared items + * + * @return Collection + */ + public function shared() + { + // Get user + $user_id = Auth::id(); + + // Get shared folders and files + $folder_ids = Share::where('user_id', $user_id) + ->where('type', 'folder') + ->pluck('item_id'); + + $file_ids = Share::where('user_id', $user_id) + ->where('type', '!=', 'folder') + ->pluck('item_id'); + + // Get folders and files + $folders = FileManagerFolder::with(['parent', 'shared:token,id,item_id,permission,protected']) + ->where('user_id', $user_id) + ->whereIn('unique_id', $folder_ids) + ->get(); + + $files = FileManagerFile::with(['parent', 'shared:token,id,item_id,permission,protected']) + ->where('user_id', $user_id) + ->whereIn('unique_id', $file_ids) + ->get(); + + // Collect folders and files to single array + return collect([$folders, $files])->collapse(); + } + + /** + * Get directory with files + * + * @param Request $request + * @param $unique_id + * @return Collection + */ + public function folder(Request $request, $unique_id) + { + // Get user + $user_id = Auth::id(); + + // Get folder trash items + if ($request->query('trash')) { + + // Get folders and files + $folders = FileManagerFolder::onlyTrashed() + ->where('user_id', $user_id) + ->with('parent') + ->where('parent_id', $unique_id) + ->get(); + + $files = FileManagerFile::onlyTrashed() + ->where('user_id', $user_id) + ->with('parent') + ->where('folder_id', $unique_id) + ->get(); + + // Collect folders and files to single array + return collect([$folders, $files])->collapse(); + } + + // Get folders and files + $folders = FileManagerFolder::with(['parent', 'shared:token,id,item_id,permission,protected']) + ->where('user_id', $user_id) + ->where('parent_id', $unique_id) + ->get(); + + $files = FileManagerFile::with(['parent', 'shared:token,id,item_id,permission,protected']) + ->where('user_id', $user_id) + ->where('folder_id', $unique_id) + ->get(); + + // Collect folders and files to single array + return collect([$folders, $files])->collapse(); + } + + /** + * Get user folder tree + * + * @return array + */ + public function folder_tree() { + + $folders = FileManagerFolder::with('folders:id,parent_id,unique_id,name') + ->where('parent_id', 0) + ->where('user_id', Auth::id()) + ->get(['id', 'parent_id', 'unique_id', 'name']); + + return [ + [ + 'unique_id' => 0, + 'name' => __('vuefilemanager.home'), + 'location' => 'base', + 'folders' => $folders, + ] + ]; + } + + /** + * Search files + * + * @param Request $request + * @return \Illuminate\Database\Eloquent\Collection + */ + public function search(Request $request) + { + // Validate request + $validator = Validator::make($request->all(), [ + 'query' => 'required|string', + ]); + + // Return error + if ($validator->fails()) abort(400, 'Bad input'); + + // Get user + $user_id = Auth::id(); + + // Search files id db + $searched_files = FileManagerFile::search($request->input('query')) + ->where('user_id', $user_id) + ->get(); + $searched_folders = FileManagerFolder::search($request->input('query')) + ->where('user_id', $user_id) + ->get(); + + // Collect folders and files to single array + return collect([$searched_folders, $searched_files])->collapse(); + } + + /** + * Get file record + * + * @param $unique_id + * @return mixed + */ + public function file_detail($unique_id) + { + // Get user id + $user_id = Auth::id(); + + return FileManagerFile::with(['shared:token,id,item_id,permission,protected']) + ->where('user_id', $user_id) + ->where('unique_id', $unique_id) + ->firstOrFail(); + } +} diff --git a/app/Http/Controllers/FileManagerController.php b/app/Http/Controllers/FileFunctions/EditController.php similarity index 54% rename from app/Http/Controllers/FileManagerController.php rename to app/Http/Controllers/FileFunctions/EditController.php index 2a136c44..5d4f960a 100644 --- a/app/Http/Controllers/FileManagerController.php +++ b/app/Http/Controllers/FileFunctions/EditController.php @@ -1,14 +1,13 @@ with(['trashed_folders']) - ->where('user_id', $user_id) - ->get(['parent_id', 'unique_id', 'name']); - - $folders = FileManagerFolder::onlyTrashed() - ->where('user_id', $user_id) - ->whereIn('unique_id', filter_folders_ids($folders_trashed)) - ->get(); - - // Get files trashed - $files_trashed = FileManagerFile::onlyTrashed() - ->where('user_id', $user_id) - ->whereNotIn('folder_id', array_values(array_unique(recursiveFind($folders_trashed->toArray(), 'unique_id')))) - ->get(); - - // Collect folders and files to single array - return collect([$folders, $files_trashed])->collapse(); - } - - /** - * Get directory with files - * - * @return \Illuminate\Support\Collection - */ - public function folder(Request $request, $unique_id) - { - // Get user - $user_id = Auth::id(); - - // Get folder trash items - if ($request->query('trash')) { - - // Get folders and files - $folders = FileManagerFolder::onlyTrashed() - ->where('user_id', $user_id) - ->with('parent') - ->where('parent_id', $unique_id) - ->get(); - - $files = FileManagerFile::onlyTrashed() - ->where('user_id', $user_id) - ->with('parent') - ->where('folder_id', $unique_id) - ->get(); - - // Collect folders and files to single array - return collect([$folders, $files])->collapse(); - } - - // Get folders and files - $folders = FileManagerFolder::with('parent') - ->where('user_id', $user_id) - ->where('parent_id', $unique_id) - ->get(); - - $files = FileManagerFile::with('parent') - ->where('user_id', $user_id) - ->where('folder_id', $unique_id) - ->get(); - - // Collect folders and files to single array - return collect([$folders, $files])->collapse(); - } - - /** - * Search files - * - * @param Request $request - * @return \Illuminate\Database\Eloquent\Collection - */ - public function search(Request $request) - { - // Validate request - $validator = Validator::make($request->all(), [ - 'query' => 'required|string', - ]); - - // Return error - if ($validator->fails()) abort(400, 'Bad input'); - - // Get user - $user_id = Auth::id(); - - // Search files id db - $searched_files = FileManagerFile::search($request->input('query')) - ->where('user_id', $user_id) - ->get(); - $searched_folders = FileManagerFolder::search($request->input('query')) - ->where('user_id', $user_id) - ->get(); - - // Collect folders and files to single array - return collect([$searched_folders, $searched_files])->collapse(); - } - /** * Create new folder * @@ -297,87 +188,6 @@ class FileManagerController extends Controller } } - /** - * Empty user trash - * - * @return \Illuminate\Contracts\Routing\ResponseFactory|\Illuminate\Http\Response - */ - public function empty_trash() - { - // Get user id - $user_id = Auth::id(); - - // Get files and folders - $folders = FileManagerFolder::onlyTrashed()->where('user_id', $user_id)->get(); - $files = FileManagerFile::onlyTrashed()->where('user_id', $user_id)->get(); - - // Force delete folder - $folders->each->forceDelete(); - - // Force delete files - foreach ($files as $file) { - - // Delete file - Storage::disk('local')->delete('/file-manager/' . $file->basename); - - // Delete thumbnail if exist - if ($file->thumbnail) Storage::disk('local')->delete('/file-manager/' . $file->getOriginal('thumbnail')); - - // Delete file permanently - $file->forceDelete(); - } - - // Return response - return response('Done!', 200); - } - - /** - * Restore item from trash - * - * @param Request $request - */ - public function restore_item(Request $request) - { - // Validate request - $validator = Validator::make($request->all(), [ - 'unique_id' => 'required|integer', - 'type' => 'required|string', - 'to_home' => 'boolean', - ]); - - // Return error - if ($validator->fails()) abort(400, 'Bad input'); - - // Get user id - $user_id = Auth::id(); - - // Get folder - if ($request->type === 'folder') { - - // Get folder - $item = FileManagerFolder::onlyTrashed()->where('user_id', $user_id)->where('unique_id', $request->unique_id)->first(); - - // Restore item to home directory - if ($request->has('to_home') && $request->to_home) { - $item->parent_id = 0; - $item->save(); - } - } else { - - // Get item - $item = FileManagerFile::onlyTrashed()->where('user_id', $user_id)->where('unique_id', $request->unique_id)->first(); - - // Restore item to home directory - if ($request->has('to_home') && $request->to_home) { - $item->folder_id = 0; - $item->save(); - } - } - - // Restore Item - $item->restore(); - } - /** * Upload items * @@ -456,6 +266,7 @@ class FileManagerController extends Controller * Move item * * @param Request $request + * @return \Illuminate\Contracts\Routing\ResponseFactory|\Illuminate\Http\Response */ public function move_item(Request $request) { @@ -492,91 +303,8 @@ class FileManagerController extends Controller } $item->update(); - } - /** - * Get file record - * - * @param $unique_id - * @return mixed - */ - public function get_file_detail($unique_id) - { - // Get user id - $user_id = Auth::id(); - - return FileManagerFile::where('user_id', $user_id)->where('unique_id', $unique_id)->firstOrFail(); - } - - /** - * Get file - * - * @param $filename - * @return mixed - */ - public function get_file($filename) - { - // Get user id - $user_id = Auth::id(); - - // Get file record - $file = FileManagerFile::withTrashed() - ->where('user_id', $user_id) - ->where('basename', $filename) - ->firstOrFail(); - - // Get file path - $path = storage_path() . '/app/file-manager/' . $file->basename; - - // Check if file exist - if (!File::exists($path)) abort(404); - - $file = File::get($path); - $type = File::mimeType($path); - $size = File::size($path); - - // Create response - $response = Response::make($file, 200); - $response->header("Content-Type", $type); - $response->header("Content-Disposition", 'attachment; filename=' . $filename); - $response->header("Content-Length", $size); - $response->header("Accept-Ranges", "bytes"); - $response->header("Content-Range", "bytes 0-" . $size . "/" . $size); - - return $response; - } - - /** - * Get image thumbnail - * - * @param $filename - * @return mixed - */ - public function get_thumbnail($filename) - { - // Get user id - $user_id = Auth::id(); - - // Get file record - $file = FileManagerFile::withTrashed() - ->where('user_id', $user_id) - ->where('thumbnail', $filename) - ->firstOrFail(); - - // Get file path - $path = storage_path() . '/app/file-manager/' . $file->getOriginal('thumbnail'); - - // Check if file exist - if (!File::exists($path)) abort(404); - - $file = File::get($path); - $type = File::mimeType($path); - - // Create response - $response = Response::make($file, 200); - $response->header("Content-Type", $type); - - return $response; + return response('Done!', 204); } /** diff --git a/app/Http/Controllers/FileFunctions/FavouriteController.php b/app/Http/Controllers/FileFunctions/FavouriteController.php new file mode 100644 index 00000000..1bca8335 --- /dev/null +++ b/app/Http/Controllers/FileFunctions/FavouriteController.php @@ -0,0 +1,63 @@ +all(), [ + 'unique_id' => 'required|integer', + ]); + + // Return error + if ($validator->fails()) abort(400, 'Bad input'); + + // Get user + $user = Auth::user(); + + // Add folder to user favourites + $user->favourites()->attach($request->unique_id); + + // Return updated favourites + return $user->favourites->makeHidden(['pivot']); + } + + /** + * Remove folder from user favourites + * + * @param Request $request + * @return \Illuminate\Contracts\Routing\ResponseFactory|\Illuminate\Http\Response + */ + public function remove_from_favourites(Request $request) + { + // Validate request + $validator = Validator::make($request->all(), [ + 'unique_id' => 'required|integer', + ]); + + // Return error + if ($validator->fails()) abort(400, 'Bad input'); + + // Get user + $user = Auth::user(); + + // Remove folder from user favourites + $user->favourites()->detach($request->unique_id); + + // Return updated favourites + return $user->favourites->makeHidden(['pivot']); + } +} diff --git a/app/Http/Controllers/FileFunctions/ShareController.php b/app/Http/Controllers/FileFunctions/ShareController.php new file mode 100644 index 00000000..d9bf9904 --- /dev/null +++ b/app/Http/Controllers/FileFunctions/ShareController.php @@ -0,0 +1,92 @@ +exists()); + + // Create shared options + $options = [ + 'token' => $token, + 'user_id' => Auth::id(), + 'item_id' => $request->unique_id, + 'permission' => $request->permission, + 'protected' => $request->isPassword, + 'type' => $request->type === 'folder' ? 'folder' : 'file', + 'password' => $request->has('password') ? Hash::make($request->password) : null, + ]; + + // Store shared item + $shared = Share::create($options); + + // Return shared record + return Arr::except($shared, ['password', 'user_id', 'updated_at', 'created_at']); + } + + /** + * Update sharing + * + * @param Request $request + * @return mixed + */ + public function update(Request $request) + { + // TODO: validacia + + // Get sharing record + $shared = Share::where('token', $request->get('token'))->firstOrFail(); + + // Update sharing record + $shared->update([ + 'permission' => $request->permission, + 'protected' => $request->isProtected, + 'password' => $request->has('password') ? Hash::make($request->password) : $shared->password, + ]); + + // Return shared record + return Arr::except($shared, ['password', 'user_id', 'updated_at', 'created_at']); + } + + /** + * Delete sharing item + * + * @param Request $request + * @return ResponseFactory|\Illuminate\Http\Response + */ + public function delete(Request $request) + { + // Get sharing record + $shared = Share::where('token', $request->get('token'))->firstOrFail(); + + // Delete shared record + $shared->delete(); + + // Done + return response('Done!', 202); + } +} diff --git a/app/Http/Controllers/FileFunctions/TrashController.php b/app/Http/Controllers/FileFunctions/TrashController.php new file mode 100644 index 00000000..ede6963a --- /dev/null +++ b/app/Http/Controllers/FileFunctions/TrashController.php @@ -0,0 +1,100 @@ +where('user_id', $user_id)->get(); + $files = FileManagerFile::onlyTrashed()->where('user_id', $user_id)->get(); + + // Force delete folder + $folders->each->forceDelete(); + + // Force delete files + foreach ($files as $file) { + + // Delete file + Storage::disk('local')->delete('/file-manager/' . $file->basename); + + // Delete thumbnail if exist + if ($file->thumbnail) Storage::disk('local')->delete('/file-manager/' . $file->getOriginal('thumbnail')); + + // Delete file permanently + $file->forceDelete(); + } + + // Return response + return response('Done!', 204); + } + + /** + * Restore item from trash + * + * @param Request $request + * @return ResponseFactory|\Illuminate\Http\Response + */ + public function restore(Request $request) + { + // Validate request + $validator = Validator::make($request->all(), [ + 'unique_id' => 'required|integer', + 'type' => 'required|string', + 'to_home' => 'boolean', + ]); + + // Return error + if ($validator->fails()) abort(400, 'Bad input'); + + // Get user id + $user_id = Auth::id(); + + // Get folder + if ($request->type === 'folder') { + + // Get folder + $item = FileManagerFolder::onlyTrashed()->where('user_id', $user_id)->where('unique_id', $request->unique_id)->first(); + + // Restore item to home directory + if ($request->has('to_home') && $request->to_home) { + $item->parent_id = 0; + $item->save(); + } + } else { + + // Get item + $item = FileManagerFile::onlyTrashed()->where('user_id', $user_id)->where('unique_id', $request->unique_id)->first(); + + // Restore item to home directory + if ($request->has('to_home') && $request->to_home) { + $item->folder_id = 0; + $item->save(); + } + } + + // Restore Item + $item->restore(); + + // Return response + return response('Done!', 204); + } +} diff --git a/app/Http/Controllers/FileSharingController.php b/app/Http/Controllers/FileSharingController.php deleted file mode 100644 index 6b6ee1ae..00000000 --- a/app/Http/Controllers/FileSharingController.php +++ /dev/null @@ -1,54 +0,0 @@ -all(); - } - - - public function get_shared(Request $request) { - - // Get user - $user_id = Auth::id(); - - // Get folders and files - $folders = FileManagerFolder::with('parent') - ->where('user_id', $user_id) - ->where('parent_id', 0) - ->get(); - - $files = FileManagerFile::with('parent') - ->where('user_id', $user_id) - ->where('folder_id', 0) - ->get(); - - // Collect folders and files to single array - return collect([$folders, $files])->collapse(); - } -} diff --git a/app/Http/Controllers/Sharing/FileSharingController.php b/app/Http/Controllers/Sharing/FileSharingController.php new file mode 100644 index 00000000..8f852ed6 --- /dev/null +++ b/app/Http/Controllers/Sharing/FileSharingController.php @@ -0,0 +1,220 @@ +firstOrFail(['token', 'item_id', 'type', 'permission', 'protected']); + } + + /** + * Check Password for protected item + * + * @param Request $request + * @param $token + * @return array + */ + public function authenticate(Request $request, $token) + { + // TODO: validacia + + // Get sharing record + $shared = Share::where('token', $token)->firstOrFail(); + + // Check password + if (!Hash::check($request->password, $shared->password)) { + + abort(401, 'Sorry, your password is incorrect.'); + } + + // Get owner of shared content + $user = User::find($shared->user_id); + + // Define scope + $scope = !is_null($shared->permission) ? $shared->permission : 'visitor'; + + // Generate token for visitor/editor + $token = $user->createToken('token', [$scope])->accessToken; + + // Return authorize token with shared options + return response(Arr::except($shared, ['password', 'user_id', 'updated_at', 'created_at']), 200) + ->cookie('shared_token', $shared->token, 43200) + ->cookie('token', $token, 43200); + } + + /** + * Browse private folders + * + * @param Request $request + * @param $unique_id + * @return Collection + */ + public function browse_private(Request $request, $unique_id) + { + // Check if token exist + if (!$request->has('token')) + abort(404, "Sorry, you don't request any content"); + + // Get sharing record + $shared = Share::where('token', $request->token)->firstOrFail(); + + // Check directory authentication + $this->check_authenticated_access($request); + + // Check if user can get directory + $this->check_folder_access($unique_id, $shared); + + // Get folders and files + $folders = FileManagerFolder::where('user_id', $shared->user_id) + ->where('parent_id', $unique_id) + ->get(); + + $files = FileManagerFile::where('user_id', $shared->user_id) + ->where('folder_id', $unique_id) + ->get(); + + // Collect folders and files to single array + return collect([$folders, $files])->collapse(); + } + + /** + * Browse public folders + * + * @param Request $request + * @param $unique_id + * @return Collection + */ + public function browse_public(Request $request, $unique_id) + { + + // Check if token exist + if (!$request->has('token')) + abort(404, "Sorry, you don't request any content"); + + // Get sharing record + $shared = Share::where('token', $request->token)->firstOrFail(); + + // Abort if folder is protected + if ($shared->protected) { + abort(403, "Sorry, you don't have permission"); + } + + // Check if user can get directory + $this->check_folder_access($unique_id, $shared); + + // Get folders and files + $folders = FileManagerFolder::where('user_id', $shared->user_id) + ->where('parent_id', $unique_id) + ->get(); + + $files = FileManagerFile::where('user_id', $shared->user_id) + ->where('folder_id', $unique_id) + ->get(); + + // Add shared token to file + /*if ($shared->protected) { + + $files->map(function ($file) use ($shared) { + //$file->thumbnail = $file->getOriginal('thumbnail') . '?token=' . $shared->token; + + $file->thumbnail = route('thumbnail-public', ['name' => $file->getOriginal('thumbnail')]); + }); + }*/ + + // Collect folders and files to single array + return collect([$folders, $files])->collapse(); + } + + /** + * Get shared public file record + * + * @param $token + * @return mixed + */ + public function file_public($token) + { + // Get sharing record + $shared = Share::where('token', $token)->firstOrFail(); + + // Abort if file is protected + if ($shared->protected) { + abort(403, "Sorry, you don't have permission"); + } + + // Return record + return FileManagerFile::where('user_id', $shared->user_id) + ->where('unique_id', $shared->item_id) + ->firstOrFail(['name', 'basename', 'thumbnail', 'type', 'filesize', 'mimetype']); + } + + /** + * Get shared private file record + * + * @param $token + * @return mixed + */ + public function file_private(Request $request, $token) + { + // Get sharing record + $shared = Share::where('token', $token)->firstOrFail(); + + // Check file authentication + $this->check_authenticated_access($request); + + // Return record + return FileManagerFile::where('user_id', $shared->user_id) + ->where('unique_id', $shared->item_id) + ->firstOrFail(['name', 'basename', 'thumbnail', 'type', 'filesize', 'mimetype']); + } + + /** + * Check if user has access to requested folder + * + * @param $folder_unique_id + * @param $shared + */ + protected function check_folder_access($unique_id, $shared): void + { + // Get all children folders + $foldersIds = FileManagerFolder::with('folders:id,parent_id,unique_id,name') + ->where('user_id', $shared->user_id) + ->where('parent_id', $shared->item_id) + ->get(); + + // Get all authorized parent folders by shared folder as root of tree + $authorized_parent_folder_ids = Arr::flatten([filter_folders_ids($foldersIds), $shared->item_id]); + + // Check user access + if (!in_array($unique_id, $authorized_parent_folder_ids)) abort(401); + } + + /** + * @param Request $request + */ + protected function check_authenticated_access(Request $request): void + { + // Check directory permission + if ($request->cookie('shared_token') !== $request->token) + abort(401, "Sorry, you don't have permission"); + } +} diff --git a/app/Http/Controllers/User/AccountController.php b/app/Http/Controllers/User/AccountController.php new file mode 100644 index 00000000..d450b9ce --- /dev/null +++ b/app/Http/Controllers/User/AccountController.php @@ -0,0 +1,101 @@ +where('id', Auth::id()) + ->first(); + + return [ + 'user' => $user->only(['name', 'email', 'avatar']), + 'favourites' => $user->favourites->makeHidden(['pivot']), + 'latest_uploads' => $user->latest_uploads->makeHidden(['user_id', 'basename']), + 'storage' => [ + 'used' => Metric::bytes($user->used_capacity)->format(), + 'capacity' => format_gigabytes(config('vuefilemanager.user_storage_capacity')), + 'percentage' => get_storage_fill_percentage($user->used_capacity, config('vuefilemanager.user_storage_capacity')), + ], + ]; + } + + /** + * Update user profile + * + * @param Request $request + * @return ResponseFactory|\Illuminate\Http\Response + */ + public function update_profile(Request $request) + { + // Validate request + $validator = Validator::make($request->all(), [ + 'avatar' => 'file', + '_method' => 'string', + 'name' => 'string', + 'value' => 'string', + ]); + + // Return error + if ($validator->fails()) abort(400, 'Bad input'); + + // Get user + $user = Auth::user(); + + if ($request->hasFile('avatar')) { + + // Update avatar + $avatar = store_avatar($request->file('avatar'), 'avatars'); + + // Update data + $user->update(['avatar' => $avatar]); + + } else { + + // Update text data + $user->update(make_single_input($request)); + } + + return response('Saved!', 204); + } + + /** + * Change user password + * + * @param Request $request + * @return ResponseFactory|\Illuminate\Http\Response + */ + public function change_password(Request $request) + { + // Validate request + $request->validate([ + 'password' => ['required', 'string', 'min:6', 'confirmed'], + ]); + + // Get user + $user = Auth::user(); + + // Change and store new password + $user->password = Hash::make($request->input('password')); + $user->save(); + + return response('Changed!', 204); + } +} diff --git a/app/Http/Controllers/UserAccountController.php b/app/Http/Controllers/UserAccountController.php deleted file mode 100644 index d0c75f81..00000000 --- a/app/Http/Controllers/UserAccountController.php +++ /dev/null @@ -1,177 +0,0 @@ -where('id', $user_id) - ->first(); - - return [ - 'user' => $user->only(['name', 'email', 'avatar']), - 'favourites' => $user->favourites->makeHidden(['pivot']), - 'latest_uploads' => $user->latest_uploads->makeHidden(['user_id', 'basename']), - 'storage' => [ - 'used' => Metric::bytes($user->used_capacity)->format(), - 'capacity' => format_gigabytes(config('vuefilemanager.user_storage_capacity')), - 'percentage' => get_storage_fill_percentage($user->used_capacity, config('vuefilemanager.user_storage_capacity')), - ], - ]; - } - - /** - * Get user folder tree - * - * @return array - */ - public function folder_tree() { - - $folders = FileManagerFolder::with('folders:id,parent_id,unique_id,name') - ->where('parent_id', 0) - ->where('user_id', Auth::id()) - ->get(['id', 'parent_id', 'unique_id', 'name']); - - return [ - [ - 'unique_id' => 0, - 'name' => __('vuefilemanager.home'), - 'location' => 'base', - 'folders' => $folders, - ] - ]; - } - - /** - * Update user profile - * - * @param Request $request - * @return \Illuminate\Contracts\Routing\ResponseFactory|\Illuminate\Http\Response - */ - public function update_profile(Request $request) - { - // Validate request - $validator = Validator::make($request->all(), [ - 'avatar' => 'file', - '_method' => 'string', - 'name' => 'string', - 'value' => 'string', - ]); - - // Return error - if ($validator->fails()) abort(400, 'Bad input'); - - // Get user - $user = Auth::user(); - - if ($request->hasFile('avatar')) { - - // Update avatar - $avatar = store_avatar($request->file('avatar'), 'avatars'); - - // Update data - $user->update(['avatar' => $avatar]); - - } else { - - // Update text data - $user->update(make_single_input($request)); - } - - return response('Saved!', 200); - } - - /** - * Change user password - * - * @param Request $request - * @return array - */ - public function change_password(Request $request) - { - // Validate request - $request->validate([ - 'password' => ['required', 'string', 'min:6', 'confirmed'], - ]); - - // Get user - $user = Auth::user(); - - // Change and store new password - $user->password = Hash::make($request->input('password')); - $user->save(); - } - - /** - * Add folder to user favourites - * - * @param Request $request - * @return \Illuminate\Contracts\Routing\ResponseFactory|\Illuminate\Http\Response - */ - public function add_to_favourites(Request $request) - { - // Validate request - $validator = Validator::make($request->all(), [ - 'unique_id' => 'required|integer', - ]); - - // Return error - if ($validator->fails()) abort(400, 'Bad input'); - - // Get user - $user = Auth::user(); - - // Add folder to user favourites - $user->favourites()->attach($request->unique_id); - - // Return updated favourites - return $user->favourites->makeHidden(['pivot']); - } - - /** - * Remove folder from user favourites - * - * @param Request $request - * @return \Illuminate\Contracts\Routing\ResponseFactory|\Illuminate\Http\Response - */ - public function remove_from_favourites(Request $request) - { - // Validate request - $validator = Validator::make($request->all(), [ - 'unique_id' => 'required|integer', - ]); - - // Return error - if ($validator->fails()) abort(400, 'Bad input'); - - // Get user - $user = Auth::user(); - - // Remove folder from user favourites - $user->favourites()->detach($request->unique_id); - - // Return updated favourites - return $user->favourites->makeHidden(['pivot']); - } -} diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php index a6faac49..a64e847a 100644 --- a/app/Http/Kernel.php +++ b/app/Http/Kernel.php @@ -3,6 +3,7 @@ namespace App\Http; use App\Http\Middleware\CookieAuth; +use App\Http\Middleware\LastCheck; use Illuminate\Foundation\Http\Kernel as HttpKernel; class Kernel extends HttpKernel @@ -40,6 +41,7 @@ class Kernel extends HttpKernel ], 'api' => [ + \App\Http\Middleware\EncryptCookies::class, //'throttle:60,1', \Illuminate\Routing\Middleware\SubstituteBindings::class, ], @@ -53,6 +55,7 @@ class Kernel extends HttpKernel * @var array */ protected $routeMiddleware = [ + 'auth.cookie' => CookieAuth::class, 'auth' => \App\Http\Middleware\Authenticate::class, 'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class, 'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class, @@ -63,7 +66,8 @@ class Kernel extends HttpKernel 'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class, 'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class, 'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class, - 'auth.cookie' => CookieAuth::class, + 'scopes' => \Laravel\Passport\Http\Middleware\CheckScopes::class, + 'scope' => \Laravel\Passport\Http\Middleware\CheckForAnyScope::class, ]; /** diff --git a/app/Http/Middleware/CookieAuth.php b/app/Http/Middleware/CookieAuth.php index 3954664e..1e910820 100644 --- a/app/Http/Middleware/CookieAuth.php +++ b/app/Http/Middleware/CookieAuth.php @@ -3,6 +3,7 @@ namespace App\Http\Middleware; use Closure; +use Illuminate\Support\Facades\Auth; class CookieAuth { diff --git a/app/Providers/AuthServiceProvider.php b/app/Providers/AuthServiceProvider.php index 90ce09b8..0a41da0d 100644 --- a/app/Providers/AuthServiceProvider.php +++ b/app/Providers/AuthServiceProvider.php @@ -27,5 +27,17 @@ class AuthServiceProvider extends ServiceProvider $this->registerPolicies(); Passport::routes(); + + Passport::tokensCan([ + 'master' => 'Master', + 'editor' => 'Editor', + 'visitor' => 'Visitor', + ]); + + Passport::setDefaultScope([ + 'master', + 'editor', + 'visitor', + ]); } } diff --git a/app/Share.php b/app/Share.php new file mode 100644 index 00000000..6bc9554f --- /dev/null +++ b/app/Share.php @@ -0,0 +1,22 @@ + $this->attributes['token']]); + } +} diff --git a/database/migrations/2020_04_20_071047_create_shares_table.php b/database/migrations/2020_04_20_071047_create_shares_table.php new file mode 100644 index 00000000..ba542743 --- /dev/null +++ b/database/migrations/2020_04_20_071047_create_shares_table.php @@ -0,0 +1,38 @@ +bigIncrements('id'); + $table->bigInteger('user_id'); + $table->string('token', 16)->unique(); + $table->bigInteger('item_id'); + $table->enum('type', ['file', 'files', 'folder']); + $table->enum('permission', ['visitor', 'editor'])->nullable(); + $table->boolean('protected'); + $table->string('password')->nullable(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('shares'); + } +} diff --git a/public/mix-manifest.json b/public/mix-manifest.json index 83da1651..dfa78876 100644 --- a/public/mix-manifest.json +++ b/public/mix-manifest.json @@ -1,71 +1,218 @@ { "/js/main.js": "/js/main.js", "/css/app.css": "/css/app.css", - "/js/main.7e9161693625acb57bcf.hot-update.js": "/js/main.7e9161693625acb57bcf.hot-update.js", - "/js/main.987c3e4498df1d118da2.hot-update.js": "/js/main.987c3e4498df1d118da2.hot-update.js", - "/js/main.a40fbe3eed27fc90b1e2.hot-update.js": "/js/main.a40fbe3eed27fc90b1e2.hot-update.js", - "/js/main.7a5a1814d3a02ef067d9.hot-update.js": "/js/main.7a5a1814d3a02ef067d9.hot-update.js", - "/js/main.e2df40ed1f9a5560f20e.hot-update.js": "/js/main.e2df40ed1f9a5560f20e.hot-update.js", - "/js/main.5cfccce115a0ed922869.hot-update.js": "/js/main.5cfccce115a0ed922869.hot-update.js", - "/js/main.7db75f2c81783305757c.hot-update.js": "/js/main.7db75f2c81783305757c.hot-update.js", - "/js/main.6989d8ca52ab33f39e69.hot-update.js": "/js/main.6989d8ca52ab33f39e69.hot-update.js", - "/js/main.6e44c31cb6f99d3330cd.hot-update.js": "/js/main.6e44c31cb6f99d3330cd.hot-update.js", - "/js/main.da1ab17556140889f100.hot-update.js": "/js/main.da1ab17556140889f100.hot-update.js", - "/js/main.3572f6f7831b831e9b54.hot-update.js": "/js/main.3572f6f7831b831e9b54.hot-update.js", - "/js/main.5372486c92c251b646c1.hot-update.js": "/js/main.5372486c92c251b646c1.hot-update.js", - "/js/main.8f756e17c052ec672901.hot-update.js": "/js/main.8f756e17c052ec672901.hot-update.js", - "/js/main.3ebf1ab82a10e44fa358.hot-update.js": "/js/main.3ebf1ab82a10e44fa358.hot-update.js", - "/js/main.c4894466962f57802943.hot-update.js": "/js/main.c4894466962f57802943.hot-update.js", - "/js/main.46bca7fb02572608cbe8.hot-update.js": "/js/main.46bca7fb02572608cbe8.hot-update.js", - "/js/main.715f67b09ca41d03a457.hot-update.js": "/js/main.715f67b09ca41d03a457.hot-update.js", - "/js/main.aff84531b8db5533bb6f.hot-update.js": "/js/main.aff84531b8db5533bb6f.hot-update.js", - "/js/main.8f201d83c20947c67d2e.hot-update.js": "/js/main.8f201d83c20947c67d2e.hot-update.js", - "/js/main.279d8c87134c791ceac1.hot-update.js": "/js/main.279d8c87134c791ceac1.hot-update.js", - "/js/main.d187642b4f091564f4ee.hot-update.js": "/js/main.d187642b4f091564f4ee.hot-update.js", - "/js/main.ece4398887cceea769b1.hot-update.js": "/js/main.ece4398887cceea769b1.hot-update.js", - "/js/main.ff14e9b1d7252130b887.hot-update.js": "/js/main.ff14e9b1d7252130b887.hot-update.js", - "/js/main.9969dd9da75f062e431a.hot-update.js": "/js/main.9969dd9da75f062e431a.hot-update.js", - "/js/main.c2e0646fed55fe0f84fc.hot-update.js": "/js/main.c2e0646fed55fe0f84fc.hot-update.js", - "/js/main.27e7d8971b3249f487fb.hot-update.js": "/js/main.27e7d8971b3249f487fb.hot-update.js", - "/js/main.7bb41d8795de4fb9e9b1.hot-update.js": "/js/main.7bb41d8795de4fb9e9b1.hot-update.js", - "/js/main.6cc132b1314887cb0a25.hot-update.js": "/js/main.6cc132b1314887cb0a25.hot-update.js", - "/js/main.9d1159e1b80612d351ee.hot-update.js": "/js/main.9d1159e1b80612d351ee.hot-update.js", - "/js/main.aaf7688d2b55607a4932.hot-update.js": "/js/main.aaf7688d2b55607a4932.hot-update.js", - "/js/main.d33e67a4d98740ec0496.hot-update.js": "/js/main.d33e67a4d98740ec0496.hot-update.js", - "/js/main.555618f8bd757bff9d82.hot-update.js": "/js/main.555618f8bd757bff9d82.hot-update.js", - "/js/main.9c2e37b36662dab38a65.hot-update.js": "/js/main.9c2e37b36662dab38a65.hot-update.js", - "/js/main.ee47e0af675d059f283c.hot-update.js": "/js/main.ee47e0af675d059f283c.hot-update.js", - "/js/main.51f351a7d6d58f683546.hot-update.js": "/js/main.51f351a7d6d58f683546.hot-update.js", - "/js/main.42590b5e642261c106f0.hot-update.js": "/js/main.42590b5e642261c106f0.hot-update.js", - "/js/main.b8070818a64d03de2db4.hot-update.js": "/js/main.b8070818a64d03de2db4.hot-update.js", - "/js/main.b079cdcbdc39fecde220.hot-update.js": "/js/main.b079cdcbdc39fecde220.hot-update.js", - "/js/main.bb65ebd58ac9256e12f1.hot-update.js": "/js/main.bb65ebd58ac9256e12f1.hot-update.js", - "/js/main.674d8948d58824997a3d.hot-update.js": "/js/main.674d8948d58824997a3d.hot-update.js", - "/js/main.46d9fd525f37e4202b5e.hot-update.js": "/js/main.46d9fd525f37e4202b5e.hot-update.js", - "/js/main.04f2379ff46bec0a8a23.hot-update.js": "/js/main.04f2379ff46bec0a8a23.hot-update.js", - "/js/main.6e7100b4a9c4097b8b0a.hot-update.js": "/js/main.6e7100b4a9c4097b8b0a.hot-update.js", - "/js/main.5e35c40769bc5ed7a701.hot-update.js": "/js/main.5e35c40769bc5ed7a701.hot-update.js", - "/js/main.9789eaad174abbec4830.hot-update.js": "/js/main.9789eaad174abbec4830.hot-update.js", - "/js/main.9ea2e3ccfc26b4251cc9.hot-update.js": "/js/main.9ea2e3ccfc26b4251cc9.hot-update.js", - "/js/main.fbf8d04378624a73b13e.hot-update.js": "/js/main.fbf8d04378624a73b13e.hot-update.js", - "/js/main.70ffe26807eaabd6a5c9.hot-update.js": "/js/main.70ffe26807eaabd6a5c9.hot-update.js", - "/js/main.96ee5a89db89e4995d64.hot-update.js": "/js/main.96ee5a89db89e4995d64.hot-update.js", - "/js/main.1dc8adf4e56f198645c6.hot-update.js": "/js/main.1dc8adf4e56f198645c6.hot-update.js", - "/js/main.1461ccfa9a7b972486b8.hot-update.js": "/js/main.1461ccfa9a7b972486b8.hot-update.js", - "/js/main.bd4bb3cdf79008bc4f39.hot-update.js": "/js/main.bd4bb3cdf79008bc4f39.hot-update.js", - "/js/main.9798f006b02c05b13732.hot-update.js": "/js/main.9798f006b02c05b13732.hot-update.js", - "/js/main.1884ae3ff7f1cb2583fb.hot-update.js": "/js/main.1884ae3ff7f1cb2583fb.hot-update.js", - "/js/main.c573379acdb883f75123.hot-update.js": "/js/main.c573379acdb883f75123.hot-update.js", - "/js/main.faf1eb5f87f23ef782a7.hot-update.js": "/js/main.faf1eb5f87f23ef782a7.hot-update.js", - "/js/main.d77027b5cdd9613c59c5.hot-update.js": "/js/main.d77027b5cdd9613c59c5.hot-update.js", - "/js/main.6cf73ea7b0a8da21d3e3.hot-update.js": "/js/main.6cf73ea7b0a8da21d3e3.hot-update.js", - "/js/main.7298d2627989dba610e6.hot-update.js": "/js/main.7298d2627989dba610e6.hot-update.js", - "/js/main.b788d5d978f78f7b020e.hot-update.js": "/js/main.b788d5d978f78f7b020e.hot-update.js", - "/js/main.521673210a92d008e657.hot-update.js": "/js/main.521673210a92d008e657.hot-update.js", - "/js/main.857d808c85abcb81e209.hot-update.js": "/js/main.857d808c85abcb81e209.hot-update.js", - "/js/main.3e6a766612fcec30c27b.hot-update.js": "/js/main.3e6a766612fcec30c27b.hot-update.js", - "/js/main.e42f73a1aff80626d361.hot-update.js": "/js/main.e42f73a1aff80626d361.hot-update.js", - "/js/main.099e582740355c2f1bca.hot-update.js": "/js/main.099e582740355c2f1bca.hot-update.js", - "/js/main.feca0a1aa3fc71608819.hot-update.js": "/js/main.feca0a1aa3fc71608819.hot-update.js", - "/js/main.45917de646170b07e78a.hot-update.js": "/js/main.45917de646170b07e78a.hot-update.js" + "/js/main.45d46e1933bf6dec5ef6.hot-update.js": "/js/main.45d46e1933bf6dec5ef6.hot-update.js", + "/js/main.e842979da4bf159e5ad2.hot-update.js": "/js/main.e842979da4bf159e5ad2.hot-update.js", + "/js/main.0e5fdb03fbf3b0a89168.hot-update.js": "/js/main.0e5fdb03fbf3b0a89168.hot-update.js", + "/js/main.c36a7b12e330fb1dc0c1.hot-update.js": "/js/main.c36a7b12e330fb1dc0c1.hot-update.js", + "/js/main.59c5c17472ec18755906.hot-update.js": "/js/main.59c5c17472ec18755906.hot-update.js", + "/js/main.675ab9309b6bff002024.hot-update.js": "/js/main.675ab9309b6bff002024.hot-update.js", + "/js/main.11438e0b6172ac156942.hot-update.js": "/js/main.11438e0b6172ac156942.hot-update.js", + "/js/main.a1114f307285712e8af2.hot-update.js": "/js/main.a1114f307285712e8af2.hot-update.js", + "/js/main.ab269a7a54493f3c8d81.hot-update.js": "/js/main.ab269a7a54493f3c8d81.hot-update.js", + "/js/main.d993f351202c0e02c241.hot-update.js": "/js/main.d993f351202c0e02c241.hot-update.js", + "/js/main.ed79cccc5224df201cd1.hot-update.js": "/js/main.ed79cccc5224df201cd1.hot-update.js", + "/js/main.91d10f3eaa8e47ae0d5f.hot-update.js": "/js/main.91d10f3eaa8e47ae0d5f.hot-update.js", + "/js/main.472e5ddce4a79bbc3090.hot-update.js": "/js/main.472e5ddce4a79bbc3090.hot-update.js", + "/js/main.1941f48e75b8c10dc7ec.hot-update.js": "/js/main.1941f48e75b8c10dc7ec.hot-update.js", + "/js/main.ff9704078c22b00cd66a.hot-update.js": "/js/main.ff9704078c22b00cd66a.hot-update.js", + "/js/main.59307cce9ecf88ce9b42.hot-update.js": "/js/main.59307cce9ecf88ce9b42.hot-update.js", + "/js/main.a79c63d0e5afda037179.hot-update.js": "/js/main.a79c63d0e5afda037179.hot-update.js", + "/js/main.bcb4911901a4471bb474.hot-update.js": "/js/main.bcb4911901a4471bb474.hot-update.js", + "/js/main.b0a97918d2211a77afe5.hot-update.js": "/js/main.b0a97918d2211a77afe5.hot-update.js", + "/js/main.3c893e1ab5aeaf9abaa3.hot-update.js": "/js/main.3c893e1ab5aeaf9abaa3.hot-update.js", + "/js/main.e1313d34b87de7f5bba1.hot-update.js": "/js/main.e1313d34b87de7f5bba1.hot-update.js", + "/js/main.5e697db90b162e63c7d5.hot-update.js": "/js/main.5e697db90b162e63c7d5.hot-update.js", + "/js/main.bf8ab4a1792c30ac5025.hot-update.js": "/js/main.bf8ab4a1792c30ac5025.hot-update.js", + "/js/main.3c7645b1e9c1075b521a.hot-update.js": "/js/main.3c7645b1e9c1075b521a.hot-update.js", + "/js/main.c747bad73a39c418162d.hot-update.js": "/js/main.c747bad73a39c418162d.hot-update.js", + "/js/main.b7b2c7af1da4c77f317b.hot-update.js": "/js/main.b7b2c7af1da4c77f317b.hot-update.js", + "/js/main.17eeedab7f672cdd3a6a.hot-update.js": "/js/main.17eeedab7f672cdd3a6a.hot-update.js", + "/js/main.89438e99f4850b9b2de8.hot-update.js": "/js/main.89438e99f4850b9b2de8.hot-update.js", + "/js/main.10f3e535029ec20e54da.hot-update.js": "/js/main.10f3e535029ec20e54da.hot-update.js", + "/js/main.072647b9d5467ef5ca7a.hot-update.js": "/js/main.072647b9d5467ef5ca7a.hot-update.js", + "/js/main.8bf2cd5eef3d7019e7da.hot-update.js": "/js/main.8bf2cd5eef3d7019e7da.hot-update.js", + "/js/main.95752955f0e17cd17f76.hot-update.js": "/js/main.95752955f0e17cd17f76.hot-update.js", + "/js/main.c6e4e5ff66ecfdecaea6.hot-update.js": "/js/main.c6e4e5ff66ecfdecaea6.hot-update.js", + "/js/main.34a49e954ecf67649032.hot-update.js": "/js/main.34a49e954ecf67649032.hot-update.js", + "/js/main.862ff3f40b2875106861.hot-update.js": "/js/main.862ff3f40b2875106861.hot-update.js", + "/js/main.931f8a36f8a71f9cdec8.hot-update.js": "/js/main.931f8a36f8a71f9cdec8.hot-update.js", + "/js/main.711924356b1a6d486ed7.hot-update.js": "/js/main.711924356b1a6d486ed7.hot-update.js", + "/js/main.8b6b6e691061b928cf69.hot-update.js": "/js/main.8b6b6e691061b928cf69.hot-update.js", + "/js/main.3d894099836551049ffc.hot-update.js": "/js/main.3d894099836551049ffc.hot-update.js", + "/js/main.ba496f798cb18def602c.hot-update.js": "/js/main.ba496f798cb18def602c.hot-update.js", + "/js/main.d2ec88e0eae1126cdf51.hot-update.js": "/js/main.d2ec88e0eae1126cdf51.hot-update.js", + "/js/main.6aed355d780dd1a86963.hot-update.js": "/js/main.6aed355d780dd1a86963.hot-update.js", + "/js/main.7e75319276974a331581.hot-update.js": "/js/main.7e75319276974a331581.hot-update.js", + "/js/main.9e88fe1df870daaa7481.hot-update.js": "/js/main.9e88fe1df870daaa7481.hot-update.js", + "/js/main.31b039255f8ef5527038.hot-update.js": "/js/main.31b039255f8ef5527038.hot-update.js", + "/js/main.4ed6ff73227b2913e6f7.hot-update.js": "/js/main.4ed6ff73227b2913e6f7.hot-update.js", + "/js/main.1bc6127eb1a40c20fb6d.hot-update.js": "/js/main.1bc6127eb1a40c20fb6d.hot-update.js", + "/js/main.27a6304f341c207df2e2.hot-update.js": "/js/main.27a6304f341c207df2e2.hot-update.js", + "/js/main.3d276a8f5ab90573ec50.hot-update.js": "/js/main.3d276a8f5ab90573ec50.hot-update.js", + "/js/main.3fe8960049a61eb6cc8a.hot-update.js": "/js/main.3fe8960049a61eb6cc8a.hot-update.js", + "/js/main.213e19eba62d8ebb1015.hot-update.js": "/js/main.213e19eba62d8ebb1015.hot-update.js", + "/js/main.4343cb3b56c8ec78e09e.hot-update.js": "/js/main.4343cb3b56c8ec78e09e.hot-update.js", + "/js/main.002f43371ba3f184e49a.hot-update.js": "/js/main.002f43371ba3f184e49a.hot-update.js", + "/js/main.c2cc0809ddf5e69a8994.hot-update.js": "/js/main.c2cc0809ddf5e69a8994.hot-update.js", + "/js/main.a83f42619db1298b3e69.hot-update.js": "/js/main.a83f42619db1298b3e69.hot-update.js", + "/js/main.1c709721edba358dfd1e.hot-update.js": "/js/main.1c709721edba358dfd1e.hot-update.js", + "/js/main.858da95bee6edb257415.hot-update.js": "/js/main.858da95bee6edb257415.hot-update.js", + "/js/main.13993f34d46f529728dc.hot-update.js": "/js/main.13993f34d46f529728dc.hot-update.js", + "/js/main.a3ab176a478047d662d3.hot-update.js": "/js/main.a3ab176a478047d662d3.hot-update.js", + "/js/main.cabeee86f654c3861499.hot-update.js": "/js/main.cabeee86f654c3861499.hot-update.js", + "/js/main.564dd46378b597c73c5e.hot-update.js": "/js/main.564dd46378b597c73c5e.hot-update.js", + "/js/main.1c75a700a099d485590e.hot-update.js": "/js/main.1c75a700a099d485590e.hot-update.js", + "/js/main.4f13b79ce32ab3777860.hot-update.js": "/js/main.4f13b79ce32ab3777860.hot-update.js", + "/js/main.42d7f4a867e436534858.hot-update.js": "/js/main.42d7f4a867e436534858.hot-update.js", + "/js/main.86ee25e1fe9db767199e.hot-update.js": "/js/main.86ee25e1fe9db767199e.hot-update.js", + "/js/main.7b439d432a35a2da1b48.hot-update.js": "/js/main.7b439d432a35a2da1b48.hot-update.js", + "/js/main.4ad2c9def324e6b15861.hot-update.js": "/js/main.4ad2c9def324e6b15861.hot-update.js", + "/js/main.56e704cb0f20e4553758.hot-update.js": "/js/main.56e704cb0f20e4553758.hot-update.js", + "/js/main.26313fd284c76a567f7c.hot-update.js": "/js/main.26313fd284c76a567f7c.hot-update.js", + "/js/main.8c4962b73c1a932049a5.hot-update.js": "/js/main.8c4962b73c1a932049a5.hot-update.js", + "/js/main.592716617990cae822eb.hot-update.js": "/js/main.592716617990cae822eb.hot-update.js", + "/js/main.e7f7f34179ead881e97f.hot-update.js": "/js/main.e7f7f34179ead881e97f.hot-update.js", + "/js/main.703e1592587b3ceb481a.hot-update.js": "/js/main.703e1592587b3ceb481a.hot-update.js", + "/js/main.63aede9942e996b36e82.hot-update.js": "/js/main.63aede9942e996b36e82.hot-update.js", + "/js/main.e292d358239ca41a9d97.hot-update.js": "/js/main.e292d358239ca41a9d97.hot-update.js", + "/js/main.53adb5f1d356c467bac0.hot-update.js": "/js/main.53adb5f1d356c467bac0.hot-update.js", + "/js/main.2d8c026be3662d72ee9e.hot-update.js": "/js/main.2d8c026be3662d72ee9e.hot-update.js", + "/js/main.acaac070a297898b221f.hot-update.js": "/js/main.acaac070a297898b221f.hot-update.js", + "/js/main.9efaeff7c4d9050159e4.hot-update.js": "/js/main.9efaeff7c4d9050159e4.hot-update.js", + "/js/main.52626570ec535bcdbb28.hot-update.js": "/js/main.52626570ec535bcdbb28.hot-update.js", + "/js/main.4d7397481511eba8c454.hot-update.js": "/js/main.4d7397481511eba8c454.hot-update.js", + "/js/main.3c77b8175b2b9a6e3212.hot-update.js": "/js/main.3c77b8175b2b9a6e3212.hot-update.js", + "/js/main.24c4f93ebebb137492b1.hot-update.js": "/js/main.24c4f93ebebb137492b1.hot-update.js", + "/js/main.7cd7ddded3974c828799.hot-update.js": "/js/main.7cd7ddded3974c828799.hot-update.js", + "/js/main.695d3326e0294da3f532.hot-update.js": "/js/main.695d3326e0294da3f532.hot-update.js", + "/js/main.828bc145abbb6764ac01.hot-update.js": "/js/main.828bc145abbb6764ac01.hot-update.js", + "/js/main.0f0f76487d80ed6498d0.hot-update.js": "/js/main.0f0f76487d80ed6498d0.hot-update.js", + "/js/main.89d9b57d5aed031dd88c.hot-update.js": "/js/main.89d9b57d5aed031dd88c.hot-update.js", + "/js/main.27200ce35055fc58a734.hot-update.js": "/js/main.27200ce35055fc58a734.hot-update.js", + "/js/main.7919f0cae849b26ebc3e.hot-update.js": "/js/main.7919f0cae849b26ebc3e.hot-update.js", + "/js/main.55ae03f9fc60c32a00c3.hot-update.js": "/js/main.55ae03f9fc60c32a00c3.hot-update.js", + "/js/main.1232b65bec68e541261b.hot-update.js": "/js/main.1232b65bec68e541261b.hot-update.js", + "/js/main.8f2ee96210ab02cedea1.hot-update.js": "/js/main.8f2ee96210ab02cedea1.hot-update.js", + "/js/main.73cd77f44d0ff3e3176a.hot-update.js": "/js/main.73cd77f44d0ff3e3176a.hot-update.js", + "/js/main.3d55061b4f6152ddd121.hot-update.js": "/js/main.3d55061b4f6152ddd121.hot-update.js", + "/js/main.ab8de9ea0fe8ec65b9b5.hot-update.js": "/js/main.ab8de9ea0fe8ec65b9b5.hot-update.js", + "/js/main.145ce7ce472bbb94b0e4.hot-update.js": "/js/main.145ce7ce472bbb94b0e4.hot-update.js", + "/js/main.6fff5041d2139d02b16c.hot-update.js": "/js/main.6fff5041d2139d02b16c.hot-update.js", + "/js/main.3732624691f3c89038a8.hot-update.js": "/js/main.3732624691f3c89038a8.hot-update.js", + "/js/main.323fa7a67c34a5dbed41.hot-update.js": "/js/main.323fa7a67c34a5dbed41.hot-update.js", + "/js/main.558890a7e45943667f42.hot-update.js": "/js/main.558890a7e45943667f42.hot-update.js", + "/js/main.e8639fe53d0bc2f9c075.hot-update.js": "/js/main.e8639fe53d0bc2f9c075.hot-update.js", + "/js/main.c26183ff455c7a0ba2bc.hot-update.js": "/js/main.c26183ff455c7a0ba2bc.hot-update.js", + "/js/main.c5363f89becf3962a8a9.hot-update.js": "/js/main.c5363f89becf3962a8a9.hot-update.js", + "/js/main.1bbe5b2d0e8abc9593eb.hot-update.js": "/js/main.1bbe5b2d0e8abc9593eb.hot-update.js", + "/js/main.1016ae260ab41d4d60d4.hot-update.js": "/js/main.1016ae260ab41d4d60d4.hot-update.js", + "/js/main.8db658d779f88345fee4.hot-update.js": "/js/main.8db658d779f88345fee4.hot-update.js", + "/js/main.b678b3d77f37839d1f7a.hot-update.js": "/js/main.b678b3d77f37839d1f7a.hot-update.js", + "/js/main.744f613d1e714c12ed5b.hot-update.js": "/js/main.744f613d1e714c12ed5b.hot-update.js", + "/js/main.658ca470f6baaea9af4d.hot-update.js": "/js/main.658ca470f6baaea9af4d.hot-update.js", + "/js/main.e130c511a816f5706573.hot-update.js": "/js/main.e130c511a816f5706573.hot-update.js", + "/js/main.76b1d7ddd721a25b97f9.hot-update.js": "/js/main.76b1d7ddd721a25b97f9.hot-update.js", + "/js/main.05b32b92f0b803c49e60.hot-update.js": "/js/main.05b32b92f0b803c49e60.hot-update.js", + "/js/main.53e7251368b6f6d372ab.hot-update.js": "/js/main.53e7251368b6f6d372ab.hot-update.js", + "/js/main.5b416860fb7e890be1b9.hot-update.js": "/js/main.5b416860fb7e890be1b9.hot-update.js", + "/js/main.efbb626766a958269c45.hot-update.js": "/js/main.efbb626766a958269c45.hot-update.js", + "/js/main.eb5b4e6bfbfb52e07e69.hot-update.js": "/js/main.eb5b4e6bfbfb52e07e69.hot-update.js", + "/js/main.9e19bf8d2a3bd75d2fb1.hot-update.js": "/js/main.9e19bf8d2a3bd75d2fb1.hot-update.js", + "/js/main.707d8fd922651d415b0b.hot-update.js": "/js/main.707d8fd922651d415b0b.hot-update.js", + "/js/main.203edfce73488ee73a57.hot-update.js": "/js/main.203edfce73488ee73a57.hot-update.js", + "/js/main.2d25c25f516cc043784f.hot-update.js": "/js/main.2d25c25f516cc043784f.hot-update.js", + "/js/main.0933464207540fa40ebd.hot-update.js": "/js/main.0933464207540fa40ebd.hot-update.js", + "/js/main.4e005b6c88bcad50ad41.hot-update.js": "/js/main.4e005b6c88bcad50ad41.hot-update.js", + "/js/main.d5ed4d7d8aae0e741f81.hot-update.js": "/js/main.d5ed4d7d8aae0e741f81.hot-update.js", + "/js/main.ccbd9fa5bef657830fcd.hot-update.js": "/js/main.ccbd9fa5bef657830fcd.hot-update.js", + "/js/main.b3b669a720795d848fa0.hot-update.js": "/js/main.b3b669a720795d848fa0.hot-update.js", + "/js/main.9620631ede7cb7b2d0ac.hot-update.js": "/js/main.9620631ede7cb7b2d0ac.hot-update.js", + "/js/main.85db3fa89d441d5a1c02.hot-update.js": "/js/main.85db3fa89d441d5a1c02.hot-update.js", + "/js/main.42ae11ec799144cae3f3.hot-update.js": "/js/main.42ae11ec799144cae3f3.hot-update.js", + "/js/main.2f603032ba0d6204a269.hot-update.js": "/js/main.2f603032ba0d6204a269.hot-update.js", + "/js/main.8ac83f5b5eacbb882c16.hot-update.js": "/js/main.8ac83f5b5eacbb882c16.hot-update.js", + "/js/main.47b8be544de762157c5a.hot-update.js": "/js/main.47b8be544de762157c5a.hot-update.js", + "/js/main.106128b5d2fc9881a916.hot-update.js": "/js/main.106128b5d2fc9881a916.hot-update.js", + "/js/main.83bf778bb4df7f7bbb20.hot-update.js": "/js/main.83bf778bb4df7f7bbb20.hot-update.js", + "/js/main.f84c193ff16aec6bf8bf.hot-update.js": "/js/main.f84c193ff16aec6bf8bf.hot-update.js", + "/js/main.2f9e799e5817e5b99f13.hot-update.js": "/js/main.2f9e799e5817e5b99f13.hot-update.js", + "/js/main.9d764f2764fee87cb071.hot-update.js": "/js/main.9d764f2764fee87cb071.hot-update.js", + "/js/main.8f497de70fcfe9cae748.hot-update.js": "/js/main.8f497de70fcfe9cae748.hot-update.js", + "/js/main.2e84a1e5edd047c357e4.hot-update.js": "/js/main.2e84a1e5edd047c357e4.hot-update.js", + "/js/main.31f62b5157a78b69bdc3.hot-update.js": "/js/main.31f62b5157a78b69bdc3.hot-update.js", + "/js/main.883aae9bd1f10b0a39d0.hot-update.js": "/js/main.883aae9bd1f10b0a39d0.hot-update.js", + "/js/main.dfaad5213fb0446dc59f.hot-update.js": "/js/main.dfaad5213fb0446dc59f.hot-update.js", + "/js/main.358890311033fcbcda1b.hot-update.js": "/js/main.358890311033fcbcda1b.hot-update.js", + "/js/main.776c3466afcacdaef082.hot-update.js": "/js/main.776c3466afcacdaef082.hot-update.js", + "/js/main.660cfe8313c1662c8abe.hot-update.js": "/js/main.660cfe8313c1662c8abe.hot-update.js", + "/js/main.5289757e5c910408a662.hot-update.js": "/js/main.5289757e5c910408a662.hot-update.js", + "/js/main.634b6c4a6184da7f59fb.hot-update.js": "/js/main.634b6c4a6184da7f59fb.hot-update.js", + "/js/main.d3a4f2cf9d438b4fa893.hot-update.js": "/js/main.d3a4f2cf9d438b4fa893.hot-update.js", + "/js/main.8261f750ce4d91e7e5ac.hot-update.js": "/js/main.8261f750ce4d91e7e5ac.hot-update.js", + "/js/main.15a58ecd277cfef2657a.hot-update.js": "/js/main.15a58ecd277cfef2657a.hot-update.js", + "/js/main.2f58765abd4f4dc56274.hot-update.js": "/js/main.2f58765abd4f4dc56274.hot-update.js", + "/js/main.81a13254d54da2b40824.hot-update.js": "/js/main.81a13254d54da2b40824.hot-update.js", + "/js/main.f2b15334cba9c1ad8a8c.hot-update.js": "/js/main.f2b15334cba9c1ad8a8c.hot-update.js", + "/js/main.97aae53478c639b6dddc.hot-update.js": "/js/main.97aae53478c639b6dddc.hot-update.js", + "/js/main.3556dd93d5467ba7578f.hot-update.js": "/js/main.3556dd93d5467ba7578f.hot-update.js", + "/js/main.d4752d11ab4947241ce2.hot-update.js": "/js/main.d4752d11ab4947241ce2.hot-update.js", + "/js/main.e4e417569a21748ac27c.hot-update.js": "/js/main.e4e417569a21748ac27c.hot-update.js", + "/js/main.31ab6e3fe5983c66b4de.hot-update.js": "/js/main.31ab6e3fe5983c66b4de.hot-update.js", + "/js/main.4506bb17444908aa41e4.hot-update.js": "/js/main.4506bb17444908aa41e4.hot-update.js", + "/js/main.465fec596123d2a33496.hot-update.js": "/js/main.465fec596123d2a33496.hot-update.js", + "/js/main.df3aea3e25f48b02f55c.hot-update.js": "/js/main.df3aea3e25f48b02f55c.hot-update.js", + "/js/main.a64a4facc8cdb6a27254.hot-update.js": "/js/main.a64a4facc8cdb6a27254.hot-update.js", + "/js/main.2cfa2fde63ab332f6217.hot-update.js": "/js/main.2cfa2fde63ab332f6217.hot-update.js", + "/js/main.d1b1a4dab844221ff2d4.hot-update.js": "/js/main.d1b1a4dab844221ff2d4.hot-update.js", + "/js/main.401276474ed29c8ef833.hot-update.js": "/js/main.401276474ed29c8ef833.hot-update.js", + "/js/main.de65f4d9e0bf1e34dd35.hot-update.js": "/js/main.de65f4d9e0bf1e34dd35.hot-update.js", + "/js/main.4cff35830fcab78517a2.hot-update.js": "/js/main.4cff35830fcab78517a2.hot-update.js", + "/js/main.a092dc0af19cf2b87b22.hot-update.js": "/js/main.a092dc0af19cf2b87b22.hot-update.js", + "/js/main.45c685c3ee231c4b52e1.hot-update.js": "/js/main.45c685c3ee231c4b52e1.hot-update.js", + "/js/main.0f25388706a75d546630.hot-update.js": "/js/main.0f25388706a75d546630.hot-update.js", + "/js/main.b20b819e30d8535d32ca.hot-update.js": "/js/main.b20b819e30d8535d32ca.hot-update.js", + "/js/main.6cc87644e20a2ec17250.hot-update.js": "/js/main.6cc87644e20a2ec17250.hot-update.js", + "/js/main.144619637eba40e6f022.hot-update.js": "/js/main.144619637eba40e6f022.hot-update.js", + "/js/main.d327a1158c2756681935.hot-update.js": "/js/main.d327a1158c2756681935.hot-update.js", + "/js/main.c4483a903ccc551c48f5.hot-update.js": "/js/main.c4483a903ccc551c48f5.hot-update.js", + "/js/main.2e1ba7646fe92963192c.hot-update.js": "/js/main.2e1ba7646fe92963192c.hot-update.js", + "/js/main.2b62171bbbe844b488f3.hot-update.js": "/js/main.2b62171bbbe844b488f3.hot-update.js", + "/js/main.c83c87f33fefb0d5095c.hot-update.js": "/js/main.c83c87f33fefb0d5095c.hot-update.js", + "/js/main.34c17c3fe4b99b277081.hot-update.js": "/js/main.34c17c3fe4b99b277081.hot-update.js", + "/js/main.dfd8477f6cc1005038b5.hot-update.js": "/js/main.dfd8477f6cc1005038b5.hot-update.js", + "/js/main.958c8568de3cdf55a48f.hot-update.js": "/js/main.958c8568de3cdf55a48f.hot-update.js", + "/js/main.d60efae4269157afdea4.hot-update.js": "/js/main.d60efae4269157afdea4.hot-update.js", + "/js/main.c82f65f2d15889961770.hot-update.js": "/js/main.c82f65f2d15889961770.hot-update.js", + "/js/main.994ce6d70c8e507d70ad.hot-update.js": "/js/main.994ce6d70c8e507d70ad.hot-update.js", + "/js/main.d3a7731565736f2efc4f.hot-update.js": "/js/main.d3a7731565736f2efc4f.hot-update.js", + "/js/main.c3055934074cef2f8658.hot-update.js": "/js/main.c3055934074cef2f8658.hot-update.js", + "/js/main.1a9211874c7670611fa9.hot-update.js": "/js/main.1a9211874c7670611fa9.hot-update.js", + "/js/main.c384d3c61f6683842ff6.hot-update.js": "/js/main.c384d3c61f6683842ff6.hot-update.js", + "/js/main.0a1d1d6fab186adde3f3.hot-update.js": "/js/main.0a1d1d6fab186adde3f3.hot-update.js", + "/js/main.e8e51b9f621ac71e6824.hot-update.js": "/js/main.e8e51b9f621ac71e6824.hot-update.js", + "/js/main.775033763e36c26f30b1.hot-update.js": "/js/main.775033763e36c26f30b1.hot-update.js", + "/js/main.8ed87c301eec11775e7c.hot-update.js": "/js/main.8ed87c301eec11775e7c.hot-update.js", + "/js/main.54178160887d0b92212b.hot-update.js": "/js/main.54178160887d0b92212b.hot-update.js", + "/js/main.89c18406b0ee0cd034a0.hot-update.js": "/js/main.89c18406b0ee0cd034a0.hot-update.js", + "/js/main.511f968245b7479a82a9.hot-update.js": "/js/main.511f968245b7479a82a9.hot-update.js", + "/js/main.7482ef499551ac74d714.hot-update.js": "/js/main.7482ef499551ac74d714.hot-update.js", + "/js/main.1ef12a69cc1c7148c9e9.hot-update.js": "/js/main.1ef12a69cc1c7148c9e9.hot-update.js", + "/js/main.ca2cae0e79ae99c6a39b.hot-update.js": "/js/main.ca2cae0e79ae99c6a39b.hot-update.js", + "/js/main.ecd645c40985db5b2b63.hot-update.js": "/js/main.ecd645c40985db5b2b63.hot-update.js", + "/js/main.76cb454a020aa4748f6d.hot-update.js": "/js/main.76cb454a020aa4748f6d.hot-update.js", + "/js/main.1c2b5d0ffa57bd499fac.hot-update.js": "/js/main.1c2b5d0ffa57bd499fac.hot-update.js", + "/js/main.25e0c7c86202c8fc97cb.hot-update.js": "/js/main.25e0c7c86202c8fc97cb.hot-update.js", + "/js/main.ebd9eec92676a7b78555.hot-update.js": "/js/main.ebd9eec92676a7b78555.hot-update.js", + "/js/main.531f9b2830c4f83dfc8e.hot-update.js": "/js/main.531f9b2830c4f83dfc8e.hot-update.js", + "/js/main.f8fa5f29023dfdaa9cbd.hot-update.js": "/js/main.f8fa5f29023dfdaa9cbd.hot-update.js", + "/js/main.7e68c966f53cbf5f550a.hot-update.js": "/js/main.7e68c966f53cbf5f550a.hot-update.js", + "/js/main.424881ff861d3b67909c.hot-update.js": "/js/main.424881ff861d3b67909c.hot-update.js", + "/js/main.2ef8fb4ea78052fe413a.hot-update.js": "/js/main.2ef8fb4ea78052fe413a.hot-update.js", + "/js/main.3bd05708ee122ca84f22.hot-update.js": "/js/main.3bd05708ee122ca84f22.hot-update.js", + "/js/main.1a57bcaa919c733d2363.hot-update.js": "/js/main.1a57bcaa919c733d2363.hot-update.js", + "/js/main.7b1e18cb2577dcdb780e.hot-update.js": "/js/main.7b1e18cb2577dcdb780e.hot-update.js", + "/js/main.54ca8adfafdcafc86b0f.hot-update.js": "/js/main.54ca8adfafdcafc86b0f.hot-update.js", + "/js/main.2e70035a3831fd25a670.hot-update.js": "/js/main.2e70035a3831fd25a670.hot-update.js", + "/js/main.f7dbfad8184fb6bd1f3b.hot-update.js": "/js/main.f7dbfad8184fb6bd1f3b.hot-update.js" } diff --git a/resources/js/components/VueFileManagerComponents/FilesView/ContextMenu.vue b/resources/js/components/VueFileManagerComponents/FilesView/ContextMenu.vue index cd779ece..1b36ee9d 100644 --- a/resources/js/components/VueFileManagerComponents/FilesView/ContextMenu.vue +++ b/resources/js/components/VueFileManagerComponents/FilesView/ContextMenu.vue @@ -7,7 +7,7 @@ ref="contextmenu" > -