Merge branch 'develop'

# Conflicts:
#	public/js/app.js
This commit is contained in:
Dennis
2021-08-12 11:03:51 +02:00
53 changed files with 28143 additions and 183 deletions

View File

@@ -3,6 +3,7 @@
namespace App\Http\Controllers\Admin;
use App\Models\Package;
use Illuminate\Http\Request;
use Illuminate\Support\Str;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Storage;
@@ -87,4 +88,42 @@ class SettingController extends Controller
return redirect()->route('admin.settings')->with('success', __('Settings have been updated'));
}
public function terms()
{
return inertia('Admin/Terms', [
'settings' => [
'logo' => setting('logo'),
'name' => setting('name'),
'terms_required' => setting('accept_terms_required'),
'terms' => setting('terms'),
'privacy' => setting('privacy')
]
]);
}
public function updateTerms(Request $request)
{
setting(['accept_terms_required' => $request->input('terms_required'),]);
setting(['terms' => $request->input('terms'),]);
setting(['privacy' => $request->input('privacy'),]);
return redirect()->route('admin.settings.terms')->with('success', __('Terms have been updated'));
}
public function termsTemplate(Request $request)
{
$template = file_get_contents(storage_path('templates/terms-of-service.md'));
$template = str_replace([
'{NAME}',
'{WEBSITE}',
'{DATE}'
], [
setting('name'),
config('app.url'),
date('Y-m-d')
], $template);
return ['content' => $template];
}
}

View File

