deleted pro files

This commit is contained in:
Čarodej
2022-04-26 10:53:24 +02:00
parent 92561d137d
commit 188d5613b7
119 changed files with 607 additions and 10013 deletions

View File

@@ -11,7 +11,6 @@
"php": "^8.0.2",
"ext-json": "*",
"ext-pdo": "*",
"beyondcode/laravel-websockets": "^1.13",
"brianium/paratest": "^6.4.1",
"cocur/slugify": "^4.1",
"doctrine/dbal": "^2.13.7",
@@ -24,14 +23,11 @@
"laravel/fortify": "^1.12.0",
"laravel/framework": "^9.2",
"laravel/sanctum": "^2.14.2",
"laravel/socialite": "^5.5.1",
"laravel/tinker": "^2.7",
"laravel/ui": "^3.4.2",
"league/flysystem-aws-s3-v3": "^3.0.9",
"league/flysystem-ftp": "^3.0",
"makingcg/subscription": "^1.0.5",
"matthewbdaly/laravel-azure-storage": "^2.0",
"pusher/pusher-php-server": "^7.0",
"spatie/data-transfer-object": "^3.7.3",
"spatie/laravel-backup": "^8.0.8",
"spatie/laravel-query-builder": "^5.0.0",
@@ -55,12 +51,6 @@
"fakerphp/faker": "^1.19.0",
"ext-json": "*"
},
"repositories": [
{
"type": "vcs",
"url": "https://github.com/VueFileManager/subscription.git"
}
],
"config": {
"optimize-autoloader": true,
"preferred-install": "dist",

2840
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -161,7 +161,6 @@ return [
Illuminate\View\ViewServiceProvider::class,
TeamTNT\Scout\TNTSearchScoutServiceProvider::class,
Laravel\Socialite\SocialiteServiceProvider::class,
Intervention\Image\ImageServiceProvider::class,
App\Providers\FortifyServiceProvider::class,

View File

@@ -1,19 +0,0 @@
<?php
use Domain\Teams\Controllers\InvitationsController;
use Domain\Teams\Controllers\TeamFoldersController;
use Domain\Teams\Controllers\NavigationTreeController;
use Domain\Teams\Controllers\LeaveTeamFolderController;
use Domain\Teams\Controllers\BrowseSharedWithMeController;
use Domain\Teams\Controllers\ConvertFolderIntoTeamFolderController;
Route::apiResource('/invitations', InvitationsController::class);
Route::group(['middleware' => ['auth:sanctum']], function () {
Route::get('/shared-with-me/{id}', BrowseSharedWithMeController::class);
Route::apiResource('/folders', TeamFoldersController::class);
Route::post('/folders/{folder}/convert', ConvertFolderIntoTeamFolderController::class);
Route::delete('/folders/{folder}/leave', LeaveTeamFolderController::class);
Route::get('/folders/{folder}/tree', NavigationTreeController::class);
});

View File

@@ -1,38 +0,0 @@
<?php
use Domain\UploadRequest\Controllers\CreateFolderController;
use Domain\UploadRequest\Controllers\GetUploadRequestController;
use Domain\UploadRequest\Controllers\DeleteFileOrFolderController;
use Domain\UploadRequest\Controllers\RenameFileOrFolderController;
use Domain\UploadRequest\Controllers\BrowseUploadRequestController;
use Domain\UploadRequest\Controllers\CreateUploadRequestController;
use Domain\UploadRequest\Controllers\MoveItemInUploadRequestController;
use Domain\UploadRequest\Controllers\SetUploadRequestAsFilledController;
use Domain\UploadRequest\Controllers\UploadFilesForUploadRequestController;
use Domain\UploadRequest\Controllers\GetFolderTreeForUploadRequestController;
use Domain\RemoteUpload\Controllers\UploadFilesRemotelyForUploadRequestController;
Route::get('/{uploadRequest}', GetUploadRequestController::class);
// Available only for active upload requests
Route::group(['middleware' => 'upload-request'], function () {
// Detail
Route::delete('/{uploadRequest}', SetUploadRequestAsFilledController::class);
// Edit
Route::post('/{uploadRequest}/upload/remote', UploadFilesRemotelyForUploadRequestController::class);
Route::post('/{uploadRequest}/upload', UploadFilesForUploadRequestController::class);
Route::patch('/{uploadRequest}/rename/{id}', RenameFileOrFolderController::class);
Route::post('/{uploadRequest}/create-folder', CreateFolderController::class);
Route::post('/{uploadRequest}/remove', DeleteFileOrFolderController::class);
// Browsing
Route::get('/{uploadRequest}/navigation', GetFolderTreeForUploadRequestController::class);
Route::get('/{uploadRequest}/browse/{folder?}', BrowseUploadRequestController::class);
Route::post('/{uploadRequest}/move', MoveItemInUploadRequestController::class);
});
// User functionality
Route::group(['middleware' => ['auth:sanctum']], function () {
Route::post('/', CreateUploadRequestController::class);
});

View File

@@ -1,569 +0,0 @@
<?php
namespace App\Console\Commands;
use DB;
use App\Users\Models\User;
use Illuminate\Support\Str;
use Illuminate\Console\Command;
use Domain\Settings\Models\Setting;
use VueFileManager\Subscription\Domain\Plans\Models\Plan;
use VueFileManager\Subscription\Domain\CreditCards\Models\CreditCard;
use VueFileManager\Subscription\Domain\Plans\DTO\CreateFixedPlanData;
use VueFileManager\Subscription\Domain\Plans\DTO\CreateMeteredPlanData;
use VueFileManager\Subscription\Domain\Subscriptions\Models\Subscription;
use VueFileManager\Subscription\Domain\Plans\Actions\StoreFixedPlanAction;
use VueFileManager\Subscription\Domain\Plans\Actions\StoreMeteredPlanAction;
class GenerateDemoSubscriptionContentCommand extends Command
{
public $signature = 'subscription:demo {type=fixed}';
public $description = 'Generate demo content for subscription module';
public function __construct(
private StoreFixedPlanAction $storeFixedPlan,
private StoreMeteredPlanAction $storeMeteredPlan,
) {
parent::__construct();
}
public function handle()
{
// Truncate all subscription relate tables
collect([
'balances', 'billing_alerts', 'credit_cards', 'customers', 'failed_payments', 'metered_tiers', 'plan_drivers', 'plan_fixed_features', 'plan_metered_features', 'plans', 'subscription_drivers', 'subscriptions', 'usages', 'transactions',
])->each(fn ($name) => DB::table($name)->truncate());
// Create plans and subscriptions for metered billing
if ($this->argument('type') === 'metered') {
$this->info('Setting up new metered plans demo data...');
$this->generateMeteredPlans();
$this->info('Setting up new pre-paid subscriptions data...');
$this->generateMeteredSubscription();
}
// Create plans and subscriptions for fixed billing
if ($this->argument('type') === 'fixed') {
// TODO: check for credentials
$this->info('Setting up new fixed plans demo data...');
$this->generateFixedPlans();
$this->info('Setting up new fixed subscriptions data...');
$this->generateFixedSubscription();
}
// Set subscription type for the app
Setting::updateOrCreate([
'name' => 'subscription_type',
], [
'value' => $this->argument('type'),
]);
}
public function generateMeteredSubscription(): void
{
$plan = Plan::where('name', 'Pay as You Go')
->first();
User::all()
->each(function ($user) use ($plan) {
$isHowdy = $user->email === 'howdy@hi5ve.digital';
$this->info("Storing {$plan->name} for {$user->email}...");
// 1. Create subscription
$subscription = Subscription::create([
'user_id' => $user->id,
'type' => 'pre-paid',
'plan_id' => $plan->id,
'name' => $plan->name,
'status' => 'active',
'renews_at' => now()->addDays(16),
'created_at' => now()->subDays(14),
'updated_at' => now()->subDays(14),
]);
// 2. Log fake storage and bandwidth
foreach (range(1, 31) as $item) {
$this->info('Logging fake bandwidth usage...');
$bandwidthFeature = $plan
->meteredFeatures()
->where('key', 'bandwidth')
->first();
$subscription->usages()->create([
'metered_feature_id' => $bandwidthFeature->id,
'quantity' => random_int(111, 999) / 1000,
'created_at' => now()->subDays($item),
]);
$this->info('Logging fake storage usage...');
$storageFeature = $plan
->meteredFeatures()
->where('key', 'storage')
->first();
$subscription->usages()->create([
'metered_feature_id' => $storageFeature->id,
'quantity' => random_int(1111, 3999) / 1000,
'created_at' => now()->subDays($item),
]);
}
// 3. Store flat fee
$flatFeeFeature = $plan
->meteredFeatures()
->where('key', 'flatFee')
->first();
$subscription->usages()->create([
'metered_feature_id' => $flatFeeFeature->id,
'quantity' => 1,
'created_at' => now()->subDays(2),
]);
// 4. Store member count
$memberFeature = $plan
->meteredFeatures()
->where('key', 'member')
->first();
$subscription->usages()->create([
'metered_feature_id' => $memberFeature->id,
'quantity' => 7,
'created_at' => now()->subDays(2),
]);
// 5. Store fake transactions
$this->info("Storing transactions for {$user->email}...");
collect([
[
'type' => 'withdrawal',
'created_at' => now()->subDays(2),
'amount' => $isHowdy ? 12.59 : random_int(1, 20),
'note' => now()->subDays(32)->format('d. M') . ' - ' . now()->subDays(2)->format('d. M'),
'driver' => 'system',
],
[
'type' => 'credit',
'created_at' => now()->subDays(26 * 1),
'note' => __('Bonus'),
'amount' => $isHowdy ? 12.00 : random_int(1, 20),
'driver' => 'system',
],
[
'type' => 'withdrawal',
'created_at' => now()->subDays(26 * 1),
'note' => now()->subDays(30 + 26 * 1)->format('d. M') . ' - ' . now()->subDays(26 * 1)->format('d. M'),
'amount' => $isHowdy ? 2.38 : random_int(1, 20),
'driver' => 'system',
],
[
'type' => 'withdrawal',
'created_at' => now()->subDays(26 * 2),
'note' => now()->subDays(30 + 26 * 2)->format('d. M') . ' - ' . now()->subDays(26 * 2)->format('d. M'),
'amount' => $isHowdy ? 5.12 : random_int(1, 20),
'driver' => 'system',
],
[
'type' => 'withdrawal',
'created_at' => now()->subDays(26 * 3),
'note' => now()->subDays(30 + 26 * 3)->format('d. M') . ' - ' . now()->subDays(26 * 3)->format('d. M'),
'amount' => $isHowdy ? 3.89 : random_int(1, 20),
'driver' => 'system',
],
[
'type' => 'withdrawal',
'created_at' => now()->subDays(26 * 4),
'note' => now()->subDays(30 + 26 * 4)->format('d. M') . ' - ' . now()->subDays(26 * 4)->format('d. M'),
'amount' => $isHowdy ? 7.42 : random_int(1, 20),
'driver' => 'system',
],
[
'type' => 'charge',
'created_at' => now()->subDays(26 * 5),
'note' => 'Account Fund',
'amount' => $isHowdy ? 50.00 : random_int(1, 20),
'driver' => 'paypal',
],
])->each(
function ($transaction) use ($user, $plan) {
$bandwidthUsage = random_int(1000, 12000) / 1000;
$storageUsage = random_int(300, 4900) / 1000;
$memberUsage = random_int(3, 20);
$user->transactions()->create([
'type' => $transaction['type'],
'status' => 'completed',
'note' => $transaction['note'],
'currency' => $plan->currency,
'driver' => $transaction['driver'],
'amount' => $transaction['amount'],
'created_at' => $transaction['created_at'],
'reference' => Str::random(12),
'metadata' => $transaction['type'] === 'withdrawal'
? [
[
'feature' => 'bandwidth',
'amount' => 0.29 * $bandwidthUsage,
'usage' => $bandwidthUsage,
],
[
'feature' => 'storage',
'amount' => 0.19 * $storageUsage,
'usage' => $storageUsage,
],
[
'feature' => 'flatFee',
'amount' => 2.49,
'usage' => 1,
],
[
'feature' => 'member',
'amount' => 0.10 * $memberUsage,
'usage' => $memberUsage,
],
]
: null,
]);
}
);
// Make fake credit card
$creditCard = CreditCard::factory()
->make();
// 6. Store credit card
if (! $isHowdy) {
$user->creditCards()->create([
'brand' => $creditCard->brand,
'last4' => $creditCard->last4,
'service' => $creditCard->service,
'reference' => $creditCard->reference,
'expiration' => $creditCard->expiration,
]);
}
// 7. Add default user balance
$user->balance()->create([
'currency' => 'USD',
'amount' => $isHowdy ? 30.60 : random_int(20, 60),
]);
// 8. Create billing alert
$user->billingAlert()->create([
'amount' => $isHowdy ? 25 : random_int(30, 80),
]);
});
}
public function generateFixedSubscription(): void
{
$howdy = User::where('email', 'howdy@hi5ve.digital')
->first();
$alice = User::where('email', 'alice@hi5ve.digital')
->first();
$johan = User::where('email', 'johan@hi5ve.digital')
->first();
$professionalPackPlan = Plan::where('name', 'Professional Pack')
->where('interval', 'month')
->first();
$businessPackPlan = Plan::where('name', 'Business Pack')
->where('interval', 'month')
->first();
$this->info("Storing {$professionalPackPlan->name} for {$howdy->email}...");
$howdySubscription = $howdy->subscription()->create([
'plan_id' => $professionalPackPlan->id,
'name' => $professionalPackPlan->name,
'status' => 'active',
'created_at' => now()->subDays(14),
'updated_at' => now()->subDays(14),
]);
$this->info("Storing {$businessPackPlan->name} for {$alice->email}...");
$aliceSubscription = $alice->subscription()->create([
'plan_id' => $businessPackPlan->id,
'name' => $businessPackPlan->name,
'status' => 'active',
'created_at' => now()->subDays(9),
'updated_at' => now()->subDays(9),
]);
$this->info("Storing {$professionalPackPlan->name} for {$johan->email}...");
$johanSubscription = $johan->subscription()->create([
'plan_id' => $professionalPackPlan->id,
'name' => $professionalPackPlan->name,
'status' => 'cancelled',
'ends_at' => now()->addDays(18),
'created_at' => now()->subDays(8),
'updated_at' => now()->subDays(8),
]);
$this->info("Storing transactions for {$howdy->email}...");
collect([
['created_at' => now()->subDays(2)],
['created_at' => now()->subDays(26)],
['created_at' => now()->subDays(26 * 2)],
['created_at' => now()->subDays(26 * 3)],
['created_at' => now()->subDays(26 * 4)],
['created_at' => now()->subDays(26 * 5)],
])->each(
fn ($transaction) => $howdy->transactions()->create([
'status' => 'completed',
'note' => $professionalPackPlan->name,
'currency' => $professionalPackPlan->currency,
'amount' => $professionalPackPlan->amount,
'driver' => 'paypal',
'created_at' => $transaction['created_at'],
'reference' => Str::random(12),
])
);
$this->info("Storing transactions for {$johan->email}...");
collect([
['created_at' => now()->subDay()],
['created_at' => now()->subDays(29)],
['created_at' => now()->subDays(29 * 2)],
['created_at' => now()->subDays(29 * 3)],
])->each(
fn ($transaction) => $johan->transactions()->create([
'status' => 'completed',
'note' => $professionalPackPlan->name,
'currency' => $professionalPackPlan->currency,
'amount' => $professionalPackPlan->amount,
'driver' => 'stripe',
'created_at' => $transaction['created_at'],
'reference' => Str::random(12),
])
);
$this->info("Storing transactions for {$alice->email}...");
collect([
['created_at' => now()],
['created_at' => now()->subDays(28)],
['created_at' => now()->subDays(28 * 2)],
['created_at' => now()->subDays(28 * 3)],
['created_at' => now()->subDays(28 * 4)],
])->each(
fn ($transaction) => $alice->transactions()->create([
'status' => 'completed',
'note' => $businessPackPlan->name,
'currency' => $businessPackPlan->currency,
'amount' => $businessPackPlan->amount,
'driver' => 'paystack',
'created_at' => $transaction['created_at'],
'reference' => Str::random(12),
])
);
$howdySubscription->driver()->create([
'driver' => 'stripe',
'driver_subscription_id' => Str::random(),
]);
// Make fake credit card
$creditCard = CreditCard::factory()
->make();
// 6. Store credit card
$howdy->creditCards()->create([
'brand' => $creditCard->brand,
'last4' => $creditCard->last4,
'service' => $creditCard->service,
'reference' => $creditCard->reference,
'expiration' => $creditCard->expiration,
]);
$aliceSubscription->driver()->create([
'driver' => 'paystack',
'driver_subscription_id' => Str::random(),
]);
$johanSubscription->driver()->create([
'driver' => 'stripe',
'driver_subscription_id' => Str::random(),
]);
}
public function generateFixedPlans()
{
// Define plans
$plans = [
[
'type' => 'fixed',
'name' => 'Professional Pack',
'description' => 'Best for all professionals',
'currency' => 'USD',
'features' => [
'max_storage_amount' => 200,
'max_team_members' => 20,
],
'intervals' => [
[
'interval' => 'month',
'amount' => 9.99,
],
[
'interval' => 'year',
'amount' => 99.49,
],
],
],
[
'type' => 'fixed',
'name' => 'Business Pack',
'description' => 'Best for business needs',
'currency' => 'USD',
'features' => [
'max_storage_amount' => 500,
'max_team_members' => 50,
],
'intervals' => [
[
'interval' => 'month',
'amount' => 29.99,
],
[
'interval' => 'year',
'amount' => 189.99,
],
],
],
[
'type' => 'fixed',
'name' => 'Elite Pack',
'description' => 'Best for all your needs',
'currency' => 'USD',
'features' => [
'max_storage_amount' => 2000,
'max_team_members' => -1,
],
'intervals' => [
[
'interval' => 'month',
'amount' => 59.99,
],
[
'interval' => 'year',
'amount' => 349.99,
],
],
],
];
// Create plans
foreach ($plans as $plan) {
foreach ($plan['intervals'] as $interval) {
$data = CreateFixedPlanData::fromArray([
'type' => $plan['type'],
'name' => $plan['name'],
'description' => $plan['description'],
'features' => $plan['features'],
'currency' => $plan['currency'],
'amount' => $interval['amount'],
'interval' => $interval['interval'],
]);
$this->info("Creating plan with name: {$plan['name']} and interval: {$interval['interval']}");
// Store plans to the database and gateway
($this->storeFixedPlan)($data);
}
}
}
public function generateMeteredPlans()
{
// Define plans
$plans = [
[
'type' => 'metered',
'name' => 'Pay as You Go',
'description' => 'Best for all professionals',
'currency' => 'USD',
'meters' => [
[
'key' => 'bandwidth',
'aggregate_strategy' => 'sum_of_usage',
'tiers' => [
[
'first_unit' => 1,
'last_unit' => null,
'per_unit' => 0.29,
'flat_fee' => null,
],
],
],
[
'key' => 'storage',
'aggregate_strategy' => 'maximum_usage',
'tiers' => [
[
'first_unit' => 1,
'last_unit' => null,
'per_unit' => 0.19,
'flat_fee' => null,
],
],
],
[
'key' => 'flatFee',
'aggregate_strategy' => 'maximum_usage',
'tiers' => [
[
'first_unit' => 1,
'last_unit' => null,
'per_unit' => 2.49,
'flat_fee' => null,
],
],
],
[
'key' => 'member',
'aggregate_strategy' => 'maximum_usage',
'tiers' => [
[
'first_unit' => 1,
'last_unit' => null,
'per_unit' => 0.10,
'flat_fee' => null,
],
],
],
],
],
];
// Create plans
foreach ($plans as $plan) {
$data = CreateMeteredPlanData::fromArray([
'type' => $plan['type'],
'name' => $plan['name'],
'meters' => $plan['meters'],
'currency' => $plan['currency'],
'description' => $plan['description'],
]);
$this->info("Creating plan with name: {$plan['name']}");
// Store plans to the database and gateway
($this->storeMeteredPlan)($data);
}
}
}

View File

@@ -1,47 +0,0 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
class SetupWebsocketEnvironment extends Command
{
/**
* The name and signature of the console command.
*/
protected $signature = 'websockets:install';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Set up websocket production environment';
/**
* Execute the console command.
*/
public function handle(): void
{
// Get allowed origins
$origins = $this->ask('Type host of which you want to allow incoming requests. If you want to accept multiple hosts, separate them with comma(,)');
// Store origins to the .env file
setEnvironmentValue([
'PUSHER_APP_ALLOWED_ORIGIN' => $origins,
'APP_ENV' => 'production',
'APP_DEBUG' => 'false',
]);
$this->info('Your host/s was stored successfully.');
// Generate new app key
$this->call('key:generate', [
'--force' => true,
]);
// Clear cache
$this->call('config:clear');
$this->info('Everything is done, congratulations! 🥳🥳🥳');
}
}

View File

@@ -1,87 +0,0 @@
<?php
namespace App\Socialite\Controllers;
use App\Users\Models\User;
use App\Users\DTO\CreateUserData;
use App\Http\Controllers\Controller;
use Laravel\Socialite\Facades\Socialite;
use App\Users\Actions\CreateNewUserAction;
use Illuminate\Contracts\Auth\StatefulGuard;
use VueFileManager\Subscription\Domain\Plans\Models\Plan;
use VueFileManager\Subscription\Domain\Plans\Exceptions\MeteredBillingPlanDoesntExist;
class SocialiteCallbackController extends Controller
{
public function __construct(
protected StatefulGuard $guard,
public CreateNewUserAction $createNewUser,
) {
}
/**
* @throws MeteredBillingPlanDoesntExist
*/
public function __invoke($provider)
{
$isAllowedRegistration = intval(get_settings('registration'));
// Get socialite user
if (app()->runningUnitTests()) {
$socialite = Socialite::driver($provider)->user();
} else {
$socialite = Socialite::driver($provider)->stateless()->user();
}
// Get user by email
$user = User::where('email', $socialite->email);
// Login user when exists
if ($user->exists()) {
$this->guard->login(
$user->first()
);
return redirect()->to('/platform/files');
}
// Check for metered billing plan
if (get_settings('subscription_type') === 'metered') {
// Get metered plan
$plan = Plan::where('status', 'active')
->where('type', 'metered');
// TODO: redirect to the error page
if ($plan->doesntExist()) {
return response([
'type' => 'error',
'message' => 'User registrations are temporarily disabled',
], 409);
}
}
// Check if account registration is enabled
if (! $isAllowedRegistration) {
return response([
'type' => 'error',
'message' => 'User registration is not allowed',
], 401);
}
// Create data user data object
$data = CreateUserData::fromArray([
'role' => 'user',
'name' => $socialite->getName(),
'email' => $socialite->getEmail(),
'avatar' => store_socialite_avatar($socialite->getAvatar()),
'oauth_provider' => $provider,
]);
// Create User
$newUser = ($this->createNewUser)($data);
// Login user
$this->guard->login($newUser);
return redirect()->to('/platform/files');
}
}

View File

@@ -1,17 +0,0 @@
<?php
namespace App\Socialite\Controllers;
use App\Http\Controllers\Controller;
use Laravel\Socialite\Facades\Socialite;
class SocialiteRedirectController extends Controller
{
public function __invoke($provider)
{
$url = Socialite::driver($provider)->stateless()->redirect()->getTargetUrl();
return response()->json([
'url' => $url,
]);
}
}

View File

@@ -1,62 +0,0 @@
<?php
namespace App\Users\Actions;
use App\Users\Models\User;
use VueFileManager\Subscription\Domain\Plans\Models\Plan;
use App\Users\Notifications\RegistrationBonusAddedNotification;
class AutoSubscribeForMeteredBillingAction
{
public function __invoke(User $user)
{
// Get metered billing plan
$plan = Plan::where('status', 'active')
->where('type', 'metered')
->first();
// Get settings
$settings = get_settings([
'allowed_registration_bonus',
'registration_bonus_amount',
]);
// Create user balance
if (intval($settings['allowed_registration_bonus'])) {
// Create balance with bonus amount
$user->balance()->create([
'amount' => $settings['registration_bonus_amount'],
'currency' => $plan->currency,
]);
// Store transaction bonus
$user->transactions()->create([
'status' => 'completed',
'type' => 'credit',
'driver' => 'system',
'note' => __t('registration_bonus'),
'currency' => $plan->currency,
'amount' => $settings['registration_bonus_amount'],
]);
// Send user bonus notification
$bonus = format_currency($settings['registration_bonus_amount'], $plan->currency);
$user->notify(new RegistrationBonusAddedNotification($bonus));
} else {
// Create balance with 0 amount
$user->balance()->create([
'amount' => 0,
'currency' => $plan->currency,
]);
}
// Create user subscription
$user->subscription()->create([
'plan_id' => $plan->id,
'name' => $plan->name,
'status' => 'active',
'renews_at' => now()->addDays(config('subscription.metered_billing.settlement_period')),
'type' => 'pre-paid',
]);
}
}

View File

@@ -1,30 +0,0 @@
<?php
namespace App\Users\Actions;
use ByteUnits\Metric;
use Illuminate\Support\Collection;
class FormatUsageEstimatesAction
{
public function __invoke(string $currency, Collection|array $usage)
{
return collect($usage)
->mapWithKeys(function ($estimate) use ($currency) {
// Format usage
$usage = match ($estimate['feature']) {
'bandwidth', 'storage' => Metric::megabytes($estimate['usage'] * 1000)->format(),
'flatFee' => intval($estimate['usage']) . ' ' . __t('pcs.'),
'member' => intval($estimate['usage']) . ' ' . __t('mem.'),
};
return [
$estimate['feature'] => [
'feature' => $estimate['feature'],
'amount' => $estimate['amount'],
'cost' => format_currency($estimate['amount'], $currency),
'usage' => $usage,
],
];
});
}
}

View File

@@ -1,41 +0,0 @@
<?php
namespace App\Users\Notifications;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Notification;
use Illuminate\Contracts\Queue\ShouldQueue;
class RegistrationBonusAddedNotification extends Notification implements ShouldQueue
{
use Queueable;
/**
* Create a new notification instance.
*
* @return void
*/
public function __construct(
public string $bonus
) {
}
/**
* Get the notification's delivery channels.
*/
public function via(mixed $notifiable): array
{
return ['database'];
}
/**
* Get the array representation of the notification.
*/
public function toArray(mixed $notifiable): array
{
return [
'category' => 'gift',
'title' => __t('you_received_bonus', ['bonus' => $this->bonus]),
'description' => __t('you_received_registration_bonus_note', ['bonus' => $this->bonus]),
];
}
}

View File

@@ -1,42 +0,0 @@
<?php
namespace App\Users\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class UserSubscription extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function toArray($request)
{
$active_subscription = $this->subscription('main')
->asStripeSubscription();
// TODO: vybrat z cache
$subscription = resolve('App\Services\StripeService')
->getPlan($this->subscription('main')->stripe_plan);
return [
'data' => [
'id' => $subscription['plan']['id'],
'type' => 'subscription',
'attributes' => [
'incomplete' => $this->subscription('main')->incomplete(),
'active' => $this->subscription('main')->active(),
'canceled' => $this->subscription('main')->cancelled(),
'name' => $subscription['product']['name'],
'capacity' => (int) $subscription['product']['metadata']['capacity'],
'capacity_formatted' => format_gigabytes($subscription['product']['metadata']['capacity']),
'slug' => $subscription['plan']['id'],
'canceled_at' => format_date($active_subscription['canceled_at'], 'd. M. Y'),
'created_at' => format_date($active_subscription['current_period_start'], 'd. M. Y'),
'ends_at' => format_date($active_subscription['current_period_end'], 'd. M. Y'),
],
],
];
}
}

View File

@@ -1,46 +0,0 @@
<?php
namespace App\Users\Restrictions\Engines;
use App\Users\Models\User;
use App\Users\Restrictions\RestrictionsEngine;
use Domain\Teams\Actions\CheckMaxTeamMembersLimitAction;
class FixedBillingRestrictionsEngine implements RestrictionsEngine
{
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;
}
public function canCreateTeamFolder(User $user): bool
{
return true;
}
public function canInviteTeamMembers(User $user, array $newInvites = []): bool
{
return resolve(CheckMaxTeamMembersLimitAction::class)($user, $newInvites);
}
public function canVisitShared(User $user): bool
{
return true;
}
}

