This commit is contained in:
Saeed Vaziry 2023-07-02 12:47:50 +02:00
commit 5c72f12490
825 changed files with 41659 additions and 0 deletions

18
.editorconfig Normal file
View File

@ -0,0 +1,18 @@
root = true
[*]
charset = utf-8
end_of_line = lf
indent_size = 4
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true
[*.md]
trim_trailing_whitespace = false
[*.{yml,yaml}]
indent_size = 2
[docker-compose.yml]
indent_size = 4

75
.env.example Executable file
View File

@ -0,0 +1,75 @@
APP_NAME=Vito
APP_ENV=local
APP_KEY=
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
DB_USERNAME=root
DB_PASSWORD=
BROADCAST_DRIVER=log
CACHE_DRIVER=file
FILESYSTEM_DRIVER=local
QUEUE_CONNECTION=sync
SESSION_DRIVER=database
SESSION_LIFETIME=120
MEMCACHED_HOST=127.0.0.1
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
MAIL_MAILER=smtp
MAIL_HOST=mailhog
MAIL_PORT=1025
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS=null
MAIL_FROM_NAME="${APP_NAME}"
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_DEFAULT_REGION=us-east-1
AWS_BUCKET=
AWS_USE_PATH_STYLE_ENDPOINT=false
PUSHER_APP_ID=
PUSHER_APP_KEY=
PUSHER_APP_SECRET=
PUSHER_APP_CLUSTER=mt1
MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
FORWARD_REDIS_PORT=2060
FORWARD_DB_PORT=2070
APP_PORT=2080
HMR_PORT=2090
SENTRY_LARAVEL_DSN=
APP_SERVICE=vito
GITHUB_CLIENT_ID=
GITHUB_CLIENT_SECRET=
ABLY_KEY=
ABLY_PUBLIC_KEY=
STRIPE_KEY=pk_test_NndNPdqsaU772F7rGf9VwMBZ00QTxVOAZI
STRIPE_SECRET=sk_test_nXrcZWPhxtzVLDWo3qzk5O2e00ygZPvrAJ
STRIPE_STANDARD_MONTHLY=price_1KJelHLZWr04YoNXo85HB9L6
STRIPE_STANDARD_ANNUALLY=price_1KJelHLZWr04YoNX5JzxilBB
STRIPE_BUSINESS_MONTHLY=price_1KJelHLZWr04YoNX6rdAxIEA
STRIPE_BUSINESS_ANNUALLY=price_1KJelHLZWr04YoNXAoBOSLs9
#STRIPE_UNLIMITED_MONTHLY=price_1KJelHLZWr04YoNXDRvMP511
#STRIPE_UNLIMITED_ANNUALLY=price_1KJelHLZWr04YoNXG4Ad4Bjp

41
.env.testing Executable file
View File

@ -0,0 +1,41 @@
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=mysql
DB_PORT=3306
DB_DATABASE=testing
DB_USERNAME=root
DB_PASSWORD=password
BROADCAST_DRIVER=pusher
CACHE_DRIVER=array
FILESYSTEM_DRIVER=local
QUEUE_CONNECTION=database
SESSION_DRIVER=array
SESSION_LIFETIME=120
MAIL_MAILER=array
PUSHER_APP_ID=app-id
PUSHER_APP_KEY=app-key
PUSHER_APP_SECRET=app-secret
PUSHER_HOST=soketi
PUSHER_PORT=6001
PUSHER_SCHEME=http
PUSHER_APP_CLUSTER=mt1
VITE_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
VITE_PUSHER_HOST="${PUSHER_HOST}"
VITE_PUSHER_PORT="${PUSHER_PORT}"
VITE_PUSHER_SCHEME="${PUSHER_SCHEME}"
VITE_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
SSH_PUBLIC_KEY_NAME=ssh-public.key
SSH_PRIVATE_KEY_NAME=ssh-private.pem

11
.gitattributes vendored Normal file
View File

@ -0,0 +1,11 @@
* text=auto eol=lf
*.blade.php diff=html
*.css diff=css
*.html diff=html
*.md diff=markdown
*.php diff=php
/.github export-ignore
CHANGELOG.md export-ignore
.styleci.yml export-ignore

20
.gitignore vendored Normal file
View File

@ -0,0 +1,20 @@
/.phpunit.cache
/node_modules
/public/build
/public/hot
/public/storage
/storage/*.key
/storage/*.pem
/vendor
.env
.env.backup
.env.production
.phpunit.result.cache
Homestead.json
Homestead.yaml
auth.json
npm-debug.log
yarn-error.log
/.fleet
/.idea
/.vscode

5
Makefile Normal file
View File

@ -0,0 +1,5 @@
start:
./sail up
stop:
./sail down

1
README.md Normal file
View File

@ -0,0 +1 @@
# Vito

View File

@ -0,0 +1,85 @@
<?php
namespace App\Actions\Backup;
use App\Enums\DatabaseStatus;
use App\Models\Backup;
use App\Models\Database;
use App\Models\Server;
use App\Models\User;
use Illuminate\Auth\Access\AuthorizationException;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rule;
use Illuminate\Validation\ValidationException;
class CreateBackup
{
/**
* @throws AuthorizationException
* @throws ValidationException
*/
public function create($type, Server $server, User $user, array $input): Backup
{
$this->validate($type, $server, $user, $input);
if ($type == 'database') {
Gate::forUser($user)->authorize('viewAny', [Database::class, $server]);
}
$backup = new Backup([
'name' => $input['name'],
'type' => $type,
'server_id' => $server->id,
'database_id' => $input['database'] ?? null,
'storage_id' => $input['storage'],
'interval' => $input['interval'],
'keep_backups' => $input['keep_backups'],
'status' => 'running',
]);
$backup->save();
$backup->run();
return $backup;
}
/**
* @throws ValidationException
*/
private function validate($type, Server $server, User $user, array $input): void
{
$rules = [
'name' => 'required',
'storage' => [
'required',
Rule::exists('storage_providers', 'id')
->where('user_id', $user->id)
->where('connected', 1),
],
'keep_backups' => [
'required',
'numeric',
'min:1',
],
'interval' => [
'required',
Rule::in([
'0 * * * *',
'0 0 * * *',
'0 0 * * 0',
'0 0 1 * *',
]),
],
];
if ($type === 'database') {
$rules['database'] = [
'required',
Rule::exists('databases', 'id')
->where('server_id', $server->id)
->where('status', DatabaseStatus::READY),
];
}
Validator::make($input, $rules)->validateWithBag('createBackup');
}
}