@@ -11,15 +11,15 @@ class SynchronizeProviderController extends Controller
{
public function index()
{
if (config('app.demo')) {
if ($this->isDemo()) {
return redirect('/')->with('info', __('This feature is not available in demo mode.'));
}
$ploi = new Ploi(config('services.ploi.token'));
$availableProviders = $this->getPloi()->user()->serverProviders()->getData();
$availableProviders = $ploi->user()->serverProviders()->getData();
$currentProviders = Provider::whereNotIn('id', array_keys((array)$availableProviders))->get();
$currentProviders = Provider::query()
->whereNotIn('id', array_keys((array)$availableProviders))
->get();
return inertia('Admin/Services/Providers', [
'availableProviders' => $availableProviders,
@@ -29,7 +29,7 @@ class SynchronizeProviderController extends Controller
public function synchronize(Request $request, $providerId)
{
$ploiProvider = (new Ploi)->user()->serverProviders($providerId)->getData();
$ploiProvider = $this->getPloi()->user()->serverProviders($providerId)->getData();
$provider = Provider::updateOrCreate([
'ploi_id' => $ploiProvider->id,

View File

@@ -6,10 +6,11 @@ use Illuminate\Http\Request;
use App\Jobs\Core\UpdateSystem;
use App\Services\VersionChecker;
use App\Http\Controllers\Controller;
use Laravel\Horizon\Contracts\MasterSupervisorRepository;
class SystemController extends Controller
{
public function index()
public function index(MasterSupervisorRepository $masterSupervisorRepository)
{
if (config('app.demo')) {
return redirect('/')->with('info', __('This feature is not available in demo mode.'));
@@ -17,12 +18,18 @@ class SystemController extends Controller
$version = (new VersionChecker)->getVersions();
$horizonRunning = true;
if (!$masterSupervisorRepository->all()) {
$horizonRunning = false;
}
return inertia('Admin/System', [
'version' => [
'out_of_date' => $version->isOutOfDate(),
'current' => $version->currentVersion,
'remote' => $version->remoteVersion
]
],
'horizonRunning' => $horizonRunning
]);
}

View File

@@ -28,7 +28,7 @@ class RegisterController extends Controller
protected function validator(array $data)
{
return Validator::make($data, [
$rules = [
'name' => ['required', 'string', 'max:255'],
'email' => ['required', 'string', 'email', 'max:255', 'unique:users'],
'password' => [
@@ -40,7 +40,15 @@ class RegisterController extends Controller
->numbers()
->uncompromised()
],
]);
];
if (setting('accept_terms_required')) {
$rules['terms'] = [
'accepted'
];
}
return Validator::make($data, $rules);
}
protected function create(array $data)

View File

@@ -2,22 +2,17 @@
namespace App\Http\Controllers;
use App\Services\Ploi\Ploi;
use App\Traits\HasPloi;
use Illuminate\Routing\Controller as BaseController;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
class Controller extends BaseController
{
use ValidatesRequests, AuthorizesRequests;
use ValidatesRequests, AuthorizesRequests, HasPloi;
protected function isDemo()
{
return config('app.demo');
}
protected function getPloi()
{
return new Ploi(config('services.ploi.token'));
}
}

View File

@@ -2,10 +2,29 @@
namespace App\Http\Controllers;
use Illuminate\Support\Str;
class PageController extends Controller
{
public function installationIncomplete()
{
return inertia('Core/InstallationIncomplete');
}
public function show($slug)
{
if ($slug === 'terms-of-service' && setting('terms')) {
return inertia('Pages/Terms', [
'content' => Str::markdown(setting('terms'))
]);
}
if ($slug === 'privacy-policy' && setting('privacy')) {
return inertia('Pages/Privacy', [
'content' => Str::markdown(setting('privacy'))
]);
}
abort(404);
}
}

View File

@@ -87,7 +87,10 @@ class HandleInertiaRequests extends Middleware
'documentation' => setting('documentation', false),
'logo' => setting('logo'),
'allow_registration' => setting('allow_registration'),
'billing' => config('cashier.key') && config('cashier.secret')
'billing' => config('cashier.key') && config('cashier.secret'),
'has_terms' => (bool)setting('terms'),
'has_privacy' => (bool)setting('privacy'),
'accept_terms_required' => (bool)setting('accept_terms_required')
];
},
'flash' => function () {

View File

@@ -4,6 +4,7 @@ namespace App\Jobs\Apps;
use App\Models\Site;
use App\Services\Ploi\Ploi;
use App\Traits\HasPloi;
use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
@@ -12,9 +13,9 @@ use Illuminate\Foundation\Bus\Dispatchable;
class InstallApp implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, HasPloi;
public $site;
public Site $site;
public $type;
public $options;
@@ -25,7 +26,7 @@ class InstallApp implements ShouldQueue
* @param string $type
* @param array $options
*/
public function __construct(Site $site, $type = Site::PROJECT_WORDPRESS, array $options = [])
public function __construct(Site $site, string $type = Site::PROJECT_WORDPRESS, array $options = [])
{
$this->site = $site;
$this->type = $type;
@@ -39,8 +40,10 @@ class InstallApp implements ShouldQueue
*/
public function handle()
{
$ploi = new Ploi(config('services.ploi.token'));
$ploi->server($this->site->server->ploi_id)->sites($this->site->ploi_id)->app()->install($this->type, $this->options);
$this->getPloi()
->server($this->site->server->ploi_id)
->sites($this->site->ploi_id)
->app()
->install($this->type, $this->options);
}
}

View File

@@ -4,6 +4,7 @@ namespace App\Jobs\Apps;
use App\Models\Site;
use App\Services\Ploi\Ploi;
use App\Traits\HasPloi;
use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
@@ -12,9 +13,9 @@ use Illuminate\Foundation\Bus\Dispatchable;
class UninstallApp implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, HasPloi;
public $site;
public Site $site;
/**
* Create a new job instance.
@@ -37,9 +38,11 @@ class UninstallApp implements ShouldQueue
return;
}
$ploi = new Ploi(config('services.ploi.token'));
$ploi->server($this->site->server->ploi_id)->sites($this->site->ploi_id)->app()->uninstall($this->site->project);
$this->getPloi()
->server($this->site->server->ploi_id)
->sites($this->site->ploi_id)
->app()
->uninstall($this->site->project);
$this->site->project = null;
$this->site->save();

View File

@@ -4,6 +4,7 @@ namespace App\Jobs\Certificates;
use App\Models\Certificate;
use App\Services\Ploi\Ploi;
use App\Traits\HasPloi;
use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
@@ -12,7 +13,7 @@ use Illuminate\Foundation\Bus\Dispatchable;
class CreateCertificate implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, HasPloi;
public Certificate $certificate;
@@ -23,10 +24,9 @@ class CreateCertificate implements ShouldQueue
public function handle()
{
$ploi = new Ploi(config('services.ploi.token'));
if ($this->certificate->type === 'letsencrypt') {
$ploiCertificate = $ploi->server($this->certificate->server->ploi_id)
$ploiCertificate = $this->getPloi()
->server($this->certificate->server->ploi_id)
->sites($this->certificate->site->ploi_id)
->certificates()
->create(
@@ -38,7 +38,8 @@ class CreateCertificate implements ShouldQueue
}
if ($this->certificate->type === 'custom') {
$ploiCertificate = $ploi->server($this->certificate->server->ploi_id)
$ploiCertificate = $this->getPloi()
->server($this->certificate->server->ploi_id)
->sites($this->certificate->site->ploi_id)
->certificates()
->create(

View File

@@ -3,6 +3,7 @@
namespace App\Jobs\Certificates;
use App\Services\Ploi\Ploi;
use App\Traits\HasPloi;
use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
@@ -11,7 +12,7 @@ use Illuminate\Foundation\Bus\Dispatchable;
class DeleteCertificate implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, HasPloi;
public $serverPloiId;
public $sitePloiId;
@@ -42,9 +43,8 @@ class DeleteCertificate implements ShouldQueue
return;
}
$ploi = new Ploi(config('services.ploi.token'));
$ploi->server($this->serverPloiId)
$this->getPloi()
->server($this->serverPloiId)
->sites($this->sitePloiId)
->certificates()
->delete($this->certificatePloiId);

View File

@@ -4,6 +4,7 @@ namespace App\Jobs\Certificates;
use App\Models\Certificate;
use App\Services\Ploi\Ploi;
use App\Traits\HasPloi;
use Illuminate\Bus\Queueable;
use App\Traits\JobHasThresholds;
use Illuminate\Queue\SerializesModels;
@@ -13,7 +14,7 @@ use Illuminate\Foundation\Bus\Dispatchable;
class FetchCertificateStatus implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, JobHasThresholds;
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, JobHasThresholds, HasPloi;
public $certificate;
@@ -23,7 +24,7 @@ class FetchCertificateStatus implements ShouldQueue
* @param Certificate $certificate
* @param int $threshold
*/
public function __construct(Certificate $certificate, $threshold = 0)
public function __construct(Certificate $certificate, int $threshold = 0)
{
$this->certificate = $certificate;
$this->setThreshold($threshold);
@@ -43,9 +44,8 @@ class FetchCertificateStatus implements ShouldQueue
return;
}
$ploi = new Ploi(config('services.ploi.token'));
$ploiCronjob = $ploi->server($this->certificate->server->ploi_id)
$ploiCronjob = $this->getPloi()
->server($this->certificate->server->ploi_id)
->sites($this->certificate->site->ploi_id)
->certificates()
->get($this->certificate->ploi_id)

View File

@@ -4,6 +4,7 @@ namespace App\Jobs\Cronjobs;
use App\Models\Cronjob;
use App\Services\Ploi\Ploi;
use App\Traits\HasPloi;
use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
@@ -12,7 +13,7 @@ use Illuminate\Foundation\Bus\Dispatchable;
class CreateCronjob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, HasPloi;
public $cronjob;
@@ -35,13 +36,14 @@ class CreateCronjob implements ShouldQueue
{
$owner = $this->cronjob->site->users()->first();
$ploi = new Ploi(config('services.ploi.token'));
$ploiCronjob = $ploi->server($this->cronjob->server->ploi_id)->cronjobs()->create(
$this->cronjob->command,
$this->cronjob->frequency,
$owner->user_name
);
$ploiCronjob = $this->getPloi()
->server($this->cronjob->server->ploi_id)
->cronjobs()
->create(
$this->cronjob->command,
$this->cronjob->frequency,
$owner->user_name
);
$this->cronjob->ploi_id = $ploiCronjob->id;
$this->cronjob->save();

View File

@@ -3,6 +3,7 @@
namespace App\Jobs\Cronjobs;
use App\Services\Ploi\Ploi;
use App\Traits\HasPloi;
use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
@@ -11,7 +12,7 @@ use Illuminate\Foundation\Bus\Dispatchable;
class DeleteCronjob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, HasPloi;
public $serverPloiId;
public $cronjobPloiId;
@@ -39,8 +40,9 @@ class DeleteCronjob implements ShouldQueue
return;
}
$ploi = new Ploi(config('services.ploi.token'));
$ploi->server($this->serverPloiId)->cronjobs()->delete($this->cronjobPloiId);
$this->getPloi()
->server($this->serverPloiId)
->cronjobs()
->delete($this->cronjobPloiId);
}
}

View File

@@ -4,6 +4,7 @@ namespace App\Jobs\Cronjobs;
use App\Models\Cronjob;
use App\Services\Ploi\Ploi;
use App\Traits\HasPloi;
use Illuminate\Bus\Queueable;
use App\Traits\JobHasThresholds;
use Illuminate\Queue\SerializesModels;
@@ -13,7 +14,7 @@ use Illuminate\Foundation\Bus\Dispatchable;
class FetchCronjobStatus implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, JobHasThresholds;
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, JobHasThresholds, HasPloi;
public $cronjob;
@@ -23,7 +24,7 @@ class FetchCronjobStatus implements ShouldQueue
* @param Cronjob $cronjob
* @param int $threshold
*/
public function __construct(Cronjob $cronjob, $threshold = 0)
public function __construct(Cronjob $cronjob, int $threshold = 0)
{
$this->cronjob = $cronjob;
$this->setThreshold($threshold);
@@ -43,9 +44,11 @@ class FetchCronjobStatus implements ShouldQueue
return;
}
$ploi = new Ploi(config('services.ploi.token'));
$ploiCronjob = $ploi->server($this->cronjob->server->ploi_id)->cronjobs()->get($this->cronjob->ploi_id)->getData();
$ploiCronjob = $this->getPloi()
->server($this->cronjob->server->ploi_id)
->cronjobs()
->get($this->cronjob->ploi_id)
->getData();
if ($ploiCronjob->status !== Cronjob::STATUS_ACTIVE) {
$this->incrementThreshold();

View File

@@ -2,8 +2,8 @@
namespace App\Jobs\Databases;
use App\Traits\HasPloi;
use App\Models\Database;
use App\Services\Ploi\Ploi;
use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
@@ -12,7 +12,7 @@ use Illuminate\Foundation\Bus\Dispatchable;
class CreateDatabase implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, HasPloi;
public $database;
public $password;
@@ -38,9 +38,10 @@ class CreateDatabase implements ShouldQueue
{
$databaseUser = $this->database->users()->first();
$ploi = new Ploi(config('services.ploi.token'));
$ploiDatabase = $ploi->server($this->database->server->ploi_id)->databases()->create($this->database->name, $databaseUser->name, $this->password);
$ploiDatabase = $this->getPloi()
->server($this->database->server->ploi_id)
->databases()
->create($this->database->name, $databaseUser->name, $this->password);
$this->database->ploi_id = $ploiDatabase->id;
$this->database->save();

View File

@@ -3,6 +3,7 @@
namespace App\Jobs\Databases;
use App\Services\Ploi\Ploi;
use App\Traits\HasPloi;
use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
@@ -11,7 +12,7 @@ use Illuminate\Foundation\Bus\Dispatchable;
class DeleteDatabase implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, HasPloi;
public $serverPloiId;
public $databasePloiId;
@@ -40,8 +41,6 @@ class DeleteDatabase implements ShouldQueue
return;
}
$ploi = new Ploi(config('services.ploi.token'));
$ploi->server($this->serverPloiId)->databases()->delete($this->databasePloiId);
$this->getPloi()->server($this->serverPloiId)->databases()->delete($this->databasePloiId);
}
}

View File

@@ -2,8 +2,8 @@
namespace App\Jobs\Databases;
use App\Traits\HasPloi;
use App\Models\Database;
use App\Services\Ploi\Ploi;
use Illuminate\Bus\Queueable;
use App\Traits\JobHasThresholds;
use Illuminate\Queue\SerializesModels;
@@ -13,7 +13,7 @@ use Illuminate\Foundation\Bus\Dispatchable;
class FetchDatabaseStatus implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, JobHasThresholds;
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, JobHasThresholds, HasPloi;
public $database;
@@ -23,7 +23,7 @@ class FetchDatabaseStatus implements ShouldQueue
* @param Database $database
* @param int $threshold
*/
public function __construct(Database $database, $threshold = 0)
public function __construct(Database $database, int $threshold = 0)
{
$this->database = $database;
$this->setThreshold($threshold);
@@ -43,9 +43,11 @@ class FetchDatabaseStatus implements ShouldQueue
return;
}
$ploi = new Ploi(config('services.ploi.token'));
$ploiDatabase = $ploi->server($this->database->server->ploi_id)->databases()->get($this->database->ploi_id)->getData();
$ploiDatabase = $this->getPloi()
->server($this->database->server->ploi_id)
->databases()
->get($this->database->ploi_id)
->getData();
if ($ploiDatabase->status !== Database::STATUS_ACTIVE) {
$this->incrementThreshold();

View File

@@ -2,8 +2,8 @@
namespace App\Jobs\Redirects;
use App\Traits\HasPloi;
use App\Models\Redirect;
use App\Services\Ploi\Ploi;
use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
@@ -12,7 +12,7 @@ use Illuminate\Foundation\Bus\Dispatchable;
class CreateRedirect implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, HasPloi;
public $redirect;
@@ -33,9 +33,7 @@ class CreateRedirect implements ShouldQueue
*/
public function handle()
{
$ploi = new Ploi(config('services.ploi.token'));
$ploiRedirect = $ploi
$ploiRedirect = $this->getPloi()
->server($this->redirect->server->ploi_id)
->sites($this->redirect->site->ploi_id)
->redirects()

View File

@@ -2,7 +2,7 @@
namespace App\Jobs\Redirects;
use App\Services\Ploi\Ploi;
use App\Traits\HasPloi;
use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
@@ -11,7 +11,7 @@ use Illuminate\Foundation\Bus\Dispatchable;
class DeleteRedirect implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, HasPloi;
public $serverPloiId;
public $sitePloiId;
@@ -42,9 +42,8 @@ class DeleteRedirect implements ShouldQueue
return;
}
$ploi = new Ploi(config('services.ploi.token'));
$ploi->server($this->serverPloiId)
$this->getPloi()
->server($this->serverPloiId)
->sites($this->sitePloiId)
->redirects()
->delete($this->redirectPloiId);

View File

@@ -2,8 +2,8 @@
namespace App\Jobs\Redirects;
use App\Traits\HasPloi;
use App\Models\Redirect;
use App\Services\Ploi\Ploi;
use Illuminate\Bus\Queueable;
use App\Traits\JobHasThresholds;
use Illuminate\Queue\SerializesModels;
@@ -13,7 +13,7 @@ use Illuminate\Foundation\Bus\Dispatchable;
class FetchRedirectStatus implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, JobHasThresholds;
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, JobHasThresholds, HasPloi;
public $redirect;
@@ -23,7 +23,7 @@ class FetchRedirectStatus implements ShouldQueue
* @param Redirect $redirect
* @param int $threshold
*/
public function __construct(Redirect $redirect, $threshold = 0)
public function __construct(Redirect $redirect, int $threshold = 0)
{
$this->redirect = $redirect;
$this->setThreshold($threshold);
@@ -43,9 +43,8 @@ class FetchRedirectStatus implements ShouldQueue
return;
}
$ploi = new Ploi(config('services.ploi.token'));
$ploiRedirect = $ploi->server($this->redirect->server->ploi_id)
$ploiRedirect = $this->getPloi()
->server($this->redirect->server->ploi_id)
->sites($this->redirect->site->ploi_id)
->redirects()
->get($this->redirect->ploi_id)->getData();

View File

@@ -3,7 +3,7 @@
namespace App\Jobs\Servers;
use App\Models\Server;
use App\Services\Ploi\Ploi;
use App\Traits\HasPloi;
use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
@@ -12,7 +12,7 @@ use Illuminate\Foundation\Bus\Dispatchable;
class CreateServer implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, HasPloi;
public $server;
public $tries = 1;
@@ -34,9 +34,7 @@ class CreateServer implements ShouldQueue
*/
public function handle()
{
$ploi = new Ploi(config('services.ploi.token'));
$ploiServer = $ploi->server()->create(
$ploiServer = $this->getPloi()->server()->create(
$this->server->name,
$this->server->provider->ploi_id,
$this->server->providerRegion->region_id,

View File

@@ -2,7 +2,7 @@
namespace App\Jobs\Servers;
use App\Services\Ploi\Ploi;
use App\Traits\HasPloi;
use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
@@ -11,7 +11,7 @@ use Illuminate\Foundation\Bus\Dispatchable;
class DeleteServer implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, HasPloi;
public $serverPloiId;
@@ -32,8 +32,6 @@ class DeleteServer implements ShouldQueue
*/
public function handle()
{
$ploi = new Ploi(config('services.ploi.token'));
$ploi->server($this->serverPloiId)->delete();
$this->getPloi()->server($this->serverPloiId)->delete();
}
}

View File

@@ -3,7 +3,7 @@
namespace App\Jobs\Servers;
use App\Models\Server;
use App\Services\Ploi\Ploi;
use App\Traits\HasPloi;
use Illuminate\Bus\Queueable;
use App\Traits\JobHasThresholds;
use Illuminate\Queue\SerializesModels;
@@ -13,7 +13,7 @@ use Illuminate\Foundation\Bus\Dispatchable;
class FetchServerStatus implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, JobHasThresholds;
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, JobHasThresholds, HasPloi;
public $server;
@@ -43,9 +43,7 @@ class FetchServerStatus implements ShouldQueue
return;
}
$ploi = new Ploi;
$ploiServer = $ploi->server($this->server->ploi_id)->get()->getData();
$ploiServer = $this->getPloi()->server($this->server->ploi_id)->get()->getData();
if ($ploiServer->status !== Server::STATUS_ACTIVE) {
$this->incrementThreshold();

View File

@@ -1,35 +0,0 @@
<?php
namespace App\Jobs\Servers;
use App\Models\Server;
use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
class SynchronizeServers implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
$ploi = new \App\Services\Ploi\Ploi(config('services.ploi.token'));
$servers = $ploi->server()->get()->getData();
foreach ($servers as $server) {
Server::firstOrCreate([
'ip' => $server->ip_address,
], [
'name' => $server->name
]);
}
}
}

View File

@@ -3,7 +3,7 @@
namespace App\Jobs\Sites;
use App\Models\Site;
use App\Services\Ploi\Ploi;
use App\Traits\HasPloi;
use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
@@ -12,7 +12,7 @@ use Illuminate\Foundation\Bus\Dispatchable;
class ChangePhpVersion implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, HasPloi;
public $site;
public $version;
@@ -36,9 +36,7 @@ class ChangePhpVersion implements ShouldQueue
*/
public function handle()
{
$ploi = new Ploi(config('services.ploi.token'));
$ploi->server($this->site->server->ploi_id)->sites($this->site->ploi_id)->phpVersion($this->version);
$this->getPloi()->server($this->site->server->ploi_id)->sites($this->site->ploi_id)->phpVersion($this->version);
$this->site->php_version = $this->version;
$this->site->save();

View File

@@ -3,7 +3,7 @@
namespace App\Jobs\Sites;
use App\Models\Site;
use App\Services\Ploi\Ploi;
use App\Traits\HasPloi;
use Illuminate\Support\Arr;
use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
@@ -13,7 +13,7 @@ use Illuminate\Foundation\Bus\Dispatchable;
class CreateSite implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, HasPloi;
public $site;
@@ -34,11 +34,9 @@ class CreateSite implements ShouldQueue
*/
public function handle()
{
$ploi = new Ploi(config('services.ploi.token'));
$systemUser = $this->site->getSystemUser();
$ploiSite = $ploi->server($this->site->server->ploi_id)->sites()->create(
$ploiSite = $this->getPloi()->server($this->site->server->ploi_id)->sites()->create(
$this->site->domain,
'/public',
'/',

View File

@@ -2,7 +2,7 @@
namespace App\Jobs\Sites;
use App\Services\Ploi\Ploi;
use App\Traits\HasPloi;
use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
@@ -11,7 +11,7 @@ use Illuminate\Foundation\Bus\Dispatchable;
class DeleteSite implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, HasPloi;
public $serverPloiId;
public $sitePloiId;
@@ -34,8 +34,6 @@ class DeleteSite implements ShouldQueue
*/
public function handle()
{
$ploi = new Ploi(config('services.ploi.token'));
$ploi->server($this->serverPloiId)->sites()->delete($this->sitePloiId);
$this->getPloi()->server($this->serverPloiId)->sites()->delete($this->sitePloiId);
}
}

View File

@@ -3,7 +3,7 @@
namespace App\Jobs\Sites;
use App\Models\Site;
use App\Services\Ploi\Ploi;
use App\Traits\HasPloi;
use Illuminate\Bus\Queueable;
use App\Traits\JobHasThresholds;
use Illuminate\Queue\SerializesModels;
@@ -13,7 +13,7 @@ use Illuminate\Foundation\Bus\Dispatchable;
class FetchSiteStatus implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, JobHasThresholds;
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, JobHasThresholds, HasPloi;
public $site;
@@ -43,9 +43,7 @@ class FetchSiteStatus implements ShouldQueue
return;
}
$ploi = new Ploi;
$ploiSite = $ploi->server($this->site->server->ploi_id)->sites()->get($this->site->ploi_id)->getData();
$ploiSite = $this->getPloi()->server($this->site->server->ploi_id)->sites()->get($this->site->ploi_id)->getData();
if ($ploiSite->status !== Site::STATUS_ACTIVE) {
$this->incrementThreshold();

13
app/Traits/HasPloi.php Normal file
View File

@@ -0,0 +1,13 @@
<?php
namespace App\Traits;
use App\Services\Ploi\Ploi;
trait HasPloi
{
protected function getPloi()
{
return new Ploi(config('services.ploi.token'));
}
}

93
package-lock.json generated
View File

@@ -4,6 +4,9 @@
"requires": true,
"packages": {
"": {
"dependencies": {
"vue-simplemde": "^2.0.0"
},
"devDependencies": {
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
"@inertiajs/inertia": "^0.10.0",
@@ -3090,6 +3093,19 @@
"node": ">=6"
}
},
"node_modules/codemirror": {
"version": "5.62.2",
"resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.62.2.tgz",
"integrity": "sha512-tVFMUa4J3Q8JUd1KL9yQzQB0/BJt7ZYZujZmTPgo/54Lpuq3ez4C8x/ATUY/wv7b7X3AUq8o3Xd+2C5ZrCGWHw=="
},
"node_modules/codemirror-spell-checker": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/codemirror-spell-checker/-/codemirror-spell-checker-1.1.2.tgz",
"integrity": "sha1-HGYPkIlIPMtRE7m6nKGcP0mTNx4=",
"dependencies": {
"typo-js": "*"
}
},
"node_modules/collect.js": {
"version": "4.28.6",
"resolved": "https://registry.npmjs.org/collect.js/-/collect.js-4.28.6.tgz",
@@ -6252,6 +6268,17 @@
"node": ">=6"
}
},
"node_modules/marked": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/marked/-/marked-2.1.3.tgz",
"integrity": "sha512-/Q+7MGzaETqifOMWYEA7HVMaZb4XbcRfaOzcSsHZEith83KGlvaSG33u0SKu89Mj5h+T8V2hM+8O45Qc5XTgwA==",
"bin": {
"marked": "bin/marked"
},
"engines": {
"node": ">= 10"
}
},
"node_modules/md5": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz",
@@ -8934,6 +8961,16 @@
"integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==",
"dev": true
},
"node_modules/simplemde": {
"version": "1.11.2",
"resolved": "https://registry.npmjs.org/simplemde/-/simplemde-1.11.2.tgz",
"integrity": "sha1-ojo12XjSxA7wfewAjJLwcNjggOM=",
"dependencies": {
"codemirror": "*",
"codemirror-spell-checker": "*",
"marked": "*"
}
},
"node_modules/slash": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
@@ -9658,6 +9695,11 @@
"node": ">= 0.6"
}
},
"node_modules/typo-js": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/typo-js/-/typo-js-1.2.0.tgz",
"integrity": "sha512-dELuLBVa2jvWdU/CHTKi2L/POYaRupv942k+vRsFXsM17acXesQGAiGCio82RW7fvcr7bkuD/Zj8XpUh6aPC2A=="
},
"node_modules/unicode-canonical-property-names-ecmascript": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz",
@@ -9924,6 +9966,15 @@
"deepmerge": "^4.2.2"
}
},
"node_modules/vue-simplemde": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/vue-simplemde/-/vue-simplemde-2.0.0.tgz",
"integrity": "sha512-Wn/U/CLTy8y/bqAFg6rba8/uZHGcolx3J1TtvcaQ3mqmDlj6yXOiCy4b8uJxbG96t2A6UBdMyryzODLYAzDYmA==",
"dependencies": {
"marked": "*",
"simplemde": "*"
}
},
"node_modules/vue-style-loader": {
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/vue-style-loader/-/vue-style-loader-4.1.3.tgz",
@@ -12876,6 +12927,19 @@
"shallow-clone": "^3.0.0"
}
},
"codemirror": {
"version": "5.62.2",
"resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.62.2.tgz",
"integrity": "sha512-tVFMUa4J3Q8JUd1KL9yQzQB0/BJt7ZYZujZmTPgo/54Lpuq3ez4C8x/ATUY/wv7b7X3AUq8o3Xd+2C5ZrCGWHw=="
},
"codemirror-spell-checker": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/codemirror-spell-checker/-/codemirror-spell-checker-1.1.2.tgz",
"integrity": "sha1-HGYPkIlIPMtRE7m6nKGcP0mTNx4=",
"requires": {
"typo-js": "*"
}
},
"collect.js": {
"version": "4.28.6",
"resolved": "https://registry.npmjs.org/collect.js/-/collect.js-4.28.6.tgz",
@@ -15364,6 +15428,11 @@
"p-defer": "^1.0.0"
}
},
"marked": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/marked/-/marked-2.1.3.tgz",
"integrity": "sha512-/Q+7MGzaETqifOMWYEA7HVMaZb4XbcRfaOzcSsHZEith83KGlvaSG33u0SKu89Mj5h+T8V2hM+8O45Qc5XTgwA=="
},
"md5": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz",
@@ -17367,6 +17436,16 @@
}
}
},
"simplemde": {
"version": "1.11.2",
"resolved": "https://registry.npmjs.org/simplemde/-/simplemde-1.11.2.tgz",
"integrity": "sha1-ojo12XjSxA7wfewAjJLwcNjggOM=",
"requires": {
"codemirror": "*",
"codemirror-spell-checker": "*",
"marked": "*"
}
},
"slash": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
@@ -17925,6 +18004,11 @@
"mime-types": "~2.1.24"
}
},
"typo-js": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/typo-js/-/typo-js-1.2.0.tgz",
"integrity": "sha512-dELuLBVa2jvWdU/CHTKi2L/POYaRupv942k+vRsFXsM17acXesQGAiGCio82RW7fvcr7bkuD/Zj8XpUh6aPC2A=="
},
"unicode-canonical-property-names-ecmascript": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz",
@@ -18142,6 +18226,15 @@
"deepmerge": "^4.2.2"
}
},
"vue-simplemde": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/vue-simplemde/-/vue-simplemde-2.0.0.tgz",
"integrity": "sha512-Wn/U/CLTy8y/bqAFg6rba8/uZHGcolx3J1TtvcaQ3mqmDlj6yXOiCy4b8uJxbG96t2A6UBdMyryzODLYAzDYmA==",
"requires": {
"marked": "*",
"simplemde": "*"
}
},
"vue-style-loader": {
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/vue-style-loader/-/vue-style-loader-4.1.3.tgz",

View File

@@ -34,5 +34,8 @@
"vue-meta": "^2.4.0",
"vue-template-compiler": "^2.6.11",
"vuex": "^3.6.2"
},
"dependencies": {
"vue-simplemde": "^2.0.0"
}
}