View File

@@ -1,43 +0,0 @@
<?php
namespace App\Users\Restrictions\Engines;
use App\Users\Models\User;
use App\Users\Restrictions\RestrictionsEngine;
class MeteredBillingRestrictionsEngine implements RestrictionsEngine
{
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 download when user has more than 3 failed payments
return ! ($user->failedPayments()->count() >= 3);
}
public function canCreateFolder(User $user): bool
{
// Disable create folder when user has more than 3 failed payments
return ! ($user->failedPayments()->count() >= 3);
}
public function canCreateTeamFolder(User $user): bool
{
// Disable create folder when user has more than 3 failed payments
return ! ($user->failedPayments()->count() >= 3);
}
public function canInviteTeamMembers(User $user, array $newInvites = []): bool
{
return true;
}
public function canVisitShared(User $user): bool
{
// Disable share visit when user has more than 3 failed payments
return ! ($user->failedPayments()->count() >= 3);
}
}

View File

@@ -10,21 +10,11 @@ class RestrictionsManager extends Manager
{
public function getDefaultDriver(): string
{
return get_restriction_driver();
return 'default';
}
public function createDefaultDriver(): DefaultRestrictionsEngine
{
return new DefaultRestrictionsEngine();
}
public function createFixedDriver(): FixedBillingRestrictionsEngine
{
return new FixedBillingRestrictionsEngine();
}
public function createMeteredDriver(): MeteredBillingRestrictionsEngine
{
return new MeteredBillingRestrictionsEngine();
}
}

