mirror of
https://github.com/vitodeploy/vito.git
synced 2025-04-19 01:41:36 +00:00
parent
4f12de9586
commit
a406491160
@ -27,8 +27,7 @@ public function link(DatabaseUser $databaseUser, array $input): void
|
||||
->whereIn('name', $input['databases'])
|
||||
->count();
|
||||
if (count($input['databases']) !== $dbs) {
|
||||
throw ValidationException::withMessages(['databases' => __('Databases not found!')])
|
||||
->errorBag('linkUser');
|
||||
throw ValidationException::withMessages(['databases' => __('Databases not found!')]);
|
||||
}
|
||||
|
||||
$databaseUser->databases = $input['databases'];
|
||||
|
@ -9,7 +9,7 @@
|
||||
|
||||
class RunBackup
|
||||
{
|
||||
public function run(Backup $backup): void
|
||||
public function run(Backup $backup): BackupFile
|
||||
{
|
||||
$file = new BackupFile([
|
||||
'backup_id' => $backup->id,
|
||||
@ -26,5 +26,7 @@ public function run(Backup $backup): void
|
||||
$file->status = BackupFileStatus::FAILED;
|
||||
$file->save();
|
||||
})->onConnection('ssh');
|
||||
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,6 @@
|
||||
namespace App\Actions\Queue;
|
||||
|
||||
use App\Enums\QueueStatus;
|
||||
use App\Enums\ServiceStatus;
|
||||
use App\Models\Queue;
|
||||
|
||||
class ManageQueue
|
||||
@ -14,10 +13,7 @@ public function start(Queue $queue): void
|
||||
$queue->save();
|
||||
dispatch(function () use ($queue) {
|
||||
$queue->server->processManager()->handler()->start($queue->id, $queue->site_id);
|
||||
$queue->status = ServiceStatus::READY;
|
||||
$queue->save();
|
||||
})->catch(function () use ($queue) {
|
||||
$queue->status = ServiceStatus::FAILED;
|
||||
$queue->status = QueueStatus::RUNNING;
|
||||
$queue->save();
|
||||
})->onConnection('ssh');
|
||||
}
|
||||
@ -28,10 +24,7 @@ public function stop(Queue $queue): void
|
||||
$queue->save();
|
||||
dispatch(function () use ($queue) {
|
||||
$queue->server->processManager()->handler()->stop($queue->id, $queue->site_id);
|
||||
$queue->status = ServiceStatus::STOPPED;
|
||||
$queue->save();
|
||||
})->catch(function () use ($queue) {
|
||||
$queue->status = ServiceStatus::FAILED;
|
||||
$queue->status = QueueStatus::STOPPED;
|
||||
$queue->save();
|
||||
})->onConnection('ssh');
|
||||
}
|
||||
@ -42,10 +35,7 @@ public function restart(Queue $queue): void
|
||||
$queue->save();
|
||||
dispatch(function () use ($queue) {
|
||||
$queue->server->processManager()->handler()->restart($queue->id, $queue->site_id);
|
||||
$queue->status = ServiceStatus::READY;
|
||||
$queue->save();
|
||||
})->catch(function () use ($queue) {
|
||||
$queue->status = ServiceStatus::FAILED;
|
||||
$queue->status = QueueStatus::RUNNING;
|
||||
$queue->save();
|
||||
})->onConnection('ssh');
|
||||
}
|
||||
|
@ -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');
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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');
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -28,7 +28,7 @@ public function create(User $user, array $input): ServerProvider
|
||||
} catch (Exception) {
|
||||
throw ValidationException::withMessages([
|
||||
'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']),
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
21
app/Actions/ServerProvider/DeleteServerProvider.php
Normal file
21
app/Actions/ServerProvider/DeleteServerProvider.php
Normal 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();
|
||||
}
|
||||
}
|
@ -19,9 +19,6 @@ public function start(Service $service): void
|
||||
$service->status = ServiceStatus::FAILED;
|
||||
}
|
||||
$service->save();
|
||||
})->catch(function () use ($service) {
|
||||
$service->status = ServiceStatus::FAILED;
|
||||
$service->save();
|
||||
})->onConnection('ssh');
|
||||
}
|
||||
|
||||
@ -37,9 +34,6 @@ public function stop(Service $service): void
|
||||
$service->status = ServiceStatus::FAILED;
|
||||
}
|
||||
$service->save();
|
||||
})->catch(function () use ($service) {
|
||||
$service->status = ServiceStatus::FAILED;
|
||||
$service->save();
|
||||
})->onConnection('ssh');
|
||||
}
|
||||
|
||||
@ -55,9 +49,6 @@ public function restart(Service $service): void
|
||||
$service->status = ServiceStatus::FAILED;
|
||||
}
|
||||
$service->save();
|
||||
})->catch(function () use ($service) {
|
||||
$service->status = ServiceStatus::FAILED;
|
||||
$service->save();
|
||||
})->onConnection('ssh');
|
||||
}
|
||||
}
|
||||
|
17
app/Actions/SourceControl/DeleteSourceControl.php
Normal file
17
app/Actions/SourceControl/DeleteSourceControl.php
Normal 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();
|
||||
}
|
||||
}
|
21
app/Actions/StorageProvider/DeleteStorageProvider.php
Normal file
21
app/Actions/StorageProvider/DeleteStorageProvider.php
Normal 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();
|
||||
}
|
||||
}
|
@ -15,14 +15,19 @@ class RunBackupCommand extends Command
|
||||
|
||||
public function handle(): void
|
||||
{
|
||||
$total = 0;
|
||||
|
||||
Backup::query()
|
||||
->where('interval', $this->argument('interval'))
|
||||
->where('status', BackupStatus::RUNNING)
|
||||
->chunk(100, function ($backups) {
|
||||
->chunk(100, function ($backups) use (&$total) {
|
||||
/** @var Backup $backup */
|
||||
foreach ($backups as $backup) {
|
||||
app(RunBackup::class)->run($backup);
|
||||
$total++;
|
||||
}
|
||||
});
|
||||
|
||||
$this->info("{$total} backups started");
|
||||
}
|
||||
}
|
||||
|
@ -2,13 +2,8 @@
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use App\Models\SourceControl;
|
||||
use Exception;
|
||||
|
||||
class SourceControlIsNotConnected extends Exception
|
||||
{
|
||||
public function __construct(protected SourceControl|string|null $sourceControl, ?string $message = null)
|
||||
{
|
||||
parent::__construct($message ?? 'Source control is not connected');
|
||||
}
|
||||
}
|
||||
|
@ -88,7 +88,7 @@ public function connect(bool $sftp = false): void
|
||||
Log::error('Error connecting', [
|
||||
'msg' => $e->getMessage(),
|
||||
]);
|
||||
throw $e;
|
||||
throw new SSHConnectionError($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -47,11 +47,14 @@ public function update(Request $request, Project $project): HtmxResponse
|
||||
|
||||
public function delete(Project $project): RedirectResponse
|
||||
{
|
||||
/** @var User $user */
|
||||
$user = auth()->user();
|
||||
|
||||
/** @var Project $project */
|
||||
$project = auth()->user()->projects()->findOrFail($project->id);
|
||||
$project = $user->projects()->findOrFail($project->id);
|
||||
|
||||
try {
|
||||
app(DeleteProject::class)->delete(auth()->user(), $project);
|
||||
app(DeleteProject::class)->delete($user, $project);
|
||||
} catch (ValidationException $e) {
|
||||
Toast::error($e->getMessage());
|
||||
|
||||
|
@ -3,6 +3,7 @@
|
||||
namespace App\Http\Controllers\Settings;
|
||||
|
||||
use App\Actions\ServerProvider\CreateServerProvider;
|
||||
use App\Actions\ServerProvider\DeleteServerProvider;
|
||||
use App\Facades\Toast;
|
||||
use App\Helpers\HtmxResponse;
|
||||
use App\Http\Controllers\Controller;
|
||||
@ -32,14 +33,15 @@ public function connect(Request $request): HtmxResponse
|
||||
return htmx()->redirect(route('server-providers'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @TODO Update servers using this provider
|
||||
*/
|
||||
public function delete(int $id): RedirectResponse
|
||||
public function delete(ServerProvider $serverProvider): RedirectResponse
|
||||
{
|
||||
$serverProvider = ServerProvider::query()->findOrFail($id);
|
||||
try {
|
||||
app(DeleteServerProvider::class)->delete($serverProvider);
|
||||
} catch (\Exception $e) {
|
||||
Toast::error($e->getMessage());
|
||||
|
||||
$serverProvider->delete();
|
||||
return back();
|
||||
}
|
||||
|
||||
Toast::success('Server provider deleted.');
|
||||
|
||||
|
@ -3,6 +3,7 @@
|
||||
namespace App\Http\Controllers\Settings;
|
||||
|
||||
use App\Actions\SourceControl\ConnectSourceControl;
|
||||
use App\Actions\SourceControl\DeleteSourceControl;
|
||||
use App\Facades\Toast;
|
||||
use App\Helpers\HtmxResponse;
|
||||
use App\Http\Controllers\Controller;
|
||||
@ -11,9 +12,6 @@
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
/**
|
||||
* @TODO Assign user to source control
|
||||
*/
|
||||
class SourceControlController extends Controller
|
||||
{
|
||||
public function index(): View
|
||||
@ -34,11 +32,15 @@ public function connect(Request $request): HtmxResponse
|
||||
return htmx()->redirect(route('source-controls'));
|
||||
}
|
||||
|
||||
public function delete(int $id): RedirectResponse
|
||||
public function delete(SourceControl $sourceControl): RedirectResponse
|
||||
{
|
||||
$sourceControl = SourceControl::query()->findOrFail($id);
|
||||
try {
|
||||
app(DeleteSourceControl::class)->delete($sourceControl);
|
||||
} catch (\Exception $e) {
|
||||
Toast::error($e->getMessage());
|
||||
|
||||
$sourceControl->delete();
|
||||
return back();
|
||||
}
|
||||
|
||||
Toast::success('Source control deleted.');
|
||||
|
||||
|
@ -3,6 +3,7 @@
|
||||
namespace App\Http\Controllers\Settings;
|
||||
|
||||
use App\Actions\StorageProvider\CreateStorageProvider;
|
||||
use App\Actions\StorageProvider\DeleteStorageProvider;
|
||||
use App\Facades\Toast;
|
||||
use App\Helpers\HtmxResponse;
|
||||
use App\Http\Controllers\Controller;
|
||||
@ -32,14 +33,15 @@ public function connect(Request $request): HtmxResponse
|
||||
return htmx()->redirect(route('storage-providers'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @TODO Update servers using this provider
|
||||
*/
|
||||
public function delete(int $id): RedirectResponse
|
||||
public function delete(StorageProvider $storageProvider): RedirectResponse
|
||||
{
|
||||
$storageProvider = StorageProvider::query()->findOrFail($id);
|
||||
try {
|
||||
app(DeleteStorageProvider::class)->delete($storageProvider);
|
||||
} catch (\Exception $e) {
|
||||
Toast::error($e->getMessage());
|
||||
|
||||
$storageProvider->delete();
|
||||
return back();
|
||||
}
|
||||
|
||||
Toast::success('Storage provider deleted.');
|
||||
|
||||
|
@ -3,6 +3,7 @@
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Facades\Toast;
|
||||
use App\Helpers\HtmxResponse;
|
||||
use App\Models\Server;
|
||||
use App\Models\Site;
|
||||
use Illuminate\Contracts\View\View;
|
||||
@ -43,7 +44,7 @@ public function updateVhost(Server $server, Site $site, Request $request): Redir
|
||||
return back();
|
||||
}
|
||||
|
||||
public function updatePHPVersion(Server $server, Site $site, Request $request): RedirectResponse
|
||||
public function updatePHPVersion(Server $server, Site $site, Request $request): HtmxResponse
|
||||
{
|
||||
$this->validate($request, [
|
||||
'version' => [
|
||||
@ -60,6 +61,6 @@ public function updatePHPVersion(Server $server, Site $site, Request $request):
|
||||
Toast::error($e->getMessage());
|
||||
}
|
||||
|
||||
return back();
|
||||
return htmx()->back();
|
||||
}
|
||||
}
|
||||
|
@ -1,36 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
/**
|
||||
* @property int $site_id
|
||||
* @property int $mode
|
||||
* @property string $from
|
||||
* @property string $to
|
||||
* @property string $status
|
||||
*/
|
||||
class Redirect extends AbstractModel
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $fillable = [
|
||||
'site_id',
|
||||
'mode',
|
||||
'from',
|
||||
'to',
|
||||
'status',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'site_id' => 'integer',
|
||||
'mode' => 'integer',
|
||||
];
|
||||
|
||||
public function site(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Site::class);
|
||||
}
|
||||
}
|
@ -1,38 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
|
||||
/**
|
||||
* @property int $user_id
|
||||
* @property string $name
|
||||
* @property string $content
|
||||
* @property User $creator
|
||||
*/
|
||||
class Script extends AbstractModel
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $fillable = [
|
||||
'user_id',
|
||||
'name',
|
||||
'content',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'user_id' => 'integer',
|
||||
];
|
||||
|
||||
public function creator(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
|
||||
public function executions(): HasMany
|
||||
{
|
||||
return $this->hasMany(ScriptExecution::class, 'script_id');
|
||||
}
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
/**
|
||||
* @property int $script_id
|
||||
* @property int $server_id
|
||||
* @property string $user
|
||||
* @property Carbon $finished_at
|
||||
* @property ?Server $server
|
||||
* @property Script $script
|
||||
*/
|
||||
class ScriptExecution extends AbstractModel
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $fillable = [
|
||||
'script_id',
|
||||
'server_id',
|
||||
'user',
|
||||
'finished_at',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'script_id' => 'integer',
|
||||
'server_id' => 'integer',
|
||||
];
|
||||
|
||||
public function script(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Script::class);
|
||||
}
|
||||
|
||||
public function server(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Server::class);
|
||||
}
|
||||
}
|
@ -51,7 +51,6 @@
|
||||
* @property FirewallRule[] $firewallRules
|
||||
* @property CronJob[] $cronJobs
|
||||
* @property Queue[] $queues
|
||||
* @property ScriptExecution[] $scriptExecutions
|
||||
* @property Backup[] $backups
|
||||
* @property Queue[] $daemons
|
||||
* @property SshKey[] $sshKeys
|
||||
@ -121,7 +120,6 @@ public static function boot(): void
|
||||
$server->cronJobs()->delete();
|
||||
$server->queues()->delete();
|
||||
$server->daemons()->delete();
|
||||
$server->scriptExecutions()->delete();
|
||||
$server->sshKeys()->detach();
|
||||
if (File::exists($server->sshKey()['public_key_path'])) {
|
||||
File::delete($server->sshKey()['public_key_path']);
|
||||
@ -187,11 +185,6 @@ public function queues(): HasMany
|
||||
return $this->hasMany(Queue::class);
|
||||
}
|
||||
|
||||
public function scriptExecutions(): HasMany
|
||||
{
|
||||
return $this->hasMany(ScriptExecution::class);
|
||||
}
|
||||
|
||||
public function backups(): HasMany
|
||||
{
|
||||
return $this->hasMany(Backup::class);
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
@ -38,8 +39,12 @@ public static function boot(): void
|
||||
parent::boot();
|
||||
|
||||
static::deleting(function (ServerLog $log) {
|
||||
if (Storage::disk($log->disk)->exists($log->name)) {
|
||||
Storage::disk($log->disk)->delete($log->name);
|
||||
try {
|
||||
if (Storage::disk($log->disk)->exists($log->name)) {
|
||||
Storage::disk($log->disk)->delete($log->name);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
Log::error($e->getMessage(), ['exception' => $e]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
|
||||
/**
|
||||
* @property int $user_id
|
||||
@ -40,4 +41,9 @@ public function getCredentials(): array
|
||||
{
|
||||
return $this->credentials;
|
||||
}
|
||||
|
||||
public function servers(): HasMany
|
||||
{
|
||||
return $this->hasMany(Server::class, 'provider_id');
|
||||
}
|
||||
}
|
||||
|
@ -33,7 +33,6 @@
|
||||
* @property Deployment[] $deployments
|
||||
* @property ?GitHook $gitHook
|
||||
* @property DeploymentScript $deploymentScript
|
||||
* @property Redirect[] $redirects
|
||||
* @property Queue[] $queues
|
||||
* @property Ssl[] $ssls
|
||||
* @property ?Ssl $activeSsl
|
||||
@ -76,7 +75,6 @@ public static function boot(): void
|
||||
parent::boot();
|
||||
|
||||
static::deleting(function (Site $site) {
|
||||
$site->redirects()->delete();
|
||||
$site->queues()->delete();
|
||||
$site->ssls()->delete();
|
||||
$site->deployments()->delete();
|
||||
@ -116,11 +114,6 @@ public function deploymentScript(): HasOne
|
||||
return $this->hasOne(DeploymentScript::class);
|
||||
}
|
||||
|
||||
public function redirects(): HasMany
|
||||
{
|
||||
return $this->hasMany(Redirect::class);
|
||||
}
|
||||
|
||||
public function queues(): HasMany
|
||||
{
|
||||
return $this->hasMany(Queue::class);
|
||||
@ -192,8 +185,8 @@ public function php(): ?Service
|
||||
|
||||
public function changePHPVersion($version): void
|
||||
{
|
||||
$this->php_version = $version;
|
||||
$this->server->webserver()->handler()->changePHPVersion($this, $version);
|
||||
$this->php_version = $version;
|
||||
$this->save();
|
||||
}
|
||||
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
use App\SourceControlProviders\SourceControlProvider;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
|
||||
/**
|
||||
* @property string $provider
|
||||
@ -37,4 +38,9 @@ public function getRepo(?string $repo = null): ?array
|
||||
{
|
||||
return $this->provider()->getRepo($repo);
|
||||
}
|
||||
|
||||
public function sites(): HasMany
|
||||
{
|
||||
return $this->hasMany(Site::class);
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
|
||||
/**
|
||||
* @property int $user_id
|
||||
@ -39,4 +40,9 @@ public function provider(): \App\StorageProviders\StorageProvider
|
||||
|
||||
return new $providerClass($this);
|
||||
}
|
||||
|
||||
public function backups(): HasMany
|
||||
{
|
||||
return $this->hasMany(Backup::class, 'storage_id');
|
||||
}
|
||||
}
|
||||
|
@ -86,11 +86,6 @@ public function serverProviders(): HasMany
|
||||
return $this->hasMany(ServerProvider::class);
|
||||
}
|
||||
|
||||
public function scripts(): HasMany
|
||||
{
|
||||
return $this->hasMany(Script::class, 'user_id');
|
||||
}
|
||||
|
||||
public function sourceControl(string $provider): HasOne
|
||||
{
|
||||
return $this->hasOne(SourceControl::class)->where('provider', $provider);
|
||||
|
@ -60,6 +60,6 @@ private function sendToTelegram(string $text): void
|
||||
'text' => $text,
|
||||
'parse_mode' => 'markdown',
|
||||
'disable_web_page_preview' => true,
|
||||
]);
|
||||
])->throw();
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,6 @@
|
||||
use App\Helpers\Notifier;
|
||||
use App\Helpers\SSH;
|
||||
use App\Helpers\Toast;
|
||||
use App\Support\SocialiteProviders\DropboxProvider;
|
||||
use Illuminate\Contracts\Container\BindingResolutionException;
|
||||
use Illuminate\Http\Resources\Json\ResourceCollection;
|
||||
use Illuminate\Support\Facades\URL;
|
||||
@ -39,26 +38,8 @@ public function boot(): void
|
||||
return new Toast;
|
||||
});
|
||||
|
||||
$this->extendSocialite();
|
||||
|
||||
if (str(config('app.url'))->startsWith('https://')) {
|
||||
URL::forceScheme('https');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws BindingResolutionException
|
||||
*/
|
||||
private function extendSocialite(): void
|
||||
{
|
||||
$socialite = $this->app->make('Laravel\Socialite\Contracts\Factory');
|
||||
$socialite->extend(
|
||||
'dropbox',
|
||||
function ($app) use ($socialite) {
|
||||
$config = $app['config']['services.dropbox'];
|
||||
|
||||
return $socialite->buildProvider(DropboxProvider::class, $config);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,78 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Support\SocialiteProviders;
|
||||
|
||||
use GuzzleHttp\Exception\GuzzleException;
|
||||
use Illuminate\Support\Arr;
|
||||
use Laravel\Socialite\Two\AbstractProvider;
|
||||
use Laravel\Socialite\Two\ProviderInterface;
|
||||
use Laravel\Socialite\Two\User;
|
||||
|
||||
class DropboxProvider extends AbstractProvider implements ProviderInterface
|
||||
{
|
||||
/**
|
||||
* Unique Provider Identifier.
|
||||
*/
|
||||
public const IDENTIFIER = 'DROPBOX';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $scopeSeparator = ' ';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getAuthUrl($state): string
|
||||
{
|
||||
return $this->buildAuthUrlFromBase('https://www.dropbox.com/oauth2/authorize', $state);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getTokenUrl(): string
|
||||
{
|
||||
return 'https://api.dropboxapi.com/oauth2/token';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getTokenFields($code): array
|
||||
{
|
||||
return array_merge(parent::getTokenFields($code), [
|
||||
'grant_type' => 'authorization_code',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @throws GuzzleException
|
||||
*/
|
||||
protected function getUserByToken($token)
|
||||
{
|
||||
$response = $this->getHttpClient()->post('https://api.dropboxapi.com/2/users/get_current_account', [
|
||||
'headers' => [
|
||||
'Authorization' => 'Bearer '.$token,
|
||||
],
|
||||
]);
|
||||
|
||||
return json_decode($response->getBody(), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function mapUserToObject(array $user): User
|
||||
{
|
||||
return (new User)->setRaw($user)->map([
|
||||
'id' => $user['account_id'],
|
||||
'nickname' => null,
|
||||
'name' => $user['name']['display_name'],
|
||||
'email' => $user['email'],
|
||||
'avatar' => Arr::get($user, 'profile_photo_url'),
|
||||
]);
|
||||
}
|
||||
}
|
@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Support\Testing;
|
||||
|
||||
use App\Exceptions\SSHConnectionError;
|
||||
use App\Helpers\SSH;
|
||||
use Illuminate\Support\Traits\ReflectsClosures;
|
||||
use PHPUnit\Framework\Assert;
|
||||
@ -14,11 +15,25 @@ class SSHFake extends SSH
|
||||
|
||||
protected ?string $output;
|
||||
|
||||
protected bool $connectionWillFail = false;
|
||||
|
||||
public function __construct(?string $output = null)
|
||||
{
|
||||
$this->output = $output;
|
||||
}
|
||||
|
||||
public function connectionWillFail(): void
|
||||
{
|
||||
$this->connectionWillFail = true;
|
||||
}
|
||||
|
||||
public function connect(bool $sftp = false): void
|
||||
{
|
||||
if ($this->connectionWillFail) {
|
||||
throw new SSHConnectionError('Connection failed');
|
||||
}
|
||||
}
|
||||
|
||||
public function exec(string|array $commands, string $log = '', ?int $siteId = null): string
|
||||
{
|
||||
if ($log) {
|
||||
@ -45,6 +60,11 @@ public function exec(string|array $commands, string $log = '', ?int $siteId = nu
|
||||
return $output;
|
||||
}
|
||||
|
||||
public function upload(string $local, string $remote): void
|
||||
{
|
||||
$this->log = null;
|
||||
}
|
||||
|
||||
public function assertExecuted(array|string $commands): void
|
||||
{
|
||||
if (! $this->commands) {
|
||||
|
@ -17,7 +17,6 @@
|
||||
"laravel/fortify": "^1.17",
|
||||
"laravel/framework": "^10.0",
|
||||
"laravel/sanctum": "^3.2",
|
||||
"laravel/socialite": "^5.2",
|
||||
"laravel/tinker": "^2.8",
|
||||
"opcodesio/log-viewer": "^3.0",
|
||||
"phpseclib/phpseclib": "~3.0"
|
||||
|
150
composer.lock
generated
150
composer.lock
generated
@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "56d10a007fc0e676b85e8559984d1867",
|
||||
"content-hash": "3e51f672a4139d2a840cdcd2e48ccaaf",
|
||||
"packages": [
|
||||
{
|
||||
"name": "aws/aws-crt-php",
|
||||
@ -2200,76 +2200,6 @@
|
||||
},
|
||||
"time": "2023-11-08T14:08:06+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laravel/socialite",
|
||||
"version": "v5.12.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/laravel/socialite.git",
|
||||
"reference": "7dae1b072573809f32ab6dcf4aebb57c8b3e8acf"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/laravel/socialite/zipball/7dae1b072573809f32ab6dcf4aebb57c8b3e8acf",
|
||||
"reference": "7dae1b072573809f32ab6dcf4aebb57c8b3e8acf",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-json": "*",
|
||||
"guzzlehttp/guzzle": "^6.0|^7.0",
|
||||
"illuminate/contracts": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0",
|
||||
"illuminate/http": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0",
|
||||
"illuminate/support": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0",
|
||||
"league/oauth1-client": "^1.10.1",
|
||||
"php": "^7.2|^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"mockery/mockery": "^1.0",
|
||||
"orchestra/testbench": "^4.0|^5.0|^6.0|^7.0|^8.0|^9.0",
|
||||
"phpstan/phpstan": "^1.10",
|
||||
"phpunit/phpunit": "^8.0|^9.3|^10.4"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "5.x-dev"
|
||||
},
|
||||
"laravel": {
|
||||
"providers": [
|
||||
"Laravel\\Socialite\\SocialiteServiceProvider"
|
||||
],
|
||||
"aliases": {
|
||||
"Socialite": "Laravel\\Socialite\\Facades\\Socialite"
|
||||
}
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Laravel\\Socialite\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Taylor Otwell",
|
||||
"email": "taylor@laravel.com"
|
||||
}
|
||||
],
|
||||
"description": "Laravel wrapper around OAuth 1 & OAuth 2 libraries.",
|
||||
"homepage": "https://laravel.com",
|
||||
"keywords": [
|
||||
"laravel",
|
||||
"oauth"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/laravel/socialite/issues",
|
||||
"source": "https://github.com/laravel/socialite"
|
||||
},
|
||||
"time": "2024-02-16T08:58:20+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laravel/tinker",
|
||||
"version": "v2.9.0",
|
||||
@ -2730,82 +2660,6 @@
|
||||
],
|
||||
"time": "2024-01-28T23:22:08+00:00"
|
||||
},
|
||||
{
|
||||
"name": "league/oauth1-client",
|
||||
"version": "v1.10.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/thephpleague/oauth1-client.git",
|
||||
"reference": "d6365b901b5c287dd41f143033315e2f777e1167"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/thephpleague/oauth1-client/zipball/d6365b901b5c287dd41f143033315e2f777e1167",
|
||||
"reference": "d6365b901b5c287dd41f143033315e2f777e1167",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-json": "*",
|
||||
"ext-openssl": "*",
|
||||
"guzzlehttp/guzzle": "^6.0|^7.0",
|
||||
"guzzlehttp/psr7": "^1.7|^2.0",
|
||||
"php": ">=7.1||>=8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"ext-simplexml": "*",
|
||||
"friendsofphp/php-cs-fixer": "^2.17",
|
||||
"mockery/mockery": "^1.3.3",
|
||||
"phpstan/phpstan": "^0.12.42",
|
||||
"phpunit/phpunit": "^7.5||9.5"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-simplexml": "For decoding XML-based responses."
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.0-dev",
|
||||
"dev-develop": "2.0-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"League\\OAuth1\\Client\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Ben Corlett",
|
||||
"email": "bencorlett@me.com",
|
||||
"homepage": "http://www.webcomm.com.au",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"description": "OAuth 1.0 Client Library",
|
||||
"keywords": [
|
||||
"Authentication",
|
||||
"SSO",
|
||||
"authorization",
|
||||
"bitbucket",
|
||||
"identity",
|
||||
"idp",
|
||||
"oauth",
|
||||
"oauth1",
|
||||
"single sign on",
|
||||
"trello",
|
||||
"tumblr",
|
||||
"twitter"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/thephpleague/oauth1-client/issues",
|
||||
"source": "https://github.com/thephpleague/oauth1-client/tree/v1.10.1"
|
||||
},
|
||||
"time": "2022-04-15T14:02:14+00:00"
|
||||
},
|
||||
{
|
||||
"name": "monolog/monolog",
|
||||
"version": "3.5.0",
|
||||
@ -9529,5 +9383,5 @@
|
||||
"ext-ftp": "*"
|
||||
},
|
||||
"platform-dev": [],
|
||||
"plugin-api-version": "2.6.0"
|
||||
"plugin-api-version": "2.2.0"
|
||||
}
|
||||
|
@ -1,25 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use App\Models\Redirect;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
use Illuminate\Support\Carbon;
|
||||
|
||||
class RedirectFactory extends Factory
|
||||
{
|
||||
protected $model = Redirect::class;
|
||||
|
||||
public function definition(): array
|
||||
{
|
||||
return [
|
||||
'site_id' => $this->faker->randomNumber(),
|
||||
'mode' => $this->faker->randomNumber(),
|
||||
'from' => $this->faker->word(),
|
||||
'to' => $this->faker->word(),
|
||||
'status' => $this->faker->word(),
|
||||
'created_at' => Carbon::now(),
|
||||
'updated_at' => Carbon::now(),
|
||||
];
|
||||
}
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use App\Models\Script;
|
||||
use App\Models\ScriptExecution;
|
||||
use App\Models\Server;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
use Illuminate\Support\Carbon;
|
||||
|
||||
class ScriptExecutionFactory extends Factory
|
||||
{
|
||||
protected $model = ScriptExecution::class;
|
||||
|
||||
public function definition(): array
|
||||
{
|
||||
return [
|
||||
'user' => $this->faker->word(),
|
||||
'finished_at' => Carbon::now(),
|
||||
'created_at' => Carbon::now(),
|
||||
'updated_at' => Carbon::now(),
|
||||
'script_id' => Script::factory(),
|
||||
'server_id' => Server::factory(),
|
||||
];
|
||||
}
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
|
||||
class ScriptFactory extends Factory
|
||||
{
|
||||
public function definition(): array
|
||||
{
|
||||
return [
|
||||
//
|
||||
];
|
||||
}
|
||||
}
|
1
public/static/libs/ace/mode-sh.js
Normal file
1
public/static/libs/ace/mode-sh.js
Normal file
@ -0,0 +1 @@
|
||||
ace.define("ace/mode/sh", ["require", "exports", "module", "ace/lib/oop", "ace/mode/text", "ace/tokenizer", "ace/mode/sh_highlight_rules", "ace/range"], function (e, t, n) { var r = e("../lib/oop"), i = e("./text").Mode, s = e("../tokenizer").Tokenizer, o = e("./sh_highlight_rules").ShHighlightRules, u = e("../range").Range, a = function () { this.$tokenizer = new s((new o).getRules()) }; r.inherits(a, i), function () { this.toggleCommentLines = function (e, t, n, r) { var i = !0, s = /^(\s*)#/; for (var o = n; o <= r; o++)if (!s.test(t.getLine(o))) { i = !1; break } if (i) { var a = new u(0, 0, 0, 0); for (var o = n; o <= r; o++) { var f = t.getLine(o), l = f.match(s); a.start.row = o, a.end.row = o, a.end.column = l[0].length, t.replace(a, l[1]) } } else t.indentRows(n, r, "#") }, this.getNextLineIndent = function (e, t, n) { var r = this.$getIndent(t), i = this.$tokenizer.getLineTokens(t, e), s = i.tokens; if (s.length && s[s.length - 1].type == "comment") return r; if (e == "start") { var o = t.match(/^.*[\{\(\[\:]\s*$/); o && (r += n) } return r }; var e = { pass: 1, "return": 1, raise: 1, "break": 1, "continue": 1 }; this.checkOutdent = function (t, n, r) { if (r !== "\r\n" && r !== "\r" && r !== "\n") return !1; var i = this.$tokenizer.getLineTokens(n.trim(), t).tokens; if (!i) return !1; do var s = i.pop(); while (s && (s.type == "comment" || s.type == "text" && s.value.match(/^\s+$/))); return s ? s.type == "keyword" && e[s.value] : !1 }, this.autoOutdent = function (e, t, n) { n += 1; var r = this.$getIndent(t.getLine(n)), i = t.getTabString(); r.slice(-i.length) == i && t.remove(new u(n, r.length - i.length, n, r.length)) } }.call(a.prototype), t.Mode = a }), ace.define("ace/mode/sh_highlight_rules", ["require", "exports", "module", "ace/lib/oop", "ace/mode/text_highlight_rules"], function (e, t, n) { var r = e("../lib/oop"), i = e("./text_highlight_rules").TextHighlightRules, s = t.reservedKeywords = "!|{|}|case|do|done|elif|else|esac|fi|for|if|in|then|until|while|&|;|export|local|read|typeset|unset|elif|select|set", o = t.languageConstructs = "[|]|alias|bg|bind|break|builtin|cd|command|compgen|complete|continue|dirs|disown|echo|enable|eval|exec|exit|fc|fg|getopts|hash|help|history|jobs|kill|let|logout|popd|printf|pushd|pwd|return|set|shift|shopt|source|suspend|test|times|trap|type|ulimit|umask|unalias|wait", u = function () { var e = this.createKeywordMapper({ keyword: s, "support.function.builtin": o, "invalid.deprecated": "debugger" }, "identifier"), t = "(?:(?:[1-9]\\d*)|(?:0))", n = "(?:\\.\\d+)", r = "(?:\\d+)", i = "(?:(?:" + r + "?" + n + ")|(?:" + r + "\\.))", u = "(?:(?:" + i + "|" + r + ")" + ")", a = "(?:" + u + "|" + i + ")", f = "(?:&" + r + ")", l = "[a-zA-Z][a-zA-Z0-9_]*", c = "(?:(?:\\$" + l + ")|(?:" + l + "=))", h = "(?:\\$(?:SHLVL|\\$|\\!|\\?))", p = "(?:" + l + "\\s*\\(\\))"; this.$rules = { start: [{ token: "comment", regex: "#.*$" }, { token: "string", regex: '"(?:[^\\\\]|\\\\.)*?"' }, { token: "variable.language", regex: h }, { token: "variable", regex: c }, { token: "support.function", regex: p }, { token: "support.function", regex: f }, { token: "string", regex: "'(?:[^\\\\]|\\\\.)*?'" }, { token: "constant.numeric", regex: a }, { token: "constant.numeric", regex: t + "\\b" }, { token: e, regex: "[a-zA-Z_$][a-zA-Z0-9_$]*\\b" }, { token: "keyword.operator", regex: "\\+|\\-|\\*|\\*\\*|\\/|\\/\\/|~|<|>|<=|=>|=|!=" }, { token: "paren.lparen", regex: "[\\[\\(\\{]" }, { token: "paren.rparen", regex: "[\\]\\)\\}]" }, { token: "text", regex: "\\s+" }] } }; r.inherits(u, i), t.ShHighlightRules = u })
|
5
public/static/libs/ace/theme-github.js
Normal file
5
public/static/libs/ace/theme-github.js
Normal file
@ -0,0 +1,5 @@
|
||||
ace.define("ace/theme/github", ["require", "exports", "module", "ace/lib/dom"], function (e, t, n) {
|
||||
t.isDark = !1, t.cssClass = "ace-github", t.cssText = '/* CSS style content from github\'s default pygments highlighter template.Cursor and selection styles from textmate.css. */.ace-github .ace_gutter {background: #e8e8e8;color: #AAA;}.ace-github .ace_scroller {background: #fff;}.ace-github .ace_keyword {font-weight: bold;}.ace-github .ace_string {color: #D14;}.ace-github .ace_variable.ace_class {color: teal;}.ace-github .ace_constant.ace_numeric {color: #099;}.ace-github .ace_constant.ace_buildin {color: #0086B3;}.ace-github .ace_support.ace_function {color: #0086B3;}.ace-github .ace_comment {color: #998;font-style: italic;}.ace-github .ace_variable.ace_language {color: #0086B3;}.ace-github .ace_paren {font-weight: bold;}.ace-github .ace_boolean {font-weight: bold;}.ace-github .ace_string.ace_regexp {color: #009926;font-weight: normal;}.ace-github .ace_variable.ace_instance {color: teal;}.ace-github .ace_constant.ace_language {font-weight: bold;}.ace-github .ace_text-layer {}.ace-github .ace_cursor {border-left: 2px solid black;}.ace-github .ace_overwrite-cursors .ace_cursor {border-left: 0px;border-bottom: 1px solid black;}.ace-github .ace_marker-layer .ace_active-line {background: rgb(255, 255, 204);}.ace-github .ace_marker-layer .ace_selection {background: rgb(181, 213, 255);}.ace-github.ace_multiselect .ace_selection.ace_start {box-shadow: 0 0 3px 0px white;border-radius: 2px;}/* bold keywords cause cursor issues for some fonts *//* this disables bold style for editor and keeps for static highlighter */.ace-github.ace_nobold .ace_line > span {font-weight: normal !important;}.ace-github .ace_marker-layer .ace_step {background: rgb(252, 255, 0);}.ace-github .ace_marker-layer .ace_stack {background: rgb(164, 229, 101);}.ace-github .ace_marker-layer .ace_bracket {margin: -1px 0 0 -1px;border: 1px solid rgb(192, 192, 192);}.ace-github .ace_gutter-active-line {background-color : rgba(0, 0, 0, 0.07);}.ace-github .ace_marker-layer .ace_selected-word {background: rgb(250, 250, 255);border: 1px solid rgb(200, 200, 250);}.ace-github .ace_print-margin {width: 1px;background: #e8e8e8;}.ace-github .ace_indent-guide {background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAE0lEQVQImWP4////f4bLly//BwAmVgd1/w11/gAAAABJRU5ErkJggg==") right repeat-y;}';
|
||||
var r = e("../lib/dom");
|
||||
r.importCssString(t.cssText, t.cssClass)
|
||||
})
|
5
public/static/libs/ace/theme-one-dark.js
Normal file
5
public/static/libs/ace/theme-one-dark.js
Normal file
@ -0,0 +1,5 @@
|
||||
ace.define("ace/theme/one-dark", ["require", "exports", "module", "ace/lib/dom"], function (e, t, n) {
|
||||
t.isDark = !1, t.cssClass = "ace-one-dark", t.cssText = '/* CSS style content from one-dark\'s default pygments highlighter template.Cursor and selection styles from textmate.css. */.ace-one-dark .ace_gutter{background:#282c34;color:#6a6f7a}.ace-one-dark .ace_print-margin{width:1px;background:#e8e8e8}.ace-one-dark{background-color:#282c34;color:#abb2bf}.ace-one-dark .ace_cursor{color:#528bff}.ace-one-dark .ace_marker-layer .ace_selection{background:#3d4350}.ace-one-dark.ace_multiselect .ace_selection.ace_start{box-shadow:0 0 3px 0 #282c34;border-radius:2px}.ace-one-dark .ace_marker-layer .ace_step{background:#c6dbae}.ace-one-dark .ace_marker-layer .ace_bracket{margin:-1px 0 0 -1px;border:1px solid #747369}.ace-one-dark .ace_marker-layer .ace_active-line{background:rgba(76,87,103,.19)}.ace-one-dark .ace_gutter-active-line{background-color:rgba(76,87,103,.19)}.ace-one-dark .ace_marker-layer .ace_selected-word{border:1px solid #3d4350}.ace-one-dark .ace_fold{background-color:#61afef;border-color:#abb2bf}.ace-one-dark .ace_keyword{color:#c678dd}.ace-one-dark .ace_keyword.ace_operator{color:#c678dd}.ace-one-dark .ace_keyword.ace_other.ace_unit{color:#d19a66}.ace-one-dark .ace_constant.ace_language{color:#d19a66}.ace-one-dark .ace_constant.ace_numeric{color:#d19a66}.ace-one-dark .ace_constant.ace_character{color:#56b6c2}.ace-one-dark .ace_constant.ace_other{color:#56b6c2}.ace-one-dark .ace_support.ace_function{color:#61afef}.ace-one-dark .ace_support.ace_constant{color:#d19a66}.ace-one-dark .ace_support.ace_class{color:#e5c07b}.ace-one-dark .ace_support.ace_type{color:#e5c07b}.ace-one-dark .ace_storage{color:#c678dd}.ace-one-dark .ace_storage.ace_type{color:#c678dd}.ace-one-dark .ace_invalid{color:#fff;background-color:#f2777a}.ace-one-dark .ace_invalid.ace_deprecated{color:#272b33;background-color:#d27b53}.ace-one-dark .ace_string{color:#98c379}.ace-one-dark .ace_string.ace_regexp{color:#e06c75}.ace-one-dark .ace_comment{font-style:italic;color:#5c6370}.ace-one-dark .ace_variable{color:#e06c75}.ace-one-dark .ace_variable.ace_parameter{color:#d19a66}.ace-one-dark .ace_meta.ace_tag{color:#e06c75}.ace-one-dark .ace_entity.ace_other.ace_attribute-name{color:#e06c75}.ace-one-dark .ace_entity.ace_name.ace_function{color:#61afef}.ace-one-dark .ace_entity.ace_name.ace_tag{color:#e06c75}.ace-one-dark .ace_markup.ace_heading{color:#98c379}.ace-one-dark .ace_indent-guide{background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAEklEQVQImWPQ09NrYAgMjP4PAAtGAwchHMyAAAAAAElFTkSuQmCC) right repeat-y}';
|
||||
var r = e("../lib/dom");
|
||||
r.importCssString(t.cssText, t.cssClass)
|
||||
})
|
@ -16,6 +16,7 @@
|
||||
<tr>
|
||||
<x-th>{{ __("Database") }}</x-th>
|
||||
<x-th>{{ __("Created") }}</x-th>
|
||||
<x-th>{{ __("Storage") }}</x-th>
|
||||
<x-th>{{ __("Status") }}</x-th>
|
||||
<x-th></x-th>
|
||||
</tr>
|
||||
@ -25,6 +26,7 @@
|
||||
<x-td>
|
||||
<x-datetime :value="$backup->created_at" />
|
||||
</x-td>
|
||||
<x-td>{{ $backup->storage->profile }} ({{ $backup->storage->provider }})</x-td>
|
||||
<x-td>
|
||||
<div class="inline-flex">
|
||||
@include("databases.partials.backup-status", ["status" => $backup->status])
|
||||
|
@ -29,7 +29,7 @@
|
||||
Route::prefix('settings/server-providers')->group(function () {
|
||||
Route::get('/', [ServerProviderController::class, 'index'])->name('server-providers');
|
||||
Route::post('connect', [ServerProviderController::class, 'connect'])->name('server-providers.connect');
|
||||
Route::delete('delete/{id}', [ServerProviderController::class, 'delete'])
|
||||
Route::delete('delete/{serverProvider}', [ServerProviderController::class, 'delete'])
|
||||
->name('server-providers.delete');
|
||||
});
|
||||
|
||||
@ -37,7 +37,7 @@
|
||||
Route::prefix('settings/source-controls')->group(function () {
|
||||
Route::get('/', [SourceControlController::class, 'index'])->name('source-controls');
|
||||
Route::post('connect', [SourceControlController::class, 'connect'])->name('source-controls.connect');
|
||||
Route::delete('delete/{id}', [SourceControlController::class, 'delete'])
|
||||
Route::delete('delete/{sourceControl}', [SourceControlController::class, 'delete'])
|
||||
->name('source-controls.delete');
|
||||
});
|
||||
|
||||
@ -45,7 +45,7 @@
|
||||
Route::prefix('settings/storage-providers')->group(function () {
|
||||
Route::get('/', [StorageProviderController::class, 'index'])->name('storage-providers');
|
||||
Route::post('connect', [StorageProviderController::class, 'connect'])->name('storage-providers.connect');
|
||||
Route::delete('delete/{id}', [StorageProviderController::class, 'delete'])
|
||||
Route::delete('delete/{storageProvider}', [StorageProviderController::class, 'delete'])
|
||||
->name('storage-providers.delete');
|
||||
});
|
||||
|
||||
|
@ -184,4 +184,70 @@ public function test_update_env_file(): void
|
||||
|
||||
SSH::assertExecutedContains('echo "APP_ENV=production" | tee /home/vito/'.$this->site->domain.'/.env');
|
||||
}
|
||||
|
||||
public function test_git_hook_deployment(): void
|
||||
{
|
||||
SSH::fake();
|
||||
Http::fake([
|
||||
'github.com/*' => Http::response([
|
||||
'sha' => '123',
|
||||
'commit' => [
|
||||
'message' => 'test commit message',
|
||||
'name' => 'test commit name',
|
||||
'email' => 'user@example.com',
|
||||
'url' => 'https://github.com',
|
||||
],
|
||||
], 200),
|
||||
]);
|
||||
|
||||
$hook = GitHook::factory()->create([
|
||||
'site_id' => $this->site->id,
|
||||
'source_control_id' => $this->site->source_control_id,
|
||||
'secret' => 'secret',
|
||||
'events' => ['push'],
|
||||
'actions' => ['deploy'],
|
||||
]);
|
||||
|
||||
$this->site->deploymentScript->update([
|
||||
'content' => 'git pull',
|
||||
]);
|
||||
|
||||
$this->post(route('git-hooks'), [
|
||||
'secret' => 'secret',
|
||||
])->assertSessionDoesntHaveErrors();
|
||||
|
||||
$this->assertDatabaseHas('deployments', [
|
||||
'site_id' => $this->site->id,
|
||||
'deployment_script_id' => $this->site->deploymentScript->id,
|
||||
'status' => DeploymentStatus::FINISHED,
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_git_hook_deployment_invalid_secret(): void
|
||||
{
|
||||
SSH::fake();
|
||||
Http::fake();
|
||||
|
||||
$hook = GitHook::factory()->create([
|
||||
'site_id' => $this->site->id,
|
||||
'source_control_id' => $this->site->source_control_id,
|
||||
'secret' => 'secret',
|
||||
'events' => ['push'],
|
||||
'actions' => ['deploy'],
|
||||
]);
|
||||
|
||||
$this->site->deploymentScript->update([
|
||||
'content' => 'git pull',
|
||||
]);
|
||||
|
||||
$this->post(route('git-hooks'), [
|
||||
'secret' => 'invalid-secret',
|
||||
])->assertNotFound();
|
||||
|
||||
$this->assertDatabaseMissing('deployments', [
|
||||
'site_id' => $this->site->id,
|
||||
'deployment_script_id' => $this->site->deploymentScript->id,
|
||||
'status' => DeploymentStatus::FINISHED,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
@ -37,4 +37,11 @@ public function test_users_can_not_authenticate_with_invalid_password(): void
|
||||
|
||||
$this->assertGuest();
|
||||
}
|
||||
|
||||
public function test_redirect_if_not_authenticated(): void
|
||||
{
|
||||
$response = $this->get('/servers');
|
||||
|
||||
$response->assertRedirect('/login');
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
namespace Tests\Feature;
|
||||
|
||||
use App\Actions\Database\RunBackup;
|
||||
use App\Enums\BackupFileStatus;
|
||||
use App\Enums\BackupStatus;
|
||||
use App\Facades\SSH;
|
||||
@ -9,6 +10,7 @@
|
||||
use App\Models\Database;
|
||||
use App\Models\StorageProvider;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Facades\Bus;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Tests\TestCase;
|
||||
|
||||
@ -48,6 +50,34 @@ public function test_create_backup(): void
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_create_custom_interval_backup(): void
|
||||
{
|
||||
Bus::fake();
|
||||
|
||||
$this->actingAs($this->user);
|
||||
|
||||
$database = Database::factory()->create([
|
||||
'server_id' => $this->server,
|
||||
]);
|
||||
|
||||
$storage = StorageProvider::factory()->create([
|
||||
'user_id' => $this->user->id,
|
||||
'provider' => \App\Enums\StorageProvider::DROPBOX,
|
||||
]);
|
||||
|
||||
$this->post(route('servers.databases.backups.store', $this->server), [
|
||||
'backup_database' => $database->id,
|
||||
'backup_storage' => $storage->id,
|
||||
'backup_interval' => 'custom',
|
||||
'backup_custom' => '* * * * *',
|
||||
'backup_keep' => '10',
|
||||
])->assertSessionDoesntHaveErrors();
|
||||
|
||||
$this->assertDatabaseHas('backups', [
|
||||
'status' => BackupStatus::RUNNING,
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_see_backups_list(): void
|
||||
{
|
||||
$this->actingAs($this->user);
|
||||
@ -97,4 +127,43 @@ public function test_delete_backup(): void
|
||||
'id' => $backup->id,
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_restore_backup(): void
|
||||
{
|
||||
Http::fake();
|
||||
SSH::fake();
|
||||
|
||||
$this->actingAs($this->user);
|
||||
|
||||
$database = Database::factory()->create([
|
||||
'server_id' => $this->server,
|
||||
]);
|
||||
|
||||
$storage = StorageProvider::factory()->create([
|
||||
'user_id' => $this->user->id,
|
||||
'provider' => \App\Enums\StorageProvider::DROPBOX,
|
||||
]);
|
||||
|
||||
$backup = Backup::factory()->create([
|
||||
'server_id' => $this->server->id,
|
||||
'database_id' => $database->id,
|
||||
'storage_id' => $storage->id,
|
||||
]);
|
||||
|
||||
$backupFile = app(RunBackup::class)->run($backup);
|
||||
|
||||
$this->post(route('servers.databases.backups.files.restore', [
|
||||
'server' => $this->server,
|
||||
'backup' => $backup,
|
||||
'backupFile' => $backupFile,
|
||||
]), [
|
||||
'database' => $database->id,
|
||||
])
|
||||
->assertSessionDoesntHaveErrors();
|
||||
|
||||
$this->assertDatabaseHas('backup_files', [
|
||||
'id' => $backupFile->id,
|
||||
'status' => BackupFileStatus::RESTORED,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
namespace Tests\Feature;
|
||||
|
||||
use App\Enums\DatabaseStatus;
|
||||
use App\Enums\DatabaseUserStatus;
|
||||
use App\Facades\SSH;
|
||||
use App\Models\Database;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
@ -28,6 +29,34 @@ public function test_create_database(): void
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_create_database_with_user(): void
|
||||
{
|
||||
$this->actingAs($this->user);
|
||||
|
||||
SSH::fake();
|
||||
|
||||
$this->post(route('servers.databases.store', $this->server), [
|
||||
'name' => 'database',
|
||||
'user' => 'on',
|
||||
'username' => 'user',
|
||||
'password' => 'password',
|
||||
'remote' => 'on',
|
||||
'host' => '%',
|
||||
])->assertSessionDoesntHaveErrors();
|
||||
|
||||
$this->assertDatabaseHas('databases', [
|
||||
'name' => 'database',
|
||||
'status' => DatabaseStatus::READY,
|
||||
]);
|
||||
|
||||
$this->assertDatabaseHas('database_users', [
|
||||
'username' => 'user',
|
||||
'databases' => json_encode(['database']),
|
||||
'host' => '%',
|
||||
'status' => DatabaseUserStatus::READY,
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_see_databases_list(): void
|
||||
{
|
||||
$this->actingAs($this->user);
|
||||
|
@ -29,6 +29,26 @@ public function test_create_database_user(): void
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_create_database_user_with_remote(): void
|
||||
{
|
||||
$this->actingAs($this->user);
|
||||
|
||||
SSH::fake();
|
||||
|
||||
$this->post(route('servers.databases.users.store', $this->server), [
|
||||
'username' => 'user',
|
||||
'password' => 'password',
|
||||
'remote' => 'on',
|
||||
'host' => '%',
|
||||
])->assertSessionDoesntHaveErrors();
|
||||
|
||||
$this->assertDatabaseHas('database_users', [
|
||||
'username' => 'user',
|
||||
'host' => '%',
|
||||
'status' => DatabaseUserStatus::READY,
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_see_database_users_list(): void
|
||||
{
|
||||
$this->actingAs($this->user);
|
||||
@ -58,4 +78,26 @@ public function test_delete_database_user(): void
|
||||
'id' => $databaseUser->id,
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_unlink_database(): void
|
||||
{
|
||||
$this->actingAs($this->user);
|
||||
|
||||
SSH::fake();
|
||||
|
||||
$databaseUser = DatabaseUser::factory()->create([
|
||||
'server_id' => $this->server,
|
||||
]);
|
||||
|
||||
$this->post(route('servers.databases.users.link', [
|
||||
'server' => $this->server,
|
||||
'databaseUser' => $databaseUser,
|
||||
]), [])
|
||||
->assertSessionDoesntHaveErrors();
|
||||
|
||||
$this->assertDatabaseHas('database_users', [
|
||||
'username' => $databaseUser->username,
|
||||
'databases' => json_encode([]),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
@ -26,12 +26,35 @@ public function test_add_email_channel(): void
|
||||
/** @var \App\Models\NotificationChannel $channel */
|
||||
$channel = \App\Models\NotificationChannel::query()
|
||||
->where('provider', NotificationChannel::EMAIL)
|
||||
->where('label', 'Email')
|
||||
->first();
|
||||
|
||||
$this->assertEquals('email@example.com', $channel->data['email']);
|
||||
$this->assertTrue($channel->connected);
|
||||
}
|
||||
|
||||
public function test_cannot_add_email_channel(): void
|
||||
{
|
||||
config()->set('mail.default', 'smtp');
|
||||
config()->set('mail.mailers.smtp.host', '127.0.0.1'); // invalid host
|
||||
|
||||
$this->actingAs($this->user);
|
||||
|
||||
$this->post(route('notification-channels.add'), [
|
||||
'provider' => NotificationChannel::EMAIL,
|
||||
'email' => 'email@example.com',
|
||||
'label' => 'Email',
|
||||
])->assertSessionHasErrors();
|
||||
|
||||
/** @var \App\Models\NotificationChannel $channel */
|
||||
$channel = \App\Models\NotificationChannel::query()
|
||||
->where('provider', NotificationChannel::EMAIL)
|
||||
->where('label', 'Email')
|
||||
->first();
|
||||
|
||||
$this->assertNull($channel);
|
||||
}
|
||||
|
||||
public function test_add_slack_channel(): void
|
||||
{
|
||||
$this->actingAs($this->user);
|
||||
@ -53,6 +76,28 @@ public function test_add_slack_channel(): void
|
||||
$this->assertTrue($channel->connected);
|
||||
}
|
||||
|
||||
public function test_cannot_add_slack_channel(): void
|
||||
{
|
||||
$this->actingAs($this->user);
|
||||
|
||||
Http::fake([
|
||||
'slack.com/*' => Http::response(['ok' => false], 401),
|
||||
]);
|
||||
|
||||
$this->post(route('notification-channels.add'), [
|
||||
'provider' => NotificationChannel::SLACK,
|
||||
'webhook_url' => 'https://hooks.slack.com/services/123/token',
|
||||
'label' => 'Slack',
|
||||
])->assertSessionHasErrors();
|
||||
|
||||
/** @var \App\Models\NotificationChannel $channel */
|
||||
$channel = \App\Models\NotificationChannel::query()
|
||||
->where('provider', NotificationChannel::SLACK)
|
||||
->first();
|
||||
|
||||
$this->assertNull($channel);
|
||||
}
|
||||
|
||||
public function test_add_discord_channel(): void
|
||||
{
|
||||
$this->actingAs($this->user);
|
||||
@ -74,9 +119,27 @@ public function test_add_discord_channel(): void
|
||||
$this->assertTrue($channel->connected);
|
||||
}
|
||||
|
||||
/*
|
||||
* @TODO fix json comparison
|
||||
*/
|
||||
public function test_cannot_add_discord_channel(): void
|
||||
{
|
||||
$this->actingAs($this->user);
|
||||
|
||||
Http::fake([
|
||||
'discord.com/*' => Http::response(['ok' => false], 401),
|
||||
]);
|
||||
|
||||
$this->post(route('notification-channels.add'), [
|
||||
'provider' => NotificationChannel::DISCORD,
|
||||
'webhook_url' => 'https://discord.com/api/webhooks/123/token',
|
||||
'label' => 'Discord',
|
||||
])->assertSessionHasErrors();
|
||||
|
||||
/** @var \App\Models\NotificationChannel $channel */
|
||||
$channel = \App\Models\NotificationChannel::query()
|
||||
->where('provider', NotificationChannel::DISCORD)
|
||||
->first();
|
||||
|
||||
$this->assertNull($channel);
|
||||
}
|
||||
|
||||
public function test_add_telegram_channel(): void
|
||||
{
|
||||
@ -101,6 +164,29 @@ public function test_add_telegram_channel(): void
|
||||
$this->assertTrue($channel->connected);
|
||||
}
|
||||
|
||||
public function test_cannot_add_telegram_channel(): void
|
||||
{
|
||||
$this->actingAs($this->user);
|
||||
|
||||
Http::fake([
|
||||
'api.telegram.org/*' => Http::response(['ok' => false], 401),
|
||||
]);
|
||||
|
||||
$this->post(route('notification-channels.add'), [
|
||||
'provider' => NotificationChannel::TELEGRAM,
|
||||
'bot_token' => 'token',
|
||||
'chat_id' => '123',
|
||||
'label' => 'Telegram',
|
||||
])->assertSessionHasErrors();
|
||||
|
||||
/** @var \App\Models\NotificationChannel $channel */
|
||||
$channel = \App\Models\NotificationChannel::query()
|
||||
->where('provider', NotificationChannel::TELEGRAM)
|
||||
->first();
|
||||
|
||||
$this->assertNull($channel);
|
||||
}
|
||||
|
||||
public function test_see_channels_list(): void
|
||||
{
|
||||
$this->actingAs($this->user);
|
||||
|
@ -135,4 +135,32 @@ public function test_extension_already_installed(): void
|
||||
'extension' => 'gmp',
|
||||
]))->assertSessionHasErrors();
|
||||
}
|
||||
|
||||
public function test_get_php_ini(): void
|
||||
{
|
||||
SSH::fake('[PHP ini]');
|
||||
|
||||
$this->actingAs($this->user);
|
||||
|
||||
$this->get(route('servers.php.get-ini', [
|
||||
'server' => $this->server,
|
||||
'version' => '8.2',
|
||||
]))->assertSessionHas('ini');
|
||||
}
|
||||
|
||||
public function test_update_php_ini(): void
|
||||
{
|
||||
SSH::fake();
|
||||
|
||||
$this->actingAs($this->user);
|
||||
|
||||
$this->post(route('servers.php.update-ini', [
|
||||
'server' => $this->server,
|
||||
'version' => '8.2',
|
||||
'ini' => 'new ini',
|
||||
]))
|
||||
->assertSessionDoesntHaveErrors()
|
||||
->assertSessionHas('toast.type', 'success')
|
||||
->assertSessionHas('toast.message', 'PHP ini updated!');
|
||||
}
|
||||
}
|
||||
|
@ -68,4 +68,16 @@ public function test_edit_project(): void
|
||||
'name' => 'new-name',
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_cannot_delete_last_project(): void
|
||||
{
|
||||
$this->actingAs($this->user);
|
||||
|
||||
$this->delete(route('projects.delete', [
|
||||
'project' => $this->user->currentProject,
|
||||
]))
|
||||
->assertSessionDoesntHaveErrors()
|
||||
->assertSessionHas('toast.type', 'error')
|
||||
->assertSessionHas('toast.message', 'Cannot delete the last project.');
|
||||
}
|
||||
}
|
||||
|
@ -86,4 +86,85 @@ public function test_create_queue()
|
||||
'status' => QueueStatus::RUNNING,
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_start_queue(): void
|
||||
{
|
||||
SSH::fake();
|
||||
|
||||
$this->actingAs($this->user);
|
||||
|
||||
$queue = Queue::factory()->create([
|
||||
'server_id' => $this->server->id,
|
||||
'site_id' => $this->site->id,
|
||||
'status' => QueueStatus::STOPPED,
|
||||
]);
|
||||
|
||||
$this->post(
|
||||
route('servers.sites.queues.action', [
|
||||
'action' => 'start',
|
||||
'server' => $this->server,
|
||||
'site' => $this->site,
|
||||
'queue' => $queue,
|
||||
])
|
||||
)->assertSessionDoesntHaveErrors();
|
||||
|
||||
$this->assertDatabaseHas('queues', [
|
||||
'id' => $queue->id,
|
||||
'status' => QueueStatus::RUNNING,
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_stop_queue(): void
|
||||
{
|
||||
SSH::fake();
|
||||
|
||||
$this->actingAs($this->user);
|
||||
|
||||
$queue = Queue::factory()->create([
|
||||
'server_id' => $this->server->id,
|
||||
'site_id' => $this->site->id,
|
||||
'status' => QueueStatus::RUNNING,
|
||||
]);
|
||||
|
||||
$this->post(
|
||||
route('servers.sites.queues.action', [
|
||||
'action' => 'stop',
|
||||
'server' => $this->server,
|
||||
'site' => $this->site,
|
||||
'queue' => $queue,
|
||||
])
|
||||
)->assertSessionDoesntHaveErrors();
|
||||
|
||||
$this->assertDatabaseHas('queues', [
|
||||
'id' => $queue->id,
|
||||
'status' => QueueStatus::STOPPED,
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_restart_queue(): void
|
||||
{
|
||||
SSH::fake();
|
||||
|
||||
$this->actingAs($this->user);
|
||||
|
||||
$queue = Queue::factory()->create([
|
||||
'server_id' => $this->server->id,
|
||||
'site_id' => $this->site->id,
|
||||
'status' => QueueStatus::RUNNING,
|
||||
]);
|
||||
|
||||
$this->post(
|
||||
route('servers.sites.queues.action', [
|
||||
'action' => 'restart',
|
||||
'server' => $this->server,
|
||||
'site' => $this->site,
|
||||
'queue' => $queue,
|
||||
])
|
||||
)->assertSessionDoesntHaveErrors();
|
||||
|
||||
$this->assertDatabaseHas('queues', [
|
||||
'id' => $queue->id,
|
||||
'status' => QueueStatus::RUNNING,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
@ -11,20 +11,52 @@ class ServerProvidersTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
public function test_connect_hetzner(): void
|
||||
/**
|
||||
* @dataProvider data
|
||||
*/
|
||||
public function test_connect_provider(string $provider, array $input): void
|
||||
{
|
||||
$this->actingAs($this->user);
|
||||
|
||||
Http::fake();
|
||||
|
||||
$this->post(route('server-providers.connect'), [
|
||||
'provider' => ServerProvider::HETZNER,
|
||||
'name' => 'profile',
|
||||
'token' => 'token',
|
||||
])->assertSessionDoesntHaveErrors();
|
||||
$data = array_merge(
|
||||
[
|
||||
'provider' => $provider,
|
||||
'name' => 'profile',
|
||||
],
|
||||
$input
|
||||
);
|
||||
$this->post(route('server-providers.connect'), $data)->assertSessionDoesntHaveErrors();
|
||||
|
||||
$this->assertDatabaseHas('server_providers', [
|
||||
'provider' => ServerProvider::HETZNER,
|
||||
'provider' => $provider,
|
||||
'profile' => 'profile',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider data
|
||||
*/
|
||||
public function test_cannot_connect_to_provider(string $provider, array $input): void
|
||||
{
|
||||
$this->actingAs($this->user);
|
||||
|
||||
Http::fake([
|
||||
'*' => Http::response([], 401),
|
||||
]);
|
||||
|
||||
$data = array_merge(
|
||||
[
|
||||
'provider' => $provider,
|
||||
'name' => 'profile',
|
||||
],
|
||||
$input
|
||||
);
|
||||
$this->post(route('server-providers.connect'), $data)->assertSessionHasErrors();
|
||||
|
||||
$this->assertDatabaseMissing('server_providers', [
|
||||
'provider' => $provider,
|
||||
'profile' => 'profile',
|
||||
]);
|
||||
}
|
||||
@ -41,19 +73,86 @@ public function test_see_providers_list(): void
|
||||
->assertSee($provider->profile);
|
||||
}
|
||||
|
||||
public function test_delete_provider(): void
|
||||
/**
|
||||
* @dataProvider data
|
||||
*/
|
||||
public function test_delete_provider(string $provider): void
|
||||
{
|
||||
$this->actingAs($this->user);
|
||||
|
||||
$provider = \App\Models\ServerProvider::factory()->create([
|
||||
'user_id' => $this->user->id,
|
||||
'provider' => $provider,
|
||||
]);
|
||||
|
||||
$this->delete(route('server-providers.delete', $provider->id))
|
||||
$this->delete(route('server-providers.delete', $provider))
|
||||
->assertSessionDoesntHaveErrors();
|
||||
|
||||
$this->assertDatabaseMissing('server_providers', [
|
||||
'id' => $provider->id,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider data
|
||||
*/
|
||||
public function test_cannot_delete_provider(string $provider): void
|
||||
{
|
||||
$this->actingAs($this->user);
|
||||
|
||||
$provider = \App\Models\ServerProvider::factory()->create([
|
||||
'user_id' => $this->user->id,
|
||||
'provider' => $provider,
|
||||
]);
|
||||
|
||||
$this->server->update([
|
||||
'provider_id' => $provider->id,
|
||||
]);
|
||||
|
||||
$this->delete(route('server-providers.delete', $provider))
|
||||
->assertSessionDoesntHaveErrors()
|
||||
->assertSessionHas('toast.type', 'error')
|
||||
->assertSessionHas('toast.message', 'This server provider is being used by a server.');
|
||||
|
||||
$this->assertDatabaseHas('server_providers', [
|
||||
'id' => $provider->id,
|
||||
]);
|
||||
}
|
||||
|
||||
public static function data(): array
|
||||
{
|
||||
return [
|
||||
// [
|
||||
// ServerProvider::AWS,
|
||||
// [
|
||||
// 'key' => 'key',
|
||||
// 'secret' => 'secret',
|
||||
// ],
|
||||
// ],
|
||||
[
|
||||
ServerProvider::LINODE,
|
||||
[
|
||||
'token' => 'token',
|
||||
],
|
||||
],
|
||||
[
|
||||
ServerProvider::DIGITALOCEAN,
|
||||
[
|
||||
'token' => 'token',
|
||||
],
|
||||
],
|
||||
[
|
||||
ServerProvider::VULTR,
|
||||
[
|
||||
'token' => 'token',
|
||||
],
|
||||
],
|
||||
[
|
||||
ServerProvider::HETZNER,
|
||||
[
|
||||
'token' => 'token',
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -10,17 +10,17 @@
|
||||
use App\Enums\ServiceStatus;
|
||||
use App\Enums\Webserver;
|
||||
use App\Facades\SSH;
|
||||
use App\NotificationChannels\Email\NotificationMail;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
use Tests\TestCase;
|
||||
|
||||
/**
|
||||
* @TODO add more tests
|
||||
*/
|
||||
class ServerTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
public function test_create_custom_server(): void
|
||||
public function test_create_regular_server(): void
|
||||
{
|
||||
$this->actingAs($this->user);
|
||||
|
||||
@ -76,6 +76,62 @@ public function test_create_custom_server(): void
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_create_database_server(): void
|
||||
{
|
||||
$this->actingAs($this->user);
|
||||
|
||||
SSH::fake('Active: active'); // fake output for service installations
|
||||
|
||||
$this->post(route('servers.create'), [
|
||||
'type' => ServerType::DATABASE,
|
||||
'provider' => ServerProvider::CUSTOM,
|
||||
'name' => 'test',
|
||||
'ip' => '2.2.2.2',
|
||||
'port' => '22',
|
||||
'os' => OperatingSystem::UBUNTU22,
|
||||
'database' => Database::MYSQL80,
|
||||
])->assertSessionDoesntHaveErrors();
|
||||
|
||||
$server = \App\Models\Server::query()->where('ip', '2.2.2.2')->first();
|
||||
|
||||
$this->assertDatabaseHas('servers', [
|
||||
'name' => 'test',
|
||||
'ip' => '2.2.2.2',
|
||||
'status' => ServerStatus::READY,
|
||||
]);
|
||||
|
||||
$this->assertDatabaseMissing('services', [
|
||||
'server_id' => $server->id,
|
||||
'type' => 'php',
|
||||
'version' => '8.2',
|
||||
'status' => ServiceStatus::READY,
|
||||
]);
|
||||
|
||||
$this->assertDatabaseMissing('services', [
|
||||
'server_id' => $server->id,
|
||||
'type' => 'webserver',
|
||||
'name' => 'nginx',
|
||||
'version' => 'latest',
|
||||
'status' => ServiceStatus::READY,
|
||||
]);
|
||||
|
||||
$this->assertDatabaseHas('services', [
|
||||
'server_id' => $server->id,
|
||||
'type' => 'database',
|
||||
'name' => 'mysql',
|
||||
'version' => '8.0',
|
||||
'status' => ServiceStatus::READY,
|
||||
]);
|
||||
|
||||
$this->assertDatabaseHas('services', [
|
||||
'server_id' => $server->id,
|
||||
'type' => 'firewall',
|
||||
'name' => 'ufw',
|
||||
'version' => 'latest',
|
||||
'status' => ServiceStatus::READY,
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_delete_server(): void
|
||||
{
|
||||
$this->actingAs($this->user);
|
||||
@ -89,4 +145,139 @@ public function test_delete_server(): void
|
||||
'id' => $this->server->id,
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_cannot_delete_on_provider(): void
|
||||
{
|
||||
Mail::fake();
|
||||
Http::fake([
|
||||
'*' => Http::response([], 401),
|
||||
]);
|
||||
|
||||
$this->actingAs($this->user);
|
||||
|
||||
$provider = \App\Models\ServerProvider::factory()->create([
|
||||
'user_id' => $this->user->id,
|
||||
'provider' => ServerProvider::HETZNER,
|
||||
'credentials' => [
|
||||
'token' => 'token',
|
||||
],
|
||||
]);
|
||||
|
||||
$this->server->update([
|
||||
'provider' => ServerProvider::HETZNER,
|
||||
'provider_id' => $provider->id,
|
||||
'provider_data' => [
|
||||
'hetzner_id' => 1,
|
||||
'ssh_key_id' => 1,
|
||||
],
|
||||
]);
|
||||
|
||||
$this->delete(route('servers.delete', $this->server))
|
||||
->assertSessionDoesntHaveErrors();
|
||||
|
||||
$this->assertDatabaseMissing('servers', [
|
||||
'id' => $this->server->id,
|
||||
]);
|
||||
|
||||
Mail::assertSent(NotificationMail::class);
|
||||
}
|
||||
|
||||
public function test_check_connection_is_ready(): void
|
||||
{
|
||||
SSH::fake();
|
||||
|
||||
$this->actingAs($this->user);
|
||||
|
||||
$this->server->update(['status' => ServerStatus::DISCONNECTED]);
|
||||
|
||||
$this->post(route('servers.settings.check-connection', $this->server))
|
||||
->assertSessionDoesntHaveErrors();
|
||||
|
||||
$this->assertDatabaseHas('servers', [
|
||||
'id' => $this->server->id,
|
||||
'status' => ServerStatus::READY,
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_connection_failed(): void
|
||||
{
|
||||
SSH::fake()->connectionWillFail();
|
||||
|
||||
$this->actingAs($this->user);
|
||||
|
||||
$this->server->update(['status' => ServerStatus::READY]);
|
||||
|
||||
$this->post(route('servers.settings.check-connection', $this->server))
|
||||
->assertSessionDoesntHaveErrors();
|
||||
|
||||
$this->assertDatabaseHas('servers', [
|
||||
'id' => $this->server->id,
|
||||
'status' => ServerStatus::DISCONNECTED,
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_reboot_server(): void
|
||||
{
|
||||
SSH::fake();
|
||||
|
||||
$this->actingAs($this->user);
|
||||
|
||||
$this->post(route('servers.settings.reboot', $this->server))
|
||||
->assertSessionDoesntHaveErrors();
|
||||
|
||||
$this->assertDatabaseHas('servers', [
|
||||
'id' => $this->server->id,
|
||||
'status' => ServerStatus::DISCONNECTED,
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_edit_server(): void
|
||||
{
|
||||
$this->actingAs($this->user);
|
||||
|
||||
$this->post(route('servers.settings.edit', $this->server), [
|
||||
'name' => 'new-name',
|
||||
])->assertSessionDoesntHaveErrors();
|
||||
|
||||
$this->assertDatabaseHas('servers', [
|
||||
'id' => $this->server->id,
|
||||
'name' => 'new-name',
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_edit_server_ip_address(): void
|
||||
{
|
||||
SSH::fake();
|
||||
|
||||
$this->actingAs($this->user);
|
||||
|
||||
$this->post(route('servers.settings.edit', $this->server), [
|
||||
'ip' => '2.2.2.2',
|
||||
])->assertSessionDoesntHaveErrors();
|
||||
|
||||
$this->assertDatabaseHas('servers', [
|
||||
'id' => $this->server->id,
|
||||
'ip' => '2.2.2.2',
|
||||
'status' => ServerStatus::READY,
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_edit_server_ip_address_and_disconnect(): void
|
||||
{
|
||||
SSH::fake()->connectionWillFail();
|
||||
|
||||
$this->actingAs($this->user);
|
||||
|
||||
$this->post(route('servers.settings.edit', $this->server), [
|
||||
'ip' => '2.2.2.2',
|
||||
'port' => 2222,
|
||||
])->assertSessionDoesntHaveErrors();
|
||||
|
||||
$this->assertDatabaseHas('servers', [
|
||||
'id' => $this->server->id,
|
||||
'ip' => '2.2.2.2',
|
||||
'port' => 2222,
|
||||
'status' => ServerStatus::DISCONNECTED,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
@ -160,4 +160,16 @@ public static function create_data(): array
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function test_see_logs(): void
|
||||
{
|
||||
$this->actingAs($this->user);
|
||||
|
||||
$this->get(route('servers.sites.logs', [
|
||||
'server' => $this->server,
|
||||
'site' => $this->site,
|
||||
]))
|
||||
->assertOk()
|
||||
->assertSee('Logs');
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,6 @@
|
||||
use App\Models\SourceControl;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use JsonException;
|
||||
use Tests\TestCase;
|
||||
|
||||
class SourceControlsTest extends TestCase
|
||||
@ -14,8 +13,6 @@ class SourceControlsTest extends TestCase
|
||||
|
||||
/**
|
||||
* @dataProvider data
|
||||
*
|
||||
* @throws JsonException
|
||||
*/
|
||||
public function test_connect_provider(string $provider, ?string $customUrl): void
|
||||
{
|
||||
@ -43,8 +40,6 @@ public function test_connect_provider(string $provider, ?string $customUrl): voi
|
||||
|
||||
/**
|
||||
* @dataProvider data
|
||||
*
|
||||
* @throws JsonException
|
||||
*/
|
||||
public function test_delete_provider(string $provider): void
|
||||
{
|
||||
@ -64,6 +59,33 @@ public function test_delete_provider(string $provider): void
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider data
|
||||
*/
|
||||
public function test_cannot_delete_provider(string $provider): void
|
||||
{
|
||||
$this->actingAs($this->user);
|
||||
|
||||
/** @var SourceControl $sourceControl */
|
||||
$sourceControl = SourceControl::factory()->create([
|
||||
'provider' => $provider,
|
||||
'profile' => 'test',
|
||||
]);
|
||||
|
||||
$this->site->update([
|
||||
'source_control_id' => $sourceControl->id,
|
||||
]);
|
||||
|
||||
$this->delete(route('source-controls.delete', $sourceControl->id))
|
||||
->assertSessionDoesntHaveErrors()
|
||||
->assertSessionHas('toast.type', 'error')
|
||||
->assertSessionHas('toast.message', 'This source control is being used by a site.');
|
||||
|
||||
$this->assertDatabaseHas('source_controls', [
|
||||
'id' => $sourceControl->id,
|
||||
]);
|
||||
}
|
||||
|
||||
public static function data(): array
|
||||
{
|
||||
return [
|
||||
|
@ -3,6 +3,8 @@
|
||||
namespace Tests\Feature;
|
||||
|
||||
use App\Enums\StorageProvider;
|
||||
use App\Models\Backup;
|
||||
use App\Models\Database;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Tests\TestCase;
|
||||
@ -57,4 +59,32 @@ public function test_delete_provider(): void
|
||||
'id' => $provider->id,
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_cannot_delete_provider(): void
|
||||
{
|
||||
$this->actingAs($this->user);
|
||||
|
||||
$database = Database::factory()->create([
|
||||
'server_id' => $this->server,
|
||||
]);
|
||||
|
||||
$provider = \App\Models\StorageProvider::factory()->create([
|
||||
'user_id' => $this->user->id,
|
||||
]);
|
||||
|
||||
Backup::factory()->create([
|
||||
'server_id' => $this->server->id,
|
||||
'database_id' => $database->id,
|
||||
'storage_id' => $provider->id,
|
||||
]);
|
||||
|
||||
$this->delete(route('storage-providers.delete', $provider->id))
|
||||
->assertSessionDoesntHaveErrors()
|
||||
->assertSessionHas('toast.type', 'error')
|
||||
->assertSessionHas('toast.message', 'This storage provider is being used by a backup.');
|
||||
|
||||
$this->assertDatabaseHas('storage_providers', [
|
||||
'id' => $provider->id,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
namespace Tests;
|
||||
|
||||
use App\Enums\Database;
|
||||
use App\Enums\NotificationChannel;
|
||||
use App\Enums\ServiceStatus;
|
||||
use App\Enums\Webserver;
|
||||
use App\Models\Server;
|
||||
@ -31,6 +32,14 @@ public function setUp(): void
|
||||
|
||||
$this->user = User::factory()->create();
|
||||
|
||||
\App\Models\NotificationChannel::factory()->create([
|
||||
'provider' => NotificationChannel::EMAIL,
|
||||
'connected' => true,
|
||||
'data' => [
|
||||
'email' => 'user@example.com',
|
||||
],
|
||||
]);
|
||||
|
||||
$this->setupServer();
|
||||
|
||||
$this->setupSite();
|
||||
@ -38,6 +47,15 @@ public function setUp(): void
|
||||
$this->setupKeys();
|
||||
}
|
||||
|
||||
public function tearDown(): void
|
||||
{
|
||||
parent::tearDown();
|
||||
|
||||
if (File::exists(storage_path('app/key-pairs-test'))) {
|
||||
File::deleteDirectory(storage_path('app/key-pairs-test'));
|
||||
}
|
||||
}
|
||||
|
||||
private function setupServer(): void
|
||||
{
|
||||
$this->server = Server::factory()->create([
|
||||
|
42
tests/Unit/Commands/CreateUserCommandTest.php
Normal file
42
tests/Unit/Commands/CreateUserCommandTest.php
Normal file
@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Unit\Commands;
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Tests\TestCase;
|
||||
|
||||
class CreateUserCommandTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
public function test_create_user(): void
|
||||
{
|
||||
$this->artisan('user:create', [
|
||||
'name' => 'John Doe',
|
||||
'email' => 'john@doe.com',
|
||||
'password' => 'password',
|
||||
])->expectsOutput('User created!');
|
||||
|
||||
$this->assertDatabaseHas('users', [
|
||||
'name' => 'John Doe',
|
||||
'email' => 'john@doe.com',
|
||||
]);
|
||||
|
||||
/** @var User $user */
|
||||
$user = User::query()->where('email', 'john@doe.com')->first();
|
||||
|
||||
$this->assertDatabaseHas('projects', [
|
||||
'user_id' => $user->id,
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_skip_existing_user(): void
|
||||
{
|
||||
$this->artisan('user:create', [
|
||||
'name' => 'John Doe',
|
||||
'email' => $this->user->email,
|
||||
'password' => 'password',
|
||||
])->expectsOutput('User already exists. Skipping...');
|
||||
}
|
||||
}
|
46
tests/Unit/Commands/RunBackupCommandTest.php
Normal file
46
tests/Unit/Commands/RunBackupCommandTest.php
Normal file
@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Unit\Commands;
|
||||
|
||||
use App\Facades\SSH;
|
||||
use App\Models\Backup;
|
||||
use App\Models\Database;
|
||||
use App\Models\StorageProvider;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Tests\TestCase;
|
||||
|
||||
class RunBackupCommandTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
public function test_run_without_any_backups(): void
|
||||
{
|
||||
$this->artisan('backups:run "* * * * *"')
|
||||
->expectsOutput('0 backups started');
|
||||
}
|
||||
|
||||
public function test_run_backups(): void
|
||||
{
|
||||
SSH::fake();
|
||||
|
||||
$database = Database::factory()->create([
|
||||
'server_id' => $this->server,
|
||||
]);
|
||||
|
||||
$storage = StorageProvider::factory()->create([
|
||||
'user_id' => $this->user->id,
|
||||
'provider' => \App\Enums\StorageProvider::DROPBOX,
|
||||
]);
|
||||
|
||||
$backup = Backup::factory()->create([
|
||||
'server_id' => $this->server->id,
|
||||
'database_id' => $database->id,
|
||||
'storage_id' => $storage->id,
|
||||
'interval' => '1 * * * *',
|
||||
'keep_backups' => 10,
|
||||
]);
|
||||
|
||||
$this->artisan('backups:run "1 * * * *"')
|
||||
->expectsOutput('1 backups started');
|
||||
}
|
||||
}
|
@ -2,6 +2,8 @@
|
||||
|
||||
namespace Tests\Unit\Models;
|
||||
|
||||
use App\Enums\ServerStatus;
|
||||
use App\Facades\SSH;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Tests\TestCase;
|
||||
|
||||
@ -17,4 +19,32 @@ public function test_should_have_default_service()
|
||||
$php->refresh();
|
||||
$this->assertTrue($php->is_default);
|
||||
}
|
||||
|
||||
public function test_check_connection_is_ready(): void
|
||||
{
|
||||
SSH::fake();
|
||||
|
||||
$this->server->update(['status' => ServerStatus::DISCONNECTED]);
|
||||
|
||||
$this->server->checkConnection();
|
||||
|
||||
$this->assertDatabaseHas('servers', [
|
||||
'id' => $this->server->id,
|
||||
'status' => ServerStatus::READY,
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_connection_failed(): void
|
||||
{
|
||||
SSH::fake()->connectionWillFail();
|
||||
|
||||
$this->server->update(['status' => ServerStatus::READY]);
|
||||
|
||||
$this->server->checkConnection();
|
||||
|
||||
$this->assertDatabaseHas('servers', [
|
||||
'id' => $this->server->id,
|
||||
'status' => ServerStatus::DISCONNECTED,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user