27335
public/js/app.js vendored

File diff suppressed because one or more lines are too long

View File

@@ -121,10 +121,16 @@
</div>
</div>
<div class="space-y-2">
<div class="space-y-2" v-if="form.server_permissions['create']">
<h3 class="text-base leading-6 font-medium border-b border-dotted border-medium-emphasis pb-1">
{{ __('Available server providers') }}</h3>
<div v-if="!Object.keys(providers).length" class="bg-primary text-on-primary px-4 py-3 rounded relative space-y-2" role="alert">
<p class="block">
There are no server providers to select from. You can synchronize new server providers from the Services tab.
</p>
</div>
<div v-for="(name, id) in providers">
<input :id="`provider-${id}`" :value="id" v-model="form.providers"
class="form-checkbox" type="checkbox">

View File

@@ -111,10 +111,16 @@
</div>
</div>
<div class="space-y-2">
<div class="space-y-2" v-if="form.server_permissions['create']">
<h3 class="text-base leading-6 font-medium border-b border-dotted border-medium-emphasis pb-1">
{{ __('Available server providers') }}</h3>
<div v-if="!Object.keys(providers).length" class="bg-primary text-on-primary px-4 py-3 rounded relative space-y-2" role="alert">
<p class="block">
There are no server providers to select from. You can synchronize new server providers from the Services tab.
</p>
</div>
<div v-for="(name, id) in providers">
<input :id="`provider-${id}`" :value="id" v-model="form.providers"
class="form-checkbox" type="checkbox">

