Compare commits

...

61 Commits
0.x ... 1.1.0

Author SHA1 Message Date
d01d406d3d Merge pull request #137 from vitodeploy/phpmyadmin
add phpmyadmin
2024-03-27 13:34:40 +01:00
c66c50835a fix tests 2024-03-27 13:32:25 +01:00
b6179d6693 build 2024-03-27 11:49:48 +01:00
9244e69fd8 add phpmyadmin 2024-03-27 11:41:29 +01:00
a7ba095919 build 2024-03-25 23:02:06 +01:00
807ae01646 Merge pull request #135 from vitodeploy/console
headless console
2024-03-25 22:57:56 +01:00
cc896d82e9 Merge branch 'console' of github.com:vitodeploy/vito into console 2024-03-25 22:55:48 +01:00
d16d3c1385 test 2024-03-25 22:52:45 +01:00
3946cf6b34 Merge branch '1.x' into console 2024-03-25 22:20:39 +01:00
165212fed2 add stop button 2024-03-25 22:20:21 +01:00
f6b36dfefc Update CONTRIBUTING.md 2024-03-25 22:19:30 +01:00
33594f2dba headless console 2024-03-24 21:58:48 +01:00
f68d6c7ca2 Merge pull request #133 from vitodeploy/fix-php-ini-update
fix php ini update bug
2024-03-24 15:19:06 +01:00
d504588f95 fix php ini update bug 2024-03-24 15:16:49 +01:00
ca0e33be2f Merge branch 'main' into 1.x 2024-03-24 09:58:50 +01:00
4d051330d6 Merge (#127) 2024-03-24 09:56:34 +01:00
ab2d6f64f3 Merge branch 'main' into 1.x 2024-03-24 09:56:07 +01:00
d9a56f95dd minify ace.js 2024-03-24 09:01:18 +01:00
7fc7944354 fix style 2024-03-24 00:09:58 +01:00
9ef7bf1bc9 fix style 2024-03-24 00:07:44 +01:00
35e5e16df9 add backward compatibility to filesystem 2024-03-23 18:53:28 +01:00
369bf1b965 fix code style 2024-03-23 18:35:03 +01:00
ee2b53f824 github actions 2024-03-23 18:24:07 +01:00
46845fb41f Merge remote-tracking branch 'origin/1.x' into 1.x 2024-03-23 18:22:15 +01:00
c78462b173 fix tests 2024-03-23 18:21:15 +01:00
5f192d69de Update README.md 2024-03-23 18:14:10 +01:00
3afb6b7eb4 sqlite to mysql migrate command 2024-03-23 17:39:51 +01:00
6529c349b1 upgrade fixes 2024-03-23 15:33:36 +01:00
6389e592a4 fix route cache 2024-03-23 14:57:43 +01:00
9f577ef2d0 fix migrations for upgrade 2024-03-23 14:34:02 +01:00
77a09cec48 fix route cache 2024-03-23 14:28:22 +01:00
7b0a71fb57 config 2024-03-23 13:12:09 +01:00
d5a567cc11 Update SECURITY.md 2024-03-23 10:40:00 +01:00
3575a6d469 AGPL-3.0 2024-03-23 10:34:01 +01:00
4137e2003f fix docker 2024-03-22 17:02:18 +01:00
d4f1a2d4da cleanup
fix installation
fix docker
2024-03-22 14:14:47 +01:00
d3aaf2a6fa Redesign (#124)
- redesign the dashboard
- add search bar
- Mobile-friendly design
2024-03-21 15:57:57 +01:00
7949165648 fix custom ssl creation (#122) 2024-03-18 17:17:45 +01:00
3dc38bf56b remove blade-ui-kit/blade-heroicons 2024-03-17 14:37:19 +01:00
4f252caaf3 remove laravel/sanctum 2024-03-17 13:10:26 +01:00
839e7f496c drop dependency to bensampo/laravel-enum (#120) 2024-03-17 12:26:48 +01:00
b07ae470f9 fix bitbucket (#119)
Fix Bitbucket API errors
2024-03-17 11:36:04 +01:00
f0c4fc4812 simplify firewal protocol select confusion 2024-03-16 23:52:27 +01:00
46d320561e simplify firewal protocol select confusion 2024-03-16 23:45:46 +01:00
1333acf1ef fix copy to clipboard bug 2024-03-16 23:42:22 +01:00
77d6914cd9 add enable/disable services 2024-03-16 14:02:28 +01:00
7a6dcb5654 add log viewer for queues 2024-03-16 12:20:32 +01:00
fd93f3dd47 add PHP 8.3 support 2024-03-16 11:08:45 +01:00
d8056441c0 add mariadb 10.4 2024-03-16 10:35:18 +01:00
287c9c2a8a add postgresql (#118) 2024-03-16 01:04:57 +01:00
a406491160 increase test coverage (#117)
70% test coverage
remove socialite
2024-03-15 22:23:45 +01:00
4f12de9586 backup command cleanup 2024-03-14 22:27:13 +01:00
f907bacdd0 refactoring leftovers 2024-03-14 21:18:59 +01:00
428140b931 refactoring (#116)
- refactoring architecture
- fix incomplete ssh logs
- code editor for scripts in the app
- remove Jobs and SSHCommands
2024-03-14 20:03:43 +01:00
cee4a70c3c drop node.js from installation process 2024-03-07 17:17:29 +01:00
60e2dc0807 add npm lint to the actions 2024-03-06 21:48:02 +01:00
9d705592da refactoring - remove unused code 2024-03-06 20:33:33 +01:00
cfc0645002 remove dependencies (#115)
upgrade php
upgrade packages
remove extra dependencies
2024-03-06 20:05:31 +01:00
b2083fc6b2 Migrate to HTMX (#114)
Dropped Livewire
Added HTMX
Added Blade code lint
Drop Mysql and Redis
Migrate to SQLite
2024-03-06 17:02:59 +01:00
5b2c419e91 create sqlite database for tests 2024-02-25 17:25:25 +01:00
470274279b drop mysql and redis and use sqlite for Vito itself to optimize the resources 2024-02-25 17:16:11 +01:00
1082 changed files with 15104 additions and 20350 deletions

View File

@ -2,28 +2,9 @@ APP_NAME=Vito
APP_ENV=local APP_ENV=local
APP_KEY= APP_KEY=
APP_DEBUG=true APP_DEBUG=true
APP_URL=http://vito.test APP_URL=
LOG_CHANNEL=stack
LOG_LEVEL=debug
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=vito
DB_USERNAME=root
DB_PASSWORD=
BROADCAST_DRIVER=null
CACHE_DRIVER=file
FILESYSTEM_DRIVER=local FILESYSTEM_DRIVER=local
QUEUE_CONNECTION=default
SESSION_DRIVER=database
SESSION_LIFETIME=120
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
MAIL_MAILER=smtp MAIL_MAILER=smtp
MAIL_HOST= MAIL_HOST=
@ -33,6 +14,3 @@ MAIL_PASSWORD=null
MAIL_ENCRYPTION=null MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS=null MAIL_FROM_ADDRESS=null
MAIL_FROM_NAME="${APP_NAME}" MAIL_FROM_NAME="${APP_NAME}"
SSH_PUBLIC_KEY_NAME=ssh-public.key
SSH_PRIVATE_KEY_NAME=ssh-private.pem

View File

@ -4,26 +4,7 @@ APP_KEY=
APP_DEBUG=false APP_DEBUG=false
APP_URL= APP_URL=
LOG_CHANNEL=stack
LOG_LEVEL=debug
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=
DB_USERNAME=
DB_PASSWORD=
BROADCAST_DRIVER=null
CACHE_DRIVER=file
FILESYSTEM_DRIVER=local FILESYSTEM_DRIVER=local
QUEUE_CONNECTION=default
SESSION_DRIVER=database
SESSION_LIFETIME=120
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
MAIL_MAILER=smtp MAIL_MAILER=smtp
MAIL_HOST= MAIL_HOST=
@ -33,6 +14,3 @@ MAIL_PASSWORD=null
MAIL_ENCRYPTION=null MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS=null MAIL_FROM_ADDRESS=null
MAIL_FROM_NAME="${APP_NAME}" MAIL_FROM_NAME="${APP_NAME}"
SSH_PUBLIC_KEY_NAME=ssh-public.key
SSH_PRIVATE_KEY_NAME=ssh-private.pem

View File

@ -2,28 +2,10 @@ APP_NAME=Vito
APP_ENV=local APP_ENV=local
APP_KEY= APP_KEY=
APP_DEBUG=true APP_DEBUG=true
APP_URL=http://vito.test APP_URL=
APP_PORT=8000
LOG_CHANNEL=stack
LOG_LEVEL=debug
DB_CONNECTION=mysql
DB_HOST=mysql
DB_PORT=3306
DB_DATABASE=vito
DB_USERNAME=sail
DB_PASSWORD=password
BROADCAST_DRIVER=null
CACHE_DRIVER=redis
FILESYSTEM_DRIVER=local FILESYSTEM_DRIVER=local
QUEUE_CONNECTION=default
SESSION_DRIVER=database
SESSION_LIFETIME=120
REDIS_HOST=redis
REDIS_PASSWORD=null
REDIS_PORT=6379
MAIL_MAILER=smtp MAIL_MAILER=smtp
MAIL_HOST= MAIL_HOST=
@ -33,13 +15,3 @@ MAIL_PASSWORD=null
MAIL_ENCRYPTION=null MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS=null MAIL_FROM_ADDRESS=null
MAIL_FROM_NAME="${APP_NAME}" MAIL_FROM_NAME="${APP_NAME}"
SSH_PUBLIC_KEY_NAME=ssh-public.key
SSH_PRIVATE_KEY_NAME=ssh-private.pem
APP_SERVICE=vito
FORWARD_REDIS_PORT=2060
FORWARD_DB_PORT=2070
APP_PORT=2080
HMR_PORT=2090

View File

@ -1,27 +0,0 @@
APP_NAME=Vito
APP_ENV=local
APP_KEY=base64:d9kZW60V4lFEw2SPn6UiJ0cfi04v80EWP0GZ6kzoxNg=
APP_DEBUG=true
APP_URL=http://localhost:2080
LOG_CHANNEL=stack
LOG_LEVEL=debug
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=vito_test
DB_USERNAME=root
DB_PASSWORD=
BROADCAST_DRIVER=null
CACHE_DRIVER=array
FILESYSTEM_DRIVER=local
QUEUE_CONNECTION=database
SESSION_DRIVER=array
SESSION_LIFETIME=120
MAIL_MAILER=array
SSH_PUBLIC_KEY_NAME=ssh-public.key
SSH_PRIVATE_KEY_NAME=ssh-private.pem

View File

@ -2,9 +2,9 @@ name: code-style
on: on:
push: push:
branches:
- 1.x
pull_request: pull_request:
schedule:
- cron: '0 0 * * *'
jobs: jobs:
code-style: code-style:
@ -13,7 +13,8 @@ jobs:
strategy: strategy:
fail-fast: true fail-fast: true
matrix: matrix:
php: [ 8.1 ] php: [8.2]
node-version: ["20.x"]
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
@ -31,9 +32,21 @@ jobs:
key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }} key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }}
restore-keys: | restore-keys: |
${{ runner.os }}-php- ${{ runner.os }}-php-
- name: Install dependencies - name: Install dependencies
if: steps.composer-cache.outputs.cache-hit != 'true' if: steps.composer-cache.outputs.cache-hit != 'true'
run: composer install --prefer-dist --no-progress --no-suggest run: composer install --prefer-dist --no-progress --no-suggest
- name: Run pint - name: Run pint
run: ./vendor/bin/pint --test run: ./vendor/bin/pint --test
- name: Setup Node.js
uses: actions/setup-node@v2
with:
node-version: "20.x"
- name: Install NPM Dependencies
run: npm install
- name: Run lint
run: npm run lint

View File

@ -2,30 +2,18 @@ name: tests
on: on:
push: push:
branches:
- 1.x
pull_request: pull_request:
schedule:
- cron: '0 0 * * *'
jobs: jobs:
tests: tests:
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
services:
mysql:
image: mysql
env:
MYSQL_DATABASE: test_db
MYSQL_USER: user
MYSQL_PASSWORD: password
MYSQL_ROOT_PASSWORD: rootpassword
ports:
- 3306:3306
options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3
strategy: strategy:
fail-fast: true fail-fast: true
matrix: matrix:
php: [ 8.1 ] php: [8.2]
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
@ -47,10 +35,8 @@ jobs:
if: steps.composer-cache.outputs.cache-hit != 'true' if: steps.composer-cache.outputs.cache-hit != 'true'
run: composer install --prefer-dist --no-progress --no-suggest run: composer install --prefer-dist --no-progress --no-suggest
- name: Create sqlite database
run: touch storage/database-test.sqlite
- name: Run test suite - name: Run test suite
run: php artisan test run: php artisan test
env:
DB_HOST: 127.0.0.1
DB_DATABASE: test_db
DB_USERNAME: user
DB_PASSWORD: password

2
.gitignore vendored
View File

@ -4,6 +4,8 @@
/public/storage /public/storage
/storage/*.key /storage/*.key
/storage/*.pem /storage/*.pem
/storage/test-key
/storage/test-key.pub
/vendor /vendor
.env .env
.env.backup .env.backup

16
.prettierrc Normal file
View File

@ -0,0 +1,16 @@
{
"plugins": ["prettier-plugin-blade", "prettier-plugin-tailwindcss"],
"overrides": [
{
"files": ["*.blade.php"],
"options": {
"parser": "blade",
"printWidth": 120,
"htmlWhitespaceSensitivity": "ignore",
"tabWidth": 4,
"quoteProps": "consistent",
"trailingComma": "none"
}
}
]
}

View File

@ -1,23 +1,5 @@
# Contributing # Contributing
Thank you for your interest in contributing! There are a couple of contribution guidelines that make it easier to apply the incoming suggestions.
If you want to contribute please start with the issues. Issues labeled with "Bug" are the higher priorities. Please read the contribution guide on the website
## Issues https://vitodeploy.com/introduction/contribution-guide.html
1. Issues are the best place to propose a new feature.
2. If you are adding a feature that there is no issue for yet, please first open an issue and label it as "feature" and lets discuss it before you implement it.
3. Search the issues before proposing a feature to see if it is already under discussion. Referencing existing issues is a good way to increase the priority of your own.
4. We don't have an issue template yet, but the more detailed your explanation, the more quickly we'll be able to evaluate it.
5. Search for the issue that you also have. Give it a reaction (and comment, if you have something to add). We note that!
## Pull Requests
1. Open PRs represent issues that we're actively thinking about merging (at a pace we can manage). If we think a proposal needs more discussion, or that the existing code would require a lot of back-and-forth to merge, we might close it and suggest you make an issue.
2. All PRs should be made against the `main` branch. This can be changed in the future.
3. If you are making changes to the front-end layer, Please build the assets via `npm run build` and push it with the other changes.
4. Write tests for your code. Tests can be Unit or Feature.
5. Code refactors will be closed. For the architectural refactors open an issue first.
6. Use `./vendor/bin/pint` to style your code before opening a PR otherwise the actions will fail.
7. Typo fixes in documentation are welcome, but if it's at all debatable we might just close it.
## Misc
1. If you think we closed something incorrectly, feel free to (politely) tell us why! We're human and make mistakes.

View File

@ -11,6 +11,12 @@ ## About Vito
Vito is a self-hosted web application that helps you manage your servers and deploy your PHP applications into production servers without a hassle. Vito is a self-hosted web application that helps you manage your servers and deploy your PHP applications into production servers without a hassle.
## Quick Start
```sh
bash <(curl -Ls https://raw.githubusercontent.com/vitodeploy/vito/1.x/scripts/install.sh)
```
## Features ## Features
- Provisions and Manages the server - Provisions and Manages the server
@ -38,15 +44,14 @@ ## Useful Links
## Credits ## Credits
- Laravel - Laravel
- Tailwindcss
- Livewire
- Alpinejs
- Vite
- Laravel Enum by BenSampo
- Log Viewer by Arunas Skirius
- PHPSecLib - PHPSecLib
- Laravel Blade Icons - PHPUnit
- Guzzlehttp - Tailwindcss
- Owenvoke for `owenvoke/blade-fontawesome` - Alpinejs
- Axios - HTMX
- Vite
- Toastr by CodeSeven - Toastr by CodeSeven
- Prettier
- Postcss
- Flowbite
- svgrepo.com

View File

@ -3,8 +3,9 @@ # Security Policy
## Supported Versions ## Supported Versions
| Version | Supported | | Version | Supported |
| ------- | ------------------ | | ------- | ----------|
| 0.x | :white_check_mark: | | 0.x | |
| 1.x | ✅ |
## Reporting a Vulnerability ## Reporting a Vulnerability

View File

@ -19,11 +19,14 @@ public function create(Server $server, array $input): void
'server_id' => $server->id, 'server_id' => $server->id,
'user' => $input['user'], 'user' => $input['user'],
'command' => $input['command'], 'command' => $input['command'],
'frequency' => $input['frequency'], 'frequency' => $input['frequency'] == 'custom' ? $input['custom'] : $input['frequency'],
'status' => CronjobStatus::CREATING, 'status' => CronjobStatus::CREATING,
]); ]);
$cronJob->save(); $cronJob->save();
$cronJob->addToServer();
$server->cron()->update($cronJob->user, CronJob::crontab($server, $cronJob->user));
$cronJob->status = CronjobStatus::READY;
$cronJob->save();
} }
/** /**
@ -40,9 +43,18 @@ private function validate(array $input): void
'in:root,'.config('core.ssh_user'), 'in:root,'.config('core.ssh_user'),
], ],
'frequency' => [ 'frequency' => [
'required',
new CronRule(acceptCustom: true),
],
])->validate();
if ($input['frequency'] == 'custom') {
Validator::make($input, [
'custom' => [
'required', 'required',
new CronRule(), new CronRule(),
], ],
])->validateWithBag('createCronJob'); ])->validate();
}
} }
} }

View File

@ -0,0 +1,16 @@
<?php
namespace App\Actions\CronJob;
use App\Models\CronJob;
use App\Models\Server;
class DeleteCronJob
{
public function delete(Server $server, CronJob $cronJob): void
{
$user = $cronJob->user;
$cronJob->delete();
$server->cron()->update($cronJob->user, CronJob::crontab($server, $user));
}
}

View File

@ -24,15 +24,15 @@ public function create($type, Server $server, array $input): Backup
$backup = new Backup([ $backup = new Backup([
'type' => $type, 'type' => $type,
'server_id' => $server->id, 'server_id' => $server->id,
'database_id' => $input['database'] ?? null, 'database_id' => $input['backup_database'] ?? null,
'storage_id' => $input['storage'], 'storage_id' => $input['backup_storage'],
'interval' => $input['interval'] == 'custom' ? $input['custom'] : $input['interval'], 'interval' => $input['backup_interval'] == 'custom' ? $input['backup_custom'] : $input['backup_interval'],
'keep_backups' => $input['keep'], 'keep_backups' => $input['backup_keep'],
'status' => BackupStatus::RUNNING, 'status' => BackupStatus::RUNNING,
]); ]);
$backup->save(); $backup->save();
$backup->run(); app(RunBackup::class)->run($backup);
return $backup; return $backup;
} }
@ -43,16 +43,16 @@ public function create($type, Server $server, array $input): Backup
private function validate($type, Server $server, array $input): void private function validate($type, Server $server, array $input): void
{ {
$rules = [ $rules = [
'storage' => [ 'backup_storage' => [
'required', 'required',
Rule::exists('storage_providers', 'id'), Rule::exists('storage_providers', 'id'),
], ],
'keep' => [ 'backup_keep' => [
'required', 'required',
'numeric', 'numeric',
'min:1', 'min:1',
], ],
'interval' => [ 'backup_interval' => [
'required', 'required',
Rule::in([ Rule::in([
'0 * * * *', '0 * * * *',
@ -63,13 +63,13 @@ private function validate($type, Server $server, array $input): void
]), ]),
], ],
]; ];
if ($input['interval'] == 'custom') { if ($input['backup_interval'] == 'custom') {
$rules['custom'] = [ $rules['backup_custom'] = [
'required', 'required',
]; ];
} }
if ($type === 'database') { if ($type === 'database') {
$rules['database'] = [ $rules['backup_database'] = [
'required', 'required',
Rule::exists('databases', 'id') Rule::exists('databases', 'id')
->where('server_id', $server->id) ->where('server_id', $server->id)

View File

@ -2,6 +2,7 @@
namespace App\Actions\Database; namespace App\Actions\Database;
use App\Enums\DatabaseStatus;
use App\Models\Database; use App\Models\Database;
use App\Models\Server; use App\Models\Server;
use Illuminate\Support\Facades\Validator; use Illuminate\Support\Facades\Validator;
@ -21,8 +22,9 @@ public function create(Server $server, array $input): Database
'server_id' => $server->id, 'server_id' => $server->id,
'name' => $input['name'], 'name' => $input['name'],
]); ]);
$server->database()->handler()->create($database->name);
$database->status = DatabaseStatus::READY;
$database->save(); $database->save();
$database->createOnServer();
return $database; return $database;
} }

View File

@ -2,6 +2,7 @@
namespace App\Actions\Database; namespace App\Actions\Database;
use App\Enums\DatabaseUserStatus;
use App\Models\DatabaseUser; use App\Models\DatabaseUser;
use App\Models\Server; use App\Models\Server;
use Illuminate\Support\Facades\Validator; use Illuminate\Support\Facades\Validator;
@ -24,8 +25,17 @@ public function create(Server $server, array $input, array $links = []): Databas
'host' => isset($input['remote']) && $input['remote'] ? $input['host'] : 'localhost', 'host' => isset($input['remote']) && $input['remote'] ? $input['host'] : 'localhost',
'databases' => $links, 'databases' => $links,
]); ]);
$server->database()->handler()->createUser(
$databaseUser->username,
$databaseUser->password,
$databaseUser->host
);
$databaseUser->status = DatabaseUserStatus::READY;
$databaseUser->save(); $databaseUser->save();
$databaseUser->createOnServer();
if (count($links) > 0) {
app(LinkUser::class)->link($databaseUser, ['databases' => $links]);
}
return $databaseUser; return $databaseUser;
} }

View File

@ -0,0 +1,15 @@
<?php
namespace App\Actions\Database;
use App\Models\Database;
use App\Models\Server;
class DeleteDatabase
{
public function delete(Server $server, Database $database): void
{
$server->database()->handler()->delete($database->name);
$database->delete();
}
}

View File

@ -0,0 +1,15 @@
<?php
namespace App\Actions\Database;
use App\Models\DatabaseUser;
use App\Models\Server;
class DeleteDatabaseUser
{
public function delete(Server $server, DatabaseUser $databaseUser): void
{
$server->database()->handler()->deleteUser($databaseUser->username, $databaseUser->host);
$databaseUser->delete();
}
}

View File

@ -4,6 +4,9 @@
use App\Models\Database; use App\Models\Database;
use App\Models\DatabaseUser; use App\Models\DatabaseUser;
use App\Models\Server;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rule;
use Illuminate\Validation\ValidationException; use Illuminate\Validation\ValidationException;
class LinkUser class LinkUser
@ -11,20 +14,49 @@ class LinkUser
/** /**
* @throws ValidationException * @throws ValidationException
*/ */
public function link(DatabaseUser $databaseUser, array $databases): void public function link(DatabaseUser $databaseUser, array $input): void
{ {
$dbs = Database::query() if (! isset($input['databases']) || ! is_array($input['databases'])) {
->where('server_id', $databaseUser->server_id) $input['databases'] = [];
->whereIn('name', $databases)
->count();
if (count($databases) !== $dbs) {
throw ValidationException::withMessages(['databases' => __('Databases not found!')])
->errorBag('linkUser');
} }
$databaseUser->databases = $databases; $this->validate($databaseUser->server, $input);
$databaseUser->unlinkUser();
$databaseUser->linkUser(); $dbs = Database::query()
->where('server_id', $databaseUser->server_id)
->whereIn('name', $input['databases'])
->count();
if (count($input['databases']) !== $dbs) {
throw ValidationException::withMessages(['databases' => __('Databases not found!')]);
}
$databaseUser->databases = $input['databases'];
// Unlink the user from all databases
$databaseUser->server->database()->handler()->unlink(
$databaseUser->username,
$databaseUser->host
);
// Link the user to the selected databases
$databaseUser->server->database()->handler()->link(
$databaseUser->username,
$databaseUser->host,
$databaseUser->databases
);
$databaseUser->save(); $databaseUser->save();
} }
private function validate(Server $server, array $input): void
{
$rules = [
'databases.*' => [
'required',
Rule::exists('databases', 'name')->where('server_id', $server->id),
],
];
Validator::make($input, $rules)->validate();
}
} }

View File

@ -0,0 +1,39 @@
<?php
namespace App\Actions\Database;
use App\Enums\BackupFileStatus;
use App\Models\BackupFile;
use App\Models\Database;
use Illuminate\Support\Facades\Validator;
class RestoreBackup
{
public function restore(BackupFile $backupFile, array $input): void
{
$this->validate($input);
/** @var Database $database */
$database = Database::query()->findOrFail($input['database']);
$backupFile->status = BackupFileStatus::RESTORING;
$backupFile->restored_to = $database->name;
$backupFile->save();
dispatch(function () use ($backupFile, $database) {
$database->server->database()->handler()->restoreBackup($backupFile, $database->name);
$backupFile->status = BackupFileStatus::RESTORED;
$backupFile->restored_at = now();
$backupFile->save();
})->catch(function () use ($backupFile) {
$backupFile->status = BackupFileStatus::RESTORE_FAILED;
$backupFile->save();
})->onConnection('ssh');
}
private function validate(array $input): void
{
Validator::make($input, [
'database' => 'required|exists:databases,id',
])->validate();
}
}

View File

@ -0,0 +1,32 @@
<?php
namespace App\Actions\Database;
use App\Enums\BackupFileStatus;
use App\Models\Backup;
use App\Models\BackupFile;
use Illuminate\Support\Str;
class RunBackup
{
public function run(Backup $backup): BackupFile
{
$file = new BackupFile([
'backup_id' => $backup->id,
'name' => Str::of($backup->database->name)->slug().'-'.now()->format('YmdHis'),
'status' => BackupFileStatus::CREATING,
]);
$file->save();
dispatch(function () use ($file) {
$file->backup->server->database()->handler()->runBackup($file);
$file->status = BackupFileStatus::CREATED;
$file->save();
})->catch(function () use ($file) {
$file->status = BackupFileStatus::FAILED;
$file->save();
})->onConnection('ssh');
return $file;
}
}

View File

@ -21,10 +21,20 @@ public function create(Server $server, array $input): FirewallRule
'port' => $input['port'], 'port' => $input['port'],
'source' => $input['source'], 'source' => $input['source'],
'mask' => $input['mask'] ?? null, 'mask' => $input['mask'] ?? null,
'status' => FirewallRuleStatus::CREATING,
]); ]);
$server->firewall()
->handler()
->addRule(
$rule->type,
$rule->getRealProtocol(),
$rule->port,
$rule->source,
$rule->mask
);
$rule->status = FirewallRuleStatus::READY;
$rule->save(); $rule->save();
$rule->addToServer();
return $rule; return $rule;
} }
@ -56,6 +66,6 @@ private function validate(Server $server, array $input): void
'mask' => [ 'mask' => [
'numeric', 'numeric',
], ],
])->validateWithBag('createRule'); ])->validate();
} }
} }