View File

@@ -1,20 +0,0 @@
<?php
namespace Domain\Admin\Controllers\Dashboard;
use App\Http\Controllers\Controller;
use VueFileManager\Subscription\Domain\Transactions\Models\Transaction;
use VueFileManager\Subscription\Domain\Transactions\Resources\TransactionCollection;
class GetLatestTransactionsController extends Controller
{
public function __invoke(): TransactionCollection
{
$transactions = Transaction::sortable([
'created_at' => 'desc',
])
->take(5)
->get();
return new TransactionCollection($transactions);
}
}

View File

@@ -1,30 +0,0 @@
<?php
namespace Domain\Files\Requests;
use Illuminate\Foundation\Http\FormRequest;
class RemoteUploadRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'urls.*' => 'required|url',
'parent_id' => 'nullable|uuid',
];
}
}

View File

@@ -1,35 +0,0 @@
<?php
namespace Domain\Files\Requests;
use Domain\Admin\Rules\DisabledMimetypes;
use Illuminate\Foundation\Http\FormRequest;
class UploadRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'name' => 'required|string',
'parent_id' => 'nullable|uuid',
'path' => 'sometimes|string',
'is_last' => 'sometimes|string',
'extension' => 'sometimes|string|nullable',
'file' => ['required', 'file', new DisabledMimetypes],
];
}
}

View File

@@ -1,30 +0,0 @@
<?php
namespace Domain\Homepage\Controllers;
use Illuminate\Http\Response;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Mail;
use Domain\Homepage\Mail\SendContactMessage;
use Domain\Homepage\Requests\SendContactMessageRequest;
class SendContactMessageController extends Controller
{
/**
* Send contact message from homepage
*/
public function __invoke(
SendContactMessageRequest $request
): Response {
// Abort in demo mode
abort_if(is_demo(), 201, 'Done');
$contactEmail = get_settings('contact_email');
if ($contactEmail) {
Mail::to($contactEmail)
->send(new SendContactMessage($request->all()));
}
return response('Done', 201);
}
}

View File

@@ -1,28 +0,0 @@
<?php
namespace Domain\Homepage\Mail;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
class SendContactMessage extends Mailable
{
use Queueable, SerializesModels;
public function __construct(
private array $request
) {
}
/**
* Build the message.
*/
public function build(): static
{
return $this->from(config('mail.from')['address'])
->replyTo($this->request['email'])
->subject('New Contact Message from ' . $this->request['email'])
->view('mails.contact-message')
->with('request', $this->request);
}
}

View File

@@ -1,33 +0,0 @@
<?php
namespace Domain\Homepage\Requests;
use App\Users\Rules\ReCaptchaRules;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rules\RequiredIf;
class SendContactMessageRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'email' => 'required|email',
'message' => 'required|string',
'reCaptcha' => [new RequiredIf(get_settings('allowed_recaptcha') == 1), 'string', 'nullable', app(ReCaptchaRules::class)],
];
}
}

View File

@@ -1,37 +0,0 @@
<?php
namespace Domain\Invoices\Controllers;
use Domain\Settings\Models\Setting;
use Illuminate\Contracts\View\View;
use App\Http\Controllers\Controller;
use Illuminate\Contracts\View\Factory;
use Illuminate\Contracts\Foundation\Application;
use App\Users\Actions\FormatUsageEstimatesAction;
use VueFileManager\Subscription\Domain\Transactions\Models\Transaction;
class GetInvoiceController extends Controller
{
public function __construct(
public FormatUsageEstimatesAction $formatUsageEstimates,
) {
}
public function __invoke(Transaction $invoice): View|Factory|Application
{
// Get app settings
$settings = json_decode(
Setting::all()
->pluck('value', 'name')
);
// Format metadata
if ($invoice->metadata) {
$invoice->metadata = ($this->formatUsageEstimates)($invoice->currency, $invoice->metadata);
}
// Return invoice view
return view('vuefilemanager.invoice')
->with('settings', $settings)
->with('invoice', $invoice);
}
}

View File

@@ -1,22 +0,0 @@
<?php
namespace Domain\Notifications\Controllers;
use Illuminate\Http\Response;
use App\Http\Controllers\Controller;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Contracts\Routing\ResponseFactory;
class FlushUserNotificationsController extends Controller
{
public function __invoke(): Response|Application|ResponseFactory
{
if (is_demo_account()) {
return response('Done', 204);
}
// Delete all notifications
auth()->user()->notifications()->delete();
return response('Done', 204);
}
}

View File

@@ -1,15 +0,0 @@
<?php
namespace Domain\Notifications\Controllers;
use App\Http\Controllers\Controller;
use Domain\Notifications\Resources\NotificationCollection;
class GetUserNotificationsController extends Controller
{
public function __invoke(): NotificationCollection
{
return new NotificationCollection(
auth()->user()->notifications
);
}
}

View File

@@ -1,22 +0,0 @@
<?php
namespace Domain\Notifications\Controllers;
use Illuminate\Http\Response;
use App\Http\Controllers\Controller;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Contracts\Routing\ResponseFactory;
class MarkUserNotificationsAsReadController extends Controller
{
public function __invoke(): Response|Application|ResponseFactory
{
if (is_demo_account()) {
return response('Done', 204);
}
// Mark all notifications as read
auth()->user()->unreadNotifications()->update(['read_at' => now()]);
return response('Done', 204);
}
}

View File

@@ -1,16 +0,0 @@
<?php
namespace Domain\Notifications\Resources;
use Illuminate\Http\Resources\Json\ResourceCollection;
class NotificationCollection extends ResourceCollection
{
public $collects = NotificationResource::class;
public function toArray($request): array
{
return [
'data' => $this->collection,
];
}
}

View File

@@ -1,31 +0,0 @@
<?php
namespace Domain\Notifications\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class NotificationResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function toArray($request)
{
return [
'data' => [
'id' => $this->id,
'type' => $this->type,
'attributes' => [
'category' => $this->data['category'],
'title' => $this->data['title'],
'description' => $this->data['description'],
'action' => $this->data['action'] ?? null,
'created_at' => format_date($this->created_at, 'd. M. Y h:i'),
'read_at' => $this->read_at ? format_date($this->read_at, 'd. M. Y h:i') : null,
],
],
];
}
}

View File

@@ -1,16 +0,0 @@
<?php
namespace Domain\Pages\Actions;
use Domain\Pages\Models\Page;
class SeedDefaultPagesAction
{
/**
* Store default pages content like Terms of Service, Privacy Policy and Cookie Policy into database
*/
public function __invoke(): void
{
collect(config('content.pages'))
->each(fn ($page) => Page::updateOrCreate($page));
}
}

View File

@@ -1,49 +0,0 @@
<?php
namespace Domain\Pages\Controllers;
use Illuminate\Http\Request;
use Domain\Pages\Models\Page;
use Illuminate\Http\Response;
use App\Http\Controllers\Controller;
use Domain\Pages\Resources\PageResource;
use Domain\Pages\Resources\PageCollection;
class AdminPagesController extends Controller
{
/**
* Get all pages
*/
public function index(): PageCollection
{
return new PageCollection(
Page::sortable()
->paginate(10)
);
}
/**
* Get single page resource
*/
public function show(Page $page): PageResource
{
return new PageResource($page);
}
/**
* Update page content
*/
public function update(Request $request, Page $page): Response
{
// Abort in demo mode
abort_if(is_demo(), 204, 'Done.');
$page->update(
make_single_input($request)
);
return response(
new PageResource($page),
204
);
}
}

View File

@@ -1,17 +0,0 @@
<?php
namespace Domain\Pages\Controllers;
use Domain\Pages\Models\Page;
use App\Http\Controllers\Controller;
use Domain\Pages\Resources\PageResource;
class PagesController extends Controller
{
/**
* Get single page content
*/
public function show(Page $page): PageResource
{
return new PageResource($page);
}
}

View File

@@ -1,36 +0,0 @@
<?php
namespace Domain\Pages\Models;
use Kyslik\ColumnSortable\Sortable;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Factories\HasFactory;
/**
* @property string slug
* @property string title
* @property bool visibility
* @property string content
*/
class Page extends Model
{
use Sortable, HasFactory;
public array $sortable = [
'title',
'slug',
'visibility',
];
public $fillable = [
'slug',
'title',
'visibility',
'content',
];
protected $primaryKey = 'slug';
protected $keyType = 'string';
public $timestamps = false;
}

View File