View File

@ -0,0 +1,48 @@
<?php
namespace App\Actions\CronJob;
use App\Enums\CronjobStatus;
use App\Models\CronJob;
use App\Models\Server;
use App\ValidationRules\CronRule;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\ValidationException;
class CreateCronJob
{
public function create(Server $server, array $input): void
{
$this->validate($input);
$cronJob = new CronJob([
'server_id' => $server->id,
'user' => $input['user'],
'command' => $input['command'],
'frequency' => $input['frequency'],
'status' => CronjobStatus::CREATING,
]);
$cronJob->save();
$cronJob->addToServer();
}
/**
* @throws ValidationException
*/
private function validate(array $input): void
{
Validator::make($input, [
'command' => [
'required',
],
'user' => [
'required',
'in:root,'.config('core.ssh_user'),
],
'frequency' => [
'required',
new CronRule(),
],
])->validateWithBag('createCronJob');
}
}

View File

@ -0,0 +1,58 @@
<?php
namespace App\Actions\Database;
use App\Models\Database;
use App\Models\Server;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rule;
use Illuminate\Validation\ValidationException;
class CreateDatabase
{
/**
* @throws ValidationException
*/
public function create(Server $server, array $input): Database
{
$this->validate($server, $input);
$database = new Database([
'server_id' => $server->id,
'name' => $input['name'],
]);
$database->save();
$database->createOnServer();
return $database;
}
/**
* @throws ValidationException
*/
private function validate(Server $server, array $input): void
{
$rules = [
'name' => [
'required',
'alpha_dash',
Rule::unique('databases', 'name')->where('server_id', $server->id),
],
];
if (isset($input['user']) && $input['user']) {
$rules['username'] = [
'required',
'alpha_dash',
Rule::unique('database_users', 'username')->where('server_id', $server->id),
];
$rules['password'] = [
'required',
'min:6',
];
}
if (isset($input['remote']) && $input['remote']) {
$rules['host'] = 'required';
}
Validator::make($input, $rules)->validate();
}
}

View File

@ -0,0 +1,53 @@
<?php
namespace App\Actions\Database;
use App\Models\DatabaseUser;
use App\Models\Server;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rule;
use Illuminate\Validation\ValidationException;
class CreateDatabaseUser
{
/**
* @throws ValidationException
*/
public function create(Server $server, array $input): DatabaseUser
{
$this->validate($server, $input);
$databaseUser = new DatabaseUser([
'server_id' => $server->id,
'username' => $input['username'],
'password' => $input['password'],
'host' => isset($input['remote']) && $input['remote'] ? $input['host'] : 'localhost',
]);
$databaseUser->save();
$databaseUser->createOnServer();
return $databaseUser;
}
/**
* @throws ValidationException
*/
private function validate(Server $server, array $input): void
{
$rules = [
'username' => [
'required',
'alpha_dash',
Rule::unique('database_users', 'username')->where('server_id', $server->id),
],
'password' => [
'required',
'min:6',
],
];
if (isset($input['remote']) && $input['remote']) {
$rules['host'] = 'required';
}
Validator::make($input, $rules)->validate();
}
}

View File

@ -0,0 +1,55 @@
<?php
namespace App\Actions\Database;
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) {
if ($phpMyAdmin->status === 'ready') {
throw ValidationException::withMessages([
'install' => __('Already installed'),
])->errorBag('installPHPMyAdmin');
}
$phpMyAdmin->delete();
}
$phpMyAdmin = new Service([
'server_id' => $server->id,
'type' => 'phpmyadmin',
'type_data' => [
'allowed_ip' => $input['allowed_ip'],
'php' => $server->defaultService('php')->version,
],
'name' => 'phpmyadmin',
'version' => '5.1.2',
'status' => 'installing',
'is_default' => 1,
]);
$phpMyAdmin->save();
$phpMyAdmin->install();
return $phpMyAdmin;
}
/**
* @throws ValidationException
*/
private function validate(array $input): void
{
Validator::make($input, [
'allowed_ip' => 'required',
])->validateWithBag('installPHPMyAdmin');
}
}

View File

@ -0,0 +1,30 @@
<?php
namespace App\Actions\Database;
use App\Models\Database;
use App\Models\DatabaseUser;
use Illuminate\Validation\ValidationException;
class LinkUser
{
/**
* @throws ValidationException
*/
public function link(DatabaseUser $databaseUser, array $databases): void
{
$dbs = Database::query()
->where('server_id', $databaseUser->server_id)
->whereIn('name', $databases)
->count();
if (count($databases) !== $dbs) {
throw ValidationException::withMessages(['databases' => __('Databases not found!')])
->errorBag('linkUser');
}
$databaseUser->databases = $databases;
$databaseUser->unlinkUser();
$databaseUser->linkUser();
$databaseUser->save();
}
}

View File

@ -0,0 +1,64 @@
<?php
namespace App\Actions\FirewallRule;
use App\Enums\FirewallRuleStatus;
use App\Models\FirewallRule;
use App\Models\Server;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rule;
use Illuminate\Validation\ValidationException;
class CreateRule
{
public function create(Server $server, array $input): FirewallRule
{
$this->validate($server, $input);
$rule = new FirewallRule([
'server_id' => $server->id,
'type' => $input['type'],
'protocol' => $input['protocol'],
'port' => $input['port'],
'source' => $input['source'],
'mask' => $input['mask'],
'status' => FirewallRuleStatus::CREATING,
]);
$rule->save();
$rule->addToServer();
return $rule;
}
/**
* @throws ValidationException
*/
private function validate(Server $server, array $input): void
{
Validator::make($input, [
'type' => [
'required',
'in:allow,deny',
],
'protocol' => [
'required',
'in:'.implode(',', array_keys(config('core.firewall_protocols_port'))),
],
'port' => [
'required',
'numeric',
'min:1',
'max:65535',
Rule::unique('firewall_rules', 'port')->where('server_id', $server->id),
],
'source' => [
'required',
'ip',
],
'mask' => [
'required',
'numeric',
],
])->validateWithBag('createRule');
}
}

View File

