Compare commits

...

138 Commits

Author SHA1 Message Date
Andras Bacsai
ba85e3bc8b Merge pull request #1532 from coollabsio/next
v4.0.0-beta.155
2023-12-11 14:47:59 +01:00
Andras Bacsai
1fd12832ca refactor: application status changed realtime 2023-12-11 13:43:16 +01:00
Andras Bacsai
a8807b8d09 Merge pull request #1530 from stooit/feat/docker-compose-prebuild
feat: Build images prior to rollout in docker compose buildpack.
2023-12-11 13:03:59 +01:00
Andras Bacsai
dbc55233cb fix: add new destination 2023-12-11 13:02:29 +01:00
Andras Bacsai
af6d94c0d8 fix: realtime check 2023-12-11 12:40:56 +01:00
Andras Bacsai
e022492770 fix: realtime connection popup could be disabled 2023-12-11 12:03:32 +01:00
Andras Bacsai
5e03979f9c no cleanup 2023-12-11 11:29:59 +01:00
Andras Bacsai
956416b522 refactor: service logs are now on one page 2023-12-11 11:27:41 +01:00
Andras Bacsai
2846e049fa fix: ui 2023-12-11 10:29:03 +01:00
Andras Bacsai
3ffd3fc819 fix: channels
feat: database backup is realtime now
2023-12-11 10:23:10 +01:00
Andras Bacsai
63dff5961e fix 2023-12-11 09:41:31 +01:00
Andras Bacsai
4b024017ad fix: live mode for github webhooks 2023-12-11 09:36:21 +01:00
Andras Bacsai
720bb8c478 fix: database ui is realtime based 2023-12-11 09:02:53 +01:00
Andras Bacsai
771dc30b81 fix: do not send telegram noti on intent payment failed 2023-12-11 08:32:42 +01:00
Andras Bacsai
6f97af096d Merge pull request #1526 from stooit/feat/application-run-command
fix: Escape container commands
2023-12-11 08:23:29 +01:00
Andras Bacsai
3d28669cad fix: boarding view 2023-12-10 15:57:46 +01:00
Stuart Rowlands
efe043ec9d Build images prior to rollout. 2023-12-09 19:14:06 -08:00
Andras Bacsai
6bb79e10bc fix: double ws connection 2023-12-08 22:51:42 +01:00
Andras Bacsai
ba2e4c06f1 fix 2023-12-08 21:37:20 +01:00
Andras Bacsai
1bfb9637ba hmm 2023-12-08 21:12:22 +01:00
Stuart Rowlands
d7e9821582 Resolve merge conflicts. 2023-12-08 12:07:27 -08:00
Stuart Rowlands
08585f7e9a Merge branch 'next' into feat/application-run-command 2023-12-08 12:06:09 -08:00
Andras Bacsai
3eedb43b66 fix 2023-12-08 21:04:27 +01:00
Andras Bacsai
26aca6a4c0 remove polling 2023-12-08 20:13:17 +01:00
Andras Bacsai
3cbf8c281d remove polling 2023-12-08 20:12:54 +01:00
Andras Bacsai
d931d57373 fix github change 2023-12-08 19:57:55 +01:00
Andras Bacsai
4e680deb93 fix: service deletion job 2023-12-08 18:32:08 +01:00
Andras Bacsai
6a6275d4fa revert 2023-12-08 17:08:02 +01:00
Andras Bacsai
efd9087b74 fix buildpack form 2023-12-08 15:27:11 +01:00
Andras Bacsai
6882ce8d0f fix: service start + event 2023-12-08 15:12:08 +01:00
Andras Bacsai
eccd41217f fix network name 2023-12-08 15:07:50 +01:00
Andras Bacsai
538de9bd81 rename soketi container to realtime 2023-12-08 14:57:11 +01:00
Andras Bacsai
a249ee1b1f fix: live event 2023-12-08 13:55:55 +01:00
Andras Bacsai
828fec9448 fix: do not create duplicated networks 2023-12-08 13:24:28 +01:00
Andras Bacsai
205995cabe stop status event 2023-12-08 13:07:42 +01:00
Andras Bacsai
4071e096bc hmm 2023-12-08 12:48:21 +01:00
Andras Bacsai
14bac0f0d7 hmm 2023-12-08 12:40:32 +01:00
Andras Bacsai
69c124032c fix 2023-12-08 12:23:12 +01:00
Andras Bacsai
b55bd298f2 fix: service navbar using new realtime events 2023-12-08 12:12:44 +01:00
Andras Bacsai
86c2415210 ui fix 2023-12-08 09:15:32 +01:00
Andras Bacsai
82f3d54bc3 ui fixes 2023-12-08 00:16:22 +01:00
Andras Bacsai
a6e76dfabc custom progress bar colro 2023-12-07 23:31:03 +01:00
Andras Bacsai
f934dfef33 wip livewire migration 2023-12-07 22:56:55 +01:00
Stuart Rowlands
19c66c6628 Escape command in ExecuteContainerCommand. 2023-12-07 11:05:52 -08:00
Stuart Rowlands
d7d948caf6 Merge branch 'main' into feat/application-run-command 2023-12-07 10:48:19 -08:00
Stuart Rowlands
1b34337fe8 Wrap command run to support quotes & chaining. 2023-12-07 10:45:11 -08:00
Andras Bacsai
718603e37e wip: migrate to livewire 3 2023-12-07 19:06:32 +01:00
Andras Bacsai
7ffebd71f3 Merge pull request #1518 from coollabsio/next
v4.0.0-beta.154
2023-12-07 14:04:03 +01:00
Andras Bacsai
2f286a6595 fix: container selection 2023-12-07 13:48:23 +01:00
Andras Bacsai
13701f6030 ui: env vars 2023-12-07 13:31:06 +01:00
Andras Bacsai
91f224ddea Merge pull request #1461 from DanHulton/feature/better_env_vars
Friendlier env vars page.
2023-12-07 13:29:41 +01:00
Andras Bacsai
8cffae10b2 rename things 2023-12-07 13:28:11 +01:00
Andras Bacsai
1158b2f4db feat: execute command in container 2023-12-07 13:07:16 +01:00
Andras Bacsai
f542bcf428 Merge pull request #1524 from stooit/feat/application-run-command
Add support for command execution in containers.
2023-12-07 11:29:32 +01:00
Stuart Rowlands
22178df8ae Add support for command execution in containers. 2023-12-06 15:42:14 -08:00
Andras Bacsai
acfe1daf9b fix: switching to static build 2023-12-06 21:32:23 +01:00
Andras Bacsai
fb3f71881f fix: use hc port 80 in case of static build 2023-12-06 21:09:41 +01:00
Andras Bacsai
0f7546a4dc add error message if realtime service is not reachable 2023-12-06 18:10:49 +01:00
Andras Bacsai
6ccafacc87 very important commit 2023-12-06 17:02:27 +01:00
Andras Bacsai
852c2df12e mono 2023-12-06 17:02:07 +01:00
Andras Bacsai
4c752951ab asd 2023-12-06 17:00:05 +01:00
Andras Bacsai
f32191b889 fix modal 2023-12-06 16:59:52 +01:00
Andras Bacsai
31251ef6cb nothing to see here 2023-12-06 16:38:46 +01:00
Andras Bacsai
e9365aa09b update js 2023-12-06 16:34:34 +01:00
Andras Bacsai
e5c860319f modal fix 2023-12-06 16:32:40 +01:00
Andras Bacsai
ceedd5225f modal fix? 2023-12-06 15:50:13 +01:00
Andras Bacsai
61efdfb7c1 revert 2023-12-06 15:30:04 +01:00
Andras Bacsai
fcef5d902e hmm 2023-12-06 15:04:07 +01:00
Andras Bacsai
2073b8949b lets see now 2023-12-06 14:57:03 +01:00
Andras Bacsai
5c59a752e3 test modal closing problem 2023-12-06 14:47:29 +01:00
Andras Bacsai
caf6e3a23e fix 2023-12-06 14:23:18 +01:00
Andras Bacsai
8eb1c4da46 fix prod compose 2023-12-06 14:22:19 +01:00
Andras Bacsai
bab2f391ed fix 2023-12-06 14:21:30 +01:00
Andras Bacsai
131c6df82a fix 2023-12-06 14:11:02 +01:00
Andras Bacsai
9f44d0c47a test 2023-12-06 13:50:27 +01:00
Andras Bacsai
dff7ed5b7b fix: bind volumes for compose bp 2023-12-06 13:45:43 +01:00
Andras Bacsai
54b6472f3b update packages 2023-12-06 13:32:20 +01:00
Andras Bacsai
04a36e5c90 update packages 2023-12-06 13:29:10 +01:00
Andras Bacsai
b2f851272b updates 2023-12-06 12:57:50 +01:00
Andras Bacsai
778915599f fix 2023-12-06 12:28:05 +01:00
Andras Bacsai
a6f9527157 fix 2023-12-06 12:20:20 +01:00
Andras Bacsai
db976709c7 add local js for pusher/echo 2023-12-06 10:32:49 +01:00
Andras Bacsai
7e4947ba07 setup test event 2023-12-06 10:25:23 +01:00
Andras Bacsai
e2578a7dd0 fix: deploy the right compose file 2023-12-06 09:36:11 +01:00
Andras Bacsai
7d028b15f5 wip 2023-12-05 15:26:05 +01:00
Andras Bacsai
d240bfda8b wip 2023-12-05 15:20:47 +01:00
Andras Bacsai
d59ec2548a wip 2023-12-05 15:19:54 +01:00
Andras Bacsai
e0cefc787a fix: add hc for soketi 2023-12-05 15:15:01 +01:00
Andras Bacsai
0496198d0f wip 2023-12-05 15:09:46 +01:00
Andras Bacsai
cbf7f1fa41 wip 2023-12-05 14:56:00 +01:00
Andras Bacsai
66587d864a wip 2023-12-05 14:52:19 +01:00
Andras Bacsai
b106705c8d wip 2023-12-05 14:18:44 +01:00
Andras Bacsai
5cc22a8271 wip: 🌮 2023-12-05 13:56:11 +01:00
Andras Bacsai
cd22863a8c wip 2023-12-05 12:31:07 +01:00
Andras Bacsai
118a02f70d wip 2023-12-05 12:18:23 +01:00
Andras Bacsai
862177d61a wip 2023-12-05 12:16:48 +01:00
Andras Bacsai
41280aa780 wip 2023-12-05 12:13:57 +01:00
Andras Bacsai
3a987c8a6e wip 2023-12-05 11:47:40 +01:00
Andras Bacsai
29ca461a9a wip 2023-12-05 11:35:05 +01:00
Andras Bacsai
d48b72c160 wip 2023-12-05 11:24:23 +01:00
Andras Bacsai
ce49f26c53 wip 2023-12-05 11:17:52 +01:00
Andras Bacsai
02989678be revert a few more things 2023-12-04 21:48:10 +01:00
Andras Bacsai
810b55163c revert: wip 2023-12-04 21:37:30 +01:00
Andras Bacsai
ac13ba0957 wip 2023-12-04 21:13:46 +01:00
Andras Bacsai
a3e088199f wip 2023-12-04 21:06:02 +01:00
Andras Bacsai
42ee4ca032 wip: broadcast 2023-12-04 20:47:32 +01:00
Andras Bacsai
17deff4d86 wip 2023-12-04 15:42:08 +01:00
Andras Bacsai
8b6323b906 refactor applicationdeploymentjob 2023-12-04 15:08:24 +01:00
Andras Bacsai
a696b5271a Update release version to 4.0.0-beta.154 2023-12-04 12:13:21 +01:00
Andras Bacsai
31959f26d4 Merge pull request #1517 from coollabsio/next
v4.0.0-beta.153
2023-12-04 11:42:21 +01:00
Andras Bacsai
45d2f80f69 fix: missing docker image thing 2023-12-04 11:41:44 +01:00
Andras Bacsai
53975fcf61 Merge pull request #1516 from coollabsio/next
v4.0.0-beta.152
2023-12-04 11:26:17 +01:00
Andras Bacsai
76296c1f19 fix: prevent autorefresh of proxy status 2023-12-04 11:25:24 +01:00
Andras Bacsai
c25baf69e1 fix: workdir issue for basedir
fix: remove / mount on helpers image
2023-12-04 11:20:50 +01:00
Andras Bacsai
f952512615 fix: add cf tunnel to boarding server view 2023-12-04 09:29:55 +01:00
Andras Bacsai
c6557eada8 service: meilisearch 2023-12-03 12:16:33 +01:00
Andras Bacsai
2c2d74c0d6 Update release version to 4.0.0-beta.152 2023-12-01 22:16:48 +01:00
Andras Bacsai
028a2eb275 Fix Docker compose build command and remove debug statements 2023-12-01 22:16:27 +01:00
Andras Bacsai
ce7fad5bef Merge pull request #1511 from coollabsio/next
fix: use official install script with rancher (one will work for sure)
2023-12-01 14:02:30 +01:00
Andras Bacsai
cd7852e4f9 fix: use official install script with rancher (one will work for sure) 2023-12-01 14:02:11 +01:00
Andras Bacsai
7b022a2482 Merge pull request #1510 from coollabsio/next
v4.0.0-beta.151
2023-12-01 13:03:42 +01:00
Andras Bacsai
12d9b6538b Fix environment variable parsing in Docker Compose file 2023-12-01 12:34:23 +01:00
Andras Bacsai
335788c2d6 fix: default value do not overwrite existing env value 2023-12-01 12:14:23 +01:00
Andras Bacsai
2352e4a71d Fix directory creation inApplicationDeploymentJob.php 2023-12-01 12:13:55 +01:00
Andras Bacsai
dc03179bd1 feat: auto-restart tcp proxies for databases 2023-12-01 11:37:00 +01:00
Andras Bacsai
cc72f416e8 feat: custom log drain endpoints 2023-12-01 11:13:58 +01:00
Andras Bacsai
3b67d0a8de feat: save timestamp configuration for logs 2023-12-01 10:34:30 +01:00
Andras Bacsai
0135ba7e89 Delete docker-compose.prod.standalone.yml 2023-11-30 13:38:52 +01:00
Andras Bacsai
a28a28cd23 Add docker-compose.prod.standalone.yml
configuration file
2023-11-30 13:17:43 +01:00
Andras Bacsai
b52680a2d8 Fix dispatch_sync issue in ContainerStatusJob 2023-11-30 12:55:31 +01:00
Andras Bacsai
0670e6c1d6 fix: server view for link() 2023-11-30 12:21:53 +01:00
Andras Bacsai
c3882b75c1 Update release version to 4.0.0-beta.151 2023-11-30 12:21:42 +01:00
Andras Bacsai
64b6f86a36 Update PostgreSQL image version to 16-alpine for services 2023-11-30 12:21:21 +01:00
Andras Bacsai
b9efc22253 Merge pull request #1504 from coollabsio/next
v4.0.0-beta.150 - quick fix
2023-11-29 18:44:25 +01:00
Andras Bacsai
e3d9eb0154 Add hidden flag to docker compose command 2023-11-29 18:43:02 +01:00
Andras Bacsai
66f3967479 Merge pull request #1503 from coollabsio/next
v4.0.0-beta.150
2023-11-29 18:41:41 +01:00
Andras Bacsai
c54439e84c fix: dockercompose save ./ volumes under /data/coolify 2023-11-29 18:40:41 +01:00
Dan Hulton
ccb972dcb9 Show row-based env var display at lg, not xl. Add border for col-based env var display. 2023-11-16 21:49:51 -05:00
363 changed files with 4704 additions and 2316 deletions

View File

@@ -4,3 +4,7 @@ APP_KEY=
DB_PASSWORD=
REDIS_PASSWORD=
PUSHER_APP_ID=
PUSHER_APP_KEY=
PUSHER_APP_SECRET=

View File

