mirror of
https://github.com/VueFileManager/vuefilemanager.git
synced 2026-04-29 19:25:57 +00:00
src folder refactoring
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
namespace App;
|
||||
|
||||
use Illuminate\Foundation\Application;
|
||||
|
||||
class BaseApplication extends Application
|
||||
{
|
||||
protected $namespace = 'App\\';
|
||||
}
|
||||
@@ -0,0 +1,895 @@
|
||||
<?php
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Models\File;
|
||||
use App\Models\User;
|
||||
use App\Models\Share;
|
||||
use App\Models\Folder;
|
||||
use App\Models\Setting;
|
||||
use Illuminate\Support\Str;
|
||||
use App\Services\SetupService;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Foundation\Testing\WithFaker;
|
||||
|
||||
class SetupDevEnvironment extends Command
|
||||
{
|
||||
use WithFaker;
|
||||
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'setup:dev';
|
||||
protected $license = 'Extended';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Set up development environment with demo data';
|
||||
|
||||
private $setup;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->setUpFaker();
|
||||
$this->setup = resolve(SetupService::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
* return @void
|
||||
*/
|
||||
public function handle(): void
|
||||
{
|
||||
$this->info('Setting up development environment');
|
||||
|
||||
$this->info('Creating system directories...');
|
||||
$this->setup->create_directories();
|
||||
|
||||
$this->info('Migrating Databases...');
|
||||
$this->migrate_and_generate();
|
||||
|
||||
$this->info('Storing default settings and content...');
|
||||
$this->store_default_settings();
|
||||
$this->setup->seed_default_pages();
|
||||
$this->setup->seed_default_settings($this->license);
|
||||
$this->setup->seed_default_language();
|
||||
|
||||
$this->info('Creating default admin...');
|
||||
$this->create_admin();
|
||||
|
||||
$this->info('Creating demo users...');
|
||||
$this->create_demo_users();
|
||||
|
||||
$this->info('Creating default admin content...');
|
||||
$this->create_admin_default_content();
|
||||
$this->create_share_records();
|
||||
|
||||
$this->info('Clearing application cache...');
|
||||
$this->clear_cache();
|
||||
|
||||
$this->info('Dispatching jobs...');
|
||||
$this->call('queue:work', [
|
||||
'--stop-when-empty' => true,
|
||||
]);
|
||||
|
||||
$this->info('Everything is done, congratulations! 🥳🥳🥳');
|
||||
}
|
||||
|
||||
/**
|
||||
* Create default admin account
|
||||
*/
|
||||
private function create_admin(): void
|
||||
{
|
||||
$user = User::forceCreate([
|
||||
'role' => 'admin',
|
||||
'email' => 'howdy@hi5ve.digital',
|
||||
'password' => bcrypt('vuefilemanager'),
|
||||
'email_verified_at' => now(),
|
||||
]);
|
||||
|
||||
$user
|
||||
->settings()
|
||||
->create([
|
||||
'avatar' => 'avatars/avatar-01.png',
|
||||
'storage_capacity' => 5,
|
||||
'name' => 'Jane Doe',
|
||||
'address' => $this->faker->address,
|
||||
'state' => $this->faker->state,
|
||||
'city' => $this->faker->city,
|
||||
'postal_code' => $this->faker->postcode,
|
||||
'country' => $this->faker->randomElement(['SK', 'CZ', 'DE', 'FR']),
|
||||
'phone_number' => $this->faker->phoneNumber,
|
||||
'timezone' => $this->faker->randomElement(['+1.0', '+2.0', '+3.0']),
|
||||
]);
|
||||
|
||||
Storage::putFileAs('avatars', storage_path('demo/avatars/avatar-01.png'), 'avatar-01.png', 'private');
|
||||
|
||||
// Show user credentials
|
||||
$this->info('Default admin account created. Email: howdy@hi5ve.digital and Password: vuefilemanager');
|
||||
}
|
||||
|
||||
/**
|
||||
* Create default admin account
|
||||
*/
|
||||
private function create_demo_users(): void
|
||||
{
|
||||
collect([
|
||||
[
|
||||
'avatar' => 'avatar-02.png',
|
||||
],
|
||||
[
|
||||
'avatar' => 'avatar-03.png',
|
||||
],
|
||||
])->each(function ($user) {
|
||||
$newbie = User::forceCreate([
|
||||
'role' => 'user',
|
||||
'email' => $this->faker->email,
|
||||
'password' => bcrypt('vuefilemanager'),
|
||||
'email_verified_at' => now(),
|
||||
]);
|
||||
|
||||
$newbie
|
||||
->settings()
|
||||
->create([
|
||||
'avatar' => "avatars/{$user['avatar']}",
|
||||
'storage_capacity' => 5,
|
||||
'name' => $this->faker->name,
|
||||
'address' => $this->faker->address,
|
||||
'state' => $this->faker->state,
|
||||
'city' => $this->faker->city,
|
||||
'postal_code' => $this->faker->postcode,
|
||||
'country' => $this->faker->randomElement(['SK', 'CZ', 'DE', 'FR']),
|
||||
'phone_number' => $this->faker->phoneNumber,
|
||||
'timezone' => $this->faker->randomElement(['+1.0', '+2.0', '+3.0']),
|
||||
]);
|
||||
|
||||
Storage::putFileAs('avatars', storage_path("demo/avatars/{$user['avatar']}"), $user['avatar'], 'private');
|
||||
|
||||
$this->info("Generated user with email: $newbie->email and Password: vuefilemanager");
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Create default admin content
|
||||
*/
|
||||
private function create_admin_default_content(): void
|
||||
{
|
||||
$user = User::whereEmail('howdy@hi5ve.digital')
|
||||
->first();
|
||||
|
||||
// 1.
|
||||
$shared_folder = Folder::factory(Folder::class)
|
||||
->create([
|
||||
'user_id' => $user->id,
|
||||
'author' => 'user',
|
||||
'name' => 'Shared Folder',
|
||||
'emoji' => [
|
||||
'codes' => '1F680',
|
||||
'char' => '🚀',
|
||||
'name' => 'rocket',
|
||||
'category' => 'Travel & Places (transport-air)',
|
||||
'group' => 'Travel & Places',
|
||||
'subgroup' => 'transport-air',
|
||||
],
|
||||
'created_at' => now(),
|
||||
]);
|
||||
|
||||
Share::factory(Share::class)
|
||||
->create([
|
||||
'type' => 'folder',
|
||||
'item_id' => $shared_folder->id,
|
||||
'user_id' => $user->id,
|
||||
'permission' => 'editor',
|
||||
'is_protected' => false,
|
||||
'password' => null,
|
||||
'expire_in' => null,
|
||||
]);
|
||||
|
||||
$peters_files = Folder::factory(Folder::class)
|
||||
->create([
|
||||
'user_id' => $user->id,
|
||||
'parent_id' => $shared_folder->id,
|
||||
'author' => 'visitor',
|
||||
'name' => "Peter's Files",
|
||||
]);
|
||||
|
||||
// 2.
|
||||
$random_pics = Folder::factory(Folder::class)
|
||||
->create([
|
||||
'user_id' => $user->id,
|
||||
'author' => 'user',
|
||||
'name' => 'Random Pics',
|
||||
'emoji' => [
|
||||
'codes' => '1F4F7',
|
||||
'char' => '📷',
|
||||
'name' => 'camera',
|
||||
'category' => 'Objects (light & video)',
|
||||
'group' => 'Objects',
|
||||
'subgroup' => 'light & video',
|
||||
],
|
||||
'created_at' => now()->subMinutes(1),
|
||||
]);
|
||||
|
||||
$nature = Folder::factory(Folder::class)
|
||||
->create([
|
||||
'user_id' => $user->id,
|
||||
'parent_id' => $random_pics->id,
|
||||
'author' => 'user',
|
||||
'name' => 'Nature',
|
||||
'emoji' => [
|
||||
'codes' => '26F0',
|
||||
'char' => '⛰',
|
||||
'name' => 'mountain',
|
||||
'category' => 'Travel & Places (place-geographic)',
|
||||
'group' => 'Travel & Places',
|
||||
'subgroup' => 'place-geographic',
|
||||
],
|
||||
]);
|
||||
|
||||
$apartments = Folder::factory(Folder::class)
|
||||
->create([
|
||||
'user_id' => $user->id,
|
||||
'parent_id' => $random_pics->id,
|
||||
'author' => 'user',
|
||||
'name' => 'Apartments',
|
||||
'emoji' => [
|
||||
'codes' => '1F3E0',
|
||||
'char' => '🏠',
|
||||
'name' => 'house',
|
||||
'category' => 'Travel & Places (place-building)',
|
||||
'group' => 'Travel & Places',
|
||||
'subgroup' => 'place-building',
|
||||
],
|
||||
]);
|
||||
|
||||
// 3.
|
||||
$playable_media = Folder::factory(Folder::class)
|
||||
->create([
|
||||
'user_id' => $user->id,
|
||||
'author' => 'user',
|
||||
'name' => 'Playable Media',
|
||||
'created_at' => now()->subMinutes(2),
|
||||
]);
|
||||
|
||||
$video = Folder::factory(Folder::class)
|
||||
->create([
|
||||
'user_id' => $user->id,
|
||||
'parent_id' => $playable_media->id,
|
||||
'author' => 'user',
|
||||
'name' => 'Video',
|
||||
]);
|
||||
|
||||
$audio = Folder::factory(Folder::class)
|
||||
->create([
|
||||
'user_id' => $user->id,
|
||||
'parent_id' => $playable_media->id,
|
||||
'author' => 'user',
|
||||
'name' => 'Audio',
|
||||
]);
|
||||
|
||||
// 4.
|
||||
$multi_level = Folder::factory(Folder::class)
|
||||
->create([
|
||||
'user_id' => $user->id,
|
||||
'author' => 'user',
|
||||
'name' => 'Multi Level Folder',
|
||||
'created_at' => now()->subMinutes(3),
|
||||
]);
|
||||
|
||||
$first_level = Folder::factory(Folder::class)
|
||||
->create([
|
||||
'user_id' => $user->id,
|
||||
'parent_id' => $multi_level->id,
|
||||
'author' => 'user',
|
||||
'name' => 'First Level',
|
||||
]);
|
||||
|
||||
$second_level = Folder::factory(Folder::class)
|
||||
->create([
|
||||
'user_id' => $user->id,
|
||||
'parent_id' => $first_level->id,
|
||||
'author' => 'user',
|
||||
'name' => 'Second Level',
|
||||
]);
|
||||
|
||||
$third_level = Folder::factory(Folder::class)
|
||||
->create([
|
||||
'user_id' => $user->id,
|
||||
'parent_id' => $second_level->id,
|
||||
'author' => 'user',
|
||||
'name' => 'Third Level',
|
||||
]);
|
||||
|
||||
// 5.
|
||||
$documents = Folder::factory(Folder::class)
|
||||
->create([
|
||||
'user_id' => $user->id,
|
||||
'author' => 'user',
|
||||
'name' => 'Documents',
|
||||
'created_at' => now()->subMinutes(4),
|
||||
]);
|
||||
|
||||
Share::factory(Share::class)
|
||||
->create([
|
||||
'type' => 'folder',
|
||||
'item_id' => $documents->id,
|
||||
'user_id' => $user->id,
|
||||
'permission' => 'editor',
|
||||
'is_protected' => false,
|
||||
'password' => null,
|
||||
'expire_in' => null,
|
||||
]);
|
||||
|
||||
// 6.
|
||||
$videohive = Folder::factory(Folder::class)
|
||||
->create([
|
||||
'user_id' => $user->id,
|
||||
'author' => 'user',
|
||||
'name' => 'Videohive by MakingCG',
|
||||
'created_at' => now()->subMinutes(5),
|
||||
]);
|
||||
|
||||
$user
|
||||
->favouriteFolders()
|
||||
->sync([
|
||||
$shared_folder->id,
|
||||
$random_pics->id,
|
||||
$documents->id,
|
||||
$peters_files->id,
|
||||
]);
|
||||
|
||||
// Get documents to root directory
|
||||
collect([
|
||||
[
|
||||
'name' => 'Random Document',
|
||||
'basename' => 'Licence.pdf',
|
||||
'mimetype' => 'pdf',
|
||||
],
|
||||
[
|
||||
'name' => 'School Report',
|
||||
'basename' => 'Project Notes.pdf',
|
||||
'mimetype' => 'pdf',
|
||||
],
|
||||
[
|
||||
'name' => 'Personal Savings',
|
||||
'basename' => 'School Report.pages',
|
||||
'mimetype' => 'pages',
|
||||
],
|
||||
[
|
||||
'name' => 'Top Secret Files',
|
||||
'basename' => 'Stories of the Night Skies.pages',
|
||||
'mimetype' => 'pages',
|
||||
],
|
||||
])
|
||||
->each(function ($file) use ($user) {
|
||||
$basename = Str::random(12) . '-' . $file['basename'];
|
||||
|
||||
// Copy file into app storage
|
||||
Storage::putFileAs("files/$user->id", storage_path("demo/documents/{$file['basename']}"), $basename, 'private');
|
||||
|
||||
// Create file record
|
||||
File::create([
|
||||
'folder_id' => null,
|
||||
'user_id' => $user->id,
|
||||
'name' => $file['name'],
|
||||
'basename' => $basename,
|
||||
'type' => 'file',
|
||||
'author' => 'user',
|
||||
'mimetype' => $file['mimetype'],
|
||||
'filesize' => rand(1000000, 4000000),
|
||||
'created_at' => now()->subMinutes(rand(1, 5)),
|
||||
]);
|
||||
});
|
||||
|
||||
// Get documents to documents folder
|
||||
collect([
|
||||
[
|
||||
'name' => 'Home Improvement',
|
||||
'basename' => 'Licence.pdf',
|
||||
'mimetype' => 'pdf',
|
||||
],
|
||||
[
|
||||
'name' => 'Project Notes',
|
||||
'basename' => 'Project Notes.pdf',
|
||||
'mimetype' => 'pdf',
|
||||
],
|
||||
[
|
||||
'name' => 'Personal Savings',
|
||||
'basename' => 'School Report.pages',
|
||||
'mimetype' => 'pages',
|
||||
],
|
||||
[
|
||||
'name' => 'License',
|
||||
'basename' => 'Stories of the Night Skies.pages',
|
||||
'mimetype' => 'pages',
|
||||
],
|
||||
])
|
||||
->each(function ($file) use ($user, $documents) {
|
||||
$basename = Str::random(12) . '-' . $file['basename'];
|
||||
|
||||
// Copy file into app storage
|
||||
Storage::putFileAs("files/$user->id", storage_path("demo/documents/{$file['basename']}"), $basename, 'private');
|
||||
|
||||
// Create file record
|
||||
File::create([
|
||||
'folder_id' => $documents->id,
|
||||
'user_id' => $user->id,
|
||||
'name' => $file['name'],
|
||||
'basename' => $basename,
|
||||
'type' => 'file',
|
||||
'author' => 'user',
|
||||
'mimetype' => $file['mimetype'],
|
||||
'filesize' => rand(1000000, 4000000),
|
||||
'created_at' => now()->subMinutes(rand(1, 5)),
|
||||
]);
|
||||
});
|
||||
|
||||
// Get documents to shared folder
|
||||
collect([
|
||||
[
|
||||
'name' => 'Home plan',
|
||||
'basename' => 'Licence.pdf',
|
||||
'mimetype' => 'pdf',
|
||||
],
|
||||
[
|
||||
'name' => 'Software Licence',
|
||||
'basename' => 'Project Notes.pdf',
|
||||
'mimetype' => 'pdf',
|
||||
],
|
||||
])
|
||||
->each(function ($file) use ($user, $shared_folder) {
|
||||
$basename = Str::random(12) . '-' . $file['basename'];
|
||||
|
||||
// Copy file into app storage
|
||||
Storage::putFileAs("files/$user->id", storage_path("demo/documents/{$file['basename']}"), $basename, 'private');
|
||||
|
||||
// Create file record
|
||||
File::create([
|
||||
'folder_id' => $shared_folder->id,
|
||||
'user_id' => $user->id,
|
||||
'name' => $file['name'],
|
||||
'basename' => $basename,
|
||||
'type' => 'file',
|
||||
'author' => 'user',
|
||||
'mimetype' => $file['mimetype'],
|
||||
'filesize' => rand(1000000, 4000000),
|
||||
'created_at' => now()->subMinutes(rand(1, 5)),
|
||||
]);
|
||||
});
|
||||
|
||||
// Get documents to peter's files folder
|
||||
collect([
|
||||
[
|
||||
'name' => 'Project Backup',
|
||||
'basename' => 'Licence.pdf',
|
||||
'mimetype' => 'pdf',
|
||||
],
|
||||
[
|
||||
'name' => 'Yearly report',
|
||||
'basename' => 'Project Notes.pdf',
|
||||
'mimetype' => 'pdf',
|
||||
],
|
||||
[
|
||||
'name' => 'Work Update',
|
||||
'basename' => 'School Report.pages',
|
||||
'mimetype' => 'pages',
|
||||
],
|
||||
[
|
||||
'name' => 'Person Writing on Notebook',
|
||||
'basename' => 'Stories of the Night Skies.pages',
|
||||
'mimetype' => 'pages',
|
||||
],
|
||||
[
|
||||
'name' => 'Blank Business Composition Computer',
|
||||
'basename' => 'Licence.pdf',
|
||||
'mimetype' => 'pdf',
|
||||
],
|
||||
[
|
||||
'name' => '2020 April - Export',
|
||||
'basename' => 'Project Notes.pdf',
|
||||
'mimetype' => 'pdf',
|
||||
],
|
||||
[
|
||||
'name' => 'Ballpen Blur Close Up Computer',
|
||||
'basename' => 'School Report.pages',
|
||||
'mimetype' => 'pages',
|
||||
],
|
||||
])
|
||||
->each(function ($file) use ($user, $peters_files) {
|
||||
$basename = Str::random(12) . '-' . $file['basename'];
|
||||
|
||||
// Copy file into app storage
|
||||
Storage::putFileAs("files/$user->id", storage_path("demo/documents/{$file['basename']}"), $basename, 'private');
|
||||
|
||||
// Create file record
|
||||
File::create([
|
||||
'folder_id' => $peters_files->id,
|
||||
'user_id' => $user->id,
|
||||
'name' => $file['name'],
|
||||
'basename' => $basename,
|
||||
'type' => 'file',
|
||||
'author' => 'visitor',
|
||||
'mimetype' => $file['mimetype'],
|
||||
'filesize' => rand(1000000, 4000000),
|
||||
'created_at' => now()->subMinutes(rand(1, 5)),
|
||||
]);
|
||||
});
|
||||
|
||||
// Get videos
|
||||
collect([
|
||||
'Apple Watch App Video Promotion.mp4',
|
||||
'Professional 3D Device Pack for Element 3D.mp4',
|
||||
'Smart Watch 3D Device Pack for Element 3D.mp4',
|
||||
'Sphere Bound 3D Titles.mp4',
|
||||
])
|
||||
->each(function ($file) use ($user, $videohive) {
|
||||
$basename = Str::random(12) . '-' . $file;
|
||||
|
||||
// Copy file into app storage
|
||||
Storage::putFileAs("files/$user->id", storage_path("demo/video/$file"), $basename, 'private');
|
||||
|
||||
// Create file record
|
||||
File::create([
|
||||
'folder_id' => $videohive->id,
|
||||
'user_id' => $user->id,
|
||||
'name' => $file,
|
||||
'basename' => $basename,
|
||||
'type' => 'video',
|
||||
'author' => 'user',
|
||||
'mimetype' => 'mp4',
|
||||
'filesize' => rand(1000000, 4000000),
|
||||
'created_at' => now()->subMinutes(rand(1, 5)),
|
||||
]);
|
||||
});
|
||||
|
||||
// Get video into video folder
|
||||
collect([
|
||||
'Apple Watch App Video Promotion.mp4',
|
||||
])
|
||||
->each(function ($file) use ($user, $video) {
|
||||
$basename = Str::random(12) . '-' . $file;
|
||||
|
||||
// Copy file into app storage
|
||||
Storage::putFileAs("files/$user->id", storage_path("demo/video/$file"), $basename, 'private');
|
||||
|
||||
// Create file record
|
||||
File::create([
|
||||
'folder_id' => $video->id,
|
||||
'user_id' => $user->id,
|
||||
'name' => $file,
|
||||
'basename' => $basename,
|
||||
'type' => 'video',
|
||||
'author' => 'user',
|
||||
'mimetype' => 'mp4',
|
||||
'filesize' => rand(1000000, 4000000),
|
||||
'created_at' => now()->subMinutes(rand(1, 5)),
|
||||
]);
|
||||
});
|
||||
|
||||
// Get audios
|
||||
collect([
|
||||
'D-Block & S-te-Fan - Bla Bla.mp3',
|
||||
])
|
||||
->each(function ($file) use ($user, $audio) {
|
||||
$basename = Str::random(12) . '-' . $file;
|
||||
|
||||
// Copy file into app storage
|
||||
Storage::putFileAs("files/$user->id", storage_path("demo/audio/$file"), $basename, 'private');
|
||||
|
||||
// Create file record
|
||||
File::create([
|
||||
'folder_id' => $audio->id,
|
||||
'user_id' => $user->id,
|
||||
'name' => $file,
|
||||
'basename' => $basename,
|
||||
'type' => 'audio',
|
||||
'author' => 'user',
|
||||
'mimetype' => 'mp3',
|
||||
'filesize' => rand(1000000, 4000000),
|
||||
'created_at' => now()->subMinutes(rand(1, 5)),
|
||||
]);
|
||||
});
|
||||
|
||||
// Get meme gallery
|
||||
collect([
|
||||
'Eggcited bro.jpg',
|
||||
'Get a Rest.jpg',
|
||||
'Get Your Shit Together.jpg',
|
||||
'Happiness is when you are right beside me.jpg',
|
||||
'Have a Nice Day.jpg',
|
||||
'It Works On My Machine.jpg',
|
||||
'I am Just Trying to shine.jpg',
|
||||
'It Works On My Machine.jpg',
|
||||
'Missing you It is Pig Time.jpg',
|
||||
'Sofishticated.jpg',
|
||||
'whaaaaat.jpg',
|
||||
'You Are My Sunshine.jpg',
|
||||
])
|
||||
->each(function ($file) use ($user) {
|
||||
$basename = Str::random(12) . '-' . $file;
|
||||
|
||||
// Copy file into app storage
|
||||
Storage::putFileAs("files/$user->id", storage_path("demo/images/memes/$file"), $basename, 'private');
|
||||
Storage::putFileAs("files/$user->id", storage_path("demo/images/memes/thumbnail-$file"), "thumbnail-$basename", 'private');
|
||||
|
||||
// Create file record
|
||||
File::create([
|
||||
'folder_id' => null,
|
||||
'user_id' => $user->id,
|
||||
'name' => $file,
|
||||
'basename' => $basename,
|
||||
'type' => 'image',
|
||||
'author' => 'user',
|
||||
'mimetype' => 'jpg',
|
||||
'filesize' => rand(1000000, 4000000),
|
||||
'thumbnail' => "thumbnail-$basename",
|
||||
'created_at' => now()->subMinutes(rand(1, 5)),
|
||||
]);
|
||||
});
|
||||
|
||||
// Get apartments gallery
|
||||
collect([
|
||||
'Apartment Architecture Ceiling Chairs.jpg',
|
||||
'Apartment Chair.jpg',
|
||||
'Apartment Contemporary Couch Curtains.jpg',
|
||||
'Brown Wooden Center Table.jpg',
|
||||
'Home.jpg',
|
||||
'Kitchen Appliances.jpg',
|
||||
'Kitchen Island.jpg',
|
||||
])
|
||||
->each(function ($file) use ($user, $apartments) {
|
||||
$basename = Str::random(12) . '-' . $file;
|
||||
|
||||
// Copy file into app storage
|
||||
Storage::putFileAs("files/$user->id", storage_path("demo/images/apartments/$file"), $basename, 'private');
|
||||
Storage::putFileAs("files/$user->id", storage_path("demo/images/apartments/thumbnail-$file"), "thumbnail-$basename", 'private');
|
||||
|
||||
// Create file record
|
||||
File::create([
|
||||
'folder_id' => $apartments->id,
|
||||
'user_id' => $user->id,
|
||||
'name' => $file,
|
||||
'basename' => $basename,
|
||||
'type' => 'image',
|
||||
'author' => 'user',
|
||||
'mimetype' => 'jpg',
|
||||
'filesize' => rand(1000000, 4000000),
|
||||
'thumbnail' => "thumbnail-$basename",
|
||||
'created_at' => now()->subMinutes(rand(1, 5)),
|
||||
]);
|
||||
});
|
||||
|
||||
// Get nature gallery
|
||||
collect([
|
||||
'Bird Patterncolorful Green.jpg',
|
||||
'Close Up Of Peacock.jpg',
|
||||
'Close Up Photography Of Tiger.jpg',
|
||||
'Cold Nature Cute Ice.jpg',
|
||||
'Landscape Photo of Forest.jpg',
|
||||
'Photo of Hawksbill Sea Turtle.jpg',
|
||||
'Photo Of Reindeer in The Snow.jpg',
|
||||
'View Of Elephant in Water.jpg',
|
||||
'Waterfall Between Trees.jpg',
|
||||
'Wildlife Photography of Elephant During Golden Hour.jpg',
|
||||
'Yellow Animal Eyes Fur.jpg',
|
||||
])
|
||||
->each(function ($file) use ($user, $nature) {
|
||||
$basename = Str::random(12) . '-' . $file;
|
||||
|
||||
// Copy file into app storage
|
||||
Storage::putFileAs("files/$user->id", storage_path("demo/images/nature/$file"), $basename, 'private');
|
||||
Storage::putFileAs("files/$user->id", storage_path("demo/images/nature/thumbnail-$file"), "thumbnail-$basename", 'private');
|
||||
|
||||
// Create file record
|
||||
File::create([
|
||||
'folder_id' => $nature->id,
|
||||
'user_id' => $user->id,
|
||||
'name' => $file,
|
||||
'basename' => $basename,
|
||||
'type' => 'image',
|
||||
'author' => 'user',
|
||||
'mimetype' => 'jpg',
|
||||
'filesize' => rand(1000000, 4000000),
|
||||
'thumbnail' => "thumbnail-$basename",
|
||||
'created_at' => now()->subMinutes(rand(1, 5)),
|
||||
]);
|
||||
});
|
||||
}
|
||||
|
||||
private function create_share_records(): void
|
||||
{
|
||||
$user = User::whereEmail('howdy@hi5ve.digital')
|
||||
->first();
|
||||
|
||||
$images = File::whereType('image')
|
||||
->whereFolderId(null)
|
||||
->take(3)
|
||||
->pluck('id');
|
||||
|
||||
$images->each(function ($id) use ($user) {
|
||||
Share::create([
|
||||
'user_id' => $user->id,
|
||||
'item_id' => $id,
|
||||
'type' => 'file',
|
||||
'is_protected' => false,
|
||||
'permission' => 'editor',
|
||||
'password' => null,
|
||||
'expire_in' => null,
|
||||
]);
|
||||
});
|
||||
|
||||
$files = File::whereType('file')
|
||||
->whereFolderId(null)
|
||||
->take(2)
|
||||
->pluck('id');
|
||||
|
||||
$files->each(function ($id) use ($user) {
|
||||
Share::create([
|
||||
'user_id' => $user->id,
|
||||
'item_id' => $id,
|
||||
'type' => 'file',
|
||||
'is_protected' => false,
|
||||
'permission' => 'editor',
|
||||
'password' => null,
|
||||
'expire_in' => null,
|
||||
]);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Store main app settings into database
|
||||
*/
|
||||
private function store_default_settings(): void
|
||||
{
|
||||
// Get options
|
||||
collect([
|
||||
[
|
||||
'name' => 'setup_wizard_database',
|
||||
'value' => 1,
|
||||
],
|
||||
[
|
||||
'name' => 'app_title',
|
||||
'value' => 'VueFileManager',
|
||||
],
|
||||
[
|
||||
'name' => 'app_description',
|
||||
'value' => 'Your self-hosted storage cloud software powered by Laravel and Vue',
|
||||
],
|
||||
[
|
||||
'name' => 'app_logo',
|
||||
'value' => 'system/logo.svg',
|
||||
],
|
||||
[
|
||||
'name' => 'app_logo_horizontal',
|
||||
'value' => 'system/logo-horizontal.svg',
|
||||
],
|
||||
[
|
||||
'name' => 'app_favicon',
|
||||
'value' => 'system/favicon.png',
|
||||
],
|
||||
[
|
||||
'name' => 'app_og_image',
|
||||
'value' => 'system/og-image.jpg',
|
||||
],
|
||||
[
|
||||
'name' => 'app_touch_icon',
|
||||
'value' => 'system/touch-icon.png',
|
||||
],
|
||||
[
|
||||
'name' => 'google_analytics',
|
||||
'value' => '',
|
||||
],
|
||||
[
|
||||
'name' => 'contact_email',
|
||||
'value' => '',
|
||||
],
|
||||
[
|
||||
'name' => 'registration',
|
||||
'value' => 1,
|
||||
],
|
||||
[
|
||||
'name' => 'user_verification',
|
||||
'value' => 1,
|
||||
],
|
||||
[
|
||||
'name' => 'payments_active',
|
||||
'value' => 1,
|
||||
],
|
||||
[
|
||||
'name' => 'storage_limitation',
|
||||
'value' => 1,
|
||||
],
|
||||
[
|
||||
'name' => 'storage_default',
|
||||
'value' => 5,
|
||||
],
|
||||
[
|
||||
'name' => 'setup_wizard_success',
|
||||
'value' => 1,
|
||||
],
|
||||
[
|
||||
'name' => 'license',
|
||||
'value' => $this->license,
|
||||
],
|
||||
[
|
||||
'name' => 'purchase_code',
|
||||
'value' => '26b889eb-3602-4bf2-beb3-3sc378fcf484',
|
||||
],
|
||||
[
|
||||
'name' => 'billing_address',
|
||||
'value' => 'Palo Alto 20',
|
||||
],
|
||||
[
|
||||
'name' => 'billing_city',
|
||||
'value' => 'Palo Alto',
|
||||
],
|
||||
[
|
||||
'name' => 'billing_country',
|
||||
'value' => 'US',
|
||||
],
|
||||
[
|
||||
'name' => 'billing_name',
|
||||
'value' => 'VueFileManager Inc.',
|
||||
],
|
||||
[
|
||||
'name' => 'billing_phone_number',
|
||||
'value' => '312343141243214',
|
||||
],
|
||||
[
|
||||
'name' => 'billing_postal_code',
|
||||
'value' => '43213',
|
||||
],
|
||||
[
|
||||
'name' => 'billing_state',
|
||||
'value' => 'California',
|
||||
],
|
||||
[
|
||||
'name' => 'billing_vat_number',
|
||||
'value' => '41241241234',
|
||||
],
|
||||
])->each(function ($col) {
|
||||
Setting::forceCreate([
|
||||
'name' => $col['name'],
|
||||
'value' => $col['value'],
|
||||
]);
|
||||
});
|
||||
|
||||
// Get system images
|
||||
collect(['logo.svg', 'logo-horizontal.svg', 'favicon.png', 'og-image.jpg', 'touch-icon.png'])
|
||||
->each(function ($file) {
|
||||
Storage::putFileAs('system', storage_path("demo/app/$file"), $file, 'private');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate database and generate application keys
|
||||
*/
|
||||
private function migrate_and_generate(): void
|
||||
{
|
||||
// Migrate database
|
||||
$this->call('migrate:fresh', [
|
||||
'--force' => true,
|
||||
]);
|
||||
|
||||
// Generate app key
|
||||
$this->call('key:generate', [
|
||||
'--force' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear app cache
|
||||
*/
|
||||
private function clear_cache(): void
|
||||
{
|
||||
$this->call('cache:clear');
|
||||
$this->call('config:clear');
|
||||
$this->call('view:clear');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,227 @@
|
||||
<?php
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Models\User;
|
||||
use App\Models\Setting;
|
||||
use App\Services\SetupService;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class SetupProdEnvironment extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'setup:prod';
|
||||
protected $license = 'Extended';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Set up production environment';
|
||||
|
||||
private $setup;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->setup = resolve(SetupService::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$this->info('Setting up production environment');
|
||||
|
||||
$this->info('Creating system directories...');
|
||||
$this->setup->create_directories();
|
||||
|
||||
$this->info('Migrating Databases...');
|
||||
$this->migrate_and_generate();
|
||||
|
||||
$this->info('Storing default settings and content...');
|
||||
$this->store_default_settings();
|
||||
$this->setup->seed_default_pages();
|
||||
$this->setup->seed_default_settings($this->license);
|
||||
$this->setup->seed_default_language();
|
||||
|
||||
$this->info('Creating default admin...');
|
||||
$this->create_admin();
|
||||
|
||||
$this->info('Clearing application cache...');
|
||||
$this->clear_cache();
|
||||
|
||||
$this->info('Everything is done, congratulations! 🥳🥳🥳');
|
||||
}
|
||||
|
||||
/**
|
||||
* Store main app settings into database
|
||||
*/
|
||||
private function store_default_settings(): void
|
||||
{
|
||||
// Get options
|
||||
collect([
|
||||
[
|
||||
'name' => 'setup_wizard_database',
|
||||
'value' => 1,
|
||||
],
|
||||
[
|
||||
'name' => 'app_title',
|
||||
'value' => 'VueFileManager',
|
||||
],
|
||||
[
|
||||
'name' => 'app_description',
|
||||
'value' => 'Your self-hosted storage cloud software powered by Laravel and Vue',
|
||||
],
|
||||
[
|
||||
'name' => 'app_logo',
|
||||
'value' => null,
|
||||
],
|
||||
[
|
||||
'name' => 'app_logo_horizontal',
|
||||
'value' => null,
|
||||
],
|
||||
[
|
||||
'name' => 'app_favicon',
|
||||
'value' => null,
|
||||
],
|
||||
[
|
||||
'name' => 'app_og_image',
|
||||
'value' => null,
|
||||
],
|
||||
[
|
||||
'name' => 'app_touch_icon',
|
||||
'value' => null,
|
||||
],
|
||||
[
|
||||
'name' => 'google_analytics',
|
||||
'value' => null,
|
||||
],
|
||||
[
|
||||
'name' => 'contact_email',
|
||||
'value' => null,
|
||||
],
|
||||
[
|
||||
'name' => 'registration',
|
||||
'value' => 0,
|
||||
],
|
||||
[
|
||||
'name' => 'user_verification',
|
||||
'value' => 1,
|
||||
],
|
||||
[
|
||||
'name' => 'storage_limitation',
|
||||
'value' => 1,
|
||||
],
|
||||
[
|
||||
'name' => 'storage_default',
|
||||
'value' => 5,
|
||||
],
|
||||
[
|
||||
'name' => 'setup_wizard_success',
|
||||
'value' => 1,
|
||||
],
|
||||
[
|
||||
'name' => 'license',
|
||||
'value' => $this->license,
|
||||
],
|
||||
[
|
||||
'name' => 'purchase_code',
|
||||
'value' => '26b889eb-3602-4bf2-beb3-3sc378fcf484',
|
||||
],
|
||||
[
|
||||
'name' => 'billing_address',
|
||||
'value' => null,
|
||||
],
|
||||
[
|
||||
'name' => 'billing_city',
|
||||
'value' => null,
|
||||
],
|
||||
[
|
||||
'name' => 'billing_country',
|
||||
'value' => null,
|
||||
],
|
||||
[
|
||||
'name' => 'billing_name',
|
||||
'value' => null,
|
||||
],
|
||||
[
|
||||
'name' => 'billing_phone_number',
|
||||
'value' => null,
|
||||
],
|
||||
[
|
||||
'name' => 'billing_postal_code',
|
||||
'value' => null,
|
||||
],
|
||||
[
|
||||
'name' => 'billing_state',
|
||||
'value' => null,
|
||||
],
|
||||
[
|
||||
'name' => 'billing_vat_number',
|
||||
'value' => null,
|
||||
],
|
||||
])->each(function ($col) {
|
||||
Setting::forceCreate([
|
||||
'name' => $col['name'],
|
||||
'value' => $col['value'],
|
||||
]);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Create default admin account
|
||||
*/
|
||||
private function create_admin(): void
|
||||
{
|
||||
$user = User::forceCreate([
|
||||
'role' => 'admin',
|
||||
'email' => 'howdy@hi5ve.digital',
|
||||
'password' => bcrypt('vuefilemanager'),
|
||||
'email_verified_at' => now(),
|
||||
]);
|
||||
|
||||
$user
|
||||
->settings()
|
||||
->create([
|
||||
'storage_capacity' => 5,
|
||||
'name' => 'Admin',
|
||||
]);
|
||||
|
||||
// Show user credentials
|
||||
$this->info('Default admin account created. Email: howdy@hi5ve.digital and Password: vuefilemanager');
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate database and generate application keys
|
||||
*/
|
||||
private function migrate_and_generate(): void
|
||||
{
|
||||
// Migrate database
|
||||
$this->call('migrate:fresh', [
|
||||
'--force' => true,
|
||||
]);
|
||||
|
||||
// Generate app key
|
||||
$this->call('key:generate', [
|
||||
'--force' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear app cache
|
||||
*/
|
||||
private function clear_cache(): void
|
||||
{
|
||||
$this->call('cache:clear');
|
||||
$this->call('config:clear');
|
||||
$this->call('view:clear');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
namespace App\Console;
|
||||
|
||||
use App\Services\SchedulerService;
|
||||
use Illuminate\Console\Scheduling\Schedule;
|
||||
use App\Console\Commands\SetupDevEnvironment;
|
||||
use App\Console\Commands\SetupProdEnvironment;
|
||||
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
|
||||
|
||||
class Kernel extends ConsoleKernel
|
||||
{
|
||||
/**
|
||||
* The Artisan commands provided by your application.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $commands = [
|
||||
SetupDevEnvironment::class,
|
||||
SetupProdEnvironment::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* Define the application's command schedule.
|
||||
*
|
||||
* @param \Illuminate\Console\Scheduling\Schedule $schedule
|
||||
* @return void
|
||||
*/
|
||||
protected function schedule(Schedule $schedule)
|
||||
{
|
||||
$scheduler = resolve(SchedulerService::class);
|
||||
|
||||
$schedule->call(function () use ($scheduler) {
|
||||
$scheduler->delete_expired_shared_links();
|
||||
})->everyTenMinutes();
|
||||
|
||||
$schedule->call(function () use ($scheduler) {
|
||||
$scheduler->delete_old_zips();
|
||||
|
||||
if (! is_storage_driver(['local'])) {
|
||||
$scheduler->delete_failed_files();
|
||||
}
|
||||
})->everySixHours();
|
||||
|
||||
$schedule->call(function () use ($scheduler) {
|
||||
$scheduler->delete_unverified_users();
|
||||
})->daily();
|
||||
|
||||
// Run queue jobs every minute
|
||||
$schedule->command('queue:work --stop-when-empty')
|
||||
->everyMinute()
|
||||
->withoutOverlapping();
|
||||
|
||||
// Backup app database daily
|
||||
$schedule->command('backup:clean')
|
||||
->daily()
|
||||
->at('01:00');
|
||||
$schedule->command('backup:run --only-db')
|
||||
->daily()
|
||||
->at('01:30');
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the commands for the application.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function commands()
|
||||
{
|
||||
$this->load(__DIR__ . '/Commands');
|
||||
|
||||
require base_path('routes/console.php');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Throwable;
|
||||
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
|
||||
|
||||
class Handler extends ExceptionHandler
|
||||
{
|
||||
/**
|
||||
* A list of the exception types that are not reported.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $dontReport = [
|
||||
];
|
||||
|
||||
/**
|
||||
* A list of the inputs that are never flashed for validation exceptions.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $dontFlash = [
|
||||
'password',
|
||||
'password_confirmation',
|
||||
];
|
||||
|
||||
/**
|
||||
* Report or log an exception.
|
||||
*
|
||||
* @param \Throwable $exception
|
||||
* @return void
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function report(Throwable $exception)
|
||||
{
|
||||
parent::report($exception);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render an exception into an HTTP response.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Throwable $exception
|
||||
* @return \Symfony\Component\HttpFoundation\Response
|
||||
*
|
||||
* @throws \Throwable
|
||||
*/
|
||||
public function render($request, Throwable $exception)
|
||||
{
|
||||
if ($exception instanceof ModelNotFoundException) {
|
||||
return response()
|
||||
->redirectTo('/not-found')->setStatusCode(404);
|
||||
}
|
||||
|
||||
return parent::render($request, $exception);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Illuminate\Foundation\Bus\DispatchesJobs;
|
||||
use Illuminate\Routing\Controller as BaseController;
|
||||
use Illuminate\Foundation\Validation\ValidatesRequests;
|
||||
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
||||
|
||||
class Controller extends BaseController
|
||||
{
|
||||
use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
namespace App\Http;
|
||||
|
||||
use Illuminate\Foundation\Http\Kernel as HttpKernel;
|
||||
use Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful;
|
||||
|
||||
class Kernel extends HttpKernel
|
||||
{
|
||||
/**
|
||||
* The application's global HTTP middleware stack.
|
||||
*
|
||||
* These middleware are run during every request to your application.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $middleware = [
|
||||
// \App\Http\Middleware\TrustHosts::class,
|
||||
\App\Http\Middleware\TrustProxies::class,
|
||||
\Fruitcake\Cors\HandleCors::class,
|
||||
\App\Http\Middleware\PreventRequestsDuringMaintenance::class,
|
||||
\Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
|
||||
\App\Http\Middleware\TrimStrings::class,
|
||||
\Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* The application's route middleware groups.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $middlewareGroups = [
|
||||
'web' => [
|
||||
\App\Http\Middleware\EncryptCookies::class,
|
||||
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
|
||||
\Illuminate\Session\Middleware\StartSession::class,
|
||||
// \Illuminate\Session\Middleware\AuthenticateSession::class,
|
||||
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
|
||||
\App\Http\Middleware\VerifyCsrfToken::class,
|
||||
\Illuminate\Routing\Middleware\SubstituteBindings::class,
|
||||
],
|
||||
|
||||
'api' => [
|
||||
EnsureFrontendRequestsAreStateful::class,
|
||||
//'throttle:api',
|
||||
//\App\Http\Middleware\EncryptCookies::class,
|
||||
\Illuminate\Routing\Middleware\SubstituteBindings::class,
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* The application's route middleware.
|
||||
*
|
||||
* These middleware may be assigned to groups or used individually.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $routeMiddleware = [
|
||||
'auth' => \App\Http\Middleware\Authenticate::class,
|
||||
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
|
||||
'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
|
||||
'can' => \Illuminate\Auth\Middleware\Authorize::class,
|
||||
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
|
||||
'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
|
||||
'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
|
||||
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
|
||||
'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
namespace App\Providers;
|
||||
|
||||
use Illuminate\Support\Facades\App;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
|
||||
class AppServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* Register any application services.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Bootstrap any application services.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
try {
|
||||
$app_locale = get_setting('language') ?? 'en';
|
||||
} catch (\PDOException $exception) {
|
||||
$app_locale = 'en';
|
||||
}
|
||||
|
||||
// Set locale for application
|
||||
app()->setLocale($app_locale);
|
||||
|
||||
// Set locale for carbon dates
|
||||
setlocale(LC_TIME, $app_locale . '_' . mb_strtoupper($app_locale));
|
||||
|
||||
// Get all migrations with all directories
|
||||
$this->loadMigrationsFrom(
|
||||
$this->get_migration_paths()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
private function get_migration_paths(): array
|
||||
{
|
||||
$mainPath = database_path('migrations');
|
||||
$directories = glob($mainPath . '/*', GLOB_ONLYDIR);
|
||||
|
||||
return array_merge([$mainPath], $directories);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
namespace App\Providers;
|
||||
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
|
||||
|
||||
//use Laravel\Passport\Passport;
|
||||
|
||||
class AuthServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* The policy mappings for the application.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $policies = [
|
||||
// 'App\Model' => 'App\Policies\ModelPolicy',
|
||||
];
|
||||
|
||||
/**
|
||||
* Register any authentication / authorization services.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
$this->registerPolicies();
|
||||
|
||||
// Define admin maintenance gate
|
||||
Gate::define('maintenance', function ($user) {
|
||||
return $user->role === 'admin';
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
namespace App\Providers;
|
||||
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use Illuminate\Support\Facades\Broadcast;
|
||||
|
||||
class BroadcastServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* Bootstrap any application services.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
Broadcast::routes();
|
||||
|
||||
require base_path('routes/channels.php');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
namespace App\Providers;
|
||||
|
||||
use Illuminate\Support\Facades\Event;
|
||||
use Illuminate\Auth\Events\Registered;
|
||||
use Illuminate\Auth\Listeners\SendEmailVerificationNotification;
|
||||
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
|
||||
|
||||
class EventServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* The event listener mappings for the application.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $listen = [
|
||||
Registered::class => [
|
||||
SendEmailVerificationNotification::class,
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* Register any events for your application.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
parent::boot();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
namespace App\Providers;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Laravel\Fortify\Fortify;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use Illuminate\Cache\RateLimiting\Limit;
|
||||
use App\Actions\Fortify\ResetUserPassword;
|
||||
use App\Actions\Fortify\UpdateUserPassword;
|
||||
use Illuminate\Support\Facades\RateLimiter;
|
||||
use App\Actions\Fortify\UpdateUserProfileInformation;
|
||||
|
||||
class FortifyServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* Register any application services.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Bootstrap any application services.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
Fortify::updateUserProfileInformationUsing(UpdateUserProfileInformation::class);
|
||||
Fortify::updateUserPasswordsUsing(UpdateUserPassword::class);
|
||||
Fortify::resetUserPasswordsUsing(ResetUserPassword::class);
|
||||
|
||||
RateLimiter::for('login', function (Request $request) {
|
||||
return Limit::perMinute(20)->by($request->email.$request->ip());
|
||||
});
|
||||
|
||||
RateLimiter::for('two-factor', function (Request $request) {
|
||||
return Limit::perMinute(5)->by($request->session()->get('login.id'));
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
<?php
|
||||
namespace App\Providers;
|
||||
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
|
||||
|
||||
class RouteServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* This namespace is applied to your controller routes.
|
||||
*
|
||||
* In addition, it is set as the URL generator's root namespace.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $namespace = null;
|
||||
|
||||
/**
|
||||
* The path to the "home" route for your application.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public const HOME = '/home';
|
||||
|
||||
/**
|
||||
* Define the routes for the application.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function map()
|
||||
{
|
||||
$this->mapApiRoutes();
|
||||
|
||||
$this->mapShareRoutes();
|
||||
|
||||
$this->mapAdminApiRoutes();
|
||||
|
||||
$this->mapSetupWizardApiRoutes();
|
||||
|
||||
$this->mapUserApiRoutes();
|
||||
|
||||
$this->mapMaintenanceRoutes();
|
||||
|
||||
$this->mapFileRoutes();
|
||||
|
||||
$this->mapWebRoutes();
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the "web" routes for the application.
|
||||
*
|
||||
* These routes all receive session state, CSRF protection, etc.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function mapWebRoutes()
|
||||
{
|
||||
Route::middleware('web')
|
||||
->namespace($this->namespace)
|
||||
->group(base_path('routes/web.php'));
|
||||
}
|
||||
|
||||
protected function mapMaintenanceRoutes()
|
||||
{
|
||||
Route::middleware('web')
|
||||
->namespace($this->namespace)
|
||||
->group(base_path('routes/maintenance.php'));
|
||||
}
|
||||
|
||||
protected function mapFileRoutes()
|
||||
{
|
||||
Route::middleware('web')
|
||||
->namespace($this->namespace)
|
||||
->group(base_path('routes/file.php'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the "api" routes for the application.
|
||||
*
|
||||
* These routes are typically stateless.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function mapApiRoutes()
|
||||
{
|
||||
Route::prefix('api')
|
||||
->middleware('api')
|
||||
->namespace($this->namespace)
|
||||
->group(base_path('routes/api.php'));
|
||||
}
|
||||
|
||||
protected function mapShareRoutes()
|
||||
{
|
||||
Route::prefix('api')
|
||||
->middleware('api')
|
||||
->namespace($this->namespace)
|
||||
->group(base_path('routes/share.php'));
|
||||
}
|
||||
|
||||
protected function mapAdminApiRoutes()
|
||||
{
|
||||
Route::prefix('api/admin')
|
||||
->middleware(['api', 'auth:sanctum'])
|
||||
->namespace($this->namespace)
|
||||
->group(base_path('routes/admin.php'));
|
||||
}
|
||||
|
||||
protected function mapUserApiRoutes()
|
||||
{
|
||||
Route::prefix('api/user')
|
||||
->middleware('api')
|
||||
->namespace($this->namespace)
|
||||
->group(base_path('routes/user.php'));
|
||||
}
|
||||
|
||||
protected function mapSetupWizardApiRoutes()
|
||||
{
|
||||
Route::prefix('api/setup')
|
||||
->middleware('api')
|
||||
->namespace($this->namespace)
|
||||
->group(base_path('routes/setup.php'));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
namespace App\Actions\Fortify;
|
||||
|
||||
use App\Models\User;
|
||||
use App\Models\Setting;
|
||||
use App\Models\UserSettings;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Response;
|
||||
use Illuminate\Validation\Rule;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Auth\Events\Registered;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Illuminate\Contracts\Auth\StatefulGuard;
|
||||
|
||||
class CreateNewUserAction extends Controller
|
||||
{
|
||||
use PasswordValidationRules;
|
||||
|
||||
public function __construct(
|
||||
protected StatefulGuard $guard
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate and create a newly registered user.
|
||||
*/
|
||||
public function __invoke(Request $request): Response
|
||||
{
|
||||
$settings = Setting::whereIn('name', [
|
||||
'storage_default', 'registration',
|
||||
])
|
||||
->pluck('value', 'name');
|
||||
|
||||
// Check if account registration is enabled
|
||||
if (! intval($settings['registration'])) {
|
||||
abort(401);
|
||||
}
|
||||
|
||||
Validator::make($request->all(), [
|
||||
'name' => ['required', 'string', 'max:255'],
|
||||
'email' => [
|
||||
'required',
|
||||
'string',
|
||||
'email',
|
||||
'max:255',
|
||||
Rule::unique(User::class),
|
||||
],
|
||||
'password' => $this->passwordRules(),
|
||||
])->validate();
|
||||
|
||||
$user = User::create([
|
||||
'email' => $request->email,
|
||||
'password' => bcrypt($request->password),
|
||||
]);
|
||||
|
||||
UserSettings::unguard();
|
||||
|
||||
$user
|
||||
->settings()
|
||||
->create([
|
||||
'name' => $request->name,
|
||||
'storage_capacity' => $settings['storage_default'],
|
||||
]);
|
||||
|
||||
if (! get_setting('user_verification')) {
|
||||
$user->markEmailAsVerified();
|
||||
}
|
||||
|
||||
UserSettings::reguard();
|
||||
|
||||
event(new Registered($user));
|
||||
|
||||
if (! get_setting('user_verification')) {
|
||||
$this->guard->login($user);
|
||||
}
|
||||
|
||||
return response('User registered successfully', 201);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
namespace App\Actions\Fortify;
|
||||
|
||||
use Laravel\Fortify\Rules\Password;
|
||||
|
||||
trait PasswordValidationRules
|
||||
{
|
||||
/**
|
||||
* Get the validation rules used to validate passwords.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function passwordRules()
|
||||
{
|
||||
return ['required', 'string', new Password, 'confirmed'];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
namespace App\Actions\Fortify;
|
||||
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Laravel\Fortify\Contracts\ResetsUserPasswords;
|
||||
|
||||
class ResetUserPassword implements ResetsUserPasswords
|
||||
{
|
||||
use PasswordValidationRules;
|
||||
|
||||
/**
|
||||
* Validate and reset the user's forgotten password.
|
||||
*
|
||||
* @param mixed $user
|
||||
* @param array $input
|
||||
* @return void
|
||||
*/
|
||||
public function reset($user, array $input)
|
||||
{
|
||||
Validator::make($input, [
|
||||
'password' => $this->passwordRules(),
|
||||
])->validate();
|
||||
|
||||
$user->forceFill([
|
||||
'password' => bcrypt($input['password']),
|
||||
])->save();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
namespace App\Actions\Fortify;
|
||||
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Laravel\Fortify\Contracts\UpdatesUserPasswords;
|
||||
|
||||
class UpdateUserPassword implements UpdatesUserPasswords
|
||||
{
|
||||
use PasswordValidationRules;
|
||||
|
||||
/**
|
||||
* Validate and update the user's password.
|
||||
*
|
||||
* @param mixed $user
|
||||
* @param array $input
|
||||
* @return void
|
||||
* @throws \Illuminate\Validation\ValidationException
|
||||
*/
|
||||
public function update($user, array $input)
|
||||
{
|
||||
Validator::make($input, [
|
||||
'current_password' => ['required', 'string'],
|
||||
'password' => $this->passwordRules(),
|
||||
])->after(function ($validator) use ($user, $input) {
|
||||
if (! isset($input['current_password']) || ! Hash::check($input['current_password'], $user->password)) {
|
||||
$validator->errors()->add('current_password', __('The provided password does not match your current password.'));
|
||||
}
|
||||
})->validateWithBag('updatePassword');
|
||||
|
||||
$user->forceFill([
|
||||
'password' => bcrypt($input['password']),
|
||||
])->save();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
namespace App\Actions\Fortify;
|
||||
|
||||
use Illuminate\Validation\Rule;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Illuminate\Contracts\Auth\MustVerifyEmail;
|
||||
use Laravel\Fortify\Contracts\UpdatesUserProfileInformation;
|
||||
|
||||
class UpdateUserProfileInformation implements UpdatesUserProfileInformation
|
||||
{
|
||||
/**
|
||||
* Validate and update the given user's profile information.
|
||||
*
|
||||
* @param mixed $user
|
||||
* @param array $input
|
||||
* @return void
|
||||
*/
|
||||
public function update($user, array $input)
|
||||
{
|
||||
Validator::make($input, [
|
||||
'name' => ['required', 'string', 'max:255'],
|
||||
|
||||
'email' => [
|
||||
'required',
|
||||
'string',
|
||||
'email',
|
||||
'max:255',
|
||||
Rule::unique('users')->ignore($user->id),
|
||||
],
|
||||
])->validateWithBag('updateProfileInformation');
|
||||
|
||||
if ($input['email'] !== $user->email &&
|
||||
$user instanceof MustVerifyEmail) {
|
||||
$this->updateVerifiedUser($user, $input);
|
||||
} else {
|
||||
$user->forceFill([
|
||||
'name' => $input['name'],
|
||||
'email' => $input['email'],
|
||||
])->save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the given verified user's profile information.
|
||||
*
|
||||
* @param mixed $user
|
||||
* @param array $input
|
||||
* @return void
|
||||
*/
|
||||
protected function updateVerifiedUser($user, array $input)
|
||||
{
|
||||
$user->forceFill([
|
||||
'name' => $input['name'],
|
||||
'email' => $input['email'],
|
||||
'email_verified_at' => null,
|
||||
])->save();
|
||||
|
||||
$user->sendEmailVerificationNotification();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,205 @@
|
||||
<?php
|
||||
namespace App\Http\Controllers\User;
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Services\DemoService;
|
||||
use Illuminate\Http\Response;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Resources\UserResource;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Laravel\Sanctum\PersonalAccessToken;
|
||||
use App\Http\Resources\InvoiceCollection;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use App\Http\Resources\UserStorageResource;
|
||||
use Illuminate\Contracts\Routing\ResponseFactory;
|
||||
use App\Http\Requests\User\UpdateUserPasswordRequest;
|
||||
use App\Http\Requests\User\UserCreateAccessTokenRequest;
|
||||
|
||||
class AccountController extends Controller
|
||||
{
|
||||
/**
|
||||
* AccountController constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->demo = resolve(DemoService::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all user data to frontend
|
||||
*
|
||||
* @return UserResource
|
||||
*/
|
||||
public function user()
|
||||
{
|
||||
return new UserResource(
|
||||
Auth::user()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get storage details
|
||||
*
|
||||
* @return UserStorageResource
|
||||
*/
|
||||
public function storage()
|
||||
{
|
||||
return new UserStorageResource(
|
||||
Auth::user()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user invoices
|
||||
*
|
||||
* @return InvoiceCollection
|
||||
*/
|
||||
public function invoices()
|
||||
{
|
||||
return new InvoiceCollection(
|
||||
Auth::user()->invoices()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update user settings relationship
|
||||
*
|
||||
* @param Request $request
|
||||
* @return ResponseFactory|\Illuminate\Http\Response
|
||||
*/
|
||||
public function update_user_settings(Request $request)
|
||||
{
|
||||
// Validate request
|
||||
// TODO: pridat validator do requestu
|
||||
$validator = Validator::make($request->all(), [
|
||||
'avatar' => 'sometimes|file',
|
||||
'name' => 'string',
|
||||
'value' => 'string',
|
||||
]);
|
||||
|
||||
// Return error
|
||||
if ($validator->fails()) {
|
||||
abort(400, 'Bad input');
|
||||
}
|
||||
|
||||
// Get user
|
||||
$user = Auth::user();
|
||||
|
||||
// Check if is demo
|
||||
abort_if(is_demo_account('howdy@hi5ve.digital'), 204, 'Done.');
|
||||
|
||||
// Update avatar
|
||||
if ($request->hasFile('avatar')) {
|
||||
$user
|
||||
->settings()
|
||||
->update([
|
||||
'avatar' => store_avatar($request, 'avatar'),
|
||||
]);
|
||||
|
||||
return response('Saved!', 204);
|
||||
}
|
||||
|
||||
$user
|
||||
->settings()
|
||||
->update(
|
||||
make_single_input($request)
|
||||
);
|
||||
|
||||
return response('Saved!', 204);
|
||||
}
|
||||
|
||||
/**
|
||||
* Change user password
|
||||
*
|
||||
* @param Request $request
|
||||
* @return ResponseFactory|\Illuminate\Http\Response
|
||||
*/
|
||||
public function change_password(UpdateUserPasswordRequest $request)
|
||||
{
|
||||
// Get user
|
||||
$user = Auth::user();
|
||||
|
||||
// Check if is demo
|
||||
abort_if(is_demo_account('howdy@hi5ve.digital'), 204, 'Done.');
|
||||
|
||||
// Change and store new password
|
||||
$user->password = bcrypt($request->input('password'));
|
||||
$user->save();
|
||||
|
||||
return response('Changed!', 204);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all user tokens
|
||||
*/
|
||||
public function tokens(): Response
|
||||
{
|
||||
return response(
|
||||
Auth::user()->tokens()->get(),
|
||||
200
|
||||
);
|
||||
}
|
||||
|
||||
public function create_token(UserCreateAccessTokenRequest $request): Response
|
||||
{
|
||||
// Check if is demo
|
||||
abort_if(is_demo_account('howdy@hi5ve.digital'), 201, [
|
||||
'name' => 'token',
|
||||
'token' => Str::random(40),
|
||||
'abilities' => '["*"]',
|
||||
'tokenable_id' => Str::uuid(),
|
||||
'updated_at' => now(),
|
||||
'created_at' => now(),
|
||||
'id' => Str::random(40),
|
||||
]);
|
||||
|
||||
$token = Auth::user()->createToken($request->input('name'));
|
||||
|
||||
return response($token, 201);
|
||||
}
|
||||
|
||||
public function revoke_token(PersonalAccessToken $token): Response
|
||||
{
|
||||
// Check if is demo
|
||||
abort_if(is_demo_account('howdy@hi5ve.digital'), 204, 'Deleted!');
|
||||
|
||||
if (Auth::id() !== $token->tokenable_id) {
|
||||
return response('Unauthorized', 401);
|
||||
}
|
||||
|
||||
$token->delete();
|
||||
|
||||
return response('Deleted!', 204);
|
||||
}
|
||||
|
||||
public function email_verification(string $id, Request $request): RedirectResponse | Response
|
||||
{
|
||||
if (! $request->hasValidSignature()) {
|
||||
return response('Invalid or expired url provided.', 401);
|
||||
}
|
||||
|
||||
$user = User::find($id);
|
||||
|
||||
if (! $user->hasVerifiedEmail()) {
|
||||
$user->markEmailAsVerified();
|
||||
}
|
||||
|
||||
return redirect()->to('/successfully-verified');
|
||||
}
|
||||
|
||||
public function resend_verification_email(Request $request): Response
|
||||
{
|
||||
$user = User::whereEmail($request->input('email'))->first();
|
||||
|
||||
if ($user->hasVerifiedEmail()) {
|
||||
return response('Email was already verified.', 204);
|
||||
}
|
||||
|
||||
$user->sendEmailVerificationNotification();
|
||||
|
||||
return response('Email verification link sent to your email', 204);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
namespace App\Http\Controllers\Auth;
|
||||
|
||||
use App\Models\User;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Auth\CheckAccountRequest;
|
||||
|
||||
class AuthController extends Controller
|
||||
{
|
||||
/**
|
||||
* Check if user account exist
|
||||
*
|
||||
* @param CheckAccountRequest $request
|
||||
* @return mixed
|
||||
*/
|
||||
public function check_account(CheckAccountRequest $request)
|
||||
{
|
||||
// Get User
|
||||
$user = User::whereEmail($request->email)
|
||||
->first();
|
||||
|
||||
if (! $user) {
|
||||
return response(__t('user_not_fount'), 404);
|
||||
}
|
||||
|
||||
return [
|
||||
'name' => $user->settings->name,
|
||||
'avatar' => $user->settings->avatar,
|
||||
'verified' => $user->email_verified_at ? true : false,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
namespace App\Http\Controllers\Auth;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Providers\RouteServiceProvider;
|
||||
use Illuminate\Foundation\Auth\ConfirmsPasswords;
|
||||
|
||||
class ConfirmPasswordController extends Controller
|
||||
{
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Confirm Password Controller
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This controller is responsible for handling password confirmations and
|
||||
| uses a simple trait to include the behavior. You're free to explore
|
||||
| this trait and override any functions that require customization.
|
||||
|
|
||||
*/
|
||||
|
||||
use ConfirmsPasswords;
|
||||
|
||||
/**
|
||||
* Where to redirect users when the intended url fails.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $redirectTo = RouteServiceProvider::HOME;
|
||||
|
||||
/**
|
||||
* Create a new controller instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->middleware('auth');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
namespace App\Http\Controllers\Auth;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Support\Facades\Lang;
|
||||
use Illuminate\Foundation\Auth\SendsPasswordResetEmails;
|
||||
|
||||
class ForgotPasswordController extends Controller
|
||||
{
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Password Reset Controller
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This controller is responsible for handling password reset emails and
|
||||
| includes a trait which assists in sending these notifications from
|
||||
| your application to your users. Feel free to explore this trait.
|
||||
|
|
||||
*/
|
||||
|
||||
use SendsPasswordResetEmails;
|
||||
|
||||
/**
|
||||
* Get the response for a successful password reset link.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param string $response
|
||||
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Http\JsonResponse
|
||||
*/
|
||||
protected function sendResetLinkResponse(Request $request, $response)
|
||||
{
|
||||
return response(['message' => Lang::get($response)]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the response for a failed password reset link.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param string $response
|
||||
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Http\JsonResponse
|
||||
*/
|
||||
protected function sendResetLinkFailedResponse(Request $request, $response)
|
||||
{
|
||||
return response(['message' => Lang::get($response)], 422);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
namespace App\Http\Controllers\Auth;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Providers\RouteServiceProvider;
|
||||
use Illuminate\Foundation\Auth\AuthenticatesUsers;
|
||||
|
||||
class LoginController extends Controller
|
||||
{
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Login Controller
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This controller handles authenticating users for the application and
|
||||
| redirecting them to your home screen. The controller uses a trait
|
||||
| to conveniently provide its functionality to your applications.
|
||||
|
|
||||
*/
|
||||
|
||||
use AuthenticatesUsers;
|
||||
|
||||
/**
|
||||
* Where to redirect users after login.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $redirectTo = RouteServiceProvider::HOME;
|
||||
|
||||
/**
|
||||
* Create a new controller instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->middleware('guest')->except('logout');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
namespace App\Http\Controllers\Auth;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Support\Facades\Lang;
|
||||
use App\Providers\RouteServiceProvider;
|
||||
use Illuminate\Foundation\Auth\ResetsPasswords;
|
||||
|
||||
class ResetPasswordController extends Controller
|
||||
{
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Password Reset Controller
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This controller is responsible for handling password reset requests
|
||||
| and uses a simple trait to include this behavior. You're free to
|
||||
| explore this trait and override any methods you wish to tweak.
|
||||
|
|
||||
*/
|
||||
|
||||
use ResetsPasswords;
|
||||
|
||||
/**
|
||||
* Where to redirect users after resetting their password.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $redirectTo = RouteServiceProvider::HOME;
|
||||
|
||||
/**
|
||||
* Get the response for a successful password reset.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param string $response
|
||||
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Http\JsonResponse
|
||||
*/
|
||||
protected function sendResetResponse(Request $request, $response)
|
||||
{
|
||||
return response(['message' => Lang::get($response)]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the response for a failed password reset.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param string $response
|
||||
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Http\JsonResponse
|
||||
*/
|
||||
protected function sendResetFailedResponse(Request $request, $response)
|
||||
{
|
||||
return response(['error' => Lang::get($response)], 422);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
namespace App\Http\Controllers\Auth;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Providers\RouteServiceProvider;
|
||||
use Illuminate\Foundation\Auth\VerifiesEmails;
|
||||
|
||||
class VerificationController extends Controller
|
||||
{
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Email Verification Controller
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This controller is responsible for handling email verification for any
|
||||
| user that recently registered with the application. Emails may also
|
||||
| be re-sent if the user didn't receive the original email message.
|
||||
|
|
||||
*/
|
||||
|
||||
use VerifiesEmails;
|
||||
|
||||
/**
|
||||
* Where to redirect users after verification.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $redirectTo = RouteServiceProvider::HOME;
|
||||
|
||||
/**
|
||||
* Create a new controller instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->middleware('auth');
|
||||
$this->middleware('signed')->only('verify');
|
||||
$this->middleware('throttle:6,1')->only('verify', 'resend');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,278 @@
|
||||
<?php
|
||||
namespace App\Models;
|
||||
|
||||
use ByteUnits\Metric;
|
||||
use Illuminate\Support\Str;
|
||||
use Laravel\Cashier\Billable;
|
||||
use App\Services\HelperService;
|
||||
use App\Services\StripeService;
|
||||
use Laravel\Sanctum\HasApiTokens;
|
||||
use Kyslik\ColumnSortable\Sortable;
|
||||
use App\Notifications\ResetPassword;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Notifications\Notifiable;
|
||||
use Laravel\Fortify\TwoFactorAuthenticatable;
|
||||
use Illuminate\Contracts\Auth\MustVerifyEmail;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||
|
||||
class User extends Authenticatable implements MustVerifyEmail
|
||||
{
|
||||
use TwoFactorAuthenticatable;
|
||||
use HasApiTokens;
|
||||
use Notifiable;
|
||||
use HasFactory;
|
||||
use Billable;
|
||||
use Sortable;
|
||||
|
||||
protected $guarded = [
|
||||
'id',
|
||||
'role',
|
||||
];
|
||||
|
||||
protected $fillable = [
|
||||
'email', 'password',
|
||||
];
|
||||
|
||||
protected $hidden = [
|
||||
'password', 'remember_token',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'id' => 'string',
|
||||
'email_verified_at' => 'datetime',
|
||||
];
|
||||
|
||||
protected $appends = [
|
||||
'used_capacity',
|
||||
'storage',
|
||||
];
|
||||
|
||||
public $sortable = [
|
||||
'id',
|
||||
'name',
|
||||
'role',
|
||||
'created_at',
|
||||
'storage_capacity',
|
||||
];
|
||||
|
||||
public $incrementing = false;
|
||||
|
||||
protected $keyType = 'string';
|
||||
|
||||
/**
|
||||
* Get tax rate id for user
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function taxRates()
|
||||
{
|
||||
// Get tax rates
|
||||
$rates = collect(resolve(StripeService::class)->getTaxRates());
|
||||
|
||||
// Find tax rate
|
||||
$user_tax_rate = $rates->first(function ($item) {
|
||||
return $item['country'] === $this->settings->country && $item['active'];
|
||||
});
|
||||
|
||||
return $user_tax_rate ? [$user_tax_rate['id']] : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user used storage details
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getStorageAttribute()
|
||||
{
|
||||
// Get storage limitation setup
|
||||
$storage_limitation = get_setting('storage_limitation');
|
||||
$is_storage_limit = $storage_limitation ? $storage_limitation : 1;
|
||||
|
||||
// Get user storage usage
|
||||
if (! $is_storage_limit) {
|
||||
return [
|
||||
'used' => $this->used_capacity,
|
||||
'used_formatted' => Metric::bytes($this->used_capacity)->format(),
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'used' => (float) get_storage_fill_percentage($this->used_capacity, $this->settings->storage_capacity),
|
||||
'used_formatted' => get_storage_fill_percentage($this->used_capacity, $this->settings->storage_capacity) . '%',
|
||||
'capacity' => $this->settings->storage_capacity,
|
||||
'capacity_formatted' => format_gigabytes($this->settings->storage_capacity),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user used storage capacity in bytes
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getUsedCapacityAttribute()
|
||||
{
|
||||
$user_capacity = $this->files_with_trashed->map(function ($item) {
|
||||
return $item->getRawOriginal();
|
||||
})->sum('filesize');
|
||||
|
||||
return $user_capacity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user full folder tree
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Builder[]|\Illuminate\Database\Eloquent\Collection
|
||||
*/
|
||||
public function getFolderTreeAttribute()
|
||||
{
|
||||
return Folder::with(['folders.shared', 'shared:token,id,item_id,permission,is_protected,expire_in'])
|
||||
->where('parent_id', null)
|
||||
->where('user_id', $this->id)
|
||||
->sortable()
|
||||
->get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set user billing info
|
||||
*
|
||||
* @param $billing
|
||||
* @return UserSettings
|
||||
*/
|
||||
public function setBilling($billing)
|
||||
{
|
||||
$this->settings()->update([
|
||||
'address' => $billing['billing_address'],
|
||||
'city' => $billing['billing_city'],
|
||||
'country' => $billing['billing_country'],
|
||||
'name' => $billing['billing_name'],
|
||||
'phone_number' => $billing['billing_phone_number'],
|
||||
'postal_code' => $billing['billing_postal_code'],
|
||||
'state' => $billing['billing_state'],
|
||||
]);
|
||||
|
||||
return $this->settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the password reset notification.
|
||||
*
|
||||
* @param string $token
|
||||
* @return void
|
||||
*/
|
||||
public function sendPasswordResetNotification($token)
|
||||
{
|
||||
$this->notify(new ResetPassword($token));
|
||||
}
|
||||
|
||||
/**
|
||||
* Record user upload filesize
|
||||
*
|
||||
* @param $file_size
|
||||
*/
|
||||
public function record_upload($file_size)
|
||||
{
|
||||
$now = now();
|
||||
|
||||
$record = Traffic::whereYear('created_at', '=', $now->year)
|
||||
->whereMonth('created_at', '=', $now->month)
|
||||
->firstOrCreate([
|
||||
'user_id' => $this->id,
|
||||
]);
|
||||
|
||||
$record->update([
|
||||
'upload' => $record->upload + $file_size,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Record user download filesize
|
||||
*
|
||||
* @param $file_size
|
||||
*/
|
||||
public function record_download($file_size)
|
||||
{
|
||||
$now = now();
|
||||
|
||||
$record = Traffic::whereYear('created_at', '=', $now->year)
|
||||
->whereMonth('created_at', '=', $now->month)
|
||||
->firstOrCreate([
|
||||
'user_id' => $this->id,
|
||||
]);
|
||||
|
||||
$record->update([
|
||||
'download' => $record->download + $file_size,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user favourites folder
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
|
||||
*/
|
||||
public function favouriteFolders()
|
||||
{
|
||||
return $this->belongsToMany(Folder::class, 'favourite_folder', 'user_id', 'folder_id', 'id', 'id')
|
||||
->with('shared:token,id,item_id,permission,is_protected,expire_in');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get 5 latest uploads
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\HasMany|\Illuminate\Database\Query\Builder
|
||||
*/
|
||||
public function latest_uploads()
|
||||
{
|
||||
return $this->hasMany(File::class)->with(['parent:id,name'])->take(40);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all user files
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\HasMany
|
||||
*/
|
||||
public function files()
|
||||
{
|
||||
return $this->hasMany(File::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all user files
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\HasMany
|
||||
*/
|
||||
public function files_with_trashed()
|
||||
{
|
||||
return $this->hasMany(File::class)->withTrashed();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user attributes
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\HasOne
|
||||
*/
|
||||
public function settings()
|
||||
{
|
||||
return $this->hasOne(UserSettings::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Model Events
|
||||
*/
|
||||
protected static function boot()
|
||||
{
|
||||
parent::boot();
|
||||
|
||||
static::creating(function ($user) {
|
||||
$user->id = Str::uuid();
|
||||
|
||||
// Create user directory for his files
|
||||
Storage::makeDirectory("files/$user->id");
|
||||
});
|
||||
|
||||
static::deleted(function ($user) {
|
||||
resolve(HelperService::class)
|
||||
->erase_user_data($user);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class UserSettings extends Model
|
||||
{
|
||||
public $timestamps = false;
|
||||
|
||||
protected $guarded = [
|
||||
'id',
|
||||
'storage_capacity',
|
||||
];
|
||||
|
||||
/**
|
||||
* Format avatar to full url
|
||||
*
|
||||
* @return \Illuminate\Contracts\Routing\UrlGenerator|string
|
||||
*/
|
||||
public function getAvatarAttribute()
|
||||
{
|
||||
// Get avatar from external storage
|
||||
if ($this->attributes['avatar'] && ! is_storage_driver('local')) {
|
||||
return Storage::temporaryUrl($this->attributes['avatar'], now()->addDay());
|
||||
}
|
||||
|
||||
// Get avatar from local storage
|
||||
if ($this->attributes['avatar']) {
|
||||
return url('/' . $this->attributes['avatar']);
|
||||
}
|
||||
|
||||
return url('/assets/images/default-avatar.png');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
namespace App\Notifications;
|
||||
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Notifications\Notification;
|
||||
use Illuminate\Notifications\Messages\MailMessage;
|
||||
|
||||
class ResetPassword extends Notification
|
||||
{
|
||||
use Queueable;
|
||||
|
||||
private $token;
|
||||
|
||||
/**
|
||||
* Create a new notification instance.
|
||||
*
|
||||
* @param $token
|
||||
*/
|
||||
public function __construct($token)
|
||||
{
|
||||
$this->token = $token;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
$reset_url = url('/create-new-password?token=' . $this->token);
|
||||
$app_name = get_setting('app_title') ?? 'VueFileManager';
|
||||
|
||||
return (new MailMessage)
|
||||
->subject(__t('reset_password_subject') . $app_name)
|
||||
->greeting(__t('reset_password_greeting'))
|
||||
->line(__t('reset_password_line_1'))
|
||||
->action(__t('reset_password_action'), $reset_url)
|
||||
->line(__t('reset_password_line_2'))
|
||||
->salutation(__t('salutation') . ', ' . $app_name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the array representation of the notification.
|
||||
*
|
||||
* @param mixed $notifiable
|
||||
* @return array
|
||||
*/
|
||||
public function toArray($notifiable)
|
||||
{
|
||||
return [
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
namespace App\Http\Requests\Auth;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class CheckAccountRequest 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|string|email',
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
namespace App\Http\Requests\User;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class UpdateUserPasswordRequest 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 [
|
||||
'password' => 'required|string|min:6|confirmed',
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
namespace App\Http\Requests\User;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class UserCreateAccessTokenRequest 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|min:3',
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
namespace App\Http\Resources;
|
||||
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
class UserResource extends JsonResource
|
||||
{
|
||||
/**
|
||||
* Transform the resource into an array.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return array
|
||||
*/
|
||||
public function toArray($request)
|
||||
{
|
||||
// TODO: zrefaktorovat
|
||||
return [
|
||||
'data' => [
|
||||
'id' => $this->id,
|
||||
'type' => 'user',
|
||||
'attributes' => [
|
||||
'storage_capacity' => $this->settings->storage_capacity,
|
||||
'subscription' => $this->subscribed('main'),
|
||||
'incomplete_payment' => $this->hasIncompletePayment('main') ? route('cashier.payment', $this->subscription('main')->latestPayment()->id) : null,
|
||||
'stripe_customer' => is_null($this->stripe_id) ? false : true,
|
||||
'email' => is_demo() ? obfuscate_email($this->email) : $this->email,
|
||||
'role' => $this->role,
|
||||
'two_factor_authentication' => $this->two_factor_secret ? true : false,
|
||||
'folders' => $this->folder_tree,
|
||||
'storage' => $this->storage,
|
||||
'created_at_formatted' => format_date($this->created_at, '%d. %B. %Y'),
|
||||
'created_at' => $this->created_at,
|
||||
'updated_at' => $this->updated_at,
|
||||
],
|
||||
'relationships' => [
|
||||
'settings' => [
|
||||
'data' => [
|
||||
'id' => $this->id,
|
||||
'type' => 'settings',
|
||||
'attributes' => [
|
||||
'avatar' => $this->settings->avatar,
|
||||
'name' => $this->settings->name,
|
||||
'address' => $this->settings->address,
|
||||
'state' => $this->settings->state,
|
||||
'city' => $this->settings->city,
|
||||
'postal_code' => $this->settings->postal_code,
|
||||
'country' => $this->settings->country,
|
||||
'phone_number' => $this->settings->phone_number,
|
||||
'timezone' => $this->settings->timezone,
|
||||
],
|
||||
],
|
||||
],
|
||||
'favourites' => [
|
||||
'data' => [
|
||||
'id' => $this->id,
|
||||
'type' => 'favourite_folders',
|
||||
'attributes' => [
|
||||
'folders' => $this->favouriteFolders->makeHidden(['pivot']),
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
namespace App\Http\Resources;
|
||||
|
||||
use App\Models\File;
|
||||
use ByteUnits\Metric;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
class UserStorageResource extends JsonResource
|
||||
{
|
||||
/**
|
||||
* Transform the resource into an array.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return array
|
||||
*/
|
||||
public function toArray($request)
|
||||
{
|
||||
$document_mimetypes = [
|
||||
'pdf', 'numbers', 'xlsx', 'xls', 'txt', 'md', 'rtf', 'pptx', 'ppt', 'odt', 'ods', 'odp', 'epub', 'docx', 'doc', 'csv', 'pages',
|
||||
];
|
||||
|
||||
// Get all images
|
||||
$images = File::where('user_id', $this->id)
|
||||
->where('type', 'image')->get()->map(function ($item) {
|
||||
return (int) $item->getRawOriginal('filesize');
|
||||
})->sum();
|
||||
|
||||
// Get all audios
|
||||
$audios = File::where('user_id', $this->id)
|
||||
->where('type', 'audio')->get()->map(function ($item) {
|
||||
return (int) $item->getRawOriginal('filesize');
|
||||
})->sum();
|
||||
|
||||
// Get all videos
|
||||
$videos = File::where('user_id', $this->id)
|
||||
->where('type', 'video')->get()->map(function ($item) {
|
||||
return (int) $item->getRawOriginal('filesize');
|
||||
})->sum();
|
||||
|
||||
// Get all documents
|
||||
$documents = File::where('user_id', $this->id)
|
||||
->whereIn('mimetype', $document_mimetypes)->get()->map(function ($item) {
|
||||
return (int) $item->getRawOriginal('filesize');
|
||||
})->sum();
|
||||
|
||||
// Get all other files
|
||||
$others = File::where('user_id', $this->id)
|
||||
->whereNotIn('mimetype', $document_mimetypes)
|
||||
->whereNotIn('type', ['audio', 'video', 'image'])
|
||||
->get()->map(function ($item) {
|
||||
return (int) $item->getRawOriginal('filesize');
|
||||
})->sum();
|
||||
|
||||
return [
|
||||
'data' => [
|
||||
'id' => (string) $this->id,
|
||||
'type' => 'storage',
|
||||
'attributes' => [
|
||||
'used' => Metric::bytes($this->used_capacity)->format(),
|
||||
'capacity' => format_gigabytes($this->settings->storage_capacity),
|
||||
'percentage' => (float) get_storage_fill_percentage($this->used_capacity, $this->settings->storage_capacity),
|
||||
],
|
||||
'meta' => [
|
||||
'images' => [
|
||||
'used' => Metric::bytes($images)->format(),
|
||||
'percentage' => (float) get_storage_fill_percentage($images, $this->settings->storage_capacity),
|
||||
],
|
||||
'audios' => [
|
||||
'used' => Metric::bytes($audios)->format(),
|
||||
'percentage' => (float) get_storage_fill_percentage($audios, $this->settings->storage_capacity),
|
||||
],
|
||||
'videos' => [
|
||||
'used' => Metric::bytes($videos)->format(),
|
||||
'percentage' => (float) get_storage_fill_percentage($videos, $this->settings->storage_capacity),
|
||||
],
|
||||
'documents' => [
|
||||
'used' => Metric::bytes($documents)->format(),
|
||||
'percentage' => (float) get_storage_fill_percentage($documents, $this->settings->storage_capacity),
|
||||
],
|
||||
'others' => [
|
||||
'used' => Metric::bytes($others)->format(),
|
||||
'percentage' => (float) get_storage_fill_percentage($others, $this->settings->storage_capacity),
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
namespace App\Http\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. %B. %Y'),
|
||||
'created_at' => format_date($active_subscription['current_period_start'], '%d. %B. %Y'),
|
||||
'ends_at' => format_date($active_subscription['current_period_end'], '%d. %B. %Y'),
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
namespace App\Http\Resources;
|
||||
|
||||
use Illuminate\Http\Resources\Json\ResourceCollection;
|
||||
|
||||
class UsersCollection extends ResourceCollection
|
||||
{
|
||||
public $collects = UserResource::class;
|
||||
|
||||
/**
|
||||
* Transform the resource collection into an array.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return array
|
||||
*/
|
||||
public function toArray($request)
|
||||
{
|
||||
return [
|
||||
'data' => $this->collection,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Models\User;
|
||||
use ByteUnits\Metric;
|
||||
use App\Services\StripeService;
|
||||
use Laravel\Cashier\Subscription;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Resources\UsersCollection;
|
||||
|
||||
class DashboardController extends Controller
|
||||
{
|
||||
private StripeService $stripe;
|
||||
|
||||
public function __construct(StripeService $stripe)
|
||||
{
|
||||
$this->stripe = $stripe;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get data for dashboard
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
// Get total premium users
|
||||
$premium_users = Subscription::whereStripeStatus('active')
|
||||
->count();
|
||||
|
||||
// Get total storage usage
|
||||
$storage_usage = Metric::bytes(
|
||||
\DB::table('files')->sum('filesize')
|
||||
)->format();
|
||||
|
||||
return [
|
||||
'license' => get_setting('license'),
|
||||
'app_version' => config('vuefilemanager.version'),
|
||||
'total_users' => User::count(),
|
||||
'total_used_space' => $storage_usage,
|
||||
'total_premium_users' => $premium_users,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the newest users
|
||||
*
|
||||
* @return UsersCollection
|
||||
*/
|
||||
public function newbies()
|
||||
{
|
||||
return new UsersCollection(
|
||||
User::sortable(['created_at' => 'desc'])
|
||||
->paginate(10)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Models\Invoice;
|
||||
use App\Services\StripeService;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Resources\InvoiceResource;
|
||||
use App\Http\Resources\InvoiceAdminCollection;
|
||||
|
||||
class InvoiceController extends Controller
|
||||
{
|
||||
private StripeService $stripe;
|
||||
|
||||
public function __construct(StripeService $stripe)
|
||||
{
|
||||
$this->stripe = $stripe;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all invoices
|
||||
*
|
||||
* @return InvoiceAdminCollection
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
return new InvoiceAdminCollection(
|
||||
$this->stripe->getInvoices()['data']
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get single invoice by invoice $token
|
||||
*
|
||||
* @param $customer
|
||||
* @param $token
|
||||
* @return InvoiceResource|\Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View
|
||||
*/
|
||||
public function show($customer, $token)
|
||||
{
|
||||
return view('vuefilemanager.invoice')
|
||||
->with('settings', get_settings_in_json())
|
||||
->with('invoice', $this->stripe->getUserInvoice($customer, $token));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,231 @@
|
||||
<?php
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use Storage;
|
||||
use App\Models\User;
|
||||
use App\Models\UserSettings;
|
||||
use Illuminate\Http\Response;
|
||||
use App\Services\StripeService;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Resources\UserResource;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use App\Http\Resources\UsersCollection;
|
||||
use App\Http\Resources\UserSubscription;
|
||||
use Illuminate\Support\Facades\Password;
|
||||
use App\Http\Resources\InvoiceCollection;
|
||||
use App\Http\Resources\UserStorageResource;
|
||||
use App\Http\Requests\Admin\ChangeRoleRequest;
|
||||
use App\Http\Requests\Admin\CreateUserByAdmin;
|
||||
use App\Http\Requests\Admin\DeleteUserRequest;
|
||||
use Illuminate\Contracts\Foundation\Application;
|
||||
use Illuminate\Contracts\Routing\ResponseFactory;
|
||||
use App\Http\Requests\Admin\ChangeStorageCapacityRequest;
|
||||
|
||||
class UserController extends Controller
|
||||
{
|
||||
private StripeService $stripe;
|
||||
|
||||
public function __construct(StripeService $stripe)
|
||||
{
|
||||
$this->stripe = $stripe;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user details
|
||||
*
|
||||
* @param User $user
|
||||
* @return UserResource
|
||||
*/
|
||||
public function details(User $user)
|
||||
{
|
||||
return new UserResource(
|
||||
$user
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user storage details
|
||||
*
|
||||
* @param User $user
|
||||
* @return UserStorageResource
|
||||
*/
|
||||
public function storage(User $user)
|
||||
{
|
||||
return new UserStorageResource(
|
||||
$user
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user storage details
|
||||
*
|
||||
* @param User $user
|
||||
* @return InvoiceCollection
|
||||
*/
|
||||
public function invoices(User $user)
|
||||
{
|
||||
return new InvoiceCollection(
|
||||
$this
|
||||
->stripe
|
||||
->getUserInvoices($user)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user subscription details
|
||||
*
|
||||
* @param User $user
|
||||
* @return UserSubscription|Application|ResponseFactory|Response
|
||||
*/
|
||||
public function subscription(User $user)
|
||||
{
|
||||
if (! $user->stripeId() || ! $user->subscription('main')) {
|
||||
return response("User doesn't have any subscription.", 404);
|
||||
}
|
||||
|
||||
return new UserSubscription(
|
||||
$user
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all users
|
||||
*
|
||||
* @return UsersCollection
|
||||
*/
|
||||
public function users()
|
||||
{
|
||||
return new UsersCollection(
|
||||
User::sortable(['created_at', 'DESC'])
|
||||
->paginate(20)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Change user role
|
||||
*
|
||||
* @param ChangeRoleRequest $request
|
||||
* @param User $user
|
||||
* @return UserResource
|
||||
*/
|
||||
public function change_role(ChangeRoleRequest $request, User $user)
|
||||
{
|
||||
// Demo preview
|
||||
if (is_demo_account('howdy@hi5ve.digial')) {
|
||||
return new UserResource($user);
|
||||
}
|
||||
|
||||
// Update user role
|
||||
$user->role = $request->input('attributes.role');
|
||||
$user->save();
|
||||
|
||||
return new UserResource(
|
||||
$user
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Change user storage capacity
|
||||
*
|
||||
* @param ChangeStorageCapacityRequest $request
|
||||
* @param User $user
|
||||
* @return UserStorageResource
|
||||
*/
|
||||
public function change_storage_capacity(ChangeStorageCapacityRequest $request, User $user)
|
||||
{
|
||||
$user
|
||||
->settings()
|
||||
->update(
|
||||
$request->input('attributes')
|
||||
);
|
||||
|
||||
return new UserStorageResource(
|
||||
$user
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send user password reset link
|
||||
*
|
||||
* @param User $user
|
||||
* @return ResponseFactory|Response
|
||||
*/
|
||||
public function reset_password(User $user)
|
||||
{
|
||||
// Demo preview
|
||||
if (is_demo()) {
|
||||
return response('Done!', 204);
|
||||
}
|
||||
|
||||
// Get password token
|
||||
$token = Password::getRepository()
|
||||
->create($user);
|
||||
|
||||
// Send user email
|
||||
$user->sendPasswordResetNotification($token);
|
||||
|
||||
return response('Done!', 204);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new user by admin
|
||||
*
|
||||
* @param CreateUserByAdmin $request
|
||||
* @return UserResource|Application|ResponseFactory|Response
|
||||
*/
|
||||
public function create_user(CreateUserByAdmin $request)
|
||||
{
|
||||
// Create user
|
||||
$user = User::forceCreate([
|
||||
'role' => $request->role,
|
||||
'email' => $request->email,
|
||||
'password' => bcrypt($request->password),
|
||||
'email_verified_at' => now(),
|
||||
]);
|
||||
|
||||
UserSettings::unguard();
|
||||
|
||||
$user
|
||||
->settings()
|
||||
->create([
|
||||
'name' => $request->name,
|
||||
'avatar' => store_avatar($request, 'avatar'),
|
||||
'storage_capacity' => $request->storage_capacity,
|
||||
]);
|
||||
|
||||
UserSettings::reguard();
|
||||
|
||||
return response(new UserResource($user), 201);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete user with all user data
|
||||
*
|
||||
* @param DeleteUserRequest $request
|
||||
* @param User $user
|
||||
* @return ResponseFactory|Response
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function delete_user(DeleteUserRequest $request, User $user)
|
||||
{
|
||||
if (is_demo()) {
|
||||
return response('Done!', 204);
|
||||
}
|
||||
|
||||
if ($user->subscribed('main')) {
|
||||
abort(202, "You can\'t delete this account while user have active subscription.");
|
||||
}
|
||||
|
||||
if ($user->id === Auth::id()) {
|
||||
abort(406, "You can\'t delete your account");
|
||||
}
|
||||
|
||||
if ($user->settings->name !== $request->name) {
|
||||
abort(403, 'The name you typed is wrong!');
|
||||
}
|
||||
|
||||
$user->delete();
|
||||
|
||||
return response('Done!', 204);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
namespace App\Http\Requests\Admin;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class ChangeRoleRequest 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 [
|
||||
'attributes' => 'required|array',
|
||||
'attributes.role' => 'required|string',
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
namespace App\Http\Requests\Admin;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class ChangeStorageCapacityRequest 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 [
|
||||
'attributes' => 'required|array',
|
||||
'attributes.storage_capacity' => 'required|digits_between:1,9',
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
namespace App\Http\Requests\Admin;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class CreateUserByAdmin 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|string|email|max:255|unique:users',
|
||||
'password' => 'required|string|min:6|confirmed',
|
||||
'name' => 'required|string|max:255',
|
||||
'storage_capacity' => 'required|digits_between:1,9',
|
||||
'role' => 'required|string',
|
||||
'avatar' => 'sometimes|file',
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
namespace App\Http\Requests\Admin;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class DeleteUserRequest 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|max:255',
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
namespace App\Http\Resources;
|
||||
|
||||
use Illuminate\Http\Resources\Json\ResourceCollection;
|
||||
|
||||
class InvoiceAdminCollection extends ResourceCollection
|
||||
{
|
||||
public $collects = InvoiceAdminResource::class;
|
||||
|
||||
/**
|
||||
* Transform the resource collection into an array.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return array
|
||||
*/
|
||||
public function toArray($request)
|
||||
{
|
||||
return [
|
||||
'data' => $this->collection,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
namespace App\Http\Resources;
|
||||
|
||||
use App\Models\User;
|
||||
use Laravel\Cashier\Cashier;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
class InvoiceAdminResource extends JsonResource
|
||||
{
|
||||
/**
|
||||
* Transform the resource into an array.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return array
|
||||
*/
|
||||
public function toArray($request)
|
||||
{
|
||||
$user = User::where('stripe_id', $this['customer'])
|
||||
->first();
|
||||
|
||||
return [
|
||||
'data' => [
|
||||
'id' => $this['id'],
|
||||
'type' => 'invoices',
|
||||
'attributes' => [
|
||||
'customer' => $this['customer'],
|
||||
'total' => Cashier::formatAmount($this['total']),
|
||||
'currency' => $this['currency'],
|
||||
'created_at_formatted' => format_date($this['created']),
|
||||
'created_at' => $this['created'],
|
||||
'order' => $this['number'],
|
||||
'user_id' => $user->id ?? null,
|
||||
'client' => [
|
||||
'billing_address' => $this['customer_address'],
|
||||
'billing_name' => $this['customer_name'],
|
||||
'billing_phone_number' => $this['customer_phone'],
|
||||
],
|
||||
'bag' => [
|
||||
'amount' => $this['lines']['data'][0]['amount'],
|
||||
'currency' => $this['lines']['data'][0]['currency'],
|
||||
'type' => $this['lines']['data'][0]['type'],
|
||||
'description' => $this['lines']['data'][0]['description'],
|
||||
],
|
||||
'seller' => null,
|
||||
],
|
||||
$this->mergeWhen($user, function () use ($user) {
|
||||
return [
|
||||
'relationships' => [
|
||||
'user' => [
|
||||
'data' => [
|
||||
'id' => $user->id,
|
||||
'type' => 'user',
|
||||
'attributes' => [
|
||||
'name' => $user->settings->name,
|
||||
'avatar' => $user->settings->avatar,
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
}),
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
namespace App\Rules;
|
||||
|
||||
use Illuminate\Contracts\Validation\Rule;
|
||||
|
||||
class DisabledMimetypes implements Rule
|
||||
{
|
||||
/**
|
||||
* Determine if the validation rule passes.
|
||||
*
|
||||
* @param string $attribute
|
||||
* @param mixed $value
|
||||
* @return bool
|
||||
*/
|
||||
public function passes($attribute, $value)
|
||||
{
|
||||
$mimetype_blacklist = explode(',', get_setting('mimetypes_blacklist'));
|
||||
$file_mimetype = explode('/', $value->getMimeType());
|
||||
|
||||
return ! array_intersect($file_mimetype, $mimetype_blacklist);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation error message.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function message()
|
||||
{
|
||||
return 'Type of this mime type is not allowed.';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,215 @@
|
||||
<?php
|
||||
namespace App\Http\Controllers\FileManager;
|
||||
|
||||
use App\Models\File;
|
||||
use App\Models\User;
|
||||
use App\Models\Share;
|
||||
use App\Models\Folder;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Collection;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use App\Http\Requests\FileBrowser\SearchRequest;
|
||||
|
||||
class BrowseController extends Controller
|
||||
{
|
||||
/**
|
||||
* Get directory with files
|
||||
*
|
||||
* @param Request $request
|
||||
* @param $id
|
||||
* @return Collection
|
||||
*/
|
||||
public function folder(Request $request, $id)
|
||||
{
|
||||
$root_id = $id === 'undefined' ? null : $id;
|
||||
|
||||
// Get folder trash items
|
||||
if ($request->query('trash')) {
|
||||
// Get folders and files
|
||||
$folders = Folder::onlyTrashed()
|
||||
->with('parent')
|
||||
->where('parent_id', $root_id)
|
||||
->sortable()
|
||||
->get();
|
||||
|
||||
$files = File::onlyTrashed()
|
||||
->with('parent')
|
||||
->where('folder_id', $root_id)
|
||||
->sortable()
|
||||
->get();
|
||||
|
||||
// Collect folders and files to single array
|
||||
return collect([$folders, $files])->collapse();
|
||||
}
|
||||
|
||||
// Get folders and files
|
||||
$folders = Folder::with(['parent:id,name', 'shared:token,id,item_id,permission,is_protected,expire_in'])
|
||||
->where('parent_id', $root_id)
|
||||
->where('user_id', Auth::id())
|
||||
->sortable()
|
||||
->get();
|
||||
|
||||
$files = File::with(['parent:id,name', 'shared:token,id,item_id,permission,is_protected,expire_in'])
|
||||
->where('folder_id', $root_id)
|
||||
->where('user_id', Auth::id())
|
||||
->sortable()
|
||||
->get();
|
||||
|
||||
// Collect folders and files to single array
|
||||
return collect([$folders, $files])
|
||||
->collapse();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get latest user uploads
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function latest()
|
||||
{
|
||||
$user = User::with(['latest_uploads' => function ($query) {
|
||||
$query->sortable(['created_at' => 'desc']);
|
||||
}])
|
||||
->where('id', Auth::id())
|
||||
->first();
|
||||
|
||||
return $user->latest_uploads;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get trashed files
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public function trash()
|
||||
{
|
||||
$user_id = Auth::id();
|
||||
|
||||
// Get folders and files
|
||||
$folders_trashed = Folder::onlyTrashed()
|
||||
->with(['trashed_folders', 'parent'])
|
||||
->where('user_id', $user_id)
|
||||
->get(['parent_id', 'id', 'name']);
|
||||
|
||||
$folders = Folder::onlyTrashed()
|
||||
->with(['parent'])
|
||||
->where('user_id', $user_id)
|
||||
->whereIn('id', filter_folders_ids($folders_trashed))
|
||||
->sortable()
|
||||
->get();
|
||||
|
||||
// Get files trashed
|
||||
$files_trashed = File::onlyTrashed()
|
||||
->with(['parent'])
|
||||
->where('user_id', $user_id)
|
||||
->where(function ($query) use ($folders_trashed) {
|
||||
$query->whereNull('folder_id');
|
||||
$query->orWhereNotIn('folder_id', array_values(array_unique(recursiveFind($folders_trashed->toArray(), 'id'))));
|
||||
})
|
||||
->sortable()
|
||||
->get();
|
||||
|
||||
// Collect folders and files to single array
|
||||
return collect([$folders, $files_trashed])
|
||||
->collapse();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user shared items
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public function shared()
|
||||
{
|
||||
$user_id = Auth::id();
|
||||
|
||||
// Get shared folders and files
|
||||
$folder_ids = Share::where('user_id', $user_id)
|
||||
->where('type', 'folder')
|
||||
->pluck('item_id');
|
||||
|
||||
$file_ids = Share::where('user_id', $user_id)
|
||||
->where('type', '!=', 'folder')
|
||||
->pluck('item_id');
|
||||
|
||||
// Get folders and files
|
||||
$folders = Folder::with(['parent', 'shared:token,id,item_id,permission,is_protected,expire_in'])
|
||||
->where('user_id', $user_id)
|
||||
->whereIn('id', $folder_ids)
|
||||
->sortable()
|
||||
->get();
|
||||
|
||||
$files = File::with(['parent', 'shared:token,id,item_id,permission,is_protected,expire_in'])
|
||||
->where('user_id', $user_id)
|
||||
->whereIn('id', $file_ids)
|
||||
->sortable()
|
||||
->get();
|
||||
|
||||
// Collect folders and files to single array
|
||||
return collect([$folders, $files])
|
||||
->collapse();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get participant uploads
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function participant_uploads()
|
||||
{
|
||||
return File::with(['parent'])
|
||||
->where('user_id', Auth::id())
|
||||
->whereAuthor('visitor')
|
||||
->sortable()
|
||||
->get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user folder tree
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function navigation_tree()
|
||||
{
|
||||
$folders = Folder::with('folders:id,parent_id,id,name')
|
||||
->where('parent_id', null)
|
||||
->where('user_id', Auth::id())
|
||||
->sortable()
|
||||
->get(['id', 'parent_id', 'id', 'name']);
|
||||
|
||||
return [
|
||||
[
|
||||
'name' => __t('home'),
|
||||
'location' => 'base',
|
||||
'folders' => $folders,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Search files
|
||||
*
|
||||
* @param SearchRequest $request
|
||||
* @return Collection
|
||||
*/
|
||||
public function search(SearchRequest $request)
|
||||
{
|
||||
$user_id = Auth::id();
|
||||
|
||||
$query = remove_accents($request->input('query'));
|
||||
|
||||
// Search files id db
|
||||
$searched_files = File::search($query)
|
||||
->where('user_id', $user_id)
|
||||
->get();
|
||||
|
||||
$searched_folders = Folder::search($query)
|
||||
->where('user_id', $user_id)
|
||||
->get();
|
||||
|
||||
// Collect folders and files to single array
|
||||
return collect([$searched_folders, $searched_files])
|
||||
->collapse();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
<?php
|
||||
namespace App\Http\Controllers\FileManager;
|
||||
|
||||
use App\Models\Zip;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Services\HelperService;
|
||||
use App\Models\File as UserFile;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class FileAccessController extends Controller
|
||||
{
|
||||
private $helper;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->helper = resolve(HelperService::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get avatar
|
||||
*
|
||||
* @param $basename
|
||||
* @return mixed
|
||||
* @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
|
||||
*/
|
||||
public function get_avatar($basename)
|
||||
{
|
||||
// Check if file exist
|
||||
if (! Storage::exists("/avatars/$basename")) {
|
||||
abort(404);
|
||||
}
|
||||
|
||||
// Return avatar
|
||||
return Storage::download("/avatars/$basename", $basename);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get system image
|
||||
*
|
||||
* @param $basename
|
||||
* @return mixed
|
||||
* @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
|
||||
*/
|
||||
public function get_system_image($basename)
|
||||
{
|
||||
// Check if file exist
|
||||
if (! Storage::exists("/system/$basename")) {
|
||||
abort(404);
|
||||
}
|
||||
|
||||
// Return avatar
|
||||
return Storage::download("/system/$basename", $basename);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get file
|
||||
*
|
||||
* @param Request $request
|
||||
* @param $filename
|
||||
* @return mixed
|
||||
* @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
|
||||
*/
|
||||
public function get_file(Request $request, $filename)
|
||||
{
|
||||
// Get file record
|
||||
$file = UserFile::withTrashed()
|
||||
->where('user_id', Auth::id())
|
||||
->where('basename', $filename)
|
||||
->firstOrFail();
|
||||
|
||||
// Check user permission
|
||||
/*if (!$request->user()->tokenCan('master')) {
|
||||
|
||||
// Get shared token
|
||||
$shared = get_shared($request->cookie('shared_token'));
|
||||
|
||||
// Check access to file
|
||||
$this->check_file_access($shared, $file);
|
||||
}*/
|
||||
|
||||
// Store user download size
|
||||
$request->user()->record_download(
|
||||
(int) $file->getRawOriginal('filesize')
|
||||
);
|
||||
|
||||
return $this->helper->download_file($file, Auth::id());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get generated zip for user
|
||||
*
|
||||
* @param $id
|
||||
* @return \Symfony\Component\HttpFoundation\StreamedResponse
|
||||
*/
|
||||
public function get_zip($id)
|
||||
{
|
||||
$disk = Storage::disk('local');
|
||||
|
||||
$zip = Zip::whereId($id)
|
||||
->where('user_id', Auth::id())
|
||||
->firstOrFail();
|
||||
|
||||
$zip
|
||||
->user
|
||||
->record_download(
|
||||
$disk->size("zip/$zip->basename")
|
||||
);
|
||||
|
||||
return $disk->download("zip/$zip->basename", $zip->basename, [
|
||||
'Content-Type' => 'application/zip',
|
||||
'Content-Length' => $disk->size("zip/$zip->basename"),
|
||||
'Accept-Ranges' => 'bytes',
|
||||
'Content-Range' => 'bytes 0-600/' . $disk->size("zip/$zip->basename"),
|
||||
'Content-Disposition' => "attachment; filename=$zip->basename",
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get image thumbnail
|
||||
*
|
||||
* @param Request $request
|
||||
* @param $filename
|
||||
* @return mixed
|
||||
* @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
|
||||
*/
|
||||
public function get_thumbnail(Request $request, $filename)
|
||||
{
|
||||
// Get file record
|
||||
$file = UserFile::withTrashed()
|
||||
->whereUserId(Auth::id())
|
||||
->whereThumbnail($filename)
|
||||
->firstOrFail();
|
||||
|
||||
// Check user permission
|
||||
/*if (!$request->user()->tokenCan('master')) {
|
||||
$this->check_file_access($request, $file);
|
||||
}*/
|
||||
|
||||
return $this->helper->download_thumbnail_file($file, Auth::id());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,211 @@
|
||||
<?php
|
||||
namespace App\Models;
|
||||
|
||||
use ByteUnits\Metric;
|
||||
use Illuminate\Support\Str;
|
||||
use Laravel\Scout\Searchable;
|
||||
use Kyslik\ColumnSortable\Sortable;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use TeamTNT\TNTSearch\Indexer\TNTIndexer;
|
||||
use \Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
|
||||
/**
|
||||
* @method static whereUserId($user_id)
|
||||
* @method static whereId($id)
|
||||
*/
|
||||
class File extends Model
|
||||
{
|
||||
use Searchable, SoftDeletes, Sortable, HasFactory;
|
||||
|
||||
public $public_access = null;
|
||||
|
||||
protected $guarded = [
|
||||
'id',
|
||||
];
|
||||
|
||||
protected $appends = [
|
||||
'file_url',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'metadata' => 'array',
|
||||
];
|
||||
|
||||
protected $hidden = [
|
||||
'author_id',
|
||||
];
|
||||
|
||||
/**
|
||||
* Sortable columns
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
public $sortable = [
|
||||
'name',
|
||||
'created_at',
|
||||
];
|
||||
|
||||
public $incrementing = false;
|
||||
|
||||
protected $keyType = 'string';
|
||||
|
||||
/**
|
||||
* Set routes with public access
|
||||
*
|
||||
* @param $token
|
||||
*/
|
||||
public function setPublicUrl($token)
|
||||
{
|
||||
$this->public_access = $token;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format created at date
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getCreatedAtAttribute()
|
||||
{
|
||||
return format_date(set_time_by_user_timezone($this->attributes['created_at']), __t('time'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Form\a\t created at date reformat
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getDeletedAtAttribute()
|
||||
{
|
||||
if (! $this->attributes['deleted_at']) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return format_date(set_time_by_user_timezone($this->attributes['deleted_at']), __t('time'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Format fileSize
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getFilesizeAttribute()
|
||||
{
|
||||
return Metric::bytes($this->attributes['filesize'])->format();
|
||||
}
|
||||
|
||||
/**
|
||||
* Format thumbnail url
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getThumbnailAttribute()
|
||||
{
|
||||
// Get thumbnail from external storage
|
||||
if ($this->attributes['thumbnail'] && ! is_storage_driver(['local'])) {
|
||||
return Storage::temporaryUrl("files/$this->user_id/{$this->attributes['thumbnail']}", now()->addHour());
|
||||
}
|
||||
|
||||
// Get thumbnail from local storage
|
||||
if ($this->attributes['thumbnail']) {
|
||||
// Thumbnail route
|
||||
$route = route('thumbnail', ['name' => $this->attributes['thumbnail']]);
|
||||
|
||||
if ($this->public_access) {
|
||||
return "$route/$this->public_access";
|
||||
}
|
||||
|
||||
return $route;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format file url
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getFileUrlAttribute()
|
||||
{
|
||||
// Get file from external storage
|
||||
if (! is_storage_driver(['local'])) {
|
||||
$file_pretty_name = is_storage_driver('backblaze')
|
||||
? Str::snake(mb_strtolower($this->attributes['name']))
|
||||
: get_pretty_name($this->attributes['basename'], $this->attributes['name'], $this->attributes['mimetype']);
|
||||
|
||||
$header = [
|
||||
'ResponseAcceptRanges' => 'bytes',
|
||||
'ResponseContentType' => $this->attributes['mimetype'],
|
||||
'ResponseContentLength' => $this->attributes['filesize'],
|
||||
'ResponseContentRange' => 'bytes 0-600/' . $this->attributes['filesize'],
|
||||
'ResponseContentDisposition' => 'attachment; filename=' . $file_pretty_name,
|
||||
];
|
||||
|
||||
return Storage::temporaryUrl("files/$this->user_id/{$this->attributes['basename']}", now()->addDay(), $header);
|
||||
}
|
||||
|
||||
// Get thumbnail from local storage
|
||||
$route = route('file', ['name' => $this->attributes['basename']]);
|
||||
|
||||
if ($this->public_access) {
|
||||
return "$route/$this->public_access";
|
||||
}
|
||||
|
||||
return $route;
|
||||
}
|
||||
|
||||
/**
|
||||
* Index file
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toSearchableArray()
|
||||
{
|
||||
$array = $this->toArray();
|
||||
$name = Str::slug($array['name'], ' ');
|
||||
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'name' => $name,
|
||||
'nameNgrams' => utf8_encode((new TNTIndexer)->buildTrigrams(implode(', ', [$name]))),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||
*/
|
||||
public function parent()
|
||||
{
|
||||
return $this->belongsTo(Folder::class, 'folder_id', 'id');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Illuminate\Database\Eloquent\Relations\HasOne
|
||||
*/
|
||||
public function folder()
|
||||
{
|
||||
return $this->hasOne(Folder::class, 'id', 'folder_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Illuminate\Database\Eloquent\Relations\HasOne
|
||||
*/
|
||||
public function shared()
|
||||
{
|
||||
return $this->hasOne(Share::class, 'item_id', 'id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Model events
|
||||
*/
|
||||
protected static function boot()
|
||||
{
|
||||
parent::boot();
|
||||
|
||||
static::creating(function ($file) {
|
||||
$file->id = (string) Str::uuid();
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
namespace App\Http\Requests\FileFunctions;
|
||||
|
||||
use App\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 [
|
||||
'filename' => 'required|string',
|
||||
'folder_id' => 'nullable|uuid',
|
||||
'is_last' => 'sometimes|boolean',
|
||||
'file' => ['required', 'file', new DisabledMimetypes],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
namespace App\Http\Resources;
|
||||
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
class FileResource 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' => 'file',
|
||||
'attributes' => [
|
||||
'name' => $this->name,
|
||||
'basename' => $this->basename,
|
||||
'mimetype' => $this->mimetype,
|
||||
'filesize' => $this->filesize,
|
||||
'type' => $this->type,
|
||||
'file_url' => $this->file_url,
|
||||
'thumbnail' => $this->thumbnail,
|
||||
'created_at' => $this->created_at,
|
||||
'updated_at' => $this->created_at,
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
namespace App\Http\Controllers\FileManager;
|
||||
|
||||
use App\Models\Folder;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Services\DemoService;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
|
||||
class FavouriteController extends Controller
|
||||
{
|
||||
/**
|
||||
* FavouriteController constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->demo = resolve(DemoService::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add folder to user favourites
|
||||
*
|
||||
* @param Request $request
|
||||
* @return \Illuminate\Contracts\Routing\ResponseFactory|\Illuminate\Http\Response
|
||||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
// todo: pridat validator ako AddToFavouritesRequest
|
||||
|
||||
foreach ($request->folders as $id) {
|
||||
// Get user & folder
|
||||
$user = Auth::user();
|
||||
|
||||
if (is_demo($user->id)) {
|
||||
return $this->demo->favourites($user);
|
||||
}
|
||||
|
||||
// Add folder to user favourites
|
||||
$user
|
||||
->favouriteFolders()
|
||||
->syncWithoutDetaching($id);
|
||||
}
|
||||
|
||||
// Return updated favourites
|
||||
return response($user->favouriteFolders, 204);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove folder from user favourites
|
||||
*
|
||||
* @param $id
|
||||
* @return \Illuminate\Contracts\Routing\ResponseFactory|\Illuminate\Http\Response
|
||||
*/
|
||||
public function destroy($id)
|
||||
{
|
||||
// Get user
|
||||
$user = Auth::user();
|
||||
|
||||
if (is_demo($user->id)) {
|
||||
return $this->demo->favourites($user);
|
||||
}
|
||||
|
||||
// Remove folder from user favourites
|
||||
$user->favouriteFolders()->detach($id);
|
||||
|
||||
// Return updated favourites
|
||||
return response($user->favouriteFolders, 204);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,245 @@
|
||||
<?php
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Support\Str;
|
||||
use Laravel\Scout\Searchable;
|
||||
use Kyslik\ColumnSortable\Sortable;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use TeamTNT\TNTSearch\Indexer\TNTIndexer;
|
||||
use \Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
|
||||
/**
|
||||
* @method static whereUserId(int|string|null $id)
|
||||
*/
|
||||
class Folder extends Model
|
||||
{
|
||||
use Searchable, SoftDeletes, Sortable, HasFactory;
|
||||
|
||||
protected $guarded = [
|
||||
'id',
|
||||
];
|
||||
|
||||
protected $appends = [
|
||||
'items',
|
||||
'trashed_items',
|
||||
'type',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'emoji' => 'array',
|
||||
];
|
||||
|
||||
protected $hidden = [
|
||||
'author_id',
|
||||
];
|
||||
|
||||
/**
|
||||
* Sortable columns
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
public $sortable = [
|
||||
'name',
|
||||
'created_at',
|
||||
];
|
||||
|
||||
public $incrementing = false;
|
||||
|
||||
protected $keyType = 'string';
|
||||
|
||||
public function getTypeAttribute()
|
||||
{
|
||||
return 'folder';
|
||||
}
|
||||
|
||||
/**
|
||||
* Index folder
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toSearchableArray()
|
||||
{
|
||||
$array = $this->toArray();
|
||||
$name = Str::slug($array['name'], ' ');
|
||||
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'name' => $name,
|
||||
'nameNgrams' => utf8_encode((new TNTIndexer)->buildTrigrams(implode(', ', [$name]))),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Counts how many folder have items
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getItemsAttribute()
|
||||
{
|
||||
$folders = $this->folders()->count();
|
||||
$files = $this->files()->count();
|
||||
|
||||
return $folders + $files;
|
||||
}
|
||||
|
||||
/**
|
||||
* Counts how many folder have items
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getTrashedItemsAttribute()
|
||||
{
|
||||
$folders = $this->trashed_folders()->count();
|
||||
$files = $this->trashed_files()->count();
|
||||
|
||||
return $folders + $files;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format created at date reformat
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getCreatedAtAttribute()
|
||||
{
|
||||
return format_date(set_time_by_user_timezone($this->attributes['created_at']), __t('time'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Format created at date reformat
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getDeletedAtAttribute()
|
||||
{
|
||||
if (! $this->attributes['deleted_at']) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return format_date(set_time_by_user_timezone($this->attributes['deleted_at']), __t('time'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get parent
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||
*/
|
||||
public function parent()
|
||||
{
|
||||
return $this->belongsTo(Folder::class, 'parent_id', 'id');
|
||||
}
|
||||
|
||||
public function folderIds()
|
||||
{
|
||||
return $this->children()->with('folderIds')->select(['id', 'parent_id']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all files
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\HasMany
|
||||
*/
|
||||
public function files()
|
||||
{
|
||||
return $this->hasMany(File::class, 'folder_id', 'id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all trashed files
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\HasMany
|
||||
*/
|
||||
public function trashed_files()
|
||||
{
|
||||
return $this->hasMany(File::class, 'folder_id', 'id')->withTrashed();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all folders
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\HasMany
|
||||
*/
|
||||
public function folders()
|
||||
{
|
||||
return $this->children()->with('folders');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all trashed folders
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\HasMany
|
||||
*/
|
||||
public function trashed_folders()
|
||||
{
|
||||
return $this->children()->with('trashed_folders')->withTrashed()->select(['parent_id', 'id', 'name']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get childrens
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\HasMany
|
||||
*/
|
||||
public function children()
|
||||
{
|
||||
return $this->hasMany(Folder::class, 'parent_id', 'id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get trashed childrens
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\HasMany
|
||||
*/
|
||||
public function trashed_children()
|
||||
{
|
||||
return $this->hasMany(Folder::class, 'parent_id', 'id')->withTrashed();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get sharing attributes
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\HasOne
|
||||
*/
|
||||
public function shared()
|
||||
{
|
||||
return $this->hasOne(Share::class, 'item_id', 'id');
|
||||
}
|
||||
|
||||
// Delete all folder children
|
||||
public static function boot()
|
||||
{
|
||||
parent::boot();
|
||||
|
||||
static::creating(function ($model) {
|
||||
$model->id = (string) Str::uuid();
|
||||
});
|
||||
|
||||
static::deleting(function ($item) {
|
||||
if ($item->isForceDeleting()) {
|
||||
$item->trashed_children()->each(function ($folder) {
|
||||
$folder->forceDelete();
|
||||
});
|
||||
} else {
|
||||
$item->children()->each(function ($folder) {
|
||||
$folder->delete();
|
||||
});
|
||||
|
||||
$item->files()->each(function ($file) {
|
||||
$file->delete();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
static::restoring(function ($item) {
|
||||
// Restore children folders
|
||||
$item->trashed_children()->each(function ($folder) {
|
||||
$folder->restore();
|
||||
});
|
||||
|
||||
// Restore children files
|
||||
$item->trashed_files()->each(function ($files) {
|
||||
$files->restore();
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
namespace App\Http\Requests\FileFunctions;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class CreateFolderRequest 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 [
|
||||
'parent_id' => 'nullable|uuid',
|
||||
'name' => 'required|string',
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,202 @@
|
||||
<?php
|
||||
namespace App\Http\Controllers\App;
|
||||
|
||||
use App\Models\Page;
|
||||
use App\Models\Share;
|
||||
use App\Models\Setting;
|
||||
use App\Models\Language;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Response;
|
||||
use App\Services\StripeService;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Resources\PageResource;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
use App\Http\Mail\SendContactMessage;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Doctrine\DBAL\Driver\PDOException;
|
||||
use Illuminate\Database\QueryException;
|
||||
use App\Http\Resources\PricingCollection;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Illuminate\Contracts\Routing\ResponseFactory;
|
||||
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||
use App\Http\Requests\PublicPages\SendContactMessageRequest;
|
||||
|
||||
class AppFunctionsController extends Controller
|
||||
{
|
||||
/**
|
||||
* List of allowed settings to get from public request
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private array $blacklist = [
|
||||
'purchase_code',
|
||||
'license',
|
||||
];
|
||||
|
||||
private StripeService $stripe;
|
||||
|
||||
public function __construct(StripeService $stripe)
|
||||
{
|
||||
$this->stripe = $stripe;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show index page
|
||||
*
|
||||
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
try {
|
||||
// Try to connect to database
|
||||
\DB::getPdo();
|
||||
|
||||
// Get setup status
|
||||
$setup_status = get_setup_status();
|
||||
|
||||
// Get app pages
|
||||
$pages = Page::all();
|
||||
|
||||
// Get all settings
|
||||
$settings = get_settings_in_json();
|
||||
} catch (PDOException $e) {
|
||||
$setup_status = 'setup-database';
|
||||
}
|
||||
|
||||
return view('index')
|
||||
->with('settings', $settings ?? null)
|
||||
->with('legal', $pages ?? null)
|
||||
->with('installation', $setup_status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get og site for web crawlers
|
||||
*
|
||||
* @param Share $shared
|
||||
* @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View
|
||||
*/
|
||||
public function og_site(Share $shared)
|
||||
{
|
||||
// Get file/folder record
|
||||
$item = ('App\\Models\\' . ucfirst($shared->type))
|
||||
::where('user_id', $shared->user->id)
|
||||
->where('id', $shared->item_id)
|
||||
->first();
|
||||
|
||||
if ($item->thumbnail) {
|
||||
$item->setPublicUrl($shared->token);
|
||||
}
|
||||
|
||||
return view('vuefilemanager.crawler.og-view')
|
||||
->with('settings', get_settings_in_json())
|
||||
->with('metadata', [
|
||||
'url' => url('/share', ['token' => $shared->token]),
|
||||
'is_protected' => $shared->is_protected,
|
||||
'user' => $shared->user->settings->name,
|
||||
'name' => $item->name,
|
||||
'size' => $shared->type === 'folder'
|
||||
? $item->items
|
||||
: $item->filesize,
|
||||
'thumbnail' => $item->thumbnail ?? null,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send contact message from pages
|
||||
*
|
||||
* @param SendContactMessageRequest $request
|
||||
* @return ResponseFactory|Response
|
||||
*/
|
||||
public function contact_form(SendContactMessageRequest $request)
|
||||
{
|
||||
Mail::to(
|
||||
get_setting('contact_email')
|
||||
)->send(
|
||||
new SendContactMessage($request->all())
|
||||
);
|
||||
|
||||
return response('Done', 201);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get single page content
|
||||
*
|
||||
* @param Page $page
|
||||
* @return PageResource
|
||||
*/
|
||||
public function get_page(Page $page)
|
||||
{
|
||||
return new PageResource($page);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get selected settings from public route
|
||||
*
|
||||
* @param Request $request
|
||||
* @return mixed
|
||||
*/
|
||||
public function get_setting_columns(Request $request)
|
||||
{
|
||||
if (strpos($request->column, '|') !== false) {
|
||||
$columns = collect(explode('|', $request->column))
|
||||
->each(function ($column) {
|
||||
if (in_array($column, $this->blacklist)) {
|
||||
abort(401);
|
||||
}
|
||||
});
|
||||
|
||||
return Setting::whereIn('name', $columns)
|
||||
->pluck('value', 'name');
|
||||
}
|
||||
|
||||
if (in_array($request->column, $this->blacklist)) {
|
||||
abort(401);
|
||||
}
|
||||
|
||||
return Setting::where('name', $request->column)
|
||||
->pluck('value', 'name');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all active storage plans
|
||||
*
|
||||
* @return PricingCollection
|
||||
*/
|
||||
public function get_storage_plans()
|
||||
{
|
||||
// Get pricing from cache
|
||||
$pricing = Cache::rememberForever('pricing', function () {
|
||||
return $this->stripe->getActivePlans();
|
||||
});
|
||||
|
||||
// Format pricing to collection
|
||||
$collection = new PricingCollection($pricing);
|
||||
|
||||
// Sort and return pricing
|
||||
return $collection
|
||||
->sortBy('product.metadata.capacity')
|
||||
->values()
|
||||
->all();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get language translations for frontend app
|
||||
*/
|
||||
public function get_translations($lang)
|
||||
{
|
||||
$translations = cache()
|
||||
->rememberForever("language-translations-$lang", function () use ($lang) {
|
||||
try {
|
||||
return Language::whereLocale($lang)
|
||||
->firstOrFail()
|
||||
->languageTranslations;
|
||||
} catch (QueryException | ModelNotFoundException $e) {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
return $translations
|
||||
? map_language_translations($translations)
|
||||
: get_default_language_translations();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
namespace App\Http\Mail;
|
||||
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Mail\Mailable;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class SendContactMessage extends Mailable
|
||||
{
|
||||
use Queueable, SerializesModels;
|
||||
private $request;
|
||||
|
||||
/**
|
||||
* Create a new message instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($request)
|
||||
{
|
||||
$this->request = $request;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the message.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function build()
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
namespace App\Http\Requests\PublicPages;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
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',
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
<?php
|
||||
namespace App\Http\Controllers\FileManager;
|
||||
|
||||
use Exception;
|
||||
use App\Models\File;
|
||||
use App\Models\Folder;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Services\DemoService;
|
||||
use App\Services\HelperService;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Services\FileManagerService;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Contracts\Routing\ResponseFactory;
|
||||
use App\Http\Requests\FileFunctions\UploadRequest;
|
||||
use App\Http\Requests\FileFunctions\MoveItemRequest;
|
||||
use App\Http\Requests\FileFunctions\DeleteItemRequest;
|
||||
use App\Http\Requests\FileFunctions\RenameItemRequest;
|
||||
use App\Http\Requests\FileFunctions\CreateFolderRequest;
|
||||
|
||||
class EditItemsController extends Controller
|
||||
{
|
||||
private $filemanager;
|
||||
private $helper;
|
||||
private $demo;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->filemanager = resolve(FileManagerService::class);
|
||||
$this->helper = resolve(HelperService::class);
|
||||
$this->demo = resolve(DemoService::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new folder for authenticated master|editor user
|
||||
*
|
||||
* @param CreateFolderRequest $request
|
||||
* @return Folder|array|Model
|
||||
* @throws Exception
|
||||
*/
|
||||
public function create_folder(CreateFolderRequest $request)
|
||||
{
|
||||
if (is_demo_account('howdy@hi5ve.digital')) {
|
||||
return $this->demo->create_folder($request);
|
||||
}
|
||||
|
||||
// Create new folder
|
||||
return $this->filemanager->create_folder($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rename item for authenticated master|editor user
|
||||
*
|
||||
* @param RenameItemRequest $request
|
||||
* @param $id
|
||||
* @return mixed
|
||||
* @throws Exception
|
||||
*/
|
||||
public function rename_item(RenameItemRequest $request, $id)
|
||||
{
|
||||
if (is_demo_account('howdy@hi5ve.digital')) {
|
||||
return $this->demo->rename_item($request, $id);
|
||||
}
|
||||
|
||||
// If request contain icon or color, then change it
|
||||
if ($request->filled('emoji') || $request->filled('color')) {
|
||||
$this->filemanager->edit_folder_properties($request, $id);
|
||||
}
|
||||
|
||||
// Rename Item
|
||||
return $this->filemanager->rename_item($request, $id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete item for authenticated master|editor user
|
||||
*
|
||||
* @param DeleteItemRequest $request
|
||||
* @return ResponseFactory|\Illuminate\Http\Response
|
||||
* @throws Exception
|
||||
*/
|
||||
public function delete_item(DeleteItemRequest $request)
|
||||
{
|
||||
abort_if(is_demo_account('howdy@hi5ve.digital'), 204, 'Done.');
|
||||
|
||||
foreach ($request->input('items') as $item) {
|
||||
$this->filemanager->delete_item($item, $item['id']);
|
||||
}
|
||||
|
||||
return response('Done', 204);
|
||||
}
|
||||
|
||||
/**
|
||||
* Upload file for authenticated master|editor user
|
||||
*
|
||||
* @param UploadRequest $request
|
||||
* @return array|Model|\Illuminate\Support\Facades\File
|
||||
* @throws Exception
|
||||
*/
|
||||
public function upload(UploadRequest $request)
|
||||
{
|
||||
if (is_demo_account('howdy@hi5ve.digital')) {
|
||||
return $this->demo->upload($request);
|
||||
}
|
||||
|
||||
return $this->filemanager->upload($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Move item for authenticated master|editor user
|
||||
*
|
||||
* @param MoveItemRequest $request
|
||||
* @return ResponseFactory|\Illuminate\Http\Response
|
||||
*/
|
||||
public function move(MoveItemRequest $request)
|
||||
{
|
||||
abort_if(is_demo_account('howdy@hi5ve.digital'), 204, 'Done.');
|
||||
|
||||
$this->filemanager->move($request, $request->to_id);
|
||||
|
||||
return response('Done!', 204);
|
||||
}
|
||||
|
||||
/**
|
||||
* User download folder via zip
|
||||
*
|
||||
* @param $id
|
||||
* @return string
|
||||
* @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
|
||||
*/
|
||||
public function zip_folder($id)
|
||||
{
|
||||
$folder = Folder::whereUserId(Auth::id())
|
||||
->where('id', $id);
|
||||
|
||||
if (! $folder->exists()) {
|
||||
abort(404, "Requested folder doesn't exists.");
|
||||
}
|
||||
|
||||
$zip = $this->filemanager->zip_folder($id);
|
||||
|
||||
return response([
|
||||
'url' => route('zip', $zip->id),
|
||||
'name' => $zip->basename,
|
||||
], 201);
|
||||
}
|
||||
|
||||
/**
|
||||
* User download multiple files via zip
|
||||
*
|
||||
* @param Request $request
|
||||
* @return string
|
||||
* @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
|
||||
*/
|
||||
public function zip_multiple_files(Request $request)
|
||||
{
|
||||
$files = File::whereUserId(Auth::id())
|
||||
->whereIn('id', $request->input('items'))
|
||||
->get();
|
||||
|
||||
$zip = $this->filemanager->zip_files($files);
|
||||
|
||||
return response([
|
||||
'url' => route('zip', $zip->id),
|
||||
'name' => $zip->basename,
|
||||
], 201);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
namespace App\Http\Requests\FileFunctions;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class DeleteItemRequest 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 [
|
||||
'data[*].force_delete' => 'required|boolean',
|
||||
'data[*].type' => 'required|string',
|
||||
'data[*].id' => 'required|integer',
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
namespace App\Http\Requests\FileFunctions;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class MoveItemRequest 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 [
|
||||
'to_id' => 'nullable|uuid',
|
||||
'items[*].type' => 'required|string',
|
||||
'items[*].id' => 'required|uuid',
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
namespace App\Http\Requests\FileFunctions;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class RenameItemRequest 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',
|
||||
'type' => 'required|string',
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
namespace App\Http\Requests\FileBrowser;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class SearchRequest 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 [
|
||||
'query' => 'required|string',
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
<?php
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Models\Setting;
|
||||
use App\Models\Language;
|
||||
use Illuminate\Http\Response;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Resources\LanguageResource;
|
||||
use App\Http\Resources\LanguageCollection;
|
||||
use Illuminate\Contracts\Foundation\Application;
|
||||
use Illuminate\Contracts\Routing\ResponseFactory;
|
||||
use App\Http\Requests\Languages\UpdateStringRequest;
|
||||
use App\Http\Requests\Languages\CreateLanguageRequest;
|
||||
use App\Http\Requests\Languages\UpdateLanguageRequest;
|
||||
|
||||
class LanguageController extends Controller
|
||||
{
|
||||
/**
|
||||
* Get all languages for admin translate
|
||||
*
|
||||
* @return array|Application|ResponseFactory|Response
|
||||
*/
|
||||
public function get_languages()
|
||||
{
|
||||
return response(
|
||||
new LanguageCollection(Language::sortable(['created_at', 'DESC'])->get()),
|
||||
200
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all language strings for admin translate
|
||||
*
|
||||
* @param Language $language
|
||||
*/
|
||||
public function get_language(Language $language)
|
||||
{
|
||||
return response(
|
||||
new LanguageResource($language),
|
||||
200
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new language
|
||||
*
|
||||
* @param CreateLanguageRequest $request
|
||||
* @return string
|
||||
*/
|
||||
public function create_language(CreateLanguageRequest $request)
|
||||
{
|
||||
// Abort in demo mode
|
||||
abort_if(is_demo(), 204, 'Done.');
|
||||
|
||||
$language = Language::create([
|
||||
'name' => $request->input('name'),
|
||||
'locale' => $request->input('locale'),
|
||||
]);
|
||||
|
||||
return response(
|
||||
new LanguageResource($language),
|
||||
201
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update language
|
||||
*
|
||||
* @param UpdateLanguageRequest $request
|
||||
* @param Language $language
|
||||
*/
|
||||
public function update_language(UpdateLanguageRequest $request, Language $language)
|
||||
{
|
||||
// Abort in demo mode
|
||||
abort_if(is_demo(), 204, 'Done.');
|
||||
|
||||
$language->update(make_single_input($request));
|
||||
|
||||
return response(
|
||||
new LanguageResource($language),
|
||||
201
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update string for language
|
||||
*
|
||||
* @param UpdateStringRequest $request
|
||||
* @param Language $language
|
||||
* @return Application|ResponseFactory|Response
|
||||
*/
|
||||
public function update_string(UpdateStringRequest $request, Language $language)
|
||||
{
|
||||
// Abort in demo mode
|
||||
abort_if(is_demo(), 204, 'Done.');
|
||||
|
||||
$language
|
||||
->languageTranslations()
|
||||
->where('key', $request->name)
|
||||
->update([
|
||||
'value' => $request->value,
|
||||
]);
|
||||
|
||||
cache()->forget("language-translations-{$language->locale}");
|
||||
|
||||
return response(
|
||||
'Done',
|
||||
204
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the language with all children strings
|
||||
* @param Language $language
|
||||
* @return Response
|
||||
*/
|
||||
public function delete_language(Language $language): Response
|
||||
{
|
||||
// Abort in demo mode
|
||||
abort_if(is_demo(), 204, 'Done.');
|
||||
|
||||
if ($language->locale === 'en') {
|
||||
return response("Sorry, you can't delete default language.", 401);
|
||||
}
|
||||
|
||||
// If user try to delete language used as default,
|
||||
// then set en language as default
|
||||
if ($language->locale === get_setting('language')) {
|
||||
Setting::whereName('language')->first()
|
||||
->update(['value' => 'en']);
|
||||
}
|
||||
|
||||
$language->delete();
|
||||
|
||||
return response(
|
||||
'Done',
|
||||
204
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Support\Str;
|
||||
use App\Services\LanguageService;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Kyslik\ColumnSortable\Sortable;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
/**
|
||||
* @method static whereLocale(string $param)
|
||||
*/
|
||||
class Language extends Model
|
||||
{
|
||||
use Sortable;
|
||||
|
||||
public $sortable = [
|
||||
'created_at',
|
||||
];
|
||||
|
||||
protected $guarded = [
|
||||
'id',
|
||||
];
|
||||
|
||||
protected $keyType = 'string';
|
||||
|
||||
protected $primaryKey = 'id';
|
||||
|
||||
public $incrementing = false;
|
||||
|
||||
public function languageTranslations()
|
||||
{
|
||||
return $this->hasMany(LanguageTranslation::class, 'lang', 'locale');
|
||||
}
|
||||
|
||||
protected static function boot()
|
||||
{
|
||||
parent::boot();
|
||||
|
||||
static::creating(function ($language) {
|
||||
$language->id = Str::uuid();
|
||||
|
||||
resolve(LanguageService::class)
|
||||
->create_default_language_translations(
|
||||
get_setting('license') ?? 'extended',
|
||||
$language->locale
|
||||
);
|
||||
});
|
||||
|
||||
static::updating(function ($language) {
|
||||
cache()->forget("language-translations-$language->locale");
|
||||
});
|
||||
|
||||
static::deleting(function ($language) {
|
||||
DB::table('language_translations')
|
||||
->whereLang($language->locale)
|
||||
->delete();
|
||||
|
||||
cache()->forget("language-translations-$language->locale");
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
/**
|
||||
* @method static whereLang(string $string)
|
||||
*/
|
||||
class LanguageTranslation extends Model
|
||||
{
|
||||
public $timestamps = false;
|
||||
|
||||
public $primaryKey = null;
|
||||
|
||||
public $incrementing = false;
|
||||
|
||||
protected $fillable = [
|
||||
'value',
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
namespace App\Http\Requests\Languages;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class CreateLanguageRequest 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',
|
||||
'locale' => 'required|string',
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
namespace App\Http\Requests\Languages;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class UpdateLanguageRequest 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',
|
||||
'value' => 'required|string',
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
namespace App\Http\Requests\Languages;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class UpdateStringRequest 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',
|
||||
'value' => 'required|string',
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
namespace App\Http\Resources;
|
||||
|
||||
use App\Models\Language;
|
||||
use Illuminate\Http\Resources\Json\ResourceCollection;
|
||||
|
||||
class LanguageCollection extends ResourceCollection
|
||||
{
|
||||
public $collects = LanguageResource::class;
|
||||
|
||||
/**
|
||||
* Transform the resource collection into an array.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return array
|
||||
*/
|
||||
public function toArray($request)
|
||||
{
|
||||
$current_language = Language::with('languageTranslations')
|
||||
->whereLocale(get_setting('language') ?? 'en')
|
||||
->first();
|
||||
|
||||
return [
|
||||
'data' => $this->collection,
|
||||
'meta' => [
|
||||
'current_language' => new LanguageResource($current_language),
|
||||
'reference_translations' => get_default_language_translations(),
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
namespace App\Http\Resources;
|
||||
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
class LanguageResource 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' => 'languages',
|
||||
'attributes' => [
|
||||
'name' => $this->name,
|
||||
'locale' => $this->locale,
|
||||
'translations' => map_language_translations($this->languageTranslations),
|
||||
'updated_at' => $this->updated_at,
|
||||
'created_at' => $this->created_at,
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
<?php
|
||||
namespace App\Services;
|
||||
|
||||
use DB;
|
||||
use App\Models\Language;
|
||||
use App\Models\LanguageTranslation;
|
||||
|
||||
class LanguageService
|
||||
{
|
||||
/**
|
||||
* @param $license
|
||||
* @param $locale
|
||||
*/
|
||||
public function create_default_language_translations($license, $locale)
|
||||
{
|
||||
$translations = [
|
||||
'extended' => collect([
|
||||
config('language-translations.extended'),
|
||||
config('language-translations.regular'),
|
||||
config('custom-language-translations'),
|
||||
])->collapse(),
|
||||
'regular' => collect([
|
||||
config('language-translations.regular'),
|
||||
config('custom-language-translations'),
|
||||
])->collapse(),
|
||||
];
|
||||
|
||||
$translations = $translations[strtolower($license)]
|
||||
->map(function ($value, $key) use ($locale) {
|
||||
return [
|
||||
'lang' => $locale,
|
||||
'value' => $value,
|
||||
'key' => $key,
|
||||
];
|
||||
})->toArray();
|
||||
|
||||
$chunks = array_chunk($translations, 100);
|
||||
|
||||
foreach ($chunks as $chunk) {
|
||||
DB::table('language_translations')
|
||||
->insert($chunk);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find newly added translations in default language
|
||||
* translations file and insert it into database
|
||||
*/
|
||||
public function upgrade_language_translations()
|
||||
{
|
||||
// Get all app locales
|
||||
$locales = Language::all()
|
||||
->pluck('locale');
|
||||
|
||||
// Get default translations
|
||||
$translations = LanguageTranslation::whereLang('en')
|
||||
->get();
|
||||
|
||||
$default_translations = [
|
||||
'extended' => collect([
|
||||
config('language-translations.extended'),
|
||||
config('language-translations.regular'),
|
||||
config('custom-language-translations'),
|
||||
])->collapse(),
|
||||
'regular' => collect([
|
||||
config('language-translations.regular'),
|
||||
config('custom-language-translations'),
|
||||
])->collapse(),
|
||||
];
|
||||
|
||||
$license = strtolower(get_setting('license'));
|
||||
|
||||
// Find new translations in default translations
|
||||
$newbies = $default_translations[$license]
|
||||
->diffKeys(map_language_translations($translations));
|
||||
|
||||
// Store new translations for every language
|
||||
$locales->each(function ($locale) use ($newbies) {
|
||||
$translations = $newbies
|
||||
->map(function ($value, $key) use ($locale) {
|
||||
return [
|
||||
'lang' => $locale,
|
||||
'value' => $value,
|
||||
'key' => $key,
|
||||
];
|
||||
})->toArray();
|
||||
|
||||
$chunks = array_chunk($translations, 100);
|
||||
|
||||
foreach ($chunks as $chunk) {
|
||||
// Store translations into database
|
||||
DB::table('language_translations')
|
||||
->insert($chunk);
|
||||
}
|
||||
|
||||
// Flush cache
|
||||
cache()->forget("language-translations-$locale");
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
namespace App\Http\Controllers\App;
|
||||
|
||||
use Gate;
|
||||
use Artisan;
|
||||
use App\Models\Language;
|
||||
use Illuminate\Http\Response;
|
||||
use App\Services\LanguageService;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Contracts\Foundation\Application;
|
||||
use Illuminate\Contracts\Routing\ResponseFactory;
|
||||
|
||||
class MaintenanceController extends Controller
|
||||
{
|
||||
/**
|
||||
* Start maintenance mode
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
// Check admin permission
|
||||
Gate::authorize('maintenance');
|
||||
|
||||
$command = Artisan::call('up');
|
||||
|
||||
if ($command === 0) {
|
||||
echo 'System is in production mode';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* End maintenance mode
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
// Check admin permission
|
||||
Gate::authorize('maintenance');
|
||||
|
||||
$command = Artisan::call('down');
|
||||
|
||||
if ($command === 0) {
|
||||
echo 'System is in maintenance mode';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get new language translations from default translations
|
||||
* and insert it into database
|
||||
*
|
||||
* @return Application|ResponseFactory|Response
|
||||
*/
|
||||
public function upgrade_translations()
|
||||
{
|
||||
// Check admin permission
|
||||
Gate::authorize('maintenance');
|
||||
|
||||
resolve(LanguageService::class)
|
||||
->upgrade_language_translations();
|
||||
|
||||
return response('Done.', 201);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int|mixed
|
||||
*/
|
||||
public function upgrade_database()
|
||||
{
|
||||
// Check admin permission
|
||||
Gate::authorize('maintenance');
|
||||
|
||||
$command = Artisan::call('migrate', [
|
||||
'--force' => true,
|
||||
]);
|
||||
|
||||
if ($command === 0) {
|
||||
echo 'Operation was successful.';
|
||||
}
|
||||
|
||||
if ($command === 1) {
|
||||
echo 'Operation failed.';
|
||||
}
|
||||
|
||||
return $command;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Models\Page;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Services\DemoService;
|
||||
use Illuminate\Http\Response;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Resources\PageResource;
|
||||
use App\Http\Resources\PageCollection;
|
||||
use Illuminate\Contracts\Routing\ResponseFactory;
|
||||
|
||||
class PagesController extends Controller
|
||||
{
|
||||
private $demo;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->demo = resolve(DemoService::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all pages
|
||||
*
|
||||
* @return PageCollection
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
return new PageCollection(
|
||||
Page::sortable()
|
||||
->paginate(10)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get single page resource
|
||||
*
|
||||
* @param $page
|
||||
* @return PageResource
|
||||
*/
|
||||
public function show(Page $page)
|
||||
{
|
||||
return new PageResource($page);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update page content
|
||||
*
|
||||
* @param Request $request
|
||||
* @param Page $page
|
||||
* @return ResponseFactory|Response
|
||||
*/
|
||||
public function update(Request $request, Page $page)
|
||||
{
|
||||
// Abort in demo mode
|
||||
abort_if(is_demo(), 204, 'Done.');
|
||||
|
||||
$page->update(
|
||||
make_single_input($request)
|
||||
);
|
||||
|
||||
return response(new PageResource($page), 204);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
namespace App\Models;
|
||||
|
||||
use Kyslik\ColumnSortable\Sortable;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
|
||||
class Page extends Model
|
||||
{
|
||||
use Sortable, HasFactory;
|
||||
|
||||
/**
|
||||
* Sortable columns
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
public $sortable = [
|
||||
'title',
|
||||
'slug',
|
||||
'visibility',
|
||||
];
|
||||
|
||||
public $fillable = [
|
||||
'slug',
|
||||
'title',
|
||||
'visibility',
|
||||
'content',
|
||||
];
|
||||
|
||||
protected $primaryKey = 'slug';
|
||||
|
||||
protected $keyType = 'string';
|
||||
|
||||
public $timestamps = false;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
namespace App\Http\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,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
namespace App\Http\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),
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
<?php
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Models\Plan;
|
||||
use App\Models\User;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Services\DemoService;
|
||||
use Illuminate\Http\Response;
|
||||
use App\Services\StripeService;
|
||||
use Laravel\Cashier\Subscription;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Resources\PlanResource;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use App\Http\Resources\PlanCollection;
|
||||
use App\Http\Resources\UsersCollection;
|
||||
use Illuminate\Contracts\Foundation\Application;
|
||||
use Illuminate\Contracts\Routing\ResponseFactory;
|
||||
|
||||
class PlansController extends Controller
|
||||
{
|
||||
private StripeService $stripe;
|
||||
|
||||
private DemoService $demo;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->stripe = resolve(StripeService::class);
|
||||
$this->demo = resolve(DemoService::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all plans
|
||||
*
|
||||
* @return PlanCollection|Application|ResponseFactory|Response
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
// Store or Get plans to cache
|
||||
if (Cache::has('plans')) {
|
||||
$plans = Cache::get('plans');
|
||||
} else {
|
||||
$plans = Cache::rememberForever('plans', function () {
|
||||
return $this->stripe->getPlans();
|
||||
});
|
||||
}
|
||||
|
||||
return response(new PlanCollection($plans), 200);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get plan record
|
||||
*
|
||||
* @param $id
|
||||
* @return PlanResource|Application|ResponseFactory|Response
|
||||
*/
|
||||
public function show($id)
|
||||
{
|
||||
// Store or Get plan to cache
|
||||
if (Cache::has('plan-' . $id)) {
|
||||
$plan = Cache::get('plan-' . $id);
|
||||
} else {
|
||||
$plan = Cache::rememberForever('plan-' . $id, function () use ($id) {
|
||||
return $this->stripe->getPlan($id);
|
||||
});
|
||||
}
|
||||
|
||||
return response(new PlanResource($plan), 200);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new plan
|
||||
*
|
||||
* @param Request $request
|
||||
* @return PlanResource|Application|ResponseFactory|Response
|
||||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
// TODO: inline request
|
||||
if (is_demo()) {
|
||||
if (Cache::has('plan-starter-pack')) {
|
||||
$plan = Cache::get('plan-starter-pack');
|
||||
} else {
|
||||
$plan = Cache::rememberForever('plan-starter-pack', function () {
|
||||
return $this->stripe->getPlan('starter-pack');
|
||||
});
|
||||
}
|
||||
|
||||
return new PlanResource($plan);
|
||||
}
|
||||
|
||||
$plan = new PlanResource(
|
||||
$this->stripe->createPlan($request)
|
||||
);
|
||||
|
||||
// Clear cached plans
|
||||
cache_forget_many(['plans', 'pricing']);
|
||||
|
||||
return response($plan, 201);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update plan attribute
|
||||
*
|
||||
* @param Request $request
|
||||
* @param $id
|
||||
* @return ResponseFactory|Response
|
||||
*/
|
||||
public function update(Request $request, $id)
|
||||
{
|
||||
// Abort in demo mode
|
||||
abort_if(is_demo(), 204, 'Done.');
|
||||
|
||||
// Update plan
|
||||
$this->stripe->updatePlan($request, $id);
|
||||
|
||||
// Clear cached plans
|
||||
cache_forget_many(['plans', 'pricing', 'plan-' . $id]);
|
||||
|
||||
return response('Saved!', 201);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete plan
|
||||
*
|
||||
* @param $id
|
||||
* @return ResponseFactory|Response
|
||||
*/
|
||||
public function delete($id)
|
||||
{
|
||||
// Abort in demo mode
|
||||
abort_if(is_demo(), 204, 'Done.');
|
||||
|
||||
// Delete plan
|
||||
$this->stripe->deletePlan($id);
|
||||
|
||||
// Clear cached plans
|
||||
cache_forget_many(['plans', 'pricing']);
|
||||
|
||||
return response('Done!', 204);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get subscriptions
|
||||
*
|
||||
* @param $id
|
||||
* @return mixed
|
||||
*/
|
||||
public function subscribers($id)
|
||||
{
|
||||
$subscribers = Subscription::whereStripePlan($id)
|
||||
->pluck('user_id');
|
||||
|
||||
return new UsersCollection(
|
||||
User::sortable()
|
||||
->findMany($subscribers)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
<?php
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use Stripe;
|
||||
use Artisan;
|
||||
use App\Models\Setting;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Services\DemoService;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Cartalyst\Stripe\Exception\UnauthorizedException;
|
||||
use Symfony\Component\HttpKernel\Exception\HttpException;
|
||||
|
||||
class SettingController extends Controller
|
||||
{
|
||||
private $demo;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->demo = resolve(DemoService::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get table content
|
||||
*
|
||||
* @param Request $request
|
||||
* @return mixed
|
||||
*/
|
||||
public function show(Request $request)
|
||||
{
|
||||
if (strpos($request->column, '|') !== false) {
|
||||
$columns = explode('|', $request->column);
|
||||
|
||||
return Setting::whereIn('name', $columns)
|
||||
->pluck('value', 'name');
|
||||
}
|
||||
|
||||
return Setting::where('name', $request->column)
|
||||
->pluck('value', 'name');
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function update(Request $request)
|
||||
{
|
||||
// Abort in demo mode
|
||||
abort_if(is_demo(), 204, 'Done.');
|
||||
|
||||
// Store image if exist
|
||||
if ($request->hasFile($request->name)) {
|
||||
// Find and update image path
|
||||
Setting::updateOrCreate([
|
||||
'name' => $request->name,
|
||||
], [
|
||||
'value' => store_system_image($request, $request->name),
|
||||
]);
|
||||
|
||||
return response('Done', 204);
|
||||
}
|
||||
|
||||
// Find and update variable
|
||||
Setting::updateOrCreate(
|
||||
['name' => $request->name],
|
||||
['value' => $request->value]
|
||||
);
|
||||
|
||||
return response('Done', 204);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set new email credentials to .env file
|
||||
*
|
||||
* @param Request $request
|
||||
* @return \Illuminate\Contracts\Routing\ResponseFactory|\Illuminate\Http\Response
|
||||
*/
|
||||
public function set_email(Request $request)
|
||||
{
|
||||
// TODO: pridat validator do requestu
|
||||
// Abort in demo mode
|
||||
abort_if(is_demo(), 204, 'Done.');
|
||||
|
||||
if (! app()->runningUnitTests()) {
|
||||
setEnvironmentValue([
|
||||
'MAIL_DRIVER' => $request->driver,
|
||||
'MAIL_HOST' => $request->host,
|
||||
'MAIL_PORT' => $request->port,
|
||||
'MAIL_USERNAME' => $request->username,
|
||||
'MAIL_PASSWORD' => $request->password,
|
||||
'MAIL_ENCRYPTION' => $request->encryption,
|
||||
]);
|
||||
|
||||
// Clear config cache
|
||||
Artisan::call('config:clear');
|
||||
Artisan::call('config:cache');
|
||||
}
|
||||
|
||||
return response('Done', 204);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure stripe additionally
|
||||
*
|
||||
* @param Request $request
|
||||
*/
|
||||
public function set_stripe(Request $request)
|
||||
{
|
||||
// TODO: pridat validator do requestu
|
||||
// Check payment setup status
|
||||
if (get_setting('payments_configured')) {
|
||||
abort(401, 'Gone');
|
||||
}
|
||||
|
||||
// Try to get stripe account details
|
||||
try {
|
||||
if (! app()->runningUnitTests()) {
|
||||
Stripe::make($request->secret, '2020-03-02')
|
||||
->account()
|
||||
->details();
|
||||
}
|
||||
} catch (UnauthorizedException $e) {
|
||||
throw new HttpException(401, $e->getMessage());
|
||||
}
|
||||
|
||||
// Get options
|
||||
collect([
|
||||
[
|
||||
'name' => 'stripe_currency',
|
||||
'value' => $request->currency,
|
||||
],
|
||||
[
|
||||
'name' => 'payments_configured',
|
||||
'value' => 1,
|
||||
],
|
||||
[
|
||||
'name' => 'payments_active',
|
||||
'value' => 1,
|
||||
],
|
||||
])->each(function ($col) {
|
||||
Setting::forceCreate([
|
||||
'name' => $col['name'],
|
||||
'value' => $col['value'],
|
||||
]);
|
||||
});
|
||||
|
||||
if (! app()->runningUnitTests()) {
|
||||
// Set stripe credentials to .env
|
||||
setEnvironmentValue([
|
||||
'CASHIER_CURRENCY' => $request->currency,
|
||||
'STRIPE_KEY' => $request->key,
|
||||
'STRIPE_SECRET' => $request->secret,
|
||||
'STRIPE_WEBHOOK_SECRET' => $request->webhookSecret,
|
||||
]);
|
||||
|
||||
// Clear cache
|
||||
Artisan::call('cache:clear');
|
||||
Artisan::call('config:clear');
|
||||
Artisan::call('config:cache');
|
||||
}
|
||||
|
||||
return response('Done', 204);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear application cache
|
||||
*/
|
||||
public function flush_cache()
|
||||
{
|
||||
// Abort in demo mode
|
||||
abort_if(is_demo(), 204, 'Done.');
|
||||
|
||||
if (! app()->runningUnitTests()) {
|
||||
Artisan::call('cache:clear');
|
||||
Artisan::call('config:clear');
|
||||
Artisan::call('config:cache');
|
||||
}
|
||||
|
||||
return response('Done', 204);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
|
||||
/**
|
||||
* @method static whereName(string $string)
|
||||
*/
|
||||
class Setting extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $fillable = [
|
||||
'value', 'name',
|
||||
];
|
||||
|
||||
public $timestamps = false;
|
||||
|
||||
protected $primaryKey = 'name';
|
||||
|
||||
protected $keyType = 'string';
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
|
||||
class Traffic extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $fillable = [
|
||||
'user_id',
|
||||
'upload',
|
||||
'download',
|
||||
];
|
||||
|
||||
public $incrementing = false;
|
||||
|
||||
protected $keyType = 'string';
|
||||
|
||||
/**
|
||||
* Model events
|
||||
*/
|
||||
protected static function boot()
|
||||
{
|
||||
parent::boot();
|
||||
|
||||
static::creating(function ($model) {
|
||||
$model->id = (string) Str::uuid();
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,479 @@
|
||||
<?php
|
||||
namespace App\Http\Controllers\App;
|
||||
|
||||
use Schema;
|
||||
use Stripe;
|
||||
use Artisan;
|
||||
use App\Models\User;
|
||||
use App\Models\Setting;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Response;
|
||||
use App\Services\SetupService;
|
||||
use App\Services\StripeService;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Doctrine\DBAL\Driver\PDOException;
|
||||
use Illuminate\Contracts\Routing\ResponseFactory;
|
||||
use Cartalyst\Stripe\Exception\UnauthorizedException;
|
||||
use App\Http\Requests\SetupWizard\StoreAppSetupRequest;
|
||||
use Symfony\Component\HttpKernel\Exception\HttpException;
|
||||
use App\Http\Requests\SetupWizard\StoreStripePlansRequest;
|
||||
use App\Http\Requests\SetupWizard\StoreStripeBillingRequest;
|
||||
use App\Http\Requests\SetupWizard\StoreEnvironmentSetupRequest;
|
||||
use App\Http\Requests\SetupWizard\StoreStripeCredentialsRequest;
|
||||
use App\Http\Requests\SetupWizard\StoreDatabaseCredentialsRequest;
|
||||
|
||||
class SetupWizardController extends Controller
|
||||
{
|
||||
/**
|
||||
* Inject Stripe Service
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->stripe = resolve(StripeService::class);
|
||||
$this->setup = resolve(SetupService::class);
|
||||
|
||||
$this->check_setup_status();
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify Envato purchase code
|
||||
*
|
||||
* @param Request $request
|
||||
* @return ResponseFactory|\Illuminate\Http\Response|mixed
|
||||
*/
|
||||
public function verify_purchase_code(Request $request)
|
||||
{
|
||||
// Verify purchase code
|
||||
$response = Http::get('https://verify.vuefilemanager.com/api/verify-code/' . $request->purchaseCode);
|
||||
|
||||
if ($response->successful()) {
|
||||
return response($response, 204);
|
||||
}
|
||||
|
||||
return response('Purchase code is invalid.', 400);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up database credentials
|
||||
*
|
||||
* @param StoreDatabaseCredentialsRequest $request
|
||||
* @return ResponseFactory|\Illuminate\Http\Response
|
||||
*/
|
||||
public function setup_database(StoreDatabaseCredentialsRequest $request)
|
||||
{
|
||||
if (! app()->runningUnitTests()) {
|
||||
try {
|
||||
// Set temporary database connection
|
||||
config(['database.connections.test.driver' => $request->connection]);
|
||||
config(['database.connections.test.host' => $request->host]);
|
||||
config(['database.connections.test.port' => $request->port]);
|
||||
config(['database.connections.test.database' => $request->name]);
|
||||
config(['database.connections.test.username' => $request->username]);
|
||||
config(['database.connections.test.password' => $request->password]);
|
||||
|
||||
// Test connection
|
||||
\DB::connection('test')->getPdo();
|
||||
} catch (PDOException $e) {
|
||||
throw new HttpException(500, $e->getMessage());
|
||||
}
|
||||
|
||||
// TODO: add SANCTUM_STATEFUL_DOMAINS parameter
|
||||
|
||||
setEnvironmentValue([
|
||||
'DB_CONNECTION' => $request->connection,
|
||||
'DB_HOST' => $request->host,
|
||||
'DB_PORT' => $request->port,
|
||||
'DB_DATABASE' => $request->name,
|
||||
'DB_USERNAME' => $request->username,
|
||||
'DB_PASSWORD' => $request->password,
|
||||
]);
|
||||
|
||||
Artisan::call('config:cache');
|
||||
|
||||
Artisan::call('key:generate', [
|
||||
'--force' => true,
|
||||
]);
|
||||
|
||||
Artisan::call('migrate:fresh', [
|
||||
'--force' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
// Store setup wizard progress
|
||||
Setting::forceCreate([
|
||||
'name' => 'setup_wizard_database',
|
||||
'value' => 1,
|
||||
]);
|
||||
|
||||
return response('Done', 204);
|
||||
}
|
||||
|
||||
/**
|
||||
* Store and test stripe credentials
|
||||
*
|
||||
* @param StoreStripeCredentialsRequest $request
|
||||
* @return ResponseFactory|\Illuminate\Http\Response
|
||||
*/
|
||||
public function store_stripe_credentials(StoreStripeCredentialsRequest $request)
|
||||
{
|
||||
if (! app()->runningUnitTests()) {
|
||||
// Create stripe instance
|
||||
$stripe = Stripe::make($request->secret, '2020-03-02');
|
||||
|
||||
try {
|
||||
// Try to get stripe account details
|
||||
$stripe->account()->details();
|
||||
} catch (UnauthorizedException $e) {
|
||||
throw new HttpException(401, $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// Set settings
|
||||
collect([
|
||||
[
|
||||
'name' => 'stripe_currency',
|
||||
'value' => $request->currency,
|
||||
],
|
||||
[
|
||||
'name' => 'payments_configured',
|
||||
'value' => 1,
|
||||
],
|
||||
[
|
||||
'name' => 'payments_active',
|
||||
'value' => 1,
|
||||
],
|
||||
])->each(function ($col) {
|
||||
Setting::forceCreate([
|
||||
'name' => $col['name'],
|
||||
'value' => $col['value'],
|
||||
]);
|
||||
});
|
||||
|
||||
if (! app()->runningUnitTests()) {
|
||||
// Set stripe credentials to .env
|
||||
setEnvironmentValue([
|
||||
'CASHIER_CURRENCY' => $request->currency,
|
||||
'STRIPE_KEY' => $request->key,
|
||||
'STRIPE_SECRET' => $request->secret,
|
||||
'STRIPE_WEBHOOK_SECRET' => $request->webhookSecret,
|
||||
]);
|
||||
|
||||
// Clear cache
|
||||
Artisan::call('config:cache');
|
||||
}
|
||||
|
||||
return response('Done', 204);
|
||||
}
|
||||
|
||||
/**
|
||||
* Store Stripe billings
|
||||
*
|
||||
* @param StoreStripeBillingRequest $request
|
||||
* @return ResponseFactory|\Illuminate\Http\Response
|
||||
*/
|
||||
public function store_stripe_billings(StoreStripeBillingRequest $request)
|
||||
{
|
||||
// Get options
|
||||
collect([
|
||||
[
|
||||
'name' => 'billing_phone_number',
|
||||
'value' => $request->billing_phone_number,
|
||||
],
|
||||
[
|
||||
'name' => 'billing_postal_code',
|
||||
'value' => $request->billing_postal_code,
|
||||
],
|
||||
[
|
||||
'name' => 'billing_vat_number',
|
||||
'value' => $request->billing_vat_number,
|
||||
],
|
||||
[
|
||||
'name' => 'billing_address',
|
||||
'value' => $request->billing_address,
|
||||
],
|
||||
[
|
||||
'name' => 'billing_country',
|
||||
'value' => $request->billing_country,
|
||||
],
|
||||
[
|
||||
'name' => 'billing_state',
|
||||
'value' => $request->billing_state,
|
||||
],
|
||||
[
|
||||
'name' => 'billing_city',
|
||||
'value' => $request->billing_city,
|
||||
],
|
||||
[
|
||||
'name' => 'billing_name',
|
||||
'value' => $request->billing_name,
|
||||
],
|
||||
])->each(function ($col) {
|
||||
Setting::forceCreate([
|
||||
'name' => $col['name'],
|
||||
'value' => $col['value'],
|
||||
]);
|
||||
});
|
||||
|
||||
if (! app()->runningUnitTests()) {
|
||||
Artisan::call('config:cache');
|
||||
}
|
||||
|
||||
return response('Done', 204);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create Stripe subscription plan
|
||||
*
|
||||
* @param StoreStripePlansRequest $request
|
||||
* @return \Illuminate\Contracts\Foundation\Application|ResponseFactory|\Illuminate\Http\Response
|
||||
*/
|
||||
public function store_stripe_plans(StoreStripePlansRequest $request)
|
||||
{
|
||||
foreach ($request->plans as $plan) {
|
||||
$this->stripe->createPlan($plan);
|
||||
}
|
||||
|
||||
return response('Done', 204);
|
||||
}
|
||||
|
||||
/**
|
||||
* Store environment setup
|
||||
*
|
||||
* @param StoreEnvironmentSetupRequest $request
|
||||
* @return string
|
||||
*/
|
||||
public function store_environment_setup(StoreEnvironmentSetupRequest $request)
|
||||
{
|
||||
if (! app()->runningUnitTests()) {
|
||||
$drivers = [
|
||||
'local' => [
|
||||
'FILESYSTEM_DRIVER' => 'local',
|
||||
],
|
||||
's3' => [
|
||||
'FILESYSTEM_DRIVER' => $request->storage['driver'] ?? null,
|
||||
'AWS_ACCESS_KEY_ID' => $request->storage['key'] ?? null,
|
||||
'AWS_SECRET_ACCESS_KEY' => $request->storage['secret'] ?? null,
|
||||
'AWS_DEFAULT_REGION' => $request->storage['region'] ?? null,
|
||||
'AWS_BUCKET' => $request->storage['bucket'] ?? null,
|
||||
],
|
||||
'spaces' => [
|
||||
'FILESYSTEM_DRIVER' => $request->storage['driver'] ?? null,
|
||||
'DO_SPACES_KEY' => $request->storage['key'] ?? null,
|
||||
'DO_SPACES_SECRET' => $request->storage['secret'] ?? null,
|
||||
'DO_SPACES_ENDPOINT' => $request->storage['endpoint'] ?? null,
|
||||
'DO_SPACES_REGION' => $request->storage['region'] ?? null,
|
||||
'DO_SPACES_BUCKET' => $request->storage['bucket'] ?? null,
|
||||
],
|
||||
'wasabi' => [
|
||||
'FILESYSTEM_DRIVER' => $request->storage['driver'] ?? null,
|
||||
'WASABI_KEY' => $request->storage['key'] ?? null,
|
||||
'WASABI_SECRET' => $request->storage['secret'] ?? null,
|
||||
'WASABI_ENDPOINT' => $request->storage['endpoint'] ?? null,
|
||||
'WASABI_REGION' => $request->storage['region'] ?? null,
|
||||
'WASABI_BUCKET' => $request->storage['bucket'] ?? null,
|
||||
],
|
||||
'backblaze' => [
|
||||
'FILESYSTEM_DRIVER' => $request->storage['driver'] ?? null,
|
||||
'BACKBLAZE_KEY' => $request->storage['key'] ?? null,
|
||||
'BACKBLAZE_SECRET' => $request->storage['secret'] ?? null,
|
||||
'BACKBLAZE_ENDPOINT' => $request->storage['endpoint'] ?? null,
|
||||
'BACKBLAZE_REGION' => $request->storage['region'] ?? null,
|
||||
'BACKBLAZE_BUCKET' => $request->storage['bucket'] ?? null,
|
||||
],
|
||||
'oss' => [
|
||||
'FILESYSTEM_DRIVER' => $request->storage['driver'] ?? null,
|
||||
'OSS_ACCESS_KEY_ID' => $request->storage['key'] ?? null,
|
||||
'OSS_SECRET_ACCESS_KEY' => $request->storage['secret'] ?? null,
|
||||
'OSS_ENDPOINT' => $request->storage['endpoint'] ?? null,
|
||||
'OSS_REGION' => $request->storage['region'] ?? null,
|
||||
'OSS_BUCKET' => $request->storage['bucket'] ?? null,
|
||||
],
|
||||
];
|
||||
|
||||
// Storage credentials for storage
|
||||
setEnvironmentValue(
|
||||
$drivers[$request->storage['driver']]
|
||||
);
|
||||
|
||||
// Store credentials for mail
|
||||
// TODO: add options for mailgun
|
||||
setEnvironmentValue([
|
||||
'MAIL_DRIVER' => $request->mail['driver'],
|
||||
'MAIL_HOST' => $request->mail['host'],
|
||||
'MAIL_PORT' => $request->mail['port'],
|
||||
'MAIL_USERNAME' => $request->mail['username'],
|
||||
'MAIL_PASSWORD' => $request->mail['password'],
|
||||
'MAIL_ENCRYPTION' => $request->mail['encryption'],
|
||||
]);
|
||||
|
||||
Artisan::call('config:cache');
|
||||
}
|
||||
|
||||
return response('Done', 204);
|
||||
}
|
||||
|
||||
/**
|
||||
* Store app settings
|
||||
* @param StoreAppSetupRequest $request
|
||||
* @return ResponseFactory|\Illuminate\Http\Response
|
||||
*/
|
||||
public function store_app_settings(StoreAppSetupRequest $request)
|
||||
{
|
||||
// Get options
|
||||
collect([
|
||||
[
|
||||
'name' => 'app_title',
|
||||
'value' => $request->title,
|
||||
],
|
||||
[
|
||||
'name' => 'app_description',
|
||||
'value' => $request->description,
|
||||
],
|
||||
[
|
||||
'name' => 'app_logo',
|
||||
'value' => store_system_image($request, 'logo'),
|
||||
],
|
||||
[
|
||||
'name' => 'app_logo_horizontal',
|
||||
'value' => store_system_image($request, 'logo_horizontal'),
|
||||
],
|
||||
[
|
||||
'name' => 'app_favicon',
|
||||
'value' => store_system_image($request, 'favicon'),
|
||||
],
|
||||
[
|
||||
'name' => 'app_og_image',
|
||||
'value' => store_system_image($request, 'og_image'),
|
||||
],
|
||||
[
|
||||
'name' => 'app_touch_icon',
|
||||
'value' => store_system_image($request, 'touch_icon'),
|
||||
],
|
||||
[
|
||||
'name' => 'google_analytics',
|
||||
'value' => $request->googleAnalytics,
|
||||
],
|
||||
[
|
||||
'name' => 'contact_email',
|
||||
'value' => $request->contactMail,
|
||||
],
|
||||
[
|
||||
'name' => 'registration',
|
||||
'value' => $request->userRegistration,
|
||||
],
|
||||
[
|
||||
'name' => 'storage_limitation',
|
||||
'value' => $request->storageLimitation,
|
||||
],
|
||||
[
|
||||
'name' => 'storage_default',
|
||||
'value' => $request->defaultStorage ?? 5,
|
||||
],
|
||||
])->each(function ($col) {
|
||||
Setting::forceCreate([
|
||||
'name' => $col['name'],
|
||||
'value' => $col['value'],
|
||||
]);
|
||||
});
|
||||
|
||||
if (! app()->runningUnitTests()) {
|
||||
setEnvironmentValue([
|
||||
'APP_NAME' => Str::camel($request->title),
|
||||
]);
|
||||
}
|
||||
|
||||
return response('Done', 204);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and login admin account
|
||||
*
|
||||
* @param Request $request
|
||||
* @return ResponseFactory|\Illuminate\Http\Response|\Symfony\Component\HttpFoundation\Response
|
||||
*/
|
||||
public function create_admin_account(Request $request)
|
||||
{
|
||||
// Validate request
|
||||
// TODO: validator do requestu
|
||||
$request->validate([
|
||||
'email' => 'required|string|email|unique:users',
|
||||
'password' => 'required|string|min:6|confirmed',
|
||||
'name' => 'required|string',
|
||||
'purchase_code' => 'required|string',
|
||||
'license' => 'required|string',
|
||||
'avatar' => 'sometimes|file',
|
||||
]);
|
||||
|
||||
// Create user
|
||||
$user = User::forceCreate([
|
||||
'role' => 'admin',
|
||||
'email' => $request->email,
|
||||
'password' => bcrypt($request->password),
|
||||
'email_verified_at' => now(),
|
||||
]);
|
||||
|
||||
$user
|
||||
->settings()
|
||||
->create([
|
||||
'storage_capacity' => get_setting('storage_default') ?? 5,
|
||||
'avatar' => store_avatar($request, 'avatar'),
|
||||
'name' => $request->name,
|
||||
]);
|
||||
|
||||
collect([
|
||||
[
|
||||
'name' => 'setup_wizard_success',
|
||||
'value' => 1,
|
||||
],
|
||||
[
|
||||
'name' => 'license',
|
||||
'value' => $request->license,
|
||||
],
|
||||
[
|
||||
'name' => 'purchase_code',
|
||||
'value' => $request->purchase_code,
|
||||
],
|
||||
])->each(function ($col) {
|
||||
Setting::forceCreate([
|
||||
'name' => $col['name'],
|
||||
'value' => $col['value'],
|
||||
]);
|
||||
});
|
||||
|
||||
// Set up application
|
||||
$this->setup->seed_default_pages();
|
||||
$this->setup->seed_default_settings($request->license);
|
||||
$this->setup->seed_default_language();
|
||||
|
||||
// Login account
|
||||
if (Auth::attempt($request->only(['email', 'password']))) {
|
||||
$request->session()->regenerate();
|
||||
|
||||
return response('Registration was successful', 204);
|
||||
}
|
||||
|
||||
return response('Something went wrong', 500);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get setup wizard status
|
||||
*/
|
||||
private function check_setup_status()
|
||||
{
|
||||
try {
|
||||
// Check database connections
|
||||
DB::getPdo();
|
||||
|
||||
// Get setup_wizard status
|
||||
if (Schema::hasTable('settings') && get_setting('setup_wizard_success')) {
|
||||
abort(410, 'Gone');
|
||||
}
|
||||
} catch (PDOException $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
namespace App\Http\Requests\SetupWizard;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class StoreAppSetupRequest 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 [
|
||||
'title' => 'required|string',
|
||||
'description' => 'required|string',
|
||||
'logo' => 'sometimes|file',
|
||||
'logo_horizontal' => 'sometimes|file',
|
||||
'favicon' => 'sometimes|file',
|
||||
'contactMail' => 'required|email',
|
||||
'googleAnalytics' => 'sometimes|string',
|
||||
'defaultStorage' => 'sometimes|digits_between:1,9',
|
||||
'userRegistration' => 'required|boolean',
|
||||
'storageLimitation' => 'required|boolean',
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
namespace App\Http\Requests\SetupWizard;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class StoreDatabaseCredentialsRequest 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 [
|
||||
'connection' => 'required|string',
|
||||
'host' => 'required|string',
|
||||
'port' => 'required|string',
|
||||
'name' => 'required|string',
|
||||
'username' => 'required|string',
|
||||
'password' => 'required|string',
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
namespace App\Http\Requests\SetupWizard;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class StoreEnvironmentSetupRequest 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 [
|
||||
'storage' => 'required|array',
|
||||
'storage.driver' => 'required|string',
|
||||
'storage.key' => 'sometimes|nullable|string',
|
||||
'storage.secret' => 'sometimes|nullable|string',
|
||||
'storage.endpoint' => 'sometimes|nullable|string',
|
||||
'storage.region' => 'sometimes|nullable|string',
|
||||
'storage.bucket' => 'sometimes|nullable|string',
|
||||
'mail' => 'required|array',
|
||||
'mail.driver' => 'required|string',
|
||||
'mail.host' => 'required|string',
|
||||
'mail.port' => 'required|string',
|
||||
'mail.username' => 'required|string',
|
||||
'mail.password' => 'required|string',
|
||||
'mail.encryption' => 'required|string',
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
namespace App\Http\Requests\SetupWizard;
|
||||
|
||||
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',
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
namespace App\Http\Requests\SetupWizard;
|
||||
|
||||
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',
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
namespace App\Http\Requests\SetupWizard;
|
||||
|
||||
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',
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
namespace App\Services;
|
||||
|
||||
use App\Models\Page;
|
||||
use App\Models\Setting;
|
||||
use App\Models\Language;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class SetupService
|
||||
{
|
||||
/**
|
||||
* Create default folders which application to process files.
|
||||
*/
|
||||
public function create_directories()
|
||||
{
|
||||
collect(['avatars', 'chunks', 'system', 'files', 'temp', 'zip'])
|
||||
->each(function ($directory) {
|
||||
// Create directory for local driver
|
||||
Storage::disk('local')
|
||||
->makeDirectory($directory);
|
||||
|
||||
// Create directory for external driver
|
||||
Storage::makeDirectory($directory);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Store default pages content like Terms of Service, Privacy Policy and Cookie Policy into database
|
||||
*/
|
||||
public function seed_default_pages()
|
||||
{
|
||||
collect(config('content.pages'))
|
||||
->each(function ($page) {
|
||||
Page::updateOrCreate($page);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Store default VueFileManager settings into database
|
||||
*
|
||||
* @param $license
|
||||
*/
|
||||
public function seed_default_settings($license)
|
||||
{
|
||||
collect(config('content.content.' . strtolower($license)))
|
||||
->each(function ($content) {
|
||||
Setting::forceCreate($content);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Store default VueFileManager settings into database
|
||||
*
|
||||
* @param $license
|
||||
*/
|
||||
public function seed_default_language()
|
||||
{
|
||||
Language::create([
|
||||
'name' => 'English',
|
||||
'locale' => 'en',
|
||||
]);
|
||||
|
||||
Setting::create([
|
||||
'name' => 'language',
|
||||
'value' => 'en',
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,248 @@
|
||||
<?php
|
||||
namespace App\Http\Controllers\Sharing;
|
||||
|
||||
use App\Models\File;
|
||||
use App\Models\Share;
|
||||
use App\Models\Folder;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Services\HelperService;
|
||||
use Illuminate\Support\Collection;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Resources\FileResource;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use App\Http\Resources\ShareResource;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use App\Http\Requests\Share\AuthenticateShareRequest;
|
||||
|
||||
class BrowseShareController extends Controller
|
||||
{
|
||||
private $helper;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->helper = resolve(HelperService::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show page index and delete access_token & shared_token cookie
|
||||
* @param Share $shared
|
||||
* @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View|\Symfony\Component\HttpFoundation\StreamedResponse
|
||||
*/
|
||||
public function index(Share $shared)
|
||||
{
|
||||
// Delete share_session if exist
|
||||
if ($shared->is_protected) {
|
||||
cookie()->queue('share_session', '', -1);
|
||||
}
|
||||
|
||||
// Check if shared is image file and then show it
|
||||
if ($shared->type === 'file' && ! $shared->is_protected) {
|
||||
$image = File::whereUserId($shared->user_id)
|
||||
->whereType('image')
|
||||
->whereId($shared->item_id)
|
||||
->first();
|
||||
|
||||
if ($image) {
|
||||
// Store user download size
|
||||
$shared
|
||||
->user
|
||||
->record_download(
|
||||
(int) $image->getRawOriginal('filesize')
|
||||
);
|
||||
|
||||
return $this->get_single_image($image, $shared->user_id);
|
||||
}
|
||||
}
|
||||
|
||||
return view('index')
|
||||
->with('installation', 'setup-done')
|
||||
->with('settings', get_settings_in_json() ?? null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check Password for protected item
|
||||
*
|
||||
* @param AuthenticateShareRequest $request
|
||||
* @param Share $shared
|
||||
* @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\Routing\ResponseFactory|\Illuminate\Http\Response
|
||||
*/
|
||||
public function authenticate(AuthenticateShareRequest $request, Share $shared)
|
||||
{
|
||||
// Check password
|
||||
if (Hash::check($request->password, $shared->password)) {
|
||||
$cookie = json_encode([
|
||||
'token' => $shared->token,
|
||||
'authenticated' => true,
|
||||
]);
|
||||
|
||||
// Return authorize token with shared options
|
||||
return response(new ShareResource($shared), 200)
|
||||
->cookie('share_session', $cookie, 43200);
|
||||
}
|
||||
|
||||
return response(__t('incorrect_password'), 401);
|
||||
}
|
||||
|
||||
/**
|
||||
* Browse shared folder
|
||||
*
|
||||
* @param $id
|
||||
* @param Share $shared
|
||||
* @return Collection
|
||||
*/
|
||||
public function browse_folder($id, Share $shared)
|
||||
{
|
||||
// Check ability to access protected share record
|
||||
$this->helper->check_protected_share_record($shared);
|
||||
|
||||
// Check if user can get directory
|
||||
$this->helper->check_item_access($id, $shared);
|
||||
|
||||
// Get files and folders
|
||||
list($folders, $files) = $this->helper->get_items_under_shared_by_folder_id($id, $shared);
|
||||
|
||||
// Set thumbnail links for public files
|
||||
$files->map(function ($file) use ($shared) {
|
||||
$file->setPublicUrl($shared->token);
|
||||
});
|
||||
|
||||
// Collect folders and files to single array
|
||||
return collect([$folders, $files])
|
||||
->collapse();
|
||||
}
|
||||
|
||||
/**
|
||||
* Search shared files
|
||||
*
|
||||
* @param Request $request
|
||||
* @param Share $shared
|
||||
* @return Collection
|
||||
*/
|
||||
public function search(Request $request, Share $shared)
|
||||
{
|
||||
// Check ability to access protected share record
|
||||
$this->helper->check_protected_share_record($shared);
|
||||
|
||||
$query = remove_accents(
|
||||
$request->input('query')
|
||||
);
|
||||
|
||||
// Search files id db
|
||||
$searched_files = File::search($query)
|
||||
->where('user_id', $shared->user_id)
|
||||
->get();
|
||||
$searched_folders = Folder::search($query)
|
||||
->where('user_id', $shared->user_id)
|
||||
->get();
|
||||
|
||||
// Get all children content
|
||||
$foldersIds = Folder::with('folders:id,parent_id,id,name')
|
||||
->where('user_id', $shared->user_id)
|
||||
->where('parent_id', $shared->item_id)
|
||||
->get();
|
||||
|
||||
// Get accessible folders
|
||||
$accessible_folder_ids = Arr::flatten([filter_folders_ids($foldersIds), $shared->item_id]);
|
||||
|
||||
// Filter files
|
||||
$files = $searched_files->filter(function ($file) use ($accessible_folder_ids, $shared) {
|
||||
// Set public urls
|
||||
$file->setPublicUrl($shared->token);
|
||||
|
||||
// check if item is in accessible folders
|
||||
return in_array($file->folder_id, $accessible_folder_ids);
|
||||
});
|
||||
|
||||
// Filter folders
|
||||
$folders = $searched_folders->filter(function ($folder) use ($accessible_folder_ids) {
|
||||
// check if item is in accessible folders
|
||||
return in_array($folder->id, $accessible_folder_ids);
|
||||
});
|
||||
|
||||
// Collect folders and files to single array
|
||||
return collect([$folders, $files])
|
||||
->collapse();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get navigation tree of shared folder
|
||||
*
|
||||
* @param Share $shared
|
||||
* @return array
|
||||
*/
|
||||
public function navigation_tree(Share $shared)
|
||||
{
|
||||
// Check ability to access protected share record
|
||||
$this->helper->check_protected_share_record($shared);
|
||||
|
||||
// Check if user can get directory
|
||||
$this->helper->check_item_access($shared->item_id, $shared);
|
||||
|
||||
// Get folders
|
||||
$folders = Folder::with('folders:id,parent_id,name')
|
||||
->whereParentId($shared->item_id)
|
||||
->whereUserId($shared->user_id)
|
||||
->sortable()
|
||||
->get(['id', 'parent_id', 'id', 'name']);
|
||||
|
||||
return [
|
||||
[
|
||||
'id' => $shared->item_id,
|
||||
'name' => __t('home'),
|
||||
'location' => 'public',
|
||||
'folders' => $folders,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get shared file record
|
||||
*
|
||||
* @param Share $shared
|
||||
* @return mixed
|
||||
*/
|
||||
public function get_single_file(Share $shared)
|
||||
{
|
||||
// Check ability to access protected share files
|
||||
$this->helper->check_protected_share_record($shared);
|
||||
|
||||
// Get file
|
||||
$file = File::whereUserId($shared->user_id)
|
||||
->whereId($shared->item_id)
|
||||
->firstOrFail();
|
||||
|
||||
// Set access urls
|
||||
$file->setPublicUrl($shared->token);
|
||||
|
||||
return response(new FileResource($file), 200);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get image from storage and show it
|
||||
*
|
||||
* @param $file
|
||||
* @param $user_id
|
||||
* @return \Symfony\Component\HttpFoundation\StreamedResponse
|
||||
*/
|
||||
private function get_single_image($file, $user_id)
|
||||
{
|
||||
// Format pretty filename
|
||||
$file_pretty_name = $file->name . '.' . $file->mimetype;
|
||||
|
||||
// Get file path
|
||||
$path = "/files/$user_id/$file->basename";
|
||||
|
||||
// Check if file exist
|
||||
if (! Storage::exists($path)) {
|
||||
abort(404);
|
||||
}
|
||||
|
||||
return Storage::response($path, $file_pretty_name, [
|
||||
'Content-Type' => Storage::mimeType($path),
|
||||
'Content-Length' => Storage::size($path),
|
||||
'Accept-Ranges' => 'bytes',
|
||||
'Content-Range' => 'bytes 0-600/' . Storage::size($path),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
<?php
|
||||
namespace App\Http\Controllers\Sharing;
|
||||
|
||||
use App\Models\Zip;
|
||||
use App\Models\Share;
|
||||
use App\Services\HelperService;
|
||||
use App\Models\File as UserFile;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class FileSharedAccessController extends Controller
|
||||
{
|
||||
private $helper;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->helper = resolve(HelperService::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get generated zip for guest
|
||||
*
|
||||
* @param $id
|
||||
* @param $token
|
||||
* @return \Symfony\Component\HttpFoundation\StreamedResponse
|
||||
*/
|
||||
public function get_zip_public($id, $token)
|
||||
{
|
||||
$disk = Storage::disk('local');
|
||||
|
||||
$zip = Zip::where('id', $id)
|
||||
->where('shared_token', $token)
|
||||
->first();
|
||||
|
||||
$zip
|
||||
->user
|
||||
->record_download(
|
||||
$disk->size("zip/$zip->basename")
|
||||
);
|
||||
|
||||
return $disk
|
||||
->download("zip/$zip->basename", $zip->basename, [
|
||||
'Content-Type' => 'application/zip',
|
||||
'Content-Length' => $disk->size("zip/$zip->basename"),
|
||||
'Accept-Ranges' => 'bytes',
|
||||
'Content-Range' => 'bytes 0-600/' . $disk->size("zip/$zip->basename"),
|
||||
'Content-Disposition' => 'attachment; filename=' . $zip->basename,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get file public
|
||||
*
|
||||
* @param $filename
|
||||
* @param Share $shared
|
||||
* @return mixed
|
||||
*/
|
||||
public function get_file_public($filename, Share $shared)
|
||||
{
|
||||
// Check ability to access protected share files
|
||||
$this->helper->check_protected_share_record($shared);
|
||||
|
||||
// Get file record
|
||||
$file = UserFile::where('user_id', $shared->user_id)
|
||||
->where('basename', $filename)
|
||||
->firstOrFail();
|
||||
|
||||
// Check file access
|
||||
$this->helper->check_guest_access_to_shared_items($shared, $file);
|
||||
|
||||
// Store user download size
|
||||
$shared
|
||||
->user
|
||||
->record_download(
|
||||
(int) $file->getRawOriginal('filesize')
|
||||
);
|
||||
|
||||
return $this->helper->download_file($file, $shared->user_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get public image thumbnail
|
||||
*
|
||||
* @param $filename
|
||||
* @param Share $shared
|
||||
* @return mixed
|
||||
*/
|
||||
public function get_thumbnail_public($filename, Share $shared)
|
||||
{
|
||||
// Check ability to access protected share files
|
||||
$this->helper->check_protected_share_record($shared);
|
||||
|
||||
// Get file record
|
||||
$file = UserFile::where('user_id', $shared->user_id)
|
||||
->where('thumbnail', $filename)
|
||||
->firstOrFail();
|
||||
|
||||
// Check file access
|
||||
$this->helper->check_guest_access_to_shared_items($shared, $file);
|
||||
|
||||
// Store user download size
|
||||
$shared
|
||||
->user
|
||||
->record_download(
|
||||
(int) $file->getRawOriginal('filesize')
|
||||
);
|
||||
|
||||
return $this->helper->download_thumbnail_file($file, $shared->user_id);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,300 @@
|
||||
<?php
|
||||
namespace App\Http\Controllers\Sharing;
|
||||
|
||||
use App\Models\File;
|
||||
use App\Models\Share;
|
||||
use App\Models\Folder;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Services\DemoService;
|
||||
use App\Services\HelperService;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Services\FileManagerService;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Contracts\Routing\ResponseFactory;
|
||||
use App\Http\Requests\FileFunctions\UploadRequest;
|
||||
use App\Http\Requests\FileFunctions\MoveItemRequest;
|
||||
use App\Http\Requests\FileFunctions\DeleteItemRequest;
|
||||
use App\Http\Requests\FileFunctions\RenameItemRequest;
|
||||
use App\Http\Requests\FileFunctions\CreateFolderRequest;
|
||||
|
||||
class ManipulateShareItemsController extends Controller
|
||||
{
|
||||
private $filemanager;
|
||||
private $helper;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->filemanager = resolve(FileManagerService::class);
|
||||
$this->helper = resolve(HelperService::class);
|
||||
$this->demo = resolve(DemoService::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new folder for guest user with edit permission
|
||||
*
|
||||
* @param CreateFolderRequest $request
|
||||
* @param Share $shared
|
||||
* @return array|\Illuminate\Contracts\Foundation\Application|ResponseFactory|\Illuminate\Http\Response
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function create_folder(CreateFolderRequest $request, Share $shared)
|
||||
{
|
||||
if (is_demo_account($shared->user->email)) {
|
||||
return $this->demo->create_folder($request);
|
||||
}
|
||||
|
||||
// Check ability to access protected share record
|
||||
$this->helper->check_protected_share_record($shared);
|
||||
|
||||
// Check shared permission
|
||||
if (is_visitor($shared)) {
|
||||
abort(403);
|
||||
}
|
||||
|
||||
// Check access to requested directory
|
||||
$this->helper->check_item_access($request->parent_id, $shared);
|
||||
|
||||
// Create folder
|
||||
$folder = $this->filemanager->create_folder($request, $shared);
|
||||
|
||||
return response($folder, 201);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rename item for guest user with edit permission
|
||||
*
|
||||
* @param RenameItemRequest $request
|
||||
* @param $id
|
||||
* @param Share $shared
|
||||
* @return mixed
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function rename_item(RenameItemRequest $request, $id, Share $shared)
|
||||
{
|
||||
if (is_demo_account($shared->user->email)) {
|
||||
return $this->demo->rename_item($request, $id);
|
||||
}
|
||||
|
||||
// Check ability to access protected share record
|
||||
$this->helper->check_protected_share_record($shared);
|
||||
|
||||
// Check shared permission
|
||||
if (is_visitor($shared)) {
|
||||
abort(403);
|
||||
}
|
||||
|
||||
// Get file|folder item
|
||||
$item = get_item($request->type, $id);
|
||||
|
||||
// Check access to requested item
|
||||
if ($request->type === 'folder') {
|
||||
$this->helper->check_item_access($item->id, $shared);
|
||||
} else {
|
||||
$this->helper->check_item_access($item->folder_id, $shared);
|
||||
}
|
||||
|
||||
// If request have a change folder icon values set the folder icon
|
||||
if ($request->type === 'folder' && $request->filled('icon')) {
|
||||
$this->filemanager->edit_folder_properties($request, $id);
|
||||
}
|
||||
|
||||
// Rename item
|
||||
$item = $this->filemanager->rename_item($request, $id, $shared);
|
||||
|
||||
// Set public url
|
||||
if ($item->type !== 'folder') {
|
||||
$item->setPublicUrl($shared->token);
|
||||
}
|
||||
|
||||
return response($item, 201);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete item for guest user with edit permission
|
||||
*
|
||||
* @param DeleteItemRequest $request
|
||||
* @param Share $shared
|
||||
* @return ResponseFactory|\Illuminate\Http\Response
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function delete_item(DeleteItemRequest $request, Share $shared)
|
||||
{
|
||||
abort_if(is_demo_account($shared->user->email), 204, 'Done.');
|
||||
|
||||
// Check ability to access protected share record
|
||||
$this->helper->check_protected_share_record($shared);
|
||||
|
||||
// Check shared permission
|
||||
if (is_visitor($shared)) {
|
||||
abort(403);
|
||||
}
|
||||
|
||||
foreach ($request->items as $file) {
|
||||
// Get file|folder item
|
||||
$item = get_item($file['type'], $file['id']);
|
||||
|
||||
// Check access to requested item
|
||||
if ($file['type'] === 'folder') {
|
||||
$this->helper->check_item_access($item->id, $shared);
|
||||
} else {
|
||||
$this->helper->check_item_access($item->folder_id, $shared);
|
||||
}
|
||||
|
||||
// Delete item
|
||||
$this->filemanager->delete_item($file, $file['id'], $shared);
|
||||
}
|
||||
|
||||
return response('Done', 204);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete file for guest user with edit permission
|
||||
*
|
||||
* @param UploadRequest $request
|
||||
* @param Share $shared
|
||||
* @return File|\Illuminate\Contracts\Foundation\Application|ResponseFactory|Model|\Illuminate\Http\Response
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function upload(UploadRequest $request, Share $shared)
|
||||
{
|
||||
if (is_demo_account($shared->user->email)) {
|
||||
return $this->demo->upload($request);
|
||||
}
|
||||
|
||||
// Check ability to access protected share record
|
||||
$this->helper->check_protected_share_record($shared);
|
||||
|
||||
// Check shared permission
|
||||
if (is_visitor($shared)) {
|
||||
abort(403);
|
||||
}
|
||||
|
||||
// Check access to requested directory
|
||||
$this->helper->check_item_access($request->folder_id, $shared);
|
||||
|
||||
// Return new uploaded file
|
||||
$new_file = $this->filemanager->upload($request, $shared);
|
||||
|
||||
// Set public access url
|
||||
$new_file->setPublicUrl($shared->token);
|
||||
|
||||
return response($new_file, 201);
|
||||
}
|
||||
|
||||
/**
|
||||
* Move item for guest user with edit permission
|
||||
*
|
||||
* @param MoveItemRequest $request
|
||||
* @param Share $shared
|
||||
* @return ResponseFactory|\Illuminate\Http\Response
|
||||
*/
|
||||
public function move(MoveItemRequest $request, Share $shared)
|
||||
{
|
||||
abort_if(is_demo_account($shared->user->email), 204, 'Done.');
|
||||
|
||||
// Check ability to access protected share record
|
||||
$this->helper->check_protected_share_record($shared);
|
||||
|
||||
// Check shared permission
|
||||
if (is_visitor($shared)) {
|
||||
abort(403);
|
||||
}
|
||||
|
||||
foreach ($request->items as $item) {
|
||||
if ($item['type'] === 'folder') {
|
||||
$this->helper->check_item_access([
|
||||
$request->to_id, $item['id'],
|
||||
], $shared);
|
||||
}
|
||||
|
||||
if ($item['type'] !== 'folder') {
|
||||
$file = File::where('id', $item['id'])
|
||||
->where('user_id', $shared->user_id)
|
||||
->firstOrFail();
|
||||
|
||||
$this->helper->check_item_access([
|
||||
$request->to_id, $file->folder_id,
|
||||
], $shared);
|
||||
}
|
||||
}
|
||||
|
||||
$this->filemanager->move($request, $request->to_id);
|
||||
|
||||
return response('Done!', 204);
|
||||
}
|
||||
|
||||
/**
|
||||
* Guest download folder via zip
|
||||
*
|
||||
* @param $id
|
||||
* @param Share $shared
|
||||
* @return string
|
||||
* @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
|
||||
*/
|
||||
public function zip_folder($id, Share $shared)
|
||||
{
|
||||
// Check ability to access protected share record
|
||||
$this->helper->check_protected_share_record($shared);
|
||||
|
||||
// Check access to requested folder
|
||||
$this->helper->check_item_access($id, $shared);
|
||||
|
||||
// Get folder
|
||||
$folder = Folder::whereUserId($shared->user_id)
|
||||
->where('id', $id);
|
||||
|
||||
if (! $folder->exists()) {
|
||||
abort(404, 'Requested folder doesn\'t exists.');
|
||||
}
|
||||
|
||||
$zip = $this->filemanager->zip_folder($id, $shared);
|
||||
|
||||
// Get file
|
||||
return response([
|
||||
'url' => route('zip_public', [
|
||||
'id' => $zip->id,
|
||||
'token' => $shared->token,
|
||||
]),
|
||||
'name' => $zip->basename,
|
||||
], 201);
|
||||
}
|
||||
|
||||
/**
|
||||
* Guest download multiple files via zip
|
||||
*
|
||||
* @param Request $request
|
||||
* @param Share $shared
|
||||
* @return string
|
||||
* @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
|
||||
*/
|
||||
public function zip_multiple_files(Request $request, Share $shared)
|
||||
{
|
||||
// Check ability to access protected share record
|
||||
$this->helper->check_protected_share_record($shared);
|
||||
|
||||
$file_parent_folders = File::whereUserId($shared->user_id)
|
||||
->whereIn('id', $request->items)
|
||||
->get()
|
||||
->pluck('folder_id')
|
||||
->toArray();
|
||||
|
||||
// Check access to requested directory
|
||||
$this->helper->check_item_access($file_parent_folders, $shared);
|
||||
|
||||
// Get requested files
|
||||
$files = File::whereUserId($shared->user_id)
|
||||
->whereIn('id', $request->items)
|
||||
->get();
|
||||
|
||||
$zip = $this->filemanager->zip_files($files, $shared);
|
||||
|
||||
// Get file
|
||||
return response([
|
||||
'url' => route('zip_public', [
|
||||
'id' => $zip->id,
|
||||
'token' => $shared->token,
|
||||
]),
|
||||
'name' => $zip->basename,
|
||||
], 201);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
<?php
|
||||
namespace App\Http\Controllers\FileManager;
|
||||
|
||||
use Validator;
|
||||
use App\Models\Zip;
|
||||
use App\Models\Share;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use App\Http\Resources\ShareResource;
|
||||
use App\Notifications\SharedSendViaEmail;
|
||||
use Illuminate\Support\Facades\Notification;
|
||||
use App\Http\Requests\Share\CreateShareRequest;
|
||||
use App\Http\Requests\Share\UpdateShareRequest;
|
||||
use Illuminate\Contracts\Routing\ResponseFactory;
|
||||
|
||||
class ShareController extends Controller
|
||||
{
|
||||
/**
|
||||
* Get shared record
|
||||
*
|
||||
* @param Share $shared
|
||||
* @return ShareResource
|
||||
*/
|
||||
public function show(Share $shared)
|
||||
{
|
||||
return new ShareResource(
|
||||
$shared
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate file share link
|
||||
*
|
||||
* @param CreateShareRequest $request
|
||||
* @param $id
|
||||
* @return ShareResource
|
||||
*/
|
||||
public function store(CreateShareRequest $request, $id)
|
||||
{
|
||||
// Create shared options
|
||||
$shared = Share::create([
|
||||
'password' => $request->has('password') ? bcrypt($request->password) : null,
|
||||
'type' => $request->type === 'folder' ? 'folder' : 'file',
|
||||
'is_protected' => $request->isPassword,
|
||||
'permission' => $request->permission ?? null,
|
||||
'item_id' => $id,
|
||||
'expire_in' => $request->expiration ?? null,
|
||||
'user_id' => Auth::id(),
|
||||
]);
|
||||
|
||||
// Send shared link via email
|
||||
if ($request->has('emails')) {
|
||||
foreach ($request->emails as $email) {
|
||||
Notification::route('mail', $email)->notify(
|
||||
new SharedSendViaEmail($shared->token)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Return created shared record
|
||||
return new ShareResource($shared);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update sharing
|
||||
*
|
||||
* @param UpdateShareRequest $request
|
||||
* @param $token
|
||||
* @return ShareResource
|
||||
*/
|
||||
public function update(UpdateShareRequest $request, $token)
|
||||
{
|
||||
// Get sharing record
|
||||
$shared = Share::where('token', $token)
|
||||
->where('user_id', Auth::id())
|
||||
->firstOrFail();
|
||||
|
||||
// Update sharing record
|
||||
$shared->update([
|
||||
'permission' => $request->permission,
|
||||
'is_protected' => $request->protected,
|
||||
'expire_in' => $request->expiration,
|
||||
'password' => $request->password ? bcrypt($request->password) : $shared->password,
|
||||
]);
|
||||
|
||||
// Return shared record
|
||||
return new ShareResource($shared);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete sharing item
|
||||
*
|
||||
* @param Request $request
|
||||
* @return ResponseFactory|\Illuminate\Http\Response
|
||||
*/
|
||||
public function destroy(Request $request)
|
||||
{
|
||||
foreach ($request->tokens as $token) {
|
||||
// Get sharing record
|
||||
Share::where('token', $token)
|
||||
->where('user_id', Auth::id())
|
||||
->firstOrFail()
|
||||
->delete();
|
||||
|
||||
// Get zip record
|
||||
$zip = Zip::where('shared_token', $token)
|
||||
->where('user_id', Auth::id())
|
||||
->first();
|
||||
|
||||
if ($zip) {
|
||||
$zip->delete();
|
||||
}
|
||||
}
|
||||
|
||||
return response('Done!', 204);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send shared link via email to recipients
|
||||
*
|
||||
* @param $token
|
||||
* @param $request
|
||||
*/
|
||||
public function send_to_emails_recipients(Request $request, $token)
|
||||
{
|
||||
// TODO: pridat validation request
|
||||
// Make validation of array of emails
|
||||
$validator = Validator::make($request->all(), [
|
||||
'emails.*' => 'required|email',
|
||||
]);
|
||||
|
||||
// Return error
|
||||
if ($validator->fails()) {
|
||||
abort(400, 'Bad email input');
|
||||
}
|
||||
|
||||
// Send shared link via email
|
||||
if ($request->has('emails')) {
|
||||
foreach ($request->emails as $email) {
|
||||
Notification::route('mail', $email)
|
||||
->notify(new SharedSendViaEmail($token));
|
||||
}
|
||||
}
|
||||
|
||||
return response('Done!', 204);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Notifications\Notifiable;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
|
||||
/**
|
||||
* @method static whereNotNull(string $string)
|
||||
*/
|
||||
class Share extends Model
|
||||
{
|
||||
use Notifiable, HasFactory;
|
||||
|
||||
protected $guarded = ['id'];
|
||||
|
||||
protected $appends = ['link'];
|
||||
|
||||
public $incrementing = false;
|
||||
|
||||
protected $keyType = 'string';
|
||||
|
||||
protected $primaryKey = 'token';
|
||||
|
||||
protected $casts = [
|
||||
'is_protected' => 'boolean',
|
||||
];
|
||||
|
||||
/**
|
||||
* Generate share link
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getLinkAttribute()
|
||||
{
|
||||
return url('/share', ['token' => $this->attributes['token']]);
|
||||
}
|
||||
|
||||
public function user()
|
||||
{
|
||||
return $this->hasOne(User::class, 'id', 'user_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Model events
|
||||
*/
|
||||
protected static function boot()
|
||||
{
|
||||
parent::boot();
|
||||
|
||||
static::creating(function ($shared) {
|
||||
$shared->id = (string) Str::uuid();
|
||||
$shared->token = Str::random(16);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
namespace App\Notifications;
|
||||
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Notifications\Notification;
|
||||
use Illuminate\Notifications\Messages\MailMessage;
|
||||
|
||||
class SharedSendViaEmail extends Notification
|
||||
{
|
||||
use Queueable;
|
||||
|
||||
/**
|
||||
* Create a new notification instance.
|
||||
*
|
||||
* @param $token
|
||||
*/
|
||||
public function __construct($token)
|
||||
{
|
||||
$this->token = $token;
|
||||
$this->user = Auth::user();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
return (new MailMessage)
|
||||
->subject(__t('shared_link_email_subject', ['user' => $this->user->settings->name]))
|
||||
->greeting(__t('shared_link_email_greeting'))
|
||||
->line(__t('shared_link_email_user', ['user' => $this->user->settings->name, 'email' => $this->user->email]))
|
||||
->action(__t('shared_link_email_link'), url('/share', ['token' => $this->token]))
|
||||
->salutation(__t('shared_link_email_salutation', ['app_name' => get_setting('app_title') ?? 'VueFileManager']));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the array representation of the notification.
|
||||
*
|
||||
* @param mixed $notifiable
|
||||
* @return array
|
||||
*/
|
||||
public function toArray($notifiable)
|
||||
{
|
||||
return [
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
namespace App\Http\Requests\Share;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class AuthenticateShareRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'password' => 'required|string',
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
namespace App\Http\Requests\Share;
|
||||
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class CreateShareRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize()
|
||||
{
|
||||
return Auth::check();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'isPassword' => 'required|boolean',
|
||||
'type' => 'required|string',
|
||||
'expiration' => 'integer|nullable',
|
||||
'permission' => 'string',
|
||||
'password' => 'string',
|
||||
'emails.*' => 'email',
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
namespace App\Http\Requests\Share;
|
||||
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class UpdateShareRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize()
|
||||
{
|
||||
return Auth::check();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'protected' => 'required|boolean',
|
||||
'permission' => 'nullable|string',
|
||||
'expiration' => 'integer|nullable',
|
||||
'password' => 'string',
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
namespace App\Http\Resources;
|
||||
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
class ShareResource extends JsonResource
|
||||
{
|
||||
/**
|
||||
* Transform the resource into an array.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return array
|
||||
*/
|
||||
public function toArray($request)
|
||||
{
|
||||
return [
|
||||
'data' => [
|
||||
'id' => (string) $this->id,
|
||||
'type' => 'shares',
|
||||
'attributes' => [
|
||||
'permission' => $this->permission,
|
||||
'is_protected' => $this->is_protected,
|
||||
'item_id' => $this->item_id,
|
||||
'expire_in' => (int) $this->expire_in,
|
||||
'token' => $this->token,
|
||||
'link' => $this->link,
|
||||
'type' => $this->type,
|
||||
'created_at' => $this->created_at,
|
||||
'updated_at' => $this->updated_at,
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
<?php
|
||||
namespace App\Http\Controllers\User;
|
||||
|
||||
use Auth;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Services\DemoService;
|
||||
use App\Services\StripeService;
|
||||
use Laravel\Cashier\PaymentMethod;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use App\Http\Resources\PaymentCardResource;
|
||||
use App\Http\Resources\PaymentCardCollection;
|
||||
use App\Http\Resources\PaymentDefaultCardResource;
|
||||
use App\Http\Requests\Payments\RegisterNewPaymentMethodRequest;
|
||||
|
||||
class PaymentMethodsController extends Controller
|
||||
{
|
||||
private StripeService $stripe;
|
||||
|
||||
private DemoService $demo;
|
||||
|
||||
public function __construct(StripeService $stripe)
|
||||
{
|
||||
$this->stripe = $stripe;
|
||||
$this->demo = resolve(DemoService::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user payment methods grouped by default and others
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$user = Auth::user();
|
||||
|
||||
if (! $user->hasPaymentMethod()) {
|
||||
return abort(204, 'User don\'t have any payment methods');
|
||||
}
|
||||
|
||||
$slug_payment_methods = 'payment-methods-user-' . $user->id;
|
||||
$slug_default_payment_method = 'default-payment-methods-user-' . $user->id;
|
||||
|
||||
if (Cache::has($slug_payment_methods) && Cache::has($slug_default_payment_method)) {
|
||||
$defaultPaymentMethod = Cache::get($slug_default_payment_method);
|
||||
$paymentMethodsMapped = Cache::get($slug_payment_methods);
|
||||
} else {
|
||||
// Get default payment method
|
||||
$defaultPaymentMethod = Cache::rememberForever($slug_default_payment_method, function () use ($user) {
|
||||
$defaultPaymentMethodObject = $user->defaultPaymentMethod();
|
||||
|
||||
return $defaultPaymentMethodObject instanceof PaymentMethod
|
||||
? $defaultPaymentMethodObject->asStripePaymentMethod()
|
||||
: $defaultPaymentMethodObject;
|
||||
});
|
||||
|
||||
// filter payment methods without default payment
|
||||
$paymentMethodsMapped = Cache::rememberForever($slug_payment_methods, function () use ($defaultPaymentMethod, $user) {
|
||||
$paymentMethods = $user->paymentMethods()->filter(function ($paymentMethod) use ($defaultPaymentMethod) {
|
||||
return $paymentMethod->id !== $defaultPaymentMethod->id;
|
||||
});
|
||||
|
||||
// Get payment methods
|
||||
return $paymentMethods->map(function ($paymentMethod) {
|
||||
return $paymentMethod->asStripePaymentMethod();
|
||||
})->values()->all();
|
||||
});
|
||||
}
|
||||
|
||||
if (! $user->card_brand || ! $user->stripe_id || is_null($paymentMethodsMapped) && is_null($paymentMethodsMapped)) {
|
||||
return [
|
||||
'default' => null,
|
||||
'others' => [],
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'default' => $defaultPaymentMethod instanceof PaymentMethod
|
||||
? new PaymentCardResource($defaultPaymentMethod)
|
||||
: new PaymentDefaultCardResource($defaultPaymentMethod),
|
||||
'others' => new PaymentCardCollection($paymentMethodsMapped),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Update default payment method
|
||||
*
|
||||
* @param Request $request
|
||||
* @param $id
|
||||
* @return \Illuminate\Contracts\Routing\ResponseFactory|\Illuminate\Http\Response
|
||||
*/
|
||||
public function update($id)
|
||||
{
|
||||
$user = Auth::user();
|
||||
|
||||
// Check if is demo
|
||||
abort_if(is_demo_account('howdy@hi5ve.digital'), 204, 'Done.');
|
||||
|
||||
// Update DefaultPayment Method
|
||||
$user->updateDefaultPaymentMethod($id);
|
||||
|
||||
// Sync default payment method
|
||||
$user->updateDefaultPaymentMethodFromStripe();
|
||||
|
||||
// Clear cached payment methods
|
||||
cache_forget_many([
|
||||
'payment-methods-user-' . $user->id,
|
||||
'default-payment-methods-user-' . $user->id,
|
||||
]);
|
||||
|
||||
return response('Done', 204);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register new payment method for user
|
||||
*
|
||||
* @param Request $request
|
||||
* @return \Illuminate\Contracts\Routing\ResponseFactory|\Illuminate\Http\Response
|
||||
*/
|
||||
public function store(RegisterNewPaymentMethodRequest $request)
|
||||
{
|
||||
// Get user
|
||||
$user = Auth::user();
|
||||
|
||||
// Check if is demo
|
||||
if (is_demo($user->id)) {
|
||||
return response('Done', 201);
|
||||
}
|
||||
|
||||
// Register new payment method
|
||||
$this->stripe->registerNewPaymentMethod($request, $user);
|
||||
|
||||
return response('Done', 201);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete user payment method
|
||||
*
|
||||
*/
|
||||
public function delete($id)
|
||||
{
|
||||
$user = Auth::user();
|
||||
|
||||
// Check if is demo
|
||||
abort_if(is_demo_account('howdy@hi5ve.digital'), 204, 'Done.');
|
||||
|
||||
// Get payment method
|
||||
$paymentMethod = $user->findPaymentMethod($id);
|
||||
|
||||
// Delete payment method
|
||||
$paymentMethod->delete();
|
||||
|
||||
// Sync default payment method
|
||||
$user->updateDefaultPaymentMethodFromStripe();
|
||||
|
||||
// Clear cached payment methods
|
||||
cache_forget_many([
|
||||
'payment-methods-user-' . $user->id,
|
||||
'default-payment-methods-user-' . $user->id,
|
||||
]);
|
||||
|
||||
return response('Done!', 204);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
namespace App\Http\Controllers\Subscription;
|
||||
|
||||
use App\Models\User;
|
||||
use App\Services\StripeService;
|
||||
use Laravel\Cashier\Http\Controllers\WebhookController as CashierController;
|
||||
|
||||
class StripeWebhookController extends CashierController
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
$this->stripe = resolve(StripeService::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a cancelled customer from a Stripe subscription.
|
||||
*
|
||||
* @param array $payload
|
||||
* @return \Symfony\Component\HttpFoundation\Response
|
||||
*/
|
||||
public function handleCustomerSubscriptionDeleted($payload)
|
||||
{
|
||||
if ($user = $this->getUserByStripeId($payload['data']['object']['customer'])) {
|
||||
$user->subscriptions->filter(function ($subscription) use ($payload) {
|
||||
return $subscription->stripe_id === $payload['data']['object']['id'];
|
||||
})->each(function ($subscription) {
|
||||
$subscription->markAsCancelled();
|
||||
});
|
||||
}
|
||||
|
||||
// Get user
|
||||
$user = User::whereStripeId($payload['data']['object']['customer'])
|
||||
->firstOrFail();
|
||||
|
||||
// Update storage capacity
|
||||
$user
|
||||
->settings()
|
||||
->update([
|
||||
'storage_capacity' => get_setting('storage_default'),
|
||||
]);
|
||||
|
||||
return $this->successMethod();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle Invoice Payment Succeeded
|
||||
*
|
||||
* @param $payload
|
||||
* @return \Symfony\Component\HttpFoundation\Response
|
||||
*/
|
||||
public function handleInvoicePaymentSucceeded($payload)
|
||||
{
|
||||
// Get user
|
||||
$user = User::whereStripeId($payload['data']['object']['customer'])
|
||||
->firstOrFail();
|
||||
|
||||
// Get requested plan
|
||||
$plan = $this->stripe->getPlan($user->subscription('main')->stripe_plan);
|
||||
|
||||
// Update user storage limit
|
||||
$user
|
||||
->settings()
|
||||
->update([
|
||||
'storage_capacity' => $plan['product']['metadata']['capacity'],
|
||||
]);
|
||||
|
||||
return $this->successMethod();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
<?php
|
||||
namespace App\Http\Controllers\User;
|
||||
|
||||
use Auth;
|
||||
use App\Models\User;
|
||||
use Stripe\SetupIntent;
|
||||
use App\Services\DemoService;
|
||||
use Illuminate\Http\Response;
|
||||
use App\Services\StripeService;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use App\Http\Resources\UserSubscription;
|
||||
use Illuminate\Contracts\Foundation\Application;
|
||||
use Illuminate\Contracts\Routing\ResponseFactory;
|
||||
use App\Http\Requests\Subscription\StoreUpgradeAccountRequest;
|
||||
|
||||
class SubscriptionController extends Controller
|
||||
{
|
||||
private $stripe;
|
||||
private $demo;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->stripe = resolve(StripeService::class);
|
||||
$this->demo = DemoService::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate setup intent
|
||||
*
|
||||
* @return Application|ResponseFactory|Response|SetupIntent
|
||||
*/
|
||||
public function setup_intent()
|
||||
{
|
||||
return response(
|
||||
$this->stripe->getSetupIntent(Auth::user()),
|
||||
201
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user subscription detail
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function show()
|
||||
{
|
||||
$user = User::find(Auth::id());
|
||||
|
||||
if (! $user->subscription('main')) {
|
||||
return abort(204, 'User don\'t have any subscription');
|
||||
}
|
||||
|
||||
$slug = 'subscription-user-' . $user->id;
|
||||
|
||||
if (Cache::has($slug)) {
|
||||
return Cache::get($slug);
|
||||
}
|
||||
|
||||
return Cache::rememberForever($slug, function () use ($user) {
|
||||
return new UserSubscription(
|
||||
$user
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Upgrade account to subscription
|
||||
*
|
||||
* @param StoreUpgradeAccountRequest $request
|
||||
* @return ResponseFactory|Response
|
||||
*/
|
||||
public function upgrade(StoreUpgradeAccountRequest $request)
|
||||
{
|
||||
// Get user
|
||||
$user = Auth::user();
|
||||
|
||||
// Check if is demo
|
||||
if (is_demo($user->id)) {
|
||||
return $this->demo->response_204();
|
||||
}
|
||||
|
||||
// Forget user subscription
|
||||
Cache::forget('subscription-user-' . $user->id);
|
||||
|
||||
// Get requested plan
|
||||
$plan = $this->stripe->getPlan($request->input('plan.data.id'));
|
||||
|
||||
// Set user billing
|
||||
$user->setBilling($request->input('billing'));
|
||||
|
||||
// Update stripe customer billing info
|
||||
$this->stripe->updateCustomerDetails($user);
|
||||
|
||||
// Make subscription
|
||||
$this->stripe->createOrReplaceSubscription($request, $user);
|
||||
|
||||
// Update user storage limit
|
||||
$user->settings()->update([
|
||||
'storage_capacity' => $plan['product']['metadata']['capacity'],
|
||||
]);
|
||||
|
||||
return response('Done!', 204);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel Subscription
|
||||
*
|
||||
* @return ResponseFactory|Response
|
||||
*/
|
||||
public function cancel()
|
||||
{
|
||||
$user = User::find(Auth::id());
|
||||
|
||||
// Check if is demo
|
||||
if (is_demo($user->id)) {
|
||||
return $this->demo->response_204();
|
||||
}
|
||||
|
||||
// Cancel subscription
|
||||
$user->subscription('main')->cancel();
|
||||
|
||||
// Forget user subscription
|
||||
Cache::forget('subscription-user-' . $user->id);
|
||||
|
||||
return response('Done!', 204);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resume Subscription
|
||||
*
|
||||
* @return ResponseFactory|Response
|
||||
*/
|
||||
public function resume()
|
||||
{
|
||||
$user = User::find(Auth::id());
|
||||
|
||||
// Check if is demo
|
||||
if (is_demo($user->id)) {
|
||||
return $this->demo->response_204();
|
||||
}
|
||||
|
||||
// Resume subscription
|
||||
$user->subscription('main')->resume();
|
||||
|
||||
// Forget user subscription
|
||||
Cache::forget('subscription-user-' . $user->id);
|
||||
|
||||
return response('Done!', 204);
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user