@ -0,0 +1,55 @@
<?php
namespace App\Actions\NotificationChannels;
use App\Models\NotificationChannel;
use App\Models\User;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\ValidationException;
class AddChannel
{
/**
* @throws ValidationException
*/
public function add(User $user, array $input): void
{
$this->validate($input);
$channel = new NotificationChannel([
'user_id' => $user->id,
'provider' => $input['provider'],
'label' => $input['label'],
]);
$this->validateType($channel, $input);
$channel->data = $channel->provider()->data($input);
$channel->save();
if (! $channel->provider()->connect()) {
$channel->delete();
throw ValidationException::withMessages([
'provider' => __('Could not connect'),
]);
}
}
/**
* @throws ValidationException
*/
protected function validate(array $input): void
{
Validator::make($input, [
'provider' => 'required|in:'.implode(',', config('core.notification_channels_providers')),
'label' => 'required',
])->validate();
}
/**
* @throws ValidationException
*/
protected function validateType(NotificationChannel $channel, array $input): void
{
Validator::make($input, $channel->provider()->validationRules())
->validate();
}
}

View File

@ -0,0 +1,52 @@
<?php
namespace App\Actions\PHP;
use App\Enums\ServiceStatus;
use App\Models\Server;
use App\Models\Service;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rule;
use Illuminate\Validation\ValidationException;
class InstallNewPHP
{
public function install(Server $server, array $input): void
{
$this->validate($server, $input);
$php = new Service([
'server_id' => $server->id,
'type' => 'php',
'type_data' => [
'extensions' => [],
'settings' => config('core.php_settings'),
],
'name' => 'php',
'version' => $input['version'],
'status' => ServiceStatus::INSTALLING,
'is_default' => false,
]);
$php->save();
$php->install();
}
/**
* @throws ValidationException
*/
private function validate(Server $server, array $input): void
{
Validator::make($input, [
'version' => [
'required',
Rule::in(config('core.php_versions')),
],
])->validateWithBag('installPHP');
if (in_array($input['version'], $server->installedPHPVersions())) {
throw ValidationException::withMessages(
['version' => __('This version is already installed')]
)->errorBag('installPHP');
}
}
}

View File

@ -0,0 +1,46 @@
<?php
namespace App\Actions\PHP;
use App\Models\Service;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\ValidationException;
class InstallPHPExtension
{
/**
* @throws ValidationException
*/
public function handle(Service $service, array $input): Service
{
$typeData = $service->type_data;
$typeData['extensions'] = $typeData['extensions'] ?? [];
$service->type_data = $typeData;
$service->save();
$this->validate($service, $input);
$service->handler()->installExtension($input['name']);
return $service;
}
/**
* @throws ValidationException
*/
private function validate(Service $service, array $input): void
{
Validator::make($input, [
'name' => [
'required',
'in:'.implode(',', config('core.php_extensions')),
],
])->validateWithBag('installPHPExtension');
if (in_array($input['name'], $service->type_data['extensions'])) {
throw ValidationException::withMessages(
['name' => __('This extension already installed')]
)->errorBag('installPHPExtension');
}
}
}

View File

@ -0,0 +1,39 @@
<?php
namespace App\Actions\PHP;
use App\Models\Server;
use Illuminate\Validation\ValidationException;
class UninstallPHP
{
public function uninstall(Server $server, string $version): void
{
$this->validate($server, $version);
$php = $server->services()->where('type', 'php')->where('version', $version)->first();
$php->uninstall();
}
/**
* @throws ValidationException
*/
private function validate(Server $server, string $version): void
{
$php = $server->services()->where('type', 'php')->where('version', $version)->first();
if (! $php) {
throw ValidationException::withMessages(
['version' => __('This version has not been installed yet!')]
);
}
$hasSite = $server->sites()->where('php_version', $version)->first();
if ($hasSite) {
throw ValidationException::withMessages(
['version' => __('Cannot uninstall this version because some sites are using it!')]
);
}
}
}

View File

@ -0,0 +1,40 @@
<?php
namespace App\Actions\PHP;
use App\Models\Service;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
use Illuminate\Validation\ValidationException;
use Throwable;
class UpdatePHPIni
{
/**
* @throws ValidationException
*/
public function update(Service $service, string $ini): void
{
$tmpName = Str::random(10).strtotime('now');
try {
Storage::disk('local')->put($tmpName, $ini);
$service->server->ssh('root')->upload(
Storage::disk('local')->path($tmpName),
"/etc/php/$service->version/cli/php.ini"
);
$this->deleteTempFile($tmpName);
} catch (Throwable) {
$this->deleteTempFile($tmpName);
throw ValidationException::withMessages([
'ini' => __("Couldn't update php.ini file!"),
]);
}
}
private function deleteTempFile(string $name): void
{
if (Storage::disk('local')->exists($name)) {
Storage::disk('local')->delete($name);
}
}
}

View File

@ -0,0 +1,65 @@
<?php
namespace App\Actions\Queue;
use App\Enums\QueueStatus;
use App\Models\Queue;
use App\Models\Server;
use App\Models\Site;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\ValidationException;
class CreateQueue
{
/**
* @throws ValidationException
*/
public function create(mixed $queueable, array $input): void
{
$this->validate($input);
$queue = new Queue([
'server_id' => $queueable instanceof Server ? $queueable->id : $queueable->server_id,
'site_id' => $queueable instanceof Site ? $queueable->id : null,
'command' => $input['command'],
'user' => $input['user'],
'auto_start' => $input['auto_start'],
'auto_restart' => $input['auto_restart'],
'numprocs' => $input['numprocs'],
'status' => QueueStatus::CREATING,
]);
$queue->save();
$queue->deploy();
}
/**
* @throws ValidationException
*/
protected function validate(array $input): void
{
$rules = [
'command' => [
'required',
],
'user' => [
'required',
'in:root,'.config('core.ssh_user'),
],
'auto_start' => [
'required',
'in:0,1',
],
'auto_restart' => [
'required',
'in:0,1',
],
'numprocs' => [
'required',
'numeric',
'min:1',
],
];
Validator::make($input, $rules)->validateWithBag('createQueue');
}
}

View File