@@ -39,7 +39,7 @@ class PrepareCoolifyTask
public function __invoke(): Activity
{
$job = new CoolifyTask($this->activity, ignore_errors: $this->remoteProcessArgs->ignore_errors);
$job = new CoolifyTask($this->activity, ignore_errors: $this->remoteProcessArgs->ignore_errors, call_event_on_finish: $this->remoteProcessArgs->call_event_on_finish);
dispatch($job);
$this->activity->refresh();
return $this->activity;

View File

@@ -17,24 +17,24 @@ class RunRemoteProcess
public bool $hide_from_output;
public bool $is_finished;
public bool $ignore_errors;
public $call_event_on_finish = null;
protected $time_start;
protected $current_time;
protected $last_write_at = 0;
protected $throttle_interval_ms = 500;
protected $throttle_interval_ms = 200;
protected int $counter = 1;
/**
* Create a new job instance.
*/
public function __construct(Activity $activity, bool $hide_from_output = false, bool $is_finished = false, bool $ignore_errors = false)
public function __construct(Activity $activity, bool $hide_from_output = false, bool $ignore_errors = false, $call_event_on_finish = null)
{
if ($activity->getExtraProperty('type') !== ActivityTypes::INLINE->value) {
@@ -43,8 +43,8 @@ class RunRemoteProcess
$this->activity = $activity;
$this->hide_from_output = $hide_from_output;
$this->is_finished = $is_finished;
$this->ignore_errors = $ignore_errors;
$this->call_event_on_finish = $call_event_on_finish;
}
public static function decodeOutput(?Activity $activity = null): string
@@ -74,17 +74,29 @@ class RunRemoteProcess
$this->time_start = hrtime(true);
$status = ProcessStatus::IN_PROGRESS;
$processResult = Process::forever()->run($this->getCommand(), $this->handleOutput(...));
$timeout = config('constants.ssh.command_timeout');
$process = Process::timeout($timeout)->start($this->getCommand(), $this->handleOutput(...));
$this->activity->properties = $this->activity->properties->merge([
'process_id' => $process->id(),
]);
$processResult = $process->wait();
// $processResult = Process::timeout($timeout)->run($this->getCommand(), $this->handleOutput(...));
if ($this->activity->properties->get('status') === ProcessStatus::ERROR->value) {
$status = ProcessStatus::ERROR;
} else {
if (($processResult->exitCode() == 0 && $this->is_finished) || $this->activity->properties->get('status') === ProcessStatus::FINISHED->value) {
if ($processResult->exitCode() == 0) {
$status = ProcessStatus::FINISHED;
}
if ($processResult->exitCode() != 0 && !$this->ignore_errors) {
$status = ProcessStatus::ERROR;
}
// if (($processResult->exitCode() == 0 && $this->is_finished) || $this->activity->properties->get('status') === ProcessStatus::FINISHED->value) {
// $status = ProcessStatus::FINISHED;
// }
// if ($processResult->exitCode() != 0 && !$this->ignore_errors) {
// $status = ProcessStatus::ERROR;
// }
}
$this->activity->properties = $this->activity->properties->merge([
@@ -97,7 +109,15 @@ class RunRemoteProcess
if ($processResult->exitCode() != 0 && !$this->ignore_errors) {
throw new \RuntimeException($processResult->errorOutput(), $processResult->exitCode());
}
if ($this->call_event_on_finish) {
try {
event(resolve("App\\Events\\$this->call_event_on_finish", [
'userId' => $this->activity->causer_id,
]));
} catch (\Throwable $e) {
ray($e);
}
}
return $processResult;
}
@@ -117,7 +137,6 @@ class RunRemoteProcess
}
$this->current_time = $this->elapsedTime();
$this->activity->description = $this->encodeOutput($type, $output);
if ($this->isAfterLastThrottle()) {
// Let's write to database.
DB::transaction(function () {

View File

@@ -105,7 +105,7 @@ class StartMariadb
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml pull";
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml up -d";
$this->commands[] = "echo '{$database->name} started.'";
return remote_process($this->commands, $database->destination->server);
return remote_process($this->commands, $database->destination->server, callEventOnFinish: 'DatabaseStatusChanged');
}
private function generate_local_persistent_volumes()

View File

@@ -121,7 +121,7 @@ class StartMongodb
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml pull";
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml up -d";
$this->commands[] = "echo '{$database->name} started.'";
return remote_process($this->commands, $database->destination->server);
return remote_process($this->commands, $database->destination->server,callEventOnFinish: 'DatabaseStatusChanged');
}
private function generate_local_persistent_volumes()

View File

@@ -105,7 +105,7 @@ class StartMysql
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml pull";
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml up -d";
$this->commands[] = "echo '{$database->name} started.'";
return remote_process($this->commands, $database->destination->server);
return remote_process($this->commands, $database->destination->server,callEventOnFinish: 'DatabaseStatusChanged');
}
private function generate_local_persistent_volumes()

View File

@@ -131,7 +131,7 @@ class StartPostgresql
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml pull";
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml up -d";
$this->commands[] = "echo '{$database->name} started.'";
return remote_process($this->commands, $database->destination->server);
return remote_process($this->commands, $database->destination->server, callEventOnFinish: 'DatabaseStatusChanged');
}
private function generate_local_persistent_volumes()

View File

@@ -115,7 +115,7 @@ class StartRedis
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml pull";
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml up -d";
$this->commands[] = "echo '{$database->name} started.'";
return remote_process($this->commands, $database->destination->server);
return remote_process($this->commands, $database->destination->server, callEventOnFinish: 'DatabaseStatusChanged');
}
private function generate_local_persistent_volumes()

View File

@@ -2,6 +2,7 @@
namespace App\Actions\Database;
use App\Events\DatabaseStatusChanged;
use App\Models\StandaloneMariadb;
use App\Models\StandaloneMongodb;
use App\Models\StandaloneMysql;

View File

@@ -67,7 +67,7 @@ class InstallDocker
}
$command = $command->merge([
"echo 'Installing Docker Engine...'",
"curl https://releases.rancher.com/install-docker/{$dockerVersion}.sh | sh",
"curl https://releases.rancher.com/install-docker/{$dockerVersion}.sh | sh || curl https://get.docker.com | sh -s -- --version {$dockerVersion}",
"echo 'Configuring Docker Engine (merging existing configuration with the required)...'",
"test -s /etc/docker/daemon.json && cp /etc/docker/daemon.json \"/etc/docker/daemon.json.original-`date +\"%Y%m%d-%H%M%S\"`\" || echo '{$config}' | base64 -d > /etc/docker/daemon.json",
"echo '{$config}' | base64 -d > /etc/docker/daemon.json.coolify",

View File

@@ -16,6 +16,8 @@ class InstallLogDrain
$type = 'highlight';
} else if ($server->settings->is_logdrain_axiom_enabled) {
$type = 'axiom';
} else if ($server->settings->is_logdrain_custom_enabled) {
$type = 'custom';
} else {
$type = 'none';
}
@@ -114,15 +116,23 @@ class InstallLogDrain
json_date_format iso8601
tls On
");
} else if ($type === 'custom') {
if (!$server->settings->is_logdrain_custom_enabled) {
throw new \Exception('Custom log drain is not enabled.');
}
$config = base64_encode($server->settings->logdrain_custom_config);
$parsers = base64_encode($server->settings->logdrain_custom_config_parser);
} else {
throw new \Exception('Unknown log drain type.');
}
$parsers = base64_encode("
if ($type !== 'custom') {
$parsers = base64_encode("
[PARSER]
Name empty_line_skipper
Format regex
Regex /^(?!\s*$).+/
Name empty_line_skipper
Format regex
Regex /^(?!\s*$).+/
");
}
$compose = base64_encode("
services:
coolify-log-drain:
@@ -179,6 +189,12 @@ Files:
"echo AXIOM_DATASET_NAME={$server->settings->logdrain_axiom_dataset_name} >> $config_path/.env",
"echo AXIOM_API_KEY={$server->settings->logdrain_axiom_api_key} >> $config_path/.env",
];
} else if ($type === 'custom') {
$add_envs_command = [
"touch $config_path/.env"
];
} else {
throw new \Exception('Unknown log drain type.');
}
$restart_command = [
"echo 'Stopping old Fluent Bit'",

View File

@@ -0,0 +1,42 @@
<?php
namespace App\Actions\Service;
use Lorisleiva\Actions\Concerns\AsAction;
use App\Models\Service;
class DeleteService
{
use AsAction;
public function handle(Service $service)
{
StopService::run($service);
$server = data_get($service, 'server');
$storagesToDelete = collect([]);
$service->environment_variables()->delete();
$commands = [];
foreach ($service->applications()->get() as $application) {
$storages = $application->persistentStorages()->get();
foreach ($storages as $storage) {
$storagesToDelete->push($storage);
}
$application->delete();
}
foreach ($service->databases()->get() as $database) {
$storages = $database->persistentStorages()->get();
foreach ($storages as $storage) {
$storagesToDelete->push($storage);
}
$database->delete();
}
foreach ($storagesToDelete as $storage) {
$commands[] = "docker volume rm -f $storage->name";
}
$commands[] = "docker rm -f $service->uuid";
instant_remote_process($commands, $server, false);
$service->forceDelete();
}
}

View File

@@ -16,19 +16,19 @@ class StartService
$commands[] = "cd " . $service->workdir();
$commands[] = "echo 'Saved configuration files to {$service->workdir()}.'";
$commands[] = "echo 'Creating Docker network.'";
$commands[] = "docker network create --attachable '{$service->uuid}' >/dev/null 2>&1 || true";
$commands[] = "echo 'Starting service {$service->name} on {$service->server->name}.'";
$commands[] = "docker network inspect $service->uuid >/dev/null 2>&1 || docker network create --attachable $service->uuid >/dev/null 2>&1 || true";
$commands[] = "echo 'Starting service $service->name on {$service->server->name}.'";
$commands[] = "echo 'Pulling images.'";
$commands[] = "docker compose pull";
$commands[] = "echo 'Starting containers.'";
$commands[] = "docker compose up -d --remove-orphans --force-recreate";
$commands[] = "docker compose up -d --remove-orphans --force-recreate --build";
$commands[] = "docker network connect $service->uuid coolify-proxy >/dev/null 2>&1 || true";
$compose = data_get($service,'docker_compose',[]);
$serviceNames = data_get(Yaml::parse($compose),'services',[]);
foreach($serviceNames as $serviceName => $serviceConfig){
$compose = data_get($service, 'docker_compose', []);
$serviceNames = data_get(Yaml::parse($compose), 'services', []);
foreach ($serviceNames as $serviceName => $serviceConfig) {
$commands[] = "docker network connect --alias {$serviceName}-{$service->uuid} $network {$serviceName}-{$service->uuid} || true";
}
$activity = remote_process($commands, $service->server);
$activity = remote_process($commands, $service->server, type_uuid: $service->uuid, callEventOnFinish: 'ServiceStatusChanged');
return $activity;
}
}

View File

@@ -16,9 +16,11 @@ class CoolifyTaskArgs extends Data
public string $command,
public string $type,
public ?string $type_uuid = null,
public ?int $process_id = null,
public ?Model $model = null,
public ?string $status = null ,
public bool $ignore_errors = false,
public $call_event_on_finish = null,
) {
if(is_null($status)){
$this->status = ProcessStatus::QUEUED->value;

View File

@@ -8,5 +8,6 @@ enum ProcessStatus: string
case IN_PROGRESS = 'in_progress';
case FINISHED = 'finished';
case ERROR = 'error';
case KILLED = 'killed';
case CANCELLED = 'cancelled';
}

View File

@@ -0,0 +1,34 @@
<?php
namespace App\Events;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class ApplicationStatusChanged implements ShouldBroadcast
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public $teamId;
public function __construct($teamId = null)
{
if (is_null($teamId)) {
$teamId = auth()->user()->currentTeam()->id ?? null;
}
if (is_null($teamId)) {
throw new \Exception("Team id is null");
}
$this->teamId = $teamId;
}
public function broadcastOn(): array
{
return [
new PrivateChannel("team.{$this->teamId}"),
];
}
}

View File

@@ -0,0 +1,34 @@
<?php
namespace App\Events;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class BackupCreated implements ShouldBroadcast
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public $teamId;
public function __construct($teamId = null)
{
if (is_null($teamId)) {
$teamId = auth()->user()->currentTeam()->id ?? null;
}
if (is_null($teamId)) {
throw new \Exception("Team id is null");
}
$this->teamId = $teamId;
}
public function broadcastOn(): array
{
return [
new PrivateChannel("team.{$this->teamId}"),
];
}
}

View File

@@ -0,0 +1,34 @@
<?php
namespace App\Events;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class DatabaseStatusChanged implements ShouldBroadcast
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public $userId;
public function __construct($userId = null)
{
if (is_null($userId)) {
$userId = auth()->user()->id ?? null;
}
if (is_null($userId)) {
throw new \Exception("User id is null");
}
$this->userId = $userId;
}
public function broadcastOn(): array
{
return [
new PrivateChannel("user.{$this->userId}"),
];
}
}

View File

@@ -0,0 +1,34 @@
<?php
namespace App\Events;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class ServiceStatusChanged implements ShouldBroadcast
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public $userId;
public function __construct($userId = null)
{
if (is_null($userId)) {
$userId = auth()->user()->id ?? null;
}
if (is_null($userId)) {
throw new \Exception("User id is null");
}
$this->userId = $userId;
}
public function broadcastOn(): array
{
return [
new PrivateChannel("user.{$this->userId}"),
];
}
}

28
app/Events/TestEvent.php Normal file
View File

@@ -0,0 +1,28 @@
<?php
namespace App\Events;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class TestEvent implements ShouldBroadcast
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public $teamId;
public function __construct()
{
$this->teamId = auth()->user()->currentTeam()->id;
}
public function broadcastOn(): array
{
return [
new PrivateChannel("team.{$this->teamId}"),
];
}
}

View File

