mirror of
https://github.com/vitodeploy/vito.git
synced 2025-07-01 22:16:15 +00:00
Compare commits
17 Commits
Author | SHA1 | Date | |
---|---|---|---|
75aed62d75 | |||
aaef73d89d | |||
f03a029e36 | |||
52d195710b | |||
ddacc32e64 | |||
2ae9a14d02 | |||
3019c3d213 | |||
c43869d255 | |||
18748f77ac | |||
052e28d2e3 | |||
87ec0af697 | |||
e9016737d4 | |||
f34d5eb82b | |||
12c500e125 | |||
2d566b853f | |||
ca93b521ec | |||
a0af4e3e9d |
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
|
@ -36,7 +36,7 @@ ## Useful Links
|
|||||||
- [Install via Docker](https://vitodeploy.com/introduction/installation.html#install-via-docker)
|
- [Install via Docker](https://vitodeploy.com/introduction/installation.html#install-via-docker)
|
||||||
- [Feedbacks](https://vitodeploy.featurebase.app)
|
- [Feedbacks](https://vitodeploy.featurebase.app)
|
||||||
- [Roadmap](https://vitodeploy.featurebase.app/roadmap)
|
- [Roadmap](https://vitodeploy.featurebase.app/roadmap)
|
||||||
- [Video Demo](https://youtu.be/rLRHIyEfON8)
|
- [Video Demo](https://youtu.be/AbmUOBDOc28)
|
||||||
- [Discord](https://discord.gg/uZeeHZZnm5)
|
- [Discord](https://discord.gg/uZeeHZZnm5)
|
||||||
- [Contribution](/CONTRIBUTING.md)
|
- [Contribution](/CONTRIBUTING.md)
|
||||||
- [Security](/SECURITY.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\Enums\ServiceStatus;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
|
use App\SSH\Services\PHP\PHP;
|
||||||
use Illuminate\Validation\ValidationException;
|
use Illuminate\Validation\ValidationException;
|
||||||
|
|
||||||
class ChangeDefaultCli
|
class ChangeDefaultCli
|
||||||
@ -12,7 +13,9 @@ public function change(Server $server, array $input): void
|
|||||||
{
|
{
|
||||||
$this->validate($server, $input);
|
$this->validate($server, $input);
|
||||||
$service = $server->php($input['version']);
|
$service = $server->php($input['version']);
|
||||||
$service->handler()->setDefaultCli();
|
/** @var PHP $handler */
|
||||||
|
$handler = $service->handler();
|
||||||
|
$handler->setDefaultCli();
|
||||||
$server->defaultService('php')->update(['is_default' => 0]);
|
$server->defaultService('php')->update(['is_default' => 0]);
|
||||||
$service->update(['is_default' => 1]);
|
$service->update(['is_default' => 1]);
|
||||||
$service->update(['status' => ServiceStatus::READY]);
|
$service->update(['status' => ServiceStatus::READY]);
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
namespace App\Actions\PHP;
|
namespace App\Actions\PHP;
|
||||||
|
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
|
use App\SSH\Services\PHP\PHP;
|
||||||
use Illuminate\Validation\ValidationException;
|
use Illuminate\Validation\ValidationException;
|
||||||
|
|
||||||
class GetPHPIni
|
class GetPHPIni
|
||||||
@ -14,7 +15,10 @@ public function getIni(Server $server, array $input): string
|
|||||||
$php = $server->php($input['version']);
|
$php = $server->php($input['version']);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return $php->handler()->getPHPIni();
|
/** @var PHP $handler */
|
||||||
|
$handler = $php->handler();
|
||||||
|
|
||||||
|
return $handler->getPHPIni();
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
throw ValidationException::withMessages(
|
throw ValidationException::withMessages(
|
||||||
['ini' => $e->getMessage()]
|
['ini' => $e->getMessage()]
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use App\Models\Service;
|
use App\Models\Service;
|
||||||
|
use App\SSH\Services\PHP\PHP;
|
||||||
use Illuminate\Support\Facades\Validator;
|
use Illuminate\Support\Facades\Validator;
|
||||||
use Illuminate\Validation\Rule;
|
use Illuminate\Validation\Rule;
|
||||||
use Illuminate\Validation\ValidationException;
|
use Illuminate\Validation\ValidationException;
|
||||||
@ -23,7 +24,9 @@ public function install(Server $server, array $input): Service
|
|||||||
$service->save();
|
$service->save();
|
||||||
|
|
||||||
dispatch(function () use ($service, $input) {
|
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) {
|
})->catch(function () use ($service, $input) {
|
||||||
$service->refresh();
|
$service->refresh();
|
||||||
$typeData = $service->type_data;
|
$typeData = $service->type_data;
|
||||||
|
@ -34,8 +34,6 @@ public function create(Site $site, array $input): void
|
|||||||
$ssl->status = SslStatus::CREATED;
|
$ssl->status = SslStatus::CREATED;
|
||||||
$ssl->save();
|
$ssl->save();
|
||||||
$site->type()->edit();
|
$site->type()->edit();
|
||||||
})->catch(function () use ($ssl) {
|
|
||||||
$ssl->delete();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,14 +8,15 @@
|
|||||||
use Illuminate\Support\Facades\Validator;
|
use Illuminate\Support\Facades\Validator;
|
||||||
use Illuminate\Validation\Rule;
|
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);
|
$this->validate($server, $input);
|
||||||
|
|
||||||
$service = new Service([
|
$service = new Service([
|
||||||
'name' => $input['type'],
|
'server_id' => $server->id,
|
||||||
|
'name' => $input['name'],
|
||||||
'type' => $input['type'],
|
'type' => $input['type'],
|
||||||
'version' => $input['version'],
|
'version' => $input['version'],
|
||||||
'status' => ServiceStatus::INSTALLING,
|
'status' => ServiceStatus::INSTALLING,
|
||||||
@ -27,15 +28,13 @@ public function create(Server $server, array $input): Service
|
|||||||
|
|
||||||
$service->save();
|
$service->save();
|
||||||
|
|
||||||
$service->handler()->create();
|
|
||||||
|
|
||||||
dispatch(function () use ($service) {
|
dispatch(function () use ($service) {
|
||||||
$service->handler()->install();
|
$service->handler()->install();
|
||||||
$service->status = ServiceStatus::READY;
|
$service->status = ServiceStatus::READY;
|
||||||
$service->save();
|
$service->save();
|
||||||
})->catch(function () use ($service) {
|
})->catch(function () use ($service) {
|
||||||
$service->handler()->delete();
|
$service->status = ServiceStatus::INSTALLATION_FAILED;
|
||||||
$service->delete();
|
$service->save();
|
||||||
})->onConnection('ssh');
|
})->onConnection('ssh');
|
||||||
|
|
||||||
return $service;
|
return $service;
|
||||||
@ -46,8 +45,11 @@ private function validate(Server $server, array $input): void
|
|||||||
Validator::make($input, [
|
Validator::make($input, [
|
||||||
'type' => [
|
'type' => [
|
||||||
'required',
|
'required',
|
||||||
Rule::in(config('core.add_on_services')),
|
Rule::in(config('core.service_types')),
|
||||||
Rule::unique('services', 'type')->where('server_id', $server->id),
|
],
|
||||||
|
'name' => [
|
||||||
|
'required',
|
||||||
|
Rule::in(array_keys(config('core.service_types'))),
|
||||||
],
|
],
|
||||||
'version' => 'required',
|
'version' => 'required',
|
||||||
])->validate();
|
])->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;
|
namespace App\Actions\Site;
|
||||||
|
|
||||||
use App\Enums\SiteStatus;
|
use App\Enums\SiteStatus;
|
||||||
|
use App\Exceptions\RepositoryNotFound;
|
||||||
|
use App\Exceptions\RepositoryPermissionDenied;
|
||||||
use App\Exceptions\SourceControlIsNotConnected;
|
use App\Exceptions\SourceControlIsNotConnected;
|
||||||
use App\Facades\Notifier;
|
use App\Facades\Notifier;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
@ -19,7 +21,6 @@
|
|||||||
class CreateSite
|
class CreateSite
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @throws SourceControlIsNotConnected
|
|
||||||
* @throws ValidationException
|
* @throws ValidationException
|
||||||
*/
|
*/
|
||||||
public function create(Server $server, array $input): Site
|
public function create(Server $server, array $input): Site
|
||||||
@ -47,7 +48,15 @@ public function create(Server $server, array $input): Site
|
|||||||
}
|
}
|
||||||
} catch (SourceControlIsNotConnected) {
|
} catch (SourceControlIsNotConnected) {
|
||||||
throw ValidationException::withMessages([
|
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');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -16,6 +16,7 @@ protected function schedule(Schedule $schedule): void
|
|||||||
$schedule->command('backups:run "0 0 * * *"')->daily();
|
$schedule->command('backups:run "0 0 * * *"')->daily();
|
||||||
$schedule->command('backups:run "0 0 * * 0"')->weekly();
|
$schedule->command('backups:run "0 0 * * 0"')->weekly();
|
||||||
$schedule->command('backups:run "0 0 1 * *"')->monthly();
|
$schedule->command('backups:run "0 0 1 * *"')->monthly();
|
||||||
|
$schedule->command('metrics:delete-older-metrics')->daily();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -2,9 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Exceptions;
|
namespace App\Exceptions;
|
||||||
|
|
||||||
use Exception;
|
class SSHAuthenticationError extends SSHError
|
||||||
|
|
||||||
class SSHAuthenticationError extends Exception
|
|
||||||
{
|
{
|
||||||
//
|
//
|
||||||
}
|
}
|
||||||
|
@ -2,9 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Exceptions;
|
namespace App\Exceptions;
|
||||||
|
|
||||||
use Exception;
|
class SSLCreationException extends SSHError
|
||||||
|
|
||||||
class SSLCreationException extends Exception
|
|
||||||
{
|
{
|
||||||
//
|
//
|
||||||
}
|
}
|
||||||
|
@ -133,7 +133,7 @@ public function exec(string $command, string $log = '', ?int $siteId = null, ?bo
|
|||||||
$this->log?->write($output);
|
$this->log?->write($output);
|
||||||
|
|
||||||
if (Str::contains($output, 'VITO_SSH_ERROR')) {
|
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;
|
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();
|
||||||
|
}
|
||||||
|
}
|
@ -7,6 +7,8 @@
|
|||||||
use App\Actions\Site\UpdateDeploymentScript;
|
use App\Actions\Site\UpdateDeploymentScript;
|
||||||
use App\Actions\Site\UpdateEnv;
|
use App\Actions\Site\UpdateEnv;
|
||||||
use App\Exceptions\DeploymentScriptIsEmptyException;
|
use App\Exceptions\DeploymentScriptIsEmptyException;
|
||||||
|
use App\Exceptions\RepositoryNotFound;
|
||||||
|
use App\Exceptions\RepositoryPermissionDenied;
|
||||||
use App\Exceptions\SourceControlIsNotConnected;
|
use App\Exceptions\SourceControlIsNotConnected;
|
||||||
use App\Facades\Toast;
|
use App\Facades\Toast;
|
||||||
use App\Helpers\HtmxResponse;
|
use App\Helpers\HtmxResponse;
|
||||||
@ -24,12 +26,14 @@ public function deploy(Server $server, Site $site): HtmxResponse
|
|||||||
app(Deploy::class)->run($site);
|
app(Deploy::class)->run($site);
|
||||||
|
|
||||||
Toast::success('Deployment started!');
|
Toast::success('Deployment started!');
|
||||||
} catch (SourceControlIsNotConnected $e) {
|
} catch (SourceControlIsNotConnected) {
|
||||||
Toast::error($e->getMessage());
|
Toast::error('Source control is not connected. Check site\'s settings.');
|
||||||
|
|
||||||
return htmx()->redirect(route('source-controls'));
|
|
||||||
} catch (DeploymentScriptIsEmptyException) {
|
} catch (DeploymentScriptIsEmptyException) {
|
||||||
Toast::error('Deployment script is empty!');
|
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();
|
return htmx()->back();
|
||||||
@ -83,6 +87,12 @@ public function enableAutoDeployment(Server $server, Site $site): HtmxResponse
|
|||||||
Toast::success('Auto deployment has been enabled.');
|
Toast::success('Auto deployment has been enabled.');
|
||||||
} catch (SourceControlIsNotConnected) {
|
} catch (SourceControlIsNotConnected) {
|
||||||
Toast::error('Source control is not connected. Check site\'s settings.');
|
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');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -2,7 +2,8 @@
|
|||||||
|
|
||||||
namespace App\Http\Controllers;
|
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\Facades\Toast;
|
||||||
use App\Helpers\HtmxResponse;
|
use App\Helpers\HtmxResponse;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
@ -68,7 +69,16 @@ public function disable(Server $server, Service $service): RedirectResponse
|
|||||||
|
|
||||||
public function install(Server $server, Request $request): HtmxResponse
|
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!');
|
Toast::success('Service is being uninstalled!');
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
use App\Actions\Site\CreateSite;
|
use App\Actions\Site\CreateSite;
|
||||||
use App\Actions\Site\DeleteSite;
|
use App\Actions\Site\DeleteSite;
|
||||||
|
use App\Enums\SiteStatus;
|
||||||
use App\Enums\SiteType;
|
use App\Enums\SiteType;
|
||||||
use App\Facades\Toast;
|
use App\Facades\Toast;
|
||||||
use App\Helpers\HtmxResponse;
|
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', [
|
return view('sites.show', [
|
||||||
'server' => $server,
|
'server' => $server,
|
||||||
'site' => $site,
|
'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
|
public function destroy(Server $server, Site $site): RedirectResponse
|
||||||
{
|
{
|
||||||
app(DeleteSite::class)->delete($site);
|
app(DeleteSite::class)->delete($site);
|
||||||
|
@ -2,10 +2,12 @@
|
|||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use App\Actions\Site\UpdateSourceControl;
|
||||||
use App\Facades\Toast;
|
use App\Facades\Toast;
|
||||||
use App\Helpers\HtmxResponse;
|
use App\Helpers\HtmxResponse;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use App\Models\Site;
|
use App\Models\Site;
|
||||||
|
use App\SSH\Services\Webserver\Webserver;
|
||||||
use Illuminate\Contracts\View\View;
|
use Illuminate\Contracts\View\View;
|
||||||
use Illuminate\Http\RedirectResponse;
|
use Illuminate\Http\RedirectResponse;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
@ -24,7 +26,10 @@ public function index(Server $server, Site $site): View
|
|||||||
|
|
||||||
public function getVhost(Server $server, Site $site): RedirectResponse
|
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
|
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 {
|
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!');
|
Toast::success('VHost updated successfully!');
|
||||||
} catch (Throwable $e) {
|
} catch (Throwable $e) {
|
||||||
@ -63,4 +70,13 @@ public function updatePHPVersion(Server $server, Site $site, Request $request):
|
|||||||
|
|
||||||
return htmx()->back();
|
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
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');
|
return $this->queues()->whereNull('site_id');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function metrics(): HasMany
|
||||||
|
{
|
||||||
|
return $this->hasMany(Metric::class);
|
||||||
|
}
|
||||||
|
|
||||||
public function sshKeys(): BelongsToMany
|
public function sshKeys(): BelongsToMany
|
||||||
{
|
{
|
||||||
return $this->belongsToMany(SshKey::class, 'server_ssh_keys')
|
return $this->belongsToMany(SshKey::class, 'server_ssh_keys')
|
||||||
@ -325,6 +330,24 @@ public function php(?string $version = null): ?Service
|
|||||||
return $this->service('php', $version);
|
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
|
public function sshKey(): array
|
||||||
{
|
{
|
||||||
/** @var FilesystemAdapter $storageDisk */
|
/** @var FilesystemAdapter $storageDisk */
|
||||||
|
@ -4,13 +4,7 @@
|
|||||||
|
|
||||||
use App\Actions\Service\Manage;
|
use App\Actions\Service\Manage;
|
||||||
use App\Exceptions\ServiceInstallationFailed;
|
use App\Exceptions\ServiceInstallationFailed;
|
||||||
use App\SSH\Services\AddOnServices\AbstractAddOnService;
|
use App\SSH\Services\ServiceInterface;
|
||||||
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 Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
@ -65,8 +59,8 @@ public function server(): BelongsTo
|
|||||||
return $this->belongsTo(Server::class);
|
return $this->belongsTo(Server::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function handler(
|
public function handler(): ServiceInterface
|
||||||
): PHPHandler|WebserverHandler|DatabaseHandler|FirewallHandler|ProcessManagerHandler|RedisHandler|AbstractAddOnService {
|
{
|
||||||
$handler = config('core.service_handlers')[$this->name];
|
$handler = config('core.service_handlers')[$this->name];
|
||||||
|
|
||||||
return new $handler($this);
|
return new $handler($this);
|
||||||
|
@ -148,4 +148,12 @@ public function unzip(string $path): string
|
|||||||
'unzip '.$path
|
'unzip '.$path
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function cleanup(): void
|
||||||
|
{
|
||||||
|
$this->server->ssh()->exec(
|
||||||
|
$this->getScript('cleanup.sh'),
|
||||||
|
'cleanup'
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
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."
|
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;
|
namespace App\SSH\Services\Database;
|
||||||
|
|
||||||
|
use App\Enums\BackupStatus;
|
||||||
use App\Models\BackupFile;
|
use App\Models\BackupFile;
|
||||||
use App\Models\Server;
|
|
||||||
use App\Models\Service;
|
|
||||||
use App\SSH\HasScripts;
|
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;
|
use HasScripts;
|
||||||
|
|
||||||
protected Service $service;
|
|
||||||
|
|
||||||
protected Server $server;
|
|
||||||
|
|
||||||
abstract protected function getScriptsDir(): string;
|
abstract protected function getScriptsDir(): string;
|
||||||
|
|
||||||
public function __construct(Service $service)
|
public function creationRules(array $input): array
|
||||||
{
|
{
|
||||||
$this->service = $service;
|
return [
|
||||||
$this->server = $service->server;
|
'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
|
public function install(): void
|
||||||
{
|
{
|
||||||
$version = $this->service->version;
|
$version = $this->service->version;
|
||||||
$command = $this->getScript($this->service->name.'/install-'.$version.'.sh');
|
$command = $this->getScript($this->service->name.'/install-'.$version.'.sh');
|
||||||
$this->server->ssh()->exec($command, 'install-'.$this->service->name.'-'.$version);
|
$this->service->server->ssh()->exec($command, 'install-'.$this->service->name.'-'.$version);
|
||||||
$status = $this->server->systemd()->status($this->service->unit);
|
$status = $this->service->server->systemd()->status($this->service->unit);
|
||||||
$this->service->validateInstall($status);
|
$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
|
public function create(string $name): void
|
||||||
{
|
{
|
||||||
$this->server->ssh()->exec(
|
$this->service->server->ssh()->exec(
|
||||||
$this->getScript($this->getScriptsDir().'/create.sh', [
|
$this->getScript($this->getScriptsDir().'/create.sh', [
|
||||||
'name' => $name,
|
'name' => $name,
|
||||||
]),
|
]),
|
||||||
@ -45,7 +83,7 @@ public function create(string $name): void
|
|||||||
|
|
||||||
public function delete(string $name): void
|
public function delete(string $name): void
|
||||||
{
|
{
|
||||||
$this->server->ssh()->exec(
|
$this->service->server->ssh()->exec(
|
||||||
$this->getScript($this->getScriptsDir().'/delete.sh', [
|
$this->getScript($this->getScriptsDir().'/delete.sh', [
|
||||||
'name' => $name,
|
'name' => $name,
|
||||||
]),
|
]),
|
||||||
@ -55,7 +93,7 @@ public function delete(string $name): void
|
|||||||
|
|
||||||
public function createUser(string $username, string $password, string $host): 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', [
|
$this->getScript($this->getScriptsDir().'/create-user.sh', [
|
||||||
'username' => $username,
|
'username' => $username,
|
||||||
'password' => $password,
|
'password' => $password,
|
||||||
@ -67,7 +105,7 @@ public function createUser(string $username, string $password, string $host): vo
|
|||||||
|
|
||||||
public function deleteUser(string $username, string $host): void
|
public function deleteUser(string $username, string $host): void
|
||||||
{
|
{
|
||||||
$this->server->ssh()->exec(
|
$this->service->server->ssh()->exec(
|
||||||
$this->getScript($this->getScriptsDir().'/delete-user.sh', [
|
$this->getScript($this->getScriptsDir().'/delete-user.sh', [
|
||||||
'username' => $username,
|
'username' => $username,
|
||||||
'host' => $host,
|
'host' => $host,
|
||||||
@ -78,7 +116,7 @@ public function deleteUser(string $username, string $host): void
|
|||||||
|
|
||||||
public function link(string $username, string $host, array $databases): void
|
public function link(string $username, string $host, array $databases): void
|
||||||
{
|
{
|
||||||
$ssh = $this->server->ssh();
|
$ssh = $this->service->server->ssh();
|
||||||
|
|
||||||
foreach ($databases as $database) {
|
foreach ($databases as $database) {
|
||||||
$ssh->exec(
|
$ssh->exec(
|
||||||
@ -94,7 +132,7 @@ public function link(string $username, string $host, array $databases): void
|
|||||||
|
|
||||||
public function unlink(string $username, string $host): void
|
public function unlink(string $username, string $host): void
|
||||||
{
|
{
|
||||||
$this->server->ssh()->exec(
|
$this->service->server->ssh()->exec(
|
||||||
$this->getScript($this->getScriptsDir().'/unlink.sh', [
|
$this->getScript($this->getScriptsDir().'/unlink.sh', [
|
||||||
'username' => $username,
|
'username' => $username,
|
||||||
'host' => $host,
|
'host' => $host,
|
||||||
@ -106,7 +144,7 @@ public function unlink(string $username, string $host): void
|
|||||||
public function runBackup(BackupFile $backupFile): void
|
public function runBackup(BackupFile $backupFile): void
|
||||||
{
|
{
|
||||||
// backup
|
// backup
|
||||||
$this->server->ssh()->exec(
|
$this->service->server->ssh()->exec(
|
||||||
$this->getScript($this->getScriptsDir().'/backup.sh', [
|
$this->getScript($this->getScriptsDir().'/backup.sh', [
|
||||||
'file' => $backupFile->name,
|
'file' => $backupFile->name,
|
||||||
'database' => $backupFile->backup->database->name,
|
'database' => $backupFile->backup->database->name,
|
||||||
@ -115,13 +153,13 @@ public function runBackup(BackupFile $backupFile): void
|
|||||||
);
|
);
|
||||||
|
|
||||||
// upload to storage
|
// 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->path(),
|
||||||
$backupFile->storagePath(),
|
$backupFile->storagePath(),
|
||||||
);
|
);
|
||||||
|
|
||||||
// cleanup
|
// cleanup
|
||||||
$this->server->ssh()->exec('rm '.$backupFile->name.'.zip');
|
$this->service->server->ssh()->exec('rm '.$backupFile->name.'.zip');
|
||||||
|
|
||||||
$backupFile->size = $upload['size'];
|
$backupFile->size = $upload['size'];
|
||||||
$backupFile->save();
|
$backupFile->save();
|
||||||
@ -130,12 +168,12 @@ public function runBackup(BackupFile $backupFile): void
|
|||||||
public function restoreBackup(BackupFile $backupFile, string $database): void
|
public function restoreBackup(BackupFile $backupFile, string $database): void
|
||||||
{
|
{
|
||||||
// download
|
// download
|
||||||
$backupFile->backup->storage->provider()->ssh($this->server)->download(
|
$backupFile->backup->storage->provider()->ssh($this->service->server)->download(
|
||||||
$backupFile->storagePath(),
|
$backupFile->storagePath(),
|
||||||
$backupFile->name.'.zip',
|
$backupFile->name.'.zip',
|
||||||
);
|
);
|
||||||
|
|
||||||
$this->server->ssh()->exec(
|
$this->service->server->ssh()->exec(
|
||||||
$this->getScript($this->getScriptsDir().'/restore.sh', [
|
$this->getScript($this->getScriptsDir().'/restore.sh', [
|
||||||
'database' => $database,
|
'database' => $database,
|
||||||
'file' => $backupFile->name,
|
'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 DEBIAN_FRONTEND=noninteractive apt-get install mariadb-server mariadb-backup -y
|
||||||
|
|
||||||
|
sudo systemctl unmask mysql.service
|
||||||
|
|
||||||
sudo service mysql start
|
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 DEBIAN_FRONTEND=noninteractive apt-get install mariadb-server mariadb-backup -y
|
||||||
|
|
||||||
|
sudo systemctl unmask mysql.service
|
||||||
|
|
||||||
sudo service mysql start
|
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 DEBIAN_FRONTEND=noninteractive apt-get install mysql-server -y
|
||||||
|
|
||||||
|
sudo systemctl unmask mysql.service
|
||||||
|
|
||||||
sudo service mysql enable
|
sudo service mysql enable
|
||||||
|
|
||||||
sudo service mysql start
|
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 DEBIAN_FRONTEND=noninteractive apt-get install mysql-server -y
|
||||||
|
|
||||||
|
sudo systemctl unmask mysql.service
|
||||||
|
|
||||||
sudo service mysql enable
|
sudo service mysql enable
|
||||||
|
|
||||||
sudo service mysql start
|
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;
|
namespace App\SSH\Services\Firewall;
|
||||||
|
|
||||||
use App\Models\Service;
|
use App\SSH\Services\AbstractService;
|
||||||
use App\SSH\Services\ServiceInterface;
|
|
||||||
|
|
||||||
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'),
|
$this->getScript('ufw/install-ufw.sh'),
|
||||||
'install-ufw'
|
'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
|
public function addRule(string $type, string $protocol, int $port, string $source, ?string $mask): void
|
||||||
|
@ -3,20 +3,43 @@
|
|||||||
namespace App\SSH\Services\PHP;
|
namespace App\SSH\Services\PHP;
|
||||||
|
|
||||||
use App\Exceptions\SSHCommandError;
|
use App\Exceptions\SSHCommandError;
|
||||||
use App\Models\Service;
|
|
||||||
use App\SSH\HasScripts;
|
use App\SSH\HasScripts;
|
||||||
use App\SSH\Services\ServiceInterface;
|
use App\SSH\Services\AbstractService;
|
||||||
|
use Closure;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
|
use Illuminate\Validation\Rule;
|
||||||
|
|
||||||
class PHP implements ServiceInterface
|
class PHP extends AbstractService
|
||||||
{
|
{
|
||||||
use HasScripts;
|
use HasScripts;
|
||||||
|
|
||||||
protected Service $service;
|
public function creationRules(array $input): array
|
||||||
|
|
||||||
public function __construct(Service $service)
|
|
||||||
{
|
{
|
||||||
$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
|
public function install(): void
|
||||||
@ -29,6 +52,7 @@ public function install(): void
|
|||||||
]),
|
]),
|
||||||
'install-php-'.$this->service->version
|
'install-php-'.$this->service->version
|
||||||
);
|
);
|
||||||
|
$this->service->server->os()->cleanup();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function uninstall(): void
|
public function uninstall(): void
|
||||||
@ -39,6 +63,7 @@ public function uninstall(): void
|
|||||||
]),
|
]),
|
||||||
'uninstall-php-'.$this->service->version
|
'uninstall-php-'.$this->service->version
|
||||||
);
|
);
|
||||||
|
$this->service->server->os()->cleanup();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setDefaultCli(): void
|
public function setDefaultCli(): void
|
||||||
|
@ -2,15 +2,37 @@
|
|||||||
|
|
||||||
namespace App\SSH\Services\ProcessManager;
|
namespace App\SSH\Services\ProcessManager;
|
||||||
|
|
||||||
use App\Models\Service;
|
use App\SSH\Services\AbstractService;
|
||||||
use App\SSH\Services\ServiceInterface;
|
use Closure;
|
||||||
|
|
||||||
abstract class AbstractProcessManager implements ProcessManager, ServiceInterface
|
abstract class AbstractProcessManager extends AbstractService implements ProcessManager
|
||||||
{
|
{
|
||||||
protected Service $service;
|
public function creationRules(array $input): array
|
||||||
|
|
||||||
public function __construct(Service $service)
|
|
||||||
{
|
{
|
||||||
$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'),
|
$this->getScript('supervisor/install-supervisor.sh'),
|
||||||
'install-supervisor'
|
'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;
|
namespace App\SSH\Services\Redis;
|
||||||
|
|
||||||
use App\Models\Service;
|
|
||||||
use App\SSH\HasScripts;
|
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;
|
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
|
public function install(): void
|
||||||
@ -20,5 +31,17 @@ public function install(): void
|
|||||||
$this->getScript('install.sh'),
|
$this->getScript('install.sh'),
|
||||||
'install-redis'
|
'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
|
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 install(): void;
|
||||||
|
|
||||||
|
public function uninstall(): void;
|
||||||
}
|
}
|
||||||
|
87
app/SSH/Services/VitoAgent/VitoAgent.php
Normal file
87
app/SSH/Services/VitoAgent/VitoAgent.php
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\SSH\Services\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/VitoAgent/scripts/install.sh
Normal file
53
app/SSH/Services/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/VitoAgent/scripts/uninstall.sh
Normal file
13
app/SSH/Services/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"
|
@ -2,12 +2,8 @@
|
|||||||
|
|
||||||
namespace App\SSH\Services\Webserver;
|
namespace App\SSH\Services\Webserver;
|
||||||
|
|
||||||
use App\Models\Service;
|
use App\SSH\Services\AbstractService;
|
||||||
use App\SSH\Services\ServiceInterface;
|
|
||||||
|
|
||||||
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\Site;
|
||||||
use App\Models\Ssl;
|
use App\Models\Ssl;
|
||||||
use App\SSH\HasScripts;
|
use App\SSH\HasScripts;
|
||||||
|
use Closure;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
use Throwable;
|
use Throwable;
|
||||||
|
|
||||||
@ -23,6 +24,31 @@ public function install(): void
|
|||||||
]),
|
]),
|
||||||
'install-nginx'
|
'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
|
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', [
|
$this->getScript('nginx/update-vhost.sh', [
|
||||||
'domain' => $site->domain,
|
'domain' => $site->domain,
|
||||||
'path' => $site->path,
|
'path' => $site->path,
|
||||||
'vhost' => $this->generateVhost($site, $noSSL),
|
'vhost' => $vhost ?? $this->generateVhost($site, $noSSL),
|
||||||
]),
|
]),
|
||||||
'update-vhost',
|
'update-vhost',
|
||||||
$site->id
|
$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\Enums\ServiceStatus;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
|
use App\SSH\Services\PHP\PHP;
|
||||||
|
|
||||||
abstract class AbstractType implements ServerType
|
abstract class AbstractType implements ServerType
|
||||||
{
|
{
|
||||||
@ -31,7 +32,9 @@ public function install(): void
|
|||||||
$service->update(['status' => ServiceStatus::READY]);
|
$service->update(['status' => ServiceStatus::READY]);
|
||||||
if ($service->type == 'php') {
|
if ($service->type == 'php') {
|
||||||
$this->progress($currentProgress, 'installing-composer');
|
$this->progress($currentProgress, 'installing-composer');
|
||||||
$service->handler()->installComposer();
|
/** @var PHP $handler */
|
||||||
|
$handler = $service->handler();
|
||||||
|
$handler->installComposer();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$this->progress(100, 'finishing');
|
$this->progress(100, 'finishing');
|
||||||
|
@ -13,7 +13,7 @@ public function createRules(array $input): array
|
|||||||
],
|
],
|
||||||
'php' => [
|
'php' => [
|
||||||
'required',
|
'required',
|
||||||
'in:'.implode(',', config('core.php_versions')),
|
'in:none,'.implode(',', config('core.php_versions')),
|
||||||
],
|
],
|
||||||
'database' => [
|
'database' => [
|
||||||
'required',
|
'required',
|
||||||
|
@ -34,3 +34,12 @@ function vito_version(): string
|
|||||||
{
|
{
|
||||||
return exec('git describe --tags');
|
return exec('git describe --tags');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
223
config/core.php
223
config/core.php
@ -1,35 +1,5 @@
|
|||||||
<?php
|
<?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 [
|
return [
|
||||||
/*
|
/*
|
||||||
* SSH
|
* SSH
|
||||||
@ -44,12 +14,12 @@
|
|||||||
* General
|
* General
|
||||||
*/
|
*/
|
||||||
'operating_systems' => [
|
'operating_systems' => [
|
||||||
OperatingSystem::UBUNTU20,
|
\App\Enums\OperatingSystem::UBUNTU20,
|
||||||
OperatingSystem::UBUNTU22,
|
\App\Enums\OperatingSystem::UBUNTU22,
|
||||||
],
|
],
|
||||||
'webservers' => ['none', 'nginx'],
|
'webservers' => ['none', 'nginx'],
|
||||||
'php_versions' => [
|
'php_versions' => [
|
||||||
'none',
|
// 'none',
|
||||||
// '5.6',
|
// '5.6',
|
||||||
'7.0',
|
'7.0',
|
||||||
'7.1',
|
'7.1',
|
||||||
@ -126,117 +96,102 @@
|
|||||||
],
|
],
|
||||||
'server_providers_class' => [
|
'server_providers_class' => [
|
||||||
\App\Enums\ServerProvider::CUSTOM => \App\ServerProviders\Custom::class,
|
\App\Enums\ServerProvider::CUSTOM => \App\ServerProviders\Custom::class,
|
||||||
\App\Enums\ServerProvider::AWS => AWS::class,
|
\App\Enums\ServerProvider::AWS => \App\ServerProviders\AWS::class,
|
||||||
\App\Enums\ServerProvider::LINODE => Linode::class,
|
\App\Enums\ServerProvider::LINODE => \App\ServerProviders\Linode::class,
|
||||||
\App\Enums\ServerProvider::DIGITALOCEAN => DigitalOcean::class,
|
\App\Enums\ServerProvider::DIGITALOCEAN => \App\ServerProviders\DigitalOcean::class,
|
||||||
\App\Enums\ServerProvider::VULTR => Vultr::class,
|
\App\Enums\ServerProvider::VULTR => \App\ServerProviders\Vultr::class,
|
||||||
\App\Enums\ServerProvider::HETZNER => Hetzner::class,
|
\App\Enums\ServerProvider::HETZNER => \App\ServerProviders\Hetzner::class,
|
||||||
],
|
],
|
||||||
'server_providers_default_user' => [
|
'server_providers_default_user' => [
|
||||||
'custom' => [
|
'custom' => [
|
||||||
'ubuntu_18' => 'root',
|
\App\Enums\OperatingSystem::UBUNTU20 => 'root',
|
||||||
'ubuntu_20' => 'root',
|
\App\Enums\OperatingSystem::UBUNTU22 => 'root',
|
||||||
'ubuntu_22' => 'root',
|
|
||||||
],
|
],
|
||||||
'aws' => [
|
'aws' => [
|
||||||
'ubuntu_18' => 'ubuntu',
|
\App\Enums\OperatingSystem::UBUNTU20 => 'ubuntu',
|
||||||
'ubuntu_20' => 'ubuntu',
|
\App\Enums\OperatingSystem::UBUNTU22 => 'ubuntu',
|
||||||
'ubuntu_22' => 'ubuntu',
|
|
||||||
],
|
],
|
||||||
'linode' => [
|
'linode' => [
|
||||||
'ubuntu_18' => 'root',
|
\App\Enums\OperatingSystem::UBUNTU20 => 'root',
|
||||||
'ubuntu_20' => 'root',
|
\App\Enums\OperatingSystem::UBUNTU22 => 'root',
|
||||||
'ubuntu_22' => 'root',
|
|
||||||
],
|
],
|
||||||
'digitalocean' => [
|
'digitalocean' => [
|
||||||
'ubuntu_18' => 'root',
|
\App\Enums\OperatingSystem::UBUNTU20 => 'root',
|
||||||
'ubuntu_20' => 'root',
|
\App\Enums\OperatingSystem::UBUNTU22 => 'root',
|
||||||
'ubuntu_22' => 'root',
|
|
||||||
],
|
],
|
||||||
'vultr' => [
|
'vultr' => [
|
||||||
'ubuntu_18' => 'root',
|
\App\Enums\OperatingSystem::UBUNTU20 => 'root',
|
||||||
'ubuntu_20' => 'root',
|
\App\Enums\OperatingSystem::UBUNTU22 => 'root',
|
||||||
'ubuntu_22' => 'root',
|
|
||||||
],
|
],
|
||||||
'hetzner' => [
|
'hetzner' => [
|
||||||
'ubuntu_18' => 'root',
|
\App\Enums\OperatingSystem::UBUNTU20 => 'root',
|
||||||
'ubuntu_20' => 'root',
|
\App\Enums\OperatingSystem::UBUNTU22 => 'root',
|
||||||
'ubuntu_22' => 'root',
|
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Service
|
* Service
|
||||||
*/
|
*/
|
||||||
'service_handlers' => [
|
'service_types' => [
|
||||||
'nginx' => Nginx::class,
|
'nginx' => 'webserver',
|
||||||
'mysql' => Mysql::class,
|
'mysql' => 'database',
|
||||||
'mariadb' => Mariadb::class,
|
'mariadb' => 'database',
|
||||||
'postgresql' => Postgresql::class,
|
'postgresql' => 'database',
|
||||||
'redis' => Redis::class,
|
'redis' => 'memory_database',
|
||||||
'php' => PHP::class,
|
'php' => 'php',
|
||||||
'ufw' => Ufw::class,
|
'ufw' => 'firewall',
|
||||||
'supervisor' => Supervisor::class,
|
'supervisor' => 'process_manager',
|
||||||
|
'vito-agent' => 'monitoring',
|
||||||
],
|
],
|
||||||
'add_on_services' => [
|
'service_handlers' => [
|
||||||
// add-on services
|
'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\VitoAgent\VitoAgent::class,
|
||||||
],
|
],
|
||||||
'service_units' => [
|
'service_units' => [
|
||||||
'nginx' => [
|
'nginx' => [
|
||||||
'ubuntu_18' => [
|
\App\Enums\OperatingSystem::UBUNTU20 => [
|
||||||
'latest' => 'nginx',
|
'latest' => 'nginx',
|
||||||
],
|
],
|
||||||
'ubuntu_20' => [
|
\App\Enums\OperatingSystem::UBUNTU22 => [
|
||||||
'latest' => 'nginx',
|
|
||||||
],
|
|
||||||
'ubuntu_22' => [
|
|
||||||
'latest' => 'nginx',
|
'latest' => 'nginx',
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
'mysql' => [
|
'mysql' => [
|
||||||
'ubuntu_18' => [
|
\App\Enums\OperatingSystem::UBUNTU20 => [
|
||||||
'5.7' => 'mysql',
|
'5.7' => 'mysql',
|
||||||
'8.0' => 'mysql',
|
'8.0' => 'mysql',
|
||||||
],
|
],
|
||||||
'ubuntu_20' => [
|
\App\Enums\OperatingSystem::UBUNTU22 => [
|
||||||
'5.7' => 'mysql',
|
|
||||||
'8.0' => 'mysql',
|
|
||||||
],
|
|
||||||
'ubuntu_22' => [
|
|
||||||
'5.7' => 'mysql',
|
'5.7' => 'mysql',
|
||||||
'8.0' => 'mysql',
|
'8.0' => 'mysql',
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
'mariadb' => [
|
'mariadb' => [
|
||||||
'ubuntu_18' => [
|
\App\Enums\OperatingSystem::UBUNTU20 => [
|
||||||
'10.3' => 'mariadb',
|
'10.3' => 'mariadb',
|
||||||
'10.4' => 'mariadb',
|
'10.4' => 'mariadb',
|
||||||
],
|
],
|
||||||
'ubuntu_20' => [
|
\App\Enums\OperatingSystem::UBUNTU22 => [
|
||||||
'10.3' => 'mariadb',
|
|
||||||
'10.4' => 'mariadb',
|
|
||||||
],
|
|
||||||
'ubuntu_22' => [
|
|
||||||
'10.3' => 'mariadb',
|
'10.3' => 'mariadb',
|
||||||
'10.4' => 'mariadb',
|
'10.4' => 'mariadb',
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
'postgresql' => [
|
'postgresql' => [
|
||||||
'ubuntu_18' => [
|
\App\Enums\OperatingSystem::UBUNTU20 => [
|
||||||
'12' => 'postgresql',
|
'12' => 'postgresql',
|
||||||
'13' => 'postgresql',
|
'13' => 'postgresql',
|
||||||
'14' => 'postgresql',
|
'14' => 'postgresql',
|
||||||
'15' => 'postgresql',
|
'15' => 'postgresql',
|
||||||
'16' => 'postgresql',
|
'16' => 'postgresql',
|
||||||
],
|
],
|
||||||
'ubuntu_20' => [
|
\App\Enums\OperatingSystem::UBUNTU22 => [
|
||||||
'12' => 'postgresql',
|
|
||||||
'13' => 'postgresql',
|
|
||||||
'14' => 'postgresql',
|
|
||||||
'15' => 'postgresql',
|
|
||||||
'16' => 'postgresql',
|
|
||||||
],
|
|
||||||
'ubuntu_22' => [
|
|
||||||
'12' => 'postgresql',
|
'12' => 'postgresql',
|
||||||
'13' => 'postgresql',
|
'13' => 'postgresql',
|
||||||
'14' => 'postgresql',
|
'14' => 'postgresql',
|
||||||
@ -245,19 +200,7 @@
|
|||||||
],
|
],
|
||||||
],
|
],
|
||||||
'php' => [
|
'php' => [
|
||||||
'ubuntu_18' => [
|
\App\Enums\OperatingSystem::UBUNTU20 => [
|
||||||
'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' => [
|
|
||||||
'5.6' => 'php5.6-fpm',
|
'5.6' => 'php5.6-fpm',
|
||||||
'7.0' => 'php7.0-fpm',
|
'7.0' => 'php7.0-fpm',
|
||||||
'7.1' => 'php7.1-fpm',
|
'7.1' => 'php7.1-fpm',
|
||||||
@ -268,7 +211,7 @@
|
|||||||
'8.1' => 'php8.1-fpm',
|
'8.1' => 'php8.1-fpm',
|
||||||
'8.3' => 'php8.3-fpm',
|
'8.3' => 'php8.3-fpm',
|
||||||
],
|
],
|
||||||
'ubuntu_22' => [
|
\App\Enums\OperatingSystem::UBUNTU22 => [
|
||||||
'5.6' => 'php5.6-fpm',
|
'5.6' => 'php5.6-fpm',
|
||||||
'7.0' => 'php7.0-fpm',
|
'7.0' => 'php7.0-fpm',
|
||||||
'7.1' => 'php7.1-fpm',
|
'7.1' => 'php7.1-fpm',
|
||||||
@ -282,36 +225,35 @@
|
|||||||
],
|
],
|
||||||
],
|
],
|
||||||
'redis' => [
|
'redis' => [
|
||||||
'ubuntu_18' => [
|
\App\Enums\OperatingSystem::UBUNTU20 => [
|
||||||
'latest' => 'redis',
|
'latest' => 'redis',
|
||||||
],
|
],
|
||||||
'ubuntu_20' => [
|
\App\Enums\OperatingSystem::UBUNTU22 => [
|
||||||
'latest' => 'redis',
|
|
||||||
],
|
|
||||||
'ubuntu_22' => [
|
|
||||||
'latest' => 'redis',
|
'latest' => 'redis',
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
'supervisor' => [
|
'supervisor' => [
|
||||||
'ubuntu_18' => [
|
\App\Enums\OperatingSystem::UBUNTU20 => [
|
||||||
'latest' => 'supervisor',
|
'latest' => 'supervisor',
|
||||||
],
|
],
|
||||||
'ubuntu_20' => [
|
\App\Enums\OperatingSystem::UBUNTU22 => [
|
||||||
'latest' => 'supervisor',
|
|
||||||
],
|
|
||||||
'ubuntu_22' => [
|
|
||||||
'latest' => 'supervisor',
|
'latest' => 'supervisor',
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
'ufw' => [
|
'ufw' => [
|
||||||
'ubuntu_18' => [
|
\App\Enums\OperatingSystem::UBUNTU20 => [
|
||||||
'latest' => 'ufw',
|
'latest' => 'ufw',
|
||||||
],
|
],
|
||||||
'ubuntu_20' => [
|
\App\Enums\OperatingSystem::UBUNTU22 => [
|
||||||
'latest' => 'ufw',
|
'latest' => 'ufw',
|
||||||
],
|
],
|
||||||
'ubuntu_22' => [
|
],
|
||||||
'latest' => 'ufw',
|
'vito-agent' => [
|
||||||
|
\App\Enums\OperatingSystem::UBUNTU20 => [
|
||||||
|
'latest' => 'vito-agent',
|
||||||
|
],
|
||||||
|
\App\Enums\OperatingSystem::UBUNTU22 => [
|
||||||
|
'latest' => 'vito-agent',
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
@ -327,11 +269,11 @@
|
|||||||
\App\Enums\SiteType::PHPMYADMIN,
|
\App\Enums\SiteType::PHPMYADMIN,
|
||||||
],
|
],
|
||||||
'site_types_class' => [
|
'site_types_class' => [
|
||||||
\App\Enums\SiteType::PHP => PHPSite::class,
|
\App\Enums\SiteType::PHP => \App\SiteTypes\PHPSite::class,
|
||||||
\App\Enums\SiteType::PHP_BLANK => PHPBlank::class,
|
\App\Enums\SiteType::PHP_BLANK => \App\SiteTypes\PHPBlank::class,
|
||||||
\App\Enums\SiteType::LARAVEL => Laravel::class,
|
\App\Enums\SiteType::LARAVEL => \App\SiteTypes\Laravel::class,
|
||||||
\App\Enums\SiteType::WORDPRESS => Wordpress::class,
|
\App\Enums\SiteType::WORDPRESS => \App\SiteTypes\Wordpress::class,
|
||||||
\App\Enums\SiteType::PHPMYADMIN => PHPMyAdmin::class,
|
\App\Enums\SiteType::PHPMYADMIN => \App\SiteTypes\PHPMyAdmin::class,
|
||||||
],
|
],
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -343,9 +285,9 @@
|
|||||||
'bitbucket',
|
'bitbucket',
|
||||||
],
|
],
|
||||||
'source_control_providers_class' => [
|
'source_control_providers_class' => [
|
||||||
'github' => Github::class,
|
'github' => \App\SourceControlProviders\Github::class,
|
||||||
'gitlab' => Gitlab::class,
|
'gitlab' => \App\SourceControlProviders\Gitlab::class,
|
||||||
'bitbucket' => Bitbucket::class,
|
'bitbucket' => \App\SourceControlProviders\Bitbucket::class,
|
||||||
],
|
],
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -403,26 +345,33 @@
|
|||||||
\App\Enums\NotificationChannel::TELEGRAM,
|
\App\Enums\NotificationChannel::TELEGRAM,
|
||||||
],
|
],
|
||||||
'notification_channels_providers_class' => [
|
'notification_channels_providers_class' => [
|
||||||
\App\Enums\NotificationChannel::SLACK => Slack::class,
|
\App\Enums\NotificationChannel::SLACK => \App\NotificationChannels\Slack::class,
|
||||||
\App\Enums\NotificationChannel::DISCORD => Discord::class,
|
\App\Enums\NotificationChannel::DISCORD => \App\NotificationChannels\Discord::class,
|
||||||
\App\Enums\NotificationChannel::EMAIL => Email::class,
|
\App\Enums\NotificationChannel::EMAIL => \App\NotificationChannels\Email::class,
|
||||||
\App\Enums\NotificationChannel::TELEGRAM => Telegram::class,
|
\App\Enums\NotificationChannel::TELEGRAM => \App\NotificationChannels\Telegram::class,
|
||||||
],
|
],
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* storage providers
|
* storage providers
|
||||||
*/
|
*/
|
||||||
'storage_providers' => [
|
'storage_providers' => [
|
||||||
StorageProvider::DROPBOX,
|
\App\Enums\StorageProvider::DROPBOX,
|
||||||
StorageProvider::FTP,
|
\App\Enums\StorageProvider::FTP,
|
||||||
],
|
],
|
||||||
'storage_providers_class' => [
|
'storage_providers_class' => [
|
||||||
'dropbox' => Dropbox::class,
|
'dropbox' => \App\StorageProviders\Dropbox::class,
|
||||||
'ftp' => FTP::class,
|
'ftp' => \App\StorageProviders\Ftp::class,
|
||||||
],
|
],
|
||||||
|
|
||||||
'ssl_types' => [
|
'ssl_types' => [
|
||||||
\App\Enums\SslType::LETSENCRYPT,
|
\App\Enums\SslType::LETSENCRYPT,
|
||||||
\App\Enums\SslType::CUSTOM,
|
\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,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
|
FROM ubuntu:22.04
|
||||||
|
|
||||||
|
ENV RELEASE_ARG=$RELEASE
|
||||||
|
|
||||||
WORKDIR /var/www/html
|
WORKDIR /var/www/html
|
||||||
|
|
||||||
ENV DEBIAN_FRONTEND noninteractive
|
ENV DEBIAN_FRONTEND noninteractive
|
||||||
|
|
||||||
# upgrade
|
# 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
|
# 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
|
# nginx
|
||||||
RUN apt install -y nginx
|
RUN apt-get install -y nginx
|
||||||
|
|
||||||
# php
|
# php
|
||||||
RUN apt update \
|
RUN apt-get update \
|
||||||
&& apt install -y gnupg gosu curl ca-certificates zip unzip git supervisor libcap2-bin libpng-dev \
|
&& apt-get install -y gnupg gosu curl ca-certificates zip unzip git supervisor libcap2-bin libpng-dev \
|
||||||
python2 dnsutils librsvg2-bin fswatch wget \
|
python2 dnsutils librsvg2-bin fswatch wget \
|
||||||
&& add-apt-repository ppa:ondrej/php -y \
|
&& add-apt-repository ppa:ondrej/php -y \
|
||||||
&& apt update \
|
&& apt-get update \
|
||||||
&& apt install -y php8.2 php8.2-fpm php8.2-mbstring php8.2-mcrypt php8.2-gd php8.2-xml \
|
&& 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
|
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
|
COPY docker/php.ini /etc/php/8.2/cli/conf.d/99-vito.ini
|
||||||
|
|
||||||
@ -29,7 +33,7 @@ RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local
|
|||||||
# app
|
# app
|
||||||
RUN rm -rf /var/www/html
|
RUN rm -rf /var/www/html
|
||||||
RUN git clone -b 1.x https://github.com/vitodeploy/vito.git /var/www/html
|
RUN git clone -b 1.x https://github.com/vitodeploy/vito.git /var/www/html
|
||||||
RUN git checkout $(git tag -l --merged 1.x --sort=-v:refname | head -n 1)
|
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 composer install --no-dev --prefer-dist
|
||||||
RUN chown -R www-data:www-data /var/www/html \
|
RUN chown -R www-data:www-data /var/www/html \
|
||||||
&& chmod -R 755 /var/www/html/storage /var/www/html/bootstrap/cache
|
&& chmod -R 755 /var/www/html/storage /var/www/html/bootstrap/cache
|
||||||
|
239
package-lock.json
generated
239
package-lock.json
generated
@ -8,8 +8,10 @@
|
|||||||
"@tailwindcss/forms": "^0.5.2",
|
"@tailwindcss/forms": "^0.5.2",
|
||||||
"@tailwindcss/typography": "^0.5.9",
|
"@tailwindcss/typography": "^0.5.9",
|
||||||
"alpinejs": "^3.4.2",
|
"alpinejs": "^3.4.2",
|
||||||
|
"apexcharts": "^3.44.2",
|
||||||
"autoprefixer": "^10.4.2",
|
"autoprefixer": "^10.4.2",
|
||||||
"flowbite": "^2.3.0",
|
"flowbite": "^2.3.0",
|
||||||
|
"flowbite-datepicker": "^1.2.6",
|
||||||
"htmx.org": "^1.9.10",
|
"htmx.org": "^1.9.10",
|
||||||
"laravel-echo": "^1.15.0",
|
"laravel-echo": "^1.15.0",
|
||||||
"laravel-vite-plugin": "^0.7.2",
|
"laravel-vite-plugin": "^0.7.2",
|
||||||
@ -20,7 +22,7 @@
|
|||||||
"tailwindcss": "^3.1.0",
|
"tailwindcss": "^3.1.0",
|
||||||
"tippy.js": "^6.3.7",
|
"tippy.js": "^6.3.7",
|
||||||
"toastr": "^2.1.4",
|
"toastr": "^2.1.4",
|
||||||
"vite": "^4.5.2"
|
"vite": "^4.5.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/android-arm": {
|
"node_modules/@esbuild/android-arm": {
|
||||||
@ -529,6 +531,12 @@
|
|||||||
"integrity": "sha512-oJ4F3TnvpXaQwZJNF3ZK+kLPHKarDmJjJ6jyzVNDKH9md1dptjC7lWR//jrGuLdek/U6iltWxqAnYOu8gCiOvA==",
|
"integrity": "sha512-oJ4F3TnvpXaQwZJNF3ZK+kLPHKarDmJjJ6jyzVNDKH9md1dptjC7lWR//jrGuLdek/U6iltWxqAnYOu8gCiOvA==",
|
||||||
"dev": true
|
"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": {
|
"node_modules/alpinejs": {
|
||||||
"version": "3.12.0",
|
"version": "3.12.0",
|
||||||
"resolved": "https://registry.npmjs.org/alpinejs/-/alpinejs-3.12.0.tgz",
|
"resolved": "https://registry.npmjs.org/alpinejs/-/alpinejs-3.12.0.tgz",
|
||||||
@ -557,6 +565,21 @@
|
|||||||
"node": ">= 8"
|
"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": {
|
"node_modules/arg": {
|
||||||
"version": "5.0.2",
|
"version": "5.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz",
|
||||||
@ -885,6 +908,15 @@
|
|||||||
"mini-svg-data-uri": "^1.4.3"
|
"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": {
|
"node_modules/fraction.js": {
|
||||||
"version": "4.2.0",
|
"version": "4.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz",
|
||||||
@ -1680,6 +1712,97 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"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": {
|
"node_modules/tailwindcss": {
|
||||||
"version": "3.3.1",
|
"version": "3.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.3.1.tgz",
|
||||||
@ -1812,9 +1935,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/vite": {
|
"node_modules/vite": {
|
||||||
"version": "4.5.2",
|
"version": "4.5.3",
|
||||||
"resolved": "https://registry.npmjs.org/vite/-/vite-4.5.2.tgz",
|
"resolved": "https://registry.npmjs.org/vite/-/vite-4.5.3.tgz",
|
||||||
"integrity": "sha512-tBCZBNSBbHQkaGyhGCDUGqeo2ph8Fstyp6FMSvTtsXeZSPpSMGlviAOav2hxVTqFcx8Hj/twtWKsMJXNY0xI8w==",
|
"integrity": "sha512-kQL23kMeX92v3ph7IauVkXkikdDRsYMGTVl5KY2E9OY4ONLvkHf04MDTbnfo6NKxZiDLWzVpP5oTa8hQD8U3dg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"esbuild": "^0.18.10",
|
"esbuild": "^0.18.10",
|
||||||
@ -2177,6 +2300,12 @@
|
|||||||
"integrity": "sha512-oJ4F3TnvpXaQwZJNF3ZK+kLPHKarDmJjJ6jyzVNDKH9md1dptjC7lWR//jrGuLdek/U6iltWxqAnYOu8gCiOvA==",
|
"integrity": "sha512-oJ4F3TnvpXaQwZJNF3ZK+kLPHKarDmJjJ6jyzVNDKH9md1dptjC7lWR//jrGuLdek/U6iltWxqAnYOu8gCiOvA==",
|
||||||
"dev": true
|
"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": {
|
"alpinejs": {
|
||||||
"version": "3.12.0",
|
"version": "3.12.0",
|
||||||
"resolved": "https://registry.npmjs.org/alpinejs/-/alpinejs-3.12.0.tgz",
|
"resolved": "https://registry.npmjs.org/alpinejs/-/alpinejs-3.12.0.tgz",
|
||||||
@ -2202,6 +2331,21 @@
|
|||||||
"picomatch": "^2.0.4"
|
"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": {
|
"arg": {
|
||||||
"version": "5.0.2",
|
"version": "5.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz",
|
||||||
@ -2434,6 +2578,15 @@
|
|||||||
"mini-svg-data-uri": "^1.4.3"
|
"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": {
|
"fraction.js": {
|
||||||
"version": "4.2.0",
|
"version": "4.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz",
|
||||||
@ -2911,6 +3064,78 @@
|
|||||||
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
|
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
|
||||||
"dev": true
|
"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": {
|
"tailwindcss": {
|
||||||
"version": "3.3.1",
|
"version": "3.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.3.1.tgz",
|
||||||
@ -3011,9 +3236,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"vite": {
|
"vite": {
|
||||||
"version": "4.5.2",
|
"version": "4.5.3",
|
||||||
"resolved": "https://registry.npmjs.org/vite/-/vite-4.5.2.tgz",
|
"resolved": "https://registry.npmjs.org/vite/-/vite-4.5.3.tgz",
|
||||||
"integrity": "sha512-tBCZBNSBbHQkaGyhGCDUGqeo2ph8Fstyp6FMSvTtsXeZSPpSMGlviAOav2hxVTqFcx8Hj/twtWKsMJXNY0xI8w==",
|
"integrity": "sha512-kQL23kMeX92v3ph7IauVkXkikdDRsYMGTVl5KY2E9OY4ONLvkHf04MDTbnfo6NKxZiDLWzVpP5oTa8hQD8U3dg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"esbuild": "^0.18.10",
|
"esbuild": "^0.18.10",
|
||||||
|
@ -22,6 +22,8 @@
|
|||||||
"tailwindcss": "^3.1.0",
|
"tailwindcss": "^3.1.0",
|
||||||
"tippy.js": "^6.3.7",
|
"tippy.js": "^6.3.7",
|
||||||
"toastr": "^2.1.4",
|
"toastr": "^2.1.4",
|
||||||
"vite": "^4.5.2"
|
"vite": "^4.5.3",
|
||||||
|
"apexcharts": "^3.44.2",
|
||||||
|
"flowbite-datepicker": "^1.2.6"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
1
public/build/assets/app-53e4d707.css
Normal file
1
public/build/assets/app-53e4d707.css
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
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
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"resources/css/app.css": {
|
"resources/css/app.css": {
|
||||||
"file": "assets/app-e3775b0a.css",
|
"file": "assets/app-53e4d707.css",
|
||||||
"isEntry": true,
|
"isEntry": true,
|
||||||
"src": "resources/css/app.css"
|
"src": "resources/css/app.css"
|
||||||
},
|
},
|
||||||
@ -12,7 +12,7 @@
|
|||||||
"css": [
|
"css": [
|
||||||
"assets/app-a1ae07b3.css"
|
"assets/app-a1ae07b3.css"
|
||||||
],
|
],
|
||||||
"file": "assets/app-5f99a92f.js",
|
"file": "assets/app-66009dff.js",
|
||||||
"isEntry": true,
|
"isEntry": true,
|
||||||
"src": "resources/js/app.js"
|
"src": "resources/js/app.js"
|
||||||
}
|
}
|
||||||
|
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';
|
||||||
|
import 'flowbite/dist/datepicker.js';
|
||||||
|
|
||||||
import Alpine from 'alpinejs';
|
import Alpine from 'alpinejs';
|
||||||
window.Alpine = Alpine;
|
window.Alpine = Alpine;
|
||||||
Alpine.start();
|
Alpine.start();
|
||||||
|
|
||||||
|
import ApexCharts from 'apexcharts';
|
||||||
|
window.ApexCharts = ApexCharts;
|
||||||
|
|
||||||
import htmx from "htmx.org";
|
import htmx from "htmx.org";
|
||||||
window.htmx = htmx;
|
window.htmx = htmx;
|
||||||
window.htmx.defineExtension('disable-element', {
|
window.htmx.defineExtension('disable-element', {
|
||||||
@ -23,12 +27,10 @@ window.htmx.defineExtension('disable-element', {
|
|||||||
});
|
});
|
||||||
document.body.addEventListener('htmx:configRequest', (event) => {
|
document.body.addEventListener('htmx:configRequest', (event) => {
|
||||||
event.detail.headers['X-CSRF-TOKEN'] = document.head.querySelector('meta[name="csrf-token"]').content;
|
event.detail.headers['X-CSRF-TOKEN'] = document.head.querySelector('meta[name="csrf-token"]').content;
|
||||||
if (window.getSelection) { window.getSelection().removeAllRanges(); }
|
// if (window.getSelection) { window.getSelection().removeAllRanges(); }
|
||||||
else if (document.selection) { document.selection.empty(); }
|
// else if (document.selection) { document.selection.empty(); }
|
||||||
});
|
});
|
||||||
let activeElement = null;
|
|
||||||
document.body.addEventListener('htmx:beforeRequest', (event) => {
|
document.body.addEventListener('htmx:beforeRequest', (event) => {
|
||||||
activeElement = document.activeElement;
|
|
||||||
let targetElements = event.target.querySelectorAll('[hx-disable]');
|
let targetElements = event.target.querySelectorAll('[hx-disable]');
|
||||||
for (let i = 0; i < targetElements.length; i++) {
|
for (let i = 0; i < targetElements.length; i++) {
|
||||||
targetElements[i].disabled = true;
|
targetElements[i].disabled = true;
|
||||||
@ -46,11 +48,6 @@ document.body.addEventListener('htmx:afterSwap', (event) => {
|
|||||||
return reference.getAttribute('data-tooltip');
|
return reference.getAttribute('data-tooltip');
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
if (activeElement) {
|
|
||||||
activeElement.blur();
|
|
||||||
activeElement.focus();
|
|
||||||
activeElement = null;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
import toastr from 'toastr';
|
import toastr from 'toastr';
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
<x-slot name="trigger">
|
<x-slot name="trigger">
|
||||||
<x-secondary-button>
|
<x-secondary-button>
|
||||||
{{ __("Auto Deployment") }}
|
{{ __("Auto Deployment") }}
|
||||||
|
<x-heroicon name="o-chevron-down" class="ml-1 h-4 w-4" />
|
||||||
</x-secondary-button>
|
</x-secondary-button>
|
||||||
</x-slot>
|
</x-slot>
|
||||||
<x-slot name="content">
|
<x-slot name="content">
|
||||||
|
@ -12,11 +12,13 @@ class="p-6"
|
|||||||
{{ __("Deployment Script") }}
|
{{ __("Deployment Script") }}
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
|
<div class="mt-6">A bash script that will be executed when you run the deployment process.</div>
|
||||||
|
|
||||||
<div class="mt-6">
|
<div class="mt-6">
|
||||||
<x-input-label for="script" :value="__('Script')" />
|
<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) }}
|
{{ old("script", $site->deploymentScript?->content) }}
|
||||||
</x-code-editor>
|
</x-textarea>
|
||||||
@error("script")
|
@error("script")
|
||||||
<x-input-error class="mt-2" :messages="$message" />
|
<x-input-error class="mt-2" :messages="$message" />
|
||||||
@enderror
|
@enderror
|
||||||
|
@ -21,9 +21,9 @@ class="mt-6"
|
|||||||
>
|
>
|
||||||
<x-input-label for="env" :value="__('.env')" />
|
<x-input-label for="env" :value="__('.env')" />
|
||||||
<div id="env-content">
|
<div id="env-content">
|
||||||
<x-code-editor id="env" name="env" rows="10" class="mt-1 block w-full">
|
<x-textarea id="env" name="env" rows="10" class="mt-1 block min-h-[400px] w-full">
|
||||||
{{ old("env", session()->get("env") ?? "Loading...") }}
|
{{ old("env", session()->get("env") ?? "Loading...") }}
|
||||||
</x-code-editor>
|
</x-textarea>
|
||||||
</div>
|
</div>
|
||||||
@error("env")
|
@error("env")
|
||||||
<x-input-error class="mt-2" :messages="$message" />
|
<x-input-error class="mt-2" :messages="$message" />
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
<x-slot name="trigger">
|
<x-slot name="trigger">
|
||||||
<x-secondary-button>
|
<x-secondary-button>
|
||||||
{{ __("Manage") }}
|
{{ __("Manage") }}
|
||||||
|
<x-heroicon name="o-chevron-down" class="ml-1 h-4 w-4" />
|
||||||
</x-secondary-button>
|
</x-secondary-button>
|
||||||
</x-slot>
|
</x-slot>
|
||||||
<x-slot name="content">
|
<x-slot name="content">
|
||||||
|
104
resources/views/components/chart.blade.php
Normal file
104
resources/views/components/chart.blade.php
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
@props([
|
||||||
|
"id",
|
||||||
|
"type",
|
||||||
|
"title",
|
||||||
|
"color",
|
||||||
|
"sets",
|
||||||
|
"categories",
|
||||||
|
"toolbar" => false,
|
||||||
|
"formatter" => null,
|
||||||
|
])
|
||||||
|
<x-simple-card {{ $attributes }}>
|
||||||
|
<div class="relative">
|
||||||
|
<div class="absolute left-4 top-4">{{ $title }}</div>
|
||||||
|
</div>
|
||||||
|
<div id="{{ $id }}" class="pt-4"></div>
|
||||||
|
<script>
|
||||||
|
window.addEventListener('load', function () {
|
||||||
|
let options = {
|
||||||
|
series: [
|
||||||
|
@foreach ($sets as $set)
|
||||||
|
{
|
||||||
|
name: '{{ $set["name"] }}',
|
||||||
|
data: @json($set["data"]),
|
||||||
|
color: '{{ $set["color"] }}'
|
||||||
|
},
|
||||||
|
@endforeach
|
||||||
|
],
|
||||||
|
chart: {
|
||||||
|
height: '100%',
|
||||||
|
maxWidth: '100%',
|
||||||
|
type: '{{ $type }}',
|
||||||
|
fontFamily: 'Inter, sans-serif',
|
||||||
|
dropShadow: {
|
||||||
|
enabled: false
|
||||||
|
},
|
||||||
|
toolbar: {
|
||||||
|
show: @js($toolbar)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
enabled: true,
|
||||||
|
x: {
|
||||||
|
show: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
show: true
|
||||||
|
},
|
||||||
|
@if ($type == 'area')
|
||||||
|
fill: {
|
||||||
|
type: 'gradient',
|
||||||
|
gradient: {
|
||||||
|
opacityFrom: 0.55,
|
||||||
|
opacityTo: 0,
|
||||||
|
shade: '{{ $color }}',
|
||||||
|
gradientToColors: ['{{ $color }}']
|
||||||
|
}
|
||||||
|
},
|
||||||
|
@endif
|
||||||
|
dataLabels: {
|
||||||
|
enabled: false
|
||||||
|
},
|
||||||
|
stroke: {
|
||||||
|
width: 2,
|
||||||
|
curve: 'smooth'
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
show: false,
|
||||||
|
strokeDashArray: 4,
|
||||||
|
padding: {
|
||||||
|
left: 2,
|
||||||
|
right: 2,
|
||||||
|
top: 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
xaxis: {
|
||||||
|
categories: @json($categories),
|
||||||
|
labels: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
axisBorder: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
axisTicks: {
|
||||||
|
show: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
yaxis: {
|
||||||
|
show: false,
|
||||||
|
labels: {
|
||||||
|
@if ($formatter)
|
||||||
|
formatter: {{ $formatter }},
|
||||||
|
@endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (document.getElementById('{{ $id }}') && typeof ApexCharts !== 'undefined') {
|
||||||
|
const chart = new ApexCharts(document.getElementById('{{ $id }}'), options);
|
||||||
|
chart.render();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</x-simple-card>
|
@ -1,45 +0,0 @@
|
|||||||
@props([
|
|
||||||
"id",
|
|
||||||
"name",
|
|
||||||
"disabled" => false,
|
|
||||||
"lang" => "text",
|
|
||||||
])
|
|
||||||
|
|
||||||
<div
|
|
||||||
x-data="{
|
|
||||||
editorId: @js($id),
|
|
||||||
disabled: @js($disabled),
|
|
||||||
lang: @js($lang),
|
|
||||||
init() {
|
|
||||||
document.body.addEventListener('htmx:afterSettle', (event) => {
|
|
||||||
let editor = null
|
|
||||||
let theme =
|
|
||||||
document.documentElement.className === 'dark'
|
|
||||||
? 'one-dark'
|
|
||||||
: 'github'
|
|
||||||
editor = window.ace.edit(this.editorId, {})
|
|
||||||
let contentElement = document.getElementById(
|
|
||||||
`text-${this.editorId}`,
|
|
||||||
)
|
|
||||||
editor.setValue(contentElement.innerText, 1)
|
|
||||||
if (this.disabled) {
|
|
||||||
editor.setReadOnly(true)
|
|
||||||
}
|
|
||||||
editor.getSession().setMode(`ace/mode/${this.lang}`)
|
|
||||||
editor.setTheme(`ace/theme/${theme}`)
|
|
||||||
editor.setFontSize('15px')
|
|
||||||
editor.setShowPrintMargin(false)
|
|
||||||
editor.on('change', () => {
|
|
||||||
contentElement.innerHTML = editor.getValue()
|
|
||||||
})
|
|
||||||
document.body.addEventListener('color-scheme-changed', (event) => {
|
|
||||||
theme = event.detail.theme === 'dark' ? 'one-dark' : 'github'
|
|
||||||
editor.setTheme(`ace/theme/${theme}`)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
},
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
<div id="{{ $id }}" class="min-h-[400px] w-full rounded-md border border-gray-200 dark:border-gray-700"></div>
|
|
||||||
<textarea id="text-{{ $id }}" name="{{ $name }}" class="hidden">{{ $slot }}</textarea>
|
|
||||||
</div>
|
|
14
resources/views/components/heroicons/o-calendar.blade.php
Normal file
14
resources/views/components/heroicons/o-calendar.blade.php
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
stroke-width="1.5"
|
||||||
|
stroke="currentColor"
|
||||||
|
{{ $attributes }}
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
d="M6.75 3v2.25M17.25 3v2.25M3 18.75V7.5a2.25 2.25 0 0 1 2.25-2.25h13.5A2.25 2.25 0 0 1 21 7.5v11.25m-18 0A2.25 2.25 0 0 0 5.25 21h13.5A2.25 2.25 0 0 0 21 18.75m-18 0v-7.5A2.25 2.25 0 0 1 5.25 9h13.5A2.25 2.25 0 0 1 21 11.25v7.5"
|
||||||
|
/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 482 B |
14
resources/views/components/heroicons/o-chart-bar.blade.php
Normal file
14
resources/views/components/heroicons/o-chart-bar.blade.php
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
stroke-width="1.5"
|
||||||
|
stroke="currentColor"
|
||||||
|
{{ $attributes }}
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
d="M3 13.125C3 12.504 3.504 12 4.125 12h2.25c.621 0 1.125.504 1.125 1.125v6.75C7.5 20.496 6.996 21 6.375 21h-2.25A1.125 1.125 0 0 1 3 19.875v-6.75ZM9.75 8.625c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125v11.25c0 .621-.504 1.125-1.125 1.125h-2.25a1.125 1.125 0 0 1-1.125-1.125V8.625ZM16.5 4.125c0-.621.504-1.125 1.125-1.125h2.25C20.496 3 21 3.504 21 4.125v15.75c0 .621-.504 1.125-1.125 1.125h-2.25a1.125 1.125 0 0 1-1.125-1.125V4.125Z"
|
||||||
|
/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 705 B |
@ -10,7 +10,7 @@
|
|||||||
runUrl: '{{ route("servers.console.run", ["server" => $server]) }}',
|
runUrl: '{{ route("servers.console.run", ["server" => $server]) }}',
|
||||||
async run() {
|
async run() {
|
||||||
this.running = true
|
this.running = true
|
||||||
this.output = 'Running...\n'
|
this.output = this.command + '\n'
|
||||||
const fetchOptions = {
|
const fetchOptions = {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
@ -23,6 +23,7 @@
|
|||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.command = ''
|
||||||
const response = await fetch(this.runUrl, fetchOptions)
|
const response = await fetch(this.runUrl, fetchOptions)
|
||||||
const reader = response.body.getReader()
|
const reader = response.body.getReader()
|
||||||
const decoder = new TextDecoder('utf-8')
|
const decoder = new TextDecoder('utf-8')
|
||||||
|
@ -21,8 +21,6 @@
|
|||||||
<link rel="preconnect" href="https://fonts.bunny.net" />
|
<link rel="preconnect" href="https://fonts.bunny.net" />
|
||||||
<link href="https://fonts.bunny.net/css?family=figtree:400,500,600&display=swap" rel="stylesheet" />
|
<link href="https://fonts.bunny.net/css?family=figtree:400,500,600&display=swap" rel="stylesheet" />
|
||||||
|
|
||||||
<script src="{{ asset("static/libs/ace/ace.js") }}"></script>
|
|
||||||
|
|
||||||
@include("layouts.partials.favicon")
|
@include("layouts.partials.favicon")
|
||||||
|
|
||||||
<!-- Scripts -->
|
<!-- Scripts -->
|
||||||
|
@ -132,7 +132,10 @@
|
|||||||
}"
|
}"
|
||||||
@open-search.window="openSearch"
|
@open-search.window="openSearch"
|
||||||
>
|
>
|
||||||
<div x-show="open" class="absolute bottom-0 left-0 right-0 top-0 flex max-w-full items-start justify-center">
|
<div
|
||||||
|
x-show="open"
|
||||||
|
class="fixed bottom-0 left-0 right-0 top-0 z-[2000] flex max-w-full items-start justify-center"
|
||||||
|
>
|
||||||
<div
|
<div
|
||||||
x-on:click="close"
|
x-on:click="close"
|
||||||
class="fixed inset-0 bottom-0 left-0 right-0 top-0 z-[1000] items-center bg-gray-500 opacity-75 dark:bg-gray-900"
|
class="fixed inset-0 bottom-0 left-0 right-0 top-0 z-[1000] items-center bg-gray-500 opacity-75 dark:bg-gray-900"
|
||||||
|
@ -37,7 +37,7 @@ class="fixed left-0 top-0 z-40 h-screen w-64 -translate-x-full border-r border-g
|
|||||||
</li>
|
</li>
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
@if ($server->database())
|
@if ($server->database()?->status == \App\Enums\ServiceStatus::READY)
|
||||||
<li>
|
<li>
|
||||||
<x-sidebar-link
|
<x-sidebar-link
|
||||||
:href="route('servers.databases', ['server' => $server])"
|
:href="route('servers.databases', ['server' => $server])"
|
||||||
@ -52,19 +52,17 @@ class="fixed left-0 top-0 z-40 h-screen w-64 -translate-x-full border-r border-g
|
|||||||
</li>
|
</li>
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
@if ($server->php())
|
<li>
|
||||||
<li>
|
<x-sidebar-link
|
||||||
<x-sidebar-link
|
:href="route('servers.php', ['server' => $server])"
|
||||||
:href="route('servers.php', ['server' => $server])"
|
:active="request()->routeIs('servers.php')"
|
||||||
:active="request()->routeIs('servers.php')"
|
>
|
||||||
>
|
<x-heroicon name="o-code-bracket" class="h-6 w-6" />
|
||||||
<x-heroicon name="o-code-bracket" class="h-6 w-6" />
|
<span class="ml-2">
|
||||||
<span class="ml-2">
|
{{ __("PHP") }}
|
||||||
{{ __("PHP") }}
|
</span>
|
||||||
</span>
|
</x-sidebar-link>
|
||||||
</x-sidebar-link>
|
</li>
|
||||||
</li>
|
|
||||||
@endif
|
|
||||||
|
|
||||||
@if ($server->firewall())
|
@if ($server->firewall())
|
||||||
<li>
|
<li>
|
||||||
@ -116,6 +114,20 @@ class="fixed left-0 top-0 z-40 h-screen w-64 -translate-x-full border-r border-g
|
|||||||
</x-sidebar-link>
|
</x-sidebar-link>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
@if ($server->monitoring())
|
||||||
|
<li>
|
||||||
|
<x-sidebar-link
|
||||||
|
:href="route('servers.metrics', ['server' => $server])"
|
||||||
|
:active="request()->routeIs('servers.metrics')"
|
||||||
|
>
|
||||||
|
<x-heroicon name="o-chart-bar" class="h-6 w-6" />
|
||||||
|
<span class="ml-2">
|
||||||
|
{{ __("Metrics") }}
|
||||||
|
</span>
|
||||||
|
</x-sidebar-link>
|
||||||
|
</li>
|
||||||
|
@endif
|
||||||
|
|
||||||
<li>
|
<li>
|
||||||
<x-sidebar-link
|
<x-sidebar-link
|
||||||
:href="route('servers.console', ['server' => $server])"
|
:href="route('servers.console', ['server' => $server])"
|
||||||
|
@ -28,7 +28,7 @@ class="mr-1"
|
|||||||
</x-tab-item>
|
</x-tab-item>
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
@if ($site->hasFeature(SiteFeature::QUEUES))
|
@if ($site->hasFeature(SiteFeature::QUEUES) && $site->server->processManager()?->status == \App\Enums\ServiceStatus::READY)
|
||||||
<x-tab-item
|
<x-tab-item
|
||||||
class="mr-1"
|
class="mr-1"
|
||||||
:href="route('servers.sites.queues', ['server' => $site->server, 'site' => $site])"
|
:href="route('servers.sites.queues', ['server' => $site->server, 'site' => $site])"
|
||||||
|
106
resources/views/metrics/index.blade.php
Normal file
106
resources/views/metrics/index.blade.php
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
<x-server-layout :server="$server">
|
||||||
|
<x-slot name="pageTitle">{{ $server->name }} - Metrics</x-slot>
|
||||||
|
|
||||||
|
<div class="space-y-4">
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
@include("metrics.partials.filter")
|
||||||
|
@include("metrics.partials.data-retention")
|
||||||
|
</div>
|
||||||
|
@php
|
||||||
|
$cpuSets = [
|
||||||
|
"name" => "CPU Load",
|
||||||
|
"data" => $data["metrics"]->pluck("load")->toArray(),
|
||||||
|
"color" => "#ff9900",
|
||||||
|
];
|
||||||
|
$memorySets = [
|
||||||
|
"name" => "Memory Usage",
|
||||||
|
"data" => $data["metrics"]->pluck("memory_used")->toArray(),
|
||||||
|
"color" => "#3366cc",
|
||||||
|
];
|
||||||
|
$diskSets = [
|
||||||
|
"name" => "Disk Usage",
|
||||||
|
"data" => $data["metrics"]->pluck("disk_used")->toArray(),
|
||||||
|
"color" => "#109618",
|
||||||
|
];
|
||||||
|
@endphp
|
||||||
|
|
||||||
|
<div class="grid grid-cols-1 gap-4 lg:grid-cols-3">
|
||||||
|
<x-chart
|
||||||
|
id="cpu-load"
|
||||||
|
type="area"
|
||||||
|
title="CPU Load"
|
||||||
|
:sets="[$cpuSets]"
|
||||||
|
:categories="$data['metrics']->pluck('date')->toArray()"
|
||||||
|
color="#ff9900"
|
||||||
|
class="h-[200px] !p-0"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<x-chart
|
||||||
|
id="memory-usage"
|
||||||
|
type="area"
|
||||||
|
title="Memory"
|
||||||
|
:sets="[$memorySets]"
|
||||||
|
:categories="$data['metrics']->pluck('date')->toArray()"
|
||||||
|
color="#3366cc"
|
||||||
|
formatter="function (value) { return (value / 1000).toFixed(2) + ` MB`; }"
|
||||||
|
class="h-[200px] !p-0"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<x-chart
|
||||||
|
id="disk-usage"
|
||||||
|
type="area"
|
||||||
|
title="Disk"
|
||||||
|
:sets="[$diskSets]"
|
||||||
|
:categories="$data['metrics']->pluck('date')->toArray()"
|
||||||
|
formatter="function (value) { return value + ` MB`; }"
|
||||||
|
color="#109618"
|
||||||
|
class="h-[200px] !p-0"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||||
|
<div class="grid grid-cols-1 gap-4">
|
||||||
|
<x-simple-card class="grid grid-cols-1 gap-4 lg:grid-cols-2">
|
||||||
|
<span class="text-center lg:text-left">Total Memory</span>
|
||||||
|
<div class="text-center text-xl font-bold text-gray-600 dark:text-gray-400 lg:text-right">
|
||||||
|
{{ $lastMetric ? $lastMetric->memory_total : "-" }} MB
|
||||||
|
</div>
|
||||||
|
</x-simple-card>
|
||||||
|
<x-simple-card class="grid grid-cols-1 gap-4 lg:grid-cols-2">
|
||||||
|
<span class="text-center lg:text-left">Used Memory</span>
|
||||||
|
<div class="text-center text-xl font-bold text-gray-600 dark:text-gray-400 lg:text-right">
|
||||||
|
{{ $lastMetric ? $lastMetric->memory_used : "-" }} MB
|
||||||
|
</div>
|
||||||
|
</x-simple-card>
|
||||||
|
<x-simple-card class="grid grid-cols-1 gap-4 lg:grid-cols-2">
|
||||||
|
<span class="text-center lg:text-left">Free Memory</span>
|
||||||
|
<div class="text-center text-xl font-bold text-gray-600 dark:text-gray-400 lg:text-right">
|
||||||
|
{{ $lastMetric ? $lastMetric->memory_free : "-" }} MB
|
||||||
|
</div>
|
||||||
|
</x-simple-card>
|
||||||
|
</div>
|
||||||
|
<div class="grid grid-cols-1 gap-4">
|
||||||
|
<x-simple-card class="grid grid-cols-1 gap-4 lg:grid-cols-2">
|
||||||
|
<span class="text-center lg:text-left">Total Space</span>
|
||||||
|
<div class="text-center text-xl font-bold text-gray-600 dark:text-gray-400 lg:text-right">
|
||||||
|
{{ $lastMetric ? $lastMetric->disk_total : "-" }} MB
|
||||||
|
</div>
|
||||||
|
</x-simple-card>
|
||||||
|
<x-simple-card class="grid grid-cols-1 gap-4 lg:grid-cols-2">
|
||||||
|
<span class="text-center lg:text-left">Used Space</span>
|
||||||
|
<div class="text-center text-xl font-bold text-gray-600 dark:text-gray-400 lg:text-right">
|
||||||
|
{{ $lastMetric ? $lastMetric->disk_used : "-" }} MB
|
||||||
|
</div>
|
||||||
|
</x-simple-card>
|
||||||
|
<x-simple-card class="grid grid-cols-1 gap-4 lg:grid-cols-2">
|
||||||
|
<span class="text-center lg:text-left">Free Space</span>
|
||||||
|
<div class="text-center text-xl font-bold text-gray-600 dark:text-gray-400 lg:text-right">
|
||||||
|
{{ $lastMetric ? $lastMetric->disk_free : "-" }} MB
|
||||||
|
</div>
|
||||||
|
</x-simple-card>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@stack("modals")
|
||||||
|
</x-server-layout>
|
44
resources/views/metrics/partials/data-retention.blade.php
Normal file
44
resources/views/metrics/partials/data-retention.blade.php
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
<x-secondary-button
|
||||||
|
class="ml-2 hidden h-[42px] items-center lg:flex"
|
||||||
|
x-on:click="$dispatch('open-modal', 'metric-settings')"
|
||||||
|
>
|
||||||
|
<x-heroicon name="o-trash" class="mr-1 h-5 w-5" />
|
||||||
|
Data Retention
|
||||||
|
</x-secondary-button>
|
||||||
|
@push("modals")
|
||||||
|
<x-modal name="metric-settings">
|
||||||
|
<form
|
||||||
|
id="metric-settings-form"
|
||||||
|
hx-post="{{ route("servers.metrics.settings", ["server" => $server]) }}"
|
||||||
|
hx-swap="outerHTML"
|
||||||
|
hx-select="#metric-settings-form"
|
||||||
|
class="p-6"
|
||||||
|
>
|
||||||
|
@csrf
|
||||||
|
|
||||||
|
<h2 class="text-lg font-medium text-gray-900 dark:text-gray-100">Data Retention</h2>
|
||||||
|
|
||||||
|
<div class="mt-6">
|
||||||
|
<x-input-label for="data_retention" value="Delete metrics older than" />
|
||||||
|
<x-select-input id="data_retention" name="data_retention" class="mt-1 w-full">
|
||||||
|
@foreach (config("core.metrics_data_retention") as $item)
|
||||||
|
<option value="{{ $item }}">{{ $item }} Days</option>
|
||||||
|
@endforeach
|
||||||
|
</x-select-input>
|
||||||
|
@error("data_retention")
|
||||||
|
<x-input-error class="mt-2" :messages="$message" />
|
||||||
|
@enderror
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-6 flex justify-end">
|
||||||
|
<x-secondary-button type="button" x-on:click="$dispatch('close')">
|
||||||
|
{{ __("Cancel") }}
|
||||||
|
</x-secondary-button>
|
||||||
|
|
||||||
|
<x-primary-button id="btn-metric-settings" hx-disable class="ml-3">
|
||||||
|
{{ __("Save") }}
|
||||||
|
</x-primary-button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</x-modal>
|
||||||
|
@endpush
|
76
resources/views/metrics/partials/filter.blade.php
Normal file
76
resources/views/metrics/partials/filter.blade.php
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
<div class="flex items-center" x-data="{ period: '{{ request()->query("period") ?? "10m" }}' }">
|
||||||
|
<x-dropdown align="left" class="ml-2">
|
||||||
|
<x-slot name="trigger">
|
||||||
|
<div data-tooltip="Change Period">
|
||||||
|
<div
|
||||||
|
class="flex w-full items-center rounded-md border border-gray-300 bg-white p-2.5 pr-10 text-sm capitalize text-gray-900 focus:border-primary-500 focus:ring-primary-500 dark:border-gray-600 dark:bg-gray-700 dark:text-white dark:placeholder-gray-400 dark:focus:border-primary-500 dark:focus:ring-primary-500"
|
||||||
|
>
|
||||||
|
<div x-text="period"></div>
|
||||||
|
</div>
|
||||||
|
<button type="button" class="absolute inset-y-0 right-0 flex items-center pr-2">
|
||||||
|
<x-heroicon name="o-chevron-down" class="h-4 w-4 text-gray-400" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</x-slot>
|
||||||
|
<x-slot name="content">
|
||||||
|
<x-dropdown-link :href="route('servers.metrics', ['server' => $server, 'period' => '10m'])">
|
||||||
|
10 Minutes
|
||||||
|
</x-dropdown-link>
|
||||||
|
<x-dropdown-link :href="route('servers.metrics', ['server' => $server, 'period' => '30m'])">
|
||||||
|
30 Minutes
|
||||||
|
</x-dropdown-link>
|
||||||
|
<x-dropdown-link :href="route('servers.metrics', ['server' => $server, 'period' => '1h'])">
|
||||||
|
1 Hour
|
||||||
|
</x-dropdown-link>
|
||||||
|
<x-dropdown-link :href="route('servers.metrics', ['server' => $server, 'period' => '12h'])">
|
||||||
|
12 Hours
|
||||||
|
</x-dropdown-link>
|
||||||
|
<x-dropdown-link :href="route('servers.metrics', ['server' => $server, 'period' => '1d'])">
|
||||||
|
1 Day
|
||||||
|
</x-dropdown-link>
|
||||||
|
<x-dropdown-link :href="route('servers.metrics', ['server' => $server, 'period' => '7d'])">
|
||||||
|
7 Days
|
||||||
|
</x-dropdown-link>
|
||||||
|
<x-dropdown-link x-on:click="period = 'custom'" class="cursor-pointer">Custom</x-dropdown-link>
|
||||||
|
</x-slot>
|
||||||
|
</x-dropdown>
|
||||||
|
|
||||||
|
<form
|
||||||
|
x-show="period === 'custom'"
|
||||||
|
class="flex items-center"
|
||||||
|
action="{{ route("servers.metrics", ["server" => $server, "period" => "custom"]) }}"
|
||||||
|
>
|
||||||
|
<input type="hidden" name="period" value="custom" />
|
||||||
|
<div date-rangepicker datepicker-format="yyyy-mm-dd" class="ml-2 flex items-center">
|
||||||
|
<div class="relative">
|
||||||
|
<div class="pointer-events-none absolute inset-y-0 start-0 flex items-center ps-3">
|
||||||
|
<x-heroicon name="o-calendar" class="h-4 w-4 text-gray-500 dark:text-gray-400" />
|
||||||
|
</div>
|
||||||
|
<input
|
||||||
|
name="from"
|
||||||
|
type="text"
|
||||||
|
class="block w-full rounded-md border border-gray-300 bg-white p-2.5 ps-10 text-sm text-gray-900 focus:border-blue-500 focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-700 dark:text-white dark:placeholder-gray-400 dark:focus:border-blue-500 dark:focus:ring-blue-500"
|
||||||
|
placeholder="{{ __("From Date") }}"
|
||||||
|
value="{{ request()->query("from") }}"
|
||||||
|
autocomplete="off"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<span class="mx-2 text-gray-500">to</span>
|
||||||
|
<div class="relative">
|
||||||
|
<div class="pointer-events-none absolute inset-y-0 start-0 flex items-center ps-3">
|
||||||
|
<x-heroicon name="o-calendar" class="h-4 w-4 text-gray-500 dark:text-gray-400" />
|
||||||
|
</div>
|
||||||
|
<input
|
||||||
|
name="to"
|
||||||
|
type="text"
|
||||||
|
class="block w-full rounded-md border border-gray-300 bg-white p-2.5 ps-10 text-sm text-gray-900 focus:border-blue-500 focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-700 dark:text-white dark:placeholder-gray-400 dark:focus:border-blue-500 dark:focus:ring-blue-500"
|
||||||
|
placeholder="{{ __("To Date") }}"
|
||||||
|
value="{{ request()->query("to") }}"
|
||||||
|
autocomplete="off"
|
||||||
|
/>
|
||||||
|
<x-input-error class="absolute left-0 top-10 ml-1 mt-1" :messages="$errors->get('to')" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<x-primary-button class="ml-2 h-[42px]">{{ __("Filter") }}</x-primary-button>
|
||||||
|
</form>
|
||||||
|
</div>
|
@ -239,6 +239,7 @@ class="mt-1 block w-full"
|
|||||||
<div x-show="['{{ ServerType::REGULAR }}'].includes(type)">
|
<div x-show="['{{ ServerType::REGULAR }}'].includes(type)">
|
||||||
<x-input-label for="php" value="PHP" />
|
<x-input-label for="php" value="PHP" />
|
||||||
<x-select-input id="php" name="php" class="mt-1 w-full">
|
<x-select-input id="php" name="php" class="mt-1 w-full">
|
||||||
|
<option value="none" @if('none' == old('php', '8.2')) selected @endif>none</option>
|
||||||
@foreach (config("core.php_versions") as $p)
|
@foreach (config("core.php_versions") as $p)
|
||||||
<option value="{{ $p }}" @if($p == old('php', '8.2')) selected @endif>
|
<option value="{{ $p }}" @if($p == old('php', '8.2')) selected @endif>
|
||||||
{{ $p }}
|
{{ $p }}
|
||||||
|
@ -13,20 +13,7 @@ class="@if($server->webserver() && $server->database()) grid-cols-3 @else grid-c
|
|||||||
@if ($server->webserver())
|
@if ($server->webserver())
|
||||||
<div class="border-r border-gray-200 p-5 dark:border-gray-900">
|
<div class="border-r border-gray-200 p-5 dark:border-gray-900">
|
||||||
<div class="flex items-center justify-center md:justify-start">
|
<div class="flex items-center justify-center md:justify-start">
|
||||||
<svg
|
<x-heroicon name="o-globe-alt" class="h-8 w-8 text-primary-500" />
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
fill="none"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
stroke-width="1.5"
|
|
||||||
stroke="currentColor"
|
|
||||||
class="h-8 w-8 text-primary-500"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
d="M12 21a9.004 9.004 0 008.716-6.747M12 21a9.004 9.004 0 01-8.716-6.747M12 21c2.485 0 4.5-4.03 4.5-9S14.485 3 12 3m0 18c-2.485 0-4.5-4.03-4.5-9S9.515 3 12 3m0 0a8.997 8.997 0 017.843 4.582M12 3a8.997 8.997 0 00-7.843 4.582m15.686 0A11.953 11.953 0 0112 10.5c-2.998 0-5.74-1.1-7.843-2.918m15.686 0A8.959 8.959 0 0121 12c0 .778-.099 1.533-.284 2.253m0 0A17.919 17.919 0 0112 16.5c-3.162 0-6.133-.815-8.716-2.247m0 0A9.015 9.015 0 013 12c0-1.605.42-3.113 1.157-4.418"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
<div class="ml-2 hidden md:block">{{ __("Sites") }}</div>
|
<div class="ml-2 hidden md:block">{{ __("Sites") }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-3 text-center text-3xl font-bold text-gray-600 dark:text-gray-400 md:text-left">
|
<div class="mt-3 text-center text-3xl font-bold text-gray-600 dark:text-gray-400 md:text-left">
|
||||||
@ -38,20 +25,7 @@ class="h-8 w-8 text-primary-500"
|
|||||||
@if ($server->database())
|
@if ($server->database())
|
||||||
<div class="border-r border-gray-200 p-5 dark:border-gray-900">
|
<div class="border-r border-gray-200 p-5 dark:border-gray-900">
|
||||||
<div class="flex items-center justify-center md:justify-start">
|
<div class="flex items-center justify-center md:justify-start">
|
||||||
<svg
|
<x-heroicon name="o-circle-stack" class="h-8 w-8 text-primary-500" />
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
fill="none"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
stroke-width="1.5"
|
|
||||||
stroke="currentColor"
|
|
||||||
class="h-8 w-8 text-primary-500"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
d="M20.25 6.375c0 2.278-3.694 4.125-8.25 4.125S3.75 8.653 3.75 6.375m16.5 0c0-2.278-3.694-4.125-8.25-4.125S3.75 4.097 3.75 6.375m16.5 0v11.25c0 2.278-3.694 4.125-8.25 4.125s-8.25-1.847-8.25-4.125V6.375m16.5 0v3.75m-16.5-3.75v3.75m16.5 0v3.75C20.25 16.153 16.556 18 12 18s-8.25-1.847-8.25-4.125v-3.75m16.5 0c0 2.278-3.694 4.125-8.25 4.125s-8.25-1.847-8.25-4.125"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
<div class="ml-2 hidden md:block">
|
<div class="ml-2 hidden md:block">
|
||||||
{{ __("Databases") }}
|
{{ __("Databases") }}
|
||||||
</div>
|
</div>
|
||||||
@ -64,20 +38,7 @@ class="h-8 w-8 text-primary-500"
|
|||||||
|
|
||||||
<div class="p-5">
|
<div class="p-5">
|
||||||
<div class="flex items-center justify-center md:justify-start">
|
<div class="flex items-center justify-center md:justify-start">
|
||||||
<svg
|
<x-heroicon name="o-clock" class="h-8 w-8 text-primary-500" />
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
fill="none"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
stroke-width="1.5"
|
|
||||||
stroke="currentColor"
|
|
||||||
class="h-8 w-8 text-primary-500"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
d="M12 6v6h4.5m4.5 0a9 9 0 11-18 0 9 9 0 0118 0z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
<div class="ml-2 hidden md:block">{{ __("Cron Jobs") }}</div>
|
<div class="ml-2 hidden md:block">{{ __("Cron Jobs") }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-3 text-center text-3xl font-bold text-gray-600 dark:text-gray-400 md:text-left">
|
<div class="mt-3 text-center text-3xl font-bold text-gray-600 dark:text-gray-400 md:text-left">
|
||||||
|
@ -3,5 +3,7 @@
|
|||||||
|
|
||||||
@include("services.partials.services-list")
|
@include("services.partials.services-list")
|
||||||
|
|
||||||
{{-- @include("services.partials.add-ons") --}}
|
@include("services.partials.available-services")
|
||||||
|
|
||||||
|
@stack("modals")
|
||||||
</x-server-layout>
|
</x-server-layout>
|
||||||
|
@ -1 +1,6 @@
|
|||||||
@include("services.partials.unit-actions")
|
@include("services.partials.unit-actions.restart")
|
||||||
|
@include("services.partials.unit-actions.start")
|
||||||
|
@include("services.partials.unit-actions.stop")
|
||||||
|
@include("services.partials.unit-actions.enable")
|
||||||
|
@include("services.partials.unit-actions.disable")
|
||||||
|
@include("services.partials.unit-actions.uninstall")
|
||||||
|
@ -1 +1,6 @@
|
|||||||
@include("services.partials.unit-actions")
|
@include("services.partials.unit-actions.restart")
|
||||||
|
@include("services.partials.unit-actions.start")
|
||||||
|
@include("services.partials.unit-actions.stop")
|
||||||
|
@include("services.partials.unit-actions.enable")
|
||||||
|
@include("services.partials.unit-actions.disable")
|
||||||
|
@include("services.partials.unit-actions.uninstall")
|
||||||
|
@ -1 +1,6 @@
|
|||||||
@include("services.partials.unit-actions")
|
@include("services.partials.unit-actions.restart")
|
||||||
|
@include("services.partials.unit-actions.start")
|
||||||
|
@include("services.partials.unit-actions.stop")
|
||||||
|
@include("services.partials.unit-actions.enable")
|
||||||
|
@include("services.partials.unit-actions.disable")
|
||||||
|
@include("services.partials.unit-actions.uninstall")
|
||||||
|
@ -1 +1,6 @@
|
|||||||
@include("services.partials.unit-actions")
|
@include("services.partials.unit-actions.restart")
|
||||||
|
@include("services.partials.unit-actions.start")
|
||||||
|
@include("services.partials.unit-actions.stop")
|
||||||
|
@include("services.partials.unit-actions.enable")
|
||||||
|
@include("services.partials.unit-actions.disable")
|
||||||
|
@include("services.partials.unit-actions.uninstall")
|
||||||
|
@ -1 +1,6 @@
|
|||||||
@include("services.partials.unit-actions")
|
@include("services.partials.unit-actions.restart")
|
||||||
|
@include("services.partials.unit-actions.start")
|
||||||
|
@include("services.partials.unit-actions.stop")
|
||||||
|
@include("services.partials.unit-actions.enable")
|
||||||
|
@include("services.partials.unit-actions.disable")
|
||||||
|
@include("services.partials.unit-actions.uninstall")
|
||||||
|
@ -1 +1,6 @@
|
|||||||
@include("services.partials.unit-actions")
|
@include("services.partials.unit-actions.restart")
|
||||||
|
@include("services.partials.unit-actions.start")
|
||||||
|
@include("services.partials.unit-actions.stop")
|
||||||
|
@include("services.partials.unit-actions.enable")
|
||||||
|
@include("services.partials.unit-actions.disable")
|
||||||
|
@include("services.partials.unit-actions.uninstall")
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user