@ -0,0 +1,47 @@
<?php
namespace App\Actions\SSL;
use App\Enums\SslType;
use App\Models\Site;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rule;
use Illuminate\Validation\ValidationException;
class CreateSSL
{
/**
* @throws ValidationException
*/
public function create(Site $site, array $input): void
{
$this->validate($input);
if ($input['type'] == SslType::LETSENCRYPT) {
$site->createFreeSsl();
}
if ($input['type'] == SslType::CUSTOM) {
$site->createCustomSsl($input['certificate'], $input['private']);
}
}
/**
* @throws ValidationException
*/
protected function validate(array $input): void
{
$rules = [
'type' => [
'required',
Rule::in(SslType::getValues()),
],
];
if (isset($input['type']) && $input['type'] == SslType::CUSTOM) {
$rules['certificate'] = 'required';
$rules['private'] = 'required';
}
Validator::make($input, $rules)->validateWithBag('createSSL');
}
}

View File

@ -0,0 +1,41 @@
<?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

@ -0,0 +1,17 @@
<?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

@ -0,0 +1,37 @@
<?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,183 @@
<?php
namespace App\Actions\Server;
use App\Enums\FirewallRuleStatus;
use App\Exceptions\ServerProviderError;
use App\Jobs\Installation\ContinueInstallation;
use App\Models\Server;
use App\Models\User;
use App\ValidationRules\RestrictedIPAddressesRule;
use Exception;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Str;
use Illuminate\Validation\Rule;
use Illuminate\Validation\ValidationException;
use Throwable;
class CreateServer
{
/**
* @throws Throwable
*/
public function create(User $creator, array $input): Server
{
$this->validateInputs($input);
$server = new Server([
'user_id' => $creator->id,
'name' => $input['name'],
'ssh_user' => config('core.server_providers_default_user')[$input['provider']][$input['os']],
'ip' => $input['ip'],
'port' => $input['port'] ?? 22,
'os' => $input['os'],
'type' => $input['type'],
'provider' => $input['provider'],
'authentication' => [
'user' => config('core.ssh_user'),
'pass' => Str::random(10),
'root_pass' => Str::random(10),
],
'progress' => 0,
'progress_step' => 'Initializing',
]);
try {
DB::beginTransaction();
if ($server->provider != 'custom') {
$server->provider_id = $input['server_provider'];
}
// validate type
$this->validateType($server, $input);
$server->type_data = $server->type()->data($input);
// validate provider
$this->validateProvider($server, $input);
$server->provider_data = $server->provider()->data($input);
// save
$server->save();
// create firewall rules
$this->createFirewallRules($server);
// create instance
$server->provider()->create();
// add services
$server->type()->createServices($input);
// install server
if ($server->provider == 'custom') {
$server->install();
} else {
$server->progress_step = __('Installation will begin in 3 minutes!');
$server->save();
dispatch(new ContinueInstallation($server))
->delay(now()->addMinutes(3))
->onQueue('default');
}
DB::commit();
return $server;
} catch (Exception $e) {
$server->provider()->delete();
DB::rollBack();
if ($e instanceof ServerProviderError) {
throw ValidationException::withMessages([
'provider' => __('Provider Error: ').$e->getMessage(),
])->errorBag('createServer');
}
throw $e;
}
}
/**
* @throws ValidationException
*/
private function validateInputs(array $input): void
{
$rules = [
'provider' => 'required|in:'.implode(',', config('core.server_providers')),
'name' => 'required',
'os' => 'required|in:'.implode(',', config('core.operating_systems')),
'type' => [
'required',
Rule::in(config('core.server_types')),
],
];
Validator::make($input, $rules)->validate();
if ($input['provider'] != 'custom') {
$rules['server_provider'] = 'required|exists:server_providers,id,user_id,'.auth()->user()->id;
}
if ($input['provider'] == 'custom') {
$rules['ip'] = [
'required',
'ip',
new RestrictedIPAddressesRule(),
];
$rules['port'] = [
'required',
'numeric',
'min:1',
'max:65535',
];
}
Validator::make($input, $rules)->validate();
}
/**
* @throws ValidationException
*/
private function validateType(Server $server, array $input): void
{
Validator::make($input, $server->type()->createValidationRules($input))
->validate();
}
/**
* @throws ValidationException
*/
private function validateProvider(Server $server, array $input): void
{
Validator::make($input, $server->provider()->createValidationRules($input))
->validate();
}
private function createFirewallRules(Server $server): void
{
$server->firewallRules()->createMany([
[
'type' => 'allow',
'protocol' => 'ssh',
'port' => 22,
'source' => '0.0.0.0',
'mask' => 0,
'status' => FirewallRuleStatus::READY,
],
[
'type' => 'allow',
'protocol' => 'http',
'port' => 80,
'source' => '0.0.0.0',
'mask' => 0,
'status' => FirewallRuleStatus::READY,
],
[
'type' => 'allow',
'protocol' => 'https',
'port' => 443,
'source' => '0.0.0.0',
'mask' => 0,
'status' => FirewallRuleStatus::READY,
],
]);
}
}

View File

@ -0,0 +1,33 @@
<?php
namespace App\Actions\Server;
use App\Models\Server;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\ValidationException;
class DeleteServer
{
/**
* @throws ValidationException
*/
public function delete(Server $server, array $input): void
{
$this->validateDelete($input);
DB::transaction(function () use ($server) {
$server->cleanDelete();
});
}
/**
* @throws ValidationException
*/
protected function validateDelete(array $input): void
{
Validator::make($input, [
'confirm' => 'required|in:delete',
])->validateWithBag('deleteServer');
}
}

View File

@ -0,0 +1,55 @@
<?php
namespace App\Actions\Server;
use App\Models\Server;
use App\ValidationRules\RestrictedIPAddressesRule;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\ValidationException;
class EditServer
{
/**
* @throws ValidationException
*/
public function edit(Server $server, array $input): Server
{
$this->validate($input);
$checkConnection = false;
if (isset($input['name'])) {
$server->name = $input['name'];
}
if (isset($input['ip'])) {
if ($server->ip !== $input['ip']) {
$checkConnection = true;
}
$server->ip = $input['ip'];
}
if (isset($input['port'])) {
if ($server->port !== $input['port']) {
$checkConnection = true;
}
$server->port = $input['port'];
}
$server->save();
if ($checkConnection) {
$server->checkConnection();
}
return $server;
}
/**
* @throws ValidationException
*/
protected function validate(array $input): void
{
Validator::make($input, [
'ip' => [
new RestrictedIPAddressesRule(),
],
])->validateWithBag('editServer');
}
}

