mirror of
https://github.com/ershisan99/coolify.git
synced 2025-12-24 12:33:17 +00:00
Compare commits
188 Commits
v4.0.0-bet
...
v4.0.0-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dc7f56b7aa | ||
|
|
2295ddddee | ||
|
|
3328d20b2d | ||
|
|
d444211c68 | ||
|
|
2de8cd1d6f | ||
|
|
115813ac44 | ||
|
|
7c0e8d9421 | ||
|
|
1c349cf55c | ||
|
|
7b4d8a8f05 | ||
|
|
6e7e2de490 | ||
|
|
0a9a92ee7d | ||
|
|
3bfac7ef3b | ||
|
|
1581e0e439 | ||
|
|
6773848874 | ||
|
|
3e9219173c | ||
|
|
d46a565e6c | ||
|
|
6267804d38 | ||
|
|
25c4d282ce | ||
|
|
45281360d5 | ||
|
|
7a32b8d1d2 | ||
|
|
7b0018b661 | ||
|
|
9032879e20 | ||
|
|
8b668cf8b7 | ||
|
|
3323533fd0 | ||
|
|
50e96baea1 | ||
|
|
2ac92df9d3 | ||
|
|
63e64cea60 | ||
|
|
0267c76de2 | ||
|
|
2382a10bba | ||
|
|
a8db40e99a | ||
|
|
8553fffffe | ||
|
|
d9d38a27ff | ||
|
|
1e7a5562ab | ||
|
|
e71032a8fc | ||
|
|
484a3dbe5c | ||
|
|
9c7f40e4fe | ||
|
|
f35b7ab6f4 | ||
|
|
a38dd3d5c8 | ||
|
|
00ad35fc3e | ||
|
|
0d78b44c80 | ||
|
|
fb614637a9 | ||
|
|
d0482d66c2 | ||
|
|
ef178dfd24 | ||
|
|
6cdc6e0780 | ||
|
|
c961e6d9c7 | ||
|
|
c6844ff47a | ||
|
|
605a630411 | ||
|
|
2b75cf1508 | ||
|
|
007054301b | ||
|
|
cf01d32237 | ||
|
|
5ec553f68d | ||
|
|
d95e5a169d | ||
|
|
8debefddad | ||
|
|
bd25860ccf | ||
|
|
b21cb5c0e9 | ||
|
|
ff79a2d3f4 | ||
|
|
9ae3743a58 | ||
|
|
ffcdbcc802 | ||
|
|
3ff78a3a47 | ||
|
|
4d78ac4789 | ||
|
|
be24f2d520 | ||
|
|
ff7fccb6a2 | ||
|
|
ee5a2b3c38 | ||
|
|
72b9001447 | ||
|
|
353245bb7d | ||
|
|
f9411bf0ed | ||
|
|
0eedbd2aa1 | ||
|
|
5e36c37838 | ||
|
|
35c76c8e2a | ||
|
|
90ad46b7c5 | ||
|
|
1c06e1e2a4 | ||
|
|
15d2c0e436 | ||
|
|
9984aea42f | ||
|
|
a9cc5cc351 | ||
|
|
a6cbabfba5 | ||
|
|
1ef85242ec | ||
|
|
ffe75b0856 | ||
|
|
25e3c4fcdc | ||
|
|
a922497f5d | ||
|
|
acb100908b | ||
|
|
7fa2cbc746 | ||
|
|
b36491af8e | ||
|
|
ea4fe81cb2 | ||
|
|
2191f1b826 | ||
|
|
08f6367752 | ||
|
|
e1b09f4844 | ||
|
|
884c46b054 | ||
|
|
d1cde123dc | ||
|
|
5eaded7e3a | ||
|
|
4ae4e88800 | ||
|
|
85fb27a631 | ||
|
|
8bf0561009 | ||
|
|
ddfded048c | ||
|
|
a2e889587e | ||
|
|
b63aaad645 | ||
|
|
2c41b5d4fb | ||
|
|
34aa4c6412 | ||
|
|
82372ea252 | ||
|
|
1f4440bcf9 | ||
|
|
31fcff4afc | ||
|
|
082f17f940 | ||
|
|
aa2ac3865c | ||
|
|
db8ffe50ac | ||
|
|
8fe658bacc | ||
|
|
c71d5e2dfb | ||
|
|
36c31dcd67 | ||
|
|
c223408c3c | ||
|
|
3cdccb49ef | ||
|
|
0844645a8b | ||
|
|
30bfad455c | ||
|
|
d84fdc3cd5 | ||
|
|
2b64b9de63 | ||
|
|
cdb3d863db | ||
|
|
74e3524e92 | ||
|
|
d31f75d1ec | ||
|
|
0b34207148 | ||
|
|
3c2beded68 | ||
|
|
f33fdb3bfd | ||
|
|
96a0f29f19 | ||
|
|
49b3a75a8b | ||
|
|
f13fc737f1 | ||
|
|
b7121c5000 | ||
|
|
22a1d3882e | ||
|
|
82f74e2264 | ||
|
|
3dd5699cde | ||
|
|
a198bfc5c0 | ||
|
|
132807b55d | ||
|
|
735081af50 | ||
|
|
31651aeaab | ||
|
|
7f4230d026 | ||
|
|
6333d3fd13 | ||
|
|
fd9dae6e4b | ||
|
|
db5d7857c8 | ||
|
|
0f4eab3cf2 | ||
|
|
75b9f4fcbf | ||
|
|
b9b58b8985 | ||
|
|
64b8aa1c01 | ||
|
|
6b21dc132d | ||
|
|
7aca4930db | ||
|
|
7a92ecfa30 | ||
|
|
171f6f4608 | ||
|
|
d569c8d31f | ||
|
|
51d716253f | ||
|
|
971b17b364 | ||
|
|
9616d858cf | ||
|
|
db5ff7f16d | ||
|
|
5db045f392 | ||
|
|
42c143d19e | ||
|
|
7c1948ebd9 | ||
|
|
9cd15645a2 | ||
|
|
c0a4a5c2f5 | ||
|
|
518004afbc | ||
|
|
833a4b9367 | ||
|
|
6aa82724b4 | ||
|
|
cfbee40ecd | ||
|
|
79d589c7a9 | ||
|
|
6b82fc3011 | ||
|
|
d921456036 | ||
|
|
6cc93250b8 | ||
|
|
39082541ff | ||
|
|
8cff40fdd4 | ||
|
|
a777db1234 | ||
|
|
3fca169096 | ||
|
|
58b451f616 | ||
|
|
f9b82f711f | ||
|
|
d92b5db320 | ||
|
|
a164e4bf3a | ||
|
|
be3cbd9e21 | ||
|
|
43fed96af1 | ||
|
|
3d3d31ef29 | ||
|
|
d51e70bcaa | ||
|
|
32f4c6c982 | ||
|
|
8f47761200 | ||
|
|
342ebecef2 | ||
|
|
220a8fe2cc | ||
|
|
d9389c91ee | ||
|
|
480cb00098 | ||
|
|
07ed550dc2 | ||
|
|
6045870398 | ||
|
|
2bf102cdf1 | ||
|
|
889a5b2bce | ||
|
|
df964a094b | ||
|
|
e395d4ecee | ||
|
|
d077e0c83c | ||
|
|
d931241edc | ||
|
|
647376ab3f | ||
|
|
64a9a72457 | ||
|
|
a8417aca16 |
2
.github/ISSUE_TEMPLATE/BUG_REPORT.yml
vendored
2
.github/ISSUE_TEMPLATE/BUG_REPORT.yml
vendored
@@ -21,6 +21,6 @@ body:
|
||||
- type: input
|
||||
attributes:
|
||||
label: Version
|
||||
description: Coolify's version (see bottom left corner).
|
||||
description: Coolify's version (see top of your screen).
|
||||
validations:
|
||||
required: true
|
||||
|
||||
20
.github/workflows/coolify-helper-next.yml
vendored
20
.github/workflows/coolify-helper-next.yml
vendored
@@ -18,15 +18,15 @@ jobs:
|
||||
contents: read
|
||||
packages: write
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Login to ghcr.io
|
||||
uses: docker/login-action@v2
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Build image and push to registry
|
||||
uses: docker/build-push-action@v3
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
no-cache: true
|
||||
context: .
|
||||
@@ -40,15 +40,15 @@ jobs:
|
||||
contents: read
|
||||
packages: write
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Login to ghcr.io
|
||||
uses: docker/login-action@v2
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Build image and push to registry
|
||||
uses: docker/build-push-action@v3
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
no-cache: true
|
||||
context: .
|
||||
@@ -64,13 +64,13 @@ jobs:
|
||||
needs: [ amd64, aarch64 ]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2
|
||||
uses: docker/setup-qemu-action@v3
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
uses: docker/setup-buildx-action@v3
|
||||
- name: Login to ghcr.io
|
||||
uses: docker/login-action@v2
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
|
||||
20
.github/workflows/coolify-helper.yml
vendored
20
.github/workflows/coolify-helper.yml
vendored
@@ -18,15 +18,15 @@ jobs:
|
||||
contents: read
|
||||
packages: write
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Login to ghcr.io
|
||||
uses: docker/login-action@v2
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Build image and push to registry
|
||||
uses: docker/build-push-action@v3
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
no-cache: true
|
||||
context: .
|
||||
@@ -40,15 +40,15 @@ jobs:
|
||||
contents: read
|
||||
packages: write
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Login to ghcr.io
|
||||
uses: docker/login-action@v2
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Build image and push to registry
|
||||
uses: docker/build-push-action@v3
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
no-cache: true
|
||||
context: .
|
||||
@@ -64,13 +64,13 @@ jobs:
|
||||
needs: [ amd64, aarch64 ]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2
|
||||
uses: docker/setup-qemu-action@v3
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
uses: docker/setup-buildx-action@v3
|
||||
- name: Login to ghcr.io
|
||||
uses: docker/login-action@v2
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
|
||||
20
.github/workflows/coolify-testing-host.yml
vendored
20
.github/workflows/coolify-testing-host.yml
vendored
@@ -18,15 +18,15 @@ jobs:
|
||||
contents: read
|
||||
packages: write
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Login to ghcr.io
|
||||
uses: docker/login-action@v2
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Build image and push to registry
|
||||
uses: docker/build-push-action@v3
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
no-cache: true
|
||||
context: .
|
||||
@@ -40,15 +40,15 @@ jobs:
|
||||
contents: read
|
||||
packages: write
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Login to ghcr.io
|
||||
uses: docker/login-action@v2
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Build image and push to registry
|
||||
uses: docker/build-push-action@v3
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
no-cache: true
|
||||
context: .
|
||||
@@ -64,13 +64,13 @@ jobs:
|
||||
needs: [ amd64, aarch64 ]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2
|
||||
uses: docker/setup-qemu-action@v3
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
uses: docker/setup-buildx-action@v3
|
||||
- name: Login to ghcr.io
|
||||
uses: docker/login-action@v2
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
|
||||
2
.github/workflows/docker-image.yml
vendored
2
.github/workflows/docker-image.yml
vendored
@@ -14,7 +14,7 @@ jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Cache Docker layers
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
|
||||
@@ -30,5 +30,5 @@ Your horizon (Laravel scheduler): `localhost:8000/horizon` - Only reachable if y
|
||||
Mails are caught by Mailpit: `localhost:8025`
|
||||
|
||||
## New Service Contribution
|
||||
Check out the docs [here](https://coolify.io/docs/how-to-add-a-service).
|
||||
Check out the docs [here](https://coolify.io/docs/resources/services/add-service).
|
||||
|
||||
|
||||
11
README.md
11
README.md
@@ -28,17 +28,18 @@ https://coolify.io/sponsorships
|
||||
|
||||
Thank you so much!
|
||||
|
||||
Special thanks to our biggest sponsors, [CCCareers](https://cccareers.org/) and [Appwrite](https://appwrite.io)!
|
||||
Special thanks to our biggest sponsor, [CCCareers](https://cccareers.org/)!
|
||||
|
||||
<a href="https://cccareers.org/" target="_blank"><img src="./other/logos/ccc-logo.webp" alt="cccareers 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 ($40+)
|
||||
<a href="https://cryptojobslist.com/?utm_source=coolify.io"><img src="https://github.com/cryptojobslist.png" width="60px" alt="CryptoJobsList" /></a>
|
||||
<a href="https://typebot.io/?utm_source=coolify.io"><img src="https://pbs.twimg.com/profile_images/1509194008366657543/9I-C7uWT_400x400.jpg" width="60px" alt="typebot"/></a>
|
||||
<a href="https://bc.direct"><img width="60px" alt="BC Direct" src="https://github.com/coollabsio/coolify/assets/5845193/a4063c41-95ed-4a32-8814-cd1475572e37"/></a>
|
||||
<a href="https://typebot.io/?utm_source=coolify.io"><img src="https://pbs.twimg.com/profile_images/1509194008366657543/9I-C7uWT_400x400.jpg" width="60px" alt="typebot"/></a>
|
||||
<a href="https://americancloud.com/?utm_source=coolify.io"><img src="https://github.com/American-Cloud.png" width="60px" alt="American Cloud"/></a>
|
||||
<a href="https://cryptojobslist.com/?utm_source=coolify.io"><img src="https://github.com/cryptojobslist.png" width="60px" alt="CryptoJobsList" /></a>
|
||||
<a href="https://www.uxwizz.com/?utm_source=coolify.io"><img width="60px" alt="UXWizz" src="https://github.com/UXWizz.png"/></a>
|
||||
<a href="https://github.com/automazeio"><img src="https://github.com/automazeio.png" width="60px" alt="Corentin Clichy" /></a>
|
||||
<a href="https://github.com/Flowko"><img src="https://barrad.me/_ipx/f_webp&s_300x300/younes.jpg" width="60px" alt="Younes Barrad" /></a>
|
||||
<a href="https://github.com/automazeio"><img src="https://github.com/automazeio.png" width="60px" alt="Automaze" /></a>
|
||||
<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>
|
||||
|
||||
16
SECURITY.md
Normal file
16
SECURITY.md
Normal file
@@ -0,0 +1,16 @@
|
||||
# Security Policy
|
||||
|
||||
## Supported Versions
|
||||
|
||||
Use this section to tell people about which versions of your project are
|
||||
currently being supported with security updates.
|
||||
|
||||
| Version | Supported |
|
||||
| ------- | ------------------ |
|
||||
| > 4 | :white_check_mark: |
|
||||
| 3 | :x: |
|
||||
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
If you have any vulnerability please report at hi@coollabs.io
|
||||
159
app/Actions/Database/StartClickhouse.php
Normal file
159
app/Actions/Database/StartClickhouse.php
Normal file
@@ -0,0 +1,159 @@
|
||||
<?php
|
||||
|
||||
namespace App\Actions\Database;
|
||||
|
||||
use App\Models\StandaloneClickhouse;
|
||||
use Illuminate\Support\Str;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
use Lorisleiva\Actions\Concerns\AsAction;
|
||||
|
||||
class StartClickhouse
|
||||
{
|
||||
use AsAction;
|
||||
|
||||
public StandaloneClickhouse $database;
|
||||
public array $commands = [];
|
||||
public string $configuration_dir;
|
||||
|
||||
public function handle(StandaloneClickhouse $database)
|
||||
{
|
||||
$this->database = $database;
|
||||
|
||||
|
||||
$container_name = $this->database->uuid;
|
||||
$this->configuration_dir = database_configuration_dir() . '/' . $container_name;
|
||||
|
||||
$this->commands = [
|
||||
"echo 'Starting {$database->name}.'",
|
||||
"mkdir -p $this->configuration_dir",
|
||||
];
|
||||
|
||||
$persistent_storages = $this->generate_local_persistent_volumes();
|
||||
$volume_names = $this->generate_local_persistent_volumes_only_volume_names();
|
||||
$environment_variables = $this->generate_environment_variables();
|
||||
|
||||
$docker_compose = [
|
||||
'version' => '3.8',
|
||||
'services' => [
|
||||
$container_name => [
|
||||
'image' => $this->database->image,
|
||||
'container_name' => $container_name,
|
||||
'environment' => $environment_variables,
|
||||
'restart' => RESTART_MODE,
|
||||
'networks' => [
|
||||
$this->database->destination->network,
|
||||
],
|
||||
'ulimits' => [
|
||||
'nofile' => [
|
||||
'soft' => 262144,
|
||||
'hard' => 262144,
|
||||
],
|
||||
],
|
||||
'labels' => [
|
||||
'coolify.managed' => 'true',
|
||||
],
|
||||
'healthcheck' => [
|
||||
'test' => "clickhouse-client --password {$this->database->clickhouse_admin_password} --query 'SELECT 1'",
|
||||
'interval' => '5s',
|
||||
'timeout' => '5s',
|
||||
'retries' => 10,
|
||||
'start_period' => '5s'
|
||||
],
|
||||
'mem_limit' => $this->database->limits_memory,
|
||||
'memswap_limit' => $this->database->limits_memory_swap,
|
||||
'mem_swappiness' => $this->database->limits_memory_swappiness,
|
||||
'mem_reservation' => $this->database->limits_memory_reservation,
|
||||
'cpus' => (float) $this->database->limits_cpus,
|
||||
'cpu_shares' => $this->database->limits_cpu_shares,
|
||||
]
|
||||
],
|
||||
'networks' => [
|
||||
$this->database->destination->network => [
|
||||
'external' => true,
|
||||
'name' => $this->database->destination->network,
|
||||
'attachable' => true,
|
||||
]
|
||||
]
|
||||
];
|
||||
if (!is_null($this->database->limits_cpuset)) {
|
||||
data_set($docker_compose, "services.{$container_name}.cpuset", $this->database->limits_cpuset);
|
||||
}
|
||||
if ($this->database->destination->server->isLogDrainEnabled() && $this->database->isLogDrainEnabled()) {
|
||||
$docker_compose['services'][$container_name]['logging'] = [
|
||||
'driver' => 'fluentd',
|
||||
'options' => [
|
||||
'fluentd-address' => "tcp://127.0.0.1:24224",
|
||||
'fluentd-async' => "true",
|
||||
'fluentd-sub-second-precision' => "true",
|
||||
]
|
||||
];
|
||||
}
|
||||
if (count($this->database->ports_mappings_array) > 0) {
|
||||
$docker_compose['services'][$container_name]['ports'] = $this->database->ports_mappings_array;
|
||||
}
|
||||
if (count($persistent_storages) > 0) {
|
||||
$docker_compose['services'][$container_name]['volumes'] = $persistent_storages;
|
||||
}
|
||||
if (count($volume_names) > 0) {
|
||||
$docker_compose['volumes'] = $volume_names;
|
||||
}
|
||||
$docker_compose = Yaml::dump($docker_compose, 10);
|
||||
$docker_compose_base64 = base64_encode($docker_compose);
|
||||
$this->commands[] = "echo '{$docker_compose_base64}' | base64 -d > $this->configuration_dir/docker-compose.yml";
|
||||
$readme = generate_readme_file($this->database->name, now());
|
||||
$this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md";
|
||||
$this->commands[] = "echo 'Pulling {$database->image} image.'";
|
||||
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml pull";
|
||||
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml up -d";
|
||||
$this->commands[] = "echo 'Database started.'";
|
||||
return remote_process($this->commands, $database->destination->server, callEventOnFinish: 'DatabaseStatusChanged');
|
||||
}
|
||||
|
||||
private function generate_local_persistent_volumes()
|
||||
{
|
||||
$local_persistent_volumes = [];
|
||||
foreach ($this->database->persistentStorages as $persistentStorage) {
|
||||
if ($persistentStorage->host_path !== '' && $persistentStorage->host_path !== null) {
|
||||
$local_persistent_volumes[] = $persistentStorage->host_path . ':' . $persistentStorage->mount_path;
|
||||
} else {
|
||||
$volume_name = $persistentStorage->name;
|
||||
$local_persistent_volumes[] = $volume_name . ':' . $persistentStorage->mount_path;
|
||||
}
|
||||
}
|
||||
return $local_persistent_volumes;
|
||||
}
|
||||
|
||||
private function generate_local_persistent_volumes_only_volume_names()
|
||||
{
|
||||
$local_persistent_volumes_names = [];
|
||||
foreach ($this->database->persistentStorages as $persistentStorage) {
|
||||
if ($persistentStorage->host_path) {
|
||||
continue;
|
||||
}
|
||||
$name = $persistentStorage->name;
|
||||
$local_persistent_volumes_names[$name] = [
|
||||
'name' => $name,
|
||||
'external' => false,
|
||||
];
|
||||
}
|
||||
return $local_persistent_volumes_names;
|
||||
}
|
||||
|
||||
private function generate_environment_variables()
|
||||
{
|
||||
$environment_variables = collect();
|
||||
foreach ($this->database->runtime_environment_variables as $env) {
|
||||
$environment_variables->push("$env->key=$env->real_value");
|
||||
}
|
||||
|
||||
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('CLICKHOUSE_ADMIN_USER'))->isEmpty()) {
|
||||
$environment_variables->push("CLICKHOUSE_ADMIN_USER={$this->database->clickhouse_admin_user}");
|
||||
}
|
||||
|
||||
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('CLICKHOUSE_ADMIN_PASSWORD'))->isEmpty()) {
|
||||
$environment_variables->push("CLICKHOUSE_ADMIN_PASSWORD={$this->database->clickhouse_admin_password}");
|
||||
}
|
||||
|
||||
return $environment_variables->all();
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,9 @@
|
||||
namespace App\Actions\Database;
|
||||
|
||||
use App\Models\ServiceDatabase;
|
||||
use App\Models\StandaloneClickhouse;
|
||||
use App\Models\StandaloneDragonfly;
|
||||
use App\Models\StandaloneKeydb;
|
||||
use App\Models\StandaloneMariadb;
|
||||
use App\Models\StandaloneMongodb;
|
||||
use App\Models\StandaloneMysql;
|
||||
@@ -15,7 +18,7 @@ class StartDatabaseProxy
|
||||
{
|
||||
use AsAction;
|
||||
|
||||
public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb|StandaloneMysql|StandaloneMariadb|ServiceDatabase $database)
|
||||
public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb|StandaloneMysql|StandaloneMariadb|StandaloneKeydb|StandaloneDragonfly|StandaloneClickhouse|ServiceDatabase $database)
|
||||
{
|
||||
$internalPort = null;
|
||||
$type = $database->getMorphClass();
|
||||
@@ -50,6 +53,18 @@ class StartDatabaseProxy
|
||||
$type = 'App\Models\StandaloneRedis';
|
||||
$containerName = "redis-{$database->service->uuid}";
|
||||
break;
|
||||
case 'standalone-keydb':
|
||||
$type = 'App\Models\StandaloneKeydb';
|
||||
$containerName = "keydb-{$database->service->uuid}";
|
||||
break;
|
||||
case 'standalone-dragonfly':
|
||||
$type = 'App\Models\StandaloneDragonfly';
|
||||
$containerName = "dragonfly-{$database->service->uuid}";
|
||||
break;
|
||||
case 'standalone-clickhouse':
|
||||
$type = 'App\Models\StandaloneClickhouse';
|
||||
$containerName = "clickhouse-{$database->service->uuid}";
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($type === 'App\Models\StandaloneRedis') {
|
||||
@@ -62,6 +77,12 @@ class StartDatabaseProxy
|
||||
$internalPort = 3306;
|
||||
} else if ($type === 'App\Models\StandaloneMariadb') {
|
||||
$internalPort = 3306;
|
||||
} else if ($type === 'App\Models\StandaloneKeydb') {
|
||||
$internalPort = 6379;
|
||||
} else if ($type === 'App\Models\StandaloneDragonfly') {
|
||||
$internalPort = 6379;
|
||||
} else if ($type === 'App\Models\StandaloneClickhouse') {
|
||||
$internalPort = 9000;
|
||||
}
|
||||
$configuration_dir = database_proxy_dir($database->uuid);
|
||||
$nginxconf = <<<EOF
|
||||
|
||||
156
app/Actions/Database/StartDragonfly.php
Normal file
156
app/Actions/Database/StartDragonfly.php
Normal file
@@ -0,0 +1,156 @@
|
||||
<?php
|
||||
|
||||
namespace App\Actions\Database;
|
||||
|
||||
use App\Models\StandaloneDragonfly;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Str;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
use Lorisleiva\Actions\Concerns\AsAction;
|
||||
|
||||
class StartDragonfly
|
||||
{
|
||||
use AsAction;
|
||||
|
||||
public StandaloneDragonfly $database;
|
||||
public array $commands = [];
|
||||
public string $configuration_dir;
|
||||
|
||||
|
||||
public function handle(StandaloneDragonfly $database)
|
||||
{
|
||||
$this->database = $database;
|
||||
|
||||
$startCommand = "dragonfly --requirepass {$this->database->dragonfly_password}";
|
||||
|
||||
$container_name = $this->database->uuid;
|
||||
$this->configuration_dir = database_configuration_dir() . '/' . $container_name;
|
||||
|
||||
$this->commands = [
|
||||
"echo 'Starting {$database->name}.'",
|
||||
"mkdir -p $this->configuration_dir",
|
||||
];
|
||||
|
||||
$persistent_storages = $this->generate_local_persistent_volumes();
|
||||
$volume_names = $this->generate_local_persistent_volumes_only_volume_names();
|
||||
$environment_variables = $this->generate_environment_variables();
|
||||
|
||||
$docker_compose = [
|
||||
'version' => '3.8',
|
||||
'services' => [
|
||||
$container_name => [
|
||||
'image' => $this->database->image,
|
||||
'command' => $startCommand,
|
||||
'container_name' => $container_name,
|
||||
'environment' => $environment_variables,
|
||||
'restart' => RESTART_MODE,
|
||||
'networks' => [
|
||||
$this->database->destination->network,
|
||||
],
|
||||
'ulimits' => [
|
||||
'memlock'=> '-1'
|
||||
],
|
||||
'labels' => [
|
||||
'coolify.managed' => 'true',
|
||||
],
|
||||
'healthcheck' => [
|
||||
'test' => "redis-cli -a {$this->database->dragonfly_password} ping",
|
||||
'interval' => '5s',
|
||||
'timeout' => '5s',
|
||||
'retries' => 10,
|
||||
'start_period' => '5s'
|
||||
],
|
||||
'mem_limit' => $this->database->limits_memory,
|
||||
'memswap_limit' => $this->database->limits_memory_swap,
|
||||
'mem_swappiness' => $this->database->limits_memory_swappiness,
|
||||
'mem_reservation' => $this->database->limits_memory_reservation,
|
||||
'cpus' => (float) $this->database->limits_cpus,
|
||||
'cpu_shares' => $this->database->limits_cpu_shares,
|
||||
]
|
||||
],
|
||||
'networks' => [
|
||||
$this->database->destination->network => [
|
||||
'external' => true,
|
||||
'name' => $this->database->destination->network,
|
||||
'attachable' => true,
|
||||
]
|
||||
]
|
||||
];
|
||||
if (!is_null($this->database->limits_cpuset)) {
|
||||
data_set($docker_compose, "services.{$container_name}.cpuset", $this->database->limits_cpuset);
|
||||
}
|
||||
if ($this->database->destination->server->isLogDrainEnabled() && $this->database->isLogDrainEnabled()) {
|
||||
$docker_compose['services'][$container_name]['logging'] = [
|
||||
'driver' => 'fluentd',
|
||||
'options' => [
|
||||
'fluentd-address' => "tcp://127.0.0.1:24224",
|
||||
'fluentd-async' => "true",
|
||||
'fluentd-sub-second-precision' => "true",
|
||||
]
|
||||
];
|
||||
}
|
||||
if (count($this->database->ports_mappings_array) > 0) {
|
||||
$docker_compose['services'][$container_name]['ports'] = $this->database->ports_mappings_array;
|
||||
}
|
||||
if (count($persistent_storages) > 0) {
|
||||
$docker_compose['services'][$container_name]['volumes'] = $persistent_storages;
|
||||
}
|
||||
if (count($volume_names) > 0) {
|
||||
$docker_compose['volumes'] = $volume_names;
|
||||
}
|
||||
$docker_compose = Yaml::dump($docker_compose, 10);
|
||||
$docker_compose_base64 = base64_encode($docker_compose);
|
||||
$this->commands[] = "echo '{$docker_compose_base64}' | base64 -d > $this->configuration_dir/docker-compose.yml";
|
||||
$readme = generate_readme_file($this->database->name, now());
|
||||
$this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md";
|
||||
$this->commands[] = "echo 'Pulling {$database->image} image.'";
|
||||
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml pull";
|
||||
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml up -d";
|
||||
$this->commands[] = "echo 'Database started.'";
|
||||
return remote_process($this->commands, $database->destination->server, callEventOnFinish: 'DatabaseStatusChanged');
|
||||
}
|
||||
|
||||
private function generate_local_persistent_volumes()
|
||||
{
|
||||
$local_persistent_volumes = [];
|
||||
foreach ($this->database->persistentStorages as $persistentStorage) {
|
||||
if ($persistentStorage->host_path !== '' && $persistentStorage->host_path !== null) {
|
||||
$local_persistent_volumes[] = $persistentStorage->host_path . ':' . $persistentStorage->mount_path;
|
||||
} else {
|
||||
$volume_name = $persistentStorage->name;
|
||||
$local_persistent_volumes[] = $volume_name . ':' . $persistentStorage->mount_path;
|
||||
}
|
||||
}
|
||||
return $local_persistent_volumes;
|
||||
}
|
||||
|
||||
private function generate_local_persistent_volumes_only_volume_names()
|
||||
{
|
||||
$local_persistent_volumes_names = [];
|
||||
foreach ($this->database->persistentStorages as $persistentStorage) {
|
||||
if ($persistentStorage->host_path) {
|
||||
continue;
|
||||
}
|
||||
$name = $persistentStorage->name;
|
||||
$local_persistent_volumes_names[$name] = [
|
||||
'name' => $name,
|
||||
'external' => false,
|
||||
];
|
||||
}
|
||||
return $local_persistent_volumes_names;
|
||||
}
|
||||
|
||||
private function generate_environment_variables()
|
||||
{
|
||||
$environment_variables = collect();
|
||||
foreach ($this->database->runtime_environment_variables as $env) {
|
||||
$environment_variables->push("$env->key=$env->real_value");
|
||||
}
|
||||
|
||||
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('REDIS_PASSWORD'))->isEmpty()) {
|
||||
$environment_variables->push("REDIS_PASSWORD={$this->database->dragonfly_password}");
|
||||
}
|
||||
|
||||
return $environment_variables->all();
|
||||
}
|
||||
}
|
||||
174
app/Actions/Database/StartKeydb.php
Normal file
174
app/Actions/Database/StartKeydb.php
Normal file
@@ -0,0 +1,174 @@
|
||||
<?php
|
||||
|
||||
namespace App\Actions\Database;
|
||||
|
||||
use App\Models\StandaloneKeydb;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Str;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
use Lorisleiva\Actions\Concerns\AsAction;
|
||||
|
||||
class StartKeydb
|
||||
{
|
||||
use AsAction;
|
||||
|
||||
public StandaloneKeydb $database;
|
||||
public array $commands = [];
|
||||
public string $configuration_dir;
|
||||
|
||||
|
||||
public function handle(StandaloneKeydb $database)
|
||||
{
|
||||
$this->database = $database;
|
||||
|
||||
$startCommand = "keydb-server --requirepass {$this->database->keydb_password} --appendonly yes";
|
||||
|
||||
$container_name = $this->database->uuid;
|
||||
$this->configuration_dir = database_configuration_dir() . '/' . $container_name;
|
||||
|
||||
$this->commands = [
|
||||
"echo 'Starting {$database->name}.'",
|
||||
"mkdir -p $this->configuration_dir",
|
||||
];
|
||||
|
||||
$persistent_storages = $this->generate_local_persistent_volumes();
|
||||
$volume_names = $this->generate_local_persistent_volumes_only_volume_names();
|
||||
$environment_variables = $this->generate_environment_variables();
|
||||
$this->add_custom_keydb();
|
||||
|
||||
$docker_compose = [
|
||||
'version' => '3.8',
|
||||
'services' => [
|
||||
$container_name => [
|
||||
'image' => $this->database->image,
|
||||
'command' => $startCommand,
|
||||
'container_name' => $container_name,
|
||||
'environment' => $environment_variables,
|
||||
'restart' => RESTART_MODE,
|
||||
'networks' => [
|
||||
$this->database->destination->network,
|
||||
],
|
||||
'labels' => [
|
||||
'coolify.managed' => 'true',
|
||||
],
|
||||
'healthcheck' => [
|
||||
'test' => "keydb-cli --pass {$this->database->keydb_password} ping",
|
||||
'interval' => '5s',
|
||||
'timeout' => '5s',
|
||||
'retries' => 10,
|
||||
'start_period' => '5s'
|
||||
],
|
||||
'mem_limit' => $this->database->limits_memory,
|
||||
'memswap_limit' => $this->database->limits_memory_swap,
|
||||
'mem_swappiness' => $this->database->limits_memory_swappiness,
|
||||
'mem_reservation' => $this->database->limits_memory_reservation,
|
||||
'cpus' => (float) $this->database->limits_cpus,
|
||||
'cpu_shares' => $this->database->limits_cpu_shares,
|
||||
]
|
||||
],
|
||||
'networks' => [
|
||||
$this->database->destination->network => [
|
||||
'external' => true,
|
||||
'name' => $this->database->destination->network,
|
||||
'attachable' => true,
|
||||
]
|
||||
]
|
||||
];
|
||||
if (!is_null($this->database->limits_cpuset)) {
|
||||
data_set($docker_compose, "services.{$container_name}.cpuset", $this->database->limits_cpuset);
|
||||
}
|
||||
if ($this->database->destination->server->isLogDrainEnabled() && $this->database->isLogDrainEnabled()) {
|
||||
$docker_compose['services'][$container_name]['logging'] = [
|
||||
'driver' => 'fluentd',
|
||||
'options' => [
|
||||
'fluentd-address' => "tcp://127.0.0.1:24224",
|
||||
'fluentd-async' => "true",
|
||||
'fluentd-sub-second-precision' => "true",
|
||||
]
|
||||
];
|
||||
}
|
||||
if (count($this->database->ports_mappings_array) > 0) {
|
||||
$docker_compose['services'][$container_name]['ports'] = $this->database->ports_mappings_array;
|
||||
}
|
||||
if (count($persistent_storages) > 0) {
|
||||
$docker_compose['services'][$container_name]['volumes'] = $persistent_storages;
|
||||
}
|
||||
if (count($volume_names) > 0) {
|
||||
$docker_compose['volumes'] = $volume_names;
|
||||
}
|
||||
if (!is_null($this->database->keydb_conf)) {
|
||||
$docker_compose['services'][$container_name]['volumes'][] = [
|
||||
'type' => 'bind',
|
||||
'source' => $this->configuration_dir . '/keydb.conf',
|
||||
'target' => '/etc/keydb/keydb.conf',
|
||||
'read_only' => true,
|
||||
];
|
||||
$docker_compose['services'][$container_name]['command'] = "keydb-server /etc/keydb/keydb.conf --requirepass {$this->database->keydb_password} --appendonly yes";
|
||||
}
|
||||
$docker_compose = Yaml::dump($docker_compose, 10);
|
||||
$docker_compose_base64 = base64_encode($docker_compose);
|
||||
$this->commands[] = "echo '{$docker_compose_base64}' | base64 -d > $this->configuration_dir/docker-compose.yml";
|
||||
$readme = generate_readme_file($this->database->name, now());
|
||||
$this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md";
|
||||
$this->commands[] = "echo 'Pulling {$database->image} image.'";
|
||||
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml pull";
|
||||
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml up -d";
|
||||
$this->commands[] = "echo 'Database started.'";
|
||||
return remote_process($this->commands, $database->destination->server, callEventOnFinish: 'DatabaseStatusChanged');
|
||||
}
|
||||
|
||||
private function generate_local_persistent_volumes()
|
||||
{
|
||||
$local_persistent_volumes = [];
|
||||
foreach ($this->database->persistentStorages as $persistentStorage) {
|
||||
if ($persistentStorage->host_path !== '' && $persistentStorage->host_path !== null) {
|
||||
$local_persistent_volumes[] = $persistentStorage->host_path . ':' . $persistentStorage->mount_path;
|
||||
} else {
|
||||
$volume_name = $persistentStorage->name;
|
||||
$local_persistent_volumes[] = $volume_name . ':' . $persistentStorage->mount_path;
|
||||
}
|
||||
}
|
||||
return $local_persistent_volumes;
|
||||
}
|
||||
|
||||
private function generate_local_persistent_volumes_only_volume_names()
|
||||
{
|
||||
$local_persistent_volumes_names = [];
|
||||
foreach ($this->database->persistentStorages as $persistentStorage) {
|
||||
if ($persistentStorage->host_path) {
|
||||
continue;
|
||||
}
|
||||
$name = $persistentStorage->name;
|
||||
$local_persistent_volumes_names[$name] = [
|
||||
'name' => $name,
|
||||
'external' => false,
|
||||
];
|
||||
}
|
||||
return $local_persistent_volumes_names;
|
||||
}
|
||||
|
||||
private function generate_environment_variables()
|
||||
{
|
||||
$environment_variables = collect();
|
||||
foreach ($this->database->runtime_environment_variables as $env) {
|
||||
$environment_variables->push("$env->key=$env->real_value");
|
||||
}
|
||||
|
||||
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('REDIS_PASSWORD'))->isEmpty()) {
|
||||
$environment_variables->push("REDIS_PASSWORD={$this->database->keydb_password}");
|
||||
}
|
||||
|
||||
return $environment_variables->all();
|
||||
}
|
||||
private function add_custom_keydb()
|
||||
{
|
||||
if (is_null($this->database->keydb_conf)) {
|
||||
return;
|
||||
}
|
||||
$filename = 'keydb.conf';
|
||||
Storage::disk('local')->put("tmp/keydb.conf_{$this->database->uuid}", $this->database->keydb_conf);
|
||||
$path = Storage::path("tmp/keydb.conf_{$this->database->uuid}");
|
||||
instant_scp($path, "{$this->configuration_dir}/{$filename}", $this->database->destination->server);
|
||||
Storage::disk('local')->delete("tmp/keydb.conf_{$this->database->uuid}");
|
||||
}
|
||||
}
|
||||
@@ -114,8 +114,12 @@ class StartMariadb
|
||||
{
|
||||
$local_persistent_volumes = [];
|
||||
foreach ($this->database->persistentStorages as $persistentStorage) {
|
||||
$volume_name = $persistentStorage->host_path ?? $persistentStorage->name;
|
||||
$local_persistent_volumes[] = $volume_name . ':' . $persistentStorage->mount_path;
|
||||
if ($persistentStorage->host_path !== '' && $persistentStorage->host_path !== null) {
|
||||
$local_persistent_volumes[] = $persistentStorage->host_path . ':' . $persistentStorage->mount_path;
|
||||
} else {
|
||||
$volume_name = $persistentStorage->name;
|
||||
$local_persistent_volumes[] = $volume_name . ':' . $persistentStorage->mount_path;
|
||||
}
|
||||
}
|
||||
return $local_persistent_volumes;
|
||||
}
|
||||
|
||||
@@ -130,8 +130,12 @@ class StartMongodb
|
||||
{
|
||||
$local_persistent_volumes = [];
|
||||
foreach ($this->database->persistentStorages as $persistentStorage) {
|
||||
$volume_name = $persistentStorage->host_path ?? $persistentStorage->name;
|
||||
$local_persistent_volumes[] = $volume_name . ':' . $persistentStorage->mount_path;
|
||||
if ($persistentStorage->host_path !== '' && $persistentStorage->host_path !== null) {
|
||||
$local_persistent_volumes[] = $persistentStorage->host_path . ':' . $persistentStorage->mount_path;
|
||||
} else {
|
||||
$volume_name = $persistentStorage->name;
|
||||
$local_persistent_volumes[] = $volume_name . ':' . $persistentStorage->mount_path;
|
||||
}
|
||||
}
|
||||
return $local_persistent_volumes;
|
||||
}
|
||||
|
||||
@@ -114,8 +114,12 @@ class StartMysql
|
||||
{
|
||||
$local_persistent_volumes = [];
|
||||
foreach ($this->database->persistentStorages as $persistentStorage) {
|
||||
$volume_name = $persistentStorage->host_path ?? $persistentStorage->name;
|
||||
$local_persistent_volumes[] = $volume_name . ':' . $persistentStorage->mount_path;
|
||||
if ($persistentStorage->host_path !== '' && $persistentStorage->host_path !== null) {
|
||||
$local_persistent_volumes[] = $persistentStorage->host_path . ':' . $persistentStorage->mount_path;
|
||||
} else {
|
||||
$volume_name = $persistentStorage->name;
|
||||
$local_persistent_volumes[] = $volume_name . ':' . $persistentStorage->mount_path;
|
||||
}
|
||||
}
|
||||
return $local_persistent_volumes;
|
||||
}
|
||||
|
||||
@@ -136,8 +136,12 @@ class StartPostgresql
|
||||
{
|
||||
$local_persistent_volumes = [];
|
||||
foreach ($this->database->persistentStorages as $persistentStorage) {
|
||||
$volume_name = $persistentStorage->host_path ?? $persistentStorage->name;
|
||||
$local_persistent_volumes[] = $volume_name . ':' . $persistentStorage->mount_path;
|
||||
if ($persistentStorage->host_path !== '' && $persistentStorage->host_path !== null) {
|
||||
$local_persistent_volumes[] = $persistentStorage->host_path . ':' . $persistentStorage->mount_path;
|
||||
} else {
|
||||
$volume_name = $persistentStorage->name;
|
||||
$local_persistent_volumes[] = $volume_name . ':' . $persistentStorage->mount_path;
|
||||
}
|
||||
}
|
||||
return $local_persistent_volumes;
|
||||
}
|
||||
|
||||
@@ -125,8 +125,12 @@ class StartRedis
|
||||
{
|
||||
$local_persistent_volumes = [];
|
||||
foreach ($this->database->persistentStorages as $persistentStorage) {
|
||||
$volume_name = $persistentStorage->host_path ?? $persistentStorage->name;
|
||||
$local_persistent_volumes[] = $volume_name . ':' . $persistentStorage->mount_path;
|
||||
if ($persistentStorage->host_path !== '' && $persistentStorage->host_path !== null) {
|
||||
$local_persistent_volumes[] = $persistentStorage->host_path . ':' . $persistentStorage->mount_path;
|
||||
} else {
|
||||
$volume_name = $persistentStorage->name;
|
||||
$local_persistent_volumes[] = $volume_name . ':' . $persistentStorage->mount_path;
|
||||
}
|
||||
}
|
||||
return $local_persistent_volumes;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
|
||||
namespace App\Actions\Database;
|
||||
|
||||
use App\Events\DatabaseStatusChanged;
|
||||
use App\Models\StandaloneClickhouse;
|
||||
use App\Models\StandaloneDragonfly;
|
||||
use App\Models\StandaloneKeydb;
|
||||
use App\Models\StandaloneMariadb;
|
||||
use App\Models\StandaloneMongodb;
|
||||
use App\Models\StandaloneMysql;
|
||||
@@ -14,7 +16,7 @@ class StopDatabase
|
||||
{
|
||||
use AsAction;
|
||||
|
||||
public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb|StandaloneMysql|StandaloneMariadb $database)
|
||||
public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb|StandaloneMysql|StandaloneMariadb|StandaloneKeydb|StandaloneDragonfly|StandaloneClickhouse $database)
|
||||
{
|
||||
$server = $database->destination->server;
|
||||
if (!$server->isFunctional()) {
|
||||
|
||||
@@ -3,6 +3,9 @@
|
||||
namespace App\Actions\Database;
|
||||
|
||||
use App\Models\ServiceDatabase;
|
||||
use App\Models\StandaloneClickhouse;
|
||||
use App\Models\StandaloneDragonfly;
|
||||
use App\Models\StandaloneKeydb;
|
||||
use App\Models\StandaloneMariadb;
|
||||
use App\Models\StandaloneMongodb;
|
||||
use App\Models\StandaloneMysql;
|
||||
@@ -14,7 +17,7 @@ class StopDatabaseProxy
|
||||
{
|
||||
use AsAction;
|
||||
|
||||
public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb|StandaloneMysql|StandaloneMariadb|ServiceDatabase $database)
|
||||
public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb|StandaloneMysql|StandaloneMariadb|StandaloneKeydb|ServiceDatabase|StandaloneDragonfly|StandaloneClickhouse $database)
|
||||
{
|
||||
$server = data_get($database, 'destination.server');
|
||||
$uuid = $database->uuid;
|
||||
|
||||
@@ -13,6 +13,9 @@ class CheckProxy
|
||||
if ($server->proxyType() === 'NONE') {
|
||||
return false;
|
||||
}
|
||||
if (!$server->validateConnection()) {
|
||||
throw new \Exception("Server Connection Error");
|
||||
}
|
||||
if (!$server->isProxyShouldRun()) {
|
||||
if ($fromUI) {
|
||||
throw new \Exception("Proxy should not run. You selected the Custom Proxy.");
|
||||
@@ -35,6 +38,9 @@ class CheckProxy
|
||||
$server->save();
|
||||
return false;
|
||||
}
|
||||
if ($server->settings->is_cloudflare_tunnel) {
|
||||
return false;
|
||||
}
|
||||
$ip = $server->ip;
|
||||
if ($server->id === 0) {
|
||||
$ip = 'host.docker.internal';
|
||||
|
||||
44
app/Actions/Server/ConfigureCloudflared.php
Normal file
44
app/Actions/Server/ConfigureCloudflared.php
Normal file
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
namespace App\Actions\Server;
|
||||
|
||||
use App\Models\Server;
|
||||
use Lorisleiva\Actions\Concerns\AsAction;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
|
||||
class ConfigureCloudflared
|
||||
{
|
||||
use AsAction;
|
||||
public function handle(Server $server, string $cloudflare_token)
|
||||
{
|
||||
try {
|
||||
$config = [
|
||||
"services" => [
|
||||
"coolify-cloudflared" => [
|
||||
"container_name" => "coolify-cloudflared",
|
||||
"image" => "cloudflare/cloudflared:latest",
|
||||
"restart" => RESTART_MODE,
|
||||
"network_mode" => "host",
|
||||
"command" => "tunnel run",
|
||||
"environment" => [
|
||||
"TUNNEL_TOKEN={$cloudflare_token}",
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
$config = Yaml::dump($config, 12, 2);
|
||||
$docker_compose_yml_base64 = base64_encode($config);
|
||||
$commands = collect([
|
||||
"mkdir -p /tmp/cloudflared && cd /tmp/cloudflared",
|
||||
"echo '$docker_compose_yml_base64' | base64 -d > docker-compose.yml",
|
||||
"docker compose pull",
|
||||
"docker compose down -v --remove-orphans > /dev/null 2>&1",
|
||||
"docker compose up -d --remove-orphans",
|
||||
]);
|
||||
instant_remote_process($commands, $server);
|
||||
} catch (\Throwable $e) {
|
||||
ray($e);
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,7 @@ class InstallDocker
|
||||
{
|
||||
$supported_os_type = $server->validateOS();
|
||||
if (!$supported_os_type) {
|
||||
throw new \Exception('Server OS type is not supported for automated installation. Please install Docker manually before continuing: <a target="_blank" class="underline" href="https://coolify.io/docs/servers#install-docker-engine-manually">documentation</a>.');
|
||||
throw new \Exception('Server OS type is not supported for automated installation. Please install Docker manually before continuing: <a target="_blank" class="underline" href="https://coolify.io/docs/installation#manually">documentation</a>.');
|
||||
}
|
||||
ray('Installing Docker on server: ' . $server->name . ' (' . $server->ip . ')' . ' with OS type: ' . $supported_os_type);
|
||||
$dockerVersion = '24.0';
|
||||
|
||||
@@ -12,10 +12,12 @@ class UpdateCoolify
|
||||
public ?Server $server = null;
|
||||
public ?string $latestVersion = null;
|
||||
public ?string $currentVersion = null;
|
||||
public bool $async = false;
|
||||
|
||||
public function handle(bool $force)
|
||||
public function handle(bool $force = false, bool $async = false)
|
||||
{
|
||||
try {
|
||||
$this->async = $async;
|
||||
$settings = InstanceSettings::get();
|
||||
ray('Running InstanceAutoUpdateJob');
|
||||
$this->server = Server::find(0);
|
||||
@@ -43,7 +45,6 @@ class UpdateCoolify
|
||||
}
|
||||
$this->update();
|
||||
}
|
||||
send_internal_notification("Instance updated from {$this->currentVersion} -> {$this->latestVersion}");
|
||||
} catch (\Throwable $e) {
|
||||
ray('InstanceAutoUpdateJob failed');
|
||||
ray($e->getMessage());
|
||||
@@ -56,17 +57,32 @@ class UpdateCoolify
|
||||
{
|
||||
if (isDev()) {
|
||||
ray("Running update on local docker container. Updating to $this->latestVersion");
|
||||
remote_process([
|
||||
"sleep 10"
|
||||
], $this->server);
|
||||
if ($this->async) {
|
||||
ray('Running async update');
|
||||
remote_process([
|
||||
"sleep 10"
|
||||
], $this->server);
|
||||
} else {
|
||||
instant_remote_process([
|
||||
"sleep 10"
|
||||
], $this->server);
|
||||
}
|
||||
ray('Update done');
|
||||
return;
|
||||
} else {
|
||||
ray('Running update on production server');
|
||||
remote_process([
|
||||
"curl -fsSL https://cdn.coollabs.io/coolify/upgrade.sh -o /data/coolify/source/upgrade.sh",
|
||||
"bash /data/coolify/source/upgrade.sh $this->latestVersion"
|
||||
], $this->server);
|
||||
if ($this->async) {
|
||||
remote_process([
|
||||
"curl -fsSL https://cdn.coollabs.io/coolify/upgrade.sh -o /data/coolify/source/upgrade.sh",
|
||||
"bash /data/coolify/source/upgrade.sh $this->latestVersion"
|
||||
], $this->server);
|
||||
} else {
|
||||
instant_remote_process([
|
||||
"curl -fsSL https://cdn.coollabs.io/coolify/upgrade.sh -o /data/coolify/source/upgrade.sh",
|
||||
"bash /data/coolify/source/upgrade.sh $this->latestVersion"
|
||||
], $this->server);
|
||||
}
|
||||
send_internal_notification("Instance updated from {$this->currentVersion} -> {$this->latestVersion}");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ class CleanupDatabase extends Command
|
||||
$keep_days = 60;
|
||||
echo "Keep days: $keep_days\n";
|
||||
// Cleanup failed jobs table
|
||||
$failed_jobs = DB::table('failed_jobs')->where('failed_at', '<', now()->subDays(7));
|
||||
$failed_jobs = DB::table('failed_jobs')->where('failed_at', '<', now()->subDays(1));
|
||||
$count = $failed_jobs->count();
|
||||
echo "Delete $count entries from failed_jobs.\n";
|
||||
if ($this->option('yes')) {
|
||||
|
||||
@@ -7,6 +7,9 @@ use App\Models\ScheduledTask;
|
||||
use App\Models\Service;
|
||||
use App\Models\ServiceApplication;
|
||||
use App\Models\ServiceDatabase;
|
||||
use App\Models\StandaloneClickhouse;
|
||||
use App\Models\StandaloneDragonfly;
|
||||
use App\Models\StandaloneKeydb;
|
||||
use App\Models\StandaloneMariadb;
|
||||
use App\Models\StandaloneMongodb;
|
||||
use App\Models\StandaloneMysql;
|
||||
@@ -55,6 +58,33 @@ class CleanupStuckedResources extends Command
|
||||
} catch (\Throwable $e) {
|
||||
echo "Error in cleaning stuck redis: {$e->getMessage()}\n";
|
||||
}
|
||||
try {
|
||||
$keydbs = StandaloneKeydb::withTrashed()->whereNotNull('deleted_at')->get();
|
||||
foreach ($keydbs as $keydb) {
|
||||
echo "Deleting stuck keydb: {$keydb->name}\n";
|
||||
$redis->forceDelete();
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
echo "Error in cleaning stuck keydb: {$e->getMessage()}\n";
|
||||
}
|
||||
try {
|
||||
$dragonflies = StandaloneDragonfly::withTrashed()->whereNotNull('deleted_at')->get();
|
||||
foreach ($dragonflies as $dragonfly) {
|
||||
echo "Deleting stuck dragonfly: {$dragonfly->name}\n";
|
||||
$redis->forceDelete();
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
echo "Error in cleaning stuck dragonfly: {$e->getMessage()}\n";
|
||||
}
|
||||
try {
|
||||
$clickhouses = StandaloneClickhouse::withTrashed()->whereNotNull('deleted_at')->get();
|
||||
foreach ($clickhouses as $clickhouse) {
|
||||
echo "Deleting stuck clickhouse: {$clickhouse->name}\n";
|
||||
$redis->forceDelete();
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
echo "Error in cleaning stuck clickhouse: {$e->getMessage()}\n";
|
||||
}
|
||||
try {
|
||||
$mongodbs = StandaloneMongodb::withTrashed()->whereNotNull('deleted_at')->get();
|
||||
foreach ($mongodbs as $mongodb) {
|
||||
|
||||
@@ -34,11 +34,13 @@ class Init extends Command
|
||||
$this->cleanup_stucked_helper_containers();
|
||||
$this->call('cleanup:queue');
|
||||
$this->call('cleanup:stucked-resources');
|
||||
try {
|
||||
$server = Server::find(0)->first();
|
||||
$server->setupDynamicProxyConfiguration();
|
||||
} catch (\Throwable $e) {
|
||||
echo "Could not setup dynamic configuration: {$e->getMessage()}\n";
|
||||
if (!isCloud()) {
|
||||
try {
|
||||
$server = Server::find(0)->first();
|
||||
$server->setupDynamicProxyConfiguration();
|
||||
} catch (\Throwable $e) {
|
||||
echo "Could not setup dynamic configuration: {$e->getMessage()}\n";
|
||||
}
|
||||
}
|
||||
|
||||
$settings = InstanceSettings::get();
|
||||
|
||||
@@ -33,6 +33,7 @@ class Kernel extends ConsoleKernel
|
||||
$this->check_scheduled_backups($schedule);
|
||||
$this->pull_helper_image($schedule);
|
||||
$this->check_scheduled_tasks($schedule);
|
||||
$schedule->command('uploads:clear')->everyTwoMinutes();
|
||||
} else {
|
||||
// Instance Jobs
|
||||
$schedule->command('horizon:snapshot')->everyFiveMinutes();
|
||||
@@ -48,9 +49,8 @@ class Kernel extends ConsoleKernel
|
||||
$this->pull_helper_image($schedule);
|
||||
$this->check_scheduled_tasks($schedule);
|
||||
|
||||
if (!isCloud()) {
|
||||
$schedule->command('cleanup:database --yes')->daily();
|
||||
}
|
||||
$schedule->command('cleanup:database --yes')->daily();
|
||||
$schedule->command('uploads:clear')->everyTwoMinutes();
|
||||
}
|
||||
}
|
||||
private function pull_helper_image($schedule)
|
||||
@@ -72,43 +72,14 @@ class Kernel extends ConsoleKernel
|
||||
$containerServers = $servers->where('settings.is_swarm_worker', false)->where('settings.is_build_server', false);
|
||||
}
|
||||
foreach ($containerServers as $server) {
|
||||
$schedule->job(new ContainerStatusJob($server))->everyMinute()->onOneServer();
|
||||
$schedule->job(new ContainerStatusJob($server))->everyTwoMinutes()->onOneServer();
|
||||
if ($server->isLogDrainEnabled()) {
|
||||
$schedule->job(new CheckLogDrainContainerJob($server))->everyMinute()->onOneServer();
|
||||
$schedule->job(new CheckLogDrainContainerJob($server))->everyTwoMinutes()->onOneServer();
|
||||
}
|
||||
}
|
||||
foreach ($servers as $server) {
|
||||
$schedule->job(new ServerStatusJob($server))->everyMinute()->onOneServer();
|
||||
$schedule->job(new ServerStatusJob($server))->everyTwoMinutes()->onOneServer();
|
||||
}
|
||||
// Delayed Jobs
|
||||
// foreach ($containerServers as $server) {
|
||||
// $schedule
|
||||
// ->call(function () use ($server) {
|
||||
// $randomSeconds = rand(1, 40);
|
||||
// $job = new ContainerStatusJob($server);
|
||||
// $job->delay($randomSeconds);
|
||||
// ray('dispatching container status job in ' . $randomSeconds . ' seconds');
|
||||
// dispatch($job);
|
||||
// })->name('container-status-' . $server->id)->everyMinute()->onOneServer();
|
||||
// if ($server->isLogDrainEnabled()) {
|
||||
// $schedule
|
||||
// ->call(function () use ($server) {
|
||||
// $randomSeconds = rand(1, 40);
|
||||
// $job = new CheckLogDrainContainerJob($server);
|
||||
// $job->delay($randomSeconds);
|
||||
// dispatch($job);
|
||||
// })->name('log-drain-container-check-' . $server->id)->everyMinute()->onOneServer();
|
||||
// }
|
||||
// }
|
||||
// foreach ($servers as $server) {
|
||||
// $schedule
|
||||
// ->call(function () use ($server) {
|
||||
// $randomSeconds = rand(1, 40);
|
||||
// $job = new ServerStatusJob($server);
|
||||
// $job->delay($randomSeconds);
|
||||
// dispatch($job);
|
||||
// })->name('server-status-job-' . $server->id)->everyMinute()->onOneServer();
|
||||
// }
|
||||
}
|
||||
private function instance_auto_update($schedule)
|
||||
{
|
||||
@@ -152,6 +123,9 @@ class Kernel extends ConsoleKernel
|
||||
return;
|
||||
}
|
||||
foreach ($scheduled_tasks as $scheduled_task) {
|
||||
if ($scheduled_task->enabled === false) {
|
||||
continue;
|
||||
}
|
||||
$service = $scheduled_task->service;
|
||||
$application = $scheduled_task->application;
|
||||
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Actions\Database\StartClickhouse;
|
||||
use App\Actions\Database\StartDragonfly;
|
||||
use App\Actions\Database\StartKeydb;
|
||||
use App\Actions\Database\StartMariadb;
|
||||
use App\Actions\Database\StartMongodb;
|
||||
use App\Actions\Database\StartMysql;
|
||||
@@ -44,7 +47,7 @@ class Deploy extends Controller
|
||||
$force = $request->query->get('force') ?? false;
|
||||
|
||||
if ($uuids && $tags) {
|
||||
return response()->json(['error' => 'You can only use uuid or tag, not both.', 'docs' => 'https://coolify.io/docs/api/deploy-webhook'], 400);
|
||||
return response()->json(['error' => 'You can only use uuid or tag, not both.', 'docs' => 'https://coolify.io/docs/api-reference/deploy-webhook'], 400);
|
||||
}
|
||||
if (is_null($teamId)) {
|
||||
return invalid_token();
|
||||
@@ -54,7 +57,7 @@ class Deploy extends Controller
|
||||
} else if ($uuids) {
|
||||
return $this->by_uuids($uuids, $teamId, $force);
|
||||
}
|
||||
return response()->json(['error' => 'You must provide uuid or tag.', 'docs' => 'https://coolify.io/docs/api/deploy-webhook'], 400);
|
||||
return response()->json(['error' => 'You must provide uuid or tag.', 'docs' => 'https://coolify.io/docs/api-reference/deploy-webhook'], 400);
|
||||
}
|
||||
private function by_uuids(string $uuid, int $teamId, bool $force = false)
|
||||
{
|
||||
@@ -62,7 +65,7 @@ class Deploy extends Controller
|
||||
$uuids = collect(array_filter($uuids));
|
||||
|
||||
if (count($uuids) === 0) {
|
||||
return response()->json(['error' => 'No UUIDs provided.', 'docs' => 'https://coolify.io/docs/api/deploy-webhook'], 400);
|
||||
return response()->json(['error' => 'No UUIDs provided.', 'docs' => 'https://coolify.io/docs/api-reference/deploy-webhook'], 400);
|
||||
}
|
||||
$deployments = collect();
|
||||
$payload = collect();
|
||||
@@ -81,7 +84,7 @@ class Deploy extends Controller
|
||||
$payload->put('deployments', $deployments->toArray());
|
||||
return response()->json($payload->toArray(), 200);
|
||||
}
|
||||
return response()->json(['error' => "No resources found.", 'docs' => 'https://coolify.io/docs/api/deploy-webhook'], 404);
|
||||
return response()->json(['error' => "No resources found.", 'docs' => 'https://coolify.io/docs/api-reference/deploy-webhook'], 404);
|
||||
}
|
||||
public function by_tags(string $tags, int $team_id, bool $force = false)
|
||||
{
|
||||
@@ -89,7 +92,7 @@ class Deploy extends Controller
|
||||
$tags = collect(array_filter($tags));
|
||||
|
||||
if (count($tags) === 0) {
|
||||
return response()->json(['error' => 'No TAGs provided.', 'docs' => 'https://coolify.io/docs/api/deploy-webhook'], 400);
|
||||
return response()->json(['error' => 'No TAGs provided.', 'docs' => 'https://coolify.io/docs/api-reference/deploy-webhook'], 400);
|
||||
}
|
||||
$message = collect([]);
|
||||
$deployments = collect();
|
||||
@@ -127,7 +130,7 @@ class Deploy extends Controller
|
||||
return response()->json($payload->toArray(), 200);
|
||||
}
|
||||
|
||||
return response()->json(['error' => "No resources found with this tag.", 'docs' => 'https://coolify.io/docs/api/deploy-webhook'], 404);
|
||||
return response()->json(['error' => "No resources found with this tag.", 'docs' => 'https://coolify.io/docs/api-reference/deploy-webhook'], 404);
|
||||
}
|
||||
public function deploy_resource($resource, bool $force = false): array
|
||||
{
|
||||
@@ -157,6 +160,24 @@ class Deploy extends Controller
|
||||
'started_at' => now(),
|
||||
]);
|
||||
$message = "Database {$resource->name} started.";
|
||||
} else if ($type === 'App\Models\StandaloneKeydb') {
|
||||
StartKeydb::run($resource);
|
||||
$resource->update([
|
||||
'started_at' => now(),
|
||||
]);
|
||||
$message = "Database {$resource->name} started.";
|
||||
} else if ($type === 'App\Models\StandaloneDragonfly') {
|
||||
StartDragonfly::run($resource);
|
||||
$resource->update([
|
||||
'started_at' => now(),
|
||||
]);
|
||||
$message = "Database {$resource->name} started.";
|
||||
} else if ($type === 'App\Models\StandaloneClickhouse') {
|
||||
StartClickhouse::run($resource);
|
||||
$resource->update([
|
||||
'started_at' => now(),
|
||||
]);
|
||||
$message = "Database {$resource->name} started.";
|
||||
} else if ($type === 'App\Models\StandaloneMongodb') {
|
||||
StartMongodb::run($resource);
|
||||
$resource->update([
|
||||
|
||||
@@ -26,7 +26,7 @@ class Team extends Controller
|
||||
$teams = auth()->user()->teams;
|
||||
$team = $teams->where('id', $id)->first();
|
||||
if (is_null($team)) {
|
||||
return response()->json(['error' => 'Team not found.', "docs" => "https://coolify.io/docs/api/team-by-id"], 404);
|
||||
return response()->json(['error' => 'Team not found.', "docs" => "https://coolify.io/docs/api-reference/get-team-by-teamid"], 404);
|
||||
}
|
||||
return response()->json($team);
|
||||
}
|
||||
@@ -40,7 +40,7 @@ class Team extends Controller
|
||||
$teams = auth()->user()->teams;
|
||||
$team = $teams->where('id', $id)->first();
|
||||
if (is_null($team)) {
|
||||
return response()->json(['error' => 'Team not found.', "docs" => "https://coolify.io/docs/api/team-by-id-members"], 404);
|
||||
return response()->json(['error' => 'Team not found.', "docs" => "https://coolify.io/docs/api-reference/get-team-by-teamid-members"], 404);
|
||||
}
|
||||
return response()->json($team->members);
|
||||
}
|
||||
|
||||
83
app/Http/Controllers/UploadController.php
Normal file
83
app/Http/Controllers/UploadController.php
Normal file
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Illuminate\Routing\Controller as BaseController;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Pion\Laravel\ChunkUpload\Exceptions\UploadFailedException;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\UploadedFile;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Pion\Laravel\ChunkUpload\Exceptions\UploadMissingFileException;
|
||||
use Pion\Laravel\ChunkUpload\Handler\AbstractHandler;
|
||||
use Pion\Laravel\ChunkUpload\Handler\HandlerFactory;
|
||||
use Pion\Laravel\ChunkUpload\Receiver\FileReceiver;
|
||||
|
||||
class UploadController extends BaseController
|
||||
{
|
||||
public function upload(Request $request)
|
||||
{
|
||||
$resource = getResourceByUuid(request()->route('databaseUuid'), data_get(auth()->user()->currentTeam(), 'id'));
|
||||
if (is_null($resource)) {
|
||||
return response()->json(['error' => 'You do not have permission for this database'], 500);
|
||||
}
|
||||
$receiver = new FileReceiver("file", $request, HandlerFactory::classFromRequest($request));
|
||||
|
||||
if ($receiver->isUploaded() === false) {
|
||||
throw new UploadMissingFileException();
|
||||
}
|
||||
|
||||
$save = $receiver->receive();
|
||||
|
||||
if ($save->isFinished()) {
|
||||
return $this->saveFile($save->getFile(), $resource);
|
||||
}
|
||||
|
||||
$handler = $save->handler();
|
||||
return response()->json([
|
||||
"done" => $handler->getPercentageDone(),
|
||||
'status' => true
|
||||
]);
|
||||
}
|
||||
// protected function saveFileToS3($file)
|
||||
// {
|
||||
// $fileName = $this->createFilename($file);
|
||||
|
||||
// $disk = Storage::disk('s3');
|
||||
// // It's better to use streaming Streaming (laravel 5.4+)
|
||||
// $disk->putFileAs('photos', $file, $fileName);
|
||||
|
||||
// // for older laravel
|
||||
// // $disk->put($fileName, file_get_contents($file), 'public');
|
||||
// $mime = str_replace('/', '-', $file->getMimeType());
|
||||
|
||||
// // We need to delete the file when uploaded to s3
|
||||
// unlink($file->getPathname());
|
||||
|
||||
// return response()->json([
|
||||
// 'path' => $disk->url($fileName),
|
||||
// 'name' => $fileName,
|
||||
// 'mime_type' => $mime
|
||||
// ]);
|
||||
// }
|
||||
protected function saveFile(UploadedFile $file, $resource)
|
||||
{
|
||||
$mime = str_replace('/', '-', $file->getMimeType());
|
||||
$filePath = "upload/{$resource->uuid}";
|
||||
$finalPath = storage_path("app/" . $filePath);
|
||||
$file->move($finalPath, 'restore');
|
||||
|
||||
return response()->json([
|
||||
'mime_type' => $mime
|
||||
]);
|
||||
}
|
||||
protected function createFilename(UploadedFile $file)
|
||||
{
|
||||
$extension = $file->getClientOriginalExtension();
|
||||
$filename = str_replace("." . $extension, "", $file->getClientOriginalName()); // Filename without extension
|
||||
|
||||
$filename .= "_" . md5(time()) . "." . $extension;
|
||||
|
||||
return $filename;
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,6 @@ class Github extends Controller
|
||||
public function manual(Request $request)
|
||||
{
|
||||
try {
|
||||
ray($request);
|
||||
$return_payloads = collect([]);
|
||||
$x_github_delivery = request()->header('X-GitHub-Delivery');
|
||||
if (app()->isDownForMaintenance()) {
|
||||
@@ -68,6 +67,10 @@ class Github extends Controller
|
||||
if (Str::isMatch('/refs\/heads\/*/', $branch)) {
|
||||
$branch = Str::after($branch, 'refs/heads/');
|
||||
}
|
||||
$added_files = data_get($payload, 'commits.*.added');
|
||||
$removed_files = data_get($payload, 'commits.*.removed');
|
||||
$modified_files = data_get($payload, 'commits.*.modified');
|
||||
$changed_files = collect($added_files)->concat($removed_files)->concat($modified_files)->unique()->flatten();
|
||||
ray('Manual Webhook GitHub Push Event with branch: ' . $branch);
|
||||
}
|
||||
if ($x_github_event === 'pull_request') {
|
||||
@@ -118,24 +121,41 @@ class Github extends Controller
|
||||
}
|
||||
if ($x_github_event === 'push') {
|
||||
if ($application->isDeployable()) {
|
||||
ray('Deploying ' . $application->name . ' with branch ' . $branch);
|
||||
$deployment_uuid = new Cuid2(7);
|
||||
queue_application_deployment(
|
||||
application: $application,
|
||||
deployment_uuid: $deployment_uuid,
|
||||
force_rebuild: false,
|
||||
is_webhook: true,
|
||||
);
|
||||
$return_payloads->push([
|
||||
'application' => $application->name,
|
||||
'status' => 'success',
|
||||
'message' => 'Deployment queued.',
|
||||
]);
|
||||
$is_watch_path_triggered = $application->isWatchPathsTriggered($changed_files);
|
||||
if ($is_watch_path_triggered || is_null($application->watch_paths)) {
|
||||
ray('Deploying ' . $application->name . ' with branch ' . $branch);
|
||||
$deployment_uuid = new Cuid2(7);
|
||||
queue_application_deployment(
|
||||
application: $application,
|
||||
deployment_uuid: $deployment_uuid,
|
||||
force_rebuild: false,
|
||||
is_webhook: true,
|
||||
);
|
||||
$return_payloads->push([
|
||||
'status' => 'success',
|
||||
'message' => 'Deployment queued.',
|
||||
'application_uuid' => $application->uuid,
|
||||
'application_name' => $application->name,
|
||||
]);
|
||||
} else {
|
||||
$paths = str($application->watch_paths)->explode("\n");
|
||||
$return_payloads->push([
|
||||
'status' => 'failed',
|
||||
'message' => 'Changed files do not match watch paths. Ignoring deployment.',
|
||||
'application_uuid' => $application->uuid,
|
||||
'application_name' => $application->name,
|
||||
'details' => [
|
||||
'changed_files' => $changed_files,
|
||||
'watch_paths' => $paths,
|
||||
],
|
||||
]);
|
||||
}
|
||||
} else {
|
||||
$return_payloads->push([
|
||||
'application' => $application->name,
|
||||
'status' => 'failed',
|
||||
'message' => 'Deployments disabled.',
|
||||
'application_uuid' => $application->uuid,
|
||||
'application_name' => $application->name,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -266,6 +286,10 @@ class Github extends Controller
|
||||
if (Str::isMatch('/refs\/heads\/*/', $branch)) {
|
||||
$branch = Str::after($branch, 'refs/heads/');
|
||||
}
|
||||
$added_files = data_get($payload, 'commits.*.added');
|
||||
$removed_files = data_get($payload, 'commits.*.removed');
|
||||
$modified_files = data_get($payload, 'commits.*.modified');
|
||||
$changed_files = collect($added_files)->concat($removed_files)->concat($modified_files)->unique()->flatten();
|
||||
ray('Webhook GitHub Push Event: ' . $id . ' with branch: ' . $branch);
|
||||
}
|
||||
if ($x_github_event === 'pull_request') {
|
||||
@@ -298,32 +322,50 @@ class Github extends Controller
|
||||
$isFunctional = $application->destination->server->isFunctional();
|
||||
if (!$isFunctional) {
|
||||
$return_payloads->push([
|
||||
'application' => $application->name,
|
||||
'status' => 'failed',
|
||||
'message' => 'Server is not functional.',
|
||||
'application_uuid' => $application->uuid,
|
||||
'application_name' => $application->name,
|
||||
]);
|
||||
continue;
|
||||
}
|
||||
if ($x_github_event === 'push') {
|
||||
if ($application->isDeployable()) {
|
||||
ray('Deploying ' . $application->name . ' with branch ' . $branch);
|
||||
$deployment_uuid = new Cuid2(7);
|
||||
queue_application_deployment(
|
||||
application: $application,
|
||||
deployment_uuid: $deployment_uuid,
|
||||
force_rebuild: false,
|
||||
is_webhook: true
|
||||
);
|
||||
$return_payloads->push([
|
||||
'application' => $application->name,
|
||||
'status' => 'success',
|
||||
'message' => 'Deployment queued.',
|
||||
]);
|
||||
$is_watch_path_triggered = $application->isWatchPathsTriggered($changed_files);
|
||||
if ($is_watch_path_triggered || is_null($application->watch_paths)) {
|
||||
ray('Deploying ' . $application->name . ' with branch ' . $branch);
|
||||
$deployment_uuid = new Cuid2(7);
|
||||
queue_application_deployment(
|
||||
application: $application,
|
||||
deployment_uuid: $deployment_uuid,
|
||||
force_rebuild: false,
|
||||
is_webhook: true,
|
||||
);
|
||||
$return_payloads->push([
|
||||
'status' => 'success',
|
||||
'message' => 'Deployment queued.',
|
||||
'application_uuid' => $application->uuid,
|
||||
'application_name' => $application->name,
|
||||
]);
|
||||
} else {
|
||||
$paths = str($application->watch_paths)->explode("\n");
|
||||
$return_payloads->push([
|
||||
'status' => 'failed',
|
||||
'message' => 'Changed files do not match watch paths. Ignoring deployment.',
|
||||
'application_uuid' => $application->uuid,
|
||||
'application_name' => $application->name,
|
||||
'details' => [
|
||||
'changed_files' => $changed_files,
|
||||
'watch_paths' => $paths,
|
||||
],
|
||||
]);
|
||||
}
|
||||
} else {
|
||||
$return_payloads->push([
|
||||
'application' => $application->name,
|
||||
'status' => 'failed',
|
||||
'message' => 'Deployments disabled.',
|
||||
'application_uuid' => $application->uuid,
|
||||
'application_name' => $application->name,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,6 +51,10 @@ class Gitlab extends Controller
|
||||
]);
|
||||
return response($return_payloads);
|
||||
}
|
||||
$added_files = data_get($payload, 'commits.*.added');
|
||||
$removed_files = data_get($payload, 'commits.*.removed');
|
||||
$modified_files = data_get($payload, 'commits.*.modified');
|
||||
$changed_files = collect($added_files)->concat($removed_files)->concat($modified_files)->unique()->flatten();
|
||||
ray('Manual Webhook GitLab Push Event with branch: ' . $branch);
|
||||
}
|
||||
if ($x_gitlab_event === 'merge_request') {
|
||||
@@ -113,19 +117,41 @@ class Gitlab extends Controller
|
||||
}
|
||||
if ($x_gitlab_event === 'push') {
|
||||
if ($application->isDeployable()) {
|
||||
ray('Deploying ' . $application->name . ' with branch ' . $branch);
|
||||
$deployment_uuid = new Cuid2(7);
|
||||
queue_application_deployment(
|
||||
application: $application,
|
||||
deployment_uuid: $deployment_uuid,
|
||||
force_rebuild: false,
|
||||
is_webhook: true
|
||||
);
|
||||
$is_watch_path_triggered = $application->isWatchPathsTriggered($changed_files);
|
||||
if ($is_watch_path_triggered || is_null($application->watch_paths)) {
|
||||
ray('Deploying ' . $application->name . ' with branch ' . $branch);
|
||||
$deployment_uuid = new Cuid2(7);
|
||||
queue_application_deployment(
|
||||
application: $application,
|
||||
deployment_uuid: $deployment_uuid,
|
||||
force_rebuild: false,
|
||||
is_webhook: true,
|
||||
);
|
||||
$return_payloads->push([
|
||||
'status' => 'success',
|
||||
'message' => 'Deployment queued.',
|
||||
'application_uuid' => $application->uuid,
|
||||
'application_name' => $application->name,
|
||||
]);
|
||||
} else {
|
||||
$paths = str($application->watch_paths)->explode("\n");
|
||||
$return_payloads->push([
|
||||
'status' => 'failed',
|
||||
'message' => 'Changed files do not match watch paths. Ignoring deployment.',
|
||||
'application_uuid' => $application->uuid,
|
||||
'application_name' => $application->name,
|
||||
'details' => [
|
||||
'changed_files' => $changed_files,
|
||||
'watch_paths' => $paths,
|
||||
],
|
||||
]);
|
||||
}
|
||||
} else {
|
||||
$return_payloads->push([
|
||||
'application' => $application->name,
|
||||
'status' => 'failed',
|
||||
'message' => 'Deployments disabled',
|
||||
'application_uuid' => $application->uuid,
|
||||
'application_name' => $application->name,
|
||||
]);
|
||||
ray('Deployments disabled for ' . $application->name);
|
||||
}
|
||||
|
||||
@@ -159,8 +159,12 @@ class Stripe extends Controller
|
||||
$feedback = data_get($data, 'cancellation_details.feedback');
|
||||
$comment = data_get($data, 'cancellation_details.comment');
|
||||
$lookup_key = data_get($data, 'items.data.0.price.lookup_key');
|
||||
if (str($lookup_key)->contains('ultimate')) {
|
||||
$quantity = data_get($data, 'items.data.0.quantity', 10);
|
||||
if (str($lookup_key)->contains('ultimate') || str($lookup_key)->contains('dynamic')) {
|
||||
if (str($lookup_key)->contains('dynamic')) {
|
||||
$quantity = data_get($data, 'items.data.0.quantity', 2);
|
||||
} else {
|
||||
$quantity = data_get($data, 'items.data.0.quantity', 10);
|
||||
}
|
||||
$team = data_get($subscription, 'team');
|
||||
$team->update([
|
||||
'custom_server_limit' => $quantity,
|
||||
|
||||
@@ -83,6 +83,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
private $env_nixpacks_args;
|
||||
private $docker_compose;
|
||||
private $docker_compose_base64;
|
||||
private ?string $env_filename = null;
|
||||
private ?string $nixpacks_plan = null;
|
||||
private ?string $nixpacks_type = null;
|
||||
private string $dockerfile_location = '/Dockerfile';
|
||||
@@ -179,6 +180,11 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
|
||||
public function handle(): void
|
||||
{
|
||||
if (!$this->server->isFunctional()) {
|
||||
$this->application_deployment_queue->addLogEntry("Server is not functional.");
|
||||
$this->fail("Server is not functional.");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
// Generate custom host<->ip mapping
|
||||
$allContainers = instant_remote_process(["docker network inspect {$this->destination->network} -f '{{json .Containers}}' "], $this->server);
|
||||
@@ -234,50 +240,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
$this->build_server = $this->server;
|
||||
$this->original_server = $this->server;
|
||||
}
|
||||
if ($this->restart_only && $this->application->build_pack !== 'dockerimage') {
|
||||
$this->just_restart();
|
||||
if ($this->server->isProxyShouldRun()) {
|
||||
dispatch(new ContainerStatusJob($this->server));
|
||||
}
|
||||
$this->next(ApplicationDeploymentStatus::FINISHED->value);
|
||||
$this->application->isConfigurationChanged(false);
|
||||
$this->run_post_deployment_command();
|
||||
return;
|
||||
} else if ($this->pull_request_id !== 0) {
|
||||
$this->deploy_pull_request();
|
||||
} else if ($this->application->dockerfile) {
|
||||
$this->deploy_simple_dockerfile();
|
||||
} else if ($this->application->build_pack === 'dockercompose') {
|
||||
$this->deploy_docker_compose_buildpack();
|
||||
} else if ($this->application->build_pack === 'dockerimage') {
|
||||
$this->deploy_dockerimage_buildpack();
|
||||
} else if ($this->application->build_pack === 'dockerfile') {
|
||||
$this->deploy_dockerfile_buildpack();
|
||||
} else if ($this->application->build_pack === 'static') {
|
||||
$this->deploy_static_buildpack();
|
||||
} else {
|
||||
$this->deploy_nixpacks_buildpack();
|
||||
}
|
||||
if ($this->server->isProxyShouldRun()) {
|
||||
dispatch(new ContainerStatusJob($this->server));
|
||||
}
|
||||
// Otherwise built image needs to be pushed before from the build server.
|
||||
// ray($this->use_build_server);
|
||||
// if (!$this->use_build_server) {
|
||||
// if ($this->application->additional_servers->count() > 0) {
|
||||
// $this->push_to_docker_registry(forceFail: true);
|
||||
// } else {
|
||||
// $this->push_to_docker_registry();
|
||||
// }
|
||||
// }
|
||||
$this->next(ApplicationDeploymentStatus::FINISHED->value);
|
||||
if ($this->pull_request_id !== 0) {
|
||||
if ($this->application->is_github_based()) {
|
||||
ApplicationPullRequestUpdateJob::dispatch(application: $this->application, preview: $this->preview, deployment_uuid: $this->deployment_uuid, status: ProcessStatus::FINISHED);
|
||||
}
|
||||
}
|
||||
$this->run_post_deployment_command();
|
||||
$this->application->isConfigurationChanged(true);
|
||||
$this->decide_what_to_do();
|
||||
} catch (Exception $e) {
|
||||
if ($this->pull_request_id !== 0 && $this->application->is_github_based()) {
|
||||
ApplicationPullRequestUpdateJob::dispatch(application: $this->application, preview: $this->preview, deployment_uuid: $this->deployment_uuid, status: ProcessStatus::ERROR);
|
||||
@@ -312,6 +275,43 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
ApplicationStatusChanged::dispatch(data_get($this->application, 'environment.project.team.id'));
|
||||
}
|
||||
}
|
||||
private function decide_what_to_do()
|
||||
{
|
||||
if ($this->restart_only && $this->application->build_pack !== 'dockerimage' && $this->application->build_pack !== 'dockerfile') {
|
||||
$this->just_restart();
|
||||
return;
|
||||
} else if ($this->pull_request_id !== 0) {
|
||||
$this->deploy_pull_request();
|
||||
} else if ($this->application->dockerfile) {
|
||||
$this->deploy_simple_dockerfile();
|
||||
} else if ($this->application->build_pack === 'dockercompose') {
|
||||
$this->deploy_docker_compose_buildpack();
|
||||
} else if ($this->application->build_pack === 'dockerimage') {
|
||||
$this->deploy_dockerimage_buildpack();
|
||||
} else if ($this->application->build_pack === 'dockerfile') {
|
||||
$this->deploy_dockerfile_buildpack();
|
||||
} else if ($this->application->build_pack === 'static') {
|
||||
$this->deploy_static_buildpack();
|
||||
} else {
|
||||
$this->deploy_nixpacks_buildpack();
|
||||
}
|
||||
$this->post_deployment();
|
||||
}
|
||||
private function post_deployment()
|
||||
{
|
||||
|
||||
if ($this->server->isProxyShouldRun()) {
|
||||
dispatch(new ContainerStatusJob($this->server));
|
||||
}
|
||||
$this->next(ApplicationDeploymentStatus::FINISHED->value);
|
||||
if ($this->pull_request_id !== 0) {
|
||||
if ($this->application->is_github_based()) {
|
||||
ApplicationPullRequestUpdateJob::dispatch(application: $this->application, preview: $this->preview, deployment_uuid: $this->deployment_uuid, status: ProcessStatus::FINISHED);
|
||||
}
|
||||
}
|
||||
$this->run_post_deployment_command();
|
||||
$this->application->isConfigurationChanged(true);
|
||||
}
|
||||
private function deploy_simple_dockerfile()
|
||||
{
|
||||
if ($this->use_build_server) {
|
||||
@@ -326,17 +326,18 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
],
|
||||
);
|
||||
$this->generate_image_names();
|
||||
if (!$this->force_rebuild) {
|
||||
$this->check_image_locally_or_remotely();
|
||||
if (str($this->saved_outputs->get('local_image_found'))->isNotEmpty() && !$this->application->isConfigurationChanged()) {
|
||||
$this->create_workdir();
|
||||
$this->application_deployment_queue->addLogEntry("No configuration changed & image found ({$this->production_image_name}) with the same Git Commit SHA. Build step skipped.");
|
||||
$this->generate_compose_file();
|
||||
$this->push_to_docker_registry();
|
||||
$this->rolling_update();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Always rebuild dockerfile based container.
|
||||
// if (!$this->force_rebuild) {
|
||||
// $this->check_image_locally_or_remotely();
|
||||
// if (str($this->saved_outputs->get('local_image_found'))->isNotEmpty() && !$this->application->isConfigurationChanged()) {
|
||||
// $this->application_deployment_queue->addLogEntry("No configuration changed & image found ({$this->production_image_name}) with the same Git Commit SHA. Build step skipped.");
|
||||
// $this->generate_compose_file();
|
||||
// $this->push_to_docker_registry();
|
||||
// $this->rolling_update();
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
$this->generate_compose_file();
|
||||
$this->generate_build_env_variables();
|
||||
$this->add_build_env_variables_to_dockerfile();
|
||||
@@ -470,7 +471,6 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
if (!$this->force_rebuild) {
|
||||
$this->check_image_locally_or_remotely();
|
||||
if (str($this->saved_outputs->get('local_image_found'))->isNotEmpty() && !$this->application->isConfigurationChanged()) {
|
||||
$this->create_workdir();
|
||||
$this->application_deployment_queue->addLogEntry("No configuration changed & image found ({$this->production_image_name}) with the same Git Commit SHA. Build step skipped.");
|
||||
$this->generate_compose_file();
|
||||
$this->push_to_docker_registry();
|
||||
@@ -499,7 +499,6 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
if (!$this->force_rebuild) {
|
||||
$this->check_image_locally_or_remotely();
|
||||
if (str($this->saved_outputs->get('local_image_found'))->isNotEmpty() && !$this->application->isConfigurationChanged()) {
|
||||
$this->create_workdir();
|
||||
$this->application_deployment_queue->addLogEntry("No configuration changed & image found ({$this->production_image_name}) with the same Git Commit SHA. Build step skipped.");
|
||||
$this->generate_compose_file();
|
||||
ray('pushing to docker registry');
|
||||
@@ -533,7 +532,6 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
if (!$this->force_rebuild) {
|
||||
$this->check_image_locally_or_remotely();
|
||||
if (str($this->saved_outputs->get('local_image_found'))->isNotEmpty() && !$this->application->isConfigurationChanged()) {
|
||||
$this->create_workdir();
|
||||
$this->application_deployment_queue->addLogEntry("No configuration changed & image found ({$this->production_image_name}) with the same Git Commit SHA. Build step skipped.");
|
||||
$this->generate_compose_file();
|
||||
$this->push_to_docker_registry();
|
||||
@@ -644,21 +642,21 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
{
|
||||
if ($this->application->dockerfile) {
|
||||
if ($this->application->docker_registry_image_name) {
|
||||
$this->build_image_name = Str::lower("{$this->application->docker_registry_image_name}:build");
|
||||
$this->production_image_name = Str::lower("{$this->application->docker_registry_image_name}:latest");
|
||||
$this->build_image_name = "{$this->application->docker_registry_image_name}:build";
|
||||
$this->production_image_name = "{$this->application->docker_registry_image_name}:latest";
|
||||
} else {
|
||||
$this->build_image_name = Str::lower("{$this->application->uuid}:build");
|
||||
$this->production_image_name = Str::lower("{$this->application->uuid}:latest");
|
||||
$this->build_image_name = "{$this->application->uuid}:build";
|
||||
$this->production_image_name = "{$this->application->uuid}:latest";
|
||||
}
|
||||
} else if ($this->application->build_pack === 'dockerimage') {
|
||||
$this->production_image_name = Str::lower("{$this->dockerImage}:{$this->dockerImageTag}");
|
||||
$this->production_image_name = "{$this->dockerImage}:{$this->dockerImageTag}";
|
||||
} else if ($this->pull_request_id !== 0) {
|
||||
if ($this->application->docker_registry_image_name) {
|
||||
$this->build_image_name = Str::lower("{$this->application->docker_registry_image_name}:pr-{$this->pull_request_id}-build");
|
||||
$this->production_image_name = Str::lower("{$this->application->docker_registry_image_name}:pr-{$this->pull_request_id}");
|
||||
$this->build_image_name = "{$this->application->docker_registry_image_name}:pr-{$this->pull_request_id}-build";
|
||||
$this->production_image_name = "{$this->application->docker_registry_image_name}:pr-{$this->pull_request_id}";
|
||||
} else {
|
||||
$this->build_image_name = Str::lower("{$this->application->uuid}:pr-{$this->pull_request_id}-build");
|
||||
$this->production_image_name = Str::lower("{$this->application->uuid}:pr-{$this->pull_request_id}");
|
||||
$this->build_image_name = "{$this->application->uuid}:pr-{$this->pull_request_id}-build";
|
||||
$this->production_image_name = "{$this->application->uuid}:pr-{$this->pull_request_id}";
|
||||
}
|
||||
} else {
|
||||
$this->dockerImageTag = str($this->commit)->substr(0, 128);
|
||||
@@ -666,17 +664,17 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
$this->dockerImageTag = $this->application->docker_registry_image_tag;
|
||||
}
|
||||
if ($this->application->docker_registry_image_name) {
|
||||
$this->build_image_name = Str::lower("{$this->application->docker_registry_image_name}:{$this->dockerImageTag}-build");
|
||||
$this->production_image_name = Str::lower("{$this->application->docker_registry_image_name}:{$this->dockerImageTag}");
|
||||
$this->build_image_name = "{$this->application->docker_registry_image_name}:{$this->dockerImageTag}-build";
|
||||
$this->production_image_name = "{$this->application->docker_registry_image_name}:{$this->dockerImageTag}";
|
||||
} else {
|
||||
$this->build_image_name = Str::lower("{$this->application->uuid}:{$this->dockerImageTag}-build");
|
||||
$this->production_image_name = Str::lower("{$this->application->uuid}:{$this->dockerImageTag}");
|
||||
$this->build_image_name = "{$this->application->uuid}:{$this->dockerImageTag}-build";
|
||||
$this->production_image_name = "{$this->application->uuid}:{$this->dockerImageTag}";
|
||||
}
|
||||
}
|
||||
}
|
||||
private function just_restart()
|
||||
{
|
||||
$this->application_deployment_queue->addLogEntry("Starting deployment of {$this->customRepository}:{$this->application->git_branch} to {$this->server->name}.");
|
||||
$this->application_deployment_queue->addLogEntry("Restarting {$this->customRepository}:{$this->application->git_branch} on {$this->server->name}.");
|
||||
$this->prepare_builder_image();
|
||||
$this->check_git_if_build_needed();
|
||||
$this->set_base_dir();
|
||||
@@ -684,12 +682,14 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
$this->check_image_locally_or_remotely();
|
||||
if (str($this->saved_outputs->get('local_image_found'))->isNotEmpty()) {
|
||||
$this->application_deployment_queue->addLogEntry("Image found ({$this->production_image_name}) with the same Git Commit SHA. Restarting container.");
|
||||
$this->create_workdir();
|
||||
$this->generate_compose_file();
|
||||
$this->rolling_update();
|
||||
return;
|
||||
$this->post_deployment();
|
||||
} else {
|
||||
$this->application_deployment_queue->addLogEntry("Image not found ({$this->production_image_name}). Redeploying the application.");
|
||||
$this->restart_only = false;
|
||||
$this->decide_what_to_do();
|
||||
}
|
||||
throw new RuntimeException('Cannot find image anywhere. Please redeploy the application.');
|
||||
}
|
||||
private function check_image_locally_or_remotely()
|
||||
{
|
||||
@@ -709,19 +709,35 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
{
|
||||
$envs = collect([]);
|
||||
if ($this->pull_request_id !== 0) {
|
||||
$this->env_filename = ".env-pr-$this->pull_request_id";
|
||||
foreach ($this->application->environment_variables_preview as $env) {
|
||||
$envs->push($env->key . '=' . $env->real_value);
|
||||
}
|
||||
} else {
|
||||
$this->env_filename = ".env";
|
||||
foreach ($this->application->environment_variables as $env) {
|
||||
$envs->push($env->key . '=' . $env->real_value);
|
||||
}
|
||||
}
|
||||
if ($envs->isEmpty()) {
|
||||
$this->env_filename = null;
|
||||
$this->execute_remote_command(
|
||||
[
|
||||
"command" => "rm -f $this->configuration_dir/{$this->env_filename}",
|
||||
"hidden" => true,
|
||||
"ignore_errors" => true
|
||||
]
|
||||
);
|
||||
return;
|
||||
}
|
||||
$envs_base64 = base64_encode($envs->implode("\n"));
|
||||
$this->execute_remote_command(
|
||||
[
|
||||
executeInDocker($this->deployment_uuid, "echo '$envs_base64' | base64 -d > $this->workdir/.env")
|
||||
],
|
||||
[
|
||||
"echo '$envs_base64' | base64 -d > $this->configuration_dir/{$this->env_filename}"
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
@@ -737,7 +753,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
$nixpacks_php_root_dir = $this->application->environment_variables_preview->where('key', 'NIXPACKS_PHP_ROOT_DIR')->first();
|
||||
}
|
||||
if ($nixpacks_php_fallback_path?->value === '/index.php' && $nixpacks_php_root_dir?->value === '/app/public' && $this->newVersionIsHealthy === false) {
|
||||
$this->application_deployment_queue->addLogEntry("There was a change in how Laravel is deployed. Please update your environment variables to match the new deployment method. More details here: https://coolify.io/docs/frameworks/laravel#requirements", 'stderr');
|
||||
$this->application_deployment_queue->addLogEntry("There was a change in how Laravel is deployed. Please update your environment variables to match the new deployment method. More details here: https://coolify.io/docs/resources/laravel", 'stderr');
|
||||
}
|
||||
}
|
||||
private function rolling_update()
|
||||
@@ -861,6 +877,9 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
[
|
||||
"command" => executeInDocker($this->deployment_uuid, "mkdir -p {$this->workdir}")
|
||||
],
|
||||
[
|
||||
"command" => "mkdir -p {$this->configuration_dir}"
|
||||
],
|
||||
);
|
||||
}
|
||||
private function prepare_builder_image()
|
||||
@@ -883,6 +902,11 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
}
|
||||
$this->application_deployment_queue->addLogEntry("Preparing container with helper image: $helperImage.");
|
||||
$this->execute_remote_command(
|
||||
[
|
||||
"command" => "docker rm -f {$this->deployment_uuid}",
|
||||
"ignore_errors" => true,
|
||||
"hidden" => true
|
||||
],
|
||||
[
|
||||
$runCommand,
|
||||
"hidden" => true,
|
||||
@@ -898,6 +922,9 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
if ($this->application->additional_networks->count() === 0) {
|
||||
return;
|
||||
}
|
||||
if ($this->pull_request_id !== 0) {
|
||||
return;
|
||||
}
|
||||
$destination_ids = $this->application->additional_networks->pluck('id');
|
||||
if ($this->server->isSwarm()) {
|
||||
$this->application_deployment_queue->addLogEntry("Additional destinations are not supported in swarm mode.");
|
||||
@@ -983,6 +1010,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
$importCommands, "hidden" => true
|
||||
]
|
||||
);
|
||||
$this->create_workdir();
|
||||
}
|
||||
|
||||
private function generate_git_import_commands()
|
||||
@@ -996,11 +1024,6 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
return $commands;
|
||||
}
|
||||
|
||||
private function set_git_import_settings($git_clone_command)
|
||||
{
|
||||
return $this->application->setGitImportSettings($this->deployment_uuid, $git_clone_command);
|
||||
}
|
||||
|
||||
private function cleanup_git()
|
||||
{
|
||||
$this->execute_remote_command(
|
||||
@@ -1093,6 +1116,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
|
||||
private function generate_compose_file()
|
||||
{
|
||||
$this->create_workdir();
|
||||
$ports = $this->application->settings->is_static ? [80] : $this->application->ports_exposes_array;
|
||||
$onlyPort = null;
|
||||
if (count($ports) > 0) {
|
||||
@@ -1176,6 +1200,11 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
]
|
||||
]
|
||||
];
|
||||
if ($this->env_filename) {
|
||||
$docker_compose['services'][$this->container_name]['env_file'] = [
|
||||
$this->env_filename
|
||||
];
|
||||
}
|
||||
if (!$this->custom_healthcheck_found) {
|
||||
$docker_compose['services'][$this->container_name]['healthcheck'] = [
|
||||
'test' => [
|
||||
@@ -1329,13 +1358,18 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
$this->docker_compose = Yaml::dump($docker_compose, 10);
|
||||
$this->docker_compose_base64 = base64_encode($this->docker_compose);
|
||||
$this->execute_remote_command([executeInDocker($this->deployment_uuid, "echo '{$this->docker_compose_base64}' | base64 -d > {$this->workdir}/docker-compose.yml"), "hidden" => true]);
|
||||
$this->save_environment_variables();
|
||||
}
|
||||
|
||||
private function generate_local_persistent_volumes()
|
||||
{
|
||||
$local_persistent_volumes = [];
|
||||
foreach ($this->application->persistentStorages as $persistentStorage) {
|
||||
$volume_name = $persistentStorage->host_path ?? $persistentStorage->name;
|
||||
if ($persistentStorage->host_path !== '' && $persistentStorage->host_path !== null) {
|
||||
$volume_name = $persistentStorage->host_path;
|
||||
} else {
|
||||
$volume_name = $persistentStorage->name;
|
||||
}
|
||||
if ($this->pull_request_id !== 0) {
|
||||
$volume_name = $volume_name . '-pr-' . $this->pull_request_id;
|
||||
}
|
||||
@@ -1809,7 +1843,6 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
|
||||
|
||||
public function failed(Throwable $exception): void
|
||||
{
|
||||
|
||||
$this->next(ApplicationDeploymentStatus::FAILED->value);
|
||||
$this->application_deployment_queue->addLogEntry("Oops something is not okay, are you okay? 😢", 'stderr');
|
||||
if (str($exception->getMessage())->isNotEmpty()) {
|
||||
|
||||
@@ -170,16 +170,13 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
|
||||
})->first();
|
||||
if (!$foundTcpProxy) {
|
||||
StartDatabaseProxy::run($service_db);
|
||||
$this->server->team?->notify(new ContainerRestarted("TCP Proxy for {$service_db->service->name}", $this->server));
|
||||
// $this->server->team?->notify(new ContainerRestarted("TCP Proxy for {$service_db->service->name}", $this->server));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$database = $databases->where('uuid', $uuid)->first();
|
||||
if ($uuid == 'postgresql') {
|
||||
ray($database);
|
||||
}
|
||||
if ($database) {
|
||||
$isPublic = data_get($database, 'is_public');
|
||||
$foundDatabases[] = $database->id;
|
||||
@@ -187,7 +184,6 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
|
||||
if ($statusFromDb !== $containerStatus) {
|
||||
$database->update(['status' => $containerStatus]);
|
||||
}
|
||||
ray($database);
|
||||
if ($isPublic) {
|
||||
$foundTcpProxy = $containers->filter(function ($value, $key) use ($uuid) {
|
||||
if ($this->server->isSwarm()) {
|
||||
@@ -197,7 +193,6 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
|
||||
}
|
||||
})->first();
|
||||
if (!$foundTcpProxy) {
|
||||
ray('asdffff');
|
||||
StartDatabaseProxy::run($database);
|
||||
$this->server->team?->notify(new ContainerRestarted("TCP Proxy for {$database->name}", $this->server));
|
||||
}
|
||||
|
||||
@@ -95,7 +95,7 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted
|
||||
$databaseType = $this->database->databaseType();
|
||||
$serviceUuid = $this->database->service->uuid;
|
||||
$serviceName = str($this->database->service->name)->slug();
|
||||
if ($databaseType === 'standalone-postgresql') {
|
||||
if (str($databaseType)->contains('postgres')) {
|
||||
$this->container_name = "{$this->database->name}-$serviceUuid";
|
||||
$this->directory_name = $serviceName . '-' . $this->container_name;
|
||||
$commands[] = "docker exec $this->container_name env | grep POSTGRES_";
|
||||
@@ -120,7 +120,7 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted
|
||||
} else {
|
||||
$databasesToBackup = $this->database->postgres_user;
|
||||
}
|
||||
} else if ($databaseType === 'standalone-mysql') {
|
||||
} else if (str($databaseType)->contains('mysql')) {
|
||||
$this->container_name = "{$this->database->name}-$serviceUuid";
|
||||
$this->directory_name = $serviceName . '-' . $this->container_name;
|
||||
$commands[] = "docker exec $this->container_name env | grep MYSQL_";
|
||||
@@ -143,7 +143,7 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted
|
||||
} else {
|
||||
throw new \Exception('MYSQL_DATABASE not found');
|
||||
}
|
||||
} else if ($databaseType === 'standalone-mariadb') {
|
||||
} else if (str($databaseType)->contains('mariadb')) {
|
||||
$this->container_name = "{$this->database->name}-$serviceUuid";
|
||||
$this->directory_name = $serviceName . '-' . $this->container_name;
|
||||
$commands[] = "docker exec $this->container_name env";
|
||||
@@ -190,32 +190,32 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted
|
||||
}
|
||||
|
||||
if (is_null($databasesToBackup)) {
|
||||
if ($databaseType === 'standalone-postgresql') {
|
||||
if (str($databaseType)->contains('postgres')) {
|
||||
$databasesToBackup = [$this->database->postgres_db];
|
||||
} else if ($databaseType === 'standalone-mongodb') {
|
||||
} else if (str($databaseType)->contains('mongodb')) {
|
||||
$databasesToBackup = ['*'];
|
||||
} else if ($databaseType === 'standalone-mysql') {
|
||||
} else if (str($databaseType)->contains('mysql')) {
|
||||
$databasesToBackup = [$this->database->mysql_database];
|
||||
} else if ($databaseType === 'standalone-mariadb') {
|
||||
} else if (str($databaseType)->contains('mariadb')) {
|
||||
$databasesToBackup = [$this->database->mariadb_database];
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if ($databaseType === 'standalone-postgresql') {
|
||||
if (str($databaseType)->contains('postgres')) {
|
||||
// Format: db1,db2,db3
|
||||
$databasesToBackup = explode(',', $databasesToBackup);
|
||||
$databasesToBackup = array_map('trim', $databasesToBackup);
|
||||
} else if ($databaseType === 'standalone-mongodb') {
|
||||
} else if (str($databaseType)->contains('mongodb')) {
|
||||
// Format: db1:collection1,collection2|db2:collection3,collection4
|
||||
$databasesToBackup = explode('|', $databasesToBackup);
|
||||
$databasesToBackup = array_map('trim', $databasesToBackup);
|
||||
ray($databasesToBackup);
|
||||
} else if ($databaseType === 'standalone-mysql') {
|
||||
} else if (str($databaseType)->contains('mysql')) {
|
||||
// Format: db1,db2,db3
|
||||
$databasesToBackup = explode(',', $databasesToBackup);
|
||||
$databasesToBackup = array_map('trim', $databasesToBackup);
|
||||
} else if ($databaseType === 'standalone-mariadb') {
|
||||
} else if (str($databaseType)->contains('mariadb')) {
|
||||
// Format: db1,db2,db3
|
||||
$databasesToBackup = explode(',', $databasesToBackup);
|
||||
$databasesToBackup = array_map('trim', $databasesToBackup);
|
||||
@@ -235,7 +235,7 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted
|
||||
$size = 0;
|
||||
ray('Backing up ' . $database);
|
||||
try {
|
||||
if ($databaseType === 'standalone-postgresql') {
|
||||
if (str($databaseType)->contains('postgres')) {
|
||||
$this->backup_file = "/pg-dump-$database-" . Carbon::now()->timestamp . ".dmp";
|
||||
$this->backup_location = $this->backup_dir . $this->backup_file;
|
||||
$this->backup_log = ScheduledDatabaseBackupExecution::create([
|
||||
@@ -244,7 +244,7 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted
|
||||
'scheduled_database_backup_id' => $this->backup->id,
|
||||
]);
|
||||
$this->backup_standalone_postgresql($database);
|
||||
} else if ($databaseType === 'standalone-mongodb') {
|
||||
} else if (str($databaseType)->contains('mongodb')) {
|
||||
if ($database === '*') {
|
||||
$database = 'all';
|
||||
$databaseName = 'all';
|
||||
@@ -263,7 +263,7 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted
|
||||
'scheduled_database_backup_id' => $this->backup->id,
|
||||
]);
|
||||
$this->backup_standalone_mongodb($database);
|
||||
} else if ($databaseType === 'standalone-mysql') {
|
||||
} else if (str($databaseType)->contains('mysql')) {
|
||||
$this->backup_file = "/mysql-dump-$database-" . Carbon::now()->timestamp . ".dmp";
|
||||
$this->backup_location = $this->backup_dir . $this->backup_file;
|
||||
$this->backup_log = ScheduledDatabaseBackupExecution::create([
|
||||
@@ -272,7 +272,7 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted
|
||||
'scheduled_database_backup_id' => $this->backup->id,
|
||||
]);
|
||||
$this->backup_standalone_mysql($database);
|
||||
} else if ($databaseType === 'standalone-mariadb') {
|
||||
} else if (str($databaseType)->contains('mariadb')) {
|
||||
$this->backup_file = "/mariadb-dump-$database-" . Carbon::now()->timestamp . ".dmp";
|
||||
$this->backup_location = $this->backup_dir . $this->backup_file;
|
||||
$this->backup_log = ScheduledDatabaseBackupExecution::create([
|
||||
|
||||
@@ -8,6 +8,9 @@ use App\Actions\Service\DeleteService;
|
||||
use App\Actions\Service\StopService;
|
||||
use App\Models\Application;
|
||||
use App\Models\Service;
|
||||
use App\Models\StandaloneClickhouse;
|
||||
use App\Models\StandaloneDragonfly;
|
||||
use App\Models\StandaloneKeydb;
|
||||
use App\Models\StandaloneMariadb;
|
||||
use App\Models\StandaloneMongodb;
|
||||
use App\Models\StandaloneMysql;
|
||||
@@ -25,7 +28,7 @@ class DeleteResourceJob implements ShouldQueue, ShouldBeEncrypted
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
public function __construct(public Application|Service|StandalonePostgresql|StandaloneRedis|StandaloneMongodb|StandaloneMysql|StandaloneMariadb $resource)
|
||||
public function __construct(public Application|Service|StandalonePostgresql|StandaloneRedis|StandaloneMongodb|StandaloneMysql|StandaloneMariadb|StandaloneKeydb|StandaloneDragonfly|StandaloneClickhouse $resource, public bool $deleteConfigurations = false)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -42,6 +45,9 @@ class DeleteResourceJob implements ShouldQueue, ShouldBeEncrypted
|
||||
case 'standalone-mongodb':
|
||||
case 'standalone-mysql':
|
||||
case 'standalone-mariadb':
|
||||
case 'standalone-keydb':
|
||||
case 'standalone-dragonfly':
|
||||
case 'standalone-clickhouse':
|
||||
StopDatabase::run($this->resource);
|
||||
break;
|
||||
case 'service':
|
||||
@@ -49,6 +55,9 @@ class DeleteResourceJob implements ShouldQueue, ShouldBeEncrypted
|
||||
DeleteService::run($this->resource);
|
||||
break;
|
||||
}
|
||||
if ($this->deleteConfigurations) {
|
||||
$this->resource?->delete_configurations();
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
ray($e->getMessage());
|
||||
send_internal_notification('ContainerStoppingJob failed with: ' . $e->getMessage());
|
||||
|
||||
@@ -23,6 +23,6 @@ class InstanceAutoUpdateJob implements ShouldQueue, ShouldBeUnique, ShouldBeEncr
|
||||
|
||||
public function handle(): void
|
||||
{
|
||||
UpdateCoolify::run($this->force);
|
||||
UpdateCoolify::run(force: $this->force, async: false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,31 @@ use Livewire\Component;
|
||||
|
||||
class Index extends Component
|
||||
{
|
||||
public $users = [];
|
||||
public $active_subscribers = [];
|
||||
public $inactive_subscribers = [];
|
||||
public $search = '';
|
||||
public function submitSearch() {
|
||||
if ($this->search !== "") {
|
||||
$this->inactive_subscribers = User::whereDoesntHave('teams', function ($query) {
|
||||
$query->whereRelation('subscription', 'stripe_subscription_id', '!=', null);
|
||||
})->where(function ($query) {
|
||||
$query->where('name', 'like', "%{$this->search}%")
|
||||
->orWhere('email', 'like', "%{$this->search}%");
|
||||
})->get()->filter(function ($user) {
|
||||
return $user->id !== 0;
|
||||
});
|
||||
$this->active_subscribers = User::whereHas('teams', function ($query) {
|
||||
$query->whereRelation('subscription', 'stripe_subscription_id', '!=', null);
|
||||
})->where(function ($query) {
|
||||
$query->where('name', 'like', "%{$this->search}%")
|
||||
->orWhere('email', 'like', "%{$this->search}%");
|
||||
})->get()->filter(function ($user) {
|
||||
return $user->id !== 0;
|
||||
});
|
||||
} else {
|
||||
$this->getSubscribers();
|
||||
}
|
||||
}
|
||||
public function mount()
|
||||
{
|
||||
if (!isCloud()) {
|
||||
@@ -17,7 +41,15 @@ class Index extends Component
|
||||
if (auth()->user()->id !== 0) {
|
||||
return redirect()->route('dashboard');
|
||||
}
|
||||
$this->users = User::whereHas('teams', function ($query) {
|
||||
$this->getSubscribers();
|
||||
}
|
||||
public function getSubscribers() {
|
||||
$this->inactive_subscribers = User::whereDoesntHave('teams', function ($query) {
|
||||
$query->whereRelation('subscription', 'stripe_subscription_id', '!=', null);
|
||||
})->get()->filter(function ($user) {
|
||||
return $user->id !== 0;
|
||||
});
|
||||
$this->active_subscribers = User::whereHas('teams', function ($query) {
|
||||
$query->whereRelation('subscription', 'stripe_subscription_id', '!=', null);
|
||||
})->get()->filter(function ($user) {
|
||||
return $user->id !== 0;
|
||||
|
||||
@@ -24,7 +24,7 @@ class ForcePasswordReset extends Component
|
||||
}
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.force-password-reset')->layout('layouts.simple');
|
||||
return view('livewire.force-password-reset');
|
||||
}
|
||||
public function submit()
|
||||
{
|
||||
|
||||
@@ -24,7 +24,7 @@ class General extends Component
|
||||
|
||||
public $customLabels;
|
||||
public bool $labelsChanged = false;
|
||||
public bool $isConfigurationChanged = false;
|
||||
public bool $initLoadingCompose = false;
|
||||
|
||||
public ?string $initialDockerComposeLocation = null;
|
||||
public ?string $initialDockerComposePrLocation = null;
|
||||
@@ -73,6 +73,7 @@ class General extends Component
|
||||
'application.settings.is_static' => 'boolean|required',
|
||||
'application.settings.is_raw_compose_deployment_enabled' => 'boolean|required',
|
||||
'application.settings.is_build_server_enabled' => 'boolean|required',
|
||||
'application.watch_paths' => 'nullable',
|
||||
];
|
||||
protected $validationAttributes = [
|
||||
'application.name' => 'name',
|
||||
@@ -108,6 +109,7 @@ class General extends Component
|
||||
'application.settings.is_static' => 'Is static',
|
||||
'application.settings.is_raw_compose_deployment_enabled' => 'Is raw compose deployment enabled',
|
||||
'application.settings.is_build_server_enabled' => 'Is build server enabled',
|
||||
'application.watch_paths' => 'Watch paths',
|
||||
];
|
||||
public function mount()
|
||||
{
|
||||
@@ -121,19 +123,22 @@ class General extends Component
|
||||
$this->application->settings->save();
|
||||
}
|
||||
$this->parsedServiceDomains = $this->application->docker_compose_domains ? json_decode($this->application->docker_compose_domains, true) : [];
|
||||
|
||||
$this->ports_exposes = $this->application->ports_exposes;
|
||||
if (str($this->application->status)->startsWith('running') && is_null($this->application->config_hash)) {
|
||||
$this->application->isConfigurationChanged(true);
|
||||
}
|
||||
$this->isConfigurationChanged = $this->application->isConfigurationChanged();
|
||||
$this->customLabels = $this->application->parseContainerLabels();
|
||||
$this->customLabels = $this->application->parseContainerLabels();
|
||||
if (!$this->customLabels && $this->application->destination->server->proxyType() !== 'NONE') {
|
||||
$this->customLabels = str(implode("|", generateLabelsApplication($this->application)))->replace("|", "\n");
|
||||
$this->application->custom_labels = base64_encode($this->customLabels);
|
||||
$this->application->save();
|
||||
}
|
||||
$this->initialDockerComposeLocation = $this->application->docker_compose_location;
|
||||
if ($this->application->build_pack === 'dockercompose' && !$this->application->docker_compose_raw) {
|
||||
$this->initLoadingCompose = true;
|
||||
$this->dispatch('info', 'Loading docker compose file...');
|
||||
}
|
||||
|
||||
if (str($this->application->status)->startsWith('running') && is_null($this->application->config_hash)) {
|
||||
$this->dispatch('configurationChanged');
|
||||
}
|
||||
}
|
||||
public function instantSave()
|
||||
{
|
||||
@@ -152,31 +157,36 @@ class General extends Component
|
||||
}
|
||||
['parsedServices' => $this->parsedServices, 'initialDockerComposeLocation' => $this->initialDockerComposeLocation, 'initialDockerComposePrLocation' => $this->initialDockerComposePrLocation] = $this->application->loadComposeFile($isInit);
|
||||
$this->dispatch('success', 'Docker compose file loaded.');
|
||||
$this->dispatch('compose_loaded');
|
||||
} catch (\Throwable $e) {
|
||||
$this->application->docker_compose_location = $this->initialDockerComposeLocation;
|
||||
$this->application->docker_compose_pr_location = $this->initialDockerComposePrLocation;
|
||||
$this->application->save();
|
||||
return handleError($e, $this);
|
||||
} finally {
|
||||
$this->initLoadingCompose = false;
|
||||
|
||||
}
|
||||
}
|
||||
public function generateDomain(string $serviceName)
|
||||
{
|
||||
$domain = $this->parsedServiceDomains[$serviceName]['domain'] ?? null;
|
||||
if (!$domain) {
|
||||
$uuid = new Cuid2(7);
|
||||
$domain = generateFqdn($this->application->destination->server, $uuid);
|
||||
$this->parsedServiceDomains[$serviceName]['domain'] = $domain;
|
||||
$this->application->docker_compose_domains = json_encode($this->parsedServiceDomains);
|
||||
$this->application->save();
|
||||
}
|
||||
$uuid = new Cuid2(7);
|
||||
$domain = generateFqdn($this->application->destination->server, $uuid);
|
||||
$this->parsedServiceDomains[$serviceName]['domain'] = $domain;
|
||||
$this->application->docker_compose_domains = json_encode($this->parsedServiceDomains);
|
||||
$this->application->save();
|
||||
$this->dispatch('success', 'Domain generated.');
|
||||
return $domain;
|
||||
}
|
||||
public function updatedApplicationBaseDirectory() {
|
||||
raY('asdf');
|
||||
public function updatedApplicationBaseDirectory()
|
||||
{
|
||||
if ($this->application->build_pack === 'dockercompose') {
|
||||
$this->loadComposeFile();
|
||||
}
|
||||
}
|
||||
public function updatedApplicationFqdn() {
|
||||
$this->resetDefaultLabels();
|
||||
}
|
||||
public function updatedApplicationBuildPack()
|
||||
{
|
||||
if ($this->application->build_pack !== 'nixpacks') {
|
||||
@@ -204,30 +214,49 @@ class General extends Component
|
||||
$fqdn = generateFqdn($server, $this->application->uuid);
|
||||
$this->application->fqdn = $fqdn;
|
||||
$this->application->save();
|
||||
$this->updatedApplicationFqdn();
|
||||
$this->resetDefaultLabels();
|
||||
$this->dispatch('success', 'Wildcard domain generated.');
|
||||
}
|
||||
}
|
||||
public function resetDefaultLabels($showToaster = true)
|
||||
public function resetDefaultLabels()
|
||||
{
|
||||
ray('resetDefaultLabels');
|
||||
$this->customLabels = str(implode("|", generateLabelsApplication($this->application)))->replace("|", "\n");
|
||||
$this->ports_exposes = $this->application->ports_exposes;
|
||||
$this->submit($showToaster);
|
||||
|
||||
$this->application->custom_labels = base64_encode($this->customLabels);
|
||||
$this->application->save();
|
||||
}
|
||||
|
||||
public function updatedApplicationFqdn()
|
||||
public function checkFqdns($showToaster = true)
|
||||
{
|
||||
$this->application->fqdn = str($this->application->fqdn)->replaceEnd(',', '')->trim();
|
||||
$this->application->fqdn = str($this->application->fqdn)->replaceStart(',', '')->trim();
|
||||
$this->application->fqdn = str($this->application->fqdn)->trim()->explode(',')->map(function ($domain) {
|
||||
return str($domain)->trim()->lower();
|
||||
});
|
||||
$this->application->fqdn = $this->application->fqdn->unique()->implode(',');
|
||||
$this->application->save();
|
||||
$this->resetDefaultLabels(false);
|
||||
if (data_get($this->application, 'fqdn')) {
|
||||
$domains = str($this->application->fqdn)->trim()->explode(',');
|
||||
if ($this->application->additional_servers->count() === 0) {
|
||||
foreach ($domains as $domain) {
|
||||
if (!validate_dns_entry($domain, $this->application->destination->server)) {
|
||||
$showToaster && $this->dispatch('error', "Validating DNS ($domain) failed.", "Make sure you have added the DNS records correctly.<br><br>Check this <a target='_blank' class='underline dark:text-white' href='https://coolify.io/docs/knowledge-base/dns-configuration'>documentation</a> for further help.");
|
||||
}
|
||||
}
|
||||
}
|
||||
check_domain_usage(resource: $this->application);
|
||||
$this->application->fqdn = $domains->implode(',');
|
||||
}
|
||||
}
|
||||
public function submit($showToaster = true)
|
||||
{
|
||||
try {
|
||||
$this->application->fqdn = str($this->application->fqdn)->replaceEnd(',', '')->trim();
|
||||
$this->application->fqdn = str($this->application->fqdn)->replaceStart(',', '')->trim();
|
||||
$this->application->fqdn = str($this->application->fqdn)->trim()->explode(',')->map(function ($domain) {
|
||||
return str($domain)->trim()->lower();
|
||||
});
|
||||
$this->application->fqdn = $this->application->fqdn->unique()->implode(',');
|
||||
|
||||
$this->checkFqdns();
|
||||
|
||||
$this->application->save();
|
||||
|
||||
if (!$this->customLabels && $this->application->destination->server->proxyType() !== 'NONE') {
|
||||
$this->customLabels = str(implode("|", generateLabelsApplication($this->application)))->replace("|", "\n");
|
||||
$this->application->custom_labels = base64_encode($this->customLabels);
|
||||
@@ -239,25 +268,14 @@ class General extends Component
|
||||
}
|
||||
$this->validate();
|
||||
if ($this->ports_exposes !== $this->application->ports_exposes) {
|
||||
$this->resetDefaultLabels(false);
|
||||
$this->resetDefaultLabels();
|
||||
}
|
||||
if (data_get($this->application, 'build_pack') === 'dockerimage') {
|
||||
$this->validate([
|
||||
'application.docker_registry_image_name' => 'required',
|
||||
]);
|
||||
}
|
||||
if (data_get($this->application, 'fqdn')) {
|
||||
$domains = str($this->application->fqdn)->trim()->explode(',');
|
||||
if ($this->application->additional_servers->count() === 0) {
|
||||
foreach ($domains as $domain) {
|
||||
if (!validate_dns_entry($domain, $this->application->destination->server)) {
|
||||
$showToaster && $this->dispatch('error', "Validating DNS ($domain) failed.", "Make sure you have added the DNS records correctly.<br><br>Check this <a target='_blank' class='dark:text-white underline' href='https://coolify.io/docs/dns-settings'>documentation</a> for further help.");
|
||||
}
|
||||
}
|
||||
}
|
||||
check_fqdn_usage($this->application);
|
||||
$this->application->fqdn = $domains->implode(',');
|
||||
}
|
||||
|
||||
if (data_get($this->application, 'custom_docker_run_options')) {
|
||||
$this->application->custom_docker_run_options = str($this->application->custom_docker_run_options)->trim();
|
||||
}
|
||||
@@ -275,6 +293,15 @@ class General extends Component
|
||||
}
|
||||
if ($this->application->build_pack === 'dockercompose') {
|
||||
$this->application->docker_compose_domains = json_encode($this->parsedServiceDomains);
|
||||
foreach ($this->parsedServiceDomains as $serviceName => $service) {
|
||||
$domain = data_get($service, 'domain');
|
||||
if ($domain) {
|
||||
if (!validate_dns_entry($domain, $this->application->destination->server)) {
|
||||
$showToaster && $this->dispatch('error', "Validating DNS ($domain) failed.", "Make sure you have added the DNS records correctly.<br><br>Check this <a target='_blank' class='underline dark:text-white' href='https://coolify.io/docs/knowledge-base/dns-configuration'>documentation</a> for further help.");
|
||||
}
|
||||
check_domain_usage(resource: $this->application);
|
||||
}
|
||||
}
|
||||
if ($this->application->settings->is_raw_compose_deployment_enabled) {
|
||||
$this->application->parseRawCompose();
|
||||
} else {
|
||||
@@ -287,7 +314,7 @@ class General extends Component
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
} finally {
|
||||
$this->isConfigurationChanged = $this->application->isConfigurationChanged();
|
||||
$this->dispatch('configurationChanged');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ class Heading extends Component
|
||||
$teamId = auth()->user()->currentTeam()->id;
|
||||
return [
|
||||
"echo-private:team.{$teamId},ApplicationStatusChanged" => 'check_status',
|
||||
"compose_loaded" => '$refresh',
|
||||
];
|
||||
}
|
||||
public function mount()
|
||||
@@ -38,6 +39,7 @@ class Heading extends Component
|
||||
}
|
||||
|
||||
if ($showNotification) $this->dispatch('success', "Success", "Application status updated.");
|
||||
$this->dispatch('configurationChanged');
|
||||
}
|
||||
|
||||
public function force_deploy_without_cache()
|
||||
@@ -56,11 +58,11 @@ class Heading extends Component
|
||||
return;
|
||||
}
|
||||
if (data_get($this->application, 'settings.is_build_server_enabled') && str($this->application->docker_registry_image_name)->isEmpty()) {
|
||||
$this->dispatch('error', 'Failed to deploy.', 'To use a build server, you must first set a Docker image.<br>More information here: <a target="_blank" class="underline" href="https://coolify.io/docs/server/build-server">documentation</a>');
|
||||
$this->dispatch('error', 'Failed to deploy.', 'To use a build server, you must first set a Docker image.<br>More information here: <a target="_blank" class="underline" href="https://coolify.io/docs/knowledge-base/server/build-server">documentation</a>');
|
||||
return;
|
||||
}
|
||||
if ($this->application->additional_servers->count() > 0 && str($this->application->docker_registry_image_name)->isEmpty()) {
|
||||
$this->dispatch('error', 'Failed to deploy.', 'Before deploying to multiple servers, you must first set a Docker image in the General tab.<br>More information here: <a target="_blank" class="underline" href="https://coolify.io/docs/server/multiple-servers">documentation</a>');
|
||||
$this->dispatch('error', 'Failed to deploy.', 'Before deploying to multiple servers, you must first set a Docker image in the General tab.<br>More information here: <a target="_blank" class="underline" href="https://coolify.io/docs/knowledge-base/server/multiple-servers">documentation</a>');
|
||||
return;
|
||||
}
|
||||
$this->setDeploymentUuid();
|
||||
@@ -99,7 +101,7 @@ class Heading extends Component
|
||||
public function restart()
|
||||
{
|
||||
if ($this->application->additional_servers->count() > 0 && str($this->application->docker_registry_image_name)->isEmpty()) {
|
||||
$this->dispatch('error', 'Failed to deploy', 'Before deploying to multiple servers, you must first set a Docker image in the General tab.<br>More information here: <a target="_blank" class="underline" href="https://coolify.io/docs/server/multiple-servers">documentation</a>');
|
||||
$this->dispatch('error', 'Failed to deploy', 'Before deploying to multiple servers, you must first set a Docker image in the General tab.<br>More information here: <a target="_blank" class="underline" href="https://coolify.io/docs/knowledge-base/server/multiple-servers">documentation</a>');
|
||||
return;
|
||||
}
|
||||
$this->setDeploymentUuid();
|
||||
|
||||
@@ -8,7 +8,8 @@ class Index extends Component
|
||||
{
|
||||
public $database;
|
||||
public $s3s;
|
||||
public function mount() {
|
||||
public function mount()
|
||||
{
|
||||
$project = currentTeam()->load(['projects'])->projects->where('uuid', request()->route('project_uuid'))->first();
|
||||
if (!$project) {
|
||||
return redirect()->route('dashboard');
|
||||
@@ -21,8 +22,13 @@ class Index extends Component
|
||||
if (!$database) {
|
||||
return redirect()->route('dashboard');
|
||||
}
|
||||
// No backups for redis
|
||||
if ($database->getMorphClass() === 'App\Models\StandaloneRedis') {
|
||||
// No backups
|
||||
if (
|
||||
$database->getMorphClass() === 'App\Models\StandaloneRedis' ||
|
||||
$database->getMorphClass() === 'App\Models\StandaloneKeydb' ||
|
||||
$database->getMorphClass() === 'App\Models\StandaloneDragonfly'||
|
||||
$database->getMorphClass() === 'App\Models\StandaloneClickhouse'
|
||||
) {
|
||||
return redirect()->route('project.database.configuration', [
|
||||
'project_uuid' => $project->uuid,
|
||||
'environment_name' => $environment->name,
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace App\Livewire\Project\Database;
|
||||
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Livewire\Component;
|
||||
use Symfony\Component\HttpFoundation\StreamedResponse;
|
||||
|
||||
class BackupExecutions extends Component
|
||||
{
|
||||
@@ -36,32 +37,9 @@ class BackupExecutions extends Component
|
||||
$this->dispatch('success', 'Backup deleted.');
|
||||
$this->refreshBackupExecutions();
|
||||
}
|
||||
public function download($exeuctionId)
|
||||
public function download_file($exeuctionId)
|
||||
{
|
||||
try {
|
||||
$execution = $this->backup->executions()->where('id', $exeuctionId)->first();
|
||||
if (is_null($execution)) {
|
||||
$this->dispatch('error', 'Backup execution not found.');
|
||||
return;
|
||||
}
|
||||
$filename = data_get($execution, 'filename');
|
||||
if ($execution->scheduledDatabaseBackup->database->getMorphClass() === 'App\Models\ServiceDatabase') {
|
||||
$server = $execution->scheduledDatabaseBackup->database->service->destination->server;
|
||||
} else {
|
||||
$server = $execution->scheduledDatabaseBackup->database->destination->server;
|
||||
}
|
||||
$privateKeyLocation = savePrivateKeyToFs($server);
|
||||
$disk = Storage::build([
|
||||
'driver' => 'sftp',
|
||||
'host' => $server->ip,
|
||||
'port' => $server->port,
|
||||
'username' => $server->user,
|
||||
'privateKey' => $privateKeyLocation,
|
||||
]);
|
||||
return $disk->download($filename);
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
return redirect()->route('download.backup', $exeuctionId);
|
||||
}
|
||||
public function refreshBackupExecutions(): void
|
||||
{
|
||||
|
||||
115
app/Livewire/Project/Database/Clickhouse/General.php
Normal file
115
app/Livewire/Project/Database/Clickhouse/General.php
Normal file
@@ -0,0 +1,115 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire\Project\Database\Clickhouse;
|
||||
|
||||
use App\Actions\Database\StartDatabaseProxy;
|
||||
use App\Actions\Database\StopDatabaseProxy;
|
||||
use App\Models\StandaloneClickhouse;
|
||||
use Exception;
|
||||
use Livewire\Component;
|
||||
|
||||
class General extends Component
|
||||
{
|
||||
public StandaloneClickhouse $database;
|
||||
public ?string $db_url = null;
|
||||
public ?string $db_url_public = null;
|
||||
|
||||
protected $listeners = ['refresh'];
|
||||
|
||||
protected $rules = [
|
||||
'database.name' => 'required',
|
||||
'database.description' => 'nullable',
|
||||
'database.clickhouse_admin_user' => 'required',
|
||||
'database.clickhouse_admin_password' => 'required',
|
||||
'database.image' => 'required',
|
||||
'database.ports_mappings' => 'nullable',
|
||||
'database.is_public' => 'nullable|boolean',
|
||||
'database.public_port' => 'nullable|integer',
|
||||
'database.is_log_drain_enabled' => 'nullable|boolean',
|
||||
];
|
||||
protected $validationAttributes = [
|
||||
'database.name' => 'Name',
|
||||
'database.description' => 'Description',
|
||||
'database.clickhouse_admin_user' => 'Postgres User',
|
||||
'database.clickhouse_admin_password' => 'Postgres Password',
|
||||
'database.image' => 'Image',
|
||||
'database.ports_mappings' => 'Port Mapping',
|
||||
'database.is_public' => 'Is Public',
|
||||
'database.public_port' => 'Public Port',
|
||||
];
|
||||
public function mount()
|
||||
{
|
||||
$this->db_url = $this->database->get_db_url(true);
|
||||
if ($this->database->is_public) {
|
||||
$this->db_url_public = $this->database->get_db_url();
|
||||
}
|
||||
}
|
||||
public function instantSaveAdvanced() {
|
||||
try {
|
||||
if (!$this->database->destination->server->isLogDrainEnabled()) {
|
||||
$this->database->is_log_drain_enabled = false;
|
||||
$this->dispatch('error', 'Log drain is not enabled on the server. Please enable it first.');
|
||||
return;
|
||||
}
|
||||
$this->database->save();
|
||||
$this->dispatch('success', 'Database updated.');
|
||||
$this->dispatch('success', 'You need to restart the service for the changes to take effect.');
|
||||
} catch (Exception $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
public function instantSave()
|
||||
{
|
||||
try {
|
||||
if ($this->database->is_public && !$this->database->public_port) {
|
||||
$this->dispatch('error', 'Public port is required.');
|
||||
$this->database->is_public = false;
|
||||
return;
|
||||
}
|
||||
if ($this->database->is_public) {
|
||||
if (!str($this->database->status)->startsWith('running')) {
|
||||
$this->dispatch('error', 'Database must be started to be publicly accessible.');
|
||||
$this->database->is_public = false;
|
||||
return;
|
||||
}
|
||||
StartDatabaseProxy::run($this->database);
|
||||
$this->db_url_public = $this->database->get_db_url();
|
||||
$this->dispatch('success', 'Database is now publicly accessible.');
|
||||
} else {
|
||||
StopDatabaseProxy::run($this->database);
|
||||
$this->db_url_public = null;
|
||||
$this->dispatch('success', 'Database is no longer publicly accessible.');
|
||||
}
|
||||
$this->database->save();
|
||||
} catch (\Throwable $e) {
|
||||
$this->database->is_public = !$this->database->is_public;
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
public function refresh(): void
|
||||
{
|
||||
$this->database->refresh();
|
||||
}
|
||||
|
||||
|
||||
public function submit()
|
||||
{
|
||||
try {
|
||||
if (str($this->database->public_port)->isEmpty()) {
|
||||
$this->database->public_port = null;
|
||||
}
|
||||
$this->validate();
|
||||
$this->database->save();
|
||||
$this->dispatch('success', 'Database updated.');
|
||||
} catch (Exception $e) {
|
||||
return handleError($e, $this);
|
||||
} finally {
|
||||
if (is_null($this->database->config_hash)) {
|
||||
$this->database->isConfigurationChanged(true);
|
||||
} else {
|
||||
$this->dispatch('configurationChanged');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,8 @@ use Livewire\Component;
|
||||
class Configuration extends Component
|
||||
{
|
||||
public $database;
|
||||
public function mount() {
|
||||
public function mount()
|
||||
{
|
||||
$project = currentTeam()->load(['projects'])->projects->where('uuid', request()->route('project_uuid'))->first();
|
||||
if (!$project) {
|
||||
return redirect()->route('dashboard');
|
||||
@@ -21,6 +22,10 @@ class Configuration extends Component
|
||||
return redirect()->route('dashboard');
|
||||
}
|
||||
$this->database = $database;
|
||||
if (str($this->database->status)->startsWith('running') && is_null($this->database->config_hash)) {
|
||||
$this->database->isConfigurationChanged(true);
|
||||
$this->dispatch('configurationChanged');
|
||||
}
|
||||
}
|
||||
public function render()
|
||||
{
|
||||
|
||||
113
app/Livewire/Project/Database/Dragonfly/General.php
Normal file
113
app/Livewire/Project/Database/Dragonfly/General.php
Normal file
@@ -0,0 +1,113 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire\Project\Database\Dragonfly;
|
||||
|
||||
use App\Actions\Database\StartDatabaseProxy;
|
||||
use App\Actions\Database\StopDatabaseProxy;
|
||||
use App\Models\StandaloneDragonfly;
|
||||
use Exception;
|
||||
use Livewire\Component;
|
||||
|
||||
class General extends Component
|
||||
{
|
||||
protected $listeners = ['refresh'];
|
||||
|
||||
public StandaloneDragonfly $database;
|
||||
public ?string $db_url = null;
|
||||
public ?string $db_url_public = null;
|
||||
|
||||
protected $rules = [
|
||||
'database.name' => 'required',
|
||||
'database.description' => 'nullable',
|
||||
'database.dragonfly_password' => 'required',
|
||||
'database.image' => 'required',
|
||||
'database.ports_mappings' => 'nullable',
|
||||
'database.is_public' => 'nullable|boolean',
|
||||
'database.public_port' => 'nullable|integer',
|
||||
'database.is_log_drain_enabled' => 'nullable|boolean',
|
||||
];
|
||||
protected $validationAttributes = [
|
||||
'database.name' => 'Name',
|
||||
'database.description' => 'Description',
|
||||
'database.dragonfly_password' => 'Redis Password',
|
||||
'database.image' => 'Image',
|
||||
'database.ports_mappings' => 'Port Mapping',
|
||||
'database.is_public' => 'Is Public',
|
||||
'database.public_port' => 'Public Port',
|
||||
];
|
||||
public function mount()
|
||||
{
|
||||
$this->db_url = $this->database->get_db_url(true);
|
||||
if ($this->database->is_public) {
|
||||
$this->db_url_public = $this->database->get_db_url();
|
||||
}
|
||||
}
|
||||
public function instantSaveAdvanced() {
|
||||
try {
|
||||
if (!$this->database->destination->server->isLogDrainEnabled()) {
|
||||
$this->database->is_log_drain_enabled = false;
|
||||
$this->dispatch('error', 'Log drain is not enabled on the server. Please enable it first.');
|
||||
return;
|
||||
}
|
||||
$this->database->save();
|
||||
$this->dispatch('success', 'Database updated.');
|
||||
$this->dispatch('success', 'You need to restart the service for the changes to take effect.');
|
||||
} catch (Exception $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
public function submit()
|
||||
{
|
||||
try {
|
||||
$this->validate();
|
||||
$this->database->save();
|
||||
$this->dispatch('success', 'Database updated.');
|
||||
} catch (Exception $e) {
|
||||
return handleError($e, $this);
|
||||
} finally {
|
||||
if (is_null($this->database->config_hash)) {
|
||||
$this->database->isConfigurationChanged(true);
|
||||
} else {
|
||||
$this->dispatch('configurationChanged');
|
||||
}
|
||||
}
|
||||
}
|
||||
public function instantSave()
|
||||
{
|
||||
try {
|
||||
if ($this->database->is_public && !$this->database->public_port) {
|
||||
$this->dispatch('error', 'Public port is required.');
|
||||
$this->database->is_public = false;
|
||||
return;
|
||||
}
|
||||
if ($this->database->is_public) {
|
||||
if (!str($this->database->status)->startsWith('running')) {
|
||||
$this->dispatch('error', 'Database must be started to be publicly accessible.');
|
||||
$this->database->is_public = false;
|
||||
return;
|
||||
}
|
||||
StartDatabaseProxy::run($this->database);
|
||||
$this->db_url_public = $this->database->get_db_url();
|
||||
$this->dispatch('success', 'Database is now publicly accessible.');
|
||||
} else {
|
||||
StopDatabaseProxy::run($this->database);
|
||||
$this->db_url_public = null;
|
||||
$this->dispatch('success', 'Database is no longer publicly accessible.');
|
||||
}
|
||||
$this->database->save();
|
||||
} catch (\Throwable $e) {
|
||||
$this->database->is_public = !$this->database->is_public;
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
public function refresh(): void
|
||||
{
|
||||
$this->database->refresh();
|
||||
}
|
||||
|
||||
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.project.database.dragonfly.general');
|
||||
}
|
||||
}
|
||||
@@ -2,13 +2,15 @@
|
||||
|
||||
namespace App\Livewire\Project\Database;
|
||||
|
||||
use App\Actions\Database\StartClickhouse;
|
||||
use App\Actions\Database\StartDragonfly;
|
||||
use App\Actions\Database\StartKeydb;
|
||||
use App\Actions\Database\StartMariadb;
|
||||
use App\Actions\Database\StartMongodb;
|
||||
use App\Actions\Database\StartMysql;
|
||||
use App\Actions\Database\StartPostgresql;
|
||||
use App\Actions\Database\StartRedis;
|
||||
use App\Actions\Database\StopDatabase;
|
||||
use App\Events\DatabaseStatusChanged;
|
||||
use App\Jobs\ContainerStatusJob;
|
||||
use Livewire\Component;
|
||||
|
||||
@@ -32,6 +34,12 @@ class Heading extends Component
|
||||
]);
|
||||
$this->dispatch('refresh');
|
||||
$this->check_status();
|
||||
if (is_null($this->database->config_hash) || $this->database->isConfigurationChanged()) {
|
||||
$this->database->isConfigurationChanged(true);
|
||||
$this->dispatch('configurationChanged');
|
||||
} else {
|
||||
$this->dispatch('configurationChanged');
|
||||
}
|
||||
}
|
||||
|
||||
public function check_status($showNotification = false)
|
||||
@@ -71,6 +79,15 @@ class Heading extends Component
|
||||
} else if ($this->database->type() === 'standalone-mariadb') {
|
||||
$activity = StartMariadb::run($this->database);
|
||||
$this->dispatch('activityMonitor', $activity->id);
|
||||
} else if ($this->database->type() === 'standalone-keydb') {
|
||||
$activity = StartKeydb::run($this->database);
|
||||
$this->dispatch('activityMonitor', $activity->id);
|
||||
} else if ($this->database->type() === 'standalone-dragonfly') {
|
||||
$activity = StartDragonfly::run($this->database);
|
||||
$this->dispatch('activityMonitor', $activity->id);
|
||||
} else if ($this->database->type() === 'standalone-clickhouse') {
|
||||
$activity = StartClickhouse::run($this->database);
|
||||
$this->dispatch('activityMonitor', $activity->id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,29 +2,25 @@
|
||||
|
||||
namespace App\Livewire\Project\Database;
|
||||
|
||||
use Exception;
|
||||
use Livewire\Component;
|
||||
use Livewire\WithFileUploads;
|
||||
use App\Models\Server;
|
||||
use App\Models\StandaloneMariadb;
|
||||
use App\Models\StandaloneMongodb;
|
||||
use App\Models\StandaloneMysql;
|
||||
use App\Models\StandalonePostgresql;
|
||||
use App\Models\StandaloneRedis;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class Import extends Component
|
||||
{
|
||||
use WithFileUploads;
|
||||
|
||||
public $file;
|
||||
public bool $unsupported = false;
|
||||
public $resource;
|
||||
public $parameters;
|
||||
public $containers;
|
||||
public bool $validated = true;
|
||||
public bool $scpInProgress = false;
|
||||
public bool $importRunning = false;
|
||||
public string $validationMsg = '';
|
||||
|
||||
public ?string $filename = null;
|
||||
public ?string $filesize = null;
|
||||
public bool $isUploading = false;
|
||||
public int $progress = 0;
|
||||
public bool $error = false;
|
||||
|
||||
public Server $server;
|
||||
public string $container;
|
||||
public array $importCommands = [];
|
||||
@@ -51,22 +47,9 @@ class Import extends Component
|
||||
if (!data_get($this->parameters, 'database_uuid')) {
|
||||
abort(404);
|
||||
}
|
||||
|
||||
$resource = StandalonePostgresql::where('uuid', $this->parameters['database_uuid'])->first();
|
||||
$resource = getResourceByUuid($this->parameters['database_uuid'], data_get(auth()->user()->currentTeam(), 'id'));
|
||||
if (is_null($resource)) {
|
||||
$resource = StandaloneRedis::where('uuid', $this->parameters['database_uuid'])->first();
|
||||
if (is_null($resource)) {
|
||||
$resource = StandaloneMongodb::where('uuid', $this->parameters['database_uuid'])->first();
|
||||
if (is_null($resource)) {
|
||||
$resource = StandaloneMysql::where('uuid', $this->parameters['database_uuid'])->first();
|
||||
if (is_null($resource)) {
|
||||
$resource = StandaloneMariadb::where('uuid', $this->parameters['database_uuid'])->first();
|
||||
if (is_null($resource)) {
|
||||
abort(404);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
abort(404);
|
||||
}
|
||||
$this->resource = $resource;
|
||||
$this->server = $this->resource->destination->server;
|
||||
@@ -75,38 +58,34 @@ class Import extends Component
|
||||
$this->containers->push($this->container);
|
||||
}
|
||||
|
||||
if ($this->containers->count() > 1) {
|
||||
$this->validated = false;
|
||||
$this->validationMsg = 'The database service has more than one container running. Cannot import.';
|
||||
}
|
||||
|
||||
if (
|
||||
$this->resource->getMorphClass() == 'App\Models\StandaloneRedis'
|
||||
|| $this->resource->getMorphClass() == 'App\Models\StandaloneMongodb'
|
||||
$this->resource->getMorphClass() == 'App\Models\StandaloneRedis' ||
|
||||
$this->resource->getMorphClass() == 'App\Models\StandaloneKeydb' ||
|
||||
$this->resource->getMorphClass() == 'App\Models\StandaloneDragonfly' ||
|
||||
$this->resource->getMorphClass() == 'App\Models\StandaloneClickhouse' ||
|
||||
$this->resource->getMorphClass() == 'App\Models\StandaloneMongodb'
|
||||
) {
|
||||
$this->validated = false;
|
||||
$this->validationMsg = 'This database type is not currently supported.';
|
||||
$this->unsupported = true;
|
||||
}
|
||||
}
|
||||
|
||||
public function runImport()
|
||||
{
|
||||
$this->validate([
|
||||
'file' => 'required|file|max:102400'
|
||||
]);
|
||||
|
||||
$this->importRunning = true;
|
||||
$this->scpInProgress = true;
|
||||
|
||||
if ($this->filename == '') {
|
||||
$this->dispatch('error', 'Please select a file to import.');
|
||||
return;
|
||||
}
|
||||
try {
|
||||
$uploadedFilename = $this->file->store('backup-import');
|
||||
$uploadedFilename = "upload/{$this->resource->uuid}/restore";
|
||||
$path = Storage::path($uploadedFilename);
|
||||
if (!Storage::exists($uploadedFilename)) {
|
||||
$this->dispatch('error', 'The file does not exist or has been deleted.');
|
||||
return;
|
||||
}
|
||||
$tmpPath = '/tmp/' . basename($uploadedFilename);
|
||||
|
||||
// SCP the backup file to the server.
|
||||
instant_scp($path, $tmpPath, $this->server);
|
||||
$this->scpInProgress = false;
|
||||
|
||||
Storage::delete($uploadedFilename);
|
||||
$this->importCommands[] = "docker cp {$tmpPath} {$this->container}:{$tmpPath}";
|
||||
|
||||
switch ($this->resource->getMorphClass()) {
|
||||
@@ -132,8 +111,7 @@ class Import extends Component
|
||||
$this->dispatch('activityMonitor', $activity->id);
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
$this->validated = false;
|
||||
$this->validationMsg = $e->getMessage();
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
117
app/Livewire/Project/Database/Keydb/General.php
Normal file
117
app/Livewire/Project/Database/Keydb/General.php
Normal file
@@ -0,0 +1,117 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire\Project\Database\Keydb;
|
||||
|
||||
use App\Actions\Database\StartDatabaseProxy;
|
||||
use App\Actions\Database\StopDatabaseProxy;
|
||||
use App\Models\StandaloneKeydb;
|
||||
use Exception;
|
||||
use Livewire\Component;
|
||||
|
||||
class General extends Component
|
||||
{
|
||||
protected $listeners = ['refresh'];
|
||||
|
||||
public StandaloneKeydb $database;
|
||||
public ?string $db_url = null;
|
||||
public ?string $db_url_public = null;
|
||||
|
||||
protected $rules = [
|
||||
'database.name' => 'required',
|
||||
'database.description' => 'nullable',
|
||||
'database.keydb_conf' => 'nullable',
|
||||
'database.keydb_password' => 'required',
|
||||
'database.image' => 'required',
|
||||
'database.ports_mappings' => 'nullable',
|
||||
'database.is_public' => 'nullable|boolean',
|
||||
'database.public_port' => 'nullable|integer',
|
||||
'database.is_log_drain_enabled' => 'nullable|boolean',
|
||||
];
|
||||
protected $validationAttributes = [
|
||||
'database.name' => 'Name',
|
||||
'database.description' => 'Description',
|
||||
'database.keydb_conf' => 'Redis Configuration',
|
||||
'database.keydb_password' => 'Redis Password',
|
||||
'database.image' => 'Image',
|
||||
'database.ports_mappings' => 'Port Mapping',
|
||||
'database.is_public' => 'Is Public',
|
||||
'database.public_port' => 'Public Port',
|
||||
];
|
||||
public function mount()
|
||||
{
|
||||
$this->db_url = $this->database->get_db_url(true);
|
||||
if ($this->database->is_public) {
|
||||
$this->db_url_public = $this->database->get_db_url();
|
||||
}
|
||||
}
|
||||
public function instantSaveAdvanced() {
|
||||
try {
|
||||
if (!$this->database->destination->server->isLogDrainEnabled()) {
|
||||
$this->database->is_log_drain_enabled = false;
|
||||
$this->dispatch('error', 'Log drain is not enabled on the server. Please enable it first.');
|
||||
return;
|
||||
}
|
||||
$this->database->save();
|
||||
$this->dispatch('success', 'Database updated.');
|
||||
$this->dispatch('success', 'You need to restart the service for the changes to take effect.');
|
||||
} catch (Exception $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
public function submit()
|
||||
{
|
||||
try {
|
||||
$this->validate();
|
||||
if ($this->database->keydb_conf === "") {
|
||||
$this->database->keydb_conf = null;
|
||||
}
|
||||
$this->database->save();
|
||||
$this->dispatch('success', 'Database updated.');
|
||||
} catch (Exception $e) {
|
||||
return handleError($e, $this);
|
||||
} finally {
|
||||
if (is_null($this->database->config_hash)) {
|
||||
$this->database->isConfigurationChanged(true);
|
||||
} else {
|
||||
$this->dispatch('configurationChanged');
|
||||
}
|
||||
}
|
||||
}
|
||||
public function instantSave()
|
||||
{
|
||||
try {
|
||||
if ($this->database->is_public && !$this->database->public_port) {
|
||||
$this->dispatch('error', 'Public port is required.');
|
||||
$this->database->is_public = false;
|
||||
return;
|
||||
}
|
||||
if ($this->database->is_public) {
|
||||
if (!str($this->database->status)->startsWith('running')) {
|
||||
$this->dispatch('error', 'Database must be started to be publicly accessible.');
|
||||
$this->database->is_public = false;
|
||||
return;
|
||||
}
|
||||
StartDatabaseProxy::run($this->database);
|
||||
$this->db_url_public = $this->database->get_db_url();
|
||||
$this->dispatch('success', 'Database is now publicly accessible.');
|
||||
} else {
|
||||
StopDatabaseProxy::run($this->database);
|
||||
$this->db_url_public = null;
|
||||
$this->dispatch('success', 'Database is no longer publicly accessible.');
|
||||
}
|
||||
$this->database->save();
|
||||
} catch (\Throwable $e) {
|
||||
$this->database->is_public = !$this->database->is_public;
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
public function refresh(): void
|
||||
{
|
||||
$this->database->refresh();
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.project.database.keydb.general');
|
||||
}
|
||||
}
|
||||
@@ -76,6 +76,12 @@ class General extends Component
|
||||
$this->dispatch('success', 'Database updated.');
|
||||
} catch (Exception $e) {
|
||||
return handleError($e, $this);
|
||||
} finally {
|
||||
if (is_null($this->database->config_hash)) {
|
||||
$this->database->isConfigurationChanged(true);
|
||||
} else {
|
||||
$this->dispatch('configurationChanged');
|
||||
}
|
||||
}
|
||||
}
|
||||
public function instantSave()
|
||||
|
||||
@@ -78,6 +78,12 @@ class General extends Component
|
||||
$this->dispatch('success', 'Database updated.');
|
||||
} catch (Exception $e) {
|
||||
return handleError($e, $this);
|
||||
} finally {
|
||||
if (is_null($this->database->config_hash)) {
|
||||
$this->database->isConfigurationChanged(true);
|
||||
} else {
|
||||
$this->dispatch('configurationChanged');
|
||||
}
|
||||
}
|
||||
}
|
||||
public function instantSave()
|
||||
|
||||
@@ -77,6 +77,12 @@ class General extends Component
|
||||
$this->dispatch('success', 'Database updated.');
|
||||
} catch (Exception $e) {
|
||||
return handleError($e, $this);
|
||||
} finally {
|
||||
if (is_null($this->database->config_hash)) {
|
||||
$this->database->isConfigurationChanged(true);
|
||||
} else {
|
||||
$this->dispatch('configurationChanged');
|
||||
}
|
||||
}
|
||||
}
|
||||
public function instantSave()
|
||||
|
||||
@@ -58,7 +58,8 @@ class General extends Component
|
||||
$this->db_url_public = $this->database->get_db_url();
|
||||
}
|
||||
}
|
||||
public function instantSaveAdvanced() {
|
||||
public function instantSaveAdvanced()
|
||||
{
|
||||
try {
|
||||
if (!$this->database->destination->server->isLogDrainEnabled()) {
|
||||
$this->database->is_log_drain_enabled = false;
|
||||
@@ -164,6 +165,12 @@ class General extends Component
|
||||
$this->dispatch('success', 'Database updated.');
|
||||
} catch (Exception $e) {
|
||||
return handleError($e, $this);
|
||||
} finally {
|
||||
if (is_null($this->database->config_hash)) {
|
||||
$this->database->isConfigurationChanged(true);
|
||||
} else {
|
||||
$this->dispatch('configurationChanged');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,14 +7,12 @@ use App\Models\GithubApp;
|
||||
use App\Models\Project;
|
||||
use App\Models\StandaloneDocker;
|
||||
use App\Models\SwarmDocker;
|
||||
use App\Traits\SaveFromRedirect;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use Livewire\Component;
|
||||
|
||||
class GithubPrivateRepository extends Component
|
||||
{
|
||||
use SaveFromRedirect;
|
||||
public $current_step = 'github_apps';
|
||||
public $github_apps;
|
||||
public GithubApp $github_app;
|
||||
|
||||
@@ -49,7 +49,6 @@ class Select extends Component
|
||||
}
|
||||
public function render()
|
||||
{
|
||||
$this->loadServices();
|
||||
return view('livewire.project.new.select');
|
||||
}
|
||||
|
||||
@@ -71,9 +70,14 @@ class Select extends Component
|
||||
// }
|
||||
// }
|
||||
|
||||
public function updatedSearch()
|
||||
{
|
||||
$this->loadServices();
|
||||
}
|
||||
public function loadServices(bool $force = false)
|
||||
{
|
||||
try {
|
||||
$this->loadingServices = true;
|
||||
if (count($this->allServices) > 0 && !$force) {
|
||||
if (!$this->search) {
|
||||
$this->services = $this->allServices;
|
||||
@@ -116,6 +120,9 @@ class Select extends Component
|
||||
case 'mysql':
|
||||
case 'mariadb':
|
||||
case 'redis':
|
||||
case 'keydb':
|
||||
case 'dragonfly':
|
||||
case 'clickhouse':
|
||||
case 'mongodb':
|
||||
$this->isDatabase = true;
|
||||
$this->includeSwarm = false;
|
||||
|
||||
@@ -36,6 +36,12 @@ class Create extends Component
|
||||
$database = create_standalone_mysql($environment->id, $destination_uuid);
|
||||
} else if ($type->value() === 'mariadb') {
|
||||
$database = create_standalone_mariadb($environment->id, $destination_uuid);
|
||||
} else if ($type->value() === 'keydb') {
|
||||
$database = create_standalone_keydb($environment->id, $destination_uuid);
|
||||
} else if ($type->value() === 'dragonfly') {
|
||||
$database = create_standalone_dragonfly($environment->id, $destination_uuid);
|
||||
}else if ($type->value() === 'clickhouse') {
|
||||
$database = create_standalone_clickhouse($environment->id, $destination_uuid);
|
||||
}
|
||||
return redirect()->route('project.database.configuration', [
|
||||
'project_uuid' => $project->uuid,
|
||||
|
||||
@@ -16,6 +16,9 @@ class Index extends Component
|
||||
public $mongodbs = [];
|
||||
public $mysqls = [];
|
||||
public $mariadbs = [];
|
||||
public $keydbs = [];
|
||||
public $dragonflies = [];
|
||||
public $clickhouses = [];
|
||||
public $services = [];
|
||||
public function mount()
|
||||
{
|
||||
@@ -96,6 +99,39 @@ class Index extends Component
|
||||
}
|
||||
return $mariadb;
|
||||
});
|
||||
$this->keydbs = $this->environment->keydbs->load(['tags'])->sortBy('name');
|
||||
$this->keydbs = $this->keydbs->map(function ($keydb) {
|
||||
if (data_get($keydb, 'environment.project.uuid')) {
|
||||
$keydb->hrefLink = route('project.database.configuration', [
|
||||
'project_uuid' => data_get($keydb, 'environment.project.uuid'),
|
||||
'environment_name' => data_get($keydb, 'environment.name'),
|
||||
'database_uuid' => data_get($keydb, 'uuid')
|
||||
]);
|
||||
}
|
||||
return $keydb;
|
||||
});
|
||||
$this->dragonflies = $this->environment->dragonflies->load(['tags'])->sortBy('name');
|
||||
$this->dragonflies = $this->dragonflies->map(function ($dragonfly) {
|
||||
if (data_get($dragonfly, 'environment.project.uuid')) {
|
||||
$dragonfly->hrefLink = route('project.database.configuration', [
|
||||
'project_uuid' => data_get($dragonfly, 'environment.project.uuid'),
|
||||
'environment_name' => data_get($dragonfly, 'environment.name'),
|
||||
'database_uuid' => data_get($dragonfly, 'uuid')
|
||||
]);
|
||||
}
|
||||
return $dragonfly;
|
||||
});
|
||||
$this->clickhouses = $this->environment->clickhouses->load(['tags'])->sortBy('name');
|
||||
$this->clickhouses = $this->clickhouses->map(function ($clickhouse) {
|
||||
if (data_get($clickhouse, 'environment.project.uuid')) {
|
||||
$clickhouse->hrefLink = route('project.database.configuration', [
|
||||
'project_uuid' => data_get($clickhouse, 'environment.project.uuid'),
|
||||
'environment_name' => data_get($clickhouse, 'environment.name'),
|
||||
'database_uuid' => data_get($clickhouse, 'uuid')
|
||||
]);
|
||||
}
|
||||
return $clickhouse;
|
||||
});
|
||||
$this->services = $this->environment->services->load(['tags'])->sortBy('name');
|
||||
$this->services = $this->services->map(function ($service) {
|
||||
if (data_get($service, 'environment.project.uuid')) {
|
||||
|
||||
@@ -18,7 +18,8 @@ class Configuration extends Component
|
||||
$userId = auth()->user()->id;
|
||||
return [
|
||||
"echo-private:user.{$userId},ServiceStatusChanged" => 'check_status',
|
||||
"check_status"
|
||||
"check_status",
|
||||
"refresh" => '$refresh',
|
||||
];
|
||||
}
|
||||
public function render()
|
||||
@@ -65,7 +66,7 @@ class Configuration extends Component
|
||||
try {
|
||||
dispatch_sync(new ContainerStatusJob($this->service->server));
|
||||
$this->dispatch('refresh')->self();
|
||||
$this->dispatch('serviceStatusChanged');
|
||||
$this->dispatch('updateStatus');
|
||||
} catch (\Exception $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ class EditCompose extends Component
|
||||
}
|
||||
|
||||
public function saveEditedCompose() {
|
||||
$this->dispatch('warning', "Saving new docker compose...");
|
||||
$this->dispatch('info', "Saving new docker compose...");
|
||||
$this->dispatch('saveCompose', $this->service->docker_compose_raw);
|
||||
}
|
||||
public function render()
|
||||
|
||||
53
app/Livewire/Project/Service/EditDomain.php
Normal file
53
app/Livewire/Project/Service/EditDomain.php
Normal file
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire\Project\Service;
|
||||
|
||||
use App\Models\ServiceApplication;
|
||||
use Livewire\Component;
|
||||
|
||||
class EditDomain extends Component
|
||||
{
|
||||
public $applicationId;
|
||||
public ServiceApplication $application;
|
||||
protected $rules = [
|
||||
'application.fqdn' => 'nullable',
|
||||
'application.required_fqdn' => 'required|boolean',
|
||||
];
|
||||
public function mount() {
|
||||
$this->application = ServiceApplication::find($this->applicationId);
|
||||
}
|
||||
public function updatedApplicationFqdn()
|
||||
{
|
||||
$this->application->fqdn = str($this->application->fqdn)->replaceEnd(',', '')->trim();
|
||||
$this->application->fqdn = str($this->application->fqdn)->replaceStart(',', '')->trim();
|
||||
$this->application->fqdn = str($this->application->fqdn)->trim()->explode(',')->map(function ($domain) {
|
||||
return str($domain)->trim()->lower();
|
||||
});
|
||||
$this->application->fqdn = $this->application->fqdn->unique()->implode(',');
|
||||
$this->application->save();
|
||||
}
|
||||
public function submit()
|
||||
{
|
||||
try {
|
||||
check_domain_usage(resource: $this->application);
|
||||
$this->validate();
|
||||
$this->application->save();
|
||||
updateCompose($this->application);
|
||||
if (str($this->application->fqdn)->contains(',')) {
|
||||
$this->dispatch('warning', 'Some services do not support multiple domains, which can lead to problems and is NOT RECOMMENDED.<br><br>Only use multiple domains if you know what you are doing.');
|
||||
} else {
|
||||
$this->dispatch('success', 'Service saved.');
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
} finally {
|
||||
$this->dispatch('generateDockerCompose');
|
||||
$this->dispatch('refresh');
|
||||
$this->dispatch('configurationChanged');
|
||||
}
|
||||
}
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.project.service.edit-domain');
|
||||
}
|
||||
}
|
||||
@@ -5,13 +5,14 @@ namespace App\Livewire\Project\Service;
|
||||
use App\Models\LocalFileVolume;
|
||||
use App\Models\ServiceApplication;
|
||||
use App\Models\ServiceDatabase;
|
||||
use App\Models\StandaloneClickhouse;
|
||||
use Livewire\Component;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class FileStorage extends Component
|
||||
{
|
||||
public LocalFileVolume $fileStorage;
|
||||
public ServiceApplication|ServiceDatabase $service;
|
||||
public ServiceApplication|ServiceDatabase|StandaloneClickhouse $resource;
|
||||
public string $fs_path;
|
||||
public ?string $workdir = null;
|
||||
|
||||
@@ -23,14 +24,14 @@ class FileStorage extends Component
|
||||
];
|
||||
public function mount()
|
||||
{
|
||||
$this->service = $this->fileStorage->service;
|
||||
if (Str::of($this->fileStorage->fs_path)->startsWith('.')) {
|
||||
$this->workdir = $this->service->service->workdir();
|
||||
$this->resource = $this->fileStorage->service;
|
||||
if (Str::of($this->fileStorage->fs_path)->startsWith('.')) {
|
||||
$this->workdir = $this->resource->service->workdir();
|
||||
$this->fs_path = Str::of($this->fileStorage->fs_path)->after('.');
|
||||
} else {
|
||||
} else {
|
||||
$this->workdir = null;
|
||||
$this->fs_path = $this->fileStorage->fs_path;
|
||||
}
|
||||
}
|
||||
}
|
||||
public function submit()
|
||||
{
|
||||
|
||||
@@ -10,7 +10,7 @@ use Livewire\Component;
|
||||
|
||||
class Index extends Component
|
||||
{
|
||||
public Service $service;
|
||||
public ?Service $service = null;
|
||||
public ?ServiceApplication $serviceApplication = null;
|
||||
public ?ServiceDatabase $serviceDatabase = null;
|
||||
public array $parameters;
|
||||
@@ -26,7 +26,10 @@ class Index extends Component
|
||||
$this->services = collect([]);
|
||||
$this->parameters = get_route_parameters();
|
||||
$this->query = request()->query();
|
||||
$this->service = Service::whereUuid($this->parameters['service_uuid'])->firstOrFail();
|
||||
$this->service = Service::whereUuid($this->parameters['service_uuid'])->first();
|
||||
if (!$this->service) {
|
||||
return redirect()->route('dashboard');
|
||||
}
|
||||
$service = $this->service->applications()->whereUuid($this->parameters['stack_service_uuid'])->first();
|
||||
if ($service) {
|
||||
$this->serviceApplication = $service;
|
||||
|
||||
@@ -17,21 +17,33 @@ class Navbar extends Component
|
||||
public array $query;
|
||||
public $isDeploymentProgress = false;
|
||||
|
||||
public function mount()
|
||||
{
|
||||
if (str($this->service->status())->contains('running') && is_null($this->service->config_hash)) {
|
||||
ray('isConfigurationChanged init');
|
||||
$this->service->isConfigurationChanged(true);
|
||||
$this->dispatch('configurationChanged');
|
||||
}
|
||||
}
|
||||
public function getListeners()
|
||||
{
|
||||
$userId = auth()->user()->id;
|
||||
return [
|
||||
"echo-private:user.{$userId},ServiceStatusChanged" => 'serviceStarted',
|
||||
"serviceStatusChanged"
|
||||
"updateStatus"=> '$refresh',
|
||||
];
|
||||
}
|
||||
public function serviceStarted() {
|
||||
$this->dispatch('success', 'Service status changed.');
|
||||
}
|
||||
public function serviceStatusChanged()
|
||||
public function serviceStarted()
|
||||
{
|
||||
$this->dispatch('refresh')->self();
|
||||
$this->dispatch('success', 'Service status changed.');
|
||||
if (is_null($this->service->config_hash) || $this->service->isConfigurationChanged()) {
|
||||
$this->service->isConfigurationChanged(true);
|
||||
$this->dispatch('configurationChanged');
|
||||
} else {
|
||||
$this->dispatch('configurationChanged');
|
||||
}
|
||||
}
|
||||
|
||||
public function check_status()
|
||||
{
|
||||
$this->dispatch('check_status');
|
||||
|
||||
@@ -24,6 +24,16 @@ class ServiceApplicationView extends Component
|
||||
{
|
||||
return view('livewire.project.service.service-application-view');
|
||||
}
|
||||
public function updatedApplicationFqdn()
|
||||
{
|
||||
$this->application->fqdn = str($this->application->fqdn)->replaceEnd(',', '')->trim();
|
||||
$this->application->fqdn = str($this->application->fqdn)->replaceStart(',', '')->trim();
|
||||
$this->application->fqdn = str($this->application->fqdn)->trim()->explode(',')->map(function ($domain) {
|
||||
return str($domain)->trim()->lower();
|
||||
});
|
||||
$this->application->fqdn = $this->application->fqdn->unique()->implode(',');
|
||||
$this->application->save();
|
||||
}
|
||||
public function instantSave()
|
||||
{
|
||||
$this->submit();
|
||||
@@ -55,11 +65,15 @@ class ServiceApplicationView extends Component
|
||||
public function submit()
|
||||
{
|
||||
try {
|
||||
check_fqdn_usage($this->application);
|
||||
check_domain_usage(resource: $this->application);
|
||||
$this->validate();
|
||||
$this->application->save();
|
||||
updateCompose($this->application);
|
||||
$this->dispatch('success', 'Service saved.');
|
||||
if (str($this->application->fqdn)->contains(',')) {
|
||||
$this->dispatch('warning', 'Some services do not support multiple domains, which can lead to problems and is NOT RECOMMENDED.<br><br>Only use multiple domains if you know what you are doing.');
|
||||
} else {
|
||||
$this->dispatch('success', 'Service saved.');
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
} finally {
|
||||
|
||||
@@ -69,6 +69,13 @@ class StackForm extends Component
|
||||
$this->dispatch('success', 'Service saved.');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
} finally {
|
||||
if (is_null($this->service->config_hash)) {
|
||||
ray('asdf');
|
||||
$this->service->isConfigurationChanged(true);
|
||||
} else {
|
||||
$this->dispatch('configurationChanged');
|
||||
}
|
||||
}
|
||||
}
|
||||
public function render()
|
||||
|
||||
34
app/Livewire/Project/Shared/ConfigurationChecker.php
Normal file
34
app/Livewire/Project/Shared/ConfigurationChecker.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire\Project\Shared;
|
||||
|
||||
use App\Models\Application;
|
||||
use App\Models\Service;
|
||||
use App\Models\StandaloneClickhouse;
|
||||
use App\Models\StandaloneDragonfly;
|
||||
use App\Models\StandaloneKeydb;
|
||||
use App\Models\StandaloneMariadb;
|
||||
use App\Models\StandaloneMongodb;
|
||||
use App\Models\StandaloneMysql;
|
||||
use App\Models\StandalonePostgresql;
|
||||
use App\Models\StandaloneRedis;
|
||||
use Livewire\Component;
|
||||
|
||||
class ConfigurationChecker extends Component
|
||||
{
|
||||
public bool $isConfigurationChanged = false;
|
||||
public Application|Service|StandaloneRedis|StandalonePostgresql|StandaloneMongodb|StandaloneMysql|StandaloneMariadb|StandaloneKeydb|StandaloneDragonfly|StandaloneClickhouse $resource;
|
||||
protected $listeners = ['configurationChanged'];
|
||||
public function mount()
|
||||
{
|
||||
$this->configurationChanged();
|
||||
}
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.project.shared.configuration-checker');
|
||||
}
|
||||
public function configurationChanged()
|
||||
{
|
||||
$this->isConfigurationChanged = $this->resource->isConfigurationChanged();
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,7 @@ class Danger extends Component
|
||||
public $resource;
|
||||
public $projectUuid;
|
||||
public $environmentName;
|
||||
public bool $delete_configurations = true;
|
||||
public ?string $modalId = null;
|
||||
|
||||
public function mount()
|
||||
@@ -20,12 +21,11 @@ class Danger extends Component
|
||||
$this->projectUuid = data_get($parameters, 'project_uuid');
|
||||
$this->environmentName = data_get($parameters, 'environment_name');
|
||||
}
|
||||
|
||||
public function delete()
|
||||
{
|
||||
try {
|
||||
$this->resource->delete();
|
||||
DeleteResourceJob::dispatch($this->resource);
|
||||
DeleteResourceJob::dispatch($this->resource, $this->delete_configurations);
|
||||
return redirect()->route('project.resource.index', [
|
||||
'project_uuid' => $this->projectUuid,
|
||||
'environment_name' => $this->environmentName
|
||||
|
||||
@@ -56,7 +56,7 @@ class Destination extends Component
|
||||
public function redeploy(int $network_id, int $server_id)
|
||||
{
|
||||
if ($this->resource->additional_servers->count() > 0 && str($this->resource->docker_registry_image_name)->isEmpty()) {
|
||||
$this->dispatch('error', 'Failed to deploy.', 'Before deploying to multiple servers, you must first set a Docker image in the General tab.<br>More information here: <a target="_blank" class="underline" href="https://coolify.io/docs/server/multiple-servers">documentation</a>');
|
||||
$this->dispatch('error', 'Failed to deploy.', 'Before deploying to multiple servers, you must first set a Docker image in the General tab.<br>More information here: <a target="_blank" class="underline" href="https://coolify.io/docs/knowledge-base/server/multiple-servers">documentation</a>');
|
||||
return;
|
||||
}
|
||||
$deployment_uuid = new Cuid2(7);
|
||||
|
||||
@@ -98,6 +98,7 @@ class All extends Component
|
||||
}
|
||||
}
|
||||
$environment->is_build_time = false;
|
||||
$environment->is_multiline = false;
|
||||
$environment->is_preview = $isPreview ? true : false;
|
||||
switch ($this->resource->type()) {
|
||||
case 'application':
|
||||
@@ -118,6 +119,15 @@ class All extends Component
|
||||
case 'standalone-mariadb':
|
||||
$environment->standalone_mariadb_id = $this->resource->id;
|
||||
break;
|
||||
case 'standalone-keydb':
|
||||
$environment->standalone_keydb_id = $this->resource->id;
|
||||
break;
|
||||
case 'standalone-dragonfly':
|
||||
$environment->standalone_dragonfly_id = $this->resource->id;
|
||||
break;
|
||||
case 'standalone-clickhouse':
|
||||
$environment->standalone_clickhouse_id = $this->resource->id;
|
||||
break;
|
||||
case 'service':
|
||||
$environment->service_id = $this->resource->id;
|
||||
break;
|
||||
@@ -172,6 +182,15 @@ class All extends Component
|
||||
case 'standalone-mariadb':
|
||||
$environment->standalone_mariadb_id = $this->resource->id;
|
||||
break;
|
||||
case 'standalone-keydb':
|
||||
$environment->standalone_keydb_id = $this->resource->id;
|
||||
break;
|
||||
case 'standalone-dragonfly':
|
||||
$environment->standalone_dragonfly_id = $this->resource->id;
|
||||
break;
|
||||
case 'standalone-clickhouse':
|
||||
$environment->standalone_clickhouse_id = $this->resource->id;
|
||||
break;
|
||||
case 'service':
|
||||
$environment->service_id = $this->resource->id;
|
||||
break;
|
||||
|
||||
@@ -5,11 +5,6 @@ namespace App\Livewire\Project\Shared;
|
||||
use App\Models\Application;
|
||||
use App\Models\Server;
|
||||
use App\Models\Service;
|
||||
use App\Models\StandaloneMariadb;
|
||||
use App\Models\StandaloneMongodb;
|
||||
use App\Models\StandaloneMysql;
|
||||
use App\Models\StandalonePostgresql;
|
||||
use App\Models\StandaloneRedis;
|
||||
use Illuminate\Support\Collection;
|
||||
use Livewire\Component;
|
||||
|
||||
@@ -50,21 +45,9 @@ class ExecuteContainerCommand extends Component
|
||||
}
|
||||
} else if (data_get($this->parameters, 'database_uuid')) {
|
||||
$this->type = 'database';
|
||||
$resource = StandalonePostgresql::where('uuid', $this->parameters['database_uuid'])->first();
|
||||
$resource = getResourceByUuid($this->parameters['database_uuid'], data_get(auth()->user()->currentTeam(),'id'));
|
||||
if (is_null($resource)) {
|
||||
$resource = StandaloneRedis::where('uuid', $this->parameters['database_uuid'])->first();
|
||||
if (is_null($resource)) {
|
||||
$resource = StandaloneMongodb::where('uuid', $this->parameters['database_uuid'])->first();
|
||||
if (is_null($resource)) {
|
||||
$resource = StandaloneMysql::where('uuid', $this->parameters['database_uuid'])->first();
|
||||
if (is_null($resource)) {
|
||||
$resource = StandaloneMariadb::where('uuid', $this->parameters['database_uuid'])->first();
|
||||
if (is_null($resource)) {
|
||||
abort(404);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
abort(404);
|
||||
}
|
||||
$this->resource = $resource;
|
||||
if ($this->resource->destination->server->isFunctional()) {
|
||||
@@ -109,7 +92,7 @@ class ExecuteContainerCommand extends Component
|
||||
];
|
||||
$this->containers = $this->containers->push($payload);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($this->containers->count() > 0) {
|
||||
if (data_get($this->parameters, 'application_uuid')) {
|
||||
|
||||
@@ -7,6 +7,9 @@ use App\Models\Server;
|
||||
use App\Models\Service;
|
||||
use App\Models\ServiceApplication;
|
||||
use App\Models\ServiceDatabase;
|
||||
use App\Models\StandaloneClickhouse;
|
||||
use App\Models\StandaloneDragonfly;
|
||||
use App\Models\StandaloneKeydb;
|
||||
use App\Models\StandaloneMariadb;
|
||||
use App\Models\StandaloneMongodb;
|
||||
use App\Models\StandaloneMysql;
|
||||
@@ -19,7 +22,7 @@ class GetLogs extends Component
|
||||
{
|
||||
public string $outputs = '';
|
||||
public string $errors = '';
|
||||
public Application|Service|StandalonePostgresql|StandaloneRedis|StandaloneMongodb|StandaloneMysql|StandaloneMariadb|null $resource = null;
|
||||
public Application|Service|StandalonePostgresql|StandaloneRedis|StandaloneMongodb|StandaloneMysql|StandaloneMariadb|StandaloneKeydb|StandaloneDragonfly|StandaloneClickhouse|null $resource = null;
|
||||
public ServiceApplication|ServiceDatabase|null $servicesubtype = null;
|
||||
public Server $server;
|
||||
public ?string $container = null;
|
||||
|
||||
@@ -5,6 +5,9 @@ namespace App\Livewire\Project\Shared;
|
||||
use App\Models\Application;
|
||||
use App\Models\Server;
|
||||
use App\Models\Service;
|
||||
use App\Models\StandaloneClickhouse;
|
||||
use App\Models\StandaloneDragonfly;
|
||||
use App\Models\StandaloneKeydb;
|
||||
use App\Models\StandaloneMariadb;
|
||||
use App\Models\StandaloneMongodb;
|
||||
use App\Models\StandaloneMysql;
|
||||
@@ -16,7 +19,7 @@ use Livewire\Component;
|
||||
class Logs extends Component
|
||||
{
|
||||
public ?string $type = null;
|
||||
public Application|Service|StandalonePostgresql|StandaloneRedis|StandaloneMongodb|StandaloneMysql|StandaloneMariadb $resource;
|
||||
public Application|Service|StandalonePostgresql|StandaloneRedis|StandaloneMongodb|StandaloneMysql|StandaloneMariadb|StandaloneKeydb|StandaloneDragonfly|StandaloneClickhouse $resource;
|
||||
public Collection $servers;
|
||||
public Collection $containers;
|
||||
public $container = [];
|
||||
@@ -67,21 +70,9 @@ class Logs extends Component
|
||||
}
|
||||
} else if (data_get($this->parameters, 'database_uuid')) {
|
||||
$this->type = 'database';
|
||||
$resource = StandalonePostgresql::where('uuid', $this->parameters['database_uuid'])->first();
|
||||
$resource = getResourceByUuid($this->parameters['database_uuid'], data_get(auth()->user()->currentTeam(), 'id'));
|
||||
if (is_null($resource)) {
|
||||
$resource = StandaloneRedis::where('uuid', $this->parameters['database_uuid'])->first();
|
||||
if (is_null($resource)) {
|
||||
$resource = StandaloneMongodb::where('uuid', $this->parameters['database_uuid'])->first();
|
||||
if (is_null($resource)) {
|
||||
$resource = StandaloneMysql::where('uuid', $this->parameters['database_uuid'])->first();
|
||||
if (is_null($resource)) {
|
||||
$resource = StandaloneMariadb::where('uuid', $this->parameters['database_uuid'])->first();
|
||||
if (is_null($resource)) {
|
||||
abort(404);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
abort(404);
|
||||
}
|
||||
$this->resource = $resource;
|
||||
$this->status = $this->resource->status;
|
||||
|
||||
@@ -76,7 +76,10 @@ class ResourceOperations extends Component
|
||||
$this->resource->getMorphClass() === 'App\Models\StandaloneMongodb' ||
|
||||
$this->resource->getMorphClass() === 'App\Models\StandaloneMysql' ||
|
||||
$this->resource->getMorphClass() === 'App\Models\StandaloneMariadb' ||
|
||||
$this->resource->getMorphClass() === 'App\Models\StandaloneRedis'
|
||||
$this->resource->getMorphClass() === 'App\Models\StandaloneRedis' ||
|
||||
$this->resource->getMorphClass() === 'App\Models\StandaloneKeydb' ||
|
||||
$this->resource->getMorphClass() === 'App\Models\StandaloneDragonfly' ||
|
||||
$this->resource->getMorphClass() === 'App\Models\StandaloneClickhouse'
|
||||
) {
|
||||
$uuid = (string)new Cuid2(7);
|
||||
$new_resource = $this->resource->replicate()->fill([
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
namespace App\Livewire\Project\Shared\Storages;
|
||||
|
||||
use App\Models\LocalPersistentVolume;
|
||||
use Livewire\Component;
|
||||
|
||||
class All extends Component
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire;
|
||||
|
||||
use Livewire\Component;
|
||||
|
||||
class RealtimeConnection extends Component
|
||||
{
|
||||
public $checkConnection = false;
|
||||
public $showNotification = false;
|
||||
public $isNotificationEnabled = true;
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.realtime-connection');
|
||||
}
|
||||
public function disable()
|
||||
{
|
||||
auth()->user()->update(['is_notification_realtime_enabled' => false]);
|
||||
$this->showNotification = false;
|
||||
}
|
||||
public function mount() {
|
||||
$this->isNotificationEnabled = auth()->user()->is_notification_realtime_enabled;
|
||||
$this->checkConnection = auth()->user()->id === 0;
|
||||
}
|
||||
}
|
||||
@@ -26,7 +26,7 @@ class Create extends Component
|
||||
'value' => 'private Key',
|
||||
];
|
||||
|
||||
public function generateNewKey()
|
||||
public function generateNewRSAKey()
|
||||
{
|
||||
try {
|
||||
$this->rateLimit(10);
|
||||
@@ -37,6 +37,17 @@ class Create extends Component
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
public function generateNewEDKey()
|
||||
{
|
||||
try {
|
||||
$this->rateLimit(10);
|
||||
$this->name = generate_random_name();
|
||||
$this->description = 'Created by Coolify';
|
||||
['private' => $this->value, 'public' => $this->publicKey] = generateSSHKey('ed25519');
|
||||
} catch(\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
public function updated($updateProperty)
|
||||
{
|
||||
if ($updateProperty === 'value') {
|
||||
|
||||
@@ -8,7 +8,7 @@ use Livewire\Component;
|
||||
class Show extends Component
|
||||
{
|
||||
public PrivateKey $private_key;
|
||||
public $public_key;
|
||||
public $public_key = "Loading...";
|
||||
protected $rules = [
|
||||
'private_key.name' => 'required|string',
|
||||
'private_key.description' => 'nullable|string',
|
||||
@@ -25,11 +25,13 @@ class Show extends Component
|
||||
{
|
||||
try {
|
||||
$this->private_key = PrivateKey::ownedByCurrentTeam(['name', 'description', 'private_key', 'is_git_related'])->whereUuid(request()->private_key_uuid)->firstOrFail();
|
||||
$this->public_key = $this->private_key->publicKey();
|
||||
}catch(\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
public function loadPublicKey() {
|
||||
$this->public_key = $this->private_key->publicKey();
|
||||
}
|
||||
public function delete()
|
||||
{
|
||||
try {
|
||||
|
||||
45
app/Livewire/Server/ConfigureCloudflareTunnels.php
Normal file
45
app/Livewire/Server/ConfigureCloudflareTunnels.php
Normal file
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire\Server;
|
||||
|
||||
use App\Actions\Server\ConfigureCloudflared;
|
||||
use App\Models\Server;
|
||||
use Livewire\Component;
|
||||
|
||||
class ConfigureCloudflareTunnels extends Component
|
||||
{
|
||||
public $server_id;
|
||||
public string $cloudflare_token;
|
||||
public string $ssh_domain;
|
||||
public function alreadyConfigured()
|
||||
{
|
||||
try {
|
||||
$server = Server::ownedByCurrentTeam()->where('id', $this->server_id)->firstOrFail();
|
||||
$server->settings->is_cloudflare_tunnel = true;
|
||||
$server->settings->save();
|
||||
$this->dispatch('success', 'Cloudflare Tunnels configured successfully.');
|
||||
$this->dispatch('serverInstalled');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
public function submit()
|
||||
{
|
||||
try {
|
||||
$server = Server::ownedByCurrentTeam()->where('id', $this->server_id)->firstOrFail();
|
||||
ConfigureCloudflared::run($server, $this->cloudflare_token);
|
||||
$server->settings->is_cloudflare_tunnel = true;
|
||||
$server->ip = $this->ssh_domain;
|
||||
$server->save();
|
||||
$server->settings->save();
|
||||
$this->dispatch('success', 'Cloudflare Tunnels configured successfully.');
|
||||
$this->dispatch('serverInstalled');
|
||||
} catch(\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.server.configure-cloudflare-tunnels');
|
||||
}
|
||||
}
|
||||
@@ -75,6 +75,7 @@ class Form extends Component
|
||||
}
|
||||
public function checkLocalhostConnection()
|
||||
{
|
||||
$this->submit();
|
||||
$uptime = $this->server->validateConnection();
|
||||
if ($uptime) {
|
||||
$this->dispatch('success', 'Server is reachable.');
|
||||
@@ -82,7 +83,7 @@ class Form extends Component
|
||||
$this->server->settings->is_usable = true;
|
||||
$this->server->settings->save();
|
||||
} else {
|
||||
$this->dispatch('error', 'Server is not reachable.', 'Please validate your configuration and connection.<br><br>Check this <a target="_blank" class="underline" href="https://coolify.io/docs/server/openssh">documentation</a> for further help.');
|
||||
$this->dispatch('error', 'Server is not reachable.', 'Please validate your configuration and connection.<br><br>Check this <a target="_blank" class="underline" href="https://coolify.io/docs/knowledge-base/server/openssh">documentation</a> for further help.');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,7 +42,11 @@ class Resources extends Component
|
||||
$this->dispatch('success', 'Resource statuses refreshed.');
|
||||
}
|
||||
public function loadUnmanagedContainers() {
|
||||
$this->unmanagedContainers = $this->server->loadUnmanagedContainers();
|
||||
try {
|
||||
$this->unmanagedContainers = $this->server->loadUnmanagedContainers();
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
public function mount() {
|
||||
$this->unmanagedContainers = collect();
|
||||
|
||||
@@ -39,7 +39,7 @@ class ShowPrivateKey extends Component
|
||||
if ($uptime) {
|
||||
$this->dispatch('success', 'Server is reachable.');
|
||||
} else {
|
||||
$this->dispatch('error', 'Server is not reachable.<br>Please validate your configuration and connection.<br><br>Check this <a target="_blank" class="underline" href="https://coolify.io/docs/server/openssh#openssh">documentation</a> for further help.');
|
||||
$this->dispatch('error', 'Server is not reachable.<br>Please validate your configuration and connection.<br><br>Check this <a target="_blank" class="underline" href="https://coolify.io/docs/knowledge-base/server/openssh">documentation</a> for further help.');
|
||||
return;
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
|
||||
@@ -75,7 +75,7 @@ class ValidateAndInstall extends Component
|
||||
{
|
||||
$this->uptime = $this->server->validateConnection();
|
||||
if (!$this->uptime) {
|
||||
$this->error = 'Server is not reachable. Please validate your configuration and connection.<br><br>Check this <a target="_blank" class="underline" href="https://coolify.io/docs/server/openssh">documentation</a> for further help.';
|
||||
$this->error = 'Server is not reachable. Please validate your configuration and connection.<br><br>Check this <a target="_blank" class="underline" href="https://coolify.io/docs/knowledge-base/server/openssh">documentation</a> for further help.';
|
||||
return;
|
||||
}
|
||||
$this->dispatch('validateOS');
|
||||
|
||||
@@ -41,35 +41,39 @@ class Backup extends Component
|
||||
}
|
||||
public function add_coolify_database()
|
||||
{
|
||||
$server = Server::find(0);
|
||||
$out = instant_remote_process(['docker inspect coolify-db'], $server);
|
||||
$envs = format_docker_envs_to_json($out);
|
||||
$postgres_password = $envs['POSTGRES_PASSWORD'];
|
||||
$postgres_user = $envs['POSTGRES_USER'];
|
||||
$postgres_db = $envs['POSTGRES_DB'];
|
||||
$this->database = StandalonePostgresql::create([
|
||||
'id' => 0,
|
||||
'name' => 'coolify-db',
|
||||
'description' => 'Coolify database',
|
||||
'postgres_user' => $postgres_user,
|
||||
'postgres_password' => $postgres_password,
|
||||
'postgres_db' => $postgres_db,
|
||||
'status' => 'running',
|
||||
'destination_type' => 'App\Models\StandaloneDocker',
|
||||
'destination_id' => 0,
|
||||
]);
|
||||
$this->backup = ScheduledDatabaseBackup::create([
|
||||
'id' => 0,
|
||||
'enabled' => true,
|
||||
'save_s3' => false,
|
||||
'frequency' => '0 0 * * *',
|
||||
'database_id' => $this->database->id,
|
||||
'database_type' => 'App\Models\StandalonePostgresql',
|
||||
'team_id' => currentTeam()->id,
|
||||
]);
|
||||
$this->database->refresh();
|
||||
$this->backup->refresh();
|
||||
$this->s3s = S3Storage::whereTeamId(0)->get();
|
||||
try {
|
||||
$server = Server::findOrFail(0);
|
||||
$out = instant_remote_process(['docker inspect coolify-db'], $server);
|
||||
$envs = format_docker_envs_to_json($out);
|
||||
$postgres_password = $envs['POSTGRES_PASSWORD'];
|
||||
$postgres_user = $envs['POSTGRES_USER'];
|
||||
$postgres_db = $envs['POSTGRES_DB'];
|
||||
$this->database = StandalonePostgresql::create([
|
||||
'id' => 0,
|
||||
'name' => 'coolify-db',
|
||||
'description' => 'Coolify database',
|
||||
'postgres_user' => $postgres_user,
|
||||
'postgres_password' => $postgres_password,
|
||||
'postgres_db' => $postgres_db,
|
||||
'status' => 'running',
|
||||
'destination_type' => 'App\Models\StandaloneDocker',
|
||||
'destination_id' => 0,
|
||||
]);
|
||||
$this->backup = ScheduledDatabaseBackup::create([
|
||||
'id' => 0,
|
||||
'enabled' => true,
|
||||
'save_s3' => false,
|
||||
'frequency' => '0 0 * * *',
|
||||
'database_id' => $this->database->id,
|
||||
'database_type' => 'App\Models\StandalonePostgresql',
|
||||
'team_id' => currentTeam()->id,
|
||||
]);
|
||||
$this->database->refresh();
|
||||
$this->backup->refresh();
|
||||
$this->s3s = S3Storage::whereTeamId(0)->get();
|
||||
} catch (\Exception $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
public function backup_now()
|
||||
|
||||
@@ -59,23 +59,37 @@ class Configuration extends Component
|
||||
|
||||
public function submit()
|
||||
{
|
||||
$this->resetErrorBag();
|
||||
if ($this->settings->public_port_min > $this->settings->public_port_max) {
|
||||
$this->addError('settings.public_port_min', 'The minimum port must be lower than the maximum port.');
|
||||
return;
|
||||
try {
|
||||
$error_show = false;
|
||||
$this->server = Server::findOrFail(0);
|
||||
$this->resetErrorBag();
|
||||
if ($this->settings->public_port_min > $this->settings->public_port_max) {
|
||||
$this->addError('settings.public_port_min', 'The minimum port must be lower than the maximum port.');
|
||||
return;
|
||||
}
|
||||
$this->validate();
|
||||
|
||||
if ($this->settings->is_dns_validation_enabled) {
|
||||
if (!validate_dns_entry($this->settings->fqdn, $this->server)) {
|
||||
$this->dispatch('error', "Validating DNS ({$this->settings->fqdn}) failed.<br><br>Make sure you have added the DNS records correctly.<br><br>Check this <a target='_blank' class='underline dark:text-white' href='https://coolify.io/docs/knowledge-base/dns-configuration'>documentation</a> for further help.");
|
||||
$error_show = true;
|
||||
}
|
||||
}
|
||||
check_domain_usage(domain: $this->settings->fqdn);
|
||||
$this->settings->custom_dns_servers = str($this->settings->custom_dns_servers)->replaceEnd(',', '')->trim();
|
||||
$this->settings->custom_dns_servers = str($this->settings->custom_dns_servers)->trim()->explode(',')->map(function ($dns) {
|
||||
return str($dns)->trim()->lower();
|
||||
});
|
||||
$this->settings->custom_dns_servers = $this->settings->custom_dns_servers->unique();
|
||||
$this->settings->custom_dns_servers = $this->settings->custom_dns_servers->implode(',');
|
||||
|
||||
$this->settings->save();
|
||||
$this->server->setupDynamicProxyConfiguration();
|
||||
if (!$error_show) {
|
||||
$this->dispatch('success', 'Instance settings updated successfully!');
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
$this->validate();
|
||||
|
||||
$this->settings->custom_dns_servers = str($this->settings->custom_dns_servers)->replaceEnd(',', '')->trim();
|
||||
$this->settings->custom_dns_servers = str($this->settings->custom_dns_servers)->trim()->explode(',')->map(function ($dns) {
|
||||
return str($dns)->trim()->lower();
|
||||
});
|
||||
$this->settings->custom_dns_servers = $this->settings->custom_dns_servers->unique();
|
||||
$this->settings->custom_dns_servers = $this->settings->custom_dns_servers->implode(',');
|
||||
|
||||
$this->settings->save();
|
||||
$this->server = Server::findOrFail(0);
|
||||
$this->server->setupDynamicProxyConfiguration();
|
||||
$this->dispatch('success', 'Instance settings updated successfully!');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,6 +58,8 @@ class Email extends Component
|
||||
try {
|
||||
$this->resetErrorBag();
|
||||
$this->validate([
|
||||
'settings.smtp_from_address' => 'required|email',
|
||||
'settings.smtp_from_name' => 'required',
|
||||
'settings.resend_api_key' => 'required'
|
||||
]);
|
||||
$this->settings->save();
|
||||
@@ -90,6 +92,8 @@ class Email extends Component
|
||||
try {
|
||||
$this->resetErrorBag();
|
||||
$this->validate([
|
||||
'settings.smtp_from_address' => 'required|email',
|
||||
'settings.smtp_from_name' => 'required',
|
||||
'settings.smtp_host' => 'required',
|
||||
'settings.smtp_port' => 'required|numeric',
|
||||
'settings.smtp_encryption' => 'nullable',
|
||||
|
||||
@@ -15,7 +15,10 @@ class Index extends Component
|
||||
if (!isCloud()) {
|
||||
return redirect(RouteServiceProvider::HOME);
|
||||
}
|
||||
if (data_get(currentTeam(), 'subscription')) {
|
||||
if (auth()->user()?->isMember()) {
|
||||
return redirect()->route('dashboard');
|
||||
}
|
||||
if (data_get(currentTeam(), 'subscription') && isSubscriptionActive()) {
|
||||
return redirect()->route('subscription.show');
|
||||
}
|
||||
$this->settings = InstanceSettings::get();
|
||||
|
||||
@@ -39,6 +39,12 @@ class PricingPlans extends Component
|
||||
case 'ultimate-yearly':
|
||||
$priceId = config('subscription.stripe_price_id_ultimate_yearly');
|
||||
break;
|
||||
case 'dynamic-monthly':
|
||||
$priceId = config('subscription.stripe_price_id_dynamic_monthly');
|
||||
break;
|
||||
case 'dynamic-yearly':
|
||||
$priceId = config('subscription.stripe_price_id_dynamic_yearly');
|
||||
break;
|
||||
default:
|
||||
$priceId = config('subscription.stripe_price_id_basic_monthly');
|
||||
break;
|
||||
@@ -72,6 +78,13 @@ class PricingPlans extends Component
|
||||
];
|
||||
$payload['line_items'][0]['quantity'] = 10;
|
||||
}
|
||||
if (str($type)->contains('dynamic')) {
|
||||
$payload['line_items'][0]['adjustable_quantity'] = [
|
||||
'enabled' => true,
|
||||
'minimum' => 2,
|
||||
];
|
||||
$payload['line_items'][0]['quantity'] = 2;
|
||||
}
|
||||
|
||||
if (!data_get($team, 'subscription.stripe_trial_already_ended')) {
|
||||
if (config('constants.limits.trial_period') > 0) {
|
||||
|
||||
@@ -11,6 +11,9 @@ class Show extends Component
|
||||
if (!isCloud()) {
|
||||
return redirect()->route('dashboard');
|
||||
}
|
||||
if (auth()->user()?->isMember()) {
|
||||
return redirect()->route('dashboard');
|
||||
}
|
||||
if (!data_get(currentTeam(), 'subscription')) {
|
||||
return redirect()->route('subscription.index');
|
||||
}
|
||||
|
||||
@@ -32,13 +32,13 @@ class Upgrade extends Component
|
||||
public function upgrade()
|
||||
{
|
||||
try {
|
||||
$this->rateLimit(1, 30);
|
||||
if ($this->showProgress) {
|
||||
return;
|
||||
}
|
||||
$this->rateLimit(1, 30);
|
||||
$this->showProgress = true;
|
||||
UpdateCoolify::run(true);
|
||||
$this->dispatch('success', "Upgrading to {$this->latestVersion} version...");
|
||||
UpdateCoolify::run(force: true, async: true);
|
||||
$this->dispatch('success', "Updating Coolify to {$this->latestVersion} version...");
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ use App\Enums\ApplicationDeploymentStatus;
|
||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Support\Collection;
|
||||
use Spatie\Activitylog\Models\Activity;
|
||||
use Illuminate\Support\Str;
|
||||
use RuntimeException;
|
||||
@@ -56,6 +57,15 @@ class Application extends BaseModel
|
||||
});
|
||||
}
|
||||
|
||||
public function delete_configurations()
|
||||
{
|
||||
$server = data_get($this, 'destination.server');
|
||||
$workdir = $this->workdir();
|
||||
if (str($workdir)->endsWith($this->uuid)) {
|
||||
ray('Deleting workdir');
|
||||
instant_remote_process(["rm -rf " . $this->workdir()], $server, false);
|
||||
}
|
||||
}
|
||||
public function additional_servers()
|
||||
{
|
||||
return $this->belongsToMany(Server::class, 'additional_destinations')
|
||||
@@ -499,11 +509,11 @@ class Application extends BaseModel
|
||||
}
|
||||
public function isConfigurationChanged(bool $save = false)
|
||||
{
|
||||
$newConfigHash = $this->fqdn . $this->git_repository . $this->git_branch . $this->git_commit_sha . $this->build_pack . $this->static_image . $this->install_command . $this->build_command . $this->start_command . $this->port_exposes . $this->port_mappings . $this->base_directory . $this->publish_directory . $this->dockerfile . $this->dockerfile_location . $this->custom_labels;
|
||||
$newConfigHash = $this->fqdn . $this->git_repository . $this->git_branch . $this->git_commit_sha . $this->build_pack . $this->static_image . $this->install_command . $this->build_command . $this->start_command . $this->ports_exposes . $this->ports_mappings . $this->base_directory . $this->publish_directory . $this->dockerfile . $this->dockerfile_location . $this->custom_labels . $this->custom_docker_run_options . $this->dockerfile_target_build;
|
||||
if ($this->pull_request_id === 0 || $this->pull_request_id === null) {
|
||||
$newConfigHash .= json_encode($this->environment_variables());
|
||||
$newConfigHash .= json_encode($this->environment_variables()->get('updated_at'));
|
||||
} else {
|
||||
$newConfigHash .= json_encode($this->environment_variables_preview->all());
|
||||
$newConfigHash .= json_encode($this->environment_variables_preview->get('updated_at'));
|
||||
}
|
||||
$newConfigHash = md5($newConfigHash);
|
||||
$oldConfigHash = data_get($this, 'config_hash');
|
||||
@@ -597,19 +607,27 @@ class Application extends BaseModel
|
||||
} else {
|
||||
$github_access_token = generate_github_installation_token($this->source);
|
||||
if ($exec_in_docker) {
|
||||
$commands->push(executeInDocker($deployment_uuid, "{$git_clone_command} $source_html_url_scheme://x-access-token:$github_access_token@$source_html_url_host/{$customRepository}.git {$baseDir}"));
|
||||
$git_clone_command = "{$git_clone_command} $source_html_url_scheme://x-access-token:$github_access_token@$source_html_url_host/{$customRepository}.git {$baseDir}";
|
||||
$fullRepoUrl = "$source_html_url_scheme://x-access-token:$github_access_token@$source_html_url_host/{$customRepository}.git";
|
||||
} else {
|
||||
$commands->push("{$git_clone_command} $source_html_url_scheme://x-access-token:$github_access_token@$source_html_url_host/{$customRepository} {$baseDir}");
|
||||
$git_clone_command = "{$git_clone_command} $source_html_url_scheme://x-access-token:$github_access_token@$source_html_url_host/{$customRepository} {$baseDir}";
|
||||
$fullRepoUrl = "$source_html_url_scheme://x-access-token:$github_access_token@$source_html_url_host/{$customRepository}";
|
||||
}
|
||||
$git_clone_command = $this->setGitImportSettings($deployment_uuid, $git_clone_command, public: false);
|
||||
if ($exec_in_docker) {
|
||||
$commands->push(executeInDocker($deployment_uuid, $git_clone_command));
|
||||
} else {
|
||||
$commands->push($git_clone_command);
|
||||
}
|
||||
}
|
||||
if ($pull_request_id !== 0) {
|
||||
$branch = "pull/{$pull_request_id}/head:$pr_branch_name";
|
||||
|
||||
$git_checkout_command = $this->buildGitCheckoutCommand($pr_branch_name);
|
||||
if ($exec_in_docker) {
|
||||
$commands->push(executeInDocker($deployment_uuid, "cd {$baseDir} && git fetch origin {$branch} && git checkout $pr_branch_name"));
|
||||
$commands->push(executeInDocker($deployment_uuid, "cd {$baseDir} && git fetch origin {$branch} && $git_checkout_command"));
|
||||
} else {
|
||||
$commands->push("cd {$baseDir} && git fetch origin {$branch} && git checkout $pr_branch_name");
|
||||
$commands->push("cd {$baseDir} && git fetch origin {$branch} && $git_checkout_command");
|
||||
}
|
||||
}
|
||||
return [
|
||||
@@ -653,7 +671,7 @@ class Application extends BaseModel
|
||||
} else {
|
||||
$commands->push("echo 'Checking out $branch'");
|
||||
}
|
||||
$git_clone_command = "{$git_clone_command} && cd {$baseDir} && GIT_SSH_COMMAND=\"ssh -o ConnectTimeout=30 -p {$customPort} -o Port={$customPort} -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /root/.ssh/id_rsa\" git fetch origin $branch && git checkout $pr_branch_name";
|
||||
$git_clone_command = "{$git_clone_command} && cd {$baseDir} && GIT_SSH_COMMAND=\"ssh -o ConnectTimeout=30 -p {$customPort} -o Port={$customPort} -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /root/.ssh/id_rsa\" git fetch origin $branch && " . $this->buildGitCheckoutCommand($pr_branch_name);
|
||||
} else if ($git_type === 'github') {
|
||||
$branch = "pull/{$pull_request_id}/head:$pr_branch_name";
|
||||
if ($exec_in_docker) {
|
||||
@@ -661,14 +679,14 @@ class Application extends BaseModel
|
||||
} else {
|
||||
$commands->push("echo 'Checking out $branch'");
|
||||
}
|
||||
$git_clone_command = "{$git_clone_command} && cd {$baseDir} && GIT_SSH_COMMAND=\"ssh -o ConnectTimeout=30 -p {$customPort} -o Port={$customPort} -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /root/.ssh/id_rsa\" git fetch origin $branch && git checkout $pr_branch_name";
|
||||
$git_clone_command = "{$git_clone_command} && cd {$baseDir} && GIT_SSH_COMMAND=\"ssh -o ConnectTimeout=30 -p {$customPort} -o Port={$customPort} -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /root/.ssh/id_rsa\" git fetch origin $branch && " . $this->buildGitCheckoutCommand($pr_branch_name);
|
||||
} else if ($git_type === 'bitbucket') {
|
||||
if ($exec_in_docker) {
|
||||
$commands->push(executeInDocker($deployment_uuid, "echo 'Checking out $branch'"));
|
||||
} else {
|
||||
$commands->push("echo 'Checking out $branch'");
|
||||
}
|
||||
$git_clone_command = "{$git_clone_command} && cd {$baseDir} && GIT_SSH_COMMAND=\"ssh -o ConnectTimeout=30 -p {$customPort} -o Port={$customPort} -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /root/.ssh/id_rsa\" git checkout $commit";
|
||||
$git_clone_command = "{$git_clone_command} && cd {$baseDir} && GIT_SSH_COMMAND=\"ssh -o ConnectTimeout=30 -p {$customPort} -o Port={$customPort} -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /root/.ssh/id_rsa\" " . $this->buildGitCheckoutCommand($commit);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -696,7 +714,7 @@ class Application extends BaseModel
|
||||
} else {
|
||||
$commands->push("echo 'Checking out $branch'");
|
||||
}
|
||||
$git_clone_command = "{$git_clone_command} && cd {$baseDir} && GIT_SSH_COMMAND=\"ssh -o ConnectTimeout=30 -p {$customPort} -o Port={$customPort} -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /root/.ssh/id_rsa\" git fetch origin $branch && git checkout $pr_branch_name";
|
||||
$git_clone_command = "{$git_clone_command} && cd {$baseDir} && GIT_SSH_COMMAND=\"ssh -o ConnectTimeout=30 -p {$customPort} -o Port={$customPort} -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /root/.ssh/id_rsa\" git fetch origin $branch && " . $this->buildGitCheckoutCommand($pr_branch_name);
|
||||
} else if ($git_type === 'github') {
|
||||
$branch = "pull/{$pull_request_id}/head:$pr_branch_name";
|
||||
if ($exec_in_docker) {
|
||||
@@ -704,14 +722,14 @@ class Application extends BaseModel
|
||||
} else {
|
||||
$commands->push("echo 'Checking out $branch'");
|
||||
}
|
||||
$git_clone_command = "{$git_clone_command} && cd {$baseDir} && GIT_SSH_COMMAND=\"ssh -o ConnectTimeout=30 -p {$customPort} -o Port={$customPort} -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /root/.ssh/id_rsa\" git fetch origin $branch && git checkout $pr_branch_name";
|
||||
$git_clone_command = "{$git_clone_command} && cd {$baseDir} && GIT_SSH_COMMAND=\"ssh -o ConnectTimeout=30 -p {$customPort} -o Port={$customPort} -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /root/.ssh/id_rsa\" git fetch origin $branch && " . $this->buildGitCheckoutCommand($pr_branch_name);
|
||||
} else if ($git_type === 'bitbucket') {
|
||||
if ($exec_in_docker) {
|
||||
$commands->push(executeInDocker($deployment_uuid, "echo 'Checking out $branch'"));
|
||||
} else {
|
||||
$commands->push("echo 'Checking out $branch'");
|
||||
}
|
||||
$git_clone_command = "{$git_clone_command} && cd {$baseDir} && GIT_SSH_COMMAND=\"ssh -o ConnectTimeout=30 -p {$customPort} -o Port={$customPort} -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /root/.ssh/id_rsa\" git checkout $commit";
|
||||
$git_clone_command = "{$git_clone_command} && cd {$baseDir} && GIT_SSH_COMMAND=\"ssh -o ConnectTimeout=30 -p {$customPort} -o Port={$customPort} -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /root/.ssh/id_rsa\" " . $this->buildGitCheckoutCommand($commit);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -903,4 +921,37 @@ class Application extends BaseModel
|
||||
: explode(',', $this->fqdn),
|
||||
);
|
||||
}
|
||||
protected function buildGitCheckoutCommand($target): string
|
||||
{
|
||||
$command = "git checkout $target";
|
||||
|
||||
if ($this->settings->is_git_submodules_enabled) {
|
||||
$command .= " && git submodule update --init --recursive";
|
||||
}
|
||||
|
||||
return $command;
|
||||
}
|
||||
public function watchPaths(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
set: function ($value) {
|
||||
if ($value) {
|
||||
return trim($value);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
public function isWatchPathsTriggered(Collection $modified_files): bool
|
||||
{
|
||||
if (is_null($this->watch_paths)) {
|
||||
return false;
|
||||
}
|
||||
$watch_paths = collect(explode("\n", $this->watch_paths));
|
||||
$matches = $modified_files->filter(function ($file) use ($watch_paths) {
|
||||
return $watch_paths->contains(function ($glob) use ($file) {
|
||||
return fnmatch($glob, $file);
|
||||
});
|
||||
});
|
||||
return $matches->count() > 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,7 +46,18 @@ class Environment extends Model
|
||||
{
|
||||
return $this->hasMany(StandaloneMariadb::class);
|
||||
}
|
||||
|
||||
public function keydbs()
|
||||
{
|
||||
return $this->hasMany(StandaloneKeydb::class);
|
||||
}
|
||||
public function dragonflies()
|
||||
{
|
||||
return $this->hasMany(StandaloneDragonfly::class);
|
||||
}
|
||||
public function clickhouses()
|
||||
{
|
||||
return $this->hasMany(StandaloneClickhouse::class);
|
||||
}
|
||||
public function databases()
|
||||
{
|
||||
$postgresqls = $this->postgresqls;
|
||||
@@ -54,7 +65,10 @@ class Environment extends Model
|
||||
$mongodbs = $this->mongodbs;
|
||||
$mysqls = $this->mysqls;
|
||||
$mariadbs = $this->mariadbs;
|
||||
return $postgresqls->concat($redis)->concat($mongodbs)->concat($mysqls)->concat($mariadbs);
|
||||
$keydbs = $this->keydbs;
|
||||
$dragonflies = $this->dragonflies;
|
||||
$clickhouses = $this->clickhouses;
|
||||
return $postgresqls->concat($redis)->concat($mongodbs)->concat($mysqls)->concat($mariadbs)->concat($keydbs)->concat($dragonflies)->concat($clickhouses);
|
||||
}
|
||||
|
||||
public function project()
|
||||
|
||||
@@ -25,19 +25,18 @@ class EnvironmentVariable extends Model
|
||||
static::created(function (EnvironmentVariable $environment_variable) {
|
||||
if ($environment_variable->application_id && !$environment_variable->is_preview) {
|
||||
$found = ModelsEnvironmentVariable::where('key', $environment_variable->key)->where('application_id', $environment_variable->application_id)->where('is_preview', true)->first();
|
||||
$application = Application::find($environment_variable->application_id);
|
||||
if ($application->build_pack === 'dockerfile') {
|
||||
return;
|
||||
}
|
||||
if (!$found) {
|
||||
ModelsEnvironmentVariable::create([
|
||||
'key' => $environment_variable->key,
|
||||
'value' => $environment_variable->value,
|
||||
'is_build_time' => $environment_variable->is_build_time,
|
||||
'is_multiline' => $environment_variable->is_multiline,
|
||||
'application_id' => $environment_variable->application_id,
|
||||
'is_preview' => true
|
||||
]);
|
||||
$application = Application::find($environment_variable->application_id);
|
||||
if ($application->build_pack !== 'dockerfile') {
|
||||
ModelsEnvironmentVariable::create([
|
||||
'key' => $environment_variable->key,
|
||||
'value' => $environment_variable->value,
|
||||
'is_build_time' => $environment_variable->is_build_time,
|
||||
'is_multiline' => $environment_variable->is_multiline ?? false,
|
||||
'application_id' => $environment_variable->application_id,
|
||||
'is_preview' => true
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
$environment_variable->update([
|
||||
@@ -64,19 +63,7 @@ class EnvironmentVariable extends Model
|
||||
} else if ($this->service_id) {
|
||||
$resource = Service::find($this->service_id);
|
||||
} else if ($this->database_id) {
|
||||
$resource = StandalonePostgresql::find($this->database_id);
|
||||
if (!$resource) {
|
||||
$resource = StandaloneMysql::find($this->database_id);
|
||||
if (!$resource) {
|
||||
$resource = StandaloneRedis::find($this->database_id);
|
||||
if (!$resource) {
|
||||
$resource = StandaloneMongodb::find($this->database_id);
|
||||
if (!$resource) {
|
||||
$resource = StandaloneMariadb::find($this->database_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$resource = getResourceByUuid($this->parameters['database_uuid'], data_get(auth()->user()->currentTeam(), 'id'));
|
||||
}
|
||||
return $resource;
|
||||
}
|
||||
|
||||
@@ -22,8 +22,14 @@ class LocalFileVolume extends BaseModel
|
||||
}
|
||||
public function saveStorageOnServer()
|
||||
{
|
||||
$workdir = $this->resource->service->workdir();
|
||||
$server = $this->resource->service->server;
|
||||
$isService = data_get($this->resource, 'service');
|
||||
if ($isService) {
|
||||
$workdir = $this->resource->service->workdir();
|
||||
$server = $this->resource->service->server;
|
||||
} else {
|
||||
$workdir = $this->resource->workdir();
|
||||
$server = $this->resource->destination->server;
|
||||
}
|
||||
$commands = collect([
|
||||
"mkdir -p $workdir > /dev/null 2>&1 || true",
|
||||
"cd $workdir"
|
||||
@@ -55,8 +61,18 @@ class LocalFileVolume extends BaseModel
|
||||
if (!$fileVolume->is_directory && $isDir == 'NOK') {
|
||||
if ($content) {
|
||||
$content = base64_encode($content);
|
||||
$chmod = $fileVolume->chmod;
|
||||
$chown = $fileVolume->chown;
|
||||
ray($content, $path, $chmod, $chown);
|
||||
$commands->push("echo '$content' | base64 -d > $path");
|
||||
$commands->push("chmod +x $path");
|
||||
if ($chown) {
|
||||
$commands->push("chown $chown $path");
|
||||
}
|
||||
if ($chmod) {
|
||||
$commands->push("chmod $chmod $path");
|
||||
}
|
||||
|
||||
}
|
||||
} else if ($isDir == 'NOK' && $fileVolume->is_directory) {
|
||||
$commands->push("mkdir -p $path > /dev/null 2>&1 || true");
|
||||
|
||||
@@ -63,6 +63,18 @@ class Project extends BaseModel
|
||||
{
|
||||
return $this->hasManyThrough(StandaloneRedis::class, Environment::class);
|
||||
}
|
||||
public function keydbs()
|
||||
{
|
||||
return $this->hasManyThrough(StandaloneKeydb::class, Environment::class);
|
||||
}
|
||||
public function dragonflies()
|
||||
{
|
||||
return $this->hasManyThrough(StandaloneDragonfly::class, Environment::class);
|
||||
}
|
||||
public function clickhouses()
|
||||
{
|
||||
return $this->hasManyThrough(StandaloneClickhouse::class, Environment::class);
|
||||
}
|
||||
public function mongodbs()
|
||||
{
|
||||
return $this->hasManyThrough(StandaloneMongodb::class, Environment::class);
|
||||
@@ -77,6 +89,6 @@ class Project extends BaseModel
|
||||
}
|
||||
public function resource_count()
|
||||
{
|
||||
return $this->applications()->count() + $this->postgresqls()->count() + $this->redis()->count() + $this->mongodbs()->count() + $this->mysqls()->count() + $this->mariadbs()->count();
|
||||
return $this->applications()->count() + $this->postgresqls()->count() + $this->redis()->count() + $this->mongodbs()->count() + $this->mysqls()->count() + $this->mariadbs()->count() + $this->keydbs()->count() + $this->dragonflies()->count() + $this->services()->count() + $this->clickhouses()->count();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -585,7 +585,10 @@ $schema://$host {
|
||||
$mongodbs = data_get($standaloneDocker, 'mongodbs', collect([]));
|
||||
$mysqls = data_get($standaloneDocker, 'mysqls', collect([]));
|
||||
$mariadbs = data_get($standaloneDocker, 'mariadbs', collect([]));
|
||||
return $postgresqls->concat($redis)->concat($mongodbs)->concat($mysqls)->concat($mariadbs);
|
||||
$keydbs = data_get($standaloneDocker, 'keydbs', collect([]));
|
||||
$dragonflies = data_get($standaloneDocker, 'dragonflies', collect([]));
|
||||
$clickhouses = data_get($standaloneDocker, 'clickhouses', collect([]));
|
||||
return $postgresqls->concat($redis)->concat($mongodbs)->concat($mysqls)->concat($mariadbs)->concat($keydbs)->concat($dragonflies)->concat($clickhouses);
|
||||
})->filter(function ($item) {
|
||||
return data_get($item, 'name') !== 'coolify-db';
|
||||
})->flatten();
|
||||
@@ -690,7 +693,13 @@ $schema://$host {
|
||||
}
|
||||
public function isFunctional()
|
||||
{
|
||||
return $this->settings->is_reachable && $this->settings->is_usable && !$this->settings->force_disabled;
|
||||
$isFunctional = $this->settings->is_reachable && $this->settings->is_usable && !$this->settings->force_disabled;
|
||||
['private_key_filename' => $private_key_filename, 'mux_filename' => $mux_filename] = server_ssh_configuration($this);
|
||||
if (!$isFunctional) {
|
||||
Storage::disk('ssh-keys')->delete($private_key_filename);
|
||||
Storage::disk('ssh-mux')->delete($mux_filename);
|
||||
}
|
||||
return $isFunctional;
|
||||
}
|
||||
public function isLogDrainEnabled()
|
||||
{
|
||||
|
||||
@@ -11,6 +11,46 @@ class Service extends BaseModel
|
||||
{
|
||||
use HasFactory, SoftDeletes;
|
||||
protected $guarded = [];
|
||||
|
||||
public function isConfigurationChanged(bool $save = false)
|
||||
{
|
||||
$domains = $this->applications()->get()->pluck('fqdn')->toArray();
|
||||
$domains = implode(',', $domains);
|
||||
|
||||
$applicationImages = $this->applications()->get()->pluck('image');
|
||||
$databaseImages = $this->databases()->get()->pluck('image');
|
||||
$images = $applicationImages->merge($databaseImages);
|
||||
$images = implode(',', $images->toArray());
|
||||
|
||||
$applicationStorages = $this->applications()->get()->pluck('persistentStorages')->flatten();
|
||||
$databaseStorages = $this->databases()->get()->pluck('persistentStorages')->flatten();
|
||||
$storages = $applicationStorages->merge($databaseStorages)->implode('updated_at');
|
||||
|
||||
$newConfigHash = $images . $domains . $images . $storages;
|
||||
$newConfigHash .= json_encode($this->environment_variables()->get('value'));
|
||||
$newConfigHash = md5($newConfigHash);
|
||||
$oldConfigHash = data_get($this, 'config_hash');
|
||||
if ($oldConfigHash === null) {
|
||||
if ($save) {
|
||||
$this->config_hash = $newConfigHash;
|
||||
$this->save();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if ($oldConfigHash === $newConfigHash) {
|
||||
return false;
|
||||
} else {
|
||||
if ($save) {
|
||||
$this->config_hash = $newConfigHash;
|
||||
$this->save();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
public function isExited()
|
||||
{
|
||||
return (bool) str($this->status())->contains('exited');
|
||||
}
|
||||
public function type()
|
||||
{
|
||||
return 'service';
|
||||
@@ -27,6 +67,14 @@ class Service extends BaseModel
|
||||
{
|
||||
return $this->morphToMany(Tag::class, 'taggable');
|
||||
}
|
||||
public function delete_configurations()
|
||||
{
|
||||
$server = data_get($this, 'server');
|
||||
$workdir = $this->workdir();
|
||||
if (str($workdir)->endsWith($this->uuid)) {
|
||||
instant_remote_process(["rm -rf " . $this->workdir()], $server, false);
|
||||
}
|
||||
}
|
||||
public function status()
|
||||
{
|
||||
$applications = $this->applications;
|
||||
|
||||
210
app/Models/StandaloneClickhouse.php
Normal file
210
app/Models/StandaloneClickhouse.php
Normal file
@@ -0,0 +1,210 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
class StandaloneClickhouse extends BaseModel
|
||||
{
|
||||
use HasFactory, SoftDeletes;
|
||||
|
||||
protected $guarded = [];
|
||||
protected $casts = [
|
||||
'clickhouse_password' => 'encrypted',
|
||||
];
|
||||
|
||||
protected static function booted()
|
||||
{
|
||||
static::created(function ($database) {
|
||||
LocalPersistentVolume::create([
|
||||
'name' => 'clickhouse-data-' . $database->uuid,
|
||||
'mount_path' => '/bitnami/clickhouse',
|
||||
'host_path' => null,
|
||||
'resource_id' => $database->id,
|
||||
'resource_type' => $database->getMorphClass(),
|
||||
'is_readonly' => true
|
||||
]);
|
||||
});
|
||||
static::deleting(function ($database) {
|
||||
$storages = $database->persistentStorages()->get();
|
||||
$server = data_get($database, 'destination.server');
|
||||
if ($server) {
|
||||
foreach ($storages as $storage) {
|
||||
instant_remote_process(["docker volume rm -f $storage->name"], $server, false);
|
||||
}
|
||||
}
|
||||
$database->scheduledBackups()->delete();
|
||||
$database->persistentStorages()->delete();
|
||||
$database->environment_variables()->delete();
|
||||
$database->tags()->detach();
|
||||
});
|
||||
}
|
||||
public function isConfigurationChanged(bool $save = false)
|
||||
{
|
||||
$newConfigHash = $this->image . $this->ports_mappings;
|
||||
$newConfigHash .= json_encode($this->environment_variables()->get('updated_at'));
|
||||
$newConfigHash = md5($newConfigHash);
|
||||
$oldConfigHash = data_get($this, 'config_hash');
|
||||
if ($oldConfigHash === null) {
|
||||
if ($save) {
|
||||
$this->config_hash = $newConfigHash;
|
||||
$this->save();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if ($oldConfigHash === $newConfigHash) {
|
||||
return false;
|
||||
} else {
|
||||
if ($save) {
|
||||
$this->config_hash = $newConfigHash;
|
||||
$this->save();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
public function isExited()
|
||||
{
|
||||
return (bool) str($this->status)->startsWith('exited');
|
||||
}
|
||||
public function workdir()
|
||||
{
|
||||
return database_configuration_dir() . "/{$this->uuid}";
|
||||
}
|
||||
public function delete_configurations()
|
||||
{
|
||||
$server = data_get($this, 'destination.server');
|
||||
$workdir = $this->workdir();
|
||||
if (str($workdir)->endsWith($this->uuid)) {
|
||||
instant_remote_process(["rm -rf " . $this->workdir()], $server, false);
|
||||
}
|
||||
}
|
||||
public function realStatus()
|
||||
{
|
||||
return $this->getRawOriginal('status');
|
||||
}
|
||||
public function status(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
set: function ($value) {
|
||||
if (str($value)->contains('(')) {
|
||||
$status = str($value)->before('(')->trim()->value();
|
||||
$health = str($value)->after('(')->before(')')->trim()->value() ?? 'unhealthy';
|
||||
} else if (str($value)->contains(':')) {
|
||||
$status = str($value)->before(':')->trim()->value();
|
||||
$health = str($value)->after(':')->trim()->value() ?? 'unhealthy';
|
||||
} else {
|
||||
$status = $value;
|
||||
$health = 'unhealthy';
|
||||
}
|
||||
return "$status:$health";
|
||||
},
|
||||
get: function ($value) {
|
||||
if (str($value)->contains('(')) {
|
||||
$status = str($value)->before('(')->trim()->value();
|
||||
$health = str($value)->after('(')->before(')')->trim()->value() ?? 'unhealthy';
|
||||
} else if (str($value)->contains(':')) {
|
||||
$status = str($value)->before(':')->trim()->value();
|
||||
$health = str($value)->after(':')->trim()->value() ?? 'unhealthy';
|
||||
} else {
|
||||
$status = $value;
|
||||
$health = 'unhealthy';
|
||||
}
|
||||
return "$status:$health";
|
||||
},
|
||||
);
|
||||
}
|
||||
public function tags()
|
||||
{
|
||||
return $this->morphToMany(Tag::class, 'taggable');
|
||||
}
|
||||
public function project()
|
||||
{
|
||||
return data_get($this, 'environment.project');
|
||||
}
|
||||
public function link()
|
||||
{
|
||||
if (data_get($this, 'environment.project.uuid')) {
|
||||
return route('project.database.configuration', [
|
||||
'project_uuid' => data_get($this, 'environment.project.uuid'),
|
||||
'environment_name' => data_get($this, 'environment.name'),
|
||||
'database_uuid' => data_get($this, 'uuid')
|
||||
]);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public function isLogDrainEnabled()
|
||||
{
|
||||
return data_get($this, 'is_log_drain_enabled', false);
|
||||
}
|
||||
|
||||
public function portsMappings(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
set: fn ($value) => $value === "" ? null : $value,
|
||||
);
|
||||
}
|
||||
|
||||
public function portsMappingsArray(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
get: fn () => is_null($this->ports_mappings)
|
||||
? []
|
||||
: explode(',', $this->ports_mappings),
|
||||
|
||||
);
|
||||
}
|
||||
public function team()
|
||||
{
|
||||
return data_get($this, 'environment.project.team');
|
||||
}
|
||||
public function type(): string
|
||||
{
|
||||
return 'standalone-clickhouse';
|
||||
}
|
||||
public function get_db_url(bool $useInternal = false): string
|
||||
{
|
||||
if ($this->is_public && !$useInternal) {
|
||||
return "clickhouse://{$this->clickhouse_user}:{$this->clickhouse_password}@{$this->destination->server->getIp}:{$this->public_port}/{$this->clickhouse_db}";
|
||||
} else {
|
||||
return "clickhouse://{$this->clickhouse_user}:{$this->clickhouse_password}@{$this->uuid}:9000/{$this->clickhouse_db}";
|
||||
}
|
||||
}
|
||||
|
||||
public function environment()
|
||||
{
|
||||
return $this->belongsTo(Environment::class);
|
||||
}
|
||||
|
||||
public function fileStorages()
|
||||
{
|
||||
return $this->morphMany(LocalFileVolume::class, 'resource');
|
||||
}
|
||||
|
||||
public function destination()
|
||||
{
|
||||
return $this->morphTo();
|
||||
}
|
||||
|
||||
public function environment_variables(): HasMany
|
||||
{
|
||||
return $this->hasMany(EnvironmentVariable::class);
|
||||
}
|
||||
|
||||
public function runtime_environment_variables(): HasMany
|
||||
{
|
||||
return $this->hasMany(EnvironmentVariable::class);
|
||||
}
|
||||
|
||||
public function persistentStorages()
|
||||
{
|
||||
return $this->morphMany(LocalPersistentVolume::class, 'resource');
|
||||
}
|
||||
|
||||
public function scheduledBackups()
|
||||
{
|
||||
return $this->morphMany(ScheduledDatabaseBackup::class, 'database');
|
||||
}
|
||||
}
|
||||
@@ -32,6 +32,18 @@ class StandaloneDocker extends BaseModel
|
||||
{
|
||||
return $this->morphMany(StandaloneMariadb::class, 'destination');
|
||||
}
|
||||
public function keydbs()
|
||||
{
|
||||
return $this->morphMany(StandaloneKeydb::class, 'destination');
|
||||
}
|
||||
public function dragonflies()
|
||||
{
|
||||
return $this->morphMany(StandaloneDragonfly::class, 'destination');
|
||||
}
|
||||
public function clickhouses()
|
||||
{
|
||||
return $this->morphMany(StandaloneClickhouse::class, 'destination');
|
||||
}
|
||||
|
||||
public function server()
|
||||
{
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user