@@ -79,6 +79,10 @@ class Controller extends BaseController
if (isInstanceAdmin()) {
$settings = InstanceSettings::get();
$database = StandalonePostgresql::whereName('coolify-db')->first();
if ($database->status !== 'running') {
$database->status = 'running';
$database->save();
}
if ($database) {
$s3s = S3Storage::whereTeamId(0)->get();
}

View File

@@ -116,7 +116,6 @@ class ProjectController extends Controller
});
}
$service->parse(isNew: true);
return redirect()->route('project.service.configuration', [
'service_uuid' => $service->uuid,
'environment_name' => $environment->name,

View File

@@ -1,141 +0,0 @@
<?php
namespace App\Http\Livewire\Project\New;
use App\Models\EnvironmentVariable;
use App\Models\Project;
use App\Models\Service;
use Livewire\Component;
use Illuminate\Support\Str;
use Symfony\Component\Yaml\Yaml;
class DockerCompose extends Component
{
public string $dockerComposeRaw = '';
public string $envFile = '';
public array $parameters;
public array $query;
public function mount()
{
$this->parameters = get_route_parameters();
$this->query = request()->query();
if (isDev()) {
$this->dockerComposeRaw = 'services:
ghost:
image: ghost:5
volumes:
- ~/configs:/etc/configs/:ro
- ./var/lib/ghost/content:/tmp/ghost2/content:ro
- /var/lib/ghost/content:/tmp/ghost/content:rw
- ghost-content-data:/var/lib/ghost/content
- type: volume
source: mydata
target: /data
- type: bind
source: ./var/lib/ghost/data
target: /data
- type: bind
source: /tmp
target: /tmp
labels:
- "test.label=true"
ports:
- "3000"
- "3000-3005"
- "8000:8000"
- "9090-9091:8080-8081"
- "49100:22"
- "127.0.0.1:8001:8001"
- "127.0.0.1:5000-5010:5000-5010"
- "127.0.0.1::5000"
- "6060:6060/udp"
- "12400-12500:1240"
- target: 80
published: 8080
protocol: tcp
mode: host
networks:
- some-network
- other-network
environment:
- database__client=${DATABASE_CLIENT:-mysql}
- database__connection__database=${MYSQL_DATABASE:-ghost}
- database__connection__host=${DATABASE_CONNECTION_HOST:-mysql}
- test=${TEST:?true}
- url=$SERVICE_FQDN_GHOST
- database__connection__user=$SERVICE_USER_MYSQL
- database__connection__password=$SERVICE_PASSWORD_MYSQL
depends_on:
- mysql
mysql:
image: mysql:8.0
volumes:
- ghost-mysql-data:/var/lib/mysql
environment:
- MYSQL_USER=${SERVICE_USER_MYSQL}
- MYSQL_PASSWORD=${SERVICE_PASSWORD_MYSQL}
- MYSQL_DATABASE=$MYSQL_DATABASE
- MYSQL_ROOT_PASSWORD=${SERVICE_PASSWORD_MYSQLROOT}
- SESSION_SECRET
minio:
image: minio/minio
environment:
RACK_ENV: development
A: $A
SHOW: ${SHOW}
SHOW1: ${SHOW2-show1}
SHOW2: ${SHOW3:-show2}
SHOW3: ${SHOW4?show3}
SHOW4: ${SHOW5:?show4}
SHOW5: ${SERVICE_USER_MINIO}
SHOW6: ${SERVICE_PASSWORD_MINIO}
SHOW7: ${SERVICE_PASSWORD_64_MINIO}
SHOW8: ${SERVICE_BASE64_64_MINIO}
SHOW9: ${SERVICE_BASE64_128_MINIO}
SHOW10: ${SERVICE_BASE64_MINIO}
SHOW11:
';
}
}
public function submit()
{
try {
$this->validate([
'dockerComposeRaw' => 'required'
]);
$this->dockerComposeRaw = Yaml::dump(Yaml::parse($this->dockerComposeRaw), 10, 2, Yaml::DUMP_MULTI_LINE_LITERAL_BLOCK);
$server_id = $this->query['server_id'];
$project = Project::where('uuid', $this->parameters['project_uuid'])->first();
$environment = $project->load(['environments'])->environments->where('name', $this->parameters['environment_name'])->first();
$service = Service::create([
'name' => 'service' . Str::random(10),
'docker_compose_raw' => $this->dockerComposeRaw,
'environment_id' => $environment->id,
'server_id' => (int) $server_id,
]);
$variables = parseEnvFormatToArray($this->envFile);
foreach ($variables as $key => $variable) {
EnvironmentVariable::create([
'key' => $key,
'value' => $variable,
'is_build_time' => false,
'is_preview' => false,
'service_id' => $service->id,
]);
}
$service->name = "service-$service->uuid";
$service->parse(isNew: true);
return redirect()->route('project.service.configuration', [
'service_uuid' => $service->uuid,
'environment_name' => $environment->name,
'project_uuid' => $project->uuid,
]);
} catch (\Throwable $e) {
return handleError($e, $this);
}
}
}

View File

@@ -1,19 +0,0 @@
<?php
namespace App\Http\Livewire\Project\Service;
use Livewire\Component;
class ComposeModal extends Component
{
public ?string $raw = null;
public ?string $actual = null;
public function render()
{
return view('livewire.project.service.compose-modal');
}
public function submit() {
$this->emit('warning', "Saving new docker compose...");
$this->emit('saveCompose', $this->raw);
}
}

View File

@@ -1,41 +0,0 @@
<?php
namespace App\Http\Livewire\Project\Service;
use App\Actions\Service\StartService;
use App\Actions\Service\StopService;
use App\Models\Service;
use Livewire\Component;
class Navbar extends Component
{
public Service $service;
public array $parameters;
public array $query;
protected $listeners = ["checkStatus"];
public function render()
{
return view('livewire.project.service.navbar');
}
public function checkStatus() {
$this->service->refresh();
}
public function deploy()
{
$this->service->parse();
$activity = StartService::run($this->service);
$this->emit('newMonitorActivity', $activity->id);
}
public function stop(bool $forceCleanup = false)
{
StopService::run($this->service);
$this->service->refresh();
if ($forceCleanup) {
$this->emit('success', 'Force cleanup service successfully.');
} else {
$this->emit('success', 'Service stopped successfully.');
}
$this->emit('checkStatus');
}
}

View File

@@ -1,33 +0,0 @@
<?php
namespace App\Http\Livewire\Project\Shared;
use App\Jobs\DeleteResourceJob;
use Livewire\Component;
use Visus\Cuid2\Cuid2;
class Danger extends Component
{
public $resource;
public array $parameters;
public ?string $modalId = null;
public function mount()
{
$this->modalId = new Cuid2(7);
$this->parameters = get_route_parameters();
}
public function delete()
{
try {
DeleteResourceJob::dispatchSync($this->resource);
return redirect()->route('project.resources', [
'project_uuid' => $this->parameters['project_uuid'],
'environment_name' => $this->parameters['environment_name']
]);
} catch (\Throwable $e) {
return handleError($e, $this);
}
}
}

View File

@@ -1,52 +0,0 @@
<?php
namespace App\Http\Livewire\Project\Shared;
use App\Models\Server;
use Illuminate\Support\Facades\Process;
use Livewire\Component;
class GetLogs extends Component
{
public string $outputs = '';
public string $errors = '';
public Server $server;
public ?string $container = null;
public ?bool $streamLogs = false;
public ?bool $showTimeStamps = true;
public int $numberOfLines = 100;
public function doSomethingWithThisChunkOfOutput($output)
{
$this->outputs .= removeAnsiColors($output);
}
public function instantSave()
{
}
public function getLogs($refresh = false)
{
if ($this->container) {
if ($this->showTimeStamps) {
$sshCommand = generateSshCommand($this->server, "docker logs -n {$this->numberOfLines} -t {$this->container}");
} else {
$sshCommand = generateSshCommand($this->server, "docker logs -n {$this->numberOfLines} {$this->container}");
}
if ($refresh) {
$this->outputs = '';
}
Process::run($sshCommand, function (string $type, string $output) {
$this->doSomethingWithThisChunkOfOutput($output);
});
if ($this->showTimeStamps) {
$this->outputs = str($this->outputs)->split('/\n/')->sort(function ($a, $b) {
$a = explode(' ', $a);
$b = explode(' ', $b);
return $a[0] <=> $b[0];
})->join("\n");
}
}
}
public function render()
{
return view('livewire.project.shared.get-logs');
}
}

View File

@@ -1,17 +0,0 @@
<?php
namespace App\Http\Livewire;
use Livewire\Component;
class Sponsorship extends Component
{
public function disable()
{
auth()->user()->update(['is_notification_sponsorship_enabled' => false]);
}
public function render()
{
return view('livewire.sponsorship');
}
}

View File

@@ -4,6 +4,7 @@ namespace App\Jobs;
use App\Enums\ApplicationDeploymentStatus;
use App\Enums\ProxyTypes;
use App\Events\ApplicationStatusChanged;
use App\Models\Application;
use App\Models\ApplicationDeploymentQueue;
use App\Models\ApplicationPreview;
@@ -266,6 +267,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
"ignore_errors" => true,
]
);
ApplicationStatusChanged::dispatch(data_get($this->application,'environment.project.team.id'));
}
}
private function push_to_docker_registry()
@@ -347,6 +349,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
$this->generate_image_names();
$this->check_image_locally_or_remotely();
if (str($this->saved_outputs->get('local_image_found'))->isNotEmpty()) {
$this->create_workdir();
$this->generate_compose_file();
$this->rolling_update();
return;
@@ -442,23 +445,22 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
$this->clone_repository();
$this->generate_image_names();
$this->cleanup_git();
$this->application->loadComposeFile(isInit: false);
$composeFile = $this->application->parseCompose(pull_request_id: $this->pull_request_id);
$yaml = Yaml::dump($composeFile->toArray(), 10);
ray($composeFile);
ray($this->container_name);
$this->docker_compose_base64 = base64_encode($yaml);
$this->execute_remote_command([
executeInDocker($this->deployment_uuid, "echo '{$this->docker_compose_base64}' | base64 -d > {$this->workdir}{$this->docker_compose_location}"), "hidden" => true
]);
$this->save_environment_variables();
// Build new container to limit downtime.
$this->build_by_compose_file();
$this->stop_running_container(force: true);
ray($this->pull_request_id);
$networkId = $this->application->uuid;
if ($this->pull_request_id !== 0) {
$networkId = "{$this->application->uuid}-{$this->pull_request_id}";
}
ray($networkId);
if ($this->server->isSwarm()) {
// TODO
} else {
@@ -468,9 +470,25 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
"docker network connect {$networkId} coolify-proxy || true", "hidden" => true, "ignore_errors" => true
]);
}
if (isset($this->docker_compose_base64)) {
$readme = generate_readme_file($this->application->name, $this->application_deployment_queue->updated_at);
$composeFileName = "$this->configuration_dir/docker-compose.yml";
if ($this->pull_request_id !== 0) {
$composeFileName = "$this->configuration_dir/docker-compose-pr-{$this->pull_request_id}.yml";
}
$this->execute_remote_command(
[
"mkdir -p $this->configuration_dir"
],
[
"echo '{$this->docker_compose_base64}' | base64 -d > $composeFileName",
],
[
"echo '{$readme}' > $this->configuration_dir/README.md",
]
);
}
$this->start_by_compose_file();
$this->application->loadComposeFile(isInit: false);
}
private function deploy_dockerfile_buildpack()
{
@@ -513,6 +531,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
if (!$this->force_rebuild) {
$this->check_image_locally_or_remotely();
if (str($this->saved_outputs->get('local_image_found'))->isNotEmpty() && !$this->application->isConfigurationChanged()) {
$this->create_workdir();
$this->execute_remote_command([
"echo 'No configuration changed & image found ({$this->production_image_name}) with the same Git Commit SHA. Build step skipped.'",
]);
@@ -660,7 +679,14 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
[executeInDocker($this->deployment_uuid, "docker compose --project-directory {$this->workdir} up -d"), "hidden" => true],
);
}
private function create_workdir()
{
$this->execute_remote_command(
[
"command" => executeInDocker($this->deployment_uuid, "mkdir -p {$this->workdir}")
],
);
}
private function prepare_builder_image()
{
$helperImage = config('coolify.helper_image');
@@ -669,9 +695,9 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
$this->dockerConfigFileExists = instant_remote_process(["test -f {$this->serverUserHomeDir}/.docker/config.json && echo 'OK' || echo 'NOK'"], $this->server);
if ($this->dockerConfigFileExists === 'OK') {
$runCommand = "docker run -d --network {$this->destination->network} -v /:/host --name {$this->deployment_uuid} --rm -v {$this->serverUserHomeDir}/.docker/config.json:/root/.docker/config.json:ro -v /var/run/docker.sock:/var/run/docker.sock {$helperImage}";
$runCommand = "docker run -d --network {$this->destination->network} --name {$this->deployment_uuid} --rm -v {$this->serverUserHomeDir}/.docker/config.json:/root/.docker/config.json:ro -v /var/run/docker.sock:/var/run/docker.sock {$helperImage}";
} else {
$runCommand = "docker run -d --network {$this->destination->network} -v /:/host --name {$this->deployment_uuid} --rm -v /var/run/docker.sock:/var/run/docker.sock {$helperImage}";
$runCommand = "docker run -d --network {$this->destination->network} --name {$this->deployment_uuid} --rm -v /var/run/docker.sock:/var/run/docker.sock {$helperImage}";
}
$this->execute_remote_command(
[
@@ -684,6 +710,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
[
"command" => executeInDocker($this->deployment_uuid, "mkdir -p {$this->basedir}")
],
);
}
private function deploy_to_additional_destinations()
@@ -1071,6 +1098,9 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
} else {
$health_check_port = $this->application->health_check_port;
}
if ($this->application->settings->is_static || $this->application->build_pack === 'static') {
$health_check_port = 80;
}
if ($this->application->health_check_path) {
$this->full_healthcheck_url = "{$this->application->health_check_method}: {$this->application->health_check_scheme}://{$this->application->health_check_host}:{$health_check_port}{$this->application->health_check_path}";
$generated_healthchecks_commands = [
@@ -1217,6 +1247,28 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
}
}
private function build_by_compose_file() {
$this->application_deployment_queue->addLogEntry("Pulling & building required images.");
if ($this->application->build_pack === 'dockerimage') {
$this->application_deployment_queue->addLogEntry("Pulling latest images from the registry.");
$this->execute_remote_command(
[executeInDocker($this->deployment_uuid, "docker compose --project-directory {$this->workdir} pull"), "hidden" => true],
[executeInDocker($this->deployment_uuid, "docker compose --project-directory {$this->workdir} build"), "hidden" => true],
);
} else {
if ($this->docker_compose_location) {
$this->execute_remote_command(
[executeInDocker($this->deployment_uuid, "docker compose --project-directory {$this->workdir} -f {$this->workdir}{$this->docker_compose_location} build"), "hidden" => true],
);
} else {
$this->execute_remote_command(
[executeInDocker($this->deployment_uuid, "docker compose --project-directory {$this->workdir} build"), "hidden" => true],
);
}
}
$this->application_deployment_queue->addLogEntry("New images built.");
}
private function start_by_compose_file()
{
if ($this->application->build_pack === 'dockerimage') {
@@ -1226,9 +1278,15 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
[executeInDocker($this->deployment_uuid, "docker compose --project-directory {$this->workdir} up --build -d"), "hidden" => true],
);
} else {
$this->execute_remote_command(
[executeInDocker($this->deployment_uuid, "docker compose --project-directory {$this->workdir} up --build -d"), "hidden" => true],
);
if ($this->docker_compose_location) {
$this->execute_remote_command(
[executeInDocker($this->deployment_uuid, "docker compose --project-directory {$this->workdir} -f {$this->workdir}{$this->docker_compose_location} up --build -d"), "hidden" => true],
);
} else {
$this->execute_remote_command(
[executeInDocker($this->deployment_uuid, "docker compose --project-directory {$this->workdir} up --build -d"), "hidden" => true],
);
}
}
$this->application_deployment_queue->addLogEntry("New container started.");
}
@@ -1264,7 +1322,6 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
$dockerfile->splice(1, 0, "ARG {$env->key}={$env->value}");
}
}
ray($dockerfile->implode("\n"));
$dockerfile_base64 = base64_encode($dockerfile->implode("\n"));
$this->execute_remote_command([
executeInDocker($this->deployment_uuid, "echo '{$dockerfile_base64}' | base64 -d > {$this->workdir}{$this->dockerfile_location}"),

View File

@@ -0,0 +1,180 @@
<?php
namespace App\Jobs;
use App\Enums\ApplicationDeploymentStatus;
use App\Models\Application;
use App\Models\ApplicationDeploymentQueue;
use App\Models\Server;
use App\Traits\ExecuteRemoteCommand;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use RuntimeException;
use Throwable;
class ApplicationDeploymentNewJob implements ShouldQueue, ShouldBeEncrypted
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, ExecuteRemoteCommand;
public $timeout = 3600;
public $tries = 1;
public static int $batch_counter = 0;
public Server $mainServer;
public $servers;
public string $basedir;
public string $workdir;
public string $deploymentUuid;
public int $pullRequestId = 0;
// Git related
public string $gitImportCommands;
public ?string $gitType = null;
public string $gitRepository;
public string $gitBranch;
public int $gitPort;
public string $gitFullRepoUrl;
public function __construct(public ApplicationDeploymentQueue $deployment, public Application $application)
{
$this->mainServer = data_get($this->application, 'destination.server');
$this->deploymentUuid = data_get($this->deployment, 'deployment_uuid');
$this->pullRequestId = data_get($this->deployment, 'pull_request_id', 0);
$this->gitType = data_get($this->deployment, 'git_type');
$this->basedir = $this->application->generateBaseDir($this->deploymentUuid);
$this->workdir = $this->basedir . rtrim($this->application->base_directory, '/');
}
public function handle()
{
try {
ray()->clearAll();
$this->deployment->setStatus(ApplicationDeploymentStatus::IN_PROGRESS->value);
$hostIpMappings = $this->mainServer->getHostIPMappings($this->application->destination->network);
if ($this->application->dockerfile_target_build) {
$buildTarget = " --target {$this->application->dockerfile_target_build} ";
}
// Get the git repository and port (custom port or default port)
[
'repository' => $this->gitRepository,
'port' => $this->gitPort
] = $this->application->customRepository();
// Get the git branch and git import commands
[
'commands' => $this->gitImportCommands,
'branch' => $this->gitBranch,
'fullRepoUrl' => $this->gitFullRepoUrl
] = $this->application->generateGitImportCommands($this->deploymentUuid, $this->pullRequestId, $this->gitType);
$this->servers = $this->application->servers();
if ($this->deployment->restart_only) {
if ($this->application->build_pack === 'dockerimage') {
throw new \Exception('Restart only is not supported for docker image based deployments');
}
$this->deployment->addLogEntry("Starting deployment of {$this->application->name}.");
$this->servers->each(function ($server) {
$this->deployment->addLogEntry("Restarting {$this->application->name} on {$server->name}.");
$this->restartOnly($server);
});
}
$this->next(ApplicationDeploymentStatus::FINISHED->value);
} catch (Throwable $exception) {
$this->fail($exception);
} finally {
$this->servers->each(function ($server) {
$this->deployment->addLogEntry("Cleaning up temporary containers on {$server->name}.");
$server->executeRemoteCommand(
commands: collect([])->push([
"command" => "docker rm -f {$this->deploymentUuid}",
"hidden" => true,
"ignoreErrors" => true,
]),
loggingModel: $this->deployment
);
});
}
}
public function restartOnly(Server $server)
{
$server->executeRemoteCommand(
commands: $this->application->prepareHelperImage($this->deploymentUuid),
loggingModel: $this->deployment
);
$privateKey = data_get($this->application, 'private_key.private_key', null);
$gitLsRemoteCommand = collect([]);
if ($privateKey) {
$privateKey = base64_decode($privateKey);
$gitLsRemoteCommand
->push([
"command" => executeInDocker($this->deploymentUuid, "mkdir -p /root/.ssh")
])
->push([
"command" => executeInDocker($this->deploymentUuid, "echo '{$privateKey}' | base64 -d > /root/.ssh/id_rsa")
])
->push([
"command" => executeInDocker($this->deploymentUuid, "chmod 600 /root/.ssh/id_rsa")
])
->push([
"name" => "git_commit_sha",
"command" => executeInDocker($this->deploymentUuid, "GIT_SSH_COMMAND=\"ssh -o ConnectTimeout=30 -p {$this->gitPort} -o Port={$this->gitPort} -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /root/.ssh/id_rsa\" git ls-remote {$this->gitFullRepoUrl} {$this->gitBranch}"),
"hidden" => true,
]);
} else {
$gitLsRemoteCommand->push([
"name" => "git_commit_sha",
"command" => executeInDocker($this->deploymentUuid, "GIT_SSH_COMMAND=\"ssh -o ConnectTimeout=30 -p {$this->gitPort} -o Port={$this->gitPort} -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null\" git ls-remote {$this->gitFullRepoUrl} {$this->gitBranch}"),
"hidden" => true,
]);
}
$this->deployment->addLogEntry("Checking if there is any new commit on {$this->gitBranch} branch.");
$server->executeRemoteCommand(
commands: $gitLsRemoteCommand,
loggingModel: $this->deployment
);
$commit = str($this->deployment->getOutput('git_commit_sha'))->before("\t");
[
'productionImageName' => $productionImageName
] = $this->application->generateImageNames($commit, $this->pullRequestId);
$this->deployment->addLogEntry("Checking if the image {$productionImageName} already exists.");
$server->checkIfDockerImageExists($productionImageName, $this->deployment);
if (str($this->deployment->getOutput('local_image_found'))->isNotEmpty()) {
$this->deployment->addLogEntry("Image {$productionImageName} already exists. Skipping the build.");
$server->createWorkDirForDeployment($this->workdir, $this->deployment);
$this->application->generateDockerComposeFile($server, $this->deployment, $this->workdir);
$this->application->rollingUpdateApplication($server, $this->deployment, $this->workdir);
return;
}
throw new RuntimeException('Cannot find image anywhere. Please redeploy the application.');
}
public function failed(Throwable $exception): void
{
ray($exception);
$this->next(ApplicationDeploymentStatus::FAILED->value);
}
private function next(string $status)
{
// If the deployment is cancelled by the user, don't update the status
if ($this->deployment->status !== ApplicationDeploymentStatus::CANCELLED_BY_USER->value) {
$this->deployment->update([
'status' => $status,
]);
}
queue_next_deployment($this->application, isNew: true);
}
}

View File

@@ -2,6 +2,7 @@
namespace App\Jobs;
use App\Actions\Database\StartDatabaseProxy;
use App\Actions\Proxy\CheckProxy;
use App\Actions\Proxy\StartProxy;
use App\Models\ApplicationPreview;
@@ -21,10 +22,6 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public function __construct(public Server $server)
{
$this->handle();
}
public function middleware(): array
{
return [(new WithoutOverlapping($this->server->id))->dontRelease()];
@@ -35,6 +32,12 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
return $this->server->id;
}
public function __construct(public Server $server)
{
$this->handle();
}
public function handle()
{
// ray("checking container statuses for {$this->server->id}");
@@ -83,7 +86,6 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
$databases = $this->server->databases();
$services = $this->server->services()->get();
$previews = $this->server->previews();
$foundApplications = [];
$foundApplicationPreviews = [];
$foundDatabases = [];
@@ -134,11 +136,25 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
if ($uuid) {
$database = $databases->where('uuid', $uuid)->first();
if ($database) {
$isPublic = data_get($database, 'is_public');
$foundDatabases[] = $database->id;
$statusFromDb = $database->status;
if ($statusFromDb !== $containerStatus) {
$database->update(['status' => $containerStatus]);
}
if ($isPublic) {
$foundTcpProxy = $containers->filter(function ($value, $key) use ($uuid) {
if ($this->server->isSwarm()) {
return data_get($value, 'Spec.Name') === "coolify-proxy_$uuid";
} else {
return data_get($value, 'Name') === "/$uuid-proxy";
}
})->first();
if (!$foundTcpProxy) {
StartDatabaseProxy::run($database);
$this->server->team?->notify(new ContainerRestarted("TCP Proxy for {$database->name}", $this->server));
}
}
} else {
// Notify user that this container should not be there.
}
@@ -297,8 +313,6 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
if ($shouldStart) {
StartProxy::run($this->server, false);
$this->server->team?->notify(new ContainerRestarted('coolify-proxy', $this->server));
} else {
ray('Proxy could not be started.');
}
} catch (\Throwable $e) {
ray($e);

View File

@@ -21,6 +21,7 @@ class CoolifyTask implements ShouldQueue, ShouldBeEncrypted
public function __construct(
public Activity $activity,
public bool $ignore_errors = false,
public $call_event_on_finish = null
) {
}
@@ -32,6 +33,7 @@ class CoolifyTask implements ShouldQueue, ShouldBeEncrypted
$remote_process = resolve(RunRemoteProcess::class, [
'activity' => $this->activity,
'ignore_errors' => $this->ignore_errors,
'call_event_on_finish' => $this->call_event_on_finish
]);
$remote_process();

View File

@@ -3,6 +3,7 @@
namespace App\Jobs;
use App\Actions\Database\StopDatabase;
use App\Events\BackupCreated;
use App\Models\S3Storage;
use App\Models\ScheduledDatabaseBackup;
use App\Models\ScheduledDatabaseBackupExecution;
@@ -74,6 +75,7 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted
public function handle(): void
{
try {
BackupCreated::dispatch($this->team->id);
// Check if team is exists
if (is_null($this->team)) {
$this->backup->update(['status' => 'failed']);
@@ -307,6 +309,8 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted
} catch (\Throwable $e) {
send_internal_notification('DatabaseBackupJob failed with: ' . $e->getMessage());
throw $e;
} finally {
BackupCreated::dispatch($this->team->id);
}
}
private function backup_standalone_mongodb(string $databaseWithCollections): void

View File

@@ -4,7 +4,7 @@ namespace App\Jobs;
use App\Actions\Application\StopApplication;
use App\Actions\Database\StopDatabase;
use App\Actions\Service\StopService;
use App\Actions\Service\DeleteService;
use App\Models\Application;
use App\Models\Service;
use App\Models\StandaloneMariadb;
@@ -54,11 +54,13 @@ class DeleteResourceJob implements ShouldQueue, ShouldBeEncrypted
case 'standalone-mariadb':
StopDatabase::run($this->resource);
break;
case 'service':
StopService::run($this->resource);
break;
}
$this->resource->delete();
if ($this->resource->type() === 'service') {
$this->resource->delete();
DeleteService::dispatch($this->resource);
} else {
$this->resource->delete();
}
} catch (\Throwable $e) {
send_internal_notification('ContainerStoppingJob failed with: ' . $e->getMessage());
throw $e;

View File

@@ -1,6 +1,6 @@
<?php
namespace App\Http\Livewire;
namespace App\Livewire;
use App\Enums\ProcessStatus;
use Livewire\Component;
@@ -8,7 +8,7 @@ use Spatie\Activitylog\Models\Activity;
class ActivityMonitor extends Component
{
public string|null $header = null;
public ?string $header = null;
public $activityId;
public $isPollingActive = false;
@@ -26,31 +26,30 @@ class ActivityMonitor extends Component
public function hydrateActivity()
{
$this->activity = Activity::query()
->find($this->activityId);
$this->activity = Activity::find($this->activityId);
}
public function polling()
{
$this->hydrateActivity();
$this->setStatus(ProcessStatus::IN_PROGRESS);
// $this->setStatus(ProcessStatus::IN_PROGRESS);
$exit_code = data_get($this->activity, 'properties.exitCode');
if ($exit_code !== null) {
if ($exit_code === 0) {
$this->setStatus(ProcessStatus::FINISHED);
// $this->setStatus(ProcessStatus::FINISHED);
} else {
$this->setStatus(ProcessStatus::ERROR);
// $this->setStatus(ProcessStatus::ERROR);
}
$this->isPollingActive = false;
$this->emit('activityFinished');
$this->dispatch('activityFinished');
}
}
protected function setStatus($status)
{
$this->activity->properties = $this->activity->properties->merge([
'status' => $status,
]);
$this->activity->save();
}
// protected function setStatus($status)
// {
// $this->activity->properties = $this->activity->properties->merge([
// 'status' => $status,
// ]);
// $this->activity->save();
// }
}

View File

@@ -1,6 +1,6 @@
<?php
namespace App\Http\Livewire\Boarding;
namespace App\Livewire\Boarding;
use App\Actions\Server\InstallDocker;
use App\Models\PrivateKey;
@@ -32,6 +32,7 @@ class Index extends Component
public ?int $remoteServerPort = 22;
public ?string $remoteServerUser = 'root';
public bool $isSwarmManager = false;
public bool $isCloudflareTunnel = false;
public ?Server $createdServer = null;
public Collection $projects;
@@ -87,7 +88,7 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
if ($this->selectedServerType === 'localhost') {
$this->createdServer = Server::find(0);
if (!$this->createdServer) {
return $this->emit('error', 'Localhost server is not found. Something went wrong during installation. Please try to reinstall or contact support.');
return $this->dispatch('error', 'Localhost server is not found. Something went wrong during installation. Please try to reinstall or contact support.');
}
$this->serverPublicKey = $this->createdServer->privateKey->publicKey();
return $this->validateServer('localhost');
@@ -109,7 +110,7 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
{
$this->createdServer = Server::find($this->selectedExistingServer);
if (!$this->createdServer) {
$this->emit('error', 'Server is not found.');
$this->dispatch('error', 'Server is not found.');
$this->currentState = 'private-key';
return;
}
@@ -172,7 +173,7 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
$this->privateKey = formatPrivateKey($this->privateKey);
$foundServer = Server::whereIp($this->remoteServerHost)->first();
if ($foundServer) {
return $this->emit('error', 'IP address is already in use by another team.');
return $this->dispatch('error', 'IP address is already in use by another team.');
}
$this->createdServer = Server::create([
'name' => $this->remoteServerName,
@@ -184,6 +185,7 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
'team_id' => currentTeam()->id,
]);
$this->createdServer->settings->is_swarm_manager = $this->isSwarmManager;
$this->createdServer->settings->is_cloudflare_tunnel = $this->isCloudflareTunnel;
$this->createdServer->settings->save();
$this->createdServer->addInitialNetwork();
$this->validateServer();
@@ -200,6 +202,7 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
]);
} catch (\Throwable $e) {
$this->serverReachable = false;
$this->createdServer->delete();
return handleError(error: $e, livewire: $this);
}
@@ -224,8 +227,8 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
try {
$this->dockerInstallationStarted = true;
$activity = InstallDocker::run($this->createdServer);
$this->emit('installDocker');
$this->emit('newMonitorActivity', $activity->id);
$this->dispatch('installDocker');
$this->dispatch('newMonitorActivity', $activity->id);
} catch (\Throwable $e) {
$this->dockerInstallationStarted = false;
return handleError(error: $e, livewire: $this);

View File

@@ -1,6 +1,6 @@
<?php
namespace App\Http\Livewire;
namespace App\Livewire;
use App\Actions\License\CheckResaleLicense;
use App\Models\InstanceSettings;
@@ -33,11 +33,11 @@ class CheckLicense extends Component
if ($this->settings->resale_license) {
try {
CheckResaleLicense::run();
$this->emit('reloadWindow');
$this->dispatch('reloadWindow');
} catch (\Throwable $e) {
session()->flash('error', 'Something went wrong. Please contact support. <br>Error: ' . $e->getMessage());
ray($e->getMessage());
return redirect()->to('/settings/license');
return $this->redirect('/settings/license', navigate: true);
}
}
}

View File

@@ -1,6 +1,6 @@
<?php
namespace App\Http\Livewire;
namespace App\Livewire;
use App\Models\Project;
use App\Models\Server;
@@ -10,7 +10,6 @@ class Dashboard extends Component
{
public $projects = [];
public $servers = [];
public function mount()
{
$this->servers = Server::ownedByCurrentTeam()->get();

View File

@@ -1,6 +1,6 @@
<?php
namespace App\Http\Livewire\Destination;
namespace App\Livewire\Destination;
use Livewire\Component;
@@ -30,13 +30,13 @@ class Form extends Component
try {
if ($this->destination->getMorphClass() === 'App\Models\StandaloneDocker') {
if ($this->destination->attachedTo()) {
return $this->emit('error', 'You must delete all resources before deleting this destination.');
return $this->dispatch('error', 'You must delete all resources before deleting this destination.');
}
instant_remote_process(["docker network disconnect {$this->destination->network} coolify-proxy"], $this->destination->server, throwError: false);
instant_remote_process(['docker network rm -f ' . $this->destination->network], $this->destination->server);
}
$this->destination->delete();
return redirect()->route('dashboard');
return $this->redirectRoute('dashboard', navigate: true);
} catch (\Throwable $e) {
return handleError($e, $this);
}

View File

@@ -1,6 +1,6 @@
<?php
namespace App\Http\Livewire\Destination\New;
namespace App\Livewire\Destination\New;
use App\Models\Server;
use App\Models\StandaloneDocker as ModelsStandaloneDocker;
@@ -60,7 +60,7 @@ class StandaloneDocker extends Component
$found = $this->server->standaloneDockers()->where('network', $this->network)->first();
if ($found) {
$this->createNetworkAndAttachToProxy();
$this->emit('error', 'Network already added to this server.');
$this->dispatch('error', 'Network already added to this server.');
return;
} else {
$docker = ModelsStandaloneDocker::create([
@@ -70,7 +70,7 @@ class StandaloneDocker extends Component
]);
}
$this->createNetworkAndAttachToProxy();
return redirect()->route('destination.show', $docker->uuid);
return $this->redirectRoute('destination.show', $docker->uuid, navigate: true);
} catch (\Throwable $e) {
return handleError($e, $this);
}

View File

@@ -1,6 +1,6 @@
<?php
namespace App\Http\Livewire\Destination;
namespace App\Livewire\Destination;
use App\Models\Server;
use Illuminate\Support\Collection;
@@ -21,7 +21,7 @@ class Show extends Component
return !$alreadyAddedNetworks->contains('network', $network['Name']);
});
if ($this->networks->count() === 0) {
$this->emit('success', 'No new networks found.');
$this->dispatch('success', 'No new networks found.');
}
}
}