View File

@ -0,0 +1,14 @@
<?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,77 @@
<?php
namespace App\Actions\ServerProvider;
use App\Contracts\ServerProvider as ServerProviderContract;
use App\Models\ServerProvider;
use App\Models\User;
use Exception;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rule;
use Illuminate\Validation\ValidationException;
class CreateServerProvider
{
/**
* @throws ValidationException
*/
public function create(User $user, array $input): ServerProvider
{
$this->validateInput($input);
$provider = $this->getProvider($input['provider']);
$this->validateProvider($provider, $input);
try {
$provider->connect($input);
} catch (Exception) {
throw ValidationException::withMessages([
'provider' => [
__("Couldn't connect to provider. Please check your credentials and try again later."),
],
]);
}
$serverProvider = new ServerProvider();
$serverProvider->user_id = $user->id;
$serverProvider->profile = $input['name'];
$serverProvider->provider = $input['provider'];
$serverProvider->credentials = $provider->credentialData($input);
$serverProvider->save();
return $serverProvider;
}
private function getProvider($name): ServerProviderContract
{
$providerClass = config('core.server_providers_class.'.$name);
return new $providerClass();
}
/**
* @throws ValidationException
*/
private function validateInput(array $input): void
{
Validator::make($input, [
'name' => [
'required',
],
'provider' => [
'required',
Rule::in(config('core.server_providers')),
Rule::notIn('custom'),
],
])->validate();
}
/**
* @throws ValidationException
*/
private function validateProvider(ServerProviderContract $provider, array $input): void
{
Validator::make($input, $provider->credentialValidationRules($input))->validate();
}
}

View File

@ -0,0 +1,30 @@
<?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

@ -0,0 +1,51 @@
<?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');
}
}

118
app/Actions/Site/CreateSite.php Executable file
View File

@ -0,0 +1,118 @@
<?php
namespace App\Actions\Site;
use App\Enums\SiteStatus;
use App\Exceptions\SourceControlIsNotConnected;
use App\Models\Server;
use App\Models\Site;
use App\ValidationRules\DomainRule;
use Exception;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rule;
use Illuminate\Validation\ValidationException;
class CreateSite
{
/**
* @throws SourceControlIsNotConnected
* @throws ValidationException
*/
public function create(Server $server, array $input): Site
{
$this->validateInputs($server, $input);
try {
DB::beginTransaction();
$site = new Site([
'server_id' => $server->id,
'type' => $input['type'],
'domain' => $input['domain'],
'aliases' => isset($input['alias']) ? [$input['alias']] : [],
'path' => '/home/'.$server->ssh_user.'/'.$input['domain'],
'status' => SiteStatus::INSTALLING,
]);
// fields based on type
$site->fill($site->type()->createFields($input));
// check has access to repository
try {
if ($site->sourceControl()) {
$site->sourceControl()->getRepo($site->repository);
}
} catch (SourceControlIsNotConnected) {
throw ValidationException::withMessages([
'source_control' => __('Source control is not connected'),
]);
}
// detect php version
if ($site->type()->language() === 'php') {
$site->php_version = $input['php_version'];
}
// validate type
$this->validateType($site, $input);
// set type data
$site->type_data = $site->type()->data($input);
// save
$site->save();
// create default deployment script
$site->deploymentScript()->create([
'name' => 'default',
'content' => '',
]);
// install server
$site->install();
DB::commit();
return $site;
} catch (Exception $e) {
DB::rollBack();
throw $e;
}
}
/**
* @throws ValidationException
*/
private function validateInputs(Server $server, array $input): void
{
$rules = [
'type' => [
'required',
Rule::in(config('core.site_types')),
],
'domain' => [
'required',
new DomainRule(),
Rule::unique('sites', 'domain')->where(function ($query) use ($server) {
return $query->where('server_id', $server->id);
}),
],
'alias' => [
new DomainRule(),
],
];
Validator::make($input, $rules)->validate();
}
/**
* @throws ValidationException
*/
private function validateType(Site $site, array $input): void
{
$rules = $site->type()->createValidationRules($input);
Validator::make($input, $rules)->validate();
}
}

32
app/Actions/Site/DeleteSite.php Executable file
View File

@ -0,0 +1,32 @@
<?php
namespace App\Actions\Site;
use App\Models\Site;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\ValidationException;
class DeleteSite
{
/**
* @throws ValidationException
*/
public function handle(Site $site, array $input): void
{
$this->validateDelete($input);
$site->update(['status' => 'deleting']);
$site->remove();
}
/**
* @throws ValidationException
*/
protected function validateDelete(array $input): void
{
Validator::make($input, [
'confirm' => 'required|in:delete',
])->validateWithBag('deleteSite');
}
}

41
app/Actions/Site/EditSite.php Executable file
View File

@ -0,0 +1,41 @@
<?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');
}
}

14
app/Actions/Site/GetSites.php Executable file
View File

@ -0,0 +1,14 @@
<?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

@ -0,0 +1,30 @@
<?php
namespace App\Actions\Site;
use App\Models\Site;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\ValidationException;
class UpdateBranch
{
/**
* @throws ValidationException
*/
public function update(Site $site, array $input): void
{
$this->validate($input);
$site->updateBranch($input['branch']);
}
/**
* @throws ValidationException
*/
protected function validate(array $input): void
{
Validator::make($input, [
'branch' => 'required',
])->validateWithBag('updateBranch');
}
}

View File

@ -0,0 +1,32 @@
<?php
namespace App\Actions\Site;
use App\Models\Site;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\ValidationException;
class UpdateDeploymentScript
{
/**
* @throws ValidationException
*/
public function update(Site $site, array $input): void
{
$this->validate($input);
$site->deploymentScript()->update([
'content' => $input['script'],
]);
}
/**
* @throws ValidationException
*/
protected function validate(array $input): void
{
Validator::make($input, [
'script' => 'required',
])->validateWithBag('updateDeploymentScript');
}
}

18
app/Actions/Site/UpdateEnv.php Executable file
View File

@ -0,0 +1,18 @@
<?php
namespace App\Actions\Site;
use App\Models\Site;
class UpdateEnv
{
public function handle(Site $site, array $input): void
{
$typeData = $site->type_data;
$typeData['env'] = $input['env'];
$site->type_data = $typeData;
$site->save();
$site->deployEnv();
}
}

