mirror of
https://github.com/ershisan99/coolify.git
synced 2026-01-03 12:34:08 +00:00
Compare commits
67 Commits
v4.0.0-bet
...
v4.0.0-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1c90f46f2a | ||
|
|
e78d851c85 | ||
|
|
52d05005ed | ||
|
|
f03aa57758 | ||
|
|
8c20c833ba | ||
|
|
2fe6766b7f | ||
|
|
3f453ba7c0 | ||
|
|
bd02c3055a | ||
|
|
7ea7d85d15 | ||
|
|
d38350c282 | ||
|
|
a83c70004c | ||
|
|
49e1404a2c | ||
|
|
76f23e7dbf | ||
|
|
ad8653f54d | ||
|
|
8939d77051 | ||
|
|
37be4a1796 | ||
|
|
e4c923e358 | ||
|
|
62ca3ffaa5 | ||
|
|
9af3ce4be5 | ||
|
|
fe143ef8a5 | ||
|
|
5fb5845e90 | ||
|
|
794cfbd8eb | ||
|
|
29ee9915f3 | ||
|
|
331d485213 | ||
|
|
665e3761c4 | ||
|
|
ac19f0e34f | ||
|
|
d7cfa0578f | ||
|
|
694169bb84 | ||
|
|
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 |
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 }}
|
||||||
|
|||||||
61
README.md
61
README.md
@@ -17,6 +17,39 @@ https://coolify.io/sponsorships
|
|||||||
|
|
||||||
Thank you so much!
|
Thank you so much!
|
||||||
|
|
||||||
|
Special thanks to our biggest sponsors, [CCCareers](https://cccareers.org/) and [Appwrite](https://appwrite.io)!
|
||||||
|
|
||||||
|
<a href="https://cccareers.org/" target="_blank"><img src="./other/logos/ccc-logo.webp" alt="appwrite logo" width="200"/></a>
|
||||||
|
<a href="https://appwrite.io" target="_blank"><img src="./other/logos/appwrite.svg" alt="appwrite logo" width="200"/></a>
|
||||||
|
|
||||||
|
## Github Sponsors ($15+)
|
||||||
|
<a href="https://github.com/corentinclichy"><img src="https://github.com/corentinclichy.png" width="60px" alt="Corentin Clichy" /></a>
|
||||||
|
<a href="https://github.com/Niki2k1"><img src="https://github.com/Niki2k1.png" width="60px" alt="Niklas Lausch" /></a>
|
||||||
|
<a href="https://github.com/pixelinfinito"><img src="https://github.com/pixelinfinito.png" width="60px" alt="Pixel Infinito" /></a>
|
||||||
|
<a href="https://github.com/whitesidest"><img src="https://avatars.githubusercontent.com/u/12365916?s=52&v=4" width="60px" alt="Tyler Whitesides" /></a>
|
||||||
|
<a href="https://github.com/aniftyco"><img src="https://github.com/aniftyco.png" width="60px" alt="NiftyCo" /></a>
|
||||||
|
<a href="https://github.com/iujlaki"><img src="https://github.com/iujlaki.png" width="60px" alt="Imre Ujlaki" /></a>
|
||||||
|
<a href="https://github.com/Illyism"><img src="https://github.com/Illyism.png" width="60px" alt="Ilias Ism" /></a>
|
||||||
|
<a href="https://github.com/urtho"><img src="https://github.com/urtho.png" width="60px" alt="Paweł Pierścionek" /></a>
|
||||||
|
<a href="https://github.com/monocursive"><img src="https://github.com/monocursive.png" width="60px" alt="Michael Mazurczak" /></a>
|
||||||
|
<a href="https://github.com/cccareers"><img src="https://github.com/cccareers.png" width="60px" alt="Creating Coding Careers" /></a>
|
||||||
|
|
||||||
|
## Organizations
|
||||||
|
<a href="https://opencollective.com/coollabsio/organization/0/website"><img src="https://opencollective.com/coollabsio/organization/0/avatar.svg"></a>
|
||||||
|
<a href="https://opencollective.com/coollabsio/organization/1/website"><img src="https://opencollective.com/coollabsio/organization/1/avatar.svg"></a>
|
||||||
|
<a href="https://opencollective.com/coollabsio/organization/2/website"><img src="https://opencollective.com/coollabsio/organization/2/avatar.svg"></a>
|
||||||
|
<a href="https://opencollective.com/coollabsio/organization/3/website"><img src="https://opencollective.com/coollabsio/organization/3/avatar.svg"></a>
|
||||||
|
<a href="https://opencollective.com/coollabsio/organization/4/website"><img src="https://opencollective.com/coollabsio/organization/4/avatar.svg"></a>
|
||||||
|
<a href="https://opencollective.com/coollabsio/organization/5/website"><img src="https://opencollective.com/coollabsio/organization/5/avatar.svg"></a>
|
||||||
|
<a href="https://opencollective.com/coollabsio/organization/6/website"><img src="https://opencollective.com/coollabsio/organization/6/avatar.svg"></a>
|
||||||
|
<a href="https://opencollective.com/coollabsio/organization/7/website"><img src="https://opencollective.com/coollabsio/organization/7/avatar.svg"></a>
|
||||||
|
<a href="https://opencollective.com/coollabsio/organization/8/website"><img src="https://opencollective.com/coollabsio/organization/8/avatar.svg"></a>
|
||||||
|
<a href="https://opencollective.com/coollabsio/organization/9/website"><img src="https://opencollective.com/coollabsio/organization/9/avatar.svg"></a>
|
||||||
|
|
||||||
|
## Individuals
|
||||||
|
|
||||||
|
<a href="https://opencollective.com/coollabsio"><img src="https://opencollective.com/coollabsio/individuals.svg?width=890"></a>
|
||||||
|
|
||||||
# Cloud
|
# Cloud
|
||||||
|
|
||||||
If you do not want to self-host Coolify, there is a paid cloud version available: https://app.coolify.io
|
If you do not want to self-host Coolify, there is a paid cloud version available: https://app.coolify.io
|
||||||
@@ -59,34 +92,6 @@ Contact us [here](https://coolify.io/docs/contact).
|
|||||||
|
|
||||||
<a href="https://trendshift.io/repositories/634" target="_blank"><img src="https://trendshift.io/api/badge/repositories/634" alt="coollabsio%2Fcoolify | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
|
<a href="https://trendshift.io/repositories/634" target="_blank"><img src="https://trendshift.io/api/badge/repositories/634" alt="coollabsio%2Fcoolify | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
|
||||||
|
|
||||||
# 💰 Financial Contributors
|
|
||||||
|
|
||||||
Become a financial contributor and help us sustain our community. [[Contribute](https://opencollective.com/coollabsio/contribute)]
|
|
||||||
|
|
||||||
## Organizations
|
|
||||||
|
|
||||||
Special thanks to our biggest sponsors, [CCCareers](https://cccareers.org/) and [Appwrite](https://appwrite.io)!
|
|
||||||
|
|
||||||
<a href="https://cccareers.org/" target="_blank"><img src="./other/logos/ccc-logo.webp" alt="appwrite logo" width="200"/></a>
|
|
||||||
<a href="https://appwrite.io" target="_blank"><img src="./other/logos/appwrite.svg" alt="appwrite logo" width="200"/></a>
|
|
||||||
|
|
||||||
Support this project with your organization. Your logo will show up here with a link to your website.
|
|
||||||
|
|
||||||
<a href="https://opencollective.com/coollabsio/organization/0/website"><img src="https://opencollective.com/coollabsio/organization/0/avatar.svg"></a>
|
|
||||||
<a href="https://opencollective.com/coollabsio/organization/1/website"><img src="https://opencollective.com/coollabsio/organization/1/avatar.svg"></a>
|
|
||||||
<a href="https://opencollective.com/coollabsio/organization/2/website"><img src="https://opencollective.com/coollabsio/organization/2/avatar.svg"></a>
|
|
||||||
<a href="https://opencollective.com/coollabsio/organization/3/website"><img src="https://opencollective.com/coollabsio/organization/3/avatar.svg"></a>
|
|
||||||
<a href="https://opencollective.com/coollabsio/organization/4/website"><img src="https://opencollective.com/coollabsio/organization/4/avatar.svg"></a>
|
|
||||||
<a href="https://opencollective.com/coollabsio/organization/5/website"><img src="https://opencollective.com/coollabsio/organization/5/avatar.svg"></a>
|
|
||||||
<a href="https://opencollective.com/coollabsio/organization/6/website"><img src="https://opencollective.com/coollabsio/organization/6/avatar.svg"></a>
|
|
||||||
<a href="https://opencollective.com/coollabsio/organization/7/website"><img src="https://opencollective.com/coollabsio/organization/7/avatar.svg"></a>
|
|
||||||
<a href="https://opencollective.com/coollabsio/organization/8/website"><img src="https://opencollective.com/coollabsio/organization/8/avatar.svg"></a>
|
|
||||||
<a href="https://opencollective.com/coollabsio/organization/9/website"><img src="https://opencollective.com/coollabsio/organization/9/avatar.svg"></a>
|
|
||||||
|
|
||||||
## Individuals
|
|
||||||
|
|
||||||
<a href="https://opencollective.com/coollabsio"><img src="https://opencollective.com/coollabsio/individuals.svg?width=890"></a>
|
|
||||||
|
|
||||||
# Star History
|
# Star History
|
||||||
|
|
||||||
[](https://star-history.com/#coollabsio/coolify&Date)
|
[](https://star-history.com/#coollabsio/coolify&Date)
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ class StartMariadb
|
|||||||
'memswap_limit' => $this->database->limits_memory_swap,
|
'memswap_limit' => $this->database->limits_memory_swap,
|
||||||
'mem_swappiness' => $this->database->limits_memory_swappiness,
|
'mem_swappiness' => $this->database->limits_memory_swappiness,
|
||||||
'mem_reservation' => $this->database->limits_memory_reservation,
|
'mem_reservation' => $this->database->limits_memory_reservation,
|
||||||
'cpus' => (int) $this->database->limits_cpus,
|
'cpus' => (float) $this->database->limits_cpus,
|
||||||
'cpuset' => $this->database->limits_cpuset,
|
'cpuset' => $this->database->limits_cpuset,
|
||||||
'cpu_shares' => $this->database->limits_cpu_shares,
|
'cpu_shares' => $this->database->limits_cpu_shares,
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ class StartMongodb
|
|||||||
'memswap_limit' => $this->database->limits_memory_swap,
|
'memswap_limit' => $this->database->limits_memory_swap,
|
||||||
'mem_swappiness' => $this->database->limits_memory_swappiness,
|
'mem_swappiness' => $this->database->limits_memory_swappiness,
|
||||||
'mem_reservation' => $this->database->limits_memory_reservation,
|
'mem_reservation' => $this->database->limits_memory_reservation,
|
||||||
'cpus' => (int) $this->database->limits_cpus,
|
'cpus' => (float) $this->database->limits_cpus,
|
||||||
'cpuset' => $this->database->limits_cpuset,
|
'cpuset' => $this->database->limits_cpuset,
|
||||||
'cpu_shares' => $this->database->limits_cpu_shares,
|
'cpu_shares' => $this->database->limits_cpu_shares,
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ class StartMysql
|
|||||||
'memswap_limit' => $this->database->limits_memory_swap,
|
'memswap_limit' => $this->database->limits_memory_swap,
|
||||||
'mem_swappiness' => $this->database->limits_memory_swappiness,
|
'mem_swappiness' => $this->database->limits_memory_swappiness,
|
||||||
'mem_reservation' => $this->database->limits_memory_reservation,
|
'mem_reservation' => $this->database->limits_memory_reservation,
|
||||||
'cpus' => (int) $this->database->limits_cpus,
|
'cpus' => (float) $this->database->limits_cpus,
|
||||||
'cpuset' => $this->database->limits_cpuset,
|
'cpuset' => $this->database->limits_cpuset,
|
||||||
'cpu_shares' => $this->database->limits_cpu_shares,
|
'cpu_shares' => $this->database->limits_cpu_shares,
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ class StartPostgresql
|
|||||||
'memswap_limit' => $this->database->limits_memory_swap,
|
'memswap_limit' => $this->database->limits_memory_swap,
|
||||||
'mem_swappiness' => $this->database->limits_memory_swappiness,
|
'mem_swappiness' => $this->database->limits_memory_swappiness,
|
||||||
'mem_reservation' => $this->database->limits_memory_reservation,
|
'mem_reservation' => $this->database->limits_memory_reservation,
|
||||||
'cpus' => (int) $this->database->limits_cpus,
|
'cpus' => (float) $this->database->limits_cpus,
|
||||||
'cpuset' => $this->database->limits_cpuset,
|
'cpuset' => $this->database->limits_cpuset,
|
||||||
'cpu_shares' => $this->database->limits_cpu_shares,
|
'cpu_shares' => $this->database->limits_cpu_shares,
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ class StartRedis
|
|||||||
'memswap_limit' => $this->database->limits_memory_swap,
|
'memswap_limit' => $this->database->limits_memory_swap,
|
||||||
'mem_swappiness' => $this->database->limits_memory_swappiness,
|
'mem_swappiness' => $this->database->limits_memory_swappiness,
|
||||||
'mem_reservation' => $this->database->limits_memory_reservation,
|
'mem_reservation' => $this->database->limits_memory_reservation,
|
||||||
'cpus' => (int) $this->database->limits_cpus,
|
'cpus' => (float) $this->database->limits_cpus,
|
||||||
'cpuset' => $this->database->limits_cpuset,
|
'cpuset' => $this->database->limits_cpuset,
|
||||||
'cpu_shares' => $this->database->limits_cpu_shares,
|
'cpu_shares' => $this->database->limits_cpu_shares,
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -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') {
|
||||||
|
|||||||
@@ -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)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ class Handler extends ExceptionHandler
|
|||||||
if ($request->is('api/*') || $request->expectsJson() || $this->shouldReturnJson($request, $exception)) {
|
if ($request->is('api/*') || $request->expectsJson() || $this->shouldReturnJson($request, $exception)) {
|
||||||
return response()->json(['message' => $exception->getMessage()], 401);
|
return response()->json(['message' => $exception->getMessage()], 401);
|
||||||
}
|
}
|
||||||
return redirect()->guest($exception->redirectTo() ?? route('login'));
|
return redirect()->guest($exception->redirectTo() ?? route('login'));
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Register the exception handling callbacks for the application.
|
* Register the exception handling callbacks for the application.
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Http\Middleware;
|
namespace App\Http\Middleware;
|
||||||
|
|
||||||
|
use App\Providers\RouteServiceProvider;
|
||||||
use Closure;
|
use Closure;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
@@ -16,7 +17,7 @@ class DecideWhatToDoWithUser
|
|||||||
}
|
}
|
||||||
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()->route('boarding');
|
||||||
}
|
}
|
||||||
return $next($request);
|
return $next($request);
|
||||||
}
|
}
|
||||||
@@ -24,27 +25,27 @@ class DecideWhatToDoWithUser
|
|||||||
if ($request->path() === 'verify' || in_array($request->path(), allowedPathsForInvalidAccounts()) || $request->routeIs('verify.verify')) {
|
if ($request->path() === 'verify' || in_array($request->path(), allowedPathsForInvalidAccounts()) || $request->routeIs('verify.verify')) {
|
||||||
return $next($request);
|
return $next($request);
|
||||||
}
|
}
|
||||||
return redirect('/verify');
|
return redirect()-route('verify.email');
|
||||||
}
|
}
|
||||||
if (!isSubscriptionActive() && !isSubscriptionOnGracePeriod()) {
|
if (!isSubscriptionActive() && !isSubscriptionOnGracePeriod()) {
|
||||||
if (!in_array($request->path(), allowedPathsForUnsubscribedAccounts())) {
|
if (!in_array($request->path(), allowedPathsForUnsubscribedAccounts())) {
|
||||||
if (Str::startsWith($request->path(), 'invitations')) {
|
if (Str::startsWith($request->path(), 'invitations')) {
|
||||||
return $next($request);
|
return $next($request);
|
||||||
}
|
}
|
||||||
return redirect('subscription');
|
return redirect()->route('subscription');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (showBoarding() && !in_array($request->path(), allowedPathsForBoardingAccounts())) {
|
if (showBoarding() && !in_array($request->path(), allowedPathsForBoardingAccounts())) {
|
||||||
if (Str::startsWith($request->path(), 'invitations')) {
|
if (Str::startsWith($request->path(), 'invitations')) {
|
||||||
return $next($request);
|
return $next($request);
|
||||||
}
|
}
|
||||||
return redirect('boarding');
|
return redirect()->route('boarding');
|
||||||
}
|
}
|
||||||
if (auth()->user()->hasVerifiedEmail() && $request->path() === 'verify') {
|
if (auth()->user()->hasVerifiedEmail() && $request->path() === 'verify') {
|
||||||
return redirect('/');
|
return redirect(RouteServiceProvider::HOME);
|
||||||
}
|
}
|
||||||
if (isSubscriptionActive() && $request->path() === 'subscription') {
|
if (isSubscriptionActive() && $request->path() === 'subscription') {
|
||||||
return redirect('/');
|
return redirect(RouteServiceProvider::HOME);
|
||||||
}
|
}
|
||||||
return $next($request);
|
return $next($request);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -75,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;
|
||||||
@@ -215,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);
|
||||||
@@ -299,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.'"],
|
||||||
);
|
);
|
||||||
@@ -432,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 {
|
||||||
@@ -454,7 +454,18 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
]);
|
]);
|
||||||
$this->save_environment_variables();
|
$this->save_environment_variables();
|
||||||
// Build new container to limit downtime.
|
// Build new container to limit downtime.
|
||||||
$this->build_by_compose_file();
|
$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);
|
||||||
|
|
||||||
$networkId = $this->application->uuid;
|
$networkId = $this->application->uuid;
|
||||||
@@ -488,7 +499,17 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
$this->start_by_compose_file();
|
// 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()
|
||||||
{
|
{
|
||||||
@@ -575,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(
|
||||||
@@ -674,10 +704,20 @@ 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();
|
||||||
$this->execute_remote_command(
|
if ($this->application->destination->server->isSwarm()) {
|
||||||
["echo -n 'Starting preview deployment.'"],
|
ray("{$this->workdir}{$this->docker_compose_location}");
|
||||||
[executeInDocker($this->deployment_uuid, "docker compose --project-directory {$this->workdir} up -d"), "hidden" => true],
|
$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()
|
private function create_workdir()
|
||||||
{
|
{
|
||||||
@@ -914,7 +954,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
'memswap_limit' => $this->application->limits_memory_swap,
|
'memswap_limit' => $this->application->limits_memory_swap,
|
||||||
'mem_swappiness' => $this->application->limits_memory_swappiness,
|
'mem_swappiness' => $this->application->limits_memory_swappiness,
|
||||||
'mem_reservation' => $this->application->limits_memory_reservation,
|
'mem_reservation' => $this->application->limits_memory_reservation,
|
||||||
'cpus' => (int) $this->application->limits_cpus,
|
'cpus' => (float) $this->application->limits_cpus,
|
||||||
'cpuset' => $this->application->limits_cpuset,
|
'cpuset' => $this->application->limits_cpuset,
|
||||||
'cpu_shares' => $this->application->limits_cpu_shares,
|
'cpu_shares' => $this->application->limits_cpu_shares,
|
||||||
]
|
]
|
||||||
@@ -941,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'
|
||||||
],
|
],
|
||||||
@@ -966,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;
|
||||||
}
|
}
|
||||||
@@ -1084,6 +1129,10 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
// Add PORT if not exists, use the first port as default
|
// Add PORT if not exists, use the first port as default
|
||||||
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('PORT'))->isEmpty()) {
|
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('PORT'))->isEmpty()) {
|
||||||
$environment_variables->push("PORT={$ports[0]}");
|
$environment_variables->push("PORT={$ports[0]}");
|
||||||
|
} if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('SOURCE_COMMIT'))->isEmpty()) {
|
||||||
|
if (!is_null($this->commit)) {
|
||||||
|
$environment_variables->push("SOURCE_COMMIT={$this->commit}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return $environment_variables->all();
|
return $environment_variables->all();
|
||||||
}
|
}
|
||||||
@@ -1258,15 +1307,9 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
|
|||||||
[executeInDocker($this->deployment_uuid, "docker compose --project-directory {$this->workdir} build"), "hidden" => true],
|
[executeInDocker($this->deployment_uuid, "docker compose --project-directory {$this->workdir} build"), "hidden" => true],
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
if ($this->docker_compose_location) {
|
$this->execute_remote_command(
|
||||||
$this->execute_remote_command(
|
[executeInDocker($this->deployment_uuid, "docker compose --project-directory {$this->workdir} -f {$this->workdir}{$this->docker_compose_location} build"), "hidden" => true],
|
||||||
[executeInDocker($this->deployment_uuid, "docker compose --project-directory {$this->workdir} -f {$this->workdir}{$this->docker_compose_location} build"), "hidden" => true],
|
);
|
||||||
);
|
|
||||||
} else {
|
|
||||||
$this->execute_remote_command(
|
|
||||||
[executeInDocker($this->deployment_uuid, "docker compose --project-directory {$this->workdir} build"), "hidden" => true],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
$this->application_deployment_queue->addLogEntry("New images built.");
|
$this->application_deployment_queue->addLogEntry("New images built.");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,20 +17,24 @@ use Illuminate\Queue\InteractsWithQueue;
|
|||||||
use Illuminate\Queue\Middleware\WithoutOverlapping;
|
use Illuminate\Queue\Middleware\WithoutOverlapping;
|
||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
||||||
use Illuminate\Support\Arr;
|
use Illuminate\Support\Arr;
|
||||||
use Illuminate\Support\Sleep;
|
|
||||||
|
|
||||||
class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
|
class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
|
||||||
{
|
{
|
||||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||||
|
|
||||||
|
public $tries = 4;
|
||||||
|
public function backoff(): int
|
||||||
|
{
|
||||||
|
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)
|
public function __construct(public Server $server)
|
||||||
@@ -40,13 +44,13 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
|
|
||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
|
if (!$this->server->isServerReady($this->tries)) {
|
||||||
|
return '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, false);
|
$containers = instant_remote_process(["docker container ls -q"], $this->server, false);
|
||||||
@@ -54,15 +58,15 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
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) {
|
||||||
|
|||||||
@@ -17,22 +17,26 @@ 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}");
|
|
||||||
try {
|
try {
|
||||||
if ($this->server->isFunctional()) {
|
if ($this->server->isFunctional()) {
|
||||||
$this->cleanup(notify: false);
|
$this->cleanup(notify: false);
|
||||||
@@ -40,7 +44,7 @@ class ServerStatusJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
} 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
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ class CheckLicense extends Component
|
|||||||
} 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 $this->redirect('/settings/license', navigate: true);
|
return redirect()->route('settings.license');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ class Form extends Component
|
|||||||
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 $this->redirectRoute('dashboard', navigate: true);
|
return redirect()->route('dashboard');
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,29 +4,32 @@ namespace App\Livewire\Destination\New;
|
|||||||
|
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use App\Models\StandaloneDocker as ModelsStandaloneDocker;
|
use App\Models\StandaloneDocker as ModelsStandaloneDocker;
|
||||||
|
use App\Models\SwarmDocker;
|
||||||
use Illuminate\Database\Eloquent\Collection;
|
use Illuminate\Database\Eloquent\Collection;
|
||||||
use Illuminate\Support\Str;
|
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
use Visus\Cuid2\Cuid2;
|
use Visus\Cuid2\Cuid2;
|
||||||
|
|
||||||
class StandaloneDocker extends Component
|
class Docker extends Component
|
||||||
{
|
{
|
||||||
public string $name;
|
public string $name;
|
||||||
public string $network;
|
public string $network;
|
||||||
|
|
||||||
public Collection $servers;
|
public Collection $servers;
|
||||||
public Server $server;
|
public Server $server;
|
||||||
public int|null $server_id = null;
|
public ?int $server_id = null;
|
||||||
|
public bool $is_swarm = false;
|
||||||
|
|
||||||
protected $rules = [
|
protected $rules = [
|
||||||
'name' => 'required|string',
|
'name' => 'required|string',
|
||||||
'network' => 'required|string',
|
'network' => 'required|string',
|
||||||
'server_id' => 'required|integer'
|
'server_id' => 'required|integer',
|
||||||
|
'is_swarm' => 'boolean'
|
||||||
];
|
];
|
||||||
protected $validationAttributes = [
|
protected $validationAttributes = [
|
||||||
'name' => 'name',
|
'name' => 'name',
|
||||||
'network' => 'network',
|
'network' => 'network',
|
||||||
'server_id' => 'server'
|
'server_id' => 'server',
|
||||||
|
'is_swarm' => 'swarm'
|
||||||
];
|
];
|
||||||
|
|
||||||
public function mount()
|
public function mount()
|
||||||
@@ -43,13 +46,13 @@ class StandaloneDocker extends Component
|
|||||||
} else {
|
} else {
|
||||||
$this->network = new Cuid2(7);
|
$this->network = new Cuid2(7);
|
||||||
}
|
}
|
||||||
$this->name = Str::kebab("{$this->servers->first()->name}-{$this->network}");
|
$this->name = str("{$this->servers->first()->name}-{$this->network}")->kebab();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function generate_name()
|
public function generate_name()
|
||||||
{
|
{
|
||||||
$this->server = Server::find($this->server_id);
|
$this->server = Server::find($this->server_id);
|
||||||
$this->name = Str::kebab("{$this->server->name}-{$this->network}");
|
$this->name = str("{$this->server->name}-{$this->network}")->kebab();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function submit()
|
public function submit()
|
||||||
@@ -57,20 +60,33 @@ class StandaloneDocker extends Component
|
|||||||
$this->validate();
|
$this->validate();
|
||||||
try {
|
try {
|
||||||
$this->server = Server::find($this->server_id);
|
$this->server = Server::find($this->server_id);
|
||||||
$found = $this->server->standaloneDockers()->where('network', $this->network)->first();
|
if ($this->is_swarm) {
|
||||||
if ($found) {
|
$found = $this->server->swarmDockers()->where('network', $this->network)->first();
|
||||||
$this->createNetworkAndAttachToProxy();
|
if ($found) {
|
||||||
$this->dispatch('error', 'Network already added to this server.');
|
$this->dispatch('error', 'Network already added to this server.');
|
||||||
return;
|
return;
|
||||||
|
} else {
|
||||||
|
$docker = SwarmDocker::create([
|
||||||
|
'name' => $this->name,
|
||||||
|
'network' => $this->network,
|
||||||
|
'server_id' => $this->server_id,
|
||||||
|
]);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
$docker = ModelsStandaloneDocker::create([
|
$found = $this->server->standaloneDockers()->where('network', $this->network)->first();
|
||||||
'name' => $this->name,
|
if ($found) {
|
||||||
'network' => $this->network,
|
$this->dispatch('error', 'Network already added to this server.');
|
||||||
'server_id' => $this->server_id,
|
return;
|
||||||
]);
|
} else {
|
||||||
|
$docker = ModelsStandaloneDocker::create([
|
||||||
|
'name' => $this->name,
|
||||||
|
'network' => $this->network,
|
||||||
|
'server_id' => $this->server_id,
|
||||||
|
]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
$this->createNetworkAndAttachToProxy();
|
$this->createNetworkAndAttachToProxy();
|
||||||
return $this->redirectRoute('destination.show', $docker->uuid, navigate: true);
|
return redirect()->route('destination.show', $docker->uuid);
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
@@ -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';
|
||||||
|
|||||||
@@ -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 $this->redirectRoute('dashboard', navigate: true);
|
return redirect()->route('dashboard');
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ 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 $this->redirectRoute('security.private-key.index', navigate: true);
|
return redirect()->route('security.private-key.index');
|
||||||
}
|
}
|
||||||
$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.');
|
$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) {
|
||||||
|
|||||||
@@ -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 $this->redirectRoute('server.create', navigate: true);
|
return redirect()->route('server.create');
|
||||||
}
|
}
|
||||||
return $this->redirectRoute('security.private-key.show', ['private_key_uuid' => $private_key->uuid], navigate: true);
|
return redirect()->route('security.private-key.show', ['private_key_uuid' => $private_key->uuid]);
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ class AddEmpty extends Component
|
|||||||
'description' => $this->description,
|
'description' => $this->description,
|
||||||
'team_id' => currentTeam()->id,
|
'team_id' => currentTeam()->id,
|
||||||
]);
|
]);
|
||||||
return $this->redirectRoute('project.show', $project->uuid, navigate: true);
|
return redirect()->route('project.show', $project->uuid);
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
@@ -27,10 +27,10 @@ class AddEnvironment extends Component
|
|||||||
'project_id' => $this->project->id,
|
'project_id' => $this->project->id,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return $this->redirectRoute('project.resources', [
|
return redirect()->route('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 {
|
||||||
|
|||||||
@@ -15,15 +15,15 @@ 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 $this->redirectRoute('dashboard', navigate: true);
|
return redirect()->route('dashboard');
|
||||||
}
|
}
|
||||||
$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 $this->redirectRoute('dashboard', navigate: true);
|
return redirect()->route('dashboard');
|
||||||
}
|
}
|
||||||
$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 $this->redirectRoute('dashboard', navigate: true);
|
return redirect()->route('dashboard');
|
||||||
}
|
}
|
||||||
$this->application = $application;
|
$this->application = $application;
|
||||||
$mainServer = $this->application->destination->server;
|
$mainServer = $this->application->destination->server;
|
||||||
|
|||||||
@@ -64,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',
|
||||||
@@ -94,6 +96,8 @@ 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()
|
||||||
{
|
{
|
||||||
@@ -109,7 +113,7 @@ class General extends Component
|
|||||||
$this->application->isConfigurationChanged(true);
|
$this->application->isConfigurationChanged(true);
|
||||||
}
|
}
|
||||||
$this->isConfigurationChanged = $this->application->isConfigurationChanged();
|
$this->isConfigurationChanged = $this->application->isConfigurationChanged();
|
||||||
$this->customLabels = $this->application->parseContainerLabels();
|
$this->customLabels = $this->application->parseContainerLabels();
|
||||||
$this->initialDockerComposeLocation = $this->application->docker_compose_location;
|
$this->initialDockerComposeLocation = $this->application->docker_compose_location;
|
||||||
$this->checkLabelUpdates();
|
$this->checkLabelUpdates();
|
||||||
}
|
}
|
||||||
@@ -195,7 +199,8 @@ class General extends Component
|
|||||||
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();
|
||||||
|
|||||||
@@ -60,12 +60,12 @@ class Heading extends Component
|
|||||||
force_rebuild: false,
|
force_rebuild: false,
|
||||||
is_new_deployment: true,
|
is_new_deployment: true,
|
||||||
);
|
);
|
||||||
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' => $this->deploymentUuid,
|
'deployment_uuid' => $this->deploymentUuid,
|
||||||
'environment_name' => $this->parameters['environment_name'],
|
'environment_name' => $this->parameters['environment_name'],
|
||||||
], navigate: true);
|
]);
|
||||||
}
|
}
|
||||||
public function deploy(bool $force_rebuild = false)
|
public function deploy(bool $force_rebuild = false)
|
||||||
{
|
{
|
||||||
@@ -73,18 +73,22 @@ class Heading extends Component
|
|||||||
$this->dispatch('error', 'Please load a Compose file first.');
|
$this->dispatch('error', 'Please load a Compose file first.');
|
||||||
return;
|
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;
|
||||||
|
}
|
||||||
$this->setDeploymentUuid();
|
$this->setDeploymentUuid();
|
||||||
queue_application_deployment(
|
queue_application_deployment(
|
||||||
application_id: $this->application->id,
|
application_id: $this->application->id,
|
||||||
deployment_uuid: $this->deploymentUuid,
|
deployment_uuid: $this->deploymentUuid,
|
||||||
force_rebuild: $force_rebuild,
|
force_rebuild: $force_rebuild,
|
||||||
);
|
);
|
||||||
$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' => $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()
|
||||||
@@ -109,12 +113,12 @@ class Heading extends Component
|
|||||||
restart_only: true,
|
restart_only: true,
|
||||||
is_new_deployment: true,
|
is_new_deployment: true,
|
||||||
);
|
);
|
||||||
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' => $this->deploymentUuid,
|
'deployment_uuid' => $this->deploymentUuid,
|
||||||
'environment_name' => $this->parameters['environment_name'],
|
'environment_name' => $this->parameters['environment_name'],
|
||||||
], navigate: true);
|
]);
|
||||||
}
|
}
|
||||||
public function restart()
|
public function restart()
|
||||||
{
|
{
|
||||||
@@ -124,11 +128,11 @@ class Heading extends Component
|
|||||||
deployment_uuid: $this->deploymentUuid,
|
deployment_uuid: $this->deploymentUuid,
|
||||||
restart_only: true,
|
restart_only: true,
|
||||||
);
|
);
|
||||||
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' => $this->deploymentUuid,
|
'deployment_uuid' => $this->deploymentUuid,
|
||||||
'environment_name' => $this->parameters['environment_name'],
|
'environment_name' => $this->parameters['environment_name'],
|
||||||
], navigate: true);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 $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' => $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();
|
||||||
|
|||||||
@@ -29,12 +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)
|
||||||
|
|||||||
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');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -152,10 +152,10 @@ class CloneProject extends Component
|
|||||||
}
|
}
|
||||||
$newService->parse();
|
$newService->parse();
|
||||||
}
|
}
|
||||||
return $this->redirectRoute('project.resources', [
|
return redirect()->route('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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 $this->redirect($url,navigate: true);
|
return redirect($url);
|
||||||
} else {
|
} else {
|
||||||
return $this->redirectRoute('project.database.backups.all', $this->parameters);
|
return redirect()->route('project.database.backups.all', $this->parameters);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ 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 $this->redirectRoute('project.show', ['project_uuid' => $this->parameters['project_uuid']], navigate: true);
|
return redirect()->route('project.show', ['project_uuid' => $this->parameters['project_uuid']]);
|
||||||
}
|
}
|
||||||
return $this->dispatch('error', 'Environment has defined resources, please delete them first.');
|
return $this->dispatch('error', 'Environment has defined resources, please delete them first.');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,6 @@ class DeleteProject extends Component
|
|||||||
return $this->dispatch('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 $this->redirectRoute('projects', navigate: true);
|
return redirect()->route('projects');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ class DockerCompose extends Component
|
|||||||
|
|
||||||
$service->parse(isNew: true);
|
$service->parse(isNew: true);
|
||||||
|
|
||||||
return $this->redirectRoute('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,
|
||||||
'project_uuid' => $project->uuid,
|
'project_uuid' => $project->uuid,
|
||||||
|
|||||||
@@ -64,11 +64,11 @@ class DockerImage extends Component
|
|||||||
'name' => 'docker-image-' . $application->uuid,
|
'name' => 'docker-image-' . $application->uuid,
|
||||||
'fqdn' => $fqdn
|
'fqdn' => $fqdn
|
||||||
]);
|
]);
|
||||||
return $this->redirectRoute('project.application.configuration', [
|
return redirect()->route('project.application.configuration', [
|
||||||
'application_uuid' => $application->uuid,
|
'application_uuid' => $application->uuid,
|
||||||
'environment_name' => $environment->name,
|
'environment_name' => $environment->name,
|
||||||
'project_uuid' => $project->uuid,
|
'project_uuid' => $project->uuid,
|
||||||
], navigate: true);
|
]);
|
||||||
}
|
}
|
||||||
public function render()
|
public function render()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -13,6 +13,6 @@ class EmptyProject extends Component
|
|||||||
'name' => generate_random_name(),
|
'name' => generate_random_name(),
|
||||||
'team_id' => currentTeam()->id,
|
'team_id' => currentTeam()->id,
|
||||||
]);
|
]);
|
||||||
return $this->redirectRoute('project.show', ['project_uuid' => $project->uuid, 'environment_name' => 'production'], navigate: true);
|
return redirect()->route('project.show', ['project_uuid' => $project->uuid, 'environment_name' => 'production']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -151,11 +151,11 @@ class GithubPrivateRepository extends Component
|
|||||||
$application->name = generate_application_name($this->selected_repository_owner . '/' . $this->selected_repository_repo, $this->selected_branch_name, $application->uuid);
|
$application->name = generate_application_name($this->selected_repository_owner . '/' . $this->selected_repository_repo, $this->selected_branch_name, $application->uuid);
|
||||||
$application->save();
|
$application->save();
|
||||||
|
|
||||||
return $this->redirectRoute('project.application.configuration', [
|
return redirect()->route('project.application.configuration', [
|
||||||
'application_uuid' => $application->uuid,
|
'application_uuid' => $application->uuid,
|
||||||
'environment_name' => $environment->name,
|
'environment_name' => $environment->name,
|
||||||
'project_uuid' => $project->uuid,
|
'project_uuid' => $project->uuid,
|
||||||
], navigate: true);
|
]);
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -132,11 +132,11 @@ class GithubPrivateRepositoryDeployKey extends Component
|
|||||||
$application->name = generate_random_name($application->uuid);
|
$application->name = generate_random_name($application->uuid);
|
||||||
$application->save();
|
$application->save();
|
||||||
|
|
||||||
return $this->redirectRoute('project.application.configuration', [
|
return redirect()->route('project.application.configuration', [
|
||||||
'application_uuid' => $application->uuid,
|
'application_uuid' => $application->uuid,
|
||||||
'environment_name' => $environment->name,
|
'environment_name' => $environment->name,
|
||||||
'project_uuid' => $project->uuid,
|
'project_uuid' => $project->uuid,
|
||||||
], navigate: true);
|
]);
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -184,11 +184,11 @@ class PublicGitRepository extends Component
|
|||||||
$application->fqdn = $fqdn;
|
$application->fqdn = $fqdn;
|
||||||
$application->save();
|
$application->save();
|
||||||
|
|
||||||
return $this->redirectRoute('project.application.configuration', [
|
return redirect()->route('project.application.configuration', [
|
||||||
'application_uuid' => $application->uuid,
|
'application_uuid' => $application->uuid,
|
||||||
'environment_name' => $environment->name,
|
'environment_name' => $environment->name,
|
||||||
'project_uuid' => $project->uuid,
|
'project_uuid' => $project->uuid,
|
||||||
], navigate: true);
|
]);
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,22 +6,24 @@ use App\Models\Project;
|
|||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use Countable;
|
use Countable;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
use Illuminate\Support\Facades\Cache;
|
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
|
||||||
class Select extends Component
|
class Select extends Component
|
||||||
{
|
{
|
||||||
public $current_step = 'type';
|
public $current_step = 'type';
|
||||||
public ?int $server = null;
|
public ?Server $server = null;
|
||||||
public string $type;
|
public string $type;
|
||||||
public string $server_id;
|
public string $server_id;
|
||||||
public string $destination_uuid;
|
public string $destination_uuid;
|
||||||
|
public Countable|array|Server $allServers = [];
|
||||||
public Countable|array|Server $servers = [];
|
public Countable|array|Server $servers = [];
|
||||||
public Collection|array $standaloneDockers = [];
|
public Collection|array $standaloneDockers = [];
|
||||||
public Collection|array $swarmDockers = [];
|
public Collection|array $swarmDockers = [];
|
||||||
public array $parameters;
|
public array $parameters;
|
||||||
public Collection|array $services = [];
|
public Collection|array $services = [];
|
||||||
public Collection|array $allServices = [];
|
public Collection|array $allServices = [];
|
||||||
|
public bool $isDatabase = false;
|
||||||
|
public bool $includeSwarm = true;
|
||||||
|
|
||||||
public bool $loadingServices = true;
|
public bool $loadingServices = true;
|
||||||
public bool $loading = false;
|
public bool $loading = false;
|
||||||
@@ -31,7 +33,7 @@ class Select extends Component
|
|||||||
|
|
||||||
public ?string $search = null;
|
public ?string $search = null;
|
||||||
protected $queryString = [
|
protected $queryString = [
|
||||||
'server',
|
'server_id',
|
||||||
'search'
|
'search'
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -53,10 +55,10 @@ class Select extends Component
|
|||||||
|
|
||||||
public function updatedSelectedEnvironment()
|
public function updatedSelectedEnvironment()
|
||||||
{
|
{
|
||||||
return $this->redirectRoute('project.resources.new', [
|
return redirect()->route('project.resources.new', [
|
||||||
'project_uuid' => $this->parameters['project_uuid'],
|
'project_uuid' => $this->parameters['project_uuid'],
|
||||||
'environment_name' => $this->selectedEnvironment,
|
'environment_name' => $this->selectedEnvironment,
|
||||||
], navigate: true);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// public function addExistingPostgresql()
|
// public function addExistingPostgresql()
|
||||||
@@ -97,21 +99,45 @@ class Select extends Component
|
|||||||
$this->loadingServices = false;
|
$this->loadingServices = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public function instantSave()
|
||||||
|
{
|
||||||
|
if ($this->includeSwarm) {
|
||||||
|
$this->servers = $this->allServers;
|
||||||
|
} else {
|
||||||
|
$this->servers = $this->allServers->where('settings.is_swarm_worker', false)->where('settings.is_swarm_manager', false);
|
||||||
|
}
|
||||||
|
}
|
||||||
public function setType(string $type)
|
public function setType(string $type)
|
||||||
{
|
{
|
||||||
$this->type = $type;
|
|
||||||
if ($this->loading) return;
|
if ($this->loading) return;
|
||||||
$this->loading = true;
|
$this->loading = true;
|
||||||
|
$this->type = $type;
|
||||||
|
switch ($type) {
|
||||||
|
case 'postgresql':
|
||||||
|
case 'mysql':
|
||||||
|
case 'mariadb':
|
||||||
|
case 'redis':
|
||||||
|
case 'mongodb':
|
||||||
|
$this->isDatabase = true;
|
||||||
|
$this->includeSwarm = false;
|
||||||
|
$this->servers = $this->allServers->where('settings.is_swarm_worker', false)->where('settings.is_swarm_manager', false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (str($type)->startsWith('one-click-service') || str($type)->startsWith('docker-compose-empty')) {
|
||||||
|
$this->isDatabase = true;
|
||||||
|
$this->includeSwarm = false;
|
||||||
|
$this->servers = $this->allServers->where('settings.is_swarm_worker', false)->where('settings.is_swarm_manager', false);
|
||||||
|
}
|
||||||
if ($type === "existing-postgresql") {
|
if ($type === "existing-postgresql") {
|
||||||
$this->current_step = $type;
|
$this->current_step = $type;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (count($this->servers) === 1) {
|
// if (count($this->servers) === 1) {
|
||||||
$server = $this->servers->first();
|
// $server = $this->servers->first();
|
||||||
$this->setServer($server);
|
// $this->setServer($server);
|
||||||
}
|
// }
|
||||||
if (!is_null($this->server)) {
|
if (!is_null($this->server)) {
|
||||||
$foundServer = $this->servers->where('id', $this->server)->first();
|
$foundServer = $this->servers->where('id', $this->server->id)->first();
|
||||||
if ($foundServer) {
|
if ($foundServer) {
|
||||||
return $this->setServer($foundServer);
|
return $this->setServer($foundServer);
|
||||||
}
|
}
|
||||||
@@ -122,6 +148,7 @@ class Select extends Component
|
|||||||
public function setServer(Server $server)
|
public function setServer(Server $server)
|
||||||
{
|
{
|
||||||
$this->server_id = $server->id;
|
$this->server_id = $server->id;
|
||||||
|
$this->server = $server;
|
||||||
$this->standaloneDockers = $server->standaloneDockers;
|
$this->standaloneDockers = $server->standaloneDockers;
|
||||||
$this->swarmDockers = $server->swarmDockers;
|
$this->swarmDockers = $server->swarmDockers;
|
||||||
$this->current_step = 'destinations';
|
$this->current_step = 'destinations';
|
||||||
@@ -142,5 +169,6 @@ class Select extends Component
|
|||||||
public function loadServers()
|
public function loadServers()
|
||||||
{
|
{
|
||||||
$this->servers = Server::isUsable()->get();
|
$this->servers = Server::isUsable()->get();
|
||||||
|
$this->allServers = $this->servers;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -70,10 +70,10 @@ CMD ["nginx", "-g", "daemon off;"]
|
|||||||
'fqdn' => $fqdn
|
'fqdn' => $fqdn
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return $this->redirectRoute('project.application.configuration', [
|
return redirect()->route('project.application.configuration', [
|
||||||
'application_uuid' => $application->uuid,
|
'application_uuid' => $application->uuid,
|
||||||
'environment_name' => $environment->name,
|
'environment_name' => $environment->name,
|
||||||
'project_uuid' => $project->uuid,
|
'project_uuid' => $project->uuid,
|
||||||
], navigate: true);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ class Application extends Component
|
|||||||
try {
|
try {
|
||||||
$this->application->delete();
|
$this->application->delete();
|
||||||
$this->dispatch('success', 'Application deleted successfully.');
|
$this->dispatch('success', 'Application deleted successfully.');
|
||||||
return $this->redirectRoute('project.service.configuration', $this->parameters, navigate: true);
|
return redirect()->route('project.service.configuration', $this->parameters);
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,10 +25,10 @@ class Danger extends Component
|
|||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
DeleteResourceJob::dispatchSync($this->resource);
|
DeleteResourceJob::dispatchSync($this->resource);
|
||||||
return $this->redirectRoute('project.resources', [
|
return redirect()->route('project.resources', [
|
||||||
'project_uuid' => $this->projectUuid,
|
'project_uuid' => $this->projectUuid,
|
||||||
'environment_name' => $this->environmentName
|
'environment_name' => $this->environmentName
|
||||||
], navigate: true);
|
]);
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,7 +65,11 @@ class Show extends Component
|
|||||||
|
|
||||||
public function delete()
|
public function delete()
|
||||||
{
|
{
|
||||||
$this->env->delete();
|
try {
|
||||||
$this->dispatch('refreshEnvs');
|
$this->env->delete();
|
||||||
|
$this->dispatch('refreshEnvs');
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
return handleError($e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,9 +73,17 @@ class GetLogs extends Component
|
|||||||
if (!$refresh && $this->resource?->getMorphClass() === 'App\Models\Service') return;
|
if (!$refresh && $this->resource?->getMorphClass() === 'App\Models\Service') return;
|
||||||
if ($this->container) {
|
if ($this->container) {
|
||||||
if ($this->showTimeStamps) {
|
if ($this->showTimeStamps) {
|
||||||
$sshCommand = generateSshCommand($this->server, "docker logs -n {$this->numberOfLines} -t {$this->container}");
|
if ($this->server->isSwarm()) {
|
||||||
|
$sshCommand = generateSshCommand($this->server, "docker service logs -n {$this->numberOfLines} -t {$this->container}");
|
||||||
|
} else {
|
||||||
|
$sshCommand = generateSshCommand($this->server, "docker logs -n {$this->numberOfLines} -t {$this->container}");
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
$sshCommand = generateSshCommand($this->server, "docker logs -n {$this->numberOfLines} {$this->container}");
|
if ($this->server->isSwarm()) {
|
||||||
|
$sshCommand = generateSshCommand($this->server, "docker service logs -n {$this->numberOfLines} {$this->container}");
|
||||||
|
} else {
|
||||||
|
$sshCommand = generateSshCommand($this->server, "docker logs -n {$this->numberOfLines} {$this->container}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if ($refresh) {
|
if ($refresh) {
|
||||||
$this->outputs = '';
|
$this->outputs = '';
|
||||||
|
|||||||
@@ -34,7 +34,15 @@ class Logs extends Component
|
|||||||
$this->resource = Application::where('uuid', $this->parameters['application_uuid'])->firstOrFail();
|
$this->resource = Application::where('uuid', $this->parameters['application_uuid'])->firstOrFail();
|
||||||
$this->status = $this->resource->status;
|
$this->status = $this->resource->status;
|
||||||
$this->server = $this->resource->destination->server;
|
$this->server = $this->resource->destination->server;
|
||||||
$containers = getCurrentApplicationContainerStatus($this->server, $this->resource->id, 0);
|
if ($this->server->isSwarm()) {
|
||||||
|
$containers = collect([
|
||||||
|
[
|
||||||
|
'Names' => $this->resource->uuid . '_' . $this->resource->uuid,
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
$containers = getCurrentApplicationContainerStatus($this->server, $this->resource->id, 0);
|
||||||
|
}
|
||||||
if ($containers->count() > 0) {
|
if ($containers->count() > 0) {
|
||||||
$containers->each(function ($container) {
|
$containers->each(function ($container) {
|
||||||
$this->containers->push(str_replace('/', '', $container['Names']));
|
$this->containers->push(str_replace('/', '', $container['Names']));
|
||||||
@@ -62,7 +70,7 @@ class Logs extends Component
|
|||||||
$this->status = $this->resource->status;
|
$this->status = $this->resource->status;
|
||||||
$this->server = $this->resource->destination->server;
|
$this->server = $this->resource->destination->server;
|
||||||
$this->container = $this->resource->uuid;
|
$this->container = $this->resource->uuid;
|
||||||
if (str(data_get($this,'resource.status'))->startsWith('running')) {
|
if (str(data_get($this, 'resource.status'))->startsWith('running')) {
|
||||||
$this->containers->push($this->container);
|
$this->containers->push($this->container);
|
||||||
}
|
}
|
||||||
} else if (data_get($this->parameters, 'service_uuid')) {
|
} else if (data_get($this->parameters, 'service_uuid')) {
|
||||||
|
|||||||
@@ -2,22 +2,26 @@
|
|||||||
|
|
||||||
namespace App\Livewire\Project\Shared\Storages;
|
namespace App\Livewire\Project\Shared\Storages;
|
||||||
|
|
||||||
|
use App\Models\Application;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
|
||||||
class Add extends Component
|
class Add extends Component
|
||||||
{
|
{
|
||||||
public $uuid;
|
public $uuid;
|
||||||
public $parameters;
|
public $parameters;
|
||||||
|
public $isSwarm = false;
|
||||||
public string $name;
|
public string $name;
|
||||||
public string $mount_path;
|
public string $mount_path;
|
||||||
public string|null $host_path = null;
|
public ?string $host_path = null;
|
||||||
|
|
||||||
protected $listeners = ['clearAddStorage' => 'clear'];
|
public $rules = [
|
||||||
protected $rules = [
|
|
||||||
'name' => 'required|string',
|
'name' => 'required|string',
|
||||||
'mount_path' => 'required|string',
|
'mount_path' => 'required|string',
|
||||||
'host_path' => 'string|nullable',
|
'host_path' => 'string|nullable',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
protected $listeners = ['clearAddStorage' => 'clear'];
|
||||||
|
|
||||||
protected $validationAttributes = [
|
protected $validationAttributes = [
|
||||||
'name' => 'name',
|
'name' => 'name',
|
||||||
'mount_path' => 'mount',
|
'mount_path' => 'mount',
|
||||||
@@ -27,17 +31,33 @@ class Add extends Component
|
|||||||
public function mount()
|
public function mount()
|
||||||
{
|
{
|
||||||
$this->parameters = get_route_parameters();
|
$this->parameters = get_route_parameters();
|
||||||
|
if (data_get($this->parameters, 'application_uuid')) {
|
||||||
|
$applicationUuid = $this->parameters['application_uuid'];
|
||||||
|
$application = Application::where('uuid', $applicationUuid)->first();
|
||||||
|
if (!$application) {
|
||||||
|
abort(404);
|
||||||
|
}
|
||||||
|
if ($application->destination->server->isSwarm()) {
|
||||||
|
$this->isSwarm = true;
|
||||||
|
$this->rules['host_path'] = 'required|string';
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function submit()
|
public function submit()
|
||||||
{
|
{
|
||||||
$this->validate();
|
try {
|
||||||
$name = $this->uuid . '-' . $this->name;
|
$this->validate($this->rules);
|
||||||
$this->dispatch('addNewVolume', [
|
$name = $this->uuid . '-' . $this->name;
|
||||||
'name' => $name,
|
$this->dispatch('addNewVolume', [
|
||||||
'mount_path' => $this->mount_path,
|
'name' => $name,
|
||||||
'host_path' => $this->host_path,
|
'mount_path' => $this->mount_path,
|
||||||
]);
|
'host_path' => $this->host_path,
|
||||||
|
]);
|
||||||
|
$this->dispatch('closeStorageModal');
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
return handleError($e, $this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function clear()
|
public function clear()
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ class Delete extends Component
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$this->server->delete();
|
$this->server->delete();
|
||||||
return $this->redirectRoute('server.all', navigate: true);
|
return redirect()->route('server.all');
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ class Show extends Component
|
|||||||
try {
|
try {
|
||||||
$this->server = Server::ownedByCurrentTeam()->whereUuid(request()->server_uuid)->first();
|
$this->server = Server::ownedByCurrentTeam()->whereUuid(request()->server_uuid)->first();
|
||||||
if (is_null($this->server)) {
|
if (is_null($this->server)) {
|
||||||
return $this->redirectRoute('server.all', navigate: true);
|
return redirect()->route('server.all');
|
||||||
}
|
}
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ class Form extends Component
|
|||||||
'server.settings.is_cloudflare_tunnel' => 'required|boolean',
|
'server.settings.is_cloudflare_tunnel' => 'required|boolean',
|
||||||
'server.settings.is_reachable' => 'required',
|
'server.settings.is_reachable' => 'required',
|
||||||
'server.settings.is_swarm_manager' => 'required|boolean',
|
'server.settings.is_swarm_manager' => 'required|boolean',
|
||||||
// 'server.settings.is_swarm_worker' => 'required|boolean',
|
'server.settings.is_swarm_worker' => 'required|boolean',
|
||||||
'wildcard_domain' => 'nullable|url',
|
'wildcard_domain' => 'nullable|url',
|
||||||
];
|
];
|
||||||
protected $validationAttributes = [
|
protected $validationAttributes = [
|
||||||
@@ -37,16 +37,13 @@ class Form extends Component
|
|||||||
'server.settings.is_cloudflare_tunnel' => 'Cloudflare Tunnel',
|
'server.settings.is_cloudflare_tunnel' => 'Cloudflare Tunnel',
|
||||||
'server.settings.is_reachable' => 'Is reachable',
|
'server.settings.is_reachable' => 'Is reachable',
|
||||||
'server.settings.is_swarm_manager' => 'Swarm Manager',
|
'server.settings.is_swarm_manager' => 'Swarm Manager',
|
||||||
// 'server.settings.is_swarm_worker' => 'Swarm Worker',
|
'server.settings.is_swarm_worker' => 'Swarm Worker',
|
||||||
];
|
];
|
||||||
|
|
||||||
public function mount()
|
public function mount()
|
||||||
{
|
{
|
||||||
$this->wildcard_domain = $this->server->settings->wildcard_domain;
|
$this->wildcard_domain = $this->server->settings->wildcard_domain;
|
||||||
$this->cleanup_after_percentage = $this->server->settings->cleanup_after_percentage;
|
$this->cleanup_after_percentage = $this->server->settings->cleanup_after_percentage;
|
||||||
if (!$this->server->isFunctional()) {
|
|
||||||
$this->validateServer();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
public function serverRefresh($install = true)
|
public function serverRefresh($install = true)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ class LogDrains extends Component
|
|||||||
try {
|
try {
|
||||||
$server = Server::ownedByCurrentTeam()->whereUuid(request()->server_uuid)->first();
|
$server = Server::ownedByCurrentTeam()->whereUuid(request()->server_uuid)->first();
|
||||||
if (is_null($server)) {
|
if (is_null($server)) {
|
||||||
return $this->redirectRoute('server.all', navigate: true);
|
return redirect()->route('server.all');
|
||||||
}
|
}
|
||||||
$this->server = $server;
|
$this->server = $server;
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
|
|||||||
@@ -22,7 +22,10 @@ class ByIp extends Component
|
|||||||
public string $user = 'root';
|
public string $user = 'root';
|
||||||
public int $port = 22;
|
public int $port = 22;
|
||||||
public bool $is_swarm_manager = false;
|
public bool $is_swarm_manager = false;
|
||||||
|
public bool $is_swarm_worker = false;
|
||||||
|
public $selected_swarm_cluster = null;
|
||||||
|
|
||||||
|
public $swarm_managers = [];
|
||||||
protected $rules = [
|
protected $rules = [
|
||||||
'name' => 'required|string',
|
'name' => 'required|string',
|
||||||
'description' => 'nullable|string',
|
'description' => 'nullable|string',
|
||||||
@@ -30,6 +33,7 @@ class ByIp extends Component
|
|||||||
'user' => 'required|string',
|
'user' => 'required|string',
|
||||||
'port' => 'required|integer',
|
'port' => 'required|integer',
|
||||||
'is_swarm_manager' => 'required|boolean',
|
'is_swarm_manager' => 'required|boolean',
|
||||||
|
'is_swarm_worker' => 'required|boolean',
|
||||||
];
|
];
|
||||||
protected $validationAttributes = [
|
protected $validationAttributes = [
|
||||||
'name' => 'Name',
|
'name' => 'Name',
|
||||||
@@ -38,12 +42,17 @@ class ByIp extends Component
|
|||||||
'user' => 'User',
|
'user' => 'User',
|
||||||
'port' => 'Port',
|
'port' => 'Port',
|
||||||
'is_swarm_manager' => 'Swarm Manager',
|
'is_swarm_manager' => 'Swarm Manager',
|
||||||
|
'is_swarm_worker' => 'Swarm Worker',
|
||||||
];
|
];
|
||||||
|
|
||||||
public function mount()
|
public function mount()
|
||||||
{
|
{
|
||||||
$this->name = generate_random_name();
|
$this->name = generate_random_name();
|
||||||
$this->private_key_id = $this->private_keys->first()->id;
|
$this->private_key_id = $this->private_keys->first()->id;
|
||||||
|
$this->swarm_managers = Server::isUsable()->get()->where('settings.is_swarm_manager', true);
|
||||||
|
if ($this->swarm_managers->count() > 0) {
|
||||||
|
$this->selected_swarm_cluster = $this->swarm_managers->first()->id;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setPrivateKey(string $private_key_id)
|
public function setPrivateKey(string $private_key_id)
|
||||||
@@ -53,7 +62,7 @@ class ByIp extends Component
|
|||||||
|
|
||||||
public function instantSave()
|
public function instantSave()
|
||||||
{
|
{
|
||||||
$this->dispatch('success', 'Application settings updated!');
|
// $this->dispatch('success', 'Application settings updated!');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function submit()
|
public function submit()
|
||||||
@@ -63,7 +72,7 @@ class ByIp extends Component
|
|||||||
if (is_null($this->private_key_id)) {
|
if (is_null($this->private_key_id)) {
|
||||||
return $this->dispatch('error', 'You must select a private key');
|
return $this->dispatch('error', 'You must select a private key');
|
||||||
}
|
}
|
||||||
$server = Server::create([
|
$payload = [
|
||||||
'name' => $this->name,
|
'name' => $this->name,
|
||||||
'description' => $this->description,
|
'description' => $this->description,
|
||||||
'ip' => $this->ip,
|
'ip' => $this->ip,
|
||||||
@@ -75,11 +84,16 @@ class ByIp extends Component
|
|||||||
"type" => ProxyTypes::TRAEFIK_V2->value,
|
"type" => ProxyTypes::TRAEFIK_V2->value,
|
||||||
"status" => ProxyStatus::EXITED->value,
|
"status" => ProxyStatus::EXITED->value,
|
||||||
],
|
],
|
||||||
]);
|
];
|
||||||
|
if ($this->is_swarm_worker) {
|
||||||
|
$payload['swarm_cluster'] = $this->selected_swarm_cluster;
|
||||||
|
}
|
||||||
|
$server = Server::create($payload);
|
||||||
$server->settings->is_swarm_manager = $this->is_swarm_manager;
|
$server->settings->is_swarm_manager = $this->is_swarm_manager;
|
||||||
|
$server->settings->is_swarm_worker = $this->is_swarm_worker;
|
||||||
$server->settings->save();
|
$server->settings->save();
|
||||||
$server->addInitialNetwork();
|
$server->addInitialNetwork();
|
||||||
return $this->redirectRoute('server.show', $server->uuid, navigate: true);
|
return redirect()->route('server.show', $server->uuid);
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ class Show extends Component
|
|||||||
try {
|
try {
|
||||||
$this->server = Server::ownedByCurrentTeam()->whereUuid(request()->server_uuid)->first();
|
$this->server = Server::ownedByCurrentTeam()->whereUuid(request()->server_uuid)->first();
|
||||||
if (is_null($this->server)) {
|
if (is_null($this->server)) {
|
||||||
return $this->redirectRoute('server.all', navigate: true);
|
return redirect()->route('server.all');
|
||||||
}
|
}
|
||||||
$this->privateKeys = PrivateKey::ownedByCurrentTeam()->get()->where('is_git_related', false);
|
$this->privateKeys = PrivateKey::ownedByCurrentTeam()->get()->where('is_git_related', false);
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ class Logs extends Component
|
|||||||
try {
|
try {
|
||||||
$this->server = Server::ownedByCurrentTeam()->whereUuid(request()->server_uuid)->first();
|
$this->server = Server::ownedByCurrentTeam()->whereUuid(request()->server_uuid)->first();
|
||||||
if (is_null($this->server)) {
|
if (is_null($this->server)) {
|
||||||
return $this->redirectRoute('server.all', navigate: true);
|
return redirect()->route('server.all');
|
||||||
}
|
}
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ class Show extends Component
|
|||||||
try {
|
try {
|
||||||
$this->server = Server::ownedByCurrentTeam()->whereUuid(request()->server_uuid)->first();
|
$this->server = Server::ownedByCurrentTeam()->whereUuid(request()->server_uuid)->first();
|
||||||
if (is_null($this->server)) {
|
if (is_null($this->server)) {
|
||||||
return $this->redirectRoute('server.all', navigate: true);
|
return redirect()->route('server.all');
|
||||||
}
|
}
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ class Status extends Component
|
|||||||
|
|
||||||
protected $listeners = ['proxyStatusUpdated', 'startProxyPolling'];
|
protected $listeners = ['proxyStatusUpdated', 'startProxyPolling'];
|
||||||
public function mount() {
|
public function mount() {
|
||||||
$this->checkProxy();
|
|
||||||
}
|
}
|
||||||
public function startProxyPolling()
|
public function startProxyPolling()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ class Show extends Component
|
|||||||
try {
|
try {
|
||||||
$this->server = Server::ownedByCurrentTeam()->whereUuid(request()->server_uuid)->first();
|
$this->server = Server::ownedByCurrentTeam()->whereUuid(request()->server_uuid)->first();
|
||||||
if (is_null($this->server)) {
|
if (is_null($this->server)) {
|
||||||
return $this->redirectRoute('server.all', navigate: true);
|
return redirect()->route('server.all');
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ class Change extends Component
|
|||||||
$github_app_uuid = request()->github_app_uuid;
|
$github_app_uuid = request()->github_app_uuid;
|
||||||
$this->github_app = GithubApp::where('uuid', $github_app_uuid)->first();
|
$this->github_app = GithubApp::where('uuid', $github_app_uuid)->first();
|
||||||
if (!$this->github_app) {
|
if (!$this->github_app) {
|
||||||
return $this->redirectRoute('source.all', navigate: true);
|
return redirect()->route('source.all');
|
||||||
}
|
}
|
||||||
$settings = InstanceSettings::get();
|
$settings = InstanceSettings::get();
|
||||||
$this->github_app->makeVisible('client_secret')->makeVisible('webhook_secret');
|
$this->github_app->makeVisible('client_secret')->makeVisible('webhook_secret');
|
||||||
@@ -67,7 +67,7 @@ class Change extends Component
|
|||||||
$type = data_get($parameters, 'type');
|
$type = data_get($parameters, 'type');
|
||||||
$destination = data_get($parameters, 'destination');
|
$destination = data_get($parameters, 'destination');
|
||||||
session()->forget('from');
|
session()->forget('from');
|
||||||
return $this->redirectRoute($back, [
|
return redirect()->route($back, [
|
||||||
'environment_name' => $environment_name,
|
'environment_name' => $environment_name,
|
||||||
'project_uuid' => $project_uuid,
|
'project_uuid' => $project_uuid,
|
||||||
'type' => $type,
|
'type' => $type,
|
||||||
@@ -117,7 +117,7 @@ class Change extends Component
|
|||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$this->github_app->delete();
|
$this->github_app->delete();
|
||||||
return $this->redirectRoute('source.all', navigate: true);
|
return redirect()->route('source.all');
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ class Create extends Component
|
|||||||
if (session('from')) {
|
if (session('from')) {
|
||||||
session(['from' => session('from') + ['source_id' => $github_app->id]]);
|
session(['from' => session('from') + ['source_id' => $github_app->id]]);
|
||||||
}
|
}
|
||||||
return $this->redirectRoute('source.github.show', ['github_app_uuid' => $github_app->uuid], navigate: true);
|
return redirect()->route('source.github.show', ['github_app_uuid' => $github_app->uuid]);
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
namespace App\Livewire\Subscription;
|
namespace App\Livewire\Subscription;
|
||||||
|
|
||||||
use App\Models\InstanceSettings;
|
use App\Models\InstanceSettings;
|
||||||
|
use App\Providers\RouteServiceProvider;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
|
||||||
class Show extends Component
|
class Show extends Component
|
||||||
@@ -11,7 +12,7 @@ class Show extends Component
|
|||||||
public bool $alreadySubscribed = false;
|
public bool $alreadySubscribed = false;
|
||||||
public function mount() {
|
public function mount() {
|
||||||
if (!isCloud()) {
|
if (!isCloud()) {
|
||||||
return $this->redirect('/', navigate: true);
|
return redirect(RouteServiceProvider::HOME);
|
||||||
}
|
}
|
||||||
$this->settings = InstanceSettings::get();
|
$this->settings = InstanceSettings::get();
|
||||||
$this->alreadySubscribed = currentTeam()->subscription()->exists();
|
$this->alreadySubscribed = currentTeam()->subscription()->exists();
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ class Create extends Component
|
|||||||
]);
|
]);
|
||||||
auth()->user()->teams()->attach($team, ['role' => 'admin']);
|
auth()->user()->teams()->attach($team, ['role' => 'admin']);
|
||||||
refreshSession();
|
refreshSession();
|
||||||
return $this->redirectRoute('team.index', navigate: true);
|
return redirect()->route('team.index');
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,6 @@ class Delete extends Component
|
|||||||
});
|
});
|
||||||
|
|
||||||
refreshSession();
|
refreshSession();
|
||||||
return $this->redirectRoute('team.index', navigate: true);
|
return redirect()->route('team.index');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ class Create extends Component
|
|||||||
$this->storage->team_id = currentTeam()->id;
|
$this->storage->team_id = currentTeam()->id;
|
||||||
$this->storage->testConnection();
|
$this->storage->testConnection();
|
||||||
$this->storage->save();
|
$this->storage->save();
|
||||||
return $this->redirectRoute('team.storages.show', $this->storage->uuid, navigate: true);
|
return redirect()->route('team.storages.show', $this->storage->uuid);
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ class Form extends Component
|
|||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$this->storage->delete();
|
$this->storage->delete();
|
||||||
return $this->redirectRoute('team.storages.all', navigate: true);
|
return redirect()->route('team.storages.all');
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ class Index extends Component
|
|||||||
public function mount()
|
public function mount()
|
||||||
{
|
{
|
||||||
if (config('coolify.waitlist') == false) {
|
if (config('coolify.waitlist') == false) {
|
||||||
return $this->redirectRoute('register', navigate: true);
|
return redirect()->route('register');
|
||||||
}
|
}
|
||||||
$this->waitingInLine = Waitlist::whereVerified(true)->count();
|
$this->waitingInLine = Waitlist::whereVerified(true)->count();
|
||||||
$this->users = User::count();
|
$this->users = User::count();
|
||||||
|
|||||||
@@ -633,7 +633,7 @@ class Application extends BaseModel
|
|||||||
'memswap_limit' => $this->limits_memory_swap,
|
'memswap_limit' => $this->limits_memory_swap,
|
||||||
'mem_swappiness' => $this->limits_memory_swappiness,
|
'mem_swappiness' => $this->limits_memory_swappiness,
|
||||||
'mem_reservation' => $this->limits_memory_reservation,
|
'mem_reservation' => $this->limits_memory_reservation,
|
||||||
'cpus' => (int) $this->limits_cpus,
|
'cpus' => (float) $this->limits_cpus,
|
||||||
'cpuset' => $this->limits_cpuset,
|
'cpuset' => $this->limits_cpuset,
|
||||||
'cpu_shares' => $this->limits_cpu_shares,
|
'cpu_shares' => $this->limits_cpu_shares,
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ use Illuminate\Database\Eloquent\Casts\Attribute;
|
|||||||
use Illuminate\Support\Carbon;
|
use Illuminate\Support\Carbon;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
use Illuminate\Support\Facades\Process;
|
use Illuminate\Support\Facades\Process;
|
||||||
use Illuminate\Support\Sleep;
|
|
||||||
use Spatie\SchemalessAttributes\Casts\SchemalessAttributes;
|
use Spatie\SchemalessAttributes\Casts\SchemalessAttributes;
|
||||||
use Spatie\SchemalessAttributes\SchemalessAttributesTrait;
|
use Spatie\SchemalessAttributes\SchemalessAttributesTrait;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
@@ -72,7 +71,7 @@ class Server extends BaseModel
|
|||||||
|
|
||||||
static public function isUsable()
|
static public function isUsable()
|
||||||
{
|
{
|
||||||
return Server::ownedByCurrentTeam()->whereRelation('settings', 'is_reachable', true)->whereRelation('settings', 'is_usable', true);
|
return Server::ownedByCurrentTeam()->whereRelation('settings', 'is_reachable', true)->whereRelation('settings', 'is_usable', true)->whereRelation('settings', 'is_swarm_worker', false);
|
||||||
}
|
}
|
||||||
|
|
||||||
static public function destinationsByServer(string $server_id)
|
static public function destinationsByServer(string $server_id)
|
||||||
@@ -150,30 +149,45 @@ class Server extends BaseModel
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
public function isServerReady()
|
public function isServerReady(int $tries = 3)
|
||||||
{
|
{
|
||||||
|
if ($this->skipServer()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
$serverUptimeCheckNumber = $this->unreachable_count;
|
$serverUptimeCheckNumber = $this->unreachable_count;
|
||||||
$serverUptimeCheckNumberMax = 8;
|
if ($this->unreachable_count < $tries) {
|
||||||
|
$serverUptimeCheckNumber = $this->unreachable_count + 1;
|
||||||
|
}
|
||||||
|
if ($this->unreachable_count > $tries) {
|
||||||
|
$serverUptimeCheckNumber = $tries;
|
||||||
|
}
|
||||||
|
|
||||||
$currentTime = now()->timestamp;
|
$serverUptimeCheckNumberMax = $tries;
|
||||||
$runtime = 50;
|
|
||||||
|
|
||||||
$isReady = false;
|
// ray('server: ' . $this->name);
|
||||||
// Run for 50 seconds max and check every 5 seconds for 8 times
|
// ray('serverUptimeCheckNumber: ' . $serverUptimeCheckNumber);
|
||||||
while ($currentTime + $runtime > now()->timestamp) {
|
// ray('serverUptimeCheckNumberMax: ' . $serverUptimeCheckNumberMax);
|
||||||
ray('serverUptimeCheckNumber: ' . $serverUptimeCheckNumber);
|
|
||||||
|
$result = $this->validateConnection();
|
||||||
|
if ($result) {
|
||||||
|
if ($this->unreachable_notification_sent === true) {
|
||||||
|
$this->update(['unreachable_notification_sent' => false]);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
if ($serverUptimeCheckNumber >= $serverUptimeCheckNumberMax) {
|
if ($serverUptimeCheckNumber >= $serverUptimeCheckNumberMax) {
|
||||||
|
// Reached max number of retries
|
||||||
if ($this->unreachable_notification_sent === false) {
|
if ($this->unreachable_notification_sent === false) {
|
||||||
ray('Server unreachable, sending notification...');
|
ray('Server unreachable, sending notification...');
|
||||||
$this->team?->notify(new Unreachable($this));
|
$this->team?->notify(new Unreachable($this));
|
||||||
$this->update(['unreachable_notification_sent' => true]);
|
$this->update(['unreachable_notification_sent' => true]);
|
||||||
}
|
}
|
||||||
$this->settings()->update([
|
if ($this->settings->is_reachable === true) {
|
||||||
'is_reachable' => false,
|
$this->settings()->update([
|
||||||
]);
|
'is_reachable' => false,
|
||||||
$this->update([
|
]);
|
||||||
'unreachable_count' => 0,
|
}
|
||||||
]);
|
|
||||||
foreach ($this->applications() as $application) {
|
foreach ($this->applications() as $application) {
|
||||||
$application->update(['status' => 'exited']);
|
$application->update(['status' => 'exited']);
|
||||||
}
|
}
|
||||||
@@ -190,23 +204,13 @@ class Server extends BaseModel
|
|||||||
$db->update(['status' => 'exited']);
|
$db->update(['status' => 'exited']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$isReady = false;
|
} else {
|
||||||
break;
|
|
||||||
}
|
|
||||||
$result = $this->validateConnection();
|
|
||||||
// ray('validateConnection: ' . $result);
|
|
||||||
if (!$result) {
|
|
||||||
$serverUptimeCheckNumber++;
|
|
||||||
$this->update([
|
$this->update([
|
||||||
'unreachable_count' => $serverUptimeCheckNumber,
|
'unreachable_count' => $this->unreachable_count + 1,
|
||||||
]);
|
]);
|
||||||
Sleep::for(5)->seconds();
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
$isReady = true;
|
return false;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
return $isReady;
|
|
||||||
}
|
}
|
||||||
public function getDiskUsage()
|
public function getDiskUsage()
|
||||||
{
|
{
|
||||||
@@ -380,9 +384,20 @@ class Server extends BaseModel
|
|||||||
{
|
{
|
||||||
return data_get($this, 'settings.is_swarm_manager') || data_get($this, 'settings.is_swarm_worker');
|
return data_get($this, 'settings.is_swarm_manager') || data_get($this, 'settings.is_swarm_worker');
|
||||||
}
|
}
|
||||||
|
public function isSwarmManager()
|
||||||
|
{
|
||||||
|
return data_get($this, 'settings.is_swarm_manager');
|
||||||
|
}
|
||||||
|
public function isSwarmWorker()
|
||||||
|
{
|
||||||
|
return data_get($this, 'settings.is_swarm_worker');
|
||||||
|
}
|
||||||
public function validateConnection()
|
public function validateConnection()
|
||||||
{
|
{
|
||||||
$server = Server::find($this->id);
|
$server = Server::find($this->id);
|
||||||
|
if (!$server) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if ($server->skipServer()) {
|
if ($server->skipServer()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ class Revived extends Notification implements ShouldQueue
|
|||||||
|
|
||||||
public function toDiscord(): string
|
public function toDiscord(): string
|
||||||
{
|
{
|
||||||
$message = "Coolify: Server '{$this->server->name}' revived. All automations & integrations are turned on again!";
|
$message = "Coolify: Server '{$this->server->name}' revived. All automations & integrations are turned on again!";
|
||||||
return $message;
|
return $message;
|
||||||
}
|
}
|
||||||
public function toTelegram(): array
|
public function toTelegram(): array
|
||||||
|
|||||||
@@ -31,9 +31,9 @@ class FortifyServiceProvider extends ServiceProvider
|
|||||||
{
|
{
|
||||||
// First user (root) will be redirected to /settings instead of / on registration.
|
// First user (root) will be redirected to /settings instead of / on registration.
|
||||||
if ($request->user()->currentTeam->id === 0) {
|
if ($request->user()->currentTeam->id === 0) {
|
||||||
return redirect('/settings');
|
return redirect()->route('settings.configuration');
|
||||||
}
|
}
|
||||||
return redirect('/');
|
return redirect(RouteServiceProvider::HOME);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,20 +14,23 @@ use Visus\Cuid2\Cuid2;
|
|||||||
function getCurrentApplicationContainerStatus(Server $server, int $id, ?int $pullRequestId = null): Collection
|
function getCurrentApplicationContainerStatus(Server $server, int $id, ?int $pullRequestId = null): Collection
|
||||||
{
|
{
|
||||||
$containers = collect([]);
|
$containers = collect([]);
|
||||||
$containers = instant_remote_process(["docker ps -a --filter='label=coolify.applicationId={$id}' --format '{{json .}}' "], $server);
|
if (!$server->isSwarm()) {
|
||||||
$containers = format_docker_command_output_to_json($containers);
|
$containers = instant_remote_process(["docker ps -a --filter='label=coolify.applicationId={$id}' --format '{{json .}}' "], $server);
|
||||||
$containers = $containers->map(function ($container) use ($pullRequestId) {
|
$containers = format_docker_command_output_to_json($containers);
|
||||||
$labels = data_get($container, 'Labels');
|
$containers = $containers->map(function ($container) use ($pullRequestId) {
|
||||||
if (!str($labels)->contains("coolify.pullRequestId=")) {
|
$labels = data_get($container, 'Labels');
|
||||||
data_set($container, 'Labels', $labels . ",coolify.pullRequestId={$pullRequestId}");
|
if (!str($labels)->contains("coolify.pullRequestId=")) {
|
||||||
return $container;
|
data_set($container, 'Labels', $labels . ",coolify.pullRequestId={$pullRequestId}");
|
||||||
}
|
return $container;
|
||||||
if (str($labels)->contains("coolify.pullRequestId=$pullRequestId")) {
|
}
|
||||||
return $container;
|
if (str($labels)->contains("coolify.pullRequestId=$pullRequestId")) {
|
||||||
}
|
return $container;
|
||||||
return null;
|
}
|
||||||
});
|
return null;
|
||||||
$containers = $containers->filter();
|
});
|
||||||
|
$containers = $containers->filter();
|
||||||
|
return $containers;
|
||||||
|
}
|
||||||
return $containers;
|
return $containers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,10 +15,16 @@ function get_proxy_path()
|
|||||||
}
|
}
|
||||||
function connectProxyToNetworks(Server $server)
|
function connectProxyToNetworks(Server $server)
|
||||||
{
|
{
|
||||||
// Standalone networks
|
if ($server->isSwarm()) {
|
||||||
$networks = collect($server->standaloneDockers)->map(function ($docker) {
|
$networks = collect($server->swarmDockers)->map(function ($docker) {
|
||||||
return $docker['network'];
|
return $docker['network'];
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
// Standalone networks
|
||||||
|
$networks = collect($server->standaloneDockers)->map(function ($docker) {
|
||||||
|
return $docker['network'];
|
||||||
|
});
|
||||||
|
}
|
||||||
// Service networks
|
// Service networks
|
||||||
foreach ($server->services()->get() as $service) {
|
foreach ($server->services()->get() as $service) {
|
||||||
$networks->push($service->networks());
|
$networks->push($service->networks());
|
||||||
@@ -41,16 +47,30 @@ function connectProxyToNetworks(Server $server)
|
|||||||
$networks->push($network);
|
$networks->push($network);
|
||||||
}
|
}
|
||||||
$networks = collect($networks)->flatten()->unique();
|
$networks = collect($networks)->flatten()->unique();
|
||||||
if ($networks->count() === 0) {
|
if ($server->isSwarm()) {
|
||||||
$networks = collect(['coolify']);
|
if ($networks->count() === 0) {
|
||||||
|
$networks = collect(['coolify-overlay']);
|
||||||
|
}
|
||||||
|
$commands = $networks->map(function ($network) {
|
||||||
|
return [
|
||||||
|
"echo 'Connecting coolify-proxy to $network network...'",
|
||||||
|
"docker network ls --format '{{.Name}}' | grep '^$network$' >/dev/null || docker network create --driver overlay --attachable $network >/dev/null",
|
||||||
|
"docker network connect $network coolify-proxy >/dev/null 2>&1 || true",
|
||||||
|
];
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
if ($networks->count() === 0) {
|
||||||
|
$networks = collect(['coolify']);
|
||||||
|
}
|
||||||
|
$commands = $networks->map(function ($network) {
|
||||||
|
return [
|
||||||
|
"echo 'Connecting coolify-proxy to $network network...'",
|
||||||
|
"docker network ls --format '{{.Name}}' | grep '^$network$' >/dev/null || docker network create --attachable $network >/dev/null",
|
||||||
|
"docker network connect $network coolify-proxy >/dev/null 2>&1 || true",
|
||||||
|
];
|
||||||
|
});
|
||||||
}
|
}
|
||||||
$commands = $networks->map(function ($network) {
|
|
||||||
return [
|
|
||||||
"echo 'Connecting coolify-proxy to $network network...'",
|
|
||||||
"docker network ls --format '{{.Name}}' | grep '^$network$' >/dev/null || docker network create --attachable $network >/dev/null",
|
|
||||||
"docker network connect $network coolify-proxy >/dev/null 2>&1 || true",
|
|
||||||
];
|
|
||||||
});
|
|
||||||
return $commands->flatten();
|
return $commands->flatten();
|
||||||
}
|
}
|
||||||
function generate_default_proxy_configuration(Server $server)
|
function generate_default_proxy_configuration(Server $server)
|
||||||
@@ -60,14 +80,18 @@ function generate_default_proxy_configuration(Server $server)
|
|||||||
$networks = collect($server->swarmDockers)->map(function ($docker) {
|
$networks = collect($server->swarmDockers)->map(function ($docker) {
|
||||||
return $docker['network'];
|
return $docker['network'];
|
||||||
})->unique();
|
})->unique();
|
||||||
|
if ($networks->count() === 0) {
|
||||||
|
$networks = collect(['coolify-overlay']);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
$networks = collect($server->standaloneDockers)->map(function ($docker) {
|
$networks = collect($server->standaloneDockers)->map(function ($docker) {
|
||||||
return $docker['network'];
|
return $docker['network'];
|
||||||
})->unique();
|
})->unique();
|
||||||
|
if ($networks->count() === 0) {
|
||||||
|
$networks = collect(['coolify']);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if ($networks->count() === 0) {
|
|
||||||
$networks = collect(['coolify']);
|
|
||||||
}
|
|
||||||
$array_of_networks = collect([]);
|
$array_of_networks = collect([]);
|
||||||
$networks->map(function ($network) use ($array_of_networks) {
|
$networks->map(function ($network) use ($array_of_networks) {
|
||||||
$array_of_networks[$network] = [
|
$array_of_networks[$network] = [
|
||||||
|
|||||||
@@ -1330,7 +1330,8 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
|
|||||||
if ($value?->startsWith('$')) {
|
if ($value?->startsWith('$')) {
|
||||||
$foundEnv = EnvironmentVariable::where([
|
$foundEnv = EnvironmentVariable::where([
|
||||||
'key' => $key,
|
'key' => $key,
|
||||||
'service_id' => $resource->id,
|
'application_id' => $resource->id,
|
||||||
|
'is_preview' => false,
|
||||||
])->first();
|
])->first();
|
||||||
$value = Str::of(replaceVariables($value));
|
$value = Str::of(replaceVariables($value));
|
||||||
$key = $value;
|
$key = $value;
|
||||||
@@ -1397,14 +1398,22 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
|
|||||||
$defaultValue = data_get($foundEnv, 'value');
|
$defaultValue = data_get($foundEnv, 'value');
|
||||||
}
|
}
|
||||||
$isBuildTime = data_get($foundEnv, 'is_build_time', false);
|
$isBuildTime = data_get($foundEnv, 'is_build_time', false);
|
||||||
EnvironmentVariable::updateOrCreate([
|
if ($foundEnv) {
|
||||||
'key' => $key->value(),
|
$foundEnv->update([
|
||||||
'application_id' => $resource->id,
|
'key' => $key,
|
||||||
], [
|
'application_id' => $resource->id,
|
||||||
'value' => $defaultValue,
|
'is_build_time' => $isBuildTime,
|
||||||
'is_build_time' => $isBuildTime,
|
'value' => $defaultValue,
|
||||||
'application_id' => $resource->id,
|
]);
|
||||||
]);
|
} else {
|
||||||
|
EnvironmentVariable::create([
|
||||||
|
'key' => $key,
|
||||||
|
'value' => $defaultValue,
|
||||||
|
'is_build_time' => $isBuildTime,
|
||||||
|
'application_id' => $resource->id,
|
||||||
|
'is_preview' => false,
|
||||||
|
]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
991
composer.lock
generated
991
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -216,7 +216,7 @@ return [
|
|||||||
],
|
],
|
||||||
'long-running' => [
|
'long-running' => [
|
||||||
'autoScalingStrategy' => 'size',
|
'autoScalingStrategy' => 'size',
|
||||||
'maxProcesses' => env('HORIZON_MAX_PROCESSES', 2),
|
'maxProcesses' => env('HORIZON_MAX_PROCESSES', 6),
|
||||||
'balanceMaxShift' => env('HORIZON_BALANCE_MAX_SHIFT', 1),
|
'balanceMaxShift' => env('HORIZON_BALANCE_MAX_SHIFT', 1),
|
||||||
'balanceCooldown' => env('HORIZON_BALANCE_COOLDOWN', 1),
|
'balanceCooldown' => env('HORIZON_BALANCE_COOLDOWN', 1),
|
||||||
],
|
],
|
||||||
@@ -231,7 +231,7 @@ return [
|
|||||||
],
|
],
|
||||||
'long-running' => [
|
'long-running' => [
|
||||||
'autoScalingStrategy' => 'size',
|
'autoScalingStrategy' => 'size',
|
||||||
'maxProcesses' => env('HORIZON_MAX_PROCESSES', 2),
|
'maxProcesses' => env('HORIZON_MAX_PROCESSES', 6),
|
||||||
'balanceMaxShift' => env('HORIZON_BALANCE_MAX_SHIFT', 1),
|
'balanceMaxShift' => env('HORIZON_BALANCE_MAX_SHIFT', 1),
|
||||||
'balanceCooldown' => env('HORIZON_BALANCE_COOLDOWN', 1),
|
'balanceCooldown' => env('HORIZON_BALANCE_COOLDOWN', 1),
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ return [
|
|||||||
|
|
||||||
// The release version of your application
|
// The release version of your application
|
||||||
// Example with dynamic git hash: trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD'))
|
// Example with dynamic git hash: trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD'))
|
||||||
'release' => '4.0.0-beta.164',
|
'release' => '4.0.0-beta.173',
|
||||||
// When left empty or `null` the Laravel environment will be used
|
// When left empty or `null` the Laravel environment will be used
|
||||||
'environment' => config('app.env'),
|
'environment' => config('app.env'),
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
return '4.0.0-beta.164';
|
return '4.0.0-beta.173';
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::table('applications', function (Blueprint $table) {
|
||||||
|
$table->string('docker_compose_custom_start_command')->nullable();
|
||||||
|
$table->string('docker_compose_custom_build_command')->nullable();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('applications', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('docker_compose_custom_start_command');
|
||||||
|
$table->dropColumn('docker_compose_custom_build_command');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::table('applications', function (Blueprint $table) {
|
||||||
|
$table->integer('swarm_replicas')->default(1);
|
||||||
|
$table->text('swarm_placement_constraints')->nullable();
|
||||||
|
});
|
||||||
|
Schema::table('application_settings', function (Blueprint $table) {
|
||||||
|
$table->boolean('is_swarm_only_worker_nodes')->default(true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('applications', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('swarm_replicas');
|
||||||
|
$table->dropColumn('swarm_placement_constraints');
|
||||||
|
});
|
||||||
|
Schema::table('application_settings', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('is_swarm_only_worker_nodes');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::table('servers', function (Blueprint $table) {
|
||||||
|
$table->integer('swarm_cluster')->nullable();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('servers', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('swarm_cluster');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -1,17 +1,19 @@
|
|||||||
<div class="navbar-main">
|
<div class="navbar-main">
|
||||||
<a wire:navigate class="{{ request()->routeIs('project.application.configuration') ? 'text-white' : '' }}"
|
<a class="{{ request()->routeIs('project.application.configuration') ? 'text-white' : '' }}"
|
||||||
href="{{ route('project.application.configuration', $parameters) }}">
|
href="{{ route('project.application.configuration', $parameters) }}">
|
||||||
<button>Configuration</button>
|
<button>Configuration</button>
|
||||||
</a>
|
</a>
|
||||||
<a wire:navigate class="{{ request()->routeIs('project.application.command') ? 'text-white' : '' }}"
|
@if (!$application->destination->server->isSwarm())
|
||||||
href="{{ route('project.application.command', $parameters) }}">
|
<a class="{{ request()->routeIs('project.application.command') ? 'text-white' : '' }}"
|
||||||
<button>Execute Command</button>
|
href="{{ route('project.application.command', $parameters) }}">
|
||||||
</a>
|
<button>Execute Command</button>
|
||||||
<a wire:navigate class="{{ request()->routeIs('project.application.logs') ? 'text-white' : '' }}"
|
</a>
|
||||||
|
@endif
|
||||||
|
<a class="{{ request()->routeIs('project.application.logs') ? 'text-white' : '' }}"
|
||||||
href="{{ route('project.application.logs', $parameters) }}">
|
href="{{ route('project.application.logs', $parameters) }}">
|
||||||
<button>Logs</button>
|
<button>Logs</button>
|
||||||
</a>
|
</a>
|
||||||
<a wire:navigate class="{{ request()->routeIs('project.application.deployments') ? 'text-white' : '' }}"
|
<a class="{{ request()->routeIs('project.application.deployments') ? 'text-white' : '' }}"
|
||||||
href="{{ route('project.application.deployments', $parameters) }}">
|
href="{{ route('project.application.deployments', $parameters) }}">
|
||||||
<button>Deployments</button>
|
<button>Deployments</button>
|
||||||
</a>
|
</a>
|
||||||
@@ -19,37 +21,54 @@
|
|||||||
<div class="flex-1"></div>
|
<div class="flex-1"></div>
|
||||||
@if ($application->build_pack === 'dockercompose' && is_null($application->docker_compose_raw))
|
@if ($application->build_pack === 'dockercompose' && is_null($application->docker_compose_raw))
|
||||||
<div>Please load a Compose file.</div>
|
<div>Please load a Compose file.</div>
|
||||||
@elseif ($application->destination->server->isSwarm() && str($application->docker_registry_image_name)->isEmpty())
|
|
||||||
Swarm Deployments requires a Docker Image in a Registry.
|
|
||||||
@else
|
@else
|
||||||
<x-applications.advanced :application="$application" />
|
@if (!$application->destination->server->isSwarm())
|
||||||
|
<x-applications.advanced :application="$application" />
|
||||||
|
@endif
|
||||||
@if ($application->status !== 'exited')
|
@if ($application->status !== 'exited')
|
||||||
<button title="With rolling update if possible" wire:click='deploy'
|
@if (!$application->destination->server->isSwarm())
|
||||||
class="flex items-center gap-2 cursor-pointer hover:text-white text-neutral-400">
|
<button title="With rolling update if possible" wire:click='deploy'
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 text-orange-400" viewBox="0 0 24 24"
|
|
||||||
stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round"
|
|
||||||
stroke-linejoin="round">
|
|
||||||
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
|
|
||||||
<path
|
|
||||||
d="M10.09 4.01l.496 -.495a2 2 0 0 1 2.828 0l7.071 7.07a2 2 0 0 1 0 2.83l-7.07 7.07a2 2 0 0 1 -2.83 0l-7.07 -7.07a2 2 0 0 1 0 -2.83l3.535 -3.535h-3.988">
|
|
||||||
</path>
|
|
||||||
<path d="M7.05 11.038v-3.988"></path>
|
|
||||||
</svg>
|
|
||||||
Redeploy
|
|
||||||
</button>
|
|
||||||
@if ($application->build_pack !== 'dockercompose')
|
|
||||||
<button title="Restart without rebuilding" wire:click='restart'
|
|
||||||
class="flex items-center gap-2 cursor-pointer hover:text-white text-neutral-400">
|
class="flex items-center gap-2 cursor-pointer hover:text-white text-neutral-400">
|
||||||
<svg class="w-5 h-5 text-warning" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 text-orange-400" viewBox="0 0 24 24"
|
||||||
<g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
|
stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round"
|
||||||
stroke-width="2">
|
stroke-linejoin="round">
|
||||||
<path d="M19.933 13.041a8 8 0 1 1-9.925-8.788c3.899-1 7.935 1.007 9.425 4.747" />
|
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
|
||||||
<path d="M20 4v5h-5" />
|
<path
|
||||||
</g>
|
d="M10.09 4.01l.496 -.495a2 2 0 0 1 2.828 0l7.071 7.07a2 2 0 0 1 0 2.83l-7.07 7.07a2 2 0 0 1 -2.83 0l-7.07 -7.07a2 2 0 0 1 0 -2.83l3.535 -3.535h-3.988">
|
||||||
|
</path>
|
||||||
|
<path d="M7.05 11.038v-3.988"></path>
|
||||||
</svg>
|
</svg>
|
||||||
Restart
|
Redeploy
|
||||||
</button>
|
</button>
|
||||||
@if (isDev())
|
@endif
|
||||||
|
@if ($application->build_pack !== 'dockercompose')
|
||||||
|
@if ($application->destination->server->isSwarm())
|
||||||
|
<button title="Redeploy Swarm Service (rolling update)" wire:click='deploy'
|
||||||
|
class="flex items-center gap-2 cursor-pointer hover:text-white text-neutral-400">
|
||||||
|
<svg class="w-5 h-5 text-warning" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
|
||||||
|
stroke-width="2">
|
||||||
|
<path d="M19.933 13.041a8 8 0 1 1-9.925-8.788c3.899-1 7.935 1.007 9.425 4.747" />
|
||||||
|
<path d="M20 4v5h-5" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
Update Service
|
||||||
|
</button>
|
||||||
|
@else
|
||||||
|
<button title="Restart without rebuilding" wire:click='restart'
|
||||||
|
class="flex items-center gap-2 cursor-pointer hover:text-white text-neutral-400">
|
||||||
|
<svg class="w-5 h-5 text-warning" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
|
||||||
|
stroke-width="2">
|
||||||
|
<path d="M19.933 13.041a8 8 0 1 1-9.925-8.788c3.899-1 7.935 1.007 9.425 4.747" />
|
||||||
|
<path d="M20 4v5h-5" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
Restart
|
||||||
|
</button>
|
||||||
|
@endif
|
||||||
|
{{-- @if (isDev())
|
||||||
<button title="Restart without rebuilding" wire:click='restartNew'
|
<button title="Restart without rebuilding" wire:click='restartNew'
|
||||||
class="flex items-center gap-2 cursor-pointer hover:text-white text-neutral-400">
|
class="flex items-center gap-2 cursor-pointer hover:text-white text-neutral-400">
|
||||||
<svg class="w-5 h-5 text-warning" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
<svg class="w-5 h-5 text-warning" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||||
@@ -61,7 +80,7 @@
|
|||||||
</svg>
|
</svg>
|
||||||
Restart (new)
|
Restart (new)
|
||||||
</button>
|
</button>
|
||||||
@endif
|
@endif --}}
|
||||||
@endif
|
@endif
|
||||||
<button wire:click='stop' class="flex items-center gap-2 cursor-pointer hover:text-white text-neutral-400">
|
<button wire:click='stop' class="flex items-center gap-2 cursor-pointer hover:text-white text-neutral-400">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 text-error" viewBox="0 0 24 24" stroke-width="2"
|
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 text-error" viewBox="0 0 24 24" stroke-width="2"
|
||||||
@@ -83,7 +102,7 @@
|
|||||||
</svg>
|
</svg>
|
||||||
Deploy
|
Deploy
|
||||||
</button>
|
</button>
|
||||||
@if (isDev())
|
{{-- @if (isDev())
|
||||||
<button wire:click='deployNew'
|
<button wire:click='deployNew'
|
||||||
class="flex items-center gap-2 cursor-pointer hover:text-white text-neutral-400">
|
class="flex items-center gap-2 cursor-pointer hover:text-white text-neutral-400">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 text-warning" viewBox="0 0 24 24"
|
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 text-warning" viewBox="0 0 24 24"
|
||||||
@@ -94,7 +113,7 @@
|
|||||||
</svg>
|
</svg>
|
||||||
Deploy (new)
|
Deploy (new)
|
||||||
</button>
|
</button>
|
||||||
@endif
|
@endif --}}
|
||||||
@endif
|
@endif
|
||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
<div class="navbar-main">
|
<div class="navbar-main">
|
||||||
<a wire:navigate class="{{ request()->routeIs('project.database.configuration') ? 'text-white' : '' }}"
|
<a class="{{ request()->routeIs('project.database.configuration') ? 'text-white' : '' }}"
|
||||||
href="{{ route('project.database.configuration', $parameters) }}">
|
href="{{ route('project.database.configuration', $parameters) }}">
|
||||||
<button>Configuration</button>
|
<button>Configuration</button>
|
||||||
</a>
|
</a>
|
||||||
<a wire:navigate class="{{ request()->routeIs('project.database.command') ? 'text-white' : '' }}"
|
<a class="{{ request()->routeIs('project.database.command') ? 'text-white' : '' }}"
|
||||||
href="{{ route('project.database.command', $parameters) }}">
|
href="{{ route('project.database.command', $parameters) }}">
|
||||||
<button>Execute Command</button>
|
<button>Execute Command</button>
|
||||||
</a>
|
</a>
|
||||||
<a wire:navigate class="{{ request()->routeIs('project.database.logs') ? 'text-white' : '' }}"
|
<a class="{{ request()->routeIs('project.database.logs') ? 'text-white' : '' }}"
|
||||||
href="{{ route('project.database.logs', $parameters) }}">
|
href="{{ route('project.database.logs', $parameters) }}">
|
||||||
<button>Logs</button>
|
<button>Logs</button>
|
||||||
</a>
|
</a>
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
$database->getMorphClass() === 'App\Models\StandaloneMongodb' ||
|
$database->getMorphClass() === 'App\Models\StandaloneMongodb' ||
|
||||||
$database->getMorphClass() === 'App\Models\StandaloneMysql' ||
|
$database->getMorphClass() === 'App\Models\StandaloneMysql' ||
|
||||||
$database->getMorphClass() === 'App\Models\StandaloneMariadb')
|
$database->getMorphClass() === 'App\Models\StandaloneMariadb')
|
||||||
<a wire:navigate class="{{ request()->routeIs('project.database.backups.all') ? 'text-white' : '' }}"
|
<a class="{{ request()->routeIs('project.database.backups.all') ? 'text-white' : '' }}"
|
||||||
href="{{ route('project.database.backups.all', $parameters) }}">
|
href="{{ route('project.database.backups.all', $parameters) }}">
|
||||||
<button>Backups</button>
|
<button>Backups</button>
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<label class="flex gap-4 px-0 cursor-pointer label">
|
<label class="flex gap-4 px-0 cursor-pointer label">
|
||||||
<span class="flex gap-2 label-text min-w-fit">
|
<span class="flex gap-2 label-text min-w-fit">
|
||||||
@if ($label)
|
@if ($label)
|
||||||
{{ $label }}
|
{!! $label !!}
|
||||||
@else
|
@else
|
||||||
{{ $id }}
|
{{ $id }}
|
||||||
@endif
|
@endif
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
class="transition rounded w-11 h-11" src="{{ asset('coolify-transparent.png') }}"></a>
|
class="transition rounded w-11 h-11" src="{{ asset('coolify-transparent.png') }}"></a>
|
||||||
<ul class="flex flex-col h-full gap-4 menu flex-nowrap">
|
<ul class="flex flex-col h-full gap-4 menu flex-nowrap">
|
||||||
<li title="Dashboard">
|
<li title="Dashboard">
|
||||||
<a wire:navigate class="hover:bg-transparent" href="/">
|
<a class="hover:bg-transparent" href="/">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="{{ request()->is('/') ? 'text-warning icon' : 'icon' }}"
|
<svg xmlns="http://www.w3.org/2000/svg" class="{{ request()->is('/') ? 'text-warning icon' : 'icon' }}"
|
||||||
fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li title="Servers">
|
<li title="Servers">
|
||||||
<a wire:navigate class="hover:bg-transparent" href="/servers">
|
<a class="hover:bg-transparent" href="/servers">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg"
|
<svg xmlns="http://www.w3.org/2000/svg"
|
||||||
class="{{ request()->is('server/*') || request()->is('servers') ? 'text-warning icon' : 'icon' }}"
|
class="{{ request()->is('server/*') || request()->is('servers') ? 'text-warning icon' : 'icon' }}"
|
||||||
viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round"
|
viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round"
|
||||||
@@ -28,7 +28,7 @@
|
|||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li title="Projects">
|
<li title="Projects">
|
||||||
<a wire:navigate class="hover:bg-transparent" href="/projects">
|
<a class="hover:bg-transparent" href="/projects">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg"
|
<svg xmlns="http://www.w3.org/2000/svg"
|
||||||
class="{{ request()->is('project/*') || request()->is('projects') ? 'text-warning icon' : 'icon' }}"
|
class="{{ request()->is('project/*') || request()->is('projects') ? 'text-warning icon' : 'icon' }}"
|
||||||
viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round"
|
viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round"
|
||||||
@@ -41,7 +41,7 @@
|
|||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li title="Command Center">
|
<li title="Command Center">
|
||||||
<a wire:navigate class="hover:bg-transparent" href="/command-center">
|
<a class="hover:bg-transparent" href="/command-center">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg"
|
<svg xmlns="http://www.w3.org/2000/svg"
|
||||||
class="{{ request()->is('command-center') ? 'text-warning icon' : 'icon' }}" viewBox="0 0 24 24"
|
class="{{ request()->is('command-center') ? 'text-warning icon' : 'icon' }}" viewBox="0 0 24 24"
|
||||||
stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round"
|
stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round"
|
||||||
@@ -53,7 +53,7 @@
|
|||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li title="Source">
|
<li title="Source">
|
||||||
<a wire:navigate class="hover:bg-transparent" href="{{ route('source.all') }}">
|
<a class="hover:bg-transparent" href="{{ route('source.all') }}">
|
||||||
<svg class="icon" viewBox="0 0 15 15" xmlns="http://www.w3.org/2000/svg">
|
<svg class="icon" viewBox="0 0 15 15" xmlns="http://www.w3.org/2000/svg">
|
||||||
<path fill="currentColor"
|
<path fill="currentColor"
|
||||||
d="m6.793 1.207l.353.354l-.353-.354ZM1.207 6.793l-.353-.354l.353.354Zm0 1.414l.354-.353l-.354.353Zm5.586 5.586l-.354.353l.354-.353Zm1.414 0l-.353-.354l.353.354Zm5.586-5.586l.353.354l-.353-.354Zm0-1.414l-.354.353l.354-.353ZM8.207 1.207l.354-.353l-.354.353ZM6.44.854L.854 6.439l.707.707l5.585-5.585L6.44.854ZM.854 8.56l5.585 5.585l.707-.707l-5.585-5.585l-.707.707Zm7.707 5.585l5.585-5.585l-.707-.707l-5.585 5.585l.707.707Zm5.585-7.707L8.561.854l-.707.707l5.585 5.585l.707-.707Zm0 2.122a1.5 1.5 0 0 0 0-2.122l-.707.707a.5.5 0 0 1 0 .708l.707.707ZM6.44 14.146a1.5 1.5 0 0 0 2.122 0l-.707-.707a.5.5 0 0 1-.708 0l-.707.707ZM.854 6.44a1.5 1.5 0 0 0 0 2.122l.707-.707a.5.5 0 0 1 0-.708L.854 6.44Zm6.292-4.878a.5.5 0 0 1 .708 0L8.56.854a1.5 1.5 0 0 0-2.122 0l.707.707Zm-2 1.293l1 1l.708-.708l-1-1l-.708.708ZM7.5 5a.5.5 0 0 1-.5-.5H6A1.5 1.5 0 0 0 7.5 6V5Zm.5-.5a.5.5 0 0 1-.5.5v1A1.5 1.5 0 0 0 9 4.5H8ZM7.5 4a.5.5 0 0 1 .5.5h1A1.5 1.5 0 0 0 7.5 3v1Zm0-1A1.5 1.5 0 0 0 6 4.5h1a.5.5 0 0 1 .5-.5V3Zm.646 2.854l1.5 1.5l.707-.708l-1.5-1.5l-.707.708ZM10.5 8a.5.5 0 0 1-.5-.5H9A1.5 1.5 0 0 0 10.5 9V8Zm.5-.5a.5.5 0 0 1-.5.5v1A1.5 1.5 0 0 0 12 7.5h-1Zm-.5-.5a.5.5 0 0 1 .5.5h1A1.5 1.5 0 0 0 10.5 6v1Zm0-1A1.5 1.5 0 0 0 9 7.5h1a.5.5 0 0 1 .5-.5V6ZM7 5.5v4h1v-4H7Zm.5 5.5a.5.5 0 0 1-.5-.5H6A1.5 1.5 0 0 0 7.5 12v-1Zm.5-.5a.5.5 0 0 1-.5.5v1A1.5 1.5 0 0 0 9 10.5H8Zm-.5-.5a.5.5 0 0 1 .5.5h1A1.5 1.5 0 0 0 7.5 9v1Zm0-1A1.5 1.5 0 0 0 6 10.5h1a.5.5 0 0 1 .5-.5V9Z" />
|
d="m6.793 1.207l.353.354l-.353-.354ZM1.207 6.793l-.353-.354l.353.354Zm0 1.414l.354-.353l-.354.353Zm5.586 5.586l-.354.353l.354-.353Zm1.414 0l-.353-.354l.353.354Zm5.586-5.586l.353.354l-.353-.354Zm0-1.414l-.354.353l.354-.353ZM8.207 1.207l.354-.353l-.354.353ZM6.44.854L.854 6.439l.707.707l5.585-5.585L6.44.854ZM.854 8.56l5.585 5.585l.707-.707l-5.585-5.585l-.707.707Zm7.707 5.585l5.585-5.585l-.707-.707l-5.585 5.585l.707.707Zm5.585-7.707L8.561.854l-.707.707l5.585 5.585l.707-.707Zm0 2.122a1.5 1.5 0 0 0 0-2.122l-.707.707a.5.5 0 0 1 0 .708l.707.707ZM6.44 14.146a1.5 1.5 0 0 0 2.122 0l-.707-.707a.5.5 0 0 1-.708 0l-.707.707ZM.854 6.44a1.5 1.5 0 0 0 0 2.122l.707-.707a.5.5 0 0 1 0-.708L.854 6.44Zm6.292-4.878a.5.5 0 0 1 .708 0L8.56.854a1.5 1.5 0 0 0-2.122 0l.707.707Zm-2 1.293l1 1l.708-.708l-1-1l-.708.708ZM7.5 5a.5.5 0 0 1-.5-.5H6A1.5 1.5 0 0 0 7.5 6V5Zm.5-.5a.5.5 0 0 1-.5.5v1A1.5 1.5 0 0 0 9 4.5H8ZM7.5 4a.5.5 0 0 1 .5.5h1A1.5 1.5 0 0 0 7.5 3v1Zm0-1A1.5 1.5 0 0 0 6 4.5h1a.5.5 0 0 1 .5-.5V3Zm.646 2.854l1.5 1.5l.707-.708l-1.5-1.5l-.707.708ZM10.5 8a.5.5 0 0 1-.5-.5H9A1.5 1.5 0 0 0 10.5 9V8Zm.5-.5a.5.5 0 0 1-.5.5v1A1.5 1.5 0 0 0 12 7.5h-1Zm-.5-.5a.5.5 0 0 1 .5.5h1A1.5 1.5 0 0 0 10.5 6v1Zm0-1A1.5 1.5 0 0 0 9 7.5h1a.5.5 0 0 1 .5-.5V6ZM7 5.5v4h1v-4H7Zm.5 5.5a.5.5 0 0 1-.5-.5H6A1.5 1.5 0 0 0 7.5 12v-1Zm.5-.5a.5.5 0 0 1-.5.5v1A1.5 1.5 0 0 0 9 10.5H8Zm-.5-.5a.5.5 0 0 1 .5.5h1A1.5 1.5 0 0 0 7.5 9v1Zm0-1A1.5 1.5 0 0 0 6 10.5h1a.5.5 0 0 1 .5-.5V9Z" />
|
||||||
@@ -61,7 +61,7 @@
|
|||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li title="Security">
|
<li title="Security">
|
||||||
<a wire:navigate class="hover:bg-transparent" href="{{ route('security.private-key.index') }}">
|
<a class="hover:bg-transparent" href="{{ route('security.private-key.index') }}">
|
||||||
<svg class="icon" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
<svg class="icon" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||||
<path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
|
<path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
|
||||||
stroke-width="2"
|
stroke-width="2"
|
||||||
@@ -70,7 +70,7 @@
|
|||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li title="Teams">
|
<li title="Teams">
|
||||||
<a wire:navigate class="hover:bg-transparent" href="{{ route('team.index') }}">
|
<a class="hover:bg-transparent" href="{{ route('team.index') }}">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="icon" viewBox="0 0 24 24" stroke-width="1.5"
|
<svg xmlns="http://www.w3.org/2000/svg" class="icon" viewBox="0 0 24 24" stroke-width="1.5"
|
||||||
stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
||||||
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
||||||
@@ -103,7 +103,7 @@
|
|||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li title="Profile">
|
<li title="Profile">
|
||||||
<a wire:navigate class="hover:bg-transparent" href="/profile">
|
<a class="hover:bg-transparent" href="/profile">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="icon" viewBox="0 0 24 24" stroke-width="1.5"
|
<svg xmlns="http://www.w3.org/2000/svg" class="icon" viewBox="0 0 24 24" stroke-width="1.5"
|
||||||
stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
||||||
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
||||||
@@ -116,7 +116,7 @@
|
|||||||
|
|
||||||
@if (isInstanceAdmin())
|
@if (isInstanceAdmin())
|
||||||
<li title="Settings" class="mt-auto">
|
<li title="Settings" class="mt-auto">
|
||||||
<a wire:navigate class="hover:bg-transparent" href="/settings">
|
<a class="hover:bg-transparent" href="/settings">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg"
|
<svg xmlns="http://www.w3.org/2000/svg"
|
||||||
class="{{ request()->is('settings*') ? 'text-warning icon' : 'icon' }}" viewBox="0 0 24 24"
|
class="{{ request()->is('settings*') ? 'text-warning icon' : 'icon' }}" viewBox="0 0 24 24"
|
||||||
stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round"
|
stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round"
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z"
|
d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z"
|
||||||
clip-rule="evenodd"></path>
|
clip-rule="evenodd"></path>
|
||||||
</svg>
|
</svg>
|
||||||
<a wire:navigate class="text-xs truncate lg:text-sm"
|
<a class="text-xs truncate lg:text-sm"
|
||||||
href="{{ route('project.resources', ['environment_name' => $this->parameters['environment_name'], 'project_uuid' => $this->parameters['project_uuid']]) }}">{{ $this->parameters['environment_name'] }}</a>
|
href="{{ route('project.resources', ['environment_name' => $this->parameters['environment_name'], 'project_uuid' => $this->parameters['project_uuid']]) }}">{{ $this->parameters['environment_name'] }}</a>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
|
|||||||
@@ -10,10 +10,10 @@
|
|||||||
</ol>
|
</ol>
|
||||||
</nav>
|
</nav>
|
||||||
<nav class="navbar-main">
|
<nav class="navbar-main">
|
||||||
<a wire:navigate href="{{ route('security.private-key.index') }}">
|
<a href="{{ route('security.private-key.index') }}">
|
||||||
<button>Private Keys</button>
|
<button>Private Keys</button>
|
||||||
</a>
|
</a>
|
||||||
<a wire:navigate href="{{ route('security.api-tokens') }}">
|
<a href="{{ route('security.api-tokens') }}">
|
||||||
<button>API tokens</button>
|
<button>API tokens</button>
|
||||||
</a>
|
</a>
|
||||||
<div class="flex-1"></div>
|
<div class="flex-1"></div>
|
||||||
|
|||||||
@@ -2,43 +2,48 @@
|
|||||||
<livewire:server.proxy.modal :server="$server" />
|
<livewire:server.proxy.modal :server="$server" />
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<h1>Server</h1>
|
<h1>Server</h1>
|
||||||
@if ($server->proxyType() !== 'NONE')
|
@if ($server->proxyType() !== 'NONE' && $server->isFunctional() && !$server->isSwarmWorker())
|
||||||
<livewire:server.proxy.status :server="$server" />
|
<livewire:server.proxy.status :server="$server" />
|
||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
<div class="subtitle ">{{ data_get($server, 'name') }}</div>
|
<div class="subtitle ">{{ data_get($server, 'name') }}</div>
|
||||||
<nav class="navbar-main">
|
<nav class="navbar-main">
|
||||||
<a wire:navigate class="{{ request()->routeIs('server.show') ? 'text-white' : '' }}"
|
<a class="{{ request()->routeIs('server.show') ? 'text-white' : '' }}"
|
||||||
href="{{ route('server.show', [
|
href="{{ route('server.show', [
|
||||||
'server_uuid' => data_get($parameters, 'server_uuid'),
|
'server_uuid' => data_get($parameters, 'server_uuid'),
|
||||||
]) }}">
|
]) }}">
|
||||||
<button>General</button>
|
<button>General</button>
|
||||||
</a>
|
</a>
|
||||||
<a wire:navigate class="{{ request()->routeIs('server.private-key') ? 'text-white' : '' }}"
|
<a class="{{ request()->routeIs('server.private-key') ? 'text-white' : '' }}"
|
||||||
href="{{ route('server.private-key', [
|
href="{{ route('server.private-key', [
|
||||||
'server_uuid' => data_get($parameters, 'server_uuid'),
|
'server_uuid' => data_get($parameters, 'server_uuid'),
|
||||||
]) }}">
|
]) }}">
|
||||||
<button>Private Key</button>
|
<button>Private Key</button>
|
||||||
</a>
|
</a>
|
||||||
<a wire:navigate class="{{ request()->routeIs('server.proxy') ? 'text-white' : '' }}"
|
@if (!$server->isSwarmWorker())
|
||||||
href="{{ route('server.proxy', [
|
<a class="{{ request()->routeIs('server.proxy') ? 'text-white' : '' }}"
|
||||||
'server_uuid' => data_get($parameters, 'server_uuid'),
|
href="{{ route('server.proxy', [
|
||||||
]) }}">
|
'server_uuid' => data_get($parameters, 'server_uuid'),
|
||||||
<button>Proxy</button>
|
]) }}">
|
||||||
</a>
|
<button>Proxy</button>
|
||||||
<a wire:navigate class="{{ request()->routeIs('server.destinations') ? 'text-white' : '' }}"
|
</a>
|
||||||
href="{{ route('server.destinations', [
|
<a class="{{ request()->routeIs('server.destinations') ? 'text-white' : '' }}"
|
||||||
'server_uuid' => data_get($parameters, 'server_uuid'),
|
href="{{ route('server.destinations', [
|
||||||
]) }}">
|
'server_uuid' => data_get($parameters, 'server_uuid'),
|
||||||
<button>Destinations</button>
|
]) }}">
|
||||||
</a>
|
<button>Destinations</button>
|
||||||
<a wire:navigate class="{{ request()->routeIs('server.log-drains') ? 'text-white' : '' }}"
|
</a>
|
||||||
href="{{ route('server.log-drains', [
|
<a class="{{ request()->routeIs('server.log-drains') ? 'text-white' : '' }}"
|
||||||
'server_uuid' => data_get($parameters, 'server_uuid'),
|
href="{{ route('server.log-drains', [
|
||||||
]) }}">
|
'server_uuid' => data_get($parameters, 'server_uuid'),
|
||||||
<button>Log Drains</button>
|
]) }}">
|
||||||
</a>
|
<button>Log Drains</button>
|
||||||
|
</a>
|
||||||
|
@endif
|
||||||
|
|
||||||
<div class="flex-1"></div>
|
<div class="flex-1"></div>
|
||||||
<livewire:server.proxy.deploy :server="$server" />
|
@if ($server->proxyType() !== 'NONE' && $server->isFunctional() && !$server->isSwarmWorker())
|
||||||
|
<livewire:server.proxy.deploy :server="$server" />
|
||||||
|
@endif
|
||||||
</nav>
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<div class="navbar-main" x-data>
|
<div class="navbar-main" x-data>
|
||||||
<a wire:navigate class="{{ request()->routeIs('project.service.configuration') ? 'text-white' : '' }}"
|
<a class="{{ request()->routeIs('project.service.configuration') ? 'text-white' : '' }}"
|
||||||
href="{{ route('project.service.configuration', $parameters) }}">
|
href="{{ route('project.service.configuration', $parameters) }}">
|
||||||
<button>Configuration</button>
|
<button>Configuration</button>
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
@if (Str::of($status)->startsWith('running'))
|
@if (Str::of($status)->startsWith('running'))
|
||||||
<x-status.running :status="$status" />
|
<x-status.running :status="$status" />
|
||||||
@elseif(Str::of($status)->startsWith('restarting'))
|
@elseif(Str::of($status)->startsWith('restarting') || Str::of($status)->startsWith('starting'))
|
||||||
<x-status.restarting :status="$status" />
|
<x-status.restarting :status="$status" />
|
||||||
@else
|
@else
|
||||||
<x-status.stopped :status="$status" />
|
<x-status.stopped :status="$status" />
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<div class="pb-6">
|
<div class="pb-6">
|
||||||
<div class="flex items-end gap-2">
|
<div class="flex items-end gap-2">
|
||||||
<h1>Team</h1>
|
<h1>Team</h1>
|
||||||
<a wire:navigate href="/team/new"><x-forms.button>+ New Team</x-forms.button></a>
|
<a href="/team/new"><x-forms.button>+ New Team</x-forms.button></a>
|
||||||
</div>
|
</div>
|
||||||
<nav class="flex pt-2 pb-10">
|
<nav class="flex pt-2 pb-10">
|
||||||
<ol class="inline-flex items-center">
|
<ol class="inline-flex items-center">
|
||||||
@@ -14,17 +14,17 @@
|
|||||||
</ol>
|
</ol>
|
||||||
</nav>
|
</nav>
|
||||||
<nav class="navbar-main">
|
<nav class="navbar-main">
|
||||||
<a wire:navigate class="{{ request()->routeIs('team.index') ? 'text-white' : '' }}" href="{{ route('team.index') }}">
|
<a class="{{ request()->routeIs('team.index') ? 'text-white' : '' }}" href="{{ route('team.index') }}">
|
||||||
<button>General</button>
|
<button>General</button>
|
||||||
</a>
|
</a>
|
||||||
<a wire:navigate class="{{ request()->routeIs('team.members') ? 'text-white' : '' }}" href="{{ route('team.members') }}">
|
<a class="{{ request()->routeIs('team.members') ? 'text-white' : '' }}" href="{{ route('team.members') }}">
|
||||||
<button>Members</button>
|
<button>Members</button>
|
||||||
</a>
|
</a>
|
||||||
<a wire:navigate class="{{ request()->routeIs('team.storages.all') ? 'text-white' : '' }}"
|
<a class="{{ request()->routeIs('team.storages.all') ? 'text-white' : '' }}"
|
||||||
href="{{ route('team.storages.all') }}">
|
href="{{ route('team.storages.all') }}">
|
||||||
<button>S3 Storages</button>
|
<button>S3 Storages</button>
|
||||||
</a>
|
</a>
|
||||||
<a wire:navigate class="{{ request()->routeIs('team.notifications') ? 'text-white' : '' }}"
|
<a class="{{ request()->routeIs('team.notifications') ? 'text-white' : '' }}"
|
||||||
href="{{ route('team.notifications') }}">
|
href="{{ route('team.notifications') }}">
|
||||||
<button>Notifications</button>
|
<button>Notifications</button>
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
<x-layout>
|
<x-layout>
|
||||||
<livewire:destination.new.standalone-docker :servers="$servers" :server_id="$server_id" />
|
<livewire:destination.new.docker :servers="$servers" :server_id="$server_id" />
|
||||||
</x-layout>
|
</x-layout>
|
||||||
|
|||||||
@@ -29,14 +29,14 @@
|
|||||||
@foreach ($projects as $project)
|
@foreach ($projects as $project)
|
||||||
<div class="gap-2 border border-transparent cursor-pointer box group">
|
<div class="gap-2 border border-transparent cursor-pointer box group">
|
||||||
@if (data_get($project, 'environments.0.name'))
|
@if (data_get($project, 'environments.0.name'))
|
||||||
<a wire:navigate class="flex flex-col flex-1 mx-6 hover:no-underline"
|
<a class="flex flex-col flex-1 mx-6 hover:no-underline"
|
||||||
href="{{ route('project.resources', ['project_uuid' => data_get($project, 'uuid'), 'environment_name' => data_get($project, 'environments.0.name', 'production')]) }}">
|
href="{{ route('project.resources', ['project_uuid' => data_get($project, 'uuid'), 'environment_name' => data_get($project, 'environments.0.name', 'production')]) }}">
|
||||||
<div class="font-bold text-white">{{ $project->name }}</div>
|
<div class="font-bold text-white">{{ $project->name }}</div>
|
||||||
<div class="description">
|
<div class="description">
|
||||||
{{ $project->description }}</div>
|
{{ $project->description }}</div>
|
||||||
</a>
|
</a>
|
||||||
@else
|
@else
|
||||||
<a wire:navigate class="flex flex-col flex-1 mx-6 hover:no-underline"
|
<a class="flex flex-col flex-1 mx-6 hover:no-underline"
|
||||||
href="{{ route('project.show', ['project_uuid' => data_get($project, 'uuid')]) }}">
|
href="{{ route('project.show', ['project_uuid' => data_get($project, 'uuid')]) }}">
|
||||||
<div class="font-bold text-white">{{ $project->name }}</div>
|
<div class="font-bold text-white">{{ $project->name }}</div>
|
||||||
<div class="description">
|
<div class="description">
|
||||||
@@ -44,11 +44,11 @@
|
|||||||
</a>
|
</a>
|
||||||
@endif
|
@endif
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<a wire:navigate class="mx-4 rounded group-hover:text-white hover:no-underline"
|
<a class="mx-4 rounded group-hover:text-white hover:no-underline"
|
||||||
href="{{ route('project.resources.new', ['project_uuid' => data_get($project, 'uuid'), 'environment_name' => data_get($project, 'environments.0.name', 'production')]) }}">
|
href="{{ route('project.resources.new', ['project_uuid' => data_get($project, 'uuid'), 'environment_name' => data_get($project, 'environments.0.name', 'production')]) }}">
|
||||||
<span class="font-bold hover:text-warning">+ New Resource</span>
|
<span class="font-bold hover:text-warning">+ New Resource</span>
|
||||||
</a>
|
</a>
|
||||||
<a wire:navigate class="mx-4 rounded group-hover:text-white"
|
<a class="mx-4 rounded group-hover:text-white"
|
||||||
href="{{ route('project.edit', ['project_uuid' => data_get($project, 'uuid')]) }}">
|
href="{{ route('project.edit', ['project_uuid' => data_get($project, 'uuid')]) }}">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="icon hover:text-warning" viewBox="0 0 24 24"
|
<svg xmlns="http://www.w3.org/2000/svg" class="icon hover:text-warning" viewBox="0 0 24 24"
|
||||||
stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round"
|
stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round"
|
||||||
@@ -72,7 +72,7 @@
|
|||||||
<div class="grid grid-cols-1 gap-2 xl:grid-cols-2">
|
<div class="grid grid-cols-1 gap-2 xl:grid-cols-2">
|
||||||
@endif
|
@endif
|
||||||
@foreach ($servers as $server)
|
@foreach ($servers as $server)
|
||||||
<a wire:navigate href="{{ route('server.show', ['server_uuid' => data_get($server, 'uuid')]) }}"
|
<a href="{{ route('server.show', ['server_uuid' => data_get($server, 'uuid')]) }}"
|
||||||
@class([
|
@class([
|
||||||
'gap-2 border cursor-pointer box group',
|
'gap-2 border cursor-pointer box group',
|
||||||
'border-transparent' => $server->settings->is_reachable,
|
'border-transparent' => $server->settings->is_reachable,
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
<option value="{{ $server->id }}">{{ $server->name }}</option>
|
<option value="{{ $server->id }}">{{ $server->name }}</option>
|
||||||
@endforeach
|
@endforeach
|
||||||
</x-forms.select>
|
</x-forms.select>
|
||||||
|
{{-- <x-forms.checkbox type="checkbox" id="is_swarm" label="Is it a Swarm network?" /> --}}
|
||||||
<x-forms.button type="submit">
|
<x-forms.button type="submit">
|
||||||
Save Destination
|
Save Destination
|
||||||
</x-forms.button>
|
</x-forms.button>
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
@if ($server->isFunctional())
|
@if ($server->isFunctional())
|
||||||
<div class="flex items-end gap-2">
|
<div class="flex items-end gap-2">
|
||||||
<h2>Destinations</h2>
|
<h2>Destinations</h2>
|
||||||
<a wire:navigate href="{{ route('destination.new', ['server_id' => $server->id]) }}">
|
<a href="{{ route('destination.new', ['server_id' => $server->id]) }}">
|
||||||
<x-forms.button>Add a new destination</x-forms.button>
|
<x-forms.button>Add a new destination</x-forms.button>
|
||||||
</a>
|
</a>
|
||||||
<x-forms.button wire:click='scan'>Scan destinations on the server</x-forms.button>
|
<x-forms.button wire:click='scan'>Scan destinations on the server</x-forms.button>
|
||||||
@@ -11,7 +11,16 @@
|
|||||||
<div class="flex gap-2 ">
|
<div class="flex gap-2 ">
|
||||||
Available for using:
|
Available for using:
|
||||||
@forelse ($server->standaloneDockers as $docker)
|
@forelse ($server->standaloneDockers as $docker)
|
||||||
<a wire:navigate href="{{ route('destination.show', ['destination_uuid' => data_get($docker, 'uuid')]) }}">
|
<a
|
||||||
|
href="{{ route('destination.show', ['destination_uuid' => data_get($docker, 'uuid')]) }}">
|
||||||
|
<button class="text-white btn-link">{{ data_get($docker, 'network') }} </button>
|
||||||
|
</a>
|
||||||
|
@empty
|
||||||
|
<div class="">N/A</div>
|
||||||
|
@endforelse
|
||||||
|
@forelse ($server->swarmDockers as $docker)
|
||||||
|
<a
|
||||||
|
href="{{ route('destination.show', ['destination_uuid' => data_get($docker, 'uuid')]) }}">
|
||||||
<button class="text-white btn-link">{{ data_get($docker, 'network') }} </button>
|
<button class="text-white btn-link">{{ data_get($docker, 'network') }} </button>
|
||||||
</a>
|
</a>
|
||||||
@empty
|
@empty
|
||||||
@@ -25,7 +34,7 @@
|
|||||||
<div class="flex flex-wrap gap-2 ">
|
<div class="flex flex-wrap gap-2 ">
|
||||||
@foreach ($networks as $network)
|
@foreach ($networks as $network)
|
||||||
<div>
|
<div>
|
||||||
<a wire:navigate
|
<a
|
||||||
href="{{ route('destination.new', ['server_id' => $server->id, 'network_name' => data_get($network, 'Name')]) }}">
|
href="{{ route('destination.new', ['server_id' => $server->id, 'network_name' => data_get($network, 'Name')]) }}">
|
||||||
<x-forms.button>+<x-highlighted text="{{ data_get($network, 'Name') }}" />
|
<x-forms.button>+<x-highlighted text="{{ data_get($network, 'Name') }}" />
|
||||||
</x-forms.button>
|
</x-forms.button>
|
||||||
|
|||||||
@@ -5,6 +5,11 @@
|
|||||||
<div class="flex flex-col gap-4 min-w-fit">
|
<div class="flex flex-col gap-4 min-w-fit">
|
||||||
<a :class="activeTab === 'general' && 'text-white'"
|
<a :class="activeTab === 'general' && 'text-white'"
|
||||||
@click.prevent="activeTab = 'general'; window.location.hash = 'general'" href="#">General</a>
|
@click.prevent="activeTab = 'general'; window.location.hash = 'general'" href="#">General</a>
|
||||||
|
@if ($application->destination->server->isSwarm())
|
||||||
|
<a :class="activeTab === 'swarm' && 'text-white'"
|
||||||
|
@click.prevent="activeTab = 'swarm'; window.location.hash = 'swarm'" href="#">Swarm
|
||||||
|
Configuration</a>
|
||||||
|
@endif
|
||||||
<a :class="activeTab === 'advanced' && 'text-white'"
|
<a :class="activeTab === 'advanced' && 'text-white'"
|
||||||
@click.prevent="activeTab = 'advanced'; window.location.hash = 'advanced'" href="#">Advanced</a>
|
@click.prevent="activeTab = 'advanced'; window.location.hash = 'advanced'" href="#">Advanced</a>
|
||||||
@if ($application->build_pack !== 'static')
|
@if ($application->build_pack !== 'static')
|
||||||
@@ -13,6 +18,7 @@
|
|||||||
href="#">Environment
|
href="#">Environment
|
||||||
Variables</a>
|
Variables</a>
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
@if ($application->git_based())
|
@if ($application->git_based())
|
||||||
<a :class="activeTab === 'source' && 'text-white'"
|
<a :class="activeTab === 'source' && 'text-white'"
|
||||||
@click.prevent="activeTab = 'source'; window.location.hash = 'source'" href="#">Source</a>
|
@click.prevent="activeTab = 'source'; window.location.hash = 'source'" href="#">Source</a>
|
||||||
@@ -56,6 +62,9 @@
|
|||||||
<div x-cloak x-show="activeTab === 'general'" class="h-full">
|
<div x-cloak x-show="activeTab === 'general'" class="h-full">
|
||||||
<livewire:project.application.general :application="$application" />
|
<livewire:project.application.general :application="$application" />
|
||||||
</div>
|
</div>
|
||||||
|
<div x-cloak x-show="activeTab === 'swarm'" class="h-full">
|
||||||
|
<livewire:project.application.swarm :application="$application" />
|
||||||
|
</div>
|
||||||
<div x-cloak x-show="activeTab === 'advanced'" class="h-full">
|
<div x-cloak x-show="activeTab === 'advanced'" class="h-full">
|
||||||
<livewire:project.application.advanced :application="$application" />
|
<livewire:project.application.advanced :application="$application" />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
<x-forms.button type="submit">Filter</x-forms.button>
|
<x-forms.button type="submit">Filter</x-forms.button>
|
||||||
</form>
|
</form>
|
||||||
@forelse ($deployments as $deployment)
|
@forelse ($deployments as $deployment)
|
||||||
<a wire:navigate @class([
|
<a @class([
|
||||||
'bg-coolgray-100 p-2 border-l border-dashed transition-colors hover:no-underline',
|
'bg-coolgray-100 p-2 border-l border-dashed transition-colors hover:no-underline',
|
||||||
'hover:bg-coolgray-200' => data_get($deployment, 'status') === 'queued',
|
'hover:bg-coolgray-200' => data_get($deployment, 'status') === 'queued',
|
||||||
'border-warning hover:bg-warning hover:text-black' =>
|
'border-warning hover:bg-warning hover:text-black' =>
|
||||||
|
|||||||
@@ -70,8 +70,11 @@
|
|||||||
@if ($application->build_pack !== 'dockercompose')
|
@if ($application->build_pack !== 'dockercompose')
|
||||||
<h3>Docker Registry</h3>
|
<h3>Docker Registry</h3>
|
||||||
@if ($application->destination->server->isSwarm())
|
@if ($application->destination->server->isSwarm())
|
||||||
<div>Docker Swarm requires the image to be available in a registry. More info <a class="underline"
|
@if ($application->build_pack !== 'dockerimage')
|
||||||
href="https://coolify.io/docs/docker-registries" target="_blank">here</a>.</div>
|
<div>Docker Swarm requires the image to be available in a registry. More info <a
|
||||||
|
class="underline" href="https://coolify.io/docs/docker-registries"
|
||||||
|
target="_blank">here</a>.</div>
|
||||||
|
@endif
|
||||||
@else
|
@else
|
||||||
@if ($application->build_pack !== 'dockerimage')
|
@if ($application->build_pack !== 'dockerimage')
|
||||||
<div>Push the built image to a docker registry. More info <a class="underline"
|
<div>Push the built image to a docker registry. More info <a class="underline"
|
||||||
@@ -126,37 +129,58 @@
|
|||||||
</div>
|
</div>
|
||||||
@endif
|
@endif
|
||||||
@endif
|
@endif
|
||||||
<div class="flex flex-col gap-2 xl:flex-row">
|
@if ($application->build_pack === 'dockercompose')
|
||||||
<x-forms.input placeholder="/" id="application.base_directory" label="Base Directory"
|
<div class="flex flex-col gap-2" wire:init='loadComposeFile(true)'>
|
||||||
helper="Directory to use as root. Useful for monorepos." />
|
<div class="flex gap-2">
|
||||||
@if ($application->build_pack === 'dockerfile' && !$application->dockerfile)
|
<x-forms.input placeholder="/" id="application.base_directory" label="Base Directory"
|
||||||
<x-forms.input placeholder="/Dockerfile" id="application.dockerfile_location"
|
helper="Directory to use as root. Useful for monorepos." />
|
||||||
label="Dockerfile Location"
|
<x-forms.input placeholder="/docker-compose.yaml" id="application.docker_compose_location"
|
||||||
helper="It is calculated together with the Base Directory:<br><span class='text-warning'>{{ Str::start($application->base_directory . $application->dockerfile_location, '/') }}</span>" />
|
label="Docker Compose Location"
|
||||||
@endif
|
helper="It is calculated together with the Base Directory:<br><span class='text-warning'>{{ Str::start($application->base_directory . $application->docker_compose_location, '/') }}</span>" />
|
||||||
@if ($application->build_pack === 'dockercompose')
|
</div>
|
||||||
<span wire:init='loadComposeFile(true)'></span>
|
<div class="pt-4">The following commands are for advanced use cases. Only modify them if you
|
||||||
<x-forms.input placeholder="/docker-compose.yaml" id="application.docker_compose_location"
|
know what are
|
||||||
label="Docker Compose Location"
|
you doing.</div>
|
||||||
helper="It is calculated together with the Base Directory:<br><span class='text-warning'>{{ Str::start($application->base_directory . $application->docker_compose_location, '/') }}</span>" />
|
<div class="flex gap-2">
|
||||||
{{-- <x-forms.input placeholder="/docker-compose.yaml" id="application.docker_compose_pr_location"
|
<x-forms.input placeholder="docker compose build"
|
||||||
label="Docker Compose Location For Pull Requests"
|
id="application.docker_compose_custom_build_command"
|
||||||
helper="It is calculated together with the Base Directory:<br><span class='text-warning'>{{ Str::start($application->base_directory . $application->docker_compose_pr_location, '/') }}</span>" /> --}}
|
helper="If you use this, you need to specify paths relatively and should use the same compose file in the custom command, otherwise the automatically configured labels / etc won't work.<br><br>So in your case, use: <span class='text-warning'>docker compose -f .{{ Str::start($application->base_directory . $application->docker_compose_location, '/') }} build</span>"
|
||||||
@endif
|
label="Custom Build Command" />
|
||||||
@if ($application->build_pack === 'dockerfile')
|
<x-forms.input placeholder="docker compose up -d"
|
||||||
<x-forms.input id="application.dockerfile_target_build" label="Docker Build Stage Target"
|
id="application.docker_compose_custom_start_command"
|
||||||
helper="Useful if you have multi-staged dockerfile." />
|
helper="If you use this, you need to specify paths relatively and should use the same compose file in the custom command, otherwise the automatically configured labels / etc won't work.<br><br>So in your case, use: <span class='text-warning'>docker compose -f .{{ Str::start($application->base_directory . $application->docker_compose_location, '/') }} up -d</span>"
|
||||||
@endif
|
label="Custom Start Command" />
|
||||||
@if ($application->could_set_build_commands())
|
{{-- <x-forms.input placeholder="/docker-compose.yaml" id="application.docker_compose_pr_location"
|
||||||
@if ($application->settings->is_static)
|
label="Docker Compose Location For Pull Requests"
|
||||||
<x-forms.input placeholder="/dist" id="application.publish_directory"
|
helper="It is calculated together with the Base Directory:<br><span class='text-warning'>{{ Str::start($application->base_directory . $application->docker_compose_pr_location, '/') }}</span>" /> --}}
|
||||||
label="Publish Directory" required />
|
</div>
|
||||||
@else
|
</div>
|
||||||
<x-forms.input placeholder="/" id="application.publish_directory"
|
@else
|
||||||
label="Publish Directory" />
|
<div class="flex flex-col gap-2 xl:flex-row">
|
||||||
|
<x-forms.input placeholder="/" id="application.base_directory" label="Base Directory"
|
||||||
|
helper="Directory to use as root. Useful for monorepos." />
|
||||||
|
@if ($application->build_pack === 'dockerfile' && !$application->dockerfile)
|
||||||
|
<x-forms.input placeholder="/Dockerfile" id="application.dockerfile_location"
|
||||||
|
label="Dockerfile Location"
|
||||||
|
helper="It is calculated together with the Base Directory:<br><span class='text-warning'>{{ Str::start($application->base_directory . $application->dockerfile_location, '/') }}</span>" />
|
||||||
@endif
|
@endif
|
||||||
@endif
|
|
||||||
</div>
|
@if ($application->build_pack === 'dockerfile')
|
||||||
|
<x-forms.input id="application.dockerfile_target_build" label="Docker Build Stage Target"
|
||||||
|
helper="Useful if you have multi-staged dockerfile." />
|
||||||
|
@endif
|
||||||
|
@if ($application->could_set_build_commands())
|
||||||
|
@if ($application->settings->is_static)
|
||||||
|
<x-forms.input placeholder="/dist" id="application.publish_directory"
|
||||||
|
label="Publish Directory" required />
|
||||||
|
@else
|
||||||
|
<x-forms.input placeholder="/" id="application.publish_directory"
|
||||||
|
label="Publish Directory" />
|
||||||
|
@endif
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
@endif
|
@endif
|
||||||
@if ($application->build_pack === 'dockercompose')
|
@if ($application->build_pack === 'dockercompose')
|
||||||
<x-forms.button wire:click="loadComposeFile">Reload Compose File</x-forms.button>
|
<x-forms.button wire:click="loadComposeFile">Reload Compose File</x-forms.button>
|
||||||
@@ -179,8 +203,10 @@
|
|||||||
required
|
required
|
||||||
helper="A comma separated list of ports your application uses. The first port will be used as default healthcheck port if nothing defined in the Healthcheck menu. Be sure to set this correctly." />
|
helper="A comma separated list of ports your application uses. The first port will be used as default healthcheck port if nothing defined in the Healthcheck menu. Be sure to set this correctly." />
|
||||||
@endif
|
@endif
|
||||||
<x-forms.input placeholder="3000:3000" id="application.ports_mappings" label="Ports Mappings"
|
@if (!$application->destination->server->isSwarm())
|
||||||
helper="A comma separated list of ports you would like to map to the host system. Useful when you do not want to use domains.<br><br><span class='inline-block font-bold text-warning'>Example:</span><br>3000:3000,3002:3002<br><br>Rolling update is not supported if you have a port mapped to the host." />
|
<x-forms.input placeholder="3000:3000" id="application.ports_mappings" label="Ports Mappings"
|
||||||
|
helper="A comma separated list of ports you would like to map to the host system. Useful when you do not want to use domains.<br><br><span class='inline-block font-bold text-warning'>Example:</span><br>3000:3000,3002:3002<br><br>Rolling update is not supported if you have a port mapped to the host." />
|
||||||
|
@endif
|
||||||
</div>
|
</div>
|
||||||
<x-forms.textarea label="Container Labels" rows="15" id="customLabels"></x-forms.textarea>
|
<x-forms.textarea label="Container Labels" rows="15" id="customLabels"></x-forms.textarea>
|
||||||
<x-forms.button wire:click="resetDefaultLabels">Reset to Coolify Generated Labels</x-forms.button>
|
<x-forms.button wire:click="resetDefaultLabels">Reset to Coolify Generated Labels</x-forms.button>
|
||||||
|
|||||||
24
resources/views/livewire/project/application/swarm.blade.php
Normal file
24
resources/views/livewire/project/application/swarm.blade.php
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
<div>
|
||||||
|
<form wire:submit='submit' class="flex flex-col">
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<h2>Swarm Configuration</h2>
|
||||||
|
<x-forms.button type="submit">
|
||||||
|
Save
|
||||||
|
</x-forms.button>
|
||||||
|
</div>
|
||||||
|
{{-- <div>Advanced Swarm Configuration</div> --}}
|
||||||
|
<div class="flex flex-col gap-2 py-4">
|
||||||
|
<div class="flex flex-col items-end gap-2 xl:flex-row">
|
||||||
|
<x-forms.input id="application.swarm_replicas" label="Replicas" required />
|
||||||
|
<x-forms.checkbox instantSave helper="If turned off, this resource will start on manager nodes too."
|
||||||
|
id="application.settings.is_swarm_only_worker_nodes" label="Only Start on Worker nodes" />
|
||||||
|
</div>
|
||||||
|
<x-forms.textarea id="swarm_placement_constraints" rows="7"
|
||||||
|
label="Custom Placement Constraints"
|
||||||
|
placeholder="placement:
|
||||||
|
constraints:
|
||||||
|
- 'node.role == worker'" />
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
</div>
|
||||||
@@ -202,6 +202,18 @@
|
|||||||
<li class="step step-secondary">Select a Server</li>
|
<li class="step step-secondary">Select a Server</li>
|
||||||
<li class="step">Select a Destination</li>
|
<li class="step">Select a Destination</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
@if ($isDatabase)
|
||||||
|
<div class="text-center">Swarm clusters are excluded from this type of resource at the moment. It will
|
||||||
|
be activated soon. Stay tuned.</div>
|
||||||
|
@endif
|
||||||
|
{{-- @if ($isDatabase)
|
||||||
|
<div class="flex items-center justify-center pt-4">
|
||||||
|
<x-forms.checkbox instantSave wire:model="includeSwarm"
|
||||||
|
helper="Swarm clusters are excluded from this list by default. For database, services or complex compose deployments with databases to work with Swarm,
|
||||||
|
you need to set a few things on the server. Read more <a class='text-white underline' href='https://coolify.io/docs/swarm#database-requirements' target='_blank'>here</a>."
|
||||||
|
label="Include Swarm Clusters" />
|
||||||
|
</div>
|
||||||
|
@endif --}}
|
||||||
<div class="flex flex-col justify-center gap-2 text-left xl:flex-row xl:flex-wrap">
|
<div class="flex flex-col justify-center gap-2 text-left xl:flex-row xl:flex-wrap">
|
||||||
@forelse($servers as $server)
|
@forelse($servers as $server)
|
||||||
<div class="box group" wire:click="setServer({{ $server }})">
|
<div class="box group" wire:click="setServer({{ $server }})">
|
||||||
@@ -232,28 +244,30 @@
|
|||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<div class="flex flex-col justify-center gap-2 text-left xl:flex-row xl:flex-wrap">
|
<div class="flex flex-col justify-center gap-2 text-left xl:flex-row xl:flex-wrap">
|
||||||
|
@if ($server->isSwarm())
|
||||||
@foreach ($standaloneDockers as $standaloneDocker)
|
@foreach ($swarmDockers as $swarmDocker)
|
||||||
<div class="box group" wire:click="setDestination('{{ $standaloneDocker->uuid }}')">
|
<div class="box group" wire:click="setDestination('{{ $swarmDocker->uuid }}')">
|
||||||
<div class="flex flex-col mx-6">
|
<div class="flex flex-col mx-6">
|
||||||
<div class="font-bold group-hover:text-white">
|
<div class="font-bold group-hover:text-white">
|
||||||
Standalone Docker <span class="text-xs">({{ $standaloneDocker->name }})</span>
|
Swarm Docker <span class="text-xs">({{ $swarmDocker->name }})</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="text-xs group-hover:text-white">
|
|
||||||
network: {{ $standaloneDocker->network }}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
@endforeach
|
|
||||||
@foreach ($swarmDockers as $swarmDocker)
|
|
||||||
<div class="box group" wire:click="setDestination('{{ $swarmDocker->uuid }}')">
|
|
||||||
<div class="flex flex-col mx-6">
|
|
||||||
<div class="font-bold group-hover:text-white">
|
|
||||||
Swarm Docker <span class="text-xs">({{ $swarmDocker->name }})</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
@endforeach
|
||||||
@endforeach
|
@else
|
||||||
<a wire:navigate href="{{ route('destination.new', ['server_id' => $server_id]) }}"
|
@foreach ($standaloneDockers as $standaloneDocker)
|
||||||
|
<div class="box group" wire:click="setDestination('{{ $standaloneDocker->uuid }}')">
|
||||||
|
<div class="flex flex-col mx-6">
|
||||||
|
<div class="font-bold group-hover:text-white">
|
||||||
|
Standalone Docker <span class="text-xs">({{ $standaloneDocker->name }})</span>
|
||||||
|
</div>
|
||||||
|
<div class="text-xs group-hover:text-white">
|
||||||
|
network: {{ $standaloneDocker->network }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endforeach
|
||||||
|
@endif
|
||||||
|
<a href="{{ route('destination.new', ['server_id' => $server_id]) }}"
|
||||||
class="items-center justify-center pb-10 text-center box-without-bg group bg-coollabs hover:bg-coollabs-100">
|
class="items-center justify-center pb-10 text-center box-without-bg group bg-coollabs hover:bg-coollabs-100">
|
||||||
<div class="flex flex-col mx-6 ">
|
<div class="flex flex-col mx-6 ">
|
||||||
<div class="font-bold text-white">
|
<div class="font-bold text-white">
|
||||||
|
|||||||
@@ -68,7 +68,7 @@
|
|||||||
<div class="text-xs">{{ $application->status }}</div>
|
<div class="text-xs">{{ $application->status }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center px-4">
|
<div class="flex items-center px-4">
|
||||||
<a wire:navigate
|
<a
|
||||||
class="flex flex-col flex-1 group-hover:text-white hover:no-underline"
|
class="flex flex-col flex-1 group-hover:text-white hover:no-underline"
|
||||||
href="{{ route('project.service.show', [...$parameters, 'service_name' => $application->name]) }}">
|
href="{{ route('project.service.show', [...$parameters, 'service_name' => $application->name]) }}">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="icon hover:text-warning"
|
<svg xmlns="http://www.w3.org/2000/svg" class="icon hover:text-warning"
|
||||||
@@ -115,7 +115,7 @@
|
|||||||
<div class="text-xs">{{ $database->status }}</div>
|
<div class="text-xs">{{ $database->status }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center px-4">
|
<div class="flex items-center px-4">
|
||||||
<a wire:navigate
|
<a
|
||||||
class="flex flex-col flex-1 group-hover:text-white hover:no-underline"
|
class="flex flex-col flex-1 group-hover:text-white hover:no-underline"
|
||||||
href="{{ route('project.service.show', [...$parameters, 'service_name' => $database->name]) }}">
|
href="{{ route('project.service.show', [...$parameters, 'service_name' => $database->name]) }}">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="icon hover:text-warning"
|
<svg xmlns="http://www.w3.org/2000/svg" class="icon hover:text-warning"
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<livewire:project.service.navbar :service="$service" :parameters="$parameters" :query="$query" />
|
<livewire:project.service.navbar :service="$service" :parameters="$parameters" :query="$query" />
|
||||||
<div class="flex h-full pt-6">
|
<div class="flex h-full pt-6">
|
||||||
<div class="flex flex-col gap-4 min-w-fit">
|
<div class="flex flex-col gap-4 min-w-fit">
|
||||||
<a wire:navigate class="{{ request()->routeIs('project.service.configuration') ? 'text-white' : '' }}"
|
<a class="{{ request()->routeIs('project.service.configuration') ? 'text-white' : '' }}"
|
||||||
href="{{ route('project.service.configuration', [...$parameters, 'service_name' => null]) }}">
|
href="{{ route('project.service.configuration', [...$parameters, 'service_name' => null]) }}">
|
||||||
<button><- Back</button>
|
<button><- Back</button>
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user