@@ -1,22 +0,0 @@
<?php
namespace Domain\Pages\Resources;
use Illuminate\Http\Resources\Json\ResourceCollection;
class PageCollection extends ResourceCollection
{
public $collects = PageResource::class;
/**
* Transform the resource collection into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function toArray($request)
{
return [
'data' => $this->collection,
];
}
}

View File

@@ -1,30 +0,0 @@
<?php
namespace Domain\Pages\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class PageResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function toArray($request)
{
return [
'data' => [
'id' => $this->slug,
'type' => 'pages',
'attributes' => [
'visibility' => $this->visibility,
'title' => $this->title,
'slug' => $this->slug,
'content' => $this->content,
'content_formatted' => add_paragraphs($this->content),
],
],
];
}
}

View File

@@ -1,143 +0,0 @@
<?php
namespace Domain\RemoteUpload\Actions;
use Log;
use Error;
use ErrorException;
use App\Users\Models\User;
use Illuminate\Support\Str;
use Domain\Files\Models\File;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Storage;
use Domain\Files\Resources\FileResource;
use Domain\Files\Actions\ProcessFileAction;
use Spatie\QueueableAction\QueueableAction;
use Domain\Files\Actions\StoreExifDataAction;
use Domain\Files\Actions\MoveFileToFTPStorageAction;
use Domain\Files\Actions\ProcessImageThumbnailAction;
use Domain\RemoteUpload\Events\RemoteFileCreatedEvent;
use Domain\Files\Actions\MoveFileToExternalStorageAction;
class GetContentFromExternalSource
{
use QueueableAction;
public function __construct(
public ProcessFileAction $processFile,
public StoreExifDataAction $storeExifData,
public MoveFileToFTPStorageAction $moveFileToFTPStorage,
public ProcessImageThumbnailAction $createImageThumbnail,
public MoveFileToExternalStorageAction $moveFileToExternalStorage,
) {
}
public function __invoke(
array $payload,
User $user,
) {
$total = count($payload['urls']);
$processed = 0;
$failed = 0;
foreach ($payload['urls'] as $url) {
try {
// Get local disk instance
$localDisk = Storage::disk('local');
// Get file from external source
$response = Http::get($url);
// Get extension from response
$extension = extractExtensionFromUrl($url, $response);
// Get blacklisted mimetypes
$this->checkDisabledMimetypes($extension);
// Get file basename
$basename = Str::uuid() . ".$extension";
// Get file name
$name = array_key_exists('filename', pathinfo($url))
? explode('?', pathinfo($url)['filename'])[0]
: Str::uuid();
// Get file path
$path = "files/$user->id/$basename";
// Store file to main storage disk
$localDisk->put($path, $response->getBody());
// Create multiple image thumbnails
($this->createImageThumbnail)($basename, $user->id);
// Store file exif information
$exif = ($this->storeExifData)($path);
// Create new file
$file = File::create([
'mimetype' => $extension,
'type' => getFileType($localDisk->mimeType($path)),
'parent_id' => $payload['parent_id'] ?? null,
'name' => $name ?? $basename,
'basename' => $basename,
'filesize' => $localDisk->size($path),
'user_id' => $user->id,
'creator_id' => auth()->id(),
]);
// Attach file into the exif data
$exif?->update(['file_id' => $file->id]);
// Move file to external storage
match (config('filesystems.default')) {
's3' => ($this->moveFileToExternalStorage)($basename, $user->id),
'ftp', 'azure' => ($this->moveFileToFTPStorage)($basename, $user->id),
default => null
};
// Increment processed items count
$processed++;
// Broadcast new file into the frontend
RemoteFileCreatedEvent::dispatch([
'progress' => [
'total' => $total,
'processed' => $processed,
'failed' => $failed,
],
'file' => new FileResource($file),
]);
} catch (ErrorException | Error $e) {
// Increment items count
$processed++;
$failed++;
// Broadcast new file into the frontend
RemoteFileCreatedEvent::dispatch([
'progress' => [
'total' => $total,
'processed' => $processed,
'failed' => $failed,
],
'file' => null,
]);
Log::error("Remote upload failed as {$e->getMessage()}");
Log::error($e->getTraceAsString());
}
}
}
/**
* @param string|null $extension
*/
protected function checkDisabledMimetypes(?string $extension): void
{
$mimetypeBlacklist = explode(',', get_settings('mimetypes_blacklist')) ?? null;
// If is extension in mimetype blacklist, Abort!
if ($extension && array_intersect([str_replace('.', '', ".$extension")], $mimetypeBlacklist)) {
abort(422);
}
}
}

View File

@@ -1,42 +0,0 @@
<?php
namespace Domain\RemoteUpload\Controllers;
use Illuminate\Http\Response;
use Domain\Sharing\Models\Share;
use Domain\Folders\Models\Folder;
use App\Http\Controllers\Controller;
use Domain\Files\Requests\RemoteUploadRequest;
use Domain\RemoteUpload\Actions\GetContentFromExternalSource;
class RemoteUploadFileController extends Controller
{
public function __construct(
public GetContentFromExternalSource $getContentFromExternalSource,
) {
}
public function __invoke(RemoteUploadRequest $request, ?Share $shared = null): Response|array
{
if (is_demo_account()) {
return response('Files were successfully added to the upload queue', 201);
}
// Get user
$user = $request->filled('parent_id')
? Folder::find($request->input('parent_id'))
->getLatestParent()
->user
: auth()->user();
// Get content from external sources
if (isBroadcasting()) {
($this->getContentFromExternalSource)
->onQueue()
->execute($request->all(), $user);
} else {
($this->getContentFromExternalSource)($request->all(), $user);
}
return response('Files were successfully added to the upload queue', 201);
}
}

View File

@@ -1,74 +0,0 @@
<?php
namespace Domain\RemoteUpload\Controllers;
use DB;
use Domain\Folders\Models\Folder;
use Domain\Files\Requests\RemoteUploadRequest;
use Domain\UploadRequest\Models\UploadRequest;
use Illuminate\Contracts\Filesystem\FileNotFoundException;
use Domain\RemoteUpload\Actions\GetContentFromExternalSource;
class UploadFilesRemotelyForUploadRequestController
{
public function __construct(
private GetContentFromExternalSource $getContentFromExternalSource,
) {
}
/**
* @throws FileNotFoundException
*/
public function __invoke(RemoteUploadRequest $request, UploadRequest $uploadRequest)
{
// Get upload request root folder query
$folder = Folder::where('id', $uploadRequest->id);
// Create folder if not exist
if ($folder->doesntExist()) {
$this->createFolder($uploadRequest);
}
// Set default parent_id for uploaded file
if (is_null($request->input('parent_id'))) {
$request->merge(['parent_id' => $uploadRequest->id]);
}
// Get content from external sources
if (isBroadcasting()) {
($this->getContentFromExternalSource)
->onQueue()
->execute($request->all(), $uploadRequest->user);
} else {
($this->getContentFromExternalSource)($request->all(), $uploadRequest->user);
}
// Set timestamp for auto filling
cache()->set("auto-filling.$uploadRequest->id", now()->toString());
return response('Files were successfully added to the upload queue', 201);
}
/**
* Create root Upload Request folder
*/
private function createFolder(UploadRequest $uploadRequest): void
{
// Format timestamp
$timestamp = format_date($uploadRequest->created_at, 'd. M. Y');
// Create folder
DB::table('folders')->insert([
'id' => $uploadRequest->id,
'parent_id' => $uploadRequest->folder_id ?? null,
'user_id' => $uploadRequest->user_id,
'name' => $uploadRequest->name ?? __t('upload_request_default_folder', ['timestamp' => $timestamp]),
'created_at' => now(),
'updated_at' => now(),
]);
// Update upload request status
$uploadRequest->update([
'status' => 'filling',
]);
}
}

View File

@@ -1,45 +0,0 @@
<?php
namespace Domain\RemoteUpload\Controllers;
use Illuminate\Http\Response;
use Domain\Sharing\Models\Share;
use App\Http\Controllers\Controller;
use Domain\Files\Requests\RemoteUploadRequest;
use Domain\Sharing\Actions\ProtectShareRecordAction;
use Domain\Sharing\Actions\VerifyAccessToItemAction;
use Domain\RemoteUpload\Actions\GetContentFromExternalSource;
class VisitorRemoteUploadFileController extends Controller
{
public function __construct(
public ProtectShareRecordAction $protectShareRecord,
public VerifyAccessToItemAction $verifyAccessToItem,
public GetContentFromExternalSource $getContentFromExternalSource,
) {
}
public function __invoke(RemoteUploadRequest $request, ?Share $shared = null): Response|array
{
// Check ability to access protected share record
($this->protectShareRecord)($shared);
// Check shared permission
if (is_visitor($shared)) {
abort(403, "You don't have access to this item");
}
// Check access to requested directory
($this->verifyAccessToItem)($request->input('parent_id'), $shared);
// Get content from external sources
if (isBroadcasting()) {
($this->getContentFromExternalSource)
->onQueue()
->execute($request->all(), $shared->user);
} else {
($this->getContentFromExternalSource)($request->all(), $shared->user);
}
return response('Files were successfully added to the upload queue', 201);
}
}

View File

@@ -1,39 +0,0 @@
<?php
namespace Domain\RemoteUpload\Events;
use Illuminate\Queue\SerializesModels;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Contracts\Broadcasting\ShouldBroadcastNow;
class RemoteFileCreatedEvent implements ShouldBroadcastNow
{
use Dispatchable, InteractsWithSockets, SerializesModels;
/**
* Create a new event instance.
*
* @return void
*/
public function __construct(
public array $payload,
) {
}
/**
* The event's broadcast name.
*/
public function broadcastAs(): string
{
return 'RemoteFile.Created';
}
/**
* Get the channels the event should broadcast on.
*/
public function broadcastOn(): PrivateChannel
{
return new PrivateChannel("App.Users.Models.User.{$this->payload['file']->user_id}");
}
}

View File

@@ -1,59 +0,0 @@
<?php
namespace Domain\Settings\Controllers;
use Artisan;
use Illuminate\Http\Response;
use Domain\Settings\Requests\StoreBroadcastServiceCredentialsRequest;
class StoreBroadcastServiceCredentialsController
{
/**
* Configure stripe additionally
*/
public function __invoke(StoreBroadcastServiceCredentialsRequest $request): Response
{
// Abort in demo mode
abort_if(is_demo(), 204, 'Done.');
// Get and store credentials
if (! app()->runningUnitTests()) {
$credentials = [
'pusher' => [
'BROADCAST_DRIVER' => 'pusher',
'PUSHER_APP_ID' => $request->input('id'),
'PUSHER_APP_KEY' => $request->input('key'),
'PUSHER_APP_SECRET' => $request->input('secret'),
'PUSHER_APP_CLUSTER' => $request->input('cluster'),
'PUSHER_APP_HOST' => '',
'PUSHER_APP_PORT' => '',
'PUSHER_APP_TLS' => true,
],
'native' => [
'BROADCAST_DRIVER' => 'pusher',
'PUSHER_APP_ID' => 'local',
'PUSHER_APP_KEY' => 'local',
'PUSHER_APP_SECRET' => 'local',
'PUSHER_APP_CLUSTER' => 'local',
'PUSHER_APP_HOST' => $request->input('host'),
'PUSHER_APP_PORT' => '',
'PUSHER_APP_TLS' => $request->boolean('tls') ? 'true' : 'false',
],
'none' => [
'BROADCAST_DRIVER' => 'null',
],
];
// Store credentials into the .env file
setEnvironmentValue($credentials[$request->input('driver')]);
// Clear cache
if (! is_dev()) {
Artisan::call('cache:clear');
Artisan::call('config:clear');
Artisan::call('config:cache');
}
}
return response('Done', 204);
}
}

View File

@@ -1,80 +0,0 @@
<?php
namespace Domain\Settings\Controllers;
use Artisan;
use Illuminate\Http\Response;
use Domain\Settings\Models\Setting;
use Domain\Settings\Requests\StorePaymentServiceCredentialsRequest;
class StorePaymentServiceCredentialsController
{
/**
* Configure stripe additionally
*/
public function __invoke(StorePaymentServiceCredentialsRequest $request): Response
{
// Abort in demo mode
abort_if(is_demo(), 204, 'Done.');
$options = [
'stripe' => [
'name' => 'allowed_stripe',
'value' => 1,
],
'paypal' => [
'name' => 'allowed_paypal',
'value' => 1,
],
'paystack' => [
'name' => 'allowed_paystack',
'value' => 1,
],
];
// Get options
collect([$options[$request->input('service')]])
->each(fn ($setting) => Setting::updateOrCreate([
'name' => $setting['name'],
], [
'value' => $setting['value'],
]));
$PayPalDefaultMode = config('subscription.credentials.paypal.is_live') ? 'true' : 'false';
// Get and store credentials
if (! app()->runningUnitTests()) {
$credentials = [
'stripe' => [
'STRIPE_PUBLIC_KEY' => $request->input('key'),
'STRIPE_SECRET_KEY' => $request->input('secret'),
'STRIPE_WEBHOOK_SECRET' => $request->input('webhook'),
],
'paystack' => [
'PAYSTACK_PUBLIC_KEY' => $request->input('key'),
'PAYSTACK_SECRET' => $request->input('secret'),
],
'paypal' => [
'PAYPAL_CLIENT_ID' => $request->input('key'),
'PAYPAL_CLIENT_SECRET' => $request->input('secret'),
'PAYPAL_WEBHOOK_ID' => $request->input('webhook'),
'PAYPAL_IS_LIVE' => $request->has('live') ? (string) $request->input('live') : $PayPalDefaultMode,
],
];
// Store credentials into the .env file
setEnvironmentValue($credentials[$request->input('service')]);
// Call plan synchronization for makingcg/subscription package
cache()->add('action.synchronize-plans', now()->toString());
// Clear cache
if (! is_dev()) {
Artisan::call('cache:clear');
Artisan::call('config:clear');
Artisan::call('config:cache');
}
}
return response('Done', 204);
}
}

View File

@@ -1,60 +0,0 @@
<?php
namespace Domain\Settings\Controllers;
use Artisan;
use Illuminate\Http\Response;
use Domain\Settings\Models\Setting;
use Domain\Settings\Requests\StoreSocialServiceCredentialsRequest;
class StoreSocialServiceCredentialsController
{
/**
* Configure stripe additionally
*/
public function __invoke(StoreSocialServiceCredentialsRequest $request): Response
{
// Abort in demo mode
abort_if(is_demo(), 204, 'Done.');
// Set on social login
Setting::updateOrCreate([
'name' => "allowed_{$request->input('service')}",
], [
'value' => 1,
]);
// Get and store credentials
if (! app()->runningUnitTests()) {
$credentials = [
'facebook' => [
'FACEBOOK_CLIENT_ID' => $request->input('client_id'),
'FACEBOOK_CLIENT_SECRET' => $request->input('client_secret'),
],
'google' => [
'GOOGLE_CLIENT_ID' => $request->input('client_id'),
'GOOGLE_CLIENT_SECRET' => $request->input('client_secret'),
],
'github' => [
'GITHUB_CLIENT_ID' => $request->input('client_id'),
'GITHUB_CLIENT_SECRET' => $request->input('client_secret'),
],
'recaptcha' => [
'RECAPTCHA_CLIENT_ID' => $request->input('client_id'),
'RECAPTCHA_CLIENT_SECRET' => $request->input('client_secret'),
],
];
// Store credentials into the .env file
setEnvironmentValue($credentials[$request->input('service')]);
// Clear cache
if (! is_dev()) {
Artisan::call('cache:clear');
Artisan::call('config:clear');
Artisan::call('config:cache');
}
}
return response('Done', 204);
}
}