View File

@ -0,0 +1,35 @@
<?php
namespace App\Actions\SourceControl;
use App\Models\SourceControl;
use Illuminate\Validation\ValidationException;
class ConnectSourceControl
{
public function connect(string $provider, array $input): void
{
$sourceControl = SourceControl::query()
->where('provider', $provider)
->first();
if (! $sourceControl) {
$sourceControl = new SourceControl([
'provider' => $provider,
]);
}
if (! $input['token']) {
$sourceControl->delete();
return;
}
$sourceControl->access_token = $input['token'];
if (! $sourceControl->provider()->connect()) {
throw ValidationException::withMessages([
'token' => __('Cannot connect to :provider or invalid token!', ['provider' => $provider]),
]);
}
$sourceControl->save();
}
}

View File

@ -0,0 +1,43 @@
<?php
namespace App\Actions\SshKey;
use App\Models\SshKey;
use App\Models\User;
use App\ValidationRules\SshKeyRule;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\ValidationException;
class CreateSshKey
{
/**
* @throws ValidationException
*/
public function create(User $user, array $input): SshKey
{
$this->validate($input);
$key = new SshKey([
'user_id' => $user->id,
'name' => $input['name'],
'public_key' => $input['public_key'],
]);
$key->save();
return $key;
}
/**
* @throws ValidationException
*/
private function validate(array $input): void
{
Validator::make($input, [
'name' => 'required',
'public_key' => [
'required',
new SshKeyRule(),
],
])->validate();
}
}

View File

@ -0,0 +1,30 @@
<?php
namespace App\Actions\StorageProvider;
use App\Models\StorageProvider;
use App\Models\User;
use Illuminate\Validation\ValidationException;
class AddStorageProvider
{
use ValidateProvider;
/**
* @throws ValidationException
*/
public function add(User $user, array $input): mixed
{
$this->validate($user, $input);
$storageProvider = new StorageProvider([
'user_id' => $user->id,
'provider' => $input['provider'],
'label' => $input['label'],
'connected' => false,
]);
$storageProvider->save();
return $storageProvider->provider()->connect();
}
}

View File

@ -0,0 +1,34 @@
<?php
namespace App\Actions\StorageProvider;
use App\Models\StorageProvider;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Laravel\Socialite\Facades\Socialite;
use Laravel\Socialite\Two\User;
use Throwable;
class HandleProviderCallback
{
public function callback(Request $request, string $provider): string|RedirectResponse
{
try {
$providerId = $request->session()->get('storage_provider_id');
/** @var StorageProvider $storageProvider */
$storageProvider = StorageProvider::query()->findOrFail($providerId);
/** @var User $oauthUser */
$oauthUser = Socialite::driver($provider)->user();
$storageProvider->token = $oauthUser->token;
$storageProvider->refresh_token = $oauthUser->refreshToken;
$storageProvider->token_expires_at = now()->addSeconds($oauthUser->expiresIn);
$storageProvider->connected = true;
$storageProvider->save();
/** @TODO toast success message */
} catch (Throwable) {
/** @TODO toast failed message */
}
return redirect()->route('storage-providers');
}
}

View File

@ -0,0 +1,28 @@
<?php
namespace App\Actions\StorageProvider;
use App\Models\User;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rule;
use Illuminate\Validation\ValidationException;
trait ValidateProvider
{
/**
* @throws ValidationException
*/
private function validate(User $user, array $input): void
{
Validator::make($input, [
'label' => [
'required',
Rule::unique('storage_providers', 'label')->where('user_id', $user->id),
],
'provider' => [
'required',
Rule::in(config('core.storage_providers')),
],
])->validateWithBag('addStorageProvider');
}
}

View File

@ -0,0 +1,25 @@
<?php
namespace App\Actions\User;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;
class UpdateUserPassword
{
/**
* Validate and update the user's password.
*/
public function update($user, array $input): void
{
Validator::make($input, [
'current_password' => ['required', 'string', 'current-password'],
'password' => ['required', 'string'],
'password_confirmation' => ['required', 'same:password'],
])->validate();
$user->forceFill([
'password' => Hash::make($input['password']),
])->save();
}
}

View File

@ -0,0 +1,44 @@
<?php
namespace App\Actions\User;
use App\Models\User;
use Exception;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rule;
class UpdateUserProfileInformation
{
/**
* Validate and update the given user's profile information.
*
* @throws Exception
*/
public function update(User $user, array $input): void
{
Validator::make($input, [
'name' => ['required', 'string', 'max:255'],
'email' => ['required', 'email', 'max:255', Rule::unique('users')->ignore($user->id)],
])->validateWithBag('updateProfileInformation');
if ($input['email'] !== $user->email) {
$this->updateVerifiedUser($user, $input);
} else {
$user->forceFill([
'name' => $input['name'],
'email' => $input['email'],
])->save();
}
}
/**
* Update the given verified user's profile information.
*/
protected function updateVerifiedUser(User $user, array $input): void
{
$user->forceFill([
'name' => $input['name'],
'email' => $input['email'],
])->save();
}
}

View File

@ -0,0 +1,28 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
class CreateDatabaseCommand extends Command
{
protected $signature = 'database:create';
protected $description = 'Create the database if it does not exist.';
public function handle(): void
{
$schemaName = config("database.connections.mysql.database");
config(["database.connections.mysql.database" => null]);
$query = "CREATE DATABASE IF NOT EXISTS $schemaName";
DB::statement($query);
config(["database.connections.mysql.database" => $schemaName]);
$this->info(sprintf("Database `%s` created successfully.", $schemaName));
}
}

View File

@ -0,0 +1,26 @@
<?php
namespace App\Console\Commands;
use App\Models\User;
use Illuminate\Console\Command;
use Illuminate\Support\Str;
class CreateUserCommand extends Command
{
protected $signature = 'user:create {name} {email}';
protected $description = 'Create a new user';
public function handle(): void
{
$password = Str::random(20);
User::create([
'name' => $this->argument('name'),
'email' => $this->argument('email'),
'password' => bcrypt($password),
]);
$this->info("User created with password: {$password}");
}
}

27
app/Console/Kernel.php Normal file
View File

