Compare commits
79 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cf0730be89 | ||
|
|
221e67fd12 | ||
|
|
f9074309d1 | ||
|
|
b2bdbb9e30 | ||
|
|
63d0cb9626 | ||
|
|
e3bb3ae4d1 | ||
|
|
31890005ac | ||
|
|
04a216dee1 | ||
|
|
5d403c1202 | ||
|
|
a2d92c67b3 | ||
|
|
0fec3d82a3 | ||
|
|
6530a64f97 | ||
|
|
656f02d652 | ||
|
|
0dbf3bba4d | ||
|
|
ea47c0c3c6 | ||
|
|
b4072c7892 | ||
|
|
e80cd1990a | ||
|
|
2585cc1db4 | ||
|
|
0225828445 | ||
|
|
63af93592d | ||
|
|
b87fcd0f25 | ||
|
|
23a6b3cc55 | ||
|
|
dac3d229fd | ||
|
|
e360d9c5df | ||
|
|
8f100fc027 | ||
|
|
06fb331ae4 | ||
|
|
2bcf02a779 | ||
|
|
1b24664b60 | ||
|
|
2ce96a2062 | ||
|
|
5d737ba328 | ||
|
|
bafe8ba780 | ||
|
|
b7ff40fd72 | ||
|
|
8e036b1c01 | ||
|
|
b016c18880 | ||
|
|
d17ae49155 | ||
|
|
6ef5cd25f5 | ||
|
|
c98f077a0e | ||
|
|
a1f58c8e13 | ||
|
|
37281b01e4 | ||
|
|
65b0a768af | ||
|
|
39af06d3b2 | ||
|
|
7d137dd612 | ||
|
|
ce5e6c18f0 | ||
|
|
3d445ca61a | ||
|
|
8566eaaa6c | ||
|
|
db1647569c | ||
|
|
204afb7eb1 | ||
|
|
37f82d4579 | ||
|
|
2ed440e65f | ||
|
|
57a5ec2206 | ||
|
|
f14bc0494b | ||
|
|
c4147f0125 | ||
|
|
cfd0c3cbe9 | ||
|
|
0d0e2732b5 | ||
|
|
0a7b072eeb | ||
|
|
af6e12ca01 | ||
|
|
5eba94fd9b | ||
|
|
e9756494d9 | ||
|
|
f7f919b5de | ||
|
|
6f434f3b07 | ||
|
|
8820851afa | ||
|
|
4582e955d0 | ||
|
|
e07395b3d5 | ||
|
|
3ad7d06976 | ||
|
|
f57cbb76e3 | ||
|
|
bb3151a2fe | ||
|
|
5b48d204a0 | ||
|
|
680d96882a | ||
|
|
7347356646 | ||
|
|
2652e7ed71 | ||
|
|
a068da2a54 | ||
|
|
de69a2687a | ||
|
|
59552cf8e5 | ||
|
|
8d830db88c | ||
|
|
157ea41e6d | ||
|
|
10771165a7 | ||
|
|
52ff6ab96f | ||
|
|
dfc342ac2c | ||
|
|
ac1778b9e9 |
@@ -1,7 +1,7 @@
|
||||
APP_NAME=Laravel
|
||||
APP_ENV=local
|
||||
APP_ENV=production
|
||||
APP_KEY=
|
||||
APP_DEBUG=true
|
||||
APP_DEBUG=false
|
||||
APP_URL=http://localhost
|
||||
APP_DEMO=false
|
||||
|
||||
@@ -36,6 +36,10 @@ MAIL_ENCRYPTION=null
|
||||
MAIL_FROM_ADDRESS=null
|
||||
MAIL_FROM_NAME="${APP_NAME}"
|
||||
|
||||
STRIPE_KEY=your-stripe-key
|
||||
STRIPE_SECRET=your-stripe-secret
|
||||
CASHIER_MODEL=App\Models\User
|
||||
|
||||
AWS_ACCESS_KEY_ID=
|
||||
AWS_SECRET_ACCESS_KEY=
|
||||
AWS_DEFAULT_REGION=us-east-1
|
||||
|
||||
44
app/Casts/PermissionCast.php
Normal file
44
app/Casts/PermissionCast.php
Normal file
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
namespace App\Casts;
|
||||
|
||||
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
|
||||
|
||||
class PermissionCast implements CastsAttributes
|
||||
{
|
||||
/**
|
||||
* Cast the given value.
|
||||
*
|
||||
* @param \Illuminate\Database\Eloquent\Model $model
|
||||
* @param string $key
|
||||
* @param mixed $value
|
||||
* @param array $attributes
|
||||
* @return mixed
|
||||
*/
|
||||
public function get($model, $key, $value, $attributes)
|
||||
{
|
||||
if (!$value) {
|
||||
return [
|
||||
'create' => false,
|
||||
'update' => false,
|
||||
'delete' => false,
|
||||
];
|
||||
}
|
||||
|
||||
return json_decode($value, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the given value for storage.
|
||||
*
|
||||
* @param \Illuminate\Database\Eloquent\Model $model
|
||||
* @param string $key
|
||||
* @param array $value
|
||||
* @param array $attributes
|
||||
* @return mixed
|
||||
*/
|
||||
public function set($model, $key, $value, $attributes)
|
||||
{
|
||||
return json_encode($value);
|
||||
}
|
||||
}
|
||||
@@ -21,15 +21,9 @@ class Install extends Command
|
||||
protected $versionChecker;
|
||||
protected $installationFile = 'app/installation';
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->versionChecker = (new VersionChecker)->getVersions();
|
||||
}
|
||||
|
||||
public function handle()
|
||||
{
|
||||
$this->init();
|
||||
$this->intro();
|
||||
$this->isInstalled();
|
||||
$this->checkApplicationKey();
|
||||
@@ -45,6 +39,11 @@ class Install extends Command
|
||||
$this->info('Visit your platform at ' . env('APP_URL'));
|
||||
}
|
||||
|
||||
protected function init()
|
||||
{
|
||||
$this->versionChecker = (new VersionChecker)->getVersions();
|
||||
}
|
||||
|
||||
protected function askAboutAdministrationAccount()
|
||||
{
|
||||
$this->info('Let\'s start by setting up your administration account.');
|
||||
@@ -85,16 +84,46 @@ class Install extends Command
|
||||
Package::create([
|
||||
'name' => 'Basic',
|
||||
'maximum_sites' => 5,
|
||||
'site_permissions' => [
|
||||
'create' => true,
|
||||
'update' => true,
|
||||
'delete' => true
|
||||
],
|
||||
'server_permissions' => [
|
||||
'create' => false,
|
||||
'update' => false,
|
||||
'delete' => false
|
||||
]
|
||||
]);
|
||||
|
||||
Package::create([
|
||||
'name' => 'Professional',
|
||||
'maximum_sites' => 5,
|
||||
'site_permissions' => [
|
||||
'create' => true,
|
||||
'update' => true,
|
||||
'delete' => true
|
||||
],
|
||||
'server_permissions' => [
|
||||
'create' => false,
|
||||
'update' => false,
|
||||
'delete' => false
|
||||
]
|
||||
]);
|
||||
|
||||
Package::create([
|
||||
'name' => 'Unlimited',
|
||||
'maximum_sites' => 0,
|
||||
'site_permissions' => [
|
||||
'create' => true,
|
||||
'update' => true,
|
||||
'delete' => true
|
||||
],
|
||||
'server_permissions' => [
|
||||
'create' => false,
|
||||
'update' => false,
|
||||
'delete' => false
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
98
app/Http/Controllers/Admin/ApplicationLogController.php
Normal file
98
app/Http/Controllers/Admin/ApplicationLogController.php
Normal file
@@ -0,0 +1,98 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
|
||||
class ApplicationLogController extends Controller
|
||||
{
|
||||
protected $final = [];
|
||||
protected $config = [
|
||||
'date' => null
|
||||
];
|
||||
|
||||
public function index(Request $request)
|
||||
{
|
||||
$this->config['date'] = date('Y-m-d');
|
||||
|
||||
if ($date = $request->input('date')) {
|
||||
$this->config['date'] = $date;
|
||||
} else {
|
||||
$request->merge(['date' => date('Y-m-d')]);
|
||||
}
|
||||
|
||||
return inertia('Admin/ApplicationLogs', [
|
||||
'logData' => $this->get(),
|
||||
'filters' => $request->all('date')
|
||||
]);
|
||||
}
|
||||
|
||||
public function getLogFileDates()
|
||||
{
|
||||
$dates = [];
|
||||
$files = glob(storage_path('logs/laravel-*.log'));
|
||||
$files = array_reverse($files);
|
||||
|
||||
foreach ($files as $path) {
|
||||
$fileName = basename($path);
|
||||
preg_match('/(?<=laravel-)(.*)(?=.log)/', $fileName, $dtMatch);
|
||||
$date = $dtMatch[0];
|
||||
array_push($dates, $date);
|
||||
}
|
||||
|
||||
return $dates;
|
||||
}
|
||||
|
||||
public function get()
|
||||
{
|
||||
$availableDates = $this->getLogFileDates();
|
||||
|
||||
if (count($availableDates) == 0) {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => 'No log available'
|
||||
]);
|
||||
}
|
||||
|
||||
$configDate = $this->config['date'];
|
||||
if ($configDate == null) {
|
||||
$configDate = $availableDates[0];
|
||||
}
|
||||
|
||||
if (!in_array($configDate, $availableDates)) {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => 'No log file found with selected date ' . $configDate
|
||||
]);
|
||||
}
|
||||
|
||||
$pattern = "/^\[(?<date>.*)\]\s(?<env>\w+)\.(?<type>\w+):(?<message>.*)/m";
|
||||
|
||||
$fileName = 'laravel-' . $configDate . '.log';
|
||||
$content = file_get_contents(storage_path('logs/' . $fileName));
|
||||
preg_match_all($pattern, $content, $matches, PREG_SET_ORDER, 0);
|
||||
|
||||
$logs = [];
|
||||
foreach ($matches as $match) {
|
||||
$logs[] = [
|
||||
'timestamp' => $match['date'],
|
||||
'env' => $match['env'],
|
||||
'type' => $match['type'],
|
||||
'message' => trim($match['message'])
|
||||
];
|
||||
}
|
||||
|
||||
preg_match('/(?<=laravel-)(.*)(?=.log)/', $fileName, $dtMatch);
|
||||
$date = $dtMatch[0];
|
||||
|
||||
$data = [
|
||||
'available_dates' => $availableDates,
|
||||
'date' => $date,
|
||||
'filename' => $fileName,
|
||||
'logs' => array_reverse($logs)
|
||||
];
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Admin\DocumentationArticleRequest;
|
||||
use App\Models\DocumentationCategory;
|
||||
use App\Models\DocumentationItem;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class DocumentationArticleController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
$articles = DocumentationItem::query()->with('category:id,title')->latest()->paginate();
|
||||
|
||||
return inertia('Admin/Documentation/Articles/Index', [
|
||||
'articles' => $articles
|
||||
]);
|
||||
}
|
||||
|
||||
public function create()
|
||||
{
|
||||
$categories = DocumentationCategory::pluck('title', 'id');
|
||||
|
||||
return inertia('Admin/Documentation/Articles/Create', [
|
||||
'categories' => $categories,
|
||||
]);
|
||||
}
|
||||
|
||||
public function store(DocumentationArticleRequest $request)
|
||||
{
|
||||
$article = DocumentationItem::create([
|
||||
'title' => $request->input('title'),
|
||||
'content' => $request->input('content')
|
||||
]);
|
||||
|
||||
$article->documentation_category_id = $request->input('category_id');
|
||||
$article->save();
|
||||
|
||||
return redirect()->route('admin.documentation.articles.index')->with('success', __('Documentation article has been created'));
|
||||
}
|
||||
|
||||
public function edit($id)
|
||||
{
|
||||
$article = DocumentationItem::findOrFail($id);
|
||||
|
||||
$categories = DocumentationCategory::pluck('title', 'id');
|
||||
|
||||
return inertia('Admin/Documentation/Articles/Edit', [
|
||||
'article' => $article,
|
||||
'categories' => $categories
|
||||
]);
|
||||
}
|
||||
|
||||
public function update(DocumentationArticleRequest $request, $id)
|
||||
{
|
||||
$article = DocumentationItem::findOrFail($id);
|
||||
|
||||
$article->update([
|
||||
'title' => $request->input('title'),
|
||||
'content' => $request->input('content')
|
||||
]);
|
||||
|
||||
$article->documentation_category_id = $request->input('category_id');
|
||||
$article->save();
|
||||
|
||||
return redirect()->route('admin.documentation.articles.index')->with('success', __('Documentation article has been updated'));
|
||||
}
|
||||
}
|
||||
@@ -3,11 +3,54 @@
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Admin\DocumentationCategoryRequest;
|
||||
use App\Models\DocumentationCategory;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class DocumentationController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
return inertia('Admin/Documentation/Index');
|
||||
$categories = DocumentationCategory::query()->latest()->paginate();
|
||||
|
||||
return inertia('Admin/Documentation/Index', [
|
||||
'categories' => $categories
|
||||
]);
|
||||
}
|
||||
|
||||
public function create()
|
||||
{
|
||||
return inertia('Admin/Documentation/Create');
|
||||
}
|
||||
|
||||
public function store(DocumentationCategoryRequest $request)
|
||||
{
|
||||
DocumentationCategory::create([
|
||||
'title' => $request->input('title'),
|
||||
'description' => $request->input('description')
|
||||
]);
|
||||
|
||||
return redirect()->route('admin.documentation.index')->with('success', __('Documentation category has been created'));
|
||||
}
|
||||
|
||||
public function edit($id)
|
||||
{
|
||||
$category = DocumentationCategory::findOrFail($id);
|
||||
|
||||
return inertia('Admin/Documentation/Edit', [
|
||||
'category' => $category
|
||||
]);
|
||||
}
|
||||
|
||||
public function update(DocumentationCategoryRequest $request, $id)
|
||||
{
|
||||
$category = DocumentationCategory::findOrFail($id);
|
||||
|
||||
$category->update([
|
||||
'title' => $request->input('title'),
|
||||
'description' => $request->input('description'),
|
||||
]);
|
||||
|
||||
return redirect()->route('admin.documentation.index')->with('success', __('Documentation category has been updated'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Models\Package;
|
||||
use App\Models\Provider;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Admin\PackageRequest;
|
||||
|
||||
@@ -19,25 +20,32 @@ class PackageController extends Controller
|
||||
|
||||
public function create()
|
||||
{
|
||||
return inertia('Admin/Packages/Create');
|
||||
$providers = Provider::get(['name', 'label', 'id'])->pluck('nameWithLabel', 'id');
|
||||
|
||||
return inertia('Admin/Packages/Create', [
|
||||
'providers' => $providers
|
||||
]);
|
||||
}
|
||||
|
||||
public function store(PackageRequest $request)
|
||||
{
|
||||
Package::create($request->all());
|
||||
$package = Package::create($request->validated());
|
||||
|
||||
return redirect()->route('admin.packages.index')->with('success', 'Package has been created');
|
||||
}
|
||||
$package->providers()->sync($request->input('providers'));
|
||||
|
||||
public function show($id)
|
||||
{
|
||||
//
|
||||
return redirect()->route('admin.packages.index')->with('success', __('Package has been created'));
|
||||
}
|
||||
|
||||
public function edit($id)
|
||||
{
|
||||
$package = Package::with('providers:id')->findOrFail($id);
|
||||
|
||||
$providers = Provider::get(['name', 'label', 'id'])->pluck('nameWithLabel', 'id');
|
||||
|
||||
return inertia('Admin/Packages/Edit', [
|
||||
'package' => Package::findOrFail($id)
|
||||
'package' => $package,
|
||||
'providers' => $providers,
|
||||
'syncedProviders' => $package->providers->pluck('id')
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -47,7 +55,9 @@ class PackageController extends Controller
|
||||
|
||||
$package->update($request->validated());
|
||||
|
||||
return redirect()->route('admin.packages.index')->with('success', 'Package has been updated');
|
||||
$package->providers()->sync($request->input('providers'));
|
||||
|
||||
return redirect()->route('admin.packages.index')->with('success', __('Package has been updated'));
|
||||
}
|
||||
|
||||
public function destroy($id)
|
||||
@@ -56,6 +66,6 @@ class PackageController extends Controller
|
||||
|
||||
$package->delete();
|
||||
|
||||
return redirect()->route('admin.packages.index')->with('success', 'Package has been removed');
|
||||
return redirect()->route('admin.packages.index')->with('success', __('Package has been removed'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,9 +2,27 @@
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Models\Provider;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Admin\ProviderRequest;
|
||||
|
||||
class ProviderController extends Controller
|
||||
{
|
||||
//
|
||||
public function edit($id)
|
||||
{
|
||||
$provider = Provider::findOrFail($id);
|
||||
|
||||
return inertia('Admin/Services/Provider/Edit', [
|
||||
'provider' => $provider,
|
||||
]);
|
||||
}
|
||||
|
||||
public function update(ProviderRequest $request, $id)
|
||||
{
|
||||
$provider = Provider::findOrFail($id);
|
||||
|
||||
$provider->update($request->validated());
|
||||
|
||||
return redirect()->route('admin.services.index')->with('success', __('Provider has been updated'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Http\Requests\Admin\ServerAttachRequest;
|
||||
use App\Models\Server;
|
||||
use App\Models\User;
|
||||
use App\Models\Server;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Admin\ServerAttachRequest;
|
||||
|
||||
class ServerController extends Controller
|
||||
{
|
||||
@@ -14,7 +14,7 @@ class ServerController extends Controller
|
||||
{
|
||||
$server = Server::findOrFail($id);
|
||||
|
||||
$users = $server->users()->select('id', 'name', 'email')->get()->map(function($user){
|
||||
$users = $server->users()->select('id', 'name', 'email')->get()->map(function ($user) {
|
||||
return [
|
||||
'id' => $user->id,
|
||||
'name' => $user->name,
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Models\Site;
|
||||
use App\Models\Server;
|
||||
use App\Models\Provider;
|
||||
use App\Http\Controllers\Controller;
|
||||
|
||||
class ServiceController extends Controller
|
||||
@@ -13,6 +14,10 @@ class ServiceController extends Controller
|
||||
return inertia('Admin/Services/Index', [
|
||||
'servers' => Server::withCount('sites')->latest()->paginate(5, ['*'], 'servers_per_page'),
|
||||
'sites' => Site::with('server:id,name')->latest()->paginate(5, ['*'], 'sites_per_page'),
|
||||
'providers' => Provider::query()
|
||||
->withCount('regions', 'plans', 'servers')
|
||||
->latest()
|
||||
->paginate(5, ['*'], 'providers_per_page'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,13 +18,19 @@ class SettingController extends Controller
|
||||
'documentation' => setting('documentation'),
|
||||
'allow_registration' => setting('allow_registration'),
|
||||
'default_package' => setting('default_package'),
|
||||
'receive_email_on_server_creation' => setting('receive_email_on_server_creation'),
|
||||
'isolate_per_site_per_user' => setting('isolate_per_site_per_user'),
|
||||
'enable_api' => setting('enable_api'),
|
||||
'api_token' => setting('api_token') ? decrypt(setting('api_token')) : null,
|
||||
'default_language' => setting('default_language', 'en')
|
||||
];
|
||||
|
||||
$packages = Package::pluck('name', 'id');
|
||||
|
||||
return inertia('Admin/Settings', [
|
||||
'company_settings' => $settings,
|
||||
'packages' => $packages
|
||||
'packages' => $packages,
|
||||
'languages' => languages()
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -37,8 +43,17 @@ class SettingController extends Controller
|
||||
'support_emails',
|
||||
'allow_registration',
|
||||
'documentation',
|
||||
'default_package'
|
||||
'default_package',
|
||||
'receive_email_on_server_creation',
|
||||
'isolate_per_site_per_user',
|
||||
'enable_api',
|
||||
'api_token',
|
||||
'default_language'
|
||||
]) as $key => $value) {
|
||||
if ($key === 'api_token') {
|
||||
$value = encrypt($value);
|
||||
}
|
||||
|
||||
setting([$key => $value]);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,12 +2,11 @@
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Http\Requests\Admin\ServerAttachRequest;
|
||||
use App\Models\Server;
|
||||
use App\Models\Site;
|
||||
use App\Models\User;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Admin\ServerAttachRequest;
|
||||
|
||||
class SiteController extends Controller
|
||||
{
|
||||
@@ -15,7 +14,7 @@ class SiteController extends Controller
|
||||
{
|
||||
$site = Site::findOrFail($id);
|
||||
|
||||
$users = $site->users()->select('id', 'name', 'email')->get()->map(function($user){
|
||||
$users = $site->users()->select('id', 'name', 'email')->get()->map(function ($user) {
|
||||
return [
|
||||
'id' => $user->id,
|
||||
'name' => $user->name,
|
||||
|
||||
57
app/Http/Controllers/Admin/SynchronizeProviderController.php
Normal file
57
app/Http/Controllers/Admin/SynchronizeProviderController.php
Normal file
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Models\Provider;
|
||||
use App\Services\Ploi\Ploi;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
|
||||
class SynchronizeProviderController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
if (config('app.demo')) {
|
||||
return redirect('/')->with('info', __('This feature is not available in demo mode.'));
|
||||
}
|
||||
|
||||
$ploi = new Ploi(config('services.ploi.token'));
|
||||
|
||||
$availableProviders = $ploi->user()->serverProviders()->getData();
|
||||
|
||||
$currentProviders = Provider::whereNotIn('id', array_keys((array)$availableProviders))->get();
|
||||
|
||||
return inertia('Admin/Services/Providers', [
|
||||
'availableProviders' => $availableProviders,
|
||||
'currentProviders' => $currentProviders
|
||||
]);
|
||||
}
|
||||
|
||||
public function synchronize(Request $request, $providerId)
|
||||
{
|
||||
$ploiProvider = (new Ploi)->user()->serverProviders($providerId)->getData();
|
||||
|
||||
$provider = Provider::updateOrCreate([
|
||||
'ploi_id' => $ploiProvider->id,
|
||||
], [
|
||||
'label' => $ploiProvider->label,
|
||||
'name' => $ploiProvider->name
|
||||
]);
|
||||
|
||||
foreach ($ploiProvider->provider->plans as $plan) {
|
||||
$provider->plans()->updateOrCreate([
|
||||
'plan_id' => $plan->id
|
||||
], [
|
||||
'label' => $plan->name,
|
||||
]);
|
||||
}
|
||||
|
||||
foreach ($ploiProvider->provider->regions as $region) {
|
||||
$provider->regions()->updateOrCreate([
|
||||
'region_id' => $region->id
|
||||
], [
|
||||
'label' => $region->name,
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,10 +2,10 @@
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use App\Jobs\Core\UpdateSystem;
|
||||
use App\Services\VersionChecker;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class SystemController extends Controller
|
||||
{
|
||||
|
||||
@@ -31,7 +31,8 @@ class UserController extends Controller
|
||||
return inertia('Admin/Users/Create', [
|
||||
'packages' => $packages,
|
||||
'languages' => languages(),
|
||||
'defaultPackage' => (string)setting('default_package')
|
||||
'defaultPackage' => (string)setting('default_package'),
|
||||
'defaultLanguage' => (string)setting('default_language'),
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -54,7 +55,7 @@ class UserController extends Controller
|
||||
|
||||
public function show($id)
|
||||
{
|
||||
//
|
||||
// TODO: Implement show feature for a user
|
||||
}
|
||||
|
||||
public function edit($id)
|
||||
|
||||
28
app/Http/Controllers/Api/UserController.php
Normal file
28
app/Http/Controllers/Api/UserController.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Models\User;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Api\UserRequest;
|
||||
use App\Http\Resources\Api\UserResource;
|
||||
|
||||
class UserController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
return UserResource::collection(User::latest()->paginate());
|
||||
}
|
||||
|
||||
public function store(UserRequest $request)
|
||||
{
|
||||
$user = User::create($request->validated());
|
||||
|
||||
return new UserResource($user);
|
||||
}
|
||||
|
||||
public function show($id)
|
||||
{
|
||||
return new UserResource(User::findOrFail($id));
|
||||
}
|
||||
}
|
||||
@@ -8,8 +8,6 @@ class DashboardController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
$package = auth()->user()->package;
|
||||
|
||||
$logs = auth()->user()->systemLogs()
|
||||
->with('model')
|
||||
->latest()
|
||||
@@ -31,9 +29,6 @@ class DashboardController extends Controller
|
||||
return inertia('Dashboard/Index', [
|
||||
'sites' => auth()->user()->sites()->count(),
|
||||
'servers' => auth()->user()->servers()->count(),
|
||||
'package' => [
|
||||
'name' => $package->name ?? 'None'
|
||||
],
|
||||
'logs' => $logs,
|
||||
]);
|
||||
}
|
||||
|
||||
179
app/Http/Controllers/Profile/ProfileBillingController.php
Normal file
179
app/Http/Controllers/Profile/ProfileBillingController.php
Normal file
@@ -0,0 +1,179 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Profile;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use App\Models\User;
|
||||
use App\Models\Package;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Support\Arr;
|
||||
|
||||
class ProfileBillingController extends Controller
|
||||
{
|
||||
public function index(Request $request)
|
||||
{
|
||||
/* @var $user User */
|
||||
$user = auth()->user();
|
||||
|
||||
$sortByType = array_key_first($request->input('sortBy', []));
|
||||
|
||||
$packages = Package::query()
|
||||
->where(function ($query) {
|
||||
return $query
|
||||
->where('price_monthly', '>', 0)
|
||||
->whereNotNull('plan_id');
|
||||
})
|
||||
->when($request->input('sortBy.' . $sortByType), function ($query, $value) use ($sortByType) {
|
||||
if ($sortByType === 'price') {
|
||||
return $value === 'asc'
|
||||
? $query->orderBy('price_monthly', 'asc')
|
||||
: $query->orderBy('price_monthly', 'desc');
|
||||
}
|
||||
if ($sortByType === 'servers') {
|
||||
return $value === 'asc'
|
||||
? $query->orderBy('maximum_servers', 'asc')
|
||||
: $query->orderBy('maximum_servers', 'desc');
|
||||
}
|
||||
if ($sortByType === 'sites') {
|
||||
return $value === 'asc'
|
||||
? $query->orderBy('maximum_sites', 'asc')
|
||||
: $query->orderBy('maximum_sites', 'desc');
|
||||
}
|
||||
if ($sortByType === 'name') {
|
||||
return $value === 'asc'
|
||||
? $query->orderBy('name', 'asc')
|
||||
: $query->orderBy('name', 'desc');
|
||||
}
|
||||
|
||||
return $query;
|
||||
}, function ($query) {
|
||||
return $query->orderBy('price_monthly', 'asc');
|
||||
})
|
||||
->get()
|
||||
->transform(function (Package $package) {
|
||||
$currencies = [
|
||||
Package::CURRENCY_EURO => '€',
|
||||
Package::CURRENCY_USD => '$',
|
||||
Package::CURRENCY_NOK => 'KR ',
|
||||
Package::CURRENCY_CAD => 'CAD $',
|
||||
Package::CURRENCY_AUD => 'AUD $',
|
||||
];
|
||||
|
||||
$package->price_monthly = ($currencies[$package->currency] ?? '[Unknown currency]') . number_format($package->price_monthly, 2, ',', '.');
|
||||
|
||||
return $package;
|
||||
});
|
||||
|
||||
return inertia('Profile/Billing', [
|
||||
'packages' => $packages,
|
||||
'subscription' => $user->subscription('default'),
|
||||
'public_key' => config('cashier.key'),
|
||||
'data_client_secret' => function () use ($user) {
|
||||
$intent = $user->createSetupIntent();
|
||||
return $intent->client_secret;
|
||||
},
|
||||
'card' => [
|
||||
'last_four' => $user->card_last_four,
|
||||
'brand' => $user->card_brand
|
||||
],
|
||||
'filters' => [
|
||||
'sort' => [
|
||||
$sortByType => $request->input('sortBy.' . $sortByType, 'asc'),
|
||||
]
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
public function updateCard(Request $request)
|
||||
{
|
||||
/** @var User $user */
|
||||
$user = $request->user();
|
||||
|
||||
$user->createOrGetStripeCustomer([
|
||||
'name' => $user->name,
|
||||
'description' => $request->input('billing_details')
|
||||
]);
|
||||
|
||||
foreach ($user->paymentMethods() as $paymentMethod) {
|
||||
$paymentMethod->delete();
|
||||
}
|
||||
|
||||
$user->addPaymentMethod($request->get('payment_method'));
|
||||
$user->updateDefaultPaymentMethod($request->get('payment_method'));
|
||||
$user->updateDefaultPaymentMethodFromStripe();
|
||||
|
||||
return redirect()->route('profile.billing.index')->with('success', 'Your card has been added, you can now update your plan');
|
||||
}
|
||||
|
||||
public function updatePlan(Request $request)
|
||||
{
|
||||
/** @var User $user */
|
||||
$user = auth()->user();
|
||||
|
||||
if (!$user->hasStripeId() || !$user->defaultPaymentMethod()) {
|
||||
return redirect()->route('profile.billing.index')->with('error', 'You cannot change your plan without a valid creditcard, please update your billing details first');
|
||||
}
|
||||
|
||||
$plan = Package::query()->findOrFail($request->input('plan'));
|
||||
|
||||
$planId = $plan->plan_id;
|
||||
|
||||
// Only do something if the user is not already subscribed to this plan.
|
||||
if ($user->subscribedToPlan($planId, 'default')) {
|
||||
return redirect()->route('profile.billing.index')->with('error', 'You did not select a different plan');
|
||||
}
|
||||
|
||||
// If the user is already subscribed to the default plan, we have to swap it. Otherwise create a new one.
|
||||
if ($user->subscribed('default')) {
|
||||
$user->subscription('default')->swap($planId);
|
||||
} else {
|
||||
if ($coupon = $request->input('coupon')) {
|
||||
$user->newSubscription('default', $planId)
|
||||
->withCoupon($coupon)
|
||||
->create($user->defaultPaymentMethod()->id);
|
||||
} else {
|
||||
$user->newSubscription('default', $planId)->create($user->defaultPaymentMethod()->id);
|
||||
}
|
||||
}
|
||||
|
||||
$user->package_id = $plan->id;
|
||||
$user->save();
|
||||
|
||||
return redirect()->route('profile.billing.index')->with('success', sprintf("Your plan has been updated to %s", $plan->name));
|
||||
}
|
||||
|
||||
public function cancel(Request $request)
|
||||
{
|
||||
/* @var $user \App\Models\User */
|
||||
$user = $request->user();
|
||||
|
||||
$subscription = $user->subscription('default')->cancel();
|
||||
|
||||
return redirect()->route('profile.billing.index')->with('success', __('Your subscription has been cancelled, your end date is ' . $subscription->ends_at));
|
||||
}
|
||||
|
||||
public function invoices(Request $request)
|
||||
{
|
||||
return $request->user()->invoices()->map(function ($invoice) {
|
||||
$symbol = $invoice->currency === Package::CURRENCY_EURO ? '€' : '$';
|
||||
|
||||
return [
|
||||
'id' => $invoice->id,
|
||||
'created' => Carbon::createFromTimestamp($invoice->created)->format('Y-m-d H:i:s'),
|
||||
'number' => $invoice->number,
|
||||
'status' => $invoice->status,
|
||||
'total' => $symbol . number_format($invoice->total / 100, 2, ',', '.'),
|
||||
'currency' => $invoice->currency,
|
||||
];
|
||||
});
|
||||
}
|
||||
|
||||
public function pdf(Request $request, $id)
|
||||
{
|
||||
return $request->user()->downloadInvoice($id, [
|
||||
'vendor' => setting('name'),
|
||||
'product' => 'Webhosting'
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -37,22 +37,4 @@ class ProfileController extends Controller
|
||||
|
||||
return $mode;
|
||||
}
|
||||
|
||||
public function requestFtpPassword(Request $request)
|
||||
{
|
||||
$this->validate($request, ['password' => 'required|string']);
|
||||
|
||||
if (!Hash::check($request->input('password'), $request->user()->password)) {
|
||||
return response([
|
||||
'message' => 'The given data was invalid',
|
||||
'errors' => [
|
||||
'password' => [
|
||||
trans('auth.failed')
|
||||
]
|
||||
]
|
||||
], 422);
|
||||
}
|
||||
|
||||
return ['ftp_password' => decrypt($request->user()->ftp_password)];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
|
||||
namespace App\Http\Controllers\Profile;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Rules\MatchOldPassword;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Rules\MatchOldPassword;
|
||||
use App\Http\Controllers\Controller;
|
||||
|
||||
class ProfilePasswordController extends Controller
|
||||
{
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
|
||||
namespace App\Http\Controllers\Profile;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Validation\Rule;
|
||||
use App\Http\Controllers\Controller;
|
||||
|
||||
class ProfileSettingController extends Controller
|
||||
{
|
||||
@@ -12,7 +12,8 @@ class ProfileSettingController extends Controller
|
||||
{
|
||||
return inertia('Profile/Settings', [
|
||||
'profile' => [
|
||||
'theme' => auth()->user()->theme
|
||||
'theme' => auth()->user()->theme,
|
||||
'keyboard_shortcuts' => auth()->user()->keyboard_shortcuts,
|
||||
]
|
||||
]);
|
||||
}
|
||||
@@ -27,7 +28,10 @@ class ProfileSettingController extends Controller
|
||||
]
|
||||
]);
|
||||
|
||||
$request->user()->update(['theme' => $request->input('theme')]);
|
||||
$request->user()->update([
|
||||
'theme' => $request->input('theme'),
|
||||
'keyboard_shortcuts' => $request->input('keyboard_shortcuts', true),
|
||||
]);
|
||||
|
||||
return redirect()->route('profile.settings.index')->with('success', __('Instellingen zijn aangepast'));
|
||||
}
|
||||
|
||||
@@ -2,25 +2,112 @@
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Mail\Admin\Server\AdminServerCreatedEmail;
|
||||
use App\Models\Server;
|
||||
use App\Models\User;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Jobs\Servers\CreateServer;
|
||||
use App\Jobs\Servers\DeleteServer;
|
||||
use App\Http\Requests\ServerRequest;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
use App\Http\Resources\ServerResource;
|
||||
use App\Mail\Server\ServerCreatedEmail;
|
||||
use App\Http\Requests\ServerUpdateRequest;
|
||||
|
||||
class ServerController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
$servers = auth()->user()->servers()->latest()->paginate();
|
||||
$servers = auth()->user()->servers()
|
||||
->withCount('sites')
|
||||
->latest()
|
||||
->paginate();
|
||||
|
||||
$providers = auth()->user()->package ? auth()->user()->package->providers()->pluck('name', 'id') : [];
|
||||
|
||||
return inertia('Servers/Index', [
|
||||
'servers' => ServerResource::collection($servers)
|
||||
'servers' => ServerResource::collection($servers),
|
||||
'dataProviders' => $providers
|
||||
]);
|
||||
}
|
||||
|
||||
public function store(Request $request)
|
||||
public function store(ServerRequest $request)
|
||||
{
|
||||
abort_if(!$request->user()->can('create', Server::class), 403);
|
||||
$provider = $request->user()->package->providers()->findOrFail($request->input('provider'));
|
||||
$region = $provider->regions()->findOrFail($request->input('region'));
|
||||
$plan = $provider->plans()->findOrFail($request->input('plan'));
|
||||
|
||||
return 'test';
|
||||
/* @var $server \App\Models\Server */
|
||||
$server = $request->user()->servers()->create([
|
||||
'name' => $request->input('name')
|
||||
]);
|
||||
|
||||
$server->provider()->associate($provider);
|
||||
$server->providerRegion()->associate($region);
|
||||
$server->providerPlan()->associate($plan);
|
||||
$server->save();
|
||||
|
||||
dispatch(new CreateServer($server));
|
||||
|
||||
Mail::to($request->user())->send(new ServerCreatedEmail($request->user(), $server));
|
||||
|
||||
if (setting('receive_email_on_server_creation')) {
|
||||
$admins = User::query()->where('role', User::ADMIN)->get();
|
||||
|
||||
foreach($admins as $admin){
|
||||
Mail::to($admin)->send(new AdminServerCreatedEmail($request->user(), $server));
|
||||
}
|
||||
}
|
||||
|
||||
return redirect()->route('servers.index');
|
||||
}
|
||||
|
||||
public function show($id)
|
||||
{
|
||||
$server = auth()->user()->servers()->findOrFail($id);
|
||||
|
||||
return inertia('Servers/Show', [
|
||||
'server' => $server,
|
||||
'sites' => $server->sites()->latest()->paginate(5, ['*'], 'sites_per_page'),
|
||||
]);
|
||||
}
|
||||
|
||||
public function update(ServerUpdateRequest $request, $id)
|
||||
{
|
||||
$server = $request->user()->servers()->findOrFail($id);
|
||||
|
||||
$server->update([
|
||||
'name' => $request->input('name')
|
||||
]);
|
||||
|
||||
return redirect()->route('servers.settings.show', $id)->with('success', __('Server information has been updated'));
|
||||
}
|
||||
|
||||
public function destroy(Request $request, $id)
|
||||
{
|
||||
$server = $request->user()->servers()->findOrFail($id);
|
||||
|
||||
$this->authorize('delete', $server);
|
||||
|
||||
dispatch(new DeleteServer($server->ploi_id));
|
||||
|
||||
$request->user()->systemLogs()->create([
|
||||
'title' => 'Server ' . $server->name . ' has been deleted',
|
||||
'description' => 'The server ' . $server->name . ' has been deleted by user ' . $request->user()->name
|
||||
]);
|
||||
|
||||
$server->delete();
|
||||
|
||||
return redirect()->route('servers.index')->with('success', __('Your server is deleted'));
|
||||
}
|
||||
|
||||
public function plansAndRegions(Request $request, $providerId)
|
||||
{
|
||||
$provider = $request->user()->package->providers()->findOrFail($providerId);
|
||||
|
||||
return [
|
||||
'regions' => $provider->regions()->pluck('label', 'id'),
|
||||
'plans' => $provider->plans()->pluck('label', 'id'),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
15
app/Http/Controllers/ServerSettingController.php
Normal file
15
app/Http/Controllers/ServerSettingController.php
Normal file
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
class ServerSettingController extends Controller
|
||||
{
|
||||
public function show($id)
|
||||
{
|
||||
$server = auth()->user()->servers()->findOrFail($id);
|
||||
|
||||
return inertia('Servers/Settings', [
|
||||
'server' => $server
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -3,11 +3,14 @@
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Server;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Jobs\Sites\CreateSite;
|
||||
use App\Jobs\Sites\DeleteSite;
|
||||
use App\Http\Requests\SiteRequest;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use App\Http\Resources\SiteResource;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
|
||||
class SiteController extends Controller
|
||||
{
|
||||
@@ -37,9 +40,12 @@ class SiteController extends Controller
|
||||
$server = Server::query()
|
||||
->doesntHave('users')
|
||||
->withCount('sites')
|
||||
->having('sites_count', '<', DB::raw('maximum_sites'))
|
||||
->inRandomOrder()
|
||||
->first();
|
||||
|
||||
if ($server && $server->sites_count >= $server->maximum_sites) {
|
||||
$server = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$server) {
|
||||
@@ -72,13 +78,16 @@ class SiteController extends Controller
|
||||
|
||||
return inertia('Sites/Show', [
|
||||
'site' => $site,
|
||||
'system_user' => $site->getSystemUser(false),
|
||||
'ip_address' => $site->server->ip
|
||||
]);
|
||||
}
|
||||
|
||||
public function destroy($id)
|
||||
public function destroy(Request $request, $id)
|
||||
{
|
||||
$site = auth()->user()->sites()->findOrFail($id);
|
||||
$site = $request->user()->sites()->findOrFail($id);
|
||||
|
||||
$this->authorize('delete', $site);
|
||||
|
||||
dispatch(new DeleteSite($site->server->ploi_id, $site->ploi_id));
|
||||
|
||||
@@ -86,4 +95,28 @@ class SiteController extends Controller
|
||||
|
||||
return redirect()->route('sites.index')->with('success', __('Your website is deleted'));
|
||||
}
|
||||
|
||||
public function requestFtpPassword(Request $request, $id)
|
||||
{
|
||||
if ($request->user()->requires_password_for_ftp) {
|
||||
$this->validate($request, ['password' => 'required|string']);
|
||||
|
||||
if (!Hash::check($request->input('password'), $request->user()->password)) {
|
||||
return response([
|
||||
'message' => 'The given data was invalid',
|
||||
'errors' => [
|
||||
'password' => [
|
||||
trans('auth.failed')
|
||||
]
|
||||
]
|
||||
], 422);
|
||||
}
|
||||
}
|
||||
|
||||
$site = $request->user()->sites()->findOrFail($id);
|
||||
|
||||
$systemUser = $site->getSystemUser();
|
||||
|
||||
return ['ftp_password' => decrypt(Arr::get($systemUser, 'ftp_password'))];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,8 @@ class SiteSettingController extends Controller
|
||||
{
|
||||
$site = auth()->user()->sites()->findOrFail($id);
|
||||
|
||||
$this->authorize('update', $site);
|
||||
|
||||
$availablePhpVersions = $site->server->available_php_versions;
|
||||
|
||||
return inertia('Sites/Settings', [
|
||||
@@ -23,6 +25,8 @@ class SiteSettingController extends Controller
|
||||
{
|
||||
$site = $request->user()->sites()->findOrFail($id);
|
||||
|
||||
$this->authorize('update', $site);
|
||||
|
||||
$site->update($request->all());
|
||||
|
||||
return redirect()->route('sites.settings.show', $id)->with('success', __('Site settings have been updated'));
|
||||
@@ -32,6 +36,8 @@ class SiteSettingController extends Controller
|
||||
{
|
||||
$site = $request->user()->sites()->findOrFail($id);
|
||||
|
||||
$this->authorize('update', $site);
|
||||
|
||||
dispatch(new ChangePhpVersion($site, $request->input('version')));
|
||||
|
||||
$request->user()->systemLogs()->create([
|
||||
|
||||
@@ -23,6 +23,11 @@ class Kernel extends HttpKernel
|
||||
\Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
|
||||
];
|
||||
|
||||
protected $middlewarePriority = [
|
||||
\Inertia\Middleware::class,
|
||||
\App\Http\Middleware\Demo::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* The application's route middleware groups.
|
||||
*
|
||||
@@ -40,6 +45,7 @@ class Kernel extends HttpKernel
|
||||
\App\Http\Middleware\SetLocale::class,
|
||||
\App\Http\Middleware\Demo::class,
|
||||
\App\Http\Middleware\InstallationComplete::class,
|
||||
\App\Http\Middleware\HandleInertiaRequests::class,
|
||||
],
|
||||
|
||||
'api' => [
|
||||
@@ -63,6 +69,7 @@ class Kernel extends HttpKernel
|
||||
'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
|
||||
'can' => \Illuminate\Auth\Middleware\Authorize::class,
|
||||
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
|
||||
'global.api.authenticated' => \App\Http\Middleware\GlobalApiAuthenticated::class,
|
||||
'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
|
||||
'role' => \App\Http\Middleware\RoleMiddleware::class,
|
||||
'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
|
||||
|
||||
35
app/Http/Middleware/GlobalApiAuthenticated.php
Normal file
35
app/Http/Middleware/GlobalApiAuthenticated.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Services\Ploi\Exceptions\Http\Unauthenticated;
|
||||
|
||||
class GlobalApiAuthenticated
|
||||
{
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Closure $next
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle(Request $request, Closure $next)
|
||||
{
|
||||
if (!$this->isAuthenticated($request)) {
|
||||
throw new Unauthenticated('Unauthenticated for global access.');
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
protected function isAuthenticated(Request $request)
|
||||
{
|
||||
return
|
||||
setting('enable_api') &&
|
||||
setting('api_token') &&
|
||||
$request->bearerToken() &&
|
||||
$request->bearerToken() === decrypt(setting('api_token'));
|
||||
}
|
||||
}
|
||||
103
app/Http/Middleware/HandleInertiaRequests.php
Normal file
103
app/Http/Middleware/HandleInertiaRequests.php
Normal file
@@ -0,0 +1,103 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Session;
|
||||
use Inertia\Middleware;
|
||||
|
||||
class HandleInertiaRequests extends Middleware
|
||||
{
|
||||
/**
|
||||
* Determines the current asset version.
|
||||
*
|
||||
* @see https://inertiajs.com/asset-versioning
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return string|null
|
||||
*/
|
||||
public function version(Request $request)
|
||||
{
|
||||
return parent::version($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines the props that are shared by default.
|
||||
*
|
||||
* @see https://inertiajs.com/shared-data
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return array
|
||||
*/
|
||||
public function share(Request $request)
|
||||
{
|
||||
return array_merge(parent::share($request), [
|
||||
'auth' => function () use($request) {
|
||||
$package = auth()->user()->package ?? [];
|
||||
|
||||
$can = $package ? [
|
||||
'servers' => [
|
||||
'create' => Arr::get($package->server_permissions, 'create', false),
|
||||
'update' => Arr::get($package->server_permissions, 'update', false),
|
||||
'delete' => Arr::get($package->server_permissions, 'delete', false),
|
||||
],
|
||||
'sites' => [
|
||||
'create' => Arr::get($package->site_permissions, 'create', false),
|
||||
'update' => Arr::get($package->site_permissions, 'update', false),
|
||||
'delete' => Arr::get($package->site_permissions, 'delete', false),
|
||||
]
|
||||
] : [];
|
||||
|
||||
return [
|
||||
'user' => Auth::user() ? [
|
||||
'id' => Auth::user()->id,
|
||||
'name' => Auth::user()->name,
|
||||
'email' => Auth::user()->email,
|
||||
'role' => Auth::user()->role,
|
||||
'user_name' => Auth::user()->user_name,
|
||||
'avatar' => Auth::user()->getGravatar(),
|
||||
'theme' => Auth::user()->theme,
|
||||
'keyboard_shortcuts' => Auth::user()->keyboard_shortcuts,
|
||||
'requires_password_for_ftp' => Auth::user()->requires_password_for_ftp,
|
||||
] : null,
|
||||
'package' => auth()->user() && auth()->user()->package ? [
|
||||
'name' => auth()->user()->package->name,
|
||||
'maximum_sites' => auth()->user()->package->maximum_sites
|
||||
] : [
|
||||
'name' => __('None')
|
||||
],
|
||||
'can' => $can
|
||||
];
|
||||
},
|
||||
|
||||
'settings' => function () {
|
||||
return [
|
||||
'demo' => config('app.demo'),
|
||||
'name' => setting('name', 'Company'),
|
||||
'support' => setting('support', false),
|
||||
'documentation' => setting('documentation', false),
|
||||
'logo' => setting('logo'),
|
||||
'allow_registration' => setting('allow_registration'),
|
||||
'billing' => config('cashier.key') && config('cashier.secret')
|
||||
];
|
||||
},
|
||||
'flash' => function () {
|
||||
return [
|
||||
'success' => Session::get('success'),
|
||||
'error' => Session::get('error'),
|
||||
'info' => Session::get('info'),
|
||||
];
|
||||
},
|
||||
'errors' => function () {
|
||||
return Session::get('errors')
|
||||
? Session::get('errors')->getBag('default')->getMessages()
|
||||
: (object)[];
|
||||
},
|
||||
'errors_count' => function () {
|
||||
return Session::get('errors')
|
||||
? count(Session::get('errors')->getBag('default')->getMessages())
|
||||
: 0;
|
||||
},
|
||||
]);
|
||||
}
|
||||
}
|
||||
42
app/Http/Requests/Admin/DocumentationArticleRequest.php
Normal file
42
app/Http/Requests/Admin/DocumentationArticleRequest.php
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests\Admin;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class DocumentationArticleRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize()
|
||||
{
|
||||
return auth()->check() && auth()->user()->isAdmin();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'title' => [
|
||||
'required',
|
||||
'string',
|
||||
'max:255'
|
||||
],
|
||||
'content' => [
|
||||
'required',
|
||||
'min:2'
|
||||
],
|
||||
'category_id' => [
|
||||
'required',
|
||||
'exists:documentation_categories,id'
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
||||
38
app/Http/Requests/Admin/DocumentationCategoryRequest.php
Normal file
38
app/Http/Requests/Admin/DocumentationCategoryRequest.php
Normal file
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests\Admin;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class DocumentationCategoryRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize()
|
||||
{
|
||||
return auth()->check() && auth()->user()->isAdmin();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'title' => [
|
||||
'required',
|
||||
'string',
|
||||
'max:255'
|
||||
],
|
||||
'description' => [
|
||||
'nullable',
|
||||
'string'
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
namespace App\Http\Requests\Admin;
|
||||
|
||||
use App\Models\Package;
|
||||
use Illuminate\Validation\Rule;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class PackageRequest extends FormRequest
|
||||
@@ -29,15 +31,56 @@ class PackageRequest extends FormRequest
|
||||
'string',
|
||||
'max:255'
|
||||
],
|
||||
'currency' => [
|
||||
'nullable',
|
||||
Rule::in([
|
||||
Package::CURRENCY_USD,
|
||||
Package::CURRENCY_EURO,
|
||||
Package::CURRENCY_NOK,
|
||||
Package::CURRENCY_AUD,
|
||||
Package::CURRENCY_CAD,
|
||||
])
|
||||
],
|
||||
'maximum_sites' => [
|
||||
'required',
|
||||
'numeric',
|
||||
'min:0',
|
||||
],
|
||||
'server_creation' => [
|
||||
'maximum_servers' => [
|
||||
'required',
|
||||
'boolean'
|
||||
'numeric',
|
||||
'min:0',
|
||||
],
|
||||
'plan_id' => [
|
||||
'nullable',
|
||||
],
|
||||
'price_monthly' => [
|
||||
'nullable',
|
||||
'numeric'
|
||||
],
|
||||
'server_permissions' => [
|
||||
'array'
|
||||
],
|
||||
'site_permissions' => [
|
||||
'array'
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
protected function prepareForValidation()
|
||||
{
|
||||
$merge = [];
|
||||
|
||||
// If we don't have the monthly price filled in, merge a default
|
||||
if (!$this->price_monthly) {
|
||||
$merge['price_monthly'] = 0.000;
|
||||
}
|
||||
|
||||
// If we don't have the currency filled in, merge a default
|
||||
if (!$this->price_monthly) {
|
||||
$merge['currency'] = Package::CURRENCY_USD;
|
||||
}
|
||||
|
||||
$this->merge($merge);
|
||||
}
|
||||
}
|
||||
|
||||
34
app/Http/Requests/Admin/ProviderRequest.php
Normal file
34
app/Http/Requests/Admin/ProviderRequest.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests\Admin;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class ProviderRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize()
|
||||
{
|
||||
return auth()->check() && auth()->user()->isAdmin();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'name' => [
|
||||
'required',
|
||||
'string',
|
||||
'max:255'
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
||||
41
app/Http/Requests/Api/UserRequest.php
Normal file
41
app/Http/Requests/Api/UserRequest.php
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests\Api;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class UserRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize()
|
||||
{
|
||||
return $this->bearerToken() && $this->bearerToken() === setting('api_token');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'name' => [
|
||||
'required',
|
||||
'string',
|
||||
'max:255'
|
||||
],
|
||||
'email' => [
|
||||
'required',
|
||||
'string',
|
||||
'email',
|
||||
'max:255',
|
||||
'unique:users'
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
||||
51
app/Http/Requests/ServerRequest.php
Normal file
51
app/Http/Requests/ServerRequest.php
Normal file
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests;
|
||||
|
||||
use App\Models\Server;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class ServerRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize()
|
||||
{
|
||||
return $this->user()->can('create', Server::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'name' => [
|
||||
'required',
|
||||
'string',
|
||||
'alpha_dash',
|
||||
'max:40'
|
||||
],
|
||||
'provider' => [
|
||||
'required',
|
||||
'not_in:0',
|
||||
'exists:provider_plans,id'
|
||||
],
|
||||
'region' => [
|
||||
'required',
|
||||
'not_in:0',
|
||||
'exists:provider_regions,id'
|
||||
],
|
||||
'plan' => [
|
||||
'required',
|
||||
'not_in:0',
|
||||
'exists:provider_plans,id'
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
||||
36
app/Http/Requests/ServerUpdateRequest.php
Normal file
36
app/Http/Requests/ServerUpdateRequest.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests;
|
||||
|
||||
use App\Models\Server;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class ServerUpdateRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize()
|
||||
{
|
||||
return $this->user()->can('update', Server::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'name' => [
|
||||
'required',
|
||||
'string',
|
||||
'alpha_dash',
|
||||
'max:40'
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Http\Requests;
|
||||
|
||||
use App\Models\Site;
|
||||
use App\Rules\Hostname;
|
||||
use App\Rules\ValidateMaximumSites;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
@@ -15,7 +16,7 @@ class SiteRequest extends FormRequest
|
||||
*/
|
||||
public function authorize()
|
||||
{
|
||||
return auth()->check();
|
||||
return $this->user()->can('create', Site::class);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
25
app/Http/Resources/Api/UserResource.php
Normal file
25
app/Http/Resources/Api/UserResource.php
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Resources\Api;
|
||||
|
||||
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)
|
||||
{
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'avatar' => $this->avatar,
|
||||
'name' => $this->name,
|
||||
'email' => $this->email,
|
||||
'created_at' => $this->created_at,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ namespace App\Jobs\Core;
|
||||
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\Artisan;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
@@ -12,26 +13,13 @@ class UpdateSystem implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
shell_exec('git pull origin master');
|
||||
shell_exec('composer install --no-interaction --prefer-dist --no-dev --optimize-autoloader --quiet');
|
||||
|
||||
Artisan::call('horizon:terminate');
|
||||
|
||||
cache()->forget('ploi-core-current-version');
|
||||
}
|
||||
}
|
||||
|
||||
57
app/Jobs/Servers/CreateServer.php
Normal file
57
app/Jobs/Servers/CreateServer.php
Normal file
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
namespace App\Jobs\Servers;
|
||||
|
||||
use App\Models\Server;
|
||||
use App\Services\Ploi\Ploi;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
|
||||
class CreateServer implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
public $server;
|
||||
public $tries = 1;
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*
|
||||
* @param Server $server
|
||||
*/
|
||||
public function __construct(Server $server)
|
||||
{
|
||||
$this->server = $server;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$ploi = new Ploi(config('services.ploi.token'));
|
||||
|
||||
$ploiServer = $ploi->server()->create(
|
||||
$this->server->name,
|
||||
$this->server->provider->ploi_id,
|
||||
$this->server->providerRegion->region_id,
|
||||
$this->server->providerPlan->plan_id,
|
||||
);
|
||||
|
||||
$this->server->ploi_id = $ploiServer->id;
|
||||
$this->server->save();
|
||||
|
||||
// Lets fetch the status after 5 minutes
|
||||
dispatch(new FetchServerStatus($this->server))->delay(now()->addMinutes(5));
|
||||
}
|
||||
|
||||
public function failed(\Exception $exception)
|
||||
{
|
||||
$this->server->delete();
|
||||
}
|
||||
}
|
||||
39
app/Jobs/Servers/DeleteServer.php
Normal file
39
app/Jobs/Servers/DeleteServer.php
Normal file
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
namespace App\Jobs\Servers;
|
||||
|
||||
use App\Services\Ploi\Ploi;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
|
||||
class DeleteServer implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
public $serverPloiId;
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*
|
||||
* @param $serverPloiId
|
||||
*/
|
||||
public function __construct($serverPloiId)
|
||||
{
|
||||
$this->serverPloiId = $serverPloiId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$ploi = new Ploi(config('services.ploi.token'));
|
||||
|
||||
$ploi->server($this->serverPloiId)->delete();
|
||||
}
|
||||
}
|
||||
70
app/Jobs/Servers/FetchServerStatus.php
Normal file
70
app/Jobs/Servers/FetchServerStatus.php
Normal file
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
namespace App\Jobs\Servers;
|
||||
|
||||
use App\Models\Server;
|
||||
use App\Services\Ploi\Ploi;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use App\Traits\JobHasThresholds;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
|
||||
class FetchServerStatus implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, JobHasThresholds;
|
||||
|
||||
public $server;
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*
|
||||
* @param Server $server
|
||||
* @param int $threshold
|
||||
*/
|
||||
public function __construct(Server $server, $threshold = 0)
|
||||
{
|
||||
$this->server = $server;
|
||||
$this->setThreshold($threshold);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
// If we tried over 10 times, game over, delete the site
|
||||
if ($this->hasReachedThresholdLimit(10)) {
|
||||
$this->server->delete();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$ploi = new Ploi;
|
||||
|
||||
$ploiServer = $ploi->server($this->server->ploi_id)->get()->getData();
|
||||
|
||||
if ($ploiServer->status !== Server::STATUS_ACTIVE) {
|
||||
$this->incrementThreshold();
|
||||
|
||||
dispatch(new self($this->server, $this->threshold))->delay(now()->addMinutes(2));
|
||||
|
||||
// Check if an IP address is present already
|
||||
if ($ploiServer->ip_address && !$this->server->ip) {
|
||||
$this->server->ip = $ploiServer->ip_address;
|
||||
$this->server->save();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->server->status = $ploiServer->status;
|
||||
$this->server->ip = $ploiServer->ip_address;
|
||||
$this->server->internal_ip = $ploiServer->internal_ip;
|
||||
$this->server->available_php_versions = $ploiServer->installed_php_versions;
|
||||
$this->server->save();
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@ use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Support\Arr;
|
||||
|
||||
class CreateSite implements ShouldQueue
|
||||
{
|
||||
@@ -35,14 +36,14 @@ class CreateSite implements ShouldQueue
|
||||
{
|
||||
$ploi = new Ploi(config('services.ploi.token'));
|
||||
|
||||
$owner = $this->site->users()->first();
|
||||
$systemUser = $this->site->getSystemUser();
|
||||
|
||||
$ploiSite = $ploi->server($this->site->server->ploi_id)->sites()->create(
|
||||
$this->site->domain,
|
||||
'/public',
|
||||
'/',
|
||||
$owner->user_name,
|
||||
decrypt($owner->ftp_password)
|
||||
Arr::get($systemUser, 'user_name'),
|
||||
decrypt(Arr::get($systemUser, 'ftp_password'))
|
||||
);
|
||||
|
||||
$this->site->ploi_id = $ploiSite->data->id;
|
||||
|
||||
@@ -43,7 +43,7 @@ class FetchSiteStatus implements ShouldQueue
|
||||
return;
|
||||
}
|
||||
|
||||
$ploi = new Ploi(config('services.ploi.token'));
|
||||
$ploi = new Ploi;
|
||||
|
||||
$ploiSite = $ploi->server($this->site->server->ploi_id)->sites()->get($this->site->ploi_id)->getData();
|
||||
|
||||
|
||||
42
app/Mail/Admin/Server/AdminServerCreatedEmail.php
Normal file
42
app/Mail/Admin/Server/AdminServerCreatedEmail.php
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace App\Mail\Admin\Server;
|
||||
|
||||
use App\Models\Server;
|
||||
use App\Models\User;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Mail\Mailable;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class AdminServerCreatedEmail extends Mailable implements ShouldQueue
|
||||
{
|
||||
use Queueable, SerializesModels;
|
||||
|
||||
public $user;
|
||||
public $server;
|
||||
|
||||
/**
|
||||
* Create a new message instance.
|
||||
*
|
||||
* @param User $user
|
||||
* @param Server $server
|
||||
*/
|
||||
public function __construct(User $user, Server $server)
|
||||
{
|
||||
$this->user = $user;
|
||||
$this->server = $server;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the message.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function build()
|
||||
{
|
||||
return $this
|
||||
->subject(__('A user has created a new server'))
|
||||
->markdown('emails.admin.server.new-server');
|
||||
}
|
||||
}
|
||||
42
app/Mail/Server/ServerCreatedEmail.php
Normal file
42
app/Mail/Server/ServerCreatedEmail.php
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace App\Mail\Server;
|
||||
|
||||
use App\Models\User;
|
||||
use App\Models\Server;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Mail\Mailable;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
|
||||
class ServerCreatedEmail extends Mailable implements ShouldQueue
|
||||
{
|
||||
use Queueable, SerializesModels;
|
||||
|
||||
public $user;
|
||||
public $server;
|
||||
|
||||
/**
|
||||
* Create a new message instance.
|
||||
*
|
||||
* @param User $user
|
||||
* @param Server $server
|
||||
*/
|
||||
public function __construct(User $user, Server $server)
|
||||
{
|
||||
$this->user = $user;
|
||||
$this->server = $server;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the message.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function build()
|
||||
{
|
||||
return $this
|
||||
->subject(__('Your new server is being created'))
|
||||
->markdown('emails.server.new-server');
|
||||
}
|
||||
}
|
||||
@@ -4,10 +4,10 @@ namespace App\Mail\User;
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Mail\Mailable;
|
||||
use Illuminate\Support\Facades\URL;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
|
||||
class WelcomeEmail extends Mailable implements ShouldQueue
|
||||
{
|
||||
|
||||
18
app/Models/DocumentationCategory.php
Normal file
18
app/Models/DocumentationCategory.php
Normal file
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class DocumentationCategory extends Model
|
||||
{
|
||||
public $fillable = [
|
||||
'title',
|
||||
'description'
|
||||
];
|
||||
|
||||
public function items()
|
||||
{
|
||||
return $this->hasMany(DocumentationItem::class);
|
||||
}
|
||||
}
|
||||
@@ -6,5 +6,13 @@ use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class DocumentationItem extends Model
|
||||
{
|
||||
//
|
||||
public $fillable = [
|
||||
'title',
|
||||
'content'
|
||||
];
|
||||
|
||||
public function category()
|
||||
{
|
||||
return $this->belongsTo(DocumentationCategory::class, 'documentation_category_id');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,19 +2,32 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Casts\PermissionCast;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Package extends Model
|
||||
{
|
||||
const CURRENCY_EURO = 'eur';
|
||||
const CURRENCY_USD = 'usd';
|
||||
const CURRENCY_NOK = 'nok';
|
||||
const CURRENCY_AUD = 'aud';
|
||||
const CURRENCY_CAD = 'cad';
|
||||
|
||||
public $fillable = [
|
||||
'name',
|
||||
'plan_id', // This does not reflect a internal database relation, it reflects the plan ID from the PSP
|
||||
'currency',
|
||||
'price_hourly',
|
||||
'price_monthly',
|
||||
'maximum_sites',
|
||||
'maximum_servers',
|
||||
'server_creation'
|
||||
'site_permissions',
|
||||
'server_permissions'
|
||||
];
|
||||
|
||||
public $casts = [
|
||||
'server_creation' => 'boolean'
|
||||
'site_permissions' => PermissionCast::class,
|
||||
'server_permissions' => PermissionCast::class,
|
||||
];
|
||||
|
||||
public function users()
|
||||
@@ -22,6 +35,11 @@ class Package extends Model
|
||||
return $this->hasMany(User::class);
|
||||
}
|
||||
|
||||
public function providers()
|
||||
{
|
||||
return $this->belongsToMany(Provider::class);
|
||||
}
|
||||
|
||||
protected static function booted()
|
||||
{
|
||||
static::deleting(function ($package) {
|
||||
|
||||
@@ -8,6 +8,17 @@ class Provider extends Model
|
||||
{
|
||||
protected $guarded = [];
|
||||
|
||||
public function getNameWithLabelAttribute()
|
||||
{
|
||||
$string = $this->name;
|
||||
|
||||
if ($this->label) {
|
||||
$string .= ' (' . $this->label . ')';
|
||||
}
|
||||
|
||||
return $string;
|
||||
}
|
||||
|
||||
public function plans()
|
||||
{
|
||||
return $this->hasMany(ProviderPlan::class);
|
||||
@@ -17,4 +28,14 @@ class Provider extends Model
|
||||
{
|
||||
return $this->hasMany(ProviderRegion::class);
|
||||
}
|
||||
|
||||
public function packages()
|
||||
{
|
||||
return $this->belongsToMany(Package::class);
|
||||
}
|
||||
|
||||
public function servers()
|
||||
{
|
||||
return $this->hasMany(Server::class);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,6 +41,21 @@ class Server extends Model
|
||||
return $this->morphMany(SystemLog::class, 'model');
|
||||
}
|
||||
|
||||
public function provider()
|
||||
{
|
||||
return $this->belongsTo(Provider::class);
|
||||
}
|
||||
|
||||
public function providerRegion()
|
||||
{
|
||||
return $this->belongsTo(ProviderRegion::class);
|
||||
}
|
||||
|
||||
public function providerPlan()
|
||||
{
|
||||
return $this->belongsTo(ProviderPlan::class);
|
||||
}
|
||||
|
||||
public static function booted()
|
||||
{
|
||||
static::deleting(function (self $server) {
|
||||
|
||||
@@ -65,6 +65,11 @@ class Site extends Model
|
||||
return $this->morphMany(SystemLog::class, 'model');
|
||||
}
|
||||
|
||||
public function systemUsers()
|
||||
{
|
||||
return $this->belongsToMany(SiteSystemUser::class, 'site_system_user_attached');
|
||||
}
|
||||
|
||||
protected function serializeDate(DateTimeInterface $date)
|
||||
{
|
||||
return $date->format('Y-m-d H:i:s');
|
||||
@@ -75,12 +80,42 @@ class Site extends Model
|
||||
return $this->status === self::STATUS_ACTIVE;
|
||||
}
|
||||
|
||||
public function getSystemUser($withPassword = true)
|
||||
{
|
||||
if (setting('isolate_per_site_per_user') && $this->systemUsers()->first()) {
|
||||
$user = $this->systemUsers()->first();
|
||||
} else {
|
||||
$user = $this->users()->first();
|
||||
}
|
||||
|
||||
return [
|
||||
'user_name' => $user->user_name,
|
||||
] + ($withPassword ? ['ftp_password' => $user->ftp_password] : []);
|
||||
}
|
||||
|
||||
public static function booted()
|
||||
{
|
||||
static::created(function (self $site) {
|
||||
$site->systemUsers()->create();
|
||||
});
|
||||
|
||||
static::deleting(function (self $site) {
|
||||
foreach ($site->databases as $database) {
|
||||
$database->delete();
|
||||
}
|
||||
|
||||
$ids = $site->systemUsers->pluck('id');
|
||||
// Detach all db users
|
||||
$site->systemUsers()->detach();
|
||||
|
||||
// Loop through ids an remove old users.
|
||||
foreach ($ids as $id) {
|
||||
$record = SiteSystemUser::find($id);
|
||||
if ($record) {
|
||||
$record->delete();
|
||||
}
|
||||
}
|
||||
|
||||
$site->redirects()->delete();
|
||||
$site->cronjobs()->delete();
|
||||
$site->certificates()->delete();
|
||||
|
||||
38
app/Models/SiteSystemUser.php
Normal file
38
app/Models/SiteSystemUser.php
Normal file
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Casts\Encrypted;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class SiteSystemUser extends Model
|
||||
{
|
||||
public $fillable = [
|
||||
'user_name',
|
||||
'ftp_password'
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'ftp_password' => Encrypted::class,
|
||||
];
|
||||
|
||||
public function site()
|
||||
{
|
||||
return $this->belongsToMany(Site::class, 'site_system_user_attached');
|
||||
}
|
||||
|
||||
public function user()
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
|
||||
protected static function booted()
|
||||
{
|
||||
static::creating(function (self $siteSystemUser) {
|
||||
$siteSystemUser->user_name = strtolower(Str::random(10));
|
||||
$siteSystemUser->ftp_password = Str::random();
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -3,17 +3,18 @@
|
||||
namespace App\Models;
|
||||
|
||||
use App\Casts\Encrypted;
|
||||
use App\Mail\User\WelcomeEmail;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
use Illuminate\Support\Str;
|
||||
use Laravel\Cashier\Billable;
|
||||
use App\Mail\User\WelcomeEmail;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
use Illuminate\Notifications\Notifiable;
|
||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||
use Illuminate\Contracts\Translation\HasLocalePreference;
|
||||
|
||||
class User extends Authenticatable implements HasLocalePreference
|
||||
{
|
||||
use Notifiable;
|
||||
use Billable, Notifiable;
|
||||
|
||||
const ADMIN = 'admin';
|
||||
const RESELLER = 'reseller';
|
||||
@@ -30,7 +31,9 @@ class User extends Authenticatable implements HasLocalePreference
|
||||
'notes',
|
||||
'language',
|
||||
'blocked',
|
||||
'theme'
|
||||
'theme',
|
||||
'keyboard_shortcuts',
|
||||
'requires_password_for_ftp'
|
||||
];
|
||||
|
||||
protected $hidden = [
|
||||
@@ -42,6 +45,8 @@ class User extends Authenticatable implements HasLocalePreference
|
||||
protected $casts = [
|
||||
'email_verified_at' => 'datetime',
|
||||
'ftp_password' => Encrypted::class,
|
||||
'keyboard_shortcuts' => 'boolean',
|
||||
'requires_password_for_ftp' => 'boolean'
|
||||
];
|
||||
|
||||
protected $appends = [
|
||||
@@ -119,13 +124,17 @@ class User extends Authenticatable implements HasLocalePreference
|
||||
static::creating(function (self $user) {
|
||||
$user->user_name = strtolower(Str::random(10));
|
||||
$user->ftp_password = Str::random();
|
||||
|
||||
if (!$user->language) {
|
||||
$user->language = setting('default_language', 'en');
|
||||
}
|
||||
});
|
||||
|
||||
static::created(function (self $user) {
|
||||
Mail::to($user)->send(new WelcomeEmail($user));
|
||||
});
|
||||
|
||||
static::deleting(function(self $user){
|
||||
static::deleting(function (self $user) {
|
||||
$user->systemLogs()->delete();
|
||||
$user->servers()->detach();
|
||||
$user->sites()->detach();
|
||||
|
||||
@@ -3,20 +3,25 @@
|
||||
namespace App\Policies;
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Auth\Access\HandlesAuthorization;
|
||||
|
||||
class ServerPolicy
|
||||
{
|
||||
use HandlesAuthorization;
|
||||
|
||||
/**
|
||||
* Determine whether the user can create models.
|
||||
*
|
||||
* @param \App\Models\User $user
|
||||
* @return mixed
|
||||
*/
|
||||
public function create(User $user)
|
||||
{
|
||||
return $user->package->server_creation ?? false;
|
||||
return Arr::get($user->package->server_permissions, 'create', false);
|
||||
}
|
||||
|
||||
public function update(User $user)
|
||||
{
|
||||
return Arr::get($user->package->server_permissions, 'update', false);
|
||||
}
|
||||
|
||||
public function delete(User $user)
|
||||
{
|
||||
return Arr::get($user->package->server_permissions, 'delete', false);
|
||||
}
|
||||
}
|
||||
|
||||
27
app/Policies/SitePolicy.php
Normal file
27
app/Policies/SitePolicy.php
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace App\Policies;
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Auth\Access\HandlesAuthorization;
|
||||
|
||||
class SitePolicy
|
||||
{
|
||||
use HandlesAuthorization;
|
||||
|
||||
public function create(User $user)
|
||||
{
|
||||
return Arr::get($user->package->site_permissions, 'create', false);
|
||||
}
|
||||
|
||||
public function update(User $user)
|
||||
{
|
||||
return Arr::get($user->package->site_permissions, 'update', false);
|
||||
}
|
||||
|
||||
public function delete(User $user)
|
||||
{
|
||||
return Arr::get($user->package->site_permissions, 'delete', false);
|
||||
}
|
||||
}
|
||||
@@ -5,9 +5,7 @@ namespace App\Providers;
|
||||
use App\Models\Setting;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Pagination\UrlWindow;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Request;
|
||||
use Illuminate\Support\Facades\Session;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use Illuminate\Pagination\LengthAwarePaginator;
|
||||
|
||||
@@ -29,74 +27,10 @@ class AppServiceProvider extends ServiceProvider
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Bootstrap any application services.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
$this->registerInertia();
|
||||
$this->registerLengthAwarePaginator();
|
||||
}
|
||||
|
||||
public function registerInertia()
|
||||
{
|
||||
inertia()->version(function () {
|
||||
return md5_file(public_path('mix-manifest.json'));
|
||||
});
|
||||
|
||||
inertia()->share([
|
||||
'auth' => function () {
|
||||
return [
|
||||
'user' => Auth::user() ? [
|
||||
'id' => Auth::user()->id,
|
||||
'name' => Auth::user()->name,
|
||||
'email' => Auth::user()->email,
|
||||
'role' => Auth::user()->role,
|
||||
'user_name' => Auth::user()->user_name,
|
||||
'avatar' => Auth::user()->getGravatar(),
|
||||
'theme' => Auth::user()->theme,
|
||||
] : null,
|
||||
'package' => auth()->user() && auth()->user()->package ? [
|
||||
'maximum_sites' => auth()->user()->package->maximum_sites
|
||||
] : null,
|
||||
'can' => auth()->user() && auth()->user()->package ? [
|
||||
'server_creation' => auth()->user()->package->server_creation
|
||||
] : [],
|
||||
];
|
||||
},
|
||||
|
||||
'settings' => function () {
|
||||
return [
|
||||
'demo' => config('app.demo'),
|
||||
'name' => setting('name', 'Company'),
|
||||
'support' => setting('support', false),
|
||||
'documentation' => setting('documentation', false),
|
||||
'logo' => setting('logo'),
|
||||
'allow_registration' => setting('allow_registration'),
|
||||
];
|
||||
},
|
||||
'flash' => function () {
|
||||
return [
|
||||
'success' => Session::get('success'),
|
||||
'error' => Session::get('error'),
|
||||
'info' => Session::get('info'),
|
||||
];
|
||||
},
|
||||
'errors' => function () {
|
||||
return Session::get('errors')
|
||||
? Session::get('errors')->getBag('default')->getMessages()
|
||||
: (object)[];
|
||||
},
|
||||
'errors_count' => function () {
|
||||
return Session::get('errors')
|
||||
? count(Session::get('errors')->getBag('default')->getMessages())
|
||||
: 0;
|
||||
},
|
||||
]);
|
||||
if (!$this->app->request->is('api*')) {
|
||||
$this->registerLengthAwarePaginator();
|
||||
}
|
||||
}
|
||||
|
||||
protected function registerLengthAwarePaginator()
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use App\Models\Site;
|
||||
use App\Models\Server;
|
||||
use App\Policies\SitePolicy;
|
||||
use App\Policies\ServerPolicy;
|
||||
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
|
||||
|
||||
@@ -15,6 +17,7 @@ class AuthServiceProvider extends ServiceProvider
|
||||
*/
|
||||
protected $policies = [
|
||||
Server::class => ServerPolicy::class,
|
||||
Site::class => SitePolicy::class,
|
||||
];
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,7 +2,10 @@
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use Illuminate\Cache\RateLimiting\Limit;
|
||||
use Illuminate\Support\Facades\RateLimiter;
|
||||
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
|
||||
|
||||
class RouteServiceProvider extends ServiceProvider
|
||||
@@ -30,35 +33,37 @@ class RouteServiceProvider extends ServiceProvider
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
//
|
||||
$this->configureRateLimiting();
|
||||
|
||||
parent::boot();
|
||||
$this->routes(function () {
|
||||
if (setting('enable_api')) {
|
||||
Route::prefix('api')
|
||||
->middleware('api')
|
||||
->namespace($this->namespace . '\Api')
|
||||
->group(base_path('routes/api.php'));
|
||||
}
|
||||
|
||||
Route::middleware('web')
|
||||
->namespace($this->namespace)
|
||||
->group(base_path('routes/web.php'));
|
||||
|
||||
Route::middleware(['web', 'auth', 'role:admin'])
|
||||
->prefix('admin')
|
||||
->as('admin.')
|
||||
->namespace($this->namespace . '\\Admin')
|
||||
->group(base_path('routes/admin.php'));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the routes for the application.
|
||||
* Configure the rate limiters for the application.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function map()
|
||||
protected function configureRateLimiting()
|
||||
{
|
||||
$this->mapWebRoutes();
|
||||
$this->mapAdminRoutes();
|
||||
}
|
||||
|
||||
protected function mapWebRoutes()
|
||||
{
|
||||
Route::middleware('web')
|
||||
->namespace($this->namespace)
|
||||
->group(base_path('routes/web.php'));
|
||||
}
|
||||
|
||||
protected function mapAdminRoutes()
|
||||
{
|
||||
Route::middleware(['web', 'auth', 'role:admin'])
|
||||
->prefix('admin')
|
||||
->as('admin.')
|
||||
->namespace($this->namespace .'\\Admin')
|
||||
->group(base_path('routes/admin.php'));
|
||||
RateLimiter::for('api', function (Request $request) {
|
||||
return Limit::perMinute(60);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
namespace App\Rules;
|
||||
|
||||
use Illuminate\Contracts\Validation\Rule;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Contracts\Validation\Rule;
|
||||
|
||||
class MatchOldPassword implements Rule
|
||||
{
|
||||
|
||||
@@ -83,7 +83,7 @@ class Resource
|
||||
return $this->server;
|
||||
}
|
||||
|
||||
public function setServer(Server $server): self
|
||||
public function setServer(Server $server)
|
||||
{
|
||||
$this->server = $server;
|
||||
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
|
||||
namespace App\Services\Ploi\Resources;
|
||||
|
||||
use stdClass;
|
||||
use App\Services\Ploi\Ploi;
|
||||
use App\Services\Ploi\Exceptions\Http\NotValid;
|
||||
use Services\Ploi\Exceptions\Resource\RequiresId;
|
||||
|
||||
class Server extends Resource
|
||||
@@ -29,6 +31,19 @@ class Server extends Resource
|
||||
return $this->getPloi()->makeAPICall($this->getEndpoint());
|
||||
}
|
||||
|
||||
public function delete(int $id = null)
|
||||
{
|
||||
if ($id) {
|
||||
$this->setId($id);
|
||||
}
|
||||
|
||||
if ($this->getId()) {
|
||||
$this->setEndpoint($this->endpoint . '/' . $this->getId());
|
||||
}
|
||||
|
||||
return $this->getPloi()->makeAPICall($this->getEndpoint(), 'delete');
|
||||
}
|
||||
|
||||
public function logs(int $id = null)
|
||||
{
|
||||
if ($id) {
|
||||
@@ -44,6 +59,48 @@ class Server extends Resource
|
||||
return $this->getPloi()->makeAPICall($this->getEndpoint());
|
||||
}
|
||||
|
||||
public function create(
|
||||
string $name,
|
||||
int $provider,
|
||||
int $region,
|
||||
int $plan
|
||||
): stdClass {
|
||||
|
||||
// Remove the id
|
||||
$this->setId(null);
|
||||
|
||||
// Set the options
|
||||
$options = [
|
||||
'body' => json_encode([
|
||||
'name' => $name,
|
||||
'plan' => $plan,
|
||||
'region' => $region,
|
||||
'credential' => $provider,
|
||||
'type' => 'server',
|
||||
'database_type' => 'mysql',
|
||||
'webserver_type' => 'nginx',
|
||||
'php_version' => '7.4'
|
||||
]),
|
||||
];
|
||||
|
||||
// Make the request
|
||||
try {
|
||||
$response = $this->getPloi()->makeAPICall($this->getEndpoint(), 'post', $options);
|
||||
} catch (NotValid $exception) {
|
||||
$errors = json_decode($exception->getMessage())->errors;
|
||||
|
||||
dd($errors);
|
||||
|
||||
throw $exception;
|
||||
}
|
||||
|
||||
// Set the id of the site
|
||||
$this->setId($response->getJson()->data->id);
|
||||
|
||||
// Return the data
|
||||
return $response->getData();
|
||||
}
|
||||
|
||||
public function sites($id = null): Site
|
||||
{
|
||||
return new Site($this, $id);
|
||||
|
||||
@@ -43,7 +43,7 @@ class Site extends Resource
|
||||
return $this->server;
|
||||
}
|
||||
|
||||
public function setServer(Server $server): self
|
||||
public function setServer(Server $server)
|
||||
{
|
||||
$this->server = $server;
|
||||
|
||||
|
||||
@@ -20,8 +20,14 @@ class User extends Resource
|
||||
return $this->getPloi()->makeAPICall($this->getEndpoint());
|
||||
}
|
||||
|
||||
public function serverProviders()
|
||||
public function serverProviders($id = null)
|
||||
{
|
||||
return $this->getPloi()->makeAPICall($this->getEndpoint() . '/server-providers');
|
||||
$url = $this->getEndpoint() . '/server-providers';
|
||||
|
||||
if ($id) {
|
||||
$url .= '/' . $id;
|
||||
}
|
||||
|
||||
return $this->getPloi()->makeAPICall($url);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,8 @@
|
||||
"fideloper/proxy": "^4.2",
|
||||
"fruitcake/laravel-cors": "^1.0",
|
||||
"guzzlehttp/guzzle": "^6.2|^7.0.1",
|
||||
"inertiajs/inertia-laravel": "^0.2.12",
|
||||
"inertiajs/inertia-laravel": "^0.3.1",
|
||||
"laravel/cashier": "^12.3",
|
||||
"laravel/framework": "^8.4",
|
||||
"laravel/horizon": "^5.0",
|
||||
"laravel/tinker": "^2.0",
|
||||
|
||||
1109
composer.lock
generated
1109
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -17,7 +17,7 @@ return [
|
||||
|
|
||||
*/
|
||||
|
||||
'default' => env('LOG_CHANNEL', 'daily'),
|
||||
'default' => env('LOG_CHANNEL', 'stack'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
@@ -36,8 +36,8 @@ return [
|
||||
|
||||
'channels' => [
|
||||
'stack' => [
|
||||
'driver' => 'daily',
|
||||
'channels' => ['single'],
|
||||
'driver' => 'stack',
|
||||
'channels' => ['daily'],
|
||||
'ignore_exceptions' => false,
|
||||
],
|
||||
|
||||
@@ -51,7 +51,7 @@ return [
|
||||
'driver' => 'daily',
|
||||
'path' => storage_path('logs/laravel.log'),
|
||||
'level' => 'debug',
|
||||
'days' => 14,
|
||||
'days' => 7,
|
||||
],
|
||||
|
||||
'slack' => [
|
||||
|
||||
@@ -4,7 +4,6 @@ namespace Database\Factories;
|
||||
|
||||
use App\Models\Server;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class ServerFactory extends Factory
|
||||
{
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
namespace Database\Factories;
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
|
||||
class UserFactory extends Factory
|
||||
{
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class CreateFailedJobsTable extends Migration
|
||||
{
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class AddPermissionsToPackagesTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('packages', function (Blueprint $table) {
|
||||
$table->json('server_permissions')->nullable()->after('maximum_servers');
|
||||
$table->json('site_permissions')->nullable()->after('server_permissions');
|
||||
|
||||
$table->dropColumn('server_creation');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('packages', function (Blueprint $table) {
|
||||
$table->dropColumn('server_permissions', 'site_permissions');
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class CreatePaymentColumns extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('packages', function (Blueprint $table) {
|
||||
$table->decimal('price_hourly', 10, 4)->after('maximum_servers')->default(0);
|
||||
$table->decimal('price_monthly', 10, 4)->after('price_hourly')->default(0);
|
||||
$table->string('plan_id')->after('price_monthly')->nullable();
|
||||
$table->string('currency')->after('plan_id')->default(\App\Models\Package::CURRENCY_USD);
|
||||
});
|
||||
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->text('billing_details')->after('notes')->nullable();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('packages', function (Blueprint $table) {
|
||||
$table->dropColumn('price_hourly', 'price_monthly', 'plan_id', 'currency');
|
||||
});
|
||||
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->dropColumn('billing_details');
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class CreatePackageProviderTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('package_provider', function (Blueprint $table) {
|
||||
$table->bigInteger('package_id')->unsigned()->nullable();
|
||||
$table->foreign('package_id')->references('id')->on('packages');
|
||||
|
||||
$table->bigInteger('provider_id')->unsigned()->nullable();
|
||||
$table->foreign('provider_id')->references('id')->on('providers');
|
||||
|
||||
$table->boolean('default')->default(false);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('package_provider');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class AddAdditionalColumnsToServersTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('servers', function (Blueprint $table) {
|
||||
$table->bigInteger('provider_id')->after('maximum_sites')->unsigned()->nullable();
|
||||
$table->foreign('provider_id')->references('id')->on('providers');
|
||||
|
||||
$table->bigInteger('provider_plan_id')->after('provider_id')->unsigned()->nullable();
|
||||
$table->foreign('provider_plan_id')->references('id')->on('provider_plans');
|
||||
|
||||
$table->bigInteger('provider_region_id')->after('provider_plan_id')->unsigned()->nullable();
|
||||
$table->foreign('provider_region_id')->references('id')->on('provider_regions');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('servers', function (Blueprint $table) {
|
||||
//
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class AddKeyboardShortcutsToUsersTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->boolean('keyboard_shortcuts')->default(true)->after('blocked');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->dropColumn('keyboard_shortcuts');
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class CreateDocumentationCategoriesTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('documentation_categories', function (Blueprint $table) {
|
||||
$table->id();
|
||||
|
||||
$table->string('title')->nullable();
|
||||
$table->text('description')->nullable();
|
||||
|
||||
$table->timestamps();
|
||||
});
|
||||
|
||||
Schema::table('documentation_items', function (Blueprint $table) {
|
||||
$table->bigInteger('documentation_category_id')->after('content')->unsigned()->nullable();
|
||||
$table->foreign('documentation_category_id')->references('id')->on('documentation_categories');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('documentation_categories');
|
||||
|
||||
Schema::table('documentation_items', function (Blueprint $table) {
|
||||
$table->dropColumn('documentation_category_id');
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class CreateSiteSystemUsersTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('site_system_users', function (Blueprint $table) {
|
||||
$table->id();
|
||||
|
||||
$table->string('user_name')->nullable();
|
||||
$table->text('ftp_password')->nullable();
|
||||
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('site_system_users');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class CreateSiteSystemUserAttached extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('site_system_user_attached', function (Blueprint $table) {
|
||||
$table->bigInteger('site_id')->unsigned()->nullable();
|
||||
$table->foreign('site_id')->references('id')->on('sites');
|
||||
|
||||
$table->bigInteger('site_system_user_id')->unsigned()->nullable();
|
||||
$table->foreign('site_system_user_id')->references('id')->on('site_system_users');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('site_system_user_attached');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class AddRequiresPasswordForFtpToUsersTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->boolean('requires_password_for_ftp')->after('blocked')->default(true);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->dropColumn('requires_password_for_ftp');
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,5 @@ class DatabaseSeeder extends Seeder
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
49
package-lock.json
generated
49
package-lock.json
generated
@@ -1055,21 +1055,29 @@
|
||||
}
|
||||
},
|
||||
"@inertiajs/inertia": {
|
||||
"version": "0.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@inertiajs/inertia/-/inertia-0.2.1.tgz",
|
||||
"integrity": "sha512-FwaCe1c5si1k/K6pAoMiqENBd3jALuEkvJm4Yu2hDJjA23cVJKFcjfC3sD4Us0iRfGXL3QBORmJZpvSEVzEcGw==",
|
||||
"version": "0.4.3",
|
||||
"resolved": "https://registry.npmjs.org/@inertiajs/inertia/-/inertia-0.4.3.tgz",
|
||||
"integrity": "sha512-AEzyL8dd7F7thTLDhzQTi9I+p4r6k9QtacZ2sy5XYmt1E4ZI8MX67z+pKrV9yR2oIG8YAOMEJVhgIyJIg+nJ5Q==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"axios": "^0.19.0",
|
||||
"nprogress": "^0.2.0"
|
||||
"axios": "^0.19.0 || ^0.20.0"
|
||||
}
|
||||
},
|
||||
"@inertiajs/inertia-vue": {
|
||||
"version": "0.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@inertiajs/inertia-vue/-/inertia-vue-0.2.1.tgz",
|
||||
"integrity": "sha512-FRn3y7wOuqCNVLnUex4tTnVl1/f8CZordS+/Kl+qTTcsUxqn2VNR856TYniySqSS7OWxwkQFAoO9LZWqam/7FQ==",
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@inertiajs/inertia-vue/-/inertia-vue-0.3.2.tgz",
|
||||
"integrity": "sha512-rY/K6ChlmpVTAFBTM5kf6mUn5vCpXqY2AO6ZWIF32J5rK8hv8p8dczrhCeMOrekOwVUSmI9WwTwPVhP+p+KaIA==",
|
||||
"dev": true
|
||||
},
|
||||
"@inertiajs/progress": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@inertiajs/progress/-/progress-0.1.2.tgz",
|
||||
"integrity": "sha512-XazXCp9ezY2GLfS37O+LUf6Ni1xYLtrQg+svUi+videi3Aby28d4xRCtrqLvyo3HIWQE5DvNqCPQ8BFbaPZTOA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"nprogress": "^0.2.0"
|
||||
}
|
||||
},
|
||||
"@mrmlnc/readdir-enhanced": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz",
|
||||
@@ -1108,12 +1116,6 @@
|
||||
"postcss-selector-parser": "^6.0.2"
|
||||
}
|
||||
},
|
||||
"@types/color-name": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz",
|
||||
"integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/glob": {
|
||||
"version": "7.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.2.tgz",
|
||||
@@ -1381,9 +1383,9 @@
|
||||
}
|
||||
},
|
||||
"acorn": {
|
||||
"version": "7.4.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.0.tgz",
|
||||
"integrity": "sha512-+G7P8jJmCHr+S+cLfQxygbWhXy+8YTVGzAkpEbcLo2mLoL7tij/VG41QSHACSf5QgYRhMZYHuNc6drJaO0Da+w==",
|
||||
"version": "7.4.1",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz",
|
||||
"integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==",
|
||||
"dev": true
|
||||
},
|
||||
"acorn-node": {
|
||||
@@ -2310,12 +2312,11 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"ansi-styles": {
|
||||
"version": "4.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
|
||||
"integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==",
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/color-name": "^1.1.1",
|
||||
"color-convert": "^2.0.1"
|
||||
}
|
||||
},
|
||||
@@ -9030,9 +9031,9 @@
|
||||
}
|
||||
},
|
||||
"tailwindcss": {
|
||||
"version": "1.8.10",
|
||||
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-1.8.10.tgz",
|
||||
"integrity": "sha512-7QkERG/cWCzsuMqHMwjOaLMVixOGLNBiXsrkssxlE1aWfkxVbGqiuMokR2162xRyaH2mBIHKxmlf1qb3DvIPqw==",
|
||||
"version": "1.9.6",
|
||||
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-1.9.6.tgz",
|
||||
"integrity": "sha512-nY8WYM/RLPqGsPEGEV2z63riyQPcHYZUJpAwdyBzVpxQHOHqHE+F/fvbCeXhdF1+TA5l72vSkZrtYCB9hRcwkQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@fullhuman/postcss-purgecss": "^2.1.2",
|
||||
|
||||
17
package.json
17
package.json
@@ -11,24 +11,25 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
|
||||
"@inertiajs/inertia": "^0.2.1",
|
||||
"@inertiajs/inertia-vue": "^0.2.1",
|
||||
"@inertiajs/inertia": "^0.4.3",
|
||||
"@inertiajs/inertia-vue": "^0.3.2",
|
||||
"@inertiajs/progress": "^0.1.2",
|
||||
"@tailwindcss/ui": "^0.3.0",
|
||||
"axios": "^0.19",
|
||||
"balloon-css": "^1.2.0",
|
||||
"cross-env": "^7.0",
|
||||
"laravel-mix": "^5.0.1",
|
||||
"lodash": "^4.17.15",
|
||||
"portal-vue": "^2.1.7",
|
||||
"resolve-url-loader": "^3.1.0",
|
||||
"sass": "^1.15.2",
|
||||
"sass-loader": "^8.0.0",
|
||||
"vue-template-compiler": "^2.6.11",
|
||||
"tailwindcss": "^1.8.10",
|
||||
"tailwindcss": "^1.9.6",
|
||||
"v-click-outside": "^3.0.1",
|
||||
"vue": "^2.6.11",
|
||||
"portal-vue": "^2.1.7",
|
||||
"vue-meta": "^2.4.0",
|
||||
"vue-clipboard2": "^0.3.1",
|
||||
"vuex": "^3.5.1",
|
||||
"v-click-outside": "^3.0.1"
|
||||
"vue-meta": "^2.4.0",
|
||||
"vue-template-compiler": "^2.6.11",
|
||||
"vuex": "^3.5.1"
|
||||
}
|
||||
}
|
||||
|
||||
2
public/css/app.css
vendored
2
public/css/app.css
vendored
File diff suppressed because one or more lines are too long
2
public/js/0.js
vendored
2
public/js/0.js
vendored
File diff suppressed because one or more lines are too long
2
public/js/1.js
vendored
2
public/js/1.js
vendored
File diff suppressed because one or more lines are too long
2
public/js/10.js
vendored
2
public/js/10.js
vendored
File diff suppressed because one or more lines are too long
2
public/js/11.js
vendored
2
public/js/11.js
vendored
File diff suppressed because one or more lines are too long
2
public/js/12.js
vendored
2
public/js/12.js
vendored
File diff suppressed because one or more lines are too long
2
public/js/13.js
vendored
2
public/js/13.js
vendored
File diff suppressed because one or more lines are too long
2
public/js/14.js
vendored
2
public/js/14.js
vendored
File diff suppressed because one or more lines are too long
2
public/js/15.js
vendored
2
public/js/15.js
vendored
File diff suppressed because one or more lines are too long
2
public/js/16.js
vendored
2
public/js/16.js
vendored
File diff suppressed because one or more lines are too long
2
public/js/17.js
vendored
2
public/js/17.js
vendored
File diff suppressed because one or more lines are too long
2
public/js/18.js
vendored
2
public/js/18.js
vendored
File diff suppressed because one or more lines are too long
2
public/js/19.js
vendored
2
public/js/19.js
vendored
File diff suppressed because one or more lines are too long
2
public/js/2.js
vendored
2
public/js/2.js
vendored
File diff suppressed because one or more lines are too long
2
public/js/20.js
vendored
2
public/js/20.js
vendored
File diff suppressed because one or more lines are too long
2
public/js/21.js
vendored
2
public/js/21.js
vendored
File diff suppressed because one or more lines are too long
2
public/js/22.js
vendored
2
public/js/22.js
vendored
File diff suppressed because one or more lines are too long
2
public/js/23.js
vendored
2
public/js/23.js
vendored
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user