View File

@ -0,0 +1,28 @@
<?php
namespace App\Actions\FirewallRule;
use App\Enums\FirewallRuleStatus;
use App\Models\FirewallRule;
use App\Models\Server;
class DeleteRule
{
public function delete(Server $server, FirewallRule $rule): void
{
$rule->status = FirewallRuleStatus::DELETING;
$rule->save();
$server->firewall()
->handler()
->removeRule(
$rule->type,
$rule->getRealProtocol(),
$rule->port,
$rule->source,
$rule->mask
);
$rule->delete();
}
}

View File

@ -0,0 +1,29 @@
<?php
namespace App\Actions\PHP;
use App\Enums\ServiceStatus;
use App\Models\Server;
use Illuminate\Validation\ValidationException;
class ChangeDefaultCli
{
public function change(Server $server, array $input): void
{
$this->validate($server, $input);
$service = $server->php($input['version']);
$service->handler()->setDefaultCli();
$server->defaultService('php')->update(['is_default' => 0]);
$service->update(['is_default' => 1]);
$service->update(['status' => ServiceStatus::READY]);
}
public function validate(Server $server, array $input): void
{
if (! isset($input['version']) || ! in_array($input['version'], $server->installedPHPVersions())) {
throw ValidationException::withMessages(
['version' => __('This version is not installed')]
);
}
}
}

