mirror of
https://github.com/ershisan99/coolify.git
synced 2026-01-03 12:34:08 +00:00
Compare commits
37 Commits
v4.0.0-bet
...
v4.0.0-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9f3dbc3cbb | ||
|
|
8a9ee84925 | ||
|
|
689480003a | ||
|
|
3b20eee909 | ||
|
|
e8cadc176b | ||
|
|
b0c96e64c9 | ||
|
|
9ce3b43e09 | ||
|
|
4c2b3df861 | ||
|
|
467471f54a | ||
|
|
60171093c5 | ||
|
|
38f2a2dac7 | ||
|
|
307ee52ac0 | ||
|
|
b66c9835b7 | ||
|
|
d38d50dca2 | ||
|
|
40023be4ea | ||
|
|
48d7c6e76f | ||
|
|
debacfe2f7 | ||
|
|
d430813230 | ||
|
|
e30c37b041 | ||
|
|
8c73068cc7 | ||
|
|
2c4e69ad50 | ||
|
|
5ae08d009e | ||
|
|
673b944647 | ||
|
|
16281248ac | ||
|
|
8670b41671 | ||
|
|
9c69044da5 | ||
|
|
ebc4ab9af5 | ||
|
|
57738198ad | ||
|
|
b8252b85b0 | ||
|
|
479c2743bd | ||
|
|
81e6482d7a | ||
|
|
88c5d87084 | ||
|
|
6c7e091e1b | ||
|
|
91e3d33c0b | ||
|
|
aa00389824 | ||
|
|
b4e54ab3e3 | ||
|
|
8f3c5d4bd3 |
35
README.md
35
README.md
@@ -10,35 +10,40 @@ No vendor lock-in, which means that all the configuration for your applications/
|
|||||||
|
|
||||||
For more information, take a look at our landing page [here](https://coolify.io).
|
For more information, take a look at our landing page [here](https://coolify.io).
|
||||||
|
|
||||||
> If you are looking for previous (v3) version, it is [here](https://github.com/coollabsio/coolify/tree/v3).
|
# Donations
|
||||||
|
To stay completely free, open-source, no feature behind paywall and evolve the project, we need your help. If you like Coolify, please consider donating to help us fund the future development of the project.
|
||||||
|
|
||||||
|
https://coolify.io/sponsorships
|
||||||
|
|
||||||
|
Thank you so much!
|
||||||
|
|
||||||
# Cloud
|
# Cloud
|
||||||
|
|
||||||
If you do not want to self-host Coolify, there is a paid cloud version available: https://app.coolify.io
|
If you do not want to self-host Coolify, there is a paid cloud version available: https://app.coolify.io
|
||||||
|
|
||||||
You can easily attach your own servers, get all the automations, free email notifications, etc.
|
|
||||||
|
|
||||||
For more information & pricing, take a look at our landing page [here](https://coolify.io).
|
For more information & pricing, take a look at our landing page [here](https://coolify.io).
|
||||||
|
|
||||||
# Beta
|
## Why should I use the Cloud version?
|
||||||
|
The recommended way to use Coolify is to have one server for Coolify and one (or more) for the resources you are deploying. A server is around 4-5$/month.
|
||||||
|
|
||||||
The latest version (v4) is still in beta. That does not mean it is unstable. All the features that are available are stable enough be usable in real-life.
|
By subscribing to the cloud version, you get the Coolify server for the same price, but with:
|
||||||
|
- High-availability
|
||||||
There are hundreds of people using it for managing their client's applications, freelancers, hobbyists, businesses.
|
- Free email notifications
|
||||||
|
- Better support
|
||||||
|
- Less maintenance for you
|
||||||
|
|
||||||
# Installation
|
# Installation
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl -fsSL https://cdn.coollabs.io/coolify/install.sh | bash
|
curl -fsSL https://cdn.coollabs.io/coolify/install.sh | bash
|
||||||
```
|
```
|
||||||
|
You can find the installation script source [here](./scripts/install.sh).
|
||||||
|
|
||||||
You can find the installation script [here](./scripts/install.sh).
|
# Support
|
||||||
|
|
||||||
## Support
|
|
||||||
|
|
||||||
Contact us [here](https://coolify.io/docs/contact).
|
Contact us [here](https://coolify.io/docs/contact).
|
||||||
|
|
||||||
## Recognitions
|
# Recognitions
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
<a href="https://news.ycombinator.com/item?id=26624341">
|
<a href="https://news.ycombinator.com/item?id=26624341">
|
||||||
@@ -54,11 +59,11 @@ Contact us [here](https://coolify.io/docs/contact).
|
|||||||
|
|
||||||
<a href="https://trendshift.io/repositories/634" target="_blank"><img src="https://trendshift.io/api/badge/repositories/634" alt="coollabsio%2Fcoolify | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
|
<a href="https://trendshift.io/repositories/634" target="_blank"><img src="https://trendshift.io/api/badge/repositories/634" alt="coollabsio%2Fcoolify | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
|
||||||
|
|
||||||
## 💰 Financial Contributors
|
# 💰 Financial Contributors
|
||||||
|
|
||||||
Become a financial contributor and help us sustain our community. [[Contribute](https://opencollective.com/coollabsio/contribute)]
|
Become a financial contributor and help us sustain our community. [[Contribute](https://opencollective.com/coollabsio/contribute)]
|
||||||
|
|
||||||
### Organizations
|
## Organizations
|
||||||
|
|
||||||
Special thanks to our biggest sponsors, [CCCareers](https://cccareers.org/) and [Appwrite](https://appwrite.io)!
|
Special thanks to our biggest sponsors, [CCCareers](https://cccareers.org/) and [Appwrite](https://appwrite.io)!
|
||||||
|
|
||||||
@@ -78,10 +83,10 @@ Support this project with your organization. Your logo will show up here with a
|
|||||||
<a href="https://opencollective.com/coollabsio/organization/8/website"><img src="https://opencollective.com/coollabsio/organization/8/avatar.svg"></a>
|
<a href="https://opencollective.com/coollabsio/organization/8/website"><img src="https://opencollective.com/coollabsio/organization/8/avatar.svg"></a>
|
||||||
<a href="https://opencollective.com/coollabsio/organization/9/website"><img src="https://opencollective.com/coollabsio/organization/9/avatar.svg"></a>
|
<a href="https://opencollective.com/coollabsio/organization/9/website"><img src="https://opencollective.com/coollabsio/organization/9/avatar.svg"></a>
|
||||||
|
|
||||||
### Individuals
|
## Individuals
|
||||||
|
|
||||||
<a href="https://opencollective.com/coollabsio"><img src="https://opencollective.com/coollabsio/individuals.svg?width=890"></a>
|
<a href="https://opencollective.com/coollabsio"><img src="https://opencollective.com/coollabsio/individuals.svg?width=890"></a>
|
||||||
|
|
||||||
## Star History
|
# Star History
|
||||||
|
|
||||||
[](https://star-history.com/#coollabsio/coolify&Date)
|
[](https://star-history.com/#coollabsio/coolify&Date)
|
||||||
|
|||||||
@@ -69,6 +69,16 @@ class StartMariadb
|
|||||||
]
|
]
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
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) {
|
if (count($this->database->ports_mappings_array) > 0) {
|
||||||
$docker_compose['services'][$container_name]['ports'] = $this->database->ports_mappings_array;
|
$docker_compose['services'][$container_name]['ports'] = $this->database->ports_mappings_array;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -76,6 +76,16 @@ class StartMongodb
|
|||||||
]
|
]
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
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) {
|
if (count($this->database->ports_mappings_array) > 0) {
|
||||||
$docker_compose['services'][$container_name]['ports'] = $this->database->ports_mappings_array;
|
$docker_compose['services'][$container_name]['ports'] = $this->database->ports_mappings_array;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,6 +69,16 @@ class StartMysql
|
|||||||
]
|
]
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
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) {
|
if (count($this->database->ports_mappings_array) > 0) {
|
||||||
$docker_compose['services'][$container_name]['ports'] = $this->database->ports_mappings_array;
|
$docker_compose['services'][$container_name]['ports'] = $this->database->ports_mappings_array;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -79,6 +79,17 @@ class StartPostgresql
|
|||||||
]
|
]
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
if ($this->database->destination->server->isLogDrainEnabled() && $this->database->isLogDrainEnabled()) {
|
||||||
|
ray('Log Drain Enabled');
|
||||||
|
$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) {
|
if (count($this->database->ports_mappings_array) > 0) {
|
||||||
$docker_compose['services'][$container_name]['ports'] = $this->database->ports_mappings_array;
|
$docker_compose['services'][$container_name]['ports'] = $this->database->ports_mappings_array;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -78,6 +78,16 @@ class StartRedis
|
|||||||
]
|
]
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
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) {
|
if (count($this->database->ports_mappings_array) > 0) {
|
||||||
$docker_compose['services'][$container_name]['ports'] = $this->database->ports_mappings_array;
|
$docker_compose['services'][$container_name]['ports'] = $this->database->ports_mappings_array;
|
||||||
}
|
}
|
||||||
@@ -156,6 +166,5 @@ class StartRedis
|
|||||||
$content = $this->database->redis_conf;
|
$content = $this->database->redis_conf;
|
||||||
$content_base64 = base64_encode($content);
|
$content_base64 = base64_encode($content);
|
||||||
$this->commands[] = "echo '{$content_base64}' | base64 -d > $this->configuration_dir/{$filename}";
|
$this->commands[] = "echo '{$content_base64}' | base64 -d > $this->configuration_dir/{$filename}";
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
195
app/Actions/Server/InstallLogDrain.php
Normal file
195
app/Actions/Server/InstallLogDrain.php
Normal file
@@ -0,0 +1,195 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Actions\Server;
|
||||||
|
|
||||||
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
|
use App\Models\Server;
|
||||||
|
|
||||||
|
class InstallLogDrain
|
||||||
|
{
|
||||||
|
use AsAction;
|
||||||
|
public function handle(Server $server)
|
||||||
|
{
|
||||||
|
if ($server->settings->is_logdrain_newrelic_enabled) {
|
||||||
|
$type = 'newrelic';
|
||||||
|
} else if ($server->settings->is_logdrain_highlight_enabled) {
|
||||||
|
$type = 'highlight';
|
||||||
|
} else if ($server->settings->is_logdrain_axiom_enabled) {
|
||||||
|
$type = 'axiom';
|
||||||
|
} else {
|
||||||
|
$type = 'none';
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
if ($type === 'none') {
|
||||||
|
$command = [
|
||||||
|
"echo 'Stopping old Fluent Bit'",
|
||||||
|
"docker rm -f coolify-log-drain || true",
|
||||||
|
];
|
||||||
|
return instant_remote_process($command, $server);
|
||||||
|
} else if ($type === 'newrelic') {
|
||||||
|
if (!$server->settings->is_logdrain_newrelic_enabled) {
|
||||||
|
throw new \Exception('New Relic log drain is not enabled.');
|
||||||
|
}
|
||||||
|
$config = base64_encode("
|
||||||
|
[SERVICE]
|
||||||
|
Flush 5
|
||||||
|
Daemon off
|
||||||
|
Tag container_logs
|
||||||
|
Log_Level debug
|
||||||
|
Parsers_File parsers.conf
|
||||||
|
[INPUT]
|
||||||
|
Name forward
|
||||||
|
Buffer_Chunk_Size 1M
|
||||||
|
Buffer_Max_Size 6M
|
||||||
|
[FILTER]
|
||||||
|
Name grep
|
||||||
|
Match *
|
||||||
|
Exclude log 127.0.0.1
|
||||||
|
[FILTER]
|
||||||
|
Name modify
|
||||||
|
Match *
|
||||||
|
Set server_name {$server->name}
|
||||||
|
[OUTPUT]
|
||||||
|
Name nrlogs
|
||||||
|
Match *
|
||||||
|
license_key \${LICENSE_KEY}
|
||||||
|
# https://log-api.eu.newrelic.com/log/v1 - EU
|
||||||
|
# https://log-api.newrelic.com/log/v1 - US
|
||||||
|
base_uri \${BASE_URI}
|
||||||
|
");
|
||||||
|
} else if ($type === 'highlight') {
|
||||||
|
if (!$server->settings->is_logdrain_highlight_enabled) {
|
||||||
|
throw new \Exception('Highlight log drain is not enabled.');
|
||||||
|
}
|
||||||
|
$config = base64_encode("
|
||||||
|
[SERVICE]
|
||||||
|
Flush 5
|
||||||
|
Daemon off
|
||||||
|
Log_Level debug
|
||||||
|
Parsers_File parsers.conf
|
||||||
|
[INPUT]
|
||||||
|
Name forward
|
||||||
|
tag \${HIGHLIGHT_PROJECT_ID}
|
||||||
|
Buffer_Chunk_Size 1M
|
||||||
|
Buffer_Max_Size 6M
|
||||||
|
[OUTPUT]
|
||||||
|
Name forward
|
||||||
|
Match *
|
||||||
|
Host otel.highlight.io
|
||||||
|
Port 24224
|
||||||
|
");
|
||||||
|
} else if ($type === 'axiom') {
|
||||||
|
if (!$server->settings->is_logdrain_axiom_enabled) {
|
||||||
|
throw new \Exception('Axiom log drain is not enabled.');
|
||||||
|
}
|
||||||
|
$config = base64_encode("
|
||||||
|
[SERVICE]
|
||||||
|
Flush 5
|
||||||
|
Daemon off
|
||||||
|
Log_Level debug
|
||||||
|
Parsers_File parsers.conf
|
||||||
|
[INPUT]
|
||||||
|
Name forward
|
||||||
|
Buffer_Chunk_Size 1M
|
||||||
|
Buffer_Max_Size 6M
|
||||||
|
[FILTER]
|
||||||
|
Name grep
|
||||||
|
Match *
|
||||||
|
Exclude log 127.0.0.1
|
||||||
|
[FILTER]
|
||||||
|
Name modify
|
||||||
|
Match *
|
||||||
|
Set server_name {$server->name}
|
||||||
|
[OUTPUT]
|
||||||
|
Name http
|
||||||
|
Match *
|
||||||
|
Host api.axiom.co
|
||||||
|
Port 443
|
||||||
|
URI /v1/datasets/\${AXIOM_DATASET_NAME}/ingest
|
||||||
|
# Authorization Bearer should be an API token
|
||||||
|
Header Authorization Bearer \${AXIOM_API_KEY}
|
||||||
|
compress gzip
|
||||||
|
format json
|
||||||
|
json_date_key _time
|
||||||
|
json_date_format iso8601
|
||||||
|
tls On
|
||||||
|
");
|
||||||
|
} else {
|
||||||
|
throw new \Exception('Unknown log drain type.');
|
||||||
|
}
|
||||||
|
$parsers = base64_encode("
|
||||||
|
[PARSER]
|
||||||
|
Name empty_line_skipper
|
||||||
|
Format regex
|
||||||
|
Regex /^(?!\s*$).+/
|
||||||
|
");
|
||||||
|
$compose = base64_encode("
|
||||||
|
services:
|
||||||
|
coolify-log-drain:
|
||||||
|
image: cr.fluentbit.io/fluent/fluent-bit:2.0
|
||||||
|
container_name: coolify-log-drain
|
||||||
|
command: -c /fluent-bit.conf
|
||||||
|
env_file:
|
||||||
|
- .env
|
||||||
|
volumes:
|
||||||
|
- ./fluent-bit.conf:/fluent-bit.conf
|
||||||
|
- ./parsers.conf:/parsers.conf
|
||||||
|
ports:
|
||||||
|
- 127.0.0.1:24224:24224
|
||||||
|
restart: unless-stopped
|
||||||
|
");
|
||||||
|
$readme = base64_encode('# New Relic Log Drain
|
||||||
|
This log drain is based on [Fluent Bit](https://fluentbit.io/) and New Relic Log Forwarder.
|
||||||
|
|
||||||
|
Files:
|
||||||
|
- `fluent-bit.conf` - configuration file for Fluent Bit
|
||||||
|
- `docker-compose.yml` - docker-compose file to run Fluent Bit
|
||||||
|
- `.env` - environment variables for Fluent Bit
|
||||||
|
');
|
||||||
|
$license_key = $server->settings->logdrain_newrelic_license_key;
|
||||||
|
$base_uri = $server->settings->logdrain_newrelic_base_uri;
|
||||||
|
$base_path = config('coolify.base_config_path');
|
||||||
|
|
||||||
|
$config_path = $base_path . '/log-drains';
|
||||||
|
$fluent_bit_config = $config_path . '/fluent-bit.conf';
|
||||||
|
$parsers_config = $config_path . '/parsers.conf';
|
||||||
|
$compose_path = $config_path . '/docker-compose.yml';
|
||||||
|
$readme_path = $config_path . '/README.md';
|
||||||
|
$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",
|
||||||
|
"test -f $config_path/.env && rm $config_path/.env",
|
||||||
|
|
||||||
|
];
|
||||||
|
if ($type === 'newrelic') {
|
||||||
|
$add_envs_command = [
|
||||||
|
"echo LICENSE_KEY=$license_key >> $config_path/.env",
|
||||||
|
"echo BASE_URI=$base_uri >> $config_path/.env",
|
||||||
|
];
|
||||||
|
} else if ($type === 'highlight') {
|
||||||
|
$add_envs_command = [
|
||||||
|
"echo HIGHLIGHT_PROJECT_ID={$server->settings->logdrain_highlight_project_id} >> $config_path/.env",
|
||||||
|
];
|
||||||
|
} else if ($type === 'axiom') {
|
||||||
|
$add_envs_command = [
|
||||||
|
"echo AXIOM_DATASET_NAME={$server->settings->logdrain_axiom_dataset_name} >> $config_path/.env",
|
||||||
|
"echo AXIOM_API_KEY={$server->settings->logdrain_axiom_api_key} >> $config_path/.env",
|
||||||
|
];
|
||||||
|
}
|
||||||
|
$restart_command = [
|
||||||
|
"echo 'Stopping old Fluent Bit'",
|
||||||
|
"cd $config_path && docker rm -f coolify-log-drain || true",
|
||||||
|
"echo 'Starting Fluent Bit'",
|
||||||
|
"cd $config_path && docker compose up -d --remove-orphans",
|
||||||
|
];
|
||||||
|
$command = array_merge($command, $add_envs_command, $restart_command);
|
||||||
|
return instant_remote_process($command, $server);
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
return handleError($e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,9 +3,11 @@
|
|||||||
namespace App\Console\Commands;
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
use App\Enums\ApplicationDeploymentStatus;
|
use App\Enums\ApplicationDeploymentStatus;
|
||||||
|
use App\Jobs\CleanupHelperContainersJob;
|
||||||
use App\Models\Application;
|
use App\Models\Application;
|
||||||
use App\Models\ApplicationDeploymentQueue;
|
use App\Models\ApplicationDeploymentQueue;
|
||||||
use App\Models\InstanceSettings;
|
use App\Models\InstanceSettings;
|
||||||
|
use App\Models\Server;
|
||||||
use App\Models\Service;
|
use App\Models\Service;
|
||||||
use App\Models\ServiceApplication;
|
use App\Models\ServiceApplication;
|
||||||
use App\Models\ServiceDatabase;
|
use App\Models\ServiceDatabase;
|
||||||
@@ -32,10 +34,21 @@ class Init extends Command
|
|||||||
$this->cleanup_ssh();
|
$this->cleanup_ssh();
|
||||||
}
|
}
|
||||||
$this->cleanup_in_progress_application_deployments();
|
$this->cleanup_in_progress_application_deployments();
|
||||||
|
$this->cleanup_stucked_helper_containers();
|
||||||
|
}
|
||||||
|
private function cleanup_stucked_helper_containers() {
|
||||||
|
$servers = Server::all();
|
||||||
|
foreach ($servers as $server) {
|
||||||
|
if ($server->isFunctional()) {
|
||||||
|
CleanupHelperContainersJob::dispatch($server);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
private function alive()
|
private function alive()
|
||||||
{
|
{
|
||||||
$id = config('app.id');
|
$id = config('app.id');
|
||||||
|
$version = config('version');
|
||||||
$settings = InstanceSettings::get();
|
$settings = InstanceSettings::get();
|
||||||
$do_not_track = data_get($settings, 'do_not_track');
|
$do_not_track = data_get($settings, 'do_not_track');
|
||||||
if ($do_not_track == true) {
|
if ($do_not_track == true) {
|
||||||
@@ -43,8 +56,8 @@ class Init extends Command
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
|
Http::get("https://get.coollabs.io/coolify/v4/alive?appId=$id&version=$version");
|
||||||
echo "I am alive!\n";
|
echo "I am alive!\n";
|
||||||
Http::get("https://get.coollabs.io/coolify/v4/alive?appId=$id");
|
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
echo "Error in alive: {$e->getMessage()}\n";
|
echo "Error in alive: {$e->getMessage()}\n";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Console;
|
namespace App\Console;
|
||||||
|
|
||||||
|
use App\Jobs\CheckLogDrainContainerJob;
|
||||||
use App\Jobs\CleanupInstanceStuffsJob;
|
use App\Jobs\CleanupInstanceStuffsJob;
|
||||||
use App\Jobs\DatabaseBackupJob;
|
use App\Jobs\DatabaseBackupJob;
|
||||||
use App\Jobs\InstanceAutoUpdateJob;
|
use App\Jobs\InstanceAutoUpdateJob;
|
||||||
@@ -11,6 +12,7 @@ use App\Jobs\ServerStatusJob;
|
|||||||
use App\Models\InstanceSettings;
|
use App\Models\InstanceSettings;
|
||||||
use App\Models\ScheduledDatabaseBackup;
|
use App\Models\ScheduledDatabaseBackup;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
|
use App\Models\Team;
|
||||||
use Illuminate\Console\Scheduling\Schedule;
|
use Illuminate\Console\Scheduling\Schedule;
|
||||||
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
|
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
|
||||||
|
|
||||||
@@ -43,7 +45,7 @@ class Kernel extends ConsoleKernel
|
|||||||
}
|
}
|
||||||
private function pull_helper_image($schedule)
|
private function pull_helper_image($schedule)
|
||||||
{
|
{
|
||||||
$servers = Server::all()->where('settings.is_usable', true)->where('settings.is_reachable', true);
|
$servers = Server::all()->where('settings.is_usable', true)->where('settings.is_reachable', true)->where('ip', '!=', '1.2.3.4');
|
||||||
foreach ($servers as $server) {
|
foreach ($servers as $server) {
|
||||||
$schedule->job(new PullHelperImageJob($server))->everyTenMinutes()->onOneServer();
|
$schedule->job(new PullHelperImageJob($server))->everyTenMinutes()->onOneServer();
|
||||||
}
|
}
|
||||||
@@ -51,13 +53,18 @@ class Kernel extends ConsoleKernel
|
|||||||
private function check_resources($schedule)
|
private function check_resources($schedule)
|
||||||
{
|
{
|
||||||
if (isCloud()) {
|
if (isCloud()) {
|
||||||
$servers = Server::all()->whereNotNull('team.subscription')->where('team.subscription.stripe_trial_already_ended', false);
|
$servers = Server::all()->whereNotNull('team.subscription')->where('team.subscription.stripe_trial_already_ended', false)->where('ip', '!=', '1.2.3.4');
|
||||||
|
$own = Team::find(0)->servers;
|
||||||
|
$servers = $servers->merge($own);
|
||||||
} else {
|
} else {
|
||||||
$servers = Server::all();
|
$servers = Server::all()->where('ip', '!=', '1.2.3.4');
|
||||||
}
|
}
|
||||||
foreach ($servers as $server) {
|
foreach ($servers as $server) {
|
||||||
$schedule->job(new ServerStatusJob($server))->everyTenMinutes()->onOneServer();
|
$schedule->job(new ServerStatusJob($server))->everyTenMinutes()->onOneServer();
|
||||||
$schedule->job(new ContainerStatusJob($server))->everyMinute()->onOneServer();
|
$schedule->job(new ContainerStatusJob($server))->everyMinute()->onOneServer();
|
||||||
|
if ($server->isLogDrainEnabled()) {
|
||||||
|
$schedule->job(new CheckLogDrainContainerJob($server))->everyMinute()->onOneServer();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private function instance_auto_update($schedule)
|
private function instance_auto_update($schedule)
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ class General extends Component
|
|||||||
public bool $is_preview_deployments_enabled;
|
public bool $is_preview_deployments_enabled;
|
||||||
public bool $is_auto_deploy_enabled;
|
public bool $is_auto_deploy_enabled;
|
||||||
public bool $is_force_https_enabled;
|
public bool $is_force_https_enabled;
|
||||||
|
public bool $is_log_drain_enabled;
|
||||||
|
|
||||||
protected $rules = [
|
protected $rules = [
|
||||||
'application.name' => 'required',
|
'application.name' => 'required',
|
||||||
@@ -101,6 +101,7 @@ class General extends Component
|
|||||||
$this->is_preview_deployments_enabled = $this->application->settings->is_preview_deployments_enabled;
|
$this->is_preview_deployments_enabled = $this->application->settings->is_preview_deployments_enabled;
|
||||||
$this->is_auto_deploy_enabled = $this->application->settings->is_auto_deploy_enabled;
|
$this->is_auto_deploy_enabled = $this->application->settings->is_auto_deploy_enabled;
|
||||||
$this->is_force_https_enabled = $this->application->settings->is_force_https_enabled;
|
$this->is_force_https_enabled = $this->application->settings->is_force_https_enabled;
|
||||||
|
$this->is_log_drain_enabled = $this->application->settings->is_log_drain_enabled;
|
||||||
}
|
}
|
||||||
$this->checkLabelUpdates();
|
$this->checkLabelUpdates();
|
||||||
}
|
}
|
||||||
@@ -136,6 +137,14 @@ class General extends Component
|
|||||||
$this->application->settings->is_preview_deployments_enabled = $this->is_preview_deployments_enabled;
|
$this->application->settings->is_preview_deployments_enabled = $this->is_preview_deployments_enabled;
|
||||||
$this->application->settings->is_auto_deploy_enabled = $this->is_auto_deploy_enabled;
|
$this->application->settings->is_auto_deploy_enabled = $this->is_auto_deploy_enabled;
|
||||||
$this->application->settings->is_force_https_enabled = $this->is_force_https_enabled;
|
$this->application->settings->is_force_https_enabled = $this->is_force_https_enabled;
|
||||||
|
$this->application->settings->is_log_drain_enabled = $this->is_log_drain_enabled;
|
||||||
|
if ($this->is_log_drain_enabled) {
|
||||||
|
if (!$this->application->destination->server->isLogDrainEnabled()) {
|
||||||
|
$this->application->settings->is_log_drain_enabled = $this->is_log_drain_enabled = false;
|
||||||
|
$this->emit('error', 'Log drain is not enabled on the server. Please enable it first.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
$this->application->settings->save();
|
$this->application->settings->save();
|
||||||
$this->application->save();
|
$this->application->save();
|
||||||
$this->application->refresh();
|
$this->application->refresh();
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ namespace App\Http\Livewire\Project\Application;
|
|||||||
|
|
||||||
use App\Actions\Application\StopApplication;
|
use App\Actions\Application\StopApplication;
|
||||||
use App\Jobs\ContainerStatusJob;
|
use App\Jobs\ContainerStatusJob;
|
||||||
|
use App\Jobs\ServerStatusJob;
|
||||||
use App\Models\Application;
|
use App\Models\Application;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
use Visus\Cuid2\Cuid2;
|
use Visus\Cuid2\Cuid2;
|
||||||
@@ -28,6 +29,8 @@ class Heading extends Component
|
|||||||
$this->application->previews->each(function ($preview) {
|
$this->application->previews->each(function ($preview) {
|
||||||
$preview->refresh();
|
$preview->refresh();
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
dispatch(new ServerStatusJob($this->application->destination->server));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -42,28 +42,31 @@ class Rollback extends Component
|
|||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$image = $this->application->uuid;
|
$image = $this->application->uuid;
|
||||||
$output = instant_remote_process([
|
if ($this->application->destination->server->isFunctional()) {
|
||||||
"docker inspect --format='{{.Config.Image}}' {$this->application->uuid}",
|
$output = instant_remote_process([
|
||||||
], $this->application->destination->server, throwError: false);
|
"docker inspect --format='{{.Config.Image}}' {$this->application->uuid}",
|
||||||
$current_tag = Str::of($output)->trim()->explode(":");
|
], $this->application->destination->server, throwError: false);
|
||||||
$this->current = data_get($current_tag, 1);
|
$current_tag = Str::of($output)->trim()->explode(":");
|
||||||
|
$this->current = data_get($current_tag, 1);
|
||||||
|
|
||||||
$output = instant_remote_process([
|
$output = instant_remote_process([
|
||||||
"docker images --format '{{.Repository}}#{{.Tag}}#{{.CreatedAt}}'",
|
"docker images --format '{{.Repository}}#{{.Tag}}#{{.CreatedAt}}'",
|
||||||
], $this->application->destination->server);
|
], $this->application->destination->server);
|
||||||
$this->images = Str::of($output)->trim()->explode("\n")->filter(function ($item) use ($image) {
|
$this->images = Str::of($output)->trim()->explode("\n")->filter(function ($item) use ($image) {
|
||||||
return Str::of($item)->contains($image);
|
return Str::of($item)->contains($image);
|
||||||
})->map(function ($item) {
|
})->map(function ($item) {
|
||||||
$item = Str::of($item)->explode('#');
|
$item = Str::of($item)->explode('#');
|
||||||
if ($item[1] === $this->current) {
|
if ($item[1] === $this->current) {
|
||||||
// $is_current = true;
|
// $is_current = true;
|
||||||
}
|
}
|
||||||
return [
|
return [
|
||||||
'tag' => $item[1],
|
'tag' => $item[1],
|
||||||
'created_at' => $item[2],
|
'created_at' => $item[2],
|
||||||
'is_current' => $is_current ?? null,
|
'is_current' => $is_current ?? null,
|
||||||
];
|
];
|
||||||
})->toArray();
|
})->toArray();
|
||||||
|
}
|
||||||
|
return [];
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ class General extends Component
|
|||||||
'database.ports_mappings' => 'nullable',
|
'database.ports_mappings' => 'nullable',
|
||||||
'database.is_public' => 'nullable|boolean',
|
'database.is_public' => 'nullable|boolean',
|
||||||
'database.public_port' => 'nullable|integer',
|
'database.public_port' => 'nullable|integer',
|
||||||
|
'database.is_log_drain_enabled' => 'nullable|boolean',
|
||||||
];
|
];
|
||||||
protected $validationAttributes = [
|
protected $validationAttributes = [
|
||||||
'database.name' => 'Name',
|
'database.name' => 'Name',
|
||||||
@@ -50,6 +51,20 @@ class General extends Component
|
|||||||
$this->db_url_public = $this->database->getDbUrl();
|
$this->db_url_public = $this->database->getDbUrl();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public function instantSaveAdvanced() {
|
||||||
|
try {
|
||||||
|
if (!$this->database->destination->server->isLogDrainEnabled()) {
|
||||||
|
$this->database->is_log_drain_enabled = false;
|
||||||
|
$this->emit('error', 'Log drain is not enabled on the server. Please enable it first.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$this->database->save();
|
||||||
|
$this->emit('success', 'Database updated successfully.');
|
||||||
|
$this->emit('success', 'You need to restart the service for the changes to take effect.');
|
||||||
|
} catch (Exception $e) {
|
||||||
|
return handleError($e, $this);
|
||||||
|
}
|
||||||
|
}
|
||||||
public function submit()
|
public function submit()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ class General extends Component
|
|||||||
'database.ports_mappings' => 'nullable',
|
'database.ports_mappings' => 'nullable',
|
||||||
'database.is_public' => 'nullable|boolean',
|
'database.is_public' => 'nullable|boolean',
|
||||||
'database.public_port' => 'nullable|integer',
|
'database.public_port' => 'nullable|integer',
|
||||||
|
'database.is_log_drain_enabled' => 'nullable|boolean',
|
||||||
];
|
];
|
||||||
protected $validationAttributes = [
|
protected $validationAttributes = [
|
||||||
'database.name' => 'Name',
|
'database.name' => 'Name',
|
||||||
@@ -48,7 +49,21 @@ class General extends Component
|
|||||||
$this->db_url_public = $this->database->getDbUrl();
|
$this->db_url_public = $this->database->getDbUrl();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public function instantSaveAdvanced()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
if (!$this->database->destination->server->isLogDrainEnabled()) {
|
||||||
|
$this->database->is_log_drain_enabled = false;
|
||||||
|
$this->emit('error', 'Log drain is not enabled on the server. Please enable it first.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$this->database->save();
|
||||||
|
$this->emit('success', 'Database updated successfully.');
|
||||||
|
$this->emit('success', 'You need to restart the service for the changes to take effect.');
|
||||||
|
} catch (Exception $e) {
|
||||||
|
return handleError($e, $this);
|
||||||
|
}
|
||||||
|
}
|
||||||
public function submit()
|
public function submit()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ class General extends Component
|
|||||||
'database.ports_mappings' => 'nullable',
|
'database.ports_mappings' => 'nullable',
|
||||||
'database.is_public' => 'nullable|boolean',
|
'database.is_public' => 'nullable|boolean',
|
||||||
'database.public_port' => 'nullable|integer',
|
'database.public_port' => 'nullable|integer',
|
||||||
|
'database.is_log_drain_enabled' => 'nullable|boolean',
|
||||||
];
|
];
|
||||||
protected $validationAttributes = [
|
protected $validationAttributes = [
|
||||||
'database.name' => 'Name',
|
'database.name' => 'Name',
|
||||||
@@ -50,6 +51,21 @@ class General extends Component
|
|||||||
$this->db_url_public = $this->database->getDbUrl();
|
$this->db_url_public = $this->database->getDbUrl();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public function instantSaveAdvanced()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
if (!$this->database->destination->server->isLogDrainEnabled()) {
|
||||||
|
$this->database->is_log_drain_enabled = false;
|
||||||
|
$this->emit('error', 'Log drain is not enabled on the server. Please enable it first.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$this->database->save();
|
||||||
|
$this->emit('success', 'Database updated successfully.');
|
||||||
|
$this->emit('success', 'You need to restart the service for the changes to take effect.');
|
||||||
|
} catch (Exception $e) {
|
||||||
|
return handleError($e, $this);
|
||||||
|
}
|
||||||
|
}
|
||||||
public function submit()
|
public function submit()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ class General extends Component
|
|||||||
'database.ports_mappings' => 'nullable',
|
'database.ports_mappings' => 'nullable',
|
||||||
'database.is_public' => 'nullable|boolean',
|
'database.is_public' => 'nullable|boolean',
|
||||||
'database.public_port' => 'nullable|integer',
|
'database.public_port' => 'nullable|integer',
|
||||||
|
'database.is_log_drain_enabled' => 'nullable|boolean',
|
||||||
];
|
];
|
||||||
protected $validationAttributes = [
|
protected $validationAttributes = [
|
||||||
'database.name' => 'Name',
|
'database.name' => 'Name',
|
||||||
@@ -57,6 +58,20 @@ class General extends Component
|
|||||||
$this->db_url_public = $this->database->getDbUrl();
|
$this->db_url_public = $this->database->getDbUrl();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public function instantSaveAdvanced() {
|
||||||
|
try {
|
||||||
|
if (!$this->database->destination->server->isLogDrainEnabled()) {
|
||||||
|
$this->database->is_log_drain_enabled = false;
|
||||||
|
$this->emit('error', 'Log drain is not enabled on the server. Please enable it first.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$this->database->save();
|
||||||
|
$this->emit('success', 'Database updated successfully.');
|
||||||
|
$this->emit('success', 'You need to restart the service for the changes to take effect.');
|
||||||
|
} catch (Exception $e) {
|
||||||
|
return handleError($e, $this);
|
||||||
|
}
|
||||||
|
}
|
||||||
public function instantSave()
|
public function instantSave()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ class General extends Component
|
|||||||
'database.ports_mappings' => 'nullable',
|
'database.ports_mappings' => 'nullable',
|
||||||
'database.is_public' => 'nullable|boolean',
|
'database.is_public' => 'nullable|boolean',
|
||||||
'database.public_port' => 'nullable|integer',
|
'database.public_port' => 'nullable|integer',
|
||||||
|
'database.is_log_drain_enabled' => 'nullable|boolean',
|
||||||
];
|
];
|
||||||
protected $validationAttributes = [
|
protected $validationAttributes = [
|
||||||
'database.name' => 'Name',
|
'database.name' => 'Name',
|
||||||
@@ -43,6 +44,20 @@ class General extends Component
|
|||||||
$this->db_url_public = $this->database->getDbUrl();
|
$this->db_url_public = $this->database->getDbUrl();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public function instantSaveAdvanced() {
|
||||||
|
try {
|
||||||
|
if (!$this->database->destination->server->isLogDrainEnabled()) {
|
||||||
|
$this->database->is_log_drain_enabled = false;
|
||||||
|
$this->emit('error', 'Log drain is not enabled on the server. Please enable it first.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$this->database->save();
|
||||||
|
$this->emit('success', 'Database updated successfully.');
|
||||||
|
$this->emit('success', 'You need to restart the service for the changes to take effect.');
|
||||||
|
} catch (Exception $e) {
|
||||||
|
return handleError($e, $this);
|
||||||
|
}
|
||||||
|
}
|
||||||
public function submit()
|
public function submit()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ class Application extends Component
|
|||||||
'application.image' => 'required',
|
'application.image' => 'required',
|
||||||
'application.exclude_from_status' => 'required|boolean',
|
'application.exclude_from_status' => 'required|boolean',
|
||||||
'application.required_fqdn' => 'required|boolean',
|
'application.required_fqdn' => 'required|boolean',
|
||||||
|
'application.is_log_drain_enabled' => 'nullable|boolean',
|
||||||
];
|
];
|
||||||
public function render()
|
public function render()
|
||||||
{
|
{
|
||||||
@@ -25,7 +26,11 @@ class Application extends Component
|
|||||||
{
|
{
|
||||||
$this->submit();
|
$this->submit();
|
||||||
}
|
}
|
||||||
|
public function instantSaveAdvanced()
|
||||||
|
{
|
||||||
|
$this->submit();
|
||||||
|
$this->emit('success', 'You need to restart the service for the changes to take effect.');
|
||||||
|
}
|
||||||
public function delete()
|
public function delete()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
@@ -44,6 +49,11 @@ class Application extends Component
|
|||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$this->validate();
|
$this->validate();
|
||||||
|
if (!$this->application->service->destination->server->isLogDrainEnabled()) {
|
||||||
|
$this->application->is_log_drain_enabled = false;
|
||||||
|
$this->emit('error', 'Log drain is not enabled on the server. Please enable it first.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
$this->application->save();
|
$this->application->save();
|
||||||
updateCompose($this->application);
|
updateCompose($this->application);
|
||||||
$this->emit('success', 'Application saved successfully.');
|
$this->emit('success', 'Application saved successfully.');
|
||||||
|
|||||||
@@ -21,18 +21,31 @@ class Database extends Component
|
|||||||
'database.exclude_from_status' => 'required|boolean',
|
'database.exclude_from_status' => 'required|boolean',
|
||||||
'database.public_port' => 'nullable|integer',
|
'database.public_port' => 'nullable|integer',
|
||||||
'database.is_public' => 'required|boolean',
|
'database.is_public' => 'required|boolean',
|
||||||
|
'database.is_log_drain_enabled' => 'required|boolean',
|
||||||
];
|
];
|
||||||
public function render()
|
public function render()
|
||||||
{
|
{
|
||||||
return view('livewire.project.service.database');
|
return view('livewire.project.service.database');
|
||||||
}
|
}
|
||||||
public function mount() {
|
public function mount()
|
||||||
|
{
|
||||||
if ($this->database->is_public) {
|
if ($this->database->is_public) {
|
||||||
$this->db_url_public = $this->database->getServiceDatabaseUrl();
|
$this->db_url_public = $this->database->getServiceDatabaseUrl();
|
||||||
}
|
}
|
||||||
$this->refreshFileStorages();
|
$this->refreshFileStorages();
|
||||||
}
|
}
|
||||||
public function instantSave() {
|
public function instantSaveAdvanced()
|
||||||
|
{
|
||||||
|
if (!$this->database->service->destination->server->isLogDrainEnabled()) {
|
||||||
|
$this->database->is_log_drain_enabled = false;
|
||||||
|
$this->emit('error', 'Log drain is not enabled on the server. Please enable it first.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$this->submit();
|
||||||
|
$this->emit('success', 'You need to restart the service for the changes to take effect.');
|
||||||
|
}
|
||||||
|
public function instantSave()
|
||||||
|
{
|
||||||
if ($this->database->is_public && !$this->database->public_port) {
|
if ($this->database->is_public && !$this->database->public_port) {
|
||||||
$this->emit('error', 'Public port is required.');
|
$this->emit('error', 'Public port is required.');
|
||||||
$this->database->is_public = false;
|
$this->database->is_public = false;
|
||||||
|
|||||||
133
app/Http/Livewire/Server/LogDrains.php
Normal file
133
app/Http/Livewire/Server/LogDrains.php
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Livewire\Server;
|
||||||
|
|
||||||
|
use App\Actions\Server\InstallLogDrain;
|
||||||
|
use App\Models\Server;
|
||||||
|
use Livewire\Component;
|
||||||
|
|
||||||
|
class LogDrains extends Component
|
||||||
|
{
|
||||||
|
public Server $server;
|
||||||
|
public $parameters = [];
|
||||||
|
protected $rules = [
|
||||||
|
'server.settings.is_logdrain_newrelic_enabled' => 'required|boolean',
|
||||||
|
'server.settings.logdrain_newrelic_license_key' => 'required|string',
|
||||||
|
'server.settings.logdrain_newrelic_base_uri' => 'required|string',
|
||||||
|
'server.settings.is_logdrain_highlight_enabled' => 'required|boolean',
|
||||||
|
'server.settings.logdrain_highlight_project_id' => 'required|string',
|
||||||
|
'server.settings.is_logdrain_axiom_enabled' => 'required|boolean',
|
||||||
|
'server.settings.logdrain_axiom_dataset_name' => 'required|string',
|
||||||
|
'server.settings.logdrain_axiom_api_key' => 'required|string',
|
||||||
|
];
|
||||||
|
protected $validationAttributes = [
|
||||||
|
'server.settings.is_logdrain_newrelic_enabled' => 'New Relic log drain',
|
||||||
|
'server.settings.logdrain_newrelic_license_key' => 'New Relic license key',
|
||||||
|
'server.settings.logdrain_newrelic_base_uri' => 'New Relic base URI',
|
||||||
|
'server.settings.is_logdrain_highlight_enabled' => 'Highlight log drain',
|
||||||
|
'server.settings.logdrain_highlight_project_id' => 'Highlight project ID',
|
||||||
|
'server.settings.is_logdrain_axiom_enabled' => 'Axiom log drain',
|
||||||
|
'server.settings.logdrain_axiom_dataset_name' => 'Axiom dataset name',
|
||||||
|
'server.settings.logdrain_axiom_api_key' => 'Axiom API key',
|
||||||
|
];
|
||||||
|
|
||||||
|
public function mount()
|
||||||
|
{
|
||||||
|
$this->parameters = get_route_parameters();
|
||||||
|
try {
|
||||||
|
$server = Server::ownedByCurrentTeam(['name', 'description', 'ip', 'port', 'user', 'proxy'])->whereUuid(request()->server_uuid)->first();
|
||||||
|
if (is_null($server)) {
|
||||||
|
return redirect()->route('server.all');
|
||||||
|
}
|
||||||
|
$this->server = $server;
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
return handleError($e, $this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function configureLogDrain()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
InstallLogDrain::run($this->server);
|
||||||
|
if (!$this->server->isLogDrainEnabled()) {
|
||||||
|
$this->emit('serverRefresh');
|
||||||
|
$this->emit('success', 'Log drain service stopped.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$this->emit('serverRefresh');
|
||||||
|
$this->emit('success', 'Log drain service started successfully.');
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
return handleError($e, $this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function instantSave(string $type)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$ok = $this->submit($type);
|
||||||
|
if (!$ok) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$this->configureLogDrain();
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
return handleError($e, $this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function submit(string $type)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$this->resetErrorBag();
|
||||||
|
if ($type === 'newrelic') {
|
||||||
|
$this->validate([
|
||||||
|
'server.settings.is_logdrain_newrelic_enabled' => 'required|boolean',
|
||||||
|
'server.settings.logdrain_newrelic_license_key' => 'required|string',
|
||||||
|
'server.settings.logdrain_newrelic_base_uri' => 'required|string',
|
||||||
|
]);
|
||||||
|
$this->server->settings->update([
|
||||||
|
'is_logdrain_highlight_enabled' => false,
|
||||||
|
'is_logdrain_axiom_enabled' => false,
|
||||||
|
]);
|
||||||
|
} else if ($type === 'highlight') {
|
||||||
|
$this->validate([
|
||||||
|
'server.settings.is_logdrain_highlight_enabled' => 'required|boolean',
|
||||||
|
'server.settings.logdrain_highlight_project_id' => 'required|string',
|
||||||
|
]);
|
||||||
|
$this->server->settings->update([
|
||||||
|
'is_logdrain_newrelic_enabled' => false,
|
||||||
|
'is_logdrain_axiom_enabled' => false,
|
||||||
|
]);
|
||||||
|
} else if ($type === 'axiom') {
|
||||||
|
$this->validate([
|
||||||
|
'server.settings.is_logdrain_axiom_enabled' => 'required|boolean',
|
||||||
|
'server.settings.logdrain_axiom_dataset_name' => 'required|string',
|
||||||
|
'server.settings.logdrain_axiom_api_key' => 'required|string',
|
||||||
|
]);
|
||||||
|
$this->server->settings->update([
|
||||||
|
'is_logdrain_newrelic_enabled' => false,
|
||||||
|
'is_logdrain_highlight_enabled' => false,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
$this->server->settings->save();
|
||||||
|
$this->emit('success', 'Settings saved successfully.');
|
||||||
|
return true;
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
if ($type === 'newrelic') {
|
||||||
|
$this->server->settings->update([
|
||||||
|
'is_logdrain_newrelic_enabled' => false,
|
||||||
|
]);
|
||||||
|
} else if ($type === 'highlight') {
|
||||||
|
$this->server->settings->update([
|
||||||
|
'is_logdrain_highlight_enabled' => false,
|
||||||
|
]);
|
||||||
|
} else if ($type === 'axiom') {
|
||||||
|
$this->server->settings->update([
|
||||||
|
'is_logdrain_axiom_enabled' => false,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
handleError($e, $this);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
return view('livewire.server.log-drains');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -498,7 +498,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
if ($this->full_healthcheck_url) {
|
if ($this->full_healthcheck_url) {
|
||||||
$this->execute_remote_command(
|
$this->execute_remote_command(
|
||||||
[
|
[
|
||||||
"echo 'Healthcheck URL inside your container: {$this->full_healthcheck_url}'"
|
"echo 'Healthcheck URL (inside the container): {$this->full_healthcheck_url}'"
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -837,13 +837,6 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
'networks' => [
|
'networks' => [
|
||||||
$this->destination->network,
|
$this->destination->network,
|
||||||
],
|
],
|
||||||
// 'logging' => [
|
|
||||||
// 'driver' => 'fluentd',
|
|
||||||
// 'options' => [
|
|
||||||
// 'fluentd-async' => 'true',
|
|
||||||
// 'tag' => $this->application->name . '-' . $this->application->uuid
|
|
||||||
// ]
|
|
||||||
// ],
|
|
||||||
'healthcheck' => [
|
'healthcheck' => [
|
||||||
'test' => [
|
'test' => [
|
||||||
'CMD-SHELL',
|
'CMD-SHELL',
|
||||||
@@ -871,6 +864,16 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
]
|
]
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
if ($this->server->isLogDrainEnabled() && $this->application->isLogDrainEnabled()) {
|
||||||
|
$docker_compose['services'][$this->container_name]['logging'] = [
|
||||||
|
'driver' => 'fluentd',
|
||||||
|
'options' => [
|
||||||
|
'fluentd-address' => "tcp://127.0.0.1:24224",
|
||||||
|
'fluentd-async' => "true",
|
||||||
|
'fluentd-sub-second-precision' => "true",
|
||||||
|
]
|
||||||
|
];
|
||||||
|
}
|
||||||
if ($this->application->isHealthcheckDisabled()) {
|
if ($this->application->isHealthcheckDisabled()) {
|
||||||
data_forget($docker_compose, 'services.' . $this->container_name . '.healthcheck');
|
data_forget($docker_compose, 'services.' . $this->container_name . '.healthcheck');
|
||||||
}
|
}
|
||||||
@@ -1071,9 +1074,15 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
|
|||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
// Pure Dockerfile based deployment
|
// Pure Dockerfile based deployment
|
||||||
$this->execute_remote_command([
|
if ($this->application->dockerfile) {
|
||||||
executeInDocker($this->deployment_uuid, "docker build --pull $this->buildTarget $this->addHosts --network host -f {$this->workdir}{$this->dockerfile_location} {$this->build_args} --progress plain -t $this->production_image_name {$this->workdir}"), "hidden" => true
|
$this->execute_remote_command([
|
||||||
]);
|
executeInDocker($this->deployment_uuid, "docker build --pull $this->buildTarget $this->addHosts --network host -f {$this->workdir}{$this->dockerfile_location} {$this->build_args} --progress plain -t $this->production_image_name {$this->workdir}"), "hidden" => true
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
$this->execute_remote_command([
|
||||||
|
executeInDocker($this->deployment_uuid, "docker build $this->buildTarget $this->addHosts --network host -f {$this->workdir}{$this->dockerfile_location} {$this->build_args} --progress plain -t $this->production_image_name {$this->workdir}"), "hidden" => true
|
||||||
|
]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
88
app/Jobs/CheckLogDrainContainerJob.php
Normal file
88
app/Jobs/CheckLogDrainContainerJob.php
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Jobs;
|
||||||
|
|
||||||
|
use App\Actions\Server\InstallLogDrain;
|
||||||
|
use App\Models\Server;
|
||||||
|
use App\Notifications\Container\ContainerRestarted;
|
||||||
|
use App\Notifications\Container\ContainerStopped;
|
||||||
|
use Illuminate\Bus\Queueable;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
|
use Illuminate\Queue\Middleware\WithoutOverlapping;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
use Illuminate\Support\Sleep;
|
||||||
|
|
||||||
|
class CheckLogDrainContainerJob implements ShouldQueue, ShouldBeEncrypted
|
||||||
|
{
|
||||||
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||||
|
|
||||||
|
public function __construct(public Server $server)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
public function middleware(): array
|
||||||
|
{
|
||||||
|
return [(new WithoutOverlapping($this->server->id))->dontRelease()];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function uniqueId(): int
|
||||||
|
{
|
||||||
|
return $this->server->id;
|
||||||
|
}
|
||||||
|
public function healthcheck()
|
||||||
|
{
|
||||||
|
$status = instant_remote_process(["docker inspect --format='{{json .State.Status}}' coolify-log-drain"], $this->server, false);
|
||||||
|
if (str($status)->contains('running')) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function handle(): void
|
||||||
|
{
|
||||||
|
// ray("checking log drain statuses for {$this->server->id}");
|
||||||
|
try {
|
||||||
|
if (!$this->server->isServerReady()) {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
$containers = instant_remote_process(["docker container ls -q"], $this->server);
|
||||||
|
if (!$containers) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$containers = instant_remote_process(["docker container inspect $(docker container ls -q) --format '{{json .}}'"], $this->server);
|
||||||
|
$containers = format_docker_command_output_to_json($containers);
|
||||||
|
|
||||||
|
$foundLogDrainContainer = $containers->filter(function ($value, $key) {
|
||||||
|
return data_get($value, 'Name') === '/coolify-log-drain';
|
||||||
|
})->first();
|
||||||
|
if (!$foundLogDrainContainer || !$this->healthcheck()) {
|
||||||
|
ray('Log drain container not found or unhealthy. Restarting...');
|
||||||
|
InstallLogDrain::run($this->server);
|
||||||
|
Sleep::for(10)->seconds();
|
||||||
|
if ($this->healthcheck()) {
|
||||||
|
if ($this->server->log_drain_notification_sent) {
|
||||||
|
$this->server->team->notify(new ContainerRestarted('Coolify Log Drainer', $this->server));
|
||||||
|
$this->server->update(['log_drain_notification_sent' => false]);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!$this->server->log_drain_notification_sent) {
|
||||||
|
ray('Log drain container still unhealthy. Sending notification...');
|
||||||
|
$this->server->team->notify(new ContainerStopped('Coolify Log Drainer', $this->server, null));
|
||||||
|
$this->server->update(['log_drain_notification_sent' => true]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ($this->server->log_drain_notification_sent) {
|
||||||
|
$this->server->team->notify(new ContainerRestarted('Coolify Log Drainer', $this->server));
|
||||||
|
$this->server->update(['log_drain_notification_sent' => false]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
send_internal_notification("CheckLogDrainContainerJob failed on ({$this->server->id}) with: " . $e->getMessage());
|
||||||
|
ray($e->getMessage());
|
||||||
|
handleError($e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
40
app/Jobs/CleanupHelperContainersJob.php
Normal file
40
app/Jobs/CleanupHelperContainersJob.php
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Jobs;
|
||||||
|
|
||||||
|
use App\Models\Server;
|
||||||
|
use Illuminate\Bus\Queueable;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldBeUnique;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
|
class CleanupHelperContainersJob implements ShouldQueue, ShouldBeUnique, ShouldBeEncrypted
|
||||||
|
{
|
||||||
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||||
|
|
||||||
|
public function __construct(public Server $server)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handle(): void
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
ray('Cleaning up helper containers on ' . $this->server->name);
|
||||||
|
$containers = instant_remote_process(['docker container ps --filter "ancestor=ghcr.io/coollabsio/coolify-helper:next" --filter "ancestor=ghcr.io/coollabsio/coolify-helper:latest" --format \'{{json .}}\''], $this->server, false);
|
||||||
|
$containers = format_docker_command_output_to_json($containers);
|
||||||
|
if ($containers->count() > 0) {
|
||||||
|
foreach ($containers as $container) {
|
||||||
|
$containerId = data_get($container,'ID');
|
||||||
|
ray('Removing container ' . $containerId);
|
||||||
|
instant_remote_process(['docker container rm -f ' . $containerId], $this->server, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
send_internal_notification('CleanupHelperContainersJob failed with error: ' . $e->getMessage());
|
||||||
|
ray($e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -37,9 +37,11 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
|
|
||||||
public function handle(): void
|
public function handle(): void
|
||||||
{
|
{
|
||||||
// ray("checking server status for {$this->server->id}");
|
ray("checking container statuses for {$this->server->id}");
|
||||||
try {
|
try {
|
||||||
$this->server->checkServerRediness();
|
if (!$this->server->isServerReady()) {
|
||||||
|
return;
|
||||||
|
};
|
||||||
$containers = instant_remote_process(["docker container ls -q"], $this->server);
|
$containers = instant_remote_process(["docker container ls -q"], $this->server);
|
||||||
if (!$containers) {
|
if (!$containers) {
|
||||||
return;
|
return;
|
||||||
@@ -50,29 +52,7 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
$databases = $this->server->databases();
|
$databases = $this->server->databases();
|
||||||
$services = $this->server->services()->get();
|
$services = $this->server->services()->get();
|
||||||
$previews = $this->server->previews();
|
$previews = $this->server->previews();
|
||||||
$this->server->proxyType();
|
|
||||||
/// Check if proxy is running
|
|
||||||
$foundProxyContainer = $containers->filter(function ($value, $key) {
|
|
||||||
return data_get($value, 'Name') === '/coolify-proxy';
|
|
||||||
})->first();
|
|
||||||
if (!$foundProxyContainer) {
|
|
||||||
try {
|
|
||||||
$shouldStart = CheckProxy::run($this->server);
|
|
||||||
if ($shouldStart) {
|
|
||||||
StartProxy::run($this->server, false);
|
|
||||||
$this->server->team->notify(new ContainerRestarted('coolify-proxy', $this->server));
|
|
||||||
} else {
|
|
||||||
ray('Proxy could not be started.');
|
|
||||||
}
|
|
||||||
} catch (\Throwable $e) {
|
|
||||||
ray($e);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$this->server->proxy->status = data_get($foundProxyContainer, 'State.Status');
|
|
||||||
$this->server->save();
|
|
||||||
$connectProxyToDockerNetworks = connectProxyToNetworks($this->server);
|
|
||||||
instant_remote_process($connectProxyToDockerNetworks, $this->server, false);
|
|
||||||
}
|
|
||||||
$foundApplications = [];
|
$foundApplications = [];
|
||||||
$foundApplicationPreviews = [];
|
$foundApplicationPreviews = [];
|
||||||
$foundDatabases = [];
|
$foundDatabases = [];
|
||||||
@@ -265,8 +245,32 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
}
|
}
|
||||||
$this->server->team->notify(new ContainerStopped($containerName, $this->server, $url));
|
$this->server->team->notify(new ContainerStopped($containerName, $this->server, $url));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if proxy is running
|
||||||
|
$this->server->proxyType();
|
||||||
|
$foundProxyContainer = $containers->filter(function ($value, $key) {
|
||||||
|
return data_get($value, 'Name') === '/coolify-proxy';
|
||||||
|
})->first();
|
||||||
|
if (!$foundProxyContainer) {
|
||||||
|
try {
|
||||||
|
$shouldStart = CheckProxy::run($this->server);
|
||||||
|
if ($shouldStart) {
|
||||||
|
StartProxy::run($this->server, false);
|
||||||
|
$this->server->team->notify(new ContainerRestarted('coolify-proxy', $this->server));
|
||||||
|
} else {
|
||||||
|
ray('Proxy could not be started.');
|
||||||
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
ray($e);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$this->server->proxy->status = data_get($foundProxyContainer, 'State.Status');
|
||||||
|
$this->server->save();
|
||||||
|
$connectProxyToDockerNetworks = connectProxyToNetworks($this->server);
|
||||||
|
instant_remote_process($connectProxyToDockerNetworks, $this->server, false);
|
||||||
|
}
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
send_internal_notification('ContainerStatusJob failed with: ' . $e->getMessage());
|
send_internal_notification("ContainerStatusJob failed on ({$this->server->id}) with: " . $e->getMessage());
|
||||||
ray($e->getMessage());
|
ray($e->getMessage());
|
||||||
handleError($e);
|
handleError($e);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,8 +34,9 @@ class ServerStatusJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
{
|
{
|
||||||
ray("checking server status for {$this->server->id}");
|
ray("checking server status for {$this->server->id}");
|
||||||
try {
|
try {
|
||||||
$this->server->checkServerRediness();
|
if ($this->server->isServerReady()) {
|
||||||
$this->cleanup(notify: false);
|
$this->cleanup(notify: false);
|
||||||
|
}
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
send_internal_notification('ServerStatusJob failed with: ' . $e->getMessage());
|
send_internal_notification('ServerStatusJob failed with: ' . $e->getMessage());
|
||||||
ray($e->getMessage());
|
ray($e->getMessage());
|
||||||
|
|||||||
@@ -300,6 +300,9 @@ class Application extends BaseModel
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
public function isLogDrainEnabled() {
|
||||||
|
return data_get($this, 'settings.is_log_drain_enabled', false);
|
||||||
|
}
|
||||||
public function isConfigurationChanged($save = false)
|
public function isConfigurationChanged($save = false)
|
||||||
{
|
{
|
||||||
$newConfigHash = $this->fqdn . $this->git_repository . $this->git_branch . $this->git_commit_sha . $this->build_pack . $this->static_image . $this->install_command . $this->build_command . $this->start_command . $this->port_exposes . $this->port_mappings . $this->base_directory . $this->publish_directory . $this->health_check_path . $this->health_check_port . $this->health_check_host . $this->health_check_method . $this->health_check_return_code . $this->health_check_scheme . $this->health_check_response_text . $this->health_check_interval . $this->health_check_timeout . $this->health_check_retries . $this->health_check_start_period . $this->health_check_enabled . $this->limits_memory . $this->limits_swap . $this->limits_swappiness . $this->limits_reservation . $this->limits_cpus . $this->limits_cpuset . $this->limits_cpu_shares . $this->dockerfile . $this->dockerfile_location . $this->custom_labels;
|
$newConfigHash = $this->fqdn . $this->git_repository . $this->git_branch . $this->git_commit_sha . $this->build_pack . $this->static_image . $this->install_command . $this->build_command . $this->start_command . $this->port_exposes . $this->port_mappings . $this->base_directory . $this->publish_directory . $this->health_check_path . $this->health_check_port . $this->health_check_host . $this->health_check_method . $this->health_check_return_code . $this->health_check_scheme . $this->health_check_response_text . $this->health_check_interval . $this->health_check_timeout . $this->health_check_retries . $this->health_check_start_period . $this->health_check_enabled . $this->limits_memory . $this->limits_swap . $this->limits_swappiness . $this->limits_reservation . $this->limits_cpus . $this->limits_cpuset . $this->limits_cpu_shares . $this->dockerfile . $this->dockerfile_location . $this->custom_labels;
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
|
use App\Actions\Server\InstallLogDrain;
|
||||||
|
use App\Actions\Server\InstallNewRelic;
|
||||||
use App\Enums\ProxyStatus;
|
use App\Enums\ProxyStatus;
|
||||||
use App\Enums\ProxyTypes;
|
use App\Enums\ProxyTypes;
|
||||||
use App\Notifications\Server\Revived;
|
use App\Notifications\Server\Revived;
|
||||||
@@ -59,6 +61,8 @@ class Server extends BaseModel
|
|||||||
|
|
||||||
public $casts = [
|
public $casts = [
|
||||||
'proxy' => SchemalessAttributes::class,
|
'proxy' => SchemalessAttributes::class,
|
||||||
|
'logdrain_axiom_api_key' => 'encrypted',
|
||||||
|
'logdrain_newrelic_license_key' => 'encrypted',
|
||||||
];
|
];
|
||||||
protected $schemalessAttributes = [
|
protected $schemalessAttributes = [
|
||||||
'proxy',
|
'proxy',
|
||||||
@@ -116,15 +120,25 @@ class Server extends BaseModel
|
|||||||
{
|
{
|
||||||
return $this->ip === 'host.docker.internal' || $this->id === 0;
|
return $this->ip === 'host.docker.internal' || $this->id === 0;
|
||||||
}
|
}
|
||||||
public function checkServerRediness()
|
public function skipServer()
|
||||||
|
{
|
||||||
|
if ($this->ip === '1.2.3.4') {
|
||||||
|
ray('skipping 1.2.3.4');
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
public function isServerReady()
|
||||||
{
|
{
|
||||||
$serverUptimeCheckNumber = $this->unreachable_count;
|
$serverUptimeCheckNumber = $this->unreachable_count;
|
||||||
$serverUptimeCheckNumberMax = 5;
|
$serverUptimeCheckNumberMax = 3;
|
||||||
|
|
||||||
$currentTime = now()->timestamp;
|
$currentTime = now()->timestamp;
|
||||||
$runtime5Minutes = 1 * 60;
|
$runtime = 30;
|
||||||
// Run for 1 minutes max and check every 5 seconds
|
|
||||||
while ($currentTime + $runtime5Minutes > now()->timestamp) {
|
$isReady = false;
|
||||||
|
// Run for 30 seconds max and check every 5 seconds for 3 times
|
||||||
|
while ($currentTime + $runtime > now()->timestamp) {
|
||||||
if ($serverUptimeCheckNumber >= $serverUptimeCheckNumberMax) {
|
if ($serverUptimeCheckNumber >= $serverUptimeCheckNumberMax) {
|
||||||
if ($this->unreachable_notification_sent === false) {
|
if ($this->unreachable_notification_sent === false) {
|
||||||
ray('Server unreachable, sending notification...');
|
ray('Server unreachable, sending notification...');
|
||||||
@@ -153,7 +167,8 @@ class Server extends BaseModel
|
|||||||
$db->update(['status' => 'exited']);
|
$db->update(['status' => 'exited']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw new \Exception('Server is not reachable.');
|
$isReady = false;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
$result = $this->validateConnection();
|
$result = $this->validateConnection();
|
||||||
ray('validateConnection: ' . $result);
|
ray('validateConnection: ' . $result);
|
||||||
@@ -165,25 +180,10 @@ class Server extends BaseModel
|
|||||||
Sleep::for(5)->seconds();
|
Sleep::for(5)->seconds();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$this->update([
|
$isReady = true;
|
||||||
'unreachable_count' => 0,
|
|
||||||
]);
|
|
||||||
if (data_get($this, 'unreachable_notification_sent') === true) {
|
|
||||||
ray('Server is reachable again, sending notification...');
|
|
||||||
$this->team->notify(new Revived($this));
|
|
||||||
$this->update(['unreachable_notification_sent' => false]);
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
data_get($this, 'settings.is_reachable') === false ||
|
|
||||||
data_get($this, 'settings.is_usable') === false
|
|
||||||
) {
|
|
||||||
$this->settings()->update([
|
|
||||||
'is_reachable' => true,
|
|
||||||
'is_usable' => true
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
return $isReady;
|
||||||
}
|
}
|
||||||
public function getDiskUsage()
|
public function getDiskUsage()
|
||||||
{
|
{
|
||||||
@@ -300,16 +300,41 @@ class Server extends BaseModel
|
|||||||
{
|
{
|
||||||
return $this->settings->is_reachable && $this->settings->is_usable;
|
return $this->settings->is_reachable && $this->settings->is_usable;
|
||||||
}
|
}
|
||||||
|
public function isLogDrainEnabled()
|
||||||
|
{
|
||||||
|
return $this->settings->is_logdrain_newrelic_enabled || $this->settings->is_logdrain_highlight_enabled || $this->settings->is_logdrain_axiom_enabled;
|
||||||
|
}
|
||||||
public function validateConnection()
|
public function validateConnection()
|
||||||
{
|
{
|
||||||
$uptime = instant_remote_process(['uptime'], $this, false);
|
if ($this->skipServer()) {
|
||||||
if (!$uptime) {
|
|
||||||
$this->settings->is_reachable = false;
|
|
||||||
$this->settings->save();
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
$this->settings->is_reachable = true;
|
|
||||||
$this->settings->save();
|
$uptime = instant_remote_process(['uptime'], $this, false);
|
||||||
|
if (!$uptime) {
|
||||||
|
$this->settings()->update([
|
||||||
|
'is_reachable' => false,
|
||||||
|
'is_usable' => false
|
||||||
|
]);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data_get($this, 'unreachable_notification_sent') === true) {
|
||||||
|
$this->team->notify(new Revived($this));
|
||||||
|
$this->update(['unreachable_notification_sent' => false]);
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
data_get($this, 'settings.is_reachable') === false ||
|
||||||
|
data_get($this, 'settings.is_usable') === false
|
||||||
|
) {
|
||||||
|
$this->settings()->update([
|
||||||
|
'is_reachable' => true,
|
||||||
|
'is_usable' => true
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
$this->update([
|
||||||
|
'unreachable_count' => 0,
|
||||||
|
]);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
public function validateDockerEngine($throwError = false)
|
public function validateDockerEngine($throwError = false)
|
||||||
|
|||||||
@@ -797,6 +797,16 @@ class Service extends BaseModel
|
|||||||
$serviceLabels = $serviceLabels->merge(fqdnLabelsForTraefik($this->uuid, $fqdns, true));
|
$serviceLabels = $serviceLabels->merge(fqdnLabelsForTraefik($this->uuid, $fqdns, true));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if ($this->server->isLogDrainEnabled() && $savedService->isLogDrainEnabled()) {
|
||||||
|
data_set($service, 'logging', [
|
||||||
|
'driver' => 'fluentd',
|
||||||
|
'options' => [
|
||||||
|
'fluentd-address' => "tcp://127.0.0.1:24224",
|
||||||
|
'fluentd-async' => "true",
|
||||||
|
'fluentd-sub-second-precision' => "true",
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
}
|
||||||
data_set($service, 'labels', $serviceLabels->toArray());
|
data_set($service, 'labels', $serviceLabels->toArray());
|
||||||
data_forget($service, 'is_database');
|
data_forget($service, 'is_database');
|
||||||
data_set($service, 'restart', RESTART_MODE);
|
data_set($service, 'restart', RESTART_MODE);
|
||||||
|
|||||||
@@ -18,6 +18,10 @@ class ServiceApplication extends BaseModel
|
|||||||
$service->fileStorages()->delete();
|
$service->fileStorages()->delete();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
public function isLogDrainEnabled()
|
||||||
|
{
|
||||||
|
return data_get($this, 'is_log_drain_enabled', false);
|
||||||
|
}
|
||||||
public function type()
|
public function type()
|
||||||
{
|
{
|
||||||
return 'service';
|
return 'service';
|
||||||
|
|||||||
@@ -16,6 +16,10 @@ class ServiceDatabase extends BaseModel
|
|||||||
$service->fileStorages()->delete();
|
$service->fileStorages()->delete();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
public function isLogDrainEnabled()
|
||||||
|
{
|
||||||
|
return data_get($this, 'is_log_drain_enabled', false);
|
||||||
|
}
|
||||||
public function type()
|
public function type()
|
||||||
{
|
{
|
||||||
return 'service';
|
return 'service';
|
||||||
|
|||||||
@@ -41,6 +41,10 @@ class StandaloneMariadb extends BaseModel
|
|||||||
$database->environment_variables()->delete();
|
$database->environment_variables()->delete();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
public function isLogDrainEnabled()
|
||||||
|
{
|
||||||
|
return data_get($this, 'is_log_drain_enabled', false);
|
||||||
|
}
|
||||||
public function type(): string
|
public function type(): string
|
||||||
{
|
{
|
||||||
return 'standalone-mariadb';
|
return 'standalone-mariadb';
|
||||||
|
|||||||
@@ -44,7 +44,10 @@ class StandaloneMongodb extends BaseModel
|
|||||||
$database->environment_variables()->delete();
|
$database->environment_variables()->delete();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
public function isLogDrainEnabled()
|
||||||
|
{
|
||||||
|
return data_get($this, 'is_log_drain_enabled', false);
|
||||||
|
}
|
||||||
public function mongoInitdbRootPassword(): Attribute
|
public function mongoInitdbRootPassword(): Attribute
|
||||||
{
|
{
|
||||||
return Attribute::make(
|
return Attribute::make(
|
||||||
|
|||||||
@@ -46,6 +46,11 @@ class StandaloneMysql extends BaseModel
|
|||||||
return 'standalone-mysql';
|
return 'standalone-mysql';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function isLogDrainEnabled()
|
||||||
|
{
|
||||||
|
return data_get($this, 'is_log_drain_enabled', false);
|
||||||
|
}
|
||||||
|
|
||||||
public function portsMappings(): Attribute
|
public function portsMappings(): Attribute
|
||||||
{
|
{
|
||||||
return Attribute::make(
|
return Attribute::make(
|
||||||
|
|||||||
@@ -42,6 +42,11 @@ class StandalonePostgresql extends BaseModel
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function isLogDrainEnabled()
|
||||||
|
{
|
||||||
|
return data_get($this, 'is_log_drain_enabled', false);
|
||||||
|
}
|
||||||
|
|
||||||
public function portsMappings(): Attribute
|
public function portsMappings(): Attribute
|
||||||
{
|
{
|
||||||
return Attribute::make(
|
return Attribute::make(
|
||||||
|
|||||||
@@ -37,6 +37,11 @@ class StandaloneRedis extends BaseModel
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function isLogDrainEnabled()
|
||||||
|
{
|
||||||
|
return data_get($this, 'is_log_drain_enabled', false);
|
||||||
|
}
|
||||||
|
|
||||||
public function portsMappings(): Attribute
|
public function portsMappings(): Attribute
|
||||||
{
|
{
|
||||||
return Attribute::make(
|
return Attribute::make(
|
||||||
|
|||||||
@@ -52,7 +52,6 @@ class User extends Authenticatable implements SendsEmail
|
|||||||
}
|
}
|
||||||
public function createToken(string $name, array $abilities = ['*'], DateTimeInterface $expiresAt = null)
|
public function createToken(string $name, array $abilities = ['*'], DateTimeInterface $expiresAt = null)
|
||||||
{
|
{
|
||||||
ray('asd');
|
|
||||||
$plainTextToken = sprintf(
|
$plainTextToken = sprintf(
|
||||||
'%s%s%s',
|
'%s%s%s',
|
||||||
config('sanctum.token_prefix', ''),
|
config('sanctum.token_prefix', ''),
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ class ContainerRestarted extends Notification implements ShouldQueue
|
|||||||
public function toMail(): MailMessage
|
public function toMail(): MailMessage
|
||||||
{
|
{
|
||||||
$mail = new MailMessage();
|
$mail = new MailMessage();
|
||||||
$mail->subject("Coolify: Container ({$this->name}) has been restarted automatically on {$this->server->name}");
|
$mail->subject("Coolify: A service ({$this->name}) has been restarted automatically on {$this->server->name}");
|
||||||
$mail->view('emails.container-restarted', [
|
$mail->view('emails.container-restarted', [
|
||||||
'containerName' => $this->name,
|
'containerName' => $this->name,
|
||||||
'serverName' => $this->server->name,
|
'serverName' => $this->server->name,
|
||||||
@@ -38,12 +38,12 @@ class ContainerRestarted extends Notification implements ShouldQueue
|
|||||||
|
|
||||||
public function toDiscord(): string
|
public function toDiscord(): string
|
||||||
{
|
{
|
||||||
$message = "Coolify: Container ({$this->name}) has been restarted automatically on {$this->server->name}";
|
$message = "Coolify: A service ({$this->name}) has been restarted automatically on {$this->server->name}";
|
||||||
return $message;
|
return $message;
|
||||||
}
|
}
|
||||||
public function toTelegram(): array
|
public function toTelegram(): array
|
||||||
{
|
{
|
||||||
$message = "Coolify: Container ({$this->name}) has been restarted automatically on {$this->server->name}";
|
$message = "Coolify: A service ({$this->name}) has been restarted automatically on {$this->server->name}";
|
||||||
$payload = [
|
$payload = [
|
||||||
"message" => $message,
|
"message" => $message,
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ class ContainerStopped extends Notification implements ShouldQueue
|
|||||||
public function toMail(): MailMessage
|
public function toMail(): MailMessage
|
||||||
{
|
{
|
||||||
$mail = new MailMessage();
|
$mail = new MailMessage();
|
||||||
$mail->subject("Coolify: Container ({$this->name}) has been stopped on {$this->server->name}");
|
$mail->subject("Coolify: A service ({$this->name}) has been stopped on {$this->server->name}");
|
||||||
$mail->view('emails.container-stopped', [
|
$mail->view('emails.container-stopped', [
|
||||||
'containerName' => $this->name,
|
'containerName' => $this->name,
|
||||||
'serverName' => $this->server->name,
|
'serverName' => $this->server->name,
|
||||||
@@ -37,12 +37,12 @@ class ContainerStopped extends Notification implements ShouldQueue
|
|||||||
|
|
||||||
public function toDiscord(): string
|
public function toDiscord(): string
|
||||||
{
|
{
|
||||||
$message = "Coolify: Container ({$this->name}) has been stopped on {$this->server->name}";
|
$message = "Coolify: A service ({$this->name}) has been stopped on {$this->server->name}";
|
||||||
return $message;
|
return $message;
|
||||||
}
|
}
|
||||||
public function toTelegram(): array
|
public function toTelegram(): array
|
||||||
{
|
{
|
||||||
$message = "Coolify: Container ($this->name} has been stopped on {$this->server->name}";
|
$message = "Coolify: A service ($this->name} has been stopped on {$this->server->name}";
|
||||||
$payload = [
|
$payload = [
|
||||||
"message" => $message,
|
"message" => $message,
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ class Unreachable extends Notification implements ShouldQueue
|
|||||||
public function toMail(): MailMessage
|
public function toMail(): MailMessage
|
||||||
{
|
{
|
||||||
$mail = new MailMessage();
|
$mail = new MailMessage();
|
||||||
$mail->subject("Coolify: Server ({$this->server->name}) is unreachable after trying to connect to it 5 times");
|
$mail->subject("Coolify: Server ({$this->server->name}) is unreachable after trying to connect to it 3 times");
|
||||||
$mail->view('emails.server-lost-connection', [
|
$mail->view('emails.server-lost-connection', [
|
||||||
'name' => $this->server->name,
|
'name' => $this->server->name,
|
||||||
]);
|
]);
|
||||||
@@ -52,13 +52,13 @@ class Unreachable extends Notification implements ShouldQueue
|
|||||||
|
|
||||||
public function toDiscord(): string
|
public function toDiscord(): string
|
||||||
{
|
{
|
||||||
$message = "Coolify: Server '{$this->server->name}' is unreachable after trying to connect to it 5 times. All automations & integrations are turned off! Please check your server! IMPORTANT: We automatically try to revive your server. If your server is back online, we will automatically turn on all automations & integrations.";
|
$message = "Coolify: Server '{$this->server->name}' is unreachable after trying to connect to it 3 times. All automations & integrations are turned off! Please check your server! IMPORTANT: We automatically try to revive your server. If your server is back online, we will automatically turn on all automations & integrations.";
|
||||||
return $message;
|
return $message;
|
||||||
}
|
}
|
||||||
public function toTelegram(): array
|
public function toTelegram(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
"message" => "Coolify: Server '{$this->server->name}' is unreachable after trying to connect to it 5 times. All automations & integrations are turned off! Please check your server! IMPORTANT: We automatically try to revive your server. If your server is back online, we will automatically turn on all automations & integrations."
|
"message" => "Coolify: Server '{$this->server->name}' is unreachable after trying to connect to it 3 times. All automations & integrations are turned off! Please check your server! IMPORTANT: We automatically try to revive your server. If your server is back online, we will automatically turn on all automations & integrations."
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ use Visus\Cuid2\Cuid2;
|
|||||||
|
|
||||||
function getCurrentApplicationContainerStatus(Server $server, int $id, ?int $pullRequestId = null): Collection
|
function getCurrentApplicationContainerStatus(Server $server, int $id, ?int $pullRequestId = null): Collection
|
||||||
{
|
{
|
||||||
ray($id, $pullRequestId);
|
|
||||||
$containers = collect([]);
|
$containers = collect([]);
|
||||||
$containers = instant_remote_process(["docker ps -a --filter='label=coolify.applicationId={$id}' --format '{{json .}}' "], $server);
|
$containers = instant_remote_process(["docker ps -a --filter='label=coolify.applicationId={$id}' --format '{{json .}}' "], $server);
|
||||||
$containers = format_docker_command_output_to_json($containers);
|
$containers = format_docker_command_output_to_json($containers);
|
||||||
@@ -26,7 +25,6 @@ function getCurrentApplicationContainerStatus(Server $server, int $id, ?int $pul
|
|||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
$containers = $containers->filter();
|
$containers = $containers->filter();
|
||||||
ray($containers);
|
|
||||||
return $containers;
|
return $containers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ return [
|
|||||||
|
|
||||||
// The release version of your application
|
// The release version of your application
|
||||||
// Example with dynamic git hash: trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD'))
|
// Example with dynamic git hash: trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD'))
|
||||||
'release' => '4.0.0-beta.138',
|
'release' => '4.0.0-beta.144',
|
||||||
// When left empty or `null` the Laravel environment will be used
|
// When left empty or `null` the Laravel environment will be used
|
||||||
'environment' => config('app.env'),
|
'environment' => config('app.env'),
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
return '4.0.0-beta.138';
|
return '4.0.0-beta.144';
|
||||||
|
|||||||
47
database/migrations/2023_11_16_220647_add_log_drains.php
Normal file
47
database/migrations/2023_11_16_220647_add_log_drains.php
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::table('server_settings', function (Blueprint $table) {
|
||||||
|
$table->boolean('is_logdrain_newrelic_enabled')->default(false);
|
||||||
|
$table->string('logdrain_newrelic_license_key')->nullable();
|
||||||
|
$table->string('logdrain_newrelic_base_uri')->nullable();
|
||||||
|
|
||||||
|
$table->boolean('is_logdrain_highlight_enabled')->default(false);
|
||||||
|
$table->string('logdrain_highlight_project_id')->nullable();
|
||||||
|
|
||||||
|
$table->boolean('is_logdrain_axiom_enabled')->default(false);
|
||||||
|
$table->string('logdrain_axiom_dataset_name')->nullable();
|
||||||
|
$table->string('logdrain_axiom_api_key')->nullable();
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('server_settings', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('is_logdrain_newrelic_enabled');
|
||||||
|
$table->dropColumn('logdrain_newrelic_license_key');
|
||||||
|
$table->dropColumn('logdrain_newrelic_base_uri');
|
||||||
|
|
||||||
|
$table->dropColumn('is_logdrain_highlight_enabled');
|
||||||
|
$table->dropColumn('logdrain_highlight_project_id');
|
||||||
|
|
||||||
|
$table->dropColumn('is_logdrain_axiom_enabled');
|
||||||
|
$table->dropColumn('logdrain_axiom_dataset_name');
|
||||||
|
$table->dropColumn('logdrain_axiom_api_key');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::table('application_settings', function (Blueprint $table) {
|
||||||
|
$table->boolean('is_log_drain_enabled')->default(false);
|
||||||
|
});
|
||||||
|
Schema::table('standalone_redis', function (Blueprint $table) {
|
||||||
|
$table->boolean('is_log_drain_enabled')->default(false);
|
||||||
|
});
|
||||||
|
Schema::table('standalone_mysqls', function (Blueprint $table) {
|
||||||
|
$table->boolean('is_log_drain_enabled')->default(false);
|
||||||
|
});
|
||||||
|
Schema::table('standalone_mariadbs', function (Blueprint $table) {
|
||||||
|
$table->boolean('is_log_drain_enabled')->default(false);
|
||||||
|
});
|
||||||
|
Schema::table('standalone_postgresqls', function (Blueprint $table) {
|
||||||
|
$table->boolean('is_log_drain_enabled')->default(false);
|
||||||
|
});
|
||||||
|
Schema::table('standalone_mongodbs', function (Blueprint $table) {
|
||||||
|
$table->boolean('is_log_drain_enabled')->default(false);
|
||||||
|
});
|
||||||
|
Schema::table('service_applications', function (Blueprint $table) {
|
||||||
|
$table->boolean('is_log_drain_enabled')->default(false);
|
||||||
|
});
|
||||||
|
Schema::table('service_databases', function (Blueprint $table) {
|
||||||
|
$table->boolean('is_log_drain_enabled')->default(false);
|
||||||
|
});
|
||||||
|
Schema::table('servers', function (Blueprint $table) {
|
||||||
|
$table->boolean('log_drain_notification_sent')->default(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('application_settings', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('is_log_drain_enabled');
|
||||||
|
});
|
||||||
|
Schema::table('standalone_redis', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('is_log_drain_enabled');
|
||||||
|
});
|
||||||
|
Schema::table('standalone_mysqls', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('is_log_drain_enabled');
|
||||||
|
});
|
||||||
|
Schema::table('standalone_mariadbs', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('is_log_drain_enabled');
|
||||||
|
});
|
||||||
|
Schema::table('standalone_postgresqls', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('is_log_drain_enabled');
|
||||||
|
});
|
||||||
|
Schema::table('standalone_mongodbs', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('is_log_drain_enabled');
|
||||||
|
});
|
||||||
|
Schema::table('service_applications', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('is_log_drain_enabled');
|
||||||
|
});
|
||||||
|
Schema::table('service_databases', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('is_log_drain_enabled');
|
||||||
|
});
|
||||||
|
Schema::table('servers', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('log_drain_notification_sent');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
[SERVICE]
|
|
||||||
Flush 1
|
|
||||||
Daemon off
|
|
||||||
[INPUT]
|
|
||||||
Name forward
|
|
||||||
Buffer_Chunk_Size 1M
|
|
||||||
Buffer_Max_Size 6M
|
|
||||||
# [OUTPUT]
|
|
||||||
# Name nrlogs
|
|
||||||
# Match *
|
|
||||||
# license_key ${LICENSE_KEY}
|
|
||||||
# base_uri https://log-api.eu.newrelic.com/log/v1
|
|
||||||
|
|
||||||
[OUTPUT]
|
|
||||||
Name stdout
|
|
||||||
Match *
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
version: '3'
|
|
||||||
services:
|
|
||||||
coolify-fluent-bit:
|
|
||||||
image: cr.fluentbit.io/fluent/fluent-bit:2.0
|
|
||||||
command: -c /fluent-bit.conf
|
|
||||||
volumes:
|
|
||||||
- ./fluent-bit.conf:/fluent-bit.conf
|
|
||||||
ports:
|
|
||||||
- 24224:24224
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
version: '3'
|
|
||||||
services:
|
|
||||||
newrelic-infra:
|
|
||||||
container_name: newrelic-infra
|
|
||||||
image: newrelic/infrastructure:latest
|
|
||||||
networks:
|
|
||||||
- coolify
|
|
||||||
cap_add:
|
|
||||||
- SYS_PTRACE
|
|
||||||
privileged: true
|
|
||||||
pid: host
|
|
||||||
volumes:
|
|
||||||
- "/:/host:ro"
|
|
||||||
- "/var/run/docker.sock:/var/run/docker.sock"
|
|
||||||
- "newrelic-infra:/etc/newrelic-infra"
|
|
||||||
environment:
|
|
||||||
- NRIA_LICENSE_KEY=${NRIA_LICENSE_KEY}
|
|
||||||
- NRIA_DISPLAY_NAME=${HOSTNAME}
|
|
||||||
|
|
||||||
networks:
|
|
||||||
coolify:
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
receivers:
|
|
||||||
hostmetrics:
|
|
||||||
collection_interval: 5s
|
|
||||||
scrapers:
|
|
||||||
cpu:
|
|
||||||
metrics:
|
|
||||||
system.cpu.utilization:
|
|
||||||
enabled: true
|
|
||||||
processors:
|
|
||||||
resourcedetection:
|
|
||||||
detectors: [env, system]
|
|
||||||
system:
|
|
||||||
hostname_sources: ["os"]
|
|
||||||
resource_attributes:
|
|
||||||
host.id:
|
|
||||||
enabled: true
|
|
||||||
batch:
|
|
||||||
memory_limiter:
|
|
||||||
check_interval: 1s
|
|
||||||
limit_mib: 1000
|
|
||||||
spike_limit_mib: 200
|
|
||||||
exporters:
|
|
||||||
debug:
|
|
||||||
verbosity: detailed
|
|
||||||
otlp:
|
|
||||||
endpoint: ${OTLP_ENDPOINT}
|
|
||||||
headers:
|
|
||||||
api-key: ${OTLP_API_KEY}
|
|
||||||
service:
|
|
||||||
pipelines:
|
|
||||||
metrics:
|
|
||||||
receivers: [hostmetrics]
|
|
||||||
processors: [memory_limiter, resourcedetection, batch]
|
|
||||||
exporters: [debug, otlp]
|
|
||||||
265
package-lock.json
generated
265
package-lock.json
generated
@@ -6,19 +6,19 @@
|
|||||||
"": {
|
"": {
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@tailwindcss/typography": "0.5.10",
|
"@tailwindcss/typography": "0.5.10",
|
||||||
"alpinejs": "3.13.1",
|
"alpinejs": "3.13.2",
|
||||||
"daisyui": "4.0.3",
|
"daisyui": "4.3.1",
|
||||||
"tailwindcss-scrollbar": "0.1.0"
|
"tailwindcss-scrollbar": "0.1.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vitejs/plugin-vue": "4.4.0",
|
"@vitejs/plugin-vue": "4.5.0",
|
||||||
"autoprefixer": "10.4.16",
|
"autoprefixer": "10.4.16",
|
||||||
"axios": "1.5.1",
|
"axios": "1.6.2",
|
||||||
"laravel-vite-plugin": "0.8.1",
|
"laravel-vite-plugin": "0.8.1",
|
||||||
"postcss": "8.4.31",
|
"postcss": "8.4.31",
|
||||||
"tailwindcss": "3.3.5",
|
"tailwindcss": "3.3.5",
|
||||||
"vite": "4.4.11",
|
"vite": "4.5.0",
|
||||||
"vue": "3.3.4"
|
"vue": "3.3.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@alloc/quick-lru": {
|
"node_modules/@alloc/quick-lru": {
|
||||||
@@ -33,9 +33,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/parser": {
|
"node_modules/@babel/parser": {
|
||||||
"version": "7.22.4",
|
"version": "7.23.3",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.4.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.3.tgz",
|
||||||
"integrity": "sha512-VLLsx06XkEYqBtE5YGPwfSGwfrjnyPP5oiGty3S8pQLFDFLaS8VwWSIxkTXpcvr5zeYLE6+MBNl2npl/YnfofA==",
|
"integrity": "sha512-uVsWNvlVsIninV2prNz/3lHCb+5CJ+e+IUBfbjToAHODtfGYLfCFuY4AU7TskI+dAKk+njsPiBjq1gKTvZOBaw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"parser": "bin/babel-parser.js"
|
"parser": "bin/babel-parser.js"
|
||||||
@@ -503,90 +503,90 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vitejs/plugin-vue": {
|
"node_modules/@vitejs/plugin-vue": {
|
||||||
"version": "4.4.0",
|
"version": "4.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-4.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-4.5.0.tgz",
|
||||||
"integrity": "sha512-xdguqb+VUwiRpSg+nsc2HtbAUSGak25DXYvpQQi4RVU1Xq1uworyoH/md9Rfd8zMmPR/pSghr309QNcftUVseg==",
|
"integrity": "sha512-a2WSpP8X8HTEww/U00bU4mX1QpLINNuz/2KMNpLsdu3BzOpak3AGI1CJYBTXcc4SPhaD0eNRUp7IyQK405L5dQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^14.18.0 || >=16.0.0"
|
"node": "^14.18.0 || >=16.0.0"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"vite": "^4.0.0",
|
"vite": "^4.0.0 || ^5.0.0",
|
||||||
"vue": "^3.2.25"
|
"vue": "^3.2.25"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vue/compiler-core": {
|
"node_modules/@vue/compiler-core": {
|
||||||
"version": "3.3.4",
|
"version": "3.3.8",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.3.4.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.3.8.tgz",
|
||||||
"integrity": "sha512-cquyDNvZ6jTbf/+x+AgM2Arrp6G4Dzbb0R64jiG804HRMfRiFXWI6kqUVqZ6ZR0bQhIoQjB4+2bhNtVwndW15g==",
|
"integrity": "sha512-hN/NNBUECw8SusQvDSqqcVv6gWq8L6iAktUR0UF3vGu2OhzRqcOiAno0FmBJWwxhYEXRlQJT5XnoKsVq1WZx4g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/parser": "^7.21.3",
|
"@babel/parser": "^7.23.0",
|
||||||
"@vue/shared": "3.3.4",
|
"@vue/shared": "3.3.8",
|
||||||
"estree-walker": "^2.0.2",
|
"estree-walker": "^2.0.2",
|
||||||
"source-map-js": "^1.0.2"
|
"source-map-js": "^1.0.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vue/compiler-core/node_modules/@vue/shared": {
|
"node_modules/@vue/compiler-core/node_modules/@vue/shared": {
|
||||||
"version": "3.3.4",
|
"version": "3.3.8",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.3.4.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.3.8.tgz",
|
||||||
"integrity": "sha512-7OjdcV8vQ74eiz1TZLzZP4JwqM5fA94K6yntPS5Z25r9HDuGNzaGdgvwKYq6S+MxwF0TFRwe50fIR/MYnakdkQ==",
|
"integrity": "sha512-8PGwybFwM4x8pcfgqEQFy70NaQxASvOC5DJwLQfpArw1UDfUXrJkdxD3BhVTMS+0Lef/TU7YO0Jvr0jJY8T+mw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/@vue/compiler-dom": {
|
"node_modules/@vue/compiler-dom": {
|
||||||
"version": "3.3.4",
|
"version": "3.3.8",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.3.4.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.3.8.tgz",
|
||||||
"integrity": "sha512-wyM+OjOVpuUukIq6p5+nwHYtj9cFroz9cwkfmP9O1nzH68BenTTv0u7/ndggT8cIQlnBeOo6sUT/gvHcIkLA5w==",
|
"integrity": "sha512-+PPtv+p/nWDd0AvJu3w8HS0RIm/C6VGBIRe24b9hSyNWOAPEUosFZ5diwawwP8ip5sJ8n0Pe87TNNNHnvjs0FQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vue/compiler-core": "3.3.4",
|
"@vue/compiler-core": "3.3.8",
|
||||||
"@vue/shared": "3.3.4"
|
"@vue/shared": "3.3.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vue/compiler-dom/node_modules/@vue/shared": {
|
"node_modules/@vue/compiler-dom/node_modules/@vue/shared": {
|
||||||
"version": "3.3.4",
|
"version": "3.3.8",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.3.4.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.3.8.tgz",
|
||||||
"integrity": "sha512-7OjdcV8vQ74eiz1TZLzZP4JwqM5fA94K6yntPS5Z25r9HDuGNzaGdgvwKYq6S+MxwF0TFRwe50fIR/MYnakdkQ==",
|
"integrity": "sha512-8PGwybFwM4x8pcfgqEQFy70NaQxASvOC5DJwLQfpArw1UDfUXrJkdxD3BhVTMS+0Lef/TU7YO0Jvr0jJY8T+mw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/@vue/compiler-sfc": {
|
"node_modules/@vue/compiler-sfc": {
|
||||||
"version": "3.3.4",
|
"version": "3.3.8",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.3.4.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.3.8.tgz",
|
||||||
"integrity": "sha512-6y/d8uw+5TkCuzBkgLS0v3lSM3hJDntFEiUORM11pQ/hKvkhSKZrXW6i69UyXlJQisJxuUEJKAWEqWbWsLeNKQ==",
|
"integrity": "sha512-WMzbUrlTjfYF8joyT84HfwwXo+8WPALuPxhy+BZ6R4Aafls+jDBnSz8PDz60uFhuqFbl3HxRfxvDzrUf3THwpA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/parser": "^7.20.15",
|
"@babel/parser": "^7.23.0",
|
||||||
"@vue/compiler-core": "3.3.4",
|
"@vue/compiler-core": "3.3.8",
|
||||||
"@vue/compiler-dom": "3.3.4",
|
"@vue/compiler-dom": "3.3.8",
|
||||||
"@vue/compiler-ssr": "3.3.4",
|
"@vue/compiler-ssr": "3.3.8",
|
||||||
"@vue/reactivity-transform": "3.3.4",
|
"@vue/reactivity-transform": "3.3.8",
|
||||||
"@vue/shared": "3.3.4",
|
"@vue/shared": "3.3.8",
|
||||||
"estree-walker": "^2.0.2",
|
"estree-walker": "^2.0.2",
|
||||||
"magic-string": "^0.30.0",
|
"magic-string": "^0.30.5",
|
||||||
"postcss": "^8.1.10",
|
"postcss": "^8.4.31",
|
||||||
"source-map-js": "^1.0.2"
|
"source-map-js": "^1.0.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vue/compiler-sfc/node_modules/@vue/shared": {
|
"node_modules/@vue/compiler-sfc/node_modules/@vue/shared": {
|
||||||
"version": "3.3.4",
|
"version": "3.3.8",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.3.4.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.3.8.tgz",
|
||||||
"integrity": "sha512-7OjdcV8vQ74eiz1TZLzZP4JwqM5fA94K6yntPS5Z25r9HDuGNzaGdgvwKYq6S+MxwF0TFRwe50fIR/MYnakdkQ==",
|
"integrity": "sha512-8PGwybFwM4x8pcfgqEQFy70NaQxASvOC5DJwLQfpArw1UDfUXrJkdxD3BhVTMS+0Lef/TU7YO0Jvr0jJY8T+mw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/@vue/compiler-ssr": {
|
"node_modules/@vue/compiler-ssr": {
|
||||||
"version": "3.3.4",
|
"version": "3.3.8",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.3.4.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.3.8.tgz",
|
||||||
"integrity": "sha512-m0v6oKpup2nMSehwA6Uuu+j+wEwcy7QmwMkVNVfrV9P2qE5KshC6RwOCq8fjGS/Eak/uNb8AaWekfiXxbBB6gQ==",
|
"integrity": "sha512-hXCqQL/15kMVDBuoBYpUnSYT8doDNwsjvm3jTefnXr+ytn294ySnT8NlsFHmTgKNjwpuFy7XVV8yTeLtNl/P6w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vue/compiler-dom": "3.3.4",
|
"@vue/compiler-dom": "3.3.8",
|
||||||
"@vue/shared": "3.3.4"
|
"@vue/shared": "3.3.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vue/compiler-ssr/node_modules/@vue/shared": {
|
"node_modules/@vue/compiler-ssr/node_modules/@vue/shared": {
|
||||||
"version": "3.3.4",
|
"version": "3.3.8",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.3.4.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.3.8.tgz",
|
||||||
"integrity": "sha512-7OjdcV8vQ74eiz1TZLzZP4JwqM5fA94K6yntPS5Z25r9HDuGNzaGdgvwKYq6S+MxwF0TFRwe50fIR/MYnakdkQ==",
|
"integrity": "sha512-8PGwybFwM4x8pcfgqEQFy70NaQxASvOC5DJwLQfpArw1UDfUXrJkdxD3BhVTMS+0Lef/TU7YO0Jvr0jJY8T+mw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/@vue/reactivity": {
|
"node_modules/@vue/reactivity": {
|
||||||
@@ -598,83 +598,83 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vue/reactivity-transform": {
|
"node_modules/@vue/reactivity-transform": {
|
||||||
"version": "3.3.4",
|
"version": "3.3.8",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.3.4.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.3.8.tgz",
|
||||||
"integrity": "sha512-MXgwjako4nu5WFLAjpBnCj/ieqcjE2aJBINUNQzkZQfzIZA4xn+0fV1tIYBJvvva3N3OvKGofRLvQIwEQPpaXw==",
|
"integrity": "sha512-49CvBzmZNtcHua0XJ7GdGifM8GOXoUMOX4dD40Y5DxI3R8OUhMlvf2nvgUAcPxaXiV5MQQ1Nwy09ADpnLQUqRw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/parser": "^7.20.15",
|
"@babel/parser": "^7.23.0",
|
||||||
"@vue/compiler-core": "3.3.4",
|
"@vue/compiler-core": "3.3.8",
|
||||||
"@vue/shared": "3.3.4",
|
"@vue/shared": "3.3.8",
|
||||||
"estree-walker": "^2.0.2",
|
"estree-walker": "^2.0.2",
|
||||||
"magic-string": "^0.30.0"
|
"magic-string": "^0.30.5"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vue/reactivity-transform/node_modules/@vue/shared": {
|
"node_modules/@vue/reactivity-transform/node_modules/@vue/shared": {
|
||||||
"version": "3.3.4",
|
"version": "3.3.8",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.3.4.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.3.8.tgz",
|
||||||
"integrity": "sha512-7OjdcV8vQ74eiz1TZLzZP4JwqM5fA94K6yntPS5Z25r9HDuGNzaGdgvwKYq6S+MxwF0TFRwe50fIR/MYnakdkQ==",
|
"integrity": "sha512-8PGwybFwM4x8pcfgqEQFy70NaQxASvOC5DJwLQfpArw1UDfUXrJkdxD3BhVTMS+0Lef/TU7YO0Jvr0jJY8T+mw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/@vue/runtime-core": {
|
"node_modules/@vue/runtime-core": {
|
||||||
"version": "3.3.4",
|
"version": "3.3.8",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.3.4.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.3.8.tgz",
|
||||||
"integrity": "sha512-R+bqxMN6pWO7zGI4OMlmvePOdP2c93GsHFM/siJI7O2nxFRzj55pLwkpCedEY+bTMgp5miZ8CxfIZo3S+gFqvA==",
|
"integrity": "sha512-qurzOlb6q26KWQ/8IShHkMDOuJkQnQcTIp1sdP4I9MbCf9FJeGVRXJFr2mF+6bXh/3Zjr9TDgURXrsCr9bfjUw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vue/reactivity": "3.3.4",
|
"@vue/reactivity": "3.3.8",
|
||||||
"@vue/shared": "3.3.4"
|
"@vue/shared": "3.3.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vue/runtime-core/node_modules/@vue/reactivity": {
|
"node_modules/@vue/runtime-core/node_modules/@vue/reactivity": {
|
||||||
"version": "3.3.4",
|
"version": "3.3.8",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.3.4.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.3.8.tgz",
|
||||||
"integrity": "sha512-kLTDLwd0B1jG08NBF3R5rqULtv/f8x3rOFByTDz4J53ttIQEDmALqKqXY0J+XQeN0aV2FBxY8nJDf88yvOPAqQ==",
|
"integrity": "sha512-ctLWitmFBu6mtddPyOKpHg8+5ahouoTCRtmAHZAXmolDtuZXfjL2T3OJ6DL6ezBPQB1SmMnpzjiWjCiMYmpIuw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vue/shared": "3.3.4"
|
"@vue/shared": "3.3.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vue/runtime-core/node_modules/@vue/shared": {
|
"node_modules/@vue/runtime-core/node_modules/@vue/shared": {
|
||||||
"version": "3.3.4",
|
"version": "3.3.8",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.3.4.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.3.8.tgz",
|
||||||
"integrity": "sha512-7OjdcV8vQ74eiz1TZLzZP4JwqM5fA94K6yntPS5Z25r9HDuGNzaGdgvwKYq6S+MxwF0TFRwe50fIR/MYnakdkQ==",
|
"integrity": "sha512-8PGwybFwM4x8pcfgqEQFy70NaQxASvOC5DJwLQfpArw1UDfUXrJkdxD3BhVTMS+0Lef/TU7YO0Jvr0jJY8T+mw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/@vue/runtime-dom": {
|
"node_modules/@vue/runtime-dom": {
|
||||||
"version": "3.3.4",
|
"version": "3.3.8",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.3.4.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.3.8.tgz",
|
||||||
"integrity": "sha512-Aj5bTJ3u5sFsUckRghsNjVTtxZQ1OyMWCr5dZRAPijF/0Vy4xEoRCwLyHXcj4D0UFbJ4lbx3gPTgg06K/GnPnQ==",
|
"integrity": "sha512-Noy5yM5UIf9UeFoowBVgghyGGPIDPy1Qlqt0yVsUdAVbqI8eeMSsTqBtauaEoT2UFXUk5S64aWVNJN4MJ2vRdA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vue/runtime-core": "3.3.4",
|
"@vue/runtime-core": "3.3.8",
|
||||||
"@vue/shared": "3.3.4",
|
"@vue/shared": "3.3.8",
|
||||||
"csstype": "^3.1.1"
|
"csstype": "^3.1.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vue/runtime-dom/node_modules/@vue/shared": {
|
"node_modules/@vue/runtime-dom/node_modules/@vue/shared": {
|
||||||
"version": "3.3.4",
|
"version": "3.3.8",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.3.4.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.3.8.tgz",
|
||||||
"integrity": "sha512-7OjdcV8vQ74eiz1TZLzZP4JwqM5fA94K6yntPS5Z25r9HDuGNzaGdgvwKYq6S+MxwF0TFRwe50fIR/MYnakdkQ==",
|
"integrity": "sha512-8PGwybFwM4x8pcfgqEQFy70NaQxASvOC5DJwLQfpArw1UDfUXrJkdxD3BhVTMS+0Lef/TU7YO0Jvr0jJY8T+mw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/@vue/server-renderer": {
|
"node_modules/@vue/server-renderer": {
|
||||||
"version": "3.3.4",
|
"version": "3.3.8",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.3.4.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.3.8.tgz",
|
||||||
"integrity": "sha512-Q6jDDzR23ViIb67v+vM1Dqntu+HUexQcsWKhhQa4ARVzxOY2HbC7QRW/ggkDBd5BU+uM1sV6XOAP0b216o34JQ==",
|
"integrity": "sha512-zVCUw7RFskvPuNlPn/8xISbrf0zTWsTSdYTsUTN1ERGGZGVnRxM2QZ3x1OR32+vwkkCm0IW6HmJ49IsPm7ilLg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vue/compiler-ssr": "3.3.4",
|
"@vue/compiler-ssr": "3.3.8",
|
||||||
"@vue/shared": "3.3.4"
|
"@vue/shared": "3.3.8"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"vue": "3.3.4"
|
"vue": "3.3.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vue/server-renderer/node_modules/@vue/shared": {
|
"node_modules/@vue/server-renderer/node_modules/@vue/shared": {
|
||||||
"version": "3.3.4",
|
"version": "3.3.8",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.3.4.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.3.8.tgz",
|
||||||
"integrity": "sha512-7OjdcV8vQ74eiz1TZLzZP4JwqM5fA94K6yntPS5Z25r9HDuGNzaGdgvwKYq6S+MxwF0TFRwe50fIR/MYnakdkQ==",
|
"integrity": "sha512-8PGwybFwM4x8pcfgqEQFy70NaQxASvOC5DJwLQfpArw1UDfUXrJkdxD3BhVTMS+0Lef/TU7YO0Jvr0jJY8T+mw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/@vue/shared": {
|
"node_modules/@vue/shared": {
|
||||||
@@ -683,9 +683,9 @@
|
|||||||
"integrity": "sha512-oJ4F3TnvpXaQwZJNF3ZK+kLPHKarDmJjJ6jyzVNDKH9md1dptjC7lWR//jrGuLdek/U6iltWxqAnYOu8gCiOvA=="
|
"integrity": "sha512-oJ4F3TnvpXaQwZJNF3ZK+kLPHKarDmJjJ6jyzVNDKH9md1dptjC7lWR//jrGuLdek/U6iltWxqAnYOu8gCiOvA=="
|
||||||
},
|
},
|
||||||
"node_modules/alpinejs": {
|
"node_modules/alpinejs": {
|
||||||
"version": "3.13.1",
|
"version": "3.13.2",
|
||||||
"resolved": "https://registry.npmjs.org/alpinejs/-/alpinejs-3.13.1.tgz",
|
"resolved": "https://registry.npmjs.org/alpinejs/-/alpinejs-3.13.2.tgz",
|
||||||
"integrity": "sha512-/LZ7mumW02V7AV5xTTftJFHS0I3KOXLl7tHm4xpxXAV+HJ/zjTT0n8MU7RZ6UoGPhmO/i+KEhQojaH/0RsH5tg==",
|
"integrity": "sha512-WzojeeN082kLZznGI1HAuP8yFJSWqJ1fGdz2mUjj45H4y0XwToE7fFqtI3mCPRR+BpcSbxT/NL+FyPnYAWSltw==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vue/reactivity": "~3.1.1"
|
"@vue/reactivity": "~3.1.1"
|
||||||
}
|
}
|
||||||
@@ -756,9 +756,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/axios": {
|
"node_modules/axios": {
|
||||||
"version": "1.5.1",
|
"version": "1.6.2",
|
||||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.5.1.tgz",
|
"resolved": "https://registry.npmjs.org/axios/-/axios-1.6.2.tgz",
|
||||||
"integrity": "sha512-Q28iYCWzNHjAm+yEAot5QaAMxhMghWLFVf7rRdwhUI+c2jix2DUXjAHXVi+s1ibs3mjPO/cCgbA++3BjD0vP/A==",
|
"integrity": "sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"follow-redirects": "^1.15.0",
|
"follow-redirects": "^1.15.0",
|
||||||
@@ -956,9 +956,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/daisyui": {
|
"node_modules/daisyui": {
|
||||||
"version": "4.0.3",
|
"version": "4.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/daisyui/-/daisyui-4.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/daisyui/-/daisyui-4.3.1.tgz",
|
||||||
"integrity": "sha512-mG6PsdIA6MEHzdJwBlJxc1rqsIAAlcfhj2O8g0ol1uWg5y6C5zTcqfG8vKBqK4y2YfCxGIVgMsMWRTSm32N1Ow==",
|
"integrity": "sha512-dCi91VD+57lkoBd10CjdW4wPOeOPYvvzQbxti6xmyQbDMbCeCXwNq2KdoU798I4OsCcD5B+n7yVG7HAgYW+cvw==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"css-selector-tokenizer": "^0.8",
|
"css-selector-tokenizer": "^0.8",
|
||||||
"culori": "^3",
|
"culori": "^3",
|
||||||
@@ -1153,9 +1153,9 @@
|
|||||||
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="
|
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="
|
||||||
},
|
},
|
||||||
"node_modules/fsevents": {
|
"node_modules/fsevents": {
|
||||||
"version": "2.3.2",
|
"version": "2.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
||||||
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
|
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -1335,12 +1335,12 @@
|
|||||||
"integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="
|
"integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="
|
||||||
},
|
},
|
||||||
"node_modules/magic-string": {
|
"node_modules/magic-string": {
|
||||||
"version": "0.30.0",
|
"version": "0.30.5",
|
||||||
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.0.tgz",
|
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.5.tgz",
|
||||||
"integrity": "sha512-LA+31JYDJLs82r2ScLrlz1GjSgu66ZV518eyWT+S8VhyQn/JL0u9MeBOvQMGYiPk1DBiSN9DDMOcXvigJZaViQ==",
|
"integrity": "sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jridgewell/sourcemap-codec": "^1.4.13"
|
"@jridgewell/sourcemap-codec": "^1.4.15"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
@@ -1711,9 +1711,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/rollup": {
|
"node_modules/rollup": {
|
||||||
"version": "3.29.3",
|
"version": "3.29.4",
|
||||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.3.tgz",
|
"resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.4.tgz",
|
||||||
"integrity": "sha512-T7du6Hum8jOkSWetjRgbwpM6Sy0nECYrYRSmZjayFcOddtKJWU4d17AC3HNUk7HRuqy4p+G7aEZclSHytqUmEg==",
|
"integrity": "sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"rollup": "dist/bin/rollup"
|
"rollup": "dist/bin/rollup"
|
||||||
@@ -1922,9 +1922,9 @@
|
|||||||
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
|
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
|
||||||
},
|
},
|
||||||
"node_modules/vite": {
|
"node_modules/vite": {
|
||||||
"version": "4.4.11",
|
"version": "4.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/vite/-/vite-4.4.11.tgz",
|
"resolved": "https://registry.npmjs.org/vite/-/vite-4.5.0.tgz",
|
||||||
"integrity": "sha512-ksNZJlkcU9b0lBwAGZGGaZHCMqHsc8OpgtoYhsQ4/I2v5cnpmmmqe5pM4nv/4Hn6G/2GhTdj0DhZh2e+Er1q5A==",
|
"integrity": "sha512-ulr8rNLA6rkyFAlVWw2q5YJ91v098AFQ2R0PRFwPzREXOUJQPtFUG0t+/ZikhaOCDqFoDhN6/v8Sq0o4araFAw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"esbuild": "^0.18.10",
|
"esbuild": "^0.18.10",
|
||||||
@@ -1977,35 +1977,40 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/vite-plugin-full-reload": {
|
"node_modules/vite-plugin-full-reload": {
|
||||||
"version": "1.0.5",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/vite-plugin-full-reload/-/vite-plugin-full-reload-1.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/vite-plugin-full-reload/-/vite-plugin-full-reload-1.1.0.tgz",
|
||||||
"integrity": "sha512-kVZFDFWr0DxiHn6MuDVTQf7gnWIdETGlZh0hvTiMXzRN80vgF4PKbONSq8U1d0WtHsKaFODTQgJeakLacoPZEQ==",
|
"integrity": "sha512-3cObNDzX6DdfhD9E7kf6w2mNunFpD7drxyNgHLw+XwIYAgb+Xt16SEXo0Up4VH+TMf3n+DSVJZtW2POBGcBYAA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"picocolors": "^1.0.0",
|
"picocolors": "^1.0.0",
|
||||||
"picomatch": "^2.3.1"
|
"picomatch": "^2.3.1"
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"vite": "^2 || ^3 || ^4"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/vue": {
|
"node_modules/vue": {
|
||||||
"version": "3.3.4",
|
"version": "3.3.8",
|
||||||
"resolved": "https://registry.npmjs.org/vue/-/vue-3.3.4.tgz",
|
"resolved": "https://registry.npmjs.org/vue/-/vue-3.3.8.tgz",
|
||||||
"integrity": "sha512-VTyEYn3yvIeY1Py0WaYGZsXnz3y5UnGi62GjVEqvEGPl6nxbOrCXbVOTQWBEJUqAyTUk2uJ5JLVnYJ6ZzGbrSw==",
|
"integrity": "sha512-5VSX/3DabBikOXMsxzlW8JyfeLKlG9mzqnWgLQLty88vdZL7ZJgrdgBOmrArwxiLtmS+lNNpPcBYqrhE6TQW5w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vue/compiler-dom": "3.3.4",
|
"@vue/compiler-dom": "3.3.8",
|
||||||
"@vue/compiler-sfc": "3.3.4",
|
"@vue/compiler-sfc": "3.3.8",
|
||||||
"@vue/runtime-dom": "3.3.4",
|
"@vue/runtime-dom": "3.3.8",
|
||||||
"@vue/server-renderer": "3.3.4",
|
"@vue/server-renderer": "3.3.8",
|
||||||
"@vue/shared": "3.3.4"
|
"@vue/shared": "3.3.8"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"typescript": "*"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"typescript": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/vue/node_modules/@vue/shared": {
|
"node_modules/vue/node_modules/@vue/shared": {
|
||||||
"version": "3.3.4",
|
"version": "3.3.8",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.3.4.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.3.8.tgz",
|
||||||
"integrity": "sha512-7OjdcV8vQ74eiz1TZLzZP4JwqM5fA94K6yntPS5Z25r9HDuGNzaGdgvwKYq6S+MxwF0TFRwe50fIR/MYnakdkQ==",
|
"integrity": "sha512-8PGwybFwM4x8pcfgqEQFy70NaQxASvOC5DJwLQfpArw1UDfUXrJkdxD3BhVTMS+0Lef/TU7YO0Jvr0jJY8T+mw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/wrappy": {
|
"node_modules/wrappy": {
|
||||||
|
|||||||
12
package.json
12
package.json
@@ -6,19 +6,19 @@
|
|||||||
"build": "vite build"
|
"build": "vite build"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vitejs/plugin-vue": "4.4.0",
|
"@vitejs/plugin-vue": "4.5.0",
|
||||||
"autoprefixer": "10.4.16",
|
"autoprefixer": "10.4.16",
|
||||||
"axios": "1.5.1",
|
"axios": "1.6.2",
|
||||||
"laravel-vite-plugin": "0.8.1",
|
"laravel-vite-plugin": "0.8.1",
|
||||||
"postcss": "8.4.31",
|
"postcss": "8.4.31",
|
||||||
"tailwindcss": "3.3.5",
|
"tailwindcss": "3.3.5",
|
||||||
"vite": "4.4.11",
|
"vite": "4.5.0",
|
||||||
"vue": "3.3.4"
|
"vue": "3.3.8"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@tailwindcss/typography": "0.5.10",
|
"@tailwindcss/typography": "0.5.10",
|
||||||
"alpinejs": "3.13.1",
|
"alpinejs": "3.13.2",
|
||||||
"daisyui": "4.0.3",
|
"daisyui": "4.3.1",
|
||||||
"tailwindcss-scrollbar": "0.1.0"
|
"tailwindcss-scrollbar": "0.1.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,6 +32,12 @@
|
|||||||
]) }}">
|
]) }}">
|
||||||
<button>Destinations</button>
|
<button>Destinations</button>
|
||||||
</a>
|
</a>
|
||||||
|
<a class="{{ request()->routeIs('server.log-drains') ? 'text-white' : '' }}"
|
||||||
|
href="{{ route('server.log-drains', [
|
||||||
|
'server_uuid' => data_get($parameters, 'server_uuid'),
|
||||||
|
]) }}">
|
||||||
|
<button>Log Drains</button>
|
||||||
|
</a>
|
||||||
<div class="flex-1"></div>
|
<div class="flex-1"></div>
|
||||||
<livewire:server.proxy.deploy :server="$server" />
|
<livewire:server.proxy.deploy :server="$server" />
|
||||||
</nav>
|
</nav>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<x-emails.layout>
|
<x-emails.layout>
|
||||||
|
|
||||||
Container ({{ $containerName }}) has been restarted automatically on {{$serverName}}, because it was stopped unexpectedly.
|
A service ({{ $containerName }}) has been restarted automatically on {{$serverName}}, because it was stopped unexpectedly.
|
||||||
|
|
||||||
@if ($containerName === 'coolify-proxy')
|
@if ($containerName === 'coolify-proxy')
|
||||||
Coolify Proxy should run on your server as you have FQDNs set up in one of your resources.
|
Coolify Proxy should run on your server as you have FQDNs set up in one of your resources.
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<x-emails.layout>
|
<x-emails.layout>
|
||||||
|
|
||||||
Container {{ $containerName }} has been stopped unexpectedly on {{$serverName}}.
|
A service ({{ $containerName }}) has been stopped unexpectedly on {{$serverName}}.
|
||||||
|
|
||||||
@if ($url)
|
@if ($url)
|
||||||
Please check what is going on [here]({{ $url }}).
|
Please check what is going on [here]({{ $url }}).
|
||||||
|
|||||||
@@ -114,6 +114,8 @@
|
|||||||
</div>
|
</div>
|
||||||
<h3>Advanced</h3>
|
<h3>Advanced</h3>
|
||||||
<div class="flex flex-col">
|
<div class="flex flex-col">
|
||||||
|
<x-forms.checkbox helper="Drain logs to your configured log drain endpoint in your Server settings." instantSave
|
||||||
|
id="is_log_drain_enabled" label="Drain Logs" />
|
||||||
<x-forms.checkbox
|
<x-forms.checkbox
|
||||||
helper="Your application will be available only on https if your domain starts with https://..."
|
helper="Your application will be available only on https if your domain starts with https://..."
|
||||||
instantSave id="is_force_https_enabled" label="Force Https" />
|
instantSave id="is_force_https_enabled" label="Force Https" />
|
||||||
|
|||||||
@@ -59,5 +59,10 @@
|
|||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
<x-forms.textarea label="Custom MariaDB Configuration" rows="10" id="database.mariadb_conf" />
|
<x-forms.textarea label="Custom MariaDB Configuration" rows="10" id="database.mariadb_conf" />
|
||||||
|
<h3 class="pt-4">Advanced</h3>
|
||||||
|
<div class="flex flex-col">
|
||||||
|
<x-forms.checkbox helper="Drain logs to your configured log drain endpoint in your Server settings."
|
||||||
|
instantSave="instantSaveAdvanced" id="database.is_log_drain_enabled" label="Drain Logs" />
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -16,8 +16,8 @@
|
|||||||
<div class="flex gap-2">
|
<div class="flex gap-2">
|
||||||
<x-forms.input label="Initial Username" id="database.mongo_initdb_root_username"
|
<x-forms.input label="Initial Username" id="database.mongo_initdb_root_username"
|
||||||
placeholder="If empty: postgres" readonly helper="You can only change this in the database." />
|
placeholder="If empty: postgres" readonly helper="You can only change this in the database." />
|
||||||
<x-forms.input label="Initial Password" id="database.mongo_initdb_root_password" type="password" required
|
<x-forms.input label="Initial Password" id="database.mongo_initdb_root_password" type="password"
|
||||||
readonly helper="You can only change this in the database." />
|
required readonly helper="You can only change this in the database." />
|
||||||
<x-forms.input label="Initial Database" id="database.mongo_initdb_database"
|
<x-forms.input label="Initial Database" id="database.mongo_initdb_database"
|
||||||
placeholder="If empty, it will be the same as Username." readonly
|
placeholder="If empty, it will be the same as Username." readonly
|
||||||
helper="You can only change this in the database." />
|
helper="You can only change this in the database." />
|
||||||
@@ -44,14 +44,19 @@
|
|||||||
<x-forms.checkbox instantSave id="database.is_public" label="Accessible over the internet" />
|
<x-forms.checkbox instantSave id="database.is_public" label="Accessible over the internet" />
|
||||||
</div>
|
</div>
|
||||||
<x-forms.input label="Mongo URL (internal)"
|
<x-forms.input label="Mongo URL (internal)"
|
||||||
helper="If you change the user/password/port, this could be different. This is with the default values."
|
helper="If you change the user/password/port, this could be different. This is with the default values."
|
||||||
type="password" readonly wire:model="db_url" />
|
type="password" readonly wire:model="db_url" />
|
||||||
@if ($db_url_public)
|
@if ($db_url_public)
|
||||||
<x-forms.input label="Mongo URL (public)"
|
<x-forms.input label="Mongo URL (public)"
|
||||||
helper="If you change the user/password/port, this could be different. This is with the default values."
|
helper="If you change the user/password/port, this could be different. This is with the default values."
|
||||||
type="password" readonly wire:model="db_url_public" />
|
type="password" readonly wire:model="db_url_public" />
|
||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
<x-forms.textarea label="Custom MongoDB Configuration" rows="10" id="database.mongo_conf" />
|
<x-forms.textarea label="Custom MongoDB Configuration" rows="10" id="database.mongo_conf" />
|
||||||
|
<h3 class="pt-4">Advanced</h3>
|
||||||
|
<div class="flex flex-col">
|
||||||
|
<x-forms.checkbox helper="Drain logs to your configured log drain endpoint in your Server settings."
|
||||||
|
instantSave="instantSaveAdvanced" id="database.is_log_drain_enabled" label="Drain Logs" />
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -59,5 +59,10 @@
|
|||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
<x-forms.textarea label="Custom Mysql Configuration" rows="10" id="database.mysql_conf" />
|
<x-forms.textarea label="Custom Mysql Configuration" rows="10" id="database.mysql_conf" />
|
||||||
|
<h3 class="pt-4">Advanced</h3>
|
||||||
|
<div class="flex flex-col">
|
||||||
|
<x-forms.checkbox helper="Drain logs to your configured log drain endpoint in your Server settings."
|
||||||
|
instantSave="instantSaveAdvanced" id="database.is_log_drain_enabled" label="Drain Logs" />
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -74,6 +74,11 @@
|
|||||||
</div>
|
</div>
|
||||||
<x-forms.textarea label="Custom PostgreSQL Configuration" rows="10" id="database.postgres_conf" />
|
<x-forms.textarea label="Custom PostgreSQL Configuration" rows="10" id="database.postgres_conf" />
|
||||||
</form>
|
</form>
|
||||||
|
<h3 class="pt-4">Advanced</h3>
|
||||||
|
<div class="flex flex-col">
|
||||||
|
<x-forms.checkbox helper="Drain logs to your configured log drain endpoint in your Server settings." instantSave="instantSaveAdvanced"
|
||||||
|
id="database.is_log_drain_enabled" label="Drain Logs" />
|
||||||
|
</div>
|
||||||
<div class="pb-16">
|
<div class="pb-16">
|
||||||
<div class="flex gap-2 pt-4 pb-2">
|
<div class="flex gap-2 pt-4 pb-2">
|
||||||
<h3>Initialization scripts</h3>
|
<h3>Initialization scripts</h3>
|
||||||
@@ -87,4 +92,5 @@
|
|||||||
@endforelse
|
@endforelse
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -33,5 +33,10 @@
|
|||||||
<x-forms.textarea
|
<x-forms.textarea
|
||||||
helper="<a target='_blank' class='text-white underline' href='https://raw.githubusercontent.com/redis/redis/7.2/redis.conf'>Redis Default Configuration</a>"
|
helper="<a target='_blank' class='text-white underline' href='https://raw.githubusercontent.com/redis/redis/7.2/redis.conf'>Redis Default Configuration</a>"
|
||||||
label="Custom Redis Configuration" rows="10" id="database.redis_conf" />
|
label="Custom Redis Configuration" rows="10" id="database.redis_conf" />
|
||||||
|
<h3 class="pt-4">Advanced</h3>
|
||||||
|
<div class="flex flex-col">
|
||||||
|
<x-forms.checkbox helper="Drain logs to your configured log drain endpoint in your Server settings."
|
||||||
|
instantSave="instantSaveAdvanced" id="database.is_log_drain_enabled" label="Drain Logs" />
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -36,6 +36,8 @@
|
|||||||
<x-forms.checkbox instantSave label="Exclude from service status"
|
<x-forms.checkbox instantSave label="Exclude from service status"
|
||||||
helper="If you do not need to monitor this resource, enable. Useful if this service is optional."
|
helper="If you do not need to monitor this resource, enable. Useful if this service is optional."
|
||||||
id="application.exclude_from_status"></x-forms.checkbox>
|
id="application.exclude_from_status"></x-forms.checkbox>
|
||||||
|
<x-forms.checkbox helper="Drain logs to your configured log drain endpoint in your Server settings."
|
||||||
|
instantSave="instantSaveAdvanced" id="application.is_log_drain_enabled" label="Drain Logs" />
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -32,6 +32,8 @@
|
|||||||
<x-forms.checkbox instantSave label="Exclude from service status"
|
<x-forms.checkbox instantSave label="Exclude from service status"
|
||||||
helper="If you do not need to monitor this resource, enable. Useful if this service is optional."
|
helper="If you do not need to monitor this resource, enable. Useful if this service is optional."
|
||||||
id="database.exclude_from_status"></x-forms.checkbox>
|
id="database.exclude_from_status"></x-forms.checkbox>
|
||||||
|
<x-forms.checkbox helper="Drain logs to your configured log drain endpoint in your Server settings."
|
||||||
|
instantSave="instantSaveAdvanced" id="database.is_log_drain_enabled" label="Drain Logs" />
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
66
resources/views/livewire/server/log-drains.blade.php
Normal file
66
resources/views/livewire/server/log-drains.blade.php
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
<div>
|
||||||
|
<x-server.navbar :server="$server" :parameters="$parameters" />
|
||||||
|
<h2>Log Drains</h2>
|
||||||
|
<div class="pb-4">Sends service logs to 3rd party tools.</div>
|
||||||
|
<div class="flex flex-col gap-4 pt-4">
|
||||||
|
<div class="p-4 border border-coolgray-500">
|
||||||
|
<form wire:submit.prevent='submit("newrelic")' class="flex flex-col">
|
||||||
|
<h3>New Relic</h3>
|
||||||
|
<div class="w-32">
|
||||||
|
<x-forms.checkbox instantSave='instantSave("newrelic")'
|
||||||
|
id="server.settings.is_logdrain_newrelic_enabled" label="Enabled" />
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-col gap-4">
|
||||||
|
<div class="flex flex-col w-full gap-2 xl:flex-row">
|
||||||
|
<x-forms.input type="password" required id="server.settings.logdrain_newrelic_license_key"
|
||||||
|
label="License Key" />
|
||||||
|
<x-forms.input required id="server.settings.logdrain_newrelic_base_uri"
|
||||||
|
placeholder="https://log-api.eu.newrelic.com/log/v1" label="Endpoint (EU / US)" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex justify-end gap-4 pt-6">
|
||||||
|
<x-forms.button type="submit">
|
||||||
|
Save
|
||||||
|
</x-forms.button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
{{-- <h3>Highlight.io</h3>
|
||||||
|
<div class="w-32">
|
||||||
|
<x-forms.checkbox instantSave='instantSave("highlight")'
|
||||||
|
id="server.settings.is_logdrain_highlight_enabled" label="Enabled" />
|
||||||
|
</div>
|
||||||
|
<form wire:submit.prevent='submit("highlight")' class="flex flex-col">
|
||||||
|
<div class="flex flex-col gap-4">
|
||||||
|
<div class="flex flex-col w-full gap-2 xl:flex-row">
|
||||||
|
<x-forms.input type="password" required id="server.settings.logdrain_highlight_project_id"
|
||||||
|
label="Project Id" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex justify-end gap-4 pt-6">
|
||||||
|
<x-forms.button type="submit">
|
||||||
|
Save
|
||||||
|
</x-forms.button>
|
||||||
|
</div>
|
||||||
|
</form> --}}
|
||||||
|
<h3>Axiom</h3>
|
||||||
|
<div class="w-32">
|
||||||
|
<x-forms.checkbox instantSave='instantSave("axiom")' id="server.settings.is_logdrain_axiom_enabled"
|
||||||
|
label="Enabled" />
|
||||||
|
</div>
|
||||||
|
<form wire:submit.prevent='submit("axiom")' class="flex flex-col">
|
||||||
|
<div class="flex flex-col gap-4">
|
||||||
|
<div class="flex flex-col w-full gap-2 xl:flex-row">
|
||||||
|
<x-forms.input type="password" required id="server.settings.logdrain_axiom_api_key"
|
||||||
|
label="API Key" />
|
||||||
|
<x-forms.input required id="server.settings.logdrain_axiom_dataset_name" label="Dataset Name" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex justify-end gap-4 pt-6">
|
||||||
|
<x-forms.button type="submit">
|
||||||
|
Save
|
||||||
|
</x-forms.button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -16,6 +16,7 @@ use App\Http\Livewire\Security\ApiTokens;
|
|||||||
use App\Http\Livewire\Server\All;
|
use App\Http\Livewire\Server\All;
|
||||||
use App\Http\Livewire\Server\Create;
|
use App\Http\Livewire\Server\Create;
|
||||||
use App\Http\Livewire\Server\Destination\Show as DestinationShow;
|
use App\Http\Livewire\Server\Destination\Show as DestinationShow;
|
||||||
|
use App\Http\Livewire\Server\LogDrains;
|
||||||
use App\Http\Livewire\Server\PrivateKey\Show as PrivateKeyShow;
|
use App\Http\Livewire\Server\PrivateKey\Show as PrivateKeyShow;
|
||||||
use App\Http\Livewire\Server\Proxy\Show as ProxyShow;
|
use App\Http\Livewire\Server\Proxy\Show as ProxyShow;
|
||||||
use App\Http\Livewire\Server\Proxy\Logs as ProxyLogs;
|
use App\Http\Livewire\Server\Proxy\Logs as ProxyLogs;
|
||||||
@@ -130,6 +131,7 @@ Route::middleware(['auth'])->group(function () {
|
|||||||
Route::get('/server/{server_uuid}/proxy/logs', ProxyLogs::class)->name('server.proxy.logs');
|
Route::get('/server/{server_uuid}/proxy/logs', ProxyLogs::class)->name('server.proxy.logs');
|
||||||
Route::get('/server/{server_uuid}/private-key', PrivateKeyShow::class)->name('server.private-key');
|
Route::get('/server/{server_uuid}/private-key', PrivateKeyShow::class)->name('server.private-key');
|
||||||
Route::get('/server/{server_uuid}/destinations', DestinationShow::class)->name('server.destinations');
|
Route::get('/server/{server_uuid}/destinations', DestinationShow::class)->name('server.destinations');
|
||||||
|
Route::get('/server/{server_uuid}/log-drains', LogDrains::class)->name('server.log-drains');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
"version": "3.12.36"
|
"version": "3.12.36"
|
||||||
},
|
},
|
||||||
"v4": {
|
"v4": {
|
||||||
"version": "4.0.0-beta.138"
|
"version": "4.0.0-beta.144"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user