Compare commits
117 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
71158a0030 | ||
|
|
573ce14b7a | ||
|
|
00ef1f470f | ||
|
|
a9357034c8 | ||
|
|
a45f5157d4 | ||
|
|
0d1009b8db | ||
|
|
4f0416cd45 | ||
|
|
76655d76d5 | ||
|
|
3b38cbe9ac | ||
|
|
acf37b8850 | ||
|
|
f47a0699d3 | ||
|
|
8ae429b06b | ||
|
|
626dbbcb49 | ||
|
|
b623dd80fd | ||
|
|
3978a7c9f7 | ||
|
|
7d2acb7438 | ||
|
|
096032301c | ||
|
|
c3e99bf2ff | ||
|
|
9795642bc7 | ||
|
|
4281a432fb | ||
|
|
18df5589b1 | ||
|
|
79536eac2e | ||
|
|
6df82fca04 | ||
|
|
cc14123d27 | ||
|
|
bd5e7b87ff | ||
|
|
42568916d6 | ||
|
|
180803cd8a | ||
|
|
08fcb0ce01 | ||
|
|
120b4c9df7 | ||
|
|
0eb66c10f6 | ||
|
|
1a6b14a250 | ||
|
|
072c8569eb | ||
|
|
5b9378255b | ||
|
|
ac3cf16377 | ||
|
|
31154d20f6 | ||
|
|
f8031ac71a | ||
|
|
898ec6a454 | ||
|
|
9dbd54fdf6 | ||
|
|
934017384d | ||
|
|
aee3dc0d93 | ||
|
|
8046687ae7 | ||
|
|
a89ee796c1 | ||
|
|
af1d3c3edc | ||
|
|
e1a54c2781 | ||
|
|
2d9f7d49b5 | ||
|
|
d10b046033 | ||
|
|
1725c0ff65 | ||
|
|
b852756c82 | ||
|
|
f164d878d7 | ||
|
|
ecc10fffcb | ||
|
|
5c75b015ba | ||
|
|
65f7dc697e | ||
|
|
d981f0f899 | ||
|
|
ab50beefab | ||
|
|
58d1215fd6 | ||
|
|
1dc137c314 | ||
|
|
ca51e9bf5f | ||
|
|
c588583dfc | ||
|
|
57c7c53eae | ||
|
|
76a62d9992 | ||
|
|
575aa1c6b1 | ||
|
|
4867a61fd0 | ||
|
|
a63e8f350b | ||
|
|
6c1b4f28af | ||
|
|
09adccf752 | ||
|
|
79bb522dee | ||
|
|
2adc4bc7ca | ||
|
|
0a81d58051 | ||
|
|
cb84438778 | ||
|
|
b0a76d311c | ||
|
|
5604503d26 | ||
|
|
bf55092b3a | ||
|
|
8286e7f9af | ||
|
|
1255221550 | ||
|
|
f4062cd6e7 | ||
|
|
93377ae753 | ||
|
|
a43cd19efd | ||
|
|
e5eec000d3 | ||
|
|
e5c8a62b32 | ||
|
|
9ae1c145b6 | ||
|
|
eb8b75e4f9 | ||
|
|
7378b82adf | ||
|
|
6f3b588f3d | ||
|
|
a2154cf37c | ||
|
|
9dab5f8093 | ||
|
|
9c9469d2f6 | ||
|
|
94acc313b1 | ||
|
|
448398322f | ||
|
|
3a78339396 | ||
|
|
a925a70448 | ||
|
|
e283eacaa3 | ||
|
|
3df6b6baed | ||
|
|
b65526e040 | ||
|
|
673bbf73be | ||
|
|
094d22eaa8 | ||
|
|
0fdba5fdec | ||
|
|
cf0730be89 | ||
|
|
221e67fd12 | ||
|
|
f9074309d1 | ||
|
|
b2bdbb9e30 | ||
|
|
63d0cb9626 | ||
|
|
e3bb3ae4d1 | ||
|
|
31890005ac | ||
|
|
04a216dee1 | ||
|
|
5d403c1202 | ||
|
|
a2d92c67b3 | ||
|
|
0fec3d82a3 | ||
|
|
6530a64f97 | ||
|
|
656f02d652 | ||
|
|
0dbf3bba4d | ||
|
|
ea47c0c3c6 | ||
|
|
b4072c7892 | ||
|
|
e80cd1990a | ||
|
|
2585cc1db4 | ||
|
|
0225828445 | ||
|
|
63af93592d | ||
|
|
b87fcd0f25 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -12,3 +12,4 @@ npm-debug.log
|
||||
yarn-error.log
|
||||
.idea
|
||||
.php_cs.cache
|
||||
/public/js/resources*.js
|
||||
|
||||
@@ -8,11 +8,11 @@ $finder = Symfony\Component\Finder\Finder::create()
|
||||
->name('*.php')
|
||||
->notName('*.blade.php');
|
||||
|
||||
return PhpCsFixer\Config::create()
|
||||
return (new PhpCsFixer\Config)
|
||||
->setRules([
|
||||
'@PSR2' => true,
|
||||
'array_syntax' => ['syntax' => 'short'],
|
||||
'ordered_imports' => ['sortAlgorithm' => 'length'],
|
||||
'ordered_imports' => ['sort_algorithm' => 'length'],
|
||||
'no_unused_imports' => true,
|
||||
])
|
||||
->setFinder($finder);
|
||||
31
app/Console/Commands/Core/Css.php
Normal file
31
app/Console/Commands/Core/Css.php
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands\Core;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Filesystem\Filesystem;
|
||||
|
||||
class Css extends Command
|
||||
{
|
||||
protected $signature = 'core:css';
|
||||
|
||||
protected $description = 'Generates an theme.css file for you to customize';
|
||||
|
||||
public function handle()
|
||||
{
|
||||
if (file_exists(storage_path('app/public/theme.css')) && !$this->confirm('You seem to already have a theme.css published, are you sure you want to overwrite?')) {
|
||||
$this->warn('Aborted publishing of theme.css.');
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
$this->info('Publishing theme.css file..');
|
||||
|
||||
(new Filesystem)->copy(
|
||||
__DIR__ . '/stubs/theme.css',
|
||||
storage_path('app/public/theme.css')
|
||||
);
|
||||
|
||||
$this->info('Done! You can edit the theme.css file inside storage/public/theme.css');
|
||||
}
|
||||
}
|
||||
31
app/Console/Commands/Core/CssBackup.php
Normal file
31
app/Console/Commands/Core/CssBackup.php
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands\Core;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Filesystem\Filesystem;
|
||||
|
||||
class CssBackup extends Command
|
||||
{
|
||||
protected $signature = 'core:css-backup';
|
||||
|
||||
protected $description = 'Creates a backup from your own created theme.css';
|
||||
|
||||
public function handle()
|
||||
{
|
||||
if (!file_exists(storage_path('app/public/theme.css'))) {
|
||||
$this->warn('There is no custom theme.css, aborting backup.');
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
$this->info('Backing up theme.css file..');
|
||||
|
||||
(new Filesystem)->copy(
|
||||
storage_path('app/public/theme.css'),
|
||||
storage_path('app/public/theme-backup.css')
|
||||
);
|
||||
|
||||
$this->info('Done! You can find the CSS backup file here storage/public/theme-backup.css');
|
||||
}
|
||||
}
|
||||
@@ -12,11 +12,12 @@ use Illuminate\Console\Command;
|
||||
use App\Services\VersionChecker;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Illuminate\Support\Facades\Artisan;
|
||||
|
||||
class Install extends Command
|
||||
{
|
||||
protected $company;
|
||||
protected $signature = 'core:install';
|
||||
protected $signature = 'core:install {--force}';
|
||||
protected $description = 'Installation command for Ploi Core';
|
||||
protected $versionChecker;
|
||||
protected $installationFile = 'app/installation';
|
||||
@@ -34,8 +35,14 @@ class Install extends Command
|
||||
$this->askAboutDefaultPackages();
|
||||
$this->checkApplicationUrl();
|
||||
$this->createInstallationFile();
|
||||
$this->linkStorage();
|
||||
|
||||
$this->info('Succes! Installation has finished.');
|
||||
$this->info('Success! Installation has finished.');
|
||||
$this->line(' ');
|
||||
$this->writeSeparationLine();
|
||||
$this->info('Make sure to also setup emailing, the cronjob and the queue worker.');
|
||||
$this->writeSeparationLine();
|
||||
$this->line(' ');
|
||||
$this->info('Visit your platform at ' . env('APP_URL'));
|
||||
}
|
||||
|
||||
@@ -84,16 +91,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,
|
||||
'maximum_sites' => 30,
|
||||
'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
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -123,7 +160,7 @@ class Install extends Command
|
||||
|
||||
protected function intro()
|
||||
{
|
||||
$this->info('*---------------------------------------------------------------------------*');
|
||||
$this->writeSeparationLine();
|
||||
$this->line('Ploi Core Installation');
|
||||
$this->line('Ploi Core version: ' . $this->versionChecker->currentVersion);
|
||||
$this->line('Ploi Core remote: ' . $this->versionChecker->remoteVersion);
|
||||
@@ -133,13 +170,13 @@ class Install extends Command
|
||||
$this->line('Website: https://ploi-core.io');
|
||||
$this->line('E-mail: core@ploi.io');
|
||||
$this->line('Terms of service: https://ploi-core.io/terms');
|
||||
$this->info('*---------------------------------------------------------------------------*');
|
||||
$this->writeSeparationLine();
|
||||
$this->line('');
|
||||
}
|
||||
|
||||
protected function isInstalled()
|
||||
{
|
||||
if (file_exists(storage_path($this->installationFile))) {
|
||||
if (file_exists(storage_path($this->installationFile)) && !$this->option('force')) {
|
||||
$this->line('');
|
||||
$this->comment('Ploi Core has already been installed before.');
|
||||
$this->comment('If you still want to start installation, remove this file to continue: ./storage/' . $this->installationFile);
|
||||
@@ -173,6 +210,11 @@ class Install extends Command
|
||||
file_put_contents(storage_path($this->installationFile), json_encode($this->getInstallationPayload(), JSON_PRETTY_PRINT));
|
||||
}
|
||||
|
||||
protected function linkStorage()
|
||||
{
|
||||
Artisan::call('storage:link');
|
||||
}
|
||||
|
||||
protected function createDatabaseCredentials(): bool
|
||||
{
|
||||
$storeCredentials = $this->confirm(
|
||||
@@ -342,4 +384,9 @@ class Install extends Command
|
||||
{
|
||||
$this->laravel['config'][$key] = $value;
|
||||
}
|
||||
|
||||
protected function writeSeparationLine()
|
||||
{
|
||||
$this->info('*---------------------------------------------------------------------------*');
|
||||
}
|
||||
}
|
||||
|
||||
78
app/Console/Commands/Core/stubs/theme.css
vendored
Normal file
78
app/Console/Commands/Core/stubs/theme.css
vendored
Normal file
@@ -0,0 +1,78 @@
|
||||
:root {
|
||||
--font-body: 'Inter', sans-serif;
|
||||
|
||||
--color-white: #fff;
|
||||
--color-gray-1: #f7f7f7;
|
||||
--color-gray-2: #e6e6e6;
|
||||
--color-gray-3: #cacaca;
|
||||
--color-gray-4: #888;
|
||||
--color-gray-5: #666;
|
||||
--color-gray-6: #2f2f2f;
|
||||
--color-gray-7: #1b1a1a;
|
||||
--color-gray-8: #101010;
|
||||
|
||||
--color-primary: #1b8ae8;
|
||||
--color-success: #17b35d;
|
||||
--color-warning: #f5a623;
|
||||
--color-danger: #c90c4c;
|
||||
|
||||
--color-text-high-emphasis: var(--color-gray-8);
|
||||
--color-text-medium-emphasis: var(--color-gray-5);
|
||||
--color-text-low-emphasis: var(--color-gray-3);
|
||||
|
||||
--color-text-on-primary: var(--color-white);
|
||||
--color-text-on-success: var(--color-gray-8);
|
||||
--color-text-on-warning: var(--color-white);
|
||||
--color-text-on-danger: var(--color-white);
|
||||
|
||||
--color-border-high-emphasis: var(--color-gray-4);
|
||||
--color-border-medium-emphasis: var(--color-gray-3);
|
||||
--color-border-low-emphasis: var(--color-gray-2);
|
||||
|
||||
--color-backdrop: rgba(0, 0, 0, 0.5);
|
||||
--color-overlay: rgba(255, 255, 255, 0.8);
|
||||
--color-surface-1: var(--color-white);
|
||||
--color-surface-2: var(--color-gray-1);
|
||||
--color-surface-3: var(--color-white);
|
||||
|
||||
--border-radius: 0.5rem;
|
||||
--border-radius-avatar: 4rem;
|
||||
--border-radius-circle: 100%;
|
||||
|
||||
--top-bar-container: 64rem;
|
||||
--top-bar-logo-height: 3.5rem;
|
||||
--top-bar-background-color: var(--color-surface-1);
|
||||
--top-bar-text-color: var(--color-text-medium-emphasis);
|
||||
|
||||
--tab-bar-background-color: var(--color-surface-2);
|
||||
--tab-bar-item-active-background-color: var(--color-surface-1);
|
||||
--tab-bar-item-text-color: var(--color-text-medium-emphasis);
|
||||
--tab-bar-item-active-text-color: var(--color-text-high-emphasis);
|
||||
|
||||
--breadcrumbs-text-color: var(--color-text-medium-emphasis);
|
||||
}
|
||||
|
||||
.theme--dark {
|
||||
--color-primary: #63a6f5;
|
||||
--color-success: #50e3c2;
|
||||
--color-warning: #f5a623;
|
||||
--color-danger: #d4667c;
|
||||
|
||||
--color-text-high-emphasis: var(--color-white);
|
||||
--color-text-medium-emphasis: var(--color-gray-3);
|
||||
--color-text-low-emphasis: var(--color-gray-5);
|
||||
|
||||
--color-text-on-primary: var(--color-gray-7);
|
||||
--color-text-on-success: var(--color-gray-7);
|
||||
--color-text-on-warning: var(--color-gray-7);
|
||||
--color-text-on-danger: var(--color-gray-7);
|
||||
|
||||
--color-border-high-emphasis: var(--color-gray-4);
|
||||
--color-border-medium-emphasis: var(--color-gray-5);
|
||||
--color-border-low-emphasis: var(--color-gray-6);
|
||||
|
||||
--color-surface-1: var(--color-gray-7);
|
||||
--color-surface-2: var(--color-gray-6);
|
||||
--color-surface-3: var(--color-gray-6);
|
||||
--color-overlay: rgba(0, 0, 0, 0.8);
|
||||
}
|
||||
@@ -3,7 +3,9 @@
|
||||
namespace App\Console;
|
||||
|
||||
use App\Jobs\Core\Ping;
|
||||
use App\Console\Commands\Core\Css;
|
||||
use App\Console\Commands\Core\Install;
|
||||
use App\Console\Commands\Core\CssBackup;
|
||||
use App\Console\Commands\Core\Synchronize;
|
||||
use Illuminate\Console\Scheduling\Schedule;
|
||||
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
|
||||
@@ -11,6 +13,8 @@ use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
|
||||
class Kernel extends ConsoleKernel
|
||||
{
|
||||
protected $commands = [
|
||||
Css::class,
|
||||
CssBackup::class,
|
||||
Install::class,
|
||||
Synchronize::class,
|
||||
];
|
||||
|
||||
281
app/Helpers/Country.php
Normal file
281
app/Helpers/Country.php
Normal file
@@ -0,0 +1,281 @@
|
||||
<?php
|
||||
|
||||
if (!function_exists('countries')) {
|
||||
function countries($key = null)
|
||||
{
|
||||
$arr = [
|
||||
'af' => 'Afghanistan',
|
||||
'al' => 'Albania',
|
||||
'dz' => 'Algeria',
|
||||
'as' => 'American Samoa',
|
||||
'ad' => 'Andorra',
|
||||
'ao' => 'Angola',
|
||||
'ai' => 'Anguilla',
|
||||
'aq' => 'Antarctica',
|
||||
'ag' => 'Antigua and Barbuda',
|
||||
'ar' => 'Argentina',
|
||||
'am' => 'Armenia',
|
||||
'aw' => 'Aruba',
|
||||
'au' => 'Australia',
|
||||
'at' => 'Austria',
|
||||
'az' => 'Azerbaijan',
|
||||
'bs' => 'Bahamas',
|
||||
'bh' => 'Bahrain',
|
||||
'bd' => 'Bangladesh',
|
||||
'bb' => 'Barbados',
|
||||
'by' => 'Belarus',
|
||||
'be' => 'Belgium',
|
||||
'bz' => 'Belize',
|
||||
'bj' => 'Benin',
|
||||
'bm' => 'Bermuda',
|
||||
'bt' => 'Bhutan',
|
||||
'bo' => 'Bolivia',
|
||||
'ba' => 'Bosnia and Herzegovina',
|
||||
'bw' => 'Botswana',
|
||||
'bv' => 'Bouvet Island',
|
||||
'br' => 'Brazil',
|
||||
'bq' => 'British Antarctic Territory',
|
||||
'io' => 'British Indian Ocean Territory',
|
||||
'vg' => 'British Virgin Islands',
|
||||
'bn' => 'Brunei',
|
||||
'bg' => 'Bulgaria',
|
||||
'bf' => 'Burkina Faso',
|
||||
'bi' => 'Burundi',
|
||||
'kh' => 'Cambodia',
|
||||
'cm' => 'Cameroon',
|
||||
'ca' => 'Canada',
|
||||
'ct' => 'Canton and Enderbury Islands',
|
||||
'cv' => 'Cape Verde',
|
||||
'ky' => 'Cayman Islands',
|
||||
'cf' => 'Central African Republic',
|
||||
'td' => 'Chad',
|
||||
'cl' => 'Chile',
|
||||
'cn' => 'China',
|
||||
'cx' => 'Christmas Island',
|
||||
'cc' => 'Cocos [Keeling] Islands',
|
||||
'co' => 'Colombia',
|
||||
'km' => 'Comoros',
|
||||
'cg' => 'Congo - Brazzaville',
|
||||
'cd' => 'Congo - Kinshasa',
|
||||
'ck' => 'Cook Islands',
|
||||
'cr' => 'Costa Rica',
|
||||
'hr' => 'Croatia',
|
||||
'cu' => 'Cuba',
|
||||
'cy' => 'Cyprus',
|
||||
'cz' => 'Czech Republic',
|
||||
'ci' => 'Côte d’Ivoire',
|
||||
'dk' => 'Denmark',
|
||||
'dj' => 'Djibouti',
|
||||
'dm' => 'Dominica',
|
||||
'do' => 'Dominican Republic',
|
||||
'nq' => 'Dronning Maud Land',
|
||||
'dd' => 'East Germany',
|
||||
'ec' => 'Ecuador',
|
||||
'eg' => 'Egypt',
|
||||
'sv' => 'El Salvador',
|
||||
'gq' => 'Equatorial Guinea',
|
||||
'er' => 'Eritrea',
|
||||
'ee' => 'Estonia',
|
||||
'et' => 'Ethiopia',
|
||||
'fk' => 'Falkland Islands',
|
||||
'fo' => 'Faroe Islands',
|
||||
'fj' => 'Fiji',
|
||||
'fi' => 'Finland',
|
||||
'fr' => 'France',
|
||||
'gf' => 'French Guiana',
|
||||
'pf' => 'French Polynesia',
|
||||
'tf' => 'French Southern Territories',
|
||||
'fq' => 'French Southern and Antarctic Territories',
|
||||
'ga' => 'Gabon',
|
||||
'gm' => 'Gambia',
|
||||
'ge' => 'Georgia',
|
||||
'de' => 'Germany',
|
||||
'gh' => 'Ghana',
|
||||
'gi' => 'Gibraltar',
|
||||
'gr' => 'Greece',
|
||||
'gl' => 'Greenland',
|
||||
'gd' => 'Grenada',
|
||||
'gp' => 'Guadeloupe',
|
||||
'gu' => 'Guam',
|
||||
'gt' => 'Guatemala',
|
||||
'gg' => 'Guernsey',
|
||||
'gn' => 'Guinea',
|
||||
'gw' => 'Guinea-Bissau',
|
||||
'gy' => 'Guyana',
|
||||
'ht' => 'Haiti',
|
||||
'hm' => 'Heard Island and McDonald Islands',
|
||||
'hn' => 'Honduras',
|
||||
'hk' => 'Hong Kong SAR China',
|
||||
'hu' => 'Hungary',
|
||||
'is' => 'Iceland',
|
||||
'in' => 'India',
|
||||
'id' => 'Indonesia',
|
||||
'ir' => 'Iran',
|
||||
'iq' => 'Iraq',
|
||||
'ie' => 'Ireland',
|
||||
'im' => 'Isle of Man',
|
||||
'il' => 'Israel',
|
||||
'it' => 'Italy',
|
||||
'jm' => 'Jamaica',
|
||||
'jp' => 'Japan',
|
||||
'je' => 'Jersey',
|
||||
'jt' => 'Johnston Island',
|
||||
'jo' => 'Jordan',
|
||||
'kz' => 'Kazakhstan',
|
||||
'ke' => 'Kenya',
|
||||
'ki' => 'Kiribati',
|
||||
'kw' => 'Kuwait',
|
||||
'kg' => 'Kyrgyzstan',
|
||||
'la' => 'Laos',
|
||||
'lv' => 'Latvia',
|
||||
'lb' => 'Lebanon',
|
||||
'ls' => 'Lesotho',
|
||||
'lr' => 'Liberia',
|
||||
'ly' => 'Libya',
|
||||
'li' => 'Liechtenstein',
|
||||
'lt' => 'Lithuania',
|
||||
'lu' => 'Luxembourg',
|
||||
'mo' => 'Macau SAR China',
|
||||
'mk' => 'Macedonia',
|
||||
'mg' => 'Madagascar',
|
||||
'mw' => 'Malawi',
|
||||
'my' => 'Malaysia',
|
||||
'mv' => 'Maldives',
|
||||
'ml' => 'Mali',
|
||||
'mt' => 'Malta',
|
||||
'mh' => 'Marshall Islands',
|
||||
'mq' => 'Martinique',
|
||||
'mr' => 'Mauritania',
|
||||
'mu' => 'Mauritius',
|
||||
'yt' => 'Mayotte',
|
||||
'fx' => 'Metropolitan France',
|
||||
'mx' => 'Mexico',
|
||||
'fm' => 'Micronesia',
|
||||
'mi' => 'Midway Islands',
|
||||
'md' => 'Moldova',
|
||||
'mc' => 'Monaco',
|
||||
'mn' => 'Mongolia',
|
||||
'me' => 'Montenegro',
|
||||
'ms' => 'Montserrat',
|
||||
'ma' => 'Morocco',
|
||||
'mz' => 'Mozambique',
|
||||
'mm' => 'Myanmar [Burma]',
|
||||
'na' => 'Namibia',
|
||||
'nr' => 'Nauru',
|
||||
'np' => 'Nepal',
|
||||
'nl' => 'Netherlands',
|
||||
'an' => 'Netherlands Antilles',
|
||||
'nt' => 'Neutral Zone',
|
||||
'nc' => 'New Caledonia',
|
||||
'nz' => 'New Zealand',
|
||||
'ni' => 'Nicaragua',
|
||||
'ne' => 'Niger',
|
||||
'ng' => 'Nigeria',
|
||||
'nu' => 'Niue',
|
||||
'nf' => 'Norfolk Island',
|
||||
'kp' => 'North Korea',
|
||||
'vd' => 'North Vietnam',
|
||||
'mp' => 'Northern Mariana Islands',
|
||||
'no' => 'Norway',
|
||||
'om' => 'Oman',
|
||||
'pc' => 'Pacific Islands Trust Territory',
|
||||
'pk' => 'Pakistan',
|
||||
'pw' => 'Palau',
|
||||
'ps' => 'Palestinian Territories',
|
||||
'pa' => 'Panama',
|
||||
'pz' => 'Panama Canal Zone',
|
||||
'pg' => 'Papua New Guinea',
|
||||
'py' => 'Paraguay',
|
||||
'yd' => 'People\'s Democratic Republic of Yemen',
|
||||
'pe' => 'Peru',
|
||||
'ph' => 'Philippines',
|
||||
'pn' => 'Pitcairn Islands',
|
||||
'pl' => 'Poland',
|
||||
'pt' => 'Portugal',
|
||||
'pr' => 'Puerto Rico',
|
||||
'qa' => 'Qatar',
|
||||
'ro' => 'Romania',
|
||||
'ru' => 'Russia',
|
||||
'rw' => 'Rwanda',
|
||||
're' => 'Réunion',
|
||||
'bl' => 'Saint Barthélemy',
|
||||
'sh' => 'Saint Helena',
|
||||
'kn' => 'Saint Kitts and Nevis',
|
||||
'lc' => 'Saint Lucia',
|
||||
'mf' => 'Saint Martin',
|
||||
'pm' => 'Saint Pierre and Miquelon',
|
||||
'vc' => 'Saint Vincent and the Grenadines',
|
||||
'ws' => 'Samoa',
|
||||
'sm' => 'San Marino',
|
||||
'sa' => 'Saudi Arabia',
|
||||
'sn' => 'Senegal',
|
||||
'rs' => 'Serbia',
|
||||
'cs' => 'Serbia and Montenegro',
|
||||
'sc' => 'Seychelles',
|
||||
'sl' => 'Sierra Leone',
|
||||
'sg' => 'Singapore',
|
||||
'sk' => 'Slovakia',
|
||||
'si' => 'Slovenia',
|
||||
'sb' => 'Solomon Islands',
|
||||
'so' => 'Somalia',
|
||||
'za' => 'South Africa',
|
||||
'gs' => 'South Georgia and the South Sandwich Islands',
|
||||
'kr' => 'South Korea',
|
||||
'es' => 'Spain',
|
||||
'lk' => 'Sri Lanka',
|
||||
'sd' => 'Sudan',
|
||||
'sr' => 'Suriname',
|
||||
'sj' => 'Svalbard and Jan Mayen',
|
||||
'sz' => 'Swaziland',
|
||||
'se' => 'Sweden',
|
||||
'ch' => 'Switzerland',
|
||||
'sy' => 'Syria',
|
||||
'st' => 'São Tomé and Príncipe',
|
||||
'tw' => 'Taiwan',
|
||||
'tj' => 'Tajikistan',
|
||||
'tz' => 'Tanzania',
|
||||
'th' => 'Thailand',
|
||||
'tl' => 'Timor-Leste',
|
||||
'tg' => 'Togo',
|
||||
'tk' => 'Tokelau',
|
||||
'to' => 'Tonga',
|
||||
'tt' => 'Trinidad and Tobago',
|
||||
'tn' => 'Tunisia',
|
||||
'tr' => 'Turkey',
|
||||
'tm' => 'Turkmenistan',
|
||||
'tc' => 'Turks and Caicos Islands',
|
||||
'tv' => 'Tuvalu',
|
||||
'um' => 'U.S. Minor Outlying Islands',
|
||||
'pu' => 'U.S. Miscellaneous Pacific Islands',
|
||||
'vi' => 'U.S. Virgin Islands',
|
||||
'ug' => 'Uganda',
|
||||
'ua' => 'Ukraine',
|
||||
'su' => 'Union of Soviet Socialist Republics',
|
||||
'ae' => 'United Arab Emirates',
|
||||
'en' => 'United Kingdom (EN)',
|
||||
'uk' => 'United Kingdom (UK)',
|
||||
'gb' => 'United Kingdom (GB)',
|
||||
'us' => 'United States',
|
||||
'zz' => 'Unknown or Invalid Region',
|
||||
'uy' => 'Uruguay',
|
||||
'uz' => 'Uzbekistan',
|
||||
'vu' => 'Vanuatu',
|
||||
'va' => 'Vatican City',
|
||||
've' => 'Venezuela',
|
||||
'vn' => 'Vietnam',
|
||||
'wk' => 'Wake Island',
|
||||
'wf' => 'Wallis and Futuna',
|
||||
'eh' => 'Western Sahara',
|
||||
'ye' => 'Yemen',
|
||||
'zm' => 'Zambia',
|
||||
'zw' => 'Zimbabwe',
|
||||
'ax' => 'Åland Islands',
|
||||
];
|
||||
|
||||
if ($key && array_key_exists($key, $arr)) {
|
||||
return $arr[strtolower($key)];
|
||||
}
|
||||
|
||||
return $arr;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
<?php
|
||||
|
||||
if (!function_exists('languages')) {
|
||||
function languages()
|
||||
function languages(): array
|
||||
{
|
||||
$languages = [
|
||||
'en'
|
||||
|
||||
80
app/Http/Controllers/Admin/AlertController.php
Normal file
80
app/Http/Controllers/Admin/AlertController.php
Normal file
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Models\Alert;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Admin\AlertRequest;
|
||||
|
||||
class AlertController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
return inertia('Admin/Alerts/Index', [
|
||||
'alerts' => Alert::query()->latest()->paginate()
|
||||
]);
|
||||
}
|
||||
|
||||
public function create()
|
||||
{
|
||||
return inertia('Admin/Alerts/Create');
|
||||
}
|
||||
|
||||
public function store(AlertRequest $request)
|
||||
{
|
||||
Alert::create($request->all());
|
||||
|
||||
return redirect()->route('admin.alerts.index');
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the specified resource.
|
||||
*
|
||||
* @param int $id
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function show($id)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for editing the specified resource.
|
||||
*
|
||||
* @param int $id
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function edit($id)
|
||||
{
|
||||
return inertia('Admin/Alerts/Edit', [
|
||||
'alert' => Alert::findOrFail($id)
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param int $id
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function update(AlertRequest $request, $id)
|
||||
{
|
||||
Alert::findOrFail($id)->update($request->all());
|
||||
|
||||
return redirect()->route('admin.alerts.index');
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*
|
||||
* @param int $id
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function destroy($id)
|
||||
{
|
||||
Alert::findOrFail($id)->delete();
|
||||
|
||||
return redirect()->route('admin.alerts.index');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Models\DocumentationItem;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\DocumentationCategory;
|
||||
use App\Http\Requests\Admin\DocumentationArticleRequest;
|
||||
|
||||
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,53 @@
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\DocumentationCategory;
|
||||
use App\Http\Requests\Admin\DocumentationCategoryRequest;
|
||||
|
||||
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'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,4 +25,13 @@ class ProviderController extends Controller
|
||||
|
||||
return redirect()->route('admin.services.index')->with('success', __('Provider has been updated'));
|
||||
}
|
||||
|
||||
public function destroy($id)
|
||||
{
|
||||
$provider = Provider::findOrFail($id);
|
||||
|
||||
$provider->delete();
|
||||
|
||||
return redirect()->route('admin.services.index')->with('success', __('Provider has been deleted'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,9 @@
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Models\Package;
|
||||
use Illuminate\Support\Str;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use App\Http\Requests\Admin\SettingRequest;
|
||||
|
||||
class SettingController extends Controller
|
||||
@@ -19,15 +21,18 @@ class SettingController extends Controller
|
||||
'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()
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -42,16 +47,42 @@ class SettingController extends Controller
|
||||
'documentation',
|
||||
'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);
|
||||
}
|
||||
|
||||
if ($key === 'default_package' && $value === 'false') {
|
||||
$value = null;
|
||||
}
|
||||
|
||||
if ($value === 'false') {
|
||||
$value = 0;
|
||||
}
|
||||
|
||||
if ($value === 'true') {
|
||||
$value = 1;
|
||||
}
|
||||
|
||||
setting([$key => $value]);
|
||||
}
|
||||
|
||||
if ($logo = $request->file('logo')) {
|
||||
// If we previously had a logo, rotate that
|
||||
if ($oldLogo = setting('logo')) {
|
||||
Storage::delete(str_replace('/storage/', '/public/', $oldLogo));
|
||||
}
|
||||
|
||||
$version = Str::random();
|
||||
|
||||
$request->file('logo')->storePubliclyAs('logo', 'logo-' . $version . '.' . $request->file('logo')->extension(), 'public');
|
||||
setting(['logo' => '/storage/logo/logo-' . $version . '.' . $request->file('logo')->extension()]);
|
||||
}
|
||||
|
||||
cache()->forget('core.settings');
|
||||
|
||||
return redirect()->route('admin.settings')->with('success', __('Settings have been updated'));
|
||||
|
||||
@@ -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', 'en'),
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -54,7 +55,17 @@ class UserController extends Controller
|
||||
|
||||
public function show($id)
|
||||
{
|
||||
//
|
||||
$user = User::query()->findOrFail($id);
|
||||
|
||||
$servers = $user->servers()->withCount('sites')->latest()->paginate(5, ['*'], 'page_servers');
|
||||
|
||||
$sites = $user->sites()->with('server:id,name')->latest()->paginate(5, ['*'], 'page_sites');
|
||||
|
||||
return inertia('Admin/Users/Show', [
|
||||
'user' => $user,
|
||||
'sites' => $sites,
|
||||
'servers' => $servers,
|
||||
]);
|
||||
}
|
||||
|
||||
public function edit($id)
|
||||
|
||||
@@ -7,6 +7,7 @@ use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Providers\RouteServiceProvider;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Illuminate\Validation\Rules\Password;
|
||||
use Illuminate\Foundation\Auth\RegistersUsers;
|
||||
|
||||
class RegisterController extends Controller
|
||||
@@ -30,7 +31,15 @@ class RegisterController extends Controller
|
||||
return Validator::make($data, [
|
||||
'name' => ['required', 'string', 'max:255'],
|
||||
'email' => ['required', 'string', 'email', 'max:255', 'unique:users'],
|
||||
'password' => ['required', 'string', 'min:8', 'confirmed'],
|
||||
'password' => [
|
||||
'required',
|
||||
'string',
|
||||
'confirmed',
|
||||
Password::min(6)
|
||||
->letters()
|
||||
->numbers()
|
||||
->uncompromised()
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -45,8 +54,8 @@ class RegisterController extends Controller
|
||||
|
||||
protected function registered(Request $request, $user)
|
||||
{
|
||||
if ($defaultPackage = setting('default_package')) {
|
||||
$user->package_id = $defaultPackage;
|
||||
if (setting('default_package') && setting('default_package') != 'false') {
|
||||
$user->package_id = setting('default_package');
|
||||
$user->save();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,17 +2,43 @@
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Illuminate\Support\Str;
|
||||
use App\Models\DocumentationItem;
|
||||
use App\Http\Resources\DocumentationItemResource;
|
||||
use App\Models\DocumentationCategory;
|
||||
use App\Http\Resources\DocumentationCategoryRouteResource;
|
||||
|
||||
class DocumentationController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
$documentationItems = DocumentationItem::latest()->paginate();
|
||||
$items = DocumentationCategory::query()->oldest()->get();
|
||||
|
||||
return inertia('Documentation/Index', [
|
||||
'items' => DocumentationItemResource::collection($documentationItems)
|
||||
'items' => DocumentationCategoryRouteResource::collection($items)
|
||||
]);
|
||||
}
|
||||
|
||||
public function show(DocumentationCategory $documentationCategory)
|
||||
{
|
||||
$items = DocumentationCategory::query()->oldest()->get();
|
||||
|
||||
return inertia('Documentation/Show', [
|
||||
'category' => $documentationCategory,
|
||||
'articles' => $documentationCategory->items()->latest()->get(),
|
||||
'items' => DocumentationCategoryRouteResource::collection($items)
|
||||
]);
|
||||
}
|
||||
|
||||
public function showArticle(DocumentationCategory $documentationCategory, DocumentationItem $documentationItem)
|
||||
{
|
||||
$items = DocumentationCategory::query()->oldest()->get();
|
||||
|
||||
$documentationItem->content = Str::markdown($documentationItem->content);
|
||||
|
||||
return inertia('Documentation/Article', [
|
||||
'category' => $documentationCategory,
|
||||
'article' => $documentationItem,
|
||||
'items' => DocumentationCategoryRouteResource::collection($items)
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,41 +5,92 @@ namespace App\Http\Controllers\Profile;
|
||||
use Carbon\Carbon;
|
||||
use App\Models\User;
|
||||
use App\Models\Package;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Stripe\Exception\InvalidRequestException;
|
||||
|
||||
class ProfileBillingController extends Controller
|
||||
{
|
||||
public function index()
|
||||
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)
|
||||
->where(function ($query) {
|
||||
return $query
|
||||
->where('price_monthly', '>', 0)
|
||||
->orWhere('price_yearly', '>', 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) {
|
||||
$symbol = $package->currency === Package::CURRENCY_EURO ? '€' : '$';
|
||||
$currency = $this->transformCurrency($package->currency);
|
||||
|
||||
$package->price_monthly = $symbol . number_format($package->price_monthly, 2, ',', '.');
|
||||
$package->period = 'monthly';
|
||||
|
||||
if ($package->price_yearly > 0) {
|
||||
$package->period = 'yearly';
|
||||
}
|
||||
|
||||
$package->price_monthly = ($currency ?? '[Unknown currency]') . number_format($package->price_monthly, 2, ',', '.');
|
||||
$package->price_yearly = ($currency ?? '[Unknown currency]') . number_format($package->price_yearly, 2, ',', '.');
|
||||
|
||||
return $package;
|
||||
});
|
||||
|
||||
$intent = $user->createSetupIntent();
|
||||
try {
|
||||
$clientSecret = $user->createSetupIntent()->client_secret;
|
||||
} catch (\Exception $exception) {
|
||||
return inertia('Profile/BillingError');
|
||||
}
|
||||
|
||||
return inertia('Profile/Billing', [
|
||||
'packages' => $packages,
|
||||
'countries' => countries(),
|
||||
'subscription' => $user->subscription('default'),
|
||||
'public_key' => config('cashier.key'),
|
||||
'data_client_secret' => $intent->client_secret,
|
||||
'data_client_secret' => $clientSecret,
|
||||
'card' => [
|
||||
'last_four' => $user->card_last_four,
|
||||
'brand' => $user->card_brand
|
||||
],
|
||||
'filters' => [
|
||||
'sort' => [
|
||||
$sortByType => $request->input('sortBy.' . $sortByType, 'asc'),
|
||||
]
|
||||
]
|
||||
]);
|
||||
}
|
||||
@@ -50,8 +101,14 @@ class ProfileBillingController extends Controller
|
||||
$user = $request->user();
|
||||
|
||||
$user->createOrGetStripeCustomer([
|
||||
'name' => $user->name,
|
||||
'description' => $request->input('billing_details')
|
||||
'name' => $request->input('billing_details.name'),
|
||||
'description' => 'Ploi Core Customer ' . $request->input('billing_details.name'),
|
||||
'address' => [
|
||||
'line1' => $request->input('billing_details.address.line1'),
|
||||
'postal_code' => $request->input('billing_details.address.postal_code'),
|
||||
'city' => $request->input('billing_details.address.city'),
|
||||
'country' => $request->input('billing_details.address.country'),
|
||||
]
|
||||
]);
|
||||
|
||||
foreach ($user->paymentMethods() as $paymentMethod) {
|
||||
@@ -84,18 +141,27 @@ class ProfileBillingController extends Controller
|
||||
}
|
||||
|
||||
// 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);
|
||||
try {
|
||||
if ($user->subscribed('default')) {
|
||||
$user->subscription('default')->swap($planId);
|
||||
} else {
|
||||
$user->newSubscription('default', $planId)->create($user->defaultPaymentMethod()->id);
|
||||
if ($coupon = $request->input('coupon')) {
|
||||
$user->newSubscription('default', $planId)
|
||||
->withCoupon($coupon)
|
||||
->create($user->defaultPaymentMethod()->id);
|
||||
} else {
|
||||
$user->newSubscription('default', $planId)->create($user->defaultPaymentMethod()->id);
|
||||
}
|
||||
}
|
||||
} catch (InvalidRequestException $exception) {
|
||||
$error = $exception->getJsonBody();
|
||||
|
||||
return redirect()->route('profile.billing.index')->with('error', Arr::get($error, 'error.message'));
|
||||
}
|
||||
|
||||
$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));
|
||||
}
|
||||
|
||||
@@ -112,7 +178,7 @@ class ProfileBillingController extends Controller
|
||||
public function invoices(Request $request)
|
||||
{
|
||||
return $request->user()->invoices()->map(function ($invoice) {
|
||||
$symbol = $invoice->currency === Package::CURRENCY_EURO ? '€' : '$';
|
||||
$symbol = $this->transformCurrency($invoice->currency);
|
||||
|
||||
return [
|
||||
'id' => $invoice->id,
|
||||
@@ -132,4 +198,20 @@ class ProfileBillingController extends Controller
|
||||
'product' => 'Webhosting'
|
||||
]);
|
||||
}
|
||||
|
||||
protected function transformCurrency($key)
|
||||
{
|
||||
$currencies = [
|
||||
Package::CURRENCY_EURO => '€',
|
||||
Package::CURRENCY_USD => '$',
|
||||
Package::CURRENCY_NOK => 'KR ',
|
||||
Package::CURRENCY_CAD => 'CAD $',
|
||||
Package::CURRENCY_AUD => 'AUD $',
|
||||
Package::CURRENCY_GBP => 'GBP £',
|
||||
Package::CURRENCY_INR => 'INR ₹',
|
||||
Package::CURRENCY_THB => 'THB ',
|
||||
];
|
||||
|
||||
return $currencies[strtolower($key)] ?? '$';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ namespace App\Http\Controllers\Profile;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use App\Http\Requests\UserProfileRequest;
|
||||
use App\Http\Resources\UserProfileResource;
|
||||
|
||||
@@ -13,7 +12,8 @@ class ProfileController extends Controller
|
||||
public function index()
|
||||
{
|
||||
return inertia('Profile/Index', [
|
||||
'profile' => new UserProfileResource(auth()->user())
|
||||
'profile' => new UserProfileResource(auth()->user()),
|
||||
'countries' => countries()
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -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)];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Profile;
|
||||
|
||||
use App\Models\UserProvider;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\ProfileIntegrationRequest;
|
||||
|
||||
class ProfileIntegrationController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
$providers = auth()->user()->providers()->latest()->get()->map(function ($provider) {
|
||||
return [
|
||||
'id' => $provider->id,
|
||||
'type' => $provider->type,
|
||||
'created_at' => $provider->created_at->format('Y-m-d H:i:s')
|
||||
];
|
||||
});
|
||||
|
||||
return inertia('Profile/Integrations', [
|
||||
'providers' => $providers,
|
||||
]);
|
||||
}
|
||||
|
||||
public function store(ProfileIntegrationRequest $request)
|
||||
{
|
||||
$request->user()->providers()->updateOrCreate([
|
||||
'type' => UserProvider::TYPE_CLOUDFLARE
|
||||
], [
|
||||
'type' => UserProvider::TYPE_CLOUDFLARE,
|
||||
'token' => $request->input('meta.api_key'),
|
||||
'meta' => [
|
||||
'cloudflare_email' => encrypt($request->input('meta.cloudflare_email'))
|
||||
]
|
||||
]);
|
||||
|
||||
return redirect()->route('profile.integrations.index');
|
||||
}
|
||||
|
||||
public function destroy($providerId)
|
||||
{
|
||||
auth()->user()->providers()->findOrFail($providerId)->delete();
|
||||
|
||||
return redirect()->route('profile.integrations.index');
|
||||
}
|
||||
}
|
||||
@@ -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,8 +2,6 @@
|
||||
|
||||
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;
|
||||
@@ -13,6 +11,7 @@ use Illuminate\Support\Facades\Mail;
|
||||
use App\Http\Resources\ServerResource;
|
||||
use App\Mail\Server\ServerCreatedEmail;
|
||||
use App\Http\Requests\ServerUpdateRequest;
|
||||
use App\Mail\Admin\Server\AdminServerCreatedEmail;
|
||||
|
||||
class ServerController extends Controller
|
||||
{
|
||||
@@ -54,7 +53,7 @@ class ServerController extends Controller
|
||||
if (setting('receive_email_on_server_creation')) {
|
||||
$admins = User::query()->where('role', User::ADMIN)->get();
|
||||
|
||||
foreach($admins as $admin){
|
||||
foreach ($admins as $admin) {
|
||||
Mail::to($admin)->send(new AdminServerCreatedEmail($request->user(), $server));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,12 +3,13 @@
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Server;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Jobs\Sites\CreateSite;
|
||||
use App\Jobs\Sites\DeleteSite;
|
||||
use App\Http\Requests\SiteRequest;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use App\Http\Resources\SiteResource;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
|
||||
class SiteController extends Controller
|
||||
{
|
||||
@@ -36,6 +37,7 @@ class SiteController extends Controller
|
||||
$server = $request->user()->servers()->findOrFail($serverId);
|
||||
} else {
|
||||
$server = Server::query()
|
||||
->where('maximum_sites', '>', 0)
|
||||
->doesntHave('users')
|
||||
->withCount('sites')
|
||||
->inRandomOrder()
|
||||
@@ -76,6 +78,7 @@ class SiteController extends Controller
|
||||
|
||||
return inertia('Sites/Show', [
|
||||
'site' => $site,
|
||||
'system_user' => $site->getSystemUser(false),
|
||||
'ip_address' => $site->server->ip
|
||||
]);
|
||||
}
|
||||
@@ -92,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'))];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,12 @@
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Site;
|
||||
use Illuminate\Support\Arr;
|
||||
use App\Models\UserProvider;
|
||||
use App\Services\Cloudflare;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Requests\SiteDnsRequest;
|
||||
|
||||
class SiteDnsController extends Controller
|
||||
{
|
||||
@@ -16,9 +21,18 @@ class SiteDnsController extends Controller
|
||||
]);
|
||||
}
|
||||
|
||||
public function store(Request $request)
|
||||
public function store(SiteDnsRequest $request, $id)
|
||||
{
|
||||
//
|
||||
$site = auth()->user()->sites()->findOrFail($id);
|
||||
|
||||
$dns = $this->getDnsInstance($site);
|
||||
|
||||
$dns->addRecord(
|
||||
$request->input('name'),
|
||||
$request->input('address'),
|
||||
);
|
||||
|
||||
return redirect()->route('sites.dns.index', $id)->with('success', __('DNS record has been created'));
|
||||
}
|
||||
|
||||
public function update(Request $request, $id)
|
||||
@@ -28,10 +42,31 @@ class SiteDnsController extends Controller
|
||||
|
||||
public function records($id)
|
||||
{
|
||||
$site = auth()->user()->sites()->findOrFail($id);
|
||||
|
||||
$dns = $this->getDnsInstance($site);
|
||||
|
||||
return $dns->listRecords();
|
||||
}
|
||||
|
||||
public function destroy($id)
|
||||
public function destroy($id, $recordId)
|
||||
{
|
||||
//
|
||||
$site = auth()->user()->sites()->findOrFail($id);
|
||||
|
||||
$dns = $this->getDnsInstance($site);
|
||||
|
||||
$dns->deleteRecord($recordId);
|
||||
|
||||
return redirect()->route('sites.dns.index', $id)->with('success', __('DNS record has been removed'));
|
||||
}
|
||||
|
||||
private function getDnsInstance(Site $site)
|
||||
{
|
||||
$provider = auth()->user()->providers()->where('type', UserProvider::TYPE_CLOUDFLARE)->first();
|
||||
|
||||
$cloudflare = new Cloudflare(decrypt(Arr::get($provider->meta, 'cloudflare_email')), decrypt($provider->token));
|
||||
$cloudflare->zone(decrypt($site->dns_id));
|
||||
|
||||
return $cloudflare;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,11 +2,13 @@
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use App\Models\Alert;
|
||||
use Inertia\Middleware;
|
||||
use Illuminate\Support\Arr;
|
||||
use App\Models\UserProvider;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Session;
|
||||
use Inertia\Middleware;
|
||||
|
||||
class HandleInertiaRequests extends Middleware
|
||||
{
|
||||
@@ -14,7 +16,7 @@ class HandleInertiaRequests extends Middleware
|
||||
* Determines the current asset version.
|
||||
*
|
||||
* @see https://inertiajs.com/asset-versioning
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return string|null
|
||||
*/
|
||||
public function version(Request $request)
|
||||
@@ -26,13 +28,13 @@ class HandleInertiaRequests extends Middleware
|
||||
* Defines the props that are shared by default.
|
||||
*
|
||||
* @see https://inertiajs.com/shared-data
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return array
|
||||
*/
|
||||
public function share(Request $request)
|
||||
{
|
||||
return array_merge(parent::share($request), [
|
||||
'auth' => function () use($request) {
|
||||
'auth' => function () use ($request) {
|
||||
$package = auth()->user()->package ?? [];
|
||||
|
||||
$can = $package ? [
|
||||
@@ -57,6 +59,12 @@ class HandleInertiaRequests extends Middleware
|
||||
'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,
|
||||
'address' => Auth::user()->address,
|
||||
'country' => Auth::user()->country,
|
||||
'zip' => Auth::user()->zip,
|
||||
'city' => Auth::user()->city,
|
||||
] : null,
|
||||
'package' => auth()->user() && auth()->user()->package ? [
|
||||
'name' => auth()->user()->package->name,
|
||||
@@ -64,7 +72,10 @@ class HandleInertiaRequests extends Middleware
|
||||
] : [
|
||||
'name' => __('None')
|
||||
],
|
||||
'can' => $can
|
||||
'can' => $can,
|
||||
'integrations' => [
|
||||
'cloudflare' => (bool)auth()->user() ? auth()->user()->providers()->where('type', UserProvider::TYPE_CLOUDFLARE)->count() : false,
|
||||
]
|
||||
];
|
||||
},
|
||||
|
||||
@@ -96,6 +107,24 @@ class HandleInertiaRequests extends Middleware
|
||||
? count(Session::get('errors')->getBag('default')->getMessages())
|
||||
: 0;
|
||||
},
|
||||
'system_alert' => function () {
|
||||
$alert = Alert::query()
|
||||
->where(function ($query) {
|
||||
return $query
|
||||
->whereNull('expires_at')
|
||||
->orWhere('expires_at', '>', now());
|
||||
})
|
||||
->first(['message', 'expires_at', 'type']);
|
||||
|
||||
if (!$alert) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return [
|
||||
'message' => $alert->message,
|
||||
'type' => $alert->type
|
||||
];
|
||||
}
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
50
app/Http/Requests/Admin/AlertRequest.php
Normal file
50
app/Http/Requests/Admin/AlertRequest.php
Normal file
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests\Admin;
|
||||
|
||||
use App\Models\Alert;
|
||||
use Illuminate\Validation\Rule;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class AlertRequest 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 [
|
||||
'message' => [
|
||||
'required',
|
||||
'string',
|
||||
'max:500'
|
||||
],
|
||||
'type' => [
|
||||
'required',
|
||||
'string',
|
||||
Rule::in([
|
||||
Alert::TYPE_INFO,
|
||||
Alert::TYPE_DANGER,
|
||||
Alert::TYPE_WARNING
|
||||
])
|
||||
],
|
||||
'expires_at' => [
|
||||
'nullable',
|
||||
'date',
|
||||
'date_format:Y-m-d H:i:s'
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
||||
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'
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -35,7 +35,13 @@ class PackageRequest extends FormRequest
|
||||
'nullable',
|
||||
Rule::in([
|
||||
Package::CURRENCY_USD,
|
||||
Package::CURRENCY_EURO
|
||||
Package::CURRENCY_EURO,
|
||||
Package::CURRENCY_NOK,
|
||||
Package::CURRENCY_AUD,
|
||||
Package::CURRENCY_CAD,
|
||||
Package::CURRENCY_GBP,
|
||||
Package::CURRENCY_INR,
|
||||
Package::CURRENCY_THB,
|
||||
])
|
||||
],
|
||||
'maximum_sites' => [
|
||||
@@ -53,7 +59,11 @@ class PackageRequest extends FormRequest
|
||||
],
|
||||
'price_monthly' => [
|
||||
'nullable',
|
||||
'numeric'
|
||||
'numeric',
|
||||
],
|
||||
'price_yearly' => [
|
||||
'nullable',
|
||||
'numeric',
|
||||
],
|
||||
'server_permissions' => [
|
||||
'array'
|
||||
|
||||
@@ -33,6 +33,12 @@ class SettingRequest extends FormRequest
|
||||
'email' => [
|
||||
'nullable',
|
||||
'email'
|
||||
],
|
||||
|
||||
'logo' => [
|
||||
'nullable',
|
||||
'image',
|
||||
'max:2000'
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
63
app/Http/Requests/ProfileIntegrationRequest.php
Normal file
63
app/Http/Requests/ProfileIntegrationRequest.php
Normal file
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests;
|
||||
|
||||
use App\Models\UserProvider;
|
||||
use Illuminate\Validation\Rule;
|
||||
use App\Rules\CloudflareGeneralTest;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class ProfileIntegrationRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize()
|
||||
{
|
||||
return auth()->check();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
$rules = [
|
||||
'provider' => [
|
||||
'required',
|
||||
'string',
|
||||
Rule::in([
|
||||
UserProvider::TYPE_CLOUDFLARE
|
||||
])
|
||||
]
|
||||
];
|
||||
|
||||
if ($this->input('provider') === UserProvider::TYPE_CLOUDFLARE) {
|
||||
$rules['meta.api_key'] = [
|
||||
'required',
|
||||
'string',
|
||||
new CloudflareGeneralTest($this->input('meta.cloudflare_email'))
|
||||
];
|
||||
|
||||
$rules['meta.cloudflare_email'] = [
|
||||
'required',
|
||||
'string',
|
||||
'email'
|
||||
];
|
||||
}
|
||||
|
||||
return $rules;
|
||||
}
|
||||
|
||||
public function messages()
|
||||
{
|
||||
return [
|
||||
'meta.api_key.required' => __('The API key field is required'),
|
||||
'meta.cloudflare_email.required' => __('The Cloudflare email field is required'),
|
||||
];
|
||||
}
|
||||
}
|
||||
37
app/Http/Requests/SiteDnsRequest.php
Normal file
37
app/Http/Requests/SiteDnsRequest.php
Normal file
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class SiteDnsRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize()
|
||||
{
|
||||
return auth()->check();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'name' => [
|
||||
'required',
|
||||
'string',
|
||||
],
|
||||
'address' => [
|
||||
'required',
|
||||
'ipv4'
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -41,6 +41,22 @@ class UserProfileRequest extends FormRequest
|
||||
'required',
|
||||
'string',
|
||||
Rule::in(languages()),
|
||||
],
|
||||
'address' => [
|
||||
'nullable',
|
||||
'string'
|
||||
],
|
||||
'country' => [
|
||||
'nullable',
|
||||
'string'
|
||||
],
|
||||
'zip' => [
|
||||
'nullable',
|
||||
'string'
|
||||
],
|
||||
'city' => [
|
||||
'nullable',
|
||||
'string'
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
23
app/Http/Resources/DocumentationCategoryRouteResource.php
Normal file
23
app/Http/Resources/DocumentationCategoryRouteResource.php
Normal file
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Resources;
|
||||
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
class DocumentationCategoryRouteResource extends JsonResource
|
||||
{
|
||||
/**
|
||||
* Transform the resource into an array.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return array
|
||||
*/
|
||||
public function toArray($request)
|
||||
{
|
||||
return [
|
||||
'title' => $this->title,
|
||||
'to' => route('documentation.show', $this->id),
|
||||
'active' => object_get(request()->route('documentationCategory'), 'id') === $this->id ? true : false
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,11 @@ class UserProfileResource extends JsonResource
|
||||
return [
|
||||
'name' => $this->name,
|
||||
'email' => $this->email,
|
||||
'language' => $this->language
|
||||
'language' => $this->language,
|
||||
'address' => $this->address,
|
||||
'country' => $this->country,
|
||||
'zip' => $this->zip,
|
||||
'city' => $this->city
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace App\Jobs\Core;
|
||||
|
||||
use App\Services\Ploi\Ploi;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use App\Services\VersionChecker;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
@@ -16,21 +17,29 @@ class Ping implements ShouldQueue
|
||||
|
||||
public function handle()
|
||||
{
|
||||
$version = new VersionChecker;
|
||||
|
||||
$version->flushVersionData();
|
||||
|
||||
$response = Http::withHeaders([
|
||||
'Accept' => 'application/json',
|
||||
'Content-Type' => 'application/json',
|
||||
'X-Ploi-Core-Key' => config('services.ploi.core-token')
|
||||
])
|
||||
->withToken(config('services.ploi.token'))
|
||||
->get((new Ploi)->url . 'ping');
|
||||
->post((new Ploi)->url . 'ping', [
|
||||
'version' => $version->getApplicationVersion(),
|
||||
'php_version' => phpversion(),
|
||||
'panel_url' => config('app.url')
|
||||
]);
|
||||
|
||||
if (!$response->ok() || !$response->json()) {
|
||||
|
||||
// Something went wrong..
|
||||
|
||||
return;
|
||||
return 1;
|
||||
}
|
||||
|
||||
$response->json();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace App\Jobs\Sites;
|
||||
|
||||
use App\Models\Site;
|
||||
use App\Services\Ploi\Ploi;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
@@ -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;
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
|
||||
namespace App\Mail\Admin\Server;
|
||||
|
||||
use App\Models\Server;
|
||||
use App\Models\User;
|
||||
use App\Models\Server;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Mail\Mailable;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
|
||||
class AdminServerCreatedEmail extends Mailable implements ShouldQueue
|
||||
{
|
||||
|
||||
28
app/Models/Alert.php
Normal file
28
app/Models/Alert.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use DateTimeInterface;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Alert extends Model
|
||||
{
|
||||
const TYPE_INFO = 'info';
|
||||
const TYPE_WARNING = 'warning';
|
||||
const TYPE_DANGER = 'danger';
|
||||
|
||||
public $fillable = [
|
||||
'type',
|
||||
'message',
|
||||
'expires_at'
|
||||
];
|
||||
|
||||
public $dates = [
|
||||
'expires_at'
|
||||
];
|
||||
|
||||
protected function serializeDate(DateTimeInterface $date)
|
||||
{
|
||||
return $date->format('Y-m-d H:i:s');
|
||||
}
|
||||
}
|
||||
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');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,12 @@ class Package extends Model
|
||||
{
|
||||
const CURRENCY_EURO = 'eur';
|
||||
const CURRENCY_USD = 'usd';
|
||||
const CURRENCY_NOK = 'nok';
|
||||
const CURRENCY_AUD = 'aud';
|
||||
const CURRENCY_CAD = 'cad';
|
||||
const CURRENCY_GBP = 'gbp';
|
||||
const CURRENCY_INR = 'inr';
|
||||
const CURRENCY_THB = 'thb';
|
||||
|
||||
public $fillable = [
|
||||
'name',
|
||||
@@ -16,6 +22,7 @@ class Package extends Model
|
||||
'currency',
|
||||
'price_hourly',
|
||||
'price_monthly',
|
||||
'price_yearly',
|
||||
'maximum_sites',
|
||||
'maximum_servers',
|
||||
'site_permissions',
|
||||
|
||||
@@ -38,4 +38,20 @@ class Provider extends Model
|
||||
{
|
||||
return $this->hasMany(Server::class);
|
||||
}
|
||||
|
||||
public static function booted()
|
||||
{
|
||||
static::deleting(function (self $provider) {
|
||||
$provider->regions()->delete();
|
||||
$provider->plans()->delete();
|
||||
$provider->packages()->detach();
|
||||
|
||||
foreach ($provider->servers as $server) {
|
||||
$server->provider_id = null;
|
||||
$server->provider_plan_id = null;
|
||||
$server->provider_region_id = null;
|
||||
$server->save();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
37
app/Models/SiteSystemUser.php
Normal file
37
app/Models/SiteSystemUser.php
Normal file
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Casts\Encrypted;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
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();
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -28,10 +28,13 @@ class User extends Authenticatable implements HasLocalePreference
|
||||
'address',
|
||||
'city',
|
||||
'country',
|
||||
'zip',
|
||||
'notes',
|
||||
'language',
|
||||
'blocked',
|
||||
'theme'
|
||||
'theme',
|
||||
'keyboard_shortcuts',
|
||||
'requires_password_for_ftp'
|
||||
];
|
||||
|
||||
protected $hidden = [
|
||||
@@ -43,6 +46,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 = [
|
||||
@@ -105,6 +110,11 @@ class User extends Authenticatable implements HasLocalePreference
|
||||
return $this->hasMany(SupportTicket::class);
|
||||
}
|
||||
|
||||
public function supportTicketReplies()
|
||||
{
|
||||
return $this->hasMany(SupportTicketReply::class);
|
||||
}
|
||||
|
||||
public function systemLogs()
|
||||
{
|
||||
return $this->hasMany(SystemLog::class);
|
||||
@@ -120,6 +130,10 @@ 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) {
|
||||
@@ -130,6 +144,10 @@ class User extends Authenticatable implements HasLocalePreference
|
||||
$user->systemLogs()->delete();
|
||||
$user->servers()->detach();
|
||||
$user->sites()->detach();
|
||||
foreach ($user->supportTickets as $supportTicket) {
|
||||
$supportTicket->replies()->delete();
|
||||
}
|
||||
$user->supportTickets()->delete();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,16 +2,29 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Casts\Encrypted;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class UserProvider extends Model
|
||||
{
|
||||
const TYPE_DNS = 'dns';
|
||||
const TYPE_CLOUDFLARE = 'cloudflare';
|
||||
|
||||
public $hidden = [
|
||||
'token'
|
||||
];
|
||||
|
||||
public $fillable = [
|
||||
'type',
|
||||
'token',
|
||||
'meta'
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'meta' => 'array',
|
||||
'token' => Encrypted::class,
|
||||
];
|
||||
|
||||
public function user()
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
|
||||
@@ -3,19 +3,10 @@
|
||||
namespace App\Providers;
|
||||
|
||||
use App\Models\Setting;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Pagination\UrlWindow;
|
||||
use Illuminate\Support\Facades\Request;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use Illuminate\Pagination\LengthAwarePaginator;
|
||||
|
||||
class AppServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* Register any application services.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
$this->app->singleton('settings', function ($app) {
|
||||
@@ -27,75 +18,5 @@ class AppServiceProvider extends ServiceProvider
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
if (!$this->app->request->is('api*')) {
|
||||
$this->registerLengthAwarePaginator();
|
||||
}
|
||||
}
|
||||
|
||||
protected function registerLengthAwarePaginator()
|
||||
{
|
||||
$this->app->bind(LengthAwarePaginator::class, function ($app, $values) {
|
||||
return new class(...array_values($values)) extends LengthAwarePaginator {
|
||||
public function only(...$attributes)
|
||||
{
|
||||
return $this->transform(function ($item) use ($attributes) {
|
||||
return $item->only($attributes);
|
||||
});
|
||||
}
|
||||
|
||||
public function transform($callback)
|
||||
{
|
||||
$this->items->transform($callback);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function toArray()
|
||||
{
|
||||
return [
|
||||
'data' => $this->items->toArray(),
|
||||
'links' => $this->links(),
|
||||
'per_page' => $this->perPage(),
|
||||
'total' => $this->total(),
|
||||
];
|
||||
}
|
||||
|
||||
public function links($view = null, $data = [])
|
||||
{
|
||||
$this->appends(Request::all());
|
||||
|
||||
$window = UrlWindow::make($this);
|
||||
|
||||
$elements = array_filter([
|
||||
$window['first'],
|
||||
is_array($window['slider']) ? '...' : null,
|
||||
$window['slider'],
|
||||
is_array($window['last']) ? '...' : null,
|
||||
$window['last'],
|
||||
]);
|
||||
|
||||
return Collection::make($elements)->flatMap(function ($item) {
|
||||
if (is_array($item)) {
|
||||
return Collection::make($item)->map(function ($url, $page) {
|
||||
return [
|
||||
'url' => $url,
|
||||
'label' => $page,
|
||||
'active' => $this->currentPage() === $page,
|
||||
];
|
||||
});
|
||||
} else {
|
||||
return [
|
||||
[
|
||||
'url' => null,
|
||||
'label' => '...',
|
||||
'active' => false,
|
||||
],
|
||||
];
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ class HorizonServiceProvider extends HorizonApplicationServiceProvider
|
||||
protected function gate()
|
||||
{
|
||||
Gate::define('viewHorizon', function ($user) {
|
||||
return $user->isAdmin();
|
||||
return $user->isAdmin() && !config('app.demo');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
58
app/Rules/CloudflareGeneralTest.php
Normal file
58
app/Rules/CloudflareGeneralTest.php
Normal file
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
namespace App\Rules;
|
||||
|
||||
use App\Services\Cloudflare;
|
||||
use Illuminate\Contracts\Validation\Rule;
|
||||
|
||||
class CloudflareGeneralTest implements Rule
|
||||
{
|
||||
public $cloudflareEmail;
|
||||
|
||||
/**
|
||||
* Create a new rule instance.
|
||||
*
|
||||
* @param $cloudflareEmail
|
||||
*/
|
||||
public function __construct($cloudflareEmail)
|
||||
{
|
||||
$this->cloudflareEmail = $cloudflareEmail;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the validation rule passes.
|
||||
*
|
||||
* @param string $attribute
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function passes($attribute, $value)
|
||||
{
|
||||
if (!$this->cloudflareEmail) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
$cloudflare = new Cloudflare($this->cloudflareEmail, $value);
|
||||
|
||||
if ($cloudflare->user()) {
|
||||
return true;
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation error message.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function message()
|
||||
{
|
||||
return __('We could not authenticate you with Cloudflare, are you sure this is the right API key? Also make sure your profile e-mail matches the one in Cloudflare.');
|
||||
}
|
||||
}
|
||||
140
app/Services/Cloudflare.php
Normal file
140
app/Services/Cloudflare.php
Normal file
@@ -0,0 +1,140 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use Illuminate\Support\Str;
|
||||
use GuzzleHttp\Exception\ClientException;
|
||||
|
||||
class Cloudflare
|
||||
{
|
||||
public $adapter;
|
||||
public $zoneId;
|
||||
|
||||
public function __construct($email, $key)
|
||||
{
|
||||
$key = new \Cloudflare\API\Auth\APIKey($email, $key);
|
||||
$this->adapter = new \Cloudflare\API\Adapter\Guzzle($key);
|
||||
}
|
||||
|
||||
public function domains($match = '')
|
||||
{
|
||||
$zones = new \Cloudflare\API\Endpoints\Zones($this->adapter);
|
||||
|
||||
return collect(object_get($zones->listZones($match), 'result'));
|
||||
}
|
||||
|
||||
public function zone($zoneId)
|
||||
{
|
||||
$this->zoneId = $zoneId;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function listRecords($page = 1, $perPage = 50, $order = '', $direction = '', $type = '', $name = '', $content = '', $match = 'all')
|
||||
{
|
||||
$dns = new \Cloudflare\API\Endpoints\DNS($this->adapter);
|
||||
|
||||
if (!$dns || !$this->zoneId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return collect($dns->listRecords($this->zoneId, $type, $name, $content, $page, $perPage, $order, $direction, $match)->result)
|
||||
->map(function ($record) {
|
||||
// We add this property so our UI panel can see whether a record is being edited.
|
||||
$record->edit = false;
|
||||
|
||||
$record->display_content = Str::limit($record->content, 25);
|
||||
|
||||
return $record;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @param null $content
|
||||
* @param string $type
|
||||
* @param int $ttl
|
||||
* @param bool $proxied
|
||||
* @param int $priority
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function addRecord($name, $content = null, $type = 'A', $ttl = 0, $proxied = true, $priority = '0')
|
||||
{
|
||||
if ($content == null && $type = 'A') {
|
||||
$content = $_SERVER['SERVER_ADDR'];
|
||||
}
|
||||
|
||||
$dns = new \Cloudflare\API\Endpoints\DNS($this->adapter);
|
||||
|
||||
try {
|
||||
return $dns->addRecord($this->zoneId, $type, $name, $content, $ttl, $proxied, $priority);
|
||||
} catch (ClientException $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function getRecordByValues($name, $type)
|
||||
{
|
||||
$dns = new \Cloudflare\API\Endpoints\DNS($this->adapter);
|
||||
|
||||
try {
|
||||
return $dns->getRecordID($this->zoneId, $type, $name);
|
||||
} catch (ClientException $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function deleteRecord($id)
|
||||
{
|
||||
$dns = new \Cloudflare\API\Endpoints\DNS($this->adapter);
|
||||
|
||||
try {
|
||||
return $dns->deleteRecord($this->zoneId, $id);
|
||||
} catch (ClientException $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function updateRecord($id, array $data = [])
|
||||
{
|
||||
$dns = new \Cloudflare\API\Endpoints\DNS($this->adapter);
|
||||
|
||||
try {
|
||||
$record = $dns->getRecordDetails($this->zoneId, $id);
|
||||
|
||||
return $dns->updateRecordDetails($this->zoneId, $id, [
|
||||
'type' => object_get($record, 'type'),
|
||||
'name' => array_get($data, 'name'),
|
||||
'content' => array_get($data, 'content'),
|
||||
]);
|
||||
} catch (ClientException $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function toggleProxy($id)
|
||||
{
|
||||
$dns = new \Cloudflare\API\Endpoints\DNS($this->adapter);
|
||||
|
||||
try {
|
||||
$record = $dns->getRecordDetails($this->zoneId, $id);
|
||||
|
||||
return $dns->updateRecordDetails($this->zoneId, $id, [
|
||||
'type' => object_get($record, 'type'),
|
||||
'name' => object_get($record, 'name'),
|
||||
'content' => object_get($record, 'content'),
|
||||
'proxied' => !object_get($record, 'proxied')
|
||||
]);
|
||||
} catch (ClientException $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function user()
|
||||
{
|
||||
$user = new \Cloudflare\API\Endpoints\User($this->adapter);
|
||||
|
||||
return $user->getUserDetails();
|
||||
}
|
||||
}
|
||||
@@ -21,4 +21,9 @@ class Unauthenticated extends Exception
|
||||
{
|
||||
parent::__construct($message);
|
||||
}
|
||||
|
||||
public function render($request)
|
||||
{
|
||||
return redirect()->route('dashboard')->with('error', 'Unable to authenticate with backend server, please check API keys and connection.');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,9 +61,9 @@ class Server extends Resource
|
||||
|
||||
public function create(
|
||||
string $name,
|
||||
int $provider,
|
||||
int $region,
|
||||
int $plan
|
||||
$provider,
|
||||
$region,
|
||||
$plan
|
||||
): stdClass {
|
||||
|
||||
// Remove the id
|
||||
@@ -89,8 +89,6 @@ class Server extends Resource
|
||||
} catch (NotValid $exception) {
|
||||
$errors = json_decode($exception->getMessage())->errors;
|
||||
|
||||
dd($errors);
|
||||
|
||||
throw $exception;
|
||||
}
|
||||
|
||||
|
||||
@@ -67,7 +67,8 @@ class Site extends Resource
|
||||
string $projectRoot = '/',
|
||||
string $systemUser = 'ploi',
|
||||
string $systemUserPassword = null
|
||||
): stdClass {
|
||||
): stdClass
|
||||
{
|
||||
|
||||
// Remove the id
|
||||
$this->setId(null);
|
||||
@@ -100,6 +101,11 @@ class Site extends Resource
|
||||
throw $exception;
|
||||
}
|
||||
|
||||
// TODO: Debugging purposes
|
||||
if (!$response->getJson() || !$response->getJson()->data) {
|
||||
info(json_encode($response->getJson()));
|
||||
}
|
||||
|
||||
// Set the id of the site
|
||||
$this->setId($response->getJson()->data->id);
|
||||
|
||||
@@ -142,7 +148,7 @@ class Site extends Resource
|
||||
return $response->getJson()->data;
|
||||
}
|
||||
|
||||
public function phpVersion($version = '7.4') :stdClass
|
||||
public function phpVersion($version = '7.4'): stdClass
|
||||
{
|
||||
// Set the options
|
||||
$options = [
|
||||
|
||||
@@ -34,4 +34,13 @@ class VersionChecker
|
||||
{
|
||||
return $this->currentVersion < $this->remoteVersion || $this->currentVersion != $this->remoteVersion;
|
||||
}
|
||||
|
||||
public function flushVersionData()
|
||||
{
|
||||
try {
|
||||
cache()->forget('ploi-core-current-version');
|
||||
cache()->forget('ploi-core-remote-version');
|
||||
} catch (\Exception $exception) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
],
|
||||
"license": "MIT",
|
||||
"require": {
|
||||
"php": "^7.3",
|
||||
"php": "^7.3|^8.0",
|
||||
"ext-json": "*",
|
||||
"cloudflare/sdk": "^1.1",
|
||||
"fideloper/proxy": "^4.2",
|
||||
@@ -20,9 +20,10 @@
|
||||
"laravel/framework": "^8.4",
|
||||
"laravel/horizon": "^5.0",
|
||||
"laravel/tinker": "^2.0",
|
||||
"laravel/ui": "^2.1",
|
||||
"laravel/ui": "^2.1|^3.3.0",
|
||||
"league/glide": "^1.6",
|
||||
"pragmarx/google2fa-laravel": "^1.3",
|
||||
"predis/predis": "^1.1",
|
||||
"tightenco/ziggy": "^0.9.4"
|
||||
},
|
||||
"require-dev": {
|
||||
@@ -30,9 +31,10 @@
|
||||
"facade/ignition": "^2.3",
|
||||
"friendsofphp/php-cs-fixer": "^2.16",
|
||||
"fzaninotto/faker": "^1.9.1",
|
||||
"laravel/dusk": "^6.15",
|
||||
"mockery/mockery": "^1.3.1",
|
||||
"nunomaduro/collision": "^5.0",
|
||||
"phpunit/phpunit": "^8.5"
|
||||
"phpunit/phpunit": "^9.5.4"
|
||||
},
|
||||
"config": {
|
||||
"optimize-autoloader": true,
|
||||
|
||||
3795
composer.lock
generated
3795
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -15,7 +15,7 @@ return [
|
||||
|
|
||||
*/
|
||||
|
||||
'domain' => null,
|
||||
'domain' => env('HORIZON_DOMAIN', null),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
@@ -28,7 +28,7 @@ return [
|
||||
|
|
||||
*/
|
||||
|
||||
'path' => 'horizon',
|
||||
'path' => env('HORIZON_PATH', 'horizon'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
@@ -145,13 +145,13 @@ return [
|
||||
| Memory Limit (MB)
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This value describes the maximum amount of memory the Horizon worker
|
||||
| may consume before it is terminated and restarted. You should set
|
||||
| this value according to the resources available to your server.
|
||||
| This value describes the maximum amount of memory the Horizon master
|
||||
| supervisor may consume before it is terminated and restarted. For
|
||||
| configuring these limits on your workers, see the next section.
|
||||
|
|
||||
*/
|
||||
|
||||
'memory_limit' => 64,
|
||||
'memory_limit' => env('HORIZON_MEMORY_LIMIT', 64),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
@@ -164,6 +164,9 @@ return [
|
||||
|
|
||||
*/
|
||||
|
||||
'defaults' => [
|
||||
],
|
||||
|
||||
'environments' => [
|
||||
'production' => [
|
||||
'supervisor-1' => [
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
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\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
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\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
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\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
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\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
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');
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class CreateAlertsTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('alerts', function (Blueprint $table) {
|
||||
$table->id();
|
||||
|
||||
$table->text('message')->nullable();
|
||||
$table->string('type')->nullable()->default(\App\Models\Alert::TYPE_INFO);
|
||||
|
||||
$table->timestamp('expires_at')->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('alerts');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class AddMetaToUserProvidersTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('user_providers', function (Blueprint $table) {
|
||||
$table->json('meta')->nullable()->after('token');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('user_providers', function (Blueprint $table) {
|
||||
$table->dropColumn('meta');
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class AddZipToUsersTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->string('zip')->after('city')->nullable();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->dropColumn('zip');
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class AddPriceYearlyToPackagesTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('packages', function (Blueprint $table) {
|
||||
$table->decimal('price_yearly', 10, 4)->after('price_monthly')->default(0);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('packages', function (Blueprint $table) {
|
||||
$table->dropColumn('price_yearly');
|
||||
});
|
||||
}
|
||||
}
|
||||
20101
package-lock.json
generated
20101
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
35
package.json
35
package.json
@@ -2,34 +2,37 @@
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "npm run development",
|
||||
"development": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
|
||||
"watch": "npm run development -- --watch",
|
||||
"watch-poll": "npm run watch -- --watch-poll",
|
||||
"hot": "cross-env NODE_ENV=development node_modules/webpack-dev-server/bin/webpack-dev-server.js --inline --hot --disable-host-check --config=node_modules/laravel-mix/setup/webpack.config.js",
|
||||
"prod": "npm run production",
|
||||
"production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --no-progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js"
|
||||
"development": "mix",
|
||||
"watch": "mix watch",
|
||||
"watch-poll": "mix watch -- --watch-options-poll=1000",
|
||||
"hot": "mix watch --hot",
|
||||
"production": "mix --production"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
|
||||
"@inertiajs/inertia": "^0.4.3",
|
||||
"@inertiajs/inertia-vue": "^0.3.2",
|
||||
"@inertiajs/progress": "^0.1.2",
|
||||
"@tailwindcss/ui": "^0.3.0",
|
||||
"axios": "^0.19",
|
||||
"@inertiajs/inertia": "^0.9.2",
|
||||
"@inertiajs/inertia-vue": "^0.6.2",
|
||||
"@inertiajs/progress": "^0.2.5",
|
||||
"@tailwindcss/forms": "^0.3.3",
|
||||
"@tailwindcss/typography": "^0.4.1",
|
||||
"autoprefixer": "^10.2.5",
|
||||
"axios": "^0.21.1",
|
||||
"balloon-css": "^1.2.0",
|
||||
"cross-env": "^7.0",
|
||||
"laravel-mix": "^5.0.1",
|
||||
"cross-env": "^7.0.3",
|
||||
"laravel-mix": "^6.0.25",
|
||||
"lodash": "^4.17.15",
|
||||
"portal-vue": "^2.1.7",
|
||||
"postcss": "^8.2.13",
|
||||
"resolve-url-loader": "^3.1.0",
|
||||
"sass": "^1.15.2",
|
||||
"sass-loader": "^8.0.0",
|
||||
"tailwindcss": "^1.9.0",
|
||||
"v-click-outside": "^3.0.1",
|
||||
"tailwindcss": "^2.2.4",
|
||||
"v-click-outside": "^3.1.2",
|
||||
"vue": "^2.6.11",
|
||||
"vue-clipboard2": "^0.3.1",
|
||||
"vue-loader": "^15.9.6",
|
||||
"vue-meta": "^2.4.0",
|
||||
"vue-template-compiler": "^2.6.11",
|
||||
"vuex": "^3.5.1"
|
||||
"vuex": "^3.6.2"
|
||||
}
|
||||
}
|
||||
|
||||
6
public/css/app.css
vendored
6
public/css/app.css
vendored
File diff suppressed because one or more lines are too long
1
public/js/0.js
vendored
1
public/js/0.js
vendored
File diff suppressed because one or more lines are too long
1
public/js/1.js
vendored
1
public/js/1.js
vendored
File diff suppressed because one or more lines are too long
1
public/js/10.js
vendored
1
public/js/10.js
vendored
File diff suppressed because one or more lines are too long
1
public/js/11.js
vendored
1
public/js/11.js
vendored
File diff suppressed because one or more lines are too long
1
public/js/12.js
vendored
1
public/js/12.js
vendored
File diff suppressed because one or more lines are too long
1
public/js/13.js
vendored
1
public/js/13.js
vendored
File diff suppressed because one or more lines are too long
1
public/js/1355.js
vendored
Normal file
1
public/js/1355.js
vendored
Normal file
@@ -0,0 +1 @@
|
||||
(self.webpackChunk=self.webpackChunk||[]).push([[1355],{1355:(t,e,n)=>{"use strict";n.r(e),n.d(e,{default:()=>r});const i={data:function(){return{items:[{title:this.__("Categories"),to:this.route("admin.documentation.index"),active:this.route().current("admin.documentation.index")},{title:this.__("Create category"),to:this.route("admin.documentation.create"),active:this.route().current("admin.documentation.create")},{title:this.__("Articles"),to:this.route("admin.documentation.articles.index"),active:this.route().current("admin.documentation.articles.index")},{title:this.__("Create article"),to:this.route("admin.documentation.articles.create"),active:this.route().current("admin.documentation.articles.create")}]}}};const r=(0,n(1900).Z)(i,(function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("ul",{staticClass:"-ml-4 space-y-1"},t._l(t.items,(function(e){return n("li",[n("inertia-link",{staticClass:"flex items-center h-10 px-4 font-medium text-medium-emphasis",class:{"rounded shadow text-primary bg-surface-3":e.active},attrs:{href:e.to}},[t._v(t._s(e.title)+" "+t._s(e.route)+"\n ")])],1)})),0)}),[],!1,null,null,null).exports},1900:(t,e,n)=>{"use strict";function i(t,e,n,i,r,s,o,a){var c,u="function"==typeof t?t.options:t;if(e&&(u.render=e,u.staticRenderFns=n,u._compiled=!0),i&&(u.functional=!0),s&&(u._scopeId="data-v-"+s),o?(c=function(t){(t=t||this.$vnode&&this.$vnode.ssrContext||this.parent&&this.parent.$vnode&&this.parent.$vnode.ssrContext)||"undefined"==typeof __VUE_SSR_CONTEXT__||(t=__VUE_SSR_CONTEXT__),r&&r.call(this,t),t&&t._registeredComponents&&t._registeredComponents.add(o)},u._ssrRegister=c):r&&(c=a?function(){r.call(this,(u.functional?this.parent:this).$root.$options.shadowRoot)}:r),c)if(u.functional){u._injectStyles=c;var d=u.render;u.render=function(t,e){return c.call(e),d(t,e)}}else{var l=u.beforeCreate;u.beforeCreate=l?[].concat(l,c):[c]}return{exports:t,options:u}}n.d(e,{Z:()=>i})}}]);
|
||||
1
public/js/14.js
vendored
1
public/js/14.js
vendored
File diff suppressed because one or more lines are too long
1
public/js/15.js
vendored
1
public/js/15.js
vendored
File diff suppressed because one or more lines are too long
1
public/js/1535.js
vendored
Normal file
1
public/js/1535.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
public/js/1587.js
vendored
Normal file
1
public/js/1587.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
public/js/16.js
vendored
1
public/js/16.js
vendored
File diff suppressed because one or more lines are too long
1
public/js/1603.js
vendored
Normal file
1
public/js/1603.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
public/js/17.js
vendored
1
public/js/17.js
vendored
File diff suppressed because one or more lines are too long
1
public/js/18.js
vendored
1
public/js/18.js
vendored
File diff suppressed because one or more lines are too long
1
public/js/19.js
vendored
1
public/js/19.js
vendored
File diff suppressed because one or more lines are too long
1
public/js/1966.js
vendored
Normal file
1
public/js/1966.js
vendored
Normal file
@@ -0,0 +1 @@
|
||||
(self.webpackChunk=self.webpackChunk||[]).push([[1966],{3852:(e,t,s)=>{"use strict";s.r(t),s.d(t,{default:()=>r});const i={data:function(){return{items:[{title:this.__("Overview"),to:this.route("admin.services.index"),active:this.route().current("admin.services.index")},{title:this.__("Synchronize servers"),to:this.route("admin.services.servers.index"),active:this.route().current("admin.services.servers.index")},{title:this.__("Synchronize providers"),to:this.route("admin.services.providers.index"),active:this.route().current("admin.services.providers.index")}]}}};const r=(0,s(1900).Z)(i,(function(){var e=this,t=e.$createElement,s=e._self._c||t;return s("ul",{staticClass:"-ml-4 space-y-1"},e._l(e.items,(function(t){return s("li",[s("inertia-link",{staticClass:"flex items-center h-10 px-4 font-medium text-medium-emphasis",class:{"rounded shadow text-primary bg-surface-3":t.active},attrs:{href:t.to}},[e._v(e._s(t.title)+" "+e._s(t.route))])],1)})),0)}),[],!1,null,null,null).exports},1900:(e,t,s)=>{"use strict";function i(e,t,s,i,r,n,o,a){var c,d="function"==typeof e?e.options:e;if(t&&(d.render=t,d.staticRenderFns=s,d._compiled=!0),i&&(d.functional=!0),n&&(d._scopeId="data-v-"+n),o?(c=function(e){(e=e||this.$vnode&&this.$vnode.ssrContext||this.parent&&this.parent.$vnode&&this.parent.$vnode.ssrContext)||"undefined"==typeof __VUE_SSR_CONTEXT__||(e=__VUE_SSR_CONTEXT__),r&&r.call(this,e),e&&e._registeredComponents&&e._registeredComponents.add(o)},d._ssrRegister=c):r&&(c=a?function(){r.call(this,(d.functional?this.parent:this).$root.$options.shadowRoot)}:r),c)if(d.functional){d._injectStyles=c;var u=d.render;d.render=function(e,t){return c.call(t),u(e,t)}}else{var l=d.beforeCreate;d.beforeCreate=l?[].concat(l,c):[c]}return{exports:e,options:d}}s.d(t,{Z:()=>i})}}]);
|
||||
1
public/js/1970.js
vendored
Normal file
1
public/js/1970.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
public/js/1971.js
vendored
Normal file
1
public/js/1971.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
public/js/1984.js
vendored
Normal file
1
public/js/1984.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
public/js/2.js
vendored
1
public/js/2.js
vendored
File diff suppressed because one or more lines are too long
1
public/js/20.js
vendored
1
public/js/20.js
vendored
File diff suppressed because one or more lines are too long
1
public/js/2023.js
vendored
Normal file
1
public/js/2023.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
public/js/2035.js
vendored
Normal file
1
public/js/2035.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
public/js/2076.js
vendored
Normal file
1
public/js/2076.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
public/js/21.js
vendored
1
public/js/21.js
vendored
File diff suppressed because one or more lines are too long
1
public/js/22.js
vendored
1
public/js/22.js
vendored
File diff suppressed because one or more lines are too long
1
public/js/23.js
vendored
1
public/js/23.js
vendored
File diff suppressed because one or more lines are too long
1
public/js/24.js
vendored
1
public/js/24.js
vendored
File diff suppressed because one or more lines are too long
1
public/js/2407.js
vendored
Normal file
1
public/js/2407.js
vendored
Normal file
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