View File

@ -0,0 +1,33 @@
<?php
namespace App\Actions\PHP;
use App\Models\Server;
use Illuminate\Validation\ValidationException;
class GetPHPIni
{
public function getIni(Server $server, array $input): string
{
$this->validate($server, $input);
$php = $server->php($input['version']);
try {
return $php->handler()->getPHPIni();
} catch (\Throwable $e) {
throw ValidationException::withMessages(
['ini' => $e->getMessage()]
);
}
}
public function validate(Server $server, array $input): void
{
if (! isset($input['version']) || ! in_array($input['version'], $server->installedPHPVersions())) {
throw ValidationException::withMessages(
['version' => __('This version is not installed')]
);
}
}
}

View File

@ -28,7 +28,14 @@ public function install(Server $server, array $input): void
'is_default' => false, 'is_default' => false,
]); ]);
$php->save(); $php->save();
$php->install();
dispatch(function () use ($php) {
$php->handler()->install();
$php->status = ServiceStatus::READY;
$php->save();
})->catch(function () use ($php) {
$php->delete();
})->onConnection('ssh');
} }
/** /**
@ -41,12 +48,12 @@ private function validate(Server $server, array $input): void
'required', 'required',
Rule::in(config('core.php_versions')), Rule::in(config('core.php_versions')),
], ],
])->validateWithBag('installPHP'); ])->validate();
if (in_array($input['version'], $server->installedPHPVersions())) { if (in_array($input['version'], $server->installedPHPVersions())) {
throw ValidationException::withMessages( throw ValidationException::withMessages(
['version' => __('This version is already installed')] ['version' => __('This version is already installed')]
)->errorBag('installPHP'); );
} }
} }
} }

View File

@ -2,25 +2,35 @@
namespace App\Actions\PHP; namespace App\Actions\PHP;
use App\Models\Server;
use App\Models\Service; use App\Models\Service;
use Illuminate\Support\Facades\Validator; use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rule;
use Illuminate\Validation\ValidationException; use Illuminate\Validation\ValidationException;
class InstallPHPExtension class InstallPHPExtension
{ {
/** public function install(Server $server, array $input): Service
* @throws ValidationException
*/
public function handle(Service $service, array $input): Service
{ {
$this->validate($server, $input);
/** @var Service $service */
$service = $server->php($input['version']);
$typeData = $service->type_data; $typeData = $service->type_data;
$typeData['extensions'] = $typeData['extensions'] ?? []; $typeData['extensions'] = $typeData['extensions'] ?? [];
$typeData['extensions'][] = $input['extension'];
$service->type_data = $typeData; $service->type_data = $typeData;
$service->save(); $service->save();
$this->validate($service, $input); dispatch(function () use ($service, $input) {
$service->handler()->installExtension($input['extension']);
$service->handler()->installExtension($input['name']); })->catch(function () use ($service, $input) {
$service->refresh();
$typeData = $service->type_data;
$typeData['extensions'] = array_values(array_diff($typeData['extensions'], [$input['extension']]));
$service->type_data = $typeData;
$service->save();
})->onConnection('ssh');
return $service; return $service;
} }
@ -28,18 +38,25 @@ public function handle(Service $service, array $input): Service
/** /**
* @throws ValidationException * @throws ValidationException
*/ */
private function validate(Service $service, array $input): void private function validate(Server $server, array $input): void
{ {
Validator::make($input, [ Validator::make($input, [
'name' => [ 'extension' => [
'required', 'required',
'in:'.implode(',', config('core.php_extensions')), 'in:'.implode(',', config('core.php_extensions')),
], ],
])->validateWithBag('installPHPExtension'); 'version' => [
'required',
Rule::in($server->installedPHPVersions()),
],
])->validate();
if (in_array($input['name'], $service->type_data['extensions'])) { /** @var Service $service */
$service = $server->php($input['version']);
if (in_array($input['extension'], $service->type_data['extensions'])) {
throw ValidationException::withMessages( throw ValidationException::withMessages(
['name' => __('This extension already installed')] ['extension' => __('This extension already installed')]
)->errorBag('installPHPExtension'); )->errorBag('installPHPExtension');
} }
} }

