mirror of
https://github.com/ershisan99/coolify.git
synced 2025-12-18 12:33:06 +00:00
Compare commits
275 Commits
v4.0.0-bet
...
v4.0.0-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ab853cac87 | ||
|
|
51db2f797d | ||
|
|
0c90d3d0a1 | ||
|
|
51efe23690 | ||
|
|
e3ee84105c | ||
|
|
6cbd61ac6c | ||
|
|
638d0c8c99 | ||
|
|
aecc81fe9d | ||
|
|
c9a1437870 | ||
|
|
66b41b3d4c | ||
|
|
c41cfe2a2f | ||
|
|
5f2ad56529 | ||
|
|
cd842bc1b2 | ||
|
|
27b6aad53a | ||
|
|
64b58b7661 | ||
|
|
94960d96a9 | ||
|
|
2549244f97 | ||
|
|
5bfffce33b | ||
|
|
3a4f19f368 | ||
|
|
50e17ed932 | ||
|
|
a8fcd7aee4 | ||
|
|
87036cc49b | ||
|
|
e48842c6ec | ||
|
|
b9e405c497 | ||
|
|
f75effe022 | ||
|
|
a745f568f3 | ||
|
|
e2e3ad0358 | ||
|
|
ba769f5fb7 | ||
|
|
0126286731 | ||
|
|
7952202435 | ||
|
|
798acb8ee5 | ||
|
|
ef595dd4c2 | ||
|
|
70c662daf8 | ||
|
|
8ae385b9f9 | ||
|
|
802a0f7684 | ||
|
|
62c38c9859 | ||
|
|
27c36bec83 | ||
|
|
c6b8eabe10 | ||
|
|
967fca9eca | ||
|
|
40a239ddda | ||
|
|
99d07981cf | ||
|
|
b3ee6b7144 | ||
|
|
468ad7d904 | ||
|
|
f1aa97e374 | ||
|
|
3b6d3343c7 | ||
|
|
ab2f9f073f | ||
|
|
67131152cc | ||
|
|
3bda289428 | ||
|
|
fadfa0ad8e | ||
|
|
11a957c6c9 | ||
|
|
b46de99af9 | ||
|
|
03420076c9 | ||
|
|
549446abdf | ||
|
|
06ab2145ca | ||
|
|
5d088e530e | ||
|
|
123e6eddd7 | ||
|
|
2c17e431ac | ||
|
|
fe6073ba7d | ||
|
|
8a9ad04744 | ||
|
|
75d1ec4f42 | ||
|
|
a0abde8652 | ||
|
|
db13dd9304 | ||
|
|
638bcf9732 | ||
|
|
b06b465ffa | ||
|
|
02c8b9f471 | ||
|
|
1ff1664b6c | ||
|
|
52d84c5e9e | ||
|
|
e0289e2949 | ||
|
|
ff8d8371ad | ||
|
|
69343f974a | ||
|
|
2dc175be63 | ||
|
|
d93bf97919 | ||
|
|
4ea8916d53 | ||
|
|
f7fca69a23 | ||
|
|
f954ee15c3 | ||
|
|
3c54e01d87 | ||
|
|
00d708610d | ||
|
|
f3b04c1ef9 | ||
|
|
6b751f965b | ||
|
|
d3c9894479 | ||
|
|
f68aace445 | ||
|
|
f042c70b3c | ||
|
|
2116d79aad | ||
|
|
4bc63e283c | ||
|
|
d5804f99c2 | ||
|
|
dfc353ce54 | ||
|
|
8f65ddb754 | ||
|
|
29e750f0b2 | ||
|
|
b24661b876 | ||
|
|
bbbd605f32 | ||
|
|
ff13cb4e26 | ||
|
|
5cb572b6a5 | ||
|
|
d8b97e06cf | ||
|
|
becb4df950 | ||
|
|
20dc2b47fe | ||
|
|
67df166c20 | ||
|
|
87c3d0048c | ||
|
|
1c71ac78e2 | ||
|
|
41181cac12 | ||
|
|
41b6df0e6e | ||
|
|
4f3f98be0a | ||
|
|
7a97a4b69c | ||
|
|
6ae87466ca | ||
|
|
5159d47159 | ||
|
|
0138d04080 | ||
|
|
c803768e5f | ||
|
|
60c8e0d625 | ||
|
|
dd99ad0af8 | ||
|
|
24a1f02af5 | ||
|
|
601a1e128e | ||
|
|
ccb9769e67 | ||
|
|
d79da996d3 | ||
|
|
4f800f5331 | ||
|
|
a19a58338c | ||
|
|
8a80dbd5d8 | ||
|
|
ec5cca7b3e | ||
|
|
ce721c1764 | ||
|
|
f4d7c4f942 | ||
|
|
40716550ec | ||
|
|
f0ee26cd86 | ||
|
|
423dfc6280 | ||
|
|
6d9a66ff1b | ||
|
|
17c8872130 | ||
|
|
3ffa2b6b8d | ||
|
|
35134f2327 | ||
|
|
8ed4b540e1 | ||
|
|
47202a7951 | ||
|
|
fe6e76ad0d | ||
|
|
e9920f05f5 | ||
|
|
57a39f12bb | ||
|
|
ba85e3bc8b | ||
|
|
1fd12832ca | ||
|
|
a8807b8d09 | ||
|
|
dbc55233cb | ||
|
|
af6d94c0d8 | ||
|
|
e022492770 | ||
|
|
5e03979f9c | ||
|
|
956416b522 | ||
|
|
2846e049fa | ||
|
|
3ffd3fc819 | ||
|
|
63dff5961e | ||
|
|
4b024017ad | ||
|
|
720bb8c478 | ||
|
|
771dc30b81 | ||
|
|
6f97af096d | ||
|
|
3d28669cad | ||
|
|
efe043ec9d | ||
|
|
6bb79e10bc | ||
|
|
ba2e4c06f1 | ||
|
|
1bfb9637ba | ||
|
|
d7e9821582 | ||
|
|
08585f7e9a | ||
|
|
3eedb43b66 | ||
|
|
26aca6a4c0 | ||
|
|
3cbf8c281d | ||
|
|
d931d57373 | ||
|
|
4e680deb93 | ||
|
|
6a6275d4fa | ||
|
|
efd9087b74 | ||
|
|
6882ce8d0f | ||
|
|
eccd41217f | ||
|
|
538de9bd81 | ||
|
|
a249ee1b1f | ||
|
|
828fec9448 | ||
|
|
205995cabe | ||
|
|
4071e096bc | ||
|
|
14bac0f0d7 | ||
|
|
69c124032c | ||
|
|
b55bd298f2 | ||
|
|
86c2415210 | ||
|
|
82f3d54bc3 | ||
|
|
a6e76dfabc | ||
|
|
c1f6bf41f5 | ||
|
|
f934dfef33 | ||
|
|
19c66c6628 | ||
|
|
d7d948caf6 | ||
|
|
1b34337fe8 | ||
|
|
718603e37e | ||
|
|
9df0a2e545 | ||
|
|
7ffebd71f3 | ||
|
|
2f286a6595 | ||
|
|
13701f6030 | ||
|
|
91f224ddea | ||
|
|
8cffae10b2 | ||
|
|
1158b2f4db | ||
|
|
f542bcf428 | ||
|
|
22178df8ae | ||
|
|
acfe1daf9b | ||
|
|
fb3f71881f | ||
|
|
0f7546a4dc | ||
|
|
6ccafacc87 | ||
|
|
852c2df12e | ||
|
|
4c752951ab | ||
|
|
f32191b889 | ||
|
|
31251ef6cb | ||
|
|
e9365aa09b | ||
|
|
e5c860319f | ||
|
|
ceedd5225f | ||
|
|
61efdfb7c1 | ||
|
|
fcef5d902e | ||
|
|
2073b8949b | ||
|
|
5c59a752e3 | ||
|
|
caf6e3a23e | ||
|
|
8eb1c4da46 | ||
|
|
bab2f391ed | ||
|
|
131c6df82a | ||
|
|
9f44d0c47a | ||
|
|
dff7ed5b7b | ||
|
|
54b6472f3b | ||
|
|
04a36e5c90 | ||
|
|
b2f851272b | ||
|
|
778915599f | ||
|
|
a6f9527157 | ||
|
|
db976709c7 | ||
|
|
7e4947ba07 | ||
|
|
e2578a7dd0 | ||
|
|
7d028b15f5 | ||
|
|
d240bfda8b | ||
|
|
d59ec2548a | ||
|
|
e0cefc787a | ||
|
|
0496198d0f | ||
|
|
cbf7f1fa41 | ||
|
|
66587d864a | ||
|
|
b106705c8d | ||
|
|
5cc22a8271 | ||
|
|
cd22863a8c | ||
|
|
118a02f70d | ||
|
|
862177d61a | ||
|
|
41280aa780 | ||
|
|
3a987c8a6e | ||
|
|
29ca461a9a | ||
|
|
d48b72c160 | ||
|
|
ce49f26c53 | ||
|
|
02989678be | ||
|
|
810b55163c | ||
|
|
ac13ba0957 | ||
|
|
a3e088199f | ||
|
|
42ee4ca032 | ||
|
|
17deff4d86 | ||
|
|
8b6323b906 | ||
|
|
a696b5271a | ||
|
|
31959f26d4 | ||
|
|
45d2f80f69 | ||
|
|
53975fcf61 | ||
|
|
76296c1f19 | ||
|
|
c25baf69e1 | ||
|
|
f952512615 | ||
|
|
c6557eada8 | ||
|
|
2c2d74c0d6 | ||
|
|
028a2eb275 | ||
|
|
ce7fad5bef | ||
|
|
cd7852e4f9 | ||
|
|
7b022a2482 | ||
|
|
12d9b6538b | ||
|
|
335788c2d6 | ||
|
|
2352e4a71d | ||
|
|
dc03179bd1 | ||
|
|
cc72f416e8 | ||
|
|
3b67d0a8de | ||
|
|
0135ba7e89 | ||
|
|
a28a28cd23 | ||
|
|
b52680a2d8 | ||
|
|
0670e6c1d6 | ||
|
|
c3882b75c1 | ||
|
|
64b6f86a36 | ||
|
|
b9efc22253 | ||
|
|
e3d9eb0154 | ||
|
|
66f3967479 | ||
|
|
c54439e84c | ||
|
|
db4a4c74fc | ||
|
|
5c7ef80219 | ||
|
|
243d1c06fc | ||
|
|
ef25f7d800 | ||
|
|
45640ffdb1 | ||
|
|
ccb972dcb9 |
@@ -4,3 +4,7 @@ APP_KEY=
|
|||||||
|
|
||||||
DB_PASSWORD=
|
DB_PASSWORD=
|
||||||
REDIS_PASSWORD=
|
REDIS_PASSWORD=
|
||||||
|
|
||||||
|
PUSHER_APP_ID=
|
||||||
|
PUSHER_APP_KEY=
|
||||||
|
PUSHER_APP_SECRET=
|
||||||
|
|||||||
94
.github/workflows/development-build.yml
vendored
94
.github/workflows/development-build.yml
vendored
@@ -2,7 +2,7 @@ name: Development Build (v4)
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: ["next"]
|
branches-ignore: ["main", "v3"]
|
||||||
paths-ignore:
|
paths-ignore:
|
||||||
- .github/workflows/coolify-helper.yml
|
- .github/workflows/coolify-helper.yml
|
||||||
- docker/coolify-helper/Dockerfile
|
- docker/coolify-helper/Dockerfile
|
||||||
@@ -29,51 +29,51 @@ jobs:
|
|||||||
file: docker/prod-ssu/Dockerfile
|
file: docker/prod-ssu/Dockerfile
|
||||||
platforms: linux/amd64
|
platforms: linux/amd64
|
||||||
push: true
|
push: true
|
||||||
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:next
|
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.ref_name }}
|
||||||
aarch64:
|
aarch64:
|
||||||
runs-on: [self-hosted, arm64]
|
runs-on: [self-hosted, arm64]
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
packages: write
|
packages: write
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- name: Login to ghcr.io
|
- name: Login to ghcr.io
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@v2
|
||||||
with:
|
with:
|
||||||
registry: ${{ env.REGISTRY }}
|
registry: ${{ env.REGISTRY }}
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
- name: Build image and push to registry
|
- name: Build image and push to registry
|
||||||
uses: docker/build-push-action@v3
|
uses: docker/build-push-action@v3
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
file: docker/prod-ssu/Dockerfile
|
file: docker/prod-ssu/Dockerfile
|
||||||
platforms: linux/aarch64
|
platforms: linux/aarch64
|
||||||
push: true
|
push: true
|
||||||
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:next-aarch64
|
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.ref_name }}-aarch64
|
||||||
merge-manifest:
|
merge-manifest:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
packages: write
|
packages: write
|
||||||
needs: [amd64, aarch64]
|
needs: [amd64, aarch64]
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v2
|
uses: docker/setup-qemu-action@v2
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v2
|
uses: docker/setup-buildx-action@v2
|
||||||
- name: Login to ghcr.io
|
- name: Login to ghcr.io
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@v2
|
||||||
with:
|
with:
|
||||||
registry: ${{ env.REGISTRY }}
|
registry: ${{ env.REGISTRY }}
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
- name: Create & publish manifest
|
- name: Create & publish manifest
|
||||||
run: |
|
run: |
|
||||||
docker buildx imagetools create --append ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:next-aarch64 --tag ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:next
|
docker buildx imagetools create --append ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.ref_name }}-aarch64 --tag ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.ref_name }}
|
||||||
- uses: sarisia/actions-status-discord@v1
|
- uses: sarisia/actions-status-discord@v1
|
||||||
if: always()
|
if: always()
|
||||||
with:
|
with:
|
||||||
webhook: ${{ secrets.DISCORD_WEBHOOK_DEV_RELEASE_CHANNEL }}
|
webhook: ${{ secrets.DISCORD_WEBHOOK_DEV_RELEASE_CHANNEL }}
|
||||||
|
|||||||
@@ -22,8 +22,6 @@ You can ask for guidance anytime on our
|
|||||||
- Run `spin up` - You can notice that errors will be thrown. Don't worry.
|
- Run `spin up` - You can notice that errors will be thrown. Don't worry.
|
||||||
- If you see weird permission errors, especially on Mac, run `sudo spin up` instead.
|
- If you see weird permission errors, especially on Mac, run `sudo spin up` instead.
|
||||||
|
|
||||||
- Run `./scripts/run setup:dev` - This will generate a secret key for you, delete any existing database layouts, migrate database to the new layout, and seed your database.
|
|
||||||
|
|
||||||
### 4) Start development
|
### 4) Start development
|
||||||
You can login your Coolify instance at `localhost:8000` with `test@example.com` and `password`.
|
You can login your Coolify instance at `localhost:8000` with `test@example.com` and `password`.
|
||||||
|
|
||||||
@@ -31,7 +29,6 @@ Your horizon (Laravel scheduler): `localhost:8000/horizon` - Only reachable if y
|
|||||||
|
|
||||||
Mails are caught by Mailpit: `localhost:8025`
|
Mails are caught by Mailpit: `localhost:8025`
|
||||||
|
|
||||||
|
|
||||||
## New Service Contribution
|
## New Service Contribution
|
||||||
Check out the docs [here](https://coolify.io/docs/how-to-add-a-service).
|
Check out the docs [here](https://coolify.io/docs/how-to-add-a-service).
|
||||||
|
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ class PrepareCoolifyTask
|
|||||||
|
|
||||||
public function __invoke(): Activity
|
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);
|
dispatch($job);
|
||||||
$this->activity->refresh();
|
$this->activity->refresh();
|
||||||
return $this->activity;
|
return $this->activity;
|
||||||
|
|||||||
@@ -17,24 +17,24 @@ class RunRemoteProcess
|
|||||||
|
|
||||||
public bool $hide_from_output;
|
public bool $hide_from_output;
|
||||||
|
|
||||||
public bool $is_finished;
|
|
||||||
|
|
||||||
public bool $ignore_errors;
|
public bool $ignore_errors;
|
||||||
|
|
||||||
|
public $call_event_on_finish = null;
|
||||||
|
|
||||||
protected $time_start;
|
protected $time_start;
|
||||||
|
|
||||||
protected $current_time;
|
protected $current_time;
|
||||||
|
|
||||||
protected $last_write_at = 0;
|
protected $last_write_at = 0;
|
||||||
|
|
||||||
protected $throttle_interval_ms = 500;
|
protected $throttle_interval_ms = 200;
|
||||||
|
|
||||||
protected int $counter = 1;
|
protected int $counter = 1;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new job instance.
|
* 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) {
|
if ($activity->getExtraProperty('type') !== ActivityTypes::INLINE->value) {
|
||||||
@@ -43,8 +43,8 @@ class RunRemoteProcess
|
|||||||
|
|
||||||
$this->activity = $activity;
|
$this->activity = $activity;
|
||||||
$this->hide_from_output = $hide_from_output;
|
$this->hide_from_output = $hide_from_output;
|
||||||
$this->is_finished = $is_finished;
|
|
||||||
$this->ignore_errors = $ignore_errors;
|
$this->ignore_errors = $ignore_errors;
|
||||||
|
$this->call_event_on_finish = $call_event_on_finish;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function decodeOutput(?Activity $activity = null): string
|
public static function decodeOutput(?Activity $activity = null): string
|
||||||
@@ -74,17 +74,29 @@ class RunRemoteProcess
|
|||||||
$this->time_start = hrtime(true);
|
$this->time_start = hrtime(true);
|
||||||
|
|
||||||
$status = ProcessStatus::IN_PROGRESS;
|
$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) {
|
if ($this->activity->properties->get('status') === ProcessStatus::ERROR->value) {
|
||||||
$status = ProcessStatus::ERROR;
|
$status = ProcessStatus::ERROR;
|
||||||
} else {
|
} else {
|
||||||
if (($processResult->exitCode() == 0 && $this->is_finished) || $this->activity->properties->get('status') === ProcessStatus::FINISHED->value) {
|
if ($processResult->exitCode() == 0) {
|
||||||
$status = ProcessStatus::FINISHED;
|
$status = ProcessStatus::FINISHED;
|
||||||
}
|
}
|
||||||
if ($processResult->exitCode() != 0 && !$this->ignore_errors) {
|
if ($processResult->exitCode() != 0 && !$this->ignore_errors) {
|
||||||
$status = ProcessStatus::ERROR;
|
$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([
|
$this->activity->properties = $this->activity->properties->merge([
|
||||||
@@ -97,7 +109,15 @@ class RunRemoteProcess
|
|||||||
if ($processResult->exitCode() != 0 && !$this->ignore_errors) {
|
if ($processResult->exitCode() != 0 && !$this->ignore_errors) {
|
||||||
throw new \RuntimeException($processResult->errorOutput(), $processResult->exitCode());
|
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;
|
return $processResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,7 +137,6 @@ class RunRemoteProcess
|
|||||||
}
|
}
|
||||||
$this->current_time = $this->elapsedTime();
|
$this->current_time = $this->elapsedTime();
|
||||||
$this->activity->description = $this->encodeOutput($type, $output);
|
$this->activity->description = $this->encodeOutput($type, $output);
|
||||||
|
|
||||||
if ($this->isAfterLastThrottle()) {
|
if ($this->isAfterLastThrottle()) {
|
||||||
// Let's write to database.
|
// Let's write to database.
|
||||||
DB::transaction(function () {
|
DB::transaction(function () {
|
||||||
|
|||||||
@@ -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 pull";
|
||||||
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml up -d";
|
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml up -d";
|
||||||
$this->commands[] = "echo '{$database->name} started.'";
|
$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()
|
private function generate_local_persistent_volumes()
|
||||||
|
|||||||
@@ -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 pull";
|
||||||
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml up -d";
|
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml up -d";
|
||||||
$this->commands[] = "echo '{$database->name} started.'";
|
$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()
|
private function generate_local_persistent_volumes()
|
||||||
|
|||||||
@@ -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 pull";
|
||||||
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml up -d";
|
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml up -d";
|
||||||
$this->commands[] = "echo '{$database->name} started.'";
|
$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()
|
private function generate_local_persistent_volumes()
|
||||||
|
|||||||
@@ -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 pull";
|
||||||
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml up -d";
|
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml up -d";
|
||||||
$this->commands[] = "echo '{$database->name} started.'";
|
$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()
|
private function generate_local_persistent_volumes()
|
||||||
|
|||||||
@@ -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 pull";
|
||||||
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml up -d";
|
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml up -d";
|
||||||
$this->commands[] = "echo '{$database->name} started.'";
|
$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()
|
private function generate_local_persistent_volumes()
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Actions\Database;
|
namespace App\Actions\Database;
|
||||||
|
|
||||||
|
use App\Events\DatabaseStatusChanged;
|
||||||
use App\Models\StandaloneMariadb;
|
use App\Models\StandaloneMariadb;
|
||||||
use App\Models\StandaloneMongodb;
|
use App\Models\StandaloneMongodb;
|
||||||
use App\Models\StandaloneMysql;
|
use App\Models\StandaloneMysql;
|
||||||
|
|||||||
@@ -21,7 +21,10 @@ class CheckProxy
|
|||||||
$status = getContainerStatus($server, 'coolify-proxy_traefik');
|
$status = getContainerStatus($server, 'coolify-proxy_traefik');
|
||||||
$server->proxy->set('status', $status);
|
$server->proxy->set('status', $status);
|
||||||
$server->save();
|
$server->save();
|
||||||
return false;
|
if ($status === 'running') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
} else {
|
} else {
|
||||||
$status = getContainerStatus($server, 'coolify-proxy');
|
$status = getContainerStatus($server, 'coolify-proxy');
|
||||||
if ($status === 'running') {
|
if ($status === 'running') {
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ class InstallDocker
|
|||||||
}
|
}
|
||||||
$command = $command->merge([
|
$command = $command->merge([
|
||||||
"echo 'Installing Docker Engine...'",
|
"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)...'",
|
"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",
|
"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",
|
"echo '{$config}' | base64 -d > /etc/docker/daemon.json.coolify",
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ class InstallLogDrain
|
|||||||
$type = 'highlight';
|
$type = 'highlight';
|
||||||
} else if ($server->settings->is_logdrain_axiom_enabled) {
|
} else if ($server->settings->is_logdrain_axiom_enabled) {
|
||||||
$type = 'axiom';
|
$type = 'axiom';
|
||||||
|
} else if ($server->settings->is_logdrain_custom_enabled) {
|
||||||
|
$type = 'custom';
|
||||||
} else {
|
} else {
|
||||||
$type = 'none';
|
$type = 'none';
|
||||||
}
|
}
|
||||||
@@ -114,15 +116,23 @@ class InstallLogDrain
|
|||||||
json_date_format iso8601
|
json_date_format iso8601
|
||||||
tls On
|
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 {
|
} else {
|
||||||
throw new \Exception('Unknown log drain type.');
|
throw new \Exception('Unknown log drain type.');
|
||||||
}
|
}
|
||||||
$parsers = base64_encode("
|
if ($type !== 'custom') {
|
||||||
|
$parsers = base64_encode("
|
||||||
[PARSER]
|
[PARSER]
|
||||||
Name empty_line_skipper
|
Name empty_line_skipper
|
||||||
Format regex
|
Format regex
|
||||||
Regex /^(?!\s*$).+/
|
Regex /^(?!\s*$).+/
|
||||||
");
|
");
|
||||||
|
}
|
||||||
$compose = base64_encode("
|
$compose = base64_encode("
|
||||||
services:
|
services:
|
||||||
coolify-log-drain:
|
coolify-log-drain:
|
||||||
@@ -179,6 +189,12 @@ Files:
|
|||||||
"echo AXIOM_DATASET_NAME={$server->settings->logdrain_axiom_dataset_name} >> $config_path/.env",
|
"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",
|
"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 = [
|
$restart_command = [
|
||||||
"echo 'Stopping old Fluent Bit'",
|
"echo 'Stopping old Fluent Bit'",
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ class UpdateCoolify
|
|||||||
try {
|
try {
|
||||||
$settings = InstanceSettings::get();
|
$settings = InstanceSettings::get();
|
||||||
ray('Running InstanceAutoUpdateJob');
|
ray('Running InstanceAutoUpdateJob');
|
||||||
$this->server = Server::find(0)->first();
|
$this->server = Server::find(0);
|
||||||
if (!$this->server) {
|
if (!$this->server) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
42
app/Actions/Service/DeleteService.php
Normal file
42
app/Actions/Service/DeleteService.php
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,24 +11,25 @@ class StartService
|
|||||||
use AsAction;
|
use AsAction;
|
||||||
public function handle(Service $service)
|
public function handle(Service $service)
|
||||||
{
|
{
|
||||||
|
ray('Starting service: ' . $service->name);
|
||||||
$network = $service->destination->network;
|
$network = $service->destination->network;
|
||||||
$service->saveComposeConfigs();
|
$service->saveComposeConfigs();
|
||||||
$commands[] = "cd " . $service->workdir();
|
$commands[] = "cd " . $service->workdir();
|
||||||
$commands[] = "echo 'Saved configuration files to {$service->workdir()}.'";
|
$commands[] = "echo 'Saved configuration files to {$service->workdir()}.'";
|
||||||
$commands[] = "echo 'Creating Docker network.'";
|
$commands[] = "echo 'Creating Docker network.'";
|
||||||
$commands[] = "docker network create --attachable '{$service->uuid}' >/dev/null 2>&1 || true";
|
$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 'Starting service $service->name on {$service->server->name}.'";
|
||||||
$commands[] = "echo 'Pulling images.'";
|
$commands[] = "echo 'Pulling images.'";
|
||||||
$commands[] = "docker compose pull";
|
$commands[] = "docker compose pull";
|
||||||
$commands[] = "echo 'Starting containers.'";
|
$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";
|
$commands[] = "docker network connect $service->uuid coolify-proxy >/dev/null 2>&1 || true";
|
||||||
$compose = data_get($service,'docker_compose',[]);
|
$compose = data_get($service, 'docker_compose', []);
|
||||||
$serviceNames = data_get(Yaml::parse($compose),'services',[]);
|
$serviceNames = data_get(Yaml::parse($compose), 'services', []);
|
||||||
foreach($serviceNames as $serviceName => $serviceConfig){
|
foreach ($serviceNames as $serviceName => $serviceConfig) {
|
||||||
$commands[] = "docker network connect --alias {$serviceName}-{$service->uuid} $network {$serviceName}-{$service->uuid} || true";
|
$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;
|
return $activity;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ class StopService
|
|||||||
use AsAction;
|
use AsAction;
|
||||||
public function handle(Service $service)
|
public function handle(Service $service)
|
||||||
{
|
{
|
||||||
|
ray('Stopping service: ' . $service->name);
|
||||||
$applications = $service->applications()->get();
|
$applications = $service->applications()->get();
|
||||||
foreach ($applications as $application) {
|
foreach ($applications as $application) {
|
||||||
instant_remote_process(["docker rm -f {$application->name}-{$service->uuid}"], $service->server);
|
instant_remote_process(["docker rm -f {$application->name}-{$service->uuid}"], $service->server);
|
||||||
|
|||||||
25
app/Actions/Shared/PullImage.php
Normal file
25
app/Actions/Shared/PullImage.php
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Actions\Shared;
|
||||||
|
|
||||||
|
use App\Models\Service;
|
||||||
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
|
|
||||||
|
class PullImage
|
||||||
|
{
|
||||||
|
use AsAction;
|
||||||
|
public function handle(Service $resource)
|
||||||
|
{
|
||||||
|
$resource->saveComposeConfigs();
|
||||||
|
|
||||||
|
$commands[] = "cd " . $resource->workdir();
|
||||||
|
$commands[] = "echo 'Saved configuration files to {$resource->workdir()}.'";
|
||||||
|
$commands[] = "docker compose pull";
|
||||||
|
|
||||||
|
$server = data_get($resource, 'server');
|
||||||
|
|
||||||
|
if (!$server) return;
|
||||||
|
|
||||||
|
instant_remote_process($commands, $resource->server);
|
||||||
|
}
|
||||||
|
}
|
||||||
33
app/Console/Commands/Dev.php
Normal file
33
app/Console/Commands/Dev.php
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use App\Models\InstanceSettings;
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
use Illuminate\Support\Facades\Artisan;
|
||||||
|
use Illuminate\Support\Facades\Process;
|
||||||
|
|
||||||
|
class Dev extends Command
|
||||||
|
{
|
||||||
|
protected $signature = 'dev:init';
|
||||||
|
protected $description = 'Init the app in dev mode';
|
||||||
|
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
// Generate APP_KEY if not exists
|
||||||
|
if (empty(env('APP_KEY'))) {
|
||||||
|
echo "Generating APP_KEY.\n";
|
||||||
|
Artisan::call('key:generate');
|
||||||
|
}
|
||||||
|
// Seed database if it's empty
|
||||||
|
$settings = InstanceSettings::find(0);
|
||||||
|
if (!$settings) {
|
||||||
|
echo "Initializing instance, seeding database.\n";
|
||||||
|
Artisan::call('migrate --seed');
|
||||||
|
} else {
|
||||||
|
echo "Instance already initialized.\n";
|
||||||
|
}
|
||||||
|
// Set permissions
|
||||||
|
Process::run(['chmod', '-R', 'o+rwx', '.']);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,7 +18,6 @@ use App\Models\StandalonePostgresql;
|
|||||||
use App\Models\StandaloneRedis;
|
use App\Models\StandaloneRedis;
|
||||||
use Illuminate\Console\Command;
|
use Illuminate\Console\Command;
|
||||||
use Illuminate\Support\Facades\Http;
|
use Illuminate\Support\Facades\Http;
|
||||||
use Illuminate\Support\Facades\Storage;
|
|
||||||
|
|
||||||
class Init extends Command
|
class Init extends Command
|
||||||
{
|
{
|
||||||
@@ -36,15 +35,30 @@ class Init extends Command
|
|||||||
}
|
}
|
||||||
$this->cleanup_in_progress_application_deployments();
|
$this->cleanup_in_progress_application_deployments();
|
||||||
$this->cleanup_stucked_helper_containers();
|
$this->cleanup_stucked_helper_containers();
|
||||||
|
|
||||||
|
try {
|
||||||
|
setup_dynamic_configuration();
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
echo "Could not setup dynamic configuration: {$e->getMessage()}\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
$settings = InstanceSettings::get();
|
||||||
|
if (!is_null(env('AUTOUPDATE', null))) {
|
||||||
|
if (env('AUTOUPDATE') == true) {
|
||||||
|
$settings->update(['is_auto_update_enabled' => true]);
|
||||||
|
} else {
|
||||||
|
$settings->update(['is_auto_update_enabled' => false]);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
private function cleanup_stucked_helper_containers() {
|
private function cleanup_stucked_helper_containers()
|
||||||
|
{
|
||||||
$servers = Server::all();
|
$servers = Server::all();
|
||||||
foreach ($servers as $server) {
|
foreach ($servers as $server) {
|
||||||
if ($server->isFunctional()) {
|
if ($server->isFunctional()) {
|
||||||
CleanupHelperContainersJob::dispatch($server);
|
CleanupHelperContainersJob::dispatch($server);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
private function alive()
|
private function alive()
|
||||||
{
|
{
|
||||||
@@ -66,7 +80,7 @@ class Init extends Command
|
|||||||
// private function cleanup_ssh()
|
// private function cleanup_ssh()
|
||||||
// {
|
// {
|
||||||
|
|
||||||
// TODO: it will cleanup id.root@host.docker.internal
|
// TODO: it will cleanup id.root@host.docker.internal
|
||||||
// try {
|
// try {
|
||||||
// $files = Storage::allFiles('ssh/keys');
|
// $files = Storage::allFiles('ssh/keys');
|
||||||
// foreach ($files as $file) {
|
// foreach ($files as $file) {
|
||||||
@@ -85,7 +99,7 @@ class Init extends Command
|
|||||||
// Cleanup any failed deployments
|
// Cleanup any failed deployments
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$halted_deployments = ApplicationDeploymentQueue::where('status', '==', 'in_progress')->get();
|
$halted_deployments = ApplicationDeploymentQueue::where('status', '==', ApplicationDeploymentStatus::IN_PROGRESS)->where('status', '==', ApplicationDeploymentStatus::QUEUED)->get();
|
||||||
foreach ($halted_deployments as $deployment) {
|
foreach ($halted_deployments as $deployment) {
|
||||||
$deployment->status = ApplicationDeploymentStatus::FAILED->value;
|
$deployment->status = ApplicationDeploymentStatus::FAILED->value;
|
||||||
$deployment->save();
|
$deployment->save();
|
||||||
@@ -101,16 +115,19 @@ class Init extends Command
|
|||||||
$applications = Application::all();
|
$applications = Application::all();
|
||||||
foreach ($applications as $application) {
|
foreach ($applications as $application) {
|
||||||
if (!data_get($application, 'environment')) {
|
if (!data_get($application, 'environment')) {
|
||||||
ray('Application without environment', $application->name);
|
echo 'Application without environment: ' . $application->name . ' soft deleting\n';
|
||||||
$application->delete();
|
$application->delete();
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
if (!$application->destination()) {
|
if (!$application->destination()) {
|
||||||
ray('Application without destination', $application->name);
|
echo 'Application without destination: ' . $application->name . ' soft deleting\n';
|
||||||
$application->delete();
|
$application->delete();
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
if (!data_get($application, 'destination.server')) {
|
if (!data_get($application, 'destination.server')) {
|
||||||
ray('Application without server', $application->name);
|
echo 'Application without server: ' . $application->name . ' soft deleting\n';
|
||||||
$application->delete();
|
$application->delete();
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
@@ -120,16 +137,19 @@ class Init extends Command
|
|||||||
$postgresqls = StandalonePostgresql::all();
|
$postgresqls = StandalonePostgresql::all();
|
||||||
foreach ($postgresqls as $postgresql) {
|
foreach ($postgresqls as $postgresql) {
|
||||||
if (!data_get($postgresql, 'environment')) {
|
if (!data_get($postgresql, 'environment')) {
|
||||||
ray('Postgresql without environment', $postgresql->name);
|
echo 'Postgresql without environment: ' . $postgresql->name . ' soft deleting\n';
|
||||||
$postgresql->delete();
|
$postgresql->delete();
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
if (!$postgresql->destination()) {
|
if (!$postgresql->destination()) {
|
||||||
ray('Postgresql without destination', $postgresql->name);
|
echo 'Postgresql without destination: ' . $postgresql->name . ' soft deleting\n';
|
||||||
$postgresql->delete();
|
$postgresql->delete();
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
if (!data_get($postgresql, 'destination.server')) {
|
if (!data_get($postgresql, 'destination.server')) {
|
||||||
ray('Postgresql without server', $postgresql->name);
|
echo 'Postgresql without server: ' . $postgresql->name . ' soft deleting\n';
|
||||||
$postgresql->delete();
|
$postgresql->delete();
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
@@ -139,16 +159,19 @@ class Init extends Command
|
|||||||
$redis = StandaloneRedis::all();
|
$redis = StandaloneRedis::all();
|
||||||
foreach ($redis as $redis) {
|
foreach ($redis as $redis) {
|
||||||
if (!data_get($redis, 'environment')) {
|
if (!data_get($redis, 'environment')) {
|
||||||
ray('Redis without environment', $redis->name);
|
echo 'Redis without environment: ' . $redis->name . ' soft deleting\n';
|
||||||
$redis->delete();
|
$redis->delete();
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
if (!$redis->destination()) {
|
if (!$redis->destination()) {
|
||||||
ray('Redis without destination', $redis->name);
|
echo 'Redis without destination: ' . $redis->name . ' soft deleting\n';
|
||||||
$redis->delete();
|
$redis->delete();
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
if (!data_get($redis, 'destination.server')) {
|
if (!data_get($redis, 'destination.server')) {
|
||||||
ray('Redis without server', $redis->name);
|
echo 'Redis without server: ' . $redis->name . ' soft deleting\n';
|
||||||
$redis->delete();
|
$redis->delete();
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
@@ -159,16 +182,19 @@ class Init extends Command
|
|||||||
$mongodbs = StandaloneMongodb::all();
|
$mongodbs = StandaloneMongodb::all();
|
||||||
foreach ($mongodbs as $mongodb) {
|
foreach ($mongodbs as $mongodb) {
|
||||||
if (!data_get($mongodb, 'environment')) {
|
if (!data_get($mongodb, 'environment')) {
|
||||||
ray('Mongodb without environment', $mongodb->name);
|
echo 'Mongodb without environment: ' . $mongodb->name . ' soft deleting\n';
|
||||||
$mongodb->delete();
|
$mongodb->delete();
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
if (!$mongodb->destination()) {
|
if (!$mongodb->destination()) {
|
||||||
ray('Mongodb without destination', $mongodb->name);
|
echo 'Mongodb without destination: ' . $mongodb->name . ' soft deleting\n';
|
||||||
$mongodb->delete();
|
$mongodb->delete();
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
if (!data_get($mongodb, 'destination.server')) {
|
if (!data_get($mongodb, 'destination.server')) {
|
||||||
ray('Mongodb without server', $mongodb->name);
|
echo 'Mongodb without server: ' . $mongodb->name . ' soft deleting\n';
|
||||||
$mongodb->delete();
|
$mongodb->delete();
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
@@ -179,16 +205,19 @@ class Init extends Command
|
|||||||
$mysqls = StandaloneMysql::all();
|
$mysqls = StandaloneMysql::all();
|
||||||
foreach ($mysqls as $mysql) {
|
foreach ($mysqls as $mysql) {
|
||||||
if (!data_get($mysql, 'environment')) {
|
if (!data_get($mysql, 'environment')) {
|
||||||
ray('Mysql without environment', $mysql->name);
|
echo 'Mysql without environment: ' . $mysql->name . ' soft deleting\n';
|
||||||
$mysql->delete();
|
$mysql->delete();
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
if (!$mysql->destination()) {
|
if (!$mysql->destination()) {
|
||||||
ray('Mysql without destination', $mysql->name);
|
echo 'Mysql without destination: ' . $mysql->name . ' soft deleting\n';
|
||||||
$mysql->delete();
|
$mysql->delete();
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
if (!data_get($mysql, 'destination.server')) {
|
if (!data_get($mysql, 'destination.server')) {
|
||||||
ray('Mysql without server', $mysql->name);
|
echo 'Mysql without server: ' . $mysql->name . ' soft deleting\n';
|
||||||
$mysql->delete();
|
$mysql->delete();
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
@@ -199,16 +228,19 @@ class Init extends Command
|
|||||||
$mariadbs = StandaloneMariadb::all();
|
$mariadbs = StandaloneMariadb::all();
|
||||||
foreach ($mariadbs as $mariadb) {
|
foreach ($mariadbs as $mariadb) {
|
||||||
if (!data_get($mariadb, 'environment')) {
|
if (!data_get($mariadb, 'environment')) {
|
||||||
ray('Mariadb without environment', $mariadb->name);
|
echo 'Mariadb without environment: ' . $mariadb->name . ' soft deleting\n';
|
||||||
$mariadb->delete();
|
$mariadb->delete();
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
if (!$mariadb->destination()) {
|
if (!$mariadb->destination()) {
|
||||||
ray('Mariadb without destination', $mariadb->name);
|
echo 'Mariadb without destination: ' . $mariadb->name . ' soft deleting\n';
|
||||||
$mariadb->delete();
|
$mariadb->delete();
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
if (!data_get($mariadb, 'destination.server')) {
|
if (!data_get($mariadb, 'destination.server')) {
|
||||||
ray('Mariadb without server', $mariadb->name);
|
echo 'Mariadb without server: ' . $mariadb->name . ' soft deleting\n';
|
||||||
$mariadb->delete();
|
$mariadb->delete();
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
@@ -219,16 +251,19 @@ class Init extends Command
|
|||||||
$services = Service::all();
|
$services = Service::all();
|
||||||
foreach ($services as $service) {
|
foreach ($services as $service) {
|
||||||
if (!data_get($service, 'environment')) {
|
if (!data_get($service, 'environment')) {
|
||||||
ray('Service without environment', $service->name);
|
echo 'Service without environment: ' . $service->name . ' soft deleting\n';
|
||||||
$service->delete();
|
$service->delete();
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
if (!$service->destination()) {
|
if (!$service->destination()) {
|
||||||
ray('Service without destination', $service->name);
|
echo 'Service without destination: ' . $service->name . ' soft deleting\n';
|
||||||
$service->delete();
|
$service->delete();
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
if (!data_get($service, 'server')) {
|
if (!data_get($service, 'server')) {
|
||||||
ray('Service without server', $service->name);
|
echo 'Service without server: ' . $service->name . ' soft deleting\n';
|
||||||
$service->delete();
|
$service->delete();
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
@@ -238,8 +273,9 @@ class Init extends Command
|
|||||||
$serviceApplications = ServiceApplication::all();
|
$serviceApplications = ServiceApplication::all();
|
||||||
foreach ($serviceApplications as $service) {
|
foreach ($serviceApplications as $service) {
|
||||||
if (!data_get($service, 'service')) {
|
if (!data_get($service, 'service')) {
|
||||||
ray('ServiceApplication without service', $service->name);
|
echo 'ServiceApplication without service: ' . $service->name . ' soft deleting\n';
|
||||||
$service->delete();
|
$service->delete();
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
@@ -249,8 +285,9 @@ class Init extends Command
|
|||||||
$serviceDatabases = ServiceDatabase::all();
|
$serviceDatabases = ServiceDatabase::all();
|
||||||
foreach ($serviceDatabases as $service) {
|
foreach ($serviceDatabases as $service) {
|
||||||
if (!data_get($service, 'service')) {
|
if (!data_get($service, 'service')) {
|
||||||
ray('ServiceDatabase without service', $service->name);
|
echo 'ServiceDatabase without service: ' . $service->name . ' soft deleting\n';
|
||||||
$service->delete();
|
$service->delete();
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
|
|||||||
@@ -56,16 +56,20 @@ class Kernel extends ConsoleKernel
|
|||||||
$servers = Server::all()->whereNotNull('team.subscription')->where('team.subscription.stripe_trial_already_ended', false)->where('ip', '!=', '1.2.3.4');
|
$servers = Server::all()->whereNotNull('team.subscription')->where('team.subscription.stripe_trial_already_ended', false)->where('ip', '!=', '1.2.3.4');
|
||||||
$own = Team::find(0)->servers;
|
$own = Team::find(0)->servers;
|
||||||
$servers = $servers->merge($own);
|
$servers = $servers->merge($own);
|
||||||
|
$containerServers = $servers->where('settings.is_swarm_worker', false);
|
||||||
} else {
|
} else {
|
||||||
$servers = Server::all()->where('ip', '!=', '1.2.3.4');
|
$servers = Server::all()->where('ip', '!=', '1.2.3.4');
|
||||||
|
$containerServers = $servers->where('settings.is_swarm_worker', false);
|
||||||
}
|
}
|
||||||
foreach ($servers as $server) {
|
foreach ($containerServers as $server) {
|
||||||
$schedule->job(new ServerStatusJob($server))->everyTenMinutes()->onOneServer();
|
|
||||||
$schedule->job(new ContainerStatusJob($server))->everyMinute()->onOneServer();
|
$schedule->job(new ContainerStatusJob($server))->everyMinute()->onOneServer();
|
||||||
if ($server->isLogDrainEnabled()) {
|
if ($server->isLogDrainEnabled()) {
|
||||||
$schedule->job(new CheckLogDrainContainerJob($server))->everyMinute()->onOneServer();
|
$schedule->job(new CheckLogDrainContainerJob($server))->everyMinute()->onOneServer();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
foreach ($servers as $server) {
|
||||||
|
$schedule->job(new ServerStatusJob($server))->everyTenMinutes()->onOneServer();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
private function instance_auto_update($schedule)
|
private function instance_auto_update($schedule)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -16,9 +16,11 @@ class CoolifyTaskArgs extends Data
|
|||||||
public string $command,
|
public string $command,
|
||||||
public string $type,
|
public string $type,
|
||||||
public ?string $type_uuid = null,
|
public ?string $type_uuid = null,
|
||||||
|
public ?int $process_id = null,
|
||||||
public ?Model $model = null,
|
public ?Model $model = null,
|
||||||
public ?string $status = null ,
|
public ?string $status = null ,
|
||||||
public bool $ignore_errors = false,
|
public bool $ignore_errors = false,
|
||||||
|
public $call_event_on_finish = null,
|
||||||
) {
|
) {
|
||||||
if(is_null($status)){
|
if(is_null($status)){
|
||||||
$this->status = ProcessStatus::QUEUED->value;
|
$this->status = ProcessStatus::QUEUED->value;
|
||||||
|
|||||||
@@ -8,5 +8,6 @@ enum ProcessStatus: string
|
|||||||
case IN_PROGRESS = 'in_progress';
|
case IN_PROGRESS = 'in_progress';
|
||||||
case FINISHED = 'finished';
|
case FINISHED = 'finished';
|
||||||
case ERROR = 'error';
|
case ERROR = 'error';
|
||||||
|
case KILLED = 'killed';
|
||||||
case CANCELLED = 'cancelled';
|
case CANCELLED = 'cancelled';
|
||||||
}
|
}
|
||||||
|
|||||||
34
app/Events/ApplicationStatusChanged.php
Normal file
34
app/Events/ApplicationStatusChanged.php
Normal 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}"),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
34
app/Events/BackupCreated.php
Normal file
34
app/Events/BackupCreated.php
Normal 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}"),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
34
app/Events/DatabaseStatusChanged.php
Normal file
34
app/Events/DatabaseStatusChanged.php
Normal 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}"),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
34
app/Events/ServiceStatusChanged.php
Normal file
34
app/Events/ServiceStatusChanged.php
Normal 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
28
app/Events/TestEvent.php
Normal 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}"),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -39,7 +39,7 @@ class Controller extends BaseController
|
|||||||
} else {
|
} else {
|
||||||
$team = $user->teams()->first();
|
$team = $user->teams()->first();
|
||||||
}
|
}
|
||||||
if (is_null(data_get($user, 'email_verified_at'))){
|
if (is_null(data_get($user, 'email_verified_at'))) {
|
||||||
$user->email_verified_at = now();
|
$user->email_verified_at = now();
|
||||||
$user->save();
|
$user->save();
|
||||||
}
|
}
|
||||||
@@ -80,6 +80,10 @@ class Controller extends BaseController
|
|||||||
$settings = InstanceSettings::get();
|
$settings = InstanceSettings::get();
|
||||||
$database = StandalonePostgresql::whereName('coolify-db')->first();
|
$database = StandalonePostgresql::whereName('coolify-db')->first();
|
||||||
if ($database) {
|
if ($database) {
|
||||||
|
if ($database->status !== 'running') {
|
||||||
|
$database->status = 'running';
|
||||||
|
$database->save();
|
||||||
|
}
|
||||||
$s3s = S3Storage::whereTeamId(0)->get();
|
$s3s = S3Storage::whereTeamId(0)->get();
|
||||||
}
|
}
|
||||||
return view('settings.configuration', [
|
return view('settings.configuration', [
|
||||||
@@ -133,16 +137,28 @@ class Controller extends BaseController
|
|||||||
public function acceptInvitation()
|
public function acceptInvitation()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$invitation = TeamInvitation::whereUuid(request()->route('uuid'))->firstOrFail();
|
$resetPassword = request()->query('reset-password');
|
||||||
|
$invitationUuid = request()->route('uuid');
|
||||||
|
$invitation = TeamInvitation::whereUuid($invitationUuid)->firstOrFail();
|
||||||
$user = User::whereEmail($invitation->email)->firstOrFail();
|
$user = User::whereEmail($invitation->email)->firstOrFail();
|
||||||
if (auth()->user()->id !== $user->id) {
|
|
||||||
abort(401);
|
|
||||||
}
|
|
||||||
$invitationValid = $invitation->isValid();
|
$invitationValid = $invitation->isValid();
|
||||||
if ($invitationValid) {
|
if ($invitationValid) {
|
||||||
|
if ($resetPassword) {
|
||||||
|
$user->update([
|
||||||
|
'password' => Hash::make($invitationUuid),
|
||||||
|
'force_password_reset' => true
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
if ($user->teams()->where('team_id', $invitation->team->id)->exists()) {
|
||||||
|
$invitation->delete();
|
||||||
|
return redirect()->route('team.index');
|
||||||
|
}
|
||||||
$user->teams()->attach($invitation->team->id, ['role' => $invitation->role]);
|
$user->teams()->attach($invitation->team->id, ['role' => $invitation->role]);
|
||||||
refreshSession($invitation->team);
|
|
||||||
$invitation->delete();
|
$invitation->delete();
|
||||||
|
if (auth()->user()?->id !== $user->id) {
|
||||||
|
return redirect()->route('login');
|
||||||
|
}
|
||||||
|
refreshSession($invitation->team);
|
||||||
return redirect()->route('team.index');
|
return redirect()->route('team.index');
|
||||||
} else {
|
} else {
|
||||||
abort(401);
|
abort(401);
|
||||||
|
|||||||
@@ -116,7 +116,6 @@ class ProjectController extends Controller
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
$service->parse(isNew: true);
|
$service->parse(isNew: true);
|
||||||
|
|
||||||
return redirect()->route('project.service.configuration', [
|
return redirect()->route('project.service.configuration', [
|
||||||
'service_uuid' => $service->uuid,
|
'service_uuid' => $service->uuid,
|
||||||
'environment_name' => $environment->name,
|
'environment_name' => $environment->name,
|
||||||
|
|||||||
@@ -1,84 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Livewire\Destination\New;
|
|
||||||
|
|
||||||
use App\Models\Server;
|
|
||||||
use App\Models\StandaloneDocker as ModelsStandaloneDocker;
|
|
||||||
use Illuminate\Database\Eloquent\Collection;
|
|
||||||
use Illuminate\Support\Str;
|
|
||||||
use Livewire\Component;
|
|
||||||
use Visus\Cuid2\Cuid2;
|
|
||||||
|
|
||||||
class StandaloneDocker extends Component
|
|
||||||
{
|
|
||||||
public string $name;
|
|
||||||
public string $network;
|
|
||||||
|
|
||||||
public Collection $servers;
|
|
||||||
public Server $server;
|
|
||||||
public int|null $server_id = null;
|
|
||||||
|
|
||||||
protected $rules = [
|
|
||||||
'name' => 'required|string',
|
|
||||||
'network' => 'required|string',
|
|
||||||
'server_id' => 'required|integer'
|
|
||||||
];
|
|
||||||
protected $validationAttributes = [
|
|
||||||
'name' => 'name',
|
|
||||||
'network' => 'network',
|
|
||||||
'server_id' => 'server'
|
|
||||||
];
|
|
||||||
|
|
||||||
public function mount()
|
|
||||||
{
|
|
||||||
if (request()->query('server_id')) {
|
|
||||||
$this->server_id = request()->query('server_id');
|
|
||||||
} else {
|
|
||||||
if ($this->servers->count() > 0) {
|
|
||||||
$this->server_id = $this->servers->first()->id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (request()->query('network_name')) {
|
|
||||||
$this->network = request()->query('network_name');
|
|
||||||
} else {
|
|
||||||
$this->network = new Cuid2(7);
|
|
||||||
}
|
|
||||||
$this->name = Str::kebab("{$this->servers->first()->name}-{$this->network}");
|
|
||||||
}
|
|
||||||
|
|
||||||
public function generate_name()
|
|
||||||
{
|
|
||||||
$this->server = Server::find($this->server_id);
|
|
||||||
$this->name = Str::kebab("{$this->server->name}-{$this->network}");
|
|
||||||
}
|
|
||||||
|
|
||||||
public function submit()
|
|
||||||
{
|
|
||||||
$this->validate();
|
|
||||||
try {
|
|
||||||
$this->server = Server::find($this->server_id);
|
|
||||||
$found = $this->server->standaloneDockers()->where('network', $this->network)->first();
|
|
||||||
if ($found) {
|
|
||||||
$this->createNetworkAndAttachToProxy();
|
|
||||||
$this->emit('error', 'Network already added to this server.');
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
$docker = ModelsStandaloneDocker::create([
|
|
||||||
'name' => $this->name,
|
|
||||||
'network' => $this->network,
|
|
||||||
'server_id' => $this->server_id,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
$this->createNetworkAndAttachToProxy();
|
|
||||||
return redirect()->route('destination.show', $docker->uuid);
|
|
||||||
} catch (\Throwable $e) {
|
|
||||||
return handleError($e, $this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function createNetworkAndAttachToProxy()
|
|
||||||
{
|
|
||||||
$connectProxyToDockerNetworks = connectProxyToNetworks($this->server);
|
|
||||||
instant_remote_process($connectProxyToDockerNetworks, $this->server, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Livewire\Project\Shared\Storages;
|
|
||||||
|
|
||||||
use Livewire\Component;
|
|
||||||
|
|
||||||
class Add extends Component
|
|
||||||
{
|
|
||||||
public $uuid;
|
|
||||||
public $parameters;
|
|
||||||
public string $name;
|
|
||||||
public string $mount_path;
|
|
||||||
public string|null $host_path = null;
|
|
||||||
|
|
||||||
protected $listeners = ['clearAddStorage' => 'clear'];
|
|
||||||
protected $rules = [
|
|
||||||
'name' => 'required|string',
|
|
||||||
'mount_path' => 'required|string',
|
|
||||||
'host_path' => 'string|nullable',
|
|
||||||
];
|
|
||||||
protected $validationAttributes = [
|
|
||||||
'name' => 'name',
|
|
||||||
'mount_path' => 'mount',
|
|
||||||
'host_path' => 'host',
|
|
||||||
];
|
|
||||||
|
|
||||||
public function mount()
|
|
||||||
{
|
|
||||||
$this->parameters = get_route_parameters();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function submit()
|
|
||||||
{
|
|
||||||
$this->validate();
|
|
||||||
$name = $this->uuid . '-' . $this->name;
|
|
||||||
$this->emit('addNewVolume', [
|
|
||||||
'name' => $name,
|
|
||||||
'mount_path' => $this->mount_path,
|
|
||||||
'host_path' => $this->host_path,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function clear()
|
|
||||||
{
|
|
||||||
$this->name = '';
|
|
||||||
$this->mount_path = '';
|
|
||||||
$this->host_path = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,153 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Livewire\Settings;
|
|
||||||
|
|
||||||
use App\Jobs\ContainerStatusJob;
|
|
||||||
use App\Models\InstanceSettings as ModelsInstanceSettings;
|
|
||||||
use App\Models\Server;
|
|
||||||
use Livewire\Component;
|
|
||||||
use Spatie\Url\Url;
|
|
||||||
use Symfony\Component\Yaml\Yaml;
|
|
||||||
|
|
||||||
class Configuration extends Component
|
|
||||||
{
|
|
||||||
public ModelsInstanceSettings $settings;
|
|
||||||
public bool $do_not_track;
|
|
||||||
public bool $is_auto_update_enabled;
|
|
||||||
public bool $is_registration_enabled;
|
|
||||||
public bool $next_channel;
|
|
||||||
protected string $dynamic_config_path = '/data/coolify/proxy/dynamic';
|
|
||||||
protected Server $server;
|
|
||||||
|
|
||||||
protected $rules = [
|
|
||||||
'settings.fqdn' => 'nullable',
|
|
||||||
'settings.resale_license' => 'nullable',
|
|
||||||
'settings.public_port_min' => 'required',
|
|
||||||
'settings.public_port_max' => 'required',
|
|
||||||
];
|
|
||||||
protected $validationAttributes = [
|
|
||||||
'settings.fqdn' => 'FQDN',
|
|
||||||
'settings.resale_license' => 'Resale License',
|
|
||||||
'settings.public_port_min' => 'Public port min',
|
|
||||||
'settings.public_port_max' => 'Public port max',
|
|
||||||
];
|
|
||||||
|
|
||||||
public function mount()
|
|
||||||
{
|
|
||||||
$this->do_not_track = $this->settings->do_not_track;
|
|
||||||
$this->is_auto_update_enabled = $this->settings->is_auto_update_enabled;
|
|
||||||
$this->is_registration_enabled = $this->settings->is_registration_enabled;
|
|
||||||
$this->next_channel = $this->settings->next_channel;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function instantSave()
|
|
||||||
{
|
|
||||||
$this->settings->do_not_track = $this->do_not_track;
|
|
||||||
$this->settings->is_auto_update_enabled = $this->is_auto_update_enabled;
|
|
||||||
$this->settings->is_registration_enabled = $this->is_registration_enabled;
|
|
||||||
if ($this->next_channel) {
|
|
||||||
$this->settings->next_channel = false;
|
|
||||||
$this->next_channel = false;
|
|
||||||
} else {
|
|
||||||
$this->settings->next_channel = $this->next_channel;
|
|
||||||
}
|
|
||||||
$this->settings->save();
|
|
||||||
$this->emit('success', 'Settings updated!');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function submit()
|
|
||||||
{
|
|
||||||
$this->resetErrorBag();
|
|
||||||
if ($this->settings->public_port_min > $this->settings->public_port_max) {
|
|
||||||
$this->addError('settings.public_port_min', 'The minimum port must be lower than the maximum port.');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
$this->validate();
|
|
||||||
$this->settings->save();
|
|
||||||
$this->server = Server::findOrFail(0);
|
|
||||||
$this->setup_instance_fqdn();
|
|
||||||
$this->emit('success', 'Instance settings updated successfully!');
|
|
||||||
}
|
|
||||||
|
|
||||||
private function setup_instance_fqdn()
|
|
||||||
{
|
|
||||||
$file = "$this->dynamic_config_path/coolify.yaml";
|
|
||||||
if (empty($this->settings->fqdn)) {
|
|
||||||
instant_remote_process([
|
|
||||||
"rm -f $file",
|
|
||||||
], $this->server);
|
|
||||||
} else {
|
|
||||||
$url = Url::fromString($this->settings->fqdn);
|
|
||||||
$host = $url->getHost();
|
|
||||||
$schema = $url->getScheme();
|
|
||||||
$traefik_dynamic_conf = [
|
|
||||||
'http' =>
|
|
||||||
[
|
|
||||||
'routers' =>
|
|
||||||
[
|
|
||||||
'coolify-http' =>
|
|
||||||
[
|
|
||||||
'entryPoints' => [
|
|
||||||
0 => 'http',
|
|
||||||
],
|
|
||||||
'service' => 'coolify',
|
|
||||||
'rule' => "Host(`{$host}`)",
|
|
||||||
],
|
|
||||||
],
|
|
||||||
'services' =>
|
|
||||||
[
|
|
||||||
'coolify' =>
|
|
||||||
[
|
|
||||||
'loadBalancer' =>
|
|
||||||
[
|
|
||||||
'servers' =>
|
|
||||||
[
|
|
||||||
0 =>
|
|
||||||
[
|
|
||||||
'url' => 'http://coolify:80',
|
|
||||||
],
|
|
||||||
],
|
|
||||||
],
|
|
||||||
],
|
|
||||||
],
|
|
||||||
],
|
|
||||||
];
|
|
||||||
|
|
||||||
if ($schema === 'https') {
|
|
||||||
$traefik_dynamic_conf['http']['routers']['coolify-http']['middlewares'] = [
|
|
||||||
0 => 'redirect-to-https@docker',
|
|
||||||
];
|
|
||||||
$traefik_dynamic_conf['http']['routers']['coolify-https'] = [
|
|
||||||
'entryPoints' => [
|
|
||||||
0 => 'https',
|
|
||||||
],
|
|
||||||
'service' => 'coolify',
|
|
||||||
'rule' => "Host(`{$host}`)",
|
|
||||||
'tls' => [
|
|
||||||
'certresolver' => 'letsencrypt',
|
|
||||||
],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
$this->save_configuration_to_disk($traefik_dynamic_conf, $file);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function save_configuration_to_disk(array $traefik_dynamic_conf, string $file)
|
|
||||||
{
|
|
||||||
$yaml = Yaml::dump($traefik_dynamic_conf, 12, 2);
|
|
||||||
$yaml =
|
|
||||||
"# This file is automatically generated by Coolify.\n" .
|
|
||||||
"# Do not edit it manually (only if you know what are you doing).\n\n" .
|
|
||||||
$yaml;
|
|
||||||
|
|
||||||
$base64 = base64_encode($yaml);
|
|
||||||
instant_remote_process([
|
|
||||||
"mkdir -p $this->dynamic_config_path",
|
|
||||||
"echo '$base64' | base64 -d > $file",
|
|
||||||
], $this->server);
|
|
||||||
|
|
||||||
if (config('app.env') == 'local') {
|
|
||||||
ray($yaml);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -24,7 +24,7 @@ class CheckForcePasswordReset
|
|||||||
}
|
}
|
||||||
$force_password_reset = auth()->user()->force_password_reset;
|
$force_password_reset = auth()->user()->force_password_reset;
|
||||||
if ($force_password_reset) {
|
if ($force_password_reset) {
|
||||||
if ($request->routeIs('auth.force-password-reset') || $request->path() === 'livewire/message/force-password-reset') {
|
if ($request->routeIs('auth.force-password-reset') || $request->path() === 'force-password-reset' || $request->path() === 'livewire/update' || $request->path() === 'logout') {
|
||||||
return $next($request);
|
return $next($request);
|
||||||
}
|
}
|
||||||
return redirect()->route('auth.force-password-reset');
|
return redirect()->route('auth.force-password-reset');
|
||||||
|
|||||||
@@ -11,6 +11,9 @@ class DecideWhatToDoWithUser
|
|||||||
{
|
{
|
||||||
public function handle(Request $request, Closure $next): Response
|
public function handle(Request $request, Closure $next): Response
|
||||||
{
|
{
|
||||||
|
if(auth()?->user()?->currentTeam()){
|
||||||
|
refreshSession(auth()->user()->currentTeam());
|
||||||
|
}
|
||||||
if (!auth()->user() || !isCloud() || isInstanceAdmin()) {
|
if (!auth()->user() || !isCloud() || isInstanceAdmin()) {
|
||||||
if (!isCloud() && showBoarding() && !in_array($request->path(), allowedPathsForBoardingAccounts())) {
|
if (!isCloud() && showBoarding() && !in_array($request->path(), allowedPathsForBoardingAccounts())) {
|
||||||
return redirect('boarding');
|
return redirect('boarding');
|
||||||
|
|||||||
@@ -1,28 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Middleware;
|
|
||||||
|
|
||||||
use Closure;
|
|
||||||
use Illuminate\Http\Request;
|
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
|
||||||
use Illuminate\Support\Str;
|
|
||||||
|
|
||||||
class IsBoardingFlow
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Handle an incoming request.
|
|
||||||
*
|
|
||||||
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
|
|
||||||
*/
|
|
||||||
public function handle(Request $request, Closure $next): Response
|
|
||||||
{
|
|
||||||
// ray()->showQueries()->color('orange');
|
|
||||||
if (showBoarding() && !in_array($request->path(), allowedPathsForBoardingAccounts())) {
|
|
||||||
if (Str::startsWith($request->path(), 'invitations')) {
|
|
||||||
return $next($request);
|
|
||||||
}
|
|
||||||
return redirect('boarding');
|
|
||||||
}
|
|
||||||
return $next($request);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Middleware;
|
|
||||||
|
|
||||||
use Closure;
|
|
||||||
use Illuminate\Http\Request;
|
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
|
||||||
use Illuminate\Support\Str;
|
|
||||||
|
|
||||||
class IsSubscriptionValid
|
|
||||||
{
|
|
||||||
public function handle(Request $request, Closure $next): Response
|
|
||||||
{
|
|
||||||
if (isInstanceAdmin()) {
|
|
||||||
return $next($request);
|
|
||||||
}
|
|
||||||
if (!auth()->user() || !isCloud()) {
|
|
||||||
if ($request->path() === 'subscription') {
|
|
||||||
return redirect('/');
|
|
||||||
} else {
|
|
||||||
return $next($request);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (isSubscriptionActive() && $request->path() === 'subscription') {
|
|
||||||
// ray('active subscription Middleware');
|
|
||||||
return redirect('/');
|
|
||||||
}
|
|
||||||
if (isSubscriptionOnGracePeriod()) {
|
|
||||||
// ray('is_subscription_in_grace_period Middleware');
|
|
||||||
return $next($request);
|
|
||||||
}
|
|
||||||
if (!isSubscriptionActive() && !isSubscriptionOnGracePeriod()) {
|
|
||||||
// ray('SubscriptionValid Middleware');
|
|
||||||
if (!in_array($request->path(), allowedPathsForUnsubscribedAccounts())) {
|
|
||||||
if (Str::startsWith($request->path(), 'invitations')) {
|
|
||||||
return $next($request);
|
|
||||||
}
|
|
||||||
return redirect('subscription');
|
|
||||||
} else {
|
|
||||||
return $next($request);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $next($request);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -4,6 +4,7 @@ namespace App\Jobs;
|
|||||||
|
|
||||||
use App\Enums\ApplicationDeploymentStatus;
|
use App\Enums\ApplicationDeploymentStatus;
|
||||||
use App\Enums\ProxyTypes;
|
use App\Enums\ProxyTypes;
|
||||||
|
use App\Events\ApplicationStatusChanged;
|
||||||
use App\Models\Application;
|
use App\Models\Application;
|
||||||
use App\Models\ApplicationDeploymentQueue;
|
use App\Models\ApplicationDeploymentQueue;
|
||||||
use App\Models\ApplicationPreview;
|
use App\Models\ApplicationPreview;
|
||||||
@@ -74,6 +75,8 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
private $docker_compose_base64;
|
private $docker_compose_base64;
|
||||||
private string $dockerfile_location = '/Dockerfile';
|
private string $dockerfile_location = '/Dockerfile';
|
||||||
private string $docker_compose_location = '/docker-compose.yml';
|
private string $docker_compose_location = '/docker-compose.yml';
|
||||||
|
private ?string $docker_compose_custom_start_command = null;
|
||||||
|
private ?string $docker_compose_custom_build_command = null;
|
||||||
private ?string $addHosts = null;
|
private ?string $addHosts = null;
|
||||||
private ?string $buildTarget = null;
|
private ?string $buildTarget = null;
|
||||||
private Collection $saved_outputs;
|
private Collection $saved_outputs;
|
||||||
@@ -214,19 +217,8 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
if ($this->server->isProxyShouldRun()) {
|
if ($this->server->isProxyShouldRun()) {
|
||||||
dispatch(new ContainerStatusJob($this->server));
|
dispatch(new ContainerStatusJob($this->server));
|
||||||
}
|
}
|
||||||
if ($this->application->docker_registry_image_name && $this->application->build_pack !== 'dockerimage') {
|
if ($this->application->docker_registry_image_name && $this->application->build_pack !== 'dockerimage' && !$this->application->destination->server->isSwarm()) {
|
||||||
$this->push_to_docker_registry();
|
$this->push_to_docker_registry();
|
||||||
if ($this->server->isSwarm()) {
|
|
||||||
$this->application_deployment_queue->addLogEntry("Creating / updating stack.");
|
|
||||||
$this->execute_remote_command(
|
|
||||||
[
|
|
||||||
executeInDocker($this->deployment_uuid, "cd {$this->workdir} && docker stack deploy --with-registry-auth -c docker-compose.yml {$this->application->uuid}")
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"echo 'Stack deployed. It may take a few minutes to fully available in your swarm.'"
|
|
||||||
]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
$this->next(ApplicationDeploymentStatus::FINISHED->value);
|
$this->next(ApplicationDeploymentStatus::FINISHED->value);
|
||||||
$this->application->isConfigurationChanged(true);
|
$this->application->isConfigurationChanged(true);
|
||||||
@@ -266,6 +258,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
"ignore_errors" => true,
|
"ignore_errors" => true,
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
ApplicationStatusChanged::dispatch(data_get($this->application, 'environment.project.team.id'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private function push_to_docker_registry()
|
private function push_to_docker_registry()
|
||||||
@@ -297,6 +290,9 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
"echo -n 'Image pushed to docker registry.'"
|
"echo -n 'Image pushed to docker registry.'"
|
||||||
]);
|
]);
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
|
if ($this->application->destination->server->isSwarm()) {
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
$this->execute_remote_command(
|
$this->execute_remote_command(
|
||||||
["echo -n 'Failed to push image to docker registry. Please check debug logs for more information.'"],
|
["echo -n 'Failed to push image to docker registry. Please check debug logs for more information.'"],
|
||||||
);
|
);
|
||||||
@@ -347,6 +343,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
$this->generate_image_names();
|
$this->generate_image_names();
|
||||||
$this->check_image_locally_or_remotely();
|
$this->check_image_locally_or_remotely();
|
||||||
if (str($this->saved_outputs->get('local_image_found'))->isNotEmpty()) {
|
if (str($this->saved_outputs->get('local_image_found'))->isNotEmpty()) {
|
||||||
|
$this->create_workdir();
|
||||||
$this->generate_compose_file();
|
$this->generate_compose_file();
|
||||||
$this->rolling_update();
|
$this->rolling_update();
|
||||||
return;
|
return;
|
||||||
@@ -429,6 +426,12 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
if (data_get($this->application, 'docker_compose_location')) {
|
if (data_get($this->application, 'docker_compose_location')) {
|
||||||
$this->docker_compose_location = $this->application->docker_compose_location;
|
$this->docker_compose_location = $this->application->docker_compose_location;
|
||||||
}
|
}
|
||||||
|
if (data_get($this->application, 'docker_compose_custom_start_command')) {
|
||||||
|
$this->docker_compose_custom_start_command = $this->application->docker_compose_custom_start_command;
|
||||||
|
}
|
||||||
|
if (data_get($this->application, 'docker_compose_custom_build_command')) {
|
||||||
|
$this->docker_compose_custom_build_command = $this->application->docker_compose_custom_build_command;
|
||||||
|
}
|
||||||
if ($this->pull_request_id === 0) {
|
if ($this->pull_request_id === 0) {
|
||||||
$this->application_deployment_queue->addLogEntry("Starting deployment of {$this->application->name}.");
|
$this->application_deployment_queue->addLogEntry("Starting deployment of {$this->application->name}.");
|
||||||
} else {
|
} else {
|
||||||
@@ -442,23 +445,33 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
$this->clone_repository();
|
$this->clone_repository();
|
||||||
$this->generate_image_names();
|
$this->generate_image_names();
|
||||||
$this->cleanup_git();
|
$this->cleanup_git();
|
||||||
|
$this->application->loadComposeFile(isInit: false);
|
||||||
$composeFile = $this->application->parseCompose(pull_request_id: $this->pull_request_id);
|
$composeFile = $this->application->parseCompose(pull_request_id: $this->pull_request_id);
|
||||||
$yaml = Yaml::dump($composeFile->toArray(), 10);
|
$yaml = Yaml::dump($composeFile->toArray(), 10);
|
||||||
ray($composeFile);
|
|
||||||
ray($this->container_name);
|
|
||||||
$this->docker_compose_base64 = base64_encode($yaml);
|
$this->docker_compose_base64 = base64_encode($yaml);
|
||||||
$this->execute_remote_command([
|
$this->execute_remote_command([
|
||||||
executeInDocker($this->deployment_uuid, "echo '{$this->docker_compose_base64}' | base64 -d > {$this->workdir}{$this->docker_compose_location}"), "hidden" => true
|
executeInDocker($this->deployment_uuid, "echo '{$this->docker_compose_base64}' | base64 -d > {$this->workdir}{$this->docker_compose_location}"), "hidden" => true
|
||||||
]);
|
]);
|
||||||
$this->save_environment_variables();
|
$this->save_environment_variables();
|
||||||
|
// Build new container to limit downtime.
|
||||||
|
$this->application_deployment_queue->addLogEntry("Pulling & building required images.");
|
||||||
|
|
||||||
|
if ($this->docker_compose_custom_build_command) {
|
||||||
|
$this->execute_remote_command(
|
||||||
|
[executeInDocker($this->deployment_uuid, "cd {$this->basedir} && {$this->docker_compose_custom_build_command}"), "hidden" => true],
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$this->execute_remote_command(
|
||||||
|
[executeInDocker($this->deployment_uuid, "docker compose --project-directory {$this->workdir} -f {$this->workdir}{$this->docker_compose_location} build"), "hidden" => true],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
$this->stop_running_container(force: true);
|
$this->stop_running_container(force: true);
|
||||||
|
|
||||||
ray($this->pull_request_id);
|
|
||||||
$networkId = $this->application->uuid;
|
$networkId = $this->application->uuid;
|
||||||
if ($this->pull_request_id !== 0) {
|
if ($this->pull_request_id !== 0) {
|
||||||
$networkId = "{$this->application->uuid}-{$this->pull_request_id}";
|
$networkId = "{$this->application->uuid}-{$this->pull_request_id}";
|
||||||
}
|
}
|
||||||
ray($networkId);
|
|
||||||
if ($this->server->isSwarm()) {
|
if ($this->server->isSwarm()) {
|
||||||
// TODO
|
// TODO
|
||||||
} else {
|
} else {
|
||||||
@@ -468,9 +481,35 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
"docker network connect {$networkId} coolify-proxy || true", "hidden" => true, "ignore_errors" => true
|
"docker network connect {$networkId} coolify-proxy || true", "hidden" => true, "ignore_errors" => true
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
if (isset($this->docker_compose_base64)) {
|
||||||
$this->start_by_compose_file();
|
$readme = generate_readme_file($this->application->name, $this->application_deployment_queue->updated_at);
|
||||||
$this->application->loadComposeFile(isInit: false);
|
$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",
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// Start compose file
|
||||||
|
if ($this->docker_compose_custom_start_command) {
|
||||||
|
$this->execute_remote_command(
|
||||||
|
[executeInDocker($this->deployment_uuid, "cd {$this->basedir} && {$this->docker_compose_custom_start_command}"), "hidden" => true],
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$this->execute_remote_command(
|
||||||
|
[executeInDocker($this->deployment_uuid, "docker compose --project-directory {$this->workdir} -f {$this->workdir}{$this->docker_compose_location} up -d"), "hidden" => true],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$this->application_deployment_queue->addLogEntry("New container started.");
|
||||||
}
|
}
|
||||||
private function deploy_dockerfile_buildpack()
|
private function deploy_dockerfile_buildpack()
|
||||||
{
|
{
|
||||||
@@ -513,6 +552,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
if (!$this->force_rebuild) {
|
if (!$this->force_rebuild) {
|
||||||
$this->check_image_locally_or_remotely();
|
$this->check_image_locally_or_remotely();
|
||||||
if (str($this->saved_outputs->get('local_image_found'))->isNotEmpty() && !$this->application->isConfigurationChanged()) {
|
if (str($this->saved_outputs->get('local_image_found'))->isNotEmpty() && !$this->application->isConfigurationChanged()) {
|
||||||
|
$this->create_workdir();
|
||||||
$this->execute_remote_command([
|
$this->execute_remote_command([
|
||||||
"echo 'No configuration changed & image found ({$this->production_image_name}) with the same Git Commit SHA. Build step skipped.'",
|
"echo 'No configuration changed & image found ({$this->production_image_name}) with the same Git Commit SHA. Build step skipped.'",
|
||||||
]);
|
]);
|
||||||
@@ -556,7 +596,16 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
private function rolling_update()
|
private function rolling_update()
|
||||||
{
|
{
|
||||||
if ($this->server->isSwarm()) {
|
if ($this->server->isSwarm()) {
|
||||||
// Skip this.
|
if ($this->build_pack !== 'dockerimage') {
|
||||||
|
$this->push_to_docker_registry();
|
||||||
|
}
|
||||||
|
$this->application_deployment_queue->addLogEntry("Rolling update started.");
|
||||||
|
$this->execute_remote_command(
|
||||||
|
[
|
||||||
|
executeInDocker($this->deployment_uuid, "docker stack deploy --with-registry-auth -c {$this->workdir}{$this->docker_compose_location} {$this->application->uuid}")
|
||||||
|
],
|
||||||
|
);
|
||||||
|
$this->application_deployment_queue->addLogEntry("Rolling update completed.");
|
||||||
} else {
|
} else {
|
||||||
if (count($this->application->ports_mappings_array) > 0) {
|
if (count($this->application->ports_mappings_array) > 0) {
|
||||||
$this->execute_remote_command(
|
$this->execute_remote_command(
|
||||||
@@ -655,12 +704,29 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
$this->add_build_env_variables_to_dockerfile();
|
$this->add_build_env_variables_to_dockerfile();
|
||||||
$this->build_image();
|
$this->build_image();
|
||||||
$this->stop_running_container();
|
$this->stop_running_container();
|
||||||
|
if ($this->application->destination->server->isSwarm()) {
|
||||||
|
ray("{$this->workdir}{$this->docker_compose_location}");
|
||||||
|
$this->push_to_docker_registry();
|
||||||
|
$this->execute_remote_command(
|
||||||
|
[
|
||||||
|
executeInDocker($this->deployment_uuid, "docker stack deploy --with-registry-auth -c {$this->workdir}{$this->docker_compose_location} {$this->application->uuid}-{$this->pull_request_id}")
|
||||||
|
],
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$this->execute_remote_command(
|
||||||
|
["echo -n 'Starting preview deployment.'"],
|
||||||
|
[executeInDocker($this->deployment_uuid, "docker compose --project-directory {$this->workdir} up -d"), "hidden" => true],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private function create_workdir()
|
||||||
|
{
|
||||||
$this->execute_remote_command(
|
$this->execute_remote_command(
|
||||||
["echo -n 'Starting preview deployment.'"],
|
[
|
||||||
[executeInDocker($this->deployment_uuid, "docker compose --project-directory {$this->workdir} up -d"), "hidden" => true],
|
"command" => executeInDocker($this->deployment_uuid, "mkdir -p {$this->workdir}")
|
||||||
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function prepare_builder_image()
|
private function prepare_builder_image()
|
||||||
{
|
{
|
||||||
$helperImage = config('coolify.helper_image');
|
$helperImage = config('coolify.helper_image');
|
||||||
@@ -669,9 +735,9 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
$this->dockerConfigFileExists = instant_remote_process(["test -f {$this->serverUserHomeDir}/.docker/config.json && echo 'OK' || echo 'NOK'"], $this->server);
|
$this->dockerConfigFileExists = instant_remote_process(["test -f {$this->serverUserHomeDir}/.docker/config.json && echo 'OK' || echo 'NOK'"], $this->server);
|
||||||
|
|
||||||
if ($this->dockerConfigFileExists === 'OK') {
|
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 {
|
} 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(
|
$this->execute_remote_command(
|
||||||
[
|
[
|
||||||
@@ -684,6 +750,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
[
|
[
|
||||||
"command" => executeInDocker($this->deployment_uuid, "mkdir -p {$this->basedir}")
|
"command" => executeInDocker($this->deployment_uuid, "mkdir -p {$this->basedir}")
|
||||||
],
|
],
|
||||||
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
private function deploy_to_additional_destinations()
|
private function deploy_to_additional_destinations()
|
||||||
@@ -847,11 +914,12 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
$environment_variables = $this->generate_environment_variables($ports);
|
$environment_variables = $this->generate_environment_variables($ports);
|
||||||
|
|
||||||
if (data_get($this->application, 'custom_labels')) {
|
if (data_get($this->application, 'custom_labels')) {
|
||||||
$labels = collect(str($this->application->custom_labels)->explode(','));
|
$this->application->parseContainerLabels();
|
||||||
|
$labels = collect(preg_split("/\r\n|\n|\r/", base64_decode($this->application->custom_labels)));
|
||||||
$labels = $labels->filter(function ($value, $key) {
|
$labels = $labels->filter(function ($value, $key) {
|
||||||
return !Str::startsWith($value, 'coolify.');
|
return !Str::startsWith($value, 'coolify.');
|
||||||
});
|
});
|
||||||
$this->application->custom_labels = $labels->implode(',');
|
$this->application->custom_labels = base64_encode($labels->implode("\n"));
|
||||||
$this->application->save();
|
$this->application->save();
|
||||||
} else {
|
} else {
|
||||||
$labels = collect(generateLabelsApplication($this->application, $this->preview));
|
$labels = collect(generateLabelsApplication($this->application, $this->preview));
|
||||||
@@ -913,13 +981,8 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
data_forget($docker_compose, 'services.' . $this->container_name . '.cpu_shares');
|
data_forget($docker_compose, 'services.' . $this->container_name . '.cpu_shares');
|
||||||
|
|
||||||
$docker_compose['services'][$this->container_name]['deploy'] = [
|
$docker_compose['services'][$this->container_name]['deploy'] = [
|
||||||
'placement' => [
|
|
||||||
'constraints' => [
|
|
||||||
'node.role == worker'
|
|
||||||
]
|
|
||||||
],
|
|
||||||
'mode' => 'replicated',
|
'mode' => 'replicated',
|
||||||
'replicas' => 1,
|
'replicas' => data_get($this->application, 'swarm_replicas', 1),
|
||||||
'update_config' => [
|
'update_config' => [
|
||||||
'order' => 'start-first'
|
'order' => 'start-first'
|
||||||
],
|
],
|
||||||
@@ -938,6 +1001,16 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
]
|
]
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
if (data_get($this->application, 'settings.is_swarm_only_worker_nodes')) {
|
||||||
|
$docker_compose['services'][$this->container_name]['deploy']['placement'] = [
|
||||||
|
'constraints' => [
|
||||||
|
'node.role == worker'
|
||||||
|
]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
if ($this->pull_request_id !== 0) {
|
||||||
|
$docker_compose['services'][$this->container_name]['deploy']['replicas'] = 1;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
$docker_compose['services'][$this->container_name]['labels'] = $labels;
|
$docker_compose['services'][$this->container_name]['labels'] = $labels;
|
||||||
}
|
}
|
||||||
@@ -1071,6 +1144,9 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
} else {
|
} else {
|
||||||
$health_check_port = $this->application->health_check_port;
|
$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) {
|
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}";
|
$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 = [
|
$generated_healthchecks_commands = [
|
||||||
@@ -1217,6 +1293,23 @@ 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 {
|
||||||
|
$this->execute_remote_command(
|
||||||
|
[executeInDocker($this->deployment_uuid, "docker compose --project-directory {$this->workdir} -f {$this->workdir}{$this->docker_compose_location} build"), "hidden" => true],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$this->application_deployment_queue->addLogEntry("New images built.");
|
||||||
|
}
|
||||||
|
|
||||||
private function start_by_compose_file()
|
private function start_by_compose_file()
|
||||||
{
|
{
|
||||||
if ($this->application->build_pack === 'dockerimage') {
|
if ($this->application->build_pack === 'dockerimage') {
|
||||||
@@ -1226,9 +1319,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],
|
[executeInDocker($this->deployment_uuid, "docker compose --project-directory {$this->workdir} up --build -d"), "hidden" => true],
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
$this->execute_remote_command(
|
if ($this->docker_compose_location) {
|
||||||
[executeInDocker($this->deployment_uuid, "docker compose --project-directory {$this->workdir} up --build -d"), "hidden" => true],
|
$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.");
|
$this->application_deployment_queue->addLogEntry("New container started.");
|
||||||
}
|
}
|
||||||
@@ -1264,7 +1363,6 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
|
|||||||
$dockerfile->splice(1, 0, "ARG {$env->key}={$env->value}");
|
$dockerfile->splice(1, 0, "ARG {$env->key}={$env->value}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ray($dockerfile->implode("\n"));
|
|
||||||
$dockerfile_base64 = base64_encode($dockerfile->implode("\n"));
|
$dockerfile_base64 = base64_encode($dockerfile->implode("\n"));
|
||||||
$this->execute_remote_command([
|
$this->execute_remote_command([
|
||||||
executeInDocker($this->deployment_uuid, "echo '{$dockerfile_base64}' | base64 -d > {$this->workdir}{$this->dockerfile_location}"),
|
executeInDocker($this->deployment_uuid, "echo '{$dockerfile_base64}' | base64 -d > {$this->workdir}{$this->dockerfile_location}"),
|
||||||
@@ -1282,10 +1380,10 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
|
|||||||
}
|
}
|
||||||
queue_next_deployment($this->application);
|
queue_next_deployment($this->application);
|
||||||
if ($status === ApplicationDeploymentStatus::FINISHED->value) {
|
if ($status === ApplicationDeploymentStatus::FINISHED->value) {
|
||||||
$this->application->environment->project->team->notify(new DeploymentSuccess($this->application, $this->deployment_uuid, $this->preview));
|
$this->application->environment->project->team?->notify(new DeploymentSuccess($this->application, $this->deployment_uuid, $this->preview));
|
||||||
}
|
}
|
||||||
if ($status === ApplicationDeploymentStatus::FAILED->value) {
|
if ($status === ApplicationDeploymentStatus::FAILED->value) {
|
||||||
$this->application->environment->project->team->notify(new DeploymentFailed($this->application, $this->deployment_uuid, $this->preview));
|
$this->application->environment->project->team?->notify(new DeploymentFailed($this->application, $this->deployment_uuid, $this->preview));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
180
app/Jobs/ApplicationDeploymentNewJob.php
Normal file
180
app/Jobs/ApplicationDeploymentNewJob.php
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -44,10 +44,10 @@ class CheckLogDrainContainerJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
{
|
{
|
||||||
// ray("checking log drain statuses for {$this->server->id}");
|
// ray("checking log drain statuses for {$this->server->id}");
|
||||||
try {
|
try {
|
||||||
if (!$this->server->isServerReady()) {
|
if (!$this->server->isFunctional()) {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
$containers = instant_remote_process(["docker container ls -q"], $this->server);
|
$containers = instant_remote_process(["docker container ls -q"], $this->server, false);
|
||||||
if (!$containers) {
|
if (!$containers) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -63,19 +63,19 @@ class CheckLogDrainContainerJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
Sleep::for(10)->seconds();
|
Sleep::for(10)->seconds();
|
||||||
if ($this->healthcheck()) {
|
if ($this->healthcheck()) {
|
||||||
if ($this->server->log_drain_notification_sent) {
|
if ($this->server->log_drain_notification_sent) {
|
||||||
$this->server->team->notify(new ContainerRestarted('Coolify Log Drainer', $this->server));
|
$this->server->team?->notify(new ContainerRestarted('Coolify Log Drainer', $this->server));
|
||||||
$this->server->update(['log_drain_notification_sent' => false]);
|
$this->server->update(['log_drain_notification_sent' => false]);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!$this->server->log_drain_notification_sent) {
|
if (!$this->server->log_drain_notification_sent) {
|
||||||
ray('Log drain container still unhealthy. Sending notification...');
|
ray('Log drain container still unhealthy. Sending notification...');
|
||||||
$this->server->team->notify(new ContainerStopped('Coolify Log Drainer', $this->server, null));
|
$this->server->team?->notify(new ContainerStopped('Coolify Log Drainer', $this->server, null));
|
||||||
$this->server->update(['log_drain_notification_sent' => true]);
|
$this->server->update(['log_drain_notification_sent' => true]);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if ($this->server->log_drain_notification_sent) {
|
if ($this->server->log_drain_notification_sent) {
|
||||||
$this->server->team->notify(new ContainerRestarted('Coolify Log Drainer', $this->server));
|
$this->server->team?->notify(new ContainerRestarted('Coolify Log Drainer', $this->server));
|
||||||
$this->server->update(['log_drain_notification_sent' => false]);
|
$this->server->update(['log_drain_notification_sent' => false]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Jobs;
|
namespace App\Jobs;
|
||||||
|
|
||||||
|
use App\Actions\Database\StartDatabaseProxy;
|
||||||
use App\Actions\Proxy\CheckProxy;
|
use App\Actions\Proxy\CheckProxy;
|
||||||
use App\Actions\Proxy\StartProxy;
|
use App\Actions\Proxy\StartProxy;
|
||||||
use App\Models\ApplicationPreview;
|
use App\Models\ApplicationPreview;
|
||||||
@@ -21,46 +22,51 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
{
|
{
|
||||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||||
|
|
||||||
public function __construct(public Server $server)
|
public $tries = 4;
|
||||||
|
public function backoff(): int
|
||||||
{
|
{
|
||||||
$this->handle();
|
return isDev() ? 1 : 3;
|
||||||
}
|
}
|
||||||
public function middleware(): array
|
public function middleware(): array
|
||||||
{
|
{
|
||||||
return [(new WithoutOverlapping($this->server->id))->dontRelease()];
|
return [(new WithoutOverlapping($this->server->uuid))];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function uniqueId(): int
|
public function uniqueId(): int
|
||||||
{
|
{
|
||||||
return $this->server->id;
|
return $this->server->uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __construct(public Server $server)
|
||||||
|
{
|
||||||
|
// $this->handle();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
// ray("checking container statuses for {$this->server->id}");
|
if (!$this->server->isServerReady($this->tries)) {
|
||||||
|
throw new \RuntimeException('Server is not reachable.');
|
||||||
|
};
|
||||||
try {
|
try {
|
||||||
if (!$this->server->isServerReady()) {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
if ($this->server->isSwarm()) {
|
if ($this->server->isSwarm()) {
|
||||||
$containers = instant_remote_process(["docker service inspect $(docker service ls -q) --format '{{json .}}'"], $this->server, false);
|
$containers = instant_remote_process(["docker service inspect $(docker service ls -q) --format '{{json .}}'"], $this->server, false);
|
||||||
$containerReplicase = instant_remote_process(["docker service ls --format '{{json .}}'"], $this->server, false);
|
$containerReplicates = instant_remote_process(["docker service ls --format '{{json .}}'"], $this->server, false);
|
||||||
} else {
|
} else {
|
||||||
// Precheck for containers
|
// Precheck for containers
|
||||||
$containers = instant_remote_process(["docker container ls -q"], $this->server);
|
$containers = instant_remote_process(["docker container ls -q"], $this->server, false);
|
||||||
if (!$containers) {
|
if (!$containers) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$containers = instant_remote_process(["docker container inspect $(docker container ls -q) --format '{{json .}}'"], $this->server, false);
|
$containers = instant_remote_process(["docker container inspect $(docker container ls -q) --format '{{json .}}'"], $this->server, false);
|
||||||
$containerReplicase = null;
|
$containerReplicates = null;
|
||||||
}
|
}
|
||||||
if (is_null($containers)) {
|
if (is_null($containers)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$containers = format_docker_command_output_to_json($containers);
|
$containers = format_docker_command_output_to_json($containers);
|
||||||
if ($containerReplicase) {
|
if ($containerReplicates) {
|
||||||
$containerReplicase = format_docker_command_output_to_json($containerReplicase);
|
$containerReplicates = format_docker_command_output_to_json($containerReplicates);
|
||||||
foreach ($containerReplicase as $containerReplica) {
|
foreach ($containerReplicates as $containerReplica) {
|
||||||
$name = data_get($containerReplica, 'Name');
|
$name = data_get($containerReplica, 'Name');
|
||||||
$containers = $containers->map(function ($container) use ($name, $containerReplica) {
|
$containers = $containers->map(function ($container) use ($name, $containerReplica) {
|
||||||
if (data_get($container, 'Spec.Name') === $name) {
|
if (data_get($container, 'Spec.Name') === $name) {
|
||||||
@@ -83,7 +89,6 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
$databases = $this->server->databases();
|
$databases = $this->server->databases();
|
||||||
$services = $this->server->services()->get();
|
$services = $this->server->services()->get();
|
||||||
$previews = $this->server->previews();
|
$previews = $this->server->previews();
|
||||||
|
|
||||||
$foundApplications = [];
|
$foundApplications = [];
|
||||||
$foundApplicationPreviews = [];
|
$foundApplicationPreviews = [];
|
||||||
$foundDatabases = [];
|
$foundDatabases = [];
|
||||||
@@ -95,7 +100,6 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
$uuid = data_get($labels, 'coolify.name');
|
$uuid = data_get($labels, 'coolify.name');
|
||||||
} else {
|
} else {
|
||||||
$labels = data_get($container, 'Config.Labels');
|
$labels = data_get($container, 'Config.Labels');
|
||||||
$uuid = data_get($labels, 'com.docker.compose.service');
|
|
||||||
}
|
}
|
||||||
$containerStatus = data_get($container, 'State.Status');
|
$containerStatus = data_get($container, 'State.Status');
|
||||||
$containerHealth = data_get($container, 'State.Health.Status', 'unhealthy');
|
$containerHealth = data_get($container, 'State.Health.Status', 'unhealthy');
|
||||||
@@ -131,14 +135,29 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
$uuid = data_get($labels, 'com.docker.compose.service');
|
||||||
if ($uuid) {
|
if ($uuid) {
|
||||||
$database = $databases->where('uuid', $uuid)->first();
|
$database = $databases->where('uuid', $uuid)->first();
|
||||||
if ($database) {
|
if ($database) {
|
||||||
|
$isPublic = data_get($database, 'is_public');
|
||||||
$foundDatabases[] = $database->id;
|
$foundDatabases[] = $database->id;
|
||||||
$statusFromDb = $database->status;
|
$statusFromDb = $database->status;
|
||||||
if ($statusFromDb !== $containerStatus) {
|
if ($statusFromDb !== $containerStatus) {
|
||||||
$database->update(['status' => $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 {
|
} else {
|
||||||
// Notify user that this container should not be there.
|
// Notify user that this container should not be there.
|
||||||
}
|
}
|
||||||
@@ -297,8 +316,6 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
if ($shouldStart) {
|
if ($shouldStart) {
|
||||||
StartProxy::run($this->server, false);
|
StartProxy::run($this->server, false);
|
||||||
$this->server->team?->notify(new ContainerRestarted('coolify-proxy', $this->server));
|
$this->server->team?->notify(new ContainerRestarted('coolify-proxy', $this->server));
|
||||||
} else {
|
|
||||||
ray('Proxy could not be started.');
|
|
||||||
}
|
}
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
ray($e);
|
ray($e);
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ class CoolifyTask implements ShouldQueue, ShouldBeEncrypted
|
|||||||
public function __construct(
|
public function __construct(
|
||||||
public Activity $activity,
|
public Activity $activity,
|
||||||
public bool $ignore_errors = false,
|
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, [
|
$remote_process = resolve(RunRemoteProcess::class, [
|
||||||
'activity' => $this->activity,
|
'activity' => $this->activity,
|
||||||
'ignore_errors' => $this->ignore_errors,
|
'ignore_errors' => $this->ignore_errors,
|
||||||
|
'call_event_on_finish' => $this->call_event_on_finish
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$remote_process();
|
$remote_process();
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
namespace App\Jobs;
|
namespace App\Jobs;
|
||||||
|
|
||||||
use App\Actions\Database\StopDatabase;
|
use App\Actions\Database\StopDatabase;
|
||||||
|
use App\Events\BackupCreated;
|
||||||
use App\Models\S3Storage;
|
use App\Models\S3Storage;
|
||||||
use App\Models\ScheduledDatabaseBackup;
|
use App\Models\ScheduledDatabaseBackup;
|
||||||
use App\Models\ScheduledDatabaseBackupExecution;
|
use App\Models\ScheduledDatabaseBackupExecution;
|
||||||
@@ -74,6 +75,7 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
public function handle(): void
|
public function handle(): void
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
BackupCreated::dispatch($this->team->id);
|
||||||
// Check if team is exists
|
// Check if team is exists
|
||||||
if (is_null($this->team)) {
|
if (is_null($this->team)) {
|
||||||
$this->backup->update(['status' => 'failed']);
|
$this->backup->update(['status' => 'failed']);
|
||||||
@@ -284,7 +286,7 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
if ($this->backup->save_s3) {
|
if ($this->backup->save_s3) {
|
||||||
$this->upload_to_s3();
|
$this->upload_to_s3();
|
||||||
}
|
}
|
||||||
$this->team->notify(new BackupSuccess($this->backup, $this->database));
|
$this->team?->notify(new BackupSuccess($this->backup, $this->database));
|
||||||
$this->backup_log->update([
|
$this->backup_log->update([
|
||||||
'status' => 'success',
|
'status' => 'success',
|
||||||
'message' => $this->backup_output,
|
'message' => $this->backup_output,
|
||||||
@@ -300,13 +302,15 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
send_internal_notification('DatabaseBackupJob failed with: ' . $e->getMessage());
|
send_internal_notification('DatabaseBackupJob failed with: ' . $e->getMessage());
|
||||||
$this->team->notify(new BackupFailed($this->backup, $this->database, $this->backup_output));
|
$this->team?->notify(new BackupFailed($this->backup, $this->database, $this->backup_output));
|
||||||
throw $e;
|
throw $e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
send_internal_notification('DatabaseBackupJob failed with: ' . $e->getMessage());
|
send_internal_notification('DatabaseBackupJob failed with: ' . $e->getMessage());
|
||||||
throw $e;
|
throw $e;
|
||||||
|
} finally {
|
||||||
|
BackupCreated::dispatch($this->team->id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private function backup_standalone_mongodb(string $databaseWithCollections): void
|
private function backup_standalone_mongodb(string $databaseWithCollections): void
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ namespace App\Jobs;
|
|||||||
|
|
||||||
use App\Actions\Application\StopApplication;
|
use App\Actions\Application\StopApplication;
|
||||||
use App\Actions\Database\StopDatabase;
|
use App\Actions\Database\StopDatabase;
|
||||||
use App\Actions\Service\StopService;
|
use App\Actions\Service\DeleteService;
|
||||||
use App\Models\Application;
|
use App\Models\Application;
|
||||||
use App\Models\Service;
|
use App\Models\Service;
|
||||||
use App\Models\StandaloneMariadb;
|
use App\Models\StandaloneMariadb;
|
||||||
@@ -32,9 +32,10 @@ class DeleteResourceJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
try {
|
try {
|
||||||
$server = $this->resource->destination->server;
|
$server = $this->resource->destination->server;
|
||||||
if (!$server->isFunctional()) {
|
if (!$server->isFunctional()) {
|
||||||
$this->resource->delete();
|
$this->resource->forceDelete();
|
||||||
return 'Server is not functional';
|
return 'Server is not functional';
|
||||||
}
|
}
|
||||||
|
$this->resource->delete();
|
||||||
switch ($this->resource->type()) {
|
switch ($this->resource->type()) {
|
||||||
case 'application':
|
case 'application':
|
||||||
StopApplication::run($this->resource);
|
StopApplication::run($this->resource);
|
||||||
@@ -54,11 +55,12 @@ class DeleteResourceJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
case 'standalone-mariadb':
|
case 'standalone-mariadb':
|
||||||
StopDatabase::run($this->resource);
|
StopDatabase::run($this->resource);
|
||||||
break;
|
break;
|
||||||
case 'service':
|
|
||||||
StopService::run($this->resource);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
$this->resource->delete();
|
if ($this->resource->type() === 'service') {
|
||||||
|
DeleteService::dispatch($this->resource);
|
||||||
|
} else {
|
||||||
|
$this->resource->forceDelete();
|
||||||
|
}
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
send_internal_notification('ContainerStoppingJob failed with: ' . $e->getMessage());
|
send_internal_notification('ContainerStoppingJob failed with: ' . $e->getMessage());
|
||||||
throw $e;
|
throw $e;
|
||||||
|
|||||||
@@ -17,30 +17,38 @@ class ServerStatusJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||||
|
|
||||||
public ?int $disk_usage = null;
|
public ?int $disk_usage = null;
|
||||||
|
public $tries = 4;
|
||||||
|
public function backoff(): int
|
||||||
|
{
|
||||||
|
return isDev() ? 1 : 3;
|
||||||
|
}
|
||||||
public function __construct(public Server $server)
|
public function __construct(public Server $server)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
public function middleware(): array
|
public function middleware(): array
|
||||||
{
|
{
|
||||||
return [(new WithoutOverlapping($this->server->id))->dontRelease()];
|
return [(new WithoutOverlapping($this->server->uuid))];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function uniqueId(): int
|
public function uniqueId(): int
|
||||||
{
|
{
|
||||||
return $this->server->id;
|
return $this->server->uuid;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function handle(): void
|
public function handle()
|
||||||
{
|
{
|
||||||
ray("checking server status for {$this->server->id}");
|
ray("checking server status for {$this->server->id}");
|
||||||
|
if (!$this->server->isServerReady(4)) {
|
||||||
|
throw new \RuntimeException('Server is not reachable.');
|
||||||
|
};
|
||||||
try {
|
try {
|
||||||
if ($this->server->isServerReady()) {
|
if ($this->server->isFunctional()) {
|
||||||
$this->cleanup(notify: false);
|
$this->cleanup(notify: false);
|
||||||
}
|
}
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
send_internal_notification('ServerStatusJob failed with: ' . $e->getMessage());
|
send_internal_notification('ServerStatusJob failed with: ' . $e->getMessage());
|
||||||
ray($e->getMessage());
|
ray($e->getMessage());
|
||||||
handleError($e);
|
return handleError($e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public function cleanup(bool $notify = false): void
|
public function cleanup(bool $notify = false): void
|
||||||
@@ -54,7 +62,7 @@ class ServerStatusJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
} else {
|
} else {
|
||||||
$this->server->high_disk_usage_notification_sent = true;
|
$this->server->high_disk_usage_notification_sent = true;
|
||||||
$this->server->save();
|
$this->server->save();
|
||||||
$this->server->team->notify(new HighDiskUsage($this->server, $this->disk_usage, $this->server->settings->cleanup_after_percentage));
|
$this->server->team?->notify(new HighDiskUsage($this->server, $this->disk_usage, $this->server->settings->cleanup_after_percentage));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
DockerCleanupJob::dispatchSync($this->server);
|
DockerCleanupJob::dispatchSync($this->server);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Http\Livewire;
|
namespace App\Livewire;
|
||||||
|
|
||||||
use App\Enums\ProcessStatus;
|
use App\Enums\ProcessStatus;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
@@ -8,7 +8,7 @@ use Spatie\Activitylog\Models\Activity;
|
|||||||
|
|
||||||
class ActivityMonitor extends Component
|
class ActivityMonitor extends Component
|
||||||
{
|
{
|
||||||
public string|null $header = null;
|
public ?string $header = null;
|
||||||
public $activityId;
|
public $activityId;
|
||||||
public $isPollingActive = false;
|
public $isPollingActive = false;
|
||||||
|
|
||||||
@@ -26,31 +26,30 @@ class ActivityMonitor extends Component
|
|||||||
|
|
||||||
public function hydrateActivity()
|
public function hydrateActivity()
|
||||||
{
|
{
|
||||||
$this->activity = Activity::query()
|
$this->activity = Activity::find($this->activityId);
|
||||||
->find($this->activityId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function polling()
|
public function polling()
|
||||||
{
|
{
|
||||||
$this->hydrateActivity();
|
$this->hydrateActivity();
|
||||||
$this->setStatus(ProcessStatus::IN_PROGRESS);
|
// $this->setStatus(ProcessStatus::IN_PROGRESS);
|
||||||
$exit_code = data_get($this->activity, 'properties.exitCode');
|
$exit_code = data_get($this->activity, 'properties.exitCode');
|
||||||
if ($exit_code !== null) {
|
if ($exit_code !== null) {
|
||||||
if ($exit_code === 0) {
|
if ($exit_code === 0) {
|
||||||
$this->setStatus(ProcessStatus::FINISHED);
|
// $this->setStatus(ProcessStatus::FINISHED);
|
||||||
} else {
|
} else {
|
||||||
$this->setStatus(ProcessStatus::ERROR);
|
// $this->setStatus(ProcessStatus::ERROR);
|
||||||
}
|
}
|
||||||
$this->isPollingActive = false;
|
$this->isPollingActive = false;
|
||||||
$this->emit('activityFinished');
|
$this->dispatch('activityFinished');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function setStatus($status)
|
// protected function setStatus($status)
|
||||||
{
|
// {
|
||||||
$this->activity->properties = $this->activity->properties->merge([
|
// $this->activity->properties = $this->activity->properties->merge([
|
||||||
'status' => $status,
|
// 'status' => $status,
|
||||||
]);
|
// ]);
|
||||||
$this->activity->save();
|
// $this->activity->save();
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Http\Livewire\Boarding;
|
namespace App\Livewire\Boarding;
|
||||||
|
|
||||||
use App\Actions\Server\InstallDocker;
|
use App\Actions\Server\InstallDocker;
|
||||||
use App\Models\PrivateKey;
|
use App\Models\PrivateKey;
|
||||||
@@ -32,6 +32,7 @@ class Index extends Component
|
|||||||
public ?int $remoteServerPort = 22;
|
public ?int $remoteServerPort = 22;
|
||||||
public ?string $remoteServerUser = 'root';
|
public ?string $remoteServerUser = 'root';
|
||||||
public bool $isSwarmManager = false;
|
public bool $isSwarmManager = false;
|
||||||
|
public bool $isCloudflareTunnel = false;
|
||||||
public ?Server $createdServer = null;
|
public ?Server $createdServer = null;
|
||||||
|
|
||||||
public Collection $projects;
|
public Collection $projects;
|
||||||
@@ -87,7 +88,7 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
|
|||||||
if ($this->selectedServerType === 'localhost') {
|
if ($this->selectedServerType === 'localhost') {
|
||||||
$this->createdServer = Server::find(0);
|
$this->createdServer = Server::find(0);
|
||||||
if (!$this->createdServer) {
|
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();
|
$this->serverPublicKey = $this->createdServer->privateKey->publicKey();
|
||||||
return $this->validateServer('localhost');
|
return $this->validateServer('localhost');
|
||||||
@@ -109,7 +110,7 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
|
|||||||
{
|
{
|
||||||
$this->createdServer = Server::find($this->selectedExistingServer);
|
$this->createdServer = Server::find($this->selectedExistingServer);
|
||||||
if (!$this->createdServer) {
|
if (!$this->createdServer) {
|
||||||
$this->emit('error', 'Server is not found.');
|
$this->dispatch('error', 'Server is not found.');
|
||||||
$this->currentState = 'private-key';
|
$this->currentState = 'private-key';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -172,7 +173,7 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
|
|||||||
$this->privateKey = formatPrivateKey($this->privateKey);
|
$this->privateKey = formatPrivateKey($this->privateKey);
|
||||||
$foundServer = Server::whereIp($this->remoteServerHost)->first();
|
$foundServer = Server::whereIp($this->remoteServerHost)->first();
|
||||||
if ($foundServer) {
|
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([
|
$this->createdServer = Server::create([
|
||||||
'name' => $this->remoteServerName,
|
'name' => $this->remoteServerName,
|
||||||
@@ -184,6 +185,7 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
|
|||||||
'team_id' => currentTeam()->id,
|
'team_id' => currentTeam()->id,
|
||||||
]);
|
]);
|
||||||
$this->createdServer->settings->is_swarm_manager = $this->isSwarmManager;
|
$this->createdServer->settings->is_swarm_manager = $this->isSwarmManager;
|
||||||
|
$this->createdServer->settings->is_cloudflare_tunnel = $this->isCloudflareTunnel;
|
||||||
$this->createdServer->settings->save();
|
$this->createdServer->settings->save();
|
||||||
$this->createdServer->addInitialNetwork();
|
$this->createdServer->addInitialNetwork();
|
||||||
$this->validateServer();
|
$this->validateServer();
|
||||||
@@ -200,6 +202,7 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
|
|||||||
]);
|
]);
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
$this->serverReachable = false;
|
$this->serverReachable = false;
|
||||||
|
$this->createdServer->delete();
|
||||||
return handleError(error: $e, livewire: $this);
|
return handleError(error: $e, livewire: $this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -224,8 +227,8 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
|
|||||||
try {
|
try {
|
||||||
$this->dockerInstallationStarted = true;
|
$this->dockerInstallationStarted = true;
|
||||||
$activity = InstallDocker::run($this->createdServer);
|
$activity = InstallDocker::run($this->createdServer);
|
||||||
$this->emit('installDocker');
|
$this->dispatch('installDocker');
|
||||||
$this->emit('newMonitorActivity', $activity->id);
|
$this->dispatch('newMonitorActivity', $activity->id);
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
$this->dockerInstallationStarted = false;
|
$this->dockerInstallationStarted = false;
|
||||||
return handleError(error: $e, livewire: $this);
|
return handleError(error: $e, livewire: $this);
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Http\Livewire;
|
namespace App\Livewire;
|
||||||
|
|
||||||
use App\Actions\License\CheckResaleLicense;
|
use App\Actions\License\CheckResaleLicense;
|
||||||
use App\Models\InstanceSettings;
|
use App\Models\InstanceSettings;
|
||||||
@@ -33,11 +33,11 @@ class CheckLicense extends Component
|
|||||||
if ($this->settings->resale_license) {
|
if ($this->settings->resale_license) {
|
||||||
try {
|
try {
|
||||||
CheckResaleLicense::run();
|
CheckResaleLicense::run();
|
||||||
$this->emit('reloadWindow');
|
$this->dispatch('reloadWindow');
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
session()->flash('error', 'Something went wrong. Please contact support. <br>Error: ' . $e->getMessage());
|
session()->flash('error', 'Something went wrong. Please contact support. <br>Error: ' . $e->getMessage());
|
||||||
ray($e->getMessage());
|
ray($e->getMessage());
|
||||||
return redirect()->to('/settings/license');
|
return $this->redirect('/settings/license', navigate: true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Http\Livewire;
|
namespace App\Livewire;
|
||||||
|
|
||||||
use App\Models\Project;
|
use App\Models\Project;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
@@ -10,7 +10,6 @@ class Dashboard extends Component
|
|||||||
{
|
{
|
||||||
public $projects = [];
|
public $projects = [];
|
||||||
public $servers = [];
|
public $servers = [];
|
||||||
|
|
||||||
public function mount()
|
public function mount()
|
||||||
{
|
{
|
||||||
$this->servers = Server::ownedByCurrentTeam()->get();
|
$this->servers = Server::ownedByCurrentTeam()->get();
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Http\Livewire\Destination;
|
namespace App\Livewire\Destination;
|
||||||
|
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
|
||||||
@@ -30,13 +30,13 @@ class Form extends Component
|
|||||||
try {
|
try {
|
||||||
if ($this->destination->getMorphClass() === 'App\Models\StandaloneDocker') {
|
if ($this->destination->getMorphClass() === 'App\Models\StandaloneDocker') {
|
||||||
if ($this->destination->attachedTo()) {
|
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 disconnect {$this->destination->network} coolify-proxy"], $this->destination->server, throwError: false);
|
||||||
instant_remote_process(['docker network rm -f ' . $this->destination->network], $this->destination->server);
|
instant_remote_process(['docker network rm -f ' . $this->destination->network], $this->destination->server);
|
||||||
}
|
}
|
||||||
$this->destination->delete();
|
$this->destination->delete();
|
||||||
return redirect()->route('dashboard');
|
return $this->redirectRoute('dashboard', navigate: true);
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
100
app/Livewire/Destination/New/Docker.php
Normal file
100
app/Livewire/Destination/New/Docker.php
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Livewire\Destination\New;
|
||||||
|
|
||||||
|
use App\Models\Server;
|
||||||
|
use App\Models\StandaloneDocker as ModelsStandaloneDocker;
|
||||||
|
use App\Models\SwarmDocker;
|
||||||
|
use Illuminate\Database\Eloquent\Collection;
|
||||||
|
use Livewire\Component;
|
||||||
|
use Visus\Cuid2\Cuid2;
|
||||||
|
|
||||||
|
class Docker extends Component
|
||||||
|
{
|
||||||
|
public string $name;
|
||||||
|
public string $network;
|
||||||
|
|
||||||
|
public Collection $servers;
|
||||||
|
public Server $server;
|
||||||
|
public ?int $server_id = null;
|
||||||
|
public bool $is_swarm = false;
|
||||||
|
|
||||||
|
protected $rules = [
|
||||||
|
'name' => 'required|string',
|
||||||
|
'network' => 'required|string',
|
||||||
|
'server_id' => 'required|integer',
|
||||||
|
'is_swarm' => 'boolean'
|
||||||
|
];
|
||||||
|
protected $validationAttributes = [
|
||||||
|
'name' => 'name',
|
||||||
|
'network' => 'network',
|
||||||
|
'server_id' => 'server',
|
||||||
|
'is_swarm' => 'swarm'
|
||||||
|
];
|
||||||
|
|
||||||
|
public function mount()
|
||||||
|
{
|
||||||
|
if (request()->query('server_id')) {
|
||||||
|
$this->server_id = request()->query('server_id');
|
||||||
|
} else {
|
||||||
|
if ($this->servers->count() > 0) {
|
||||||
|
$this->server_id = $this->servers->first()->id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (request()->query('network_name')) {
|
||||||
|
$this->network = request()->query('network_name');
|
||||||
|
} else {
|
||||||
|
$this->network = new Cuid2(7);
|
||||||
|
}
|
||||||
|
$this->name = str("{$this->servers->first()->name}-{$this->network}")->kebab();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function generate_name()
|
||||||
|
{
|
||||||
|
$this->server = Server::find($this->server_id);
|
||||||
|
$this->name = str("{$this->server->name}-{$this->network}")->kebab();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function submit()
|
||||||
|
{
|
||||||
|
$this->validate();
|
||||||
|
try {
|
||||||
|
$this->server = Server::find($this->server_id);
|
||||||
|
if ($this->is_swarm) {
|
||||||
|
$found = $this->server->swarmDockers()->where('network', $this->network)->first();
|
||||||
|
if ($found) {
|
||||||
|
$this->dispatch('error', 'Network already added to this server.');
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
$docker = SwarmDocker::create([
|
||||||
|
'name' => $this->name,
|
||||||
|
'network' => $this->network,
|
||||||
|
'server_id' => $this->server_id,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$found = $this->server->standaloneDockers()->where('network', $this->network)->first();
|
||||||
|
if ($found) {
|
||||||
|
$this->dispatch('error', 'Network already added to this server.');
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
$docker = ModelsStandaloneDocker::create([
|
||||||
|
'name' => $this->name,
|
||||||
|
'network' => $this->network,
|
||||||
|
'server_id' => $this->server_id,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$this->createNetworkAndAttachToProxy();
|
||||||
|
return $this->redirectRoute('destination.show', $docker->uuid, navigate: true);
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
return handleError($e, $this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function createNetworkAndAttachToProxy()
|
||||||
|
{
|
||||||
|
$connectProxyToDockerNetworks = connectProxyToNetworks($this->server);
|
||||||
|
instant_remote_process($connectProxyToDockerNetworks, $this->server, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Http\Livewire\Destination;
|
namespace App\Livewire\Destination;
|
||||||
|
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
@@ -13,7 +13,11 @@ class Show extends Component
|
|||||||
|
|
||||||
public function scan()
|
public function scan()
|
||||||
{
|
{
|
||||||
$alreadyAddedNetworks = $this->server->standaloneDockers;
|
if ($this->server->isSwarm()) {
|
||||||
|
$alreadyAddedNetworks = $this->server->swarmDockers;
|
||||||
|
} else {
|
||||||
|
$alreadyAddedNetworks = $this->server->standaloneDockers;
|
||||||
|
}
|
||||||
$networks = instant_remote_process(['docker network ls --format "{{json .}}"'], $this->server, false);
|
$networks = instant_remote_process(['docker network ls --format "{{json .}}"'], $this->server, false);
|
||||||
$this->networks = format_docker_command_output_to_json($networks)->filter(function ($network) {
|
$this->networks = format_docker_command_output_to_json($networks)->filter(function ($network) {
|
||||||
return $network['Name'] !== 'bridge' && $network['Name'] !== 'host' && $network['Name'] !== 'none';
|
return $network['Name'] !== 'bridge' && $network['Name'] !== 'host' && $network['Name'] !== 'none';
|
||||||
@@ -21,7 +25,7 @@ class Show extends Component
|
|||||||
return !$alreadyAddedNetworks->contains('network', $network['Name']);
|
return !$alreadyAddedNetworks->contains('network', $network['Name']);
|
||||||
});
|
});
|
||||||
if ($this->networks->count() === 0) {
|
if ($this->networks->count() === 0) {
|
||||||
$this->emit('success', 'No new networks found.');
|
$this->dispatch('success', 'No new networks found.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Http\Livewire\Dev;
|
namespace App\Livewire\Dev;
|
||||||
|
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Http\Livewire;
|
namespace App\Livewire;
|
||||||
|
|
||||||
use Illuminate\Support\Facades\Hash;
|
use Illuminate\Support\Facades\Hash;
|
||||||
use DanHarrin\LivewireRateLimiting\WithRateLimiting;
|
use DanHarrin\LivewireRateLimiting\WithRateLimiting;
|
||||||
@@ -35,7 +35,7 @@ class ForcePasswordReset extends Component
|
|||||||
if ($firstLogin) {
|
if ($firstLogin) {
|
||||||
send_internal_notification('First login for ' . auth()->user()->email);
|
send_internal_notification('First login for ' . auth()->user()->email);
|
||||||
}
|
}
|
||||||
return redirect()->route('dashboard');
|
return $this->redirectRoute('dashboard', navigate: true);
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Http\Livewire;
|
namespace App\Livewire;
|
||||||
|
|
||||||
use DanHarrin\LivewireRateLimiting\WithRateLimiting;
|
use DanHarrin\LivewireRateLimiting\WithRateLimiting;
|
||||||
use Illuminate\Notifications\Messages\MailMessage;
|
use Illuminate\Notifications\Messages\MailMessage;
|
||||||
@@ -42,7 +42,7 @@ class Help extends Component
|
|||||||
);
|
);
|
||||||
$mail->subject("[HELP - {$subscriptionType}]: {$this->subject}");
|
$mail->subject("[HELP - {$subscriptionType}]: {$this->subject}");
|
||||||
send_user_an_email($mail, auth()->user()?->email, 'hi@coollabs.io');
|
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) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
28
app/Livewire/Modal/EditCompose.php
Normal file
28
app/Livewire/Modal/EditCompose.php
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Http\Livewire\Notifications;
|
namespace App\Livewire\Notifications;
|
||||||
|
|
||||||
use App\Models\Team;
|
use App\Models\Team;
|
||||||
use App\Notifications\Test;
|
use App\Notifications\Test;
|
||||||
@@ -47,12 +47,12 @@ class DiscordSettings extends Component
|
|||||||
{
|
{
|
||||||
$this->team->save();
|
$this->team->save();
|
||||||
refreshSession();
|
refreshSession();
|
||||||
$this->emit('success', 'Settings saved.');
|
$this->dispatch('success', 'Settings saved.');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function sendTestNotification()
|
public function sendTestNotification()
|
||||||
{
|
{
|
||||||
$this->team->notify(new Test());
|
$this->team?->notify(new Test());
|
||||||
$this->emit('success', 'Test notification sent.');
|
$this->dispatch('success', 'Test notification sent.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Http\Livewire\Notifications;
|
namespace App\Livewire\Notifications;
|
||||||
|
|
||||||
use App\Models\InstanceSettings;
|
use App\Models\InstanceSettings;
|
||||||
use App\Models\Team;
|
use App\Models\Team;
|
||||||
@@ -63,15 +63,15 @@ class EmailSettings extends Component
|
|||||||
]);
|
]);
|
||||||
$this->team->save();
|
$this->team->save();
|
||||||
refreshSession();
|
refreshSession();
|
||||||
$this->emit('success', 'Settings saved successfully.');
|
$this->dispatch('success', 'Settings saved successfully.');
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public function sendTestNotification()
|
public function sendTestNotification()
|
||||||
{
|
{
|
||||||
$this->team->notify(new Test($this->emails));
|
$this->team?->notify(new Test($this->emails));
|
||||||
$this->emit('success', 'Test Email sent successfully.');
|
$this->dispatch('success', 'Test Email sent successfully.');
|
||||||
}
|
}
|
||||||
public function instantSaveInstance()
|
public function instantSaveInstance()
|
||||||
{
|
{
|
||||||
@@ -83,7 +83,7 @@ class EmailSettings extends Component
|
|||||||
$this->team->resend_enabled = false;
|
$this->team->resend_enabled = false;
|
||||||
$this->team->save();
|
$this->team->save();
|
||||||
refreshSession();
|
refreshSession();
|
||||||
$this->emit('success', 'Settings saved successfully.');
|
$this->dispatch('success', 'Settings saved successfully.');
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
@@ -113,7 +113,7 @@ class EmailSettings extends Component
|
|||||||
{
|
{
|
||||||
$this->team->save();
|
$this->team->save();
|
||||||
refreshSession();
|
refreshSession();
|
||||||
$this->emit('success', 'Settings saved.');
|
$this->dispatch('success', 'Settings saved.');
|
||||||
}
|
}
|
||||||
public function submit()
|
public function submit()
|
||||||
{
|
{
|
||||||
@@ -131,7 +131,7 @@ class EmailSettings extends Component
|
|||||||
]);
|
]);
|
||||||
$this->team->save();
|
$this->team->save();
|
||||||
refreshSession();
|
refreshSession();
|
||||||
$this->emit('success', 'Settings saved successfully.');
|
$this->dispatch('success', 'Settings saved successfully.');
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
$this->team->smtp_enabled = false;
|
$this->team->smtp_enabled = false;
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
@@ -148,7 +148,7 @@ class EmailSettings extends Component
|
|||||||
]);
|
]);
|
||||||
$this->team->save();
|
$this->team->save();
|
||||||
refreshSession();
|
refreshSession();
|
||||||
$this->emit('success', 'Settings saved successfully.');
|
$this->dispatch('success', 'Settings saved successfully.');
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
$this->team->resend_enabled = false;
|
$this->team->resend_enabled = false;
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
@@ -173,7 +173,7 @@ class EmailSettings extends Component
|
|||||||
]);
|
]);
|
||||||
refreshSession();
|
refreshSession();
|
||||||
$this->team = $team;
|
$this->team = $team;
|
||||||
$this->emit('success', 'Settings saved.');
|
$this->dispatch('success', 'Settings saved.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if ($settings->resend_enabled) {
|
if ($settings->resend_enabled) {
|
||||||
@@ -184,9 +184,9 @@ class EmailSettings extends Component
|
|||||||
]);
|
]);
|
||||||
refreshSession();
|
refreshSession();
|
||||||
$this->team = $team;
|
$this->team = $team;
|
||||||
$this->emit('success', 'Settings saved.');
|
$this->dispatch('success', 'Settings saved.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$this->emit('error', 'Instance SMTP/Resend settings are not enabled.');
|
$this->dispatch('error', 'Instance SMTP/Resend settings are not enabled.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Http\Livewire\Notifications;
|
namespace App\Livewire\Notifications;
|
||||||
|
|
||||||
use App\Models\Team;
|
use App\Models\Team;
|
||||||
use App\Notifications\Test;
|
use App\Notifications\Test;
|
||||||
@@ -53,12 +53,12 @@ class TelegramSettings extends Component
|
|||||||
{
|
{
|
||||||
$this->team->save();
|
$this->team->save();
|
||||||
refreshSession();
|
refreshSession();
|
||||||
$this->emit('success', 'Settings saved.');
|
$this->dispatch('success', 'Settings saved.');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function sendTestNotification()
|
public function sendTestNotification()
|
||||||
{
|
{
|
||||||
$this->team->notify(new Test());
|
$this->team?->notify(new Test());
|
||||||
$this->emit('success', 'Test notification sent.');
|
$this->dispatch('success', 'Test notification sent.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Http\Livewire\PrivateKey;
|
namespace App\Livewire\PrivateKey;
|
||||||
|
|
||||||
use App\Models\PrivateKey;
|
use App\Models\PrivateKey;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
@@ -35,9 +35,9 @@ class Change extends Component
|
|||||||
if ($this->private_key->isEmpty()) {
|
if ($this->private_key->isEmpty()) {
|
||||||
$this->private_key->delete();
|
$this->private_key->delete();
|
||||||
currentTeam()->privateKeys = PrivateKey::where('team_id', currentTeam()->id)->get();
|
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) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Http\Livewire\PrivateKey;
|
namespace App\Livewire\PrivateKey;
|
||||||
|
|
||||||
use App\Models\PrivateKey;
|
use App\Models\PrivateKey;
|
||||||
use DanHarrin\LivewireRateLimiting\WithRateLimiting;
|
use DanHarrin\LivewireRateLimiting\WithRateLimiting;
|
||||||
@@ -67,9 +67,9 @@ class Create extends Component
|
|||||||
'team_id' => currentTeam()->id
|
'team_id' => currentTeam()->id
|
||||||
]);
|
]);
|
||||||
if ($this->from === 'server') {
|
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) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
@@ -1,22 +1,17 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Http\Livewire\Profile;
|
namespace App\Livewire\Profile;
|
||||||
|
|
||||||
use App\Models\User;
|
use Livewire\Attributes\Validate;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
|
||||||
class Form extends Component
|
class Form extends Component
|
||||||
{
|
{
|
||||||
public int $userId;
|
public int $userId;
|
||||||
public string $name;
|
|
||||||
public string $email;
|
public string $email;
|
||||||
|
|
||||||
protected $rules = [
|
#[Validate('required')]
|
||||||
'name' => 'required',
|
public string $name;
|
||||||
];
|
|
||||||
protected $validationAttributes = [
|
|
||||||
'name' => 'name',
|
|
||||||
];
|
|
||||||
|
|
||||||
public function mount()
|
public function mount()
|
||||||
{
|
{
|
||||||
@@ -30,9 +25,11 @@ class Form extends Component
|
|||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$this->validate();
|
$this->validate();
|
||||||
User::where('id', $this->userId)->update([
|
auth()->user()->update([
|
||||||
'name' => $this->name,
|
'name' => $this->name,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
$this->dispatch('success', 'Profile updated successfully.');
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Http\Livewire\Project;
|
namespace App\Livewire\Project;
|
||||||
|
|
||||||
use App\Models\Project;
|
use App\Models\Project;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
@@ -27,7 +27,7 @@ class AddEmpty extends Component
|
|||||||
'description' => $this->description,
|
'description' => $this->description,
|
||||||
'team_id' => currentTeam()->id,
|
'team_id' => currentTeam()->id,
|
||||||
]);
|
]);
|
||||||
return redirect()->route('project.show', $project->uuid);
|
return $this->redirectRoute('project.show', $project->uuid, navigate: true);
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
} finally {
|
} finally {
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Http\Livewire\Project;
|
namespace App\Livewire\Project;
|
||||||
|
|
||||||
use App\Models\Environment;
|
use App\Models\Environment;
|
||||||
use App\Models\Project;
|
use App\Models\Project;
|
||||||
@@ -27,10 +27,10 @@ class AddEnvironment extends Component
|
|||||||
'project_id' => $this->project->id,
|
'project_id' => $this->project->id,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return redirect()->route('project.resources', [
|
return $this->redirectRoute('project.resources', [
|
||||||
'project_uuid' => $this->project->uuid,
|
'project_uuid' => $this->project->uuid,
|
||||||
'environment_name' => $environment->name,
|
'environment_name' => $environment->name,
|
||||||
]);
|
], navigate: true);
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
handleError($e, $this);
|
handleError($e, $this);
|
||||||
} finally {
|
} finally {
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Http\Livewire\Project\Application;
|
namespace App\Livewire\Project\Application;
|
||||||
|
|
||||||
use App\Models\Application;
|
use App\Models\Application;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
@@ -26,26 +26,26 @@ class Advanced extends Component
|
|||||||
if ($this->application->isLogDrainEnabled()) {
|
if ($this->application->isLogDrainEnabled()) {
|
||||||
if (!$this->application->destination->server->isLogDrainEnabled()) {
|
if (!$this->application->destination->server->isLogDrainEnabled()) {
|
||||||
$this->application->settings->is_log_drain_enabled = false;
|
$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;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ($this->application->settings->is_force_https_enabled) {
|
if ($this->application->settings->is_force_https_enabled) {
|
||||||
$this->emit('resetDefaultLabels', false);
|
$this->dispatch('resetDefaultLabels', false);
|
||||||
}
|
}
|
||||||
$this->application->settings->save();
|
$this->application->settings->save();
|
||||||
$this->emit('success', 'Settings saved.');
|
$this->dispatch('success', 'Settings saved.');
|
||||||
}
|
}
|
||||||
public function submit() {
|
public function submit() {
|
||||||
if ($this->application->settings->gpu_count && $this->application->settings->gpu_device_ids) {
|
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_count = null;
|
||||||
$this->application->settings->gpu_device_ids = null;
|
$this->application->settings->gpu_device_ids = null;
|
||||||
$this->application->settings->save();
|
$this->application->settings->save();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$this->application->settings->save();
|
$this->application->settings->save();
|
||||||
$this->emit('success', 'Settings saved.');
|
$this->dispatch('success', 'Settings saved.');
|
||||||
}
|
}
|
||||||
public function render()
|
public function render()
|
||||||
{
|
{
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Http\Livewire\Project\Application;
|
namespace App\Livewire\Project\Application;
|
||||||
|
|
||||||
use App\Models\Application;
|
use App\Models\Application;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
@@ -15,18 +15,18 @@ class Configuration extends Component
|
|||||||
{
|
{
|
||||||
$project = currentTeam()->load(['projects'])->projects->where('uuid', request()->route('project_uuid'))->first();
|
$project = currentTeam()->load(['projects'])->projects->where('uuid', request()->route('project_uuid'))->first();
|
||||||
if (!$project) {
|
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']);
|
$environment = $project->load(['environments'])->environments->where('name', request()->route('environment_name'))->first()->load(['applications']);
|
||||||
if (!$environment) {
|
if (!$environment) {
|
||||||
return redirect()->route('dashboard');
|
return $this->redirectRoute('dashboard', navigate: true);
|
||||||
}
|
}
|
||||||
$application = $environment->applications->where('uuid', request()->route('application_uuid'))->first();
|
$application = $environment->applications->where('uuid', request()->route('application_uuid'))->first();
|
||||||
if (!$application) {
|
if (!$application) {
|
||||||
return redirect()->route('dashboard');
|
return $this->redirectRoute('dashboard', navigate: true);
|
||||||
}
|
}
|
||||||
$this->application = $application;
|
$this->application = $application;
|
||||||
$mainServer = $application->destination->server;
|
$mainServer = $this->application->destination->server;
|
||||||
$servers = Server::ownedByCurrentTeam()->get();
|
$servers = Server::ownedByCurrentTeam()->get();
|
||||||
$this->servers = $servers->filter(function ($server) use ($mainServer) {
|
$this->servers = $servers->filter(function ($server) use ($mainServer) {
|
||||||
return $server->id != $mainServer->id;
|
return $server->id != $mainServer->id;
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Http\Livewire\Project\Application;
|
namespace App\Livewire\Project\Application;
|
||||||
|
|
||||||
use App\Models\ApplicationDeploymentQueue;
|
use App\Models\ApplicationDeploymentQueue;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
@@ -18,7 +18,7 @@ class DeploymentLogs extends Component
|
|||||||
|
|
||||||
public function polling()
|
public function polling()
|
||||||
{
|
{
|
||||||
$this->emit('deploymentFinished');
|
$this->dispatch('deploymentFinished');
|
||||||
$this->application_deployment_queue->refresh();
|
$this->application_deployment_queue->refresh();
|
||||||
if (data_get($this->application_deployment_queue, 'status') == 'finished' || data_get($this->application_deployment_queue, 'status') == 'failed') {
|
if (data_get($this->application_deployment_queue, 'status') == 'finished' || data_get($this->application_deployment_queue, 'status') == 'failed') {
|
||||||
$this->isKeepAliveOn = false;
|
$this->isKeepAliveOn = false;
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Http\Livewire\Project\Application;
|
namespace App\Livewire\Project\Application;
|
||||||
|
|
||||||
use App\Enums\ApplicationDeploymentStatus;
|
use App\Enums\ApplicationDeploymentStatus;
|
||||||
use App\Models\Application;
|
use App\Models\Application;
|
||||||
@@ -18,7 +18,6 @@ class DeploymentNavbar extends Component
|
|||||||
public Server $server;
|
public Server $server;
|
||||||
public bool $is_debug_enabled = false;
|
public bool $is_debug_enabled = false;
|
||||||
protected $listeners = ['deploymentFinished'];
|
protected $listeners = ['deploymentFinished'];
|
||||||
|
|
||||||
public function mount()
|
public function mount()
|
||||||
{
|
{
|
||||||
$this->application = Application::find($this->application_deployment_queue->application_id);
|
$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->is_debug_enabled = !$this->application->settings->is_debug_enabled;
|
||||||
$this->application->settings->save();
|
$this->application->settings->save();
|
||||||
$this->is_debug_enabled = $this->application->settings->is_debug_enabled;
|
$this->is_debug_enabled = $this->application->settings->is_debug_enabled;
|
||||||
$this->emit('refreshQueue');
|
$this->dispatch('refreshQueue');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function cancel()
|
public function cancel()
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Http\Livewire\Project\Application;
|
namespace App\Livewire\Project\Application;
|
||||||
|
|
||||||
use App\Models\Application;
|
use App\Models\Application;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Http\Livewire\Project\Application;
|
namespace App\Livewire\Project\Application;
|
||||||
|
|
||||||
use App\Models\Application;
|
use App\Models\Application;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
@@ -29,8 +29,6 @@ class General extends Component
|
|||||||
public ?string $initialDockerComposeLocation = null;
|
public ?string $initialDockerComposeLocation = null;
|
||||||
public ?string $initialDockerComposePrLocation = null;
|
public ?string $initialDockerComposePrLocation = null;
|
||||||
|
|
||||||
public bool $is_static;
|
|
||||||
|
|
||||||
public $parsedServices = [];
|
public $parsedServices = [];
|
||||||
public $parsedServiceDomains = [];
|
public $parsedServiceDomains = [];
|
||||||
|
|
||||||
@@ -66,6 +64,8 @@ class General extends Component
|
|||||||
'application.custom_labels' => 'nullable',
|
'application.custom_labels' => 'nullable',
|
||||||
'application.dockerfile_target_build' => 'nullable',
|
'application.dockerfile_target_build' => 'nullable',
|
||||||
'application.settings.is_static' => 'boolean|required',
|
'application.settings.is_static' => 'boolean|required',
|
||||||
|
'application.docker_compose_custom_start_command' => 'nullable',
|
||||||
|
'application.docker_compose_custom_build_command' => 'nullable',
|
||||||
];
|
];
|
||||||
protected $validationAttributes = [
|
protected $validationAttributes = [
|
||||||
'application.name' => 'name',
|
'application.name' => 'name',
|
||||||
@@ -96,14 +96,15 @@ class General extends Component
|
|||||||
'application.custom_labels' => 'Custom labels',
|
'application.custom_labels' => 'Custom labels',
|
||||||
'application.dockerfile_target_build' => 'Dockerfile target build',
|
'application.dockerfile_target_build' => 'Dockerfile target build',
|
||||||
'application.settings.is_static' => 'Is static',
|
'application.settings.is_static' => 'Is static',
|
||||||
|
'application.docker_compose_custom_start_command' => 'Docker compose custom start command',
|
||||||
|
'application.docker_compose_custom_build_command' => 'Docker compose custom build command',
|
||||||
];
|
];
|
||||||
|
|
||||||
public function mount()
|
public function mount()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$this->parsedServices = $this->application->parseCompose();
|
$this->parsedServices = $this->application->parseCompose();
|
||||||
} catch (\Throwable $e) {
|
} 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) : [];
|
$this->parsedServiceDomains = $this->application->docker_compose_domains ? json_decode($this->application->docker_compose_domains, true) : [];
|
||||||
|
|
||||||
@@ -112,18 +113,18 @@ class General extends Component
|
|||||||
$this->application->isConfigurationChanged(true);
|
$this->application->isConfigurationChanged(true);
|
||||||
}
|
}
|
||||||
$this->isConfigurationChanged = $this->application->isConfigurationChanged();
|
$this->isConfigurationChanged = $this->application->isConfigurationChanged();
|
||||||
if (is_null(data_get($this->application, 'custom_labels'))) {
|
$this->customLabels = $this->application->parseContainerLabels();
|
||||||
$this->customLabels = str(implode(",", generateLabelsApplication($this->application)))->replace(',', "\n");
|
|
||||||
} else {
|
|
||||||
$this->customLabels = str($this->application->custom_labels)->replace(',', "\n");
|
|
||||||
}
|
|
||||||
$this->initialDockerComposeLocation = $this->application->docker_compose_location;
|
$this->initialDockerComposeLocation = $this->application->docker_compose_location;
|
||||||
$this->checkLabelUpdates();
|
$this->checkLabelUpdates();
|
||||||
}
|
}
|
||||||
public function instantSave()
|
public function instantSave()
|
||||||
{
|
{
|
||||||
$this->application->settings->save();
|
$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)
|
public function loadComposeFile($isInit = false)
|
||||||
{
|
{
|
||||||
@@ -132,7 +133,7 @@ class General extends Component
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
['parsedServices' => $this->parsedServices, 'initialDockerComposeLocation' => $this->initialDockerComposeLocation, 'initialDockerComposePrLocation' => $this->initialDockerComposePrLocation] = $this->application->loadComposeFile($isInit);
|
['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) {
|
} catch (\Throwable $e) {
|
||||||
$this->application->docker_compose_location = $this->initialDockerComposeLocation;
|
$this->application->docker_compose_location = $this->initialDockerComposeLocation;
|
||||||
$this->application->docker_compose_pr_location = $this->initialDockerComposePrLocation;
|
$this->application->docker_compose_pr_location = $this->initialDockerComposePrLocation;
|
||||||
@@ -149,14 +150,13 @@ class General extends Component
|
|||||||
$this->parsedServiceDomains[$serviceName]['domain'] = $domain;
|
$this->parsedServiceDomains[$serviceName]['domain'] = $domain;
|
||||||
$this->application->docker_compose_domains = json_encode($this->parsedServiceDomains);
|
$this->application->docker_compose_domains = json_encode($this->parsedServiceDomains);
|
||||||
$this->application->save();
|
$this->application->save();
|
||||||
$this->emit('success', 'Domain generated.');
|
|
||||||
}
|
}
|
||||||
return $domain;
|
return $domain;
|
||||||
}
|
}
|
||||||
public function updatedApplicationBuildPack()
|
public function updatedApplicationBuildPack()
|
||||||
{
|
{
|
||||||
if ($this->application->build_pack !== 'nixpacks') {
|
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();
|
$this->application->settings->save();
|
||||||
}
|
}
|
||||||
if ($this->application->build_pack === 'dockercompose') {
|
if ($this->application->build_pack === 'dockercompose') {
|
||||||
@@ -194,12 +194,13 @@ class General extends Component
|
|||||||
public function updatedApplicationFqdn()
|
public function updatedApplicationFqdn()
|
||||||
{
|
{
|
||||||
$this->resetDefaultLabels(false);
|
$this->resetDefaultLabels(false);
|
||||||
$this->emit('success', 'Labels reseted to default!');
|
$this->dispatch('success', 'Labels reseted to default!');
|
||||||
}
|
}
|
||||||
public function submit($showToaster = true)
|
public function submit($showToaster = true)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
if ($this->application->build_pack === 'dockercompose' && ($this->initialDockerComposeLocation !== $this->application->docker_compose_location || $this->initialDockerComposePrLocation !== $this->application->docker_compose_pr_location)) {
|
ray($this->initialDockerComposeLocation, $this->application->docker_compose_location);
|
||||||
|
if ($this->application->build_pack === 'dockercompose' && $this->initialDockerComposeLocation !== $this->application->docker_compose_location) {
|
||||||
$this->loadComposeFile();
|
$this->loadComposeFile();
|
||||||
}
|
}
|
||||||
$this->validate();
|
$this->validate();
|
||||||
@@ -218,6 +219,7 @@ class General extends Component
|
|||||||
});
|
});
|
||||||
$this->application->fqdn = $domains->implode(',');
|
$this->application->fqdn = $domains->implode(',');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data_get($this->application, 'dockerfile')) {
|
if (data_get($this->application, 'dockerfile')) {
|
||||||
$port = get_port_from_dockerfile($this->application->dockerfile);
|
$port = get_port_from_dockerfile($this->application->dockerfile);
|
||||||
if ($port && !$this->application->ports_exposes) {
|
if ($port && !$this->application->ports_exposes) {
|
||||||
@@ -230,16 +232,13 @@ class General extends Component
|
|||||||
if ($this->application->publish_directory && $this->application->publish_directory !== '/') {
|
if ($this->application->publish_directory && $this->application->publish_directory !== '/') {
|
||||||
$this->application->publish_directory = rtrim($this->application->publish_directory, '/');
|
$this->application->publish_directory = rtrim($this->application->publish_directory, '/');
|
||||||
}
|
}
|
||||||
if (gettype($this->customLabels) === 'string') {
|
|
||||||
$this->customLabels = str($this->customLabels)->replace(',', "\n");
|
|
||||||
}
|
|
||||||
$this->application->custom_labels = $this->customLabels->explode("\n")->implode(',');
|
|
||||||
if ($this->application->build_pack === 'dockercompose') {
|
if ($this->application->build_pack === 'dockercompose') {
|
||||||
$this->application->docker_compose_domains = json_encode($this->parsedServiceDomains);
|
$this->application->docker_compose_domains = json_encode($this->parsedServiceDomains);
|
||||||
$this->parsedServices = $this->application->parseCompose();
|
$this->parsedServices = $this->application->parseCompose();
|
||||||
}
|
}
|
||||||
|
$this->application->custom_labels = base64_encode($this->customLabels);
|
||||||
$this->application->save();
|
$this->application->save();
|
||||||
$showToaster && $this->emit('success', 'Application settings updated!');
|
$showToaster && $this->dispatch('success', 'Application settings updated!');
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
} finally {
|
} finally {
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Http\Livewire\Project\Application;
|
namespace App\Livewire\Project\Application;
|
||||||
|
|
||||||
use App\Actions\Application\StopApplication;
|
use App\Actions\Application\StopApplication;
|
||||||
|
use App\Events\ApplicationStatusChanged;
|
||||||
use App\Jobs\ContainerStatusJob;
|
use App\Jobs\ContainerStatusJob;
|
||||||
use App\Jobs\ServerStatusJob;
|
use App\Jobs\ServerStatusJob;
|
||||||
use App\Models\Application;
|
use App\Models\Application;
|
||||||
@@ -15,13 +16,19 @@ class Heading extends Component
|
|||||||
public array $parameters;
|
public array $parameters;
|
||||||
|
|
||||||
protected string $deploymentUuid;
|
protected string $deploymentUuid;
|
||||||
|
public function getListeners()
|
||||||
|
{
|
||||||
|
$teamId = auth()->user()->currentTeam()->id;
|
||||||
|
return [
|
||||||
|
"echo-private:team.{$teamId},ApplicationStatusChanged" => 'check_status',
|
||||||
|
];
|
||||||
|
}
|
||||||
public function mount()
|
public function mount()
|
||||||
{
|
{
|
||||||
$this->parameters = get_route_parameters();
|
$this->parameters = get_route_parameters();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function check_status()
|
public function check_status($showNotification = false)
|
||||||
{
|
{
|
||||||
if ($this->application->destination->server->isFunctional()) {
|
if ($this->application->destination->server->isFunctional()) {
|
||||||
dispatch(new ContainerStatusJob($this->application->destination->server));
|
dispatch(new ContainerStatusJob($this->application->destination->server));
|
||||||
@@ -32,6 +39,7 @@ class Heading extends Component
|
|||||||
} else {
|
} else {
|
||||||
dispatch(new ServerStatusJob($this->application->destination->server));
|
dispatch(new ServerStatusJob($this->application->destination->server));
|
||||||
}
|
}
|
||||||
|
if ($showNotification) $this->dispatch('success', 'Application status updated.');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function force_deploy_without_cache()
|
public function force_deploy_without_cache()
|
||||||
@@ -39,10 +47,34 @@ class Heading extends Component
|
|||||||
$this->deploy(force_rebuild: true);
|
$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)
|
public function deploy(bool $force_rebuild = false)
|
||||||
{
|
{
|
||||||
if ($this->application->build_pack === 'dockercompose' && is_null($this->application->docker_compose_raw)) {
|
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;
|
||||||
|
}
|
||||||
|
if ($this->application->destination->server->isSwarm() && is_null($this->application->docker_registry_image_name)) {
|
||||||
|
$this->dispatch('error', 'Please set a Docker image name first.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$this->setDeploymentUuid();
|
$this->setDeploymentUuid();
|
||||||
@@ -51,12 +83,12 @@ class Heading extends Component
|
|||||||
deployment_uuid: $this->deploymentUuid,
|
deployment_uuid: $this->deploymentUuid,
|
||||||
force_rebuild: $force_rebuild,
|
force_rebuild: $force_rebuild,
|
||||||
);
|
);
|
||||||
return redirect()->route('project.application.deployment', [
|
$this->redirectRoute('project.application.deployment', [
|
||||||
'project_uuid' => $this->parameters['project_uuid'],
|
'project_uuid' => $this->parameters['project_uuid'],
|
||||||
'application_uuid' => $this->parameters['application_uuid'],
|
'application_uuid' => $this->parameters['application_uuid'],
|
||||||
'deployment_uuid' => $this->deploymentUuid,
|
'deployment_uuid' => $this->deploymentUuid,
|
||||||
'environment_name' => $this->parameters['environment_name'],
|
'environment_name' => $this->parameters['environment_name'],
|
||||||
]);
|
], navigate: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function setDeploymentUuid()
|
protected function setDeploymentUuid()
|
||||||
@@ -72,6 +104,22 @@ class Heading extends Component
|
|||||||
$this->application->save();
|
$this->application->save();
|
||||||
$this->application->refresh();
|
$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()
|
public function restart()
|
||||||
{
|
{
|
||||||
$this->setDeploymentUuid();
|
$this->setDeploymentUuid();
|
||||||
@@ -80,11 +128,11 @@ class Heading extends Component
|
|||||||
deployment_uuid: $this->deploymentUuid,
|
deployment_uuid: $this->deploymentUuid,
|
||||||
restart_only: true,
|
restart_only: true,
|
||||||
);
|
);
|
||||||
return redirect()->route('project.application.deployment', [
|
return $this->redirectRoute('project.application.deployment', [
|
||||||
'project_uuid' => $this->parameters['project_uuid'],
|
'project_uuid' => $this->parameters['project_uuid'],
|
||||||
'application_uuid' => $this->parameters['application_uuid'],
|
'application_uuid' => $this->parameters['application_uuid'],
|
||||||
'deployment_uuid' => $this->deploymentUuid,
|
'deployment_uuid' => $this->deploymentUuid,
|
||||||
'environment_name' => $this->parameters['environment_name'],
|
'environment_name' => $this->parameters['environment_name'],
|
||||||
]);
|
], navigate: true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Http\Livewire\Project\Application\Preview;
|
namespace App\Livewire\Project\Application\Preview;
|
||||||
|
|
||||||
use App\Models\Application;
|
use App\Models\Application;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
@@ -46,7 +46,7 @@ class Form extends Component
|
|||||||
$this->validate();
|
$this->validate();
|
||||||
$this->application->preview_url_template = str_replace(' ', '', $this->application->preview_url_template);
|
$this->application->preview_url_template = str_replace(' ', '', $this->application->preview_url_template);
|
||||||
$this->application->save();
|
$this->application->save();
|
||||||
$this->emit('success', 'Preview url template updated successfully.');
|
$this->dispatch('success', 'Preview url template updated successfully.');
|
||||||
$this->generate_real_url();
|
$this->generate_real_url();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Http\Livewire\Project\Application;
|
namespace App\Livewire\Project\Application;
|
||||||
|
|
||||||
use App\Models\Application;
|
use App\Models\Application;
|
||||||
use App\Models\ApplicationPreview;
|
use App\Models\ApplicationPreview;
|
||||||
@@ -52,12 +52,12 @@ class Previews extends Component
|
|||||||
force_rebuild: true,
|
force_rebuild: true,
|
||||||
pull_request_id: $pull_request_id,
|
pull_request_id: $pull_request_id,
|
||||||
);
|
);
|
||||||
return redirect()->route('project.application.deployment', [
|
return $this->redirectRoute('project.application.deployment', [
|
||||||
'project_uuid' => $this->parameters['project_uuid'],
|
'project_uuid' => $this->parameters['project_uuid'],
|
||||||
'application_uuid' => $this->parameters['application_uuid'],
|
'application_uuid' => $this->parameters['application_uuid'],
|
||||||
'deployment_uuid' => $this->deployment_uuid,
|
'deployment_uuid' => $this->deployment_uuid,
|
||||||
'environment_name' => $this->parameters['environment_name'],
|
'environment_name' => $this->parameters['environment_name'],
|
||||||
]);
|
], navigate: true);
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
@@ -72,10 +72,14 @@ class Previews extends Component
|
|||||||
public function stop(int $pull_request_id)
|
public function stop(int $pull_request_id)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$containers = getCurrentApplicationContainerStatus($this->application->destination->server, $this->application->id, $pull_request_id);
|
if ($this->application->destination->server->isSwarm()) {
|
||||||
foreach ($containers as $container) {
|
instant_remote_process(["docker stack rm {$this->application->uuid}-{$pull_request_id}"], $this->application->destination->server);
|
||||||
$name = str_replace('/', '', $container['Names']);
|
} else {
|
||||||
instant_remote_process(["docker rm -f $name"], $this->application->destination->server, throwError: false);
|
$containers = getCurrentApplicationContainerStatus($this->application->destination->server, $this->application->id, $pull_request_id);
|
||||||
|
foreach ($containers as $container) {
|
||||||
|
$name = str_replace('/', '', $container['Names']);
|
||||||
|
instant_remote_process(["docker rm -f $name"], $this->application->destination->server, throwError: false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ApplicationPreview::where('application_id', $this->application->id)->where('pull_request_id', $pull_request_id)->first()->delete();
|
ApplicationPreview::where('application_id', $this->application->id)->where('pull_request_id', $pull_request_id)->first()->delete();
|
||||||
$this->application->refresh();
|
$this->application->refresh();
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Http\Livewire\Project\Application;
|
namespace App\Livewire\Project\Application;
|
||||||
|
|
||||||
use App\Models\Application;
|
use App\Models\Application;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
@@ -29,13 +29,12 @@ class Rollback extends Component
|
|||||||
commit: $commit,
|
commit: $commit,
|
||||||
force_rebuild: false,
|
force_rebuild: false,
|
||||||
);
|
);
|
||||||
|
return $this->redirectRoute('project.application.deployment', [
|
||||||
return redirect()->route('project.application.deployment', [
|
|
||||||
'project_uuid' => $this->parameters['project_uuid'],
|
'project_uuid' => $this->parameters['project_uuid'],
|
||||||
'application_uuid' => $this->parameters['application_uuid'],
|
'application_uuid' => $this->parameters['application_uuid'],
|
||||||
'deployment_uuid' => $deployment_uuid,
|
'deployment_uuid' => $deployment_uuid,
|
||||||
'environment_name' => $this->parameters['environment_name'],
|
'environment_name' => $this->parameters['environment_name'],
|
||||||
]);
|
], navigate: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function loadImages($showToast = false)
|
public function loadImages($showToast = false)
|
||||||
@@ -66,7 +65,7 @@ class Rollback extends Component
|
|||||||
];
|
];
|
||||||
})->toArray();
|
})->toArray();
|
||||||
}
|
}
|
||||||
$showToast && $this->emit('success', 'Images loaded.');
|
$showToast && $this->dispatch('success', 'Images loaded.');
|
||||||
return [];
|
return [];
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Http\Livewire\Project\Application;
|
namespace App\Livewire\Project\Application;
|
||||||
|
|
||||||
use App\Models\Application;
|
use App\Models\Application;
|
||||||
use App\Models\PrivateKey;
|
use App\Models\PrivateKey;
|
||||||
@@ -49,6 +49,6 @@ class Source extends Component
|
|||||||
$this->application->git_commit_sha = 'HEAD';
|
$this->application->git_commit_sha = 'HEAD';
|
||||||
}
|
}
|
||||||
$this->application->save();
|
$this->application->save();
|
||||||
$this->emit('success', 'Application source updated!');
|
$this->dispatch('success', 'Application source updated!');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
51
app/Livewire/Project/Application/Swarm.php
Normal file
51
app/Livewire/Project/Application/Swarm.php
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Livewire\Project\Application;
|
||||||
|
|
||||||
|
use App\Models\Application;
|
||||||
|
use Livewire\Component;
|
||||||
|
|
||||||
|
class Swarm extends Component
|
||||||
|
{
|
||||||
|
public Application $application;
|
||||||
|
public string $swarm_placement_constraints = '';
|
||||||
|
|
||||||
|
protected $rules = [
|
||||||
|
'application.swarm_replicas' => 'required',
|
||||||
|
'application.swarm_placement_constraints' => 'nullable',
|
||||||
|
'application.settings.is_swarm_only_worker_nodes' => 'required',
|
||||||
|
];
|
||||||
|
public function mount() {
|
||||||
|
if ($this->application->swarm_placement_constraints) {
|
||||||
|
$this->swarm_placement_constraints = base64_decode($this->application->swarm_placement_constraints);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function instantSave() {
|
||||||
|
try {
|
||||||
|
$this->validate();
|
||||||
|
$this->application->settings->save();
|
||||||
|
$this->dispatch('success', 'Swarm settings updated.');
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
return handleError($e, $this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function submit() {
|
||||||
|
try {
|
||||||
|
$this->validate();
|
||||||
|
if ($this->swarm_placement_constraints) {
|
||||||
|
$this->application->swarm_placement_constraints = base64_encode($this->swarm_placement_constraints);
|
||||||
|
} else {
|
||||||
|
$this->application->swarm_placement_constraints = null;
|
||||||
|
}
|
||||||
|
$this->application->save();
|
||||||
|
|
||||||
|
$this->dispatch('success', 'Swarm settings updated.');
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
return handleError($e, $this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
return view('livewire.project.application.swarm');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Http\Livewire\Project;
|
namespace App\Livewire\Project;
|
||||||
|
|
||||||
use App\Models\Environment;
|
use App\Models\Environment;
|
||||||
use App\Models\Project;
|
use App\Models\Project;
|
||||||
@@ -19,12 +19,14 @@ class CloneProject extends Component
|
|||||||
public $servers;
|
public $servers;
|
||||||
public ?Environment $environment = null;
|
public ?Environment $environment = null;
|
||||||
public ?int $selectedServer = null;
|
public ?int $selectedServer = null;
|
||||||
|
public ?int $selectedDestination = null;
|
||||||
public ?Server $server = null;
|
public ?Server $server = null;
|
||||||
public $resources = [];
|
public $resources = [];
|
||||||
public string $newProjectName = '';
|
public string $newProjectName = '';
|
||||||
|
|
||||||
protected $messages = [
|
protected $messages = [
|
||||||
'selectedServer' => 'Please select a server.',
|
'selectedServer' => 'Please select a server.',
|
||||||
|
'selectedDestination' => 'Please select a server & destination.',
|
||||||
'newProjectName' => 'Please enter a name for the new project.',
|
'newProjectName' => 'Please enter a name for the new project.',
|
||||||
];
|
];
|
||||||
public function mount($project_uuid)
|
public function mount($project_uuid)
|
||||||
@@ -34,7 +36,7 @@ class CloneProject extends Component
|
|||||||
$this->environment = $this->project->environments->where('name', $this->environment_name)->first();
|
$this->environment = $this->project->environments->where('name', $this->environment_name)->first();
|
||||||
$this->project_id = $this->project->id;
|
$this->project_id = $this->project->id;
|
||||||
$this->servers = currentTeam()->servers;
|
$this->servers = currentTeam()->servers;
|
||||||
$this->newProjectName = $this->project->name . ' (clone)';
|
$this->newProjectName = str($this->project->name . '-clone-' . (string)new Cuid2(7))->slug();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function render()
|
public function render()
|
||||||
@@ -42,9 +44,10 @@ class CloneProject extends Component
|
|||||||
return view('livewire.project.clone-project');
|
return view('livewire.project.clone-project');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function selectServer($server_id)
|
public function selectServer($server_id, $destination_id)
|
||||||
{
|
{
|
||||||
$this->selectedServer = $server_id;
|
$this->selectedServer = $server_id;
|
||||||
|
$this->selectedDestination = $destination_id;
|
||||||
$this->server = $this->servers->where('id', $server_id)->first();
|
$this->server = $this->servers->where('id', $server_id)->first();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,7 +55,7 @@ class CloneProject extends Component
|
|||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$this->validate([
|
$this->validate([
|
||||||
'selectedServer' => 'required',
|
'selectedDestination' => 'required',
|
||||||
'newProjectName' => 'required',
|
'newProjectName' => 'required',
|
||||||
]);
|
]);
|
||||||
$foundProject = Project::where('name', $this->newProjectName)->first();
|
$foundProject = Project::where('name', $this->newProjectName)->first();
|
||||||
@@ -81,7 +84,8 @@ class CloneProject extends Component
|
|||||||
'fqdn' => generateFqdn($this->server, $uuid),
|
'fqdn' => generateFqdn($this->server, $uuid),
|
||||||
'status' => 'exited',
|
'status' => 'exited',
|
||||||
'environment_id' => $newEnvironment->id,
|
'environment_id' => $newEnvironment->id,
|
||||||
'destination_id' => $this->selectedServer,
|
// This is not correct, but we need to set it to something
|
||||||
|
'destination_id' => $this->selectedDestination,
|
||||||
]);
|
]);
|
||||||
$newApplication->save();
|
$newApplication->save();
|
||||||
$environmentVaribles = $application->environment_variables()->get();
|
$environmentVaribles = $application->environment_variables()->get();
|
||||||
@@ -107,7 +111,7 @@ class CloneProject extends Component
|
|||||||
'status' => 'exited',
|
'status' => 'exited',
|
||||||
'started_at' => null,
|
'started_at' => null,
|
||||||
'environment_id' => $newEnvironment->id,
|
'environment_id' => $newEnvironment->id,
|
||||||
'destination_id' => $this->selectedServer,
|
'destination_id' => $this->selectedDestination,
|
||||||
]);
|
]);
|
||||||
$newDatabase->save();
|
$newDatabase->save();
|
||||||
$environmentVaribles = $database->environment_variables()->get();
|
$environmentVaribles = $database->environment_variables()->get();
|
||||||
@@ -133,7 +137,7 @@ class CloneProject extends Component
|
|||||||
$newService = $service->replicate()->fill([
|
$newService = $service->replicate()->fill([
|
||||||
'uuid' => $uuid,
|
'uuid' => $uuid,
|
||||||
'environment_id' => $newEnvironment->id,
|
'environment_id' => $newEnvironment->id,
|
||||||
'destination_id' => $this->selectedServer,
|
'destination_id' => $this->selectedDestination,
|
||||||
]);
|
]);
|
||||||
$newService->save();
|
$newService->save();
|
||||||
foreach ($newService->applications() as $application) {
|
foreach ($newService->applications() as $application) {
|
||||||
@@ -148,10 +152,10 @@ class CloneProject extends Component
|
|||||||
}
|
}
|
||||||
$newService->parse();
|
$newService->parse();
|
||||||
}
|
}
|
||||||
return redirect()->route('project.resources', [
|
return $this->redirectRoute('project.resources', [
|
||||||
'project_uuid' => $newProject->uuid,
|
'project_uuid' => $newProject->uuid,
|
||||||
'environment_name' => $newEnvironment->name,
|
'environment_name' => $newEnvironment->name,
|
||||||
]);
|
], navigate: true);
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Http\Livewire\Project\Database;
|
namespace App\Livewire\Project\Database;
|
||||||
|
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
use Spatie\Url\Url;
|
use Spatie\Url\Url;
|
||||||
@@ -50,9 +50,9 @@ class BackupEdit extends Component
|
|||||||
$url = $url->withoutQueryParameter('selectedBackupId');
|
$url = $url->withoutQueryParameter('selectedBackupId');
|
||||||
$url = $url->withFragment('backups');
|
$url = $url->withFragment('backups');
|
||||||
$url = $url->getPath() . "#{$url->getFragment()}";
|
$url = $url->getPath() . "#{$url->getFragment()}";
|
||||||
return redirect()->to($url);
|
return $this->redirect($url,navigate: true);
|
||||||
} else {
|
} 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->custom_validate();
|
||||||
$this->backup->save();
|
$this->backup->save();
|
||||||
$this->backup->refresh();
|
$this->backup->refresh();
|
||||||
$this->emit('success', 'Backup updated successfully');
|
$this->dispatch('success', 'Backup updated successfully');
|
||||||
} catch (\Throwable $e) {
|
} 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->save();
|
||||||
$this->backup->refresh();
|
$this->backup->refresh();
|
||||||
$this->emit('success', 'Backup updated successfully');
|
$this->dispatch('success', 'Backup updated successfully');
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
$this->emit('error', $e->getMessage());
|
$this->dispatch('error', $e->getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Http\Livewire\Project\Database;
|
namespace App\Livewire\Project\Database;
|
||||||
|
|
||||||
use Illuminate\Support\Facades\Storage;
|
use Illuminate\Support\Facades\Storage;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
@@ -8,15 +8,23 @@ use Livewire\Component;
|
|||||||
class BackupExecutions extends Component
|
class BackupExecutions extends Component
|
||||||
{
|
{
|
||||||
public $backup;
|
public $backup;
|
||||||
public $executions;
|
public $executions = [];
|
||||||
public $setDeletableBackup;
|
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)
|
public function deleteBackup($exeuctionId)
|
||||||
{
|
{
|
||||||
$execution = $this->backup->executions()->where('id', $exeuctionId)->first();
|
$execution = $this->backup->executions()->where('id', $exeuctionId)->first();
|
||||||
if (is_null($execution)) {
|
if (is_null($execution)) {
|
||||||
$this->emit('error', 'Backup execution not found.');
|
$this->dispatch('error', 'Backup execution not found.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if ($execution->scheduledDatabaseBackup->database->getMorphClass() === 'App\Models\ServiceDatabase') {
|
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);
|
delete_backup_locally($execution->filename, $execution->scheduledDatabaseBackup->database->destination->server);
|
||||||
}
|
}
|
||||||
$execution->delete();
|
$execution->delete();
|
||||||
$this->emit('success', 'Backup deleted successfully.');
|
$this->dispatch('success', 'Backup deleted successfully.');
|
||||||
$this->emit('refreshBackupExecutions');
|
$this->dispatch('refreshBackupExecutions');
|
||||||
}
|
}
|
||||||
public function download($exeuctionId)
|
public function download($exeuctionId)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$execution = $this->backup->executions()->where('id', $exeuctionId)->first();
|
$execution = $this->backup->executions()->where('id', $exeuctionId)->first();
|
||||||
if (is_null($execution)) {
|
if (is_null($execution)) {
|
||||||
$this->emit('error', 'Backup execution not found.');
|
$this->dispatch('error', 'Backup execution not found.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$filename = data_get($execution, 'filename');
|
$filename = data_get($execution, 'filename');
|
||||||
@@ -57,6 +65,6 @@ class BackupExecutions extends Component
|
|||||||
}
|
}
|
||||||
public function refreshBackupExecutions(): void
|
public function refreshBackupExecutions(): void
|
||||||
{
|
{
|
||||||
$this->executions = $this->backup->executions;
|
$this->executions = data_get($this->backup, 'executions', []);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Http\Livewire\Project\Database;
|
namespace App\Livewire\Project\Database;
|
||||||
|
|
||||||
use App\Jobs\DatabaseBackupJob;
|
use App\Jobs\DatabaseBackupJob;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
@@ -13,6 +13,6 @@ class BackupNow extends Component
|
|||||||
dispatch(new DatabaseBackupJob(
|
dispatch(new DatabaseBackupJob(
|
||||||
backup: $this->backup
|
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.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Http\Livewire\Project\Database;
|
namespace App\Livewire\Project\Database;
|
||||||
|
|
||||||
use App\Models\ScheduledDatabaseBackup;
|
use App\Models\ScheduledDatabaseBackup;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
@@ -35,7 +35,7 @@ class CreateScheduledBackup extends Component
|
|||||||
$this->validate();
|
$this->validate();
|
||||||
$isValid = validate_cron_expression($this->frequency);
|
$isValid = validate_cron_expression($this->frequency);
|
||||||
if (!$isValid) {
|
if (!$isValid) {
|
||||||
$this->emit('error', 'Invalid Cron / Human expression.');
|
$this->dispatch('error', 'Invalid Cron / Human expression.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$payload = [
|
$payload = [
|
||||||
@@ -57,9 +57,9 @@ class CreateScheduledBackup extends Component
|
|||||||
|
|
||||||
$databaseBackup = ScheduledDatabaseBackup::create($payload);
|
$databaseBackup = ScheduledDatabaseBackup::create($payload);
|
||||||
if ($this->database->getMorphClass() === 'App\Models\ServiceDatabase') {
|
if ($this->database->getMorphClass() === 'App\Models\ServiceDatabase') {
|
||||||
$this->emit('refreshScheduledBackups', $databaseBackup->id);
|
$this->dispatch('refreshScheduledBackups', $databaseBackup->id);
|
||||||
} else {
|
} else {
|
||||||
$this->emit('refreshScheduledBackups');
|
$this->dispatch('refreshScheduledBackups');
|
||||||
}
|
}
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
handleError($e, $this);
|
handleError($e, $this);
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Http\Livewire\Project\Database;
|
namespace App\Livewire\Project\Database;
|
||||||
|
|
||||||
use App\Actions\Database\StartMariadb;
|
use App\Actions\Database\StartMariadb;
|
||||||
use App\Actions\Database\StartMongodb;
|
use App\Actions\Database\StartMongodb;
|
||||||
@@ -8,6 +8,7 @@ use App\Actions\Database\StartMysql;
|
|||||||
use App\Actions\Database\StartPostgresql;
|
use App\Actions\Database\StartPostgresql;
|
||||||
use App\Actions\Database\StartRedis;
|
use App\Actions\Database\StartRedis;
|
||||||
use App\Actions\Database\StopDatabase;
|
use App\Actions\Database\StopDatabase;
|
||||||
|
use App\Events\DatabaseStatusChanged;
|
||||||
use App\Jobs\ContainerStatusJob;
|
use App\Jobs\ContainerStatusJob;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
|
||||||
@@ -16,21 +17,28 @@ class Heading extends Component
|
|||||||
public $database;
|
public $database;
|
||||||
public array $parameters;
|
public array $parameters;
|
||||||
|
|
||||||
protected $listeners = ['activityFinished'];
|
public function getListeners()
|
||||||
|
{
|
||||||
|
$userId = auth()->user()->id;
|
||||||
|
return [
|
||||||
|
"echo-private:user.{$userId},DatabaseStatusChanged" => 'activityFinished',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
public function activityFinished()
|
public function activityFinished()
|
||||||
{
|
{
|
||||||
$this->database->update([
|
$this->database->update([
|
||||||
'started_at' => now(),
|
'started_at' => now(),
|
||||||
]);
|
]);
|
||||||
$this->emit('refresh');
|
$this->dispatch('refresh');
|
||||||
$this->check_status();
|
$this->check_status();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function check_status()
|
public function check_status($showNotification = false)
|
||||||
{
|
{
|
||||||
dispatch_sync(new ContainerStatusJob($this->database->destination->server));
|
dispatch_sync(new ContainerStatusJob($this->database->destination->server));
|
||||||
$this->database->refresh();
|
$this->database->refresh();
|
||||||
|
if ($showNotification) $this->dispatch('success', 'Database status updated.');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function mount()
|
public function mount()
|
||||||
@@ -50,19 +58,19 @@ class Heading extends Component
|
|||||||
{
|
{
|
||||||
if ($this->database->type() === 'standalone-postgresql') {
|
if ($this->database->type() === 'standalone-postgresql') {
|
||||||
$activity = StartPostgresql::run($this->database);
|
$activity = StartPostgresql::run($this->database);
|
||||||
$this->emit('newMonitorActivity', $activity->id);
|
$this->dispatch('newMonitorActivity', $activity->id);
|
||||||
} else if ($this->database->type() === 'standalone-redis') {
|
} else if ($this->database->type() === 'standalone-redis') {
|
||||||
$activity = StartRedis::run($this->database);
|
$activity = StartRedis::run($this->database);
|
||||||
$this->emit('newMonitorActivity', $activity->id);
|
$this->dispatch('newMonitorActivity', $activity->id);
|
||||||
} else if ($this->database->type() === 'standalone-mongodb') {
|
} else if ($this->database->type() === 'standalone-mongodb') {
|
||||||
$activity = StartMongodb::run($this->database);
|
$activity = StartMongodb::run($this->database);
|
||||||
$this->emit('newMonitorActivity', $activity->id);
|
$this->dispatch('newMonitorActivity', $activity->id);
|
||||||
} else if ($this->database->type() === 'standalone-mysql') {
|
} else if ($this->database->type() === 'standalone-mysql') {
|
||||||
$activity = StartMysql::run($this->database);
|
$activity = StartMysql::run($this->database);
|
||||||
$this->emit('newMonitorActivity', $activity->id);
|
$this->dispatch('newMonitorActivity', $activity->id);
|
||||||
} else if ($this->database->type() === 'standalone-mariadb') {
|
} else if ($this->database->type() === 'standalone-mariadb') {
|
||||||
$activity = StartMariadb::run($this->database);
|
$activity = StartMariadb::run($this->database);
|
||||||
$this->emit('newMonitorActivity', $activity->id);
|
$this->dispatch('newMonitorActivity', $activity->id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Http\Livewire\Project\Database;
|
namespace App\Livewire\Project\Database;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
|
||||||
class InitScript extends Component
|
class InitScript extends Component
|
||||||
@@ -34,7 +35,7 @@ class InitScript extends Component
|
|||||||
$this->script['index'] = $this->index;
|
$this->script['index'] = $this->index;
|
||||||
$this->script['content'] = $this->content;
|
$this->script['content'] = $this->content;
|
||||||
$this->script['filename'] = $this->filename;
|
$this->script['filename'] = $this->filename;
|
||||||
$this->emitUp('save_init_script', $this->script);
|
$this->dispatch('save_init_script', $this->script);
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
@@ -42,6 +43,6 @@ class InitScript extends Component
|
|||||||
|
|
||||||
public function delete()
|
public function delete()
|
||||||
{
|
{
|
||||||
$this->emitUp('delete_init_script', $this->script);
|
$this->dispatch('delete_init_script', $this->script);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Http\Livewire\Project\Database\Mariadb;
|
namespace App\Livewire\Project\Database\Mariadb;
|
||||||
|
|
||||||
use App\Actions\Database\StartDatabaseProxy;
|
use App\Actions\Database\StartDatabaseProxy;
|
||||||
use App\Actions\Database\StopDatabaseProxy;
|
use App\Actions\Database\StopDatabaseProxy;
|
||||||
@@ -55,12 +55,12 @@ class General extends Component
|
|||||||
try {
|
try {
|
||||||
if (!$this->database->destination->server->isLogDrainEnabled()) {
|
if (!$this->database->destination->server->isLogDrainEnabled()) {
|
||||||
$this->database->is_log_drain_enabled = false;
|
$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;
|
return;
|
||||||
}
|
}
|
||||||
$this->database->save();
|
$this->database->save();
|
||||||
$this->emit('success', 'Database updated successfully.');
|
$this->dispatch('success', 'Database updated successfully.');
|
||||||
$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.');
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
@@ -73,7 +73,7 @@ class General extends Component
|
|||||||
}
|
}
|
||||||
$this->validate();
|
$this->validate();
|
||||||
$this->database->save();
|
$this->database->save();
|
||||||
$this->emit('success', 'Database updated successfully.');
|
$this->dispatch('success', 'Database updated successfully.');
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
@@ -82,23 +82,23 @@ class General extends Component
|
|||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
if ($this->database->is_public && !$this->database->public_port) {
|
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;
|
$this->database->is_public = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if ($this->database->is_public) {
|
if ($this->database->is_public) {
|
||||||
if (!str($this->database->status)->startsWith('running')) {
|
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;
|
$this->database->is_public = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
StartDatabaseProxy::run($this->database);
|
StartDatabaseProxy::run($this->database);
|
||||||
$this->db_url_public = $this->database->getDbUrl();
|
$this->db_url_public = $this->database->getDbUrl();
|
||||||
$this->emit('success', 'Database is now publicly accessible.');
|
$this->dispatch('success', 'Database is now publicly accessible.');
|
||||||
} else {
|
} else {
|
||||||
StopDatabaseProxy::run($this->database);
|
StopDatabaseProxy::run($this->database);
|
||||||
$this->db_url_public = null;
|
$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();
|
$this->database->save();
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Http\Livewire\Project\Database\Mongodb;
|
namespace App\Livewire\Project\Database\Mongodb;
|
||||||
|
|
||||||
use App\Actions\Database\StartDatabaseProxy;
|
use App\Actions\Database\StartDatabaseProxy;
|
||||||
use App\Actions\Database\StopDatabaseProxy;
|
use App\Actions\Database\StopDatabaseProxy;
|
||||||
@@ -54,12 +54,12 @@ class General extends Component
|
|||||||
try {
|
try {
|
||||||
if (!$this->database->destination->server->isLogDrainEnabled()) {
|
if (!$this->database->destination->server->isLogDrainEnabled()) {
|
||||||
$this->database->is_log_drain_enabled = false;
|
$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;
|
return;
|
||||||
}
|
}
|
||||||
$this->database->save();
|
$this->database->save();
|
||||||
$this->emit('success', 'Database updated successfully.');
|
$this->dispatch('success', 'Database updated successfully.');
|
||||||
$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.');
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
@@ -75,7 +75,7 @@ class General extends Component
|
|||||||
}
|
}
|
||||||
$this->validate();
|
$this->validate();
|
||||||
$this->database->save();
|
$this->database->save();
|
||||||
$this->emit('success', 'Database updated successfully.');
|
$this->dispatch('success', 'Database updated successfully.');
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
@@ -84,23 +84,23 @@ class General extends Component
|
|||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
if ($this->database->is_public && !$this->database->public_port) {
|
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;
|
$this->database->is_public = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if ($this->database->is_public) {
|
if ($this->database->is_public) {
|
||||||
if (!str($this->database->status)->startsWith('running')) {
|
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;
|
$this->database->is_public = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
StartDatabaseProxy::run($this->database);
|
StartDatabaseProxy::run($this->database);
|
||||||
$this->db_url_public = $this->database->getDbUrl();
|
$this->db_url_public = $this->database->getDbUrl();
|
||||||
$this->emit('success', 'Database is now publicly accessible.');
|
$this->dispatch('success', 'Database is now publicly accessible.');
|
||||||
} else {
|
} else {
|
||||||
StopDatabaseProxy::run($this->database);
|
StopDatabaseProxy::run($this->database);
|
||||||
$this->db_url_public = null;
|
$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();
|
$this->database->save();
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Http\Livewire\Project\Database\Mysql;
|
namespace App\Livewire\Project\Database\Mysql;
|
||||||
|
|
||||||
use App\Actions\Database\StartDatabaseProxy;
|
use App\Actions\Database\StartDatabaseProxy;
|
||||||
use App\Actions\Database\StopDatabaseProxy;
|
use App\Actions\Database\StopDatabaseProxy;
|
||||||
@@ -56,12 +56,12 @@ class General extends Component
|
|||||||
try {
|
try {
|
||||||
if (!$this->database->destination->server->isLogDrainEnabled()) {
|
if (!$this->database->destination->server->isLogDrainEnabled()) {
|
||||||
$this->database->is_log_drain_enabled = false;
|
$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;
|
return;
|
||||||
}
|
}
|
||||||
$this->database->save();
|
$this->database->save();
|
||||||
$this->emit('success', 'Database updated successfully.');
|
$this->dispatch('success', 'Database updated successfully.');
|
||||||
$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.');
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
@@ -74,7 +74,7 @@ class General extends Component
|
|||||||
}
|
}
|
||||||
$this->validate();
|
$this->validate();
|
||||||
$this->database->save();
|
$this->database->save();
|
||||||
$this->emit('success', 'Database updated successfully.');
|
$this->dispatch('success', 'Database updated successfully.');
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
@@ -83,23 +83,23 @@ class General extends Component
|
|||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
if ($this->database->is_public && !$this->database->public_port) {
|
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;
|
$this->database->is_public = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if ($this->database->is_public) {
|
if ($this->database->is_public) {
|
||||||
if (!str($this->database->status)->startsWith('running')) {
|
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;
|
$this->database->is_public = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
StartDatabaseProxy::run($this->database);
|
StartDatabaseProxy::run($this->database);
|
||||||
$this->db_url_public = $this->database->getDbUrl();
|
$this->db_url_public = $this->database->getDbUrl();
|
||||||
$this->emit('success', 'Database is now publicly accessible.');
|
$this->dispatch('success', 'Database is now publicly accessible.');
|
||||||
} else {
|
} else {
|
||||||
StopDatabaseProxy::run($this->database);
|
StopDatabaseProxy::run($this->database);
|
||||||
$this->db_url_public = null;
|
$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();
|
$this->database->save();
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Http\Livewire\Project\Database\Postgresql;
|
namespace App\Livewire\Project\Database\Postgresql;
|
||||||
|
|
||||||
use App\Actions\Database\StartDatabaseProxy;
|
use App\Actions\Database\StartDatabaseProxy;
|
||||||
use App\Actions\Database\StopDatabaseProxy;
|
use App\Actions\Database\StopDatabaseProxy;
|
||||||
@@ -62,12 +62,12 @@ class General extends Component
|
|||||||
try {
|
try {
|
||||||
if (!$this->database->destination->server->isLogDrainEnabled()) {
|
if (!$this->database->destination->server->isLogDrainEnabled()) {
|
||||||
$this->database->is_log_drain_enabled = false;
|
$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;
|
return;
|
||||||
}
|
}
|
||||||
$this->database->save();
|
$this->database->save();
|
||||||
$this->emit('success', 'Database updated successfully.');
|
$this->dispatch('success', 'Database updated successfully.');
|
||||||
$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.');
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
@@ -76,23 +76,23 @@ class General extends Component
|
|||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
if ($this->database->is_public && !$this->database->public_port) {
|
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;
|
$this->database->is_public = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if ($this->database->is_public) {
|
if ($this->database->is_public) {
|
||||||
if (!str($this->database->status)->startsWith('running')) {
|
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;
|
$this->database->is_public = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
StartDatabaseProxy::run($this->database);
|
StartDatabaseProxy::run($this->database);
|
||||||
$this->db_url_public = $this->database->getDbUrl();
|
$this->db_url_public = $this->database->getDbUrl();
|
||||||
$this->emit('success', 'Database is now publicly accessible.');
|
$this->dispatch('success', 'Database is now publicly accessible.');
|
||||||
} else {
|
} else {
|
||||||
StopDatabaseProxy::run($this->database);
|
StopDatabaseProxy::run($this->database);
|
||||||
$this->db_url_public = null;
|
$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();
|
$this->database->save();
|
||||||
} catch (\Throwable $e) {
|
} 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 = filter($this->database->init_scripts, fn ($s) => $s['filename'] !== $script['filename']);
|
||||||
$this->database->init_scripts = array_merge($this->database->init_scripts, [$script]);
|
$this->database->init_scripts = array_merge($this->database->init_scripts, [$script]);
|
||||||
$this->database->save();
|
$this->database->save();
|
||||||
$this->emit('success', 'Init script saved successfully.');
|
$this->dispatch('success', 'Init script saved successfully.');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function delete_init_script($script)
|
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->init_scripts = $collection->filter(fn ($s) => $s['filename'] !== $script['filename'])->toArray();
|
||||||
$this->database->save();
|
$this->database->save();
|
||||||
$this->refresh();
|
$this->refresh();
|
||||||
$this->emit('success', 'Init script deleted successfully.');
|
$this->dispatch('success', 'Init script deleted successfully.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -134,7 +134,7 @@ class General extends Component
|
|||||||
]);
|
]);
|
||||||
$found = collect($this->database->init_scripts)->firstWhere('filename', $this->new_filename);
|
$found = collect($this->database->init_scripts)->firstWhere('filename', $this->new_filename);
|
||||||
if ($found) {
|
if ($found) {
|
||||||
$this->emit('error', 'Filename already exists.');
|
$this->dispatch('error', 'Filename already exists.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!isset($this->database->init_scripts)) {
|
if (!isset($this->database->init_scripts)) {
|
||||||
@@ -148,7 +148,7 @@ class General extends Component
|
|||||||
]
|
]
|
||||||
]);
|
]);
|
||||||
$this->database->save();
|
$this->database->save();
|
||||||
$this->emit('success', 'Init script added successfully.');
|
$this->dispatch('success', 'Init script added successfully.');
|
||||||
$this->new_content = '';
|
$this->new_content = '';
|
||||||
$this->new_filename = '';
|
$this->new_filename = '';
|
||||||
}
|
}
|
||||||
@@ -161,7 +161,7 @@ class General extends Component
|
|||||||
}
|
}
|
||||||
$this->validate();
|
$this->validate();
|
||||||
$this->database->save();
|
$this->database->save();
|
||||||
$this->emit('success', 'Database updated successfully.');
|
$this->dispatch('success', 'Database updated successfully.');
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Http\Livewire\Project\Database\Redis;
|
namespace App\Livewire\Project\Database\Redis;
|
||||||
|
|
||||||
use App\Actions\Database\StartDatabaseProxy;
|
use App\Actions\Database\StartDatabaseProxy;
|
||||||
use App\Actions\Database\StopDatabaseProxy;
|
use App\Actions\Database\StopDatabaseProxy;
|
||||||
@@ -48,12 +48,12 @@ class General extends Component
|
|||||||
try {
|
try {
|
||||||
if (!$this->database->destination->server->isLogDrainEnabled()) {
|
if (!$this->database->destination->server->isLogDrainEnabled()) {
|
||||||
$this->database->is_log_drain_enabled = false;
|
$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;
|
return;
|
||||||
}
|
}
|
||||||
$this->database->save();
|
$this->database->save();
|
||||||
$this->emit('success', 'Database updated successfully.');
|
$this->dispatch('success', 'Database updated successfully.');
|
||||||
$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.');
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
@@ -66,7 +66,7 @@ class General extends Component
|
|||||||
$this->database->redis_conf = null;
|
$this->database->redis_conf = null;
|
||||||
}
|
}
|
||||||
$this->database->save();
|
$this->database->save();
|
||||||
$this->emit('success', 'Database updated successfully.');
|
$this->dispatch('success', 'Database updated successfully.');
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
@@ -75,23 +75,23 @@ class General extends Component
|
|||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
if ($this->database->is_public && !$this->database->public_port) {
|
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;
|
$this->database->is_public = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if ($this->database->is_public) {
|
if ($this->database->is_public) {
|
||||||
if (!str($this->database->status)->startsWith('running')) {
|
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;
|
$this->database->is_public = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
StartDatabaseProxy::run($this->database);
|
StartDatabaseProxy::run($this->database);
|
||||||
$this->db_url_public = $this->database->getDbUrl();
|
$this->db_url_public = $this->database->getDbUrl();
|
||||||
$this->emit('success', 'Database is now publicly accessible.');
|
$this->dispatch('success', 'Database is now publicly accessible.');
|
||||||
} else {
|
} else {
|
||||||
StopDatabaseProxy::run($this->database);
|
StopDatabaseProxy::run($this->database);
|
||||||
$this->db_url_public = null;
|
$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();
|
$this->database->save();
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Http\Livewire\Project\Database;
|
namespace App\Livewire\Project\Database;
|
||||||
|
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
|
||||||
@@ -38,7 +38,7 @@ class ScheduledBackups extends Component
|
|||||||
public function delete($scheduled_backup_id): void
|
public function delete($scheduled_backup_id): void
|
||||||
{
|
{
|
||||||
$this->database->scheduledBackups->find($scheduled_backup_id)->delete();
|
$this->database->scheduledBackups->find($scheduled_backup_id)->delete();
|
||||||
$this->emit('success', 'Scheduled backup deleted successfully.');
|
$this->dispatch('success', 'Scheduled backup deleted successfully.');
|
||||||
$this->refreshScheduledBackups();
|
$this->refreshScheduledBackups();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Http\Livewire\Project;
|
namespace App\Livewire\Project;
|
||||||
|
|
||||||
use App\Models\Environment;
|
use App\Models\Environment;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
@@ -23,8 +23,8 @@ class DeleteEnvironment extends Component
|
|||||||
$environment = Environment::findOrFail($this->environment_id);
|
$environment = Environment::findOrFail($this->environment_id);
|
||||||
if ($environment->isEmpty()) {
|
if ($environment->isEmpty()) {
|
||||||
$environment->delete();
|
$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.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Http\Livewire\Project;
|
namespace App\Livewire\Project;
|
||||||
|
|
||||||
use App\Models\Project;
|
use App\Models\Project;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
@@ -22,9 +22,9 @@ class DeleteProject extends Component
|
|||||||
]);
|
]);
|
||||||
$project = Project::findOrFail($this->project_id);
|
$project = Project::findOrFail($this->project_id);
|
||||||
if ($project->applications->count() > 0) {
|
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();
|
$project->delete();
|
||||||
return redirect()->route('projects');
|
return $this->redirectRoute('projects', navigate: true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Http\Livewire\Project;
|
namespace App\Livewire\Project;
|
||||||
|
|
||||||
use App\Models\Project;
|
use App\Models\Project;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
@@ -18,7 +18,7 @@ class Edit extends Component
|
|||||||
$this->validate();
|
$this->validate();
|
||||||
try {
|
try {
|
||||||
$this->project->save();
|
$this->project->save();
|
||||||
$this->emit('saved');
|
$this->dispatch('saved');
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
82
app/Livewire/Project/New/DockerCompose.php
Normal file
82
app/Livewire/Project/New/DockerCompose.php
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user