View File

@@ -1,6 +1,6 @@
<?php
namespace App\Http\Livewire\Dev;
namespace App\Livewire\Dev;
use Livewire\Component;

View File

@@ -1,6 +1,6 @@
<?php
namespace App\Http\Livewire;
namespace App\Livewire;
use Illuminate\Support\Facades\Hash;
use DanHarrin\LivewireRateLimiting\WithRateLimiting;
@@ -35,7 +35,7 @@ class ForcePasswordReset extends Component
if ($firstLogin) {
send_internal_notification('First login for ' . auth()->user()->email);
}
return redirect()->route('dashboard');
return $this->redirectRoute('dashboard', navigate: true);
} catch (\Throwable $e) {
return handleError($e, $this);
}

View File

@@ -1,6 +1,6 @@
<?php
namespace App\Http\Livewire;
namespace App\Livewire;
use DanHarrin\LivewireRateLimiting\WithRateLimiting;
use Illuminate\Notifications\Messages\MailMessage;
@@ -42,7 +42,7 @@ class Help extends Component
);
$mail->subject("[HELP - {$subscriptionType}]: {$this->subject}");
send_user_an_email($mail, auth()->user()?->email, 'hi@coollabs.io');
$this->emit('success', 'Your message has been sent successfully. <br>We will get in touch with you as soon as possible.');
$this->dispatch('success', 'Your message has been sent successfully. <br>We will get in touch with you as soon as possible.');
} catch (\Throwable $e) {
return handleError($e, $this);
}