View File

@ -2,36 +2,48 @@
namespace App\Actions\PHP; namespace App\Actions\PHP;
use App\Enums\ServiceStatus;
use App\Models\Server; use App\Models\Server;
use App\Models\Service; use App\Models\Service;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\ValidationException; use Illuminate\Validation\ValidationException;
class UninstallPHP class UninstallPHP
{ {
public function uninstall(Server $server, string $version): void public function uninstall(Server $server, array $input): void
{ {
$this->validate($server, $version); $this->validate($server, $input);
/** @var Service $php */ /** @var Service $php */
$php = $server->services()->where('type', 'php')->where('version', $version)->first(); $php = $server->php($input['version']);
$php->status = ServiceStatus::UNINSTALLING;
$php->save();
$php->uninstall(); dispatch(function () use ($php) {
$php->handler()->uninstall();
$php->delete();
})->catch(function () use ($php) {
$php->status = ServiceStatus::FAILED;
$php->save();
})->onConnection('ssh');
} }
/** /**
* @throws ValidationException * @throws ValidationException
*/ */
private function validate(Server $server, string $version): void private function validate(Server $server, array $input): void
{ {
$php = $server->services()->where('type', 'php')->where('version', $version)->first(); Validator::make($input, [
'version' => 'required|string',
])->validate();
if (! $php) { if (! in_array($input['version'], $server->installedPHPVersions())) {
throw ValidationException::withMessages( throw ValidationException::withMessages(
['version' => __('This version has not been installed yet!')] ['version' => __('This version is not installed')]
); );
} }
$hasSite = $server->sites()->where('php_version', $version)->first(); $hasSite = $server->sites()->where('php_version', $input['version'])->first();
if ($hasSite) { if ($hasSite) {
throw ValidationException::withMessages( throw ValidationException::withMessages(
['version' => __('Cannot uninstall this version because some sites are using it!')] ['version' => __('Cannot uninstall this version because some sites are using it!')]

View File

@ -2,8 +2,9 @@
namespace App\Actions\PHP; namespace App\Actions\PHP;
use App\Models\Service; use App\Models\Server;
use Illuminate\Support\Facades\Storage; use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Str; use Illuminate\Support\Str;
use Illuminate\Validation\ValidationException; use Illuminate\Validation\ValidationException;
use Throwable; use Throwable;
@ -13,14 +14,18 @@ class UpdatePHPIni
/** /**
* @throws ValidationException * @throws ValidationException
*/ */
public function update(Service $service, string $ini): void public function update(Server $server, array $input): void
{ {
$this->validate($server, $input);
$service = $server->php($input['version']);
$tmpName = Str::random(10).strtotime('now'); $tmpName = Str::random(10).strtotime('now');
try { try {
/** @var \Illuminate\Filesystem\FilesystemAdapter $storageDisk */ /** @var \Illuminate\Filesystem\FilesystemAdapter $storageDisk */
$storageDisk = Storage::disk('local'); $storageDisk = Storage::disk('local');
$storageDisk->put($tmpName, $ini); $storageDisk->put($tmpName, $input['ini']);
$service->server->ssh('root')->upload( $service->server->ssh('root')->upload(
$storageDisk->path($tmpName), $storageDisk->path($tmpName),
"/etc/php/$service->version/cli/php.ini" "/etc/php/$service->version/cli/php.ini"
@ -42,4 +47,21 @@ private function deleteTempFile(string $name): void
Storage::disk('local')->delete($name); Storage::disk('local')->delete($name);
} }
} }
public function validate(Server $server, array $input): void
{
Validator::make($input, [
'ini' => [
'required',
'string',
],
'version' => 'required|string',
])->validate();
if (! in_array($input['version'], $server->installedPHPVersions())) {
throw ValidationException::withMessages(
['version' => __('This version is not installed')]
);
}
}
} }

View File

@ -8,11 +8,8 @@
class DeleteProject class DeleteProject
{ {
public function delete(User $user, int $projectId): void public function delete(User $user, Project $project): void
{ {
/** @var Project $project */
$project = $user->projects()->findOrFail($projectId);
if ($user->projects()->count() === 1) { if ($user->projects()->count() === 1) {
throw ValidationException::withMessages([ throw ValidationException::withMessages([
'project' => __('Cannot delete the last project.'), 'project' => __('Cannot delete the last project.'),

View File

@ -26,7 +26,7 @@ private function validate(Project $project, array $input): void
'required', 'required',
'string', 'string',
'max:255', 'max:255',
Rule::unique('projects')->ignore($project->id), Rule::unique('projects')->where('user_id', $project->user_id)->ignore($project->id),
], ],
])->validate(); ])->validate();
} }

View File

@ -29,7 +29,23 @@ public function create(mixed $queueable, array $input): void
'status' => QueueStatus::CREATING, 'status' => QueueStatus::CREATING,
]); ]);
$queue->save(); $queue->save();
$queue->deploy();
dispatch(function () use ($queue) {
$queue->server->processManager()->handler()->create(
$queue->id,
$queue->command,
$queue->user,
$queue->auto_start,
$queue->auto_restart,
$queue->numprocs,
$queue->getLogFile(),
$queue->site_id
);
$queue->status = QueueStatus::RUNNING;
$queue->save();
})->catch(function () use ($queue) {
$queue->delete();
})->onConnection('ssh');
} }
/** /**
@ -60,6 +76,6 @@ protected function validate(array $input): void
], ],
]; ];
Validator::make($input, $rules)->validateWithBag('createQueue'); Validator::make($input, $rules)->validate();
} }
} }

View File

@ -0,0 +1,14 @@
<?php
namespace App\Actions\Queue;
use App\Models\Queue;
class DeleteQueue
{
public function delete(Queue $queue): void
{
$queue->server->processManager()->handler()->delete($queue->id, $queue->site_id);
$queue->delete();
}
}

View File

@ -0,0 +1,13 @@
<?php
namespace App\Actions\Queue;
use App\Models\Queue;
class GetQueueLogs
{
public function getLogs(Queue $queue): string
{
return $queue->server->processManager()->handler()->getLogs($queue->getLogFile());
}
}

View File

@ -0,0 +1,42 @@
<?php
namespace App\Actions\Queue;
use App\Enums\QueueStatus;
use App\Models\Queue;
class ManageQueue
{
public function start(Queue $queue): void
{
$queue->status = QueueStatus::STARTING;
$queue->save();
dispatch(function () use ($queue) {
$queue->server->processManager()->handler()->start($queue->id, $queue->site_id);
$queue->status = QueueStatus::RUNNING;
$queue->save();
})->onConnection('ssh');
}
public function stop(Queue $queue): void
{
$queue->status = QueueStatus::STOPPING;
$queue->save();
dispatch(function () use ($queue) {
$queue->server->processManager()->handler()->stop($queue->id, $queue->site_id);
$queue->status = QueueStatus::STOPPED;
$queue->save();
})->onConnection('ssh');
}
public function restart(Queue $queue): void
{
$queue->status = QueueStatus::RESTARTING;
$queue->save();
dispatch(function () use ($queue) {
$queue->server->processManager()->handler()->restart($queue->id, $queue->site_id);
$queue->status = QueueStatus::RUNNING;
$queue->save();
})->onConnection('ssh');
}
}

View File

@ -2,8 +2,10 @@
namespace App\Actions\SSL; namespace App\Actions\SSL;
use App\Enums\SslStatus;
use App\Enums\SslType; use App\Enums\SslType;
use App\Models\Site; use App\Models\Site;
use App\Models\Ssl;
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;
@ -17,13 +19,24 @@ public function create(Site $site, array $input): void
{ {
$this->validate($input); $this->validate($input);
if ($input['type'] == SslType::LETSENCRYPT) { $ssl = new Ssl([
$site->createFreeSsl(); 'site_id' => $site->id,
} 'type' => $input['type'],
'certificate' => $input['certificate'] ?? null,
'pk' => $input['private'] ?? null,
'expires_at' => $input['type'] === SslType::LETSENCRYPT ? now()->addMonths(3) : $input['expires_at'],
'status' => SslStatus::CREATING,
]);
$ssl->save();
if ($input['type'] == SslType::CUSTOM) { dispatch(function () use ($site, $ssl) {
$site->createCustomSsl($input['certificate'], $input['private']); $site->server->webserver()->handler()->setupSSL($ssl);
} $ssl->status = SslStatus::CREATED;
$ssl->save();
$site->type()->edit();
})->catch(function () use ($ssl) {
$ssl->delete();
});
} }
/** /**
@ -34,14 +47,15 @@ protected function validate(array $input): void
$rules = [ $rules = [
'type' => [ 'type' => [
'required', 'required',
Rule::in(SslType::getValues()), Rule::in(config('core.ssl_types')),
], ],
]; ];
if (isset($input['type']) && $input['type'] == SslType::CUSTOM) { if (isset($input['type']) && $input['type'] == SslType::CUSTOM) {
$rules['certificate'] = 'required'; $rules['certificate'] = 'required';
$rules['private'] = 'required'; $rules['private'] = 'required';
$rules['expires_at'] = 'required|date_format:Y-m-d|after_or_equal:'.now();
} }
Validator::make($input, $rules)->validateWithBag('createSSL'); Validator::make($input, $rules)->validate();
} }
} }

View File

@ -0,0 +1,14 @@
<?php
namespace App\Actions\SSL;
use App\Models\Ssl;
class DeleteSSL
{
public function delete(Ssl $ssl): void
{
$ssl->site->server->webserver()->handler()->removeSSL($ssl);
$ssl->delete();
}
}

View File

@ -1,41 +0,0 @@
<?php
namespace App\Actions\Script;
use App\Models\Script;
use App\Models\User;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\ValidationException;
class CreateScript
{
/**
* @throws ValidationException
*/
public function handle(User $creator, array $input): Script
{
$this->validateInputs($input);
$script = new Script([
'user_id' => $creator->id,
'name' => $input['name'],
'content' => $input['content'],
]);
$script->save();
return $script;
}
/**
* @throws ValidationException
*/
private function validateInputs(array $input): void
{
$rules = [
'name' => 'required',
'content' => 'required',
];
Validator::make($input, $rules)->validateWithBag('createScript');
}
}

View File

@ -1,17 +0,0 @@
<?php
namespace App\Actions\Script;
use App\Models\User;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
class GetScripts
{
public function handle(User $user): LengthAwarePaginator
{
return $user->scripts()
->orderBy('id', 'desc')
->paginate(6)
->onEachSide(1);
}
}

View File

@ -1,37 +0,0 @@
<?php
namespace App\Actions\Script;
use App\Models\Script;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\ValidationException;
class UpdateScript
{
/**
* @throws ValidationException
*/
public function handle(Script $script, array $input): Script
{
$this->validateInputs($input);
$script->name = $input['name'];
$script->content = $input['content'];
$script->save();
return $script;
}
/**
* @throws ValidationException
*/
private function validateInputs(array $input): void
{
$rules = [
'name' => 'required',
'content' => 'required',
];
Validator::make($input, $rules)->validateWithBag('updateScript');
}
}

View File

@ -0,0 +1,30 @@
<?php
namespace App\Actions\Server;
use App\Facades\Notifier;
use App\Models\Server;
use App\Notifications\ServerDisconnected;
use Throwable;
class CheckConnection
{
public function check(Server $server): Server
{
$status = $server->status;
try {
$server->ssh()->connect();
$server->refresh();
if ($status == 'disconnected') {
$server->status = 'ready';
$server->save();
}
} catch (Throwable) {
$server->status = 'disconnected';
$server->save();
Notifier::send($server, new ServerDisconnected($server));
}
return $server;
}
}

View File

@ -3,13 +3,19 @@
namespace App\Actions\Server; namespace App\Actions\Server;
use App\Enums\FirewallRuleStatus; use App\Enums\FirewallRuleStatus;
use App\Enums\ServerProvider;
use App\Enums\ServerStatus;
use App\Exceptions\ServerProviderError; use App\Exceptions\ServerProviderError;
use App\Jobs\Installation\ContinueInstallation; use App\Facades\Notifier;
use App\Models\Server; use App\Models\Server;
use App\Models\User; use App\Models\User;
use App\Notifications\ServerInstallationFailed;
use App\Notifications\ServerInstallationSucceed;
use App\ValidationRules\RestrictedIPAddressesRule; use App\ValidationRules\RestrictedIPAddressesRule;
use Exception; use Exception;
use Illuminate\Support\Facades\Bus;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Validator; use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Str; use Illuminate\Support\Str;
use Illuminate\Validation\Rule; use Illuminate\Validation\Rule;
@ -30,7 +36,7 @@ public function create(User $creator, array $input): Server
'user_id' => $creator->id, 'user_id' => $creator->id,
'name' => $input['name'], 'name' => $input['name'],
'ssh_user' => config('core.server_providers_default_user')[$input['provider']][$input['os']], 'ssh_user' => config('core.server_providers_default_user')[$input['provider']][$input['os']],
'ip' => $input['ip'], 'ip' => $input['ip'] ?? '',
'port' => $input['port'] ?? 22, 'port' => $input['port'] ?? 22,
'os' => $input['os'], 'os' => $input['os'],
'type' => $input['type'], 'type' => $input['type'],
@ -71,14 +77,8 @@ public function create(User $creator, array $input): Server
$server->type()->createServices($input); $server->type()->createServices($input);
// install server // install server
if ($server->provider == 'custom') { $this->install($server);
$server->install();
} else {
$server->progress_step = __('Installation will begin in 3 minutes!');
$server->save();
dispatch(new ContinueInstallation($server))
->delay(now()->addMinutes(2));
}
DB::commit(); DB::commit();
return $server; return $server;
@ -88,12 +88,44 @@ public function create(User $creator, array $input): Server
if ($e instanceof ServerProviderError) { if ($e instanceof ServerProviderError) {
throw ValidationException::withMessages([ throw ValidationException::withMessages([
'provider' => __('Provider Error: ').$e->getMessage(), 'provider' => __('Provider Error: ').$e->getMessage(),
])->errorBag('createServer'); ]);
} }
throw $e; throw $e;
} }
} }
private function install(Server $server): void
{
$bus = Bus::chain([
function () use ($server) {
if (! $server->provider()->isRunning()) {
sleep(2);
}
$server->type()->install();
$server->update([
'status' => ServerStatus::READY,
]);
Notifier::send($server, new ServerInstallationSucceed($server));
},
])->catch(function (Throwable $e) use ($server) {
$server->update([
'status' => ServerStatus::INSTALLATION_FAILED,
]);
Notifier::send($server, new ServerInstallationFailed($server));
Log::error('server-installation-error', [
'error' => (string) $e,
]);
});
if ($server->provider != ServerProvider::CUSTOM) {
$server->progress_step = 'waiting-for-provider';
$server->save();
$bus->delay(now()->addMinutes(3));
}
$bus->onConnection('ssh')->dispatch();
}
/** /**
* @throws ValidationException * @throws ValidationException
*/ */
@ -136,7 +168,7 @@ private function validateInputs(array $input): void
*/ */
private function validateType(Server $server, array $input): void private function validateType(Server $server, array $input): void
{ {
Validator::make($input, $server->type()->createValidationRules($input)) Validator::make($input, $server->type()->createRules($input))
->validate(); ->validate();
} }
@ -145,7 +177,7 @@ private function validateType(Server $server, array $input): void
*/ */
private function validateProvider(Server $server, array $input): void private function validateProvider(Server $server, array $input): void
{ {
Validator::make($input, $server->provider()->createValidationRules($input)) Validator::make($input, $server->provider()->createRules($input))
->validate(); ->validate();
} }

