mirror of
https://github.com/VueFileManager/vuefilemanager.git
synced 2026-04-18 00:02:15 +00:00
s3 bandwidth usage fix
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
APP_NAME=Laravel
|
||||
APP_ENV=local
|
||||
APP_KEY=base64:IOcs+sRmD3FGF8qveF6VTgxB26b0ShnwmqIZp/fYNGo=
|
||||
APP_KEY=base64:X7/vUveJtTmrC1J+UFXb9m6l1RBbAoJvNyj7sMC+GFI=
|
||||
APP_DEBUG=true
|
||||
APP_URL=http://localhost
|
||||
APP_DEMO=false
|
||||
|
||||
@@ -82,6 +82,7 @@
|
||||
"database/factories"
|
||||
],
|
||||
"files": [
|
||||
"src/Support/errors.php",
|
||||
"src/Support/helpers.php"
|
||||
]
|
||||
},
|
||||
|
||||
@@ -1,75 +1,75 @@
|
||||
{
|
||||
"/js/main.js": "/js/main.js",
|
||||
"/chunks/request.js": "/chunks/request.js?id=37e3e34fbcc98d4c",
|
||||
"/chunks/request-upload.js": "/chunks/request-upload.js?id=2a6d910114ffb8d2",
|
||||
"/chunks/setup-wizard.js": "/chunks/setup-wizard.js?id=3c2fc454c3fce8d2",
|
||||
"/chunks/status-check.js": "/chunks/status-check.js?id=ea0f79fc9a604cff",
|
||||
"/chunks/purchase-code.js": "/chunks/purchase-code.js?id=c1df85c34d7e9521",
|
||||
"/chunks/database.js": "/chunks/database.js?id=c686d46622194c7e",
|
||||
"/chunks/environment-setup.js": "/chunks/environment-setup.js?id=d045f6827f61ac9b",
|
||||
"/chunks/app-setup.js": "/chunks/app-setup.js?id=c37d19ccd20b6656",
|
||||
"/chunks/admin-account.js": "/chunks/admin-account.js?id=666e7ee49b02b57c",
|
||||
"/chunks/shared.js": "/chunks/shared.js?id=557e8a1b4256d65a",
|
||||
"/chunks/shared/browser.js": "/chunks/shared/browser.js?id=a9710655d75c8079",
|
||||
"/chunks/shared/single-file.js": "/chunks/shared/single-file.js?id=7865f79c2905e81d",
|
||||
"/chunks/shared/authenticate.js": "/chunks/shared/authenticate.js?id=ca8cc89fe5982782",
|
||||
"/chunks/not-found.js": "/chunks/not-found.js?id=4cb8d3a7a2212c3c",
|
||||
"/chunks/temporary-unavailable.js": "/chunks/temporary-unavailable.js?id=c71981d946a9ca71",
|
||||
"/chunks/admin.js": "/chunks/admin.js?id=4c86279cd6e85aa5",
|
||||
"/chunks/dashboard.js": "/chunks/dashboard.js?id=98dade7f03d93826",
|
||||
"/chunks/invoices.js": "/chunks/invoices.js?id=70fb9a603be2f554",
|
||||
"/chunks/subscriptions.js": "/chunks/subscriptions.js?id=94e96e1bb505ae59",
|
||||
"/chunks/pages.js": "/chunks/pages.js?id=d1f5d211e9dfc4ae",
|
||||
"/chunks/page-edit.js": "/chunks/page-edit.js?id=c241f8733acb584f",
|
||||
"/chunks/plans.js": "/chunks/plans.js?id=f6e9d2f34fac6d79",
|
||||
"/chunks/users.js": "/chunks/users.js?id=651b8af7afecc88e",
|
||||
"/chunks/user-create.js": "/chunks/user-create.js?id=3b0c4a348a5b1857",
|
||||
"/chunks/plan-create/fixed.js": "/chunks/plan-create/fixed.js?id=81dcec66b3ab0f9c",
|
||||
"/chunks/plan-create/metered.js": "/chunks/plan-create/metered.js?id=e7d07663f1ec94fb",
|
||||
"/chunks/user.js": "/chunks/user.js?id=dae4ac26750f99d0",
|
||||
"/chunks/user-detail.js": "/chunks/user-detail.js?id=207de969e16d9284",
|
||||
"/chunks/user-storage.js": "/chunks/user-storage.js?id=d56b28f604b1d012",
|
||||
"/chunks/user-subscription.js": "/chunks/user-subscription.js?id=25a31d9cbbb0507a",
|
||||
"/chunks/user-password.js": "/chunks/user-password.js?id=be5d5cdf90f1e0de",
|
||||
"/chunks/user-delete.js": "/chunks/user-delete.js?id=0783eb95a7226ff8",
|
||||
"/chunks/plan.js": "/chunks/plan.js?id=d8ffa85dc9b68966",
|
||||
"/chunks/plan-subscribers.js": "/chunks/plan-subscribers.js?id=3010ddb4ba7419e9",
|
||||
"/chunks/plan-settings.js": "/chunks/plan-settings.js?id=f41fe30f2273279c",
|
||||
"/chunks/plan-delete.js": "/chunks/plan-delete.js?id=bf5e732edaff3608",
|
||||
"/chunks/payments.js": "/chunks/payments.js?id=051e8246e2b5c9d0",
|
||||
"/chunks/payments/billings.js": "/chunks/payments/billings.js?id=893998fa7380c5a0",
|
||||
"/chunks/payments/settings.js": "/chunks/payments/settings.js?id=189bb7b91cd6a32a",
|
||||
"/chunks/app-settings.js": "/chunks/app-settings.js?id=692d3291fb9d2cf7",
|
||||
"/chunks/app-appearance.js": "/chunks/app-appearance.js?id=e8377974f2444a44",
|
||||
"/chunks/app-index.js": "/chunks/app-index.js?id=9aadc4841d83e821",
|
||||
"/chunks/app-environment.js": "/chunks/app-environment.js?id=637b39d290081e0d",
|
||||
"/chunks/app-others.js": "/chunks/app-others.js?id=ac2dccb4b4a87bfb",
|
||||
"/chunks/app-sign-in-out.js": "/chunks/app-sign-in-out.js?id=04fbf6846bbc6ade",
|
||||
"/chunks/app-adsense.js": "/chunks/app-adsense.js?id=18842ed46783ea39",
|
||||
"/chunks/app-server.js": "/chunks/app-server.js?id=6dbdc01c6b0e65b3",
|
||||
"/chunks/app-language.js": "/chunks/app-language.js?id=db16d24415743d42",
|
||||
"/chunks/homepage.js": "/chunks/homepage.js?id=828e3e90bf35b652",
|
||||
"/chunks/dynamic-page.js": "/chunks/dynamic-page.js?id=e110e8923b6ca22f",
|
||||
"/chunks/contact-us.js": "/chunks/contact-us.js?id=5ca104a75598dd39",
|
||||
"/chunks/successfully-email-verified.js": "/chunks/successfully-email-verified.js?id=3153532f0d2273c8",
|
||||
"/chunks/successfully-email-send.js": "/chunks/successfully-email-send.js?id=d630ed9f6f558509",
|
||||
"/chunks/sign-in.js": "/chunks/sign-in.js?id=8cec25f9f11b217a",
|
||||
"/chunks/sign-up.js": "/chunks/sign-up.js?id=c1cadbfd4ac0df7b",
|
||||
"/chunks/forgotten-password.js": "/chunks/forgotten-password.js?id=9c62b8573fbdd567",
|
||||
"/chunks/create-new-password.js": "/chunks/create-new-password.js?id=926b35b6745d99ba",
|
||||
"/chunks/settings.js": "/chunks/settings.js?id=efd739cafc57e762",
|
||||
"/chunks/profile.js": "/chunks/profile.js?id=013b252e73d160d0",
|
||||
"/chunks/settings-password.js": "/chunks/settings-password.js?id=149343604362b7df",
|
||||
"/chunks/settings-storage.js": "/chunks/settings-storage.js?id=68765cac4e648e90",
|
||||
"/chunks/billing.js": "/chunks/billing.js?id=c77093c6a0dc65e3",
|
||||
"/chunks/platform.js": "/chunks/platform.js?id=90cb99dd8c57c5a0",
|
||||
"/chunks/files.js": "/chunks/files.js?id=5d6eb9b9f9ecd296",
|
||||
"/chunks/recent-uploads.js": "/chunks/recent-uploads.js?id=827d3a5dcce159b5",
|
||||
"/chunks/my-shared-items.js": "/chunks/my-shared-items.js?id=2a4e4e0db02cbcbb",
|
||||
"/chunks/trash.js": "/chunks/trash.js?id=8362aa0f91231350",
|
||||
"/chunks/team-folders.js": "/chunks/team-folders.js?id=0a46fecf35a23406",
|
||||
"/chunks/shared-with-me.js": "/chunks/shared-with-me.js?id=77a33583775c6d8f",
|
||||
"/chunks/invitation.js": "/chunks/invitation.js?id=64a211c90b505767",
|
||||
"/chunks/request.js": "/chunks/request.js?id=5b796b8410a96a49",
|
||||
"/chunks/request-upload.js": "/chunks/request-upload.js?id=b1aaf8357a30794a",
|
||||
"/chunks/setup-wizard.js": "/chunks/setup-wizard.js?id=19a0784e59d768ec",
|
||||
"/chunks/status-check.js": "/chunks/status-check.js?id=51a75f0b3b260189",
|
||||
"/chunks/purchase-code.js": "/chunks/purchase-code.js?id=df5bd89528649783",
|
||||
"/chunks/database.js": "/chunks/database.js?id=15cc488117dccf7b",
|
||||
"/chunks/environment-setup.js": "/chunks/environment-setup.js?id=e1ad83583367917a",
|
||||
"/chunks/app-setup.js": "/chunks/app-setup.js?id=288594cd7f628cf8",
|
||||
"/chunks/admin-account.js": "/chunks/admin-account.js?id=916450217130f3b8",
|
||||
"/chunks/shared.js": "/chunks/shared.js?id=3b541c484831dd33",
|
||||
"/chunks/shared/browser.js": "/chunks/shared/browser.js?id=3dc8fdb008b6ff5f",
|
||||
"/chunks/shared/single-file.js": "/chunks/shared/single-file.js?id=04889fdd4175d9e1",
|
||||
"/chunks/shared/authenticate.js": "/chunks/shared/authenticate.js?id=672e931a9fb0b672",
|
||||
"/chunks/not-found.js": "/chunks/not-found.js?id=9f6ce23ce5d969f1",
|
||||
"/chunks/temporary-unavailable.js": "/chunks/temporary-unavailable.js?id=f564565faa09d6d6",
|
||||
"/chunks/admin.js": "/chunks/admin.js?id=45c706127b10950f",
|
||||
"/chunks/dashboard.js": "/chunks/dashboard.js?id=ec5474f5a9da434c",
|
||||
"/chunks/invoices.js": "/chunks/invoices.js?id=1416cbf6d1a593ac",
|
||||
"/chunks/subscriptions.js": "/chunks/subscriptions.js?id=5bf6704f5b599f36",
|
||||
"/chunks/pages.js": "/chunks/pages.js?id=c8380d571e91e8be",
|
||||
"/chunks/page-edit.js": "/chunks/page-edit.js?id=fb3f9eda3dc1d15c",
|
||||
"/chunks/plans.js": "/chunks/plans.js?id=c8506e0e20966ef7",
|
||||
"/chunks/users.js": "/chunks/users.js?id=ec687ee365c4248a",
|
||||
"/chunks/user-create.js": "/chunks/user-create.js?id=8dd9d29f024132f5",
|
||||
"/chunks/plan-create/fixed.js": "/chunks/plan-create/fixed.js?id=b24d8dbe1f0f706f",
|
||||
"/chunks/plan-create/metered.js": "/chunks/plan-create/metered.js?id=d9f1bcb1fe44a6ae",
|
||||
"/chunks/user.js": "/chunks/user.js?id=c191b906a0496fe5",
|
||||
"/chunks/user-detail.js": "/chunks/user-detail.js?id=f9e17ff98354e984",
|
||||
"/chunks/user-storage.js": "/chunks/user-storage.js?id=a8e0bce4703232a0",
|
||||
"/chunks/user-subscription.js": "/chunks/user-subscription.js?id=27d046c1122783ea",
|
||||
"/chunks/user-password.js": "/chunks/user-password.js?id=23d3aee39f539a3c",
|
||||
"/chunks/user-delete.js": "/chunks/user-delete.js?id=6bea6f8cadf4d74f",
|
||||
"/chunks/plan.js": "/chunks/plan.js?id=4b267375ea9f19b3",
|
||||
"/chunks/plan-subscribers.js": "/chunks/plan-subscribers.js?id=a956ceca6865c50c",
|
||||
"/chunks/plan-settings.js": "/chunks/plan-settings.js?id=715ee86991d5e4db",
|
||||
"/chunks/plan-delete.js": "/chunks/plan-delete.js?id=1ad77372d342326f",
|
||||
"/chunks/payments.js": "/chunks/payments.js?id=dc4586691c25de6f",
|
||||
"/chunks/payments/billings.js": "/chunks/payments/billings.js?id=dd6c9d6a29a47808",
|
||||
"/chunks/payments/settings.js": "/chunks/payments/settings.js?id=5b139952f337c83e",
|
||||
"/chunks/app-settings.js": "/chunks/app-settings.js?id=55da23af2b076069",
|
||||
"/chunks/app-appearance.js": "/chunks/app-appearance.js?id=a694a01f3641712c",
|
||||
"/chunks/app-index.js": "/chunks/app-index.js?id=efdbfa062749ca00",
|
||||
"/chunks/app-environment.js": "/chunks/app-environment.js?id=9632034e8ded7d34",
|
||||
"/chunks/app-others.js": "/chunks/app-others.js?id=0f84e2ed1230558e",
|
||||
"/chunks/app-sign-in-out.js": "/chunks/app-sign-in-out.js?id=1cfffc99465b9a7a",
|
||||
"/chunks/app-adsense.js": "/chunks/app-adsense.js?id=a5dc9e715f8561bd",
|
||||
"/chunks/app-server.js": "/chunks/app-server.js?id=4510d63685353c68",
|
||||
"/chunks/app-language.js": "/chunks/app-language.js?id=7e5f3d5ec447e397",
|
||||
"/chunks/homepage.js": "/chunks/homepage.js?id=b6597181c9e4353d",
|
||||
"/chunks/dynamic-page.js": "/chunks/dynamic-page.js?id=2504793131107b1f",
|
||||
"/chunks/contact-us.js": "/chunks/contact-us.js?id=9adc7e145be4e160",
|
||||
"/chunks/successfully-email-verified.js": "/chunks/successfully-email-verified.js?id=25b805ade5230382",
|
||||
"/chunks/successfully-email-send.js": "/chunks/successfully-email-send.js?id=f4562229776d9f56",
|
||||
"/chunks/sign-in.js": "/chunks/sign-in.js?id=0d48d229038a3a1e",
|
||||
"/chunks/sign-up.js": "/chunks/sign-up.js?id=bb92bad614e60d45",
|
||||
"/chunks/forgotten-password.js": "/chunks/forgotten-password.js?id=50a1bc5e4ed86ec9",
|
||||
"/chunks/create-new-password.js": "/chunks/create-new-password.js?id=f652de052dba55c1",
|
||||
"/chunks/settings.js": "/chunks/settings.js?id=b918d0cf757fafe3",
|
||||
"/chunks/profile.js": "/chunks/profile.js?id=7d3719710c55ceeb",
|
||||
"/chunks/settings-password.js": "/chunks/settings-password.js?id=11d4331650cac280",
|
||||
"/chunks/settings-storage.js": "/chunks/settings-storage.js?id=994b669a56fd417b",
|
||||
"/chunks/billing.js": "/chunks/billing.js?id=4ea77ab501b8c575",
|
||||
"/chunks/platform.js": "/chunks/platform.js?id=486a1e4ae8ae42df",
|
||||
"/chunks/files.js": "/chunks/files.js?id=060b1a34dfdbe97c",
|
||||
"/chunks/recent-uploads.js": "/chunks/recent-uploads.js?id=0f63bbc02ad8f3e1",
|
||||
"/chunks/my-shared-items.js": "/chunks/my-shared-items.js?id=0a06d32b4cf8b52c",
|
||||
"/chunks/trash.js": "/chunks/trash.js?id=ac5389500f8f7912",
|
||||
"/chunks/team-folders.js": "/chunks/team-folders.js?id=c0a03c6937856ca1",
|
||||
"/chunks/shared-with-me.js": "/chunks/shared-with-me.js?id=88fa008db0c6a4f6",
|
||||
"/chunks/invitation.js": "/chunks/invitation.js?id=9ed8456c9d6d5ce1",
|
||||
"/css/tailwind.css": "/css/tailwind.css",
|
||||
"/css/app.css": "/css/app.css"
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<PopupHeader :title="$t('notifications')" icon="bell" />
|
||||
|
||||
<!--Content-->
|
||||
<PopupContent>
|
||||
<PopupContent v-if="readNotifications && unreadNotifications">
|
||||
<MobileActionButton
|
||||
v-if="readNotifications.length || unreadNotifications.length"
|
||||
@click.native="$store.dispatch('deleteAllNotifications')"
|
||||
|
||||
0
resources/js/helpers.js
vendored
0
resources/js/helpers.js
vendored
@@ -33,7 +33,7 @@ class Kernel extends ConsoleKernel
|
||||
*/
|
||||
protected function schedule(Schedule $schedule): void
|
||||
{
|
||||
if (! is_storage_driver('local')) {
|
||||
if (! isStorageDriver('local')) {
|
||||
$schedule->call(
|
||||
fn () => resolve(DeleteFailedFilesAction::class)()
|
||||
)->everySixHours();
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<?php
|
||||
namespace App\Users\Controllers;
|
||||
|
||||
use Illuminate\Http\Response;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Symfony\Component\HttpFoundation\StreamedResponse;
|
||||
use Illuminate\Contracts\Filesystem\FileNotFoundException;
|
||||
|
||||
class GetAvatarController
|
||||
{
|
||||
@@ -12,10 +12,10 @@ class GetAvatarController
|
||||
*/
|
||||
public function __invoke(
|
||||
string $basename
|
||||
): StreamedResponse | FileNotFoundException {
|
||||
): StreamedResponse | Response {
|
||||
// Check if file exist
|
||||
if (! Storage::exists("/avatars/$basename")) {
|
||||
abort(404);
|
||||
return response('File not found', 404);
|
||||
}
|
||||
|
||||
// Return avatar
|
||||
|
||||
@@ -42,7 +42,7 @@ class UserSetting extends Model
|
||||
$link = [];
|
||||
|
||||
// Get avatar from external storage
|
||||
if ($this->attributes['avatar'] && ! is_storage_driver('local')) {
|
||||
if ($this->attributes['avatar'] && ! isStorageDriver('local')) {
|
||||
foreach (config('vuefilemanager.avatar_sizes') as $item) {
|
||||
$filePath = "avatars/{$item['name']}-{$this->attributes['avatar']}";
|
||||
|
||||
|
||||
@@ -2,36 +2,44 @@
|
||||
namespace Domain\Files\Actions;
|
||||
|
||||
use Domain\Files\Models\File;
|
||||
use Illuminate\Http\Response;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Symfony\Component\HttpFoundation\BinaryFileResponse;
|
||||
use Symfony\Component\HttpFoundation\StreamedResponse;
|
||||
|
||||
class DownloadFileAction
|
||||
{
|
||||
/**
|
||||
* Call and download file
|
||||
*/
|
||||
public function __invoke(
|
||||
File $file,
|
||||
string $user_id,
|
||||
): BinaryFileResponse {
|
||||
public function __invoke(File $file): Response|StreamedResponse|RedirectResponse
|
||||
{
|
||||
// Get file path
|
||||
$path = "files/$user_id/$file->basename";
|
||||
|
||||
// Check if file exist
|
||||
if (! Storage::exists($path)) {
|
||||
abort(404);
|
||||
}
|
||||
$filePath = "files/$file->user_id/$file->basename";
|
||||
|
||||
// Get pretty name
|
||||
$pretty_name = get_pretty_name($file->basename, $file->name, $file->mimetype);
|
||||
$fileName = getPrettyName($file->basename, $file->name, $file->mimetype);
|
||||
|
||||
return response()
|
||||
->download(Storage::path($path), $pretty_name, [
|
||||
'Accept-Ranges' => 'bytes',
|
||||
'Content-Type' => Storage::mimeType($path),
|
||||
'Content-Length' => Storage::size($path),
|
||||
'Content-Range' => 'bytes 0-600/' . Storage::size($path),
|
||||
'Content-Disposition' => "attachment; filename=$pretty_name",
|
||||
]);
|
||||
// Check if file exist
|
||||
if (! Storage::exists($filePath)) {
|
||||
return response('The file not found.', 404);
|
||||
}
|
||||
|
||||
// Format response header
|
||||
$header = [
|
||||
'ResponseAcceptRanges' => 'bytes',
|
||||
'ResponseContentType' => Storage::mimeType($filePath),
|
||||
'ResponseContentLength' => Storage::size($filePath),
|
||||
'ResponseContentRange' => 'bytes 0-600/' . Storage::size($filePath),
|
||||
'ResponseContentDisposition' => "attachment; filename=$fileName",
|
||||
];
|
||||
|
||||
// If s3 redirect to temporary download url
|
||||
if (isStorageDriver('s3')) {
|
||||
return redirect()->away(Storage::temporaryUrl($filePath, now()->addHour(), $header));
|
||||
}
|
||||
|
||||
// Download file
|
||||
return Storage::download($filePath, $fileName, $header);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
namespace Domain\Files\Actions;
|
||||
|
||||
use Domain\Files\Models\File;
|
||||
use Illuminate\Http\Response;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Symfony\Component\HttpFoundation\StreamedResponse;
|
||||
|
||||
@@ -13,18 +14,18 @@ class DownloadThumbnailAction
|
||||
public function __invoke(
|
||||
string $filename,
|
||||
File $file
|
||||
): StreamedResponse {
|
||||
): StreamedResponse|Response {
|
||||
// Get file path
|
||||
$path = "/files/$file->user_id/$filename";
|
||||
$filePath = "/files/$file->user_id/$filename";
|
||||
|
||||
// Check if file exist
|
||||
if (! Storage::exists($path)) {
|
||||
if (! Storage::exists($filePath)) {
|
||||
// Get original file path
|
||||
$substituteFilePath = "/files/$file->user_id/$file->basename";
|
||||
|
||||
// Check if original file exist
|
||||
if (! Storage::exists($substituteFilePath)) {
|
||||
abort(404);
|
||||
return response('File not found', 404);
|
||||
}
|
||||
|
||||
// Return image thumbnail
|
||||
@@ -32,6 +33,6 @@ class DownloadThumbnailAction
|
||||
}
|
||||
|
||||
// Return image thumbnail
|
||||
return Storage::download($path, $filename);
|
||||
return Storage::download($filePath, $filename);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ class UploadFileAction
|
||||
($this->createImageThumbnail)($fileName, $file, $user->id);
|
||||
|
||||
// Move files to external storage
|
||||
if (! is_storage_driver('local')) {
|
||||
if (! isStorageDriver('local')) {
|
||||
($this->moveFileToExternalStorage)($fileName, $user->id);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,12 +2,13 @@
|
||||
namespace Domain\Files\Controllers\FileAccess;
|
||||
|
||||
use Gate;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Response;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Domain\Files\Models\File as UserFile;
|
||||
use Domain\Files\Models\File;
|
||||
use Domain\Files\Actions\DownloadFileAction;
|
||||
use Domain\Traffic\Actions\RecordDownloadAction;
|
||||
use Symfony\Component\HttpFoundation\BinaryFileResponse;
|
||||
use Symfony\Component\HttpFoundation\StreamedResponse;
|
||||
|
||||
class GetFileController extends Controller
|
||||
{
|
||||
@@ -19,31 +20,30 @@ class GetFileController extends Controller
|
||||
|
||||
public function __invoke(
|
||||
string $filename,
|
||||
): Response|BinaryFileResponse {
|
||||
$file = UserFile::withTrashed()
|
||||
): Response|RedirectResponse|StreamedResponse {
|
||||
// Get requested file
|
||||
$file = File::withTrashed()
|
||||
->where('basename', $filename)
|
||||
->firstOrFail();
|
||||
|
||||
// Check if user can download file
|
||||
if (! $file->owner->canDownload()) {
|
||||
return response([
|
||||
'type' => 'error',
|
||||
'message' => 'This user action is not allowed.',
|
||||
], 401);
|
||||
return response(userActionNotAllowedError(), 401);
|
||||
}
|
||||
|
||||
// Check if user has privileges to download file
|
||||
if (! Gate::any(['can-edit', 'can-view'], [$file, null])) {
|
||||
abort(403, 'Access Denied');
|
||||
return response(accessDeniedError(), 403);
|
||||
}
|
||||
|
||||
// TODO: resolve video buffering
|
||||
|
||||
// Store user download size
|
||||
($this->recordDownload)(
|
||||
file_size: (int) $file->getRawOriginal('filesize'),
|
||||
file_size: $file->filesize,
|
||||
user_id: $file->user_id,
|
||||
);
|
||||
|
||||
return ($this->downloadFile)($file, $file->user_id);
|
||||
return ($this->downloadFile)($file);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ use Illuminate\Http\Request;
|
||||
use Domain\Files\Models\File;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Domain\Files\Actions\DownloadThumbnailAction;
|
||||
use Illuminate\Http\Response;
|
||||
use Symfony\Component\HttpFoundation\StreamedResponse;
|
||||
use Illuminate\Contracts\Filesystem\FileNotFoundException;
|
||||
|
||||
@@ -13,19 +14,21 @@ class GetThumbnailController extends Controller
|
||||
{
|
||||
public function __construct(
|
||||
private DownloadThumbnailAction $downloadThumbnail,
|
||||
) {
|
||||
}
|
||||
) {}
|
||||
|
||||
public function __invoke(
|
||||
Request $request,
|
||||
string $filename,
|
||||
): FileNotFoundException | StreamedResponse {
|
||||
): FileNotFoundException | StreamedResponse | Response {
|
||||
|
||||
// Get requested thumbnail
|
||||
$file = File::withTrashed()
|
||||
->where('basename', substr($filename, 3))
|
||||
->firstOrFail();
|
||||
|
||||
// Check if user has privileges to download file
|
||||
if (! Gate::any(['can-edit', 'can-view'], [$file, null])) {
|
||||
abort(403, 'Access Denied');
|
||||
return response(accessDeniedError(), 403);
|
||||
}
|
||||
|
||||
return ($this->downloadThumbnail)($filename, $file);
|
||||
|
||||
@@ -2,14 +2,15 @@
|
||||
namespace Domain\Files\Controllers\FileAccess;
|
||||
|
||||
use Domain\Files\Models\File;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Response;
|
||||
use Domain\Sharing\Models\Share;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Domain\Files\Actions\DownloadFileAction;
|
||||
use Domain\Traffic\Actions\RecordDownloadAction;
|
||||
use Domain\Sharing\Actions\ProtectShareRecordAction;
|
||||
use Symfony\Component\HttpFoundation\BinaryFileResponse;
|
||||
use Domain\Sharing\Actions\VerifyAccessToItemWithinAction;
|
||||
use Symfony\Component\HttpFoundation\StreamedResponse;
|
||||
|
||||
/**
|
||||
* Get file public
|
||||
@@ -27,13 +28,10 @@ class VisitorGetFileController extends Controller
|
||||
public function __invoke(
|
||||
$filename,
|
||||
Share $shared,
|
||||
): BinaryFileResponse|Response {
|
||||
): StreamedResponse|RedirectResponse|Response {
|
||||
// Check if user can download file
|
||||
if (! $shared->user->canDownload()) {
|
||||
return response([
|
||||
'type' => 'error',
|
||||
'message' => 'This user action is not allowed.',
|
||||
], 401);
|
||||
return response(userActionNotAllowedError(), 401);
|
||||
}
|
||||
|
||||
// Check ability to access protected share files
|
||||
@@ -49,11 +47,11 @@ class VisitorGetFileController extends Controller
|
||||
|
||||
// Store user download size
|
||||
($this->recordDownload)(
|
||||
file_size: (int) $file->getRawOriginal('filesize'),
|
||||
file_size: $file->filesize,
|
||||
user_id: $shared->user_id,
|
||||
);
|
||||
|
||||
// Finally, download file
|
||||
return ($this->downloadFile)($file, $shared->user_id);
|
||||
return ($this->downloadFile)($file);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ class VisitorGetThumbnailController extends Controller
|
||||
|
||||
// Store user download size
|
||||
($this->recordDownload)(
|
||||
file_size: (int) $file->getRawOriginal('filesize'),
|
||||
file_size: $file->filesize,
|
||||
user_id: $shared->user_id,
|
||||
);
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
* @property string user_id
|
||||
* @property string parent_id
|
||||
* @property string thumbnail
|
||||
* @property string filesize
|
||||
* @property int filesize
|
||||
* @property string type
|
||||
* @property string basename
|
||||
* @property string name
|
||||
@@ -100,7 +100,7 @@ class File extends Model
|
||||
->all();
|
||||
|
||||
// Generate thumbnail link for external storage service
|
||||
if ($this->type === 'image' && ! is_storage_driver('local')) {
|
||||
if ($this->type === 'image' && ! isStorageDriver('local')) {
|
||||
foreach ($thumbnail_sizes as $item) {
|
||||
$filePath = "files/{$this->user_id}/{$item['name']}-{$this->basename}";
|
||||
|
||||
@@ -139,23 +139,6 @@ class File extends Model
|
||||
*/
|
||||
public function getFileUrlAttribute(): string
|
||||
{
|
||||
// Get file from external storage
|
||||
if (! is_storage_driver('local')) {
|
||||
$file_pretty_name = is_storage_driver('backblaze')
|
||||
? Str::snake(mb_strtolower($this->attributes['name']))
|
||||
: get_pretty_name($this->attributes['basename'], $this->attributes['name'], $this->attributes['mimetype']);
|
||||
|
||||
$header = [
|
||||
'ResponseAcceptRanges' => 'bytes',
|
||||
'ResponseContentType' => $this->attributes['mimetype'],
|
||||
'ResponseContentLength' => $this->attributes['filesize'],
|
||||
'ResponseContentRange' => 'bytes 0-600/' . $this->attributes['filesize'],
|
||||
'ResponseContentDisposition' => 'attachment; filename=' . $file_pretty_name,
|
||||
];
|
||||
|
||||
return Storage::temporaryUrl("files/$this->user_id/{$this->attributes['basename']}", now()->addDay(), $header);
|
||||
}
|
||||
|
||||
// Get thumbnail from local storage
|
||||
$route = route('file', ['name' => $this->attributes['basename']]);
|
||||
|
||||
@@ -166,7 +149,6 @@ class File extends Model
|
||||
|
||||
// Set upload request public url
|
||||
if ($this->uploadRequestAccess) {
|
||||
// TODO: review request for s3
|
||||
$route .= "/upload-request/$this->uploadRequestAccess";
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<?php
|
||||
namespace Domain\Settings\Controllers;
|
||||
|
||||
use Illuminate\Http\Response;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Symfony\Component\HttpFoundation\StreamedResponse;
|
||||
use Illuminate\Contracts\Filesystem\FileNotFoundException;
|
||||
|
||||
class GetAppImageController
|
||||
{
|
||||
@@ -12,10 +12,10 @@ class GetAppImageController
|
||||
*/
|
||||
public function __invoke(
|
||||
string $basename
|
||||
): StreamedResponse | FileNotFoundException {
|
||||
): StreamedResponse | Response {
|
||||
// Check if file exist
|
||||
if (! Storage::exists("/system/$basename")) {
|
||||
abort(404);
|
||||
return response('File not found', 404);
|
||||
}
|
||||
|
||||
// Return avatar
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace Domain\Sharing\Controllers;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Domain\Files\Actions\DownloadFileAction;
|
||||
use Domain\Files\Models\File;
|
||||
use Domain\Sharing\Actions\ProtectShareRecordAction;
|
||||
use Domain\Sharing\Actions\VerifyAccessToItemWithinAction;
|
||||
@@ -16,10 +17,11 @@ use Symfony\Component\HttpFoundation\StreamedResponse;
|
||||
class DirectlyDownloadFileController extends Controller
|
||||
{
|
||||
public function __construct(
|
||||
private DownloadFileAction $downloadFile,
|
||||
private RecordDownloadAction $recordDownload,
|
||||
private ProtectShareRecordAction $protectShareRecord,
|
||||
private VerifyAccessToItemWithinAction $verifyAccessToItemWithin,
|
||||
){}
|
||||
) {}
|
||||
|
||||
public function __invoke(
|
||||
Share $share
|
||||
@@ -51,32 +53,10 @@ class DirectlyDownloadFileController extends Controller
|
||||
|
||||
// Store user download size
|
||||
($this->recordDownload)(
|
||||
file_size: (int)$file->getRawOriginal('filesize'),
|
||||
file_size: $file->filesize,
|
||||
user_id: $share->user_id,
|
||||
);
|
||||
|
||||
// Get file path
|
||||
$path = "files/$share->user_id/$file->basename";
|
||||
|
||||
// Check if file exist
|
||||
if (!Storage::exists($path)) {
|
||||
abort(404);
|
||||
}
|
||||
|
||||
// Get pretty name
|
||||
$pretty_name = get_pretty_name($file->basename, $file->name, $file->mimetype);
|
||||
|
||||
// If s3 redirect to temporary url
|
||||
if (is_storage_driver('s3')) {
|
||||
return redirect()->away(Storage::temporaryUrl($path, now()->addHour(), [
|
||||
'ResponseAcceptRanges' => 'bytes',
|
||||
'ResponseContentType' => Storage::mimeType($path),
|
||||
'ResponseContentLength' => Storage::size($path),
|
||||
'ResponseContentRange' => 'bytes 0-600/' . Storage::size($path),
|
||||
'ResponseContentDisposition' => "attachment; filename=$pretty_name",
|
||||
]));
|
||||
}
|
||||
|
||||
return Storage::download($path, $pretty_name);
|
||||
return ($this->downloadFile)($file);
|
||||
}
|
||||
}
|
||||
@@ -2,13 +2,12 @@
|
||||
namespace Domain\UploadRequest\Controllers\FileAccess;
|
||||
|
||||
use Domain\Files\Models\File;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Response;
|
||||
use Domain\Files\Actions\DownloadFileAction;
|
||||
use Domain\UploadRequest\Models\UploadRequest;
|
||||
use Domain\Traffic\Actions\RecordDownloadAction;
|
||||
use Illuminate\Contracts\Foundation\Application;
|
||||
use Illuminate\Contracts\Routing\ResponseFactory;
|
||||
use Symfony\Component\HttpFoundation\BinaryFileResponse;
|
||||
use Symfony\Component\HttpFoundation\StreamedResponse;
|
||||
|
||||
/**
|
||||
* Get shared file record
|
||||
@@ -24,7 +23,7 @@ class GetFileFromUploadRequestController
|
||||
public function __invoke(
|
||||
string $filename,
|
||||
UploadRequest $uploadRequest
|
||||
): Application|ResponseFactory|Response|BinaryFileResponse {
|
||||
): StreamedResponse|RedirectResponse|Response {
|
||||
// Get file
|
||||
$file = File::where('user_id', $uploadRequest->user_id)
|
||||
->where('basename', $filename)
|
||||
@@ -32,11 +31,11 @@ class GetFileFromUploadRequestController
|
||||
|
||||
// Store user download size
|
||||
($this->recordDownload)(
|
||||
file_size: (int) $file->getRawOriginal('filesize'),
|
||||
file_size: $file->filesize,
|
||||
user_id: $uploadRequest->user_id,
|
||||
);
|
||||
|
||||
// Finally, download file
|
||||
return ($this->downloadFile)($file, $uploadRequest->user_id);
|
||||
return ($this->downloadFile)($file);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ class GetThumbnailFromUploadRequestController extends Controller
|
||||
|
||||
// Store user download size
|
||||
($this->recordDownload)(
|
||||
file_size: (int) $file->getRawOriginal('filesize'),
|
||||
file_size: $file->filesize,
|
||||
user_id: $uploadRequest->user_id,
|
||||
);
|
||||
|
||||
|
||||
@@ -38,12 +38,12 @@ class ZipAction
|
||||
// Add file into zip
|
||||
if (Storage::exists($filePath)) {
|
||||
// local disk
|
||||
if (is_storage_driver('local')) {
|
||||
if (isStorageDriver('local')) {
|
||||
$zip->add(Storage::path($filePath), $file->name);
|
||||
}
|
||||
|
||||
// s3 client
|
||||
if (is_storage_driver('s3')) {
|
||||
if (isStorageDriver('s3')) {
|
||||
$bucketName = config('filesystems.disks.s3.bucket');
|
||||
|
||||
$zip->add("s3://$bucketName/$filePath", $file->name);
|
||||
@@ -75,12 +75,12 @@ class ZipAction
|
||||
$zipDestination = "{$file['folder_path']}/{$file['name']}";
|
||||
|
||||
// local disk
|
||||
if (is_storage_driver('local')) {
|
||||
if (isStorageDriver('local')) {
|
||||
$zip->add(Storage::path($filePath), $zipDestination);
|
||||
}
|
||||
|
||||
// s3 client
|
||||
if (is_storage_driver('s3')) {
|
||||
if (isStorageDriver('s3')) {
|
||||
$bucketName = config('filesystems.disks.s3.bucket');
|
||||
|
||||
$zip->add("s3://$bucketName/$filePath", $zipDestination);
|
||||
|
||||
15
src/Support/errors.php
Normal file
15
src/Support/errors.php
Normal file
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
function accessDeniedError(): array {
|
||||
return [
|
||||
'type' => 'error',
|
||||
'message' => 'Access Denied',
|
||||
];
|
||||
}
|
||||
|
||||
function userActionNotAllowedError(): array {
|
||||
return [
|
||||
'type' => 'error',
|
||||
'message' => 'This user action is not allowed.',
|
||||
];
|
||||
}
|
||||
@@ -269,13 +269,13 @@ if (! function_exists('get_storage')) {
|
||||
}
|
||||
}
|
||||
|
||||
if (! function_exists('is_storage_driver')) {
|
||||
if (! function_exists('isStorageDriver')) {
|
||||
/**
|
||||
* Check if is running AWS s3 as storage
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
function is_storage_driver($driver)
|
||||
function isStorageDriver($driver)
|
||||
{
|
||||
if (is_array($driver)) {
|
||||
return in_array(config('filesystems.default'), $driver);
|
||||
@@ -722,7 +722,7 @@ if (! function_exists('get_file_type_from_mimetype')) {
|
||||
}
|
||||
}
|
||||
|
||||
if (! function_exists('get_pretty_name')) {
|
||||
if (! function_exists('getPrettyName')) {
|
||||
/**
|
||||
* Format pretty name file
|
||||
*
|
||||
@@ -731,11 +731,11 @@ if (! function_exists('get_pretty_name')) {
|
||||
* @param $mimetype
|
||||
* @return string
|
||||
*/
|
||||
function get_pretty_name($basename, $name, $mimetype)
|
||||
function getPrettyName($basename, $name, $mimetype): string
|
||||
{
|
||||
$file_extension = substr(strrchr($basename, '.'), 1);
|
||||
|
||||
if (strpos($name, $file_extension) !== false) {
|
||||
if (str_contains($name, $file_extension)) {
|
||||
return $name;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Domain\Admin;
|
||||
|
||||
use Tests\TestCase;
|
||||
@@ -24,17 +25,18 @@ class DashboardTest extends TestCase
|
||||
->getJson('/api/admin/dashboard')
|
||||
->assertStatus(200)
|
||||
->assertJsonFragment([
|
||||
'app' => [
|
||||
'earnings' => '$0.00',
|
||||
'isRunningCron' => false,
|
||||
'license' => 'extended',
|
||||
'version' => config('vuefilemanager.version'),
|
||||
'app' => [
|
||||
'shouldUpgradeTranslations' => true,
|
||||
'earnings' => '$0.00',
|
||||
'isRunningCron' => false,
|
||||
'license' => 'extended',
|
||||
'version' => config('vuefilemanager.version'),
|
||||
],
|
||||
'users' => [
|
||||
'total' => 1,
|
||||
'usersPremiumTotal' => 0,
|
||||
],
|
||||
'used' => '2.00MB',
|
||||
'used' => '2.00MB',
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -53,8 +55,7 @@ class DashboardTest extends TestCase
|
||||
->create(['role' => 'admin']);
|
||||
|
||||
$users->each(
|
||||
fn ($user) =>
|
||||
$this
|
||||
fn($user) => $this
|
||||
->actingAs($admin)
|
||||
->getJson('/api/admin/dashboard/newbies')
|
||||
->assertStatus(200)
|
||||
|
||||
@@ -61,13 +61,13 @@ class ContentAccessTest extends TestCase
|
||||
->create([
|
||||
'user_id' => $user->id,
|
||||
'basename' => $file->name,
|
||||
'name' => 'fake-file.pdf',
|
||||
'name' => $file->name,
|
||||
]);
|
||||
|
||||
$this
|
||||
->actingAs($user)
|
||||
->get("file/$file->name")
|
||||
->assertOk();
|
||||
->assertDownload($file->name);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -93,7 +93,7 @@ class ContentAccessTest extends TestCase
|
||||
$this
|
||||
->actingAs($user)
|
||||
->get("thumbnail/xs-$thumbnail->name")
|
||||
->assertStatus(200);
|
||||
->assertDownload("xs-$thumbnail->name");
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -49,14 +49,16 @@ class VisitorAccessToItemsTest extends TestCase
|
||||
])];
|
||||
|
||||
$this->withCookies($cookie)
|
||||
->get("/file/$document->name/$share->token")
|
||||
->assertStatus(200);
|
||||
->get("/file/$document->name/shared/$share->token")
|
||||
->assertStatus(200)
|
||||
->assertDownload($document->name);
|
||||
}
|
||||
|
||||
if (! $is_protected) {
|
||||
// Get shared file
|
||||
$this->get("/file/$document->name/$share->token")
|
||||
->assertStatus(200);
|
||||
$this->get("/file/$document->name/shared/$share->token")
|
||||
->assertStatus(200)
|
||||
->assertDownload($document->name);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -236,13 +238,13 @@ class VisitorAccessToItemsTest extends TestCase
|
||||
];
|
||||
|
||||
$this->withCookies($cookie)
|
||||
->get("/thumbnail/xs-$fileName/$share->token")
|
||||
->assertStatus(200);
|
||||
->get("/thumbnail/xs-$fileName/shared/$share->token")
|
||||
->assertDownload("xs-$fileName");
|
||||
}
|
||||
|
||||
if (! $is_protected) {
|
||||
$this->get("/thumbnail/xs-$fileName/$share->token")
|
||||
->assertStatus(200);
|
||||
$this->get("/thumbnail/xs-$fileName/shared/$share->token")
|
||||
->assertDownload("xs-$fileName");
|
||||
}
|
||||
|
||||
$this->assertDatabaseMissing('traffic', [
|
||||
|
||||
@@ -186,7 +186,8 @@ class TrafficTest extends TestCase
|
||||
]);
|
||||
|
||||
$this->get("/file/$document->name/shared/$share->token")
|
||||
->assertStatus(200);
|
||||
->assertStatus(200)
|
||||
->assertDownload($document->name);
|
||||
|
||||
$this->assertDatabaseHas('traffic', [
|
||||
'user_id' => $this->user->id,
|
||||
|
||||
@@ -37,12 +37,12 @@ class UploadRequestAccessTest extends TestCase
|
||||
'parent_id' => $uploadRequest->id,
|
||||
'basename' => $file->name,
|
||||
'user_id' => $user->id,
|
||||
'name' => 'fake-file.pdf',
|
||||
'name' => $file->name,
|
||||
]);
|
||||
|
||||
$this
|
||||
->get("/file/$file->name/upload-request/$uploadRequest->id")
|
||||
->assertOk();
|
||||
->assertDownload($file->name);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -75,7 +75,7 @@ class UploadRequestAccessTest extends TestCase
|
||||
|
||||
$this
|
||||
->get("/thumbnail/xs-$thumbnail->name/upload-request/$uploadRequest->id")
|
||||
->assertOk();
|
||||
->assertDownload("xs-$thumbnail->name");
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user