View File

@@ -0,0 +1,28 @@
<?php
namespace App\Livewire\Modal;
use App\Models\Service;
use LivewireUI\Modal\ModalComponent;
class EditCompose extends ModalComponent
{
public Service $service;
public $serviceId;
protected $rules = [
'service.docker_compose_raw' => 'required',
'service.docker_compose' => 'required',
];
public function mount() {
$this->service = Service::find($this->serviceId);
}
public function render()
{
return view('livewire.modal.edit-compose');
}
public function submit() {
$this->dispatch('warning', "Saving new docker compose...");
$this->dispatch('saveCompose', $this->service->docker_compose_raw);
$this->closeModal();
}
}

View File

@@ -1,6 +1,6 @@
<?php
namespace App\Http\Livewire\Notifications;
namespace App\Livewire\Notifications;
use App\Models\Team;
use App\Notifications\Test;
@@ -47,12 +47,12 @@ class DiscordSettings extends Component
{
$this->team->save();
refreshSession();
$this->emit('success', 'Settings saved.');
$this->dispatch('success', 'Settings saved.');
}
public function sendTestNotification()
{
$this->team->notify(new Test());
$this->emit('success', 'Test notification sent.');
$this->dispatch('success', 'Test notification sent.');
}
}

View File

@@ -1,6 +1,6 @@
<?php
namespace App\Http\Livewire\Notifications;
namespace App\Livewire\Notifications;
use App\Models\InstanceSettings;
use App\Models\Team;
@@ -63,7 +63,7 @@ class EmailSettings extends Component
]);
$this->team->save();
refreshSession();
$this->emit('success', 'Settings saved successfully.');
$this->dispatch('success', 'Settings saved successfully.');
} catch (\Throwable $e) {
return handleError($e, $this);
}
@@ -71,7 +71,7 @@ class EmailSettings extends Component
public function sendTestNotification()
{
$this->team->notify(new Test($this->emails));
$this->emit('success', 'Test Email sent successfully.');
$this->dispatch('success', 'Test Email sent successfully.');
}
public function instantSaveInstance()
{
@@ -83,7 +83,7 @@ class EmailSettings extends Component
$this->team->resend_enabled = false;
$this->team->save();
refreshSession();
$this->emit('success', 'Settings saved successfully.');
$this->dispatch('success', 'Settings saved successfully.');
} catch (\Throwable $e) {
return handleError($e, $this);
}
@@ -113,7 +113,7 @@ class EmailSettings extends Component
{
$this->team->save();
refreshSession();
$this->emit('success', 'Settings saved.');
$this->dispatch('success', 'Settings saved.');
}
public function submit()
{
@@ -131,7 +131,7 @@ class EmailSettings extends Component
]);
$this->team->save();
refreshSession();
$this->emit('success', 'Settings saved successfully.');
$this->dispatch('success', 'Settings saved successfully.');
} catch (\Throwable $e) {
$this->team->smtp_enabled = false;
return handleError($e, $this);
@@ -148,7 +148,7 @@ class EmailSettings extends Component
]);
$this->team->save();
refreshSession();
$this->emit('success', 'Settings saved successfully.');
$this->dispatch('success', 'Settings saved successfully.');
} catch (\Throwable $e) {
$this->team->resend_enabled = false;
return handleError($e, $this);
@@ -173,7 +173,7 @@ class EmailSettings extends Component
]);
refreshSession();
$this->team = $team;
$this->emit('success', 'Settings saved.');
$this->dispatch('success', 'Settings saved.');
return;
}
if ($settings->resend_enabled) {
@@ -184,9 +184,9 @@ class EmailSettings extends Component
]);
refreshSession();
$this->team = $team;
$this->emit('success', 'Settings saved.');
$this->dispatch('success', 'Settings saved.');
return;
}
$this->emit('error', 'Instance SMTP/Resend settings are not enabled.');
$this->dispatch('error', 'Instance SMTP/Resend settings are not enabled.');
}
}

View File

@@ -1,6 +1,6 @@
<?php
namespace App\Http\Livewire\Notifications;
namespace App\Livewire\Notifications;
use App\Models\Team;
use App\Notifications\Test;
@@ -53,12 +53,12 @@ class TelegramSettings extends Component
{
$this->team->save();
refreshSession();
$this->emit('success', 'Settings saved.');
$this->dispatch('success', 'Settings saved.');
}
public function sendTestNotification()
{
$this->team->notify(new Test());
$this->emit('success', 'Test notification sent.');
$this->dispatch('success', 'Test notification sent.');
}
}

View File

@@ -1,6 +1,6 @@
<?php
namespace App\Http\Livewire\PrivateKey;
namespace App\Livewire\PrivateKey;
use App\Models\PrivateKey;
use Livewire\Component;
@@ -35,9 +35,9 @@ class Change extends Component
if ($this->private_key->isEmpty()) {
$this->private_key->delete();
currentTeam()->privateKeys = PrivateKey::where('team_id', currentTeam()->id)->get();
return redirect()->route('security.private-key.index');
return $this->redirectRoute('security.private-key.index', navigate: true);
}
$this->emit('error', 'This private key is in use and cannot be deleted. Please delete all servers, applications, and GitHub/GitLab apps that use this private key before deleting it.');
$this->dispatch('error', 'This private key is in use and cannot be deleted. Please delete all servers, applications, and GitHub/GitLab apps that use this private key before deleting it.');
} catch (\Throwable $e) {
return handleError($e, $this);
}

View File

@@ -1,6 +1,6 @@
<?php
namespace App\Http\Livewire\PrivateKey;
namespace App\Livewire\PrivateKey;
use App\Models\PrivateKey;
use DanHarrin\LivewireRateLimiting\WithRateLimiting;
@@ -67,9 +67,9 @@ class Create extends Component
'team_id' => currentTeam()->id
]);
if ($this->from === 'server') {
return redirect()->route('server.create');
return $this->redirectRoute('server.create', navigate: true);
}
return redirect()->route('security.private-key.show', ['private_key_uuid' => $private_key->uuid]);
return $this->redirectRoute('security.private-key.show', ['private_key_uuid' => $private_key->uuid], navigate: true);
} catch (\Throwable $e) {
return handleError($e, $this);
}

View File

@@ -1,22 +1,17 @@
<?php
namespace App\Http\Livewire\Profile;
namespace App\Livewire\Profile;
use App\Models\User;
use Livewire\Attributes\Validate;
use Livewire\Component;
class Form extends Component
{
public int $userId;
public string $name;
public string $email;
protected $rules = [
'name' => 'required',
];
protected $validationAttributes = [
'name' => 'name',
];
#[Validate('required')]
public string $name;
public function mount()
{
@@ -30,9 +25,11 @@ class Form extends Component
{
try {
$this->validate();
User::where('id', $this->userId)->update([
auth()->user()->update([
'name' => $this->name,
]);
$this->dispatch('success', 'Profile updated successfully.');
} catch (\Throwable $e) {
return handleError($e, $this);
}

View File

@@ -1,6 +1,6 @@
<?php
namespace App\Http\Livewire\Project;
namespace App\Livewire\Project;
use App\Models\Project;
use Livewire\Component;
@@ -27,7 +27,7 @@ class AddEmpty extends Component
'description' => $this->description,
'team_id' => currentTeam()->id,
]);
return redirect()->route('project.show', $project->uuid);
return $this->redirectRoute('project.show', $project->uuid, navigate: true);
} catch (\Throwable $e) {
return handleError($e, $this);
} finally {

View File

@@ -1,6 +1,6 @@
<?php
namespace App\Http\Livewire\Project;
namespace App\Livewire\Project;
use App\Models\Environment;
use App\Models\Project;
@@ -27,10 +27,10 @@ class AddEnvironment extends Component
'project_id' => $this->project->id,
]);
return redirect()->route('project.resources', [
return $this->redirectRoute('project.resources', [
'project_uuid' => $this->project->uuid,
'environment_name' => $environment->name,
]);
], navigate: true);
} catch (\Throwable $e) {
handleError($e, $this);
} finally {

View File

@@ -1,6 +1,6 @@
<?php
namespace App\Http\Livewire\Project\Application;
namespace App\Livewire\Project\Application;
use App\Models\Application;
use Livewire\Component;
@@ -26,26 +26,26 @@ class Advanced extends Component
if ($this->application->isLogDrainEnabled()) {
if (!$this->application->destination->server->isLogDrainEnabled()) {
$this->application->settings->is_log_drain_enabled = false;
$this->emit('error', 'Log drain is not enabled on this server.');
$this->dispatch('error', 'Log drain is not enabled on this server.');
return;
}
}
if ($this->application->settings->is_force_https_enabled) {
$this->emit('resetDefaultLabels', false);
$this->dispatch('resetDefaultLabels', false);
}
$this->application->settings->save();
$this->emit('success', 'Settings saved.');
$this->dispatch('success', 'Settings saved.');
}
public function submit() {
if ($this->application->settings->gpu_count && $this->application->settings->gpu_device_ids) {
$this->emit('error', 'You cannot set both GPU count and GPU device IDs.');
$this->dispatch('error', 'You cannot set both GPU count and GPU device IDs.');
$this->application->settings->gpu_count = null;
$this->application->settings->gpu_device_ids = null;
$this->application->settings->save();
return;
}
$this->application->settings->save();
$this->emit('success', 'Settings saved.');
$this->dispatch('success', 'Settings saved.');
}
public function render()
{

View File

@@ -1,6 +1,6 @@
<?php
namespace App\Http\Livewire\Project\Application;
namespace App\Livewire\Project\Application;
use App\Models\Application;
use App\Models\Server;
@@ -15,18 +15,18 @@ class Configuration extends Component
{
$project = currentTeam()->load(['projects'])->projects->where('uuid', request()->route('project_uuid'))->first();
if (!$project) {
return redirect()->route('dashboard');
return $this->redirectRoute('dashboard', navigate: true);
}
$environment = $project->load(['environments'])->environments->where('name', request()->route('environment_name'))->first()->load(['applications']);
if (!$environment) {
return redirect()->route('dashboard');
return $this->redirectRoute('dashboard', navigate: true);
}
$application = $environment->applications->where('uuid', request()->route('application_uuid'))->first();
if (!$application) {
return redirect()->route('dashboard');
return $this->redirectRoute('dashboard', navigate: true);
}
$this->application = $application;
$mainServer = $application->destination->server;
$mainServer = $this->application->destination->server;
$servers = Server::ownedByCurrentTeam()->get();
$this->servers = $servers->filter(function ($server) use ($mainServer) {
return $server->id != $mainServer->id;

View File

@@ -1,6 +1,6 @@
<?php
namespace App\Http\Livewire\Project\Application;
namespace App\Livewire\Project\Application;
use App\Models\ApplicationDeploymentQueue;
use Livewire\Component;
@@ -18,7 +18,7 @@ class DeploymentLogs extends Component
public function polling()
{
$this->emit('deploymentFinished');
$this->dispatch('deploymentFinished');
$this->application_deployment_queue->refresh();
if (data_get($this->application_deployment_queue, 'status') == 'finished' || data_get($this->application_deployment_queue, 'status') == 'failed') {
$this->isKeepAliveOn = false;

View File

@@ -1,6 +1,6 @@
<?php
namespace App\Http\Livewire\Project\Application;
namespace App\Livewire\Project\Application;
use App\Enums\ApplicationDeploymentStatus;
use App\Models\Application;
@@ -18,7 +18,6 @@ class DeploymentNavbar extends Component
public Server $server;
public bool $is_debug_enabled = false;
protected $listeners = ['deploymentFinished'];
public function mount()
{
$this->application = Application::find($this->application_deployment_queue->application_id);
@@ -36,7 +35,7 @@ class DeploymentNavbar extends Component
$this->application->settings->is_debug_enabled = !$this->application->settings->is_debug_enabled;
$this->application->settings->save();
$this->is_debug_enabled = $this->application->settings->is_debug_enabled;
$this->emit('refreshQueue');
$this->dispatch('refreshQueue');
}
public function cancel()

View File

@@ -1,6 +1,6 @@
<?php
namespace App\Http\Livewire\Project\Application;
namespace App\Livewire\Project\Application;
use App\Models\Application;
use Illuminate\Support\Collection;

View File

@@ -1,6 +1,6 @@
<?php
namespace App\Http\Livewire\Project\Application;
namespace App\Livewire\Project\Application;
use App\Models\Application;
use Illuminate\Support\Collection;
@@ -29,8 +29,6 @@ class General extends Component
public ?string $initialDockerComposeLocation = null;
public ?string $initialDockerComposePrLocation = null;
public bool $is_static;
public $parsedServices = [];
public $parsedServiceDomains = [];
@@ -103,7 +101,7 @@ class General extends Component
try {
$this->parsedServices = $this->application->parseCompose();
} catch (\Throwable $e) {
$this->emit('error', $e->getMessage());
$this->dispatch('error', $e->getMessage());
}
$this->parsedServiceDomains = $this->application->docker_compose_domains ? json_decode($this->application->docker_compose_domains, true) : [];
@@ -123,7 +121,11 @@ class General extends Component
public function instantSave()
{
$this->application->settings->save();
$this->emit('success', 'Settings saved.');
$this->dispatch('success', 'Settings saved.');
$this->application->refresh();
if ($this->ports_exposes !== $this->application->ports_exposes) {
$this->resetDefaultLabels(false);
}
}
public function loadComposeFile($isInit = false)
{
@@ -132,7 +134,7 @@ class General extends Component
return;
}
['parsedServices' => $this->parsedServices, 'initialDockerComposeLocation' => $this->initialDockerComposeLocation, 'initialDockerComposePrLocation' => $this->initialDockerComposePrLocation] = $this->application->loadComposeFile($isInit);
$this->emit('success', 'Docker compose file loaded.');
$this->dispatch('success', 'Docker compose file loaded.');
} catch (\Throwable $e) {
$this->application->docker_compose_location = $this->initialDockerComposeLocation;
$this->application->docker_compose_pr_location = $this->initialDockerComposePrLocation;
@@ -149,14 +151,14 @@ class General extends Component
$this->parsedServiceDomains[$serviceName]['domain'] = $domain;
$this->application->docker_compose_domains = json_encode($this->parsedServiceDomains);
$this->application->save();
$this->emit('success', 'Domain generated.');
$this->dispatch('success', 'Domain generated.');
}
return $domain;
}
public function updatedApplicationBuildPack()
{
if ($this->application->build_pack !== 'nixpacks') {
$this->application->settings->is_static = $this->is_static = false;
$this->application->settings->is_static = false;
$this->application->settings->save();
}
if ($this->application->build_pack === 'dockercompose') {
@@ -194,7 +196,7 @@ class General extends Component
public function updatedApplicationFqdn()
{
$this->resetDefaultLabels(false);
$this->emit('success', 'Labels reseted to default!');
$this->dispatch('success', 'Labels reseted to default!');
}
public function submit($showToaster = true)
{
@@ -218,6 +220,7 @@ class General extends Component
});
$this->application->fqdn = $domains->implode(',');
}
if (data_get($this->application, 'dockerfile')) {
$port = get_port_from_dockerfile($this->application->dockerfile);
if ($port && !$this->application->ports_exposes) {
@@ -239,7 +242,7 @@ class General extends Component
$this->parsedServices = $this->application->parseCompose();
}
$this->application->save();
$showToaster && $this->emit('success', 'Application settings updated!');
$showToaster && $this->dispatch('success', 'Application settings updated!');
} catch (\Throwable $e) {
return handleError($e, $this);
} finally {

View File

@@ -1,8 +1,9 @@
<?php
namespace App\Http\Livewire\Project\Application;
namespace App\Livewire\Project\Application;
use App\Actions\Application\StopApplication;
use App\Events\ApplicationStatusChanged;
use App\Jobs\ContainerStatusJob;
use App\Jobs\ServerStatusJob;
use App\Models\Application;
@@ -15,13 +16,19 @@ class Heading extends Component
public array $parameters;
protected string $deploymentUuid;
public function getListeners()
{
$teamId = auth()->user()->currentTeam()->id;
return [
"echo-private:team.{$teamId},ApplicationStatusChanged" => 'check_status',
];
}
public function mount()
{
$this->parameters = get_route_parameters();
}
public function check_status()
public function check_status($showNotification = false)
{
if ($this->application->destination->server->isFunctional()) {
dispatch(new ContainerStatusJob($this->application->destination->server));
@@ -32,6 +39,7 @@ class Heading extends Component
} else {
dispatch(new ServerStatusJob($this->application->destination->server));
}
if ($showNotification) $this->dispatch('success', 'Application status updated.');
}
public function force_deploy_without_cache()
@@ -39,10 +47,30 @@ class Heading extends Component
$this->deploy(force_rebuild: true);
}
public function deployNew()
{
if ($this->application->build_pack === 'dockercompose' && is_null($this->application->docker_compose_raw)) {
$this->dispatch('error', 'Please load a Compose file first.');
return;
}
$this->setDeploymentUuid();
queue_application_deployment(
application_id: $this->application->id,
deployment_uuid: $this->deploymentUuid,
force_rebuild: false,
is_new_deployment: true,
);
return $this->redirectRoute('project.application.deployment', [
'project_uuid' => $this->parameters['project_uuid'],
'application_uuid' => $this->parameters['application_uuid'],
'deployment_uuid' => $this->deploymentUuid,
'environment_name' => $this->parameters['environment_name'],
], navigate: true);
}
public function deploy(bool $force_rebuild = false)
{
if ($this->application->build_pack === 'dockercompose' && is_null($this->application->docker_compose_raw)) {
$this->emit('error', 'Please load a Compose file first.');
$this->dispatch('error', 'Please load a Compose file first.');
return;
}
$this->setDeploymentUuid();
@@ -51,12 +79,12 @@ class Heading extends Component
deployment_uuid: $this->deploymentUuid,
force_rebuild: $force_rebuild,
);
return redirect()->route('project.application.deployment', [
$this->redirectRoute('project.application.deployment', [
'project_uuid' => $this->parameters['project_uuid'],
'application_uuid' => $this->parameters['application_uuid'],
'deployment_uuid' => $this->deploymentUuid,
'environment_name' => $this->parameters['environment_name'],
]);
], navigate: true);
}
protected function setDeploymentUuid()
@@ -72,6 +100,22 @@ class Heading extends Component
$this->application->save();
$this->application->refresh();
}
public function restartNew()
{
$this->setDeploymentUuid();
queue_application_deployment(
application_id: $this->application->id,
deployment_uuid: $this->deploymentUuid,
restart_only: true,
is_new_deployment: true,
);
return $this->redirectRoute('project.application.deployment', [
'project_uuid' => $this->parameters['project_uuid'],
'application_uuid' => $this->parameters['application_uuid'],
'deployment_uuid' => $this->deploymentUuid,
'environment_name' => $this->parameters['environment_name'],
], navigate: true);
}
public function restart()
{
$this->setDeploymentUuid();
@@ -80,11 +124,11 @@ class Heading extends Component
deployment_uuid: $this->deploymentUuid,
restart_only: true,
);
return redirect()->route('project.application.deployment', [
return $this->redirectRoute('project.application.deployment', [
'project_uuid' => $this->parameters['project_uuid'],
'application_uuid' => $this->parameters['application_uuid'],
'deployment_uuid' => $this->deploymentUuid,
'environment_name' => $this->parameters['environment_name'],
]);
], navigate: true);
}
}