View File

@ -35,7 +35,7 @@ public function edit(Server $server, array $input): Server
$server->save(); $server->save();
if ($checkConnection) { if ($checkConnection) {
$server->checkConnection(); return $server->checkConnection();
} }
return $server; return $server;

View File

@ -1,14 +0,0 @@
<?php
namespace App\Actions\Server;
use App\Models\Server;
use Illuminate\Database\Eloquent\Collection;
class GetServers
{
public function handle(): Collection
{
return Server::query()->latest()->get();
}
}

View File

@ -0,0 +1,23 @@
<?php
namespace App\Actions\Server;
use App\Enums\ServerStatus;
use App\Models\Server;
use Throwable;
class RebootServer
{
public function reboot(Server $server): Server
{
try {
$server->os()->reboot();
$server->status = ServerStatus::DISCONNECTED;
$server->save();
} catch (Throwable) {
$server = $server->checkConnection();
}
return $server;
}
}

View File

@ -2,9 +2,9 @@
namespace App\Actions\ServerProvider; namespace App\Actions\ServerProvider;
use App\Contracts\ServerProvider as ServerProviderContract;
use App\Models\ServerProvider; use App\Models\ServerProvider;
use App\Models\User; use App\Models\User;
use App\ServerProviders\ServerProvider as ServerProviderContract;
use Exception; use Exception;
use Illuminate\Support\Facades\Validator; use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rule; use Illuminate\Validation\Rule;
@ -28,7 +28,7 @@ public function create(User $user, array $input): ServerProvider
} catch (Exception) { } catch (Exception) {
throw ValidationException::withMessages([ throw ValidationException::withMessages([
'provider' => [ 'provider' => [
__("Couldn't connect to provider. Please check your credentials and try again later."), sprintf("Couldn't connect to %s. Please check your credentials.", $input['provider']),
], ],
]); ]);
} }

