mirror of
https://github.com/vitodeploy/vito.git
synced 2025-07-01 05:56:16 +00:00
Compare commits
25 Commits
Author | SHA1 | Date | |
---|---|---|---|
4cda14f4b8 | |||
5e6d338bdc | |||
7312e3f515 | |||
b771db882b | |||
94977797cc | |||
c45872df55 | |||
16fae5334c | |||
7b8deddeca | |||
1bf3c94358 | |||
700cc5f44c | |||
9d13cc0756 | |||
f51d7900f0 | |||
4bd4b34d24 | |||
7c5505be16 | |||
7249cf9ed6 | |||
8282d39722 | |||
38e23a1ceb | |||
1e1204fe40 | |||
7d98986f52 | |||
2c81e324f6 | |||
422da431ec | |||
6c27215ea1 | |||
fb840204ad | |||
e07e197dd9 | |||
3a2dba4ad3 |
@ -15,9 +15,9 @@ DB_USERNAME=root
|
||||
DB_PASSWORD=
|
||||
|
||||
BROADCAST_DRIVER=null
|
||||
CACHE_DRIVER=redis
|
||||
CACHE_DRIVER=file
|
||||
FILESYSTEM_DRIVER=local
|
||||
QUEUE_CONNECTION=sync
|
||||
QUEUE_CONNECTION=default
|
||||
SESSION_DRIVER=database
|
||||
SESSION_LIFETIME=120
|
||||
|
||||
|
2
.github/workflows/code-style.yml
vendored
2
.github/workflows/code-style.yml
vendored
@ -8,7 +8,7 @@ on:
|
||||
|
||||
jobs:
|
||||
code-style:
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
strategy:
|
||||
fail-fast: true
|
||||
|
2
.github/workflows/tests.yml
vendored
2
.github/workflows/tests.yml
vendored
@ -8,7 +8,7 @@ on:
|
||||
|
||||
jobs:
|
||||
tests:
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
services:
|
||||
mysql:
|
||||
|
23
CONTRIBUTING.md
Normal file
23
CONTRIBUTING.md
Normal file
@ -0,0 +1,23 @@
|
||||
# Contributing
|
||||
Thank you for your interest in contributing! There are a couple of contribution guidelines that make it easier to apply the incoming suggestions.
|
||||
|
||||
If you want to contribute please start with the issues. Issues labeled with "Bug" are the higher priorities.
|
||||
|
||||
## Issues
|
||||
1. Issues are the best place to propose a new feature.
|
||||
2. If you are adding a feature that there is no issue for yet, please first open an issue and label it as "feature" and lets discuss it before you implement it.
|
||||
3. Search the issues before proposing a feature to see if it is already under discussion. Referencing existing issues is a good way to increase the priority of your own.
|
||||
4. We don't have an issue template yet, but the more detailed your explanation, the more quickly we'll be able to evaluate it.
|
||||
5. Search for the issue that you also have. Give it a reaction (and comment, if you have something to add). We note that!
|
||||
|
||||
## Pull Requests
|
||||
1. Open PRs represent issues that we're actively thinking about merging (at a pace we can manage). If we think a proposal needs more discussion, or that the existing code would require a lot of back-and-forth to merge, we might close it and suggest you make an issue.
|
||||
2. All PRs should be made against the `main` branch. This can be changed in the future.
|
||||
3. If you are making changes to the front-end layer, Please build the assets via `npm run build` and push it with the other changes.
|
||||
4. Write tests for your code. Tests can be Unit or Feature.
|
||||
5. Code refactors will be closed. For the architectural refactors open an issue first.
|
||||
6. Use `./vendor/bin/pint` to style your code before opening a PR otherwise the actions will fail.
|
||||
7. Typo fixes in documentation are welcome, but if it's at all debatable we might just close it.
|
||||
|
||||
## Misc
|
||||
1. If you think we closed something incorrectly, feel free to (politely) tell us why! We're human and make mistakes.
|
@ -12,7 +12,11 @@ ## Documentation
|
||||
|
||||
## Contribution
|
||||
|
||||
Feel free to open a PR
|
||||
Please read the contribution guide [Here](/CONTRIBUTING.md)
|
||||
|
||||
## Security
|
||||
|
||||
Please read the security policy [Here](/SECURITY.md)
|
||||
|
||||
## Credits
|
||||
|
||||
|
11
SECURITY.md
Normal file
11
SECURITY.md
Normal file
@ -0,0 +1,11 @@
|
||||
# Security Policy
|
||||
|
||||
## Supported Versions
|
||||
|
||||
| Version | Supported |
|
||||
| ------- | ------------------ |
|
||||
| 0.x | :white_check_mark: |
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
If you see a vulnerability, please open an issue or report it directly to me (sa.vaziry@gmail.com)
|
@ -1,7 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace App\Actions\Database;
|
||||
namespace App\Actions\Service;
|
||||
|
||||
use App\Enums\ServiceStatus;
|
||||
use App\Models\Server;
|
||||
use App\Models\Service;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
@ -18,23 +19,21 @@ public function install(Server $server, array $input): Service
|
||||
|
||||
$phpMyAdmin = $server->defaultService('phpmyadmin');
|
||||
if ($phpMyAdmin) {
|
||||
if ($phpMyAdmin->status === 'ready') {
|
||||
throw ValidationException::withMessages([
|
||||
'install' => __('Already installed'),
|
||||
])->errorBag('installPHPMyAdmin');
|
||||
}
|
||||
$phpMyAdmin->delete();
|
||||
throw ValidationException::withMessages([
|
||||
'allowed_ip' => __('Already installed'),
|
||||
]);
|
||||
}
|
||||
$phpMyAdmin = new Service([
|
||||
'server_id' => $server->id,
|
||||
'type' => 'phpmyadmin',
|
||||
'type_data' => [
|
||||
'allowed_ip' => $input['allowed_ip'],
|
||||
'port' => $input['port'],
|
||||
'php' => $server->defaultService('php')->version,
|
||||
],
|
||||
'name' => 'phpmyadmin',
|
||||
'version' => '5.1.2',
|
||||
'status' => 'installing',
|
||||
'status' => ServiceStatus::INSTALLING,
|
||||
'is_default' => 1,
|
||||
]);
|
||||
$phpMyAdmin->save();
|
||||
@ -50,6 +49,12 @@ private function validate(array $input): void
|
||||
{
|
||||
Validator::make($input, [
|
||||
'allowed_ip' => 'required',
|
||||
])->validateWithBag('installPHPMyAdmin');
|
||||
'port' => [
|
||||
'required',
|
||||
'numeric',
|
||||
'min:1',
|
||||
'max:65535',
|
||||
],
|
||||
])->validate();
|
||||
}
|
||||
}
|
@ -49,11 +49,6 @@ public function create(Server $server, array $input): Site
|
||||
]);
|
||||
}
|
||||
|
||||
// detect php version
|
||||
if ($site->type()->language() === 'php') {
|
||||
$site->php_version = $input['php_version'];
|
||||
}
|
||||
|
||||
// validate type
|
||||
$this->validateType($site, $input);
|
||||
|
||||
|
35
app/Actions/Site/UpdateSourceControl.php
Executable file
35
app/Actions/Site/UpdateSourceControl.php
Executable file
@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace App\Actions\Site;
|
||||
|
||||
use App\Models\Site;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Illuminate\Validation\Rule;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
|
||||
class UpdateSourceControl
|
||||
{
|
||||
/**
|
||||
* @throws ValidationException
|
||||
*/
|
||||
public function update(Site $site, array $input): void
|
||||
{
|
||||
$this->validate($input);
|
||||
|
||||
$site->source_control_id = $input['source_control'];
|
||||
$site->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws ValidationException
|
||||
*/
|
||||
protected function validate(array $input): void
|
||||
{
|
||||
Validator::make($input, [
|
||||
'source_control' => [
|
||||
'required',
|
||||
Rule::exists('source_controls', 'id'),
|
||||
],
|
||||
])->validate();
|
||||
}
|
||||
}
|
@ -3,6 +3,7 @@
|
||||
namespace App\Actions\SourceControl;
|
||||
|
||||
use App\Models\SourceControl;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Illuminate\Validation\Rule;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
@ -16,6 +17,7 @@ public function connect(array $input): void
|
||||
'provider' => $input['provider'],
|
||||
'profile' => $input['name'],
|
||||
'access_token' => $input['token'],
|
||||
'url' => Arr::has($input, 'url') ? $input['url'] : null,
|
||||
]);
|
||||
|
||||
if (! $sourceControl->provider()->connect()) {
|
||||
@ -44,6 +46,11 @@ private function validate(array $input): void
|
||||
'token' => [
|
||||
'required',
|
||||
],
|
||||
'url' => [
|
||||
'nullable',
|
||||
'url:http,https',
|
||||
'ends_with:/',
|
||||
],
|
||||
];
|
||||
Validator::make($input, $rules)->validate();
|
||||
}
|
||||
|
@ -21,13 +21,15 @@ public function create(User $user, array $input): void
|
||||
'user_id' => $user->id,
|
||||
'provider' => $input['provider'],
|
||||
'profile' => $input['name'],
|
||||
'credentials' => [
|
||||
'token' => $input['token'],
|
||||
],
|
||||
]);
|
||||
|
||||
$this->validateProvider($input, $storageProvider->provider()->validationRules());
|
||||
|
||||
$storageProvider->credentials = $storageProvider->provider()->credentialData($input);
|
||||
|
||||
if (! $storageProvider->provider()->connect()) {
|
||||
throw ValidationException::withMessages([
|
||||
'token' => __("Couldn't connect to the provider"),
|
||||
'provider' => __("Couldn't connect to the provider"),
|
||||
]);
|
||||
}
|
||||
$storageProvider->save();
|
||||
@ -44,9 +46,11 @@ private function validate(User $user, array $input): void
|
||||
'required',
|
||||
Rule::unique('storage_providers', 'profile')->where('user_id', $user->id),
|
||||
],
|
||||
'token' => [
|
||||
'required',
|
||||
],
|
||||
])->validate();
|
||||
}
|
||||
|
||||
private function validateProvider(array $input, array $rules): void
|
||||
{
|
||||
Validator::make($input, $rules)->validate();
|
||||
}
|
||||
}
|
||||
|
@ -19,6 +19,10 @@ public function update(User $user, array $input): void
|
||||
Validator::make($input, [
|
||||
'name' => ['required', 'string', 'max:255'],
|
||||
'email' => ['required', 'email', 'max:255', Rule::unique('users')->ignore($user->id)],
|
||||
'timezone' => [
|
||||
'required',
|
||||
Rule::in(timezone_identifiers_list()),
|
||||
],
|
||||
])->validateWithBag('updateProfileInformation');
|
||||
|
||||
if ($input['email'] !== $user->email) {
|
||||
@ -27,6 +31,7 @@ public function update(User $user, array $input): void
|
||||
$user->forceFill([
|
||||
'name' => $input['name'],
|
||||
'email' => $input['email'],
|
||||
'timezone' => $input['timezone'],
|
||||
])->save();
|
||||
}
|
||||
}
|
||||
@ -39,6 +44,7 @@ protected function updateVerifiedUser(User $user, array $input): void
|
||||
$user->forceFill([
|
||||
'name' => $input['name'],
|
||||
'email' => $input['email'],
|
||||
'timezone' => $input['timezone'],
|
||||
])->save();
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,8 @@ interface SiteType
|
||||
{
|
||||
public function language(): string;
|
||||
|
||||
public function supportedFeatures(): array;
|
||||
|
||||
public function createValidationRules(array $input): array;
|
||||
|
||||
public function createFields(array $input): array;
|
||||
|
@ -6,6 +6,10 @@
|
||||
|
||||
interface StorageProvider
|
||||
{
|
||||
public function validationRules(): array;
|
||||
|
||||
public function credentialData(array $input): array;
|
||||
|
||||
public function connect(): bool;
|
||||
|
||||
public function upload(Server $server, string $src, string $dest): array;
|
||||
|
16
app/Enums/SiteFeature.php
Normal file
16
app/Enums/SiteFeature.php
Normal file
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace App\Enums;
|
||||
|
||||
use BenSampo\Enum\Enum;
|
||||
|
||||
final class SiteFeature extends Enum
|
||||
{
|
||||
const DEPLOYMENT = 'deployment';
|
||||
|
||||
const ENV = 'env';
|
||||
|
||||
const SSL = 'ssl';
|
||||
|
||||
const QUEUES = 'queues';
|
||||
}
|
@ -11,6 +11,4 @@ final class SourceControl extends Enum
|
||||
const GITLAB = 'gitlab';
|
||||
|
||||
const BITBUCKET = 'bitbucket';
|
||||
|
||||
const CUSTOM = 'custom';
|
||||
}
|
||||
|
@ -6,7 +6,7 @@
|
||||
|
||||
final class StorageProvider extends Enum
|
||||
{
|
||||
const GOOGLE = 'google';
|
||||
|
||||
const DROPBOX = 'dropbox';
|
||||
|
||||
const FTP = 'ftp';
|
||||
}
|
||||
|
@ -7,7 +7,7 @@
|
||||
|
||||
class SourceControlIsNotConnected extends Exception
|
||||
{
|
||||
public function __construct(protected SourceControl|string $sourceControl, string $message = null)
|
||||
public function __construct(protected SourceControl|string|null $sourceControl, string $message = null)
|
||||
{
|
||||
parent::__construct($message ?? 'Source control is not connected');
|
||||
}
|
||||
|
39
app/Http/Controllers/GitHookController.php
Normal file
39
app/Http/Controllers/GitHookController.php
Normal file
@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Exceptions\SourceControlIsNotConnected;
|
||||
use App\Models\GitHook;
|
||||
use Illuminate\Http\Request;
|
||||
use Throwable;
|
||||
|
||||
class GitHookController extends Controller
|
||||
{
|
||||
public function __invoke(Request $request)
|
||||
{
|
||||
if (! $request->input('secret')) {
|
||||
abort(404);
|
||||
}
|
||||
|
||||
/** @var GitHook $gitHook */
|
||||
$gitHook = GitHook::query()
|
||||
->where('secret', $request->input('secret'))
|
||||
->firstOrFail();
|
||||
|
||||
foreach ($gitHook->actions as $action) {
|
||||
if ($action == 'deploy') {
|
||||
try {
|
||||
$gitHook->site->deploy();
|
||||
} catch (SourceControlIsNotConnected) {
|
||||
// TODO: send notification
|
||||
} catch (Throwable $e) {
|
||||
Log::error('git-hook-exception', (array) $e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
]);
|
||||
}
|
||||
}
|
60
app/Http/Livewire/Application/AutoDeployment.php
Normal file
60
app/Http/Livewire/Application/AutoDeployment.php
Normal file
@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Livewire\Application;
|
||||
|
||||
use App\Exceptions\SourceControlIsNotConnected;
|
||||
use App\Models\Site;
|
||||
use App\Traits\HasToast;
|
||||
use App\Traits\RefreshComponentOnBroadcast;
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Livewire\Component;
|
||||
use Throwable;
|
||||
|
||||
class AutoDeployment extends Component
|
||||
{
|
||||
use RefreshComponentOnBroadcast;
|
||||
use HasToast;
|
||||
|
||||
public Site $site;
|
||||
|
||||
/**
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function enable(): void
|
||||
{
|
||||
if (! $this->site->auto_deployment) {
|
||||
try {
|
||||
$this->site->enableAutoDeployment();
|
||||
|
||||
$this->site->refresh();
|
||||
|
||||
$this->toast()->success(__('Auto deployment has been enabled.'));
|
||||
} catch (SourceControlIsNotConnected) {
|
||||
$this->toast()->error(__('Source control is not connected. Check site\'s settings.'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function disable(): void
|
||||
{
|
||||
if ($this->site->auto_deployment) {
|
||||
try {
|
||||
$this->site->disableAutoDeployment();
|
||||
|
||||
$this->site->refresh();
|
||||
|
||||
$this->toast()->success(__('Auto deployment has been disabled.'));
|
||||
} catch (SourceControlIsNotConnected) {
|
||||
$this->toast()->error(__('Source control is not connected. Check site\'s settings.'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function render(): View
|
||||
{
|
||||
return view('livewire.application.auto-deployment');
|
||||
}
|
||||
}
|
@ -28,6 +28,7 @@ public function save(): void
|
||||
session()->flash('status', 'script-updated');
|
||||
|
||||
$this->emitTo(Deploy::class, '$refresh');
|
||||
$this->emitTo(AutoDeployment::class, '$refresh');
|
||||
}
|
||||
|
||||
public function render(): View
|
||||
|
@ -3,11 +3,13 @@
|
||||
namespace App\Http\Livewire\Php;
|
||||
|
||||
use App\Actions\PHP\InstallNewPHP;
|
||||
use App\Actions\PHP\InstallPHPExtension;
|
||||
use App\Actions\PHP\UpdatePHPIni;
|
||||
use App\Models\Server;
|
||||
use App\Models\Service;
|
||||
use App\SSHCommands\PHP\GetPHPIniCommand;
|
||||
use App\Traits\RefreshComponentOnBroadcast;
|
||||
use Exception;
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Livewire\Component;
|
||||
use Throwable;
|
||||
@ -24,6 +26,10 @@ class InstalledVersions extends Component
|
||||
|
||||
public string $ini = 'Loading php.ini';
|
||||
|
||||
public ?int $extensionId = null;
|
||||
|
||||
public string $extension = '';
|
||||
|
||||
public function install(string $version): void
|
||||
{
|
||||
app(InstallNewPHP::class)->install($this->server, [
|
||||
@ -35,6 +41,7 @@ public function install(string $version): void
|
||||
|
||||
public function restart(int $id): void
|
||||
{
|
||||
/** @var Service $service */
|
||||
$service = Service::query()->findOrFail($id);
|
||||
$service->restart();
|
||||
|
||||
@ -43,6 +50,7 @@ public function restart(int $id): void
|
||||
|
||||
public function uninstall(): void
|
||||
{
|
||||
/** @var Service $service */
|
||||
$service = Service::query()->findOrFail($this->uninstallId);
|
||||
$service->uninstall();
|
||||
|
||||
@ -56,6 +64,7 @@ public function loadIni(int $id): void
|
||||
$this->iniId = $id;
|
||||
$this->ini = 'Loading php.ini';
|
||||
|
||||
/** @var Service $service */
|
||||
$service = Service::query()->findOrFail($this->iniId);
|
||||
|
||||
try {
|
||||
@ -67,6 +76,7 @@ public function loadIni(int $id): void
|
||||
|
||||
public function saveIni(): void
|
||||
{
|
||||
/** @var Service $service */
|
||||
$service = Service::query()->findOrFail($this->iniId);
|
||||
|
||||
app(UpdatePHPIni::class)->update($service, $this->all()['ini']);
|
||||
@ -76,10 +86,32 @@ public function saveIni(): void
|
||||
session()->flash('status', 'ini-updated');
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
*/
|
||||
public function installExtension(): void
|
||||
{
|
||||
/** @var Service $service */
|
||||
$service = Service::query()->findOrFail($this->extensionId);
|
||||
|
||||
app(InstallPHPExtension::class)->handle($service, [
|
||||
'name' => $this->extension,
|
||||
]);
|
||||
|
||||
session()->flash('status', 'started-installation');
|
||||
}
|
||||
|
||||
public function render(): View
|
||||
{
|
||||
if ($this->extensionId) {
|
||||
/** @var Service $php */
|
||||
$php = Service::query()->findOrFail($this->extensionId);
|
||||
$installedExtensions = $php->type_data['extensions'] ?? [];
|
||||
}
|
||||
|
||||
return view('livewire.php.installed-versions', [
|
||||
'phps' => $this->server->services()->where('type', 'php')->get(),
|
||||
'installedExtensions' => $installedExtensions ?? [],
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
@ -15,10 +15,13 @@ class UpdateProfileInformation extends Component
|
||||
|
||||
public string $email;
|
||||
|
||||
public string $timezone;
|
||||
|
||||
public function mount(): void
|
||||
{
|
||||
$this->name = auth()->user()->name;
|
||||
$this->email = auth()->user()->email;
|
||||
$this->timezone = auth()->user()->timezone;
|
||||
}
|
||||
|
||||
/**
|
||||
|
31
app/Http/Livewire/Services/InstallPHPMyAdmin.php
Normal file
31
app/Http/Livewire/Services/InstallPHPMyAdmin.php
Normal file
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Livewire\Services;
|
||||
|
||||
use App\Actions\Service\InstallPHPMyAdmin as InstallPHPMyAdminAction;
|
||||
use App\Models\Server;
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Livewire\Component;
|
||||
|
||||
class InstallPHPMyAdmin extends Component
|
||||
{
|
||||
public Server $server;
|
||||
|
||||
public string $allowed_ip;
|
||||
|
||||
public string $port = '5433';
|
||||
|
||||
public function install(): void
|
||||
{
|
||||
app(InstallPHPMyAdminAction::class)->install($this->server, $this->all());
|
||||
|
||||
$this->dispatchBrowserEvent('started', true);
|
||||
|
||||
$this->emitTo(ServicesList::class, '$refresh');
|
||||
}
|
||||
|
||||
public function render(): View
|
||||
{
|
||||
return view('livewire.services.install-phpmyadmin');
|
||||
}
|
||||
}
|
@ -3,6 +3,7 @@
|
||||
namespace App\Http\Livewire\Services;
|
||||
|
||||
use App\Models\Server;
|
||||
use App\Models\Service;
|
||||
use App\Traits\RefreshComponentOnBroadcast;
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Livewire\Component;
|
||||
@ -15,6 +16,7 @@ class ServicesList extends Component
|
||||
|
||||
public function stop(int $id): void
|
||||
{
|
||||
/** @var Service $service */
|
||||
$service = $this->server->services()->where('id', $id)->firstOrFail();
|
||||
|
||||
$service->stop();
|
||||
@ -24,6 +26,7 @@ public function stop(int $id): void
|
||||
|
||||
public function start(int $id): void
|
||||
{
|
||||
/** @var Service $service */
|
||||
$service = $this->server->services()->where('id', $id)->firstOrFail();
|
||||
|
||||
$service->start();
|
||||
@ -33,6 +36,7 @@ public function start(int $id): void
|
||||
|
||||
public function restart(int $id): void
|
||||
{
|
||||
/** @var Service $service */
|
||||
$service = $this->server->services()->where('id', $id)->firstOrFail();
|
||||
|
||||
$service->restart();
|
||||
@ -40,6 +44,16 @@ public function restart(int $id): void
|
||||
$this->refreshComponent([]);
|
||||
}
|
||||
|
||||
public function uninstall(int $id): void
|
||||
{
|
||||
/** @var Service $service */
|
||||
$service = $this->server->services()->where('id', $id)->firstOrFail();
|
||||
|
||||
$service->uninstall();
|
||||
|
||||
$this->refreshComponent([]);
|
||||
}
|
||||
|
||||
public function render(): View
|
||||
{
|
||||
return view('livewire.services.services-list', [
|
||||
|
@ -2,7 +2,6 @@
|
||||
|
||||
namespace App\Http\Livewire\Sites;
|
||||
|
||||
use App\Enums\SiteType;
|
||||
use App\Exceptions\SourceControlIsNotConnected;
|
||||
use App\Models\Server;
|
||||
use App\Models\SourceControl;
|
||||
@ -16,23 +15,12 @@ class CreateSite extends Component
|
||||
|
||||
public Server $server;
|
||||
|
||||
public string $type = SiteType::LARAVEL;
|
||||
|
||||
public string $domain;
|
||||
|
||||
public string $alias;
|
||||
|
||||
public string $php_version = '';
|
||||
|
||||
public string $web_directory = 'public';
|
||||
|
||||
public string $source_control = '';
|
||||
|
||||
public string $repository;
|
||||
|
||||
public string $branch;
|
||||
|
||||
public bool $composer;
|
||||
public array $inputs = [
|
||||
'type' => '',
|
||||
'web_directory' => 'public',
|
||||
'source_control' => '',
|
||||
'php_version' => '',
|
||||
];
|
||||
|
||||
/**
|
||||
* @throws SourceControlIsNotConnected
|
||||
@ -41,7 +29,7 @@ public function create(): void
|
||||
{
|
||||
$site = app(\App\Actions\Site\CreateSite::class)->create(
|
||||
$this->server,
|
||||
$this->all()
|
||||
$this->inputs
|
||||
);
|
||||
|
||||
$this->redirect(route('servers.sites.show', [
|
||||
|
33
app/Http/Livewire/Sites/UpdateSourceControlProvider.php
Normal file
33
app/Http/Livewire/Sites/UpdateSourceControlProvider.php
Normal file
@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Livewire\Sites;
|
||||
|
||||
use App\Actions\Site\UpdateSourceControl;
|
||||
use App\Models\Site;
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Livewire\Component;
|
||||
|
||||
class UpdateSourceControlProvider extends Component
|
||||
{
|
||||
public Site $site;
|
||||
|
||||
public $source_control = null;
|
||||
|
||||
public function update(): void
|
||||
{
|
||||
app(UpdateSourceControl::class)->update($this->site, $this->all());
|
||||
|
||||
$this->resetErrorBag();
|
||||
|
||||
session()->flash('status', 'source-control-updated');
|
||||
}
|
||||
|
||||
public function render(): View
|
||||
{
|
||||
if (! $this->source_control) {
|
||||
$this->source_control = $this->site->source_control_id;
|
||||
}
|
||||
|
||||
return view('livewire.sites.update-source-control-provider');
|
||||
}
|
||||
}
|
@ -14,6 +14,20 @@ class ConnectProvider extends Component
|
||||
|
||||
public string $token;
|
||||
|
||||
public string $host;
|
||||
|
||||
public string $port;
|
||||
|
||||
public string $path = '/';
|
||||
|
||||
public string $username;
|
||||
|
||||
public string $password;
|
||||
|
||||
public int $ssl = 1;
|
||||
|
||||
public int $passive = 0;
|
||||
|
||||
public function connect(): void
|
||||
{
|
||||
app(CreateStorageProvider::class)->create(auth()->user(), $this->all());
|
||||
|
@ -3,6 +3,7 @@
|
||||
namespace App\Jobs\Installation;
|
||||
|
||||
use App\Actions\FirewallRule\CreateRule;
|
||||
use App\Enums\ServiceStatus;
|
||||
use App\Jobs\Job;
|
||||
use App\Models\FirewallRule;
|
||||
use App\Models\Service;
|
||||
@ -32,6 +33,9 @@ public function handle(): void
|
||||
$this->downloadSource();
|
||||
$this->setUpVHost();
|
||||
$this->restartPHP();
|
||||
$this->service->update([
|
||||
'status' => ServiceStatus::READY,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -41,7 +45,7 @@ private function setUpFirewall(): void
|
||||
{
|
||||
$this->firewallRule = FirewallRule::query()
|
||||
->where('server_id', $this->service->server_id)
|
||||
->where('port', '54331')
|
||||
->where('port', $this->service->type_data['port'])
|
||||
->first();
|
||||
if ($this->firewallRule) {
|
||||
$this->firewallRule->source = $this->service->type_data['allowed_ip'];
|
||||
@ -52,7 +56,7 @@ private function setUpFirewall(): void
|
||||
[
|
||||
'type' => 'allow',
|
||||
'protocol' => 'tcp',
|
||||
'port' => '54331',
|
||||
'port' => $this->service->type_data['port'],
|
||||
'source' => $this->service->type_data['allowed_ip'],
|
||||
'mask' => '0',
|
||||
]
|
||||
@ -78,6 +82,7 @@ private function setUpVHost(): void
|
||||
{
|
||||
$vhost = File::get(resource_path('commands/webserver/nginx/phpmyadmin-vhost.conf'));
|
||||
$vhost = Str::replace('__php_version__', $this->service->server->defaultService('php')->version, $vhost);
|
||||
$vhost = Str::replace('__port__', $this->service->type_data['port'], $vhost);
|
||||
$this->service->server->ssh()->exec(
|
||||
new CreateNginxPHPMyAdminVHostCommand($vhost),
|
||||
'create-phpmyadmin-vhost'
|
||||
@ -98,6 +103,9 @@ private function restartPHP(): void
|
||||
public function failed(Throwable $throwable): Throwable
|
||||
{
|
||||
$this->firewallRule?->removeFromServer();
|
||||
$this->service->update([
|
||||
'status' => ServiceStatus::INSTALLATION_FAILED,
|
||||
]);
|
||||
throw $throwable;
|
||||
}
|
||||
}
|
||||
|
@ -37,7 +37,7 @@ private function removeFirewallRule(): void
|
||||
/** @var ?FirewallRule $rule */
|
||||
$rule = FirewallRule::query()
|
||||
->where('server_id', $this->service->server_id)
|
||||
->where('port', '54331')
|
||||
->where('port', $this->service->type_data['port'])
|
||||
->first();
|
||||
$rule?->removeFromServer();
|
||||
}
|
||||
|
@ -3,13 +3,12 @@
|
||||
namespace App\Jobs;
|
||||
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldBeUnique;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
abstract class Job implements ShouldQueue, ShouldBeUnique
|
||||
abstract class Job implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
}
|
||||
|
@ -4,7 +4,6 @@
|
||||
|
||||
use App\Enums\DeploymentStatus;
|
||||
use App\Events\Broadcast;
|
||||
use App\Helpers\SSH;
|
||||
use App\Jobs\Job;
|
||||
use App\Models\Deployment;
|
||||
use App\SSHCommands\System\RunScriptCommand;
|
||||
@ -18,13 +17,11 @@ class Deploy extends Job
|
||||
|
||||
protected string $script;
|
||||
|
||||
protected SSH $ssh;
|
||||
|
||||
public function __construct(Deployment $deployment, string $path, string $script)
|
||||
public function __construct(Deployment $deployment, string $path)
|
||||
{
|
||||
$this->script = $deployment->deploymentScript->content;
|
||||
$this->deployment = $deployment;
|
||||
$this->path = $path;
|
||||
$this->script = $script;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -32,28 +29,31 @@ public function __construct(Deployment $deployment, string $path, string $script
|
||||
*/
|
||||
public function handle(): void
|
||||
{
|
||||
$this->ssh = $this->deployment->site->server->ssh();
|
||||
$this->ssh->exec(
|
||||
new RunScriptCommand($this->path, $this->script),
|
||||
'deploy',
|
||||
$this->deployment->site_id
|
||||
);
|
||||
$this->deployment->status = DeploymentStatus::FINISHED;
|
||||
$this->deployment->log_id = $this->ssh->log->id;
|
||||
$this->deployment->save();
|
||||
event(
|
||||
new Broadcast('deploy-site-finished', [
|
||||
'deployment' => $this->deployment,
|
||||
])
|
||||
);
|
||||
$ssh = $this->deployment->site->server->ssh();
|
||||
try {
|
||||
$ssh->exec(
|
||||
new RunScriptCommand($this->path, $this->script),
|
||||
'deploy',
|
||||
$this->deployment->site_id
|
||||
);
|
||||
$this->deployment->status = DeploymentStatus::FINISHED;
|
||||
$this->deployment->log_id = $ssh->log->id;
|
||||
$this->deployment->save();
|
||||
event(
|
||||
new Broadcast('deploy-site-finished', [
|
||||
'deployment' => $this->deployment,
|
||||
])
|
||||
);
|
||||
} catch (Throwable) {
|
||||
$this->deployment->log_id = $ssh->log->id;
|
||||
$this->deployment->save();
|
||||
$this->failed();
|
||||
}
|
||||
}
|
||||
|
||||
public function failed(): void
|
||||
{
|
||||
$this->deployment->status = DeploymentStatus::FAILED;
|
||||
if ($this->ssh->log) {
|
||||
$this->deployment->log_id = $this->ssh->log->id;
|
||||
}
|
||||
$this->deployment->save();
|
||||
event(
|
||||
new Broadcast('deploy-site-failed', [
|
||||
|
@ -52,7 +52,7 @@ public function handle(): void
|
||||
$this->site->id
|
||||
);
|
||||
|
||||
if (! Str::contains($result, 'Wordpress installed!')) {
|
||||
if (! Str::contains($result, 'Success')) {
|
||||
throw new FailedToInstallWordpress($result);
|
||||
}
|
||||
}
|
||||
|
@ -76,7 +76,7 @@ public function getPathAttribute(): string
|
||||
|
||||
public function getStoragePathAttribute(): string
|
||||
{
|
||||
return '/'.$this->backup->database->name.'/'.$this->name.'.zip';
|
||||
return '/'.$this->name.'.zip';
|
||||
}
|
||||
|
||||
public function restore(Database $database): void
|
||||
|
@ -57,19 +57,19 @@ public function server(): BelongsTo
|
||||
/**
|
||||
* create database on server
|
||||
*/
|
||||
public function createOnServer(): void
|
||||
public function createOnServer(string $queue = 'ssh'): void
|
||||
{
|
||||
dispatch(new CreateOnServer($this))->onConnection('ssh');
|
||||
dispatch(new CreateOnServer($this))->onConnection($queue);
|
||||
}
|
||||
|
||||
/**
|
||||
* delete database from server
|
||||
*/
|
||||
public function deleteFromServer(): void
|
||||
public function deleteFromServer(string $queue = 'ssh'): void
|
||||
{
|
||||
$this->status = DatabaseStatus::DELETING;
|
||||
$this->save();
|
||||
dispatch(new DeleteFromServer($this))->onConnection('ssh');
|
||||
dispatch(new DeleteFromServer($this))->onConnection($queue);
|
||||
}
|
||||
|
||||
public function backups(): HasMany
|
||||
|
@ -54,17 +54,17 @@ public function scopeHasDatabase(Builder $query, string $databaseName): Builder
|
||||
return $query->where('databases', 'like', "%\"$databaseName\"%");
|
||||
}
|
||||
|
||||
public function createOnServer(): void
|
||||
public function createOnServer(string $queue = 'ssh'): void
|
||||
{
|
||||
dispatch(new CreateOnServer($this))->onConnection('ssh');
|
||||
dispatch(new CreateOnServer($this))->onConnection($queue);
|
||||
}
|
||||
|
||||
public function deleteFromServer(): void
|
||||
public function deleteFromServer(string $queue = 'ssh'): void
|
||||
{
|
||||
$this->status = DatabaseStatus::DELETING;
|
||||
$this->save();
|
||||
|
||||
dispatch(new DeleteFromServer($this))->onConnection('ssh');
|
||||
dispatch(new DeleteFromServer($this))->onConnection($queue);
|
||||
}
|
||||
|
||||
public function linkNewDatabase(string $name): void
|
||||
@ -79,14 +79,14 @@ public function linkNewDatabase(string $name): void
|
||||
}
|
||||
}
|
||||
|
||||
public function linkUser(): void
|
||||
public function linkUser(string $queue = 'ssh'): void
|
||||
{
|
||||
dispatch(new LinkUser($this))->onConnection('ssh');
|
||||
dispatch(new LinkUser($this))->onConnection($queue);
|
||||
}
|
||||
|
||||
public function unlinkUser(): void
|
||||
public function unlinkUser(string $queue = 'ssh'): void
|
||||
{
|
||||
dispatch(new UnlinkUser($this))->onConnection('ssh');
|
||||
dispatch(new UnlinkUser($this))->onConnection($queue);
|
||||
}
|
||||
|
||||
public function getFullUserAttribute(): string
|
||||
|
@ -2,9 +2,9 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Exceptions\FailedToDeployGitHook;
|
||||
use Exception;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Throwable;
|
||||
@ -22,6 +22,8 @@
|
||||
*/
|
||||
class GitHook extends AbstractModel
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $fillable = [
|
||||
'site_id',
|
||||
'source_control_id',
|
||||
@ -55,9 +57,6 @@ public function scopeHasEvent(Builder $query, string $event): Builder
|
||||
return $query->where('events', 'like', "%\"{$event}\"%");
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws FailedToDeployGitHook
|
||||
*/
|
||||
public function deployHook(): void
|
||||
{
|
||||
$this->update(
|
||||
|
@ -76,7 +76,7 @@ public function uninstaller(): mixed
|
||||
return new $uninstaller($this);
|
||||
}
|
||||
|
||||
public function getUnitAttribute($value): string
|
||||
public function getUnitAttribute($value): ?string
|
||||
{
|
||||
if ($value) {
|
||||
return $value;
|
||||
|
@ -6,7 +6,7 @@
|
||||
use App\Enums\DeploymentStatus;
|
||||
use App\Enums\SiteStatus;
|
||||
use App\Enums\SslStatus;
|
||||
use App\Exceptions\FailedToDeployGitHook;
|
||||
use App\Events\Broadcast;
|
||||
use App\Exceptions\SourceControlIsNotConnected;
|
||||
use App\Jobs\Site\ChangePHPVersion;
|
||||
use App\Jobs\Site\Deploy;
|
||||
@ -19,7 +19,8 @@
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Database\Eloquent\Relations\HasOne;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Str;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
@ -266,13 +267,7 @@ public function deploy(): Deployment
|
||||
}
|
||||
$deployment->save();
|
||||
|
||||
dispatch(
|
||||
new Deploy(
|
||||
$deployment,
|
||||
$this->path,
|
||||
$this->deployment_script_text
|
||||
)
|
||||
)->onConnection('ssh');
|
||||
dispatch(new Deploy($deployment, $this->path))->onConnection('ssh');
|
||||
|
||||
return $deployment;
|
||||
}
|
||||
@ -347,22 +342,16 @@ public function getWebDirectoryPathAttribute(): string
|
||||
|
||||
/**
|
||||
* @throws SourceControlIsNotConnected
|
||||
* @throws ValidationException
|
||||
* @throws FailedToDeployGitHook
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function enableAutoDeployment(): void
|
||||
{
|
||||
if ($this->gitHook) {
|
||||
throw ValidationException::withMessages([
|
||||
'auto_deployment' => __('Auto deployment already enabled'),
|
||||
])->errorBag('auto_deployment');
|
||||
return;
|
||||
}
|
||||
|
||||
if (! $this->sourceControl()) {
|
||||
throw ValidationException::withMessages([
|
||||
'auto_deployment' => __('Your application does not use any source controls'),
|
||||
])->errorBag('auto_deployment');
|
||||
throw new SourceControlIsNotConnected($this->source_control);
|
||||
}
|
||||
|
||||
try {
|
||||
@ -370,7 +359,7 @@ public function enableAutoDeployment(): void
|
||||
$gitHook = new GitHook([
|
||||
'site_id' => $this->id,
|
||||
'source_control_id' => $this->sourceControl()->id,
|
||||
'secret' => generate_uid(),
|
||||
'secret' => Str::uuid()->toString(),
|
||||
'actions' => ['deploy'],
|
||||
'events' => ['push'],
|
||||
]);
|
||||
@ -405,4 +394,49 @@ public function getSshKeyNameAttribute(): string
|
||||
{
|
||||
return str('site_'.$this->id)->toString();
|
||||
}
|
||||
|
||||
public function installationFinished(): void
|
||||
{
|
||||
$this->update([
|
||||
'status' => SiteStatus::READY,
|
||||
'progress' => 100,
|
||||
]);
|
||||
event(
|
||||
new Broadcast('install-site-finished', [
|
||||
'site' => $this,
|
||||
])
|
||||
);
|
||||
/** @todo notify */
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function installationFailed(Throwable $e): void
|
||||
{
|
||||
$this->update([
|
||||
'status' => SiteStatus::INSTALLATION_FAILED,
|
||||
]);
|
||||
event(
|
||||
new Broadcast('install-site-failed', [
|
||||
'site' => $this,
|
||||
])
|
||||
);
|
||||
/** @todo notify */
|
||||
Log::error('install-site-error', [
|
||||
'error' => (string) $e,
|
||||
]);
|
||||
|
||||
throw $e;
|
||||
}
|
||||
|
||||
public function hasFeature(string $feature): bool
|
||||
{
|
||||
return in_array($feature, $this->type()->supportedFeatures());
|
||||
}
|
||||
|
||||
public function isReady(): bool
|
||||
{
|
||||
return $this->status === SiteStatus::READY;
|
||||
}
|
||||
}
|
||||
|
40
app/SSHCommands/Storage/DownloadFromFTPCommand.php
Normal file
40
app/SSHCommands/Storage/DownloadFromFTPCommand.php
Normal file
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace App\SSHCommands\Storage;
|
||||
|
||||
use App\SSHCommands\Command;
|
||||
use Illuminate\Support\Facades\File;
|
||||
|
||||
class DownloadFromFTPCommand extends Command
|
||||
{
|
||||
public function __construct(
|
||||
protected string $src,
|
||||
protected string $dest,
|
||||
protected string $host,
|
||||
protected string $port,
|
||||
protected string $username,
|
||||
protected string $password,
|
||||
protected bool $ssl,
|
||||
protected bool $passive,
|
||||
) {
|
||||
}
|
||||
|
||||
public function file(): string
|
||||
{
|
||||
return File::get(resource_path('commands/storage/download-from-ftp.sh'));
|
||||
}
|
||||
|
||||
public function content(): string
|
||||
{
|
||||
return str($this->file())
|
||||
->replace('__src__', $this->src)
|
||||
->replace('__dest__', $this->dest)
|
||||
->replace('__host__', $this->host)
|
||||
->replace('__port__', $this->port)
|
||||
->replace('__username__', $this->username)
|
||||
->replace('__password__', $this->password)
|
||||
->replace('__ssl__', $this->ssl ? 's' : '')
|
||||
->replace('__passive__', $this->passive ? '--ftp-pasv' : '')
|
||||
->toString();
|
||||
}
|
||||
}
|
40
app/SSHCommands/Storage/UploadToFTPCommand.php
Normal file
40
app/SSHCommands/Storage/UploadToFTPCommand.php
Normal file
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace App\SSHCommands\Storage;
|
||||
|
||||
use App\SSHCommands\Command;
|
||||
use Illuminate\Support\Facades\File;
|
||||
|
||||
class UploadToFTPCommand extends Command
|
||||
{
|
||||
public function __construct(
|
||||
protected string $src,
|
||||
protected string $dest,
|
||||
protected string $host,
|
||||
protected string $port,
|
||||
protected string $username,
|
||||
protected string $password,
|
||||
protected bool $ssl,
|
||||
protected bool $passive,
|
||||
) {
|
||||
}
|
||||
|
||||
public function file(): string
|
||||
{
|
||||
return File::get(resource_path('commands/storage/upload-to-ftp.sh'));
|
||||
}
|
||||
|
||||
public function content(): string
|
||||
{
|
||||
return str($this->file())
|
||||
->replace('__src__', $this->src)
|
||||
->replace('__dest__', $this->dest)
|
||||
->replace('__host__', $this->host)
|
||||
->replace('__port__', $this->port)
|
||||
->replace('__username__', $this->username)
|
||||
->replace('__password__', $this->password)
|
||||
->replace('__ssl__', $this->ssl ? 's' : '')
|
||||
->replace('__passive__', $this->passive ? '--ftp-pasv' : '')
|
||||
->toString();
|
||||
}
|
||||
}
|
@ -2,14 +2,12 @@
|
||||
|
||||
namespace App\SiteTypes;
|
||||
|
||||
use App\Enums\SiteStatus;
|
||||
use App\Events\Broadcast;
|
||||
use App\Enums\SiteFeature;
|
||||
use App\Jobs\Site\CloneRepository;
|
||||
use App\Jobs\Site\ComposerInstall;
|
||||
use App\Jobs\Site\CreateVHost;
|
||||
use App\Jobs\Site\DeployKey;
|
||||
use Illuminate\Support\Facades\Bus;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Validation\Rule;
|
||||
use Throwable;
|
||||
|
||||
@ -20,12 +18,22 @@ public function language(): string
|
||||
return 'php';
|
||||
}
|
||||
|
||||
public function supportedFeatures(): array
|
||||
{
|
||||
return [
|
||||
SiteFeature::DEPLOYMENT,
|
||||
SiteFeature::ENV,
|
||||
SiteFeature::SSL,
|
||||
SiteFeature::QUEUES,
|
||||
];
|
||||
}
|
||||
|
||||
public function createValidationRules(array $input): array
|
||||
{
|
||||
return [
|
||||
'php_version' => [
|
||||
'required',
|
||||
'in:'.implode(',', $this->site->server->installedPHPVersions()),
|
||||
Rule::in($this->site->server->installedPHPVersions()),
|
||||
],
|
||||
'source_control' => [
|
||||
'required',
|
||||
@ -44,9 +52,9 @@ public function createFields(array $input): array
|
||||
{
|
||||
return [
|
||||
'web_directory' => $input['web_directory'] ?? '',
|
||||
'source_control_id' => $input['source_control'] ?? '',
|
||||
'repository' => $input['repository'] ?? '',
|
||||
'branch' => $input['branch'] ?? '',
|
||||
'source_control_id' => $input['source_control'],
|
||||
'repository' => $input['repository'],
|
||||
'branch' => $input['branch'],
|
||||
];
|
||||
}
|
||||
|
||||
@ -54,6 +62,7 @@ public function data(array $input): array
|
||||
{
|
||||
return [
|
||||
'composer' => (bool) $input['composer'],
|
||||
'php_version' => $input['php_version'],
|
||||
];
|
||||
}
|
||||
|
||||
@ -76,33 +85,12 @@ function () {
|
||||
}
|
||||
|
||||
$chain[] = function () {
|
||||
$this->site->update([
|
||||
'status' => SiteStatus::READY,
|
||||
'progress' => 100,
|
||||
]);
|
||||
event(
|
||||
new Broadcast('install-site-finished', [
|
||||
'site' => $this->site,
|
||||
])
|
||||
);
|
||||
/** @todo notify */
|
||||
$this->site->installationFinished();
|
||||
};
|
||||
|
||||
Bus::chain($chain)
|
||||
->catch(function (Throwable $e) {
|
||||
$this->site->update([
|
||||
'status' => SiteStatus::INSTALLATION_FAILED,
|
||||
]);
|
||||
event(
|
||||
new Broadcast('install-site-failed', [
|
||||
'site' => $this->site,
|
||||
])
|
||||
);
|
||||
/** @todo notify */
|
||||
Log::error('install-site-error', [
|
||||
'error' => (string) $e,
|
||||
]);
|
||||
throw $e;
|
||||
$this->site->installationFailed($e);
|
||||
})
|
||||
->onConnection('ssh-long')
|
||||
->dispatch();
|
||||
|
@ -2,13 +2,15 @@
|
||||
|
||||
namespace App\SiteTypes;
|
||||
|
||||
use App\Enums\SiteStatus;
|
||||
use App\Events\Broadcast;
|
||||
use App\Enums\SiteFeature;
|
||||
use App\Jobs\Site\CreateVHost;
|
||||
use App\Jobs\Site\InstallWordpress;
|
||||
use App\Models\Database;
|
||||
use App\Models\DatabaseUser;
|
||||
use App\SSHCommands\Wordpress\UpdateWordpressCommand;
|
||||
use Closure;
|
||||
use Illuminate\Support\Facades\Bus;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Validation\Rule;
|
||||
use Throwable;
|
||||
|
||||
class Wordpress extends AbstractSiteType
|
||||
@ -18,94 +20,100 @@ public function language(): string
|
||||
return 'php';
|
||||
}
|
||||
|
||||
public function supportedFeatures(): array
|
||||
{
|
||||
return [
|
||||
SiteFeature::SSL,
|
||||
];
|
||||
}
|
||||
|
||||
public function createValidationRules(array $input): array
|
||||
{
|
||||
return [
|
||||
'php_version' => [
|
||||
'required',
|
||||
Rule::in($this->site->server->installedPHPVersions()),
|
||||
],
|
||||
'title' => 'required',
|
||||
'username' => 'required',
|
||||
'password' => 'required',
|
||||
'email' => 'required|email',
|
||||
'database' => 'required',
|
||||
'database_user' => 'required',
|
||||
'database' => [
|
||||
'required',
|
||||
Rule::unique('databases', 'name')->where(function ($query) {
|
||||
return $query->where('server_id', $this->site->server_id);
|
||||
}),
|
||||
function (string $attribute, mixed $value, Closure $fail) {
|
||||
if (! $this->site->server->database()) {
|
||||
$fail(__('Database is not installed'));
|
||||
}
|
||||
},
|
||||
],
|
||||
'database_user' => [
|
||||
'required',
|
||||
Rule::unique('database_users', 'username')->where(function ($query) {
|
||||
return $query->where('server_id', $this->site->server_id);
|
||||
}),
|
||||
],
|
||||
'database_password' => 'required',
|
||||
];
|
||||
}
|
||||
|
||||
public function createFields(array $input): array
|
||||
{
|
||||
return [
|
||||
'web_directory' => $input['web_directory'] ?? '',
|
||||
'web_directory' => '',
|
||||
'php_version' => $input['php_version'],
|
||||
];
|
||||
}
|
||||
|
||||
public function data(array $input): array
|
||||
{
|
||||
$data = $this->site->type_data;
|
||||
$data['url'] = $this->site->url;
|
||||
if (isset($input['title']) && $input['title']) {
|
||||
$data['title'] = $input['title'];
|
||||
}
|
||||
if (isset($input['username']) && $input['username']) {
|
||||
$data['username'] = $input['username'];
|
||||
}
|
||||
if (isset($input['email']) && $input['email']) {
|
||||
$data['email'] = $input['email'];
|
||||
}
|
||||
if (isset($input['password']) && $input['password']) {
|
||||
$data['password'] = $input['password'];
|
||||
}
|
||||
if (isset($input['database']) && $input['database']) {
|
||||
$data['database'] = $input['database'];
|
||||
}
|
||||
if (isset($input['database_user']) && $input['database_user']) {
|
||||
$data['database_user'] = $input['database_user'];
|
||||
}
|
||||
if (isset($input['url']) && $input['url']) {
|
||||
$data['url'] = $input['url'];
|
||||
}
|
||||
|
||||
return $data;
|
||||
return [
|
||||
'url' => $this->site->url,
|
||||
'title' => $input['title'],
|
||||
'username' => $input['username'],
|
||||
'email' => $input['email'],
|
||||
'password' => $input['password'],
|
||||
'database' => $input['database'],
|
||||
'database_user' => $input['database_user'],
|
||||
'database_password' => $input['database_password'],
|
||||
];
|
||||
}
|
||||
|
||||
public function install(): void
|
||||
{
|
||||
$chain = [
|
||||
new CreateVHost($this->site),
|
||||
$this->progress(30),
|
||||
$this->progress(15),
|
||||
function () {
|
||||
/** @var Database $database */
|
||||
$database = $this->site->server->databases()->create([
|
||||
'name' => $this->site->type_data['database'],
|
||||
]);
|
||||
$database->createOnServer('sync');
|
||||
/** @var DatabaseUser $databaseUser */
|
||||
$databaseUser = $this->site->server->databaseUsers()->create([
|
||||
'username' => $this->site->type_data['database_user'],
|
||||
'password' => $this->site->type_data['database_password'],
|
||||
'databases' => [$this->site->type_data['database']],
|
||||
]);
|
||||
$databaseUser->createOnServer('sync');
|
||||
$databaseUser->unlinkUser('sync');
|
||||
$databaseUser->linkUser('sync');
|
||||
},
|
||||
$this->progress(50),
|
||||
new InstallWordpress($this->site),
|
||||
$this->progress(65),
|
||||
$this->progress(75),
|
||||
function () {
|
||||
$this->site->php()?->restart();
|
||||
$this->site->installationFinished();
|
||||
},
|
||||
];
|
||||
|
||||
$chain[] = function () {
|
||||
$this->site->update([
|
||||
'status' => SiteStatus::READY,
|
||||
'progress' => 100,
|
||||
]);
|
||||
event(
|
||||
new Broadcast('install-site-finished', [
|
||||
'site' => $this->site,
|
||||
])
|
||||
);
|
||||
/** @todo notify */
|
||||
};
|
||||
|
||||
Bus::chain($chain)
|
||||
->catch(function (Throwable $e) {
|
||||
$this->site->update([
|
||||
'status' => SiteStatus::INSTALLATION_FAILED,
|
||||
]);
|
||||
event(
|
||||
new Broadcast('install-site-failed', [
|
||||
'site' => $this->site,
|
||||
])
|
||||
);
|
||||
/** @todo notify */
|
||||
Log::error('install-site-error', [
|
||||
'error' => (string) $e,
|
||||
]);
|
||||
throw $e;
|
||||
$this->site->installationFailed($e);
|
||||
})
|
||||
->onConnection('ssh-long')
|
||||
->dispatch();
|
||||
@ -139,32 +147,13 @@ function () {
|
||||
'update-wordpress',
|
||||
$this->site->id
|
||||
);
|
||||
$this->site->update([
|
||||
'status' => SiteStatus::READY,
|
||||
]);
|
||||
event(
|
||||
new Broadcast('install-site-finished', [
|
||||
'site' => $this->site,
|
||||
])
|
||||
);
|
||||
$this->site->installationFinished();
|
||||
},
|
||||
];
|
||||
|
||||
Bus::chain($chain)
|
||||
->catch(function (Throwable $e) {
|
||||
$this->site->update([
|
||||
'status' => SiteStatus::INSTALLATION_FAILED,
|
||||
]);
|
||||
event(
|
||||
new Broadcast('install-site-failed', [
|
||||
'site' => $this->site,
|
||||
])
|
||||
);
|
||||
/** @todo notify */
|
||||
Log::error('install-site-error', [
|
||||
'error' => (string) $e,
|
||||
]);
|
||||
throw $e;
|
||||
$this->site->installationFailed($e);
|
||||
})
|
||||
->onConnection('ssh')
|
||||
->dispatch();
|
||||
|
@ -46,7 +46,7 @@ public function deployHook(string $repo, array $events, string $secret): array
|
||||
{
|
||||
$response = Http::withToken($this->sourceControl->access_token)->post($this->apiUrl."/repositories/$repo/hooks", [
|
||||
'description' => 'deploy',
|
||||
'url' => url('/git-hooks?secret='.$secret),
|
||||
'url' => url('/api/git-hooks?secret='.$secret),
|
||||
'events' => [
|
||||
'repo:'.implode(',', $events),
|
||||
],
|
||||
|
@ -1,41 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\SourceControlProviders;
|
||||
|
||||
class Custom extends AbstractSourceControlProvider
|
||||
{
|
||||
public function connect(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getRepo(string $repo = null): string
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
public function fullRepoUrl(string $repo, string $key): string
|
||||
{
|
||||
return $repo;
|
||||
}
|
||||
|
||||
public function deployHook(string $repo, array $events, string $secret): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function destroyHook(string $repo, string $hookId): void
|
||||
{
|
||||
// TODO: Implement destroyHook() method.
|
||||
}
|
||||
|
||||
public function getLastCommit(string $repo, string $branch): ?array
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public function deployKey(string $title, string $repo, string $key): void
|
||||
{
|
||||
// TODO: Implement deployKey() method.
|
||||
}
|
||||
}
|
@ -59,7 +59,7 @@ public function deployHook(string $repo, array $events, string $secret): array
|
||||
'name' => 'web',
|
||||
'events' => $events,
|
||||
'config' => [
|
||||
'url' => url('/git-hooks?secret='.$secret),
|
||||
'url' => url('/api/git-hooks?secret='.$secret),
|
||||
'content_type' => 'json',
|
||||
],
|
||||
'active' => true,
|
||||
|
@ -10,12 +10,14 @@
|
||||
|
||||
class Gitlab extends AbstractSourceControlProvider
|
||||
{
|
||||
protected string $apiUrl = 'https://gitlab.com/api/v4';
|
||||
protected string $defaultApiHost = 'https://gitlab.com/';
|
||||
|
||||
protected string $apiVersion = 'api/v4';
|
||||
|
||||
public function connect(): bool
|
||||
{
|
||||
$res = Http::withToken($this->sourceControl->access_token)
|
||||
->get($this->apiUrl.'/projects');
|
||||
->get($this->getApiUrl().'/projects');
|
||||
|
||||
return $res->successful();
|
||||
}
|
||||
@ -27,7 +29,7 @@ public function getRepo(string $repo = null): mixed
|
||||
{
|
||||
$repository = $repo ? urlencode($repo) : null;
|
||||
$res = Http::withToken($this->sourceControl->access_token)
|
||||
->get($this->apiUrl.'/projects/'.$repository.'/repository/commits');
|
||||
->get($this->getApiUrl().'/projects/'.$repository.'/repository/commits');
|
||||
|
||||
$this->handleResponseErrors($res, $repo);
|
||||
|
||||
@ -36,7 +38,9 @@ public function getRepo(string $repo = null): mixed
|
||||
|
||||
public function fullRepoUrl(string $repo, string $key): string
|
||||
{
|
||||
return sprintf('git@gitlab.com-%s:%s.git', $key, $repo);
|
||||
$host = parse_url($this->getApiUrl())['host'];
|
||||
|
||||
return sprintf('git@%s-%s:%s.git', $host, $key, $repo);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -46,10 +50,10 @@ public function deployHook(string $repo, array $events, string $secret): array
|
||||
{
|
||||
$repository = urlencode($repo);
|
||||
$response = Http::withToken($this->sourceControl->access_token)->post(
|
||||
$this->apiUrl.'/projects/'.$repository.'/hooks',
|
||||
$this->getApiUrl().'/projects/'.$repository.'/hooks',
|
||||
[
|
||||
'description' => 'deploy',
|
||||
'url' => url('/git-hooks?secret='.$secret),
|
||||
'url' => url('/api/git-hooks?secret='.$secret),
|
||||
'push_events' => in_array('push', $events),
|
||||
'issues_events' => false,
|
||||
'job_events' => false,
|
||||
@ -81,7 +85,7 @@ public function destroyHook(string $repo, string $hookId): void
|
||||
{
|
||||
$repository = urlencode($repo);
|
||||
$response = Http::withToken($this->sourceControl->access_token)->delete(
|
||||
$this->apiUrl.'/projects/'.$repository.'/hooks/'.$hookId
|
||||
$this->getApiUrl().'/projects/'.$repository.'/hooks/'.$hookId
|
||||
);
|
||||
|
||||
if ($response->status() != 204) {
|
||||
@ -96,7 +100,7 @@ public function getLastCommit(string $repo, string $branch): ?array
|
||||
{
|
||||
$repository = urlencode($repo);
|
||||
$res = Http::withToken($this->sourceControl->access_token)
|
||||
->get($this->apiUrl.'/projects/'.$repository.'/repository/commits?ref_name='.$branch);
|
||||
->get($this->getApiUrl().'/projects/'.$repository.'/repository/commits?ref_name='.$branch);
|
||||
|
||||
$this->handleResponseErrors($res, $repo);
|
||||
|
||||
@ -123,7 +127,7 @@ public function deployKey(string $title, string $repo, string $key): void
|
||||
{
|
||||
$repository = urlencode($repo);
|
||||
$response = Http::withToken($this->sourceControl->access_token)->post(
|
||||
$this->apiUrl.'/projects/'.$repository.'/deploy_keys',
|
||||
$this->getApiUrl().'/projects/'.$repository.'/deploy_keys',
|
||||
[
|
||||
'title' => $title,
|
||||
'key' => $key,
|
||||
@ -135,4 +139,13 @@ public function deployKey(string $title, string $repo, string $key): void
|
||||
throw new FailedToDeployGitKey(json_decode($response->body())->message);
|
||||
}
|
||||
}
|
||||
|
||||
public function getApiUrl(): string
|
||||
{
|
||||
$host = $this->sourceControl->url === null
|
||||
? $this->defaultApiHost
|
||||
: $this->sourceControl->url;
|
||||
|
||||
return $host.$this->apiVersion;
|
||||
}
|
||||
}
|
||||
|
@ -13,6 +13,20 @@ class Dropbox extends AbstractStorageProvider
|
||||
{
|
||||
protected string $apiUrl = 'https://api.dropboxapi.com/2';
|
||||
|
||||
public function validationRules(): array
|
||||
{
|
||||
return [
|
||||
'token' => 'required',
|
||||
];
|
||||
}
|
||||
|
||||
public function credentialData(array $input): array
|
||||
{
|
||||
return [
|
||||
'token' => $input['token'],
|
||||
];
|
||||
}
|
||||
|
||||
public function connect(): bool
|
||||
{
|
||||
$res = Http::withToken($this->storageProvider->credentials['token'])
|
||||
|
129
app/StorageProviders/FTP.php
Normal file
129
app/StorageProviders/FTP.php
Normal file
@ -0,0 +1,129 @@
|
||||
<?php
|
||||
|
||||
namespace App\StorageProviders;
|
||||
|
||||
use App\Models\Server;
|
||||
use App\SSHCommands\Storage\DownloadFromFTPCommand;
|
||||
use App\SSHCommands\Storage\UploadToFTPCommand;
|
||||
use FTP\Connection;
|
||||
use Throwable;
|
||||
|
||||
class FTP extends AbstractStorageProvider
|
||||
{
|
||||
public function validationRules(): array
|
||||
{
|
||||
return [
|
||||
'host' => 'required',
|
||||
'port' => 'required|numeric',
|
||||
'path' => 'required',
|
||||
'username' => 'required',
|
||||
'password' => 'required',
|
||||
'ssl' => 'required',
|
||||
'passive' => 'required',
|
||||
];
|
||||
}
|
||||
|
||||
public function credentialData(array $input): array
|
||||
{
|
||||
return [
|
||||
'host' => $input['host'],
|
||||
'port' => $input['port'],
|
||||
'path' => $input['path'],
|
||||
'username' => $input['username'],
|
||||
'password' => $input['password'],
|
||||
'ssl' => (bool) $input['ssl'],
|
||||
'passive' => (bool) $input['passive'],
|
||||
];
|
||||
}
|
||||
|
||||
public function connect(): bool
|
||||
{
|
||||
$connection = $this->connection();
|
||||
|
||||
$isConnected = $connection && $this->login($connection);
|
||||
|
||||
if ($isConnected) {
|
||||
ftp_close($connection);
|
||||
}
|
||||
|
||||
return $isConnected;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function upload(Server $server, string $src, string $dest): array
|
||||
{
|
||||
$server->ssh()->exec(
|
||||
new UploadToFTPCommand(
|
||||
$src,
|
||||
$this->storageProvider->credentials['path'].'/'.$dest,
|
||||
$this->storageProvider->credentials['host'],
|
||||
$this->storageProvider->credentials['port'],
|
||||
$this->storageProvider->credentials['username'],
|
||||
$this->storageProvider->credentials['password'],
|
||||
$this->storageProvider->credentials['ssl'],
|
||||
$this->storageProvider->credentials['passive'],
|
||||
),
|
||||
'upload-to-ftp'
|
||||
);
|
||||
|
||||
return [
|
||||
'size' => null,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function download(Server $server, string $src, string $dest): void
|
||||
{
|
||||
$server->ssh()->exec(
|
||||
new DownloadFromFTPCommand(
|
||||
$this->storageProvider->credentials['path'].'/'.$src,
|
||||
$dest,
|
||||
$this->storageProvider->credentials['host'],
|
||||
$this->storageProvider->credentials['port'],
|
||||
$this->storageProvider->credentials['username'],
|
||||
$this->storageProvider->credentials['password'],
|
||||
$this->storageProvider->credentials['ssl'],
|
||||
$this->storageProvider->credentials['passive'],
|
||||
),
|
||||
'download-from-ftp'
|
||||
);
|
||||
}
|
||||
|
||||
public function delete(array $paths): void
|
||||
{
|
||||
$connection = $this->connection();
|
||||
|
||||
if ($connection && $this->login($connection)) {
|
||||
if ($this->storageProvider->credentials['passive']) {
|
||||
ftp_pasv($connection, true);
|
||||
}
|
||||
|
||||
foreach ($paths as $path) {
|
||||
ftp_delete($connection, $this->storageProvider->credentials['path'].'/'.$path);
|
||||
}
|
||||
}
|
||||
|
||||
ftp_close($connection);
|
||||
}
|
||||
|
||||
private function connection(): bool|Connection
|
||||
{
|
||||
$credentials = $this->storageProvider->credentials;
|
||||
if ($credentials['ssl']) {
|
||||
return ftp_ssl_connect($credentials['host'], $credentials['port'], 5);
|
||||
}
|
||||
|
||||
return ftp_connect($credentials['host'], $credentials['port'], 5);
|
||||
}
|
||||
|
||||
private function login(Connection $connection): bool
|
||||
{
|
||||
$credentials = $this->storageProvider->credentials;
|
||||
|
||||
return ftp_login($connection, $credentials['username'], $credentials['password']);
|
||||
}
|
||||
}
|
@ -15,7 +15,8 @@
|
||||
"laravel/socialite": "^5.2",
|
||||
"laravel/tinker": "^2.8",
|
||||
"livewire/livewire": "^2.12",
|
||||
"phpseclib/phpseclib": "~3.0"
|
||||
"phpseclib/phpseclib": "~3.0",
|
||||
"ext-ftp": "*"
|
||||
},
|
||||
"require-dev": {
|
||||
"fakerphp/faker": "^1.9.1",
|
||||
|
29
composer.lock
generated
29
composer.lock
generated
@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "41386d67a21e3c07c635228527722815",
|
||||
"content-hash": "53be6925a69aeafb21d079b82e51c1c4",
|
||||
"packages": [
|
||||
{
|
||||
"name": "aws/aws-crt-php",
|
||||
@ -1586,16 +1586,16 @@
|
||||
},
|
||||
{
|
||||
"name": "laravel/framework",
|
||||
"version": "v10.7.1",
|
||||
"version": "v10.16.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/laravel/framework.git",
|
||||
"reference": "ddbbb2b50388721fe63312bb4469cae13163fd36"
|
||||
"reference": "5c93d2795c393b462481179ce42dedfb30cc19b5"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/laravel/framework/zipball/ddbbb2b50388721fe63312bb4469cae13163fd36",
|
||||
"reference": "ddbbb2b50388721fe63312bb4469cae13163fd36",
|
||||
"url": "https://api.github.com/repos/laravel/framework/zipball/5c93d2795c393b462481179ce42dedfb30cc19b5",
|
||||
"reference": "5c93d2795c393b462481179ce42dedfb30cc19b5",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -1782,7 +1782,7 @@
|
||||
"issues": "https://github.com/laravel/framework/issues",
|
||||
"source": "https://github.com/laravel/framework"
|
||||
},
|
||||
"time": "2023-04-11T14:11:49+00:00"
|
||||
"time": "2023-07-26T03:30:46+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laravel/sanctum",
|
||||
@ -3275,16 +3275,16 @@
|
||||
},
|
||||
{
|
||||
"name": "phpseclib/phpseclib",
|
||||
"version": "3.0.21",
|
||||
"version": "3.0.34",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpseclib/phpseclib.git",
|
||||
"reference": "4580645d3fc05c189024eb3b834c6c1e4f0f30a1"
|
||||
"reference": "56c79f16a6ae17e42089c06a2144467acc35348a"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/4580645d3fc05c189024eb3b834c6c1e4f0f30a1",
|
||||
"reference": "4580645d3fc05c189024eb3b834c6c1e4f0f30a1",
|
||||
"url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/56c79f16a6ae17e42089c06a2144467acc35348a",
|
||||
"reference": "56c79f16a6ae17e42089c06a2144467acc35348a",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -3365,7 +3365,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/phpseclib/phpseclib/issues",
|
||||
"source": "https://github.com/phpseclib/phpseclib/tree/3.0.21"
|
||||
"source": "https://github.com/phpseclib/phpseclib/tree/3.0.34"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -3381,7 +3381,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2023-07-09T15:24:48+00:00"
|
||||
"time": "2023-11-27T11:13:31+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pragmarx/google2fa",
|
||||
@ -9080,8 +9080,9 @@
|
||||
"prefer-stable": true,
|
||||
"prefer-lowest": false,
|
||||
"platform": {
|
||||
"php": "^8.1"
|
||||
"php": "^8.1",
|
||||
"ext-ftp": "*"
|
||||
},
|
||||
"platform-dev": [],
|
||||
"plugin-api-version": "2.3.0"
|
||||
"plugin-api-version": "2.6.0"
|
||||
}
|
||||
|
@ -25,11 +25,12 @@
|
||||
use App\ServiceHandlers\Webserver\Nginx;
|
||||
use App\SiteTypes\Laravel;
|
||||
use App\SiteTypes\PHPSite;
|
||||
use App\SiteTypes\Wordpress;
|
||||
use App\SourceControlProviders\Bitbucket;
|
||||
use App\SourceControlProviders\Custom;
|
||||
use App\SourceControlProviders\Github;
|
||||
use App\SourceControlProviders\Gitlab;
|
||||
use App\StorageProviders\Dropbox;
|
||||
use App\StorageProviders\FTP;
|
||||
|
||||
return [
|
||||
/*
|
||||
@ -262,12 +263,12 @@
|
||||
'site_types' => [
|
||||
\App\Enums\SiteType::PHP,
|
||||
\App\Enums\SiteType::LARAVEL,
|
||||
// \App\Enums\SiteType::WORDPRESS,
|
||||
\App\Enums\SiteType::WORDPRESS,
|
||||
],
|
||||
'site_types_class' => [
|
||||
\App\Enums\SiteType::PHP => PHPSite::class,
|
||||
\App\Enums\SiteType::LARAVEL => Laravel::class,
|
||||
// \App\Enums\SiteType::WORDPRESS => Wordpress::class,
|
||||
\App\Enums\SiteType::WORDPRESS => Wordpress::class,
|
||||
],
|
||||
|
||||
/*
|
||||
@ -283,7 +284,6 @@
|
||||
'github' => Github::class,
|
||||
'gitlab' => Gitlab::class,
|
||||
'bitbucket' => Bitbucket::class,
|
||||
'custom' => Custom::class,
|
||||
],
|
||||
|
||||
/*
|
||||
@ -291,11 +291,12 @@
|
||||
*/
|
||||
'php_extensions' => [
|
||||
'imagick',
|
||||
'geoip',
|
||||
'exif',
|
||||
'gmagick',
|
||||
'gmp',
|
||||
'intl',
|
||||
'sqlite3',
|
||||
'opcache',
|
||||
],
|
||||
|
||||
/*
|
||||
@ -323,7 +324,6 @@
|
||||
'https' => 443,
|
||||
'mysql' => 3306,
|
||||
'ftp' => 21,
|
||||
'phpmyadmin' => 54331,
|
||||
'tcp' => '',
|
||||
'udp' => '',
|
||||
],
|
||||
@ -355,8 +355,10 @@
|
||||
*/
|
||||
'storage_providers' => [
|
||||
'dropbox',
|
||||
'ftp',
|
||||
],
|
||||
'storage_providers_class' => [
|
||||
'dropbox' => Dropbox::class,
|
||||
'ftp' => FTP::class,
|
||||
],
|
||||
];
|
||||
|
@ -13,7 +13,7 @@
|
||||
|
|
||||
*/
|
||||
|
||||
'default' => env('QUEUE_CONNECTION', 'sync'),
|
||||
'default' => env('QUEUE_CONNECTION', 'default'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
|
@ -139,85 +139,125 @@
|
||||
],
|
||||
],
|
||||
'images' => [
|
||||
'us-east-1' => [
|
||||
'ubuntu_18' => 'ami-0279c3b3186e54acd',
|
||||
'ubuntu_20' => 'ami-083654bd07b5da81d',
|
||||
],
|
||||
'us-east-2' => [
|
||||
'ubuntu_18' => 'ami-020db2c14939a8efb',
|
||||
'ubuntu_20' => 'ami-0629230e074c580f2',
|
||||
],
|
||||
'us-west-1' => [
|
||||
'ubuntu_18' => 'ami-083f68207d3376798',
|
||||
'ubuntu_20' => 'ami-053ac55bdcfe96e85',
|
||||
],
|
||||
'us-west-2' => [
|
||||
'ubuntu_18' => 'ami-09889d8d54f9e0a0e',
|
||||
'ubuntu_20' => 'ami-036d46416a34a611c',
|
||||
'af-south-1' => [
|
||||
'ubuntu_20' => 'ami-03684d4c2541e5333',
|
||||
'ubuntu_22' => 'ami-05759acc7d8973892',
|
||||
],
|
||||
'ap-east-1' => [
|
||||
'ubuntu_18' => 'ami-032c0a4bd39a5772c',
|
||||
'ubuntu_20' => 'ami-0a9c1cc3697104990',
|
||||
],
|
||||
'ap-south-1' => [
|
||||
'ubuntu_18' => 'ami-00782a7608c7fc226',
|
||||
'ubuntu_20' => 'ami-0567e0d2b4b2169ae',
|
||||
'ubuntu_20' => 'ami-0b19a97bf326b4931',
|
||||
'ubuntu_22' => 'ami-03490b1b7425e5fe3',
|
||||
],
|
||||
'ap-northeast-1' => [
|
||||
'ubuntu_18' => 'ami-085e9421f80dbe728',
|
||||
'ubuntu_20' => 'ami-036d0684fc96830ca',
|
||||
'ubuntu_20' => 'ami-0e25df74d27e028e6',
|
||||
'ubuntu_22' => 'ami-09a81b370b76de6a2',
|
||||
],
|
||||
'ap-northeast-2' => [
|
||||
'ubuntu_18' => 'ami-0252a84eb1d66c2a0',
|
||||
'ubuntu_20' => 'ami-0f8b8babb98cc66d0',
|
||||
'ubuntu_20' => 'ami-003a709e1e4ce3729',
|
||||
'ubuntu_22' => 'ami-086cae3329a3f7d75',
|
||||
],
|
||||
'ap-northeast-3' => [
|
||||
'ubuntu_20' => 'ami-06c1367bd83de7d47',
|
||||
'ubuntu_22' => 'ami-0690c54203f5f67da',
|
||||
],
|
||||
'ap-south-1' => [
|
||||
'ubuntu_20' => 'ami-0b88997c830e88c76',
|
||||
'ubuntu_22' => 'ami-0287a05f0ef0e9d9a',
|
||||
],
|
||||
'ap-south-2' => [
|
||||
'ubuntu_20' => 'ami-049e2ae605332dba6',
|
||||
'ubuntu_22' => 'ami-06fe902e167e67d33',
|
||||
],
|
||||
'ap-southeast-1' => [
|
||||
'ubuntu_18' => 'ami-0907c2c44ea451f84',
|
||||
'ubuntu_20' => 'ami-0fed77069cd5a6d6c',
|
||||
'ubuntu_20' => 'ami-0a6461ddb52e9db63',
|
||||
'ubuntu_22' => 'ami-078c1149d8ad719a7',
|
||||
],
|
||||
'ap-southeast-2' => [
|
||||
'ubuntu_18' => 'ami-00abf0511a7f4cee5',
|
||||
'ubuntu_20' => 'ami-0bf8b986de7e3c7ce',
|
||||
'ubuntu_20' => 'ami-0a9fb81cc3289919c',
|
||||
'ubuntu_22' => 'ami-0df4b2961410d4cff',
|
||||
],
|
||||
'ap-southeast-3' => [
|
||||
'ubuntu_20' => 'ami-05ee5bed682a3fff0',
|
||||
'ubuntu_22' => 'ami-0fb6d1fdeeea10488',
|
||||
],
|
||||
'ap-southeast-4' => [
|
||||
'ubuntu_20' => 'ami-02f9759882b112414',
|
||||
'ubuntu_22' => 'ami-043a030d3eeabec75',
|
||||
],
|
||||
'ca-central-1' => [
|
||||
'ubuntu_18' => 'ami-0e471deaa43652c4a',
|
||||
'ubuntu_20' => 'ami-0bb84e7329f4fa1f7',
|
||||
'ubuntu_20' => 'ami-0daaea212e620de87',
|
||||
'ubuntu_22' => 'ami-06873c81b882339ac',
|
||||
],
|
||||
'cn-north-1' => [
|
||||
'ubuntu_20' => 'ami-0c8bcac1fe3389a72',
|
||||
'ubuntu_22' => 'ami-0728a1a4cc9e07753',
|
||||
],
|
||||
'cn-northwest-1' => [
|
||||
'ubuntu_20' => 'ami-0415bfb3ea62e17c0',
|
||||
'ubuntu_22' => 'ami-05529cf859783e600',
|
||||
],
|
||||
'eu-central-1' => [
|
||||
'ubuntu_18' => 'ami-00d5e377dd7fad751',
|
||||
'ubuntu_20' => 'ami-0a49b025fffbbdac6',
|
||||
'ubuntu_20' => 'ami-0b369586722023326',
|
||||
'ubuntu_22' => 'ami-06dd92ecc74fdfb36',
|
||||
],
|
||||
'eu-west-1' => [
|
||||
'ubuntu_18' => 'ami-095b735dce49535b5',
|
||||
'ubuntu_20' => 'ami-08edbb0e85d6a0a07',
|
||||
],
|
||||
'eu-west-2' => [
|
||||
'ubuntu_18' => 'ami-008485ca60c91a0f3',
|
||||
'ubuntu_20' => 'ami-0fdf70ed5c34c5f52',
|
||||
],
|
||||
'eu-west-3' => [
|
||||
'ubuntu_18' => 'ami-0df7d9cc2767d16cd',
|
||||
'ubuntu_20' => 'ami-06d79c60d7454e2af',
|
||||
],
|
||||
'eu-south-1' => [
|
||||
'ubuntu_18' => 'ami-09f165dd6bd167be5',
|
||||
'ubuntu_20' => 'ami-0f8ce9c417115413d',
|
||||
'eu-central-2' => [
|
||||
'ubuntu_20' => 'ami-070c78d5ed65f11c8',
|
||||
'ubuntu_22' => 'ami-07cf963e6321c9e6a',
|
||||
],
|
||||
'eu-north-1' => [
|
||||
'ubuntu_18' => 'ami-038904f9024f34a0c',
|
||||
'ubuntu_20' => 'ami-0bd9c26722573e69b',
|
||||
'ubuntu_20' => 'ami-0c5863072fc83557e',
|
||||
'ubuntu_22' => 'ami-0fe8bec493a81c7da',
|
||||
],
|
||||
'eu-south-1' => [
|
||||
'ubuntu_20' => 'ami-0966ff128f1497260',
|
||||
'ubuntu_22' => 'ami-0b03947fd0ce0eed2',
|
||||
],
|
||||
'eu-south-2' => [
|
||||
'ubuntu_20' => 'ami-087296a5b46cb95ce',
|
||||
'ubuntu_22' => 'ami-03486abd2962c176f',
|
||||
],
|
||||
'eu-west-1' => [
|
||||
'ubuntu_20' => 'ami-0e3e7f215a53e2a86',
|
||||
'ubuntu_22' => 'ami-0694d931cee176e7d',
|
||||
],
|
||||
'eu-west-2' => [
|
||||
'ubuntu_20' => 'ami-0b22eee5ba6bb6772',
|
||||
'ubuntu_22' => 'ami-0505148b3591e4c07',
|
||||
],
|
||||
'eu-west-3' => [
|
||||
'ubuntu_20' => 'ami-0f14fa1f9c69f4111',
|
||||
'ubuntu_22' => 'ami-00983e8a26e4c9bd9',
|
||||
],
|
||||
'il-central-1' => [
|
||||
'ubuntu_20' => 'ami-0703881563bf5fab7',
|
||||
'ubuntu_22' => 'ami-03869c813f5a2e20c',
|
||||
],
|
||||
'me-central-1' => [
|
||||
'ubuntu_20' => 'ami-04a5bde3b044c7c21',
|
||||
'ubuntu_22' => 'ami-02168d82d5c12118f',
|
||||
],
|
||||
'me-south-1' => [
|
||||
'ubuntu_18' => 'ami-0ef669c57b73af73b',
|
||||
'ubuntu_20' => 'ami-0b4946d7420c44be4',
|
||||
'ubuntu_20' => 'ami-0165b692f5714e330',
|
||||
'ubuntu_22' => 'ami-0f8d2a6080634ee69',
|
||||
],
|
||||
'sa-east-1' => [
|
||||
'ubuntu_18' => 'ami-0ed2b3edeb28afa59',
|
||||
'ubuntu_20' => 'ami-0e66f5495b4efdd0f',
|
||||
'ubuntu_20' => 'ami-095ca107fb46b81e6',
|
||||
'ubuntu_22' => 'ami-0b6c2d49148000cd5',
|
||||
],
|
||||
'af-south-1' => [
|
||||
'ubuntu_18' => 'ami-0191bb2cf509687ee',
|
||||
'ubuntu_20' => 'ami-0ff86122fd4ad7208',
|
||||
'us-east-1' => [
|
||||
'ubuntu_20' => 'ami-0fe0238291c8e3f07',
|
||||
'ubuntu_22' => 'ami-0fc5d935ebf8bc3bc',
|
||||
],
|
||||
'us-east-2' => [
|
||||
'ubuntu_20' => 'ami-0b6968e5c7117349a',
|
||||
'ubuntu_22' => 'ami-0e83be366243f524a',
|
||||
],
|
||||
'us-west-1' => [
|
||||
'ubuntu_20' => 'ami-092efbcc9a2d2be8a',
|
||||
'ubuntu_22' => 'ami-0cbd40f694b804622',
|
||||
],
|
||||
'us-west-2' => [
|
||||
'ubuntu_20' => 'ami-0a55cdf919d10eac9',
|
||||
'ubuntu_22' => 'ami-0efcece6bed30fd98',
|
||||
],
|
||||
],
|
||||
],
|
||||
|
29
database/factories/GitHookFactory.php
Normal file
29
database/factories/GitHookFactory.php
Normal file
@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use App\Models\GitHook;
|
||||
use App\Models\Site;
|
||||
use App\Models\SourceControl;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
use Illuminate\Support\Carbon;
|
||||
|
||||
class GitHookFactory extends Factory
|
||||
{
|
||||
protected $model = GitHook::class;
|
||||
|
||||
public function definition(): array
|
||||
{
|
||||
return [
|
||||
'secret' => $this->faker->word(),
|
||||
'events' => $this->faker->words(),
|
||||
'actions' => $this->faker->words(),
|
||||
'hook_id' => $this->faker->word(),
|
||||
'hook_response' => $this->faker->words(),
|
||||
'created_at' => Carbon::now(),
|
||||
'updated_at' => Carbon::now(),
|
||||
'site_id' => Site::factory(),
|
||||
'source_control_id' => SourceControl::factory(),
|
||||
];
|
||||
}
|
||||
}
|
@ -13,8 +13,34 @@ class SourceControlFactory extends Factory
|
||||
public function definition(): array
|
||||
{
|
||||
return [
|
||||
'provider' => $this->faker->randomElement(\App\Enums\SourceControl::getValues()),
|
||||
'access_token' => Str::random(10),
|
||||
];
|
||||
}
|
||||
|
||||
public function gitlab(): Factory
|
||||
{
|
||||
return $this->state(function (array $attributes) {
|
||||
return [
|
||||
'provider' => \App\Enums\SourceControl::GITLAB,
|
||||
];
|
||||
});
|
||||
}
|
||||
|
||||
public function github(): Factory
|
||||
{
|
||||
return $this->state(function (array $attributes) {
|
||||
return [
|
||||
'provider' => \App\Enums\SourceControl::GITHUB,
|
||||
];
|
||||
});
|
||||
}
|
||||
|
||||
public function bitbucket(): Factory
|
||||
{
|
||||
return $this->state(function (array $attributes) {
|
||||
return [
|
||||
'provider' => \App\Enums\SourceControl::BITBUCKET,
|
||||
];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
36
package-lock.json
generated
36
package-lock.json
generated
@ -10,10 +10,10 @@
|
||||
"@tailwindcss/typography": "^0.5.9",
|
||||
"alpinejs": "^3.4.2",
|
||||
"autoprefixer": "^10.4.2",
|
||||
"axios": "^1.1.2",
|
||||
"axios": "^1.6.0",
|
||||
"laravel-echo": "^1.15.0",
|
||||
"laravel-vite-plugin": "^0.7.2",
|
||||
"postcss": "^8.4.6",
|
||||
"postcss": "^8.4.31",
|
||||
"pusher-js": "^4.3.1",
|
||||
"tailwindcss": "^3.1.0",
|
||||
"toastr": "^2.1.4",
|
||||
@ -596,9 +596,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/axios": {
|
||||
"version": "1.3.5",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.3.5.tgz",
|
||||
"integrity": "sha512-glL/PvG/E+xCWwV8S6nCHcrfg1exGx7vxyUIivIA1iL7BIh6bePylCfVHwp6k13ao7SATxB6imau2kqY+I67kw==",
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.6.0.tgz",
|
||||
"integrity": "sha512-EZ1DYihju9pwVB+jg67ogm+Tmqc6JmhamRN6I4Zt8DfZu5lbcQGw3ozH9lFejSJgs/ibaef3A9PMXPLeefFGJg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"follow-redirects": "^1.15.0",
|
||||
@ -1380,9 +1380,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/postcss": {
|
||||
"version": "8.4.21",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.21.tgz",
|
||||
"integrity": "sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==",
|
||||
"version": "8.4.31",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
|
||||
"integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
@ -1392,10 +1392,14 @@
|
||||
{
|
||||
"type": "tidelift",
|
||||
"url": "https://tidelift.com/funding/github/npm/postcss"
|
||||
},
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ai"
|
||||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"nanoid": "^3.3.4",
|
||||
"nanoid": "^3.3.6",
|
||||
"picocolors": "^1.0.0",
|
||||
"source-map-js": "^1.0.2"
|
||||
},
|
||||
@ -2284,9 +2288,9 @@
|
||||
}
|
||||
},
|
||||
"axios": {
|
||||
"version": "1.3.5",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.3.5.tgz",
|
||||
"integrity": "sha512-glL/PvG/E+xCWwV8S6nCHcrfg1exGx7vxyUIivIA1iL7BIh6bePylCfVHwp6k13ao7SATxB6imau2kqY+I67kw==",
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.6.0.tgz",
|
||||
"integrity": "sha512-EZ1DYihju9pwVB+jg67ogm+Tmqc6JmhamRN6I4Zt8DfZu5lbcQGw3ozH9lFejSJgs/ibaef3A9PMXPLeefFGJg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"follow-redirects": "^1.15.0",
|
||||
@ -2852,12 +2856,12 @@
|
||||
"dev": true
|
||||
},
|
||||
"postcss": {
|
||||
"version": "8.4.21",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.21.tgz",
|
||||
"integrity": "sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==",
|
||||
"version": "8.4.31",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
|
||||
"integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"nanoid": "^3.3.4",
|
||||
"nanoid": "^3.3.6",
|
||||
"picocolors": "^1.0.0",
|
||||
"source-map-js": "^1.0.2"
|
||||
}
|
||||
|
@ -12,10 +12,10 @@
|
||||
"@tailwindcss/typography": "^0.5.9",
|
||||
"alpinejs": "^3.4.2",
|
||||
"autoprefixer": "^10.4.2",
|
||||
"axios": "^1.1.2",
|
||||
"axios": "^1.6.0",
|
||||
"laravel-echo": "^1.15.0",
|
||||
"laravel-vite-plugin": "^0.7.2",
|
||||
"postcss": "^8.4.6",
|
||||
"postcss": "^8.4.31",
|
||||
"pusher-js": "^4.3.1",
|
||||
"tailwindcss": "^3.1.0",
|
||||
"toastr": "^2.1.4",
|
||||
|
@ -4,6 +4,9 @@
|
||||
<testsuite name="Feature">
|
||||
<directory suffix="Test.php">./tests/Feature</directory>
|
||||
</testsuite>
|
||||
<testsuite name="Unit">
|
||||
<directory suffix="Test.php">./tests/Unit</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
<coverage/>
|
||||
<php>
|
||||
|
1
public/build/assets/app-328222da.css
Normal file
1
public/build/assets/app-328222da.css
Normal file
File diff suppressed because one or more lines are too long
24
public/build/assets/app-9aa488bb.js
Normal file
24
public/build/assets/app-9aa488bb.js
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1,11 +1,11 @@
|
||||
{
|
||||
"resources/css/app.css": {
|
||||
"file": "assets/app-bdf134de.css",
|
||||
"file": "assets/app-328222da.css",
|
||||
"isEntry": true,
|
||||
"src": "resources/css/app.css"
|
||||
},
|
||||
"resources/js/app.js": {
|
||||
"file": "assets/app-fa1f93fa.js",
|
||||
"file": "assets/app-9aa488bb.js",
|
||||
"isEntry": true,
|
||||
"src": "resources/js/app.js"
|
||||
}
|
||||
|
43
public/static/images/phpmyadmin.svg
Normal file
43
public/static/images/phpmyadmin.svg
Normal file
@ -0,0 +1,43 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" version="1.1" width="3890" height="2168" id="svg2">
|
||||
<metadata id="metadata8">
|
||||
<rdf:RDF>
|
||||
<cc:Work rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
|
||||
<dc:title/>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs id="defs6"/>
|
||||
<g id="g5" style="fill:#cccccc">
|
||||
<path d="m 2889.39,6.3480122 -2.04,-4.07 c -1.01,-1.01 -2.03,-2.04 -4.06,-2.04 l -4.08,1.03 c -2.03,2.03 -3.05,3.05 -3.05,5.08 L 2789.64,1572.695 l 13.24,-2.035 83.46,-1523.70199 99.75,163.88 -1.02,0 c 75.32,221.88 106.87,458.02 94.66,708.41003 l 6.11,9.16 98.73,175.05996 6.1,6.11 c 151.66,133.336 321.63,222.907 509.94,268.707 l 45.8,74.309 6.11,2.031 1.02,-2.031 -866.19,-1416.82599 2.04,-29.52" id="path14"/>
|
||||
<path d="m 2858.86,559.02801 c 106.87,218.84 145.54,549.62999 117.05,992.39199 l 416.29,-50.894 z" id="path18"/>
|
||||
<path d="m 3807.48,1520.881 c 42.74,-5.086 70.22,-14.246 82.44,-27.476 l -2178.16,265.656 -1.02,0 c 1.02,90.586 41.73,161.836 121.12,212.723 21.37,15.269 43.77,26.465 64.12,33.59 19.34,-22.387 40.72,-38.676 66.17,-53.946 l 1.01,0 c 228,-138.422 566.94,-159.8 1014.78,-65.136 l 5.09,1.011 c 48.86,10.18 97.71,22.399 143.52,36.645 13.22,2.035 24.42,-2.035 33.58,-10.18 16.29,-12.215 36.65,-21.375 64.13,-27.476 l 0,-1.02 c 72.27,-128.25 169.97,-222.906 292.12,-284.996 84.48,-41.727 182.19,-69.215 291.1,-79.395" id="path20"/>
|
||||
<path d="M 2761.15,1560.585 2860.89,31.798012 C 2716.36,589.56801 2484.29,1110.698 2165.71,1594.167 l 595.44,-33.582" id="path22"/>
|
||||
<path d="m 2200.32,329.008 -4.07,-2.04 -4.07,1.02 -2.04,5.08 -82.44,1323.18799 12.21,-1.019 82.45,-1322.169 -2.04,-4.06" id="path24"/>
|
||||
<path d="m 2084.29,1627.756 0,0 87.53,-1290.608 -461.08,1330.308 373.55,-39.7" id="path26"/>
|
||||
</g>
|
||||
<g id="g13" style="fill:none;stroke:#cccccc;stroke-width:12;stroke-linecap:round;stroke-linejoin:round">
|
||||
<path d="m 2102.61,2057.9534 c -115.02,54.9606 -198.48,64.1207 -251.41,30.5387 -52.93,-35.6247 -134.36,-27.4847 -244.28,24.4219" id="path34"/>
|
||||
<path d="m 2372.33,2066.093 c -21.37,-7.125 -44.78,-12.211 -69.21,-14.25 -49.87,-4.066 -109.93,15.27 -180.16,57.0042 -70.23,42.746 -154.71,59.0351 -255.48,49.8711" id="path36"/>
|
||||
<path d="m 3000.34,1962.273 c -484.5,-99.742 -833.61,-76.336 -1047.35,71.25" id="path38"/>
|
||||
<path d="m 3305.69,2010.117 c -21.38,-8.145 -44.79,-13.235 -69.22,-14.254 -49.87,-4.067 -109.92,15.269 -180.15,57.004 -70.23,41.7262 -154.71,58.0114 -255.48,49.8708" id="path40"/>
|
||||
<path d="m 2655.29,341.21801 c -49.88,-30.54 -101.78,-22.39 -155.73,25.44 -46.82,-47.83 -95.67,-55.98 -146.57,-25.44" id="path42" style="stroke-width:29"/>
|
||||
<path d="m 2542.31,170.21801 c -50.89,-30.53 -100.76,-18.32 -151.66,36.64 -49.88,-54.96 -100.76,-67.17 -151.66,-36.64" id="path44" style="stroke-width:29"/>
|
||||
</g>
|
||||
<g style="fill:#6c78af" id="g33">
|
||||
<path d="m 56.770404,1763.8603 134.914516,0 c 40.53785,0 70.3051,11.5406 89.30315,34.6149 18.99058,23.0812 25.15303,55.2422 18.48477,96.4878 -2.73388,16.9207 -7.61168,32.4501 -14.63698,46.5933 -7.02825,14.1442 -16.30891,27.067 -27.84598,38.7573 -13.71822,14.0684 -29.07355,24.148 -46.07783,30.2307 -17.00335,6.0818 -38.74881,9.1232 -65.23042,9.1232 l -60.093245,0 -14.951878,92.5154 -70.16450702,0 56.29840402,-348.3226 z m 61.252276,55.1047 -23.613063,146.1033 42.675303,0 c 28.28742,0 49.25184,-5.7744 62.90101,-17.3383 13.63797,-11.5649 22.6337,-30.807 26.98842,-57.7355 4.19709,-25.968 1.75658,-44.2941 -7.2996,-54.9935 -9.06329,-10.6883 -26.92099,-16.036 -53.57751,-16.036 l -48.07456,0" id="path66"/>
|
||||
<path d="m 378.24196,1671.3451 69.63516,0 -14.95189,92.5152 61.94982,0 c 38.98987,0 66.01185,7.3901 81.07593,22.1571 15.06891,14.7762 19.95224,38.5804 14.64264,71.4329 l -26.22037,162.2172 -70.68973,0 24.9538,-154.3893 c 2.84147,-17.5516 1.46456,-29.4876 -4.12427,-35.815 -5.58913,-6.3193 -17.28356,-9.4871 -35.06873,-9.4871 l -55.57891,0 -32.27423,199.6914 -69.64729,0 56.29807,-348.3224" id="path68"/>
|
||||
<path d="m 666.43774,1763.8603 134.9135,0 c 40.54695,0 70.30511,11.5406 89.30319,34.6149 18.99454,23.0812 25.15404,55.2422 18.4898,96.4878 -2.7339,16.9207 -7.61577,32.4501 -14.63297,46.5933 -7.03329,14.1442 -16.32202,27.067 -27.85103,38.7573 -13.72229,14.0684 -29.07657,24.148 -46.08084,30.2307 -17.00032,6.0818 -38.74478,9.1232 -65.23548,9.1232 l -60.08435,0 -14.95188,92.5154 -70.16093,0 56.29099,-348.3226 z m 61.26036,55.1047 -23.61306,146.1033 42.6753,0 c 28.28641,0 49.2478,-5.7744 62.88888,-17.3383 13.641,-11.5649 22.64179,-30.807 26.99549,-57.7355 4.19406,-25.968 1.7576,-44.2941 -7.30364,-54.9935 -9.06329,-10.6883 -26.92099,-16.036 -53.56841,-16.036 l -48.07456,0" id="path70"/>
|
||||
</g>
|
||||
<g style="fill:#f89c0e" id="g65">
|
||||
<path d="m 1027.7488,1597.3243 134.9473,0 59.2813,323.8687 163.9729,-323.8687 134.4328,0 -68.203,422.055 -90.0955,0 60.5624,-328.7447 -165.335,328.7447 -97.63,0 -62.0922,-332.1897 -44.4691,332.1897 -93.58345,0 68.21155,-422.055" id="path72"/>
|
||||
<path d="m 1682.3349,1951.0787 68.129,0 39.0484,-241.6556 84.6928,0 -48.7474,301.634 c -6.6176,41.0085 -21.4426,71.3829 -44.4508,91.1237 -23.0089,19.7312 -54.9295,29.6038 -95.7507,29.6038 l -166.0776,0 10.1515,-62.7878 151.4988,0 c 16.2574,0 29.3646,-3.7991 39.3223,-11.39 9.9443,-7.6043 16.0994,-18.6108 18.427,-33.0342 l 0.8443,-5.1933 -74.9653,0 c -47.9513,0 -80.6531,-9.1406 -98.0934,-27.4219 -17.4525,-18.2801 -22.7167,-48.7649 -15.8297,-91.453 l 30.891,-191.0813 83.7372,0 -29.8633,184.7645 c -3.7745,23.3729 -2.642,38.6968 3.4343,45.9753 6.0641,7.2784 20.5937,10.9158 43.6016,10.9158" id="path74"/>
|
||||
<path d="m 2102.3044,1597.3243 97.5197,0 118.1874,422.055 -102.2734,0 -23.907,-100.4601 -189.9554,0 -55.1868,100.4601 -97.5321,0 253.1476,-422.055 z m 31.6507,83.8058 -90.1808,162.444 130.2183,0 -40.0375,-162.444" id="path76"/>
|
||||
<path d="m 2637.3031,2019.3793 -162.6964,0 c -49.1642,0 -85.2663,-13.9786 -108.3193,-41.9409 -23.0404,-27.9573 -30.5053,-66.9321 -22.4309,-116.9135 3.316,-20.4913 9.2287,-39.3092 17.7509,-56.4463 8.5217,-17.1419 19.771,-32.7965 33.7592,-46.9712 16.6286,-17.0415 35.3095,-29.2498 56.0305,-36.6239 20.7209,-7.3752 47.0449,-11.0604 78.9594,-11.0604 l 72.6621,0 18.1132,-112.0988 84.3865,0 -68.2152,422.055 z m -73.6376,-66.4629 28.5697,-176.7241 -51.2224,0 c -34.3524,0 -59.8397,6.9489 -76.4874,20.8321 -16.6223,13.8929 -27.5723,37.0269 -32.8017,69.397 -5.0795,31.4221 -2.0777,53.6898 9.0062,66.8122 11.0837,13.1222 32.7053,19.6828 64.8648,19.6828 l 58.0708,0" id="path78"/>
|
||||
<path d="m 2761.0228,1709.4231 325.2334,0 c 47.155,0 79.7016,8.9067 97.6259,26.6992 17.9129,17.8022 23.6511,46.7028 17.1903,86.702 l -31.7718,196.555 -85.3299,0 30.0434,-185.9259 c 3.6903,-22.7947 2.5585,-37.8305 -3.3836,-45.1151 -5.966,-7.2785 -20.0182,-10.9208 -42.1807,-10.9208 l -47.5103,0 -39.1073,241.9618 -86.604,0 39.1073,-241.9618 -98.9777,0 -39.1073,241.9618 -85.3298,0 50.1021,-309.9562" id="path80"/>
|
||||
<path d="m 3389.6515,1674.2008 -88.8214,0 12.4228,-76.8765 88.8214,0 -12.4228,76.8765 z m -55.7924,345.1785 -88.8215,0 50.0899,-309.9562 88.8214,0 -50.0898,309.9562" id="path82"/>
|
||||
<path d="m 3457.2112,1709.4231 159.3518,0 c 48.1596,0 81.1348,8.7634 98.9115,26.2691 17.7775,17.5156 23.3935,46.5596 16.8359,87.1321 l -31.7597,196.555 -84.705,0 30.1524,-186.5005 c 3.7421,-23.1817 2.4511,-38.2189 -3.8732,-45.1102 -6.325,-6.9 -20.6913,-10.3511 -43.0866,-10.3511 l -68.1168,0 -39.1072,241.9618 -84.6928,0 50.0897,-309.9562" id="path84"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 7.6 KiB |
1
resources/commands/storage/download-from-ftp.sh
Normal file
1
resources/commands/storage/download-from-ftp.sh
Normal file
@ -0,0 +1 @@
|
||||
curl __passive__ -u "__username__:__password__" ftp__ssl__://__host__:__port__/__src__ -o "__dest__"
|
1
resources/commands/storage/upload-to-ftp.sh
Normal file
1
resources/commands/storage/upload-to-ftp.sh
Normal file
@ -0,0 +1 @@
|
||||
curl __passive__ -T "__src__" -u "__username__:__password__" ftp__ssl__://__host__:__port__/__dest__
|
@ -2,6 +2,4 @@ if ! cd __path__; then
|
||||
echo 'VITO_SSH_ERROR' && exit 1
|
||||
fi
|
||||
|
||||
if ! __script__; then
|
||||
echo 'VITO_SSH_ERROR' && exit 1
|
||||
fi
|
||||
__script__
|
||||
|
@ -1,5 +1,5 @@
|
||||
server {
|
||||
listen 54331;
|
||||
listen __port__;
|
||||
server_name _;
|
||||
root /home/vito/phpmyadmin;
|
||||
|
||||
|
@ -4,44 +4,57 @@
|
||||
|
||||
<form method="POST" action="{{ route('login') }}">
|
||||
@csrf
|
||||
<div x-data="{ isPasswordVisible: false }">
|
||||
<!-- Email Address -->
|
||||
<div>
|
||||
<x-input-label for="email" :value="__('Email')" />
|
||||
<x-text-input id="email" class="block mt-1 w-full" type="email" name="email" :value="old('email')"
|
||||
required autofocus autocomplete="username" />
|
||||
<x-input-error :messages="$errors->get('email')" class="mt-2" />
|
||||
</div>
|
||||
|
||||
<!-- Email Address -->
|
||||
<div>
|
||||
<x-input-label for="email" :value="__('Email')" />
|
||||
<x-text-input id="email" class="block mt-1 w-full" type="email" name="email" :value="old('email')" required autofocus autocomplete="username" />
|
||||
<x-input-error :messages="$errors->get('email')" class="mt-2" />
|
||||
</div>
|
||||
<!-- Password -->
|
||||
<div class="mt-4">
|
||||
<x-input-label for="password" :value="__('Password')" />
|
||||
<x-text-input id="password" class="block mt-1 w-full"
|
||||
x-bind:type="isPasswordVisible ? 'text' : 'password'" name="password" required
|
||||
autocomplete="current-password" />
|
||||
<x-input-error :messages="$errors->get('password')" class="mt-2" />
|
||||
</div>
|
||||
|
||||
<!-- Password -->
|
||||
<div class="mt-4">
|
||||
<x-input-label for="password" :value="__('Password')" />
|
||||
<div class="flex items-center justify-between">
|
||||
<!-- Remember Me -->
|
||||
<div class="block mt-4 ">
|
||||
<label for="remember_me" class="inline-flex items-center">
|
||||
<input id="remember_me" type="checkbox"
|
||||
class="rounded dark:bg-gray-900 border-gray-300 dark:border-gray-700 text-indigo-600 shadow-sm focus:ring-indigo-500 dark:focus:ring-indigo-600 dark:focus:ring-offset-gray-800"
|
||||
name="remember">
|
||||
<span class="ml-2 text-sm text-gray-600 dark:text-gray-400">{{ __('Remember me') }}</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<x-text-input id="password" class="block mt-1 w-full"
|
||||
type="password"
|
||||
name="password"
|
||||
required autocomplete="current-password" />
|
||||
<!-- Show Password -->
|
||||
<div class="block mt-4">
|
||||
<label for="show_password" class="inline-flex items-center float-right">
|
||||
<input id="show_password" type="checkbox" x-model="isPasswordVisible"
|
||||
class="rounded dark:bg-gray-900 border-gray-300 dark:border-gray-700 text-indigo-600 shadow-sm focus:ring-indigo-500 dark:focus:ring-indigo-600 dark:focus:ring-offset-gray-800">
|
||||
<span class="ml-2 text-sm text-gray-600 dark:text-gray-400">{{ __('Show password') }}</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<x-input-error :messages="$errors->get('password')" class="mt-2" />
|
||||
</div>
|
||||
<div class="flex items-center justify-end mt-4">
|
||||
@if (Route::has('password.request'))
|
||||
<a class="underline text-sm text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-100 rounded-md focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 dark:focus:ring-offset-gray-800"
|
||||
href="{{ route('password.request') }}">
|
||||
{{ __('Forgot your password?') }}
|
||||
</a>
|
||||
@endif
|
||||
|
||||
<!-- Remember Me -->
|
||||
<div class="block mt-4">
|
||||
<label for="remember_me" class="inline-flex items-center">
|
||||
<input id="remember_me" type="checkbox" class="rounded dark:bg-gray-900 border-gray-300 dark:border-gray-700 text-indigo-600 shadow-sm focus:ring-indigo-500 dark:focus:ring-indigo-600 dark:focus:ring-offset-gray-800" name="remember">
|
||||
<span class="ml-2 text-sm text-gray-600 dark:text-gray-400">{{ __('Remember me') }}</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-end mt-4">
|
||||
@if (Route::has('password.request'))
|
||||
<a class="underline text-sm text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-100 rounded-md focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 dark:focus:ring-offset-gray-800" href="{{ route('password.request') }}">
|
||||
{{ __('Forgot your password?') }}
|
||||
</a>
|
||||
@endif
|
||||
|
||||
<x-primary-button class="ml-3">
|
||||
{{ __('Log in') }}
|
||||
</x-primary-button>
|
||||
<x-primary-button class="ml-3">
|
||||
{{ __('Log in') }}
|
||||
</x-primary-button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</x-guest-layout>
|
||||
|
1
resources/views/components/input-help.blade.php
Normal file
1
resources/views/components/input-help.blade.php
Normal file
@ -0,0 +1 @@
|
||||
<p {{ $attributes->merge(['class' => 'mt-2 text-sm text-gray-500 dark:text-gray-300']) }}>{{ $slot }}</p>
|
@ -1,13 +1,14 @@
|
||||
@props(['server'])
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="csrf-token" content="{{ csrf_token() }}">
|
||||
|
||||
<title>
|
||||
@if(isset($pageTitle))
|
||||
@if (isset($pageTitle))
|
||||
{{ $pageTitle }} -
|
||||
@endif
|
||||
{{ config('app.name', 'Laravel') }}
|
||||
@ -29,88 +30,115 @@
|
||||
|
||||
@include('layouts.partials.favicon')
|
||||
</head>
|
||||
<body class="font-sans antialiased bg-gray-100 dark:bg-gray-900 dark:text-gray-300 min-h-screen min-w-max" x-data="" x-cloak>
|
||||
|
||||
<body class="font-sans antialiased bg-gray-100 dark:bg-gray-900 dark:text-gray-300 min-h-screen min-w-max"
|
||||
x-data="" x-cloak>
|
||||
<div class="flex min-h-screen">
|
||||
<div class="left-0 top-0 min-h-screen w-64 flex-none bg-gray-800 dark:bg-gray-800/50 p-3 dark:border-r-2 dark:border-gray-800">
|
||||
<div
|
||||
class="left-0 top-0 min-h-screen w-64 flex-none bg-gray-800 dark:bg-gray-800/50 p-3 dark:border-r-2 dark:border-gray-800">
|
||||
<div class="h-16 block">
|
||||
<div class="flex items-center justify-start text-3xl font-extrabold text-white">
|
||||
Vito
|
||||
<x-application-logo class="w-10 h-10 rounded-md"/>
|
||||
<span class="ml-1">Deploy</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-5 space-y-2">
|
||||
@include('layouts.partials.server-select', ['server' => isset($server) ? $server : null ])
|
||||
@include('layouts.partials.server-select', ['server' => isset($server) ? $server : null])
|
||||
|
||||
@if(isset($server))
|
||||
@if (isset($server))
|
||||
<x-sidebar-link :href="route('servers.show', ['server' => $server])" :active="request()->routeIs('servers.show')">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M2.25 12l8.954-8.955c.44-.439 1.152-.439 1.591 0L21.75 12M4.5 9.75v10.125c0 .621.504 1.125 1.125 1.125H9.75v-4.875c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125V21h4.125c.621 0 1.125-.504 1.125-1.125V9.75M8.25 21h8.25" />
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
|
||||
stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round"
|
||||
d="M2.25 12l8.954-8.955c.44-.439 1.152-.439 1.591 0L21.75 12M4.5 9.75v10.125c0 .621.504 1.125 1.125 1.125H9.75v-4.875c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125V21h4.125c.621 0 1.125-.504 1.125-1.125V9.75M8.25 21h8.25" />
|
||||
</svg>
|
||||
<span class="ml-2 text-gray-50">{{ __('Overview') }}</span>
|
||||
</x-sidebar-link>
|
||||
@if($server->isReady())
|
||||
@if($server->webserver())
|
||||
@if ($server->isReady())
|
||||
@if ($server->webserver())
|
||||
<x-sidebar-link :href="route('servers.sites', ['server' => $server])" :active="request()->routeIs('servers.sites') || request()->is('servers/*/sites/*')">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M12 21a9.004 9.004 0 008.716-6.747M12 21a9.004 9.004 0 01-8.716-6.747M12 21c2.485 0 4.5-4.03 4.5-9S14.485 3 12 3m0 18c-2.485 0-4.5-4.03-4.5-9S9.515 3 12 3m0 0a8.997 8.997 0 017.843 4.582M12 3a8.997 8.997 0 00-7.843 4.582m15.686 0A11.953 11.953 0 0112 10.5c-2.998 0-5.74-1.1-7.843-2.918m15.686 0A8.959 8.959 0 0121 12c0 .778-.099 1.533-.284 2.253m0 0A17.919 17.919 0 0112 16.5c-3.162 0-6.133-.815-8.716-2.247m0 0A9.015 9.015 0 013 12c0-1.605.42-3.113 1.157-4.418" />
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
|
||||
stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round"
|
||||
d="M12 21a9.004 9.004 0 008.716-6.747M12 21a9.004 9.004 0 01-8.716-6.747M12 21c2.485 0 4.5-4.03 4.5-9S14.485 3 12 3m0 18c-2.485 0-4.5-4.03-4.5-9S9.515 3 12 3m0 0a8.997 8.997 0 017.843 4.582M12 3a8.997 8.997 0 00-7.843 4.582m15.686 0A11.953 11.953 0 0112 10.5c-2.998 0-5.74-1.1-7.843-2.918m15.686 0A8.959 8.959 0 0121 12c0 .778-.099 1.533-.284 2.253m0 0A17.919 17.919 0 0112 16.5c-3.162 0-6.133-.815-8.716-2.247m0 0A9.015 9.015 0 013 12c0-1.605.42-3.113 1.157-4.418" />
|
||||
</svg>
|
||||
<span class="ml-2 text-gray-50">{{ __('Sites') }}</span>
|
||||
</x-sidebar-link>
|
||||
@endif
|
||||
@if($server->database())
|
||||
<x-sidebar-link :href="route('servers.databases', ['server' => $server])" :active="request()->routeIs('servers.databases') || request()->routeIs('servers.databases.backups')">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M20.25 6.375c0 2.278-3.694 4.125-8.25 4.125S3.75 8.653 3.75 6.375m16.5 0c0-2.278-3.694-4.125-8.25-4.125S3.75 4.097 3.75 6.375m16.5 0v11.25c0 2.278-3.694 4.125-8.25 4.125s-8.25-1.847-8.25-4.125V6.375m16.5 0v3.75m-16.5-3.75v3.75m16.5 0v3.75C20.25 16.153 16.556 18 12 18s-8.25-1.847-8.25-4.125v-3.75m16.5 0c0 2.278-3.694 4.125-8.25 4.125s-8.25-1.847-8.25-4.125" />
|
||||
@if ($server->database())
|
||||
<x-sidebar-link :href="route('servers.databases', ['server' => $server])" :active="request()->routeIs('servers.databases') ||
|
||||
request()->routeIs('servers.databases.backups')">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
|
||||
stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round"
|
||||
d="M20.25 6.375c0 2.278-3.694 4.125-8.25 4.125S3.75 8.653 3.75 6.375m16.5 0c0-2.278-3.694-4.125-8.25-4.125S3.75 4.097 3.75 6.375m16.5 0v11.25c0 2.278-3.694 4.125-8.25 4.125s-8.25-1.847-8.25-4.125V6.375m16.5 0v3.75m-16.5-3.75v3.75m16.5 0v3.75C20.25 16.153 16.556 18 12 18s-8.25-1.847-8.25-4.125v-3.75m16.5 0c0 2.278-3.694 4.125-8.25 4.125s-8.25-1.847-8.25-4.125" />
|
||||
</svg>
|
||||
<span class="ml-2 text-gray-50">{{ __('Databases') }}</span>
|
||||
</x-sidebar-link>
|
||||
@endif
|
||||
@if($server->php())
|
||||
@if ($server->php())
|
||||
<x-sidebar-link :href="route('servers.php', ['server' => $server])" :active="request()->routeIs('servers.php')">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M17.25 6.75L22.5 12l-5.25 5.25m-10.5 0L1.5 12l5.25-5.25m7.5-3l-4.5 16.5" />
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
|
||||
stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round"
|
||||
d="M17.25 6.75L22.5 12l-5.25 5.25m-10.5 0L1.5 12l5.25-5.25m7.5-3l-4.5 16.5" />
|
||||
</svg>
|
||||
<span class="ml-2 text-gray-50">{{ __('PHP') }}</span>
|
||||
</x-sidebar-link>
|
||||
@endif
|
||||
@if($server->firewall())
|
||||
@if ($server->firewall())
|
||||
<x-sidebar-link :href="route('servers.firewall', ['server' => $server])" :active="request()->routeIs('servers.firewall')">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M15.362 5.214A8.252 8.252 0 0112 21 8.25 8.25 0 016.038 7.048 8.287 8.287 0 009 9.6a8.983 8.983 0 013.361-6.867 8.21 8.21 0 003 2.48z" />
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M12 18a3.75 3.75 0 00.495-7.467 5.99 5.99 0 00-1.925 3.546 5.974 5.974 0 01-2.133-1A3.75 3.75 0 0012 18z" />
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
|
||||
stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round"
|
||||
d="M15.362 5.214A8.252 8.252 0 0112 21 8.25 8.25 0 016.038 7.048 8.287 8.287 0 009 9.6a8.983 8.983 0 013.361-6.867 8.21 8.21 0 003 2.48z" />
|
||||
<path stroke-linecap="round" stroke-linejoin="round"
|
||||
d="M12 18a3.75 3.75 0 00.495-7.467 5.99 5.99 0 00-1.925 3.546 5.974 5.974 0 01-2.133-1A3.75 3.75 0 0012 18z" />
|
||||
</svg>
|
||||
<span class="ml-2 text-gray-50">{{ __('Firewall') }}</span>
|
||||
</x-sidebar-link>
|
||||
@endif
|
||||
<x-sidebar-link :href="route('servers.cronjobs', ['server' => $server])" :active="request()->routeIs('servers.cronjobs')">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M12 6v6h4.5m4.5 0a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
|
||||
stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round"
|
||||
d="M12 6v6h4.5m4.5 0a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
</svg>
|
||||
<span class="ml-2 text-gray-50">{{ __('Cronjobs') }}</span>
|
||||
</x-sidebar-link>
|
||||
<x-sidebar-link :href="route('servers.ssh-keys', ['server' => $server])" :active="request()->routeIs('servers.ssh-keys')">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M15.75 5.25a3 3 0 013 3m3 0a6 6 0 01-7.029 5.912c-.563-.097-1.159.026-1.563.43L10.5 17.25H8.25v2.25H6v2.25H2.25v-2.818c0-.597.237-1.17.659-1.591l6.499-6.499c.404-.404.527-1 .43-1.563A6 6 0 1121.75 8.25z" />
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
|
||||
stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round"
|
||||
d="M15.75 5.25a3 3 0 013 3m3 0a6 6 0 01-7.029 5.912c-.563-.097-1.159.026-1.563.43L10.5 17.25H8.25v2.25H6v2.25H2.25v-2.818c0-.597.237-1.17.659-1.591l6.499-6.499c.404-.404.527-1 .43-1.563A6 6 0 1121.75 8.25z" />
|
||||
</svg>
|
||||
<span class="ml-2 text-gray-50">{{ __('SSH Keys') }}</span>
|
||||
</x-sidebar-link>
|
||||
<x-sidebar-link :href="route('servers.services', ['server' => $server])" :active="request()->routeIs('servers.services')">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M9.594 3.94c.09-.542.56-.94 1.11-.94h2.593c.55 0 1.02.398 1.11.94l.213 1.281c.063.374.313.686.645.87.074.04.147.083.22.127.324.196.72.257 1.075.124l1.217-.456a1.125 1.125 0 011.37.49l1.296 2.247a1.125 1.125 0 01-.26 1.431l-1.003.827c-.293.24-.438.613-.431.992a6.759 6.759 0 010 .255c-.007.378.138.75.43.99l1.005.828c.424.35.534.954.26 1.43l-1.298 2.247a1.125 1.125 0 01-1.369.491l-1.217-.456c-.355-.133-.75-.072-1.076.124a6.57 6.57 0 01-.22.128c-.331.183-.581.495-.644.869l-.213 1.28c-.09.543-.56.941-1.11.941h-2.594c-.55 0-1.02-.398-1.11-.94l-.213-1.281c-.062-.374-.312-.686-.644-.87a6.52 6.52 0 01-.22-.127c-.325-.196-.72-.257-1.076-.124l-1.217.456a1.125 1.125 0 01-1.369-.49l-1.297-2.247a1.125 1.125 0 01.26-1.431l1.004-.827c.292-.24.437-.613.43-.992a6.932 6.932 0 010-.255c.007-.378-.138-.75-.43-.99l-1.004-.828a1.125 1.125 0 01-.26-1.43l1.297-2.247a1.125 1.125 0 011.37-.491l1.216.456c.356.133.751.072 1.076-.124.072-.044.146-.087.22-.128.332-.183.582-.495.644-.869l.214-1.281z" />
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
|
||||
stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round"
|
||||
d="M9.594 3.94c.09-.542.56-.94 1.11-.94h2.593c.55 0 1.02.398 1.11.94l.213 1.281c.063.374.313.686.645.87.074.04.147.083.22.127.324.196.72.257 1.075.124l1.217-.456a1.125 1.125 0 011.37.49l1.296 2.247a1.125 1.125 0 01-.26 1.431l-1.003.827c-.293.24-.438.613-.431.992a6.759 6.759 0 010 .255c-.007.378.138.75.43.99l1.005.828c.424.35.534.954.26 1.43l-1.298 2.247a1.125 1.125 0 01-1.369.491l-1.217-.456c-.355-.133-.75-.072-1.076.124a6.57 6.57 0 01-.22.128c-.331.183-.581.495-.644.869l-.213 1.28c-.09.543-.56.941-1.11.941h-2.594c-.55 0-1.02-.398-1.11-.94l-.213-1.281c-.062-.374-.312-.686-.644-.87a6.52 6.52 0 01-.22-.127c-.325-.196-.72-.257-1.076-.124l-1.217.456a1.125 1.125 0 01-1.369-.49l-1.297-2.247a1.125 1.125 0 01.26-1.431l1.004-.827c.292-.24.437-.613.43-.992a6.932 6.932 0 010-.255c.007-.378-.138-.75-.43-.99l-1.004-.828a1.125 1.125 0 01-.26-1.43l1.297-2.247a1.125 1.125 0 011.37-.491l1.216.456c.356.133.751.072 1.076-.124.072-.044.146-.087.22-.128.332-.183.582-.495.644-.869l.214-1.281z" />
|
||||
<path stroke-linecap="round" stroke-linejoin="round"
|
||||
d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
|
||||
</svg>
|
||||
<span class="ml-2 text-gray-50">{{ __('Services') }}</span>
|
||||
</x-sidebar-link>
|
||||
@endif
|
||||
<x-sidebar-link :href="route('servers.settings', ['server' => $server])" :active="request()->routeIs('servers.settings')">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M11.42 15.17L17.25 21A2.652 2.652 0 0021 17.25l-5.877-5.877M11.42 15.17l2.496-3.03c.317-.384.74-.626 1.208-.766M11.42 15.17l-4.655 5.653a2.548 2.548 0 11-3.586-3.586l6.837-5.63m5.108-.233c.55-.164 1.163-.188 1.743-.14a4.5 4.5 0 004.486-6.336l-3.276 3.277a3.004 3.004 0 01-2.25-2.25l3.276-3.276a4.5 4.5 0 00-6.336 4.486c.091 1.076-.071 2.264-.904 2.95l-.102.085m-1.745 1.437L5.909 7.5H4.5L2.25 3.75l1.5-1.5L7.5 4.5v1.409l4.26 4.26m-1.745 1.437l1.745-1.437m6.615 8.206L15.75 15.75M4.867 19.125h.008v.008h-.008v-.008z" />
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
|
||||
stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round"
|
||||
d="M11.42 15.17L17.25 21A2.652 2.652 0 0021 17.25l-5.877-5.877M11.42 15.17l2.496-3.03c.317-.384.74-.626 1.208-.766M11.42 15.17l-4.655 5.653a2.548 2.548 0 11-3.586-3.586l6.837-5.63m5.108-.233c.55-.164 1.163-.188 1.743-.14a4.5 4.5 0 004.486-6.336l-3.276 3.277a3.004 3.004 0 01-2.25-2.25l3.276-3.276a4.5 4.5 0 00-6.336 4.486c.091 1.076-.071 2.264-.904 2.95l-.102.085m-1.745 1.437L5.909 7.5H4.5L2.25 3.75l1.5-1.5L7.5 4.5v1.409l4.26 4.26m-1.745 1.437l1.745-1.437m6.615 8.206L15.75 15.75M4.867 19.125h.008v.008h-.008v-.008z" />
|
||||
</svg>
|
||||
<span class="ml-2 text-gray-50">{{ __('Settings') }}</span>
|
||||
</x-sidebar-link>
|
||||
<x-sidebar-link :href="route('servers.logs', ['server' => $server])" :active="request()->routeIs('servers.logs')">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M6.429 9.75L2.25 12l4.179 2.25m0-4.5l5.571 3 5.571-3m-11.142 0L2.25 7.5 12 2.25l9.75 5.25-4.179 2.25m0 0L21.75 12l-4.179 2.25m0 0l4.179 2.25L12 21.75 2.25 16.5l4.179-2.25m11.142 0l-5.571 3-5.571-3" />
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
|
||||
stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round"
|
||||
d="M6.429 9.75L2.25 12l4.179 2.25m0-4.5l5.571 3 5.571-3m-11.142 0L2.25 7.5 12 2.25l9.75 5.25-4.179 2.25m0 0L21.75 12l-4.179 2.25m0 0l4.179 2.25L12 21.75 2.25 16.5l4.179-2.25m11.142 0l-5.571 3-5.571-3" />
|
||||
</svg>
|
||||
<span class="ml-2 text-gray-50">{{ __('Logs') }}</span>
|
||||
</x-sidebar-link>
|
||||
@ -118,8 +146,9 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if(isset($sidebar))
|
||||
<div class="min-h-screen w-64 flex-none border-r border-gray-200 bg-white dark:border-gray-800 dark:bg-gray-900">
|
||||
@if (isset($sidebar))
|
||||
<div
|
||||
class="min-h-screen w-64 flex-none border-r border-gray-200 bg-white dark:border-gray-800 dark:bg-gray-900">
|
||||
{{ $sidebar }}
|
||||
</div>
|
||||
@endif
|
||||
@ -128,29 +157,35 @@
|
||||
<nav class="h-16 border-b border-gray-200 dark:border-gray-800 bg-white dark:bg-gray-900">
|
||||
<!-- Primary Navigation Menu -->
|
||||
<div class="mx-auto max-w-full px-4 sm:px-6 lg:px-8">
|
||||
<div class="flex h-16 justify-between">
|
||||
<div class="flex h-16 justify-end">
|
||||
<div class="flex items-center justify-center">
|
||||
{{-- Search --}}
|
||||
</div>
|
||||
|
||||
@include('layouts.partials.color-scheme')
|
||||
<div class="ml-6 flex items-center">
|
||||
<div class="relative ml-5">
|
||||
<x-dropdown align="right" width="48">
|
||||
<x-slot name="trigger">
|
||||
<div class="flex cursor-pointer items-center justify-between">
|
||||
{{ auth()->user()->name }}
|
||||
<svg class="ml-2 -mr-0.5 h-4 w-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor">
|
||||
<path fill-rule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clip-rule="evenodd" />
|
||||
<svg class="ml-2 -mr-0.5 h-4 w-4" xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 20 20" fill="currentColor">
|
||||
<path fill-rule="evenodd"
|
||||
d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"
|
||||
clip-rule="evenodd" />
|
||||
</svg>
|
||||
</div>
|
||||
</x-slot>
|
||||
|
||||
<x-slot name="content">
|
||||
<x-dropdown-link :href="route('profile')">Profile</x-dropdown-link>
|
||||
<div class="border-t border-gray-100 dark:border-gray-700"></div>
|
||||
|
||||
<div class="border-t border-gray-100 dark:border-gray-700"></div>
|
||||
<form method="POST" action="{{ route('logout') }}">
|
||||
@csrf
|
||||
<x-dropdown-link :href="route('logout')" onclick="event.preventDefault(); this.closest('form').submit();">
|
||||
<x-dropdown-link :href="route('logout')"
|
||||
onclick="event.preventDefault(); this.closest('form').submit();">
|
||||
{{ __('Log Out') }}
|
||||
</x-dropdown-link>
|
||||
</form>
|
||||
@ -163,7 +198,7 @@
|
||||
</nav>
|
||||
|
||||
<!-- Page Heading -->
|
||||
@if(isset($header))
|
||||
@if (isset($header))
|
||||
<header class="border-b border-gray-200 bg-white dark:border-gray-800 dark:bg-gray-900">
|
||||
<div class="mx-auto flex h-20 w-full max-w-full items-center justify-between px-8">
|
||||
{{ $header }}
|
||||
@ -171,7 +206,7 @@
|
||||
</header>
|
||||
@endif
|
||||
|
||||
@if(isset($header2))
|
||||
@if (isset($header2))
|
||||
<header class="border-b border-gray-200 bg-white dark:border-gray-800 dark:bg-gray-900">
|
||||
<div class="mx-auto max-w-full py-6 px-8">
|
||||
{{ $header2 }}
|
||||
@ -191,9 +226,20 @@
|
||||
<script>
|
||||
document.addEventListener('livewire:load', () => {
|
||||
Livewire.onPageExpired((response, message) => {
|
||||
({href: window.location.href} = window.location);
|
||||
({
|
||||
href: window.location.href
|
||||
} = window.location);
|
||||
})
|
||||
})
|
||||
|
||||
// On page load or when changing themes, best to add inline in `head` to avoid FOUC
|
||||
if (localStorage.theme === 'dark' || (!('theme' in localStorage) && window.matchMedia(
|
||||
'(prefers-color-scheme: dark)').matches)) {
|
||||
document.documentElement.classList.add('dark')
|
||||
} else {
|
||||
document.documentElement.classList.remove('dark')
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
@ -29,4 +29,5 @@
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
37
resources/views/layouts/partials/color-scheme.blade.php
Normal file
37
resources/views/layouts/partials/color-scheme.blade.php
Normal file
@ -0,0 +1,37 @@
|
||||
<div class="flex items-center" x-data="{
|
||||
isDarkMode: localStorage.theme,
|
||||
toggleTheme() {
|
||||
localStorage.theme = this.isDarkMode === 'dark' ? 'light' : 'dark';
|
||||
|
||||
if (localStorage.theme === 'dark') {
|
||||
document.documentElement.classList.add('dark')
|
||||
} else {
|
||||
document.documentElement.classList.remove('dark')
|
||||
}
|
||||
|
||||
this.isDarkMode = localStorage.theme
|
||||
}
|
||||
}" x-on:click="toggleTheme()">
|
||||
<div class="flex items-center">
|
||||
<div class="flex items-center justify-end">
|
||||
<button id="theme-toggle" type="button" class="text-sm p-2"
|
||||
:class="isDarkMode === 'dark' ? 'text-gray-300 border-gray-300' :
|
||||
'text-gray-800 border-gray-800'">
|
||||
<svg x-show="isDarkMode !== 'dark'" id="theme-toggle-dark-icon" class="w-5 h-5"
|
||||
fill="currentColor" viewBox="0 0 20 20"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M17.293 13.293A8 8 0 016.707 2.707a8.001 8.001 0 1010.586 10.586z">
|
||||
</path>
|
||||
</svg>
|
||||
<svg x-show="isDarkMode === 'dark'" id="theme-toggle-light-icon" class="w-5 h-5"
|
||||
fill="currentColor" viewBox="0 0 20 20"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M10 2a1 1 0 011 1v1a1 1 0 11-2 0V3a1 1 0 011-1zm4 8a4 4 0 11-8 0 4 4 0 018 0zm-.464 4.95l.707.707a1 1 0 001.414-1.414l-.707-.707a1 1 0 00-1.414 1.414zm2.12-10.607a1 1 0 010 1.414l-.706.707a1 1 0 11-1.414-1.414l.707-.707a1 1 0 011.414 0zM17 11a1 1 0 100-2h-1a1 1 0 100 2h1zm-7 4a1 1 0 011 1v1a1 1 0 11-2 0v-1a1 1 0 011-1zM5.05 6.464A1 1 0 106.465 5.05l-.708-.707a1 1 0 00-1.414 1.414l.707.707zm1.414 8.486l-.707.707a1 1 0 01-1.414-1.414l.707-.707a1 1 0 011.414 1.414zM4 11a1 1 0 100-2H3a1 1 0 000 2h1z"
|
||||
fillRule="evenodd" clipRule="evenodd"></path>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -1,3 +1,4 @@
|
||||
@php use App\Enums\SiteFeature; @endphp
|
||||
<x-app-layout :server="$site->server">
|
||||
@if(isset($pageTitle))
|
||||
<x-slot name="pageTitle">{{ $site->domain }} - {{ $pageTitle }}</x-slot>
|
||||
@ -58,10 +59,12 @@
|
||||
<x-secondary-sidebar-link :href="route('servers.sites.show', ['server' => $site->server, 'site' => $site])" :active="request()->routeIs('servers.sites.show')">
|
||||
{{ __('Application') }}
|
||||
</x-secondary-sidebar-link>
|
||||
@if($site->status == \App\Enums\SiteStatus::READY)
|
||||
@if($site->isReady() && $site->hasFeature(SiteFeature::SSL))
|
||||
<x-secondary-sidebar-link :href="route('servers.sites.ssl', ['server' => $site->server, 'site' => $site])" :active="request()->routeIs('servers.sites.ssl')">
|
||||
{{ __('SSL') }}
|
||||
</x-secondary-sidebar-link>
|
||||
@endif
|
||||
@if($site->isReady() && $site->hasFeature(SiteFeature::QUEUES))
|
||||
<x-secondary-sidebar-link :href="route('servers.sites.queues', ['server' => $site->server, 'site' => $site])" :active="request()->routeIs('servers.sites.queues')">
|
||||
{{ __('Queues') }}
|
||||
</x-secondary-sidebar-link>
|
||||
|
@ -0,0 +1,32 @@
|
||||
<div>
|
||||
@if($site->deploymentScript?->content)
|
||||
<x-dropdown>
|
||||
<x-slot name="trigger">
|
||||
<x-secondary-button>
|
||||
{{ __('Auto Deployment') }}
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-5 h-5 ml-1">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M8.25 15L12 18.75 15.75 15m-7.5-6L12 5.25 15.75 9" />
|
||||
</svg>
|
||||
</x-secondary-button>
|
||||
</x-slot>
|
||||
<x-slot name="content">
|
||||
<x-dropdown-link class="cursor-pointer" wire:click="enable">
|
||||
{{ __("Enable") }}
|
||||
@if($site->auto_deployment)
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-5 h-5 ml-1 text-green-600">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M9 12.75L11.25 15 15 9.75M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
</svg>
|
||||
@endif
|
||||
</x-dropdown-link>
|
||||
<x-dropdown-link class="cursor-pointer" wire:click="disable">
|
||||
{{ __("Disable") }}
|
||||
@if(!$site->auto_deployment)
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-5 h-5 ml-1 text-green-600">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M9 12.75L11.25 15 15 9.75M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
</svg>
|
||||
@endif
|
||||
</x-dropdown-link>
|
||||
</x-slot>
|
||||
</x-dropdown>
|
||||
@endif
|
||||
</div>
|
@ -1,5 +1,4 @@
|
||||
<div x-data="">
|
||||
<x-secondary-button x-on:click="$dispatch('open-modal', 'change-branch')">{{ __("Branch") }}</x-secondary-button>
|
||||
<x-modal name="change-branch">
|
||||
<form wire:submit.prevent="change" class="p-6">
|
||||
<h2 class="text-lg font-medium text-gray-900 dark:text-gray-100">
|
||||
|
@ -1,5 +1,4 @@
|
||||
<div x-data="">
|
||||
<x-secondary-button x-on:click="$dispatch('open-modal', 'deployment-script')">{{ __("Deployment Script") }}</x-secondary-button>
|
||||
<x-modal name="deployment-script">
|
||||
<form wire:submit.prevent="save" class="p-6">
|
||||
<h2 class="text-lg font-medium text-gray-900 dark:text-gray-100">
|
||||
|
@ -1,5 +1,4 @@
|
||||
<div x-data="">
|
||||
<x-secondary-button x-on:click="$dispatch('open-modal', 'update-env')">{{ __(".env") }}</x-secondary-button>
|
||||
<x-modal name="update-env">
|
||||
<form wire:submit.prevent="save" class="p-6">
|
||||
<h2 class="text-lg font-medium text-gray-900 dark:text-gray-100">
|
||||
|
@ -5,17 +5,29 @@
|
||||
<x-slot name="aside">
|
||||
<div class="flex items-center">
|
||||
<div class="mr-2">
|
||||
<livewire:application.change-branch :site="$site" />
|
||||
</div>
|
||||
<div class="mr-2">
|
||||
<livewire:application.deployment-script :site="$site" />
|
||||
</div>
|
||||
<div class="mr-2">
|
||||
<livewire:application.env :site="$site" />
|
||||
</div>
|
||||
<div>
|
||||
<livewire:application.deploy :site="$site" />
|
||||
</div>
|
||||
<div class="mr-2">
|
||||
<livewire:application.auto-deployment :site="$site" />
|
||||
</div>
|
||||
<x-dropdown>
|
||||
<x-slot name="trigger">
|
||||
<x-secondary-button>
|
||||
{{ __('Manage') }}
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-5 h-5 ml-1">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M8.25 15L12 18.75 15.75 15m-7.5-6L12 5.25 15.75 9" />
|
||||
</svg>
|
||||
</x-secondary-button>
|
||||
</x-slot>
|
||||
<x-slot name="content">
|
||||
<x-dropdown-link class="cursor-pointer" x-on:click="$dispatch('open-modal', 'change-branch')">{{ __("Branch") }}</x-dropdown-link>
|
||||
<x-dropdown-link class="cursor-pointer" x-on:click="$dispatch('open-modal', 'deployment-script')">{{ __("Deployment Script") }}</x-dropdown-link>
|
||||
<x-dropdown-link class="cursor-pointer" x-on:click="$dispatch('open-modal', 'update-env')">{{ __(".env") }}</x-dropdown-link>
|
||||
</x-slot>
|
||||
</x-dropdown>
|
||||
<livewire:application.change-branch :site="$site" />
|
||||
<livewire:application.deployment-script :site="$site" />
|
||||
<livewire:application.env :site="$site" />
|
||||
</div>
|
||||
</x-slot>
|
||||
</x-card-header>
|
||||
|
@ -1,3 +1,6 @@
|
||||
<div>
|
||||
|
||||
<x-simple-card class="flex items-center justify-between">
|
||||
<span>{{ __("Your Wordpress site is installed and ready to use! ") }}</span>
|
||||
<x-secondary-button :href="$site->url" target="_blank">{{ __("Open Website") }}</x-secondary-button>
|
||||
</x-simple-card>
|
||||
</div>
|
||||
|
@ -18,7 +18,7 @@
|
||||
<tr>
|
||||
<x-th>{{ __("Name") }}</x-th>
|
||||
<x-th>{{ __("Created") }}</x-th>
|
||||
<x-th>{{ __("Size") }}</x-th>
|
||||
{{--<x-th>{{ __("Size") }}</x-th>--}}
|
||||
<x-th>{{ __("Status") }}</x-th>
|
||||
<x-th>{{ __("Restored") }}</x-th>
|
||||
<x-th>{{ __("Restored To") }}</x-th>
|
||||
@ -30,7 +30,7 @@
|
||||
<x-td>
|
||||
<x-datetime :value="$file->created_at" />
|
||||
</x-td>
|
||||
<x-td>{{ $file->size }}</x-td>
|
||||
{{--<x-td>{{ $file->size }}</x-td>--}}
|
||||
<x-td>
|
||||
<div class="inline-flex">
|
||||
@include('livewire.databases.partials.backup-file-status', ['status' => $file->status])
|
||||
|
@ -28,9 +28,9 @@
|
||||
</x-secondary-button>
|
||||
</x-slot>
|
||||
<x-slot name="content">
|
||||
{{--<x-dropdown-link class="cursor-pointer">--}}
|
||||
{{-- {{ __("Install Extension") }}--}}
|
||||
{{--</x-dropdown-link>--}}
|
||||
<x-dropdown-link class="cursor-pointer" x-on:click="$wire.extensionId = {{ $php->id }}; $dispatch('open-modal', 'install-extension')">
|
||||
{{ __("Install Extension") }}
|
||||
</x-dropdown-link>
|
||||
<x-dropdown-link class="cursor-pointer" x-on:click="$dispatch('open-modal', 'update-php-ini')" wire:click="loadIni({{ $php->id }})">
|
||||
{{ __("Edit php.ini") }}
|
||||
</x-dropdown-link>
|
||||
@ -52,6 +52,7 @@
|
||||
</div>
|
||||
@include('livewire.php.partials.uninstall-php')
|
||||
@include('livewire.php.partials.update-php-ini')
|
||||
@include('livewire.php.partials.install-extension')
|
||||
@else
|
||||
<x-simple-card>
|
||||
<div class="text-center">
|
||||
|
@ -0,0 +1,36 @@
|
||||
<x-modal name="install-extension">
|
||||
<form wire:submit.prevent="installExtension" class="p-6">
|
||||
<h2 class="text-lg font-medium text-gray-900 dark:text-gray-100">
|
||||
{{ __('Install Extension') }}
|
||||
</h2>
|
||||
|
||||
<div class="mt-6">
|
||||
<x-input-label for="extension" value="Name" />
|
||||
<x-select-input wire:model.defer="extension" name="extension" class="mt-1 w-full">
|
||||
<option value="" selected>{{ __("Select") }}</option>
|
||||
@foreach(config('core.php_extensions') as $extension)
|
||||
<option value="{{ $extension }}" @if(in_array($extension, $installedExtensions)) disabled @endif>
|
||||
{{ $extension }} @if(in_array($extension, $installedExtensions)) ({{ __("Installed") }}) @endif
|
||||
</option>
|
||||
@endforeach
|
||||
</x-select-input>
|
||||
@error('name')
|
||||
<x-input-error class="mt-2" :messages="$message" />
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div class="mt-6 flex items-center justify-end">
|
||||
@if (session('status') === 'started-installation')
|
||||
<p class="mr-2">{{ __('Installation Started!') }}</p>
|
||||
@endif
|
||||
|
||||
<x-secondary-button type="button" x-on:click="$dispatch('close')">
|
||||
{{ __('Cancel') }}
|
||||
</x-secondary-button>
|
||||
|
||||
<x-primary-button class="ml-3">
|
||||
{{ __('Install') }}
|
||||
</x-primary-button>
|
||||
</div>
|
||||
</form>
|
||||
</x-modal>
|
@ -45,6 +45,18 @@
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<x-input-label for="timezone" :value="__('Timezone')" />
|
||||
<x-select-input wire:model.defer="timezone" id="timezone" name="timezone" class="mt-1 block w-full" required>
|
||||
@foreach(timezone_identifiers_list() as $timezone)
|
||||
<option value="{{ $timezone }}">{{ $timezone }}</option>
|
||||
@endforeach
|
||||
</x-select-input>
|
||||
@error('timezone')
|
||||
<x-input-error class="mt-2" :messages="$message" />
|
||||
@enderror
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<x-slot name="actions">
|
||||
|
@ -0,0 +1,33 @@
|
||||
<x-modal name="install-phpmyadmin">
|
||||
<form wire:submit.prevent="install" class="p-6">
|
||||
<h2 class="text-lg font-medium text-gray-900 dark:text-gray-100">
|
||||
{{ __('Install PHPMyAdmin') }}
|
||||
</h2>
|
||||
|
||||
<div class="mt-6">
|
||||
<x-input-label for="allowed_ip" :value="__('Allowed IP')" />
|
||||
<x-text-input wire:model.defer="allowed_ip" id="allowed_ip" name="allowed_ip" class="mt-1 w-full" />
|
||||
@error('allowed_ip')
|
||||
<x-input-error class="mt-2" :messages="$message" />
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div class="mt-6">
|
||||
<x-input-label for="port" :value="__('Port')" />
|
||||
<x-text-input wire:model.defer="port" id="port" name="port" class="mt-1 w-full" />
|
||||
@error('port')
|
||||
<x-input-error class="mt-2" :messages="$message" />
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div class="mt-6 flex justify-end">
|
||||
<x-secondary-button type="button" x-on:click="$dispatch('close')">
|
||||
{{ __('Cancel') }}
|
||||
</x-secondary-button>
|
||||
|
||||
<x-primary-button class="ml-3" @started.window="$dispatch('close')">
|
||||
{{ __('Install') }}
|
||||
</x-primary-button>
|
||||
</div>
|
||||
</form>
|
||||
</x-modal>
|
@ -1,11 +1,29 @@
|
||||
<div>
|
||||
<x-card-header>
|
||||
<x-slot name="title">{{ __("Services") }}</x-slot>
|
||||
<x-slot name="description">{{ __("All services that we installed on your server are here") }}</x-slot>
|
||||
<x-slot name="title">{{ __('Services') }}</x-slot>
|
||||
<x-slot name="description">{{ __('All services that we installed on your server are here') }}</x-slot>
|
||||
<x-slot name="aside">
|
||||
<x-dropdown>
|
||||
<x-slot name="trigger">
|
||||
<x-primary-button>
|
||||
{{ __('Install Service') }}
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4 ml-1">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M19.5 8.25l-7.5 7.5-7.5-7.5" />
|
||||
</svg>
|
||||
</x-primary-button>
|
||||
</x-slot>
|
||||
<x-slot name="content">
|
||||
<x-dropdown-link class="cursor-pointer" x-on:click="$dispatch('open-modal', 'install-phpmyadmin')">
|
||||
{{ __('PHPMyAdmin') }}
|
||||
</x-dropdown-link>
|
||||
</x-slot>
|
||||
</x-dropdown>
|
||||
<livewire:services.install-p-h-p-my-admin :server="$server" />
|
||||
</x-slot>
|
||||
</x-card-header>
|
||||
|
||||
<div class="space-y-3">
|
||||
@foreach($services as $service)
|
||||
@foreach ($services as $service)
|
||||
<x-item-card>
|
||||
<div class="flex-none">
|
||||
<img src="{{ asset('static/images/' . $service->name . '.svg') }}" class="h-10 w-10" alt="">
|
||||
@ -20,24 +38,30 @@
|
||||
<x-dropdown>
|
||||
<x-slot name="trigger">
|
||||
<x-secondary-button>
|
||||
{{ __("Actions") }}
|
||||
{{ __('Actions') }}
|
||||
</x-secondary-button>
|
||||
</x-slot>
|
||||
|
||||
<x-slot name="content">
|
||||
@if($service->status == \App\Enums\ServiceStatus::STOPPED)
|
||||
<x-dropdown-link class="cursor-pointer" wire:click="start({{ $service->id }})">
|
||||
{{ __("Start") }}
|
||||
@if($service->unit)
|
||||
@if ($service->status == \App\Enums\ServiceStatus::STOPPED)
|
||||
<x-dropdown-link class="cursor-pointer" wire:click="start({{ $service->id }})">
|
||||
{{ __('Start') }}
|
||||
</x-dropdown-link>
|
||||
@endif
|
||||
@if ($service->status == \App\Enums\ServiceStatus::READY)
|
||||
<x-dropdown-link class="cursor-pointer" wire:click="stop({{ $service->id }})">
|
||||
{{ __('Stop') }}
|
||||
</x-dropdown-link>
|
||||
@endif
|
||||
<x-dropdown-link class="cursor-pointer" wire:click="restart({{ $service->id }})">
|
||||
{{ __('Restart') }}
|
||||
</x-dropdown-link>
|
||||
@else
|
||||
<x-dropdown-link class="cursor-pointer" wire:click="uninstall({{ $service->id }})">
|
||||
{{ __('Uninstall') }}
|
||||
</x-dropdown-link>
|
||||
@endif
|
||||
@if($service->status == \App\Enums\ServiceStatus::READY)
|
||||
<x-dropdown-link class="cursor-pointer" wire:click="stop({{ $service->id }})">
|
||||
{{ __("Stop") }}
|
||||
</x-dropdown-link>
|
||||
@endif
|
||||
<x-dropdown-link class="cursor-pointer" wire:click="restart({{ $service->id }})">
|
||||
{{ __("Restart") }}
|
||||
</x-dropdown-link>
|
||||
</x-slot>
|
||||
</x-dropdown>
|
||||
</div>
|
||||
|
@ -5,10 +5,10 @@
|
||||
<form id="create-site" wire:submit.prevent="create" class="space-y-6">
|
||||
<div>
|
||||
<x-input-label>{{ __("Select site type") }}</x-input-label>
|
||||
<x-select-input wire:model="type" id="type" name="type" class="mt-1 w-full">
|
||||
<x-select-input wire:model="inputs.type" id="type" name="type" class="mt-1 w-full">
|
||||
<option value="" selected disabled>{{ __("Select") }}</option>
|
||||
@foreach(config('core.site_types') as $t)
|
||||
<option value="{{ $t }}" @if($t === $type) selected @endif>
|
||||
<option value="{{ $t }}" @if($t === $inputs['type']) selected @endif>
|
||||
{{ $t }}
|
||||
</option>
|
||||
@endforeach
|
||||
@ -20,7 +20,7 @@
|
||||
|
||||
<div>
|
||||
<x-input-label for="domain" :value="__('Domain')" />
|
||||
<x-text-input wire:model.defer="domain" id="domain" name="domain" type="text" class="mt-1 block w-full" autocomplete="domain" placeholder="example.com" />
|
||||
<x-text-input wire:model.defer="inputs.domain" id="domain" name="domain" type="text" class="mt-1 block w-full" autocomplete="domain" placeholder="example.com" />
|
||||
@error('domain')
|
||||
<x-input-error class="mt-2" :messages="$message" />
|
||||
@enderror
|
||||
@ -28,75 +28,15 @@
|
||||
|
||||
<div>
|
||||
<x-input-label for="alias" :value="__('Alias')" />
|
||||
<x-text-input wire:model.defer="alias" id="alias" name="alias" type="text" class="mt-1 block w-full" autocomplete="alias" placeholder="www.example.com" />
|
||||
<x-text-input wire:model.defer="inputs.alias" id="alias" name="alias" type="text" class="mt-1 block w-full" autocomplete="alias" placeholder="www.example.com" />
|
||||
@error('alias')
|
||||
<x-input-error class="mt-2" :messages="$message" />
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<x-input-label for="php_version" :value="__('PHP Version')" />
|
||||
<x-select-input wire:model.defer="php_version" id="php_version" name="php_version" class="mt-1 w-full">
|
||||
<option value="" selected disabled>{{ __("Select") }}</option>
|
||||
@foreach($server->installedPHPVersions() as $version)
|
||||
<option value="{{ $version }}" @if($version === $php_version) selected @endif>
|
||||
PHP {{ $version }}
|
||||
</option>
|
||||
@endforeach
|
||||
</x-select-input>
|
||||
@error('php_version')
|
||||
<x-input-error class="mt-2" :messages="$message" />
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<x-input-label for="web_directory" :value="__('Web Directory')" />
|
||||
<x-text-input wire:model.defer="web_directory" id="web_directory" name="web_directory" type="text" class="mt-1 block w-full" autocomplete="web_directory" />
|
||||
@error('web_directory')
|
||||
<x-input-error class="mt-2" :messages="$message" />
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<x-input-label for="source_control" :value="__('Source Control')" />
|
||||
<div class="flex items-center mt-1">
|
||||
<x-select-input wire:model="source_control" id="source_control" name="source_control" class="mt-1 w-full">
|
||||
<option value="" selected disabled>{{ __("Select") }}</option>
|
||||
@foreach($sourceControls as $sourceControl)
|
||||
<option value="{{ $sourceControl->id }}" @if($sourceControl->id === $source_control) selected @endif>
|
||||
{{ $sourceControl->profile }} ({{ $sourceControl->provider }})
|
||||
</option>
|
||||
@endforeach
|
||||
</x-select-input>
|
||||
<x-secondary-button :href="route('source-controls', ['redirect' => request()->url()])" class="flex-none ml-2">{{ __('Connect') }}</x-secondary-button>
|
||||
</div>
|
||||
@error('source_control')
|
||||
<x-input-error class="mt-2" :messages="$message" />
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<x-input-label for="repository" :value="__('Repository')" />
|
||||
<x-text-input wire:model.defer="repository" id="repository" name="repository" type="text" class="mt-1 block w-full" autocomplete="repository" placeholder="organization/repository" />
|
||||
@error('repository')
|
||||
<x-input-error class="mt-2" :messages="$message" />
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<x-input-label for="branch" :value="__('Branch')" />
|
||||
<x-text-input wire:model.defer="branch" id="branch" name="branch" type="text" class="mt-1 block w-full" autocomplete="branch" placeholder="main" />
|
||||
@error('branch')
|
||||
<x-input-error class="mt-2" :messages="$message" />
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div class="mt-6">
|
||||
<label for="composer" class="inline-flex items-center">
|
||||
<input id="composer" wire:model.defer="composer" type="checkbox" class="rounded dark:bg-gray-900 border-gray-300 dark:border-gray-700 text-indigo-600 shadow-sm focus:ring-indigo-500 dark:focus:ring-indigo-600 dark:focus:ring-offset-gray-800" name="composer">
|
||||
<span class="ml-2 text-sm text-gray-600 dark:text-gray-400">{{ __('Run `composer install --no-dev`') }}</span>
|
||||
</label>
|
||||
</div>
|
||||
@if (isset($inputs['type']) && $inputs['type'])
|
||||
@include('livewire.sites.partials.create.' . $inputs['type'])
|
||||
@endif
|
||||
</form>
|
||||
<x-slot name="actions">
|
||||
<x-primary-button form="create-site" wire:loading.attr="disabled">{{ __('Create') }}</x-primary-button>
|
||||
|
@ -0,0 +1,7 @@
|
||||
<div>
|
||||
<x-input-label for="branch" :value="__('Branch')" />
|
||||
<x-text-input wire:model.defer="inputs.branch" id="branch" name="branch" type="text" class="mt-1 block w-full" autocomplete="branch" placeholder="main" />
|
||||
@error('branch')
|
||||
<x-input-error class="mt-2" :messages="$message" />
|
||||
@enderror
|
||||
</div>
|
@ -0,0 +1,6 @@
|
||||
<div class="mt-6">
|
||||
<label for="composer" class="inline-flex items-center">
|
||||
<input id="composer" wire:model.defer="inputs.composer" type="checkbox" class="rounded dark:bg-gray-900 border-gray-300 dark:border-gray-700 text-indigo-600 shadow-sm focus:ring-indigo-500 dark:focus:ring-indigo-600 dark:focus:ring-offset-gray-800" name="composer">
|
||||
<span class="ml-2 text-sm text-gray-600 dark:text-gray-400">{{ __('Run `composer install --no-dev`') }}</span>
|
||||
</label>
|
||||
</div>
|
@ -0,0 +1,17 @@
|
||||
@php
|
||||
/* @var \App\Models\Server $server */
|
||||
@endphp
|
||||
<div>
|
||||
<x-input-label for="php_version" :value="__('PHP Version')" />
|
||||
<x-select-input wire:model.defer="inputs.php_version" id="php_version" name="php_version" class="mt-1 w-full">
|
||||
<option value="" selected>{{ __("Select") }}</option>
|
||||
@foreach($server->installedPHPVersions() as $version)
|
||||
<option value="{{ $version }}" @if($version === $inputs['php_version']) selected @endif>
|
||||
PHP {{ $version }}
|
||||
</option>
|
||||
@endforeach
|
||||
</x-select-input>
|
||||
@error('php_version')
|
||||
<x-input-error class="mt-2" :messages="$message" />
|
||||
@enderror
|
||||
</div>
|
@ -0,0 +1,7 @@
|
||||
<div>
|
||||
<x-input-label for="repository" :value="__('Repository')" />
|
||||
<x-text-input wire:model.defer="inputs.repository" id="repository" name="repository" type="text" class="mt-1 block w-full" autocomplete="repository" placeholder="organization/repository" />
|
||||
@error('repository')
|
||||
<x-input-error class="mt-2" :messages="$message" />
|
||||
@enderror
|
||||
</div>
|
@ -0,0 +1,17 @@
|
||||
<div>
|
||||
<x-input-label for="source_control" :value="__('Source Control')" />
|
||||
<div class="flex items-center mt-1">
|
||||
<x-select-input wire:model="inputs.source_control" id="source_control" name="source_control" class="mt-1 w-full">
|
||||
<option value="" selected>{{ __("Select") }}</option>
|
||||
@foreach($sourceControls as $sourceControl)
|
||||
<option value="{{ $sourceControl->id }}" @if($sourceControl->id === $inputs['source_control']) selected @endif>
|
||||
{{ $sourceControl->profile }} ({{ $sourceControl->provider }})
|
||||
</option>
|
||||
@endforeach
|
||||
</x-select-input>
|
||||
<x-secondary-button :href="route('source-controls', ['redirect' => request()->url()])" class="flex-none ml-2">{{ __('Connect') }}</x-secondary-button>
|
||||
</div>
|
||||
@error('source_control')
|
||||
<x-input-error class="mt-2" :messages="$message" />
|
||||
@enderror
|
||||
</div>
|
@ -0,0 +1,10 @@
|
||||
<div>
|
||||
<x-input-label for="web_directory" :value="__('Web Directory')" />
|
||||
<x-text-input wire:model.defer="inputs.web_directory" id="web_directory" name="web_directory" type="text" class="mt-1 block w-full" autocomplete="web_directory" />
|
||||
<x-input-help>
|
||||
{{ __("For root, leave this blank") }}
|
||||
</x-input-help>
|
||||
@error('web_directory')
|
||||
<x-input-error class="mt-2" :messages="$message" />
|
||||
@enderror
|
||||
</div>
|
@ -0,0 +1,11 @@
|
||||
@include('livewire.sites.partials.create.fields.php-version')
|
||||
|
||||
@include('livewire.sites.partials.create.fields.web-directory')
|
||||
|
||||
@include('livewire.sites.partials.create.fields.source-control')
|
||||
|
||||
@include('livewire.sites.partials.create.fields.repository')
|
||||
|
||||
@include('livewire.sites.partials.create.fields.branch')
|
||||
|
||||
@include('livewire.sites.partials.create.fields.composer')
|
11
resources/views/livewire/sites/partials/create/php.blade.php
Normal file
11
resources/views/livewire/sites/partials/create/php.blade.php
Normal file
@ -0,0 +1,11 @@
|
||||
@include('livewire.sites.partials.create.fields.php-version')
|
||||
|
||||
@include('livewire.sites.partials.create.fields.web-directory')
|
||||
|
||||
@include('livewire.sites.partials.create.fields.source-control')
|
||||
|
||||
@include('livewire.sites.partials.create.fields.repository')
|
||||
|
||||
@include('livewire.sites.partials.create.fields.branch')
|
||||
|
||||
@include('livewire.sites.partials.create.fields.composer')
|
@ -0,0 +1,63 @@
|
||||
@include('livewire.sites.partials.create.fields.php-version')
|
||||
|
||||
<div>
|
||||
<x-input-label for="title" :value="__('Title')" />
|
||||
<x-text-input wire:model.defer="inputs.title" id="title" name="title" type="text" class="mt-1 block w-full" autocomplete="branch" />
|
||||
@error('title')
|
||||
<x-input-error class="mt-2" :messages="$message" />
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 lg:grid-cols-3 gap-5">
|
||||
<div>
|
||||
<x-input-label for="email" :value="__('WP Admin Email')" />
|
||||
<x-text-input wire:model.defer="inputs.email" id="email" name="email" type="email" class="mt-1 block w-full" autocomplete="email" />
|
||||
@error('email')
|
||||
<x-input-error class="mt-2" :messages="$message" />
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<x-input-label for="username" :value="__('WP Admin Username')" />
|
||||
<x-text-input wire:model.defer="inputs.username" id="username" name="username" type="text" class="mt-1 block w-full" autocomplete="username" />
|
||||
@error('username')
|
||||
<x-input-error class="mt-2" :messages="$message" />
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<x-input-label for="password" :value="__('WP Admin Password')" />
|
||||
<x-text-input wire:model.defer="inputs.password" id="password" name="password" type="text" class="mt-1 block w-full" />
|
||||
@error('title')
|
||||
<x-input-error class="mt-2" :messages="$message" />
|
||||
@enderror
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 lg:grid-cols-3 gap-5">
|
||||
<div>
|
||||
<x-input-label for="database" :value="__('Database Name')" />
|
||||
<x-text-input wire:model.defer="inputs.database" id="database" name="database" type="text" class="mt-1 block w-full" autocomplete="database" />
|
||||
<x-input-help>{{ __("It will create a database with this name") }}</x-input-help>
|
||||
@error('database')
|
||||
<x-input-error class="mt-2" :messages="$message" />
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<x-input-label for="database" :value="__('Database User')" />
|
||||
<x-text-input wire:model.defer="inputs.database_user" id="database_user" name="database_user" type="text" class="mt-1 block w-full" autocomplete="database_user" />
|
||||
<x-input-help>{{ __("It will create a database user with this username") }}</x-input-help>
|
||||
@error('database_user')
|
||||
<x-input-error class="mt-2" :messages="$message" />
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<x-input-label for="password" :value="__('Database Password')" />
|
||||
<x-text-input wire:model.defer="inputs.database_password" id="database_password" name="database_password" type="text" class="mt-1 block w-full" />
|
||||
@error('database_password')
|
||||
<x-input-error class="mt-2" :messages="$message" />
|
||||
@enderror
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,27 @@
|
||||
<x-card>
|
||||
<x-slot name="title">{{ __("Update Source Control") }}</x-slot>
|
||||
|
||||
<x-slot name="description">{{ __("You can change the source control provider for this site") }}</x-slot>
|
||||
|
||||
<form id="update-source-control" wire:submit.prevent="update" class="space-y-6">
|
||||
<div>
|
||||
<x-input-label for="provider" :value="__('Source Control')" />
|
||||
<x-select-input wire:model.defer="source_control" id="source_control" name="source_control" class="mt-1 w-full">
|
||||
<option value="" selected>{{ __("Select") }}</option>
|
||||
@foreach(\App\Models\SourceControl::all() as $sourceControl)
|
||||
<option value="{{ $sourceControl->id }}" @if($sourceControl->id === $source_control) selected @endif>{{ $sourceControl->profile }} ({{ $sourceControl->provider }})</option>
|
||||
@endforeach
|
||||
</x-select-input>
|
||||
@error('source_control')
|
||||
<x-input-error class="mt-2" :messages="$message" />
|
||||
@enderror
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<x-slot name="actions">
|
||||
@if (session('status') === 'source-control-updated')
|
||||
<p class="mr-2">{{ __('Saved') }}</p>
|
||||
@endif
|
||||
<x-primary-button form="update-source-control" wire:loading.attr="disabled">{{ __('Save') }}</x-primary-button>
|
||||
</x-slot>
|
||||
</x-card>
|
@ -32,6 +32,17 @@
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
@if($provider === App\Enums\SourceControl::GITLAB)
|
||||
<div class="mt-6">
|
||||
<x-input-label for="url" value="Url (optional)" />
|
||||
<x-text-input wire:model.defer="url" id="url" name="url" type="text" class="mt-1 w-full" placeholder="e.g. https://gitlab.example.com/" />
|
||||
<x-input-help>If you run a self-managed gitlab enter the url here, leave empty to use gitlab.com</x-input-help>
|
||||
@error('url')
|
||||
<x-input-error class="mt-2" :messages="$message" />
|
||||
@enderror
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="mt-6">
|
||||
<x-input-label for="token" value="API Key" />
|
||||
<x-text-input wire:model.defer="token" id="token" name="token" type="text" class="mt-1 w-full" />
|
||||
|
@ -32,13 +32,79 @@
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div class="mt-6">
|
||||
<x-input-label for="token" value="API Key" />
|
||||
<x-text-input wire:model.defer="token" id="token" name="token" type="text" class="mt-1 w-full" />
|
||||
@error('token')
|
||||
<x-input-error class="mt-2" :messages="$message" />
|
||||
@enderror
|
||||
</div>
|
||||
@if($provider == \App\Enums\StorageProvider::DROPBOX)
|
||||
<div class="mt-6">
|
||||
<x-input-label for="token" value="API Key" />
|
||||
<x-text-input wire:model.defer="token" id="token" name="token" type="text" class="mt-1 w-full" />
|
||||
@error('token')
|
||||
<x-input-error class="mt-2" :messages="$message" />
|
||||
@enderror
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if($provider == \App\Enums\StorageProvider::FTP)
|
||||
<div class="grid grid-cols-2 gap-2">
|
||||
<div class="mt-6">
|
||||
<x-input-label for="host" value="Host" />
|
||||
<x-text-input wire:model.defer="host" id="host" name="host" type="text" class="mt-1 w-full" />
|
||||
@error('host')
|
||||
<x-input-error class="mt-2" :messages="$message" />
|
||||
@enderror
|
||||
</div>
|
||||
<div class="mt-6">
|
||||
<x-input-label for="port" value="Port" />
|
||||
<x-text-input wire:model.defer="port" id="port" name="port" type="text" class="mt-1 w-full" />
|
||||
@error('port')
|
||||
<x-input-error class="mt-2" :messages="$message" />
|
||||
@enderror
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-6">
|
||||
<x-input-label for="path" value="Path" />
|
||||
<x-text-input wire:model.defer="path" id="path" name="path" type="text" class="mt-1 w-full" />
|
||||
@error('path')
|
||||
<x-input-error class="mt-2" :messages="$message" />
|
||||
@enderror
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-2">
|
||||
<div class="mt-6">
|
||||
<x-input-label for="username" value="Username" />
|
||||
<x-text-input wire:model.defer="username" id="username" name="username" type="text" class="mt-1 w-full" />
|
||||
@error('username')
|
||||
<x-input-error class="mt-2" :messages="$message" />
|
||||
@enderror
|
||||
</div>
|
||||
<div class="mt-6">
|
||||
<x-input-label for="password" value="Password" />
|
||||
<x-text-input wire:model.defer="password" id="password" name="password" type="text" class="mt-1 w-full" />
|
||||
@error('password')
|
||||
<x-input-error class="mt-2" :messages="$message" />
|
||||
@enderror
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-2">
|
||||
<div class="mt-6">
|
||||
<x-input-label for="ssl" :value="__('SSL')" />
|
||||
<x-select-input wire:model="ssl" id="ssl" name="ssl" class="mt-1 w-full">
|
||||
<option value="1" @if($ssl) selected @endif>{{ __("Yes") }}</option>
|
||||
<option value="0" @if(!$ssl) selected @endif>{{ __("No") }}</option>
|
||||
</x-select-input>
|
||||
@error('ssl')
|
||||
<x-input-error class="mt-2" :messages="$message" />
|
||||
@enderror
|
||||
</div>
|
||||
<div class="mt-6">
|
||||
<x-input-label for="passive" :value="__('Passive')" />
|
||||
<x-select-input wire:model="passive" id="passive" name="passive" class="mt-1 w-full">
|
||||
<option value="1" @if($passive) selected @endif>{{ __("Yes") }}</option>
|
||||
<option value="0" @if(!$passive) selected @endif>{{ __("No") }}</option>
|
||||
</x-select-input>
|
||||
@error('passive')
|
||||
<x-input-error class="mt-2" :messages="$message" />
|
||||
@enderror
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="mt-6 flex justify-end">
|
||||
<x-secondary-button type="button" x-on:click="$dispatch('close')">
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user