mirror of
https://github.com/ershisan99/coolify.git
synced 2026-01-08 05:02:21 +00:00
Compare commits
83 Commits
v4.0.0-bet
...
v4.0.0-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ae65172946 | ||
|
|
3a2d17bc05 | ||
|
|
4c1067cf36 | ||
|
|
b046a3e9f7 | ||
|
|
199881c596 | ||
|
|
99c8607ff4 | ||
|
|
e61fcc77f9 | ||
|
|
20dca179fb | ||
|
|
0f542c65ae | ||
|
|
d609fcaee1 | ||
|
|
fe8a7fc54f | ||
|
|
398f122593 | ||
|
|
f0abdcc2da | ||
|
|
c9a278b750 | ||
|
|
6990c593a4 | ||
|
|
8f54b51ecd | ||
|
|
3eb628b773 | ||
|
|
fabb97330a | ||
|
|
03c9793d11 | ||
|
|
a4320b7cee | ||
|
|
cbf9bc99ea | ||
|
|
0fbc382467 | ||
|
|
0f8ccac775 | ||
|
|
ee20c3339e | ||
|
|
0b11093d18 | ||
|
|
de8118b59d | ||
|
|
58522b59b7 | ||
|
|
356394c03d | ||
|
|
3e4db2f5b2 | ||
|
|
872981b8b4 | ||
|
|
51c468ae0b | ||
|
|
80a797aec8 | ||
|
|
cfdab13d77 | ||
|
|
4fc8988ff4 | ||
|
|
ab5619292e | ||
|
|
b02e5d3f27 | ||
|
|
5f0c9c3a31 | ||
|
|
ea6ec07a45 | ||
|
|
36be325d0d | ||
|
|
6138ddcac6 | ||
|
|
0509da6730 | ||
|
|
5b877b84c2 | ||
|
|
0c35726a8d | ||
|
|
e74899611b | ||
|
|
e9149e534d | ||
|
|
7cded7a36d | ||
|
|
ba74d55b4c | ||
|
|
a1d13fc14e | ||
|
|
cdb2a3a8e5 | ||
|
|
074c56edaa | ||
|
|
66bc03b4cd | ||
|
|
2dcb7fec05 | ||
|
|
249bccb49e | ||
|
|
a55eaa10ac | ||
|
|
ca2c75ce19 | ||
|
|
250d7cbc53 | ||
|
|
92a53a151e | ||
|
|
db3148c080 | ||
|
|
c46eeac4b5 | ||
|
|
19111ba059 | ||
|
|
9bc61a0a17 | ||
|
|
78b9166bdb | ||
|
|
1752448050 | ||
|
|
3fc78bc760 | ||
|
|
0c30b6222d | ||
|
|
3f74609c7f | ||
|
|
31b0ccba99 | ||
|
|
3fc544e0b9 | ||
|
|
67078fdc71 | ||
|
|
c91f426af3 | ||
|
|
9c2fea4b2e | ||
|
|
53d1fa0331 | ||
|
|
4ae7e46e81 | ||
|
|
ebfc0bd1e1 | ||
|
|
e1a1490911 | ||
|
|
6b75ff7de4 | ||
|
|
301469de6b | ||
|
|
b4d69a22df | ||
|
|
a86e971020 | ||
|
|
145af41c82 | ||
|
|
69c0b7240a | ||
|
|
6543132bcb | ||
|
|
9134437218 |
@@ -21,6 +21,7 @@ class StartPostgresql
|
|||||||
$this->configuration_dir = database_configuration_dir() . '/' . $container_name;
|
$this->configuration_dir = database_configuration_dir() . '/' . $container_name;
|
||||||
|
|
||||||
$this->commands = [
|
$this->commands = [
|
||||||
|
"echo '####### Starting {$database->name}.'",
|
||||||
"mkdir -p $this->configuration_dir",
|
"mkdir -p $this->configuration_dir",
|
||||||
"mkdir -p $this->configuration_dir/docker-entrypoint-initdb.d/"
|
"mkdir -p $this->configuration_dir/docker-entrypoint-initdb.d/"
|
||||||
];
|
];
|
||||||
@@ -96,6 +97,7 @@ class StartPostgresql
|
|||||||
$readme = generate_readme_file($this->database->name, now());
|
$readme = generate_readme_file($this->database->name, now());
|
||||||
$this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md";
|
$this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md";
|
||||||
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml up -d";
|
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml up -d";
|
||||||
|
$this->commands[] = "echo '####### {$database->name} started.'";
|
||||||
return remote_process($this->commands, $server);
|
return remote_process($this->commands, $server);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ class CheckConfiguration
|
|||||||
{
|
{
|
||||||
$proxy_path = get_proxy_path();
|
$proxy_path = get_proxy_path();
|
||||||
$proxy_configuration = instant_remote_process([
|
$proxy_configuration = instant_remote_process([
|
||||||
|
"mkdir -p $proxy_path",
|
||||||
"cat $proxy_path/docker-compose.yml",
|
"cat $proxy_path/docker-compose.yml",
|
||||||
], $server, false);
|
], $server, false);
|
||||||
|
|
||||||
|
|||||||
@@ -10,9 +10,11 @@ class SaveConfiguration
|
|||||||
{
|
{
|
||||||
use AsAction;
|
use AsAction;
|
||||||
|
|
||||||
public function handle(Server $server)
|
public function handle(Server $server, ?string $proxy_settings = null)
|
||||||
{
|
{
|
||||||
|
if (is_null($proxy_settings)) {
|
||||||
$proxy_settings = CheckConfiguration::run($server, true);
|
$proxy_settings = CheckConfiguration::run($server, true);
|
||||||
|
}
|
||||||
$proxy_path = get_proxy_path();
|
$proxy_path = get_proxy_path();
|
||||||
$docker_compose_yml_base64 = base64_encode($proxy_settings);
|
$docker_compose_yml_base64 = base64_encode($proxy_settings);
|
||||||
|
|
||||||
|
|||||||
@@ -2,8 +2,6 @@
|
|||||||
|
|
||||||
namespace App\Actions\Proxy;
|
namespace App\Actions\Proxy;
|
||||||
|
|
||||||
use App\Enums\ProxyStatus;
|
|
||||||
use App\Enums\ProxyTypes;
|
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
use Lorisleiva\Actions\Concerns\AsAction;
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
@@ -14,46 +12,32 @@ class StartProxy
|
|||||||
use AsAction;
|
use AsAction;
|
||||||
public function handle(Server $server, bool $async = true): Activity|string
|
public function handle(Server $server, bool $async = true): Activity|string
|
||||||
{
|
{
|
||||||
$proxyType = data_get($server,'proxy.type');
|
$commands = collect([]);
|
||||||
|
$proxyType = $server->proxyType();
|
||||||
if ($proxyType === 'none') {
|
if ($proxyType === 'none') {
|
||||||
return 'OK';
|
return 'OK';
|
||||||
}
|
}
|
||||||
if (is_null($proxyType)) {
|
|
||||||
$server->proxy->type = ProxyTypes::TRAEFIK_V2->value;
|
|
||||||
$server->proxy->status = ProxyStatus::EXITED->value;
|
|
||||||
$server->save();
|
|
||||||
}
|
|
||||||
$proxy_path = get_proxy_path();
|
$proxy_path = get_proxy_path();
|
||||||
$networks = collect($server->standaloneDockers)->map(function ($docker) {
|
|
||||||
return $docker['network'];
|
|
||||||
})->unique();
|
|
||||||
if ($networks->count() === 0) {
|
|
||||||
$networks = collect(['coolify']);
|
|
||||||
}
|
|
||||||
$create_networks_command = $networks->map(function ($network) {
|
|
||||||
return "docker network ls --format '{{.Name}}' | grep '^$network$' >/dev/null 2>&1 || docker network create --attachable $network > /dev/null 2>&1";
|
|
||||||
});
|
|
||||||
|
|
||||||
$configuration = CheckConfiguration::run($server);
|
$configuration = CheckConfiguration::run($server);
|
||||||
if (!$configuration) {
|
if (!$configuration) {
|
||||||
throw new \Exception("Configuration is not synced");
|
throw new \Exception("Configuration is not synced");
|
||||||
}
|
}
|
||||||
|
SaveConfiguration::run($server, $configuration);
|
||||||
$docker_compose_yml_base64 = base64_encode($configuration);
|
$docker_compose_yml_base64 = base64_encode($configuration);
|
||||||
$server->proxy->last_applied_settings = Str::of($docker_compose_yml_base64)->pipe('md5')->value;
|
$server->proxy->last_applied_settings = Str::of($docker_compose_yml_base64)->pipe('md5')->value;
|
||||||
$server->save();
|
$server->save();
|
||||||
$commands = [
|
|
||||||
"command -v lsof >/dev/null || echo '####### Installing lsof...'",
|
$commands = $commands->merge([
|
||||||
"command -v lsof >/dev/null || apt-get update",
|
"apt-get update > /dev/null 2>&1 || true",
|
||||||
|
"command -v lsof >/dev/null || echo '####### Installing lsof.'",
|
||||||
"command -v lsof >/dev/null || apt install -y lsof",
|
"command -v lsof >/dev/null || apt install -y lsof",
|
||||||
"command -v lsof >/dev/null || command -v fuser >/dev/null || apt install -y psmisc",
|
"command -v lsof >/dev/null || command -v fuser >/dev/null || apt install -y psmisc",
|
||||||
"echo '####### Creating required Docker networks...'",
|
"mkdir -p $proxy_path && cd $proxy_path",
|
||||||
...$create_networks_command,
|
"echo '####### Creating Docker Compose file.'",
|
||||||
"cd $proxy_path",
|
"echo '####### Pulling docker image.'",
|
||||||
"echo '####### Creating Docker Compose file...'",
|
'docker compose pull',
|
||||||
"echo '####### Pulling docker image...'",
|
"echo '####### Stopping existing coolify-proxy.'",
|
||||||
'docker compose pull || docker-compose pull',
|
"docker compose down -v --remove-orphans > /dev/null 2>&1",
|
||||||
"echo '####### Stopping existing coolify-proxy...'",
|
|
||||||
"docker compose down -v --remove-orphans > /dev/null 2>&1 || docker-compose down -v --remove-orphans > /dev/null 2>&1 || true",
|
|
||||||
"command -v fuser >/dev/null || command -v lsof >/dev/null || echo '####### Could not kill existing processes listening on port 80 & 443. Please stop the process holding these ports...'",
|
"command -v fuser >/dev/null || command -v lsof >/dev/null || echo '####### Could not kill existing processes listening on port 80 & 443. Please stop the process holding these ports...'",
|
||||||
"command -v lsof >/dev/null && lsof -nt -i:80 | xargs -r kill -9 || true",
|
"command -v lsof >/dev/null && lsof -nt -i:80 | xargs -r kill -9 || true",
|
||||||
"command -v lsof >/dev/null && lsof -nt -i:443 | xargs -r kill -9 || true",
|
"command -v lsof >/dev/null && lsof -nt -i:443 | xargs -r kill -9 || true",
|
||||||
@@ -62,16 +46,20 @@ class StartProxy
|
|||||||
"systemctl disable nginx > /dev/null 2>&1 || true",
|
"systemctl disable nginx > /dev/null 2>&1 || true",
|
||||||
"systemctl disable apache2 > /dev/null 2>&1 || true",
|
"systemctl disable apache2 > /dev/null 2>&1 || true",
|
||||||
"systemctl disable apache > /dev/null 2>&1 || true",
|
"systemctl disable apache > /dev/null 2>&1 || true",
|
||||||
"echo '####### Starting coolify-proxy...'",
|
"echo '####### Starting coolify-proxy.'",
|
||||||
'docker compose up -d --remove-orphans || docker-compose up -d --remove-orphans',
|
'docker compose up -d --remove-orphans',
|
||||||
"echo '####### Proxy installed successfully...'"
|
"echo '####### Proxy installed successfully.'"
|
||||||
];
|
]);
|
||||||
if (!$async) {
|
$commands = $commands->merge(connectProxyToNetworks($server));
|
||||||
instant_remote_process($commands, $server);
|
if ($async) {
|
||||||
return 'OK';
|
|
||||||
} else {
|
|
||||||
$activity = remote_process($commands, $server);
|
$activity = remote_process($commands, $server);
|
||||||
return $activity;
|
return $activity;
|
||||||
|
} else {
|
||||||
|
instant_remote_process($commands, $server);
|
||||||
|
$server->proxy->set('status', 'running');
|
||||||
|
$server->proxy->set('type', $proxyType);
|
||||||
|
$server->save();
|
||||||
|
return 'OK';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
27
app/Actions/Service/StartService.php
Normal file
27
app/Actions/Service/StartService.php
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Actions\Service;
|
||||||
|
|
||||||
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
|
use App\Models\Service;
|
||||||
|
|
||||||
|
class StartService
|
||||||
|
{
|
||||||
|
use AsAction;
|
||||||
|
public function handle(Service $service)
|
||||||
|
{
|
||||||
|
$service->saveComposeConfigs();
|
||||||
|
$commands[] = "cd " . $service->workdir();
|
||||||
|
$commands[] = "echo '####### Saved configuration files to {$service->workdir()}.'";
|
||||||
|
$commands[] = "echo '####### Creating Docker network.'";
|
||||||
|
$commands[] = "docker network create --attachable {$service->uuid} >/dev/null 2>/dev/null || true";
|
||||||
|
$commands[] = "echo '####### Starting service {$service->name} on {$service->server->name}.'";
|
||||||
|
$commands[] = "echo '####### Pulling images.'";
|
||||||
|
$commands[] = "docker compose pull";
|
||||||
|
$commands[] = "echo '####### Starting containers.'";
|
||||||
|
$commands[] = "docker compose up -d --remove-orphans --force-recreate";
|
||||||
|
$commands[] = "docker network connect $service->uuid coolify-proxy 2>/dev/null || true";
|
||||||
|
$activity = remote_process($commands, $service->server);
|
||||||
|
return $activity;
|
||||||
|
}
|
||||||
|
}
|
||||||
26
app/Actions/Service/StopService.php
Normal file
26
app/Actions/Service/StopService.php
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Actions\Service;
|
||||||
|
|
||||||
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
|
use App\Models\Service;
|
||||||
|
|
||||||
|
class StopService
|
||||||
|
{
|
||||||
|
use AsAction;
|
||||||
|
public function handle(Service $service)
|
||||||
|
{
|
||||||
|
$applications = $service->applications()->get();
|
||||||
|
foreach ($applications as $application) {
|
||||||
|
instant_remote_process(["docker rm -f {$application->name}-{$service->uuid}"], $service->server);
|
||||||
|
$application->update(['status' => 'exited']);
|
||||||
|
}
|
||||||
|
$dbs = $service->databases()->get();
|
||||||
|
foreach ($dbs as $db) {
|
||||||
|
instant_remote_process(["docker rm -f {$db->name}-{$service->uuid}"], $service->server);
|
||||||
|
$db->update(['status' => 'exited']);
|
||||||
|
}
|
||||||
|
instant_remote_process(["docker network disconnect {$service->uuid} coolify-proxy 2>/dev/null"], $service->server, false);
|
||||||
|
instant_remote_process(["docker network rm {$service->uuid} 2>/dev/null"], $service->server, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -56,6 +56,7 @@ class Emails extends Command
|
|||||||
$type = select(
|
$type = select(
|
||||||
'Which Email should be sent?',
|
'Which Email should be sent?',
|
||||||
options: [
|
options: [
|
||||||
|
'updates' => 'Send Update Email to all users',
|
||||||
'emails-test' => 'Test',
|
'emails-test' => 'Test',
|
||||||
'application-deployment-success' => 'Application - Deployment Success',
|
'application-deployment-success' => 'Application - Deployment Success',
|
||||||
'application-deployment-failed' => 'Application - Deployment Failed',
|
'application-deployment-failed' => 'Application - Deployment Failed',
|
||||||
@@ -78,6 +79,38 @@ class Emails extends Command
|
|||||||
$this->mail = new MailMessage();
|
$this->mail = new MailMessage();
|
||||||
$this->mail->subject("Test Email");
|
$this->mail->subject("Test Email");
|
||||||
switch ($type) {
|
switch ($type) {
|
||||||
|
case 'updates':
|
||||||
|
$teams = Team::all();
|
||||||
|
if (!$teams || $teams->isEmpty()) {
|
||||||
|
echo 'No teams found.' . PHP_EOL;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$emails = [];
|
||||||
|
foreach ($teams as $team) {
|
||||||
|
foreach ($team->members as $member) {
|
||||||
|
if ($member->email && $member->marketing_emails) {
|
||||||
|
$emails[] = $member->email;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$emails = array_unique($emails);
|
||||||
|
$this->info("Sending to " . count($emails) . " emails.");
|
||||||
|
foreach ($emails as $email) {
|
||||||
|
$this->info($email);
|
||||||
|
}
|
||||||
|
$confirmed = confirm('Are you sure?');
|
||||||
|
if ($confirmed) {
|
||||||
|
foreach ($emails as $email) {
|
||||||
|
$this->mail = new MailMessage();
|
||||||
|
$this->mail->subject('One-click Services, Docker Compose support');
|
||||||
|
$unsubscribeUrl = route('unsubscribe.marketing.emails', [
|
||||||
|
'token' => encrypt($email),
|
||||||
|
]);
|
||||||
|
$this->mail->view('emails.updates',["unsubscribeUrl" => $unsubscribeUrl]);
|
||||||
|
$this->sendEmail($email);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
case 'emails-test':
|
case 'emails-test':
|
||||||
$this->mail = (new Test())->toMail();
|
$this->mail = (new Test())->toMail();
|
||||||
$this->sendEmail();
|
$this->sendEmail();
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ use Illuminate\Http\Client\PendingRequest;
|
|||||||
use Illuminate\Http\Client\Pool;
|
use Illuminate\Http\Client\Pool;
|
||||||
use Illuminate\Support\Facades\Http;
|
use Illuminate\Support\Facades\Http;
|
||||||
|
|
||||||
|
use function Laravel\Prompts\confirm;
|
||||||
|
|
||||||
class SyncBunny extends Command
|
class SyncBunny extends Command
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
@@ -14,7 +16,7 @@ class SyncBunny extends Command
|
|||||||
*
|
*
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
protected $signature = 'sync:bunny';
|
protected $signature = 'sync:bunny {--only-template}';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The console command description.
|
* The console command description.
|
||||||
@@ -28,6 +30,7 @@ class SyncBunny extends Command
|
|||||||
*/
|
*/
|
||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
|
$only_template = $this->option('only-template');
|
||||||
$bunny_cdn = "https://cdn.coollabs.io";
|
$bunny_cdn = "https://cdn.coollabs.io";
|
||||||
$bunny_cdn_path = "coolify";
|
$bunny_cdn_path = "coolify";
|
||||||
$bunny_cdn_storage_name = "coolcdn";
|
$bunny_cdn_storage_name = "coolcdn";
|
||||||
@@ -39,6 +42,7 @@ class SyncBunny extends Command
|
|||||||
$install_script = "install.sh";
|
$install_script = "install.sh";
|
||||||
$upgrade_script = "upgrade.sh";
|
$upgrade_script = "upgrade.sh";
|
||||||
$production_env = ".env.production";
|
$production_env = ".env.production";
|
||||||
|
$service_template = "service-templates.json";
|
||||||
|
|
||||||
$versions = "versions.json";
|
$versions = "versions.json";
|
||||||
|
|
||||||
@@ -64,6 +68,19 @@ class SyncBunny extends Command
|
|||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
try {
|
try {
|
||||||
|
$confirmed = confirm('Are you sure?');
|
||||||
|
if (!$confirmed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ($only_template) {
|
||||||
|
Http::pool(fn (Pool $pool) => [
|
||||||
|
$pool->storage(file: "$parent_dir/templates/$service_template")->put("/$bunny_cdn_storage_name/$bunny_cdn_path/$service_template"),
|
||||||
|
$pool->purge("$bunny_cdn/$bunny_cdn_path/$service_template"),
|
||||||
|
]);
|
||||||
|
$this->info('Service template uploaded & purged...');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Http::pool(fn (Pool $pool) => [
|
Http::pool(fn (Pool $pool) => [
|
||||||
$pool->storage(file: "$parent_dir/$compose_file")->put("/$bunny_cdn_storage_name/$bunny_cdn_path/$compose_file"),
|
$pool->storage(file: "$parent_dir/$compose_file")->put("/$bunny_cdn_storage_name/$bunny_cdn_path/$compose_file"),
|
||||||
$pool->storage(file: "$parent_dir/$compose_file_prod")->put("/$bunny_cdn_storage_name/$bunny_cdn_path/$compose_file_prod"),
|
$pool->storage(file: "$parent_dir/$compose_file_prod")->put("/$bunny_cdn_storage_name/$bunny_cdn_path/$compose_file_prod"),
|
||||||
@@ -72,7 +89,7 @@ class SyncBunny extends Command
|
|||||||
$pool->storage(file: "$parent_dir/scripts/$install_script")->put("/$bunny_cdn_storage_name/$bunny_cdn_path/$install_script"),
|
$pool->storage(file: "$parent_dir/scripts/$install_script")->put("/$bunny_cdn_storage_name/$bunny_cdn_path/$install_script"),
|
||||||
$pool->storage(file: "$parent_dir/$versions")->put("/$bunny_cdn_storage_name/$bunny_cdn_path/$versions"),
|
$pool->storage(file: "$parent_dir/$versions")->put("/$bunny_cdn_storage_name/$bunny_cdn_path/$versions"),
|
||||||
]);
|
]);
|
||||||
ray("{$bunny_cdn}/{$bunny_cdn_path}");
|
$this->info("{$bunny_cdn}/{$bunny_cdn_path}");
|
||||||
Http::pool(fn (Pool $pool) => [
|
Http::pool(fn (Pool $pool) => [
|
||||||
$pool->purge("$bunny_cdn/$bunny_cdn_path/$compose_file"),
|
$pool->purge("$bunny_cdn/$bunny_cdn_path/$compose_file"),
|
||||||
$pool->purge("$bunny_cdn/$bunny_cdn_path/$compose_file_prod"),
|
$pool->purge("$bunny_cdn/$bunny_cdn_path/$compose_file_prod"),
|
||||||
@@ -81,9 +98,9 @@ class SyncBunny extends Command
|
|||||||
$pool->purge("$bunny_cdn/$bunny_cdn_path/$install_script"),
|
$pool->purge("$bunny_cdn/$bunny_cdn_path/$install_script"),
|
||||||
$pool->purge("$bunny_cdn/$bunny_cdn_path/$versions"),
|
$pool->purge("$bunny_cdn/$bunny_cdn_path/$versions"),
|
||||||
]);
|
]);
|
||||||
echo "All files uploaded & purged...\n";
|
$this->info("All files uploaded & purged...");
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
echo $e->getMessage();
|
$this->error("Error: " . $e->getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,8 +40,6 @@ class Kernel extends ConsoleKernel
|
|||||||
private function check_resources($schedule)
|
private function check_resources($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);
|
||||||
ray($servers);
|
|
||||||
|
|
||||||
foreach ($servers as $server) {
|
foreach ($servers as $server) {
|
||||||
$schedule->job(new ContainerStatusJob($server))->everyMinute()->onOneServer();
|
$schedule->job(new ContainerStatusJob($server))->everyMinute()->onOneServer();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,12 @@
|
|||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use App\Models\EnvironmentVariable;
|
||||||
use App\Models\Project;
|
use App\Models\Project;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
|
use App\Models\Service;
|
||||||
|
use Illuminate\Support\Facades\Cache;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
class ProjectController extends Controller
|
class ProjectController extends Controller
|
||||||
{
|
{
|
||||||
@@ -41,9 +45,10 @@ class ProjectController extends Controller
|
|||||||
|
|
||||||
public function new()
|
public function new()
|
||||||
{
|
{
|
||||||
$type = request()->query('type');
|
$services = Cache::get('services', []);
|
||||||
|
$type = Str::of(request()->query('type'));
|
||||||
$destination_uuid = request()->query('destination');
|
$destination_uuid = request()->query('destination');
|
||||||
$server = requesT()->query('server');
|
$server_id = request()->query('server');
|
||||||
|
|
||||||
$project = currentTeam()->load(['projects'])->projects->where('uuid', request()->route('project_uuid'))->first();
|
$project = currentTeam()->load(['projects'])->projects->where('uuid', request()->route('project_uuid'))->first();
|
||||||
if (!$project) {
|
if (!$project) {
|
||||||
@@ -61,8 +66,70 @@ class ProjectController extends Controller
|
|||||||
'database_uuid' => $standalone_postgresql->uuid,
|
'database_uuid' => $standalone_postgresql->uuid,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
if ($type->startsWith('one-click-service-')) {
|
||||||
|
$oneClickServiceName = $type->after('one-click-service-')->value();
|
||||||
|
$oneClickService = data_get($services, "$oneClickServiceName.compose");
|
||||||
|
$oneClickDotEnvs = data_get($services, "$oneClickServiceName.envs", null);
|
||||||
|
if ($oneClickDotEnvs) {
|
||||||
|
$oneClickDotEnvs = Str::of(base64_decode($oneClickDotEnvs))->split('/\r\n|\r|\n/');
|
||||||
|
}
|
||||||
|
if ($oneClickService) {
|
||||||
|
$service = Service::create([
|
||||||
|
'name' => "$oneClickServiceName-" . Str::random(10),
|
||||||
|
'docker_compose_raw' => base64_decode($oneClickService),
|
||||||
|
'environment_id' => $environment->id,
|
||||||
|
'server_id' => (int) $server_id,
|
||||||
|
]);
|
||||||
|
$service->name = "$oneClickServiceName-" . $service->uuid;
|
||||||
|
$service->save();
|
||||||
|
if ($oneClickDotEnvs?->count() > 0) {
|
||||||
|
$oneClickDotEnvs->each(function ($value) use ($service) {
|
||||||
|
$key = Str::before($value, '=');
|
||||||
|
$value = Str::of(Str::after($value, '='));
|
||||||
|
$generatedValue = $value;
|
||||||
|
if ($value->contains('SERVICE_')) {
|
||||||
|
$command = $value->after('SERVICE_')->beforeLast('_');
|
||||||
|
switch ($command->value()) {
|
||||||
|
case 'PASSWORD':
|
||||||
|
$generatedValue = Str::password(symbols: false);
|
||||||
|
break;
|
||||||
|
case 'PASSWORD_64':
|
||||||
|
$generatedValue = Str::password(length: 64, symbols: false);
|
||||||
|
break;
|
||||||
|
case 'BASE64_64':
|
||||||
|
$generatedValue = Str::random(64);
|
||||||
|
break;
|
||||||
|
case 'BASE64_128':
|
||||||
|
$generatedValue = Str::random(128);
|
||||||
|
break;
|
||||||
|
case 'BASE64':
|
||||||
|
$generatedValue = Str::random(32);
|
||||||
|
break;
|
||||||
|
case 'USER':
|
||||||
|
$generatedValue = Str::random(16);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EnvironmentVariable::create([
|
||||||
|
'key' => $key,
|
||||||
|
'value' => $generatedValue,
|
||||||
|
'service_id' => $service->id,
|
||||||
|
'is_build_time' => false,
|
||||||
|
'is_preview' => false,
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
$service->parse(isNew: true);
|
||||||
|
|
||||||
|
return redirect()->route('project.service', [
|
||||||
|
'service_uuid' => $service->uuid,
|
||||||
|
'environment_name' => $environment->name,
|
||||||
|
'project_uuid' => $project->uuid,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
return view('project.new', [
|
return view('project.new', [
|
||||||
'type' => $type
|
'type' => $type->value()
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ class Form extends Component
|
|||||||
$this->destination->delete();
|
$this->destination->delete();
|
||||||
return redirect()->route('dashboard');
|
return redirect()->route('dashboard');
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ class StandaloneDocker extends Component
|
|||||||
|
|
||||||
private function createNetworkAndAttachToProxy()
|
private function createNetworkAndAttachToProxy()
|
||||||
{
|
{
|
||||||
instant_remote_process(['docker network create --attachable ' . $this->network], $this->server, throwError: false);
|
$connectProxyToDockerNetworks = connectProxyToNetworks($this->server);
|
||||||
instant_remote_process(["docker network connect $this->network coolify-proxy"], $this->server, throwError: false);
|
instant_remote_process($connectProxyToDockerNetworks, $this->server, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ namespace App\Http\Livewire;
|
|||||||
|
|
||||||
use DanHarrin\LivewireRateLimiting\WithRateLimiting;
|
use DanHarrin\LivewireRateLimiting\WithRateLimiting;
|
||||||
use Illuminate\Notifications\Messages\MailMessage;
|
use Illuminate\Notifications\Messages\MailMessage;
|
||||||
|
use Illuminate\Support\Facades\Route;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
use Route;
|
|
||||||
|
|
||||||
class Help extends Component
|
class Help extends Component
|
||||||
{
|
{
|
||||||
@@ -28,7 +28,7 @@ class Help extends Component
|
|||||||
public function submit()
|
public function submit()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$this->rateLimit(1, 60);
|
$this->rateLimit(3, 60);
|
||||||
$this->validate();
|
$this->validate();
|
||||||
$subscriptionType = auth()->user()?->subscription?->type() ?? 'Free';
|
$subscriptionType = auth()->user()?->subscription?->type() ?? 'Free';
|
||||||
$debug = "Route: {$this->path}";
|
$debug = "Route: {$this->path}";
|
||||||
@@ -42,7 +42,7 @@ class Help extends Component
|
|||||||
);
|
);
|
||||||
$mail->subject("[HELP - {$subscriptionType}]: {$this->subject}");
|
$mail->subject("[HELP - {$subscriptionType}]: {$this->subject}");
|
||||||
send_user_an_email($mail, auth()->user()?->email, 'hi@coollabs.io');
|
send_user_an_email($mail, auth()->user()?->email, 'hi@coollabs.io');
|
||||||
$this->emit('success', 'Your message has been sent successfully. We will get in touch with you as soon as possible.');
|
$this->emit('success', 'Your message has been sent successfully. <br>We will get in touch with you as soon as possible.');
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,15 +4,18 @@ namespace App\Http\Livewire\Project\Application;
|
|||||||
|
|
||||||
use App\Models\Application;
|
use App\Models\Application;
|
||||||
use App\Models\InstanceSettings;
|
use App\Models\InstanceSettings;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
use Spatie\Url\Url;
|
use Spatie\Url\Url;
|
||||||
|
use Symfony\Component\Yaml\Yaml;
|
||||||
|
|
||||||
class General extends Component
|
class General extends Component
|
||||||
{
|
{
|
||||||
public string $applicationId;
|
public string $applicationId;
|
||||||
|
|
||||||
public Application $application;
|
public Application $application;
|
||||||
|
public Collection $services;
|
||||||
public string $name;
|
public string $name;
|
||||||
public string|null $fqdn;
|
public string|null $fqdn;
|
||||||
public string $git_repository;
|
public string $git_repository;
|
||||||
@@ -31,6 +34,7 @@ class General extends Component
|
|||||||
public bool $is_auto_deploy_enabled;
|
public bool $is_auto_deploy_enabled;
|
||||||
public bool $is_force_https_enabled;
|
public bool $is_force_https_enabled;
|
||||||
|
|
||||||
|
|
||||||
protected $rules = [
|
protected $rules = [
|
||||||
'application.name' => 'required',
|
'application.name' => 'required',
|
||||||
'application.description' => 'nullable',
|
'application.description' => 'nullable',
|
||||||
@@ -66,6 +70,7 @@ class General extends Component
|
|||||||
'application.ports_exposes' => 'Ports exposes',
|
'application.ports_exposes' => 'Ports exposes',
|
||||||
'application.ports_mappings' => 'Ports mappings',
|
'application.ports_mappings' => 'Ports mappings',
|
||||||
'application.dockerfile' => 'Dockerfile',
|
'application.dockerfile' => 'Dockerfile',
|
||||||
|
|
||||||
];
|
];
|
||||||
|
|
||||||
public function instantSave()
|
public function instantSave()
|
||||||
@@ -86,8 +91,8 @@ class General extends Component
|
|||||||
$this->application->settings->save();
|
$this->application->settings->save();
|
||||||
$this->application->save();
|
$this->application->save();
|
||||||
$this->application->refresh();
|
$this->application->refresh();
|
||||||
$this->emit('success', 'Application settings updated!');
|
|
||||||
$this->checkWildCardDomain();
|
$this->checkWildCardDomain();
|
||||||
|
$this->emit('success', 'Application settings updated!');
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function checkWildCardDomain()
|
protected function checkWildCardDomain()
|
||||||
@@ -136,7 +141,6 @@ class General extends Component
|
|||||||
|
|
||||||
public function submit()
|
public function submit()
|
||||||
{
|
{
|
||||||
ray($this->application);
|
|
||||||
try {
|
try {
|
||||||
$this->validate();
|
$this->validate();
|
||||||
if (data_get($this->application, 'fqdn')) {
|
if (data_get($this->application, 'fqdn')) {
|
||||||
@@ -145,7 +149,7 @@ class General extends Component
|
|||||||
});
|
});
|
||||||
$this->application->fqdn = $domains->implode(',');
|
$this->application->fqdn = $domains->implode(',');
|
||||||
}
|
}
|
||||||
if ($this->application->dockerfile) {
|
if (data_get($this->application, 'dockerfile')) {
|
||||||
$port = get_port_from_dockerfile($this->application->dockerfile);
|
$port = get_port_from_dockerfile($this->application->dockerfile);
|
||||||
if ($port) {
|
if ($port) {
|
||||||
$this->application->ports_exposes = $port;
|
$this->application->ports_exposes = $port;
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ class Heading extends Component
|
|||||||
|
|
||||||
public function check_status()
|
public function check_status()
|
||||||
{
|
{
|
||||||
dispatch_sync(new ContainerStatusJob($this->application->destination->server));
|
dispatch(new ContainerStatusJob($this->application->destination->server));
|
||||||
$this->application->refresh();
|
$this->application->refresh();
|
||||||
$this->application->previews->each(function ($preview) {
|
$this->application->previews->each(function ($preview) {
|
||||||
$preview->refresh();
|
$preview->refresh();
|
||||||
@@ -64,7 +64,7 @@ class Heading extends Component
|
|||||||
foreach ($containers as $container) {
|
foreach ($containers as $container) {
|
||||||
$containerName = data_get($container, 'Names');
|
$containerName = data_get($container, 'Names');
|
||||||
if ($containerName) {
|
if ($containerName) {
|
||||||
remote_process(
|
instant_remote_process(
|
||||||
["docker rm -f {$containerName}"],
|
["docker rm -f {$containerName}"],
|
||||||
$this->application->destination->server
|
$this->application->destination->server
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ class Previews extends Component
|
|||||||
public function stop(int $pull_request_id)
|
public function stop(int $pull_request_id)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$container_name = generateApplicationContainerName($this->application->uuid, $pull_request_id);
|
$container_name = generateApplicationContainerName($this->application);
|
||||||
ray('Stopping container: ' . $container_name);
|
ray('Stopping container: ' . $container_name);
|
||||||
|
|
||||||
instant_remote_process(["docker rm -f $container_name"], $this->application->destination->server, throwError: false);
|
instant_remote_process(["docker rm -f $container_name"], $this->application->destination->server, throwError: false);
|
||||||
|
|||||||
143
app/Http/Livewire/Project/New/DockerCompose.php
Normal file
143
app/Http/Livewire/Project/New/DockerCompose.php
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Livewire\Project\New;
|
||||||
|
|
||||||
|
use App\Models\EnvironmentVariable;
|
||||||
|
use App\Models\Project;
|
||||||
|
use App\Models\Service;
|
||||||
|
use Livewire\Component;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
use Symfony\Component\Yaml\Yaml;
|
||||||
|
|
||||||
|
class DockerCompose extends Component
|
||||||
|
{
|
||||||
|
public string $dockerComposeRaw = '';
|
||||||
|
public string $envFile = '';
|
||||||
|
public array $parameters;
|
||||||
|
public array $query;
|
||||||
|
public function mount()
|
||||||
|
{
|
||||||
|
|
||||||
|
$this->parameters = get_route_parameters();
|
||||||
|
$this->query = request()->query();
|
||||||
|
if (isDev()) {
|
||||||
|
$this->dockerComposeRaw = 'services:
|
||||||
|
ghost:
|
||||||
|
image: ghost:5
|
||||||
|
volumes:
|
||||||
|
- ~/configs:/etc/configs/:ro
|
||||||
|
- ./var/lib/ghost/content:/tmp/ghost2/content:ro
|
||||||
|
- /var/lib/ghost/content:/tmp/ghost/content:rw
|
||||||
|
- ghost-content-data:/var/lib/ghost/content
|
||||||
|
- type: volume
|
||||||
|
source: mydata
|
||||||
|
target: /data
|
||||||
|
volume:
|
||||||
|
nocopy: true
|
||||||
|
- type: bind
|
||||||
|
source: ./var/lib/ghost/data
|
||||||
|
target: /data
|
||||||
|
- type: bind
|
||||||
|
source: /tmp
|
||||||
|
target: /tmp
|
||||||
|
labels:
|
||||||
|
- "test.label=true"
|
||||||
|
ports:
|
||||||
|
- "3000"
|
||||||
|
- "3000-3005"
|
||||||
|
- "8000:8000"
|
||||||
|
- "9090-9091:8080-8081"
|
||||||
|
- "49100:22"
|
||||||
|
- "127.0.0.1:8001:8001"
|
||||||
|
- "127.0.0.1:5000-5010:5000-5010"
|
||||||
|
- "127.0.0.1::5000"
|
||||||
|
- "6060:6060/udp"
|
||||||
|
- "12400-12500:1240"
|
||||||
|
- target: 80
|
||||||
|
published: 8080
|
||||||
|
protocol: tcp
|
||||||
|
mode: host
|
||||||
|
networks:
|
||||||
|
- some-network
|
||||||
|
- other-network
|
||||||
|
environment:
|
||||||
|
- database__client=${DATABASE_CLIENT:-mysql}
|
||||||
|
- database__connection__database=${MYSQL_DATABASE:-ghost}
|
||||||
|
- database__connection__host=${DATABASE_CONNECTION_HOST:-mysql}
|
||||||
|
- test=${TEST:?true}
|
||||||
|
- url=$SERVICE_FQDN_GHOST
|
||||||
|
- database__connection__user=$SERVICE_USER_MYSQL
|
||||||
|
- database__connection__password=$SERVICE_PASSWORD_MYSQL
|
||||||
|
depends_on:
|
||||||
|
- mysql
|
||||||
|
mysql:
|
||||||
|
image: mysql:8.0
|
||||||
|
volumes:
|
||||||
|
- ghost-mysql-data:/var/lib/mysql
|
||||||
|
environment:
|
||||||
|
- MYSQL_USER=${SERVICE_USER_MYSQL}
|
||||||
|
- MYSQL_PASSWORD=${SERVICE_PASSWORD_MYSQL}
|
||||||
|
- MYSQL_DATABASE=$MYSQL_DATABASE
|
||||||
|
- MYSQL_ROOT_PASSWORD=${SERVICE_PASSWORD_MYSQLROOT}
|
||||||
|
- SESSION_SECRET
|
||||||
|
minio:
|
||||||
|
image: minio/minio
|
||||||
|
environment:
|
||||||
|
RACK_ENV: development
|
||||||
|
A: $A
|
||||||
|
SHOW: ${SHOW}
|
||||||
|
SHOW1: ${SHOW2-show1}
|
||||||
|
SHOW2: ${SHOW3:-show2}
|
||||||
|
SHOW3: ${SHOW4?show3}
|
||||||
|
SHOW4: ${SHOW5:?show4}
|
||||||
|
SHOW5: ${SERVICE_USER_MINIO}
|
||||||
|
SHOW6: ${SERVICE_PASSWORD_MINIO}
|
||||||
|
SHOW7: ${SERVICE_PASSWORD_64_MINIO}
|
||||||
|
SHOW8: ${SERVICE_BASE64_64_MINIO}
|
||||||
|
SHOW9: ${SERVICE_BASE64_128_MINIO}
|
||||||
|
SHOW10: ${SERVICE_BASE64_MINIO}
|
||||||
|
SHOW11:
|
||||||
|
';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function submit()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$this->validate([
|
||||||
|
'dockerComposeRaw' => 'required'
|
||||||
|
]);
|
||||||
|
$this->dockerComposeRaw = Yaml::dump(Yaml::parse($this->dockerComposeRaw), 10, 2, Yaml::DUMP_MULTI_LINE_LITERAL_BLOCK);
|
||||||
|
$server_id = $this->query['server_id'];
|
||||||
|
|
||||||
|
$project = Project::where('uuid', $this->parameters['project_uuid'])->first();
|
||||||
|
$environment = $project->load(['environments'])->environments->where('name', $this->parameters['environment_name'])->first();
|
||||||
|
$service = Service::create([
|
||||||
|
'name' => 'service' . Str::random(10),
|
||||||
|
'docker_compose_raw' => $this->dockerComposeRaw,
|
||||||
|
'environment_id' => $environment->id,
|
||||||
|
'server_id' => (int) $server_id,
|
||||||
|
]);
|
||||||
|
$variables = parseEnvFormatToArray($this->envFile);
|
||||||
|
foreach ($variables as $key => $variable) {
|
||||||
|
EnvironmentVariable::create([
|
||||||
|
'key' => $key,
|
||||||
|
'value' => $variable,
|
||||||
|
'is_build_time' => false,
|
||||||
|
'is_preview' => false,
|
||||||
|
'service_id' => $service->id,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
$service->name = "service-$service->uuid";
|
||||||
|
|
||||||
|
$service->parse(isNew: true);
|
||||||
|
|
||||||
|
return redirect()->route('project.service', [
|
||||||
|
'service_uuid' => $service->uuid,
|
||||||
|
'environment_name' => $environment->name,
|
||||||
|
'project_uuid' => $project->uuid,
|
||||||
|
]);
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
return handleError($e, $this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,8 +9,8 @@ use App\Models\StandaloneDocker;
|
|||||||
use App\Models\SwarmDocker;
|
use App\Models\SwarmDocker;
|
||||||
use App\Traits\SaveFromRedirect;
|
use App\Traits\SaveFromRedirect;
|
||||||
use Illuminate\Support\Facades\Http;
|
use Illuminate\Support\Facades\Http;
|
||||||
|
use Illuminate\Support\Facades\Route;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
use Route;
|
|
||||||
|
|
||||||
class GithubPrivateRepository extends Component
|
class GithubPrivateRepository extends Component
|
||||||
{
|
{
|
||||||
@@ -40,21 +40,6 @@ class GithubPrivateRepository extends Component
|
|||||||
public string|null $publish_directory = null;
|
public string|null $publish_directory = null;
|
||||||
protected int $page = 1;
|
protected int $page = 1;
|
||||||
|
|
||||||
// public function saveFromRedirect(string $route, ?Collection $parameters = null){
|
|
||||||
// session()->forget('from');
|
|
||||||
// if (!$parameters || $parameters->count() === 0) {
|
|
||||||
// $parameters = $this->parameters;
|
|
||||||
// }
|
|
||||||
// $parameters = collect($parameters) ?? collect([]);
|
|
||||||
// $queries = collect($this->query) ?? collect([]);
|
|
||||||
// $parameters = $parameters->merge($queries);
|
|
||||||
// session(['from'=> [
|
|
||||||
// 'back'=> $this->currentRoute,
|
|
||||||
// 'route' => $route,
|
|
||||||
// 'parameters' => $parameters
|
|
||||||
// ]]);
|
|
||||||
// return redirect()->route($route);
|
|
||||||
// }
|
|
||||||
|
|
||||||
public function mount()
|
public function mount()
|
||||||
{
|
{
|
||||||
@@ -159,6 +144,11 @@ class GithubPrivateRepository extends Component
|
|||||||
$application->settings->is_static = $this->is_static;
|
$application->settings->is_static = $this->is_static;
|
||||||
$application->settings->save();
|
$application->settings->save();
|
||||||
|
|
||||||
|
$sslip = sslip($destination->server);
|
||||||
|
$application->fqdn = "http://{$application->uuid}.$sslip";
|
||||||
|
$application->name = generate_application_name($this->selected_repository_owner . '/' . $this->selected_repository_repo, $this->selected_branch_name, $application->uuid);
|
||||||
|
$application->save();
|
||||||
|
|
||||||
redirect()->route('project.application.configuration', [
|
redirect()->route('project.application.configuration', [
|
||||||
'application_uuid' => $application->uuid,
|
'application_uuid' => $application->uuid,
|
||||||
'environment_name' => $environment->name,
|
'environment_name' => $environment->name,
|
||||||
|
|||||||
@@ -112,6 +112,11 @@ class GithubPrivateRepositoryDeployKey extends Component
|
|||||||
$application->settings->is_static = $this->is_static;
|
$application->settings->is_static = $this->is_static;
|
||||||
$application->settings->save();
|
$application->settings->save();
|
||||||
|
|
||||||
|
$sslip = sslip($destination->server);
|
||||||
|
$application->fqdn = "http://{$application->uuid}.$sslip";
|
||||||
|
$application->name = generate_random_name($application->uuid);
|
||||||
|
$application->save();
|
||||||
|
|
||||||
return redirect()->route('project.application.configuration', [
|
return redirect()->route('project.application.configuration', [
|
||||||
'project_uuid' => $project->uuid,
|
'project_uuid' => $project->uuid,
|
||||||
'environment_name' => $environment->name,
|
'environment_name' => $environment->name,
|
||||||
|
|||||||
@@ -137,7 +137,6 @@ class PublicGitRepository extends Component
|
|||||||
$project = Project::where('uuid', $project_uuid)->first();
|
$project = Project::where('uuid', $project_uuid)->first();
|
||||||
$environment = $project->load(['environments'])->environments->where('name', $environment_name)->first();
|
$environment = $project->load(['environments'])->environments->where('name', $environment_name)->first();
|
||||||
|
|
||||||
|
|
||||||
$application_init = [
|
$application_init = [
|
||||||
'name' => generate_application_name($this->git_repository, $this->git_branch),
|
'name' => generate_application_name($this->git_repository, $this->git_branch),
|
||||||
'git_repository' => $this->git_repository,
|
'git_repository' => $this->git_repository,
|
||||||
@@ -153,9 +152,15 @@ class PublicGitRepository extends Component
|
|||||||
];
|
];
|
||||||
|
|
||||||
$application = Application::create($application_init);
|
$application = Application::create($application_init);
|
||||||
|
|
||||||
$application->settings->is_static = $this->is_static;
|
$application->settings->is_static = $this->is_static;
|
||||||
$application->settings->save();
|
$application->settings->save();
|
||||||
|
|
||||||
|
$sslip = sslip($destination->server);
|
||||||
|
$application->fqdn = "http://{$application->uuid}.$sslip";
|
||||||
|
$application->name = generate_application_name($this->git_repository, $this->git_branch, $application->uuid);
|
||||||
|
$application->save();
|
||||||
|
|
||||||
return redirect()->route('project.application.configuration', [
|
return redirect()->route('project.application.configuration', [
|
||||||
'project_uuid' => $project->uuid,
|
'project_uuid' => $project->uuid,
|
||||||
'environment_name' => $environment->name,
|
'environment_name' => $environment->name,
|
||||||
|
|||||||
@@ -3,12 +3,12 @@
|
|||||||
namespace App\Http\Livewire\Project\New;
|
namespace App\Http\Livewire\Project\New;
|
||||||
|
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use App\Models\StandaloneDocker;
|
|
||||||
use App\Models\SwarmDocker;
|
|
||||||
use Countable;
|
use Countable;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
|
use Illuminate\Support\Facades\Cache;
|
||||||
|
use Illuminate\Support\Facades\File;
|
||||||
|
use Illuminate\Support\Facades\Http;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
use Route;
|
|
||||||
|
|
||||||
class Select extends Component
|
class Select extends Component
|
||||||
{
|
{
|
||||||
@@ -17,16 +17,20 @@ class Select extends Component
|
|||||||
public string $type;
|
public string $type;
|
||||||
public string $server_id;
|
public string $server_id;
|
||||||
public string $destination_uuid;
|
public string $destination_uuid;
|
||||||
public Countable|array|Server $servers;
|
public Countable|array|Server $servers = [];
|
||||||
public Collection|array $standaloneDockers = [];
|
public Collection|array $standaloneDockers = [];
|
||||||
public Collection|array $swarmDockers = [];
|
public Collection|array $swarmDockers = [];
|
||||||
public array $parameters;
|
public array $parameters;
|
||||||
|
public Collection|array $services = [];
|
||||||
|
public bool $loadingServices = true;
|
||||||
|
public bool $loading = false;
|
||||||
|
|
||||||
public ?string $existingPostgresqlUrl = null;
|
public ?string $existingPostgresqlUrl = null;
|
||||||
|
|
||||||
protected $queryString = [
|
protected $queryString = [
|
||||||
'server',
|
'server',
|
||||||
];
|
];
|
||||||
|
|
||||||
public function mount()
|
public function mount()
|
||||||
{
|
{
|
||||||
$this->parameters = get_route_parameters();
|
$this->parameters = get_route_parameters();
|
||||||
@@ -44,9 +48,50 @@ class Select extends Component
|
|||||||
// return handleError($e, $this);
|
// return handleError($e, $this);
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
public function loadThings()
|
||||||
|
{
|
||||||
|
$this->loadServices();
|
||||||
|
$this->loadServers();
|
||||||
|
}
|
||||||
|
public function loadServices(bool $forceReload = false)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
if ($forceReload) {
|
||||||
|
Cache::forget('services');
|
||||||
|
}
|
||||||
|
if (isDev()) {
|
||||||
|
$cached = Cache::remember('services', 3600, function () {
|
||||||
|
$services = File::get(base_path('templates/service-templates.json'));
|
||||||
|
$services = collect(json_decode($services))->sortKeys();
|
||||||
|
$this->emit('success', 'Successfully reloaded services from filesystem (development mode).');
|
||||||
|
return $services;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
$cached = Cache::remember('services', 3600, function () {
|
||||||
|
$services = Http::get(config('constants.services.official'));
|
||||||
|
if ($services->failed()) {
|
||||||
|
throw new \Exception($services->body());
|
||||||
|
}
|
||||||
|
|
||||||
|
$services = collect($services->json())->sortKeys();
|
||||||
|
$this->emit('success', 'Successfully reloaded services from the internet.');
|
||||||
|
return $services;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
$this->services = $cached;
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
ray($e);
|
||||||
|
return handleError($e, $this);
|
||||||
|
} finally {
|
||||||
|
$this->loadingServices = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
public function setType(string $type)
|
public function setType(string $type)
|
||||||
{
|
{
|
||||||
$this->type = $type;
|
$this->type = $type;
|
||||||
|
if ($this->loading) return;
|
||||||
|
$this->loading = true;
|
||||||
if ($type === "existing-postgresql") {
|
if ($type === "existing-postgresql") {
|
||||||
$this->current_step = $type;
|
$this->current_step = $type;
|
||||||
return;
|
return;
|
||||||
@@ -83,10 +128,11 @@ class Select extends Component
|
|||||||
'environment_name' => $this->parameters['environment_name'],
|
'environment_name' => $this->parameters['environment_name'],
|
||||||
'type' => $this->type,
|
'type' => $this->type,
|
||||||
'destination' => $this->destination_uuid,
|
'destination' => $this->destination_uuid,
|
||||||
|
'server_id' => $this->server_id,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function load_servers()
|
public function loadServers()
|
||||||
{
|
{
|
||||||
$this->servers = Server::isUsable()->get();
|
$this->servers = Server::isUsable()->get();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,8 +59,11 @@ CMD ["nginx", "-g", "daemon off;"]
|
|||||||
'source_id' => 0,
|
'source_id' => 0,
|
||||||
'source_type' => GithubApp::class
|
'source_type' => GithubApp::class
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
$sslip = sslip($destination->server);
|
||||||
$application->update([
|
$application->update([
|
||||||
'name' => 'dockerfile-' . $application->id
|
'name' => 'dockerfile-' . $application->uuid,
|
||||||
|
'fqdn' => "http://{$application->uuid}.$sslip"
|
||||||
]);
|
]);
|
||||||
|
|
||||||
redirect()->route('project.application.configuration', [
|
redirect()->route('project.application.configuration', [
|
||||||
|
|||||||
56
app/Http/Livewire/Project/Service/Application.php
Normal file
56
app/Http/Livewire/Project/Service/Application.php
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Livewire\Project\Service;
|
||||||
|
|
||||||
|
use App\Models\ServiceApplication;
|
||||||
|
use Livewire\Component;
|
||||||
|
|
||||||
|
class Application extends Component
|
||||||
|
{
|
||||||
|
public ServiceApplication $application;
|
||||||
|
public $parameters;
|
||||||
|
protected $rules = [
|
||||||
|
'application.human_name' => 'nullable',
|
||||||
|
'application.description' => 'nullable',
|
||||||
|
'application.fqdn' => 'nullable',
|
||||||
|
'application.image' => 'required',
|
||||||
|
'application.exclude_from_status' => 'required|boolean',
|
||||||
|
'application.required_fqdn' => 'required|boolean',
|
||||||
|
];
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
return view('livewire.project.service.application');
|
||||||
|
}
|
||||||
|
public function instantSave()
|
||||||
|
{
|
||||||
|
$this->submit();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function delete()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$this->application->delete();
|
||||||
|
$this->emit('success', 'Application deleted successfully.');
|
||||||
|
return redirect()->route('project.service', $this->parameters);
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
return handleError($e, $this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function mount()
|
||||||
|
{
|
||||||
|
$this->parameters = get_route_parameters();
|
||||||
|
}
|
||||||
|
public function submit()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$this->validate();
|
||||||
|
$this->application->save();
|
||||||
|
updateCompose($this->application);
|
||||||
|
$this->emit('success', 'Application saved successfully.');
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
return handleError($e, $this);
|
||||||
|
} finally {
|
||||||
|
$this->emit('generateDockerCompose');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
46
app/Http/Livewire/Project/Service/Database.php
Normal file
46
app/Http/Livewire/Project/Service/Database.php
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Livewire\Project\Service;
|
||||||
|
|
||||||
|
use App\Models\ServiceDatabase;
|
||||||
|
use Livewire\Component;
|
||||||
|
|
||||||
|
class Database extends Component
|
||||||
|
{
|
||||||
|
public ServiceDatabase $database;
|
||||||
|
public $fileStorages;
|
||||||
|
protected $listeners = ["refreshFileStorages"];
|
||||||
|
protected $rules = [
|
||||||
|
'database.human_name' => 'nullable',
|
||||||
|
'database.description' => 'nullable',
|
||||||
|
'database.image' => 'required',
|
||||||
|
'database.exclude_from_status' => 'required|boolean',
|
||||||
|
];
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
return view('livewire.project.service.database');
|
||||||
|
}
|
||||||
|
public function mount() {
|
||||||
|
$this->refreshFileStorages();
|
||||||
|
}
|
||||||
|
public function instantSave() {
|
||||||
|
$this->submit();
|
||||||
|
}
|
||||||
|
public function refreshFileStorages()
|
||||||
|
{
|
||||||
|
$this->fileStorages = $this->database->fileStorages()->get();
|
||||||
|
}
|
||||||
|
public function submit()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$this->validate();
|
||||||
|
$this->database->save();
|
||||||
|
updateCompose($this->database);
|
||||||
|
$this->emit('success', 'Database saved successfully.');
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
ray($e);
|
||||||
|
} finally {
|
||||||
|
$this->emit('generateDockerCompose');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
53
app/Http/Livewire/Project/Service/FileStorage.php
Normal file
53
app/Http/Livewire/Project/Service/FileStorage.php
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Livewire\Project\Service;
|
||||||
|
|
||||||
|
use App\Models\LocalFileVolume;
|
||||||
|
use App\Models\ServiceApplication;
|
||||||
|
use App\Models\ServiceDatabase;
|
||||||
|
use Livewire\Component;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
|
class FileStorage extends Component
|
||||||
|
{
|
||||||
|
public LocalFileVolume $fileStorage;
|
||||||
|
public ServiceApplication|ServiceDatabase $service;
|
||||||
|
public string $fs_path;
|
||||||
|
|
||||||
|
protected $rules = [
|
||||||
|
'fileStorage.is_directory' => 'required',
|
||||||
|
'fileStorage.fs_path' => 'required',
|
||||||
|
'fileStorage.mount_path' => 'required',
|
||||||
|
'fileStorage.content' => 'nullable',
|
||||||
|
];
|
||||||
|
public function mount()
|
||||||
|
{
|
||||||
|
$this->service = $this->fileStorage->service;
|
||||||
|
$this->fs_path = Str::of($this->fileStorage->fs_path)->beforeLast('/');
|
||||||
|
$file = Str::of($this->fileStorage->fs_path)->afterLast('/');
|
||||||
|
if (Str::of($this->fs_path)->startsWith('.')) {
|
||||||
|
$this->fs_path = Str::of($this->fs_path)->after('.');
|
||||||
|
$this->fs_path = $this->service->service->workdir() . $this->fs_path . "/" . $file;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
public function submit()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$this->validate();
|
||||||
|
$this->fileStorage->save();
|
||||||
|
$this->service->saveFileVolumes();
|
||||||
|
$this->emit('success', 'File updated successfully.');
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
return handleError($e, $this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function instantSave()
|
||||||
|
{
|
||||||
|
$this->submit();
|
||||||
|
}
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
return view('livewire.project.service.file-storage');
|
||||||
|
}
|
||||||
|
}
|
||||||
64
app/Http/Livewire/Project/Service/Index.php
Normal file
64
app/Http/Livewire/Project/Service/Index.php
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Livewire\Project\Service;
|
||||||
|
|
||||||
|
use App\Jobs\ContainerStatusJob;
|
||||||
|
use App\Models\Service;
|
||||||
|
use DanHarrin\LivewireRateLimiting\WithRateLimiting;
|
||||||
|
use Livewire\Component;
|
||||||
|
|
||||||
|
class Index extends Component
|
||||||
|
{
|
||||||
|
public Service $service;
|
||||||
|
public $applications;
|
||||||
|
public $databases;
|
||||||
|
public array $parameters;
|
||||||
|
public array $query;
|
||||||
|
protected $rules = [
|
||||||
|
'service.docker_compose_raw' => 'required',
|
||||||
|
'service.docker_compose' => 'required',
|
||||||
|
'service.name' => 'required',
|
||||||
|
'service.description' => 'nullable',
|
||||||
|
];
|
||||||
|
public function checkStatus() {
|
||||||
|
dispatch_sync(new ContainerStatusJob($this->service->server));
|
||||||
|
$this->refreshStack();
|
||||||
|
}
|
||||||
|
public function refreshStack()
|
||||||
|
{
|
||||||
|
$this->applications = $this->service->applications->sort();
|
||||||
|
$this->applications->each(function ($application) {
|
||||||
|
$application->refresh();
|
||||||
|
});
|
||||||
|
$this->databases = $this->service->databases->sort();
|
||||||
|
$this->databases->each(function ($database) {
|
||||||
|
$database->refresh();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
public function mount()
|
||||||
|
{
|
||||||
|
$this->parameters = get_route_parameters();
|
||||||
|
$this->query = request()->query();
|
||||||
|
$this->service = Service::whereUuid($this->parameters['service_uuid'])->firstOrFail();
|
||||||
|
$this->refreshStack();
|
||||||
|
}
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
return view('livewire.project.service.index');
|
||||||
|
}
|
||||||
|
public function submit()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$this->validate();
|
||||||
|
$this->service->save();
|
||||||
|
$this->service->parse();
|
||||||
|
$this->service->refresh();
|
||||||
|
$this->service->saveComposeConfigs();
|
||||||
|
$this->refreshStack();
|
||||||
|
$this->emit('refreshEnvs');
|
||||||
|
$this->emit('success', 'Service saved successfully.');
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
return handleError($e, $this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
16
app/Http/Livewire/Project/Service/Modal.php
Normal file
16
app/Http/Livewire/Project/Service/Modal.php
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Livewire\Project\Service;
|
||||||
|
|
||||||
|
use Livewire\Component;
|
||||||
|
|
||||||
|
class Modal extends Component
|
||||||
|
{
|
||||||
|
public function serviceStatusUpdated() {
|
||||||
|
$this->emit('serviceStatusUpdated');
|
||||||
|
}
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
return view('livewire.project.service.modal');
|
||||||
|
}
|
||||||
|
}
|
||||||
44
app/Http/Livewire/Project/Service/Navbar.php
Normal file
44
app/Http/Livewire/Project/Service/Navbar.php
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Livewire\Project\Service;
|
||||||
|
|
||||||
|
use App\Actions\Service\StartService;
|
||||||
|
use App\Actions\Service\StopService;
|
||||||
|
use App\Jobs\ContainerStatusJob;
|
||||||
|
use App\Models\Service;
|
||||||
|
use Livewire\Component;
|
||||||
|
|
||||||
|
class Navbar extends Component
|
||||||
|
{
|
||||||
|
public Service $service;
|
||||||
|
public array $parameters;
|
||||||
|
public array $query;
|
||||||
|
protected $listeners = ['serviceStatusUpdated'];
|
||||||
|
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
return view('livewire.project.service.navbar');
|
||||||
|
}
|
||||||
|
public function serviceStatusUpdated()
|
||||||
|
{
|
||||||
|
ray('serviceStatusUpdated');
|
||||||
|
$this->check_status();
|
||||||
|
}
|
||||||
|
public function check_status()
|
||||||
|
{
|
||||||
|
dispatch_sync(new ContainerStatusJob($this->service->server));
|
||||||
|
$this->service->refresh();
|
||||||
|
}
|
||||||
|
public function deploy()
|
||||||
|
{
|
||||||
|
$this->service->parse();
|
||||||
|
$activity = StartService::run($this->service);
|
||||||
|
$this->emit('newMonitorActivity', $activity->id);
|
||||||
|
}
|
||||||
|
public function stop()
|
||||||
|
{
|
||||||
|
StopService::run($this->service);
|
||||||
|
$this->service->refresh();
|
||||||
|
$this->emit('success', 'Service stopped successfully.');
|
||||||
|
}
|
||||||
|
}
|
||||||
42
app/Http/Livewire/Project/Service/Show.php
Normal file
42
app/Http/Livewire/Project/Service/Show.php
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Livewire\Project\Service;
|
||||||
|
|
||||||
|
use App\Models\Service;
|
||||||
|
use App\Models\ServiceApplication;
|
||||||
|
use App\Models\ServiceDatabase;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
use Livewire\Component;
|
||||||
|
|
||||||
|
class Show extends Component
|
||||||
|
{
|
||||||
|
public Service $service;
|
||||||
|
public ServiceApplication $serviceApplication;
|
||||||
|
public ServiceDatabase $serviceDatabase;
|
||||||
|
public array $parameters;
|
||||||
|
public array $query;
|
||||||
|
public Collection $services;
|
||||||
|
protected $listeners = ['generateDockerCompose'];
|
||||||
|
|
||||||
|
public function mount()
|
||||||
|
{
|
||||||
|
$this->services = collect([]);
|
||||||
|
$this->parameters = get_route_parameters();
|
||||||
|
$this->query = request()->query();
|
||||||
|
$this->service = Service::whereUuid($this->parameters['service_uuid'])->firstOrFail();
|
||||||
|
$service = $this->service->applications()->whereName($this->parameters['service_name'])->first();
|
||||||
|
if ($service) {
|
||||||
|
$this->serviceApplication = $service;
|
||||||
|
} else {
|
||||||
|
$this->serviceDatabase = $this->service->databases()->whereName($this->parameters['service_name'])->first();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function generateDockerCompose()
|
||||||
|
{
|
||||||
|
$this->service->parse();
|
||||||
|
}
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
return view('livewire.project.service.show');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Http\Livewire\Project\Shared;
|
namespace App\Http\Livewire\Project\Shared;
|
||||||
|
|
||||||
|
use App\Actions\Service\StopService;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
use Visus\Cuid2\Cuid2;
|
use Visus\Cuid2\Cuid2;
|
||||||
|
|
||||||
@@ -19,13 +20,26 @@ class Danger extends Component
|
|||||||
|
|
||||||
public function delete()
|
public function delete()
|
||||||
{
|
{
|
||||||
|
// Should be queued
|
||||||
|
try {
|
||||||
|
if ($this->resource->type() === 'service') {
|
||||||
|
$server = $this->resource->server;
|
||||||
|
StopService::run($this->resource);
|
||||||
|
} else {
|
||||||
|
$destination = data_get($this->resource, 'destination');
|
||||||
|
if ($destination) {
|
||||||
$destination = $this->resource->destination->getMorphClass()::where('id', $this->resource->destination->id)->first();
|
$destination = $this->resource->destination->getMorphClass()::where('id', $this->resource->destination->id)->first();
|
||||||
|
$server = $destination->server;
|
||||||
instant_remote_process(["docker rm -f {$this->resource->uuid}"], $destination->server);
|
}
|
||||||
|
instant_remote_process(["docker rm -f {$this->resource->uuid}"], $server);
|
||||||
|
}
|
||||||
$this->resource->delete();
|
$this->resource->delete();
|
||||||
return redirect()->route('project.resources', [
|
return redirect()->route('project.resources', [
|
||||||
'project_uuid' => $this->parameters['project_uuid'],
|
'project_uuid' => $this->parameters['project_uuid'],
|
||||||
'environment_name' => $this->parameters['environment_name']
|
'environment_name' => $this->parameters['environment_name']
|
||||||
]);
|
]);
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
return handleError($e, $this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,13 +9,13 @@ class Add extends Component
|
|||||||
public $parameters;
|
public $parameters;
|
||||||
public bool $is_preview = false;
|
public bool $is_preview = false;
|
||||||
public string $key;
|
public string $key;
|
||||||
public string $value;
|
public ?string $value = null;
|
||||||
public bool $is_build_time = false;
|
public bool $is_build_time = false;
|
||||||
|
|
||||||
protected $listeners = ['clearAddEnv' => 'clear'];
|
protected $listeners = ['clearAddEnv' => 'clear'];
|
||||||
protected $rules = [
|
protected $rules = [
|
||||||
'key' => 'required|string',
|
'key' => 'required|string',
|
||||||
'value' => 'required|string',
|
'value' => 'nullable',
|
||||||
'is_build_time' => 'required|boolean',
|
'is_build_time' => 'required|boolean',
|
||||||
];
|
];
|
||||||
protected $validationAttributes = [
|
protected $validationAttributes = [
|
||||||
@@ -31,8 +31,8 @@ class Add extends Component
|
|||||||
|
|
||||||
public function submit()
|
public function submit()
|
||||||
{
|
{
|
||||||
ray('submitting');
|
|
||||||
$this->validate();
|
$this->validate();
|
||||||
|
ray($this->key, $this->value, $this->is_build_time);
|
||||||
$this->emitUp('submit', [
|
$this->emitUp('submit', [
|
||||||
'key' => $this->key,
|
'key' => $this->key,
|
||||||
'value' => $this->value,
|
'value' => $this->value,
|
||||||
|
|||||||
@@ -68,11 +68,16 @@ class All extends Component
|
|||||||
$environment->value = $variable;
|
$environment->value = $variable;
|
||||||
$environment->is_build_time = false;
|
$environment->is_build_time = false;
|
||||||
$environment->is_preview = $isPreview ? true : false;
|
$environment->is_preview = $isPreview ? true : false;
|
||||||
if ($this->resource->type() === 'application') {
|
switch ($this->resource->type()) {
|
||||||
|
case 'application':
|
||||||
$environment->application_id = $this->resource->id;
|
$environment->application_id = $this->resource->id;
|
||||||
}
|
break;
|
||||||
if ($this->resource->type() === 'standalone-postgresql') {
|
case 'standalone-postgresql':
|
||||||
$environment->standalone_postgresql_id = $this->resource->id;
|
$environment->standalone_postgresql_id = $this->resource->id;
|
||||||
|
break;
|
||||||
|
case 'service':
|
||||||
|
$environment->service_id = $this->resource->id;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
$environment->save();
|
$environment->save();
|
||||||
}
|
}
|
||||||
@@ -104,11 +109,16 @@ class All extends Component
|
|||||||
$environment->is_build_time = $data['is_build_time'];
|
$environment->is_build_time = $data['is_build_time'];
|
||||||
$environment->is_preview = $data['is_preview'];
|
$environment->is_preview = $data['is_preview'];
|
||||||
|
|
||||||
if ($this->resource->type() === 'application') {
|
switch ($this->resource->type()) {
|
||||||
|
case 'application':
|
||||||
$environment->application_id = $this->resource->id;
|
$environment->application_id = $this->resource->id;
|
||||||
}
|
break;
|
||||||
if ($this->resource->type() === 'standalone-postgresql') {
|
case 'standalone-postgresql':
|
||||||
$environment->standalone_postgresql_id = $this->resource->id;
|
$environment->standalone_postgresql_id = $this->resource->id;
|
||||||
|
break;
|
||||||
|
case 'service':
|
||||||
|
$environment->service_id = $this->resource->id;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
$environment->save();
|
$environment->save();
|
||||||
$this->refreshEnvs();
|
$this->refreshEnvs();
|
||||||
|
|||||||
@@ -5,15 +5,19 @@ namespace App\Http\Livewire\Project\Shared\EnvironmentVariable;
|
|||||||
use App\Models\EnvironmentVariable as ModelsEnvironmentVariable;
|
use App\Models\EnvironmentVariable as ModelsEnvironmentVariable;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
use Visus\Cuid2\Cuid2;
|
use Visus\Cuid2\Cuid2;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
class Show extends Component
|
class Show extends Component
|
||||||
{
|
{
|
||||||
public $parameters;
|
public $parameters;
|
||||||
public ModelsEnvironmentVariable $env;
|
public ModelsEnvironmentVariable $env;
|
||||||
public string|null $modalId = null;
|
public ?string $modalId = null;
|
||||||
|
public bool $isDisabled = false;
|
||||||
|
public string $type;
|
||||||
|
|
||||||
protected $rules = [
|
protected $rules = [
|
||||||
'env.key' => 'required|string',
|
'env.key' => 'required|string',
|
||||||
'env.value' => 'required|string',
|
'env.value' => 'nullable',
|
||||||
'env.is_build_time' => 'required|boolean',
|
'env.is_build_time' => 'required|boolean',
|
||||||
];
|
];
|
||||||
protected $validationAttributes = [
|
protected $validationAttributes = [
|
||||||
@@ -24,6 +28,10 @@ class Show extends Component
|
|||||||
|
|
||||||
public function mount()
|
public function mount()
|
||||||
{
|
{
|
||||||
|
$this->isDisabled = false;
|
||||||
|
if (Str::of($this->env->key)->startsWith('SERVICE_FQDN') || Str::of($this->env->key)->startsWith('SERVICE_URL')) {
|
||||||
|
$this->isDisabled = true;
|
||||||
|
}
|
||||||
$this->modalId = new Cuid2(7);
|
$this->modalId = new Cuid2(7);
|
||||||
$this->parameters = get_route_parameters();
|
$this->parameters = get_route_parameters();
|
||||||
}
|
}
|
||||||
@@ -37,6 +45,7 @@ class Show extends Component
|
|||||||
$this->validate();
|
$this->validate();
|
||||||
$this->env->save();
|
$this->env->save();
|
||||||
$this->emit('success', 'Environment variable updated successfully.');
|
$this->emit('success', 'Environment variable updated successfully.');
|
||||||
|
$this->emit('refreshEnvs');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function delete()
|
public function delete()
|
||||||
|
|||||||
39
app/Http/Livewire/Project/Shared/HealthChecks.php
Normal file
39
app/Http/Livewire/Project/Shared/HealthChecks.php
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Livewire\Project\Shared;
|
||||||
|
|
||||||
|
use Livewire\Component;
|
||||||
|
|
||||||
|
class HealthChecks extends Component
|
||||||
|
{
|
||||||
|
|
||||||
|
public $resource;
|
||||||
|
protected $rules = [
|
||||||
|
'resource.health_check_path' => 'string',
|
||||||
|
'resource.health_check_port' => 'nullable|string',
|
||||||
|
'resource.health_check_host' => 'string',
|
||||||
|
'resource.health_check_method' => 'string',
|
||||||
|
'resource.health_check_return_code' => 'integer',
|
||||||
|
'resource.health_check_scheme' => 'string',
|
||||||
|
'resource.health_check_response_text' => 'nullable|string',
|
||||||
|
'resource.health_check_interval' => 'integer',
|
||||||
|
'resource.health_check_timeout' => 'integer',
|
||||||
|
'resource.health_check_retries' => 'integer',
|
||||||
|
'resource.health_check_start_period' => 'integer',
|
||||||
|
|
||||||
|
];
|
||||||
|
public function submit()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$this->validate();
|
||||||
|
$this->resource->save();
|
||||||
|
$this->emit('saved');
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
return handleError($e, $this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
return view('livewire.project.shared.health-checks');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,13 +2,17 @@
|
|||||||
|
|
||||||
namespace App\Http\Livewire\Project\Shared\Storages;
|
namespace App\Http\Livewire\Project\Shared\Storages;
|
||||||
|
|
||||||
|
use App\Models\LocalPersistentVolume;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
use Visus\Cuid2\Cuid2;
|
use Visus\Cuid2\Cuid2;
|
||||||
|
|
||||||
class Show extends Component
|
class Show extends Component
|
||||||
{
|
{
|
||||||
public $storage;
|
public LocalPersistentVolume $storage;
|
||||||
public string|null $modalId = null;
|
public bool $isReadOnly = false;
|
||||||
|
public ?string $modalId = null;
|
||||||
|
public ?string $realName = null;
|
||||||
|
|
||||||
protected $rules = [
|
protected $rules = [
|
||||||
'storage.name' => 'required|string',
|
'storage.name' => 'required|string',
|
||||||
'storage.mount_path' => 'required|string',
|
'storage.mount_path' => 'required|string',
|
||||||
@@ -22,6 +26,11 @@ class Show extends Component
|
|||||||
|
|
||||||
public function mount()
|
public function mount()
|
||||||
{
|
{
|
||||||
|
if ($this->storage->resource_type === 'App\Models\ServiceApplication' || $this->storage->resource_type === 'App\Models\ServiceDatabase') {
|
||||||
|
$this->realName = "{$this->storage->service->service->uuid}_{$this->storage->name}";
|
||||||
|
} else {
|
||||||
|
$this->realName = $this->storage->name;
|
||||||
|
}
|
||||||
$this->modalId = new Cuid2(7);
|
$this->modalId = new Cuid2(7);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ class Form extends Component
|
|||||||
'server.ip' => 'required',
|
'server.ip' => 'required',
|
||||||
'server.user' => 'required',
|
'server.user' => 'required',
|
||||||
'server.port' => 'required',
|
'server.port' => 'required',
|
||||||
|
'server.settings.is_cloudflare_tunnel' => 'required',
|
||||||
'server.settings.is_reachable' => 'required',
|
'server.settings.is_reachable' => 'required',
|
||||||
'server.settings.is_part_of_swarm' => 'required',
|
'server.settings.is_part_of_swarm' => 'required',
|
||||||
'wildcard_domain' => 'nullable|url',
|
'wildcard_domain' => 'nullable|url',
|
||||||
@@ -33,6 +34,7 @@ class Form extends Component
|
|||||||
'server.ip' => 'ip',
|
'server.ip' => 'ip',
|
||||||
'server.user' => 'user',
|
'server.user' => 'user',
|
||||||
'server.port' => 'port',
|
'server.port' => 'port',
|
||||||
|
'server.settings.is_cloudflare_tunnel' => 'Cloudflare Tunnel',
|
||||||
'server.settings.is_reachable' => 'is reachable',
|
'server.settings.is_reachable' => 'is reachable',
|
||||||
'server.settings.is_part_of_swarm' => 'is part of swarm'
|
'server.settings.is_part_of_swarm' => 'is part of swarm'
|
||||||
];
|
];
|
||||||
@@ -42,7 +44,11 @@ class Form extends Component
|
|||||||
$this->wildcard_domain = $this->server->settings->wildcard_domain;
|
$this->wildcard_domain = $this->server->settings->wildcard_domain;
|
||||||
$this->cleanup_after_percentage = $this->server->settings->cleanup_after_percentage;
|
$this->cleanup_after_percentage = $this->server->settings->cleanup_after_percentage;
|
||||||
}
|
}
|
||||||
|
public function instantSave() {
|
||||||
|
refresh_server_connection($this->server->privateKey);
|
||||||
|
$this->validateServer();
|
||||||
|
$this->server->settings->save();
|
||||||
|
}
|
||||||
public function installDocker()
|
public function installDocker()
|
||||||
{
|
{
|
||||||
$this->dockerInstallationStarted = true;
|
$this->dockerInstallationStarted = true;
|
||||||
@@ -58,21 +64,19 @@ class Form extends Component
|
|||||||
$this->uptime = $uptime;
|
$this->uptime = $uptime;
|
||||||
$this->emit('success', 'Server is reachable.');
|
$this->emit('success', 'Server is reachable.');
|
||||||
} else {
|
} else {
|
||||||
ray($this->uptime);
|
|
||||||
|
|
||||||
$this->emit('error', 'Server is not reachable.');
|
$this->emit('error', 'Server is not reachable.');
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if ($dockerVersion) {
|
if ($dockerVersion) {
|
||||||
$this->dockerVersion = $dockerVersion;
|
$this->dockerVersion = $dockerVersion;
|
||||||
$this->emit('proxyStatusUpdated');
|
|
||||||
$this->emit('success', 'Docker Engine 23+ is installed!');
|
$this->emit('success', 'Docker Engine 23+ is installed!');
|
||||||
} else {
|
} else {
|
||||||
$this->emit('error', 'No Docker Engine or older than 23 version installed.');
|
$this->emit('error', 'No Docker Engine or older than 23 version installed.');
|
||||||
}
|
}
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this, customErrorMessage: "Server is not reachable: ");
|
return handleError($e, $this, customErrorMessage: "Server is not reachable: ");
|
||||||
|
} finally {
|
||||||
|
$this->emit('proxyStatusUpdated');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ class ByIp extends Component
|
|||||||
$server->settings->save();
|
$server->settings->save();
|
||||||
return redirect()->route('server.show', $server->uuid);
|
return redirect()->route('server.show', $server->uuid);
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ use App\Actions\Proxy\CheckConfiguration;
|
|||||||
use App\Actions\Proxy\SaveConfiguration;
|
use App\Actions\Proxy\SaveConfiguration;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
class Proxy extends Component
|
class Proxy extends Component
|
||||||
{
|
{
|
||||||
@@ -37,8 +38,8 @@ class Proxy extends Component
|
|||||||
|
|
||||||
public function select_proxy($proxy_type)
|
public function select_proxy($proxy_type)
|
||||||
{
|
{
|
||||||
$this->server->proxy->type = $proxy_type;
|
$this->server->proxy->set('status', 'exited');
|
||||||
$this->server->proxy->status = 'exited';
|
$this->server->proxy->set('type', $proxy_type);
|
||||||
$this->server->save();
|
$this->server->save();
|
||||||
$this->selectedProxy = $this->server->proxy->type;
|
$this->selectedProxy = $this->server->proxy->type;
|
||||||
$this->emit('proxyStatusUpdated');
|
$this->emit('proxyStatusUpdated');
|
||||||
@@ -47,14 +48,14 @@ class Proxy extends Component
|
|||||||
public function submit()
|
public function submit()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
SaveConfiguration::run($this->server);
|
SaveConfiguration::run($this->server, $this->proxy_settings);
|
||||||
$this->server->proxy->redirect_url = $this->redirect_url;
|
$this->server->proxy->redirect_url = $this->redirect_url;
|
||||||
$this->server->save();
|
$this->server->save();
|
||||||
|
|
||||||
setup_default_redirect_404(redirect_url: $this->server->proxy->redirect_url, server: $this->server);
|
setup_default_redirect_404(redirect_url: $this->server->proxy->redirect_url, server: $this->server);
|
||||||
$this->emit('success', 'Proxy configuration saved.');
|
$this->emit('success', 'Proxy configuration saved.');
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,7 +64,7 @@ class Proxy extends Component
|
|||||||
try {
|
try {
|
||||||
$this->proxy_settings = CheckConfiguration::run($this->server, true);
|
$this->proxy_settings = CheckConfiguration::run($this->server, true);
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,8 +72,14 @@ class Proxy extends Component
|
|||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$this->proxy_settings = CheckConfiguration::run($this->server);
|
$this->proxy_settings = CheckConfiguration::run($this->server);
|
||||||
|
if (Str::of($this->proxy_settings)->contains('--api.dashboard=true') && Str::of($this->proxy_settings)->contains('--api.insecure=true')) {
|
||||||
|
$this->emit('traefikDashboardAvailable', true);
|
||||||
|
} else {
|
||||||
|
$this->emit('traefikDashboardAvailable', false);
|
||||||
|
}
|
||||||
|
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
namespace App\Http\Livewire\Server\Proxy;
|
namespace App\Http\Livewire\Server\Proxy;
|
||||||
|
|
||||||
use App\Actions\Proxy\SaveConfiguration;
|
|
||||||
use App\Actions\Proxy\StartProxy;
|
use App\Actions\Proxy\StartProxy;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
@@ -10,9 +9,16 @@ use Livewire\Component;
|
|||||||
class Deploy extends Component
|
class Deploy extends Component
|
||||||
{
|
{
|
||||||
public Server $server;
|
public Server $server;
|
||||||
public $proxy_settings = null;
|
public bool $traefikDashboardAvailable = false;
|
||||||
protected $listeners = ['proxyStatusUpdated'];
|
public ?string $currentRoute = null;
|
||||||
|
protected $listeners = ['proxyStatusUpdated', 'traefikDashboardAvailable'];
|
||||||
|
|
||||||
|
public function mount() {
|
||||||
|
$this->currentRoute = request()->route()->getName();
|
||||||
|
}
|
||||||
|
public function traefikDashboardAvailable(bool $data) {
|
||||||
|
$this->traefikDashboardAvailable = $data;
|
||||||
|
}
|
||||||
public function proxyStatusUpdated()
|
public function proxyStatusUpdated()
|
||||||
{
|
{
|
||||||
$this->server->refresh();
|
$this->server->refresh();
|
||||||
@@ -20,17 +26,10 @@ class Deploy extends Component
|
|||||||
public function startProxy()
|
public function startProxy()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
if (
|
|
||||||
$this->server->proxy->last_applied_settings &&
|
|
||||||
$this->server->proxy->last_saved_settings !== $this->server->proxy->last_applied_settings
|
|
||||||
) {
|
|
||||||
SaveConfiguration::run($this->server);
|
|
||||||
}
|
|
||||||
|
|
||||||
$activity = StartProxy::run($this->server);
|
$activity = StartProxy::run($this->server);
|
||||||
$this->emit('newMonitorActivity', $activity->id);
|
$this->emit('newMonitorActivity', $activity->id);
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ class Modal extends Component
|
|||||||
|
|
||||||
public function proxyStatusUpdated()
|
public function proxyStatusUpdated()
|
||||||
{
|
{
|
||||||
|
$this->server->proxy->set('status', 'running');
|
||||||
|
$this->server->save();
|
||||||
$this->emit('proxyStatusUpdated');
|
$this->emit('proxyStatusUpdated');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,12 +18,10 @@ class Status extends Component
|
|||||||
public function getProxyStatus()
|
public function getProxyStatus()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
if ($this->server->isFunctional()) {
|
|
||||||
dispatch_sync(new ContainerStatusJob($this->server));
|
dispatch_sync(new ContainerStatusJob($this->server));
|
||||||
$this->emit('proxyStatusUpdated');
|
$this->emit('proxyStatusUpdated');
|
||||||
}
|
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public function getProxyStatusWithNoti()
|
public function getProxyStatusWithNoti()
|
||||||
|
|||||||
@@ -45,7 +45,12 @@ class Configuration extends Component
|
|||||||
$this->settings->do_not_track = $this->do_not_track;
|
$this->settings->do_not_track = $this->do_not_track;
|
||||||
$this->settings->is_auto_update_enabled = $this->is_auto_update_enabled;
|
$this->settings->is_auto_update_enabled = $this->is_auto_update_enabled;
|
||||||
$this->settings->is_registration_enabled = $this->is_registration_enabled;
|
$this->settings->is_registration_enabled = $this->is_registration_enabled;
|
||||||
|
if ($this->next_channel) {
|
||||||
|
$this->settings->next_channel = false;
|
||||||
|
$this->next_channel = false;
|
||||||
|
} else {
|
||||||
$this->settings->next_channel = $this->next_channel;
|
$this->settings->next_channel = $this->next_channel;
|
||||||
|
}
|
||||||
$this->settings->save();
|
$this->settings->save();
|
||||||
$this->emit('success', 'Settings updated!');
|
$this->emit('success', 'Settings updated!');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
|
|
||||||
private int $application_deployment_queue_id;
|
private int $application_deployment_queue_id;
|
||||||
|
|
||||||
|
private bool $newVersionIsHealthy = false;
|
||||||
private ApplicationDeploymentQueue $application_deployment_queue;
|
private ApplicationDeploymentQueue $application_deployment_queue;
|
||||||
private Application $application;
|
private Application $application;
|
||||||
private string $deployment_uuid;
|
private string $deployment_uuid;
|
||||||
@@ -88,7 +89,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
$this->build_workdir = "{$this->workdir}" . rtrim($this->application->base_directory, '/');
|
$this->build_workdir = "{$this->workdir}" . rtrim($this->application->base_directory, '/');
|
||||||
$this->is_debug_enabled = $this->application->settings->is_debug_enabled;
|
$this->is_debug_enabled = $this->application->settings->is_debug_enabled;
|
||||||
|
|
||||||
$this->container_name = generateApplicationContainerName($this->application->uuid, $this->pull_request_id);
|
$this->container_name = generateApplicationContainerName($this->application);
|
||||||
savePrivateKeyToFs($this->server);
|
savePrivateKeyToFs($this->server);
|
||||||
$this->saved_outputs = collect();
|
$this->saved_outputs = collect();
|
||||||
|
|
||||||
@@ -96,7 +97,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
if ($this->pull_request_id !== 0) {
|
if ($this->pull_request_id !== 0) {
|
||||||
$this->preview = ApplicationPreview::findPreviewByApplicationAndPullId($this->application->id, $this->pull_request_id);
|
$this->preview = ApplicationPreview::findPreviewByApplicationAndPullId($this->application->id, $this->pull_request_id);
|
||||||
if ($this->application->fqdn) {
|
if ($this->application->fqdn) {
|
||||||
$preview_fqdn = data_get($this->preview, 'fqdn');
|
$preview_fqdn = getFqdnWithoutPort(data_get($this->preview, 'fqdn'));
|
||||||
$template = $this->application->preview_url_template;
|
$template = $this->application->preview_url_template;
|
||||||
$url = Url::fromString($this->application->fqdn);
|
$url = Url::fromString($this->application->fqdn);
|
||||||
$host = $url->getHost();
|
$host = $url->getHost();
|
||||||
@@ -166,6 +167,54 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
private function deploy_docker_compose()
|
||||||
|
{
|
||||||
|
$dockercompose_base64 = base64_encode($this->application->dockercompose);
|
||||||
|
$this->execute_remote_command(
|
||||||
|
[
|
||||||
|
"echo 'Starting deployment of {$this->application->name}.'"
|
||||||
|
],
|
||||||
|
);
|
||||||
|
$this->prepare_builder_image();
|
||||||
|
$this->execute_remote_command(
|
||||||
|
[
|
||||||
|
executeInDocker($this->deployment_uuid, "echo '$dockercompose_base64' | base64 -d > $this->workdir/docker-compose.yaml")
|
||||||
|
],
|
||||||
|
);
|
||||||
|
$this->build_image_name = Str::lower("{$this->application->git_repository}:build");
|
||||||
|
$this->production_image_name = Str::lower("{$this->application->uuid}:latest");
|
||||||
|
$this->save_environment_variables();
|
||||||
|
$containers = getCurrentApplicationContainerStatus($this->application->destination->server, $this->application->id);
|
||||||
|
if ($containers->count() > 0) {
|
||||||
|
foreach ($containers as $container) {
|
||||||
|
$containerName = data_get($container, 'Names');
|
||||||
|
if ($containerName) {
|
||||||
|
instant_remote_process(
|
||||||
|
["docker rm -f {$containerName}"],
|
||||||
|
$this->application->destination->server
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->execute_remote_command(
|
||||||
|
["echo -n 'Starting services (could take a while)...'"],
|
||||||
|
[executeInDocker($this->deployment_uuid, "docker compose --project-directory {$this->workdir} up -d"), "hidden" => true],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
private function save_environment_variables()
|
||||||
|
{
|
||||||
|
$envs = collect([]);
|
||||||
|
foreach ($this->application->environment_variables as $env) {
|
||||||
|
$envs->push($env->key . '=' . $env->value);
|
||||||
|
}
|
||||||
|
$envs_base64 = base64_encode($envs->implode("\n"));
|
||||||
|
$this->execute_remote_command(
|
||||||
|
[
|
||||||
|
executeInDocker($this->deployment_uuid, "echo '$envs_base64' | base64 -d > $this->workdir/.env")
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
private function deploy_simple_dockerfile()
|
private function deploy_simple_dockerfile()
|
||||||
{
|
{
|
||||||
$dockerfile_base64 = base64_encode($this->application->dockerfile);
|
$dockerfile_base64 = base64_encode($this->application->dockerfile);
|
||||||
@@ -235,10 +284,21 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
|
|
||||||
private function rolling_update()
|
private function rolling_update()
|
||||||
{
|
{
|
||||||
|
if (count($this->application->ports_mappings_array) > 0){
|
||||||
|
$this->execute_remote_command(
|
||||||
|
["echo -n 'Application has ports mapped to the host system, rolling update is not supported. Stopping current container.'"],
|
||||||
|
);
|
||||||
|
$this->stop_running_container(force: true);
|
||||||
|
$this->start_by_compose_file();
|
||||||
|
} else {
|
||||||
|
$this->execute_remote_command(
|
||||||
|
["echo -n 'Rolling update started.'"],
|
||||||
|
);
|
||||||
$this->start_by_compose_file();
|
$this->start_by_compose_file();
|
||||||
$this->health_check();
|
$this->health_check();
|
||||||
$this->stop_running_container();
|
$this->stop_running_container();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
private function health_check()
|
private function health_check()
|
||||||
{
|
{
|
||||||
ray('New container name: ', $this->container_name);
|
ray('New container name: ', $this->container_name);
|
||||||
@@ -267,7 +327,11 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
],
|
],
|
||||||
);
|
);
|
||||||
if (Str::of($this->saved_outputs->get('health_check'))->contains('healthy')) {
|
if (Str::of($this->saved_outputs->get('health_check'))->contains('healthy')) {
|
||||||
|
$this->newVersionIsHealthy = true;
|
||||||
$this->execute_remote_command(
|
$this->execute_remote_command(
|
||||||
|
[
|
||||||
|
"echo 'New version of your application is healthy.'"
|
||||||
|
],
|
||||||
[
|
[
|
||||||
"echo 'Rolling update completed.'"
|
"echo 'Rolling update completed.'"
|
||||||
],
|
],
|
||||||
@@ -475,7 +539,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
'container_name' => $this->container_name,
|
'container_name' => $this->container_name,
|
||||||
'restart' => RESTART_MODE,
|
'restart' => RESTART_MODE,
|
||||||
'environment' => $environment_variables,
|
'environment' => $environment_variables,
|
||||||
'labels' => $this->set_labels_for_applications(),
|
'labels' => generateLabelsApplication($this->application, $this->preview),
|
||||||
'expose' => $ports,
|
'expose' => $ports,
|
||||||
'networks' => [
|
'networks' => [
|
||||||
$this->destination->network,
|
$this->destination->network,
|
||||||
@@ -577,75 +641,6 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
return $environment_variables->all();
|
return $environment_variables->all();
|
||||||
}
|
}
|
||||||
|
|
||||||
private function set_labels_for_applications()
|
|
||||||
{
|
|
||||||
|
|
||||||
$appId = $this->application->id;
|
|
||||||
if ($this->pull_request_id !== 0) {
|
|
||||||
$appId = $appId . '-pr-' . $this->pull_request_id;
|
|
||||||
}
|
|
||||||
$labels = [];
|
|
||||||
$labels[] = 'coolify.managed=true';
|
|
||||||
$labels[] = 'coolify.version=' . config('version');
|
|
||||||
$labels[] = 'coolify.applicationId=' . $appId;
|
|
||||||
$labels[] = 'coolify.type=application';
|
|
||||||
$labels[] = 'coolify.name=' . $this->application->name;
|
|
||||||
if ($this->pull_request_id !== 0) {
|
|
||||||
$labels[] = 'coolify.pullRequestId=' . $this->pull_request_id;
|
|
||||||
}
|
|
||||||
if ($this->application->fqdn) {
|
|
||||||
if ($this->pull_request_id !== 0) {
|
|
||||||
$domains = Str::of(data_get($this->preview, 'fqdn'))->explode(',');
|
|
||||||
} else {
|
|
||||||
$domains = Str::of(data_get($this->application, 'fqdn'))->explode(',');
|
|
||||||
}
|
|
||||||
if ($this->application->destination->server->proxy->type === ProxyTypes::TRAEFIK_V2->value) {
|
|
||||||
$labels[] = 'traefik.enable=true';
|
|
||||||
foreach ($domains as $domain) {
|
|
||||||
$url = Url::fromString($domain);
|
|
||||||
$host = $url->getHost();
|
|
||||||
$path = $url->getPath();
|
|
||||||
$schema = $url->getScheme();
|
|
||||||
$slug = Str::slug($host . $path);
|
|
||||||
|
|
||||||
$http_label = "{$this->container_name}-{$slug}-http";
|
|
||||||
$https_label = "{$this->container_name}-{$slug}-https";
|
|
||||||
|
|
||||||
if ($schema === 'https') {
|
|
||||||
// Set labels for https
|
|
||||||
$labels[] = "traefik.http.routers.{$https_label}.rule=Host(`{$host}`) && PathPrefix(`{$path}`)";
|
|
||||||
$labels[] = "traefik.http.routers.{$https_label}.entryPoints=https";
|
|
||||||
$labels[] = "traefik.http.routers.{$https_label}.middlewares=gzip";
|
|
||||||
if ($path !== '/') {
|
|
||||||
$labels[] = "traefik.http.routers.{$https_label}.middlewares={$https_label}-stripprefix";
|
|
||||||
$labels[] = "traefik.http.middlewares.{$https_label}-stripprefix.stripprefix.prefixes={$path}";
|
|
||||||
}
|
|
||||||
|
|
||||||
$labels[] = "traefik.http.routers.{$https_label}.tls=true";
|
|
||||||
$labels[] = "traefik.http.routers.{$https_label}.tls.certresolver=letsencrypt";
|
|
||||||
|
|
||||||
// Set labels for http (redirect to https)
|
|
||||||
$labels[] = "traefik.http.routers.{$http_label}.rule=Host(`{$host}`) && PathPrefix(`{$path}`)";
|
|
||||||
$labels[] = "traefik.http.routers.{$http_label}.entryPoints=http";
|
|
||||||
if ($this->application->settings->is_force_https_enabled) {
|
|
||||||
$labels[] = "traefik.http.routers.{$http_label}.middlewares=redirect-to-https";
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Set labels for http
|
|
||||||
$labels[] = "traefik.http.routers.{$http_label}.rule=Host(`{$host}`) && PathPrefix(`{$path}`)";
|
|
||||||
$labels[] = "traefik.http.routers.{$http_label}.entryPoints=http";
|
|
||||||
$labels[] = "traefik.http.routers.{$http_label}.middlewares=gzip";
|
|
||||||
if ($path !== '/') {
|
|
||||||
$labels[] = "traefik.http.routers.{$http_label}.middlewares={$http_label}-stripprefix";
|
|
||||||
$labels[] = "traefik.http.middlewares.{$http_label}-stripprefix.stripprefix.prefixes={$path}";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $labels;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function generate_healthcheck_commands()
|
private function generate_healthcheck_commands()
|
||||||
{
|
{
|
||||||
if ($this->application->dockerfile || $this->application->build_pack === 'dockerfile') {
|
if ($this->application->dockerfile || $this->application->build_pack === 'dockerfile') {
|
||||||
@@ -653,15 +648,17 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
return 'exit 0';
|
return 'exit 0';
|
||||||
}
|
}
|
||||||
if (!$this->application->health_check_port) {
|
if (!$this->application->health_check_port) {
|
||||||
$this->application->health_check_port = $this->application->ports_exposes_array[0];
|
$health_check_port = $this->application->ports_exposes_array[0];
|
||||||
|
} else {
|
||||||
|
$health_check_port = $this->application->health_check_port;
|
||||||
}
|
}
|
||||||
if ($this->application->health_check_path) {
|
if ($this->application->health_check_path) {
|
||||||
$generated_healthchecks_commands = [
|
$generated_healthchecks_commands = [
|
||||||
"curl -s -X {$this->application->health_check_method} -f {$this->application->health_check_scheme}://{$this->application->health_check_host}:{$this->application->health_check_port}{$this->application->health_check_path} > /dev/null"
|
"curl -s -X {$this->application->health_check_method} -f {$this->application->health_check_scheme}://{$this->application->health_check_host}:{$health_check_port}{$this->application->health_check_path} > /dev/null"
|
||||||
];
|
];
|
||||||
} else {
|
} else {
|
||||||
$generated_healthchecks_commands = [
|
$generated_healthchecks_commands = [
|
||||||
"curl -s -X {$this->application->health_check_method} -f {$this->application->health_check_scheme}://{$this->application->health_check_host}:{$this->application->health_check_port}/"
|
"curl -s -X {$this->application->health_check_method} -f {$this->application->health_check_scheme}://{$this->application->health_check_host}:{$health_check_port}/"
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
return implode(' ', $generated_healthchecks_commands);
|
return implode(' ', $generated_healthchecks_commands);
|
||||||
@@ -718,20 +715,27 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function stop_running_container()
|
private function stop_running_container(bool $force = false)
|
||||||
{
|
{
|
||||||
if ($this->currently_running_container_name) {
|
if ($this->currently_running_container_name) {
|
||||||
|
if ($this->newVersionIsHealthy || $force) {
|
||||||
$this->execute_remote_command(
|
$this->execute_remote_command(
|
||||||
["echo -n 'Removing old version of your application.'"],
|
["echo -n 'Removing old version of your application.'"],
|
||||||
[executeInDocker($this->deployment_uuid, "docker rm -f $this->currently_running_container_name >/dev/null 2>&1"), "hidden" => true],
|
[executeInDocker($this->deployment_uuid, "docker rm -f $this->currently_running_container_name >/dev/null 2>&1"), "hidden" => true],
|
||||||
);
|
);
|
||||||
|
} else {
|
||||||
|
$this->execute_remote_command(
|
||||||
|
["echo -n 'New version is not healthy, rolling back to the old version.'"],
|
||||||
|
[executeInDocker($this->deployment_uuid, "docker rm -f $this->container_name >/dev/null 2>&1"), "hidden" => true],
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function start_by_compose_file()
|
private function start_by_compose_file()
|
||||||
{
|
{
|
||||||
$this->execute_remote_command(
|
$this->execute_remote_command(
|
||||||
["echo -n 'Rolling update started.'"],
|
["echo -n 'Starting application (could take a while).'"],
|
||||||
[executeInDocker($this->deployment_uuid, "docker compose --project-directory {$this->workdir} up -d >/dev/null"), "hidden" => true],
|
[executeInDocker($this->deployment_uuid, "docker compose --project-directory {$this->workdir} up -d >/dev/null"), "hidden" => true],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ use App\Models\Server;
|
|||||||
use App\Notifications\Container\ContainerRestarted;
|
use App\Notifications\Container\ContainerRestarted;
|
||||||
use App\Notifications\Container\ContainerStopped;
|
use App\Notifications\Container\ContainerStopped;
|
||||||
use App\Notifications\Server\Unreachable;
|
use App\Notifications\Server\Unreachable;
|
||||||
use Arr;
|
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
|
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
|
||||||
use Illuminate\Contracts\Queue\ShouldBeUnique;
|
use Illuminate\Contracts\Queue\ShouldBeUnique;
|
||||||
@@ -17,6 +16,7 @@ use Illuminate\Foundation\Bus\Dispatchable;
|
|||||||
use Illuminate\Queue\InteractsWithQueue;
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
use Illuminate\Queue\Middleware\WithoutOverlapping;
|
use Illuminate\Queue\Middleware\WithoutOverlapping;
|
||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
use Illuminate\Support\Arr;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
|
class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
|
||||||
@@ -28,11 +28,12 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
|
|
||||||
public function __construct(public Server $server)
|
public function __construct(public Server $server)
|
||||||
{
|
{
|
||||||
|
$this->handle();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function middleware(): array
|
public function middleware(): array
|
||||||
{
|
{
|
||||||
return [new WithoutOverlapping($this->server->uuid)];
|
return [(new WithoutOverlapping($this->server->uuid))->dontRelease()];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function uniqueId(): string
|
public function uniqueId(): string
|
||||||
@@ -74,13 +75,15 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
$containers = format_docker_command_output_to_json($containers);
|
$containers = format_docker_command_output_to_json($containers);
|
||||||
$applications = $this->server->applications();
|
$applications = $this->server->applications();
|
||||||
$databases = $this->server->databases();
|
$databases = $this->server->databases();
|
||||||
|
$services = $this->server->services()->get();
|
||||||
$previews = $this->server->previews();
|
$previews = $this->server->previews();
|
||||||
|
$this->server->proxyType();
|
||||||
/// Check if proxy is running
|
/// Check if proxy is running
|
||||||
$foundProxyContainer = $containers->filter(function ($value, $key) {
|
$foundProxyContainer = $containers->filter(function ($value, $key) {
|
||||||
return data_get($value, 'Name') === '/coolify-proxy';
|
return data_get($value, 'Name') === '/coolify-proxy';
|
||||||
})->first();
|
})->first();
|
||||||
if (!$foundProxyContainer) {
|
if (!$foundProxyContainer) {
|
||||||
|
ray('Proxy not found, starting it...');
|
||||||
if ($this->server->isProxyShouldRun()) {
|
if ($this->server->isProxyShouldRun()) {
|
||||||
StartProxy::run($this->server, false);
|
StartProxy::run($this->server, false);
|
||||||
$this->server->team->notify(new ContainerRestarted('coolify-proxy', $this->server));
|
$this->server->team->notify(new ContainerRestarted('coolify-proxy', $this->server));
|
||||||
@@ -88,12 +91,18 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
} else {
|
} else {
|
||||||
$this->server->proxy->status = data_get($foundProxyContainer, 'State.Status');
|
$this->server->proxy->status = data_get($foundProxyContainer, 'State.Status');
|
||||||
$this->server->save();
|
$this->server->save();
|
||||||
|
$connectProxyToDockerNetworks = connectProxyToNetworks($this->server);
|
||||||
|
instant_remote_process($connectProxyToDockerNetworks, $this->server, false);
|
||||||
}
|
}
|
||||||
$foundApplications = [];
|
$foundApplications = [];
|
||||||
$foundApplicationPreviews = [];
|
$foundApplicationPreviews = [];
|
||||||
$foundDatabases = [];
|
$foundDatabases = [];
|
||||||
|
$foundServices = [];
|
||||||
|
|
||||||
foreach ($containers as $container) {
|
foreach ($containers as $container) {
|
||||||
$containerStatus = data_get($container, 'State.Status');
|
$containerStatus = data_get($container, 'State.Status');
|
||||||
|
$containerHealth = data_get($container, 'State.Health.Status', 'unhealthy');
|
||||||
|
$containerStatus = "$containerStatus ($containerHealth)";
|
||||||
$labels = data_get($container, 'Config.Labels');
|
$labels = data_get($container, 'Config.Labels');
|
||||||
$labels = Arr::undot(format_docker_labels_to_json($labels));
|
$labels = Arr::undot(format_docker_labels_to_json($labels));
|
||||||
$labelId = data_get($labels, 'coolify.applicationId');
|
$labelId = data_get($labels, 'coolify.applicationId');
|
||||||
@@ -138,7 +147,64 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
$serviceLabelId = data_get($labels, 'coolify.serviceId');
|
||||||
|
if ($serviceLabelId) {
|
||||||
|
$subType = data_get($labels, 'coolify.service.subType');
|
||||||
|
$subId = data_get($labels, 'coolify.service.subId');
|
||||||
|
$service = $services->where('id', $serviceLabelId)->first();
|
||||||
|
if (!$service) {
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
if ($subType === 'application') {
|
||||||
|
$service = $service->applications()->where('id', $subId)->first();
|
||||||
|
} else {
|
||||||
|
$service = $service->databases()->where('id', $subId)->first();
|
||||||
|
}
|
||||||
|
if ($service) {
|
||||||
|
$foundServices[] = "$service->id-$service->name";
|
||||||
|
$statusFromDb = $service->status;
|
||||||
|
if ($statusFromDb !== $containerStatus) {
|
||||||
|
// ray('Updating status: ' . $containerStatus);
|
||||||
|
$service->update(['status' => $containerStatus]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$exitedServices = collect([]);
|
||||||
|
foreach ($services as $service) {
|
||||||
|
$apps = $service->applications()->get();
|
||||||
|
$dbs = $service->databases()->get();
|
||||||
|
foreach ($apps as $app) {
|
||||||
|
if (in_array("$app->id-$app->name", $foundServices)) {
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
$exitedServices->push($app);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach ($dbs as $db) {
|
||||||
|
if (in_array("$db->id-$db->name", $foundServices)) {
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
$exitedServices->push($db);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$exitedServices = $exitedServices->unique('id');
|
||||||
|
foreach ($exitedServices as $exitedService) {
|
||||||
|
if ($exitedService->status === 'exited') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$name = data_get($exitedService, 'name');
|
||||||
|
$fqdn = data_get($exitedService, 'fqdn');
|
||||||
|
$containerName = $name ? "$name ($fqdn)" : $fqdn;
|
||||||
|
$project = data_get($service, 'environment.project');
|
||||||
|
$environment = data_get($service, 'environment');
|
||||||
|
|
||||||
|
$url = base_url() . '/project/' . $project->uuid . "/" . $environment->name . "/service/" . $service->uuid;
|
||||||
|
$this->server->team->notify(new ContainerStopped($containerName, $this->server, $url));
|
||||||
|
$exitedService->update(['status' => 'exited']);
|
||||||
|
}
|
||||||
|
|
||||||
$notRunningApplications = $applications->pluck('id')->diff($foundApplications);
|
$notRunningApplications = $applications->pluck('id')->diff($foundApplications);
|
||||||
foreach ($notRunningApplications as $applicationId) {
|
foreach ($notRunningApplications as $applicationId) {
|
||||||
$application = $applications->where('id', $applicationId)->first();
|
$application = $applications->where('id', $applicationId)->first();
|
||||||
|
|||||||
@@ -19,7 +19,13 @@ class Application extends BaseModel
|
|||||||
});
|
});
|
||||||
static::deleting(function ($application) {
|
static::deleting(function ($application) {
|
||||||
$application->settings()->delete();
|
$application->settings()->delete();
|
||||||
|
$storages = $application->persistentStorages()->get();
|
||||||
|
foreach ($storages as $storage) {
|
||||||
|
instant_remote_process(["docker volume rm -f $storage->name"], $application->destination->server);
|
||||||
|
}
|
||||||
$application->persistentStorages()->delete();
|
$application->persistentStorages()->delete();
|
||||||
|
$application->environment_variables()->delete();
|
||||||
|
$application->environment_variables_preview()->delete();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -224,7 +230,7 @@ class Application extends BaseModel
|
|||||||
}
|
}
|
||||||
public function git_based(): bool
|
public function git_based(): bool
|
||||||
{
|
{
|
||||||
if ($this->dockerfile || $this->build_pack === 'dockerfile') {
|
if ($this->dockerfile || $this->build_pack === 'dockerfile' || $this->dockercompose || $this->build_pack === 'dockercompose') {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -33,18 +33,23 @@ class EnvironmentVariable extends Model
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
public function service() {
|
||||||
|
return $this->belongsTo(Service::class);
|
||||||
|
}
|
||||||
protected function value(): Attribute
|
protected function value(): Attribute
|
||||||
{
|
{
|
||||||
return Attribute::make(
|
return Attribute::make(
|
||||||
get: fn (string $value) => $this->get_environment_variables($value),
|
get: fn (?string $value = null) => $this->get_environment_variables($value),
|
||||||
set: fn (string $value) => $this->set_environment_variables($value),
|
set: fn (?string $value = null) => $this->set_environment_variables($value),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function get_environment_variables(string $environment_variable): string|null
|
private function get_environment_variables(?string $environment_variable = null): string|null
|
||||||
{
|
{
|
||||||
// $team_id = currentTeam()->id;
|
// $team_id = currentTeam()->id;
|
||||||
|
if (!$environment_variable) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
$environment_variable = trim(decrypt($environment_variable));
|
$environment_variable = trim(decrypt($environment_variable));
|
||||||
if (Str::startsWith($environment_variable, '{{') && Str::endsWith($environment_variable, '}}') && Str::contains($environment_variable, 'global.')) {
|
if (Str::startsWith($environment_variable, '{{') && Str::endsWith($environment_variable, '}}') && Str::contains($environment_variable, 'global.')) {
|
||||||
$variable = Str::after($environment_variable, 'global.');
|
$variable = Str::after($environment_variable, 'global.');
|
||||||
@@ -57,8 +62,11 @@ class EnvironmentVariable extends Model
|
|||||||
return $environment_variable;
|
return $environment_variable;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function set_environment_variables(string $environment_variable): string|null
|
private function set_environment_variables(?string $environment_variable = null): string|null
|
||||||
{
|
{
|
||||||
|
if (is_null($environment_variable) && $environment_variable == '') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
$environment_variable = trim($environment_variable);
|
$environment_variable = trim($environment_variable);
|
||||||
return encrypt($environment_variable);
|
return encrypt($environment_variable);
|
||||||
}
|
}
|
||||||
@@ -69,4 +77,5 @@ class EnvironmentVariable extends Model
|
|||||||
set: fn (string $value) => Str::of($value)->trim(),
|
set: fn (string $value) => Str::of($value)->trim(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
16
app/Models/LocalFileVolume.php
Normal file
16
app/Models/LocalFileVolume.php
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
|
|
||||||
|
class LocalFileVolume extends BaseModel
|
||||||
|
{
|
||||||
|
use HasFactory;
|
||||||
|
protected $guarded = [];
|
||||||
|
|
||||||
|
public function service()
|
||||||
|
{
|
||||||
|
return $this->morphTo('resource');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,12 +12,19 @@ class LocalPersistentVolume extends Model
|
|||||||
|
|
||||||
public function application()
|
public function application()
|
||||||
{
|
{
|
||||||
return $this->morphTo();
|
return $this->morphTo('resource');
|
||||||
|
}
|
||||||
|
public function service()
|
||||||
|
{
|
||||||
|
return $this->morphTo('resource');
|
||||||
|
}
|
||||||
|
public function database()
|
||||||
|
{
|
||||||
|
return $this->morphTo('resource');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function standalone_postgresql()
|
public function standalone_postgresql()
|
||||||
{
|
{
|
||||||
return $this->morphTo();
|
return $this->morphTo('resource');
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function name(): Attribute
|
protected function name(): Attribute
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
|
use App\Enums\ProxyStatus;
|
||||||
|
use App\Enums\ProxyTypes;
|
||||||
use Illuminate\Database\Eloquent\Builder;
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
use Spatie\SchemalessAttributes\Casts\SchemalessAttributes;
|
use Spatie\SchemalessAttributes\Casts\SchemalessAttributes;
|
||||||
use Spatie\SchemalessAttributes\SchemalessAttributesTrait;
|
use Spatie\SchemalessAttributes\SchemalessAttributesTrait;
|
||||||
@@ -76,6 +78,16 @@ class Server extends BaseModel
|
|||||||
return $this->hasOne(ServerSetting::class);
|
return $this->hasOne(ServerSetting::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function proxyType()
|
||||||
|
{
|
||||||
|
$type = $this->proxy->get('type');
|
||||||
|
if (is_null($type)) {
|
||||||
|
$this->proxy->type = ProxyTypes::TRAEFIK_V2->value;
|
||||||
|
$this->proxy->status = ProxyStatus::EXITED->value;
|
||||||
|
$this->save();
|
||||||
|
}
|
||||||
|
return $this->proxy->get('type');
|
||||||
|
}
|
||||||
public function scopeWithProxy(): Builder
|
public function scopeWithProxy(): Builder
|
||||||
{
|
{
|
||||||
return $this->proxy->modelScope();
|
return $this->proxy->modelScope();
|
||||||
@@ -104,8 +116,13 @@ class Server extends BaseModel
|
|||||||
return $standaloneDocker->applications;
|
return $standaloneDocker->applications;
|
||||||
})->flatten();
|
})->flatten();
|
||||||
}
|
}
|
||||||
|
public function services()
|
||||||
|
{
|
||||||
|
return $this->hasMany(Service::class);
|
||||||
|
}
|
||||||
|
|
||||||
public function previews() {
|
public function previews()
|
||||||
|
{
|
||||||
return $this->destinations()->map(function ($standaloneDocker) {
|
return $this->destinations()->map(function ($standaloneDocker) {
|
||||||
return $standaloneDocker->applications->map(function ($application) {
|
return $standaloneDocker->applications->map(function ($application) {
|
||||||
return $application->previews;
|
return $application->previews;
|
||||||
@@ -147,6 +164,9 @@ class Server extends BaseModel
|
|||||||
public function isProxyShouldRun()
|
public function isProxyShouldRun()
|
||||||
{
|
{
|
||||||
$shouldRun = false;
|
$shouldRun = false;
|
||||||
|
if ($this->proxyType() === ProxyTypes::NONE->value) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
foreach ($this->applications() as $application) {
|
foreach ($this->applications() as $application) {
|
||||||
if (data_get($application, 'fqdn')) {
|
if (data_get($application, 'fqdn')) {
|
||||||
$shouldRun = true;
|
$shouldRun = true;
|
||||||
@@ -161,7 +181,8 @@ class Server extends BaseModel
|
|||||||
}
|
}
|
||||||
return $shouldRun;
|
return $shouldRun;
|
||||||
}
|
}
|
||||||
public function isFunctional() {
|
public function isFunctional()
|
||||||
|
{
|
||||||
return $this->settings->is_reachable && $this->settings->is_usable;
|
return $this->settings->is_reachable && $this->settings->is_usable;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
507
app/Models/Service.php
Normal file
507
app/Models/Service.php
Normal file
@@ -0,0 +1,507 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
use Illuminate\Support\Facades\Cache;
|
||||||
|
use Symfony\Component\Yaml\Yaml;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
use Spatie\Url\Url;
|
||||||
|
|
||||||
|
class Service extends BaseModel
|
||||||
|
{
|
||||||
|
use HasFactory;
|
||||||
|
protected $guarded = [];
|
||||||
|
|
||||||
|
protected static function booted()
|
||||||
|
{
|
||||||
|
static::deleted(function ($service) {
|
||||||
|
$storagesToDelete = collect([]);
|
||||||
|
foreach ($service->applications()->get() as $application) {
|
||||||
|
$storages = $application->persistentStorages()->get();
|
||||||
|
foreach ($storages as $storage) {
|
||||||
|
$storagesToDelete->push($storage);
|
||||||
|
}
|
||||||
|
$application->persistentStorages()->delete();
|
||||||
|
}
|
||||||
|
foreach ($service->databases()->get() as $database) {
|
||||||
|
$storages = $database->persistentStorages()->get();
|
||||||
|
foreach ($storages as $storage) {
|
||||||
|
$storagesToDelete->push($storage);
|
||||||
|
}
|
||||||
|
$database->persistentStorages()->delete();
|
||||||
|
}
|
||||||
|
$service->environment_variables()->delete();
|
||||||
|
$service->applications()->delete();
|
||||||
|
$service->databases()->delete();
|
||||||
|
if ($storagesToDelete->count() > 0) {
|
||||||
|
$storagesToDelete->each(function ($storage) use ($service) {
|
||||||
|
instant_remote_process(["docker volume rm -f $storage->name"], $service->server, false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
instant_remote_process(["docker network rm {$service->uuid}"], $service->server, false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
public function type()
|
||||||
|
{
|
||||||
|
return 'service';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function documentation()
|
||||||
|
{
|
||||||
|
$services = Cache::get('services', []);
|
||||||
|
$service = data_get($services, Str::of($this->name)->beforeLast('-')->value, []);
|
||||||
|
return data_get($service, 'documentation', config('constants.docs.base_url'));
|
||||||
|
}
|
||||||
|
public function applications()
|
||||||
|
{
|
||||||
|
return $this->hasMany(ServiceApplication::class);
|
||||||
|
}
|
||||||
|
public function databases()
|
||||||
|
{
|
||||||
|
return $this->hasMany(ServiceDatabase::class);
|
||||||
|
}
|
||||||
|
public function environment()
|
||||||
|
{
|
||||||
|
return $this->belongsTo(Environment::class);
|
||||||
|
}
|
||||||
|
public function server()
|
||||||
|
{
|
||||||
|
return $this->belongsTo(Server::class);
|
||||||
|
}
|
||||||
|
public function byName(string $name)
|
||||||
|
{
|
||||||
|
$app = $this->applications()->whereName($name)->first();
|
||||||
|
if ($app) {
|
||||||
|
return $app;
|
||||||
|
}
|
||||||
|
$db = $this->databases()->whereName($name)->first();
|
||||||
|
if ($db) {
|
||||||
|
return $db;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
public function environment_variables(): HasMany
|
||||||
|
{
|
||||||
|
return $this->hasMany(EnvironmentVariable::class)->orderBy('key', 'asc');
|
||||||
|
}
|
||||||
|
public function workdir()
|
||||||
|
{
|
||||||
|
return service_configuration_dir() . "/{$this->uuid}";
|
||||||
|
}
|
||||||
|
public function saveComposeConfigs()
|
||||||
|
{
|
||||||
|
$workdir = $this->workdir();
|
||||||
|
$commands[] = "mkdir -p $workdir";
|
||||||
|
$commands[] = "cd $workdir";
|
||||||
|
|
||||||
|
$docker_compose_base64 = base64_encode($this->docker_compose);
|
||||||
|
$commands[] = "echo $docker_compose_base64 | base64 -d > docker-compose.yml";
|
||||||
|
$envs = $this->environment_variables()->get();
|
||||||
|
$commands[] = "rm -f .env || true";
|
||||||
|
foreach ($envs as $env) {
|
||||||
|
$commands[] = "echo '{$env->key}={$env->value}' >> .env";
|
||||||
|
}
|
||||||
|
if ($envs->count() === 0) {
|
||||||
|
$commands[] = "touch .env";
|
||||||
|
}
|
||||||
|
instant_remote_process($commands, $this->server);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function parse(bool $isNew = false): Collection
|
||||||
|
{
|
||||||
|
// ray()->clearAll();
|
||||||
|
if ($this->docker_compose_raw) {
|
||||||
|
try {
|
||||||
|
$yaml = Yaml::parse($this->docker_compose_raw);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
throw new \Exception($e->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
$topLevelVolumes = collect(data_get($yaml, 'volumes', []));
|
||||||
|
$topLevelNetworks = collect(data_get($yaml, 'networks', []));
|
||||||
|
$dockerComposeVersion = data_get($yaml, 'version') ?? '3.8';
|
||||||
|
$services = data_get($yaml, 'services');
|
||||||
|
$definedNetwork = $this->uuid;
|
||||||
|
|
||||||
|
$generatedServiceFQDNS = collect([]);
|
||||||
|
|
||||||
|
$services = collect($services)->map(function ($service, $serviceName) use ($topLevelVolumes, $topLevelNetworks, $definedNetwork, $isNew, $generatedServiceFQDNS) {
|
||||||
|
$serviceVolumes = collect(data_get($service, 'volumes', []));
|
||||||
|
$servicePorts = collect(data_get($service, 'ports', []));
|
||||||
|
$serviceNetworks = collect(data_get($service, 'networks', []));
|
||||||
|
$serviceVariables = collect(data_get($service, 'environment', []));
|
||||||
|
$serviceLabels = collect(data_get($service, 'labels', []));
|
||||||
|
|
||||||
|
$containerName = "$serviceName-{$this->uuid}";
|
||||||
|
|
||||||
|
// Decide if the service is a database
|
||||||
|
$isDatabase = false;
|
||||||
|
$image = data_get_str($service, 'image');
|
||||||
|
if ($image->contains(':')) {
|
||||||
|
$image = Str::of($image);
|
||||||
|
} else {
|
||||||
|
$image = Str::of($image)->append(':latest');
|
||||||
|
}
|
||||||
|
$imageName = $image->before(':');
|
||||||
|
|
||||||
|
if (collect(DATABASE_DOCKER_IMAGES)->contains($imageName)) {
|
||||||
|
$isDatabase = true;
|
||||||
|
}
|
||||||
|
data_set($service, 'is_database', $isDatabase);
|
||||||
|
|
||||||
|
// Create new serviceApplication or serviceDatabase
|
||||||
|
if ($isDatabase) {
|
||||||
|
if ($isNew) {
|
||||||
|
$savedService = ServiceDatabase::create([
|
||||||
|
'name' => $serviceName,
|
||||||
|
'image' => $image,
|
||||||
|
'service_id' => $this->id
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
$savedService = ServiceDatabase::where([
|
||||||
|
'name' => $serviceName,
|
||||||
|
'service_id' => $this->id
|
||||||
|
])->first();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ($isNew) {
|
||||||
|
$savedService = ServiceApplication::create([
|
||||||
|
'name' => $serviceName,
|
||||||
|
'image' => $image,
|
||||||
|
'service_id' => $this->id
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
$savedService = ServiceApplication::where([
|
||||||
|
'name' => $serviceName,
|
||||||
|
'service_id' => $this->id
|
||||||
|
])->first();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (is_null($savedService)) {
|
||||||
|
if ($isDatabase) {
|
||||||
|
$savedService = ServiceDatabase::create([
|
||||||
|
'name' => $serviceName,
|
||||||
|
'image' => $image,
|
||||||
|
'service_id' => $this->id
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
$savedService = ServiceApplication::create([
|
||||||
|
'name' => $serviceName,
|
||||||
|
'image' => $image,
|
||||||
|
'service_id' => $this->id
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collect/create/update networks
|
||||||
|
if ($serviceNetworks->count() > 0) {
|
||||||
|
foreach ($serviceNetworks as $networkName => $networkDetails) {
|
||||||
|
$networkExists = $topLevelNetworks->contains(function ($value, $key) use ($networkName) {
|
||||||
|
return $value == $networkName || $key == $networkName;
|
||||||
|
});
|
||||||
|
if (!$networkExists) {
|
||||||
|
$topLevelNetworks->put($networkDetails, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collect/create/update ports
|
||||||
|
$collectedPorts = collect([]);
|
||||||
|
if ($servicePorts->count() > 0) {
|
||||||
|
foreach ($servicePorts as $sport) {
|
||||||
|
if (is_string($sport) || is_numeric($sport)) {
|
||||||
|
$collectedPorts->push($sport);
|
||||||
|
}
|
||||||
|
if (is_array($sport)) {
|
||||||
|
$target = data_get($sport, 'target');
|
||||||
|
$published = data_get($sport, 'published');
|
||||||
|
$protocol = data_get($sport, 'protocol');
|
||||||
|
$collectedPorts->push("$target:$published/$protocol");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$savedService->ports = $collectedPorts->implode(',');
|
||||||
|
$savedService->save();
|
||||||
|
|
||||||
|
// Add Coolify specific networks
|
||||||
|
$definedNetworkExists = $topLevelNetworks->contains(function ($value, $_) use ($definedNetwork) {
|
||||||
|
return $value == $definedNetwork;
|
||||||
|
});
|
||||||
|
if (!$definedNetworkExists) {
|
||||||
|
$topLevelNetworks->put($definedNetwork, [
|
||||||
|
'name' => $definedNetwork,
|
||||||
|
'external' => true
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
$networks = $serviceNetworks->toArray();
|
||||||
|
$networks = array_merge($networks, [$definedNetwork]);
|
||||||
|
data_set($service, 'networks', $networks);
|
||||||
|
|
||||||
|
// Collect/create/update volumes
|
||||||
|
if ($serviceVolumes->count() > 0) {
|
||||||
|
foreach ($serviceVolumes as $volume) {
|
||||||
|
$type = null;
|
||||||
|
$source = null;
|
||||||
|
$target = null;
|
||||||
|
$content = null;
|
||||||
|
$isDirectory = false;
|
||||||
|
if (is_string($volume)) {
|
||||||
|
$source = Str::of($volume)->before(':');
|
||||||
|
$target = Str::of($volume)->after(':')->beforeLast(':');
|
||||||
|
if ($source->startsWith('./') || $source->startsWith('/') || $source->startsWith('~')) {
|
||||||
|
$type = Str::of('bind');
|
||||||
|
} else {
|
||||||
|
$type = Str::of('volume');
|
||||||
|
}
|
||||||
|
} else if (is_array($volume)) {
|
||||||
|
$type = data_get_str($volume, 'type');
|
||||||
|
$source = data_get_str($volume, 'source');
|
||||||
|
$target = data_get_str($volume, 'target');
|
||||||
|
$content = data_get($volume, 'content');
|
||||||
|
$isDirectory = (bool) data_get($volume, 'isDirectory', false);
|
||||||
|
$foundConfig = $savedService->fileStorages()->whereMountPath($target)->first();
|
||||||
|
if ($foundConfig) {
|
||||||
|
$content = data_get($foundConfig, 'content');
|
||||||
|
$isDirectory = (bool) data_get($foundConfig, 'is_directory');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($type->value() === 'bind') {
|
||||||
|
if ($source->value() === "/var/run/docker.sock") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ($source->value() === '/tmp' || $source->value() === '/tmp/') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
LocalFileVolume::updateOrCreate(
|
||||||
|
[
|
||||||
|
'mount_path' => $target,
|
||||||
|
'resource_id' => $savedService->id,
|
||||||
|
'resource_type' => get_class($savedService)
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'fs_path' => $source,
|
||||||
|
'mount_path' => $target,
|
||||||
|
'content' => $content,
|
||||||
|
'is_directory' => $isDirectory,
|
||||||
|
'resource_id' => $savedService->id,
|
||||||
|
'resource_type' => get_class($savedService)
|
||||||
|
]
|
||||||
|
);
|
||||||
|
} else if ($type->value() === 'volume') {
|
||||||
|
$topLevelVolumes->put($source->value(), null);
|
||||||
|
LocalPersistentVolume::updateOrCreate(
|
||||||
|
[
|
||||||
|
'mount_path' => $target,
|
||||||
|
'resource_id' => $savedService->id,
|
||||||
|
'resource_type' => get_class($savedService)
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'name' => Str::slug($source, '-'),
|
||||||
|
'mount_path' => $target,
|
||||||
|
'resource_id' => $savedService->id,
|
||||||
|
'resource_type' => get_class($savedService)
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$savedService->saveFileVolumes();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add env_file with at least .env to the service
|
||||||
|
// $envFile = collect(data_get($service, 'env_file', []));
|
||||||
|
// if ($envFile->count() > 0) {
|
||||||
|
// if (!$envFile->contains('.env')) {
|
||||||
|
// $envFile->push('.env');
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
// $envFile = collect(['.env']);
|
||||||
|
// }
|
||||||
|
// data_set($service, 'env_file', $envFile->toArray());
|
||||||
|
|
||||||
|
|
||||||
|
// Get variables from the service
|
||||||
|
foreach ($serviceVariables as $variableName => $variable) {
|
||||||
|
if (is_numeric($variableName)) {
|
||||||
|
$variable = Str::of($variable);
|
||||||
|
if ($variable->contains('=')) {
|
||||||
|
// - SESSION_SECRET=123
|
||||||
|
// - SESSION_SECRET=
|
||||||
|
$key = $variable->before('=');
|
||||||
|
$value = $variable->after('=');
|
||||||
|
} else {
|
||||||
|
// - SESSION_SECRET
|
||||||
|
$key = $variable;
|
||||||
|
$value = null;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// SESSION_SECRET: 123
|
||||||
|
// SESSION_SECRET:
|
||||||
|
$key = Str::of($variableName);
|
||||||
|
$value = Str::of($variable);
|
||||||
|
}
|
||||||
|
if ($key->startsWith('SERVICE_FQDN')) {
|
||||||
|
if (is_null(data_get($savedService, 'fqdn'))) {
|
||||||
|
$sslip = sslip($this->server);
|
||||||
|
$fqdn = "http://$containerName.$sslip";
|
||||||
|
if (substr_count($key->value(), '_') === 2 && $key->contains("=")) {
|
||||||
|
$path = $value->value();
|
||||||
|
if ($generatedServiceFQDNS->count() > 0) {
|
||||||
|
$alreadyGenerated = $generatedServiceFQDNS->has($key->value());
|
||||||
|
if ($alreadyGenerated) {
|
||||||
|
$fqdn = $generatedServiceFQDNS->get($key->value());
|
||||||
|
} else {
|
||||||
|
$generatedServiceFQDNS->put($key->value(), $fqdn);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$generatedServiceFQDNS->put($key->value(), $fqdn);
|
||||||
|
}
|
||||||
|
$fqdn = "http://$containerName.$sslip$path";
|
||||||
|
}
|
||||||
|
if (!$isDatabase) {
|
||||||
|
$savedService->fqdn = $fqdn;
|
||||||
|
$savedService->save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ($value?->startsWith('$')) {
|
||||||
|
$value = Str::of(replaceVariables($value));
|
||||||
|
$key = $value;
|
||||||
|
$foundEnv = EnvironmentVariable::where([
|
||||||
|
'key' => $key,
|
||||||
|
'service_id' => $this->id,
|
||||||
|
])->first();
|
||||||
|
if ($value->startsWith('SERVICE_')) {
|
||||||
|
$command = $value->after('SERVICE_')->beforeLast('_');
|
||||||
|
$forService = $value->afterLast('_');
|
||||||
|
$generatedValue = null;
|
||||||
|
if ($command->value() === 'FQDN' || $command->value() === 'URL') {
|
||||||
|
$sslip = sslip($this->server);
|
||||||
|
$fqdn = "http://$containerName.$sslip";
|
||||||
|
if ($foundEnv) {
|
||||||
|
$fqdn = data_get($foundEnv, 'value');
|
||||||
|
} else {
|
||||||
|
EnvironmentVariable::create([
|
||||||
|
'key' => $key,
|
||||||
|
'value' => $fqdn,
|
||||||
|
'is_build_time' => false,
|
||||||
|
'service_id' => $this->id,
|
||||||
|
'is_preview' => false,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$isDatabase) {
|
||||||
|
$savedService->fqdn = $fqdn;
|
||||||
|
$savedService->save();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
switch ($command) {
|
||||||
|
case 'PASSWORD':
|
||||||
|
$generatedValue = Str::password(symbols: false);
|
||||||
|
break;
|
||||||
|
case 'PASSWORD_64':
|
||||||
|
$generatedValue = Str::password(length: 64, symbols: false);
|
||||||
|
break;
|
||||||
|
case 'BASE64_64':
|
||||||
|
$generatedValue = Str::random(64);
|
||||||
|
break;
|
||||||
|
case 'BASE64_128':
|
||||||
|
$generatedValue = Str::random(128);
|
||||||
|
break;
|
||||||
|
case 'BASE64':
|
||||||
|
$generatedValue = Str::random(32);
|
||||||
|
break;
|
||||||
|
case 'USER':
|
||||||
|
$generatedValue = Str::random(16);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$foundEnv) {
|
||||||
|
EnvironmentVariable::create([
|
||||||
|
'key' => $key,
|
||||||
|
'value' => $generatedValue,
|
||||||
|
'is_build_time' => false,
|
||||||
|
'service_id' => $this->id,
|
||||||
|
'is_preview' => false,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ($value->contains(':-')) {
|
||||||
|
$key = $value->before(':');
|
||||||
|
$defaultValue = $value->after(':-');
|
||||||
|
} else if ($value->contains('-')) {
|
||||||
|
$key = $value->before('-');
|
||||||
|
$defaultValue = $value->after('-');
|
||||||
|
} else if ($value->contains(':?')) {
|
||||||
|
$key = $value->before(':');
|
||||||
|
$defaultValue = $value->after(':?');
|
||||||
|
} else if ($value->contains('?')) {
|
||||||
|
$key = $value->before('?');
|
||||||
|
$defaultValue = $value->after('?');
|
||||||
|
} else {
|
||||||
|
$key = $value;
|
||||||
|
$defaultValue = null;
|
||||||
|
}
|
||||||
|
if ($foundEnv) {
|
||||||
|
$defaultValue = data_get($foundEnv, 'value');
|
||||||
|
}
|
||||||
|
EnvironmentVariable::updateOrCreate([
|
||||||
|
'key' => $key,
|
||||||
|
'service_id' => $this->id,
|
||||||
|
], [
|
||||||
|
'value' => $defaultValue,
|
||||||
|
'is_build_time' => false,
|
||||||
|
'service_id' => $this->id,
|
||||||
|
'is_preview' => false,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add labels to the service
|
||||||
|
$fqdns = collect(data_get($savedService, 'fqdns'));
|
||||||
|
$defaultLabels = defaultLabels($this->id, $containerName, type: 'service', subType: $isDatabase ? 'database' : 'application', subId: $savedService->id);
|
||||||
|
$serviceLabels = $serviceLabels->merge($defaultLabels);
|
||||||
|
if (!$isDatabase && $fqdns->count() > 0) {
|
||||||
|
if ($fqdns) {
|
||||||
|
$serviceLabels = $serviceLabels->merge(fqdnLabelsForTraefik($fqdns, $containerName, true));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data_set($service, 'labels', $serviceLabels->toArray());
|
||||||
|
data_forget($service, 'is_database');
|
||||||
|
data_set($service, 'restart', RESTART_MODE);
|
||||||
|
data_set($service, 'container_name', $containerName);
|
||||||
|
data_forget($service, 'volumes.*.content');
|
||||||
|
data_forget($service, 'volumes.*.isDirectory');
|
||||||
|
|
||||||
|
// Remove unnecessary variables from service.environment
|
||||||
|
$withoutServiceEnvs = collect([]);
|
||||||
|
collect(data_get($service, 'environment'))->each(function ($value, $key) use ($withoutServiceEnvs) {
|
||||||
|
if (!Str::of($key)->startsWith('$SERVICE_')) {
|
||||||
|
$withoutServiceEnvs->put($key, $value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
data_set($service, 'environment', $withoutServiceEnvs->toArray());
|
||||||
|
return $service;
|
||||||
|
});
|
||||||
|
$finalServices = [
|
||||||
|
'version' => $dockerComposeVersion,
|
||||||
|
'services' => $services->toArray(),
|
||||||
|
'volumes' => $topLevelVolumes->toArray(),
|
||||||
|
'networks' => $topLevelNetworks->toArray(),
|
||||||
|
];
|
||||||
|
$this->docker_compose_raw = Yaml::dump($yaml, 10, 2);
|
||||||
|
$this->docker_compose = Yaml::dump($finalServices, 10, 2);
|
||||||
|
$this->save();
|
||||||
|
$this->saveComposeConfigs();
|
||||||
|
return collect([]);
|
||||||
|
} else {
|
||||||
|
return collect([]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
43
app/Models/ServiceApplication.php
Normal file
43
app/Models/ServiceApplication.php
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||||
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
|
use Illuminate\Support\Facades\Cache;
|
||||||
|
|
||||||
|
class ServiceApplication extends BaseModel
|
||||||
|
{
|
||||||
|
use HasFactory;
|
||||||
|
protected $guarded = [];
|
||||||
|
|
||||||
|
public function type()
|
||||||
|
{
|
||||||
|
return 'service';
|
||||||
|
}
|
||||||
|
public function service()
|
||||||
|
{
|
||||||
|
return $this->belongsTo(Service::class);
|
||||||
|
}
|
||||||
|
public function persistentStorages()
|
||||||
|
{
|
||||||
|
return $this->morphMany(LocalPersistentVolume::class, 'resource');
|
||||||
|
}
|
||||||
|
public function fileStorages()
|
||||||
|
{
|
||||||
|
return $this->morphMany(LocalFileVolume::class, 'resource');
|
||||||
|
}
|
||||||
|
public function fqdns(): Attribute
|
||||||
|
{
|
||||||
|
return Attribute::make(
|
||||||
|
get: fn () => is_null($this->fqdn)
|
||||||
|
? []
|
||||||
|
: explode(',', $this->fqdn),
|
||||||
|
|
||||||
|
);
|
||||||
|
}
|
||||||
|
public function saveFileVolumes()
|
||||||
|
{
|
||||||
|
saveFileVolumesHelper($this);
|
||||||
|
}
|
||||||
|
}
|
||||||
33
app/Models/ServiceDatabase.php
Normal file
33
app/Models/ServiceDatabase.php
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
|
use Symfony\Component\Yaml\Yaml;
|
||||||
|
|
||||||
|
class ServiceDatabase extends BaseModel
|
||||||
|
{
|
||||||
|
use HasFactory;
|
||||||
|
protected $guarded = [];
|
||||||
|
|
||||||
|
public function type()
|
||||||
|
{
|
||||||
|
return 'service';
|
||||||
|
}
|
||||||
|
public function service()
|
||||||
|
{
|
||||||
|
return $this->belongsTo(Service::class);
|
||||||
|
}
|
||||||
|
public function persistentStorages()
|
||||||
|
{
|
||||||
|
return $this->morphMany(LocalPersistentVolume::class, 'resource');
|
||||||
|
}
|
||||||
|
public function fileStorages()
|
||||||
|
{
|
||||||
|
return $this->morphMany(LocalFileVolume::class, 'resource');
|
||||||
|
}
|
||||||
|
public function saveFileVolumes()
|
||||||
|
{
|
||||||
|
saveFileVolumesHelper($this);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -21,6 +21,11 @@ class StandaloneDocker extends BaseModel
|
|||||||
return $this->belongsTo(Server::class);
|
return $this->belongsTo(Server::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function services()
|
||||||
|
{
|
||||||
|
return $this->morphMany(Service::class, 'destination');
|
||||||
|
}
|
||||||
|
|
||||||
public function attachedTo()
|
public function attachedTo()
|
||||||
{
|
{
|
||||||
return $this->applications?->count() > 0 || $this->databases?->count() > 0;
|
return $this->applications?->count() > 0 || $this->databases?->count() > 0;
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ class StandalonePostgresql extends BaseModel
|
|||||||
static::deleted(function ($database) {
|
static::deleted(function ($database) {
|
||||||
$database->scheduledBackups()->delete();
|
$database->scheduledBackups()->delete();
|
||||||
$database->persistentStorages()->delete();
|
$database->persistentStorages()->delete();
|
||||||
|
$database->environment_variables()->delete();
|
||||||
instant_remote_process(['docker volume rm postgres-data-' . $database->uuid], $database->destination->server, false);
|
instant_remote_process(['docker volume rm postgres-data-' . $database->uuid], $database->destination->server, false);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ class GeneralNotification extends Notification implements ShouldQueue
|
|||||||
{
|
{
|
||||||
use Queueable;
|
use Queueable;
|
||||||
|
|
||||||
public $tries = 5;
|
public $tries = 1;
|
||||||
public function __construct(public string $message)
|
public function __construct(public string $message)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,9 +2,6 @@
|
|||||||
|
|
||||||
namespace App\Notifications;
|
namespace App\Notifications;
|
||||||
|
|
||||||
use App\Notifications\Channels\DiscordChannel;
|
|
||||||
use App\Notifications\Channels\EmailChannel;
|
|
||||||
use App\Notifications\Channels\TelegramChannel;
|
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
use Illuminate\Notifications\Messages\MailMessage;
|
use Illuminate\Notifications\Messages\MailMessage;
|
||||||
|
|||||||
@@ -50,5 +50,8 @@ class RouteServiceProvider extends ServiceProvider
|
|||||||
}
|
}
|
||||||
return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip());
|
return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip());
|
||||||
});
|
});
|
||||||
|
RateLimiter::for('5', function (Request $request) {
|
||||||
|
return Limit::perMinute(5)->by($request->user()?->id ?: $request->ip());
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ class Textarea extends Component
|
|||||||
public bool $readonly = false,
|
public bool $readonly = false,
|
||||||
public string|null $helper = null,
|
public string|null $helper = null,
|
||||||
public bool $realtimeValidation = false,
|
public bool $realtimeValidation = false,
|
||||||
public string $defaultClass = "textarea bg-coolgray-200 rounded text-white scrollbar disabled:bg-coolgray-200/50 disabled:border-none placeholder:text-coolgray-500 read-only:text-neutral-500 read-only:bg-coolgray-200/50"
|
public string $defaultClass = "textarea leading-normal bg-coolgray-200 rounded text-white scrollbar disabled:bg-coolgray-200/50 disabled:border-none placeholder:text-coolgray-500 read-only:text-neutral-500 read-only:bg-coolgray-200/50"
|
||||||
) {
|
) {
|
||||||
//
|
//
|
||||||
}
|
}
|
||||||
|
|||||||
26
app/View/Components/Services/Explanation.php
Normal file
26
app/View/Components/Services/Explanation.php
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\View\Components\Services;
|
||||||
|
|
||||||
|
use Closure;
|
||||||
|
use Illuminate\Contracts\View\View;
|
||||||
|
use Illuminate\View\Component;
|
||||||
|
|
||||||
|
class Explanation extends Component
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Create a new component instance.
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the view / contents that represent the component.
|
||||||
|
*/
|
||||||
|
public function render(): View|Closure|string
|
||||||
|
{
|
||||||
|
return view('components.services.explanation');
|
||||||
|
}
|
||||||
|
}
|
||||||
46
app/View/Components/Services/Links.php
Normal file
46
app/View/Components/Services/Links.php
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\View\Components\Services;
|
||||||
|
|
||||||
|
use App\Models\Service;
|
||||||
|
use Closure;
|
||||||
|
use Illuminate\Contracts\View\View;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
use Illuminate\View\Component;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
|
class Links extends Component
|
||||||
|
{
|
||||||
|
public Collection $links;
|
||||||
|
public function __construct(public Service $service)
|
||||||
|
{
|
||||||
|
$this->links = collect([]);
|
||||||
|
$service->applications()->get()->map(function ($application) {
|
||||||
|
if ($application->fqdn) {
|
||||||
|
$fqdns = collect(Str::of($application->fqdn)->explode(','));
|
||||||
|
$fqdns->map(function ($fqdn) {
|
||||||
|
$this->links->push(getFqdnWithoutPort($fqdn));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if ($application->ports) {
|
||||||
|
$portsCollection = collect(Str::of($application->ports)->explode(','));
|
||||||
|
$portsCollection->map(function ($port) {
|
||||||
|
if (Str::of($port)->contains(':')) {
|
||||||
|
$hostPort = Str::of($port)->before(':');
|
||||||
|
} else {
|
||||||
|
$hostPort = $port;
|
||||||
|
}
|
||||||
|
$this->links->push(base_url(withPort:false) . ":{$hostPort}");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the view / contents that represent the component.
|
||||||
|
*/
|
||||||
|
public function render(): View|Closure|string
|
||||||
|
{
|
||||||
|
return view('components.services.links');
|
||||||
|
}
|
||||||
|
}
|
||||||
28
app/View/Components/Status/Index.php
Normal file
28
app/View/Components/Status/Index.php
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\View\Components\Status;
|
||||||
|
|
||||||
|
use Closure;
|
||||||
|
use Illuminate\Contracts\View\View;
|
||||||
|
use Illuminate\View\Component;
|
||||||
|
|
||||||
|
class Index extends Component
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Create a new component instance.
|
||||||
|
*/
|
||||||
|
public function __construct(
|
||||||
|
public string $status = 'exited',
|
||||||
|
)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the view / contents that represent the component.
|
||||||
|
*/
|
||||||
|
public function render(): View|Closure|string
|
||||||
|
{
|
||||||
|
return view('components.status.index');
|
||||||
|
}
|
||||||
|
}
|
||||||
29
app/View/Components/Status/Services.php
Normal file
29
app/View/Components/Status/Services.php
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\View\Components\Status;
|
||||||
|
|
||||||
|
use App\Models\Service;
|
||||||
|
use Closure;
|
||||||
|
use Illuminate\Contracts\View\View;
|
||||||
|
use Illuminate\View\Component;
|
||||||
|
|
||||||
|
class Services extends Component
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Create a new component instance.
|
||||||
|
*/
|
||||||
|
public function __construct(
|
||||||
|
public Service $service,
|
||||||
|
public string $complexStatus = 'exited',
|
||||||
|
) {
|
||||||
|
$this->complexStatus = serviceStatus($service);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the view / contents that represent the component.
|
||||||
|
*/
|
||||||
|
public function render(): View|Closure|string
|
||||||
|
{
|
||||||
|
return view('components.status.services');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,3 +10,16 @@ const VALID_CRON_STRINGS = [
|
|||||||
'yearly' => '0 0 1 1 *',
|
'yearly' => '0 0 1 1 *',
|
||||||
];
|
];
|
||||||
const RESTART_MODE = 'unless-stopped';
|
const RESTART_MODE = 'unless-stopped';
|
||||||
|
|
||||||
|
const DATABASE_DOCKER_IMAGES = [
|
||||||
|
'mysql',
|
||||||
|
'mariadb',
|
||||||
|
'postgres',
|
||||||
|
'mongo',
|
||||||
|
'redis',
|
||||||
|
'memcached',
|
||||||
|
'couchdb',
|
||||||
|
'neo4j',
|
||||||
|
'influxdb',
|
||||||
|
'clickhouse/clickhouse-server'
|
||||||
|
];
|
||||||
|
|||||||
@@ -1,11 +1,15 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use App\Enums\ProxyTypes;
|
||||||
use App\Models\Application;
|
use App\Models\Application;
|
||||||
|
use App\Models\ApplicationPreview;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
|
use Spatie\Url\Url;
|
||||||
|
|
||||||
function getCurrentApplicationContainerStatus(Server $server, int $id): Collection {
|
function getCurrentApplicationContainerStatus(Server $server, int $id): Collection
|
||||||
|
{
|
||||||
$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);
|
||||||
if (!$containers) {
|
if (!$containers) {
|
||||||
return collect([]);
|
return collect([]);
|
||||||
@@ -26,7 +30,7 @@ function format_docker_command_output_to_json($rawOutput): Collection
|
|||||||
->map(fn ($outputLine) => json_decode($outputLine, true, flags: JSON_THROW_ON_ERROR));
|
->map(fn ($outputLine) => json_decode($outputLine, true, flags: JSON_THROW_ON_ERROR));
|
||||||
}
|
}
|
||||||
|
|
||||||
function format_docker_labels_to_json(string|Array $rawOutput): Collection
|
function format_docker_labels_to_json(string|array $rawOutput): Collection
|
||||||
{
|
{
|
||||||
if (is_array($rawOutput)) {
|
if (is_array($rawOutput)) {
|
||||||
return collect($rawOutput);
|
return collect($rawOutput);
|
||||||
@@ -59,7 +63,8 @@ function format_docker_envs_to_json($rawOutput)
|
|||||||
return collect([]);
|
return collect([]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function checkMinimumDockerEngineVersion($dockerVersion) {
|
function checkMinimumDockerEngineVersion($dockerVersion)
|
||||||
|
{
|
||||||
$majorDockerVersion = Str::of($dockerVersion)->before('.')->value();
|
$majorDockerVersion = Str::of($dockerVersion)->before('.')->value();
|
||||||
if ($majorDockerVersion <= 22) {
|
if ($majorDockerVersion <= 22) {
|
||||||
$dockerVersion = null;
|
$dockerVersion = null;
|
||||||
@@ -72,7 +77,8 @@ function executeInDocker(string $containerId, string $command)
|
|||||||
// return "docker exec {$this->deployment_uuid} bash -c '{$command} |& tee -a /proc/1/fd/1; [ \$PIPESTATUS -eq 0 ] || exit \$PIPESTATUS'";
|
// return "docker exec {$this->deployment_uuid} bash -c '{$command} |& tee -a /proc/1/fd/1; [ \$PIPESTATUS -eq 0 ] || exit \$PIPESTATUS'";
|
||||||
}
|
}
|
||||||
|
|
||||||
function getApplicationContainerStatus(Application $application) {
|
function getApplicationContainerStatus(Application $application)
|
||||||
|
{
|
||||||
$server = data_get($application, 'destination.server');
|
$server = data_get($application, 'destination.server');
|
||||||
$id = $application->id;
|
$id = $application->id;
|
||||||
if (!$server) {
|
if (!$server) {
|
||||||
@@ -98,13 +104,13 @@ function getContainerStatus(Server $server, string $container_id, bool $all_data
|
|||||||
return data_get($container[0], 'State.Status', 'exited');
|
return data_get($container[0], 'State.Status', 'exited');
|
||||||
}
|
}
|
||||||
|
|
||||||
function generateApplicationContainerName(string $uuid, int $pull_request_id = 0)
|
function generateApplicationContainerName(Application $application)
|
||||||
{
|
{
|
||||||
$now = now()->format('Hisu');
|
$now = now()->format('Hisu');
|
||||||
if ($pull_request_id !== 0 && $pull_request_id !== null) {
|
if ($application->pull_request_id !== 0 && $application->pull_request_id !== null) {
|
||||||
return $uuid . '-pr-' . $pull_request_id;
|
return $application->uuid . '-pr-' . $application->pull_request_id;
|
||||||
} else {
|
} else {
|
||||||
return $uuid . '-' . $now;
|
return $application->uuid . '-' . $now;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function get_port_from_dockerfile($dockerfile): int
|
function get_port_from_dockerfile($dockerfile): int
|
||||||
@@ -123,3 +129,99 @@ function get_port_from_dockerfile($dockerfile): int
|
|||||||
}
|
}
|
||||||
return 80;
|
return 80;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function defaultLabels($id, $name, $pull_request_id = 0, string $type = 'application', $subType = null, $subId = null)
|
||||||
|
{
|
||||||
|
$labels = collect([]);
|
||||||
|
$labels->push('coolify.managed=true');
|
||||||
|
$labels->push('coolify.version=' . config('version'));
|
||||||
|
$labels->push("coolify." . $type . "Id=" . $id);
|
||||||
|
$labels->push("coolify.type=$type");
|
||||||
|
$labels->push('coolify.name=' . $name);
|
||||||
|
if ($pull_request_id !== 0) {
|
||||||
|
$labels->push('coolify.pullRequestId=' . $pull_request_id);
|
||||||
|
}
|
||||||
|
if ($type === 'service') {
|
||||||
|
$labels->push('coolify.service.subId=' . $subId);
|
||||||
|
$labels->push('coolify.service.subType=' . $subType);
|
||||||
|
}
|
||||||
|
return $labels;
|
||||||
|
}
|
||||||
|
function fqdnLabelsForTraefik(Collection $domains, $container_name, $is_force_https_enabled)
|
||||||
|
{
|
||||||
|
$labels = collect([]);
|
||||||
|
$labels->push('traefik.enable=true');
|
||||||
|
foreach ($domains as $domain) {
|
||||||
|
$url = Url::fromString($domain);
|
||||||
|
$host = $url->getHost();
|
||||||
|
$path = $url->getPath();
|
||||||
|
$schema = $url->getScheme();
|
||||||
|
$port = $url->getPort();
|
||||||
|
$slug = Str::slug($host . $path);
|
||||||
|
|
||||||
|
$http_label = "{$container_name}-{$slug}-http";
|
||||||
|
$https_label = "{$container_name}-{$slug}-https";
|
||||||
|
|
||||||
|
if ($schema === 'https') {
|
||||||
|
// Set labels for https
|
||||||
|
$labels->push("traefik.http.routers.{$https_label}.rule=Host(`{$host}`) && PathPrefix(`{$path}`)");
|
||||||
|
$labels->push("traefik.http.routers.{$https_label}.entryPoints=https");
|
||||||
|
$labels->push("traefik.http.routers.{$https_label}.middlewares=gzip");
|
||||||
|
if ($port) {
|
||||||
|
$labels->push("traefik.http.routers.{$https_label}.service={$https_label}");
|
||||||
|
$labels->push("traefik.http.services.{$https_label}.loadbalancer.server.port=$port");
|
||||||
|
}
|
||||||
|
if ($path !== '/') {
|
||||||
|
$labels->push("traefik.http.routers.{$https_label}.middlewares={$https_label}-stripprefix");
|
||||||
|
$labels->push("traefik.http.middlewares.{$https_label}-stripprefix.stripprefix.prefixes={$path}");
|
||||||
|
}
|
||||||
|
|
||||||
|
$labels->push("traefik.http.routers.{$https_label}.tls=true");
|
||||||
|
$labels->push("traefik.http.routers.{$https_label}.tls.certresolver=letsencrypt");
|
||||||
|
|
||||||
|
// Set labels for http (redirect to https)
|
||||||
|
$labels->push("traefik.http.routers.{$http_label}.rule=Host(`{$host}`) && PathPrefix(`{$path}`)");
|
||||||
|
$labels->push("traefik.http.routers.{$http_label}.entryPoints=http");
|
||||||
|
if ($is_force_https_enabled) {
|
||||||
|
$labels->push("traefik.http.routers.{$http_label}.middlewares=redirect-to-https");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Set labels for http
|
||||||
|
$labels->push("traefik.http.routers.{$http_label}.rule=Host(`{$host}`) && PathPrefix(`{$path}`)");
|
||||||
|
$labels->push("traefik.http.routers.{$http_label}.entryPoints=http");
|
||||||
|
$labels->push("traefik.http.routers.{$http_label}.middlewares=gzip");
|
||||||
|
if ($port) {
|
||||||
|
$labels->push("traefik.http.routers.{$http_label}.service={$http_label}");
|
||||||
|
$labels->push("traefik.http.services.{$http_label}.loadbalancer.server.port=$port");
|
||||||
|
}
|
||||||
|
if ($path !== '/') {
|
||||||
|
$labels->push("traefik.http.routers.{$http_label}.middlewares={$http_label}-stripprefix");
|
||||||
|
$labels->push("traefik.http.middlewares.{$http_label}-stripprefix.stripprefix.prefixes={$path}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $labels;
|
||||||
|
}
|
||||||
|
function generateLabelsApplication(Application $application, ?ApplicationPreview $preview = null): array
|
||||||
|
{
|
||||||
|
|
||||||
|
$pull_request_id = data_get($preview, 'pull_request_id', 0);
|
||||||
|
$container_name = generateApplicationContainerName($application);
|
||||||
|
$appId = $application->id;
|
||||||
|
if ($pull_request_id !== 0) {
|
||||||
|
$appId = $appId . '-pr-' . $application->pull_request_id;
|
||||||
|
}
|
||||||
|
$labels = collect([]);
|
||||||
|
$labels = $labels->merge(defaultLabels($appId, $container_name, $pull_request_id));
|
||||||
|
if ($application->fqdn) {
|
||||||
|
if ($pull_request_id !== 0) {
|
||||||
|
$domains = Str::of(data_get($preview, 'fqdn'))->explode(',');
|
||||||
|
} else {
|
||||||
|
$domains = Str::of(data_get($application, 'fqdn'))->explode(',');
|
||||||
|
}
|
||||||
|
// Add Traefik labels no matter which proxy is selected
|
||||||
|
$labels = $labels->merge(fqdnLabelsForTraefik($domains, $container_name, $application->settings->is_force_https_enabled));
|
||||||
|
}
|
||||||
|
return $labels->all();
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use App\Actions\Proxy\SaveConfiguration;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use App\Models\StandalonePostgresql;
|
use App\Models\StandalonePostgresql;
|
||||||
use Symfony\Component\Yaml\Yaml;
|
use Symfony\Component\Yaml\Yaml;
|
||||||
@@ -10,7 +11,23 @@ function get_proxy_path()
|
|||||||
$proxy_path = "$base_path/proxy";
|
$proxy_path = "$base_path/proxy";
|
||||||
return $proxy_path;
|
return $proxy_path;
|
||||||
}
|
}
|
||||||
|
function connectProxyToNetworks(Server $server)
|
||||||
|
{
|
||||||
|
$networks = collect($server->standaloneDockers)->map(function ($docker) {
|
||||||
|
return $docker['network'];
|
||||||
|
})->unique();
|
||||||
|
if ($networks->count() === 0) {
|
||||||
|
$networks = collect(['coolify']);
|
||||||
|
}
|
||||||
|
$commands = $networks->map(function ($network) {
|
||||||
|
return [
|
||||||
|
"echo '####### Connecting coolify-proxy to $network network...'",
|
||||||
|
"docker network ls --format '{{.Name}}' | grep '^$network$' >/dev/null || docker network create --attachable $network >/dev/null",
|
||||||
|
"docker network connect $network coolify-proxy >/dev/null 2>&1 || true",
|
||||||
|
];
|
||||||
|
});
|
||||||
|
return $commands->flatten();
|
||||||
|
}
|
||||||
function generate_default_proxy_configuration(Server $server)
|
function generate_default_proxy_configuration(Server $server)
|
||||||
{
|
{
|
||||||
$proxy_path = get_proxy_path();
|
$proxy_path = get_proxy_path();
|
||||||
@@ -86,17 +103,18 @@ function generate_default_proxy_configuration(Server $server)
|
|||||||
if (isDev()) {
|
if (isDev()) {
|
||||||
$config['services']['traefik']['command'][] = "--log.level=debug";
|
$config['services']['traefik']['command'][] = "--log.level=debug";
|
||||||
}
|
}
|
||||||
return Yaml::dump($config, 4, 2);
|
$config = Yaml::dump($config, 4, 2);
|
||||||
|
SaveConfiguration::run($server, $config);
|
||||||
|
return $config;
|
||||||
}
|
}
|
||||||
|
|
||||||
function setup_default_redirect_404(string|null $redirect_url, Server $server)
|
function setup_default_redirect_404(string|null $redirect_url, Server $server)
|
||||||
{
|
{
|
||||||
ray('called');
|
|
||||||
$traefik_dynamic_conf_path = get_proxy_path() . "/dynamic";
|
$traefik_dynamic_conf_path = get_proxy_path() . "/dynamic";
|
||||||
$traefik_default_redirect_file = "$traefik_dynamic_conf_path/default_redirect_404.yaml";
|
$traefik_default_redirect_file = "$traefik_dynamic_conf_path/default_redirect_404.yaml";
|
||||||
ray($redirect_url);
|
|
||||||
if (empty($redirect_url)) {
|
if (empty($redirect_url)) {
|
||||||
instant_remote_process([
|
instant_remote_process([
|
||||||
|
"mkdir -p $traefik_dynamic_conf_path",
|
||||||
"rm -f $traefik_default_redirect_file",
|
"rm -f $traefik_default_redirect_file",
|
||||||
], $server);
|
], $server);
|
||||||
} else {
|
} else {
|
||||||
@@ -156,7 +174,6 @@ function setup_default_redirect_404(string|null $redirect_url, Server $server)
|
|||||||
$yaml;
|
$yaml;
|
||||||
|
|
||||||
$base64 = base64_encode($yaml);
|
$base64 = base64_encode($yaml);
|
||||||
ray("mkdir -p $traefik_dynamic_conf_path");
|
|
||||||
instant_remote_process([
|
instant_remote_process([
|
||||||
"mkdir -p $traefik_dynamic_conf_path",
|
"mkdir -p $traefik_dynamic_conf_path",
|
||||||
"echo '$base64' | base64 -d > $traefik_default_redirect_file",
|
"echo '$base64' | base64 -d > $traefik_default_redirect_file",
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ use Illuminate\Support\Str;
|
|||||||
use Spatie\Activitylog\Contracts\Activity;
|
use Spatie\Activitylog\Contracts\Activity;
|
||||||
|
|
||||||
function remote_process(
|
function remote_process(
|
||||||
array $command,
|
Collection|array $command,
|
||||||
Server $server,
|
Server $server,
|
||||||
?string $type = null,
|
?string $type = null,
|
||||||
?string $type_uuid = null,
|
?string $type_uuid = null,
|
||||||
@@ -26,6 +26,9 @@ function remote_process(
|
|||||||
if (is_null($type)) {
|
if (is_null($type)) {
|
||||||
$type = ActivityTypes::INLINE->value;
|
$type = ActivityTypes::INLINE->value;
|
||||||
}
|
}
|
||||||
|
if ($command instanceof Collection) {
|
||||||
|
$command = $command->toArray();
|
||||||
|
}
|
||||||
$command_string = implode("\n", $command);
|
$command_string = implode("\n", $command);
|
||||||
if (auth()->user()) {
|
if (auth()->user()) {
|
||||||
$teams = auth()->user()->teams->pluck('id');
|
$teams = auth()->user()->teams->pluck('id');
|
||||||
@@ -33,7 +36,6 @@ function remote_process(
|
|||||||
throw new \Exception("User is not part of the team that owns this server");
|
throw new \Exception("User is not part of the team that owns this server");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return resolve(PrepareCoolifyTask::class, [
|
return resolve(PrepareCoolifyTask::class, [
|
||||||
'remoteProcessArgs' => new CoolifyTaskArgs(
|
'remoteProcessArgs' => new CoolifyTaskArgs(
|
||||||
server_uuid: $server->uuid,
|
server_uuid: $server->uuid,
|
||||||
@@ -83,6 +85,9 @@ function generateSshCommand(Server $server, string $command, bool $isMux = true)
|
|||||||
if ($isMux && config('coolify.mux_enabled')) {
|
if ($isMux && config('coolify.mux_enabled')) {
|
||||||
$ssh_command .= '-o ControlMaster=auto -o ControlPersist=1m -o ControlPath=/var/www/html/storage/app/ssh/mux/%h_%p_%r ';
|
$ssh_command .= '-o ControlMaster=auto -o ControlPersist=1m -o ControlPath=/var/www/html/storage/app/ssh/mux/%h_%p_%r ';
|
||||||
}
|
}
|
||||||
|
if (data_get($server,'settings.is_cloudflare_tunnel')) {
|
||||||
|
$ssh_command .= '-o ProxyCommand="/usr/local/bin/cloudflared access ssh --hostname %h" ';
|
||||||
|
}
|
||||||
$command = "PATH=\$PATH:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/host/usr/local/sbin:/host/usr/local/bin:/host/usr/sbin:/host/usr/bin:/host/sbin:/host/bin && $command";
|
$command = "PATH=\$PATH:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/host/usr/local/sbin:/host/usr/local/bin:/host/usr/sbin:/host/usr/bin:/host/sbin:/host/bin && $command";
|
||||||
$ssh_command .= "-i {$privateKeyLocation} "
|
$ssh_command .= "-i {$privateKeyLocation} "
|
||||||
. '-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null '
|
. '-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null '
|
||||||
@@ -99,8 +104,11 @@ function generateSshCommand(Server $server, string $command, bool $isMux = true)
|
|||||||
// ray($ssh_command);
|
// ray($ssh_command);
|
||||||
return $ssh_command;
|
return $ssh_command;
|
||||||
}
|
}
|
||||||
function instant_remote_process(array $command, Server $server, $throwError = true)
|
function instant_remote_process(Collection|array $command, Server $server, $throwError = true)
|
||||||
{
|
{
|
||||||
|
if ($command instanceof Collection) {
|
||||||
|
$command = $command->toArray();
|
||||||
|
}
|
||||||
$command_string = implode("\n", $command);
|
$command_string = implode("\n", $command);
|
||||||
$ssh_command = generateSshCommand($server, $command_string);
|
$ssh_command = generateSshCommand($server, $command_string);
|
||||||
$process = Process::run($ssh_command);
|
$process = Process::run($ssh_command);
|
||||||
@@ -181,7 +189,7 @@ function validateServer(Server $server, bool $throwError = false)
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
$server->settings->is_reachable = true;
|
$server->settings->is_reachable = true;
|
||||||
|
instant_remote_process(["docker ps"], $server, $throwError);
|
||||||
$dockerVersion = instant_remote_process(["docker version|head -2|grep -i version| awk '{print $2}'"], $server, $throwError);
|
$dockerVersion = instant_remote_process(["docker version|head -2|grep -i version| awk '{print $2}'"], $server, $throwError);
|
||||||
if (!$dockerVersion) {
|
if (!$dockerVersion) {
|
||||||
$dockerVersion = null;
|
$dockerVersion = null;
|
||||||
|
|||||||
126
bootstrap/helpers/services.php
Normal file
126
bootstrap/helpers/services.php
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use App\Models\EnvironmentVariable;
|
||||||
|
use App\Models\Service;
|
||||||
|
use App\Models\ServiceApplication;
|
||||||
|
use App\Models\ServiceDatabase;
|
||||||
|
use Illuminate\Support\Facades\Storage;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
use Symfony\Component\Yaml\Yaml;
|
||||||
|
|
||||||
|
function replaceRegex(?string $name = null)
|
||||||
|
{
|
||||||
|
return "/\\\${?{$name}[^}]*}?|\\\${$name}\w+/";
|
||||||
|
}
|
||||||
|
function collectRegex(string $name)
|
||||||
|
{
|
||||||
|
return "/{$name}\w+/";
|
||||||
|
}
|
||||||
|
function replaceVariables($variable)
|
||||||
|
{
|
||||||
|
return $variable->replaceFirst('$', '')->replaceFirst('{', '')->replaceLast('}', '');
|
||||||
|
}
|
||||||
|
|
||||||
|
function serviceStatus(Service $service)
|
||||||
|
{
|
||||||
|
$foundRunning = false;
|
||||||
|
$isDegraded = false;
|
||||||
|
$foundRestaring = false;
|
||||||
|
$applications = $service->applications;
|
||||||
|
$databases = $service->databases;
|
||||||
|
foreach ($applications as $application) {
|
||||||
|
if ($application->exclude_from_status) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (Str::of($application->status)->startsWith('running')) {
|
||||||
|
$foundRunning = true;
|
||||||
|
} else if (Str::of($application->status)->startsWith('restarting')) {
|
||||||
|
$foundRestaring = true;
|
||||||
|
} else {
|
||||||
|
$isDegraded = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach ($databases as $database) {
|
||||||
|
if ($database->exclude_from_status) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (Str::of($database->status)->startsWith('running')) {
|
||||||
|
$foundRunning = true;
|
||||||
|
} else if (Str::of($database->status)->startsWith('restarting')) {
|
||||||
|
$foundRestaring = true;
|
||||||
|
} else {
|
||||||
|
$isDegraded = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($foundRestaring) {
|
||||||
|
return 'degraded';
|
||||||
|
}
|
||||||
|
if ($foundRunning && !$isDegraded) {
|
||||||
|
return 'running';
|
||||||
|
} else if ($foundRunning && $isDegraded) {
|
||||||
|
return 'degraded';
|
||||||
|
} else if (!$foundRunning && !$isDegraded) {
|
||||||
|
return 'exited';
|
||||||
|
}
|
||||||
|
return 'exited';
|
||||||
|
}
|
||||||
|
function saveFileVolumesHelper(ServiceApplication|ServiceDatabase $oneService)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$workdir = $oneService->service->workdir();
|
||||||
|
$server = $oneService->service->server;
|
||||||
|
$applicationFileVolume = $oneService->fileStorages()->get();
|
||||||
|
$commands = collect([
|
||||||
|
"mkdir -p $workdir > /dev/null 2>&1 || true",
|
||||||
|
"cd $workdir"
|
||||||
|
]);
|
||||||
|
foreach ($applicationFileVolume as $fileVolume) {
|
||||||
|
$path = Str::of($fileVolume->fs_path);
|
||||||
|
if ($fileVolume->is_directory) {
|
||||||
|
$commands->push("test -f $path && rm -f $path > /dev/null 2>&1 || true");
|
||||||
|
$commands->push("mkdir -p $path > /dev/null 2>&1 || true");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$content = $fileVolume->content;
|
||||||
|
$dir = $path->beforeLast('/');
|
||||||
|
if ($dir->startsWith('.')) {
|
||||||
|
$dir = $dir->after('.');
|
||||||
|
$dir = $workdir . $dir;
|
||||||
|
}
|
||||||
|
$content = base64_encode($content);
|
||||||
|
$commands->push("test -d $path && rm -rf $path > /dev/null 2>&1 || true");
|
||||||
|
$commands->push("mkdir -p $dir > /dev/null 2>&1 || true");
|
||||||
|
$commands->push("echo '$content' | base64 -d > $path");
|
||||||
|
}
|
||||||
|
return instant_remote_process($commands, $server);
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
return handleError($e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function updateCompose($resource) {
|
||||||
|
try {
|
||||||
|
$name = data_get($resource, 'name');
|
||||||
|
$dockerComposeRaw = data_get($resource, 'service.docker_compose_raw');
|
||||||
|
$dockerCompose = Yaml::parse($dockerComposeRaw);
|
||||||
|
|
||||||
|
// Switch Image
|
||||||
|
$image = data_get($resource, 'image');
|
||||||
|
data_set($dockerCompose, "services.{$name}.image", $image);
|
||||||
|
|
||||||
|
// Update FQDN
|
||||||
|
$variableName = "SERVICE_FQDN_" . Str::of($resource->name)->upper();
|
||||||
|
ray($variableName);
|
||||||
|
$generatedEnv = EnvironmentVariable::where('service_id', $resource->service_id)->where('key', $variableName)->first();
|
||||||
|
if ($generatedEnv){
|
||||||
|
$generatedEnv->value = $resource->fqdn;
|
||||||
|
$generatedEnv->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$dockerComposeRaw = Yaml::dump($dockerCompose, 10, 2);
|
||||||
|
$resource->service->docker_compose_raw = $dockerComposeRaw;
|
||||||
|
$resource->service->save();
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
return handleError($e);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
use App\Models\InstanceSettings;
|
use App\Models\InstanceSettings;
|
||||||
|
use App\Models\Server;
|
||||||
use App\Models\Team;
|
use App\Models\Team;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use App\Notifications\Channels\DiscordChannel;
|
use App\Notifications\Channels\DiscordChannel;
|
||||||
@@ -11,33 +12,42 @@ use DanHarrin\LivewireRateLimiting\Exceptions\TooManyRequestsException;
|
|||||||
use Illuminate\Database\QueryException;
|
use Illuminate\Database\QueryException;
|
||||||
use Illuminate\Mail\Message;
|
use Illuminate\Mail\Message;
|
||||||
use Illuminate\Notifications\Messages\MailMessage;
|
use Illuminate\Notifications\Messages\MailMessage;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
use Illuminate\Support\Facades\Cache;
|
use Illuminate\Support\Facades\Cache;
|
||||||
use Illuminate\Support\Facades\Http;
|
use Illuminate\Support\Facades\Http;
|
||||||
use Illuminate\Support\Facades\Mail;
|
use Illuminate\Support\Facades\Mail;
|
||||||
use Illuminate\Support\Facades\Route;
|
use Illuminate\Support\Facades\Route;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
|
use Illuminate\Support\Stringable;
|
||||||
use Nubs\RandomNameGenerator\All;
|
use Nubs\RandomNameGenerator\All;
|
||||||
use Poliander\Cron\CronExpression;
|
use Poliander\Cron\CronExpression;
|
||||||
use Visus\Cuid2\Cuid2;
|
use Visus\Cuid2\Cuid2;
|
||||||
use phpseclib3\Crypt\RSA;
|
use phpseclib3\Crypt\RSA;
|
||||||
|
use Spatie\Url\Url;
|
||||||
|
|
||||||
|
function base_configuration_dir(): string
|
||||||
|
{
|
||||||
|
return '/data/coolify';
|
||||||
|
}
|
||||||
function application_configuration_dir(): string
|
function application_configuration_dir(): string
|
||||||
{
|
{
|
||||||
return '/data/coolify/applications';
|
return base_configuration_dir() . "/applications";
|
||||||
|
}
|
||||||
|
function service_configuration_dir(): string
|
||||||
|
{
|
||||||
|
return base_configuration_dir() . "/services";
|
||||||
}
|
}
|
||||||
|
|
||||||
function database_configuration_dir(): string
|
function database_configuration_dir(): string
|
||||||
{
|
{
|
||||||
return '/data/coolify/databases';
|
return base_configuration_dir() . "/databases";
|
||||||
}
|
}
|
||||||
function database_proxy_dir($uuid): string
|
function database_proxy_dir($uuid): string
|
||||||
{
|
{
|
||||||
return "/data/coolify/databases/$uuid/proxy";
|
return base_configuration_dir() . "/databases/$uuid/proxy";
|
||||||
}
|
}
|
||||||
|
|
||||||
function backup_dir(): string
|
function backup_dir(): string
|
||||||
{
|
{
|
||||||
return '/data/coolify/backups';
|
return base_configuration_dir() . "/backups";
|
||||||
}
|
}
|
||||||
|
|
||||||
function generate_readme_file(string $name, string $updated_at): string
|
function generate_readme_file(string $name, string $updated_at): string
|
||||||
@@ -77,6 +87,7 @@ function refreshSession(?Team $team = null): void
|
|||||||
function handleError(?Throwable $error = null, ?Livewire\Component $livewire = null, ?string $customErrorMessage = null)
|
function handleError(?Throwable $error = null, ?Livewire\Component $livewire = null, ?string $customErrorMessage = null)
|
||||||
{
|
{
|
||||||
ray('handleError');
|
ray('handleError');
|
||||||
|
ray($error);
|
||||||
if ($error instanceof Throwable) {
|
if ($error instanceof Throwable) {
|
||||||
$message = $error->getMessage();
|
$message = $error->getMessage();
|
||||||
} else {
|
} else {
|
||||||
@@ -94,6 +105,7 @@ function handleError(?Throwable $error = null, ?Livewire\Component $livewire = n
|
|||||||
if (isset($livewire)) {
|
if (isset($livewire)) {
|
||||||
return $livewire->emit('error', $message);
|
return $livewire->emit('error', $message);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new RuntimeException($message);
|
throw new RuntimeException($message);
|
||||||
}
|
}
|
||||||
function general_error_handler(Throwable $err, Livewire\Component $that = null, $isJson = false, $customErrorMessage = null): mixed
|
function general_error_handler(Throwable $err, Livewire\Component $that = null, $isJson = false, $customErrorMessage = null): mixed
|
||||||
@@ -151,10 +163,12 @@ function get_latest_version_of_coolify(): string
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function generate_random_name(): string
|
function generate_random_name(?string $cuid = null): string
|
||||||
{
|
{
|
||||||
$generator = All::create();
|
$generator = All::create();
|
||||||
|
if (is_null($cuid)) {
|
||||||
$cuid = new Cuid2(7);
|
$cuid = new Cuid2(7);
|
||||||
|
}
|
||||||
return Str::kebab("{$generator->getName()}-$cuid");
|
return Str::kebab("{$generator->getName()}-$cuid");
|
||||||
}
|
}
|
||||||
function generateSSHKey()
|
function generateSSHKey()
|
||||||
@@ -173,9 +187,11 @@ function formatPrivateKey(string $privateKey)
|
|||||||
}
|
}
|
||||||
return $privateKey;
|
return $privateKey;
|
||||||
}
|
}
|
||||||
function generate_application_name(string $git_repository, string $git_branch): string
|
function generate_application_name(string $git_repository, string $git_branch, ?string $cuid = null): string
|
||||||
{
|
{
|
||||||
|
if (is_null($cuid)) {
|
||||||
$cuid = new Cuid2(7);
|
$cuid = new Cuid2(7);
|
||||||
|
}
|
||||||
return Str::kebab("$git_repository:$git_branch-$cuid");
|
return Str::kebab("$git_repository:$git_branch-$cuid");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -227,7 +243,14 @@ function base_ip(): string
|
|||||||
}
|
}
|
||||||
return "localhost";
|
return "localhost";
|
||||||
}
|
}
|
||||||
|
function getFqdnWithoutPort(String $fqdn)
|
||||||
|
{
|
||||||
|
$url = Url::fromString($fqdn);
|
||||||
|
$host = $url->getHost();
|
||||||
|
$scheme = $url->getScheme();
|
||||||
|
$path = $url->getPath();
|
||||||
|
return "$scheme://$host$path";
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* If fqdn is set, return it, otherwise return public ip.
|
* If fqdn is set, return it, otherwise return public ip.
|
||||||
*/
|
*/
|
||||||
@@ -365,3 +388,21 @@ function parseEnvFormatToArray($env_file_contents)
|
|||||||
}
|
}
|
||||||
return $env_array;
|
return $env_array;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function data_get_str($data, $key, $default = null): Stringable
|
||||||
|
{
|
||||||
|
$str = data_get($data, $key, $default) ?? $default;
|
||||||
|
return Str::of($str);
|
||||||
|
}
|
||||||
|
|
||||||
|
function sslip(Server $server)
|
||||||
|
{
|
||||||
|
if (isDev()) {
|
||||||
|
return "127.0.0.1.sslip.io";
|
||||||
|
}
|
||||||
|
if ($server->ip === 'host.docker.internal') {
|
||||||
|
$baseIp = base_ip();
|
||||||
|
return "$baseIp.sslip.io";
|
||||||
|
}
|
||||||
|
return "{$server->ip}.sslip.io";
|
||||||
|
}
|
||||||
|
|||||||
725
composer.lock
generated
725
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,9 @@
|
|||||||
<?php
|
<?php
|
||||||
return [
|
return [
|
||||||
|
'docs' => [
|
||||||
|
'base_url' => 'https://coolify.io/docs',
|
||||||
|
'contact' => 'https://coolify.io/docs/contact',
|
||||||
|
],
|
||||||
'ssh' => [
|
'ssh' => [
|
||||||
'connection_timeout' => 10,
|
'connection_timeout' => 10,
|
||||||
'server_interval' => 20,
|
'server_interval' => 20,
|
||||||
@@ -14,8 +18,11 @@ return [
|
|||||||
'expiration' => 10,
|
'expiration' => 10,
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
'services' => [
|
||||||
|
'official' => 'https://cdn.coollabs.io/coolify/service-templates.json',
|
||||||
|
],
|
||||||
'limits' => [
|
'limits' => [
|
||||||
'trial_period'=> 14,
|
'trial_period' => 7,
|
||||||
'server' => [
|
'server' => [
|
||||||
'zero' => 0,
|
'zero' => 0,
|
||||||
'self-hosted' => 999999999999,
|
'self-hosted' => 999999999999,
|
||||||
|
|||||||
@@ -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.43',
|
'release' => '4.0.0-beta.46',
|
||||||
// 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.43';
|
return '4.0.0-beta.46';
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
<?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('services', function (Blueprint $table) {
|
||||||
|
$table->foreignId('server_id')->nullable();
|
||||||
|
$table->longText('description')->nullable();
|
||||||
|
$table->longText('docker_compose_raw');
|
||||||
|
$table->longText('docker_compose')->nullable();
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('services', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('server_id');
|
||||||
|
$table->dropColumn('description');
|
||||||
|
$table->dropColumn('docker_compose_raw');
|
||||||
|
$table->dropColumn('docker_compose');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
<?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::create('service_databases', function (Blueprint $table) {
|
||||||
|
$table->id();
|
||||||
|
$table->string('uuid')->unique();
|
||||||
|
$table->string('name');
|
||||||
|
$table->string('human_name')->nullable();
|
||||||
|
$table->longText('description')->nullable();
|
||||||
|
|
||||||
|
$table->longText('ports')->nullable();
|
||||||
|
$table->longText('exposes')->nullable();
|
||||||
|
|
||||||
|
$table->string('status')->default('exited');
|
||||||
|
|
||||||
|
$table->foreignId('service_id');
|
||||||
|
$table->timestamps();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('service_databases');
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
<?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::create('service_applications', function (Blueprint $table) {
|
||||||
|
$table->id();
|
||||||
|
$table->string('uuid')->unique();
|
||||||
|
$table->string('name');
|
||||||
|
$table->string('human_name')->nullable();
|
||||||
|
$table->longText('description')->nullable();
|
||||||
|
|
||||||
|
$table->string('fqdn')->unique()->nullable();
|
||||||
|
$table->longText('ports')->nullable();
|
||||||
|
$table->longText('exposes')->nullable();
|
||||||
|
|
||||||
|
$table->string('status')->default('exited');
|
||||||
|
|
||||||
|
$table->foreignId('service_id');
|
||||||
|
$table->timestamps();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('service_applications');
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
<?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('environment_variables', function (Blueprint $table) {
|
||||||
|
$table->foreignId('service_id')->nullable();
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('environment_variables', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('service_id');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
<?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::create('local_file_volumes', function (Blueprint $table) {
|
||||||
|
$table->id();
|
||||||
|
$table->string('uuid');
|
||||||
|
$table->mediumText('fs_path');
|
||||||
|
$table->string('mount_path');
|
||||||
|
$table->mediumText('content')->nullable();
|
||||||
|
$table->nullableMorphs('resource');
|
||||||
|
|
||||||
|
$table->unique(['mount_path', 'resource_id', 'resource_type']);
|
||||||
|
$table->timestamps();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('local_file_volumes');
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::table('server_settings', function (Blueprint $table) {
|
||||||
|
$table->boolean('is_cloudflare_tunnel')->default(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('server_settings', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('is_cloudflare_tunnel');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
<?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('services', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('destination_type');
|
||||||
|
$table->dropColumn('destination_id');
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('services', function (Blueprint $table) {
|
||||||
|
$table->morphs('destination');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
<?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('service_applications', function (Blueprint $table) {
|
||||||
|
$table->boolean('exclude_from_status')->default(false);
|
||||||
|
$table->boolean('required_fqdn')->default(false);
|
||||||
|
$table->string('image')->nullable();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('service_applications', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('exclude_from_status');
|
||||||
|
$table->dropColumn('required_fqdn');
|
||||||
|
$table->dropColumn('image');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
<?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('service_databases', function (Blueprint $table) {
|
||||||
|
$table->boolean('exclude_from_status')->default(false);
|
||||||
|
$table->string('image')->nullable();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('service_databases', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('exclude_from_status');
|
||||||
|
$table->dropColumn('image');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::table('users', function (Blueprint $table) {
|
||||||
|
$table->boolean('marketing_emails')->default(true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('users', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('marketing_emails');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::table('local_file_volumes', function (Blueprint $table) {
|
||||||
|
$table->boolean('is_directory')->default(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('local_file_volumes', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('is_directory');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
17
database/seeders/LocalFileVolumeSeeder.php
Normal file
17
database/seeders/LocalFileVolumeSeeder.php
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Database\Seeders;
|
||||||
|
|
||||||
|
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
|
||||||
|
use Illuminate\Database\Seeder;
|
||||||
|
|
||||||
|
class LocalFileVolumeSeeder extends Seeder
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the database seeds.
|
||||||
|
*/
|
||||||
|
public function run(): void
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
}
|
||||||
17
database/seeders/ServiceApplicationSeeder.php
Normal file
17
database/seeders/ServiceApplicationSeeder.php
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Database\Seeders;
|
||||||
|
|
||||||
|
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
|
||||||
|
use Illuminate\Database\Seeder;
|
||||||
|
|
||||||
|
class ServiceApplicationSeeder extends Seeder
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the database seeds.
|
||||||
|
*/
|
||||||
|
public function run(): void
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
}
|
||||||
17
database/seeders/ServiceDatabaseSeeder.php
Normal file
17
database/seeders/ServiceDatabaseSeeder.php
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Database\Seeders;
|
||||||
|
|
||||||
|
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
|
||||||
|
use Illuminate\Database\Seeder;
|
||||||
|
|
||||||
|
class ServiceDatabaseSeeder extends Seeder
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the database seeds.
|
||||||
|
*/
|
||||||
|
public function run(): void
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
}
|
||||||
17
database/seeders/ServiceSeeder.php
Normal file
17
database/seeders/ServiceSeeder.php
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Database\Seeders;
|
||||||
|
|
||||||
|
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
|
||||||
|
use Illuminate\Database\Seeder;
|
||||||
|
|
||||||
|
class ServiceSeeder extends Seeder
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the database seeds.
|
||||||
|
*/
|
||||||
|
public function run(): void
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,7 +10,7 @@ ARG DOCKER_BUILDX_VERSION=0.11.2
|
|||||||
# https://github.com/buildpacks/pack/releases
|
# https://github.com/buildpacks/pack/releases
|
||||||
ARG PACK_VERSION=0.30.0
|
ARG PACK_VERSION=0.30.0
|
||||||
# https://github.com/railwayapp/nixpacks/releases
|
# https://github.com/railwayapp/nixpacks/releases
|
||||||
ARG NIXPACKS_VERSION=1.14.0
|
ARG NIXPACKS_VERSION=1.16.0
|
||||||
|
|
||||||
USER root
|
USER root
|
||||||
WORKDIR /artifacts
|
WORKDIR /artifacts
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
FROM serversideup/php:8.2-fpm-nginx
|
FROM serversideup/php:8.2-fpm-nginx
|
||||||
|
|
||||||
|
ARG TARGETPLATFORM
|
||||||
|
# https://github.com/cloudflare/cloudflared/releases
|
||||||
|
ARG CLOUDFLARED_VERSION=2023.8.2
|
||||||
|
|
||||||
ARG POSTGRES_VERSION=15
|
ARG POSTGRES_VERSION=15
|
||||||
RUN apt-get update
|
RUN apt-get update
|
||||||
# Postgres version requirements
|
# Postgres version requirements
|
||||||
@@ -13,15 +17,23 @@ RUN apt-get install postgresql-client-$POSTGRES_VERSION -y
|
|||||||
|
|
||||||
# Coolify requirements
|
# Coolify requirements
|
||||||
RUN apt-get install -y php-pgsql openssh-client git git-lfs jq lsof
|
RUN apt-get install -y php-pgsql openssh-client git git-lfs jq lsof
|
||||||
|
|
||||||
RUN apt-get -y autoremove && apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* /usr/share/doc/*
|
RUN apt-get -y autoremove && apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* /usr/share/doc/*
|
||||||
|
COPY --chmod=755 docker/dev-ssu/etc/s6-overlay/ /etc/s6-overlay/
|
||||||
|
|
||||||
COPY docker/dev-ssu/nginx.conf /etc/nginx/conf.d/custom.conf
|
COPY docker/dev-ssu/nginx.conf /etc/nginx/conf.d/custom.conf
|
||||||
|
|
||||||
RUN echo "alias ll='ls -al'" >>/etc/bash.bashrc
|
RUN echo "alias ll='ls -al'" >>/etc/bash.bashrc
|
||||||
RUN echo "alias a='php artisan'" >>/etc/bash.bashrc
|
RUN echo "alias a='php artisan'" >>/etc/bash.bashrc
|
||||||
RUN echo "alias mfs='php artisan migrate:fresh --seed'" >>/etc/bash.bashrc
|
|
||||||
RUN echo "alias cda='composer dump-autoload'" >>/etc/bash.bashrc
|
|
||||||
RUN echo "alias run='./scripts/run'" >>/etc/bash.bashrc
|
|
||||||
|
|
||||||
COPY --chmod=755 docker/dev-ssu/etc/s6-overlay/ /etc/s6-overlay/
|
RUN mkdir -p /usr/local/bin
|
||||||
|
|
||||||
|
RUN /bin/bash -c "if [[ ${TARGETPLATFORM} == 'linux/amd64' ]]; then \
|
||||||
|
echo 'amd64' && \
|
||||||
|
curl -sSL https://github.com/cloudflare/cloudflared/releases/download/${CLOUDFLARED_VERSION}/cloudflared-linux-amd64 -o /usr/local/bin/cloudflared && chmod +x /usr/local/bin/cloudflared \
|
||||||
|
;fi"
|
||||||
|
|
||||||
|
RUN /bin/bash -c "if [[ ${TARGETPLATFORM} == 'linux/arm64' ]]; then \
|
||||||
|
echo 'arm64' && \
|
||||||
|
curl -L https://github.com/cloudflare/cloudflared/releases/download/${CLOUDFLARED_VERSION}/cloudflared-linux-arm64 -o /usr/local/bin/cloudflared && chmod +x /usr/local/bin/cloudflared \
|
||||||
|
;fi"
|
||||||
|
|
||||||
|
|||||||
@@ -12,9 +12,14 @@ RUN npm install
|
|||||||
RUN npm run build
|
RUN npm run build
|
||||||
|
|
||||||
FROM serversideup/php:8.2-fpm-nginx
|
FROM serversideup/php:8.2-fpm-nginx
|
||||||
WORKDIR /var/www/html
|
|
||||||
|
ARG TARGETPLATFORM
|
||||||
|
# https://github.com/cloudflare/cloudflared/releases
|
||||||
|
ARG CLOUDFLARED_VERSION=2023.8.2
|
||||||
ARG POSTGRES_VERSION=15
|
ARG POSTGRES_VERSION=15
|
||||||
|
|
||||||
|
WORKDIR /var/www/html
|
||||||
|
|
||||||
RUN apt-get update
|
RUN apt-get update
|
||||||
# Postgres version requirements
|
# Postgres version requirements
|
||||||
RUN apt install dirmngr ca-certificates software-properties-common gnupg gnupg2 apt-transport-https curl -y
|
RUN apt install dirmngr ca-certificates software-properties-common gnupg gnupg2 apt-transport-https curl -y
|
||||||
@@ -44,7 +49,16 @@ RUN php artisan view:cache
|
|||||||
|
|
||||||
RUN echo "alias ll='ls -al'" >>/etc/bash.bashrc
|
RUN echo "alias ll='ls -al'" >>/etc/bash.bashrc
|
||||||
RUN echo "alias a='php artisan'" >>/etc/bash.bashrc
|
RUN echo "alias a='php artisan'" >>/etc/bash.bashrc
|
||||||
RUN echo "alias mfs='php artisan migrate:fresh --seed'" >>/etc/bash.bashrc
|
|
||||||
RUN echo "alias cda='composer dump-autoload'" >>/etc/bash.bashrc
|
|
||||||
RUN echo "alias run='./scripts/run'" >>/etc/bash.bashrc
|
|
||||||
RUN echo "alias logs='tail -f storage/logs/laravel.log'" >>/etc/bash.bashrc
|
RUN echo "alias logs='tail -f storage/logs/laravel.log'" >>/etc/bash.bashrc
|
||||||
|
|
||||||
|
RUN mkdir -p /usr/local/bin
|
||||||
|
|
||||||
|
RUN /bin/bash -c "if [[ ${TARGETPLATFORM} == 'linux/amd64' ]]; then \
|
||||||
|
echo 'amd64' && \
|
||||||
|
curl -sSL https://github.com/cloudflare/cloudflared/releases/download/${CLOUDFLARED_VERSION}/cloudflared-linux-amd64 -o /usr/local/bin/cloudflared && chmod +x /usr/local/bin/cloudflared \
|
||||||
|
;fi"
|
||||||
|
|
||||||
|
RUN /bin/bash -c "if [[ ${TARGETPLATFORM} == 'linux/arm64' ]]; then \
|
||||||
|
echo 'arm64' && \
|
||||||
|
curl -L https://github.com/cloudflare/cloudflared/releases/download/${CLOUDFLARED_VERSION}/cloudflared-linux-arm64 -o /usr/local/bin/cloudflared && chmod +x /usr/local/bin/cloudflared \
|
||||||
|
;fi"
|
||||||
|
|||||||
@@ -7,10 +7,6 @@ ARG DOCKER_VERSION=24.0.5
|
|||||||
ARG DOCKER_COMPOSE_VERSION=2.21.0
|
ARG DOCKER_COMPOSE_VERSION=2.21.0
|
||||||
# https://github.com/docker/buildx/releases
|
# https://github.com/docker/buildx/releases
|
||||||
ARG DOCKER_BUILDX_VERSION=0.11.2
|
ARG DOCKER_BUILDX_VERSION=0.11.2
|
||||||
# https://github.com/buildpacks/pack/releases
|
|
||||||
ARG PACK_VERSION=0.30.0
|
|
||||||
# https://github.com/railwayapp/nixpacks/releases
|
|
||||||
ARG NIXPACKS_VERSION=1.14.0
|
|
||||||
|
|
||||||
USER root
|
USER root
|
||||||
WORKDIR /root
|
WORKDIR /root
|
||||||
|
|||||||
27
examples/docker-compose-fider.yaml
Normal file
27
examples/docker-compose-fider.yaml
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
services:
|
||||||
|
fider:
|
||||||
|
image: getfider/fider:stable
|
||||||
|
environment:
|
||||||
|
BASE_URL: $SERVICE_FQDN_FIDER
|
||||||
|
DATABASE_URL: postgres://$SERVICE_USER_MYSQL:$SERVICE_PASSWORD_MYSQL@database:5432/fider?sslmode=disable
|
||||||
|
JWT_SECRET: $SERVICE_PASSWORD64_FIDER
|
||||||
|
EMAIL_NOREPLY: ${EMAIL_NOREPLY:-noreply@example.com}
|
||||||
|
EMAIL_MAILGUN_API: $EMAIL_MAILGUN_API
|
||||||
|
EMAIL_MAILGUN_DOMAIN: $EMAIL_MAILGUN_DOMAIN
|
||||||
|
EMAIL_MAILGUN_REGION: $EMAIL_MAILGUN_REGION
|
||||||
|
EMAIL_SMTP_HOST: ${EMAIL_SMTP_HOST:-smtp.mailgun.com}
|
||||||
|
EMAIL_SMTP_PORT: ${EMAIL_SMTP_PORT:-587}
|
||||||
|
EMAIL_SMTP_USERNAME: ${EMAIL_SMTP_USERNAME:-postmaster@mailgun.com}
|
||||||
|
EMAIL_SMTP_PASSWORD: $EMAIL_SMTP_PASSWORD
|
||||||
|
EMAIL_SMTP_ENABLE_STARTTLS: $EMAIL_SMTP_ENABLE_STARTTLS
|
||||||
|
EMAIL_AWSSES_REGION: $EMAIL_AWSSES_REGION
|
||||||
|
EMAIL_AWSSES_ACCESS_KEY_ID: $EMAIL_AWSSES_ACCESS_KEY_ID
|
||||||
|
EMAIL_AWSSES_SECRET_ACCESS_KEY: $EMAIL_AWSSES_SECRET_ACCESS_KEY
|
||||||
|
database:
|
||||||
|
image: postgres:12
|
||||||
|
volumes:
|
||||||
|
- pg_data:/var/lib/postgresql/data
|
||||||
|
environment:
|
||||||
|
POSTGRES_USER: $SERVICE_USER_MYSQL
|
||||||
|
POSTGRES_PASSWORD: $SERVICE_PASSWORD_MYSQL
|
||||||
|
POSTGRES_DB: ${POSTGRES_DB:-fider}
|
||||||
23
examples/docker-compose-ghost.yaml
Normal file
23
examples/docker-compose-ghost.yaml
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
services:
|
||||||
|
ghost:
|
||||||
|
image: ghost:5
|
||||||
|
volumes:
|
||||||
|
- ghost-content-data:/var/lib/ghost/content
|
||||||
|
environment:
|
||||||
|
- url=$SERVICE_FQDN_GHOST
|
||||||
|
- database__client=mysql
|
||||||
|
- database__connection__host=mysql
|
||||||
|
- database__connection__user=$SERVICE_USER_MYSQL
|
||||||
|
- database__connection__password=$SERVICE_PASSWORD_MYSQL
|
||||||
|
- database__connection__database=${MYSQL_DATABASE-ghost}
|
||||||
|
depends_on:
|
||||||
|
- mysql
|
||||||
|
mysql:
|
||||||
|
image: mysql:8.0
|
||||||
|
volumes:
|
||||||
|
- ghost-mysql-data:/var/lib/mysql
|
||||||
|
environment:
|
||||||
|
- MYSQL_USER=${SERVICE_USER_MYSQL}
|
||||||
|
- MYSQL_PASSWORD=${SERVICE_PASSWORD_MYSQL}
|
||||||
|
- MYSQL_DATABASE=${MYSQL_DATABASE}
|
||||||
|
- MYSQL_ROOT_PASSWORD=${SERVICE_PASSWORD_MYSQL_ROOT}
|
||||||
52
examples/docker-compose-plausible.yaml
Normal file
52
examples/docker-compose-plausible.yaml
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
version: "3.3"
|
||||||
|
services:
|
||||||
|
plausible:
|
||||||
|
image: plausible/analytics:v2.0
|
||||||
|
command: sh -c "sleep 10 && /entrypoint.sh db createdb && /entrypoint.sh db migrate && /entrypoint.sh run"
|
||||||
|
environment:
|
||||||
|
- DATABASE_URL=postgres://postgres:$SERVICE_PASSWORD_POSTGRES@plausible_db/plausible
|
||||||
|
- BASE_URL=$SERVICE_FQDN_PLAUSIBLE
|
||||||
|
- SECRET_KEY_BASE=$SERVICE_BASE64_64_PLAUSIBLE
|
||||||
|
depends_on:
|
||||||
|
- plausible_db
|
||||||
|
- plausible_events_db
|
||||||
|
- mail
|
||||||
|
|
||||||
|
mail:
|
||||||
|
image: bytemark/smtp
|
||||||
|
|
||||||
|
plausible_db:
|
||||||
|
image: postgres:14-alpine
|
||||||
|
volumes:
|
||||||
|
- db-data:/var/lib/postgresql/data
|
||||||
|
environment:
|
||||||
|
- POSTGRES_DB=plausible
|
||||||
|
- POSTGRES_PASSWORD=$SERVICE_PASSWORD_POSTGRES
|
||||||
|
|
||||||
|
plausible_events_db:
|
||||||
|
image: clickhouse/clickhouse-server:23.3.7.5-alpine
|
||||||
|
volumes:
|
||||||
|
- type: volume
|
||||||
|
source: event-data
|
||||||
|
target: /var/lib/clickhouse
|
||||||
|
- type: bind
|
||||||
|
source: ./clickhouse/clickhouse-config.xml
|
||||||
|
target: /etc/clickhouse-server/config.d/logging.xml
|
||||||
|
read_only: true
|
||||||
|
content: >-
|
||||||
|
<clickhouse><profiles><default><log_queries>0</log_queries><log_query_threads>0</log_query_threads></default></profiles></clickhouse>
|
||||||
|
- type: bind
|
||||||
|
source: ./clickhouse/clickhouse-user-config.xml
|
||||||
|
target: /etc/clickhouse-server/users.d/logging.xml
|
||||||
|
read_only: true
|
||||||
|
content: >-
|
||||||
|
<clickhouse><logger><level>warning</level><console>true</console></logger><query_thread_log
|
||||||
|
remove="remove"/><query_log remove="remove"/><text_log
|
||||||
|
remove="remove"/><trace_log remove="remove"/><metric_log
|
||||||
|
remove="remove"/><asynchronous_metric_log
|
||||||
|
remove="remove"/><session_log remove="remove"/><part_log
|
||||||
|
remove="remove"/></clickhouse>
|
||||||
|
ulimits:
|
||||||
|
nofile:
|
||||||
|
soft: 262144
|
||||||
|
hard: 262144
|
||||||
15
examples/docker-compose-postgres.yaml
Normal file
15
examples/docker-compose-postgres.yaml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
services:
|
||||||
|
postgres:
|
||||||
|
image: postgres
|
||||||
|
command: 'postgres -c config_file=/etc/postgresql/postgresql.conf'
|
||||||
|
volumes:
|
||||||
|
- type: bind
|
||||||
|
source: ./postgresql.conf
|
||||||
|
target: /etc/postgresql/postgresql.conf
|
||||||
|
- type: bind
|
||||||
|
source: ./docker-entrypoint-initdb.d
|
||||||
|
target: /docker-entrypoint-initdb.d/
|
||||||
|
isDirectory: true
|
||||||
|
environment:
|
||||||
|
POSTGRES_USER: $SERVICE_USER_POSTGRES
|
||||||
|
POSTGRES_PASSWORD: $SERVICE_PASSWORD_POSTGRES
|
||||||
5
examples/docker-compose-uptime-kuma.yaml
Normal file
5
examples/docker-compose-uptime-kuma.yaml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
services:
|
||||||
|
uptime-kuma:
|
||||||
|
image: louislam/uptime-kuma:1
|
||||||
|
volumes:
|
||||||
|
- uptime-kuma:/app/data
|
||||||
76
examples/docker-compose-weird.yaml
Normal file
76
examples/docker-compose-weird.yaml
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
services:
|
||||||
|
ghost:
|
||||||
|
image: ghost:5
|
||||||
|
volumes:
|
||||||
|
- ~/configs:/etc/configs/:ro
|
||||||
|
- ./var/lib/ghost/content:/tmp/ghost2/content:ro
|
||||||
|
- /var/lib/ghost/content:/tmp/ghost/content:rw
|
||||||
|
- ghost-content-data:/var/lib/ghost/content
|
||||||
|
- type: volume
|
||||||
|
source: mydata
|
||||||
|
target: /data
|
||||||
|
volume:
|
||||||
|
nocopy: true
|
||||||
|
- type: bind
|
||||||
|
source: ./var/lib/ghost/data
|
||||||
|
target: /data
|
||||||
|
- type: bind
|
||||||
|
source: /tmp
|
||||||
|
target: /tmp
|
||||||
|
labels:
|
||||||
|
- "test.label=true"
|
||||||
|
ports:
|
||||||
|
- "3000"
|
||||||
|
- "3000-3005"
|
||||||
|
- "8000:8000"
|
||||||
|
- "9090-9091:8080-8081"
|
||||||
|
- "49100:22"
|
||||||
|
- "127.0.0.1:8001:8001"
|
||||||
|
- "127.0.0.1:5000-5010:5000-5010"
|
||||||
|
- "127.0.0.1::5000"
|
||||||
|
- "6060:6060/udp"
|
||||||
|
- "12400-12500:1240"
|
||||||
|
- target: 80
|
||||||
|
published: 8080
|
||||||
|
protocol: tcp
|
||||||
|
mode: host
|
||||||
|
networks:
|
||||||
|
- some-network
|
||||||
|
- other-network
|
||||||
|
environment:
|
||||||
|
- database__client=${DATABASE_CLIENT:-mysql}
|
||||||
|
- database__connection__database=${MYSQL_DATABASE:-ghost}
|
||||||
|
- database__connection__host=${DATABASE_CONNECTION_HOST:-mysql}
|
||||||
|
- test=${TEST:?true}
|
||||||
|
- url=$SERVICE_FQDN_GHOST
|
||||||
|
- database__connection__user=$SERVICE_USER_MYSQL
|
||||||
|
- database__connection__password=$SERVICE_PASSWORD_MYSQL
|
||||||
|
depends_on:
|
||||||
|
- mysql
|
||||||
|
mysql:
|
||||||
|
image: mysql:8.0
|
||||||
|
volumes:
|
||||||
|
- ghost-mysql-data:/var/lib/mysql
|
||||||
|
environment:
|
||||||
|
- MYSQL_USER=${SERVICE_USER_MYSQL}
|
||||||
|
- MYSQL_PASSWORD=${SERVICE_PASSWORD_MYSQL}
|
||||||
|
- MYSQL_DATABASE=$MYSQL_DATABASE
|
||||||
|
- MYSQL_ROOT_PASSWORD=${SERVICE_PASSWORD_MYSQLROOT}
|
||||||
|
- SESSION_SECRET
|
||||||
|
minio:
|
||||||
|
image: minio/minio
|
||||||
|
environment:
|
||||||
|
RACK_ENV: development
|
||||||
|
A: $A
|
||||||
|
SHOW: ${SHOW}
|
||||||
|
SHOW1: ${SHOW2-show1}
|
||||||
|
SHOW2: ${SHOW3:-show2}
|
||||||
|
SHOW3: ${SHOW4?show3}
|
||||||
|
SHOW4: ${SHOW5:?show4}
|
||||||
|
SHOW5: ${SERVICE_USER_MINIO}
|
||||||
|
SHOW6: ${SERVICE_PASSWORD_MINIO}
|
||||||
|
SHOW7: ${SERVICE_PASSWORD_64_MINIO}
|
||||||
|
SHOW8: ${SERVICE_BASE64_64_MINIO}
|
||||||
|
SHOW9: ${SERVICE_BASE64_128_MINIO}
|
||||||
|
SHOW10: ${SERVICE_BASE64_MINIO}
|
||||||
|
SHOW11:
|
||||||
352
package-lock.json
generated
352
package-lock.json
generated
@@ -5,19 +5,19 @@
|
|||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@tailwindcss/typography": "0.5.9",
|
"@tailwindcss/typography": "0.5.10",
|
||||||
"alpinejs": "3.12.2",
|
"alpinejs": "3.13.0",
|
||||||
"daisyui": "3.2.1",
|
"daisyui": "3.7.7",
|
||||||
"tailwindcss-scrollbar": "0.1.0"
|
"tailwindcss-scrollbar": "0.1.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vitejs/plugin-vue": "4.2.3",
|
"@vitejs/plugin-vue": "4.3.4",
|
||||||
"autoprefixer": "10.4.14",
|
"autoprefixer": "10.4.16",
|
||||||
"axios": "1.4.0",
|
"axios": "1.5.0",
|
||||||
"laravel-vite-plugin": "0.7.8",
|
"laravel-vite-plugin": "0.8.0",
|
||||||
"postcss": "8.4.24",
|
"postcss": "8.4.30",
|
||||||
"tailwindcss": "3.3.2",
|
"tailwindcss": "3.3.3",
|
||||||
"vite": "4.3.9",
|
"vite": "4.4.9",
|
||||||
"vue": "3.3.4"
|
"vue": "3.3.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -45,9 +45,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/android-arm": {
|
"node_modules/@esbuild/android-arm": {
|
||||||
"version": "0.17.12",
|
"version": "0.18.20",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.12.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz",
|
||||||
"integrity": "sha512-E/sgkvwoIfj4aMAPL2e35VnUJspzVYl7+M1B2cqeubdBhADV4uPon0KCc8p2G+LqSJ6i8ocYPCqY3A4GGq0zkQ==",
|
"integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm"
|
"arm"
|
||||||
],
|
],
|
||||||
@@ -61,9 +61,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/android-arm64": {
|
"node_modules/@esbuild/android-arm64": {
|
||||||
"version": "0.17.12",
|
"version": "0.18.20",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.17.12.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz",
|
||||||
"integrity": "sha512-WQ9p5oiXXYJ33F2EkE3r0FRDFVpEdcDiwNX3u7Xaibxfx6vQE0Sb8ytrfQsA5WO6kDn6mDfKLh6KrPBjvkk7xA==",
|
"integrity": "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -77,9 +77,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/android-x64": {
|
"node_modules/@esbuild/android-x64": {
|
||||||
"version": "0.17.12",
|
"version": "0.18.20",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.17.12.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.20.tgz",
|
||||||
"integrity": "sha512-m4OsaCr5gT+se25rFPHKQXARMyAehHTQAz4XX1Vk3d27VtqiX0ALMBPoXZsGaB6JYryCLfgGwUslMqTfqeLU0w==",
|
"integrity": "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -93,9 +93,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/darwin-arm64": {
|
"node_modules/@esbuild/darwin-arm64": {
|
||||||
"version": "0.17.12",
|
"version": "0.18.20",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.17.12.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz",
|
||||||
"integrity": "sha512-O3GCZghRIx+RAN0NDPhyyhRgwa19MoKlzGonIb5hgTj78krqp9XZbYCvFr9N1eUxg0ZQEpiiZ4QvsOQwBpP+lg==",
|
"integrity": "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -109,9 +109,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/darwin-x64": {
|
"node_modules/@esbuild/darwin-x64": {
|
||||||
"version": "0.17.12",
|
"version": "0.18.20",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.17.12.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz",
|
||||||
"integrity": "sha512-5D48jM3tW27h1qjaD9UNRuN+4v0zvksqZSPZqeSWggfMlsVdAhH3pwSfQIFJwcs9QJ9BRibPS4ViZgs3d2wsCA==",
|
"integrity": "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -125,9 +125,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/freebsd-arm64": {
|
"node_modules/@esbuild/freebsd-arm64": {
|
||||||
"version": "0.17.12",
|
"version": "0.18.20",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.12.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz",
|
||||||
"integrity": "sha512-OWvHzmLNTdF1erSvrfoEBGlN94IE6vCEaGEkEH29uo/VoONqPnoDFfShi41Ew+yKimx4vrmmAJEGNoyyP+OgOQ==",
|
"integrity": "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -141,9 +141,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/freebsd-x64": {
|
"node_modules/@esbuild/freebsd-x64": {
|
||||||
"version": "0.17.12",
|
"version": "0.18.20",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.17.12.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz",
|
||||||
"integrity": "sha512-A0Xg5CZv8MU9xh4a+7NUpi5VHBKh1RaGJKqjxe4KG87X+mTjDE6ZvlJqpWoeJxgfXHT7IMP9tDFu7IZ03OtJAw==",
|
"integrity": "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -157,9 +157,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/linux-arm": {
|
"node_modules/@esbuild/linux-arm": {
|
||||||
"version": "0.17.12",
|
"version": "0.18.20",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.17.12.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz",
|
||||||
"integrity": "sha512-WsHyJ7b7vzHdJ1fv67Yf++2dz3D726oO3QCu8iNYik4fb5YuuReOI9OtA+n7Mk0xyQivNTPbl181s+5oZ38gyA==",
|
"integrity": "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm"
|
"arm"
|
||||||
],
|
],
|
||||||
@@ -173,9 +173,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/linux-arm64": {
|
"node_modules/@esbuild/linux-arm64": {
|
||||||
"version": "0.17.12",
|
"version": "0.18.20",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.17.12.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz",
|
||||||
"integrity": "sha512-cK3AjkEc+8v8YG02hYLQIQlOznW+v9N+OI9BAFuyqkfQFR+DnDLhEM5N8QRxAUz99cJTo1rLNXqRrvY15gbQUg==",
|
"integrity": "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -189,9 +189,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/linux-ia32": {
|
"node_modules/@esbuild/linux-ia32": {
|
||||||
"version": "0.17.12",
|
"version": "0.18.20",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.17.12.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz",
|
||||||
"integrity": "sha512-jdOBXJqcgHlah/nYHnj3Hrnl9l63RjtQ4vn9+bohjQPI2QafASB5MtHAoEv0JQHVb/xYQTFOeuHnNYE1zF7tYw==",
|
"integrity": "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"ia32"
|
"ia32"
|
||||||
],
|
],
|
||||||
@@ -205,9 +205,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/linux-loong64": {
|
"node_modules/@esbuild/linux-loong64": {
|
||||||
"version": "0.17.12",
|
"version": "0.18.20",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.17.12.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz",
|
||||||
"integrity": "sha512-GTOEtj8h9qPKXCyiBBnHconSCV9LwFyx/gv3Phw0pa25qPYjVuuGZ4Dk14bGCfGX3qKF0+ceeQvwmtI+aYBbVA==",
|
"integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"loong64"
|
"loong64"
|
||||||
],
|
],
|
||||||
@@ -221,9 +221,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/linux-mips64el": {
|
"node_modules/@esbuild/linux-mips64el": {
|
||||||
"version": "0.17.12",
|
"version": "0.18.20",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.17.12.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz",
|
||||||
"integrity": "sha512-o8CIhfBwKcxmEENOH9RwmUejs5jFiNoDw7YgS0EJTF6kgPgcqLFjgoc5kDey5cMHRVCIWc6kK2ShUePOcc7RbA==",
|
"integrity": "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"mips64el"
|
"mips64el"
|
||||||
],
|
],
|
||||||
@@ -237,9 +237,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/linux-ppc64": {
|
"node_modules/@esbuild/linux-ppc64": {
|
||||||
"version": "0.17.12",
|
"version": "0.18.20",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.17.12.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz",
|
||||||
"integrity": "sha512-biMLH6NR/GR4z+ap0oJYb877LdBpGac8KfZoEnDiBKd7MD/xt8eaw1SFfYRUeMVx519kVkAOL2GExdFmYnZx3A==",
|
"integrity": "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"ppc64"
|
"ppc64"
|
||||||
],
|
],
|
||||||
@@ -253,9 +253,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/linux-riscv64": {
|
"node_modules/@esbuild/linux-riscv64": {
|
||||||
"version": "0.17.12",
|
"version": "0.18.20",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.17.12.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz",
|
||||||
"integrity": "sha512-jkphYUiO38wZGeWlfIBMB72auOllNA2sLfiZPGDtOBb1ELN8lmqBrlMiucgL8awBw1zBXN69PmZM6g4yTX84TA==",
|
"integrity": "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"riscv64"
|
"riscv64"
|
||||||
],
|
],
|
||||||
@@ -269,9 +269,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/linux-s390x": {
|
"node_modules/@esbuild/linux-s390x": {
|
||||||
"version": "0.17.12",
|
"version": "0.18.20",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.17.12.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz",
|
||||||
"integrity": "sha512-j3ucLdeY9HBcvODhCY4b+Ds3hWGO8t+SAidtmWu/ukfLLG/oYDMaA+dnugTVAg5fnUOGNbIYL9TOjhWgQB8W5g==",
|
"integrity": "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"s390x"
|
"s390x"
|
||||||
],
|
],
|
||||||
@@ -285,9 +285,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/linux-x64": {
|
"node_modules/@esbuild/linux-x64": {
|
||||||
"version": "0.17.12",
|
"version": "0.18.20",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.17.12.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz",
|
||||||
"integrity": "sha512-uo5JL3cgaEGotaqSaJdRfFNSCUJOIliKLnDGWaVCgIKkHxwhYMm95pfMbWZ9l7GeW9kDg0tSxcy9NYdEtjwwmA==",
|
"integrity": "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -301,9 +301,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/netbsd-x64": {
|
"node_modules/@esbuild/netbsd-x64": {
|
||||||
"version": "0.17.12",
|
"version": "0.18.20",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.17.12.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz",
|
||||||
"integrity": "sha512-DNdoRg8JX+gGsbqt2gPgkgb00mqOgOO27KnrWZtdABl6yWTST30aibGJ6geBq3WM2TIeW6COs5AScnC7GwtGPg==",
|
"integrity": "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -317,9 +317,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/openbsd-x64": {
|
"node_modules/@esbuild/openbsd-x64": {
|
||||||
"version": "0.17.12",
|
"version": "0.18.20",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.17.12.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz",
|
||||||
"integrity": "sha512-aVsENlr7B64w8I1lhHShND5o8cW6sB9n9MUtLumFlPhG3elhNWtE7M1TFpj3m7lT3sKQUMkGFjTQBrvDDO1YWA==",
|
"integrity": "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -333,9 +333,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/sunos-x64": {
|
"node_modules/@esbuild/sunos-x64": {
|
||||||
"version": "0.17.12",
|
"version": "0.18.20",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.17.12.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz",
|
||||||
"integrity": "sha512-qbHGVQdKSwi0JQJuZznS4SyY27tYXYF0mrgthbxXrZI3AHKuRvU+Eqbg/F0rmLDpW/jkIZBlCO1XfHUBMNJ1pg==",
|
"integrity": "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -349,9 +349,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/win32-arm64": {
|
"node_modules/@esbuild/win32-arm64": {
|
||||||
"version": "0.17.12",
|
"version": "0.18.20",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.17.12.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz",
|
||||||
"integrity": "sha512-zsCp8Ql+96xXTVTmm6ffvoTSZSV2B/LzzkUXAY33F/76EajNw1m+jZ9zPfNJlJ3Rh4EzOszNDHsmG/fZOhtqDg==",
|
"integrity": "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -365,9 +365,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/win32-ia32": {
|
"node_modules/@esbuild/win32-ia32": {
|
||||||
"version": "0.17.12",
|
"version": "0.18.20",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.17.12.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz",
|
||||||
"integrity": "sha512-FfrFjR4id7wcFYOdqbDfDET3tjxCozUgbqdkOABsSFzoZGFC92UK7mg4JKRc/B3NNEf1s2WHxJ7VfTdVDPN3ng==",
|
"integrity": "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"ia32"
|
"ia32"
|
||||||
],
|
],
|
||||||
@@ -381,9 +381,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/win32-x64": {
|
"node_modules/@esbuild/win32-x64": {
|
||||||
"version": "0.17.12",
|
"version": "0.18.20",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.17.12.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz",
|
||||||
"integrity": "sha512-JOOxw49BVZx2/5tW3FqkdjSD/5gXYeVGPDcB0lvap0gLQshkh1Nyel1QazC+wNxus3xPlsYAgqU1BUmrmCvWtw==",
|
"integrity": "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -477,9 +477,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tailwindcss/typography": {
|
"node_modules/@tailwindcss/typography": {
|
||||||
"version": "0.5.9",
|
"version": "0.5.10",
|
||||||
"resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.9.tgz",
|
"resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.10.tgz",
|
||||||
"integrity": "sha512-t8Sg3DyynFysV9f4JDOVISGsjazNb48AeIYQwcL+Bsq5uf4RYL75C1giZ43KISjeDGBaTN3Kxh7Xj/vRSMJUUg==",
|
"integrity": "sha512-Pe8BuPJQJd3FfRnm6H0ulKIGoMEQS+Vq01R6M5aCrFB/ccR/shT+0kXLjouGC1gFLm9hopTFN+DMP0pfwRWzPw==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"lodash.castarray": "^4.4.0",
|
"lodash.castarray": "^4.4.0",
|
||||||
"lodash.isplainobject": "^4.0.6",
|
"lodash.isplainobject": "^4.0.6",
|
||||||
@@ -503,9 +503,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vitejs/plugin-vue": {
|
"node_modules/@vitejs/plugin-vue": {
|
||||||
"version": "4.2.3",
|
"version": "4.3.4",
|
||||||
"resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-4.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-4.3.4.tgz",
|
||||||
"integrity": "sha512-R6JDUfiZbJA9cMiguQ7jxALsgiprjBeHL5ikpXfJCH62pPHtI+JdJ5xWj6Ev73yXSlYl86+blXn1kZHQ7uElxw==",
|
"integrity": "sha512-ciXNIHKPriERBisHFBvnTbfKa6r9SAesOYXeGDzgegcvy9Q4xdScSHAmKbNT0M3O0S9LKhIf5/G+UYG4NnnzYw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^14.18.0 || >=16.0.0"
|
"node": "^14.18.0 || >=16.0.0"
|
||||||
@@ -683,9 +683,9 @@
|
|||||||
"integrity": "sha512-oJ4F3TnvpXaQwZJNF3ZK+kLPHKarDmJjJ6jyzVNDKH9md1dptjC7lWR//jrGuLdek/U6iltWxqAnYOu8gCiOvA=="
|
"integrity": "sha512-oJ4F3TnvpXaQwZJNF3ZK+kLPHKarDmJjJ6jyzVNDKH9md1dptjC7lWR//jrGuLdek/U6iltWxqAnYOu8gCiOvA=="
|
||||||
},
|
},
|
||||||
"node_modules/alpinejs": {
|
"node_modules/alpinejs": {
|
||||||
"version": "3.12.2",
|
"version": "3.13.0",
|
||||||
"resolved": "https://registry.npmjs.org/alpinejs/-/alpinejs-3.12.2.tgz",
|
"resolved": "https://registry.npmjs.org/alpinejs/-/alpinejs-3.13.0.tgz",
|
||||||
"integrity": "sha512-wrUZULm9w6DYwWcUtB/anFLgRaF4riSuPgIJ3gcPUS8st9luAJnAxoIgro/Al97d2McSSz/JypWg/NlmKFIJJA==",
|
"integrity": "sha512-7FYR1Yz3evIjlJD1mZ3SYWSw+jlOmQGeQ1QiSufSQ6J84XMQFkzxm6OobiZ928SfqhGdoIp2SsABNsS4rXMMJw==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vue/reactivity": "~3.1.1"
|
"@vue/reactivity": "~3.1.1"
|
||||||
}
|
}
|
||||||
@@ -719,9 +719,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/autoprefixer": {
|
"node_modules/autoprefixer": {
|
||||||
"version": "10.4.14",
|
"version": "10.4.16",
|
||||||
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.14.tgz",
|
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.16.tgz",
|
||||||
"integrity": "sha512-FQzyfOsTlwVzjHxKEqRIAdJx9niO6VCBCoEwax/VLSoQF29ggECcPuBqUMZ+u8jCZOPSy8b8/8KnuFbp0SaFZQ==",
|
"integrity": "sha512-7vd3UC6xKp0HLfua5IjZlcXvGAGy7cBAXTg2lyQ/8WpNhd6SiZ8Be+xm3FyBSYJx5GKcpRCzBh7RH4/0dnY+uQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -731,12 +731,16 @@
|
|||||||
{
|
{
|
||||||
"type": "tidelift",
|
"type": "tidelift",
|
||||||
"url": "https://tidelift.com/funding/github/npm/autoprefixer"
|
"url": "https://tidelift.com/funding/github/npm/autoprefixer"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/ai"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"browserslist": "^4.21.5",
|
"browserslist": "^4.21.10",
|
||||||
"caniuse-lite": "^1.0.30001464",
|
"caniuse-lite": "^1.0.30001538",
|
||||||
"fraction.js": "^4.2.0",
|
"fraction.js": "^4.3.6",
|
||||||
"normalize-range": "^0.1.2",
|
"normalize-range": "^0.1.2",
|
||||||
"picocolors": "^1.0.0",
|
"picocolors": "^1.0.0",
|
||||||
"postcss-value-parser": "^4.2.0"
|
"postcss-value-parser": "^4.2.0"
|
||||||
@@ -752,9 +756,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/axios": {
|
"node_modules/axios": {
|
||||||
"version": "1.4.0",
|
"version": "1.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/axios/-/axios-1.5.0.tgz",
|
||||||
"integrity": "sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA==",
|
"integrity": "sha512-D4DdjDo5CY50Qms0qGQTTw6Q44jl7zRwY7bthds06pUGfChBCTcQs+N743eFWGEd6pRTMd6A+I87aWyFV5wiZQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"follow-redirects": "^1.15.0",
|
"follow-redirects": "^1.15.0",
|
||||||
@@ -796,9 +800,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/browserslist": {
|
"node_modules/browserslist": {
|
||||||
"version": "4.21.5",
|
"version": "4.21.11",
|
||||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz",
|
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.11.tgz",
|
||||||
"integrity": "sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==",
|
"integrity": "sha512-xn1UXOKUz7DjdGlg9RrUr0GGiWzI97UQJnugHtH0OLDfJB7jMgoIkYvRIEO1l9EeEERVqeqLYOcFBW9ldjypbQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -808,13 +812,17 @@
|
|||||||
{
|
{
|
||||||
"type": "tidelift",
|
"type": "tidelift",
|
||||||
"url": "https://tidelift.com/funding/github/npm/browserslist"
|
"url": "https://tidelift.com/funding/github/npm/browserslist"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/ai"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"caniuse-lite": "^1.0.30001449",
|
"caniuse-lite": "^1.0.30001538",
|
||||||
"electron-to-chromium": "^1.4.284",
|
"electron-to-chromium": "^1.4.526",
|
||||||
"node-releases": "^2.0.8",
|
"node-releases": "^2.0.13",
|
||||||
"update-browserslist-db": "^1.0.10"
|
"update-browserslist-db": "^1.0.13"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"browserslist": "cli.js"
|
"browserslist": "cli.js"
|
||||||
@@ -832,9 +840,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/caniuse-lite": {
|
"node_modules/caniuse-lite": {
|
||||||
"version": "1.0.30001467",
|
"version": "1.0.30001539",
|
||||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001467.tgz",
|
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001539.tgz",
|
||||||
"integrity": "sha512-cEdN/5e+RPikvl9AHm4uuLXxeCNq8rFsQ+lPHTfe/OtypP3WwnVVbjn+6uBV7PaFL6xUFzTh+sSCOz1rKhcO+Q==",
|
"integrity": "sha512-hfS5tE8bnNiNvEOEkm8HElUHroYwlqMMENEzELymy77+tJ6m+gA2krtHl5hxJaj71OlpC2cHZbdSMX1/YEqEkA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -844,6 +852,10 @@
|
|||||||
{
|
{
|
||||||
"type": "tidelift",
|
"type": "tidelift",
|
||||||
"url": "https://tidelift.com/funding/github/npm/caniuse-lite"
|
"url": "https://tidelift.com/funding/github/npm/caniuse-lite"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/ai"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@@ -941,9 +953,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/daisyui": {
|
"node_modules/daisyui": {
|
||||||
"version": "3.2.1",
|
"version": "3.7.7",
|
||||||
"resolved": "https://registry.npmjs.org/daisyui/-/daisyui-3.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/daisyui/-/daisyui-3.7.7.tgz",
|
||||||
"integrity": "sha512-gIqE6wiqoJt9G8+n3R/SwLeUnpNCE2eDhT73rP0yZYVaM7o6zVcakBH3aEW5RGpx3UkonPiLuvcgxRcb2lE8TA==",
|
"integrity": "sha512-2/nFdW/6R9MMnR8tTm07jPVyPaZwpUSkVsFAADb7Oq8N2Ynbls57laDdNqxTCUmn0QvcZi01TKl8zQbAwRfw1w==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"colord": "^2.9",
|
"colord": "^2.9",
|
||||||
"css-selector-tokenizer": "^0.8",
|
"css-selector-tokenizer": "^0.8",
|
||||||
@@ -979,15 +991,15 @@
|
|||||||
"integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA=="
|
"integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA=="
|
||||||
},
|
},
|
||||||
"node_modules/electron-to-chromium": {
|
"node_modules/electron-to-chromium": {
|
||||||
"version": "1.4.332",
|
"version": "1.4.528",
|
||||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.332.tgz",
|
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.528.tgz",
|
||||||
"integrity": "sha512-c1Vbv5tuUlBFp0mb3mCIjw+REEsgthRgNE8BlbEDKmvzb8rxjcVki6OkQP83vLN34s0XCxpSkq7AZNep1a6xhw==",
|
"integrity": "sha512-UdREXMXzLkREF4jA8t89FQjA8WHI6ssP38PMY4/4KhXFQbtImnghh4GkCgrtiZwLKUKVD2iTVXvDVQjfomEQuA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/esbuild": {
|
"node_modules/esbuild": {
|
||||||
"version": "0.17.12",
|
"version": "0.18.20",
|
||||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.17.12.tgz",
|
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz",
|
||||||
"integrity": "sha512-bX/zHl7Gn2CpQwcMtRogTTBf9l1nl+H6R8nUbjk+RuKqAE3+8FDulLA+pHvX7aA7Xe07Iwa+CWvy9I8Y2qqPKQ==",
|
"integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
@@ -997,28 +1009,28 @@
|
|||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@esbuild/android-arm": "0.17.12",
|
"@esbuild/android-arm": "0.18.20",
|
||||||
"@esbuild/android-arm64": "0.17.12",
|
"@esbuild/android-arm64": "0.18.20",
|
||||||
"@esbuild/android-x64": "0.17.12",
|
"@esbuild/android-x64": "0.18.20",
|
||||||
"@esbuild/darwin-arm64": "0.17.12",
|
"@esbuild/darwin-arm64": "0.18.20",
|
||||||
"@esbuild/darwin-x64": "0.17.12",
|
"@esbuild/darwin-x64": "0.18.20",
|
||||||
"@esbuild/freebsd-arm64": "0.17.12",
|
"@esbuild/freebsd-arm64": "0.18.20",
|
||||||
"@esbuild/freebsd-x64": "0.17.12",
|
"@esbuild/freebsd-x64": "0.18.20",
|
||||||
"@esbuild/linux-arm": "0.17.12",
|
"@esbuild/linux-arm": "0.18.20",
|
||||||
"@esbuild/linux-arm64": "0.17.12",
|
"@esbuild/linux-arm64": "0.18.20",
|
||||||
"@esbuild/linux-ia32": "0.17.12",
|
"@esbuild/linux-ia32": "0.18.20",
|
||||||
"@esbuild/linux-loong64": "0.17.12",
|
"@esbuild/linux-loong64": "0.18.20",
|
||||||
"@esbuild/linux-mips64el": "0.17.12",
|
"@esbuild/linux-mips64el": "0.18.20",
|
||||||
"@esbuild/linux-ppc64": "0.17.12",
|
"@esbuild/linux-ppc64": "0.18.20",
|
||||||
"@esbuild/linux-riscv64": "0.17.12",
|
"@esbuild/linux-riscv64": "0.18.20",
|
||||||
"@esbuild/linux-s390x": "0.17.12",
|
"@esbuild/linux-s390x": "0.18.20",
|
||||||
"@esbuild/linux-x64": "0.17.12",
|
"@esbuild/linux-x64": "0.18.20",
|
||||||
"@esbuild/netbsd-x64": "0.17.12",
|
"@esbuild/netbsd-x64": "0.18.20",
|
||||||
"@esbuild/openbsd-x64": "0.17.12",
|
"@esbuild/openbsd-x64": "0.18.20",
|
||||||
"@esbuild/sunos-x64": "0.17.12",
|
"@esbuild/sunos-x64": "0.18.20",
|
||||||
"@esbuild/win32-arm64": "0.17.12",
|
"@esbuild/win32-arm64": "0.18.20",
|
||||||
"@esbuild/win32-ia32": "0.17.12",
|
"@esbuild/win32-ia32": "0.18.20",
|
||||||
"@esbuild/win32-x64": "0.17.12"
|
"@esbuild/win32-x64": "0.18.20"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/escalade": {
|
"node_modules/escalade": {
|
||||||
@@ -1121,16 +1133,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/fraction.js": {
|
"node_modules/fraction.js": {
|
||||||
"version": "4.2.0",
|
"version": "4.3.6",
|
||||||
"resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.6.tgz",
|
||||||
"integrity": "sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==",
|
"integrity": "sha512-n2aZ9tNfYDwaHhvFTkhFErqOMIb8uyzSQ+vGJBjZyanAKZVbGUQ1sngfk9FdkBw7G26O7AgNjLcecLffD1c7eg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "*"
|
"node": "*"
|
||||||
},
|
},
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "patreon",
|
"type": "patreon",
|
||||||
"url": "https://www.patreon.com/infusion"
|
"url": "https://github.com/sponsors/rawify"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/fs.realpath": {
|
"node_modules/fs.realpath": {
|
||||||
@@ -1277,9 +1289,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/laravel-vite-plugin": {
|
"node_modules/laravel-vite-plugin": {
|
||||||
"version": "0.7.8",
|
"version": "0.8.0",
|
||||||
"resolved": "https://registry.npmjs.org/laravel-vite-plugin/-/laravel-vite-plugin-0.7.8.tgz",
|
"resolved": "https://registry.npmjs.org/laravel-vite-plugin/-/laravel-vite-plugin-0.8.0.tgz",
|
||||||
"integrity": "sha512-HWYqpQYHR3kEQ1LsHX7gHJoNNf0bz5z5mDaHBLzS+PGLCTmYqlU5/SZyeEgObV7z7bC/cnStYcY9H1DI1D5Udg==",
|
"integrity": "sha512-6VjLI+azBpeK6rWBiKcb/En5GnTdYpL0U4zS8gXYvb2/VSq4mlau5H3NWpSktUDBMM1b97LLgICx5zevi8IY0w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"picocolors": "^1.0.0",
|
"picocolors": "^1.0.0",
|
||||||
@@ -1412,9 +1424,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/node-releases": {
|
"node_modules/node-releases": {
|
||||||
"version": "2.0.10",
|
"version": "2.0.13",
|
||||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz",
|
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz",
|
||||||
"integrity": "sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==",
|
"integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/normalize-path": {
|
"node_modules/normalize-path": {
|
||||||
@@ -1504,9 +1516,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/postcss": {
|
"node_modules/postcss": {
|
||||||
"version": "8.4.24",
|
"version": "8.4.30",
|
||||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.24.tgz",
|
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.30.tgz",
|
||||||
"integrity": "sha512-M0RzbcI0sO/XJNucsGjvWU9ERWxb/ytp1w6dKtxTKgixdtQDq4rmx/g8W1hnaheq9jgwL/oyEdH5Bc4WwJKMqg==",
|
"integrity": "sha512-7ZEao1g4kd68l97aWG/etQKPKq07us0ieSZ2TnFDk11i0ZfDW2AwKHYU8qv4MZKqN2fdBfg+7q0ES06UA73C1g==",
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "opencollective",
|
"type": "opencollective",
|
||||||
@@ -1697,9 +1709,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/rollup": {
|
"node_modules/rollup": {
|
||||||
"version": "3.21.0",
|
"version": "3.29.3",
|
||||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-3.21.0.tgz",
|
"resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.3.tgz",
|
||||||
"integrity": "sha512-ANPhVcyeHvYdQMUyCbczy33nbLzI7RzrBje4uvNiTDJGIMtlKoOStmympwr9OtS1LZxiDmE2wvxHyVhoLtf1KQ==",
|
"integrity": "sha512-T7du6Hum8jOkSWetjRgbwpM6Sy0nECYrYRSmZjayFcOddtKJWU4d17AC3HNUk7HRuqy4p+G7aEZclSHytqUmEg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"rollup": "dist/bin/rollup"
|
"rollup": "dist/bin/rollup"
|
||||||
@@ -1794,9 +1806,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/tailwindcss": {
|
"node_modules/tailwindcss": {
|
||||||
"version": "3.3.2",
|
"version": "3.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.3.3.tgz",
|
||||||
"integrity": "sha512-9jPkMiIBXvPc2KywkraqsUfbfj+dHDb+JPWtSJa9MLFdrPyazI7q6WX2sUrm7R9eVR7qqv3Pas7EvQFzxKnI6w==",
|
"integrity": "sha512-A0KgSkef7eE4Mf+nKJ83i75TMyq8HqY3qmFIJSWy8bNt0v1lG7jUcpGpoTFxAwYcWOphcTBLPPJg+bDfhDf52w==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@alloc/quick-lru": "^5.2.0",
|
"@alloc/quick-lru": "^5.2.0",
|
||||||
"arg": "^5.0.2",
|
"arg": "^5.0.2",
|
||||||
@@ -1818,7 +1830,6 @@
|
|||||||
"postcss-load-config": "^4.0.1",
|
"postcss-load-config": "^4.0.1",
|
||||||
"postcss-nested": "^6.0.1",
|
"postcss-nested": "^6.0.1",
|
||||||
"postcss-selector-parser": "^6.0.11",
|
"postcss-selector-parser": "^6.0.11",
|
||||||
"postcss-value-parser": "^4.2.0",
|
|
||||||
"resolve": "^1.22.2",
|
"resolve": "^1.22.2",
|
||||||
"sucrase": "^3.32.0"
|
"sucrase": "^3.32.0"
|
||||||
},
|
},
|
||||||
@@ -1874,9 +1885,9 @@
|
|||||||
"integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA=="
|
"integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA=="
|
||||||
},
|
},
|
||||||
"node_modules/update-browserslist-db": {
|
"node_modules/update-browserslist-db": {
|
||||||
"version": "1.0.10",
|
"version": "1.0.13",
|
||||||
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz",
|
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz",
|
||||||
"integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==",
|
"integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -1886,6 +1897,10 @@
|
|||||||
{
|
{
|
||||||
"type": "tidelift",
|
"type": "tidelift",
|
||||||
"url": "https://tidelift.com/funding/github/npm/browserslist"
|
"url": "https://tidelift.com/funding/github/npm/browserslist"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/ai"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -1893,7 +1908,7 @@
|
|||||||
"picocolors": "^1.0.0"
|
"picocolors": "^1.0.0"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"browserslist-lint": "cli.js"
|
"update-browserslist-db": "cli.js"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"browserslist": ">= 4.21.0"
|
"browserslist": ">= 4.21.0"
|
||||||
@@ -1905,14 +1920,14 @@
|
|||||||
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
|
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
|
||||||
},
|
},
|
||||||
"node_modules/vite": {
|
"node_modules/vite": {
|
||||||
"version": "4.3.9",
|
"version": "4.4.9",
|
||||||
"resolved": "https://registry.npmjs.org/vite/-/vite-4.3.9.tgz",
|
"resolved": "https://registry.npmjs.org/vite/-/vite-4.4.9.tgz",
|
||||||
"integrity": "sha512-qsTNZjO9NoJNW7KnOrgYwczm0WctJ8m/yqYAMAK9Lxt4SoySUfS5S8ia9K7JHpa3KEeMfyF8LoJ3c5NeBJy6pg==",
|
"integrity": "sha512-2mbUn2LlUmNASWwSCNSJ/EG2HuSRTnVNaydp6vMCm5VIqJsjMfbIWtbH2kDuwUVW5mMUKKZvGPX/rqeqVvv1XA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"esbuild": "^0.17.5",
|
"esbuild": "^0.18.10",
|
||||||
"postcss": "^8.4.23",
|
"postcss": "^8.4.27",
|
||||||
"rollup": "^3.21.0"
|
"rollup": "^3.27.1"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"vite": "bin/vite.js"
|
"vite": "bin/vite.js"
|
||||||
@@ -1920,12 +1935,16 @@
|
|||||||
"engines": {
|
"engines": {
|
||||||
"node": "^14.18.0 || >=16.0.0"
|
"node": "^14.18.0 || >=16.0.0"
|
||||||
},
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/vitejs/vite?sponsor=1"
|
||||||
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"fsevents": "~2.3.2"
|
"fsevents": "~2.3.2"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@types/node": ">= 14",
|
"@types/node": ">= 14",
|
||||||
"less": "*",
|
"less": "*",
|
||||||
|
"lightningcss": "^1.21.0",
|
||||||
"sass": "*",
|
"sass": "*",
|
||||||
"stylus": "*",
|
"stylus": "*",
|
||||||
"sugarss": "*",
|
"sugarss": "*",
|
||||||
@@ -1938,6 +1957,9 @@
|
|||||||
"less": {
|
"less": {
|
||||||
"optional": true
|
"optional": true
|
||||||
},
|
},
|
||||||
|
"lightningcss": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
"sass": {
|
"sass": {
|
||||||
"optional": true
|
"optional": true
|
||||||
},
|
},
|
||||||
|
|||||||
20
package.json
20
package.json
@@ -6,19 +6,19 @@
|
|||||||
"build": "vite build"
|
"build": "vite build"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vitejs/plugin-vue": "4.2.3",
|
"@vitejs/plugin-vue": "4.3.4",
|
||||||
"autoprefixer": "10.4.14",
|
"autoprefixer": "10.4.16",
|
||||||
"axios": "1.4.0",
|
"axios": "1.5.0",
|
||||||
"laravel-vite-plugin": "0.7.8",
|
"laravel-vite-plugin": "0.8.0",
|
||||||
"postcss": "8.4.24",
|
"postcss": "8.4.30",
|
||||||
"tailwindcss": "3.3.2",
|
"tailwindcss": "3.3.3",
|
||||||
"vite": "4.3.9",
|
"vite": "4.4.9",
|
||||||
"vue": "3.3.4"
|
"vue": "3.3.4"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@tailwindcss/typography": "0.5.9",
|
"@tailwindcss/typography": "0.5.10",
|
||||||
"alpinejs": "3.12.2",
|
"alpinejs": "3.13.0",
|
||||||
"daisyui": "3.2.1",
|
"daisyui": "3.7.7",
|
||||||
"tailwindcss-scrollbar": "0.1.0"
|
"tailwindcss-scrollbar": "0.1.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
2
public/vendor/horizon/app.js
vendored
2
public/vendor/horizon/app.js
vendored
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user