@ -0,0 +1,27 @@
<?php
namespace App\Console;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
class Kernel extends ConsoleKernel
{
/**
* Define the application's command schedule.
*/
protected function schedule(Schedule $schedule): void
{
// $schedule->command('inspire')->hourly();
}
/**
* Register the commands for the application.
*/
protected function commands(): void
{
$this->load(__DIR__.'/Commands');
require base_path('routes/console.php');
}
}

24
app/Contracts/Database.php Executable file
View File

@ -0,0 +1,24 @@
<?php
namespace App\Contracts;
use App\Models\BackupFile;
interface Database
{
public function create(string $name): void;
public function delete(string $name): void;
public function createUser(string $username, string $password, string $host): void;
public function deleteUser(string $username, string $host): void;
public function link(string $username, string $host, array $databases): void;
public function unlink(string $username, string $host): void;
public function runBackup(BackupFile $backupFile): void;
public function restoreBackup(BackupFile $backupFile, string $database): void;
}

10
app/Contracts/Firewall.php Executable file
View File

@ -0,0 +1,10 @@
<?php
namespace App\Contracts;
interface Firewall
{
public function addRule(string $type, string $protocol, int $port, string $source, string $mask): void;
public function removeRule(string $type, string $protocol, int $port, string $source, string $mask): void;
}

View File

@ -0,0 +1,10 @@
<?php
namespace App\Contracts;
interface Notification
{
public function subject(): string;
public function message(bool $mail = false): mixed;
}

View File

@ -0,0 +1,14 @@
<?php
namespace App\Contracts;
interface NotificationChannel
{
public function validationRules(): array;
public function data(array $input): array;
public function connect(): bool;
public function sendMessage(string $subject, string $text): void;
}

View File

@ -0,0 +1,27 @@
<?php
namespace App\Contracts;
interface ProcessManager
{
public function create(
int $id,
string $command,
string $user,
bool $autoStart,
bool $autoRestart,
int $numprocs,
string $logFile,
?int $siteId = null
): void;
public function delete(int $id, int $siteId = null): void;
public function restart(int $id, int $siteId = null): void;
public function stop(int $id, int $siteId = null): void;
public function start(int $id, int $siteId = null): void;
public function getLogs(string $logPath): string;
}

10
app/Contracts/SSHCommand.php Executable file
View File

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

View File

@ -0,0 +1,26 @@
<?php
namespace App\Contracts;
interface ServerProvider
{
public function createValidationRules(array $input): array;
public function credentialValidationRules(array $input): array;
public function credentialData(array $input): array;
public function data(array $input): array;
public function connect(array $credentials = null): bool;
public function plans(): array;
public function regions(): array;
public function create(): void;
public function isRunning(): bool;
public function delete(): void;
}

14
app/Contracts/ServerType.php Executable file
View File

@ -0,0 +1,14 @@
<?php
namespace App\Contracts;
interface ServerType
{
public function createValidationRules(array $input): array;
public function data(array $input): array;
public function createServices(array $input): void;
public function install(): void;
}

22
app/Contracts/SiteType.php Executable file
View File

@ -0,0 +1,22 @@
<?php
namespace App\Contracts;
interface SiteType
{
public function language(): string;
public function createValidationRules(array $input): array;
public function createFields(array $input): array;
public function data(array $input): array;
public function install(): void;
public function delete(): void;
public function editValidationRules(array $input): array;
public function edit(): void;
}

View File

@ -0,0 +1,18 @@
<?php
namespace App\Contracts;
interface SourceControlProvider
{
public function connect(): bool;
public function getRepo(string $repo = null): mixed;
public function fullRepoUrl(string $repo): string;
public function deployHook(string $repo, array $events, string $secret): array;
public function destroyHook(string $repo, string $hookId): void;
public function getLastCommit(string $repo, string $branch): ?array;
}

View File

@ -0,0 +1,17 @@
<?php
namespace App\Contracts;
use App\Models\Server;
use Symfony\Component\HttpFoundation\RedirectResponse;
interface StorageProvider
{
public function connect(): RedirectResponse;
public function upload(Server $server, string $src, string $dest): array;
public function download(Server $server, string $src, string $dest): void;
public function delete(array $paths): void;
}

23
app/Contracts/Webserver.php Executable file
View File

@ -0,0 +1,23 @@
<?php
namespace App\Contracts;
use App\Models\Site;
use App\Models\Ssl;
interface Webserver
{
public function createVHost(Site $site): void;
public function updateVHost(Site $site): void;
public function deleteSite(Site $site): void;
public function changePHPVersion(Site $site, string $version): void;
public function setupSSL(Ssl $ssl): void;
public function removeSSL(Ssl $ssl): void;
public function updateRedirects(Site $site, array $redirects): void;
}

View File

@ -0,0 +1,14 @@
<?php
namespace App\Enums;
use BenSampo\Enum\Enum;
final class CronjobStatus extends Enum
{
const CREATING = 'creating';
const READY = 'ready';
const DELETING = 'deleting';
}

16
app/Enums/Database.php Normal file
View File

@ -0,0 +1,16 @@
<?php
namespace App\Enums;
use BenSampo\Enum\Enum;
final class Database extends Enum
{
const NONE = 'none';
const MYSQL57 = 'mysql57';
const MYSQL80 = 'mysql80';
const MARIADB = 'mariadb';
}

View File

@ -0,0 +1,16 @@
<?php
namespace App\Enums;
use BenSampo\Enum\Enum;
final class DatabaseStatus extends Enum
{
const READY = 'ready';
const CREATING = 'creating';
const FAILED = 'failed';
const DELETING = 'deleting';
}

View File

@ -0,0 +1,16 @@
<?php
namespace App\Enums;
use BenSampo\Enum\Enum;
final class DatabaseUserStatus extends Enum
{
const READY = 'ready';
const CREATING = 'creating';
const FAILED = 'failed';
const DELETING = 'deleting';
}

View File

@ -0,0 +1,14 @@
<?php
namespace App\Enums;
use BenSampo\Enum\Enum;
final class DeploymentStatus extends Enum
{
const DEPLOYING = 'deploying';
const FINISHED = 'finished';
const FAILED = 'failed';
}

View File

@ -0,0 +1,14 @@
<?php
namespace App\Enums;
use BenSampo\Enum\Enum;
final class FirewallRuleStatus extends Enum
{
const CREATING = 'creating';
const READY = 'ready';
const DELETING = 'deleting';
}

