Limitation API skelet with can upload tests

This commit is contained in:
Čarodej
2022-01-05 11:20:57 +01:00
parent 49c6f25acb
commit c7c11fe5b9
42 changed files with 549 additions and 187 deletions
@@ -202,7 +202,7 @@ class SetupDevEnvironment extends Command
'created_at' => now(),
]);
Share::factory(Share::class)
Share::factory()
->create([
'type' => 'folder',
'item_id' => $shared_folder->id,
@@ -337,7 +337,7 @@ class SetupDevEnvironment extends Command
'created_at' => now()->subMinutes(4),
]);
Share::factory(Share::class)
Share::factory()
->create([
'type' => 'folder',
'item_id' => $documents->id,
@@ -0,0 +1,36 @@
<?php
namespace App\Limitations\Engines;
use App\Users\Models\User;
use App\Limitations\LimitationEngine;
class DefaultLimitationEngine implements LimitationEngine
{
public function canUpload(User $user, int $fileSize = 0): bool
{
// 1. If storage limitations is set to false, then allow upload
if (! get_settings('storage_limitation')) {
return true;
}
// Get used storage percentage
$usedPercentage = get_storage_percentage(
used: $user->usedCapacity + $fileSize,
maxAmount: $user->limitations->max_storage_amount,
);
// 2. Check if storage usage exceed predefined capacity
return ! ($usedPercentage >= 100)
;
}
public function canDownload(User $user): bool
{
return true;
}
public function canCreateFolder(User $user): bool
{
return true;
}
}
@@ -0,0 +1,31 @@
<?php
namespace App\Limitations\Engines;
use App\Users\Models\User;
use App\Limitations\LimitationEngine;
class FixedBillingLimitationEngine implements LimitationEngine
{
public function canUpload(User $user, int $fileSize = 0): bool
{
// Get used capacity
$usedPercentage = get_storage_percentage(
used: $user->usedCapacity + $fileSize,
maxAmount: $user->limitations->max_storage_amount,
);
// Check if storage usage exceed predefined capacity
return ! ($usedPercentage >= 100)
;
}
public function canDownload(User $user): bool
{
return true;
}
public function canCreateFolder(User $user): bool
{
return true;
}
}
@@ -0,0 +1,29 @@
<?php
namespace App\Limitations\Engines;
use App\Users\Models\User;
use App\Limitations\LimitationEngine;
class MeteredBillingLimitationEngine implements LimitationEngine
{
public function canUpload(User $user, int $fileSize = 0): bool
{
// Disable upload when user has more than 3 failed payments
return ! ($user->failedPayments()->count() >= 3)
;
}
public function canDownload(User $user): bool
{
// Disable upload when user has more than 3 failed payments
return ! ($user->failedPayments()->count() >= 3)
;
}
public function canCreateFolder(User $user): bool
{
// Disable upload when user has more than 3 failed payments
return ! ($user->failedPayments()->count() >= 3)
;
}
}
+13
View File
@@ -0,0 +1,13 @@
<?php
namespace App\Limitations;
use App\Users\Models\User;
interface LimitationEngine
{
public function canUpload(User $user, int $fileSize = 0): bool;
public function canDownload(User $user): bool;
public function canCreateFolder(User $user): bool;
}
+30
View File
@@ -0,0 +1,30 @@
<?php
namespace App\Limitations;
use Illuminate\Support\Manager;
use App\Limitations\Engines\DefaultLimitationEngine;
use App\Limitations\Engines\FixedBillingLimitationEngine;
use App\Limitations\Engines\MeteredBillingLimitationEngine;
class LimitationManager extends Manager
{
public function getDefaultDriver(): string
{
return get_limitation_driver();
}
public function createDefaultDriver(): DefaultLimitationEngine
{
return new DefaultLimitationEngine();
}
public function createFixedDriver(): FixedBillingLimitationEngine
{
return new FixedBillingLimitationEngine();
}
public function createMeteredDriver(): MeteredBillingLimitationEngine
{
return new MeteredBillingLimitationEngine();
}
}
@@ -1,29 +0,0 @@
<?php
namespace App\Users\Actions;
use Illuminate\Support\Facades\Storage;
class CheckStorageCapacityAction
{
/**
* Check if user has enough space to upload file
*/
public function __invoke(
string $user_id,
int $file_size,
string $temp_filename,
): void {
// Get user storage percentage and get storage_limitation setting
$user_storage_used = user_storage_percentage($user_id, $file_size);
// Check if user can upload
if (get_settings('storage_limitation') && $user_storage_used >= 100) {
// Delete file
Storage::disk('local')
->delete("chunks/$temp_filename");
// Abort uploading
abort(423, 'You exceed your storage limit!');
}
}
}
@@ -1,5 +1,4 @@
<?php
namespace App\Users\Actions;
use ByteUnits\Metric;
@@ -14,7 +13,7 @@ class FormatUsageEstimatesAction
$usage = match ($estimate['feature']) {
'bandwidth', 'storage' => Metric::megabytes($estimate['usage'])->format(),
'flatFee' => intval($estimate['usage']) . ' ' . __('Pcs.'),
'member' => intval($estimate['usage']) . ' ' . __('Mem.'),
'member' => intval($estimate['usage']) . ' ' . __('Mem.'),
};
// Normalize units
@@ -29,7 +28,7 @@ class FormatUsageEstimatesAction
'amount' => $amount,
'cost' => format_currency($amount, $currency),
'usage' => $usage,
]
],
];
});
}
+14 -2
View File
@@ -12,6 +12,7 @@ use Illuminate\Support\Facades\DB;
use Database\Factories\UserFactory;
use Domain\Settings\Models\Setting;
use Kyslik\ColumnSortable\Sortable;
use App\Limitations\LimitationManager;
use Illuminate\Support\Facades\Storage;
use Illuminate\Notifications\Notifiable;
use App\Users\Notifications\ResetPassword;
@@ -87,6 +88,17 @@ class User extends Authenticatable implements MustVerifyEmail
return UserFactory::new();
}
public function __call($method, $parameters)
{
if (str_starts_with($method, 'can')) {
return resolve(LimitationManager::class)
->driver()
->$method($this, ...$parameters);
}
return parent::__call($method, $parameters);
}
/**
* Get user used storage details
*/
@@ -102,8 +114,8 @@ class User extends Authenticatable implements MustVerifyEmail
}
return [
'used' => (float) get_storage_fill_percentage($this->usedCapacity, $this->limitations->max_storage_amount),
'used_formatted' => get_storage_fill_percentage($this->usedCapacity, $this->limitations->max_storage_amount) . '%',
'used' => (float) get_storage_percentage($this->usedCapacity, $this->limitations->max_storage_amount),
'used_formatted' => get_storage_percentage($this->usedCapacity, $this->limitations->max_storage_amount) . '%',
'capacity' => $this->limitations->max_storage_amount,
'capacity_formatted' => format_gigabytes($this->limitations->max_storage_amount),
];
+1 -1
View File
@@ -60,7 +60,7 @@ class UserLimitation extends Model
return [
'use' => Metric::bytes($userCapacity)->format(),
'total' => format_gigabytes($this->max_storage_amount),
'percentage' => get_storage_fill_percentage($userCapacity, $this->max_storage_amount),
'percentage' => get_storage_percentage($userCapacity, $this->max_storage_amount),
];
}
+4 -4
View File
@@ -4,12 +4,12 @@ namespace App\Users\Resources;
use Domain\Folders\Resources\FolderCollection;
use Illuminate\Http\Resources\Json\JsonResource;
use App\Users\Actions\FormatUsageEstimatesAction;
use VueFileManager\Subscription\Domain\CreditCards\Resources\CreditCardCollection;
use VueFileManager\Subscription\Domain\Credits\Resources\BalanceResource;
use VueFileManager\Subscription\Domain\CreditCards\Resources\CreditCardCollection;
use VueFileManager\Subscription\Domain\BillingAlerts\Resources\BillingAlertResource;
use VueFileManager\Subscription\Domain\FailedPayments\Resources\FailedPaymentsCollection;
use VueFileManager\Subscription\Domain\Subscriptions\Resources\SubscriptionResource;
use VueFileManager\Subscription\Domain\Usage\Actions\SumUsageForCurrentPeriodAction;
use VueFileManager\Subscription\Domain\FailedPayments\Resources\FailedPaymentsCollection;
class UserResource extends JsonResource
{
@@ -41,8 +41,8 @@ class UserResource extends JsonResource
'updated_at' => format_date($this->updated_at, '%d. %B. %Y'),
],
'relationships' => [
'settings' => new SettingsResource($this->settings),
'favourites' => new FolderCollection($this->favouriteFolders),
'settings' => new SettingsResource($this->settings),
'favourites' => new FolderCollection($this->favouriteFolders),
'creditCards' => new CreditCardCollection($this->creditCards),
$this->mergeWhen($this->hasSubscription(), fn () => [
'subscription' => new SubscriptionResource($this->subscription),
@@ -31,7 +31,7 @@ class UserStorageResource extends JsonResource
'attributes' => [
'used' => Metric::bytes($this->usedCapacity)->format(),
'capacity' => format_gigabytes($totalCapacity),
'percentage' => (float) get_storage_fill_percentage($this->usedCapacity, $totalCapacity),
'percentage' => (float) get_storage_percentage($this->usedCapacity, $totalCapacity),
],
'meta' => [
'traffic' => [
@@ -44,23 +44,23 @@ class UserStorageResource extends JsonResource
],
'images' => [
'used' => Metric::bytes($images)->format(),
'percentage' => (float) get_storage_fill_percentage($images, $totalCapacity),
'percentage' => (float) get_storage_percentage($images, $totalCapacity),
],
'audios' => [
'used' => Metric::bytes($audios)->format(),
'percentage' => (float) get_storage_fill_percentage($audios, $totalCapacity),
'percentage' => (float) get_storage_percentage($audios, $totalCapacity),
],
'videos' => [
'used' => Metric::bytes($videos)->format(),
'percentage' => (float) get_storage_fill_percentage($videos, $totalCapacity),
'percentage' => (float) get_storage_percentage($videos, $totalCapacity),
],
'documents' => [
'used' => Metric::bytes($documents)->format(),
'percentage' => (float) get_storage_fill_percentage($documents, $totalCapacity),
'percentage' => (float) get_storage_percentage($documents, $totalCapacity),
],
'others' => [
'used' => Metric::bytes($others)->format(),
'percentage' => (float) get_storage_fill_percentage($others, $totalCapacity),
'percentage' => (float) get_storage_percentage($others, $totalCapacity),
],
],
],
@@ -7,7 +7,8 @@ class ProcessImageThumbnailAction
{
public function __construct(
public GenerateImageThumbnailAction $generateImageThumbnail,
) {}
) {
}
private array $availableFormats = [
'image/gif',
+13 -10
View File
@@ -9,16 +9,15 @@ use Illuminate\Support\Facades\Storage;
use Domain\Files\Requests\UploadRequest;
use Domain\Files\Models\File as UserFile;
use Domain\Traffic\Actions\RecordUploadAction;
use App\Users\Actions\CheckStorageCapacityAction;
class UploadFileAction
{
public function __construct(
public RecordUploadAction $recordUpload,
public CheckStorageCapacityAction $checkStorageCapacity,
public ProcessImageThumbnailAction $createImageThumbnail,
public MoveFileToExternalStorageAction $moveFileToExternalStorage,
) {}
) {
}
/**
* Upload new file
@@ -58,7 +57,7 @@ class UploadFileAction
$disk_local = Storage::disk('local');
// Get user data
$user_id = $shared->user_id ?? Auth::id();
$user = $shared->user ?? Auth::user();
// File Info
$fileSize = $disk_local->size("chunks/$chunkName");
@@ -66,21 +65,25 @@ class UploadFileAction
$file_mimetype = $disk_local->mimeType("chunks/$chunkName");
// Check if user has enough space to upload file
($this->checkStorageCapacity)($user_id, $fileSize, $chunkName);
if (! $user->canUpload($fileSize)) {
Storage::disk('local')->delete("chunks/$chunkName");
abort(423, 'You exceed your storage limit!');
}
// Move finished file from chunk to file-manager directory
$disk_local->move("chunks/$chunkName", "files/$user_id/$fileName");
$disk_local->move("chunks/$chunkName", "files/$user->id/$fileName");
// Create multiple image thumbnails
($this->createImageThumbnail)($fileName, $file, $user_id);
($this->createImageThumbnail)($fileName, $file, $user->id);
// Move files to external storage
if (! is_storage_driver('local')) {
($this->moveFileToExternalStorage)($fileName, $user_id);
($this->moveFileToExternalStorage)($fileName, $user->id);
}
// Store user upload size
($this->recordUpload)($fileSize, $user_id);
($this->recordUpload)($fileSize, $user->id);
// Return new file
return UserFile::create([
@@ -92,7 +95,7 @@ class UploadFileAction
'basename' => $fileName,
'author' => $shared ? 'visitor' : 'user',
'filesize' => $fileSize,
'user_id' => $user_id,
'user_id' => $user->id,
]);
}
}
+1 -1
View File
@@ -35,7 +35,7 @@ class FileResource extends JsonResource
'parent_id' => $this->parent_id,
'updated_at' => $this->updated_at,
'created_at' => Carbon::parse($this->created_at)->diffForHumans(),
'deleted_at' => Carbon::parse($this->deleted_at)->diffForHumans(),
'deleted_at' => $this->deleted_at ? Carbon::parse($this->deleted_at)->diffForHumans() : null,
/*'updated_at' => format_date(
set_time_by_user_timezone($this->updated_at), __t('time')
),
@@ -26,6 +26,7 @@ class FolderResource extends JsonResource
'trashed_items' => $this->trashed_items,
'updated_at' => $this->updated_at,
'created_at' => Carbon::parse($this->created_at)->diffForHumans(),
'deleted_at' => $this->deleted_at ? Carbon::parse($this->deleted_at)->diffForHumans() : null,
/*'updated_at' => format_date(
set_time_by_user_timezone($this->updated_at), __t('time')
),
@@ -11,13 +11,15 @@ class SeedDefaultLanguageAction
*/
public function __invoke(): void
{
Language::create([
Language::updateOrCreate([
'name' => 'English',
], [
'locale' => 'en',
]);
Setting::create([
Setting::updateOrCreate([
'name' => 'language',
], [
'value' => 'en',
]);
}
@@ -1,8 +1,8 @@
<?php
namespace Support\Scheduler\Actions;
use App\Users\Models\User;
use DB;
use App\Users\Models\User;
use VueFileManager\Subscription\Domain\Subscriptions\Models\Subscription;
class ReportUsageAction
+21 -11
View File
@@ -33,6 +33,20 @@ if (! function_exists('obfuscate_email')) {
}
}
if (! function_exists('get_limitation_driver')) {
/**
* Get driver for limitation API
*/
function get_limitation_driver(): string
{
return match (get_settings('subscription_type')) {
'fixed' => 'fixed',
'metered' => 'metered',
default => 'default',
};
}
}
if (! function_exists('get_email_provider')) {
/**
* Get single or multiple values from settings table
@@ -456,18 +470,14 @@ if (! function_exists('format_bytes')) {
}
}
if (! function_exists('get_storage_fill_percentage')) {
if (! function_exists('get_storage_percentage')) {
/**
* Get storage usage in percent
*
* @param $used
* @param $from
* @return string
*/
function get_storage_fill_percentage($used, $from)
function get_storage_percentage(int $used, int $maxAmount): float
{
// Format gigabytes to bytes
$total = intval(Metric::gigabytes($from)->numberOfBytes());
$total = intval(Metric::gigabytes($maxAmount)->numberOfBytes());
// Count progress
if ($total == 0) {
@@ -485,17 +495,17 @@ if (! function_exists('user_storage_percentage')) {
/**
* Get user capacity fill by percentage
*/
function user_storage_percentage($id, ?int $additionals = null)
function user_storage_percentage($id, ?int $additional = null)
{
$user = User::findOrFail($id);
$used = $user->usedCapacity;
if ($additionals) {
$used = $user->usedCapacity + $additionals;
if ($additional) {
$used = $user->usedCapacity + $additional;
}
return get_storage_fill_percentage($used, $user->limitations->max_storage_amount);
return get_storage_percentage($used, $user->limitations->max_storage_amount);
}
}