mirror of
https://github.com/vitodeploy/vito.git
synced 2025-07-02 14:36:17 +00:00
API Feature (#334)
This commit is contained in:
@ -9,7 +9,7 @@
|
||||
|
||||
class CreateCronJob
|
||||
{
|
||||
public function create(Server $server, array $input): void
|
||||
public function create(Server $server, array $input): CronJob
|
||||
{
|
||||
$cronJob = new CronJob([
|
||||
'server_id' => $server->id,
|
||||
@ -23,6 +23,8 @@ public function create(Server $server, array $input): void
|
||||
$server->cron()->update($cronJob->user, CronJob::crontab($server, $cronJob->user));
|
||||
$cronJob->status = CronjobStatus::READY;
|
||||
$cronJob->save();
|
||||
|
||||
return $cronJob;
|
||||
}
|
||||
|
||||
public static function rules(array $input): array
|
||||
|
@ -20,7 +20,7 @@ public function create(Server $server, array $input, array $links = []): Databas
|
||||
'server_id' => $server->id,
|
||||
'username' => $input['username'],
|
||||
'password' => $input['password'],
|
||||
'host' => isset($input['remote']) && $input['remote'] ? $input['host'] : 'localhost',
|
||||
'host' => (isset($input['remote']) && $input['remote']) || isset($input['host']) ? $input['host'] : 'localhost',
|
||||
'databases' => $links,
|
||||
]);
|
||||
/** @var Database $databaseHandler */
|
||||
|
@ -13,7 +13,7 @@ class LinkUser
|
||||
/**
|
||||
* @throws ValidationException
|
||||
*/
|
||||
public function link(DatabaseUser $databaseUser, array $input): void
|
||||
public function link(DatabaseUser $databaseUser, array $input): DatabaseUser
|
||||
{
|
||||
if (! isset($input['databases']) || ! is_array($input['databases'])) {
|
||||
$input['databases'] = [];
|
||||
@ -43,6 +43,10 @@ public function link(DatabaseUser $databaseUser, array $input): void
|
||||
);
|
||||
|
||||
$databaseUser->save();
|
||||
|
||||
$databaseUser->refresh();
|
||||
|
||||
return $databaseUser;
|
||||
}
|
||||
|
||||
public static function rules(Server $server, array $input): array
|
||||
|
@ -6,28 +6,28 @@
|
||||
use App\Enums\ServerProvider;
|
||||
use App\Enums\ServerStatus;
|
||||
use App\Facades\Notifier;
|
||||
use App\Models\Project;
|
||||
use App\Models\Server;
|
||||
use App\Models\User;
|
||||
use App\Notifications\ServerInstallationFailed;
|
||||
use App\Notifications\ServerInstallationSucceed;
|
||||
use App\ValidationRules\RestrictedIPAddressesRule;
|
||||
use Exception;
|
||||
use Illuminate\Database\Query\Builder;
|
||||
use Illuminate\Support\Facades\Bus;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
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
|
||||
public function create(User $creator, Project $project, array $input): Server
|
||||
{
|
||||
$server = new Server([
|
||||
'project_id' => $creator->currentProject->id,
|
||||
'project_id' => $project->id,
|
||||
'user_id' => $creator->id,
|
||||
'name' => $input['name'],
|
||||
'ssh_user' => config('core.server_providers_default_user')[$input['provider']][$input['os']],
|
||||
@ -76,7 +76,9 @@ public function create(User $creator, array $input): Server
|
||||
} catch (Exception $e) {
|
||||
$server->provider()->delete();
|
||||
DB::rollBack();
|
||||
throw $e;
|
||||
throw ValidationException::withMessages([
|
||||
'provider' => $e->getMessage(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -112,7 +114,7 @@ function () use ($server) {
|
||||
$bus->onConnection('ssh')->dispatch();
|
||||
}
|
||||
|
||||
public static function rules(array $input): array
|
||||
public static function rules(Project $project, array $input): array
|
||||
{
|
||||
$rules = [
|
||||
'provider' => [
|
||||
@ -132,15 +134,18 @@ public static function rules(array $input): array
|
||||
],
|
||||
'server_provider' => [
|
||||
Rule::when(function () use ($input) {
|
||||
return $input['provider'] != ServerProvider::CUSTOM;
|
||||
return isset($input['provider']) && $input['provider'] != ServerProvider::CUSTOM;
|
||||
}, [
|
||||
'required',
|
||||
'exists:server_providers,id,user_id,'.auth()->user()->id,
|
||||
Rule::exists('server_providers', 'id')->where(function (Builder $query) use ($project) {
|
||||
$query->where('project_id', $project->id)
|
||||
->orWhereNull('project_id');
|
||||
}),
|
||||
]),
|
||||
],
|
||||
'ip' => [
|
||||
Rule::when(function () use ($input) {
|
||||
return $input['provider'] == ServerProvider::CUSTOM;
|
||||
return isset($input['provider']) && $input['provider'] == ServerProvider::CUSTOM;
|
||||
}, [
|
||||
'required',
|
||||
new RestrictedIPAddressesRule,
|
||||
@ -148,7 +153,7 @@ public static function rules(array $input): array
|
||||
],
|
||||
'port' => [
|
||||
Rule::when(function () use ($input) {
|
||||
return $input['provider'] == ServerProvider::CUSTOM;
|
||||
return isset($input['provider']) && $input['provider'] == ServerProvider::CUSTOM;
|
||||
}, [
|
||||
'required',
|
||||
'numeric',
|
||||
@ -176,6 +181,7 @@ private static function providerRules(array $input): array
|
||||
{
|
||||
if (
|
||||
! isset($input['provider']) ||
|
||||
! isset($input['server_provider']) ||
|
||||
! in_array($input['provider'], config('core.server_providers')) ||
|
||||
$input['provider'] == ServerProvider::CUSTOM
|
||||
) {
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Actions\ServerProvider;
|
||||
|
||||
use App\Models\Project;
|
||||
use App\Models\ServerProvider;
|
||||
use App\Models\User;
|
||||
use App\ServerProviders\ServerProvider as ServerProviderContract;
|
||||
@ -14,7 +15,7 @@ class CreateServerProvider
|
||||
/**
|
||||
* @throws ValidationException
|
||||
*/
|
||||
public function create(User $user, array $input): ServerProvider
|
||||
public function create(User $user, Project $project, array $input): ServerProvider
|
||||
{
|
||||
$provider = static::getProvider($input['provider']);
|
||||
|
||||
@ -33,7 +34,7 @@ public function create(User $user, array $input): ServerProvider
|
||||
$serverProvider->profile = $input['name'];
|
||||
$serverProvider->provider = $input['provider'];
|
||||
$serverProvider->credentials = $provider->credentialData($input);
|
||||
$serverProvider->project_id = isset($input['global']) && $input['global'] ? null : $user->current_project_id;
|
||||
$serverProvider->project_id = isset($input['global']) && $input['global'] ? null : $project->id;
|
||||
$serverProvider->save();
|
||||
|
||||
return $serverProvider;
|
||||
@ -46,9 +47,9 @@ private static function getProvider($name): ServerProviderContract
|
||||
return new $providerClass;
|
||||
}
|
||||
|
||||
public static function rules(): array
|
||||
public static function rules(array $input): array
|
||||
{
|
||||
return [
|
||||
$rules = [
|
||||
'name' => [
|
||||
'required',
|
||||
],
|
||||
@ -58,10 +59,16 @@ public static function rules(): array
|
||||
Rule::notIn('custom'),
|
||||
],
|
||||
];
|
||||
|
||||
return array_merge($rules, static::providerRules($input));
|
||||
}
|
||||
|
||||
public static function providerRules(array $input): array
|
||||
private static function providerRules(array $input): array
|
||||
{
|
||||
if (! isset($input['provider'])) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return static::getProvider($input['provider'])->credentialValidationRules($input);
|
||||
}
|
||||
}
|
||||
|
@ -3,17 +3,16 @@
|
||||
namespace App\Actions\ServerProvider;
|
||||
|
||||
use App\Models\ServerProvider;
|
||||
use Exception;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
|
||||
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.');
|
||||
throw ValidationException::withMessages([
|
||||
'provider' => 'This server provider is being used by a server.',
|
||||
]);
|
||||
}
|
||||
|
||||
$serverProvider->delete();
|
||||
|
@ -2,17 +2,19 @@
|
||||
|
||||
namespace App\Actions\ServerProvider;
|
||||
|
||||
use App\Models\Project;
|
||||
use App\Models\ServerProvider;
|
||||
use App\Models\User;
|
||||
|
||||
class EditServerProvider
|
||||
{
|
||||
public function edit(ServerProvider $serverProvider, User $user, array $input): void
|
||||
public function edit(ServerProvider $serverProvider, Project $project, array $input): ServerProvider
|
||||
{
|
||||
$serverProvider->profile = $input['name'];
|
||||
$serverProvider->project_id = isset($input['global']) && $input['global'] ? null : $user->current_project_id;
|
||||
$serverProvider->project_id = isset($input['global']) && $input['global'] ? null : $project->id;
|
||||
|
||||
$serverProvider->save();
|
||||
|
||||
return $serverProvider;
|
||||
}
|
||||
|
||||
public static function rules(): array
|
||||
|
@ -8,6 +8,9 @@
|
||||
|
||||
class Uninstall
|
||||
{
|
||||
/*
|
||||
* @TODO: Implement the uninstaller for all service handlers
|
||||
*/
|
||||
public function uninstall(Service $service): void
|
||||
{
|
||||
Validator::make([
|
||||
|
@ -19,9 +19,6 @@
|
||||
|
||||
class CreateSite
|
||||
{
|
||||
/**
|
||||
* @throws SourceControlIsNotConnected
|
||||
*/
|
||||
public function create(Server $server, array $input): Site
|
||||
{
|
||||
DB::beginTransaction();
|
||||
@ -88,13 +85,12 @@ public function create(Server $server, array $input): Site
|
||||
return $site;
|
||||
} catch (Exception $e) {
|
||||
DB::rollBack();
|
||||
throw $e;
|
||||
throw ValidationException::withMessages([
|
||||
'type' => $e->getMessage(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws ValidationException
|
||||
*/
|
||||
public static function rules(Server $server, array $input): array
|
||||
{
|
||||
$rules = [
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Actions\SourceControl;
|
||||
|
||||
use App\Models\Project;
|
||||
use App\Models\SourceControl;
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Arr;
|
||||
@ -10,13 +11,13 @@
|
||||
|
||||
class ConnectSourceControl
|
||||
{
|
||||
public function connect(User $user, array $input): void
|
||||
public function connect(User $user, Project $project, array $input): SourceControl
|
||||
{
|
||||
$sourceControl = new SourceControl([
|
||||
'provider' => $input['provider'],
|
||||
'profile' => $input['name'],
|
||||
'url' => Arr::has($input, 'url') ? $input['url'] : null,
|
||||
'project_id' => isset($input['global']) && $input['global'] ? null : $user->current_project_id,
|
||||
'project_id' => isset($input['global']) && $input['global'] ? null : $project->id,
|
||||
]);
|
||||
|
||||
$sourceControl->provider_data = $sourceControl->provider()->createData($input);
|
||||
@ -29,6 +30,8 @@ public function connect(User $user, array $input): void
|
||||
}
|
||||
|
||||
$sourceControl->save();
|
||||
|
||||
return $sourceControl;
|
||||
}
|
||||
|
||||
public static function rules(array $input): array
|
||||
|
@ -3,13 +3,16 @@
|
||||
namespace App\Actions\SourceControl;
|
||||
|
||||
use App\Models\SourceControl;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
|
||||
class DeleteSourceControl
|
||||
{
|
||||
public function delete(SourceControl $sourceControl): void
|
||||
{
|
||||
if ($sourceControl->sites()->exists()) {
|
||||
throw new \Exception('This source control is being used by a site.');
|
||||
throw ValidationException::withMessages([
|
||||
'source_control' => __('This source control is being used by a site.'),
|
||||
]);
|
||||
}
|
||||
|
||||
$sourceControl->delete();
|
||||
|
@ -2,59 +2,47 @@
|
||||
|
||||
namespace App\Actions\SourceControl;
|
||||
|
||||
use App\Models\Project;
|
||||
use App\Models\SourceControl;
|
||||
use App\Models\User;
|
||||
use Illuminate\Validation\Rule;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
|
||||
class EditSourceControl
|
||||
{
|
||||
public function edit(SourceControl $sourceControl, User $user, array $input): void
|
||||
public function edit(SourceControl $sourceControl, Project $project, array $input): SourceControl
|
||||
{
|
||||
$sourceControl->profile = $input['name'];
|
||||
$sourceControl->url = $input['url'] ?? null;
|
||||
$sourceControl->project_id = isset($input['global']) && $input['global'] ? null : $user->current_project_id;
|
||||
$sourceControl->project_id = isset($input['global']) && $input['global'] ? null : $project->id;
|
||||
|
||||
$sourceControl->provider_data = $sourceControl->provider()->createData($input);
|
||||
$sourceControl->provider_data = $sourceControl->provider()->editData($input);
|
||||
|
||||
if (! $sourceControl->provider()->connect()) {
|
||||
throw ValidationException::withMessages([
|
||||
'token' => __('Cannot connect to :provider or invalid token!', ['provider' => $sourceControl->provider]
|
||||
),
|
||||
'token' => __('Cannot connect to :provider or invalid token!', ['provider' => $sourceControl->provider]),
|
||||
]);
|
||||
}
|
||||
|
||||
$sourceControl->save();
|
||||
|
||||
return $sourceControl;
|
||||
}
|
||||
|
||||
public static function rules(array $input): array
|
||||
public static function rules(SourceControl $sourceControl, array $input): array
|
||||
{
|
||||
$rules = [
|
||||
'name' => [
|
||||
'required',
|
||||
],
|
||||
'provider' => [
|
||||
'required',
|
||||
Rule::in(config('core.source_control_providers')),
|
||||
],
|
||||
];
|
||||
|
||||
return array_merge($rules, static::providerRules($input));
|
||||
return array_merge($rules, static::providerRules($sourceControl, $input));
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws ValidationException
|
||||
*/
|
||||
private static function providerRules(array $input): array
|
||||
private static function providerRules(SourceControl $sourceControl, array $input): array
|
||||
{
|
||||
if (! isset($input['provider'])) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$sourceControl = new SourceControl([
|
||||
'provider' => $input['provider'],
|
||||
]);
|
||||
|
||||
return $sourceControl->provider()->createRules($input);
|
||||
return $sourceControl->provider()->editRules($input);
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Actions\StorageProvider;
|
||||
|
||||
use App\Models\Project;
|
||||
use App\Models\StorageProvider;
|
||||
use App\Models\User;
|
||||
use Illuminate\Validation\Rule;
|
||||
@ -12,13 +13,13 @@ class CreateStorageProvider
|
||||
/**
|
||||
* @throws ValidationException
|
||||
*/
|
||||
public function create(User $user, array $input): void
|
||||
public function create(User $user, Project $project, array $input): StorageProvider
|
||||
{
|
||||
$storageProvider = new StorageProvider([
|
||||
'user_id' => $user->id,
|
||||
'provider' => $input['provider'],
|
||||
'profile' => $input['name'],
|
||||
'project_id' => isset($input['global']) && $input['global'] ? null : $user->current_project_id,
|
||||
'project_id' => isset($input['global']) && $input['global'] ? null : $project->id,
|
||||
]);
|
||||
|
||||
$storageProvider->credentials = $storageProvider->provider()->credentialData($input);
|
||||
@ -36,6 +37,8 @@ public function create(User $user, array $input): void
|
||||
}
|
||||
|
||||
$storageProvider->save();
|
||||
|
||||
return $storageProvider;
|
||||
}
|
||||
|
||||
public static function rules(array $input): array
|
||||
|
@ -3,17 +3,16 @@
|
||||
namespace App\Actions\StorageProvider;
|
||||
|
||||
use App\Models\StorageProvider;
|
||||
use Exception;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
|
||||
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.');
|
||||
throw ValidationException::withMessages([
|
||||
'provider' => __('This storage provider is being used by a backup.'),
|
||||
]);
|
||||
}
|
||||
|
||||
$storageProvider->delete();
|
||||
|
@ -2,18 +2,20 @@
|
||||
|
||||
namespace App\Actions\StorageProvider;
|
||||
|
||||
use App\Models\Project;
|
||||
use App\Models\StorageProvider;
|
||||
use App\Models\User;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
|
||||
class EditStorageProvider
|
||||
{
|
||||
public function edit(StorageProvider $storageProvider, User $user, array $input): void
|
||||
public function edit(StorageProvider $storageProvider, Project $project, array $input): StorageProvider
|
||||
{
|
||||
$storageProvider->profile = $input['name'];
|
||||
$storageProvider->project_id = isset($input['global']) && $input['global'] ? null : $user->current_project_id;
|
||||
$storageProvider->project_id = isset($input['global']) && $input['global'] ? null : $project->id;
|
||||
|
||||
$storageProvider->save();
|
||||
|
||||
return $storageProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,61 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Actions\Tag;
|
||||
|
||||
use App\Models\Server;
|
||||
use App\Models\Site;
|
||||
use App\Models\Tag;
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
class AttachTag
|
||||
{
|
||||
public function attach(User $user, array $input): Tag
|
||||
{
|
||||
$this->validate($input);
|
||||
|
||||
/** @var Server|Site $taggable */
|
||||
$taggable = $input['taggable_type']::findOrFail($input['taggable_id']);
|
||||
|
||||
$tag = Tag::query()->where('name', $input['name'])->first();
|
||||
if ($tag) {
|
||||
if (! $taggable->tags->contains($tag->id)) {
|
||||
$taggable->tags()->attach($tag->id);
|
||||
}
|
||||
|
||||
return $tag;
|
||||
}
|
||||
|
||||
$tag = new Tag([
|
||||
'project_id' => $user->currentProject->id,
|
||||
'name' => $input['name'],
|
||||
'color' => config('core.tag_colors')[array_rand(config('core.tag_colors'))],
|
||||
]);
|
||||
$tag->save();
|
||||
|
||||
$taggable->tags()->attach($tag->id);
|
||||
|
||||
return $tag;
|
||||
}
|
||||
|
||||
private function validate(array $input): void
|
||||
{
|
||||
Validator::make($input, [
|
||||
'name' => [
|
||||
'required',
|
||||
],
|
||||
'taggable_id' => [
|
||||
'required',
|
||||
'integer',
|
||||
],
|
||||
'taggable_type' => [
|
||||
'required',
|
||||
Rule::in(config('core.taggable_types')),
|
||||
],
|
||||
])->validate();
|
||||
}
|
||||
}
|
@ -1,39 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Actions\Tag;
|
||||
|
||||
use App\Models\Server;
|
||||
use App\Models\Site;
|
||||
use App\Models\Tag;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
class DetachTag
|
||||
{
|
||||
public function detach(Tag $tag, array $input): void
|
||||
{
|
||||
$this->validate($input);
|
||||
|
||||
/** @var Server|Site $taggable */
|
||||
$taggable = $input['taggable_type']::findOrFail($input['taggable_id']);
|
||||
|
||||
$taggable->tags()->detach($tag->id);
|
||||
}
|
||||
|
||||
private function validate(array $input): void
|
||||
{
|
||||
Validator::make($input, [
|
||||
'taggable_id' => [
|
||||
'required',
|
||||
'integer',
|
||||
],
|
||||
'taggable_type' => [
|
||||
'required',
|
||||
Rule::in(config('core.taggable_types')),
|
||||
],
|
||||
])->validate();
|
||||
}
|
||||
}
|
@ -21,13 +21,15 @@ public function handle(): void
|
||||
return;
|
||||
}
|
||||
|
||||
User::query()->create([
|
||||
$user = User::query()->create([
|
||||
'name' => $this->argument('name'),
|
||||
'email' => $this->argument('email'),
|
||||
'password' => bcrypt($this->argument('password')),
|
||||
'role' => $this->option('role'),
|
||||
]);
|
||||
|
||||
$user->createDefaultProject();
|
||||
|
||||
$this->info('User created!');
|
||||
}
|
||||
}
|
||||
|
@ -2,8 +2,12 @@
|
||||
|
||||
namespace App\Enums;
|
||||
|
||||
use App\Traits\Enum;
|
||||
|
||||
final class Database
|
||||
{
|
||||
use Enum;
|
||||
|
||||
const NONE = 'none';
|
||||
|
||||
const MYSQL57 = 'mysql57';
|
||||
|
30
app/Enums/PHP.php
Normal file
30
app/Enums/PHP.php
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace App\Enums;
|
||||
|
||||
use App\Traits\Enum;
|
||||
|
||||
final class PHP
|
||||
{
|
||||
use Enum;
|
||||
|
||||
const NONE = 'none';
|
||||
|
||||
const V70 = '7.0';
|
||||
|
||||
const V71 = '7.1';
|
||||
|
||||
const V72 = '7.2';
|
||||
|
||||
const V73 = '7.3';
|
||||
|
||||
const V74 = '7.4';
|
||||
|
||||
const V80 = '8.0';
|
||||
|
||||
const V81 = '8.1';
|
||||
|
||||
const V82 = '8.2';
|
||||
|
||||
const V83 = '8.3';
|
||||
}
|
@ -2,7 +2,9 @@
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Throwable;
|
||||
|
||||
class Handler extends ExceptionHandler
|
||||
@ -10,7 +12,7 @@ class Handler extends ExceptionHandler
|
||||
/**
|
||||
* A list of exception types with their corresponding custom log levels.
|
||||
*
|
||||
* @var array<class-string<\Throwable>, \Psr\Log\LogLevel::*>
|
||||
* @var array<class-string<Throwable>, \Psr\Log\LogLevel::*>
|
||||
*/
|
||||
protected $levels = [
|
||||
//
|
||||
@ -19,7 +21,7 @@ class Handler extends ExceptionHandler
|
||||
/**
|
||||
* A list of the exception types that are not reported.
|
||||
*
|
||||
* @var array<int, class-string<\Throwable>>
|
||||
* @var array<int, class-string<Throwable>>
|
||||
*/
|
||||
protected $dontReport = [
|
||||
//
|
||||
@ -45,4 +47,13 @@ public function register(): void
|
||||
//
|
||||
});
|
||||
}
|
||||
|
||||
public function render($request, Throwable $e): Response
|
||||
{
|
||||
if ($e instanceof ModelNotFoundException) {
|
||||
abort(404, class_basename($e->getModel()).' not found.');
|
||||
}
|
||||
|
||||
return parent::render($request, $e);
|
||||
}
|
||||
}
|
||||
|
@ -7,9 +7,11 @@
|
||||
use App\Models\Service;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Spatie\RouteAttributes\Attributes\Post;
|
||||
|
||||
class AgentController extends Controller
|
||||
{
|
||||
#[Post('api/servers/{server}/agent/{id}', name: 'api.servers.agent')]
|
||||
public function __invoke(Request $request, Server $server, int $id): JsonResponse
|
||||
{
|
||||
$validated = $this->validate($request, [
|
||||
|
97
app/Http/Controllers/API/CronJobController.php
Normal file
97
app/Http/Controllers/API/CronJobController.php
Normal file
@ -0,0 +1,97 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\API;
|
||||
|
||||
use App\Actions\CronJob\CreateCronJob;
|
||||
use App\Actions\CronJob\DeleteCronJob;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Resources\CronJobResource;
|
||||
use App\Models\CronJob;
|
||||
use App\Models\Project;
|
||||
use App\Models\Server;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\ResourceCollection;
|
||||
use Knuckles\Scribe\Attributes\BodyParam;
|
||||
use Knuckles\Scribe\Attributes\Endpoint;
|
||||
use Knuckles\Scribe\Attributes\Group;
|
||||
use Knuckles\Scribe\Attributes\Response;
|
||||
use Knuckles\Scribe\Attributes\ResponseFromApiResource;
|
||||
use Spatie\RouteAttributes\Attributes\Delete;
|
||||
use Spatie\RouteAttributes\Attributes\Get;
|
||||
use Spatie\RouteAttributes\Attributes\Middleware;
|
||||
use Spatie\RouteAttributes\Attributes\Post;
|
||||
use Spatie\RouteAttributes\Attributes\Prefix;
|
||||
|
||||
#[Prefix('api/projects/{project}/servers/{server}/cron-jobs')]
|
||||
#[Middleware(['auth:sanctum', 'can-see-project'])]
|
||||
#[Group(name: 'cron-jobs')]
|
||||
class CronJobController extends Controller
|
||||
{
|
||||
#[Get('/', name: 'api.projects.servers.cron-jobs', middleware: 'ability:read')]
|
||||
#[Endpoint(title: 'list', description: 'Get all cron jobs.')]
|
||||
#[ResponseFromApiResource(CronJobResource::class, CronJob::class, collection: true, paginate: 25)]
|
||||
public function index(Project $project, Server $server): ResourceCollection
|
||||
{
|
||||
$this->authorize('viewAny', [CronJob::class, $server]);
|
||||
|
||||
$this->validateRoute($project, $server);
|
||||
|
||||
return CronJobResource::collection($server->cronJobs()->simplePaginate(25));
|
||||
}
|
||||
|
||||
#[Post('/', name: 'api.projects.servers.cron-jobs.create', middleware: 'ability:write')]
|
||||
#[Endpoint(title: 'create', description: 'Create a new cron job.')]
|
||||
#[BodyParam(name: 'command', required: true)]
|
||||
#[BodyParam(name: 'user', required: true, enum: ['root', 'vito'])]
|
||||
#[BodyParam(name: 'frequency', description: 'Frequency of the cron job.', required: true, example: '* * * * *')]
|
||||
#[ResponseFromApiResource(CronJobResource::class, CronJob::class)]
|
||||
public function create(Request $request, Project $project, Server $server): CronJobResource
|
||||
{
|
||||
$this->authorize('create', [CronJob::class, $server]);
|
||||
|
||||
$this->validateRoute($project, $server);
|
||||
|
||||
$this->validate($request, CreateCronJob::rules($request->all()));
|
||||
|
||||
$cronJob = app(CreateCronJob::class)->create($server, $request->all());
|
||||
|
||||
return new CronJobResource($cronJob);
|
||||
}
|
||||
|
||||
#[Get('{cronJob}', name: 'api.projects.servers.cron-jobs.show', middleware: 'ability:read')]
|
||||
#[Endpoint(title: 'show', description: 'Get a cron job by ID.')]
|
||||
#[ResponseFromApiResource(CronJobResource::class, CronJob::class)]
|
||||
public function show(Project $project, Server $server, CronJob $cronJob): CronJobResource
|
||||
{
|
||||
$this->authorize('view', [$cronJob, $server]);
|
||||
|
||||
$this->validateRoute($project, $server, $cronJob);
|
||||
|
||||
return new CronJobResource($cronJob);
|
||||
}
|
||||
|
||||
#[Delete('{cronJob}', name: 'api.projects.servers.cron-jobs.delete', middleware: 'ability:write')]
|
||||
#[Endpoint(title: 'delete', description: 'Delete cron job.')]
|
||||
#[Response(status: 204)]
|
||||
public function delete(Project $project, Server $server, CronJob $cronJob)
|
||||
{
|
||||
$this->authorize('delete', [$cronJob, $server]);
|
||||
|
||||
$this->validateRoute($project, $server, $cronJob);
|
||||
|
||||
app(DeleteCronJob::class)->delete($server, $cronJob);
|
||||
|
||||
return response()->noContent();
|
||||
}
|
||||
|
||||
private function validateRoute(Project $project, Server $server, ?CronJob $cronJob = null): void
|
||||
{
|
||||
if ($project->id !== $server->project_id) {
|
||||
abort(404, 'Server not found in project');
|
||||
}
|
||||
|
||||
if ($cronJob && $cronJob->server_id !== $server->id) {
|
||||
abort(404, 'Firewall rule not found in server');
|
||||
}
|
||||
}
|
||||
}
|
94
app/Http/Controllers/API/DatabaseController.php
Normal file
94
app/Http/Controllers/API/DatabaseController.php
Normal file
@ -0,0 +1,94 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\API;
|
||||
|
||||
use App\Actions\Database\CreateDatabase;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Resources\DatabaseResource;
|
||||
use App\Models\Database;
|
||||
use App\Models\Project;
|
||||
use App\Models\Server;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\ResourceCollection;
|
||||
use Knuckles\Scribe\Attributes\BodyParam;
|
||||
use Knuckles\Scribe\Attributes\Endpoint;
|
||||
use Knuckles\Scribe\Attributes\Group;
|
||||
use Knuckles\Scribe\Attributes\Response;
|
||||
use Knuckles\Scribe\Attributes\ResponseFromApiResource;
|
||||
use Spatie\RouteAttributes\Attributes\Delete;
|
||||
use Spatie\RouteAttributes\Attributes\Get;
|
||||
use Spatie\RouteAttributes\Attributes\Middleware;
|
||||
use Spatie\RouteAttributes\Attributes\Post;
|
||||
use Spatie\RouteAttributes\Attributes\Prefix;
|
||||
|
||||
#[Prefix('api/projects/{project}/servers/{server}/databases')]
|
||||
#[Middleware(['auth:sanctum', 'can-see-project'])]
|
||||
#[Group(name: 'databases')]
|
||||
class DatabaseController extends Controller
|
||||
{
|
||||
#[Get('/', name: 'api.projects.servers.databases', middleware: 'ability:read')]
|
||||
#[Endpoint(title: 'list', description: 'Get all databases.')]
|
||||
#[ResponseFromApiResource(DatabaseResource::class, Database::class, collection: true, paginate: 25)]
|
||||
public function index(Project $project, Server $server): ResourceCollection
|
||||
{
|
||||
$this->authorize('viewAny', [Database::class, $server]);
|
||||
|
||||
$this->validateRoute($project, $server);
|
||||
|
||||
return DatabaseResource::collection($server->databases()->simplePaginate(25));
|
||||
}
|
||||
|
||||
#[Post('/', name: 'api.projects.servers.databases.create', middleware: 'ability:write')]
|
||||
#[Endpoint(title: 'create', description: 'Create a new database.')]
|
||||
#[BodyParam(name: 'name', required: true)]
|
||||
#[ResponseFromApiResource(DatabaseResource::class, Database::class)]
|
||||
public function create(Request $request, Project $project, Server $server): DatabaseResource
|
||||
{
|
||||
$this->authorize('create', [Database::class, $server]);
|
||||
|
||||
$this->validateRoute($project, $server);
|
||||
|
||||
$this->validate($request, CreateDatabase::rules($server, $request->input()));
|
||||
|
||||
$database = app(CreateDatabase::class)->create($server, $request->all());
|
||||
|
||||
return new DatabaseResource($database);
|
||||
}
|
||||
|
||||
#[Get('{database}', name: 'api.projects.servers.databases.show', middleware: 'ability:read')]
|
||||
#[Endpoint(title: 'show', description: 'Get a database by ID.')]
|
||||
#[ResponseFromApiResource(DatabaseResource::class, Database::class)]
|
||||
public function show(Project $project, Server $server, Database $database): DatabaseResource
|
||||
{
|
||||
$this->authorize('view', [$database, $server]);
|
||||
|
||||
$this->validateRoute($project, $server, $database);
|
||||
|
||||
return new DatabaseResource($database);
|
||||
}
|
||||
|
||||
#[Delete('{database}', name: 'api.projects.servers.databases.delete', middleware: 'ability:write')]
|
||||
#[Endpoint(title: 'delete', description: 'Delete database.')]
|
||||
#[Response(status: 204)]
|
||||
public function delete(Project $project, Server $server, Database $database)
|
||||
{
|
||||
$this->authorize('delete', [$database, $server]);
|
||||
|
||||
$this->validateRoute($project, $server, $database);
|
||||
|
||||
$database->delete();
|
||||
|
||||
return response()->noContent();
|
||||
}
|
||||
|
||||
private function validateRoute(Project $project, Server $server, ?Database $database = null): void
|
||||
{
|
||||
if ($project->id !== $server->project_id) {
|
||||
abort(404, 'Server not found in project');
|
||||
}
|
||||
|
||||
if ($database && $database->server_id !== $server->id) {
|
||||
abort(404, 'Database not found in server');
|
||||
}
|
||||
}
|
||||
}
|
114
app/Http/Controllers/API/DatabaseUserController.php
Normal file
114
app/Http/Controllers/API/DatabaseUserController.php
Normal file
@ -0,0 +1,114 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\API;
|
||||
|
||||
use App\Actions\Database\CreateDatabaseUser;
|
||||
use App\Actions\Database\LinkUser;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Resources\DatabaseUserResource;
|
||||
use App\Models\DatabaseUser;
|
||||
use App\Models\Project;
|
||||
use App\Models\Server;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\ResourceCollection;
|
||||
use Knuckles\Scribe\Attributes\BodyParam;
|
||||
use Knuckles\Scribe\Attributes\Endpoint;
|
||||
use Knuckles\Scribe\Attributes\Group;
|
||||
use Knuckles\Scribe\Attributes\Response;
|
||||
use Knuckles\Scribe\Attributes\ResponseFromApiResource;
|
||||
use Spatie\RouteAttributes\Attributes\Delete;
|
||||
use Spatie\RouteAttributes\Attributes\Get;
|
||||
use Spatie\RouteAttributes\Attributes\Middleware;
|
||||
use Spatie\RouteAttributes\Attributes\Post;
|
||||
use Spatie\RouteAttributes\Attributes\Prefix;
|
||||
|
||||
#[Prefix('api/projects/{project}/servers/{server}/database-users')]
|
||||
#[Middleware(['auth:sanctum', 'can-see-project'])]
|
||||
#[Group(name: 'database-users')]
|
||||
class DatabaseUserController extends Controller
|
||||
{
|
||||
#[Get('/', name: 'api.projects.servers.database-users', middleware: 'ability:read')]
|
||||
#[Endpoint(title: 'list', description: 'Get all database users.')]
|
||||
#[ResponseFromApiResource(DatabaseUserResource::class, DatabaseUser::class, collection: true, paginate: 25)]
|
||||
public function index(Project $project, Server $server): ResourceCollection
|
||||
{
|
||||
$this->authorize('viewAny', [DatabaseUser::class, $server]);
|
||||
|
||||
$this->validateRoute($project, $server);
|
||||
|
||||
return DatabaseUserResource::collection($server->databaseUsers()->simplePaginate(25));
|
||||
}
|
||||
|
||||
#[Post('/', name: 'api.projects.servers.database-users.create', middleware: 'ability:write')]
|
||||
#[Endpoint(title: 'create', description: 'Create a new database user.')]
|
||||
#[BodyParam(name: 'username', required: true)]
|
||||
#[BodyParam(name: 'password', required: true)]
|
||||
#[BodyParam(name: 'host', description: 'Host, if it is a remote user.', example: '%')]
|
||||
#[ResponseFromApiResource(DatabaseUserResource::class, DatabaseUser::class)]
|
||||
public function create(Request $request, Project $project, Server $server): DatabaseUserResource
|
||||
{
|
||||
$this->authorize('create', [DatabaseUser::class, $server]);
|
||||
|
||||
$this->validateRoute($project, $server);
|
||||
|
||||
$this->validate($request, CreateDatabaseUser::rules($server, $request->input()));
|
||||
|
||||
$databaseUser = app(CreateDatabaseUser::class)->create($server, $request->all());
|
||||
|
||||
return new DatabaseUserResource($databaseUser);
|
||||
}
|
||||
|
||||
#[Get('{databaseUser}', name: 'api.projects.servers.database-users.show', middleware: 'ability:read')]
|
||||
#[Endpoint(title: 'show', description: 'Get a database user by ID.')]
|
||||
#[ResponseFromApiResource(DatabaseUserResource::class, DatabaseUser::class)]
|
||||
public function show(Project $project, Server $server, DatabaseUser $databaseUser): DatabaseUserResource
|
||||
{
|
||||
$this->authorize('view', [$databaseUser, $server]);
|
||||
|
||||
$this->validateRoute($project, $server, $databaseUser);
|
||||
|
||||
return new DatabaseUserResource($databaseUser);
|
||||
}
|
||||
|
||||
#[Post('{databaseUser}/link', name: 'api.projects.servers.database-users.link', middleware: 'ability:write')]
|
||||
#[Endpoint(title: 'link', description: 'Link to databases')]
|
||||
#[BodyParam(name: 'databases', description: 'Array of database names to link to the user.', required: true)]
|
||||
#[ResponseFromApiResource(DatabaseUserResource::class, DatabaseUser::class)]
|
||||
public function link(Request $request, Project $project, Server $server, DatabaseUser $databaseUser): DatabaseUserResource
|
||||
{
|
||||
$this->authorize('update', [$databaseUser, $server]);
|
||||
|
||||
$this->validateRoute($project, $server, $databaseUser);
|
||||
|
||||
$this->validate($request, LinkUser::rules($server, $request->all()));
|
||||
|
||||
$databaseUser = app(LinkUser::class)->link($databaseUser, $request->all());
|
||||
|
||||
return new DatabaseUserResource($databaseUser);
|
||||
}
|
||||
|
||||
#[Delete('{databaseUser}', name: 'api.projects.servers.database-users.delete', middleware: 'ability:write')]
|
||||
#[Endpoint(title: 'delete', description: 'Delete database user.')]
|
||||
#[Response(status: 204)]
|
||||
public function delete(Project $project, Server $server, DatabaseUser $databaseUser)
|
||||
{
|
||||
$this->authorize('delete', [$databaseUser, $server]);
|
||||
|
||||
$this->validateRoute($project, $server, $databaseUser);
|
||||
|
||||
$databaseUser->delete();
|
||||
|
||||
return response()->noContent();
|
||||
}
|
||||
|
||||
private function validateRoute(Project $project, Server $server, ?DatabaseUser $databaseUser = null): void
|
||||
{
|
||||
if ($project->id !== $server->project_id) {
|
||||
abort(404, 'Server not found in project');
|
||||
}
|
||||
|
||||
if ($databaseUser && $databaseUser->server_id !== $server->id) {
|
||||
abort(404, 'Database user not found in server');
|
||||
}
|
||||
}
|
||||
}
|
99
app/Http/Controllers/API/FirewallRuleController.php
Normal file
99
app/Http/Controllers/API/FirewallRuleController.php
Normal file
@ -0,0 +1,99 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\API;
|
||||
|
||||
use App\Actions\FirewallRule\CreateRule;
|
||||
use App\Actions\FirewallRule\DeleteRule;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Resources\FirewallRuleResource;
|
||||
use App\Models\FirewallRule;
|
||||
use App\Models\Project;
|
||||
use App\Models\Server;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\ResourceCollection;
|
||||
use Knuckles\Scribe\Attributes\BodyParam;
|
||||
use Knuckles\Scribe\Attributes\Endpoint;
|
||||
use Knuckles\Scribe\Attributes\Group;
|
||||
use Knuckles\Scribe\Attributes\Response;
|
||||
use Knuckles\Scribe\Attributes\ResponseFromApiResource;
|
||||
use Spatie\RouteAttributes\Attributes\Delete;
|
||||
use Spatie\RouteAttributes\Attributes\Get;
|
||||
use Spatie\RouteAttributes\Attributes\Middleware;
|
||||
use Spatie\RouteAttributes\Attributes\Post;
|
||||
use Spatie\RouteAttributes\Attributes\Prefix;
|
||||
|
||||
#[Prefix('api/projects/{project}/servers/{server}/firewall-rules')]
|
||||
#[Middleware(['auth:sanctum', 'can-see-project'])]
|
||||
#[Group(name: 'firewall-rules')]
|
||||
class FirewallRuleController extends Controller
|
||||
{
|
||||
#[Get('/', name: 'api.projects.servers.firewall-rules', middleware: 'ability:read')]
|
||||
#[Endpoint(title: 'list', description: 'Get all firewall rules.')]
|
||||
#[ResponseFromApiResource(FirewallRuleResource::class, FirewallRule::class, collection: true, paginate: 25)]
|
||||
public function index(Project $project, Server $server): ResourceCollection
|
||||
{
|
||||
$this->authorize('viewAny', [FirewallRule::class, $server]);
|
||||
|
||||
$this->validateRoute($project, $server);
|
||||
|
||||
return FirewallRuleResource::collection($server->firewallRules()->simplePaginate(25));
|
||||
}
|
||||
|
||||
#[Post('/', name: 'api.projects.servers.firewall-rules.create', middleware: 'ability:write')]
|
||||
#[Endpoint(title: 'create', description: 'Create a new firewall rule.')]
|
||||
#[BodyParam(name: 'type', required: true, enum: ['allow', 'deny'])]
|
||||
#[BodyParam(name: 'protocol', required: true, enum: ['tcp', 'udp'])]
|
||||
#[BodyParam(name: 'port', required: true)]
|
||||
#[BodyParam(name: 'source', required: true)]
|
||||
#[BodyParam(name: 'mask', description: 'Mask for source IP.', example: '0')]
|
||||
#[ResponseFromApiResource(FirewallRuleResource::class, FirewallRule::class)]
|
||||
public function create(Request $request, Project $project, Server $server): FirewallRuleResource
|
||||
{
|
||||
$this->authorize('create', [FirewallRule::class, $server]);
|
||||
|
||||
$this->validateRoute($project, $server);
|
||||
|
||||
$this->validate($request, CreateRule::rules());
|
||||
|
||||
$firewallRule = app(CreateRule::class)->create($server, $request->all());
|
||||
|
||||
return new FirewallRuleResource($firewallRule);
|
||||
}
|
||||
|
||||
#[Get('{firewallRule}', name: 'api.projects.servers.firewall-rules.show', middleware: 'ability:read')]
|
||||
#[Endpoint(title: 'show', description: 'Get a firewall rule by ID.')]
|
||||
#[ResponseFromApiResource(FirewallRuleResource::class, FirewallRule::class)]
|
||||
public function show(Project $project, Server $server, FirewallRule $firewallRule): FirewallRuleResource
|
||||
{
|
||||
$this->authorize('view', [$firewallRule, $server]);
|
||||
|
||||
$this->validateRoute($project, $server, $firewallRule);
|
||||
|
||||
return new FirewallRuleResource($firewallRule);
|
||||
}
|
||||
|
||||
#[Delete('{firewallRule}', name: 'api.projects.servers.firewall-rules.delete', middleware: 'ability:write')]
|
||||
#[Endpoint(title: 'delete', description: 'Delete firewall rule.')]
|
||||
#[Response(status: 204)]
|
||||
public function delete(Project $project, Server $server, FirewallRule $firewallRule)
|
||||
{
|
||||
$this->authorize('delete', [$firewallRule, $server]);
|
||||
|
||||
$this->validateRoute($project, $server, $firewallRule);
|
||||
|
||||
app(DeleteRule::class)->delete($server, $firewallRule);
|
||||
|
||||
return response()->noContent();
|
||||
}
|
||||
|
||||
private function validateRoute(Project $project, Server $server, ?FirewallRule $firewallRule = null): void
|
||||
{
|
||||
if ($project->id !== $server->project_id) {
|
||||
abort(404, 'Server not found in project');
|
||||
}
|
||||
|
||||
if ($firewallRule && $firewallRule->server_id !== $server->id) {
|
||||
abort(404, 'Firewall rule not found in server');
|
||||
}
|
||||
}
|
||||
}
|
@ -11,10 +11,12 @@
|
||||
use App\Notifications\SourceControlDisconnected;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Spatie\RouteAttributes\Attributes\Any;
|
||||
use Throwable;
|
||||
|
||||
class GitHookController extends Controller
|
||||
{
|
||||
#[Any('api/git-hooks', name: 'api.git-hooks')]
|
||||
public function __invoke(Request $request)
|
||||
{
|
||||
if (! $request->input('secret')) {
|
||||
|
@ -3,9 +3,17 @@
|
||||
namespace App\Http\Controllers\API;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Knuckles\Scribe\Attributes\Endpoint;
|
||||
use Knuckles\Scribe\Attributes\Group;
|
||||
use Knuckles\Scribe\Attributes\Unauthenticated;
|
||||
use Spatie\RouteAttributes\Attributes\Get;
|
||||
|
||||
#[Group(name: 'general')]
|
||||
class HealthController extends Controller
|
||||
{
|
||||
#[Get('api/health', name: 'api.health')]
|
||||
#[Unauthenticated]
|
||||
#[Endpoint(title: 'health-check')]
|
||||
public function __invoke()
|
||||
{
|
||||
return response()->json([
|
||||
|
89
app/Http/Controllers/API/ProjectController.php
Normal file
89
app/Http/Controllers/API/ProjectController.php
Normal file
@ -0,0 +1,89 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\API;
|
||||
|
||||
use App\Actions\Projects\CreateProject;
|
||||
use App\Actions\Projects\DeleteProject;
|
||||
use App\Actions\Projects\UpdateProject;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Resources\ProjectResource;
|
||||
use App\Models\Project;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\ResourceCollection;
|
||||
use Illuminate\Http\Response;
|
||||
use Knuckles\Scribe\Attributes\BodyParam;
|
||||
use Knuckles\Scribe\Attributes\Endpoint;
|
||||
use Knuckles\Scribe\Attributes\Group;
|
||||
use Knuckles\Scribe\Attributes\ResponseFromApiResource;
|
||||
use Spatie\RouteAttributes\Attributes\Delete;
|
||||
use Spatie\RouteAttributes\Attributes\Get;
|
||||
use Spatie\RouteAttributes\Attributes\Middleware;
|
||||
use Spatie\RouteAttributes\Attributes\Post;
|
||||
use Spatie\RouteAttributes\Attributes\Put;
|
||||
|
||||
#[Middleware('auth:sanctum')]
|
||||
#[Group(name: 'projects')]
|
||||
class ProjectController extends Controller
|
||||
{
|
||||
#[Get('api/projects', name: 'api.projects.index', middleware: 'ability:read')]
|
||||
#[Endpoint(title: 'list', description: 'Get all projects.')]
|
||||
#[ResponseFromApiResource(ProjectResource::class, Project::class, collection: true, paginate: 25)]
|
||||
public function index(): ResourceCollection
|
||||
{
|
||||
$this->authorize('viewAny', Project::class);
|
||||
|
||||
return ProjectResource::collection(Project::all());
|
||||
}
|
||||
|
||||
#[Post('api/projects', name: 'api.projects.create', middleware: 'ability:write')]
|
||||
#[Endpoint(title: 'create', description: 'Create a new project.')]
|
||||
#[BodyParam(name: 'name', description: 'The name of the project.', required: true)]
|
||||
#[ResponseFromApiResource(ProjectResource::class, Project::class)]
|
||||
public function create(Request $request): ProjectResource
|
||||
{
|
||||
$this->authorize('create', Project::class);
|
||||
|
||||
$this->validate($request, CreateProject::rules());
|
||||
|
||||
$project = app(CreateProject::class)->create(auth()->user(), $request->all());
|
||||
|
||||
return new ProjectResource($project);
|
||||
}
|
||||
|
||||
#[Get('api/projects/{project}', name: 'api.projects.show', middleware: 'ability:read')]
|
||||
#[Endpoint(title: 'show', description: 'Get a project by ID.')]
|
||||
#[ResponseFromApiResource(ProjectResource::class, Project::class)]
|
||||
public function show(Project $project): ProjectResource
|
||||
{
|
||||
$this->authorize('view', $project);
|
||||
|
||||
return new ProjectResource($project);
|
||||
}
|
||||
|
||||
#[Put('api/projects/{project}', name: 'api.projects.update', middleware: 'ability:write')]
|
||||
#[Endpoint(title: 'update', description: 'Update project.')]
|
||||
#[BodyParam(name: 'name', description: 'The name of the project.', required: true)]
|
||||
#[ResponseFromApiResource(ProjectResource::class, Project::class)]
|
||||
public function update(Request $request, Project $project): ProjectResource
|
||||
{
|
||||
$this->authorize('update', $project);
|
||||
|
||||
$this->validate($request, UpdateProject::rules($project));
|
||||
|
||||
$project = app(UpdateProject::class)->update($project, $request->all());
|
||||
|
||||
return new ProjectResource($project);
|
||||
}
|
||||
|
||||
#[Delete('api/projects/{project}', name: 'api.projects.delete', middleware: 'ability:write')]
|
||||
#[Endpoint(title: 'delete', description: 'Delete project.')]
|
||||
#[\Knuckles\Scribe\Attributes\Response(status: 204)]
|
||||
public function delete(Project $project): Response
|
||||
{
|
||||
$this->authorize('delete', $project);
|
||||
|
||||
app(DeleteProject::class)->delete(auth()->user(), $project);
|
||||
|
||||
return response()->noContent();
|
||||
}
|
||||
}
|
131
app/Http/Controllers/API/ServerController.php
Normal file
131
app/Http/Controllers/API/ServerController.php
Normal file
@ -0,0 +1,131 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\API;
|
||||
|
||||
use App\Actions\Server\CreateServer;
|
||||
use App\Actions\Server\RebootServer;
|
||||
use App\Actions\Server\Update;
|
||||
use App\Enums\Database;
|
||||
use App\Enums\PHP;
|
||||
use App\Enums\ServerProvider;
|
||||
use App\Enums\ServerType;
|
||||
use App\Enums\Webserver;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Resources\ServerResource;
|
||||
use App\Models\Project;
|
||||
use App\Models\Server;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\ResourceCollection;
|
||||
use Knuckles\Scribe\Attributes\BodyParam;
|
||||
use Knuckles\Scribe\Attributes\Endpoint;
|
||||
use Knuckles\Scribe\Attributes\Group;
|
||||
use Knuckles\Scribe\Attributes\Response;
|
||||
use Knuckles\Scribe\Attributes\ResponseFromApiResource;
|
||||
use Spatie\RouteAttributes\Attributes\Delete;
|
||||
use Spatie\RouteAttributes\Attributes\Get;
|
||||
use Spatie\RouteAttributes\Attributes\Middleware;
|
||||
use Spatie\RouteAttributes\Attributes\Post;
|
||||
use Spatie\RouteAttributes\Attributes\Prefix;
|
||||
|
||||
#[Prefix('api/projects/{project}/servers')]
|
||||
#[Middleware(['auth:sanctum', 'can-see-project'])]
|
||||
#[Group(name: 'servers')]
|
||||
class ServerController extends Controller
|
||||
{
|
||||
#[Get('/', name: 'api.projects.servers', middleware: 'ability:read')]
|
||||
#[Endpoint(title: 'list', description: 'Get all servers in a project.')]
|
||||
#[ResponseFromApiResource(ServerResource::class, Server::class, collection: true, paginate: 25)]
|
||||
public function index(Project $project): ResourceCollection
|
||||
{
|
||||
$this->authorize('viewAny', [Server::class, $project]);
|
||||
|
||||
return ServerResource::collection($project->servers()->simplePaginate(25));
|
||||
}
|
||||
|
||||
#[Post('/', name: 'api.projects.servers.create', middleware: 'ability:write')]
|
||||
#[Endpoint(title: 'create', description: 'Create a new server.')]
|
||||
#[BodyParam(name: 'provider', description: 'The server provider type', required: true)]
|
||||
#[BodyParam(name: 'server_provider', description: 'If the provider is not custom, the ID of the server provider profile', enum: [ServerProvider::CUSTOM, ServerProvider::HETZNER, ServerProvider::DIGITALOCEAN, ServerProvider::LINODE, ServerProvider::VULTR])]
|
||||
#[BodyParam(name: 'region', description: 'Provider region if the provider is not custom')]
|
||||
#[BodyParam(name: 'plan', description: 'Provider plan if the provider is not custom')]
|
||||
#[BodyParam(name: 'ip', description: 'SSH IP address if the provider is custom')]
|
||||
#[BodyParam(name: 'port', description: 'SSH Port if the provider is custom')]
|
||||
#[BodyParam(name: 'name', description: 'The name of the server.', required: true)]
|
||||
#[BodyParam(name: 'os', description: 'The os of the server', required: true)]
|
||||
#[BodyParam(name: 'type', description: 'Server type', required: true, enum: [ServerType::REGULAR, ServerType::DATABASE])]
|
||||
#[BodyParam(name: 'webserver', description: 'Web server', required: true, enum: [Webserver::NONE, Webserver::NGINX])]
|
||||
#[BodyParam(name: 'database', description: 'Database', required: true, enum: [Database::NONE, Database::MYSQL57, Database::MYSQL80, Database::MARIADB103, Database::MARIADB104, Database::MARIADB103, Database::POSTGRESQL12, Database::POSTGRESQL13, Database::POSTGRESQL14, Database::POSTGRESQL15, Database::POSTGRESQL16], )]
|
||||
#[BodyParam(name: 'php', description: 'PHP version', required: true, enum: [PHP::V70, PHP::V71, PHP::V72, PHP::V73, PHP::V74, PHP::V80, PHP::V81, PHP::V82, PHP::V83])]
|
||||
#[ResponseFromApiResource(ServerResource::class, Server::class)]
|
||||
public function create(Request $request, Project $project): ServerResource
|
||||
{
|
||||
$this->authorize('create', [Server::class, $project]);
|
||||
|
||||
$this->validate($request, CreateServer::rules($project, $request->input()));
|
||||
|
||||
$server = app(CreateServer::class)->create(auth()->user(), $project, $request->all());
|
||||
|
||||
return new ServerResource($server);
|
||||
}
|
||||
|
||||
#[Get('{server}', name: 'api.projects.servers.show', middleware: 'ability:read')]
|
||||
#[Endpoint(title: 'show', description: 'Get a server by ID.')]
|
||||
#[ResponseFromApiResource(ServerResource::class, Server::class)]
|
||||
public function show(Project $project, Server $server): ServerResource
|
||||
{
|
||||
$this->authorize('view', [$server, $project]);
|
||||
|
||||
$this->validateRoute($project, $server);
|
||||
|
||||
return new ServerResource($server);
|
||||
}
|
||||
|
||||
#[Post('{server}/reboot', name: 'api.projects.servers.reboot', middleware: 'ability:write')]
|
||||
#[Endpoint(title: 'reboot', description: 'Reboot a server.')]
|
||||
#[Response(status: 204)]
|
||||
public function reboot(Project $project, Server $server)
|
||||
{
|
||||
$this->authorize('update', [$server, $project]);
|
||||
|
||||
$this->validateRoute($project, $server);
|
||||
|
||||
app(RebootServer::class)->reboot($server);
|
||||
|
||||
return response()->noContent();
|
||||
}
|
||||
|
||||
#[Post('{server}/upgrade', name: 'api.projects.servers.upgrade', middleware: 'ability:write')]
|
||||
#[Endpoint(title: 'upgrade', description: 'Upgrade server.')]
|
||||
#[Response(status: 204)]
|
||||
public function upgrade(Project $project, Server $server)
|
||||
{
|
||||
$this->authorize('update', [$server, $project]);
|
||||
|
||||
$this->validateRoute($project, $server);
|
||||
|
||||
app(Update::class)->update($server);
|
||||
|
||||
return response()->noContent();
|
||||
}
|
||||
|
||||
#[Delete('{server}', name: 'api.projects.servers.delete', middleware: 'ability:write')]
|
||||
#[Endpoint(title: 'delete', description: 'Delete server.')]
|
||||
#[Response(status: 204)]
|
||||
public function delete(Project $project, Server $server)
|
||||
{
|
||||
$this->authorize('delete', [$server, $project]);
|
||||
|
||||
$this->validateRoute($project, $server);
|
||||
|
||||
$server->delete();
|
||||
|
||||
return response()->noContent();
|
||||
}
|
||||
|
||||
private function validateRoute(Project $project, Server $server): void
|
||||
{
|
||||
if ($project->id !== $server->project_id) {
|
||||
abort(404, 'Server not found in project');
|
||||
}
|
||||
}
|
||||
}
|
116
app/Http/Controllers/API/ServerProviderController.php
Normal file
116
app/Http/Controllers/API/ServerProviderController.php
Normal file
@ -0,0 +1,116 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\API;
|
||||
|
||||
use App\Actions\ServerProvider\CreateServerProvider;
|
||||
use App\Actions\ServerProvider\DeleteServerProvider;
|
||||
use App\Actions\ServerProvider\EditServerProvider;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Resources\ServerProviderResource;
|
||||
use App\Models\Project;
|
||||
use App\Models\ServerProvider;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\ResourceCollection;
|
||||
use Knuckles\Scribe\Attributes\BodyParam;
|
||||
use Knuckles\Scribe\Attributes\Endpoint;
|
||||
use Knuckles\Scribe\Attributes\Group;
|
||||
use Knuckles\Scribe\Attributes\Response;
|
||||
use Knuckles\Scribe\Attributes\ResponseFromApiResource;
|
||||
use Spatie\RouteAttributes\Attributes\Delete;
|
||||
use Spatie\RouteAttributes\Attributes\Get;
|
||||
use Spatie\RouteAttributes\Attributes\Middleware;
|
||||
use Spatie\RouteAttributes\Attributes\Post;
|
||||
use Spatie\RouteAttributes\Attributes\Prefix;
|
||||
use Spatie\RouteAttributes\Attributes\Put;
|
||||
|
||||
#[Prefix('api/projects/{project}/server-providers')]
|
||||
#[Middleware(['auth:sanctum', 'can-see-project'])]
|
||||
#[Group(name: 'server-providers')]
|
||||
class ServerProviderController extends Controller
|
||||
{
|
||||
#[Get('/', name: 'api.projects.server-providers', middleware: 'ability:read')]
|
||||
#[Endpoint(title: 'list')]
|
||||
#[ResponseFromApiResource(ServerProviderResource::class, ServerProvider::class, collection: true, paginate: 25)]
|
||||
public function index(Project $project): ResourceCollection
|
||||
{
|
||||
$this->authorize('viewAny', ServerProvider::class);
|
||||
|
||||
$serverProviders = ServerProvider::getByProjectId($project->id)->simplePaginate(25);
|
||||
|
||||
return ServerProviderResource::collection($serverProviders);
|
||||
}
|
||||
|
||||
#[Post('/', name: 'api.projects.server-providers.create', middleware: 'ability:write')]
|
||||
#[Endpoint(title: 'create')]
|
||||
#[BodyParam(name: 'provider', description: 'The provider (aws, linode, hetzner, digitalocean, vultr, ...)', required: true)]
|
||||
#[BodyParam(name: 'name', description: 'The name of the server provider.', required: true)]
|
||||
#[BodyParam(name: 'token', description: 'The token if provider requires api token')]
|
||||
#[BodyParam(name: 'key', description: 'The key if provider requires key')]
|
||||
#[BodyParam(name: 'secret', description: 'The secret if provider requires key')]
|
||||
#[ResponseFromApiResource(ServerProviderResource::class, ServerProvider::class)]
|
||||
public function create(Request $request, Project $project): ServerProviderResource
|
||||
{
|
||||
$this->authorize('create', ServerProvider::class);
|
||||
|
||||
$this->validate($request, CreateServerProvider::rules($request->all()));
|
||||
|
||||
$serverProvider = app(CreateServerProvider::class)->create(auth()->user(), $project, $request->all());
|
||||
|
||||
return new ServerProviderResource($serverProvider);
|
||||
}
|
||||
|
||||
#[Get('{serverProvider}', name: 'api.projects.server-providers.show', middleware: 'ability:read')]
|
||||
#[Endpoint(title: 'show')]
|
||||
#[ResponseFromApiResource(ServerProviderResource::class, ServerProvider::class)]
|
||||
public function show(Project $project, ServerProvider $serverProvider)
|
||||
{
|
||||
$this->authorize('view', $serverProvider);
|
||||
|
||||
$this->validateRoute($project, $serverProvider);
|
||||
|
||||
return new ServerProviderResource($serverProvider);
|
||||
}
|
||||
|
||||
#[Put('{serverProvider}', name: 'api.projects.server-providers.update', middleware: 'ability:write')]
|
||||
#[Endpoint(title: 'update')]
|
||||
#[BodyParam(name: 'name', description: 'The name of the server provider.', required: true)]
|
||||
#[BodyParam(name: 'global', description: 'Accessible in all projects', enum: [true, false])]
|
||||
#[ResponseFromApiResource(ServerProviderResource::class, ServerProvider::class)]
|
||||
public function update(Request $request, Project $project, ServerProvider $serverProvider)
|
||||
{
|
||||
$this->authorize('update', $serverProvider);
|
||||
|
||||
$this->validateRoute($project, $serverProvider);
|
||||
|
||||
$this->validate($request, EditServerProvider::rules());
|
||||
|
||||
$serverProvider = app(EditServerProvider::class)->edit($serverProvider, $project, $request->all());
|
||||
|
||||
return new ServerProviderResource($serverProvider);
|
||||
}
|
||||
|
||||
#[Delete('{serverProvider}', name: 'api.projects.server-providers.delete', middleware: 'ability:write')]
|
||||
#[Endpoint(title: 'delete')]
|
||||
#[Response(status: 204)]
|
||||
public function delete(Project $project, ServerProvider $serverProvider)
|
||||
{
|
||||
$this->authorize('delete', $serverProvider);
|
||||
|
||||
$this->validateRoute($project, $serverProvider);
|
||||
|
||||
app(DeleteServerProvider::class)->delete($serverProvider);
|
||||
|
||||
return response()->noContent();
|
||||
}
|
||||
|
||||
private function validateRoute(Project $project, ServerProvider $serverProvider): void
|
||||
{
|
||||
if (! $serverProvider->project_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($project->id !== $serverProvider->project_id) {
|
||||
abort(404, 'Server provider not found in project');
|
||||
}
|
||||
}
|
||||
}
|
92
app/Http/Controllers/API/ServerSSHKeyController.php
Normal file
92
app/Http/Controllers/API/ServerSSHKeyController.php
Normal file
@ -0,0 +1,92 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\API;
|
||||
|
||||
use App\Actions\SshKey\CreateSshKey;
|
||||
use App\Actions\SshKey\DeleteKeyFromServer;
|
||||
use App\Actions\SshKey\DeployKeyToServer;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Resources\SshKeyResource;
|
||||
use App\Models\Project;
|
||||
use App\Models\Server;
|
||||
use App\Models\SshKey;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\ResourceCollection;
|
||||
use Knuckles\Scribe\Attributes\BodyParam;
|
||||
use Knuckles\Scribe\Attributes\Endpoint;
|
||||
use Knuckles\Scribe\Attributes\Group;
|
||||
use Knuckles\Scribe\Attributes\Response;
|
||||
use Knuckles\Scribe\Attributes\ResponseFromApiResource;
|
||||
use Spatie\RouteAttributes\Attributes\Delete;
|
||||
use Spatie\RouteAttributes\Attributes\Get;
|
||||
use Spatie\RouteAttributes\Attributes\Middleware;
|
||||
use Spatie\RouteAttributes\Attributes\Post;
|
||||
use Spatie\RouteAttributes\Attributes\Prefix;
|
||||
|
||||
#[Prefix('api/projects/{project}/servers/{server}/ssh-keys')]
|
||||
#[Middleware(['auth:sanctum', 'can-see-project'])]
|
||||
#[Group(name: 'ssh-keys')]
|
||||
class ServerSSHKeyController extends Controller
|
||||
{
|
||||
#[Get('/', name: 'api.projects.servers.ssh-keys', middleware: 'ability:read')]
|
||||
#[Endpoint(title: 'list', description: 'Get all ssh keys.')]
|
||||
#[ResponseFromApiResource(SshKeyResource::class, SshKey::class, collection: true, paginate: 25)]
|
||||
public function index(Project $project, Server $server): ResourceCollection
|
||||
{
|
||||
$this->authorize('viewAnyServer', [SshKey::class, $server]);
|
||||
|
||||
$this->validateRoute($project, $server);
|
||||
|
||||
return SshKeyResource::collection($server->sshKeys()->simplePaginate(25));
|
||||
}
|
||||
|
||||
#[Post('/', name: 'api.projects.servers.ssh-keys.create', middleware: 'ability:write')]
|
||||
#[Endpoint(title: 'create', description: 'Deploy ssh key to server.')]
|
||||
#[BodyParam(name: 'key_id', description: 'The ID of the key.')]
|
||||
#[BodyParam(name: 'name', description: 'Key name, required if key_id is not provided.')]
|
||||
#[BodyParam(name: 'public_key', description: 'Public Key, required if key_id is not provided.')]
|
||||
#[ResponseFromApiResource(SshKeyResource::class, SshKey::class)]
|
||||
public function create(Request $request, Project $project, Server $server): SshKeyResource
|
||||
{
|
||||
$this->authorize('create', [SshKey::class, $server]);
|
||||
|
||||
$this->validateRoute($project, $server);
|
||||
|
||||
$sshKey = null;
|
||||
if ($request->has('key_id')) {
|
||||
$this->validate($request, DeployKeyToServer::rules($request->user(), $server));
|
||||
|
||||
$sshKey = $request->user()->sshKeys()->findOrFail($request->key_id);
|
||||
}
|
||||
|
||||
if (! $sshKey) {
|
||||
$this->validate($request, CreateSshKey::rules());
|
||||
$sshKey = app(CreateSshKey::class)->create($request->user(), $request->all());
|
||||
}
|
||||
|
||||
app(DeployKeyToServer::class)->deploy($server, ['key_id' => $sshKey->id]);
|
||||
|
||||
return new SshKeyResource($sshKey);
|
||||
}
|
||||
|
||||
#[Delete('{sshKey}', name: 'api.projects.servers.ssh-keys.delete', middleware: 'ability:write')]
|
||||
#[Endpoint(title: 'delete', description: 'Delete ssh key from server.')]
|
||||
#[Response(status: 204)]
|
||||
public function delete(Project $project, Server $server, SshKey $sshKey)
|
||||
{
|
||||
$this->authorize('delete', [$sshKey, $server]);
|
||||
|
||||
$this->validateRoute($project, $server);
|
||||
|
||||
app(DeleteKeyFromServer::class)->delete($server, $sshKey);
|
||||
|
||||
return response()->noContent();
|
||||
}
|
||||
|
||||
private function validateRoute(Project $project, Server $server): void
|
||||
{
|
||||
if ($project->id !== $server->project_id) {
|
||||
abort(404, 'Server not found in project');
|
||||
}
|
||||
}
|
||||
}
|
146
app/Http/Controllers/API/ServiceController.php
Normal file
146
app/Http/Controllers/API/ServiceController.php
Normal file
@ -0,0 +1,146 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\API;
|
||||
|
||||
use App\Actions\Service\Manage;
|
||||
use App\Actions\Service\Uninstall;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Resources\ServiceResource;
|
||||
use App\Models\Project;
|
||||
use App\Models\Server;
|
||||
use App\Models\Service;
|
||||
use Illuminate\Http\Resources\Json\ResourceCollection;
|
||||
use Knuckles\Scribe\Attributes\Endpoint;
|
||||
use Knuckles\Scribe\Attributes\Group;
|
||||
use Knuckles\Scribe\Attributes\Response;
|
||||
use Knuckles\Scribe\Attributes\ResponseFromApiResource;
|
||||
use Spatie\RouteAttributes\Attributes\Delete;
|
||||
use Spatie\RouteAttributes\Attributes\Get;
|
||||
use Spatie\RouteAttributes\Attributes\Middleware;
|
||||
use Spatie\RouteAttributes\Attributes\Post;
|
||||
use Spatie\RouteAttributes\Attributes\Prefix;
|
||||
|
||||
#[Prefix('api/projects/{project}/servers/{server}/services')]
|
||||
#[Middleware(['auth:sanctum', 'can-see-project'])]
|
||||
#[Group(name: 'services')]
|
||||
class ServiceController extends Controller
|
||||
{
|
||||
#[Get('/', name: 'api.projects.servers.services', middleware: 'ability:read')]
|
||||
#[Endpoint(title: 'list', description: 'Get all services.')]
|
||||
#[ResponseFromApiResource(ServiceResource::class, Service::class, collection: true, paginate: 25)]
|
||||
public function index(Project $project, Server $server): ResourceCollection
|
||||
{
|
||||
$this->authorize('viewAny', [Service::class, $server]);
|
||||
|
||||
$this->validateRoute($project, $server);
|
||||
|
||||
return ServiceResource::collection($server->services()->simplePaginate(25));
|
||||
}
|
||||
|
||||
#[Get('{service}', name: 'api.projects.servers.services.show', middleware: 'ability:read')]
|
||||
#[Endpoint(title: 'show', description: 'Get a service by ID.')]
|
||||
#[ResponseFromApiResource(ServiceResource::class, Service::class)]
|
||||
public function show(Project $project, Server $server, Service $service): ServiceResource
|
||||
{
|
||||
$this->authorize('view', [$service, $server]);
|
||||
|
||||
$this->validateRoute($project, $server, $service);
|
||||
|
||||
return new ServiceResource($service);
|
||||
}
|
||||
|
||||
#[Post('{service}/start', name: 'api.projects.servers.services.start', middleware: 'ability:write')]
|
||||
#[Endpoint(title: 'start', description: 'Start service.')]
|
||||
#[Response(status: 204)]
|
||||
public function start(Project $project, Server $server, Service $service): \Illuminate\Http\Response
|
||||
{
|
||||
$this->authorize('update', [$service, $server]);
|
||||
|
||||
$this->validateRoute($project, $server, $service);
|
||||
|
||||
app(Manage::class)->start($service);
|
||||
|
||||
return response()->noContent();
|
||||
}
|
||||
|
||||
#[Post('{service}/stop', name: 'api.projects.servers.services.stop', middleware: 'ability:write')]
|
||||
#[Endpoint(title: 'stop', description: 'Stop service.')]
|
||||
#[Response(status: 204)]
|
||||
public function stop(Project $project, Server $server, Service $service): \Illuminate\Http\Response
|
||||
{
|
||||
$this->authorize('update', [$service, $server]);
|
||||
|
||||
$this->validateRoute($project, $server, $service);
|
||||
|
||||
app(Manage::class)->stop($service);
|
||||
|
||||
return response()->noContent();
|
||||
}
|
||||
|
||||
#[Post('{service}/restart', name: 'api.projects.servers.services.restart', middleware: 'ability:write')]
|
||||
#[Endpoint(title: 'restart', description: 'Restart service.')]
|
||||
#[Response(status: 204)]
|
||||
public function restart(Project $project, Server $server, Service $service): \Illuminate\Http\Response
|
||||
{
|
||||
$this->authorize('update', [$service, $server]);
|
||||
|
||||
$this->validateRoute($project, $server, $service);
|
||||
|
||||
app(Manage::class)->restart($service);
|
||||
|
||||
return response()->noContent();
|
||||
}
|
||||
|
||||
#[Post('{service}/enable', name: 'api.projects.servers.services.enable', middleware: 'ability:write')]
|
||||
#[Endpoint(title: 'enable', description: 'Enable service.')]
|
||||
#[Response(status: 204)]
|
||||
public function enable(Project $project, Server $server, Service $service): \Illuminate\Http\Response
|
||||
{
|
||||
$this->authorize('update', [$service, $server]);
|
||||
|
||||
$this->validateRoute($project, $server, $service);
|
||||
|
||||
app(Manage::class)->enable($service);
|
||||
|
||||
return response()->noContent();
|
||||
}
|
||||
|
||||
#[Post('{service}/disable', name: 'api.projects.servers.services.disable', middleware: 'ability:write')]
|
||||
#[Endpoint(title: 'disable', description: 'Disable service.')]
|
||||
#[Response(status: 204)]
|
||||
public function disable(Project $project, Server $server, Service $service): \Illuminate\Http\Response
|
||||
{
|
||||
$this->authorize('update', [$service, $server]);
|
||||
|
||||
$this->validateRoute($project, $server, $service);
|
||||
|
||||
app(Manage::class)->disable($service);
|
||||
|
||||
return response()->noContent();
|
||||
}
|
||||
|
||||
#[Delete('{service}', name: 'api.projects.servers.services.uninstall', middleware: 'ability:write')]
|
||||
#[Endpoint(title: 'delete', description: 'Delete service.')]
|
||||
#[Response(status: 204)]
|
||||
public function uninstall(Project $project, Server $server, Service $service): \Illuminate\Http\Response
|
||||
{
|
||||
$this->authorize('delete', [$service, $server]);
|
||||
|
||||
$this->validateRoute($project, $server, $service);
|
||||
|
||||
app(Uninstall::class)->uninstall($service);
|
||||
|
||||
return response()->noContent();
|
||||
}
|
||||
|
||||
private function validateRoute(Project $project, Server $server, ?Service $service = null): void
|
||||
{
|
||||
if ($project->id !== $server->project_id) {
|
||||
abort(404, 'Server not found in project');
|
||||
}
|
||||
|
||||
if ($service && $service->server_id !== $server->id) {
|
||||
abort(404, 'Service not found in server');
|
||||
}
|
||||
}
|
||||
}
|
105
app/Http/Controllers/API/SiteController.php
Normal file
105
app/Http/Controllers/API/SiteController.php
Normal file
@ -0,0 +1,105 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\API;
|
||||
|
||||
use App\Actions\Site\CreateSite;
|
||||
use App\Enums\SiteType;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Resources\ServerResource;
|
||||
use App\Http\Resources\SiteResource;
|
||||
use App\Models\Project;
|
||||
use App\Models\Server;
|
||||
use App\Models\Site;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\ResourceCollection;
|
||||
use Knuckles\Scribe\Attributes\BodyParam;
|
||||
use Knuckles\Scribe\Attributes\Endpoint;
|
||||
use Knuckles\Scribe\Attributes\Group;
|
||||
use Knuckles\Scribe\Attributes\Response;
|
||||
use Knuckles\Scribe\Attributes\ResponseFromApiResource;
|
||||
use Spatie\RouteAttributes\Attributes\Delete;
|
||||
use Spatie\RouteAttributes\Attributes\Get;
|
||||
use Spatie\RouteAttributes\Attributes\Middleware;
|
||||
use Spatie\RouteAttributes\Attributes\Post;
|
||||
use Spatie\RouteAttributes\Attributes\Prefix;
|
||||
|
||||
#[Prefix('api/projects/{project}/servers/{server}/sites')]
|
||||
#[Middleware(['auth:sanctum', 'can-see-project'])]
|
||||
#[Group(name: 'sites')]
|
||||
class SiteController extends Controller
|
||||
{
|
||||
#[Get('/', name: 'api.projects.servers.sites', middleware: 'ability:read')]
|
||||
#[Endpoint(title: 'list', description: 'Get all sites.')]
|
||||
#[ResponseFromApiResource(SiteResource::class, Site::class, collection: true, paginate: 25)]
|
||||
public function index(Project $project, Server $server): ResourceCollection
|
||||
{
|
||||
$this->authorize('viewAny', [Site::class, $server]);
|
||||
|
||||
$this->validateRoute($project, $server);
|
||||
|
||||
return SiteResource::collection($server->sites()->simplePaginate(25));
|
||||
}
|
||||
|
||||
#[Post('/', name: 'api.projects.servers.sites.create', middleware: 'ability:write')]
|
||||
#[Endpoint(title: 'create', description: 'Create a new site.')]
|
||||
#[BodyParam(name: 'type', required: true, enum: [SiteType::PHP, SiteType::PHP_BLANK, SiteType::PHPMYADMIN, SiteType::LARAVEL, SiteType::WORDPRESS])]
|
||||
#[BodyParam(name: 'domain', required: true)]
|
||||
#[BodyParam(name: 'aliases', type: 'array')]
|
||||
#[BodyParam(name: 'php_version', description: 'One of the installed PHP Versions', required: true, example: '7.4')]
|
||||
#[BodyParam(name: 'web_directory', description: 'Required for PHP and Laravel sites', example: 'public')]
|
||||
#[BodyParam(name: 'source_control', description: 'Source control ID, Required for Sites which support source control')]
|
||||
#[BodyParam(name: 'repository', description: 'Repository, Required for Sites which support source control', example: 'organization/repository')]
|
||||
#[BodyParam(name: 'branch', description: 'Branch, Required for Sites which support source control', example: 'main')]
|
||||
#[BodyParam(name: 'composer', type: 'boolean', description: 'Run composer if site supports composer', example: true)]
|
||||
#[BodyParam(name: 'version', description: 'Version, if the site type requires a version like PHPMyAdmin', example: '5.2.1')]
|
||||
#[ResponseFromApiResource(SiteResource::class, Site::class)]
|
||||
public function create(Request $request, Project $project, Server $server): SiteResource
|
||||
{
|
||||
$this->authorize('create', [Site::class, $server]);
|
||||
|
||||
$this->validateRoute($project, $server);
|
||||
|
||||
$this->validate($request, CreateSite::rules($server, $request->input()));
|
||||
|
||||
$site = app(CreateSite::class)->create($server, $request->all());
|
||||
|
||||
return new SiteResource($site);
|
||||
}
|
||||
|
||||
#[Get('{site}', name: 'api.projects.servers.sites.show', middleware: 'ability:read')]
|
||||
#[Endpoint(title: 'show', description: 'Get a site by ID.')]
|
||||
#[ResponseFromApiResource(SiteResource::class, Site::class)]
|
||||
public function show(Project $project, Server $server, Site $site): ServerResource
|
||||
{
|
||||
$this->authorize('view', [$site, $server]);
|
||||
|
||||
$this->validateRoute($project, $server, $site);
|
||||
|
||||
return new ServerResource($server);
|
||||
}
|
||||
|
||||
#[Delete('{site}', name: 'api.projects.servers.sites.delete', middleware: 'ability:write')]
|
||||
#[Endpoint(title: 'delete', description: 'Delete site.')]
|
||||
#[Response(status: 204)]
|
||||
public function delete(Project $project, Server $server, Site $site)
|
||||
{
|
||||
$this->authorize('delete', [$site, $server]);
|
||||
|
||||
$this->validateRoute($project, $server, $site);
|
||||
|
||||
$site->delete();
|
||||
|
||||
return response()->noContent();
|
||||
}
|
||||
|
||||
private function validateRoute(Project $project, Server $server, ?Site $site = null): void
|
||||
{
|
||||
if ($project->id !== $server->project_id) {
|
||||
abort(404, 'Server not found in project');
|
||||
}
|
||||
|
||||
if ($site && $site->server_id !== $server->id) {
|
||||
abort(404, 'Site not found in server');
|
||||
}
|
||||
}
|
||||
}
|
121
app/Http/Controllers/API/SourceControlController.php
Normal file
121
app/Http/Controllers/API/SourceControlController.php
Normal file
@ -0,0 +1,121 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\API;
|
||||
|
||||
use App\Actions\SourceControl\ConnectSourceControl;
|
||||
use App\Actions\SourceControl\DeleteSourceControl;
|
||||
use App\Actions\SourceControl\EditSourceControl;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Resources\SourceControlResource;
|
||||
use App\Models\Project;
|
||||
use App\Models\SourceControl;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\ResourceCollection;
|
||||
use Knuckles\Scribe\Attributes\BodyParam;
|
||||
use Knuckles\Scribe\Attributes\Endpoint;
|
||||
use Knuckles\Scribe\Attributes\Group;
|
||||
use Knuckles\Scribe\Attributes\Response;
|
||||
use Knuckles\Scribe\Attributes\ResponseFromApiResource;
|
||||
use Spatie\RouteAttributes\Attributes\Delete;
|
||||
use Spatie\RouteAttributes\Attributes\Get;
|
||||
use Spatie\RouteAttributes\Attributes\Middleware;
|
||||
use Spatie\RouteAttributes\Attributes\Post;
|
||||
use Spatie\RouteAttributes\Attributes\Prefix;
|
||||
use Spatie\RouteAttributes\Attributes\Put;
|
||||
|
||||
#[Prefix('api/projects/{project}/source-controls')]
|
||||
#[Middleware(['auth:sanctum', 'can-see-project'])]
|
||||
#[Group(name: 'source-controls')]
|
||||
class SourceControlController extends Controller
|
||||
{
|
||||
#[Get('/', name: 'api.projects.source-controls', middleware: 'ability:read')]
|
||||
#[Endpoint(title: 'list')]
|
||||
#[ResponseFromApiResource(SourceControlResource::class, SourceControl::class, collection: true, paginate: 25)]
|
||||
public function index(Project $project): ResourceCollection
|
||||
{
|
||||
$this->authorize('viewAny', SourceControl::class);
|
||||
|
||||
$sourceControls = SourceControl::getByProjectId($project->id)->simplePaginate(25);
|
||||
|
||||
return SourceControlResource::collection($sourceControls);
|
||||
}
|
||||
|
||||
#[Post('/', name: 'api.projects.source-controls.create', middleware: 'ability:write')]
|
||||
#[Endpoint(title: 'create')]
|
||||
#[BodyParam(name: 'provider', description: 'The provider', required: true, enum: [\App\Enums\SourceControl::GITLAB, \App\Enums\SourceControl::GITHUB, \App\Enums\SourceControl::BITBUCKET])]
|
||||
#[BodyParam(name: 'name', description: 'The name of the storage provider.', required: true)]
|
||||
#[BodyParam(name: 'token', description: 'The token if provider requires api token')]
|
||||
#[BodyParam(name: 'url', description: 'The URL if the provider is Gitlab and it is self-hosted')]
|
||||
#[BodyParam(name: 'username', description: 'The username if the provider is Bitbucket')]
|
||||
#[BodyParam(name: 'password', description: 'The password if the provider is Bitbucket')]
|
||||
#[ResponseFromApiResource(SourceControlResource::class, SourceControl::class)]
|
||||
public function create(Request $request, Project $project): SourceControlResource
|
||||
{
|
||||
$this->authorize('create', SourceControl::class);
|
||||
|
||||
$this->validate($request, ConnectSourceControl::rules($request->all()));
|
||||
|
||||
$sourceControl = app(ConnectSourceControl::class)->connect(auth()->user(), $project, $request->all());
|
||||
|
||||
return new SourceControlResource($sourceControl);
|
||||
}
|
||||
|
||||
#[Get('{sourceControl}', name: 'api.projects.source-controls.show', middleware: 'ability:read')]
|
||||
#[Endpoint(title: 'show')]
|
||||
#[ResponseFromApiResource(SourceControlResource::class, SourceControl::class)]
|
||||
public function show(Project $project, SourceControl $sourceControl)
|
||||
{
|
||||
$this->authorize('view', $sourceControl);
|
||||
|
||||
$this->validateRoute($project, $sourceControl);
|
||||
|
||||
return new SourceControlResource($sourceControl);
|
||||
}
|
||||
|
||||
#[Put('{sourceControl}', name: 'api.projects.source-controls.update', middleware: 'ability:write')]
|
||||
#[Endpoint(title: 'update')]
|
||||
#[BodyParam(name: 'name', description: 'The name of the storage provider.', required: true)]
|
||||
#[BodyParam(name: 'token', description: 'The token if provider requires api token')]
|
||||
#[BodyParam(name: 'url', description: 'The URL if the provider is Gitlab and it is self-hosted')]
|
||||
#[BodyParam(name: 'username', description: 'The username if the provider is Bitbucket')]
|
||||
#[BodyParam(name: 'password', description: 'The password if the provider is Bitbucket')]
|
||||
#[BodyParam(name: 'global', description: 'Accessible in all projects', enum: [true, false])]
|
||||
#[ResponseFromApiResource(SourceControlResource::class, SourceControl::class)]
|
||||
public function update(Request $request, Project $project, SourceControl $sourceControl)
|
||||
{
|
||||
$this->authorize('update', $sourceControl);
|
||||
|
||||
$this->validateRoute($project, $sourceControl);
|
||||
|
||||
$this->validate($request, EditSourceControl::rules($sourceControl, $request->all()));
|
||||
|
||||
$sourceControl = app(EditSourceControl::class)->edit($sourceControl, $project, $request->all());
|
||||
|
||||
return new SourceControlResource($sourceControl);
|
||||
}
|
||||
|
||||
#[Delete('{sourceControl}', name: 'api.projects.source-controls.delete', middleware: 'ability:write')]
|
||||
#[Endpoint(title: 'delete')]
|
||||
#[Response(status: 204)]
|
||||
public function delete(Project $project, SourceControl $sourceControl)
|
||||
{
|
||||
$this->authorize('delete', $sourceControl);
|
||||
|
||||
$this->validateRoute($project, $sourceControl);
|
||||
|
||||
app(DeleteSourceControl::class)->delete($sourceControl);
|
||||
|
||||
return response()->noContent();
|
||||
}
|
||||
|
||||
private function validateRoute(Project $project, SourceControl $sourceControl): void
|
||||
{
|
||||
if (! $sourceControl->project_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($project->id !== $sourceControl->project_id) {
|
||||
abort(404, 'Source Control not found in project');
|
||||
}
|
||||
}
|
||||
}
|
116
app/Http/Controllers/API/StorageProviderController.php
Normal file
116
app/Http/Controllers/API/StorageProviderController.php
Normal file
@ -0,0 +1,116 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\API;
|
||||
|
||||
use App\Actions\StorageProvider\CreateStorageProvider;
|
||||
use App\Actions\StorageProvider\DeleteStorageProvider;
|
||||
use App\Actions\StorageProvider\EditStorageProvider;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Resources\StorageProviderResource;
|
||||
use App\Models\Project;
|
||||
use App\Models\StorageProvider;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\ResourceCollection;
|
||||
use Knuckles\Scribe\Attributes\BodyParam;
|
||||
use Knuckles\Scribe\Attributes\Endpoint;
|
||||
use Knuckles\Scribe\Attributes\Group;
|
||||
use Knuckles\Scribe\Attributes\Response;
|
||||
use Knuckles\Scribe\Attributes\ResponseFromApiResource;
|
||||
use Spatie\RouteAttributes\Attributes\Delete;
|
||||
use Spatie\RouteAttributes\Attributes\Get;
|
||||
use Spatie\RouteAttributes\Attributes\Middleware;
|
||||
use Spatie\RouteAttributes\Attributes\Post;
|
||||
use Spatie\RouteAttributes\Attributes\Prefix;
|
||||
use Spatie\RouteAttributes\Attributes\Put;
|
||||
|
||||
#[Prefix('api/projects/{project}/storage-providers')]
|
||||
#[Middleware(['auth:sanctum', 'can-see-project'])]
|
||||
#[Group(name: 'storage-providers')]
|
||||
class StorageProviderController extends Controller
|
||||
{
|
||||
#[Get('/', name: 'api.projects.storage-providers', middleware: 'ability:read')]
|
||||
#[Endpoint(title: 'list')]
|
||||
#[ResponseFromApiResource(StorageProviderResource::class, StorageProvider::class, collection: true, paginate: 25)]
|
||||
public function index(Project $project): ResourceCollection
|
||||
{
|
||||
$this->authorize('viewAny', StorageProvider::class);
|
||||
|
||||
$storageProviders = StorageProvider::getByProjectId($project->id)->simplePaginate(25);
|
||||
|
||||
return StorageProviderResource::collection($storageProviders);
|
||||
}
|
||||
|
||||
#[Post('/', name: 'api.projects.storage-providers.create', middleware: 'ability:write')]
|
||||
#[Endpoint(title: 'create')]
|
||||
#[BodyParam(name: 'provider', description: 'The provider (aws, linode, hetzner, digitalocean, vultr, ...)', required: true)]
|
||||
#[BodyParam(name: 'name', description: 'The name of the storage provider.', required: true)]
|
||||
#[BodyParam(name: 'token', description: 'The token if provider requires api token')]
|
||||
#[BodyParam(name: 'key', description: 'The key if provider requires key')]
|
||||
#[BodyParam(name: 'secret', description: 'The secret if provider requires key')]
|
||||
#[ResponseFromApiResource(StorageProviderResource::class, StorageProvider::class)]
|
||||
public function create(Request $request, Project $project): StorageProviderResource
|
||||
{
|
||||
$this->authorize('create', StorageProvider::class);
|
||||
|
||||
$this->validate($request, CreateStorageProvider::rules($request->all()));
|
||||
|
||||
$storageProvider = app(CreateStorageProvider::class)->create(auth()->user(), $project, $request->all());
|
||||
|
||||
return new StorageProviderResource($storageProvider);
|
||||
}
|
||||
|
||||
#[Get('{storageProvider}', name: 'api.projects.storage-providers.show', middleware: 'ability:read')]
|
||||
#[Endpoint(title: 'show')]
|
||||
#[ResponseFromApiResource(StorageProviderResource::class, StorageProvider::class)]
|
||||
public function show(Project $project, StorageProvider $storageProvider)
|
||||
{
|
||||
$this->authorize('view', $storageProvider);
|
||||
|
||||
$this->validateRoute($project, $storageProvider);
|
||||
|
||||
return new StorageProviderResource($storageProvider);
|
||||
}
|
||||
|
||||
#[Put('{storageProvider}', name: 'api.projects.storage-providers.update', middleware: 'ability:write')]
|
||||
#[Endpoint(title: 'update')]
|
||||
#[BodyParam(name: 'name', description: 'The name of the storage provider.', required: true)]
|
||||
#[BodyParam(name: 'global', description: 'Accessible in all projects', enum: [true, false])]
|
||||
#[ResponseFromApiResource(StorageProviderResource::class, StorageProvider::class)]
|
||||
public function update(Request $request, Project $project, StorageProvider $storageProvider)
|
||||
{
|
||||
$this->authorize('update', $storageProvider);
|
||||
|
||||
$this->validateRoute($project, $storageProvider);
|
||||
|
||||
$this->validate($request, EditStorageProvider::rules());
|
||||
|
||||
$storageProvider = app(EditStorageProvider::class)->edit($storageProvider, $project, $request->all());
|
||||
|
||||
return new StorageProviderResource($storageProvider);
|
||||
}
|
||||
|
||||
#[Delete('{storageProvider}', name: 'api.projects.storage-providers.delete', middleware: 'ability:write')]
|
||||
#[Endpoint(title: 'delete')]
|
||||
#[Response(status: 204)]
|
||||
public function delete(Project $project, StorageProvider $storageProvider)
|
||||
{
|
||||
$this->authorize('delete', $storageProvider);
|
||||
|
||||
$this->validateRoute($project, $storageProvider);
|
||||
|
||||
app(DeleteStorageProvider::class)->delete($storageProvider);
|
||||
|
||||
return response()->noContent();
|
||||
}
|
||||
|
||||
private function validateRoute(Project $project, StorageProvider $storageProvider): void
|
||||
{
|
||||
if (! $storageProvider->project_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($project->id !== $storageProvider->project_id) {
|
||||
abort(404, 'Storage provider not found in project');
|
||||
}
|
||||
}
|
||||
}
|
@ -5,9 +5,13 @@
|
||||
use App\Models\Server;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Validation\Rule;
|
||||
use Spatie\RouteAttributes\Attributes\Middleware;
|
||||
use Spatie\RouteAttributes\Attributes\Post;
|
||||
|
||||
#[Middleware('auth')]
|
||||
class ConsoleController extends Controller
|
||||
{
|
||||
#[Post('/{server}/console', name: 'servers.console.run')]
|
||||
public function run(Server $server, Request $request)
|
||||
{
|
||||
$this->authorize('update', $server);
|
||||
|
@ -62,5 +62,9 @@ class Kernel extends HttpKernel
|
||||
'signed' => \App\Http\Middleware\ValidateSignature::class,
|
||||
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
|
||||
'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
|
||||
'abilities' => \Laravel\Sanctum\Http\Middleware\CheckAbilities::class,
|
||||
'ability' => \Laravel\Sanctum\Http\Middleware\CheckForAnyAbility::class,
|
||||
'has-project' => \App\Http\Middleware\HasProjectMiddleware::class,
|
||||
'can-see-project' => \App\Http\Middleware\CanSeeProjectMiddleware::class,
|
||||
];
|
||||
}
|
||||
|
@ -12,6 +12,6 @@ class Authenticate extends Middleware
|
||||
*/
|
||||
protected function redirectTo(Request $request): ?string
|
||||
{
|
||||
return $request->expectsJson() ? null : route('login');
|
||||
return $request->expectsJson() ? null : url('/');
|
||||
}
|
||||
}
|
||||
|
26
app/Http/Middleware/CanSeeProjectMiddleware.php
Normal file
26
app/Http/Middleware/CanSeeProjectMiddleware.php
Normal file
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use App\Models\Project;
|
||||
use App\Models\User;
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class CanSeeProjectMiddleware
|
||||
{
|
||||
public function handle(Request $request, Closure $next)
|
||||
{
|
||||
/** @var User $user */
|
||||
$user = $request->user();
|
||||
|
||||
/** @var Project $project */
|
||||
$project = $request->route('project');
|
||||
|
||||
if (! $user->can('view', $project)) {
|
||||
abort(403, 'You do not have permission to view this project.');
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
}
|
32
app/Http/Middleware/HasProjectMiddleware.php
Normal file
32
app/Http/Middleware/HasProjectMiddleware.php
Normal file
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use App\Models\User;
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class HasProjectMiddleware
|
||||
{
|
||||
public function handle(Request $request, Closure $next)
|
||||
{
|
||||
/** @var ?User $user */
|
||||
$user = $request->user();
|
||||
if (! $user) {
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
if (! $user->currentProject) {
|
||||
if ($user->allProjects()->count() > 0) {
|
||||
$user->current_project_id = $user->projects->first()->id;
|
||||
$user->save();
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
abort(403, 'You must have a project to access the panel.');
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
}
|
25
app/Http/Resources/CronJobResource.php
Normal file
25
app/Http/Resources/CronJobResource.php
Normal file
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Resources;
|
||||
|
||||
use App\Models\CronJob;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
/** @mixin CronJob */
|
||||
class CronJobResource extends JsonResource
|
||||
{
|
||||
public function toArray(Request $request): array
|
||||
{
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'server_id' => $this->server_id,
|
||||
'command' => $this->command,
|
||||
'user' => $this->user,
|
||||
'frequency' => $this->frequency,
|
||||
'status' => $this->status,
|
||||
'created_at' => $this->created_at,
|
||||
'updated_at' => $this->updated_at,
|
||||
];
|
||||
}
|
||||
}
|
23
app/Http/Resources/DatabaseResource.php
Normal file
23
app/Http/Resources/DatabaseResource.php
Normal file
@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Resources;
|
||||
|
||||
use App\Models\Database;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
/** @mixin Database */
|
||||
class DatabaseResource extends JsonResource
|
||||
{
|
||||
public function toArray(Request $request): array
|
||||
{
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'server_id' => $this->server_id,
|
||||
'name' => $this->name,
|
||||
'status' => $this->status,
|
||||
'created_at' => $this->created_at,
|
||||
'updated_at' => $this->updated_at,
|
||||
];
|
||||
}
|
||||
}
|
25
app/Http/Resources/DatabaseUserResource.php
Normal file
25
app/Http/Resources/DatabaseUserResource.php
Normal file
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Resources;
|
||||
|
||||
use App\Models\DatabaseUser;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
/** @mixin DatabaseUser */
|
||||
class DatabaseUserResource extends JsonResource
|
||||
{
|
||||
public function toArray(Request $request): array
|
||||
{
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'server_id' => $this->server_id,
|
||||
'username' => $this->username,
|
||||
'databases' => $this->databases,
|
||||
'host' => $this->host,
|
||||
'status' => $this->status,
|
||||
'created_at' => $this->created_at,
|
||||
'updated_at' => $this->updated_at,
|
||||
];
|
||||
}
|
||||
}
|
28
app/Http/Resources/FirewallRuleResource.php
Normal file
28
app/Http/Resources/FirewallRuleResource.php
Normal file
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Resources;
|
||||
|
||||
use App\Models\FirewallRule;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
/** @mixin FirewallRule */
|
||||
class FirewallRuleResource extends JsonResource
|
||||
{
|
||||
public function toArray(Request $request): array
|
||||
{
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'server_id' => $this->server_id,
|
||||
'type' => $this->type,
|
||||
'protocol' => $this->protocol,
|
||||
'port' => $this->port,
|
||||
'source' => $this->source,
|
||||
'mask' => $this->mask,
|
||||
'note' => $this->note,
|
||||
'status' => $this->status,
|
||||
'created_at' => $this->created_at,
|
||||
'updated_at' => $this->updated_at,
|
||||
];
|
||||
}
|
||||
}
|
21
app/Http/Resources/ProjectResource.php
Normal file
21
app/Http/Resources/ProjectResource.php
Normal file
@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Resources;
|
||||
|
||||
use App\Models\Project;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
/** @mixin Project */
|
||||
class ProjectResource extends JsonResource
|
||||
{
|
||||
public function toArray(Request $request): array
|
||||
{
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'name' => $this->name,
|
||||
'created_at' => $this->created_at,
|
||||
'updated_at' => $this->updated_at,
|
||||
];
|
||||
}
|
||||
}
|
24
app/Http/Resources/ServerProviderResource.php
Normal file
24
app/Http/Resources/ServerProviderResource.php
Normal file
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Resources;
|
||||
|
||||
use App\Models\ServerProvider;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
/** @mixin ServerProvider */
|
||||
class ServerProviderResource extends JsonResource
|
||||
{
|
||||
public function toArray(Request $request): array
|
||||
{
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'project_id' => $this->project_id,
|
||||
'global' => is_null($this->project_id),
|
||||
'name' => $this->profile,
|
||||
'provider' => $this->provider,
|
||||
'created_at' => $this->created_at,
|
||||
'updated_at' => $this->updated_at,
|
||||
];
|
||||
}
|
||||
}
|
42
app/Http/Resources/ServerResource.php
Normal file
42
app/Http/Resources/ServerResource.php
Normal file
@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Resources;
|
||||
|
||||
use App\Models\Server;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
/** @mixin Server */
|
||||
class ServerResource extends JsonResource
|
||||
{
|
||||
public function toArray(Request $request): array
|
||||
{
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'project_id' => $this->project_id,
|
||||
'user_id' => $this->user_id,
|
||||
'provider_id' => $this->provider_id,
|
||||
'name' => $this->name,
|
||||
'ssh_user' => $this->ssh_user,
|
||||
'ip' => $this->ip,
|
||||
'local_ip' => $this->local_ip,
|
||||
'port' => $this->port,
|
||||
'os' => $this->os,
|
||||
'type' => $this->type,
|
||||
'type_data' => $this->type_data,
|
||||
'provider' => $this->provider,
|
||||
'provider_data' => $this->provider_data,
|
||||
'public_key' => $this->public_key,
|
||||
'status' => $this->status,
|
||||
'auto_update' => $this->auto_update,
|
||||
'available_updates' => $this->available_updates,
|
||||
'security_updates' => $this->security_updates,
|
||||
'progress' => $this->progress,
|
||||
'progress_step' => $this->progress_step,
|
||||
'updates' => $this->updates,
|
||||
'last_update_check' => $this->last_update_check,
|
||||
'created_at' => $this->created_at,
|
||||
'updated_at' => $this->updated_at,
|
||||
];
|
||||
}
|
||||
}
|
28
app/Http/Resources/ServiceResource.php
Normal file
28
app/Http/Resources/ServiceResource.php
Normal file
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Resources;
|
||||
|
||||
use App\Models\Service;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
/** @mixin Service */
|
||||
class ServiceResource extends JsonResource
|
||||
{
|
||||
public function toArray(Request $request): array
|
||||
{
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'server_id' => $this->server_id,
|
||||
'type' => $this->type,
|
||||
'type_data' => $this->type_data,
|
||||
'name' => $this->name,
|
||||
'version' => $this->version,
|
||||
'unit' => $this->unit,
|
||||
'status' => $this->status,
|
||||
'is_default' => $this->is_default,
|
||||
'created_at' => $this->created_at,
|
||||
'updated_at' => $this->updated_at,
|
||||
];
|
||||
}
|
||||
}
|
34
app/Http/Resources/SiteResource.php
Normal file
34
app/Http/Resources/SiteResource.php
Normal file
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Resources;
|
||||
|
||||
use App\Models\Site;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
/** @mixin Site */
|
||||
class SiteResource extends JsonResource
|
||||
{
|
||||
public function toArray(Request $request): array
|
||||
{
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'server_id' => $this->server_id,
|
||||
'source_control_id' => $this->source_control_id,
|
||||
'type' => $this->type,
|
||||
'type_data' => $this->type_data,
|
||||
'domain' => $this->domain,
|
||||
'aliases' => $this->aliases,
|
||||
'web_directory' => $this->web_directory,
|
||||
'path' => $this->path,
|
||||
'php_version' => $this->php_version,
|
||||
'repository' => $this->repository,
|
||||
'branch' => $this->branch,
|
||||
'status' => $this->status,
|
||||
'port' => $this->port,
|
||||
'progress' => $this->progress,
|
||||
'created_at' => $this->created_at,
|
||||
'updated_at' => $this->updated_at,
|
||||
];
|
||||
}
|
||||
}
|
24
app/Http/Resources/SourceControlResource.php
Normal file
24
app/Http/Resources/SourceControlResource.php
Normal file
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Resources;
|
||||
|
||||
use App\Models\SourceControl;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
/** @mixin SourceControl */
|
||||
class SourceControlResource extends JsonResource
|
||||
{
|
||||
public function toArray(Request $request): array
|
||||
{
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'project_id' => $this->project_id,
|
||||
'global' => is_null($this->project_id),
|
||||
'name' => $this->profile,
|
||||
'provider' => $this->provider,
|
||||
'created_at' => $this->created_at,
|
||||
'updated_at' => $this->updated_at,
|
||||
];
|
||||
}
|
||||
}
|
22
app/Http/Resources/SshKeyResource.php
Normal file
22
app/Http/Resources/SshKeyResource.php
Normal file
@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Resources;
|
||||
|
||||
use App\Models\SshKey;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
/** @mixin SshKey */
|
||||
class SshKeyResource extends JsonResource
|
||||
{
|
||||
public function toArray(Request $request): array
|
||||
{
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'user' => $this->user_id ? new UserResource($this->user) : null,
|
||||
'name' => $this->name,
|
||||
'created_at' => $this->created_at,
|
||||
'updated_at' => $this->updated_at,
|
||||
];
|
||||
}
|
||||
}
|
24
app/Http/Resources/StorageProviderResource.php
Normal file
24
app/Http/Resources/StorageProviderResource.php
Normal file
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Resources;
|
||||
|
||||
use App\Models\StorageProvider;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
/** @mixin StorageProvider */
|
||||
class StorageProviderResource extends JsonResource
|
||||
{
|
||||
public function toArray(Request $request): array
|
||||
{
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'project_id' => $this->project_id,
|
||||
'global' => is_null($this->project_id),
|
||||
'name' => $this->profile,
|
||||
'provider' => $this->provider,
|
||||
'created_at' => $this->created_at,
|
||||
'updated_at' => $this->updated_at,
|
||||
];
|
||||
}
|
||||
}
|
22
app/Http/Resources/UserResource.php
Normal file
22
app/Http/Resources/UserResource.php
Normal file
@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Resources;
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
/** @mixin User */
|
||||
class UserResource extends JsonResource
|
||||
{
|
||||
public function toArray(Request $request): array
|
||||
{
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'name' => $this->name,
|
||||
'email' => $this->email,
|
||||
'created_at' => $this->created_at,
|
||||
'updated_at' => $this->updated_at,
|
||||
];
|
||||
}
|
||||
}
|
23
app/Models/PersonalAccessToken.php
Normal file
23
app/Models/PersonalAccessToken.php
Normal file
@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Traits\HasTimezoneTimestamps;
|
||||
use Carbon\Carbon;
|
||||
use Laravel\Sanctum\PersonalAccessToken as SanctumPersonalAccessToken;
|
||||
|
||||
/**
|
||||
* @property int $id
|
||||
* @property string $tokenable_type
|
||||
* @property int $tokenable_id
|
||||
* @property string $name
|
||||
* @property string $token
|
||||
* @property array $abilities
|
||||
* @property Carbon $last_used_at
|
||||
* @property Carbon $created_at
|
||||
* @property Carbon $updated_at
|
||||
*/
|
||||
class PersonalAccessToken extends SanctumPersonalAccessToken
|
||||
{
|
||||
use HasTimezoneTimestamps;
|
||||
}
|
@ -16,6 +16,7 @@
|
||||
use Illuminate\Notifications\Notifiable;
|
||||
use Illuminate\Support\Collection;
|
||||
use Laravel\Fortify\TwoFactorAuthenticatable;
|
||||
use Laravel\Sanctum\HasApiTokens;
|
||||
|
||||
/**
|
||||
* @property int $id
|
||||
@ -43,6 +44,7 @@
|
||||
*/
|
||||
class User extends Authenticatable implements FilamentUser
|
||||
{
|
||||
use HasApiTokens;
|
||||
use HasFactory;
|
||||
use HasTimezoneTimestamps;
|
||||
use Notifiable;
|
||||
@ -67,22 +69,6 @@ class User extends Authenticatable implements FilamentUser
|
||||
protected $appends = [
|
||||
];
|
||||
|
||||
public static function boot(): void
|
||||
{
|
||||
parent::boot();
|
||||
|
||||
static::created(function (User $user) {
|
||||
if ($user->projects()->count() === 0) {
|
||||
$user->createDefaultProject();
|
||||
$user->refresh();
|
||||
}
|
||||
if (! $user->currentProject) {
|
||||
$user->current_project_id = $user->projects()->first()->id;
|
||||
$user->save();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public function servers(): HasMany
|
||||
{
|
||||
return $this->hasMany(Server::class);
|
||||
@ -118,9 +104,19 @@ public function storageProvider(string $provider): HasOne
|
||||
return $this->hasOne(StorageProvider::class)->where('provider', $provider);
|
||||
}
|
||||
|
||||
public function allProjects(): Builder|BelongsToMany
|
||||
{
|
||||
if ($this->isAdmin()) {
|
||||
return Project::query();
|
||||
}
|
||||
|
||||
return $this->projects();
|
||||
}
|
||||
|
||||
public function projects(): BelongsToMany
|
||||
{
|
||||
return $this->belongsToMany(Project::class, 'user_project')->withTimestamps();
|
||||
return $this->belongsToMany(Project::class, 'user_project')
|
||||
->withTimestamps();
|
||||
}
|
||||
|
||||
public function currentProject(): HasOne
|
||||
|
42
app/Policies/PersonalAccessTokenPolicy.php
Normal file
42
app/Policies/PersonalAccessTokenPolicy.php
Normal file
@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace App\Policies;
|
||||
|
||||
use App\Models\PersonalAccessToken;
|
||||
use App\Models\User;
|
||||
use Illuminate\Auth\Access\HandlesAuthorization;
|
||||
|
||||
class PersonalAccessTokenPolicy
|
||||
{
|
||||
use HandlesAuthorization;
|
||||
|
||||
public function viewAny(User $user): bool
|
||||
{
|
||||
return $user->isAdmin();
|
||||
}
|
||||
|
||||
public function view(User $user, PersonalAccessToken $personalAccessToken): bool
|
||||
{
|
||||
return $user->isAdmin();
|
||||
}
|
||||
|
||||
public function create(User $user): bool
|
||||
{
|
||||
return $user->isAdmin();
|
||||
}
|
||||
|
||||
public function update(User $user, PersonalAccessToken $personalAccessToken): bool
|
||||
{
|
||||
return $user->isAdmin();
|
||||
}
|
||||
|
||||
public function delete(User $user, PersonalAccessToken $personalAccessToken): bool
|
||||
{
|
||||
return $user->isAdmin();
|
||||
}
|
||||
|
||||
public function deleteMany(User $user): bool
|
||||
{
|
||||
return $user->isAdmin();
|
||||
}
|
||||
}
|
@ -2,14 +2,15 @@
|
||||
|
||||
namespace App\Policies;
|
||||
|
||||
use App\Models\Project;
|
||||
use App\Models\Server;
|
||||
use App\Models\User;
|
||||
|
||||
class ServerPolicy
|
||||
{
|
||||
public function viewAny(User $user): bool
|
||||
public function viewAny(User $user, Project $project): bool
|
||||
{
|
||||
return $user->isAdmin() || $user->currentProject?->users->contains($user);
|
||||
return $user->isAdmin() || $project->users->contains($user);
|
||||
}
|
||||
|
||||
public function view(User $user, Server $server): bool
|
||||
@ -17,9 +18,9 @@ public function view(User $user, Server $server): bool
|
||||
return $user->isAdmin() || $server->project->users->contains($user);
|
||||
}
|
||||
|
||||
public function create(User $user): bool
|
||||
public function create(User $user, Project $project): bool
|
||||
{
|
||||
return $user->isAdmin() || $user->currentProject?->users->contains($user);
|
||||
return $user->isAdmin() || $project->users->contains($user);
|
||||
}
|
||||
|
||||
public function update(User $user, Server $server): bool
|
||||
|
@ -12,26 +12,29 @@ class ServerProviderPolicy
|
||||
|
||||
public function viewAny(User $user): bool
|
||||
{
|
||||
return $user->isAdmin();
|
||||
return true;
|
||||
}
|
||||
|
||||
public function view(User $user, ServerProvider $serverProvider): bool
|
||||
{
|
||||
return $user->isAdmin();
|
||||
return $user->isAdmin() ||
|
||||
$user->id === $serverProvider->user_id ||
|
||||
$serverProvider->project_id === null ||
|
||||
$serverProvider->project?->users()->where('user_id', $user->id)->exists();
|
||||
}
|
||||
|
||||
public function create(User $user): bool
|
||||
{
|
||||
return $user->isAdmin();
|
||||
return true;
|
||||
}
|
||||
|
||||
public function update(User $user, ServerProvider $serverProvider): bool
|
||||
{
|
||||
return $user->isAdmin();
|
||||
return $user->isAdmin() || $user->id === $serverProvider->user_id;
|
||||
}
|
||||
|
||||
public function delete(User $user, ServerProvider $serverProvider): bool
|
||||
{
|
||||
return $user->isAdmin();
|
||||
return $user->isAdmin() || $user->id === $serverProvider->user_id;
|
||||
}
|
||||
}
|
||||
|
@ -5,9 +5,11 @@
|
||||
use App\Helpers\FTP;
|
||||
use App\Helpers\Notifier;
|
||||
use App\Helpers\SSH;
|
||||
use App\Models\PersonalAccessToken;
|
||||
use Illuminate\Http\Resources\Json\ResourceCollection;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use Laravel\Fortify\Fortify;
|
||||
use Laravel\Sanctum\Sanctum;
|
||||
|
||||
class AppServiceProvider extends ServiceProvider
|
||||
{
|
||||
@ -33,5 +35,7 @@ public function boot(): void
|
||||
$this->app->bind('ftp', function () {
|
||||
return new FTP;
|
||||
});
|
||||
|
||||
Sanctum::usePersonalAccessTokenModel(PersonalAccessToken::class);
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,6 @@
|
||||
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\RateLimiter;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
|
||||
class RouteServiceProvider extends ServiceProvider
|
||||
{
|
||||
@ -25,15 +24,6 @@ class RouteServiceProvider extends ServiceProvider
|
||||
public function boot(): void
|
||||
{
|
||||
$this->configureRateLimiting();
|
||||
|
||||
$this->routes(function () {
|
||||
Route::middleware('api')
|
||||
->prefix('api')
|
||||
->group(base_path('routes/api.php'));
|
||||
|
||||
Route::middleware('web')
|
||||
->group(base_path('routes/web.php'));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use App\Http\Middleware\HasProjectMiddleware;
|
||||
use App\Web\Pages\Settings\Projects\Widgets\SelectProject;
|
||||
use Exception;
|
||||
use Filament\Facades\Filament;
|
||||
@ -102,6 +103,7 @@ public function panel(Panel $panel): Panel
|
||||
])
|
||||
->authMiddleware([
|
||||
Authenticate::class,
|
||||
HasProjectMiddleware::class,
|
||||
])
|
||||
->login()
|
||||
->spa()
|
||||
|
@ -24,8 +24,6 @@ public function uninstall(): void
|
||||
$this->getScript('supervisor/uninstall-supervisor.sh'),
|
||||
'uninstall-supervisor'
|
||||
);
|
||||
$status = $this->service->server->systemd()->status($this->service->unit);
|
||||
$this->service->validateInstall($status);
|
||||
$this->service->server->os()->cleanup();
|
||||
}
|
||||
|
||||
|
@ -21,11 +21,6 @@ public function createRules(array $input): array
|
||||
{
|
||||
return [
|
||||
'token' => 'required',
|
||||
'url' => [
|
||||
'nullable',
|
||||
'url:http,https',
|
||||
'ends_with:/',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
@ -36,6 +31,16 @@ public function createData(array $input): array
|
||||
];
|
||||
}
|
||||
|
||||
public function editRules(array $input): array
|
||||
{
|
||||
return $this->createRules($input);
|
||||
}
|
||||
|
||||
public function editData(array $input): array
|
||||
{
|
||||
return $this->createData($input);
|
||||
}
|
||||
|
||||
public function data(): array
|
||||
{
|
||||
// support for older data
|
||||
|
@ -14,6 +14,18 @@ class Gitlab extends AbstractSourceControlProvider
|
||||
|
||||
protected string $apiVersion = 'api/v4';
|
||||
|
||||
public function createRules(array $input): array
|
||||
{
|
||||
return [
|
||||
'token' => 'required',
|
||||
'url' => [
|
||||
'nullable',
|
||||
'url:http,https',
|
||||
'ends_with:/',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function connect(): bool
|
||||
{
|
||||
$res = Http::withToken($this->data()['token'])
|
||||
|
@ -8,6 +8,10 @@ public function createRules(array $input): array;
|
||||
|
||||
public function createData(array $input): array;
|
||||
|
||||
public function editRules(array $input): array;
|
||||
|
||||
public function editData(array $input): array;
|
||||
|
||||
public function data(): array;
|
||||
|
||||
public function connect(): bool;
|
||||
|
@ -38,12 +38,7 @@ function htmx(): HtmxResponse
|
||||
|
||||
function vito_version(): string
|
||||
{
|
||||
$version = exec('git describe --tags');
|
||||
if (str($version)->contains('-')) {
|
||||
return str($version)->before('-').' (dev)';
|
||||
}
|
||||
|
||||
return $version;
|
||||
return config('app.version');
|
||||
}
|
||||
|
||||
function convert_time_format($string): string
|
||||
|
13
app/Traits/Enum.php
Normal file
13
app/Traits/Enum.php
Normal file
@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace App\Traits;
|
||||
|
||||
trait Enum
|
||||
{
|
||||
public static function all(): array
|
||||
{
|
||||
$reflection = new \ReflectionClass(self::class);
|
||||
|
||||
return $reflection->getConstants();
|
||||
}
|
||||
}
|
@ -34,7 +34,7 @@ public static function getNavigationItemActiveRoutePattern(): string
|
||||
|
||||
public function mount(): void
|
||||
{
|
||||
$this->authorize('viewAny', Server::class);
|
||||
$this->authorize('viewAny', [Server::class, auth()->user()->currentProject]);
|
||||
}
|
||||
|
||||
public function getWidgets(): array
|
||||
@ -50,6 +50,8 @@ protected function getHeaderActions(): array
|
||||
'public_key' => get_public_key_content(),
|
||||
]);
|
||||
|
||||
$project = auth()->user()->currentProject;
|
||||
|
||||
return [
|
||||
\Filament\Actions\Action::make('read-the-docs')
|
||||
->label('Read the Docs')
|
||||
@ -60,7 +62,7 @@ protected function getHeaderActions(): array
|
||||
\Filament\Actions\Action::make('create')
|
||||
->label('Create a Server')
|
||||
->icon('heroicon-o-plus')
|
||||
->authorize('create', Server::class)
|
||||
->authorize('create', [Server::class, auth()->user()->currentProject])
|
||||
->modalWidth(MaxWidth::FiveExtraLarge)
|
||||
->slideOver()
|
||||
->form([
|
||||
@ -74,7 +76,7 @@ protected function getHeaderActions(): array
|
||||
$set('region', null);
|
||||
$set('plan', null);
|
||||
})
|
||||
->rules(fn ($get) => CreateServerAction::rules($get())['provider']),
|
||||
->rules(fn ($get) => CreateServerAction::rules($project, $get())['provider']),
|
||||
AlertField::make('alert')
|
||||
->warning()
|
||||
->message(__('servers.create.public_key_warning'))
|
||||
@ -82,7 +84,7 @@ protected function getHeaderActions(): array
|
||||
Select::make('server_provider')
|
||||
->visible(fn ($get) => $get('provider') !== ServerProvider::CUSTOM)
|
||||
->label('Server provider connection')
|
||||
->rules(fn ($get) => CreateServerAction::rules($get())['server_provider'])
|
||||
->rules(fn ($get) => CreateServerAction::rules($project, $get())['server_provider'])
|
||||
->options(function ($get) {
|
||||
return \App\Models\ServerProvider::getByProjectId(auth()->user()->current_project_id)
|
||||
->where('provider', $get('provider'))
|
||||
@ -109,7 +111,7 @@ protected function getHeaderActions(): array
|
||||
->schema([
|
||||
Select::make('region')
|
||||
->label('Region')
|
||||
->rules(fn ($get) => CreateServerAction::rules($get())['region'])
|
||||
->rules(fn ($get) => CreateServerAction::rules($project, $get())['region'])
|
||||
->live()
|
||||
->reactive()
|
||||
->options(function ($get) {
|
||||
@ -125,7 +127,7 @@ protected function getHeaderActions(): array
|
||||
->searchable(),
|
||||
Select::make('plan')
|
||||
->label('Plan')
|
||||
->rules(fn ($get) => CreateServerAction::rules($get())['plan'])
|
||||
->rules(fn ($get) => CreateServerAction::rules($project, $get())['plan'])
|
||||
->reactive()
|
||||
->options(function ($get) {
|
||||
if (! $get('server_provider') || ! $get('region')) {
|
||||
@ -162,15 +164,15 @@ protected function getHeaderActions(): array
|
||||
->visible(fn ($get) => $get('provider') === ServerProvider::CUSTOM),
|
||||
TextInput::make('name')
|
||||
->label('Name')
|
||||
->rules(fn ($get) => CreateServerAction::rules($get())['name']),
|
||||
->rules(fn ($get) => CreateServerAction::rules($project, $get())['name']),
|
||||
Grid::make()
|
||||
->schema([
|
||||
TextInput::make('ip')
|
||||
->label('SSH IP Address')
|
||||
->rules(fn ($get) => CreateServerAction::rules($get())['ip']),
|
||||
->rules(fn ($get) => CreateServerAction::rules($project, $get())['ip']),
|
||||
TextInput::make('port')
|
||||
->label('SSH Port')
|
||||
->rules(fn ($get) => CreateServerAction::rules($get())['port']),
|
||||
->rules(fn ($get) => CreateServerAction::rules($project, $get())['port']),
|
||||
])
|
||||
->visible(fn ($get) => $get('provider') === ServerProvider::CUSTOM),
|
||||
Grid::make()
|
||||
@ -178,7 +180,7 @@ protected function getHeaderActions(): array
|
||||
Select::make('os')
|
||||
->label('OS')
|
||||
->native(false)
|
||||
->rules(fn ($get) => CreateServerAction::rules($get())['os'])
|
||||
->rules(fn ($get) => CreateServerAction::rules($project, $get())['os'])
|
||||
->options(
|
||||
collect(config('core.operating_systems'))
|
||||
->mapWithKeys(fn ($value) => [$value => $value])
|
||||
@ -187,7 +189,7 @@ protected function getHeaderActions(): array
|
||||
->label('Server Type')
|
||||
->native(false)
|
||||
->selectablePlaceholder(false)
|
||||
->rules(fn ($get) => CreateServerAction::rules($get())['type'])
|
||||
->rules(fn ($get) => CreateServerAction::rules($project, $get())['type'])
|
||||
->options(
|
||||
collect(config('core.server_types'))
|
||||
->mapWithKeys(fn ($value) => [$value => $value])
|
||||
@ -200,7 +202,7 @@ protected function getHeaderActions(): array
|
||||
->label('Webserver')
|
||||
->native(false)
|
||||
->selectablePlaceholder(false)
|
||||
->rules(fn ($get) => CreateServerAction::rules($get())['webserver'] ?? [])
|
||||
->rules(fn ($get) => CreateServerAction::rules($project, $get())['webserver'] ?? [])
|
||||
->options(
|
||||
collect(config('core.webservers'))->mapWithKeys(fn ($value) => [$value => $value])
|
||||
),
|
||||
@ -208,7 +210,7 @@ protected function getHeaderActions(): array
|
||||
->label('Database')
|
||||
->native(false)
|
||||
->selectablePlaceholder(false)
|
||||
->rules(fn ($get) => CreateServerAction::rules($get())['database'] ?? [])
|
||||
->rules(fn ($get) => CreateServerAction::rules($project, $get())['database'] ?? [])
|
||||
->options(
|
||||
collect(config('core.databases_name'))
|
||||
->mapWithKeys(fn ($value, $key) => [
|
||||
@ -219,7 +221,7 @@ protected function getHeaderActions(): array
|
||||
->label('PHP')
|
||||
->native(false)
|
||||
->selectablePlaceholder(false)
|
||||
->rules(fn ($get) => CreateServerAction::rules($get())['php'] ?? [])
|
||||
->rules(fn ($get) => CreateServerAction::rules($project, $get())['php'] ?? [])
|
||||
->options(
|
||||
collect(config('core.php_versions'))
|
||||
->mapWithKeys(fn ($value) => [$value => $value])
|
||||
@ -229,7 +231,7 @@ protected function getHeaderActions(): array
|
||||
->modalSubmitActionLabel('Create')
|
||||
->action(function (array $data) {
|
||||
run_action($this, function () use ($data) {
|
||||
$server = app(CreateServerAction::class)->create(auth()->user(), $data);
|
||||
$server = app(CreateServerAction::class)->create(auth()->user(), auth()->user()->currentProject, $data);
|
||||
|
||||
$this->redirect(View::getUrl(['server' => $server]));
|
||||
});
|
||||
|
@ -8,7 +8,6 @@
|
||||
use Exception;
|
||||
use Filament\Forms\Components\DatePicker;
|
||||
use Filament\Tables\Actions\Action;
|
||||
use Filament\Tables\Actions\BulkActionGroup;
|
||||
use Filament\Tables\Actions\DeleteAction;
|
||||
use Filament\Tables\Actions\DeleteBulkAction;
|
||||
use Filament\Tables\Columns\TextColumn;
|
||||
@ -115,12 +114,10 @@ public function getTable(): Table
|
||||
->color('danger')
|
||||
->authorize(fn ($record) => auth()->user()->can('delete', $record)),
|
||||
])
|
||||
->bulkActions(
|
||||
BulkActionGroup::make([
|
||||
DeleteBulkAction::make()
|
||||
->requiresConfirmation()
|
||||
->authorize(auth()->user()->can('deleteMany', [ServerLog::class, $this->server])),
|
||||
])
|
||||
);
|
||||
->bulkActions([
|
||||
DeleteBulkAction::make()
|
||||
->requiresConfirmation()
|
||||
->authorize(auth()->user()->can('deleteMany', [ServerLog::class, $this->server])),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ class Settings extends Page
|
||||
|
||||
public function mount(): void
|
||||
{
|
||||
$this->authorize('update', $this->server);
|
||||
$this->authorize('update', [$this->server, auth()->user()->currentProject]);
|
||||
}
|
||||
|
||||
public function getWidgets(): array
|
||||
@ -45,6 +45,7 @@ protected function getHeaderActions(): array
|
||||
->requiresConfirmation()
|
||||
->modalHeading('Delete Server')
|
||||
->modalDescription('Once your server is deleted, all of its resources and data will be permanently deleted and can\'t be restored')
|
||||
->authorize('delete', $this->server)
|
||||
->action(function () {
|
||||
try {
|
||||
$this->server->delete();
|
||||
|
@ -18,7 +18,7 @@ class View extends Page
|
||||
|
||||
public function mount(): void
|
||||
{
|
||||
$this->authorize('view', $this->server);
|
||||
$this->authorize('view', [$this->server, auth()->user()->currentProject]);
|
||||
$this->previousStatus = $this->server->status;
|
||||
}
|
||||
|
||||
|
115
app/Web/Pages/Settings/APIKeys/Index.php
Normal file
115
app/Web/Pages/Settings/APIKeys/Index.php
Normal file
@ -0,0 +1,115 @@
|
||||
<?php
|
||||
|
||||
namespace App\Web\Pages\Settings\APIKeys;
|
||||
|
||||
use App\Models\PersonalAccessToken;
|
||||
use App\Web\Components\Page;
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Forms\Components\Radio;
|
||||
use Filament\Forms\Components\TextInput;
|
||||
use Filament\Infolists\Components\TextEntry;
|
||||
use Filament\Support\Enums\MaxWidth;
|
||||
|
||||
class Index extends Page
|
||||
{
|
||||
protected static ?string $navigationGroup = 'Settings';
|
||||
|
||||
protected static ?string $slug = 'settings/api-keys';
|
||||
|
||||
protected static ?string $title = 'API Keys';
|
||||
|
||||
protected static ?string $navigationIcon = 'icon-plug';
|
||||
|
||||
protected static ?int $navigationSort = 11;
|
||||
|
||||
public string $token = '';
|
||||
|
||||
protected $listeners = ['$refresh'];
|
||||
|
||||
public static function canAccess(): bool
|
||||
{
|
||||
return auth()->user()?->can('viewAny', PersonalAccessToken::class) ?? false;
|
||||
}
|
||||
|
||||
public function getWidgets(): array
|
||||
{
|
||||
return [
|
||||
[Widgets\ApiKeysList::class],
|
||||
];
|
||||
}
|
||||
|
||||
public function unmountAction(bool $shouldCancelParentActions = true, bool $shouldCloseModal = true): void
|
||||
{
|
||||
parent::unmountAction($shouldCancelParentActions, $shouldCloseModal);
|
||||
|
||||
$this->token = '';
|
||||
}
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
Action::make('read-the-docs')
|
||||
->label('Read the Docs')
|
||||
->icon('heroicon-o-document-text')
|
||||
->color('gray')
|
||||
->url(config('scribe.static.url'))
|
||||
->openUrlInNewTab(),
|
||||
Action::make('create')
|
||||
->label('Create new Key')
|
||||
->icon('heroicon-o-plus')
|
||||
->modalHeading('Create a new Key')
|
||||
->modalSubmitActionLabel('Create')
|
||||
->form(function () {
|
||||
if ($this->token) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return [
|
||||
TextInput::make('name')
|
||||
->label('Token Name')
|
||||
->required(),
|
||||
Radio::make('permission')
|
||||
->options([
|
||||
'read' => 'Read',
|
||||
'write' => 'Read & Write',
|
||||
])
|
||||
->required(),
|
||||
];
|
||||
})
|
||||
->infolist(function () {
|
||||
if ($this->token) {
|
||||
return [
|
||||
TextEntry::make('token')
|
||||
->state($this->token)
|
||||
->tooltip('Copy')
|
||||
->copyable()
|
||||
->helperText('You can see the token only one!'),
|
||||
];
|
||||
}
|
||||
|
||||
return [];
|
||||
})
|
||||
->authorize('create', PersonalAccessToken::class)
|
||||
->modalWidth(MaxWidth::Large)
|
||||
->action(function (array $data) {
|
||||
$permissions = ['read'];
|
||||
if ($data['permission'] === 'write') {
|
||||
$permissions[] = 'write';
|
||||
}
|
||||
$token = auth()->user()->createToken($data['name'], $permissions);
|
||||
|
||||
$this->dispatch('$refresh');
|
||||
|
||||
$this->token = $token->plainTextToken;
|
||||
|
||||
$this->halt();
|
||||
})
|
||||
->modalSubmitAction(function () {
|
||||
if ($this->token) {
|
||||
return false;
|
||||
}
|
||||
})
|
||||
->closeModalByClickingAway(fn () => ! $this->token),
|
||||
];
|
||||
}
|
||||
}
|
62
app/Web/Pages/Settings/APIKeys/Widgets/ApiKeysList.php
Normal file
62
app/Web/Pages/Settings/APIKeys/Widgets/ApiKeysList.php
Normal file
@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
namespace App\Web\Pages\Settings\APIKeys\Widgets;
|
||||
|
||||
use App\Models\PersonalAccessToken;
|
||||
use Filament\Tables\Actions\DeleteAction;
|
||||
use Filament\Tables\Actions\DeleteBulkAction;
|
||||
use Filament\Tables\Columns\TextColumn;
|
||||
use Filament\Tables\Table;
|
||||
use Filament\Widgets\TableWidget as Widget;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
|
||||
class ApiKeysList extends Widget
|
||||
{
|
||||
protected $listeners = ['$refresh'];
|
||||
|
||||
protected function getTableQuery(): Builder
|
||||
{
|
||||
return auth()->user()->tokens()->getQuery();
|
||||
}
|
||||
|
||||
protected function getTableColumns(): array
|
||||
{
|
||||
return [
|
||||
TextColumn::make('name')
|
||||
->searchable()
|
||||
->sortable(),
|
||||
TextColumn::make('abilities')
|
||||
->searchable()
|
||||
->sortable(),
|
||||
TextColumn::make('created_at')
|
||||
->label('Created At')
|
||||
->formatStateUsing(fn (PersonalAccessToken $record) => $record->created_at_by_timezone)
|
||||
->searchable()
|
||||
->sortable(),
|
||||
TextColumn::make('last_used_at')
|
||||
->label('Last Used At')
|
||||
->formatStateUsing(fn (PersonalAccessToken $record) => $record->getDateTimeByTimezone($record->last_used_at))
|
||||
->searchable()
|
||||
->sortable(),
|
||||
];
|
||||
}
|
||||
|
||||
public function getTable(): Table
|
||||
{
|
||||
return $this->table
|
||||
->heading('')
|
||||
->actions([
|
||||
DeleteAction::make('delete')
|
||||
->modalHeading('Delete Token')
|
||||
->authorize(fn (PersonalAccessToken $record) => auth()->user()->can('delete', $record))
|
||||
->using(function (array $data, PersonalAccessToken $record) {
|
||||
$record->delete();
|
||||
}),
|
||||
])
|
||||
->bulkActions([
|
||||
DeleteBulkAction::make()
|
||||
->requiresConfirmation()
|
||||
->authorize(auth()->user()->can('deleteMany', PersonalAccessToken::class)),
|
||||
]);
|
||||
}
|
||||
}
|
@ -15,9 +15,7 @@
|
||||
|
||||
class NotificationChannelsList extends Widget
|
||||
{
|
||||
protected $listeners = [
|
||||
'$refresh' => 'refreshTable',
|
||||
];
|
||||
protected $listeners = ['$refresh'];
|
||||
|
||||
protected function getTableQuery(): Builder
|
||||
{
|
||||
|
@ -4,7 +4,6 @@
|
||||
|
||||
use App\Models\Project;
|
||||
use Filament\Widgets\Widget;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
class SelectProject extends Widget
|
||||
@ -20,15 +19,7 @@ class SelectProject extends Widget
|
||||
public function mount(): void
|
||||
{
|
||||
$this->currentProject = auth()->user()->currentProject;
|
||||
$this->projects = Project::query()
|
||||
->where(function (Builder $query) {
|
||||
if (auth()->user()->isAdmin()) {
|
||||
return;
|
||||
}
|
||||
$query->where('user_id', auth()->id())
|
||||
->orWhereHas('users', fn ($query) => $query->where('user_id', auth()->id()));
|
||||
})
|
||||
->get();
|
||||
$this->projects = auth()->user()->allProjects()->get();
|
||||
}
|
||||
|
||||
public function updateProject(Project $project): void
|
||||
|
@ -8,6 +8,7 @@
|
||||
use Filament\Forms\Components\Checkbox;
|
||||
use Filament\Forms\Components\Select;
|
||||
use Filament\Forms\Components\TextInput;
|
||||
use Filament\Forms\Get;
|
||||
use Filament\Notifications\Notification;
|
||||
|
||||
class Create
|
||||
@ -23,29 +24,22 @@ public static function form(): array
|
||||
)
|
||||
->live()
|
||||
->reactive()
|
||||
->rules(CreateServerProvider::rules()['provider']),
|
||||
->rules(fn (Get $get) => CreateServerProvider::rules($get())['provider']),
|
||||
TextInput::make('name')
|
||||
->rules(CreateServerProvider::rules()['name']),
|
||||
->rules(fn (Get $get) => CreateServerProvider::rules($get())['name']),
|
||||
TextInput::make('token')
|
||||
->label('API Key')
|
||||
->validationAttribute('API Key')
|
||||
->visible(fn ($get) => in_array($get('provider'), [
|
||||
ServerProvider::DIGITALOCEAN,
|
||||
ServerProvider::LINODE,
|
||||
ServerProvider::VULTR,
|
||||
ServerProvider::HETZNER,
|
||||
]))
|
||||
->rules(fn ($get) => CreateServerProvider::providerRules($get())['token']),
|
||||
->visible(fn ($get) => isset(CreateServerProvider::rules($get())['token']))
|
||||
->rules(fn (Get $get) => CreateServerProvider::rules($get())['token']),
|
||||
TextInput::make('key')
|
||||
->label('Access Key')
|
||||
->visible(function ($get) {
|
||||
return $get('provider') == ServerProvider::AWS;
|
||||
})
|
||||
->rules(fn ($get) => CreateServerProvider::providerRules($get())['key']),
|
||||
->visible(fn ($get) => isset(CreateServerProvider::rules($get())['key']))
|
||||
->rules(fn (Get $get) => CreateServerProvider::rules($get())['key']),
|
||||
TextInput::make('secret')
|
||||
->label('Secret')
|
||||
->visible(fn ($get) => $get('provider') == ServerProvider::AWS)
|
||||
->rules(fn ($get) => CreateServerProvider::providerRules($get())['secret']),
|
||||
->visible(fn ($get) => isset(CreateServerProvider::rules($get())['secret']))
|
||||
->rules(fn (Get $get) => CreateServerProvider::rules($get())['secret']),
|
||||
Checkbox::make('global')
|
||||
->label('Is Global (Accessible in all projects)'),
|
||||
];
|
||||
@ -57,7 +51,7 @@ public static function form(): array
|
||||
public static function action(array $data): void
|
||||
{
|
||||
try {
|
||||
app(CreateServerProvider::class)->create(auth()->user(), $data);
|
||||
app(CreateServerProvider::class)->create(auth()->user(), auth()->user()->currentProject, $data);
|
||||
} catch (Exception $e) {
|
||||
Notification::make()
|
||||
->title($e->getMessage())
|
||||
|
@ -22,6 +22,6 @@ public static function form(): array
|
||||
|
||||
public static function action(ServerProvider $provider, array $data): void
|
||||
{
|
||||
app(EditServerProvider::class)->edit($provider, auth()->user(), $data);
|
||||
app(EditServerProvider::class)->edit($provider, auth()->user()->currentProject, $data);
|
||||
}
|
||||
}
|
||||
|
@ -56,7 +56,7 @@ public static function form(): array
|
||||
public static function action(array $data): void
|
||||
{
|
||||
try {
|
||||
app(ConnectSourceControl::class)->connect(auth()->user(), $data);
|
||||
app(ConnectSourceControl::class)->connect(auth()->user(), auth()->user()->currentProject, $data);
|
||||
} catch (Exception $e) {
|
||||
Notification::make()
|
||||
->title($e->getMessage())
|
||||
|
@ -2,9 +2,8 @@
|
||||
|
||||
namespace App\Web\Pages\Settings\SourceControls\Actions;
|
||||
|
||||
use App\Actions\SourceControl\ConnectSourceControl;
|
||||
use App\Actions\SourceControl\EditSourceControl;
|
||||
use App\Enums\SourceControl;
|
||||
use App\Models\SourceControl;
|
||||
use Exception;
|
||||
use Filament\Forms\Components\Checkbox;
|
||||
use Filament\Forms\Components\TextInput;
|
||||
@ -13,30 +12,27 @@
|
||||
|
||||
class Edit
|
||||
{
|
||||
public static function form(): array
|
||||
public static function form(SourceControl $sourceControl): array
|
||||
{
|
||||
return [
|
||||
TextInput::make('name')
|
||||
->rules(fn (Get $get) => ConnectSourceControl::rules($get())['name']),
|
||||
->rules(fn (Get $get) => EditSourceControl::rules($sourceControl, $get())['name']),
|
||||
TextInput::make('token')
|
||||
->label('API Key')
|
||||
->validationAttribute('API Key')
|
||||
->visible(fn ($get) => in_array($get('provider'), [
|
||||
SourceControl::GITHUB,
|
||||
SourceControl::GITLAB,
|
||||
]))
|
||||
->rules(fn (Get $get) => ConnectSourceControl::rules($get())['token']),
|
||||
->visible(fn (Get $get) => EditSourceControl::rules($sourceControl, $get())['token'] ?? false)
|
||||
->rules(fn (Get $get) => EditSourceControl::rules($sourceControl, $get())['token']),
|
||||
TextInput::make('url')
|
||||
->label('URL (optional)')
|
||||
->visible(fn ($get) => $get('provider') == SourceControl::GITLAB)
|
||||
->rules(fn (Get $get) => ConnectSourceControl::rules($get())['url'])
|
||||
->visible(fn (Get $get) => EditSourceControl::rules($sourceControl, $get())['url'] ?? false)
|
||||
->rules(fn (Get $get) => EditSourceControl::rules($sourceControl, $get())['url'])
|
||||
->helperText('If you run a self-managed gitlab enter the url here, leave empty to use gitlab.com'),
|
||||
TextInput::make('username')
|
||||
->visible(fn ($get) => $get('provider') == SourceControl::BITBUCKET)
|
||||
->rules(fn (Get $get) => ConnectSourceControl::rules($get())['username']),
|
||||
->visible(fn (Get $get) => EditSourceControl::rules($sourceControl, $get())['username'] ?? false)
|
||||
->rules(fn (Get $get) => EditSourceControl::rules($sourceControl, $get())['username']),
|
||||
TextInput::make('password')
|
||||
->visible(fn ($get) => $get('provider') == SourceControl::BITBUCKET)
|
||||
->rules(fn (Get $get) => ConnectSourceControl::rules($get())['password']),
|
||||
->visible(fn (Get $get) => EditSourceControl::rules($sourceControl, $get())['password'] ?? false)
|
||||
->rules(fn (Get $get) => EditSourceControl::rules($sourceControl, $get())['password']),
|
||||
Checkbox::make('global')
|
||||
->label('Is Global (Accessible in all projects)'),
|
||||
];
|
||||
@ -45,10 +41,10 @@ public static function form(): array
|
||||
/**
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function action(\App\Models\SourceControl $sourceControl, array $data): void
|
||||
public static function action(SourceControl $sourceControl, array $data): void
|
||||
{
|
||||
try {
|
||||
app(EditSourceControl::class)->edit($sourceControl, auth()->user(), $data);
|
||||
app(EditSourceControl::class)->edit($sourceControl, auth()->user()->currentProject, $data);
|
||||
} catch (Exception $e) {
|
||||
Notification::make()
|
||||
->title($e->getMessage())
|
||||
|
@ -69,7 +69,7 @@ public function getTable(): Table
|
||||
'global' => $record->project_id === null,
|
||||
];
|
||||
})
|
||||
->form(Edit::form())
|
||||
->form(fn (SourceControl $record) => Edit::form($record))
|
||||
->authorize(fn (SourceControl $record) => auth()->user()->can('update', $record))
|
||||
->using(fn (array $data, SourceControl $record) => Edit::action($record, $data))
|
||||
->modalWidth(MaxWidth::Medium),
|
||||
|
@ -126,7 +126,7 @@ public static function form(): array
|
||||
public static function action(array $data): void
|
||||
{
|
||||
try {
|
||||
app(CreateStorageProvider::class)->create(auth()->user(), $data);
|
||||
app(CreateStorageProvider::class)->create(auth()->user(), auth()->user()->currentProject, $data);
|
||||
} catch (Exception $e) {
|
||||
Notification::make()
|
||||
->title($e->getMessage())
|
||||
|
@ -22,6 +22,6 @@ public static function form(): array
|
||||
|
||||
public static function action(StorageProvider $provider, array $data): void
|
||||
{
|
||||
app(EditStorageProvider::class)->edit($provider, auth()->user(), $data);
|
||||
app(EditStorageProvider::class)->edit($provider, auth()->user()->currentProject, $data);
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user