12
app/Enums/LogType.php Normal file
View File

@ -0,0 +1,12 @@
<?php
namespace App\Enums;
use BenSampo\Enum\Enum;
final class LogType extends Enum
{
const SERVER = 'server';
const SITE = 'site';
}

View File

@ -0,0 +1,14 @@
<?php
namespace App\Enums;
use BenSampo\Enum\Enum;
final class NotificationChannel extends Enum
{
const EMAIL = 'email';
const SLACK = 'slack';
const DISCORD = 'discord';
}

View File

@ -0,0 +1,14 @@
<?php
namespace App\Enums;
use BenSampo\Enum\Enum;
final class OperatingSystem extends Enum
{
const UBUNTU18 = 'ubuntu_18';
const UBUNTU20 = 'ubuntu_20';
const UBUNTU22 = 'ubuntu_22';
}

24
app/Enums/QueueStatus.php Normal file
View File

@ -0,0 +1,24 @@
<?php
namespace App\Enums;
use BenSampo\Enum\Enum;
final class QueueStatus extends Enum
{
const RUNNING = 'running';
const CREATING = 'creating';
const DELETING = 'deleting';
const FAILED = 'failed';
const STARTING = 'starting';
const STOPPING = 'stopping';
const RESTARTING = 'restarting';
const STOPPED = 'stopped';
}

View File

@ -0,0 +1,20 @@
<?php
namespace App\Enums;
use BenSampo\Enum\Enum;
final class ServerProvider extends Enum
{
const CUSTOM = 'custom';
const AWS = 'aws';
const LINODE = 'linode';
const DIGITALOCEAN = 'digitalocean';
const VULTR = 'vultr';
const HETZNER = 'hetzner';
}

View File

@ -0,0 +1,16 @@
<?php
namespace App\Enums;
use BenSampo\Enum\Enum;
final class ServerStatus extends Enum
{
const READY = 'ready';
const INSTALLING = 'installing';
const INSTALLATION_FAILED = 'installation_failed';
const DISCONNECTED = 'disconnected';
}

10
app/Enums/ServerType.php Normal file
View File

@ -0,0 +1,10 @@
<?php
namespace App\Enums;
use BenSampo\Enum\Enum;
final class ServerType extends Enum
{
const REGULAR = 'regular';
}

View File

@ -0,0 +1,26 @@
<?php
namespace App\Enums;
use BenSampo\Enum\Enum;
final class ServiceStatus extends Enum
{
const READY = 'ready';
const INSTALLING = 'installing';
const INSTALLATION_FAILED = 'installation_failed';
const UNINSTALLING = 'uninstalling';
const FAILED = 'failed';
const STARTING = 'starting';
const STOPPING = 'stopping';
const RESTARTING = 'restarting';
const STOPPED = 'stopped';
}

16
app/Enums/SiteStatus.php Normal file
View File

@ -0,0 +1,16 @@
<?php
namespace App\Enums;
use BenSampo\Enum\Enum;
final class SiteStatus extends Enum
{
const READY = 'ready';
const INSTALLING = 'installing';
const INSTALLATION_FAILED = 'installation_failed';
const DELETING = 'deleting';
}

14
app/Enums/SiteType.php Normal file
View File

@ -0,0 +1,14 @@
<?php
namespace App\Enums;
use BenSampo\Enum\Enum;
final class SiteType extends Enum
{
const PHP = 'php';
const LARAVEL = 'laravel';
const WORDPRESS = 'wordpress';
}

View File

@ -0,0 +1,16 @@
<?php
namespace App\Enums;
use BenSampo\Enum\Enum;
final class SourceControl extends Enum
{
const GITHUB = 'github';
const GITLAB = 'gitlab';
const BITBUCKET = 'bitbucket';
const CUSTOM = 'custom';
}

View File

@ -0,0 +1,14 @@
<?php
namespace App\Enums;
use BenSampo\Enum\Enum;
final class SshKeyStatus extends Enum
{
const ADDING = 'adding';
const ADDED = 'added';
const DELETING = 'deleting';
}

16
app/Enums/SslStatus.php Normal file
View File

@ -0,0 +1,16 @@
<?php
namespace App\Enums;
use BenSampo\Enum\Enum;
final class SslStatus extends Enum
{
const CREATED = 'created';
const CREATING = 'creating';
const DELETING = 'deleting';
const FAILED = 'failed';
}

12
app/Enums/SslType.php Normal file
View File

@ -0,0 +1,12 @@
<?php
namespace App\Enums;
use BenSampo\Enum\Enum;
final class SslType extends Enum
{
const LETSENCRYPT = 'letsencrypt';
const CUSTOM = 'custom';
}

View File

@ -0,0 +1,12 @@
<?php
namespace App\Enums;
use BenSampo\Enum\Enum;
final class StorageProvider extends Enum
{
const GOOGLE = 'google';
const DROPBOX = 'dropbox';
}

10
app/Enums/Webserver.php Normal file
View File

@ -0,0 +1,10 @@
<?php
namespace App\Enums;
use BenSampo\Enum\Enum;
final class Webserver extends Enum
{
const NGINX = 'nginx';
}

25
app/Events/Broadcast.php Normal file
View File

@ -0,0 +1,25 @@
<?php
namespace App\Events;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class Broadcast implements ShouldBroadcast
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public function __construct(public string $type, public array $data)
{
}
public function broadcastOn(): array
{
return [
new PrivateChannel('app'),
];
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,48 @@
<?php
namespace App\Exceptions;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Throwable;
class Handler extends ExceptionHandler
{
/**
* A list of exception types with their corresponding custom log levels.
*
* @var array<class-string<\Throwable>, \Psr\Log\LogLevel::*>
*/
protected $levels = [
//
];
/**
* A list of the exception types that are not reported.
*
* @var array<int, class-string<\Throwable>>
*/
protected $dontReport = [
//
];
/**
* A list of the inputs that are never flashed to the session on validation exceptions.
*
* @var array<int, string>
*/
protected $dontFlash = [
'current_password',
'password',
'password_confirmation',
];
/**
* Register the exception handling callbacks for the application.
*/
public function register(): void
{
$this->reportable(function (Throwable $e) {
//
});
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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