View File

@@ -1,6 +1,6 @@
<?php
namespace App\Http\Livewire\Project\Application\Preview;
namespace App\Livewire\Project\Application\Preview;
use App\Models\Application;
use Illuminate\Support\Str;
@@ -46,7 +46,7 @@ class Form extends Component
$this->validate();
$this->application->preview_url_template = str_replace(' ', '', $this->application->preview_url_template);
$this->application->save();
$this->emit('success', 'Preview url template updated successfully.');
$this->dispatch('success', 'Preview url template updated successfully.');
$this->generate_real_url();
}
}

View File

@@ -1,6 +1,6 @@
<?php
namespace App\Http\Livewire\Project\Application;
namespace App\Livewire\Project\Application;
use App\Models\Application;
use App\Models\ApplicationPreview;
@@ -52,12 +52,12 @@ class Previews extends Component
force_rebuild: true,
pull_request_id: $pull_request_id,
);
return redirect()->route('project.application.deployment', [
return $this->redirectRoute('project.application.deployment', [
'project_uuid' => $this->parameters['project_uuid'],
'application_uuid' => $this->parameters['application_uuid'],
'deployment_uuid' => $this->deployment_uuid,
'environment_name' => $this->parameters['environment_name'],
]);
], navigate: true);
} catch (\Throwable $e) {
return handleError($e, $this);
}

View File

@@ -1,6 +1,6 @@
<?php
namespace App\Http\Livewire\Project\Application;
namespace App\Livewire\Project\Application;
use App\Models\Application;
use Illuminate\Support\Str;
@@ -29,13 +29,12 @@ class Rollback extends Component
commit: $commit,
force_rebuild: false,
);
return redirect()->route('project.application.deployment', [
return $this->redirectRoute('project.application.deployment', [
'project_uuid' => $this->parameters['project_uuid'],
'application_uuid' => $this->parameters['application_uuid'],
'deployment_uuid' => $deployment_uuid,
'environment_name' => $this->parameters['environment_name'],
]);
], navigate: true);
}
public function loadImages($showToast = false)
@@ -66,7 +65,7 @@ class Rollback extends Component
];
})->toArray();
}
$showToast && $this->emit('success', 'Images loaded.');
$showToast && $this->dispatch('success', 'Images loaded.');
return [];
} catch (\Throwable $e) {
return handleError($e, $this);

View File

@@ -1,6 +1,6 @@
<?php
namespace App\Http\Livewire\Project\Application;
namespace App\Livewire\Project\Application;
use App\Models\Application;
use App\Models\PrivateKey;
@@ -49,6 +49,6 @@ class Source extends Component
$this->application->git_commit_sha = 'HEAD';
}
$this->application->save();
$this->emit('success', 'Application source updated!');
$this->dispatch('success', 'Application source updated!');
}
}

View File

@@ -1,6 +1,6 @@
<?php
namespace App\Http\Livewire\Project;
namespace App\Livewire\Project;
use App\Models\Environment;
use App\Models\Project;
@@ -148,10 +148,10 @@ class CloneProject extends Component
}
$newService->parse();
}
return redirect()->route('project.resources', [
return $this->redirectRoute('project.resources', [
'project_uuid' => $newProject->uuid,
'environment_name' => $newEnvironment->name,
]);
], navigate: true);
} catch (\Exception $e) {
return handleError($e, $this);
}

View File

@@ -1,6 +1,6 @@
<?php
namespace App\Http\Livewire\Project\Database;
namespace App\Livewire\Project\Database;
use Livewire\Component;
use Spatie\Url\Url;
@@ -50,9 +50,9 @@ class BackupEdit extends Component
$url = $url->withoutQueryParameter('selectedBackupId');
$url = $url->withFragment('backups');
$url = $url->getPath() . "#{$url->getFragment()}";
return redirect()->to($url);
return $this->redirect($url,navigate: true);
} else {
redirect()->route('project.database.backups.all', $this->parameters);
return $this->redirectRoute('project.database.backups.all', $this->parameters);
}
}
@@ -63,9 +63,9 @@ class BackupEdit extends Component
$this->custom_validate();
$this->backup->save();
$this->backup->refresh();
$this->emit('success', 'Backup updated successfully');
$this->dispatch('success', 'Backup updated successfully');
} catch (\Throwable $e) {
$this->emit('error', $e->getMessage());
$this->dispatch('error', $e->getMessage());
}
}
@@ -90,9 +90,9 @@ class BackupEdit extends Component
}
$this->backup->save();
$this->backup->refresh();
$this->emit('success', 'Backup updated successfully');
$this->dispatch('success', 'Backup updated successfully');
} catch (\Throwable $e) {
$this->emit('error', $e->getMessage());
$this->dispatch('error', $e->getMessage());
}
}
}

View File

@@ -1,6 +1,6 @@
<?php
namespace App\Http\Livewire\Project\Database;
namespace App\Livewire\Project\Database;
use Illuminate\Support\Facades\Storage;
use Livewire\Component;
@@ -10,13 +10,21 @@ class BackupExecutions extends Component
public $backup;
public $executions;
public $setDeletableBackup;
protected $listeners = ['refreshBackupExecutions', 'deleteBackup'];
public function getListeners()
{
$userId = auth()->user()->id;
return [
"echo-private:team.{$userId},BackupCreated" => 'refreshBackupExecutions',
"refreshBackupExecutions",
"deleteBackup"
];
}
public function deleteBackup($exeuctionId)
{
$execution = $this->backup->executions()->where('id', $exeuctionId)->first();
if (is_null($execution)) {
$this->emit('error', 'Backup execution not found.');
$this->dispatch('error', 'Backup execution not found.');
return;
}
if ($execution->scheduledDatabaseBackup->database->getMorphClass() === 'App\Models\ServiceDatabase') {
@@ -25,15 +33,15 @@ class BackupExecutions extends Component
delete_backup_locally($execution->filename, $execution->scheduledDatabaseBackup->database->destination->server);
}
$execution->delete();
$this->emit('success', 'Backup deleted successfully.');
$this->emit('refreshBackupExecutions');
$this->dispatch('success', 'Backup deleted successfully.');
$this->dispatch('refreshBackupExecutions');
}
public function download($exeuctionId)
{
try {
$execution = $this->backup->executions()->where('id', $exeuctionId)->first();
if (is_null($execution)) {
$this->emit('error', 'Backup execution not found.');
$this->dispatch('error', 'Backup execution not found.');
return;
}
$filename = data_get($execution, 'filename');

View File

@@ -1,6 +1,6 @@
<?php
namespace App\Http\Livewire\Project\Database;
namespace App\Livewire\Project\Database;
use App\Jobs\DatabaseBackupJob;
use Livewire\Component;
@@ -13,6 +13,6 @@ class BackupNow extends Component
dispatch(new DatabaseBackupJob(
backup: $this->backup
));
$this->emit('success', 'Backup queued. It will be available in a few minutes.');
$this->dispatch('success', 'Backup queued. It will be available in a few minutes.');
}
}

View File

@@ -1,6 +1,6 @@
<?php
namespace App\Http\Livewire\Project\Database;
namespace App\Livewire\Project\Database;
use App\Models\ScheduledDatabaseBackup;
use Livewire\Component;
@@ -35,7 +35,7 @@ class CreateScheduledBackup extends Component
$this->validate();
$isValid = validate_cron_expression($this->frequency);
if (!$isValid) {
$this->emit('error', 'Invalid Cron / Human expression.');
$this->dispatch('error', 'Invalid Cron / Human expression.');
return;
}
$payload = [
@@ -57,9 +57,9 @@ class CreateScheduledBackup extends Component
$databaseBackup = ScheduledDatabaseBackup::create($payload);
if ($this->database->getMorphClass() === 'App\Models\ServiceDatabase') {
$this->emit('refreshScheduledBackups', $databaseBackup->id);
$this->dispatch('refreshScheduledBackups', $databaseBackup->id);
} else {
$this->emit('refreshScheduledBackups');
$this->dispatch('refreshScheduledBackups');
}
} catch (\Throwable $e) {
handleError($e, $this);

View File

@@ -1,6 +1,6 @@
<?php
namespace App\Http\Livewire\Project\Database;
namespace App\Livewire\Project\Database;
use App\Actions\Database\StartMariadb;
use App\Actions\Database\StartMongodb;
@@ -8,6 +8,7 @@ use App\Actions\Database\StartMysql;
use App\Actions\Database\StartPostgresql;
use App\Actions\Database\StartRedis;
use App\Actions\Database\StopDatabase;
use App\Events\DatabaseStatusChanged;
use App\Jobs\ContainerStatusJob;
use Livewire\Component;
@@ -16,21 +17,28 @@ class Heading extends Component
public $database;
public array $parameters;
protected $listeners = ['activityFinished'];
public function getListeners()
{
$userId = auth()->user()->id;
return [
"echo-private:user.{$userId},DatabaseStatusChanged" => 'activityFinished',
];
}
public function activityFinished()
{
$this->database->update([
'started_at' => now(),
]);
$this->emit('refresh');
$this->dispatch('refresh');
$this->check_status();
}
public function check_status()
public function check_status($showNotification = false)
{
dispatch_sync(new ContainerStatusJob($this->database->destination->server));
$this->database->refresh();
if ($showNotification) $this->dispatch('success', 'Database status updated.');
}
public function mount()
@@ -50,19 +58,19 @@ class Heading extends Component
{
if ($this->database->type() === 'standalone-postgresql') {
$activity = StartPostgresql::run($this->database);
$this->emit('newMonitorActivity', $activity->id);
$this->dispatch('newMonitorActivity', $activity->id);
} else if ($this->database->type() === 'standalone-redis') {
$activity = StartRedis::run($this->database);
$this->emit('newMonitorActivity', $activity->id);
$this->dispatch('newMonitorActivity', $activity->id);
} else if ($this->database->type() === 'standalone-mongodb') {
$activity = StartMongodb::run($this->database);
$this->emit('newMonitorActivity', $activity->id);
$this->dispatch('newMonitorActivity', $activity->id);
} else if ($this->database->type() === 'standalone-mysql') {
$activity = StartMysql::run($this->database);
$this->emit('newMonitorActivity', $activity->id);
$this->dispatch('newMonitorActivity', $activity->id);
} else if ($this->database->type() === 'standalone-mariadb') {
$activity = StartMariadb::run($this->database);
$this->emit('newMonitorActivity', $activity->id);
$this->dispatch('newMonitorActivity', $activity->id);
}
}
}

View File

@@ -1,7 +1,8 @@
<?php
namespace App\Http\Livewire\Project\Database;
namespace App\Livewire\Project\Database;
use Exception;
use Livewire\Component;
class InitScript extends Component
@@ -34,7 +35,7 @@ class InitScript extends Component
$this->script['index'] = $this->index;
$this->script['content'] = $this->content;
$this->script['filename'] = $this->filename;
$this->emitUp('save_init_script', $this->script);
$this->dispatch('save_init_script', $this->script);
} catch (Exception $e) {
return handleError($e, $this);
}
@@ -42,6 +43,6 @@ class InitScript extends Component
public function delete()
{
$this->emitUp('delete_init_script', $this->script);
$this->dispatch('delete_init_script', $this->script);
}
}

View File