View File

@@ -1,35 +0,0 @@
<?php
namespace Domain\Settings\Requests;
use Illuminate\Foundation\Http\FormRequest;
class StoreBroadcastServiceCredentialsRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'driver' => 'required|string',
'id' => 'sometimes|nullable|string',
'key' => 'sometimes|nullable|string',
'secret' => 'sometimes|nullable|string',
'cluster' => 'sometimes|nullable|string',
'port' => 'sometimes|nullable|string',
'host' => 'sometimes|nullable|string',
];
}
}

View File

@@ -1,32 +0,0 @@
<?php
namespace Domain\Settings\Requests;
use Illuminate\Foundation\Http\FormRequest;
class StorePaymentServiceCredentialsRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'key' => 'required|string',
'secret' => 'required|string',
'webhook' => 'sometimes|string',
'live' => 'sometimes|nullable|boolean',
];
}
}

View File

@@ -1,31 +0,0 @@
<?php
namespace Domain\Settings\Requests;
use Illuminate\Foundation\Http\FormRequest;
class StoreSocialServiceCredentialsRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'client_id' => 'required|string',
'client_secret' => 'required|string',
'service' => 'required|string',
];
}
}

View File

@@ -1,29 +0,0 @@
<?php
namespace Domain\Settings\Requests;
use Illuminate\Foundation\Http\FormRequest;
class UpgradeLicenseRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'purchaseCode' => 'required|string',
];
}
}

View File

@@ -1,36 +0,0 @@
<?php
namespace Domain\SetupWizard\Requests;
use Illuminate\Foundation\Http\FormRequest;
class StoreStripeBillingRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'billing_phone_number' => 'sometimes|nullable|string',
'billing_postal_code' => 'required|string',
'billing_vat_number' => 'required|string',
'billing_address' => 'required|string',
'billing_country' => 'required|string',
'billing_state' => 'required|string',
'billing_city' => 'required|string',
'billing_name' => 'required|string',
];
}
}

View File

@@ -1,32 +0,0 @@
<?php
namespace Domain\SetupWizard\Requests;
use Illuminate\Foundation\Http\FormRequest;
class StoreStripeCredentialsRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'currency' => 'required|string',
'webhookSecret' => 'required|string',
'secret' => 'required|string',
'key' => 'required|string',
];
}
}

View File

@@ -1,34 +0,0 @@
<?php
namespace Domain\SetupWizard\Requests;
use Illuminate\Foundation\Http\FormRequest;
class StoreStripePlansRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'plans' => 'required|array',
'plans.*.type' => 'required|string',
'plans.*.attributes.name' => 'required|string',
'plans.*.attributes.price' => 'required|string',
'plans.*.attributes.description' => 'sometimes|nullable|string',
'plans.*.attributes.capacity' => 'required|digits_between:1,9',
];
}
}

View File

@@ -1,42 +0,0 @@
<?php
namespace Domain\Subscriptions\Notifications;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Notification;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
class BillingAlertTriggeredNotification extends Notification implements ShouldQueue
{
use Queueable;
public function via(): array
{
return ['mail', 'database', 'broadcast'];
}
public function toMail(): MailMessage
{
return (new MailMessage)
->subject(__t('billing_alert_reached_long'))
->greeting(__t('hello'))
->line(__t('billing_alert_reached_long_note'))
->action(__t('show_billing'), url('/user/settings/billing'));
}
public function toArray(): array
{
return [
'category' => 'billing-alert',
'title' => __t('billing_alert_reached_short'),
'description' => __t('billing_alert_reached_short_note'),
'action' => [
'type' => 'route',
'params' => [
'route' => __t('billing'),
'button' => __t('show_billing'),
],
],
];
}
}

View File

@@ -1,41 +0,0 @@
<?php
namespace Domain\Subscriptions\Notifications;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Notification;
use Illuminate\Contracts\Queue\ShouldQueue;
class BonusCreditAddedNotification extends Notification implements ShouldQueue
{
use Queueable;
/**
* Create a new notification instance.
*
* @return void
*/
public function __construct(
public string $bonus
) {
}
/**
* Get the notification's delivery channels.
*/
public function via(mixed $notifiable): array
{
return ['database', 'broadcast'];
}
/**
* Get the array representation of the notification.
*/
public function toArray(mixed $notifiable): array
{
return [
'category' => 'gift',
'title' => __t('you_received_bonus', ['bonus' => $this->bonus]),
'description' => __t('you_received_bonus_note', ['bonus' => $this->bonus]),
];
}
}

View File

@@ -1,26 +0,0 @@
<?php
namespace Domain\Subscriptions\Notifications;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Notification;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
class ChargeFromCreditCardFailedAgainNotification extends Notification implements ShouldQueue
{
use Queueable;
public function via(): array
{
return ['mail'];
}
public function toMail(): MailMessage
{
return (new MailMessage)
->subject(__t('charge_from_card_failed_again_subject'))
->greeting(__t('hello'))
->line(__t('charge_from_card_failed_again_line'))
->action(__t('charge_from_card_failed_again_action'), url('/user/settings/billing'));
}
}

View File

@@ -1,26 +0,0 @@
<?php
namespace Domain\Subscriptions\Notifications;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Notification;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
class ChargeFromCreditCardFailedNotification extends Notification implements ShouldQueue
{
use Queueable;
public function via(): array
{
return ['mail'];
}
public function toMail(): MailMessage
{
return (new MailMessage)
->subject(__t('charge_from_card_failed_subject'))
->greeting(__t('hello'))
->line(__t('charge_from_card_failed_line'))
->action(__t('charge_from_card_failed_action'), url('/user/settings/billing'));
}
}

View File

@@ -1,31 +0,0 @@
<?php
namespace Domain\Subscriptions\Notifications;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Notification;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
class ConfirmStripePaymentNotification extends Notification implements ShouldQueue
{
use Queueable;
public function __construct(
public array $payload
) {
}
public function via(): array
{
return ['mail'];
}
public function toMail(): MailMessage
{
return (new MailMessage)
->subject(__t('confirm_payment'))
->greeting(__t('confirm_payment_greeting', ['amount' => $this->payload['amount']]))
->line(__t('confirm_payment_line'))
->action(__t('confirm_payment_action'), $this->payload['url']);
}
}

View File

@@ -1,42 +0,0 @@
<?php
namespace Domain\Subscriptions\Notifications;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Notification;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
class InsufficientBalanceNotification extends Notification implements ShouldQueue
{
use Queueable;
public function via(): array
{
return ['mail', 'database', 'broadcast'];
}
public function toMail(): MailMessage
{
return (new MailMessage)
->subject(__t('withdrawal_failed_long'))
->greeting(__t('hello'))
->line(__t('withdrawal_failed_long_note'))
->action(__t('fund_your_account'), url('/user/settings/billing'));
}
public function toArray(): array
{
return [
'category' => 'insufficient-balance',
'title' => __t('withdrawal_failed_short'),
'description' => __t('withdrawal_failed_short_note'),
'action' => [
'type' => 'route',
'params' => [
'route' => __t('billing'),
'button' => __t('show_billing'),
],
],
];
}
}

View File

@@ -1,48 +0,0 @@
<?php
namespace Domain\Subscriptions\Notifications;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Notification;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use VueFileManager\Subscription\Domain\Subscriptions\Models\Subscription;
class SubscriptionWasCreatedNotification extends Notification implements ShouldQueue
{
use Queueable;
public function __construct(
public Subscription $subscription,
) {
}
public function via(): array
{
return ['database', 'broadcast'];
}
public function toMail(): MailMessage
{
return (new MailMessage)
->subject(__t('subscription_created_long', ['plan' => $this->subscription->plan->name]))
->greeting(__t('hello'))
->line(__t('subscription_created_long_note', ['plan' => $this->subscription->plan->name]))
->action(__t('go_to_subscription'), url('/user/settings/billing'));
}
public function toArray(): array
{
return [
'category' => 'subscription-created',
'title' => __t('subscription_created_short'),
'description' => __t('subscription_created_short_note', ['plan' => $this->subscription->plan->name]),
'action' => [
'type' => 'route',
'params' => [
'route' => __t('billing'),
'button' => __t('show_billing'),
],
],
];
}
}

View File

@@ -1,34 +0,0 @@
<?php
namespace Domain\Teams\Actions;
use App\Users\Models\User;
class CheckMaxTeamMembersLimitAction
{
public function __invoke(User $user, array $newInvites): bool
{
// Get user limitation summary
$limits = $user->limitations->summary();
// Check unlimited option
if ((int) $limits['max_team_members']['total'] === -1) {
return true;
}
// Get currently used member emails
$allowedEmails = $limits['max_team_members']['meta']['allowed_emails'];
// Get new email invites from request
$invitationEmails = collect($newInvites)
->pluck('email');
// Count total unique members
$totalMembers = $allowedEmails
->merge($invitationEmails)
->unique()
->count();
// Check if there is more unique members than total max team members are allowed
return ! ($totalMembers > $limits['max_team_members']['total']);
}
}

View File

@@ -1,38 +0,0 @@
<?php
namespace Domain\Teams\Actions;
use DB;
use App\Users\Models\User;
use Domain\Teams\Models\TeamFolderInvitation;
class ClearActionInInvitationNotificationAction
{
public function __invoke(User $user, TeamFolderInvitation $invitation): void
{
if (is_demo_account()) {
return;
}
// Get notification with invitation
$notification = DB::table('notifications')
->where('notifiable_id', $user->id)
->where('data', 'LIKE', "%{$invitation->id}%")
->first();
if ($notification) {
// Get data
$data = json_decode($notification->data);
// Clear action object
$data->action = null;
// Update notification
DB::table('notifications')
->where('notifiable_id', $user->id)
->where('data', 'LIKE', "%{$invitation->id}%")
->update([
'data' => json_encode($data),
]);
}
}
}

View File

@@ -1,44 +0,0 @@
<?php
namespace Domain\Teams\Actions;
use App\Users\Models\User;
use Domain\Folders\Models\Folder;
use Spatie\QueueableAction\QueueableAction;
use Illuminate\Support\Facades\Notification;
use Domain\Teams\Models\TeamFolderInvitation;
use Domain\Teams\Notifications\InvitationIntoTeamFolder;
class InviteMembersIntoTeamFolderAction
{
use QueueableAction;
public function __invoke(
array $members,
Folder $folder,
): void {
collect($members)
->each(function ($member) use ($folder) {
// Create invitation
$invitation = TeamFolderInvitation::create([
'permission' => $member['permission'],
'email' => $member['email'],
'parent_id' => $folder->id,
'inviter_id' => $folder->user_id,
]);
// Get user
$user = User::where('email', $member['email'])->first();
// Invite native user
if ($user) {
$user->notify(new InvitationIntoTeamFolder($folder, $invitation));
}
// Invite guest
if (! $user) {
Notification::route('mail', $member['email'])
->notify(new InvitationIntoTeamFolder($folder, $invitation));
}
});
}
}

View File

@@ -1,22 +0,0 @@
<?php
namespace Domain\Teams\Actions;
use Illuminate\Support\Arr;
use Domain\Folders\Models\Folder;
use Illuminate\Support\Facades\DB;
class SetTeamFolderPropertyForAllChildrenAction
{
public function __invoke(Folder $folder, bool $isTeamFolder)
{
// Get all children of team folder
$childrenFolderIds = Folder::with('folders:id,parent_id')
->where('id', $folder->id)
->get('id');
// Set all children as team_folder = true
DB::table('folders')
->whereIn('id', Arr::flatten(filter_folders_ids($childrenFolderIds)))
->update(['team_folder' => $isTeamFolder]);
}
}

View File

@@ -1,57 +0,0 @@
<?php
namespace Domain\Teams\Actions;
use Domain\Folders\Models\Folder;
use Illuminate\Support\Facades\DB;
class UpdateInvitationsAction
{
public function __construct(
public InviteMembersIntoTeamFolderAction $inviteMembers,
) {
}
public function __invoke(Folder $folder, $invitations): void
{
// Get stored invitations from team folder
$storedInvitations = $folder
->teamInvitations()
->pluck('email');
// Get newbies added by user in request
$newbies = collect($invitations)
->filter(
fn ($invitation) => ! in_array($invitation['email'], $storedInvitations->toArray())
);
// Get deleted invitations by user in request
$removed = $storedInvitations->diff(
collect($invitations)->pluck('email')->toArray()
);
// Invite team members
if ($newbies->isNotEmpty()) {
$this->inviteMembers->onQueue()->execute($newbies->toArray(), $folder);
}
// Delete invite from team folder
if ($removed->isNotEmpty()) {
DB::table('team_folder_invitations')
->where('parent_id', $folder->id)
->whereIn('email', $removed)
->delete();
}
// Update privileges
collect($invitations)
->each(
fn ($invitation) =>
DB::table('team_folder_invitations')
->where('parent_id', $folder->id)
->where('email', $invitation['email'])
->update([
'permission' => $invitation['permission'],
])
);
}
}

View File

@@ -1,40 +0,0 @@
<?php
namespace Domain\Teams\Actions;
use DB;
use Domain\Folders\Models\Folder;
class UpdateMembersAction
{
public function __invoke(Folder $folder, $members): void
{
$existingMembers = $folder
->teamMembers()
->pluck('user_id');
// Get deleted members from request
$deletedMembers = $existingMembers->diff(
collect($members)->pluck('id')->toArray()
);
// Remove team members from team folder
if ($deletedMembers->isNotEmpty()) {
DB::table('team_folder_members')
->where('parent_id', $folder->id)
->whereIn('user_id', $deletedMembers->toArray())
->delete();
}
// Update privileges
collect($members)
->each(
fn ($member) =>
DB::table('team_folder_members')
->where('parent_id', $folder->id)
->where('user_id', $member['id'])
->update([
'permission' => $member['permission'],
])
);
}
}

