mirror of
https://github.com/vitodeploy/vito.git
synced 2025-07-01 14:06:15 +00:00
Compare commits
19 Commits
Author | SHA1 | Date | |
---|---|---|---|
f51d7900f0 | |||
4bd4b34d24 | |||
7c5505be16 | |||
7249cf9ed6 | |||
8282d39722 | |||
38e23a1ceb | |||
1e1204fe40 | |||
7d98986f52 | |||
2c81e324f6 | |||
422da431ec | |||
6c27215ea1 | |||
fb840204ad | |||
e07e197dd9 | |||
3a2dba4ad3 | |||
b990f9ce32 | |||
a6727ff459 | |||
170535760f | |||
648529c3bd | |||
84192b7cb7 |
38
.env.example
38
.env.example
@ -2,7 +2,7 @@ APP_NAME=Vito
|
||||
APP_ENV=local
|
||||
APP_KEY=
|
||||
APP_DEBUG=true
|
||||
APP_URL=http://localhost:2080
|
||||
APP_URL=http://vito.test
|
||||
|
||||
LOG_CHANNEL=stack
|
||||
LOG_LEVEL=debug
|
||||
@ -15,14 +15,12 @@ DB_USERNAME=root
|
||||
DB_PASSWORD=
|
||||
|
||||
BROADCAST_DRIVER=null
|
||||
CACHE_DRIVER=redis
|
||||
CACHE_DRIVER=file
|
||||
FILESYSTEM_DRIVER=local
|
||||
QUEUE_CONNECTION=sync
|
||||
QUEUE_CONNECTION=default
|
||||
SESSION_DRIVER=database
|
||||
SESSION_LIFETIME=120
|
||||
|
||||
MEMCACHED_HOST=127.0.0.1
|
||||
|
||||
REDIS_HOST=127.0.0.1
|
||||
REDIS_PASSWORD=null
|
||||
REDIS_PORT=6379
|
||||
@ -36,31 +34,5 @@ MAIL_ENCRYPTION=null
|
||||
MAIL_FROM_ADDRESS=null
|
||||
MAIL_FROM_NAME="${APP_NAME}"
|
||||
|
||||
AWS_ACCESS_KEY_ID=
|
||||
AWS_SECRET_ACCESS_KEY=
|
||||
AWS_DEFAULT_REGION=us-east-1
|
||||
AWS_BUCKET=
|
||||
AWS_USE_PATH_STYLE_ENDPOINT=false
|
||||
|
||||
PUSHER_APP_ID=
|
||||
PUSHER_APP_KEY=
|
||||
PUSHER_APP_SECRET=
|
||||
PUSHER_APP_CLUSTER=mt1
|
||||
|
||||
MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
|
||||
MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
|
||||
|
||||
FORWARD_REDIS_PORT=2060
|
||||
FORWARD_DB_PORT=2070
|
||||
APP_PORT=2080
|
||||
HMR_PORT=2090
|
||||
|
||||
SENTRY_LARAVEL_DSN=
|
||||
|
||||
APP_SERVICE=vito
|
||||
|
||||
GITHUB_CLIENT_ID=
|
||||
GITHUB_CLIENT_SECRET=
|
||||
|
||||
ABLY_KEY=
|
||||
ABLY_PUBLIC_KEY=
|
||||
SSH_PUBLIC_KEY_NAME=ssh-public.key
|
||||
SSH_PRIVATE_KEY_NAME=ssh-private.pem
|
||||
|
28
.env.prod
28
.env.prod
@ -14,7 +14,7 @@ DB_DATABASE=
|
||||
DB_USERNAME=
|
||||
DB_PASSWORD=
|
||||
|
||||
BROADCAST_DRIVER=pusher
|
||||
BROADCAST_DRIVER=null
|
||||
CACHE_DRIVER=file
|
||||
FILESYSTEM_DRIVER=local
|
||||
QUEUE_CONNECTION=default
|
||||
@ -34,31 +34,5 @@ MAIL_ENCRYPTION=null
|
||||
MAIL_FROM_ADDRESS=null
|
||||
MAIL_FROM_NAME="${APP_NAME}"
|
||||
|
||||
AWS_ACCESS_KEY_ID=
|
||||
AWS_SECRET_ACCESS_KEY=
|
||||
AWS_DEFAULT_REGION=us-east-1
|
||||
AWS_BUCKET=
|
||||
AWS_USE_PATH_STYLE_ENDPOINT=false
|
||||
|
||||
PUSHER_APP_ID=app-id
|
||||
PUSHER_APP_KEY=app-key
|
||||
PUSHER_APP_SECRET=app-secret
|
||||
PUSHER_HOST=soketi
|
||||
PUSHER_PORT=6001
|
||||
PUSHER_SCHEME=http
|
||||
PUSHER_APP_CLUSTER=mt1
|
||||
|
||||
VITE_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
|
||||
VITE_PUSHER_HOST="${PUSHER_HOST}"
|
||||
VITE_PUSHER_PORT="${PUSHER_PORT}"
|
||||
VITE_PUSHER_SCHEME="${PUSHER_SCHEME}"
|
||||
VITE_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
|
||||
|
||||
SSH_PUBLIC_KEY_NAME=ssh-public.key
|
||||
SSH_PRIVATE_KEY_NAME=ssh-private.pem
|
||||
|
||||
GITHUB_CLIENT_ID=
|
||||
GITHUB_CLIENT_SECRET=
|
||||
|
||||
ABLY_KEY=
|
||||
ABLY_PUBLIC_KEY=
|
||||
|
45
.env.sail
Normal file
45
.env.sail
Normal file
@ -0,0 +1,45 @@
|
||||
APP_NAME=Vito
|
||||
APP_ENV=local
|
||||
APP_KEY=
|
||||
APP_DEBUG=true
|
||||
APP_URL=http://vito.test
|
||||
|
||||
LOG_CHANNEL=stack
|
||||
LOG_LEVEL=debug
|
||||
|
||||
DB_CONNECTION=mysql
|
||||
DB_HOST=mysql
|
||||
DB_PORT=3306
|
||||
DB_DATABASE=vito
|
||||
DB_USERNAME=sail
|
||||
DB_PASSWORD=password
|
||||
|
||||
BROADCAST_DRIVER=null
|
||||
CACHE_DRIVER=redis
|
||||
FILESYSTEM_DRIVER=local
|
||||
QUEUE_CONNECTION=default
|
||||
SESSION_DRIVER=database
|
||||
SESSION_LIFETIME=120
|
||||
|
||||
REDIS_HOST=redis
|
||||
REDIS_PASSWORD=null
|
||||
REDIS_PORT=6379
|
||||
|
||||
MAIL_MAILER=smtp
|
||||
MAIL_HOST=mailhog
|
||||
MAIL_PORT=1025
|
||||
MAIL_USERNAME=null
|
||||
MAIL_PASSWORD=null
|
||||
MAIL_ENCRYPTION=null
|
||||
MAIL_FROM_ADDRESS=null
|
||||
MAIL_FROM_NAME="${APP_NAME}"
|
||||
|
||||
SSH_PUBLIC_KEY_NAME=ssh-public.key
|
||||
SSH_PRIVATE_KEY_NAME=ssh-private.pem
|
||||
|
||||
APP_SERVICE=vito
|
||||
|
||||
FORWARD_REDIS_PORT=2060
|
||||
FORWARD_DB_PORT=2070
|
||||
APP_PORT=2080
|
||||
HMR_PORT=2090
|
16
.env.testing
16
.env.testing
@ -14,7 +14,7 @@ DB_DATABASE=vito_test
|
||||
DB_USERNAME=root
|
||||
DB_PASSWORD=
|
||||
|
||||
BROADCAST_DRIVER=pusher
|
||||
BROADCAST_DRIVER=null
|
||||
CACHE_DRIVER=array
|
||||
FILESYSTEM_DRIVER=local
|
||||
QUEUE_CONNECTION=database
|
||||
@ -23,19 +23,5 @@ SESSION_LIFETIME=120
|
||||
|
||||
MAIL_MAILER=array
|
||||
|
||||
PUSHER_APP_ID=app-id
|
||||
PUSHER_APP_KEY=app-key
|
||||
PUSHER_APP_SECRET=app-secret
|
||||
PUSHER_HOST=soketi
|
||||
PUSHER_PORT=6001
|
||||
PUSHER_SCHEME=http
|
||||
PUSHER_APP_CLUSTER=mt1
|
||||
|
||||
VITE_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
|
||||
VITE_PUSHER_HOST="${PUSHER_HOST}"
|
||||
VITE_PUSHER_PORT="${PUSHER_PORT}"
|
||||
VITE_PUSHER_SCHEME="${PUSHER_SCHEME}"
|
||||
VITE_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
|
||||
|
||||
SSH_PUBLIC_KEY_NAME=ssh-public.key
|
||||
SSH_PRIVATE_KEY_NAME=ssh-private.pem
|
||||
|
2
.github/workflows/code-style.yml
vendored
2
.github/workflows/code-style.yml
vendored
@ -8,7 +8,7 @@ on:
|
||||
|
||||
jobs:
|
||||
code-style:
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
strategy:
|
||||
fail-fast: true
|
||||
|
2
.github/workflows/tests.yml
vendored
2
.github/workflows/tests.yml
vendored
@ -8,7 +8,7 @@ on:
|
||||
|
||||
jobs:
|
||||
tests:
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
services:
|
||||
mysql:
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
@ -3,6 +3,7 @@
|
||||
namespace App\Actions\SourceControl;
|
||||
|
||||
use App\Models\SourceControl;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Illuminate\Validation\Rule;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
@ -16,6 +17,7 @@ public function connect(array $input): void
|
||||
'provider' => $input['provider'],
|
||||
'profile' => $input['name'],
|
||||
'access_token' => $input['token'],
|
||||
'url' => Arr::has($input, 'url') ? $input['url'] : null,
|
||||
]);
|
||||
|
||||
if (! $sourceControl->provider()->connect()) {
|
||||
@ -44,6 +46,11 @@ private function validate(array $input): void
|
||||
'token' => [
|
||||
'required',
|
||||
],
|
||||
'url' => [
|
||||
'nullable',
|
||||
'url:http,https',
|
||||
'ends_with:/',
|
||||
],
|
||||
];
|
||||
Validator::make($input, $rules)->validate();
|
||||
}
|
||||
|
@ -21,13 +21,15 @@ public function create(User $user, array $input): void
|
||||
'user_id' => $user->id,
|
||||
'provider' => $input['provider'],
|
||||
'profile' => $input['name'],
|
||||
'credentials' => [
|
||||
'token' => $input['token'],
|
||||
],
|
||||
]);
|
||||
|
||||
$this->validateProvider($input, $storageProvider->provider()->validationRules());
|
||||
|
||||
$storageProvider->credentials = $storageProvider->provider()->credentialData($input);
|
||||
|
||||
if (! $storageProvider->provider()->connect()) {
|
||||
throw ValidationException::withMessages([
|
||||
'token' => __("Couldn't connect to the provider"),
|
||||
'provider' => __("Couldn't connect to the provider"),
|
||||
]);
|
||||
}
|
||||
$storageProvider->save();
|
||||
@ -44,9 +46,11 @@ private function validate(User $user, array $input): void
|
||||
'required',
|
||||
Rule::unique('storage_providers', 'profile')->where('user_id', $user->id),
|
||||
],
|
||||
'token' => [
|
||||
'required',
|
||||
],
|
||||
])->validate();
|
||||
}
|
||||
|
||||
private function validateProvider(array $input, array $rules): void
|
||||
{
|
||||
Validator::make($input, $rules)->validate();
|
||||
}
|
||||
}
|
||||
|
@ -19,6 +19,10 @@ public function update(User $user, array $input): void
|
||||
Validator::make($input, [
|
||||
'name' => ['required', 'string', 'max:255'],
|
||||
'email' => ['required', 'email', 'max:255', Rule::unique('users')->ignore($user->id)],
|
||||
'timezone' => [
|
||||
'required',
|
||||
Rule::in(timezone_identifiers_list()),
|
||||
],
|
||||
])->validateWithBag('updateProfileInformation');
|
||||
|
||||
if ($input['email'] !== $user->email) {
|
||||
@ -27,6 +31,7 @@ public function update(User $user, array $input): void
|
||||
$user->forceFill([
|
||||
'name' => $input['name'],
|
||||
'email' => $input['email'],
|
||||
'timezone' => $input['timezone'],
|
||||
])->save();
|
||||
}
|
||||
}
|
||||
@ -39,6 +44,7 @@ protected function updateVerifiedUser(User $user, array $input): void
|
||||
$user->forceFill([
|
||||
'name' => $input['name'],
|
||||
'email' => $input['email'],
|
||||
'timezone' => $input['timezone'],
|
||||
])->save();
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,10 @@
|
||||
|
||||
interface StorageProvider
|
||||
{
|
||||
public function validationRules(): array;
|
||||
|
||||
public function credentialData(array $input): array;
|
||||
|
||||
public function connect(): bool;
|
||||
|
||||
public function upload(Server $server, string $src, string $dest): array;
|
||||
|
@ -6,7 +6,7 @@
|
||||
|
||||
final class StorageProvider extends Enum
|
||||
{
|
||||
const GOOGLE = 'google';
|
||||
|
||||
const DROPBOX = 'dropbox';
|
||||
|
||||
const FTP = 'ftp';
|
||||
}
|
||||
|
@ -15,10 +15,13 @@ class UpdateProfileInformation extends Component
|
||||
|
||||
public string $email;
|
||||
|
||||
public string $timezone;
|
||||
|
||||
public function mount(): void
|
||||
{
|
||||
$this->name = auth()->user()->name;
|
||||
$this->email = auth()->user()->email;
|
||||
$this->timezone = auth()->user()->timezone;
|
||||
}
|
||||
|
||||
/**
|
||||
|
31
app/Http/Livewire/Services/InstallPHPMyAdmin.php
Normal file
31
app/Http/Livewire/Services/InstallPHPMyAdmin.php
Normal file
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Livewire\Services;
|
||||
|
||||
use App\Actions\Service\InstallPHPMyAdmin as InstallPHPMyAdminAction;
|
||||
use App\Models\Server;
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Livewire\Component;
|
||||
|
||||
class InstallPHPMyAdmin extends Component
|
||||
{
|
||||
public Server $server;
|
||||
|
||||
public string $allowed_ip;
|
||||
|
||||
public string $port = '5433';
|
||||
|
||||
public function install(): void
|
||||
{
|
||||
app(InstallPHPMyAdminAction::class)->install($this->server, $this->all());
|
||||
|
||||
$this->dispatchBrowserEvent('started', true);
|
||||
|
||||
$this->emitTo(ServicesList::class, '$refresh');
|
||||
}
|
||||
|
||||
public function render(): View
|
||||
{
|
||||
return view('livewire.services.install-phpmyadmin');
|
||||
}
|
||||
}
|
@ -3,6 +3,7 @@
|
||||
namespace App\Http\Livewire\Services;
|
||||
|
||||
use App\Models\Server;
|
||||
use App\Models\Service;
|
||||
use App\Traits\RefreshComponentOnBroadcast;
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Livewire\Component;
|
||||
@ -15,6 +16,7 @@ class ServicesList extends Component
|
||||
|
||||
public function stop(int $id): void
|
||||
{
|
||||
/** @var Service $service */
|
||||
$service = $this->server->services()->where('id', $id)->firstOrFail();
|
||||
|
||||
$service->stop();
|
||||
@ -24,6 +26,7 @@ public function stop(int $id): void
|
||||
|
||||
public function start(int $id): void
|
||||
{
|
||||
/** @var Service $service */
|
||||
$service = $this->server->services()->where('id', $id)->firstOrFail();
|
||||
|
||||
$service->start();
|
||||
@ -33,6 +36,7 @@ public function start(int $id): void
|
||||
|
||||
public function restart(int $id): void
|
||||
{
|
||||
/** @var Service $service */
|
||||
$service = $this->server->services()->where('id', $id)->firstOrFail();
|
||||
|
||||
$service->restart();
|
||||
@ -40,6 +44,16 @@ public function restart(int $id): void
|
||||
$this->refreshComponent([]);
|
||||
}
|
||||
|
||||
public function uninstall(int $id): void
|
||||
{
|
||||
/** @var Service $service */
|
||||
$service = $this->server->services()->where('id', $id)->firstOrFail();
|
||||
|
||||
$service->uninstall();
|
||||
|
||||
$this->refreshComponent([]);
|
||||
}
|
||||
|
||||
public function render(): View
|
||||
{
|
||||
return view('livewire.services.services-list', [
|
||||
|
@ -14,6 +14,20 @@ class ConnectProvider extends Component
|
||||
|
||||
public string $token;
|
||||
|
||||
public string $host;
|
||||
|
||||
public string $port;
|
||||
|
||||
public string $path = '/';
|
||||
|
||||
public string $username;
|
||||
|
||||
public string $password;
|
||||
|
||||
public int $ssl = 1;
|
||||
|
||||
public int $passive = 0;
|
||||
|
||||
public function connect(): void
|
||||
{
|
||||
app(CreateStorageProvider::class)->create(auth()->user(), $this->all());
|
||||
|
@ -3,6 +3,7 @@
|
||||
namespace App\Jobs\Installation;
|
||||
|
||||
use App\Actions\FirewallRule\CreateRule;
|
||||
use App\Enums\ServiceStatus;
|
||||
use App\Jobs\Job;
|
||||
use App\Models\FirewallRule;
|
||||
use App\Models\Service;
|
||||
@ -32,6 +33,9 @@ public function handle(): void
|
||||
$this->downloadSource();
|
||||
$this->setUpVHost();
|
||||
$this->restartPHP();
|
||||
$this->service->update([
|
||||
'status' => ServiceStatus::READY,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -41,7 +45,7 @@ private function setUpFirewall(): void
|
||||
{
|
||||
$this->firewallRule = FirewallRule::query()
|
||||
->where('server_id', $this->service->server_id)
|
||||
->where('port', '54331')
|
||||
->where('port', $this->service->type_data['port'])
|
||||
->first();
|
||||
if ($this->firewallRule) {
|
||||
$this->firewallRule->source = $this->service->type_data['allowed_ip'];
|
||||
@ -52,7 +56,7 @@ private function setUpFirewall(): void
|
||||
[
|
||||
'type' => 'allow',
|
||||
'protocol' => 'tcp',
|
||||
'port' => '54331',
|
||||
'port' => $this->service->type_data['port'],
|
||||
'source' => $this->service->type_data['allowed_ip'],
|
||||
'mask' => '0',
|
||||
]
|
||||
@ -78,6 +82,7 @@ private function setUpVHost(): void
|
||||
{
|
||||
$vhost = File::get(resource_path('commands/webserver/nginx/phpmyadmin-vhost.conf'));
|
||||
$vhost = Str::replace('__php_version__', $this->service->server->defaultService('php')->version, $vhost);
|
||||
$vhost = Str::replace('__port__', $this->service->type_data['port'], $vhost);
|
||||
$this->service->server->ssh()->exec(
|
||||
new CreateNginxPHPMyAdminVHostCommand($vhost),
|
||||
'create-phpmyadmin-vhost'
|
||||
@ -98,6 +103,9 @@ private function restartPHP(): void
|
||||
public function failed(Throwable $throwable): Throwable
|
||||
{
|
||||
$this->firewallRule?->removeFromServer();
|
||||
$this->service->update([
|
||||
'status' => ServiceStatus::INSTALLATION_FAILED,
|
||||
]);
|
||||
throw $throwable;
|
||||
}
|
||||
}
|
||||
|
@ -37,7 +37,7 @@ private function removeFirewallRule(): void
|
||||
/** @var ?FirewallRule $rule */
|
||||
$rule = FirewallRule::query()
|
||||
->where('server_id', $this->service->server_id)
|
||||
->where('port', '54331')
|
||||
->where('port', $this->service->type_data['port'])
|
||||
->first();
|
||||
$rule?->removeFromServer();
|
||||
}
|
||||
|
@ -3,13 +3,12 @@
|
||||
namespace App\Jobs;
|
||||
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldBeUnique;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
abstract class Job implements ShouldQueue, ShouldBeUnique
|
||||
abstract class Job implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
}
|
||||
|
@ -4,7 +4,6 @@
|
||||
|
||||
use App\Enums\DeploymentStatus;
|
||||
use App\Events\Broadcast;
|
||||
use App\Helpers\SSH;
|
||||
use App\Jobs\Job;
|
||||
use App\Models\Deployment;
|
||||
use App\SSHCommands\System\RunScriptCommand;
|
||||
@ -18,13 +17,11 @@ class Deploy extends Job
|
||||
|
||||
protected string $script;
|
||||
|
||||
protected SSH $ssh;
|
||||
|
||||
public function __construct(Deployment $deployment, string $path, string $script)
|
||||
public function __construct(Deployment $deployment, string $path)
|
||||
{
|
||||
$this->script = $deployment->deploymentScript->content;
|
||||
$this->deployment = $deployment;
|
||||
$this->path = $path;
|
||||
$this->script = $script;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -32,28 +29,31 @@ public function __construct(Deployment $deployment, string $path, string $script
|
||||
*/
|
||||
public function handle(): void
|
||||
{
|
||||
$this->ssh = $this->deployment->site->server->ssh();
|
||||
$this->ssh->exec(
|
||||
$ssh = $this->deployment->site->server->ssh();
|
||||
try {
|
||||
$ssh->exec(
|
||||
new RunScriptCommand($this->path, $this->script),
|
||||
'deploy',
|
||||
$this->deployment->site_id
|
||||
);
|
||||
$this->deployment->status = DeploymentStatus::FINISHED;
|
||||
$this->deployment->log_id = $this->ssh->log->id;
|
||||
$this->deployment->log_id = $ssh->log->id;
|
||||
$this->deployment->save();
|
||||
event(
|
||||
new Broadcast('deploy-site-finished', [
|
||||
'deployment' => $this->deployment,
|
||||
])
|
||||
);
|
||||
} catch (Throwable) {
|
||||
$this->deployment->log_id = $ssh->log->id;
|
||||
$this->deployment->save();
|
||||
$this->failed();
|
||||
}
|
||||
}
|
||||
|
||||
public function failed(): void
|
||||
{
|
||||
$this->deployment->status = DeploymentStatus::FAILED;
|
||||
if ($this->ssh->log) {
|
||||
$this->deployment->log_id = $this->ssh->log->id;
|
||||
}
|
||||
$this->deployment->save();
|
||||
event(
|
||||
new Broadcast('deploy-site-failed', [
|
||||
|
@ -13,7 +13,7 @@ public function __construct()
|
||||
|
||||
public function handle(Broadcast $event): void
|
||||
{
|
||||
Cache::set('broadcast', [
|
||||
Cache::put('broadcast', [
|
||||
'type' => $event->type,
|
||||
'data' => $event->data,
|
||||
], now()->addMinutes(5));
|
||||
|
@ -76,7 +76,7 @@ public function getPathAttribute(): string
|
||||
|
||||
public function getStoragePathAttribute(): string
|
||||
{
|
||||
return '/'.$this->backup->database->name.'/'.$this->name.'.zip';
|
||||
return '/'.$this->name.'.zip';
|
||||
}
|
||||
|
||||
public function restore(Database $database): void
|
||||
|
@ -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;
|
||||
|
@ -266,13 +266,7 @@ public function deploy(): Deployment
|
||||
}
|
||||
$deployment->save();
|
||||
|
||||
dispatch(
|
||||
new Deploy(
|
||||
$deployment,
|
||||
$this->path,
|
||||
$this->deployment_script_text
|
||||
)
|
||||
)->onConnection('ssh');
|
||||
dispatch(new Deploy($deployment, $this->path))->onConnection('ssh');
|
||||
|
||||
return $deployment;
|
||||
}
|
||||
|
40
app/SSHCommands/Storage/DownloadFromFTPCommand.php
Normal file
40
app/SSHCommands/Storage/DownloadFromFTPCommand.php
Normal file
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace App\SSHCommands\Storage;
|
||||
|
||||
use App\SSHCommands\Command;
|
||||
use Illuminate\Support\Facades\File;
|
||||
|
||||
class DownloadFromFTPCommand extends Command
|
||||
{
|
||||
public function __construct(
|
||||
protected string $src,
|
||||
protected string $dest,
|
||||
protected string $host,
|
||||
protected string $port,
|
||||
protected string $username,
|
||||
protected string $password,
|
||||
protected bool $ssl,
|
||||
protected bool $passive,
|
||||
) {
|
||||
}
|
||||
|
||||
public function file(): string
|
||||
{
|
||||
return File::get(resource_path('commands/storage/download-from-ftp.sh'));
|
||||
}
|
||||
|
||||
public function content(): string
|
||||
{
|
||||
return str($this->file())
|
||||
->replace('__src__', $this->src)
|
||||
->replace('__dest__', $this->dest)
|
||||
->replace('__host__', $this->host)
|
||||
->replace('__port__', $this->port)
|
||||
->replace('__username__', $this->username)
|
||||
->replace('__password__', $this->password)
|
||||
->replace('__ssl__', $this->ssl ? 's' : '')
|
||||
->replace('__passive__', $this->passive ? '--ftp-pasv' : '')
|
||||
->toString();
|
||||
}
|
||||
}
|
40
app/SSHCommands/Storage/UploadToFTPCommand.php
Normal file
40
app/SSHCommands/Storage/UploadToFTPCommand.php
Normal file
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace App\SSHCommands\Storage;
|
||||
|
||||
use App\SSHCommands\Command;
|
||||
use Illuminate\Support\Facades\File;
|
||||
|
||||
class UploadToFTPCommand extends Command
|
||||
{
|
||||
public function __construct(
|
||||
protected string $src,
|
||||
protected string $dest,
|
||||
protected string $host,
|
||||
protected string $port,
|
||||
protected string $username,
|
||||
protected string $password,
|
||||
protected bool $ssl,
|
||||
protected bool $passive,
|
||||
) {
|
||||
}
|
||||
|
||||
public function file(): string
|
||||
{
|
||||
return File::get(resource_path('commands/storage/upload-to-ftp.sh'));
|
||||
}
|
||||
|
||||
public function content(): string
|
||||
{
|
||||
return str($this->file())
|
||||
->replace('__src__', $this->src)
|
||||
->replace('__dest__', $this->dest)
|
||||
->replace('__host__', $this->host)
|
||||
->replace('__port__', $this->port)
|
||||
->replace('__username__', $this->username)
|
||||
->replace('__password__', $this->password)
|
||||
->replace('__ssl__', $this->ssl ? 's' : '')
|
||||
->replace('__passive__', $this->passive ? '--ftp-pasv' : '')
|
||||
->toString();
|
||||
}
|
||||
}
|
@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\ServerTypes;
|
||||
|
||||
use App\Enums\ServerStatus;
|
||||
use App\Events\Broadcast;
|
||||
use App\Jobs\Installation\Initialize;
|
||||
use App\Jobs\Installation\InstallCertbot;
|
||||
@ -79,7 +80,7 @@ public function install(): void
|
||||
|
||||
$jobs[] = function () {
|
||||
$this->server->update([
|
||||
'status' => 'ready',
|
||||
'status' => ServerStatus::READY,
|
||||
'progress' => 100,
|
||||
]);
|
||||
event(
|
||||
|
@ -10,12 +10,14 @@
|
||||
|
||||
class Gitlab extends AbstractSourceControlProvider
|
||||
{
|
||||
protected string $apiUrl = 'https://gitlab.com/api/v4';
|
||||
protected string $defaultApiHost = 'https://gitlab.com/';
|
||||
|
||||
protected string $apiVersion = 'api/v4';
|
||||
|
||||
public function connect(): bool
|
||||
{
|
||||
$res = Http::withToken($this->sourceControl->access_token)
|
||||
->get($this->apiUrl.'/projects');
|
||||
->get($this->getApiUrl().'/projects');
|
||||
|
||||
return $res->successful();
|
||||
}
|
||||
@ -27,7 +29,7 @@ public function getRepo(string $repo = null): mixed
|
||||
{
|
||||
$repository = $repo ? urlencode($repo) : null;
|
||||
$res = Http::withToken($this->sourceControl->access_token)
|
||||
->get($this->apiUrl.'/projects/'.$repository.'/repository/commits');
|
||||
->get($this->getApiUrl().'/projects/'.$repository.'/repository/commits');
|
||||
|
||||
$this->handleResponseErrors($res, $repo);
|
||||
|
||||
@ -36,7 +38,9 @@ public function getRepo(string $repo = null): mixed
|
||||
|
||||
public function fullRepoUrl(string $repo, string $key): string
|
||||
{
|
||||
return sprintf('git@gitlab.com-%s:%s.git', $key, $repo);
|
||||
$host = parse_url($this->getApiUrl())['host'];
|
||||
|
||||
return sprintf('git@%s-%s:%s.git', $host, $key, $repo);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -46,7 +50,7 @@ public function deployHook(string $repo, array $events, string $secret): array
|
||||
{
|
||||
$repository = urlencode($repo);
|
||||
$response = Http::withToken($this->sourceControl->access_token)->post(
|
||||
$this->apiUrl.'/projects/'.$repository.'/hooks',
|
||||
$this->getApiUrl().'/projects/'.$repository.'/hooks',
|
||||
[
|
||||
'description' => 'deploy',
|
||||
'url' => url('/git-hooks?secret='.$secret),
|
||||
@ -81,7 +85,7 @@ public function destroyHook(string $repo, string $hookId): void
|
||||
{
|
||||
$repository = urlencode($repo);
|
||||
$response = Http::withToken($this->sourceControl->access_token)->delete(
|
||||
$this->apiUrl.'/projects/'.$repository.'/hooks/'.$hookId
|
||||
$this->getApiUrl().'/projects/'.$repository.'/hooks/'.$hookId
|
||||
);
|
||||
|
||||
if ($response->status() != 204) {
|
||||
@ -96,7 +100,7 @@ public function getLastCommit(string $repo, string $branch): ?array
|
||||
{
|
||||
$repository = urlencode($repo);
|
||||
$res = Http::withToken($this->sourceControl->access_token)
|
||||
->get($this->apiUrl.'/projects/'.$repository.'/repository/commits?ref_name='.$branch);
|
||||
->get($this->getApiUrl().'/projects/'.$repository.'/repository/commits?ref_name='.$branch);
|
||||
|
||||
$this->handleResponseErrors($res, $repo);
|
||||
|
||||
@ -123,7 +127,7 @@ public function deployKey(string $title, string $repo, string $key): void
|
||||
{
|
||||
$repository = urlencode($repo);
|
||||
$response = Http::withToken($this->sourceControl->access_token)->post(
|
||||
$this->apiUrl.'/projects/'.$repository.'/deploy_keys',
|
||||
$this->getApiUrl().'/projects/'.$repository.'/deploy_keys',
|
||||
[
|
||||
'title' => $title,
|
||||
'key' => $key,
|
||||
@ -135,4 +139,13 @@ public function deployKey(string $title, string $repo, string $key): void
|
||||
throw new FailedToDeployGitKey(json_decode($response->body())->message);
|
||||
}
|
||||
}
|
||||
|
||||
public function getApiUrl(): string
|
||||
{
|
||||
$host = $this->sourceControl->url === null
|
||||
? $this->defaultApiHost
|
||||
: $this->sourceControl->url;
|
||||
|
||||
return $host.$this->apiVersion;
|
||||
}
|
||||
}
|
||||
|
@ -13,6 +13,20 @@ class Dropbox extends AbstractStorageProvider
|
||||
{
|
||||
protected string $apiUrl = 'https://api.dropboxapi.com/2';
|
||||
|
||||
public function validationRules(): array
|
||||
{
|
||||
return [
|
||||
'token' => 'required',
|
||||
];
|
||||
}
|
||||
|
||||
public function credentialData(array $input): array
|
||||
{
|
||||
return [
|
||||
'token' => $input['token'],
|
||||
];
|
||||
}
|
||||
|
||||
public function connect(): bool
|
||||
{
|
||||
$res = Http::withToken($this->storageProvider->credentials['token'])
|
||||
|
129
app/StorageProviders/FTP.php
Normal file
129
app/StorageProviders/FTP.php
Normal file
@ -0,0 +1,129 @@
|
||||
<?php
|
||||
|
||||
namespace App\StorageProviders;
|
||||
|
||||
use App\Models\Server;
|
||||
use App\SSHCommands\Storage\DownloadFromFTPCommand;
|
||||
use App\SSHCommands\Storage\UploadToFTPCommand;
|
||||
use FTP\Connection;
|
||||
use Throwable;
|
||||
|
||||
class FTP extends AbstractStorageProvider
|
||||
{
|
||||
public function validationRules(): array
|
||||
{
|
||||
return [
|
||||
'host' => 'required',
|
||||
'port' => 'required|numeric',
|
||||
'path' => 'required',
|
||||
'username' => 'required',
|
||||
'password' => 'required',
|
||||
'ssl' => 'required',
|
||||
'passive' => 'required',
|
||||
];
|
||||
}
|
||||
|
||||
public function credentialData(array $input): array
|
||||
{
|
||||
return [
|
||||
'host' => $input['host'],
|
||||
'port' => $input['port'],
|
||||
'path' => $input['path'],
|
||||
'username' => $input['username'],
|
||||
'password' => $input['password'],
|
||||
'ssl' => (bool) $input['ssl'],
|
||||
'passive' => (bool) $input['passive'],
|
||||
];
|
||||
}
|
||||
|
||||
public function connect(): bool
|
||||
{
|
||||
$connection = $this->connection();
|
||||
|
||||
$isConnected = $connection && $this->login($connection);
|
||||
|
||||
if ($isConnected) {
|
||||
ftp_close($connection);
|
||||
}
|
||||
|
||||
return $isConnected;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function upload(Server $server, string $src, string $dest): array
|
||||
{
|
||||
$server->ssh()->exec(
|
||||
new UploadToFTPCommand(
|
||||
$src,
|
||||
$this->storageProvider->credentials['path'].'/'.$dest,
|
||||
$this->storageProvider->credentials['host'],
|
||||
$this->storageProvider->credentials['port'],
|
||||
$this->storageProvider->credentials['username'],
|
||||
$this->storageProvider->credentials['password'],
|
||||
$this->storageProvider->credentials['ssl'],
|
||||
$this->storageProvider->credentials['passive'],
|
||||
),
|
||||
'upload-to-ftp'
|
||||
);
|
||||
|
||||
return [
|
||||
'size' => null,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function download(Server $server, string $src, string $dest): void
|
||||
{
|
||||
$server->ssh()->exec(
|
||||
new DownloadFromFTPCommand(
|
||||
$this->storageProvider->credentials['path'].'/'.$src,
|
||||
$dest,
|
||||
$this->storageProvider->credentials['host'],
|
||||
$this->storageProvider->credentials['port'],
|
||||
$this->storageProvider->credentials['username'],
|
||||
$this->storageProvider->credentials['password'],
|
||||
$this->storageProvider->credentials['ssl'],
|
||||
$this->storageProvider->credentials['passive'],
|
||||
),
|
||||
'download-from-ftp'
|
||||
);
|
||||
}
|
||||
|
||||
public function delete(array $paths): void
|
||||
{
|
||||
$connection = $this->connection();
|
||||
|
||||
if ($connection && $this->login($connection)) {
|
||||
if ($this->storageProvider->credentials['passive']) {
|
||||
ftp_pasv($connection, true);
|
||||
}
|
||||
|
||||
foreach ($paths as $path) {
|
||||
ftp_delete($connection, $this->storageProvider->credentials['path'].'/'.$path);
|
||||
}
|
||||
}
|
||||
|
||||
ftp_close($connection);
|
||||
}
|
||||
|
||||
private function connection(): bool|Connection
|
||||
{
|
||||
$credentials = $this->storageProvider->credentials;
|
||||
if ($credentials['ssl']) {
|
||||
return ftp_ssl_connect($credentials['host'], $credentials['port'], 5);
|
||||
}
|
||||
|
||||
return ftp_connect($credentials['host'], $credentials['port'], 5);
|
||||
}
|
||||
|
||||
private function login(Connection $connection): bool
|
||||
{
|
||||
$credentials = $this->storageProvider->credentials;
|
||||
|
||||
return ftp_login($connection, $credentials['username'], $credentials['password']);
|
||||
}
|
||||
}
|
@ -16,7 +16,7 @@
|
||||
"laravel/tinker": "^2.8",
|
||||
"livewire/livewire": "^2.12",
|
||||
"phpseclib/phpseclib": "~3.0",
|
||||
"pusher/pusher-php-server": "^7.2"
|
||||
"ext-ftp": "*"
|
||||
},
|
||||
"require-dev": {
|
||||
"fakerphp/faker": "^1.9.1",
|
||||
|
164
composer.lock
generated
164
composer.lock
generated
@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "0f725da8271d545b318e319f7b616053",
|
||||
"content-hash": "53be6925a69aeafb21d079b82e51c1c4",
|
||||
"packages": [
|
||||
{
|
||||
"name": "aws/aws-crt-php",
|
||||
@ -1586,16 +1586,16 @@
|
||||
},
|
||||
{
|
||||
"name": "laravel/framework",
|
||||
"version": "v10.7.1",
|
||||
"version": "v10.16.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/laravel/framework.git",
|
||||
"reference": "ddbbb2b50388721fe63312bb4469cae13163fd36"
|
||||
"reference": "5c93d2795c393b462481179ce42dedfb30cc19b5"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/laravel/framework/zipball/ddbbb2b50388721fe63312bb4469cae13163fd36",
|
||||
"reference": "ddbbb2b50388721fe63312bb4469cae13163fd36",
|
||||
"url": "https://api.github.com/repos/laravel/framework/zipball/5c93d2795c393b462481179ce42dedfb30cc19b5",
|
||||
"reference": "5c93d2795c393b462481179ce42dedfb30cc19b5",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -1782,7 +1782,7 @@
|
||||
"issues": "https://github.com/laravel/framework/issues",
|
||||
"source": "https://github.com/laravel/framework"
|
||||
},
|
||||
"time": "2023-04-11T14:11:49+00:00"
|
||||
"time": "2023-07-26T03:30:46+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laravel/sanctum",
|
||||
@ -3198,92 +3198,6 @@
|
||||
},
|
||||
"time": "2020-10-15T08:29:30+00:00"
|
||||
},
|
||||
{
|
||||
"name": "paragonie/sodium_compat",
|
||||
"version": "v1.19.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/paragonie/sodium_compat.git",
|
||||
"reference": "cb15e403ecbe6a6cc515f855c310eb6b1872a933"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/paragonie/sodium_compat/zipball/cb15e403ecbe6a6cc515f855c310eb6b1872a933",
|
||||
"reference": "cb15e403ecbe6a6cc515f855c310eb6b1872a933",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"paragonie/random_compat": ">=1",
|
||||
"php": "^5.2.4|^5.3|^5.4|^5.5|^5.6|^7|^8"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^3|^4|^5|^6|^7|^8|^9"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-libsodium": "PHP < 7.0: Better performance, password hashing (Argon2i), secure memory management (memzero), and better security.",
|
||||
"ext-sodium": "PHP >= 7.0: Better performance, password hashing (Argon2i), secure memory management (memzero), and better security."
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"files": [
|
||||
"autoload.php"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"ISC"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Paragon Initiative Enterprises",
|
||||
"email": "security@paragonie.com"
|
||||
},
|
||||
{
|
||||
"name": "Frank Denis",
|
||||
"email": "jedisct1@pureftpd.org"
|
||||
}
|
||||
],
|
||||
"description": "Pure PHP implementation of libsodium; uses the PHP extension if it exists",
|
||||
"keywords": [
|
||||
"Authentication",
|
||||
"BLAKE2b",
|
||||
"ChaCha20",
|
||||
"ChaCha20-Poly1305",
|
||||
"Chapoly",
|
||||
"Curve25519",
|
||||
"Ed25519",
|
||||
"EdDSA",
|
||||
"Edwards-curve Digital Signature Algorithm",
|
||||
"Elliptic Curve Diffie-Hellman",
|
||||
"Poly1305",
|
||||
"Pure-PHP cryptography",
|
||||
"RFC 7748",
|
||||
"RFC 8032",
|
||||
"Salpoly",
|
||||
"Salsa20",
|
||||
"X25519",
|
||||
"XChaCha20-Poly1305",
|
||||
"XSalsa20-Poly1305",
|
||||
"Xchacha20",
|
||||
"Xsalsa20",
|
||||
"aead",
|
||||
"cryptography",
|
||||
"ecdh",
|
||||
"elliptic curve",
|
||||
"elliptic curve cryptography",
|
||||
"encryption",
|
||||
"libsodium",
|
||||
"php",
|
||||
"public-key cryptography",
|
||||
"secret-key cryptography",
|
||||
"side-channel resistant"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/paragonie/sodium_compat/issues",
|
||||
"source": "https://github.com/paragonie/sodium_compat/tree/v1.19.0"
|
||||
},
|
||||
"time": "2022-09-26T03:40:35+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpoption/phpoption",
|
||||
"version": "1.9.1",
|
||||
@ -3961,67 +3875,6 @@
|
||||
},
|
||||
"time": "2023-04-07T21:57:09+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pusher/pusher-php-server",
|
||||
"version": "7.2.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pusher/pusher-http-php.git",
|
||||
"reference": "4ace4873873b06c25cecb2dd6d9fdcbf2f20b640"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pusher/pusher-http-php/zipball/4ace4873873b06c25cecb2dd6d9fdcbf2f20b640",
|
||||
"reference": "4ace4873873b06c25cecb2dd6d9fdcbf2f20b640",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-curl": "*",
|
||||
"ext-json": "*",
|
||||
"guzzlehttp/guzzle": "^7.2",
|
||||
"paragonie/sodium_compat": "^1.6",
|
||||
"php": "^7.3|^8.0",
|
||||
"psr/log": "^1.0|^2.0|^3.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"overtrue/phplint": "^2.3",
|
||||
"phpunit/phpunit": "^9.3"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "5.0-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Pusher\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"description": "Library for interacting with the Pusher REST API",
|
||||
"keywords": [
|
||||
"events",
|
||||
"messaging",
|
||||
"php-pusher-server",
|
||||
"publish",
|
||||
"push",
|
||||
"pusher",
|
||||
"real time",
|
||||
"real-time",
|
||||
"realtime",
|
||||
"rest",
|
||||
"trigger"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/pusher/pusher-http-php/issues",
|
||||
"source": "https://github.com/pusher/pusher-http-php/tree/7.2.2"
|
||||
},
|
||||
"time": "2022-12-20T19:52:36+00:00"
|
||||
},
|
||||
{
|
||||
"name": "ralouphie/getallheaders",
|
||||
"version": "3.0.3",
|
||||
@ -9227,8 +9080,9 @@
|
||||
"prefer-stable": true,
|
||||
"prefer-lowest": false,
|
||||
"platform": {
|
||||
"php": "^8.1"
|
||||
"php": "^8.1",
|
||||
"ext-ftp": "*"
|
||||
},
|
||||
"platform-dev": [],
|
||||
"plugin-api-version": "2.3.0"
|
||||
"plugin-api-version": "2.2.0"
|
||||
}
|
||||
|
@ -30,14 +30,15 @@
|
||||
use App\SourceControlProviders\Github;
|
||||
use App\SourceControlProviders\Gitlab;
|
||||
use App\StorageProviders\Dropbox;
|
||||
use App\StorageProviders\FTP;
|
||||
|
||||
return [
|
||||
/*
|
||||
* SSH
|
||||
*/
|
||||
'ssh_user' => env('SSH_USER', 'vito'),
|
||||
'ssh_public_key_name' => env('SSH_PUBLIC_KEY_NAME'),
|
||||
'ssh_private_key_name' => env('SSH_PRIVATE_KEY_NAME'),
|
||||
'ssh_public_key_name' => env('SSH_PUBLIC_KEY_NAME', 'ssh-public.key'),
|
||||
'ssh_private_key_name' => env('SSH_PRIVATE_KEY_NAME', 'ssh-private.pem'),
|
||||
'logs_disk' => env('SERVER_LOGS_DISK', 'server-logs-local'),
|
||||
'key_pairs_disk' => env('KEY_PAIRS_DISK', 'key-pairs-local'),
|
||||
|
||||
@ -323,7 +324,6 @@
|
||||
'https' => 443,
|
||||
'mysql' => 3306,
|
||||
'ftp' => 21,
|
||||
'phpmyadmin' => 54331,
|
||||
'tcp' => '',
|
||||
'udp' => '',
|
||||
],
|
||||
@ -355,8 +355,10 @@
|
||||
*/
|
||||
'storage_providers' => [
|
||||
'dropbox',
|
||||
'ftp',
|
||||
],
|
||||
'storage_providers_class' => [
|
||||
'dropbox' => Dropbox::class,
|
||||
'ftp' => FTP::class,
|
||||
],
|
||||
];
|
||||
|
@ -13,7 +13,7 @@
|
||||
|
|
||||
*/
|
||||
|
||||
'default' => env('QUEUE_CONNECTION', 'sync'),
|
||||
'default' => env('QUEUE_CONNECTION', 'default'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
|
@ -139,85 +139,125 @@
|
||||
],
|
||||
],
|
||||
'images' => [
|
||||
'us-east-1' => [
|
||||
'ubuntu_18' => 'ami-0279c3b3186e54acd',
|
||||
'ubuntu_20' => 'ami-083654bd07b5da81d',
|
||||
],
|
||||
'us-east-2' => [
|
||||
'ubuntu_18' => 'ami-020db2c14939a8efb',
|
||||
'ubuntu_20' => 'ami-0629230e074c580f2',
|
||||
],
|
||||
'us-west-1' => [
|
||||
'ubuntu_18' => 'ami-083f68207d3376798',
|
||||
'ubuntu_20' => 'ami-053ac55bdcfe96e85',
|
||||
],
|
||||
'us-west-2' => [
|
||||
'ubuntu_18' => 'ami-09889d8d54f9e0a0e',
|
||||
'ubuntu_20' => 'ami-036d46416a34a611c',
|
||||
'af-south-1' => [
|
||||
'ubuntu_20' => 'ami-03684d4c2541e5333',
|
||||
'ubuntu_22' => 'ami-05759acc7d8973892',
|
||||
],
|
||||
'ap-east-1' => [
|
||||
'ubuntu_18' => 'ami-032c0a4bd39a5772c',
|
||||
'ubuntu_20' => 'ami-0a9c1cc3697104990',
|
||||
],
|
||||
'ap-south-1' => [
|
||||
'ubuntu_18' => 'ami-00782a7608c7fc226',
|
||||
'ubuntu_20' => 'ami-0567e0d2b4b2169ae',
|
||||
'ubuntu_20' => 'ami-0b19a97bf326b4931',
|
||||
'ubuntu_22' => 'ami-03490b1b7425e5fe3',
|
||||
],
|
||||
'ap-northeast-1' => [
|
||||
'ubuntu_18' => 'ami-085e9421f80dbe728',
|
||||
'ubuntu_20' => 'ami-036d0684fc96830ca',
|
||||
'ubuntu_20' => 'ami-0e25df74d27e028e6',
|
||||
'ubuntu_22' => 'ami-09a81b370b76de6a2',
|
||||
],
|
||||
'ap-northeast-2' => [
|
||||
'ubuntu_18' => 'ami-0252a84eb1d66c2a0',
|
||||
'ubuntu_20' => 'ami-0f8b8babb98cc66d0',
|
||||
'ubuntu_20' => 'ami-003a709e1e4ce3729',
|
||||
'ubuntu_22' => 'ami-086cae3329a3f7d75',
|
||||
],
|
||||
'ap-northeast-3' => [
|
||||
'ubuntu_20' => 'ami-06c1367bd83de7d47',
|
||||
'ubuntu_22' => 'ami-0690c54203f5f67da',
|
||||
],
|
||||
'ap-south-1' => [
|
||||
'ubuntu_20' => 'ami-0b88997c830e88c76',
|
||||
'ubuntu_22' => 'ami-0287a05f0ef0e9d9a',
|
||||
],
|
||||
'ap-south-2' => [
|
||||
'ubuntu_20' => 'ami-049e2ae605332dba6',
|
||||
'ubuntu_22' => 'ami-06fe902e167e67d33',
|
||||
],
|
||||
'ap-southeast-1' => [
|
||||
'ubuntu_18' => 'ami-0907c2c44ea451f84',
|
||||
'ubuntu_20' => 'ami-0fed77069cd5a6d6c',
|
||||
'ubuntu_20' => 'ami-0a6461ddb52e9db63',
|
||||
'ubuntu_22' => 'ami-078c1149d8ad719a7',
|
||||
],
|
||||
'ap-southeast-2' => [
|
||||
'ubuntu_18' => 'ami-00abf0511a7f4cee5',
|
||||
'ubuntu_20' => 'ami-0bf8b986de7e3c7ce',
|
||||
'ubuntu_20' => 'ami-0a9fb81cc3289919c',
|
||||
'ubuntu_22' => 'ami-0df4b2961410d4cff',
|
||||
],
|
||||
'ap-southeast-3' => [
|
||||
'ubuntu_20' => 'ami-05ee5bed682a3fff0',
|
||||
'ubuntu_22' => 'ami-0fb6d1fdeeea10488',
|
||||
],
|
||||
'ap-southeast-4' => [
|
||||
'ubuntu_20' => 'ami-02f9759882b112414',
|
||||
'ubuntu_22' => 'ami-043a030d3eeabec75',
|
||||
],
|
||||
'ca-central-1' => [
|
||||
'ubuntu_18' => 'ami-0e471deaa43652c4a',
|
||||
'ubuntu_20' => 'ami-0bb84e7329f4fa1f7',
|
||||
'ubuntu_20' => 'ami-0daaea212e620de87',
|
||||
'ubuntu_22' => 'ami-06873c81b882339ac',
|
||||
],
|
||||
'cn-north-1' => [
|
||||
'ubuntu_20' => 'ami-0c8bcac1fe3389a72',
|
||||
'ubuntu_22' => 'ami-0728a1a4cc9e07753',
|
||||
],
|
||||
'cn-northwest-1' => [
|
||||
'ubuntu_20' => 'ami-0415bfb3ea62e17c0',
|
||||
'ubuntu_22' => 'ami-05529cf859783e600',
|
||||
],
|
||||
'eu-central-1' => [
|
||||
'ubuntu_18' => 'ami-00d5e377dd7fad751',
|
||||
'ubuntu_20' => 'ami-0a49b025fffbbdac6',
|
||||
'ubuntu_20' => 'ami-0b369586722023326',
|
||||
'ubuntu_22' => 'ami-06dd92ecc74fdfb36',
|
||||
],
|
||||
'eu-west-1' => [
|
||||
'ubuntu_18' => 'ami-095b735dce49535b5',
|
||||
'ubuntu_20' => 'ami-08edbb0e85d6a0a07',
|
||||
],
|
||||
'eu-west-2' => [
|
||||
'ubuntu_18' => 'ami-008485ca60c91a0f3',
|
||||
'ubuntu_20' => 'ami-0fdf70ed5c34c5f52',
|
||||
],
|
||||
'eu-west-3' => [
|
||||
'ubuntu_18' => 'ami-0df7d9cc2767d16cd',
|
||||
'ubuntu_20' => 'ami-06d79c60d7454e2af',
|
||||
],
|
||||
'eu-south-1' => [
|
||||
'ubuntu_18' => 'ami-09f165dd6bd167be5',
|
||||
'ubuntu_20' => 'ami-0f8ce9c417115413d',
|
||||
'eu-central-2' => [
|
||||
'ubuntu_20' => 'ami-070c78d5ed65f11c8',
|
||||
'ubuntu_22' => 'ami-07cf963e6321c9e6a',
|
||||
],
|
||||
'eu-north-1' => [
|
||||
'ubuntu_18' => 'ami-038904f9024f34a0c',
|
||||
'ubuntu_20' => 'ami-0bd9c26722573e69b',
|
||||
'ubuntu_20' => 'ami-0c5863072fc83557e',
|
||||
'ubuntu_22' => 'ami-0fe8bec493a81c7da',
|
||||
],
|
||||
'eu-south-1' => [
|
||||
'ubuntu_20' => 'ami-0966ff128f1497260',
|
||||
'ubuntu_22' => 'ami-0b03947fd0ce0eed2',
|
||||
],
|
||||
'eu-south-2' => [
|
||||
'ubuntu_20' => 'ami-087296a5b46cb95ce',
|
||||
'ubuntu_22' => 'ami-03486abd2962c176f',
|
||||
],
|
||||
'eu-west-1' => [
|
||||
'ubuntu_20' => 'ami-0e3e7f215a53e2a86',
|
||||
'ubuntu_22' => 'ami-0694d931cee176e7d',
|
||||
],
|
||||
'eu-west-2' => [
|
||||
'ubuntu_20' => 'ami-0b22eee5ba6bb6772',
|
||||
'ubuntu_22' => 'ami-0505148b3591e4c07',
|
||||
],
|
||||
'eu-west-3' => [
|
||||
'ubuntu_20' => 'ami-0f14fa1f9c69f4111',
|
||||
'ubuntu_22' => 'ami-00983e8a26e4c9bd9',
|
||||
],
|
||||
'il-central-1' => [
|
||||
'ubuntu_20' => 'ami-0703881563bf5fab7',
|
||||
'ubuntu_22' => 'ami-03869c813f5a2e20c',
|
||||
],
|
||||
'me-central-1' => [
|
||||
'ubuntu_20' => 'ami-04a5bde3b044c7c21',
|
||||
'ubuntu_22' => 'ami-02168d82d5c12118f',
|
||||
],
|
||||
'me-south-1' => [
|
||||
'ubuntu_18' => 'ami-0ef669c57b73af73b',
|
||||
'ubuntu_20' => 'ami-0b4946d7420c44be4',
|
||||
'ubuntu_20' => 'ami-0165b692f5714e330',
|
||||
'ubuntu_22' => 'ami-0f8d2a6080634ee69',
|
||||
],
|
||||
'sa-east-1' => [
|
||||
'ubuntu_18' => 'ami-0ed2b3edeb28afa59',
|
||||
'ubuntu_20' => 'ami-0e66f5495b4efdd0f',
|
||||
'ubuntu_20' => 'ami-095ca107fb46b81e6',
|
||||
'ubuntu_22' => 'ami-0b6c2d49148000cd5',
|
||||
],
|
||||
'af-south-1' => [
|
||||
'ubuntu_18' => 'ami-0191bb2cf509687ee',
|
||||
'ubuntu_20' => 'ami-0ff86122fd4ad7208',
|
||||
'us-east-1' => [
|
||||
'ubuntu_20' => 'ami-0fe0238291c8e3f07',
|
||||
'ubuntu_22' => 'ami-0fc5d935ebf8bc3bc',
|
||||
],
|
||||
'us-east-2' => [
|
||||
'ubuntu_20' => 'ami-0b6968e5c7117349a',
|
||||
'ubuntu_22' => 'ami-0e83be366243f524a',
|
||||
],
|
||||
'us-west-1' => [
|
||||
'ubuntu_20' => 'ami-092efbcc9a2d2be8a',
|
||||
'ubuntu_22' => 'ami-0cbd40f694b804622',
|
||||
],
|
||||
'us-west-2' => [
|
||||
'ubuntu_20' => 'ami-0a55cdf919d10eac9',
|
||||
'ubuntu_22' => 'ami-0efcece6bed30fd98',
|
||||
],
|
||||
],
|
||||
],
|
||||
|
@ -17,4 +17,40 @@ public function definition(): array
|
||||
'access_token' => Str::random(10),
|
||||
];
|
||||
}
|
||||
|
||||
public function gitlab(): Factory
|
||||
{
|
||||
return $this->state(function (array $attributes) {
|
||||
return [
|
||||
'provider' => \App\Enums\SourceControl::GITLAB,
|
||||
];
|
||||
});
|
||||
}
|
||||
|
||||
public function github(): Factory
|
||||
{
|
||||
return $this->state(function (array $attributes) {
|
||||
return [
|
||||
'provider' => \App\Enums\SourceControl::GITHUB,
|
||||
];
|
||||
});
|
||||
}
|
||||
|
||||
public function bitbucket(): Factory
|
||||
{
|
||||
return $this->state(function (array $attributes) {
|
||||
return [
|
||||
'provider' => \App\Enums\SourceControl::BITBUCKET,
|
||||
];
|
||||
});
|
||||
}
|
||||
|
||||
public function custom(): Factory
|
||||
{
|
||||
return $this->state(function (array $attributes) {
|
||||
return [
|
||||
'provider' => \App\Enums\SourceControl::CUSTOM,
|
||||
];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
version: '3'
|
||||
services:
|
||||
app:
|
||||
vito:
|
||||
build:
|
||||
context: ./docker/8.1
|
||||
dockerfile: Dockerfile
|
||||
|
@ -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-46ad72d7.css
Normal file
1
public/build/assets/app-46ad72d7.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
21
public/build/assets/app-dfd48f80.js
Normal file
21
public/build/assets/app-dfd48f80.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-bdf134de.css",
|
||||
"file": "assets/app-46ad72d7.css",
|
||||
"isEntry": true,
|
||||
"src": "resources/css/app.css"
|
||||
},
|
||||
"resources/js/app.js": {
|
||||
"file": "assets/app-fa1f93fa.js",
|
||||
"file": "assets/app-dfd48f80.js",
|
||||
"isEntry": true,
|
||||
"src": "resources/js/app.js"
|
||||
}
|
||||
|
43
public/static/images/phpmyadmin.svg
Normal file
43
public/static/images/phpmyadmin.svg
Normal file
@ -0,0 +1,43 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" version="1.1" width="3890" height="2168" id="svg2">
|
||||
<metadata id="metadata8">
|
||||
<rdf:RDF>
|
||||
<cc:Work rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
|
||||
<dc:title/>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs id="defs6"/>
|
||||
<g id="g5" style="fill:#cccccc">
|
||||
<path d="m 2889.39,6.3480122 -2.04,-4.07 c -1.01,-1.01 -2.03,-2.04 -4.06,-2.04 l -4.08,1.03 c -2.03,2.03 -3.05,3.05 -3.05,5.08 L 2789.64,1572.695 l 13.24,-2.035 83.46,-1523.70199 99.75,163.88 -1.02,0 c 75.32,221.88 106.87,458.02 94.66,708.41003 l 6.11,9.16 98.73,175.05996 6.1,6.11 c 151.66,133.336 321.63,222.907 509.94,268.707 l 45.8,74.309 6.11,2.031 1.02,-2.031 -866.19,-1416.82599 2.04,-29.52" id="path14"/>
|
||||
<path d="m 2858.86,559.02801 c 106.87,218.84 145.54,549.62999 117.05,992.39199 l 416.29,-50.894 z" id="path18"/>
|
||||
<path d="m 3807.48,1520.881 c 42.74,-5.086 70.22,-14.246 82.44,-27.476 l -2178.16,265.656 -1.02,0 c 1.02,90.586 41.73,161.836 121.12,212.723 21.37,15.269 43.77,26.465 64.12,33.59 19.34,-22.387 40.72,-38.676 66.17,-53.946 l 1.01,0 c 228,-138.422 566.94,-159.8 1014.78,-65.136 l 5.09,1.011 c 48.86,10.18 97.71,22.399 143.52,36.645 13.22,2.035 24.42,-2.035 33.58,-10.18 16.29,-12.215 36.65,-21.375 64.13,-27.476 l 0,-1.02 c 72.27,-128.25 169.97,-222.906 292.12,-284.996 84.48,-41.727 182.19,-69.215 291.1,-79.395" id="path20"/>
|
||||
<path d="M 2761.15,1560.585 2860.89,31.798012 C 2716.36,589.56801 2484.29,1110.698 2165.71,1594.167 l 595.44,-33.582" id="path22"/>
|
||||
<path d="m 2200.32,329.008 -4.07,-2.04 -4.07,1.02 -2.04,5.08 -82.44,1323.18799 12.21,-1.019 82.45,-1322.169 -2.04,-4.06" id="path24"/>
|
||||
<path d="m 2084.29,1627.756 0,0 87.53,-1290.608 -461.08,1330.308 373.55,-39.7" id="path26"/>
|
||||
</g>
|
||||
<g id="g13" style="fill:none;stroke:#cccccc;stroke-width:12;stroke-linecap:round;stroke-linejoin:round">
|
||||
<path d="m 2102.61,2057.9534 c -115.02,54.9606 -198.48,64.1207 -251.41,30.5387 -52.93,-35.6247 -134.36,-27.4847 -244.28,24.4219" id="path34"/>
|
||||
<path d="m 2372.33,2066.093 c -21.37,-7.125 -44.78,-12.211 -69.21,-14.25 -49.87,-4.066 -109.93,15.27 -180.16,57.0042 -70.23,42.746 -154.71,59.0351 -255.48,49.8711" id="path36"/>
|
||||
<path d="m 3000.34,1962.273 c -484.5,-99.742 -833.61,-76.336 -1047.35,71.25" id="path38"/>
|
||||
<path d="m 3305.69,2010.117 c -21.38,-8.145 -44.79,-13.235 -69.22,-14.254 -49.87,-4.067 -109.92,15.269 -180.15,57.004 -70.23,41.7262 -154.71,58.0114 -255.48,49.8708" id="path40"/>
|
||||
<path d="m 2655.29,341.21801 c -49.88,-30.54 -101.78,-22.39 -155.73,25.44 -46.82,-47.83 -95.67,-55.98 -146.57,-25.44" id="path42" style="stroke-width:29"/>
|
||||
<path d="m 2542.31,170.21801 c -50.89,-30.53 -100.76,-18.32 -151.66,36.64 -49.88,-54.96 -100.76,-67.17 -151.66,-36.64" id="path44" style="stroke-width:29"/>
|
||||
</g>
|
||||
<g style="fill:#6c78af" id="g33">
|
||||
<path d="m 56.770404,1763.8603 134.914516,0 c 40.53785,0 70.3051,11.5406 89.30315,34.6149 18.99058,23.0812 25.15303,55.2422 18.48477,96.4878 -2.73388,16.9207 -7.61168,32.4501 -14.63698,46.5933 -7.02825,14.1442 -16.30891,27.067 -27.84598,38.7573 -13.71822,14.0684 -29.07355,24.148 -46.07783,30.2307 -17.00335,6.0818 -38.74881,9.1232 -65.23042,9.1232 l -60.093245,0 -14.951878,92.5154 -70.16450702,0 56.29840402,-348.3226 z m 61.252276,55.1047 -23.613063,146.1033 42.675303,0 c 28.28742,0 49.25184,-5.7744 62.90101,-17.3383 13.63797,-11.5649 22.6337,-30.807 26.98842,-57.7355 4.19709,-25.968 1.75658,-44.2941 -7.2996,-54.9935 -9.06329,-10.6883 -26.92099,-16.036 -53.57751,-16.036 l -48.07456,0" id="path66"/>
|
||||
<path d="m 378.24196,1671.3451 69.63516,0 -14.95189,92.5152 61.94982,0 c 38.98987,0 66.01185,7.3901 81.07593,22.1571 15.06891,14.7762 19.95224,38.5804 14.64264,71.4329 l -26.22037,162.2172 -70.68973,0 24.9538,-154.3893 c 2.84147,-17.5516 1.46456,-29.4876 -4.12427,-35.815 -5.58913,-6.3193 -17.28356,-9.4871 -35.06873,-9.4871 l -55.57891,0 -32.27423,199.6914 -69.64729,0 56.29807,-348.3224" id="path68"/>
|
||||
<path d="m 666.43774,1763.8603 134.9135,0 c 40.54695,0 70.30511,11.5406 89.30319,34.6149 18.99454,23.0812 25.15404,55.2422 18.4898,96.4878 -2.7339,16.9207 -7.61577,32.4501 -14.63297,46.5933 -7.03329,14.1442 -16.32202,27.067 -27.85103,38.7573 -13.72229,14.0684 -29.07657,24.148 -46.08084,30.2307 -17.00032,6.0818 -38.74478,9.1232 -65.23548,9.1232 l -60.08435,0 -14.95188,92.5154 -70.16093,0 56.29099,-348.3226 z m 61.26036,55.1047 -23.61306,146.1033 42.6753,0 c 28.28641,0 49.2478,-5.7744 62.88888,-17.3383 13.641,-11.5649 22.64179,-30.807 26.99549,-57.7355 4.19406,-25.968 1.7576,-44.2941 -7.30364,-54.9935 -9.06329,-10.6883 -26.92099,-16.036 -53.56841,-16.036 l -48.07456,0" id="path70"/>
|
||||
</g>
|
||||
<g style="fill:#f89c0e" id="g65">
|
||||
<path d="m 1027.7488,1597.3243 134.9473,0 59.2813,323.8687 163.9729,-323.8687 134.4328,0 -68.203,422.055 -90.0955,0 60.5624,-328.7447 -165.335,328.7447 -97.63,0 -62.0922,-332.1897 -44.4691,332.1897 -93.58345,0 68.21155,-422.055" id="path72"/>
|
||||
<path d="m 1682.3349,1951.0787 68.129,0 39.0484,-241.6556 84.6928,0 -48.7474,301.634 c -6.6176,41.0085 -21.4426,71.3829 -44.4508,91.1237 -23.0089,19.7312 -54.9295,29.6038 -95.7507,29.6038 l -166.0776,0 10.1515,-62.7878 151.4988,0 c 16.2574,0 29.3646,-3.7991 39.3223,-11.39 9.9443,-7.6043 16.0994,-18.6108 18.427,-33.0342 l 0.8443,-5.1933 -74.9653,0 c -47.9513,0 -80.6531,-9.1406 -98.0934,-27.4219 -17.4525,-18.2801 -22.7167,-48.7649 -15.8297,-91.453 l 30.891,-191.0813 83.7372,0 -29.8633,184.7645 c -3.7745,23.3729 -2.642,38.6968 3.4343,45.9753 6.0641,7.2784 20.5937,10.9158 43.6016,10.9158" id="path74"/>
|
||||
<path d="m 2102.3044,1597.3243 97.5197,0 118.1874,422.055 -102.2734,0 -23.907,-100.4601 -189.9554,0 -55.1868,100.4601 -97.5321,0 253.1476,-422.055 z m 31.6507,83.8058 -90.1808,162.444 130.2183,0 -40.0375,-162.444" id="path76"/>
|
||||
<path d="m 2637.3031,2019.3793 -162.6964,0 c -49.1642,0 -85.2663,-13.9786 -108.3193,-41.9409 -23.0404,-27.9573 -30.5053,-66.9321 -22.4309,-116.9135 3.316,-20.4913 9.2287,-39.3092 17.7509,-56.4463 8.5217,-17.1419 19.771,-32.7965 33.7592,-46.9712 16.6286,-17.0415 35.3095,-29.2498 56.0305,-36.6239 20.7209,-7.3752 47.0449,-11.0604 78.9594,-11.0604 l 72.6621,0 18.1132,-112.0988 84.3865,0 -68.2152,422.055 z m -73.6376,-66.4629 28.5697,-176.7241 -51.2224,0 c -34.3524,0 -59.8397,6.9489 -76.4874,20.8321 -16.6223,13.8929 -27.5723,37.0269 -32.8017,69.397 -5.0795,31.4221 -2.0777,53.6898 9.0062,66.8122 11.0837,13.1222 32.7053,19.6828 64.8648,19.6828 l 58.0708,0" id="path78"/>
|
||||
<path d="m 2761.0228,1709.4231 325.2334,0 c 47.155,0 79.7016,8.9067 97.6259,26.6992 17.9129,17.8022 23.6511,46.7028 17.1903,86.702 l -31.7718,196.555 -85.3299,0 30.0434,-185.9259 c 3.6903,-22.7947 2.5585,-37.8305 -3.3836,-45.1151 -5.966,-7.2785 -20.0182,-10.9208 -42.1807,-10.9208 l -47.5103,0 -39.1073,241.9618 -86.604,0 39.1073,-241.9618 -98.9777,0 -39.1073,241.9618 -85.3298,0 50.1021,-309.9562" id="path80"/>
|
||||
<path d="m 3389.6515,1674.2008 -88.8214,0 12.4228,-76.8765 88.8214,0 -12.4228,76.8765 z m -55.7924,345.1785 -88.8215,0 50.0899,-309.9562 88.8214,0 -50.0898,309.9562" id="path82"/>
|
||||
<path d="m 3457.2112,1709.4231 159.3518,0 c 48.1596,0 81.1348,8.7634 98.9115,26.2691 17.7775,17.5156 23.3935,46.5596 16.8359,87.1321 l -31.7597,196.555 -84.705,0 30.1524,-186.5005 c 3.7421,-23.1817 2.4511,-38.2189 -3.8732,-45.1102 -6.325,-6.9 -20.6913,-10.3511 -43.0866,-10.3511 l -68.1168,0 -39.1072,241.9618 -84.6928,0 50.0897,-309.9562" id="path84"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 7.6 KiB |
1
resources/commands/storage/download-from-ftp.sh
Normal file
1
resources/commands/storage/download-from-ftp.sh
Normal file
@ -0,0 +1 @@
|
||||
curl __passive__ -u "__username__:__password__" ftp__ssl__://__host__:__port__/__src__ -o "__dest__"
|
1
resources/commands/storage/upload-to-ftp.sh
Normal file
1
resources/commands/storage/upload-to-ftp.sh
Normal file
@ -0,0 +1 @@
|
||||
curl __passive__ -T "__src__" -u "__username__:__password__" ftp__ssl__://__host__:__port__/__dest__
|
@ -2,6 +2,4 @@ if ! cd __path__; then
|
||||
echo 'VITO_SSH_ERROR' && exit 1
|
||||
fi
|
||||
|
||||
if ! __script__; then
|
||||
echo 'VITO_SSH_ERROR' && exit 1
|
||||
fi
|
||||
__script__
|
||||
|
@ -1,5 +1,5 @@
|
||||
server {
|
||||
listen 54331;
|
||||
listen __port__;
|
||||
server_name _;
|
||||
root /home/vito/phpmyadmin;
|
||||
|
||||
|
@ -4,37 +4,49 @@
|
||||
|
||||
<form method="POST" action="{{ route('login') }}">
|
||||
@csrf
|
||||
|
||||
<div x-data="{ isPasswordVisible: false }">
|
||||
<!-- Email Address -->
|
||||
<div>
|
||||
<x-input-label for="email" :value="__('Email')" />
|
||||
<x-text-input id="email" class="block mt-1 w-full" type="email" name="email" :value="old('email')" required autofocus autocomplete="username" />
|
||||
<x-text-input id="email" class="block mt-1 w-full" type="email" name="email" :value="old('email')"
|
||||
required autofocus autocomplete="username" />
|
||||
<x-input-error :messages="$errors->get('email')" class="mt-2" />
|
||||
</div>
|
||||
|
||||
<!-- Password -->
|
||||
<div class="mt-4">
|
||||
<x-input-label for="password" :value="__('Password')" />
|
||||
|
||||
<x-text-input id="password" class="block mt-1 w-full"
|
||||
type="password"
|
||||
name="password"
|
||||
required autocomplete="current-password" />
|
||||
|
||||
x-bind:type="isPasswordVisible ? 'text' : 'password'" name="password" required
|
||||
autocomplete="current-password" />
|
||||
<x-input-error :messages="$errors->get('password')" class="mt-2" />
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-between">
|
||||
<!-- Remember Me -->
|
||||
<div class="block mt-4">
|
||||
<div class="block mt-4 ">
|
||||
<label for="remember_me" class="inline-flex items-center">
|
||||
<input id="remember_me" type="checkbox" class="rounded dark:bg-gray-900 border-gray-300 dark:border-gray-700 text-indigo-600 shadow-sm focus:ring-indigo-500 dark:focus:ring-indigo-600 dark:focus:ring-offset-gray-800" name="remember">
|
||||
<input id="remember_me" type="checkbox"
|
||||
class="rounded dark:bg-gray-900 border-gray-300 dark:border-gray-700 text-indigo-600 shadow-sm focus:ring-indigo-500 dark:focus:ring-indigo-600 dark:focus:ring-offset-gray-800"
|
||||
name="remember">
|
||||
<span class="ml-2 text-sm text-gray-600 dark:text-gray-400">{{ __('Remember me') }}</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<!-- Show Password -->
|
||||
<div class="block mt-4">
|
||||
<label for="show_password" class="inline-flex items-center float-right">
|
||||
<input id="show_password" type="checkbox" x-model="isPasswordVisible"
|
||||
class="rounded dark:bg-gray-900 border-gray-300 dark:border-gray-700 text-indigo-600 shadow-sm focus:ring-indigo-500 dark:focus:ring-indigo-600 dark:focus:ring-offset-gray-800">
|
||||
<span class="ml-2 text-sm text-gray-600 dark:text-gray-400">{{ __('Show password') }}</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-end mt-4">
|
||||
@if (Route::has('password.request'))
|
||||
<a class="underline text-sm text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-100 rounded-md focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 dark:focus:ring-offset-gray-800" href="{{ route('password.request') }}">
|
||||
<a class="underline text-sm text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-100 rounded-md focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 dark:focus:ring-offset-gray-800"
|
||||
href="{{ route('password.request') }}">
|
||||
{{ __('Forgot your password?') }}
|
||||
</a>
|
||||
@endif
|
||||
@ -43,5 +55,6 @@
|
||||
{{ __('Log in') }}
|
||||
</x-primary-button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</x-guest-layout>
|
||||
|
1
resources/views/components/input-help.blade.php
Normal file
1
resources/views/components/input-help.blade.php
Normal file
@ -0,0 +1 @@
|
||||
<p {{ $attributes->merge(['class' => 'mt-2 text-sm text-gray-500 dark:text-gray-300']) }}>{{ $slot }}</p>
|
@ -1,13 +1,14 @@
|
||||
@props(['server'])
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="csrf-token" content="{{ csrf_token() }}">
|
||||
|
||||
<title>
|
||||
@if(isset($pageTitle))
|
||||
@if (isset($pageTitle))
|
||||
{{ $pageTitle }} -
|
||||
@endif
|
||||
{{ config('app.name', 'Laravel') }}
|
||||
@ -29,9 +30,12 @@
|
||||
|
||||
@include('layouts.partials.favicon')
|
||||
</head>
|
||||
<body class="font-sans antialiased bg-gray-100 dark:bg-gray-900 dark:text-gray-300 min-h-screen min-w-max" x-data="" x-cloak>
|
||||
|
||||
<body class="font-sans antialiased bg-gray-100 dark:bg-gray-900 dark:text-gray-300 min-h-screen min-w-max"
|
||||
x-data="" x-cloak>
|
||||
<div class="flex min-h-screen">
|
||||
<div class="left-0 top-0 min-h-screen w-64 flex-none bg-gray-800 dark:bg-gray-800/50 p-3 dark:border-r-2 dark:border-gray-800">
|
||||
<div
|
||||
class="left-0 top-0 min-h-screen w-64 flex-none bg-gray-800 dark:bg-gray-800/50 p-3 dark:border-r-2 dark:border-gray-800">
|
||||
<div class="h-16 block">
|
||||
<div class="flex items-center justify-start text-3xl font-extrabold text-white">
|
||||
Vito
|
||||
@ -39,78 +43,101 @@
|
||||
</div>
|
||||
|
||||
<div class="mb-5 space-y-2">
|
||||
@include('layouts.partials.server-select', ['server' => isset($server) ? $server : null ])
|
||||
@include('layouts.partials.server-select', ['server' => isset($server) ? $server : null])
|
||||
|
||||
@if(isset($server))
|
||||
@if (isset($server))
|
||||
<x-sidebar-link :href="route('servers.show', ['server' => $server])" :active="request()->routeIs('servers.show')">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M2.25 12l8.954-8.955c.44-.439 1.152-.439 1.591 0L21.75 12M4.5 9.75v10.125c0 .621.504 1.125 1.125 1.125H9.75v-4.875c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125V21h4.125c.621 0 1.125-.504 1.125-1.125V9.75M8.25 21h8.25" />
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
|
||||
stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round"
|
||||
d="M2.25 12l8.954-8.955c.44-.439 1.152-.439 1.591 0L21.75 12M4.5 9.75v10.125c0 .621.504 1.125 1.125 1.125H9.75v-4.875c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125V21h4.125c.621 0 1.125-.504 1.125-1.125V9.75M8.25 21h8.25" />
|
||||
</svg>
|
||||
<span class="ml-2 text-gray-50">{{ __('Overview') }}</span>
|
||||
</x-sidebar-link>
|
||||
@if($server->isReady())
|
||||
@if($server->webserver())
|
||||
@if ($server->isReady())
|
||||
@if ($server->webserver())
|
||||
<x-sidebar-link :href="route('servers.sites', ['server' => $server])" :active="request()->routeIs('servers.sites') || request()->is('servers/*/sites/*')">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M12 21a9.004 9.004 0 008.716-6.747M12 21a9.004 9.004 0 01-8.716-6.747M12 21c2.485 0 4.5-4.03 4.5-9S14.485 3 12 3m0 18c-2.485 0-4.5-4.03-4.5-9S9.515 3 12 3m0 0a8.997 8.997 0 017.843 4.582M12 3a8.997 8.997 0 00-7.843 4.582m15.686 0A11.953 11.953 0 0112 10.5c-2.998 0-5.74-1.1-7.843-2.918m15.686 0A8.959 8.959 0 0121 12c0 .778-.099 1.533-.284 2.253m0 0A17.919 17.919 0 0112 16.5c-3.162 0-6.133-.815-8.716-2.247m0 0A9.015 9.015 0 013 12c0-1.605.42-3.113 1.157-4.418" />
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
|
||||
stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round"
|
||||
d="M12 21a9.004 9.004 0 008.716-6.747M12 21a9.004 9.004 0 01-8.716-6.747M12 21c2.485 0 4.5-4.03 4.5-9S14.485 3 12 3m0 18c-2.485 0-4.5-4.03-4.5-9S9.515 3 12 3m0 0a8.997 8.997 0 017.843 4.582M12 3a8.997 8.997 0 00-7.843 4.582m15.686 0A11.953 11.953 0 0112 10.5c-2.998 0-5.74-1.1-7.843-2.918m15.686 0A8.959 8.959 0 0121 12c0 .778-.099 1.533-.284 2.253m0 0A17.919 17.919 0 0112 16.5c-3.162 0-6.133-.815-8.716-2.247m0 0A9.015 9.015 0 013 12c0-1.605.42-3.113 1.157-4.418" />
|
||||
</svg>
|
||||
<span class="ml-2 text-gray-50">{{ __('Sites') }}</span>
|
||||
</x-sidebar-link>
|
||||
@endif
|
||||
@if($server->database())
|
||||
<x-sidebar-link :href="route('servers.databases', ['server' => $server])" :active="request()->routeIs('servers.databases') || request()->routeIs('servers.databases.backups')">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M20.25 6.375c0 2.278-3.694 4.125-8.25 4.125S3.75 8.653 3.75 6.375m16.5 0c0-2.278-3.694-4.125-8.25-4.125S3.75 4.097 3.75 6.375m16.5 0v11.25c0 2.278-3.694 4.125-8.25 4.125s-8.25-1.847-8.25-4.125V6.375m16.5 0v3.75m-16.5-3.75v3.75m16.5 0v3.75C20.25 16.153 16.556 18 12 18s-8.25-1.847-8.25-4.125v-3.75m16.5 0c0 2.278-3.694 4.125-8.25 4.125s-8.25-1.847-8.25-4.125" />
|
||||
@if ($server->database())
|
||||
<x-sidebar-link :href="route('servers.databases', ['server' => $server])" :active="request()->routeIs('servers.databases') ||
|
||||
request()->routeIs('servers.databases.backups')">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
|
||||
stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round"
|
||||
d="M20.25 6.375c0 2.278-3.694 4.125-8.25 4.125S3.75 8.653 3.75 6.375m16.5 0c0-2.278-3.694-4.125-8.25-4.125S3.75 4.097 3.75 6.375m16.5 0v11.25c0 2.278-3.694 4.125-8.25 4.125s-8.25-1.847-8.25-4.125V6.375m16.5 0v3.75m-16.5-3.75v3.75m16.5 0v3.75C20.25 16.153 16.556 18 12 18s-8.25-1.847-8.25-4.125v-3.75m16.5 0c0 2.278-3.694 4.125-8.25 4.125s-8.25-1.847-8.25-4.125" />
|
||||
</svg>
|
||||
<span class="ml-2 text-gray-50">{{ __('Databases') }}</span>
|
||||
</x-sidebar-link>
|
||||
@endif
|
||||
@if($server->php())
|
||||
@if ($server->php())
|
||||
<x-sidebar-link :href="route('servers.php', ['server' => $server])" :active="request()->routeIs('servers.php')">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M17.25 6.75L22.5 12l-5.25 5.25m-10.5 0L1.5 12l5.25-5.25m7.5-3l-4.5 16.5" />
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
|
||||
stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round"
|
||||
d="M17.25 6.75L22.5 12l-5.25 5.25m-10.5 0L1.5 12l5.25-5.25m7.5-3l-4.5 16.5" />
|
||||
</svg>
|
||||
<span class="ml-2 text-gray-50">{{ __('PHP') }}</span>
|
||||
</x-sidebar-link>
|
||||
@endif
|
||||
@if($server->firewall())
|
||||
@if ($server->firewall())
|
||||
<x-sidebar-link :href="route('servers.firewall', ['server' => $server])" :active="request()->routeIs('servers.firewall')">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M15.362 5.214A8.252 8.252 0 0112 21 8.25 8.25 0 016.038 7.048 8.287 8.287 0 009 9.6a8.983 8.983 0 013.361-6.867 8.21 8.21 0 003 2.48z" />
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M12 18a3.75 3.75 0 00.495-7.467 5.99 5.99 0 00-1.925 3.546 5.974 5.974 0 01-2.133-1A3.75 3.75 0 0012 18z" />
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
|
||||
stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round"
|
||||
d="M15.362 5.214A8.252 8.252 0 0112 21 8.25 8.25 0 016.038 7.048 8.287 8.287 0 009 9.6a8.983 8.983 0 013.361-6.867 8.21 8.21 0 003 2.48z" />
|
||||
<path stroke-linecap="round" stroke-linejoin="round"
|
||||
d="M12 18a3.75 3.75 0 00.495-7.467 5.99 5.99 0 00-1.925 3.546 5.974 5.974 0 01-2.133-1A3.75 3.75 0 0012 18z" />
|
||||
</svg>
|
||||
<span class="ml-2 text-gray-50">{{ __('Firewall') }}</span>
|
||||
</x-sidebar-link>
|
||||
@endif
|
||||
<x-sidebar-link :href="route('servers.cronjobs', ['server' => $server])" :active="request()->routeIs('servers.cronjobs')">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M12 6v6h4.5m4.5 0a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
|
||||
stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round"
|
||||
d="M12 6v6h4.5m4.5 0a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
</svg>
|
||||
<span class="ml-2 text-gray-50">{{ __('Cronjobs') }}</span>
|
||||
</x-sidebar-link>
|
||||
<x-sidebar-link :href="route('servers.ssh-keys', ['server' => $server])" :active="request()->routeIs('servers.ssh-keys')">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M15.75 5.25a3 3 0 013 3m3 0a6 6 0 01-7.029 5.912c-.563-.097-1.159.026-1.563.43L10.5 17.25H8.25v2.25H6v2.25H2.25v-2.818c0-.597.237-1.17.659-1.591l6.499-6.499c.404-.404.527-1 .43-1.563A6 6 0 1121.75 8.25z" />
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
|
||||
stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round"
|
||||
d="M15.75 5.25a3 3 0 013 3m3 0a6 6 0 01-7.029 5.912c-.563-.097-1.159.026-1.563.43L10.5 17.25H8.25v2.25H6v2.25H2.25v-2.818c0-.597.237-1.17.659-1.591l6.499-6.499c.404-.404.527-1 .43-1.563A6 6 0 1121.75 8.25z" />
|
||||
</svg>
|
||||
<span class="ml-2 text-gray-50">{{ __('SSH Keys') }}</span>
|
||||
</x-sidebar-link>
|
||||
<x-sidebar-link :href="route('servers.services', ['server' => $server])" :active="request()->routeIs('servers.services')">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M9.594 3.94c.09-.542.56-.94 1.11-.94h2.593c.55 0 1.02.398 1.11.94l.213 1.281c.063.374.313.686.645.87.074.04.147.083.22.127.324.196.72.257 1.075.124l1.217-.456a1.125 1.125 0 011.37.49l1.296 2.247a1.125 1.125 0 01-.26 1.431l-1.003.827c-.293.24-.438.613-.431.992a6.759 6.759 0 010 .255c-.007.378.138.75.43.99l1.005.828c.424.35.534.954.26 1.43l-1.298 2.247a1.125 1.125 0 01-1.369.491l-1.217-.456c-.355-.133-.75-.072-1.076.124a6.57 6.57 0 01-.22.128c-.331.183-.581.495-.644.869l-.213 1.28c-.09.543-.56.941-1.11.941h-2.594c-.55 0-1.02-.398-1.11-.94l-.213-1.281c-.062-.374-.312-.686-.644-.87a6.52 6.52 0 01-.22-.127c-.325-.196-.72-.257-1.076-.124l-1.217.456a1.125 1.125 0 01-1.369-.49l-1.297-2.247a1.125 1.125 0 01.26-1.431l1.004-.827c.292-.24.437-.613.43-.992a6.932 6.932 0 010-.255c.007-.378-.138-.75-.43-.99l-1.004-.828a1.125 1.125 0 01-.26-1.43l1.297-2.247a1.125 1.125 0 011.37-.491l1.216.456c.356.133.751.072 1.076-.124.072-.044.146-.087.22-.128.332-.183.582-.495.644-.869l.214-1.281z" />
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
|
||||
stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round"
|
||||
d="M9.594 3.94c.09-.542.56-.94 1.11-.94h2.593c.55 0 1.02.398 1.11.94l.213 1.281c.063.374.313.686.645.87.074.04.147.083.22.127.324.196.72.257 1.075.124l1.217-.456a1.125 1.125 0 011.37.49l1.296 2.247a1.125 1.125 0 01-.26 1.431l-1.003.827c-.293.24-.438.613-.431.992a6.759 6.759 0 010 .255c-.007.378.138.75.43.99l1.005.828c.424.35.534.954.26 1.43l-1.298 2.247a1.125 1.125 0 01-1.369.491l-1.217-.456c-.355-.133-.75-.072-1.076.124a6.57 6.57 0 01-.22.128c-.331.183-.581.495-.644.869l-.213 1.28c-.09.543-.56.941-1.11.941h-2.594c-.55 0-1.02-.398-1.11-.94l-.213-1.281c-.062-.374-.312-.686-.644-.87a6.52 6.52 0 01-.22-.127c-.325-.196-.72-.257-1.076-.124l-1.217.456a1.125 1.125 0 01-1.369-.49l-1.297-2.247a1.125 1.125 0 01.26-1.431l1.004-.827c.292-.24.437-.613.43-.992a6.932 6.932 0 010-.255c.007-.378-.138-.75-.43-.99l-1.004-.828a1.125 1.125 0 01-.26-1.43l1.297-2.247a1.125 1.125 0 011.37-.491l1.216.456c.356.133.751.072 1.076-.124.072-.044.146-.087.22-.128.332-.183.582-.495.644-.869l.214-1.281z" />
|
||||
<path stroke-linecap="round" stroke-linejoin="round"
|
||||
d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
|
||||
</svg>
|
||||
<span class="ml-2 text-gray-50">{{ __('Services') }}</span>
|
||||
</x-sidebar-link>
|
||||
@endif
|
||||
<x-sidebar-link :href="route('servers.settings', ['server' => $server])" :active="request()->routeIs('servers.settings')">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M11.42 15.17L17.25 21A2.652 2.652 0 0021 17.25l-5.877-5.877M11.42 15.17l2.496-3.03c.317-.384.74-.626 1.208-.766M11.42 15.17l-4.655 5.653a2.548 2.548 0 11-3.586-3.586l6.837-5.63m5.108-.233c.55-.164 1.163-.188 1.743-.14a4.5 4.5 0 004.486-6.336l-3.276 3.277a3.004 3.004 0 01-2.25-2.25l3.276-3.276a4.5 4.5 0 00-6.336 4.486c.091 1.076-.071 2.264-.904 2.95l-.102.085m-1.745 1.437L5.909 7.5H4.5L2.25 3.75l1.5-1.5L7.5 4.5v1.409l4.26 4.26m-1.745 1.437l1.745-1.437m6.615 8.206L15.75 15.75M4.867 19.125h.008v.008h-.008v-.008z" />
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
|
||||
stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round"
|
||||
d="M11.42 15.17L17.25 21A2.652 2.652 0 0021 17.25l-5.877-5.877M11.42 15.17l2.496-3.03c.317-.384.74-.626 1.208-.766M11.42 15.17l-4.655 5.653a2.548 2.548 0 11-3.586-3.586l6.837-5.63m5.108-.233c.55-.164 1.163-.188 1.743-.14a4.5 4.5 0 004.486-6.336l-3.276 3.277a3.004 3.004 0 01-2.25-2.25l3.276-3.276a4.5 4.5 0 00-6.336 4.486c.091 1.076-.071 2.264-.904 2.95l-.102.085m-1.745 1.437L5.909 7.5H4.5L2.25 3.75l1.5-1.5L7.5 4.5v1.409l4.26 4.26m-1.745 1.437l1.745-1.437m6.615 8.206L15.75 15.75M4.867 19.125h.008v.008h-.008v-.008z" />
|
||||
</svg>
|
||||
<span class="ml-2 text-gray-50">{{ __('Settings') }}</span>
|
||||
</x-sidebar-link>
|
||||
<x-sidebar-link :href="route('servers.logs', ['server' => $server])" :active="request()->routeIs('servers.logs')">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M6.429 9.75L2.25 12l4.179 2.25m0-4.5l5.571 3 5.571-3m-11.142 0L2.25 7.5 12 2.25l9.75 5.25-4.179 2.25m0 0L21.75 12l-4.179 2.25m0 0l4.179 2.25L12 21.75 2.25 16.5l4.179-2.25m11.142 0l-5.571 3-5.571-3" />
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
|
||||
stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round"
|
||||
d="M6.429 9.75L2.25 12l4.179 2.25m0-4.5l5.571 3 5.571-3m-11.142 0L2.25 7.5 12 2.25l9.75 5.25-4.179 2.25m0 0L21.75 12l-4.179 2.25m0 0l4.179 2.25L12 21.75 2.25 16.5l4.179-2.25m11.142 0l-5.571 3-5.571-3" />
|
||||
</svg>
|
||||
<span class="ml-2 text-gray-50">{{ __('Logs') }}</span>
|
||||
</x-sidebar-link>
|
||||
@ -118,8 +145,9 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if(isset($sidebar))
|
||||
<div class="min-h-screen w-64 flex-none border-r border-gray-200 bg-white dark:border-gray-800 dark:bg-gray-900">
|
||||
@if (isset($sidebar))
|
||||
<div
|
||||
class="min-h-screen w-64 flex-none border-r border-gray-200 bg-white dark:border-gray-800 dark:bg-gray-900">
|
||||
{{ $sidebar }}
|
||||
</div>
|
||||
@endif
|
||||
@ -128,29 +156,73 @@
|
||||
<nav class="h-16 border-b border-gray-200 dark:border-gray-800 bg-white dark:bg-gray-900">
|
||||
<!-- Primary Navigation Menu -->
|
||||
<div class="mx-auto max-w-full px-4 sm:px-6 lg:px-8">
|
||||
<div class="flex h-16 justify-between">
|
||||
<div class="flex h-16 justify-end">
|
||||
<div class="flex items-center justify-center">
|
||||
{{-- Search --}}
|
||||
</div>
|
||||
{{-- 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 --}}
|
||||
<div class="ml-6 flex items-center">
|
||||
<div class="relative ml-5">
|
||||
<x-dropdown align="right" width="48">
|
||||
<x-slot name="trigger">
|
||||
<div class="flex cursor-pointer items-center justify-between">
|
||||
{{ auth()->user()->name }}
|
||||
<svg class="ml-2 -mr-0.5 h-4 w-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor">
|
||||
<path fill-rule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clip-rule="evenodd" />
|
||||
<svg class="ml-2 -mr-0.5 h-4 w-4" xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 20 20" fill="currentColor">
|
||||
<path fill-rule="evenodd"
|
||||
d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"
|
||||
clip-rule="evenodd" />
|
||||
</svg>
|
||||
</div>
|
||||
</x-slot>
|
||||
|
||||
<x-slot name="content">
|
||||
<x-dropdown-link :href="route('profile')">Profile</x-dropdown-link>
|
||||
<div class="border-t border-gray-100 dark:border-gray-700"></div>
|
||||
|
||||
<div class="border-t border-gray-100 dark:border-gray-700"></div>
|
||||
<form method="POST" action="{{ route('logout') }}">
|
||||
@csrf
|
||||
<x-dropdown-link :href="route('logout')" onclick="event.preventDefault(); this.closest('form').submit();">
|
||||
<x-dropdown-link :href="route('logout')"
|
||||
onclick="event.preventDefault(); this.closest('form').submit();">
|
||||
{{ __('Log Out') }}
|
||||
</x-dropdown-link>
|
||||
</form>
|
||||
@ -163,7 +235,7 @@
|
||||
</nav>
|
||||
|
||||
<!-- Page Heading -->
|
||||
@if(isset($header))
|
||||
@if (isset($header))
|
||||
<header class="border-b border-gray-200 bg-white dark:border-gray-800 dark:bg-gray-900">
|
||||
<div class="mx-auto flex h-20 w-full max-w-full items-center justify-between px-8">
|
||||
{{ $header }}
|
||||
@ -171,7 +243,7 @@
|
||||
</header>
|
||||
@endif
|
||||
|
||||
@if(isset($header2))
|
||||
@if (isset($header2))
|
||||
<header class="border-b border-gray-200 bg-white dark:border-gray-800 dark:bg-gray-900">
|
||||
<div class="mx-auto max-w-full py-6 px-8">
|
||||
{{ $header2 }}
|
||||
@ -191,9 +263,20 @@
|
||||
<script>
|
||||
document.addEventListener('livewire:load', () => {
|
||||
Livewire.onPageExpired((response, message) => {
|
||||
({href: window.location.href} = window.location);
|
||||
({
|
||||
href: window.location.href
|
||||
} = window.location);
|
||||
})
|
||||
})
|
||||
|
||||
// On page load or when changing themes, best to add inline in `head` to avoid FOUC
|
||||
if (localStorage.theme === 'dark' || (!('theme' in localStorage) && window.matchMedia(
|
||||
'(prefers-color-scheme: dark)').matches)) {
|
||||
document.documentElement.classList.add('dark')
|
||||
} else {
|
||||
document.documentElement.classList.remove('dark')
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
@ -29,4 +29,5 @@
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
@ -18,7 +18,7 @@
|
||||
<tr>
|
||||
<x-th>{{ __("Name") }}</x-th>
|
||||
<x-th>{{ __("Created") }}</x-th>
|
||||
<x-th>{{ __("Size") }}</x-th>
|
||||
{{--<x-th>{{ __("Size") }}</x-th>--}}
|
||||
<x-th>{{ __("Status") }}</x-th>
|
||||
<x-th>{{ __("Restored") }}</x-th>
|
||||
<x-th>{{ __("Restored To") }}</x-th>
|
||||
@ -30,7 +30,7 @@
|
||||
<x-td>
|
||||
<x-datetime :value="$file->created_at" />
|
||||
</x-td>
|
||||
<x-td>{{ $file->size }}</x-td>
|
||||
{{--<x-td>{{ $file->size }}</x-td>--}}
|
||||
<x-td>
|
||||
<div class="inline-flex">
|
||||
@include('livewire.databases.partials.backup-file-status', ['status' => $file->status])
|
||||
|
@ -45,6 +45,18 @@
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<x-input-label for="timezone" :value="__('Timezone')" />
|
||||
<x-select-input wire:model.defer="timezone" id="timezone" name="timezone" class="mt-1 block w-full" required>
|
||||
@foreach(timezone_identifiers_list() as $timezone)
|
||||
<option value="{{ $timezone }}">{{ $timezone }}</option>
|
||||
@endforeach
|
||||
</x-select-input>
|
||||
@error('timezone')
|
||||
<x-input-error class="mt-2" :messages="$message" />
|
||||
@enderror
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<x-slot name="actions">
|
||||
|
@ -0,0 +1,33 @@
|
||||
<x-modal name="install-phpmyadmin">
|
||||
<form wire:submit.prevent="install" class="p-6">
|
||||
<h2 class="text-lg font-medium text-gray-900 dark:text-gray-100">
|
||||
{{ __('Install PHPMyAdmin') }}
|
||||
</h2>
|
||||
|
||||
<div class="mt-6">
|
||||
<x-input-label for="allowed_ip" :value="__('Allowed IP')" />
|
||||
<x-text-input wire:model.defer="allowed_ip" id="allowed_ip" name="allowed_ip" class="mt-1 w-full" />
|
||||
@error('allowed_ip')
|
||||
<x-input-error class="mt-2" :messages="$message" />
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div class="mt-6">
|
||||
<x-input-label for="port" :value="__('Port')" />
|
||||
<x-text-input wire:model.defer="port" id="port" name="port" class="mt-1 w-full" />
|
||||
@error('port')
|
||||
<x-input-error class="mt-2" :messages="$message" />
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div class="mt-6 flex justify-end">
|
||||
<x-secondary-button type="button" x-on:click="$dispatch('close')">
|
||||
{{ __('Cancel') }}
|
||||
</x-secondary-button>
|
||||
|
||||
<x-primary-button class="ml-3" @started.window="$dispatch('close')">
|
||||
{{ __('Install') }}
|
||||
</x-primary-button>
|
||||
</div>
|
||||
</form>
|
||||
</x-modal>
|
@ -1,11 +1,29 @@
|
||||
<div>
|
||||
<x-card-header>
|
||||
<x-slot name="title">{{ __("Services") }}</x-slot>
|
||||
<x-slot name="description">{{ __("All services that we installed on your server are here") }}</x-slot>
|
||||
<x-slot name="title">{{ __('Services') }}</x-slot>
|
||||
<x-slot name="description">{{ __('All services that we installed on your server are here') }}</x-slot>
|
||||
<x-slot name="aside">
|
||||
<x-dropdown>
|
||||
<x-slot name="trigger">
|
||||
<x-primary-button>
|
||||
{{ __('Install Service') }}
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4 ml-1">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M19.5 8.25l-7.5 7.5-7.5-7.5" />
|
||||
</svg>
|
||||
</x-primary-button>
|
||||
</x-slot>
|
||||
<x-slot name="content">
|
||||
<x-dropdown-link class="cursor-pointer" x-on:click="$dispatch('open-modal', 'install-phpmyadmin')">
|
||||
{{ __('PHPMyAdmin') }}
|
||||
</x-dropdown-link>
|
||||
</x-slot>
|
||||
</x-dropdown>
|
||||
<livewire:services.install-p-h-p-my-admin :server="$server" />
|
||||
</x-slot>
|
||||
</x-card-header>
|
||||
|
||||
<div class="space-y-3">
|
||||
@foreach($services as $service)
|
||||
@foreach ($services as $service)
|
||||
<x-item-card>
|
||||
<div class="flex-none">
|
||||
<img src="{{ asset('static/images/' . $service->name . '.svg') }}" class="h-10 w-10" alt="">
|
||||
@ -20,24 +38,30 @@
|
||||
<x-dropdown>
|
||||
<x-slot name="trigger">
|
||||
<x-secondary-button>
|
||||
{{ __("Actions") }}
|
||||
{{ __('Actions') }}
|
||||
</x-secondary-button>
|
||||
</x-slot>
|
||||
|
||||
<x-slot name="content">
|
||||
@if($service->status == \App\Enums\ServiceStatus::STOPPED)
|
||||
@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)
|
||||
@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>
|
||||
|
@ -32,6 +32,17 @@
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
@if($provider === App\Enums\SourceControl::GITLAB)
|
||||
<div class="mt-6">
|
||||
<x-input-label for="url" value="Url (optional)" />
|
||||
<x-text-input wire:model.defer="url" id="url" name="url" type="text" class="mt-1 w-full" placeholder="e.g. https://gitlab.example.com/" />
|
||||
<x-input-help>If you run a self-managed gitlab enter the url here, leave empty to use gitlab.com</x-input-help>
|
||||
@error('url')
|
||||
<x-input-error class="mt-2" :messages="$message" />
|
||||
@enderror
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="mt-6">
|
||||
<x-input-label for="token" value="API Key" />
|
||||
<x-text-input wire:model.defer="token" id="token" name="token" type="text" class="mt-1 w-full" />
|
||||
|
@ -32,6 +32,7 @@
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
@if($provider == \App\Enums\StorageProvider::DROPBOX)
|
||||
<div class="mt-6">
|
||||
<x-input-label for="token" value="API Key" />
|
||||
<x-text-input wire:model.defer="token" id="token" name="token" type="text" class="mt-1 w-full" />
|
||||
@ -39,6 +40,71 @@
|
||||
<x-input-error class="mt-2" :messages="$message" />
|
||||
@enderror
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if($provider == \App\Enums\StorageProvider::FTP)
|
||||
<div class="grid grid-cols-2 gap-2">
|
||||
<div class="mt-6">
|
||||
<x-input-label for="host" value="Host" />
|
||||
<x-text-input wire:model.defer="host" id="host" name="host" type="text" class="mt-1 w-full" />
|
||||
@error('host')
|
||||
<x-input-error class="mt-2" :messages="$message" />
|
||||
@enderror
|
||||
</div>
|
||||
<div class="mt-6">
|
||||
<x-input-label for="port" value="Port" />
|
||||
<x-text-input wire:model.defer="port" id="port" name="port" type="text" class="mt-1 w-full" />
|
||||
@error('port')
|
||||
<x-input-error class="mt-2" :messages="$message" />
|
||||
@enderror
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-6">
|
||||
<x-input-label for="path" value="Path" />
|
||||
<x-text-input wire:model.defer="path" id="path" name="path" type="text" class="mt-1 w-full" />
|
||||
@error('path')
|
||||
<x-input-error class="mt-2" :messages="$message" />
|
||||
@enderror
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-2">
|
||||
<div class="mt-6">
|
||||
<x-input-label for="username" value="Username" />
|
||||
<x-text-input wire:model.defer="username" id="username" name="username" type="text" class="mt-1 w-full" />
|
||||
@error('username')
|
||||
<x-input-error class="mt-2" :messages="$message" />
|
||||
@enderror
|
||||
</div>
|
||||
<div class="mt-6">
|
||||
<x-input-label for="password" value="Password" />
|
||||
<x-text-input wire:model.defer="password" id="password" name="password" type="text" class="mt-1 w-full" />
|
||||
@error('password')
|
||||
<x-input-error class="mt-2" :messages="$message" />
|
||||
@enderror
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-2">
|
||||
<div class="mt-6">
|
||||
<x-input-label for="ssl" :value="__('SSL')" />
|
||||
<x-select-input wire:model="ssl" id="ssl" name="ssl" class="mt-1 w-full">
|
||||
<option value="1" @if($ssl) selected @endif>{{ __("Yes") }}</option>
|
||||
<option value="0" @if(!$ssl) selected @endif>{{ __("No") }}</option>
|
||||
</x-select-input>
|
||||
@error('ssl')
|
||||
<x-input-error class="mt-2" :messages="$message" />
|
||||
@enderror
|
||||
</div>
|
||||
<div class="mt-6">
|
||||
<x-input-label for="passive" :value="__('Passive')" />
|
||||
<x-select-input wire:model="passive" id="passive" name="passive" class="mt-1 w-full">
|
||||
<option value="1" @if($passive) selected @endif>{{ __("Yes") }}</option>
|
||||
<option value="0" @if(!$passive) selected @endif>{{ __("No") }}</option>
|
||||
</x-select-input>
|
||||
@error('passive')
|
||||
<x-input-error class="mt-2" :messages="$message" />
|
||||
@enderror
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="mt-6 flex justify-end">
|
||||
<x-secondary-button type="button" x-on:click="$dispatch('close')">
|
||||
|
@ -11,12 +11,18 @@
|
||||
@foreach($providers as $provider)
|
||||
<x-item-card>
|
||||
<div class="flex-none">
|
||||
@if($provider->provider == \App\Enums\StorageProvider::FTP)
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-10 h-10 text-gray-600 dark:text-gray-200">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M19.5 14.25v-2.625a3.375 3.375 0 00-3.375-3.375h-1.5A1.125 1.125 0 0113.5 7.125v-1.5a3.375 3.375 0 00-3.375-3.375H8.25m2.25 0H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 00-9-9z" />
|
||||
</svg>
|
||||
@else
|
||||
<img src="{{ asset('static/images/' . $provider->provider . '.svg') }}" class="h-10 w-10" alt="">
|
||||
@endif
|
||||
</div>
|
||||
<div class="ml-3 flex flex-grow flex-col items-start justify-center">
|
||||
<span class="mb-1">{{ $provider->profile }}</span>
|
||||
<span class="text-sm text-gray-400">
|
||||
<x-datetime :value="$provider->created_at"/>
|
||||
<x-datetime :value="$provider->created_at" />
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
|
@ -3,6 +3,7 @@ const colors = require("tailwindcss/colors");
|
||||
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
darkMode: 'class',
|
||||
content: [
|
||||
'./vendor/laravel/framework/src/Illuminate/Pagination/resources/views/*.blade.php',
|
||||
'./storage/framework/views/*.php',
|
||||
|
@ -29,6 +29,7 @@ public function test_profile_information_can_be_updated(): void
|
||||
Livewire::test(UpdateProfileInformation::class)
|
||||
->set('name', 'Test')
|
||||
->set('email', 'test@example.com')
|
||||
->set('timezone', 'Europe/Berlin')
|
||||
->call('submit')
|
||||
->assertSuccessful();
|
||||
|
||||
@ -36,5 +37,6 @@ public function test_profile_information_can_be_updated(): void
|
||||
|
||||
$this->assertSame('Test', $this->user->name);
|
||||
$this->assertSame('test@example.com', $this->user->email);
|
||||
$this->assertSame('Europe/Berlin', $this->user->timezone);
|
||||
}
|
||||
}
|
||||
|
@ -3,8 +3,12 @@
|
||||
namespace Tests\Feature\Http;
|
||||
|
||||
use App\Enums\ServiceStatus;
|
||||
use App\Http\Livewire\Services\InstallPHPMyAdmin;
|
||||
use App\Http\Livewire\Services\ServicesList;
|
||||
use App\Jobs\Installation\InstallPHPMyAdmin as InstallationInstallPHPMyAdmin;
|
||||
use App\Jobs\Installation\UninstallPHPMyAdmin;
|
||||
use App\Jobs\Service\Manage;
|
||||
use App\Models\Service;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Facades\Bus;
|
||||
use Livewire\Livewire;
|
||||
@ -80,6 +84,45 @@ public function test_start_service(string $name): void
|
||||
Bus::assertDispatched(Manage::class);
|
||||
}
|
||||
|
||||
public function test_install_phpmyadmin(): void
|
||||
{
|
||||
Bus::fake();
|
||||
|
||||
Livewire::test(InstallPHPMyAdmin::class, ['server' => $this->server])
|
||||
->set('allowed_ip', '0.0.0.0')
|
||||
->set('port', 5433)
|
||||
->call('install')
|
||||
->assertSuccessful();
|
||||
|
||||
Bus::assertDispatched(InstallationInstallPHPMyAdmin::class);
|
||||
}
|
||||
|
||||
public function test_uninstall_phpmyadmin(): void
|
||||
{
|
||||
$service = Service::factory()->create([
|
||||
'server_id' => $this->server->id,
|
||||
'type' => 'phpmyadmin',
|
||||
'type_data' => [
|
||||
'allowed_ip' => '0.0.0.0',
|
||||
'port' => '5433',
|
||||
'php' => '8.1',
|
||||
],
|
||||
'name' => 'phpmyadmin',
|
||||
'version' => '5.1.2',
|
||||
'status' => ServiceStatus::READY,
|
||||
'is_default' => 1,
|
||||
|
||||
]);
|
||||
|
||||
Bus::fake();
|
||||
|
||||
Livewire::test(ServicesList::class, ['server' => $this->server])
|
||||
->call('uninstall', $service->id)
|
||||
->assertSuccessful();
|
||||
|
||||
Bus::assertDispatched(UninstallPHPMyAdmin::class);
|
||||
}
|
||||
|
||||
public static function data(): array
|
||||
{
|
||||
return [
|
||||
|
@ -17,21 +17,28 @@ class SourceControlsTest extends TestCase
|
||||
/**
|
||||
* @dataProvider data
|
||||
*/
|
||||
public function test_connect_provider(string $provider): void
|
||||
public function test_connect_provider(string $provider, ?string $customUrl): void
|
||||
{
|
||||
$this->actingAs($this->user);
|
||||
|
||||
Http::fake();
|
||||
|
||||
Livewire::test(Connect::class)
|
||||
$livewire = Livewire::test(Connect::class)
|
||||
->set('token', 'token')
|
||||
->set('name', 'profile')
|
||||
->set('provider', $provider)
|
||||
->set('provider', $provider);
|
||||
|
||||
if ($customUrl !== null) {
|
||||
$livewire->set('url', $customUrl);
|
||||
}
|
||||
|
||||
$livewire
|
||||
->call('connect')
|
||||
->assertSuccessful();
|
||||
|
||||
$this->assertDatabaseHas('source_controls', [
|
||||
'provider' => $provider,
|
||||
'url' => $customUrl,
|
||||
]);
|
||||
}
|
||||
|
||||
@ -61,9 +68,10 @@ public function test_delete_provider(string $provider): void
|
||||
public static function data(): array
|
||||
{
|
||||
return [
|
||||
['github'],
|
||||
['gitlab'],
|
||||
['bitbucket'],
|
||||
['github', null],
|
||||
['gitlab', null],
|
||||
['gitlab', 'https://git.example.com/'],
|
||||
['bitbucket', null],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature\SSHCommands\Storage;
|
||||
|
||||
use App\SSHCommands\Storage\DownloadFromFTPCommand;
|
||||
use Tests\TestCase;
|
||||
|
||||
class DownloadFromFTPCommandTest extends TestCase
|
||||
{
|
||||
public function test_generate_command()
|
||||
{
|
||||
$command = new DownloadFromFTPCommand(
|
||||
'src',
|
||||
'dest',
|
||||
'1.1.1.1',
|
||||
'21',
|
||||
'username',
|
||||
'password',
|
||||
false,
|
||||
true,
|
||||
);
|
||||
|
||||
$expected = <<<'EOD'
|
||||
curl --ftp-pasv -u "username:password" ftp://1.1.1.1:21/src -o "dest"
|
||||
EOD;
|
||||
|
||||
$this->assertStringContainsString($expected, $command->content());
|
||||
}
|
||||
}
|
29
tests/Feature/SSHCommands/Storage/UploadToFTPCommandTest.php
Normal file
29
tests/Feature/SSHCommands/Storage/UploadToFTPCommandTest.php
Normal file
@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature\SSHCommands\Storage;
|
||||
|
||||
use App\SSHCommands\Storage\UploadToFTPCommand;
|
||||
use Tests\TestCase;
|
||||
|
||||
class UploadToFTPCommandTest extends TestCase
|
||||
{
|
||||
public function test_generate_command()
|
||||
{
|
||||
$command = new UploadToFTPCommand(
|
||||
'src',
|
||||
'dest',
|
||||
'1.1.1.1',
|
||||
'21',
|
||||
'username',
|
||||
'password',
|
||||
true,
|
||||
true
|
||||
);
|
||||
|
||||
$expected = <<<'EOD'
|
||||
curl --ftp-pasv -T "src" -u "username:password" ftps://1.1.1.1:21/dest
|
||||
EOD;
|
||||
|
||||
$this->assertStringContainsString($expected, $command->content());
|
||||
}
|
||||
}
|
@ -16,9 +16,7 @@ public function test_generate_command()
|
||||
echo 'VITO_SSH_ERROR' && exit 1
|
||||
fi
|
||||
|
||||
if ! script; then
|
||||
echo 'VITO_SSH_ERROR' && exit 1
|
||||
fi
|
||||
script
|
||||
EOD;
|
||||
|
||||
$this->assertStringContainsString($expected, $command->content());
|
||||
|
87
tests/Unit/SourceControlProviders/GitlabTest.php
Normal file
87
tests/Unit/SourceControlProviders/GitlabTest.php
Normal file
@ -0,0 +1,87 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Unit\SourceControlProviders;
|
||||
|
||||
use App\Models\SourceControl;
|
||||
use App\SourceControlProviders\Gitlab;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Tests\TestCase;
|
||||
|
||||
class GitlabTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
public function test_default_gitlab_url(): void
|
||||
{
|
||||
$sourceControlModel = SourceControl::factory()
|
||||
->gitlab()
|
||||
->create();
|
||||
|
||||
$gitlab = new Gitlab($sourceControlModel);
|
||||
|
||||
$this->assertSame('https://gitlab.com/api/v4', $gitlab->getApiUrl());
|
||||
}
|
||||
|
||||
public function test_default_gitlab_repo_url(): void
|
||||
{
|
||||
$repo = 'test/repo';
|
||||
$key = 'TEST_KEY';
|
||||
|
||||
$sourceControlModel = SourceControl::factory()
|
||||
->gitlab()
|
||||
->create();
|
||||
|
||||
$gitlab = new Gitlab($sourceControlModel);
|
||||
|
||||
$this->assertSame('git@gitlab.com-TEST_KEY:test/repo.git', $gitlab->fullRepoUrl($repo, $key));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider customUrlData
|
||||
*/
|
||||
public function test_custom_url(string $url, string $expected): void
|
||||
{
|
||||
$sourceControlModel = SourceControl::factory()
|
||||
->gitlab()
|
||||
->create(['url' => $url]);
|
||||
|
||||
$gitlab = new Gitlab($sourceControlModel);
|
||||
|
||||
$this->assertSame($expected, $gitlab->getApiUrl());
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider customRepoUrlData
|
||||
*/
|
||||
public function test_custom_full_repository_url(string $url, string $expected): void
|
||||
{
|
||||
$repo = 'test/repo';
|
||||
$key = 'TEST_KEY';
|
||||
|
||||
$sourceControlModel = SourceControl::factory()
|
||||
->gitlab()
|
||||
->create(['url' => $url]);
|
||||
|
||||
$gitlab = new Gitlab($sourceControlModel);
|
||||
|
||||
$this->assertSame($expected, $gitlab->fullRepoUrl($repo, $key));
|
||||
}
|
||||
|
||||
public static function customRepoUrlData(): array
|
||||
{
|
||||
return [
|
||||
['https://git.example.com/', 'git@git.example.com-TEST_KEY:test/repo.git'],
|
||||
['https://git.test.example.com/', 'git@git.test.example.com-TEST_KEY:test/repo.git'],
|
||||
['https://git.example.co.uk/', 'git@git.example.co.uk-TEST_KEY:test/repo.git'],
|
||||
];
|
||||
}
|
||||
|
||||
public static function customUrlData(): array
|
||||
{
|
||||
return [
|
||||
['https://git.example.com/', 'https://git.example.com/api/v4'],
|
||||
['https://git.test.example.com/', 'https://git.test.example.com/api/v4'],
|
||||
['https://git.example.co.uk/', 'https://git.example.co.uk/api/v4'],
|
||||
];
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user