@@ -1,6 +1,6 @@
<?php
namespace App\Http\Livewire\Project\Database\Mariadb;
namespace App\Livewire\Project\Database\Mariadb;
use App\Actions\Database\StartDatabaseProxy;
use App\Actions\Database\StopDatabaseProxy;
@@ -55,12 +55,12 @@ class General extends Component
try {
if (!$this->database->destination->server->isLogDrainEnabled()) {
$this->database->is_log_drain_enabled = false;
$this->emit('error', 'Log drain is not enabled on the server. Please enable it first.');
$this->dispatch('error', 'Log drain is not enabled on the server. Please enable it first.');
return;
}
$this->database->save();
$this->emit('success', 'Database updated successfully.');
$this->emit('success', 'You need to restart the service for the changes to take effect.');
$this->dispatch('success', 'Database updated successfully.');
$this->dispatch('success', 'You need to restart the service for the changes to take effect.');
} catch (Exception $e) {
return handleError($e, $this);
}
@@ -73,7 +73,7 @@ class General extends Component
}
$this->validate();
$this->database->save();
$this->emit('success', 'Database updated successfully.');
$this->dispatch('success', 'Database updated successfully.');
} catch (Exception $e) {
return handleError($e, $this);
}
@@ -82,23 +82,23 @@ class General extends Component
{
try {
if ($this->database->is_public && !$this->database->public_port) {
$this->emit('error', 'Public port is required.');
$this->dispatch('error', 'Public port is required.');
$this->database->is_public = false;
return;
}
if ($this->database->is_public) {
if (!str($this->database->status)->startsWith('running')) {
$this->emit('error', 'Database must be started to be publicly accessible.');
$this->dispatch('error', 'Database must be started to be publicly accessible.');
$this->database->is_public = false;
return;
}
StartDatabaseProxy::run($this->database);
$this->db_url_public = $this->database->getDbUrl();
$this->emit('success', 'Database is now publicly accessible.');
$this->dispatch('success', 'Database is now publicly accessible.');
} else {
StopDatabaseProxy::run($this->database);
$this->db_url_public = null;
$this->emit('success', 'Database is no longer publicly accessible.');
$this->dispatch('success', 'Database is no longer publicly accessible.');
}
$this->database->save();
} catch (\Throwable $e) {

View File

@@ -1,6 +1,6 @@
<?php
namespace App\Http\Livewire\Project\Database\Mongodb;
namespace App\Livewire\Project\Database\Mongodb;
use App\Actions\Database\StartDatabaseProxy;
use App\Actions\Database\StopDatabaseProxy;
@@ -54,12 +54,12 @@ class General extends Component
try {
if (!$this->database->destination->server->isLogDrainEnabled()) {
$this->database->is_log_drain_enabled = false;
$this->emit('error', 'Log drain is not enabled on the server. Please enable it first.');
$this->dispatch('error', 'Log drain is not enabled on the server. Please enable it first.');
return;
}
$this->database->save();
$this->emit('success', 'Database updated successfully.');
$this->emit('success', 'You need to restart the service for the changes to take effect.');
$this->dispatch('success', 'Database updated successfully.');
$this->dispatch('success', 'You need to restart the service for the changes to take effect.');
} catch (Exception $e) {
return handleError($e, $this);
}
@@ -75,7 +75,7 @@ class General extends Component
}
$this->validate();
$this->database->save();
$this->emit('success', 'Database updated successfully.');
$this->dispatch('success', 'Database updated successfully.');
} catch (Exception $e) {
return handleError($e, $this);
}
@@ -84,23 +84,23 @@ class General extends Component
{
try {
if ($this->database->is_public && !$this->database->public_port) {
$this->emit('error', 'Public port is required.');
$this->dispatch('error', 'Public port is required.');
$this->database->is_public = false;
return;
}
if ($this->database->is_public) {
if (!str($this->database->status)->startsWith('running')) {
$this->emit('error', 'Database must be started to be publicly accessible.');
$this->dispatch('error', 'Database must be started to be publicly accessible.');
$this->database->is_public = false;
return;
}
StartDatabaseProxy::run($this->database);
$this->db_url_public = $this->database->getDbUrl();
$this->emit('success', 'Database is now publicly accessible.');
$this->dispatch('success', 'Database is now publicly accessible.');
} else {
StopDatabaseProxy::run($this->database);
$this->db_url_public = null;
$this->emit('success', 'Database is no longer publicly accessible.');
$this->dispatch('success', 'Database is no longer publicly accessible.');
}
$this->database->save();
} catch (\Throwable $e) {

View File

@@ -1,6 +1,6 @@
<?php
namespace App\Http\Livewire\Project\Database\Mysql;
namespace App\Livewire\Project\Database\Mysql;
use App\Actions\Database\StartDatabaseProxy;
use App\Actions\Database\StopDatabaseProxy;
@@ -56,12 +56,12 @@ class General extends Component
try {
if (!$this->database->destination->server->isLogDrainEnabled()) {
$this->database->is_log_drain_enabled = false;
$this->emit('error', 'Log drain is not enabled on the server. Please enable it first.');
$this->dispatch('error', 'Log drain is not enabled on the server. Please enable it first.');
return;
}
$this->database->save();
$this->emit('success', 'Database updated successfully.');
$this->emit('success', 'You need to restart the service for the changes to take effect.');
$this->dispatch('success', 'Database updated successfully.');
$this->dispatch('success', 'You need to restart the service for the changes to take effect.');
} catch (Exception $e) {
return handleError($e, $this);
}
@@ -74,7 +74,7 @@ class General extends Component
}
$this->validate();
$this->database->save();
$this->emit('success', 'Database updated successfully.');
$this->dispatch('success', 'Database updated successfully.');
} catch (Exception $e) {
return handleError($e, $this);
}
@@ -83,23 +83,23 @@ class General extends Component
{
try {
if ($this->database->is_public && !$this->database->public_port) {
$this->emit('error', 'Public port is required.');
$this->dispatch('error', 'Public port is required.');
$this->database->is_public = false;
return;
}
if ($this->database->is_public) {
if (!str($this->database->status)->startsWith('running')) {
$this->emit('error', 'Database must be started to be publicly accessible.');
$this->dispatch('error', 'Database must be started to be publicly accessible.');
$this->database->is_public = false;
return;
}
StartDatabaseProxy::run($this->database);
$this->db_url_public = $this->database->getDbUrl();
$this->emit('success', 'Database is now publicly accessible.');
$this->dispatch('success', 'Database is now publicly accessible.');
} else {
StopDatabaseProxy::run($this->database);
$this->db_url_public = null;
$this->emit('success', 'Database is no longer publicly accessible.');
$this->dispatch('success', 'Database is no longer publicly accessible.');
}
$this->database->save();
} catch (\Throwable $e) {

View File

@@ -1,6 +1,6 @@
<?php
namespace App\Http\Livewire\Project\Database\Postgresql;
namespace App\Livewire\Project\Database\Postgresql;
use App\Actions\Database\StartDatabaseProxy;
use App\Actions\Database\StopDatabaseProxy;
@@ -62,12 +62,12 @@ class General extends Component
try {
if (!$this->database->destination->server->isLogDrainEnabled()) {
$this->database->is_log_drain_enabled = false;
$this->emit('error', 'Log drain is not enabled on the server. Please enable it first.');
$this->dispatch('error', 'Log drain is not enabled on the server. Please enable it first.');
return;
}
$this->database->save();
$this->emit('success', 'Database updated successfully.');
$this->emit('success', 'You need to restart the service for the changes to take effect.');
$this->dispatch('success', 'Database updated successfully.');
$this->dispatch('success', 'You need to restart the service for the changes to take effect.');
} catch (Exception $e) {
return handleError($e, $this);
}
@@ -76,23 +76,23 @@ class General extends Component
{
try {
if ($this->database->is_public && !$this->database->public_port) {
$this->emit('error', 'Public port is required.');
$this->dispatch('error', 'Public port is required.');
$this->database->is_public = false;
return;
}
if ($this->database->is_public) {
if (!str($this->database->status)->startsWith('running')) {
$this->emit('error', 'Database must be started to be publicly accessible.');
$this->dispatch('error', 'Database must be started to be publicly accessible.');
$this->database->is_public = false;
return;
}
StartDatabaseProxy::run($this->database);
$this->db_url_public = $this->database->getDbUrl();
$this->emit('success', 'Database is now publicly accessible.');
$this->dispatch('success', 'Database is now publicly accessible.');
} else {
StopDatabaseProxy::run($this->database);
$this->db_url_public = null;
$this->emit('success', 'Database is no longer publicly accessible.');
$this->dispatch('success', 'Database is no longer publicly accessible.');
}
$this->database->save();
} catch (\Throwable $e) {
@@ -105,7 +105,7 @@ class General extends Component
$this->database->init_scripts = filter($this->database->init_scripts, fn ($s) => $s['filename'] !== $script['filename']);
$this->database->init_scripts = array_merge($this->database->init_scripts, [$script]);
$this->database->save();
$this->emit('success', 'Init script saved successfully.');
$this->dispatch('success', 'Init script saved successfully.');
}
public function delete_init_script($script)
@@ -116,7 +116,7 @@ class General extends Component
$this->database->init_scripts = $collection->filter(fn ($s) => $s['filename'] !== $script['filename'])->toArray();
$this->database->save();
$this->refresh();
$this->emit('success', 'Init script deleted successfully.');
$this->dispatch('success', 'Init script deleted successfully.');
return;
}
}
@@ -134,7 +134,7 @@ class General extends Component
]);
$found = collect($this->database->init_scripts)->firstWhere('filename', $this->new_filename);
if ($found) {
$this->emit('error', 'Filename already exists.');
$this->dispatch('error', 'Filename already exists.');
return;
}
if (!isset($this->database->init_scripts)) {
@@ -148,7 +148,7 @@ class General extends Component
]
]);
$this->database->save();
$this->emit('success', 'Init script added successfully.');
$this->dispatch('success', 'Init script added successfully.');
$this->new_content = '';
$this->new_filename = '';
}
@@ -161,7 +161,7 @@ class General extends Component
}
$this->validate();
$this->database->save();
$this->emit('success', 'Database updated successfully.');
$this->dispatch('success', 'Database updated successfully.');
} catch (Exception $e) {
return handleError($e, $this);
}

View File

@@ -1,6 +1,6 @@
<?php
namespace App\Http\Livewire\Project\Database\Redis;
namespace App\Livewire\Project\Database\Redis;
use App\Actions\Database\StartDatabaseProxy;
use App\Actions\Database\StopDatabaseProxy;
@@ -48,12 +48,12 @@ class General extends Component
try {
if (!$this->database->destination->server->isLogDrainEnabled()) {
$this->database->is_log_drain_enabled = false;
$this->emit('error', 'Log drain is not enabled on the server. Please enable it first.');
$this->dispatch('error', 'Log drain is not enabled on the server. Please enable it first.');
return;
}
$this->database->save();
$this->emit('success', 'Database updated successfully.');
$this->emit('success', 'You need to restart the service for the changes to take effect.');
$this->dispatch('success', 'Database updated successfully.');
$this->dispatch('success', 'You need to restart the service for the changes to take effect.');
} catch (Exception $e) {
return handleError($e, $this);
}
@@ -66,7 +66,7 @@ class General extends Component
$this->database->redis_conf = null;
}
$this->database->save();
$this->emit('success', 'Database updated successfully.');
$this->dispatch('success', 'Database updated successfully.');
} catch (Exception $e) {
return handleError($e, $this);
}
@@ -75,23 +75,23 @@ class General extends Component
{
try {
if ($this->database->is_public && !$this->database->public_port) {
$this->emit('error', 'Public port is required.');
$this->dispatch('error', 'Public port is required.');
$this->database->is_public = false;
return;
}
if ($this->database->is_public) {
if (!str($this->database->status)->startsWith('running')) {
$this->emit('error', 'Database must be started to be publicly accessible.');
$this->dispatch('error', 'Database must be started to be publicly accessible.');
$this->database->is_public = false;
return;
}
StartDatabaseProxy::run($this->database);
$this->db_url_public = $this->database->getDbUrl();
$this->emit('success', 'Database is now publicly accessible.');
$this->dispatch('success', 'Database is now publicly accessible.');
} else {
StopDatabaseProxy::run($this->database);
$this->db_url_public = null;
$this->emit('success', 'Database is no longer publicly accessible.');
$this->dispatch('success', 'Database is no longer publicly accessible.');
}
$this->database->save();
} catch (\Throwable $e) {

View File

@@ -1,6 +1,6 @@
<?php
namespace App\Http\Livewire\Project\Database;
namespace App\Livewire\Project\Database;
use Livewire\Component;
@@ -38,7 +38,7 @@ class ScheduledBackups extends Component
public function delete($scheduled_backup_id): void
{
$this->database->scheduledBackups->find($scheduled_backup_id)->delete();
$this->emit('success', 'Scheduled backup deleted successfully.');
$this->dispatch('success', 'Scheduled backup deleted successfully.');
$this->refreshScheduledBackups();
}

View File

@@ -1,6 +1,6 @@
<?php
namespace App\Http\Livewire\Project;
namespace App\Livewire\Project;
use App\Models\Environment;
use Livewire\Component;
@@ -23,8 +23,8 @@ class DeleteEnvironment extends Component
$environment = Environment::findOrFail($this->environment_id);
if ($environment->isEmpty()) {
$environment->delete();
return redirect()->route('project.show', ['project_uuid' => $this->parameters['project_uuid']]);
return $this->redirectRoute('project.show', ['project_uuid' => $this->parameters['project_uuid']], navigate: true);
}
return $this->emit('error', 'Environment has defined resources, please delete them first.');
return $this->dispatch('error', 'Environment has defined resources, please delete them first.');
}
}

View File

@@ -1,6 +1,6 @@
<?php
namespace App\Http\Livewire\Project;
namespace App\Livewire\Project;
use App\Models\Project;
use Livewire\Component;
@@ -22,9 +22,9 @@ class DeleteProject extends Component
]);
$project = Project::findOrFail($this->project_id);
if ($project->applications->count() > 0) {
return $this->emit('error', 'Project has resources defined, please delete them first.');
return $this->dispatch('error', 'Project has resources defined, please delete them first.');
}
$project->delete();
return redirect()->route('projects');
return $this->redirectRoute('projects', navigate: true);
}
}

View File

@@ -1,6 +1,6 @@
<?php
namespace App\Http\Livewire\Project;
namespace App\Livewire\Project;
use App\Models\Project;
use Livewire\Component;
@@ -18,7 +18,7 @@ class Edit extends Component
$this->validate();
try {
$this->project->save();
$this->emit('saved');
$this->dispatch('saved');
} catch (\Throwable $e) {
return handleError($e, $this);
}

View File

@@ -0,0 +1,82 @@
<?php
namespace App\Livewire\Project\New;
use App\Models\EnvironmentVariable;
use App\Models\Project;
use App\Models\Service;
use Livewire\Component;
use Illuminate\Support\Str;
use Symfony\Component\Yaml\Yaml;
class DockerCompose extends Component
{
public string $dockerComposeRaw = '';
public string $envFile = '';
public array $parameters;
public array $query;
public function mount()
{
$this->parameters = get_route_parameters();
$this->query = request()->query();
if (isDev()) {
$this->dockerComposeRaw = 'services:
appsmith:
build:
context: .
dockerfile_inline: |
FROM nginx
ARG GIT_COMMIT
ARG GIT_BRANCH
RUN echo "Hello World ${GIT_COMMIT} ${GIT_BRANCH}"
args:
- GIT_COMMIT=cdc3b19
- GIT_BRANCH=${GIT_BRANCH}
environment:
- APPSMITH_MAIL_ENABLED=${APPSMITH_MAIL_ENABLED}
';
}
}
public function submit()
{
try {
$this->validate([
'dockerComposeRaw' => 'required'
]);
$this->dockerComposeRaw = Yaml::dump(Yaml::parse($this->dockerComposeRaw), 10, 2, Yaml::DUMP_MULTI_LINE_LITERAL_BLOCK);
$server_id = $this->query['server_id'];
$project = Project::where('uuid', $this->parameters['project_uuid'])->first();
$environment = $project->load(['environments'])->environments->where('name', $this->parameters['environment_name'])->first();
$service = Service::create([
'name' => 'service' . Str::random(10),
'docker_compose_raw' => $this->dockerComposeRaw,
'environment_id' => $environment->id,
'server_id' => (int) $server_id,
]);
$variables = parseEnvFormatToArray($this->envFile);
foreach ($variables as $key => $variable) {
EnvironmentVariable::create([
'key' => $key,
'value' => $variable,
'is_build_time' => false,
'is_preview' => false,
'service_id' => $service->id,
]);
}
$service->name = "service-$service->uuid";
$service->parse(isNew: true);
return $this->redirectRoute('project.service.configuration', [
'service_uuid' => $service->uuid,
'environment_name' => $environment->name,
'project_uuid' => $project->uuid,
]);
} catch (\Throwable $e) {
return handleError($e, $this);
}
}
}

View File

@@ -1,6 +1,6 @@
<?php
namespace App\Http\Livewire\Project\New;
namespace App\Livewire\Project\New;
use App\Models\Application;
use App\Models\Project;
@@ -64,12 +64,11 @@ class DockerImage extends Component
'name' => 'docker-image-' . $application->uuid,
'fqdn' => $fqdn
]);
redirect()->route('project.application.configuration', [
return $this->redirectRoute('project.application.configuration', [
'application_uuid' => $application->uuid,
'environment_name' => $environment->name,
'project_uuid' => $project->uuid,
]);
], navigate: true);
}
public function render()
{

View File

@@ -1,6 +1,6 @@
<?php
namespace App\Http\Livewire\Project\New;
namespace App\Livewire\Project\New;
use App\Models\Project;
use Livewire\Component;
@@ -13,6 +13,6 @@ class EmptyProject extends Component
'name' => generate_random_name(),
'team_id' => currentTeam()->id,
]);
return redirect()->route('project.show', ['project_uuid' => $project->uuid, 'environment_name' => 'production']);
return $this->redirectRoute('project.show', ['project_uuid' => $project->uuid, 'environment_name' => 'production'], navigate: true);
}
}