View File

@@ -88,18 +88,18 @@
</p>
</div>
<FormInput v-if="form.enable_api" allow-random-string :label="__('API token')" :errors="$page.props.errors.api_token"
v-model="form.api_token"/>
<div>
<input id="isolate_per_site_per_user" class="form-checkbox" type="checkbox"
v-model="form.isolate_per_site_per_user">
<label for="isolate_per_site_per_user" class="ml-2 text-sm">{{ __('Enable site isolation per site & user') }}</label>
<p class="text-small mt-1 text-medium-emphasis">
{{ __('This will make sure each site created by 1 user is always isolated from another.') }}
{{ __('This will make sure each site created by one user is always isolated from another.') }}
</p>
</div>
<FormInput v-if="form.enable_api" allow-random-string :label="__('API token')" :errors="$page.props.errors.api_token"
v-model="form.api_token"/>
<FormActions>
<Button>{{ __('Save changes') }}</Button>
</FormActions>

View File

@@ -26,6 +26,7 @@
<template #content>
<p>{{ __('Current version') }}: {{ version.current }}</p>
<p>{{ __('Remote version') }}: {{ version.remote }}</p>
<p>Horizon worker status: <span v-if="horizonRunning" class="text-success">Active</span><span v-else class="text-danger">Inactive</span></p>
<div v-if="version.out_of_date && !updating" class="bg-primary text-on-primary px-4 py-3 rounded relative space-y-2" role="alert">
<strong class="font-bold">Update available</strong>
@@ -111,7 +112,8 @@
},
props: {
version: Object
version: Object,
horizonRunning: Boolean
},
data() {

View File

@@ -25,21 +25,26 @@
to: this.route('admin.settings'),
active: this.route().current('admin.settings')
},
{
title: this.__('Alert messages'),
to: this.route('admin.alerts.index'),
active: this.route().current('admin.alerts.*')
},
{
title: this.__('System'),
to: this.route('admin.system'),
active: this.route().current('admin.system')
},
{
title: this.__('Terms'),
to: this.route('admin.settings.terms'),
active: this.route().current('admin.settings.terms')
},
{
title: this.__('Alert messages'),
to: this.route('admin.alerts.index'),
active: this.route().current('admin.alerts.*')
},
{
title: this.__('Application logs'),
to: this.route('admin.application-logs'),
active: this.route().current('admin.application-logs')
}
},
],
}
},