View File

@ -0,0 +1,21 @@
<?php
namespace App\Actions\ServerProvider;
use App\Models\ServerProvider;
use Exception;
class DeleteServerProvider
{
/**
* @throws Exception
*/
public function delete(ServerProvider $serverProvider): void
{
if ($serverProvider->servers()->exists()) {
throw new Exception('This server provider is being used by a server.');
}
$serverProvider->delete();
}
}

View File

@ -0,0 +1,55 @@
<?php
namespace App\Actions\Service;
use App\Enums\ServiceStatus;
use App\Models\Server;
use App\Models\Service;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rule;
class Create
{
public function create(Server $server, array $input): Service
{
$this->validate($server, $input);
$service = new Service([
'name' => $input['type'],
'type' => $input['type'],
'version' => $input['version'],
'status' => ServiceStatus::INSTALLING,
]);
Validator::make($input, $service->handler()->creationRules($input))->validate();
$service->type_data = $service->handler()->creationData($input);
$service->save();
$service->handler()->create();
dispatch(function () use ($service) {
$service->handler()->install();
$service->status = ServiceStatus::READY;
$service->save();
})->catch(function () use ($service) {
$service->handler()->delete();
$service->delete();
})->onConnection('ssh');
return $service;
}
private function validate(Server $server, array $input): void
{
Validator::make($input, [
'type' => [
'required',
Rule::in(config('core.add_on_services')),
Rule::unique('services', 'type')->where('server_id', $server->id),
],
'version' => 'required',
])->validate();
}
}

View File

@ -1,60 +0,0 @@
<?php
namespace App\Actions\Service;
use App\Enums\ServiceStatus;
use App\Models\Server;
use App\Models\Service;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\ValidationException;
class InstallPHPMyAdmin
{
/**
* @throws ValidationException
*/
public function install(Server $server, array $input): Service
{
$this->validate($input);
$phpMyAdmin = $server->defaultService('phpmyadmin');
if ($phpMyAdmin) {
throw ValidationException::withMessages([
'allowed_ip' => __('Already installed'),
]);
}
$phpMyAdmin = new Service([
'server_id' => $server->id,
'type' => 'phpmyadmin',
'type_data' => [
'allowed_ip' => $input['allowed_ip'],
'port' => $input['port'],
'php' => $server->defaultService('php')->version,
],
'name' => 'phpmyadmin',
'version' => '5.1.2',
'status' => ServiceStatus::INSTALLING,
'is_default' => 1,
]);
$phpMyAdmin->save();
$phpMyAdmin->install();
return $phpMyAdmin;
}
/**
* @throws ValidationException
*/
private function validate(array $input): void
{
Validator::make($input, [
'allowed_ip' => 'required',
'port' => [
'required',
'numeric',
'min:1',
'max:65535',
],
])->validate();
}
}

View File

@ -0,0 +1,84 @@
<?php
namespace App\Actions\Service;
use App\Enums\ServiceStatus;
use App\Models\Service;
class Manage
{
public function start(Service $service): void
{
$service->status = ServiceStatus::STARTING;
$service->save();
dispatch(function () use ($service) {
$status = $service->server->systemd()->start($service->unit);
if (str($status)->contains('Active: active')) {
$service->status = ServiceStatus::READY;
} else {
$service->status = ServiceStatus::FAILED;
}
$service->save();
})->onConnection('ssh');
}
public function stop(Service $service): void
{
$service->status = ServiceStatus::STOPPING;
$service->save();
dispatch(function () use ($service) {
$status = $service->server->systemd()->stop($service->unit);
if (str($status)->contains('Active: inactive')) {
$service->status = ServiceStatus::STOPPED;
} else {
$service->status = ServiceStatus::FAILED;
}
$service->save();
})->onConnection('ssh');
}
public function restart(Service $service): void
{
$service->status = ServiceStatus::RESTARTING;
$service->save();
dispatch(function () use ($service) {
$status = $service->server->systemd()->restart($service->unit);
if (str($status)->contains('Active: active')) {
$service->status = ServiceStatus::READY;
} else {
$service->status = ServiceStatus::FAILED;
}
$service->save();
})->onConnection('ssh');
}
public function enable(Service $service): void
{
$service->status = ServiceStatus::ENABLING;
$service->save();
dispatch(function () use ($service) {
$status = $service->server->systemd()->enable($service->unit);
if (str($status)->contains('Active: active')) {
$service->status = ServiceStatus::READY;
} else {
$service->status = ServiceStatus::FAILED;
}
$service->save();
})->onConnection('ssh');
}
public function disable(Service $service): void
{
$service->status = ServiceStatus::DISABLING;
$service->save();
dispatch(function () use ($service) {
$status = $service->server->systemd()->disable($service->unit);
if (str($status)->contains('Active: inactive')) {
$service->status = ServiceStatus::DISABLED;
} else {
$service->status = ServiceStatus::FAILED;
}
$service->save();
})->onConnection('ssh');
}
}

View File

@ -1,30 +0,0 @@
<?php
namespace App\Actions\Site;
use App\Models\Site;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\ValidationException;
class ChangePHPVersion
{
/**
* @throws ValidationException
*/
public function handle(Site $site, array $input): void
{
$this->validate($site, $input);
$site->changePHPVersion($input['php_version']);
}
/**
* @throws ValidationException
*/
protected function validate(Site $site, array $input): void
{
Validator::make($input, [
'php_version' => 'required|in:'.implode(',', $site->server->installedPHPVersions()),
])->validateWithBag('changePHPVersion');
}
}

View File

@ -1,51 +0,0 @@
<?php
namespace App\Actions\Site;
use App\Models\Redirect;
use App\Models\Site;
use Exception;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\ValidationException;
class CreateRedirect
{
/**
* @throws Exception
*/
public function handle(Site $site, array $input): void
{
$this->validate($input);
$redirect = new Redirect([
'site_id' => $site->id,
'mode' => $input['mode'],
'from' => $input['from'],
'to' => $input['to'],
]);
$redirect->save();
$redirect->addToServer();
}
/**
* @throws ValidationException
*/
private function validate(array $input): void
{
$rules = [
'mode' => [
'required',
'in:301,302',
],
'from' => [
'required',
],
'to' => [
'required',
'url',
],
];
Validator::make($input, $rules)->validateWithBag('createRedirect');
}
}

View File

@ -4,8 +4,11 @@
use App\Enums\SiteStatus; use App\Enums\SiteStatus;
use App\Exceptions\SourceControlIsNotConnected; use App\Exceptions\SourceControlIsNotConnected;
use App\Facades\Notifier;
use App\Models\Server; use App\Models\Server;
use App\Models\Site; use App\Models\Site;
use App\Notifications\SiteInstallationFailed;
use App\Notifications\SiteInstallationSucceed;
use App\ValidationRules\DomainRule; use App\ValidationRules\DomainRule;
use Exception; use Exception;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
@ -30,11 +33,11 @@ public function create(Server $server, array $input): Site
'type' => $input['type'], 'type' => $input['type'],
'domain' => $input['domain'], 'domain' => $input['domain'],
'aliases' => isset($input['alias']) ? [$input['alias']] : [], 'aliases' => isset($input['alias']) ? [$input['alias']] : [],
'path' => '/home/'.$server->ssh_user.'/'.$input['domain'], 'path' => '/home/'.$server->getSshUser().'/'.$input['domain'],
'status' => SiteStatus::INSTALLING, 'status' => SiteStatus::INSTALLING,
]); ]);
// fields based on type // fields based on the type
$site->fill($site->type()->createFields($input)); $site->fill($site->type()->createFields($input));
// check has access to repository // check has access to repository
@ -63,8 +66,19 @@ public function create(Server $server, array $input): Site
'content' => '', 'content' => '',
]); ]);
// install server // install site
$site->install(); dispatch(function () use ($site) {
$site->type()->install();
$site->update([
'status' => SiteStatus::READY,
'progress' => 100,
]);
Notifier::send($site, new SiteInstallationSucceed($site));
})->catch(function () use ($site) {
$site->status = SiteStatus::INSTALLATION_FAILED;
$site->save();
Notifier::send($site, new SiteInstallationFailed($site));
})->onConnection('ssh');
DB::commit(); DB::commit();
@ -105,7 +119,7 @@ private function validateInputs(Server $server, array $input): void
*/ */
private function validateType(Site $site, array $input): void private function validateType(Site $site, array $input): void
{ {
$rules = $site->type()->createValidationRules($input); $rules = $site->type()->createRules($input);
Validator::make($input, $rules)->validate(); Validator::make($input, $rules)->validate();
} }

View File

@ -0,0 +1,14 @@
<?php
namespace App\Actions\Site;
use App\Models\Site;
class DeleteSite
{
public function delete(Site $site): void
{
$site->server->webserver()->handler()->deleteSite($site);
$site->delete();
}
}

View File

@ -0,0 +1,51 @@
<?php
namespace App\Actions\Site;
use App\Enums\DeploymentStatus;
use App\Exceptions\DeploymentScriptIsEmptyException;
use App\Exceptions\SourceControlIsNotConnected;
use App\Models\Deployment;
use App\Models\Site;
class Deploy
{
/**
* @throws SourceControlIsNotConnected
* @throws DeploymentScriptIsEmptyException
*/
public function run(Site $site): Deployment
{
if ($site->sourceControl()) {
$site->sourceControl()->getRepo($site->repository);
}
if (! $site->deploymentScript?->content) {
throw new DeploymentScriptIsEmptyException();
}
$deployment = new Deployment([
'site_id' => $site->id,
'deployment_script_id' => $site->deploymentScript->id,
'status' => DeploymentStatus::DEPLOYING,
]);
$lastCommit = $site->sourceControl()->provider()->getLastCommit($site->repository, $site->branch);
if ($lastCommit) {
$deployment->commit_id = $lastCommit['commit_id'];
$deployment->commit_data = $lastCommit['commit_data'];
}
$deployment->save();
dispatch(function () use ($site, $deployment) {
$log = $site->server->os()->runScript($site->path, $site->deploymentScript->content, $site->id);
$deployment->status = DeploymentStatus::FINISHED;
$deployment->log_id = $log->id;
$deployment->save();
})->catch(function () use ($deployment) {
$deployment->status = DeploymentStatus::FAILED;
$deployment->save();
})->onConnection('ssh');
return $deployment;
}
}

