From c3f69f32475cb6bf00b93e6b1fe546d09b50fbc7 Mon Sep 17 00:00:00 2001 From: Saeed Vaziry Date: Wed, 4 Jun 2025 08:08:20 +0200 Subject: [PATCH] #591 - sites --- app/Actions/FirewallRule/ManageRule.php | 12 +- app/Actions/Redirect/CreateRedirect.php | 3 + app/Actions/SSL/CreateSSL.php | 3 + app/Actions/SSL/DeactivateSSL.php | 15 ++ app/Actions/Site/CreateCommand.php | 3 + app/Actions/Site/DeleteSite.php | 18 +- app/Actions/Site/Deploy.php | 14 +- app/Actions/Site/EditCommand.php | 3 + app/Actions/Site/ExecuteCommand.php | 36 ++- app/Actions/Site/UpdateBranch.php | 3 + app/Actions/Site/UpdateDeploymentScript.php | 3 + app/Actions/Site/UpdateEnv.php | 9 +- app/Actions/Site/UpdateLoadBalancer.php | 3 + app/Actions/Site/UpdatePHPVersion.php | 23 +- app/Actions/Site/UpdateSourceControl.php | 3 + .../DeploymentScriptIsEmptyException.php | 17 +- app/Exceptions/FailedToDeployGitHook.php | 11 +- app/Exceptions/FailedToDestroyGitHook.php | 11 +- .../SourceControlIsNotConnected.php | 14 +- .../Controllers/ApplicationController.php | 136 ++++++++++ app/Http/Controllers/CommandController.php | 92 +++++++ app/Http/Controllers/RedirectController.php | 56 +++++ app/Http/Controllers/SSLController.php | 106 ++++++++ app/Http/Controllers/SiteController.php | 36 +-- .../Controllers/SiteSettingController.php | 110 ++++++++ app/Http/Controllers/WorkerController.php | 2 +- .../Resources/CommandExecutionResource.php | 31 +++ app/Http/Resources/CommandResource.php | 28 +++ app/Http/Resources/DeploymentResource.php | 32 +++ .../Resources/LoadBalancerServerResource.php | 28 +++ app/Http/Resources/RedirectResource.php | 4 +- app/Http/Resources/ServerResource.php | 1 + app/Http/Resources/SiteResource.php | 4 + app/Http/Resources/SslResource.php | 31 +++ app/Models/Command.php | 3 +- app/Models/CommandExecution.php | 5 +- app/Models/Deployment.php | 3 +- app/Models/DeploymentScript.php | 3 +- app/Models/LoadBalancerServer.php | 3 +- app/Models/Ssl.php | 3 +- app/Policies/CommandPolicy.php | 6 - app/Policies/RedirectPolicy.php | 26 +- app/Policies/SslPolicy.php | 14 +- app/Policies/WorkerPolicy.php | 50 +--- app/SSH/Services/Webserver/Caddy.php | 5 + app/SSH/Services/Webserver/Nginx.php | 5 + app/SSH/Services/Webserver/Webserver.php | 2 + .../js/components/app-sidebar-nested.tsx | 2 +- resources/js/components/app-sidebar.tsx | 4 +- resources/js/components/copyable-badge.tsx | 2 +- resources/js/components/site-switch.tsx | 13 +- resources/js/components/ui/dynamic-field.tsx | 11 +- resources/js/hooks/use-appearance.tsx | 10 +- resources/js/icons/php.tsx | 5 +- resources/js/layouts/server/layout.tsx | 48 +++- resources/js/lib/editor.ts | 235 ++++++++++++++++++ resources/js/lib/site-helper.ts | 20 ++ .../components/app-with-deployment.tsx | 77 ++++++ .../components/auto-deployment.tsx | 59 +++++ .../pages/application/components/deploy.tsx | 53 ++++ .../components/deployment-columns.tsx | 61 +++++ .../components/deployment-script.tsx | 69 +++++ .../js/pages/application/components/env.tsx | 91 +++++++ .../application/components/load-balancer.tsx | 108 ++++++++ resources/js/pages/application/index.tsx | 20 ++ .../pages/backups/components/edit-backup.tsx | 8 +- .../js/pages/commands/components/columns.tsx | 118 +++++++++ .../commands/components/create-command.tsx | 87 +++++++ .../commands/components/edit-command.tsx | 81 ++++++ .../js/pages/commands/components/execute.tsx | 69 +++++ .../commands/components/execution-columns.tsx | 53 ++++ resources/js/pages/commands/index.tsx | 44 ++++ resources/js/pages/commands/show.tsx | 35 +++ resources/js/pages/php/components/ini.tsx | 19 +- .../js/pages/redirects/components/columns.tsx | 129 ++++++++++ .../redirects/components/create-redirect.tsx | 112 +++++++++ resources/js/pages/redirects/index.tsx | 50 ++++ .../pages/server-logs/components/columns.tsx | 8 +- resources/js/pages/server-settings/index.tsx | 17 ++ .../js/pages/servers/components/actions.tsx | 9 +- .../js/pages/servers/components/header.tsx | 4 +- .../pages/site-settings/components/branch.tsx | 70 ++++++ .../site-settings/components/delete-site.tsx | 73 ++++++ .../site-settings/components/php-version.tsx | 75 ++++++ .../components/source-control.tsx | 70 ++++++ .../pages/site-settings/components/vhost.tsx | 104 ++++++++ resources/js/pages/site-settings/index.tsx | 167 +++++++++++++ .../js/pages/sites/components/columns.tsx | 2 +- resources/js/pages/sites/logs.tsx | 36 +++ .../js/pages/ssls/components/columns.tsx | 189 ++++++++++++++ .../js/pages/ssls/components/create-ssl.tsx | 141 +++++++++++ .../js/pages/ssls/components/force-ssl.tsx | 59 +++++ resources/js/pages/ssls/index.tsx | 64 +++++ .../js/pages/workers/components/form.tsx | 5 +- resources/js/pages/workers/index.tsx | 4 +- resources/js/types/command-execution.d.ts | 16 ++ resources/js/types/command.d.ts | 12 + resources/js/types/deployment.d.ts | 23 ++ resources/js/types/index.d.ts | 3 +- resources/js/types/load-balancer-server.d.ts | 11 + resources/js/types/redirect.d.ts | 14 ++ resources/js/types/server.d.ts | 3 + resources/js/types/site.d.ts | 14 +- resources/js/types/ssl.d.ts | 17 ++ resources/svg/php.svg | 2 +- tests/Feature/ApplicationTest.php | 131 +++------- tests/Feature/CommandsTest.php | 82 +++--- tests/Feature/FileManagerTest.php | 138 ---------- tests/Feature/LoadBalancerTest.php | 62 ++--- tests/Feature/RedirectsTest.php | 40 ++- tests/Feature/ServerTest.php | 156 ++++-------- tests/Feature/SiteLogsTest.php | 37 --- tests/Feature/SitesTest.php | 135 +++++----- tests/Feature/SslTest.php | 82 +++--- 114 files changed, 4032 insertions(+), 765 deletions(-) create mode 100644 app/Actions/SSL/DeactivateSSL.php create mode 100644 app/Http/Controllers/ApplicationController.php create mode 100644 app/Http/Controllers/CommandController.php create mode 100644 app/Http/Controllers/RedirectController.php create mode 100644 app/Http/Controllers/SSLController.php create mode 100644 app/Http/Controllers/SiteSettingController.php create mode 100644 app/Http/Resources/CommandExecutionResource.php create mode 100644 app/Http/Resources/CommandResource.php create mode 100644 app/Http/Resources/DeploymentResource.php create mode 100644 app/Http/Resources/LoadBalancerServerResource.php create mode 100644 app/Http/Resources/SslResource.php create mode 100644 resources/js/lib/editor.ts create mode 100644 resources/js/lib/site-helper.ts create mode 100644 resources/js/pages/application/components/app-with-deployment.tsx create mode 100644 resources/js/pages/application/components/auto-deployment.tsx create mode 100644 resources/js/pages/application/components/deploy.tsx create mode 100644 resources/js/pages/application/components/deployment-columns.tsx create mode 100644 resources/js/pages/application/components/deployment-script.tsx create mode 100644 resources/js/pages/application/components/env.tsx create mode 100644 resources/js/pages/application/components/load-balancer.tsx create mode 100644 resources/js/pages/application/index.tsx create mode 100644 resources/js/pages/commands/components/columns.tsx create mode 100644 resources/js/pages/commands/components/create-command.tsx create mode 100644 resources/js/pages/commands/components/edit-command.tsx create mode 100644 resources/js/pages/commands/components/execute.tsx create mode 100644 resources/js/pages/commands/components/execution-columns.tsx create mode 100644 resources/js/pages/commands/index.tsx create mode 100644 resources/js/pages/commands/show.tsx create mode 100644 resources/js/pages/redirects/components/columns.tsx create mode 100644 resources/js/pages/redirects/components/create-redirect.tsx create mode 100644 resources/js/pages/redirects/index.tsx create mode 100644 resources/js/pages/site-settings/components/branch.tsx create mode 100644 resources/js/pages/site-settings/components/delete-site.tsx create mode 100644 resources/js/pages/site-settings/components/php-version.tsx create mode 100644 resources/js/pages/site-settings/components/source-control.tsx create mode 100644 resources/js/pages/site-settings/components/vhost.tsx create mode 100644 resources/js/pages/site-settings/index.tsx create mode 100644 resources/js/pages/sites/logs.tsx create mode 100644 resources/js/pages/ssls/components/columns.tsx create mode 100644 resources/js/pages/ssls/components/create-ssl.tsx create mode 100644 resources/js/pages/ssls/components/force-ssl.tsx create mode 100644 resources/js/pages/ssls/index.tsx create mode 100644 resources/js/types/command-execution.d.ts create mode 100644 resources/js/types/command.d.ts create mode 100644 resources/js/types/deployment.d.ts create mode 100644 resources/js/types/load-balancer-server.d.ts create mode 100644 resources/js/types/redirect.d.ts create mode 100644 resources/js/types/ssl.d.ts delete mode 100644 tests/Feature/FileManagerTest.php delete mode 100644 tests/Feature/SiteLogsTest.php diff --git a/app/Actions/FirewallRule/ManageRule.php b/app/Actions/FirewallRule/ManageRule.php index c3ab1a6c..e341c30f 100755 --- a/app/Actions/FirewallRule/ManageRule.php +++ b/app/Actions/FirewallRule/ManageRule.php @@ -123,9 +123,19 @@ public static function rules(array $input): array 'min:1', 'max:65535', ], + 'source' => [ + 'nullable', + 'ip', + ], + 'mask' => [ + 'nullable', + 'numeric', + 'min:1', + 'max:32', + ], ]; - if (! ($input['source_any'] ?? false)) { + if (isset($input['source_any']) && $input['source_any'] === false) { $rules['source'] = ['required', 'ip']; $rules['mask'] = ['required', 'numeric', 'min:1', 'max:32']; } diff --git a/app/Actions/Redirect/CreateRedirect.php b/app/Actions/Redirect/CreateRedirect.php index 297f0663..b91aa916 100644 --- a/app/Actions/Redirect/CreateRedirect.php +++ b/app/Actions/Redirect/CreateRedirect.php @@ -7,6 +7,7 @@ use App\Models\Service; use App\Models\Site; use App\SSH\Services\Webserver\Webserver; +use Illuminate\Support\Facades\Validator; use Illuminate\Validation\Rule; class CreateRedirect @@ -16,6 +17,8 @@ class CreateRedirect */ public function create(Site $site, array $input): Redirect { + Validator::make($input, self::rules($site))->validate(); + $redirect = new Redirect; $redirect->site_id = $site->id; diff --git a/app/Actions/SSL/CreateSSL.php b/app/Actions/SSL/CreateSSL.php index 6048aeea..c4eb3dc6 100644 --- a/app/Actions/SSL/CreateSSL.php +++ b/app/Actions/SSL/CreateSSL.php @@ -9,6 +9,7 @@ use App\Models\Site; use App\Models\Ssl; use App\SSH\Services\Webserver\Webserver; +use Illuminate\Support\Facades\Validator; use Illuminate\Validation\Rule; use Illuminate\Validation\ValidationException; @@ -21,6 +22,8 @@ class CreateSSL */ public function create(Site $site, array $input): void { + Validator::make($input, self::rules($input))->validate(); + $site->ssls() ->where('type', $input['type']) ->where('status', SslStatus::FAILED) diff --git a/app/Actions/SSL/DeactivateSSL.php b/app/Actions/SSL/DeactivateSSL.php new file mode 100644 index 00000000..55c00477 --- /dev/null +++ b/app/Actions/SSL/DeactivateSSL.php @@ -0,0 +1,15 @@ +is_active = false; + $ssl->save(); + $ssl->site->webserver()->updateVHost($ssl->site); + } +} diff --git a/app/Actions/Site/CreateCommand.php b/app/Actions/Site/CreateCommand.php index 0a0ac6cf..727a086e 100644 --- a/app/Actions/Site/CreateCommand.php +++ b/app/Actions/Site/CreateCommand.php @@ -4,6 +4,7 @@ use App\Models\Command; use App\Models\Site; +use Illuminate\Support\Facades\Validator; class CreateCommand { @@ -12,6 +13,8 @@ class CreateCommand */ public function create(Site $site, array $input): Command { + Validator::make($input, self::rules())->validate(); + $script = new Command([ 'site_id' => $site->id, 'name' => $input['name'], diff --git a/app/Actions/Site/DeleteSite.php b/app/Actions/Site/DeleteSite.php index b92c3b6c..bb60c019 100644 --- a/app/Actions/Site/DeleteSite.php +++ b/app/Actions/Site/DeleteSite.php @@ -7,14 +7,20 @@ use App\Models\Site; use App\SSH\Services\PHP\PHP; use App\SSH\Services\Webserver\Webserver; +use Illuminate\Support\Facades\Validator; +use Illuminate\Validation\Rule; class DeleteSite { /** + * @param array $input + * * @throws SSHError */ - public function delete(Site $site): void + public function delete(Site $site, array $input): void { + $this->validate($site, $input); + /** @var Service $service */ $service = $site->server->webserver(); @@ -35,4 +41,14 @@ public function delete(Site $site): void $site->delete(); } + + private function validate(Site $site, array $input): void + { + Validator::make($input, [ + 'domain' => [ + 'required', + Rule::in($site->domain), + ], + ])->validate(); + } } diff --git a/app/Actions/Site/Deploy.php b/app/Actions/Site/Deploy.php index 80e51518..e262e5ff 100644 --- a/app/Actions/Site/Deploy.php +++ b/app/Actions/Site/Deploy.php @@ -4,7 +4,6 @@ use App\Enums\DeploymentStatus; use App\Exceptions\DeploymentScriptIsEmptyException; -use App\Exceptions\SSHError; use App\Facades\Notifier; use App\Models\Deployment; use App\Models\ServerLog; @@ -15,7 +14,6 @@ class Deploy { /** * @throws DeploymentScriptIsEmptyException - * @throws SSHError */ public function run(Site $site): Deployment { @@ -32,6 +30,11 @@ public function run(Site $site): Deployment 'deployment_script_id' => $site->deploymentScript->id, 'status' => DeploymentStatus::DEPLOYING, ]); + $log = ServerLog::newLog($site->server, 'deploy-'.strtotime('now')) + ->forSite($site); + $log->save(); + $deployment->log_id = $log->id; + $deployment->save(); $lastCommit = $site->sourceControl?->provider()?->getLastCommit($site->repository, $site->branch); if ($lastCommit) { $deployment->commit_id = $lastCommit['commit_id']; @@ -39,12 +42,7 @@ public function run(Site $site): Deployment } $deployment->save(); - dispatch(function () use ($site, $deployment): void { - $log = ServerLog::newLog($site->server, 'deploy-'.strtotime('now')) - ->forSite($site); - $log->save(); - $deployment->log_id = $log->id; - $deployment->save(); + dispatch(function () use ($site, $deployment, $log): void { $site->server->os()->runScript( path: $site->path, script: $site->deploymentScript->content, diff --git a/app/Actions/Site/EditCommand.php b/app/Actions/Site/EditCommand.php index 6479f56d..8aeeb5e9 100644 --- a/app/Actions/Site/EditCommand.php +++ b/app/Actions/Site/EditCommand.php @@ -3,6 +3,7 @@ namespace App\Actions\Site; use App\Models\Command; +use Illuminate\Support\Facades\Validator; class EditCommand { @@ -11,6 +12,8 @@ class EditCommand */ public function edit(Command $command, array $input): Command { + Validator::make($input, self::rules())->validate(); + $command->name = $input['name']; $command->command = $input['command']; $command->save(); diff --git a/app/Actions/Site/ExecuteCommand.php b/app/Actions/Site/ExecuteCommand.php index 2bc86eeb..adad9666 100644 --- a/app/Actions/Site/ExecuteCommand.php +++ b/app/Actions/Site/ExecuteCommand.php @@ -7,6 +7,7 @@ use App\Models\CommandExecution; use App\Models\ServerLog; use App\Models\User; +use Illuminate\Support\Facades\Validator; class ExecuteCommand { @@ -15,19 +16,31 @@ class ExecuteCommand */ public function execute(Command $command, User $user, array $input): CommandExecution { + Validator::make($input, self::rules($command))->validate(); + + $variables = []; + foreach ($command->getVariables() as $variable) { + if (array_key_exists($variable, $input)) { + $variables[$variable] = $input[$variable] ?? ''; + } + } + $execution = new CommandExecution([ 'command_id' => $command->id, 'server_id' => $command->site->server_id, 'user_id' => $user->id, - 'variables' => $input['variables'] ?? [], + 'variables' => $variables, 'status' => CommandExecutionStatus::EXECUTING, ]); $execution->save(); - dispatch(function () use ($execution, $command): void { + $log = ServerLog::newLog($execution->server, 'command-'.$command->id.'-'.strtotime('now')); + $log->save(); + $execution->server_log_id = $log->id; + $execution->save(); + + dispatch(function () use ($execution, $command, $log): void { $content = $execution->getContent(); - $log = ServerLog::newLog($execution->server, 'command-'.$command->id.'-'.strtotime('now')); - $log->save(); $execution->server_log_id = $log->id; $execution->save(); $execution->server->os()->runScript( @@ -48,18 +61,19 @@ public function execute(Command $command, User $user, array $input): CommandExec } /** - * @param array $input * @return array> */ - public static function rules(array $input): array + public static function rules(Command $command): array { - return [ - 'variables' => 'array', - 'variables.*' => [ + $rules = []; + foreach ($command->getVariables() as $variable) { + $rules[$variable] = [ 'required', 'string', 'max:255', - ], - ]; + ]; + } + + return $rules; } } diff --git a/app/Actions/Site/UpdateBranch.php b/app/Actions/Site/UpdateBranch.php index e66b6a9f..aa6c7815 100755 --- a/app/Actions/Site/UpdateBranch.php +++ b/app/Actions/Site/UpdateBranch.php @@ -5,6 +5,7 @@ use App\Exceptions\SSHError; use App\Models\Site; use App\SSH\Git\Git; +use Illuminate\Support\Facades\Validator; class UpdateBranch { @@ -15,6 +16,8 @@ class UpdateBranch */ public function update(Site $site, array $input): void { + Validator::make($input, self::rules())->validate(); + $site->branch = $input['branch']; app(Git::class)->fetchOrigin($site); app(Git::class)->checkout($site); diff --git a/app/Actions/Site/UpdateDeploymentScript.php b/app/Actions/Site/UpdateDeploymentScript.php index a7e91fac..e273dafb 100755 --- a/app/Actions/Site/UpdateDeploymentScript.php +++ b/app/Actions/Site/UpdateDeploymentScript.php @@ -4,6 +4,7 @@ use App\Models\DeploymentScript; use App\Models\Site; +use Illuminate\Support\Facades\Validator; class UpdateDeploymentScript { @@ -12,6 +13,8 @@ class UpdateDeploymentScript */ public function update(Site $site, array $input): void { + Validator::make($input, self::rules())->validate(); + /** @var DeploymentScript $script */ $script = $site->deploymentScript; $script->content = $input['script']; diff --git a/app/Actions/Site/UpdateEnv.php b/app/Actions/Site/UpdateEnv.php index b0b92073..1219d6a9 100644 --- a/app/Actions/Site/UpdateEnv.php +++ b/app/Actions/Site/UpdateEnv.php @@ -4,6 +4,7 @@ use App\Exceptions\SSHError; use App\Models\Site; +use Illuminate\Support\Facades\Validator; class UpdateEnv { @@ -14,10 +15,14 @@ class UpdateEnv */ public function update(Site $site, array $input): void { - $site->server->os()->editFileAs( + Validator::make($input, [ + 'env' => ['required', 'string'], + ])->validate(); + + $site->server->os()->write( $site->path.'/.env', - $site->user, trim((string) $input['env']), + $site->user, ); } } diff --git a/app/Actions/Site/UpdateLoadBalancer.php b/app/Actions/Site/UpdateLoadBalancer.php index 83cab4b9..a628e9d8 100644 --- a/app/Actions/Site/UpdateLoadBalancer.php +++ b/app/Actions/Site/UpdateLoadBalancer.php @@ -5,6 +5,7 @@ use App\Enums\LoadBalancerMethod; use App\Models\LoadBalancerServer; use App\Models\Site; +use Illuminate\Support\Facades\Validator; use Illuminate\Validation\Rule; class UpdateLoadBalancer @@ -14,6 +15,8 @@ class UpdateLoadBalancer */ public function update(Site $site, array $input): void { + Validator::make($input, self::rules($site))->validate(); + $site->loadBalancerServers()->delete(); foreach ($input['servers'] as $server) { diff --git a/app/Actions/Site/UpdatePHPVersion.php b/app/Actions/Site/UpdatePHPVersion.php index ed5a4f53..dbb4915d 100644 --- a/app/Actions/Site/UpdatePHPVersion.php +++ b/app/Actions/Site/UpdatePHPVersion.php @@ -4,10 +4,23 @@ use App\Exceptions\SSHError; use App\Models\Site; +use Illuminate\Support\Facades\Validator; use Illuminate\Validation\Rule; class UpdatePHPVersion { + /** + * @param array $input + * + * @throws SSHError + */ + public function update(Site $site, array $input): void + { + Validator::make($input, self::rules($site))->validate(); + + $site->changePHPVersion($input['version']); + } + /** * @return array> */ @@ -22,14 +35,4 @@ public static function rules(Site $site): array ], ]; } - - /** - * @param array $input - * - * @throws SSHError - */ - public function update(Site $site, array $input): void - { - $site->changePHPVersion($input['version']); - } } diff --git a/app/Actions/Site/UpdateSourceControl.php b/app/Actions/Site/UpdateSourceControl.php index 56a5b7bc..7be47a2b 100644 --- a/app/Actions/Site/UpdateSourceControl.php +++ b/app/Actions/Site/UpdateSourceControl.php @@ -6,6 +6,7 @@ use App\Exceptions\RepositoryPermissionDenied; use App\Exceptions\SourceControlIsNotConnected; use App\Models\Site; +use Illuminate\Support\Facades\Validator; use Illuminate\Validation\Rule; use Illuminate\Validation\ValidationException; @@ -18,6 +19,8 @@ class UpdateSourceControl */ public function update(Site $site, array $input): void { + Validator::make($input, self::rules())->validate(); + $site->source_control_id = $input['source_control']; try { if ($site->sourceControl) { diff --git a/app/Exceptions/DeploymentScriptIsEmptyException.php b/app/Exceptions/DeploymentScriptIsEmptyException.php index 034e1735..df00bd16 100644 --- a/app/Exceptions/DeploymentScriptIsEmptyException.php +++ b/app/Exceptions/DeploymentScriptIsEmptyException.php @@ -3,5 +3,20 @@ namespace App\Exceptions; use Exception; +use Illuminate\Http\RedirectResponse; +use Illuminate\Http\Request; +use Illuminate\Validation\ValidationException; -class DeploymentScriptIsEmptyException extends Exception {} +class DeploymentScriptIsEmptyException extends Exception +{ + public function render(Request $request): RedirectResponse + { + if ($request->header('X-Inertia')) { + return back()->with('error', 'Cannot deploy an empty deployment script.'); + } + + throw ValidationException::withMessages([ + 'deployment_script' => 'Deployment script cannot be empty.', + ]); + } +} diff --git a/app/Exceptions/FailedToDeployGitHook.php b/app/Exceptions/FailedToDeployGitHook.php index 3c060447..ffd943b6 100644 --- a/app/Exceptions/FailedToDeployGitHook.php +++ b/app/Exceptions/FailedToDeployGitHook.php @@ -3,8 +3,17 @@ namespace App\Exceptions; use Exception; +use Illuminate\Http\RedirectResponse; +use Illuminate\Http\Request; class FailedToDeployGitHook extends Exception { - // + public function render(Request $request): ?RedirectResponse + { + if ($request->header('X-Inertia')) { + return back()->with('error', 'Failed to deploy git hook.'); + } + + return null; + } } diff --git a/app/Exceptions/FailedToDestroyGitHook.php b/app/Exceptions/FailedToDestroyGitHook.php index 5fa4959e..ee632361 100644 --- a/app/Exceptions/FailedToDestroyGitHook.php +++ b/app/Exceptions/FailedToDestroyGitHook.php @@ -3,8 +3,17 @@ namespace App\Exceptions; use Exception; +use Illuminate\Http\RedirectResponse; +use Illuminate\Http\Request; class FailedToDestroyGitHook extends Exception { - // + public function render(Request $request): ?RedirectResponse + { + if ($request->header('X-Inertia')) { + return back()->with('error', 'Failed to destroy git hook.'); + } + + return null; + } } diff --git a/app/Exceptions/SourceControlIsNotConnected.php b/app/Exceptions/SourceControlIsNotConnected.php index 70bc5e13..d0e0f078 100755 --- a/app/Exceptions/SourceControlIsNotConnected.php +++ b/app/Exceptions/SourceControlIsNotConnected.php @@ -3,5 +3,17 @@ namespace App\Exceptions; use Exception; +use Illuminate\Http\RedirectResponse; +use Illuminate\Http\Request; -class SourceControlIsNotConnected extends Exception {} +class SourceControlIsNotConnected extends Exception +{ + public function render(Request $request): ?RedirectResponse + { + if ($request->header('X-Inertia')) { + return back()->with('error', 'Source control is not connected.'); + } + + return null; + } +} diff --git a/app/Http/Controllers/ApplicationController.php b/app/Http/Controllers/ApplicationController.php new file mode 100644 index 00000000..897e635f --- /dev/null +++ b/app/Http/Controllers/ApplicationController.php @@ -0,0 +1,136 @@ +authorize('view', [$site, $server]); + + return Inertia::render('application/index', [ + 'deployments' => DeploymentResource::collection($site->deployments()->latest()->simplePaginate(config('web.pagination_size'))), + 'deploymentScript' => $site->deploymentScript?->content, + 'loadBalancerServers' => LoadBalancerServerResource::collection($site->loadBalancerServers) + ]); + } + + #[Put('/deployment-script', name: 'application.update-deployment-script')] + public function updateDeploymentScript(Request $request, Server $server, Site $site): RedirectResponse + { + $this->authorize('update', [$site, $server]); + + app(UpdateDeploymentScript::class)->update($site, $request->input()); + + return back()->with('success', 'Deployment script updated successfully.'); + } + + /** + * @throws DeploymentScriptIsEmptyException + */ + #[Post('/deploy', name: 'application.deploy')] + public function deploy(Server $server, Site $site): RedirectResponse + { + $this->authorize('update', [$site, $server]); + + app(Deploy::class)->run($site); + + return back()->with('info', 'Deployment started, please wait...'); + } + + #[Get('/env', name: 'application.env')] + public function env(Server $server, Site $site): JsonResponse + { + $this->authorize('view', [$site, $server]); + + $env = $site->getEnv(); + + return response()->json([ + 'env' => $env, + ]); + } + + /** + * @throws SSHError + */ + #[Put('/env', name: 'application.update-env')] + public function updateEnv(Request $request, Server $server, Site $site): RedirectResponse + { + $this->authorize('update', [$site, $server]); + + app(UpdateEnv::class)->update($site, $request->input()); + + return back()->with('success', '.env file updated successfully.'); + } + + /** + * @throws SourceControlIsNotConnected + */ + #[Post('/enable-auto-deployment', name: 'application.enable-auto-deployment')] + public function enableAutoDeployment(Server $server, Site $site): RedirectResponse + { + $this->authorize('update', [$site, $server]); + + if (! $site->sourceControl) { + return back()->with('error', 'Cannot find source control for this site.'); + } + + $site->enableAutoDeployment(); + + return back()->with('success', 'Auto deployment enabled successfully.'); + } + + /** + * @throws SourceControlIsNotConnected + * @throws FailedToDestroyGitHook + */ + #[Post('/disable-auto-deployment', name: 'application.disable-auto-deployment')] + public function disableAutoDeployment(Server $server, Site $site): RedirectResponse + { + $this->authorize('update', [$site, $server]); + + if (! $site->sourceControl) { + return back()->with('error', 'Cannot find source control for this site.'); + } + + $site->disableAutoDeployment(); + + return back()->with('success', 'Auto deployment disabled successfully.'); + } + + #[Post('/load-balancer', name: 'application.update-load-balancer')] + public function updateLoadBalancer(Request $request, Server $server, Site $site): RedirectResponse + { + $this->authorize('update', [$site, $server]); + + app(UpdateLoadBalancer::class)->update($site, $request->input()); + + return back()->with('success', 'Load balancer updated successfully.'); + } +} diff --git a/app/Http/Controllers/CommandController.php b/app/Http/Controllers/CommandController.php new file mode 100644 index 00000000..997fcc2f --- /dev/null +++ b/app/Http/Controllers/CommandController.php @@ -0,0 +1,92 @@ +authorize('viewAny', [Command::class, $site, $server]); + + return Inertia::render('commands/index', [ + 'commands' => CommandResource::collection($site->commands()->latest()->simplePaginate(config('web.pagination_size'))), + ]); + } + + #[Post('/', name: 'commands.store')] + public function store(Request $request, Server $server, Site $site): RedirectResponse + { + $this->authorize('create', [Command::class, $site, $server]); + + app(CreateCommand::class)->create($site, $request->input()); + + return back() + ->with('success', 'Command created successfully.'); + } + + #[Get('/{command}', name: 'commands.show')] + public function show(Server $server, Site $site, Command $command): Response + { + $this->authorize('view', [$command, $site, $server]); + + return Inertia::render('commands/show', [ + 'command' => new CommandResource($command), + 'executions' => CommandExecutionResource::collection($command->executions()->latest()->simplePaginate(config('web.pagination_size'))), + ]); + } + + #[Put('/{command}', name: 'commands.update')] + public function update(Request $request, Server $server, Site $site, Command $command): RedirectResponse + { + $this->authorize('update', [$command, $site, $server]); + + app(EditCommand::class)->edit($command, $request->input()); + + return back() + ->with('success', 'Command updated successfully.'); + } + + #[Delete('/{command}', name: 'commands.destroy')] + public function destroy(Server $server, Site $site, Command $command): RedirectResponse + { + $this->authorize('delete', [$command, $site, $server]); + + $command->delete(); + + return back() + ->with('success', 'Command deleted successfully.'); + } + + #[Post('/{command}/execute', name: 'commands.execute')] + public function execute(Request $request, Server $server, Site $site, Command $command): RedirectResponse + { + $this->authorize('update', [$site, $server]); + + app(ExecuteCommand::class)->execute($command, user(), $request->input()); + + return redirect()->route('commands.show', ['server' => $server, 'site' => $site, 'command' => $command]) + ->with('info', 'Command is being executed.'); + } +} diff --git a/app/Http/Controllers/RedirectController.php b/app/Http/Controllers/RedirectController.php new file mode 100644 index 00000000..fda6f691 --- /dev/null +++ b/app/Http/Controllers/RedirectController.php @@ -0,0 +1,56 @@ +authorize('viewAny', [Redirect::class, $site, $server]); + + return Inertia::render('redirects/index', [ + 'redirects' => RedirectResource::collection($site->redirects()->latest()->simplePaginate(config('web.pagination_size'))), + ]); + } + + #[Post('/', name: 'redirects.store')] + public function store(Request $request, Server $server, Site $site): RedirectResponse + { + $this->authorize('create', [Redirect::class, $site, $server]); + + app(CreateRedirect::class)->create($site, $request->input()); + + return back() + ->with('info', 'Creating the redirect'); + } + + #[Delete('/{redirect}', name: 'redirects.destroy')] + public function destroy(Server $server, Site $site, Redirect $redirect): RedirectResponse + { + $this->authorize('delete', [$redirect, $site, $server]); + + app(DeleteRedirect::class)->delete($site, $redirect); + + return back() + ->with('info', 'Deleting the redirect'); + } +} diff --git a/app/Http/Controllers/SSLController.php b/app/Http/Controllers/SSLController.php new file mode 100644 index 00000000..a70833b9 --- /dev/null +++ b/app/Http/Controllers/SSLController.php @@ -0,0 +1,106 @@ +authorize('viewAny', [Ssl::class, $site, $server]); + + return Inertia::render('ssls/index', [ + 'ssls' => SslResource::collection($site->ssls()->latest()->simplePaginate(config('web.pagination_size'))), + ]); + } + + #[Post('/', name: 'ssls.store')] + public function store(Request $request, Server $server, Site $site): RedirectResponse + { + $this->authorize('create', [Ssl::class, $site, $server]); + + app(CreateSSL::class)->create($site, $request->input()); + + return back() + ->with('info', 'Setting up SSL.'); + } + + #[Delete('/{ssl}', name: 'ssls.destroy')] + public function destroy(Server $server, Site $site, Ssl $ssl): RedirectResponse + { + $this->authorize('delete', [$ssl, $site, $server]); + + app(DeleteSSL::class)->delete($ssl); + + return back() + ->with('success', 'SSL deleted successfully.'); + } + + #[Post('/enable-force-ssl', name: 'ssls.enable-force-ssl')] + public function enableForceSSL(Server $server, Site $site): RedirectResponse + { + $this->authorize('update', [$site, $server]); + + $site->force_ssl = true; + $site->save(); + $site->webserver()->updateVHost($site); + + return back() + ->with('success', 'Force SSL enabled successfully.'); + } + + #[Post('/disable-force-ssl', name: 'ssls.disable-force-ssl')] + public function disableForceSSL(Server $server, Site $site): RedirectResponse + { + $this->authorize('update', [$site, $server]); + + $site->force_ssl = false; + $site->save(); + $site->webserver()->updateVHost($site); + + return back() + ->with('success', 'Force SSL disabled successfully.'); + } + + #[Post('/{ssl}/activate', name: 'ssls.activate')] + public function activate(Server $server, Site $site, Ssl $ssl): RedirectResponse + { + $this->authorize('update', [$ssl, $site, $server]); + + app(ActivateSSL::class)->activate($ssl); + + return back() + ->with('success', 'SSL activated successfully.'); + } + + #[Post('/{ssl}/deactivate', name: 'ssls.deactivate')] + public function deactivate(Server $server, Site $site, Ssl $ssl): RedirectResponse + { + $this->authorize('update', [$ssl, $site, $server]); + + app(DeactivateSSL::class)->deactivate($ssl); + + return back() + ->with('success', 'SSL deactivated successfully.'); + } +} diff --git a/app/Http/Controllers/SiteController.php b/app/Http/Controllers/SiteController.php index fcc85f22..271aa243 100644 --- a/app/Http/Controllers/SiteController.php +++ b/app/Http/Controllers/SiteController.php @@ -3,8 +3,6 @@ namespace App\Http\Controllers; use App\Actions\Site\CreateSite; -use App\Actions\Site\DeleteSite; -use App\Exceptions\SSHError; use App\Http\Resources\ServerLogResource; use App\Http\Resources\SiteResource; use App\Models\Server; @@ -14,7 +12,6 @@ use Illuminate\Support\Facades\URL; use Inertia\Inertia; use Inertia\Response; -use Spatie\RouteAttributes\Attributes\Delete; use Spatie\RouteAttributes\Attributes\Get; use Spatie\RouteAttributes\Attributes\Middleware; use Spatie\RouteAttributes\Attributes\Post; @@ -43,17 +40,6 @@ public function server(Server $server): Response ]); } - #[Get('/servers/{server}/sites/{site}', name: 'sites.show')] - public function show(Server $server, Site $site): Response - { - $this->authorize('view', [$site, $server]); - - return Inertia::render('sites/show', [ - 'site' => SiteResource::make($site), - 'logs' => ServerLogResource::collection($site->logs()->latest()->simplePaginate(config('web.pagination_size'), pageName: 'logsPage')), - ]); - } - /** * @throws Throwable */ @@ -64,7 +50,7 @@ public function store(Request $request, Server $server): RedirectResponse $site = app(CreateSite::class)->create($server, $request->all()); - return redirect()->route('sites.show', ['server' => $server, 'site' => $site]) + return redirect()->route('application', ['server' => $server, 'site' => $site]) ->with('info', 'Installing site, please wait...'); } @@ -79,26 +65,22 @@ public function switch(Server $server, Site $site): RedirectResponse if ($previousRoute->hasParameter('site')) { if (count($previousRoute->parameters()) > 2) { - return redirect()->route('sites.show', ['server' => $server->id, 'site' => $site->id]); + return redirect()->route('application', ['server' => $server->id, 'site' => $site->id]); } return redirect()->route($previousRoute->getName(), ['server' => $server, 'site' => $site->id]); } - return redirect()->route('sites.show', ['server' => $server->id, 'site' => $site->id]); + return redirect()->route('application', ['server' => $server->id, 'site' => $site->id]); } - /** - * @throws SSHError - */ - #[Delete('/servers/{server}/sites/{site}', name: 'sites.destroy')] - public function destroy(Server $server, Site $site): RedirectResponse + #[Get('/servers/{server}/sites/{site}/logs', name: 'sites.logs')] + public function logs(Server $server, Site $site): Response { - $this->authorize('delete', [$site, $server]); + $this->authorize('view', [$site, $server]); - app(DeleteSite::class)->delete($site); - - return redirect()->route('sites', ['server' => $server]) - ->with('success', 'Site deleted successfully.'); + return Inertia::render('sites/logs', [ + 'logs' => ServerLogResource::collection($site->logs()->latest()->simplePaginate(config('web.pagination_size'))), + ]); } } diff --git a/app/Http/Controllers/SiteSettingController.php b/app/Http/Controllers/SiteSettingController.php new file mode 100644 index 00000000..2021077e --- /dev/null +++ b/app/Http/Controllers/SiteSettingController.php @@ -0,0 +1,110 @@ + $site->sourceControl ? SourceControlResource::make($site->sourceControl) : null, + ]); + } + + /** + * @throws SSHError + */ + #[Patch('/branch', name: 'site-settings.update-branch')] + public function updateBranch(Request $request, Server $server, Site $site): RedirectResponse + { + $this->authorize('update', [$site, $server]); + + app(UpdateBranch::class)->update($site, $request->input()); + + return back()->with('success', 'Branch updated successfully.'); + } + + #[Patch('/source-control', name: 'site-settings.update-source-control')] + public function updateSourceControl(Request $request, Server $server, Site $site): RedirectResponse + { + $this->authorize('update', [$site, $server]); + + app(UpdateSourceControl::class)->update($site, $request->input()); + + return back()->with('success', 'Source control updated successfully.'); + } + + /** + * @throws SSHError + */ + #[Patch('/php-version', name: 'site-settings.update-php-version')] + public function updatePHPVersion(Request $request, Server $server, Site $site): RedirectResponse + { + $this->authorize('update', [$site, $server]); + + app(UpdatePHPVersion::class)->update($site, $request->input()); + + return back()->with('success', 'PHP version updated successfully.'); + } + + #[Get('/vhost', name: 'site-settings.vhost')] + public function vhost(Server $server, Site $site): JsonResponse + { + $this->authorize('update', [$site, $server]); + + return response()->json([ + 'vhost' => $site->webserver()->getVHost($site), + ]); + } + + #[Put('/vhost', name: 'site-settings.update-vhost')] + public function updateVhost(Request $request, Server $server, Site $site): RedirectResponse + { + $this->authorize('update', [$site, $server]); + + $this->validate($request, [ + 'vhost' => 'required|string', + ]); + + $site->webserver()->updateVHost($site, $request->input('vhost')); + + return back()->with('success', 'VHost updated successfully.'); + } + + /** + * @throws SSHError + */ + #[Delete('/', name: 'site-settings.destroy')] + public function destroy(Request $request, Server $server, Site $site): RedirectResponse + { + $this->authorize('delete', [$site, $server]); + + app(DeleteSite::class)->delete($site, $request->input()); + + return redirect()->route('sites', ['server' => $server]) + ->with('success', 'Site deleted successfully.'); + } +} diff --git a/app/Http/Controllers/WorkerController.php b/app/Http/Controllers/WorkerController.php index 2e3d89d0..e56ee897 100644 --- a/app/Http/Controllers/WorkerController.php +++ b/app/Http/Controllers/WorkerController.php @@ -39,7 +39,7 @@ public function index(Server $server): Response ]); } - #[Get('/sites/{site}/workers', name: 'sites.workers')] + #[Get('/sites/{site}/workers', name: 'workers.site')] public function site(Server $server, Site $site): Response { $this->authorize('viewAny', [Worker::class, $server, $site]); diff --git a/app/Http/Resources/CommandExecutionResource.php b/app/Http/Resources/CommandExecutionResource.php new file mode 100644 index 00000000..562895d9 --- /dev/null +++ b/app/Http/Resources/CommandExecutionResource.php @@ -0,0 +1,31 @@ + + */ + public function toArray(Request $request): array + { + return [ + 'id' => $this->id, + 'command_id' => $this->command_id, + 'server_id' => $this->server_id, + 'user_id' => $this->user_id, + 'server_log_id' => $this->server_log_id, + 'log' => ServerLogResource::make($this->serverLog), + 'variables' => $this->variables, + 'status' => $this->status, + 'status_color' => CommandExecution::$statusColors[$this->status] ?? 'gray', + 'created_at' => $this->created_at, + 'updated_at' => $this->updated_at, + ]; + } +} diff --git a/app/Http/Resources/CommandResource.php b/app/Http/Resources/CommandResource.php new file mode 100644 index 00000000..9656bb04 --- /dev/null +++ b/app/Http/Resources/CommandResource.php @@ -0,0 +1,28 @@ + + */ + public function toArray(Request $request): array + { + return [ + 'id' => $this->id, + 'server_id' => $this->site->server_id, + 'site_id' => $this->site_id, + 'name' => $this->name, + 'command' => $this->command, + 'variables' => $this->getVariables(), + 'updated_at' => $this->updated_at, + 'created_at' => $this->created_at, + ]; + } +} diff --git a/app/Http/Resources/DeploymentResource.php b/app/Http/Resources/DeploymentResource.php new file mode 100644 index 00000000..568454f4 --- /dev/null +++ b/app/Http/Resources/DeploymentResource.php @@ -0,0 +1,32 @@ + + */ + public function toArray(Request $request): array + { + return [ + 'id' => $this->id, + 'site_id' => $this->site_id, + 'deployment_script_id' => $this->deployment_script_id, + 'log_id' => $this->log_id, + 'log' => new ServerLogResource($this->log), + 'commit_id' => $this->commit_id, + 'commit_id_short' => $this->commit_id_short, + 'commit_data' => $this->commit_data, + 'status' => $this->status, + 'status_color' => Deployment::$statusColors[$this->status] ?? 'gray', + 'updated_at' => $this->updated_at, + 'created_at' => $this->created_at, + ]; + } +} diff --git a/app/Http/Resources/LoadBalancerServerResource.php b/app/Http/Resources/LoadBalancerServerResource.php new file mode 100644 index 00000000..154a77c4 --- /dev/null +++ b/app/Http/Resources/LoadBalancerServerResource.php @@ -0,0 +1,28 @@ + + */ + public function toArray(Request $request): array + { + return [ + 'id' => $this->id, + 'load_balancer_id' => $this->load_balancer_id, + 'ip' => $this->ip, + 'port' => $this->port, + 'weight' => $this->weight, + 'backup' => $this->backup, + 'created_at' => $this->created_at, + 'updated_at' => $this->updated_at, + ]; + } +} diff --git a/app/Http/Resources/RedirectResource.php b/app/Http/Resources/RedirectResource.php index d2b7b106..2ef8750e 100644 --- a/app/Http/Resources/RedirectResource.php +++ b/app/Http/Resources/RedirectResource.php @@ -16,11 +16,13 @@ public function toArray(Request $request): array { return [ 'id' => $this->id, + 'server_id' => $this->site->server_id, 'site_id' => $this->site_id, - 'mode' => $this->mode, 'from' => $this->from, 'to' => $this->to, + 'mode' => $this->mode, 'status' => $this->status, + 'status_color' => Redirect::$statusColors[$this->status] ?? 'gray', 'created_at' => $this->created_at, 'updated_at' => $this->updated_at, ]; diff --git a/app/Http/Resources/ServerResource.php b/app/Http/Resources/ServerResource.php index 93b49ed3..f3676525 100644 --- a/app/Http/Resources/ServerResource.php +++ b/app/Http/Resources/ServerResource.php @@ -17,6 +17,7 @@ public function toArray(Request $request): array return [ 'id' => $this->id, 'project_id' => $this->project_id, + 'services' => $this->services()->pluck('name', 'type'), 'user_id' => $this->user_id, 'provider_id' => $this->provider_id, 'name' => $this->name, diff --git a/app/Http/Resources/SiteResource.php b/app/Http/Resources/SiteResource.php index bded229b..c55e67fb 100644 --- a/app/Http/Resources/SiteResource.php +++ b/app/Http/Resources/SiteResource.php @@ -21,18 +21,22 @@ public function toArray(Request $request): array 'source_control_id' => $this->source_control_id, 'type' => $this->type, 'type_data' => $this->type_data, + 'features' => $this->type()->supportedFeatures(), 'domain' => $this->domain, 'aliases' => $this->aliases, 'web_directory' => $this->web_directory, + 'webserver' => $this->webserver()->name(), 'path' => $this->path, 'php_version' => $this->php_version, 'repository' => $this->repository, 'branch' => $this->branch, 'status' => $this->status, 'status_color' => Site::$statusColors[$this->status] ?? 'default', + 'auto_deploy' => $this->isAutoDeployment(), 'port' => $this->port, 'user' => $this->user, 'url' => $this->getUrl(), + 'force_ssl' => $this->force_ssl, 'progress' => $this->progress, 'created_at' => $this->created_at, 'updated_at' => $this->updated_at, diff --git a/app/Http/Resources/SslResource.php b/app/Http/Resources/SslResource.php new file mode 100644 index 00000000..fe624cc5 --- /dev/null +++ b/app/Http/Resources/SslResource.php @@ -0,0 +1,31 @@ + + */ + public function toArray(Request $request): array + { + return [ + 'id' => $this->id, + 'server_id' => $this->site->server_id, + 'site_id' => $this->site_id, + 'is_active' => $this->is_active, + 'type' => $this->type, + 'status' => $this->status, + 'log' => $this->log_id ? ServerLogResource::make($this->log) : null, + 'status_color' => Ssl::$statusColors[$this->status] ?? 'secondary', + 'expires_at' => $this->expires_at, + 'created_at' => $this->created_at, + 'updated_at' => $this->updated_at, + ]; + } +} diff --git a/app/Models/Command.php b/app/Models/Command.php index b28fabf5..b493a007 100644 --- a/app/Models/Command.php +++ b/app/Models/Command.php @@ -3,6 +3,7 @@ namespace App\Models; use Carbon\Carbon; +use Database\Factories\CommandFactory; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\HasMany; @@ -22,7 +23,7 @@ */ class Command extends AbstractModel { - /** @use HasFactory<\Database\Factories\CommandFactory> */ + /** @use HasFactory */ use HasFactory; protected $fillable = [ diff --git a/app/Models/CommandExecution.php b/app/Models/CommandExecution.php index e02ee7b7..720a3a2b 100644 --- a/app/Models/CommandExecution.php +++ b/app/Models/CommandExecution.php @@ -4,6 +4,7 @@ use App\Enums\CommandExecutionStatus; use Carbon\Carbon; +use Database\Factories\CommandExecutionFactory; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Relations\BelongsTo; @@ -18,13 +19,13 @@ * @property Carbon $created_at * @property Carbon $updated_at * @property Command $command - * @property ?ServerLog $serverLog + * @property ServerLog $serverLog * @property Server $server * @property ?User $user */ class CommandExecution extends AbstractModel { - /** @use HasFactory<\Database\Factories\CommandExecutionFactory> */ + /** @use HasFactory */ use HasFactory; protected $fillable = [ diff --git a/app/Models/Deployment.php b/app/Models/Deployment.php index bcdfcf90..04ede8cb 100755 --- a/app/Models/Deployment.php +++ b/app/Models/Deployment.php @@ -3,6 +3,7 @@ namespace App\Models; use App\Enums\DeploymentStatus; +use Database\Factories\DeploymentFactory; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Relations\BelongsTo; @@ -20,7 +21,7 @@ */ class Deployment extends AbstractModel { - /** @use HasFactory<\Database\Factories\DeploymentFactory> */ + /** @use HasFactory */ use HasFactory; protected $fillable = [ diff --git a/app/Models/DeploymentScript.php b/app/Models/DeploymentScript.php index 68fc18d6..263d47ea 100644 --- a/app/Models/DeploymentScript.php +++ b/app/Models/DeploymentScript.php @@ -2,6 +2,7 @@ namespace App\Models; +use Database\Factories\DeploymentScriptFactory; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Relations\BelongsTo; @@ -13,7 +14,7 @@ */ class DeploymentScript extends AbstractModel { - /** @use HasFactory<\Database\Factories\DeploymentScriptFactory> */ + /** @use HasFactory */ use HasFactory; protected static function boot(): void diff --git a/app/Models/LoadBalancerServer.php b/app/Models/LoadBalancerServer.php index 1a999534..b2427abb 100644 --- a/app/Models/LoadBalancerServer.php +++ b/app/Models/LoadBalancerServer.php @@ -2,6 +2,7 @@ namespace App\Models; +use Database\Factories\LoadBalancerServerFactory; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Relations\BelongsTo; @@ -15,7 +16,7 @@ */ class LoadBalancerServer extends AbstractModel { - /** @use HasFactory<\Database\Factories\LoadBalancerServerFactory> */ + /** @use HasFactory */ use HasFactory; protected $fillable = [ diff --git a/app/Models/Ssl.php b/app/Models/Ssl.php index f09c7eb9..87776ae4 100644 --- a/app/Models/Ssl.php +++ b/app/Models/Ssl.php @@ -4,6 +4,7 @@ use App\Enums\SslStatus; use Carbon\Carbon; +use Database\Factories\SslFactory; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Support\Str; @@ -28,7 +29,7 @@ */ class Ssl extends AbstractModel { - /** @use HasFactory<\Database\Factories\SslFactory> */ + /** @use HasFactory */ use HasFactory; protected $fillable = [ diff --git a/app/Policies/CommandPolicy.php b/app/Policies/CommandPolicy.php index f806e34e..01d6f491 100644 --- a/app/Policies/CommandPolicy.php +++ b/app/Policies/CommandPolicy.php @@ -2,7 +2,6 @@ namespace App\Policies; -use App\Enums\SiteFeature; use App\Models\Command; use App\Models\Server; use App\Models\Site; @@ -17,7 +16,6 @@ public function viewAny(User $user, Site $site, Server $server): bool { return ($user->isAdmin() || $server->project->users->contains($user)) && $server->isReady() && - $site->hasFeature(SiteFeature::COMMANDS) && $site->isReady(); } @@ -27,7 +25,6 @@ public function view(User $user, Command $command, Site $site, Server $server): $site->server_id === $server->id && $server->isReady() && $site->isReady() && - $site->hasFeature(SiteFeature::COMMANDS) && $command->site_id === $site->id; } @@ -35,7 +32,6 @@ public function create(User $user, Site $site, Server $server): bool { return ($user->isAdmin() || $server->project->users->contains($user)) && $server->isReady() && - $site->hasFeature(SiteFeature::COMMANDS) && $site->isReady(); } @@ -45,7 +41,6 @@ public function update(User $user, Command $command, Site $site, Server $server) $site->server_id === $server->id && $server->isReady() && $site->isReady() && - $site->hasFeature(SiteFeature::COMMANDS) && $command->site_id === $site->id; } @@ -55,7 +50,6 @@ public function delete(User $user, Command $command, Site $site, Server $server) $site->server_id === $server->id && $server->isReady() && $site->isReady() && - $site->hasFeature(SiteFeature::COMMANDS) && $command->site_id === $site->id; } } diff --git a/app/Policies/RedirectPolicy.php b/app/Policies/RedirectPolicy.php index f732f275..6b43da48 100644 --- a/app/Policies/RedirectPolicy.php +++ b/app/Policies/RedirectPolicy.php @@ -2,34 +2,40 @@ namespace App\Policies; +use App\Models\Redirect; use App\Models\Server; use App\Models\Site; use App\Models\User; class RedirectPolicy { - public function view(User $user, Site $site, Server $server): bool + public function viewAny(User $user, Site $site, Server $server): bool { - if ($user->isAdmin()) { - return true; - } + return ($user->isAdmin() || $server->project->users->contains($user)) && + $server->isReady() && + $site->isReady(); + } - return $site->server->project->users->contains($user); + public function view(User $user, Redirect $redirect, Site $site, Server $server): bool + { + return ($user->isAdmin() || $site->server->project->users->contains($user)) + && $site->server_id === $server->id + && $site->server->isReady() + && $redirect->site_id === $site->id; } public function create(User $user, Site $site, Server $server): bool { return ($user->isAdmin() || $site->server->project->users->contains($user)) && $site->server_id === $server->id - && $site->server->isReady() - && $site->server->webserver(); + && $site->server->isReady(); } - public function delete(User $user, Site $site, Server $server): bool + public function delete(User $user, Redirect $redirect, Site $site, Server $server): bool { return ($user->isAdmin() || $site->server->project->users->contains($user)) && $site->server_id === $server->id - && $site->server->isReady() - && $site->server->webserver(); + && $server->isReady() + && $redirect->site_id === $site->id; } } diff --git a/app/Policies/SslPolicy.php b/app/Policies/SslPolicy.php index 8f0226b1..15ba69ed 100644 --- a/app/Policies/SslPolicy.php +++ b/app/Policies/SslPolicy.php @@ -2,7 +2,6 @@ namespace App\Policies; -use App\Enums\SiteFeature; use App\Models\Server; use App\Models\Site; use App\Models\Ssl; @@ -17,7 +16,6 @@ public function viewAny(User $user, Site $site, Server $server): bool { return ($user->isAdmin() || $server->project->users->contains($user)) && $server->isReady() && - $site->hasFeature(SiteFeature::SSL) && $site->isReady(); } @@ -27,7 +25,6 @@ public function view(User $user, Ssl $ssl, Site $site, Server $server): bool $site->server_id === $server->id && $server->isReady() && $site->isReady() && - $site->hasFeature(SiteFeature::SSL) && $ssl->site_id === $site->id; } @@ -35,18 +32,16 @@ public function create(User $user, Site $site, Server $server): bool { return ($user->isAdmin() || $server->project->users->contains($user)) && $server->isReady() && - $site->hasFeature(SiteFeature::SSL) && $site->isReady(); } public function update(User $user, Ssl $ssl, Site $site, Server $server): bool { return ($user->isAdmin() || $server->project->users->contains($user)) && - $site->server_id === $server->id && - $server->isReady() && - $site->isReady() && - $site->hasFeature(SiteFeature::SSL) && - $ssl->site_id === $site->id; + $site->server_id === $server->id && + $server->isReady() && + $site->isReady() && + $ssl->site_id === $site->id; } public function delete(User $user, Ssl $ssl, Site $site, Server $server): bool @@ -55,7 +50,6 @@ public function delete(User $user, Ssl $ssl, Site $site, Server $server): bool $site->server_id === $server->id && $server->isReady() && $site->isReady() && - $site->hasFeature(SiteFeature::SSL) && $ssl->site_id === $site->id; } } diff --git a/app/Policies/WorkerPolicy.php b/app/Policies/WorkerPolicy.php index 54fe4ccf..00394321 100644 --- a/app/Policies/WorkerPolicy.php +++ b/app/Policies/WorkerPolicy.php @@ -2,7 +2,6 @@ namespace App\Policies; -use App\Enums\SiteFeature; use App\Models\Server; use App\Models\Site; use App\Models\User; @@ -17,70 +16,37 @@ public function viewAny(User $user, Server $server, ?Site $site = null): bool { return ($user->isAdmin() || $server->project->users->contains($user)) && $server->isReady() && - ( - ! $site instanceof Site || - ( - $site->hasFeature(SiteFeature::WORKERS) && - $site->isReady() - ) - ); + $server->processManager(); } public function view(User $user, Worker $worker, Server $server, ?Site $site = null): bool { return ($user->isAdmin() || $server->project->users->contains($user)) && $server->isReady() && - ( - ! $site instanceof Site || - ( - $site->server_id === $server->id && - $site->hasFeature(SiteFeature::WORKERS) && - $site->isReady() && - $worker->site_id === $site->id - ) - ); + $worker->server_id === $server->id && + $server->processManager(); } public function create(User $user, Server $server, ?Site $site = null): bool { return ($user->isAdmin() || $server->project->users->contains($user)) && $server->isReady() && - ( - ! $site instanceof Site || - ( - $site->hasFeature(SiteFeature::WORKERS) && - $site->isReady() - ) - ); + $server->processManager(); } public function update(User $user, Worker $worker, Server $server, ?Site $site = null): bool { return ($user->isAdmin() || $server->project->users->contains($user)) && $server->isReady() && - ( - ! $site instanceof Site || - ( - $site->server_id === $server->id && - $site->hasFeature(SiteFeature::WORKERS) && - $site->isReady() && - $worker->site_id === $site->id - ) - ); + $worker->server_id === $server->id && + $server->processManager(); } public function delete(User $user, Worker $worker, Server $server, ?Site $site = null): bool { return ($user->isAdmin() || $server->project->users->contains($user)) && $server->isReady() && - ( - ! $site instanceof Site || - ( - $site->server_id === $server->id && - $site->hasFeature(SiteFeature::WORKERS) && - $site->isReady() && - $worker->site_id === $site->id - ) - ); + $worker->server_id === $server->id && + $server->processManager(); } } diff --git a/app/SSH/Services/Webserver/Caddy.php b/app/SSH/Services/Webserver/Caddy.php index e8ea0285..dbb2917d 100755 --- a/app/SSH/Services/Webserver/Caddy.php +++ b/app/SSH/Services/Webserver/Caddy.php @@ -10,6 +10,11 @@ class Caddy extends AbstractWebserver { + public function name(): string + { + return \App\Enums\Webserver::CADDY; + } + /** * @throws SSHError */ diff --git a/app/SSH/Services/Webserver/Nginx.php b/app/SSH/Services/Webserver/Nginx.php index 4904924e..44009f6f 100755 --- a/app/SSH/Services/Webserver/Nginx.php +++ b/app/SSH/Services/Webserver/Nginx.php @@ -10,6 +10,11 @@ class Nginx extends AbstractWebserver { + public function name(): string + { + return \App\Enums\Webserver::NGINX; + } + /** * @throws SSHError */ diff --git a/app/SSH/Services/Webserver/Webserver.php b/app/SSH/Services/Webserver/Webserver.php index 9e9722a4..0c161c9b 100755 --- a/app/SSH/Services/Webserver/Webserver.php +++ b/app/SSH/Services/Webserver/Webserver.php @@ -8,6 +8,8 @@ interface Webserver extends ServiceInterface { + public function name(): string; + public function createVHost(Site $site): void; public function updateVHost(Site $site, ?string $vhost = null): void; diff --git a/resources/js/components/app-sidebar-nested.tsx b/resources/js/components/app-sidebar-nested.tsx index 160a7731..a20c92c1 100644 --- a/resources/js/components/app-sidebar-nested.tsx +++ b/resources/js/components/app-sidebar-nested.tsx @@ -103,7 +103,7 @@ export function AppSidebar() { }, { title: 'Application', - href: route('sites.show', { server: page.props.server?.id || 0, site: page.props.site?.id || 0 }), + href: route('application', { server: page.props.server?.id || 0, site: page.props.site?.id || 0 }), icon: RocketIcon, }, ] diff --git a/resources/js/components/app-sidebar.tsx b/resources/js/components/app-sidebar.tsx index 13029317..d7178929 100644 --- a/resources/js/components/app-sidebar.tsx +++ b/resources/js/components/app-sidebar.tsx @@ -129,7 +129,7 @@ export function AppSidebar({ secondNavItems, secondNavTitle }: { secondNavItems? - +