mirror of
https://github.com/ershisan99/coolify.git
synced 2025-12-18 20:59:24 +00:00
Compare commits
320 Commits
v4.0.0-bet
...
v4.0.0-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
83a3871d43 | ||
|
|
8b6023c45a | ||
|
|
a401d4e760 | ||
|
|
a8520f6593 | ||
|
|
9a95d207cf | ||
|
|
c75d779f85 | ||
|
|
b8f2066408 | ||
|
|
cc48b6cd96 | ||
|
|
b36c735445 | ||
|
|
5f0f26a3ac | ||
|
|
6b81eaa65d | ||
|
|
644f15e80d | ||
|
|
a8ddf4c2df | ||
|
|
3f9833117e | ||
|
|
6140d0c849 | ||
|
|
f4cb7cea21 | ||
|
|
140ec1f90f | ||
|
|
31d7220785 | ||
|
|
38e1d36684 | ||
|
|
6397655f7d | ||
|
|
821bfe68c1 | ||
|
|
596eaa7590 | ||
|
|
08fd0f0fa9 | ||
|
|
77c925ce4b | ||
|
|
3338352b41 | ||
|
|
e3aa4143c7 | ||
|
|
0a93808076 | ||
|
|
0cc21f9b99 | ||
|
|
d3543ea291 | ||
|
|
e824f7a28c | ||
|
|
56be0744f0 | ||
|
|
d874c71e0b | ||
|
|
487177af87 | ||
|
|
0452a4e1ac | ||
|
|
959a03214a | ||
|
|
fe22dfc531 | ||
|
|
139e258664 | ||
|
|
d1ab14966b | ||
|
|
40ff3c731e | ||
|
|
75d77a3648 | ||
|
|
921da7242d | ||
|
|
67c9937e67 | ||
|
|
bdcb3af6fa | ||
|
|
17e81ab6bd | ||
|
|
70e1ec2cd2 | ||
|
|
c6a7fd405b | ||
|
|
4f0d1704c4 | ||
|
|
a966a5097e | ||
|
|
080db4d462 | ||
|
|
4af766162e | ||
|
|
57d67bc4a8 | ||
|
|
16278f36ec | ||
|
|
05c6d67cab | ||
|
|
0c516843d8 | ||
|
|
d400ac57d5 | ||
|
|
2644efd9f7 | ||
|
|
11baa97ef6 | ||
|
|
c36636bd2d | ||
|
|
6bb05a6780 | ||
|
|
360f5db2cf | ||
|
|
bbbeacee4d | ||
|
|
94ad56fc96 | ||
|
|
3df80f2653 | ||
|
|
bb6c9cf49e | ||
|
|
ae12222687 | ||
|
|
094dfde048 | ||
|
|
714b887274 | ||
|
|
8cfa88eff8 | ||
|
|
3976b57100 | ||
|
|
bbcf484f7f | ||
|
|
249f35f948 | ||
|
|
b71f1a79c8 | ||
|
|
e4aa3d310f | ||
|
|
99f57269fb | ||
|
|
93def3a557 | ||
|
|
a04674d93d | ||
|
|
539cc187a8 | ||
|
|
fcdd975751 | ||
|
|
8a4c2bf208 | ||
|
|
50c5d533b0 | ||
|
|
f952553c76 | ||
|
|
9a9be466f7 | ||
|
|
bbad029aa1 | ||
|
|
eb748554c5 | ||
|
|
c8b494e909 | ||
|
|
bd7fd725f6 | ||
|
|
3e0440ba53 | ||
|
|
83a29f8d85 | ||
|
|
8551e6e74a | ||
|
|
3582cb3f46 | ||
|
|
23de13b82c | ||
|
|
93acd4f18f | ||
|
|
96883dabe3 | ||
|
|
da521020f6 | ||
|
|
ac78738061 | ||
|
|
b78ca71c0f | ||
|
|
cd7272d00a | ||
|
|
cd7489c569 | ||
|
|
1f3f369df0 | ||
|
|
911e29f2ea | ||
|
|
d7b359e432 | ||
|
|
0b2c92cbe5 | ||
|
|
f626d964c5 | ||
|
|
b0e0b38549 | ||
|
|
94af4b2c7b | ||
|
|
0b235e146f | ||
|
|
40f3301324 | ||
|
|
4fe90e59a8 | ||
|
|
b28611ed08 | ||
|
|
672ad22e4f | ||
|
|
c17bd5ec3a | ||
|
|
2770755f9d | ||
|
|
ee69cdbf7b | ||
|
|
a72a25640f | ||
|
|
e86f8ddd09 | ||
|
|
66bdb39f1a | ||
|
|
aa368c3a63 | ||
|
|
b499a2ab87 | ||
|
|
49e4482947 | ||
|
|
6e7e4250e6 | ||
|
|
5a8ea15c4f | ||
|
|
f9aa029e8e | ||
|
|
6653e379b3 | ||
|
|
930a611374 | ||
|
|
51e918be6d | ||
|
|
b0170eea8f | ||
|
|
144b34ca2e | ||
|
|
676c022e41 | ||
|
|
7c0b98bb70 | ||
|
|
7779713392 | ||
|
|
5e1396025c | ||
|
|
d09c88f71c | ||
|
|
7980f21b1b | ||
|
|
3843994a05 | ||
|
|
a1b08ca037 | ||
|
|
144cdd11ec | ||
|
|
b6531cdb10 | ||
|
|
0eef4a5fa1 | ||
|
|
42baaf8f2d | ||
|
|
08d9dff8eb | ||
|
|
01b3aab9bc | ||
|
|
fde34ef178 | ||
|
|
5195abec94 | ||
|
|
aa79acd09e | ||
|
|
67011ccd72 | ||
|
|
e2cd7fe17e | ||
|
|
4c0624f489 | ||
|
|
8c033250b1 | ||
|
|
05b5a6fddf | ||
|
|
df5bde7b8e | ||
|
|
ca5f52c48c | ||
|
|
3656adf059 | ||
|
|
4381792b05 | ||
|
|
0a3c20b08a | ||
|
|
4ab3f41665 | ||
|
|
c75ce9cbba | ||
|
|
10356a4376 | ||
|
|
4fb4e19e99 | ||
|
|
98c2056f53 | ||
|
|
74e8ae4d79 | ||
|
|
6b890cf3b3 | ||
|
|
d62b471afe | ||
|
|
b97240963e | ||
|
|
55ed0f2374 | ||
|
|
2c1b3d5790 | ||
|
|
9c3b757578 | ||
|
|
bc04ad96b5 | ||
|
|
35b07a9c18 | ||
|
|
17955fc419 | ||
|
|
ac98f5862a | ||
|
|
63758c6679 | ||
|
|
7c765c47e6 | ||
|
|
58523b0000 | ||
|
|
41f4b36593 | ||
|
|
379212b8fe | ||
|
|
a04c7831b1 | ||
|
|
1aec16a240 | ||
|
|
498626748d | ||
|
|
f24ec97607 | ||
|
|
f0ad260eab | ||
|
|
92ee2be021 | ||
|
|
02e4528f3e | ||
|
|
f871353acc | ||
|
|
faa1120e14 | ||
|
|
8a8000c80a | ||
|
|
8164610105 | ||
|
|
3935a3c885 | ||
|
|
15ec39bc56 | ||
|
|
5dd2b4c439 | ||
|
|
1ee05dda4b | ||
|
|
5a86588cb5 | ||
|
|
af3269b362 | ||
|
|
a7a4baa06e | ||
|
|
9a497b9bba | ||
|
|
d2a1c449da | ||
|
|
504524ea56 | ||
|
|
51e22b406c | ||
|
|
1ec81482cc | ||
|
|
fd610414fd | ||
|
|
1802d5d5da | ||
|
|
e7e6c76b4e | ||
|
|
84c7bd63f5 | ||
|
|
f7187bb798 | ||
|
|
00db5b69ab | ||
|
|
b16109ca56 | ||
|
|
a00b5aa136 | ||
|
|
0e43e5d295 | ||
|
|
9f1e2d6192 | ||
|
|
52c794a259 | ||
|
|
fcf4c5f328 | ||
|
|
85b33a60b3 | ||
|
|
c99bb4cfd7 | ||
|
|
5b0a942b42 | ||
|
|
d94c9e522c | ||
|
|
8fab15abd3 | ||
|
|
7717304549 | ||
|
|
b3f1cc3edd | ||
|
|
16f9c727f2 | ||
|
|
007ba5fce9 | ||
|
|
5b36f07493 | ||
|
|
cbd2580736 | ||
|
|
fe872a59ca | ||
|
|
c4dfd99a8c | ||
|
|
5a34fe4704 | ||
|
|
2fc3dcce7e | ||
|
|
1a932fb749 | ||
|
|
f8a2ee9a8d | ||
|
|
a972d456ba | ||
|
|
e9f4c9e687 | ||
|
|
58f7cdc15e | ||
|
|
3f9b0643ee | ||
|
|
24bd4da94b | ||
|
|
a18b4ffd0f | ||
|
|
c9e032e350 | ||
|
|
e1679ad433 | ||
|
|
933e395945 | ||
|
|
1f64a00bbf | ||
|
|
7eebc0c3a5 | ||
|
|
a2c9dabd77 | ||
|
|
6187c826b4 | ||
|
|
b8987963b3 | ||
|
|
dc7f56b7aa | ||
|
|
2295ddddee | ||
|
|
3328d20b2d | ||
|
|
d444211c68 | ||
|
|
2de8cd1d6f | ||
|
|
115813ac44 | ||
|
|
7c0e8d9421 | ||
|
|
1c349cf55c | ||
|
|
7b4d8a8f05 | ||
|
|
6e7e2de490 | ||
|
|
0168ef55f0 | ||
|
|
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 | ||
|
|
c3da3499d8 | ||
|
|
4d8553f959 | ||
|
|
eee1534da7 | ||
|
|
38c20f5737 | ||
|
|
ba85c5159d | ||
|
|
0388c5d5bc | ||
|
|
3714c80adb | ||
|
|
735c1a23ea | ||
|
|
88cfc96bd3 | ||
|
|
11f389289d | ||
|
|
553c1eacfc | ||
|
|
0032d3cf6c | ||
|
|
bd81cc1cc4 | ||
|
|
bc36fcb722 | ||
|
|
11cd553949 | ||
|
|
45a3f82c8d | ||
|
|
e9c9a51b8d |
1
.github/pull_request_template.md
vendored
Normal file
1
.github/pull_request_template.md
vendored
Normal file
@@ -0,0 +1 @@
|
||||
> Always use `next` branch as destination branch for PRs, not `main`
|
||||
2
.github/workflows/production-build.yml
vendored
2
.github/workflows/production-build.yml
vendored
@@ -3,6 +3,8 @@ name: Production Build (v4)
|
||||
on:
|
||||
push:
|
||||
branches: ["main"]
|
||||
paths-ignore:
|
||||
- templates/service-templates.json
|
||||
|
||||
env:
|
||||
REGISTRY: ghcr.io
|
||||
|
||||
@@ -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/resources/services/add-service).
|
||||
Check out the docs [here](https://coolify.io/docs/knowledge-base/add-a-service).
|
||||
|
||||
|
||||
@@ -33,12 +33,13 @@ 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>
|
||||
|
||||
## Github Sponsors ($40+)
|
||||
<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://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://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 | tee $this->configuration_dir/docker-compose.yml > /dev/null";
|
||||
$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
|
||||
@@ -128,9 +149,9 @@ class StartDatabaseProxy
|
||||
instant_remote_process(["docker rm -f $proxyContainerName"], $server, false);
|
||||
instant_remote_process([
|
||||
"mkdir -p $configuration_dir",
|
||||
"echo '{$dockerfile_base64}' | base64 -d > $configuration_dir/Dockerfile",
|
||||
"echo '{$nginxconf_base64}' | base64 -d > $configuration_dir/nginx.conf",
|
||||
"echo '{$dockercompose_base64}' | base64 -d > $configuration_dir/docker-compose.yaml",
|
||||
"echo '{$dockerfile_base64}' | base64 -d | tee $configuration_dir/Dockerfile > /dev/null",
|
||||
"echo '{$nginxconf_base64}' | base64 -d | tee $configuration_dir/nginx.conf > /dev/null",
|
||||
"echo '{$dockercompose_base64}' | base64 -d | tee $configuration_dir/docker-compose.yaml > /dev/null",
|
||||
"docker compose --project-directory {$configuration_dir} pull",
|
||||
"docker compose --project-directory {$configuration_dir} up --build -d",
|
||||
], $server);
|
||||
|
||||
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 | tee $this->configuration_dir/docker-compose.yml > /dev/null";
|
||||
$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 | tee $this->configuration_dir/docker-compose.yml > /dev/null";
|
||||
$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}");
|
||||
}
|
||||
}
|
||||
@@ -100,7 +100,7 @@ class StartMariadb
|
||||
}
|
||||
$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";
|
||||
$this->commands[] = "echo '{$docker_compose_base64}' | base64 -d | tee $this->configuration_dir/docker-compose.yml > /dev/null";
|
||||
$readme = generate_readme_file($this->database->name, now());
|
||||
$this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md";
|
||||
$this->commands[] = "echo 'Pulling {$database->image} image.'";
|
||||
@@ -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;
|
||||
}
|
||||
@@ -167,6 +171,6 @@ class StartMariadb
|
||||
$filename = 'custom-config.cnf';
|
||||
$content = $this->database->mariadb_conf;
|
||||
$content_base64 = base64_encode($content);
|
||||
$this->commands[] = "echo '{$content_base64}' | base64 -d > $this->configuration_dir/{$filename}";
|
||||
$this->commands[] = "echo '{$content_base64}' | base64 -d | tee $this->configuration_dir/{$filename} > /dev/null";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,7 +116,7 @@ class StartMongodb
|
||||
|
||||
$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";
|
||||
$this->commands[] = "echo '{$docker_compose_base64}' | base64 -d | tee $this->configuration_dir/docker-compose.yml > /dev/null";
|
||||
$readme = generate_readme_file($this->database->name, now());
|
||||
$this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md";
|
||||
$this->commands[] = "echo 'Pulling {$database->image} image.'";
|
||||
@@ -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;
|
||||
}
|
||||
@@ -180,13 +184,13 @@ class StartMongodb
|
||||
$filename = 'mongod.conf';
|
||||
$content = $this->database->mongo_conf;
|
||||
$content_base64 = base64_encode($content);
|
||||
$this->commands[] = "echo '{$content_base64}' | base64 -d > $this->configuration_dir/{$filename}";
|
||||
$this->commands[] = "echo '{$content_base64}' | base64 -d | tee $this->configuration_dir/{$filename} > /dev/null";
|
||||
}
|
||||
private function add_default_database()
|
||||
{
|
||||
$content = "db = db.getSiblingDB(\"{$this->database->mongo_initdb_database}\");db.createCollection('init_collection');db.createUser({user: \"{$this->database->mongo_initdb_root_username}\", pwd: \"{$this->database->mongo_initdb_root_password}\",roles: [{role:\"readWrite\",db:\"{$this->database->mongo_initdb_database}\"}]});";
|
||||
$content_base64 = base64_encode($content);
|
||||
$this->commands[] = "mkdir -p $this->configuration_dir/docker-entrypoint-initdb.d";
|
||||
$this->commands[] = "echo '{$content_base64}' | base64 -d > $this->configuration_dir/docker-entrypoint-initdb.d/01-default-database.js";
|
||||
$this->commands[] = "echo '{$content_base64}' | base64 -d | tee $this->configuration_dir/docker-entrypoint-initdb.d/01-default-database.js > /dev/null";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,7 +100,7 @@ class StartMysql
|
||||
}
|
||||
$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";
|
||||
$this->commands[] = "echo '{$docker_compose_base64}' | base64 -d | tee $this->configuration_dir/docker-compose.yml > /dev/null";
|
||||
$readme = generate_readme_file($this->database->name, now());
|
||||
$this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md";
|
||||
$this->commands[] = "echo 'Pulling {$database->image} image.'";
|
||||
@@ -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;
|
||||
}
|
||||
@@ -167,6 +171,6 @@ class StartMysql
|
||||
$filename = 'custom-config.cnf';
|
||||
$content = $this->database->mysql_conf;
|
||||
$content_base64 = base64_encode($content);
|
||||
$this->commands[] = "echo '{$content_base64}' | base64 -d > $this->configuration_dir/{$filename}";
|
||||
$this->commands[] = "echo '{$content_base64}' | base64 -d | tee $this->configuration_dir/{$filename} > /dev/null";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,7 +122,7 @@ class StartPostgresql
|
||||
}
|
||||
$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";
|
||||
$this->commands[] = "echo '{$docker_compose_base64}' | base64 -d | tee $this->configuration_dir/docker-compose.yml > /dev/null";
|
||||
$readme = generate_readme_file($this->database->name, now());
|
||||
$this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md";
|
||||
$this->commands[] = "echo 'Pulling {$database->image} image.'";
|
||||
@@ -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;
|
||||
}
|
||||
@@ -193,7 +197,7 @@ class StartPostgresql
|
||||
$filename = data_get($init_script, 'filename');
|
||||
$content = data_get($init_script, 'content');
|
||||
$content_base64 = base64_encode($content);
|
||||
$this->commands[] = "echo '{$content_base64}' | base64 -d > $this->configuration_dir/docker-entrypoint-initdb.d/{$filename}";
|
||||
$this->commands[] = "echo '{$content_base64}' | base64 -d | tee $this->configuration_dir/docker-entrypoint-initdb.d/{$filename} > /dev/null";
|
||||
$this->init_scripts[] = "$this->configuration_dir/docker-entrypoint-initdb.d/{$filename}";
|
||||
}
|
||||
}
|
||||
@@ -205,6 +209,6 @@ class StartPostgresql
|
||||
$filename = 'custom-postgres.conf';
|
||||
$content = $this->database->postgres_conf;
|
||||
$content_base64 = base64_encode($content);
|
||||
$this->commands[] = "echo '{$content_base64}' | base64 -d > $this->configuration_dir/{$filename}";
|
||||
$this->commands[] = "echo '{$content_base64}' | base64 -d | tee $this->configuration_dir/{$filename} > /dev/null";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,7 +111,7 @@ class StartRedis
|
||||
}
|
||||
$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";
|
||||
$this->commands[] = "echo '{$docker_compose_base64}' | base64 -d | tee $this->configuration_dir/docker-compose.yml > /dev/null";
|
||||
$readme = generate_readme_file($this->database->name, now());
|
||||
$this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md";
|
||||
$this->commands[] = "echo 'Pulling {$database->image} image.'";
|
||||
@@ -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;
|
||||
|
||||
@@ -16,11 +16,11 @@ class CheckConfiguration
|
||||
return 'OK';
|
||||
}
|
||||
$proxy_path = $server->proxyPath();
|
||||
|
||||
$proxy_configuration = instant_remote_process([
|
||||
$payload = [
|
||||
"mkdir -p $proxy_path",
|
||||
"cat $proxy_path/docker-compose.yml",
|
||||
], $server, false);
|
||||
];
|
||||
$proxy_configuration = instant_remote_process($payload, $server, false);
|
||||
|
||||
if ($reset || !$proxy_configuration || is_null($proxy_configuration)) {
|
||||
$proxy_configuration = Str::of(generate_default_proxy_configuration($server))->trim()->value;
|
||||
|
||||
@@ -13,8 +13,9 @@ class CheckProxy
|
||||
if ($server->proxyType() === 'NONE') {
|
||||
return false;
|
||||
}
|
||||
if (!$server->validateConnection()) {
|
||||
throw new \Exception("Server Connection Error");
|
||||
['uptime' => $uptime, 'error' => $error] = $server->validateConnection();
|
||||
if (!$uptime) {
|
||||
throw new \Exception($error);
|
||||
}
|
||||
if (!$server->isProxyShouldRun()) {
|
||||
if ($fromUI) {
|
||||
|
||||
@@ -23,7 +23,7 @@ class SaveConfiguration
|
||||
|
||||
return instant_remote_process([
|
||||
"mkdir -p $proxy_path",
|
||||
"echo '$docker_compose_yml_base64' | base64 -d > $proxy_path/docker-compose.yml",
|
||||
"echo '$docker_compose_yml_base64' | base64 -d | tee $proxy_path/docker-compose.yml > /dev/null",
|
||||
], $server);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,16 +30,18 @@ class StartProxy
|
||||
$server->save();
|
||||
if ($server->isSwarm()) {
|
||||
$commands = $commands->merge([
|
||||
"mkdir -p $proxy_path/dynamic && cd $proxy_path",
|
||||
"mkdir -p $proxy_path/dynamic",
|
||||
"cd $proxy_path",
|
||||
"echo 'Creating required Docker Compose file.'",
|
||||
"echo 'Starting coolify-proxy.'",
|
||||
"cd $proxy_path && docker stack deploy -c docker-compose.yml coolify-proxy",
|
||||
"docker stack deploy -c docker-compose.yml coolify-proxy",
|
||||
"echo 'Proxy started successfully.'"
|
||||
]);
|
||||
} else {
|
||||
$caddfile = "import /dynamic/*.caddy";
|
||||
$commands = $commands->merge([
|
||||
"mkdir -p $proxy_path/dynamic && cd $proxy_path",
|
||||
"mkdir -p $proxy_path/dynamic",
|
||||
"cd $proxy_path",
|
||||
"echo '$caddfile' > $proxy_path/dynamic/Caddyfile",
|
||||
"echo 'Creating required Docker Compose file.'",
|
||||
"echo 'Pulling docker image.'",
|
||||
|
||||
@@ -29,8 +29,9 @@ class ConfigureCloudflared
|
||||
$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",
|
||||
"mkdir -p /tmp/cloudflared",
|
||||
"cd /tmp/cloudflared",
|
||||
"echo '$docker_compose_yml_base64' | base64 -d | tee docker-compose.yml > /dev/null",
|
||||
"docker compose pull",
|
||||
"docker compose down -v --remove-orphans > /dev/null 2>&1",
|
||||
"docker compose up -d --remove-orphans",
|
||||
@@ -39,6 +40,11 @@ class ConfigureCloudflared
|
||||
} catch (\Throwable $e) {
|
||||
ray($e);
|
||||
throw $e;
|
||||
} finally {
|
||||
$commands = collect([
|
||||
"rm -fr /tmp/cloudflared",
|
||||
]);
|
||||
instant_remote_process($commands, $server);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,20 +48,28 @@ class InstallDocker
|
||||
if ($supported_os_type->contains('debian')) {
|
||||
$command = $command->merge([
|
||||
"echo 'Installing Prerequisites...'",
|
||||
"command -v jq >/dev/null || apt-get update -y",
|
||||
"command -v jq >/dev/null || apt install -y curl wget git jq",
|
||||
|
||||
"apt-get update -y",
|
||||
"command -v curl >/dev/null || apt install -y curl",
|
||||
"command -v wget >/dev/null || apt install -y wget",
|
||||
"command -v git >/dev/null || apt install -y git",
|
||||
"command -v jq >/dev/null || apt install -y jq",
|
||||
]);
|
||||
} else if ($supported_os_type->contains('rhel')) {
|
||||
$command = $command->merge([
|
||||
"echo 'Installing Prerequisites...'",
|
||||
"command -v jq >/dev/null || dnf install -y curl wget git jq",
|
||||
"command -v curl >/dev/null || dnf install -y curl",
|
||||
"command -v wget >/dev/null || dnf install -y wget",
|
||||
"command -v git >/dev/null || dnf install -y git",
|
||||
"command -v jq >/dev/null || dnf install -y jq",
|
||||
]);
|
||||
} else if ($supported_os_type->contains('sles')) {
|
||||
$command = $command->merge([
|
||||
"echo 'Installing Prerequisites...'",
|
||||
"command -v jq >/dev/null || zypper update -y",
|
||||
"command -v jq >/dev/null || zypper install -y curl wget git jq",
|
||||
"zypper update -y",
|
||||
"command -v curl >/dev/null || zypper install -y curl",
|
||||
"command -v wget >/dev/null || zypper install -y wget",
|
||||
"command -v git >/dev/null || zypper install -y git",
|
||||
"command -v jq >/dev/null || zypper install -y jq",
|
||||
]);
|
||||
} else {
|
||||
throw new \Exception('Unsupported OS');
|
||||
@@ -70,10 +78,13 @@ class InstallDocker
|
||||
"echo 'Installing Docker Engine...'",
|
||||
"curl https://releases.rancher.com/install-docker/{$dockerVersion}.sh | sh || curl https://get.docker.com | sh -s -- --version {$dockerVersion}",
|
||||
"echo 'Configuring Docker Engine (merging existing configuration with the required)...'",
|
||||
"test -s /etc/docker/daemon.json && cp /etc/docker/daemon.json \"/etc/docker/daemon.json.original-`date +\"%Y%m%d-%H%M%S\"`\" || echo '{$config}' | base64 -d > /etc/docker/daemon.json",
|
||||
"echo '{$config}' | base64 -d > /etc/docker/daemon.json.coolify",
|
||||
"cat <<< $(jq . /etc/docker/daemon.json.coolify) > /etc/docker/daemon.json.coolify",
|
||||
"cat <<< $(jq -s '.[0] * .[1]' /etc/docker/daemon.json /etc/docker/daemon.json.coolify) > /etc/docker/daemon.json",
|
||||
"test -s /etc/docker/daemon.json && cp /etc/docker/daemon.json \"/etc/docker/daemon.json.original-$(date +\"%Y%m%d-%H%M%S\")\"",
|
||||
"test ! -s /etc/docker/daemon.json && echo '{$config}' | base64 -d | tee /etc/docker/daemon.json > /dev/null",
|
||||
"echo '{$config}' | base64 -d | tee /etc/docker/daemon.json.coolify > /dev/null",
|
||||
"jq . /etc/docker/daemon.json.coolify | tee /etc/docker/daemon.json.coolify.pretty > /dev/null",
|
||||
"mv /etc/docker/daemon.json.coolify.pretty /etc/docker/daemon.json.coolify",
|
||||
"jq -s '.[0] * .[1]' /etc/docker/daemon.json.coolify /etc/docker/daemon.json | tee /etc/docker/daemon.json.appended > /dev/null",
|
||||
"mv /etc/docker/daemon.json.appended /etc/docker/daemon.json",
|
||||
"echo 'Restarting Docker Engine...'",
|
||||
"systemctl enable docker >/dev/null 2>&1 || true",
|
||||
"systemctl restart docker",
|
||||
|
||||
@@ -168,10 +168,10 @@ Files:
|
||||
$command = [
|
||||
"echo 'Saving configuration'",
|
||||
"mkdir -p $config_path",
|
||||
"echo '{$parsers}' | base64 -d > $parsers_config",
|
||||
"echo '{$config}' | base64 -d > $fluent_bit_config",
|
||||
"echo '{$compose}' | base64 -d > $compose_path",
|
||||
"echo '{$readme}' | base64 -d > $readme_path",
|
||||
"echo '{$parsers}' | base64 -d | tee $parsers_config > /dev/null",
|
||||
"echo '{$config}' | base64 -d | tee $fluent_bit_config > /dev/null",
|
||||
"echo '{$compose}' | base64 -d | tee $compose_path > /dev/null",
|
||||
"echo '{$readme}' | base64 -d | tee $readme_path > /dev/null",
|
||||
"test -f $config_path/.env && rm $config_path/.env",
|
||||
|
||||
];
|
||||
|
||||
@@ -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";
|
||||
$keydb->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";
|
||||
$dragonfly->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";
|
||||
$clickhouse->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) {
|
||||
|
||||
@@ -20,6 +20,7 @@ class Init extends Command
|
||||
public function handle()
|
||||
{
|
||||
$this->alive();
|
||||
get_public_ips();
|
||||
$full_cleanup = $this->option('full-cleanup');
|
||||
$cleanup_deployments = $this->option('cleanup-deployments');
|
||||
if ($cleanup_deployments) {
|
||||
@@ -56,6 +57,7 @@ class Init extends Command
|
||||
$this->cleanup_stucked_helper_containers();
|
||||
$this->call('cleanup:stucked-resources');
|
||||
}
|
||||
|
||||
private function restore_coolify_db_backup()
|
||||
{
|
||||
try {
|
||||
|
||||
@@ -31,8 +31,9 @@ class Kernel extends ConsoleKernel
|
||||
$this->check_scheduled_backups($schedule);
|
||||
$this->check_resources($schedule);
|
||||
$this->check_scheduled_backups($schedule);
|
||||
$this->pull_helper_image($schedule);
|
||||
// $this->pull_helper_image($schedule);
|
||||
$this->check_scheduled_tasks($schedule);
|
||||
$schedule->command('uploads:clear')->everyTwoMinutes();
|
||||
} else {
|
||||
// Instance Jobs
|
||||
$schedule->command('horizon:snapshot')->everyFiveMinutes();
|
||||
@@ -49,6 +50,7 @@ class Kernel extends ConsoleKernel
|
||||
$this->check_scheduled_tasks($schedule);
|
||||
|
||||
$schedule->command('cleanup:database --yes')->daily();
|
||||
$schedule->command('uploads:clear')->everyTwoMinutes();
|
||||
}
|
||||
}
|
||||
private function pull_helper_image($schedule)
|
||||
@@ -70,18 +72,18 @@ 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))->everyTwoMinutes()->onOneServer();
|
||||
$schedule->job(new ContainerStatusJob($server))->everyMinute()->onOneServer();
|
||||
if ($server->isLogDrainEnabled()) {
|
||||
$schedule->job(new CheckLogDrainContainerJob($server))->everyTwoMinutes()->onOneServer();
|
||||
$schedule->job(new CheckLogDrainContainerJob($server))->everyMinute()->onOneServer();
|
||||
}
|
||||
}
|
||||
foreach ($servers as $server) {
|
||||
$schedule->job(new ServerStatusJob($server))->everyTwoMinutes()->onOneServer();
|
||||
$schedule->job(new ServerStatusJob($server))->everyMinute()->onOneServer();
|
||||
}
|
||||
}
|
||||
private function instance_auto_update($schedule)
|
||||
{
|
||||
if (isDev()) {
|
||||
if (isDev() || isCloud()) {
|
||||
return;
|
||||
}
|
||||
$settings = InstanceSettings::get();
|
||||
@@ -121,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;
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ class Handler extends ExceptionHandler
|
||||
{
|
||||
$this->reportable(function (Throwable $e) {
|
||||
if (isDev()) {
|
||||
// return;
|
||||
return;
|
||||
}
|
||||
if ($e instanceof RuntimeException) {
|
||||
return;
|
||||
|
||||
@@ -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;
|
||||
@@ -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([
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
@@ -49,6 +49,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
private string $deployment_uuid;
|
||||
private int $pull_request_id;
|
||||
private string $commit;
|
||||
private bool $rollback;
|
||||
private bool $force_rebuild;
|
||||
private bool $restart_only;
|
||||
|
||||
@@ -83,6 +84,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';
|
||||
@@ -93,7 +95,6 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
private ?string $buildTarget = null;
|
||||
private Collection $saved_outputs;
|
||||
private ?string $full_healthcheck_url = null;
|
||||
private bool $custom_healthcheck_found = false;
|
||||
|
||||
private string $serverUser = 'root';
|
||||
private string $serverUserHomeDir = '/root';
|
||||
@@ -116,6 +117,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
$this->deployment_uuid = $this->application_deployment_queue->deployment_uuid;
|
||||
$this->pull_request_id = $this->application_deployment_queue->pull_request_id;
|
||||
$this->commit = $this->application_deployment_queue->commit;
|
||||
$this->rollback = $this->application_deployment_queue->rollback;
|
||||
$this->force_rebuild = $this->application_deployment_queue->force_rebuild;
|
||||
$this->restart_only = $this->application_deployment_queue->restart_only;
|
||||
$this->only_this_server = $this->application_deployment_queue->only_this_server;
|
||||
@@ -239,50 +241,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->application->build_pack !== 'dockerfile') {
|
||||
$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);
|
||||
@@ -317,6 +276,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) {
|
||||
@@ -327,7 +323,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
$this->prepare_builder_image();
|
||||
$this->execute_remote_command(
|
||||
[
|
||||
executeInDocker($this->deployment_uuid, "echo '$dockerfile_base64' | base64 -d > {$this->workdir}{$this->dockerfile_location}")
|
||||
executeInDocker($this->deployment_uuid, "echo '$dockerfile_base64' | base64 -d | tee {$this->workdir}{$this->dockerfile_location} > /dev/null")
|
||||
],
|
||||
);
|
||||
$this->generate_image_names();
|
||||
@@ -336,7 +332,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();
|
||||
@@ -397,7 +392,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
}
|
||||
$this->docker_compose_base64 = base64_encode($yaml);
|
||||
$this->execute_remote_command([
|
||||
executeInDocker($this->deployment_uuid, "echo '{$this->docker_compose_base64}' | base64 -d > {$this->workdir}{$this->docker_compose_location}"), "hidden" => true
|
||||
executeInDocker($this->deployment_uuid, "echo '{$this->docker_compose_base64}' | base64 -d | tee {$this->workdir}{$this->docker_compose_location} > /dev/null"), "hidden" => true
|
||||
]);
|
||||
$this->save_environment_variables();
|
||||
// Build new container to limit downtime.
|
||||
@@ -477,7 +472,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();
|
||||
@@ -506,7 +500,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');
|
||||
@@ -540,7 +533,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();
|
||||
@@ -574,7 +566,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
"mkdir -p $this->configuration_dir"
|
||||
],
|
||||
[
|
||||
"echo '{$this->docker_compose_base64}' | base64 -d > $composeFileName",
|
||||
"echo '{$this->docker_compose_base64}' | base64 -d | tee $composeFileName > /dev/null",
|
||||
],
|
||||
[
|
||||
"echo '{$readme}' > $this->configuration_dir/README.md",
|
||||
@@ -683,7 +675,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
}
|
||||
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();
|
||||
@@ -691,12 +683,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()
|
||||
{
|
||||
@@ -715,21 +709,134 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
private function save_environment_variables()
|
||||
{
|
||||
$envs = collect([]);
|
||||
$ports = $this->application->main_port();
|
||||
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);
|
||||
$real_value = $env->real_value;
|
||||
if ($env->version === '4.0.0-beta.239') {
|
||||
$real_value = $env->real_value;
|
||||
} else {
|
||||
if ($env->is_literal) {
|
||||
$real_value = '\'' . $real_value . '\'';
|
||||
} else {
|
||||
$real_value = escapeEnvVariables($env->real_value);
|
||||
}
|
||||
}
|
||||
$envs->push($env->key . '=' . $real_value);
|
||||
}
|
||||
// Add PORT if not exists, use the first port as default
|
||||
if ($this->application->environment_variables_preview->where('key', 'PORT')->isEmpty()) {
|
||||
$envs->push("PORT={$ports[0]}");
|
||||
}
|
||||
// Add HOST if not exists
|
||||
if ($this->application->environment_variables_preview->where('key', 'HOST')->isEmpty()) {
|
||||
$envs->push("HOST=0.0.0.0");
|
||||
}
|
||||
// Add SOURCE_COMMIT if not exists
|
||||
if ($this->application->environment_variables_preview->where('key', 'SOURCE_COMMIT')->isEmpty()) {
|
||||
if (!is_null($this->commit)) {
|
||||
$envs->push("SOURCE_COMMIT={$this->commit}");
|
||||
} else {
|
||||
$envs->push("SOURCE_COMMIT=unknown");
|
||||
}
|
||||
}
|
||||
$envs = $envs->sort(function ($a, $b) {
|
||||
return strpos($a, '$') === false ? -1 : 1;
|
||||
});
|
||||
} else {
|
||||
$this->env_filename = ".env";
|
||||
foreach ($this->application->environment_variables as $env) {
|
||||
$real_value = $env->real_value;
|
||||
if ($env->version === '4.0.0-beta.239') {
|
||||
$real_value = $env->real_value;
|
||||
} else {
|
||||
if ($env->is_literal) {
|
||||
$real_value = '\'' . $real_value . '\'';
|
||||
} else {
|
||||
$real_value = escapeEnvVariables($env->real_value);
|
||||
}
|
||||
}
|
||||
$envs->push($env->key . '=' . $real_value);
|
||||
}
|
||||
// Add PORT if not exists, use the first port as default
|
||||
if ($this->application->environment_variables->where('key', 'PORT')->isEmpty()) {
|
||||
$envs->push("PORT={$ports[0]}");
|
||||
}
|
||||
// Add HOST if not exists
|
||||
if ($this->application->environment_variables->where('key', 'HOST')->isEmpty()) {
|
||||
$envs->push("HOST=0.0.0.0");
|
||||
}
|
||||
// Add SOURCE_COMMIT if not exists
|
||||
if ($this->application->environment_variables->where('key', 'SOURCE_COMMIT')->isEmpty()) {
|
||||
if (!is_null($this->commit)) {
|
||||
$envs->push("SOURCE_COMMIT={$this->commit}");
|
||||
} else {
|
||||
$envs->push("SOURCE_COMMIT=unknown");
|
||||
}
|
||||
}
|
||||
$envs = $envs->sort(function ($a, $b) {
|
||||
return strpos($a, '$') === false ? -1 : 1;
|
||||
});
|
||||
}
|
||||
|
||||
if ($envs->isEmpty()) {
|
||||
$this->env_filename = null;
|
||||
if ($this->use_build_server) {
|
||||
$this->server = $this->original_server;
|
||||
}
|
||||
$this->execute_remote_command(
|
||||
[
|
||||
"command" => "rm -f $this->configuration_dir/{$this->env_filename}",
|
||||
"hidden" => true,
|
||||
"ignore_errors" => true
|
||||
]
|
||||
);
|
||||
if ($this->use_build_server) {
|
||||
$this->server = $this->build_server;
|
||||
}
|
||||
} else {
|
||||
foreach ($this->application->environment_variables as $env) {
|
||||
$envs->push($env->key . '=' . $env->real_value);
|
||||
$envs_base64 = base64_encode($envs->implode("\n"));
|
||||
$this->execute_remote_command(
|
||||
[
|
||||
executeInDocker($this->deployment_uuid, "echo '$envs_base64' | base64 -d | tee $this->workdir/{$this->env_filename} > /dev/null")
|
||||
],
|
||||
|
||||
);
|
||||
if ($this->use_build_server) {
|
||||
$this->server = $this->original_server;
|
||||
}
|
||||
$this->execute_remote_command(
|
||||
[
|
||||
"echo '$envs_base64' | base64 -d | tee $this->configuration_dir/{$this->env_filename} > /dev/null"
|
||||
]
|
||||
);
|
||||
if ($this->use_build_server) {
|
||||
$this->server = $this->build_server;
|
||||
}
|
||||
}
|
||||
$envs_base64 = base64_encode($envs->implode("\n"));
|
||||
$this->execute_remote_command(
|
||||
[
|
||||
executeInDocker($this->deployment_uuid, "echo '$envs_base64' | base64 -d > $this->workdir/.env")
|
||||
],
|
||||
);
|
||||
// $this->execute_remote_command([
|
||||
// executeInDocker($this->deployment_uuid, "cat $this->workdir/.env 2>/dev/null || true"),
|
||||
// "hidden" => true,
|
||||
// "save" => "dotenv"
|
||||
// ]);
|
||||
// if (str($this->saved_outputs->get('dotenv'))->isNotEmpty()) {
|
||||
// $base64_dotenv = base64_encode($this->saved_outputs->get('dotenv')->value());
|
||||
// $this->execute_remote_command(
|
||||
// [
|
||||
// "echo '{$base64_dotenv}' | base64 -d | tee $this->configuration_dir/.env > /dev/null"
|
||||
// ]
|
||||
// );
|
||||
// } else {
|
||||
// $this->execute_remote_command(
|
||||
// [
|
||||
// "command" => "rm -f $this->configuration_dir/.env",
|
||||
// "hidden" => true,
|
||||
// "ignore_errors" => true
|
||||
// ]
|
||||
// );
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -795,10 +902,13 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
if ($this->server->isSwarm()) {
|
||||
// Implement healthcheck for swarm
|
||||
} else {
|
||||
if ($this->application->isHealthcheckDisabled() && $this->custom_healthcheck_found === false) {
|
||||
if ($this->application->isHealthcheckDisabled() && $this->application->custom_healthcheck_found === false) {
|
||||
$this->newVersionIsHealthy = true;
|
||||
return;
|
||||
}
|
||||
if ($this->application->custom_healthcheck_found) {
|
||||
$this->application_deployment_queue->addLogEntry("Custom healthcheck found, skipping default healthcheck.");
|
||||
}
|
||||
// ray('New container name: ', $this->container_name);
|
||||
if ($this->container_name) {
|
||||
$counter = 1;
|
||||
@@ -806,6 +916,12 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
if ($this->full_healthcheck_url) {
|
||||
$this->application_deployment_queue->addLogEntry("Healthcheck URL (inside the container): {$this->full_healthcheck_url}");
|
||||
}
|
||||
$this->application_deployment_queue->addLogEntry("Waiting for the start period ({$this->application->health_check_start_period} seconds) before starting healthcheck.");
|
||||
$sleeptime = 0;
|
||||
while ($sleeptime < $this->application->health_check_start_period) {
|
||||
Sleep::for(1)->seconds();
|
||||
$sleeptime++;
|
||||
}
|
||||
while ($counter <= $this->application->health_check_retries) {
|
||||
$this->execute_remote_command(
|
||||
[
|
||||
@@ -828,7 +944,11 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
break;
|
||||
}
|
||||
$counter++;
|
||||
Sleep::for($this->application->health_check_interval)->seconds();
|
||||
$sleeptime = 0;
|
||||
while ($sleeptime < $this->application->health_check_interval) {
|
||||
Sleep::for(1)->seconds();
|
||||
$sleeptime++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -868,6 +988,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()
|
||||
@@ -890,6 +1013,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,
|
||||
@@ -956,7 +1084,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
executeInDocker($this->deployment_uuid, "mkdir -p /root/.ssh")
|
||||
],
|
||||
[
|
||||
executeInDocker($this->deployment_uuid, "echo '{$private_key}' | base64 -d > /root/.ssh/id_rsa")
|
||||
executeInDocker($this->deployment_uuid, "echo '{$private_key}' | base64 -d | tee /root/.ssh/id_rsa > /dev/null")
|
||||
],
|
||||
[
|
||||
executeInDocker($this->deployment_uuid, "chmod 600 /root/.ssh/id_rsa")
|
||||
@@ -976,7 +1104,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
],
|
||||
);
|
||||
}
|
||||
if ($this->saved_outputs->get('git_commit_sha')) {
|
||||
if ($this->saved_outputs->get('git_commit_sha') && !$this->rollback) {
|
||||
$this->commit = $this->saved_outputs->get('git_commit_sha')->before("\t");
|
||||
}
|
||||
}
|
||||
@@ -993,6 +1121,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
$importCommands, "hidden" => true
|
||||
]
|
||||
);
|
||||
$this->create_workdir();
|
||||
}
|
||||
|
||||
private function generate_git_import_commands()
|
||||
@@ -1098,15 +1227,16 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
|
||||
private function generate_compose_file()
|
||||
{
|
||||
$ports = $this->application->settings->is_static ? [80] : $this->application->ports_exposes_array;
|
||||
$this->create_workdir();
|
||||
$ports = $this->application->main_port();
|
||||
$onlyPort = null;
|
||||
if (count($ports) > 0) {
|
||||
$onlyPort = $ports[0];
|
||||
}
|
||||
$persistent_storages = $this->generate_local_persistent_volumes();
|
||||
$volume_names = $this->generate_local_persistent_volumes_only_volume_names();
|
||||
$environment_variables = $this->generate_environment_variables($ports);
|
||||
|
||||
// $environment_variables = $this->generate_environment_variables($ports);
|
||||
$this->save_environment_variables();
|
||||
if (data_get($this->application, 'custom_labels')) {
|
||||
$this->application->parseContainerLabels();
|
||||
$labels = collect(preg_split("/\r\n|\n|\r/", base64_decode($this->application->custom_labels)));
|
||||
@@ -1140,18 +1270,18 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
if ($this->pull_request_id !== 0) {
|
||||
$labels = collect(generateLabelsApplication($this->application, $this->preview));
|
||||
}
|
||||
$labels = $labels->map(function ($value, $key) {
|
||||
return escapeDollarSign($value);
|
||||
});
|
||||
$labels = $labels->merge(defaultLabels($this->application->id, $this->application->uuid, $this->pull_request_id))->toArray();
|
||||
|
||||
// Check for custom HEALTHCHECK
|
||||
$this->custom_healthcheck_found = false;
|
||||
if ($this->application->build_pack === 'dockerfile' || $this->application->dockerfile) {
|
||||
$this->execute_remote_command([
|
||||
executeInDocker($this->deployment_uuid, "cat {$this->workdir}{$this->dockerfile_location}"), "hidden" => true, "save" => 'dockerfile_from_repo', "ignore_errors" => true
|
||||
]);
|
||||
$dockerfile = collect(Str::of($this->saved_outputs->get('dockerfile_from_repo'))->trim()->explode("\n"));
|
||||
if (str($dockerfile)->contains('HEALTHCHECK')) {
|
||||
$this->custom_healthcheck_found = true;
|
||||
}
|
||||
$this->application->parseHealthcheckFromDockerfile($dockerfile);
|
||||
}
|
||||
$docker_compose = [
|
||||
'version' => '3.8',
|
||||
@@ -1160,7 +1290,6 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
'image' => $this->production_image_name,
|
||||
'container_name' => $this->container_name,
|
||||
'restart' => RESTART_MODE,
|
||||
'environment' => $environment_variables,
|
||||
'expose' => $ports,
|
||||
'networks' => [
|
||||
$this->destination->network,
|
||||
@@ -1181,18 +1310,34 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
]
|
||||
]
|
||||
];
|
||||
if (!$this->custom_healthcheck_found) {
|
||||
$docker_compose['services'][$this->container_name]['healthcheck'] = [
|
||||
'test' => [
|
||||
'CMD-SHELL',
|
||||
$this->generate_healthcheck_commands()
|
||||
],
|
||||
'interval' => $this->application->health_check_interval . 's',
|
||||
'timeout' => $this->application->health_check_timeout . 's',
|
||||
'retries' => $this->application->health_check_retries,
|
||||
'start_period' => $this->application->health_check_start_period . 's'
|
||||
];
|
||||
// if (str($this->saved_outputs->get('dotenv'))->isNotEmpty()) {
|
||||
// if (data_get($docker_compose, "services.{$this->container_name}.env_file")) {
|
||||
// $docker_compose['services'][$this->container_name]['env_file'][] = '.env';
|
||||
// } else {
|
||||
// $docker_compose['services'][$this->container_name]['env_file'] = ['.env'];
|
||||
// }
|
||||
// }
|
||||
// if ($this->env_filename) {
|
||||
// if (data_get($docker_compose, "services.{$this->container_name}.env_file")) {
|
||||
// $docker_compose['services'][$this->container_name]['env_file'][] = $this->env_filename;
|
||||
// } else {
|
||||
// $docker_compose['services'][$this->container_name]['env_file'] = [$this->env_filename];
|
||||
// }
|
||||
// }
|
||||
if (!is_null($this->env_filename)) {
|
||||
$docker_compose['services'][$this->container_name]['env_file'] = [$this->env_filename];
|
||||
}
|
||||
$docker_compose['services'][$this->container_name]['healthcheck'] = [
|
||||
'test' => [
|
||||
'CMD-SHELL',
|
||||
$this->generate_healthcheck_commands()
|
||||
],
|
||||
'interval' => $this->application->health_check_interval . 's',
|
||||
'timeout' => $this->application->health_check_timeout . 's',
|
||||
'retries' => $this->application->health_check_retries,
|
||||
'start_period' => $this->application->health_check_start_period . 's'
|
||||
];
|
||||
|
||||
if (!is_null($this->application->limits_cpuset)) {
|
||||
data_set($docker_compose, 'services.' . $this->container_name . '.cpuset', $this->application->limits_cpuset);
|
||||
}
|
||||
@@ -1333,14 +1478,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->execute_remote_command([executeInDocker($this->deployment_uuid, "echo '{$this->docker_compose_base64}' | base64 -d | tee {$this->workdir}/docker-compose.yml > /dev/null"), "hidden" => true]);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -1370,7 +1519,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
return $local_persistent_volumes_names;
|
||||
}
|
||||
|
||||
private function generate_environment_variables($ports)
|
||||
/*private function generate_environment_variables($ports)
|
||||
{
|
||||
$environment_variables = collect();
|
||||
if ($this->pull_request_id === 0) {
|
||||
@@ -1386,7 +1535,12 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
} else {
|
||||
$real_value = escapeEnvVariables($env->real_value);
|
||||
}
|
||||
$environment_variables->push("$env->key=$real_value");
|
||||
if ($env->is_literal) {
|
||||
$real_value = escapeDollarSign($real_value);
|
||||
$environment_variables->push("$env->key='$real_value'");
|
||||
} else {
|
||||
$environment_variables->push("$env->key=$real_value");
|
||||
}
|
||||
}
|
||||
foreach ($this->application->nixpacks_environment_variables as $env) {
|
||||
if ($env->version === '4.0.0-beta.239') {
|
||||
@@ -1394,7 +1548,12 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
} else {
|
||||
$real_value = escapeEnvVariables($env->real_value);
|
||||
}
|
||||
$environment_variables->push("$env->key=$real_value");
|
||||
if ($env->is_literal) {
|
||||
$real_value = escapeDollarSign($real_value);
|
||||
$environment_variables->push("$env->key='$real_value'");
|
||||
} else {
|
||||
$environment_variables->push("$env->key=$real_value");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
foreach ($this->application->runtime_environment_variables_preview as $env) {
|
||||
@@ -1403,7 +1562,12 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
} else {
|
||||
$real_value = escapeEnvVariables($env->real_value);
|
||||
}
|
||||
$environment_variables->push("$env->key=$real_value");
|
||||
if ($env->is_literal) {
|
||||
$real_value = escapeDollarSign($real_value);
|
||||
$environment_variables->push("$env->key='$real_value'");
|
||||
} else {
|
||||
$environment_variables->push("$env->key=$real_value");
|
||||
}
|
||||
}
|
||||
foreach ($this->application->nixpacks_environment_variables_preview as $env) {
|
||||
if ($env->version === '4.0.0-beta.239') {
|
||||
@@ -1411,7 +1575,12 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
} else {
|
||||
$real_value = escapeEnvVariables($env->real_value);
|
||||
}
|
||||
$environment_variables->push("$env->key=$real_value");
|
||||
if ($env->is_literal) {
|
||||
$real_value = escapeDollarSign($real_value);
|
||||
$environment_variables->push("$env->key='$real_value'");
|
||||
} else {
|
||||
$environment_variables->push("$env->key=$real_value");
|
||||
}
|
||||
}
|
||||
}
|
||||
// Add PORT if not exists, use the first port as default
|
||||
@@ -1429,15 +1598,16 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
$environment_variables->push("SOURCE_COMMIT=unknown");
|
||||
}
|
||||
}
|
||||
ray($environment_variables->all());
|
||||
return $environment_variables->all();
|
||||
}
|
||||
}*/
|
||||
|
||||
private function generate_healthcheck_commands()
|
||||
{
|
||||
if ($this->application->dockerfile || $this->application->build_pack === 'dockerfile' || $this->application->build_pack === 'dockerimage') {
|
||||
// TODO: disabled HC because there are several ways to hc a simple docker image, hard to figure out a good way. Like some docker images (pocketbase) does not have curl.
|
||||
return 'exit 0';
|
||||
}
|
||||
// if ($this->application->dockerfile || $this->application->build_pack === 'dockerfile' || $this->application->build_pack === 'dockerimage') {
|
||||
// // TODO: disabled HC because there are several ways to hc a simple docker image, hard to figure out a good way. Like some docker images (pocketbase) does not have curl.
|
||||
// return 'exit 0';
|
||||
// }
|
||||
if (!$this->application->health_check_port) {
|
||||
$health_check_port = $this->application->ports_exposes_array[0];
|
||||
} else {
|
||||
@@ -1510,7 +1680,7 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
|
||||
} else {
|
||||
if ($this->application->build_pack === 'nixpacks') {
|
||||
$this->nixpacks_plan = base64_encode($this->nixpacks_plan);
|
||||
$this->execute_remote_command([executeInDocker($this->deployment_uuid, "echo '{$this->nixpacks_plan}' | base64 -d > /artifacts/thegameplan.json"), "hidden" => true]);
|
||||
$this->execute_remote_command([executeInDocker($this->deployment_uuid, "echo '{$this->nixpacks_plan}' | base64 -d | tee /artifacts/thegameplan.json > /dev/null"), "hidden" => true]);
|
||||
if ($this->force_rebuild) {
|
||||
$this->execute_remote_command([
|
||||
executeInDocker($this->deployment_uuid, "nixpacks build -c /artifacts/thegameplan.json --no-cache --no-error-without-start -n {$this->build_image_name} {$this->workdir}"), "hidden" => true
|
||||
@@ -1531,7 +1701,7 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
|
||||
}
|
||||
$this->execute_remote_command(
|
||||
[
|
||||
executeInDocker($this->deployment_uuid, "echo '{$base64_build_command}' | base64 -d > /artifacts/build.sh"), "hidden" => true
|
||||
executeInDocker($this->deployment_uuid, "echo '{$base64_build_command}' | base64 -d | tee /artifacts/build.sh > /dev/null"), "hidden" => true
|
||||
],
|
||||
[
|
||||
executeInDocker($this->deployment_uuid, "bash /artifacts/build.sh"), "hidden" => true
|
||||
@@ -1566,13 +1736,13 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
|
||||
$base64_build_command = base64_encode($build_command);
|
||||
$this->execute_remote_command(
|
||||
[
|
||||
executeInDocker($this->deployment_uuid, "echo '{$dockerfile}' | base64 -d > {$this->workdir}/Dockerfile")
|
||||
executeInDocker($this->deployment_uuid, "echo '{$dockerfile}' | base64 -d | tee {$this->workdir}/Dockerfile > /dev/null")
|
||||
],
|
||||
[
|
||||
executeInDocker($this->deployment_uuid, "echo '{$nginx_config}' | base64 -d > {$this->workdir}/nginx.conf")
|
||||
executeInDocker($this->deployment_uuid, "echo '{$nginx_config}' | base64 -d | tee {$this->workdir}/nginx.conf > /dev/null")
|
||||
],
|
||||
[
|
||||
executeInDocker($this->deployment_uuid, "echo '{$base64_build_command}' | base64 -d > /artifacts/build.sh"), "hidden" => true
|
||||
executeInDocker($this->deployment_uuid, "echo '{$base64_build_command}' | base64 -d | tee /artifacts/build.sh > /dev/null"), "hidden" => true
|
||||
],
|
||||
[
|
||||
executeInDocker($this->deployment_uuid, "bash /artifacts/build.sh"), "hidden" => true
|
||||
@@ -1585,7 +1755,7 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
|
||||
$base64_build_command = base64_encode($build_command);
|
||||
$this->execute_remote_command(
|
||||
[
|
||||
executeInDocker($this->deployment_uuid, "echo '{$base64_build_command}' | base64 -d > /artifacts/build.sh"), "hidden" => true
|
||||
executeInDocker($this->deployment_uuid, "echo '{$base64_build_command}' | base64 -d | tee /artifacts/build.sh > /dev/null"), "hidden" => true
|
||||
],
|
||||
[
|
||||
executeInDocker($this->deployment_uuid, "bash /artifacts/build.sh"), "hidden" => true
|
||||
@@ -1594,7 +1764,7 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
|
||||
} else {
|
||||
if ($this->application->build_pack === 'nixpacks') {
|
||||
$this->nixpacks_plan = base64_encode($this->nixpacks_plan);
|
||||
$this->execute_remote_command([executeInDocker($this->deployment_uuid, "echo '{$this->nixpacks_plan}' | base64 -d > /artifacts/thegameplan.json"), "hidden" => true]);
|
||||
$this->execute_remote_command([executeInDocker($this->deployment_uuid, "echo '{$this->nixpacks_plan}' | base64 -d | tee /artifacts/thegameplan.json > /dev/null"), "hidden" => true]);
|
||||
if ($this->force_rebuild) {
|
||||
$this->execute_remote_command([
|
||||
executeInDocker($this->deployment_uuid, "nixpacks build -c /artifacts/thegameplan.json --no-cache --no-error-without-start -n {$this->production_image_name} {$this->workdir}"), "hidden" => true
|
||||
@@ -1615,7 +1785,7 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
|
||||
}
|
||||
$this->execute_remote_command(
|
||||
[
|
||||
executeInDocker($this->deployment_uuid, "echo '{$base64_build_command}' | base64 -d > /artifacts/build.sh"), "hidden" => true
|
||||
executeInDocker($this->deployment_uuid, "echo '{$base64_build_command}' | base64 -d | tee /artifacts/build.sh > /dev/null"), "hidden" => true
|
||||
],
|
||||
[
|
||||
executeInDocker($this->deployment_uuid, "bash /artifacts/build.sh"), "hidden" => true
|
||||
@@ -1733,7 +1903,7 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
|
||||
}
|
||||
$dockerfile_base64 = base64_encode($dockerfile->implode("\n"));
|
||||
$this->execute_remote_command([
|
||||
executeInDocker($this->deployment_uuid, "echo '{$dockerfile_base64}' | base64 -d > {$this->workdir}{$this->dockerfile_location}"),
|
||||
executeInDocker($this->deployment_uuid, "echo '{$dockerfile_base64}' | base64 -d | tee {$this->workdir}{$this->dockerfile_location} > /dev/null"),
|
||||
"hidden" => true
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -47,7 +47,6 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
|
||||
if (!$this->server->isFunctional()) {
|
||||
return 'Server is not ready.';
|
||||
};
|
||||
|
||||
$applications = $this->server->applications();
|
||||
$skip_these_applications = collect([]);
|
||||
foreach ($applications as $application) {
|
||||
@@ -78,6 +77,7 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
|
||||
if (is_null($containers)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$containers = format_docker_command_output_to_json($containers);
|
||||
if ($containerReplicates) {
|
||||
$containerReplicates = format_docker_command_output_to_json($containerReplicates);
|
||||
@@ -201,7 +201,6 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
|
||||
// Notify user that this container should not be there.
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
if (data_get($container, 'Name') === '/coolify-db') {
|
||||
$foundDatabases[] = 0;
|
||||
|
||||
@@ -289,7 +289,7 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted
|
||||
if ($this->backup->save_s3) {
|
||||
$this->upload_to_s3();
|
||||
}
|
||||
$this->team?->notify(new BackupSuccess($this->backup, $this->database));
|
||||
$this->team?->notify(new BackupSuccess($this->backup, $this->database, $database));
|
||||
$this->backup_log->update([
|
||||
'status' => 'success',
|
||||
'message' => $this->backup_output,
|
||||
@@ -305,8 +305,7 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted
|
||||
]);
|
||||
}
|
||||
send_internal_notification('DatabaseBackupJob failed with: ' . $e->getMessage());
|
||||
$this->team?->notify(new BackupFailed($this->backup, $this->database, $this->backup_output));
|
||||
throw $e;
|
||||
$this->team?->notify(new BackupFailed($this->backup, $this->database, $this->backup_output, $database));
|
||||
}
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
@@ -319,10 +318,15 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted
|
||||
private function backup_standalone_mongodb(string $databaseWithCollections): void
|
||||
{
|
||||
try {
|
||||
ray($this->database->toArray());
|
||||
$url = $this->database->get_db_url(useInternal: true);
|
||||
if ($databaseWithCollections === 'all') {
|
||||
$commands[] = "mkdir -p " . $this->backup_dir;
|
||||
$commands[] = "docker exec $this->container_name mongodump --authenticationDatabase=admin --uri=$url --gzip --archive > $this->backup_location";
|
||||
if (str($this->database->image)->startsWith('mongo:4.0')) {
|
||||
$commands[] = "docker exec $this->container_name mongodump --uri=$url --gzip --archive > $this->backup_location";
|
||||
} else {
|
||||
$commands[] = "docker exec $this->container_name mongodump --authenticationDatabase=admin --uri=$url --gzip --archive > $this->backup_location";
|
||||
}
|
||||
} else {
|
||||
if (str($databaseWithCollections)->contains(':')) {
|
||||
$databaseName = str($databaseWithCollections)->before(':');
|
||||
@@ -333,9 +337,17 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted
|
||||
}
|
||||
$commands[] = "mkdir -p " . $this->backup_dir;
|
||||
if ($collectionsToExclude->count() === 0) {
|
||||
$commands[] = "docker exec $this->container_name mongodump --authenticationDatabase=admin --uri=$url --db $databaseName --gzip --archive > $this->backup_location";
|
||||
if (str($this->database->image)->startsWith('mongo:4.0')) {
|
||||
$commands[] = "docker exec $this->container_name mongodump --uri=$url --gzip --archive > $this->backup_location";
|
||||
} else {
|
||||
$commands[] = "docker exec $this->container_name mongodump --authenticationDatabase=admin --uri=$url --db $databaseName --gzip --archive > $this->backup_location";
|
||||
}
|
||||
} else {
|
||||
$commands[] = "docker exec $this->container_name mongodump --authenticationDatabase=admin --uri=$url --db $databaseName --gzip --excludeCollection " . $collectionsToExclude->implode(' --excludeCollection ') . " --archive > $this->backup_location";
|
||||
if (str($this->database->image)->startsWith('mongo:4.0')) {
|
||||
$commands[] = "docker exec $this->container_name mongodump --uri=$url --gzip --excludeCollection " . $collectionsToExclude->implode(' --excludeCollection ') . " --archive > $this->backup_location";
|
||||
} else {
|
||||
$commands[] = "docker exec $this->container_name mongodump --authenticationDatabase=admin --uri=$url --db $databaseName --gzip --excludeCollection " . $collectionsToExclude->implode(' --excludeCollection ') . " --archive > $this->backup_location";
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->backup_output = instant_remote_process($commands, $this->server);
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -15,7 +15,8 @@ class InstanceAutoUpdateJob implements ShouldQueue, ShouldBeUnique, ShouldBeEncr
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
public $timeout = 120;
|
||||
public $timeout = 600;
|
||||
public $tries = 1;
|
||||
|
||||
public function __construct(private bool $force = false)
|
||||
{
|
||||
|
||||
@@ -20,11 +20,12 @@ class SendMessageToDiscordJob implements ShouldQueue, ShouldBeEncrypted
|
||||
* @var int
|
||||
*/
|
||||
public $tries = 5;
|
||||
public $backoff = 10;
|
||||
|
||||
/**
|
||||
* The maximum number of unhandled exceptions to allow before failing.
|
||||
*/
|
||||
public int $maxExceptions = 3;
|
||||
public int $maxExceptions = 5;
|
||||
|
||||
public function __construct(
|
||||
public string $text,
|
||||
|
||||
@@ -57,7 +57,7 @@ class SendMessageToTelegramJob implements ShouldQueue, ShouldBeEncrypted
|
||||
}
|
||||
}
|
||||
$payload = [
|
||||
'parse_mode' => 'markdown',
|
||||
// 'parse_mode' => 'markdown',
|
||||
'reply_markup' => json_encode([
|
||||
'inline_keyboard' => [
|
||||
[...$inlineButtons],
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Jobs;
|
||||
|
||||
use App\Models\Application;
|
||||
use App\Models\ServiceApplication;
|
||||
use App\Models\ServiceDatabase;
|
||||
use Illuminate\Bus\Queueable;
|
||||
@@ -16,11 +17,11 @@ class ServerFilesFromServerJob implements ShouldQueue, ShouldBeEncrypted
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
|
||||
public function __construct(public ServiceApplication|ServiceDatabase $service)
|
||||
public function __construct(public ServiceApplication|ServiceDatabase|Application $resource)
|
||||
{
|
||||
}
|
||||
public function handle()
|
||||
{
|
||||
$this->service->getFilesFromServer(isInit: true);
|
||||
$this->resource->getFilesFromServer(isInit: true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,11 +38,12 @@ class ServerStatusJob implements ShouldQueue, ShouldBeEncrypted
|
||||
public function handle()
|
||||
{
|
||||
if (!$this->server->isServerReady($this->tries)) {
|
||||
throw new \RuntimeException('Server is not ready.');
|
||||
return "Server is not ready yet.";
|
||||
};
|
||||
try {
|
||||
if ($this->server->isFunctional()) {
|
||||
$this->cleanup(notify: false);
|
||||
$this->removeCoolifyYaml();
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
send_internal_notification('ServerStatusJob failed with: ' . $e->getMessage());
|
||||
@@ -50,6 +51,16 @@ class ServerStatusJob implements ShouldQueue, ShouldBeEncrypted
|
||||
return handleError($e);
|
||||
}
|
||||
}
|
||||
private function removeCoolifyYaml()
|
||||
{
|
||||
// This will remote the coolify.yaml file from the server as it is not needed on cloud servers
|
||||
if (isCloud() && $this->server->id !== 0) {
|
||||
$file = $this->server->proxyPath() . "/dynamic/coolify.yaml";
|
||||
return instant_remote_process([
|
||||
"rm -f $file",
|
||||
], $this->server, false);
|
||||
}
|
||||
}
|
||||
public function cleanup(bool $notify = false): void
|
||||
{
|
||||
$this->disk_usage = $this->server->getDiskUsage();
|
||||
|
||||
@@ -13,6 +13,7 @@ class ActivityMonitor extends Component
|
||||
public $activityId;
|
||||
public $eventToDispatch = 'activityFinished';
|
||||
public $isPollingActive = false;
|
||||
public bool $fullHeight = false;
|
||||
public bool $showWaiting = false;
|
||||
|
||||
protected $activity;
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
@@ -11,15 +11,17 @@ class NewActivityMonitor extends Component
|
||||
public ?string $header = null;
|
||||
public $activityId;
|
||||
public $eventToDispatch = 'activityFinished';
|
||||
public $eventData = null;
|
||||
public $isPollingActive = false;
|
||||
|
||||
protected $activity;
|
||||
protected $listeners = ['newActivityMonitor' => 'newMonitorActivity'];
|
||||
|
||||
public function newMonitorActivity($activityId, $eventToDispatch = 'activityFinished')
|
||||
public function newMonitorActivity($activityId, $eventToDispatch = 'activityFinished', $eventData = null)
|
||||
{
|
||||
$this->activityId = $activityId;
|
||||
$this->eventToDispatch = $eventToDispatch;
|
||||
$this->eventData = $eventData;
|
||||
|
||||
$this->hydrateActivity();
|
||||
|
||||
@@ -55,8 +57,12 @@ class NewActivityMonitor extends Component
|
||||
}
|
||||
return;
|
||||
}
|
||||
$this->dispatch($this->eventToDispatch);
|
||||
ray('Dispatched event: ' . $this->eventToDispatch);
|
||||
if (!is_null($this->eventData)) {
|
||||
$this->dispatch($this->eventToDispatch, $this->eventData);
|
||||
} else {
|
||||
$this->dispatch($this->eventToDispatch);
|
||||
}
|
||||
ray('Dispatched event: ' . $this->eventToDispatch . ' with data: ' . $this->eventData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,8 @@ class Advanced extends Component
|
||||
'application.settings.gpu_count' => 'string|required',
|
||||
'application.settings.gpu_device_ids' => 'string|required',
|
||||
'application.settings.gpu_options' => 'string|required',
|
||||
'application.settings.is_raw_compose_deployment_enabled' => 'boolean|required',
|
||||
'application.settings.connect_to_docker_network' => 'boolean|required',
|
||||
];
|
||||
public function mount() {
|
||||
$this->is_force_https_enabled = $this->application->isForceHttpsEnabled();
|
||||
@@ -54,8 +56,14 @@ class Advanced extends Component
|
||||
$this->application->settings->is_stripprefix_enabled = $this->is_stripprefix_enabled;
|
||||
$this->dispatch('resetDefaultLabels', false);
|
||||
}
|
||||
if ($this->application->settings->is_raw_compose_deployment_enabled) {
|
||||
$this->application->parseRawCompose();
|
||||
} else {
|
||||
$this->application->parseCompose();
|
||||
}
|
||||
$this->application->settings->save();
|
||||
$this->dispatch('success', 'Settings saved.');
|
||||
$this->dispatch('configurationChanged');
|
||||
}
|
||||
public function submit() {
|
||||
if ($this->application->settings->gpu_count && $this->application->settings->gpu_device_ids) {
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace App\Livewire\Project\Application;
|
||||
|
||||
use App\Models\Application;
|
||||
use App\Models\LocalFileVolume;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Str;
|
||||
use Livewire\Component;
|
||||
@@ -24,7 +25,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;
|
||||
@@ -33,7 +34,8 @@ class General extends Component
|
||||
public $parsedServiceDomains = [];
|
||||
|
||||
protected $listeners = [
|
||||
'resetDefaultLabels'
|
||||
'resetDefaultLabels',
|
||||
'configurationChanged' => '$refresh'
|
||||
];
|
||||
protected $rules = [
|
||||
'application.name' => 'required',
|
||||
@@ -71,7 +73,6 @@ class General extends Component
|
||||
'application.post_deployment_command' => 'nullable',
|
||||
'application.post_deployment_command_container' => 'nullable',
|
||||
'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',
|
||||
];
|
||||
@@ -107,7 +108,6 @@ class General extends Component
|
||||
'application.docker_compose_custom_start_command' => 'Docker compose custom start command',
|
||||
'application.docker_compose_custom_build_command' => 'Docker compose custom build command',
|
||||
'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',
|
||||
];
|
||||
@@ -123,12 +123,7 @@ 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();
|
||||
if (!$this->customLabels && $this->application->destination->server->proxyType() !== 'NONE') {
|
||||
$this->customLabels = str(implode("|", generateLabelsApplication($this->application)))->replace("|", "\n");
|
||||
@@ -136,6 +131,14 @@ class General extends Component
|
||||
$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()
|
||||
{
|
||||
@@ -153,12 +156,44 @@ class General extends Component
|
||||
return;
|
||||
}
|
||||
['parsedServices' => $this->parsedServices, 'initialDockerComposeLocation' => $this->initialDockerComposeLocation, 'initialDockerComposePrLocation' => $this->initialDockerComposePrLocation] = $this->application->loadComposeFile($isInit);
|
||||
$compose = $this->application->parseCompose();
|
||||
$services = data_get($compose, 'services');
|
||||
if ($services) {
|
||||
$volumes = collect($services)->map(function ($service) {
|
||||
return data_get($service, 'volumes');
|
||||
})->flatten()->filter(function ($volume) {
|
||||
return str($volume)->startsWith('/data/coolify');
|
||||
})->unique()->values();
|
||||
foreach ($volumes as $volume) {
|
||||
$source = Str::of($volume)->before(':');
|
||||
$target = Str::of($volume)->after(':')->beforeLast(':');
|
||||
|
||||
LocalFileVolume::updateOrCreate(
|
||||
[
|
||||
'mount_path' => $target,
|
||||
'resource_id' => $this->application->id,
|
||||
'resource_type' => get_class($this->application)
|
||||
],
|
||||
[
|
||||
'fs_path' => $source,
|
||||
'mount_path' => $target,
|
||||
'resource_id' => $this->application->id,
|
||||
'resource_type' => get_class($this->application)
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
$this->dispatch('success', 'Docker compose file loaded.');
|
||||
$this->dispatch('compose_loaded');
|
||||
$this->dispatch('refresh_storages');
|
||||
$this->dispatch('refreshEnvs');
|
||||
} 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)
|
||||
@@ -177,6 +212,16 @@ class General extends Component
|
||||
$this->loadComposeFile();
|
||||
}
|
||||
}
|
||||
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->resetDefaultLabels();
|
||||
}
|
||||
public function updatedApplicationBuildPack()
|
||||
{
|
||||
if ($this->application->build_pack !== 'nixpacks') {
|
||||
@@ -204,11 +249,13 @@ class General extends Component
|
||||
$fqdn = generateFqdn($server, $this->application->uuid);
|
||||
$this->application->fqdn = $fqdn;
|
||||
$this->application->save();
|
||||
$this->resetDefaultLabels();
|
||||
$this->dispatch('success', 'Wildcard domain generated.');
|
||||
}
|
||||
}
|
||||
public function resetDefaultLabels()
|
||||
{
|
||||
ray('resetDefaultLabels');
|
||||
$this->customLabels = str(implode("|", generateLabelsApplication($this->application)))->replace("|", "\n");
|
||||
$this->ports_exposes = $this->application->ports_exposes;
|
||||
|
||||
@@ -290,11 +337,6 @@ class General extends Component
|
||||
check_domain_usage(resource: $this->application);
|
||||
}
|
||||
}
|
||||
if ($this->application->settings->is_raw_compose_deployment_enabled) {
|
||||
$this->application->parseRawCompose();
|
||||
} else {
|
||||
$this->parsedServices = $this->application->parseCompose();
|
||||
}
|
||||
}
|
||||
$this->application->custom_labels = base64_encode($this->customLabels);
|
||||
$this->application->save();
|
||||
@@ -302,7 +344,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,8 @@ class Heading extends Component
|
||||
}
|
||||
|
||||
if ($showNotification) $this->dispatch('success', "Success", "Application status updated.");
|
||||
// Removed because it caused flickering
|
||||
// $this->dispatch('configurationChanged');
|
||||
}
|
||||
|
||||
public function force_deploy_without_cache()
|
||||
|
||||
@@ -18,7 +18,6 @@ class Rollback extends Component
|
||||
{
|
||||
$this->parameters = get_route_parameters();
|
||||
}
|
||||
|
||||
public function rollbackImage($commit)
|
||||
{
|
||||
$deployment_uuid = new Cuid2(7);
|
||||
@@ -27,6 +26,7 @@ class Rollback extends Component
|
||||
application: $this->application,
|
||||
deployment_uuid: $deployment_uuid,
|
||||
commit: $commit,
|
||||
rollback: true,
|
||||
force_rebuild: false,
|
||||
);
|
||||
return redirect()->route('project.application.deployment.show', [
|
||||
|
||||
@@ -35,11 +35,6 @@ class Execution extends Component
|
||||
$this->executions = $executions;
|
||||
$this->s3s = currentTeam()->s3s;
|
||||
}
|
||||
public function cleanupFailed()
|
||||
{
|
||||
$this->backup->executions()->where('status', 'failed')->delete();
|
||||
$this->dispatch('refreshBackupExecutions');
|
||||
}
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.project.database.backup.execution');
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
namespace App\Livewire\Project\Database;
|
||||
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Livewire\Component;
|
||||
|
||||
class BackupExecutions extends Component
|
||||
@@ -15,11 +14,15 @@ class BackupExecutions extends Component
|
||||
$userId = auth()->user()->id;
|
||||
return [
|
||||
"echo-private:team.{$userId},BackupCreated" => 'refreshBackupExecutions',
|
||||
"refreshBackupExecutions",
|
||||
"deleteBackup"
|
||||
];
|
||||
}
|
||||
|
||||
public function cleanupFailed()
|
||||
{
|
||||
$this->backup->executions()->where('status', 'failed')->delete();
|
||||
$this->refreshBackupExecutions();
|
||||
}
|
||||
public function deleteBackup($exeuctionId)
|
||||
{
|
||||
$execution = $this->backup->executions()->where('id', $exeuctionId)->first();
|
||||
@@ -36,32 +39,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,35 +2,32 @@
|
||||
|
||||
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 = [];
|
||||
public string $postgresqlRestoreCommand = 'pg_restore -U $POSTGRES_USER -d $POSTGRES_DB';
|
||||
public string $mysqlRestoreCommand = 'mysql -u $MYSQL_USER -p$MYSQL_PASSWORD $MYSQL_DATABASE';
|
||||
public string $mariadbRestoreCommand = 'mariadb -u $MARIADB_USER -p$MARIADB_PASSWORD $MARIADB_DATABASE';
|
||||
public string $mongodbRestoreCommand = 'mongorestore --authenticationDatabase=admin --username $MONGO_INITDB_ROOT_USERNAME --password $MONGO_INITDB_ROOT_PASSWORD --uri mongodb://localhost:27017 --gzip --archive=';
|
||||
|
||||
public function getListeners()
|
||||
{
|
||||
@@ -51,22 +48,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 +59,33 @@ 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->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()) {
|
||||
@@ -122,6 +101,10 @@ class Import extends Component
|
||||
$this->importCommands[] = "docker exec {$this->container} sh -c '{$this->postgresqlRestoreCommand} {$tmpPath}'";
|
||||
$this->importCommands[] = "rm {$tmpPath}";
|
||||
break;
|
||||
case 'App\Models\StandaloneMongodb':
|
||||
$this->importCommands[] = "docker exec {$this->container} sh -c '{$this->mongodbRestoreCommand}{$tmpPath}'";
|
||||
$this->importCommands[] = "rm {$tmpPath}";
|
||||
break;
|
||||
}
|
||||
|
||||
$this->importCommands[] = "docker exec {$this->container} sh -c 'rm {$tmpPath}'";
|
||||
@@ -132,8 +115,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');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,27 +12,6 @@ class Edit extends Component
|
||||
'project.name' => 'required|min:3|max:255',
|
||||
'project.description' => 'nullable|string|max:255',
|
||||
];
|
||||
protected $listeners = ['refreshEnvs' => '$refresh', 'saveKey' => 'saveKey'];
|
||||
|
||||
public function saveKey($data)
|
||||
{
|
||||
try {
|
||||
$found = $this->project->environment_variables()->where('key', $data['key'])->first();
|
||||
if ($found) {
|
||||
throw new \Exception('Variable already exists.');
|
||||
}
|
||||
$this->project->environment_variables()->create([
|
||||
'key' => $data['key'],
|
||||
'value' => $data['value'],
|
||||
'is_multiline' => $data['is_multiline'],
|
||||
'type' => 'project',
|
||||
'team_id' => currentTeam()->id,
|
||||
]);
|
||||
$this->project->refresh();
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
public function mount()
|
||||
{
|
||||
$projectUuid = request()->route('project_uuid');
|
||||
|
||||
@@ -16,28 +16,6 @@ class EnvironmentEdit extends Component
|
||||
'environment.name' => 'required|min:3|max:255',
|
||||
'environment.description' => 'nullable|min:3|max:255',
|
||||
];
|
||||
protected $listeners = ['refreshEnvs' => '$refresh', 'saveKey' => 'saveKey'];
|
||||
|
||||
public function saveKey($data)
|
||||
{
|
||||
try {
|
||||
$found = $this->environment->environment_variables()->where('key', $data['key'])->first();
|
||||
if ($found) {
|
||||
throw new \Exception('Variable already exists.');
|
||||
}
|
||||
$this->environment->environment_variables()->create([
|
||||
'key' => $data['key'],
|
||||
'value' => $data['value'],
|
||||
'is_multiline' => $data['is_multiline'],
|
||||
'type' => 'environment',
|
||||
'team_id' => currentTeam()->id,
|
||||
]);
|
||||
$this->environment->refresh();
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
public function mount()
|
||||
{
|
||||
$this->parameters = get_route_parameters();
|
||||
|
||||
@@ -89,9 +89,22 @@ class PublicGitRepository extends Component
|
||||
public function load_branch()
|
||||
{
|
||||
try {
|
||||
$this->validate([
|
||||
'repository_url' => 'required|url'
|
||||
]);
|
||||
if (str($this->repository_url)->startsWith('git@')) {
|
||||
$github_instance = str($this->repository_url)->after('git@')->before(':');
|
||||
$repository = str($this->repository_url)->after(':')->before('.git');
|
||||
$this->repository_url = 'https://' . str($github_instance) . '/' . $repository;
|
||||
}
|
||||
if (
|
||||
(str($this->repository_url)->startsWith('https://') ||
|
||||
str($this->repository_url)->startsWith('http://')) &&
|
||||
!str($this->repository_url)->endsWith('.git') &&
|
||||
!str($this->repository_url)->contains('github.com')
|
||||
) {
|
||||
$this->repository_url = $this->repository_url . '.git';
|
||||
}
|
||||
if (str($this->repository_url)->contains('github.com')) {
|
||||
$this->repository_url = str($this->repository_url)->before('.git')->value();
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
@@ -168,7 +181,6 @@ class PublicGitRepository extends Component
|
||||
'name' => generate_random_name(),
|
||||
'git_repository' => $this->git_repository,
|
||||
'git_branch' => $this->git_branch,
|
||||
'build_pack' => 'nixpacks',
|
||||
'ports_exposes' => $this->port,
|
||||
'publish_directory' => $this->publish_directory,
|
||||
'environment_id' => $environment->id,
|
||||
@@ -181,7 +193,6 @@ class PublicGitRepository extends Component
|
||||
'name' => generate_application_name($this->git_repository, $this->git_branch),
|
||||
'git_repository' => $this->git_repository,
|
||||
'git_branch' => $this->git_branch,
|
||||
'build_pack' => 'nixpacks',
|
||||
'ports_exposes' => $this->port,
|
||||
'publish_directory' => $this->publish_directory,
|
||||
'environment_id' => $environment->id,
|
||||
@@ -193,7 +204,6 @@ class PublicGitRepository extends Component
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
$application = Application::create($application_init);
|
||||
|
||||
$application->settings->is_static = $this->is_static;
|
||||
|
||||
@@ -120,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;
|
||||
|
||||
@@ -70,6 +70,8 @@ CMD ["nginx", "-g", "daemon off;"]
|
||||
'fqdn' => $fqdn
|
||||
]);
|
||||
|
||||
$application->parseHealthcheckFromDockerfile(dockerfile: collect(str($this->dockerfile)->trim()->explode("\n")), isInit: true);
|
||||
|
||||
return redirect()->route('project.application.configuration', [
|
||||
'application_uuid' => $application->uuid,
|
||||
'environment_name' => $environment->name,
|
||||
|
||||
@@ -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,
|
||||
@@ -54,14 +60,19 @@ class Create extends Component
|
||||
}
|
||||
if ($oneClickService) {
|
||||
$destination = StandaloneDocker::whereUuid($destination_uuid)->first();
|
||||
$service = Service::create([
|
||||
$service_payload = [
|
||||
'name' => "$oneClickServiceName-" . str()->random(10),
|
||||
'docker_compose_raw' => base64_decode($oneClickService),
|
||||
'environment_id' => $environment->id,
|
||||
'service_type' => $oneClickServiceName,
|
||||
'server_id' => (int) $server_id,
|
||||
'destination_id' => $destination->id,
|
||||
'destination_type' => $destination->getMorphClass(),
|
||||
]);
|
||||
];
|
||||
if ($oneClickServiceName === 'cloudflared') {
|
||||
data_set($service_payload, 'connect_to_docker_network', true);
|
||||
}
|
||||
$service = Service::create($service_payload);
|
||||
$service->name = "$oneClickServiceName-" . $service->uuid;
|
||||
$service->save();
|
||||
if ($oneClickDotEnvs?->count() > 0) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -13,12 +13,14 @@ class EditCompose extends Component
|
||||
'service.docker_compose_raw' => 'required',
|
||||
'service.docker_compose' => 'required',
|
||||
];
|
||||
public function mount() {
|
||||
public function mount()
|
||||
{
|
||||
$this->service = Service::find($this->serviceId);
|
||||
}
|
||||
|
||||
public function saveEditedCompose() {
|
||||
$this->dispatch('warning', "Saving new docker compose...");
|
||||
public function saveEditedCompose()
|
||||
{
|
||||
$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->application->service->parse();
|
||||
$this->dispatch('refresh');
|
||||
$this->dispatch('configurationChanged');
|
||||
}
|
||||
}
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.project.service.edit-domain');
|
||||
}
|
||||
}
|
||||
@@ -2,16 +2,18 @@
|
||||
|
||||
namespace App\Livewire\Project\Service;
|
||||
|
||||
use App\Models\Application;
|
||||
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|Application $resource;
|
||||
public string $fs_path;
|
||||
public ?string $workdir = null;
|
||||
|
||||
@@ -23,14 +25,51 @@ 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 convertToDirectory() {
|
||||
try {
|
||||
$this->fileStorage->deleteStorageOnServer();
|
||||
$this->fileStorage->is_directory = true;
|
||||
$this->fileStorage->content = null;
|
||||
$this->fileStorage->save();
|
||||
$this->fileStorage->saveStorageOnServer();
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
} finally {
|
||||
$this->dispatch('refresh_storages');
|
||||
}
|
||||
}
|
||||
public function convertToFile() {
|
||||
try {
|
||||
$this->fileStorage->deleteStorageOnServer();
|
||||
$this->fileStorage->is_directory = false;
|
||||
$this->fileStorage->content = null;
|
||||
$this->fileStorage->save();
|
||||
$this->fileStorage->saveStorageOnServer();
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
} finally {
|
||||
$this->dispatch('refresh_storages');
|
||||
}
|
||||
}
|
||||
public function delete() {
|
||||
try {
|
||||
$this->fileStorage->deleteStorageOnServer();
|
||||
$this->fileStorage->delete();
|
||||
$this->dispatch('success', 'File deleted.');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
} finally {
|
||||
$this->dispatch('refresh_storages');
|
||||
}
|
||||
}
|
||||
public function submit()
|
||||
{
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -3,12 +3,13 @@
|
||||
namespace App\Livewire\Project\Service;
|
||||
|
||||
use App\Models\Service;
|
||||
use Illuminate\Support\Collection;
|
||||
use Livewire\Component;
|
||||
|
||||
class StackForm extends Component
|
||||
{
|
||||
public Service $service;
|
||||
public $fields = [];
|
||||
public Collection $fields;
|
||||
protected $listeners = ["saveCompose"];
|
||||
public $rules = [
|
||||
'service.docker_compose_raw' => 'required',
|
||||
@@ -20,6 +21,7 @@ class StackForm extends Component
|
||||
public $validationAttributes = [];
|
||||
public function mount()
|
||||
{
|
||||
$this->fields = collect([]);
|
||||
$extraFields = $this->service->extraFields();
|
||||
foreach ($extraFields as $serviceName => $fields) {
|
||||
foreach ($fields as $fieldKey => $field) {
|
||||
@@ -27,18 +29,20 @@ class StackForm extends Component
|
||||
$value = data_get($field, 'value');
|
||||
$rules = data_get($field, 'rules', 'nullable');
|
||||
$isPassword = data_get($field, 'isPassword');
|
||||
$this->fields[$key] = [
|
||||
$this->fields->put($key, [
|
||||
"serviceName" => $serviceName,
|
||||
"key" => $key,
|
||||
"name" => $fieldKey,
|
||||
"value" => $value,
|
||||
"isPassword" => $isPassword,
|
||||
"rules" => $rules
|
||||
];
|
||||
]);
|
||||
|
||||
$this->rules["fields.$key.value"] = $rules;
|
||||
$this->validationAttributes["fields.$key.value"] = $fieldKey;
|
||||
}
|
||||
}
|
||||
$this->fields = $this->fields->sortDesc();
|
||||
}
|
||||
public function saveCompose($raw)
|
||||
{
|
||||
@@ -69,6 +73,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()
|
||||
|
||||
@@ -7,12 +7,13 @@ use Livewire\Component;
|
||||
|
||||
class Storage extends Component
|
||||
{
|
||||
protected $listeners = ['addNewVolume'];
|
||||
public $resource;
|
||||
|
||||
public function render()
|
||||
public function getListeners()
|
||||
{
|
||||
return view('livewire.project.service.storage');
|
||||
return [
|
||||
'addNewVolume',
|
||||
'refresh_storages' => '$refresh',
|
||||
];
|
||||
}
|
||||
public function addNewVolume($data)
|
||||
{
|
||||
@@ -27,9 +28,13 @@ class Storage extends Component
|
||||
$this->resource->refresh();
|
||||
$this->dispatch('success', 'Storage added successfully');
|
||||
$this->dispatch('clearAddStorage');
|
||||
$this->dispatch('refreshStorages');
|
||||
$this->dispatch('refresh_storages');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.project.service.storage');
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
@@ -7,11 +7,13 @@ use Livewire\Component;
|
||||
class Add extends Component
|
||||
{
|
||||
public $parameters;
|
||||
public bool $shared = false;
|
||||
public bool $is_preview = false;
|
||||
public string $key;
|
||||
public ?string $value = null;
|
||||
public bool $is_build_time = false;
|
||||
public bool $is_multiline = false;
|
||||
public bool $is_literal = false;
|
||||
|
||||
protected $listeners = ['clearAddEnv' => 'clear'];
|
||||
protected $rules = [
|
||||
@@ -19,12 +21,14 @@ class Add extends Component
|
||||
'value' => 'nullable',
|
||||
'is_build_time' => 'required|boolean',
|
||||
'is_multiline' => 'required|boolean',
|
||||
'is_literal' => 'required|boolean',
|
||||
];
|
||||
protected $validationAttributes = [
|
||||
'key' => 'key',
|
||||
'value' => 'value',
|
||||
'is_build_time' => 'build',
|
||||
'is_multiline' => 'multiline',
|
||||
'is_literal' => 'literal',
|
||||
];
|
||||
|
||||
public function mount()
|
||||
@@ -47,6 +51,7 @@ class Add extends Component
|
||||
'value' => $this->value,
|
||||
'is_build_time' => $this->is_build_time,
|
||||
'is_multiline' => $this->is_multiline,
|
||||
'is_literal' => $this->is_literal,
|
||||
'is_preview' => $this->is_preview,
|
||||
]);
|
||||
$this->clear();
|
||||
@@ -58,5 +63,6 @@ class Add extends Component
|
||||
$this->value = '';
|
||||
$this->is_build_time = false;
|
||||
$this->is_multiline = false;
|
||||
$this->is_literal = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,8 +15,10 @@ class All extends Component
|
||||
public ?string $variables = null;
|
||||
public ?string $variablesPreview = null;
|
||||
public string $view = 'normal';
|
||||
protected $listeners = ['refreshEnvs', 'saveKey' => 'submit'];
|
||||
|
||||
protected $listeners = [
|
||||
'refreshEnvs',
|
||||
'saveKey' => 'submit',
|
||||
];
|
||||
public function mount()
|
||||
{
|
||||
$resourceClass = get_class($this->resource);
|
||||
@@ -119,6 +121,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;
|
||||
@@ -152,6 +163,7 @@ class All extends Component
|
||||
$environment->value = $data['value'];
|
||||
$environment->is_build_time = $data['is_build_time'];
|
||||
$environment->is_multiline = $data['is_multiline'];
|
||||
$environment->is_literal = $data['is_literal'];
|
||||
$environment->is_preview = $data['is_preview'];
|
||||
|
||||
switch ($this->resource->type()) {
|
||||
@@ -173,6 +185,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;
|
||||
|
||||
@@ -16,12 +16,16 @@ class Show extends Component
|
||||
public bool $isLocked = false;
|
||||
public bool $isSharedVariable = false;
|
||||
public string $type;
|
||||
protected $listeners = [
|
||||
"compose_loaded" => '$refresh',
|
||||
];
|
||||
|
||||
protected $rules = [
|
||||
'env.key' => 'required|string',
|
||||
'env.value' => 'nullable',
|
||||
'env.is_build_time' => 'required|boolean',
|
||||
'env.is_multiline' => 'required|boolean',
|
||||
'env.is_literal' => 'required|boolean',
|
||||
'env.is_shown_once' => 'required|boolean',
|
||||
'env.real_value' => 'nullable',
|
||||
];
|
||||
@@ -30,6 +34,7 @@ class Show extends Component
|
||||
'env.value' => 'Value',
|
||||
'env.is_build_time' => 'Build Time',
|
||||
'env.is_multiline' => 'Multiline',
|
||||
'env.is_literal' => 'Literal',
|
||||
'env.is_shown_once' => 'Shown Once',
|
||||
];
|
||||
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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;
|
||||
@@ -88,15 +91,35 @@ class GetLogs extends Component
|
||||
if ($this->container) {
|
||||
if ($this->showTimeStamps) {
|
||||
if ($this->server->isSwarm()) {
|
||||
$sshCommand = generateSshCommand($this->server, "docker service logs -n {$this->numberOfLines} -t {$this->container}");
|
||||
$command = "docker service logs -n {$this->numberOfLines} -t {$this->container}";
|
||||
if ($this->server->isNonRoot()) {
|
||||
$command = parseCommandsByLineForSudo(collect($command), $this->server);
|
||||
$command = $command[0];
|
||||
}
|
||||
$sshCommand = generateSshCommand($this->server, $command);
|
||||
} else {
|
||||
$sshCommand = generateSshCommand($this->server, "docker logs -n {$this->numberOfLines} -t {$this->container}");
|
||||
$command = "docker logs -n {$this->numberOfLines} -t {$this->container}";
|
||||
if ($this->server->isNonRoot()) {
|
||||
$command = parseCommandsByLineForSudo(collect($command), $this->server);
|
||||
$command = $command[0];
|
||||
}
|
||||
$sshCommand = generateSshCommand($this->server, $command);
|
||||
}
|
||||
} else {
|
||||
if ($this->server->isSwarm()) {
|
||||
$sshCommand = generateSshCommand($this->server, "docker service logs -n {$this->numberOfLines} {$this->container}");
|
||||
$command = "docker service logs -n {$this->numberOfLines} {$this->container}";
|
||||
if ($this->server->isNonRoot()) {
|
||||
$command = parseCommandsByLineForSudo(collect($command), $this->server);
|
||||
$command = $command[0];
|
||||
}
|
||||
$sshCommand = generateSshCommand($this->server, $command);
|
||||
} else {
|
||||
$sshCommand = generateSshCommand($this->server, "docker logs -n {$this->numberOfLines} {$this->container}");
|
||||
$command = "docker logs -n {$this->numberOfLines} {$this->container}";
|
||||
if ($this->server->isNonRoot()) {
|
||||
$command = parseCommandsByLineForSudo(collect($command), $this->server);
|
||||
$command = $command[0];
|
||||
}
|
||||
$sshCommand = generateSshCommand($this->server, $command);
|
||||
}
|
||||
}
|
||||
if ($refresh) {
|
||||
|
||||
@@ -17,18 +17,17 @@ class HealthChecks extends Component
|
||||
'resource.health_check_return_code' => 'integer',
|
||||
'resource.health_check_scheme' => 'string',
|
||||
'resource.health_check_response_text' => 'nullable|string',
|
||||
'resource.health_check_interval' => 'integer',
|
||||
'resource.health_check_timeout' => 'integer',
|
||||
'resource.health_check_retries' => 'integer',
|
||||
'resource.health_check_interval' => 'integer|min:1',
|
||||
'resource.health_check_timeout' => 'integer|min:1',
|
||||
'resource.health_check_retries' => 'integer|min:1',
|
||||
'resource.health_check_start_period' => 'integer',
|
||||
'resource.custom_healthcheck_found' => 'boolean',
|
||||
|
||||
];
|
||||
public function instantSave()
|
||||
{
|
||||
$this->resource->save();
|
||||
$this->dispatch('success', 'Health check updated.');
|
||||
|
||||
|
||||
}
|
||||
public function submit()
|
||||
{
|
||||
|
||||
@@ -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 = [];
|
||||
@@ -41,7 +44,7 @@ class Logs extends Component
|
||||
} else {
|
||||
$containers = getCurrentApplicationContainerStatus($server, $this->resource->id, includePullrequests: true);
|
||||
}
|
||||
$server->containers = $containers;
|
||||
$server->containers = $containers->sort();
|
||||
} catch (\Exception $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
@@ -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;
|
||||
@@ -103,6 +94,7 @@ class Logs extends Component
|
||||
$this->servers = $this->servers->push($this->resource->server);
|
||||
}
|
||||
}
|
||||
$this->containers = $this->containers->sort();
|
||||
} catch (\Exception $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
|
||||
@@ -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,16 +2,10 @@
|
||||
|
||||
namespace App\Livewire\Project\Shared\Storages;
|
||||
|
||||
use App\Models\LocalPersistentVolume;
|
||||
use Livewire\Component;
|
||||
|
||||
class All extends Component
|
||||
{
|
||||
public $resource;
|
||||
protected $listeners = ['refreshStorages'];
|
||||
|
||||
public function refreshStorages()
|
||||
{
|
||||
$this->resource->refresh();
|
||||
}
|
||||
protected $listeners = ['refresh_storages' => '$refresh'];
|
||||
}
|
||||
|
||||
@@ -39,6 +39,6 @@ class Show extends Component
|
||||
public function delete()
|
||||
{
|
||||
$this->storage->delete();
|
||||
$this->dispatch('refreshStorages');
|
||||
$this->dispatch('refresh_storages');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,14 +75,16 @@ class Form extends Component
|
||||
}
|
||||
public function checkLocalhostConnection()
|
||||
{
|
||||
$uptime = $this->server->validateConnection();
|
||||
$this->submit();
|
||||
['uptime' => $uptime, 'error' => $error] = $this->server->validateConnection();
|
||||
if ($uptime) {
|
||||
$this->dispatch('success', 'Server is reachable.');
|
||||
$this->server->settings->is_reachable = true;
|
||||
$this->server->settings->is_usable = true;
|
||||
$this->server->settings->save();
|
||||
$this->dispatch('proxyStatusUpdated');
|
||||
} 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/knowledge-base/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. <br><br>Error: ' . $error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,8 @@ class Deploy extends Component
|
||||
'proxyStatusUpdated',
|
||||
'traefikDashboardAvailable',
|
||||
'serverRefresh' => 'proxyStatusUpdated',
|
||||
"checkProxy", "startProxy"
|
||||
"checkProxy",
|
||||
"startProxy"
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@ class NewDynamicConfiguration extends Component
|
||||
}
|
||||
$base64_value = base64_encode($this->value);
|
||||
instant_remote_process([
|
||||
"echo '{$base64_value}' | base64 -d > {$file}",
|
||||
"echo '{$base64_value}' | base64 -d | tee {$file} > /dev/null",
|
||||
], $this->server);
|
||||
if ($proxy_type === 'CADDY') {
|
||||
$this->server->reloadCaddy();
|
||||
|
||||
@@ -35,10 +35,11 @@ class ShowPrivateKey extends Component
|
||||
public function checkConnection()
|
||||
{
|
||||
try {
|
||||
$uptime = $this->server->validateConnection();
|
||||
['uptime' => $uptime, 'error' => $error] = $this->server->validateConnection();
|
||||
if ($uptime) {
|
||||
$this->dispatch('success', 'Server is reachable.');
|
||||
} else {
|
||||
ray($error);
|
||||
$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;
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ class ValidateAndInstall extends Component
|
||||
{
|
||||
public Server $server;
|
||||
public int $number_of_tries = 0;
|
||||
public int $max_tries = 1;
|
||||
public int $max_tries = 3;
|
||||
public bool $install = true;
|
||||
public $uptime = null;
|
||||
public $supported_os_type = null;
|
||||
@@ -32,9 +32,8 @@ class ValidateAndInstall extends Component
|
||||
'refresh' => '$refresh',
|
||||
];
|
||||
|
||||
public function init(bool $install = true)
|
||||
public function init(int $data = 0)
|
||||
{
|
||||
$this->install = $install;
|
||||
$this->uptime = null;
|
||||
$this->supported_os_type = null;
|
||||
$this->docker_installed = null;
|
||||
@@ -42,7 +41,7 @@ class ValidateAndInstall extends Component
|
||||
$this->docker_compose_installed = null;
|
||||
$this->proxy_started = null;
|
||||
$this->error = null;
|
||||
$this->number_of_tries = 0;
|
||||
$this->number_of_tries = $data;
|
||||
if (!$this->ask) {
|
||||
$this->dispatch('validateConnection');
|
||||
}
|
||||
@@ -66,16 +65,15 @@ class ValidateAndInstall extends Component
|
||||
} else {
|
||||
$this->proxy_started = true;
|
||||
}
|
||||
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
public function validateConnection()
|
||||
{
|
||||
$this->uptime = $this->server->validateConnection();
|
||||
['uptime' => $this->uptime, 'error' => $error] = $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/knowledge-base/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. <br><br>Error: ' . $error;
|
||||
return;
|
||||
}
|
||||
$this->dispatch('validateOS');
|
||||
@@ -99,10 +97,10 @@ class ValidateAndInstall extends Component
|
||||
$this->error = 'Docker Engine could not be installed. Please install Docker manually before continuing: <a target="_blank" class="underline" href="https://docs.docker.com/engine/install/#server">documentation</a>.';
|
||||
return;
|
||||
} else {
|
||||
if ($this->number_of_tries == 0) {
|
||||
if ($this->number_of_tries <= $this->max_tries) {
|
||||
$activity = $this->server->installDocker();
|
||||
$this->number_of_tries++;
|
||||
$this->dispatch('newActivityMonitor', $activity->id, 'init');
|
||||
$this->dispatch('newActivityMonitor', $activity->id, 'init', $this->number_of_tries);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -69,13 +69,14 @@ class Configuration extends Component
|
||||
}
|
||||
$this->validate();
|
||||
|
||||
if ($this->settings->is_dns_validation_enabled) {
|
||||
if ($this->settings->is_dns_validation_enabled && $this->settings->fqdn) {
|
||||
ray('asdf');
|
||||
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);
|
||||
if ($this->settings->fqdn) 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();
|
||||
|
||||
19
app/Livewire/SharedVariables/Environment/Index.php
Normal file
19
app/Livewire/SharedVariables/Environment/Index.php
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire\SharedVariables\Environment;
|
||||
|
||||
use App\Models\Project;
|
||||
use Illuminate\Support\Collection;
|
||||
use Livewire\Component;
|
||||
|
||||
class Index extends Component
|
||||
{
|
||||
public Collection $projects;
|
||||
public function mount() {
|
||||
$this->projects = Project::ownedByCurrentTeam()->get();
|
||||
}
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.shared-variables.environment.index');
|
||||
}
|
||||
}
|
||||
47
app/Livewire/SharedVariables/Environment/Show.php
Normal file
47
app/Livewire/SharedVariables/Environment/Show.php
Normal file
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire\SharedVariables\Environment;
|
||||
|
||||
use App\Models\Application;
|
||||
use App\Models\Project;
|
||||
use Livewire\Component;
|
||||
|
||||
class Show extends Component
|
||||
{
|
||||
public Project $project;
|
||||
public Application $application;
|
||||
public $environment;
|
||||
public array $parameters;
|
||||
protected $listeners = ['refreshEnvs' => '$refresh', 'saveKey' => 'saveKey'];
|
||||
|
||||
public function saveKey($data)
|
||||
{
|
||||
try {
|
||||
$found = $this->environment->environment_variables()->where('key', $data['key'])->first();
|
||||
if ($found) {
|
||||
throw new \Exception('Variable already exists.');
|
||||
}
|
||||
$this->environment->environment_variables()->create([
|
||||
'key' => $data['key'],
|
||||
'value' => $data['value'],
|
||||
'is_multiline' => $data['is_multiline'],
|
||||
'is_literal' => $data['is_literal'],
|
||||
'type' => 'environment',
|
||||
'team_id' => currentTeam()->id,
|
||||
]);
|
||||
$this->environment->refresh();
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
public function mount()
|
||||
{
|
||||
$this->parameters = get_route_parameters();
|
||||
$this->project = Project::ownedByCurrentTeam()->where('uuid', request()->route('project_uuid'))->first();
|
||||
$this->environment = $this->project->environments()->where('name', request()->route('environment_name'))->first();
|
||||
}
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.shared-variables.environment.show');
|
||||
}
|
||||
}
|
||||
13
app/Livewire/SharedVariables/Index.php
Normal file
13
app/Livewire/SharedVariables/Index.php
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire\SharedVariables;
|
||||
|
||||
use Livewire\Component;
|
||||
|
||||
class Index extends Component
|
||||
{
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.shared-variables.index');
|
||||
}
|
||||
}
|
||||
19
app/Livewire/SharedVariables/Project/Index.php
Normal file
19
app/Livewire/SharedVariables/Project/Index.php
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire\SharedVariables\Project;
|
||||
|
||||
use App\Models\Project;
|
||||
use Illuminate\Support\Collection;
|
||||
use Livewire\Component;
|
||||
|
||||
class Index extends Component
|
||||
{
|
||||
public Collection $projects;
|
||||
public function mount() {
|
||||
$this->projects = Project::ownedByCurrentTeam()->get();
|
||||
}
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.shared-variables.project.index');
|
||||
}
|
||||
}
|
||||
47
app/Livewire/SharedVariables/Project/Show.php
Normal file
47
app/Livewire/SharedVariables/Project/Show.php
Normal file
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire\SharedVariables\Project;
|
||||
|
||||
use App\Models\Project;
|
||||
use Livewire\Component;
|
||||
|
||||
class Show extends Component
|
||||
{
|
||||
public Project $project;
|
||||
protected $listeners = ['refreshEnvs' => '$refresh', 'saveKey' => 'saveKey'];
|
||||
|
||||
public function saveKey($data)
|
||||
{
|
||||
try {
|
||||
$found = $this->project->environment_variables()->where('key', $data['key'])->first();
|
||||
if ($found) {
|
||||
throw new \Exception('Variable already exists.');
|
||||
}
|
||||
$this->project->environment_variables()->create([
|
||||
'key' => $data['key'],
|
||||
'value' => $data['value'],
|
||||
'is_multiline' => $data['is_multiline'],
|
||||
'is_literal' => $data['is_literal'],
|
||||
'type' => 'project',
|
||||
'team_id' => currentTeam()->id,
|
||||
]);
|
||||
$this->project->refresh();
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
public function mount()
|
||||
{
|
||||
$projectUuid = request()->route('project_uuid');
|
||||
$teamId = currentTeam()->id;
|
||||
$project = Project::where('team_id', $teamId)->where('uuid', $projectUuid)->first();
|
||||
if (!$project) {
|
||||
return redirect()->route('dashboard');
|
||||
}
|
||||
$this->project = $project;
|
||||
}
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.shared-variables.project.show');
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire;
|
||||
namespace App\Livewire\SharedVariables\Team;
|
||||
|
||||
use App\Models\Team;
|
||||
use Livewire\Component;
|
||||
|
||||
class TeamSharedVariablesIndex extends Component
|
||||
class Index extends Component
|
||||
{
|
||||
public Team $team;
|
||||
protected $listeners = ['refreshEnvs' => '$refresh', 'saveKey' => 'saveKey'];
|
||||
@@ -21,6 +21,7 @@ class TeamSharedVariablesIndex extends Component
|
||||
'key' => $data['key'],
|
||||
'value' => $data['value'],
|
||||
'is_multiline' => $data['is_multiline'],
|
||||
'is_literal' => $data['is_literal'],
|
||||
'type' => 'team',
|
||||
'team_id' => currentTeam()->id,
|
||||
]);
|
||||
@@ -36,6 +37,6 @@ class TeamSharedVariablesIndex extends Component
|
||||
}
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.team-shared-variables-index');
|
||||
return view('livewire.shared-variables.team.index');
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire\Team\Storage;
|
||||
namespace App\Livewire\Storage;
|
||||
|
||||
use App\Models\S3Storage;
|
||||
use Livewire\Component;
|
||||
@@ -16,13 +16,13 @@ class Create extends Component
|
||||
public string $endpoint;
|
||||
public S3Storage $storage;
|
||||
protected $rules = [
|
||||
'name' => 'nullable|min:3|max:255',
|
||||
'name' => 'required|min:3|max:255',
|
||||
'description' => 'nullable|min:3|max:255',
|
||||
'region' => 'required|max:255',
|
||||
'key' => 'required|max:255',
|
||||
'secret' => 'required|max:255',
|
||||
'bucket' => 'required|max:255',
|
||||
'endpoint' => 'nullable|url|max:255',
|
||||
'endpoint' => 'required|url|max:255',
|
||||
];
|
||||
protected $validationAttributes = [
|
||||
'name' => 'Name',
|
||||
@@ -65,7 +65,7 @@ class Create extends Component
|
||||
$this->storage->team_id = currentTeam()->id;
|
||||
$this->storage->testConnection();
|
||||
$this->storage->save();
|
||||
return redirect()->route('team.storage.show', $this->storage->uuid);
|
||||
return redirect()->route('storage.show', $this->storage->uuid);
|
||||
} catch (\Throwable $e) {
|
||||
$this->dispatch('error', 'Failed to create storage.', $e->getMessage());
|
||||
// return handleError($e, $this);
|
||||
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire\Team\Storage;
|
||||
namespace App\Livewire\Storage;
|
||||
|
||||
use App\Models\S3Storage;
|
||||
use Livewire\Component;
|
||||
@@ -43,7 +43,7 @@ class Form extends Component
|
||||
{
|
||||
try {
|
||||
$this->storage->delete();
|
||||
return redirect()->route('team.storage.index');
|
||||
return redirect()->route('storage.index');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire\Team\Storage;
|
||||
namespace App\Livewire\Storage;
|
||||
|
||||
use App\Models\S3Storage;
|
||||
use Livewire\Component;
|
||||
@@ -13,6 +13,6 @@ class Index extends Component
|
||||
}
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.team.storage.index');
|
||||
return view('livewire.storage.index');
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user