View File

@ -1,41 +0,0 @@
<?php
namespace App\Actions\Site;
use App\Models\Site;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\ValidationException;
class EditSite
{
/**
* @throws ValidationException
*/
public function handle(Site $site, array $input): Site
{
// validate type
$this->validateType($site, $input);
// set type data
$site->type_data = $site->type()->data($input);
// save
$site->port = $input['port'] ?? null;
$site->save();
// edit
$site->type()->edit();
return $site;
}
/**
* @throws ValidationException
*/
private function validateType(Site $site, array $input): void
{
$rules = $site->type()->editValidationRules($input);
Validator::make($input, $rules)->validateWithBag('editSite');
}
}

View File

@ -1,14 +0,0 @@
<?php
namespace App\Actions\Site;
use App\Models\Server;
use Illuminate\Database\Eloquent\Collection;
class GetSites
{
public function handle(Server $server): Collection
{
return $server->sites()->orderBy('id', 'desc')->get();
}
}

View File

@ -3,6 +3,7 @@
namespace App\Actions\Site; namespace App\Actions\Site;
use App\Models\Site; use App\Models\Site;
use App\SSH\Git\Git;
use Illuminate\Support\Facades\Validator; use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\ValidationException; use Illuminate\Validation\ValidationException;
@ -14,8 +15,9 @@ class UpdateBranch
public function update(Site $site, array $input): void public function update(Site $site, array $input): void
{ {
$this->validate($input); $this->validate($input);
$site->branch = $input['branch'];
$site->updateBranch($input['branch']); app(Git::class)->checkout($site);
$site->save();
} }
/** /**
@ -25,6 +27,6 @@ protected function validate(array $input): void
{ {
Validator::make($input, [ Validator::make($input, [
'branch' => 'required', 'branch' => 'required',
])->validateWithBag('updateBranch'); ]);
} }
} }

View File

@ -27,6 +27,6 @@ protected function validate(array $input): void
{ {
Validator::make($input, [ Validator::make($input, [
'script' => 'required', 'script' => 'required',
])->validateWithBag('updateDeploymentScript'); ]);
} }
} }

View File

@ -8,11 +8,9 @@ class UpdateEnv
{ {
public function update(Site $site, array $input): void public function update(Site $site, array $input): void
{ {
$typeData = $site->type_data; $site->server->os()->editFile(
$typeData['env'] = $input['env']; $site->path.'/.env',
$site->type_data = $typeData; $input['env']
$site->save(); );
$site->deployEnv();
} }
} }

View File

@ -1,35 +0,0 @@
<?php
namespace App\Actions\Site;
use App\Models\Site;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rule;
use Illuminate\Validation\ValidationException;
class UpdateSourceControl
{
/**
* @throws ValidationException
*/
public function update(Site $site, array $input): void
{
$this->validate($input);
$site->source_control_id = $input['source_control'];
$site->save();
}
/**
* @throws ValidationException
*/
protected function validate(array $input): void
{
Validator::make($input, [
'source_control' => [
'required',
Rule::exists('source_controls', 'id'),
],
])->validate();
}
}

View File