View File

@@ -1,6 +1,6 @@
<?php
namespace App\Http\Livewire\Project\New;
namespace App\Livewire\Project\New;
use App\Models\Application;
use App\Models\GithubApp;
@@ -72,7 +72,7 @@ class GithubPrivateRepository extends Component
$response = Http::withToken($this->token)->get("{$this->github_app->api_url}/installation/repositories?per_page=100&page={$this->page}");
$json = $response->json();
if ($response->status() !== 200) {
return $this->emit('error', $json['message']);
return $this->dispatch('error', $json['message']);
}
if ($json['total_count'] === 0) {
@@ -104,7 +104,7 @@ class GithubPrivateRepository extends Component
$response = Http::withToken($this->token)->get("{$this->github_app->api_url}/repos/{$this->selected_repository_owner}/{$this->selected_repository_repo}/branches?per_page=100&page={$this->page}");
$json = $response->json();
if ($response->status() !== 200) {
return $this->emit('error', $json['message']);
return $this->dispatch('error', $json['message']);
}
$this->total_branches_count = count($json);
@@ -151,11 +151,11 @@ class GithubPrivateRepository extends Component
$application->name = generate_application_name($this->selected_repository_owner . '/' . $this->selected_repository_repo, $this->selected_branch_name, $application->uuid);
$application->save();
redirect()->route('project.application.configuration', [
return $this->redirectRoute('project.application.configuration', [
'application_uuid' => $application->uuid,
'environment_name' => $environment->name,
'project_uuid' => $project->uuid,
]);
], navigate: true);
} catch (\Throwable $e) {
return handleError($e, $this);
}
@@ -170,6 +170,6 @@ class GithubPrivateRepository extends Component
$this->port = 3000;
$this->publish_directory = null;
}
$this->emit('success', 'Application settings updated!');
$this->dispatch('success', 'Application settings updated!');
}
}

View File

@@ -1,6 +1,6 @@
<?php
namespace App\Http\Livewire\Project\New;
namespace App\Livewire\Project\New;
use App\Models\Application;
use App\Models\GithubApp;
@@ -132,11 +132,11 @@ class GithubPrivateRepositoryDeployKey extends Component
$application->name = generate_random_name($application->uuid);
$application->save();
return redirect()->route('project.application.configuration', [
'project_uuid' => $project->uuid,
'environment_name' => $environment->name,
return $this->redirectRoute('project.application.configuration', [
'application_uuid' => $application->uuid,
]);
'environment_name' => $environment->name,
'project_uuid' => $project->uuid,
], navigate: true);
} catch (\Throwable $e) {
return handleError($e, $this);
}

View File

@@ -1,6 +1,6 @@
<?php
namespace App\Http\Livewire\Project\New;
namespace App\Livewire\Project\New;
use App\Models\Application;
use App\Models\GithubApp;
@@ -63,7 +63,7 @@ class PublicGitRepository extends Component
$this->port = 3000;
$this->publish_directory = null;
}
$this->emit('success', 'Application settings updated!');
$this->dispatch('success', 'Application settings updated!');
}
public function load_any_git()
{
@@ -184,11 +184,11 @@ class PublicGitRepository extends Component
$application->fqdn = $fqdn;
$application->save();
return redirect()->route('project.application.configuration', [
'project_uuid' => $project->uuid,
'environment_name' => $environment->name,
return $this->redirectRoute('project.application.configuration', [
'application_uuid' => $application->uuid,
]);
'environment_name' => $environment->name,
'project_uuid' => $project->uuid,
], navigate: true);
} catch (\Throwable $e) {
return handleError($e, $this);
}

View File

@@ -1,6 +1,6 @@
<?php
namespace App\Http\Livewire\Project\New;
namespace App\Livewire\Project\New;
use App\Models\Project;
use App\Models\Server;
@@ -47,23 +47,23 @@ class Select extends Component
}
public function render()
{
if ($this->search) $this->loadServices();
$this->loadServices();
return view('livewire.project.new.select');
}
public function updatedSelectedEnvironment()
{
return redirect()->route('project.resources.new', [
return $this->redirectRoute('project.resources.new', [
'project_uuid' => $this->parameters['project_uuid'],
'environment_name' => $this->selectedEnvironment,
]);
], navigate: true);
}
// public function addExistingPostgresql()
// {
// try {
// instantCommand("psql {$this->existingPostgresqlUrl} -c 'SELECT 1'");
// $this->emit('success', 'Successfully connected to the database.');
// $this->dispatch('success', 'Successfully connected to the database.');
// } catch (\Throwable $e) {
// return handleError($e, $this);
// }
@@ -89,7 +89,7 @@ class Select extends Component
$this->services = $this->allServices->filter(function ($service, $key) {
return str_contains(strtolower($key), strtolower($this->search));
});;
$this->emit('success', 'Successfully loaded services.');
$this->dispatch('success', 'Successfully loaded services.');
}
} catch (\Throwable $e) {
return handleError($e, $this);
@@ -109,9 +109,6 @@ class Select extends Component
if (count($this->servers) === 1) {
$server = $this->servers->first();
$this->setServer($server);
if (count($server->destinations()) === 1) {
$this->setDestination($server->destinations()->first()->uuid);
}
}
if (!is_null($this->server)) {
$foundServer = $this->servers->where('id', $this->server)->first();
@@ -133,7 +130,7 @@ class Select extends Component
public function setDestination(string $destination_uuid)
{
$this->destination_uuid = $destination_uuid;
redirect()->route('project.resources.new', [
return redirect()->route('project.resources.new', [
'project_uuid' => $this->parameters['project_uuid'],
'environment_name' => $this->parameters['environment_name'],
'type' => $this->type,

View File

@@ -1,6 +1,6 @@
<?php
namespace App\Http\Livewire\Project\New;
namespace App\Livewire\Project\New;
use App\Models\Application;
use App\Models\GithubApp;
@@ -70,10 +70,10 @@ CMD ["nginx", "-g", "daemon off;"]
'fqdn' => $fqdn
]);
redirect()->route('project.application.configuration', [
return $this->redirectRoute('project.application.configuration', [
'application_uuid' => $application->uuid,
'environment_name' => $environment->name,
'project_uuid' => $project->uuid,
]);
], navigate: true);
}
}

View File

@@ -1,6 +1,6 @@
<?php
namespace App\Http\Livewire\Project\Service;
namespace App\Livewire\Project\Service;
use App\Models\ServiceApplication;
use Livewire\Component;
@@ -30,18 +30,18 @@ class Application extends Component
{
if (!$this->application->service->destination->server->isLogDrainEnabled()) {
$this->application->is_log_drain_enabled = false;
$this->emit('error', 'Log drain is not enabled on the server. Please enable it first.');
$this->dispatch('error', 'Log drain is not enabled on the server. Please enable it first.');
return;
}
$this->application->save();
$this->emit('success', 'You need to restart the service for the changes to take effect.');
$this->dispatch('success', 'You need to restart the service for the changes to take effect.');
}
public function delete()
{
try {
$this->application->delete();
$this->emit('success', 'Application deleted successfully.');
return redirect()->route('project.service.configuration', $this->parameters);
$this->dispatch('success', 'Application deleted successfully.');
return $this->redirectRoute('project.service.configuration', $this->parameters, navigate: true);
} catch (\Throwable $e) {
return handleError($e, $this);
}
@@ -56,11 +56,11 @@ class Application extends Component
$this->validate();
$this->application->save();
updateCompose($this->application);
$this->emit('success', 'Application saved successfully.');
$this->dispatch('success', 'Application saved successfully.');
} catch (\Throwable $e) {
return handleError($e, $this);
} finally {
$this->emit('generateDockerCompose');
$this->dispatch('generateDockerCompose');
}
}
}

View File

@@ -1,6 +1,6 @@
<?php
namespace App\Http\Livewire\Project\Service;
namespace App\Livewire\Project\Service;
use App\Actions\Database\StartDatabaseProxy;
use App\Actions\Database\StopDatabaseProxy;
@@ -38,32 +38,32 @@ class Database extends Component
{
if (!$this->database->service->destination->server->isLogDrainEnabled()) {
$this->database->is_log_drain_enabled = false;
$this->emit('error', 'Log drain is not enabled on the server. Please enable it first.');
$this->dispatch('error', 'Log drain is not enabled on the server. Please enable it first.');
return;
}
$this->submit();
$this->emit('success', 'You need to restart the service for the changes to take effect.');
$this->dispatch('success', 'You need to restart the service for the changes to take effect.');
}
public function instantSave()
{
if ($this->database->is_public && !$this->database->public_port) {
$this->emit('error', 'Public port is required.');
$this->dispatch('error', 'Public port is required.');
$this->database->is_public = false;
return;
}
if ($this->database->is_public) {
if (!str($this->database->status)->startsWith('running')) {
$this->emit('error', 'Database must be started to be publicly accessible.');
$this->dispatch('error', 'Database must be started to be publicly accessible.');
$this->database->is_public = false;
return;
}
StartDatabaseProxy::run($this->database);
$this->db_url_public = $this->database->getServiceDatabaseUrl();
$this->emit('success', 'Database is now publicly accessible.');
$this->dispatch('success', 'Database is now publicly accessible.');
} else {
StopDatabaseProxy::run($this->database);
$this->db_url_public = null;
$this->emit('success', 'Database is no longer publicly accessible.');
$this->dispatch('success', 'Database is no longer publicly accessible.');
}
$this->submit();
}
@@ -77,11 +77,11 @@ class Database extends Component
$this->validate();
$this->database->save();
updateCompose($this->database);
$this->emit('success', 'Database saved successfully.');
$this->dispatch('success', 'Database saved successfully.');
} catch (\Throwable $e) {
ray($e);
} finally {
$this->emit('generateDockerCompose');
$this->dispatch('generateDockerCompose');
}
}
}

View File

@@ -1,6 +1,6 @@
<?php
namespace App\Http\Livewire\Project\Service;
namespace App\Livewire\Project\Service;
use App\Models\LocalFileVolume;
use App\Models\ServiceApplication;
@@ -42,7 +42,7 @@ class FileStorage extends Component
}
$this->fileStorage->save();
$this->fileStorage->saveStorageOnServer($this->service);
$this->emit('success', 'File updated successfully.');
$this->dispatch('success', 'File updated successfully.');
} catch (\Throwable $e) {
$this->fileStorage->setRawAttributes($original);
$this->fileStorage->save();

View File

@@ -1,6 +1,6 @@
<?php
namespace App\Http\Livewire\Project\Service;
namespace App\Livewire\Project\Service;
use App\Jobs\ContainerStatusJob;
use App\Models\Service;
@@ -13,7 +13,15 @@ class Index extends Component
public $databases;
public array $parameters;
public array $query;
protected $listeners = ["refreshStacks", "checkStatus"];
public function getListeners()
{
$userId = auth()->user()->id;
return [
"echo-private:user.{$userId},ServiceStatusChanged" => 'checkStatus',
"refreshStacks",
"checkStatus",
];
}
public function render()
{
return view('livewire.project.service.index');
@@ -30,6 +38,7 @@ class Index extends Component
{
dispatch_sync(new ContainerStatusJob($this->service->server));
$this->refreshStacks();
$this->dispatch('serviceStatusChanged');
}
public function refreshStacks()
{

View File

@@ -1,14 +1,11 @@
<?php
namespace App\Http\Livewire\Project\Service;
namespace App\Livewire\Project\Service;
use Livewire\Component;
class Modal extends Component
{
public function checkStatus() {
$this->emit('checkStatus');
}
public function render()
{
return view('livewire.project.service.modal');

View File

@@ -0,0 +1,72 @@
<?php
namespace App\Livewire\Project\Service;
use App\Actions\Service\StartService;
use App\Actions\Service\StopService;
use App\Events\ServiceStatusChanged;
use App\Jobs\ContainerStatusJob;
use App\Models\Service;
use Livewire\Component;
use Spatie\Activitylog\Models\Activity;
class Navbar extends Component
{
public Service $service;
public array $parameters;
public array $query;
public $isDeploymentProgress = false;
public function checkDeployments()
{
$activity = Activity::where('properties->type_uuid', $this->service->uuid)->latest()->first();
$status = data_get($activity, 'properties.status');
if ($status === 'queued' || $status === 'in_progress') {
$this->isDeploymentProgress = true;
} else {
$this->isDeploymentProgress = false;
}
}
public function getListeners()
{
return [
"serviceStatusChanged"
];
}
public function serviceStatusChanged()
{
$this->service->refresh();
}
public function render()
{
return view('livewire.project.service.navbar');
}
public function check_status($showNotification = false)
{
dispatch_sync(new ContainerStatusJob($this->service->destination->server));
$this->service->refresh();
if ($showNotification) $this->dispatch('success', 'Service status updated.');
}
public function deploy()
{
$this->checkDeployments();
if ($this->isDeploymentProgress) {
$this->dispatch('error', 'There is a deployment in progress.');
return;
}
$this->service->parse();
$activity = StartService::run($this->service);
$this->dispatch('newMonitorActivity', $activity->id);
}
public function stop(bool $forceCleanup = false)
{
StopService::run($this->service);
$this->service->refresh();
if ($forceCleanup) {
$this->dispatch('success', 'Force cleanup service successfully.');
} else {
$this->dispatch('success', 'Service stopped successfully.');
}
ServiceStatusChanged::dispatch();
}
}

View File

@@ -1,6 +1,6 @@
<?php
namespace App\Http\Livewire\Project\Service;
namespace App\Livewire\Project\Service;
use App\Models\Service;
use App\Models\ServiceApplication;

View File

@@ -1,6 +1,6 @@
<?php
namespace App\Http\Livewire\Project\Service;
namespace App\Livewire\Project\Service;
use Livewire\Component;
@@ -54,9 +54,9 @@ class StackForm extends Component
$this->service->parse();
$this->service->refresh();
$this->service->saveComposeConfigs();
$this->emit('refreshStacks');
$this->emit('refreshEnvs');
$this->emit('success', 'Service saved successfully.');
$this->dispatch('refreshStacks');
$this->dispatch('refreshEnvs');
$this->dispatch('success', 'Service saved successfully.');
} catch (\Throwable $e) {
return handleError($e, $this);
}

View File

@@ -1,6 +1,6 @@
<?php
namespace App\Http\Livewire\Project\Service;
namespace App\Livewire\Project\Service;
use App\Models\LocalPersistentVolume;
use Livewire\Component;
@@ -25,9 +25,9 @@ class Storage extends Component
'resource_type' => $this->resource->getMorphClass(),
]);
$this->resource->refresh();
$this->emit('success', 'Storage added successfully');
$this->emit('clearAddStorage');
$this->emit('refreshStorages');
$this->dispatch('success', 'Storage added successfully');
$this->dispatch('clearAddStorage');
$this->dispatch('refreshStorages');
} catch (\Throwable $e) {
return handleError($e, $this);
}

View File

@@ -0,0 +1,36 @@
<?php
namespace App\Livewire\Project\Shared;
use App\Jobs\DeleteResourceJob;
use Livewire\Component;
use Visus\Cuid2\Cuid2;
class Danger extends Component
{
public $resource;
public $projectUuid;
public $environmentName;
public ?string $modalId = null;
public function mount()
{
$this->modalId = new Cuid2(7);
$parameters = get_route_parameters();
$this->projectUuid = $parameters['project_uuid'];
$this->environmentName = $parameters['environment_name'];
}
public function delete()
{
try {
DeleteResourceJob::dispatchSync($this->resource);
return $this->redirectRoute('project.resources', [
'project_uuid' => $this->projectUuid,
'environment_name' => $this->environmentName
], navigate: true);
} catch (\Throwable $e) {
return handleError($e, $this);
}
}
}

View File

@@ -1,6 +1,6 @@
<?php
namespace App\Http\Livewire\Project\Shared;
namespace App\Livewire\Project\Shared;
use Livewire\Component;

View File

@@ -1,6 +1,6 @@
<?php
namespace App\Http\Livewire\Project\Shared\EnvironmentVariable;
namespace App\Livewire\Project\Shared\EnvironmentVariable;
use Livewire\Component;
@@ -33,7 +33,7 @@ class Add extends Component
{
$this->validate();
ray($this->key, $this->value, $this->is_build_time);
$this->emitUp('submit', [
$this->dispatch('saveKey', [
'key' => $this->key,
'value' => $this->value,
'is_build_time' => $this->is_build_time,

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