View File

@@ -0,0 +1,179 @@
<template>
<Page>
<TopBar/>
<Content>
<Container>
<PageHeader>
<template #start>
<PageHeaderTitle>{{ __('Terms') }}</PageHeaderTitle>
</template>
</PageHeader>
<PageBody>
<SettingsLayout>
<template #nav>
<Tabs/>
</template>
<template #segments>
<SettingsSegment>
<template #title>{{ __('Overview') }}</template>
<template #subtitle>
{{
__('Enter content for your terms of service and privacy policy here. You may use markdown.')
}}
</template>
<template #form>
<form class="space-y-4" @submit.prevent="submit">
<div>
<input id="terms_required" class="form-checkbox" type="checkbox"
v-model="form.terms_required">
<label for="terms_required" class="ml-2 text-sm">{{
__('Require users to accept terms of service on registration')
}}</label>
<p class="text-small mt-1 text-medium-emphasis">
{{
__('This will require newly registered users to accept the terms of service.')
}}
</p>
</div>
<Button type="button" size="sm" @click="getTemplate('terms')">
Load Terms of Service template
</Button>
<FormCustom label="Content Terms Of Service">
<vue-simplemde v-model="form.terms" ref="terms_of_service"/>
</FormCustom>
<FormCustom label="Content Privacy Policy">
<vue-simplemde v-model="form.privacy"/>
</FormCustom>
<FormActions>
<Button>{{ __('Save changes') }}</Button>
</FormActions>
</form>
</template>
</SettingsSegment>
</template>
</SettingsLayout>
</PageBody>
</Container>
</Content>
</Page>
</template>
<script>
import TopBar from './components/TopBar'
import Container from '@/components/Container'
import Content from '@/components/Content'
import Page from '@/components/Page'
import PageHeader from '@/components/PageHeader'
import PageHeaderTitle from '@/components/PageHeaderTitle'
import PageBody from '@/components/PageBody'
import Button from '@/components/Button'
import List from '@/components/List'
import ListItem from '@/components/ListItem'
import StatusBubble from '@/components/StatusBubble'
import NotificationBadge from '@/components/NotificationBadge'
import MainLayout from '@/Layouts/MainLayout'
import SettingsLayout from '@/components/layouts/SettingsLayout'
import SettingsSegment from '@/components/SettingsSegment'
import FormInput from '@/components/forms/FormInput'
import FormFileInput from '@/components/forms/FormFileInput'
import FormSelect from '@/components/forms/FormSelect'
import Form from '@/components/Form'
import FormActions from '@/components/FormActions'
import FormCustom from '@/components/forms/FormCustom'
import {useNotification} from '@/hooks/notification'
import Tabs from './Tabs'
import VueSimplemde from 'vue-simplemde'
export default {
metaInfo() {
return {
title: `${this.__('Terms')}`,
}
},
layout: MainLayout,
components: {
TopBar,
Container,
Content,
Page,
PageHeader,
PageHeaderTitle,
PageBody,
Button,
List,
ListItem,
StatusBubble,
NotificationBadge,
FormInput,
FormFileInput,
FormSelect,
SettingsLayout,
SettingsSegment,
Form,
FormActions,
Tabs,
VueSimplemde,
FormCustom
},
data() {
return {
sending: false,
form: this.$inertia.form({
terms: this.settings.terms,
terms_required: this.settings.terms_required,
privacy: this.settings.privacy,
})
}
},
props: {
settings: Object,
},
methods: {
useNotification,
submit() {
this.form.patch(this.route('admin.settings.terms.update'));
},
getTemplate(type) {
axios.get(this.route('admin.settings.terms.template'))
.then(response => {
this.$refs.terms_of_service.simplemde.value(response.data.content);
this.form.terms = response.data.content;
useNotification({
variant: 'success',
title: this.__(`Terms`),
message: 'Template has been loaded in, do not forget to save.',
})
})
}
}
}
</script>
<style>
@import '~simplemde/dist/simplemde.min.css';
.editor-toolbar.fullscreen {
z-index: 50;
}
.CodeMirror-fullscreen {
z-index: 50;
}
.editor-preview-side {
z-index: 50;
}
</style>