@ -13,13 +13,17 @@ class ConnectSourceControl
public function connect(array $input): void public function connect(array $input): void
{ {
$this->validate($input); $this->validate($input);
$sourceControl = new SourceControl([ $sourceControl = new SourceControl([
'provider' => $input['provider'], 'provider' => $input['provider'],
'profile' => $input['name'], 'profile' => $input['name'],
'access_token' => $input['token'],
'url' => Arr::has($input, 'url') ? $input['url'] : null, 'url' => Arr::has($input, 'url') ? $input['url'] : null,
]); ]);
$this->validateProvider($sourceControl, $input);
$sourceControl->provider_data = $sourceControl->provider()->createData($input);
if (! $sourceControl->provider()->connect()) { if (! $sourceControl->provider()->connect()) {
throw ValidationException::withMessages([ throw ValidationException::withMessages([
'token' => __('Cannot connect to :provider or invalid token!', ['provider' => $sourceControl->provider] 'token' => __('Cannot connect to :provider or invalid token!', ['provider' => $sourceControl->provider]
@ -38,20 +42,20 @@ private function validate(array $input): void
$rules = [ $rules = [
'provider' => [ 'provider' => [
'required', 'required',
Rule::in(\App\Enums\SourceControl::getValues()), Rule::in(config('core.source_control_providers')),
], ],
'name' => [ 'name' => [
'required', 'required',
], ],
'token' => [
'required',
],
'url' => [
'nullable',
'url:http,https',
'ends_with:/',
],
]; ];
Validator::make($input, $rules)->validate(); Validator::make($input, $rules)->validate();
} }
/**
* @throws ValidationException
*/
private function validateProvider(SourceControl $sourceControl, array $input): void
{
Validator::make($input, $sourceControl->provider()->createRules($input))->validate();
}
} }

View File

@ -0,0 +1,17 @@
<?php
namespace App\Actions\SourceControl;
use App\Models\SourceControl;
class DeleteSourceControl
{
public function delete(SourceControl $sourceControl): void
{
if ($sourceControl->sites()->exists()) {
throw new \Exception('This source control is being used by a site.');
}
$sourceControl->delete();
}
}

View File

@ -0,0 +1,19 @@
<?php
namespace App\Actions\SshKey;
use App\Enums\SshKeyStatus;
use App\Models\Server;
use App\Models\SshKey;
class DeleteKeyFromServer
{
public function delete(Server $server, SshKey $sshKey): void
{
$sshKey->servers()->updateExistingPivot($server->id, [
'status' => SshKeyStatus::DELETING,
]);
$server->os()->deleteSSHKey($sshKey->public_key);
$server->sshKeys()->detach($sshKey);
}
}

View File

@ -0,0 +1,38 @@
<?php
namespace App\Actions\SshKey;
use App\Enums\SshKeyStatus;
use App\Models\Server;
use App\Models\SshKey;
use App\Models\User;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rule;
class DeployKeyToServer
{
public function deploy(User $user, Server $server, array $input): void
{
$this->validate($user, $input);
/** @var SshKey $sshKey */
$sshKey = SshKey::findOrFail($input['key_id']);
$server->sshKeys()->attach($sshKey, [
'status' => SshKeyStatus::ADDING,
]);
$server->os()->deploySSHKey($sshKey->public_key);
$sshKey->servers()->updateExistingPivot($server->id, [
'status' => SshKeyStatus::ADDED,
]);
}
private function validate(User $user, array $input): void
{
Validator::make($input, [
'key_id' => [
'required',
Rule::exists('ssh_keys', 'id')->where('user_id', $user->id),
],
])->validate();
}
}

View File

@ -27,11 +27,18 @@ public function create(User $user, array $input): void
$storageProvider->credentials = $storageProvider->provider()->credentialData($input); $storageProvider->credentials = $storageProvider->provider()->credentialData($input);
try {
if (! $storageProvider->provider()->connect()) { if (! $storageProvider->provider()->connect()) {
throw ValidationException::withMessages([ throw ValidationException::withMessages([
'provider' => __("Couldn't connect to the provider"), 'provider' => __("Couldn't connect to the provider"),
]); ]);
} }
} catch (\Throwable $e) {
throw ValidationException::withMessages([
'provider' => $e->getMessage(),
]);
}
$storageProvider->save(); $storageProvider->save();
} }

View File

@ -0,0 +1,21 @@
<?php
namespace App\Actions\StorageProvider;
use App\Models\StorageProvider;
use Exception;
class DeleteStorageProvider
{
/**
* @throws Exception
*/
public function delete(StorageProvider $storageProvider): void
{
if ($storageProvider->backups()->exists()) {
throw new Exception('This storage provider is being used by a backup.');
}
$storageProvider->delete();
}
}

View File

@ -0,0 +1,74 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\File;
class MigrateFromMysqlToSqlite extends Command
{
protected $signature = 'migrate-from-mysql-to-sqlite';
protected $description = 'Migrate from Mysql to SQLite';
public function handle(): void
{
$this->info('Migrating from Mysql to SQLite...');
File::exists(storage_path('database.sqlite'))
? File::delete(storage_path('database.sqlite'))
: null;
File::put(storage_path('database.sqlite'), '');
config(['database.default' => 'sqlite']);
$this->call('migrate', ['--force' => true]);
$this->migrateModel(\App\Models\Backup::class);
$this->migrateModel(\App\Models\BackupFile::class);
$this->migrateModel(\App\Models\CronJob::class);
$this->migrateModel(\App\Models\Database::class);
$this->migrateModel(\App\Models\DatabaseUser::class);
$this->migrateModel(\App\Models\Deployment::class);
$this->migrateModel(\App\Models\DeploymentScript::class);
$this->migrateModel(\App\Models\FirewallRule::class);
$this->migrateModel(\App\Models\GitHook::class);
$this->migrateModel(\App\Models\NotificationChannel::class);
$this->migrateModel(\App\Models\Project::class);
$this->migrateModel(\App\Models\Queue::class);
$this->migrateModel(\App\Models\Server::class);
$this->migrateModel(\App\Models\ServerLog::class);
$this->migrateModel(\App\Models\ServerProvider::class);
$this->migrateModel(\App\Models\Service::class);
$this->migrateModel(\App\Models\Site::class);
$this->migrateModel(\App\Models\SourceControl::class);
$this->migrateModel(\App\Models\SshKey::class);
$this->migrateModel(\App\Models\Ssl::class);
$this->migrateModel(\App\Models\StorageProvider::class);
$this->migrateModel(\App\Models\User::class);
$env = File::get(base_path('.env'));
$env = str_replace('DB_CONNECTION=mysql', 'DB_CONNECTION=sqlite', $env);
$env = str_replace('DB_DATABASE=vito', '', $env);
File::put(base_path('.env'), $env);
$this->info('Migrated from Mysql to SQLite');
}
private function migrateModel(string $model): void
{
$this->info("Migrating model: {$model}");
config(['database.default' => 'mysql']);
$rows = $model::where('id', '>', 0)->get();
foreach ($rows as $row) {
DB::connection('sqlite')->table($row->getTable())->insert($row->getAttributes());
}
$this->info("Migrated model: {$model}");
}
}

View File

@ -1,49 +0,0 @@
<?php
namespace App\Console\Commands;
use App\Models\Backup;
use Illuminate\Console\Command;
class RunBackup extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'backups:run {interval}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Run backup';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*/
public function handle(): void
{
Backup::query()
->where('interval', $this->argument('interval'))
->where('status', 'running')
->chunk(100, function ($backups) {
/** @var Backup $backup */
foreach ($backups as $backup) {
$backup->run();
}
});
}
}

View File

@ -0,0 +1,33 @@
<?php
namespace App\Console\Commands;
use App\Actions\Database\RunBackup;
use App\Enums\BackupStatus;
use App\Models\Backup;
use Illuminate\Console\Command;
class RunBackupCommand extends Command
{
protected $signature = 'backups:run {interval}';
protected $description = 'Run backup';
public function handle(): void
{
$total = 0;
Backup::query()
->where('interval', $this->argument('interval'))
->where('status', BackupStatus::RUNNING)
->chunk(100, function ($backups) use (&$total) {
/** @var Backup $backup */
foreach ($backups as $backup) {
app(RunBackup::class)->run($backup);
$total++;
}
});
$this->info("{$total} backups started");
}
}

View File

@ -1,10 +0,0 @@
<?php
namespace App\Contracts;
interface SSHCommand
{
public function file(): string;
public function content(): string;
}

View File

@ -2,9 +2,7 @@
namespace App\Enums; namespace App\Enums;
use BenSampo\Enum\Enum; final class BackupFileStatus
final class BackupFileStatus extends Enum
{ {
const CREATED = 'created'; const CREATED = 'created';

View File

@ -2,9 +2,7 @@
namespace App\Enums; namespace App\Enums;
use BenSampo\Enum\Enum; final class BackupStatus
final class BackupStatus extends Enum
{ {
const READY = 'ready'; const READY = 'ready';

View File

@ -2,9 +2,7 @@
namespace App\Enums; namespace App\Enums;
use BenSampo\Enum\Enum; final class CronjobStatus
final class CronjobStatus extends Enum
{ {
const CREATING = 'creating'; const CREATING = 'creating';

View File

@ -2,9 +2,7 @@
namespace App\Enums; namespace App\Enums;
use BenSampo\Enum\Enum; final class Database
final class Database extends Enum
{ {
const NONE = 'none'; const NONE = 'none';
@ -12,5 +10,17 @@ final class Database extends Enum
const MYSQL80 = 'mysql80'; const MYSQL80 = 'mysql80';
const MARIADB = 'mariadb'; const MARIADB103 = 'mariadb103';
const MARIADB104 = 'mariadb104';
const POSTGRESQL12 = 'postgresql12';
const POSTGRESQL13 = 'postgresql13';
const POSTGRESQL14 = 'postgresql14';
const POSTGRESQL15 = 'postgresql15';
const POSTGRESQL16 = 'postgresql16';
} }

View File

@ -2,9 +2,7 @@
namespace App\Enums; namespace App\Enums;
use BenSampo\Enum\Enum; final class DatabaseStatus
final class DatabaseStatus extends Enum
{ {
const READY = 'ready'; const READY = 'ready';

View File

@ -2,9 +2,7 @@
namespace App\Enums; namespace App\Enums;
use BenSampo\Enum\Enum; final class DatabaseUserStatus
final class DatabaseUserStatus extends Enum
{ {
const READY = 'ready'; const READY = 'ready';

View File

@ -2,9 +2,7 @@
namespace App\Enums; namespace App\Enums;
use BenSampo\Enum\Enum; final class DeploymentStatus
final class DeploymentStatus extends Enum
{ {
const DEPLOYING = 'deploying'; const DEPLOYING = 'deploying';

View File

@ -2,9 +2,7 @@
namespace App\Enums; namespace App\Enums;
use BenSampo\Enum\Enum; final class FirewallRuleStatus
final class FirewallRuleStatus extends Enum
{ {
const CREATING = 'creating'; const CREATING = 'creating';

View File

@ -2,9 +2,7 @@
namespace App\Enums; namespace App\Enums;
use BenSampo\Enum\Enum; final class LogType
final class LogType extends Enum
{ {
const SERVER = 'server'; const SERVER = 'server';

View File

@ -2,9 +2,7 @@
namespace App\Enums; namespace App\Enums;
use BenSampo\Enum\Enum; final class NotificationChannel
final class NotificationChannel extends Enum
{ {
const EMAIL = 'email'; const EMAIL = 'email';

View File

@ -2,9 +2,7 @@
namespace App\Enums; namespace App\Enums;
use BenSampo\Enum\Enum; final class OperatingSystem
final class OperatingSystem extends Enum
{ {
const UBUNTU18 = 'ubuntu_18'; const UBUNTU18 = 'ubuntu_18';

View File

@ -2,9 +2,7 @@
namespace App\Enums; namespace App\Enums;
use BenSampo\Enum\Enum; final class QueueStatus
final class QueueStatus extends Enum
{ {
const RUNNING = 'running'; const RUNNING = 'running';

View File

@ -2,9 +2,7 @@
namespace App\Enums; namespace App\Enums;
use BenSampo\Enum\Enum; final class ServerProvider
final class ServerProvider extends Enum
{ {
const CUSTOM = 'custom'; const CUSTOM = 'custom';

View File

@ -2,9 +2,7 @@
namespace App\Enums; namespace App\Enums;
use BenSampo\Enum\Enum; final class ServerStatus
final class ServerStatus extends Enum
{ {
const READY = 'ready'; const READY = 'ready';

View File

@ -2,9 +2,7 @@
namespace App\Enums; namespace App\Enums;
use BenSampo\Enum\Enum; final class ServerType
final class ServerType extends Enum
{ {
const REGULAR = 'regular'; const REGULAR = 'regular';

View File

@ -2,9 +2,7 @@
namespace App\Enums; namespace App\Enums;
use BenSampo\Enum\Enum; final class ServiceStatus
final class ServiceStatus extends Enum
{ {
const READY = 'ready'; const READY = 'ready';
@ -23,4 +21,10 @@ final class ServiceStatus extends Enum
const RESTARTING = 'restarting'; const RESTARTING = 'restarting';
const STOPPED = 'stopped'; const STOPPED = 'stopped';
const ENABLING = 'enabling';
const DISABLING = 'disabling';
const DISABLED = 'disabled';
} }

View File

@ -2,9 +2,7 @@
namespace App\Enums; namespace App\Enums;
use BenSampo\Enum\Enum; final class SiteFeature
final class SiteFeature extends Enum
{ {
const DEPLOYMENT = 'deployment'; const DEPLOYMENT = 'deployment';

View File

@ -2,9 +2,7 @@
namespace App\Enums; namespace App\Enums;
use BenSampo\Enum\Enum; final class SiteStatus
final class SiteStatus extends Enum
{ {
const READY = 'ready'; const READY = 'ready';

View File

@ -2,9 +2,7 @@
namespace App\Enums; namespace App\Enums;
use BenSampo\Enum\Enum; final class SiteType
final class SiteType extends Enum
{ {
const PHP = 'php'; const PHP = 'php';
@ -13,4 +11,6 @@ final class SiteType extends Enum
const LARAVEL = 'laravel'; const LARAVEL = 'laravel';
const WORDPRESS = 'wordpress'; const WORDPRESS = 'wordpress';
const PHPMYADMIN = 'phpmyadmin';
} }

View File

@ -2,9 +2,7 @@
namespace App\Enums; namespace App\Enums;
use BenSampo\Enum\Enum; final class SourceControl
final class SourceControl extends Enum
{ {
const GITHUB = 'github'; const GITHUB = 'github';

View File

@ -2,9 +2,7 @@
namespace App\Enums; namespace App\Enums;
use BenSampo\Enum\Enum; final class SshKeyStatus
final class SshKeyStatus extends Enum
{ {
const ADDING = 'adding'; const ADDING = 'adding';

View File

@ -2,9 +2,7 @@
namespace App\Enums; namespace App\Enums;
use BenSampo\Enum\Enum; final class SslStatus
final class SslStatus extends Enum
{ {
const CREATED = 'created'; const CREATED = 'created';

View File

@ -2,9 +2,7 @@
namespace App\Enums; namespace App\Enums;
use BenSampo\Enum\Enum; final class SslType
final class SslType extends Enum
{ {
const LETSENCRYPT = 'letsencrypt'; const LETSENCRYPT = 'letsencrypt';

View File

@ -2,9 +2,7 @@
namespace App\Enums; namespace App\Enums;
use BenSampo\Enum\Enum; final class StorageProvider
final class StorageProvider extends Enum
{ {
const DROPBOX = 'dropbox'; const DROPBOX = 'dropbox';

View File

@ -2,9 +2,7 @@
namespace App\Enums; namespace App\Enums;
use BenSampo\Enum\Enum; final class Webserver
final class Webserver extends Enum
{ {
const NONE = 'none'; const NONE = 'none';

View File

@ -1,16 +0,0 @@
<?php
namespace App\Events;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class Broadcast
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public function __construct(public string $type, public array $data)
{
}
}

View File

@ -1,9 +0,0 @@
<?php
namespace App\Exceptions;
use Exception;
class BackupFileException extends Exception
{
}

View File

@ -1,10 +0,0 @@
<?php
namespace App\Exceptions;
use Exception;
class CannotDeployKey extends Exception
{
//
}

View File

@ -1,10 +0,0 @@
<?php
namespace App\Exceptions;
use Exception;
class ComposerInstallFailed extends Exception
{
//
}

Some files were not shown because too many files have changed in this diff Show More