View File

@@ -1,56 +0,0 @@
<?php
namespace Domain\Teams\Controllers;
use Str;
use Gate;
use Domain\Files\Models\File;
use Domain\Folders\Models\Folder;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Auth;
use Domain\Files\Resources\FilesCollection;
use Domain\Folders\Resources\FolderResource;
use Domain\Folders\Resources\FolderCollection;
class BrowseSharedWithMeController
{
public function __invoke($id): array
{
$id = Str::isUuid($id) ? $id : null;
if ($id) {
$teamFolder = Folder::findOrFail($id)->getLatestParent();
if (! Gate::any(['can-edit', 'can-view'], [$teamFolder, null])) {
abort(403, 'Access Denied');
}
$folders = Folder::with(['parent:id,name'])
->where('parent_id', $id)
->sortable()
->get();
$files = File::with(['parent:id,name'])
->where('parent_id', $id)
->sortable()
->get();
}
if (! $id) {
$sharedFolderIds = DB::table('team_folder_members')
->where('user_id', Auth::id())
->whereIn('permission', ['can-edit', 'can-view'])
->pluck('parent_id');
$folders = Folder::whereIn('id', $sharedFolderIds)
->sortable()
->get();
}
return [
'root' => $id ? new FolderResource(Folder::findOrFail($id)) : null,
'folders' => new FolderCollection($folders),
'files' => isset($files) ? new FilesCollection($files) : new FilesCollection([]),
'teamFolder' => $id ? new FolderResource($teamFolder) : null,
];
}
}

View File

@@ -1,59 +0,0 @@
<?php
namespace Domain\Teams\Controllers;
use Illuminate\Http\Response;
use Domain\Folders\Models\Folder;
use App\Http\Controllers\Controller;
use Domain\Teams\Models\TeamFolderMember;
use Illuminate\Contracts\Routing\ResponseFactory;
use Domain\Teams\Requests\ConvertIntoTeamFolderRequest;
use Domain\Teams\Actions\InviteMembersIntoTeamFolderAction;
use Domain\Teams\Actions\SetTeamFolderPropertyForAllChildrenAction;
class ConvertFolderIntoTeamFolderController extends Controller
{
public function __construct(
public InviteMembersIntoTeamFolderAction $inviteMembers,
public SetTeamFolderPropertyForAllChildrenAction $setTeamFolderPropertyForAllChildren,
) {
}
public function __invoke(
ConvertIntoTeamFolderRequest $request,
Folder $folder
): ResponseFactory|Response {
// Abort in demo mode
if (is_demo_account()) {
return response($folder, 201);
}
// Check if user didn't exceed max team members limit
if (! $folder->user->canInviteTeamMembers($request->input('invitations'))) {
return response([
'type' => 'error',
'message' => 'You exceed your members limit.',
], 401);
}
// Update root team folder
$folder->update([
'team_folder' => 1,
'parent_id' => null,
]);
// Mark all children folders as team folder
($this->setTeamFolderPropertyForAllChildren)($folder, true);
// Attach owner into members
TeamFolderMember::create([
'parent_id' => $folder->id,
'user_id' => $folder->user_id,
'permission' => 'owner',
]);
// Invite team members
($this->inviteMembers)($request->input('invitations'), $folder);
return response($folder, 201);
}
}

View File

@@ -1,79 +0,0 @@
<?php
namespace Domain\Teams\Controllers;
use App\Users\Models\User;
use Illuminate\Http\Response;
use App\Http\Controllers\Controller;
use Domain\Teams\Models\TeamFolderMember;
use Domain\Teams\Models\TeamFolderInvitation;
use Illuminate\Contracts\Routing\ResponseFactory;
use Domain\Teams\Resources\TeamInvitationResource;
use Domain\Teams\Actions\ClearActionInInvitationNotificationAction;
class InvitationsController extends Controller
{
public function show(TeamFolderInvitation $invitation)
{
if ($invitation->status !== 'pending') {
abort(410);
}
return new TeamInvitationResource($invitation);
}
public function update(
TeamFolderInvitation $invitation,
ClearActionInInvitationNotificationAction $clearActionInInvitationNotification,
): ResponseFactory|Response {
$user = User::where('email', $invitation->email)
->first();
if ($user) {
if (is_demo_account()) {
return response('Done', 204);
}
$invitation->accept();
// Store team member
TeamFolderMember::create([
'user_id' => $user->id,
'parent_id' => $invitation->parent_id,
'permission' => $invitation->permission,
]);
// Clear action in existing notification
$clearActionInInvitationNotification($user, $invitation);
}
if (! $user) {
$invitation->update([
'status' => 'waiting-for-registration',
]);
}
return response('Done', 204);
}
public function destroy(
TeamFolderInvitation $invitation,
ClearActionInInvitationNotificationAction $clearActionInInvitationNotification,
): ResponseFactory|Response {
$invitation->reject();
// Get user from invitation
$user = User::where('email', $invitation->email)
->first();
// Clear action in existing notification
if ($user) {
if (is_demo_account()) {
return response('Done', 204);
}
$clearActionInInvitationNotification($user, $invitation);
}
return response('Done', 204);
}
}

View File

@@ -1,34 +0,0 @@
<?php
namespace Domain\Teams\Controllers;
use Gate;
use Illuminate\Http\Response;
use Domain\Folders\Models\Folder;
use Illuminate\Support\Facades\DB;
use App\Http\Controllers\Controller;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Contracts\Routing\ResponseFactory;
class LeaveTeamFolderController extends Controller
{
public function __invoke(Folder $folder): Response|Application|ResponseFactory
{
// Abort in demo mode
if (is_demo_account()) {
return response('Done.', 204);
}
// Authorize action
if (! Gate::any(['can-edit', 'can-view'], [$folder, null])) {
abort(403, 'Access Denied');
}
// Find and delete attached member from team folder
DB::table('team_folder_members')
->where('parent_id', $folder->id)
->where('user_id', auth()->id())
->delete();
return response('Done.', 204);
}
}

View File

@@ -1,31 +0,0 @@
<?php
namespace Domain\Teams\Controllers;
use Gate;
use Domain\Folders\Models\Folder;
class NavigationTreeController
{
public function __invoke(Folder $folder): array
{
$teamFolder = $folder->getLatestParent();
if (! Gate::any(['can-edit', 'can-view'], [$teamFolder, null])) {
abort(403, 'Access Denied');
}
$folders = Folder::with('folders:id,parent_id,id,name,team_folder')
->where('parent_id', $teamFolder->id)
->sortable()
->get(['id', 'parent_id', 'id', 'name', 'team_folder']);
return [
[
'name' => $teamFolder->name,
'folders' => $folders,
'isMovable' => true,
'isOpen' => true,
],
];
}
}

View File

@@ -1,170 +0,0 @@
<?php
namespace Domain\Teams\Controllers;
use Illuminate\Support\Str;
use Domain\Files\Models\File;
use Illuminate\Http\Response;
use Domain\Folders\Models\Folder;
use Illuminate\Support\Facades\DB;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Auth;
use Domain\Teams\Models\TeamFolderMember;
use Domain\Teams\DTO\CreateTeamFolderData;
use Domain\Files\Resources\FilesCollection;
use Domain\Folders\Resources\FolderResource;
use Domain\Teams\Actions\UpdateMembersAction;
use Domain\Folders\Resources\FolderCollection;
use Domain\Teams\Actions\UpdateInvitationsAction;
use Illuminate\Contracts\Routing\ResponseFactory;
use Domain\Teams\Requests\CreateTeamFolderRequest;
use Domain\Teams\Requests\UpdateTeamFolderMembersRequest;
use Domain\Teams\Actions\InviteMembersIntoTeamFolderAction;
use Domain\Teams\Actions\SetTeamFolderPropertyForAllChildrenAction;
class TeamFoldersController extends Controller
{
public function __construct(
public InviteMembersIntoTeamFolderAction $inviteMembers,
public SetTeamFolderPropertyForAllChildrenAction $setTeamFolderPropertyForAllChildren,
) {
}
public function show($id): array
{
$id = Str::isUuid($id) ? $id : null;
if ($id) {
$folders = Folder::where('parent_id', $id)
->where('team_folder', true)
->sortable()
->get();
$files = File::where('parent_id', $id)
->sortable()
->get();
}
if (! $id) {
$folders = Folder::where('parent_id', null)
->where('team_folder', true)
->where('user_id', Auth::id())
->sortable()
->get();
}
// Collect folders and files to single array
return [
'folders' => new FolderCollection($folders),
'files' => isset($files) ? new FilesCollection($files) : new FilesCollection([]),
'root' => $id ? new FolderResource(Folder::findOrFail($id)) : null,
'teamFolder' => $id ? new FolderResource(Folder::findOrFail($id)->getLatestParent()) : null,
];
}
public function store(
CreateTeamFolderRequest $request,
): ResponseFactory | Response {
// Abort in demo mode
abort_if(is_demo_account(), 201, 'Done.');
$data = CreateTeamFolderData::fromRequest($request);
// Check if user can create team folder
if (! $request->user()->canCreateTeamFolder()) {
return response([
'type' => 'error',
'message' => 'This user action is not allowed.',
], 401);
}
// Check if user didn't exceed max team members limit
if (! $request->user()->canInviteTeamMembers($data->invitations)) {
return response([
'type' => 'error',
'message' => 'You exceed your members limit.',
], 401);
}
// Create folder
$folder = Folder::create([
'user_id' => $request->user()->id,
'name' => $data->name,
'team_folder' => 1,
]);
// Attach owner into members
TeamFolderMember::create([
'parent_id' => $folder->id,
'user_id' => $request->user()->id,
'permission' => 'owner',
]);
// Invite team members
$this->inviteMembers->onQueue()->execute($data->invitations, $folder);
return response(new FolderResource($folder), 201);
}
public function update(
UpdateTeamFolderMembersRequest $request,
Folder $folder,
UpdateInvitationsAction $updateInvitations,
UpdateMembersAction $updateMembers,
): ResponseFactory | Response {
// Abort in demo mode
if (is_demo_account()) {
return response(new FolderResource($folder), 201);
}
// Authorize request
$this->authorize('owner', $folder);
// Check if user didn't exceed max team members limit
if (! $request->user()->canInviteTeamMembers($request->input('invitations'))) {
return response([
'type' => 'error',
'message' => 'You exceed your members limit.',
], 401);
}
$updateInvitations(
$folder,
$request->input('invitations')
);
$updateMembers(
$folder,
$request->input('members')
);
return response(new FolderResource($folder), 201);
}
public function destroy(Folder $folder): ResponseFactory | Response
{
// Abort in demo mode
if (is_demo_account()) {
return response('Done.', 201);
}
$this->authorize('owner', $folder);
// Delete existing invitations
DB::table('team_folder_invitations')
->where('parent_id', $folder->id)
->delete();
// Delete attached members from folder
DB::table('team_folder_members')
->where('parent_id', $folder->id)
->delete();
($this->setTeamFolderPropertyForAllChildren)($folder, false);
$folder->update([
'team_folder' => 0,
]);
return response('Done.', 204);
}
}

View File

@@ -1,18 +0,0 @@
<?php
namespace Domain\Teams\DTO;
use Spatie\DataTransferObject\DataTransferObject;
class CreateTeamFolderData extends DataTransferObject
{
public string $name;
public array $invitations;
public static function fromRequest($request): self
{
return new self([
'name' => $request->input('name'),
'invitations' => $request->input('invitations'),
]);
}
}

View File

@@ -1,67 +0,0 @@
<?php
namespace Domain\Teams\Models;
use App\Users\Models\User;
use Illuminate\Support\Str;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasOne;
use Database\Factories\TeamFolderInvitationFactory;
use Illuminate\Database\Eloquent\Factories\HasFactory;
/**
* @method static create(array $array)
* @property string id
* @property string parent_id
* @property string email
* @property string status
* @property string created_at
* @property string updated_at
*/
class TeamFolderInvitation extends Model
{
use HasFactory;
protected $casts = [
'id' => 'string',
];
protected $guarded = ['id'];
public $incrementing = false;
protected $keyType = 'string';
public function accept()
{
$this->update([
'status' => 'accepted',
]);
}
public function reject()
{
$this->update([
'status' => 'rejected',
]);
}
protected static function newFactory(): TeamFolderInvitationFactory
{
return TeamFolderInvitationFactory::new();
}
public function inviter(): HasOne
{
return $this->hasOne(User::class, 'id', 'inviter_id');
}
protected static function boot()
{
parent::boot();
static::creating(function ($invitation) {
$invitation->id = Str::uuid();
$invitation->color = config('vuefilemanager.colors')[rand(0, 4)];
});
}
}

View File

@@ -1,31 +0,0 @@
<?php
namespace Domain\Teams\Models;
use Illuminate\Database\Eloquent\Model;
use Database\Factories\TeamFolderMemberFactory;
use Illuminate\Database\Eloquent\Factories\HasFactory;
/**
* @method static create(array $array)
* @property string id
* @property string parent_id
* @property string email
* @property string status
* @property string created_at
* @property string updated_at
*/
class TeamFolderMember extends Model
{
use HasFactory;
protected $guarded = [];
public $incrementing = false;
public $timestamps = false;
protected static function newFactory(): TeamFolderMemberFactory
{
return TeamFolderMemberFactory::new();
}
}

