mirror of
https://github.com/vitodeploy/vito.git
synced 2025-07-01 05:56:16 +00:00
Compare commits
38 Commits
Author | SHA1 | Date | |
---|---|---|---|
e2dd9177f7 | |||
5a9e8d6799 | |||
868b70f530 | |||
d07e9bcad2 | |||
0cd815cce6 | |||
5ab6617b5d | |||
72b37c56fd | |||
8a4ef66946 | |||
4517ca7d2a | |||
75aed62d75 | |||
aaef73d89d | |||
f03a029e36 | |||
52d195710b | |||
ddacc32e64 | |||
2ae9a14d02 | |||
3019c3d213 | |||
c43869d255 | |||
18748f77ac | |||
052e28d2e3 | |||
87ec0af697 | |||
e9016737d4 | |||
f34d5eb82b | |||
12c500e125 | |||
2d566b853f | |||
ca93b521ec | |||
bce05d3171 | |||
929dd1dbaa | |||
2bcd145bea | |||
c0f903d4ca | |||
cca4ab7ae3 | |||
51e7325d3d | |||
ce085879c1 | |||
8a49003e9e | |||
dcc4276f09 | |||
f089779045 | |||
f1efb9a6c8 | |||
a7d472fb45 | |||
a0af4e3e9d |
@ -12,5 +12,5 @@ MAIL_PORT=
|
||||
MAIL_USERNAME=null
|
||||
MAIL_PASSWORD=null
|
||||
MAIL_ENCRYPTION=null
|
||||
MAIL_FROM_ADDRESS=null
|
||||
MAIL_FROM_ADDRESS="noreply@${APP_NAME}"
|
||||
MAIL_FROM_NAME="${APP_NAME}"
|
||||
|
@ -12,5 +12,5 @@ MAIL_PORT=
|
||||
MAIL_USERNAME=null
|
||||
MAIL_PASSWORD=null
|
||||
MAIL_ENCRYPTION=null
|
||||
MAIL_FROM_ADDRESS=null
|
||||
MAIL_FROM_ADDRESS="noreply@${APP_NAME}"
|
||||
MAIL_FROM_NAME="${APP_NAME}"
|
||||
|
@ -13,5 +13,5 @@ MAIL_PORT=
|
||||
MAIL_USERNAME=null
|
||||
MAIL_PASSWORD=null
|
||||
MAIL_ENCRYPTION=null
|
||||
MAIL_FROM_ADDRESS=null
|
||||
MAIL_FROM_ADDRESS="noreply@${APP_NAME}"
|
||||
MAIL_FROM_NAME="${APP_NAME}"
|
||||
|
35
.github/workflows/docker-1x.yml
vendored
Normal file
35
.github/workflows/docker-1x.yml
vendored
Normal file
@ -0,0 +1,35 @@
|
||||
name: Build and push Docker image
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 1.x
|
||||
|
||||
jobs:
|
||||
build-and-push:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v1
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
|
||||
- name: Build and push Docker image
|
||||
run: |
|
||||
docker buildx build . \
|
||||
-f docker/Dockerfile \
|
||||
-t vitodeploy/vito:1.x \
|
||||
--build-arg="RELEASE=0" \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--no-cache \
|
||||
--push
|
35
.github/workflows/docker-release.yml
vendored
Normal file
35
.github/workflows/docker-release.yml
vendored
Normal file
@ -0,0 +1,35 @@
|
||||
name: Build and push Docker image
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [created]
|
||||
|
||||
jobs:
|
||||
build-and-push:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v1
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
|
||||
- name: Build and push Docker image
|
||||
run: |
|
||||
docker buildx build . \
|
||||
-f docker/Dockerfile \
|
||||
-t vitodeploy/vito:${{ github.event.release.tag_name }} \
|
||||
-t vitodeploy/vito:latest \
|
||||
--build-arg="RELEASE=0" \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--no-cache \
|
||||
--push
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -7,6 +7,8 @@
|
||||
/storage/test-key
|
||||
/storage/test-key.pub
|
||||
/vendor
|
||||
/storage/database.sqlite
|
||||
/storage/database-test.sqlite
|
||||
.env
|
||||
.env.backup
|
||||
.env.production
|
||||
|
@ -36,8 +36,8 @@ ## Useful Links
|
||||
- [Install via Docker](https://vitodeploy.com/introduction/installation.html#install-via-docker)
|
||||
- [Feedbacks](https://vitodeploy.featurebase.app)
|
||||
- [Roadmap](https://vitodeploy.featurebase.app/roadmap)
|
||||
- [Video Demo](https://youtu.be/rLRHIyEfON8)
|
||||
- [Discord](https://discord.gg/dcUWA5DV)
|
||||
- [Video Demo](https://youtu.be/AbmUOBDOc28)
|
||||
- [Discord](https://discord.gg/uZeeHZZnm5)
|
||||
- [Contribution](/CONTRIBUTING.md)
|
||||
- [Security](/SECURITY.md)
|
||||
|
||||
|
150
app/Actions/Monitoring/GetMetrics.php
Normal file
150
app/Actions/Monitoring/GetMetrics.php
Normal file
@ -0,0 +1,150 @@
|
||||
<?php
|
||||
|
||||
namespace App\Actions\Monitoring;
|
||||
|
||||
use App\Models\Server;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Contracts\Database\Query\Expression;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
class GetMetrics
|
||||
{
|
||||
public function filter(Server $server, array $input): array
|
||||
{
|
||||
if (isset($input['from']) && isset($input['to']) && $input['from'] === $input['to']) {
|
||||
$input['from'] = Carbon::parse($input['from'])->format('Y-m-d').' 00:00:00';
|
||||
$input['to'] = Carbon::parse($input['to'])->format('Y-m-d').' 23:59:59';
|
||||
}
|
||||
|
||||
$defaultInput = [
|
||||
'period' => '10m',
|
||||
];
|
||||
|
||||
$input = array_merge($defaultInput, $input);
|
||||
|
||||
$this->validate($input);
|
||||
|
||||
return $this->metrics(
|
||||
server: $server,
|
||||
fromDate: $this->getFromDate($input),
|
||||
toDate: $this->getToDate($input),
|
||||
interval: $this->getInterval($input)
|
||||
);
|
||||
}
|
||||
|
||||
private function metrics(
|
||||
Server $server,
|
||||
Carbon $fromDate,
|
||||
Carbon $toDate,
|
||||
?Expression $interval = null
|
||||
): array {
|
||||
$metrics = DB::table('metrics')
|
||||
->where('server_id', $server->id)
|
||||
->whereBetween('created_at', [$fromDate->format('Y-m-d H:i:s'), $toDate->format('Y-m-d H:i:s')])
|
||||
->select(
|
||||
[
|
||||
DB::raw('created_at as date'),
|
||||
DB::raw('ROUND(AVG(load), 2) as load'),
|
||||
DB::raw('ROUND(AVG(memory_total), 2) as memory_total'),
|
||||
DB::raw('ROUND(AVG(memory_used), 2) as memory_used'),
|
||||
DB::raw('ROUND(AVG(memory_free), 2) as memory_free'),
|
||||
DB::raw('ROUND(AVG(disk_total), 2) as disk_total'),
|
||||
DB::raw('ROUND(AVG(disk_used), 2) as disk_used'),
|
||||
DB::raw('ROUND(AVG(disk_free), 2) as disk_free'),
|
||||
$interval,
|
||||
],
|
||||
)
|
||||
->groupByRaw('date_interval')
|
||||
->orderBy('date_interval')
|
||||
->get()
|
||||
->map(function ($item) {
|
||||
$item->date = Carbon::parse($item->date)->format('Y-m-d H:i');
|
||||
|
||||
return $item;
|
||||
});
|
||||
|
||||
return [
|
||||
'metrics' => $metrics,
|
||||
];
|
||||
}
|
||||
|
||||
private function getFromDate(array $input): Carbon
|
||||
{
|
||||
if ($input['period'] === 'custom') {
|
||||
return new Carbon($input['from']);
|
||||
}
|
||||
|
||||
return Carbon::parse('-'.convert_time_format($input['period']));
|
||||
}
|
||||
|
||||
private function getToDate(array $input): Carbon
|
||||
{
|
||||
if ($input['period'] === 'custom') {
|
||||
return new Carbon($input['to']);
|
||||
}
|
||||
|
||||
return Carbon::now();
|
||||
}
|
||||
|
||||
private function getInterval(array $input): Expression
|
||||
{
|
||||
if ($input['period'] === 'custom') {
|
||||
$from = new Carbon($input['from']);
|
||||
$to = new Carbon($input['to']);
|
||||
$periodInHours = $from->diffInHours($to);
|
||||
}
|
||||
|
||||
if (! isset($periodInHours)) {
|
||||
$periodInHours = Carbon::parse(
|
||||
convert_time_format($input['period'])
|
||||
)->diffInHours();
|
||||
}
|
||||
|
||||
if ($periodInHours <= 1) {
|
||||
return DB::raw("strftime('%Y-%m-%d %H:%M:00', created_at) as date_interval");
|
||||
}
|
||||
|
||||
if ($periodInHours <= 24) {
|
||||
return DB::raw("strftime('%Y-%m-%d %H:00:00', created_at) as date_interval");
|
||||
}
|
||||
|
||||
if ($periodInHours > 24) {
|
||||
return DB::raw("strftime('%Y-%m-%d 00:00:00', created_at) as date_interval");
|
||||
}
|
||||
}
|
||||
|
||||
private function validate(array $input): void
|
||||
{
|
||||
Validator::make($input, [
|
||||
'period' => [
|
||||
'required',
|
||||
Rule::in([
|
||||
'10m',
|
||||
'30m',
|
||||
'1h',
|
||||
'12h',
|
||||
'1d',
|
||||
'7d',
|
||||
'custom',
|
||||
]),
|
||||
],
|
||||
])->validate();
|
||||
|
||||
if ($input['period'] === 'custom') {
|
||||
Validator::make($input, [
|
||||
'from' => [
|
||||
'required',
|
||||
'date',
|
||||
'before:to',
|
||||
],
|
||||
'to' => [
|
||||
'required',
|
||||
'date',
|
||||
'after:from',
|
||||
],
|
||||
])->validate();
|
||||
}
|
||||
}
|
||||
}
|
32
app/Actions/Monitoring/UpdateMetricSettings.php
Normal file
32
app/Actions/Monitoring/UpdateMetricSettings.php
Normal file
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace App\Actions\Monitoring;
|
||||
|
||||
use App\Models\Server;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
class UpdateMetricSettings
|
||||
{
|
||||
public function update(Server $server, array $input): void
|
||||
{
|
||||
$this->validate($input);
|
||||
|
||||
$service = $server->monitoring();
|
||||
|
||||
$data = $service->handler()->data();
|
||||
$data['data_retention'] = $input['data_retention'];
|
||||
$service->type_data = $data;
|
||||
$service->save();
|
||||
}
|
||||
|
||||
private function validate(array $input): void
|
||||
{
|
||||
Validator::make($input, [
|
||||
'data_retention' => [
|
||||
'required',
|
||||
Rule::in(config('core.metrics_data_retention')),
|
||||
],
|
||||
])->validate();
|
||||
}
|
||||
}
|
@ -4,6 +4,7 @@
|
||||
|
||||
use App\Enums\ServiceStatus;
|
||||
use App\Models\Server;
|
||||
use App\SSH\Services\PHP\PHP;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
|
||||
class ChangeDefaultCli
|
||||
@ -12,7 +13,9 @@ public function change(Server $server, array $input): void
|
||||
{
|
||||
$this->validate($server, $input);
|
||||
$service = $server->php($input['version']);
|
||||
$service->handler()->setDefaultCli();
|
||||
/** @var PHP $handler */
|
||||
$handler = $service->handler();
|
||||
$handler->setDefaultCli();
|
||||
$server->defaultService('php')->update(['is_default' => 0]);
|
||||
$service->update(['is_default' => 1]);
|
||||
$service->update(['status' => ServiceStatus::READY]);
|
||||
|
@ -3,6 +3,7 @@
|
||||
namespace App\Actions\PHP;
|
||||
|
||||
use App\Models\Server;
|
||||
use App\SSH\Services\PHP\PHP;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
|
||||
class GetPHPIni
|
||||
@ -14,7 +15,10 @@ public function getIni(Server $server, array $input): string
|
||||
$php = $server->php($input['version']);
|
||||
|
||||
try {
|
||||
return $php->handler()->getPHPIni();
|
||||
/** @var PHP $handler */
|
||||
$handler = $php->handler();
|
||||
|
||||
return $handler->getPHPIni();
|
||||
} catch (\Throwable $e) {
|
||||
throw ValidationException::withMessages(
|
||||
['ini' => $e->getMessage()]
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
use App\Models\Server;
|
||||
use App\Models\Service;
|
||||
use App\SSH\Services\PHP\PHP;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Illuminate\Validation\Rule;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
@ -23,7 +24,9 @@ public function install(Server $server, array $input): Service
|
||||
$service->save();
|
||||
|
||||
dispatch(function () use ($service, $input) {
|
||||
$service->handler()->installExtension($input['extension']);
|
||||
/** @var PHP $handler */
|
||||
$handler = $service->handler();
|
||||
$handler->installExtension($input['extension']);
|
||||
})->catch(function () use ($service, $input) {
|
||||
$service->refresh();
|
||||
$typeData = $service->type_data;
|
||||
|
@ -34,8 +34,6 @@ public function create(Site $site, array $input): void
|
||||
$ssl->status = SslStatus::CREATED;
|
||||
$ssl->save();
|
||||
$site->type()->edit();
|
||||
})->catch(function () use ($ssl) {
|
||||
$ssl->delete();
|
||||
});
|
||||
}
|
||||
|
||||
|
35
app/Actions/Server/CreateServerLog.php
Executable file
35
app/Actions/Server/CreateServerLog.php
Executable file
@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace App\Actions\Server;
|
||||
|
||||
use App\Models\Server;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
|
||||
class CreateServerLog
|
||||
{
|
||||
/**
|
||||
* @throws ValidationException
|
||||
*/
|
||||
public function create(Server $server, array $input): void
|
||||
{
|
||||
$this->validate($input);
|
||||
|
||||
$server->logs()->create([
|
||||
'is_remote' => true,
|
||||
'name' => $input['path'],
|
||||
'type' => 'remote',
|
||||
'disk' => 'ssh',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws ValidationException
|
||||
*/
|
||||
protected function validate(array $input): void
|
||||
{
|
||||
Validator::make($input, [
|
||||
'path' => 'required',
|
||||
])->validate();
|
||||
}
|
||||
}
|
@ -8,14 +8,15 @@
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
class Create
|
||||
class Install
|
||||
{
|
||||
public function create(Server $server, array $input): Service
|
||||
public function install(Server $server, array $input): Service
|
||||
{
|
||||
$this->validate($server, $input);
|
||||
|
||||
$service = new Service([
|
||||
'name' => $input['type'],
|
||||
'server_id' => $server->id,
|
||||
'name' => $input['name'],
|
||||
'type' => $input['type'],
|
||||
'version' => $input['version'],
|
||||
'status' => ServiceStatus::INSTALLING,
|
||||
@ -27,15 +28,13 @@ public function create(Server $server, array $input): Service
|
||||
|
||||
$service->save();
|
||||
|
||||
$service->handler()->create();
|
||||
|
||||
dispatch(function () use ($service) {
|
||||
$service->handler()->install();
|
||||
$service->status = ServiceStatus::READY;
|
||||
$service->save();
|
||||
})->catch(function () use ($service) {
|
||||
$service->handler()->delete();
|
||||
$service->delete();
|
||||
$service->status = ServiceStatus::INSTALLATION_FAILED;
|
||||
$service->save();
|
||||
})->onConnection('ssh');
|
||||
|
||||
return $service;
|
||||
@ -46,8 +45,11 @@ private function validate(Server $server, array $input): void
|
||||
Validator::make($input, [
|
||||
'type' => [
|
||||
'required',
|
||||
Rule::in(config('core.add_on_services')),
|
||||
Rule::unique('services', 'type')->where('server_id', $server->id),
|
||||
Rule::in(config('core.service_types')),
|
||||
],
|
||||
'name' => [
|
||||
'required',
|
||||
Rule::in(array_keys(config('core.service_types'))),
|
||||
],
|
||||
'version' => 'required',
|
||||
])->validate();
|
28
app/Actions/Service/Uninstall.php
Normal file
28
app/Actions/Service/Uninstall.php
Normal file
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace App\Actions\Service;
|
||||
|
||||
use App\Enums\ServiceStatus;
|
||||
use App\Models\Service;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
|
||||
class Uninstall
|
||||
{
|
||||
public function uninstall(Service $service): void
|
||||
{
|
||||
Validator::make([
|
||||
'service' => $service->id,
|
||||
], $service->handler()->deletionRules())->validate();
|
||||
|
||||
$service->status = ServiceStatus::UNINSTALLING;
|
||||
$service->save();
|
||||
|
||||
dispatch(function () use ($service) {
|
||||
$service->handler()->uninstall();
|
||||
$service->delete();
|
||||
})->catch(function () use ($service) {
|
||||
$service->status = ServiceStatus::FAILED;
|
||||
$service->save();
|
||||
})->onConnection('ssh');
|
||||
}
|
||||
}
|
@ -3,6 +3,8 @@
|
||||
namespace App\Actions\Site;
|
||||
|
||||
use App\Enums\SiteStatus;
|
||||
use App\Exceptions\RepositoryNotFound;
|
||||
use App\Exceptions\RepositoryPermissionDenied;
|
||||
use App\Exceptions\SourceControlIsNotConnected;
|
||||
use App\Facades\Notifier;
|
||||
use App\Models\Server;
|
||||
@ -19,7 +21,6 @@
|
||||
class CreateSite
|
||||
{
|
||||
/**
|
||||
* @throws SourceControlIsNotConnected
|
||||
* @throws ValidationException
|
||||
*/
|
||||
public function create(Server $server, array $input): Site
|
||||
@ -47,7 +48,15 @@ public function create(Server $server, array $input): Site
|
||||
}
|
||||
} catch (SourceControlIsNotConnected) {
|
||||
throw ValidationException::withMessages([
|
||||
'source_control' => __('Source control is not connected'),
|
||||
'source_control' => 'Source control is not connected',
|
||||
]);
|
||||
} catch (RepositoryPermissionDenied) {
|
||||
throw ValidationException::withMessages([
|
||||
'repository' => 'You do not have permission to access this repository',
|
||||
]);
|
||||
} catch (RepositoryNotFound) {
|
||||
throw ValidationException::withMessages([
|
||||
'repository' => 'Repository not found',
|
||||
]);
|
||||
}
|
||||
|
||||
|
49
app/Actions/Site/UpdateSourceControl.php
Normal file
49
app/Actions/Site/UpdateSourceControl.php
Normal file
@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
namespace App\Actions\Site;
|
||||
|
||||
use App\Exceptions\RepositoryNotFound;
|
||||
use App\Exceptions\RepositoryPermissionDenied;
|
||||
use App\Exceptions\SourceControlIsNotConnected;
|
||||
use App\Models\Site;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Illuminate\Validation\Rule;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
|
||||
class UpdateSourceControl
|
||||
{
|
||||
public function update(Site $site, array $input): void
|
||||
{
|
||||
$this->validate($input);
|
||||
|
||||
$site->source_control_id = $input['source_control'];
|
||||
try {
|
||||
if ($site->sourceControl()) {
|
||||
$site->sourceControl()->getRepo($site->repository);
|
||||
}
|
||||
} catch (SourceControlIsNotConnected) {
|
||||
throw ValidationException::withMessages([
|
||||
'source_control' => 'Source control is not connected',
|
||||
]);
|
||||
} catch (RepositoryPermissionDenied) {
|
||||
throw ValidationException::withMessages([
|
||||
'repository' => 'You do not have permission to access this repository',
|
||||
]);
|
||||
} catch (RepositoryNotFound) {
|
||||
throw ValidationException::withMessages([
|
||||
'repository' => 'Repository not found',
|
||||
]);
|
||||
}
|
||||
$site->save();
|
||||
}
|
||||
|
||||
private function validate(array $input): void
|
||||
{
|
||||
Validator::make($input, [
|
||||
'source_control' => [
|
||||
'required',
|
||||
Rule::exists('source_controls', 'id'),
|
||||
],
|
||||
])->validate();
|
||||
}
|
||||
}
|
28
app/Console/Commands/DeleteOlderMetricsCommand.php
Normal file
28
app/Console/Commands/DeleteOlderMetricsCommand.php
Normal file
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Models\Service;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class DeleteOlderMetricsCommand extends Command
|
||||
{
|
||||
protected $signature = 'metrics:delete-older-metrics';
|
||||
|
||||
protected $description = 'Delete older metrics from database';
|
||||
|
||||
public function handle()
|
||||
{
|
||||
Service::query()->where('type', 'monitoring')->chunk(100, function ($services) {
|
||||
$services->each(function ($service) {
|
||||
$this->info("Deleting older metrics for service {$service->server->name}");
|
||||
$service
|
||||
->server
|
||||
->metrics()
|
||||
->where('created_at', '<', now()->subDays($service->handler()->data()['data_retention']))
|
||||
->delete();
|
||||
$this->info('Metrics deleted');
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
31
app/Console/Commands/GetMetricsCommand.php
Normal file
31
app/Console/Commands/GetMetricsCommand.php
Normal file
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Models\Server;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
|
||||
class GetMetricsCommand extends Command
|
||||
{
|
||||
protected $signature = 'metrics:get';
|
||||
|
||||
protected $description = 'Get server metrics';
|
||||
|
||||
public function handle(): void
|
||||
{
|
||||
$checkedMetrics = 0;
|
||||
Server::query()->whereHas('services', function (Builder $query) {
|
||||
$query->where('type', 'monitoring')
|
||||
->where('name', 'remote-monitor');
|
||||
})->chunk(10, function ($servers) use (&$checkedMetrics) {
|
||||
/** @var Server $server */
|
||||
foreach ($servers as $server) {
|
||||
$info = $server->os()->resourceInfo();
|
||||
$server->metrics()->create(array_merge($info, ['server_id' => $server->id]));
|
||||
$checkedMetrics++;
|
||||
}
|
||||
});
|
||||
$this->info("Checked $checkedMetrics metrics");
|
||||
}
|
||||
}
|
@ -16,6 +16,8 @@ protected function schedule(Schedule $schedule): void
|
||||
$schedule->command('backups:run "0 0 * * *"')->daily();
|
||||
$schedule->command('backups:run "0 0 * * 0"')->weekly();
|
||||
$schedule->command('backups:run "0 0 1 * *"')->monthly();
|
||||
$schedule->command('metrics:delete-older-metrics')->daily();
|
||||
$schedule->command('metrics:get')->everyMinute();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2,9 +2,7 @@
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class SSHAuthenticationError extends Exception
|
||||
class SSHAuthenticationError extends SSHError
|
||||
{
|
||||
//
|
||||
}
|
||||
|
@ -2,9 +2,7 @@
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class SSLCreationException extends Exception
|
||||
class SSLCreationException extends SSHError
|
||||
{
|
||||
//
|
||||
}
|
||||
|
@ -133,7 +133,7 @@ public function exec(string $command, string $log = '', ?int $siteId = null, ?bo
|
||||
$this->log?->write($output);
|
||||
|
||||
if (Str::contains($output, 'VITO_SSH_ERROR')) {
|
||||
throw new Exception('SSH command failed with an error');
|
||||
throw new SSHCommandError('SSH command failed with an error');
|
||||
}
|
||||
|
||||
return $output;
|
||||
|
36
app/Http/Controllers/API/AgentController.php
Normal file
36
app/Http/Controllers/API/AgentController.php
Normal file
@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\API;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Server;
|
||||
use App\Models\Service;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class AgentController extends Controller
|
||||
{
|
||||
public function __invoke(Request $request, Server $server, int $id): JsonResponse
|
||||
{
|
||||
$validated = $this->validate($request, [
|
||||
'load' => 'required|numeric',
|
||||
'memory_total' => 'required|numeric',
|
||||
'memory_used' => 'required|numeric',
|
||||
'memory_free' => 'required|numeric',
|
||||
'disk_total' => 'required|numeric',
|
||||
'disk_used' => 'required|numeric',
|
||||
'disk_free' => 'required|numeric',
|
||||
]);
|
||||
|
||||
/** @var Service $service */
|
||||
$service = $server->services()->findOrFail($id);
|
||||
|
||||
if ($request->header('secret') !== $service->handler()->data()['secret']) {
|
||||
return response()->json(['error' => 'Unauthorized'], 401);
|
||||
}
|
||||
|
||||
$server->metrics()->create(array_merge($validated, ['server_id' => $server->id]));
|
||||
|
||||
return response()->json();
|
||||
}
|
||||
}
|
@ -1,10 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
namespace App\Http\Controllers\API;
|
||||
|
||||
use App\Actions\Site\Deploy;
|
||||
use App\Exceptions\SourceControlIsNotConnected;
|
||||
use App\Facades\Notifier;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\GitHook;
|
||||
use App\Models\ServerLog;
|
||||
use App\Notifications\SourceControlDisconnected;
|
16
app/Http/Controllers/API/HealthController.php
Normal file
16
app/Http/Controllers/API/HealthController.php
Normal file
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\API;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
|
||||
class HealthController extends Controller
|
||||
{
|
||||
public function __invoke()
|
||||
{
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'version' => vito_version(),
|
||||
]);
|
||||
}
|
||||
}
|
@ -7,6 +7,8 @@
|
||||
use App\Actions\Site\UpdateDeploymentScript;
|
||||
use App\Actions\Site\UpdateEnv;
|
||||
use App\Exceptions\DeploymentScriptIsEmptyException;
|
||||
use App\Exceptions\RepositoryNotFound;
|
||||
use App\Exceptions\RepositoryPermissionDenied;
|
||||
use App\Exceptions\SourceControlIsNotConnected;
|
||||
use App\Facades\Toast;
|
||||
use App\Helpers\HtmxResponse;
|
||||
@ -24,12 +26,14 @@ public function deploy(Server $server, Site $site): HtmxResponse
|
||||
app(Deploy::class)->run($site);
|
||||
|
||||
Toast::success('Deployment started!');
|
||||
} catch (SourceControlIsNotConnected $e) {
|
||||
Toast::error($e->getMessage());
|
||||
|
||||
return htmx()->redirect(route('source-controls'));
|
||||
} catch (SourceControlIsNotConnected) {
|
||||
Toast::error('Source control is not connected. Check site\'s settings.');
|
||||
} catch (DeploymentScriptIsEmptyException) {
|
||||
Toast::error('Deployment script is empty!');
|
||||
} catch (RepositoryPermissionDenied) {
|
||||
Toast::error('You do not have permission to access this repository!');
|
||||
} catch (RepositoryNotFound) {
|
||||
Toast::error('Repository not found!');
|
||||
}
|
||||
|
||||
return htmx()->back();
|
||||
@ -83,6 +87,12 @@ public function enableAutoDeployment(Server $server, Site $site): HtmxResponse
|
||||
Toast::success('Auto deployment has been enabled.');
|
||||
} catch (SourceControlIsNotConnected) {
|
||||
Toast::error('Source control is not connected. Check site\'s settings.');
|
||||
} catch (DeploymentScriptIsEmptyException) {
|
||||
Toast::error('Deployment script is empty!');
|
||||
} catch (RepositoryPermissionDenied) {
|
||||
Toast::error('You do not have permission to access this repository!');
|
||||
} catch (RepositoryNotFound) {
|
||||
Toast::error('Repository not found!');
|
||||
}
|
||||
}
|
||||
|
||||
|
44
app/Http/Controllers/MetricController.php
Normal file
44
app/Http/Controllers/MetricController.php
Normal file
@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Actions\Monitoring\GetMetrics;
|
||||
use App\Actions\Monitoring\UpdateMetricSettings;
|
||||
use App\Facades\Toast;
|
||||
use App\Helpers\HtmxResponse;
|
||||
use App\Models\Server;
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class MetricController extends Controller
|
||||
{
|
||||
public function index(Server $server, Request $request): View|RedirectResponse
|
||||
{
|
||||
$this->checkIfMonitoringServiceInstalled($server);
|
||||
|
||||
return view('metrics.index', [
|
||||
'server' => $server,
|
||||
'data' => app(GetMetrics::class)->filter($server, $request->input()),
|
||||
'lastMetric' => $server->metrics()->latest()->first(),
|
||||
]);
|
||||
}
|
||||
|
||||
public function settings(Server $server, Request $request): HtmxResponse
|
||||
{
|
||||
$this->checkIfMonitoringServiceInstalled($server);
|
||||
|
||||
app(UpdateMetricSettings::class)->update($server, $request->input());
|
||||
|
||||
Toast::success('Metric settings updated successfully');
|
||||
|
||||
return htmx()->back();
|
||||
}
|
||||
|
||||
private function checkIfMonitoringServiceInstalled(Server $server): void
|
||||
{
|
||||
if (! $server->monitoring()) {
|
||||
abort(404, 'Monitoring service is not installed on this server');
|
||||
}
|
||||
}
|
||||
}
|
@ -54,7 +54,6 @@ public function show(Server $server): View
|
||||
{
|
||||
return view('servers.show', [
|
||||
'server' => $server,
|
||||
'logs' => $server->logs()->latest()->limit(10)->get(),
|
||||
]);
|
||||
}
|
||||
|
||||
|
@ -2,10 +2,13 @@
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Actions\Server\CreateServerLog;
|
||||
use App\Facades\Toast;
|
||||
use App\Models\Server;
|
||||
use App\Models\ServerLog;
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class ServerLogController extends Controller
|
||||
{
|
||||
@ -13,6 +16,7 @@ public function index(Server $server): View
|
||||
{
|
||||
return view('server-logs.index', [
|
||||
'server' => $server,
|
||||
'pageTitle' => __('Vito Logs'),
|
||||
]);
|
||||
}
|
||||
|
||||
@ -26,4 +30,31 @@ public function show(Server $server, ServerLog $serverLog): RedirectResponse
|
||||
'content' => $serverLog->getContent(),
|
||||
]);
|
||||
}
|
||||
|
||||
public function remote(Server $server): View
|
||||
{
|
||||
return view('server-logs.remote-logs', [
|
||||
'server' => $server,
|
||||
'remote' => true,
|
||||
'pageTitle' => __('Remote Logs'),
|
||||
]);
|
||||
}
|
||||
|
||||
public function store(Server $server, Request $request): \App\Helpers\HtmxResponse
|
||||
{
|
||||
app(CreateServerLog::class)->create($server, $request->input());
|
||||
|
||||
Toast::success('Log added successfully.');
|
||||
|
||||
return htmx()->redirect(route('servers.logs.remote', ['server' => $server]));
|
||||
}
|
||||
|
||||
public function destroy(Server $server, ServerLog $serverLog): RedirectResponse
|
||||
{
|
||||
$serverLog->delete();
|
||||
|
||||
Toast::success('Remote log deleted successfully.');
|
||||
|
||||
return redirect()->route('servers.logs.remote', ['server' => $server]);
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,8 @@
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Actions\Service\Create;
|
||||
use App\Actions\Service\Install;
|
||||
use App\Actions\Service\Uninstall;
|
||||
use App\Facades\Toast;
|
||||
use App\Helpers\HtmxResponse;
|
||||
use App\Models\Server;
|
||||
@ -68,7 +69,16 @@ public function disable(Server $server, Service $service): RedirectResponse
|
||||
|
||||
public function install(Server $server, Request $request): HtmxResponse
|
||||
{
|
||||
app(Create::class)->create($server, $request->input());
|
||||
app(Install::class)->install($server, $request->input());
|
||||
|
||||
Toast::success('Service is being uninstalled!');
|
||||
|
||||
return htmx()->back();
|
||||
}
|
||||
|
||||
public function uninstall(Server $server, Service $service): HtmxResponse
|
||||
{
|
||||
app(Uninstall::class)->uninstall($service);
|
||||
|
||||
Toast::success('Service is being uninstalled!');
|
||||
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
use App\Actions\Site\CreateSite;
|
||||
use App\Actions\Site\DeleteSite;
|
||||
use App\Enums\SiteStatus;
|
||||
use App\Enums\SiteType;
|
||||
use App\Facades\Toast;
|
||||
use App\Helpers\HtmxResponse;
|
||||
@ -42,14 +43,38 @@ public function create(Server $server): View
|
||||
]);
|
||||
}
|
||||
|
||||
public function show(Server $server, Site $site): View
|
||||
public function show(Server $server, Site $site, Request $request): View|RedirectResponse|HtmxResponse
|
||||
{
|
||||
if (in_array($site->status, [SiteStatus::INSTALLING, SiteStatus::INSTALLATION_FAILED])) {
|
||||
if ($request->hasHeader('HX-Request')) {
|
||||
return htmx()->redirect(route('servers.sites.installing', [$server, $site]));
|
||||
}
|
||||
|
||||
return redirect()->route('servers.sites.installing', [$server, $site]);
|
||||
}
|
||||
|
||||
return view('sites.show', [
|
||||
'server' => $server,
|
||||
'site' => $site,
|
||||
]);
|
||||
}
|
||||
|
||||
public function installing(Server $server, Site $site, Request $request): View|RedirectResponse|HtmxResponse
|
||||
{
|
||||
if (! in_array($site->status, [SiteStatus::INSTALLING, SiteStatus::INSTALLATION_FAILED])) {
|
||||
if ($request->hasHeader('HX-Request')) {
|
||||
return htmx()->redirect(route('servers.sites.show', [$server, $site]));
|
||||
}
|
||||
|
||||
return redirect()->route('servers.sites.show', [$server, $site]);
|
||||
}
|
||||
|
||||
return view('sites.installing', [
|
||||
'server' => $server,
|
||||
'site' => $site,
|
||||
]);
|
||||
}
|
||||
|
||||
public function destroy(Server $server, Site $site): RedirectResponse
|
||||
{
|
||||
app(DeleteSite::class)->delete($site);
|
||||
|
@ -13,6 +13,7 @@ public function index(Server $server, Site $site): View
|
||||
return view('site-logs.index', [
|
||||
'server' => $server,
|
||||
'site' => $site,
|
||||
'pageTitle' => __('Vito Logs'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
@ -2,10 +2,12 @@
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Actions\Site\UpdateSourceControl;
|
||||
use App\Facades\Toast;
|
||||
use App\Helpers\HtmxResponse;
|
||||
use App\Models\Server;
|
||||
use App\Models\Site;
|
||||
use App\SSH\Services\Webserver\Webserver;
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
@ -24,7 +26,10 @@ public function index(Server $server, Site $site): View
|
||||
|
||||
public function getVhost(Server $server, Site $site): RedirectResponse
|
||||
{
|
||||
return back()->with('vhost', $server->webserver()->handler()->getVHost($site));
|
||||
/** @var Webserver $handler */
|
||||
$handler = $server->webserver()->handler();
|
||||
|
||||
return back()->with('vhost', $handler->getVHost($site));
|
||||
}
|
||||
|
||||
public function updateVhost(Server $server, Site $site, Request $request): RedirectResponse
|
||||
@ -34,7 +39,9 @@ public function updateVhost(Server $server, Site $site, Request $request): Redir
|
||||
]);
|
||||
|
||||
try {
|
||||
$server->webserver()->handler()->updateVHost($site, false, $request->input('vhost'));
|
||||
/** @var Webserver $handler */
|
||||
$handler = $server->webserver()->handler();
|
||||
$handler->updateVHost($site, false, $request->input('vhost'));
|
||||
|
||||
Toast::success('VHost updated successfully!');
|
||||
} catch (Throwable $e) {
|
||||
@ -63,4 +70,13 @@ public function updatePHPVersion(Server $server, Site $site, Request $request):
|
||||
|
||||
return htmx()->back();
|
||||
}
|
||||
|
||||
public function updateSourceControl(Server $server, Site $site, Request $request): HtmxResponse
|
||||
{
|
||||
$site = app(UpdateSourceControl::class)->update($site, $request->input());
|
||||
|
||||
Toast::success('Source control updated successfully!');
|
||||
|
||||
return htmx()->back();
|
||||
}
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ class TrustProxies extends Middleware
|
||||
*
|
||||
* @var array<int, string>|string|null
|
||||
*/
|
||||
protected $proxies;
|
||||
protected $proxies = '*';
|
||||
|
||||
/**
|
||||
* The headers that should be used to detect proxies.
|
||||
@ -20,7 +20,7 @@ class TrustProxies extends Middleware
|
||||
* @var int
|
||||
*/
|
||||
protected $headers =
|
||||
Request::HEADER_X_FORWARDED_FOR |
|
||||
Request::HEADER_X_FORWARDED_FOR |
|
||||
Request::HEADER_X_FORWARDED_HOST |
|
||||
Request::HEADER_X_FORWARDED_PORT |
|
||||
Request::HEADER_X_FORWARDED_PROTO |
|
||||
|
53
app/Models/Metric.php
Normal file
53
app/Models/Metric.php
Normal file
@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
/**
|
||||
* @property int $id
|
||||
* @property int $server_id
|
||||
* @property float $load
|
||||
* @property float $memory_total
|
||||
* @property float $memory_used
|
||||
* @property float $memory_free
|
||||
* @property float $disk_total
|
||||
* @property float $disk_used
|
||||
* @property float $disk_free
|
||||
* @property Server $server
|
||||
* @property \Carbon\Carbon $created_at
|
||||
* @property \Carbon\Carbon $updated_at
|
||||
*/
|
||||
class Metric extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $fillable = [
|
||||
'server_id',
|
||||
'load',
|
||||
'memory_total',
|
||||
'memory_used',
|
||||
'memory_free',
|
||||
'disk_total',
|
||||
'disk_used',
|
||||
'disk_free',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'server_id' => 'integer',
|
||||
'load' => 'float',
|
||||
'memory_total' => 'float',
|
||||
'memory_used' => 'float',
|
||||
'memory_free' => 'float',
|
||||
'disk_total' => 'float',
|
||||
'disk_used' => 'float',
|
||||
'disk_free' => 'float',
|
||||
];
|
||||
|
||||
public function server(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Server::class);
|
||||
}
|
||||
}
|
@ -195,6 +195,11 @@ public function daemons(): HasMany
|
||||
return $this->queues()->whereNull('site_id');
|
||||
}
|
||||
|
||||
public function metrics(): HasMany
|
||||
{
|
||||
return $this->hasMany(Metric::class);
|
||||
}
|
||||
|
||||
public function sshKeys(): BelongsToMany
|
||||
{
|
||||
return $this->belongsToMany(SshKey::class, 'server_ssh_keys')
|
||||
@ -325,6 +330,24 @@ public function php(?string $version = null): ?Service
|
||||
return $this->service('php', $version);
|
||||
}
|
||||
|
||||
public function memoryDatabase(?string $version = null): ?Service
|
||||
{
|
||||
if (! $version) {
|
||||
return $this->defaultService('memory_database');
|
||||
}
|
||||
|
||||
return $this->service('memory_database', $version);
|
||||
}
|
||||
|
||||
public function monitoring(?string $version = null): ?Service
|
||||
{
|
||||
if (! $version) {
|
||||
return $this->defaultService('monitoring');
|
||||
}
|
||||
|
||||
return $this->service('monitoring', $version);
|
||||
}
|
||||
|
||||
public function sshKey(): array
|
||||
{
|
||||
/** @var FilesystemAdapter $storageDisk */
|
||||
|
@ -16,6 +16,7 @@
|
||||
* @property string $disk
|
||||
* @property Server $server
|
||||
* @property ?Site $site
|
||||
* @property bool $is_remote
|
||||
*/
|
||||
class ServerLog extends AbstractModel
|
||||
{
|
||||
@ -27,11 +28,13 @@ class ServerLog extends AbstractModel
|
||||
'type',
|
||||
'name',
|
||||
'disk',
|
||||
'is_remote',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'server_id' => 'integer',
|
||||
'site_id' => 'integer',
|
||||
'is_remote' => 'boolean',
|
||||
];
|
||||
|
||||
public static function boot(): void
|
||||
@ -64,6 +67,17 @@ public function site(): BelongsTo
|
||||
return $this->belongsTo(Site::class);
|
||||
}
|
||||
|
||||
public static function getRemote($query, bool $active = true, ?Site $site = null)
|
||||
{
|
||||
$query->where('is_remote', $active);
|
||||
|
||||
if ($site) {
|
||||
$query->where('name', 'like', $site->path.'%');
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
public function write($buf): void
|
||||
{
|
||||
if (Str::contains($buf, 'VITO_SSH_ERROR')) {
|
||||
@ -78,6 +92,10 @@ public function write($buf): void
|
||||
|
||||
public function getContent(): ?string
|
||||
{
|
||||
if ($this->is_remote) {
|
||||
return $this->server->os()->tail($this->name, 150);
|
||||
}
|
||||
|
||||
if (Storage::disk($this->disk)->exists($this->name)) {
|
||||
return Storage::disk($this->disk)->get($this->name);
|
||||
}
|
||||
|
@ -4,13 +4,7 @@
|
||||
|
||||
use App\Actions\Service\Manage;
|
||||
use App\Exceptions\ServiceInstallationFailed;
|
||||
use App\SSH\Services\AddOnServices\AbstractAddOnService;
|
||||
use App\SSH\Services\Database\Database as DatabaseHandler;
|
||||
use App\SSH\Services\Firewall\Firewall as FirewallHandler;
|
||||
use App\SSH\Services\PHP\PHP as PHPHandler;
|
||||
use App\SSH\Services\ProcessManager\ProcessManager as ProcessManagerHandler;
|
||||
use App\SSH\Services\Redis\Redis as RedisHandler;
|
||||
use App\SSH\Services\Webserver\Webserver as WebserverHandler;
|
||||
use App\SSH\Services\ServiceInterface;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Support\Str;
|
||||
@ -65,8 +59,8 @@ public function server(): BelongsTo
|
||||
return $this->belongsTo(Server::class);
|
||||
}
|
||||
|
||||
public function handler(
|
||||
): PHPHandler|WebserverHandler|DatabaseHandler|FirewallHandler|ProcessManagerHandler|RedisHandler|AbstractAddOnService {
|
||||
public function handler(): ServiceInterface
|
||||
{
|
||||
$handler = config('core.service_handlers')[$this->name];
|
||||
|
||||
return new $handler($this);
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
use App\Exceptions\SourceControlIsNotConnected;
|
||||
use App\SiteTypes\SiteType;
|
||||
use App\SSH\Services\Webserver\Webserver;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
@ -185,7 +186,9 @@ public function php(): ?Service
|
||||
|
||||
public function changePHPVersion($version): void
|
||||
{
|
||||
$this->server->webserver()->handler()->changePHPVersion($this, $version);
|
||||
/** @var Webserver $handler */
|
||||
$handler = $this->server->webserver()->handler();
|
||||
$handler->changePHPVersion($this, $version);
|
||||
$this->php_version = $version;
|
||||
$this->save();
|
||||
}
|
||||
|
@ -7,7 +7,6 @@
|
||||
use App\Helpers\Toast;
|
||||
use Illuminate\Contracts\Container\BindingResolutionException;
|
||||
use Illuminate\Http\Resources\Json\ResourceCollection;
|
||||
use Illuminate\Support\Facades\URL;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
|
||||
class AppServiceProvider extends ServiceProvider
|
||||
@ -37,9 +36,5 @@ public function boot(): void
|
||||
$this->app->bind('toast', function () {
|
||||
return new Toast;
|
||||
});
|
||||
|
||||
if (str(config('app.url'))->startsWith('https://')) {
|
||||
URL::forceScheme('https');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -98,12 +98,12 @@ public function reboot(): void
|
||||
);
|
||||
}
|
||||
|
||||
public function editFile(string $path, string $content): void
|
||||
public function editFile(string $path, ?string $content = null): void
|
||||
{
|
||||
$this->server->ssh()->exec(
|
||||
$this->getScript('edit-file.sh', [
|
||||
'path' => $path,
|
||||
'content' => $content,
|
||||
'content' => $content ?? '',
|
||||
]),
|
||||
);
|
||||
}
|
||||
@ -117,6 +117,16 @@ public function readFile(string $path): string
|
||||
);
|
||||
}
|
||||
|
||||
public function tail(string $path, int $lines): string
|
||||
{
|
||||
return $this->server->ssh()->exec(
|
||||
$this->getScript('tail.sh', [
|
||||
'path' => $path,
|
||||
'lines' => $lines,
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
public function runScript(string $path, string $script, ?int $siteId = null): ServerLog
|
||||
{
|
||||
$ssh = $this->server->ssh();
|
||||
@ -148,4 +158,29 @@ public function unzip(string $path): string
|
||||
'unzip '.$path
|
||||
);
|
||||
}
|
||||
|
||||
public function cleanup(): void
|
||||
{
|
||||
$this->server->ssh()->exec(
|
||||
$this->getScript('cleanup.sh'),
|
||||
'cleanup'
|
||||
);
|
||||
}
|
||||
|
||||
public function resourceInfo(): array
|
||||
{
|
||||
$info = $this->server->ssh()->exec(
|
||||
$this->getScript('resource-info.sh'),
|
||||
);
|
||||
|
||||
return [
|
||||
'load' => str($info)->after('load:')->before(PHP_EOL)->toString(),
|
||||
'memory_total' => str($info)->after('memory_total:')->before(PHP_EOL)->toString(),
|
||||
'memory_used' => str($info)->after('memory_used:')->before(PHP_EOL)->toString(),
|
||||
'memory_free' => str($info)->after('memory_free:')->before(PHP_EOL)->toString(),
|
||||
'disk_total' => str($info)->after('disk_total:')->before(PHP_EOL)->toString(),
|
||||
'disk_used' => str($info)->after('disk_used:')->before(PHP_EOL)->toString(),
|
||||
'disk_free' => str($info)->after('disk_free:')->before(PHP_EOL)->toString(),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
19
app/SSH/OS/scripts/cleanup.sh
Normal file
19
app/SSH/OS/scripts/cleanup.sh
Normal file
@ -0,0 +1,19 @@
|
||||
# Update package lists
|
||||
sudo DEBIAN_FRONTEND=noninteractive apt-get update -y
|
||||
|
||||
# Remove unnecessary dependencies
|
||||
sudo DEBIAN_FRONTEND=noninteractive apt-get autoremove --purge -y
|
||||
|
||||
# Clear package cache
|
||||
sudo DEBIAN_FRONTEND=noninteractive apt-get clean -y
|
||||
|
||||
# Remove old configuration files
|
||||
sudo DEBIAN_FRONTEND=noninteractive apt-get purge -y $(dpkg -l | grep '^rc' | awk '{print $2}')
|
||||
|
||||
# Clear temporary files
|
||||
sudo rm -rf /tmp/*
|
||||
|
||||
# Clear journal logs
|
||||
sudo DEBIAN_FRONTEND=noninteractive journalctl --vacuum-time=1d
|
||||
|
||||
echo "Cleanup completed."
|
@ -1 +1 @@
|
||||
[ -f __path__ ] && cat __path__
|
||||
[ -f __path__ ] && sudo cat __path__
|
||||
|
7
app/SSH/OS/scripts/resource-info.sh
Normal file
7
app/SSH/OS/scripts/resource-info.sh
Normal file
@ -0,0 +1,7 @@
|
||||
echo "load:$(uptime | awk -F'load average:' '{print $2}' | awk -F, '{print $1}' | tr -d ' ')"
|
||||
echo "memory_total:$(free -k | awk 'NR==2{print $2}')"
|
||||
echo "memory_used:$(free -k | awk 'NR==2{print $3}')"
|
||||
echo "memory_free:$(free -k | awk 'NR==2{print $7}')"
|
||||
echo "disk_total:$(df -BM / | awk 'NR==2{print $2}' | sed 's/M//')"
|
||||
echo "disk_used:$(df -BM / | awk 'NR==2{print $3}' | sed 's/M//')"
|
||||
echo "disk_free:$(df -BM / | awk 'NR==2{print $4}' | sed 's/M//')"
|
1
app/SSH/OS/scripts/tail.sh
Normal file
1
app/SSH/OS/scripts/tail.sh
Normal file
@ -0,0 +1 @@
|
||||
sudo tail -n __lines__ __path__
|
42
app/SSH/Services/AbstractService.php
Normal file
42
app/SSH/Services/AbstractService.php
Normal file
@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace App\SSH\Services;
|
||||
|
||||
use App\Models\Service;
|
||||
|
||||
abstract class AbstractService implements ServiceInterface
|
||||
{
|
||||
public function __construct(protected Service $service)
|
||||
{
|
||||
}
|
||||
|
||||
public function creationRules(array $input): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function creationData(array $input): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function deletionRules(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function data(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function install(): void
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
public function uninstall(): void
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\SSH\Services\AddOnServices;
|
||||
|
||||
use App\SSH\Services\ServiceInterface;
|
||||
|
||||
abstract class AbstractAddOnService implements ServiceInterface
|
||||
{
|
||||
abstract public function creationRules(array $input): array;
|
||||
|
||||
abstract public function creationData(array $input): array;
|
||||
|
||||
abstract public function create(): void;
|
||||
|
||||
abstract public function delete(): void;
|
||||
|
||||
abstract public function data(): array;
|
||||
}
|
@ -2,40 +2,78 @@
|
||||
|
||||
namespace App\SSH\Services\Database;
|
||||
|
||||
use App\Enums\BackupStatus;
|
||||
use App\Models\BackupFile;
|
||||
use App\Models\Server;
|
||||
use App\Models\Service;
|
||||
use App\SSH\HasScripts;
|
||||
use App\SSH\Services\ServiceInterface;
|
||||
use App\SSH\Services\AbstractService;
|
||||
use Closure;
|
||||
|
||||
abstract class AbstractDatabase implements Database, ServiceInterface
|
||||
abstract class AbstractDatabase extends AbstractService implements Database
|
||||
{
|
||||
use HasScripts;
|
||||
|
||||
protected Service $service;
|
||||
|
||||
protected Server $server;
|
||||
|
||||
abstract protected function getScriptsDir(): string;
|
||||
|
||||
public function __construct(Service $service)
|
||||
public function creationRules(array $input): array
|
||||
{
|
||||
$this->service = $service;
|
||||
$this->server = $service->server;
|
||||
return [
|
||||
'type' => [
|
||||
'required',
|
||||
function (string $attribute, mixed $value, Closure $fail) {
|
||||
$databaseExists = $this->service->server->database();
|
||||
if ($databaseExists) {
|
||||
$fail('You already have a database service on the server.');
|
||||
}
|
||||
},
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function install(): void
|
||||
{
|
||||
$version = $this->service->version;
|
||||
$command = $this->getScript($this->service->name.'/install-'.$version.'.sh');
|
||||
$this->server->ssh()->exec($command, 'install-'.$this->service->name.'-'.$version);
|
||||
$status = $this->server->systemd()->status($this->service->unit);
|
||||
$this->service->server->ssh()->exec($command, 'install-'.$this->service->name.'-'.$version);
|
||||
$status = $this->service->server->systemd()->status($this->service->unit);
|
||||
$this->service->validateInstall($status);
|
||||
$this->service->server->os()->cleanup();
|
||||
}
|
||||
|
||||
public function deletionRules(): array
|
||||
{
|
||||
return [
|
||||
'service' => [
|
||||
function (string $attribute, mixed $value, Closure $fail) {
|
||||
$hasDatabase = $this->service->server->databases()->exists();
|
||||
if ($hasDatabase) {
|
||||
$fail('You have database(s) on the server.');
|
||||
}
|
||||
$hasDatabaseUser = $this->service->server->databaseUsers()->exists();
|
||||
if ($hasDatabaseUser) {
|
||||
$fail('You have database user(s) on the server.');
|
||||
}
|
||||
$hasRunningBackup = $this->service->server->backups()
|
||||
->where('status', BackupStatus::RUNNING)
|
||||
->exists();
|
||||
if ($hasRunningBackup) {
|
||||
$fail('You have database backup(s) on the server.');
|
||||
}
|
||||
},
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function uninstall(): void
|
||||
{
|
||||
$version = $this->service->version;
|
||||
$command = $this->getScript($this->service->name.'/uninstall.sh');
|
||||
$this->service->server->ssh()->exec($command, 'uninstall-'.$this->service->name.'-'.$version);
|
||||
$this->service->server->os()->cleanup();
|
||||
}
|
||||
|
||||
public function create(string $name): void
|
||||
{
|
||||
$this->server->ssh()->exec(
|
||||
$this->service->server->ssh()->exec(
|
||||
$this->getScript($this->getScriptsDir().'/create.sh', [
|
||||
'name' => $name,
|
||||
]),
|
||||
@ -45,7 +83,7 @@ public function create(string $name): void
|
||||
|
||||
public function delete(string $name): void
|
||||
{
|
||||
$this->server->ssh()->exec(
|
||||
$this->service->server->ssh()->exec(
|
||||
$this->getScript($this->getScriptsDir().'/delete.sh', [
|
||||
'name' => $name,
|
||||
]),
|
||||
@ -55,7 +93,7 @@ public function delete(string $name): void
|
||||
|
||||
public function createUser(string $username, string $password, string $host): void
|
||||
{
|
||||
$this->server->ssh()->exec(
|
||||
$this->service->server->ssh()->exec(
|
||||
$this->getScript($this->getScriptsDir().'/create-user.sh', [
|
||||
'username' => $username,
|
||||
'password' => $password,
|
||||
@ -67,7 +105,7 @@ public function createUser(string $username, string $password, string $host): vo
|
||||
|
||||
public function deleteUser(string $username, string $host): void
|
||||
{
|
||||
$this->server->ssh()->exec(
|
||||
$this->service->server->ssh()->exec(
|
||||
$this->getScript($this->getScriptsDir().'/delete-user.sh', [
|
||||
'username' => $username,
|
||||
'host' => $host,
|
||||
@ -78,7 +116,7 @@ public function deleteUser(string $username, string $host): void
|
||||
|
||||
public function link(string $username, string $host, array $databases): void
|
||||
{
|
||||
$ssh = $this->server->ssh();
|
||||
$ssh = $this->service->server->ssh();
|
||||
|
||||
foreach ($databases as $database) {
|
||||
$ssh->exec(
|
||||
@ -94,7 +132,7 @@ public function link(string $username, string $host, array $databases): void
|
||||
|
||||
public function unlink(string $username, string $host): void
|
||||
{
|
||||
$this->server->ssh()->exec(
|
||||
$this->service->server->ssh()->exec(
|
||||
$this->getScript($this->getScriptsDir().'/unlink.sh', [
|
||||
'username' => $username,
|
||||
'host' => $host,
|
||||
@ -106,7 +144,7 @@ public function unlink(string $username, string $host): void
|
||||
public function runBackup(BackupFile $backupFile): void
|
||||
{
|
||||
// backup
|
||||
$this->server->ssh()->exec(
|
||||
$this->service->server->ssh()->exec(
|
||||
$this->getScript($this->getScriptsDir().'/backup.sh', [
|
||||
'file' => $backupFile->name,
|
||||
'database' => $backupFile->backup->database->name,
|
||||
@ -115,13 +153,13 @@ public function runBackup(BackupFile $backupFile): void
|
||||
);
|
||||
|
||||
// upload to storage
|
||||
$upload = $backupFile->backup->storage->provider()->ssh($this->server)->upload(
|
||||
$upload = $backupFile->backup->storage->provider()->ssh($this->service->server)->upload(
|
||||
$backupFile->path(),
|
||||
$backupFile->storagePath(),
|
||||
);
|
||||
|
||||
// cleanup
|
||||
$this->server->ssh()->exec('rm '.$backupFile->name.'.zip');
|
||||
$this->service->server->ssh()->exec('rm '.$backupFile->name.'.zip');
|
||||
|
||||
$backupFile->size = $upload['size'];
|
||||
$backupFile->save();
|
||||
@ -130,12 +168,12 @@ public function runBackup(BackupFile $backupFile): void
|
||||
public function restoreBackup(BackupFile $backupFile, string $database): void
|
||||
{
|
||||
// download
|
||||
$backupFile->backup->storage->provider()->ssh($this->server)->download(
|
||||
$backupFile->backup->storage->provider()->ssh($this->service->server)->download(
|
||||
$backupFile->storagePath(),
|
||||
$backupFile->name.'.zip',
|
||||
);
|
||||
|
||||
$this->server->ssh()->exec(
|
||||
$this->service->server->ssh()->exec(
|
||||
$this->getScript($this->getScriptsDir().'/restore.sh', [
|
||||
'database' => $database,
|
||||
'file' => $backupFile->name,
|
||||
|
@ -9,4 +9,6 @@ sudo DEBIAN_FRONTEND=noninteractive apt-get update
|
||||
|
||||
sudo DEBIAN_FRONTEND=noninteractive apt-get install mariadb-server mariadb-backup -y
|
||||
|
||||
sudo systemctl unmask mysql.service
|
||||
|
||||
sudo service mysql start
|
||||
|
@ -9,4 +9,6 @@ sudo DEBIAN_FRONTEND=noninteractive apt-get update
|
||||
|
||||
sudo DEBIAN_FRONTEND=noninteractive apt-get install mariadb-server mariadb-backup -y
|
||||
|
||||
sudo systemctl unmask mysql.service
|
||||
|
||||
sudo service mysql start
|
||||
|
9
app/SSH/Services/Database/scripts/mariadb/uninstall.sh
Normal file
9
app/SSH/Services/Database/scripts/mariadb/uninstall.sh
Normal file
@ -0,0 +1,9 @@
|
||||
sudo service mysql stop
|
||||
|
||||
sudo DEBIAN_FRONTEND=noninteractive apt-get remove mariadb-server mariadb-backup -y
|
||||
|
||||
sudo rm -rf /etc/mysql
|
||||
sudo rm -rf /var/lib/mysql
|
||||
sudo rm -rf /var/log/mysql
|
||||
sudo rm -rf /var/run/mysqld
|
||||
sudo rm -rf /var/run/mysqld/mysqld.sock
|
@ -1,5 +1,7 @@
|
||||
sudo DEBIAN_FRONTEND=noninteractive apt-get install mysql-server -y
|
||||
|
||||
sudo systemctl unmask mysql.service
|
||||
|
||||
sudo service mysql enable
|
||||
|
||||
sudo service mysql start
|
||||
|
@ -6,6 +6,8 @@ sudo DEBIAN_FRONTEND=noninteractive apt-get update
|
||||
|
||||
sudo DEBIAN_FRONTEND=noninteractive apt-get install mysql-server -y
|
||||
|
||||
sudo systemctl unmask mysql.service
|
||||
|
||||
sudo service mysql enable
|
||||
|
||||
sudo service mysql start
|
||||
|
9
app/SSH/Services/Database/scripts/mysql/uninstall.sh
Executable file
9
app/SSH/Services/Database/scripts/mysql/uninstall.sh
Executable file
@ -0,0 +1,9 @@
|
||||
sudo service mysql stop
|
||||
|
||||
sudo DEBIAN_FRONTEND=noninteractive apt-get remove mysql-server -y
|
||||
|
||||
sudo rm -rf /etc/mysql
|
||||
sudo rm -rf /var/lib/mysql
|
||||
sudo rm -rf /var/log/mysql
|
||||
sudo rm -rf /var/run/mysqld
|
||||
sudo rm -rf /var/run/mysqld/mysqld.sock
|
11
app/SSH/Services/Database/scripts/postgresql/uninstall.sh
Normal file
11
app/SSH/Services/Database/scripts/postgresql/uninstall.sh
Normal file
@ -0,0 +1,11 @@
|
||||
sudo service postgresql stop
|
||||
|
||||
sudo DEBIAN_FRONTEND=noninteractive apt-get remove postgresql-* -y
|
||||
|
||||
sudo rm -rf /etc/postgresql
|
||||
sudo rm -rf /var/lib/postgresql
|
||||
sudo rm -rf /var/log/postgresql
|
||||
sudo rm -rf /var/run/postgresql
|
||||
sudo rm -rf /var/run/postgresql/postmaster.pid
|
||||
sudo rm -rf /var/run/postgresql/.s.PGSQL.5432
|
||||
sudo rm -rf /var/run/postgresql/.s.PGSQL.5432.lock
|
@ -2,15 +2,8 @@
|
||||
|
||||
namespace App\SSH\Services\Firewall;
|
||||
|
||||
use App\Models\Service;
|
||||
use App\SSH\Services\ServiceInterface;
|
||||
use App\SSH\Services\AbstractService;
|
||||
|
||||
abstract class AbstractFirewall implements Firewall, ServiceInterface
|
||||
abstract class AbstractFirewall extends AbstractService implements Firewall
|
||||
{
|
||||
protected Service $service;
|
||||
|
||||
public function __construct(Service $service)
|
||||
{
|
||||
$this->service = $service;
|
||||
}
|
||||
}
|
||||
|
@ -14,6 +14,12 @@ public function install(): void
|
||||
$this->getScript('ufw/install-ufw.sh'),
|
||||
'install-ufw'
|
||||
);
|
||||
$this->service->server->os()->cleanup();
|
||||
}
|
||||
|
||||
public function uninstall(): void
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
public function addRule(string $type, string $protocol, int $port, string $source, ?string $mask): void
|
||||
|
53
app/SSH/Services/Monitoring/RemoteMonitor/RemoteMonitor.php
Normal file
53
app/SSH/Services/Monitoring/RemoteMonitor/RemoteMonitor.php
Normal file
@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
namespace App\SSH\Services\Monitoring\RemoteMonitor;
|
||||
|
||||
use App\Models\Metric;
|
||||
use App\SSH\Services\AbstractService;
|
||||
use Closure;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
class RemoteMonitor extends AbstractService
|
||||
{
|
||||
public function creationRules(array $input): array
|
||||
{
|
||||
return [
|
||||
'type' => [
|
||||
function (string $attribute, mixed $value, Closure $fail) {
|
||||
$monitoringExists = $this->service->server->monitoring();
|
||||
if ($monitoringExists) {
|
||||
$fail('You already have a monitoring service on the server.');
|
||||
}
|
||||
},
|
||||
],
|
||||
'version' => [
|
||||
'required',
|
||||
Rule::in(['latest']),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function creationData(array $input): array
|
||||
{
|
||||
return [
|
||||
'data_retention' => 10,
|
||||
];
|
||||
}
|
||||
|
||||
public function data(): array
|
||||
{
|
||||
return [
|
||||
'data_retention' => $this->service->type_data['data_retention'] ?? 10,
|
||||
];
|
||||
}
|
||||
|
||||
public function install(): void
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
public function uninstall(): void
|
||||
{
|
||||
Metric::where('server_id', $this->service->server_id)->delete();
|
||||
}
|
||||
}
|
87
app/SSH/Services/Monitoring/VitoAgent/VitoAgent.php
Normal file
87
app/SSH/Services/Monitoring/VitoAgent/VitoAgent.php
Normal file
@ -0,0 +1,87 @@
|
||||
<?php
|
||||
|
||||
namespace App\SSH\Services\Monitoring\VitoAgent;
|
||||
|
||||
use App\Models\Metric;
|
||||
use App\SSH\HasScripts;
|
||||
use App\SSH\Services\AbstractService;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Illuminate\Validation\Rule;
|
||||
use Ramsey\Uuid\Uuid;
|
||||
|
||||
class VitoAgent extends AbstractService
|
||||
{
|
||||
use HasScripts;
|
||||
|
||||
const TAGS_URL = 'https://api.github.com/repos/vitodeploy/agent/tags';
|
||||
|
||||
const DOWNLOAD_URL = 'https://github.com/vitodeploy/agent/releases/download/%s';
|
||||
|
||||
public function creationRules(array $input): array
|
||||
{
|
||||
return [
|
||||
'type' => [
|
||||
Rule::unique('services', 'type')->where('server_id', $this->service->server_id),
|
||||
],
|
||||
'version' => [
|
||||
'required',
|
||||
Rule::in(['latest']),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function creationData(array $input): array
|
||||
{
|
||||
return [
|
||||
'url' => '',
|
||||
'secret' => Uuid::uuid4()->toString(),
|
||||
'data_retention' => 10,
|
||||
];
|
||||
}
|
||||
|
||||
public function data(): array
|
||||
{
|
||||
return [
|
||||
'url' => $this->service->type_data['url'] ?? null,
|
||||
'secret' => $this->service->type_data['secret'] ?? null,
|
||||
'data_retention' => $this->service->type_data['data_retention'] ?? 10,
|
||||
];
|
||||
}
|
||||
|
||||
public function install(): void
|
||||
{
|
||||
$tags = Http::get(self::TAGS_URL)->json();
|
||||
if (empty($tags)) {
|
||||
throw new \Exception('Failed to fetch tags');
|
||||
}
|
||||
$this->service->version = $tags[0]['name'];
|
||||
$this->service->save();
|
||||
$downloadUrl = sprintf(self::DOWNLOAD_URL, $this->service->version);
|
||||
|
||||
$data = $this->data();
|
||||
$data['url'] = route('api.servers.agent', [$this->service->server, $this->service->id]);
|
||||
$this->service->type_data = $data;
|
||||
$this->service->save();
|
||||
$this->service->refresh();
|
||||
|
||||
$this->service->server->ssh()->exec(
|
||||
$this->getScript('install.sh', [
|
||||
'download_url' => $downloadUrl,
|
||||
'config_url' => $this->data()['url'],
|
||||
'config_secret' => $this->data()['secret'],
|
||||
]),
|
||||
'install-vito-agent'
|
||||
);
|
||||
$status = $this->service->server->systemd()->status($this->service->unit);
|
||||
$this->service->validateInstall($status);
|
||||
}
|
||||
|
||||
public function uninstall(): void
|
||||
{
|
||||
$this->service->server->ssh()->exec(
|
||||
$this->getScript('uninstall.sh'),
|
||||
'uninstall-vito-agent'
|
||||
);
|
||||
Metric::where('server_id', $this->service->server_id)->delete();
|
||||
}
|
||||
}
|
53
app/SSH/Services/Monitoring/VitoAgent/scripts/install.sh
Normal file
53
app/SSH/Services/Monitoring/VitoAgent/scripts/install.sh
Normal file
@ -0,0 +1,53 @@
|
||||
arch=$(uname -m)
|
||||
|
||||
if [ "$arch" == "x86_64" ]; then
|
||||
executable="vitoagent-linux-amd64"
|
||||
elif [ "$arch" == "i686" ]; then
|
||||
executable="vitoagent-linux-amd"
|
||||
elif [ "$arch" == "armv7l" ]; then
|
||||
executable="vitoagent-linux-arm"
|
||||
elif [ "$arch" == "aarch64" ]; then
|
||||
executable="vitoagent-linux-arm64"
|
||||
else
|
||||
executable="vitoagent-linux-amd64"
|
||||
fi
|
||||
|
||||
wget __download_url__/$executable
|
||||
|
||||
chmod +x ./$executable
|
||||
|
||||
sudo mv ./$executable /usr/local/bin/vito-agent
|
||||
|
||||
# create service
|
||||
export VITO_AGENT_SERVICE="
|
||||
[Unit]
|
||||
Description=Vito Agent
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=root
|
||||
ExecStart=/usr/local/bin/vito-agent
|
||||
Restart=on-failure
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
"
|
||||
echo "${VITO_AGENT_SERVICE}" | sudo tee /etc/systemd/system/vito-agent.service
|
||||
|
||||
sudo mkdir -p /etc/vito-agent
|
||||
|
||||
export VITO_AGENT_CONFIG="
|
||||
{
|
||||
\"url\": \"__config_url__\",
|
||||
\"secret\": \"__config_secret__\"
|
||||
}
|
||||
"
|
||||
|
||||
echo "${VITO_AGENT_CONFIG}" | sudo tee /etc/vito-agent/config.json
|
||||
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl enable vito-agent
|
||||
sudo systemctl start vito-agent
|
||||
|
||||
echo "Vito Agent installed successfully"
|
13
app/SSH/Services/Monitoring/VitoAgent/scripts/uninstall.sh
Normal file
13
app/SSH/Services/Monitoring/VitoAgent/scripts/uninstall.sh
Normal file
@ -0,0 +1,13 @@
|
||||
sudo service vito-agent stop
|
||||
|
||||
sudo systemctl disable vito-agent
|
||||
|
||||
sudo rm -f /usr/local/bin/vito-agent
|
||||
|
||||
sudo rm -f /etc/systemd/system/vito-agent.service
|
||||
|
||||
sudo rm -rf /etc/vito-agent
|
||||
|
||||
sudo systemctl daemon-reload
|
||||
|
||||
echo "Vito Agent uninstalled successfully"
|
@ -3,20 +3,43 @@
|
||||
namespace App\SSH\Services\PHP;
|
||||
|
||||
use App\Exceptions\SSHCommandError;
|
||||
use App\Models\Service;
|
||||
use App\SSH\HasScripts;
|
||||
use App\SSH\Services\ServiceInterface;
|
||||
use App\SSH\Services\AbstractService;
|
||||
use Closure;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
class PHP implements ServiceInterface
|
||||
class PHP extends AbstractService
|
||||
{
|
||||
use HasScripts;
|
||||
|
||||
protected Service $service;
|
||||
|
||||
public function __construct(Service $service)
|
||||
public function creationRules(array $input): array
|
||||
{
|
||||
$this->service = $service;
|
||||
return [
|
||||
'version' => [
|
||||
'required',
|
||||
Rule::in(config('core.php_versions')),
|
||||
Rule::unique('services', 'version')
|
||||
->where('type', 'php')
|
||||
->where('server_id', $this->service->server_id),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function deletionRules(): array
|
||||
{
|
||||
return [
|
||||
'service' => [
|
||||
function (string $attribute, mixed $value, Closure $fail) {
|
||||
$hasSite = $this->service->server->sites()
|
||||
->where('php_version', $this->service->version)
|
||||
->exists();
|
||||
if ($hasSite) {
|
||||
$fail('Some sites are using this PHP version.');
|
||||
}
|
||||
},
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function install(): void
|
||||
@ -29,6 +52,7 @@ public function install(): void
|
||||
]),
|
||||
'install-php-'.$this->service->version
|
||||
);
|
||||
$this->service->server->os()->cleanup();
|
||||
}
|
||||
|
||||
public function uninstall(): void
|
||||
@ -39,6 +63,7 @@ public function uninstall(): void
|
||||
]),
|
||||
'uninstall-php-'.$this->service->version
|
||||
);
|
||||
$this->service->server->os()->cleanup();
|
||||
}
|
||||
|
||||
public function setDefaultCli(): void
|
||||
|
@ -2,15 +2,37 @@
|
||||
|
||||
namespace App\SSH\Services\ProcessManager;
|
||||
|
||||
use App\Models\Service;
|
||||
use App\SSH\Services\ServiceInterface;
|
||||
use App\SSH\Services\AbstractService;
|
||||
use Closure;
|
||||
|
||||
abstract class AbstractProcessManager implements ProcessManager, ServiceInterface
|
||||
abstract class AbstractProcessManager extends AbstractService implements ProcessManager
|
||||
{
|
||||
protected Service $service;
|
||||
|
||||
public function __construct(Service $service)
|
||||
public function creationRules(array $input): array
|
||||
{
|
||||
$this->service = $service;
|
||||
return [
|
||||
'type' => [
|
||||
'required',
|
||||
function (string $attribute, mixed $value, Closure $fail) {
|
||||
$processManagerExists = $this->service->server->processManager();
|
||||
if ($processManagerExists) {
|
||||
$fail('You already have a process manager service on the server.');
|
||||
}
|
||||
},
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function deletionRules(): array
|
||||
{
|
||||
return [
|
||||
'service' => [
|
||||
function (string $attribute, mixed $value, Closure $fail) {
|
||||
$hasQueue = $this->service->server->queues()->exists();
|
||||
if ($hasQueue) {
|
||||
$fail('You have queue(s) on the server.');
|
||||
}
|
||||
},
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -15,6 +15,18 @@ public function install(): void
|
||||
$this->getScript('supervisor/install-supervisor.sh'),
|
||||
'install-supervisor'
|
||||
);
|
||||
$this->service->server->os()->cleanup();
|
||||
}
|
||||
|
||||
public function uninstall(): void
|
||||
{
|
||||
$this->service->server->ssh()->exec(
|
||||
$this->getScript('supervisor/uninstall-supervisor.sh'),
|
||||
'uninstall-supervisor'
|
||||
);
|
||||
$status = $this->service->server->systemd()->status($this->service->unit);
|
||||
$this->service->validateInstall($status);
|
||||
$this->service->server->os()->cleanup();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -0,0 +1,8 @@
|
||||
sudo service supervisor stop
|
||||
|
||||
sudo DEBIAN_FRONTEND=noninteractive apt-get remove supervisor -y
|
||||
|
||||
sudo rm -rf /etc/supervisor
|
||||
sudo rm -rf /var/log/supervisor
|
||||
sudo rm -rf /var/run/supervisor
|
||||
sudo rm -rf /var/run/supervisor/supervisor.sock
|
@ -2,16 +2,27 @@
|
||||
|
||||
namespace App\SSH\Services\Redis;
|
||||
|
||||
use App\Models\Service;
|
||||
use App\SSH\HasScripts;
|
||||
use App\SSH\Services\ServiceInterface;
|
||||
use App\SSH\Services\AbstractService;
|
||||
use Closure;
|
||||
|
||||
class Redis implements ServiceInterface
|
||||
class Redis extends AbstractService
|
||||
{
|
||||
use HasScripts;
|
||||
|
||||
public function __construct(protected Service $service)
|
||||
public function creationRules(array $input): array
|
||||
{
|
||||
return [
|
||||
'type' => [
|
||||
'required',
|
||||
function (string $attribute, mixed $value, Closure $fail) {
|
||||
$redisExists = $this->service->server->memoryDatabase();
|
||||
if ($redisExists) {
|
||||
$fail('You already have a Redis service on the server.');
|
||||
}
|
||||
},
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function install(): void
|
||||
@ -20,5 +31,17 @@ public function install(): void
|
||||
$this->getScript('install.sh'),
|
||||
'install-redis'
|
||||
);
|
||||
$status = $this->service->server->systemd()->status($this->service->unit);
|
||||
$this->service->validateInstall($status);
|
||||
$this->service->server->os()->cleanup();
|
||||
}
|
||||
|
||||
public function uninstall(): void
|
||||
{
|
||||
$this->service->server->ssh()->exec(
|
||||
$this->getScript('uninstall.sh'),
|
||||
'uninstall-redis'
|
||||
);
|
||||
$this->service->server->os()->cleanup();
|
||||
}
|
||||
}
|
||||
|
15
app/SSH/Services/Redis/scripts/uninstall.sh
Executable file
15
app/SSH/Services/Redis/scripts/uninstall.sh
Executable file
@ -0,0 +1,15 @@
|
||||
sudo service redis stop
|
||||
|
||||
sudo DEBIAN_FRONTEND=noninteractive apt-get remove redis-server -y
|
||||
|
||||
sudo rm -rf /etc/redis
|
||||
sudo rm -rf /var/lib/redis
|
||||
sudo rm -rf /var/log/redis
|
||||
sudo rm -rf /var/run/redis
|
||||
sudo rm -rf /var/run/redis/redis-server.pid
|
||||
sudo rm -rf /var/run/redis/redis-server.sock
|
||||
sudo rm -rf /var/run/redis/redis-server.sock
|
||||
|
||||
sudo DEBIAN_FRONTEND=noninteractive sudo apt-get autoremove -y
|
||||
|
||||
sudo DEBIAN_FRONTEND=noninteractive sudo apt-get autoclean -y
|
@ -4,5 +4,15 @@
|
||||
|
||||
interface ServiceInterface
|
||||
{
|
||||
public function creationRules(array $input): array;
|
||||
|
||||
public function creationData(array $input): array;
|
||||
|
||||
public function deletionRules(): array;
|
||||
|
||||
public function data(): array;
|
||||
|
||||
public function install(): void;
|
||||
|
||||
public function uninstall(): void;
|
||||
}
|
||||
|
@ -2,12 +2,8 @@
|
||||
|
||||
namespace App\SSH\Services\Webserver;
|
||||
|
||||
use App\Models\Service;
|
||||
use App\SSH\Services\ServiceInterface;
|
||||
use App\SSH\Services\AbstractService;
|
||||
|
||||
abstract class AbstractWebserver implements ServiceInterface, Webserver
|
||||
abstract class AbstractWebserver extends AbstractService implements Webserver
|
||||
{
|
||||
public function __construct(protected Service $service)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,7 @@
|
||||
use App\Models\Site;
|
||||
use App\Models\Ssl;
|
||||
use App\SSH\HasScripts;
|
||||
use Closure;
|
||||
use Illuminate\Support\Str;
|
||||
use Throwable;
|
||||
|
||||
@ -23,6 +24,31 @@ public function install(): void
|
||||
]),
|
||||
'install-nginx'
|
||||
);
|
||||
$this->service->server->os()->cleanup();
|
||||
}
|
||||
|
||||
public function deletionRules(): array
|
||||
{
|
||||
return [
|
||||
'service' => [
|
||||
function (string $attribute, mixed $value, Closure $fail) {
|
||||
$hasSite = $this->service->server->sites()
|
||||
->exists();
|
||||
if ($hasSite) {
|
||||
$fail('Cannot uninstall webserver while you have websites using it.');
|
||||
}
|
||||
},
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function uninstall(): void
|
||||
{
|
||||
$this->service->server->ssh()->exec(
|
||||
$this->getScript('nginx/uninstall-nginx.sh'),
|
||||
'uninstall-nginx'
|
||||
);
|
||||
$this->service->server->os()->cleanup();
|
||||
}
|
||||
|
||||
public function createVHost(Site $site): void
|
||||
@ -44,7 +70,7 @@ public function updateVHost(Site $site, bool $noSSL = false, ?string $vhost = nu
|
||||
$this->getScript('nginx/update-vhost.sh', [
|
||||
'domain' => $site->domain,
|
||||
'path' => $site->path,
|
||||
'vhost' => $this->generateVhost($site, $noSSL),
|
||||
'vhost' => $vhost ?? $this->generateVhost($site, $noSSL),
|
||||
]),
|
||||
'update-vhost',
|
||||
$site->id
|
||||
|
12
app/SSH/Services/Webserver/scripts/nginx/uninstall-nginx.sh
Executable file
12
app/SSH/Services/Webserver/scripts/nginx/uninstall-nginx.sh
Executable file
@ -0,0 +1,12 @@
|
||||
sudo service nginx stop
|
||||
|
||||
sudo DEBIAN_FRONTEND=noninteractive apt-get purge nginx nginx-common nginx-full -y
|
||||
|
||||
sudo rm -rf /etc/nginx
|
||||
sudo rm -rf /var/log/nginx
|
||||
sudo rm -rf /var/lib/nginx
|
||||
sudo rm -rf /var/cache/nginx
|
||||
sudo rm -rf /usr/share/nginx
|
||||
sudo rm -rf /etc/systemd/system/nginx.service
|
||||
|
||||
sudo systemctl daemon-reload
|
@ -4,6 +4,7 @@
|
||||
|
||||
use App\Enums\ServiceStatus;
|
||||
use App\Models\Server;
|
||||
use App\SSH\Services\PHP\PHP;
|
||||
|
||||
abstract class AbstractType implements ServerType
|
||||
{
|
||||
@ -31,7 +32,9 @@ public function install(): void
|
||||
$service->update(['status' => ServiceStatus::READY]);
|
||||
if ($service->type == 'php') {
|
||||
$this->progress($currentProgress, 'installing-composer');
|
||||
$service->handler()->installComposer();
|
||||
/** @var PHP $handler */
|
||||
$handler = $service->handler();
|
||||
$handler->installComposer();
|
||||
}
|
||||
}
|
||||
$this->progress(100, 'finishing');
|
||||
|
@ -13,7 +13,7 @@ public function createRules(array $input): array
|
||||
],
|
||||
'php' => [
|
||||
'required',
|
||||
'in:'.implode(',', config('core.php_versions')),
|
||||
'in:none,'.implode(',', config('core.php_versions')),
|
||||
],
|
||||
'database' => [
|
||||
'required',
|
||||
|
@ -29,3 +29,22 @@ function htmx(): HtmxResponse
|
||||
{
|
||||
return new HtmxResponse();
|
||||
}
|
||||
|
||||
function vito_version(): string
|
||||
{
|
||||
$version = exec('git describe --tags');
|
||||
if (str($version)->contains('-')) {
|
||||
return str($version)->before('-').' (dev)';
|
||||
}
|
||||
|
||||
return $version;
|
||||
}
|
||||
|
||||
function convert_time_format($string): string
|
||||
{
|
||||
$string = preg_replace('/(\d+)m/', '$1 minutes', $string);
|
||||
$string = preg_replace('/(\d+)s/', '$1 seconds', $string);
|
||||
$string = preg_replace('/(\d+)d/', '$1 days', $string);
|
||||
|
||||
return preg_replace('/(\d+)h/', '$1 hours', $string);
|
||||
}
|
||||
|
225
config/core.php
225
config/core.php
@ -1,35 +1,5 @@
|
||||
<?php
|
||||
|
||||
use App\Enums\OperatingSystem;
|
||||
use App\Enums\StorageProvider;
|
||||
use App\NotificationChannels\Discord;
|
||||
use App\NotificationChannels\Email;
|
||||
use App\NotificationChannels\Slack;
|
||||
use App\NotificationChannels\Telegram;
|
||||
use App\ServerProviders\AWS;
|
||||
use App\ServerProviders\DigitalOcean;
|
||||
use App\ServerProviders\Hetzner;
|
||||
use App\ServerProviders\Linode;
|
||||
use App\ServerProviders\Vultr;
|
||||
use App\SiteTypes\Laravel;
|
||||
use App\SiteTypes\PHPBlank;
|
||||
use App\SiteTypes\PHPMyAdmin;
|
||||
use App\SiteTypes\PHPSite;
|
||||
use App\SiteTypes\Wordpress;
|
||||
use App\SourceControlProviders\Bitbucket;
|
||||
use App\SourceControlProviders\Github;
|
||||
use App\SourceControlProviders\Gitlab;
|
||||
use App\SSH\Services\Database\Mariadb;
|
||||
use App\SSH\Services\Database\Mysql;
|
||||
use App\SSH\Services\Database\Postgresql;
|
||||
use App\SSH\Services\Firewall\Ufw;
|
||||
use App\SSH\Services\PHP\PHP;
|
||||
use App\SSH\Services\ProcessManager\Supervisor;
|
||||
use App\SSH\Services\Redis\Redis;
|
||||
use App\SSH\Services\Webserver\Nginx;
|
||||
use App\StorageProviders\Dropbox;
|
||||
use App\StorageProviders\FTP;
|
||||
|
||||
return [
|
||||
/*
|
||||
* SSH
|
||||
@ -44,12 +14,12 @@
|
||||
* General
|
||||
*/
|
||||
'operating_systems' => [
|
||||
OperatingSystem::UBUNTU20,
|
||||
OperatingSystem::UBUNTU22,
|
||||
\App\Enums\OperatingSystem::UBUNTU20,
|
||||
\App\Enums\OperatingSystem::UBUNTU22,
|
||||
],
|
||||
'webservers' => ['none', 'nginx'],
|
||||
'php_versions' => [
|
||||
'none',
|
||||
// 'none',
|
||||
// '5.6',
|
||||
'7.0',
|
||||
'7.1',
|
||||
@ -126,117 +96,104 @@
|
||||
],
|
||||
'server_providers_class' => [
|
||||
\App\Enums\ServerProvider::CUSTOM => \App\ServerProviders\Custom::class,
|
||||
\App\Enums\ServerProvider::AWS => AWS::class,
|
||||
\App\Enums\ServerProvider::LINODE => Linode::class,
|
||||
\App\Enums\ServerProvider::DIGITALOCEAN => DigitalOcean::class,
|
||||
\App\Enums\ServerProvider::VULTR => Vultr::class,
|
||||
\App\Enums\ServerProvider::HETZNER => Hetzner::class,
|
||||
\App\Enums\ServerProvider::AWS => \App\ServerProviders\AWS::class,
|
||||
\App\Enums\ServerProvider::LINODE => \App\ServerProviders\Linode::class,
|
||||
\App\Enums\ServerProvider::DIGITALOCEAN => \App\ServerProviders\DigitalOcean::class,
|
||||
\App\Enums\ServerProvider::VULTR => \App\ServerProviders\Vultr::class,
|
||||
\App\Enums\ServerProvider::HETZNER => \App\ServerProviders\Hetzner::class,
|
||||
],
|
||||
'server_providers_default_user' => [
|
||||
'custom' => [
|
||||
'ubuntu_18' => 'root',
|
||||
'ubuntu_20' => 'root',
|
||||
'ubuntu_22' => 'root',
|
||||
\App\Enums\OperatingSystem::UBUNTU20 => 'root',
|
||||
\App\Enums\OperatingSystem::UBUNTU22 => 'root',
|
||||
],
|
||||
'aws' => [
|
||||
'ubuntu_18' => 'ubuntu',
|
||||
'ubuntu_20' => 'ubuntu',
|
||||
'ubuntu_22' => 'ubuntu',
|
||||
\App\Enums\OperatingSystem::UBUNTU20 => 'ubuntu',
|
||||
\App\Enums\OperatingSystem::UBUNTU22 => 'ubuntu',
|
||||
],
|
||||
'linode' => [
|
||||
'ubuntu_18' => 'root',
|
||||
'ubuntu_20' => 'root',
|
||||
'ubuntu_22' => 'root',
|
||||
\App\Enums\OperatingSystem::UBUNTU20 => 'root',
|
||||
\App\Enums\OperatingSystem::UBUNTU22 => 'root',
|
||||
],
|
||||
'digitalocean' => [
|
||||
'ubuntu_18' => 'root',
|
||||
'ubuntu_20' => 'root',
|
||||
'ubuntu_22' => 'root',
|
||||
\App\Enums\OperatingSystem::UBUNTU20 => 'root',
|
||||
\App\Enums\OperatingSystem::UBUNTU22 => 'root',
|
||||
],
|
||||
'vultr' => [
|
||||
'ubuntu_18' => 'root',
|
||||
'ubuntu_20' => 'root',
|
||||
'ubuntu_22' => 'root',
|
||||
\App\Enums\OperatingSystem::UBUNTU20 => 'root',
|
||||
\App\Enums\OperatingSystem::UBUNTU22 => 'root',
|
||||
],
|
||||
'hetzner' => [
|
||||
'ubuntu_18' => 'root',
|
||||
'ubuntu_20' => 'root',
|
||||
'ubuntu_22' => 'root',
|
||||
\App\Enums\OperatingSystem::UBUNTU20 => 'root',
|
||||
\App\Enums\OperatingSystem::UBUNTU22 => 'root',
|
||||
],
|
||||
],
|
||||
|
||||
/*
|
||||
* Service
|
||||
*/
|
||||
'service_handlers' => [
|
||||
'nginx' => Nginx::class,
|
||||
'mysql' => Mysql::class,
|
||||
'mariadb' => Mariadb::class,
|
||||
'postgresql' => Postgresql::class,
|
||||
'redis' => Redis::class,
|
||||
'php' => PHP::class,
|
||||
'ufw' => Ufw::class,
|
||||
'supervisor' => Supervisor::class,
|
||||
'service_types' => [
|
||||
'nginx' => 'webserver',
|
||||
'mysql' => 'database',
|
||||
'mariadb' => 'database',
|
||||
'postgresql' => 'database',
|
||||
'redis' => 'memory_database',
|
||||
'php' => 'php',
|
||||
'ufw' => 'firewall',
|
||||
'supervisor' => 'process_manager',
|
||||
'vito-agent' => 'monitoring',
|
||||
'remote-monitor' => 'monitoring',
|
||||
],
|
||||
'add_on_services' => [
|
||||
// add-on services
|
||||
'service_handlers' => [
|
||||
'nginx' => \App\SSH\Services\Webserver\Nginx::class,
|
||||
'mysql' => \App\SSH\Services\Database\Mysql::class,
|
||||
'mariadb' => \App\SSH\Services\Database\Mariadb::class,
|
||||
'postgresql' => \App\SSH\Services\Database\Postgresql::class,
|
||||
'redis' => \App\SSH\Services\Redis\Redis::class,
|
||||
'php' => \App\SSH\Services\PHP\PHP::class,
|
||||
'ufw' => \App\SSH\Services\Firewall\Ufw::class,
|
||||
'supervisor' => \App\SSH\Services\ProcessManager\Supervisor::class,
|
||||
'vito-agent' => \App\SSH\Services\Monitoring\VitoAgent\VitoAgent::class,
|
||||
'remote-monitor' => \App\SSH\Services\Monitoring\RemoteMonitor\RemoteMonitor::class,
|
||||
],
|
||||
'service_units' => [
|
||||
'nginx' => [
|
||||
'ubuntu_18' => [
|
||||
\App\Enums\OperatingSystem::UBUNTU20 => [
|
||||
'latest' => 'nginx',
|
||||
],
|
||||
'ubuntu_20' => [
|
||||
'latest' => 'nginx',
|
||||
],
|
||||
'ubuntu_22' => [
|
||||
\App\Enums\OperatingSystem::UBUNTU22 => [
|
||||
'latest' => 'nginx',
|
||||
],
|
||||
],
|
||||
'mysql' => [
|
||||
'ubuntu_18' => [
|
||||
\App\Enums\OperatingSystem::UBUNTU20 => [
|
||||
'5.7' => 'mysql',
|
||||
'8.0' => 'mysql',
|
||||
],
|
||||
'ubuntu_20' => [
|
||||
'5.7' => 'mysql',
|
||||
'8.0' => 'mysql',
|
||||
],
|
||||
'ubuntu_22' => [
|
||||
\App\Enums\OperatingSystem::UBUNTU22 => [
|
||||
'5.7' => 'mysql',
|
||||
'8.0' => 'mysql',
|
||||
],
|
||||
],
|
||||
'mariadb' => [
|
||||
'ubuntu_18' => [
|
||||
\App\Enums\OperatingSystem::UBUNTU20 => [
|
||||
'10.3' => 'mariadb',
|
||||
'10.4' => 'mariadb',
|
||||
],
|
||||
'ubuntu_20' => [
|
||||
'10.3' => 'mariadb',
|
||||
'10.4' => 'mariadb',
|
||||
],
|
||||
'ubuntu_22' => [
|
||||
\App\Enums\OperatingSystem::UBUNTU22 => [
|
||||
'10.3' => 'mariadb',
|
||||
'10.4' => 'mariadb',
|
||||
],
|
||||
],
|
||||
'postgresql' => [
|
||||
'ubuntu_18' => [
|
||||
\App\Enums\OperatingSystem::UBUNTU20 => [
|
||||
'12' => 'postgresql',
|
||||
'13' => 'postgresql',
|
||||
'14' => 'postgresql',
|
||||
'15' => 'postgresql',
|
||||
'16' => 'postgresql',
|
||||
],
|
||||
'ubuntu_20' => [
|
||||
'12' => 'postgresql',
|
||||
'13' => 'postgresql',
|
||||
'14' => 'postgresql',
|
||||
'15' => 'postgresql',
|
||||
'16' => 'postgresql',
|
||||
],
|
||||
'ubuntu_22' => [
|
||||
\App\Enums\OperatingSystem::UBUNTU22 => [
|
||||
'12' => 'postgresql',
|
||||
'13' => 'postgresql',
|
||||
'14' => 'postgresql',
|
||||
@ -245,19 +202,7 @@
|
||||
],
|
||||
],
|
||||
'php' => [
|
||||
'ubuntu_18' => [
|
||||
'5.6' => 'php5.6-fpm',
|
||||
'7.0' => 'php7.0-fpm',
|
||||
'7.1' => 'php7.1-fpm',
|
||||
'7.2' => 'php7.2-fpm',
|
||||
'7.3' => 'php7.3-fpm',
|
||||
'7.4' => 'php7.4-fpm',
|
||||
'8.0' => 'php8.0-fpm',
|
||||
'8.1' => 'php8.1-fpm',
|
||||
'8.2' => 'php8.2-fpm',
|
||||
'8.3' => 'php8.3-fpm',
|
||||
],
|
||||
'ubuntu_20' => [
|
||||
\App\Enums\OperatingSystem::UBUNTU20 => [
|
||||
'5.6' => 'php5.6-fpm',
|
||||
'7.0' => 'php7.0-fpm',
|
||||
'7.1' => 'php7.1-fpm',
|
||||
@ -268,7 +213,7 @@
|
||||
'8.1' => 'php8.1-fpm',
|
||||
'8.3' => 'php8.3-fpm',
|
||||
],
|
||||
'ubuntu_22' => [
|
||||
\App\Enums\OperatingSystem::UBUNTU22 => [
|
||||
'5.6' => 'php5.6-fpm',
|
||||
'7.0' => 'php7.0-fpm',
|
||||
'7.1' => 'php7.1-fpm',
|
||||
@ -282,36 +227,35 @@
|
||||
],
|
||||
],
|
||||
'redis' => [
|
||||
'ubuntu_18' => [
|
||||
\App\Enums\OperatingSystem::UBUNTU20 => [
|
||||
'latest' => 'redis',
|
||||
],
|
||||
'ubuntu_20' => [
|
||||
'latest' => 'redis',
|
||||
],
|
||||
'ubuntu_22' => [
|
||||
\App\Enums\OperatingSystem::UBUNTU22 => [
|
||||
'latest' => 'redis',
|
||||
],
|
||||
],
|
||||
'supervisor' => [
|
||||
'ubuntu_18' => [
|
||||
\App\Enums\OperatingSystem::UBUNTU20 => [
|
||||
'latest' => 'supervisor',
|
||||
],
|
||||
'ubuntu_20' => [
|
||||
'latest' => 'supervisor',
|
||||
],
|
||||
'ubuntu_22' => [
|
||||
\App\Enums\OperatingSystem::UBUNTU22 => [
|
||||
'latest' => 'supervisor',
|
||||
],
|
||||
],
|
||||
'ufw' => [
|
||||
'ubuntu_18' => [
|
||||
\App\Enums\OperatingSystem::UBUNTU20 => [
|
||||
'latest' => 'ufw',
|
||||
],
|
||||
'ubuntu_20' => [
|
||||
\App\Enums\OperatingSystem::UBUNTU22 => [
|
||||
'latest' => 'ufw',
|
||||
],
|
||||
'ubuntu_22' => [
|
||||
'latest' => 'ufw',
|
||||
],
|
||||
'vito-agent' => [
|
||||
\App\Enums\OperatingSystem::UBUNTU20 => [
|
||||
'latest' => 'vito-agent',
|
||||
],
|
||||
\App\Enums\OperatingSystem::UBUNTU22 => [
|
||||
'latest' => 'vito-agent',
|
||||
],
|
||||
],
|
||||
],
|
||||
@ -327,11 +271,11 @@
|
||||
\App\Enums\SiteType::PHPMYADMIN,
|
||||
],
|
||||
'site_types_class' => [
|
||||
\App\Enums\SiteType::PHP => PHPSite::class,
|
||||
\App\Enums\SiteType::PHP_BLANK => PHPBlank::class,
|
||||
\App\Enums\SiteType::LARAVEL => Laravel::class,
|
||||
\App\Enums\SiteType::WORDPRESS => Wordpress::class,
|
||||
\App\Enums\SiteType::PHPMYADMIN => PHPMyAdmin::class,
|
||||
\App\Enums\SiteType::PHP => \App\SiteTypes\PHPSite::class,
|
||||
\App\Enums\SiteType::PHP_BLANK => \App\SiteTypes\PHPBlank::class,
|
||||
\App\Enums\SiteType::LARAVEL => \App\SiteTypes\Laravel::class,
|
||||
\App\Enums\SiteType::WORDPRESS => \App\SiteTypes\Wordpress::class,
|
||||
\App\Enums\SiteType::PHPMYADMIN => \App\SiteTypes\PHPMyAdmin::class,
|
||||
],
|
||||
|
||||
/*
|
||||
@ -343,9 +287,9 @@
|
||||
'bitbucket',
|
||||
],
|
||||
'source_control_providers_class' => [
|
||||
'github' => Github::class,
|
||||
'gitlab' => Gitlab::class,
|
||||
'bitbucket' => Bitbucket::class,
|
||||
'github' => \App\SourceControlProviders\Github::class,
|
||||
'gitlab' => \App\SourceControlProviders\Gitlab::class,
|
||||
'bitbucket' => \App\SourceControlProviders\Bitbucket::class,
|
||||
],
|
||||
|
||||
/*
|
||||
@ -403,26 +347,33 @@
|
||||
\App\Enums\NotificationChannel::TELEGRAM,
|
||||
],
|
||||
'notification_channels_providers_class' => [
|
||||
\App\Enums\NotificationChannel::SLACK => Slack::class,
|
||||
\App\Enums\NotificationChannel::DISCORD => Discord::class,
|
||||
\App\Enums\NotificationChannel::EMAIL => Email::class,
|
||||
\App\Enums\NotificationChannel::TELEGRAM => Telegram::class,
|
||||
\App\Enums\NotificationChannel::SLACK => \App\NotificationChannels\Slack::class,
|
||||
\App\Enums\NotificationChannel::DISCORD => \App\NotificationChannels\Discord::class,
|
||||
\App\Enums\NotificationChannel::EMAIL => \App\NotificationChannels\Email::class,
|
||||
\App\Enums\NotificationChannel::TELEGRAM => \App\NotificationChannels\Telegram::class,
|
||||
],
|
||||
|
||||
/*
|
||||
* storage providers
|
||||
*/
|
||||
'storage_providers' => [
|
||||
StorageProvider::DROPBOX,
|
||||
StorageProvider::FTP,
|
||||
\App\Enums\StorageProvider::DROPBOX,
|
||||
\App\Enums\StorageProvider::FTP,
|
||||
],
|
||||
'storage_providers_class' => [
|
||||
'dropbox' => Dropbox::class,
|
||||
'ftp' => FTP::class,
|
||||
'dropbox' => \App\StorageProviders\Dropbox::class,
|
||||
'ftp' => \App\StorageProviders\Ftp::class,
|
||||
],
|
||||
|
||||
'ssl_types' => [
|
||||
\App\Enums\SslType::LETSENCRYPT,
|
||||
\App\Enums\SslType::CUSTOM,
|
||||
],
|
||||
|
||||
'metrics_data_retention' => [
|
||||
7,
|
||||
14,
|
||||
30,
|
||||
90,
|
||||
],
|
||||
];
|
||||
|
22
database/factories/MetricFactory.php
Normal file
22
database/factories/MetricFactory.php
Normal file
@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
|
||||
class MetricFactory extends Factory
|
||||
{
|
||||
public function definition(): array
|
||||
{
|
||||
return [
|
||||
'server_id' => 1,
|
||||
'load' => $this->faker->randomFloat(2, 0, 100),
|
||||
'memory_total' => $this->faker->randomFloat(0, 0, 100),
|
||||
'memory_used' => $this->faker->randomFloat(0, 0, 100),
|
||||
'memory_free' => $this->faker->randomFloat(0, 0, 100),
|
||||
'disk_total' => $this->faker->randomFloat(0, 0, 100),
|
||||
'disk_used' => $this->faker->randomFloat(0, 0, 100),
|
||||
'disk_free' => $this->faker->randomFloat(0, 0, 100),
|
||||
];
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('server_logs', function (Blueprint $table) {
|
||||
$table->boolean('is_remote')->default(false);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('server_logs', function (Blueprint $table) {
|
||||
$table->dropColumn('is_remote');
|
||||
});
|
||||
}
|
||||
};
|
@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('metrics', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->unsignedBigInteger('server_id');
|
||||
$table->decimal('load', 5, 2);
|
||||
$table->decimal('memory_total', 15, 0);
|
||||
$table->decimal('memory_used', 15, 0);
|
||||
$table->decimal('memory_free', 15, 0);
|
||||
$table->decimal('disk_total', 15, 0);
|
||||
$table->decimal('disk_used', 15, 0);
|
||||
$table->decimal('disk_free', 15, 0);
|
||||
$table->timestamps();
|
||||
|
||||
$table->index(['server_id', 'created_at']);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('metrics');
|
||||
}
|
||||
};
|
32
database/seeders/MetricsSeeder.php
Normal file
32
database/seeders/MetricsSeeder.php
Normal file
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use App\Models\Metric;
|
||||
use App\Models\Service;
|
||||
use Carbon\Carbon;
|
||||
use Carbon\CarbonPeriod;
|
||||
use Illuminate\Database\Seeder;
|
||||
|
||||
class MetricsSeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* Run the database seeds.
|
||||
*/
|
||||
public function run(): void
|
||||
{
|
||||
Metric::query()->delete();
|
||||
|
||||
$monitoring = Service::query()
|
||||
->where('type', 'monitoring')
|
||||
->firstOrFail();
|
||||
|
||||
$range = CarbonPeriod::create(Carbon::now()->subDays(7), '1 minute', Carbon::now());
|
||||
foreach ($range as $date) {
|
||||
Metric::factory()->create([
|
||||
'server_id' => $monitoring->server_id,
|
||||
'created_at' => $date,
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,25 +1,29 @@
|
||||
ARG RELEASE=1
|
||||
|
||||
FROM ubuntu:22.04
|
||||
|
||||
ENV RELEASE_ARG=$RELEASE
|
||||
|
||||
WORKDIR /var/www/html
|
||||
|
||||
ENV DEBIAN_FRONTEND noninteractive
|
||||
|
||||
# upgrade
|
||||
RUN apt clean && apt update && apt update && apt upgrade -y && apt autoremove -y
|
||||
RUN apt-get clean && apt-get update && apt-get update && apt-get upgrade -y && apt-get autoremove -y
|
||||
|
||||
# requirements
|
||||
RUN apt install -y software-properties-common curl zip unzip git gcc
|
||||
RUN apt-get install -y software-properties-common curl zip unzip git gcc
|
||||
|
||||
# nginx
|
||||
RUN apt install -y nginx
|
||||
RUN apt-get install -y nginx
|
||||
|
||||
# php
|
||||
RUN apt update \
|
||||
&& apt install -y gnupg gosu curl ca-certificates zip unzip git supervisor libcap2-bin libpng-dev \
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y cron gnupg gosu curl ca-certificates zip unzip git supervisor libcap2-bin libpng-dev \
|
||||
python2 dnsutils librsvg2-bin fswatch wget \
|
||||
&& add-apt-repository ppa:ondrej/php -y \
|
||||
&& apt update \
|
||||
&& apt install -y php8.2 php8.2-fpm php8.2-mbstring php8.2-mcrypt php8.2-gd php8.2-xml \
|
||||
&& apt-get update \
|
||||
&& apt-get install -y php8.2 php8.2-fpm php8.2-mbstring php8.2-mcrypt php8.2-gd php8.2-xml \
|
||||
php8.2-curl php8.2-gettext php8.2-zip php8.2-bcmath php8.2-soap php8.2-redis php8.2-sqlite3
|
||||
COPY docker/php.ini /etc/php/8.2/cli/conf.d/99-vito.ini
|
||||
|
||||
@ -27,9 +31,9 @@ COPY docker/php.ini /etc/php/8.2/cli/conf.d/99-vito.ini
|
||||
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
|
||||
|
||||
# app
|
||||
COPY . /var/www/html
|
||||
RUN rm -rf /var/www/html/vendor
|
||||
RUN rm -rf /var/www/html/.env
|
||||
RUN rm -rf /var/www/html
|
||||
RUN git clone -b 1.x https://github.com/vitodeploy/vito.git /var/www/html
|
||||
RUN [ "$RELEASE_ARG" = "1" ] && git checkout $(git tag -l --merged 1.x --sort=-v:refname | head -n 1) || true
|
||||
RUN composer install --no-dev --prefer-dist
|
||||
RUN chown -R www-data:www-data /var/www/html \
|
||||
&& chmod -R 755 /var/www/html/storage /var/www/html/bootstrap/cache
|
||||
@ -40,6 +44,8 @@ RUN rm /etc/nginx/sites-enabled/default
|
||||
COPY docker/nginx.conf /etc/nginx/sites-available/default
|
||||
RUN ln -s /etc/nginx/sites-available/default /etc/nginx/sites-enabled/default
|
||||
|
||||
RUN echo "* * * * * cd /var/www/html && php artisan schedule:run >> /var/log/cron.log 2>&1" | crontab -
|
||||
|
||||
# supervisord
|
||||
COPY docker/supervisord.conf /etc/supervisor/conf.d/supervisord.conf
|
||||
|
||||
|
@ -37,6 +37,8 @@ php /var/www/html/artisan view:cache
|
||||
|
||||
php /var/www/html/artisan user:create "$NAME" "$EMAIL" "$PASSWORD"
|
||||
|
||||
cron
|
||||
|
||||
echo "Vito is running! 🚀"
|
||||
|
||||
/usr/bin/supervisord
|
||||
|
239
package-lock.json
generated
239
package-lock.json
generated
@ -8,8 +8,10 @@
|
||||
"@tailwindcss/forms": "^0.5.2",
|
||||
"@tailwindcss/typography": "^0.5.9",
|
||||
"alpinejs": "^3.4.2",
|
||||
"apexcharts": "^3.44.2",
|
||||
"autoprefixer": "^10.4.2",
|
||||
"flowbite": "^2.3.0",
|
||||
"flowbite-datepicker": "^1.2.6",
|
||||
"htmx.org": "^1.9.10",
|
||||
"laravel-echo": "^1.15.0",
|
||||
"laravel-vite-plugin": "^0.7.2",
|
||||
@ -20,7 +22,7 @@
|
||||
"tailwindcss": "^3.1.0",
|
||||
"tippy.js": "^6.3.7",
|
||||
"toastr": "^2.1.4",
|
||||
"vite": "^4.5.2"
|
||||
"vite": "^4.5.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-arm": {
|
||||
@ -529,6 +531,12 @@
|
||||
"integrity": "sha512-oJ4F3TnvpXaQwZJNF3ZK+kLPHKarDmJjJ6jyzVNDKH9md1dptjC7lWR//jrGuLdek/U6iltWxqAnYOu8gCiOvA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@yr/monotone-cubic-spline": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@yr/monotone-cubic-spline/-/monotone-cubic-spline-1.0.3.tgz",
|
||||
"integrity": "sha512-FQXkOta0XBSUPHndIKON2Y9JeQz5ZeMqLYZVVK93FliNBFm7LNMIZmY6FrMEB9XPcDbE2bekMbZD6kzDkxwYjA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/alpinejs": {
|
||||
"version": "3.12.0",
|
||||
"resolved": "https://registry.npmjs.org/alpinejs/-/alpinejs-3.12.0.tgz",
|
||||
@ -557,6 +565,21 @@
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/apexcharts": {
|
||||
"version": "3.48.0",
|
||||
"resolved": "https://registry.npmjs.org/apexcharts/-/apexcharts-3.48.0.tgz",
|
||||
"integrity": "sha512-Lhpj1Ij6lKlrUke8gf+P+SE6uGUn+Pe1TnCJ+zqrY0YMvbqM3LMb1lY+eybbTczUyk0RmMZomlTa2NgX2EUs4Q==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@yr/monotone-cubic-spline": "^1.0.3",
|
||||
"svg.draggable.js": "^2.2.2",
|
||||
"svg.easing.js": "^2.0.0",
|
||||
"svg.filter.js": "^2.0.2",
|
||||
"svg.pathmorphing.js": "^0.1.3",
|
||||
"svg.resize.js": "^1.4.3",
|
||||
"svg.select.js": "^3.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/arg": {
|
||||
"version": "5.0.2",
|
||||
"resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz",
|
||||
@ -885,6 +908,15 @@
|
||||
"mini-svg-data-uri": "^1.4.3"
|
||||
}
|
||||
},
|
||||
"node_modules/flowbite-datepicker": {
|
||||
"version": "1.2.6",
|
||||
"resolved": "https://registry.npmjs.org/flowbite-datepicker/-/flowbite-datepicker-1.2.6.tgz",
|
||||
"integrity": "sha512-UbU/xXs9HFiwWfL4M1vpwIo8EpS0NUQSOvYnp0Z9u3N118nU7lPFGoUOq7su9d0aOJy9FssXzx1SZwN8MXhE1g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"flowbite": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/fraction.js": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz",
|
||||
@ -1680,6 +1712,97 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/svg.draggable.js": {
|
||||
"version": "2.2.2",
|
||||
"resolved": "https://registry.npmjs.org/svg.draggable.js/-/svg.draggable.js-2.2.2.tgz",
|
||||
"integrity": "sha512-JzNHBc2fLQMzYCZ90KZHN2ohXL0BQJGQimK1kGk6AvSeibuKcIdDX9Kr0dT9+UJ5O8nYA0RB839Lhvk4CY4MZw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"svg.js": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/svg.easing.js": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/svg.easing.js/-/svg.easing.js-2.0.0.tgz",
|
||||
"integrity": "sha512-//ctPdJMGy22YoYGV+3HEfHbm6/69LJUTAqI2/5qBvaNHZ9uUFVC82B0Pl299HzgH13rKrBgi4+XyXXyVWWthA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"svg.js": ">=2.3.x"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/svg.filter.js": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/svg.filter.js/-/svg.filter.js-2.0.2.tgz",
|
||||
"integrity": "sha512-xkGBwU+dKBzqg5PtilaTb0EYPqPfJ9Q6saVldX+5vCRy31P6TlRCP3U9NxH3HEufkKkpNgdTLBJnmhDHeTqAkw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"svg.js": "^2.2.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/svg.js": {
|
||||
"version": "2.7.1",
|
||||
"resolved": "https://registry.npmjs.org/svg.js/-/svg.js-2.7.1.tgz",
|
||||
"integrity": "sha512-ycbxpizEQktk3FYvn/8BH+6/EuWXg7ZpQREJvgacqn46gIddG24tNNe4Son6omdXCnSOaApnpZw6MPCBA1dODA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/svg.pathmorphing.js": {
|
||||
"version": "0.1.3",
|
||||
"resolved": "https://registry.npmjs.org/svg.pathmorphing.js/-/svg.pathmorphing.js-0.1.3.tgz",
|
||||
"integrity": "sha512-49HWI9X4XQR/JG1qXkSDV8xViuTLIWm/B/7YuQELV5KMOPtXjiwH4XPJvr/ghEDibmLQ9Oc22dpWpG0vUDDNww==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"svg.js": "^2.4.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/svg.resize.js": {
|
||||
"version": "1.4.3",
|
||||
"resolved": "https://registry.npmjs.org/svg.resize.js/-/svg.resize.js-1.4.3.tgz",
|
||||
"integrity": "sha512-9k5sXJuPKp+mVzXNvxz7U0uC9oVMQrrf7cFsETznzUDDm0x8+77dtZkWdMfRlmbkEEYvUn9btKuZ3n41oNA+uw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"svg.js": "^2.6.5",
|
||||
"svg.select.js": "^2.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/svg.resize.js/node_modules/svg.select.js": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/svg.select.js/-/svg.select.js-2.1.2.tgz",
|
||||
"integrity": "sha512-tH6ABEyJsAOVAhwcCjF8mw4crjXSI1aa7j2VQR8ZuJ37H2MBUbyeqYr5nEO7sSN3cy9AR9DUwNg0t/962HlDbQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"svg.js": "^2.2.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/svg.select.js": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/svg.select.js/-/svg.select.js-3.0.1.tgz",
|
||||
"integrity": "sha512-h5IS/hKkuVCbKSieR9uQCj9w+zLHoPh+ce19bBYyqF53g6mnPB8sAtIbe1s9dh2S2fCmYX2xel1Ln3PJBbK4kw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"svg.js": "^2.6.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/tailwindcss": {
|
||||
"version": "3.3.1",
|
||||
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.3.1.tgz",
|
||||
@ -1812,9 +1935,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/vite": {
|
||||
"version": "4.5.2",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-4.5.2.tgz",
|
||||
"integrity": "sha512-tBCZBNSBbHQkaGyhGCDUGqeo2ph8Fstyp6FMSvTtsXeZSPpSMGlviAOav2hxVTqFcx8Hj/twtWKsMJXNY0xI8w==",
|
||||
"version": "4.5.3",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-4.5.3.tgz",
|
||||
"integrity": "sha512-kQL23kMeX92v3ph7IauVkXkikdDRsYMGTVl5KY2E9OY4ONLvkHf04MDTbnfo6NKxZiDLWzVpP5oTa8hQD8U3dg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"esbuild": "^0.18.10",
|
||||
@ -2177,6 +2300,12 @@
|
||||
"integrity": "sha512-oJ4F3TnvpXaQwZJNF3ZK+kLPHKarDmJjJ6jyzVNDKH9md1dptjC7lWR//jrGuLdek/U6iltWxqAnYOu8gCiOvA==",
|
||||
"dev": true
|
||||
},
|
||||
"@yr/monotone-cubic-spline": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@yr/monotone-cubic-spline/-/monotone-cubic-spline-1.0.3.tgz",
|
||||
"integrity": "sha512-FQXkOta0XBSUPHndIKON2Y9JeQz5ZeMqLYZVVK93FliNBFm7LNMIZmY6FrMEB9XPcDbE2bekMbZD6kzDkxwYjA==",
|
||||
"dev": true
|
||||
},
|
||||
"alpinejs": {
|
||||
"version": "3.12.0",
|
||||
"resolved": "https://registry.npmjs.org/alpinejs/-/alpinejs-3.12.0.tgz",
|
||||
@ -2202,6 +2331,21 @@
|
||||
"picomatch": "^2.0.4"
|
||||
}
|
||||
},
|
||||
"apexcharts": {
|
||||
"version": "3.48.0",
|
||||
"resolved": "https://registry.npmjs.org/apexcharts/-/apexcharts-3.48.0.tgz",
|
||||
"integrity": "sha512-Lhpj1Ij6lKlrUke8gf+P+SE6uGUn+Pe1TnCJ+zqrY0YMvbqM3LMb1lY+eybbTczUyk0RmMZomlTa2NgX2EUs4Q==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@yr/monotone-cubic-spline": "^1.0.3",
|
||||
"svg.draggable.js": "^2.2.2",
|
||||
"svg.easing.js": "^2.0.0",
|
||||
"svg.filter.js": "^2.0.2",
|
||||
"svg.pathmorphing.js": "^0.1.3",
|
||||
"svg.resize.js": "^1.4.3",
|
||||
"svg.select.js": "^3.0.1"
|
||||
}
|
||||
},
|
||||
"arg": {
|
||||
"version": "5.0.2",
|
||||
"resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz",
|
||||
@ -2434,6 +2578,15 @@
|
||||
"mini-svg-data-uri": "^1.4.3"
|
||||
}
|
||||
},
|
||||
"flowbite-datepicker": {
|
||||
"version": "1.2.6",
|
||||
"resolved": "https://registry.npmjs.org/flowbite-datepicker/-/flowbite-datepicker-1.2.6.tgz",
|
||||
"integrity": "sha512-UbU/xXs9HFiwWfL4M1vpwIo8EpS0NUQSOvYnp0Z9u3N118nU7lPFGoUOq7su9d0aOJy9FssXzx1SZwN8MXhE1g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"flowbite": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"fraction.js": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz",
|
||||
@ -2911,6 +3064,78 @@
|
||||
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
|
||||
"dev": true
|
||||
},
|
||||
"svg.draggable.js": {
|
||||
"version": "2.2.2",
|
||||
"resolved": "https://registry.npmjs.org/svg.draggable.js/-/svg.draggable.js-2.2.2.tgz",
|
||||
"integrity": "sha512-JzNHBc2fLQMzYCZ90KZHN2ohXL0BQJGQimK1kGk6AvSeibuKcIdDX9Kr0dT9+UJ5O8nYA0RB839Lhvk4CY4MZw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"svg.js": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"svg.easing.js": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/svg.easing.js/-/svg.easing.js-2.0.0.tgz",
|
||||
"integrity": "sha512-//ctPdJMGy22YoYGV+3HEfHbm6/69LJUTAqI2/5qBvaNHZ9uUFVC82B0Pl299HzgH13rKrBgi4+XyXXyVWWthA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"svg.js": ">=2.3.x"
|
||||
}
|
||||
},
|
||||
"svg.filter.js": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/svg.filter.js/-/svg.filter.js-2.0.2.tgz",
|
||||
"integrity": "sha512-xkGBwU+dKBzqg5PtilaTb0EYPqPfJ9Q6saVldX+5vCRy31P6TlRCP3U9NxH3HEufkKkpNgdTLBJnmhDHeTqAkw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"svg.js": "^2.2.5"
|
||||
}
|
||||
},
|
||||
"svg.js": {
|
||||
"version": "2.7.1",
|
||||
"resolved": "https://registry.npmjs.org/svg.js/-/svg.js-2.7.1.tgz",
|
||||
"integrity": "sha512-ycbxpizEQktk3FYvn/8BH+6/EuWXg7ZpQREJvgacqn46gIddG24tNNe4Son6omdXCnSOaApnpZw6MPCBA1dODA==",
|
||||
"dev": true
|
||||
},
|
||||
"svg.pathmorphing.js": {
|
||||
"version": "0.1.3",
|
||||
"resolved": "https://registry.npmjs.org/svg.pathmorphing.js/-/svg.pathmorphing.js-0.1.3.tgz",
|
||||
"integrity": "sha512-49HWI9X4XQR/JG1qXkSDV8xViuTLIWm/B/7YuQELV5KMOPtXjiwH4XPJvr/ghEDibmLQ9Oc22dpWpG0vUDDNww==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"svg.js": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"svg.resize.js": {
|
||||
"version": "1.4.3",
|
||||
"resolved": "https://registry.npmjs.org/svg.resize.js/-/svg.resize.js-1.4.3.tgz",
|
||||
"integrity": "sha512-9k5sXJuPKp+mVzXNvxz7U0uC9oVMQrrf7cFsETznzUDDm0x8+77dtZkWdMfRlmbkEEYvUn9btKuZ3n41oNA+uw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"svg.js": "^2.6.5",
|
||||
"svg.select.js": "^2.1.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"svg.select.js": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/svg.select.js/-/svg.select.js-2.1.2.tgz",
|
||||
"integrity": "sha512-tH6ABEyJsAOVAhwcCjF8mw4crjXSI1aa7j2VQR8ZuJ37H2MBUbyeqYr5nEO7sSN3cy9AR9DUwNg0t/962HlDbQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"svg.js": "^2.2.5"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"svg.select.js": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/svg.select.js/-/svg.select.js-3.0.1.tgz",
|
||||
"integrity": "sha512-h5IS/hKkuVCbKSieR9uQCj9w+zLHoPh+ce19bBYyqF53g6mnPB8sAtIbe1s9dh2S2fCmYX2xel1Ln3PJBbK4kw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"svg.js": "^2.6.5"
|
||||
}
|
||||
},
|
||||
"tailwindcss": {
|
||||
"version": "3.3.1",
|
||||
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.3.1.tgz",
|
||||
@ -3011,9 +3236,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"vite": {
|
||||
"version": "4.5.2",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-4.5.2.tgz",
|
||||
"integrity": "sha512-tBCZBNSBbHQkaGyhGCDUGqeo2ph8Fstyp6FMSvTtsXeZSPpSMGlviAOav2hxVTqFcx8Hj/twtWKsMJXNY0xI8w==",
|
||||
"version": "4.5.3",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-4.5.3.tgz",
|
||||
"integrity": "sha512-kQL23kMeX92v3ph7IauVkXkikdDRsYMGTVl5KY2E9OY4ONLvkHf04MDTbnfo6NKxZiDLWzVpP5oTa8hQD8U3dg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"esbuild": "^0.18.10",
|
||||
|
@ -22,6 +22,8 @@
|
||||
"tailwindcss": "^3.1.0",
|
||||
"tippy.js": "^6.3.7",
|
||||
"toastr": "^2.1.4",
|
||||
"vite": "^4.5.2"
|
||||
"vite": "^4.5.3",
|
||||
"apexcharts": "^3.44.2",
|
||||
"flowbite-datepicker": "^1.2.6"
|
||||
}
|
||||
}
|
||||
|
747
public/build/assets/app-66009dff.js
Normal file
747
public/build/assets/app-66009dff.js
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
public/build/assets/app-f65997bb.css
Normal file
1
public/build/assets/app-f65997bb.css
Normal file
File diff suppressed because one or more lines are too long
@ -1,6 +1,6 @@
|
||||
{
|
||||
"resources/css/app.css": {
|
||||
"file": "assets/app-eff15392.css",
|
||||
"file": "assets/app-f65997bb.css",
|
||||
"isEntry": true,
|
||||
"src": "resources/css/app.css"
|
||||
},
|
||||
@ -12,7 +12,7 @@
|
||||
"css": [
|
||||
"assets/app-a1ae07b3.css"
|
||||
],
|
||||
"file": "assets/app-a74f846c.js",
|
||||
"file": "assets/app-66009dff.js",
|
||||
"isEntry": true,
|
||||
"src": "resources/js/app.js"
|
||||
}
|
||||
|
29
public/static/images/remote-monitor.svg
Normal file
29
public/static/images/remote-monitor.svg
Normal file
@ -0,0 +1,29 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||
<svg width="800px" height="800px" viewBox="0 -5.23 70 70" 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="http://www.w3.org/2000/svg">
|
||||
<metadata>
|
||||
<rdf:RDF>
|
||||
<cc:Work>
|
||||
<dc:subject>
|
||||
Miscellaneous
|
||||
</dc:subject>
|
||||
<dc:identifier>
|
||||
health-monitoring
|
||||
</dc:identifier>
|
||||
<dc:title>
|
||||
Health Monitoring
|
||||
</dc:title>
|
||||
<dc:format>
|
||||
image/svg+xml
|
||||
</dc:format>
|
||||
<dc:publisher>
|
||||
Amido Limited
|
||||
</dc:publisher>
|
||||
<dc:creator>
|
||||
Richard Slater
|
||||
</dc:creator>
|
||||
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<path d="m -633.94123,753.25889 c -6.59942,-3.2916 -17.80605,-13.7307 -24.90952,-23.2035 l -0.38611,-0.5149 4.90905,0 c 3.3284,0 5.08031,-0.051 5.44092,-0.1594 0.95525,-0.2862 1.50799,-0.9179 2.58607,-2.9554 1.97619,-3.735 2.24879,-4.2224 2.2879,-4.0904 0.0218,0.074 0.44604,4.3009 0.94276,9.3939 0.87326,8.9538 0.91529,9.2823 1.27154,9.9368 0.62081,1.1407 1.47439,1.6301 2.85312,1.6359 1.01617,0 1.76269,-0.3415 2.41627,-1.1191 0.25355,-0.3016 1.82033,-3.2056 3.48173,-6.4532 l 3.02073,-5.9047 10.36659,-0.039 c 10.32236,-0.039 10.36894,-0.041 10.91581,-0.3356 1.1802,-0.6369 1.77594,-1.6202 1.77528,-2.9304 -6.9e-4,-1.3721 -0.67396,-2.4208 -1.91258,-2.9791 -0.5125,-0.231 -1.30161,-0.2501 -11.80218,-0.2858 -7.69785,-0.026 -11.47959,0.01 -11.97032,0.1108 -1.27206,0.264 -1.77303,0.7868 -3.0106,3.1416 l -1.08999,2.0739 -0.1043,-0.5158 c -0.0574,-0.2837 -0.47667,-4.3775 -0.9318,-9.0974 -0.45513,-4.7199 -0.88563,-8.7992 -0.95668,-9.0652 -0.36496,-1.3662 -1.62876,-2.2659 -3.16688,-2.2544 -1.04822,0.01 -1.94772,0.4395 -2.48617,1.1931 -0.17485,0.2447 -1.92936,3.5346 -3.8989,7.311 l -3.581,6.866 -5.76782,0.036 -5.76783,0.036 -0.83086,-1.6834 c -2.06318,-4.1804 -2.89449,-7.6097 -2.738,-11.2949 0.12425,-2.9261 0.69392,-5.0125 2.04328,-7.4832 1.10812,-2.029 3.06519,-4.3559 4.69277,-5.5795 1.78333,-1.3407 4.15216,-2.2461 6.64618,-2.5403 2.10735,-0.2485 4.60651,0.089 7.37391,0.9964 1.2153,0.3984 4.21499,1.9073 5.62954,2.8318 2.45012,1.6012 5.68511,4.4633 7.84072,6.9369 l 0.80955,0.929 0.94007,-1.2397 c 1.88483,-2.4857 4.78785,-5.1075 7.55221,-6.8208 5.19337,-3.2187 11.05786,-4.2791 15.6703,-2.8335 3.74959,1.1752 6.7744,3.9944 8.98105,8.3706 2.19828,4.3596 2.39398,9.8576 0.53892,15.1404 -1.06649,3.0372 -2.39805,5.6594 -4.46756,8.7979 -2.55838,3.88 -4.87538,6.6471 -9.08862,10.8542 -5.31708,5.3093 -11.00984,9.9038 -16.48777,13.3068 -1.60577,0.9976 -3.84246,2.2037 -4.0818,2.201 -0.0583,0 -0.75536,-0.325 -1.54898,-0.7208 z" fill="#00bcf2" transform="translate(667.003 -694.43)"/>
|
||||
</svg>
|
After Width: | Height: | Size: 3.0 KiB |
22
public/static/images/vito-agent.svg
Normal file
22
public/static/images/vito-agent.svg
Normal file
@ -0,0 +1,22 @@
|
||||
<svg width="800" height="800" viewBox="0 0 800 800" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="800" height="800" rx="50" fill="#5046E5" />
|
||||
<g filter="url(#filter0_d_1_35)">
|
||||
<path
|
||||
d="M400 601.2C392.4 601.2 386.2 599.4 381.4 595.8C376.6 592.2 372.8 586.8 370 579.6L205 202.2C202.2 195.4 201.4 189.8 202.6 185.4C203.8 180.6 206.4 176.8 210.4 174C214.8 171.2 219.8 169.8 225.4 169.8C233 169.8 238.6 171.6 242.2 175.2C246.2 178.4 249.4 183.2 251.8 189.6L410.2 557.4H391L548.8 189C551.6 183 555 178.4 559 175.2C563 171.6 568.6 169.8 575.8 169.8C581.4 169.8 586 171.2 589.6 174C593.6 176.8 596 180.6 596.8 185.4C598 190.2 597.2 195.8 594.4 202.2L429.4 579.6C426.6 586.8 422.8 592.2 418 595.8C413.6 599.4 407.6 601.2 400 601.2Z"
|
||||
fill="white" />
|
||||
</g>
|
||||
<defs>
|
||||
<filter id="filter0_d_1_35" x="196.8" y="169.8" width="405.8" height="439.4" filterUnits="userSpaceOnUse"
|
||||
color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix" />
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"
|
||||
result="hardAlpha" />
|
||||
<feOffset dy="4" />
|
||||
<feGaussianBlur stdDeviation="2" />
|
||||
<feComposite in2="hardAlpha" operator="out" />
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0" />
|
||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_1_35" />
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_1_35" result="shape" />
|
||||
</filter>
|
||||
</defs>
|
||||
</svg>
|
After Width: | Height: | Size: 1.6 KiB |
File diff suppressed because one or more lines are too long
@ -1 +0,0 @@
|
||||
ace.define("ace/mode/sh", ["require", "exports", "module", "ace/lib/oop", "ace/mode/text", "ace/tokenizer", "ace/mode/sh_highlight_rules", "ace/range"], function (e, t, n) { var r = e("../lib/oop"), i = e("./text").Mode, s = e("../tokenizer").Tokenizer, o = e("./sh_highlight_rules").ShHighlightRules, u = e("../range").Range, a = function () { this.$tokenizer = new s((new o).getRules()) }; r.inherits(a, i), function () { this.toggleCommentLines = function (e, t, n, r) { var i = !0, s = /^(\s*)#/; for (var o = n; o <= r; o++)if (!s.test(t.getLine(o))) { i = !1; break } if (i) { var a = new u(0, 0, 0, 0); for (var o = n; o <= r; o++) { var f = t.getLine(o), l = f.match(s); a.start.row = o, a.end.row = o, a.end.column = l[0].length, t.replace(a, l[1]) } } else t.indentRows(n, r, "#") }, this.getNextLineIndent = function (e, t, n) { var r = this.$getIndent(t), i = this.$tokenizer.getLineTokens(t, e), s = i.tokens; if (s.length && s[s.length - 1].type == "comment") return r; if (e == "start") { var o = t.match(/^.*[\{\(\[\:]\s*$/); o && (r += n) } return r }; var e = { pass: 1, "return": 1, raise: 1, "break": 1, "continue": 1 }; this.checkOutdent = function (t, n, r) { if (r !== "\r\n" && r !== "\r" && r !== "\n") return !1; var i = this.$tokenizer.getLineTokens(n.trim(), t).tokens; if (!i) return !1; do var s = i.pop(); while (s && (s.type == "comment" || s.type == "text" && s.value.match(/^\s+$/))); return s ? s.type == "keyword" && e[s.value] : !1 }, this.autoOutdent = function (e, t, n) { n += 1; var r = this.$getIndent(t.getLine(n)), i = t.getTabString(); r.slice(-i.length) == i && t.remove(new u(n, r.length - i.length, n, r.length)) } }.call(a.prototype), t.Mode = a }), ace.define("ace/mode/sh_highlight_rules", ["require", "exports", "module", "ace/lib/oop", "ace/mode/text_highlight_rules"], function (e, t, n) { var r = e("../lib/oop"), i = e("./text_highlight_rules").TextHighlightRules, s = t.reservedKeywords = "!|{|}|case|do|done|elif|else|esac|fi|for|if|in|then|until|while|&|;|export|local|read|typeset|unset|elif|select|set", o = t.languageConstructs = "[|]|alias|bg|bind|break|builtin|cd|command|compgen|complete|continue|dirs|disown|echo|enable|eval|exec|exit|fc|fg|getopts|hash|help|history|jobs|kill|let|logout|popd|printf|pushd|pwd|return|set|shift|shopt|source|suspend|test|times|trap|type|ulimit|umask|unalias|wait", u = function () { var e = this.createKeywordMapper({ keyword: s, "support.function.builtin": o, "invalid.deprecated": "debugger" }, "identifier"), t = "(?:(?:[1-9]\\d*)|(?:0))", n = "(?:\\.\\d+)", r = "(?:\\d+)", i = "(?:(?:" + r + "?" + n + ")|(?:" + r + "\\.))", u = "(?:(?:" + i + "|" + r + ")" + ")", a = "(?:" + u + "|" + i + ")", f = "(?:&" + r + ")", l = "[a-zA-Z][a-zA-Z0-9_]*", c = "(?:(?:\\$" + l + ")|(?:" + l + "=))", h = "(?:\\$(?:SHLVL|\\$|\\!|\\?))", p = "(?:" + l + "\\s*\\(\\))"; this.$rules = { start: [{ token: "comment", regex: "#.*$" }, { token: "string", regex: '"(?:[^\\\\]|\\\\.)*?"' }, { token: "variable.language", regex: h }, { token: "variable", regex: c }, { token: "support.function", regex: p }, { token: "support.function", regex: f }, { token: "string", regex: "'(?:[^\\\\]|\\\\.)*?'" }, { token: "constant.numeric", regex: a }, { token: "constant.numeric", regex: t + "\\b" }, { token: e, regex: "[a-zA-Z_$][a-zA-Z0-9_$]*\\b" }, { token: "keyword.operator", regex: "\\+|\\-|\\*|\\*\\*|\\/|\\/\\/|~|<|>|<=|=>|=|!=" }, { token: "paren.lparen", regex: "[\\[\\(\\{]" }, { token: "paren.rparen", regex: "[\\]\\)\\}]" }, { token: "text", regex: "\\s+" }] } }; r.inherits(u, i), t.ShHighlightRules = u })
|
@ -1,5 +0,0 @@
|
||||
ace.define("ace/theme/github", ["require", "exports", "module", "ace/lib/dom"], function (e, t, n) {
|
||||
t.isDark = !1, t.cssClass = "ace-github", t.cssText = '/* CSS style content from github\'s default pygments highlighter template.Cursor and selection styles from textmate.css. */.ace-github .ace_gutter {background: #e8e8e8;color: #AAA;}.ace-github .ace_scroller {background: #fff;}.ace-github .ace_keyword {font-weight: bold;}.ace-github .ace_string {color: #D14;}.ace-github .ace_variable.ace_class {color: teal;}.ace-github .ace_constant.ace_numeric {color: #099;}.ace-github .ace_constant.ace_buildin {color: #0086B3;}.ace-github .ace_support.ace_function {color: #0086B3;}.ace-github .ace_comment {color: #998;font-style: italic;}.ace-github .ace_variable.ace_language {color: #0086B3;}.ace-github .ace_paren {font-weight: bold;}.ace-github .ace_boolean {font-weight: bold;}.ace-github .ace_string.ace_regexp {color: #009926;font-weight: normal;}.ace-github .ace_variable.ace_instance {color: teal;}.ace-github .ace_constant.ace_language {font-weight: bold;}.ace-github .ace_text-layer {}.ace-github .ace_cursor {border-left: 2px solid black;}.ace-github .ace_overwrite-cursors .ace_cursor {border-left: 0px;border-bottom: 1px solid black;}.ace-github .ace_marker-layer .ace_active-line {background: rgb(255, 255, 204);}.ace-github .ace_marker-layer .ace_selection {background: rgb(181, 213, 255);}.ace-github.ace_multiselect .ace_selection.ace_start {box-shadow: 0 0 3px 0px white;border-radius: 2px;}/* bold keywords cause cursor issues for some fonts *//* this disables bold style for editor and keeps for static highlighter */.ace-github.ace_nobold .ace_line > span {font-weight: normal !important;}.ace-github .ace_marker-layer .ace_step {background: rgb(252, 255, 0);}.ace-github .ace_marker-layer .ace_stack {background: rgb(164, 229, 101);}.ace-github .ace_marker-layer .ace_bracket {margin: -1px 0 0 -1px;border: 1px solid rgb(192, 192, 192);}.ace-github .ace_gutter-active-line {background-color : rgba(0, 0, 0, 0.07);}.ace-github .ace_marker-layer .ace_selected-word {background: rgb(250, 250, 255);border: 1px solid rgb(200, 200, 250);}.ace-github .ace_print-margin {width: 1px;background: #e8e8e8;}.ace-github .ace_indent-guide {background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAE0lEQVQImWP4////f4bLly//BwAmVgd1/w11/gAAAABJRU5ErkJggg==") right repeat-y;}';
|
||||
var r = e("../lib/dom");
|
||||
r.importCssString(t.cssText, t.cssClass)
|
||||
})
|
@ -1,5 +0,0 @@
|
||||
ace.define("ace/theme/one-dark", ["require", "exports", "module", "ace/lib/dom"], function (e, t, n) {
|
||||
t.isDark = !1, t.cssClass = "ace-one-dark", t.cssText = '/* CSS style content from one-dark\'s default pygments highlighter template.Cursor and selection styles from textmate.css. */.ace-one-dark .ace_gutter{background:#282c34;color:#6a6f7a}.ace-one-dark .ace_print-margin{width:1px;background:#e8e8e8}.ace-one-dark{background-color:#282c34;color:#abb2bf}.ace-one-dark .ace_cursor{color:#528bff}.ace-one-dark .ace_marker-layer .ace_selection{background:#3d4350}.ace-one-dark.ace_multiselect .ace_selection.ace_start{box-shadow:0 0 3px 0 #282c34;border-radius:2px}.ace-one-dark .ace_marker-layer .ace_step{background:#c6dbae}.ace-one-dark .ace_marker-layer .ace_bracket{margin:-1px 0 0 -1px;border:1px solid #747369}.ace-one-dark .ace_marker-layer .ace_active-line{background:rgba(76,87,103,.19)}.ace-one-dark .ace_gutter-active-line{background-color:rgba(76,87,103,.19)}.ace-one-dark .ace_marker-layer .ace_selected-word{border:1px solid #3d4350}.ace-one-dark .ace_fold{background-color:#61afef;border-color:#abb2bf}.ace-one-dark .ace_keyword{color:#c678dd}.ace-one-dark .ace_keyword.ace_operator{color:#c678dd}.ace-one-dark .ace_keyword.ace_other.ace_unit{color:#d19a66}.ace-one-dark .ace_constant.ace_language{color:#d19a66}.ace-one-dark .ace_constant.ace_numeric{color:#d19a66}.ace-one-dark .ace_constant.ace_character{color:#56b6c2}.ace-one-dark .ace_constant.ace_other{color:#56b6c2}.ace-one-dark .ace_support.ace_function{color:#61afef}.ace-one-dark .ace_support.ace_constant{color:#d19a66}.ace-one-dark .ace_support.ace_class{color:#e5c07b}.ace-one-dark .ace_support.ace_type{color:#e5c07b}.ace-one-dark .ace_storage{color:#c678dd}.ace-one-dark .ace_storage.ace_type{color:#c678dd}.ace-one-dark .ace_invalid{color:#fff;background-color:#f2777a}.ace-one-dark .ace_invalid.ace_deprecated{color:#272b33;background-color:#d27b53}.ace-one-dark .ace_string{color:#98c379}.ace-one-dark .ace_string.ace_regexp{color:#e06c75}.ace-one-dark .ace_comment{font-style:italic;color:#5c6370}.ace-one-dark .ace_variable{color:#e06c75}.ace-one-dark .ace_variable.ace_parameter{color:#d19a66}.ace-one-dark .ace_meta.ace_tag{color:#e06c75}.ace-one-dark .ace_entity.ace_other.ace_attribute-name{color:#e06c75}.ace-one-dark .ace_entity.ace_name.ace_function{color:#61afef}.ace-one-dark .ace_entity.ace_name.ace_tag{color:#e06c75}.ace-one-dark .ace_markup.ace_heading{color:#98c379}.ace-one-dark .ace_indent-guide{background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAEklEQVQImWPQ09NrYAgMjP4PAAtGAwchHMyAAAAAAElFTkSuQmCC) right repeat-y}';
|
||||
var r = e("../lib/dom");
|
||||
r.importCssString(t.cssText, t.cssClass)
|
||||
})
|
@ -1,9 +1,13 @@
|
||||
import 'flowbite';
|
||||
import 'flowbite/dist/datepicker.js';
|
||||
|
||||
import Alpine from 'alpinejs';
|
||||
window.Alpine = Alpine;
|
||||
Alpine.start();
|
||||
|
||||
import ApexCharts from 'apexcharts';
|
||||
window.ApexCharts = ApexCharts;
|
||||
|
||||
import htmx from "htmx.org";
|
||||
window.htmx = htmx;
|
||||
window.htmx.defineExtension('disable-element', {
|
||||
@ -23,8 +27,8 @@ window.htmx.defineExtension('disable-element', {
|
||||
});
|
||||
document.body.addEventListener('htmx:configRequest', (event) => {
|
||||
event.detail.headers['X-CSRF-TOKEN'] = document.head.querySelector('meta[name="csrf-token"]').content;
|
||||
if (window.getSelection) { window.getSelection().removeAllRanges(); }
|
||||
else if (document.selection) { document.selection.empty(); }
|
||||
// if (window.getSelection) { window.getSelection().removeAllRanges(); }
|
||||
// else if (document.selection) { document.selection.empty(); }
|
||||
});
|
||||
document.body.addEventListener('htmx:beforeRequest', (event) => {
|
||||
let targetElements = event.target.querySelectorAll('[hx-disable]');
|
||||
@ -38,6 +42,13 @@ document.body.addEventListener('htmx:afterRequest', (event) => {
|
||||
targetElements[i].disabled = false;
|
||||
}
|
||||
});
|
||||
document.body.addEventListener('htmx:afterSwap', (event) => {
|
||||
tippy('[data-tooltip]', {
|
||||
content(reference) {
|
||||
return reference.getAttribute('data-tooltip');
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
import toastr from 'toastr';
|
||||
window.toastr = toastr;
|
||||
@ -49,13 +60,6 @@ window.toastr.options = {
|
||||
|
||||
import tippy from 'tippy.js';
|
||||
import 'tippy.js/dist/tippy.css';
|
||||
document.body.addEventListener('htmx:afterSettle', (event) => {
|
||||
tippy('[data-tooltip]', {
|
||||
content(reference) {
|
||||
return reference.getAttribute('data-tooltip');
|
||||
},
|
||||
});
|
||||
});
|
||||
tippy('[data-tooltip]', {
|
||||
content(reference) {
|
||||
return reference.getAttribute('data-tooltip');
|
||||
|
@ -4,6 +4,7 @@
|
||||
<x-slot name="trigger">
|
||||
<x-secondary-button>
|
||||
{{ __("Auto Deployment") }}
|
||||
<x-heroicon name="o-chevron-down" class="ml-1 h-4 w-4" />
|
||||
</x-secondary-button>
|
||||
</x-slot>
|
||||
<x-slot name="content">
|
||||
|
@ -6,8 +6,9 @@
|
||||
hx-swap="outerHTML"
|
||||
hx-select="#deploy"
|
||||
>
|
||||
<x-primary-button hx-disable>
|
||||
<x-primary-button class="flex items-center justify-between" :active="true" hx-disable>
|
||||
{{ __("Deploy") }}
|
||||
<x-heroicon name="o-play-circle" class="ml-1 h-5 w-5" />
|
||||
</x-primary-button>
|
||||
</form>
|
||||
@endif
|
||||
|
@ -12,11 +12,13 @@ class="p-6"
|
||||
{{ __("Deployment Script") }}
|
||||
</h2>
|
||||
|
||||
<div class="mt-6">A bash script that will be executed when you run the deployment process.</div>
|
||||
|
||||
<div class="mt-6">
|
||||
<x-input-label for="script" :value="__('Script')" />
|
||||
<x-code-editor id="script" name="script" lang="sh" class="mt-1 w-full">
|
||||
<x-textarea id="script" name="script" class="mt-1 min-h-[400px] w-full">
|
||||
{{ old("script", $site->deploymentScript?->content) }}
|
||||
</x-code-editor>
|
||||
</x-textarea>
|
||||
@error("script")
|
||||
<x-input-error class="mt-2" :messages="$message" />
|
||||
@enderror
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user