mirror of
https://github.com/vitodeploy/vito.git
synced 2025-07-01 14:06:15 +00:00
Compare commits
20 Commits
Author | SHA1 | Date | |
---|---|---|---|
fd2244d382 | |||
551f1ce40e | |||
1ce92d9361 | |||
ec6e55e30c | |||
4cda14f4b8 | |||
5e6d338bdc | |||
7312e3f515 | |||
b771db882b | |||
94977797cc | |||
c45872df55 | |||
16fae5334c | |||
7b8deddeca | |||
1bf3c94358 | |||
700cc5f44c | |||
9d13cc0756 | |||
f51d7900f0 | |||
4bd4b34d24 | |||
7c5505be16 | |||
7249cf9ed6 | |||
8282d39722 |
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.
|
14
README.md
14
README.md
@ -10,9 +10,21 @@ ## Documentation
|
||||
|
||||
https://vitodeploy.com
|
||||
|
||||
## Feedbacks
|
||||
|
||||
https://features.vitodeploy.com
|
||||
|
||||
## Roadmap
|
||||
|
||||
https://https://features.vitodeploy.com/roadmap
|
||||
|
||||
## 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();
|
||||
'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();
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -15,13 +15,13 @@ public function create(
|
||||
?int $siteId = null
|
||||
): void;
|
||||
|
||||
public function delete(int $id, int $siteId = null): void;
|
||||
public function delete(int $id, ?int $siteId = null): void;
|
||||
|
||||
public function restart(int $id, int $siteId = null): void;
|
||||
public function restart(int $id, ?int $siteId = null): void;
|
||||
|
||||
public function stop(int $id, int $siteId = null): void;
|
||||
public function stop(int $id, ?int $siteId = null): void;
|
||||
|
||||
public function start(int $id, int $siteId = null): void;
|
||||
public function start(int $id, ?int $siteId = null): void;
|
||||
|
||||
public function getLogs(string $logPath): string;
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ public function credentialData(array $input): array;
|
||||
|
||||
public function data(array $input): array;
|
||||
|
||||
public function connect(array $credentials = null): bool;
|
||||
public function connect(?array $credentials = null): bool;
|
||||
|
||||
public function plans(): array;
|
||||
|
||||
|
@ -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,7 +6,7 @@ interface SourceControlProvider
|
||||
{
|
||||
public function connect(): bool;
|
||||
|
||||
public function getRepo(string $repo = null): mixed;
|
||||
public function getRepo(?string $repo = null): mixed;
|
||||
|
||||
public function fullRepoUrl(string $repo, string $key): string;
|
||||
|
||||
|
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';
|
||||
}
|
||||
|
@ -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');
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ class SSH
|
||||
|
||||
protected PrivateKey $privateKey;
|
||||
|
||||
public function init(Server $server, string $asUser = null): self
|
||||
public function init(Server $server, ?string $asUser = null): self
|
||||
{
|
||||
$this->connection = null;
|
||||
$this->log = null;
|
||||
@ -87,7 +87,7 @@ public function connect(bool $sftp = false): void
|
||||
/**
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function exec(string|array|SSHCommand $commands, string $log = '', int $siteId = null): string
|
||||
public function exec(string|array|SSHCommand $commands, string $log = '', ?int $siteId = null): string
|
||||
{
|
||||
if ($log) {
|
||||
$this->setLog($log, $siteId);
|
||||
|
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 HasToast;
|
||||
use RefreshComponentOnBroadcast;
|
||||
|
||||
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');
|
||||
}
|
||||
}
|
@ -11,8 +11,8 @@
|
||||
|
||||
class Deploy extends Component
|
||||
{
|
||||
use RefreshComponentOnBroadcast;
|
||||
use HasToast;
|
||||
use RefreshComponentOnBroadcast;
|
||||
|
||||
public Site $site;
|
||||
|
||||
|
@ -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
|
||||
|
@ -10,8 +10,8 @@
|
||||
|
||||
class DeploymentsList extends Component
|
||||
{
|
||||
use RefreshComponentOnBroadcast;
|
||||
use HasCustomPaginationView;
|
||||
use RefreshComponentOnBroadcast;
|
||||
|
||||
public Site $site;
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -11,8 +11,8 @@
|
||||
|
||||
class LogsList extends Component
|
||||
{
|
||||
use RefreshComponentOnBroadcast;
|
||||
use HasCustomPaginationView;
|
||||
use RefreshComponentOnBroadcast;
|
||||
|
||||
public ?int $count = null;
|
||||
|
||||
|
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');
|
||||
}
|
||||
}
|
@ -10,8 +10,8 @@
|
||||
|
||||
class SslsList extends Component
|
||||
{
|
||||
use RefreshComponentOnBroadcast;
|
||||
use HasToast;
|
||||
use RefreshComponentOnBroadcast;
|
||||
|
||||
public Site $site;
|
||||
|
||||
|
@ -13,7 +13,7 @@ class Initialize extends InstallationJob
|
||||
|
||||
protected ?string $asUser;
|
||||
|
||||
public function __construct(Server $server, string $asUser = null)
|
||||
public function __construct(Server $server, ?string $asUser = null)
|
||||
{
|
||||
$this->server = $server->refresh();
|
||||
$this->asUser = $asUser;
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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(
|
||||
|
@ -233,7 +233,7 @@ public function install(): void
|
||||
// $this->team->notify(new ServerInstallationStarted($this));
|
||||
}
|
||||
|
||||
public function ssh(string $user = null): \App\Helpers\SSH|SSHFake
|
||||
public function ssh(?string $user = null): \App\Helpers\SSH|SSHFake
|
||||
{
|
||||
return SSH::init($this, $user);
|
||||
}
|
||||
@ -263,7 +263,7 @@ public function provider(): \App\Contracts\ServerProvider
|
||||
return new $providerClass($this);
|
||||
}
|
||||
|
||||
public function webserver(string $version = null): ?Service
|
||||
public function webserver(?string $version = null): ?Service
|
||||
{
|
||||
if (! $version) {
|
||||
return $this->defaultService('webserver');
|
||||
@ -272,7 +272,7 @@ public function webserver(string $version = null): ?Service
|
||||
return $this->service('webserver', $version);
|
||||
}
|
||||
|
||||
public function database(string $version = null): ?Service
|
||||
public function database(?string $version = null): ?Service
|
||||
{
|
||||
if (! $version) {
|
||||
return $this->defaultService('database');
|
||||
@ -281,7 +281,7 @@ public function database(string $version = null): ?Service
|
||||
return $this->service('database', $version);
|
||||
}
|
||||
|
||||
public function firewall(string $version = null): ?Service
|
||||
public function firewall(?string $version = null): ?Service
|
||||
{
|
||||
if (! $version) {
|
||||
return $this->defaultService('firewall');
|
||||
@ -290,7 +290,7 @@ public function firewall(string $version = null): ?Service
|
||||
return $this->service('firewall', $version);
|
||||
}
|
||||
|
||||
public function processManager(string $version = null): ?Service
|
||||
public function processManager(?string $version = null): ?Service
|
||||
{
|
||||
if (! $version) {
|
||||
return $this->defaultService('process_manager');
|
||||
@ -299,7 +299,7 @@ public function processManager(string $version = null): ?Service
|
||||
return $this->service('process_manager', $version);
|
||||
}
|
||||
|
||||
public function php(string $version = null): ?Service
|
||||
public function php(?string $version = null): ?Service
|
||||
{
|
||||
if (! $version) {
|
||||
return $this->defaultService('php');
|
||||
|
@ -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;
|
||||
|
||||
/**
|
||||
@ -341,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 {
|
||||
@ -364,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'],
|
||||
]);
|
||||
@ -399,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;
|
||||
}
|
||||
}
|
||||
|
@ -33,7 +33,7 @@ public function provider(): SourceControlProvider
|
||||
return new $providerClass($this);
|
||||
}
|
||||
|
||||
public function getRepo(string $repo = null): ?array
|
||||
public function getRepo(?string $repo = null): ?array
|
||||
{
|
||||
return $this->provider()->getRepo($repo);
|
||||
}
|
||||
|
@ -63,7 +63,7 @@ public function data(array $input): array
|
||||
/**
|
||||
* @throws CouldNotConnectToProvider
|
||||
*/
|
||||
public function connect(array $credentials = null): bool
|
||||
public function connect(?array $credentials = null): bool
|
||||
{
|
||||
try {
|
||||
$this->connectToEc2ClientTest($credentials);
|
||||
|
@ -10,7 +10,7 @@ abstract class AbstractProvider implements ServerProvider
|
||||
{
|
||||
protected ?Server $server;
|
||||
|
||||
public function __construct(Server $server = null)
|
||||
public function __construct(?Server $server = null)
|
||||
{
|
||||
$this->server = $server;
|
||||
}
|
||||
|
@ -42,7 +42,7 @@ public function data(array $input): array
|
||||
return [];
|
||||
}
|
||||
|
||||
public function connect(array $credentials = null): bool
|
||||
public function connect(?array $credentials = null): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
@ -58,7 +58,7 @@ public function data(array $input): array
|
||||
/**
|
||||
* @throws CouldNotConnectToProvider
|
||||
*/
|
||||
public function connect(array $credentials = null): bool
|
||||
public function connect(?array $credentials = null): bool
|
||||
{
|
||||
$connect = Http::withToken($credentials['token'])->get($this->apiUrl.'/account');
|
||||
if (! $connect->ok()) {
|
||||
|
@ -45,7 +45,7 @@ public function data(array $input): array
|
||||
/**
|
||||
* @throws CouldNotConnectToProvider
|
||||
*/
|
||||
public function connect(array $credentials = null): bool
|
||||
public function connect(?array $credentials = null): bool
|
||||
{
|
||||
$connect = Http::withToken($credentials['token'])->get($this->apiUrl.'/servers');
|
||||
if (! $connect->ok()) {
|
||||
|
@ -57,7 +57,7 @@ public function data(array $input): array
|
||||
/**
|
||||
* @throws CouldNotConnectToProvider
|
||||
*/
|
||||
public function connect(array $credentials = null): bool
|
||||
public function connect(?array $credentials = null): bool
|
||||
{
|
||||
$connect = Http::withToken($credentials['token'])->get($this->apiUrl.'/account');
|
||||
if (! $connect->ok()) {
|
||||
|
@ -59,7 +59,7 @@ public function data(array $input): array
|
||||
/**
|
||||
* @throws CouldNotConnectToProvider
|
||||
*/
|
||||
public function connect(array $credentials = null): bool
|
||||
public function connect(?array $credentials = null): bool
|
||||
{
|
||||
$connect = Http::withToken($credentials['token'])->get($this->apiUrl.'/account');
|
||||
if (! $connect->ok()) {
|
||||
|
@ -16,7 +16,7 @@ public function __construct(Server $server)
|
||||
$this->server = $server;
|
||||
}
|
||||
|
||||
protected function progress(int $percentage, string $step = null): Closure
|
||||
protected function progress(int $percentage, ?string $step = null): Closure
|
||||
{
|
||||
return function () use ($percentage, $step) {
|
||||
$this->server->progress = $percentage;
|
||||
|
@ -47,7 +47,7 @@ public function create(
|
||||
/**
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function delete(int $id, int $siteId = null): void
|
||||
public function delete(int $id, ?int $siteId = null): void
|
||||
{
|
||||
$this->service->server->ssh()->exec(
|
||||
new DeleteWorkerCommand($id),
|
||||
@ -59,7 +59,7 @@ public function delete(int $id, int $siteId = null): void
|
||||
/**
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function restart(int $id, int $siteId = null): void
|
||||
public function restart(int $id, ?int $siteId = null): void
|
||||
{
|
||||
$this->service->server->ssh()->exec(
|
||||
new RestartWorkerCommand($id),
|
||||
@ -71,7 +71,7 @@ public function restart(int $id, int $siteId = null): void
|
||||
/**
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function stop(int $id, int $siteId = null): void
|
||||
public function stop(int $id, ?int $siteId = null): void
|
||||
{
|
||||
$this->service->server->ssh()->exec(
|
||||
new StopWorkerCommand($id),
|
||||
@ -83,7 +83,7 @@ public function stop(int $id, int $siteId = null): void
|
||||
/**
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function start(int $id, int $siteId = null): void
|
||||
public function start(int $id, ?int $siteId = null): void
|
||||
{
|
||||
$this->service->server->ssh()->exec(
|
||||
new StartWorkerCommand($id),
|
||||
|
@ -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',
|
||||
@ -53,7 +61,8 @@ public function createFields(array $input): array
|
||||
public function data(array $input): array
|
||||
{
|
||||
return [
|
||||
'composer' => (bool) $input['composer'],
|
||||
'composer' => isset($input['composer']) && $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();
|
||||
|
@ -24,7 +24,7 @@ public function connect(): bool
|
||||
/**
|
||||
* @throws Exception
|
||||
*/
|
||||
public function getRepo(string $repo = null): mixed
|
||||
public function getRepo(?string $repo = null): mixed
|
||||
{
|
||||
$res = Http::withToken($this->sourceControl->access_token)
|
||||
->get($this->apiUrl."/repositories/$repo");
|
||||
@ -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.
|
||||
}
|
||||
}
|
@ -25,7 +25,7 @@ public function connect(): bool
|
||||
/**
|
||||
* @throws Exception
|
||||
*/
|
||||
public function getRepo(string $repo = null): mixed
|
||||
public function getRepo(?string $repo = null): mixed
|
||||
{
|
||||
if ($repo) {
|
||||
$url = $this->apiUrl.'/repos/'.$repo;
|
||||
@ -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,
|
||||
|
@ -25,7 +25,7 @@ public function connect(): bool
|
||||
/**
|
||||
* @throws Exception
|
||||
*/
|
||||
public function getRepo(string $repo = null): mixed
|
||||
public function getRepo(?string $repo = null): mixed
|
||||
{
|
||||
$repository = $repo ? urlencode($repo) : null;
|
||||
$res = Http::withToken($this->sourceControl->access_token)
|
||||
@ -53,7 +53,7 @@ public function deployHook(string $repo, array $events, string $secret): array
|
||||
$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,
|
||||
|
@ -15,7 +15,7 @@ class SSHFake
|
||||
|
||||
protected string $output = '';
|
||||
|
||||
public function init(Server $server, string $asUser = null): self
|
||||
public function init(Server $server, ?string $asUser = null): self
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
@ -47,7 +47,7 @@ public function assertExecuted(array|string $commands): void
|
||||
PHPUnit::assertTrue(true, $allExecuted);
|
||||
}
|
||||
|
||||
public function exec(string|array|SSHCommand $commands, string $log = '', int $siteId = null): string
|
||||
public function exec(string|array|SSHCommand $commands, string $log = '', ?int $siteId = null): string
|
||||
{
|
||||
if (! is_array($commands)) {
|
||||
$commands = [$commands];
|
||||
|
@ -16,6 +16,7 @@
|
||||
"laravel/tinker": "^2.8",
|
||||
"livewire/livewire": "^2.12",
|
||||
"phpseclib/phpseclib": "~3.0",
|
||||
"opcodesio/log-viewer": "^2.5",
|
||||
"ext-ftp": "*"
|
||||
},
|
||||
"require-dev": {
|
||||
@ -24,7 +25,6 @@
|
||||
"laravel/sail": "^1.18",
|
||||
"mockery/mockery": "^1.4.4",
|
||||
"nunomaduro/collision": "^7.0",
|
||||
"opcodesio/log-viewer": "^2.5",
|
||||
"phpunit/phpunit": "^10.0",
|
||||
"spatie/laravel-ignition": "^2.0"
|
||||
},
|
||||
|
2186
composer.lock
generated
2186
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@ -25,8 +25,8 @@
|
||||
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;
|
||||
@ -263,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,
|
||||
],
|
||||
|
||||
/*
|
||||
@ -284,7 +284,6 @@
|
||||
'github' => Github::class,
|
||||
'gitlab' => Gitlab::class,
|
||||
'bitbucket' => Bitbucket::class,
|
||||
'custom' => Custom::class,
|
||||
],
|
||||
|
||||
/*
|
||||
@ -292,11 +291,12 @@
|
||||
*/
|
||||
'php_extensions' => [
|
||||
'imagick',
|
||||
'geoip',
|
||||
'exif',
|
||||
'gmagick',
|
||||
'gmp',
|
||||
'intl',
|
||||
'sqlite3',
|
||||
'opcache',
|
||||
],
|
||||
|
||||
/*
|
||||
@ -324,7 +324,6 @@
|
||||
'https' => 443,
|
||||
'mysql' => 3306,
|
||||
'ftp' => 21,
|
||||
'phpmyadmin' => 54331,
|
||||
'tcp' => '',
|
||||
'udp' => '',
|
||||
],
|
||||
|
217
config/log-viewer.php
Normal file
217
config/log-viewer.php
Normal file
@ -0,0 +1,217 @@
|
||||
<?php
|
||||
|
||||
use Opcodes\LogViewer\Level;
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Log Viewer
|
||||
|--------------------------------------------------------------------------
|
||||
| Log Viewer can be disabled, so it's no longer accessible via browser.
|
||||
|
|
||||
*/
|
||||
|
||||
'enabled' => env('LOG_VIEWER_ENABLED', true),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Log Viewer Domain
|
||||
|--------------------------------------------------------------------------
|
||||
| You may change the domain where Log Viewer should be active.
|
||||
| If the domain is empty, all domains will be valid.
|
||||
|
|
||||
*/
|
||||
|
||||
'route_domain' => null,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Log Viewer Route
|
||||
|--------------------------------------------------------------------------
|
||||
| Log Viewer will be available under this URL.
|
||||
|
|
||||
*/
|
||||
|
||||
'route_path' => 'log-viewer',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Back to system URL
|
||||
|--------------------------------------------------------------------------
|
||||
| When set, displays a link to easily get back to this URL.
|
||||
| Set to `null` to hide this link.
|
||||
|
|
||||
| Optional label to display for the above URL.
|
||||
|
|
||||
*/
|
||||
|
||||
'back_to_system_url' => config('app.url', null),
|
||||
|
||||
'back_to_system_label' => null, // Displayed by default: "Back to {{ app.name }}"
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Log Viewer time zone.
|
||||
|--------------------------------------------------------------------------
|
||||
| The time zone in which to display the times in the UI. Defaults to
|
||||
| the application's timezone defined in config/app.php.
|
||||
|
|
||||
*/
|
||||
|
||||
'timezone' => null,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Log Viewer route middleware.
|
||||
|--------------------------------------------------------------------------
|
||||
| Optional middleware to use when loading the initial Log Viewer page.
|
||||
|
|
||||
*/
|
||||
|
||||
'middleware' => [
|
||||
'web',
|
||||
\Opcodes\LogViewer\Http\Middleware\AuthorizeLogViewer::class,
|
||||
'auth',
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Log Viewer API middleware.
|
||||
|--------------------------------------------------------------------------
|
||||
| Optional middleware to use on every API request. The same API is also
|
||||
| used from within the Log Viewer user interface.
|
||||
|
|
||||
*/
|
||||
|
||||
'api_middleware' => [
|
||||
\Opcodes\LogViewer\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
|
||||
\Opcodes\LogViewer\Http\Middleware\AuthorizeLogViewer::class,
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Log Viewer Remote hosts.
|
||||
|--------------------------------------------------------------------------
|
||||
| Log Viewer supports viewing Laravel logs from remote hosts. They must
|
||||
| be running Log Viewer as well. Below you can define the hosts you
|
||||
| would like to show in this Log Viewer instance.
|
||||
|
|
||||
*/
|
||||
|
||||
'hosts' => [
|
||||
'local' => [
|
||||
'name' => ucfirst(env('APP_ENV', 'local')),
|
||||
],
|
||||
|
||||
// 'staging' => [
|
||||
// 'name' => 'Staging',
|
||||
// 'host' => 'https://staging.example.com/log-viewer',
|
||||
// 'auth' => [ // Example of HTTP Basic auth
|
||||
// 'username' => 'username',
|
||||
// 'password' => 'password',
|
||||
// ],
|
||||
// ],
|
||||
//
|
||||
// 'production' => [
|
||||
// 'name' => 'Production',
|
||||
// 'host' => 'https://example.com/log-viewer',
|
||||
// 'auth' => [ // Example of Bearer token auth
|
||||
// 'token' => env('LOG_VIEWER_PRODUCTION_TOKEN'),
|
||||
// ],
|
||||
// 'headers' => [
|
||||
// 'X-Foo' => 'Bar',
|
||||
// ],
|
||||
// ],
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Include file patterns
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
*/
|
||||
|
||||
'include_files' => [
|
||||
'*.log',
|
||||
'**/*.log',
|
||||
// '/absolute/paths/supported',
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Exclude file patterns.
|
||||
|--------------------------------------------------------------------------
|
||||
| This will take precedence over included files.
|
||||
|
|
||||
*/
|
||||
|
||||
'exclude_files' => [
|
||||
// 'my_secret.log'
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Shorter stack trace filters.
|
||||
|--------------------------------------------------------------------------
|
||||
| Lines containing any of these strings will be excluded from the full log.
|
||||
| This setting is only active when the function is enabled via the user interface.
|
||||
|
|
||||
*/
|
||||
|
||||
'shorter_stack_trace_excludes' => [
|
||||
'/vendor/symfony/',
|
||||
'/vendor/laravel/framework/',
|
||||
'/vendor/barryvdh/laravel-debugbar/',
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Log matching patterns
|
||||
|--------------------------------------------------------------------------
|
||||
| Regexes for matching log files
|
||||
|
|
||||
*/
|
||||
|
||||
'patterns' => [
|
||||
'laravel' => [
|
||||
'log_matching_regex' => '/^\[(\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}:\d{2}\.?(\d{6}([\+-]\d\d:\d\d)?)?)\].*/',
|
||||
|
||||
/**
|
||||
* This pattern, used for processing Laravel logs, returns these results:
|
||||
* $matches[0] - the full log line being tested.
|
||||
* $matches[1] - full timestamp between the square brackets (includes microseconds and timezone offset)
|
||||
* $matches[2] - timestamp microseconds, if available
|
||||
* $matches[3] - timestamp timezone offset, if available
|
||||
* $matches[4] - contents between timestamp and the severity level
|
||||
* $matches[5] - environment (local, production, etc)
|
||||
* $matches[6] - log severity (info, debug, error, etc)
|
||||
* $matches[7] - the log text, the rest of the text.
|
||||
*/
|
||||
'log_parsing_regex' => '/^\[(\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}:\d{2}\.?(\d{6}([\+-]\d\d:\d\d)?)?)\](.*?(\w+)\.|.*?)('
|
||||
.implode('|', array_filter(Level::caseValues()))
|
||||
.')?: (.*?)( in [\/].*?:[0-9]+)?$/is',
|
||||
],
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Cache driver
|
||||
|--------------------------------------------------------------------------
|
||||
| Cache driver to use for storing the log indices. Indices are used to speed up
|
||||
| log navigation. Defaults to your application's default cache driver.
|
||||
|
|
||||
*/
|
||||
|
||||
'cache_driver' => env('LOG_VIEWER_CACHE_DRIVER', null),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Chunk size when scanning log files lazily
|
||||
|--------------------------------------------------------------------------
|
||||
| The size in MB of files to scan before updating the progress bar when searching across all files.
|
||||
|
|
||||
*/
|
||||
|
||||
'lazy_scan_chunk_size_in_mb' => 50,
|
||||
];
|
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,7 +13,6 @@ class SourceControlFactory extends Factory
|
||||
public function definition(): array
|
||||
{
|
||||
return [
|
||||
'provider' => $this->faker->randomElement(\App\Enums\SourceControl::getValues()),
|
||||
'access_token' => Str::random(10),
|
||||
];
|
||||
}
|
||||
@ -44,13 +43,4 @@ public function bitbucket(): Factory
|
||||
];
|
||||
});
|
||||
}
|
||||
|
||||
public function custom(): Factory
|
||||
{
|
||||
return $this->state(function (array $attributes) {
|
||||
return [
|
||||
'provider' => \App\Enums\SourceControl::CUSTOM,
|
||||
];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
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
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
@ -1,11 +1,11 @@
|
||||
{
|
||||
"resources/css/app.css": {
|
||||
"file": "assets/app-99c9ce18.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 |
2
public/vendor/log-viewer/app.css
vendored
2
public/vendor/log-viewer/app.css
vendored
File diff suppressed because one or more lines are too long
2
public/vendor/log-viewer/app.js
vendored
2
public/vendor/log-viewer/app.js
vendored
File diff suppressed because one or more lines are too long
4
public/vendor/log-viewer/mix-manifest.json
vendored
4
public/vendor/log-viewer/mix-manifest.json
vendored
@ -1,6 +1,6 @@
|
||||
{
|
||||
"/app.js": "/app.js?id=2ca3fa12f273bd645611f1acf3d81355",
|
||||
"/app.css": "/app.css?id=93151d8b186ef7758df8582425ff8082",
|
||||
"/app.js": "/app.js?id=5f574f36f456b103dffcfa21d5612785",
|
||||
"/app.css": "/app.css?id=b701a4344131bb2c00e9f0b1ef1ab3c1",
|
||||
"/img/log-viewer-128.png": "/img/log-viewer-128.png?id=d576c6d2e16074d3f064e60fe4f35166",
|
||||
"/img/log-viewer-32.png": "/img/log-viewer-32.png?id=f8ec67d10f996aa8baf00df3b61eea6d",
|
||||
"/img/log-viewer-64.png": "/img/log-viewer-64.png?id=8902d596fc883ca9eb8105bb683568c6"
|
||||
|
@ -1,5 +1,5 @@
|
||||
server {
|
||||
listen 54331;
|
||||
listen __port__;
|
||||
server_name _;
|
||||
root /home/vito/phpmyadmin;
|
||||
|
||||
|
@ -38,7 +38,8 @@
|
||||
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>
|
||||
|
||||
@ -160,45 +161,7 @@ class="min-h-screen w-64 flex-none border-r border-gray-200 bg-white dark:border
|
||||
<div class="flex items-center justify-center">
|
||||
{{-- Search --}}
|
||||
</div>
|
||||
{{-- Dark Mode Toggle Button section --}}
|
||||
<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>
|
||||
{{-- End of Dark Mode Toggle Button section --}}
|
||||
@include('layouts.partials.color-scheme')
|
||||
<div class="ml-6 flex items-center">
|
||||
<div class="relative ml-5">
|
||||
<x-dropdown align="right" width="48">
|
||||
|
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>
|
||||
|
@ -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,7 +1,25 @@
|
||||
<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">
|
||||
@ -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->unit)
|
||||
@if ($service->status == \App\Enums\ServiceStatus::STOPPED)
|
||||
<x-dropdown-link class="cursor-pointer" wire:click="start({{ $service->id }})">
|
||||
{{ __("Start") }}
|
||||
{{ __('Start') }}
|
||||
</x-dropdown-link>
|
||||
@endif
|
||||
@if ($service->status == \App\Enums\ServiceStatus::READY)
|
||||
<x-dropdown-link class="cursor-pointer" wire:click="stop({{ $service->id }})">
|
||||
{{ __("Stop") }}
|
||||
{{ __('Stop') }}
|
||||
</x-dropdown-link>
|
||||
@endif
|
||||
<x-dropdown-link class="cursor-pointer" wire:click="restart({{ $service->id }})">
|
||||
{{ __("Restart") }}
|
||||
{{ __('Restart') }}
|
||||
</x-dropdown-link>
|
||||
@else
|
||||
<x-dropdown-link class="cursor-pointer" wire:click="uninstall({{ $service->id }})">
|
||||
{{ __('Uninstall') }}
|
||||
</x-dropdown-link>
|
||||
@endif
|
||||
</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>
|
@ -3,6 +3,8 @@
|
||||
|
||||
<livewire:sites.change-php-version :site="$site"/>
|
||||
|
||||
<livewire:sites.update-source-control-provider :site="$site"/>
|
||||
|
||||
<x-card>
|
||||
<x-slot name="title">{{ __("Delete Site") }}</x-slot>
|
||||
<x-slot name="description">{{ __("Permanently delete the site from server") }}</x-slot>
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user