View File

@@ -1,73 +0,0 @@
<?php
namespace Domain\Teams\Notifications;
use App\Users\Models\User;
use Illuminate\Bus\Queueable;
use Domain\Folders\Models\Folder;
use Illuminate\Notifications\Notification;
use Illuminate\Contracts\Queue\ShouldQueue;
use Domain\Teams\Models\TeamFolderInvitation;
use Illuminate\Notifications\Messages\MailMessage;
class InvitationIntoTeamFolder extends Notification implements ShouldQueue
{
use Queueable;
public function __construct(
public Folder $teamFolder,
public TeamFolderInvitation $invitation,
) {
}
/**
* Get the notification's delivery channels.
*/
public function via(): array
{
return ['mail', 'database', 'broadcast'];
}
/**
* Get the mail representation of the notification.
*/
public function toMail(): MailMessage
{
$appTitle = get_settings('app_title') ?? 'VueFileManager';
$user = User::find($this->invitation->email);
if ($user) {
return (new MailMessage)
->subject(__t('team_invitation_notify_title', ['app' => $appTitle]))
->greeting(__t('hello'))
->line(__t('team_invitation_notify_desc'))
->action(__t('join_into_team_folder'), url('/team-folder-invitation', ['id' => $this->invitation->id]))
->salutation(__t('salutation') . ', ' . $appTitle);
}
return (new MailMessage)
->subject(__t('team_invitation_notify_title', ['app' => $appTitle]))
->greeting(__t('hello'))
->line(__t('team_invitation_notify_desc_without_account'))
->action(__t('join_and_create_account'), url('/team-folder-invitation', ['id' => $this->invitation->id]))
->salutation(__t('salutation') . ', ' . $appTitle);
}
/**
* Get the array representation of the notification.
*/
public function toArray(mixed $notifiable): array
{
return [
'category' => 'team-invitation',
'title' => __t('new_team_invitation'),
'description' => __t('x_invite_to_join_team', ['name' => $this->invitation->inviter->settings->name, ]),
'action' => [
'type' => 'invitation',
'params' => [
'id' => $this->invitation->id,
],
],
];
}
}

View File

@@ -1,29 +0,0 @@
<?php
namespace Domain\Teams\Requests;
use Illuminate\Foundation\Http\FormRequest;
class ConvertIntoTeamFolderRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'invitations' => 'required|array',
];
}
}

View File

@@ -1,30 +0,0 @@
<?php
namespace Domain\Teams\Requests;
use Illuminate\Foundation\Http\FormRequest;
class CreateTeamFolderRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'name' => 'required|string',
'invitations' => 'required|array',
];
}
}

View File

@@ -1,30 +0,0 @@
<?php
namespace Domain\Teams\Requests;
use Illuminate\Foundation\Http\FormRequest;
class UpdateTeamFolderMembersRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'members' => 'present|array',
'invitations' => 'present|array',
];
}
}

View File

@@ -1,41 +0,0 @@
<?php
namespace Domain\Teams\Resources;
use App\Users\Models\User;
use Illuminate\Http\Resources\Json\JsonResource;
class TeamInvitationResource extends JsonResource
{
public function toArray($request): array
{
return [
'data' => [
'id' => $this->id,
'type' => 'invitation',
'attributes' => [
'parent_id' => $this->parent_id,
'email' => $this->email,
'color' => $this->color,
'status' => $this->status,
'permission' => $this->permission,
'isExistedUser' => User::where('email', $this->email)->exists(),
],
'relationships' => [
$this->mergeWhen($this->inviter, fn () => [
'inviter' => [
'data' => [
'type' => 'user',
'id' => $this->inviter->id,
'attributes' => [
'name' => $this->inviter->settings->name,
'avatar' => $this->inviter->settings->avatar,
'color' => $this->inviter->settings->color,
],
],
],
]),
],
],
];
}
}

View File

@@ -1,16 +0,0 @@
<?php
namespace Domain\Teams\Resources;
use Illuminate\Http\Resources\Json\ResourceCollection;
class TeamInvitationsCollection extends ResourceCollection
{
public $collects = TeamInvitationResource::class;
public function toArray($request): array
{
return [
'data' => $this->collection,
];
}
}

View File

@@ -1,24 +0,0 @@
<?php
namespace Domain\Teams\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class TeamMemberResource extends JsonResource
{
public function toArray($request): array
{
return [
'data' => [
'id' => $this->id,
'type' => 'member',
'attributes' => [
'email' => $this->email,
'name' => $this->settings->name,
'avatar' => $this->settings->avatar,
'color' => $this->settings->color,
'permission' => $this->pivot->permission,
],
],
];
}
}

View File

@@ -1,16 +0,0 @@
<?php
namespace Domain\Teams\Resources;
use Illuminate\Http\Resources\Json\ResourceCollection;
class TeamMembersCollection extends ResourceCollection
{
public $collects = TeamMemberResource::class;
public function toArray($request): array
{
return [
'data' => $this->collection,
];
}
}

View File

@@ -1,19 +0,0 @@
<?php
namespace Domain\Transactions\Controllers;
use App\Users\Models\User;
use App\Http\Controllers\Controller;
use Domain\Transactions\Resources\TransactionCollection;
use VueFileManager\Subscription\Domain\Transactions\Models\Transaction;
class GetAllTransactionsController extends Controller
{
public function __invoke(User $user)
{
$transactions = Transaction::with('user')
->sortable(['created_at' => 'desc'])
->paginate(20);
return new TransactionCollection($transactions);
}
}

View File

@@ -1,19 +0,0 @@
<?php
namespace Domain\Transactions\Controllers;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Auth;
use Domain\Transactions\Resources\TransactionCollection;
class GetTransactionsController extends Controller
{
public function __invoke()
{
$transactions = Auth::user()
->transactions()
->sortable(['created_at' => 'desc'])
->paginate(15);
return new TransactionCollection($transactions);
}
}

View File

@@ -1,19 +0,0 @@
<?php
namespace Domain\Transactions\Controllers;
use App\Users\Models\User;
use App\Http\Controllers\Controller;
use Domain\Transactions\Resources\TransactionCollection;
class GetUserTransactionsController extends Controller
{
public function __invoke(User $user)
{
$transactions = $user
->transactions()
->sortable(['created_at' => 'desc'])
->paginate(20);
return new TransactionCollection($transactions);
}
}

View File