View File

@@ -3,13 +3,14 @@
<Container size="small">
<form class="space-y-4" @submit.prevent="submit">
<div class="flex flex-col items-center space-y-5">
<img class="h-14" v-if="$page.props.settings.logo" :src="$page.props.settings.logo" />
<img class="h-14" v-if="$page.props.settings.logo" :src="$page.props.settings.logo"/>
<h1 class="font-semibold text-center text-title">
Login to {{ $page.props.settings.name }}
</h1>
</div>
<FormInput :label="__('Email')" :autofocus="true" :errors="$page.props.errors.email" v-model="form.email" id="email"
<FormInput :label="__('Email')" :autofocus="true" :errors="$page.props.errors.email"
v-model="form.email" id="email"
type="email" required/>
<FormInput :label="__('Password')" v-model="form.password" id="password" type="password" required/>
@@ -27,6 +28,24 @@
v-if="$page.props.settings.allow_registration" block>Register
</Button>
</div>
<TextDivider v-if="$page.props.settings.has_terms" :without-text="true"></TextDivider>
<div class="flex justify-between"
v-if="$page.props.settings.has_terms || $page.props.settings.has_privacy">
<div v-if="$page.props.settings.has_terms">
<inertia-link :href="route('page.show', 'terms-of-service')"
class="text-small text-medium-emphasis hover:text-high-emphasis border-b border-dotted">
Terms Of Service
</inertia-link>
</div>
<div v-if="$page.props.settings.has_privacy">
<inertia-link :href="route('page.show', 'privacy-policy')"
class="text-small text-medium-emphasis hover:text-high-emphasis border-b border-dotted">
Privacy Policy
</inertia-link>
</div>
</div>
</form>
</Container>
</div>