@@ -1,22 +0,0 @@
<?php
namespace Domain\Transactions\Resources;
use Illuminate\Http\Resources\Json\ResourceCollection;
class TransactionCollection extends ResourceCollection
{
public $collects = TransactionResource::class;
/**
* Transform the resource collection into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function toArray($request)
{
return [
'data' => $this->collection,
];
}
}

View File

@@ -1,57 +0,0 @@
<?php
namespace Domain\Transactions\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
use App\Users\Actions\FormatUsageEstimatesAction;
class TransactionResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function toArray($request)
{
return [
'data' => [
'id' => $this->id,
'type' => 'transactions',
'attributes' => [
'type' => $this->type,
'status' => $this->status,
'note' => $this->note,
'price' => format_currency($this->amount, $this->currency),
'currency' => $this->currency,
'amount' => $this->amount,
'driver' => $this->driver,
'reference' => $this->reference,
'metadata' => $this->metadata
? resolve(FormatUsageEstimatesAction::class)($this->currency, $this->metadata)
: null,
'created_at' => format_date($this->created_at, 'd. M. Y'),
'updated_at' => format_date($this->updated_at, 'd. M. Y'),
],
'relationships' => [
$this->mergeWhen($this->user && $this->user->settings, fn () => [
'user' => [
'data' => [
'id' => $this->user->id,
'type' => 'users',
'attributes' => [
'avatar' => $this->user->settings->avatar,
'first_name' => $this->user->settings->first_name,
'last_name' => $this->user->settings->last_name,
'name' => $this->user->settings->name,
'color' => $this->user->settings->color,
'email' => $this->user->email,
],
],
],
]),
],
],
];
}
}

View File

@@ -1,40 +0,0 @@
<?php
namespace Domain\UploadRequest\Controllers;
use Illuminate\Support\Str;
use Domain\Files\Models\File;
use Domain\Folders\Models\Folder;
use App\Http\Controllers\Controller;
use Domain\Files\Resources\FilesCollection;
use Domain\Folders\Resources\FolderResource;
use Domain\Folders\Resources\FolderCollection;
use Domain\UploadRequest\Models\UploadRequest;
class BrowseUploadRequestController extends Controller
{
public function __invoke(UploadRequest $uploadRequest, $id): array
{
$rootId = Str::isUuid($id) ? $id : $uploadRequest->id;
$folders = Folder::with(['parent:id,name'])
->where('parent_id', $rootId)
->where('user_id', $uploadRequest->user_id)
->sortable()
->get();
// TODO: security check
$files = File::with(['parent:id,name'])
->where('parent_id', $rootId)
->where('user_id', $uploadRequest->user_id)
->sortable()
->get()
->each(fn ($file) => $file->setUploadRequestPublicUrl($uploadRequest->id));
// Collect folders and files to single array
return [
'folders' => new FolderCollection($folders),
'files' => new FilesCollection($files),
'root' => new FolderResource(Folder::find($rootId)),
];
}
}

View File

@@ -1,40 +0,0 @@
<?php
namespace Domain\UploadRequest\Controllers;
use Domain\Folders\Models\Folder;
use Domain\Folders\Resources\FolderResource;
use Domain\Folders\Actions\CreateFolderAction;
use Domain\UploadRequest\Models\UploadRequest;
use Domain\Folders\Requests\CreateFolderRequest;
use Support\Demo\Actions\FakeCreateFolderAction;
class CreateFolderController
{
public function __construct(
public CreateFolderAction $createFolder,
public FakeCreateFolderAction $fakeCreateFolder,
) {
}
public function __invoke(CreateFolderRequest $request, UploadRequest $uploadRequest)
{
// Check privileges
if (! in_array($request->input('parent_id'), getChildrenFolderIds($uploadRequest->id))) {
return response('Access Denied', 403);
}
// Create new folder
$folder = Folder::create([
'parent_id' => $request->input('parent_id'),
'name' => $request->input('name'),
'color' => $request->input('color') ?? null,
'emoji' => $request->input('emoji') ?? null,
'author' => 'visitor',
'user_id' => $uploadRequest->user_id,
'team_folder' => false,
]);
// Return new folder
return response(new FolderResource($folder), 201);
}
}

View File

@@ -1,30 +0,0 @@
<?php
namespace Domain\UploadRequest\Controllers;
use Auth;
use Notification;
use App\Http\Controllers\Controller;
use Domain\UploadRequest\Requests\StoreUploadRequest;
use Domain\UploadRequest\Resources\UploadRequestResource;
use Domain\UploadRequest\Notifications\UploadRequestNotification;
class CreateUploadRequestController extends Controller
{
public function __invoke(StoreUploadRequest $request)
{
$uploadRequest = Auth::user()->uploadRequest()->create([
'folder_id' => $request->input('folder_id'),
'email' => $request->input('email'),
'notes' => $request->input('notes'),
'name' => $request->input('name'),
]);
// If user type email, notify by email
if ($uploadRequest->email) {
Notification::route('mail', $uploadRequest->email)
->notify(new UploadRequestNotification($uploadRequest));
}
return response(new UploadRequestResource($uploadRequest), 201);
}
}

View File

@@ -1,71 +0,0 @@
<?php
namespace Domain\UploadRequest\Controllers;
use Illuminate\Support\Arr;
use Domain\Files\Models\File;
use Domain\Folders\Models\Folder;
use Illuminate\Support\Facades\Storage;
use Domain\Items\Requests\DeleteItemRequest;
use Domain\UploadRequest\Models\UploadRequest;
class DeleteFileOrFolderController
{
public function __invoke(DeleteItemRequest $request, UploadRequest $uploadRequest)
{
foreach ($request->input('items') as $file) {
// Get file or folder item
$item = get_item($file['type'], $file['id']);
// Delete folder
if ($file['type'] === 'folder') {
$this->destroyFolder($item);
}
// Delete file
if ($file['type'] !== 'folder') {
$this->destroyFile($item);
}
}
return response('Done', 204);
}
private function destroyFile(File $file): void
{
// Delete file
Storage::delete("/files/$file->user_id/$file->basename");
// Delete thumbnail if exist
if ($file->type === 'image') {
getThumbnailFileList($file->basename)
->each(fn ($thumbnail) => Storage::delete("files/$file->user_id/$thumbnail"));
}
// Delete file permanently
$file->forceDelete();
}
private function destroyFolder(Folder $folder): void
{
// Get children files
$files = File::whereIn('parent_id', Arr::flatten([filter_folders_ids($folder->folders), $folder->id]))
->get();
// Remove all children files
foreach ($files as $file) {
// Delete file
Storage::delete("/files/$file->user_id/$file->basename");
// Delete thumbnail if exist
if ($file->type === 'image') {
getThumbnailFileList($file->basename)
->each(fn ($thumbnail) => Storage::delete("files/$file->user_id/$thumbnail"));
}
// Delete file permanently
$file->forceDelete();
}
$folder->forceDelete();
}
}

View File

@@ -1,41 +0,0 @@
<?php
namespace Domain\UploadRequest\Controllers\FileAccess;
use Domain\Files\Models\File;
use Illuminate\Http\Response;
use Illuminate\Http\RedirectResponse;
use Domain\Files\Actions\DownloadFileAction;
use Domain\UploadRequest\Models\UploadRequest;
use Domain\Traffic\Actions\RecordDownloadAction;
use Symfony\Component\HttpFoundation\StreamedResponse;
/**
* Get shared file record
*/
class GetFileFromUploadRequestController
{
public function __construct(
private DownloadFileAction $downloadFile,
private RecordDownloadAction $recordDownload,
) {
}
public function __invoke(
string $filename,
UploadRequest $uploadRequest
): StreamedResponse|RedirectResponse|Response {
// Get file
$file = File::where('user_id', $uploadRequest->user_id)
->where('basename', $filename)
->firstOrFail();
// Store user download size
($this->recordDownload)(
file_size: $file->filesize,
user_id: $uploadRequest->user_id,
);
// Finally, download file
return ($this->downloadFile)($file);
}
}

View File

@@ -1,43 +0,0 @@
<?php
namespace Domain\UploadRequest\Controllers\FileAccess;
use Domain\Files\Models\File;
use Illuminate\Http\Response;
use App\Http\Controllers\Controller;
use Domain\UploadRequest\Models\UploadRequest;
use Domain\Traffic\Actions\RecordDownloadAction;
use Illuminate\Contracts\Foundation\Application;
use Domain\Files\Actions\DownloadThumbnailAction;
use Illuminate\Contracts\Routing\ResponseFactory;
use Symfony\Component\HttpFoundation\StreamedResponse;
/**
* Get shared file record
*/
class GetThumbnailFromUploadRequestController extends Controller
{
public function __construct(
private DownloadThumbnailAction $downloadThumbnail,
private RecordDownloadAction $recordDownload,
) {
}
public function __invoke(
string $filename,
UploadRequest $uploadRequest
): Application|ResponseFactory|Response|StreamedResponse {
// Get file
$file = File::where('user_id', $uploadRequest->user_id)
->where('basename', substr($filename, 3))
->firstOrFail();
// Store user download size
($this->recordDownload)(
file_size: $file->filesize,
user_id: $uploadRequest->user_id,
);
// Finally, download thumbnail
return ($this->downloadThumbnail)($filename, $file);
}
}

View File

@@ -1,32 +0,0 @@
<?php
namespace Domain\UploadRequest\Controllers;
use Illuminate\Http\Response;
use Domain\Folders\Models\Folder;
use App\Http\Controllers\Controller;
use Domain\UploadRequest\Models\UploadRequest;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Contracts\Routing\ResponseFactory;
class GetFolderTreeForUploadRequestController extends Controller
{
public function __invoke(UploadRequest $uploadRequest): Application|ResponseFactory|Response|array
{
// Get folders
$folders = Folder::with('folders:id,parent_id,name')
->whereParentId($uploadRequest->id)
->whereUserId($uploadRequest->user_id)
->sortable()
->get(['id', 'parent_id', 'id', 'name']);
return [
[
'name' => __t('upload_request'),
'location' => 'upload-request',
'folders' => $folders,
'isMovable' => true,
'isOpen' => true,
],
];
}
}

View File

@@ -1,13 +0,0 @@
<?php
namespace Domain\UploadRequest\Controllers;
use Domain\UploadRequest\Models\UploadRequest;
use Domain\UploadRequest\Resources\UploadRequestResource;
class GetUploadRequestController
{
public function __invoke(UploadRequest $uploadRequest)
{
return new UploadRequestResource($uploadRequest);
}
}

View File

@@ -1,27 +0,0 @@
<?php
namespace Domain\UploadRequest\Controllers;
use App\Http\Controllers\Controller;
use Domain\Items\Requests\MoveItemRequest;
use Domain\UploadRequest\Models\UploadRequest;
class MoveItemInUploadRequestController extends Controller
{
public function __invoke(
MoveItemRequest $request,
UploadRequest $uploadRequest,
) {
foreach ($request->input('items') as $item) {
$item = get_item($item['type'], $item['id']);
// Check privileges
if (! in_array($item['parent_id'], getChildrenFolderIds($uploadRequest->id))) {
return response('Access Denied', 403);
}
$item->update(['parent_id' => $request->input('to_id') ?? $uploadRequest->id]);
}
return response('Done.', 204);
}
}

View File

@@ -1,44 +0,0 @@
<?php
namespace Domain\UploadRequest\Controllers;
use App\Http\Controllers\Controller;
use Domain\Files\Resources\FileResource;
use Domain\Folders\Resources\FolderResource;
use Domain\Items\Requests\RenameItemRequest;
use Domain\UploadRequest\Models\UploadRequest;
use Domain\Folders\Actions\UpdateFolderPropertyAction;
use Support\Demo\Actions\FakeRenameFileOrFolderAction;
class RenameFileOrFolderController extends Controller
{
public function __construct(
public UpdateFolderPropertyAction $updateFolderProperty,
public FakeRenameFileOrFolderAction $fakeRenameFileOrFolder,
) {
}
public function __invoke(UploadRequest $uploadRequest, string $id, RenameItemRequest $request)
{
// Get item
$item = get_item($request->input('type'), $id);
// Check privileges
if (! in_array($item->parent_id, getChildrenFolderIds($uploadRequest->id))) {
return response('Access Denied', 403);
}
// If request contain icon or color, then change it
if ($request->input('type') === 'folder' && $request->hasAny(['emoji', 'color'])) {
($this->updateFolderProperty)($request, $id);
}
// Update item
$item->update(['name' => $request->input('name')]);
if ($request->input('type') === 'folder') {
return new FolderResource($item);
}
return new FileResource($item);
}
}

View File

@@ -1,26 +0,0 @@
<?php
namespace Domain\UploadRequest\Controllers;
use Illuminate\Http\Response;
use Domain\UploadRequest\Models\UploadRequest;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Contracts\Routing\ResponseFactory;
use Domain\UploadRequest\Resources\UploadRequestResource;
use Domain\UploadRequest\Notifications\UploadRequestFulfilledNotification;
class SetUploadRequestAsFilledController
{
public function __invoke(UploadRequest $uploadRequest): Response|Application|ResponseFactory
{
$uploadRequest->update([
'status' => 'filled',
]);
// Send user notification
if ($uploadRequest->user->email !== 'howdy@hi5ve.digital') {
$uploadRequest->user->notify(new UploadRequestFulfilledNotification($uploadRequest));
}
return response(new UploadRequestResource($uploadRequest), 201);
}
}

View File

@@ -1,79 +0,0 @@
<?php
namespace Domain\UploadRequest\Controllers;
use DB;
use Domain\Folders\Models\Folder;
use Domain\Files\Resources\FileResource;
use Domain\Files\Actions\ProcessFileAction;
use Domain\UploadRequest\Models\UploadRequest;
use Domain\Files\Actions\StoreFileChunksAction;
use Illuminate\Contracts\Filesystem\FileNotFoundException;
class UploadFilesForUploadRequestController
{
public function __construct(
private ProcessFileAction $processFie,
private StoreFileChunksAction $storeFileChunks,
) {
}
/**
* @throws FileNotFoundException
*/
public function __invoke(\Domain\Files\Requests\UploadRequest $request, UploadRequest $uploadRequest)
{
// Get upload request root folder query
$folder = Folder::where('id', $uploadRequest->id);
// Create folder if not exist
if ($folder->doesntExist()) {
$this->createFolder($uploadRequest);
}
// Set default parent_id for uploaded file
if (is_null($request->input('parent_id'))) {
$request->merge(['parent_id' => $uploadRequest->id]);
}
// Store file chunks
$chunkPath = ($this->storeFileChunks)($request);
// Proceed after last chunk
if ($request->boolean('is_last')) {
// Process file
$file = ($this->processFie)($request, $uploadRequest->user, $chunkPath);
// Set public access url
$file->setUploadRequestPublicUrl($uploadRequest->id);
// Set timestamp for auto filling
cache()->set("auto-filling.$uploadRequest->id", now()->toString());
return response(new FileResource($file), 201);
}
}
/**
* Create root Upload Request folder
*/
private function createFolder(UploadRequest $uploadRequest): void
{
// Format timestamp
$timestamp = format_date($uploadRequest->created_at, 'd. M. Y');
// Create folder
DB::table('folders')->insert([
'id' => $uploadRequest->id,
'parent_id' => $uploadRequest->folder_id ?? null,
'user_id' => $uploadRequest->user_id,
'name' => $uploadRequest->name ?? __t('upload_request_default_folder', ['timestamp' => $timestamp]),
'created_at' => now(),
'updated_at' => now(),
]);
// Update upload request status
$uploadRequest->update([
'status' => 'filling',
]);
}
}

View File

@@ -1,24 +0,0 @@
<?php
namespace Domain\UploadRequest\Middleware;
use Closure;
use Illuminate\Http\Request;
class ProtectUploadRequestRoutes
{
/**
* Prevent access for setup wizard controllers after initial app installation.
*/
public function handle(Request $request, Closure $next): mixed
{
// Get upload request
$uploadRequest = $request->route()->parameter('uploadRequest');
// Check if upload request is active
if (! in_array($uploadRequest->status, ['active', 'filling'])) {
return response('Gone', 410);
}
return $next($request);
}
}

View File

@@ -1,63 +0,0 @@
<?php
namespace Domain\UploadRequest\Models;
use App\Users\Models\User;
use Illuminate\Support\Str;
use Domain\Folders\Models\Folder;
use Illuminate\Database\Eloquent\Model;
use Database\Factories\UploadRequestFactory;
use Illuminate\Database\Eloquent\Relations\HasOne;
use Illuminate\Database\Eloquent\Factories\HasFactory;
/**
* @method static create(array $array)
* @property string id
* @property string user_id
* @property string folder_id
* @property string status
* @property string email
* @property string notes
* @property string created_at
* @property string updated_at
*/
class UploadRequest extends Model
{
use HasFactory;
protected $casts = [
'id' => 'string',
];
protected $guarded = ['id'];
public $incrementing = false;
protected $keyType = 'string';
protected static function newFactory(): UploadRequestFactory
{
return UploadRequestFactory::new();
}
public function user(): HasOne
{
return $this->hasOne(User::class, 'id', 'user_id');
}
public function folder(): HasOne
{
return $this->hasOne(Folder::class, 'id', 'id');
}
public function parent(): HasOne
{
return $this->hasOne(Folder::class, 'id', 'folder_id');
}
protected static function boot()
{
parent::boot();
static::creating(fn ($invitation) => $invitation->id = Str::uuid());
}
}

View File

@@ -1,64 +0,0 @@
<?php
namespace Domain\UploadRequest\Notifications;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Notification;
use Illuminate\Contracts\Queue\ShouldQueue;
use Domain\UploadRequest\Models\UploadRequest;
use Illuminate\Notifications\Messages\MailMessage;
class UploadRequestFulfilledNotification extends Notification implements ShouldQueue
{
use Queueable;
/**
* Create a new notification instance.
*
* @return void
*/
public function __construct(
public UploadRequest $uploadRequest
) {
}
/**
* Get the notification's delivery channels.
*/
public function via(mixed $notifiable): array
{
return ['mail', 'database', 'broadcast'];
}
/**
* Get the mail representation of the notification.
*/
public function toMail(mixed $notifiable): MailMessage
{
return (new MailMessage)
->subject(__t('file_request_filled_mail', ['name' => $this->uploadRequest->parent->name]))
->greeting(__t('hello'))
->line(__t('file_request_filled_mail_note'))
->action(__t('show_files'), url("/platform/files/{$this->uploadRequest->id}"))
->salutation(__t('thanks_salutation'));
}
/**
* Get the array representation of the notification.
*/
public function toArray(mixed $notifiable): array
{
return [
'category' => 'file-request',
'title' => __t('file_request_filled'),
'description' => __t('file_request_filled_desc', ['name' => $this->uploadRequest->parent->name, ]),
'action' => [
'type' => 'route',
'params' => [
'route' => 'Files',
'button' => __t('show_files'),
'id' => $this->uploadRequest->id,
],
],
];
}
}

View File

@@ -1,68 +0,0 @@
<?php
namespace Domain\UploadRequest\Notifications;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Notification;
use Illuminate\Contracts\Queue\ShouldQueue;
use Domain\UploadRequest\Models\UploadRequest;
use Illuminate\Notifications\Messages\MailMessage;
class UploadRequestNotification extends Notification implements ShouldQueue
{
use Queueable;
/**
* Create a new notification instance.
*
* @return void
*/
public function __construct(
public UploadRequest $uploadRequest
) {
}
/**
* Get the notification's delivery channels.
*
* @param mixed $notifiable
* @return array
*/
public function via($notifiable)
{
return ['mail'];
}
/**
* Get the mail representation of the notification.
*
* @param mixed $notifiable
* @return \Illuminate\Notifications\Messages\MailMessage
*/
public function toMail($notifiable)
{
// Format optional message
$message = $this->uploadRequest->notes
? __t('file_request_optional_message', ['name' => $this->uploadRequest->user->settings->first_name, 'notes' => $this->uploadRequest->notes])
: null;
return (new MailMessage)
->subject(__t('file_request_notify_title', ['name' => $this->uploadRequest->user->settings->first_name]))
->greeting(__t('hello'))
->line(__t('file_request_notify_description', ['name' => $this->uploadRequest->user->settings->first_name]))
->line($message)
->action(__t('upload_your_files'), url("/request/{$this->uploadRequest->id}/upload"))
->salutation(__t('thanks_salutation'));
}
/**
* Get the array representation of the notification.
*
* @param mixed $notifiable
* @return array
*/
public function toArray($notifiable)
{
return [
];
}
}

Some files were not shown because too many files have changed in this diff Show More