View File

@@ -14,7 +14,40 @@
<FormInput :label="__('Password')" :errors="$page.props.errors.password" v-model="form.password" id="password" type="password" required/>
<FormInput :label="__('Confirm password')" :errors="$page.props.errors.password_confirmation" v-model="form.password_confirmation" id="password_confirmation" type="password" required/>
<Button variant="primary" :disabled="sending" block>{{ __('Start') }}</Button>
<div v-if="$page.props.settings.accept_terms_required">
<input id="terms_required" class="form-checkbox" type="checkbox"
v-model="form.terms">
<label for="terms_required" class="ml-2 text-sm">
{{__('Accept terms of service') }}
</label>
<ErrorText v-if="$page.props.errors.terms">{{ $page.props.errors.terms[0] }}</ErrorText>
</div>
<Button variant="primary" :disabled="sending" block>{{ __('Register') }}</Button>
<TextDivider>{{ __('Or') }}</TextDivider>
<div class="space-y-3">
<Button as="inertia-link" :href="route('login')" variant="secondary" block>{{ __('Login') }}</Button>
</div>
<TextDivider v-if="$page.props.settings.has_terms" :without-text="true"></TextDivider>
<div class="flex justify-between"
v-if="$page.props.settings.has_terms || $page.props.settings.has_privacy">
<div v-if="$page.props.settings.has_terms">
<inertia-link :href="route('page.show', 'terms-of-service')"
class="text-small text-medium-emphasis hover:text-high-emphasis border-b border-dotted">
Terms Of Service
</inertia-link>
</div>
<div v-if="$page.props.settings.has_privacy">
<inertia-link :href="route('page.show', 'privacy-policy')"
class="text-small text-medium-emphasis hover:text-high-emphasis border-b border-dotted">
Privacy Policy
</inertia-link>
</div>
</div>
</form>
</Container>
</div>
@@ -23,6 +56,7 @@
<script>
import TextDivider from '@/components/TextDivider'
import FormInput from '@/components/forms/FormInput'
import ErrorText from '@/components/ErrorText'
import Button from '@/components/Button'
import Container from '@/components/Container'
import {useNotification} from '@/hooks/notification'
@@ -39,12 +73,14 @@
FormInput,
Button,
Container,
ErrorText,
},
data() {
return {
sending: false,
form: {
terms: false,
name: null,
email: null,
password: null,

View File

@@ -0,0 +1,45 @@
<template>
<div class="flex items-center justify-center w-full min-h-screen py-8 px-8">
<Container size="medium" class="py-4 space-y-8">
<div class="flex flex-col items-center space-y-5">
<img class="h-14" v-if="$page.props.settings.logo" :src="$page.props.settings.logo" />
<h1 class="font-semibold text-center text-heading">
Privacy Policy
</h1>
</div>
<TextDivider :without-text="true"></TextDivider>
<ul class="flex justify-between">
<li>
<inertia-link :href="route('login')" class="text-medium-emphasis hover:text-high-emphasis border-b border-dotted">Back to login</inertia-link>
</li>
</ul>
<TextDivider :without-text="true"></TextDivider>
<div class="prose" v-html="content"></div>
</Container>
</div>
</template>
<script>
import TextDivider from '@/components/TextDivider'
import FormInput from '@/components/forms/FormInput'
import Button from '@/components/Button'
import Container from '@/components/Container'
export default {
metaInfo: {title: 'Privacy Policy'},
components: {
TextDivider,
FormInput,
Button,
Container,
},
props: {
content: {
type: String,
required: false
}
}
}
</script>

View File

@@ -0,0 +1,45 @@
<template>
<div class="flex items-center justify-center w-full min-h-screen py-8 px-8">
<Container size="medium" class="py-4 space-y-8">
<div class="flex flex-col items-center space-y-5">
<img class="h-14" v-if="$page.props.settings.logo" :src="$page.props.settings.logo" />
<h1 class="font-semibold text-center text-heading">
Terms of Service
</h1>
</div>
<TextDivider :without-text="true"></TextDivider>
<ul class="flex justify-between">
<li>
<inertia-link :href="route('login')" class="text-medium-emphasis hover:text-high-emphasis border-b border-dotted">Back to login</inertia-link>
</li>
</ul>
<TextDivider :without-text="true"></TextDivider>
<div class="prose" v-html="content"></div>
</Container>
</div>
</template>
<script>
import TextDivider from '@/components/TextDivider'
import FormInput from '@/components/forms/FormInput'
import Button from '@/components/Button'
import Container from '@/components/Container'
export default {
metaInfo: {title: 'Terms of Service'},
components: {
TextDivider,
FormInput,
Button,
Container,
},
props: {
content: {
type: String,
required: false
}
}
}
</script>

View File

@@ -8,6 +8,7 @@
const baseClasses = 'w-full px-4 sm:px-8 mx-auto'
const sizeClasses = {
small: 'max-w-sm',
medium: 'max-w-xl',
base: 'max-w-5xl',
large: 'max-w-screen-xl',
fluid: 'max-w-none',

View File

@@ -1,5 +1,6 @@
<template>
<inertia-link
:as="componentIs"
:href="to"
:method="method"
class="flex items-center w-full h-10 px-6 whitespace-nowrap text-medium-emphasis text-small focus:bg-primary focus:text-on-primary hover:text-high-emphasis focus:outline-none"
@@ -15,6 +16,10 @@ export default {
type: String,
required: true,
},
componentIs: {
type: String,
default: 'a'
},
method: {
required: false,
default: 'get'

View File

@@ -1,8 +1,19 @@
<template>
<div class="relative flex items-center justify-center">
<div class="absolute inset-x-0 w-full border-t border-low-emphasis"></div>
<div class="relative px-2 py-1 bg-surface-1 text-body text-medium-emphasis">
<div v-if="!withoutText" class="relative px-2 py-1 bg-surface-1 text-body text-medium-emphasis">
<slot></slot>
</div>
</div>
</template>
<script>
export default {
props: {
withoutText: {
type: Boolean,
default: false,
}
}
}
</script>

View File

@@ -44,7 +44,7 @@
<DropdownListItem class="text-danger" v-if="$page.props.auth.user.role === 'admin'"
:to="route('admin.dashboard')">{{ __('Administration') }}
</DropdownListItem>
<DropdownListItem :to="route('logout')" method="post">{{ __('Sign out') }}
<DropdownListItem :to="route('logout')" component-is="button" method="post">{{ __('Sign out') }}
</DropdownListItem>
</DropdownList>
</Dropdown>

View File

@@ -0,0 +1,72 @@
<template>
<FormGroup>
<Label :errors="errors" :forId="id">{{ label }}</Label>
<slot />
<ErrorText v-if="errors">{{ errors[0] }}</ErrorText>
<HelperText v-if="helperText && !errors">{{ helperText }}</HelperText>
</FormGroup>
</template>
<script>
import FormGroup from '@/components/FormGroup'
import Label from '@/components/Label'
import ErrorText from '@/components/ErrorText'
import HelperText from '@/components/HelperText'
const defaultClasses =
'w-full border-medium-emphasis text-body max-w-lg px-2 border rounded bg-surface-1 focus:outline-none focus:border-primary'
export default {
props: {
id: {
type: String,
required: false,
},
label: {
type: String,
required: true,
},
required: {
type: Boolean,
default: () => false,
},
errors: {
type: Array,
},
placeholder: {
type: String,
},
value: {
required: false,
default: '',
},
rows: {
default: 3,
required: false,
},
helperText: {
type: String
},
},
components: {
FormGroup,
Label,
ErrorText,
HelperText
},
data() {
return {
defaultClasses,
}
},
methods: {
updateValue(value) {
this.$emit('input', value);
},
},
}
</script>

View File

@@ -237,6 +237,7 @@
"This will allow your customers to make support requests to you.": "Dit maakt het mogelijk voor je klanten om contact op te kunnen nemen.",
"Enable documentation platform": "Zet documentatie platform aan",
"This will allow you to create articles for your users to look at.": "Dit maakt het mogelijk om artikelen te schrijven zoals tutorials.",
"This will make sure each site created by one user is always isolated from another.": "Dit zorgt ervoor dat elke site die aangemaakt wordt op een aparte system user aangemaakt wordt, zodat elke site geïsoleerd is van elkaar.",
"Send welcome email": "Verstuur welkom email",
"Server has been updated": "Server is geüpdatet",
"Select default package": "Selecteer standaard pakket",

View File

@@ -10,7 +10,7 @@
@else
{{ __('Welcome to :app, you can directly login with the credentials you have used when signing up.', ['app' => config('app.name')]) }}
@component('mail::button', ['url' => config('app.url')])
{{ __('Visit') }}
{{ __('Visit ' . config('app.name')) }}
@endcomponent
@endif
@endcomponent

View File

@@ -4,8 +4,14 @@ use Illuminate\Support\Facades\Route;
Route::get('/', 'DashboardController@index')->name('dashboard');
Route::get('settings', 'SettingController@index')->name('settings');
Route::patch('settings', 'SettingController@update')->name('settings.update');
Route::group(['prefix' => 'settings'], function () {
Route::get('/', 'SettingController@index')->name('settings');
Route::patch('/', 'SettingController@update')->name('settings.update');
Route::get('terms', 'SettingController@terms')->name('settings.terms');
Route::get('terms/template', 'SettingController@termsTemplate')->name('settings.terms.template');
Route::patch('terms', 'SettingController@updateTerms')->name('settings.terms.update');
});
Route::get('system', 'SystemController@index')->name('system');
Route::post('system/update', 'SystemController@update')->name('system.update');

View File

@@ -11,6 +11,8 @@ Route::post('password-creation', 'Auth\CreatePasswordController@start')->name('p
Route::get('installation-incomplete', 'PageController@installationIncomplete')->name('installation-incomplete');
Route::get('page/{slug}', 'PageController@show')->name('page.show');
// All auth protected routes
Route::group(['middleware' => ['auth', 'auth.blocked']], function () {
Route::get('/', 'DashboardController@index')->name('dashboard');

View File

@@ -0,0 +1,43 @@
This document is created on {DATE}.
Please read these terms of service ("terms", "terms of service") carefully before using {WEBSITE} website (the "service") operated by {NAME} ("us", 'we", "our").
## Conditions of Use
We will provide their services to you, which are subject to the conditions stated below in this document. Every time you visit this website, use its services or make a purchase, you accept the following conditions. This is why we urge you to read them carefully.
## Privacy Policy
Before you continue using our website we advise you to read our privacy policy regarding our user data collection. It will help you better understand our practices.
## Copyright
Content published on this website (digital downloads, images, texts, graphics, logos) is the property of {NAME} and/or its content creators and protected by international copyright laws. The entire compilation of the content found on this website is the exclusive property of {NAME}, with copyright authorship for this compilation by {NAME}.
## Communications
The entire communication with us is electronic. Every time you send us an email or visit our website, you are going to be communicating with us. You hereby consent to receive communications from us. If you subscribe to the news on our website, you are going to receive regular emails from us. We will continue to communicate with you by posting news and notices on our website and by sending you emails. You also agree that all notices, disclosures, agreements and other communications we provide to you electronically meet the legal requirements that such communications be in writing.
## Applicable Law
By visiting this website, you agree that the laws of the location you are currently at, without regard to principles of conflict laws, will govern these terms of service, or any dispute of any sort that might come between {NAME} and you, or its business partners and associates.
## Disputes
Any dispute related in any way to your visit to this website or to products you purchase from us shall be arbitrated by state or federal court from your location and you consent to exclusive jurisdiction and venue of such courts.
## Comments, Reviews, and Emails
Visitors may post content as long as it is not obscene, illegal, defamatory, threatening, infringing of intellectual property rights, invasive of privacy or injurious in any other way to third parties. Content has to be free of software viruses, political campaign, and commercial solicitation.
We reserve all rights (but not the obligation) to remove and/or edit such content. When you post your content, you grant {NAME} non-exclusive, royalty-free and irrevocable right to use, reproduce, publish, modify such content throughout the world in any media.
## License and Site Access
We grant you a limited license to access and make personal use of this website. You are not allowed to download or modify it. This may be done only with written consent from us.
## User Account
If you are an owner of an account on this website, you are solely responsible for maintaining the confidentiality of your private user details (username and password). You are responsible for all activities that occur under your account or password.
We reserve all rights to terminate accounts, edit or remove content and cancel orders in their sole discretion.